@tanstack/ai-code-mode 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/agent-store.js +23 -27
- package/dist/esm/agent-store.js.map +1 -1
- package/dist/esm/bindings/tool-to-binding.js +92 -87
- package/dist/esm/bindings/tool-to-binding.js.map +1 -1
- package/dist/esm/code-wrapper.js +14 -5
- package/dist/esm/code-wrapper.js.map +1 -1
- package/dist/esm/create-code-mode-tool.js +155 -153
- package/dist/esm/create-code-mode-tool.js.map +1 -1
- package/dist/esm/create-code-mode.js +36 -14
- package/dist/esm/create-code-mode.js.map +1 -1
- package/dist/esm/create-discovery-tool.js +63 -55
- package/dist/esm/create-discovery-tool.js.map +1 -1
- package/dist/esm/create-system-prompt.js +43 -24
- package/dist/esm/create-system-prompt.js.map +1 -1
- package/dist/esm/index.js +5 -20
- package/dist/esm/strip-typescript.js +64 -46
- package/dist/esm/strip-typescript.js.map +1 -1
- package/dist/esm/type-generator/json-schema-to-ts.js +82 -92
- package/dist/esm/type-generator/json-schema-to-ts.js.map +1 -1
- package/dist/esm/validate-bindings.js +102 -91
- package/dist/esm/validate-bindings.js.map +1 -1
- package/package.json +5 -5
- package/skills/ai-code-mode/SKILL.md +1 -1
- package/dist/esm/index.js.map +0 -1
|
@@ -1,160 +1,162 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { toolDefinition } from "@tanstack/ai";
|
|
3
|
-
import { toolsToBindings, createEventAwareBindings } from "./bindings/tool-to-binding.js";
|
|
1
|
+
import { createEventAwareBindings, toolsToBindings } from "./bindings/tool-to-binding.js";
|
|
4
2
|
import { stripTypeScript } from "./strip-typescript.js";
|
|
5
3
|
import { warnIfBindingsExposeSecrets } from "./validate-bindings.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { toolDefinition } from "@tanstack/ai";
|
|
6
|
+
//#region src/create-code-mode-tool.ts
|
|
7
|
+
/**
|
|
8
|
+
* Schema for the execute_typescript tool input
|
|
9
|
+
*/
|
|
10
|
+
var executeTypescriptInputSchema = z.object({ typescriptCode: z.string().describe("TypeScript code to execute in the sandbox. Use external_* functions to call available APIs. Return a value to pass results back.") });
|
|
11
|
+
/**
|
|
12
|
+
* Schema for the execute_typescript tool output
|
|
13
|
+
*/
|
|
14
|
+
var executeTypescriptOutputSchema = z.object({
|
|
15
|
+
success: z.boolean().describe("Whether execution completed without errors"),
|
|
16
|
+
result: z.unknown().optional().describe("Return value from the executed code"),
|
|
17
|
+
logs: z.array(z.string()).optional().describe("Console output captured during execution"),
|
|
18
|
+
error: z.object({
|
|
19
|
+
message: z.string(),
|
|
20
|
+
name: z.string().optional(),
|
|
21
|
+
line: z.number().optional()
|
|
22
|
+
}).optional().describe("Error details if execution failed")
|
|
20
23
|
});
|
|
24
|
+
/**
|
|
25
|
+
* Create an execute_typescript tool that can be used alongside other agent tools.
|
|
26
|
+
*
|
|
27
|
+
* This tool allows an LLM to execute TypeScript code in a secure sandbox.
|
|
28
|
+
* Tools passed in the config become `external_*` functions available inside the sandbox.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* import { createCodeMode } from '@tanstack/ai-code-mode'
|
|
33
|
+
* import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'
|
|
34
|
+
*
|
|
35
|
+
* const { tool, systemPrompt } = createCodeMode({
|
|
36
|
+
* driver: createNodeIsolateDriver(),
|
|
37
|
+
* tools: [weatherTool, dbTool], // Become external_fetchWeather, external_dbQuery
|
|
38
|
+
* timeout: 30000,
|
|
39
|
+
* })
|
|
40
|
+
*
|
|
41
|
+
* chat({
|
|
42
|
+
* systemPrompts: [myPrompt, systemPrompt],
|
|
43
|
+
* tools: [tool, searchTool, emailTool],
|
|
44
|
+
* messages,
|
|
45
|
+
* })
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
21
48
|
function createCodeModeTool(config) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
};
|
|
124
|
-
} else {
|
|
125
|
-
return {
|
|
126
|
-
success: false,
|
|
127
|
-
error: executionResult.error ? {
|
|
128
|
-
message: executionResult.error.message,
|
|
129
|
-
name: executionResult.error.name
|
|
130
|
-
} : { message: "Unknown execution error" },
|
|
131
|
-
logs: executionResult.logs
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
} catch (error) {
|
|
135
|
-
return {
|
|
136
|
-
success: false,
|
|
137
|
-
error: {
|
|
138
|
-
message: error instanceof Error ? error.message : String(error),
|
|
139
|
-
name: error instanceof Error ? error.name : "Error"
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
} finally {
|
|
143
|
-
if (isolateContext) {
|
|
144
|
-
await isolateContext.dispose();
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
);
|
|
49
|
+
const { driver, tools, timeout = 3e4, memoryLimit = 128, getSkillBindings, onSecretParameter, transpile = stripTypeScript } = config;
|
|
50
|
+
if (tools.length === 0) throw new Error("At least one tool must be provided to createCodeModeTool");
|
|
51
|
+
const staticBindings = toolsToBindings(tools, "external_");
|
|
52
|
+
const secretDedupCache = /* @__PURE__ */ new Set();
|
|
53
|
+
warnIfBindingsExposeSecrets(Object.values(staticBindings), {
|
|
54
|
+
handler: onSecretParameter,
|
|
55
|
+
dedupCache: secretDedupCache
|
|
56
|
+
});
|
|
57
|
+
return toolDefinition({
|
|
58
|
+
name: "execute_typescript",
|
|
59
|
+
description: buildToolDescription(tools),
|
|
60
|
+
inputSchema: executeTypescriptInputSchema,
|
|
61
|
+
outputSchema: executeTypescriptOutputSchema
|
|
62
|
+
}).server(async (input, toolContext) => {
|
|
63
|
+
const { typescriptCode } = input;
|
|
64
|
+
const emitCustomEvent = toolContext?.emitCustomEvent || (() => {});
|
|
65
|
+
if (!typescriptCode || typeof typescriptCode !== "string") return {
|
|
66
|
+
success: false,
|
|
67
|
+
error: {
|
|
68
|
+
message: "typescriptCode must be a non-empty string",
|
|
69
|
+
name: "ValidationError"
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
let isolateContext = null;
|
|
73
|
+
emitCustomEvent("code_mode:execution_started", {
|
|
74
|
+
timestamp: Date.now(),
|
|
75
|
+
codeLength: typescriptCode.length
|
|
76
|
+
});
|
|
77
|
+
try {
|
|
78
|
+
let strippedCode;
|
|
79
|
+
try {
|
|
80
|
+
strippedCode = await transpile(typescriptCode);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return {
|
|
83
|
+
success: false,
|
|
84
|
+
error: {
|
|
85
|
+
message: error instanceof Error ? error.message : String(error),
|
|
86
|
+
name: "TypeScriptError"
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const skillBindings = getSkillBindings ? await getSkillBindings() : {};
|
|
91
|
+
const skillBindingValues = Object.values(skillBindings);
|
|
92
|
+
if (skillBindingValues.length > 0) warnIfBindingsExposeSecrets(skillBindingValues, {
|
|
93
|
+
handler: onSecretParameter,
|
|
94
|
+
dedupCache: secretDedupCache
|
|
95
|
+
});
|
|
96
|
+
const eventAwareBindings = createEventAwareBindings({
|
|
97
|
+
...staticBindings,
|
|
98
|
+
...skillBindings
|
|
99
|
+
}, emitCustomEvent);
|
|
100
|
+
isolateContext = await driver.createContext({
|
|
101
|
+
bindings: eventAwareBindings,
|
|
102
|
+
timeout,
|
|
103
|
+
memoryLimit
|
|
104
|
+
});
|
|
105
|
+
const executionResult = await isolateContext.execute(strippedCode);
|
|
106
|
+
if (executionResult.logs && executionResult.logs.length > 0) for (const log of executionResult.logs) {
|
|
107
|
+
let level = "log";
|
|
108
|
+
let message = log;
|
|
109
|
+
if (log.startsWith("ERROR: ")) {
|
|
110
|
+
level = "error";
|
|
111
|
+
message = log.slice(7);
|
|
112
|
+
} else if (log.startsWith("WARN: ")) {
|
|
113
|
+
level = "warn";
|
|
114
|
+
message = log.slice(6);
|
|
115
|
+
} else if (log.startsWith("INFO: ")) {
|
|
116
|
+
level = "info";
|
|
117
|
+
message = log.slice(6);
|
|
118
|
+
}
|
|
119
|
+
emitCustomEvent("code_mode:console", {
|
|
120
|
+
level,
|
|
121
|
+
message,
|
|
122
|
+
timestamp: Date.now()
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (executionResult.success) return {
|
|
126
|
+
success: true,
|
|
127
|
+
result: executionResult.value,
|
|
128
|
+
logs: executionResult.logs
|
|
129
|
+
};
|
|
130
|
+
else return {
|
|
131
|
+
success: false,
|
|
132
|
+
error: executionResult.error ? {
|
|
133
|
+
message: executionResult.error.message,
|
|
134
|
+
name: executionResult.error.name
|
|
135
|
+
} : { message: "Unknown execution error" },
|
|
136
|
+
logs: executionResult.logs
|
|
137
|
+
};
|
|
138
|
+
} catch (error) {
|
|
139
|
+
return {
|
|
140
|
+
success: false,
|
|
141
|
+
error: {
|
|
142
|
+
message: error instanceof Error ? error.message : String(error),
|
|
143
|
+
name: error instanceof Error ? error.name : "Error"
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
} finally {
|
|
147
|
+
if (isolateContext) await isolateContext.dispose();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
149
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Build the tool description including available external functions
|
|
153
|
+
*/
|
|
150
154
|
function buildToolDescription(tools) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const discoverable = hasLazy ? ` Additional functions can be discovered via the discover_tools tool.` : "";
|
|
155
|
-
return `Execute TypeScript code in a secure sandbox environment. The code can use these external API functions: ${externalFunctions}.${discoverable} All external_* calls are async and must be awaited. Return a value to pass results back. Use console.log() for debugging.`;
|
|
155
|
+
const eager = tools.filter((t) => !t.lazy);
|
|
156
|
+
const hasLazy = tools.some((t) => t.lazy);
|
|
157
|
+
return `Execute TypeScript code in a secure sandbox environment. The code can use these external API functions: ${eager.map((t) => `external_${t.name}`).join(", ")}.${hasLazy ? ` Additional functions can be discovered via the discover_tools tool.` : ""} All external_* calls are async and must be awaited. Return a value to pass results back. Use console.log() for debugging.`;
|
|
156
158
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
//# sourceMappingURL=create-code-mode-tool.js.map
|
|
159
|
+
//#endregion
|
|
160
|
+
export { createCodeModeTool };
|
|
161
|
+
|
|
162
|
+
//# sourceMappingURL=create-code-mode-tool.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-code-mode-tool.js","sources":["../../src/create-code-mode-tool.ts"],"sourcesContent":["import { z } from 'zod'\nimport { toolDefinition } from '@tanstack/ai'\nimport {\n createEventAwareBindings,\n toolsToBindings,\n} from './bindings/tool-to-binding'\nimport { stripTypeScript } from './strip-typescript'\nimport { warnIfBindingsExposeSecrets } from './validate-bindings'\nimport type { ServerTool, ToolExecutionContext } from '@tanstack/ai'\nimport type {\n CodeModeTool,\n CodeModeToolConfig,\n CodeModeToolResult,\n IsolateContext,\n} from './types'\n\n/**\n * Schema for the execute_typescript tool input\n */\nconst executeTypescriptInputSchema = z.object({\n typescriptCode: z\n .string()\n .describe(\n 'TypeScript code to execute in the sandbox. ' +\n 'Use external_* functions to call available APIs. ' +\n 'Return a value to pass results back.',\n ),\n})\n\n/**\n * Schema for the execute_typescript tool output\n */\nconst executeTypescriptOutputSchema = z.object({\n success: z.boolean().describe('Whether execution completed without errors'),\n result: z\n .unknown()\n .optional()\n .describe('Return value from the executed code'),\n logs: z\n .array(z.string())\n .optional()\n .describe('Console output captured during execution'),\n error: z\n .object({\n message: z.string(),\n name: z.string().optional(),\n line: z.number().optional(),\n })\n .optional()\n .describe('Error details if execution failed'),\n})\n\nexport type ExecuteTypescriptInput = z.infer<\n typeof executeTypescriptInputSchema\n>\nexport type ExecuteTypescriptOutput = z.infer<\n typeof executeTypescriptOutputSchema\n>\n\n/**\n * Create an execute_typescript tool that can be used alongside other agent tools.\n *\n * This tool allows an LLM to execute TypeScript code in a secure sandbox.\n * Tools passed in the config become `external_*` functions available inside the sandbox.\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], // Become external_fetchWeather, external_dbQuery\n * timeout: 30000,\n * })\n *\n * chat({\n * systemPrompts: [myPrompt, systemPrompt],\n * tools: [tool, searchTool, emailTool],\n * messages,\n * })\n * ```\n */\nexport function createCodeModeTool(\n config: CodeModeToolConfig,\n): ServerTool<\n typeof executeTypescriptInputSchema,\n typeof executeTypescriptOutputSchema,\n 'execute_typescript'\n> {\n const {\n driver,\n tools,\n timeout = 30000,\n memoryLimit = 128,\n getSkillBindings,\n onSecretParameter,\n transpile = stripTypeScript,\n } = config\n\n // Validate tools\n if (tools.length === 0) {\n throw new Error('At least one tool must be provided to createCodeModeTool')\n }\n\n // Transform tools to bindings with external_ prefix (static bindings)\n const staticBindings = toolsToBindings(tools, 'external_')\n\n // Shared across static + dynamic (skill) binding scans so a given\n // (toolName, paramPath) pair surfaces at most once per code-mode instance.\n const secretDedupCache = new Set<string>()\n\n warnIfBindingsExposeSecrets(Object.values(staticBindings), {\n handler: onSecretParameter,\n dedupCache: secretDedupCache,\n })\n\n // Create the tool definition\n const definition = toolDefinition({\n name: 'execute_typescript' as const,\n description: buildToolDescription(tools),\n inputSchema: executeTypescriptInputSchema,\n outputSchema: executeTypescriptOutputSchema,\n })\n\n // Return server tool with execute function that accepts context\n return definition.server(\n async (\n input,\n toolContext?: ToolExecutionContext,\n ): Promise<CodeModeToolResult> => {\n const { typescriptCode } = input\n\n // Get emitCustomEvent from context or use no-op\n const emitCustomEvent = toolContext?.emitCustomEvent || (() => {})\n\n if (!typescriptCode || typeof typescriptCode !== 'string') {\n return {\n success: false,\n error: {\n message: 'typescriptCode must be a non-empty string',\n name: 'ValidationError',\n },\n }\n }\n\n // Create a fresh sandbox context for this execution\n let isolateContext: IsolateContext | null = null\n\n // Emit execution started event immediately\n emitCustomEvent('code_mode:execution_started', {\n timestamp: Date.now(),\n codeLength: typescriptCode.length,\n })\n\n try {\n // Step 1: Strip TypeScript (also serves as syntax validation via the\n // transpiler — sucrase by default, or a user-supplied `transpile`)\n let strippedCode: string\n try {\n strippedCode = await transpile(typescriptCode)\n } catch (error) {\n // Type/syntax error from the transpiler\n return {\n success: false,\n error: {\n message: error instanceof Error ? error.message : String(error),\n name: 'TypeScriptError',\n },\n }\n }\n\n // Step 2: Get dynamic skill bindings if available\n const skillBindings = getSkillBindings ? await getSkillBindings() : {}\n\n // Scan dynamic bindings too — their schemas are equally in-scope for\n // the same exfiltration threat. Dedup cache prevents repeat warnings\n // when the same binding reappears across executions.\n const skillBindingValues = Object.values(skillBindings)\n if (skillBindingValues.length > 0) {\n warnIfBindingsExposeSecrets(skillBindingValues, {\n handler: onSecretParameter,\n dedupCache: secretDedupCache,\n })\n }\n\n // Step 3: Merge static and dynamic bindings, then wrap with event awareness\n const allBindings = { ...staticBindings, ...skillBindings }\n const eventAwareBindings = createEventAwareBindings(\n allBindings,\n emitCustomEvent,\n )\n\n // Step 4: Create sandbox context with event-aware bindings\n isolateContext = await driver.createContext({\n bindings: eventAwareBindings,\n timeout,\n memoryLimit,\n })\n\n // Step 5: Execute the code in the sandbox\n const executionResult = await isolateContext.execute(strippedCode)\n\n // Emit console logs as custom events\n if (executionResult.logs && executionResult.logs.length > 0) {\n for (const log of executionResult.logs) {\n // Parse log level from prefix (added by sandbox console implementation)\n let level: 'log' | 'warn' | 'error' | 'info' = 'log'\n let message = log\n\n if (log.startsWith('ERROR: ')) {\n level = 'error'\n message = log.slice(7)\n } else if (log.startsWith('WARN: ')) {\n level = 'warn'\n message = log.slice(6)\n } else if (log.startsWith('INFO: ')) {\n level = 'info'\n message = log.slice(6)\n }\n\n emitCustomEvent('code_mode:console', {\n level,\n message,\n timestamp: Date.now(),\n })\n }\n }\n\n if (executionResult.success) {\n return {\n success: true,\n result: executionResult.value,\n logs: executionResult.logs,\n }\n } else {\n return {\n success: false,\n error: executionResult.error\n ? {\n message: executionResult.error.message,\n name: executionResult.error.name,\n }\n : { message: 'Unknown execution error' },\n logs: executionResult.logs,\n }\n }\n } catch (error) {\n return {\n success: false,\n error: {\n message: error instanceof Error ? error.message : String(error),\n name: error instanceof Error ? error.name : 'Error',\n },\n }\n } finally {\n // Always clean up the sandbox context\n if (isolateContext) {\n await isolateContext.dispose()\n }\n }\n },\n )\n}\n\n/**\n * Build the tool description including available external functions\n */\nfunction buildToolDescription(tools: Array<CodeModeTool>): string {\n const eager = tools.filter((t) => !t.lazy)\n const hasLazy = tools.some((t) => t.lazy)\n const externalFunctions = eager.map((t) => `external_${t.name}`).join(', ')\n\n const discoverable = hasLazy\n ? ` Additional functions can be discovered via the discover_tools tool.`\n : ''\n\n return (\n `Execute TypeScript code in a secure sandbox environment. ` +\n `The code can use these external API functions: ${externalFunctions}.${discoverable} ` +\n `All external_* calls are async and must be awaited. ` +\n `Return a value to pass results back. Use console.log() for debugging.`\n )\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"create-code-mode-tool.js","names":[],"sources":["../../src/create-code-mode-tool.ts"],"sourcesContent":["import { z } from 'zod'\nimport { toolDefinition } from '@tanstack/ai'\nimport {\n createEventAwareBindings,\n toolsToBindings,\n} from './bindings/tool-to-binding'\nimport { stripTypeScript } from './strip-typescript'\nimport { warnIfBindingsExposeSecrets } from './validate-bindings'\nimport type { ServerTool, ToolExecutionContext } from '@tanstack/ai'\nimport type {\n CodeModeTool,\n CodeModeToolConfig,\n CodeModeToolResult,\n IsolateContext,\n} from './types'\n\n/**\n * Schema for the execute_typescript tool input\n */\nconst executeTypescriptInputSchema = z.object({\n typescriptCode: z\n .string()\n .describe(\n 'TypeScript code to execute in the sandbox. ' +\n 'Use external_* functions to call available APIs. ' +\n 'Return a value to pass results back.',\n ),\n})\n\n/**\n * Schema for the execute_typescript tool output\n */\nconst executeTypescriptOutputSchema = z.object({\n success: z.boolean().describe('Whether execution completed without errors'),\n result: z\n .unknown()\n .optional()\n .describe('Return value from the executed code'),\n logs: z\n .array(z.string())\n .optional()\n .describe('Console output captured during execution'),\n error: z\n .object({\n message: z.string(),\n name: z.string().optional(),\n line: z.number().optional(),\n })\n .optional()\n .describe('Error details if execution failed'),\n})\n\nexport type ExecuteTypescriptInput = z.infer<\n typeof executeTypescriptInputSchema\n>\nexport type ExecuteTypescriptOutput = z.infer<\n typeof executeTypescriptOutputSchema\n>\n\n/**\n * Create an execute_typescript tool that can be used alongside other agent tools.\n *\n * This tool allows an LLM to execute TypeScript code in a secure sandbox.\n * Tools passed in the config become `external_*` functions available inside the sandbox.\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], // Become external_fetchWeather, external_dbQuery\n * timeout: 30000,\n * })\n *\n * chat({\n * systemPrompts: [myPrompt, systemPrompt],\n * tools: [tool, searchTool, emailTool],\n * messages,\n * })\n * ```\n */\nexport function createCodeModeTool(\n config: CodeModeToolConfig,\n): ServerTool<\n typeof executeTypescriptInputSchema,\n typeof executeTypescriptOutputSchema,\n 'execute_typescript'\n> {\n const {\n driver,\n tools,\n timeout = 30000,\n memoryLimit = 128,\n getSkillBindings,\n onSecretParameter,\n transpile = stripTypeScript,\n } = config\n\n // Validate tools\n if (tools.length === 0) {\n throw new Error('At least one tool must be provided to createCodeModeTool')\n }\n\n // Transform tools to bindings with external_ prefix (static bindings)\n const staticBindings = toolsToBindings(tools, 'external_')\n\n // Shared across static + dynamic (skill) binding scans so a given\n // (toolName, paramPath) pair surfaces at most once per code-mode instance.\n const secretDedupCache = new Set<string>()\n\n warnIfBindingsExposeSecrets(Object.values(staticBindings), {\n handler: onSecretParameter,\n dedupCache: secretDedupCache,\n })\n\n // Create the tool definition\n const definition = toolDefinition({\n name: 'execute_typescript' as const,\n description: buildToolDescription(tools),\n inputSchema: executeTypescriptInputSchema,\n outputSchema: executeTypescriptOutputSchema,\n })\n\n // Return server tool with execute function that accepts context\n return definition.server(\n async (\n input,\n toolContext?: ToolExecutionContext,\n ): Promise<CodeModeToolResult> => {\n const { typescriptCode } = input\n\n // Get emitCustomEvent from context or use no-op\n const emitCustomEvent = toolContext?.emitCustomEvent || (() => {})\n\n if (!typescriptCode || typeof typescriptCode !== 'string') {\n return {\n success: false,\n error: {\n message: 'typescriptCode must be a non-empty string',\n name: 'ValidationError',\n },\n }\n }\n\n // Create a fresh sandbox context for this execution\n let isolateContext: IsolateContext | null = null\n\n // Emit execution started event immediately\n emitCustomEvent('code_mode:execution_started', {\n timestamp: Date.now(),\n codeLength: typescriptCode.length,\n })\n\n try {\n // Step 1: Strip TypeScript (also serves as syntax validation via the\n // transpiler — sucrase by default, or a user-supplied `transpile`)\n let strippedCode: string\n try {\n strippedCode = await transpile(typescriptCode)\n } catch (error) {\n // Type/syntax error from the transpiler\n return {\n success: false,\n error: {\n message: error instanceof Error ? error.message : String(error),\n name: 'TypeScriptError',\n },\n }\n }\n\n // Step 2: Get dynamic skill bindings if available\n const skillBindings = getSkillBindings ? await getSkillBindings() : {}\n\n // Scan dynamic bindings too — their schemas are equally in-scope for\n // the same exfiltration threat. Dedup cache prevents repeat warnings\n // when the same binding reappears across executions.\n const skillBindingValues = Object.values(skillBindings)\n if (skillBindingValues.length > 0) {\n warnIfBindingsExposeSecrets(skillBindingValues, {\n handler: onSecretParameter,\n dedupCache: secretDedupCache,\n })\n }\n\n // Step 3: Merge static and dynamic bindings, then wrap with event awareness\n const allBindings = { ...staticBindings, ...skillBindings }\n const eventAwareBindings = createEventAwareBindings(\n allBindings,\n emitCustomEvent,\n )\n\n // Step 4: Create sandbox context with event-aware bindings\n isolateContext = await driver.createContext({\n bindings: eventAwareBindings,\n timeout,\n memoryLimit,\n })\n\n // Step 5: Execute the code in the sandbox\n const executionResult = await isolateContext.execute(strippedCode)\n\n // Emit console logs as custom events\n if (executionResult.logs && executionResult.logs.length > 0) {\n for (const log of executionResult.logs) {\n // Parse log level from prefix (added by sandbox console implementation)\n let level: 'log' | 'warn' | 'error' | 'info' = 'log'\n let message = log\n\n if (log.startsWith('ERROR: ')) {\n level = 'error'\n message = log.slice(7)\n } else if (log.startsWith('WARN: ')) {\n level = 'warn'\n message = log.slice(6)\n } else if (log.startsWith('INFO: ')) {\n level = 'info'\n message = log.slice(6)\n }\n\n emitCustomEvent('code_mode:console', {\n level,\n message,\n timestamp: Date.now(),\n })\n }\n }\n\n if (executionResult.success) {\n return {\n success: true,\n result: executionResult.value,\n logs: executionResult.logs,\n }\n } else {\n return {\n success: false,\n error: executionResult.error\n ? {\n message: executionResult.error.message,\n name: executionResult.error.name,\n }\n : { message: 'Unknown execution error' },\n logs: executionResult.logs,\n }\n }\n } catch (error) {\n return {\n success: false,\n error: {\n message: error instanceof Error ? error.message : String(error),\n name: error instanceof Error ? error.name : 'Error',\n },\n }\n } finally {\n // Always clean up the sandbox context\n if (isolateContext) {\n await isolateContext.dispose()\n }\n }\n },\n )\n}\n\n/**\n * Build the tool description including available external functions\n */\nfunction buildToolDescription(tools: Array<CodeModeTool>): string {\n const eager = tools.filter((t) => !t.lazy)\n const hasLazy = tools.some((t) => t.lazy)\n const externalFunctions = eager.map((t) => `external_${t.name}`).join(', ')\n\n const discoverable = hasLazy\n ? ` Additional functions can be discovered via the discover_tools tool.`\n : ''\n\n return (\n `Execute TypeScript code in a secure sandbox environment. ` +\n `The code can use these external API functions: ${externalFunctions}.${discoverable} ` +\n `All external_* calls are async and must be awaited. ` +\n `Return a value to pass results back. Use console.log() for debugging.`\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,19 +1,41 @@
|
|
|
1
1
|
import { createCodeModeTool } from "./create-code-mode-tool.js";
|
|
2
2
|
import { createCodeModeSystemPrompt } from "./create-system-prompt.js";
|
|
3
3
|
import { createDiscoveryTool } from "./create-discovery-tool.js";
|
|
4
|
+
//#region src/create-code-mode.ts
|
|
5
|
+
/**
|
|
6
|
+
* Create the `execute_typescript` tool, its matching system prompt, and (when
|
|
7
|
+
* any tools are marked `lazy: true`) a `discover_tools` companion tool.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { createCodeMode } from '@tanstack/ai-code-mode'
|
|
12
|
+
* import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'
|
|
13
|
+
*
|
|
14
|
+
* const { tools, systemPrompt } = createCodeMode({
|
|
15
|
+
* driver: createNodeIsolateDriver(),
|
|
16
|
+
* tools: [weatherTool, rarelyUsedTool], // mark rarelyUsedTool lazy: true
|
|
17
|
+
* })
|
|
18
|
+
*
|
|
19
|
+
* chat({
|
|
20
|
+
* systemPrompts: [myPrompt, systemPrompt],
|
|
21
|
+
* tools: [...tools, ...otherTools],
|
|
22
|
+
* messages,
|
|
23
|
+
* })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
4
26
|
function createCodeMode(config) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
27
|
+
const tool = createCodeModeTool(config);
|
|
28
|
+
const systemPrompt = createCodeModeSystemPrompt(config);
|
|
29
|
+
const lazyTools = config.tools.filter((t) => t.lazy);
|
|
30
|
+
const discoveryTool = lazyTools.length > 0 ? createDiscoveryTool(lazyTools, config.lazyToolsConfig) : null;
|
|
31
|
+
return {
|
|
32
|
+
tool,
|
|
33
|
+
discoveryTool,
|
|
34
|
+
tools: discoveryTool ? [tool, discoveryTool] : [tool],
|
|
35
|
+
systemPrompt
|
|
36
|
+
};
|
|
15
37
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
//# sourceMappingURL=create-code-mode.js.map
|
|
38
|
+
//#endregion
|
|
39
|
+
export { createCodeMode };
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=create-code-mode.js.map
|
|
@@ -1 +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 { createDiscoveryTool } from './create-discovery-tool'\nimport type { CodeModeToolConfig, CreateCodeModeResult } from './types'\n\n/**\n * Create the `execute_typescript` tool, its matching system prompt, and (when\n * any tools are marked `lazy: true`) a `discover_tools` companion tool.\n *\n * @example\n * ```typescript\n * import { createCodeMode } from '@tanstack/ai-code-mode'\n * import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'\n *\n * const { tools, systemPrompt } = createCodeMode({\n * driver: createNodeIsolateDriver(),\n * tools: [weatherTool, rarelyUsedTool], // mark rarelyUsedTool lazy: true\n * })\n *\n * chat({\n * systemPrompts: [myPrompt, systemPrompt],\n * tools: [...tools, ...otherTools],\n * messages,\n * })\n * ```\n */\nexport function createCodeMode(\n config: CodeModeToolConfig,\n): CreateCodeModeResult {\n const tool = createCodeModeTool(config)\n const systemPrompt = createCodeModeSystemPrompt(config)\n\n const lazyTools = config.tools.filter((t) => t.lazy)\n const discoveryTool =\n lazyTools.length > 0\n ? createDiscoveryTool(lazyTools, config.lazyToolsConfig)\n : null\n\n return {\n tool,\n discoveryTool,\n tools: discoveryTool ? [tool, discoveryTool] : [tool],\n systemPrompt,\n }\n}\n"],"
|
|
1
|
+
{"version":3,"file":"create-code-mode.js","names":[],"sources":["../../src/create-code-mode.ts"],"sourcesContent":["import { createCodeModeTool } from './create-code-mode-tool'\nimport { createCodeModeSystemPrompt } from './create-system-prompt'\nimport { createDiscoveryTool } from './create-discovery-tool'\nimport type { CodeModeToolConfig, CreateCodeModeResult } from './types'\n\n/**\n * Create the `execute_typescript` tool, its matching system prompt, and (when\n * any tools are marked `lazy: true`) a `discover_tools` companion tool.\n *\n * @example\n * ```typescript\n * import { createCodeMode } from '@tanstack/ai-code-mode'\n * import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'\n *\n * const { tools, systemPrompt } = createCodeMode({\n * driver: createNodeIsolateDriver(),\n * tools: [weatherTool, rarelyUsedTool], // mark rarelyUsedTool lazy: true\n * })\n *\n * chat({\n * systemPrompts: [myPrompt, systemPrompt],\n * tools: [...tools, ...otherTools],\n * messages,\n * })\n * ```\n */\nexport function createCodeMode(\n config: CodeModeToolConfig,\n): CreateCodeModeResult {\n const tool = createCodeModeTool(config)\n const systemPrompt = createCodeModeSystemPrompt(config)\n\n const lazyTools = config.tools.filter((t) => t.lazy)\n const discoveryTool =\n lazyTools.length > 0\n ? createDiscoveryTool(lazyTools, config.lazyToolsConfig)\n : null\n\n return {\n tool,\n discoveryTool,\n tools: discoveryTool ? [tool, discoveryTool] : [tool],\n systemPrompt,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,eACd,QACsB;CACtB,MAAM,OAAO,mBAAmB,MAAM;CACtC,MAAM,eAAe,2BAA2B,MAAM;CAEtD,MAAM,YAAY,OAAO,MAAM,QAAQ,MAAM,EAAE,IAAI;CACnD,MAAM,gBACJ,UAAU,SAAS,IACf,oBAAoB,WAAW,OAAO,eAAe,IACrD;CAEN,OAAO;EACL;EACA;EACA,OAAO,gBAAgB,CAAC,MAAM,aAAa,IAAI,CAAC,IAAI;EACpD;CACF;AACF"}
|
|
@@ -1,62 +1,70 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { renderLazyCatalogEntry, toolDefinition } from "@tanstack/ai";
|
|
3
1
|
import { toolToBinding } from "./bindings/tool-to-binding.js";
|
|
4
2
|
import { generateTypeStubs } from "./type-generator/json-schema-to-ts.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
})
|
|
17
|
-
),
|
|
18
|
-
errors: z.array(z.string()).optional()
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { renderLazyCatalogEntry, toolDefinition } from "@tanstack/ai";
|
|
5
|
+
//#region src/create-discovery-tool.ts
|
|
6
|
+
var discoverInputSchema = z.object({ toolNames: z.array(z.string()).describe("Names of tools to discover, exactly as shown in the Discoverable APIs catalog. The external_ prefix is optional — both \"external_fetchStocks\" and \"fetchStocks\" resolve.") });
|
|
7
|
+
var discoverOutputSchema = z.object({
|
|
8
|
+
tools: z.array(z.object({
|
|
9
|
+
name: z.string().describe("The sandbox function name, e.g. external_fetchStocks"),
|
|
10
|
+
description: z.string(),
|
|
11
|
+
typeStub: z.string().describe("TypeScript declaration for the function")
|
|
12
|
+
})),
|
|
13
|
+
errors: z.array(z.string()).optional()
|
|
19
14
|
});
|
|
20
|
-
|
|
15
|
+
var EXTERNAL_PREFIX = "external_";
|
|
16
|
+
/**
|
|
17
|
+
* Strip a single leading `external_` prefix so the model can pass either the
|
|
18
|
+
* catalog name (`external_fetchStocks`) or the bare name (`fetchStocks`).
|
|
19
|
+
*/
|
|
21
20
|
function stripExternalPrefix(name) {
|
|
22
|
-
|
|
21
|
+
return name.startsWith(EXTERNAL_PREFIX) ? name.slice(9) : name;
|
|
23
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Build the `discover_tools` sibling tool for Code Mode lazy tools. The model
|
|
25
|
+
* calls it with lazy tool names and receives each one's TypeScript type stub +
|
|
26
|
+
* description, which it can then use to write correctly-typed `external_*`
|
|
27
|
+
* calls inside `execute_typescript`. The bindings themselves are always present
|
|
28
|
+
* in the sandbox — this only reveals documentation.
|
|
29
|
+
*
|
|
30
|
+
* Tools are catalogued in `external_<name>` form to match the "Discoverable
|
|
31
|
+
* APIs" section of the Code Mode system prompt; lookups tolerate either form.
|
|
32
|
+
* `lazyToolsConfig.includeDescription` controls how much of each tool's
|
|
33
|
+
* description appears in this tool's own catalog (mirroring the system prompt).
|
|
34
|
+
*/
|
|
24
35
|
function createDiscoveryTool(lazyTools, lazyToolsConfig) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
return errors.length > 0 ? { tools, errors } : { tools };
|
|
57
|
-
});
|
|
36
|
+
const lazyMap = new Map(lazyTools.map((t) => [t.name, t]));
|
|
37
|
+
const include = lazyToolsConfig?.includeDescription ?? "none";
|
|
38
|
+
const catalog = lazyTools.map((t) => renderLazyCatalogEntry(`${EXTERNAL_PREFIX}${t.name}`, t.description, include)).join(", ");
|
|
39
|
+
return toolDefinition({
|
|
40
|
+
name: "discover_tools",
|
|
41
|
+
description: `Discover full TypeScript signatures for additional sandbox APIs before using them inside execute_typescript. Discoverable tools: [${catalog}]. Pass the names exactly as shown (the external_ prefix is optional).`,
|
|
42
|
+
inputSchema: discoverInputSchema,
|
|
43
|
+
outputSchema: discoverOutputSchema
|
|
44
|
+
}).server(async ({ toolNames }) => {
|
|
45
|
+
const tools = [];
|
|
46
|
+
const errors = [];
|
|
47
|
+
for (const name of toolNames) {
|
|
48
|
+
const tool = lazyMap.get(stripExternalPrefix(name));
|
|
49
|
+
if (!tool) {
|
|
50
|
+
errors.push(`Unknown tool: '${name}'. Discoverable tools: [${catalog}]`);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const binding = toolToBinding(tool, EXTERNAL_PREFIX);
|
|
54
|
+
const typeStub = generateTypeStubs({ [binding.name]: binding });
|
|
55
|
+
tools.push({
|
|
56
|
+
name: binding.name,
|
|
57
|
+
description: tool.description,
|
|
58
|
+
typeStub
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return errors.length > 0 ? {
|
|
62
|
+
tools,
|
|
63
|
+
errors
|
|
64
|
+
} : { tools };
|
|
65
|
+
});
|
|
58
66
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
//# sourceMappingURL=create-discovery-tool.js.map
|
|
67
|
+
//#endregion
|
|
68
|
+
export { createDiscoveryTool };
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=create-discovery-tool.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-discovery-tool.js","sources":["../../src/create-discovery-tool.ts"],"sourcesContent":["import { z } from 'zod'\nimport { renderLazyCatalogEntry, toolDefinition } from '@tanstack/ai'\nimport { toolToBinding } from './bindings/tool-to-binding'\nimport { generateTypeStubs } from './type-generator/json-schema-to-ts'\nimport type { LazyToolsConfig, ServerTool } from '@tanstack/ai'\nimport type { CodeModeTool } from './types'\n\nconst discoverInputSchema = z.object({\n toolNames: z\n .array(z.string())\n .describe(\n 'Names of tools to discover, exactly as shown in the Discoverable APIs ' +\n 'catalog. The external_ prefix is optional — both \"external_fetchStocks\" ' +\n 'and \"fetchStocks\" resolve.',\n ),\n})\n\nconst discoverOutputSchema = z.object({\n tools: z.array(\n z.object({\n name: z\n .string()\n .describe('The sandbox function name, e.g. external_fetchStocks'),\n description: z.string(),\n typeStub: z.string().describe('TypeScript declaration for the function'),\n }),\n ),\n errors: z.array(z.string()).optional(),\n})\n\nconst EXTERNAL_PREFIX = 'external_'\n\n/**\n * Strip a single leading `external_` prefix so the model can pass either the\n * catalog name (`external_fetchStocks`) or the bare name (`fetchStocks`).\n */\nfunction stripExternalPrefix(name: string): string {\n return name.startsWith(EXTERNAL_PREFIX)\n ? name.slice(EXTERNAL_PREFIX.length)\n : name\n}\n\n/**\n * Build the `discover_tools` sibling tool for Code Mode lazy tools. The model\n * calls it with lazy tool names and receives each one's TypeScript type stub +\n * description, which it can then use to write correctly-typed `external_*`\n * calls inside `execute_typescript`. The bindings themselves are always present\n * in the sandbox — this only reveals documentation.\n *\n * Tools are catalogued in `external_<name>` form to match the \"Discoverable\n * APIs\" section of the Code Mode system prompt; lookups tolerate either form.\n * `lazyToolsConfig.includeDescription` controls how much of each tool's\n * description appears in this tool's own catalog (mirroring the system prompt).\n */\nexport function createDiscoveryTool(\n lazyTools: Array<CodeModeTool>,\n lazyToolsConfig?: LazyToolsConfig,\n): ServerTool<\n typeof discoverInputSchema,\n typeof discoverOutputSchema,\n 'discover_tools'\n> {\n const lazyMap = new Map(lazyTools.map((t) => [t.name, t]))\n const include = lazyToolsConfig?.includeDescription ?? 'none'\n const catalog = lazyTools\n .map((t) =>\n renderLazyCatalogEntry(\n `${EXTERNAL_PREFIX}${t.name}`,\n t.description,\n include,\n ),\n )\n .join(', ')\n\n return toolDefinition({\n name: 'discover_tools' as const,\n description:\n `Discover full TypeScript signatures for additional sandbox APIs before ` +\n `using them inside execute_typescript. Discoverable tools: [${catalog}]. ` +\n `Pass the names exactly as shown (the external_ prefix is optional).`,\n inputSchema: discoverInputSchema,\n outputSchema: discoverOutputSchema,\n }).server(async ({ toolNames }) => {\n const tools: Array<{\n name: string\n description: string\n typeStub: string\n }> = []\n const errors: Array<string> = []\n\n for (const name of toolNames) {\n const tool = lazyMap.get(stripExternalPrefix(name))\n if (!tool) {\n errors.push(`Unknown tool: '${name}'. Discoverable tools: [${catalog}]`)\n continue\n }\n const binding = toolToBinding(tool, EXTERNAL_PREFIX)\n const typeStub = generateTypeStubs({ [binding.name]: binding })\n tools.push({\n name: binding.name,\n description: tool.description,\n typeStub,\n })\n }\n\n return errors.length > 0 ? { tools, errors } : { tools }\n })\n}\n"],"
|
|
1
|
+
{"version":3,"file":"create-discovery-tool.js","names":[],"sources":["../../src/create-discovery-tool.ts"],"sourcesContent":["import { z } from 'zod'\nimport { renderLazyCatalogEntry, toolDefinition } from '@tanstack/ai'\nimport { toolToBinding } from './bindings/tool-to-binding'\nimport { generateTypeStubs } from './type-generator/json-schema-to-ts'\nimport type { LazyToolsConfig, ServerTool } from '@tanstack/ai'\nimport type { CodeModeTool } from './types'\n\nconst discoverInputSchema = z.object({\n toolNames: z\n .array(z.string())\n .describe(\n 'Names of tools to discover, exactly as shown in the Discoverable APIs ' +\n 'catalog. The external_ prefix is optional — both \"external_fetchStocks\" ' +\n 'and \"fetchStocks\" resolve.',\n ),\n})\n\nconst discoverOutputSchema = z.object({\n tools: z.array(\n z.object({\n name: z\n .string()\n .describe('The sandbox function name, e.g. external_fetchStocks'),\n description: z.string(),\n typeStub: z.string().describe('TypeScript declaration for the function'),\n }),\n ),\n errors: z.array(z.string()).optional(),\n})\n\nconst EXTERNAL_PREFIX = 'external_'\n\n/**\n * Strip a single leading `external_` prefix so the model can pass either the\n * catalog name (`external_fetchStocks`) or the bare name (`fetchStocks`).\n */\nfunction stripExternalPrefix(name: string): string {\n return name.startsWith(EXTERNAL_PREFIX)\n ? name.slice(EXTERNAL_PREFIX.length)\n : name\n}\n\n/**\n * Build the `discover_tools` sibling tool for Code Mode lazy tools. The model\n * calls it with lazy tool names and receives each one's TypeScript type stub +\n * description, which it can then use to write correctly-typed `external_*`\n * calls inside `execute_typescript`. The bindings themselves are always present\n * in the sandbox — this only reveals documentation.\n *\n * Tools are catalogued in `external_<name>` form to match the \"Discoverable\n * APIs\" section of the Code Mode system prompt; lookups tolerate either form.\n * `lazyToolsConfig.includeDescription` controls how much of each tool's\n * description appears in this tool's own catalog (mirroring the system prompt).\n */\nexport function createDiscoveryTool(\n lazyTools: Array<CodeModeTool>,\n lazyToolsConfig?: LazyToolsConfig,\n): ServerTool<\n typeof discoverInputSchema,\n typeof discoverOutputSchema,\n 'discover_tools'\n> {\n const lazyMap = new Map(lazyTools.map((t) => [t.name, t]))\n const include = lazyToolsConfig?.includeDescription ?? 'none'\n const catalog = lazyTools\n .map((t) =>\n renderLazyCatalogEntry(\n `${EXTERNAL_PREFIX}${t.name}`,\n t.description,\n include,\n ),\n )\n .join(', ')\n\n return toolDefinition({\n name: 'discover_tools' as const,\n description:\n `Discover full TypeScript signatures for additional sandbox APIs before ` +\n `using them inside execute_typescript. Discoverable tools: [${catalog}]. ` +\n `Pass the names exactly as shown (the external_ prefix is optional).`,\n inputSchema: discoverInputSchema,\n outputSchema: discoverOutputSchema,\n }).server(async ({ toolNames }) => {\n const tools: Array<{\n name: string\n description: string\n typeStub: string\n }> = []\n const errors: Array<string> = []\n\n for (const name of toolNames) {\n const tool = lazyMap.get(stripExternalPrefix(name))\n if (!tool) {\n errors.push(`Unknown tool: '${name}'. Discoverable tools: [${catalog}]`)\n continue\n }\n const binding = toolToBinding(tool, EXTERNAL_PREFIX)\n const typeStub = generateTypeStubs({ [binding.name]: binding })\n tools.push({\n name: binding.name,\n description: tool.description,\n typeStub,\n })\n }\n\n return errors.length > 0 ? { tools, errors } : { tools }\n })\n}\n"],"mappings":";;;;;AAOA,IAAM,sBAAsB,EAAE,OAAO,EACnC,WAAW,EACR,MAAM,EAAE,OAAO,CAAC,CAAC,CACjB,SACC,8KAGF,EACJ,CAAC;AAED,IAAM,uBAAuB,EAAE,OAAO;CACpC,OAAO,EAAE,MACP,EAAE,OAAO;EACP,MAAM,EACH,OAAO,CAAC,CACR,SAAS,sDAAsD;EAClE,aAAa,EAAE,OAAO;EACtB,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS,yCAAyC;CACzE,CAAC,CACH;CACA,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS;AACvC,CAAC;AAED,IAAM,kBAAkB;;;;;AAMxB,SAAS,oBAAoB,MAAsB;CACjD,OAAO,KAAK,WAAW,eAAe,IAClC,KAAK,MAAM,CAAsB,IACjC;AACN;;;;;;;;;;;;;AAcA,SAAgB,oBACd,WACA,iBAKA;CACA,MAAM,UAAU,IAAI,IAAI,UAAU,KAAK,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;CACzD,MAAM,UAAU,iBAAiB,sBAAsB;CACvD,MAAM,UAAU,UACb,KAAK,MACJ,uBACE,GAAG,kBAAkB,EAAE,QACvB,EAAE,aACF,OACF,CACF,CAAC,CACA,KAAK,IAAI;CAEZ,OAAO,eAAe;EACpB,MAAM;EACN,aACE,qIAC8D,QAAQ;EAExE,aAAa;EACb,cAAc;CAChB,CAAC,CAAC,CAAC,OAAO,OAAO,EAAE,gBAAgB;EACjC,MAAM,QAID,CAAC;EACN,MAAM,SAAwB,CAAC;EAE/B,KAAK,MAAM,QAAQ,WAAW;GAC5B,MAAM,OAAO,QAAQ,IAAI,oBAAoB,IAAI,CAAC;GAClD,IAAI,CAAC,MAAM;IACT,OAAO,KAAK,kBAAkB,KAAK,0BAA0B,QAAQ,EAAE;IACvE;GACF;GACA,MAAM,UAAU,cAAc,MAAM,eAAe;GACnD,MAAM,WAAW,kBAAkB,GAAG,QAAQ,OAAO,QAAQ,CAAC;GAC9D,MAAM,KAAK;IACT,MAAM,QAAQ;IACd,aAAa,KAAK;IAClB;GACF,CAAC;EACH;EAEA,OAAO,OAAO,SAAS,IAAI;GAAE;GAAO;EAAO,IAAI,EAAE,MAAM;CACzD,CAAC;AACH"}
|