Skip to content

Commit 87a009a

Browse files
committed
🤖 feat: Implement PTC Phases 1-5.6 with cleanup fixes
Programmatic Tool Calling (PTC) infrastructure for executing JavaScript code in a sandboxed QuickJS environment with access to Mux tools. Phases completed: - Phase 1: Experiment flag (PROGRAMMATIC_TOOL_CALLING) - Phase 2: Runtime abstraction (IJSRuntime interface) - Phase 3: QuickJS implementation with Asyncify - Phase 4: Tool bridge exposing tools under mux.* namespace - Phase 4.5: Static analysis (syntax, forbidden patterns, AST-based globals) - Phase 5: code_execution tool definition - Phase 5.5: TypeScript type generation & validation from Zod schemas - Phase 5.6: Cleanup fixes (JSDoc, cache unification, test improvements) Key features: - Sandboxed execution with 64MB memory / 5min timeout limits - TypeScript type checking before execution (blocking errors) - Partial results on failure for debugging - Event streaming for nested tool calls - Abort signal support for cancellation 131 tests passing, make static-check passes. --- _Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high`_
1 parent b328d2d commit 87a009a

21 files changed

+4060
-93
lines changed

bun.lock

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"@ai-sdk/openai": "^2.0.72",
1414
"@ai-sdk/xai": "^2.0.36",
1515
"@aws-sdk/credential-providers": "^3.940.0",
16+
"@jitl/quickjs-wasmfile-release-asyncify": "^0.31.0",
1617
"@lydell/node-pty": "1.1.0",
1718
"@mozilla/readability": "^0.6.0",
1819
"@openrouter/ai-sdk-provider": "^1.2.5",
@@ -49,6 +50,7 @@
4950
"express": "^5.1.0",
5051
"ghostty-web": "^0.3.0-next.13.g3dd4aef",
5152
"jsdom": "^27.2.0",
53+
"json-schema-to-typescript": "^15.0.4",
5254
"jsonc-parser": "^3.3.1",
5355
"lru-cache": "^11.2.2",
5456
"lucide-react": "^0.553.0",
@@ -59,6 +61,8 @@
5961
"openai": "^6.9.1",
6062
"parse-duration": "^2.1.4",
6163
"posthog-node": "^5.17.0",
64+
"quickjs-emscripten": "^0.31.0",
65+
"quickjs-emscripten-core": "^0.31.0",
6266
"rehype-harden": "^1.1.5",
6367
"rehype-sanitize": "^6.0.0",
6468
"shescape": "^2.1.6",
@@ -196,6 +200,8 @@
196200

197201
"@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
198202

203+
"@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@11.9.3", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ=="],
204+
199205
"@asamuzakjp/css-color": ["@asamuzakjp/css-color@4.1.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.4", "@csstools/css-color-parser": "^3.1.0", "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", "lru-cache": "^11.2.2" } }, "sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w=="],
200206

201207
"@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@6.7.5", "", { "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.1.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.2.2" } }, "sha512-Eks6dY8zau4m4wNRQjRVaKQRTalNcPcBvU1ZQ35w5kKRk1gUeNCkVLsRiATurjASTp3TKM4H10wsI50nx3NZdw=="],
@@ -734,6 +740,16 @@
734740

735741
"@jest/types": ["@jest/types@30.2.0", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg=="],
736742

743+
"@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.31.0", "", {}, "sha512-1yrgvXlmXH2oNj3eFTrkwacGJbmM0crwipA3ohCrjv52gBeDaD7PsTvFYinlAnqU8iPME3LGP437yk05a2oejw=="],
744+
745+
"@jitl/quickjs-wasmfile-debug-asyncify": ["@jitl/quickjs-wasmfile-debug-asyncify@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-YkdzQdr1uaftFhgEnTRjTTZHk2SFZdpWO7XhOmRVbi6CEVsH9g5oNF8Ta1q3OuSJHRwwT8YsuR1YzEiEIJEk6w=="],
746+
747+
"@jitl/quickjs-wasmfile-debug-sync": ["@jitl/quickjs-wasmfile-debug-sync@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-8XvloaaWBONqcHXYs5tWOjdhQVxzULilIfB2hvZfS6S+fI4m2+lFiwQy7xeP8ExHmiZ7D8gZGChNkdLgjGfknw=="],
748+
749+
"@jitl/quickjs-wasmfile-release-asyncify": ["@jitl/quickjs-wasmfile-release-asyncify@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-uz0BbQYTxNsFkvkurd7vk2dOg57ElTBLCuvNtRl4rgrtbC++NIndD5qv2+AXb6yXDD3Uy1O2PCwmoaH0eXgEOg=="],
750+
751+
"@jitl/quickjs-wasmfile-release-sync": ["@jitl/quickjs-wasmfile-release-sync@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-hYduecOByj9AsAfsJhZh5nA6exokmuFC8cls39+lYmTCGY51bgjJJJwReEu7Ff7vBWaQCL6TeDdVlnp2WYz0jw=="],
752+
737753
"@joshwooding/vite-plugin-react-docgen-typescript": ["@joshwooding/vite-plugin-react-docgen-typescript@0.6.1", "", { "dependencies": { "glob": "^10.0.0", "magic-string": "^0.30.0", "react-docgen-typescript": "^2.2.2" }, "peerDependencies": { "typescript": ">= 4.3.x", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["typescript"] }, "sha512-J4BaTocTOYFkMHIra1JDWrMWpNmBl4EkplIwHEsV8aeUOtdWjwSnln9U7twjMFTAEB7mptNtSKyVi1Y2W9sDJw=="],
738754

739755
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
@@ -746,6 +762,8 @@
746762

747763
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
748764

765+
"@jsdevtools/ono": ["@jsdevtools/ono@7.1.3", "", {}, "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="],
766+
749767
"@lydell/node-pty": ["@lydell/node-pty@1.1.0", "", { "optionalDependencies": { "@lydell/node-pty-darwin-arm64": "1.1.0", "@lydell/node-pty-darwin-x64": "1.1.0", "@lydell/node-pty-linux-arm64": "1.1.0", "@lydell/node-pty-linux-x64": "1.1.0", "@lydell/node-pty-win32-arm64": "1.1.0", "@lydell/node-pty-win32-x64": "1.1.0" } }, "sha512-VDD8LtlMTOrPKWMXUAcB9+LTktzuunqrMwkYR1DMRBkS6LQrCt+0/Ws1o2rMml/n3guePpS7cxhHF7Nm5K4iMw=="],
750768

751769
"@lydell/node-pty-darwin-arm64": ["@lydell/node-pty-darwin-arm64@1.1.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7kFD+owAA61qmhJCtoMbqj3Uvff3YHDiU+4on5F2vQdcMI3MuwGi7dM6MkFG/yuzpw8LF2xULpL71tOPUfxs0w=="],
@@ -1356,6 +1374,8 @@
13561374

13571375
"@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="],
13581376

1377+
"@types/lodash": ["@types/lodash@4.17.21", "", {}, "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ=="],
1378+
13591379
"@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="],
13601380

13611381
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
@@ -2562,6 +2582,8 @@
25622582

25632583
"json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
25642584

2585+
"json-schema-to-typescript": ["json-schema-to-typescript@15.0.4", "", { "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.5.5", "@types/json-schema": "^7.0.15", "@types/lodash": "^4.17.7", "is-glob": "^4.0.3", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "minimist": "^1.2.8", "prettier": "^3.2.5", "tinyglobby": "^0.2.9" }, "bin": { "json2ts": "dist/src/cli.js" } }, "sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ=="],
2586+
25652587
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
25662588

25672589
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
@@ -3082,6 +3104,10 @@
30823104

30833105
"quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="],
30843106

3107+
"quickjs-emscripten": ["quickjs-emscripten@0.31.0", "", { "dependencies": { "@jitl/quickjs-wasmfile-debug-asyncify": "0.31.0", "@jitl/quickjs-wasmfile-debug-sync": "0.31.0", "@jitl/quickjs-wasmfile-release-asyncify": "0.31.0", "@jitl/quickjs-wasmfile-release-sync": "0.31.0", "quickjs-emscripten-core": "0.31.0" } }, "sha512-K7Yt78aRPLjPcqv3fIuLW1jW3pvwO21B9pmFOolsjM/57ZhdVXBr51GqJpalgBlkPu9foAvhEAuuQPnvIGvLvQ=="],
3108+
3109+
"quickjs-emscripten-core": ["quickjs-emscripten-core@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-oQz8p0SiKDBc1TC7ZBK2fr0GoSHZKA0jZIeXxsnCyCs4y32FStzCW4d1h6E1sE0uHDMbGITbk2zhNaytaoJwXQ=="],
3110+
30853111
"radash": ["radash@12.1.1", "", {}, "sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA=="],
30863112

30873113
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"@ai-sdk/openai": "^2.0.72",
5454
"@ai-sdk/xai": "^2.0.36",
5555
"@aws-sdk/credential-providers": "^3.940.0",
56+
"@jitl/quickjs-wasmfile-release-asyncify": "^0.31.0",
5657
"@lydell/node-pty": "1.1.0",
5758
"@mozilla/readability": "^0.6.0",
5859
"@openrouter/ai-sdk-provider": "^1.2.5",
@@ -89,6 +90,7 @@
8990
"express": "^5.1.0",
9091
"ghostty-web": "^0.3.0-next.13.g3dd4aef",
9192
"jsdom": "^27.2.0",
93+
"json-schema-to-typescript": "^15.0.4",
9294
"jsonc-parser": "^3.3.1",
9395
"lru-cache": "^11.2.2",
9496
"lucide-react": "^0.553.0",
@@ -99,6 +101,8 @@
99101
"openai": "^6.9.1",
100102
"parse-duration": "^2.1.4",
101103
"posthog-node": "^5.17.0",
104+
"quickjs-emscripten": "^0.31.0",
105+
"quickjs-emscripten-core": "^0.31.0",
102106
"rehype-harden": "^1.1.5",
103107
"rehype-sanitize": "^6.0.0",
104108
"shescape": "^2.1.6",

src/common/constants/experiments.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
export const EXPERIMENT_IDS = {
99
POST_COMPACTION_CONTEXT: "post-compaction-context",
10+
PROGRAMMATIC_TOOL_CALLING: "programmatic-tool-calling",
1011
} as const;
1112

1213
export type ExperimentId = (typeof EXPERIMENT_IDS)[keyof typeof EXPERIMENT_IDS];
@@ -42,6 +43,12 @@ export const EXPERIMENTS: Record<ExperimentId, ExperimentDefinition> = {
4243
userOverridable: true, // User can opt-out via Settings
4344
showInSettings: true,
4445
},
46+
[EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING]: {
47+
id: EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING,
48+
name: "Programmatic Tool Calling",
49+
description: "Enable code_execution tool for multi-tool workflows in a sandboxed JS runtime",
50+
enabledByDefault: false,
51+
},
4552
};
4653

4754
/**

src/common/orpc/schemas/stream.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ export const ToolPolicySchema = z.array(ToolPolicyFilterSchema).meta({
332332
// Experiments schema for feature gating
333333
export const ExperimentsSchema = z.object({
334334
postCompactionContext: z.boolean().optional(),
335+
programmaticToolCalling: z.boolean().optional(),
335336
});
336337

337338
// SendMessage options

src/common/types/tools.ts

Lines changed: 29 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@ import type {
88
AskUserQuestionOptionSchema,
99
AskUserQuestionQuestionSchema,
1010
AskUserQuestionToolResultSchema,
11+
BashBackgroundListResultSchema,
12+
BashBackgroundTerminateResultSchema,
13+
BashOutputToolResultSchema,
14+
BashToolResultSchema,
15+
FileEditInsertToolResultSchema,
16+
FileEditReplaceStringToolResultSchema,
17+
FileReadToolResultSchema,
1118
TOOL_DEFINITIONS,
19+
WebFetchToolResultSchema,
1220
} from "@/common/utils/tools/toolDefinitions";
1321

1422
// Bash Tool Types
@@ -19,40 +27,8 @@ export interface BashToolArgs {
1927
display_name: string; // Required - used as process identifier if sent to background
2028
}
2129

22-
interface CommonBashFields {
23-
// wall_duration_ms is provided to give the agent a sense of how long a command takes which
24-
// should inform future timeouts.
25-
wall_duration_ms: number;
26-
}
27-
28-
export type BashToolResult =
29-
| (CommonBashFields & {
30-
success: true;
31-
output: string;
32-
exitCode: 0;
33-
note?: string; // Agent-only message (not displayed in UI)
34-
truncated?: {
35-
reason: string;
36-
totalLines: number;
37-
};
38-
})
39-
| (CommonBashFields & {
40-
success: true;
41-
output: string;
42-
exitCode: 0;
43-
backgroundProcessId: string; // Background spawn succeeded
44-
})
45-
| (CommonBashFields & {
46-
success: false;
47-
output?: string;
48-
exitCode: number;
49-
error: string;
50-
note?: string; // Agent-only message (not displayed in UI)
51-
truncated?: {
52-
reason: string;
53-
totalLines: number;
54-
};
55-
});
30+
// BashToolResult derived from Zod schema (single source of truth)
31+
export type BashToolResult = z.infer<typeof BashToolResultSchema>;
5632

5733
// File Read Tool Types
5834
export interface FileReadToolArgs {
@@ -61,19 +37,8 @@ export interface FileReadToolArgs {
6137
limit?: number; // number of lines to return from offset (optional)
6238
}
6339

64-
export type FileReadToolResult =
65-
| {
66-
success: true;
67-
file_size: number;
68-
modifiedTime: string;
69-
lines_read: number;
70-
content: string;
71-
warning?: string;
72-
}
73-
| {
74-
success: false;
75-
error: string;
76-
};
40+
// FileReadToolResult derived from Zod schema (single source of truth)
41+
export type FileReadToolResult = z.infer<typeof FileReadToolResultSchema>;
7742

7843
export interface FileEditDiffSuccessBase {
7944
success: true;
@@ -96,7 +61,8 @@ export interface FileEditInsertToolArgs {
9661
after?: string;
9762
}
9863

99-
export type FileEditInsertToolResult = FileEditDiffSuccessBase | FileEditErrorResult;
64+
// FileEditInsertToolResult derived from Zod schema (single source of truth)
65+
export type FileEditInsertToolResult = z.infer<typeof FileEditInsertToolResultSchema>;
10066

10167
export interface FileEditReplaceStringToolArgs {
10268
file_path: string;
@@ -105,11 +71,8 @@ export interface FileEditReplaceStringToolArgs {
10571
replace_count?: number;
10672
}
10773

108-
export type FileEditReplaceStringToolResult =
109-
| (FileEditDiffSuccessBase & {
110-
edits_applied: number;
111-
})
112-
| FileEditErrorResult;
74+
// FileEditReplaceStringToolResult derived from Zod schema (single source of truth)
75+
export type FileEditReplaceStringToolResult = z.infer<typeof FileEditReplaceStringToolResultSchema>;
11376

11477
export interface FileEditReplaceLinesToolArgs {
11578
file_path: string;
@@ -255,42 +218,28 @@ export interface BashOutputToolArgs {
255218
timeout_secs: number;
256219
}
257220

258-
export type BashOutputToolResult =
259-
| {
260-
success: true;
261-
status: "running" | "exited" | "killed" | "failed" | "interrupted";
262-
output: string;
263-
exitCode?: number;
264-
note?: string; // Agent-only message (not displayed in UI)
265-
// Time spent waiting for output (helps agent detect/avoid busy loops)
266-
elapsed_ms: number;
267-
}
268-
| { success: false; error: string };
221+
// BashOutputToolResult derived from Zod schema (single source of truth)
222+
export type BashOutputToolResult = z.infer<typeof BashOutputToolResultSchema>;
269223

270224
// Bash Background Tool Types
271225
export interface BashBackgroundTerminateArgs {
272226
process_id: string;
273227
}
274228

275-
export type BashBackgroundTerminateResult =
276-
| { success: true; message: string; display_name?: string }
277-
| { success: false; error: string };
229+
// BashBackgroundTerminateResult derived from Zod schema (single source of truth)
230+
export type BashBackgroundTerminateResult = z.infer<typeof BashBackgroundTerminateResultSchema>;
278231

279232
// Bash Background List Tool Types
280233
export type BashBackgroundListArgs = Record<string, never>;
281234

282-
export interface BashBackgroundListProcess {
283-
process_id: string;
284-
status: "running" | "exited" | "killed" | "failed";
285-
script: string;
286-
uptime_ms: number;
287-
exitCode?: number;
288-
display_name?: string; // Human-readable name (e.g., "Dev Server")
289-
}
235+
// BashBackgroundListResult derived from Zod schema (single source of truth)
236+
export type BashBackgroundListResult = z.infer<typeof BashBackgroundListResultSchema>;
290237

291-
export type BashBackgroundListResult =
292-
| { success: true; processes: BashBackgroundListProcess[] }
293-
| { success: false; error: string };
238+
// BashBackgroundListProcess extracted from result type for convenience
239+
export type BashBackgroundListProcess = Extract<
240+
BashBackgroundListResult,
241+
{ success: true }
242+
>["processes"][number];
294243

295244
export type StatusSetToolResult =
296245
| {
@@ -309,18 +258,5 @@ export interface WebFetchToolArgs {
309258
url: string;
310259
}
311260

312-
export type WebFetchToolResult =
313-
| {
314-
success: true;
315-
title: string;
316-
content: string;
317-
url: string;
318-
byline?: string;
319-
length: number;
320-
}
321-
| {
322-
success: false;
323-
error: string;
324-
/** Parsed error response body (e.g., from HTTP 4xx/5xx pages) */
325-
content?: string;
326-
};
261+
// WebFetchToolResult derived from Zod schema (single source of truth)
262+
export type WebFetchToolResult = z.infer<typeof WebFetchToolResultSchema>;

0 commit comments

Comments
 (0)