@tanstack/ai-code-mode 0.3.8 → 0.3.10

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.
@@ -1,24 +1,37 @@
1
- import { renderLazyCatalogEntry } from "@tanstack/ai";
2
1
  import { toolsToBindings } from "./bindings/tool-to-binding.js";
3
2
  import { generateTypeStubs } from "./type-generator/json-schema-to-ts.js";
3
+ import { renderLazyCatalogEntry } from "@tanstack/ai";
4
+ //#region src/create-system-prompt.ts
5
+ /**
6
+ * Create a system prompt snippet that documents the execute_typescript tool
7
+ * and all available external_* functions.
8
+ *
9
+ * Add this to your system prompts array when using createCodeModeTool.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { createCodeMode } from '@tanstack/ai-code-mode'
14
+ * import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'
15
+ *
16
+ * const { tool, systemPrompt } = createCodeMode({
17
+ * driver: createNodeIsolateDriver(),
18
+ * tools: [weatherTool, dbTool],
19
+ * })
20
+ *
21
+ * chat({
22
+ * systemPrompts: ['You are a helpful assistant.', systemPrompt],
23
+ * tools: [tool, ...otherTools],
24
+ * })
25
+ * ```
26
+ */
4
27
  function createCodeModeSystemPrompt(config) {
5
- const { tools } = config;
6
- const include = config.lazyToolsConfig?.includeDescription ?? "none";
7
- const eagerTools = tools.filter((t) => !t.lazy);
8
- const lazyTools = tools.filter((t) => t.lazy);
9
- const bindings = toolsToBindings(eagerTools, "external_");
10
- const typeStubs = generateTypeStubs(bindings);
11
- const functionDocs = Object.entries(bindings).map(([name, binding]) => `- \`${name}(input)\`: ${binding.description}`).join("\n");
12
- const discoverableSection = lazyTools.length > 0 ? `
13
-
14
- ### Discoverable APIs
15
-
16
- These additional functions are available but not yet documented. Before calling \`external_<name>\` for any of them inside \`execute_typescript\`, call the \`discover_tools\` tool with their names to get full TypeScript signatures:
17
-
18
- ${lazyTools.map(
19
- (t) => `- ${renderLazyCatalogEntry(`external_${t.name}`, t.description, include)}`
20
- ).join("\n")}` : "";
21
- return `## Code Execution Tool
28
+ const { tools } = config;
29
+ const include = config.lazyToolsConfig?.includeDescription ?? "none";
30
+ const eagerTools = tools.filter((t) => !t.lazy);
31
+ const lazyTools = tools.filter((t) => t.lazy);
32
+ const bindings = toolsToBindings(eagerTools, "external_");
33
+ const typeStubs = generateTypeStubs(bindings);
34
+ return `## Code Execution Tool
22
35
 
23
36
  You have access to \`execute_typescript\` which runs TypeScript code in a sandboxed environment.
24
37
 
@@ -36,13 +49,19 @@ For simple operations, prefer calling tools directly.
36
49
 
37
50
  Inside your TypeScript code, you can call these async functions:
38
51
 
39
- ${functionDocs}
52
+ ${Object.entries(bindings).map(([name, binding]) => `- \`${name}(input)\`: ${binding.description}`).join("\n")}
40
53
 
41
54
  ### Type Definitions
42
55
 
43
56
  \`\`\`typescript
44
57
  ${typeStubs}
45
- \`\`\`${discoverableSection}
58
+ \`\`\`${lazyTools.length > 0 ? `
59
+
60
+ ### Discoverable APIs
61
+
62
+ These additional functions are available but not yet documented. Before calling \`external_<name>\` for any of them inside \`execute_typescript\`, call the \`discover_tools\` tool with their names to get full TypeScript signatures:
63
+
64
+ ${lazyTools.map((t) => `- ${renderLazyCatalogEntry(`external_${t.name}`, t.description, include)}`).join("\n")}` : ""}
46
65
 
47
66
  ### Example
48
67
 
@@ -70,7 +89,7 @@ return { warmestCity: warmest.location, temperature: warmest.temperature };
70
89
  - Each execution is independent (no shared state between calls)
71
90
  `;
72
91
  }
73
- export {
74
- createCodeModeSystemPrompt
75
- };
76
- //# sourceMappingURL=create-system-prompt.js.map
92
+ //#endregion
93
+ export { createCodeModeSystemPrompt };
94
+
95
+ //# sourceMappingURL=create-system-prompt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-system-prompt.js","sources":["../../src/create-system-prompt.ts"],"sourcesContent":["import { renderLazyCatalogEntry } from '@tanstack/ai'\nimport { toolsToBindings } from './bindings/tool-to-binding'\nimport { generateTypeStubs } from './type-generator/json-schema-to-ts'\nimport type { CodeModeToolConfig } from './types'\n\n/**\n * Create a system prompt snippet that documents the execute_typescript tool\n * and all available external_* functions.\n *\n * Add this to your system prompts array when using createCodeModeTool.\n *\n * @example\n * ```typescript\n * import { createCodeMode } from '@tanstack/ai-code-mode'\n * import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'\n *\n * const { tool, systemPrompt } = createCodeMode({\n * driver: createNodeIsolateDriver(),\n * tools: [weatherTool, dbTool],\n * })\n *\n * chat({\n * systemPrompts: ['You are a helpful assistant.', systemPrompt],\n * tools: [tool, ...otherTools],\n * })\n * ```\n */\nexport function createCodeModeSystemPrompt(config: CodeModeToolConfig): string {\n const { tools } = config\n const include = config.lazyToolsConfig?.includeDescription ?? 'none'\n\n const eagerTools = tools.filter((t) => !t.lazy)\n const lazyTools = tools.filter((t) => t.lazy)\n\n // Only eager tools get full type stubs + doc lines.\n const bindings = toolsToBindings(eagerTools, 'external_')\n const typeStubs = generateTypeStubs(bindings)\n\n const functionDocs = Object.entries(bindings)\n .map(([name, binding]) => `- \\`${name}(input)\\`: ${binding.description}`)\n .join('\\n')\n\n const discoverableSection =\n lazyTools.length > 0\n ? `\n\n### Discoverable APIs\n\nThese additional functions are available but not yet documented. Before calling \\`external_<name>\\` for any of them inside \\`execute_typescript\\`, call the \\`discover_tools\\` tool with their names to get full TypeScript signatures:\n\n${lazyTools\n .map(\n (t) =>\n `- ${renderLazyCatalogEntry(`external_${t.name}`, t.description, include)}`,\n )\n .join('\\n')}`\n : ''\n\n return `## Code Execution Tool\n\nYou have access to \\`execute_typescript\\` which runs TypeScript code in a sandboxed environment.\n\n### When to Use\n\nUse \\`execute_typescript\\` when you need to:\n- Process data with loops, conditionals, or complex logic\n- Make multiple API calls in parallel (Promise.all)\n- Transform, filter, or aggregate data\n- Perform calculations or data analysis\n\nFor simple operations, prefer calling tools directly.\n\n### Available External APIs\n\nInside your TypeScript code, you can call these async functions:\n\n${functionDocs}\n\n### Type Definitions\n\n\\`\\`\\`typescript\n${typeStubs}\n\\`\\`\\`${discoverableSection}\n\n### Example\n\n\\`\\`\\`typescript\n// Fetch weather for multiple cities in parallel\nconst cities = [\"Tokyo\", \"Paris\", \"NYC\"];\nconst results = await Promise.all(\n cities.map(city => external_fetchWeather({ location: city }))\n);\n\n// Find the warmest city\nconst warmest = results.reduce((prev, curr) =>\n curr.temperature > prev.temperature ? curr : prev\n);\n\nreturn { warmestCity: warmest.location, temperature: warmest.temperature };\n\\`\\`\\`\n\n### Important Notes\n\n- All \\`external_*\\` calls are async - always use \\`await\\`\n- Return a value to pass results back to you\n- Use \\`console.log()\\` for debugging (logs are captured)\n- The sandbox is isolated - no network access or file system\n- Each execution is independent (no shared state between calls)\n`\n}\n"],"names":[],"mappings":";;;AA2BO,SAAS,2BAA2B,QAAoC;AAC7E,QAAM,EAAE,UAAU;AAClB,QAAM,UAAU,OAAO,iBAAiB,sBAAsB;AAE9D,QAAM,aAAa,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI;AAC9C,QAAM,YAAY,MAAM,OAAO,CAAC,MAAM,EAAE,IAAI;AAG5C,QAAM,WAAW,gBAAgB,YAAY,WAAW;AACxD,QAAM,YAAY,kBAAkB,QAAQ;AAE5C,QAAM,eAAe,OAAO,QAAQ,QAAQ,EACzC,IAAI,CAAC,CAAC,MAAM,OAAO,MAAM,OAAO,IAAI,cAAc,QAAQ,WAAW,EAAE,EACvE,KAAK,IAAI;AAEZ,QAAM,sBACJ,UAAU,SAAS,IACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMN,UACC;AAAA,IACC,CAAC,MACC,KAAK,uBAAuB,YAAY,EAAE,IAAI,IAAI,EAAE,aAAa,OAAO,CAAC;AAAA,EAAA,EAE5E,KAAK,IAAI,CAAC,KACL;AAEN,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBP,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAKZ,SAAS;AAAA,QACH,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2B3B;"}
1
+ {"version":3,"file":"create-system-prompt.js","names":[],"sources":["../../src/create-system-prompt.ts"],"sourcesContent":["import { renderLazyCatalogEntry } from '@tanstack/ai'\nimport { toolsToBindings } from './bindings/tool-to-binding'\nimport { generateTypeStubs } from './type-generator/json-schema-to-ts'\nimport type { CodeModeToolConfig } from './types'\n\n/**\n * Create a system prompt snippet that documents the execute_typescript tool\n * and all available external_* functions.\n *\n * Add this to your system prompts array when using createCodeModeTool.\n *\n * @example\n * ```typescript\n * import { createCodeMode } from '@tanstack/ai-code-mode'\n * import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'\n *\n * const { tool, systemPrompt } = createCodeMode({\n * driver: createNodeIsolateDriver(),\n * tools: [weatherTool, dbTool],\n * })\n *\n * chat({\n * systemPrompts: ['You are a helpful assistant.', systemPrompt],\n * tools: [tool, ...otherTools],\n * })\n * ```\n */\nexport function createCodeModeSystemPrompt(config: CodeModeToolConfig): string {\n const { tools } = config\n const include = config.lazyToolsConfig?.includeDescription ?? 'none'\n\n const eagerTools = tools.filter((t) => !t.lazy)\n const lazyTools = tools.filter((t) => t.lazy)\n\n // Only eager tools get full type stubs + doc lines.\n const bindings = toolsToBindings(eagerTools, 'external_')\n const typeStubs = generateTypeStubs(bindings)\n\n const functionDocs = Object.entries(bindings)\n .map(([name, binding]) => `- \\`${name}(input)\\`: ${binding.description}`)\n .join('\\n')\n\n const discoverableSection =\n lazyTools.length > 0\n ? `\n\n### Discoverable APIs\n\nThese additional functions are available but not yet documented. Before calling \\`external_<name>\\` for any of them inside \\`execute_typescript\\`, call the \\`discover_tools\\` tool with their names to get full TypeScript signatures:\n\n${lazyTools\n .map(\n (t) =>\n `- ${renderLazyCatalogEntry(`external_${t.name}`, t.description, include)}`,\n )\n .join('\\n')}`\n : ''\n\n return `## Code Execution Tool\n\nYou have access to \\`execute_typescript\\` which runs TypeScript code in a sandboxed environment.\n\n### When to Use\n\nUse \\`execute_typescript\\` when you need to:\n- Process data with loops, conditionals, or complex logic\n- Make multiple API calls in parallel (Promise.all)\n- Transform, filter, or aggregate data\n- Perform calculations or data analysis\n\nFor simple operations, prefer calling tools directly.\n\n### Available External APIs\n\nInside your TypeScript code, you can call these async functions:\n\n${functionDocs}\n\n### Type Definitions\n\n\\`\\`\\`typescript\n${typeStubs}\n\\`\\`\\`${discoverableSection}\n\n### Example\n\n\\`\\`\\`typescript\n// Fetch weather for multiple cities in parallel\nconst cities = [\"Tokyo\", \"Paris\", \"NYC\"];\nconst results = await Promise.all(\n cities.map(city => external_fetchWeather({ location: city }))\n);\n\n// Find the warmest city\nconst warmest = results.reduce((prev, curr) =>\n curr.temperature > prev.temperature ? curr : prev\n);\n\nreturn { warmestCity: warmest.location, temperature: warmest.temperature };\n\\`\\`\\`\n\n### Important Notes\n\n- All \\`external_*\\` calls are async - always use \\`await\\`\n- Return a value to pass results back to you\n- Use \\`console.log()\\` for debugging (logs are captured)\n- The sandbox is isolated - no network access or file system\n- Each execution is independent (no shared state between calls)\n`\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,2BAA2B,QAAoC;CAC7E,MAAM,EAAE,UAAU;CAClB,MAAM,UAAU,OAAO,iBAAiB,sBAAsB;CAE9D,MAAM,aAAa,MAAM,QAAQ,MAAM,CAAC,EAAE,IAAI;CAC9C,MAAM,YAAY,MAAM,QAAQ,MAAM,EAAE,IAAI;CAG5C,MAAM,WAAW,gBAAgB,YAAY,WAAW;CACxD,MAAM,YAAY,kBAAkB,QAAQ;CAsB5C,OAAO;;;;;;;;;;;;;;;;;;EApBc,OAAO,QAAQ,QAAQ,CAAC,CAC1C,KAAK,CAAC,MAAM,aAAa,OAAO,KAAK,aAAa,QAAQ,aAAa,CAAC,CACxE,KAAK,IAoCR,EAAa;;;;;EAKb,UAAU;QAtCR,UAAU,SAAS,IACf;;;;;;EAMN,UACC,KACE,MACC,KAAK,uBAAuB,YAAY,EAAE,QAAQ,EAAE,aAAa,OAAO,GAC5E,CAAC,CACA,KAAK,IAAI,MACJ,GA0BoB;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2B5B"}
package/dist/esm/index.js CHANGED
@@ -1,25 +1,10 @@
1
+ import { createEventAwareBindings, toolToBinding, toolsToBindings } from "./bindings/tool-to-binding.js";
2
+ import { stripTypeScript } from "./strip-typescript.js";
1
3
  import { createCodeModeTool } from "./create-code-mode-tool.js";
4
+ import { generateTypeStubs, jsonSchemaToTypeScript } from "./type-generator/json-schema-to-ts.js";
2
5
  import { createCodeModeSystemPrompt } from "./create-system-prompt.js";
3
- import { createCodeMode } from "./create-code-mode.js";
4
6
  import { createDiscoveryTool } from "./create-discovery-tool.js";
7
+ import { createCodeMode } from "./create-code-mode.js";
5
8
  import { InMemoryAgentStore, generateAgentName } from "./agent-store.js";
6
- import { createEventAwareBindings, toolToBinding, toolsToBindings } from "./bindings/tool-to-binding.js";
7
- import { generateTypeStubs, jsonSchemaToTypeScript } from "./type-generator/json-schema-to-ts.js";
8
- import { stripTypeScript } from "./strip-typescript.js";
9
9
  import { wrapCode } from "./code-wrapper.js";
10
- export {
11
- InMemoryAgentStore,
12
- createCodeMode,
13
- createCodeModeSystemPrompt,
14
- createCodeModeTool,
15
- createDiscoveryTool,
16
- createEventAwareBindings,
17
- generateAgentName,
18
- generateTypeStubs,
19
- jsonSchemaToTypeScript,
20
- stripTypeScript,
21
- toolToBinding,
22
- toolsToBindings,
23
- wrapCode
24
- };
25
- //# sourceMappingURL=index.js.map
10
+ export { InMemoryAgentStore, createCodeMode, createCodeModeSystemPrompt, createCodeModeTool, createDiscoveryTool, createEventAwareBindings, generateAgentName, generateTypeStubs, jsonSchemaToTypeScript, stripTypeScript, toolToBinding, toolsToBindings, wrapCode };
@@ -1,49 +1,67 @@
1
1
  import { transform } from "sucrase";
2
- const WRAPPER_START = "___TANSTACK_WRAPPER_START___";
3
- const WRAPPER_END = "___TANSTACK_WRAPPER_END___";
2
+ //#region src/strip-typescript.ts
3
+ var WRAPPER_START = "___TANSTACK_WRAPPER_START___";
4
+ var WRAPPER_END = "___TANSTACK_WRAPPER_END___";
5
+ /**
6
+ * Strip TypeScript syntax from code, converting it to plain JavaScript.
7
+ *
8
+ * This is a safety net to ensure that even if an LLM generates TypeScript
9
+ * code with type annotations, it will be converted to valid JavaScript
10
+ * before being sent to the sandbox for execution.
11
+ *
12
+ * Uses sucrase's pure-JavaScript `transform`, which strips the TypeScript
13
+ * syntax that LLM-generated snippets use in practice:
14
+ * - Type annotations (: string, : number, etc.)
15
+ * - Generic types (Array<T>, Record<K, V>, etc.)
16
+ * - Interface and type declarations
17
+ * - Type assertions
18
+ * - Enums (converted to JavaScript objects)
19
+ *
20
+ * Unlike esbuild, sucrase has no native binary and pulls in no Node-only
21
+ * built-ins on its `transform` path, so this module is safe to bundle for
22
+ * browsers and edge runtimes (Cloudflare Workers/Pages etc.).
23
+ *
24
+ * Limitations vs esbuild: sucrase is a type-stripper, not a down-leveler.
25
+ * `disableESTransforms` leaves modern ECMAScript syntax untouched (the sandbox
26
+ * engines are modern), and sucrase does NOT compile a few exotic constructs:
27
+ * - TypeScript value `namespace`/`module` blocks are DROPPED (not emitted as an
28
+ * IIFE), so referencing the namespace at runtime throws `ReferenceError`.
29
+ * - Decorators and the `accessor` keyword pass through un-lowered, so the
30
+ * sandbox sees invalid syntax.
31
+ * - Post-ES2022 syntax (`using` declarations, RegExp `/v`·`/d` flags) is passed
32
+ * through; it runs on modern V8/Node sandboxes but may fail on older engines
33
+ * (e.g. QuickJS).
34
+ * If you need any of these, supply a heavier (Node-only) transpiler via the
35
+ * `transpile` option on `createCodeModeTool`.
36
+ *
37
+ * The code is wrapped in an async function before transformation to allow
38
+ * top-level `return` and `await` statements, then unwrapped after.
39
+ *
40
+ * Note on errors: sucrase reports syntax errors with a position relative to the
41
+ * *wrapped* code (offset by the one-line wrapper prefix), so any line numbers
42
+ * surfaced downstream (e.g. `CodeModeToolResult.error.line`) are approximate.
43
+ *
44
+ * @param code - TypeScript or JavaScript code
45
+ * @returns Plain JavaScript code with all type syntax removed
46
+ * @throws Error if sucrase fails (e.g., syntax error) or wrapper extraction fails
47
+ */
4
48
  async function stripTypeScript(code) {
5
- const wrappedCode = `async function ${WRAPPER_START}() {
6
- ${code}
7
- }; ${WRAPPER_END}`;
8
- const result = transform(wrappedCode, {
9
- // Only strip/lower TypeScript-specific syntax...
10
- transforms: ["typescript"],
11
- // ...and leave modern ECMAScript syntax untouched for the sandbox engines.
12
- disableESTransforms: true
13
- });
14
- const transformed = result.code;
15
- const functionStart = transformed.indexOf(`async function ${WRAPPER_START}()`);
16
- if (functionStart === -1) {
17
- throw new Error(
18
- "[stripTypeScript] Could not find wrapper function start in transformed output"
19
- );
20
- }
21
- const openBrace = transformed.indexOf("{", functionStart);
22
- if (openBrace === -1) {
23
- throw new Error(
24
- "[stripTypeScript] Could not find opening brace in transformed output"
25
- );
26
- }
27
- const endMarkerIndex = transformed.indexOf(WRAPPER_END);
28
- if (endMarkerIndex === -1) {
29
- throw new Error(
30
- "[stripTypeScript] Could not find end marker in transformed output"
31
- );
32
- }
33
- const codeBeforeEndMarker = transformed.substring(
34
- openBrace + 1,
35
- endMarkerIndex
36
- );
37
- const closingBraceIndex = codeBeforeEndMarker.lastIndexOf("}");
38
- if (closingBraceIndex === -1) {
39
- throw new Error(
40
- "[stripTypeScript] Could not find closing brace in transformed output"
41
- );
42
- }
43
- const functionBody = codeBeforeEndMarker.substring(0, closingBraceIndex).trim();
44
- return functionBody;
49
+ const transformed = transform(`async function ${WRAPPER_START}() {\n${code}\n}; ${WRAPPER_END}`, {
50
+ transforms: ["typescript"],
51
+ disableESTransforms: true
52
+ }).code;
53
+ const functionStart = transformed.indexOf(`async function ${WRAPPER_START}()`);
54
+ if (functionStart === -1) throw new Error("[stripTypeScript] Could not find wrapper function start in transformed output");
55
+ const openBrace = transformed.indexOf("{", functionStart);
56
+ if (openBrace === -1) throw new Error("[stripTypeScript] Could not find opening brace in transformed output");
57
+ const endMarkerIndex = transformed.indexOf(WRAPPER_END);
58
+ if (endMarkerIndex === -1) throw new Error("[stripTypeScript] Could not find end marker in transformed output");
59
+ const codeBeforeEndMarker = transformed.substring(openBrace + 1, endMarkerIndex);
60
+ const closingBraceIndex = codeBeforeEndMarker.lastIndexOf("}");
61
+ if (closingBraceIndex === -1) throw new Error("[stripTypeScript] Could not find closing brace in transformed output");
62
+ return codeBeforeEndMarker.substring(0, closingBraceIndex).trim();
45
63
  }
46
- export {
47
- stripTypeScript
48
- };
49
- //# sourceMappingURL=strip-typescript.js.map
64
+ //#endregion
65
+ export { stripTypeScript };
66
+
67
+ //# sourceMappingURL=strip-typescript.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"strip-typescript.js","sources":["../../src/strip-typescript.ts"],"sourcesContent":["import { transform } from 'sucrase'\n\n// Unique markers for wrapping/unwrapping code\nconst WRAPPER_START = '___TANSTACK_WRAPPER_START___'\nconst WRAPPER_END = '___TANSTACK_WRAPPER_END___'\n\n/**\n * Strip TypeScript syntax from code, converting it to plain JavaScript.\n *\n * This is a safety net to ensure that even if an LLM generates TypeScript\n * code with type annotations, it will be converted to valid JavaScript\n * before being sent to the sandbox for execution.\n *\n * Uses sucrase's pure-JavaScript `transform`, which strips the TypeScript\n * syntax that LLM-generated snippets use in practice:\n * - Type annotations (: string, : number, etc.)\n * - Generic types (Array<T>, Record<K, V>, etc.)\n * - Interface and type declarations\n * - Type assertions\n * - Enums (converted to JavaScript objects)\n *\n * Unlike esbuild, sucrase has no native binary and pulls in no Node-only\n * built-ins on its `transform` path, so this module is safe to bundle for\n * browsers and edge runtimes (Cloudflare Workers/Pages etc.).\n *\n * Limitations vs esbuild: sucrase is a type-stripper, not a down-leveler.\n * `disableESTransforms` leaves modern ECMAScript syntax untouched (the sandbox\n * engines are modern), and sucrase does NOT compile a few exotic constructs:\n * - TypeScript value `namespace`/`module` blocks are DROPPED (not emitted as an\n * IIFE), so referencing the namespace at runtime throws `ReferenceError`.\n * - Decorators and the `accessor` keyword pass through un-lowered, so the\n * sandbox sees invalid syntax.\n * - Post-ES2022 syntax (`using` declarations, RegExp `/v`·`/d` flags) is passed\n * through; it runs on modern V8/Node sandboxes but may fail on older engines\n * (e.g. QuickJS).\n * If you need any of these, supply a heavier (Node-only) transpiler via the\n * `transpile` option on `createCodeModeTool`.\n *\n * The code is wrapped in an async function before transformation to allow\n * top-level `return` and `await` statements, then unwrapped after.\n *\n * Note on errors: sucrase reports syntax errors with a position relative to the\n * *wrapped* code (offset by the one-line wrapper prefix), so any line numbers\n * surfaced downstream (e.g. `CodeModeToolResult.error.line`) are approximate.\n *\n * @param code - TypeScript or JavaScript code\n * @returns Plain JavaScript code with all type syntax removed\n * @throws Error if sucrase fails (e.g., syntax error) or wrapper extraction fails\n */\n// sucrase's transform is synchronous, but we keep the published Promise-returning\n// signature so existing `await stripTypeScript(...)` callers (and a custom async\n// `transpile` hook) stay source-compatible across this swap.\n// eslint-disable-next-line @typescript-eslint/require-await\nexport async function stripTypeScript(code: string): Promise<string> {\n // Wrap the code in an async function to allow top-level return/await.\n // This is necessary because top-level `return` is invalid outside a function.\n const wrappedCode = `async function ${WRAPPER_START}() {\\n${code}\\n}; ${WRAPPER_END}`\n\n const result = transform(wrappedCode, {\n // Only strip/lower TypeScript-specific syntax...\n transforms: ['typescript'],\n // ...and leave modern ECMAScript syntax untouched for the sandbox engines.\n disableESTransforms: true,\n })\n\n // Extract the code from inside the wrapper function\n const transformed = result.code\n\n // Find the function declaration start\n const functionStart = transformed.indexOf(`async function ${WRAPPER_START}()`)\n if (functionStart === -1) {\n throw new Error(\n '[stripTypeScript] Could not find wrapper function start in transformed output',\n )\n }\n\n // Find the opening brace of the function\n const openBrace = transformed.indexOf('{', functionStart)\n if (openBrace === -1) {\n throw new Error(\n '[stripTypeScript] Could not find opening brace in transformed output',\n )\n }\n\n // Find the end marker (regardless of formatting)\n const endMarkerIndex = transformed.indexOf(WRAPPER_END)\n if (endMarkerIndex === -1) {\n throw new Error(\n '[stripTypeScript] Could not find end marker in transformed output',\n )\n }\n\n // Find the closing brace of the function (last } before the end marker)\n // We need to find the } that matches the function opening\n const codeBeforeEndMarker = transformed.substring(\n openBrace + 1,\n endMarkerIndex,\n )\n\n // Find the last } before the end marker, accounting for the semicolon\n // The code will be: ...function body...}; WRAPPER_END or ...};\\nWRAPPER_END\n const closingBraceIndex = codeBeforeEndMarker.lastIndexOf('}')\n\n if (closingBraceIndex === -1) {\n throw new Error(\n '[stripTypeScript] Could not find closing brace in transformed output',\n )\n }\n\n // Extract the function body (between { and })\n const functionBody = codeBeforeEndMarker\n .substring(0, closingBraceIndex)\n .trim()\n\n return functionBody\n}\n"],"names":[],"mappings":";AAGA,MAAM,gBAAgB;AACtB,MAAM,cAAc;AAiDpB,eAAsB,gBAAgB,MAA+B;AAGnE,QAAM,cAAc,kBAAkB,aAAa;AAAA,EAAS,IAAI;AAAA,KAAQ,WAAW;AAEnF,QAAM,SAAS,UAAU,aAAa;AAAA;AAAA,IAEpC,YAAY,CAAC,YAAY;AAAA;AAAA,IAEzB,qBAAqB;AAAA,EAAA,CACtB;AAGD,QAAM,cAAc,OAAO;AAG3B,QAAM,gBAAgB,YAAY,QAAQ,kBAAkB,aAAa,IAAI;AAC7E,MAAI,kBAAkB,IAAI;AACxB,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAGA,QAAM,YAAY,YAAY,QAAQ,KAAK,aAAa;AACxD,MAAI,cAAc,IAAI;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAGA,QAAM,iBAAiB,YAAY,QAAQ,WAAW;AACtD,MAAI,mBAAmB,IAAI;AACzB,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAIA,QAAM,sBAAsB,YAAY;AAAA,IACtC,YAAY;AAAA,IACZ;AAAA,EAAA;AAKF,QAAM,oBAAoB,oBAAoB,YAAY,GAAG;AAE7D,MAAI,sBAAsB,IAAI;AAC5B,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAGA,QAAM,eAAe,oBAClB,UAAU,GAAG,iBAAiB,EAC9B,KAAA;AAEH,SAAO;AACT;"}
1
+ {"version":3,"file":"strip-typescript.js","names":[],"sources":["../../src/strip-typescript.ts"],"sourcesContent":["import { transform } from 'sucrase'\n\n// Unique markers for wrapping/unwrapping code\nconst WRAPPER_START = '___TANSTACK_WRAPPER_START___'\nconst WRAPPER_END = '___TANSTACK_WRAPPER_END___'\n\n/**\n * Strip TypeScript syntax from code, converting it to plain JavaScript.\n *\n * This is a safety net to ensure that even if an LLM generates TypeScript\n * code with type annotations, it will be converted to valid JavaScript\n * before being sent to the sandbox for execution.\n *\n * Uses sucrase's pure-JavaScript `transform`, which strips the TypeScript\n * syntax that LLM-generated snippets use in practice:\n * - Type annotations (: string, : number, etc.)\n * - Generic types (Array<T>, Record<K, V>, etc.)\n * - Interface and type declarations\n * - Type assertions\n * - Enums (converted to JavaScript objects)\n *\n * Unlike esbuild, sucrase has no native binary and pulls in no Node-only\n * built-ins on its `transform` path, so this module is safe to bundle for\n * browsers and edge runtimes (Cloudflare Workers/Pages etc.).\n *\n * Limitations vs esbuild: sucrase is a type-stripper, not a down-leveler.\n * `disableESTransforms` leaves modern ECMAScript syntax untouched (the sandbox\n * engines are modern), and sucrase does NOT compile a few exotic constructs:\n * - TypeScript value `namespace`/`module` blocks are DROPPED (not emitted as an\n * IIFE), so referencing the namespace at runtime throws `ReferenceError`.\n * - Decorators and the `accessor` keyword pass through un-lowered, so the\n * sandbox sees invalid syntax.\n * - Post-ES2022 syntax (`using` declarations, RegExp `/v`·`/d` flags) is passed\n * through; it runs on modern V8/Node sandboxes but may fail on older engines\n * (e.g. QuickJS).\n * If you need any of these, supply a heavier (Node-only) transpiler via the\n * `transpile` option on `createCodeModeTool`.\n *\n * The code is wrapped in an async function before transformation to allow\n * top-level `return` and `await` statements, then unwrapped after.\n *\n * Note on errors: sucrase reports syntax errors with a position relative to the\n * *wrapped* code (offset by the one-line wrapper prefix), so any line numbers\n * surfaced downstream (e.g. `CodeModeToolResult.error.line`) are approximate.\n *\n * @param code - TypeScript or JavaScript code\n * @returns Plain JavaScript code with all type syntax removed\n * @throws Error if sucrase fails (e.g., syntax error) or wrapper extraction fails\n */\n// sucrase's transform is synchronous, but we keep the published Promise-returning\n// signature so existing `await stripTypeScript(...)` callers (and a custom async\n// `transpile` hook) stay source-compatible across this swap.\n// eslint-disable-next-line @typescript-eslint/require-await\nexport async function stripTypeScript(code: string): Promise<string> {\n // Wrap the code in an async function to allow top-level return/await.\n // This is necessary because top-level `return` is invalid outside a function.\n const wrappedCode = `async function ${WRAPPER_START}() {\\n${code}\\n}; ${WRAPPER_END}`\n\n const result = transform(wrappedCode, {\n // Only strip/lower TypeScript-specific syntax...\n transforms: ['typescript'],\n // ...and leave modern ECMAScript syntax untouched for the sandbox engines.\n disableESTransforms: true,\n })\n\n // Extract the code from inside the wrapper function\n const transformed = result.code\n\n // Find the function declaration start\n const functionStart = transformed.indexOf(`async function ${WRAPPER_START}()`)\n if (functionStart === -1) {\n throw new Error(\n '[stripTypeScript] Could not find wrapper function start in transformed output',\n )\n }\n\n // Find the opening brace of the function\n const openBrace = transformed.indexOf('{', functionStart)\n if (openBrace === -1) {\n throw new Error(\n '[stripTypeScript] Could not find opening brace in transformed output',\n )\n }\n\n // Find the end marker (regardless of formatting)\n const endMarkerIndex = transformed.indexOf(WRAPPER_END)\n if (endMarkerIndex === -1) {\n throw new Error(\n '[stripTypeScript] Could not find end marker in transformed output',\n )\n }\n\n // Find the closing brace of the function (last } before the end marker)\n // We need to find the } that matches the function opening\n const codeBeforeEndMarker = transformed.substring(\n openBrace + 1,\n endMarkerIndex,\n )\n\n // Find the last } before the end marker, accounting for the semicolon\n // The code will be: ...function body...}; WRAPPER_END or ...};\\nWRAPPER_END\n const closingBraceIndex = codeBeforeEndMarker.lastIndexOf('}')\n\n if (closingBraceIndex === -1) {\n throw new Error(\n '[stripTypeScript] Could not find closing brace in transformed output',\n )\n }\n\n // Extract the function body (between { and })\n const functionBody = codeBeforeEndMarker\n .substring(0, closingBraceIndex)\n .trim()\n\n return functionBody\n}\n"],"mappings":";;AAGA,IAAM,gBAAgB;AACtB,IAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDpB,eAAsB,gBAAgB,MAA+B;CAanE,MAAM,cARS,UAAU,kBAFa,cAAc,QAAQ,KAAK,OAAO,eAElC;EAEpC,YAAY,CAAC,YAAY;EAEzB,qBAAqB;CACvB,CAGoB,CAAA,CAAO;CAG3B,MAAM,gBAAgB,YAAY,QAAQ,kBAAkB,cAAc,GAAG;CAC7E,IAAI,kBAAkB,IACpB,MAAM,IAAI,MACR,+EACF;CAIF,MAAM,YAAY,YAAY,QAAQ,KAAK,aAAa;CACxD,IAAI,cAAc,IAChB,MAAM,IAAI,MACR,sEACF;CAIF,MAAM,iBAAiB,YAAY,QAAQ,WAAW;CACtD,IAAI,mBAAmB,IACrB,MAAM,IAAI,MACR,mEACF;CAKF,MAAM,sBAAsB,YAAY,UACtC,YAAY,GACZ,cACF;CAIA,MAAM,oBAAoB,oBAAoB,YAAY,GAAG;CAE7D,IAAI,sBAAsB,IACxB,MAAM,IAAI,MACR,sEACF;CAQF,OAJqB,oBAClB,UAAU,GAAG,iBAAiB,CAAC,CAC/B,KAEI;AACT"}
@@ -1,100 +1,90 @@
1
+ //#region src/type-generator/json-schema-to-ts.ts
2
+ /**
3
+ * Generate TypeScript type stubs for all tool bindings
4
+ *
5
+ * These stubs are included in the LLM system prompt so it knows
6
+ * the exact type signatures of available tools.
7
+ *
8
+ * Tool names match the actual function names injected into the sandbox.
9
+ */
1
10
  function generateTypeStubs(bindings, options = {}) {
2
- const { includeDescriptions = true } = options;
3
- const declarations = [];
4
- for (const [name, binding] of Object.entries(bindings)) {
5
- const inputTypeName = `${capitalize(name)}Input`;
6
- const outputTypeName = `${capitalize(name)}Output`;
7
- const inputType = jsonSchemaToTypeScript(binding.inputSchema, inputTypeName);
8
- if (inputType.declaration) {
9
- declarations.push(inputType.declaration);
10
- }
11
- let outputTypeRef = "unknown";
12
- if (binding.outputSchema) {
13
- const outputType = jsonSchemaToTypeScript(
14
- binding.outputSchema,
15
- outputTypeName
16
- );
17
- if (outputType.declaration) {
18
- declarations.push(outputType.declaration);
19
- }
20
- outputTypeRef = outputType.name;
21
- }
22
- const description = includeDescriptions && binding.description ? `/** ${binding.description} */
23
- ` : "";
24
- declarations.push(
25
- `${description}declare function ${name}(input: ${inputType.name}): Promise<${outputTypeRef}>;`
26
- );
27
- }
28
- return declarations.join("\n\n");
11
+ const { includeDescriptions = true } = options;
12
+ const declarations = [];
13
+ for (const [name, binding] of Object.entries(bindings)) {
14
+ const inputTypeName = `${capitalize(name)}Input`;
15
+ const outputTypeName = `${capitalize(name)}Output`;
16
+ const inputType = jsonSchemaToTypeScript(binding.inputSchema, inputTypeName);
17
+ if (inputType.declaration) declarations.push(inputType.declaration);
18
+ let outputTypeRef = "unknown";
19
+ if (binding.outputSchema) {
20
+ const outputType = jsonSchemaToTypeScript(binding.outputSchema, outputTypeName);
21
+ if (outputType.declaration) declarations.push(outputType.declaration);
22
+ outputTypeRef = outputType.name;
23
+ }
24
+ const description = includeDescriptions && binding.description ? `/** ${binding.description} */\n` : "";
25
+ declarations.push(`${description}declare function ${name}(input: ${inputType.name}): Promise<${outputTypeRef}>;`);
26
+ }
27
+ return declarations.join("\n\n");
29
28
  }
29
+ /**
30
+ * Convert a JSON Schema to a TypeScript type
31
+ *
32
+ * Supports basic types: string, number, boolean, object, array
33
+ */
30
34
  function jsonSchemaToTypeScript(schema, typeName) {
31
- const type = schemaToType(schema);
32
- if (schema.type === "object" && schema.properties && Object.keys(schema.properties).length > 0) {
33
- return {
34
- name: typeName,
35
- declaration: `interface ${typeName} ${type}`
36
- };
37
- }
38
- return {
39
- name: type,
40
- declaration: ""
41
- };
35
+ const type = schemaToType(schema);
36
+ if (schema.type === "object" && schema.properties && Object.keys(schema.properties).length > 0) return {
37
+ name: typeName,
38
+ declaration: `interface ${typeName} ${type}`
39
+ };
40
+ return {
41
+ name: type,
42
+ declaration: ""
43
+ };
42
44
  }
45
+ /**
46
+ * Convert a JSON Schema to a TypeScript type string
47
+ */
43
48
  function schemaToType(schema) {
44
- if (typeof schema !== "object") {
45
- return "unknown";
46
- }
47
- const schemaType = schema.type;
48
- if (schemaType === "string") return "string";
49
- if (schemaType === "number" || schemaType === "integer") return "number";
50
- if (schemaType === "boolean") return "boolean";
51
- if (schemaType === "null") return "null";
52
- if (schemaType === "array") {
53
- const items = schema.items;
54
- const itemType = items ? schemaToType(items) : "unknown";
55
- return `Array<${itemType}>`;
56
- }
57
- if (schemaType === "object" && schema.properties) {
58
- const properties = schema.properties;
59
- const required = new Set(
60
- schema.required ?? []
61
- );
62
- const props = Object.entries(properties).map(([key, propSchema]) => {
63
- const optional = required.has(key) ? "" : "?";
64
- const propType = schemaToType(propSchema);
65
- const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `"${key}"`;
66
- return ` ${safeName}${optional}: ${propType};`;
67
- }).join("\n");
68
- return `{
69
- ${props}
70
- }`;
71
- }
72
- if (schema.enum) {
73
- const enumValues = schema.enum;
74
- return enumValues.map((v) => JSON.stringify(v)).join(" | ");
75
- }
76
- if (schema.anyOf || schema.oneOf) {
77
- const variants = schema.anyOf || schema.oneOf;
78
- return variants.map((v) => schemaToType(v)).join(" | ");
79
- }
80
- if (Array.isArray(schemaType)) {
81
- return schemaType.map((t) => {
82
- if (t === "string") return "string";
83
- if (t === "number" || t === "integer") return "number";
84
- if (t === "boolean") return "boolean";
85
- if (t === "null") return "null";
86
- if (t === "array") return "Array<unknown>";
87
- if (t === "object") return "object";
88
- return "unknown";
89
- }).join(" | ");
90
- }
91
- return "unknown";
49
+ if (typeof schema !== "object") return "unknown";
50
+ const schemaType = schema.type;
51
+ if (schemaType === "string") return "string";
52
+ if (schemaType === "number" || schemaType === "integer") return "number";
53
+ if (schemaType === "boolean") return "boolean";
54
+ if (schemaType === "null") return "null";
55
+ if (schemaType === "array") {
56
+ const items = schema.items;
57
+ return `Array<${items ? schemaToType(items) : "unknown"}>`;
58
+ }
59
+ if (schemaType === "object" && schema.properties) {
60
+ const properties = schema.properties;
61
+ const required = new Set(schema.required ?? []);
62
+ return `{\n${Object.entries(properties).map(([key, propSchema]) => {
63
+ const optional = required.has(key) ? "" : "?";
64
+ const propType = schemaToType(propSchema);
65
+ return ` ${/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `"${key}"`}${optional}: ${propType};`;
66
+ }).join("\n")}\n}`;
67
+ }
68
+ if (schema.enum) return schema.enum.map((v) => JSON.stringify(v)).join(" | ");
69
+ if (schema.anyOf || schema.oneOf) return (schema.anyOf || schema.oneOf).map((v) => schemaToType(v)).join(" | ");
70
+ if (Array.isArray(schemaType)) return schemaType.map((t) => {
71
+ if (t === "string") return "string";
72
+ if (t === "number" || t === "integer") return "number";
73
+ if (t === "boolean") return "boolean";
74
+ if (t === "null") return "null";
75
+ if (t === "array") return "Array<unknown>";
76
+ if (t === "object") return "object";
77
+ return "unknown";
78
+ }).join(" | ");
79
+ return "unknown";
92
80
  }
81
+ /**
82
+ * Capitalize the first letter of a string
83
+ */
93
84
  function capitalize(str) {
94
- return str.charAt(0).toUpperCase() + str.slice(1);
85
+ return str.charAt(0).toUpperCase() + str.slice(1);
95
86
  }
96
- export {
97
- generateTypeStubs,
98
- jsonSchemaToTypeScript
99
- };
100
- //# sourceMappingURL=json-schema-to-ts.js.map
87
+ //#endregion
88
+ export { generateTypeStubs, jsonSchemaToTypeScript };
89
+
90
+ //# sourceMappingURL=json-schema-to-ts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema-to-ts.js","sources":["../../../src/type-generator/json-schema-to-ts.ts"],"sourcesContent":["import type { ToolBinding } from '../types'\n\n/**\n * Options for type stub generation\n */\nexport interface TypeGeneratorOptions {\n /**\n * Include JSDoc comments with descriptions\n * @default true\n */\n includeDescriptions?: boolean\n}\n\n/**\n * Generate TypeScript type stubs for all tool bindings\n *\n * These stubs are included in the LLM system prompt so it knows\n * the exact type signatures of available tools.\n *\n * Tool names match the actual function names injected into the sandbox.\n */\nexport function generateTypeStubs(\n bindings: Record<string, ToolBinding>,\n options: TypeGeneratorOptions = {},\n): string {\n const { includeDescriptions = true } = options\n\n const declarations: Array<string> = []\n\n for (const [name, binding] of Object.entries(bindings)) {\n const inputTypeName = `${capitalize(name)}Input`\n const outputTypeName = `${capitalize(name)}Output`\n\n // Generate input type\n const inputType = jsonSchemaToTypeScript(binding.inputSchema, inputTypeName)\n if (inputType.declaration) {\n declarations.push(inputType.declaration)\n }\n\n // Generate output type if present\n let outputTypeRef = 'unknown'\n if (binding.outputSchema) {\n const outputType = jsonSchemaToTypeScript(\n binding.outputSchema,\n outputTypeName,\n )\n if (outputType.declaration) {\n declarations.push(outputType.declaration)\n }\n outputTypeRef = outputType.name\n }\n\n // Generate function declaration matching the actual sandbox function name\n const description =\n includeDescriptions && binding.description\n ? `/** ${binding.description} */\\n`\n : ''\n\n declarations.push(\n `${description}declare function ${name}(input: ${inputType.name}): Promise<${outputTypeRef}>;`,\n )\n }\n\n return declarations.join('\\n\\n')\n}\n\ninterface TypeResult {\n name: string\n declaration: string\n}\n\n/**\n * Convert a JSON Schema to a TypeScript type\n *\n * Supports basic types: string, number, boolean, object, array\n */\nexport function jsonSchemaToTypeScript(\n schema: Record<string, unknown>,\n typeName: string,\n): TypeResult {\n const type = schemaToType(schema)\n\n // For object schemas with properties, create a named interface\n if (\n schema.type === 'object' &&\n schema.properties &&\n Object.keys(schema.properties).length > 0\n ) {\n return {\n name: typeName,\n declaration: `interface ${typeName} ${type}`,\n }\n }\n\n // For simple types or empty objects, create a type alias\n return {\n name: type,\n declaration: '',\n }\n}\n\n/**\n * Convert a JSON Schema to a TypeScript type string\n */\nfunction schemaToType(schema: Record<string, unknown>): string {\n if (typeof schema !== 'object') {\n return 'unknown'\n }\n\n const schemaType = schema.type\n\n // Handle basic types\n if (schemaType === 'string') return 'string'\n if (schemaType === 'number' || schemaType === 'integer') return 'number'\n if (schemaType === 'boolean') return 'boolean'\n if (schemaType === 'null') return 'null'\n\n // Handle arrays\n if (schemaType === 'array') {\n const items = schema.items as Record<string, unknown> | undefined\n const itemType = items ? schemaToType(items) : 'unknown'\n return `Array<${itemType}>`\n }\n\n // Handle objects with properties\n if (schemaType === 'object' && schema.properties) {\n const properties = schema.properties as Record<\n string,\n Record<string, unknown>\n >\n const required = new Set(\n (schema.required as Array<string> | undefined) ?? [],\n )\n\n const props = Object.entries(properties)\n .map(([key, propSchema]) => {\n const optional = required.has(key) ? '' : '?'\n const propType = schemaToType(propSchema)\n // Handle property names that need quoting\n const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key)\n ? key\n : `\"${key}\"`\n return ` ${safeName}${optional}: ${propType};`\n })\n .join('\\n')\n\n return `{\\n${props}\\n}`\n }\n\n // Handle enums\n if (schema.enum) {\n const enumValues = schema.enum as Array<unknown>\n return enumValues.map((v) => JSON.stringify(v)).join(' | ')\n }\n\n // Handle union types (anyOf, oneOf)\n if (schema.anyOf || schema.oneOf) {\n const variants = (schema.anyOf || schema.oneOf) as Array<\n Record<string, unknown>\n >\n return variants.map((v) => schemaToType(v)).join(' | ')\n }\n\n // Handle type arrays (e.g., [\"string\", \"null\"])\n if (Array.isArray(schemaType)) {\n return schemaType\n .map((t) => {\n if (t === 'string') return 'string'\n if (t === 'number' || t === 'integer') return 'number'\n if (t === 'boolean') return 'boolean'\n if (t === 'null') return 'null'\n if (t === 'array') return 'Array<unknown>'\n if (t === 'object') return 'object'\n return 'unknown'\n })\n .join(' | ')\n }\n\n // Fallback for unknown schemas\n return 'unknown'\n}\n\n/**\n * Capitalize the first letter of a string\n */\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1)\n}\n"],"names":[],"mappings":"AAqBO,SAAS,kBACd,UACA,UAAgC,IACxB;AACR,QAAM,EAAE,sBAAsB,KAAA,IAAS;AAEvC,QAAM,eAA8B,CAAA;AAEpC,aAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACtD,UAAM,gBAAgB,GAAG,WAAW,IAAI,CAAC;AACzC,UAAM,iBAAiB,GAAG,WAAW,IAAI,CAAC;AAG1C,UAAM,YAAY,uBAAuB,QAAQ,aAAa,aAAa;AAC3E,QAAI,UAAU,aAAa;AACzB,mBAAa,KAAK,UAAU,WAAW;AAAA,IACzC;AAGA,QAAI,gBAAgB;AACpB,QAAI,QAAQ,cAAc;AACxB,YAAM,aAAa;AAAA,QACjB,QAAQ;AAAA,QACR;AAAA,MAAA;AAEF,UAAI,WAAW,aAAa;AAC1B,qBAAa,KAAK,WAAW,WAAW;AAAA,MAC1C;AACA,sBAAgB,WAAW;AAAA,IAC7B;AAGA,UAAM,cACJ,uBAAuB,QAAQ,cAC3B,OAAO,QAAQ,WAAW;AAAA,IAC1B;AAEN,iBAAa;AAAA,MACX,GAAG,WAAW,oBAAoB,IAAI,WAAW,UAAU,IAAI,cAAc,aAAa;AAAA,IAAA;AAAA,EAE9F;AAEA,SAAO,aAAa,KAAK,MAAM;AACjC;AAYO,SAAS,uBACd,QACA,UACY;AACZ,QAAM,OAAO,aAAa,MAAM;AAGhC,MACE,OAAO,SAAS,YAChB,OAAO,cACP,OAAO,KAAK,OAAO,UAAU,EAAE,SAAS,GACxC;AACA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,aAAa,aAAa,QAAQ,IAAI,IAAI;AAAA,IAAA;AAAA,EAE9C;AAGA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,EAAA;AAEjB;AAKA,SAAS,aAAa,QAAyC;AAC7D,MAAI,OAAO,WAAW,UAAU;AAC9B,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,OAAO;AAG1B,MAAI,eAAe,SAAU,QAAO;AACpC,MAAI,eAAe,YAAY,eAAe,UAAW,QAAO;AAChE,MAAI,eAAe,UAAW,QAAO;AACrC,MAAI,eAAe,OAAQ,QAAO;AAGlC,MAAI,eAAe,SAAS;AAC1B,UAAM,QAAQ,OAAO;AACrB,UAAM,WAAW,QAAQ,aAAa,KAAK,IAAI;AAC/C,WAAO,SAAS,QAAQ;AAAA,EAC1B;AAGA,MAAI,eAAe,YAAY,OAAO,YAAY;AAChD,UAAM,aAAa,OAAO;AAI1B,UAAM,WAAW,IAAI;AAAA,MAClB,OAAO,YAA0C,CAAA;AAAA,IAAC;AAGrD,UAAM,QAAQ,OAAO,QAAQ,UAAU,EACpC,IAAI,CAAC,CAAC,KAAK,UAAU,MAAM;AAC1B,YAAM,WAAW,SAAS,IAAI,GAAG,IAAI,KAAK;AAC1C,YAAM,WAAW,aAAa,UAAU;AAExC,YAAM,WAAW,6BAA6B,KAAK,GAAG,IAClD,MACA,IAAI,GAAG;AACX,aAAO,KAAK,QAAQ,GAAG,QAAQ,KAAK,QAAQ;AAAA,IAC9C,CAAC,EACA,KAAK,IAAI;AAEZ,WAAO;AAAA,EAAM,KAAK;AAAA;AAAA,EACpB;AAGA,MAAI,OAAO,MAAM;AACf,UAAM,aAAa,OAAO;AAC1B,WAAO,WAAW,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,KAAK;AAAA,EAC5D;AAGA,MAAI,OAAO,SAAS,OAAO,OAAO;AAChC,UAAM,WAAY,OAAO,SAAS,OAAO;AAGzC,WAAO,SAAS,IAAI,CAAC,MAAM,aAAa,CAAC,CAAC,EAAE,KAAK,KAAK;AAAA,EACxD;AAGA,MAAI,MAAM,QAAQ,UAAU,GAAG;AAC7B,WAAO,WACJ,IAAI,CAAC,MAAM;AACV,UAAI,MAAM,SAAU,QAAO;AAC3B,UAAI,MAAM,YAAY,MAAM,UAAW,QAAO;AAC9C,UAAI,MAAM,UAAW,QAAO;AAC5B,UAAI,MAAM,OAAQ,QAAO;AACzB,UAAI,MAAM,QAAS,QAAO;AAC1B,UAAI,MAAM,SAAU,QAAO;AAC3B,aAAO;AAAA,IACT,CAAC,EACA,KAAK,KAAK;AAAA,EACf;AAGA,SAAO;AACT;AAKA,SAAS,WAAW,KAAqB;AACvC,SAAO,IAAI,OAAO,CAAC,EAAE,gBAAgB,IAAI,MAAM,CAAC;AAClD;"}
1
+ {"version":3,"file":"json-schema-to-ts.js","names":[],"sources":["../../../src/type-generator/json-schema-to-ts.ts"],"sourcesContent":["import type { ToolBinding } from '../types'\n\n/**\n * Options for type stub generation\n */\nexport interface TypeGeneratorOptions {\n /**\n * Include JSDoc comments with descriptions\n * @default true\n */\n includeDescriptions?: boolean\n}\n\n/**\n * Generate TypeScript type stubs for all tool bindings\n *\n * These stubs are included in the LLM system prompt so it knows\n * the exact type signatures of available tools.\n *\n * Tool names match the actual function names injected into the sandbox.\n */\nexport function generateTypeStubs(\n bindings: Record<string, ToolBinding>,\n options: TypeGeneratorOptions = {},\n): string {\n const { includeDescriptions = true } = options\n\n const declarations: Array<string> = []\n\n for (const [name, binding] of Object.entries(bindings)) {\n const inputTypeName = `${capitalize(name)}Input`\n const outputTypeName = `${capitalize(name)}Output`\n\n // Generate input type\n const inputType = jsonSchemaToTypeScript(binding.inputSchema, inputTypeName)\n if (inputType.declaration) {\n declarations.push(inputType.declaration)\n }\n\n // Generate output type if present\n let outputTypeRef = 'unknown'\n if (binding.outputSchema) {\n const outputType = jsonSchemaToTypeScript(\n binding.outputSchema,\n outputTypeName,\n )\n if (outputType.declaration) {\n declarations.push(outputType.declaration)\n }\n outputTypeRef = outputType.name\n }\n\n // Generate function declaration matching the actual sandbox function name\n const description =\n includeDescriptions && binding.description\n ? `/** ${binding.description} */\\n`\n : ''\n\n declarations.push(\n `${description}declare function ${name}(input: ${inputType.name}): Promise<${outputTypeRef}>;`,\n )\n }\n\n return declarations.join('\\n\\n')\n}\n\ninterface TypeResult {\n name: string\n declaration: string\n}\n\n/**\n * Convert a JSON Schema to a TypeScript type\n *\n * Supports basic types: string, number, boolean, object, array\n */\nexport function jsonSchemaToTypeScript(\n schema: Record<string, unknown>,\n typeName: string,\n): TypeResult {\n const type = schemaToType(schema)\n\n // For object schemas with properties, create a named interface\n if (\n schema.type === 'object' &&\n schema.properties &&\n Object.keys(schema.properties).length > 0\n ) {\n return {\n name: typeName,\n declaration: `interface ${typeName} ${type}`,\n }\n }\n\n // For simple types or empty objects, create a type alias\n return {\n name: type,\n declaration: '',\n }\n}\n\n/**\n * Convert a JSON Schema to a TypeScript type string\n */\nfunction schemaToType(schema: Record<string, unknown>): string {\n if (typeof schema !== 'object') {\n return 'unknown'\n }\n\n const schemaType = schema.type\n\n // Handle basic types\n if (schemaType === 'string') return 'string'\n if (schemaType === 'number' || schemaType === 'integer') return 'number'\n if (schemaType === 'boolean') return 'boolean'\n if (schemaType === 'null') return 'null'\n\n // Handle arrays\n if (schemaType === 'array') {\n const items = schema.items as Record<string, unknown> | undefined\n const itemType = items ? schemaToType(items) : 'unknown'\n return `Array<${itemType}>`\n }\n\n // Handle objects with properties\n if (schemaType === 'object' && schema.properties) {\n const properties = schema.properties as Record<\n string,\n Record<string, unknown>\n >\n const required = new Set(\n (schema.required as Array<string> | undefined) ?? [],\n )\n\n const props = Object.entries(properties)\n .map(([key, propSchema]) => {\n const optional = required.has(key) ? '' : '?'\n const propType = schemaToType(propSchema)\n // Handle property names that need quoting\n const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key)\n ? key\n : `\"${key}\"`\n return ` ${safeName}${optional}: ${propType};`\n })\n .join('\\n')\n\n return `{\\n${props}\\n}`\n }\n\n // Handle enums\n if (schema.enum) {\n const enumValues = schema.enum as Array<unknown>\n return enumValues.map((v) => JSON.stringify(v)).join(' | ')\n }\n\n // Handle union types (anyOf, oneOf)\n if (schema.anyOf || schema.oneOf) {\n const variants = (schema.anyOf || schema.oneOf) as Array<\n Record<string, unknown>\n >\n return variants.map((v) => schemaToType(v)).join(' | ')\n }\n\n // Handle type arrays (e.g., [\"string\", \"null\"])\n if (Array.isArray(schemaType)) {\n return schemaType\n .map((t) => {\n if (t === 'string') return 'string'\n if (t === 'number' || t === 'integer') return 'number'\n if (t === 'boolean') return 'boolean'\n if (t === 'null') return 'null'\n if (t === 'array') return 'Array<unknown>'\n if (t === 'object') return 'object'\n return 'unknown'\n })\n .join(' | ')\n }\n\n // Fallback for unknown schemas\n return 'unknown'\n}\n\n/**\n * Capitalize the first letter of a string\n */\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1)\n}\n"],"mappings":"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"}