tools: move executionMode from Tool to ToolDefinition#32342
tools: move executionMode from Tool to ToolDefinition#32342vellum-apollo-bot[bot] wants to merge 1 commit into
Conversation
Brings the dispatch-intent field into the author-facing tool surface so all dispatcher-relevant knobs (executionTarget + executionMode) live on ToolDefinition. Tool now only adds 'category' on top of LoadedTool — one step closer to Tool === LoadedTool. LoadedTool keeps executionMode genuinely optional via Required<Omit<>> because undefined is the canonical 'local' state — load-time stamping would force a meaningless default. No runtime change. Pure type-level move. ### AGENTS.md compliance Grepped assistant/AGENTS.md for executionMode|ToolDefinition|LoadedTool| plugin-api|skill manifest|breaking change|public surface — no conventions apply to this move.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 053ac911ad
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| /** When set to 'proxy', the tool is forwarded to a connected client rather than executed locally. Genuinely optional — omit (or set to 'local') for in-process tools. */ | ||
| executionMode?: "local" | "proxy"; |
There was a problem hiding this comment.
Don't expose proxy mode for plugin tool definitions
When this ToolDefinition is consumed through @vellumai/plugin-api, external plugin tools loaded in external-plugin-loader.ts now type-check with executionMode: "proxy", but the conversation tool list still only includes non-proxy registry tools plus the hard-coded UI/app proxy tools (getAllToolDefinitions() filters t.executionMode !== "proxy", and buildToolDefinitions() only re-adds allUiSurfaceTools/coreAppProxyTools). A plugin author following this new public type would have their tool registered and then silently omitted from the model-visible tool catalog, with no plugin projection path to add it back.
Useful? React with 👍 / 👎.
Eliminates the executor's mode branch in favor of self-contained execute() bodies for every host-proxied tool, then deletes the now-redundant executionMode field entirely. Why --- PR #32342 moved executionMode from `Tool` to `ToolDefinition` as a pure type-level move. This PR (alternative) goes further: it asks whether the field is needed at all once dispatch is unified. After the unification, executionMode had two genuinely different concerns conflated under one name: 1. **Dispatch** — folded into each tool's `execute()`. The executor no longer special-cases mode='proxy'; every tool's execute() handles its own forwarding (to context.proxyToolResolver for client tools, to supervisor.dispatchTool for meet tools, etc.). 2. **Skill projection dedup** — already removed in #32365, which proved the owner-only filter on `getAllToolDefinitions` and `getRegisteredToolNames` is sufficient. No replacement field needed. So executionMode just gets deleted outright. No rename. Changes ------- - Each proxy tool's `execute()` now forwards via context.proxyToolResolver with name-bound closure. No-resolver case returns a structured error result (isError: true) instead of throwing. - executor.ts drops both mode branches (initial dispatch + CES retry). - `resolveExecutionTarget` drops rule #2 (mode=proxy ⇒ host). Every declaration site that used to rely on it already stamps target='host' directly. - `tool-approval-handler.ts` drops the resolver-aware available-tools filter — execute() handles no-resolver gracefully now. - IPC schema in skill-routes/registries.ts drops executionMode from the wire payload; contracts-side Tool + client.ts manifest projection drop it too. Field count: -1 executionMode, +0. The win is dispatch unification plus the simpler tool surface that #32365 enabled.
Eliminates the executor's mode branch in favor of self-contained execute() bodies for every host-proxied tool, then deletes the now-redundant executionMode field entirely. Why --- PR #32342 moved executionMode from `Tool` to `ToolDefinition` as a pure type-level move. This PR (alternative) goes further: it asks whether the field is needed at all once dispatch is unified. After the unification, executionMode had two genuinely different concerns conflated under one name: 1. **Dispatch** — folded into each tool's `execute()`. The executor no longer special-cases mode='proxy'; every tool's execute() handles its own forwarding (to context.proxyToolResolver for client tools, to supervisor.dispatchTool for meet tools, etc.). 2. **Skill projection dedup** — already removed in #32365, which proved the owner-only filter on `getAllToolDefinitions` and `getRegisteredToolNames` is sufficient. No replacement field needed. So executionMode just gets deleted outright. No rename. Changes ------- - Each proxy tool's `execute()` now forwards via context.proxyToolResolver with name-bound closure. No-resolver case returns a structured error result (isError: true) instead of throwing. - executor.ts drops both mode branches (initial dispatch + CES retry). - `resolveExecutionTarget` drops rule #2 (mode=proxy ⇒ host). Every declaration site that used to rely on it already stamps target='host' directly. - `tool-approval-handler.ts` drops the resolver-aware available-tools filter — execute() handles no-resolver gracefully now. - IPC schema in skill-routes/registries.ts drops executionMode from the wire payload; contracts-side Tool + client.ts manifest projection drop it too. Field count: -1 executionMode, +0. The win is dispatch unification plus the simpler tool surface that #32365 enabled.
Eliminates the executor's mode branch in favor of self-contained execute() bodies for every host-proxied tool, then deletes the now-redundant executionMode field entirely. Why --- PR #32342 moved executionMode from `Tool` to `ToolDefinition` as a pure type-level move. This PR (alternative) goes further: it asks whether the field is needed at all once dispatch is unified. After the unification, executionMode had two genuinely different concerns conflated under one name: 1. **Dispatch** — folded into each tool's `execute()`. The executor no longer special-cases mode='proxy'; every tool's execute() handles its own forwarding (to context.proxyToolResolver for client tools, to supervisor.dispatchTool for meet tools, etc.). 2. **Skill projection dedup** — already removed in #32365, which proved the owner-only filter on `getAllToolDefinitions` and `getRegisteredToolNames` is sufficient. No replacement field needed. So executionMode just gets deleted outright. No rename. Changes ------- - Each proxy tool's `execute()` now forwards via context.proxyToolResolver with name-bound closure. No-resolver case returns a structured error result (isError: true) instead of throwing. - executor.ts drops both mode branches (initial dispatch + CES retry). - `resolveExecutionTarget` drops rule #2 (mode=proxy ⇒ host). Every declaration site that used to rely on it already stamps target='host' directly. - `tool-approval-handler.ts` drops the resolver-aware available-tools filter — execute() handles no-resolver gracefully now. - IPC schema in skill-routes/registries.ts drops executionMode from the wire payload; contracts-side Tool + client.ts manifest projection drop it too. Field count: -1 executionMode, +0. The win is dispatch unification plus the simpler tool surface that #32365 enabled. Co-authored-by: vellum-apollo-bot[bot] <220839023+vellum-apollo-bot[bot]@users.noreply.github.com>
Summary
Next step in the Tool surface triage workstream. Moves
executionModefromToolintoToolDefinitionso all dispatcher-relevant fields (executionTarget+executionMode) live on the author-facing surface together.After this change
Toolonly addscategoryon top ofLoadedTool— one step closer to a futureTool === LoadedToolcollapse.What changed
ToolDefinitiongainsexecutionMode?: "local" | "proxy"(optional in author-facing).LoadedToolis nowRequired<Omit<ToolDefinition, "executionMode">> & { name; executionMode? }— executionMode stays optional becauseundefinedIS the canonical "local" state, and load-time stamping would force a meaningless default.Toolinterface drops its inlineexecutionMode?declaration (now inherited viaLoadedTool←ToolDefinition).No runtime change. Pure type-level move. All existing Tool literals (computer-use, ui-surface, apps, meet manifest proxy) continue to compile and behave identically.
Triage series
Follows #32132 → #32163 + #32192 → #32224 → #32294 → #32339. Tracked in workstream
1f150aef-3857-419f-be65-34d699940826(Everything is a Skill).Verification
tsc --noEmitclean.eslint src/tools/types.tsclean.src/__tests__/registry.test.ts— 27/27src/__tests__/computer-use-tools.test.ts— 26/26src/__tests__/tool-executor.test.ts— 80/80src/__tests__/tool-executor-lifecycle-events.test.ts— 17/17src/__tests__/checker.test.ts— 132/132src/ipc/skill-routes/__tests__/registries.test.ts— 16/16AGENTS.md compliance
Grepped
assistant/AGENTS.mdforexecutionMode|ToolDefinition|LoadedTool|plugin-api|skill manifest|breaking change|public surface— no conventions apply to this move.Out of scope
Toolinterface inpackages/skill-host-contracts/src/tool-types.tshas a different shape entirely (flat interface withgetDefinition(): ToolDefinition+ inline fields). That's its own audit, tracked as a next-candidate in the workstream.executionMode: "proxy"+executionTarget: "host"dual-stamps at definition sites are intentional per tools: stamp executionTarget at load time (move Tool field to LoadedTool) #32017's design (load-time stamping enforced at the type level).