Include start times in terminal turn events (#32263)

## What changed

- Add an optional `started_at` Unix timestamp to `TurnCompleteEvent` and
  `TurnAbortedEvent`.
- Populate the timestamp from turn timing state when a turn completes or is
  aborted.
- Preserve the timestamp when synthesizing interrupted fork history and when
  importing external sessions.

## Testing

- Verify that interrupting a turn emits a `TurnAbortedEvent` with a start
  timestamp.

GitOrigin-RevId: 75b3911b95c060e5fc1843a5e293d8fe47377186
This commit is contained in:
Owen Lin
2026-07-10 20:25:03 +00:00
committed by copyberry
parent b66c25c6a3
commit bca577d69a
20 changed files with 144 additions and 7 deletions
@@ -97,6 +97,7 @@ fn completed_user_turn_rollout(
codex_protocol::protocol::TurnCompleteEvent {
turn_id,
last_agent_message: None,
started_at: None,
completed_at: None,
duration_ms: None,
time_to_first_token_ms: None,
@@ -265,6 +266,7 @@ async fn record_initial_history_resumed_hydrates_previous_turn_settings_from_lif
codex_protocol::protocol::TurnCompleteEvent {
turn_id,
last_agent_message: None,
started_at: None,
completed_at: None,
duration_ms: None,
time_to_first_token_ms: None,
@@ -339,6 +341,7 @@ async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_com
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: first_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -373,6 +376,7 @@ async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_com
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: rolled_back_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -452,6 +456,7 @@ async fn reconstruct_history_rollback_keeps_history_and_metadata_in_sync_for_inc
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: first_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -549,6 +554,7 @@ async fn reconstruct_history_rollback_skips_non_user_turns_for_history_and_metad
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: first_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -579,6 +585,7 @@ async fn reconstruct_history_rollback_skips_non_user_turns_for_history_and_metad
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: second_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -598,6 +605,7 @@ async fn reconstruct_history_rollback_skips_non_user_turns_for_history_and_metad
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: standalone_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -675,6 +683,7 @@ async fn reconstruct_history_rollback_counts_inter_agent_assistant_turns() {
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: first_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -696,6 +705,7 @@ async fn reconstruct_history_rollback_counts_inter_agent_assistant_turns() {
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: assistant_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -768,6 +778,7 @@ async fn reconstruct_history_rollback_clears_history_and_metadata_when_exceeding
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: only_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -821,6 +832,7 @@ async fn record_initial_history_resumed_rollback_skips_only_user_turns() {
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: user_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -840,6 +852,7 @@ async fn record_initial_history_resumed_rollback_skips_only_user_turns() {
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: standalone_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -897,6 +910,7 @@ async fn record_initial_history_resumed_rollback_drops_incomplete_user_turn_comp
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: previous_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -1226,6 +1240,7 @@ async fn reconstruct_history_legacy_compaction_without_replacement_history_clear
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: current_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -1306,6 +1321,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: previous_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -1418,6 +1434,7 @@ async fn record_initial_history_resumed_aborted_turn_without_id_clears_active_tu
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: previous_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -1446,6 +1463,7 @@ async fn record_initial_history_resumed_aborted_turn_without_id_clears_active_tu
RolloutItem::EventMsg(EventMsg::TurnAborted(
codex_protocol::protocol::TurnAbortedEvent {
turn_id: None,
started_at: None,
reason: TurnAbortReason::Interrupted,
completed_at: None,
duration_ms: None,
@@ -1540,6 +1558,7 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: previous_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -1568,6 +1587,7 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo
RolloutItem::EventMsg(EventMsg::TurnAborted(
codex_protocol::protocol::TurnAbortedEvent {
turn_id: Some(unmatched_abort_turn_id),
started_at: None,
reason: TurnAbortReason::Interrupted,
completed_at: None,
duration_ms: None,
@@ -1577,6 +1597,7 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: current_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -1667,6 +1688,7 @@ async fn record_initial_history_resumed_trailing_incomplete_turn_compaction_clea
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: previous_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -1836,6 +1858,7 @@ async fn record_initial_history_resumed_replaced_incomplete_compacted_turn_clear
RolloutItem::EventMsg(EventMsg::TurnComplete(
codex_protocol::protocol::TurnCompleteEvent {
turn_id: previous_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
+11
View File
@@ -486,6 +486,7 @@ async fn interrupting_regular_turn_waiting_on_startup_prewarm_emits_turn_aborted
let EventMsg::TurnAborted(TurnAbortedEvent {
turn_id,
reason,
started_at,
completed_at,
duration_ms,
}) = second.msg
@@ -494,6 +495,7 @@ async fn interrupting_regular_turn_waiting_on_startup_prewarm_emits_turn_aborted
};
assert_eq!(turn_id, Some(tc.sub_id.clone()));
assert_eq!(reason, TurnAbortReason::Interrupted);
assert!(started_at.is_some());
assert!(completed_at.is_some());
assert!(duration_ms.is_some());
}
@@ -3134,6 +3136,7 @@ async fn record_initial_history_forked_hydrates_previous_turn_settings() {
codex_protocol::protocol::TurnCompleteEvent {
turn_id,
last_agent_message: None,
started_at: None,
completed_at: None,
duration_ms: None,
time_to_first_token_ms: None,
@@ -3333,6 +3336,7 @@ async fn thread_rollback_recomputes_previous_turn_settings_and_reference_context
RolloutItem::ResponseItem(turn_one_assistant.clone()),
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
turn_id: first_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -3362,6 +3366,7 @@ async fn thread_rollback_recomputes_previous_turn_settings_and_reference_context
RolloutItem::ResponseItem(turn_two_assistant),
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
turn_id: rolled_back_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -3451,6 +3456,7 @@ async fn thread_rollback_restores_cleared_reference_context_item_after_compactio
RolloutItem::ResponseItem(assistant_message("turn 1 assistant")),
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
turn_id: first_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -3475,6 +3481,7 @@ async fn thread_rollback_restores_cleared_reference_context_item_after_compactio
}),
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
turn_id: compact_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -3507,6 +3514,7 @@ async fn thread_rollback_restores_cleared_reference_context_item_after_compactio
RolloutItem::ResponseItem(assistant_message("turn 2 assistant")),
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
turn_id: rolled_back_turn_id,
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -3580,6 +3588,7 @@ async fn thread_rollback_persists_marker_and_replays_cumulatively() {
RolloutItem::ResponseItem(assistant_message("turn 1 assistant")),
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
turn_id: "turn-1".to_string(),
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -3607,6 +3616,7 @@ async fn thread_rollback_persists_marker_and_replays_cumulatively() {
RolloutItem::ResponseItem(assistant_message("turn 2 assistant")),
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
turn_id: "turn-2".to_string(),
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,
@@ -3634,6 +3644,7 @@ async fn thread_rollback_persists_marker_and_replays_cumulatively() {
RolloutItem::ResponseItem(assistant_message("turn 3 assistant")),
RolloutItem::EventMsg(EventMsg::TurnComplete(TurnCompleteEvent {
turn_id: "turn-3".to_string(),
started_at: None,
last_agent_message: None,
completed_at: None,
duration_ms: None,