@tanstack/ai-isolate-cloudflare 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.
@@ -0,0 +1,13 @@
1
+ import { ToolResultPayload, ToolSchema } from '../types.js';
2
+ /**
3
+ * Generate tool wrapper code that collects calls or returns cached results.
4
+ *
5
+ * Tool calls are identified by a sequential index (__toolCallIdx) rather than
6
+ * by hashing the input. This avoids mismatches when re-executing code whose
7
+ * inputs contain non-deterministic values (e.g. random UUIDs).
8
+ */
9
+ export declare function generateToolWrappers(tools: Array<ToolSchema>, toolResults?: Record<string, ToolResultPayload>): string;
10
+ /**
11
+ * Wrap user code in an async IIFE with tool wrappers
12
+ */
13
+ export declare function wrapCode(code: string, tools: Array<ToolSchema>, toolResults?: Record<string, ToolResultPayload>): string;
@@ -0,0 +1,102 @@
1
+ function generateToolWrappers(tools, toolResults) {
2
+ const wrappers = [];
3
+ for (const tool of tools) {
4
+ if (toolResults) {
5
+ wrappers.push(`
6
+ async function ${tool.name}(input) {
7
+ const callId = 'tc_' + (__toolCallIdx++);
8
+ const result = __toolResults[callId];
9
+ if (!result) {
10
+ __pendingToolCalls.push({ id: callId, name: '${tool.name}', args: input });
11
+ throw new __ToolCallNeeded(callId);
12
+ }
13
+ if (!result.success) {
14
+ throw new Error(result.error || 'Tool call failed');
15
+ }
16
+ return result.value;
17
+ }
18
+ `);
19
+ } else {
20
+ wrappers.push(`
21
+ async function ${tool.name}(input) {
22
+ const callId = 'tc_' + (__toolCallIdx++);
23
+ __pendingToolCalls.push({ id: callId, name: '${tool.name}', args: input });
24
+ throw new __ToolCallNeeded(callId);
25
+ }
26
+ `);
27
+ }
28
+ }
29
+ return wrappers.join("\n");
30
+ }
31
+ function wrapCode(code, tools, toolResults) {
32
+ const toolWrappers = generateToolWrappers(tools, toolResults);
33
+ const toolResultsJson = toolResults ? JSON.stringify(toolResults) : "{}";
34
+ return `
35
+ (async function() {
36
+ // Tool call tracking (sequential index for stable IDs across re-executions)
37
+ let __toolCallIdx = 0;
38
+ const __pendingToolCalls = [];
39
+ const __toolResults = ${toolResultsJson};
40
+ const __logs = [];
41
+
42
+ // Special error class for tool calls
43
+ class __ToolCallNeeded extends Error {
44
+ constructor(callId) {
45
+ super('Tool call needed: ' + callId);
46
+ this.callId = callId;
47
+ }
48
+ }
49
+
50
+ // Console capture
51
+ const console = {
52
+ log: (...args) => __logs.push(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ')),
53
+ error: (...args) => __logs.push('ERROR: ' + args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ')),
54
+ warn: (...args) => __logs.push('WARN: ' + args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ')),
55
+ info: (...args) => __logs.push('INFO: ' + args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ')),
56
+ };
57
+
58
+ // Tool wrappers
59
+ ${toolWrappers}
60
+
61
+ try {
62
+ // Execute user code
63
+ const __userResult = await (async function() {
64
+ ${code}
65
+ })();
66
+
67
+ return {
68
+ status: 'done',
69
+ success: true,
70
+ value: __userResult,
71
+ logs: __logs
72
+ };
73
+ } catch (__error) {
74
+ if (__error instanceof __ToolCallNeeded) {
75
+ // Tool calls needed - return pending calls
76
+ return {
77
+ status: 'need_tools',
78
+ toolCalls: __pendingToolCalls,
79
+ logs: __logs
80
+ };
81
+ }
82
+
83
+ // Regular error
84
+ return {
85
+ status: 'done',
86
+ success: false,
87
+ error: {
88
+ name: __error.name || 'Error',
89
+ message: __error.message || String(__error),
90
+ stack: __error.stack
91
+ },
92
+ logs: __logs
93
+ };
94
+ }
95
+ })()
96
+ `;
97
+ }
98
+ export {
99
+ generateToolWrappers,
100
+ wrapCode
101
+ };
102
+ //# sourceMappingURL=wrap-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrap-code.js","sources":["../../../src/worker/wrap-code.ts"],"sourcesContent":["/**\n * Code wrapping utilities for the Cloudflare Worker.\n * Extracted for testability without UNSAFE_EVAL.\n */\n\nimport type { ToolResultPayload, ToolSchema } from '../types'\n\n/**\n * Generate tool wrapper code that collects calls or returns cached results.\n *\n * Tool calls are identified by a sequential index (__toolCallIdx) rather than\n * by hashing the input. This avoids mismatches when re-executing code whose\n * inputs contain non-deterministic values (e.g. random UUIDs).\n */\nexport function generateToolWrappers(\n tools: Array<ToolSchema>,\n toolResults?: Record<string, ToolResultPayload>,\n): string {\n const wrappers: Array<string> = []\n\n for (const tool of tools) {\n if (toolResults) {\n wrappers.push(`\n async function ${tool.name}(input) {\n const callId = 'tc_' + (__toolCallIdx++);\n const result = __toolResults[callId];\n if (!result) {\n __pendingToolCalls.push({ id: callId, name: '${tool.name}', args: input });\n throw new __ToolCallNeeded(callId);\n }\n if (!result.success) {\n throw new Error(result.error || 'Tool call failed');\n }\n return result.value;\n }\n `)\n } else {\n wrappers.push(`\n async function ${tool.name}(input) {\n const callId = 'tc_' + (__toolCallIdx++);\n __pendingToolCalls.push({ id: callId, name: '${tool.name}', args: input });\n throw new __ToolCallNeeded(callId);\n }\n `)\n }\n }\n\n return wrappers.join('\\n')\n}\n\n/**\n * Wrap user code in an async IIFE with tool wrappers\n */\nexport function wrapCode(\n code: string,\n tools: Array<ToolSchema>,\n toolResults?: Record<string, ToolResultPayload>,\n): string {\n const toolWrappers = generateToolWrappers(tools, toolResults)\n const toolResultsJson = toolResults ? JSON.stringify(toolResults) : '{}'\n\n return `\n (async function() {\n // Tool call tracking (sequential index for stable IDs across re-executions)\n let __toolCallIdx = 0;\n const __pendingToolCalls = [];\n const __toolResults = ${toolResultsJson};\n const __logs = [];\n\n // Special error class for tool calls\n class __ToolCallNeeded extends Error {\n constructor(callId) {\n super('Tool call needed: ' + callId);\n this.callId = callId;\n }\n }\n\n // Console capture\n const console = {\n log: (...args) => __logs.push(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ')),\n error: (...args) => __logs.push('ERROR: ' + args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ')),\n warn: (...args) => __logs.push('WARN: ' + args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ')),\n info: (...args) => __logs.push('INFO: ' + args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ')),\n };\n\n // Tool wrappers\n ${toolWrappers}\n\n try {\n // Execute user code\n const __userResult = await (async function() {\n ${code}\n })();\n\n return {\n status: 'done',\n success: true,\n value: __userResult,\n logs: __logs\n };\n } catch (__error) {\n if (__error instanceof __ToolCallNeeded) {\n // Tool calls needed - return pending calls\n return {\n status: 'need_tools',\n toolCalls: __pendingToolCalls,\n logs: __logs\n };\n }\n\n // Regular error\n return {\n status: 'done',\n success: false,\n error: {\n name: __error.name || 'Error',\n message: __error.message || String(__error),\n stack: __error.stack\n },\n logs: __logs\n };\n }\n })()\n `\n}\n"],"names":[],"mappings":"AAcO,SAAS,qBACd,OACA,aACQ;AACR,QAAM,WAA0B,CAAA;AAEhC,aAAW,QAAQ,OAAO;AACxB,QAAI,aAAa;AACf,eAAS,KAAK;AAAA,yBACK,KAAK,IAAI;AAAA;AAAA;AAAA;AAAA,2DAIyB,KAAK,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAQ7D;AAAA,IACH,OAAO;AACL,eAAS,KAAK;AAAA,yBACK,KAAK,IAAI;AAAA;AAAA,yDAEuB,KAAK,IAAI;AAAA;AAAA;AAAA,OAG3D;AAAA,IACH;AAAA,EACF;AAEA,SAAO,SAAS,KAAK,IAAI;AAC3B;AAKO,SAAS,SACd,MACA,OACA,aACQ;AACR,QAAM,eAAe,qBAAqB,OAAO,WAAW;AAC5D,QAAM,kBAAkB,cAAc,KAAK,UAAU,WAAW,IAAI;AAEpE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,8BAKqB,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAoBrC,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,YAKR,IAAI;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAiChB;"}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@tanstack/ai-isolate-cloudflare",
3
+ "version": "0.1.0",
4
+ "description": "Cloudflare Workers driver for TanStack AI Code Mode - execute code on the edge",
5
+ "author": "",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/TanStack/ai.git",
10
+ "directory": "packages/typescript/ai-isolate-cloudflare"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "type": "module",
16
+ "module": "./dist/esm/index.js",
17
+ "types": "./dist/esm/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/esm/index.d.ts",
21
+ "import": "./dist/esm/index.js"
22
+ },
23
+ "./worker": {
24
+ "types": "./dist/esm/worker/index.d.ts",
25
+ "import": "./dist/esm/worker/index.js"
26
+ }
27
+ },
28
+ "sideEffects": false,
29
+ "engines": {
30
+ "node": ">=18"
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "src",
35
+ "worker",
36
+ "wrangler.toml"
37
+ ],
38
+ "scripts": {
39
+ "build": "vite build",
40
+ "clean": "premove ./build ./dist",
41
+ "dev:worker": "node dev-server.mjs",
42
+ "deploy:worker": "wrangler deploy",
43
+ "lint:fix": "eslint ./src --fix",
44
+ "test:build": "publint --strict",
45
+ "test:eslint": "eslint ./src",
46
+ "test:lib": "vitest --passWithNoTests",
47
+ "test:lib:dev": "pnpm test:lib --watch",
48
+ "test:types": "tsc"
49
+ },
50
+ "keywords": [
51
+ "ai",
52
+ "tanstack",
53
+ "code-mode",
54
+ "cloudflare",
55
+ "workers",
56
+ "edge",
57
+ "isolate"
58
+ ],
59
+ "dependencies": {
60
+ "@tanstack/ai-code-mode": "workspace:*"
61
+ },
62
+ "devDependencies": {
63
+ "@cloudflare/workers-types": "^4.20241230.0",
64
+ "@vitest/coverage-v8": "4.0.14",
65
+ "esbuild": "^0.25.12",
66
+ "miniflare": "^4.20260305.0",
67
+ "wrangler": "^4.19.1"
68
+ }
69
+ }
package/src/index.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @tanstack/ai-isolate-cloudflare
3
+ *
4
+ * Cloudflare Workers driver for TanStack AI Code Mode.
5
+ * Execute LLM-generated code on Cloudflare's global edge network.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { createCloudflareIsolateDriver } from '@tanstack/ai-isolate-cloudflare'
10
+ *
11
+ * const driver = createCloudflareIsolateDriver({
12
+ * workerUrl: 'https://your-worker.workers.dev',
13
+ * })
14
+ * ```
15
+ *
16
+ * @packageDocumentation
17
+ */
18
+
19
+ export {
20
+ createCloudflareIsolateDriver,
21
+ type CloudflareIsolateDriverConfig,
22
+ } from './isolate-driver'
23
+
24
+ export type {
25
+ ExecuteRequest,
26
+ ExecuteResponse,
27
+ ToolSchema,
28
+ ToolCallRequest,
29
+ ToolResultPayload,
30
+ } from './types'
@@ -0,0 +1,310 @@
1
+ import type {
2
+ ExecutionResult,
3
+ IsolateConfig,
4
+ IsolateContext,
5
+ IsolateDriver,
6
+ ToolBinding,
7
+ } from '@tanstack/ai-code-mode'
8
+ import type {
9
+ ExecuteRequest,
10
+ ExecuteResponse,
11
+ ToolResultPayload,
12
+ ToolSchema,
13
+ } from './types'
14
+
15
+ /**
16
+ * Configuration for the Cloudflare Workers isolate driver
17
+ */
18
+ export interface CloudflareIsolateDriverConfig {
19
+ /**
20
+ * URL of the deployed Cloudflare Worker
21
+ * For local development, use: http://localhost:8787
22
+ */
23
+ workerUrl: string
24
+
25
+ /**
26
+ * Optional authorization header value
27
+ * Useful for protecting your Worker endpoint
28
+ */
29
+ authorization?: string
30
+
31
+ /**
32
+ * Default execution timeout in ms (default: 30000)
33
+ */
34
+ timeout?: number
35
+
36
+ /**
37
+ * Maximum number of tool callback rounds (default: 10)
38
+ * Prevents infinite loops
39
+ */
40
+ maxToolRounds?: number
41
+ }
42
+
43
+ /**
44
+ * Convert tool bindings to schemas for the Worker
45
+ */
46
+ function bindingsToSchemas(
47
+ bindings: Record<string, ToolBinding>,
48
+ ): Array<ToolSchema> {
49
+ return Object.entries(bindings).map(([name, binding]) => ({
50
+ name,
51
+ description: binding.description,
52
+ inputSchema: binding.inputSchema,
53
+ }))
54
+ }
55
+
56
+ /**
57
+ * Normalize errors from various sources
58
+ */
59
+ function normalizeError(error: unknown): { name: string; message: string } {
60
+ if (error instanceof Error) {
61
+ return { name: error.name, message: error.message }
62
+ }
63
+ if (typeof error === 'object' && error !== null) {
64
+ const e = error as Record<string, unknown>
65
+ return {
66
+ name: String(e.name || 'Error'),
67
+ message: String(e.message || JSON.stringify(error)),
68
+ }
69
+ }
70
+ return { name: 'Error', message: String(error) }
71
+ }
72
+
73
+ /**
74
+ * IsolateContext implementation using Cloudflare Workers
75
+ */
76
+ class CloudflareIsolateContext implements IsolateContext {
77
+ private workerUrl: string
78
+ private authorization?: string
79
+ private timeout: number
80
+ private maxToolRounds: number
81
+ private bindings: Record<string, ToolBinding>
82
+ private disposed = false
83
+
84
+ constructor(
85
+ workerUrl: string,
86
+ bindings: Record<string, ToolBinding>,
87
+ timeout: number,
88
+ maxToolRounds: number,
89
+ authorization?: string,
90
+ ) {
91
+ this.workerUrl = workerUrl
92
+ this.bindings = bindings
93
+ this.timeout = timeout
94
+ this.maxToolRounds = maxToolRounds
95
+ this.authorization = authorization
96
+ }
97
+
98
+ async execute<T = unknown>(code: string): Promise<ExecutionResult<T>> {
99
+ if (this.disposed) {
100
+ return {
101
+ success: false,
102
+ error: {
103
+ name: 'DisposedError',
104
+ message: 'Context has been disposed',
105
+ },
106
+ logs: [],
107
+ }
108
+ }
109
+
110
+ const tools = bindingsToSchemas(this.bindings)
111
+ let toolResults: Record<string, ToolResultPayload> | undefined
112
+ let allLogs: Array<string> = []
113
+ let rounds = 0
114
+
115
+ // Request/response loop for tool callbacks
116
+ while (rounds < this.maxToolRounds) {
117
+ rounds++
118
+
119
+ const request: ExecuteRequest = {
120
+ code,
121
+ tools,
122
+ toolResults,
123
+ timeout: this.timeout,
124
+ }
125
+
126
+ try {
127
+ const headers: Record<string, string> = {
128
+ 'Content-Type': 'application/json',
129
+ }
130
+
131
+ if (this.authorization) {
132
+ headers['Authorization'] = this.authorization
133
+ }
134
+
135
+ const response = await fetch(this.workerUrl, {
136
+ method: 'POST',
137
+ headers,
138
+ body: JSON.stringify(request),
139
+ })
140
+
141
+ if (!response.ok) {
142
+ const errorText = await response.text()
143
+ return {
144
+ success: false,
145
+ error: {
146
+ name: 'WorkerError',
147
+ message: `Worker returned ${response.status}: ${errorText}`,
148
+ },
149
+ logs: allLogs,
150
+ }
151
+ }
152
+
153
+ const result: ExecuteResponse = await response.json()
154
+
155
+ if (result.status === 'error') {
156
+ return {
157
+ success: false,
158
+ error: result.error,
159
+ logs: allLogs,
160
+ }
161
+ }
162
+
163
+ if (result.status === 'done') {
164
+ allLogs = [...allLogs, ...result.logs]
165
+ return {
166
+ success: result.success,
167
+ value: result.value as T,
168
+ error: result.error,
169
+ logs: allLogs,
170
+ }
171
+ }
172
+
173
+ // status === 'need_tools'
174
+ // Collect logs from this round
175
+ allLogs = [...allLogs, ...result.logs]
176
+
177
+ // Execute tool calls locally
178
+ toolResults = {}
179
+
180
+ for (const toolCall of result.toolCalls) {
181
+ const binding = this.bindings[toolCall.name] as
182
+ | ToolBinding
183
+ | undefined
184
+
185
+ if (!binding) {
186
+ toolResults[toolCall.id] = {
187
+ success: false,
188
+ error: `Unknown tool: ${toolCall.name}`,
189
+ }
190
+ continue
191
+ }
192
+
193
+ try {
194
+ const toolResult = await binding.execute(toolCall.args)
195
+ toolResults[toolCall.id] = {
196
+ success: true,
197
+ value: toolResult,
198
+ }
199
+ } catch (toolError) {
200
+ const err = normalizeError(toolError)
201
+ toolResults[toolCall.id] = {
202
+ success: false,
203
+ error: err.message,
204
+ }
205
+ }
206
+ }
207
+
208
+ // Continue loop to send results back to Worker
209
+ } catch (fetchError) {
210
+ const err = normalizeError(fetchError)
211
+ return {
212
+ success: false,
213
+ error: {
214
+ name: 'NetworkError',
215
+ message: `Failed to communicate with Worker: ${err.message}`,
216
+ },
217
+ logs: allLogs,
218
+ }
219
+ }
220
+ }
221
+
222
+ // Max rounds exceeded
223
+ return {
224
+ success: false,
225
+ error: {
226
+ name: 'MaxRoundsExceeded',
227
+ message: `Exceeded maximum tool callback rounds (${this.maxToolRounds})`,
228
+ },
229
+ logs: allLogs,
230
+ }
231
+ }
232
+
233
+ dispose(): Promise<void> {
234
+ this.disposed = true
235
+ return Promise.resolve()
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Create a Cloudflare Workers isolate driver
241
+ *
242
+ * This driver executes code on Cloudflare's global edge network,
243
+ * providing true distributed execution capabilities.
244
+ *
245
+ * Tool calls are handled via a request/response loop:
246
+ * 1. Code is sent to the Worker
247
+ * 2. Worker executes until it needs a tool
248
+ * 3. Tool call is returned to the driver
249
+ * 4. Driver executes the tool locally
250
+ * 5. Result is sent back to the Worker
251
+ * 6. Worker continues execution
252
+ *
253
+ * @example
254
+ * ```typescript
255
+ * import { createCloudflareIsolateDriver } from '@tanstack/ai-isolate-cloudflare'
256
+ *
257
+ * // For local development with wrangler
258
+ * const driver = createCloudflareIsolateDriver({
259
+ * workerUrl: 'http://localhost:8787',
260
+ * })
261
+ *
262
+ * // For production
263
+ * const driver = createCloudflareIsolateDriver({
264
+ * workerUrl: 'https://code-mode-worker.your-account.workers.dev',
265
+ * authorization: 'Bearer your-secret-token',
266
+ * })
267
+ *
268
+ * const context = await driver.createContext({
269
+ * bindings: {
270
+ * readFile: {
271
+ * name: 'readFile',
272
+ * description: 'Read a file',
273
+ * inputSchema: { type: 'object', properties: { path: { type: 'string' } } },
274
+ * execute: async ({ path }) => fs.readFile(path, 'utf-8'),
275
+ * },
276
+ * },
277
+ * })
278
+ *
279
+ * const result = await context.execute(`
280
+ * const content = await readFile({ path: './data.json' })
281
+ * return JSON.parse(content)
282
+ * `)
283
+ * ```
284
+ */
285
+ export function createCloudflareIsolateDriver(
286
+ config: CloudflareIsolateDriverConfig,
287
+ ): IsolateDriver {
288
+ const {
289
+ workerUrl,
290
+ authorization,
291
+ timeout: defaultTimeout = 30000,
292
+ maxToolRounds = 10,
293
+ } = config
294
+
295
+ return {
296
+ createContext(isolateConfig: IsolateConfig): Promise<IsolateContext> {
297
+ const timeout = isolateConfig.timeout ?? defaultTimeout
298
+
299
+ return Promise.resolve(
300
+ new CloudflareIsolateContext(
301
+ workerUrl,
302
+ isolateConfig.bindings,
303
+ timeout,
304
+ maxToolRounds,
305
+ authorization,
306
+ ),
307
+ )
308
+ },
309
+ }
310
+ }
package/src/types.ts ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Shared types between the Cloudflare Worker and the driver
3
+ */
4
+
5
+ /**
6
+ * Tool schema passed to the worker
7
+ */
8
+ export interface ToolSchema {
9
+ name: string
10
+ description: string
11
+ inputSchema: Record<string, unknown>
12
+ }
13
+
14
+ /**
15
+ * Request to execute code in the worker
16
+ */
17
+ export interface ExecuteRequest {
18
+ /** The code to execute */
19
+ code: string
20
+ /** Tool schemas available for the code to call */
21
+ tools: Array<ToolSchema>
22
+ /** Results from previous tool calls (for continuation) */
23
+ toolResults?: Record<string, ToolResultPayload>
24
+ /** Execution timeout in ms */
25
+ timeout?: number
26
+ }
27
+
28
+ /**
29
+ * Tool call requested by the worker
30
+ */
31
+ export interface ToolCallRequest {
32
+ /** Unique ID for this tool call */
33
+ id: string
34
+ /** Name of the tool to call */
35
+ name: string
36
+ /** Arguments to pass to the tool */
37
+ args: unknown
38
+ }
39
+
40
+ /**
41
+ * Result of a tool call
42
+ */
43
+ export interface ToolResultPayload {
44
+ /** Whether the tool call succeeded */
45
+ success: boolean
46
+ /** The result value if successful */
47
+ value?: unknown
48
+ /** Error message if failed */
49
+ error?: string
50
+ }
51
+
52
+ /**
53
+ * Response from the worker - either done or needs tool calls
54
+ */
55
+ export type ExecuteResponse =
56
+ | {
57
+ status: 'done'
58
+ success: boolean
59
+ value?: unknown
60
+ error?: {
61
+ name: string
62
+ message: string
63
+ stack?: string
64
+ }
65
+ logs: Array<string>
66
+ }
67
+ | {
68
+ status: 'need_tools'
69
+ toolCalls: Array<ToolCallRequest>
70
+ logs: Array<string>
71
+ /** Continuation state to send back with tool results */
72
+ continuationId: string
73
+ }
74
+ | {
75
+ status: 'error'
76
+ error: {
77
+ name: string
78
+ message: string
79
+ }
80
+ }