1use crate::agent::Agent;
2use crate::config::ResolvedModelConfig;
3use crate::run_config::RunConfig;
4use crate::types::{AgentResult, AgentStatus};
5
6#[derive(Clone)]
7pub struct RunResult {
8 agent_name: String,
9 result: AgentResult,
10 resolved: ResolvedModelConfig,
11 resume_context: Option<RunResumeContext>,
12}
13
14impl RunResult {
15 pub fn new(
16 agent_name: impl Into<String>,
17 result: AgentResult,
18 resolved: ResolvedModelConfig,
19 ) -> Self {
20 Self {
21 agent_name: agent_name.into(),
22 result,
23 resolved,
24 resume_context: None,
25 }
26 }
27
28 pub fn agent_name(&self) -> &str {
29 &self.agent_name
30 }
31
32 pub fn status(&self) -> AgentStatus {
33 self.result.status
34 }
35
36 pub fn final_output(&self) -> Option<&str> {
37 self.result.final_answer.as_deref()
38 }
39
40 pub fn result(&self) -> &AgentResult {
41 &self.result
42 }
43
44 pub fn resolved_model(&self) -> &ResolvedModelConfig {
45 &self.resolved
46 }
47
48 pub fn into_state(self) -> Result<RunState, String> {
49 RunState::from_result(self)
50 }
51
52 pub(crate) fn with_resume_context(mut self, context: RunResumeContext) -> Self {
53 self.resume_context = Some(context);
54 self
55 }
56
57 pub(crate) fn resume_context(&self) -> Option<&RunResumeContext> {
58 self.resume_context.as_ref()
59 }
60}
61
62#[derive(Clone)]
63pub struct RunState {
64 result: RunResult,
65 approved_interruption_ids: Vec<String>,
66}
67
68impl RunState {
69 pub fn from_result(result: RunResult) -> Result<Self, String> {
70 if result.status() != AgentStatus::WaitUser {
71 return Err("only interrupted runs can be converted into RunState".to_string());
72 }
73 Ok(Self {
74 result,
75 approved_interruption_ids: Vec::new(),
76 })
77 }
78
79 pub fn result(&self) -> &RunResult {
80 &self.result
81 }
82
83 pub fn approve(&mut self, interruption_id: &str) -> Result<(), String> {
84 if !self
85 .pending_approval_ids()
86 .iter()
87 .any(|id| id == interruption_id)
88 {
89 return Err(format!("unknown approval interruption: {interruption_id}"));
90 }
91 if !self
92 .approved_interruption_ids
93 .iter()
94 .any(|id| id == interruption_id)
95 {
96 self.approved_interruption_ids
97 .push(interruption_id.to_string());
98 }
99 Ok(())
100 }
101
102 pub fn approved_interruption_ids(&self) -> &[String] {
103 &self.approved_interruption_ids
104 }
105
106 pub fn pending_approval_ids(&self) -> Vec<String> {
107 self.result
108 .result()
109 .cycles
110 .iter()
111 .flat_map(|cycle| cycle.tool_results.iter())
112 .filter_map(|tool_result| {
113 tool_result
114 .metadata
115 .get("approval_interruption_id")
116 .and_then(serde_json::Value::as_str)
117 .map(str::to_string)
118 })
119 .collect()
120 }
121
122 pub(crate) fn into_inner(self) -> (RunResult, Vec<String>) {
123 (self.result, self.approved_interruption_ids)
124 }
125}
126
127#[derive(Clone)]
128pub(crate) struct RunResumeContext {
129 pub agent: Agent,
130 pub input: crate::runner::NormalizedInput,
131 pub config: RunConfig,
132}