/** * The host⇄worker wire protocol: one string-valued enum of message tags per * direction, a payload map giving each tag its parameters (the single source * of truth), and the message unions derived from them. Everything in a * payload is plain JSON data by construction (the runtime materializes * script values before they reach a message; the host projects seam results * down to their JSON fields), so the structured-clone hop never meets a * value it cannot carry. * * Both directions are CLOSED (engine-owned): each side switches on `type` * and ends with `assertNever` — an unknown message is a protocol bug, never * something to skip silently. Senders go through a generic * `post(type, payload)` whose payload parameter is looked up from the map, * so a tag/payload mismatch is a compile error at the call site. * * @module @deepseek-ai/dsh-workflow-workerthread/protocol */ import type { WorkflowAgentEndInfo, WorkflowAgentInfo, WorkflowResult } from '@deepseek-ai/dsh-workflow' import type { ChildResult, ChildStartRequest } from './types.ts' /** Message tags the worker sends the host (the wire values are the tag strings). */ export enum WorkerToHostType { /** The startup handshake: the session is listening and awaits {@link HostToWorkerType.Go}. */ Ready = 'ready', /** Observer narration: a `phase(title)` call. */ Phase = 'phase', /** Observer narration: a `log(message)` call. */ Log = 'log', /** Observer lifecycle: one `agent()` call started a child. */ AgentStart = 'agent-start', /** Observer lifecycle: one `agent()` call settled. */ AgentEnd = 'agent-end', /** Child RPC: start a child on the host (answered by ChildStarted or ChildStartError). */ ChildStart = 'child-start', /** Child RPC: cancel a started child (fire-and-forget). */ ChildCancel = 'child-cancel', /** Child RPC: dispose a started child (answered by ChildDisposed). */ ChildDispose = 'child-dispose', /** The run's single terminal result. */ Result = 'result', } /** The payload each worker→host tag carries. */ export interface WorkerToHostPayloads { /** Ready carries nothing. */ [WorkerToHostType.Ready]: Record /** The phase title, verbatim. */ [WorkerToHostType.Phase]: { title: string } /** The logged message, verbatim. */ [WorkerToHostType.Log]: { message: string } /** The call's sequence number, label, phase, and child id. */ [WorkerToHostType.AgentStart]: { info: WorkflowAgentInfo } /** The call identity plus its outcome. */ [WorkerToHostType.AgentEnd]: { info: WorkflowAgentEndInfo } /** The RPC correlation id and the prompt plus validated options. */ [WorkerToHostType.ChildStart]: { callId: number; request: ChildStartRequest } /** The RPC correlation id and the cancel reason (undefined = unspecified). */ [WorkerToHostType.ChildCancel]: { callId: number; reason: string | undefined } /** The RPC correlation id of the child to dispose. */ [WorkerToHostType.ChildDispose]: { callId: number } /** The run's terminal outcome. */ [WorkerToHostType.Result]: { result: WorkflowResult } } /** Message tags the host sends the worker (the wire values are the tag strings). */ export enum HostToWorkerType { /** Releases the startup gate: run the script body. */ Go = 'go', /** Cancel the run: hooks start throwing and the script dies at its next await. */ Cancel = 'cancel', /** Child RPC reply: the start succeeded (exactly one of ChildStarted/ChildStartError per ChildStart). */ ChildStarted = 'child-started', /** Child RPC reply: the start was refused or threw. */ ChildStartError = 'child-start-error', /** Child RPC: a started child's result RESOLVED (its JSON projection). */ ChildSettled = 'child-settled', /** Child RPC: a started child's result REJECTED (an infrastructure fault, rendered). */ ChildFailed = 'child-failed', /** Child RPC reply: a requested disposal completed. */ ChildDisposed = 'child-disposed', } /** The payload each host→worker tag carries. */ export interface HostToWorkerPayloads { /** Go carries nothing. */ [HostToWorkerType.Go]: Record /** The cancel reason, canonical for the whole run. */ [HostToWorkerType.Cancel]: { reason: string } /** The RPC correlation id and the child agent's id (minted by the subagent seam). */ [HostToWorkerType.ChildStarted]: { callId: number; childId: string } /** The RPC correlation id and the rendered start failure. */ [HostToWorkerType.ChildStartError]: { callId: number; rendered: string } /** The RPC correlation id and the child's terminal result projection. */ [HostToWorkerType.ChildSettled]: { callId: number; result: ChildResult } /** The RPC correlation id and the rendered infrastructure fault. */ [HostToWorkerType.ChildFailed]: { callId: number; rendered: string } /** The RPC correlation id of the completed disposal. */ [HostToWorkerType.ChildDisposed]: { callId: number } } /** * One worker→host message of tag `T`; unparameterized, the closed union over * every tag (a discriminated union — `switch` on `type` narrows). */ export type WorkerToHostMessage = { [K in T]: { type: K } & WorkerToHostPayloads[K] }[T] /** * One host→worker message of tag `T`; unparameterized, the closed union over * every tag (a discriminated union — `switch` on `type` narrows). */ export type HostToWorkerMessage = { [K in T]: { type: K } & HostToWorkerPayloads[K] }[T]