/** * dsh `Agent` contract implemented as a thin forwarder to a durable * `CodexGateway`. The dsh host constructs the gateway (attach flow) and * supplies the live session/inbox/context association at registration. * * Semantics follow the verified app-server behavior: * - followup -> `turn/start` when idle, `thread/queue/add` when busy; * - steer -> `turn/steer` (or a fresh turn when idle); * - inject -> buffered and merged as leading text into the next submission; * - cancel -> best-effort `turn/interrupt` (thread/process stay alive). * * Intermediate Codex output is projected into the dsh session log as * log-only events (R1-A1, A2) via {@link GatewayEventForwarder}; image blocks * are resolved to Codex `localImage` inputs (Q3); images pass through * untouched (visual understanding is handled by the hosts' `ocgw-vision` skill). * * @module dsh-subagent-codex-plus/gateway/agent */ import type { Context } from '@deepseek-ai/cordis'; import type { Agent, AgentOptions, AgentStatus, CancelOptions, Inbox, InboxTarget } from '@deepseek-ai/dsh-agent'; import type { AgentCancelCause, Session, SessionId, UserMessage } from '@deepseek-ai/dsh-session'; import { CodexGateway } from './gateway.ts'; import { type GatewayEventForwarderOptions } from './events.ts'; import type { GatewayImageResolver } from './images.ts'; import type { GatewayUserInput } from './wire.ts'; /** Live dsh association supplied by the host when the agent is registered. */ export interface GatewayAgentHost { readonly id: SessionId; readonly session: Session; readonly inbox: Inbox; /** Agent-scoped context; bound by the attach wiring after construction. */ readonly ctx: Context | undefined; readonly options: AgentOptions; } export interface GatewayAgentOptions { /** Codex → dsh session event forwarding policy (R1-A1/A2). */ readonly eventForwarder?: GatewayEventForwarderOptions; /** Resolves dsh image blocks to Codex `localImage` inputs (Q3). */ readonly imageResolver?: GatewayImageResolver; } /** * Project one dsh user message onto gateway input blocks. Text blocks pass * through; image blocks are resolved asynchronously by the image resolver. */ export declare function resolveInputs(message: UserMessage, injected: readonly string[], resolver: GatewayImageResolver | undefined): Promise; /** Forwarder agent driving one Codex thread for one dsh session. */ export declare class GatewayAgent implements Agent { private readonly host; private readonly gateway; private readonly agentOptions; readonly id: SessionId; readonly options: AgentOptions; readonly session: Session; readonly inbox: Inbox; ctx: Context; private pendingInject; /** Prompts waiting for their Codex turn to start (released as durable `user/message`). */ private readonly pendingUserMessages; private readonly idleResolvers; private maintenanceSignal; /** Last status reported to the host; guards against duplicate `agent/status`. */ private reportedStatus; /** Codex → dsh event projection; also owns the dsh turn ordinal. */ private readonly forwarder; constructor(host: GatewayAgentHost, gateway: CodexGateway, agentOptions?: GatewayAgentOptions); get status(): AgentStatus; send(message: UserMessage, target: InboxTarget, wakeup: boolean): void; followup(message: UserMessage): void; steer(message: UserMessage): void; inject(message: UserMessage): void; /** Bind the agent-scoped context after construction (attach wiring). */ bindCtx(ctx: Context): void; cancel(cause: AgentCancelCause, options?: CancelOptions): void; whenIdle(): Promise; runMaintenance(task: (signal: AbortSignal) => Promise): Promise; private dispatch; /** Append the next buffered prompt as a durable surface `user/message`. */ private releasePendingPrompt; /** * Submit the next inbox-queued prompt once the gateway is idle. Called from * every turn transition, so a chain of queued prompts drains one turn at a * time (mirroring the loop agent's claim-on-boundary semantics). The * claimed message is held in `pendingUserMessages` until `turn/started` * releases it as the durable surface prompt. */ private drainQueue; private resolveAndRoute; private report; /** * Publish the agent status transition to the host. The dsh host derives * each client session's live `running` bit from `agent/status` events * (`host/session-status` frames); without them a client that learned the * agent was busy (session summary) never flips back to idle, so the chat * keeps rendering "thinking" and the stop button stays enabled while * `cancel()` has no active turn to interrupt. Emits only on real * transitions: the agent invariant rejects repeated no-op status events. */ private emitStatus; private reconcileIdle; }