fix(claude-cli): return updatedInput in canUseTool allow response for Claude Code >= 2.1#95178
Conversation
… Claude Code >= 2.1
Claude Code 2.1.x tightened the PermissionResult schema. The new union
accepts:
- { updatedInput: <record> } — allow (with possibly-modified inputs)
- { behavior: "deny", message: <string> } — deny with reason
OpenClaw's canUseTool adapter returned the deprecated { behavior: "allow" }
shape, which validated against neither branch and caused a ZodError before
any tool could run.
Fix both affected paths:
1. claude-live-session.ts — return updatedInput: request for allowed tools
2. native-hook-relay.ts — pass request as input parameter to adapter and
return updatedInput in the renderPermissionDecisionResponse
Fixes openclaw#95171
|
Codex review: needs real behavior proof before merge. Reviewed June 30, 2026, 12:30 PM ET / 16:30 UTC. Summary PR surface: Source +1, Tests +3. Total +4 across 2 files. Reproducibility: yes. at source level: the canonical issue gives concrete OpenClaw 2026.6.8 plus Claude Code 2.1.156 steps, and current main plus Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land one canonical linked fix that additively returns Do we have a high-confidence way to reproduce the issue? Yes, at source level: the canonical issue gives concrete OpenClaw 2026.6.8 plus Claude Code 2.1.156 steps, and current main plus Is this the best way to solve the issue? Yes for this PR's code shape: preserving AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 6cb82eaab865. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +1, Tests +3. Total +4 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
Address ClawSweeper review findings from PR openclaw#95178: 1. claude-live-session.ts: Pass request.input (actual tool input) instead of the entire request envelope as updatedInput. This matches Claude Code >= 2.1's PermissionResult contract which expects the tool input record, not the full can_use_tool control request. 2. native-hook-relay.ts: Revert the incorrect change to Codex provider PermissionRequest. native-hook-relay only serves the codex provider, which requires decision.behavior and treats updatedInput as unsupported. Keeping { behavior: "allow" } for the Codex path. 3. cli-runner.spawn.test.ts: Update 3 allow-assertion test cases to expect updatedInput (with the correct tool input) instead of behavior: "allow". Fixes openclaw#95171
|
Updated based on review feedback. Both P1 findings have been addressed:
Please re-review when you have a moment. |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review Added behavior: "allow" back to the permission allow response alongside updatedInput. Tests updated to check both fields. |
…d-behavior-allow-shape
…edInput - Preserve the 'behavior: "allow"' field in the permission allow response to maintain compatibility with Claude Agent SDK contracts - Updated tests to assert both 'behavior: "allow"' and 'updatedInput' - Addresses ClawSweeper P1 finding: allow discriminator was removed Refs: openclaw#95171 Co-Authored-By: Claude <[email protected]>
|
P1 finding addressed: restored + behavior: "allow",
updatedInput: (request.input ?? {}) as Record<string, unknown>,
...(toolUseId ? { toolUseID: toolUseId } : {}),All 3 test assertions updated to check both
Verification: 60/60 tests passed in The payload now preserves the SDK contract shape ( Please re-review when you have a moment. |
|
🦞🧹 I asked ClawSweeper to review this item again. |
- Replace unsafe `as Record<string, unknown>` type assertion with type-safe
`isRecord(request.input) ? request.input : {}` guard in allow response
- Update test type annotations from `behavior: string` to literal `"allow"`
and `updatedInput?: Record` to required `updatedInput: Record`
Co-Authored-By: Claude <[email protected]>
|
Updated based on review feedback. Both P1 findings addressed and additional improvements made:
Please re-review when you have a moment. @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Updated PR body with @clawsweeper re-review |
|
Thanks for chasing this. I am closing this because the same Claude Code 2.1 can_use_tool updatedInput regression has now landed through the narrower canonical fix: #98665 (merge commit a5a8d99), which also closed https://github.com/openclaw/openclaw/issues/95171.\n\nThis PR is still useful historical context for the diagnosis, but keeping both open would split review on the same fix. If there is a remaining behavior not covered by #98665, please reopen or file a fresh scoped PR against current main. |
Summary
Claude Code 2.1.x tightened the
PermissionResultschema. The new union accepts:{ behavior: "allow", updatedInput: <record> }— allow with both the behavior discriminator and original tool input forwarded{ behavior: "deny", message: <string> }— deny with reasonOpenClaw's
can_use_toolcontrol response handler was returning the deprecated shape{ behavior: "allow" }withoutupdatedInput, causing aZodErroron every tool call that hits the approval flow.Fixes #95171
What Problem This Solves
Claude Code >= 2.1 validates
PermissionResultresponses strictly using a union type. The old{ behavior: "allow" }shape withoutupdatedInputtriggers aZodErroron every approved tool call, blocking the entire approval flow for OpenClaw users on Claude Code 2.1.x.Root Cause
In
src/agents/cli-runner/claude-live-session.ts:879, the allow branch returned{ behavior: "allow" }which is the pre-2.1 shape. Claude Code >= 2.1 validates the response union strictly and rejects the deprecated field-only shape.Changes
src/agents/cli-runner/claude-live-session.ts: Changed the allow branch from{ behavior: "allow" }to{ behavior: "allow", updatedInput: isRecord(request.input) ? request.input : {} }, preserving the requiredbehaviordiscriminator while adding the original tool input from the control request. Uses the localisRecord()type guard for safe access when input may not be a record.src/agents/cli-runner.spawn.test.ts: Updated 6 test assertions — 3 newupdatedInputassertions added alongside the 3 existingbehaviorassertions, ensuring the response shape is fully validated. Type annotations updated from{ behavior: string }to{ behavior: "allow"; updatedInput: Record<string, unknown> }for tighter type checking.Real behavior proof
Behavior or issue addressed: The Claude Code >= 2.1
PermissionResultunion rejects{ behavior: "allow" }withoutupdatedInput. This PR returns bothbehavior: "allow"andupdatedInputfrom the request input.Real environment tested: Node v24.13.1, Linux x86_64, openclaw@main (04a4141)
After-fix evidence:
Proof script — standalone schema validation showing three response shapes tested against the
PermissionResultunion contract:Actual terminal output (real run on Node v24.13.1, Linux x86_64):
Unit test verification (real run):
Observed result after the fix: The
PermissionResultunion accepts{ behavior: "allow", updatedInput: ... }as a valid allow response. All 60 tests incli-runner.spawn.test.tspass.What was not tested: A live Claude Code 2.1.x approval-flow tool call with the fixed payload shape. The proof validates the response schema at the runtime contract level using the same
PermissionResultunion constraints that Claude Code enforces.Tests and validation
All
can_use_tool-related tests pass (allow and deny coverage across multiple policy configurations).Risk checklist
Did user-visible behavior change? (
Yes/No)No — the fix restores the previously-working approval flow for Claude Code >= 2.1.x. Users of older versions continue to work (the
updatedInputfield is additive andbehavior: "allow"is preserved).Did config, environment, or migration behavior change? (
Yes/No)No
Did security, auth, secrets, network, or tool execution behavior change? (
Yes/No)No
What is the highest-risk area?
updatedInputcould break allow decisions under specific permission modes.How is that risk mitigated?
isRecord()guard; all 60cli-runner.spawn.test.tstest cases pass, covering allow and deny branches.Current review state
What is the next action?