diff --git a/codex-rs/rollout/src/list.rs b/codex-rs/rollout/src/list.rs index 8ba63a713ea8..de14a7b75cbd 100644 --- a/codex-rs/rollout/src/list.rs +++ b/codex-rs/rollout/src/list.rs @@ -1130,8 +1130,8 @@ async fn read_head_summary(path: &Path, head_limit: usize) -> io::Result { // Not included in `head`; skip. } - RolloutItem::EventMsg(ev) => { - if let EventMsg::UserMessage(user) = ev { + RolloutItem::EventMsg(ev) => match ev { + EventMsg::UserMessage(user) => { summary.saw_user_event = true; if summary.first_user_message.is_none() { let message = strip_user_message_prefix(user.message.as_str()).to_string(); @@ -1140,7 +1140,17 @@ async fn read_head_summary(path: &Path, head_limit: usize) -> io::Result { + let objective = event.goal.objective.trim(); + if !objective.is_empty() { + summary.saw_user_event = true; + if summary.first_user_message.is_none() { + summary.first_user_message = Some(objective.to_string()); + } + } + } + _ => {} + }, } if summary.saw_session_meta && summary.saw_user_event { diff --git a/codex-rs/rollout/src/policy.rs b/codex-rs/rollout/src/policy.rs index 558c3fef9887..75f15ac07179 100644 --- a/codex-rs/rollout/src/policy.rs +++ b/codex-rs/rollout/src/policy.rs @@ -98,6 +98,7 @@ fn event_msg_persistence_mode(ev: &EventMsg) -> Option { | EventMsg::AgentReasoningRawContent(_) | EventMsg::PatchApplyEnd(_) | EventMsg::TokenCount(_) + | EventMsg::ThreadGoalUpdated(_) | EventMsg::ContextCompacted(_) | EventMsg::EnteredReviewMode(_) | EventMsg::ExitedReviewMode(_) @@ -140,7 +141,6 @@ fn event_msg_persistence_mode(ev: &EventMsg) -> Option { | EventMsg::AgentReasoningSectionBreak(_) | EventMsg::RawResponseItem(_) | EventMsg::SessionConfigured(_) - | EventMsg::ThreadGoalUpdated(_) | EventMsg::McpToolCallBegin(_) | EventMsg::ExecCommandBegin(_) | EventMsg::TerminalInteraction(_) diff --git a/codex-rs/state/src/extract.rs b/codex-rs/state/src/extract.rs index 723c5084eb35..92136d9376bf 100644 --- a/codex-rs/state/src/extract.rs +++ b/codex-rs/state/src/extract.rs @@ -33,7 +33,9 @@ pub fn apply_rollout_item( pub fn rollout_item_affects_thread_metadata(item: &RolloutItem) -> bool { match item { RolloutItem::SessionMeta(_) | RolloutItem::TurnContext(_) => true, - RolloutItem::EventMsg(EventMsg::TokenCount(_) | EventMsg::UserMessage(_)) => true, + RolloutItem::EventMsg( + EventMsg::TokenCount(_) | EventMsg::UserMessage(_) | EventMsg::ThreadGoalUpdated(_), + ) => true, RolloutItem::EventMsg(_) | RolloutItem::ResponseItem(_) | RolloutItem::Compacted(_) => { false } @@ -96,6 +98,14 @@ fn apply_event_msg(metadata: &mut ThreadMetadata, event: &EventMsg) { } } } + EventMsg::ThreadGoalUpdated(event) => { + if metadata.first_user_message.is_none() { + let objective = event.goal.objective.trim(); + if !objective.is_empty() { + metadata.first_user_message = Some(objective.to_string()); + } + } + } _ => {} } } @@ -136,6 +146,7 @@ pub(crate) fn enum_to_string(value: &T) -> String { #[cfg(test)] mod tests { use super::apply_rollout_item; + use super::rollout_item_affects_thread_metadata; use crate::model::ThreadMetadata; use chrono::DateTime; use chrono::Utc; @@ -151,6 +162,9 @@ mod tests { use codex_protocol::protocol::SessionMeta; use codex_protocol::protocol::SessionMetaLine; use codex_protocol::protocol::SessionSource; + use codex_protocol::protocol::ThreadGoal; + use codex_protocol::protocol::ThreadGoalStatus; + use codex_protocol::protocol::ThreadGoalUpdatedEvent; use codex_protocol::protocol::TurnContextItem; use codex_protocol::protocol::USER_MESSAGE_BEGIN; use codex_protocol::protocol::UserMessageEvent; @@ -231,6 +245,34 @@ mod tests { assert_eq!(metadata.title, ""); } + #[test] + fn event_msg_thread_goal_sets_first_user_message_preview() { + let mut metadata = metadata_for_test(); + let item = RolloutItem::EventMsg(EventMsg::ThreadGoalUpdated(ThreadGoalUpdatedEvent { + thread_id: metadata.id, + turn_id: None, + goal: ThreadGoal { + thread_id: metadata.id, + objective: "optimize the benchmark".to_string(), + status: ThreadGoalStatus::Active, + token_budget: None, + tokens_used: 0, + time_used_seconds: 0, + created_at: 1, + updated_at: 1, + }, + })); + + assert!(rollout_item_affects_thread_metadata(&item)); + apply_rollout_item(&mut metadata, &item, "test-provider"); + + assert_eq!( + metadata.first_user_message.as_deref(), + Some("optimize the benchmark") + ); + assert_eq!(metadata.title, ""); + } + #[test] fn turn_context_does_not_override_session_cwd() { let mut metadata = metadata_for_test();