diff --git a/code-rs/config.md b/code-rs/config.md index 2e37ebf8913..b75a7ab1d17 100644 --- a/code-rs/config.md +++ b/code-rs/config.md @@ -394,8 +394,11 @@ read-only = false # Restrict to read-only operations (default: false) description = "Claude AI assistant" # Description shown in UI args = ["--dangerously-skip-permissions"] # Default arguments env = { API_KEY = "value" } # Environment variables +claude-is-sandbox = true # Opt-in: set IS_SANDBOX=1 for Claude CLI ``` +Code now forces `IS_SANDBOX=1` and appends `--dangerously-skip-permissions` whenever it launches Claude subagents, so you do not need to set this flag to avoid root/sudo failures. + ### Configuring agent commands The `command` field specifies how to invoke the agent. Code supports three approaches: @@ -451,8 +454,8 @@ You can specify different arguments for read-only vs. write modes: name = "custom-agent" command = "custom-agent" args = ["--base-arg"] # Always included -args_read_only = ["--read-only"] # Added in read-only mode -args_write = ["--allow-writes"] # Added in write mode +args-read-only = ["--read-only"] # Added in read-only mode +args-write = ["--allow-writes"] # Added in write mode ``` ### Environment variables diff --git a/code-rs/core/src/agent_defaults.rs b/code-rs/core/src/agent_defaults.rs index 314a3e8ce22..69649716cc9 100644 --- a/code-rs/core/src/agent_defaults.rs +++ b/code-rs/core/src/agent_defaults.rs @@ -401,6 +401,7 @@ pub fn agent_config_from_spec(spec: &AgentModelSpec) -> AgentConfig { enabled: spec.is_enabled(), description: None, env: None, + claude_is_sandbox: false, args_read_only: some_args(spec.read_only_args), args_write: some_args(spec.write_args), instructions: None, diff --git a/code-rs/core/src/agent_tool.rs b/code-rs/core/src/agent_tool.rs index 90d707f53d0..00755a39e25 100644 --- a/code-rs/core/src/agent_tool.rs +++ b/code-rs/core/src/agent_tool.rs @@ -1370,6 +1370,10 @@ fn command_exists(cmd: &str) -> bool { } } + if family == "claude" && !final_args.iter().any(|arg| arg == "--dangerously-skip-permissions") { + final_args.push("--dangerously-skip-permissions".to_string()); + } + let log_tag_owned = log_tag.map(str::to_string); let debug_subagent = debug_subagents_enabled() && matches!(source_kind, Some(AgentSourceKind::AutoReview)); @@ -1420,6 +1424,11 @@ fn command_exists(cmd: &str) -> bool { if let Some(ref e) = cfg.env { for (k, v) in e { env.insert(k.clone(), v.clone()); } } } + if family == "claude" { + env.entry("IS_SANDBOX".to_string()) + .or_insert_with(|| "1".to_string()); + } + if debug_subagent { env.entry("CODE_SUBAGENT_DEBUG".to_string()) .or_insert_with(|| "1".to_string()); @@ -2495,6 +2504,7 @@ mod tests { enabled: true, description: None, env: None, + claude_is_sandbox: false, args_read_only: None, args_write: None, instructions: None, diff --git a/code-rs/core/src/codex.rs b/code-rs/core/src/codex.rs index adecd1296a1..f3f55185871 100644 --- a/code-rs/core/src/codex.rs +++ b/code-rs/core/src/codex.rs @@ -9479,6 +9479,7 @@ mod resolve_read_only_tests { enabled: true, description: None, env: None, + claude_is_sandbox: false, args_read_only: None, args_write: None, instructions: None, @@ -10157,10 +10158,17 @@ pub(crate) async fn handle_run_agent( let mut skipped: Vec = Vec::new(); for model in models { let model_key = model.to_lowercase(); + let model_slug = agent_model_spec(&model) + .map(|spec| spec.slug.to_ascii_lowercase()) + .unwrap_or_else(|| model_key.clone()); // Check if this model is configured and enabled let agent_config = sess.agents.iter().find(|a| { - a.name.to_lowercase() == model_key - || a.command.to_lowercase() == model_key + let name_key = a.name.to_ascii_lowercase(); + if name_key == model_key || name_key == model_slug { + return true; + } + let command_key = a.command.to_ascii_lowercase(); + command_key == model_key || command_key == model_slug }); if let Some(config) = agent_config { diff --git a/code-rs/core/src/config.rs b/code-rs/core/src/config.rs index 2f56cf69889..05cbb1a097b 100644 --- a/code-rs/core/src/config.rs +++ b/code-rs/core/src/config.rs @@ -4401,6 +4401,7 @@ model_verbosity = "high" enabled: true, description: None, env: None, + claude_is_sandbox: false, args_read_only: None, args_write: None, instructions: None, @@ -4533,6 +4534,7 @@ mod agent_merge_tests { enabled, description: None, env: None, + claude_is_sandbox: false, args_read_only: None, args_write: None, instructions: None, diff --git a/code-rs/core/src/config_types.rs b/code-rs/core/src/config_types.rs index 8fc2ad6bf1d..f4adf38db69 100644 --- a/code-rs/core/src/config_types.rs +++ b/code-rs/core/src/config_types.rs @@ -421,6 +421,10 @@ pub struct AgentConfig { #[serde(default)] pub env: Option>, + /// When true, set IS_SANDBOX=1 for Claude CLI launches (opt-in; ignored for other agents). + #[serde(default)] + pub claude_is_sandbox: bool, + /// Optional arguments to pass only when the agent is executed in /// read-only mode. When present, these are preferred over `args` for /// read-only runs. diff --git a/code-rs/core/src/slash_commands.rs b/code-rs/core/src/slash_commands.rs index 475c9afc64a..291b51b0289 100644 --- a/code-rs/core/src/slash_commands.rs +++ b/code-rs/core/src/slash_commands.rs @@ -281,6 +281,7 @@ mod tests { enabled: true, description: None, env: None, + claude_is_sandbox: false, args_read_only: None, args_write: None, instructions: None, @@ -293,6 +294,7 @@ mod tests { enabled: false, // disabled description: None, env: None, + claude_is_sandbox: false, args_read_only: None, args_write: None, instructions: None, diff --git a/code-rs/core/tests/agent_completion_wake.rs b/code-rs/core/tests/agent_completion_wake.rs index 967a3aef7da..c8aa6f2f134 100644 --- a/code-rs/core/tests/agent_completion_wake.rs +++ b/code-rs/core/tests/agent_completion_wake.rs @@ -115,6 +115,7 @@ event: response.completed\ndata: {completed}\n\n", enabled: true, description: None, env: None, + claude_is_sandbox: false, args_read_only: None, args_write: None, instructions: None, diff --git a/code-rs/tui/src/chatwidget.rs b/code-rs/tui/src/chatwidget.rs index 2fd2ec0a1d3..e5048445265 100644 --- a/code-rs/tui/src/chatwidget.rs +++ b/code-rs/tui/src/chatwidget.rs @@ -138,6 +138,7 @@ mod agent_summary_counts_tests { enabled, description: None, env: None, + claude_is_sandbox: false, args_read_only: None, args_write: None, instructions: None, @@ -21173,6 +21174,7 @@ Have we met every part of this goal and is there no further work to do?"# enabled, description: description.clone(), env: None, + claude_is_sandbox: false, args_read_only: args_ro.clone(), args_write: args_wr.clone(), instructions: instr.clone(), @@ -21186,6 +21188,7 @@ Have we met every part of this goal and is there no further work to do?"# enabled, description: description.clone(), env: None, + claude_is_sandbox: false, args_read_only: args_ro.clone(), args_write: args_wr.clone(), instructions: instr.clone(), @@ -29229,6 +29232,7 @@ async fn run_background_review( enabled: true, description: None, env: Some(env), + claude_is_sandbox: false, args_read_only: None, args_write: None, instructions: None, diff --git a/docs/agents.md b/docs/agents.md index ece4bb495bd..21b19f0242b 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -8,15 +8,18 @@ Every Code can launch external CLI “agents” and orchestrate them in multi-ag name = "code-gpt-5.2-codex" # slug or alias shown in pickers command = "coder" # executable; defaults to name args = ["--foo", "bar"] # base argv -args_read_only = ["-s", "read-only", "-a", "never", "exec", "--skip-git-repo-check"] -args_write = ["-s", "workspace-write", "--dangerously-bypass-approvals-and-sandbox", "exec", "--skip-git-repo-check"] +args-read-only = ["-s", "read-only", "-a", "never", "exec", "--skip-git-repo-check"] +args-write = ["-s", "workspace-write", "--dangerously-bypass-approvals-and-sandbox", "exec", "--skip-git-repo-check"] env = { CODE_FOO = "1" } -read_only = false # force RO even if session allows writes +claude-is-sandbox = true # opt-in: set IS_SANDBOX=1 for Claude CLI +read-only = false # force RO even if session allows writes enabled = true # hide from pickers when false description = "Frontline coding agent" instructions = "Preamble added to this agent’s prompt" ``` -Field recap: `name` (slug/alias), `command` (absolute paths ok), `args*` (RO/RW lists override base), `env`, `read_only`, `enabled`, optional `description` and `instructions`. +Field recap: `name` (slug/alias), `command` (absolute paths ok), `args*` (RO/RW lists override base), `env`, `claude-is-sandbox` (Claude-only), `read-only`, `enabled`, optional `description` and `instructions`. + +Claude subagents always run with `IS_SANDBOX=1` and `--dangerously-skip-permissions` so they can execute under root/sudo without failing. ### Built-in defaults If no `[[agents]]` are configured, Code advertises built-ins (gated by env `CODE_ENABLE_CLOUD_AGENT_MODEL` for cloud variants): `code-gpt-5.2`, `code-gpt-5.2-codex`, `claude-opus-4.5`, `gemini-3-pro`, `code-gpt-5.1-codex-mini`, `claude-sonnet-4.5`, `gemini-3-flash`, `claude-haiku-4.5`, `qwen-3-coder`, `cloud-gpt-5.1-codex-max`. Built-ins strip any user `--model/-m` flags to avoid conflicts and inject their own. @@ -27,16 +30,16 @@ Tip: `gemini` resolves to `gemini-3-flash` (fast/cheap). Use `gemini-3-pro` when ```toml [[subagents.commands]] name = "plan" # slash name (/plan, /solve, /code, or custom) -read_only = true # default plan/solve=true, code=false +read-only = true # default plan/solve=true, code=false agents = ["code-gpt-5.2-codex", "claude-opus-4.5"] # falls back to enabled agents or built-ins -orchestrator_instructions = "Guidance for Code before spawning agents" -agent_instructions = "Preamble added to each spawned agent" +orchestrator-instructions = "Guidance for Code before spawning agents" +agent-instructions = "Preamble added to each spawned agent" ``` - `name`: slash command created/overridden. -- `read_only`: forces spawned agents to RO when true. +- `read-only`: forces spawned agents to RO when true. - `agents`: explicit list; empty → enabled `[[agents]]`; none configured → built-in roster. -- `orchestrator_instructions`: appended to the Code-side prompt before issuing `agent.create`. -- `agent_instructions`: appended to each spawned agent prompt. +- `orchestrator-instructions`: appended to the Code-side prompt before issuing `agent.create`. +- `agent-instructions`: appended to each spawned agent prompt. The orchestrator fans out agents, waits for results, and merges reasoning according to your `hide_agent_reasoning` / `show_raw_agent_reasoning` settings. @@ -76,15 +79,15 @@ The orchestrator fans out agents, waits for results, and merges reasoning accord [[agents]] name = "my-coder" command = "/usr/local/bin/coder" -args_write = ["-s", "workspace-write", "--dangerously-bypass-approvals-and-sandbox", "exec", "--skip-git-repo-check"] +args-write = ["-s", "workspace-write", "--dangerously-bypass-approvals-and-sandbox", "exec", "--skip-git-repo-check"] enabled = true ``` - Custom context sweep command: ```toml [[subagents.commands]] name = "context" -read_only = true +read-only = true agents = ["code-gpt-5.2-codex", "claude-opus-4.5"] -orchestrator_instructions = "Have each agent summarize the most relevant files and tests." -agent_instructions = "Return paths plus 1–2 sentence rationale; do not edit files." +orchestrator-instructions = "Have each agent summarize the most relevant files and tests." +agent-instructions = "Return paths plus 1–2 sentence rationale; do not edit files." ``` diff --git a/docs/config.md b/docs/config.md index ef7da1b1c69..33a8c950806 100644 --- a/docs/config.md +++ b/docs/config.md @@ -184,7 +184,7 @@ approval_policy = "never" Use `[[agents]]` blocks to register additional CLI programs that Code can launch as peers. Each block maps a short `name` (referenced elsewhere in the config) to the command to execute, optional default flags, and environment variables. -> **Note:** Built-in model slugs (for example `code-gpt-5.2-codex`, `claude-sonnet-4.5`) automatically inject the correct `--model` or `-m` flag. To avoid conflicting arguments, Code strips any `--model`/`-m` flags you place in `args`, `args_read_only`, or `args_write` before launching the agent. If you need a new model variant, add a slug in `code-rs/core/src/agent_defaults.rs` (or set an environment variable consumed by the CLI) rather than pinning the flag here. +> **Note:** Built-in model slugs (for example `code-gpt-5.2-codex`, `claude-sonnet-4.5`) automatically inject the correct `--model` or `-m` flag. To avoid conflicting arguments, Code strips any `--model`/`-m` flags you place in `args`, `args-read-only`, or `args-write` before launching the agent. If you need a new model variant, add a slug in `code-rs/core/src/agent_defaults.rs` (or set an environment variable consumed by the CLI) rather than pinning the flag here. ```toml [[agents]] @@ -197,6 +197,17 @@ args = ["-y"] env = { GEMINI_API_KEY = "..." } ``` +For Claude CLI agents, set `claude-is-sandbox = true` to set `IS_SANDBOX=1` when invoking the agent (respects an existing `IS_SANDBOX` in `env`): + +```toml +[[agents]] +name = "claude-sonnet-4.5" +command = "claude" +claude-is-sandbox = true +``` + +Code now forces `IS_SANDBOX=1` and appends `--dangerously-skip-permissions` whenever it launches Claude subagents, so you do not need to set this flag to avoid root/sudo failures. + ## notice Code stores acknowledgement flags for one-time upgrade prompts inside a `[notice]`