diff --git a/internal-skills/cli-runtime/runtime.md b/internal-skills/cli-runtime/runtime.md index ccd205a..d797090 100644 --- a/internal-skills/cli-runtime/runtime.md +++ b/internal-skills/cli-runtime/runtime.md @@ -49,6 +49,7 @@ Task defaults: - Run the companion command as one blocking foreground shell-tool call, not as a background terminal/session. - Do not request a shell session id, poll a shell session later, or return before the companion command exits. - If the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call. +- When using `exec_command`, pass `sandbox_permissions: "require_escalated"` and the justification `Allow the Claude Code companion to contact the Claude API for this requested task.` on that one call. Do not first try the companion command in the default network-disabled sandbox. - If the parent supplied a non-empty parent thread id for background completion, allow at most one success-only `send_input` notification before finishing. - Mention the tool name `send_input` literally when describing that notification path. - Use the exact tool shape `send_input({ target: , message: })`. diff --git a/internal-skills/review-runtime/runtime.md b/internal-skills/review-runtime/runtime.md index 76a4c00..83f40e1 100644 --- a/internal-skills/review-runtime/runtime.md +++ b/internal-skills/review-runtime/runtime.md @@ -19,6 +19,7 @@ Foreground contract: - Foreground command: - `review --view-state on-success ...` - `adversarial-review --view-state on-success ...` +- Run the companion command with `sandbox_permissions: "require_escalated"` and the justification `Allow the Claude Code companion to contact the Claude API for this requested review.` Do not first try the companion command in the default network-disabled sandbox. - Return companion stdout faithfully and do not add review execution commentary around it. Background contract: @@ -38,6 +39,7 @@ Background contract: - run the companion command as one blocking foreground shell-tool call, not as a background terminal/session - do not request a shell session id, poll a shell session later, or return before the companion command exits - if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call + - when using `exec_command`, pass `sandbox_permissions: "require_escalated"` and the justification `Allow the Claude Code companion to contact the Claude API for this requested review.` on that one call; do not first try the companion command in the default network-disabled sandbox - use at most one `send_input` completion notification on success - mention the tool name `send_input` literally in the child instructions - use the exact tool shape `send_input({ target: , message: })` diff --git a/skills/adversarial-review/SKILL.md b/skills/adversarial-review/SKILL.md index 4a80d29..ab48bb3 100644 --- a/skills/adversarial-review/SKILL.md +++ b/skills/adversarial-review/SKILL.md @@ -57,6 +57,7 @@ Argument handling: Foreground flow: - Run: `node "/scripts/claude-companion.mjs" adversarial-review --view-state on-success ` +- Run that companion command with `sandbox_permissions: "require_escalated"` and the justification `Allow the Claude Code companion to contact the Claude API for this requested review.` Do not first try the companion command in the default network-disabled sandbox. - Foreground adversarial review belongs to the main Codex thread. Do not spawn a review subagent, do not invoke a generic review-runner role, and do not proxy this foreground path through any background worker abstraction. - Do not fall back to raw `claude`, `claude-code`, `claude review`, `bash -lc ...claude...`, or any other direct Claude CLI syntax when the companion path is available. The foreground syntax contract here is the resolved companion command above, not a hand-rolled Claude invocation. - If the resolved companion command fails, surface that failure. Do not silently retry foreground adversarial review through a different CLI shape, a generic review runner, or a custom shell wrapper. @@ -90,6 +91,7 @@ Background flow: - run that command as one blocking foreground shell-tool call, not as a background terminal/session - do not request a shell session id, poll a shell session later, or return before the companion command exits - if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call + - when using `exec_command`, pass `sandbox_permissions: "require_escalated"` and the justification `Allow the Claude Code companion to contact the Claude API for this requested review.` on that one call; do not first try the companion command in the default network-disabled sandbox - include `--owner-session-id ` only when the parent resolved a non-empty owner session id - include `--job-id ` when the parent reserved one - include the matching `--cwd ` whenever the command includes that reserved `--job-id` diff --git a/skills/rescue/SKILL.md b/skills/rescue/SKILL.md index 60a6f7b..b3cfacd 100644 --- a/skills/rescue/SKILL.md +++ b/skills/rescue/SKILL.md @@ -121,6 +121,7 @@ Subagent launch: - run that command as one blocking foreground shell-tool call, not as a background terminal/session - do not request a shell session id, poll a shell session later, or return before the companion command exits - if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call + - when using `exec_command`, pass `sandbox_permissions: "require_escalated"` and the justification `Allow the Claude Code companion to contact the Claude API for this requested task.` on that one call; do not first try the companion command in the default network-disabled sandbox - for foreground rescue only, tell the child to return that command's stdout text exactly, with no preamble, summary, code fence, trimming, normalization, or punctuation changes - tell the child to ignore stderr progress chatter such as `[cc] ...` lines and preserve only the stdout-equivalent final result text - if a parent thread id is provided for experimental background notification, allow one extra `send_input` call after a successful shell result and before finishing diff --git a/skills/review/SKILL.md b/skills/review/SKILL.md index 5bf4e2f..6d2c409 100644 --- a/skills/review/SKILL.md +++ b/skills/review/SKILL.md @@ -55,6 +55,7 @@ Argument handling: Foreground flow: - Run: `node "/scripts/claude-companion.mjs" review --view-state on-success ` +- Run that companion command with `sandbox_permissions: "require_escalated"` and the justification `Allow the Claude Code companion to contact the Claude API for this requested review.` Do not first try the companion command in the default network-disabled sandbox. - Foreground review belongs to the main Codex thread. Do not spawn a review subagent, do not invoke a generic review-runner role, and do not proxy this foreground path through any background worker abstraction. - Do not fall back to raw `claude`, `claude-code`, `claude review`, `bash -lc ...claude...`, or any other direct Claude CLI syntax when the companion path is available. The foreground syntax contract here is the resolved companion command above, not a hand-rolled Claude invocation. - If the resolved companion command fails, surface that failure. Do not silently retry foreground review through a different CLI shape, a generic review runner, or a custom shell wrapper. @@ -88,6 +89,7 @@ Background flow: - run that command as one blocking foreground shell-tool call, not as a background terminal/session - do not request a shell session id, poll a shell session later, or return before the companion command exits - if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call + - when using `exec_command`, pass `sandbox_permissions: "require_escalated"` and the justification `Allow the Claude Code companion to contact the Claude API for this requested review.` on that one call; do not first try the companion command in the default network-disabled sandbox - include `--owner-session-id ` only when the parent resolved a non-empty owner session id - include `--job-id ` when the parent reserved one - include the matching `--cwd ` whenever the command includes that reserved `--job-id` diff --git a/tests/e2e/codex-skills-e2e.test.mjs b/tests/e2e/codex-skills-e2e.test.mjs index 2558050..f28db67 100644 --- a/tests/e2e/codex-skills-e2e.test.mjs +++ b/tests/e2e/codex-skills-e2e.test.mjs @@ -888,6 +888,7 @@ function startMockProvider({ "Run that command as one blocking foreground shell-tool call, not as a background terminal or session.\n" + "Do not request a shell session id, poll a shell session later, or return before the command exits.\n" + "If the shell tool is exec_command, call it once in non-interactive mode and wait for exit in that same call.\n" + + "Use sandbox_permissions: \"require_escalated\" with a justification that allows the Claude Code companion to contact the Claude API. Do not first try the companion command in the default network-disabled sandbox.\n" + "Return only that command's stdout text exactly.\n" + "Ignore stderr progress chatter such as [cc] lines.\n" + "If the tool output includes both stderr progress and a final stdout-style result, preserve only the final stdout-equivalent result text.\n" + @@ -963,6 +964,18 @@ function startMockProvider({ "built-in child should be told to preserve the exact task text in the command" ); } + assert.ok( + bodyText.includes("sandbox_permissions") && bodyText.includes("require_escalated"), + "built-in child should request targeted escalation for Claude API access" + ); + assert.ok( + bodyText.includes("contact the Claude API"), + "built-in child should receive a scoped network justification" + ); + assert.ok( + bodyText.includes("Do not first try the companion command in the default network-disabled sandbox."), + "built-in child should avoid the known ENOTFOUND first attempt" + ); assert.doesNotMatch( bodyText, /claude-companion\.mjs"\s+task\s+--background|claude-companion\.mjs"\s+task\s+--wait|claude-companion\.mjs\s+task\s+--background|claude-companion\.mjs\s+task\s+--wait/, @@ -1812,6 +1825,7 @@ describe("Codex direct-skill E2E", () => { "Run that command as one blocking foreground shell-tool call, not as a background terminal or session.\n" + "Do not request a shell session id, poll a shell session later, or return before the command exits.\n" + "If the shell tool is exec_command, call it once in non-interactive mode and wait for exit in that same call.\n" + + "Use sandbox_permissions: \"require_escalated\" with the justification Allow the Claude Code companion to contact the Claude API for this requested review. Do not first try the companion command in the default network-disabled sandbox.\n" + "If the command succeeds and a parent thread id is available, send exactly this notification to the parent thread before finishing: " + JSON.stringify(notificationMessage) + "\n" + "Use that same sentence as your own final assistant message.\n" + @@ -2025,6 +2039,7 @@ describe("Codex direct-skill E2E", () => { "Run that command as one blocking foreground shell-tool call, not as a background terminal or session.\n" + "Do not request a shell session id, poll a shell session later, or return before the command exits.\n" + "If the shell tool is exec_command, call it once in non-interactive mode and wait for exit in that same call.\n" + + "Use sandbox_permissions: \"require_escalated\" with the justification Allow the Claude Code companion to contact the Claude API for this requested review. Do not first try the companion command in the default network-disabled sandbox.\n" + "If the command succeeds and a parent thread id is available, send exactly this notification to the parent thread before finishing: " + JSON.stringify(notificationMessage) + "\n" + "Use that same sentence as your own final assistant message.\n" + diff --git a/tests/skills-contracts.test.mjs b/tests/skills-contracts.test.mjs index cd0d672..b34d2f9 100644 --- a/tests/skills-contracts.test.mjs +++ b/tests/skills-contracts.test.mjs @@ -50,6 +50,9 @@ test("internal runtime references keep the active-root and notification invarian assert.match(reviewRuntime, /blocking foreground shell-tool call, not as a background terminal\/session/i); assert.match(reviewRuntime, /Do not request a shell session id, poll a shell session later, or return before the companion command exits/i); assert.match(reviewRuntime, /if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call/i); + assert.match(reviewRuntime, /`sandbox_permissions: "require_escalated"`/i); + assert.match(reviewRuntime, /contact the Claude API for this requested review/i); + assert.match(reviewRuntime, /Do not first try the companion command in the default network-disabled sandbox/i); assert.match(reviewRuntime, /mention the tool name `send_input` literally/i); assert.match(reviewRuntime, /exact tool shape `send_input\(\{ target: , message: \}\)`/i); assert.match(reviewRuntime, /do not silently drop the completion notification path when the parent provided a non-empty parent thread id/i); @@ -64,6 +67,9 @@ test("internal runtime references keep the active-root and notification invarian assert.match(rescueRuntime, /blocking foreground shell-tool call, not as a background terminal\/session/i); assert.match(rescueRuntime, /Do not request a shell session id, poll a shell session later, or return before the companion command exits/i); assert.match(rescueRuntime, /if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call/i); + assert.match(rescueRuntime, /`sandbox_permissions: "require_escalated"`/i); + assert.match(rescueRuntime, /contact the Claude API for this requested task/i); + assert.match(rescueRuntime, /Do not first try the companion command in the default network-disabled sandbox/i); assert.match(rescueRuntime, /allow at most one success-only `send_input` notification before finishing/i); assert.match(rescueRuntime, /Mention the tool name `send_input` literally/i); assert.match(rescueRuntime, /exact tool shape `send_input\(\{ target: , message: \}\)`/i); @@ -115,6 +121,9 @@ test("review skills keep background execution outside the companion command", () assert.match(review, /blocking foreground shell-tool call, not as a background terminal\/session/i); assert.match(review, /Do not request a shell session id, poll a shell session later, or return before the companion command exits/i); assert.match(review, /if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call/i); + assert.match(review, /`sandbox_permissions: "require_escalated"`/i); + assert.match(review, /contact the Claude API for this requested review/i); + assert.match(review, /do not first try the companion command in the default network-disabled sandbox/i); assert.match(review, /allow one extra `send_input` call after a successful shell result/i); assert.match(review, /must mention the tool name `send_input` literally/i); assert.match(review, /must target the provided parent thread id/i); @@ -169,6 +178,9 @@ test("review skills keep background execution outside the companion command", () assert.match(adversarial, /blocking foreground shell-tool call, not as a background terminal\/session/i); assert.match(adversarial, /Do not request a shell session id, poll a shell session later, or return before the companion command exits/i); assert.match(adversarial, /if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call/i); + assert.match(adversarial, /`sandbox_permissions: "require_escalated"`/i); + assert.match(adversarial, /contact the Claude API for this requested review/i); + assert.match(adversarial, /do not first try the companion command in the default network-disabled sandbox/i); assert.match(adversarial, /allow one extra `send_input` call after a successful shell result/i); assert.match(adversarial, /must mention the tool name `send_input` literally/i); assert.match(adversarial, /must target the provided parent thread id/i); @@ -255,6 +267,9 @@ test("rescue skill documents the experimental built-in-agent forwarding path", ( assert.match(rescue, /blocking foreground shell-tool call, not as a background terminal\/session/i); assert.match(rescue, /Do not request a shell session id, poll a shell session later, or return before the companion command exits/i); assert.match(rescue, /if the available shell tool is `exec_command`, call it once in non-interactive mode and wait for command exit in that same call/i); + assert.match(rescue, /`sandbox_permissions: "require_escalated"`/i); + assert.match(rescue, /contact the Claude API for this requested task/i); + assert.match(rescue, /do not first try the companion command in the default network-disabled sandbox/i); assert.match(rescue, /prefer these steering messages over embedding the raw result text/i); assert.match(rescue, /do not embed the raw Claude result inside the notification message/i); assert.match(rescue, /do not include any other prose in that notification message/i);