UNPKG

agents

Version:

A home for your AI agents

332 lines (331 loc) • 10.9 kB
//#region src/harness/container/protocol.d.ts /** * The wire between `ContainerHarness` (in a Durable Object) and the harness * daemon (in the container). Isomorphic and dependency-free: the daemon * imports it in Node, the harness in Workers. * * One WebSocket per session, dialled by the object through * `ctx.container.getTcpPort(port)` at `/sessions/<id>`. The daemon speaks * first with `hello`; the object answers with `open` (when the daemon does * not hold the session yet) and `replay`, then delivers `prompt`s. * * Everything the daemon produces for a session is a numbered `frame`. The * numbers start at 1 for each session in each container, so a frame is * addressed by `(runtimeId, seq)`. The daemon keeps frames until they are * acknowledged, so an object that lost its socket (evicted, redeployed) * replays what it missed from its cursor. A different `runtimeId` in * `hello` means a different container: its numbering starts again, and * whatever the old container was doing is gone. * * @experimental The wire may change before it stabilizes. The object and the image are * a matched pair; a version mismatch is reported in `hello`. */ /** Bumped on any incompatible change. */ declare const CONTAINER_PROTOCOL_VERSION = 1; /** The daemon answers `200` here once it accepts connections. */ declare const CONTAINER_HEALTH_PATH = "/healthz"; /** The session socket: `/sessions/<encoded session id>`. */ declare const CONTAINER_SESSION_PATH = "/sessions/"; /** The shared secret the object passes on every request. */ declare const CONTAINER_TOKEN_HEADER = "x-cf-harness-token"; /** The environment variables the object starts the container with. */ declare const CONTAINER_ENV: { /** Shared secret for `CONTAINER_TOKEN_HEADER`. */ readonly token: "CF_HARNESS_TOKEN" /** This container's id. Echoed in `hello`. */; readonly runtimeId: "CF_HARNESS_RUNTIME_ID" /** The port the daemon listens on. */; readonly port: "CF_HARNESS_PORT"; }; /** WebSocket close code: a newer connection for the session replaced this one. */ declare const CLOSE_REPLACED = 4001; /** WebSocket close code: the request was not authorized. */ declare const CLOSE_UNAUTHORIZED = 4003; /** A JSON value on the wire. */ type JsonValue = | string | number | boolean | null | readonly JsonValue[] | { readonly [key: string]: JsonValue; }; /** One part of a prompt. */ type ContainerInputPart = | { readonly type: "text"; readonly text: string; } | { readonly type: "image" /** IANA media type, such as `image/png`. */; readonly mediaType: string /** Base64 bytes. */; readonly data: string; }; /** What a prompt carries: plain text, or parts. */ type ContainerInput = string | readonly ContainerInputPart[]; /** What a submission does when the session is already running. */ type ContainerWhenBusy = "followUp" | "steer"; /** * Per-session settings the object sends the adapter. `model` is the * adapter's own model name; `options` is opaque to everything but the * adapter. */ type ContainerSettings = { readonly model?: string; readonly options?: JsonValue; }; /** One part of a transcript message. */ type ContainerPart = | { readonly type: "text"; readonly text: string; } | { readonly type: "reasoning"; readonly text: string; } | { readonly type: "image"; readonly mediaType: string; readonly data: string; } | { readonly type: "tool"; readonly toolCallId: string; readonly toolName: string; readonly input: JsonValue; readonly state: "running" | "done" | "error"; readonly output?: JsonValue; }; /** * A transcript message, in the one format every container adapter * produces. An adapter emits a message again, under the same id, to update * it (a tool result arriving, for example). */ type ContainerMessage = { readonly id: string; readonly role: "user" | "assistant"; readonly parts: readonly ContainerPart[] /** The operation the message belongs to. */; readonly operationId?: string; readonly createdAt: number; }; /** What an adapter reports while it runs. */ type ContainerEvent = | /** Live text for an assistant message that is still being written. */ { readonly type: "text-delta"; readonly messageId: string; readonly delta: string; } /** Live reasoning for an assistant message that is still being written. */ | { readonly type: "reasoning-delta"; readonly messageId: string; readonly delta: string; } /** A finished or updated transcript message. Stored. */ | { readonly type: "message"; readonly message: ContainerMessage; } /** Token and cost figures the adapter reports, as the adapter counts them. */ | { readonly type: "usage"; readonly inputTokens?: number; readonly outputTokens?: number; readonly costUsd?: number; } /** A diagnostic line. */ | { readonly type: "log"; readonly level: "info" | "warn" | "error"; readonly message: string; } /** Anything adapter-specific, passed through untouched. */ | { readonly type: "raw"; readonly value: JsonValue; }; /** How an operation ended, as the daemon reports it. */ type ContainerOutcome = | { readonly status: "done"; readonly text: string; } | { readonly status: "unanswered"; readonly reason: string; }; /** What the daemon knows about one operation it was given. */ type DaemonOperation = | { readonly operationId: string; readonly status: "queued" | "running"; } | { readonly operationId: string; readonly status: "settled"; readonly outcome: ContainerOutcome; }; /** One numbered thing the daemon produced for a session. */ type DaemonFrame = | { readonly kind: "start"; readonly operationId: string; } | { readonly kind: "event"; readonly operationId: string | null; readonly event: ContainerEvent; } /** * Adapter resume state to keep. The object stores these entries in order * and hands all of them back in `open` on a new container. */ | { readonly kind: "persist"; readonly entries: readonly JsonValue[]; } | { readonly kind: "settle"; readonly operationId: string; readonly outcome: ContainerOutcome; }; /** The adapter running in the container. */ type AdapterInfo = { readonly id: string; readonly version: string; readonly capabilities: readonly string[]; }; /** Daemon → object. */ type DaemonMessage = | { readonly type: "hello"; readonly protocol: number; readonly runtimeId: string; readonly session: string; readonly adapter: AdapterInfo /** The generation of the open adapter session, or null if none is open. */; readonly open: | number | null /** The newest frame number for this session in this container. */; readonly lastSeq: number /** Every operation this container was given for the session. */; readonly operations: readonly DaemonOperation[]; } | { readonly type: "frame"; readonly seq: number; readonly frame: DaemonFrame; } /** Every kept frame after the `replay` cursor has been sent. */ | { readonly type: "caught-up"; readonly lastSeq: number; } | { readonly type: "error"; readonly message: string; }; /** * Most JSON characters the object puts in one `restore` message, and the * daemon in one `persist` frame (a single larger entry travels alone). * Keeps every message well inside WebSocket message limits. */ declare const MAX_CHUNK_CHARS: number; /** Object → daemon. */ type HostMessage = /** * Part of the resume state for the next `open` of `generation`. Sent * before that `open`, in order, so no single message grows with the * session's history. */ | { readonly type: "restore"; readonly generation: number; readonly entries: readonly JsonValue[]; } /** * Open the adapter session. Ignored when the same generation is already * open; a different generation closes the open one first. */ | { readonly type: "open"; readonly generation: number; readonly settings: ContainerSettings; /** * The last of the entries the adapter persisted for this session, * after any sent in `restore` messages for this generation. */ readonly restore: readonly JsonValue[]; } /** Send every kept frame after `after`, then live frames. */ | { readonly type: "replay"; readonly after: number; } /** Queue an operation. Idempotent on `operationId`. */ | { readonly type: "prompt"; readonly operationId: string; readonly input: ContainerInput; readonly whenBusy: ContainerWhenBusy; } /** Stop one operation, or every operation of the session. */ | { readonly type: "abort"; readonly operationId?: string; } | { readonly type: "configure"; readonly settings: ContainerSettings; } /** Frames up to `seq` are stored; the daemon may forget them. */ | { readonly type: "ack"; readonly seq: number; }; /** * Parse one message from the daemon. * * @param text - The raw WebSocket message. * @returns The message, or `undefined` when it is not one. */ declare function parseDaemonMessage(text: string): DaemonMessage | undefined; /** * Parse one message from the object. * * @param text - The raw WebSocket message. * @returns The message, or `undefined` when it is not one. */ declare function parseHostMessage(text: string): HostMessage | undefined; /** * The text of an input, with image parts left out. * * @param input - A prompt's input. * @returns Its text. */ declare function inputText(input: ContainerInput): string; /** * Split entries into groups whose JSON stays under `MAX_CHUNK_CHARS`. An * entry larger than that is a group of its own. * * @param entries - The entries, in order. * @returns The groups, in order. Empty for no entries. */ declare function chunkEntries(entries: readonly JsonValue[]): JsonValue[][]; //#endregion export { chunkEntries as C, parseHostMessage as E, MAX_CHUNK_CHARS as S, parseDaemonMessage as T, DaemonFrame as _, CONTAINER_HEALTH_PATH as a, HostMessage as b, CONTAINER_TOKEN_HEADER as c, ContainerInputPart as d, ContainerMessage as f, ContainerWhenBusy as g, ContainerSettings as h, CONTAINER_ENV as i, ContainerEvent as l, ContainerPart as m, CLOSE_REPLACED as n, CONTAINER_PROTOCOL_VERSION as o, ContainerOutcome as p, CLOSE_UNAUTHORIZED as r, CONTAINER_SESSION_PATH as s, AdapterInfo as t, ContainerInput as u, DaemonMessage as v, inputText as w, JsonValue as x, DaemonOperation as y }; //# sourceMappingURL=protocol-BEgrJYC5.d.ts.map