@workerdeck/core 0.22.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.d.mts CHANGED
@@ -5,50 +5,17 @@ import { SandboxEngine, SandboxVfs } from "@workerdeck/sandbox";
5
5
  import { Readable, Writable } from "node:stream";
6
6
 
7
7
  //#region src/lib/attachments.d.ts
8
- /**
9
- * An attachment plus its bytes — what the host hands a runner at send time.
10
- *
11
- * The split matters: `data` goes into the message the engine sends and nowhere
12
- * else. What the runner emits into the seq-numbered event log is the
13
- * {@link MessageAttachment} half, so replay and parking stay cheap (see the
14
- * protocol's note on why the bytes are not on the wire).
15
- */
16
8
  type AttachmentInput = MessageAttachment & {
17
- /** Base64, no data-URL prefix. */data: string;
9
+ data: string;
18
10
  };
19
- /**
20
- * How an attachment reaches the model. Not every file can be handed to a model
21
- * as itself: images and PDFs have native block types, anything textual can be
22
- * inlined, and the rest has no representation at all — so uploads of it are
23
- * refused at the door rather than silently dropped from the message.
24
- */
25
11
  type AttachmentKind = 'image' | 'document' | 'text';
26
- /** Strips any `; charset=…` parameter and lowercases. */
27
12
  declare function normalizeMediaType(mediaType: string): string;
28
- /** How this media type can be sent, or null if it can't be. */
29
13
  declare function attachmentKind(mediaType: string): AttachmentKind | null;
30
- /** Human-readable list for the 415 an unsupported upload gets. */
31
14
  declare const SUPPORTED_ATTACHMENT_TYPES: string;
32
- /**
33
- * Anthropic content blocks for a set of attachments, in the given order.
34
- *
35
- * Blocks lead the message and the user's text follows: the model reads the
36
- * picture, then the instruction about it. Text files are inlined in a named
37
- * envelope rather than as a bare block, so "here is my config" doesn't read as
38
- * something the user typed.
39
- *
40
- * Structurally typed — `packages/core` models Anthropic content the way
41
- * `packages/protocol` does, and the caller casts into the SDK's own param type.
42
- */
43
15
  declare function attachmentContentBlocks(attachments: readonly AttachmentInput[]): Array<Record<string, unknown>>;
44
- /** Strip the bytes: the log-safe half of an attachment. */
45
16
  declare function attachmentRef(attachment: AttachmentInput): MessageAttachment;
46
17
  //#endregion
47
18
  //#region src/executors/tool-executor.d.ts
48
- /**
49
- * Result of one tool execution, whenever it arrives. `failed` is a normal
50
- * outcome the agent loop adapts to — not an exception.
51
- */
52
19
  type ToolExecutionResult = {
53
20
  status: 'ok';
54
21
  output: unknown;
@@ -60,10 +27,10 @@ type ToolExecutionResult = {
60
27
  logs?: string[];
61
28
  };
62
29
  type ToolExecutionCall = {
63
- /** Stable, persisted correlation id. Results are matched and applied by it. */executionId: string;
64
- sessionId: string; /** Tool name, e.g. 'eval_script'. */
65
- tool: string; /** Validated tool input. */
66
- input: unknown; /** Scoped scratch filesystem for this execution's thread. */
30
+ executionId: string;
31
+ sessionId: string;
32
+ tool: string;
33
+ input: unknown;
67
34
  vfs?: SandboxVfs;
68
35
  limits?: {
69
36
  timeoutMs?: number;
@@ -71,11 +38,6 @@ type ToolExecutionCall = {
71
38
  };
72
39
  signal?: AbortSignal;
73
40
  };
74
- /**
75
- * Dispatch outcome. `settled` carries the result inline; `pending` means it
76
- * arrives out-of-band later, keyed by executionId — the shape that lets a
77
- * deferred or remote executor drop in without touching the runner or protocol.
78
- */
79
41
  type ToolExecutionDispatch = {
80
42
  executionId: string;
81
43
  status: 'settled';
@@ -84,70 +46,31 @@ type ToolExecutionDispatch = {
84
46
  executionId: string;
85
47
  status: 'pending';
86
48
  };
87
- /**
88
- * The seam between the agent loop and wherever code actually runs — in-process
89
- * QuickJS, a browser tab over the WS bridge, or a managed sandbox. Backends are
90
- * interchangeable and selected by context.
91
- */
92
- /**
93
- * How an executor will handle one specific call, asked before dispatch so the
94
- * runner can announce it on `execution_dispatched` (which is emitted before the
95
- * call goes out, so a bridged request never precedes its own record).
96
- *
97
- * Per **call**, not per executor: an executor that routes by tool name can send
98
- * `eval_script` to the in-process sandbox and a long-running tool to a remote
99
- * worker, and only the latter should park the session.
100
- */
101
49
  type ToolExecutionProfile = {
102
- /** Reported on `execution_dispatched`; falls back to the runner's configured default. */backend?: ToolExecutionBackend;
103
- /**
104
- * True when this execution may outlive not just the turn but the live runner:
105
- * the session parks (state persisted, runner torn down) and the result arrives
106
- * out of band, keyed by `executionId`.
107
- */
50
+ backend?: ToolExecutionBackend;
108
51
  deferred?: boolean;
109
- /** Advisory deadline, published as `expiresAt`. For a deferred execution the
110
- * timer itself belongs to the host — the runner may be gone when it fires. */
111
52
  timeoutMs?: number;
112
53
  };
113
54
  interface ToolExecutor {
114
- /** Describe what this call will be: backend, deferredness, deadline. Omitted =
115
- * an in-band execution on the runner's configured backend. */
116
55
  describe?(call: ToolExecutionCall): ToolExecutionProfile;
117
56
  dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch>;
118
57
  }
119
58
  //#endregion
120
59
  //#region src/runner-interface.d.ts
121
60
  type SessionEventListener = (event: SessionEvent) => void;
122
- /** One deferred execution a parked session is waiting on. */
123
61
  type ParkedExecution = {
124
62
  executionId: string;
125
- toolName: string; /** Epoch ms the host's execution watchdog should fire at, when the backend set one. */
63
+ toolName: string;
126
64
  expiresAt?: number;
127
65
  };
128
- /**
129
- * Everything needed to rebuild a torn-down session under the same id — the durable
130
- * half of deferred execution. The engine-neutral fields are what the host persists,
131
- * indexes, and replays; `state` is the engine's own continuation state (for the
132
- * provider engine, its ModelMessage history) and is **opaque** outside it. Keeping
133
- * it opaque is what lets `packages/server` persist a provider session without ever
134
- * importing a model SDK.
135
- *
136
- * Must stay JSON-serializable end to end: a durable store round-trips it verbatim.
137
- * "Serializable" here means round-trips *unchanged* — a Date, a Map, or a typed
138
- * array inside `state` survives `JSON.stringify` as something else and rehydrates
139
- * wrong. Only the in-memory store hides that, by never serializing at all.
140
- */
141
66
  type RunnerSnapshot = {
142
- /** Engine that produced it. Rehydrating into a different one is refused. */engine: ProfileEngine; /** Session id the rebuilt runner must adopt. */
67
+ engine: ProfileEngine;
143
68
  id: string;
144
69
  createdAt: number;
145
- /** Last emitted seq — the rebuilt runner continues numbering from here, so a
146
- * client reattaching with `afterSeq` sees one unbroken stream. */
147
- seq: number; /** The seq-numbered event log, replayed to clients on rehydration. */
148
- events: SessionEvent[]; /** Scratch filesystem contents, for engines that have one. */
149
- vfs?: Record<string, string>; /** The deferred executions this session parked on. */
150
- parked: ParkedExecution[]; /** Engine-private continuation state. Never inspected by the host. */
70
+ seq: number;
71
+ events: SessionEvent[];
72
+ vfs?: Record<string, string>;
73
+ parked: ParkedExecution[];
151
74
  state: unknown;
152
75
  };
153
76
  type PermissionDecision = {
@@ -158,156 +81,36 @@ type PermissionDecision = {
158
81
  message?: string;
159
82
  interrupt?: boolean;
160
83
  };
161
- /**
162
- * Engine-independent runner surface — exactly what the server and queue consume.
163
- * `SessionRunner` (Claude / Agent SDK) implements it today; additional engines
164
- * implement the same contract and are selected behind it. Engine-specific
165
- * machinery (SDK options, approval callbacks, input-queue shapes) stays inside
166
- * the implementations.
167
- */
168
84
  interface Runner {
169
85
  readonly id: string;
170
86
  readonly pendingApprovals: PermissionRequest[];
171
- /** The session's scratch filesystem, when its engine has one. The server's
172
- * file routes (GET /sessions/:id/files[...]) read it to serve deliverables;
173
- * engines without a VFS (the Claude CLI engine) simply don't expose it. */
174
87
  readonly vfs?: SandboxVfs;
175
- /** Begin the session. Idempotent; returns the run promise (resolves when the run ends). */
176
88
  start(): Promise<void>;
177
89
  info(): SessionInfo;
178
- /** Replay buffered events with seq > afterSeq, then deliver live events. Returns unsubscribe.
179
- *
180
- * `coalesceReplay` drops state readings superseded later in the same replay —
181
- * the fifty stale context/rate-limit polls a long session accumulates, which
182
- * a client otherwise applies one by one and *renders*, counting its usage
183
- * meters up through the session's history on every attach. Opt-in, and the
184
- * default must stay off: it is only sound for a consumer whose handling of
185
- * those events is last-write-wins, and `parking.ts` — which subscribes from
186
- * seq 0 — branches on `status_changed` instead. Live events are never
187
- * affected; this touches the buffered replay alone.
188
- *
189
- * `truncateResults` delivers an oversized `tool_result` block as its head plus
190
- * the markers that say so (protocol's {@link TOOL_RESULT_HEAD_CHARS}), leaving
191
- * the whole thing one fetch away. Measured, that is 68% of a long session's
192
- * attach in three frames. Opt-in for the same reason and with one extra
193
- * condition: **the opt-in must be issued by the unit that renders**, because
194
- * `client` and `react` are separate packages an embedder can skew, and a
195
- * client that asked for heads without knowing how to fetch the rest would show
196
- * one as though it were the whole result. Live events are untouched — a result
197
- * arriving while you watch is already on screen — and so is the stored log,
198
- * which parking snapshots and the fetch route both read.
199
- *
200
- * `imageRefs` replaces a `tool_result`'s base64 image parts with `image_ref`
201
- * addresses (protocol's {@link ImageRefPart}), their bytes one REST fetch
202
- * away. Opt-in under the same rule — issued by the unit that renders — but
203
- * unlike the other two it applies to **live events as well as the replay**,
204
- * because the client's one render path is ref-then-fetch and bytes on a live
205
- * event would only be discarded or pinned. Measured, this is 91% of all
206
- * tool-result payload and 0% of what any client draws. The stored log keeps
207
- * every byte, which is what the fetch route serves back. */
208
90
  subscribe(listener: SessionEventListener, afterSeq?: number, options?: {
209
91
  coalesceReplay?: boolean;
210
92
  truncateResults?: boolean;
211
93
  imageRefs?: boolean;
212
94
  }): () => void;
213
- /** One buffered event by seq, or undefined — the read side of the log the
214
- * replay already walks.
215
- *
216
- * Optional, like every member added after `Runner` became public API: an
217
- * out-of-tree runner that declines it declines only the on-demand tool result
218
- * with it (the route 404s), which is exactly the degradation a runner with no
219
- * `truncateResults` support wants anyway.
220
- *
221
- * Deliberately **not** a "give me the whole log" accessor. The one caller
222
- * needs a single event by a seq a client is holding, and a method that handed
223
- * out the array would invite a second copy of the bytes this feature exists
224
- * to stop shipping. */
225
95
  eventAt?(seq: number): SessionEvent | undefined;
226
- /** Queue a user message for the session (starts the next turn when idle).
227
- * `attachments` carry their bytes to the engine and their reference to the
228
- * event log (see {@link AttachmentInput}). */
229
96
  sendMessage(text: string, attachments?: readonly AttachmentInput[]): void;
230
- /** Live MCP server status. Resolves undefined when the engine cannot answer
231
- * (no MCP surface, or a fake query in tests); omitted entirely by engines that
232
- * have no MCP at all. */
233
97
  mcpServers?(): Promise<McpServerStatusInfo[] | undefined>;
234
- /** Reconnect one MCP server by name. Throws if it fails. */
235
98
  reconnectMcpServer?(name: string): Promise<void>;
236
- /** Enable or disable one MCP server by name. Throws if it fails. */
237
99
  setMcpServerEnabled?(name: string, enabled: boolean): Promise<void>;
238
- /** Set (or clear, with undefined) the host's display title — `meta.title`, which
239
- * `info().title` prefers over the derived one. A host-facing edit only: nothing
240
- * is sent to the engine. */
241
100
  setTitle(title: string | undefined): void;
242
- /** Resolve a pending permission request. Returns false if the id is unknown (e.g. timed out). */
243
101
  resolvePermission(requestId: string, decision: PermissionDecision): boolean;
244
102
  interrupt(): Promise<void>;
245
- /**
246
- * Reset the conversation in place: the session keeps its id, its watermarks
247
- * and its place in the list, and the engine starts over with an empty
248
- * context. Announced with a `conversation_reset` event, whose replay rules
249
- * (see `transcriptContent` in `@workerdeck/protocol`) are what stop an
250
- * attaching client from resurrecting the cleared rows.
251
- *
252
- * Optional, like every member added after `Runner` became public API: an
253
- * out-of-tree runner that declines it declines the `clear_context` command
254
- * with it, which is exactly what `EngineCapabilities.clearContext: false`
255
- * tells clients to expect.
256
- *
257
- * **Queues behind in-flight work rather than racing it** — resolving when the
258
- * clear has actually happened, not when it was accepted. A clear that landed
259
- * in the middle of the turn it was clearing would be neither, and the engines
260
- * differ in how they wait (claude hands `/clear` to a CLI that queues its own
261
- * streamed input; codex and the provider put it on their turn chain), so the
262
- * one thing callers may rely on is the resolution, not the mechanism.
263
- */
264
103
  clearContext?(): Promise<void>;
265
104
  setPermissionMode(mode: PermissionMode): Promise<void>;
266
- /** Switch the model for subsequent responses; undefined = back to the default. */
267
105
  setModel(model?: string): Promise<void>;
268
- /** Deliver the terminal result of an out-of-band tool execution (e.g. a bridged
269
- * call answered by a browser client). Optional: engines that never execute
270
- * out-of-band simply don't expose it. Idempotent by executionId — unknown or
271
- * already-settled ids return false. */
272
106
  settleExecution?(executionId: string, result: ToolExecutionResult): boolean;
273
- /**
274
- * Park: capture durable state, release engine resources, and go inert — without
275
- * ending the session (no `session_closed`; the status becomes `parked`). The host
276
- * persists the snapshot, drops the runner, and rebuilds it under the same id when
277
- * a deferred execution's result arrives.
278
- *
279
- * Returns undefined when parking is not possible right now — a turn is in flight,
280
- * nothing is actually parked, or the engine doesn't support it (the Claude engine
281
- * doesn't: the CLI owns its own process state).
282
- */
283
107
  park?(): RunnerSnapshot | undefined;
284
- /**
285
- * The same snapshot, taken **without ending anything** — the runner stays live,
286
- * attached and warm.
287
- *
288
- * Park and snapshot are two operations that happen to produce the same value,
289
- * and separating them is what makes restart-survival possible for an engine
290
- * that has no on-disk session of its own. A park is for a session with nothing
291
- * to do for possibly days; this is for one whose user is mid-conversation and
292
- * whose process might be redeployed out from under it. The host writes it
293
- * through after each turn — never on a shutdown hook, because a `kill -9` runs
294
- * no hook and that is precisely the case worth surviving — and rebuilds from
295
- * the last write through the ordinary `restore` path.
296
- *
297
- * Returns undefined when a snapshot would capture a half-happened turn: one in
298
- * flight, or pending in-process executions whose results die with the process.
299
- * Optional for the same reason `park()` is — claude and codex run behind a
300
- * binary that owns its process state, and have engine-side resume instead.
301
- */
302
108
  snapshot?(): RunnerSnapshot | undefined;
303
- /** Emit a session_error and terminate. For host-enforced policy (e.g. requireApiKey). */
304
109
  fail(message: string): void;
305
- /** Terminate the session and any underlying engine process. */
306
110
  close(reason?: 'client' | 'server' | 'error'): void;
307
111
  }
308
112
  //#endregion
309
113
  //#region src/lib/subscribers.d.ts
310
- /** What a subscriber asked for. Absent fields mean the untransformed stream. */
311
114
  type SubscribeOptions = {
312
115
  coalesceReplay?: boolean;
313
116
  truncateResults?: boolean;
@@ -326,25 +129,15 @@ type SessionInfoFn = (sdkSessionId: string, options: {
326
129
  dir?: string;
327
130
  }) => Promise<SDKSessionInfo | undefined>;
328
131
  type SessionRunnerConfig = CreateSessionRequest & {
329
- /** Injectable query implementation (tests, instrumentation). Defaults to the SDK's query(). */queryFn?: QueryFn; /** Environment for the spawned Claude Code process. Defaults to process.env. */
132
+ queryFn?: QueryFn;
330
133
  env?: Record<string, string | undefined>;
331
- pathToClaudeCodeExecutable?: string; /** Escape hatch merged last into the SDK Options. */
332
- extraOptions?: Partial<Options>; /** Timeout for pending approvals when the request itself doesn't set one. Default 300000. */
134
+ pathToClaudeCodeExecutable?: string;
135
+ extraOptions?: Partial<Options>;
333
136
  defaultApprovalTimeoutMs?: number;
334
- /** With `resume`: emit the resumed session's history as replay events before the query
335
- * starts, so late-attaching clients get a full transcript. Default true. */
336
- backfillHistory?: boolean; /** Injectable history reader (tests). Defaults to the SDK's getSessionMessages. */
137
+ backfillHistory?: boolean;
337
138
  historyFn?: HistoryFn;
338
- /** Injectable session-metadata reader (tests). Defaults to the SDK's
339
- * getSessionInfo — the only place the CLI's own session title is readable
340
- * from, since no message on the stream carries it. */
341
139
  sessionInfoFn?: SessionInfoFn;
342
140
  };
343
- /**
344
- * One live Agent SDK session: owns the query() call, the streaming input queue, the
345
- * pending-approval table, and a seq-numbered event log that subscribers can replay.
346
- * No transport — the server (or any host) subscribes and bridges to the wire.
347
- */
348
141
  declare class SessionRunner implements Runner {
349
142
  #private;
350
143
  readonly id: string;
@@ -353,116 +146,27 @@ declare class SessionRunner implements Runner {
353
146
  get status(): SessionStatus;
354
147
  get sdkSessionId(): string | undefined;
355
148
  get lastSeq(): number;
356
- /** 'oauth' = claude.ai subscription credentials; other values are API-key provenance. */
357
149
  get apiKeySource(): string | undefined;
358
150
  get pendingApprovals(): PermissionRequest[];
359
151
  info(): SessionInfo;
360
- /** Host-facing rename: writes `meta.title`, which `#title()` prefers. Clearing
361
- * it (undefined) restores the derived title. The engine is never told. */
362
152
  setTitle(title: string | undefined): void;
363
- /** Begin the session. Idempotent; returns the run promise (resolves when the query ends). */
364
153
  start(): Promise<void>;
365
- /** Queue a user message for the session (starts the next turn when idle).
366
- *
367
- * `attachments` carry their own bytes; they reach the CLI as content blocks and
368
- * are logged as references. A message may be attachments alone — an empty text
369
- * block is not valid API input, so the text is only added when there is some. */
370
154
  sendMessage(text: string, attachments?: readonly AttachmentInput[]): void;
371
- /** Live MCP server status, straight from the CLI. Undefined when the engine
372
- * can't answer (an injected fake query in tests) — the caller 501s rather than
373
- * pretending the session has no servers. */
374
155
  mcpServers(): Promise<McpServerStatusInfo[] | undefined>;
375
156
  reconnectMcpServer(name: string): Promise<void>;
376
157
  setMcpServerEnabled(name: string, enabled: boolean): Promise<void>;
377
- /** Resolve a pending permission request. Returns false if the id is unknown (e.g. timed out). */
378
158
  resolvePermission(requestId: string, decision: PermissionDecision): boolean;
379
159
  interrupt(): Promise<void>;
380
- /**
381
- * Reset the conversation by sending the `/clear` the CLI already honors.
382
- *
383
- * Deliberately not a second mechanism: this engine's reset arrives *from the
384
- * SDK*, and `normalizeSdkMessage` turns the CLI's report of it into the
385
- * `conversation_reset` event (adopting the new conversation id and re-polling
386
- * context usage on the way through). Reimplementing the clear here would give
387
- * one engine two ways to reach the same state, and only one of them would get
388
- * the id adoption right. So the command and the composer's `/clear` are one
389
- * behaviour, and this method is the thin end of it.
390
- *
391
- * The one place it differs from the other two engines: this resolves when the
392
- * `/clear` has been **handed to the CLI**, not when the reset has happened —
393
- * the CLI queues its own streamed input, so waiting is its job, and there is
394
- * no chain here to ride. The observable contract is the same (a clear sent
395
- * mid-turn queues rather than cutting the turn short); only the moment the
396
- * promise settles is weaker, and no caller depends on it.
397
- */
398
160
  clearContext(): Promise<void>;
399
161
  setPermissionMode(mode: PermissionMode): Promise<void>;
400
- /** Switch the model for subsequent responses; undefined = back to the default. */
401
162
  setModel(model?: string): Promise<void>;
402
- /** Emit a session_error and terminate. For host-enforced policy (e.g. requireApiKey). */
403
163
  fail(message: string): void;
404
- /** Terminate the session and the underlying CLI subprocess. */
405
164
  close(reason?: 'client' | 'server' | 'error'): void;
406
- /** See `Runner.eventAt`. A linear scan: the one caller is a reader pressing
407
- * "show everything" on one row, so a per-runner seq index would be a map
408
- * maintained on every emit to save a walk nobody makes twice a minute. */
409
165
  eventAt(seq: number): SessionEvent | undefined;
410
- /**
411
- * Replay buffered events with seq > afterSeq, then deliver live events.
412
- * Returns an unsubscribe function.
413
- *
414
- * Replay honours the reset watermark: transcript content below the latest
415
- * `conversation_reset` is skipped (the reducer would clear it again anyway,
416
- * and a pre-reset client that never learned the reducer's case would render
417
- * a conversation the engine has discarded), while state-bearing events —
418
- * which are emitted once and never again — always replay. The reset event
419
- * itself replays (the skip is strictly-below), which is what clears a
420
- * reconnecting client still holding pre-reset rows; superseded resets are
421
- * content below the newer one and are skipped with what they cleared.
422
- */
423
166
  subscribe(listener: SessionEventListener, afterSeq?: number, options?: SubscribeOptions): () => void;
424
167
  }
425
168
  //#endregion
426
169
  //#region src/lib/replay.d.ts
427
- /**
428
- * The one replay body, and what a socket receives from it.
429
- *
430
- * Every runner had a byte-identical copy of this loop — three spellings of four
431
- * rules, one of which ("never drop the highest-seq event, whatever the rule
432
- * says") is load-bearing and was three copies of a comment. Not a base class:
433
- * the runners share nothing else, and a base class would have to own `#emit`,
434
- * the most engine-specific method each of them has.
435
- *
436
- * The rules, in the order they are applied:
437
- *
438
- * 1. `afterSeq` — the caller already holds everything at or below it.
439
- * 2. `resetSeq` — transcript *content* strictly below the latest
440
- * `conversation_reset` is skipped, so a re-attach cannot resurrect a cleared
441
- * conversation while state events still replay. **Every engine that can emit
442
- * a reset must track it and pass it** — this was Claude's alone only for as
443
- * long as Claude's was the only engine that could produce the event, and the
444
- * failure when a runner forgets is quiet: the end state is right for a
445
- * current reducer, so nothing looks broken while every attach re-sends the
446
- * whole cleared conversation for the process's lifetime.
447
- * 3. `coalesceReplay` — last-write-wins state readings superseded later in the
448
- * same replay (`staleReplaySeqs`), plus everything `replayRetains` says the
449
- * reducer reads and discards. Opt-in, and only sound for a consumer whose
450
- * handling of those events is last-write-wins.
451
- * 4. `imageRefs` — a base64 image part is delivered as an `image_ref` address
452
- * (protocol's {@link imagePartRef}), its bytes one REST fetch away. Applied
453
- * **before** rule 5, because it stamps indices from the stored part array
454
- * which rule 5 then reshapes. Unlike rule 5 this also applies to the live
455
- * path (see `SubscriberSet`), which is the one place these two rules differ.
456
- * 5. `truncateResults` — a huge `tool_result` block is delivered as its head
457
- * plus the markers that say so. **Never mutates the stored event**: the live
458
- * path, the parking snapshot and the fetch route all need the whole thing,
459
- * so this builds a copy and the log stays the log.
460
- *
461
- * The highest-seq event is delivered whatever rules 2 and 3 say — a client's
462
- * replay hold waits for `state.lastSeq` to reach the attach's and would
463
- * otherwise hang forever — but it is still *truncated* when rule 4 applies. A
464
- * session that ends on a `find /` puts its 641 KB frame exactly there.
465
- */
466
170
  declare function replaySlice(events: readonly SessionEvent[], options: {
467
171
  afterSeq: number;
468
172
  resetSeq?: number;
@@ -470,88 +174,43 @@ declare function replaySlice(events: readonly SessionEvent[], options: {
470
174
  truncateResults?: boolean;
471
175
  imageRefs?: boolean;
472
176
  }): SessionEvent[];
473
- /**
474
- * A copy of `event` whose oversized `tool_result` blocks carry their head and
475
- * say so — or `event` itself, unchanged and un-copied, when nothing is over the
476
- * budget. That identity matters: an attach is mostly small events, and a fresh
477
- * object for every one of them would cost more than the feature saves.
478
- *
479
- * Blocks are measured and cut **individually**. A message answering three calls
480
- * where one is a `find /` keeps the two small results whole, which is what makes
481
- * the per-block marker (rather than a per-event one) honest.
482
- */
483
177
  declare function truncateResultBlocks(event: SessionEvent): SessionEvent;
484
178
  //#endregion
485
179
  //#region src/engines/provider/runner.d.ts
486
- /** `cwd` is optional for this engine: the loop has no host-filesystem coupling
487
- * (tools get scoped VFS handles instead). Defaults to process.cwd() for display. */
488
180
  type AiSdkRunnerConfig = Omit<CreateSessionRequest, 'cwd'> & {
489
181
  cwd?: string;
490
- /** AI SDK language model instance (or gateway model id string). Provider
491
- * resolution from profiles happens host-side; core takes the resolved model. */
492
182
  languageModel: LanguageModel$1;
493
- /** Tools available to the loop. Tools WITHOUT `execute` halt the loop when
494
- * called; their calls surface via `pendingToolCalls` and are answered with
495
- * `resolveToolCall()`, which re-enters the loop by message-state replay. */
496
- tools?: ToolSet$1; /** System prompt (AI SDK v7 `instructions`). */
497
- instructions?: string; /** Max loop steps per turn. Default 20. */
183
+ tools?: ToolSet$1;
184
+ instructions?: string;
498
185
  maxSteps?: number;
499
- /**
500
- * Executes tool calls the loop cannot run inline (tools declared without
501
- * `execute`). With one set, the runner drives the whole cycle itself:
502
- * dispatch on park, apply the result, re-enter. Without one, parked calls
503
- * stay on {@link pendingToolCalls} for the host to answer via
504
- * {@link resolveToolCall}.
505
- */
506
- executor?: ToolExecutor; /** Names the executor handles. Others stay pending for the host. */
507
- executableTools?: string[]; /** Scratch filesystem handed to sandboxed executions. */
508
- vfs?: SandboxVfs; /** Per-execution limits passed to the executor. */
186
+ executor?: ToolExecutor;
187
+ executableTools?: string[];
188
+ vfs?: SandboxVfs;
509
189
  executionLimits?: {
510
190
  timeoutMs?: number;
511
191
  memoryLimitBytes?: number;
512
- }; /** Which backend the executor represents, for `execution_dispatched` events. */
513
- executionBackend?: ToolExecutionBackend; /** Swap models mid-session (`set_model`). Unset = setModel() is rejected. */
192
+ };
193
+ executionBackend?: ToolExecutionBackend;
194
+ shouldApprove?: (call: {
195
+ toolName: string;
196
+ input: unknown;
197
+ }) => boolean;
198
+ approvalTimeoutMs?: number;
514
199
  resolveModel?: (modelId: string | undefined) => LanguageModel$1;
515
- /**
516
- * Live MCP status for this session, when the host wired MCP at all. Unlike
517
- * the CLI engines — which ask their binary — this engine's MCP is entirely
518
- * host-assembled, so the host is the only party that can answer. Unset means
519
- * "no MCP here", which reads as an empty list rather than an error: a session
520
- * with no servers is a fact, not a missing feature.
521
- *
522
- * Named apart from the inherited `mcpServers` request field on purpose —
523
- * that one is the *wire configuration* a client asked for, this one is what
524
- * the host actually connected.
525
- */
526
200
  reportMcpServers?: () => Promise<McpServerStatusInfo[] | undefined>;
527
- /** Called once when the session closes — release per-session resources the
528
- * host attached (an MCP connection, a watcher). Errors are swallowed. Also
529
- * runs when the session parks: parking releases the same resources. */
530
201
  onClose?: () => void | Promise<void>;
531
- /**
532
- * Rebuild a parked session from {@link AiSdkRunner.park}'s snapshot instead of
533
- * starting a fresh one: the id, event log, seq counter, message history, and
534
- * the executions it parked on are all adopted. The rest of the config is the
535
- * live wiring (model, tools, executor, VFS) and is taken as given — a
536
- * rehydrated session may legitimately come up against a re-created tool set.
537
- */
538
202
  restore?: RunnerSnapshot;
539
203
  };
540
- /** An external (execute-less) tool call the loop is parked on. */
541
204
  type PendingToolCall = {
542
205
  toolCallId: string;
543
206
  toolName: string;
544
207
  input: unknown;
545
- /** True when the executor declared the execution deferred — the session may
546
- * park on it, and only a host-delivered result can settle it. */
547
- deferred?: boolean; /** Epoch ms the host's execution watchdog should fire at. */
208
+ deferred?: boolean;
548
209
  expiresAt?: number;
549
210
  };
550
- /** The provider engine's half of a {@link RunnerSnapshot} — its continuation
551
- * state. Opaque to the host; only this class reads it. */
552
211
  type AiSdkSessionState = {
553
212
  messages: ModelMessage[];
554
- pendingToolCalls: PendingToolCall[]; /** Calls already handed to an executor, so rehydration never re-dispatches them. */
213
+ pendingToolCalls: PendingToolCall[];
555
214
  dispatched: string[];
556
215
  numTurns: number;
557
216
  totalUsage: {
@@ -560,8 +219,6 @@ type AiSdkSessionState = {
560
219
  cacheWrite: number;
561
220
  cacheRead: number;
562
221
  };
563
- /** The in-progress turn's accumulator: a parked turn's earlier legs still owe
564
- * their tokens and elapsed time to the turn_result that eventually lands. */
565
222
  turnAccum?: {
566
223
  startedAt: number;
567
224
  input: number;
@@ -570,12 +227,8 @@ type AiSdkSessionState = {
570
227
  cacheRead: number;
571
228
  };
572
229
  permissionMode: PermissionMode;
573
- /** Model alias last requested (config.model or a set_model), NOT the resolved
574
- * provider model id — re-resolution goes back through `resolveModel`. */
575
230
  model?: string;
576
231
  lastActivityAt?: number;
577
- /** When the snapshot was taken, so a rehydrated turn can discount the time it
578
- * spent parked instead of billing it as elapsed turn duration. */
579
232
  parkedAt?: number;
580
233
  };
581
234
  type ToolCallOutput = {
@@ -585,16 +238,6 @@ type ToolCallOutput = {
585
238
  type: 'json';
586
239
  value: unknown;
587
240
  };
588
- /**
589
- * Model-agnostic runner over the AI SDK v7 ToolLoopAgent. The session's durable
590
- * state is its ModelMessage history: every turn — including continuation after an
591
- * externally-executed tool call — is a fresh streamed call over that history
592
- * (message-state replay; the loop cannot be suspended). Output is emitted as it
593
- * happens: `stream_delta` per token (unless includePartialMessages is false) and
594
- * assistant/tool messages per step. Emits the same seq-numbered SessionEvent log
595
- * as SessionRunner; engine-specific CLI telemetry (system_init, capabilities,
596
- * rate_limit, ...) is simply never emitted.
597
- */
598
241
  declare class AiSdkRunner implements Runner {
599
242
  #private;
600
243
  readonly id: string;
@@ -602,200 +245,66 @@ declare class AiSdkRunner implements Runner {
602
245
  constructor(config: AiSdkRunnerConfig, id?: string);
603
246
  get status(): SessionStatus;
604
247
  get lastSeq(): number;
605
- /** The session's durable state — persist to park, replay to rehydrate. */
606
248
  get messages(): ModelMessage[];
607
- /** External tool calls the loop is currently parked on. */
608
249
  get pendingToolCalls(): PendingToolCall[];
609
250
  get pendingApprovals(): PermissionRequest[];
610
- /** The session's scratch filesystem (see Runner.vfs) — the server's file
611
- * routes serve deliverables straight from it. */
612
251
  get vfs(): SandboxVfs | undefined;
613
252
  info(): SessionInfo;
614
253
  start(): Promise<void>;
615
- /**
616
- * Snapshot durable state, release engine resources, and go inert — the session
617
- * continues in the snapshot, not in this object. Returns undefined when parking
618
- * would lose work or has nothing to wait for: a turn in flight, no parked call,
619
- * or an already-closed/parked runner.
620
- */
621
254
  park(): RunnerSnapshot | undefined;
622
- /**
623
- * The same snapshot, taken without ending anything.
624
- *
625
- * `park()` and this are two operations that happen to produce the same value,
626
- * and the difference is the whole point: `park()` *ends* the live runner
627
- * (inert, listeners dropped, `onClose` called), which is right for deferred
628
- * execution — the session has nothing to do for possibly days — and wrong for
629
- * restart-survival, where the session is active and someone is mid-
630
- * conversation. This one changes nothing at all: no status emit, no listener
631
- * clear, no disposer. The host writes the value through to durable storage
632
- * after each turn and keeps the runner live and warm, so a restart rebuilds
633
- * from the last write through the existing `restore` path and the next message
634
- * costs no wake.
635
- *
636
- * The gate is `park()`'s minus the requirement that there be something parked:
637
- *
638
- * - `#abort` set is refused for the reason it always was — a `generate()` in
639
- * flight has produced messages that are not in the history yet, so the
640
- * snapshot would be of a turn that half-happened.
641
- * - Pending calls that are **not** all deferred are refused, which is
642
- * `park()`'s rule wearing a different hat. An in-process execution's result
643
- * is coming back to *this* runner and dies with the process; a restore would
644
- * wait on it forever, and `state.dispatched` is what would stop the rebuilt
645
- * runner from simply calling it again.
646
- * - Idle with nothing pending — the case `park()` exists to refuse — is
647
- * exactly the case this exists to allow.
648
- */
649
255
  snapshot(): RunnerSnapshot | undefined;
650
256
  sendMessage(text: string, attachments?: readonly AttachmentInput[]): void;
651
- /**
652
- * Deliver the result of an external (execute-less) tool call. Appends the
653
- * tool-result message and, once no calls remain pending, re-enters the loop.
654
- * Idempotent per toolCallId: unknown/already-settled ids return false.
655
- */
656
257
  resolveToolCall(toolCallId: string, output: ToolCallOutput, options?: {
657
258
  isError?: boolean;
658
259
  }): boolean;
659
- resolvePermission(_requestId: string, _decision: PermissionDecision): boolean;
660
- /** Emit `file_delivered` — the deliver_file tool's hand-over event (wired by
661
- * createEngineSession via ToolContextOptions.onFileDelivered). */
260
+ resolvePermission(requestId: string, decision: PermissionDecision): boolean;
662
261
  emitFileDelivered(file: {
663
262
  path: string;
664
263
  bytes: number;
665
264
  description?: string;
666
265
  }): void;
667
- /**
668
- * One plain generateText over the session's current model, billed into the
669
- * running turn's usage accumulator — the web_fetch digest pass uses this so
670
- * its tokens are never lost from the turn's accounting.
671
- */
672
266
  generateDigest(prompt: string): Promise<string>;
673
- /**
674
- * Reset the conversation: drop the message array the next turn would have
675
- * been built from. There is no engine round trip — this runner *is* where the
676
- * transcript lives, so clearing it is the whole operation.
677
- *
678
- * Two things ride along, both already written elsewhere and both load-bearing
679
- * here. `#emit`'s `conversation_reset` arm retires `#contextUsage` (the
680
- * reading described a conversation that no longer exists), and the same arm
681
- * in `restore` keeps a parked session that comes back after a clear from
682
- * resurrecting it. Pending tool calls are NOT swept: a parked call is work a
683
- * backend still owes an answer for, and a clear is not an interrupt — the
684
- * refusal below is what keeps the two apart.
685
- */
686
267
  clearContext(): Promise<void>;
687
268
  interrupt(): Promise<void>;
688
269
  setPermissionMode(mode: PermissionMode): Promise<void>;
689
270
  setModel(model?: string): Promise<void>;
690
271
  fail(message: string): void;
691
272
  close(reason?: 'client' | 'server' | 'error'): void;
692
- /** See `Runner.eventAt`. A linear scan: the one caller is a reader pressing
693
- * "show everything" on one row, so a per-runner seq index would be a map
694
- * maintained on every emit to save a walk nobody makes twice a minute. */
695
273
  eventAt(seq: number): SessionEvent | undefined;
696
274
  subscribe(listener: SessionEventListener, afterSeq?: number, options?: SubscribeOptions): () => void;
697
- /**
698
- * Deliver the result of an execution this runner dispatched. Used by the host
699
- * when a backend settled out-of-band (a browser bridge answering later, a
700
- * deferred executor). Idempotent by executionId.
701
- */
702
275
  settleExecution(executionId: string, result: ToolExecutionResult): boolean;
703
- /**
704
- * This session's MCP servers, as the host assembled them.
705
- *
706
- * Always answers — an empty list when no MCP was wired — because the
707
- * alternative (undefined, which the server turns into a 501) says "this
708
- * engine cannot tell you", and this engine can: the host that built the
709
- * session is the only party who knows, and it has been asked.
710
- */
711
276
  mcpServers(): Promise<McpServerStatusInfo[] | undefined>;
712
- /** Host-facing rename: writes `meta.title`, which `#title()` prefers. Clearing
713
- * it (undefined) restores the derived title. The engine is never told. */
714
277
  setTitle(title: string | undefined): void;
715
278
  }
716
279
  //#endregion
717
280
  //#region src/engines/claude/auth.d.ts
718
- /**
719
- * Credential presence for one Claude Code environment, as the CLI itself reports
720
- * it. 'unknown' means the check could not run at all (no binary, a CLI too old
721
- * for `auth status`, unparseable output) — which is NOT evidence of a missing
722
- * login and must never be surfaced as one.
723
- */
724
281
  type ClaudeAuthStatus = 'logged_in' | 'logged_out' | 'unknown';
725
- /** Injectable form of {@link checkClaudeAuth} (tests, custom probes). */
726
282
  type ClaudeAuthProbe = (env: Record<string, string | undefined>) => Promise<ClaudeAuthStatus>;
727
- /**
728
- * The native Claude Code binary the Agent SDK itself spawns, resolved the way
729
- * the SDK resolves it: the platform-specific optional dependency installed next
730
- * to the SDK package (`@anthropic-ai/claude-agent-sdk-<platform>-<arch>/claude`).
731
- * Probing this binary rather than whatever `claude` is on PATH means an auth
732
- * check answers for the executable sessions will actually run — the two can be
733
- * different versions logged into different places. Returns undefined when it
734
- * can't be found (optional dep skipped, unsupported platform); callers degrade
735
- * to 'unknown', and the SDK surfaces its own error if a session is created.
736
- */
737
283
  declare function resolveBundledClaudeExecutable(): string | undefined;
738
- /**
739
- * Ask the CLI whether `env` holds usable credentials: `claude auth status`
740
- * prints a JSON verdict covering every source the CLI itself consults for that
741
- * environment — `<CLAUDE_CONFIG_DIR>/.credentials.json`, the macOS login
742
- * Keychain, ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, Bedrock/Vertex
743
- * (verified against 2.1.217). Only the `loggedIn` boolean is ever read; the
744
- * identity fields in the payload (email, org, subscription) never leave the
745
- * parse. The exit code is deliberately ignored — 2.1.217 exits 1 on a
746
- * logged-out verdict where other versions exit 0 — and anything that doesn't
747
- * parse to a `loggedIn` boolean is 'unknown', because `auth status` is not a
748
- * stable contract. Never rejects.
749
- */
750
284
  declare function checkClaudeAuth(env: Record<string, string | undefined>, options?: {
751
285
  executable?: string;
752
286
  timeoutMs?: number;
753
287
  }): Promise<ClaudeAuthStatus>;
754
288
  //#endregion
755
289
  //#region src/executors/quickjs-executor.d.ts
756
- /** Resolve a URL to text for the guest. Runs host-side with host authority —
757
- * this is where a credential may be attached, never inside the sandbox. */
758
290
  type HostFetch = (url: string, signal: AbortSignal) => Promise<string>;
759
291
  type QuickJsExecutorOptions = {
760
292
  engine: SandboxEngine;
761
- /**
762
- * Hostnames the guest may reach, exact or `*.example.com`. Empty/unset =
763
- * no network at all (the guest's fetchText throws). Matched host-side; the
764
- * guest is never told the allowlist and never holds a credential.
765
- */
766
- allowedHosts?: string[]; /** Performs the actual request. Unset = global fetch, text body. */
293
+ allowedHosts?: string[];
767
294
  hostFetch?: HostFetch;
768
- /** Per-fetch cap. The guest deadline does NOT cover host-function time, so
769
- * every capability needs its own bound. Default 10000. */
770
- fetchTimeoutMs?: number; /** Default guest wall-clock limit when the call doesn't set one. Default 5000. */
771
- defaultTimeoutMs?: number; /** Default guest allocator cap when the call doesn't set one. Default 64 MiB. */
295
+ fetchTimeoutMs?: number;
296
+ defaultTimeoutMs?: number;
772
297
  defaultMemoryLimitBytes?: number;
773
298
  };
774
- /**
775
- * In-process execution backend: runs a tool's untrusted script in the QuickJS
776
- * WASM guest. Always settles inline — nothing downstream assumes that, which is
777
- * what lets a deferred backend replace it behind the same seam.
778
- */
779
299
  declare class QuickJsExecutor implements ToolExecutor {
780
300
  #private;
781
301
  constructor(options: QuickJsExecutorOptions);
782
302
  dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch>;
783
303
  }
784
- /** Exact hostname match, or a single leading `*.` wildcard covering subdomains
785
- * (not the bare parent). Only http(s) — no file:, data:, or other schemes. */
786
304
  declare function isHostAllowed(url: string, allowedHosts: string[]): boolean;
787
305
  //#endregion
788
306
  //#region src/lib/pending-registry.d.ts
789
- /**
790
- * One registry for every request that leaves the runner and must come back:
791
- * permission approvals, browser-bridged tool calls, and deferred executions.
792
- * They differ only in who answers and how long that takes — the correlation,
793
- * timeout, idempotent settle, and provenance tagging are identical, so they
794
- * live here once.
795
- */
796
- /** What kind of async request this is. Purely descriptive — the mechanics are shared. */
797
307
  type PendingKind = 'approval' | 'tool_call' | 'execution';
798
- /** Who settled a request. Mirrors the existing approval vocabulary. */
799
308
  type SettledBy = 'client' | 'timeout' | 'policy' | 'server';
800
309
  type PendingOutcome<T> = {
801
310
  ok: true;
@@ -810,45 +319,30 @@ type PendingOutcome<T> = {
810
319
  type PendingEntry = {
811
320
  id: string;
812
321
  kind: PendingKind;
813
- createdAt: number; /** Epoch ms the timeout policy fires at, when one was set. */
814
- expiresAt?: number; /** Caller-supplied descriptor for display/rehydration (tool name, request, ...). */
322
+ createdAt: number;
323
+ expiresAt?: number;
815
324
  meta?: Record<string, unknown>;
816
325
  };
817
326
  type RegisterOptions<T> = {
818
327
  id: string;
819
328
  kind: PendingKind;
820
- /** Fail the request automatically after this long. Omit for no deadline
821
- * (deferred executions whose watchdog lives elsewhere). */
822
329
  timeoutMs?: number;
823
- meta?: Record<string, unknown>; /** Called when the entry settles, however it settled. For emitting events. */
330
+ meta?: Record<string, unknown>;
824
331
  onSettle?: (outcome: PendingOutcome<T>, entry: PendingEntry) => void;
825
332
  };
826
333
  declare class PendingRequestRegistry {
827
334
  #private;
828
335
  get size(): number;
829
- /**
830
- * Register a request and get a promise for its outcome. The promise **never
831
- * rejects**: a timeout or cancellation resolves with `ok: false` so callers
832
- * feed the failure back into the agent loop instead of unwinding it.
833
- *
834
- * Re-registering a live id throws — silently replacing it would strand the
835
- * first waiter forever.
836
- */
837
336
  register<T>(options: RegisterOptions<T>): Promise<PendingOutcome<T>>;
838
- /** Deliver a result. Returns false for unknown or already-settled ids —
839
- * duplicate and late deliveries are no-ops, never a second application. */
840
337
  settle<T>(id: string, value: T, settledBy?: SettledBy): boolean;
841
- /** Fail a request. Same idempotence guarantee as {@link settle}. */
842
338
  fail(id: string, reason: string, error: string, settledBy?: SettledBy): boolean;
843
339
  has(id: string): boolean;
844
340
  get(id: string): PendingEntry | undefined;
845
341
  list(kind?: PendingKind): PendingEntry[];
846
- /** Fail everything (optionally of one kind) — session close, turn interrupt. */
847
342
  cancelAll(reason: string, error: string, kind?: PendingKind): number;
848
343
  }
849
344
  //#endregion
850
345
  //#region src/executors/browser-bridge-executor.d.ts
851
- /** Answer a bridged call, as delivered by the client over the wire. */
852
346
  type BridgeAnswer = {
853
347
  output: ToolExecutionOutput;
854
348
  logs?: string[];
@@ -858,404 +352,163 @@ type BridgeAnswer = {
858
352
  logs?: string[];
859
353
  };
860
354
  type BrowserBridgeExecutorOptions = {
861
- /**
862
- * Put a `tool_call_request` on the wire to the attached client. Returning
863
- * false means nobody is attached — the execution fails immediately rather
864
- * than hanging until its deadline.
865
- */
866
- send: (frame: ToolCallRequestFrame) => boolean; /** Tell the client to abandon a call the server gave up on. */
867
- cancel?: (executionId: string, reason: string) => void; /** How long to wait for the client before failing the execution. Default 60000. */
355
+ send: (frame: ToolCallRequestFrame) => boolean;
356
+ cancel?: (executionId: string, reason: string) => void;
868
357
  timeoutMs?: number;
869
- /**
870
- * Called once per dispatched execution when it reaches a terminal result,
871
- * however it got there (client answer, timeout, abort, no client). This is
872
- * the wire back into the agent loop — the host feeds it to the runner's
873
- * `resolveToolCall`. A timeout arrives here as a failed result, not silence.
874
- */
875
358
  onResult?: (executionId: string, result: ToolExecutionResult) => void;
876
- /** Share the session's registry so approvals, bridged calls, and deferred
877
- * executions live in one table. Omit to get a private one. */
878
359
  registry?: PendingRequestRegistry;
879
360
  };
880
- /**
881
- * Executes tool calls in the attached client's own sandbox. The first backend
882
- * that genuinely returns `pending`: dispatch puts a request on the wire and
883
- * returns, and the result arrives later through {@link resolve}.
884
- *
885
- * Data locality is the point — documents can stay in the browser and never
886
- * reach the server. The tradeoff is trust: whatever comes back is untrusted
887
- * input, fine for the user's own data but never a source for authoritative
888
- * server state (that is why MCP and secret-bearing tools are never bridged).
889
- */
890
361
  declare class BrowserBridgeExecutor implements ToolExecutor {
891
362
  #private;
892
363
  readonly registry: PendingRequestRegistry;
893
364
  constructor(options: BrowserBridgeExecutorOptions);
894
365
  dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch>;
895
- /**
896
- * Apply a client's answer. Returns false when the id is unknown or already
897
- * settled — a late result after a timeout must not re-open a settled call.
898
- */
899
366
  resolve(executionId: string, answer: BridgeAnswer): boolean;
900
367
  }
901
- /** Map a registry outcome onto the executor's result contract. */
902
368
  declare function toExecutionResult(outcome: PendingOutcome<BridgeAnswer>): ToolExecutionResult;
903
369
  //#endregion
904
370
  //#region src/executors/deferred-executor.d.ts
905
- /** A dispatched execution, as handed to the backend that will run it. */
906
371
  type DeferredDispatch = {
907
- /** Correlation id. The result is delivered under it — `POST
908
- * {basePath}/executions/:executionId/result` — and applied idempotently. */
909
372
  executionId: string;
910
373
  sessionId: string;
911
374
  tool: string;
912
- input: unknown; /** The session's scratch filesystem at dispatch time, by value. */
375
+ input: unknown;
913
376
  vfsSeed?: Record<string, string>;
914
377
  limits?: {
915
378
  timeoutMs?: number;
916
379
  memoryLimitBytes?: number;
917
- }; /** Epoch ms the host's execution watchdog fires at, when a timeout was configured. */
380
+ };
918
381
  expiresAt?: number;
919
382
  };
920
383
  type DeferredExecutorOptions = {
921
- /**
922
- * Hand the call to whatever actually runs it — enqueue it, POST it to a worker,
923
- * page a human. Called synchronously during dispatch; throwing fails the
924
- * execution (the failure reaches the agent as ordinary tool output).
925
- */
926
384
  onDispatch: (call: DeferredDispatch) => void | Promise<void>;
927
- /** How long the result may take before the host's watchdog fails the execution.
928
- * Unset = no deadline; the execution then relies on the job's parked cap. */
929
- timeoutMs?: number; /** Reported on `execution_dispatched`. Default 'remote'. */
385
+ timeoutMs?: number;
930
386
  backend?: ToolExecutionBackend;
931
387
  };
932
- /**
933
- * The executor for work that outlives the session's process residency: dispatch
934
- * hands the call off and returns `pending` **without holding a promise**, because
935
- * the runner it would resolve into is about to be torn down. The result can only
936
- * come back through the host — the execution-result route → `settleExecution` on a
937
- * rehydrated runner — which is exactly what makes a park durable rather than a
938
- * long in-memory await.
939
- *
940
- * Contrast {@link BrowserBridgeExecutor}, which is also `pending` but keeps its
941
- * answer in memory for the ~60s the tab has to reply.
942
- */
943
388
  declare class DeferredExecutor implements ToolExecutor {
944
389
  #private;
945
390
  readonly backend: ToolExecutionBackend;
946
391
  readonly timeoutMs: number | undefined;
947
392
  constructor(options: DeferredExecutorOptions);
948
- /** Every call this executor takes is deferred — route only the tools that
949
- * belong on the remote side to it. */
950
393
  describe(): ToolExecutionProfile;
951
394
  dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch>;
952
395
  }
953
396
  //#endregion
954
397
  //#region src/engines/provider/web-fetch.d.ts
955
- /**
956
- * `web_fetch` backend, close to Claude Code's original WebFetch: fetch a URL,
957
- * convert HTML to markdown, and (optionally) digest it with a model against the
958
- * caller's prompt. Server-side only — this runs with server egress, which is
959
- * exactly why it is an authoritative capability the operator grants explicitly.
960
- */
961
398
  type WebFetchResult = {
962
- /** The URL that was fetched (after same-host redirects). */url: string; /** Model digest of the page against the prompt (when a digest fn is wired). */
963
- digest?: string; /** Page content as markdown (when no digest fn is wired, or digesting failed). */
964
- markdown?: string; /** True when the markdown was cut at the size cap. */
399
+ url: string;
400
+ digest?: string;
401
+ markdown?: string;
965
402
  truncated?: boolean;
966
- /** Redirect-to-a-different-host notice: the redirect is surfaced, not followed
967
- * (the agent can decide to fetch `redirectUrl` itself). */
968
403
  notice?: string;
969
404
  redirectUrl?: string;
970
405
  error?: string;
971
406
  };
972
407
  type WebFetchFn = (url: string, prompt: string) => Promise<WebFetchResult>;
973
- /** Runs the digest pass over the fetched markdown. Wire the session's own model
974
- * here (see createEngineSession) so its tokens land in the turn's usage. */
975
408
  type WebFetchDigest = (markdown: string, prompt: string) => Promise<string>;
976
409
  type WebFetchOptions = {
977
- fetchImpl?: typeof fetch; /** Raw-body cap, enforced while streaming (before any conversion). Default 1 MiB. */
978
- maxContentBytes?: number; /** Markdown cap handed to the model. Default 50 KB. */
410
+ fetchImpl?: typeof fetch;
411
+ maxContentBytes?: number;
979
412
  maxMarkdownBytes?: number;
980
- /** Fetched-page cache TTL (keyed by URL; the digest is per-prompt and never
981
- * cached). Default 15 minutes. */
982
413
  cacheTtlMs?: number;
983
- /** Optional hostname allowlist on top of the SSRF guard (exact or `*.example.com`).
984
- * Unset = any public host. */
985
- allowedHosts?: string[]; /** Per-request timeout. Default 30000. */
414
+ allowedHosts?: string[];
986
415
  timeoutMs?: number;
987
416
  digest?: WebFetchDigest;
988
417
  };
989
418
  declare function createWebFetch(options?: WebFetchOptions): WebFetchFn;
990
- /** Private / loopback / link-local / unspecified, IPv4 and IPv6 (incl. v4-mapped). */
991
419
  declare function isPrivateAddress(address: string): boolean;
992
- /**
993
- * Dependency-free HTML → markdown, tuned for "give the model readable text":
994
- * drops non-content subtrees, keeps headings/lists/links/emphasis/code, strips
995
- * everything else. Not a spec-grade converter on purpose — a small predictable
996
- * transform beats dragging a DOM into core.
997
- */
998
420
  declare function htmlToMarkdown(html: string): string;
999
421
  //#endregion
1000
422
  //#region src/engines/provider/tools.d.ts
1001
- /**
1002
- * How much authority a tool carries, which decides where it may run.
1003
- *
1004
- * - `sandboxed` — no ambient authority; safe to execute anywhere, including an
1005
- * untrusted browser tab. Its results are untrusted input.
1006
- * - `authoritative` — runs server-side with server credentials (MCP, secret-bearing
1007
- * APIs). **Never bridged to a client**: bridging it would hand a browser the
1008
- * ability to forge authoritative results.
1009
- */
1010
423
  type ToolTrust = 'sandboxed' | 'authoritative';
1011
424
  type ToolDefinition = {
1012
425
  name: string;
1013
426
  trust: ToolTrust;
1014
- /** The AI SDK tool. Sandboxed tools are declared WITHOUT `execute` so the loop
1015
- * hands them to the ToolExecutor seam rather than running them inline. */
1016
427
  tool: Tool$1;
1017
428
  };
1018
429
  type ToolContextOptions = {
1019
- /** Executor for sandboxed tools. Selected per call by the host (browser bridge
1020
- * when a client is attached, server QuickJS otherwise). */
1021
430
  executor: ToolExecutor;
1022
- sessionId: string; /** Scratch filesystem shared by this session's sandboxed tools. */
1023
- vfs?: SandboxVfs; /** Search backend. Omitted = `web_search` is not granted at all. */
431
+ sessionId: string;
432
+ vfs?: SandboxVfs;
1024
433
  search?: (query: string, limit: number) => Promise<Array<{
1025
434
  title: string;
1026
435
  url: string;
1027
436
  snippet?: string;
1028
- }>>; /** Document fetcher for `download`. Omitted = the tool is not granted. */
437
+ }>>;
1029
438
  download?: (url: string) => Promise<{
1030
439
  contentType?: string;
1031
440
  text: string;
1032
441
  }>;
1033
- /** Page digester for `web_fetch` (see {@link createWebFetch}). Omitted = the
1034
- * tool is not granted. */
1035
442
  webFetch?: WebFetchFn;
1036
- /** Notified when the agent hands over a VFS file via `deliver_file`, so the
1037
- * host can emit the `file_delivered` session event. The tool is only granted
1038
- * when this is set — a delivery nobody hears is not a delivery. */
1039
443
  onFileDelivered?: (file: {
1040
444
  path: string;
1041
445
  bytes: number;
1042
446
  description?: string;
1043
- }) => void; /** Per-call sandbox limits. */
447
+ }) => void;
1044
448
  limits?: {
1045
449
  timeoutMs?: number;
1046
450
  memoryLimitBytes?: number;
1047
451
  };
1048
- /** Notified when a sandboxed execution is dispatched and when it settles, so
1049
- * the host can emit execution_* events. */
1050
452
  onDispatch?: (executionId: string, toolName: string) => void;
1051
453
  onSettle?: (executionId: string, result: ToolExecutionResult) => void;
1052
454
  };
1053
- /** Everything a session's tools need, plus the tool set to hand the runner. */
1054
455
  type ToolContext = {
1055
456
  vfs: SandboxVfs;
1056
457
  tools: ToolSet$1;
1057
- definitions: ToolDefinition[]; /** Names the loop must not execute inline (they go through the executor). */
458
+ definitions: ToolDefinition[];
1058
459
  sandboxedToolNames: string[];
1059
460
  };
1060
- /**
1061
- * Build the capability-scoped tool set for a session.
1062
- *
1063
- * The agent's authority is exactly what is granted here — there are no built-in
1064
- * filesystem or shell tools, and nothing reaches the host filesystem: `fs_*`
1065
- * operate on an in-memory scratch VFS. Tools whose backend is not supplied are
1066
- * simply absent rather than present-and-failing, so a model cannot be tempted
1067
- * by a capability the operator did not grant.
1068
- */
1069
461
  declare function createToolContext(options: ToolContextOptions): ToolContext;
1070
- /** Add host-side MCP tools to a context. They are ALWAYS authoritative: they run
1071
- * server-side with server credentials, and must never be handed to a browser. */
1072
462
  declare function withMcpTools(context: ToolContext, mcpTools: ToolSet$1): ToolContext;
1073
- /** A tool the host supplies, with the trust level it is to run at. */
1074
463
  type HostToolDefinition = {
1075
464
  tool: Tool$1;
1076
- /**
1077
- * Where this tool may run. `authoritative` tools execute inline in the
1078
- * gateway and MUST declare `execute`; `sandboxed` ones must NOT, because the
1079
- * loop hands them to the {@link ToolExecutor} seam instead — which is what
1080
- * makes them bridgeable to an untrusted tab.
1081
- */
1082
465
  trust: ToolTrust;
1083
466
  };
1084
- /**
1085
- * Add host-supplied tools to a context at an explicit trust level.
1086
- *
1087
- * The trust level is the whole point of the seam: {@link withMcpTools} can only
1088
- * produce authoritative tools, so a host tool that *should* be sandboxed — and
1089
- * therefore executable in the browser tab that asked for it — had no way to be
1090
- * expressed at all. Here the host says which it is, and the contradictions are
1091
- * refused rather than silently resolved:
1092
- *
1093
- * - a `sandboxed` tool carrying `execute` would run inline in this process with
1094
- * the gateway's ambient authority, which is exactly what sandboxing it was
1095
- * meant to prevent;
1096
- * - an `authoritative` tool *without* `execute` would park the turn on a call no
1097
- * executor claims, and the session would simply stop.
1098
- */
1099
- declare function withHostTools(context: ToolContext, hostTools: Record<string, HostToolDefinition>, /** What to call these in error messages ('MCP tool', 'host tool'). */
1100
-
1101
- kind?: string): ToolContext;
467
+ declare function withHostTools(context: ToolContext, hostTools: Record<string, HostToolDefinition>, kind?: string): ToolContext;
1102
468
  //#endregion
1103
469
  //#region src/engines/provider/session.d.ts
1104
470
  type EngineSessionOptions = {
1105
- /** Resolved session config (profile defaults already applied). */config: AiSdkRunnerConfig; /** The profile that selected this engine, when there was one. */
471
+ config: AiSdkRunnerConfig;
1106
472
  profile?: ProfileInfo;
1107
- /**
1108
- * Resolve the profile's provider config into a model instance. The host owns
1109
- * this so core never imports a provider SDK and never reads credentials —
1110
- * they come from the operator's environment, exactly like the Claude chain.
1111
- */
1112
473
  resolveModel: (profile: ProfileInfo | undefined, config: AiSdkRunnerConfig) => LanguageModel$1;
1113
- /**
1114
- * Executor for sandboxed tools. Return the browser bridge when a client is
1115
- * attached and the server sandbox otherwise; the seam makes them
1116
- * interchangeable, so this is the only place the choice is made.
1117
- */
1118
- selectExecutor: () => ToolExecutor; /** Which backend `selectExecutor` returned, for the execution_* events. */
1119
- backend?: 'server' | 'browser' | 'managed' | 'remote'; /** Backends for the granted capabilities. Omitted ones are simply not granted. */
474
+ selectExecutor: (() => ToolExecutor) | ((call: ToolExecutionCall) => ToolExecutor);
475
+ backend?: 'server' | 'browser' | 'managed' | 'remote' | ((call: ToolExecutionCall) => 'server' | 'browser' | 'managed' | 'remote');
1120
476
  capabilities?: {
1121
477
  search?: ToolContextOptions['search'];
1122
478
  download?: ToolContextOptions['download'];
1123
- /**
1124
- * Grants `web_fetch`. Pass options (or `{}`) to use the built-in
1125
- * {@link createWebFetch} backend — its digest pass then runs on the
1126
- * session's own model, billed into the turn's usage. Pass `digest: false`
1127
- * to skip the digest (the tool returns page markdown), a custom digest fn
1128
- * to bring your own model, or a complete {@link WebFetchFn} to replace the
1129
- * backend outright.
1130
- */
1131
479
  webFetch?: WebFetchFn | (Omit<WebFetchOptions, 'digest'> & {
1132
480
  digest?: WebFetchOptions['digest'] | false;
1133
481
  });
1134
- /** Grants `deliver_file`: the agent can hand VFS files over to the user
1135
- * (emitting `file_delivered`, downloadable via the server's file routes).
1136
- * Default true — set false to withhold it. */
1137
482
  deliverFiles?: boolean;
1138
483
  };
1139
- /**
1140
- * A live MCP connection from {@link connectMcpTools} — the preferred way to
1141
- * hand MCP to a session, and the only one that can fail loudly.
1142
- *
1143
- * With this set, the session knows *which servers connected*, so two things
1144
- * that were previously silent become impossible: a profile naming a server
1145
- * that never connected refuses to build (see {@link mcpTools} for what that
1146
- * used to look like), and `runner.mcpServers()` answers `GET
1147
- * /sessions/:id/mcp` with the real per-server status instead of 501.
1148
- */
1149
484
  mcp?: McpConnection;
1150
- /** Authoritative tools that run server-side with server credentials (MCP).
1151
- * Never bridged to a client. Namespaced `<server>__<tool>` by
1152
- * {@link connectMcpTools}, which is how a profile grants servers by name.
1153
- *
1154
- * The bare tool set, for a host assembling one itself. Prefer {@link mcp}:
1155
- * a tool set alone cannot distinguish "this server connected and exposes no
1156
- * tools" from "this server never connected", so the check here has to be the
1157
- * cruder one — a declared server contributing no tools is refused. */
1158
485
  mcpTools?: ToolSet$1;
1159
- /**
1160
- * Extra host tools, each at an explicit trust level (see
1161
- * {@link withHostTools}). This is the seam for a tool that is neither one of
1162
- * the built-in capabilities nor MCP — including a **sandboxed** one, which
1163
- * `mcpTools` cannot express because everything in it is authoritative by
1164
- * construction.
1165
- *
1166
- * A sandboxed tool here rides the same {@link ToolExecutor} seam
1167
- * `eval_script` does, so it executes wherever `selectExecutor` points — an
1168
- * in-process QuickJS guest, or the browser tab that asked the question.
1169
- */
1170
486
  tools?: Record<string, HostToolDefinition>;
1171
- /** Extra instructions prepended to the session's system prompt. Overridden by
1172
- * the profile's `session.instructions` when it declares one. */
1173
487
  instructions?: string;
1174
488
  executionLimits?: {
1175
489
  timeoutMs?: number;
1176
490
  memoryLimitBytes?: number;
1177
491
  };
1178
- /**
1179
- * Initial scratch-filesystem contents for a **new** session, and the safe way
1180
- * to seed one: it is ignored outright when `config.restore` is set, because a
1181
- * rehydrated session brings back the files its parked turn already wrote and
1182
- * seeding over them destroys exactly the work that was preserved.
1183
- *
1184
- * (Hand-building `config.vfs` still works and still wins — but then the
1185
- * `restore ? undefined : createVfs(...)` dance is yours to get right.)
1186
- */
492
+ shouldApprove?: (call: {
493
+ toolName: string;
494
+ input: unknown;
495
+ }) => boolean;
496
+ approvalTimeoutMs?: number;
1187
497
  seedVfs?: Record<string, string>;
1188
- /**
1189
- * Build the session under this id rather than minting one.
1190
- *
1191
- * Forward the server's `EngineRunnerContext.id` here, always: it is set when
1192
- * the gateway is rehydrating a session across a restart, and a runner that
1193
- * ignores it comes back as a *different* session — the rebuild is refused,
1194
- * and every client's route and unread watermark is stranded. Ignored when
1195
- * `config.restore` is present, which carries its own id.
1196
- */
1197
498
  id?: string;
1198
499
  };
1199
- /**
1200
- * Assemble a model-agnostic session: provider model, capability-scoped tools,
1201
- * a scratch VFS, and the executor that runs the sandboxed ones.
1202
- *
1203
- * This is the piece an operator wires into the server's `createEngineRunner`.
1204
- *
1205
- * The host wires the *backends*; the profile and the session request decide which
1206
- * of them are actually granted (`profile.session`, `config.capabilities`). A
1207
- * backend that isn't granted is simply not built into the tool set, so withholding
1208
- * a capability costs the host no branching. No declaration anywhere = everything
1209
- * the host wired, which is what a host that ignores profiles gets.
1210
- */
1211
500
  declare function createEngineSession(options: EngineSessionOptions): AiSdkRunner;
1212
501
  type McpConnection = {
1213
502
  tools: ToolSet$1;
1214
- /**
1215
- * One entry per configured server, connected or not — the truth a session was
1216
- * assembled against. Handed to {@link createEngineSession} as `mcp`, it is
1217
- * what `GET /sessions/:id/mcp` answers with and what makes a half-connected
1218
- * session refuse to build rather than run degraded.
1219
- */
1220
503
  servers: McpServerStatusInfo[];
1221
504
  close: () => Promise<void>;
1222
505
  };
1223
- /**
1224
- * Connect to MCP servers and return their tools, ready for {@link withMcpTools}.
1225
- *
1226
- * Server-side only, with server credentials: these tools are authoritative and
1227
- * must never be bridged to a browser. `@ai-sdk/mcp` is imported lazily and is an
1228
- * optional dependency — an operator who wires no MCP servers never needs it.
1229
- *
1230
- * **A stateless MCP server must answer `GET` with 405.** The client opens the
1231
- * SSE stream with a `GET` before it sends anything, and a POST-only server
1232
- * mounted under a framework's default 404 makes the whole connect fail with an
1233
- * error that names neither the method nor the route. This is the single most
1234
- * common way an otherwise-correct MCP mount fails.
1235
- */
1236
506
  declare function connectMcpTools(servers: Record<string, McpServerConfigWire>, options?: {
1237
- /** `onError` may fire more than once for a single server: transport-level
1238
- * failures surface through the client's own uncaught-error channel as well as
1239
- * the connect failure. Treat it as a report, not a count. */
1240
507
  onError?: (name: string, error: unknown) => void;
1241
- /**
1242
- * Reject if any server fails to connect, after closing the ones that did.
1243
- *
1244
- * Off by default, which is right for an operator's fleet — one unreachable
1245
- * server should not take a whole gateway's sessions down. Turn it **on**
1246
- * when the servers are the app's own: an embedder who mounts one wiki server
1247
- * and gets a session without it has a session that cannot do its job, and
1248
- * finding that out at connect time beats finding it out from a transcript
1249
- * where the agent apologises.
1250
- */
1251
508
  required?: boolean;
1252
509
  }): Promise<McpConnection>;
1253
510
  //#endregion
1254
511
  //#region src/lib/input-queue.d.ts
1255
- /**
1256
- * Push-based single-consumer AsyncIterable bridging imperative sendMessage() calls
1257
- * into the streaming `prompt` the Agent SDK consumes.
1258
- */
1259
512
  declare class InputQueue implements AsyncIterable<SDKUserMessage> {
1260
513
  #private;
1261
514
  push(message: SDKUserMessage): void;
@@ -1265,40 +518,19 @@ declare class InputQueue implements AsyncIterable<SDKUserMessage> {
1265
518
  //#endregion
1266
519
  //#region src/lib/normalize.d.ts
1267
520
  declare function toApiMessage(message: unknown): ApiMessage;
1268
- /**
1269
- * The CLI's MCP status, as `McpServerStatusInfo`.
1270
- *
1271
- * The narrowing is the point: the SDK's config object carries `env` for stdio
1272
- * servers and `headers` for HTTP ones, and both routinely hold API tokens. This
1273
- * is the one place they are dropped, so no client — dashboard, phone, or a host
1274
- * app reading the REST route — can turn "show me my MCP servers" into a
1275
- * credential dump. Only the connection's identity survives.
1276
- */
1277
521
  declare function mcpStatusInfo(status: McpServerStatus): McpServerStatusInfo;
1278
- /** The half of the SDK's `ModelInfo` this package forwards. Structurally typed so
1279
- * the mapping can be unit-tested without a live query. */
1280
522
  type SdkModelInfo = {
1281
523
  value: string;
1282
524
  resolvedModel?: string;
1283
525
  displayName: string;
1284
- description?: string; /** Per-model reasoning efforts, when the SDK reports them (0.3.221+). */
526
+ description?: string;
1285
527
  supportedEffortLevels?: string[];
1286
528
  supportsEffort?: boolean;
1287
529
  };
1288
530
  declare function modelOptionsFromSdk(models: readonly SdkModelInfo[]): ModelOption[];
1289
- /**
1290
- * Map one SDKMessage to a wire-protocol event body, or null for messages the runner
1291
- * consumes itself (system_init and session-state changes carry runner state and are
1292
- * emitted by the runner with extra context).
1293
- */
1294
531
  declare function normalizeSdkMessage(msg: SDKMessage): SessionEventBody | null;
1295
532
  //#endregion
1296
533
  //#region src/engines/adapter.d.ts
1297
- /**
1298
- * A probe's verdict on one profile's credentials. 'unknown' means the probe
1299
- * could not run at all — which is NOT evidence of a missing login and must
1300
- * never be surfaced as one (the `checkClaudeAuth` discipline, generalized).
1301
- */
1302
534
  type EngineAvailability = {
1303
535
  available: true;
1304
536
  } | {
@@ -1307,68 +539,22 @@ type EngineAvailability = {
1307
539
  } | {
1308
540
  available: 'unknown';
1309
541
  };
1310
- /**
1311
- * A model catalog shipped with the release — the answer to "what can a create
1312
- * form offer" with no process spawned, correct from a gateway's first request.
1313
- *
1314
- * Never contains a 'default' sentinel row (a choice, not a model — forms add
1315
- * their own "Profile default" row mapping to an unset model). Staleness is
1316
- * bounded by the release cadence: the release checklist re-runs each catalog's
1317
- * extraction procedure (documented in its file header) and diffs.
1318
- */
1319
542
  type ModelCatalog = {
1320
- models: ModelOption[]; /** Source + date, for the release-checklist refresh. Not served. */
543
+ models: ModelOption[];
1321
544
  provenance: string;
1322
545
  };
1323
546
  type EngineRunnerRequest = {
1324
547
  config: SessionRunnerConfig;
1325
548
  profile?: ProfileInfo;
1326
- /** Rebuild a parked session instead of starting fresh. Engines that cannot
1327
- * rehydrate throw. */
1328
549
  restore?: RunnerSnapshot;
1329
- /**
1330
- * Adopt this session id instead of minting one. For rehydrating a session
1331
- * across a gateway restart: the transcript comes back from the *engine's* own
1332
- * store via `config.resume`, but every client keys its watermarks, unread
1333
- * counts and routes on the WorkerDeck id, so that id has to survive too
1334
- * (`SessionInfo.id` is documented as stable across resumes). A `restore`
1335
- * carries its own id in the snapshot and does not need this.
1336
- */
1337
550
  id?: string;
1338
551
  };
1339
- /**
1340
- * One engine, as the server consumes it: its capability record, its shipped
1341
- * model catalog, a credential probe, and a runner factory. The claude adapter
1342
- * wraps `SessionRunner` without behaviour change; the codex adapter owns the
1343
- * `codex app-server` integration; the provider adapter is a pseudo-adapter —
1344
- * its runners are built by the host's `createEngineRunner` hook, so its
1345
- * `createRunner` throws and the server routes around it.
1346
- */
1347
552
  interface EngineAdapter {
1348
553
  readonly engine: ProfileEngine;
1349
- /** Must deep-equal ENGINE_CAPABILITIES[engine] — asserted by a core test, so
1350
- * the protocol's browser-safe defaults can never drift from the adapter. */
1351
554
  readonly capabilities: EngineCapabilities;
1352
555
  readonly catalog: ModelCatalog;
1353
- /**
1354
- * Probe whether `profile`'s credentials are usable under `env` — the full
1355
- * session environment the real assembly path produces, never a delta (codex
1356
- * replaces the child env wholesale, and a delta would strand HOME/PATH and
1357
- * the auth chain with it). Never rejects.
1358
- */
1359
556
  checkAvailability(profile: ProfileInfo, env: Record<string, string | undefined>): Promise<EngineAvailability>;
1360
- /** Build a Runner. Throwing fails the create (session POST 500s, job fails). */
1361
557
  createRunner(request: EngineRunnerRequest): Runner | Promise<Runner>;
1362
- /**
1363
- * List the engine's on-disk resumable sessions (`GET /sdk-sessions`) —
1364
- * present exactly when the capability record's `listSessions` is true. Must
1365
- * not require a live session: the codex adapter answers over a short-lived
1366
- * `thread/list` app-server child it closes before returning; the claude
1367
- * adapter reads the Agent SDK's store directly. `env` follows the
1368
- * checkAvailability contract (the profile's complete session environment,
1369
- * never a delta). `dir` narrows to one project directory; `limit`/`offset`
1370
- * page the newest-first result.
1371
- */
1372
558
  listSessions?(options: {
1373
559
  profile?: ProfileInfo;
1374
560
  env: Record<string, string | undefined>;
@@ -1377,66 +563,18 @@ interface EngineAdapter {
1377
563
  offset?: number;
1378
564
  }): Promise<SdkSessionSummary[]>;
1379
565
  }
1380
- /** The in-repo adapter for an engine. An absent `engine` means 'claude'. */
1381
566
  declare function getEngineAdapter(engine: ProfileEngine | undefined): EngineAdapter;
1382
567
  //#endregion
1383
568
  //#region src/engines/claude/adapter.d.ts
1384
- /**
1385
- * The Claude engine as an adapter — a thin, behaviourally inert wrapper:
1386
- * `SessionRunner` unchanged, `checkClaudeAuth` as the probe, the static
1387
- * catalog for create forms. Exists so catalogs, capabilities and availability
1388
- * have one shape across engines; the runner itself is exactly what
1389
- * `registry.prepare()` builds.
1390
- */
1391
569
  declare const claudeAdapter: EngineAdapter;
1392
570
  //#endregion
1393
571
  //#region src/engines/claude/catalog.d.ts
1394
- /**
1395
- * The Claude engine's model catalog — what a create form offers before any
1396
- * session has run.
1397
- *
1398
- * **Refresh procedure** (release checklist): run `supportedModels()` on a
1399
- * throwaway SDK query (no tokens spent) and re-apply the shaping rules of
1400
- * `modelOptionsFromSdk` (`src/normalize.ts`) at authoring time: drop the
1401
- * `default` sentinel row, derive display names from resolved ids where
1402
- * unambiguous, mark the newest of each family `primary`, sort by family rank.
1403
- * A unit test replays the raw extraction through `modelOptionsFromSdk` and
1404
- * asserts these rows match, so the rules cannot drift.
1405
- *
1406
- * Two things the live `capabilities` event can never offer:
1407
- * - rows for **older models** the CLI no longer reports (hand-maintained, the
1408
- * accepted cost of a static catalog; the CLI silently downgrades an effort a
1409
- * model doesn't support, so `reasoningEfforts` is omitted on them and the
1410
- * engine default set applies);
1411
- * - an answer on a **cold server**. The live event still exists and remains
1412
- * the in-session truth for the model switcher; this catalog is the
1413
- * create-form truth.
1414
- *
1415
- * `defaultModel` is deliberately NOT here: a claude profile's default is the
1416
- * operator's CLI config, unknowable statically.
1417
- */
1418
572
  declare const CLAUDE_CATALOG: ModelCatalog;
1419
573
  //#endregion
1420
574
  //#region src/engines/codex/types.d.ts
1421
- /**
1422
- * Structural mirror of the slice of the `codex app-server` JSON-RPC v2 surface
1423
- * this engine consumes. Local on purpose: no published client for this
1424
- * protocol exists, the shapes are regenerated from the binary itself
1425
- * (`codex app-server generate-json-schema --out <dir>`, verified 2026-08-05
1426
- * against 0.146.0), and every open-ended axis is a plain string so a newer
1427
- * binary degrades to the unknown-item path instead of a type error.
1428
- *
1429
- * Naming note: the v2 surface is camelCase (`aggregatedOutput`, `exitCode`,
1430
- * `localImage`) where `codex exec`'s JSONL — the retired first transport, and
1431
- * what OpenAI's own docs mostly show — is snake_case. The two vocabularies
1432
- * look alike but are not interchangeable.
1433
- */
1434
- /** `TokenUsageBreakdown` — one entry of `thread/tokenUsage/updated`. OpenAI
1435
- * accounting: `inputTokens` INCLUDES the cached share (the relation the
1436
- * runner's subtraction assumes, asserted in `smoke:codex`). */
1437
575
  type AppServerTokenUsage = {
1438
576
  inputTokens: number;
1439
- cachedInputTokens: number; /** Default 0 in the schema; absent in some payloads. */
577
+ cachedInputTokens: number;
1440
578
  cacheWriteInputTokens?: number;
1441
579
  outputTokens: number;
1442
580
  reasoningOutputTokens: number;
@@ -1447,8 +585,6 @@ type AppServerAgentMessageItem = {
1447
585
  type: 'agentMessage';
1448
586
  text: string;
1449
587
  };
1450
- /** `summary` is what streams by default (`item/reasoning/summaryTextDelta`);
1451
- * `content` is raw CoT and only populated when the operator's config asks. */
1452
588
  type AppServerReasoningItem = {
1453
589
  id: string;
1454
590
  type: 'reasoning';
@@ -1460,11 +596,9 @@ type AppServerCommandExecutionItem = {
1460
596
  type: 'commandExecution';
1461
597
  command: string;
1462
598
  aggregatedOutput?: string;
1463
- exitCode?: number | null; /** 'inProgress' | 'completed' | 'failed' | 'declined' — open. */
599
+ exitCode?: number | null;
1464
600
  status: string;
1465
601
  };
1466
- /** v2 `kind` is an object (`{type: 'add'|'delete'|'update', move_path?}`) —
1467
- * the snake_case JSONL's was a bare string; mapped defensively. */
1468
602
  type AppServerFileChangeItem = {
1469
603
  id: string;
1470
604
  type: 'fileChange';
@@ -1494,18 +628,6 @@ type AppServerWebSearchItem = {
1494
628
  type: 'webSearch';
1495
629
  query: string;
1496
630
  };
1497
- /**
1498
- * A picture the model made with codex's built-in `image_gen` tool.
1499
- *
1500
- * `savedPath` is an absolute path on the **host** — by default under
1501
- * `$CODEX_HOME/generated_images/`, or inside the workspace when the model was
1502
- * told the asset belongs to the project. It is the only reference we get: the
1503
- * app-server never sends the bytes, and neither do we (the event log carries
1504
- * references, never base64 — see the protocol's note on attachments).
1505
- *
1506
- * `result` is an undocumented free-form string. Treated as untrusted length:
1507
- * short values are shown, anything long enough to be an encoded image is not.
1508
- */
1509
631
  type AppServerImageGenerationItem = {
1510
632
  id: string;
1511
633
  type: 'imageGeneration';
@@ -1514,37 +636,22 @@ type AppServerImageGenerationItem = {
1514
636
  result: string;
1515
637
  savedPath?: string;
1516
638
  };
1517
- /** The model *looked at* an image on disk (`path`, host-absolute). */
1518
639
  type AppServerImageViewItem = {
1519
640
  id: string;
1520
641
  type: 'imageView';
1521
642
  path: string;
1522
643
  };
1523
- /**
1524
- * A sub-agent lifecycle edge, on the thread that OWNS the agent. The spawn
1525
- * signal (verified live against 0.146.0, `_docs/codex-subagent-trace-fixed.jsonl`):
1526
- * `kind: 'started'` announces a new agent, and `id` is the `call_id` of the
1527
- * model's own `spawn_agent` function call — a genuine tool-use id, which is what
1528
- * lets the runner hang the whole sidechain off it. There is **no 'completed'
1529
- * kind**: an agent's end travels as its own thread's `turn/completed`.
1530
- */
1531
644
  type AppServerSubAgentActivityItem = {
1532
645
  id: string;
1533
- type: 'subAgentActivity'; /** 'started' | 'interacted' | 'interrupted' — open, and 'completed' does not exist. */
1534
- kind: string; /** The thread the agent runs in — the key every later notification carries. */
1535
- agentThreadId: string; /** The agent's address, e.g. '/root/date_one'; the basename is its name. */
646
+ type: 'subAgentActivity';
647
+ kind: string;
648
+ agentThreadId: string;
1536
649
  agentPath?: string | null;
1537
650
  };
1538
- /**
1539
- * The model's collab-agent tool surface (`spawnAgent | sendInput | resumeAgent |
1540
- * wait | closeAgent`). Decoration, not the design's load-bearing signal: on the
1541
- * wire only `wait` has been observed, with every rich field empty — the spawn
1542
- * truth is {@link AppServerSubAgentActivityItem} (same trace as above).
1543
- */
1544
651
  type AppServerCollabAgentToolCallItem = {
1545
652
  id: string;
1546
653
  type: 'collabAgentToolCall';
1547
- tool: string; /** 'inProgress' | 'completed' | 'failed' | 'declined' — open. */
654
+ tool: string;
1548
655
  status: string;
1549
656
  senderThreadId?: string | null;
1550
657
  receiverThreadIds?: string[] | null;
@@ -1553,58 +660,38 @@ type AppServerCollabAgentToolCallItem = {
1553
660
  reasoningEffort?: string | null;
1554
661
  agentsStates?: Record<string, unknown> | null;
1555
662
  };
1556
- /** The user's own message, echoed back as an item — dropped (the runner
1557
- * already emitted its `user_message`). */
1558
663
  type AppServerUserMessageItem = {
1559
664
  id: string;
1560
665
  type: 'userMessage';
1561
666
  content?: unknown;
1562
667
  };
1563
668
  type AppServerItem = AppServerAgentMessageItem | AppServerReasoningItem | AppServerCommandExecutionItem | AppServerFileChangeItem | AppServerMcpToolCallItem | AppServerWebSearchItem | AppServerImageGenerationItem | AppServerImageViewItem | AppServerUserMessageItem | AppServerSubAgentActivityItem | AppServerCollabAgentToolCallItem;
1564
- /** The `Turn` object of `turn/started` / `turn/completed`. On a completed turn
1565
- * `items` is a summary page whose final `agentMessage` is the turn's answer —
1566
- * for a sub-agent's thread, that is the agent's report. */
1567
669
  type AppServerTurn = {
1568
- id: string; /** 'inProgress' | 'completed' | 'failed' | 'interrupted' — open. */
670
+ id: string;
1569
671
  status: string;
1570
672
  error?: {
1571
673
  message: string;
1572
674
  } | null;
1573
675
  items?: AppServerItem[];
1574
676
  };
1575
- /**
1576
- * One historical turn as `thread/resume` / `thread/read {includeTurns: true}`
1577
- * return it: the same `ThreadItem` vocabulary the live `item/completed`
1578
- * notifications carry (so the live mapping replays it unchanged), plus an
1579
- * `itemsView` marker ('full' | 'summary' | 'notLoaded') saying how much of
1580
- * `items` was actually loaded. Measured against 0.146.0: both surfaces return
1581
- * 'full' items in chronological order.
1582
- */
1583
677
  type AppServerHistoryTurn = {
1584
678
  id: string;
1585
679
  items?: AppServerItem[];
1586
680
  itemsView?: string;
1587
681
  status?: string;
1588
682
  };
1589
- /**
1590
- * One `thread/list` row (the summary Thread shape — its `turns` is always
1591
- * empty on list responses). Timestamps are epoch **seconds** (the protocol's
1592
- * summaries want ms). `id` is what `CreateSessionRequest.resume` feeds
1593
- * `thread/resume`; the row's separate `sessionId` field is not it.
1594
- */
1595
683
  type AppServerThreadSummary = {
1596
- id: string; /** Operator-set thread name, when one exists. */
1597
- name?: string | null; /** First user message — the natural summary line. */
684
+ id: string;
685
+ name?: string | null;
1598
686
  preview?: string | null;
1599
687
  createdAt?: number | null;
1600
688
  updatedAt?: number | null;
1601
- cwd?: string | null; /** Ephemeral threads are never materialized on disk — not resumable. */
689
+ cwd?: string | null;
1602
690
  ephemeral?: boolean;
1603
691
  gitInfo?: {
1604
692
  branch?: string | null;
1605
693
  } | null;
1606
694
  };
1607
- /** `thread/list` result: one page plus an opaque continuation cursor. */
1608
695
  type AppServerThreadListResponse = {
1609
696
  data?: AppServerThreadSummary[];
1610
697
  nextCursor?: string | null;
@@ -1616,57 +703,21 @@ type AppServerUserInput = {
1616
703
  type: 'localImage';
1617
704
  path: string;
1618
705
  };
1619
- /**
1620
- * One live `codex app-server` child as the runner consumes it. The real
1621
- * implementation (`process.ts`) spawns the binary and frames JSON-RPC
1622
- * over its stdio; unit tests inject a scripted one — no process, no
1623
- * credentials.
1624
- */
1625
706
  type AppServerConnection = {
1626
- /** Client→server request. Rejects on a JSON-RPC error response, a dead
1627
- * child, or a closed connection. */
1628
- request(method: string, params?: unknown): Promise<unknown>; /** Client→server notification (fire and forget). */
1629
- notify(method: string, params?: unknown): void; /** Server→client notifications. One handler (the runner). */
707
+ request(method: string, params?: unknown): Promise<unknown>;
708
+ notify(method: string, params?: unknown): void;
1630
709
  onNotification(handler: (method: string, params: unknown) => void): void;
1631
- /** Server→client REQUESTS (approvals live here): the handler's resolution is
1632
- * sent back as the JSON-RPC result; a throw becomes an error response. `id`
1633
- * is the wire request id — `serverRequest/resolved` names it when codex
1634
- * settles a request on its own (auto-resolution), so the runner can retire
1635
- * the matching pending approval instead of leaving a stale card. */
1636
710
  onRequest(handler: (method: string, params: unknown, id: string | number) => Promise<unknown>): void;
1637
- /** Fires once when the child exits or the pipe breaks — NOT on `close()`.
1638
- * The message carries an exit summary and a stderr tail for diagnostics. */
1639
- onClose(handler: (message: string) => void): void; /** Tear the child down (session close). Suppresses the onClose callback. */
711
+ onClose(handler: (message: string) => void): void;
1640
712
  close(): void;
1641
713
  };
1642
714
  type AppServerConnectOptions = {
1643
- /** Complete child environment — a provided spawn env replaces process.env,
1644
- * never merges with it (CODEX_HOME pin already applied). */
1645
715
  env: Record<string, string>;
1646
716
  };
1647
- /** The injectable connection factory: `connectAppServer` under the resolved
1648
- * binary in production, a scripted peer in tests. */
1649
717
  type AppServerConnectFn = (options: AppServerConnectOptions) => AppServerConnection;
1650
718
  //#endregion
1651
719
  //#region src/engines/codex/adapter.d.ts
1652
- /**
1653
- * The codex binary sessions will run: the per-platform package installed next
1654
- * to `@openai/codex`, `vendor/<target-triple>/bin/codex` — the same file the
1655
- * npm wrapper's own `bin/codex.js` launcher execs. Probing this binary rather
1656
- * than whatever `codex` is on PATH means the availability answer is about the
1657
- * executable sessions will actually run. Undefined when it can't be found;
1658
- * callers degrade to 'unknown'.
1659
- */
1660
720
  declare function resolveBundledCodexExecutable(): string | undefined;
1661
- /**
1662
- * CODEX_HOME's threads over ONE short-lived `codex app-server` child: the
1663
- * runner's own handshake (`experimentalApi` and all — one code path, no
1664
- * second vocabulary to drift), `thread/list` pages walked by cursor, child
1665
- * closed before returning. Requires no live session and costs no tokens —
1666
- * it is how "resume" is offered before anything is running. The `connectFn`
1667
- * seam exists for the scripted-peer tests; the adapter passes the real
1668
- * spawn.
1669
- */
1670
721
  declare function listCodexSessions(options: {
1671
722
  connectFn: AppServerConnectFn;
1672
723
  profile?: ProfileInfo;
@@ -1675,26 +726,24 @@ declare function listCodexSessions(options: {
1675
726
  limit?: number;
1676
727
  offset?: number;
1677
728
  }): Promise<SdkSessionSummary[]>;
1678
- /**
1679
- * OpenAI Codex as an engine: the codex CLI binary driven over its `app-server`
1680
- * JSON-RPC surface — structurally the Claude engine's sibling (a local agent
1681
- * binary with sessions, sandboxing and resume, resolving its own credentials
1682
- * from the operator's environment). `@openai/codex` — the npm package that
1683
- * carries the binary — is an **optional peer**: absent, every codex profile
1684
- * reports unavailable and createRunner throws the same message, and no
1685
- * consumer downloads a ~40 MB per-platform binary it never uses.
1686
- */
1687
729
  declare const codexAdapter: EngineAdapter;
1688
730
  //#endregion
1689
731
  //#region src/engines/codex/catalog.d.ts
1690
732
  /**
1691
- * The Codex engine's model catalog, seeded from the binary's own embedded
1692
- * presets `@openai/codex@0.146.0` ships its model table inside the
1693
- * executable, and that table (not the SDK's stale `ModelReasoningEffort`
1694
- * union) is the truth about which reasoning efforts each model takes.
1695
- *
1696
- * **Refresh procedure** (release checklist): extract the embedded JSON from
1697
- * the platform binary and diff
733
+ * The Codex engine's model catalog, seeded from the binary's own embedded model
734
+ * table (see `provenance` for the version) — that table, not the SDK's stale
735
+ * `ModelReasoningEffort` union, is the truth about which reasoning efforts each
736
+ * model takes. Mapping decisions: the internal `codex-auto-review` row is dropped
737
+ * (the codex analogue of the CLI's `default` sentinel), `primary` mirrors the
738
+ * binary's own `visibility` field so both UIs group the way codex's picker does,
739
+ * and `reasoningEfforts` carries `supported_reasoning_levels` verbatim `max`
740
+ * and `ultra` go beyond the SDK union, so trust the binary and keep strings open.
741
+ *
742
+ * **Refresh procedure** (release checklist): extract the embedded JSON from the
743
+ * platform binary and diff. The two-hop resolve is NOT optional — under pnpm's
744
+ * strict layout the platform package is a dependency of `@openai/codex` and
745
+ * resolves only from that wrapper's location (MODULE_NOT_FOUND otherwise), the
746
+ * same two hops `resolveBundledCodexExecutable` makes.
1698
747
  *
1699
748
  * node -e 'const d=require("fs").readFileSync(process.argv[1]);
1700
749
  * const s=d.indexOf(`{\n "models": [`);
@@ -1706,55 +755,17 @@ declare const codexAdapter: EngineAdapter;
1706
755
  * const w=require.resolve("@openai/codex/package.json");
1707
756
  * createRequire(w).resolve("@openai/codex-darwin-arm64/package.json")
1708
757
  * .replace("package.json","vendor/aarch64-apple-darwin/bin/codex")')"
1709
- *
1710
- * The two-hop resolve is NOT optional: under pnpm's strict layout the platform
1711
- * package is a dependency of `@openai/codex`, so it resolves only from that
1712
- * wrapper's location, never from the repo root. Resolving it directly throws
1713
- * MODULE_NOT_FOUND — the same two hops `resolveBundledCodexExecutable` makes.
1714
- *
1715
- * Mapping decisions:
1716
- * - the internal `codex-auto-review` row is dropped (the codex analogue of
1717
- * dropping the CLI's `default` sentinel);
1718
- * - `primary` mirrors the binary's own `visibility` field ('list' = shown in
1719
- * its picker, 'hide' = its "older models"), so both UIs group the way
1720
- * codex's own picker does;
1721
- * - `reasoningEfforts` carries `supported_reasoning_levels` verbatim — note
1722
- * `max`/`ultra` beyond the SDK union; trust the binary, keep strings open.
1723
758
  */
1724
759
  declare const CODEX_CATALOG: ModelCatalog;
1725
760
  //#endregion
1726
761
  //#region src/engines/codex/runner.d.ts
1727
762
  type CodexRunnerConfig = CreateSessionRequest & {
1728
- /** The injectable connection factory. The codex adapter passes
1729
- * `connectAppServer` under the resolved binary; unit tests pass a scripted
1730
- * peer. Required — this class never spawns anything itself. */
1731
763
  connectFn: AppServerConnectFn;
1732
- /** Base environment for the codex child. Defaults to process.env. Passed to
1733
- * spawn **complete** — a child env replaces, never merges. */
1734
- env?: Record<string, string | undefined>; /** CODEX_HOME pin from the profile (auth, config.toml, thread storage). */
764
+ env?: Record<string, string | undefined>;
1735
765
  codexHome?: string;
1736
- /** Timeout for pending approvals when the request itself doesn't set one.
1737
- * Default 300000 — the SessionRunner default. */
1738
766
  defaultApprovalTimeoutMs?: number;
1739
- /** With `resume`: replay the thread's prior turns as `replay: true` events
1740
- * before anything else, so late-attaching clients get a full transcript —
1741
- * the SessionRunner option, same name, same default (true). */
1742
767
  backfillHistory?: boolean;
1743
768
  };
1744
- /**
1745
- * The Codex engine, over the binary's `app-server` JSON-RPC surface: ONE
1746
- * `codex app-server` child per *session* (spawned lazily, held across turns),
1747
- * streaming `item/agentMessage/delta` and the reasoning deltas token-by-token
1748
- * (`streaming: 'token'`). Follows `SessionRunner`'s event-log/seq/status
1749
- * discipline with `AiSdkRunner`'s turn-chain (one turn at a time; sendMessage
1750
- * queues). The first codex transport was `codex exec --experimental-json` (one
1751
- * child per turn) — retired because its JSONL carries no partial messages, so
1752
- * a turn could never stream.
1753
- *
1754
- * A dead child is a failed *turn*, not a failed session: the thread persists
1755
- * on disk, the connection is dropped, and the next message spawns a fresh
1756
- * child that `thread/resume`s the same thread id.
1757
- */
1758
769
  declare class CodexRunner implements Runner {
1759
770
  #private;
1760
771
  readonly id: string;
@@ -1765,119 +776,32 @@ declare class CodexRunner implements Runner {
1765
776
  get lastSeq(): number;
1766
777
  get pendingApprovals(): PermissionRequest[];
1767
778
  info(): SessionInfo;
1768
- /** Host-facing rename: writes `meta.title`, which `#title()` prefers. Clearing
1769
- * it (undefined) restores the derived title. The engine is never told. */
1770
779
  setTitle(title: string | undefined): void;
1771
780
  start(): Promise<void>;
1772
781
  sendMessage(text: string, attachments?: readonly AttachmentInput[]): void;
1773
- /** Resolve a pending approval. Returns false if the id is unknown (e.g.
1774
- * timed out, or already settled by codex itself). */
1775
782
  resolvePermission(requestId: string, decision: PermissionDecision): boolean;
1776
783
  interrupt(): Promise<void>;
1777
- /**
1778
- * Reset the conversation: a **fresh thread on the same session**.
1779
- *
1780
- * Codex has no clear/reset RPC — `thread/compact/start` summarises and
1781
- * continues, `thread/fork` makes a second thread, and neither is "same
1782
- * session, empty context". So the analog is to stop resuming the old thread
1783
- * and start a new one, which is the path a dead child already takes minus the
1784
- * resume. The old thread is NOT deleted: it stays in CODEX_HOME and stays
1785
- * resumable from `GET /sdk-sessions`.
1786
- *
1787
- * Two things it does on the way through, both mirroring the Claude engine's
1788
- * SDK-driven reset (`engines/claude/runner.ts`):
1789
- *
1790
- * 1. **The new thread id is adopted before `conversation_reset` is emitted**,
1791
- * whenever a child is already up — the eager `thread/start` costs no
1792
- * tokens and no model call, and it is what keeps the dormant record from
1793
- * ever naming the conversation that was just cleared. With no child there
1794
- * is nothing to start against and the id is simply dropped; the parking
1795
- * service treats a resumable session with no engine session id as one with
1796
- * nothing to come back to, and forgets the stale record.
1797
- * 2. **The context reading is retired**, in `#emit`'s `conversation_reset`
1798
- * arm. Codex cannot re-poll it the way Claude does — the only source is
1799
- * `thread/tokenUsage/updated`, which arrives *during* a turn — so there is
1800
- * no reading at all until the next turn runs, and the protocol's rule
1801
- * applies: render nothing rather than a stale ring or a 0%.
1802
- *
1803
- * The turn counter stays monotonic across this, on purpose (it is an unread
1804
- * cursor, not an item count), and so does `activityCount` — `#emit` owns both.
1805
- */
1806
784
  clearContext(): Promise<void>;
1807
785
  setPermissionMode(mode: PermissionMode): Promise<void>;
1808
786
  setModel(model?: string): Promise<void>;
1809
787
  fail(message: string): void;
1810
788
  close(reason?: 'client' | 'server' | 'error'): void;
1811
- /** See `Runner.eventAt`. A linear scan: the one caller is a reader pressing
1812
- * "show everything" on one row, so a per-runner seq index would be a map
1813
- * maintained on every emit to save a walk nobody makes twice a minute. */
1814
789
  eventAt(seq: number): SessionEvent | undefined;
1815
790
  subscribe(listener: SessionEventListener, afterSeq?: number, options?: SubscribeOptions): () => void;
1816
- /**
1817
- * The session's MCP servers, live from the binary.
1818
- *
1819
- * Two sources merged, because codex splits them: `mcpServerStatus/list` says
1820
- * what is configured and what each server exposes (including every tool's
1821
- * full JSON Schema, which the Agent SDK does not give us), and the
1822
- * `mcpServer/startupStatus/updated` notifications say which of them are
1823
- * actually up.
1824
- *
1825
- * Answers **before the session has connected**, over a throwaway child, for
1826
- * the same reason the skill list does: a codex session spawns nothing until
1827
- * it has work, and a panel that said "no MCP servers configured" until the
1828
- * first turn would be stating something false about the operator's config.
1829
- * The request blocks until the servers are enumerated (measured: complete on
1830
- * the very first call), so there is no half-populated answer to race.
1831
- *
1832
- * Resolves undefined only when there is genuinely nothing to say — the
1833
- * session is closed, or the child could not be spoken to. The route turns
1834
- * that into a 501.
1835
- *
1836
- * **Listing only.** There is no per-server reconnect or toggle on this
1837
- * transport — hence no `reconnectMcpServer`/`setMcpServerEnabled` here, and
1838
- * `ENGINE_CAPABILITIES.codex.mcpServerActions: false` so clients render the
1839
- * panel read-only instead of offering buttons that cannot work.
1840
- */
1841
791
  mcpServers(): Promise<McpServerStatusInfo[] | undefined>;
1842
792
  }
1843
793
  //#endregion
1844
794
  //#region src/engines/codex/process.d.ts
1845
- /**
1846
- * Spawn one `codex app-server` child and frame JSON-RPC over its stdio — the
1847
- * real {@link AppServerConnectFn}. The child's env is passed **complete**
1848
- * (a provided spawn env replaces process.env, never merges with it), with the
1849
- * profile's CODEX_HOME pin already applied by the runner.
1850
- *
1851
- * No spawn cwd: the working directory is a thread/turn parameter, and a cwd
1852
- * that doesn't exist should fail the *turn* with codex's own error, not the
1853
- * spawn.
1854
- */
1855
795
  declare function connectAppServer(options: {
1856
796
  executable: string;
1857
797
  env: Record<string, string>;
1858
798
  }): AppServerConnection;
1859
799
  //#endregion
1860
800
  //#region src/engines/codex/jsonrpc.d.ts
1861
- /**
1862
- * A JSON-RPC error response from the peer, or one we return to it. `code`
1863
- * follows the JSON-RPC 2.0 reserved ranges (-32601 = method not found).
1864
- */
1865
801
  declare class JsonRpcError extends Error {
1866
802
  readonly code: number;
1867
803
  constructor(code: number, message: string);
1868
804
  }
1869
- /**
1870
- * JSON-RPC over a `codex app-server` child's stdio: **newline-delimited JSON**,
1871
- * one message per line, and — verified against 0.146.0 — an envelope *without*
1872
- * the `jsonrpc: "2.0"` field (`{id, method, params}` / `{id, result}` /
1873
- * `{id, error}`; the binary's own schema marks only those required). Server→
1874
- * client notifications additionally carry a top-level `emittedAtMs`, ignored
1875
- * here.
1876
- *
1877
- * Transport only: no method knowledge, no process ownership. The process
1878
- * wrapper (`process.ts`) owns the child and calls {@link fail} when it dies so
1879
- * every in-flight request rejects instead of hanging.
1880
- */
1881
805
  declare class JsonRpcStdioConnection {
1882
806
  #private;
1883
807
  constructor(options: {
@@ -1888,23 +812,10 @@ declare class JsonRpcStdioConnection {
1888
812
  notify(method: string, params?: unknown): void;
1889
813
  onNotification(handler: (method: string, params: unknown) => void): void;
1890
814
  onRequest(handler: (method: string, params: unknown, id: string | number) => Promise<unknown>): void;
1891
- /** Reject everything in flight and refuse new traffic — the child is gone
1892
- * (or the session is over). Idempotent. */
1893
815
  fail(message: string): void;
1894
816
  }
1895
817
  //#endregion
1896
818
  //#region src/engines/provider/adapter.d.ts
1897
- /**
1898
- * The model-agnostic provider engine as a pseudo-adapter: capabilities and an
1899
- * env-var probe live here, but its runners are assembled by the host's
1900
- * `createEngineRunner` hook (which is where provider credentials are resolved
1901
- * and model SDKs are imported — neither belongs in this repo's import graph).
1902
- * The server routes provider creates to the hook; `createRunner` here throws
1903
- * so a mis-routed call fails loudly instead of quietly building nothing.
1904
- *
1905
- * The catalog is empty by the same token: provider model ids are operator-
1906
- * declared per profile (`provider.models`), not shipped with releases.
1907
- */
1908
819
  declare const providerAdapter: EngineAdapter;
1909
820
  //#endregion
1910
821
  export { AiSdkRunner, type AiSdkRunnerConfig, type AiSdkSessionState, type AppServerConnectFn, type AppServerConnectOptions, type AppServerConnection, type AppServerHistoryTurn, type AppServerItem, type AppServerThreadListResponse, type AppServerThreadSummary, type AppServerTokenUsage, type AppServerTurn, type AppServerUserInput, type AttachmentInput, type AttachmentKind, type BridgeAnswer, BrowserBridgeExecutor, type BrowserBridgeExecutorOptions, CLAUDE_CATALOG, CODEX_CATALOG, type ClaudeAuthProbe, type ClaudeAuthStatus, CodexRunner, type CodexRunnerConfig, type DeferredDispatch, DeferredExecutor, type DeferredExecutorOptions, type EngineAdapter, type EngineAvailability, type EngineRunnerRequest, type EngineSessionOptions, type HistoryFn, type HostFetch, type HostToolDefinition, InputQueue, JsonRpcError, JsonRpcStdioConnection, type LanguageModel, type McpConnection, type ModelCatalog, type ParkedExecution, type PendingEntry, type PendingKind, type PendingOutcome, PendingRequestRegistry, type PendingToolCall, type PermissionDecision, type QueryFn, QuickJsExecutor, type QuickJsExecutorOptions, type RegisterOptions, type Runner, type RunnerSnapshot, SUPPORTED_ATTACHMENT_TYPES, type SessionEventListener, SessionRunner, type SessionRunnerConfig, type SettledBy, type Tool, type ToolCallOutput, type ToolContext, type ToolContextOptions, type ToolDefinition, type ToolExecutionCall, type ToolExecutionDispatch, type ToolExecutionResult, type ToolExecutor, type ToolSet, type ToolTrust, type WebFetchDigest, type WebFetchFn, type WebFetchOptions, type WebFetchResult, attachmentContentBlocks, attachmentKind, attachmentRef, checkClaudeAuth, claudeAdapter, codexAdapter, connectAppServer, connectMcpTools, createEngineSession, createToolContext, createWebFetch, getEngineAdapter, htmlToMarkdown, isHostAllowed, isPrivateAddress, listCodexSessions, mcpStatusInfo, modelOptionsFromSdk, normalizeMediaType, normalizeSdkMessage, providerAdapter, replaySlice, resolveBundledClaudeExecutable, resolveBundledCodexExecutable, toApiMessage, toExecutionResult, truncateResultBlocks, withHostTools, withMcpTools };