@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 +82 -1213
- package/build/index.mjs +291 -1968
- package/build/index.mjs.map +1 -1
- package/package.json +14 -14
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
|
-
|
|
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
|
-
|
|
64
|
-
sessionId: string;
|
|
65
|
-
tool: string;
|
|
66
|
-
input: unknown;
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
66
|
+
engine: ProfileEngine;
|
|
143
67
|
id: string;
|
|
144
68
|
createdAt: number;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
131
|
+
queryFn?: QueryFn;
|
|
330
132
|
env?: Record<string, string | undefined>;
|
|
331
|
-
pathToClaudeCodeExecutable?: string;
|
|
332
|
-
extraOptions?: Partial<Options>;
|
|
133
|
+
pathToClaudeCodeExecutable?: string;
|
|
134
|
+
extraOptions?: Partial<Options>;
|
|
333
135
|
defaultApprovalTimeoutMs?: number;
|
|
334
|
-
|
|
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
|
-
|
|
494
|
-
|
|
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
|
-
|
|
501
|
-
|
|
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
|
-
};
|
|
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;
|
|
527
|
-
approvalTimeoutMs?: number;
|
|
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
|
-
|
|
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[];
|
|
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
|
-
|
|
783
|
-
|
|
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;
|
|
828
|
-
expiresAt?: number;
|
|
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>;
|
|
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
|
-
|
|
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;
|
|
374
|
+
input: unknown;
|
|
927
375
|
vfsSeed?: Record<string, string>;
|
|
928
376
|
limits?: {
|
|
929
377
|
timeoutMs?: number;
|
|
930
378
|
memoryLimitBytes?: number;
|
|
931
|
-
};
|
|
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
|
-
|
|
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
|
-
|
|
977
|
-
digest?: string;
|
|
978
|
-
markdown?: string;
|
|
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;
|
|
992
|
-
maxContentBytes?: number;
|
|
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
|
-
|
|
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;
|
|
1037
|
-
vfs?: SandboxVfs;
|
|
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
|
-
}>>;
|
|
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;
|
|
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[];
|
|
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
|
-
|
|
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;
|
|
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;
|
|
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[];
|
|
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;
|
|
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;
|
|
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';
|
|
1575
|
-
kind: string;
|
|
1576
|
-
agentThreadId: string;
|
|
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;
|
|
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;
|
|
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;
|
|
1638
|
-
name?: string | null;
|
|
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;
|
|
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
|
-
|
|
1668
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
1734
|
-
*
|
|
1735
|
-
*
|
|
1736
|
-
*
|
|
1737
|
-
*
|
|
1738
|
-
*
|
|
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
|
-
|
|
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 };
|