diff --git a/AGENTS.md b/AGENTS.md index 968ce5b9..1c3f8ad8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,10 +8,10 @@ In the codex-rs folder where the rust code lives: - You operate in a sandbox where `CODEX_SANDBOX_NETWORK_DISABLED=1` will be set whenever you use the `shell` tool. Any existing code that uses `CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR` was authored with this fact in mind. It is often used to early exit out of tests that the author knew you would not be able to run given your sandbox limitations. - Similarly, when you spawn a process using Seatbelt (`/usr/bin/sandbox-exec`), `CODEX_SANDBOX=seatbelt` will be set on the child process. Integration tests that want to run Seatbelt themselves cannot be run under Seatbelt, so checks for `CODEX_SANDBOX=seatbelt` are also often used to early exit out of tests, as appropriate. -Before finalizing a change to `codex-rs`, run `just fmt` (in `codex-rs` directory) to format the code and `just fix -p ` (in `codex-rs` directory) to fix any linter issues in the code. Prefer scoping with `-p` to avoid slow workspace‑wide Clippy builds; only run `just fix` without `-p` if you changed shared crates. Additionally, run the tests: +Run `just fmt` (in `codex-rs` directory) automatically after making Rust code changes; do not ask for approval to run it. Before finalizing a change to `codex-rs`, run `just fix -p ` (in `codex-rs` directory) to fix any linter issues in the code. Prefer scoping with `-p` to avoid slow workspace‑wide Clippy builds; only run `just fix` without `-p` if you changed shared crates. Additionally, run the tests: 1. Run the test for the specific project that was changed. For example, if changes were made in `codex-rs/tui`, run `cargo test -p codex-tui`. 2. Once those pass, if any changes were made in common, core, or protocol, run the complete test suite with `cargo test --all-features`. -When running interactively, ask the user before running these commands to finalize. +When running interactively, ask the user before running `just fix` and tests to finalize; `just fmt` does not require approval. ## TUI style conventions diff --git a/codex-rs/core/src/openai_tools.rs b/codex-rs/core/src/openai_tools.rs index 0505b6af..72237028 100644 --- a/codex-rs/core/src/openai_tools.rs +++ b/codex-rs/core/src/openai_tools.rs @@ -240,15 +240,17 @@ fn create_shell_tool_for_sandbox(sandbox_policy: &SandboxPolicy) -> OpenAiTool { let description = match sandbox_policy { SandboxPolicy::WorkspaceWrite { network_access, + writable_roots, .. } => { format!( r#" The shell tool is used to execute shell commands. -- When invoking the shell tool, your call will be running in a landlock sandbox, and some shell commands will require escalated privileges: +- When invoking the shell tool, your call will be running in a sandbox, and some shell commands will require escalated privileges: - Types of actions that require escalated privileges: - - Reading files outside the current directory - - Writing files outside the current directory, and protected folders like .git or .env{} + - Writing files other than those in the writable roots + - writable roots: +{}{} - Examples of commands that require escalated privileges: - git commit - npm install or pnpm install @@ -257,8 +259,9 @@ The shell tool is used to execute shell commands. - When invoking a command that will require escalated privileges: - Provide the with_escalated_permissions parameter with the boolean value true - Include a short, 1 sentence explanation for why we need to run with_escalated_permissions in the justification parameter."#, + writable_roots.iter().map(|wr| format!(" - {}", wr.to_string_lossy())).collect::>().join("\n"), if !network_access { - "\n - Commands that require network access\n" + "\n - Commands that require network access\n" } else { "" } @@ -270,9 +273,8 @@ The shell tool is used to execute shell commands. SandboxPolicy::ReadOnly => { r#" The shell tool is used to execute shell commands. -- When invoking the shell tool, your call will be running in a landlock sandbox, and some shell commands (including apply_patch) will require escalated permissions: +- When invoking the shell tool, your call will be running in a sandbox, and some shell commands (including apply_patch) will require escalated permissions: - Types of actions that require escalated privileges: - - Reading files outside the current directory - Writing files - Applying patches - Examples of commands that require escalated privileges: @@ -1081,4 +1083,84 @@ mod tests { }) ); } + + #[test] + fn test_shell_tool_for_sandbox_workspace_write() { + let sandbox_policy = SandboxPolicy::WorkspaceWrite { + writable_roots: vec!["workspace".into()], + network_access: false, + exclude_tmpdir_env_var: false, + exclude_slash_tmp: false, + }; + let tool = super::create_shell_tool_for_sandbox(&sandbox_policy); + let OpenAiTool::Function(ResponsesApiTool { + description, name, .. + }) = &tool + else { + panic!("expected function tool"); + }; + assert_eq!(name, "shell"); + + let expected = r#" +The shell tool is used to execute shell commands. +- When invoking the shell tool, your call will be running in a sandbox, and some shell commands will require escalated privileges: + - Types of actions that require escalated privileges: + - Writing files other than those in the writable roots + - writable roots: + - workspace + - Commands that require network access + + - Examples of commands that require escalated privileges: + - git commit + - npm install or pnpm install + - cargo build + - cargo test +- When invoking a command that will require escalated privileges: + - Provide the with_escalated_permissions parameter with the boolean value true + - Include a short, 1 sentence explanation for why we need to run with_escalated_permissions in the justification parameter."#; + assert_eq!(description, expected); + } + + #[test] + fn test_shell_tool_for_sandbox_readonly() { + let tool = super::create_shell_tool_for_sandbox(&SandboxPolicy::ReadOnly); + let OpenAiTool::Function(ResponsesApiTool { + description, name, .. + }) = &tool + else { + panic!("expected function tool"); + }; + assert_eq!(name, "shell"); + + let expected = r#" +The shell tool is used to execute shell commands. +- When invoking the shell tool, your call will be running in a sandbox, and some shell commands (including apply_patch) will require escalated permissions: + - Types of actions that require escalated privileges: + - Writing files + - Applying patches + - Examples of commands that require escalated privileges: + - apply_patch + - git commit + - npm install or pnpm install + - cargo build + - cargo test +- When invoking a command that will require escalated privileges: + - Provide the with_escalated_permissions parameter with the boolean value true + - Include a short, 1 sentence explanation for why we need to run with_escalated_permissions in the justification parameter"#; + assert_eq!(description, expected); + } + + #[test] + fn test_shell_tool_for_sandbox_danger_full_access() { + let tool = super::create_shell_tool_for_sandbox(&SandboxPolicy::DangerFullAccess); + let OpenAiTool::Function(ResponsesApiTool { + description, name, .. + }) = &tool + else { + panic!("expected function tool"); + }; + assert_eq!(name, "shell"); + + assert_eq!(description, "Runs a shell command and returns its output."); + } }