/**
 * HTTP controller for Phase 1 Chat endpoints.
 *
 * Thin layer: parses + validates requests, delegates to `ChatV2Service`,
 * and maps `ChatError` to HTTP responses. All routes are owner-scoped via
 * `requireAuth` middleware; the principal is lifted from `req.user`.
 *
 * Wiring hooks (all optional — omit for tests that only need REST):
 *   - `gateway.broadcast(channelId, frame)`  — after successful persist,
 *     the controller fans a `message` frame out to WS subscribers so the
 *     sending client's WS re-broadcasts reconcile with its HTTP-201 echo.
 *   - `dispatcher.dispatchToAgent(channel, message)` — fires when a USER
 *     message lands, pushing the text into the bound agent's runtime. The
 *     agent replies via the `reply-channel` skill, which re-enters this
 *     same endpoint with an `X-Crewly-Agent-Session` header.
 *
 * @module controllers/chat-v2/chat-v2.controller
 */
import type { Request, Response } from 'express';
import type { ChatV2Service } from '../../services/chat-v2/chat-v2.service.js';
import type { ChatV2DispatcherService } from '../../services/chat-v2/chat-v2.dispatcher.service.js';
import type { ChatV2Gateway } from '../../websocket/chat-v2.gateway.js';
import { ChatError, type ChatChannelDTO, type ChatMessageDTO, type ChatPrincipal } from '../../services/chat-v2/types.js';
/**
 * Build the service-level principal from the authenticated request.
 *
 * Honors an optional `X-Crewly-Agent-Session` header — the `reply-channel`
 * skill sets this so the service-layer `resolveSender` returns senderType
 * `agent` instead of `user`. The header is only honored when the request
 * has already passed `requireAuth` (i.e. a trusted bearer), because the
 * service still verifies that `principal.agentSession` matches the
 * channel's bound `agent_session` before marking a message as an agent
 * reply.
 *
 * For Phase 1 the bearer used by the skill is the same OSS session token
 * the owner uses — the agent runs locally on the user's machine. Signed
 * per-agent tokens arrive in Phase 2.
 */
export declare function principalFromRequest(req: Request): ChatPrincipal;
/** Serialize a `ChatError` into the canonical wire shape. */
export declare function sendChatError(res: Response, err: ChatError): void;
/** Fallback 500 responder for unknown failures. */
export declare function sendInternalError(res: Response, err: unknown): void;
/** Thin wrapper that catches ChatError → 4xx and anything else → 500. */
export declare function runHandler<T>(res: Response, run: () => T): T | undefined;
/**
 * Build the canonical chat-v2 sourceConversationItemId so the
 * `RequestSlaSubscriber` can dedupe + extract channel/message ids the
 * same way it does for Slack (`slack-${channelId}-${ts}`).
 *
 * Shape: `chatv2-${channelId}__${messageId}`. The inter-field delimiter
 * is the double underscore `__` rather than a single dash because
 * `channel.store.ts:86` and `message.store.ts:165` mint ids via
 * `randomUUID()` (4 dashes embedded in each id). A single-dash
 * delimiter collides with the embedded UUID dashes and corrupts the
 * round-trip — see Arch's review on PR #364 / INBOUND-2.f1.
 * UUIDs are hex digits + dashes only and cannot contain `_`, so `__`
 * is collision-free against any current or future hex-shaped id.
 *
 * @param channelId - Persisted chat-v2 channel id (UUIDv4 in production)
 * @param messageId - Persisted chat-v2 message id (UUIDv4 in production)
 * @returns The composite source id, e.g.
 *   `chatv2-8b3c9a4e-5a02-4d51-9e7a-6f8c4d2e8a1b__fa1e2c3d-4567-89ab-cdef-0123456789ab`
 */
export declare function buildChatV2SourceId(channelId: string, messageId: string): string;
/**
 * INBOUND-2: decide whether a chat-v2 channel routes user messages to the
 * orchestrator. v1 scope = `type='dm'` channels whose `agentSession`
 * field is the orchestrator session. `type='channel'` (team-scoped) is
 * excluded — the SLA path expects the WI target to be the orchestrator
 * and team-channels haven't yet defined an orc-tagged routing concept
 * (see INBOUND-2 task spec, escalation note "no orc-tagged channels yet").
 *
 * Exported so tests can lock the scope without relying on private state.
 *
 * @param channel - The channel DTO returned by `service.getChannel`.
 * @returns True iff the channel routes to the orchestrator.
 */
export declare function isOrchestratorRoutedChatV2Channel(channel: ChatChannelDTO): boolean;
/**
 * Fire-and-forget: if a USER-origin chat-v2 message lands in a channel
 * routed to the orc, register a V3 Request so the
 * `RequestSlaSubscriber` (INBOUND-1) can attach a respond_to_user
 * WorkItem with the 5/10min SLA timers.
 *
 * Mirrors the Slack pattern at
 * `slack-orchestrator-bridge.ts:367-395`. Errors are swallowed and
 * logged at warn-level inside the lazy import — Request creation is
 * non-critical to the chat ack.
 *
 * @param channel - The persisted channel (provides type + agentSession)
 * @param message - The persisted user message (provides id + content)
 */
export declare function emitChatV2RequestCreated(channel: ChatChannelDTO, message: ChatMessageDTO): void;
/**
 * Fire-and-forget: when an AGENT-origin chat-v2 message lands in a
 * channel that may have an outstanding `respond_to_user` WI tracked by
 * the SLA subscriber, mark it resolved (transitions the WI to `done`,
 * silences the breach + escalation timers).
 *
 * Safe to call for any agent-origin message — `markResolvedByChatV2` is
 * a no-op when the channel isn't tracked.
 *
 * @param message - The persisted message (only agent-origin is acted on)
 */
export declare function notifyChatV2AgentReply(message: ChatMessageDTO): void;
/** Express handlers returned by `createChatV2Controller`. */
export interface ChatV2ControllerHandlers {
    listChannels: (req: Request, res: Response) => void;
    createChannel: (req: Request, res: Response) => void;
    getChannel: (req: Request, res: Response) => void;
    archiveChannel: (req: Request, res: Response) => void;
    listMessages: (req: Request, res: Response) => void;
    sendMessage: (req: Request, res: Response) => void | Promise<void>;
    ensureDmChannel: (req: Request, res: Response) => void;
    ensureTeamChannel: (req: Request, res: Response) => void;
    createHuddle: (req: Request, res: Response) => void;
    listAgents: (req: Request, res: Response) => void | Promise<void>;
    getAgentPresence: (req: Request, res: Response) => void | Promise<void>;
}
/**
 * Minimal directory entry surfaced by `GET /api/chat/agents`.
 *
 * Lives on the wire as-is; the /agents page renders one row per entry,
 * grouped by team. The fields match the join the controller performs
 * between {@link IAgentDirectoryProvider} (teams + members) and the
 * presence provider, with no extra denormalization.
 */
export interface ChatAgentDirectoryEntry {
    /** Member's `sessionName` — the agent's wire id, matches `Channel.agentSession`. */
    agentSession: string;
    /** Member display name (e.g. "Leo"). */
    name: string;
    /** Member role (e.g. "team-leader"). */
    role: string;
    /** Owning team id. */
    teamId: string;
    /** Owning team display name. */
    teamName: string;
    /** Stored agentStatus from the team file. */
    agentStatus: string;
    /** Stored workingStatus from the team file. */
    workingStatus: string;
    /** Optional emoji / avatar surfaced by the team file. */
    avatar?: string;
}
/**
 * Minimal storage surface the controller needs to enumerate agents.
 * Decoupled from `StorageService` so tests can pass a fixture without
 * spinning up SQLite + the team-file watcher.
 */
export interface IAgentDirectoryProvider {
    /**
     * Return all teams + members visible to the caller. The controller
     * filters/flattens; no need to pre-shape the response.
     */
    getTeams(): Promise<Array<{
        id: string;
        name: string;
        members: Array<{
            sessionName: string;
            name: string;
            role: string;
            agentStatus: string;
            workingStatus: string;
            avatar?: string;
        }>;
    }>>;
}
/** Wire shape returned by `GET /api/chat/presence/:agentId`. */
export interface ChatAgentPresenceDTO {
    agentSession: string;
    status: 'online' | 'busy' | 'offline' | 'starting';
    lastSeenAt: number | null;
}
/**
 * Presence resolver. Returns the live status + last-seen timestamp for
 * an agent session. Default implementation (`createDefaultPresenceProvider`)
 * delegates to the orchestrator-status helper; tests inject a stub.
 */
export interface IAgentPresenceProvider {
    getPresence(agentSession: string): Promise<ChatAgentPresenceDTO>;
}
/**
 * Optional wiring for real-time fan-out + agent dispatch. Leave undefined
 * in REST-only tests; supply both in the live server.
 *
 * Phase F — `directory` + `presence` providers are needed only by the
 * new `/agents`, `/presence/:id`, and `/channels/dm/ensure` endpoints.
 * When omitted, those endpoints return HTTP 503 `directory_unavailable`
 * so REST-only tests can keep mounting the router without wiring storage.
 */
export interface ChatV2ControllerDeps {
    gateway?: ChatV2Gateway;
    dispatcher?: ChatV2DispatcherService;
    directory?: IAgentDirectoryProvider;
    presence?: IAgentPresenceProvider;
}
/**
 * Build Express-compatible handlers for the chat-v2 endpoints.
 *
 * @param service - A configured ChatV2Service
 * @param deps    - Optional gateway/dispatcher for realtime wiring
 * @returns One handler per endpoint; wire into an Express Router
 */
export declare function createChatV2Controller(service: ChatV2Service, deps?: ChatV2ControllerDeps): ChatV2ControllerHandlers;
//# sourceMappingURL=chat-v2.controller.d.ts.map