objectiveai_sdk/laboratories/laboratories.rs
1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4/// A laboratory attached to an agent completion — dialed by the proxy as a
5/// client-side MCP upstream across every agent (and fallback).
6///
7/// Untagged: each variant's payload carries its own `type` discriminator
8/// field, so the wire shape is exactly that of the inner struct (e.g.
9/// `{"type":"client","id":"…"}`).
10#[derive(
11 Debug,
12 Clone,
13 Serialize,
14 Deserialize,
15 PartialEq,
16 Eq,
17 JsonSchema,
18 arbitrary::Arbitrary,
19)]
20#[serde(untagged)]
21#[schemars(rename = "laboratories.Laboratory")]
22// Single-variant untagged enum: schemars emits a single-variant `anyOf` that
23// the json-schema builder flattens to a bare top-level `$ref`, which the SDK
24// code generators inline (breaking the schema roundtrip). The transform adds a
25// sibling `"type": "object"` so it lands as the round-trippable `{type, $ref}`
26// ref-wrapper shape. See `crate::json_schema::ref_wrapper_object`.
27#[schemars(transform = crate::json_schema::ref_wrapper_object)]
28pub enum Laboratory {
29 /// A client-resolved laboratory, identified by an opaque `id`.
30 #[schemars(title = "Client")]
31 Client(ClientLaboratory),
32 /// An agent-embedded laboratory: the spec rides the marker so the
33 /// CLI conduit can materialize the laboratory on demand.
34 #[schemars(title = "Agent")]
35 Agent(AgentLaboratory),
36}
37
38/// A client-resolved laboratory: a client-side MCP server keyed by an
39/// opaque `id`. Wire shape: `{"type":"client","id":"…"}`.
40///
41/// Laboratory ids are only unique per (machine, state) — the same id
42/// can exist on different laboratory hosts. `machine` + `machine_state`
43/// pin THE laboratory this value means, so downstream routing (the
44/// CLI conduit's `/laboratory` forward) is exact rather than
45/// first-match-by-id. Absent pair ⇒ legacy id-only resolution.
46#[derive(
47 Debug,
48 Clone,
49 Serialize,
50 Deserialize,
51 PartialEq,
52 Eq,
53 JsonSchema,
54 arbitrary::Arbitrary,
55)]
56#[schemars(rename = "laboratories.ClientLaboratory")]
57pub struct ClientLaboratory {
58 /// Discriminator — always `"client"`.
59 pub r#type: ClientLaboratoryType,
60 /// The opaque laboratory id.
61 pub id: String,
62 /// The machine id of the laboratory host serving this laboratory.
63 #[serde(default, skip_serializing_if = "Option::is_none")]
64 #[schemars(extend("omitempty" = true))]
65 pub machine: Option<String>,
66 /// The state (on that machine) the laboratory host serves.
67 #[serde(default, skip_serializing_if = "Option::is_none")]
68 #[schemars(extend("omitempty" = true))]
69 pub machine_state: Option<String>,
70}
71
72/// Discriminator for [`ClientLaboratory`]. Ser/de's to the static string
73/// `"client"`.
74#[derive(
75 Debug,
76 Clone,
77 Copy,
78 Serialize,
79 Deserialize,
80 PartialEq,
81 Eq,
82 Hash,
83 JsonSchema,
84 arbitrary::Arbitrary,
85)]
86#[serde(rename_all = "snake_case")]
87#[schemars(rename = "laboratories.ClientLaboratoryType")]
88pub enum ClientLaboratoryType {
89 // NOTE: no variant-level `#[schemars(title = "...")]`. This is a
90 // single-variant unit enum, which schemars collapses to a bare string
91 // schema and hoists the variant title to the schema's top-level
92 // `title`. A title of "Client" makes the JS codegen route this type to
93 // `src/client.ts` (clobbering the real SDK client). Leaving it off lets
94 // the type's `rename` drive the title, matching its siblings.
95 /// Serializes to `"client"`.
96 Client,
97}
98
99/// An agent-embedded laboratory: identified by its DERIVED id (the
100/// reserved `oai-agent-` namespace — see
101/// [`agent::laboratories::derived_id`](crate::agent::laboratories::derived_id)),
102/// carrying the source agent's full id and the embedded spec so the
103/// CLI conduit can create the laboratory on the spot when no connected
104/// host serves the id yet. No pinned (machine, state): an agent
105/// laboratory lives wherever the conduit finds — or creates — it.
106/// Wire shape:
107/// `{"type":"agent","id":"…","agent_full_id":"…","laboratory":{…}}`.
108#[derive(
109 Debug,
110 Clone,
111 Serialize,
112 Deserialize,
113 PartialEq,
114 Eq,
115 JsonSchema,
116 arbitrary::Arbitrary,
117)]
118#[schemars(rename = "laboratories.AgentLaboratory")]
119pub struct AgentLaboratory {
120 /// Discriminator — always `"agent"`.
121 pub r#type: AgentLaboratoryType,
122 /// The derived laboratory id, under the reserved `oai-agent-`
123 /// prefix.
124 pub id: String,
125 /// The full id of the agent the laboratory derives from.
126 pub agent_full_id: String,
127 /// The embedded laboratory spec (image, env, cwd).
128 pub laboratory: crate::agent::Laboratory,
129}
130
131/// Discriminator for [`AgentLaboratory`]. Ser/de's to the static
132/// string `"agent"`.
133#[derive(
134 Debug,
135 Clone,
136 Copy,
137 Serialize,
138 Deserialize,
139 PartialEq,
140 Eq,
141 Hash,
142 JsonSchema,
143 arbitrary::Arbitrary,
144)]
145#[serde(rename_all = "snake_case")]
146#[schemars(rename = "laboratories.AgentLaboratoryType")]
147pub enum AgentLaboratoryType {
148 // NOTE: no variant-level `#[schemars(title = "...")]` — the same
149 // single-variant-unit-enum title-hoisting hazard as
150 // [`ClientLaboratoryType`].
151 /// Serializes to `"agent"`.
152 Agent,
153}