Skip to main content

agent_sdk_core/application/
run.rs

1//! Run request and result records used by both simple helpers and explicit advanced
2//! callers. Use these DTOs at host boundaries when constructing input and reading
3//! terminal output. Constructors are data-only and do not contact providers.
4//!
5use crate::{
6    domain::{AgentId, RunId, SessionId, SourceRef, TurnId},
7    output::OutputContract,
8    typed_output_ports::{TypedOutputDeserializer, TypedOutputModel},
9    validated_output::{
10        StructuredOutputResult, TypedOutputError, TypedResultPublicationRecord, ValidatedOutput,
11        ValidationReportRecord,
12    },
13};
14
15#[derive(Clone, Debug, Eq, PartialEq)]
16/// Holds run request application-layer state or configuration.
17/// Use it with the documented coordinator methods; run, journal, event, provider, or port effects are called out on those methods rather than on construction.
18pub struct RunRequest {
19    /// Run identifier used for lineage, filtering, replay, and dedupe.
20    pub run_id: RunId,
21    /// Optional host-provided session identifier for grouping related turns.
22    /// When present, the runtime copies it into journal and event envelopes.
23    pub session_id: Option<SessionId>,
24    /// Optional host-provided turn identifier for the user message or loop turn
25    /// this run answers. When absent, the runtime may derive a deterministic
26    /// turn id for traceability.
27    pub turn_id: Option<TurnId>,
28    /// Agent identifier used for lineage, filtering, and ownership checks.
29    pub agent_id: AgentId,
30    /// Source label or ref for this item; it is metadata and does not fetch
31    /// content by itself.
32    pub source: SourceRef,
33    /// Input used by this record or request.
34    pub input: String,
35    /// Optional output contract value.
36    /// When absent, callers should use the documented default or skip that optional behavior.
37    pub output_contract: Option<OutputContract>,
38}
39
40impl RunRequest {
41    /// Builds the text value.
42    /// This is data construction and performs no I/O, journal append, event publication, or
43    /// process work.
44    pub fn text(
45        run_id: RunId,
46        agent_id: AgentId,
47        source: SourceRef,
48        input: impl Into<String>,
49    ) -> Self {
50        Self {
51            run_id,
52            session_id: None,
53            turn_id: None,
54            agent_id,
55            source,
56            input: input.into(),
57            output_contract: None,
58        }
59    }
60
61    /// Returns this value with its output contract setting replaced.
62    /// The method follows builder-style data construction and does not
63    /// execute external work.
64    pub fn with_output_contract(mut self, output_contract: OutputContract) -> Self {
65        self.output_contract = Some(output_contract);
66        self
67    }
68
69    /// Returns this value with a session id attached for trace grouping.
70    /// This is data construction only; hosts still own conversation storage.
71    pub fn with_session_id(mut self, session_id: SessionId) -> Self {
72        self.session_id = Some(session_id);
73        self
74    }
75
76    /// Returns this value with a turn id attached for question-scoped tracing.
77    /// This is data construction only and does not start or resume a run.
78    pub fn with_turn_id(mut self, turn_id: TurnId) -> Self {
79        self.turn_id = Some(turn_id);
80        self
81    }
82
83    /// Returns this value with session and turn lineage attached together.
84    /// This is a thin convenience over the explicit builder methods.
85    pub fn with_session_turn(mut self, session_id: SessionId, turn_id: TurnId) -> Self {
86        self.session_id = Some(session_id);
87        self.turn_id = Some(turn_id);
88        self
89    }
90
91    /// Builds the typed text value with the documented defaults.
92    /// This uses only local coordinator state and performs no hidden host work.
93    pub fn typed_text<T: TypedOutputModel>(
94        run_id: RunId,
95        agent_id: AgentId,
96        source: SourceRef,
97        input: impl Into<String>,
98    ) -> Self {
99        Self::text(run_id, agent_id, source, input)
100            .with_output_contract(OutputContract::for_type::<T>())
101    }
102}
103
104#[derive(Clone, Debug, Eq, PartialEq)]
105/// Holds run result application-layer state or configuration.
106/// Use it with the documented coordinator methods; run, journal, event, provider, or port effects are called out on those methods rather than on construction.
107pub struct RunResult {
108    /// Run identifier used for lineage, filtering, replay, and dedupe.
109    pub run_id: RunId,
110    /// Finite status for this record or lifecycle stage.
111    pub status: RunStatus,
112    /// Output used by this record or request.
113    pub output: String,
114    /// Optional structured output value.
115    /// When absent, callers should use the documented default or skip that optional behavior.
116    pub structured_output: Option<StructuredOutputArtifacts>,
117}
118
119impl RunResult {
120    /// Creates a new application::run value with explicit
121    /// caller-provided inputs. This constructor is data-only and
122    /// performs no I/O or external side effects.
123    pub fn new(run_id: RunId, status: RunStatus, output: impl Into<String>) -> Self {
124        Self {
125            run_id,
126            status,
127            output: output.into(),
128            structured_output: None,
129        }
130    }
131
132    /// Returns this value with its structured output setting replaced.
133    /// The method follows builder-style data construction and does not
134    /// execute external work.
135    pub fn with_structured_output(mut self, structured_output: StructuredOutputArtifacts) -> Self {
136        self.structured_output = Some(structured_output);
137        self
138    }
139
140    /// Returns this value with its structured output if present setting
141    /// replaced. The method follows builder-style data construction and
142    /// does not execute external work.
143    pub fn with_structured_output_if_present(
144        mut self,
145        structured_output: Option<StructuredOutputArtifacts>,
146    ) -> Self {
147        self.structured_output = structured_output;
148        self
149    }
150
151    /// Structured output.
152    /// This decodes structured output from the completed run result and does not rerun
153    /// validation or call a provider.
154    pub fn structured_output<T, D>(
155        &self,
156        deserializer: &D,
157    ) -> Result<StructuredOutputResult<T>, TypedOutputError>
158    where
159        D: TypedOutputDeserializer<T>,
160    {
161        let artifacts = self.structured_output.as_ref().ok_or_else(|| {
162            TypedOutputError::MissingValidatedOutput {
163                run_id: self.run_id.clone(),
164            }
165        })?;
166        StructuredOutputResult::from_publication(
167            &artifacts.validated_output,
168            &artifacts.typed_result_publication,
169            deserializer,
170        )
171    }
172}
173
174#[derive(Clone, Debug, Eq, PartialEq)]
175/// Holds structured output artifacts application-layer state or configuration.
176/// Use it with the documented coordinator methods; run, journal, event, provider, or port effects are called out on those methods rather than on construction.
177pub struct StructuredOutputArtifacts {
178    /// Validation policy applied before output is accepted as typed data.
179    /// It controls validator selection, bounds, failure visibility, and local validation
180    /// behavior.
181    pub validation_reports: Vec<ValidationReportRecord>,
182    /// Validated output used by this record or request.
183    pub validated_output: ValidatedOutput,
184    /// Typed result publication used by this record or request.
185    pub typed_result_publication: TypedResultPublicationRecord,
186}
187
188#[derive(Clone, Debug, Eq, PartialEq)]
189/// Enumerates the finite run status cases.
190/// Serialized names are part of the SDK contract; update fixtures when variants change.
191pub enum RunStatus {
192    /// Use this variant when the contract needs to represent pending; selecting it has no side effect by itself.
193    Pending,
194    /// Use this variant when the contract needs to represent running; selecting it has no side effect by itself.
195    Running,
196    /// Use this variant when the contract needs to represent cancelling; selecting it has no side effect by itself.
197    Cancelling,
198    /// Use this variant when the contract needs to represent completed; selecting it has no side effect by itself.
199    Completed,
200    /// Use this variant when the contract needs to represent failed; selecting it has no side effect by itself.
201    Failed,
202    /// Use this variant when the contract needs to represent cancelled; selecting it has no side effect by itself.
203    Cancelled,
204    /// Use this variant when the contract needs to represent repair needed; selecting it has no side effect by itself.
205    RepairNeeded,
206}
207
208impl RunStatus {
209    /// Reports whether this value is terminal. The check is pure and
210    /// does not mutate SDK or host state.
211    pub fn is_terminal(&self) -> bool {
212        matches!(self, Self::Completed | Self::Failed | Self::Cancelled)
213    }
214
215    /// Returns this value as terminal str. The accessor is side-effect
216    /// free and keeps ownership with the caller.
217    pub fn as_terminal_str(&self) -> Option<&'static str> {
218        match self {
219            Self::Completed => Some("completed"),
220            Self::Failed => Some("failed"),
221            Self::Cancelled => Some("cancelled"),
222            _ => None,
223        }
224    }
225
226    /// Constructs this value from terminal str. Use it when adapting
227    /// canonical SDK records without introducing a second behavior
228    /// path.
229    pub fn from_terminal_str(value: &str) -> Option<Self> {
230        match value {
231            "completed" => Some(Self::Completed),
232            "failed" => Some(Self::Failed),
233            "cancelled" => Some(Self::Cancelled),
234            _ => None,
235        }
236    }
237}