mirror of
https://github.com/openai/codex.git
synced 2026-11-05 08:28:29 +00:00
Add agents.interrupt_message for interruption markers (#19351)
## Why Agent interruptions currently always persist a model-visible interrupted-turn marker before emitting `TurnAborted`. That marker is useful by default because it gives the next model turn context about a deliberately interrupted task, but some deployments need to suppress that history injection entirely while still keeping the client-visible interruption event. ## What changed - Add `[agents] interrupt_message = false` to disable the model-visible interrupted-turn marker. - Resolve the setting into `Config::agent_interrupt_message_enabled`, defaulting to `true` so existing behavior is unchanged. - Apply the setting to both live interrupted turns and interrupted fork snapshots. - Keep emitting `TurnAborted` even when the history marker is disabled. - Regenerate `core/config.schema.json` for the new `agents.interrupt_message` field. ## Testing - `cargo test -p codex-core load_config_resolves_agent_interrupt_message -- --nocapture` - `cargo test -p codex-core disabled_interrupted_fork_snapshot_appends_only_interrupt_event -- --nocapture` - `cargo test -p codex-core multi_agent_v2_interrupted_marker_uses_developer_input_message -- --nocapture` - `cargo test -p codex-core multi_agent_v2_followup_task_can_disable_interrupted_marker -- --nocapture` - `cargo test -p codex-core multi_agent_v2_followup_task_interrupts_busy_child_without_losing_message -- --nocapture` - `cargo check -p codex-core`
This commit is contained in:
1 parent
deb4509302
commit
28742866c7
8 files changed
+287
-57
No files matched your search
@@ -3941,6 +3941,7 @@ async fn load_config_rejects_missing_agent_role_config_file() -> std::io::Result
|
||||
max_threads: None,
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
interrupt_message: None,
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -4856,6 +4857,29 @@ model = "gpt-5-mini"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn load_config_resolves_agent_interrupt_message() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let cfg = ConfigToml {
|
||||
agents: Some(AgentsToml {
|
||||
interrupt_message: Some(false),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides::default(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
assert!(!config.agent_interrupt_message_enabled);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn load_config_normalizes_agent_role_nickname_candidates() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
@@ -4864,6 +4888,7 @@ async fn load_config_normalizes_agent_role_nickname_candidates() -> std::io::Res
|
||||
max_threads: None,
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
interrupt_message: None,
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -4906,6 +4931,7 @@ async fn load_config_rejects_empty_agent_role_nickname_candidates() -> std::io::
|
||||
max_threads: None,
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
interrupt_message: None,
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -4942,6 +4968,7 @@ async fn load_config_rejects_duplicate_agent_role_nickname_candidates() -> std::
|
||||
max_threads: None,
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
interrupt_message: None,
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -4978,6 +5005,7 @@ async fn load_config_rejects_unsafe_agent_role_nickname_candidates() -> std::io:
|
||||
max_threads: None,
|
||||
max_depth: None,
|
||||
job_max_runtime_seconds: None,
|
||||
interrupt_message: None,
|
||||
roles: BTreeMap::from([(
|
||||
"researcher".to_string(),
|
||||
AgentRoleToml {
|
||||
@@ -5234,6 +5262,7 @@ async fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
|
||||
agent_roles: BTreeMap::new(),
|
||||
memories: MemoriesConfig::default(),
|
||||
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
|
||||
agent_interrupt_message_enabled: true,
|
||||
codex_home: fixture.codex_home(),
|
||||
sqlite_home: fixture.codex_home().to_path_buf(),
|
||||
log_dir: fixture.codex_home().join("log").to_path_buf(),
|
||||
@@ -5431,6 +5460,7 @@ async fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
|
||||
agent_roles: BTreeMap::new(),
|
||||
memories: MemoriesConfig::default(),
|
||||
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
|
||||
agent_interrupt_message_enabled: true,
|
||||
codex_home: fixture.codex_home(),
|
||||
sqlite_home: fixture.codex_home().to_path_buf(),
|
||||
log_dir: fixture.codex_home().join("log").to_path_buf(),
|
||||
@@ -5582,6 +5612,7 @@ async fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
|
||||
agent_roles: BTreeMap::new(),
|
||||
memories: MemoriesConfig::default(),
|
||||
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
|
||||
agent_interrupt_message_enabled: true,
|
||||
codex_home: fixture.codex_home(),
|
||||
sqlite_home: fixture.codex_home().to_path_buf(),
|
||||
log_dir: fixture.codex_home().join("log").to_path_buf(),
|
||||
@@ -5718,6 +5749,7 @@ async fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
|
||||
agent_roles: BTreeMap::new(),
|
||||
memories: MemoriesConfig::default(),
|
||||
agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS,
|
||||
agent_interrupt_message_enabled: true,
|
||||
codex_home: fixture.codex_home(),
|
||||
sqlite_home: fixture.codex_home().to_path_buf(),
|
||||
log_dir: fixture.codex_home().join("log").to_path_buf(),
|
||||
|
||||
@@ -432,6 +432,9 @@ pub struct Config {
|
||||
/// Maximum runtime in seconds for agent job workers before they are failed.
|
||||
pub agent_job_max_runtime_seconds: Option<u64>,
|
||||
|
||||
/// Whether to record a model-visible message when an agent turn is interrupted.
|
||||
pub agent_interrupt_message_enabled: bool,
|
||||
|
||||
/// Maximum nesting depth allowed for spawned agent threads.
|
||||
pub agent_max_depth: i32,
|
||||
|
||||
@@ -2002,6 +2005,11 @@ impl Config {
|
||||
"agents.job_max_runtime_seconds must fit within a 64-bit signed integer",
|
||||
));
|
||||
}
|
||||
let agent_interrupt_message_enabled = cfg
|
||||
.agents
|
||||
.as_ref()
|
||||
.and_then(|agents| agents.interrupt_message)
|
||||
.unwrap_or(true);
|
||||
let background_terminal_max_timeout = cfg
|
||||
.background_terminal_max_timeout
|
||||
.unwrap_or(DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS)
|
||||
@@ -2366,6 +2374,7 @@ impl Config {
|
||||
agent_roles,
|
||||
memories: cfg.memories.unwrap_or_default().into(),
|
||||
agent_job_max_runtime_seconds,
|
||||
agent_interrupt_message_enabled,
|
||||
codex_home,
|
||||
sqlite_home,
|
||||
log_dir,
|
||||
|
||||
Reference in new issue
Block a user