@ai-sdk/code-mode 0.0.0-2d998b35-20260731001744

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.
Files changed (51) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +99 -0
  3. package/dist/code-mode-tool.js +40 -0
  4. package/dist/code-mode-tool.js.map +1 -0
  5. package/dist/errors.js +192 -0
  6. package/dist/errors.js.map +1 -0
  7. package/dist/index.d.ts +179 -0
  8. package/dist/index.js +33 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/run-code-mode.js +8 -0
  11. package/dist/run-code-mode.js.map +1 -0
  12. package/dist/runtime/guest-sources.js +260 -0
  13. package/dist/runtime/guest-sources.js.map +1 -0
  14. package/dist/runtime/manager.js +414 -0
  15. package/dist/runtime/manager.js.map +1 -0
  16. package/dist/runtime/max-workers.js +40 -0
  17. package/dist/runtime/max-workers.js.map +1 -0
  18. package/dist/runtime/protocol.js +1 -0
  19. package/dist/runtime/protocol.js.map +1 -0
  20. package/dist/runtime/worker-source.d.ts +11 -0
  21. package/dist/runtime/worker-source.js +2 -0
  22. package/dist/tool-invocation.js +129 -0
  23. package/dist/tool-invocation.js.map +1 -0
  24. package/dist/tool-prompt.js +440 -0
  25. package/dist/tool-prompt.js.map +1 -0
  26. package/dist/types.js +1 -0
  27. package/dist/types.js.map +1 -0
  28. package/dist/utils/options.js +76 -0
  29. package/dist/utils/options.js.map +1 -0
  30. package/dist/utils/serialization.js +49 -0
  31. package/dist/utils/serialization.js.map +1 -0
  32. package/dist/utils/source-cache.js +77 -0
  33. package/dist/utils/source-cache.js.map +1 -0
  34. package/package.json +77 -0
  35. package/src/code-mode-tool.ts +60 -0
  36. package/src/errors.ts +267 -0
  37. package/src/index.ts +26 -0
  38. package/src/run-code-mode.ts +15 -0
  39. package/src/runtime/guest-sources.ts +260 -0
  40. package/src/runtime/manager.ts +498 -0
  41. package/src/runtime/max-workers.ts +65 -0
  42. package/src/runtime/protocol.ts +52 -0
  43. package/src/runtime/worker-source.ts +10 -0
  44. package/src/runtime/worker.ts +497 -0
  45. package/src/tool-invocation.ts +181 -0
  46. package/src/tool-prompt.ts +585 -0
  47. package/src/tsconfig.json +11 -0
  48. package/src/types.ts +111 -0
  49. package/src/utils/options.ts +83 -0
  50. package/src/utils/serialization.ts +66 -0
  51. package/src/utils/source-cache.ts +91 -0
@@ -0,0 +1,76 @@
1
+ const DEFAULT_TIMEOUT_MS = 3e4;
2
+ const DEFAULT_MEMORY_LIMIT_BYTES = 64 * 1024 * 1024;
3
+ const DEFAULT_STACK_LIMIT_BYTES = 2 * 1024 * 1024;
4
+ const DEFAULT_MAX_RESULT_BYTES = 1024 * 1024;
5
+ const DEFAULT_MAX_CONSOLE_OUTPUT_BYTES = 64 * 1024;
6
+ const DEFAULT_MAX_SOURCE_BYTES = 256 * 1024;
7
+ const DEFAULT_MAX_TOOL_INPUT_BYTES = 1024 * 1024;
8
+ const DEFAULT_MAX_TOOL_OUTPUT_BYTES = 4 * 1024 * 1024;
9
+ const DEFAULT_MAX_BRIDGE_REQUESTS = 256;
10
+ const DEFAULT_MAX_IN_FLIGHT_BRIDGE_REQUESTS = 32;
11
+ function normalizeOptions(options = {}) {
12
+ const executionPolicy = options.executionPolicy ?? {};
13
+ return {
14
+ timeoutMs: positiveInteger(
15
+ executionPolicy.timeoutMs,
16
+ DEFAULT_TIMEOUT_MS,
17
+ "executionPolicy.timeoutMs"
18
+ ),
19
+ memoryLimitBytes: positiveInteger(
20
+ executionPolicy.memoryLimitBytes,
21
+ DEFAULT_MEMORY_LIMIT_BYTES,
22
+ "executionPolicy.memoryLimitBytes"
23
+ ),
24
+ maxStackSizeBytes: positiveInteger(
25
+ executionPolicy.maxStackSizeBytes,
26
+ DEFAULT_STACK_LIMIT_BYTES,
27
+ "executionPolicy.maxStackSizeBytes"
28
+ ),
29
+ maxResultBytes: positiveInteger(
30
+ executionPolicy.maxResultBytes,
31
+ DEFAULT_MAX_RESULT_BYTES,
32
+ "executionPolicy.maxResultBytes"
33
+ ),
34
+ maxConsoleOutputBytes: positiveInteger(
35
+ executionPolicy.maxConsoleOutputBytes,
36
+ DEFAULT_MAX_CONSOLE_OUTPUT_BYTES,
37
+ "executionPolicy.maxConsoleOutputBytes"
38
+ ),
39
+ maxSourceBytes: positiveInteger(
40
+ executionPolicy.maxSourceBytes,
41
+ DEFAULT_MAX_SOURCE_BYTES,
42
+ "executionPolicy.maxSourceBytes"
43
+ ),
44
+ maxToolInputBytes: positiveInteger(
45
+ executionPolicy.maxToolInputBytes,
46
+ DEFAULT_MAX_TOOL_INPUT_BYTES,
47
+ "executionPolicy.maxToolInputBytes"
48
+ ),
49
+ maxToolOutputBytes: positiveInteger(
50
+ executionPolicy.maxToolOutputBytes,
51
+ DEFAULT_MAX_TOOL_OUTPUT_BYTES,
52
+ "executionPolicy.maxToolOutputBytes"
53
+ ),
54
+ maxBridgeRequests: positiveInteger(
55
+ executionPolicy.maxBridgeRequests,
56
+ DEFAULT_MAX_BRIDGE_REQUESTS,
57
+ "executionPolicy.maxBridgeRequests"
58
+ ),
59
+ maxInFlightBridgeRequests: positiveInteger(
60
+ executionPolicy.maxInFlightBridgeRequests,
61
+ DEFAULT_MAX_IN_FLIGHT_BRIDGE_REQUESTS,
62
+ "executionPolicy.maxInFlightBridgeRequests"
63
+ )
64
+ };
65
+ }
66
+ function positiveInteger(value, fallback, path) {
67
+ const resolved = value ?? fallback;
68
+ if (!Number.isInteger(resolved) || resolved <= 0) {
69
+ throw new TypeError(`${path} must be a positive integer.`);
70
+ }
71
+ return resolved;
72
+ }
73
+ export {
74
+ normalizeOptions
75
+ };
76
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/options.ts"],"sourcesContent":["import type { CodeModeOptions, NormalizedCodeModeOptions } from '../types.js';\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst DEFAULT_MEMORY_LIMIT_BYTES = 64 * 1024 * 1024;\nconst DEFAULT_STACK_LIMIT_BYTES = 2 * 1024 * 1024;\nconst DEFAULT_MAX_RESULT_BYTES = 1024 * 1024;\nconst DEFAULT_MAX_CONSOLE_OUTPUT_BYTES = 64 * 1024;\nconst DEFAULT_MAX_SOURCE_BYTES = 256 * 1024;\nconst DEFAULT_MAX_TOOL_INPUT_BYTES = 1024 * 1024;\nconst DEFAULT_MAX_TOOL_OUTPUT_BYTES = 4 * 1024 * 1024;\nconst DEFAULT_MAX_BRIDGE_REQUESTS = 256;\nconst DEFAULT_MAX_IN_FLIGHT_BRIDGE_REQUESTS = 32;\n\nexport function normalizeOptions(\n options: CodeModeOptions = {},\n): NormalizedCodeModeOptions {\n const executionPolicy = options.executionPolicy ?? {};\n\n return {\n timeoutMs: positiveInteger(\n executionPolicy.timeoutMs,\n DEFAULT_TIMEOUT_MS,\n 'executionPolicy.timeoutMs',\n ),\n memoryLimitBytes: positiveInteger(\n executionPolicy.memoryLimitBytes,\n DEFAULT_MEMORY_LIMIT_BYTES,\n 'executionPolicy.memoryLimitBytes',\n ),\n maxStackSizeBytes: positiveInteger(\n executionPolicy.maxStackSizeBytes,\n DEFAULT_STACK_LIMIT_BYTES,\n 'executionPolicy.maxStackSizeBytes',\n ),\n maxResultBytes: positiveInteger(\n executionPolicy.maxResultBytes,\n DEFAULT_MAX_RESULT_BYTES,\n 'executionPolicy.maxResultBytes',\n ),\n maxConsoleOutputBytes: positiveInteger(\n executionPolicy.maxConsoleOutputBytes,\n DEFAULT_MAX_CONSOLE_OUTPUT_BYTES,\n 'executionPolicy.maxConsoleOutputBytes',\n ),\n maxSourceBytes: positiveInteger(\n executionPolicy.maxSourceBytes,\n DEFAULT_MAX_SOURCE_BYTES,\n 'executionPolicy.maxSourceBytes',\n ),\n maxToolInputBytes: positiveInteger(\n executionPolicy.maxToolInputBytes,\n DEFAULT_MAX_TOOL_INPUT_BYTES,\n 'executionPolicy.maxToolInputBytes',\n ),\n maxToolOutputBytes: positiveInteger(\n executionPolicy.maxToolOutputBytes,\n DEFAULT_MAX_TOOL_OUTPUT_BYTES,\n 'executionPolicy.maxToolOutputBytes',\n ),\n maxBridgeRequests: positiveInteger(\n executionPolicy.maxBridgeRequests,\n DEFAULT_MAX_BRIDGE_REQUESTS,\n 'executionPolicy.maxBridgeRequests',\n ),\n maxInFlightBridgeRequests: positiveInteger(\n executionPolicy.maxInFlightBridgeRequests,\n DEFAULT_MAX_IN_FLIGHT_BRIDGE_REQUESTS,\n 'executionPolicy.maxInFlightBridgeRequests',\n ),\n };\n}\n\nfunction positiveInteger(\n value: number | undefined,\n fallback: number,\n path: string,\n): number {\n const resolved = value ?? fallback;\n if (!Number.isInteger(resolved) || resolved <= 0) {\n throw new TypeError(`${path} must be a positive integer.`);\n }\n return resolved;\n}\n"],"mappings":"AAEA,MAAM,qBAAqB;AAC3B,MAAM,6BAA6B,KAAK,OAAO;AAC/C,MAAM,4BAA4B,IAAI,OAAO;AAC7C,MAAM,2BAA2B,OAAO;AACxC,MAAM,mCAAmC,KAAK;AAC9C,MAAM,2BAA2B,MAAM;AACvC,MAAM,+BAA+B,OAAO;AAC5C,MAAM,gCAAgC,IAAI,OAAO;AACjD,MAAM,8BAA8B;AACpC,MAAM,wCAAwC;AAEvC,SAAS,iBACd,UAA2B,CAAC,GACD;AAC3B,QAAM,kBAAkB,QAAQ,mBAAmB,CAAC;AAEpD,SAAO;AAAA,IACL,WAAW;AAAA,MACT,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,IACA,kBAAkB;AAAA,MAChB,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA,MACjB,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA,MACd,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,IACA,uBAAuB;AAAA,MACrB,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA,MACd,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA,MACjB,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,IACA,oBAAoB;AAAA,MAClB,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA,MACjB,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,IACA,2BAA2B;AAAA,MACzB,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,gBACP,OACA,UACA,MACQ;AACR,QAAM,WAAW,SAAS;AAC1B,MAAI,CAAC,OAAO,UAAU,QAAQ,KAAK,YAAY,GAAG;AAChD,UAAM,IAAI,UAAU,GAAG,IAAI,8BAA8B;AAAA,EAC3D;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,49 @@
1
+ import { CodeModeError } from "../errors.js";
2
+ function assertJsonSerializable(value, maxBytes, label) {
3
+ void toJsonPayload(value, maxBytes, label);
4
+ }
5
+ function toJsonPayload(value, maxBytes, label) {
6
+ if (value === void 0) {
7
+ return "";
8
+ }
9
+ return toStrictJsonPayload(value, maxBytes, label);
10
+ }
11
+ function toStrictJsonPayload(value, maxBytes, label) {
12
+ let encoded;
13
+ try {
14
+ encoded = JSON.stringify(value);
15
+ } catch (error) {
16
+ throw new CodeModeError(
17
+ `${label} is not JSON-serializable: ${getErrorMessage(error)}`,
18
+ "CODE_MODE_SERIALIZATION_ERROR"
19
+ );
20
+ }
21
+ if (encoded === void 0) {
22
+ throw new CodeModeError(
23
+ `${label} is not JSON-serializable.`,
24
+ "CODE_MODE_SERIALIZATION_ERROR"
25
+ );
26
+ }
27
+ assertJsonPayloadSize(encoded, maxBytes, label);
28
+ return encoded;
29
+ }
30
+ function assertJsonPayloadSize(valueJson, maxBytes, label) {
31
+ const bytes = new TextEncoder().encode(valueJson).byteLength;
32
+ if (bytes > maxBytes) {
33
+ throw new CodeModeError(
34
+ `${label} exceeds the ${maxBytes} byte size limit.`,
35
+ "CODE_MODE_SERIALIZATION_ERROR",
36
+ { bytes, maxBytes }
37
+ );
38
+ }
39
+ }
40
+ function getErrorMessage(error) {
41
+ return error instanceof Error ? error.message : String(error);
42
+ }
43
+ export {
44
+ assertJsonPayloadSize,
45
+ assertJsonSerializable,
46
+ toJsonPayload,
47
+ toStrictJsonPayload
48
+ };
49
+ //# sourceMappingURL=serialization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/serialization.ts"],"sourcesContent":["import { CodeModeError } from '../errors.js';\n\nexport function assertJsonSerializable(\n value: unknown,\n maxBytes: number,\n label: string,\n): void {\n void toJsonPayload(value, maxBytes, label);\n}\n\nexport function toJsonPayload(\n value: unknown,\n maxBytes: number,\n label: string,\n): string {\n if (value === undefined) {\n return '';\n }\n\n return toStrictJsonPayload(value, maxBytes, label);\n}\n\nexport function toStrictJsonPayload(\n value: unknown,\n maxBytes: number,\n label: string,\n): string {\n let encoded: string | undefined;\n try {\n encoded = JSON.stringify(value);\n } catch (error) {\n throw new CodeModeError(\n `${label} is not JSON-serializable: ${getErrorMessage(error)}`,\n 'CODE_MODE_SERIALIZATION_ERROR',\n );\n }\n\n if (encoded === undefined) {\n throw new CodeModeError(\n `${label} is not JSON-serializable.`,\n 'CODE_MODE_SERIALIZATION_ERROR',\n );\n }\n\n assertJsonPayloadSize(encoded, maxBytes, label);\n return encoded;\n}\n\nexport function assertJsonPayloadSize(\n valueJson: string,\n maxBytes: number,\n label: string,\n): void {\n const bytes = new TextEncoder().encode(valueJson).byteLength;\n if (bytes > maxBytes) {\n throw new CodeModeError(\n `${label} exceeds the ${maxBytes} byte size limit.`,\n 'CODE_MODE_SERIALIZATION_ERROR',\n { bytes, maxBytes },\n );\n }\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":"AAAA,SAAS,qBAAqB;AAEvB,SAAS,uBACd,OACA,UACA,OACM;AACN,OAAK,cAAc,OAAO,UAAU,KAAK;AAC3C;AAEO,SAAS,cACd,OACA,UACA,OACQ;AACR,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AAEA,SAAO,oBAAoB,OAAO,UAAU,KAAK;AACnD;AAEO,SAAS,oBACd,OACA,UACA,OACQ;AACR,MAAI;AACJ,MAAI;AACF,cAAU,KAAK,UAAU,KAAK;AAAA,EAChC,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,GAAG,KAAK,8BAA8B,gBAAgB,KAAK,CAAC;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AAEA,MAAI,YAAY,QAAW;AACzB,UAAM,IAAI;AAAA,MACR,GAAG,KAAK;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,wBAAsB,SAAS,UAAU,KAAK;AAC9C,SAAO;AACT;AAEO,SAAS,sBACd,WACA,UACA,OACM;AACN,QAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,SAAS,EAAE;AAClD,MAAI,QAAQ,UAAU;AACpB,UAAM,IAAI;AAAA,MACR,GAAG,KAAK,gBAAgB,QAAQ;AAAA,MAChC;AAAA,MACA,EAAE,OAAO,SAAS;AAAA,IACpB;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,OAAwB;AAC/C,SAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;","names":[]}
@@ -0,0 +1,77 @@
1
+ import { createHash } from "node:crypto";
2
+ import { stripTypeScriptTypes } from "node:module";
3
+ import { CodeModeSourceTooLargeError } from "../errors.js";
4
+ const MAX_CACHE_ENTRIES = 256;
5
+ const MAX_CACHE_BYTES = 4 * 1024 * 1024;
6
+ const MAX_CACHE_ENTRY_BYTES = 64 * 1024;
7
+ const transformedSourceCache = /* @__PURE__ */ new Map();
8
+ let transformedSourceCacheBytes = 0;
9
+ function assertSourceSize(source, maxSourceBytes) {
10
+ const bytes = byteLength(source);
11
+ if (bytes > maxSourceBytes) {
12
+ throw new CodeModeSourceTooLargeError(bytes, maxSourceBytes);
13
+ }
14
+ }
15
+ function transformSource(source) {
16
+ const hash = createHash("sha256").update("strip:").update(source).digest("hex");
17
+ const cached = transformedSourceCache.get(hash);
18
+ if (cached !== void 0) {
19
+ transformedSourceCache.delete(hash);
20
+ transformedSourceCache.set(hash, cached);
21
+ return cached.source;
22
+ }
23
+ const transformed = stripSnippetTypes(source);
24
+ const transformedBytes = byteLength(transformed);
25
+ if (transformedBytes <= MAX_CACHE_ENTRY_BYTES) {
26
+ transformedSourceCache.set(hash, {
27
+ source: transformed,
28
+ bytes: transformedBytes
29
+ });
30
+ transformedSourceCacheBytes += transformedBytes;
31
+ evictTransformedSourceCache();
32
+ }
33
+ return transformed;
34
+ }
35
+ function getTransformedSourceCacheStats() {
36
+ return {
37
+ entries: transformedSourceCache.size,
38
+ bytes: transformedSourceCacheBytes,
39
+ maxEntries: MAX_CACHE_ENTRIES,
40
+ maxBytes: MAX_CACHE_BYTES,
41
+ maxEntryBytes: MAX_CACHE_ENTRY_BYTES
42
+ };
43
+ }
44
+ function clearTransformedSourceCache() {
45
+ transformedSourceCache.clear();
46
+ transformedSourceCacheBytes = 0;
47
+ }
48
+ function stripSnippetTypes(source) {
49
+ const prefix = "async function __codeModeUser__(){\n";
50
+ const suffix = "\n}";
51
+ const stripped = stripTypeScriptTypes(`${prefix}${source}${suffix}`);
52
+ if (!stripped.startsWith(prefix) || !stripped.endsWith(suffix)) {
53
+ return source;
54
+ }
55
+ return stripped.slice(prefix.length, -suffix.length);
56
+ }
57
+ function evictTransformedSourceCache() {
58
+ while (transformedSourceCache.size > MAX_CACHE_ENTRIES || transformedSourceCacheBytes > MAX_CACHE_BYTES) {
59
+ const oldest = transformedSourceCache.keys().next().value;
60
+ if (oldest === void 0) {
61
+ return;
62
+ }
63
+ const entry = transformedSourceCache.get(oldest);
64
+ transformedSourceCache.delete(oldest);
65
+ transformedSourceCacheBytes -= entry?.bytes ?? 0;
66
+ }
67
+ }
68
+ function byteLength(value) {
69
+ return new TextEncoder().encode(value).byteLength;
70
+ }
71
+ export {
72
+ assertSourceSize,
73
+ clearTransformedSourceCache,
74
+ getTransformedSourceCacheStats,
75
+ transformSource
76
+ };
77
+ //# sourceMappingURL=source-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/source-cache.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { stripTypeScriptTypes } from 'node:module';\nimport { CodeModeSourceTooLargeError } from '../errors.js';\n\nconst MAX_CACHE_ENTRIES = 256;\nconst MAX_CACHE_BYTES = 4 * 1024 * 1024;\nconst MAX_CACHE_ENTRY_BYTES = 64 * 1024;\nconst transformedSourceCache = new Map<\n string,\n {\n source: string;\n bytes: number;\n }\n>();\nlet transformedSourceCacheBytes = 0;\n\nexport function assertSourceSize(source: string, maxSourceBytes: number): void {\n const bytes = byteLength(source);\n if (bytes > maxSourceBytes) {\n throw new CodeModeSourceTooLargeError(bytes, maxSourceBytes);\n }\n}\n\nexport function transformSource(source: string): string {\n const hash = createHash('sha256')\n .update('strip:')\n .update(source)\n .digest('hex');\n const cached = transformedSourceCache.get(hash);\n if (cached !== undefined) {\n transformedSourceCache.delete(hash);\n transformedSourceCache.set(hash, cached);\n return cached.source;\n }\n\n const transformed = stripSnippetTypes(source);\n const transformedBytes = byteLength(transformed);\n if (transformedBytes <= MAX_CACHE_ENTRY_BYTES) {\n transformedSourceCache.set(hash, {\n source: transformed,\n bytes: transformedBytes,\n });\n transformedSourceCacheBytes += transformedBytes;\n evictTransformedSourceCache();\n }\n return transformed;\n}\n\nexport function getTransformedSourceCacheStats() {\n return {\n entries: transformedSourceCache.size,\n bytes: transformedSourceCacheBytes,\n maxEntries: MAX_CACHE_ENTRIES,\n maxBytes: MAX_CACHE_BYTES,\n maxEntryBytes: MAX_CACHE_ENTRY_BYTES,\n };\n}\n\nexport function clearTransformedSourceCache(): void {\n transformedSourceCache.clear();\n transformedSourceCacheBytes = 0;\n}\n\nfunction stripSnippetTypes(source: string): string {\n const prefix = 'async function __codeModeUser__(){\\n';\n const suffix = '\\n}';\n const stripped = stripTypeScriptTypes(`${prefix}${source}${suffix}`);\n if (!stripped.startsWith(prefix) || !stripped.endsWith(suffix)) {\n return source;\n }\n return stripped.slice(prefix.length, -suffix.length);\n}\n\nfunction evictTransformedSourceCache(): void {\n while (\n transformedSourceCache.size > MAX_CACHE_ENTRIES ||\n transformedSourceCacheBytes > MAX_CACHE_BYTES\n ) {\n const oldest = transformedSourceCache.keys().next().value;\n if (oldest === undefined) {\n return;\n }\n const entry = transformedSourceCache.get(oldest);\n transformedSourceCache.delete(oldest);\n transformedSourceCacheBytes -= entry?.bytes ?? 0;\n }\n}\n\nfunction byteLength(value: string): number {\n return new TextEncoder().encode(value).byteLength;\n}\n"],"mappings":"AAAA,SAAS,kBAAkB;AAC3B,SAAS,4BAA4B;AACrC,SAAS,mCAAmC;AAE5C,MAAM,oBAAoB;AAC1B,MAAM,kBAAkB,IAAI,OAAO;AACnC,MAAM,wBAAwB,KAAK;AACnC,MAAM,yBAAyB,oBAAI,IAMjC;AACF,IAAI,8BAA8B;AAE3B,SAAS,iBAAiB,QAAgB,gBAA8B;AAC7E,QAAM,QAAQ,WAAW,MAAM;AAC/B,MAAI,QAAQ,gBAAgB;AAC1B,UAAM,IAAI,4BAA4B,OAAO,cAAc;AAAA,EAC7D;AACF;AAEO,SAAS,gBAAgB,QAAwB;AACtD,QAAM,OAAO,WAAW,QAAQ,EAC7B,OAAO,QAAQ,EACf,OAAO,MAAM,EACb,OAAO,KAAK;AACf,QAAM,SAAS,uBAAuB,IAAI,IAAI;AAC9C,MAAI,WAAW,QAAW;AACxB,2BAAuB,OAAO,IAAI;AAClC,2BAAuB,IAAI,MAAM,MAAM;AACvC,WAAO,OAAO;AAAA,EAChB;AAEA,QAAM,cAAc,kBAAkB,MAAM;AAC5C,QAAM,mBAAmB,WAAW,WAAW;AAC/C,MAAI,oBAAoB,uBAAuB;AAC7C,2BAAuB,IAAI,MAAM;AAAA,MAC/B,QAAQ;AAAA,MACR,OAAO;AAAA,IACT,CAAC;AACD,mCAA+B;AAC/B,gCAA4B;AAAA,EAC9B;AACA,SAAO;AACT;AAEO,SAAS,iCAAiC;AAC/C,SAAO;AAAA,IACL,SAAS,uBAAuB;AAAA,IAChC,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,eAAe;AAAA,EACjB;AACF;AAEO,SAAS,8BAAoC;AAClD,yBAAuB,MAAM;AAC7B,gCAA8B;AAChC;AAEA,SAAS,kBAAkB,QAAwB;AACjD,QAAM,SAAS;AACf,QAAM,SAAS;AACf,QAAM,WAAW,qBAAqB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE;AACnE,MAAI,CAAC,SAAS,WAAW,MAAM,KAAK,CAAC,SAAS,SAAS,MAAM,GAAG;AAC9D,WAAO;AAAA,EACT;AACA,SAAO,SAAS,MAAM,OAAO,QAAQ,CAAC,OAAO,MAAM;AACrD;AAEA,SAAS,8BAAoC;AAC3C,SACE,uBAAuB,OAAO,qBAC9B,8BAA8B,iBAC9B;AACA,UAAM,SAAS,uBAAuB,KAAK,EAAE,KAAK,EAAE;AACpD,QAAI,WAAW,QAAW;AACxB;AAAA,IACF;AACA,UAAM,QAAQ,uBAAuB,IAAI,MAAM;AAC/C,2BAAuB,OAAO,MAAM;AACpC,mCAA+B,OAAO,SAAS;AAAA,EACjD;AACF;AAEA,SAAS,WAAW,OAAuB;AACzC,SAAO,IAAI,YAAY,EAAE,OAAO,KAAK,EAAE;AACzC;","names":[]}
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@ai-sdk/code-mode",
3
+ "version": "0.0.0-2d998b35-20260731001744",
4
+ "description": "QuickJS-backed code mode tool for AI SDK",
5
+ "keywords": [
6
+ "ai",
7
+ "ai-sdk",
8
+ "agents",
9
+ "code-mode",
10
+ "quickjs",
11
+ "sandbox",
12
+ "tools"
13
+ ],
14
+ "type": "module",
15
+ "license": "Apache-2.0",
16
+ "sideEffects": false,
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "source": "./src/index.ts",
20
+ "exports": {
21
+ "./package.json": "./package.json",
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js",
25
+ "default": "./dist/index.js"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist/**/*",
30
+ "src",
31
+ "!src/**/*.test.ts",
32
+ "!src/**/*.test-d.ts",
33
+ "!src/**/__snapshots__",
34
+ "!src/**/__fixtures__",
35
+ "!src/utils/test-helpers.ts",
36
+ "LICENSE",
37
+ "README.md"
38
+ ],
39
+ "peerDependencies": {
40
+ "ai": "0.0.0-2d998b35-20260731001744"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "22.19.19",
44
+ "esbuild": "^0.28.1",
45
+ "quickjs-emscripten": "^0.32.0",
46
+ "tsup": "^8.5.1",
47
+ "typescript": "5.8.3",
48
+ "zod": "3.25.76",
49
+ "@vercel/ai-tsconfig": "0.0.0",
50
+ "ai": "0.0.0-2d998b35-20260731001744"
51
+ },
52
+ "engines": {
53
+ "node": ">=22"
54
+ },
55
+ "publishConfig": {
56
+ "access": "public",
57
+ "provenance": true
58
+ },
59
+ "homepage": "https://ai-sdk.dev/docs",
60
+ "repository": {
61
+ "type": "git",
62
+ "url": "https://github.com/vercel/ai",
63
+ "directory": "packages/code-mode"
64
+ },
65
+ "bugs": {
66
+ "url": "https://github.com/vercel/ai/issues"
67
+ },
68
+ "scripts": {
69
+ "build": "pnpm clean && tsup --tsconfig tsconfig.build.json && node scripts/embed-inline-worker.mjs",
70
+ "clean": "del-cli build dist *.tsbuildinfo",
71
+ "type-check": "tsc --build && tsc -p src/tsconfig.json",
72
+ "test": "pnpm test:node",
73
+ "test:watch": "vitest --config vitest.node.config.js",
74
+ "test:node": "vitest --config vitest.node.config.js --run",
75
+ "test:e2e": "pnpm build && vitest --config vitest.e2e.config.ts --run"
76
+ }
77
+ }
@@ -0,0 +1,60 @@
1
+ import {
2
+ experimental_toolCaller,
3
+ jsonSchema,
4
+ tool,
5
+ type Experimental_ToolCallerTool,
6
+ } from 'ai';
7
+ import { runCodeMode } from './run-code-mode.js';
8
+ import { buildCodeModeToolDescription } from './tool-prompt.js';
9
+ import type {
10
+ CodeModeOptions,
11
+ CodeModeTool,
12
+ CodeModeToolInput,
13
+ CodeModeToolSet,
14
+ } from './types.js';
15
+
16
+ /**
17
+ * Creates an AI SDK tool that executes code-mode TypeScript in an isolated
18
+ * sandbox.
19
+ */
20
+ export function createCodeModeTool(
21
+ tools: CodeModeToolSet,
22
+ options: CodeModeOptions = {},
23
+ ): CodeModeTool {
24
+ return tool<CodeModeToolInput, unknown, Record<string, unknown>>({
25
+ description: buildCodeModeToolDescription(tools),
26
+ inputSchema: jsonSchema<CodeModeToolInput>({
27
+ type: 'object',
28
+ properties: {
29
+ js: {
30
+ type: 'string',
31
+ description:
32
+ 'Code-mode TypeScript source to execute. The tool description lists the available global `tools` API, input types, and call examples.',
33
+ },
34
+ },
35
+ required: ['js'],
36
+ additionalProperties: false,
37
+ }),
38
+ execute: async (input, executionOptions) =>
39
+ await runCodeMode({
40
+ js: input.js,
41
+ tools,
42
+ toolExecutionOptions: executionOptions,
43
+ options,
44
+ }),
45
+ }) as CodeModeTool;
46
+ }
47
+
48
+ /**
49
+ * Creates a code-mode caller whose host tools are bound by the surrounding
50
+ * AI SDK generation call.
51
+ */
52
+ export function codeModeTool(
53
+ options: CodeModeOptions = {},
54
+ ): Experimental_ToolCallerTool<CodeModeTool> {
55
+ return experimental_toolCaller(createCodeModeTool({}, options), {
56
+ type: 'local',
57
+ bind: tools =>
58
+ createCodeModeTool(tools as unknown as CodeModeToolSet, options),
59
+ });
60
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,267 @@
1
+ import type { SerializableError } from './types.js';
2
+
3
+ /**
4
+ * Base class for errors raised by code mode.
5
+ *
6
+ * All package-specific errors include a stable `code` string and may include
7
+ * structured `details` for diagnostics.
8
+ */
9
+ export class CodeModeError extends Error {
10
+ /**
11
+ * Stable machine-readable error code.
12
+ */
13
+ code: string;
14
+ /**
15
+ * Optional structured diagnostic details.
16
+ */
17
+ readonly details?: unknown;
18
+
19
+ constructor(message: string, code = 'CODE_MODE_ERROR', details?: unknown) {
20
+ super(message);
21
+ this.name = new.target.name;
22
+ this.code = code;
23
+ this.details = details;
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Raised when a sandbox invocation exceeds its timeout.
29
+ */
30
+ export class CodeModeTimeoutError extends CodeModeError {
31
+ constructor(timeoutMs: number) {
32
+ super(
33
+ `Code mode execution timed out after ${timeoutMs}ms.`,
34
+ 'CODE_MODE_TIMEOUT',
35
+ { timeoutMs },
36
+ );
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Raised when the outer AI SDK abort signal aborts a code-mode invocation.
42
+ */
43
+ export class CodeModeAbortedError extends CodeModeError {
44
+ constructor() {
45
+ super('Code mode execution was aborted.', 'CODE_MODE_ABORTED');
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Raised when the process-global worker cap has been reached.
51
+ *
52
+ * Configure the cap with `setMaxWorkers`.
53
+ */
54
+ export class CodeModeConcurrencyError extends CodeModeError {
55
+ constructor(maxWorkers: number) {
56
+ super(
57
+ `Code mode maxWorkers limit reached (${maxWorkers}).`,
58
+ 'CODE_MODE_CONCURRENCY_LIMIT',
59
+ { maxWorkers },
60
+ );
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Raised when the provided source exceeds `executionPolicy.maxSourceBytes`.
66
+ */
67
+ export class CodeModeSourceTooLargeError extends CodeModeError {
68
+ constructor(bytes: number, maxBytes: number) {
69
+ super(
70
+ `Code mode source exceeds the ${maxBytes} byte size limit.`,
71
+ 'CODE_MODE_SOURCE_TOO_LARGE',
72
+ { bytes, maxBytes },
73
+ );
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Raised when sandboxed code exceeds bridge request limits.
79
+ */
80
+ export class CodeModeBridgeLimitError extends CodeModeError {
81
+ constructor(message: string, details?: unknown) {
82
+ super(message, 'CODE_MODE_BRIDGE_LIMIT', details);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Raised when sandboxed code starts host bridge work and returns without
88
+ * awaiting or otherwise observing it.
89
+ */
90
+ export class CodeModeDetachedBridgeRequestError extends CodeModeError {
91
+ constructor(message: string, details?: unknown) {
92
+ super(message, 'CODE_MODE_DETACHED_BRIDGE_REQUEST', details);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Raised when the main thread and worker protocol observes an invalid or
98
+ * mismatched message.
99
+ */
100
+ export class CodeModeProtocolError extends CodeModeError {
101
+ constructor(message: string, details?: unknown) {
102
+ super(message, 'CODE_MODE_PROTOCOL_ERROR', details);
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Base class for failures caused by nested host tool execution.
108
+ */
109
+ export class CodeModeToolError extends CodeModeError {
110
+ constructor(message: string, details?: unknown) {
111
+ super(message, 'CODE_MODE_TOOL_ERROR', details);
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Converts an unknown thrown value into a worker-safe serializable shape.
117
+ *
118
+ * @internal
119
+ */
120
+ export function serializeError(error: unknown): SerializableError {
121
+ if (error instanceof CodeModeError) {
122
+ const result = compactError({
123
+ name: error.name,
124
+ message: error.message,
125
+ code: error.code,
126
+ });
127
+ if (error.stack !== undefined) {
128
+ result.stack = error.stack;
129
+ }
130
+ if (error.details !== undefined) {
131
+ result.details = error.details;
132
+ }
133
+ return result;
134
+ }
135
+
136
+ if (error instanceof Error) {
137
+ const result = compactError({
138
+ name: error.name,
139
+ message: error.message,
140
+ });
141
+ if (error.stack !== undefined) {
142
+ result.stack = error.stack;
143
+ }
144
+ const maybeCoded = error as Error & { code?: unknown; details?: unknown };
145
+ if (typeof maybeCoded.code === 'string') {
146
+ result.code = maybeCoded.code;
147
+ }
148
+ if (maybeCoded.details !== undefined) {
149
+ result.details = maybeCoded.details;
150
+ }
151
+ return result;
152
+ }
153
+
154
+ return {
155
+ name: 'Error',
156
+ message: String(error),
157
+ };
158
+ }
159
+
160
+ /**
161
+ * Converts host bridge failures into a sandbox-visible sanitized shape.
162
+ *
163
+ * This intentionally omits stack traces and diagnostic details. Full host
164
+ * diagnostics remain on the host side.
165
+ *
166
+ * @internal
167
+ */
168
+ export function serializeBridgeErrorForGuest(
169
+ error: unknown,
170
+ context: 'tool' | 'bridge',
171
+ ): SerializableError {
172
+ if (error instanceof CodeModeError) {
173
+ return compactError({
174
+ name: error.name,
175
+ message: error.message,
176
+ code: error.code,
177
+ });
178
+ }
179
+
180
+ const fallback =
181
+ context === 'tool'
182
+ ? {
183
+ message: 'Host tool failed.',
184
+ code: 'CODE_MODE_HOST_TOOL_ERROR',
185
+ }
186
+ : {
187
+ message: 'Host bridge request failed.',
188
+ code: 'CODE_MODE_HOST_BRIDGE_ERROR',
189
+ };
190
+
191
+ return {
192
+ name: 'Error',
193
+ message: fallback.message,
194
+ code: fallback.code,
195
+ };
196
+ }
197
+
198
+ function compactError(error: {
199
+ name: string;
200
+ message: string;
201
+ stack?: string;
202
+ code?: string;
203
+ details?: unknown;
204
+ }): SerializableError {
205
+ return {
206
+ name: error.name,
207
+ message: error.message,
208
+ ...(error.stack !== undefined ? { stack: error.stack } : {}),
209
+ ...(error.code !== undefined ? { code: error.code } : {}),
210
+ ...(error.details !== undefined ? { details: error.details } : {}),
211
+ };
212
+ }
213
+
214
+ /**
215
+ * Rehydrates a serialized worker error into an Error instance.
216
+ *
217
+ * @internal
218
+ */
219
+ export function deserializeError(error: SerializableError): Error {
220
+ if (error.code === 'CODE_MODE_SOURCE_TOO_LARGE') {
221
+ const details = error.details as
222
+ | { bytes?: number; maxBytes?: number }
223
+ | undefined;
224
+ const result = new CodeModeSourceTooLargeError(
225
+ details?.bytes ?? 0,
226
+ details?.maxBytes ?? 0,
227
+ );
228
+ restoreStack(result, error);
229
+ return result;
230
+ }
231
+
232
+ if (error.code === 'CODE_MODE_BRIDGE_LIMIT') {
233
+ const result = new CodeModeBridgeLimitError(error.message, error.details);
234
+ restoreStack(result, error);
235
+ return result;
236
+ }
237
+
238
+ if (error.code === 'CODE_MODE_DETACHED_BRIDGE_REQUEST') {
239
+ const result = new CodeModeDetachedBridgeRequestError(
240
+ error.message,
241
+ error.details,
242
+ );
243
+ restoreStack(result, error);
244
+ return result;
245
+ }
246
+
247
+ if (error.code === 'CODE_MODE_PROTOCOL_ERROR') {
248
+ const result = new CodeModeProtocolError(error.message, error.details);
249
+ restoreStack(result, error);
250
+ return result;
251
+ }
252
+
253
+ const result = new CodeModeError(
254
+ error.message,
255
+ error.code ?? 'CODE_MODE_ERROR',
256
+ error.details,
257
+ );
258
+ result.name = error.name;
259
+ restoreStack(result, error);
260
+ return result;
261
+ }
262
+
263
+ function restoreStack(error: Error, serialized: SerializableError): void {
264
+ if (serialized.stack) {
265
+ error.stack = serialized.stack;
266
+ }
267
+ }
package/src/index.ts ADDED
@@ -0,0 +1,26 @@
1
+ export {
2
+ codeModeTool as experimental_codeModeTool,
3
+ createCodeModeTool as experimental_createCodeModeTool,
4
+ } from './code-mode-tool.js';
5
+ export {
6
+ CodeModeAbortedError,
7
+ CodeModeBridgeLimitError,
8
+ CodeModeConcurrencyError,
9
+ CodeModeDetachedBridgeRequestError,
10
+ CodeModeError,
11
+ CodeModeProtocolError,
12
+ CodeModeSourceTooLargeError,
13
+ CodeModeTimeoutError,
14
+ CodeModeToolError,
15
+ } from './errors.js';
16
+ export { runCodeMode as experimental_runCodeMode } from './run-code-mode.js';
17
+ export { setMaxWorkers as experimental_setMaxWorkers } from './runtime/max-workers.js';
18
+ export type {
19
+ CodeModeExecutionPolicy,
20
+ CodeModeOptions,
21
+ CodeModeTool,
22
+ CodeModeToolExecutionOptions,
23
+ CodeModeToolInput,
24
+ CodeModeToolSet,
25
+ RunCodeModeInput,
26
+ } from './types.js';
@@ -0,0 +1,15 @@
1
+ import { runManagedCodeMode } from './runtime/manager.js';
2
+ import type { RunCodeModeInput } from './types.js';
3
+
4
+ /**
5
+ * Runs one code-mode invocation directly without wrapping it as an AI SDK tool.
6
+ *
7
+ * The source is executed in a fresh QuickJS context with the provided host tools
8
+ * and execution limits.
9
+ *
10
+ * @param input - Source code, host tools, forwarded tool execution options, and code-mode options.
11
+ * @returns The JSON-serializable value returned by the sandboxed program.
12
+ */
13
+ export async function runCodeMode(input: RunCodeModeInput): Promise<unknown> {
14
+ return await runManagedCodeMode(input);
15
+ }