Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5691,6 +5691,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2292,6 +2292,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
"enum": [
"pendingInit",
"running",
"interrupted",
"completed",
"errored",
"shutdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export type CollabAgentStatus = "pendingInit" | "running" | "completed" | "errored" | "shutdown" | "notFound";
export type CollabAgentStatus = "pendingInit" | "running" | "interrupted" | "completed" | "errored" | "shutdown" | "notFound";
68 changes: 68 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/thread_history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2417,6 +2417,74 @@ mod tests {
);
}

#[test]
fn reconstructs_interrupted_send_input_as_completed_collab_call() {
// `send_input(interrupt=true)` first stops the child's active turn, then redirects it with
// new input. The transient interrupted status should remain visible in agent state, but the
// collab tool call itself is still a successful redirect rather than a failed operation.
let sender = ThreadId::try_from("00000000-0000-0000-0000-000000000001")
.expect("valid sender thread id");
let receiver = ThreadId::try_from("00000000-0000-0000-0000-000000000002")
.expect("valid receiver thread id");
let events = vec![
EventMsg::UserMessage(UserMessageEvent {
message: "redirect".into(),
images: None,
text_elements: Vec::new(),
local_images: Vec::new(),
}),
EventMsg::CollabAgentInteractionBegin(
codex_protocol::protocol::CollabAgentInteractionBeginEvent {
call_id: "send-1".into(),
sender_thread_id: sender,
receiver_thread_id: receiver,
prompt: "new task".into(),
},
),
EventMsg::CollabAgentInteractionEnd(
codex_protocol::protocol::CollabAgentInteractionEndEvent {
call_id: "send-1".into(),
sender_thread_id: sender,
receiver_thread_id: receiver,
receiver_agent_nickname: None,
receiver_agent_role: None,
prompt: "new task".into(),
status: AgentStatus::Interrupted,
},
),
];

let items = events
.into_iter()
.map(RolloutItem::EventMsg)
.collect::<Vec<_>>();
let turns = build_turns_from_rollout_items(&items);
assert_eq!(turns.len(), 1);
assert_eq!(turns[0].items.len(), 2);
assert_eq!(
turns[0].items[1],
ThreadItem::CollabAgentToolCall {
id: "send-1".into(),
tool: CollabAgentTool::SendInput,
status: CollabAgentToolCallStatus::Completed,
sender_thread_id: sender.to_string(),
receiver_thread_ids: vec![receiver.to_string()],
prompt: Some("new task".into()),
model: None,
reasoning_effort: None,
agents_states: [(
receiver.to_string(),
CollabAgentState {
status: crate::protocol::v2::CollabAgentStatus::Interrupted,
message: None,
},
)]
.into_iter()
.collect(),
}
);
}

#[test]
fn rollback_failed_error_does_not_mark_turn_failed() {
let events = vec![
Expand Down
16 changes: 16 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4542,6 +4542,7 @@ pub enum CollabAgentToolCallStatus {
pub enum CollabAgentStatus {
PendingInit,
Running,
Interrupted,
Completed,
Errored,
Shutdown,
Expand All @@ -4567,6 +4568,10 @@ impl From<CoreAgentStatus> for CollabAgentState {
status: CollabAgentStatus::Running,
message: None,
},
CoreAgentStatus::Interrupted => Self {
status: CollabAgentStatus::Interrupted,
message: None,
},
CoreAgentStatus::Completed(message) => Self {
status: CollabAgentStatus::Completed,
message,
Expand Down Expand Up @@ -5886,6 +5891,17 @@ mod tests {
absolute_path("readable")
}

#[test]
fn collab_agent_state_maps_interrupted_status() {
assert_eq!(
CollabAgentState::from(CoreAgentStatus::Interrupted),
CollabAgentState {
status: CollabAgentStatus::Interrupted,
message: None,
}
);
}

#[test]
fn command_execution_request_approval_rejects_relative_additional_permission_paths() {
let err = serde_json::from_value::<CommandExecutionRequestApprovalParams>(json!({
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/core/src/agent/control_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ async fn on_event_updates_status_from_turn_aborted() {
reason: TurnAbortReason::Interrupted,
}));

let expected = AgentStatus::Errored("Interrupted".to_string());
let expected = AgentStatus::Interrupted;
assert_eq!(status, Some(expected));
}

Expand Down
12 changes: 10 additions & 2 deletions codex-rs/core/src/agent/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@ pub(crate) fn agent_status_from_event(msg: &EventMsg) -> Option<AgentStatus> {
match msg {
EventMsg::TurnStarted(_) => Some(AgentStatus::Running),
EventMsg::TurnComplete(ev) => Some(AgentStatus::Completed(ev.last_agent_message.clone())),
EventMsg::TurnAborted(ev) => Some(AgentStatus::Errored(format!("{:?}", ev.reason))),
EventMsg::TurnAborted(ev) => match ev.reason {
codex_protocol::protocol::TurnAbortReason::Interrupted => {
Some(AgentStatus::Interrupted)
}
_ => Some(AgentStatus::Errored(format!("{:?}", ev.reason))),
},
EventMsg::Error(ev) => Some(AgentStatus::Errored(ev.message.clone())),
EventMsg::ShutdownComplete => Some(AgentStatus::Shutdown),
_ => None,
}
}

pub(crate) fn is_final(status: &AgentStatus) -> bool {
!matches!(status, AgentStatus::PendingInit | AgentStatus::Running)
!matches!(
status,
AgentStatus::PendingInit | AgentStatus::Running | AgentStatus::Interrupted
)
}
2 changes: 2 additions & 0 deletions codex-rs/exec/src/event_processor_with_human_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1260,6 +1260,7 @@ fn format_collab_status(status: &AgentStatus) -> String {
match status {
AgentStatus::PendingInit => "pending init".to_string(),
AgentStatus::Running => "running".to_string(),
AgentStatus::Interrupted => "interrupted".to_string(),
AgentStatus::Completed(Some(message)) => {
let preview = truncate_preview(message.trim(), 120);
if preview.is_empty() {
Expand Down Expand Up @@ -1289,6 +1290,7 @@ fn style_for_agent_status(
match status {
AgentStatus::PendingInit | AgentStatus::Shutdown => processor.dimmed,
AgentStatus::Running => processor.cyan,
AgentStatus::Interrupted => processor.yellow,
AgentStatus::Completed(_) => processor.green,
AgentStatus::Errored(_) | AgentStatus::NotFound => processor.red,
}
Expand Down
4 changes: 4 additions & 0 deletions codex-rs/exec/src/event_processor_with_jsonl_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,10 @@ impl From<CoreAgentStatus> for CollabAgentState {
status: CollabAgentStatus::Running,
message: None,
},
CoreAgentStatus::Interrupted => Self {
status: CollabAgentStatus::Interrupted,
message: None,
},
CoreAgentStatus::Completed(message) => Self {
status: CollabAgentStatus::Completed,
message,
Expand Down
1 change: 1 addition & 0 deletions codex-rs/exec/src/exec_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ pub enum CollabTool {
pub enum CollabAgentStatus {
PendingInit,
Running,
Interrupted,
Completed,
Errored,
Shutdown,
Expand Down
2 changes: 2 additions & 0 deletions codex-rs/protocol/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1518,6 +1518,8 @@ pub enum AgentStatus {
PendingInit,
/// Agent is currently running.
Running,
/// Agent's current turn was interrupted and it may receive more input.
Interrupted,
/// Agent is done. Contains the final assistant message.
Completed(Option<String>),
/// Agent encountered an error.
Expand Down
22 changes: 22 additions & 0 deletions codex-rs/tui/src/multi_agents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -537,10 +537,13 @@ fn status_summary_line(status: &AgentStatus) -> Line<'static> {
status_summary_spans(status).into()
}

// Allow `.yellow()`
#[allow(clippy::disallowed_methods)]
fn status_summary_spans(status: &AgentStatus) -> Vec<Span<'static>> {
match status {
AgentStatus::PendingInit => vec![Span::from("Pending init").cyan()],
AgentStatus::Running => vec![Span::from("Running").cyan().bold()],
AgentStatus::Interrupted => vec![Span::from("Interrupted").yellow()],
AgentStatus::Completed(message) => {
let mut spans = vec![Span::from("Completed").green()];
if let Some(message) = message.as_ref() {
Expand Down Expand Up @@ -762,6 +765,25 @@ mod tests {
assert_eq!(title.spans[6].style.fg, Some(Color::Magenta));
}

#[test]
fn collab_resume_interrupted_snapshot() {
let sender_thread_id = ThreadId::from_string("00000000-0000-0000-0000-000000000001")
.expect("valid sender thread id");
let robie_id = ThreadId::from_string("00000000-0000-0000-0000-000000000002")
.expect("valid robie thread id");

let cell = resume_end(CollabResumeEndEvent {
call_id: "call-resume".to_string(),
sender_thread_id,
receiver_thread_id: robie_id,
receiver_agent_nickname: Some("Robie".to_string()),
receiver_agent_role: Some("explorer".to_string()),
status: AgentStatus::Interrupted,
});

assert_snapshot!("collab_resume_interrupted", cell_to_text(&cell));
}

fn cell_to_text(cell: &PlainHistoryCell) -> String {
cell.display_lines(200)
.iter()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
source: tui/src/multi_agents.rs
expression: cell_to_text(&cell)
---
• Resumed Robie [explorer]
└ Interrupted
Loading