@tanstack/ai-code-mode 0.1.0

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 (40) hide show
  1. package/README.md +196 -0
  2. package/dist/esm/agent-store.d.ts +21 -0
  3. package/dist/esm/agent-store.js +31 -0
  4. package/dist/esm/agent-store.js.map +1 -0
  5. package/dist/esm/bindings/tool-to-binding.d.ts +24 -0
  6. package/dist/esm/bindings/tool-to-binding.js +78 -0
  7. package/dist/esm/bindings/tool-to-binding.js.map +1 -0
  8. package/dist/esm/code-wrapper.d.ts +9 -0
  9. package/dist/esm/code-wrapper.js +18 -0
  10. package/dist/esm/code-wrapper.js.map +1 -0
  11. package/dist/esm/create-code-mode-tool.d.ts +50 -0
  12. package/dist/esm/create-code-mode-tool.js +142 -0
  13. package/dist/esm/create-code-mode-tool.js.map +1 -0
  14. package/dist/esm/create-code-mode.d.ts +41 -0
  15. package/dist/esm/create-code-mode.js +12 -0
  16. package/dist/esm/create-code-mode.js.map +1 -0
  17. package/dist/esm/create-system-prompt.d.ts +24 -0
  18. package/dist/esm/create-system-prompt.js +66 -0
  19. package/dist/esm/create-system-prompt.js.map +1 -0
  20. package/dist/esm/index.d.ts +10 -0
  21. package/dist/esm/index.js +23 -0
  22. package/dist/esm/index.js.map +1 -0
  23. package/dist/esm/strip-typescript.d.ts +23 -0
  24. package/dist/esm/strip-typescript.js +52 -0
  25. package/dist/esm/strip-typescript.js.map +1 -0
  26. package/dist/esm/type-generator/json-schema-to-ts.d.ts +31 -0
  27. package/dist/esm/type-generator/json-schema-to-ts.js +100 -0
  28. package/dist/esm/type-generator/json-schema-to-ts.js.map +1 -0
  29. package/dist/esm/types.d.ts +176 -0
  30. package/package.json +62 -0
  31. package/src/agent-store.ts +43 -0
  32. package/src/bindings/tool-to-binding.ts +132 -0
  33. package/src/code-wrapper.ts +24 -0
  34. package/src/create-code-mode-tool.ts +254 -0
  35. package/src/create-code-mode.ts +35 -0
  36. package/src/create-system-prompt.ts +95 -0
  37. package/src/index.ts +55 -0
  38. package/src/strip-typescript.ts +94 -0
  39. package/src/type-generator/json-schema-to-ts.ts +188 -0
  40. package/src/types.ts +225 -0
@@ -0,0 +1,41 @@
1
+ import { CodeModeToolConfig } from './types.js';
2
+ /**
3
+ * Create both the `execute_typescript` tool and its matching system prompt
4
+ * from a single config object.
5
+ *
6
+ * This is the recommended way to set up Code Mode — it ensures the tool and
7
+ * system prompt always stay in sync.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { createCodeMode } from '@tanstack/ai-code-mode'
12
+ * import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'
13
+ *
14
+ * const { tool, systemPrompt } = createCodeMode({
15
+ * driver: createNodeIsolateDriver(),
16
+ * tools: [weatherTool, dbTool],
17
+ * timeout: 30000,
18
+ * })
19
+ *
20
+ * chat({
21
+ * systemPrompts: [myPrompt, systemPrompt],
22
+ * tools: [tool, ...otherTools],
23
+ * messages,
24
+ * })
25
+ * ```
26
+ */
27
+ export declare function createCodeMode(config: CodeModeToolConfig): {
28
+ tool: import('@tanstack/ai').ServerTool<import('zod').ZodObject<{
29
+ typescriptCode: import('zod').ZodString;
30
+ }, import('zod/v4/core').$strip>, import('zod').ZodObject<{
31
+ success: import('zod').ZodBoolean;
32
+ result: import('zod').ZodOptional<import('zod').ZodUnknown>;
33
+ logs: import('zod').ZodOptional<import('zod').ZodArray<import('zod').ZodString>>;
34
+ error: import('zod').ZodOptional<import('zod').ZodObject<{
35
+ message: import('zod').ZodString;
36
+ name: import('zod').ZodOptional<import('zod').ZodString>;
37
+ line: import('zod').ZodOptional<import('zod').ZodNumber>;
38
+ }, import('zod/v4/core').$strip>>;
39
+ }, import('zod/v4/core').$strip>, "execute_typescript">;
40
+ systemPrompt: string;
41
+ };
@@ -0,0 +1,12 @@
1
+ import { createCodeModeTool } from "./create-code-mode-tool.js";
2
+ import { createCodeModeSystemPrompt } from "./create-system-prompt.js";
3
+ function createCodeMode(config) {
4
+ return {
5
+ tool: createCodeModeTool(config),
6
+ systemPrompt: createCodeModeSystemPrompt(config)
7
+ };
8
+ }
9
+ export {
10
+ createCodeMode
11
+ };
12
+ //# sourceMappingURL=create-code-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-code-mode.js","sources":["../../src/create-code-mode.ts"],"sourcesContent":["import { createCodeModeTool } from './create-code-mode-tool'\nimport { createCodeModeSystemPrompt } from './create-system-prompt'\nimport type { CodeModeToolConfig } from './types'\n\n/**\n * Create both the `execute_typescript` tool and its matching system prompt\n * from a single config object.\n *\n * This is the recommended way to set up Code Mode — it ensures the tool and\n * system prompt always stay in sync.\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 * timeout: 30000,\n * })\n *\n * chat({\n * systemPrompts: [myPrompt, systemPrompt],\n * tools: [tool, ...otherTools],\n * messages,\n * })\n * ```\n */\nexport function createCodeMode(config: CodeModeToolConfig) {\n return {\n tool: createCodeModeTool(config),\n systemPrompt: createCodeModeSystemPrompt(config),\n }\n}\n"],"names":[],"mappings":";;AA6BO,SAAS,eAAe,QAA4B;AACzD,SAAO;AAAA,IACL,MAAM,mBAAmB,MAAM;AAAA,IAC/B,cAAc,2BAA2B,MAAM;AAAA,EAAA;AAEnD;"}
@@ -0,0 +1,24 @@
1
+ import { CodeModeToolConfig } from './types.js';
2
+ /**
3
+ * Create a system prompt snippet that documents the execute_typescript tool
4
+ * and all available external_* functions.
5
+ *
6
+ * Add this to your system prompts array when using createCodeModeTool.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { createCodeMode } from '@tanstack/ai-code-mode'
11
+ * import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'
12
+ *
13
+ * const { tool, systemPrompt } = createCodeMode({
14
+ * driver: createNodeIsolateDriver(),
15
+ * tools: [weatherTool, dbTool],
16
+ * })
17
+ *
18
+ * chat({
19
+ * systemPrompts: ['You are a helpful assistant.', systemPrompt],
20
+ * tools: [tool, ...otherTools],
21
+ * })
22
+ * ```
23
+ */
24
+ export declare function createCodeModeSystemPrompt(config: CodeModeToolConfig): string;
@@ -0,0 +1,66 @@
1
+ import { toolsToBindings } from "./bindings/tool-to-binding.js";
2
+ import { generateTypeStubs } from "./type-generator/json-schema-to-ts.js";
3
+ function createCodeModeSystemPrompt(config) {
4
+ const { tools } = config;
5
+ const bindings = toolsToBindings(tools, "external_");
6
+ const typeStubs = generateTypeStubs(bindings);
7
+ const functionDocs = Object.entries(bindings).map(([name, binding]) => {
8
+ const doc = `- \`${name}(input)\`: ${binding.description}`;
9
+ return doc;
10
+ }).join("\n");
11
+ return `## Code Execution Tool
12
+
13
+ You have access to \`execute_typescript\` which runs TypeScript code in a sandboxed environment.
14
+
15
+ ### When to Use
16
+
17
+ Use \`execute_typescript\` when you need to:
18
+ - Process data with loops, conditionals, or complex logic
19
+ - Make multiple API calls in parallel (Promise.all)
20
+ - Transform, filter, or aggregate data
21
+ - Perform calculations or data analysis
22
+
23
+ For simple operations, prefer calling tools directly.
24
+
25
+ ### Available External APIs
26
+
27
+ Inside your TypeScript code, you can call these async functions:
28
+
29
+ ${functionDocs}
30
+
31
+ ### Type Definitions
32
+
33
+ \`\`\`typescript
34
+ ${typeStubs}
35
+ \`\`\`
36
+
37
+ ### Example
38
+
39
+ \`\`\`typescript
40
+ // Fetch weather for multiple cities in parallel
41
+ const cities = ["Tokyo", "Paris", "NYC"];
42
+ const results = await Promise.all(
43
+ cities.map(city => external_fetchWeather({ location: city }))
44
+ );
45
+
46
+ // Find the warmest city
47
+ const warmest = results.reduce((prev, curr) =>
48
+ curr.temperature > prev.temperature ? curr : prev
49
+ );
50
+
51
+ return { warmestCity: warmest.location, temperature: warmest.temperature };
52
+ \`\`\`
53
+
54
+ ### Important Notes
55
+
56
+ - All \`external_*\` calls are async - always use \`await\`
57
+ - Return a value to pass results back to you
58
+ - Use \`console.log()\` for debugging (logs are captured)
59
+ - The sandbox is isolated - no network access or file system
60
+ - Each execution is independent (no shared state between calls)
61
+ `;
62
+ }
63
+ export {
64
+ createCodeModeSystemPrompt
65
+ };
66
+ //# sourceMappingURL=create-system-prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-system-prompt.js","sources":["../../src/create-system-prompt.ts"],"sourcesContent":["import { 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\n // Transform tools to bindings with external_ prefix to generate correct type stubs\n const bindings = toolsToBindings(tools, 'external_')\n\n // Generate TypeScript type stubs for the external functions\n const typeStubs = generateTypeStubs(bindings)\n\n // Build function documentation\n const functionDocs = Object.entries(bindings)\n .map(([name, binding]) => {\n const doc = `- \\`${name}(input)\\`: ${binding.description}`\n return doc\n })\n .join('\\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\\`\\`\\`\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":";;AA0BO,SAAS,2BAA2B,QAAoC;AAC7E,QAAM,EAAE,UAAU;AAGlB,QAAM,WAAW,gBAAgB,OAAO,WAAW;AAGnD,QAAM,YAAY,kBAAkB,QAAQ;AAG5C,QAAM,eAAe,OAAO,QAAQ,QAAQ,EACzC,IAAI,CAAC,CAAC,MAAM,OAAO,MAAM;AACxB,UAAM,MAAM,OAAO,IAAI,cAAc,QAAQ,WAAW;AACxD,WAAO;AAAA,EACT,CAAC,EACA,KAAK,IAAI;AAEZ,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;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;AA4BX;"}
@@ -0,0 +1,10 @@
1
+ export { createCodeModeTool } from './create-code-mode-tool.js';
2
+ export type { ExecuteTypescriptInput, ExecuteTypescriptOutput, } from './create-code-mode-tool.js';
3
+ export { createCodeModeSystemPrompt } from './create-system-prompt.js';
4
+ export { createCodeMode } from './create-code-mode.js';
5
+ export { InMemoryAgentStore, generateAgentName, type AgentSession, type AgentStore, } from './agent-store.js';
6
+ export { toolToBinding, toolsToBindings, createEventAwareBindings, } from './bindings/tool-to-binding.js';
7
+ export { generateTypeStubs, jsonSchemaToTypeScript, type TypeGeneratorOptions, } from './type-generator/json-schema-to-ts.js';
8
+ export { stripTypeScript } from './strip-typescript.js';
9
+ export { wrapCode } from './code-wrapper.js';
10
+ export type { CodeModeToolConfig, CodeModeToolResult, IsolateDriver, IsolateConfig, IsolateContext, ExecutionResult, NormalizedError, ToolBinding, CodeModeTool, ToolExecutionContext, } from './types.js';
@@ -0,0 +1,23 @@
1
+ import { createCodeModeTool } from "./create-code-mode-tool.js";
2
+ import { createCodeModeSystemPrompt } from "./create-system-prompt.js";
3
+ import { createCodeMode } from "./create-code-mode.js";
4
+ import { InMemoryAgentStore, generateAgentName } from "./agent-store.js";
5
+ import { createEventAwareBindings, toolToBinding, toolsToBindings } from "./bindings/tool-to-binding.js";
6
+ import { generateTypeStubs, jsonSchemaToTypeScript } from "./type-generator/json-schema-to-ts.js";
7
+ import { stripTypeScript } from "./strip-typescript.js";
8
+ import { wrapCode } from "./code-wrapper.js";
9
+ export {
10
+ InMemoryAgentStore,
11
+ createCodeMode,
12
+ createCodeModeSystemPrompt,
13
+ createCodeModeTool,
14
+ createEventAwareBindings,
15
+ generateAgentName,
16
+ generateTypeStubs,
17
+ jsonSchemaToTypeScript,
18
+ stripTypeScript,
19
+ toolToBinding,
20
+ toolsToBindings,
21
+ wrapCode
22
+ };
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Strip TypeScript syntax from code, converting it to plain JavaScript.
3
+ *
4
+ * This is a safety net to ensure that even if an LLM generates TypeScript
5
+ * code with type annotations, it will be converted to valid JavaScript
6
+ * before being sent to the sandbox for execution.
7
+ *
8
+ * Uses esbuild's transform API which is extremely fast and handles all
9
+ * TypeScript syntax including:
10
+ * - Type annotations (: string, : number, etc.)
11
+ * - Generic types (Array<T>, Record<K, V>, etc.)
12
+ * - Interface and type declarations
13
+ * - Type assertions
14
+ * - Enums (converted to JavaScript objects)
15
+ *
16
+ * The code is wrapped in an async function before transformation to allow
17
+ * top-level `return` and `await` statements, then unwrapped after.
18
+ *
19
+ * @param code - TypeScript or JavaScript code
20
+ * @returns Plain JavaScript code with all type syntax removed
21
+ * @throws Error if esbuild fails (e.g., syntax error) or wrapper extraction fails
22
+ */
23
+ export declare function stripTypeScript(code: string): Promise<string>;
@@ -0,0 +1,52 @@
1
+ import { transform } from "esbuild";
2
+ const WRAPPER_START = "___TANSTACK_WRAPPER_START___";
3
+ const WRAPPER_END = "___TANSTACK_WRAPPER_END___";
4
+ async function stripTypeScript(code) {
5
+ const wrappedCode = `async function ${WRAPPER_START}() {
6
+ ${code}
7
+ }; ${WRAPPER_END}`;
8
+ const result = await transform(wrappedCode, {
9
+ loader: "ts",
10
+ // Don't minify - keep the code readable for debugging
11
+ minify: false,
12
+ // Don't use keepNames as it adds __name() helper calls that aren't available in the sandbox
13
+ keepNames: false,
14
+ // Target modern JavaScript (ES2022 has top-level await)
15
+ target: "es2022"
16
+ });
17
+ const transformed = result.code;
18
+ const functionStart = transformed.indexOf(`async function ${WRAPPER_START}()`);
19
+ if (functionStart === -1) {
20
+ throw new Error(
21
+ "[stripTypeScript] Could not find wrapper function start in transformed output"
22
+ );
23
+ }
24
+ const openBrace = transformed.indexOf("{", functionStart);
25
+ if (openBrace === -1) {
26
+ throw new Error(
27
+ "[stripTypeScript] Could not find opening brace in transformed output"
28
+ );
29
+ }
30
+ const endMarkerIndex = transformed.indexOf(WRAPPER_END);
31
+ if (endMarkerIndex === -1) {
32
+ throw new Error(
33
+ "[stripTypeScript] Could not find end marker in transformed output"
34
+ );
35
+ }
36
+ const codeBeforeEndMarker = transformed.substring(
37
+ openBrace + 1,
38
+ endMarkerIndex
39
+ );
40
+ const closingBraceIndex = codeBeforeEndMarker.lastIndexOf("}");
41
+ if (closingBraceIndex === -1) {
42
+ throw new Error(
43
+ "[stripTypeScript] Could not find closing brace in transformed output"
44
+ );
45
+ }
46
+ const functionBody = codeBeforeEndMarker.substring(0, closingBraceIndex).trim();
47
+ return functionBody;
48
+ }
49
+ export {
50
+ stripTypeScript
51
+ };
52
+ //# sourceMappingURL=strip-typescript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strip-typescript.js","sources":["../../src/strip-typescript.ts"],"sourcesContent":["import { transform } from 'esbuild'\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 esbuild's transform API which is extremely fast and handles all\n * TypeScript syntax including:\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 * The code is wrapped in an async function before transformation to allow\n * top-level `return` and `await` statements, then unwrapped after.\n *\n * @param code - TypeScript or JavaScript code\n * @returns Plain JavaScript code with all type syntax removed\n * @throws Error if esbuild fails (e.g., syntax error) or wrapper extraction fails\n */\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 esbuild's ESM format doesn't allow top-level returns\n const wrappedCode = `async function ${WRAPPER_START}() {\\n${code}\\n}; ${WRAPPER_END}`\n\n const result = await transform(wrappedCode, {\n loader: 'ts',\n // Don't minify - keep the code readable for debugging\n minify: false,\n // Don't use keepNames as it adds __name() helper calls that aren't available in the sandbox\n keepNames: false,\n // Target modern JavaScript (ES2022 has top-level await)\n target: 'es2022',\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;AAwBpB,eAAsB,gBAAgB,MAA+B;AAGnE,QAAM,cAAc,kBAAkB,aAAa;AAAA,EAAS,IAAI;AAAA,KAAQ,WAAW;AAEnF,QAAM,SAAS,MAAM,UAAU,aAAa;AAAA,IAC1C,QAAQ;AAAA;AAAA,IAER,QAAQ;AAAA;AAAA,IAER,WAAW;AAAA;AAAA,IAEX,QAAQ;AAAA,EAAA,CACT;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;"}
@@ -0,0 +1,31 @@
1
+ import { ToolBinding } from '../types.js';
2
+ /**
3
+ * Options for type stub generation
4
+ */
5
+ export interface TypeGeneratorOptions {
6
+ /**
7
+ * Include JSDoc comments with descriptions
8
+ * @default true
9
+ */
10
+ includeDescriptions?: boolean;
11
+ }
12
+ /**
13
+ * Generate TypeScript type stubs for all tool bindings
14
+ *
15
+ * These stubs are included in the LLM system prompt so it knows
16
+ * the exact type signatures of available tools.
17
+ *
18
+ * Tool names match the actual function names injected into the sandbox.
19
+ */
20
+ export declare function generateTypeStubs(bindings: Record<string, ToolBinding>, options?: TypeGeneratorOptions): string;
21
+ interface TypeResult {
22
+ name: string;
23
+ declaration: string;
24
+ }
25
+ /**
26
+ * Convert a JSON Schema to a TypeScript type
27
+ *
28
+ * Supports basic types: string, number, boolean, object, array
29
+ */
30
+ export declare function jsonSchemaToTypeScript(schema: Record<string, unknown>, typeName: string): TypeResult;
31
+ export {};
@@ -0,0 +1,100 @@
1
+ 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");
29
+ }
30
+ 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
+ };
42
+ }
43
+ 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";
92
+ }
93
+ function capitalize(str) {
94
+ return str.charAt(0).toUpperCase() + str.slice(1);
95
+ }
96
+ export {
97
+ generateTypeStubs,
98
+ jsonSchemaToTypeScript
99
+ };
100
+ //# sourceMappingURL=json-schema-to-ts.js.map
@@ -0,0 +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 as object).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":"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"}
@@ -0,0 +1,176 @@
1
+ import { ServerTool, ToolDefinition, ToolExecutionContext } from '@tanstack/ai';
2
+ /**
3
+ * Interface for isolate/sandbox drivers
4
+ * Each runtime environment implements this to provide sandboxed code execution
5
+ */
6
+ export interface IsolateDriver {
7
+ /**
8
+ * Create a new isolated execution context with tool bindings
9
+ */
10
+ createContext: (config: IsolateConfig) => Promise<IsolateContext>;
11
+ }
12
+ /**
13
+ * Configuration for creating an isolate context
14
+ */
15
+ export interface IsolateConfig {
16
+ /**
17
+ * Tools transformed into callable bindings for the sandbox
18
+ */
19
+ bindings: Record<string, ToolBinding>;
20
+ /**
21
+ * Execution timeout in milliseconds (default: 30000)
22
+ */
23
+ timeout?: number;
24
+ /**
25
+ * Memory limit in MB (default: 128)
26
+ */
27
+ memoryLimit?: number;
28
+ }
29
+ /**
30
+ * Isolated execution context with tool bindings injected
31
+ */
32
+ export interface IsolateContext {
33
+ /**
34
+ * Execute generated code and return results
35
+ */
36
+ execute: <T = unknown>(code: string) => Promise<ExecutionResult<T>>;
37
+ /**
38
+ * Clean up sandbox resources
39
+ */
40
+ dispose: () => Promise<void>;
41
+ }
42
+ /**
43
+ * Result of code execution in the sandbox
44
+ */
45
+ export interface ExecutionResult<T = unknown> {
46
+ /**
47
+ * Whether execution completed without errors
48
+ */
49
+ success: boolean;
50
+ /**
51
+ * Return value from the executed code (if successful)
52
+ */
53
+ value?: T;
54
+ /**
55
+ * Normalized error information (if failed)
56
+ */
57
+ error?: NormalizedError;
58
+ /**
59
+ * Console output captured during execution
60
+ */
61
+ logs?: Array<string>;
62
+ }
63
+ /**
64
+ * Normalized error format for cross-runtime compatibility
65
+ */
66
+ export interface NormalizedError {
67
+ /**
68
+ * Error name/type
69
+ */
70
+ name: string;
71
+ /**
72
+ * Error message
73
+ */
74
+ message: string;
75
+ /**
76
+ * Stack trace (if available)
77
+ */
78
+ stack?: string;
79
+ /**
80
+ * Error code (if available)
81
+ */
82
+ code?: string;
83
+ }
84
+ /**
85
+ * A tool transformed into a format suitable for sandbox injection
86
+ */
87
+ export interface ToolBinding {
88
+ /**
89
+ * Unique tool identifier
90
+ */
91
+ name: string;
92
+ /**
93
+ * Human-readable description for the LLM
94
+ */
95
+ description: string;
96
+ /**
97
+ * JSON Schema for tool input parameters
98
+ */
99
+ inputSchema: Record<string, unknown>;
100
+ /**
101
+ * JSON Schema for tool output (optional)
102
+ */
103
+ outputSchema?: Record<string, unknown>;
104
+ /**
105
+ * The execute function that will be injected into the sandbox.
106
+ * Accepts optional context for emitting custom events.
107
+ */
108
+ execute: (args: unknown, context?: ToolExecutionContext) => Promise<unknown>;
109
+ }
110
+ export type { ToolExecutionContext };
111
+ /**
112
+ * Tool types that can be passed to Code Mode
113
+ */
114
+ export type CodeModeTool = ServerTool<any, any, any> | ToolDefinition<any, any, any>;
115
+ /**
116
+ * Configuration for createCodeModeTool
117
+ */
118
+ export interface CodeModeToolConfig {
119
+ /**
120
+ * Isolate driver for sandboxed code execution
121
+ */
122
+ driver: IsolateDriver;
123
+ /**
124
+ * Tools to expose as external_* functions in the sandbox
125
+ */
126
+ tools: Array<CodeModeTool>;
127
+ /**
128
+ * Execution timeout in milliseconds (default: 30000)
129
+ */
130
+ timeout?: number;
131
+ /**
132
+ * Memory limit for isolate in MB (default: 128)
133
+ */
134
+ memoryLimit?: number;
135
+ /**
136
+ * Optional function to get additional bindings dynamically.
137
+ * Called at execution time (each execute_typescript call) to get current skill bindings.
138
+ * These are merged with the static external_* bindings.
139
+ *
140
+ * @returns Record of skill bindings with skill_ prefix
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * getSkillBindings: async () => {
145
+ * const skills = await storage.loadAll()
146
+ * return skillsToBindings(skills, 'skill_')
147
+ * }
148
+ * ```
149
+ */
150
+ getSkillBindings?: () => Promise<Record<string, ToolBinding>>;
151
+ }
152
+ /**
153
+ * Result returned by the execute_typescript tool
154
+ */
155
+ export interface CodeModeToolResult {
156
+ /**
157
+ * Whether execution completed without errors
158
+ */
159
+ success: boolean;
160
+ /**
161
+ * Return value from the executed code (if successful)
162
+ */
163
+ result?: unknown;
164
+ /**
165
+ * Console output captured during execution
166
+ */
167
+ logs?: Array<string>;
168
+ /**
169
+ * Error details if execution failed
170
+ */
171
+ error?: {
172
+ message: string;
173
+ name?: string;
174
+ line?: number;
175
+ };
176
+ }