1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
|
# `@dispatch/wire` — in-repo reference (read THIS, not node_modules)
> MIRRORS the backend's `@dispatch/wire` package source so headless FE agents can read the wire
> 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.
```ts
/**
* @dispatch/wire — pure wire types shared by the kernel, the transport
* contract, and out-of-repo clients (the web frontend).
*
* Types ONLY: zero runtime, zero `@dispatch/*` dependencies, so a client can
* depend on the wire without pulling the kernel runtime.
*/
// ─── Conversation model ─────────────────────────────────────────────────────
/** Who produced a message. */
export type Role = "system" | "user" | "assistant" | "tool";
/** Opaque identifier for a turn (one user→assistant cycle). */
export type TurnId = string & { readonly __brand: "TurnId" };
/** Opaque identifier for a step (one LLM round-trip within a turn). */
export type StepId = string & { readonly __brand: "StepId" };
/**
* A chunk is one ordered piece of a message — the atomic unit of the
* append-only conversation log. Discriminated by `type`.
*/
export type Chunk =
| TextChunk
| ThinkingChunk
| ToolCallChunk
| ToolResultChunk
| ErrorChunk
| SystemChunk;
/** A piece of plain text content from the assistant or user. */
export interface TextChunk {
readonly type: "text";
readonly text: string;
}
/** A piece of model reasoning / thinking content (e.g. extended thinking). */
export interface ThinkingChunk {
readonly type: "thinking";
readonly text: string;
}
/**
* A model's request to run a tool. The kernel routes by `name`; the tool
* implementation never sees this directly — it receives parsed `input` via
* `ToolContract.execute`.
*/
export interface ToolCallChunk {
readonly type: "tool-call";
readonly toolCallId: string;
readonly toolName: string;
readonly input: unknown;
}
/**
* The result of a tool execution, attributed to the originating tool-call id.
* The kernel guarantees every tool-call chunk gets exactly one result chunk
* (synthesized if interrupted — see reconcile).
*/
export interface ToolResultChunk {
readonly type: "tool-result";
readonly toolCallId: string;
readonly toolName: string;
readonly content: string;
readonly isError: boolean;
}
/** An error that occurred during generation or tool dispatch. */
export interface ErrorChunk {
readonly type: "error";
readonly message: string;
readonly code?: string;
}
/**
* A system-injected message (e.g. system prompt, context assembly output).
* Kept distinct from text so the log records provenance.
*/
export interface SystemChunk {
readonly type: "system";
readonly text: string;
}
/**
* A chat message: a role plus an ordered sequence of chunks. Messages are the
* unit passed to and from the provider; chunks are the unit persisted and
* rendered.
*/
export interface ChatMessage {
readonly role: Role;
readonly chunks: readonly Chunk[];
}
/**
* 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 pure content unit, unchanged — `Chunk` itself never
* carries storage metadata (it is also passed to/from the provider, which has
* no use for a cursor).
*/
export interface StoredChunk {
readonly seq: number;
readonly role: Role;
readonly chunk: Chunk;
}
// ─── Usage ──────────────────────────────────────────────────────────────────
/**
* Token usage counters for a single step. All fields are counts of tokens.
* Cache fields are optional because not all providers expose cache metrics.
*/
export interface Usage {
readonly inputTokens: number;
readonly outputTokens: number;
readonly cacheReadTokens?: number;
readonly cacheWriteTokens?: number;
}
// ─── Outward events ─────────────────────────────────────────────────────────
/**
* The union of all events the runtime emits outward during a turn.
* Consumers (transport, persistence, notifications) pattern-match on `type`.
*/
export type AgentEvent =
| StatusEvent
| TurnStartEvent
| TurnTextDeltaEvent
| TurnReasoningDeltaEvent
| TurnToolCallEvent
| TurnToolResultEvent
| TurnToolOutputEvent
| TurnUsageEvent
| TurnErrorEvent
| TurnDoneEvent
| TurnSealedEvent;
/** Status change for a conversation (e.g. idle → running). */
export interface StatusEvent {
readonly type: "status";
readonly conversationId: string;
readonly status: string;
}
/** A turn has begun. */
export interface TurnStartEvent {
readonly type: "turn-start";
readonly conversationId: string;
readonly turnId: string;
}
/** Incremental text content from the model during a turn. */
export interface TurnTextDeltaEvent {
readonly type: "text-delta";
readonly conversationId: string;
readonly turnId: string;
readonly delta: string;
}
/** Incremental reasoning / thinking content during a turn. */
export interface TurnReasoningDeltaEvent {
readonly type: "reasoning-delta";
readonly conversationId: string;
readonly turnId: string;
readonly delta: string;
}
/** The model has requested a tool to be run. */
export interface TurnToolCallEvent {
readonly type: "tool-call";
readonly conversationId: string;
readonly turnId: string;
readonly toolCallId: string;
readonly toolName: string;
readonly input: unknown;
}
/** A tool has completed execution. */
export interface TurnToolResultEvent {
readonly type: "tool-result";
readonly conversationId: string;
readonly turnId: string;
readonly toolCallId: string;
readonly toolName: string;
readonly content: string;
readonly isError: boolean;
}
/** Streaming output from a tool execution (e.g. shell stdout/stderr). */
export interface TurnToolOutputEvent {
readonly type: "tool-output";
readonly conversationId: string;
readonly turnId: string;
readonly toolCallId: string;
readonly data: string;
readonly stream: "stdout" | "stderr";
}
/** Token usage for the current step or turn. */
export interface TurnUsageEvent {
readonly type: "usage";
readonly conversationId: string;
readonly turnId: string;
readonly usage: Usage;
}
/** An error occurred during the turn. */
export interface TurnErrorEvent {
readonly type: "error";
readonly conversationId: string;
readonly turnId: string;
readonly message: string;
readonly code?: string;
}
/** The turn has completed (model finished generating). */
export interface TurnDoneEvent {
readonly type: "done";
readonly conversationId: string;
readonly turnId: string;
readonly reason: string;
}
/**
* The turn has been sealed — all chunks persisted, history is final.
* This is the hook point for post-turn extensions (compaction, cache-warm).
*/
export interface TurnSealedEvent {
readonly type: "turn-sealed";
readonly conversationId: string;
readonly turnId: string;
}
```
|