@tanstack/ai-code-mode 0.2.13 → 0.3.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 (36) hide show
  1. package/package.json +3 -3
  2. package/skills/ai-code-mode/SKILL.md +79 -0
  3. package/src/create-code-mode-tool.ts +8 -2
  4. package/src/create-code-mode.ts +23 -13
  5. package/src/create-discovery-tool.ts +108 -0
  6. package/src/create-system-prompt.ts +25 -10
  7. package/src/index.ts +2 -0
  8. package/src/types.ts +25 -0
  9. package/dist/esm/agent-store.d.ts +0 -21
  10. package/dist/esm/agent-store.js +0 -29
  11. package/dist/esm/agent-store.js.map +0 -1
  12. package/dist/esm/bindings/tool-to-binding.d.ts +0 -25
  13. package/dist/esm/bindings/tool-to-binding.js +0 -94
  14. package/dist/esm/bindings/tool-to-binding.js.map +0 -1
  15. package/dist/esm/code-wrapper.d.ts +0 -9
  16. package/dist/esm/code-wrapper.js +0 -18
  17. package/dist/esm/code-wrapper.js.map +0 -1
  18. package/dist/esm/create-code-mode-tool.d.ts +0 -50
  19. package/dist/esm/create-code-mode-tool.js +0 -143
  20. package/dist/esm/create-code-mode-tool.js.map +0 -1
  21. package/dist/esm/create-code-mode.d.ts +0 -41
  22. package/dist/esm/create-code-mode.js +0 -12
  23. package/dist/esm/create-code-mode.js.map +0 -1
  24. package/dist/esm/create-system-prompt.d.ts +0 -24
  25. package/dist/esm/create-system-prompt.js +0 -66
  26. package/dist/esm/create-system-prompt.js.map +0 -1
  27. package/dist/esm/index.d.ts +0 -10
  28. package/dist/esm/index.js +0 -23
  29. package/dist/esm/index.js.map +0 -1
  30. package/dist/esm/strip-typescript.d.ts +0 -44
  31. package/dist/esm/strip-typescript.js +0 -49
  32. package/dist/esm/strip-typescript.js.map +0 -1
  33. package/dist/esm/type-generator/json-schema-to-ts.d.ts +0 -31
  34. package/dist/esm/type-generator/json-schema-to-ts.js +0 -100
  35. package/dist/esm/type-generator/json-schema-to-ts.js.map +0 -1
  36. package/dist/esm/types.d.ts +0 -216
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-code-mode",
3
- "version": "0.2.13",
3
+ "version": "0.3.0",
4
4
  "description": "Secure TypeScript Code Mode for TanStack AI agents to execute sandboxed tool orchestration programs.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -54,12 +54,12 @@
54
54
  },
55
55
  "peerDependencies": {
56
56
  "zod": "^3.0.0 || ^4.0.0",
57
- "@tanstack/ai": "0.35.0"
57
+ "@tanstack/ai": "0.36.0"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@vitest/coverage-v8": "4.0.14",
61
61
  "zod": "^4.2.0",
62
- "@tanstack/ai": "0.35.0"
62
+ "@tanstack/ai": "0.36.0"
63
63
  },
64
64
  "scripts": {
65
65
  "build": "vite build",
@@ -15,6 +15,7 @@ sources:
15
15
  - 'TanStack/ai:docs/code-mode/code-mode-isolates.md'
16
16
  - 'TanStack/ai:docs/code-mode/code-mode-with-skills.md'
17
17
  - 'TanStack/ai:docs/code-mode/client-integration.md'
18
+ - 'TanStack/ai:docs/code-mode/lazy-tools.md'
18
19
  ---
19
20
 
20
21
  > **Note**: This skill requires familiarity with ai-core and ai-core/chat-experience. Code Mode is always used on top of a chat experience.
@@ -328,6 +329,84 @@ Skill-specific events (when using `codeModeWithSkills`):
328
329
  | `code_mode:skill_error` | Skill failed | `skill`, `error`, `duration` |
329
330
  | `skill:registered` | New skill saved | `id`, `name`, `description` |
330
331
 
332
+ ### 4. Lazy Tools
333
+
334
+ When a large tool catalog would bloat the `execute_typescript` system prompt, mark low-priority tools `lazy: true`. Lazy tools are kept out of the full type-stub documentation and listed in a compact "Discoverable APIs" catalog instead. All sandbox bindings are always injected — `lazy` defers documentation, not callability.
335
+
336
+ **Marking a tool lazy:**
337
+
338
+ ```typescript
339
+ import { toolDefinition } from '@tanstack/ai'
340
+ import { z } from 'zod'
341
+
342
+ const rarelyUsedTool = toolDefinition({
343
+ name: 'fetchStocks',
344
+ description: 'Get stock prices for a ticker. Returns a price quote.',
345
+ inputSchema: z.object({ ticker: z.string() }),
346
+ outputSchema: z.object({ price: z.number() }),
347
+ lazy: true, // <-- opt out of full system-prompt documentation
348
+ }).server(async ({ ticker }) => {
349
+ // ...
350
+ return { price: 0 }
351
+ })
352
+ ```
353
+
354
+ **`createCodeMode` return shape:**
355
+
356
+ `createCodeMode()` returns `{ tool, discoveryTool, tools, systemPrompt }`. When lazy tools are present `discoveryTool` is a `discover_tools` server tool; otherwise it is `null`. Always spread `tools` (not just `tool`) into `chat()` so the discovery tool is registered:
357
+
358
+ ```typescript
359
+ import { chat } from '@tanstack/ai'
360
+ import { createCodeMode } from '@tanstack/ai-code-mode'
361
+ import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'
362
+ import { openaiText } from '@tanstack/ai-openai'
363
+
364
+ const { tools, systemPrompt } = createCodeMode({
365
+ driver: createNodeIsolateDriver(),
366
+ tools: [eagerTool, rarelyUsedTool], // rarelyUsedTool has lazy: true
367
+ })
368
+
369
+ const stream = chat({
370
+ adapter: openaiText('gpt-5.5'),
371
+ systemPrompts: ['You are a helpful assistant.', systemPrompt],
372
+ tools: [...tools, ...otherTools], // spread tools, not just tool
373
+ messages,
374
+ })
375
+ ```
376
+
377
+ `tools` equals `[tool]` when there are no lazy tools (backward compatible) and `[tool, discoveryTool]` when lazy tools exist.
378
+
379
+ **`discover_tools` flow:**
380
+
381
+ When the model encounters a lazy tool it has not seen before, it calls `discover_tools` with the bare name (no `external_` prefix). The tool returns each requested tool's TypeScript type stub and description. The model then writes correctly-typed `external_<name>` calls inside `execute_typescript`.
382
+
383
+ ```text
384
+ Model sees: "Discoverable APIs: external_fetchStocks"
385
+ Model calls: discover_tools({ toolNames: ["fetchStocks"] })
386
+ Response: { tools: [{ name: "external_fetchStocks", description: "...", typeStub: "declare function external_fetchStocks(...)" }] }
387
+ Model writes inside execute_typescript: const result = await external_fetchStocks({ ticker: "AAPL" })
388
+ ```
389
+
390
+ **`lazyToolsConfig.includeDescription`:**
391
+
392
+ Control how much of each lazy tool's description appears in the Discoverable APIs catalog (the pre-discovery list):
393
+
394
+ | Value | Catalog entry |
395
+ | ------------------ | ----------------------------------------------------------------- |
396
+ | `'none'` | `external_fetchStocks` (name only — default) |
397
+ | `'first-sentence'` | `external_fetchStocks — Get stock prices.` |
398
+ | `'full'` | `external_fetchStocks — Get stock prices. Returns a price quote.` |
399
+
400
+ ```typescript
401
+ const { tools, systemPrompt } = createCodeMode({
402
+ driver: createNodeIsolateDriver(),
403
+ tools: [eagerTool, rarelyUsedTool],
404
+ lazyToolsConfig: { includeDescription: 'first-sentence' },
405
+ })
406
+ ```
407
+
408
+ The same `lazyToolsConfig` option is accepted by plain `chat()` for its own lazy-tool discovery catalog (see `ai-core/tool-calling/SKILL.md`).
409
+
331
410
  ## Common Mistakes
332
411
 
333
412
  ### CRITICAL: Passing API keys or secrets to the sandbox environment
@@ -245,11 +245,17 @@ export function createCodeModeTool(
245
245
  * Build the tool description including available external functions
246
246
  */
247
247
  function buildToolDescription(tools: Array<CodeModeTool>): string {
248
- const externalFunctions = tools.map((t) => `external_${t.name}`).join(', ')
248
+ const eager = tools.filter((t) => !t.lazy)
249
+ const hasLazy = tools.some((t) => t.lazy)
250
+ const externalFunctions = eager.map((t) => `external_${t.name}`).join(', ')
251
+
252
+ const discoverable = hasLazy
253
+ ? ` Additional functions can be discovered via the discover_tools tool.`
254
+ : ''
249
255
 
250
256
  return (
251
257
  `Execute TypeScript code in a secure sandbox environment. ` +
252
- `The code can use these external API functions: ${externalFunctions}. ` +
258
+ `The code can use these external API functions: ${externalFunctions}.${discoverable} ` +
253
259
  `All external_* calls are async and must be awaited. ` +
254
260
  `Return a value to pass results back. Use console.log() for debugging.`
255
261
  )
@@ -1,35 +1,45 @@
1
1
  import { createCodeModeTool } from './create-code-mode-tool'
2
2
  import { createCodeModeSystemPrompt } from './create-system-prompt'
3
- import type { CodeModeToolConfig } from './types'
3
+ import { createDiscoveryTool } from './create-discovery-tool'
4
+ import type { CodeModeToolConfig, CreateCodeModeResult } from './types'
4
5
 
5
6
  /**
6
- * Create both the `execute_typescript` tool and its matching system prompt
7
- * from a single config object.
8
- *
9
- * This is the recommended way to set up Code Mode — it ensures the tool and
10
- * system prompt always stay in sync.
7
+ * Create the `execute_typescript` tool, its matching system prompt, and (when
8
+ * any tools are marked `lazy: true`) a `discover_tools` companion tool.
11
9
  *
12
10
  * @example
13
11
  * ```typescript
14
12
  * import { createCodeMode } from '@tanstack/ai-code-mode'
15
13
  * import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'
16
14
  *
17
- * const { tool, systemPrompt } = createCodeMode({
15
+ * const { tools, systemPrompt } = createCodeMode({
18
16
  * driver: createNodeIsolateDriver(),
19
- * tools: [weatherTool, dbTool],
20
- * timeout: 30000,
17
+ * tools: [weatherTool, rarelyUsedTool], // mark rarelyUsedTool lazy: true
21
18
  * })
22
19
  *
23
20
  * chat({
24
21
  * systemPrompts: [myPrompt, systemPrompt],
25
- * tools: [tool, ...otherTools],
22
+ * tools: [...tools, ...otherTools],
26
23
  * messages,
27
24
  * })
28
25
  * ```
29
26
  */
30
- export function createCodeMode(config: CodeModeToolConfig) {
27
+ export function createCodeMode(
28
+ config: CodeModeToolConfig,
29
+ ): CreateCodeModeResult {
30
+ const tool = createCodeModeTool(config)
31
+ const systemPrompt = createCodeModeSystemPrompt(config)
32
+
33
+ const lazyTools = config.tools.filter((t) => t.lazy)
34
+ const discoveryTool =
35
+ lazyTools.length > 0
36
+ ? createDiscoveryTool(lazyTools, config.lazyToolsConfig)
37
+ : null
38
+
31
39
  return {
32
- tool: createCodeModeTool(config),
33
- systemPrompt: createCodeModeSystemPrompt(config),
40
+ tool,
41
+ discoveryTool,
42
+ tools: discoveryTool ? [tool, discoveryTool] : [tool],
43
+ systemPrompt,
34
44
  }
35
45
  }
@@ -0,0 +1,108 @@
1
+ import { z } from 'zod'
2
+ import { renderLazyCatalogEntry, toolDefinition } from '@tanstack/ai'
3
+ import { toolToBinding } from './bindings/tool-to-binding'
4
+ import { generateTypeStubs } from './type-generator/json-schema-to-ts'
5
+ import type { LazyToolsConfig, ServerTool } from '@tanstack/ai'
6
+ import type { CodeModeTool } from './types'
7
+
8
+ const discoverInputSchema = z.object({
9
+ toolNames: z
10
+ .array(z.string())
11
+ .describe(
12
+ 'Names of tools to discover, exactly as shown in the Discoverable APIs ' +
13
+ 'catalog. The external_ prefix is optional — both "external_fetchStocks" ' +
14
+ 'and "fetchStocks" resolve.',
15
+ ),
16
+ })
17
+
18
+ const discoverOutputSchema = z.object({
19
+ tools: z.array(
20
+ z.object({
21
+ name: z
22
+ .string()
23
+ .describe('The sandbox function name, e.g. external_fetchStocks'),
24
+ description: z.string(),
25
+ typeStub: z.string().describe('TypeScript declaration for the function'),
26
+ }),
27
+ ),
28
+ errors: z.array(z.string()).optional(),
29
+ })
30
+
31
+ const EXTERNAL_PREFIX = 'external_'
32
+
33
+ /**
34
+ * Strip a single leading `external_` prefix so the model can pass either the
35
+ * catalog name (`external_fetchStocks`) or the bare name (`fetchStocks`).
36
+ */
37
+ function stripExternalPrefix(name: string): string {
38
+ return name.startsWith(EXTERNAL_PREFIX)
39
+ ? name.slice(EXTERNAL_PREFIX.length)
40
+ : name
41
+ }
42
+
43
+ /**
44
+ * Build the `discover_tools` sibling tool for Code Mode lazy tools. The model
45
+ * calls it with lazy tool names and receives each one's TypeScript type stub +
46
+ * description, which it can then use to write correctly-typed `external_*`
47
+ * calls inside `execute_typescript`. The bindings themselves are always present
48
+ * in the sandbox — this only reveals documentation.
49
+ *
50
+ * Tools are catalogued in `external_<name>` form to match the "Discoverable
51
+ * APIs" section of the Code Mode system prompt; lookups tolerate either form.
52
+ * `lazyToolsConfig.includeDescription` controls how much of each tool's
53
+ * description appears in this tool's own catalog (mirroring the system prompt).
54
+ */
55
+ export function createDiscoveryTool(
56
+ lazyTools: Array<CodeModeTool>,
57
+ lazyToolsConfig?: LazyToolsConfig,
58
+ ): ServerTool<
59
+ typeof discoverInputSchema,
60
+ typeof discoverOutputSchema,
61
+ 'discover_tools'
62
+ > {
63
+ const lazyMap = new Map(lazyTools.map((t) => [t.name, t]))
64
+ const include = lazyToolsConfig?.includeDescription ?? 'none'
65
+ const catalog = lazyTools
66
+ .map((t) =>
67
+ renderLazyCatalogEntry(
68
+ `${EXTERNAL_PREFIX}${t.name}`,
69
+ t.description,
70
+ include,
71
+ ),
72
+ )
73
+ .join(', ')
74
+
75
+ return toolDefinition({
76
+ name: 'discover_tools' as const,
77
+ description:
78
+ `Discover full TypeScript signatures for additional sandbox APIs before ` +
79
+ `using them inside execute_typescript. Discoverable tools: [${catalog}]. ` +
80
+ `Pass the names exactly as shown (the external_ prefix is optional).`,
81
+ inputSchema: discoverInputSchema,
82
+ outputSchema: discoverOutputSchema,
83
+ }).server(async ({ toolNames }) => {
84
+ const tools: Array<{
85
+ name: string
86
+ description: string
87
+ typeStub: string
88
+ }> = []
89
+ const errors: Array<string> = []
90
+
91
+ for (const name of toolNames) {
92
+ const tool = lazyMap.get(stripExternalPrefix(name))
93
+ if (!tool) {
94
+ errors.push(`Unknown tool: '${name}'. Discoverable tools: [${catalog}]`)
95
+ continue
96
+ }
97
+ const binding = toolToBinding(tool, EXTERNAL_PREFIX)
98
+ const typeStub = generateTypeStubs({ [binding.name]: binding })
99
+ tools.push({
100
+ name: binding.name,
101
+ description: tool.description,
102
+ typeStub,
103
+ })
104
+ }
105
+
106
+ return errors.length > 0 ? { tools, errors } : { tools }
107
+ })
108
+ }
@@ -1,3 +1,4 @@
1
+ import { renderLazyCatalogEntry } from '@tanstack/ai'
1
2
  import { toolsToBindings } from './bindings/tool-to-binding'
2
3
  import { generateTypeStubs } from './type-generator/json-schema-to-ts'
3
4
  import type { CodeModeToolConfig } from './types'
@@ -26,21 +27,35 @@ import type { CodeModeToolConfig } from './types'
26
27
  */
27
28
  export function createCodeModeSystemPrompt(config: CodeModeToolConfig): string {
28
29
  const { tools } = config
30
+ const include = config.lazyToolsConfig?.includeDescription ?? 'none'
29
31
 
30
- // Transform tools to bindings with external_ prefix to generate correct type stubs
31
- const bindings = toolsToBindings(tools, 'external_')
32
+ const eagerTools = tools.filter((t) => !t.lazy)
33
+ const lazyTools = tools.filter((t) => t.lazy)
32
34
 
33
- // Generate TypeScript type stubs for the external functions
35
+ // Only eager tools get full type stubs + doc lines.
36
+ const bindings = toolsToBindings(eagerTools, 'external_')
34
37
  const typeStubs = generateTypeStubs(bindings)
35
38
 
36
- // Build function documentation
37
39
  const functionDocs = Object.entries(bindings)
38
- .map(([name, binding]) => {
39
- const doc = `- \`${name}(input)\`: ${binding.description}`
40
- return doc
41
- })
40
+ .map(([name, binding]) => `- \`${name}(input)\`: ${binding.description}`)
42
41
  .join('\n')
43
42
 
43
+ const discoverableSection =
44
+ lazyTools.length > 0
45
+ ? `
46
+
47
+ ### Discoverable APIs
48
+
49
+ 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:
50
+
51
+ ${lazyTools
52
+ .map(
53
+ (t) =>
54
+ `- ${renderLazyCatalogEntry(`external_${t.name}`, t.description, include)}`,
55
+ )
56
+ .join('\n')}`
57
+ : ''
58
+
44
59
  return `## Code Execution Tool
45
60
 
46
61
  You have access to \`execute_typescript\` which runs TypeScript code in a sandboxed environment.
@@ -65,7 +80,7 @@ ${functionDocs}
65
80
 
66
81
  \`\`\`typescript
67
82
  ${typeStubs}
68
- \`\`\`
83
+ \`\`\`${discoverableSection}
69
84
 
70
85
  ### Example
71
86
 
@@ -77,7 +92,7 @@ const results = await Promise.all(
77
92
  );
78
93
 
79
94
  // Find the warmest city
80
- const warmest = results.reduce((prev, curr) =>
95
+ const warmest = results.reduce((prev, curr) =>
81
96
  curr.temperature > prev.temperature ? curr : prev
82
97
  );
83
98
 
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ export type {
7
7
 
8
8
  export { createCodeModeSystemPrompt } from './create-system-prompt'
9
9
  export { createCodeMode } from './create-code-mode'
10
+ export { createDiscoveryTool } from './create-discovery-tool'
10
11
 
11
12
  export {
12
13
  InMemoryAgentStore,
@@ -39,6 +40,7 @@ export { wrapCode } from './code-wrapper'
39
40
  export type {
40
41
  // Tool-based API types
41
42
  CodeModeToolConfig,
43
+ CreateCodeModeResult,
42
44
  CodeModeToolResult,
43
45
  // Isolate driver interfaces (used by driver packages)
44
46
  IsolateDriver,
package/src/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ LazyToolsConfig,
2
3
  SchemaInput,
3
4
  ServerTool,
4
5
  ToolExecutionContext,
@@ -196,6 +197,14 @@ export interface CodeModeToolConfig {
196
197
  */
197
198
  getSkillBindings?: () => Promise<Record<string, ToolBinding>>
198
199
 
200
+ /**
201
+ * Optional lazy-tool discovery config. Tools marked `lazy: true` are kept out
202
+ * of the system prompt's full documentation and listed in a Discoverable APIs
203
+ * catalog instead; this tunes how much of each lazy tool's description that
204
+ * catalog shows. Optional — defaults to `{ includeDescription: 'none' }`.
205
+ */
206
+ lazyToolsConfig?: LazyToolsConfig
207
+
199
208
  /**
200
209
  * Optional escape hatch to swap out the TypeScript-stripping step.
201
210
  *
@@ -264,3 +273,19 @@ export interface CodeModeToolResult {
264
273
  }
265
274
  | undefined
266
275
  }
276
+
277
+ /**
278
+ * Return shape of `createCodeMode`. `tool` (execute_typescript) and
279
+ * `systemPrompt` are preserved for backward compatibility; `discoveryTool` and
280
+ * `tools` are additive. Spread `tools` into `chat({ tools })`.
281
+ */
282
+ export interface CreateCodeModeResult {
283
+ /** The execute_typescript tool. */
284
+ tool: ServerTool<SchemaInput, SchemaInput, 'execute_typescript'>
285
+ /** The discover_tools tool, or null when there are no lazy tools. */
286
+ discoveryTool: ServerTool<SchemaInput, SchemaInput, 'discover_tools'> | null
287
+ /** [tool] or [tool, discoveryTool] — the array to spread into chat({ tools }). */
288
+ tools: Array<ServerTool<SchemaInput, SchemaInput, string>>
289
+ /** The matching system prompt. */
290
+ systemPrompt: string
291
+ }
@@ -1,21 +0,0 @@
1
- export interface AgentSession {
2
- name: string;
3
- systemPrompt: string;
4
- memory: Record<string, unknown>;
5
- createdAt: number;
6
- lastUsedAt: number;
7
- }
8
- export interface AgentStore {
9
- get: (name: string) => Promise<AgentSession | null>;
10
- set: (name: string, session: AgentSession) => Promise<void>;
11
- delete: (name: string) => Promise<void>;
12
- list: () => Promise<Array<string>>;
13
- }
14
- export declare class InMemoryAgentStore implements AgentStore {
15
- private readonly sessions;
16
- get(name: string): Promise<AgentSession | null>;
17
- set(name: string, session: AgentSession): Promise<void>;
18
- delete(name: string): Promise<void>;
19
- list(): Promise<Array<string>>;
20
- }
21
- export declare function generateAgentName(): string;
@@ -1,29 +0,0 @@
1
- class InMemoryAgentStore {
2
- sessions = /* @__PURE__ */ new Map();
3
- get(name) {
4
- return Promise.resolve(this.sessions.get(name) ?? null);
5
- }
6
- set(name, session) {
7
- this.sessions.set(name, session);
8
- return Promise.resolve();
9
- }
10
- delete(name) {
11
- this.sessions.delete(name);
12
- return Promise.resolve();
13
- }
14
- list() {
15
- return Promise.resolve(Array.from(this.sessions.keys()));
16
- }
17
- }
18
- function generateAgentName() {
19
- const hex = Array.from(
20
- { length: 8 },
21
- () => Math.floor(Math.random() * 16).toString(16)
22
- ).join("");
23
- return `agent_${hex}`;
24
- }
25
- export {
26
- InMemoryAgentStore,
27
- generateAgentName
28
- };
29
- //# sourceMappingURL=agent-store.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"agent-store.js","sources":["../../src/agent-store.ts"],"sourcesContent":["export interface AgentSession {\n name: string\n systemPrompt: string\n memory: Record<string, unknown>\n createdAt: number\n lastUsedAt: number\n}\n\nexport interface AgentStore {\n get: (name: string) => Promise<AgentSession | null>\n set: (name: string, session: AgentSession) => Promise<void>\n delete: (name: string) => Promise<void>\n list: () => Promise<Array<string>>\n}\n\nexport class InMemoryAgentStore implements AgentStore {\n private readonly sessions = new Map<string, AgentSession>()\n\n get(name: string): Promise<AgentSession | null> {\n return Promise.resolve(this.sessions.get(name) ?? null)\n }\n\n set(name: string, session: AgentSession): Promise<void> {\n this.sessions.set(name, session)\n return Promise.resolve()\n }\n\n delete(name: string): Promise<void> {\n this.sessions.delete(name)\n return Promise.resolve()\n }\n\n list(): Promise<Array<string>> {\n return Promise.resolve(Array.from(this.sessions.keys()))\n }\n}\n\nexport function generateAgentName(): string {\n const hex = Array.from({ length: 8 }, () =>\n Math.floor(Math.random() * 16).toString(16),\n ).join('')\n return `agent_${hex}`\n}\n"],"names":[],"mappings":"AAeO,MAAM,mBAAyC;AAAA,EACnC,+BAAe,IAAA;AAAA,EAEhC,IAAI,MAA4C;AAC9C,WAAO,QAAQ,QAAQ,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI;AAAA,EACxD;AAAA,EAEA,IAAI,MAAc,SAAsC;AACtD,SAAK,SAAS,IAAI,MAAM,OAAO;AAC/B,WAAO,QAAQ,QAAA;AAAA,EACjB;AAAA,EAEA,OAAO,MAA6B;AAClC,SAAK,SAAS,OAAO,IAAI;AACzB,WAAO,QAAQ,QAAA;AAAA,EACjB;AAAA,EAEA,OAA+B;AAC7B,WAAO,QAAQ,QAAQ,MAAM,KAAK,KAAK,SAAS,KAAA,CAAM,CAAC;AAAA,EACzD;AACF;AAEO,SAAS,oBAA4B;AAC1C,QAAM,MAAM,MAAM;AAAA,IAAK,EAAE,QAAQ,EAAA;AAAA,IAAK,MACpC,KAAK,MAAM,KAAK,WAAW,EAAE,EAAE,SAAS,EAAE;AAAA,EAAA,EAC1C,KAAK,EAAE;AACT,SAAO,SAAS,GAAG;AACrB;"}
@@ -1,25 +0,0 @@
1
- import { ToolExecutionContext } from '@tanstack/ai';
2
- import { CodeModeTool, ToolBinding } from '../types.js';
3
- /**
4
- * Convert an array of TanStack AI tools to a Record of ToolBindings
5
- *
6
- * @param tools - Array of tools to convert
7
- * @param prefix - Optional prefix to add to binding names (e.g., 'external_')
8
- */
9
- export declare function toolsToBindings(tools: Array<CodeModeTool>, prefix?: string): Record<string, ToolBinding>;
10
- /**
11
- * Convert a single TanStack AI tool to a ToolBinding
12
- *
13
- * @param tool - Tool to convert
14
- * @param prefix - Optional prefix to add to binding name (e.g., 'external_')
15
- * @throws Error if the tool doesn't have an execute function
16
- */
17
- export declare function toolToBinding(tool: CodeModeTool, prefix?: string): ToolBinding;
18
- /**
19
- * Create event-aware bindings that emit custom events for each external function call.
20
- * Wraps each binding's execute function to emit events before and after execution.
21
- *
22
- * @param bindings - Original tool bindings
23
- * @param emitCustomEvent - Callback to emit custom events to the stream
24
- */
25
- export declare function createEventAwareBindings(bindings: Record<string, ToolBinding>, emitCustomEvent: ToolExecutionContext['emitCustomEvent']): Record<string, ToolBinding>;
@@ -1,94 +0,0 @@
1
- import { convertSchemaToJsonSchema, isStandardSchema, parseWithStandardSchema } from "@tanstack/ai";
2
- function toolsToBindings(tools, prefix = "") {
3
- const bindings = {};
4
- for (const tool of tools) {
5
- const bindingName = `${prefix}${tool.name}`;
6
- bindings[bindingName] = toolToBinding(tool, prefix);
7
- }
8
- return bindings;
9
- }
10
- function toolToBinding(tool, prefix = "") {
11
- const inputSchema = convertSchemaToJsonSchema(tool.inputSchema) || {
12
- type: "object",
13
- properties: {}
14
- };
15
- const outputSchema = tool.outputSchema ? convertSchemaToJsonSchema(tool.outputSchema) : void 0;
16
- if (typeof tool.execute !== "function") {
17
- throw new Error(
18
- `Tool "${tool.name}" does not have an execute function. Code Mode requires server tools with implementations.`
19
- );
20
- }
21
- const toolExecute = tool.execute;
22
- const execute = async (args, context) => {
23
- let input = args;
24
- if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {
25
- try {
26
- input = parseWithStandardSchema(tool.inputSchema, args);
27
- } catch (error) {
28
- const message = error instanceof Error ? error.message : "Validation failed";
29
- throw new Error(
30
- `Input validation failed for tool ${tool.name}: ${message}`
31
- );
32
- }
33
- }
34
- let result = await Promise.resolve(toolExecute(input, context));
35
- if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {
36
- try {
37
- result = parseWithStandardSchema(tool.outputSchema, result);
38
- } catch (error) {
39
- const message = error instanceof Error ? error.message : "Validation failed";
40
- throw new Error(
41
- `Output validation failed for tool ${tool.name}: ${message}`
42
- );
43
- }
44
- }
45
- return result;
46
- };
47
- return {
48
- name: `${prefix}${tool.name}`,
49
- description: tool.description,
50
- inputSchema,
51
- outputSchema,
52
- execute
53
- };
54
- }
55
- function createEventAwareBindings(bindings, emitCustomEvent) {
56
- const wrapped = {};
57
- for (const [name, binding] of Object.entries(bindings)) {
58
- wrapped[name] = {
59
- ...binding,
60
- execute: async (args) => {
61
- emitCustomEvent("code_mode:external_call", {
62
- function: name,
63
- args,
64
- timestamp: Date.now()
65
- });
66
- const startTime = Date.now();
67
- try {
68
- const toolContext = { emitCustomEvent };
69
- const result = await binding.execute(args, toolContext);
70
- emitCustomEvent("code_mode:external_result", {
71
- function: name,
72
- result,
73
- duration: Date.now() - startTime
74
- });
75
- return result;
76
- } catch (error) {
77
- emitCustomEvent("code_mode:external_error", {
78
- function: name,
79
- error: error instanceof Error ? error.message : String(error),
80
- duration: Date.now() - startTime
81
- });
82
- throw error;
83
- }
84
- }
85
- };
86
- }
87
- return wrapped;
88
- }
89
- export {
90
- createEventAwareBindings,
91
- toolToBinding,
92
- toolsToBindings
93
- };
94
- //# sourceMappingURL=tool-to-binding.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-to-binding.js","sources":["../../../src/bindings/tool-to-binding.ts"],"sourcesContent":["import {\n convertSchemaToJsonSchema,\n isStandardSchema,\n parseWithStandardSchema,\n} from '@tanstack/ai'\nimport type { ToolExecutionContext } from '@tanstack/ai'\nimport type { CodeModeTool, ToolBinding } from '../types'\n\n/**\n * Convert an array of TanStack AI tools to a Record of ToolBindings\n *\n * @param tools - Array of tools to convert\n * @param prefix - Optional prefix to add to binding names (e.g., 'external_')\n */\nexport function toolsToBindings(\n tools: Array<CodeModeTool>,\n prefix: string = '',\n): Record<string, ToolBinding> {\n const bindings: Record<string, ToolBinding> = {}\n\n for (const tool of tools) {\n const bindingName = `${prefix}${tool.name}`\n bindings[bindingName] = toolToBinding(tool, prefix)\n }\n\n return bindings\n}\n\n/**\n * Convert a single TanStack AI tool to a ToolBinding\n *\n * @param tool - Tool to convert\n * @param prefix - Optional prefix to add to binding name (e.g., 'external_')\n * @throws Error if the tool doesn't have an execute function\n */\nexport function toolToBinding(\n tool: CodeModeTool,\n prefix: string = '',\n): ToolBinding {\n // Convert schemas (Zod or Standard Schema) to JSON Schema\n const inputSchema = convertSchemaToJsonSchema(tool.inputSchema) || {\n type: 'object',\n properties: {},\n }\n\n const outputSchema = tool.outputSchema\n ? convertSchemaToJsonSchema(tool.outputSchema)\n : undefined\n\n if (typeof tool.execute !== 'function') {\n throw new Error(\n `Tool \"${tool.name}\" does not have an execute function. ` +\n `Code Mode requires server tools with implementations.`,\n )\n }\n\n const toolExecute = tool.execute\n const execute = async (args: unknown, context?: ToolExecutionContext) => {\n let input = args\n if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {\n try {\n input = parseWithStandardSchema(tool.inputSchema, args)\n } catch (error) {\n const message =\n error instanceof Error ? error.message : 'Validation failed'\n throw new Error(\n `Input validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n let result = await Promise.resolve(toolExecute(input, context))\n\n if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {\n try {\n result = parseWithStandardSchema(tool.outputSchema, result)\n } catch (error) {\n const message =\n error instanceof Error ? error.message : 'Validation failed'\n throw new Error(\n `Output validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n return result\n }\n\n return {\n name: `${prefix}${tool.name}`,\n description: tool.description,\n inputSchema,\n outputSchema,\n execute,\n }\n}\n\n/**\n * Create event-aware bindings that emit custom events for each external function call.\n * Wraps each binding's execute function to emit events before and after execution.\n *\n * @param bindings - Original tool bindings\n * @param emitCustomEvent - Callback to emit custom events to the stream\n */\nexport function createEventAwareBindings(\n bindings: Record<string, ToolBinding>,\n emitCustomEvent: ToolExecutionContext['emitCustomEvent'],\n): Record<string, ToolBinding> {\n const wrapped: Record<string, ToolBinding> = {}\n\n for (const [name, binding] of Object.entries(bindings)) {\n wrapped[name] = {\n ...binding,\n execute: async (args: unknown) => {\n // Emit call event\n emitCustomEvent('code_mode:external_call', {\n function: name,\n args,\n timestamp: Date.now(),\n })\n\n const startTime = Date.now()\n try {\n // Create context for the underlying tool so it can also emit events\n const toolContext: ToolExecutionContext = { emitCustomEvent }\n const result = await binding.execute(args, toolContext)\n\n // Emit result event\n emitCustomEvent('code_mode:external_result', {\n function: name,\n result,\n duration: Date.now() - startTime,\n })\n\n return result\n } catch (error) {\n // Emit error event\n emitCustomEvent('code_mode:external_error', {\n function: name,\n error: error instanceof Error ? error.message : String(error),\n duration: Date.now() - startTime,\n })\n throw error\n }\n },\n }\n }\n\n return wrapped\n}\n"],"names":[],"mappings":";AAcO,SAAS,gBACd,OACA,SAAiB,IACY;AAC7B,QAAM,WAAwC,CAAA;AAE9C,aAAW,QAAQ,OAAO;AACxB,UAAM,cAAc,GAAG,MAAM,GAAG,KAAK,IAAI;AACzC,aAAS,WAAW,IAAI,cAAc,MAAM,MAAM;AAAA,EACpD;AAEA,SAAO;AACT;AASO,SAAS,cACd,MACA,SAAiB,IACJ;AAEb,QAAM,cAAc,0BAA0B,KAAK,WAAW,KAAK;AAAA,IACjE,MAAM;AAAA,IACN,YAAY,CAAA;AAAA,EAAC;AAGf,QAAM,eAAe,KAAK,eACtB,0BAA0B,KAAK,YAAY,IAC3C;AAEJ,MAAI,OAAO,KAAK,YAAY,YAAY;AACtC,UAAM,IAAI;AAAA,MACR,SAAS,KAAK,IAAI;AAAA,IAAA;AAAA,EAGtB;AAEA,QAAM,cAAc,KAAK;AACzB,QAAM,UAAU,OAAO,MAAe,YAAmC;AACvE,QAAI,QAAQ;AACZ,QAAI,KAAK,eAAe,iBAAiB,KAAK,WAAW,GAAG;AAC1D,UAAI;AACF,gBAAQ,wBAAwB,KAAK,aAAa,IAAI;AAAA,MACxD,SAAS,OAAO;AACd,cAAM,UACJ,iBAAiB,QAAQ,MAAM,UAAU;AAC3C,cAAM,IAAI;AAAA,UACR,oCAAoC,KAAK,IAAI,KAAK,OAAO;AAAA,QAAA;AAAA,MAE7D;AAAA,IACF;AAEA,QAAI,SAAS,MAAM,QAAQ,QAAQ,YAAY,OAAO,OAAO,CAAC;AAE9D,QAAI,KAAK,gBAAgB,iBAAiB,KAAK,YAAY,GAAG;AAC5D,UAAI;AACF,iBAAS,wBAAwB,KAAK,cAAc,MAAM;AAAA,MAC5D,SAAS,OAAO;AACd,cAAM,UACJ,iBAAiB,QAAQ,MAAM,UAAU;AAC3C,cAAM,IAAI;AAAA,UACR,qCAAqC,KAAK,IAAI,KAAK,OAAO;AAAA,QAAA;AAAA,MAE9D;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM,GAAG,MAAM,GAAG,KAAK,IAAI;AAAA,IAC3B,aAAa,KAAK;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;AASO,SAAS,yBACd,UACA,iBAC6B;AAC7B,QAAM,UAAuC,CAAA;AAE7C,aAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACtD,YAAQ,IAAI,IAAI;AAAA,MACd,GAAG;AAAA,MACH,SAAS,OAAO,SAAkB;AAEhC,wBAAgB,2BAA2B;AAAA,UACzC,UAAU;AAAA,UACV;AAAA,UACA,WAAW,KAAK,IAAA;AAAA,QAAI,CACrB;AAED,cAAM,YAAY,KAAK,IAAA;AACvB,YAAI;AAEF,gBAAM,cAAoC,EAAE,gBAAA;AAC5C,gBAAM,SAAS,MAAM,QAAQ,QAAQ,MAAM,WAAW;AAGtD,0BAAgB,6BAA6B;AAAA,YAC3C,UAAU;AAAA,YACV;AAAA,YACA,UAAU,KAAK,QAAQ;AAAA,UAAA,CACxB;AAED,iBAAO;AAAA,QACT,SAAS,OAAO;AAEd,0BAAgB,4BAA4B;AAAA,YAC1C,UAAU;AAAA,YACV,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,YAC5D,UAAU,KAAK,QAAQ;AAAA,UAAA,CACxB;AACD,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IAAA;AAAA,EAEJ;AAEA,SAAO;AACT;"}
@@ -1,9 +0,0 @@
1
- /**
2
- * Wrap user code in an async IIFE to support top-level await
3
- *
4
- * The code is wrapped so that:
5
- * 1. Top-level await works
6
- * 2. Return values are captured and serialized (for isolate boundary transfer)
7
- * 3. Errors propagate correctly
8
- */
9
- export declare function wrapCode(code: string): string;
@@ -1,18 +0,0 @@
1
- function wrapCode(code) {
2
- return `
3
- (async function() {
4
- try {
5
- const __userResult = await (async function() {
6
- ${code}
7
- })();
8
- return JSON.stringify(__userResult);
9
- } catch (__error) {
10
- throw __error;
11
- }
12
- })()
13
- `;
14
- }
15
- export {
16
- wrapCode
17
- };
18
- //# sourceMappingURL=code-wrapper.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"code-wrapper.js","sources":["../../src/code-wrapper.ts"],"sourcesContent":["/**\n * Wrap user code in an async IIFE to support top-level await\n *\n * The code is wrapped so that:\n * 1. Top-level await works\n * 2. Return values are captured and serialized (for isolate boundary transfer)\n * 3. Errors propagate correctly\n */\nexport function wrapCode(code: string): string {\n // Results must be serialized to JSON because objects can't be transferred\n // directly across the isolate boundary in isolated-vm\n return `\n(async function() {\n try {\n const __userResult = await (async function() {\n${code}\n })();\n return JSON.stringify(__userResult);\n } catch (__error) {\n throw __error;\n }\n})()\n`\n}\n"],"names":[],"mappings":"AAQO,SAAS,SAAS,MAAsB;AAG7C,SAAO;AAAA;AAAA;AAAA;AAAA,EAIP,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQN;"}