Repository navigation
fix(ai-code-mode): reject tools that require approval - #1949
Conversation
A tool marked `needsApproval: true` expects a human approval interrupt before it runs. Code Mode invokes tools from inside the sandbox, where the model only ever sees the outer `execute_typescript` call, so the per-tool interrupt is never raised and the tool would run unattended. The `CodeModeTool` type already pins `needsApproval` to `false`, so typed callers were blocked at compile time. This adds the matching runtime guard for callers that bypass the types (JavaScript, `as` casts, dynamically built tools): - `createCodeModeTool` / `createCodeMode` throw synchronously when any supplied tool has `needsApproval: true`, naming every offending tool. - `toolToBinding` / `toolsToBindings` enforce the same rule as a lower-level backstop, so every binding path is covered. Approving only the outer `execute_typescript` call would not preserve per-tool/per-argument approval semantics, so these tools are rejected rather than silently approved. Addresses GHSA-wmh6-35qp-cc2x. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-mode-approval-guard # Conflicts: # docs/code-mode/code-mode.md # docs/config.json # packages/ai-code-mode/tests/create-code-mode-tool.test.ts # packages/ai-code-mode/tests/tool-to-binding.test.ts
🦋 Changeset detectedLatest commit: 914f5aa The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
View your CI Pipeline Execution ↗ for commit 914f5aa
☁️ Nx Cloud last updated this comment at |
Coverage✅ Coverage held across 8 compared package(s). Each package is measured twice in this job — on this PR and on its merge-base with
|
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Rejection checks and guidance packages/ai-code-mode/src/bindings/tool-to-binding.ts, packages/ai-code-mode/src/create-code-mode-tool.ts, packages/ai-code-mode/tests/*, docs/code-mode/code-mode.md, .changeset/code-mode-reject-needs-approval.md, docs/config.json |
Binding conversion and Code Mode tool creation throw errors for tools marked needsApproval: true. Tests cover rejection, error names, lazy tools, and tools marked false. Documentation and the changeset describe the restriction and configuration guidance. The Code Mode documentation date was updated. |
Priority:
Estimated code review effort: 2 (Simple) | ~10 minutes
Change: Bug fix
Suggested reviewers: alemtuzlak
Merge Risk: 
Minimal · up to 914f5
-
Approval-required tools are rejected during setup. No merge-blocking risk was identified.
Pre-merge checks: 
5
-
Full details: Title check
-
Explanation
The title "fix(ai-code-mode): reject tools that require approval" names the main change: Code Mode now throws on tools marked needsApproval: true. It follows conventional-commit form and names the affected package. The title is specific and matches the changeset, the runtime guards in createCodeModeTool and toolToBinding, and the tests.
Full details: Description check
-
Explanation
The description follows the template. It includes the Changes section with the reasoning and the rejection behavior. The checklist is filled in. The description adds a root cause, alternatives, testing evidence, risk and rollback, and a public API note. One checklist item is unchecked: the author did not run
pnpm run test:prend to end. The author explains this in the Testing section, along with the targets that did run. The Release Impact section is marked for a changeset, and the changeset file is present in the summary.
Full details: Docstring Coverage
-
Explanation
Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 3 functions across 5 files. (3 skipped: 3 unsupported.)
-
Finishing Touches
-
📝 Generate docstrings
-
- Commit to this branch
- Create a new PR
🧪 Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands.
@tanstack/ai
@tanstack/ai-acp
@tanstack/ai-angular
@tanstack/ai-anthropic
@tanstack/ai-bedrock
@tanstack/ai-byteplus
@tanstack/ai-claude-code
@tanstack/ai-client
@tanstack/ai-cloudflare
@tanstack/ai-code-mode
@tanstack/ai-code-mode-snippets
@tanstack/ai-codex
@tanstack/ai-cohere
@tanstack/ai-compaction
@tanstack/ai-computer-local
@tanstack/ai-computer-playwright
@tanstack/ai-computer-sandbox
@tanstack/ai-computer-use
@tanstack/ai-devtools-core
@tanstack/ai-durable-stream
@tanstack/ai-elevenlabs
@tanstack/ai-event-client
@tanstack/ai-fal
@tanstack/ai-gemini
@tanstack/ai-grok
@tanstack/ai-grok-build
@tanstack/ai-groq
@tanstack/ai-harness
@tanstack/ai-harness-cli
@tanstack/ai-isolate-cloudflare
@tanstack/ai-isolate-daytona
@tanstack/ai-isolate-e2b
@tanstack/ai-isolate-node
@tanstack/ai-isolate-quickjs
@tanstack/ai-isolate-quickjs-bun
@tanstack/ai-llmgateway
@tanstack/ai-lovable
@tanstack/ai-mcp
@tanstack/ai-memory
@tanstack/ai-mistral
@tanstack/ai-neon
@tanstack/ai-octane
@tanstack/ai-ollama
@tanstack/ai-ollaya
@tanstack/ai-openai
@tanstack/ai-opencode
@tanstack/ai-openrouter
@tanstack/ai-perplexity
@tanstack/ai-persistence
@tanstack/ai-preact
@tanstack/ai-react
@tanstack/ai-reactor
@tanstack/ai-remix
@tanstack/ai-sandbox
@tanstack/ai-sandbox-blaxel
@tanstack/ai-sandbox-boxd
@tanstack/ai-sandbox-cloudflare
@tanstack/ai-sandbox-daytona
@tanstack/ai-sandbox-docker
@tanstack/ai-sandbox-e2b
@tanstack/ai-sandbox-local-process
@tanstack/ai-sandbox-railway
@tanstack/ai-sandbox-sprites
@tanstack/ai-sandbox-upstash-box
@tanstack/ai-sandbox-vercel
@tanstack/ai-skills
@tanstack/ai-solid
@tanstack/ai-svelte
@tanstack/ai-typesafe
@tanstack/ai-utils
@tanstack/ai-vercel-gateway
@tanstack/ai-vertex
@tanstack/ai-vue
@tanstack/ai-worldlabs
@tanstack/openai-base
@tanstack/preact-ai-devtools
@tanstack/react-ai-devtools
@tanstack/solid-ai-devtools
@tanstack/svelte-ai-devtools
commit: |
Code Mode could run a tool that needs approval without asking. A tool marked
needsApproval: trueexpects a human to approve each call before it runs. Code Mode converted such a tool into a sandbox binding and ran it from insideexecute_typescript, so no approval interrupt fired. This PR rejects those tools instead. Addresses GHSA-wmh6-35qp-cc2x.🎯 Changes
The
CodeModeTooltype already pinsneedsApprovaltofalse, so typed callers were blocked at compile time. This adds the matching runtime guard for callers that bypass the types (JavaScript,ascasts, or dynamically built tools):createCodeModeToolandcreateCodeModethrow at setup when any tool hasneedsApproval: true, and name every offending tool.toolToBindingandtoolsToBindingsthrow too, as a lower-level backstop. Every binding path funnels through them.A note on the behavior went into the Code Mode docs. A changeset is included.
✅ Checklist
pnpm run test:pr, or these tests do not apply to this pull request.docs/for this change, or this change is not user-facing.pnpm changeset), or this PR does not change a published package.🚀 Release Impact
Root cause
Issue. A host tool marked
needsApproval: trueran with no human approval when an app passed it to Code Mode and the model droveexecute_typescriptto call it.Cause.
toolToBindinginpackages/ai-code-mode/src/bindings/tool-to-binding.tsdroppedneedsApprovaland wrappedtool.executedirectly.createCodeModeToolinpackages/ai-code-mode/src/create-code-mode-tool.tssent every tool throughtoolsToBindingswith no check.chat()saw only the outerexecute_typescriptcall, so it raised no per-tool approval interrupt for the nested host call.Fix. Both functions now reject a tool whose
needsApprovalis true.createCodeModeToolthrows at setup and names every offending tool.toolToBindingthrows as the lower-level backstop, so no binding is ever produced for an approval-gated tool.Possible alternatives
toolToBinding. The smaller change that kills the repro, becausecreateCodeModeToolcallstoolsToBindings, which callstoolToBinding. This PR also keeps thecreateCodeModeToolguard so setup fails fast with one clear error that names every offending tool, before any binding or secret-scan work. This is the two-layer fix the advisory recommended.execute_typescriptcall. Rejected. One approval on the wrapper cannot carry per-tool or per-argument approval, so it would weaken the guarantee the app asked for.Testing
Commands run (on the branch, after merging latest
main):nx run-many --targets=test:lib,test:types,test:oxlint,build --projects=@tanstack/ai-code-mode— pass (209 unit tests).pnpm test:docs— pass (no broken links).pnpm test:sherif— pass (0 errors; pre-existing warnings only).pnpm test:knip— pass (exit 0;ai-code-modenot flagged).pnpm test:prcommand end to end. I ran its main targets for the affected package plus the doc, sherif, and knip checks above.Gate 1 repro (agent-written, run this session). An agent-authored test asserts that
toolsToBindingsandcreateCodeModeToolreject aneedsApprovaltool and that the host tool never runs.On clean
main(the bug), it fails:On this branch (fixed), the same test passes:
Manual test:
needsApproval: trueon it.createCodeMode({ driver, tools: [tool] }).main: no error, and the tool runs insideexecute_typescriptwith no approval.createCodeModethrows and names the tool, so it never reaches the sandbox.How this PR makes testing easy. The branch adds unit tests at every rejection point (
toolToBinding,toolsToBindings,createCodeModeTool,createCodeMode), including the aggregated-name and lazy-tool cases, plus an "explicitly false still allowed" case. The E2E suite has no Code Mode tool-config harness, so this stays at the unit layer.Risk / rollback
Low risk. The change only adds a throw for input the
CodeModeTooltype already rejected at compile time. Typed callers see no change. A JavaScript or cast caller that relied on the old unguarded behavior now gets a clear error. To roll back, revert this PR.Public API change
No signature change. The runtime behavior changes for one invalid input: an approval-gated tool.
Before
After
Summary by CodeRabbit