Skip to repository content316 lines · 14.0 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T03:20:24.933Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
omega_exo_lane.rs
1//! The Exo harness lane. `OMEGA-DELTA-0042`, omega#87.
2//!
3//! Omega drives **`exoharness/exo`** — the recursive-self-improvement agent
4//! harness from the Braintrust orbit — as one more executor lane beneath Omega
5//! Agent, beside the native loop and the ACP agents. It is **not** exo labs'
6//! `exo-explore/exo` cluster-inference appliance; omega#86 was closed for
7//! targeting the wrong one, and [`EXO_PIN`] names the repository so the
8//! distinction is a field rather than a sentence.
9//!
10//! This crate is the **law**, and it is a leaf: no GPUI, no process, no
11//! filesystem, no clock. It decides which Exo is admitted ([`pin`]), which
12//! command lines exist ([`command`]), where Exo may be reached ([`endpoint`]),
13//! which Exo agents may be run ([`capability`]), and what a turn's output means
14//! ([`turn`]). The half that spawns a process and builds a thread lives in
15//! `crates/agent_ui/src/omega_exo_connection.rs`, for the same reason the
16//! router's decision half lives here and its dispatch half lives there: a law
17//! that needs GPUI to check is a law nobody checks.
18//!
19//! # Streaming and self-modification authority
20//!
21//! Exo sends live text, tool calls, tool results, and completion records through
22//! its ACP standard-input transport. Omega attaches that transport through
23//! `crates/agent_servers`.
24//!
25//! Self-modification is off by default. A person can authorize one exact turn
26//! in a dedicated confirmation dialog. The grant binds the source, binary,
27//! agent, conversation, tool modules, mounts, draft, generation, and expiry.
28//! The send path consumes the grant once and writes a durable receipt.
29//!
30//! # Which executor class an Exo thread reports, and why
31//!
32//! [`ExecutorClass`] is closed at three variants by `OMEGA-AGENT-AC-04`, and it
33//! answers one question: **who ran the work.** An Exo thread reports
34//! [`ExecutorClass::ExternalAcp`].
35//!
36//! * Not [`ExecutorClass::NativeLoop`]. That is the first-party claim — Omega's
37//! own in-process loop in `crates/agent`. Presenting somebody else's output
38//! as Omega's own is the dishonest attribution omega#77 exists to stop, and
39//! this would be its worst case: Exo's agent has an unrestricted networked
40//! shell.
41//! * Not [`ExecutorClass::EngineLane`]. An engine lane *is* Full Auto
42//! authority. `ExecutorDisclosure::is_coherent` requires a `run_ref` on one,
43//! and the router will not route an unpinned thread to one because owner gate
44//! 8 admits only an explicit human action into that authority. Exo has no
45//! engine run, no run reference, and no receipt. A lane that reported
46//! `EngineLane` would be a new door into Full Auto authority reachable by
47//! adding an executor — a fourth of exactly the kind that were removed from
48//! OpenAgents Desktop on 2026-07-25. An Exo agent with a shell is precisely
49//! the caller that gate exists for.
50//! * So [`ExecutorClass::ExternalAcp`]. Its documentation says "an external ACP
51//! agent reached through `crates/agent_servers`", and Tier A reaches Exo over
52//! its CLI rather than over ACP — so the fit is worth stating rather than
53//! assuming. The class is about the *executor*, not the *wire*: a separate
54//! process, outside Omega, that Omega does not own, that reports no model
55//! through `AgentConnection::model_selector`, and that must carry no
56//! `run_ref` — which is exactly what `is_coherent` requires of this class and
57//! exactly what is true of Exo. Tier B swaps the CLI for ACP and **the class
58//! does not change**, which is the test that the class was about the right
59//! thing.
60//!
61//! A fourth variant was considered and not taken. It would need a revision of
62//! `OMEGA-AGENT-AC-04`, and the argument for it would have to be that the wire
63//! is part of who ran the work. It is not: the wire is why the lane is coarse,
64//! and the coarseness is visible to the reader without a class, because no text
65//! delta ever arrives. That argument is written here so a later reader can
66//! disagree with something concrete rather than re-derive it.
67//!
68//! # The disclosure names Exo, its executor, and its model
69//!
70//! [`ExecutorDisclosure`] is a record of parts a label renders, never a stored
71//! label — omega#74's binding condition, held mechanically by
72//! `EXECUTOR_DISCLOSURE_FIELDS`. So the lane adds no field. It fills the ones
73//! that exist:
74//!
75//! | Part | Exo |
76//! | --- | --- |
77//! | `class` | `ExternalAcp`, for the reasons above |
78//! | `agent_id` | `exo/<executor>` — the process and the executor inside it |
79//! | `provider` | the model binding's base URL, or *not disclosed* |
80//! | `model` | the upstream model the binding resolves to |
81//! | `run_ref` | `None`; Exo holds no Omega run authority |
82//!
83//! `provider` is genuinely `None` for a default binding, because Exo's LLM
84//! binding has no provider field at all — only an optional base URL. Saying
85//! "not disclosed" is the honest third option omega#77 added for exactly this,
86//! and it is better than deriving "openai" from the absence of a URL, which
87//! would be Omega inventing a fact about somebody else's configuration.
88
89pub mod authority;
90pub mod capability;
91pub mod command;
92pub mod endpoint;
93pub mod pin;
94pub mod turn;
95
96pub use capability::{ExoAgent, ExoAgentReadError, ExoConversation, ExoMount, SelfModification};
97pub use command::{ADMITTED_LANE_ARGV, ARGUMENT_TERMINATOR, ExoArg, ExoCommand, ExoRoot};
98pub use endpoint::{EXO_SERVE_DEFAULT_BIND, LoopbackEndpoint, OffLoopback};
99pub use pin::{EXO_HARNESS_ID, EXO_PIN, ExoPin, ExoPinMismatch, ObservedExoCheckout, admits_bytes};
100pub use turn::{ExoToolActivity, ExoTurn, NotATurn};
101
102use omega_front_door::{ExecutorClass, ExecutorDisclosure, RouteReason};
103
104/// The executor class an Exo thread reports. See the module documentation for
105/// why this is the one, and why it is not a fourth.
106pub const EXO_EXECUTOR_CLASS: ExecutorClass = ExecutorClass::ExternalAcp;
107
108/// One row of `exo model list`.
109#[derive(Clone, Debug, PartialEq, Eq)]
110pub struct ExoModelBinding {
111 /// The local binding name, which is what an agent record refers to.
112 pub name: String,
113 /// The model the binding actually resolves to upstream.
114 pub upstream_model: String,
115 /// The base URL, when the binding names one. `None` for Exo's default,
116 /// which carries no provider information at all.
117 pub base_url: Option<String>,
118}
119
120impl ExoModelBinding {
121 /// Read `exo model list` output.
122 ///
123 /// The table is elastic-tab-expanded, so columns are separated by runs of
124 /// two or more spaces. A row this build cannot split into four columns is
125 /// skipped rather than guessed at: a half-read binding would produce a
126 /// disclosure naming a model nobody configured.
127 #[must_use]
128 pub fn read_table(stdout: &str) -> Vec<Self> {
129 stdout
130 .lines()
131 .skip(1)
132 .filter_map(|line| {
133 let columns: Vec<&str> = line
134 .split(" ")
135 .map(str::trim)
136 .filter(|column| !column.is_empty())
137 .collect();
138 let [name, upstream_model, _secret, base_url] = columns.as_slice() else {
139 return None;
140 };
141 Some(Self {
142 name: (*name).to_owned(),
143 upstream_model: (*upstream_model).to_owned(),
144 base_url: (*base_url != "default").then(|| (*base_url).to_owned()),
145 })
146 })
147 .collect()
148 }
149}
150
151/// Everything a thread on this lane discloses.
152///
153/// Assembled from what Exo reported about itself: [`ExoAgent`] from
154/// `agent show`, and the matching [`ExoModelBinding`] from `model list`. A
155/// binding Exo did not report is *absent*, never invented.
156#[derive(Clone, Debug, PartialEq, Eq)]
157pub struct ExoLaneIdentity {
158 /// The executor Exo runs the turn with: `basic`, `rlm`, `typescript`,
159 /// `codex`, `claude-code`, `cursor`, or a module path.
160 pub executor: String,
161 /// The model the agent's binding resolves to upstream, when Exo reported a
162 /// binding for it.
163 pub model: Option<String>,
164 /// The binding's base URL, when it has one.
165 pub provider: Option<String>,
166}
167
168impl ExoLaneIdentity {
169 /// Bind an agent to the model bindings Exo reported.
170 #[must_use]
171 pub fn resolve(agent: &ExoAgent, bindings: &[ExoModelBinding]) -> Self {
172 let binding = bindings.iter().find(|binding| binding.name == agent.model);
173 Self {
174 executor: agent.harness.clone(),
175 model: binding.map(|binding| binding.upstream_model.clone()),
176 provider: binding.and_then(|binding| binding.base_url.clone()),
177 }
178 }
179
180 /// The identifier a thread on this lane presents.
181 ///
182 /// One string, because [`ExecutorDisclosure`] has one field for it and the
183 /// field list is closed on purpose. Both halves are facts Exo reported: the
184 /// process is `exo`, and the executor inside it is what `agent show` said.
185 #[must_use]
186 pub fn agent_id(&self) -> String {
187 format!("exo/{}", self.executor)
188 }
189
190 /// The disclosure record for a thread on this lane.
191 #[must_use]
192 pub fn disclosure(&self, route: Option<RouteReason>) -> ExecutorDisclosure {
193 ExecutorDisclosure {
194 class: EXO_EXECUTOR_CLASS,
195 agent_id: self.agent_id(),
196 provider: self.provider.clone(),
197 model: self.model.clone(),
198 run_ref: None,
199 route,
200 }
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 /// Real `exo model list` output from the pinned Exo, 2026-07-25.
209 const DRIVEN_MODELS: &str = "\
210MODEL UPSTREAM_MODEL SECRET BASE_URL
211gpt5mini gpt-5-mini openai default
212";
213
214 fn driven_agent() -> ExoAgent {
215 ExoAgent {
216 slug: "omega-lane".into(),
217 harness: "basic".into(),
218 model: "gpt5mini".into(),
219 agent_authored_tools: false,
220 tool_modules: 0,
221 tool_module_paths: Vec::new(),
222 read_write_mount: false,
223 mounts: Vec::new(),
224 networking: false,
225 }
226 }
227
228 #[test]
229 fn the_model_table_that_was_driven_reads_back() {
230 let bindings = ExoModelBinding::read_table(DRIVEN_MODELS);
231 assert_eq!(
232 bindings,
233 vec![ExoModelBinding {
234 name: "gpt5mini".into(),
235 upstream_model: "gpt-5-mini".into(),
236 base_url: None,
237 }]
238 );
239 }
240
241 /// The disclosure line, on the identity that was actually driven. It names
242 /// Exo, the executor inside it, and the model — and it says the provider is
243 /// not disclosed rather than inventing one.
244 #[test]
245 fn an_exo_thread_names_exo_its_executor_and_its_model() {
246 let identity =
247 ExoLaneIdentity::resolve(&driven_agent(), &ExoModelBinding::read_table(DRIVEN_MODELS));
248 let disclosure = identity.disclosure(Some(RouteReason::PinHonored));
249 assert!(disclosure.is_coherent());
250
251 let line = disclosure.label();
252 assert!(line.contains("exo/"), "{line}");
253 assert!(line.contains("basic"), "{line}");
254 assert!(line.contains("gpt-5-mini"), "{line}");
255 assert!(line.contains("provider not disclosed"), "{line}");
256 assert!(line.starts_with(EXO_EXECUTOR_CLASS.token()), "{line}");
257 }
258
259 /// The class decision, as an oracle rather than a comment. An Exo thread
260 /// must never claim Omega's own loop, and must never claim engine-lane
261 /// authority.
262 #[test]
263 fn an_exo_thread_claims_neither_omegas_own_loop_nor_full_auto_authority() {
264 assert_ne!(EXO_EXECUTOR_CLASS, ExecutorClass::NativeLoop);
265 assert_ne!(EXO_EXECUTOR_CLASS, ExecutorClass::EngineLane);
266 let identity =
267 ExoLaneIdentity::resolve(&driven_agent(), &ExoModelBinding::read_table(DRIVEN_MODELS));
268 let disclosure = identity.disclosure(Some(RouteReason::PinHonored));
269 assert_eq!(disclosure.run_ref, None, "Exo holds no Omega run authority");
270
271 // The same record with engine-lane authority claimed is incoherent, so
272 // the coherence law is what stops it rather than this lane's manners.
273 let forged = ExecutorDisclosure {
274 class: ExecutorClass::EngineLane,
275 ..disclosure
276 };
277 assert!(!forged.is_coherent());
278 }
279
280 /// A binding Exo did not report leaves the model absent. The alternative —
281 /// falling back to the agent's local alias — would put `gpt5mini` in a
282 /// field that means "the model that served the turn".
283 #[test]
284 fn an_unreported_binding_leaves_the_model_undisclosed() {
285 let identity = ExoLaneIdentity::resolve(&driven_agent(), &[]);
286 assert_eq!(identity.model, None);
287 let disclosure = identity.disclosure(None);
288 assert!(disclosure.is_coherent());
289 assert!(disclosure.label().contains("model not disclosed"));
290 }
291
292 #[test]
293 fn a_binding_with_a_base_url_discloses_it_as_the_provider() {
294 let bindings = ExoModelBinding::read_table(
295 "MODEL UPSTREAM_MODEL SECRET BASE_URL\n\
296 glm z-ai/glm-5.2 or https://openrouter.ai/api/v1\n",
297 );
298 let agent = ExoAgent {
299 model: "glm".into(),
300 ..driven_agent()
301 };
302 let identity = ExoLaneIdentity::resolve(&agent, &bindings);
303 assert_eq!(
304 identity.provider.as_deref(),
305 Some("https://openrouter.ai/api/v1")
306 );
307 assert_eq!(identity.model.as_deref(), Some("z-ai/glm-5.2"));
308 assert!(identity.disclosure(None).is_coherent());
309 }
310}
311pub use authority::{
312 ExoGrantRefusal, ExoSelfModificationCapability, ExoSelfModificationConsentOrigin,
313 ExoSelfModificationGrant, ExoSelfModificationGrantRequest, ExoSelfModificationReceipt,
314 ObservedExoCapabilityState, ObservedReadWriteMount, ObservedToolModule,
315};
316