@workerdeck/core 0.6.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/LICENSE +21 -0
- package/README.md +140 -0
- package/build/index.d.mts +848 -0
- package/build/index.mjs +2324 -0
- package/build/index.mjs.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,848 @@
|
|
|
1
|
+
import { Options, Query, SDKMessage, SDKUserMessage, SessionMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
import { LanguageModel, ModelMessage, Tool, ToolSet } from "ai";
|
|
3
|
+
import { SandboxEngine, SandboxVfs } from "@workerdeck/sandbox";
|
|
4
|
+
import { ApiMessage, CreateSessionRequest, McpServerConfigWire, PermissionMode, PermissionRequest, ProfileEngine, ProfileInfo, SessionEvent, SessionEventBody, SessionInfo, SessionStatus, ToolCallRequestFrame, ToolExecutionBackend, ToolExecutionOutput } from "@workerdeck/protocol";
|
|
5
|
+
|
|
6
|
+
//#region src/tool-executor.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Result of one tool execution, whenever it arrives. `failed` is a normal
|
|
9
|
+
* outcome the agent loop adapts to — not an exception.
|
|
10
|
+
*/
|
|
11
|
+
type ToolExecutionResult = {
|
|
12
|
+
status: 'ok';
|
|
13
|
+
output: unknown;
|
|
14
|
+
logs?: string[];
|
|
15
|
+
} | {
|
|
16
|
+
status: 'failed';
|
|
17
|
+
reason: string;
|
|
18
|
+
error: string;
|
|
19
|
+
logs?: string[];
|
|
20
|
+
};
|
|
21
|
+
type ToolExecutionCall = {
|
|
22
|
+
/** Stable, persisted correlation id. Results are matched and applied by it. */executionId: string;
|
|
23
|
+
sessionId: string; /** Tool name, e.g. 'eval_script'. */
|
|
24
|
+
tool: string; /** Validated tool input. */
|
|
25
|
+
input: unknown; /** Scoped scratch filesystem for this execution's thread. */
|
|
26
|
+
vfs?: SandboxVfs;
|
|
27
|
+
limits?: {
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
memoryLimitBytes?: number;
|
|
30
|
+
};
|
|
31
|
+
signal?: AbortSignal;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Dispatch outcome. `settled` carries the result inline; `pending` means it
|
|
35
|
+
* arrives out-of-band later, keyed by executionId — the shape that lets a
|
|
36
|
+
* deferred or remote executor drop in without touching the runner or protocol.
|
|
37
|
+
*/
|
|
38
|
+
type ToolExecutionDispatch = {
|
|
39
|
+
executionId: string;
|
|
40
|
+
status: 'settled';
|
|
41
|
+
result: ToolExecutionResult;
|
|
42
|
+
} | {
|
|
43
|
+
executionId: string;
|
|
44
|
+
status: 'pending';
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* The seam between the agent loop and wherever code actually runs — in-process
|
|
48
|
+
* QuickJS, a browser tab over the WS bridge, or a managed sandbox. Backends are
|
|
49
|
+
* interchangeable and selected by context.
|
|
50
|
+
*/
|
|
51
|
+
/**
|
|
52
|
+
* How an executor will handle one specific call, asked before dispatch so the
|
|
53
|
+
* runner can announce it on `execution_dispatched` (which is emitted before the
|
|
54
|
+
* call goes out, so a bridged request never precedes its own record).
|
|
55
|
+
*
|
|
56
|
+
* Per **call**, not per executor: an executor that routes by tool name can send
|
|
57
|
+
* `eval_script` to the in-process sandbox and a long-running tool to a remote
|
|
58
|
+
* worker, and only the latter should park the session.
|
|
59
|
+
*/
|
|
60
|
+
type ToolExecutionProfile = {
|
|
61
|
+
/** Reported on `execution_dispatched`; falls back to the runner's configured default. */backend?: ToolExecutionBackend;
|
|
62
|
+
/**
|
|
63
|
+
* True when this execution may outlive not just the turn but the live runner:
|
|
64
|
+
* the session parks (state persisted, runner torn down) and the result arrives
|
|
65
|
+
* out of band, keyed by `executionId`.
|
|
66
|
+
*/
|
|
67
|
+
deferred?: boolean;
|
|
68
|
+
/** Advisory deadline, published as `expiresAt`. For a deferred execution the
|
|
69
|
+
* timer itself belongs to the host — the runner may be gone when it fires. */
|
|
70
|
+
timeoutMs?: number;
|
|
71
|
+
};
|
|
72
|
+
interface ToolExecutor {
|
|
73
|
+
/** Describe what this call will be: backend, deferredness, deadline. Omitted =
|
|
74
|
+
* an in-band execution on the runner's configured backend. */
|
|
75
|
+
describe?(call: ToolExecutionCall): ToolExecutionProfile;
|
|
76
|
+
dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch>;
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/runner-interface.d.ts
|
|
80
|
+
type SessionEventListener = (event: SessionEvent) => void;
|
|
81
|
+
/** One deferred execution a parked session is waiting on. */
|
|
82
|
+
type ParkedExecution = {
|
|
83
|
+
executionId: string;
|
|
84
|
+
toolName: string; /** Epoch ms the host's execution watchdog should fire at, when the backend set one. */
|
|
85
|
+
expiresAt?: number;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Everything needed to rebuild a torn-down session under the same id — the durable
|
|
89
|
+
* half of deferred execution. The engine-neutral fields are what the host persists,
|
|
90
|
+
* indexes, and replays; `state` is the engine's own continuation state (for the
|
|
91
|
+
* provider engine, its ModelMessage history) and is **opaque** outside it. Keeping
|
|
92
|
+
* it opaque is what lets `packages/server` persist a provider session without ever
|
|
93
|
+
* importing a model SDK.
|
|
94
|
+
*
|
|
95
|
+
* Must stay JSON-serializable end to end: a durable store round-trips it verbatim.
|
|
96
|
+
* "Serializable" here means round-trips *unchanged* — a Date, a Map, or a typed
|
|
97
|
+
* array inside `state` survives `JSON.stringify` as something else and rehydrates
|
|
98
|
+
* wrong. Only the in-memory store hides that, by never serializing at all.
|
|
99
|
+
*/
|
|
100
|
+
type RunnerSnapshot = {
|
|
101
|
+
/** Engine that produced it. Rehydrating into a different one is refused. */engine: ProfileEngine; /** Session id the rebuilt runner must adopt. */
|
|
102
|
+
id: string;
|
|
103
|
+
createdAt: number;
|
|
104
|
+
/** Last emitted seq — the rebuilt runner continues numbering from here, so a
|
|
105
|
+
* client reattaching with `afterSeq` sees one unbroken stream. */
|
|
106
|
+
seq: number; /** The seq-numbered event log, replayed to clients on rehydration. */
|
|
107
|
+
events: SessionEvent[]; /** Scratch filesystem contents, for engines that have one. */
|
|
108
|
+
vfs?: Record<string, string>; /** The deferred executions this session parked on. */
|
|
109
|
+
parked: ParkedExecution[]; /** Engine-private continuation state. Never inspected by the host. */
|
|
110
|
+
state: unknown;
|
|
111
|
+
};
|
|
112
|
+
type PermissionDecision = {
|
|
113
|
+
behavior: 'allow';
|
|
114
|
+
updatedInput?: Record<string, unknown>;
|
|
115
|
+
} | {
|
|
116
|
+
behavior: 'deny';
|
|
117
|
+
message?: string;
|
|
118
|
+
interrupt?: boolean;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Engine-independent runner surface — exactly what the server and queue consume.
|
|
122
|
+
* `SessionRunner` (Claude / Agent SDK) implements it today; additional engines
|
|
123
|
+
* implement the same contract and are selected behind it. Engine-specific
|
|
124
|
+
* machinery (SDK options, approval callbacks, input-queue shapes) stays inside
|
|
125
|
+
* the implementations.
|
|
126
|
+
*/
|
|
127
|
+
interface Runner {
|
|
128
|
+
readonly id: string;
|
|
129
|
+
readonly pendingApprovals: PermissionRequest[];
|
|
130
|
+
/** The session's scratch filesystem, when its engine has one. The server's
|
|
131
|
+
* file routes (GET /sessions/:id/files[...]) read it to serve deliverables;
|
|
132
|
+
* engines without a VFS (the Claude CLI engine) simply don't expose it. */
|
|
133
|
+
readonly vfs?: SandboxVfs;
|
|
134
|
+
/** Begin the session. Idempotent; returns the run promise (resolves when the run ends). */
|
|
135
|
+
start(): Promise<void>;
|
|
136
|
+
info(): SessionInfo;
|
|
137
|
+
/** Replay buffered events with seq > afterSeq, then deliver live events. Returns unsubscribe. */
|
|
138
|
+
subscribe(listener: SessionEventListener, afterSeq?: number): () => void;
|
|
139
|
+
/** Queue a user message for the session (starts the next turn when idle). */
|
|
140
|
+
sendMessage(text: string): void;
|
|
141
|
+
/** Resolve a pending permission request. Returns false if the id is unknown (e.g. timed out). */
|
|
142
|
+
resolvePermission(requestId: string, decision: PermissionDecision): boolean;
|
|
143
|
+
interrupt(): Promise<void>;
|
|
144
|
+
setPermissionMode(mode: PermissionMode): Promise<void>;
|
|
145
|
+
/** Switch the model for subsequent responses; undefined = back to the default. */
|
|
146
|
+
setModel(model?: string): Promise<void>;
|
|
147
|
+
/** Deliver the terminal result of an out-of-band tool execution (e.g. a bridged
|
|
148
|
+
* call answered by a browser client). Optional: engines that never execute
|
|
149
|
+
* out-of-band simply don't expose it. Idempotent by executionId — unknown or
|
|
150
|
+
* already-settled ids return false. */
|
|
151
|
+
settleExecution?(executionId: string, result: ToolExecutionResult): boolean;
|
|
152
|
+
/**
|
|
153
|
+
* Park: capture durable state, release engine resources, and go inert — without
|
|
154
|
+
* ending the session (no `session_closed`; the status becomes `parked`). The host
|
|
155
|
+
* persists the snapshot, drops the runner, and rebuilds it under the same id when
|
|
156
|
+
* a deferred execution's result arrives.
|
|
157
|
+
*
|
|
158
|
+
* Returns undefined when parking is not possible right now — a turn is in flight,
|
|
159
|
+
* nothing is actually parked, or the engine doesn't support it (the Claude engine
|
|
160
|
+
* doesn't: the CLI owns its own process state).
|
|
161
|
+
*/
|
|
162
|
+
park?(): RunnerSnapshot | undefined;
|
|
163
|
+
/** Emit a session_error and terminate. For host-enforced policy (e.g. requireApiKey). */
|
|
164
|
+
fail(message: string): void;
|
|
165
|
+
/** Terminate the session and any underlying engine process. */
|
|
166
|
+
close(reason?: 'client' | 'server' | 'error'): void;
|
|
167
|
+
}
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/runner.d.ts
|
|
170
|
+
type QueryFn = (params: {
|
|
171
|
+
prompt: AsyncIterable<SDKUserMessage>;
|
|
172
|
+
options?: Options;
|
|
173
|
+
}) => Query;
|
|
174
|
+
type HistoryFn = (sdkSessionId: string, options: {
|
|
175
|
+
dir?: string;
|
|
176
|
+
}) => Promise<SessionMessage[]>;
|
|
177
|
+
type SessionRunnerConfig = CreateSessionRequest & {
|
|
178
|
+
/** Injectable query implementation (tests, instrumentation). Defaults to the SDK's query(). */queryFn?: QueryFn; /** Environment for the spawned Claude Code process. Defaults to process.env. */
|
|
179
|
+
env?: Record<string, string | undefined>;
|
|
180
|
+
pathToClaudeCodeExecutable?: string; /** Escape hatch merged last into the SDK Options. */
|
|
181
|
+
extraOptions?: Partial<Options>; /** Timeout for pending approvals when the request itself doesn't set one. Default 300000. */
|
|
182
|
+
defaultApprovalTimeoutMs?: number;
|
|
183
|
+
/** With `resume`: emit the resumed session's history as replay events before the query
|
|
184
|
+
* starts, so late-attaching clients get a full transcript. Default true. */
|
|
185
|
+
backfillHistory?: boolean; /** Injectable history reader (tests). Defaults to the SDK's getSessionMessages. */
|
|
186
|
+
historyFn?: HistoryFn;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* One live Agent SDK session: owns the query() call, the streaming input queue, the
|
|
190
|
+
* pending-approval table, and a seq-numbered event log that subscribers can replay.
|
|
191
|
+
* No transport — the server (or any host) subscribes and bridges to the wire.
|
|
192
|
+
*/
|
|
193
|
+
declare class SessionRunner implements Runner {
|
|
194
|
+
#private;
|
|
195
|
+
readonly id: string;
|
|
196
|
+
readonly createdAt: number;
|
|
197
|
+
constructor(config: SessionRunnerConfig, id?: string);
|
|
198
|
+
get status(): SessionStatus;
|
|
199
|
+
get sdkSessionId(): string | undefined;
|
|
200
|
+
get lastSeq(): number;
|
|
201
|
+
/** 'oauth' = claude.ai subscription credentials; other values are API-key provenance. */
|
|
202
|
+
get apiKeySource(): string | undefined;
|
|
203
|
+
get pendingApprovals(): PermissionRequest[];
|
|
204
|
+
info(): SessionInfo;
|
|
205
|
+
/** Begin the session. Idempotent; returns the run promise (resolves when the query ends). */
|
|
206
|
+
start(): Promise<void>;
|
|
207
|
+
/** Queue a user message for the session (starts the next turn when idle). */
|
|
208
|
+
sendMessage(text: string): void;
|
|
209
|
+
/** Resolve a pending permission request. Returns false if the id is unknown (e.g. timed out). */
|
|
210
|
+
resolvePermission(requestId: string, decision: PermissionDecision): boolean;
|
|
211
|
+
interrupt(): Promise<void>;
|
|
212
|
+
setPermissionMode(mode: PermissionMode): Promise<void>;
|
|
213
|
+
/** Switch the model for subsequent responses; undefined = back to the default. */
|
|
214
|
+
setModel(model?: string): Promise<void>;
|
|
215
|
+
/** Emit a session_error and terminate. For host-enforced policy (e.g. requireApiKey). */
|
|
216
|
+
fail(message: string): void;
|
|
217
|
+
/** Terminate the session and the underlying CLI subprocess. */
|
|
218
|
+
close(reason?: 'client' | 'server' | 'error'): void;
|
|
219
|
+
/**
|
|
220
|
+
* Replay buffered events with seq > afterSeq, then deliver live events.
|
|
221
|
+
* Returns an unsubscribe function.
|
|
222
|
+
*/
|
|
223
|
+
subscribe(listener: SessionEventListener, afterSeq?: number): () => void;
|
|
224
|
+
}
|
|
225
|
+
//#endregion
|
|
226
|
+
//#region src/ai-sdk-runner.d.ts
|
|
227
|
+
/** `cwd` is optional for this engine: the loop has no host-filesystem coupling
|
|
228
|
+
* (tools get scoped VFS handles instead). Defaults to process.cwd() for display. */
|
|
229
|
+
type AiSdkRunnerConfig = Omit<CreateSessionRequest, 'cwd'> & {
|
|
230
|
+
cwd?: string;
|
|
231
|
+
/** AI SDK language model instance (or gateway model id string). Provider
|
|
232
|
+
* resolution from profiles happens host-side; core takes the resolved model. */
|
|
233
|
+
languageModel: LanguageModel;
|
|
234
|
+
/** Tools available to the loop. Tools WITHOUT `execute` halt the loop when
|
|
235
|
+
* called; their calls surface via `pendingToolCalls` and are answered with
|
|
236
|
+
* `resolveToolCall()`, which re-enters the loop by message-state replay. */
|
|
237
|
+
tools?: ToolSet; /** System prompt (AI SDK v7 `instructions`). */
|
|
238
|
+
instructions?: string; /** Max loop steps per turn. Default 20. */
|
|
239
|
+
maxSteps?: number;
|
|
240
|
+
/**
|
|
241
|
+
* Executes tool calls the loop cannot run inline (tools declared without
|
|
242
|
+
* `execute`). With one set, the runner drives the whole cycle itself:
|
|
243
|
+
* dispatch on park, apply the result, re-enter. Without one, parked calls
|
|
244
|
+
* stay on {@link pendingToolCalls} for the host to answer via
|
|
245
|
+
* {@link resolveToolCall}.
|
|
246
|
+
*/
|
|
247
|
+
executor?: ToolExecutor; /** Names the executor handles. Others stay pending for the host. */
|
|
248
|
+
executableTools?: string[]; /** Scratch filesystem handed to sandboxed executions. */
|
|
249
|
+
vfs?: SandboxVfs; /** Per-execution limits passed to the executor. */
|
|
250
|
+
executionLimits?: {
|
|
251
|
+
timeoutMs?: number;
|
|
252
|
+
memoryLimitBytes?: number;
|
|
253
|
+
}; /** Which backend the executor represents, for `execution_dispatched` events. */
|
|
254
|
+
executionBackend?: ToolExecutionBackend; /** Swap models mid-session (`set_model`). Unset = setModel() is rejected. */
|
|
255
|
+
resolveModel?: (modelId: string | undefined) => LanguageModel;
|
|
256
|
+
/** Called once when the session closes — release per-session resources the
|
|
257
|
+
* host attached (an MCP connection, a watcher). Errors are swallowed. Also
|
|
258
|
+
* runs when the session parks: parking releases the same resources. */
|
|
259
|
+
onClose?: () => void | Promise<void>;
|
|
260
|
+
/**
|
|
261
|
+
* Rebuild a parked session from {@link AiSdkRunner.park}'s snapshot instead of
|
|
262
|
+
* starting a fresh one: the id, event log, seq counter, message history, and
|
|
263
|
+
* the executions it parked on are all adopted. The rest of the config is the
|
|
264
|
+
* live wiring (model, tools, executor, VFS) and is taken as given — a
|
|
265
|
+
* rehydrated session may legitimately come up against a re-created tool set.
|
|
266
|
+
*/
|
|
267
|
+
restore?: RunnerSnapshot;
|
|
268
|
+
};
|
|
269
|
+
/** An external (execute-less) tool call the loop is parked on. */
|
|
270
|
+
type PendingToolCall = {
|
|
271
|
+
toolCallId: string;
|
|
272
|
+
toolName: string;
|
|
273
|
+
input: unknown;
|
|
274
|
+
/** True when the executor declared the execution deferred — the session may
|
|
275
|
+
* park on it, and only a host-delivered result can settle it. */
|
|
276
|
+
deferred?: boolean; /** Epoch ms the host's execution watchdog should fire at. */
|
|
277
|
+
expiresAt?: number;
|
|
278
|
+
};
|
|
279
|
+
/** The provider engine's half of a {@link RunnerSnapshot} — its continuation
|
|
280
|
+
* state. Opaque to the host; only this class reads it. */
|
|
281
|
+
type AiSdkSessionState = {
|
|
282
|
+
messages: ModelMessage[];
|
|
283
|
+
pendingToolCalls: PendingToolCall[]; /** Calls already handed to an executor, so rehydration never re-dispatches them. */
|
|
284
|
+
dispatched: string[];
|
|
285
|
+
numTurns: number;
|
|
286
|
+
totalUsage: {
|
|
287
|
+
input: number;
|
|
288
|
+
output: number;
|
|
289
|
+
cacheWrite: number;
|
|
290
|
+
cacheRead: number;
|
|
291
|
+
};
|
|
292
|
+
/** The in-progress turn's accumulator: a parked turn's earlier legs still owe
|
|
293
|
+
* their tokens and elapsed time to the turn_result that eventually lands. */
|
|
294
|
+
turnAccum?: {
|
|
295
|
+
startedAt: number;
|
|
296
|
+
input: number;
|
|
297
|
+
output: number;
|
|
298
|
+
cacheWrite: number;
|
|
299
|
+
cacheRead: number;
|
|
300
|
+
};
|
|
301
|
+
permissionMode: PermissionMode;
|
|
302
|
+
/** Model alias last requested (config.model or a set_model), NOT the resolved
|
|
303
|
+
* provider model id — re-resolution goes back through `resolveModel`. */
|
|
304
|
+
model?: string;
|
|
305
|
+
lastActivityAt?: number;
|
|
306
|
+
/** When the snapshot was taken, so a rehydrated turn can discount the time it
|
|
307
|
+
* spent parked instead of billing it as elapsed turn duration. */
|
|
308
|
+
parkedAt?: number;
|
|
309
|
+
};
|
|
310
|
+
type ToolCallOutput = {
|
|
311
|
+
type: 'text';
|
|
312
|
+
value: string;
|
|
313
|
+
} | {
|
|
314
|
+
type: 'json';
|
|
315
|
+
value: unknown;
|
|
316
|
+
};
|
|
317
|
+
/**
|
|
318
|
+
* Model-agnostic runner over the AI SDK v7 ToolLoopAgent. The session's durable
|
|
319
|
+
* state is its ModelMessage history: every turn — including continuation after an
|
|
320
|
+
* externally-executed tool call — is a fresh streamed call over that history
|
|
321
|
+
* (message-state replay; the loop cannot be suspended). Output is emitted as it
|
|
322
|
+
* happens: `stream_delta` per token (unless includePartialMessages is false) and
|
|
323
|
+
* assistant/tool messages per step. Emits the same seq-numbered SessionEvent log
|
|
324
|
+
* as SessionRunner; engine-specific CLI telemetry (system_init, capabilities,
|
|
325
|
+
* rate_limit, ...) is simply never emitted.
|
|
326
|
+
*/
|
|
327
|
+
declare class AiSdkRunner implements Runner {
|
|
328
|
+
#private;
|
|
329
|
+
readonly id: string;
|
|
330
|
+
readonly createdAt: number;
|
|
331
|
+
constructor(config: AiSdkRunnerConfig, id?: string);
|
|
332
|
+
get status(): SessionStatus;
|
|
333
|
+
get lastSeq(): number;
|
|
334
|
+
/** The session's durable state — persist to park, replay to rehydrate. */
|
|
335
|
+
get messages(): ModelMessage[];
|
|
336
|
+
/** External tool calls the loop is currently parked on. */
|
|
337
|
+
get pendingToolCalls(): PendingToolCall[];
|
|
338
|
+
get pendingApprovals(): PermissionRequest[];
|
|
339
|
+
/** The session's scratch filesystem (see Runner.vfs) — the server's file
|
|
340
|
+
* routes serve deliverables straight from it. */
|
|
341
|
+
get vfs(): SandboxVfs | undefined;
|
|
342
|
+
info(): SessionInfo;
|
|
343
|
+
start(): Promise<void>;
|
|
344
|
+
/**
|
|
345
|
+
* Snapshot durable state, release engine resources, and go inert — the session
|
|
346
|
+
* continues in the snapshot, not in this object. Returns undefined when parking
|
|
347
|
+
* would lose work or has nothing to wait for: a turn in flight, no parked call,
|
|
348
|
+
* or an already-closed/parked runner.
|
|
349
|
+
*/
|
|
350
|
+
park(): RunnerSnapshot | undefined;
|
|
351
|
+
sendMessage(text: string): void;
|
|
352
|
+
/**
|
|
353
|
+
* Deliver the result of an external (execute-less) tool call. Appends the
|
|
354
|
+
* tool-result message and, once no calls remain pending, re-enters the loop.
|
|
355
|
+
* Idempotent per toolCallId: unknown/already-settled ids return false.
|
|
356
|
+
*/
|
|
357
|
+
resolveToolCall(toolCallId: string, output: ToolCallOutput, options?: {
|
|
358
|
+
isError?: boolean;
|
|
359
|
+
}): boolean;
|
|
360
|
+
resolvePermission(_requestId: string, _decision: PermissionDecision): boolean;
|
|
361
|
+
/** Emit `file_delivered` — the deliver_file tool's hand-over event (wired by
|
|
362
|
+
* createEngineSession via ToolContextOptions.onFileDelivered). */
|
|
363
|
+
emitFileDelivered(file: {
|
|
364
|
+
path: string;
|
|
365
|
+
bytes: number;
|
|
366
|
+
description?: string;
|
|
367
|
+
}): void;
|
|
368
|
+
/**
|
|
369
|
+
* One plain generateText over the session's current model, billed into the
|
|
370
|
+
* running turn's usage accumulator — the web_fetch digest pass uses this so
|
|
371
|
+
* its tokens are never lost from the turn's accounting.
|
|
372
|
+
*/
|
|
373
|
+
generateDigest(prompt: string): Promise<string>;
|
|
374
|
+
interrupt(): Promise<void>;
|
|
375
|
+
setPermissionMode(mode: PermissionMode): Promise<void>;
|
|
376
|
+
setModel(model?: string): Promise<void>;
|
|
377
|
+
fail(message: string): void;
|
|
378
|
+
close(reason?: 'client' | 'server' | 'error'): void;
|
|
379
|
+
subscribe(listener: SessionEventListener, afterSeq?: number): () => void;
|
|
380
|
+
/**
|
|
381
|
+
* Deliver the result of an execution this runner dispatched. Used by the host
|
|
382
|
+
* when a backend settled out-of-band (a browser bridge answering later, a
|
|
383
|
+
* deferred executor). Idempotent by executionId.
|
|
384
|
+
*/
|
|
385
|
+
settleExecution(executionId: string, result: ToolExecutionResult): boolean;
|
|
386
|
+
}
|
|
387
|
+
//#endregion
|
|
388
|
+
//#region src/claude-auth.d.ts
|
|
389
|
+
/**
|
|
390
|
+
* Credential presence for one Claude Code environment, as the CLI itself reports
|
|
391
|
+
* it. 'unknown' means the check could not run at all (no binary, a CLI too old
|
|
392
|
+
* for `auth status`, unparseable output) — which is NOT evidence of a missing
|
|
393
|
+
* login and must never be surfaced as one.
|
|
394
|
+
*/
|
|
395
|
+
type ClaudeAuthStatus = 'logged_in' | 'logged_out' | 'unknown';
|
|
396
|
+
/** Injectable form of {@link checkClaudeAuth} (tests, custom probes). */
|
|
397
|
+
type ClaudeAuthProbe = (env: Record<string, string | undefined>) => Promise<ClaudeAuthStatus>;
|
|
398
|
+
/**
|
|
399
|
+
* The native Claude Code binary the Agent SDK itself spawns, resolved the way
|
|
400
|
+
* the SDK resolves it: the platform-specific optional dependency installed next
|
|
401
|
+
* to the SDK package (`@anthropic-ai/claude-agent-sdk-<platform>-<arch>/claude`).
|
|
402
|
+
* Probing this binary rather than whatever `claude` is on PATH means an auth
|
|
403
|
+
* check answers for the executable sessions will actually run — the two can be
|
|
404
|
+
* different versions logged into different places. Returns undefined when it
|
|
405
|
+
* can't be found (optional dep skipped, unsupported platform); callers degrade
|
|
406
|
+
* to 'unknown', and the SDK surfaces its own error if a session is created.
|
|
407
|
+
*/
|
|
408
|
+
declare function resolveBundledClaudeExecutable(): string | undefined;
|
|
409
|
+
/**
|
|
410
|
+
* Ask the CLI whether `env` holds usable credentials: `claude auth status`
|
|
411
|
+
* prints a JSON verdict covering every source the CLI itself consults for that
|
|
412
|
+
* environment — `<CLAUDE_CONFIG_DIR>/.credentials.json`, the macOS login
|
|
413
|
+
* Keychain, ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, Bedrock/Vertex
|
|
414
|
+
* (verified against 2.1.217). Only the `loggedIn` boolean is ever read; the
|
|
415
|
+
* identity fields in the payload (email, org, subscription) never leave the
|
|
416
|
+
* parse. The exit code is deliberately ignored — 2.1.217 exits 1 on a
|
|
417
|
+
* logged-out verdict where other versions exit 0 — and anything that doesn't
|
|
418
|
+
* parse to a `loggedIn` boolean is 'unknown', because `auth status` is not a
|
|
419
|
+
* stable contract. Never rejects.
|
|
420
|
+
*/
|
|
421
|
+
declare function checkClaudeAuth(env: Record<string, string | undefined>, options?: {
|
|
422
|
+
executable?: string;
|
|
423
|
+
timeoutMs?: number;
|
|
424
|
+
}): Promise<ClaudeAuthStatus>;
|
|
425
|
+
//#endregion
|
|
426
|
+
//#region src/quickjs-executor.d.ts
|
|
427
|
+
/** Resolve a URL to text for the guest. Runs host-side with host authority —
|
|
428
|
+
* this is where a credential may be attached, never inside the sandbox. */
|
|
429
|
+
type HostFetch = (url: string, signal: AbortSignal) => Promise<string>;
|
|
430
|
+
type QuickJsExecutorOptions = {
|
|
431
|
+
engine: SandboxEngine;
|
|
432
|
+
/**
|
|
433
|
+
* Hostnames the guest may reach, exact or `*.example.com`. Empty/unset =
|
|
434
|
+
* no network at all (the guest's fetchText throws). Matched host-side; the
|
|
435
|
+
* guest is never told the allowlist and never holds a credential.
|
|
436
|
+
*/
|
|
437
|
+
allowedHosts?: string[]; /** Performs the actual request. Unset = global fetch, text body. */
|
|
438
|
+
hostFetch?: HostFetch;
|
|
439
|
+
/** Per-fetch cap. The guest deadline does NOT cover host-function time, so
|
|
440
|
+
* every capability needs its own bound. Default 10000. */
|
|
441
|
+
fetchTimeoutMs?: number; /** Default guest wall-clock limit when the call doesn't set one. Default 5000. */
|
|
442
|
+
defaultTimeoutMs?: number; /** Default guest allocator cap when the call doesn't set one. Default 64 MiB. */
|
|
443
|
+
defaultMemoryLimitBytes?: number;
|
|
444
|
+
};
|
|
445
|
+
/**
|
|
446
|
+
* In-process execution backend: runs a tool's untrusted script in the QuickJS
|
|
447
|
+
* WASM guest. Always settles inline — nothing downstream assumes that, which is
|
|
448
|
+
* what lets a deferred backend replace it behind the same seam.
|
|
449
|
+
*/
|
|
450
|
+
declare class QuickJsExecutor implements ToolExecutor {
|
|
451
|
+
#private;
|
|
452
|
+
constructor(options: QuickJsExecutorOptions);
|
|
453
|
+
dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch>;
|
|
454
|
+
}
|
|
455
|
+
/** Exact hostname match, or a single leading `*.` wildcard covering subdomains
|
|
456
|
+
* (not the bare parent). Only http(s) — no file:, data:, or other schemes. */
|
|
457
|
+
declare function isHostAllowed(url: string, allowedHosts: string[]): boolean;
|
|
458
|
+
//#endregion
|
|
459
|
+
//#region src/pending-registry.d.ts
|
|
460
|
+
/**
|
|
461
|
+
* One registry for every request that leaves the runner and must come back:
|
|
462
|
+
* permission approvals, browser-bridged tool calls, and deferred executions.
|
|
463
|
+
* They differ only in who answers and how long that takes — the correlation,
|
|
464
|
+
* timeout, idempotent settle, and provenance tagging are identical, so they
|
|
465
|
+
* live here once.
|
|
466
|
+
*/
|
|
467
|
+
/** What kind of async request this is. Purely descriptive — the mechanics are shared. */
|
|
468
|
+
type PendingKind = 'approval' | 'tool_call' | 'execution';
|
|
469
|
+
/** Who settled a request. Mirrors the existing approval vocabulary. */
|
|
470
|
+
type SettledBy = 'client' | 'timeout' | 'policy' | 'server';
|
|
471
|
+
type PendingOutcome<T> = {
|
|
472
|
+
ok: true;
|
|
473
|
+
value: T;
|
|
474
|
+
settledBy: SettledBy;
|
|
475
|
+
} | {
|
|
476
|
+
ok: false;
|
|
477
|
+
reason: string;
|
|
478
|
+
error: string;
|
|
479
|
+
settledBy: SettledBy;
|
|
480
|
+
};
|
|
481
|
+
type PendingEntry = {
|
|
482
|
+
id: string;
|
|
483
|
+
kind: PendingKind;
|
|
484
|
+
createdAt: number; /** Epoch ms the timeout policy fires at, when one was set. */
|
|
485
|
+
expiresAt?: number; /** Caller-supplied descriptor for display/rehydration (tool name, request, ...). */
|
|
486
|
+
meta?: Record<string, unknown>;
|
|
487
|
+
};
|
|
488
|
+
type RegisterOptions<T> = {
|
|
489
|
+
id: string;
|
|
490
|
+
kind: PendingKind;
|
|
491
|
+
/** Fail the request automatically after this long. Omit for no deadline
|
|
492
|
+
* (deferred executions whose watchdog lives elsewhere). */
|
|
493
|
+
timeoutMs?: number;
|
|
494
|
+
meta?: Record<string, unknown>; /** Called when the entry settles, however it settled. For emitting events. */
|
|
495
|
+
onSettle?: (outcome: PendingOutcome<T>, entry: PendingEntry) => void;
|
|
496
|
+
};
|
|
497
|
+
declare class PendingRequestRegistry {
|
|
498
|
+
#private;
|
|
499
|
+
get size(): number;
|
|
500
|
+
/**
|
|
501
|
+
* Register a request and get a promise for its outcome. The promise **never
|
|
502
|
+
* rejects**: a timeout or cancellation resolves with `ok: false` so callers
|
|
503
|
+
* feed the failure back into the agent loop instead of unwinding it.
|
|
504
|
+
*
|
|
505
|
+
* Re-registering a live id throws — silently replacing it would strand the
|
|
506
|
+
* first waiter forever.
|
|
507
|
+
*/
|
|
508
|
+
register<T>(options: RegisterOptions<T>): Promise<PendingOutcome<T>>;
|
|
509
|
+
/** Deliver a result. Returns false for unknown or already-settled ids —
|
|
510
|
+
* duplicate and late deliveries are no-ops, never a second application. */
|
|
511
|
+
settle<T>(id: string, value: T, settledBy?: SettledBy): boolean;
|
|
512
|
+
/** Fail a request. Same idempotence guarantee as {@link settle}. */
|
|
513
|
+
fail(id: string, reason: string, error: string, settledBy?: SettledBy): boolean;
|
|
514
|
+
has(id: string): boolean;
|
|
515
|
+
get(id: string): PendingEntry | undefined;
|
|
516
|
+
list(kind?: PendingKind): PendingEntry[];
|
|
517
|
+
/** Fail everything (optionally of one kind) — session close, turn interrupt. */
|
|
518
|
+
cancelAll(reason: string, error: string, kind?: PendingKind): number;
|
|
519
|
+
}
|
|
520
|
+
//#endregion
|
|
521
|
+
//#region src/browser-bridge-executor.d.ts
|
|
522
|
+
/** Answer a bridged call, as delivered by the client over the wire. */
|
|
523
|
+
type BridgeAnswer = {
|
|
524
|
+
output: ToolExecutionOutput;
|
|
525
|
+
logs?: string[];
|
|
526
|
+
} | {
|
|
527
|
+
reason: string;
|
|
528
|
+
error: string;
|
|
529
|
+
logs?: string[];
|
|
530
|
+
};
|
|
531
|
+
type BrowserBridgeExecutorOptions = {
|
|
532
|
+
/**
|
|
533
|
+
* Put a `tool_call_request` on the wire to the attached client. Returning
|
|
534
|
+
* false means nobody is attached — the execution fails immediately rather
|
|
535
|
+
* than hanging until its deadline.
|
|
536
|
+
*/
|
|
537
|
+
send: (frame: ToolCallRequestFrame) => boolean; /** Tell the client to abandon a call the server gave up on. */
|
|
538
|
+
cancel?: (executionId: string, reason: string) => void; /** How long to wait for the client before failing the execution. Default 60000. */
|
|
539
|
+
timeoutMs?: number;
|
|
540
|
+
/**
|
|
541
|
+
* Called once per dispatched execution when it reaches a terminal result,
|
|
542
|
+
* however it got there (client answer, timeout, abort, no client). This is
|
|
543
|
+
* the wire back into the agent loop — the host feeds it to the runner's
|
|
544
|
+
* `resolveToolCall`. A timeout arrives here as a failed result, not silence.
|
|
545
|
+
*/
|
|
546
|
+
onResult?: (executionId: string, result: ToolExecutionResult) => void;
|
|
547
|
+
/** Share the session's registry so approvals, bridged calls, and deferred
|
|
548
|
+
* executions live in one table. Omit to get a private one. */
|
|
549
|
+
registry?: PendingRequestRegistry;
|
|
550
|
+
};
|
|
551
|
+
/**
|
|
552
|
+
* Executes tool calls in the attached client's own sandbox. The first backend
|
|
553
|
+
* that genuinely returns `pending`: dispatch puts a request on the wire and
|
|
554
|
+
* returns, and the result arrives later through {@link resolve}.
|
|
555
|
+
*
|
|
556
|
+
* Data locality is the point — documents can stay in the browser and never
|
|
557
|
+
* reach the server. The tradeoff is trust: whatever comes back is untrusted
|
|
558
|
+
* input, fine for the user's own data but never a source for authoritative
|
|
559
|
+
* server state (that is why MCP and secret-bearing tools are never bridged).
|
|
560
|
+
*/
|
|
561
|
+
declare class BrowserBridgeExecutor implements ToolExecutor {
|
|
562
|
+
#private;
|
|
563
|
+
readonly registry: PendingRequestRegistry;
|
|
564
|
+
constructor(options: BrowserBridgeExecutorOptions);
|
|
565
|
+
dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch>;
|
|
566
|
+
/**
|
|
567
|
+
* Apply a client's answer. Returns false when the id is unknown or already
|
|
568
|
+
* settled — a late result after a timeout must not re-open a settled call.
|
|
569
|
+
*/
|
|
570
|
+
resolve(executionId: string, answer: BridgeAnswer): boolean;
|
|
571
|
+
}
|
|
572
|
+
/** Map a registry outcome onto the executor's result contract. */
|
|
573
|
+
declare function toExecutionResult(outcome: PendingOutcome<BridgeAnswer>): ToolExecutionResult;
|
|
574
|
+
//#endregion
|
|
575
|
+
//#region src/deferred-executor.d.ts
|
|
576
|
+
/** A dispatched execution, as handed to the backend that will run it. */
|
|
577
|
+
type DeferredDispatch = {
|
|
578
|
+
/** Correlation id. The result is delivered under it — `POST
|
|
579
|
+
* {basePath}/executions/:executionId/result` — and applied idempotently. */
|
|
580
|
+
executionId: string;
|
|
581
|
+
sessionId: string;
|
|
582
|
+
tool: string;
|
|
583
|
+
input: unknown; /** The session's scratch filesystem at dispatch time, by value. */
|
|
584
|
+
vfsSeed?: Record<string, string>;
|
|
585
|
+
limits?: {
|
|
586
|
+
timeoutMs?: number;
|
|
587
|
+
memoryLimitBytes?: number;
|
|
588
|
+
}; /** Epoch ms the host's execution watchdog fires at, when a timeout was configured. */
|
|
589
|
+
expiresAt?: number;
|
|
590
|
+
};
|
|
591
|
+
type DeferredExecutorOptions = {
|
|
592
|
+
/**
|
|
593
|
+
* Hand the call to whatever actually runs it — enqueue it, POST it to a worker,
|
|
594
|
+
* page a human. Called synchronously during dispatch; throwing fails the
|
|
595
|
+
* execution (the failure reaches the agent as ordinary tool output).
|
|
596
|
+
*/
|
|
597
|
+
onDispatch: (call: DeferredDispatch) => void | Promise<void>;
|
|
598
|
+
/** How long the result may take before the host's watchdog fails the execution.
|
|
599
|
+
* Unset = no deadline; the execution then relies on the job's parked cap. */
|
|
600
|
+
timeoutMs?: number; /** Reported on `execution_dispatched`. Default 'remote'. */
|
|
601
|
+
backend?: ToolExecutionBackend;
|
|
602
|
+
};
|
|
603
|
+
/**
|
|
604
|
+
* The executor for work that outlives the session's process residency: dispatch
|
|
605
|
+
* hands the call off and returns `pending` **without holding a promise**, because
|
|
606
|
+
* the runner it would resolve into is about to be torn down. The result can only
|
|
607
|
+
* come back through the host — the execution-result route → `settleExecution` on a
|
|
608
|
+
* rehydrated runner — which is exactly what makes a park durable rather than a
|
|
609
|
+
* long in-memory await.
|
|
610
|
+
*
|
|
611
|
+
* Contrast {@link BrowserBridgeExecutor}, which is also `pending` but keeps its
|
|
612
|
+
* answer in memory for the ~60s the tab has to reply.
|
|
613
|
+
*/
|
|
614
|
+
declare class DeferredExecutor implements ToolExecutor {
|
|
615
|
+
#private;
|
|
616
|
+
readonly backend: ToolExecutionBackend;
|
|
617
|
+
readonly timeoutMs: number | undefined;
|
|
618
|
+
constructor(options: DeferredExecutorOptions);
|
|
619
|
+
/** Every call this executor takes is deferred — route only the tools that
|
|
620
|
+
* belong on the remote side to it. */
|
|
621
|
+
describe(): ToolExecutionProfile;
|
|
622
|
+
dispatch(call: ToolExecutionCall): Promise<ToolExecutionDispatch>;
|
|
623
|
+
}
|
|
624
|
+
//#endregion
|
|
625
|
+
//#region src/web-fetch.d.ts
|
|
626
|
+
/**
|
|
627
|
+
* `web_fetch` backend, close to Claude Code's original WebFetch: fetch a URL,
|
|
628
|
+
* convert HTML to markdown, and (optionally) digest it with a model against the
|
|
629
|
+
* caller's prompt. Server-side only — this runs with server egress, which is
|
|
630
|
+
* exactly why it is an authoritative capability the operator grants explicitly.
|
|
631
|
+
*/
|
|
632
|
+
type WebFetchResult = {
|
|
633
|
+
/** The URL that was fetched (after same-host redirects). */url: string; /** Model digest of the page against the prompt (when a digest fn is wired). */
|
|
634
|
+
digest?: string; /** Page content as markdown (when no digest fn is wired, or digesting failed). */
|
|
635
|
+
markdown?: string; /** True when the markdown was cut at the size cap. */
|
|
636
|
+
truncated?: boolean;
|
|
637
|
+
/** Redirect-to-a-different-host notice: the redirect is surfaced, not followed
|
|
638
|
+
* (the agent can decide to fetch `redirectUrl` itself). */
|
|
639
|
+
notice?: string;
|
|
640
|
+
redirectUrl?: string;
|
|
641
|
+
error?: string;
|
|
642
|
+
};
|
|
643
|
+
type WebFetchFn = (url: string, prompt: string) => Promise<WebFetchResult>;
|
|
644
|
+
/** Runs the digest pass over the fetched markdown. Wire the session's own model
|
|
645
|
+
* here (see createEngineSession) so its tokens land in the turn's usage. */
|
|
646
|
+
type WebFetchDigest = (markdown: string, prompt: string) => Promise<string>;
|
|
647
|
+
type WebFetchOptions = {
|
|
648
|
+
fetchImpl?: typeof fetch; /** Raw-body cap, enforced while streaming (before any conversion). Default 1 MiB. */
|
|
649
|
+
maxContentBytes?: number; /** Markdown cap handed to the model. Default 50 KB. */
|
|
650
|
+
maxMarkdownBytes?: number;
|
|
651
|
+
/** Fetched-page cache TTL (keyed by URL; the digest is per-prompt and never
|
|
652
|
+
* cached). Default 15 minutes. */
|
|
653
|
+
cacheTtlMs?: number;
|
|
654
|
+
/** Optional hostname allowlist on top of the SSRF guard (exact or `*.example.com`).
|
|
655
|
+
* Unset = any public host. */
|
|
656
|
+
allowedHosts?: string[]; /** Per-request timeout. Default 30000. */
|
|
657
|
+
timeoutMs?: number;
|
|
658
|
+
digest?: WebFetchDigest;
|
|
659
|
+
};
|
|
660
|
+
declare function createWebFetch(options?: WebFetchOptions): WebFetchFn;
|
|
661
|
+
/** Private / loopback / link-local / unspecified, IPv4 and IPv6 (incl. v4-mapped). */
|
|
662
|
+
declare function isPrivateAddress(address: string): boolean;
|
|
663
|
+
/**
|
|
664
|
+
* Dependency-free HTML → markdown, tuned for "give the model readable text":
|
|
665
|
+
* drops non-content subtrees, keeps headings/lists/links/emphasis/code, strips
|
|
666
|
+
* everything else. Not a spec-grade converter on purpose — a small predictable
|
|
667
|
+
* transform beats dragging a DOM into core.
|
|
668
|
+
*/
|
|
669
|
+
declare function htmlToMarkdown(html: string): string;
|
|
670
|
+
//#endregion
|
|
671
|
+
//#region src/tools.d.ts
|
|
672
|
+
/**
|
|
673
|
+
* How much authority a tool carries, which decides where it may run.
|
|
674
|
+
*
|
|
675
|
+
* - `sandboxed` — no ambient authority; safe to execute anywhere, including an
|
|
676
|
+
* untrusted browser tab. Its results are untrusted input.
|
|
677
|
+
* - `authoritative` — runs server-side with server credentials (MCP, secret-bearing
|
|
678
|
+
* APIs). **Never bridged to a client**: bridging it would hand a browser the
|
|
679
|
+
* ability to forge authoritative results.
|
|
680
|
+
*/
|
|
681
|
+
type ToolTrust = 'sandboxed' | 'authoritative';
|
|
682
|
+
type ToolDefinition = {
|
|
683
|
+
name: string;
|
|
684
|
+
trust: ToolTrust;
|
|
685
|
+
/** The AI SDK tool. Sandboxed tools are declared WITHOUT `execute` so the loop
|
|
686
|
+
* hands them to the ToolExecutor seam rather than running them inline. */
|
|
687
|
+
tool: Tool;
|
|
688
|
+
};
|
|
689
|
+
type ToolContextOptions = {
|
|
690
|
+
/** Executor for sandboxed tools. Selected per call by the host (browser bridge
|
|
691
|
+
* when a client is attached, server QuickJS otherwise). */
|
|
692
|
+
executor: ToolExecutor;
|
|
693
|
+
sessionId: string; /** Scratch filesystem shared by this session's sandboxed tools. */
|
|
694
|
+
vfs?: SandboxVfs; /** Search backend. Omitted = `web_search` is not granted at all. */
|
|
695
|
+
search?: (query: string, limit: number) => Promise<Array<{
|
|
696
|
+
title: string;
|
|
697
|
+
url: string;
|
|
698
|
+
snippet?: string;
|
|
699
|
+
}>>; /** Document fetcher for `download`. Omitted = the tool is not granted. */
|
|
700
|
+
download?: (url: string) => Promise<{
|
|
701
|
+
contentType?: string;
|
|
702
|
+
text: string;
|
|
703
|
+
}>;
|
|
704
|
+
/** Page digester for `web_fetch` (see {@link createWebFetch}). Omitted = the
|
|
705
|
+
* tool is not granted. */
|
|
706
|
+
webFetch?: WebFetchFn;
|
|
707
|
+
/** Notified when the agent hands over a VFS file via `deliver_file`, so the
|
|
708
|
+
* host can emit the `file_delivered` session event. The tool is only granted
|
|
709
|
+
* when this is set — a delivery nobody hears is not a delivery. */
|
|
710
|
+
onFileDelivered?: (file: {
|
|
711
|
+
path: string;
|
|
712
|
+
bytes: number;
|
|
713
|
+
description?: string;
|
|
714
|
+
}) => void; /** Per-call sandbox limits. */
|
|
715
|
+
limits?: {
|
|
716
|
+
timeoutMs?: number;
|
|
717
|
+
memoryLimitBytes?: number;
|
|
718
|
+
};
|
|
719
|
+
/** Notified when a sandboxed execution is dispatched and when it settles, so
|
|
720
|
+
* the host can emit execution_* events. */
|
|
721
|
+
onDispatch?: (executionId: string, toolName: string) => void;
|
|
722
|
+
onSettle?: (executionId: string, result: ToolExecutionResult) => void;
|
|
723
|
+
};
|
|
724
|
+
/** Everything a session's tools need, plus the tool set to hand the runner. */
|
|
725
|
+
type ToolContext = {
|
|
726
|
+
vfs: SandboxVfs;
|
|
727
|
+
tools: ToolSet;
|
|
728
|
+
definitions: ToolDefinition[]; /** Names the loop must not execute inline (they go through the executor). */
|
|
729
|
+
sandboxedToolNames: string[];
|
|
730
|
+
};
|
|
731
|
+
/**
|
|
732
|
+
* Build the capability-scoped tool set for a session.
|
|
733
|
+
*
|
|
734
|
+
* The agent's authority is exactly what is granted here — there are no built-in
|
|
735
|
+
* filesystem or shell tools, and nothing reaches the host filesystem: `fs_*`
|
|
736
|
+
* operate on an in-memory scratch VFS. Tools whose backend is not supplied are
|
|
737
|
+
* simply absent rather than present-and-failing, so a model cannot be tempted
|
|
738
|
+
* by a capability the operator did not grant.
|
|
739
|
+
*/
|
|
740
|
+
declare function createToolContext(options: ToolContextOptions): ToolContext;
|
|
741
|
+
/** Add host-side MCP tools to a context. They are ALWAYS authoritative: they run
|
|
742
|
+
* server-side with server credentials, and must never be handed to a browser. */
|
|
743
|
+
declare function withMcpTools(context: ToolContext, mcpTools: ToolSet): ToolContext;
|
|
744
|
+
//#endregion
|
|
745
|
+
//#region src/engine.d.ts
|
|
746
|
+
type EngineSessionOptions = {
|
|
747
|
+
/** Resolved session config (profile defaults already applied). */config: AiSdkRunnerConfig; /** The profile that selected this engine, when there was one. */
|
|
748
|
+
profile?: ProfileInfo;
|
|
749
|
+
/**
|
|
750
|
+
* Resolve the profile's provider config into a model instance. The host owns
|
|
751
|
+
* this so core never imports a provider SDK and never reads credentials —
|
|
752
|
+
* they come from the operator's environment, exactly like the Claude chain.
|
|
753
|
+
*/
|
|
754
|
+
resolveModel: (profile: ProfileInfo | undefined, config: AiSdkRunnerConfig) => LanguageModel;
|
|
755
|
+
/**
|
|
756
|
+
* Executor for sandboxed tools. Return the browser bridge when a client is
|
|
757
|
+
* attached and the server sandbox otherwise; the seam makes them
|
|
758
|
+
* interchangeable, so this is the only place the choice is made.
|
|
759
|
+
*/
|
|
760
|
+
selectExecutor: () => ToolExecutor; /** Which backend `selectExecutor` returned, for the execution_* events. */
|
|
761
|
+
backend?: 'server' | 'browser' | 'managed' | 'remote'; /** Backends for the granted capabilities. Omitted ones are simply not granted. */
|
|
762
|
+
capabilities?: {
|
|
763
|
+
search?: ToolContextOptions['search'];
|
|
764
|
+
download?: ToolContextOptions['download'];
|
|
765
|
+
/**
|
|
766
|
+
* Grants `web_fetch`. Pass options (or `{}`) to use the built-in
|
|
767
|
+
* {@link createWebFetch} backend — its digest pass then runs on the
|
|
768
|
+
* session's own model, billed into the turn's usage. Pass `digest: false`
|
|
769
|
+
* to skip the digest (the tool returns page markdown), a custom digest fn
|
|
770
|
+
* to bring your own model, or a complete {@link WebFetchFn} to replace the
|
|
771
|
+
* backend outright.
|
|
772
|
+
*/
|
|
773
|
+
webFetch?: WebFetchFn | (Omit<WebFetchOptions, 'digest'> & {
|
|
774
|
+
digest?: WebFetchOptions['digest'] | false;
|
|
775
|
+
});
|
|
776
|
+
/** Grants `deliver_file`: the agent can hand VFS files over to the user
|
|
777
|
+
* (emitting `file_delivered`, downloadable via the server's file routes).
|
|
778
|
+
* Default true — set false to withhold it. */
|
|
779
|
+
deliverFiles?: boolean;
|
|
780
|
+
};
|
|
781
|
+
/** Authoritative tools that run server-side with server credentials (MCP).
|
|
782
|
+
* Never bridged to a client. Namespaced `<server>__<tool>` by
|
|
783
|
+
* {@link connectMcpTools}, which is how a profile grants servers by name. */
|
|
784
|
+
mcpTools?: ToolSet;
|
|
785
|
+
/** Extra instructions prepended to the session's system prompt. Overridden by
|
|
786
|
+
* the profile's `session.instructions` when it declares one. */
|
|
787
|
+
instructions?: string;
|
|
788
|
+
executionLimits?: {
|
|
789
|
+
timeoutMs?: number;
|
|
790
|
+
memoryLimitBytes?: number;
|
|
791
|
+
};
|
|
792
|
+
};
|
|
793
|
+
/**
|
|
794
|
+
* Assemble a model-agnostic session: provider model, capability-scoped tools,
|
|
795
|
+
* a scratch VFS, and the executor that runs the sandboxed ones.
|
|
796
|
+
*
|
|
797
|
+
* This is the piece an operator wires into the server's `createEngineRunner`.
|
|
798
|
+
*
|
|
799
|
+
* The host wires the *backends*; the profile and the session request decide which
|
|
800
|
+
* of them are actually granted (`profile.session`, `config.capabilities`). A
|
|
801
|
+
* backend that isn't granted is simply not built into the tool set, so withholding
|
|
802
|
+
* a capability costs the host no branching. No declaration anywhere = everything
|
|
803
|
+
* the host wired, which is what a host that ignores profiles gets.
|
|
804
|
+
*/
|
|
805
|
+
declare function createEngineSession(options: EngineSessionOptions): AiSdkRunner;
|
|
806
|
+
type McpConnection = {
|
|
807
|
+
tools: ToolSet;
|
|
808
|
+
close: () => Promise<void>;
|
|
809
|
+
};
|
|
810
|
+
/**
|
|
811
|
+
* Connect to MCP servers and return their tools, ready for {@link withMcpTools}.
|
|
812
|
+
*
|
|
813
|
+
* Server-side only, with server credentials: these tools are authoritative and
|
|
814
|
+
* must never be bridged to a browser. `@ai-sdk/mcp` is imported lazily and is an
|
|
815
|
+
* optional dependency — an operator who wires no MCP servers never needs it.
|
|
816
|
+
*/
|
|
817
|
+
declare function connectMcpTools(servers: Record<string, McpServerConfigWire>,
|
|
818
|
+
/** `onError` may fire more than once for a single server: transport-level
|
|
819
|
+
* failures surface through the client's own uncaught-error channel as well as
|
|
820
|
+
* the connect failure. Treat it as a report, not a count. */
|
|
821
|
+
|
|
822
|
+
options?: {
|
|
823
|
+
onError?: (name: string, error: unknown) => void;
|
|
824
|
+
}): Promise<McpConnection>;
|
|
825
|
+
//#endregion
|
|
826
|
+
//#region src/input-queue.d.ts
|
|
827
|
+
/**
|
|
828
|
+
* Push-based single-consumer AsyncIterable bridging imperative sendMessage() calls
|
|
829
|
+
* into the streaming `prompt` the Agent SDK consumes.
|
|
830
|
+
*/
|
|
831
|
+
declare class InputQueue implements AsyncIterable<SDKUserMessage> {
|
|
832
|
+
#private;
|
|
833
|
+
push(message: SDKUserMessage): void;
|
|
834
|
+
end(): void;
|
|
835
|
+
[Symbol.asyncIterator](): AsyncIterator<SDKUserMessage>;
|
|
836
|
+
}
|
|
837
|
+
//#endregion
|
|
838
|
+
//#region src/normalize.d.ts
|
|
839
|
+
declare function toApiMessage(message: unknown): ApiMessage;
|
|
840
|
+
/**
|
|
841
|
+
* Map one SDKMessage to a wire-protocol event body, or null for messages the runner
|
|
842
|
+
* consumes itself (system_init and session-state changes carry runner state and are
|
|
843
|
+
* emitted by the runner with extra context).
|
|
844
|
+
*/
|
|
845
|
+
declare function normalizeSdkMessage(msg: SDKMessage): SessionEventBody | null;
|
|
846
|
+
//#endregion
|
|
847
|
+
export { AiSdkRunner, type AiSdkRunnerConfig, type AiSdkSessionState, type BridgeAnswer, BrowserBridgeExecutor, type BrowserBridgeExecutorOptions, type ClaudeAuthProbe, type ClaudeAuthStatus, type DeferredDispatch, DeferredExecutor, type DeferredExecutorOptions, type EngineSessionOptions, type HistoryFn, type HostFetch, InputQueue, type McpConnection, type ParkedExecution, type PendingEntry, type PendingKind, type PendingOutcome, PendingRequestRegistry, type PendingToolCall, type PermissionDecision, type QueryFn, QuickJsExecutor, type QuickJsExecutorOptions, type RegisterOptions, type Runner, type RunnerSnapshot, type SessionEventListener, SessionRunner, type SessionRunnerConfig, type SettledBy, type ToolCallOutput, type ToolContext, type ToolContextOptions, type ToolDefinition, type ToolExecutionCall, type ToolExecutionDispatch, type ToolExecutionResult, type ToolExecutor, type ToolTrust, type WebFetchDigest, type WebFetchFn, type WebFetchOptions, type WebFetchResult, checkClaudeAuth, connectMcpTools, createEngineSession, createToolContext, createWebFetch, htmlToMarkdown, isHostAllowed, isPrivateAddress, normalizeSdkMessage, resolveBundledClaudeExecutable, toApiMessage, toExecutionResult, withMcpTools };
|
|
848
|
+
//# sourceMappingURL=index.d.mts.map
|