@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
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { CodeModeInterruptPayload } from './types.js';
|
|
2
|
+
|
|
3
|
+
const CODE_MODE_HOST_INTERRUPT_SIGNAL = Symbol.for(
|
|
4
|
+
'@ai-sdk/code-mode.host-interrupt',
|
|
5
|
+
);
|
|
6
|
+
|
|
7
|
+
interface CodeModeHostInterruptSignal extends Error {
|
|
8
|
+
readonly [CODE_MODE_HOST_INTERRUPT_SIGNAL]: true;
|
|
9
|
+
readonly payload: CodeModeInterruptPayload;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function requestCodeModeInterrupt<
|
|
13
|
+
TPayload extends CodeModeInterruptPayload,
|
|
14
|
+
>(payload: TPayload): never {
|
|
15
|
+
if (
|
|
16
|
+
typeof payload !== 'object' ||
|
|
17
|
+
payload === null ||
|
|
18
|
+
Array.isArray(payload)
|
|
19
|
+
) {
|
|
20
|
+
throw new TypeError('Code mode interrupt payload must be an object.');
|
|
21
|
+
}
|
|
22
|
+
if (typeof payload.kind !== 'string' || payload.kind.length === 0) {
|
|
23
|
+
throw new TypeError(
|
|
24
|
+
'Code mode interrupt payload must include a string kind.',
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const error = new Error('Code mode host interruption requested.');
|
|
29
|
+
error.name = 'CodeModeHostInterruptSignal';
|
|
30
|
+
Object.defineProperties(error, {
|
|
31
|
+
[CODE_MODE_HOST_INTERRUPT_SIGNAL]: { value: true },
|
|
32
|
+
payload: { value: payload },
|
|
33
|
+
});
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isCodeModeHostInterruptSignal(
|
|
38
|
+
value: unknown,
|
|
39
|
+
): value is CodeModeHostInterruptSignal {
|
|
40
|
+
return (
|
|
41
|
+
typeof value === 'object' &&
|
|
42
|
+
value !== null &&
|
|
43
|
+
(value as { [CODE_MODE_HOST_INTERRUPT_SIGNAL]?: unknown })[
|
|
44
|
+
CODE_MODE_HOST_INTERRUPT_SIGNAL
|
|
45
|
+
] === true &&
|
|
46
|
+
typeof (value as { payload?: unknown }).payload === 'object' &&
|
|
47
|
+
(value as { payload?: unknown }).payload !== null
|
|
48
|
+
);
|
|
49
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
export { DIRECT_TOOL_CALL } from './direct-tool-call.js';
|
|
1
2
|
export {
|
|
2
3
|
codeModeTool as experimental_codeModeTool,
|
|
3
4
|
createCodeModeTool as experimental_createCodeModeTool,
|
|
4
5
|
} from './code-mode-tool.js';
|
|
6
|
+
export {
|
|
7
|
+
continueCodeModeApproval as experimental_continueCodeModeApproval,
|
|
8
|
+
getCodeModeApprovalResponse as experimental_getCodeModeApprovalResponse,
|
|
9
|
+
isCodeModeApprovalInterrupt as experimental_isCodeModeApprovalInterrupt,
|
|
10
|
+
toCodeModeApprovalMessages as experimental_toCodeModeApprovalMessages,
|
|
11
|
+
} from './approval-continuation.js';
|
|
5
12
|
export {
|
|
6
13
|
CodeModeAbortedError,
|
|
7
14
|
CodeModeBridgeLimitError,
|
|
@@ -11,16 +18,37 @@ export {
|
|
|
11
18
|
CodeModeProtocolError,
|
|
12
19
|
CodeModeSourceTooLargeError,
|
|
13
20
|
CodeModeTimeoutError,
|
|
21
|
+
CodeModeToolApprovalDeniedError,
|
|
22
|
+
CodeModeToolApprovalRequiredError,
|
|
14
23
|
CodeModeToolError,
|
|
15
24
|
} from './errors.js';
|
|
25
|
+
export { requestCodeModeInterrupt as experimental_requestCodeModeInterrupt } from './host-interrupt.js';
|
|
26
|
+
export {
|
|
27
|
+
continueCodeModeInterrupt as experimental_continueCodeModeInterrupt,
|
|
28
|
+
getCodeModeInterrupt as experimental_getCodeModeInterrupt,
|
|
29
|
+
isCodeModeInterrupt as experimental_isCodeModeInterrupt,
|
|
30
|
+
unwrapCodeModeResult as experimental_unwrapCodeModeResult,
|
|
31
|
+
} from './interrupt-continuation.js';
|
|
16
32
|
export { runCodeMode as experimental_runCodeMode } from './run-code-mode.js';
|
|
33
|
+
export { setCodeModeContinuationSigningKey as experimental_setCodeModeContinuationSigningKey } from './continuation-capability.js';
|
|
17
34
|
export { setMaxWorkers as experimental_setMaxWorkers } from './runtime/max-workers.js';
|
|
18
35
|
export type {
|
|
36
|
+
ApprovalDecision,
|
|
37
|
+
CodeModeApprovalInterrupt,
|
|
38
|
+
CodeModeApprovalRequest,
|
|
39
|
+
CodeModeApprovalResponse,
|
|
40
|
+
CodeModeContinuation,
|
|
41
|
+
CodeModeContinuationSecurityOptions,
|
|
19
42
|
CodeModeExecutionPolicy,
|
|
43
|
+
CodeModeInterrupt,
|
|
44
|
+
CodeModeInterruptExecutionContext,
|
|
45
|
+
CodeModeInterruptPayload,
|
|
46
|
+
CodeModeInterruptResolution,
|
|
20
47
|
CodeModeOptions,
|
|
21
48
|
CodeModeTool,
|
|
22
49
|
CodeModeToolExecutionOptions,
|
|
23
50
|
CodeModeToolInput,
|
|
24
51
|
CodeModeToolSet,
|
|
52
|
+
CodeModeUnwrappedResult,
|
|
25
53
|
RunCodeModeInput,
|
|
26
54
|
} from './types.js';
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hasValidCodeModeContinuationCapability,
|
|
3
|
+
verifyCodeModeContinuation,
|
|
4
|
+
} from './continuation-capability.js';
|
|
5
|
+
import { CodeModeProtocolError } from './errors.js';
|
|
6
|
+
import { runCodeMode } from './run-code-mode.js';
|
|
7
|
+
import type {
|
|
8
|
+
CodeModeContinuationSecurityOptions,
|
|
9
|
+
CodeModeInterrupt,
|
|
10
|
+
CodeModeInterruptPayload,
|
|
11
|
+
CodeModeInterruptResolution,
|
|
12
|
+
CodeModeOptions,
|
|
13
|
+
CodeModeToolExecutionOptions,
|
|
14
|
+
CodeModeToolSet,
|
|
15
|
+
CodeModeUnwrappedResult,
|
|
16
|
+
} from './types.js';
|
|
17
|
+
|
|
18
|
+
export function isCodeModeInterrupt(
|
|
19
|
+
value: unknown,
|
|
20
|
+
continuationSecurity: CodeModeContinuationSecurityOptions = {},
|
|
21
|
+
): value is CodeModeInterrupt {
|
|
22
|
+
if (
|
|
23
|
+
isRecord(value) &&
|
|
24
|
+
value.type === 'code-mode-interrupt' &&
|
|
25
|
+
typeof value.interruptId === 'string' &&
|
|
26
|
+
typeof value.toolCallId === 'string' &&
|
|
27
|
+
typeof value.toolName === 'string' &&
|
|
28
|
+
typeof value.outerToolCallId === 'string' &&
|
|
29
|
+
isRecord(value.payload) &&
|
|
30
|
+
typeof value.payload.kind === 'string' &&
|
|
31
|
+
isRecord(value.continuation) &&
|
|
32
|
+
hasValidCodeModeContinuationCapability(
|
|
33
|
+
value.continuation,
|
|
34
|
+
continuationSecurity,
|
|
35
|
+
)
|
|
36
|
+
) {
|
|
37
|
+
try {
|
|
38
|
+
assertInterruptMatchesLedger(value as unknown as CodeModeInterrupt);
|
|
39
|
+
return true;
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function continueCodeModeInterrupt<
|
|
48
|
+
TPayload extends CodeModeInterruptPayload = CodeModeInterruptPayload,
|
|
49
|
+
TResolution = unknown,
|
|
50
|
+
>({
|
|
51
|
+
interrupt,
|
|
52
|
+
resolution,
|
|
53
|
+
tools,
|
|
54
|
+
options = {},
|
|
55
|
+
toolExecutionOptions,
|
|
56
|
+
}: {
|
|
57
|
+
interrupt: CodeModeInterrupt<TPayload>;
|
|
58
|
+
resolution: TResolution;
|
|
59
|
+
tools: CodeModeToolSet;
|
|
60
|
+
options?: CodeModeOptions;
|
|
61
|
+
toolExecutionOptions?: Partial<CodeModeToolExecutionOptions>;
|
|
62
|
+
}): Promise<unknown> {
|
|
63
|
+
verifyCodeModeContinuation(
|
|
64
|
+
interrupt.continuation,
|
|
65
|
+
options.continuationSecurity,
|
|
66
|
+
);
|
|
67
|
+
assertInterruptMatchesLedger(interrupt);
|
|
68
|
+
|
|
69
|
+
const interruptResolution: CodeModeInterruptResolution<TResolution> = {
|
|
70
|
+
interruptId: interrupt.interruptId,
|
|
71
|
+
resolution,
|
|
72
|
+
};
|
|
73
|
+
return await runCodeMode({
|
|
74
|
+
js: interrupt.continuation.js,
|
|
75
|
+
tools,
|
|
76
|
+
options,
|
|
77
|
+
continuation: interrupt.continuation,
|
|
78
|
+
interruptResolution,
|
|
79
|
+
...(toolExecutionOptions !== undefined ? { toolExecutionOptions } : {}),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getCodeModeInterrupt(
|
|
84
|
+
result: unknown,
|
|
85
|
+
continuationSecurity: CodeModeContinuationSecurityOptions = {},
|
|
86
|
+
): CodeModeInterrupt | undefined {
|
|
87
|
+
const direct = readInterruptValue(result, continuationSecurity);
|
|
88
|
+
if (direct !== undefined) {
|
|
89
|
+
return direct;
|
|
90
|
+
}
|
|
91
|
+
if (!isRecord(result)) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (const key of ['toolResults', 'content'] as const) {
|
|
96
|
+
const parts = result[key];
|
|
97
|
+
if (!Array.isArray(parts)) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
for (const part of parts) {
|
|
101
|
+
if (!isRecord(part)) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const interrupt = readInterruptValue(part.output, continuationSecurity);
|
|
105
|
+
if (interrupt !== undefined) {
|
|
106
|
+
return interrupt;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function unwrapCodeModeResult(
|
|
114
|
+
result: unknown,
|
|
115
|
+
continuationSecurity: CodeModeContinuationSecurityOptions = {},
|
|
116
|
+
): CodeModeUnwrappedResult {
|
|
117
|
+
const interrupt = getCodeModeInterrupt(result, continuationSecurity);
|
|
118
|
+
return interrupt === undefined
|
|
119
|
+
? { status: 'completed', output: result }
|
|
120
|
+
: { status: 'interrupted', interrupt };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function readInterruptValue(
|
|
124
|
+
value: unknown,
|
|
125
|
+
continuationSecurity: CodeModeContinuationSecurityOptions,
|
|
126
|
+
): CodeModeInterrupt | undefined {
|
|
127
|
+
if (isCodeModeInterrupt(value, continuationSecurity)) {
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
if (
|
|
131
|
+
isRecord(value) &&
|
|
132
|
+
(value.type === 'json' || value.type === 'text') &&
|
|
133
|
+
'value' in value
|
|
134
|
+
) {
|
|
135
|
+
return readInterruptValue(value.value, continuationSecurity);
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function assertInterruptMatchesLedger(interrupt: CodeModeInterrupt): void {
|
|
141
|
+
if (interrupt.continuation.outerToolCallId !== interrupt.outerToolCallId) {
|
|
142
|
+
throw new CodeModeProtocolError(
|
|
143
|
+
'Code-mode interrupt outer tool call id does not match its continuation.',
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
const matches = interrupt.continuation.ledger.filter(
|
|
147
|
+
entry =>
|
|
148
|
+
entry.status === 'interrupted' &&
|
|
149
|
+
entry.interruptId === interrupt.interruptId &&
|
|
150
|
+
entry.toolCallId === interrupt.toolCallId &&
|
|
151
|
+
entry.name === interrupt.toolName &&
|
|
152
|
+
jsonEqual(fromJsonPayload(entry.inputJson), interrupt.input) &&
|
|
153
|
+
jsonEqual(entry.interruptPayload, interrupt.payload),
|
|
154
|
+
);
|
|
155
|
+
if (matches.length !== 1) {
|
|
156
|
+
throw new CodeModeProtocolError(
|
|
157
|
+
'Code-mode interrupt metadata does not match the signed continuation ledger.',
|
|
158
|
+
{ interruptId: interrupt.interruptId, matches: matches.length },
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function jsonEqual(left: unknown, right: unknown): boolean {
|
|
164
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function fromJsonPayload(valueJson: string): unknown {
|
|
168
|
+
return valueJson === '' ? undefined : JSON.parse(valueJson);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
172
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
173
|
+
}
|
|
@@ -90,6 +90,93 @@ const HARDENING_SOURCE = `
|
|
|
90
90
|
})();
|
|
91
91
|
`;
|
|
92
92
|
|
|
93
|
+
const DETERMINISTIC_APIS_SOURCE = `
|
|
94
|
+
var __codeModeResetDateNow = (function(config) {
|
|
95
|
+
var OriginalDate = Date;
|
|
96
|
+
var dateNowMs = Number(config && config.dateNowMs);
|
|
97
|
+
if (!Number.isFinite(dateNowMs)) dateNowMs = 0;
|
|
98
|
+
|
|
99
|
+
function resetDateNow(value) {
|
|
100
|
+
var next = Number(value);
|
|
101
|
+
if (Number.isFinite(next)) dateNowMs = Math.trunc(next);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function nextDateMs() {
|
|
105
|
+
var value = dateNowMs;
|
|
106
|
+
dateNowMs += 1;
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function CodeModeDate() {
|
|
111
|
+
if (new.target) {
|
|
112
|
+
if (arguments.length === 0) return new OriginalDate(nextDateMs());
|
|
113
|
+
return Reflect.construct(OriginalDate, arguments, new.target);
|
|
114
|
+
}
|
|
115
|
+
return new OriginalDate(nextDateMs()).toString();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
Object.defineProperty(CodeModeDate, 'prototype', {
|
|
119
|
+
value: OriginalDate.prototype,
|
|
120
|
+
writable: false,
|
|
121
|
+
configurable: false,
|
|
122
|
+
});
|
|
123
|
+
Object.defineProperty(OriginalDate.prototype, 'constructor', {
|
|
124
|
+
value: CodeModeDate,
|
|
125
|
+
writable: false,
|
|
126
|
+
configurable: false,
|
|
127
|
+
});
|
|
128
|
+
Object.defineProperty(CodeModeDate, 'now', {
|
|
129
|
+
value: nextDateMs,
|
|
130
|
+
writable: false,
|
|
131
|
+
configurable: false,
|
|
132
|
+
});
|
|
133
|
+
Object.defineProperty(CodeModeDate, 'parse', {
|
|
134
|
+
value: OriginalDate.parse,
|
|
135
|
+
writable: false,
|
|
136
|
+
configurable: false,
|
|
137
|
+
});
|
|
138
|
+
Object.defineProperty(CodeModeDate, 'UTC', {
|
|
139
|
+
value: OriginalDate.UTC,
|
|
140
|
+
writable: false,
|
|
141
|
+
configurable: false,
|
|
142
|
+
});
|
|
143
|
+
Object.defineProperty(globalThis, 'Date', {
|
|
144
|
+
value: CodeModeDate,
|
|
145
|
+
writable: false,
|
|
146
|
+
configurable: false,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
var randomSeed = String(config && config.randomSeed || '');
|
|
150
|
+
var mask64 = (1n << 64n) - 1n;
|
|
151
|
+
function readSeedPart(offset, fallback) {
|
|
152
|
+
var part = randomSeed.slice(offset, offset + 16);
|
|
153
|
+
return /^[0-9a-fA-F]{16}$/.test(part) ? BigInt('0x' + part) : fallback;
|
|
154
|
+
}
|
|
155
|
+
var randomState0 = readSeedPart(0, 0x243f6a8885a308d3n);
|
|
156
|
+
var randomState1 = readSeedPart(16, 0x13198a2e03707344n);
|
|
157
|
+
if ((randomState0 | randomState1) === 0n) {
|
|
158
|
+
randomState1 = 0x9e3779b97f4a7c15n;
|
|
159
|
+
}
|
|
160
|
+
function nextRandom64() {
|
|
161
|
+
var s1 = randomState0;
|
|
162
|
+
var s0 = randomState1;
|
|
163
|
+
randomState0 = s0;
|
|
164
|
+
s1 = (s1 ^ ((s1 << 23n) & mask64)) & mask64;
|
|
165
|
+
randomState1 = (s1 ^ s0 ^ (s1 >> 17n) ^ (s0 >> 26n)) & mask64;
|
|
166
|
+
return (randomState1 + s0) & mask64;
|
|
167
|
+
}
|
|
168
|
+
Object.defineProperty(Math, 'random', {
|
|
169
|
+
value: function() {
|
|
170
|
+
return Number(nextRandom64() >> 11n) / 9007199254740992;
|
|
171
|
+
},
|
|
172
|
+
writable: false,
|
|
173
|
+
configurable: false,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return resetDateNow;
|
|
177
|
+
})(__codeModeDeterminism);
|
|
178
|
+
`;
|
|
179
|
+
|
|
93
180
|
const BRIDGE_TRACKING_SOURCE = `
|
|
94
181
|
(function() {
|
|
95
182
|
var nextRecordId = 0;
|
|
@@ -242,11 +329,15 @@ const SERIALIZATION_GUARD_SOURCE = `
|
|
|
242
329
|
|
|
243
330
|
export function buildGuestRuntimeSetupSource(): string {
|
|
244
331
|
return `
|
|
245
|
-
(function(__codeModeInvokeTool) {
|
|
332
|
+
(function(__codeModeInvokeTool, __codeModeDeterminism) {
|
|
333
|
+
${DETERMINISTIC_APIS_SOURCE}
|
|
246
334
|
${HARDENING_SOURCE}
|
|
247
335
|
${BRIDGE_TRACKING_SOURCE}
|
|
248
336
|
${TOOLS_PROXY_SOURCE}
|
|
249
337
|
${SERIALIZATION_GUARD_SOURCE}
|
|
338
|
+
return Object.freeze({
|
|
339
|
+
resetDateNow: __codeModeResetDateNow,
|
|
340
|
+
});
|
|
250
341
|
})
|
|
251
342
|
`;
|
|
252
343
|
}
|