summaryrefslogtreecommitdiffhomepage
path: root/.dispatch
diff options
context:
space:
mode:
Diffstat (limited to '.dispatch')
-rw-r--r--.dispatch/transport-contract.reference.md14
-rw-r--r--.dispatch/wire.reference.md113
2 files changed, 55 insertions, 72 deletions
diff --git a/.dispatch/transport-contract.reference.md b/.dispatch/transport-contract.reference.md
index ef0235a..fcc2cbf 100644
--- a/.dispatch/transport-contract.reference.md
+++ b/.dispatch/transport-contract.reference.md
@@ -5,15 +5,15 @@
> hangs on a permission prompt). Your CODE still imports `@dispatch/transport-contract` normally —
> this file is for READING only.
>
-> **Orchestrator:** SNAPSHOT of `[email protected]`. Regenerate whenever it changes.
-> Depends on `@dispatch/[email protected]` (see `wire.reference.md`) + `@dispatch/ui-contract`
+> **Orchestrator:** SNAPSHOT of `[email protected]`. Regenerate whenever it changes.
+> Depends on `@dispatch/[email protected]` (see `wire.reference.md`) + `@dispatch/ui-contract`
> (see `ui-contract.reference.md`).
>
-> **0.3.0 change (live metrics):** no shape change HERE — this contract's own types are identical.
-> It re-exports the bumped `@dispatch/wire`, whose `AgentEvent` union gained a `step-complete`
-> variant and timing fields on `usage`/`tool-result`/`done`. So the `chat.delta` events you stream
-> over WS now also carry the live metrics. See `frontend-metrics-handoff.md` for the full guide.
-> (0.2.0: tool-call `stepId` grouping.)
+> **0.2.0 change (step grouping):** no shape change HERE — this contract's own types are
+> identical. It only re-exports the bumped `@dispatch/wire`, whose `AgentEvent` tool variants
+> now carry a required `stepId` and whose tool `Chunk`s carry an optional `stepId`. The
+> `chat.delta` events streamed over WS and the `ConversationHistoryResponse.chunks` you already
+> consume therefore now carry the step grouping key (see `wire.reference.md`).
## Endpoints (backend, confirmed live — CORS wildcard `*`, HTTP port 24203, WS port 24205)
diff --git a/.dispatch/wire.reference.md b/.dispatch/wire.reference.md
index 7814bc3..ed95351 100644
--- a/.dispatch/wire.reference.md
+++ b/.dispatch/wire.reference.md
@@ -4,14 +4,13 @@
> types WITHOUT following the `file:` dep symlink out of this repo (which hangs on a permission
> prompt). Your CODE still imports `@dispatch/wire` normally — this file is for READING only.
>
-> **Orchestrator:** SNAPSHOT of `[email protected]`. Regenerate whenever `@dispatch/wire` changes.
+> **Orchestrator:** SNAPSHOT of `[email protected]`. Regenerate whenever `@dispatch/wire` changes.
>
-> **0.3.0 change (live metrics — see `frontend-metrics-handoff.md` for the full guide):** new
-> `TurnStepCompleteEvent` (`type:"step-complete"`) in the `AgentEvent` union with per-step
-> `ttftMs?`/`decodeMs?`/`genTotalMs?`; `TurnUsageEvent` gained `stepId?`; `TurnToolResultEvent`
-> gained `durationMs?` (tool exec time); `TurnDoneEvent` gained `durationMs?` (turn wall-clock) +
-> `usage?` (turn total). All additive/optional — existing handling is unaffected. (0.2.0 added
-> `stepId` for tool-call grouping.)
+> **0.2.0 change (step grouping):** `ToolCallChunk`/`ToolResultChunk` gained an OPTIONAL
+> `stepId?: StepId`; `TurnToolCallEvent`/`TurnToolResultEvent` gained a REQUIRED `stepId: StepId`.
+> A `StepId` is the per-step grouping key for batched/parallel tool calls — group by equality.
+> Live: read `event.stepId`. Replay: read `storedChunk.chunk.stepId` (NOT the envelope; absent on
+> pre-0.2.0 rows / non-tool chunks — tolerate absence). `StoredChunk` envelope is UNCHANGED.
```ts
/**
@@ -76,7 +75,17 @@ export interface ToolCallChunk {
readonly toolCallId: string;
readonly toolName: string;
readonly input: unknown;
- /** Step grouping key (generation provenance). Optional — tolerate absence. */
+ /**
+ * The step that produced this call — generation provenance stamped by the
+ * runtime when the model emits the call (NOT storage metadata like `seq`,
+ * which is why it lives on the chunk and travels with it through persistence
+ * and replay). Tool calls a model batches together in one step share the same
+ * `stepId`: the grouping key for rendering a parallel batch as one unit, and
+ * equal to the `stepId` on the matching `tool-call` AgentEvent. Optional:
+ * absent on chunks reconstructed outside a turn and on rows persisted before
+ * this field existed, so a consumer must tolerate its absence (render
+ * ungrouped).
+ */
readonly stepId?: StepId;
}
@@ -91,7 +100,14 @@ export interface ToolResultChunk {
readonly toolName: string;
readonly content: string;
readonly isError: boolean;
- /** Step grouping key — equals the originating call's. Optional. */
+ /**
+ * The step that produced the originating call — equal to the `stepId` on the
+ * matching `tool-call` chunk (same `toolCallId`) and on the `tool-result`
+ * AgentEvent, so a consumer groups a step's calls with their results.
+ * Generation provenance, not storage metadata (see `ToolCallChunk.stepId`).
+ * Optional for the same reasons; `reconcile` copies it from the originating
+ * call onto a synthesized (interrupted) result.
+ */
readonly stepId?: StepId;
}
@@ -122,10 +138,16 @@ export interface ChatMessage {
}
/**
- * A persisted chunk plus its sync metadata: `{ seq, role, chunk }`. `seq` is the
- * per-conversation sync cursor (envelope); a tool chunk's `stepId` rides on
- * `chunk` (generation provenance). NOTE: usage/timing metrics are NOT persisted —
- * they exist only on the live stream (see `frontend-metrics-handoff.md`).
+ * A persisted chunk plus its sync metadata. The append-only conversation log
+ * stamps every chunk with a monotonic, gap-free, per-conversation `seq` (the
+ * sync cursor, assigned in append order) and records the `role` of the message
+ * it belongs to. This makes a flat seq-ordered stream both incrementally
+ * syncable ("give me chunks after seq N") and regroupable into messages by the
+ * client. `chunk` is the content unit — `Chunk` carries no storage/sync cursor
+ * (`seq` lives here on the envelope, not on the chunk, since it is assigned by
+ * the store and the provider has no use for it). A chunk MAY still carry
+ * generation provenance assigned at production time (e.g. a tool chunk's
+ * `stepId`), which is intrinsic to the content and so travels with it.
*/
export interface StoredChunk {
readonly seq: number;
@@ -161,7 +183,6 @@ export type AgentEvent =
| TurnToolResultEvent
| TurnToolOutputEvent
| TurnUsageEvent
- | TurnStepCompleteEvent
| TurnErrorEvent
| TurnDoneEvent
| TurnSealedEvent;
@@ -201,7 +222,13 @@ export interface TurnToolCallEvent {
readonly type: "tool-call";
readonly conversationId: string;
readonly turnId: string;
- /** Step grouping key (matches the tool-result event + persisted chunk). */
+ /**
+ * The step that produced this call. Tool calls a model batches together in
+ * one step share the same `stepId` — the grouping key for rendering a
+ * parallel batch as one unit. Matches the `stepId` on the matching
+ * `tool-result` event and on the persisted tool chunk
+ * (`StoredChunk.chunk.stepId`).
+ */
readonly stepId: StepId;
readonly toolCallId: string;
readonly toolName: string;
@@ -213,18 +240,17 @@ export interface TurnToolResultEvent {
readonly type: "tool-result";
readonly conversationId: string;
readonly turnId: string;
- /** Step grouping key — equals the matching tool-call's. */
+ /**
+ * The step that produced the originating call. Equal to the `stepId` on the
+ * matching `tool-call` event (same `toolCallId`) and on the persisted tool
+ * chunk (`StoredChunk.chunk.stepId`), so a client groups a step's calls with
+ * their results.
+ */
readonly stepId: StepId;
readonly toolCallId: string;
readonly toolName: string;
readonly content: string;
readonly isError: boolean;
- /**
- * How long the tool took to execute (dispatch → result), in milliseconds —
- * the backend's authoritative execution time, distinct from any client-side
- * wall-clock. Optional: present only when the runtime was given a clock.
- */
- readonly durationMs?: number;
}
/** Streaming output from a tool execution (e.g. shell stdout/stderr). */
@@ -242,42 +268,9 @@ export interface TurnUsageEvent {
readonly type: "usage";
readonly conversationId: string;
readonly turnId: string;
- /**
- * The step this usage report belongs to, so a consumer can attribute tokens
- * per step (and join with the matching `step-complete` timing by `stepId`).
- * Optional: absent when the runtime had no step context.
- */
- readonly stepId?: StepId;
readonly usage: Usage;
}
-/**
- * A step (one LLM round-trip) has completed — the authoritative per-step metrics
- * packet, emitted once at the step's end (after the generation stream finishes),
- * so its timing is final (unlike `usage`, which may arrive mid-stream). Carries
- * the step's generation timing; join to the step's tokens via `stepId` on the
- * `usage` event. All timing fields are optional: present only when the runtime
- * was given a clock, and `ttftMs`/`decodeMs` additionally require that a first
- * content token (text or reasoning) was observed this step.
- */
-export interface TurnStepCompleteEvent {
- readonly type: "step-complete";
- readonly conversationId: string;
- readonly turnId: string;
- readonly stepId: StepId;
- /** Time to first token: stream start → first text/reasoning delta. */
- readonly ttftMs?: number;
- /** Decode time: first token → stream end (generation total − TTFT). */
- readonly decodeMs?: number;
- /**
- * Total generation time for the step: stream start → stream end. Present
- * whenever a clock was available, even if no first token was seen (then
- * `ttftMs`/`decodeMs` are absent). When a first token was seen,
- * `genTotalMs === ttftMs + decodeMs`.
- */
- readonly genTotalMs?: number;
-}
-
/** An error occurred during the turn. */
export interface TurnErrorEvent {
readonly type: "error";
@@ -293,16 +286,6 @@ export interface TurnDoneEvent {
readonly conversationId: string;
readonly turnId: string;
readonly reason: string;
- /**
- * Total wall-clock duration of the turn (turn start → turn end), in
- * milliseconds. Optional: present only when the runtime was given a clock.
- */
- readonly durationMs?: number;
- /**
- * Aggregate token usage across all steps in the turn — a convenience total so
- * a consumer need not sum the per-step `usage` events. Optional.
- */
- readonly usage?: Usage;
}
/**