Skip to main content

objectiveai_sdk/agent/
agent.rs

1//! Core Agent types — unified enum dispatching to per-upstream implementations.
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6// ── Pre-validation types (no computed ID) ──────────────────────────
7
8/// The base inline configuration for an Agent (without computed ID or metadata).
9///
10/// This is an untagged enum that dispatches to the per-upstream AgentBase.
11/// Deserialization tries each variant in order until one matches.
12#[derive(
13    Clone,
14    Debug,
15    PartialEq,
16    Serialize,
17    Deserialize,
18    JsonSchema,
19    arbitrary::Arbitrary,
20)]
21#[serde(untagged)]
22#[schemars(rename = "agent.InlineAgentBase")]
23pub enum InlineAgentBase {
24    #[schemars(title = "Openrouter")]
25    Openrouter(super::openrouter::AgentBase),
26    #[schemars(title = "ClaudeAgentSdk")]
27    ClaudeAgentSdk(super::claude_agent_sdk::AgentBase),
28    #[schemars(title = "CodexSdk")]
29    CodexSdk(super::codex_sdk::AgentBase),
30    #[schemars(title = "Mock")]
31    Mock(super::mock::AgentBase),
32    #[schemars(title = "Script")]
33    Script(super::script::AgentBase),
34}
35
36impl InlineAgentBase {
37    pub fn as_ref(&self) -> InlineAgentRef<'_> {
38        match self {
39            InlineAgentBase::Openrouter(b) => InlineAgentRef::Openrouter(b),
40            InlineAgentBase::ClaudeAgentSdk(b) => {
41                InlineAgentRef::ClaudeAgentSdk(b)
42            }
43            InlineAgentBase::CodexSdk(b) => InlineAgentRef::CodexSdk(b),
44            InlineAgentBase::Mock(b) => InlineAgentRef::Mock(b),
45            InlineAgentBase::Script(b) => InlineAgentRef::Script(b),
46        }
47    }
48
49    pub fn model(&self) -> &str {
50        self.as_ref().model()
51    }
52
53    pub fn upstream(&self) -> super::Upstream {
54        self.as_ref().upstream()
55    }
56
57    pub fn output_mode(&self) -> super::OutputMode {
58        self.as_ref().output_mode()
59    }
60
61    pub fn mcp_servers(&self) -> Option<&super::McpServers> {
62        self.as_ref().mcp_servers()
63    }
64
65    pub fn laboratories(&self) -> Option<&super::Laboratories> {
66        self.as_ref().laboratories()
67    }
68
69    pub fn client_objectiveai_mcp(
70        &self,
71    ) -> Option<&super::ClientObjectiveaiMcp> {
72        self.as_ref().client_objectiveai_mcp()
73    }
74
75    pub fn prepare(&mut self) {
76        match self {
77            InlineAgentBase::Openrouter(b) => b.prepare(),
78            InlineAgentBase::ClaudeAgentSdk(b) => b.prepare(),
79            InlineAgentBase::CodexSdk(b) => b.prepare(),
80            InlineAgentBase::Mock(b) => b.prepare(),
81            InlineAgentBase::Script(b) => b.prepare(),
82        }
83    }
84
85    pub fn validate(&self) -> Result<(), String> {
86        match self {
87            InlineAgentBase::Openrouter(b) => b.validate(),
88            InlineAgentBase::ClaudeAgentSdk(b) => b.validate(),
89            InlineAgentBase::CodexSdk(b) => b.validate(),
90            InlineAgentBase::Mock(b) => b.validate(),
91            InlineAgentBase::Script(b) => b.validate(),
92        }
93    }
94
95    pub fn id(&self) -> String {
96        match self {
97            InlineAgentBase::Openrouter(b) => b.id(),
98            InlineAgentBase::ClaudeAgentSdk(b) => b.id(),
99            InlineAgentBase::CodexSdk(b) => b.id(),
100            InlineAgentBase::Mock(b) => b.id(),
101            InlineAgentBase::Script(b) => b.id(),
102        }
103    }
104
105    /// Validates and converts to an [`InlineAgent`] with computed ID.
106    pub fn convert(self) -> Result<InlineAgent, String> {
107        match self {
108            InlineAgentBase::Openrouter(b) => {
109                Ok(InlineAgent::Openrouter(b.try_into()?))
110            }
111            InlineAgentBase::ClaudeAgentSdk(b) => {
112                Ok(InlineAgent::ClaudeAgentSdk(b.try_into()?))
113            }
114            InlineAgentBase::CodexSdk(b) => {
115                Ok(InlineAgent::CodexSdk(b.try_into()?))
116            }
117            InlineAgentBase::Mock(b) => Ok(InlineAgent::Mock(b.try_into()?)),
118            InlineAgentBase::Script(b) => {
119                Ok(InlineAgent::Script(b.try_into()?))
120            }
121        }
122    }
123}
124
125/// A remote agent base definition with metadata.
126///
127/// Like [`InlineAgentBase`] but includes a description field for remote storage.
128#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
129#[schemars(rename = "agent.RemoteAgentBase")]
130pub struct RemoteAgentBase {
131    pub description: String,
132    #[serde(flatten)]
133    #[schemars(schema_with = "crate::flatten_schema::<InlineAgentBase>")]
134    pub inner: InlineAgentBase,
135}
136
137impl RemoteAgentBase {
138    pub fn as_ref(&self) -> InlineAgentRef<'_> {
139        self.inner.as_ref()
140    }
141
142    pub fn model(&self) -> &str {
143        self.inner.model()
144    }
145
146    pub fn upstream(&self) -> super::Upstream {
147        self.inner.upstream()
148    }
149
150    pub fn output_mode(&self) -> super::OutputMode {
151        self.inner.output_mode()
152    }
153
154    pub fn mcp_servers(&self) -> Option<&super::McpServers> {
155        self.inner.mcp_servers()
156    }
157
158    pub fn laboratories(&self) -> Option<&super::Laboratories> {
159        self.inner.laboratories()
160    }
161
162    pub fn client_objectiveai_mcp(
163        &self,
164    ) -> Option<&super::ClientObjectiveaiMcp> {
165        self.inner.client_objectiveai_mcp()
166    }
167
168    pub fn prepare(&mut self) {
169        self.inner.prepare()
170    }
171
172    pub fn validate(&self) -> Result<(), String> {
173        self.inner.validate()
174    }
175
176    pub fn id(&self) -> String {
177        self.inner.id()
178    }
179
180    /// Validates and converts to a [`RemoteAgent`] with computed ID.
181    pub fn convert(self) -> Result<RemoteAgent, String> {
182        Ok(RemoteAgent {
183            description: self.description,
184            inner: self.inner.convert()?,
185        })
186    }
187}
188
189/// An Agent base definition, either remote (with metadata) or inline.
190#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
191#[serde(untagged)]
192#[schemars(rename = "agent.AgentBase")]
193pub enum AgentBase {
194    #[schemars(title = "Remote")]
195    Remote(RemoteAgentBase),
196    #[schemars(title = "Inline")]
197    Inline(InlineAgentBase),
198}
199
200impl AgentBase {
201    pub fn as_ref(&self) -> InlineAgentRef<'_> {
202        match self {
203            AgentBase::Remote(r) => r.as_ref(),
204            AgentBase::Inline(i) => i.as_ref(),
205        }
206    }
207
208    pub fn model(&self) -> &str {
209        match self {
210            AgentBase::Remote(r) => r.model(),
211            AgentBase::Inline(i) => i.model(),
212        }
213    }
214
215    pub fn upstream(&self) -> super::Upstream {
216        match self {
217            AgentBase::Remote(r) => r.upstream(),
218            AgentBase::Inline(i) => i.upstream(),
219        }
220    }
221
222    pub fn output_mode(&self) -> super::OutputMode {
223        match self {
224            AgentBase::Remote(r) => r.output_mode(),
225            AgentBase::Inline(i) => i.output_mode(),
226        }
227    }
228
229    pub fn mcp_servers(&self) -> Option<&super::McpServers> {
230        match self {
231            AgentBase::Remote(r) => r.mcp_servers(),
232            AgentBase::Inline(i) => i.mcp_servers(),
233        }
234    }
235
236    pub fn laboratories(&self) -> Option<&super::Laboratories> {
237        match self {
238            AgentBase::Remote(r) => r.laboratories(),
239            AgentBase::Inline(i) => i.laboratories(),
240        }
241    }
242
243    pub fn client_objectiveai_mcp(
244        &self,
245    ) -> Option<&super::ClientObjectiveaiMcp> {
246        match self {
247            AgentBase::Remote(r) => r.client_objectiveai_mcp(),
248            AgentBase::Inline(i) => i.client_objectiveai_mcp(),
249        }
250    }
251
252    pub fn prepare(&mut self) {
253        match self {
254            AgentBase::Remote(r) => r.prepare(),
255            AgentBase::Inline(i) => i.prepare(),
256        }
257    }
258
259    pub fn validate(&self) -> Result<(), String> {
260        match self {
261            AgentBase::Remote(r) => r.validate(),
262            AgentBase::Inline(i) => i.validate(),
263        }
264    }
265
266    pub fn id(&self) -> String {
267        match self {
268            AgentBase::Remote(r) => r.id(),
269            AgentBase::Inline(i) => i.id(),
270        }
271    }
272
273    /// Validates and converts to an [`Agent`] with computed ID.
274    pub fn convert(self) -> Result<Agent, String> {
275        match self {
276            AgentBase::Remote(r) => Ok(Agent::Remote(r.convert()?)),
277            AgentBase::Inline(i) => Ok(Agent::Inline(i.convert()?)),
278        }
279    }
280}
281
282// ── Ref types ──────────────────────────────────────────────────────
283
284/// A borrowed reference into an [`InlineAgentBase`] variant.
285#[derive(Clone, Copy, Debug)]
286pub enum InlineAgentRef<'a> {
287    Openrouter(&'a super::openrouter::AgentBase),
288    ClaudeAgentSdk(&'a super::claude_agent_sdk::AgentBase),
289    CodexSdk(&'a super::codex_sdk::AgentBase),
290    Mock(&'a super::mock::AgentBase),
291    Script(&'a super::script::AgentBase),
292}
293
294impl<'a> InlineAgentRef<'a> {
295    pub fn to_owned(self) -> InlineAgentBase {
296        match self {
297            InlineAgentRef::Openrouter(b) => {
298                InlineAgentBase::Openrouter(b.clone())
299            }
300            InlineAgentRef::ClaudeAgentSdk(b) => {
301                InlineAgentBase::ClaudeAgentSdk(b.clone())
302            }
303            InlineAgentRef::CodexSdk(b) => InlineAgentBase::CodexSdk(b.clone()),
304            InlineAgentRef::Mock(b) => InlineAgentBase::Mock(b.clone()),
305            InlineAgentRef::Script(b) => InlineAgentBase::Script(b.clone()),
306        }
307    }
308
309    pub fn model(&self) -> &'a str {
310        match self {
311            InlineAgentRef::Openrouter(b) => &b.model,
312            InlineAgentRef::ClaudeAgentSdk(b) => &b.model,
313            InlineAgentRef::CodexSdk(b) => &b.model,
314            InlineAgentRef::Mock(_) => super::mock::AgentBase::model(),
315            InlineAgentRef::Script(_) => super::script::AgentBase::model(),
316        }
317    }
318
319    pub fn upstream(&self) -> super::Upstream {
320        match self {
321            InlineAgentRef::Openrouter(_) => super::Upstream::Openrouter,
322            InlineAgentRef::ClaudeAgentSdk(_) => {
323                super::Upstream::ClaudeAgentSdk
324            }
325            InlineAgentRef::CodexSdk(_) => super::Upstream::CodexSdk,
326            InlineAgentRef::Mock(_) => super::Upstream::Mock,
327            InlineAgentRef::Script(_) => super::Upstream::Script,
328        }
329    }
330
331    pub fn output_mode(&self) -> super::OutputMode {
332        match self {
333            InlineAgentRef::Openrouter(b) => b.output_mode.into(),
334            InlineAgentRef::ClaudeAgentSdk(b) => b.output_mode.into(),
335            InlineAgentRef::CodexSdk(b) => b.output_mode.into(),
336            InlineAgentRef::Mock(b) => b.output_mode.into(),
337            InlineAgentRef::Script(b) => b.output_mode.into(),
338        }
339    }
340
341    pub fn mcp_servers(&self) -> Option<&'a super::McpServers> {
342        match self {
343            InlineAgentRef::Openrouter(b) => b.mcp_servers.as_ref(),
344            InlineAgentRef::ClaudeAgentSdk(b) => b.mcp_servers.as_ref(),
345            InlineAgentRef::CodexSdk(b) => b.mcp_servers.as_ref(),
346            InlineAgentRef::Mock(b) => b.mcp_servers.as_ref(),
347            InlineAgentRef::Script(b) => b.mcp_servers.as_ref(),
348        }
349    }
350
351    pub fn laboratories(&self) -> Option<&'a super::Laboratories> {
352        match self {
353            InlineAgentRef::Openrouter(b) => b.laboratories.as_ref(),
354            InlineAgentRef::ClaudeAgentSdk(b) => b.laboratories.as_ref(),
355            InlineAgentRef::CodexSdk(b) => b.laboratories.as_ref(),
356            InlineAgentRef::Mock(b) => b.laboratories.as_ref(),
357            InlineAgentRef::Script(b) => b.laboratories.as_ref(),
358        }
359    }
360
361    pub fn client_objectiveai_mcp(
362        &self,
363    ) -> Option<&'a super::ClientObjectiveaiMcp> {
364        match self {
365            InlineAgentRef::Openrouter(b) => b.client_objectiveai_mcp.as_ref(),
366            InlineAgentRef::ClaudeAgentSdk(b) => {
367                b.client_objectiveai_mcp.as_ref()
368            }
369            InlineAgentRef::CodexSdk(b) => b.client_objectiveai_mcp.as_ref(),
370            InlineAgentRef::Mock(b) => b.client_objectiveai_mcp.as_ref(),
371            InlineAgentRef::Script(b) => b.client_objectiveai_mcp.as_ref(),
372        }
373    }
374
375    pub fn top_logprobs(&self) -> Option<u64> {
376        match self {
377            InlineAgentRef::Openrouter(b) => b.top_logprobs,
378            InlineAgentRef::ClaudeAgentSdk(_) => None,
379            InlineAgentRef::CodexSdk(_) => None,
380            InlineAgentRef::Mock(b) => b.top_logprobs,
381            InlineAgentRef::Script(_) => None,
382        }
383    }
384
385    pub fn merged_messages(
386        &self,
387        messages: Vec<super::completions::message::Message>,
388    ) -> Vec<super::completions::message::Message> {
389        match self {
390            InlineAgentRef::Openrouter(b) => b.merged_messages(messages),
391            InlineAgentRef::ClaudeAgentSdk(b) => b.merged_messages(messages),
392            InlineAgentRef::CodexSdk(b) => b.merged_messages(messages),
393            InlineAgentRef::Mock(b) => b.merged_messages(messages),
394            InlineAgentRef::Script(b) => b.merged_messages(messages),
395        }
396    }
397}
398
399// ── Post-validation types (with computed ID) ───────────────────────
400
401/// A validated inline Agent with its computed content-addressed ID.
402///
403/// This is an untagged enum that dispatches to the per-upstream Agent.
404#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
405#[serde(untagged)]
406#[schemars(rename = "agent.InlineAgent")]
407pub enum InlineAgent {
408    #[schemars(title = "Openrouter")]
409    Openrouter(super::openrouter::Agent),
410    #[schemars(title = "ClaudeAgentSdk")]
411    ClaudeAgentSdk(super::claude_agent_sdk::Agent),
412    #[schemars(title = "CodexSdk")]
413    CodexSdk(super::codex_sdk::Agent),
414    #[schemars(title = "Mock")]
415    Mock(super::mock::Agent),
416    #[schemars(title = "Script")]
417    Script(super::script::Agent),
418}
419
420impl InlineAgent {
421    pub fn id(&self) -> &str {
422        match self {
423            InlineAgent::Openrouter(a) => &a.id,
424            InlineAgent::ClaudeAgentSdk(a) => &a.id,
425            InlineAgent::CodexSdk(a) => &a.id,
426            InlineAgent::Mock(a) => &a.id,
427            InlineAgent::Script(a) => &a.id,
428        }
429    }
430
431    pub fn base(&self) -> InlineAgentRef<'_> {
432        match self {
433            InlineAgent::Openrouter(a) => InlineAgentRef::Openrouter(&a.base),
434            InlineAgent::ClaudeAgentSdk(a) => {
435                InlineAgentRef::ClaudeAgentSdk(&a.base)
436            }
437            InlineAgent::CodexSdk(a) => InlineAgentRef::CodexSdk(&a.base),
438            InlineAgent::Mock(a) => InlineAgentRef::Mock(&a.base),
439            InlineAgent::Script(a) => InlineAgentRef::Script(&a.base),
440        }
441    }
442
443    pub fn into_base(self) -> InlineAgentBase {
444        match self {
445            InlineAgent::Openrouter(a) => InlineAgentBase::Openrouter(a.base),
446            InlineAgent::ClaudeAgentSdk(a) => {
447                InlineAgentBase::ClaudeAgentSdk(a.base)
448            }
449            InlineAgent::CodexSdk(a) => InlineAgentBase::CodexSdk(a.base),
450            InlineAgent::Mock(a) => InlineAgentBase::Mock(a.base),
451            InlineAgent::Script(a) => InlineAgentBase::Script(a.base),
452        }
453    }
454
455    pub fn top_logprobs(&self) -> Option<u64> {
456        self.base().top_logprobs()
457    }
458}
459
460/// A validated remote Agent with metadata and computed content-addressed ID.
461#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
462#[schemars(rename = "agent.RemoteAgent")]
463pub struct RemoteAgent {
464    pub description: String,
465    #[serde(flatten)]
466    #[schemars(schema_with = "crate::flatten_schema::<InlineAgent>")]
467    pub inner: InlineAgent,
468}
469
470impl RemoteAgent {
471    pub fn id(&self) -> &str {
472        self.inner.id()
473    }
474
475    pub fn base(&self) -> InlineAgentRef<'_> {
476        self.inner.base()
477    }
478
479    pub fn into_base(self) -> RemoteAgentBase {
480        RemoteAgentBase {
481            description: self.description,
482            inner: self.inner.into_base(),
483        }
484    }
485
486    pub fn top_logprobs(&self) -> Option<u64> {
487        self.inner.top_logprobs()
488    }
489}
490
491/// A validated Agent, either remote (with metadata) or inline.
492#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
493#[serde(untagged)]
494#[schemars(rename = "agent.Agent")]
495pub enum Agent {
496    #[schemars(title = "Remote")]
497    Remote(RemoteAgent),
498    #[schemars(title = "Inline")]
499    Inline(InlineAgent),
500}
501
502impl Agent {
503    pub fn id(&self) -> &str {
504        match self {
505            Agent::Remote(a) => a.inner.id(),
506            Agent::Inline(a) => a.id(),
507        }
508    }
509
510    pub fn base(&self) -> InlineAgentRef<'_> {
511        match self {
512            Agent::Remote(a) => a.inner.base(),
513            Agent::Inline(a) => a.base(),
514        }
515    }
516
517    pub fn into_base(self) -> AgentBase {
518        match self {
519            Agent::Remote(a) => AgentBase::Remote(RemoteAgentBase {
520                description: a.description,
521                inner: a.inner.into_base(),
522            }),
523            Agent::Inline(a) => AgentBase::Inline(a.into_base()),
524        }
525    }
526
527    pub fn top_logprobs(&self) -> Option<u64> {
528        self.base().top_logprobs()
529    }
530}
531
532// ── WithFallbacks types ────────────────────────────────────────────
533
534/// An [`InlineAgentBase`] with optional fallbacks (no description).
535#[derive(
536    Clone,
537    Debug,
538    PartialEq,
539    Serialize,
540    Deserialize,
541    JsonSchema,
542    arbitrary::Arbitrary,
543)]
544#[schemars(rename = "agent.InlineAgentBaseWithFallbacks")]
545pub struct InlineAgentBaseWithFallbacks {
546    /// The primary agent configuration.
547    #[serde(flatten)]
548    #[schemars(schema_with = "crate::flatten_schema::<InlineAgentBase>")]
549    pub inner: InlineAgentBase,
550    /// Fallback agents to try if the primary fails.
551    #[serde(skip_serializing_if = "Option::is_none")]
552    #[schemars(extend("omitempty" = true))]
553    pub fallbacks: Option<Vec<InlineAgentBase>>,
554}
555
556impl InlineAgentBaseWithFallbacks {
557    /// Validates and converts to an [`InlineAgentWithFallbacks`] with computed IDs.
558    pub fn convert(self) -> Result<InlineAgentWithFallbacks, String> {
559        let inner = self.inner.convert()?;
560        let fallbacks = match self.fallbacks {
561            Some(fallbacks) if !fallbacks.is_empty() => {
562                let mut converted = Vec::with_capacity(fallbacks.len());
563                for fallback in fallbacks {
564                    converted.push(fallback.convert()?);
565                }
566                Some(converted)
567            }
568            _ => None,
569        };
570        Ok(InlineAgentWithFallbacks { inner, fallbacks })
571    }
572}
573
574/// A validated [`InlineAgent`] with optional fallbacks (no description).
575#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
576#[schemars(rename = "agent.InlineAgentWithFallbacks")]
577pub struct InlineAgentWithFallbacks {
578    /// The primary agent configuration.
579    #[serde(flatten)]
580    #[schemars(schema_with = "crate::flatten_schema::<InlineAgent>")]
581    pub inner: InlineAgent,
582    /// Fallback agents to try if the primary fails.
583    #[serde(skip_serializing_if = "Option::is_none")]
584    #[schemars(extend("omitempty" = true))]
585    pub fallbacks: Option<Vec<InlineAgent>>,
586}
587
588impl InlineAgentWithFallbacks {
589    /// Returns the concatenated IDs of the primary agent and all fallbacks.
590    ///
591    /// Used by swarms to compute their content-addressed ID.
592    pub fn full_id(&self) -> String {
593        match &self.fallbacks {
594            Some(fallbacks) => {
595                let id = self.inner.id();
596                let mut full_id =
597                    String::with_capacity(id.len() + fallbacks.len() * 22);
598                full_id.push_str(id);
599                for fallback in fallbacks {
600                    full_id.push_str(fallback.id());
601                }
602                full_id
603            }
604            None => self.inner.id().to_owned(),
605        }
606    }
607
608    /// Returns an iterator over the IDs of the primary agent and all fallbacks.
609    pub fn ids(&self) -> impl Iterator<Item = &str> + Send {
610        std::iter::once(self.inner.id()).chain(
611            self.fallbacks.as_ref().into_iter().flat_map(|fallbacks| {
612                fallbacks.iter().map(|fallback| fallback.id())
613            }),
614        )
615    }
616}
617
618/// A remote agent base definition with description and optional fallbacks.
619#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
620#[schemars(rename = "agent.RemoteAgentBaseWithFallbacks")]
621pub struct RemoteAgentBaseWithFallbacks {
622    pub description: String,
623    #[serde(flatten)]
624    #[schemars(
625        schema_with = "crate::flatten_schema::<InlineAgentBaseWithFallbacks>"
626    )]
627    pub inner: InlineAgentBaseWithFallbacks,
628}
629
630impl RemoteAgentBaseWithFallbacks {
631    /// Validates and converts to a [`RemoteAgentWithFallbacks`].
632    pub fn convert(self) -> Result<RemoteAgentWithFallbacks, String> {
633        Ok(RemoteAgentWithFallbacks {
634            description: self.description,
635            inner: self.inner.convert()?,
636        })
637    }
638}
639
640/// A validated remote agent with description and optional fallbacks.
641#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
642#[schemars(rename = "agent.RemoteAgentWithFallbacks")]
643pub struct RemoteAgentWithFallbacks {
644    pub description: String,
645    #[serde(flatten)]
646    #[schemars(
647        schema_with = "crate::flatten_schema::<InlineAgentWithFallbacks>"
648    )]
649    pub inner: InlineAgentWithFallbacks,
650}
651
652impl RemoteAgentWithFallbacks {
653    /// Returns the concatenated IDs of the primary agent and all fallbacks.
654    pub fn full_id(&self) -> String {
655        self.inner.full_id()
656    }
657
658    /// Returns an iterator over the IDs of the primary agent and all fallbacks.
659    pub fn ids(&self) -> impl Iterator<Item = &str> + Send {
660        self.inner.ids()
661    }
662}
663
664/// A validated agent with optional fallbacks, either remote (with description) or inline.
665#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
666#[serde(untagged)]
667#[schemars(rename = "agent.AgentWithFallbacks")]
668pub enum AgentWithFallbacks {
669    #[schemars(title = "Remote")]
670    Remote(RemoteAgentWithFallbacks),
671    #[schemars(title = "Inline")]
672    Inline(InlineAgentWithFallbacks),
673}
674
675impl AgentWithFallbacks {
676    /// Returns the inner `InlineAgentWithFallbacks` regardless of variant.
677    pub fn inline(&self) -> &InlineAgentWithFallbacks {
678        match self {
679            AgentWithFallbacks::Remote(a) => &a.inner,
680            AgentWithFallbacks::Inline(a) => a,
681        }
682    }
683
684    /// Returns the primary agent.
685    pub fn agent(&self) -> &InlineAgent {
686        &self.inline().inner
687    }
688
689    /// Returns the fallback agents.
690    pub fn fallbacks(&self) -> Option<&Vec<InlineAgent>> {
691        self.inline().fallbacks.as_ref()
692    }
693
694    /// Returns the concatenated IDs of the primary agent and all fallbacks.
695    pub fn full_id(&self) -> String {
696        self.inline().full_id()
697    }
698
699    /// Returns an iterator over the IDs of the primary agent and all fallbacks.
700    pub fn ids(&self) -> impl Iterator<Item = &str> + Send {
701        self.inline().ids()
702    }
703
704    pub fn id(&self) -> &str {
705        self.agent().id()
706    }
707
708    pub fn base(&self) -> InlineAgentRef<'_> {
709        self.agent().base()
710    }
711
712    pub fn top_logprobs(&self) -> Option<u64> {
713        self.agent().top_logprobs()
714    }
715}
716
717// ── InlineAgentBaseWithFallbacksOrRemote ─────────────────────────────────
718
719/// An agent specification that is either an inline agent base with fallbacks
720/// or a remote path reference.
721///
722/// Used in swarm definitions to allow agents to be specified inline
723/// (with optional fallbacks) or resolved from a remote source via a
724/// hashmap during conversion.
725#[derive(
726    Clone,
727    Debug,
728    PartialEq,
729    Serialize,
730    Deserialize,
731    JsonSchema,
732    arbitrary::Arbitrary,
733)]
734#[serde(untagged)]
735#[schemars(rename = "agent.InlineAgentBaseWithFallbacksOrRemote")]
736pub enum InlineAgentBaseWithFallbacksOrRemote {
737    #[schemars(title = "AgentBase")]
738    AgentBase(InlineAgentBaseWithFallbacks),
739    #[schemars(title = "Remote")]
740    Remote(crate::RemotePath),
741}
742
743/// Like [`InlineAgentBaseWithFallbacksOrRemote`] but with optional commit.
744/// Used in request types where commit resolution happens server-side.
745#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
746#[serde(untagged)]
747#[schemars(rename = "agent.InlineAgentBaseWithFallbacksOrRemoteCommitOptional")]
748pub enum InlineAgentBaseWithFallbacksOrRemoteCommitOptional {
749    #[schemars(title = "AgentBase")]
750    AgentBase(InlineAgentBaseWithFallbacks),
751    #[schemars(title = "Remote")]
752    Remote(crate::RemotePathCommitOptional),
753}
754
755// ── WithCount types (for swarm agent slots) ────────────────────────
756
757fn default_count() -> u64 {
758    1
759}
760
761/// An [`InlineAgentBaseWithFallbacksOrRemote`] with a count
762/// (pre-validation swarm agent slot).
763#[derive(
764    Clone,
765    Debug,
766    PartialEq,
767    Serialize,
768    Deserialize,
769    JsonSchema,
770    arbitrary::Arbitrary,
771)]
772#[schemars(rename = "agent.InlineAgentBaseWithFallbacksOrRemoteWithCount")]
773pub struct InlineAgentBaseWithFallbacksOrRemoteWithCount {
774    /// Number of instances of this agent in the swarm. Defaults to 1.
775    #[serde(default = "default_count")]
776    #[arbitrary(with = crate::arbitrary_util::arbitrary_u64)]
777    pub count: u64,
778    /// The agent configuration.
779    #[serde(flatten)]
780    #[schemars(
781        schema_with = "crate::flatten_schema::<InlineAgentBaseWithFallbacksOrRemote>"
782    )]
783    pub inner: InlineAgentBaseWithFallbacksOrRemote,
784}
785
786/// An [`AgentWithFallbacks`] with a count (post-validation swarm agent slot).
787#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
788#[schemars(rename = "agent.AgentWithFallbacksWithCount")]
789pub struct AgentWithFallbacksWithCount {
790    /// Number of instances of this agent in the swarm. Defaults to 1.
791    #[serde(default = "default_count")]
792    pub count: u64,
793    /// The validated agent configuration.
794    #[serde(flatten)]
795    #[schemars(schema_with = "crate::flatten_schema::<AgentWithFallbacks>")]
796    pub inner: AgentWithFallbacks,
797}