/** * Codex app-server notification stream → dsh session log (R1-A1). * * Every Codex turn/item/delta is projected onto the dsh session's append-only * log as **log-only** events (`turn/start`, `turn/end`, `step/start`, * `step/end`, `assistant/chunk`, `tool/call`), which never carry a * `surfaceOp` and therefore never enter the model-visible surface (A2). * * Two message-producing events DO join the surface (`surfaceOp: 'append'`): * the user prompt is recorded by the agent when its turn starts, and on * `turn/completed` the streamed deltas are assembled into a durable * `assistant/message`. Without that durable message the chat fold has no * settled node and renders every finished reply as a synthetic interrupted * node ("已停止"); with it the reply renders as a normal completed bubble * while the session log still preserves the full Codex intermediate * transcript for UI, tooling, and replay. * * Mapping (verified against a real `codex app-server --stdio` stream, see * TECH-VERIFICATION §3.6): * - `turn/started` → `turn/start` + `step/start` * - `turn/completed` → `step/end` + `turn/end` * - `item/reasoning/textDelta` → `assistant/chunk` (`reasoning-delta`) * - `item/agentMessage/delta` → `assistant/chunk` (`text-delta`) * - `item/started|completed` with a tool item → `tool/call` * * @module dsh-subagent-codex-plus/gateway/events */ import type { Session, UserMessage } from '@deepseek-ai/dsh-session'; import type { CodexGatewayNotification } from './wire.ts'; /** Forwarding policy for the Codex → dsh session event stream. */ export interface GatewayEventForwarderOptions { /** Append Codex turn/step/chunk/tool events to the session log (A1). */ readonly enabled: boolean; /** Forwarding-side diagnostics sink (defaults to no-op). */ readonly onError?: (message: string) => void; } export declare const DEFAULT_EVENT_FORWARDER_OPTIONS: GatewayEventForwarderOptions; /** * Project one app-server notification onto the bound dsh session log. Safe to * call for every observed notification; unrecognized methods are ignored. * Append failures (e.g. a disposed session) are swallowed so the gateway * stream never dies from a logging side effect. */ export declare class GatewayEventForwarder { private readonly session; private readonly options; /** dsh turn ordinal for the active Codex turn (1-based). */ private turn; /** dsh step ordinal within the active turn (1; one step per Codex turn). */ private step; private activeTurnId; private stepOpen; /** * Content snapshots of steps closed by a mid-turn steer. When a steer * races turn completion the final step streams nothing, and the durable * reply must settle the last step that actually produced content instead. */ private readonly stepContent; /** Reasoning deltas keyed by Codex item id, in first-seen order. */ private readonly reasoningByItem; private readonly reasoningOrder; /** Text deltas keyed by content index (agentMessage deltas carry none). */ private readonly textByIndex; private readonly textOrder; constructor(session: Session, options?: GatewayEventForwarderOptions); private readonly appendBound; /** Highest turn ordinal already present in the durable event log (0 when empty). */ private static recordedTurnCount; forward(notification: CodexGatewayNotification): void; private dispatch; private onTurnStarted; private onTurnCompleted; private onItemStarted; private onTextDelta; private onReasoningDelta; /** * Land a mid-turn steer on the surface as its own step boundary. * * Codex `turn/steer` redirects the ACTIVE turn without a new * `turn/started`, so without an explicit boundary every post-steer chunk * folds back into the pre-steer assistant node (same turn:step key) and * renders ABOVE the inserted message. Closing the step, appending the * inserted prompt, then opening a fresh step gives the fold the ordering * the user expects: pre-steer output, inserted prompt, post-steer output. */ appendSteerMessage(message: UserMessage): void; /** * Assemble the streamed deltas into the durable `assistant/message` that * closes the step on the surface. Interrupted turns carry `interrupted` * exactly like a cancelled dsh turn, so the UI marks only genuinely * aborted replies; completed turns settle normally. */ private appendFinalMessage; /** Assemble the current step's accumulated reasoning/text blocks. */ private collectBlocks; private resetAccumulators; private recordToolCall; private appendChunk; private openStep; private closeStep; private append; /** Append a message-producing event on the model-visible surface (A2). */ private appendSurface; /** dsh turn ordinal the next `turn/started` will open (for inbox claims). */ nextTurnOrdinal(): number; }