mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Include terminal errors in turn completion events (#32280)
## What changed - Add an optional `error` payload to `TurnCompleteEvent` and omit it when a turn completes without an error. - Preserve the full terminal `ErrorEvent` through the turn lifecycle so the completion event includes the same error details emitted separately by `EventMsg::Error`. ## Testing - Extend the stream-error integration test to verify that `TurnCompleteEvent` contains the emitted error and that the next turn can still proceed. GitOrigin-RevId: 8d32942a44132763076130102d8b89ab356d59d0
This commit is contained in:
@@ -1762,6 +1762,7 @@ mod tests {
|
||||
turn_id: "turn-1".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -1821,6 +1822,7 @@ mod tests {
|
||||
turn_id: "turn-1".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -1924,6 +1926,7 @@ mod tests {
|
||||
turn_id: turn_id.to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -1976,6 +1979,7 @@ mod tests {
|
||||
turn_id: turn_id.to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -2029,6 +2033,7 @@ mod tests {
|
||||
turn_id: turn_id.to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -2093,6 +2098,7 @@ mod tests {
|
||||
turn_id: turn_id.to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -2162,6 +2168,7 @@ mod tests {
|
||||
turn_id: turn_id.to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -2241,6 +2248,7 @@ mod tests {
|
||||
turn_id: "turn-image".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -2594,6 +2602,7 @@ mod tests {
|
||||
turn_id: "turn-a".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3163,6 +3172,7 @@ mod tests {
|
||||
turn_id: "turn-a".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3206,6 +3216,7 @@ mod tests {
|
||||
turn_id: "turn-b".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3263,6 +3274,7 @@ mod tests {
|
||||
turn_id: "turn-a".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3306,6 +3318,7 @@ mod tests {
|
||||
turn_id: "turn-b".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3495,6 +3508,7 @@ mod tests {
|
||||
turn_id: "turn-a".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3518,6 +3532,7 @@ mod tests {
|
||||
turn_id: "turn-a".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3531,6 +3546,7 @@ mod tests {
|
||||
turn_id: "turn-b".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3570,6 +3586,7 @@ mod tests {
|
||||
turn_id: "turn-a".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3637,6 +3654,7 @@ mod tests {
|
||||
turn_id: "turn-compact".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3900,6 +3918,7 @@ mod tests {
|
||||
turn_id: "turn-a".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3966,6 +3985,7 @@ mod tests {
|
||||
turn_id: "turn-a".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -4022,6 +4042,7 @@ mod tests {
|
||||
turn_id: "turn-a".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -4104,6 +4125,7 @@ mod tests {
|
||||
turn_id: "turn-a".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -4270,6 +4292,7 @@ mod tests {
|
||||
turn_id: "turn-a".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: Some(20),
|
||||
duration_ms: Some(123),
|
||||
time_to_first_token_ms: None,
|
||||
@@ -4351,6 +4374,7 @@ mod tests {
|
||||
turn_id: "turn-a".into(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: Some(20),
|
||||
duration_ms: Some(123),
|
||||
time_to_first_token_ms: None,
|
||||
|
||||
@@ -2193,6 +2193,7 @@ mod tests {
|
||||
turn_id: turn_id.to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: Some(TEST_TURN_COMPLETED_AT),
|
||||
duration_ms: Some(TEST_TURN_DURATION_MS),
|
||||
time_to_first_token_ms: None,
|
||||
|
||||
@@ -161,6 +161,7 @@ async fn mark_thread_completed(thread: &CodexThread) {
|
||||
turn_id: turn.sub_id.clone(),
|
||||
started_at: None,
|
||||
last_agent_message: Some("done".to_string()),
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
|
||||
@@ -351,6 +351,7 @@ async fn on_event_updates_status_from_task_complete() {
|
||||
turn_id: "turn-1".to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: Some("done".to_string()),
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -2029,6 +2030,7 @@ async fn multi_agent_v2_completion_ignores_dead_direct_parent() {
|
||||
turn_id: tester_turn.sub_id.clone(),
|
||||
started_at: None,
|
||||
last_agent_message: Some("done".to_string()),
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -2117,6 +2119,7 @@ async fn multi_agent_v2_completion_queues_message_for_direct_parent() {
|
||||
turn_id: tester_turn.sub_id.clone(),
|
||||
started_at: None,
|
||||
last_agent_message: Some("done".to_string()),
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
|
||||
@@ -1179,6 +1179,7 @@ mod tests {
|
||||
turn_id: turn_id.to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: last_agent_message.map(str::to_string),
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms,
|
||||
|
||||
@@ -2234,6 +2234,7 @@ async fn guardian_reused_trunk_ignores_stale_prior_turn_completion() -> anyhow::
|
||||
"{\"risk_level\":\"high\",\"user_authorization\":\"low\",\"outcome\":\"deny\",\"rationale\":\"stale guardian rationale\"}"
|
||||
.to_string(),
|
||||
),
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: Some(1),
|
||||
|
||||
@@ -1776,7 +1776,7 @@ impl Session {
|
||||
.terminal_error
|
||||
.lock()
|
||||
.await
|
||||
.replace(error.message.clone());
|
||||
.replace(error.clone());
|
||||
}
|
||||
self.services
|
||||
.rollout_thread_trace
|
||||
@@ -1834,7 +1834,7 @@ impl Session {
|
||||
|
||||
let status = match turn_context.terminal_error.lock().await.take() {
|
||||
Some(error) => {
|
||||
let status = AgentStatus::Errored(error);
|
||||
let status = AgentStatus::Errored(error.message);
|
||||
self.agent_status.send_replace(status.clone());
|
||||
status
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ fn completed_user_turn_rollout(
|
||||
codex_protocol::protocol::TurnCompleteEvent {
|
||||
turn_id,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
started_at: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
@@ -266,6 +267,7 @@ async fn record_initial_history_resumed_hydrates_previous_turn_settings_from_lif
|
||||
codex_protocol::protocol::TurnCompleteEvent {
|
||||
turn_id,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
started_at: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
@@ -343,6 +345,7 @@ async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_com
|
||||
turn_id: first_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -378,6 +381,7 @@ async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_com
|
||||
turn_id: rolled_back_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -458,6 +462,7 @@ async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_inc
|
||||
turn_id: first_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -556,6 +561,7 @@ async fn reconstruct_history_rollback_skips_non_user_turns_for_history_and_metad
|
||||
turn_id: first_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -587,6 +593,7 @@ async fn reconstruct_history_rollback_skips_non_user_turns_for_history_and_metad
|
||||
turn_id: second_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -607,6 +614,7 @@ async fn reconstruct_history_rollback_skips_non_user_turns_for_history_and_metad
|
||||
turn_id: standalone_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -685,6 +693,7 @@ async fn reconstruct_history_rollback_counts_inter_agent_assistant_turns() {
|
||||
turn_id: first_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -707,6 +716,7 @@ async fn reconstruct_history_rollback_counts_inter_agent_assistant_turns() {
|
||||
turn_id: assistant_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -780,6 +790,7 @@ async fn reconstruct_history_rollback_clears_history_and_metadata_when_exceeding
|
||||
turn_id: only_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -834,6 +845,7 @@ async fn record_initial_history_resumed_rollback_skips_only_user_turns() {
|
||||
turn_id: user_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -854,6 +866,7 @@ async fn record_initial_history_resumed_rollback_skips_only_user_turns() {
|
||||
turn_id: standalone_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -912,6 +925,7 @@ async fn record_initial_history_resumed_rollback_drops_incomplete_user_turn_comp
|
||||
turn_id: previous_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -1242,6 +1256,7 @@ async fn reconstruct_history_legacy_compaction_without_replacement_history_clear
|
||||
turn_id: current_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -1323,6 +1338,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis
|
||||
turn_id: previous_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -1436,6 +1452,7 @@ async fn record_initial_history_resumed_aborted_turn_without_id_clears_active_tu
|
||||
turn_id: previous_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -1560,6 +1577,7 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo
|
||||
turn_id: previous_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -1599,6 +1617,7 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo
|
||||
turn_id: current_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -1690,6 +1709,7 @@ async fn record_initial_history_resumed_trailing_incomplete_turn_compaction_clea
|
||||
turn_id: previous_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -1860,6 +1880,7 @@ async fn record_initial_history_resumed_replaced_incomplete_compacted_turn_clear
|
||||
turn_id: previous_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
|
||||
@@ -3136,6 +3136,7 @@ async fn record_initial_history_forked_hydrates_previous_turn_settings() {
|
||||
codex_protocol::protocol::TurnCompleteEvent {
|
||||
turn_id,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
started_at: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
@@ -3338,6 +3339,7 @@ async fn thread_rollback_recomputes_previous_turn_settings_and_reference_context
|
||||
turn_id: first_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3368,6 +3370,7 @@ async fn thread_rollback_recomputes_previous_turn_settings_and_reference_context
|
||||
turn_id: rolled_back_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3458,6 +3461,7 @@ async fn thread_rollback_restores_cleared_reference_context_item_after_compactio
|
||||
turn_id: first_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3483,6 +3487,7 @@ async fn thread_rollback_restores_cleared_reference_context_item_after_compactio
|
||||
turn_id: compact_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3516,6 +3521,7 @@ async fn thread_rollback_restores_cleared_reference_context_item_after_compactio
|
||||
turn_id: rolled_back_turn_id,
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3590,6 +3596,7 @@ async fn thread_rollback_persists_marker_and_replays_cumulatively() {
|
||||
turn_id: "turn-1".to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3618,6 +3625,7 @@ async fn thread_rollback_persists_marker_and_replays_cumulatively() {
|
||||
turn_id: "turn-2".to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -3646,6 +3654,7 @@ async fn thread_rollback_persists_marker_and_replays_cumulatively() {
|
||||
turn_id: "turn-3".to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -9762,6 +9771,7 @@ async fn task_finish_emits_turn_item_lifecycle_for_leftover_pending_user_input()
|
||||
EventMsg::TurnComplete(TurnCompleteEvent {
|
||||
turn_id,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
time_to_first_token_ms: None,
|
||||
..
|
||||
}) if turn_id == tc.sub_id
|
||||
|
||||
@@ -9,6 +9,7 @@ use codex_protocol::SessionId;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::models::AdditionalPermissionProfile;
|
||||
use codex_protocol::openai_models::ModelInfo;
|
||||
use codex_protocol::protocol::ErrorEvent;
|
||||
use codex_protocol::protocol::MultiAgentVersion;
|
||||
use codex_protocol::protocol::ThreadHistoryMode;
|
||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||
@@ -140,7 +141,7 @@ pub struct TurnContext {
|
||||
pub(crate) extension_data: Arc<codex_extension_api::ExtensionData>,
|
||||
pub(crate) turn_skills: TurnSkillsContext,
|
||||
pub(crate) turn_timing_state: Arc<TurnTimingState>,
|
||||
pub(crate) terminal_error: Arc<Mutex<Option<String>>>,
|
||||
pub(crate) terminal_error: Arc<Mutex<Option<ErrorEvent>>>,
|
||||
pub(crate) server_model_warning_emitted: AtomicBool,
|
||||
pub(crate) model_verification_emitted: AtomicBool,
|
||||
}
|
||||
|
||||
@@ -766,11 +766,13 @@ impl Session {
|
||||
.turn_timing_state
|
||||
.time_to_first_token_ms()
|
||||
.await;
|
||||
let error = turn_context.terminal_error.lock().await.clone();
|
||||
self.emit_turn_stop_lifecycle(turn_context.extension_data.as_ref())
|
||||
.await;
|
||||
EventMsg::TurnComplete(TurnCompleteEvent {
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
last_agent_message,
|
||||
error,
|
||||
started_at,
|
||||
completed_at,
|
||||
duration_ms,
|
||||
|
||||
@@ -84,6 +84,7 @@ fn turn_completed(turn_id: &str) -> RolloutItem {
|
||||
turn_id: turn_id.to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
|
||||
@@ -1560,6 +1560,7 @@ async fn multi_agent_v2_list_agents_returns_completed_status_without_encrypted_s
|
||||
turn_id: child_turn.sub_id.clone(),
|
||||
started_at: None,
|
||||
last_agent_message: Some("done".to_string()),
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -2010,6 +2011,7 @@ async fn multi_agent_v2_followup_task_completion_notifies_parent_on_every_turn()
|
||||
turn_id: first_turn.sub_id.clone(),
|
||||
started_at: None,
|
||||
last_agent_message: Some("first done".to_string()),
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
@@ -2052,6 +2054,7 @@ async fn multi_agent_v2_followup_task_completion_notifies_parent_on_every_turn()
|
||||
turn_id: second_turn.sub_id.clone(),
|
||||
started_at: None,
|
||||
last_agent_message: Some("second done".to_string()),
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
|
||||
@@ -74,6 +74,7 @@ fn resume_history(
|
||||
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
|
||||
turn_id,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
started_at: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
|
||||
@@ -107,9 +107,18 @@ async fn continue_after_stream_error() {
|
||||
.unwrap();
|
||||
|
||||
// Expect an Error followed by TurnComplete so the session is released.
|
||||
wait_for_event(&codex, |ev| matches!(ev, EventMsg::Error(_))).await;
|
||||
let EventMsg::Error(error) =
|
||||
wait_for_event(&codex, |ev| matches!(ev, EventMsg::Error(_))).await
|
||||
else {
|
||||
unreachable!("predicate guarantees an error event");
|
||||
};
|
||||
|
||||
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
|
||||
let EventMsg::TurnComplete(completed) =
|
||||
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await
|
||||
else {
|
||||
unreachable!("predicate guarantees a turn complete event");
|
||||
};
|
||||
assert_eq!(completed.error, Some(error));
|
||||
|
||||
// 2) Second turn: now send another prompt that should succeed using the
|
||||
// mock server SSE stream. If the agent failed to clear the running task on
|
||||
|
||||
@@ -191,6 +191,7 @@ fn turn_complete_item(
|
||||
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
|
||||
turn_id,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
started_at,
|
||||
completed_at,
|
||||
duration_ms: None,
|
||||
|
||||
@@ -1887,7 +1887,7 @@ pub struct ExitedReviewModeEvent {
|
||||
|
||||
// Individual event payload types matching each `EventMsg` variant.
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, JsonSchema, TS)]
|
||||
pub struct ErrorEvent {
|
||||
pub message: String,
|
||||
#[serde(default)]
|
||||
@@ -1955,6 +1955,10 @@ pub struct ContextCompactedEvent;
|
||||
pub struct TurnCompleteEvent {
|
||||
pub turn_id: String,
|
||||
pub last_agent_message: Option<String>,
|
||||
/// Terminal error details when the turn completed unsuccessfully.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub error: Option<ErrorEvent>,
|
||||
/// Unix timestamp (in seconds) when the turn started.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(type = "number | null", optional)]
|
||||
|
||||
@@ -53,6 +53,7 @@ fn turn_complete(turn_id: &str) -> RolloutItem {
|
||||
turn_id: turn_id.to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
|
||||
@@ -540,6 +540,7 @@ mod tests {
|
||||
turn_id: "turn-1".to_string(),
|
||||
started_at: None,
|
||||
last_agent_message: None,
|
||||
error: None,
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
time_to_first_token_ms: None,
|
||||
|
||||
Reference in New Issue
Block a user