mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Make MultiAgentV2 interruption markers assistant-authored (#19124)
## Why `MultiAgentV2` follow-up messages are delivered to agents as assistant-authored `InterAgentCommunication` envelopes. When `followup_task` used `interrupt: true`, the interrupted-turn guidance was still persisted as a contextual user message, so model-visible history made a system-generated interruption boundary look user-authored. This keeps interruption guidance consistent with the rest of the v2 inter-agent message stream while preserving the legacy marker shape for non-v2 sessions. ## What changed - Make `interrupted_turn_history_marker` feature-aware. - Record the interrupted-turn marker as an assistant `OutputText` message when `Feature::MultiAgentV2` is enabled. - Keep the existing user contextual fragment for non-v2 sessions. - Apply the same feature-aware marker to interrupted fork snapshots. - Add coverage for the live `followup_task` interrupt path and the helper-level v2 marker shape. ## Testing - `cargo test -p codex-core multi_agent_v2_followup_task_interrupts_busy_child_without_losing_message -- --nocapture` - `cargo test -p codex-core multi_agent_v2_interrupted_marker_uses_assistant_output_message -- --nocapture` - `cargo test -p codex-core interrupted_fork_snapshot -- --nocapture`
This commit is contained in:
@@ -3,6 +3,7 @@ use crate::CodexThread;
|
||||
use crate::ThreadManager;
|
||||
use crate::config::AgentRoleConfig;
|
||||
use crate::config::DEFAULT_AGENT_MAX_DEPTH;
|
||||
use crate::context::TurnAborted;
|
||||
use crate::function_tool::FunctionCallError;
|
||||
use crate::session::tests::make_session_and_context;
|
||||
use crate::session_prefix::format_subagent_notification_message;
|
||||
@@ -1566,6 +1567,52 @@ async fn multi_agent_v2_followup_task_interrupts_busy_child_without_losing_messa
|
||||
}));
|
||||
|
||||
wait_for_turn_aborted(&thread, &interrupted_turn_id, TurnAbortReason::Interrupted).await;
|
||||
let history_items = thread
|
||||
.codex
|
||||
.session
|
||||
.clone_history()
|
||||
.await
|
||||
.raw_items()
|
||||
.to_vec();
|
||||
assert!(
|
||||
history_items.iter().any(|item| matches!(
|
||||
item,
|
||||
ResponseItem::Message { role, content, .. }
|
||||
if role == "developer"
|
||||
&& content.iter().any(|content_item| matches!(
|
||||
content_item,
|
||||
ContentItem::InputText { text }
|
||||
if text.contains(TurnAborted::INTERRUPTED_DEVELOPER_GUIDANCE)
|
||||
))
|
||||
)),
|
||||
"v2 interrupted-turn marker should be recorded as a developer input message"
|
||||
);
|
||||
assert!(
|
||||
!history_items.iter().any(|item| matches!(
|
||||
item,
|
||||
ResponseItem::Message { role, content, .. }
|
||||
if role == "user"
|
||||
&& content.iter().any(|content_item| matches!(
|
||||
content_item,
|
||||
ContentItem::InputText { text } | ContentItem::OutputText { text }
|
||||
if text.contains(TurnAborted::INTERRUPTED_GUIDANCE)
|
||||
))
|
||||
)),
|
||||
"v2 interrupted-turn marker should not be recorded as a user message"
|
||||
);
|
||||
assert!(
|
||||
!history_items.iter().any(|item| matches!(
|
||||
item,
|
||||
ResponseItem::Message { role, content, .. }
|
||||
if role == "assistant"
|
||||
&& content.iter().any(|content_item| matches!(
|
||||
content_item,
|
||||
ContentItem::InputText { text } | ContentItem::OutputText { text }
|
||||
if text.contains(TurnAborted::INTERRUPTED_DEVELOPER_GUIDANCE)
|
||||
))
|
||||
)),
|
||||
"v2 interrupted-turn marker should not be recorded as an assistant message"
|
||||
);
|
||||
wait_for_redirected_envelope_in_history(
|
||||
&thread,
|
||||
&InterAgentCommunication::new(
|
||||
|
||||
Reference in New Issue
Block a user