@ai-sdk/code-mode 1.0.20 → 1.0.22
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/README.md +1 -1
- package/dist/continuation-capability.js +5 -0
- package/dist/continuation-capability.js.map +1 -1
- package/dist/errors.js +1 -136
- package/dist/errors.js.map +1 -1
- package/dist/host-interrupt.js +2 -14
- package/dist/host-interrupt.js.map +1 -1
- package/dist/index.d.ts +14 -44
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interrupt-continuation.js +3 -8
- package/dist/interrupt-continuation.js.map +1 -1
- package/dist/run-code-mode.js +543 -2
- package/dist/run-code-mode.js.map +1 -1
- package/dist/tool-invocation.js +9 -23
- package/dist/tool-invocation.js.map +1 -1
- package/dist/utils/serialization.js +4 -0
- package/dist/utils/serialization.js.map +1 -1
- package/package.json +8 -7
- package/src/continuation-capability.ts +18 -0
- package/src/errors.ts +0 -190
- package/src/host-interrupt.ts +2 -31
- package/src/index.ts +1 -1
- package/src/interrupt-continuation.ts +13 -15
- package/src/run-code-mode.ts +706 -3
- package/src/tool-invocation.ts +9 -23
- package/src/types.ts +38 -68
- package/src/utils/serialization.ts +4 -0
- package/dist/runtime/guest-sources.js +0 -350
- package/dist/runtime/guest-sources.js.map +0 -1
- package/dist/runtime/manager.js +0 -794
- package/dist/runtime/manager.js.map +0 -1
- package/dist/runtime/max-workers.js +0 -40
- package/dist/runtime/max-workers.js.map +0 -1
- package/dist/runtime/protocol.js +0 -1
- package/dist/runtime/protocol.js.map +0 -1
- package/dist/runtime/worker-source.d.ts +0 -11
- package/dist/runtime/worker-source.js +0 -2
- package/dist/utils/options.js +0 -76
- package/dist/utils/options.js.map +0 -1
- package/dist/utils/source-cache.js +0 -77
- package/dist/utils/source-cache.js.map +0 -1
- package/src/runtime/guest-sources.ts +0 -351
- package/src/runtime/manager.ts +0 -963
- package/src/runtime/max-workers.ts +0 -65
- package/src/runtime/protocol.ts +0 -74
- package/src/runtime/worker-source.ts +0 -10
- package/src/runtime/worker.ts +0 -607
- package/src/utils/options.ts +0 -83
- package/src/utils/source-cache.ts +0 -91
package/dist/runtime/manager.js
DELETED
|
@@ -1,794 +0,0 @@
|
|
|
1
|
-
import { AsyncResource } from "node:async_hooks";
|
|
2
|
-
import { Buffer } from "node:buffer";
|
|
3
|
-
import { randomBytes } from "node:crypto";
|
|
4
|
-
import { Worker } from "node:worker_threads";
|
|
5
|
-
import {
|
|
6
|
-
isCodeModeApprovalInterruptPayload,
|
|
7
|
-
normalizeApprovalResolution
|
|
8
|
-
} from "../approval.js";
|
|
9
|
-
import {
|
|
10
|
-
resolveCodeModeContinuationSecurity,
|
|
11
|
-
signCodeModeContinuation,
|
|
12
|
-
verifyCodeModeContinuation
|
|
13
|
-
} from "../continuation-capability.js";
|
|
14
|
-
import {
|
|
15
|
-
CodeModeAbortedError,
|
|
16
|
-
CodeModeBridgeLimitError,
|
|
17
|
-
CodeModeConcurrencyError,
|
|
18
|
-
CodeModeDetachedBridgeRequestError,
|
|
19
|
-
CodeModeProtocolError,
|
|
20
|
-
CodeModeTimeoutError,
|
|
21
|
-
CodeModeToolApprovalDeniedError,
|
|
22
|
-
deserializeError,
|
|
23
|
-
serializeBridgeErrorForGuest
|
|
24
|
-
} from "../errors.js";
|
|
25
|
-
import { invokeHostTool } from "../tool-invocation.js";
|
|
26
|
-
import { normalizeOptions } from "../utils/options.js";
|
|
27
|
-
import { assertSourceSize, transformSource } from "../utils/source-cache.js";
|
|
28
|
-
import { getMaxWorkers } from "./max-workers.js";
|
|
29
|
-
import { INLINE_CODE_MODE_WORKER_SOURCE } from "./worker-source.js";
|
|
30
|
-
let invocationCounter = 0;
|
|
31
|
-
let activeInvocations = 0;
|
|
32
|
-
let inlineWorkerUrl;
|
|
33
|
-
const idleWorkers = [];
|
|
34
|
-
async function runManagedCodeMode(input) {
|
|
35
|
-
const normalizedOptions = normalizeOptions(input.options);
|
|
36
|
-
const continuationSecurity = resolveCodeModeContinuationSecurity(
|
|
37
|
-
input.options?.continuationSecurity
|
|
38
|
-
);
|
|
39
|
-
const maxWorkers = getMaxWorkers({
|
|
40
|
-
memoryLimitBytes: normalizedOptions.memoryLimitBytes,
|
|
41
|
-
activeWorkers: activeInvocations
|
|
42
|
-
});
|
|
43
|
-
if (activeInvocations >= maxWorkers) {
|
|
44
|
-
throw new CodeModeConcurrencyError(maxWorkers);
|
|
45
|
-
}
|
|
46
|
-
activeInvocations++;
|
|
47
|
-
try {
|
|
48
|
-
if (input.toolExecutionOptions?.abortSignal?.aborted) {
|
|
49
|
-
throw new CodeModeAbortedError();
|
|
50
|
-
}
|
|
51
|
-
assertSourceSize(input.js, normalizedOptions.maxSourceBytes);
|
|
52
|
-
const js = transformSource(input.js);
|
|
53
|
-
assertContinuationInput({
|
|
54
|
-
js,
|
|
55
|
-
continuation: input.continuation,
|
|
56
|
-
interruptResolution: input.interruptResolution,
|
|
57
|
-
continuationSecurity
|
|
58
|
-
});
|
|
59
|
-
const run = startWorkerRun({
|
|
60
|
-
...input,
|
|
61
|
-
js,
|
|
62
|
-
normalizedOptions,
|
|
63
|
-
maxWorkers,
|
|
64
|
-
continuationSecurity
|
|
65
|
-
});
|
|
66
|
-
return await run.result;
|
|
67
|
-
} finally {
|
|
68
|
-
releaseInvocationSlot(normalizedOptions.memoryLimitBytes);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
function startWorkerRun({
|
|
72
|
-
js,
|
|
73
|
-
tools,
|
|
74
|
-
toolExecutionOptions,
|
|
75
|
-
options,
|
|
76
|
-
continuation,
|
|
77
|
-
interruptResolution,
|
|
78
|
-
normalizedOptions,
|
|
79
|
-
maxWorkers,
|
|
80
|
-
continuationSecurity
|
|
81
|
-
}) {
|
|
82
|
-
const invocationId = `code-mode-${++invocationCounter}`;
|
|
83
|
-
const pooledWorker = acquireWorker(maxWorkers);
|
|
84
|
-
const worker = pooledWorker.worker;
|
|
85
|
-
const invocationContext = new AsyncResource("ai-sdk-code-mode:invocation");
|
|
86
|
-
const bindInvocationContext = (handler) => {
|
|
87
|
-
return (...args) => {
|
|
88
|
-
invocationContext.runInAsyncScope(handler, void 0, ...args);
|
|
89
|
-
};
|
|
90
|
-
};
|
|
91
|
-
const outerAbortSignal = toolExecutionOptions?.abortSignal;
|
|
92
|
-
const invocationAbortController = new AbortController();
|
|
93
|
-
let nestedToolCounter = getMaxNestedToolCounter(continuation?.ledger ?? []);
|
|
94
|
-
const forwardedContext = toolExecutionOptions?.context ?? toolExecutionOptions?.experimental_context;
|
|
95
|
-
const forwardedExperimentalContext = toolExecutionOptions?.experimental_context ?? toolExecutionOptions?.context;
|
|
96
|
-
const baseExecutionOptions = {
|
|
97
|
-
toolCallId: toolExecutionOptions?.toolCallId ?? continuation?.outerToolCallId ?? invocationId,
|
|
98
|
-
messages: toolExecutionOptions?.messages ?? [],
|
|
99
|
-
abortSignal: invocationAbortController.signal,
|
|
100
|
-
...forwardedExperimentalContext !== void 0 ? { experimental_context: forwardedExperimentalContext } : {},
|
|
101
|
-
...forwardedContext !== void 0 ? { context: forwardedContext } : {}
|
|
102
|
-
};
|
|
103
|
-
let resultMessage;
|
|
104
|
-
let callerSettled = false;
|
|
105
|
-
let terminalReached = false;
|
|
106
|
-
let workerCleanedUp = false;
|
|
107
|
-
let totalBridgeRequests = 0;
|
|
108
|
-
let inFlightBridgeRequests = 0;
|
|
109
|
-
let interruptEntryIndex;
|
|
110
|
-
let interruptDrainCounter = 0;
|
|
111
|
-
let pendingInterruptDrainId;
|
|
112
|
-
let interruptResolutionConsumed = interruptResolution === void 0;
|
|
113
|
-
const seenWorkerRequestIds = /* @__PURE__ */ new Set();
|
|
114
|
-
const bridgeLedger = cloneLedger(continuation?.ledger ?? []);
|
|
115
|
-
const determinism = continuation?.determinism ?? createDeterminismState();
|
|
116
|
-
let resolveResult;
|
|
117
|
-
let rejectResult;
|
|
118
|
-
const result = new Promise((resolve, reject) => {
|
|
119
|
-
resolveResult = resolve;
|
|
120
|
-
rejectResult = reject;
|
|
121
|
-
});
|
|
122
|
-
const abortInvocation = (reason) => {
|
|
123
|
-
if (!invocationAbortController.signal.aborted) {
|
|
124
|
-
invocationAbortController.abort(reason);
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
const cleanupWorker = (reuseWorker) => {
|
|
128
|
-
if (workerCleanedUp) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
workerCleanedUp = true;
|
|
132
|
-
clearTimeout(timeoutHandle);
|
|
133
|
-
outerAbortSignal?.removeEventListener("abort", onAbort);
|
|
134
|
-
worker.off("message", onMessage);
|
|
135
|
-
worker.off("error", onError);
|
|
136
|
-
worker.off("exit", onExit);
|
|
137
|
-
invocationContext.emitDestroy();
|
|
138
|
-
if (reuseWorker) {
|
|
139
|
-
releaseWorker(pooledWorker);
|
|
140
|
-
} else {
|
|
141
|
-
destroyWorker(pooledWorker);
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
const settleCaller = (settle) => {
|
|
145
|
-
if (callerSettled) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
callerSettled = true;
|
|
149
|
-
try {
|
|
150
|
-
settle();
|
|
151
|
-
} catch (error) {
|
|
152
|
-
rejectResult(error);
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
const failTerminal = (error) => {
|
|
156
|
-
if (terminalReached) {
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
terminalReached = true;
|
|
160
|
-
abortInvocation(error);
|
|
161
|
-
cleanupWorker(false);
|
|
162
|
-
settleCaller(() => rejectResult(error));
|
|
163
|
-
};
|
|
164
|
-
const onAbort = bindInvocationContext(() => {
|
|
165
|
-
failTerminal(new CodeModeAbortedError());
|
|
166
|
-
});
|
|
167
|
-
const timeoutHandle = setTimeout(
|
|
168
|
-
bindInvocationContext(() => {
|
|
169
|
-
failTerminal(new CodeModeTimeoutError(normalizedOptions.timeoutMs));
|
|
170
|
-
}),
|
|
171
|
-
normalizedOptions.timeoutMs
|
|
172
|
-
);
|
|
173
|
-
const onMessage = bindInvocationContext((message) => {
|
|
174
|
-
if (message.invocationId !== invocationId) {
|
|
175
|
-
failTerminal(
|
|
176
|
-
new CodeModeProtocolError(
|
|
177
|
-
`Worker message invocationId mismatch: expected ${invocationId}, received ${message.invocationId}.`,
|
|
178
|
-
{
|
|
179
|
-
expectedInvocationId: invocationId,
|
|
180
|
-
receivedInvocationId: message.invocationId,
|
|
181
|
-
messageType: message.type
|
|
182
|
-
}
|
|
183
|
-
)
|
|
184
|
-
);
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
if (message.type === "result") {
|
|
188
|
-
if (resultMessage !== void 0) {
|
|
189
|
-
failTerminal(
|
|
190
|
-
new CodeModeProtocolError(
|
|
191
|
-
`Worker sent duplicate result for invocation ${invocationId}.`,
|
|
192
|
-
{ invocationId }
|
|
193
|
-
)
|
|
194
|
-
);
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
resultMessage = message;
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
if (message.type === "ready") {
|
|
201
|
-
handleReadyMessage(message);
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
if (message.type === "bridge-drained") {
|
|
205
|
-
if (pendingInterruptDrainId === void 0 || message.drainId !== pendingInterruptDrainId) {
|
|
206
|
-
failTerminal(
|
|
207
|
-
new CodeModeProtocolError(
|
|
208
|
-
`Worker sent unexpected bridge drain acknowledgement ${message.drainId}.`,
|
|
209
|
-
{
|
|
210
|
-
invocationId,
|
|
211
|
-
expectedDrainId: pendingInterruptDrainId,
|
|
212
|
-
receivedDrainId: message.drainId
|
|
213
|
-
}
|
|
214
|
-
)
|
|
215
|
-
);
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
pendingInterruptDrainId = void 0;
|
|
219
|
-
if (inFlightBridgeRequests === 0) {
|
|
220
|
-
settleInterrupt();
|
|
221
|
-
}
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
const bridgeIndex = markWorkerRequest(message);
|
|
225
|
-
if (bridgeIndex !== void 0) {
|
|
226
|
-
void handleToolRequest(message, bridgeIndex);
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
const onError = bindInvocationContext((error) => {
|
|
230
|
-
failTerminal(error);
|
|
231
|
-
});
|
|
232
|
-
const onExit = bindInvocationContext((code) => {
|
|
233
|
-
if (!terminalReached) {
|
|
234
|
-
failTerminal(
|
|
235
|
-
new Error(
|
|
236
|
-
`Code mode worker exited before completion with code ${code}.`
|
|
237
|
-
)
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
worker.on("message", onMessage);
|
|
242
|
-
worker.on("error", onError);
|
|
243
|
-
worker.on("exit", onExit);
|
|
244
|
-
outerAbortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
245
|
-
if (outerAbortSignal?.aborted) {
|
|
246
|
-
onAbort();
|
|
247
|
-
}
|
|
248
|
-
const runMessage = {
|
|
249
|
-
type: "run",
|
|
250
|
-
invocationId,
|
|
251
|
-
js,
|
|
252
|
-
determinism,
|
|
253
|
-
options: {
|
|
254
|
-
timeoutMs: normalizedOptions.timeoutMs,
|
|
255
|
-
memoryLimitBytes: normalizedOptions.memoryLimitBytes,
|
|
256
|
-
maxStackSizeBytes: normalizedOptions.maxStackSizeBytes,
|
|
257
|
-
maxResultBytes: normalizedOptions.maxResultBytes,
|
|
258
|
-
maxConsoleOutputBytes: normalizedOptions.maxConsoleOutputBytes
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
if (!terminalReached) {
|
|
262
|
-
try {
|
|
263
|
-
worker.postMessage(runMessage);
|
|
264
|
-
} catch (error) {
|
|
265
|
-
failTerminal(error);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
return { result };
|
|
269
|
-
function markWorkerRequest(message) {
|
|
270
|
-
if (terminalReached) {
|
|
271
|
-
return void 0;
|
|
272
|
-
}
|
|
273
|
-
if (seenWorkerRequestIds.has(message.requestId)) {
|
|
274
|
-
failTerminal(
|
|
275
|
-
new CodeModeProtocolError(
|
|
276
|
-
`Worker sent duplicate requestId ${message.requestId}.`,
|
|
277
|
-
{ invocationId, requestId: message.requestId }
|
|
278
|
-
)
|
|
279
|
-
);
|
|
280
|
-
return void 0;
|
|
281
|
-
}
|
|
282
|
-
seenWorkerRequestIds.add(message.requestId);
|
|
283
|
-
if (totalBridgeRequests >= normalizedOptions.maxBridgeRequests) {
|
|
284
|
-
failTerminal(
|
|
285
|
-
new CodeModeBridgeLimitError(
|
|
286
|
-
`Code mode exceeded the ${normalizedOptions.maxBridgeRequests} bridge request limit.`,
|
|
287
|
-
{
|
|
288
|
-
invocationId,
|
|
289
|
-
maxBridgeRequests: normalizedOptions.maxBridgeRequests
|
|
290
|
-
}
|
|
291
|
-
)
|
|
292
|
-
);
|
|
293
|
-
return void 0;
|
|
294
|
-
}
|
|
295
|
-
if (inFlightBridgeRequests >= normalizedOptions.maxInFlightBridgeRequests) {
|
|
296
|
-
failTerminal(
|
|
297
|
-
new CodeModeBridgeLimitError(
|
|
298
|
-
`Code mode exceeded the ${normalizedOptions.maxInFlightBridgeRequests} in-flight bridge request limit.`,
|
|
299
|
-
{
|
|
300
|
-
invocationId,
|
|
301
|
-
maxInFlightBridgeRequests: normalizedOptions.maxInFlightBridgeRequests
|
|
302
|
-
}
|
|
303
|
-
)
|
|
304
|
-
);
|
|
305
|
-
return void 0;
|
|
306
|
-
}
|
|
307
|
-
totalBridgeRequests++;
|
|
308
|
-
inFlightBridgeRequests++;
|
|
309
|
-
return totalBridgeRequests;
|
|
310
|
-
}
|
|
311
|
-
async function handleToolRequest(message, bridgeIndex) {
|
|
312
|
-
const replayEntry = bridgeLedger[bridgeIndex - 1];
|
|
313
|
-
try {
|
|
314
|
-
if (replayEntry !== void 0) {
|
|
315
|
-
assertReplayEntryMatches(replayEntry, message, bridgeIndex);
|
|
316
|
-
if (replayEntry.status === "fulfilled") {
|
|
317
|
-
postBridgeResponse({
|
|
318
|
-
type: "bridge-response",
|
|
319
|
-
invocationId,
|
|
320
|
-
requestId: message.requestId,
|
|
321
|
-
success: true,
|
|
322
|
-
dateNowMs: replayEntry.dateNowMs,
|
|
323
|
-
valueJson: replayEntry.valueJson
|
|
324
|
-
});
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
if (replayEntry.status === "rejected") {
|
|
328
|
-
postBridgeResponse({
|
|
329
|
-
type: "bridge-response",
|
|
330
|
-
invocationId,
|
|
331
|
-
requestId: message.requestId,
|
|
332
|
-
success: false,
|
|
333
|
-
dateNowMs: replayEntry.dateNowMs,
|
|
334
|
-
error: replayEntry.error
|
|
335
|
-
});
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
if (interruptResolution?.interruptId !== replayEntry.interruptId) {
|
|
339
|
-
requestInterrupt(bridgeIndex - 1);
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
interruptResolutionConsumed = true;
|
|
343
|
-
const isApproval = isCodeModeApprovalInterruptPayload(
|
|
344
|
-
replayEntry.interruptPayload
|
|
345
|
-
);
|
|
346
|
-
if (isApproval) {
|
|
347
|
-
const decision = normalizeApprovalResolution(
|
|
348
|
-
interruptResolution.resolution
|
|
349
|
-
);
|
|
350
|
-
if (!decision.approved) {
|
|
351
|
-
const error = new CodeModeToolApprovalDeniedError(
|
|
352
|
-
replayEntry.name,
|
|
353
|
-
fromJsonPayload(message.inputJson),
|
|
354
|
-
replayEntry.toolCallId,
|
|
355
|
-
decision.reason
|
|
356
|
-
);
|
|
357
|
-
const dateNowMs3 = Date.now();
|
|
358
|
-
const guestError = serializeBridgeErrorForGuest(error, "tool");
|
|
359
|
-
bridgeLedger[bridgeIndex - 1] = {
|
|
360
|
-
kind: "tool",
|
|
361
|
-
name: message.toolName,
|
|
362
|
-
inputJson: message.inputJson,
|
|
363
|
-
toolCallId: replayEntry.toolCallId,
|
|
364
|
-
status: "rejected",
|
|
365
|
-
dateNowMs: dateNowMs3,
|
|
366
|
-
error: guestError
|
|
367
|
-
};
|
|
368
|
-
postBridgeResponse({
|
|
369
|
-
type: "bridge-response",
|
|
370
|
-
invocationId,
|
|
371
|
-
requestId: message.requestId,
|
|
372
|
-
success: false,
|
|
373
|
-
dateNowMs: dateNowMs3,
|
|
374
|
-
error: guestError
|
|
375
|
-
});
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
const codeModeInterrupt = isApproval ? void 0 : {
|
|
380
|
-
interruptId: replayEntry.interruptId,
|
|
381
|
-
payload: replayEntry.interruptPayload,
|
|
382
|
-
resolution: interruptResolution.resolution
|
|
383
|
-
};
|
|
384
|
-
const outcome2 = await invokeHostTool({
|
|
385
|
-
toolName: message.toolName,
|
|
386
|
-
inputJson: message.inputJson,
|
|
387
|
-
tools,
|
|
388
|
-
baseExecutionOptions,
|
|
389
|
-
codeModeOptions: options ?? {},
|
|
390
|
-
maxToolInputBytes: normalizedOptions.maxToolInputBytes,
|
|
391
|
-
maxToolOutputBytes: normalizedOptions.maxToolOutputBytes,
|
|
392
|
-
toolCallId: replayEntry.toolCallId,
|
|
393
|
-
...codeModeInterrupt !== void 0 ? { codeModeInterrupt } : {},
|
|
394
|
-
skipApproval: true
|
|
395
|
-
});
|
|
396
|
-
if (outcome2.type === "interrupted") {
|
|
397
|
-
bridgeLedger[bridgeIndex - 1] = {
|
|
398
|
-
kind: "tool",
|
|
399
|
-
name: message.toolName,
|
|
400
|
-
inputJson: message.inputJson,
|
|
401
|
-
toolCallId: replayEntry.toolCallId,
|
|
402
|
-
interruptId: `${replayEntry.toolCallId}:interrupt`,
|
|
403
|
-
interruptPayload: outcome2.payload,
|
|
404
|
-
status: "interrupted"
|
|
405
|
-
};
|
|
406
|
-
requestInterrupt(bridgeIndex - 1);
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
const dateNowMs2 = Date.now();
|
|
410
|
-
bridgeLedger[bridgeIndex - 1] = {
|
|
411
|
-
kind: "tool",
|
|
412
|
-
name: message.toolName,
|
|
413
|
-
inputJson: message.inputJson,
|
|
414
|
-
toolCallId: replayEntry.toolCallId,
|
|
415
|
-
status: "fulfilled",
|
|
416
|
-
dateNowMs: dateNowMs2,
|
|
417
|
-
valueJson: outcome2.valueJson
|
|
418
|
-
};
|
|
419
|
-
postBridgeResponse({
|
|
420
|
-
type: "bridge-response",
|
|
421
|
-
invocationId,
|
|
422
|
-
requestId: message.requestId,
|
|
423
|
-
success: true,
|
|
424
|
-
dateNowMs: dateNowMs2,
|
|
425
|
-
valueJson: outcome2.valueJson
|
|
426
|
-
});
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
const toolCallId = `${baseExecutionOptions.toolCallId}:tool-${++nestedToolCounter}`;
|
|
430
|
-
const outcome = await invokeHostTool({
|
|
431
|
-
toolName: message.toolName,
|
|
432
|
-
inputJson: message.inputJson,
|
|
433
|
-
tools,
|
|
434
|
-
baseExecutionOptions,
|
|
435
|
-
codeModeOptions: options ?? {},
|
|
436
|
-
maxToolInputBytes: normalizedOptions.maxToolInputBytes,
|
|
437
|
-
maxToolOutputBytes: normalizedOptions.maxToolOutputBytes,
|
|
438
|
-
toolCallId
|
|
439
|
-
});
|
|
440
|
-
if (outcome.type === "interrupted") {
|
|
441
|
-
bridgeLedger[bridgeIndex - 1] = {
|
|
442
|
-
kind: "tool",
|
|
443
|
-
name: message.toolName,
|
|
444
|
-
inputJson: message.inputJson,
|
|
445
|
-
toolCallId,
|
|
446
|
-
interruptId: `${toolCallId}:interrupt`,
|
|
447
|
-
interruptPayload: outcome.payload,
|
|
448
|
-
status: "interrupted"
|
|
449
|
-
};
|
|
450
|
-
requestInterrupt(bridgeIndex - 1);
|
|
451
|
-
return;
|
|
452
|
-
}
|
|
453
|
-
const dateNowMs = Date.now();
|
|
454
|
-
bridgeLedger[bridgeIndex - 1] = {
|
|
455
|
-
kind: "tool",
|
|
456
|
-
name: message.toolName,
|
|
457
|
-
inputJson: message.inputJson,
|
|
458
|
-
toolCallId,
|
|
459
|
-
status: "fulfilled",
|
|
460
|
-
dateNowMs,
|
|
461
|
-
valueJson: outcome.valueJson
|
|
462
|
-
};
|
|
463
|
-
postBridgeResponse({
|
|
464
|
-
type: "bridge-response",
|
|
465
|
-
invocationId,
|
|
466
|
-
requestId: message.requestId,
|
|
467
|
-
success: true,
|
|
468
|
-
dateNowMs,
|
|
469
|
-
valueJson: outcome.valueJson
|
|
470
|
-
});
|
|
471
|
-
} catch (error) {
|
|
472
|
-
if (error instanceof CodeModeProtocolError) {
|
|
473
|
-
failTerminal(error);
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
const dateNowMs = Date.now();
|
|
477
|
-
const guestError = serializeBridgeErrorForGuest(error, "tool");
|
|
478
|
-
const toolCallId = replayEntry?.toolCallId ?? `${baseExecutionOptions.toolCallId}:tool-${nestedToolCounter}`;
|
|
479
|
-
if (replayEntry === void 0 || replayEntry.status === "interrupted" && interruptResolution?.interruptId === replayEntry.interruptId) {
|
|
480
|
-
bridgeLedger[bridgeIndex - 1] = {
|
|
481
|
-
kind: "tool",
|
|
482
|
-
name: message.toolName,
|
|
483
|
-
inputJson: message.inputJson,
|
|
484
|
-
toolCallId,
|
|
485
|
-
status: "rejected",
|
|
486
|
-
dateNowMs,
|
|
487
|
-
error: guestError
|
|
488
|
-
};
|
|
489
|
-
}
|
|
490
|
-
postBridgeResponse({
|
|
491
|
-
type: "bridge-response",
|
|
492
|
-
invocationId,
|
|
493
|
-
requestId: message.requestId,
|
|
494
|
-
success: false,
|
|
495
|
-
dateNowMs,
|
|
496
|
-
error: guestError
|
|
497
|
-
});
|
|
498
|
-
} finally {
|
|
499
|
-
inFlightBridgeRequests--;
|
|
500
|
-
settleInterruptIfReady();
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
function requestInterrupt(entryIndex) {
|
|
504
|
-
interruptEntryIndex ??= entryIndex;
|
|
505
|
-
}
|
|
506
|
-
function settleInterruptIfReady() {
|
|
507
|
-
if (interruptEntryIndex === void 0 || terminalReached || inFlightBridgeRequests > 0 || pendingInterruptDrainId !== void 0 || !interruptResolutionConsumed) {
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
pendingInterruptDrainId = `${invocationId}:drain-${++interruptDrainCounter}`;
|
|
511
|
-
postBridgeResponse({
|
|
512
|
-
type: "bridge-drain",
|
|
513
|
-
invocationId,
|
|
514
|
-
drainId: pendingInterruptDrainId
|
|
515
|
-
});
|
|
516
|
-
}
|
|
517
|
-
function settleInterrupt() {
|
|
518
|
-
if (interruptEntryIndex === void 0 || terminalReached || inFlightBridgeRequests > 0 || !interruptResolutionConsumed) {
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
const entry = bridgeLedger[interruptEntryIndex];
|
|
522
|
-
if (entry === void 0 || entry.status !== "interrupted") {
|
|
523
|
-
failTerminal(
|
|
524
|
-
new CodeModeProtocolError(
|
|
525
|
-
"Code mode interruption references a missing or resolved ledger entry.",
|
|
526
|
-
{ invocationId, interruptEntryIndex }
|
|
527
|
-
)
|
|
528
|
-
);
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
const continuationState = signCodeModeContinuation(
|
|
532
|
-
{
|
|
533
|
-
version: 1,
|
|
534
|
-
js,
|
|
535
|
-
outerToolCallId: baseExecutionOptions.toolCallId,
|
|
536
|
-
determinism: { ...determinism },
|
|
537
|
-
ledger: cloneLedger(bridgeLedger)
|
|
538
|
-
},
|
|
539
|
-
continuationSecurity
|
|
540
|
-
);
|
|
541
|
-
const interrupt = {
|
|
542
|
-
type: "code-mode-interrupt",
|
|
543
|
-
interruptId: entry.interruptId,
|
|
544
|
-
toolName: entry.name,
|
|
545
|
-
toolCallId: entry.toolCallId,
|
|
546
|
-
outerToolCallId: continuationState.outerToolCallId,
|
|
547
|
-
input: fromJsonPayload(entry.inputJson),
|
|
548
|
-
payload: structuredClone(entry.interruptPayload),
|
|
549
|
-
continuation: continuationState
|
|
550
|
-
};
|
|
551
|
-
terminalReached = true;
|
|
552
|
-
abortInvocation(interrupt);
|
|
553
|
-
cleanupWorker(false);
|
|
554
|
-
settleCaller(() => resolveResult(interrupt));
|
|
555
|
-
}
|
|
556
|
-
function postBridgeResponse(message) {
|
|
557
|
-
if (terminalReached) {
|
|
558
|
-
return;
|
|
559
|
-
}
|
|
560
|
-
try {
|
|
561
|
-
worker.postMessage(message);
|
|
562
|
-
} catch (error) {
|
|
563
|
-
failTerminal(error);
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
function handleReadyMessage(message) {
|
|
567
|
-
if (terminalReached) {
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
if (interruptEntryIndex !== void 0) {
|
|
571
|
-
settleInterruptIfReady();
|
|
572
|
-
return;
|
|
573
|
-
}
|
|
574
|
-
if (resultMessage === void 0) {
|
|
575
|
-
failTerminal(
|
|
576
|
-
new CodeModeProtocolError(
|
|
577
|
-
`Code mode worker became ready without a result for ${message.invocationId}.`,
|
|
578
|
-
{ invocationId: message.invocationId }
|
|
579
|
-
)
|
|
580
|
-
);
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
if (!interruptResolutionConsumed) {
|
|
584
|
-
failTerminal(
|
|
585
|
-
new CodeModeProtocolError(
|
|
586
|
-
"Code mode continuation completed without consuming its interrupt resolution.",
|
|
587
|
-
{ interruptId: interruptResolution?.interruptId }
|
|
588
|
-
)
|
|
589
|
-
);
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
if (continuation !== void 0 && totalBridgeRequests < bridgeLedger.length) {
|
|
593
|
-
failTerminal(
|
|
594
|
-
new CodeModeProtocolError(
|
|
595
|
-
"Code mode continuation returned before replaying the full bridge ledger.",
|
|
596
|
-
{
|
|
597
|
-
replayedBridgeRequests: totalBridgeRequests,
|
|
598
|
-
ledgerEntries: bridgeLedger.length
|
|
599
|
-
}
|
|
600
|
-
)
|
|
601
|
-
);
|
|
602
|
-
return;
|
|
603
|
-
}
|
|
604
|
-
terminalReached = true;
|
|
605
|
-
const finalResultMessage = resultMessage;
|
|
606
|
-
if (inFlightBridgeRequests > 0) {
|
|
607
|
-
const error = finalResultMessage.success ? new CodeModeDetachedBridgeRequestError(
|
|
608
|
-
`Code mode returned while ${inFlightBridgeRequests} bridge request(s) were still in flight.`,
|
|
609
|
-
{
|
|
610
|
-
invocationId,
|
|
611
|
-
inFlightBridgeRequests,
|
|
612
|
-
totalBridgeRequests
|
|
613
|
-
}
|
|
614
|
-
) : deserializeResultError(finalResultMessage);
|
|
615
|
-
abortInvocation(error);
|
|
616
|
-
cleanupWorker(false);
|
|
617
|
-
settleCaller(() => rejectResult(error));
|
|
618
|
-
return;
|
|
619
|
-
}
|
|
620
|
-
cleanupWorker(true);
|
|
621
|
-
settleCaller(
|
|
622
|
-
() => settleWithResultMessage(finalResultMessage, resolveResult, rejectResult)
|
|
623
|
-
);
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
function assertContinuationInput({
|
|
627
|
-
js,
|
|
628
|
-
continuation,
|
|
629
|
-
interruptResolution,
|
|
630
|
-
continuationSecurity
|
|
631
|
-
}) {
|
|
632
|
-
if (continuation === void 0) {
|
|
633
|
-
if (interruptResolution !== void 0) {
|
|
634
|
-
throw new CodeModeProtocolError(
|
|
635
|
-
"A code-mode interrupt resolution was provided without continuation state."
|
|
636
|
-
);
|
|
637
|
-
}
|
|
638
|
-
return;
|
|
639
|
-
}
|
|
640
|
-
verifyCodeModeContinuation(continuation, continuationSecurity);
|
|
641
|
-
if (continuation.version !== 1) {
|
|
642
|
-
throw new CodeModeProtocolError(
|
|
643
|
-
"Unsupported code-mode continuation version."
|
|
644
|
-
);
|
|
645
|
-
}
|
|
646
|
-
if (continuation.js !== js) {
|
|
647
|
-
throw new CodeModeProtocolError(
|
|
648
|
-
"Code mode continuation source does not match the resumed source."
|
|
649
|
-
);
|
|
650
|
-
}
|
|
651
|
-
if (!Number.isInteger(continuation.determinism.dateNowMs) || !/^[0-9a-f]{32}$/i.test(continuation.determinism.randomSeed)) {
|
|
652
|
-
throw new CodeModeProtocolError(
|
|
653
|
-
"Code mode continuation determinism state is malformed."
|
|
654
|
-
);
|
|
655
|
-
}
|
|
656
|
-
for (const entry of continuation.ledger) {
|
|
657
|
-
if (entry.kind !== "tool" || typeof entry.name !== "string" || typeof entry.inputJson !== "string" || typeof entry.toolCallId !== "string") {
|
|
658
|
-
throw new CodeModeProtocolError(
|
|
659
|
-
"Code mode continuation ledger is malformed."
|
|
660
|
-
);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
if (interruptResolution !== void 0) {
|
|
664
|
-
const matches = continuation.ledger.filter(
|
|
665
|
-
(entry) => entry.status === "interrupted" && entry.interruptId === interruptResolution.interruptId
|
|
666
|
-
);
|
|
667
|
-
if (matches.length !== 1) {
|
|
668
|
-
throw new CodeModeProtocolError(
|
|
669
|
-
"Interrupt resolution does not match exactly one pending continuation ledger entry.",
|
|
670
|
-
{
|
|
671
|
-
interruptId: interruptResolution.interruptId,
|
|
672
|
-
matches: matches.length
|
|
673
|
-
}
|
|
674
|
-
);
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
function assertReplayEntryMatches(entry, message, bridgeIndex) {
|
|
679
|
-
if (entry.name !== message.toolName || entry.inputJson !== message.inputJson) {
|
|
680
|
-
throw new CodeModeProtocolError(
|
|
681
|
-
"Code mode continuation replay diverged from its bridge ledger.",
|
|
682
|
-
{
|
|
683
|
-
bridgeIndex,
|
|
684
|
-
expectedToolName: entry.name,
|
|
685
|
-
receivedToolName: message.toolName
|
|
686
|
-
}
|
|
687
|
-
);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
function createDeterminismState() {
|
|
691
|
-
return {
|
|
692
|
-
dateNowMs: Date.now(),
|
|
693
|
-
randomSeed: randomBytes(16).toString("hex")
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
function cloneLedger(ledger) {
|
|
697
|
-
return structuredClone(ledger);
|
|
698
|
-
}
|
|
699
|
-
function getMaxNestedToolCounter(ledger) {
|
|
700
|
-
let max = 0;
|
|
701
|
-
for (const entry of ledger) {
|
|
702
|
-
const match = /:tool-(\d+)$/u.exec(entry.toolCallId);
|
|
703
|
-
if (match !== null) {
|
|
704
|
-
max = Math.max(max, Number(match[1]));
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
return max;
|
|
708
|
-
}
|
|
709
|
-
function fromJsonPayload(valueJson) {
|
|
710
|
-
return valueJson === "" ? void 0 : JSON.parse(valueJson);
|
|
711
|
-
}
|
|
712
|
-
function acquireWorker(maxPoolSize) {
|
|
713
|
-
let pooledWorker = idleWorkers.pop();
|
|
714
|
-
while (pooledWorker?.destroyed) {
|
|
715
|
-
pooledWorker = idleWorkers.pop();
|
|
716
|
-
}
|
|
717
|
-
pooledWorker ??= createWorker();
|
|
718
|
-
pooledWorker.worker.removeAllListeners("exit");
|
|
719
|
-
pooledWorker.worker.ref();
|
|
720
|
-
trimIdleWorkers(Math.max(0, maxPoolSize - activeInvocations));
|
|
721
|
-
return pooledWorker;
|
|
722
|
-
}
|
|
723
|
-
function createWorker() {
|
|
724
|
-
return {
|
|
725
|
-
worker: new Worker(getInlineWorkerUrl(), { execArgv: [] }),
|
|
726
|
-
destroyed: false
|
|
727
|
-
};
|
|
728
|
-
}
|
|
729
|
-
function getInlineWorkerUrl() {
|
|
730
|
-
inlineWorkerUrl ??= new URL(
|
|
731
|
-
`data:text/javascript;base64,${Buffer.from(INLINE_CODE_MODE_WORKER_SOURCE).toString("base64")}`
|
|
732
|
-
);
|
|
733
|
-
return inlineWorkerUrl;
|
|
734
|
-
}
|
|
735
|
-
function releaseWorker(pooledWorker) {
|
|
736
|
-
if (pooledWorker.destroyed) {
|
|
737
|
-
return;
|
|
738
|
-
}
|
|
739
|
-
pooledWorker.worker.once("exit", () => {
|
|
740
|
-
pooledWorker.destroyed = true;
|
|
741
|
-
const index = idleWorkers.indexOf(pooledWorker);
|
|
742
|
-
if (index !== -1) {
|
|
743
|
-
idleWorkers.splice(index, 1);
|
|
744
|
-
}
|
|
745
|
-
});
|
|
746
|
-
pooledWorker.worker.unref();
|
|
747
|
-
idleWorkers.push(pooledWorker);
|
|
748
|
-
}
|
|
749
|
-
function destroyWorker(pooledWorker) {
|
|
750
|
-
if (pooledWorker.destroyed) {
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
pooledWorker.destroyed = true;
|
|
754
|
-
pooledWorker.worker.removeAllListeners();
|
|
755
|
-
void pooledWorker.worker.terminate();
|
|
756
|
-
}
|
|
757
|
-
function trimIdleWorkers(maxIdleWorkers) {
|
|
758
|
-
while (idleWorkers.length > maxIdleWorkers) {
|
|
759
|
-
const pooledWorker = idleWorkers.pop();
|
|
760
|
-
if (pooledWorker !== void 0) {
|
|
761
|
-
destroyWorker(pooledWorker);
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
function releaseInvocationSlot(memoryLimitBytes) {
|
|
766
|
-
activeInvocations = Math.max(0, activeInvocations - 1);
|
|
767
|
-
const maxWorkers = getMaxWorkers({
|
|
768
|
-
memoryLimitBytes,
|
|
769
|
-
activeWorkers: activeInvocations
|
|
770
|
-
});
|
|
771
|
-
trimIdleWorkers(Math.max(0, maxWorkers - activeInvocations));
|
|
772
|
-
}
|
|
773
|
-
function settleWithResultMessage(message, resolve, reject) {
|
|
774
|
-
if (!message.success) {
|
|
775
|
-
reject(deserializeResultError(message));
|
|
776
|
-
return;
|
|
777
|
-
}
|
|
778
|
-
resolve(
|
|
779
|
-
message.valueJson === "" || message.valueJson === void 0 ? void 0 : JSON.parse(message.valueJson)
|
|
780
|
-
);
|
|
781
|
-
}
|
|
782
|
-
function deserializeResultError(message) {
|
|
783
|
-
if (message.error === void 0) {
|
|
784
|
-
return new CodeModeProtocolError(
|
|
785
|
-
`Code mode worker failed without an error for ${message.invocationId}.`,
|
|
786
|
-
{ invocationId: message.invocationId }
|
|
787
|
-
);
|
|
788
|
-
}
|
|
789
|
-
return deserializeError(message.error);
|
|
790
|
-
}
|
|
791
|
-
export {
|
|
792
|
-
runManagedCodeMode
|
|
793
|
-
};
|
|
794
|
-
//# sourceMappingURL=manager.js.map
|