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