Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions agent-node/src/runtime/grok-copresence/policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,46 @@ export const GROK_COPRESENCE_EFFECTIVE_TOOLS = Object.freeze([
...(GROK_COPRESENCE_REPO_READ_ENABLED ? ["read_file", "grep", "list_dir"] : []),
]);

/**
* 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",
"run_terminal_cmd",
"search_replace",
"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",
"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";
Expand Down
34 changes: 33 additions & 1 deletion agent-node/src/runtime/grok-copresence/profile-process.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -67,12 +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", "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");
});
});
12 changes: 12 additions & 0 deletions agent-node/src/runtime/grok-copresence/runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,18 @@ 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] : []);
for (const tool of [
"run_terminal_command", "run_terminal_cmd", "search_replace", "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", "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");
Expand Down
11 changes: 11 additions & 0 deletions agent-node/src/runtime/grok-copresence/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import {
import {
assertGrokCopresenceAgentProfile,
GROK_COPRESENCE_EFFECTIVE_TOOLS,
GROK_COPRESENCE_REPO_READ_ENABLED,
GROK_COPRESENCE_VENDOR_DENY_TOOLS,
GROK_COPRESENCE_WEB_SEARCH_ENABLED,
} from "./policy";
import {
Expand Down Expand Up @@ -417,10 +419,19 @@ 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);
}
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
// rules, so the experimental preview gives up terminal tools entirely.
// These are compatibility aliases, not translations for the exact
// vendor-native names appended above.
"--deny", "Bash",
"--deny", "Write",
"--deny", "WebFetch",
Expand Down
126 changes: 126 additions & 0 deletions docs/tests/report-grok-copresence-terminal-deny.txt
Original file line number Diff line number Diff line change
@@ -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.
Loading