@ai-sdk/code-mode 1.0.5 → 1.0.7
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 +8 -5
- package/dist/approval-continuation.js +84 -0
- package/dist/approval-continuation.js.map +1 -0
- package/dist/approval.js +32 -0
- package/dist/approval.js.map +1 -0
- package/dist/continuation-capability.js +125 -0
- package/dist/continuation-capability.js.map +1 -0
- package/dist/direct-tool-call.js +5 -0
- package/dist/direct-tool-call.js.map +1 -0
- package/dist/errors.js +49 -0
- package/dist/errors.js.map +1 -1
- package/dist/host-interrupt.js +28 -0
- package/dist/host-interrupt.js.map +1 -0
- package/dist/index.d.ts +152 -1
- package/dist/index.js +31 -1
- package/dist/index.js.map +1 -1
- package/dist/interrupt-continuation.js +115 -0
- package/dist/interrupt-continuation.js.map +1 -0
- package/dist/runtime/guest-sources.js +91 -1
- package/dist/runtime/guest-sources.js.map +1 -1
- package/dist/runtime/manager.js +388 -8
- package/dist/runtime/manager.js.map +1 -1
- package/dist/runtime/worker-source.js +1 -1
- package/dist/tool-invocation.js +93 -16
- package/dist/tool-invocation.js.map +1 -1
- package/package.json +3 -3
- package/src/approval-continuation.ts +115 -0
- package/src/approval.ts +60 -0
- package/src/continuation-capability.ts +201 -0
- package/src/direct-tool-call.ts +6 -0
- package/src/errors.ts +65 -0
- package/src/host-interrupt.ts +49 -0
- package/src/index.ts +28 -0
- package/src/interrupt-continuation.ts +173 -0
- package/src/runtime/guest-sources.ts +92 -1
- package/src/runtime/manager.ts +473 -8
- package/src/runtime/protocol.ts +24 -2
- package/src/runtime/worker.ts +116 -6
- package/src/tool-invocation.ts +119 -18
- package/src/types.ts +139 -0
package/src/runtime/worker.ts
CHANGED
|
@@ -28,6 +28,7 @@ const pendingBridgeRequests = new Map<
|
|
|
28
28
|
context: QuickJSAsyncContext;
|
|
29
29
|
deferred: QuickJSDeferredPromise;
|
|
30
30
|
invocationId: string;
|
|
31
|
+
resetDateNow?: QuickJSHandle;
|
|
31
32
|
}
|
|
32
33
|
>();
|
|
33
34
|
let activeInvocationId: string | undefined;
|
|
@@ -35,6 +36,20 @@ let bridgeRequestCounter = 0;
|
|
|
35
36
|
let embeddedQuickJsWasmModulePromise: Promise<WebAssembly.Module> | undefined;
|
|
36
37
|
|
|
37
38
|
parentPort.on('message', (message: MainToWorkerMessage) => {
|
|
39
|
+
if (message.type === 'bridge-drain') {
|
|
40
|
+
if (message.invocationId !== activeInvocationId) {
|
|
41
|
+
throw new CodeModeProtocolError(
|
|
42
|
+
`Bridge drain invocationId mismatch: expected ${activeInvocationId ?? 'no active invocation'}, received ${message.invocationId}.`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
parentPort?.postMessage({
|
|
46
|
+
type: 'bridge-drained',
|
|
47
|
+
invocationId: message.invocationId,
|
|
48
|
+
drainId: message.drainId,
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
if (message.type === 'bridge-response') {
|
|
39
54
|
const pending = pendingBridgeRequests.get(message.requestId);
|
|
40
55
|
if (!pending) {
|
|
@@ -59,7 +74,12 @@ parentPort.on('message', (message: MainToWorkerMessage) => {
|
|
|
59
74
|
}
|
|
60
75
|
|
|
61
76
|
pendingBridgeRequests.delete(message.requestId);
|
|
62
|
-
resolveBridgeResponse(
|
|
77
|
+
resolveBridgeResponse(
|
|
78
|
+
pending.context,
|
|
79
|
+
pending.deferred,
|
|
80
|
+
message,
|
|
81
|
+
pending.resetDateNow,
|
|
82
|
+
);
|
|
63
83
|
return;
|
|
64
84
|
}
|
|
65
85
|
|
|
@@ -111,6 +131,8 @@ async function execute(message: WorkerRunMessage): Promise<string> {
|
|
|
111
131
|
const deadline = Date.now() + message.options.timeoutMs;
|
|
112
132
|
let interruptChecks = 0;
|
|
113
133
|
let bridgeFunctions: { invokeTool: QuickJSHandle } | undefined;
|
|
134
|
+
let determinismHandle: QuickJSHandle | undefined;
|
|
135
|
+
let resetDateNowHandle: QuickJSHandle | undefined;
|
|
114
136
|
|
|
115
137
|
runtime.setMemoryLimit(message.options.memoryLimitBytes);
|
|
116
138
|
runtime.setMaxStackSize(message.options.maxStackSizeBytes);
|
|
@@ -121,7 +143,12 @@ async function execute(message: WorkerRunMessage): Promise<string> {
|
|
|
121
143
|
|
|
122
144
|
try {
|
|
123
145
|
installConsole(context, message.options.maxConsoleOutputBytes);
|
|
124
|
-
bridgeFunctions = createBridgeFunctions(
|
|
146
|
+
bridgeFunctions = createBridgeFunctions(
|
|
147
|
+
context,
|
|
148
|
+
message,
|
|
149
|
+
() => resetDateNowHandle,
|
|
150
|
+
);
|
|
151
|
+
determinismHandle = jsToHandle(context, message.determinism);
|
|
125
152
|
const setupSource = buildGuestRuntimeSetupSource();
|
|
126
153
|
const setupEvalResult = await context.evalCodeAsync(
|
|
127
154
|
setupSource,
|
|
@@ -139,6 +166,7 @@ async function execute(message: WorkerRunMessage): Promise<string> {
|
|
|
139
166
|
setupEvalResult.value,
|
|
140
167
|
context.undefined,
|
|
141
168
|
bridgeFunctions.invokeTool,
|
|
169
|
+
determinismHandle,
|
|
142
170
|
);
|
|
143
171
|
if (setupCallResult.error) {
|
|
144
172
|
const error = context.dump(setupCallResult.error);
|
|
@@ -148,6 +176,10 @@ async function execute(message: WorkerRunMessage): Promise<string> {
|
|
|
148
176
|
throw toError(error);
|
|
149
177
|
}
|
|
150
178
|
if (setupCallResult.value.alive) {
|
|
179
|
+
resetDateNowHandle = context.getProp(
|
|
180
|
+
setupCallResult.value,
|
|
181
|
+
'resetDateNow',
|
|
182
|
+
);
|
|
151
183
|
setupCallResult.value.dispose();
|
|
152
184
|
}
|
|
153
185
|
} finally {
|
|
@@ -210,6 +242,12 @@ async function execute(message: WorkerRunMessage): Promise<string> {
|
|
|
210
242
|
if (bridgeFunctions?.invokeTool.alive) {
|
|
211
243
|
bridgeFunctions.invokeTool.dispose();
|
|
212
244
|
}
|
|
245
|
+
if (determinismHandle?.alive) {
|
|
246
|
+
determinismHandle.dispose();
|
|
247
|
+
}
|
|
248
|
+
if (resetDateNowHandle?.alive) {
|
|
249
|
+
resetDateNowHandle.dispose();
|
|
250
|
+
}
|
|
213
251
|
context.dispose();
|
|
214
252
|
}
|
|
215
253
|
}
|
|
@@ -339,16 +377,19 @@ function dumpConsoleArg(
|
|
|
339
377
|
function createBridgeFunctions(
|
|
340
378
|
context: QuickJSAsyncContext,
|
|
341
379
|
message: WorkerRunMessage,
|
|
380
|
+
getResetDateNow: () => QuickJSHandle | undefined,
|
|
342
381
|
): { invokeTool: QuickJSHandle } {
|
|
343
382
|
const invokeTool = context.newFunction(
|
|
344
383
|
'__codeModeInvokeTool',
|
|
345
384
|
(toolNameHandle: QuickJSHandle, inputJsonHandle: QuickJSHandle) => {
|
|
346
385
|
const toolName = context.getString(toolNameHandle);
|
|
347
386
|
const inputJson = context.getString(inputJsonHandle);
|
|
348
|
-
return requestHost(
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
387
|
+
return requestHost(
|
|
388
|
+
context,
|
|
389
|
+
message.invocationId,
|
|
390
|
+
{ toolName, inputJson },
|
|
391
|
+
getResetDateNow(),
|
|
392
|
+
);
|
|
352
393
|
},
|
|
353
394
|
);
|
|
354
395
|
|
|
@@ -359,6 +400,7 @@ function requestHost(
|
|
|
359
400
|
context: QuickJSAsyncContext,
|
|
360
401
|
invocationId: string,
|
|
361
402
|
payload: Record<string, unknown>,
|
|
403
|
+
resetDateNow: QuickJSHandle | undefined,
|
|
362
404
|
): QuickJSHandle {
|
|
363
405
|
const requestId = `${invocationId}:bridge-${++bridgeRequestCounter}`;
|
|
364
406
|
const deferred = context.newPromise();
|
|
@@ -366,6 +408,7 @@ function requestHost(
|
|
|
366
408
|
context,
|
|
367
409
|
deferred,
|
|
368
410
|
invocationId,
|
|
411
|
+
...(resetDateNow !== undefined ? { resetDateNow } : {}),
|
|
369
412
|
});
|
|
370
413
|
deferred.settled.then(() => {
|
|
371
414
|
context.runtime.executePendingJobs();
|
|
@@ -384,7 +427,9 @@ function resolveBridgeResponse(
|
|
|
384
427
|
context: QuickJSAsyncContext,
|
|
385
428
|
deferred: QuickJSDeferredPromise,
|
|
386
429
|
message: WorkerBridgeResponse,
|
|
430
|
+
resetDateNow?: QuickJSHandle,
|
|
387
431
|
): void {
|
|
432
|
+
resetGuestDateNow(context, resetDateNow, message.dateNowMs);
|
|
388
433
|
if (message.success) {
|
|
389
434
|
const value = context.newString(message.valueJson ?? '');
|
|
390
435
|
deferred.resolve(value);
|
|
@@ -396,6 +441,32 @@ function resolveBridgeResponse(
|
|
|
396
441
|
error.dispose();
|
|
397
442
|
}
|
|
398
443
|
|
|
444
|
+
function resetGuestDateNow(
|
|
445
|
+
context: QuickJSAsyncContext,
|
|
446
|
+
resetDateNow: QuickJSHandle | undefined,
|
|
447
|
+
dateNowMs: number,
|
|
448
|
+
): void {
|
|
449
|
+
if (resetDateNow === undefined) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const value = context.newNumber(dateNowMs);
|
|
453
|
+
try {
|
|
454
|
+
const result = context.callFunction(resetDateNow, context.undefined, value);
|
|
455
|
+
if (result.error) {
|
|
456
|
+
const error = context.dump(result.error);
|
|
457
|
+
if (result.error.alive) {
|
|
458
|
+
result.error.dispose();
|
|
459
|
+
}
|
|
460
|
+
throw toError(error);
|
|
461
|
+
}
|
|
462
|
+
if (result.value.alive) {
|
|
463
|
+
result.value.dispose();
|
|
464
|
+
}
|
|
465
|
+
} finally {
|
|
466
|
+
value.dispose();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
399
470
|
function drainPendingJobs(context: QuickJSAsyncContext): void {
|
|
400
471
|
while (context.runtime.hasPendingJob()) {
|
|
401
472
|
const pending = context.runtime.executePendingJobs();
|
|
@@ -495,3 +566,42 @@ function createBridgeErrorHandle(
|
|
|
495
566
|
}
|
|
496
567
|
return handle;
|
|
497
568
|
}
|
|
569
|
+
|
|
570
|
+
function jsToHandle(
|
|
571
|
+
context: QuickJSAsyncContext,
|
|
572
|
+
value: unknown,
|
|
573
|
+
): QuickJSHandle {
|
|
574
|
+
if (value === null || value === undefined) {
|
|
575
|
+
return context.undefined;
|
|
576
|
+
}
|
|
577
|
+
if (typeof value === 'string') {
|
|
578
|
+
return context.newString(value);
|
|
579
|
+
}
|
|
580
|
+
if (typeof value === 'number') {
|
|
581
|
+
return context.newNumber(value);
|
|
582
|
+
}
|
|
583
|
+
if (typeof value === 'boolean') {
|
|
584
|
+
return value ? context.true : context.false;
|
|
585
|
+
}
|
|
586
|
+
if (Array.isArray(value)) {
|
|
587
|
+
const array = context.newArray();
|
|
588
|
+
value.forEach((item, index) => {
|
|
589
|
+
const itemHandle = jsToHandle(context, item);
|
|
590
|
+
context.setProp(array, index, itemHandle);
|
|
591
|
+
itemHandle.dispose();
|
|
592
|
+
});
|
|
593
|
+
return array;
|
|
594
|
+
}
|
|
595
|
+
if (typeof value === 'object') {
|
|
596
|
+
const object = context.newObject();
|
|
597
|
+
for (const [key, item] of Object.entries(
|
|
598
|
+
value as Record<string, unknown>,
|
|
599
|
+
)) {
|
|
600
|
+
const itemHandle = jsToHandle(context, item);
|
|
601
|
+
context.setProp(object, key, itemHandle);
|
|
602
|
+
itemHandle.dispose();
|
|
603
|
+
}
|
|
604
|
+
return object;
|
|
605
|
+
}
|
|
606
|
+
return context.undefined;
|
|
607
|
+
}
|
package/src/tool-invocation.ts
CHANGED
|
@@ -1,28 +1,57 @@
|
|
|
1
1
|
import { asSchema } from 'ai';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { CODE_MODE_TOOL_APPROVAL_KIND } from './approval.js';
|
|
3
|
+
import {
|
|
4
|
+
CodeModeProtocolError,
|
|
5
|
+
CodeModeToolApprovalDeniedError,
|
|
6
|
+
CodeModeToolApprovalRequiredError,
|
|
7
|
+
CodeModeToolError,
|
|
8
|
+
} from './errors.js';
|
|
9
|
+
import { isCodeModeHostInterruptSignal } from './host-interrupt.js';
|
|
10
|
+
import type {
|
|
11
|
+
CodeModeInterruptExecutionContext,
|
|
12
|
+
CodeModeInterruptPayload,
|
|
13
|
+
CodeModeOptions,
|
|
14
|
+
CodeModeToolExecutionOptions,
|
|
15
|
+
CodeModeToolSet,
|
|
16
|
+
} from './types.js';
|
|
4
17
|
import {
|
|
5
18
|
assertJsonSerializable,
|
|
6
19
|
toJsonPayload,
|
|
7
20
|
} from './utils/serialization.js';
|
|
8
21
|
|
|
22
|
+
export type HostToolInvocationResult =
|
|
23
|
+
| { type: 'success'; valueJson: string }
|
|
24
|
+
| {
|
|
25
|
+
type: 'interrupted';
|
|
26
|
+
toolName: string;
|
|
27
|
+
input: unknown;
|
|
28
|
+
toolCallId: string;
|
|
29
|
+
payload: CodeModeInterruptPayload;
|
|
30
|
+
};
|
|
31
|
+
|
|
9
32
|
export async function invokeHostTool({
|
|
10
33
|
toolName,
|
|
11
34
|
inputJson,
|
|
12
35
|
tools,
|
|
13
36
|
baseExecutionOptions,
|
|
37
|
+
codeModeOptions,
|
|
14
38
|
maxToolInputBytes,
|
|
15
39
|
maxToolOutputBytes,
|
|
16
40
|
toolCallId,
|
|
41
|
+
codeModeInterrupt,
|
|
42
|
+
skipApproval = false,
|
|
17
43
|
}: {
|
|
18
44
|
toolName: string;
|
|
19
45
|
inputJson: string;
|
|
20
46
|
tools: CodeModeToolSet;
|
|
21
47
|
baseExecutionOptions: CodeModeToolExecutionOptions;
|
|
48
|
+
codeModeOptions: CodeModeOptions;
|
|
22
49
|
maxToolInputBytes: number;
|
|
23
50
|
maxToolOutputBytes: number;
|
|
24
51
|
toolCallId: string;
|
|
25
|
-
|
|
52
|
+
codeModeInterrupt?: CodeModeInterruptExecutionContext;
|
|
53
|
+
skipApproval?: boolean;
|
|
54
|
+
}): Promise<HostToolInvocationResult> {
|
|
26
55
|
throwIfAborted(baseExecutionOptions.abortSignal);
|
|
27
56
|
|
|
28
57
|
const hostTool = tools[toolName];
|
|
@@ -55,28 +84,100 @@ export async function invokeHostTool({
|
|
|
55
84
|
const executionOptions: CodeModeToolExecutionOptions = {
|
|
56
85
|
...baseExecutionOptions,
|
|
57
86
|
toolCallId,
|
|
87
|
+
...(codeModeInterrupt !== undefined ? { codeModeInterrupt } : {}),
|
|
58
88
|
};
|
|
59
89
|
|
|
60
|
-
|
|
61
|
-
|
|
90
|
+
const needsApproval =
|
|
91
|
+
!skipApproval &&
|
|
92
|
+
(await raceAgainstAbort(
|
|
62
93
|
requiresApproval(hostTool, validation.value, executionOptions),
|
|
63
94
|
executionOptions.abortSignal,
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
{
|
|
95
|
+
));
|
|
96
|
+
|
|
97
|
+
if (needsApproval) {
|
|
98
|
+
if (codeModeOptions.approval?.mode === 'interrupt') {
|
|
99
|
+
return {
|
|
100
|
+
type: 'interrupted',
|
|
101
|
+
toolName,
|
|
102
|
+
input: validation.value,
|
|
103
|
+
toolCallId,
|
|
104
|
+
payload: { kind: CODE_MODE_TOOL_APPROVAL_KIND },
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const approval = await raceAgainstAbort(
|
|
109
|
+
Promise.resolve(
|
|
110
|
+
codeModeOptions.approval?.onApprovalRequired?.({
|
|
111
|
+
toolName,
|
|
112
|
+
input: validation.value,
|
|
113
|
+
toolCallId,
|
|
114
|
+
}),
|
|
115
|
+
),
|
|
116
|
+
baseExecutionOptions.abortSignal,
|
|
69
117
|
);
|
|
118
|
+
if (approval === undefined) {
|
|
119
|
+
throw new CodeModeToolApprovalRequiredError(
|
|
120
|
+
toolName,
|
|
121
|
+
validation.value,
|
|
122
|
+
toolCallId,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
const approved =
|
|
126
|
+
typeof approval === 'string'
|
|
127
|
+
? approval === 'approved'
|
|
128
|
+
: approval?.approved;
|
|
129
|
+
const reason = typeof approval === 'string' ? undefined : approval?.reason;
|
|
130
|
+
if (typeof approved !== 'boolean') {
|
|
131
|
+
throw new CodeModeProtocolError(
|
|
132
|
+
`Tool "${toolName}" approval callback returned a malformed approval decision.`,
|
|
133
|
+
{ toolName, toolCallId },
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if (reason !== undefined && typeof reason !== 'string') {
|
|
137
|
+
throw new CodeModeProtocolError(
|
|
138
|
+
`Tool "${toolName}" approval callback returned a malformed approval reason.`,
|
|
139
|
+
{ toolName, toolCallId },
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
if (!approved) {
|
|
143
|
+
throw new CodeModeToolApprovalDeniedError(
|
|
144
|
+
toolName,
|
|
145
|
+
validation.value,
|
|
146
|
+
toolCallId,
|
|
147
|
+
reason,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
70
150
|
}
|
|
71
151
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
152
|
+
let output: unknown;
|
|
153
|
+
try {
|
|
154
|
+
output = await raceAgainstAbort(
|
|
155
|
+
executeHostTool(hostTool.execute.bind(hostTool), {
|
|
156
|
+
input: validation.value,
|
|
157
|
+
options: executionOptions,
|
|
158
|
+
}),
|
|
159
|
+
executionOptions.abortSignal,
|
|
160
|
+
);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
if (isCodeModeHostInterruptSignal(error)) {
|
|
163
|
+
return {
|
|
164
|
+
type: 'interrupted',
|
|
165
|
+
toolName,
|
|
166
|
+
input: validation.value,
|
|
167
|
+
toolCallId,
|
|
168
|
+
payload: error.payload,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
type: 'success',
|
|
175
|
+
valueJson: toJsonPayload(
|
|
176
|
+
output,
|
|
177
|
+
maxToolOutputBytes,
|
|
178
|
+
`Tool "${toolName}" output`,
|
|
179
|
+
),
|
|
180
|
+
};
|
|
80
181
|
}
|
|
81
182
|
|
|
82
183
|
async function requiresApproval(
|
package/src/types.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface CodeModeToolExecutionOptions {
|
|
|
14
14
|
abortSignal?: AbortSignal;
|
|
15
15
|
experimental_context?: unknown;
|
|
16
16
|
context?: unknown;
|
|
17
|
+
codeModeInterrupt?: CodeModeInterruptExecutionContext;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -36,6 +37,124 @@ export interface CodeModeToolInput {
|
|
|
36
37
|
js: string;
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
export type ApprovalDecision =
|
|
41
|
+
| 'approved'
|
|
42
|
+
| 'denied'
|
|
43
|
+
| { approved: boolean; reason?: string };
|
|
44
|
+
|
|
45
|
+
export interface CodeModeApprovalRequest {
|
|
46
|
+
toolName: string;
|
|
47
|
+
input: unknown;
|
|
48
|
+
toolCallId: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface CodeModeApprovalResponse {
|
|
52
|
+
approvalId: string;
|
|
53
|
+
approved: boolean;
|
|
54
|
+
reason?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface CodeModeApprovalResolution {
|
|
58
|
+
approved: boolean;
|
|
59
|
+
reason?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface CodeModeInterruptPayload {
|
|
63
|
+
kind: string;
|
|
64
|
+
[key: string]: unknown;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface CodeModeInterruptResolution<TResolution = unknown> {
|
|
68
|
+
interruptId: string;
|
|
69
|
+
resolution: TResolution;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface CodeModeInterruptExecutionContext<
|
|
73
|
+
TPayload extends CodeModeInterruptPayload = CodeModeInterruptPayload,
|
|
74
|
+
TResolution = unknown,
|
|
75
|
+
> {
|
|
76
|
+
interruptId: string;
|
|
77
|
+
payload: TPayload;
|
|
78
|
+
resolution: TResolution;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type CodeModeContinuationLedgerEntry =
|
|
82
|
+
| {
|
|
83
|
+
kind: 'tool';
|
|
84
|
+
name: string;
|
|
85
|
+
inputJson: string;
|
|
86
|
+
toolCallId: string;
|
|
87
|
+
status: 'fulfilled';
|
|
88
|
+
dateNowMs: number;
|
|
89
|
+
valueJson: string;
|
|
90
|
+
}
|
|
91
|
+
| {
|
|
92
|
+
kind: 'tool';
|
|
93
|
+
name: string;
|
|
94
|
+
inputJson: string;
|
|
95
|
+
toolCallId: string;
|
|
96
|
+
status: 'rejected';
|
|
97
|
+
dateNowMs: number;
|
|
98
|
+
error: SerializableError;
|
|
99
|
+
}
|
|
100
|
+
| {
|
|
101
|
+
kind: 'tool';
|
|
102
|
+
name: string;
|
|
103
|
+
inputJson: string;
|
|
104
|
+
toolCallId: string;
|
|
105
|
+
interruptId: string;
|
|
106
|
+
interruptPayload: CodeModeInterruptPayload;
|
|
107
|
+
status: 'interrupted';
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export interface CodeModeDeterminismState {
|
|
111
|
+
dateNowMs: number;
|
|
112
|
+
randomSeed: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface CodeModeContinuationAuth {
|
|
116
|
+
alg: 'HMAC-SHA256';
|
|
117
|
+
nonce: string;
|
|
118
|
+
issuedAtMs: number;
|
|
119
|
+
expiresAtMs: number;
|
|
120
|
+
signature: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface CodeModeContinuation {
|
|
124
|
+
version: 1;
|
|
125
|
+
js: string;
|
|
126
|
+
outerToolCallId: string;
|
|
127
|
+
determinism: CodeModeDeterminismState;
|
|
128
|
+
ledger: CodeModeContinuationLedgerEntry[];
|
|
129
|
+
auth: CodeModeContinuationAuth;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type UnsignedCodeModeContinuation = Omit<CodeModeContinuation, 'auth'>;
|
|
133
|
+
|
|
134
|
+
export interface CodeModeInterrupt<
|
|
135
|
+
TPayload extends CodeModeInterruptPayload = CodeModeInterruptPayload,
|
|
136
|
+
> {
|
|
137
|
+
type: 'code-mode-interrupt';
|
|
138
|
+
interruptId: string;
|
|
139
|
+
toolName: string;
|
|
140
|
+
toolCallId: string;
|
|
141
|
+
outerToolCallId: string;
|
|
142
|
+
input: unknown;
|
|
143
|
+
payload: TPayload;
|
|
144
|
+
continuation: CodeModeContinuation;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export type CodeModeUnwrappedResult =
|
|
148
|
+
| { status: 'completed'; output: unknown }
|
|
149
|
+
| { status: 'interrupted'; interrupt: CodeModeInterrupt };
|
|
150
|
+
|
|
151
|
+
export interface CodeModeApprovalInterruptPayload extends CodeModeInterruptPayload {
|
|
152
|
+
kind: 'ai-sdk-code-mode/tool-approval';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export type CodeModeApprovalInterrupt =
|
|
156
|
+
CodeModeInterrupt<CodeModeApprovalInterruptPayload>;
|
|
157
|
+
|
|
39
158
|
/**
|
|
40
159
|
* Execution limits applied to each sandbox invocation.
|
|
41
160
|
*/
|
|
@@ -67,6 +186,24 @@ export interface CodeModeExecutionPolicy {
|
|
|
67
186
|
*/
|
|
68
187
|
export interface CodeModeOptions {
|
|
69
188
|
executionPolicy?: CodeModeExecutionPolicy;
|
|
189
|
+
continuationSecurity?: CodeModeContinuationSecurityOptions;
|
|
190
|
+
approval?: {
|
|
191
|
+
/**
|
|
192
|
+
* @defaultValue `'callback'`
|
|
193
|
+
*/
|
|
194
|
+
mode?: 'callback' | 'interrupt';
|
|
195
|
+
onApprovalRequired?: (
|
|
196
|
+
request: CodeModeApprovalRequest,
|
|
197
|
+
) => Promise<ApprovalDecision> | ApprovalDecision;
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface CodeModeContinuationSecurityOptions {
|
|
202
|
+
signingKey?: string | Uint8Array;
|
|
203
|
+
/**
|
|
204
|
+
* @defaultValue `60 * 60 * 1000`
|
|
205
|
+
*/
|
|
206
|
+
maxAgeMs?: number;
|
|
70
207
|
}
|
|
71
208
|
|
|
72
209
|
/**
|
|
@@ -77,6 +214,8 @@ export interface RunCodeModeInput {
|
|
|
77
214
|
tools: CodeModeToolSet;
|
|
78
215
|
toolExecutionOptions?: Partial<CodeModeToolExecutionOptions>;
|
|
79
216
|
options?: CodeModeOptions;
|
|
217
|
+
continuation?: CodeModeContinuation;
|
|
218
|
+
interruptResolution?: CodeModeInterruptResolution;
|
|
80
219
|
}
|
|
81
220
|
|
|
82
221
|
/**
|