fix(server): Grok's Auto mode asks you about commands its classifier blocks - #13732
Conversation
…blocks Grok only prompts for a classifier-blocked action when the ACP client declares a type that can show prompts; T3 declared none, so Grok denied those commands silently. T3 now initializes Grok as clientType "extension". Grok's prompt in Auto mode also no longer goes through T3's own policy, which would have approved it: the Grok flavor surfaces every Auto-mode permission request to the user. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
| {"type":"emit_inbound","label":"notification:session/update:agent_thought_chunk:t3-xai-prompt-1","frame":{"kind":"notification","method":"session/update","params":{"sessionId":"00000000-0000-4000-8000-000000000001","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" to"}},"_meta":{"totalTokens":23049,"eventId":"00000000-0000-4000-8000-000000000001-74","agentTimestampMs":1790383233408,"promptId":"t3-xai-prompt-1","streamStartMs":1790383232746,"turnStartMs":1790383223110,"updateType":"AgentThoughtChunk","chunkId":10}}}} | ||
| {"type":"emit_inbound","label":"notification:session/update:agent_thought_chunk:t3-xai-prompt-1","frame":{"kind":"notification","method":"session/update","params":{"sessionId":"00000000-0000-4000-8000-000000000001","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" reply"}},"_meta":{"totalTokens":23049,"eventId":"00000000-0000-4000-8000-000000000001-75","agentTimestampMs":1790383233408,"promptId":"t3-xai-prompt-1","streamStartMs":1790383232746,"turnStartMs":1790383223110,"updateType":"AgentThoughtChunk","chunkId":11}}}} | ||
| {"type":"emit_inbound","label":"notification:session/update:agent_thought_chunk:t3-xai-prompt-1","frame":{"kind":"notification","method":"session/update","params":{"sessionId":"00000000-0000-4000-8000-000000000001","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" exactly"}},"_meta":{"totalTokens":23049,"eventId":"00000000-0000-4000-8000-000000000001-76","agentTimestampMs":1790383233408,"promptId":"t3-xai-prompt-1","streamStartMs":1790383232746,"turnStartMs":1790383223110,"updateType":"AgentThoughtChunk","chunkId":12}}}} | ||
| {"type":"emit_inbound","label":"notification:session/update:agent_thought_chunk:t3-xai-prompt-1","frame":{"kind":"notification","method":"session/update","params":{"sessionId":"00000000-0000-4000-8000-000000000001","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":":"}},"_meta":{"totalTokens":23049,"eventId":"00000000-0000-4000-8000-000000000001-77","agentTimestampMs":1790383233408,"promptId":"t3-xai-prompt-1","streamStartMs":1790383232746,"turnStartMs":1790383223110,"updateType":"AgentThoughtChunk","chunkId":13}}}} |
There was a problem hiding this comment.
🟡 Medium grok_auto_blocked_command/grok_transcript.ndjson:118
The NDJSON record on line 118 is malformed, so readProviderReplayTranscript fails during JSON decoding before the replay reaches the permission approval flow. The text field has an extra colon instead of a JSON value; replace it with the intended string value so the regression test can execute.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/server/src/orchestration-v2/testkit/fixtures/grok_auto_blocked_command/grok_transcript.ndjson around line 118:
The NDJSON record on line 118 is malformed, so `readProviderReplayTranscript` fails during JSON decoding before the replay reaches the permission approval flow. The `text` field has an extra colon instead of a JSON value; replace it with the intended string value so the regression test can execute.
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. |
fd4d3f0
into
t3code/codex-turn-mapping
In T3's Auto mode, Grok silently denied every command its classifier held (
rm -rf /tmp/…,curl … | sh,sudo -n id). The tool call failed with "Auto mode blocked this action" and you were never asked. Two layers caused it, and fixing only the first would have been worse than today.Layer 1: Grok never asked T3
Grok asks the client about a classifier Block only
if client_type.can_present_permission_prompt(). Otherwise it returns aPolicyDeny, and keeps doing so until 3 consecutive or 20 total denials (permission/manager/mod.rs:1000-1060). That check is false only forClientType::Generic(permission/types.rs:105). The type comes frominitialize_meta.clientTypeor_meta.clientIdentifier(acp_agent.rs:180-200). T3 sent neither, so it was Generic.T3 now sends
_meta: { clientType: "extension" }on Grok'sinitialize(Grok runtime only, through a newinitializeMetaoption onAcpSessionRuntime).Why
extension. I checked every place Grok branches on client type:prompter.rs:48-53, 519, 573-612, 671-727):extensiongets the same generic bash and fallback options Generic gets today (always-allow/allow-once/reject-once/reject-always). T3 already maps these by kind.grok_desktop(like TUI and Pager) adds anenable-always-approveoption first, with kindallow_once. T3 picks the firstallow_oncefor "accept", so approving once would pick that option instead. It also addsallow-always-command/reject-always-command, which persist per-command grants in Grok's own project state.code_nav.rs:49: onlyGrokWeb.extensionis unaffected.spawn.rs:1470-1479), the User-Agent origin productgrok-code-extension(xai-grok-http/src/lib.rs:205-225), and the review-comment record. Leader-mode yolo scoping keys onclientIdentifier, which T3 still does not send.So
extensionis the prompting type with the fewest side effects.Layer 2: T3 would have approved the prompt itself
Every
session/request_permissiongoes throughacpPermissionDisposition. With no explicit sandbox, that returnsallowin T3's Auto mode, so T3 would have picked the allow option and the blockedrm -rfwould have run. A newpermissionDispositionhook on the ACP flavor lets the Grok flavor returnaskwhenever it launched Grok with--permission-mode auto. In that mode Grok has already decided the routine actions, so anything it still asks about goes to you. Other modes keep the shared policy:--always-approveand sends no prompts.Registry and other ACP flavors are unchanged.
Proof: live replay fixture
grok_auto_blocked_commandRecorded from Grok 1.0.41 (
grok-4.7-build-fast) with T3 in Auto mode. Grok runsecho grok-auto-routine-okwith no request, thenrm -rf ~/grok-auto-probe-does-not-exist(the path never exists). Grok's classifier holds that and sendssession/request_permission. T3 shows it as a pending runtime request (captured in the shell snapshot while pending). The fixture approves it (harmless, the path is absent), Grok answersallow-once, and the turn completesend_turn. I also tried declining, but Grok ends the turncancelled(PermissionRejected), so the fixture approves.With either layer removed, the fixture fails:
_meta(live run): Grok emitsTool run_terminal_command was not executed: Auto mode blocked this action (Recursive rm of a home-directory path is an irreversible non-scratch delete and must wait). It sends nosession/request_permission, the run completes, and the approve step finds no request. Replaying that transcript fails the same way.The 12 existing Grok transcripts now expect the new
initialize_meta. The recorder pins_metaalongsideclientCapabilities. Fixtures can now set the thread's runtime mode (default stays full access).Verification
vp test run src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts -t grok: 13 passed (all Grok replays, including the new one).vp test run GrokAdapterV2.test.ts AcpAdapterV2.test.ts GrokAcpSupport.test.ts: 150 passed.vp exec tsc --noEmit -p .(apps/server): no errors or warnings.vp run knip:check: clean.vp linton touched files: only 4 warnings that already existed._meta.~/grok-auto-probe-does-not-existnever existed.Model: Claude Opus 5.5 (Claude Code)
🤖 Generated with Claude Code