From f407ed961bc9f0e146657439a9fbfb113604362e Mon Sep 17 00:00:00 2001 From: vansin Date: Thu, 13 Aug 2026 14:24:47 +0800 Subject: [PATCH 1/7] fix(grok): deny vendor terminal tool in copresence --- .../src/runtime/grok-copresence/profile-process.test.ts | 1 + agent-node/src/runtime/grok-copresence/runtime.test.ts | 1 + agent-node/src/runtime/grok-copresence/runtime.ts | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/agent-node/src/runtime/grok-copresence/profile-process.test.ts b/agent-node/src/runtime/grok-copresence/profile-process.test.ts index d0bf43e58..dd65b50cc 100644 --- a/agent-node/src/runtime/grok-copresence/profile-process.test.ts +++ b/agent-node/src/runtime/grok-copresence/profile-process.test.ts @@ -68,6 +68,7 @@ describe("Grok co-presence profile is pinned for the whole process", () => { for (const result of [restricted, xSearch, repoRead]) { const denied = result.args.flatMap((value, index) => result.args[index - 1] === "--deny" ? [value] : []); + expect(denied).toContain("run_terminal_command"); expect(denied).toContain("Bash"); expect(denied).toContain("Write"); expect(denied).toContain("WebFetch"); diff --git a/agent-node/src/runtime/grok-copresence/runtime.test.ts b/agent-node/src/runtime/grok-copresence/runtime.test.ts index b1a80797f..a114a0319 100644 --- a/agent-node/src/runtime/grok-copresence/runtime.test.ts +++ b/agent-node/src/runtime/grok-copresence/runtime.test.ts @@ -235,6 +235,7 @@ describe("Grok copresence launch and injection policy", () => { expect(args).toContain("--always-approve"); expect(args).not.toContain("MCPTool"); const denied = args.flatMap((value, index) => args[index - 1] === "--deny" ? [value] : []); + expect(denied).toContain("run_terminal_command"); expect(denied).toContain("Bash"); expect(denied).toContain("Write"); expect(denied).toContain("WebFetch"); diff --git a/agent-node/src/runtime/grok-copresence/runtime.ts b/agent-node/src/runtime/grok-copresence/runtime.ts index 9f4d9f7a9..e82c76291 100644 --- a/agent-node/src/runtime/grok-copresence/runtime.ts +++ b/agent-node/src/runtime/grok-copresence/runtime.ts @@ -421,6 +421,10 @@ export function buildGrokCopresenceArgs(opts: BuildGrokCopresenceArgsOptions): s // The shared process must read its owner-only GROK_AUTH_PATH after its // sandbox re-exec. Shell access would bypass path-specific Read/Grep/Edit // rules, so the experimental preview gives up terminal tools entirely. + // Pinned Grok 0.2.93 emits `run_terminal_command` in its permission + // lifecycle. `Bash` is only the cross-runtime policy alias and does not + // deny that vendor-native tool name by itself. + "--deny", "run_terminal_command", "--deny", "Bash", "--deny", "Write", "--deny", "WebFetch", From 78a6de9c9d1277936326ce31db0561bc90f07151 Mon Sep 17 00:00:00 2001 From: vansin Date: Thu, 13 Aug 2026 14:35:30 +0800 Subject: [PATCH 2/7] docs(tests): record Grok terminal deny evidence --- .../report-grok-copresence-terminal-deny.txt | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 docs/tests/report-grok-copresence-terminal-deny.txt diff --git a/docs/tests/report-grok-copresence-terminal-deny.txt b/docs/tests/report-grok-copresence-terminal-deny.txt new file mode 100644 index 000000000..4b35c6faa --- /dev/null +++ b/docs/tests/report-grok-copresence-terminal-deny.txt @@ -0,0 +1,93 @@ +# Grok co-presence vendor terminal deny — exact-source evidence + +Date: 2026-08-13 +Base: 114967626f20c7ef036c3d0e0dab295e1f983a89 (#826 report head) +Source commit: f407ed961bc9f0e146657439a9fbfb113604362e +Image: anet-test830:f407ed96 +Image ID: sha256:91b8e8b06603f8b7267e2bb3a507a29bf19adf85a77ad7ada7323807643a54ba + +## Live trigger that motivated the fix + +A prompt-contained PR review explicitly prohibited tools and the Grok TUI rendered +`Run No-op; review is prompt-only`. The pinned Grok 0.2.93 `events.jsonl` then emitted this exact lifecycle: + +```text +permission_requested tool_name=run_terminal_command +permission_resolved tool_name=run_terminal_command decision=allow wait_ms=0 +tool_completed tool_name=run_terminal_command outcome=success +``` + +The pre-fix argv denied only the cross-runtime name `Bash`. The co-presence supervisor subsequently failed +closed with `grok_failure:approval_boundary` and stopped the TUI, but that happened after the vendor had already +reported the terminal tool as completed. Therefore the previous claim that shell was unavailable was false. + +The affected node was restored with its unchanged owner-only config and the pinned Grok binary into a fresh +session. This report does not treat that recovery as proof of the post-fix deny behavior. + +## Source delta + +The source adds the vendor-native permission name to the launch argv while retaining the legacy policy alias: + +```text +--deny run_terminal_command +--deny Bash +``` + +Both the direct launch-policy test and the three-profile process test require the new exact deny entry. Changed +source files and Git-blob-equivalent SHA-256 values: + +```text +a62d5e3bbbc2bdf2524a7b561bca2827e28a3f69386a94a37aa042c8c52ae33c agent-node/src/runtime/grok-copresence/runtime.ts +72b449eaa314f0f9b32eba8b1ca92cf0020ace003613fa90665a9694b5b106c4 agent-node/src/runtime/grok-copresence/runtime.test.ts +48a22832fcfda5576fba852d6d012d6d3200e1ed0998fe134df35865c47e49f0 agent-node/src/runtime/grok-copresence/profile-process.test.ts +``` + +## Docker evidence + +The image was built from source commit `f407ed961bc9f0e146657439a9fbfb113604362e` with +`tests/test725-agent-node-unit-ci/Dockerfile` and pinned Bun 1.3.14. + +Full non-root agent-node unit domain: + +```text +1284 pass +0 fail +4410 expect() calls +Ran 1284 tests across 91 files. [115.95s] +MUTATION_RED readable-attachment-runtime-disconnected rc=1 +RESULT: PASS +``` + +Focused clean run: + +```text +54 pass +0 fail +456 expect() calls +Ran 54 tests across 2 files. [60.59s] +``` + +Witnessed-red removed only the exact production argv entry +`"--deny", "run_terminal_command",` after first requiring one occurrence and a byte-changing mutation. The same +two focused test files then failed in both independent assertions: + +```text +52 pass +2 fail +432 expect() calls +Ran 54 tests across 2 files. [60.70s] +MUTATION_RED vendor-terminal-deny-removed rc=1 +``` + +The mutation harness required the named Bun failure +`Expected to contain: "run_terminal_command"`; an unrelated red did not count. + +## Honest limits + +- NOT COVERED: a released build has not yet been run against the real Grok 0.2.93 vendor after this change. + The decisive post-fix gate is a fresh-session task that previously caused `run_terminal_command`; the event + stream must show no permission request, resolution, or completion for that tool. +- The Docker evidence proves exact argv construction and regression sensitivity. It does not prove that a future + Grok binary preserves the same permission-rule vocabulary; the existing exact version pin remains required. +- This delta addresses the observed terminal tool only. It does not broaden repo-read capability, merge/publish + authority, or make claims about unobserved vendor-native write/media tools. From 5526952731b2e42358e101128270548e8ece516c Mon Sep 17 00:00:00 2001 From: vansin Date: Thu, 13 Aug 2026 14:45:09 +0800 Subject: [PATCH 3/7] fix(grok): deny native effectful tool surface --- .../src/runtime/grok-copresence/policy.ts | 24 +++++++++++++++++++ .../grok-copresence/profile-process.test.ts | 6 ++++- .../runtime/grok-copresence/runtime.test.ts | 6 ++++- .../src/runtime/grok-copresence/runtime.ts | 10 ++++---- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/agent-node/src/runtime/grok-copresence/policy.ts b/agent-node/src/runtime/grok-copresence/policy.ts index dde513951..9a8320d55 100644 --- a/agent-node/src/runtime/grok-copresence/policy.ts +++ b/agent-node/src/runtime/grok-copresence/policy.ts @@ -30,6 +30,30 @@ export const GROK_COPRESENCE_EFFECTIVE_TOOLS = Object.freeze([ ...(GROK_COPRESENCE_REPO_READ_ENABLED ? ["read_file", "grep", "list_dir"] : []), ]); +/** + * Vendor-native effectful tools that must remain unavailable even when the + * pinned interactive TUI ignores the agent profile's `tools` inventory. + * Keep the Claude-compatible aliases in runtime argv too, but never rely on + * them as translations for Grok's lifecycle names. + */ +export const GROK_COPRESENCE_VENDOR_DENY_TOOLS = Object.freeze([ + "run_terminal_command", + "run_terminal_cmd", + "search_replace", + "write_file", + "edit_file", + "apply_patch", + "web_fetch", + "http_request", + "image_gen", + "generate_image", + "video_gen", + "generate_video", + "browser", + "computer", + "screenshot", +]); + export const GROK_COPRESENCE_AGENT_NAME = "anet-copresence-preview"; export const GROK_COPRESENCE_AGENT_FILE = `${GROK_COPRESENCE_AGENT_NAME}.md`; export const GROK_COPRESENCE_PROFILE_MARKER = "ANET_COPRESENCE_PROFILE_V1"; diff --git a/agent-node/src/runtime/grok-copresence/profile-process.test.ts b/agent-node/src/runtime/grok-copresence/profile-process.test.ts index dd65b50cc..ac57bbd27 100644 --- a/agent-node/src/runtime/grok-copresence/profile-process.test.ts +++ b/agent-node/src/runtime/grok-copresence/profile-process.test.ts @@ -68,7 +68,11 @@ describe("Grok co-presence profile is pinned for the whole process", () => { for (const result of [restricted, xSearch, repoRead]) { const denied = result.args.flatMap((value, index) => result.args[index - 1] === "--deny" ? [value] : []); - expect(denied).toContain("run_terminal_command"); + for (const tool of [ + "run_terminal_command", "run_terminal_cmd", "search_replace", "write_file", + "edit_file", "apply_patch", "web_fetch", "http_request", "image_gen", + "generate_image", "video_gen", "generate_video", "browser", "computer", "screenshot", + ]) expect(denied).toContain(tool); expect(denied).toContain("Bash"); expect(denied).toContain("Write"); expect(denied).toContain("WebFetch"); diff --git a/agent-node/src/runtime/grok-copresence/runtime.test.ts b/agent-node/src/runtime/grok-copresence/runtime.test.ts index a114a0319..3e986b3d1 100644 --- a/agent-node/src/runtime/grok-copresence/runtime.test.ts +++ b/agent-node/src/runtime/grok-copresence/runtime.test.ts @@ -235,7 +235,11 @@ describe("Grok copresence launch and injection policy", () => { expect(args).toContain("--always-approve"); expect(args).not.toContain("MCPTool"); const denied = args.flatMap((value, index) => args[index - 1] === "--deny" ? [value] : []); - expect(denied).toContain("run_terminal_command"); + for (const tool of [ + "run_terminal_command", "run_terminal_cmd", "search_replace", "write_file", + "edit_file", "apply_patch", "web_fetch", "http_request", "image_gen", + "generate_image", "video_gen", "generate_video", "browser", "computer", "screenshot", + ]) expect(denied).toContain(tool); expect(denied).toContain("Bash"); expect(denied).toContain("Write"); expect(denied).toContain("WebFetch"); diff --git a/agent-node/src/runtime/grok-copresence/runtime.ts b/agent-node/src/runtime/grok-copresence/runtime.ts index e82c76291..10d438b89 100644 --- a/agent-node/src/runtime/grok-copresence/runtime.ts +++ b/agent-node/src/runtime/grok-copresence/runtime.ts @@ -45,6 +45,7 @@ import { import { assertGrokCopresenceAgentProfile, GROK_COPRESENCE_EFFECTIVE_TOOLS, + GROK_COPRESENCE_VENDOR_DENY_TOOLS, GROK_COPRESENCE_WEB_SEARCH_ENABLED, } from "./policy"; import { @@ -417,14 +418,15 @@ export function buildGrokCopresenceArgs(opts: BuildGrokCopresenceArgsOptions): s // filesystem/process/web-fetch escape routes remain denied. The explicit // x-search process profile may expose general web_search; it is not a // domain-restricted network sandbox. + for (const tool of GROK_COPRESENCE_VENDOR_DENY_TOOLS) { + args.push("--deny", tool); + } args.push( // The shared process must read its owner-only GROK_AUTH_PATH after its // sandbox re-exec. Shell access would bypass path-specific Read/Grep/Edit // rules, so the experimental preview gives up terminal tools entirely. - // Pinned Grok 0.2.93 emits `run_terminal_command` in its permission - // lifecycle. `Bash` is only the cross-runtime policy alias and does not - // deny that vendor-native tool name by itself. - "--deny", "run_terminal_command", + // These are compatibility aliases, not translations for the exact + // vendor-native names appended above. "--deny", "Bash", "--deny", "Write", "--deny", "WebFetch", From 40da92b7c3a850264603b09554e791c94d8493e3 Mon Sep 17 00:00:00 2001 From: vansin Date: Thu, 13 Aug 2026 14:45:25 +0800 Subject: [PATCH 4/7] chore(grok): reset superseded terminal deny report --- .../report-grok-copresence-terminal-deny.txt | 93 ------------------- 1 file changed, 93 deletions(-) delete mode 100644 docs/tests/report-grok-copresence-terminal-deny.txt diff --git a/docs/tests/report-grok-copresence-terminal-deny.txt b/docs/tests/report-grok-copresence-terminal-deny.txt deleted file mode 100644 index 4b35c6faa..000000000 --- a/docs/tests/report-grok-copresence-terminal-deny.txt +++ /dev/null @@ -1,93 +0,0 @@ -# Grok co-presence vendor terminal deny — exact-source evidence - -Date: 2026-08-13 -Base: 114967626f20c7ef036c3d0e0dab295e1f983a89 (#826 report head) -Source commit: f407ed961bc9f0e146657439a9fbfb113604362e -Image: anet-test830:f407ed96 -Image ID: sha256:91b8e8b06603f8b7267e2bb3a507a29bf19adf85a77ad7ada7323807643a54ba - -## Live trigger that motivated the fix - -A prompt-contained PR review explicitly prohibited tools and the Grok TUI rendered -`Run No-op; review is prompt-only`. The pinned Grok 0.2.93 `events.jsonl` then emitted this exact lifecycle: - -```text -permission_requested tool_name=run_terminal_command -permission_resolved tool_name=run_terminal_command decision=allow wait_ms=0 -tool_completed tool_name=run_terminal_command outcome=success -``` - -The pre-fix argv denied only the cross-runtime name `Bash`. The co-presence supervisor subsequently failed -closed with `grok_failure:approval_boundary` and stopped the TUI, but that happened after the vendor had already -reported the terminal tool as completed. Therefore the previous claim that shell was unavailable was false. - -The affected node was restored with its unchanged owner-only config and the pinned Grok binary into a fresh -session. This report does not treat that recovery as proof of the post-fix deny behavior. - -## Source delta - -The source adds the vendor-native permission name to the launch argv while retaining the legacy policy alias: - -```text ---deny run_terminal_command ---deny Bash -``` - -Both the direct launch-policy test and the three-profile process test require the new exact deny entry. Changed -source files and Git-blob-equivalent SHA-256 values: - -```text -a62d5e3bbbc2bdf2524a7b561bca2827e28a3f69386a94a37aa042c8c52ae33c agent-node/src/runtime/grok-copresence/runtime.ts -72b449eaa314f0f9b32eba8b1ca92cf0020ace003613fa90665a9694b5b106c4 agent-node/src/runtime/grok-copresence/runtime.test.ts -48a22832fcfda5576fba852d6d012d6d3200e1ed0998fe134df35865c47e49f0 agent-node/src/runtime/grok-copresence/profile-process.test.ts -``` - -## Docker evidence - -The image was built from source commit `f407ed961bc9f0e146657439a9fbfb113604362e` with -`tests/test725-agent-node-unit-ci/Dockerfile` and pinned Bun 1.3.14. - -Full non-root agent-node unit domain: - -```text -1284 pass -0 fail -4410 expect() calls -Ran 1284 tests across 91 files. [115.95s] -MUTATION_RED readable-attachment-runtime-disconnected rc=1 -RESULT: PASS -``` - -Focused clean run: - -```text -54 pass -0 fail -456 expect() calls -Ran 54 tests across 2 files. [60.59s] -``` - -Witnessed-red removed only the exact production argv entry -`"--deny", "run_terminal_command",` after first requiring one occurrence and a byte-changing mutation. The same -two focused test files then failed in both independent assertions: - -```text -52 pass -2 fail -432 expect() calls -Ran 54 tests across 2 files. [60.70s] -MUTATION_RED vendor-terminal-deny-removed rc=1 -``` - -The mutation harness required the named Bun failure -`Expected to contain: "run_terminal_command"`; an unrelated red did not count. - -## Honest limits - -- NOT COVERED: a released build has not yet been run against the real Grok 0.2.93 vendor after this change. - The decisive post-fix gate is a fresh-session task that previously caused `run_terminal_command`; the event - stream must show no permission request, resolution, or completion for that tool. -- The Docker evidence proves exact argv construction and regression sensitivity. It does not prove that a future - Grok binary preserves the same permission-rule vocabulary; the existing exact version pin remains required. -- This delta addresses the observed terminal tool only. It does not broaden repo-read capability, merge/publish - authority, or make claims about unobserved vendor-native write/media tools. From 8d20ba20f0e731ece3e2925b967cce78e468e84c Mon Sep 17 00:00:00 2001 From: vansin Date: Thu, 13 Aug 2026 14:54:25 +0800 Subject: [PATCH 5/7] docs(test): record exact Grok native deny evidence --- .../report-grok-copresence-terminal-deny.txt | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/tests/report-grok-copresence-terminal-deny.txt diff --git a/docs/tests/report-grok-copresence-terminal-deny.txt b/docs/tests/report-grok-copresence-terminal-deny.txt new file mode 100644 index 000000000..46df297ba --- /dev/null +++ b/docs/tests/report-grok-copresence-terminal-deny.txt @@ -0,0 +1,114 @@ +# Grok co-presence native effectful-tool deny — exact-source evidence + +Date: 2026-08-13 +Base: 114967626f20c7ef036c3d0e0dab295e1f983a89 (#826 report head) +Source commit: 40da92b7c3a850264603b09554e791c94d8493e3 +Image: anet-test830:40da92b7 +Image ID / repo digest: sha256:376dca2042d7b11b44b30dc0880bdcec8603165aeca6347d5b0c332c8c5dedbd + +## Live trigger that motivated the fix + +A prompt-contained PR review explicitly prohibited tools and the pinned Grok 0.2.93 TUI rendered +`Run No-op; review is prompt-only`. Its `events.jsonl` nevertheless emitted this exact lifecycle: + +```text +permission_requested tool_name=run_terminal_command +permission_resolved tool_name=run_terminal_command decision=allow wait_ms=0 +tool_completed tool_name=run_terminal_command outcome=success +``` + +The pre-fix argv denied only the cross-runtime name `Bash`. The co-presence supervisor subsequently failed +closed with `grok_failure:approval_boundary` and stopped the TUI, but that happened after the vendor had already +reported the terminal tool as completed. The previous claim that shell was unavailable was therefore false. + +The affected node is intentionally offline until a reviewed build is published and an authorized single-node +pilot completes. This report does not treat the earlier recovery as proof of post-fix deny behavior. + +## Source delta and denominator + +Pinned Grok 0.2.93 ignores `--tools` in its interactive TUI, so the generated agent profile is not an authority +for the complete native tool inventory. The source now adds an explicit vendor-native deny set at the final argv +boundary while retaining the compatibility aliases `Bash`, `Write`, and `WebFetch`: + +```text +run_terminal_command run_terminal_cmd search_replace write_file edit_file apply_patch +web_fetch http_request image_gen generate_image video_gen generate_video +browser computer screenshot +``` + +The set is derived from the pinned binary's observable strings plus the repository's existing Grok alias map. +Only `run_terminal_command` has been observed in a live permission lifecycle; the other entries are preventive +denies, not claims that every name is currently reachable. Both the direct launch-policy test and the +three-profile process test carry a literal expected set independent of the production constant. + +Changed source files and SHA-256 values: + +```text +c6857a2a738256355b8c36aa8a6019b32efed1cc3b6cce092a51e92c5b78e7ac agent-node/src/runtime/grok-copresence/policy.ts +dc82633d1fd3d728373aeae7f1f8dc85e6ce3ace9795e33b7c62b1a9203b425a agent-node/src/runtime/grok-copresence/runtime.ts +96e88200c7204f868ac523a94c71e11ac4c556e51a4490a816af4bcb5cb4e298 agent-node/src/runtime/grok-copresence/runtime.test.ts +7442180a6853be08bf764a1974f8c9d66ab6259a1bb04f63f2545321271f05d5 agent-node/src/runtime/grok-copresence/profile-process.test.ts +``` + +## Docker evidence + +The image was built from source commit `40da92b7c3a850264603b09554e791c94d8493e3` with +`tests/test725-agent-node-unit-ci/Dockerfile` and pinned Bun 1.3.14. + +Full non-root agent-node unit domain: + +```text +1284 pass +0 fail +4466 expect() calls +Ran 1284 tests across 91 files. [115.54s] +MUTATION_RED readable-attachment-runtime-disconnected rc=1 +RESULT: PASS +``` + +Focused clean run: + +```text +54 pass +0 fail +512 expect() calls +Ran 54 tests across 2 files. [60.71s] +``` + +Four independent product mutations changed exactly one production deny name after a byte-change check. Each +mutation ran the two named policy tests in a fresh container; the zero-mutation control above was green. Each +mutation produced two failures whose text named the missing expected tool: + +```text +MUTATION_RED run_terminal_command rc=1 # terminal +MUTATION_RED search_replace rc=1 # write/edit +MUTATION_RED web_fetch rc=1 # network fetch +MUTATION_RED image_gen rc=1 # media generation +``` + +Representative raw aggregate and named failure: + +```text +Expected to contain: "run_terminal_command" +0 pass +52 filtered out +2 fail +41 expect() calls +Ran 2 tests across 2 files. [275.00ms] +RC=1 +``` + +An unrelated failure or a byte-only no-op did not count as witnessed-red. + +## Honest limits + +- NOT COVERED: a released build has not yet been run against the real pinned Grok 0.2.93 vendor after this + change. The decisive pilot must use a fresh session and attempt prompt-only review plus representative + terminal, write, fetch, and media requests. The event stream must contain no request/resolution/completion for + the denied tools, and no filesystem/network side effect may occur. +- The Docker evidence proves exact argv construction and regression sensitivity. It does not prove that a future + Grok binary preserves the same permission-rule vocabulary; the exact version pin remains required. +- The deny list is an explicit boundary for the pinned build, not a claim that string inspection discovers every + future native tool. A Grok version change requires a fresh inventory and witnessed pilot before rollout. +- This delta does not broaden repo-read capability, grant merge/publish authority, or authorize any production + restart. The communication-dog node remains offline pending review, release approval, and rollback-bound pilot. From 433b4af44bdcc09145c75b697634f74aec42a7df Mon Sep 17 00:00:00 2001 From: vansin Date: Thu, 13 Aug 2026 15:01:58 +0800 Subject: [PATCH 6/7] fix(grok): close pinned native tool inventory --- .../src/runtime/grok-copresence/policy.ts | 24 +++- .../grok-copresence/profile-process.test.ts | 33 ++++- .../runtime/grok-copresence/runtime.test.ts | 11 +- .../src/runtime/grok-copresence/runtime.ts | 5 + .../report-grok-copresence-terminal-deny.txt | 114 ------------------ 5 files changed, 64 insertions(+), 123 deletions(-) delete mode 100644 docs/tests/report-grok-copresence-terminal-deny.txt diff --git a/agent-node/src/runtime/grok-copresence/policy.ts b/agent-node/src/runtime/grok-copresence/policy.ts index 9a8320d55..4468a42c2 100644 --- a/agent-node/src/runtime/grok-copresence/policy.ts +++ b/agent-node/src/runtime/grok-copresence/policy.ts @@ -31,10 +31,13 @@ export const GROK_COPRESENCE_EFFECTIVE_TOOLS = Object.freeze([ ]); /** - * Vendor-native effectful tools that must remain unavailable even when the - * pinned interactive TUI ignores the agent profile's `tools` inventory. - * Keep the Claude-compatible aliases in runtime argv too, but never rely on - * them as translations for Grok's lifecycle names. + * Vendor-native effectful/control tools that must remain unavailable even + * when the pinned interactive TUI ignores the agent profile's `tools` + * inventory. This includes every non-read/non-search name observed in the + * pinned 0.2.93 AvailableCommandsUpdate inventory, plus conservative native + * aliases seen in the binary/repository. Keep the Claude-compatible aliases + * in runtime argv too, but never rely on them as translations for Grok's + * lifecycle names. */ export const GROK_COPRESENCE_VENDOR_DENY_TOOLS = Object.freeze([ "run_terminal_command", @@ -43,9 +46,22 @@ export const GROK_COPRESENCE_VENDOR_DENY_TOOLS = Object.freeze([ "write_file", "edit_file", "apply_patch", + "write", + "kill_command_or_subagent", + "get_command_or_subagent_output", + "wait_commands_or_subagents", + "scheduler_create", + "scheduler_delete", + "scheduler_list", + "monitor", + "update_goal", + "enter_plan_mode", + "exit_plan_mode", + "ask_user_question", "web_fetch", "http_request", "image_gen", + "image_edit", "generate_image", "video_gen", "generate_video", diff --git a/agent-node/src/runtime/grok-copresence/profile-process.test.ts b/agent-node/src/runtime/grok-copresence/profile-process.test.ts index ac57bbd27..178abc19c 100644 --- a/agent-node/src/runtime/grok-copresence/profile-process.test.ts +++ b/agent-node/src/runtime/grok-copresence/profile-process.test.ts @@ -22,6 +22,10 @@ function probe(profile: "commhub-only" | "x-search" | "repo-read"): ProbeResult return JSON.parse(child.stdout); } +function deniedTools(result: ProbeResult): string[] { + return result.args.flatMap((value, index) => result.args[index - 1] === "--deny" ? [value] : []); +} + describe("Grok co-presence profile is pinned for the whole process", () => { test("same input yields three exact, non-overlapping process capabilities", () => { const restricted = probe("commhub-only"); @@ -67,17 +71,40 @@ describe("Grok co-presence profile is pinned for the whole process", () => { ]); for (const result of [restricted, xSearch, repoRead]) { - const denied = result.args.flatMap((value, index) => result.args[index - 1] === "--deny" ? [value] : []); + const denied = deniedTools(result); for (const tool of [ "run_terminal_command", "run_terminal_cmd", "search_replace", "write_file", - "edit_file", "apply_patch", "web_fetch", "http_request", "image_gen", - "generate_image", "video_gen", "generate_video", "browser", "computer", "screenshot", + "edit_file", "apply_patch", "write", "kill_command_or_subagent", + "get_command_or_subagent_output", "wait_commands_or_subagents", "scheduler_create", + "scheduler_delete", "scheduler_list", "monitor", "update_goal", "enter_plan_mode", + "exit_plan_mode", "ask_user_question", "web_fetch", "http_request", "image_gen", + "image_edit", "generate_image", "video_gen", "generate_video", "browser", "computer", + "screenshot", ]) expect(denied).toContain(tool); + for (const tool of [ + "run_terminal_command", "read_file", "search_replace", "list_dir", "grep", + "kill_command_or_subagent", "todo_write", "get_command_or_subagent_output", + "wait_commands_or_subagents", "scheduler_create", "scheduler_delete", "scheduler_list", + "monitor", "search_tool", "use_tool", "update_goal", "enter_plan_mode", "exit_plan_mode", + "ask_user_question", "web_search", "web_fetch", "image_gen", "image_edit", "video_gen", + "write", + ]) { + expect(result.tools.includes(tool) || denied.includes(tool)).toBe(true); + } expect(denied).toContain("Bash"); expect(denied).toContain("Write"); expect(denied).toContain("WebFetch"); expect(denied).toContain("Read(/runtime/private)"); expect(denied).toContain("Grep(/runtime/private/**)"); } + for (const tool of ["read_file", "grep", "list_dir", "web_search"]) { + expect(deniedTools(restricted)).toContain(tool); + } + for (const tool of ["read_file", "grep", "list_dir"]) expect(deniedTools(xSearch)).toContain(tool); + expect(deniedTools(xSearch)).not.toContain("web_search"); + expect(deniedTools(repoRead)).not.toContain("read_file"); + expect(deniedTools(repoRead)).not.toContain("grep"); + expect(deniedTools(repoRead)).not.toContain("list_dir"); + expect(deniedTools(repoRead)).toContain("web_search"); }); }); diff --git a/agent-node/src/runtime/grok-copresence/runtime.test.ts b/agent-node/src/runtime/grok-copresence/runtime.test.ts index 3e986b3d1..7f932253b 100644 --- a/agent-node/src/runtime/grok-copresence/runtime.test.ts +++ b/agent-node/src/runtime/grok-copresence/runtime.test.ts @@ -237,9 +237,16 @@ describe("Grok copresence launch and injection policy", () => { const denied = args.flatMap((value, index) => args[index - 1] === "--deny" ? [value] : []); for (const tool of [ "run_terminal_command", "run_terminal_cmd", "search_replace", "write_file", - "edit_file", "apply_patch", "web_fetch", "http_request", "image_gen", - "generate_image", "video_gen", "generate_video", "browser", "computer", "screenshot", + "edit_file", "apply_patch", "write", "kill_command_or_subagent", + "get_command_or_subagent_output", "wait_commands_or_subagents", "scheduler_create", + "scheduler_delete", "scheduler_list", "monitor", "update_goal", "enter_plan_mode", + "exit_plan_mode", "ask_user_question", "web_fetch", "http_request", "image_gen", + "image_edit", "generate_image", "video_gen", "generate_video", "browser", "computer", + "screenshot", ]) expect(denied).toContain(tool); + for (const tool of ["read_file", "grep", "list_dir", "web_search"]) { + expect(denied).toContain(tool); + } expect(denied).toContain("Bash"); expect(denied).toContain("Write"); expect(denied).toContain("WebFetch"); diff --git a/agent-node/src/runtime/grok-copresence/runtime.ts b/agent-node/src/runtime/grok-copresence/runtime.ts index 10d438b89..8f43d77d6 100644 --- a/agent-node/src/runtime/grok-copresence/runtime.ts +++ b/agent-node/src/runtime/grok-copresence/runtime.ts @@ -45,6 +45,7 @@ import { import { assertGrokCopresenceAgentProfile, GROK_COPRESENCE_EFFECTIVE_TOOLS, + GROK_COPRESENCE_REPO_READ_ENABLED, GROK_COPRESENCE_VENDOR_DENY_TOOLS, GROK_COPRESENCE_WEB_SEARCH_ENABLED, } from "./policy"; @@ -421,6 +422,10 @@ export function buildGrokCopresenceArgs(opts: BuildGrokCopresenceArgsOptions): s for (const tool of GROK_COPRESENCE_VENDOR_DENY_TOOLS) { args.push("--deny", tool); } + if (!GROK_COPRESENCE_REPO_READ_ENABLED) { + for (const tool of ["read_file", "grep", "list_dir"]) args.push("--deny", tool); + } + if (!GROK_COPRESENCE_WEB_SEARCH_ENABLED) args.push("--deny", "web_search"); args.push( // The shared process must read its owner-only GROK_AUTH_PATH after its // sandbox re-exec. Shell access would bypass path-specific Read/Grep/Edit diff --git a/docs/tests/report-grok-copresence-terminal-deny.txt b/docs/tests/report-grok-copresence-terminal-deny.txt deleted file mode 100644 index 46df297ba..000000000 --- a/docs/tests/report-grok-copresence-terminal-deny.txt +++ /dev/null @@ -1,114 +0,0 @@ -# Grok co-presence native effectful-tool deny — exact-source evidence - -Date: 2026-08-13 -Base: 114967626f20c7ef036c3d0e0dab295e1f983a89 (#826 report head) -Source commit: 40da92b7c3a850264603b09554e791c94d8493e3 -Image: anet-test830:40da92b7 -Image ID / repo digest: sha256:376dca2042d7b11b44b30dc0880bdcec8603165aeca6347d5b0c332c8c5dedbd - -## Live trigger that motivated the fix - -A prompt-contained PR review explicitly prohibited tools and the pinned Grok 0.2.93 TUI rendered -`Run No-op; review is prompt-only`. Its `events.jsonl` nevertheless emitted this exact lifecycle: - -```text -permission_requested tool_name=run_terminal_command -permission_resolved tool_name=run_terminal_command decision=allow wait_ms=0 -tool_completed tool_name=run_terminal_command outcome=success -``` - -The pre-fix argv denied only the cross-runtime name `Bash`. The co-presence supervisor subsequently failed -closed with `grok_failure:approval_boundary` and stopped the TUI, but that happened after the vendor had already -reported the terminal tool as completed. The previous claim that shell was unavailable was therefore false. - -The affected node is intentionally offline until a reviewed build is published and an authorized single-node -pilot completes. This report does not treat the earlier recovery as proof of post-fix deny behavior. - -## Source delta and denominator - -Pinned Grok 0.2.93 ignores `--tools` in its interactive TUI, so the generated agent profile is not an authority -for the complete native tool inventory. The source now adds an explicit vendor-native deny set at the final argv -boundary while retaining the compatibility aliases `Bash`, `Write`, and `WebFetch`: - -```text -run_terminal_command run_terminal_cmd search_replace write_file edit_file apply_patch -web_fetch http_request image_gen generate_image video_gen generate_video -browser computer screenshot -``` - -The set is derived from the pinned binary's observable strings plus the repository's existing Grok alias map. -Only `run_terminal_command` has been observed in a live permission lifecycle; the other entries are preventive -denies, not claims that every name is currently reachable. Both the direct launch-policy test and the -three-profile process test carry a literal expected set independent of the production constant. - -Changed source files and SHA-256 values: - -```text -c6857a2a738256355b8c36aa8a6019b32efed1cc3b6cce092a51e92c5b78e7ac agent-node/src/runtime/grok-copresence/policy.ts -dc82633d1fd3d728373aeae7f1f8dc85e6ce3ace9795e33b7c62b1a9203b425a agent-node/src/runtime/grok-copresence/runtime.ts -96e88200c7204f868ac523a94c71e11ac4c556e51a4490a816af4bcb5cb4e298 agent-node/src/runtime/grok-copresence/runtime.test.ts -7442180a6853be08bf764a1974f8c9d66ab6259a1bb04f63f2545321271f05d5 agent-node/src/runtime/grok-copresence/profile-process.test.ts -``` - -## Docker evidence - -The image was built from source commit `40da92b7c3a850264603b09554e791c94d8493e3` with -`tests/test725-agent-node-unit-ci/Dockerfile` and pinned Bun 1.3.14. - -Full non-root agent-node unit domain: - -```text -1284 pass -0 fail -4466 expect() calls -Ran 1284 tests across 91 files. [115.54s] -MUTATION_RED readable-attachment-runtime-disconnected rc=1 -RESULT: PASS -``` - -Focused clean run: - -```text -54 pass -0 fail -512 expect() calls -Ran 54 tests across 2 files. [60.71s] -``` - -Four independent product mutations changed exactly one production deny name after a byte-change check. Each -mutation ran the two named policy tests in a fresh container; the zero-mutation control above was green. Each -mutation produced two failures whose text named the missing expected tool: - -```text -MUTATION_RED run_terminal_command rc=1 # terminal -MUTATION_RED search_replace rc=1 # write/edit -MUTATION_RED web_fetch rc=1 # network fetch -MUTATION_RED image_gen rc=1 # media generation -``` - -Representative raw aggregate and named failure: - -```text -Expected to contain: "run_terminal_command" -0 pass -52 filtered out -2 fail -41 expect() calls -Ran 2 tests across 2 files. [275.00ms] -RC=1 -``` - -An unrelated failure or a byte-only no-op did not count as witnessed-red. - -## Honest limits - -- NOT COVERED: a released build has not yet been run against the real pinned Grok 0.2.93 vendor after this - change. The decisive pilot must use a fresh session and attempt prompt-only review plus representative - terminal, write, fetch, and media requests. The event stream must contain no request/resolution/completion for - the denied tools, and no filesystem/network side effect may occur. -- The Docker evidence proves exact argv construction and regression sensitivity. It does not prove that a future - Grok binary preserves the same permission-rule vocabulary; the exact version pin remains required. -- The deny list is an explicit boundary for the pinned build, not a claim that string inspection discovers every - future native tool. A Grok version change requires a fresh inventory and witnessed pilot before rollout. -- This delta does not broaden repo-read capability, grant merge/publish authority, or authorize any production - restart. The communication-dog node remains offline pending review, release approval, and rollback-bound pilot. From d51a4473f6aea75547437150dba729524506062b Mon Sep 17 00:00:00 2001 From: vansin Date: Thu, 13 Aug 2026 15:08:42 +0800 Subject: [PATCH 7/7] docs(test): record pinned Grok tool boundary --- .../report-grok-copresence-terminal-deny.txt | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 docs/tests/report-grok-copresence-terminal-deny.txt diff --git a/docs/tests/report-grok-copresence-terminal-deny.txt b/docs/tests/report-grok-copresence-terminal-deny.txt new file mode 100644 index 000000000..ba290f6cc --- /dev/null +++ b/docs/tests/report-grok-copresence-terminal-deny.txt @@ -0,0 +1,126 @@ +# Grok co-presence pinned native-tool boundary — exact-source evidence + +Date: 2026-08-13 +Base: 114967626f20c7ef036c3d0e0dab295e1f983a89 (#826 report head) +Source commit: 433b4af44bdcc09145c75b697634f74aec42a7df +Image: anet-test830:433b4af4 +Image ID / repo digest: sha256:df3b83526409d21ece60f0a5a12589e0fc0933616af617ea83abd6e1416deb1c + +## Live trigger + +A prompt-contained review explicitly prohibited tools and pinned Grok 0.2.93 rendered +`Run No-op; review is prompt-only`. Its `events.jsonl` nevertheless emitted: + +```text +permission_requested tool_name=run_terminal_command +permission_resolved tool_name=run_terminal_command decision=allow wait_ms=0 +tool_completed tool_name=run_terminal_command outcome=success +``` + +The old argv denied only the cross-runtime name `Bash`. The supervisor later failed closed with +`grok_failure:approval_boundary`, but only after the vendor reported successful terminal execution. The affected +communication-dog node is intentionally offline until a reviewed build and authorized pilot exist. + +## Pinned denominator and policy + +The generated Grok agent profile is not the complete native permission boundary: pinned 0.2.93 ignored its +interactive `tools` inventory. The captured `AvailableCommandsUpdate._meta.tools` denominator is exactly: + +```text +run_terminal_command read_file search_replace list_dir grep kill_command_or_subagent +todo_write get_command_or_subagent_output wait_commands_or_subagents +scheduler_create scheduler_delete scheduler_list monitor search_tool use_tool +update_goal enter_plan_mode exit_plan_mode ask_user_question web_search web_fetch +image_gen image_edit video_gen write +``` + +Every captured name is now either explicitly allowed by the selected process profile or explicitly denied at the +final argv boundary. The three profiles differ intentionally: + +```text +commhub-only: allow todo_write/search_tool/use_tool; deny read_file/grep/list_dir/web_search +x-search: additionally allow web_search; deny read_file/grep/list_dir +repo-read: additionally allow read_file/grep/list_dir; deny web_search +``` + +All 18 captured effectful/control names outside the union of those allowed sets are common denies. Ten additional +native aliases observed in the pinned binary/repository are preventive denies. `Bash`, `Write`, and `WebFetch` +remain compatibility aliases and are not treated as translations for vendor lifecycle names. + +Only `run_terminal_command` has been observed in a live permission lifecycle. The other entries are preventive +boundary rules, not claims that every name is reachable. + +Changed source files and SHA-256 values: + +```text +e0d51898ffaabbb8bf3b5275a55f97e6a86df3008d36491f9a446966f383ae60 agent-node/src/runtime/grok-copresence/policy.ts +699371996090a058d9a01c87581dc7dc4dd7bd03072cebb85bee936912028011 agent-node/src/runtime/grok-copresence/runtime.ts +56a308e12d94bebdf27c19e1eccc08e55096f6e769df35610fd916bb4a7a0704 agent-node/src/runtime/grok-copresence/runtime.test.ts +ff60c2cdd68cce0e09592dec0594f310200405f7f9c63d58787c10dd52e2483a agent-node/src/runtime/grok-copresence/profile-process.test.ts +``` + +## Docker evidence + +The image was built from source commit `433b4af44bdcc09145c75b697634f74aec42a7df` with +`tests/test725-agent-node-unit-ci/Dockerfile` and pinned Bun 1.3.14. + +Full non-root agent-node unit domain: + +```text +1284 pass +0 fail +4609 expect() calls +Ran 1284 tests across 91 files. [115.10s] +MUTATION_RED readable-attachment-runtime-disconnected rc=1 +RESULT: PASS +``` + +Focused zero-mutation control: + +```text +54 pass +0 fail +655 expect() calls +Ran 54 tests across 2 files. [61.63s] +``` + +Seven independent mutations ran in fresh containers. Each required a unique production target, a changed file +hash, a non-zero focused test result, and a failure naming the missing tool: + +```text +MUTATION_RED common:run_terminal_command rc=1 +MUTATION_RED common:write rc=1 +MUTATION_RED common:scheduler_create rc=1 +MUTATION_RED common:web_fetch rc=1 +MUTATION_RED common:image_edit rc=1 +MUTATION_RED profile:read_file rc=1 +MUTATION_RED profile:web_search rc=1 +``` + +The first five witness terminal, write, scheduler/control, fetch, and media classes in the common deny set. The +last two witness the profile-specific read/search boundary. A representative profile mutation produced: + +```text +Expected to contain: "read_file" +0 pass +52 filtered out +2 fail +98 expect() calls +Ran 2 tests across 2 files. [264.00ms] +RC=1 +``` + +An unrelated red, a self-red baseline, or a byte-only no-op did not count. + +## Honest limits + +- NOT COVERED: a released build has not yet been exercised against the real pinned Grok 0.2.93 vendor after + this change. A fresh-session pilot must probe prompt-only review plus terminal, write, scheduler/control, + fetch, media, profile-specific read, and profile-specific web-search cases. Denied cases must emit no + request/resolution/completion and produce no filesystem/network/scheduler side effect. +- The Docker evidence proves exact argv construction, captured-inventory coverage, profile separation, and + regression sensitivity. It does not prove the vendor honors every deny rule. +- A future Grok version requires a new exact inventory and witnessed pilot. This list is not claimed complete for + any other binary. +- This delta does not grant repo-read, merge, publish, deploy, DB, secret, or cloud authority. It does not change + any production package or node configuration.