use crate::product::agent::protocol::EventMsg;
use crate::product::agent::protocol::RolloutItem;
use crate::product::protocol::models::TranscriptItem;
#[inline]
pub(crate) fn is_persisted_response_item(item: &RolloutItem) -> bool {
match item {
RolloutItem::TranscriptItem(item) => should_persist_response_item(item),
RolloutItem::EventMsg(ev) => should_persist_event_msg(ev),
RolloutItem::GhostSnapshot(_) => true,
RolloutItem::Compacted(_)
| RolloutItem::InputSlimmingStoredInput(_)
| RolloutItem::TurnContext(_)
| RolloutItem::Workflow(_)
| RolloutItem::SessionMeta(_) => true,
}
}
#[inline]
pub(crate) fn should_persist_response_item(item: &TranscriptItem) -> bool {
match item {
TranscriptItem::Message { .. }
| TranscriptItem::Reasoning { .. }
| TranscriptItem::HostedActivity { .. }
| TranscriptItem::ToolCall { .. }
| TranscriptItem::ToolResult { .. }
| TranscriptItem::Unknown { .. } => true,
}
}
#[inline]
pub(crate) fn should_persist_event_msg(ev: &EventMsg) -> bool {
match ev {
EventMsg::UserMessage(_)
| EventMsg::AgentMessage(_)
| EventMsg::AgentReasoning(_)
| EventMsg::AgentReasoningRawContent(_)
| EventMsg::TokenCount(_)
| EventMsg::ContextCompacted(_)
| EventMsg::EnteredReviewMode(_)
| EventMsg::ExitedReviewMode(_)
| EventMsg::ThreadRolledBack(_)
| EventMsg::ThreadGoalUpdated(_)
| EventMsg::ThreadGoalCleared(_)
| EventMsg::UndoCompleted(_)
| EventMsg::TurnAborted(_) => true,
EventMsg::ItemCompleted(event) => {
matches!(
event.item,
crate::product::protocol::items::TurnItem::Plan(_)
| crate::product::protocol::items::TurnItem::ContextCompaction(_)
)
}
EventMsg::Error(_)
| EventMsg::Warning(_)
| EventMsg::TurnStarted(_)
| EventMsg::TurnComplete(_)
| EventMsg::InputSlimming(_)
| EventMsg::BuddyReaction(_)
| EventMsg::AgentMessageDelta(_)
| EventMsg::AgentReasoningDelta(_)
| EventMsg::AgentReasoningRawContentDelta(_)
| EventMsg::AgentReasoningSectionBreak(_)
| EventMsg::RawTranscriptItem(_)
| EventMsg::SessionConfigured(_)
| EventMsg::ThreadNameUpdated(_)
| EventMsg::ThreadGoalSnapshot(_)
| EventMsg::ThreadGoalReplaceConfirmationRequired(_)
| EventMsg::McpToolCallBegin(_)
| EventMsg::McpToolCallEnd(_)
| EventMsg::WebSearchBegin(_)
| EventMsg::WebSearchEnd(_)
| EventMsg::ExecCommandBegin(_)
| EventMsg::TerminalInteraction(_)
| EventMsg::ExecCommandOutputDelta(_)
| EventMsg::ExecCommandEnd(_)
| EventMsg::ExecApprovalRequest(_)
| EventMsg::RequestUserInput(_)
| EventMsg::DynamicToolCallRequest(_)
| EventMsg::WorkflowUpdate(_)
| EventMsg::ElicitationRequest(_)
| EventMsg::ApplyPatchApprovalRequest(_)
| EventMsg::BackgroundEvent(_)
| EventMsg::StreamError(_)
| EventMsg::PatchApplyBegin(_)
| EventMsg::PatchApplyEnd(_)
| EventMsg::TurnDiff(_)
| EventMsg::GetHistoryEntryResponse(_)
| EventMsg::UndoStarted(_)
| EventMsg::McpListToolsResponse(_)
| EventMsg::McpStartupUpdate(_)
| EventMsg::McpStartupComplete(_)
| EventMsg::ListCustomPromptsResponse(_)
| EventMsg::ListSkillsResponse(_)
| EventMsg::PlanUpdate(_)
| EventMsg::AgentJobStatus(_)
| EventMsg::ShutdownComplete
| EventMsg::ViewImageToolCall(_)
| EventMsg::DeprecationNotice(_)
| EventMsg::ItemStarted(_)
| EventMsg::AgentMessageContentDelta(_)
| EventMsg::PlanDelta(_)
| EventMsg::ReasoningContentDelta(_)
| EventMsg::ReasoningRawContentDelta(_)
| EventMsg::SkillsUpdateAvailable => false,
}
}
#[cfg(test)]
mod tests {
use super::is_persisted_response_item;
use super::should_persist_event_msg;
use crate::product::agent::protocol::EventMsg;
use crate::product::agent::protocol::ItemCompletedEvent;
use crate::product::agent::protocol::RolloutItem;
use crate::product::protocol::ThreadId;
use crate::product::protocol::items::ContextCompactionItem;
use crate::product::protocol::items::PlanItem;
use crate::product::protocol::items::TurnItem;
use crate::product::protocol::protocol::InputSlimmingStoredInputItem;
use crate::product::protocol::protocol::InputSlimmingStoredInputMetadata;
use pretty_assertions::assert_eq;
#[test]
fn persists_context_compaction_item_completed_events() {
let event = EventMsg::ItemCompleted(ItemCompletedEvent {
thread_id: ThreadId::new(),
turn_id: "turn-1".to_string(),
item: TurnItem::ContextCompaction(ContextCompactionItem::new()),
});
assert_eq!(should_persist_event_msg(&event), true);
}
#[test]
fn still_persists_plan_item_completed_events() {
let event = EventMsg::ItemCompleted(ItemCompletedEvent {
thread_id: ThreadId::new(),
turn_id: "turn-1".to_string(),
item: TurnItem::Plan(PlanItem {
id: "plan-1".to_string(),
text: "plan".to_string(),
}),
});
assert_eq!(should_persist_event_msg(&event), true);
}
#[test]
fn persists_input_slimming_stored_input_sidecars() {
let item = RolloutItem::InputSlimmingStoredInput(InputSlimmingStoredInputItem {
hash: "abc123".to_string(),
original: "original".to_string(),
metadata: InputSlimmingStoredInputMetadata {
scope: crate::product::agent::protocol::InputSlimmingScope::HistoricalToolOutputs,
strategy: "plain_text_head_tail".to_string(),
tool_name: "shell".to_string(),
original_tokens: 10,
compressed_tokens: 3,
created_turn_id: "turn-1".to_string(),
},
});
assert_eq!(is_persisted_response_item(&item), true);
assert!(!matches!(item, RolloutItem::TranscriptItem(_)));
}
}