fix(server): V2 Grok launches in the thread's permission mode - #13616
Conversation
The V2 ACP runtime input carried no runtime mode, so GrokAdapterV2 always launched `grok agent stdio` and every thread ran in Grok's ask mode. The ACP runtime input now carries the session's runtime policy and Grok maps it to its launch flags. Auto-accept edits launches asking (Grok's agent ignores `--permission-mode acceptEdits`) and T3's ACP policy approves edit prompts while asking for everything else. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — The production path now maps thread policies to Grok launch permissions and changes shared ACP decisions for filesystem mutations, terminal commands, and other operations. Tests cover the mappings, but the permission-sensitive runtime behavior and cross-provider ACP impact require careful human validation. You can add or adjust custom eligibility rules. Learn more. |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
V2 Grok ignored the thread's permission mode.
AcpAdapterV2RuntimeInputhad no runtime mode, soGrokAdapterV2always launched plaingrok agent stdio. Every Grok thread therefore ran in Grok's ask mode, and T3 imitated the other modes by auto-answering Grok's prompts. V1 passed the mode at launch (GrokAdapter.ts:1009on main).What changed
AcpAdapterV2RuntimeInputnow carries the session'sruntimePolicy, andGrokAdapterV2maps it to Grok's launch flags. Only Grok reads it. A runtime-mode change already detaches Grok sessions (supportsRuntimeModeSwitchInSession: false), so the next turn reopens the session with the new flags. The replay recorder launches the same way as production.--always-approveor Grok's auto classifier would skip the permission prompts that T3's policy checks.--permission-mode acceptEdits, becausegrok agentignores it. Grok launches indefaultmode, and T3's ACP client policy approves edit, delete and move prompts while commands and other actions still ask. Grok's prompts carry nolocations, so this rule cannot confine edits to the workspace. Registry agents on Auto-accept edits get the same rule: before this change they were auto-approved for everything, including commands.What each mode does in Grok (source at xai-org/grok-build f0e3be1, CLI 1.0.41)
--permission-mode default agent stdio[ui] permission_mode = "always-approve".--permission-mode default agent stdio--permission-mode auto agent stdioagent --always-approve stdioSource citations:
--always-approve(alias--yolo) is anAgentArgsflag:crates/codegen/xai-grok-pager/src/app/cli.rs:274-276.--permission-modeis a top-level flag validated againstPermissionMode::VALID_VALUES:cli.rs:671-679,crates/codegen/xai-grok-agent/src/config.rs:919-941. The enum comment there reads "OnlyBypassPermissionsis wired at spawn".run_agent_commanduses--permission-modeonly to decide yolo and auto:crates/codegen/xai-grok-pager-bin/src/main.rs:1316-1331.resolve_effective_yolotreats onlybypassPermissions/always-approveas yolo (crates/codegen/xai-grok-shell/src/util/config/permissions.rs:234-245), andeffective_auto_for_launchreturnsmode == "auto"(permissions.rs:187-216).acceptEditsis consumed only by headless-p(crates/codegen/xai-grok-pager/src/headless.rs:861-873). A live probe on 1.0.41 confirmed it: writes asked identically underacceptEditsanddefault.crates/codegen/xai-grok-workspace/src/permission/manager/mod.rs:778-786. Deny rules run first (:762-775).session/newandsession/load(crates/codegen/xai-grok-shell/src/agent/mvp_agent/session_setup.rs:450-455,:1106-1110), so resumed sessions get the new mode too.x.ai/exit_plan_mode) is intercepted whatever the yolo state (crates/codegen/xai-grok-shell/src/session/acp_session_impl/tool_calls.rs:119-131), so Plan mode still reaches T3 under Full access.Unchanged but worth knowing: rejecting a Grok prompt ends the whole turn (
stopReason: "cancelled",cancellationCategory: "PermissionRejected").Fixtures
No fixture changed. The replay harness swaps in its own
makeRuntime, so spawn args are never part of replay. Every Grok fixture's thread is Full access. Onlytool_call_read_only_on_requestcontains a permission prompt, and it carries anon-requestoverride, which still launches asking. I re-recordedsimple(now--always-approve; Grok reports"yolo":true) andtool_call_read_only_on_request(still asking, still one prompt) live with Grok 1.0.41. Both replayed green through the orchestrator. Their non-streaming frame sequences match the committed fixtures, apart from nondeterministic session-summary notifications. I kept the committed transcripts because a re-record would only add churn in model text.Verification
GrokAdapterV2.test.ts: new tests open a session through the real GrokmakeRuntimepath with a spawner that records the argv and then fails the spawn. They cover each of the four modes plus the override case. With theruntimeModeline removed, all 5 fail (expected [['agent','stdio']]).vp test runonGrokAdapterV2.test.ts,GrokAcpSupport.test.ts,AcpClientPolicy.test.ts: 57 passed.vp test runonAcpAdapterV2.test.ts,AntigravityAdapterV2.test.ts,AcpRegistryAdapterV2.test.ts,AntigravityAcpSupport.test.ts: 154 passed.OrchestratorReplayFixtures.integration.test.ts -t "grok|acpRegistry": 19 passed.simpleandtool_call_read_only_on_requestthroughrecord-grok-acp-replay-fixture.ts(Grok 1.0.41), replayed green (not committed).tsc --noEmit -p apps/server: noerror TSorwarning TS.vp run knip:check: clean.vp linton touched files: no new warnings.Model: Claude Opus 5.5 (Claude Code)
🤖 Generated with Claude Code