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}