From e8271aa8b482f917b2e62dca0e51e29b8d5ec156 Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Fri, 11 Sep 2026 23:04:04 +0000 Subject: [PATCH] Add scenario snapshots for remote compaction and Code Mode tools (#44934) ## What changed Add two integration scenarios for `gpt-6-astra` that snapshot request history and settings: - A multi-turn conversation with local and plugin skills, remote compaction, and a follow-up containing an image. - A release check combining direct collaboration calls with Code Mode shell commands, MCP calls, image viewing, and patch application, including reading back the edited file. GitOrigin-RevId: 9b8a48a817c3dba4b139d1b0d015e8084dcb219b --- codex-rs/core/tests/suite/mod.rs | 1 + codex-rs/core/tests/suite/scenarios.rs | 388 ++++++++++++++++ ...tra_kickoff_remote_compaction_windows.snap | 439 ++++++++++++++++++ ...ra_settings_release_check_tool_shapes.snap | 211 +++++++++ 4 files changed, 1039 insertions(+) create mode 100644 codex-rs/core/tests/suite/scenarios.rs create mode 100644 codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_kickoff_remote_compaction_windows.snap create mode 100644 codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_settings_release_check_tool_shapes.snap diff --git a/codex-rs/core/tests/suite/mod.rs b/codex-rs/core/tests/suite/mod.rs index 566cee6083..1ef839af96 100644 --- a/codex-rs/core/tests/suite/mod.rs +++ b/codex-rs/core/tests/suite/mod.rs @@ -164,6 +164,7 @@ mod rollout_compression; mod rollout_list_find; mod safety_buffering; mod safety_check_downgrade; +mod scenarios; mod search_tool; mod settings_commits; mod settings_constraints; diff --git a/codex-rs/core/tests/suite/scenarios.rs b/codex-rs/core/tests/suite/scenarios.rs new file mode 100644 index 0000000000..62bbbbc6db --- /dev/null +++ b/codex-rs/core/tests/suite/scenarios.rs @@ -0,0 +1,388 @@ +//! A multi-turn Astra scenario whose actual requests expose the context around remote compaction. + +use std::fs; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + +use anyhow::Result; +use base64::Engine; +use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; +use codex_config::ConfigLayerSource; +use codex_config::ConfigLayerStack; +use codex_config::types::McpServerConfig; +use codex_core::TurnInputRequest; +use codex_core::config::Config; +use codex_extension_api::ExtensionRegistry; +use codex_extension_api::ExtensionRegistryBuilder; +use codex_features::Feature; +use codex_login::CodexAuth; +use codex_models_manager::bundled_models_response; +use codex_protocol::protocol::EventMsg; +use codex_protocol::protocol::Op; +use codex_protocol::user_input::UserInput; +use codex_skills_extension::SkillsExtensionConfig; +use codex_skills_extension::install; +use core_test_support::context_snapshot; +use core_test_support::context_snapshot::ContextSnapshotOptions; +use core_test_support::responses::ev_assistant_message; +use core_test_support::responses::ev_completed; +use core_test_support::responses::ev_custom_tool_call; +use core_test_support::responses::ev_function_call_with_namespace; +use core_test_support::responses::ev_response_created; +use core_test_support::responses::mount_sse_sequence; +use core_test_support::responses::sse; +use core_test_support::responses::start_mock_server; +use core_test_support::skip_if_no_network; +use core_test_support::skip_if_wine_exec; +use core_test_support::stdio_server_bin; +use core_test_support::test_codex::executor_path_uri; +use core_test_support::test_codex::test_codex; +use core_test_support::wait_for_event; +use core_test_support::wait_for_mcp_server; +use serde_json::json; +use tempfile::TempDir; + +const ONE_PIXEL_PNG_BASE64: &str = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg=="; + +fn skills_extensions() -> Arc> { + let mut extensions = ExtensionRegistryBuilder::::new(); + install(&mut extensions, |config: &Config| SkillsExtensionConfig { + include_instructions: config.include_skill_instructions, + max_context_tokens: config.skill_max_context_tokens, + bundled_skills_enabled: config.bundled_skills_enabled(), + orchestrator_skills_enabled: config.orchestrator_skills_enabled, + shadow_selection_enabled: config.features.enabled(Feature::SkillSearch), + }); + Arc::new(extensions.build()) +} + +fn write_skill(path: &Path, name: &str, description: &str, body: &str) -> Result { + fs::create_dir_all(path)?; + let skill = path.join("SKILL.md"); + fs::write( + &skill, + format!("---\nname: {name}\ndescription: {description}\n---\n\n{body}\n"), + )?; + Ok(fs::canonicalize(skill)?) +} + +struct ScenarioSkills { + outline: PathBuf, + agenda: PathBuf, + summarize: PathBuf, + final_check: PathBuf, +} + +fn write_scenario_capabilities(home: &TempDir) -> Result { + fs::write( + home.path().join("config.toml"), + "[features]\nplugins = true\n\n[skills.bundled]\nenabled = false\n\n[plugins.\"calendar@test\"]\nenabled = true\n\n[plugins.\"notes@test\"]\nenabled = true\n", + )?; + let plugin_cache = home.path().join("plugins/cache/test"); + for (name, description) in [ + ("calendar", "Prepare a team schedule"), + ("notes", "Summarize meeting notes"), + ] { + let manifest = plugin_cache + .join(name) + .join("local/.codex-plugin/plugin.json"); + fs::create_dir_all(manifest.parent().expect("manifest parent"))?; + fs::write( + manifest, + json!({ "name": name, "description": description }).to_string(), + )?; + } + + Ok(ScenarioSkills { + outline: write_skill( + &home.path().join("skills/outline"), + "outline", + "Draft a project outline", + "List the goals and owners.", + )?, + agenda: write_skill( + &plugin_cache.join("calendar/local/skills/agenda"), + "agenda", + "Plan a team agenda", + "List meetings with dates and attendees.", + )?, + summarize: write_skill( + &plugin_cache.join("notes/local/skills/summarize"), + "summarize", + "Summarize team notes", + "Extract decisions and action items.", + )?, + final_check: write_skill( + &home.path().join("skills/final-check"), + "final-check", + "Review a final brief", + "Check that the brief has an owner for every action.", + )?, + }) +} + +fn text(value: &str) -> UserInput { + UserInput::Text { + text: value.to_string(), + text_elements: Vec::new(), + } +} + +fn selected_skill(name: &str, path: &Path) -> UserInput { + UserInput::Skill { + name: name.to_string(), + path: path.to_path_buf(), + } +} + +fn plugin(name: &str) -> UserInput { + UserInput::Mention { + name: name.to_string(), + path: format!("plugin://{name}@test"), + } +} + +fn configure_scenario_catalog(config: &mut Config) { + // Keep the fixture independent of the checkout's project configuration. + let stack = &config.config_layer_stack; + config.config_layer_stack = ConfigLayerStack::new( + stack + .all_layers_low_to_high() + .filter(|layer| !matches!(&layer.name, ConfigLayerSource::Project { .. })) + .cloned() + .collect(), + stack.requirements().clone(), + stack.requirements_toml().clone(), + ) + .expect("fixture config layers"); + config.model_catalog = Some(bundled_models_response().expect("bundled model catalog")); + config.orchestrator_skills_enabled = false; +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn astra_kickoff_with_skills_plugins_and_remote_compaction() -> Result<()> { + skip_if_no_network!(Ok(())); + + let server = start_mock_server().await; + let home = Arc::new(TempDir::new()?); + let skills = write_scenario_capabilities(&home)?; + let mock = mount_sse_sequence( + &server, + vec![ + sse(vec![ + ev_assistant_message("draft", "Agenda drafted for the kickoff."), + ev_completed("draft-response"), + ]), + sse(vec![ + ev_assistant_message("notes", "The team chose Friday and assigned owners."), + ev_completed("notes-response"), + ]), + sse(vec![ + json!({ + "type": "response.output_item.done", + "item": { + "type": "compaction", + "encrypted_content": "SCENARIO_REMOTE_CHECKPOINT", + } + }), + ev_completed("compact-response"), + ]), + sse(vec![ + ev_assistant_message("final", "Here is the checked kickoff brief."), + ev_completed("final-response"), + ]), + ], + ) + .await; + let mut builder = test_codex() + .with_model("gpt-6-astra") + .with_home(home) + .with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing()) + .with_extensions(skills_extensions()) + .with_workspace_setup(|cwd, fs| async move { + fs.write_file( + &executor_path_uri(cwd.join("AGENTS.md"))?, + b"Kickoff updates must name an owner and a date.".to_vec(), + Default::default(), + /*sandbox*/ None, + ) + .await?; + Ok::<(), anyhow::Error>(()) + }) + .with_config(|config| { + configure_scenario_catalog(config); + }); + let test = builder.build(&server).await?; + + for input in [ + vec![ + text("Plan a team kickoff for Friday using $outline and $calendar:agenda."), + selected_skill("outline", &skills.outline), + selected_skill("calendar:agenda", &skills.agenda), + plugin("calendar"), + ], + vec![ + text("Summarize the kickoff notes with $notes:summarize."), + selected_skill("notes:summarize", &skills.summarize), + plugin("notes"), + ], + ] + .into_iter() + { + test.codex + .start_or_steer_turn(TurnInputRequest::user_input(input)) + .await?; + wait_for_event(&test.codex, |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + } + + test.codex.submit(Op::Compact).await?; + wait_for_event(&test.codex, |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + + test.codex + .start_or_steer_turn(TurnInputRequest::user_input(vec![ + text("Check the final kickoff brief and attached sketch with $final-check and $calendar:agenda."), + UserInput::Image { + image_url: format!("data:image/png;base64,{ONE_PIXEL_PNG_BASE64}"), + detail: None, + }, + selected_skill("final-check", &skills.final_check), + plugin("calendar"), + ])) + .await?; + wait_for_event(&test.codex, |event| { + matches!(event, EventMsg::TurnComplete(_)) + }) + .await; + + let requests = mock.requests(); + insta::assert_snapshot!( + "astra_kickoff_remote_compaction_windows", + context_snapshot::format_request_history_snapshot( + "Astra plans a kickoff with local and plugin skills, remotely compacts, and checks an image brief.", + &requests, + &ContextSnapshotOptions::default().include_request_settings(), + ) + ); + Ok(()) +} + +#[cfg_attr(windows, ignore = "the fixture uses a Unix shell command")] +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn astra_settings_release_check_with_direct_and_code_mode_tools() -> Result<()> { + skip_if_no_network!(Ok(())); + skip_if_wine_exec!(Ok(()), "the fixture uses a Unix shell command"); + + let server = start_mock_server().await; + let rmcp_server_bin = stdio_server_bin()?; + let home = Arc::new(TempDir::new()?); + let mut builder = test_codex() + .with_model("gpt-6-astra") + .with_home(home) + .with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing()) + .with_config(move |config| { + configure_scenario_catalog(config); + let mut servers = config.mcp_servers.get().clone(); + servers.insert( + "rmcp".to_string(), + serde_json::from_value::(json!({ + "command": rmcp_server_bin, + "env": { "MCP_TEST_VALUE": "release-check" }, + })) + .expect("test MCP server config"), + ); + config.mcp_servers.set(servers).expect("test MCP servers"); + }); + let test = builder.build(&server).await?; + let release = test.cwd_path().join("release"); + fs::create_dir_all(&release)?; + let diagnostics = std::iter::once("UI-42: expected Settings button label: Apply".to_string()) + .chain((1..=120).map(|line| format!("diagnostic {line:03}: rendering settings panel"))) + .chain(std::iter::once( + "UI-42: observed Settings button label: Save".to_string(), + )) + .collect::>() + .join("\n"); + fs::write(release.join("diagnostics.log"), diagnostics)?; + fs::write(release.join("status.md"), "Status: pending\n")?; + fs::write( + release.join("settings.png"), + BASE64_STANDARD.decode(ONE_PIXEL_PNG_BASE64)?, + )?; + wait_for_mcp_server(&test.codex, "rmcp").await?; + + let patch = "*** Begin Patch\n*** Update File: release/status.md\n@@\n-Status: pending\n+Status: blocked\n+Reason: expected Apply; observed Save\n+MCP: reachable\n*** End Patch\n"; + let patch_code = format!("text(await tools.apply_patch(`{patch}`));"); + let mock = mount_sse_sequence( + &server, + vec![ + sse(vec![ + ev_response_created("agents-response"), + ev_function_call_with_namespace("agents-call", "collaboration", "list_agents", "{}"), + ev_completed("agents-response"), + ]), + sse(vec![ + ev_response_created("diagnostics-response"), + ev_custom_tool_call( + "diagnostics-call", + "exec", + r#"const [log, ping] = await Promise.all([ + tools.exec_command({ cmd: "cat release/diagnostics.log", login: false, max_output_tokens: 4000 }), + tools.mcp__rmcp__echo({ message: "settings-release-check" }), +]); +text(`diagnostics:\n${log.output}`); +text(`MCP: ${ping.structuredContent?.echo ?? "missing"}`);"#, + ), + ev_completed("diagnostics-response"), + ]), + sse(vec![ + ev_response_created("image-response"), + ev_custom_tool_call( + "image-call", + "exec", + "image(await tools.view_image({ path: \"release/settings.png\", detail: \"original\" }));", + ), + ev_completed("image-response"), + ]), + sse(vec![ + ev_response_created("patch-response"), + ev_custom_tool_call("patch-call", "exec", &patch_code), + ev_completed("patch-response"), + ]), + sse(vec![ + ev_response_created("readback-response"), + ev_custom_tool_call( + "readback-call", + "exec", + "text((await tools.exec_command({ cmd: \"cat release/status.md\", login: false })).output);", + ), + ev_completed("readback-response"), + ]), + sse(vec![ + ev_assistant_message( + "final", + "The Settings release is blocked: expected Apply, observed Save. The MCP integration responded and no other agent is working on this task.", + ), + ev_completed("final-response"), + ]), + ], + ) + .await; + + test.submit_turn("Check the Settings release. Read release/diagnostics.log, inspect release/settings.png, confirm the local MCP integration responds, and update release/status.md with the result. Tell me whether another agent is working on this task.").await?; + insta::assert_snapshot!( + "astra_settings_release_check_tool_shapes", + context_snapshot::format_request_history_snapshot( + "Astra checks a Settings release using direct collaboration and Code Mode tools.", + &mock.requests(), + &ContextSnapshotOptions::default().include_request_settings(), + ) + ); + Ok(()) +} diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_kickoff_remote_compaction_windows.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_kickoff_remote_compaction_windows.snap new file mode 100644 index 0000000000..1a110401d3 --- /dev/null +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_kickoff_remote_compaction_windows.snap @@ -0,0 +1,439 @@ +--- +source: core/tests/suite/scenarios.rs +expression: "context_snapshot::format_request_history_snapshot(\"Astra plans a kickoff with local and plugin skills, remotely compacts, and checks an image brief.\",\n&requests, &ContextSnapshotOptions::default().include_request_settings(),)" +--- +Scenario: Astra plans a kickoff with local and plugin skills, remotely compacts, and checks an image brief. + +## Window 1 +Settings: + include: ["reasoning.encrypted_content"] + model: "gpt-6-astra" + parallel_tool_calls: false + prompt_cache_key: "" + reasoning: {"context":"all_turns","effort":"low"} + store: false + stream: true + text: {"verbosity":"low"} + tool_choice: "auto" +-- request 1 (turn) -- +00:additional_tools/developer (3; hash=d4f3e164a92d9bf1): + - namespace/functions; hash=e7db0a45c4ec3157 + - custom/exec + - function/wait + - function/request_user_input + - function/request_user_input_async + - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 + - function/sleep + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=43f195d9cc99ee15 + - function/followup_task + - function/interrupt_agent + - function/list_agents + - function/send_message + - function/spawn_agent + - function/wait_agent +01:message/developer: + You are Codex, an agent based on GPT-6. You and the user share one workspace, and your job is to collabo...them until their intended goal is completely handled. [hash=efa50f074efa5803] + + # When to ask the user for permission + + Use your best judgement given task context for when you really need user permission, like a competent co...ork without ending the turn to clarify with the user. [hash=ca7367e658b51388] + + User authorization and preferences persist across turns. Do not request permission again when the user h... any guidelines provided in skills or external files. [hash=911f91c92c72882c] + + You MUST complete the work that is already authorized and necessary to make the proposed action concrete... in the session or implied from the task instruction. [hash=821281c2d48149b3] + + Do not use tools to send messages to others (e.g. through slack or email) unless explicit authorization is already provided. + + The user gets very frustrated when you stop and ask for confirmation or permission, so make sure to expl... commentary and final, after any permission question. [hash=7e6ab67a40648d1d] + + # Autonomy and persistence + + + An installed app's MCP tools are either provided to you already, or can be lazy-loaded through the `tool...re searchable by `tools_search` will be listed by it. [hash=4f9c0d35da3968a5] + Do not additionally call list_mcp_resources or list_mcp_resource_templates for apps. + + # Plugins + + A plugin is a local bundle of skills, MCP servers, and apps. + + ## How to use plugins + + - Skill naming: If a plugin contributes skills, those skill entries are prefixed with plugin_name: in the Skills list. + - MCP naming: Plugin-provided MCP tools keep standard MCP identifiers such as mcp__server__tool; use tool provenance to tell which plugin they come from. + - Trigger rules: If the user explicitly names a plugin, prefer capabilities associated with that plugin for that turn. + - Relationship to capabilities: Plugins are not invoked directly. Use their underlying skills, MCP tools, and app tools to help solve the task. + - Relevance: Determine what a plugin can help with from explicit user mention or from the plugin-associa..., MCP tools, and apps exposed elsewhere in this turn. [hash=620459a4e7b590f0] + - Missing/blocked: If the user requests a plugin that does not have relevant callable capabilities for t..., say so briefly and continue with the best fallback. [hash=746b0469a8f6cb3e] + +02:message/developer[3]: + [01] + ## Skills + A skill is a set of local instructions to follow that is stored in a `SKILL.md` file. Below is the list ...ed into an absolute path using the skill roots table. [hash=ca27c3957f42502b] + ### Skill roots + - `r0` = `` + - `r1` = `/test` + ### Available skills + - calendar:agenda: Plan a team agenda (file: r1/calendar/local/skills/agenda/SKILL.md) + - final-check: Review a final brief (file: r0/final-check/SKILL.md) + - notes:summarize: Summarize team notes (file: r1/notes/local/skills/summarize/SKILL.md) + - outline: Draft a project outline (file: r0/outline/SKILL.md) + + [02] + Filesystem sandboxing defines which files can be read or written. `sandbox_mode` is `read-only`: The san... permits reading files. Network access is restricted. [hash=be7da4425f89053c] + # Escalation Requests + + Commands are run outside the sandbox if they are approved by the user, or match an existing rule that al...hell control operators, including but not limited to: [hash=d24ebf5fa4e4b9bb] + + - Pipes: | + - Logical operators: &&, || + - Command separators: ; + - Subshell boundaries: (...), $(...) + + Each resulting segment is evaluated independently for sandbox restrictions and approval requirements. + + Example: + + git pull | tee output.txt + + + ## prefix_rule guidance + + When choosing a `prefix_rule`, request one that will allow you to fulfill similar requests from the user...ld rarely pass the entire command into `prefix_rule`. [hash=1c226ac9ca17df4c] + + ### Banned prefix_rules + Avoid requesting overly broad prefixes that the user would be ill-advised to approve. For example, do no...imilar prefixes that would allow arbitrary scripting. [hash=4dbef1aacccd3955] + NEVER provide a prefix_rule argument for destructive commands like rm. + NEVER provide a prefix_rule if your command uses a heredoc or herestring. + + ### Examples + Good examples of prefixes: + - ["npm", "run", "dev"] + - ["gh", "pr", "check"] + - ["cargo", "test"] + + [03] # Collaboration Mode: Default + + You are now in Default mode. Any previous instructions for other modes (e.g. Plan mode) are no longer active. + + Your active mode changes only when new developer instructions with a different `...<...by themselves. Known mode names are Default and Plan. [hash=e75f6b12fc52d812] + + ## request_user_input availability + + Use the `request_user_input` tool only when it is listed in the available tools for this turn. + + In Default mode, strongly prefer making reasonable assumptions and executing the user's request rather than stopping to ask questions. + + Use the `request_user_input` tool only for optional questions where the answer would materially improve the quality of the work. + + If `request_user_input` returns no answers, continue with best judgment instead of asking again or treating the turn as blocked. + + Never use the `request_user_input` tool for permission requests or permission-related escalations. + + If explicit user input is required for another reason before progress can safely continue, do not use th... as a textual assistant message. [hash=8213052bde4a62b2] +03:message/developer: + You are `/root`, the primary agent in a team of agents collaborating to fulfill the user's goals. + + At the start of your turn, you are the active agent. + You can spawn sub-agents to handle subtasks, and those sub-agents can spawn their own sub-agents. + All agents in the team, including the agents that you can assign tasks to, are equally intelligent and capable, and have access to the same set of tools. + + You can use `spawn_agent` to create a new agent, `followup_task` to give an existing agent a new task an...message to a running agent without triggering a turn. [hash=7d40ff8b58977202] + `send_message` calls may be read by a human, so ensure they are legible. Always put proper spaces between words and/or numbers. + Child agents can also spawn their own sub-agents. + You can decide how much context you want to propagate to your sub-agents with the `fork_turns` parameter. + + You will receive messages in the analysis channel in the form: + ``` + Message Type: MESSAGE | FINAL_ANSWER + Task name: + Sender: + Payload: + + ``` + They may be addressed as to=/root + + Note that collaboration tools cannot be called from inside `functions.exec`. Call `spawn_agent`, `send_m...ed with a `tools` namespace in the developer message. [hash=2798139748f66a39] + + All agents share the same directory. In detail: + - All agents have access to the same container and filesystem as you. + - All agents use the same current working directory. + - As a result, edits made by one agent are immediately visible to all other agents. + + When calling `wait_agent`, prefer longer waits (minutes) to avoid busy polling. + + There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. + + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] +04:message/developer: + Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] +05:message/user[2]: + [01] # AGENTS.md instructions for + + + Kickoff updates must name an owner and a date. + + [02] + + + + + [hash=911722cc43b4c3b8] + +06:message/user: + Plan a team kickoff for Friday using $outline and $calendar:agenda. +07:message/user: + + outline + /outline/SKILL.md + --- + name: outline + description: Draft a project outline + --- + + List the goals and owners. + + +08:message/user: + + calendar:agenda + /test/calendar/local/skills/agenda/SKILL.md + --- + name: agenda + description: Plan a team agenda + --- + + List meetings with dates and attendees. + + +09:message/developer: + Capabilities from the `calendar` plugin: + - Skills from this plugin are prefixed with `calendar:`. + Use these plugin-associated capabilities to help solve the task. +-- request 2 (turn) -- +10:message/assistant: + Agenda drafted for the kickoff. +11:message/user: + Summarize the kickoff notes with $notes:summarize. +12:message/user: + + notes:summarize + /test/notes/local/skills/summarize/SKILL.md + --- + name: summarize + description: Summarize team notes + --- + + Extract decisions and action items. + + +13:message/developer: + Capabilities from the `notes` plugin: + - Skills from this plugin are prefixed with `notes:`. + Use these plugin-associated capabilities to help solve the task. +-- request 3 (compaction) -- +14:message/assistant: + The team chose Friday and assigned owners. +15:compaction_trigger + +## Window 2 (after request 3: input diverged at item 02) +Settings: same as window 1 +-- request 4 (turn) -- +00:additional_tools/developer (3; hash=d4f3e164a92d9bf1): + - namespace/functions; hash=e7db0a45c4ec3157 + - custom/exec + - function/wait + - function/request_user_input + - function/request_user_input_async + - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 + - function/sleep + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=43f195d9cc99ee15 + - function/followup_task + - function/interrupt_agent + - function/list_agents + - function/send_message + - function/spawn_agent + - function/wait_agent +01:message/developer: + You are Codex, an agent based on GPT-6. You and the user share one workspace, and your job is to collabo...them until their intended goal is completely handled. [hash=efa50f074efa5803] + + # When to ask the user for permission + + Use your best judgement given task context for when you really need user permission, like a competent co...ork without ending the turn to clarify with the user. [hash=ca7367e658b51388] + + User authorization and preferences persist across turns. Do not request permission again when the user h... any guidelines provided in skills or external files. [hash=911f91c92c72882c] + + You MUST complete the work that is already authorized and necessary to make the proposed action concrete... in the session or implied from the task instruction. [hash=821281c2d48149b3] + + Do not use tools to send messages to others (e.g. through slack or email) unless explicit authorization is already provided. + + The user gets very frustrated when you stop and ask for confirmation or permission, so make sure to expl... commentary and final, after any permission question. [hash=7e6ab67a40648d1d] + + # Autonomy and persistence + + + An installed app's MCP tools are either provided to you already, or can be lazy-loaded through the `tool...re searchable by `tools_search` will be listed by it. [hash=4f9c0d35da3968a5] + Do not additionally call list_mcp_resources or list_mcp_resource_templates for apps. + + # Plugins + + A plugin is a local bundle of skills, MCP servers, and apps. + + ## How to use plugins + + - Skill naming: If a plugin contributes skills, those skill entries are prefixed with plugin_name: in the Skills list. + - MCP naming: Plugin-provided MCP tools keep standard MCP identifiers such as mcp__server__tool; use tool provenance to tell which plugin they come from. + - Trigger rules: If the user explicitly names a plugin, prefer capabilities associated with that plugin for that turn. + - Relationship to capabilities: Plugins are not invoked directly. Use their underlying skills, MCP tools, and app tools to help solve the task. + - Relevance: Determine what a plugin can help with from explicit user mention or from the plugin-associa..., MCP tools, and apps exposed elsewhere in this turn. [hash=620459a4e7b590f0] + - Missing/blocked: If the user requests a plugin that does not have relevant callable capabilities for t..., say so briefly and continue with the best fallback. [hash=746b0469a8f6cb3e] + +02:message/user: + Plan a team kickoff for Friday using $outline and $calendar:agenda. +03:message/user: + Summarize the kickoff notes with $notes:summarize. +04:compaction:encrypted=true; chars=26; hash=ec5e2724e6f5f641 +05:message/developer[3]: + [01] + ## Skills + A skill is a set of local instructions to follow that is stored in a `SKILL.md` file. Below is the list ...ed into an absolute path using the skill roots table. [hash=ca27c3957f42502b] + ### Skill roots + - `r0` = `` + - `r1` = `/test` + ### Available skills + - calendar:agenda: Plan a team agenda (file: r1/calendar/local/skills/agenda/SKILL.md) + - final-check: Review a final brief (file: r0/final-check/SKILL.md) + - notes:summarize: Summarize team notes (file: r1/notes/local/skills/summarize/SKILL.md) + - outline: Draft a project outline (file: r0/outline/SKILL.md) + + [02] + Filesystem sandboxing defines which files can be read or written. `sandbox_mode` is `read-only`: The san... permits reading files. Network access is restricted. [hash=be7da4425f89053c] + # Escalation Requests + + Commands are run outside the sandbox if they are approved by the user, or match an existing rule that al...hell control operators, including but not limited to: [hash=d24ebf5fa4e4b9bb] + + - Pipes: | + - Logical operators: &&, || + - Command separators: ; + - Subshell boundaries: (...), $(...) + + Each resulting segment is evaluated independently for sandbox restrictions and approval requirements. + + Example: + + git pull | tee output.txt + + + ## prefix_rule guidance + + When choosing a `prefix_rule`, request one that will allow you to fulfill similar requests from the user...ld rarely pass the entire command into `prefix_rule`. [hash=1c226ac9ca17df4c] + + ### Banned prefix_rules + Avoid requesting overly broad prefixes that the user would be ill-advised to approve. For example, do no...imilar prefixes that would allow arbitrary scripting. [hash=4dbef1aacccd3955] + NEVER provide a prefix_rule argument for destructive commands like rm. + NEVER provide a prefix_rule if your command uses a heredoc or herestring. + + ### Examples + Good examples of prefixes: + - ["npm", "run", "dev"] + - ["gh", "pr", "check"] + - ["cargo", "test"] + + [03] # Collaboration Mode: Default + + You are now in Default mode. Any previous instructions for other modes (e.g. Plan mode) are no longer active. + + Your active mode changes only when new developer instructions with a different `...<...by themselves. Known mode names are Default and Plan. [hash=e75f6b12fc52d812] + + ## request_user_input availability + + Use the `request_user_input` tool only when it is listed in the available tools for this turn. + + In Default mode, strongly prefer making reasonable assumptions and executing the user's request rather than stopping to ask questions. + + Use the `request_user_input` tool only for optional questions where the answer would materially improve the quality of the work. + + If `request_user_input` returns no answers, continue with best judgment instead of asking again or treating the turn as blocked. + + Never use the `request_user_input` tool for permission requests or permission-related escalations. + + If explicit user input is required for another reason before progress can safely continue, do not use th... as a textual assistant message. [hash=8213052bde4a62b2] +06:message/developer: + You are `/root`, the primary agent in a team of agents collaborating to fulfill the user's goals. + + At the start of your turn, you are the active agent. + You can spawn sub-agents to handle subtasks, and those sub-agents can spawn their own sub-agents. + All agents in the team, including the agents that you can assign tasks to, are equally intelligent and capable, and have access to the same set of tools. + + You can use `spawn_agent` to create a new agent, `followup_task` to give an existing agent a new task an...message to a running agent without triggering a turn. [hash=7d40ff8b58977202] + `send_message` calls may be read by a human, so ensure they are legible. Always put proper spaces between words and/or numbers. + Child agents can also spawn their own sub-agents. + You can decide how much context you want to propagate to your sub-agents with the `fork_turns` parameter. + + You will receive messages in the analysis channel in the form: + ``` + Message Type: MESSAGE | FINAL_ANSWER + Task name: + Sender: + Payload: + + ``` + They may be addressed as to=/root + + Note that collaboration tools cannot be called from inside `functions.exec`. Call `spawn_agent`, `send_m...ed with a `tools` namespace in the developer message. [hash=2798139748f66a39] + + All agents share the same directory. In detail: + - All agents have access to the same container and filesystem as you. + - All agents use the same current working directory. + - As a result, edits made by one agent are immediately visible to all other agents. + + When calling `wait_agent`, prefer longer waits (minutes) to avoid busy polling. + + There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. + + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] +07:message/developer: + Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] +08:message/user[2]: + [01] # AGENTS.md instructions for + + + Kickoff updates must name an owner and a date. + + [02] + + + + + [hash=911722cc43b4c3b8] + +09:message/user[2]: + [01] Check the final kickoff brief and attached sketch with $final-check and $calendar:agenda. + [02] +10:message/user: + + final-check + /final-check/SKILL.md + --- + name: final-check + description: Review a final brief + --- + + Check that the brief has an owner for every action. + + +11:message/user: + + calendar:agenda + /test/calendar/local/skills/agenda/SKILL.md + --- + name: agenda + description: Plan a team agenda + --- + + List meetings with dates and attendees. + + +12:message/developer: + Capabilities from the `calendar` plugin: + - Skills from this plugin are prefixed with `calendar:`. + Use these plugin-associated capabilities to help solve the task. diff --git a/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_settings_release_check_tool_shapes.snap b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_settings_release_check_tool_shapes.snap new file mode 100644 index 0000000000..c3b80cc3c0 --- /dev/null +++ b/codex-rs/core/tests/suite/snapshots/all__suite__scenarios__astra_settings_release_check_tool_shapes.snap @@ -0,0 +1,211 @@ +--- +source: core/tests/suite/scenarios.rs +expression: "context_snapshot::format_request_history_snapshot(\"Astra checks a Settings release using direct collaboration and Code Mode tools.\",\n&mock.requests(),\n&ContextSnapshotOptions::default().include_request_settings(),)" +--- +Scenario: Astra checks a Settings release using direct collaboration and Code Mode tools. + +## Window 1 +Settings: + include: ["reasoning.encrypted_content"] + model: "gpt-6-astra" + parallel_tool_calls: false + prompt_cache_key: "" + reasoning: {"context":"all_turns","effort":"low"} + store: false + stream: true + text: {"verbosity":"low"} + tool_choice: "auto" +-- request 1 (turn) -- +00:additional_tools/developer (3; hash=61ace4f77de8d87f): + - namespace/functions; hash=8c17932a39355f2d + - custom/exec + - function/wait + - function/request_user_input + - function/request_user_input_async + - namespace/clock: Tools for reading and waiting on time.; hash=f402e8c5e9b5e317 + - function/sleep + - namespace/collaboration: Tools for spawning and managing sub-agents.; hash=43f195d9cc99ee15 + - function/followup_task + - function/interrupt_agent + - function/list_agents + - function/send_message + - function/spawn_agent + - function/wait_agent +01:message/developer: + You are Codex, an agent based on GPT-6. You and the user share one workspace, and your job is to collabo...them until their intended goal is completely handled. [hash=efa50f074efa5803] + + # When to ask the user for permission + + Use your best judgement given task context for when you really need user permission, like a competent co...ork without ending the turn to clarify with the user. [hash=ca7367e658b51388] + + User authorization and preferences persist across turns. Do not request permission again when the user h... any guidelines provided in skills or external files. [hash=911f91c92c72882c] + + You MUST complete the work that is already authorized and necessary to make the proposed action concrete... in the session or implied from the task instruction. [hash=821281c2d48149b3] + + Do not use tools to send messages to others (e.g. through slack or email) unless explicit authorization is already provided. + + The user gets very frustrated when you stop and ask for confirmation or permission, so make sure to expl... commentary and final, after any permission question. [hash=7e6ab67a40648d1d] + + # Autonomy and persistence + + + An installed app's MCP tools are either provided to you already, or can be lazy-loaded through the `tool...re searchable by `tools_search` will be listed by it. [hash=4f9c0d35da3968a5] + Do not additionally call list_mcp_resources or list_mcp_resource_templates for apps. + + # Plugins + + A plugin is a local bundle of skills, MCP servers, and apps. + + ## How to use plugins + + - Skill naming: If a plugin contributes skills, those skill entries are prefixed with plugin_name: in the Skills list. + - MCP naming: Plugin-provided MCP tools keep standard MCP identifiers such as mcp__server__tool; use tool provenance to tell which plugin they come from. + - Trigger rules: If the user explicitly names a plugin, prefer capabilities associated with that plugin for that turn. + - Relationship to capabilities: Plugins are not invoked directly. Use their underlying skills, MCP tools, and app tools to help solve the task. + - Relevance: Determine what a plugin can help with from explicit user mention or from the plugin-associa..., MCP tools, and apps exposed elsewhere in this turn. [hash=620459a4e7b590f0] + - Missing/blocked: If the user requests a plugin that does not have relevant callable capabilities for t..., say so briefly and continue with the best fallback. [hash=746b0469a8f6cb3e] + +02:message/developer[2]: + [01] + Filesystem sandboxing defines which files can be read or written. `sandbox_mode` is `danger-full-access`...ll commands are permitted. Network access is enabled. [hash=c134061cebb94a1b] + Approval policy is currently never. Do not provide the `sandbox_permissions` for any reason, commands will be rejected. + + [02] # Collaboration Mode: Default + + You are now in Default mode. Any previous instructions for other modes (e.g. Plan mode) are no longer active. + + Your active mode changes only when new developer instructions with a different `...<...by themselves. Known mode names are Default and Plan. [hash=e75f6b12fc52d812] + + ## request_user_input availability + + Use the `request_user_input` tool only when it is listed in the available tools for this turn. + + In Default mode, strongly prefer making reasonable assumptions and executing the user's request rather than stopping to ask questions. + + Use the `request_user_input` tool only for optional questions where the answer would materially improve the quality of the work. + + If `request_user_input` returns no answers, continue with best judgment instead of asking again or treating the turn as blocked. + + Never use the `request_user_input` tool for permission requests or permission-related escalations. + + If explicit user input is required for another reason before progress can safely continue, do not use th... as a textual assistant message. [hash=8213052bde4a62b2] +03:message/developer: + You are `/root`, the primary agent in a team of agents collaborating to fulfill the user's goals. + + At the start of your turn, you are the active agent. + You can spawn sub-agents to handle subtasks, and those sub-agents can spawn their own sub-agents. + All agents in the team, including the agents that you can assign tasks to, are equally intelligent and capable, and have access to the same set of tools. + + You can use `spawn_agent` to create a new agent, `followup_task` to give an existing agent a new task an...message to a running agent without triggering a turn. [hash=7d40ff8b58977202] + `send_message` calls may be read by a human, so ensure they are legible. Always put proper spaces between words and/or numbers. + Child agents can also spawn their own sub-agents. + You can decide how much context you want to propagate to your sub-agents with the `fork_turns` parameter. + + You will receive messages in the analysis channel in the form: + ``` + Message Type: MESSAGE | FINAL_ANSWER + Task name: + Sender: + Payload: + + ``` + They may be addressed as to=/root + + Note that collaboration tools cannot be called from inside `functions.exec`. Call `spawn_agent`, `send_m...ed with a `tools` namespace in the developer message. [hash=2798139748f66a39] + + All agents share the same directory. In detail: + - All agents have access to the same container and filesystem as you. + - All agents use the same current working directory. + - As a result, edits made by one agent are immediately visible to all other agents. + + When calling `wait_agent`, prefer longer waits (minutes) to avoid busy polling. + + There are 4 available concurrency slots, meaning that up to 4 agents can be active at once, including you. + + Full-history forks (`fork_turns` omitted or `"all"`) inherit the parent model and reasoning effort and d...ne"` or a positive integer string. [hash=c7f52e90688299ce] +04:message/developer: + Any earlier instruction enabling proactive multi-agent delegation no longer applies. D...elegation, or parallel agent work. [hash=2b6e037067b4390c] +05:message/user: + + + + + + [hash=0592734c170299c1] + +06:message/user: + Check the Settings release. Read release/diagnostics.log, inspect release/settings.png, confirm the loca...ell me whether another agent is working on this task. [hash=aa221f7aaa101eb2] +-- request 2 (turn) -- +07:function_call/collaboration.list_agents:{} +08:function_call_output:{"agents":[{"agent_name":"/root","agent_status":"running"}]} +-- request 3 (turn) -- +09:custom_tool_call/exec:const [log, ping] = await Promise.all([ + tools.exec_command({ cmd: "cat release/diagnostics.log", login: false, max_output_tokens: 4000 }), + tools.mcp__rmcp__echo({ message: "settings-release-check" }), + ]); + text(`diagnostics:\\n${log.output}`); + text(`MCP: ${ping.structuredContent?.echo ?? "missing"}`); +10:custom_tool_call_output:Script completed + Wall time seconds + Output: + | diagnostics: + UI-42: expected Settings button label: Apply + diagnostic 001: rendering settings panel + diagnostic 002: rendering settings panel + diagnostic 003: rendering settings panel + diagnostic 004: rendering settings panel + diagnostic 005: rendering settings panel + diagnostic 006: rendering settings panel + diagnostic 007: rendering settings panel + diagnostic 008: rendering settings panel + diagnostic 009: rendering settings panel + diagnostic 010: rendering settings panel + diagnostic 011: rendering settings panel + diagnostic 012: rendering settings panel + diagnostic 013: rendering settings panel + diagnostic 014: rendering settings panel + + diagnostic 106: rendering settings panel + diagnostic 107: rendering settings panel + diagnostic 108: rendering settings panel + diagnostic 109: rendering settings panel + diagnostic 110: rendering settings panel + diagnostic 111: rendering settings panel + diagnostic 112: rendering settings panel + diagnostic 113: rendering settings panel + diagnostic 114: rendering settings panel + diagnostic 115: rendering settings panel + diagnostic 116: rendering settings panel + diagnostic 117: rendering settings panel + diagnostic 118: rendering settings panel + diagnostic 119: rendering settings panel + diagnostic 120: rendering settings panel + UI-42: observed Settings button label: Save | MCP: ECHOING: settings-release-check +-- request 4 (turn) -- +11:custom_tool_call/exec:image(await tools.view_image({ path: "release/settings.png", detail: "original" })); +12:custom_tool_call_output:Script completed + Wall time seconds + Output: + | +-- request 5 (turn) -- +13:custom_tool_call/exec:text(await tools.apply_patch(`*** Begin Patch + *** Update File: release/status.md + @@ + -Status: pending + +Status: blocked + +Reason: expected Apply; observed Save + +MCP: reachable + *** End Patch + `)); +14:custom_tool_call_output:Script completed + Wall time seconds + Output: + | {} +-- request 6 (turn) -- +15:custom_tool_call/exec:text((await tools.exec_command({ cmd: "cat release/status.md", login: false })).output); +16:custom_tool_call_output:Script completed + Wall time seconds + Output: + | Status: blocked + Reason: expected Apply; observed Save + MCP: reachable