Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T00:58:28.863Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git

Revision diff

a2cf2010 051d63ea
diff --git a/OMEGA_DELTAS.md b/OMEGA_DELTAS.md
index f4dab94524..2f87d17ad7 100644
--- a/OMEGA_DELTAS.md
+++ b/OMEGA_DELTAS.md
@@ -2383,3 +2383,40 @@ than it sounds, because the harness omega#81's acceptance sentence names —
23832383 - **Scope.** This is the **source** half. `0.2.0-rc17` and every earlier
23842384   candidate carry the refusal in their shipped bytes; the packaged half needs
23852385   the next candidate.
2386+### OMEGA-DELTA-0046 — Exo threads have a native conversation workspace
2387+
2388+- **Before this change:** An Exo thread used the standard transcript and
2389+  composer, but its Exo-specific interface was one compressed disclosure row.
2390+  The row did not show the observed harness state, the current turn state, or
2391+  the exact authority boundary. A user could not inspect the Exo lane as one
2392+  coherent system.
2393+- **Omega now:** An Exo thread has a dedicated workspace header and runtime
2394+  inspector. The transcript and composer remain the standard Omega components.
2395+  Text, tool calls, tool results, errors, completion, queued prompts, and
2396+  cancellation therefore keep one rendering and control path.
2397+- **The inspector is a projection, not a second source of truth.** It reads the
2398+  exact observation that gates each turn. It shows the agent, conversation,
2399+  executor, model, provider disclosure, ACP transport, turn state, source pin,
2400+  binary digest, tool modules and digests, writable mounts, network state, and
2401+  the last one-turn authority receipt. Each terminal turn also shows the
2402+  durable Exo session, turn, and latest event references that Exo returned,
2403+  whether or not that turn needed self-modification authority. If observation
2404+  fails, the inspector reports that failure and does not invent runtime facts.
2405+- **The controls use existing authority.** Refresh runs the read-only Exo
2406+  observation. Stop uses the existing ACP cancellation path. The one-turn
2407+  self-modification control appears only when the observed capability set needs
2408+  it, and it uses the existing exact-draft confirmation and receipt path.
2409+  Omega does not add an Exo configuration command, listener, proxy, or Full
2410+  Auto route.
2411+- **The layout is responsive and native.** A wide window puts the inspector
2412+  beside the transcript. A narrow window puts a bounded inspector above it.
2413+  The interface uses the active Omega theme tokens and standard controls. It
2414+  does not add a separate dark theme or a web surface.
2415+- **Enforced by:**
2416+  `an_exo_thread_has_a_live_workspace_and_exact_runtime_inspector` in
2417+  `crates/omega_deltas/`, the Exo inspection and turn-state tests in
2418+  `crates/agent_ui/`, the real Exo acceptance path `drives_a_real_exo`, and
2419+  `omega_exo_workspace_wide` plus `omega_exo_workspace_narrow` in the native
2420+  Metal visual runner. The visual path starts the shipped ACP transport, sends
2421+  one real turn, requires the reply and all three durable references, and then
2422+  records both layouts.
diff --git a/crates/agent_ui/src/conversation_view/thread_view.rs b/crates/agent_ui/src/conversation_view/thread_view.rs
index 353391c30a..fae06f1792 100644
--- a/crates/agent_ui/src/conversation_view/thread_view.rs
+++ b/crates/agent_ui/src/conversation_view/thread_view.rs
@@ -41,6 +41,7 @@ use language_model::{
4141     LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry, Speed,
4242 };
4343 use notifications::status_toast::StatusToast;
44+use omega_exo_lane::ObservedExoCapabilityState;
4445 use omega_front_door::{ExecutorClass, ExecutorDisclosure, ExecutorPin, PinGesture};
4546 use settings::{update_settings_file, update_settings_file_with_completion};
4647 use ui::{
@@ -658,6 +659,8 @@ pub struct ThreadView {
658659     /// `OMEGA-DELTA-0030`. Polls the engine for the linked run. Present only
659660     /// on threads an engine lane executed.
660661     _omega_run_link_task: Option<Task<()>>,
662+    exo_inspector_expanded: bool,
663+    _exo_inspection_task: Option<Task<()>>,
661664 }
662665 impl Focusable for ThreadView {
663666     fn focus_handle(&self, cx: &App) -> FocusHandle {
@@ -1039,6 +1042,8 @@ impl ThreadView {
10391042             _sandbox_status_refresh_task: None,
10401043             omega_run_records: None,
10411044             _omega_run_link_task: None,
1045+            exo_inspector_expanded: true,
1046+            _exo_inspection_task: None,
10421047             hovered_edited_file_buttons: None,
10431048             in_flight_prompt: None,
10441049             message_editor,
@@ -12146,21 +12151,583 @@ impl ThreadView {
1214612151         )
1214712152     }
1214812153 
12154+    fn exo_connection(
12155+        &self,
12156+        cx: &App,
12157+    ) -> Option<Rc<crate::omega_exo_connection::ExoHarnessConnection>> {
12158+        self.thread
12159+            .read(cx)
12160+            .connection()
12161+            .clone()
12162+            .downcast::<crate::omega_exo_connection::ExoHarnessConnection>()
12163+    }
12164+
12165+    fn refresh_exo_inspection(&mut self, cx: &mut Context<Self>) {
12166+        if self._exo_inspection_task.is_some() {
12167+            return;
12168+        }
12169+        let Some(exo) = self.exo_connection(cx) else {
12170+            return;
12171+        };
12172+        let refresh = exo.refresh_inspection(cx);
12173+        self._exo_inspection_task = Some(cx.spawn(async move |this, cx| {
12174+            let result = refresh.await;
12175+            if let Err(error) = this.update(cx, |this, cx| {
12176+                this._exo_inspection_task = None;
12177+                cx.notify();
12178+            }) {
12179+                log::debug!("omega#95: Exo inspector closed during refresh: {error}");
12180+                return;
12181+            }
12182+            if let Err(error) = result {
12183+                log::warn!("omega#95: Exo inspection refresh failed: {error}");
12184+            }
12185+        }));
12186+        cx.notify();
12187+    }
12188+
12189+    fn ensure_exo_inspection(&mut self, cx: &mut Context<Self>) {
12190+        let needs_refresh = self.exo_connection(cx).is_some_and(|exo| {
12191+            exo.inspection().phase == crate::omega_exo_connection::ExoInspectionPhase::NotLoaded
12192+        });
12193+        if needs_refresh {
12194+            self.refresh_exo_inspection(cx);
12195+        }
12196+    }
12197+
12198+    fn authorize_exo_self_modification(
12199+        &mut self,
12200+        exo: Rc<crate::omega_exo_connection::ExoHarnessConnection>,
12201+        window: &mut Window,
12202+        cx: &mut Context<Self>,
12203+    ) {
12204+        let (session_id, prompt) = {
12205+            let thread = self.thread.read(cx);
12206+            let prompt = thread
12207+                .draft_prompt()
12208+                .filter(|prompt| !prompt.is_empty())
12209+                .map(<[acp::ContentBlock]>::to_vec);
12210+            (thread.session_id().clone(), prompt)
12211+        };
12212+        let Some(prompt) = prompt else {
12213+            log::warn!("omega#95: a self-modification grant needs a non-empty draft");
12214+            return;
12215+        };
12216+        let objective = crate::omega_exo_connection::exo_prompt_objective(&prompt);
12217+        let Ok(turn_ref) = crate::omega_exo_connection::exo_turn_ref(&session_id, &prompt) else {
12218+            log::error!("omega#95: failed to bind the Exo draft to a turn");
12219+            return;
12220+        };
12221+        window
12222+            .spawn(cx, async move |cx| {
12223+                let request = exo.self_modification_request(objective, turn_ref).await?;
12224+                let detail = format!(
12225+                    "This grant expires in 60 seconds and applies only to this exact draft, Exo \
12226+                     generation, source tree, binary, tool modules, and read-write mounts.\n\n\
12227+                     Exact capabilities:\n{:#?}",
12228+                    request.capabilities
12229+                );
12230+                let answer = cx
12231+                    .prompt(
12232+                        PromptLevel::Warning,
12233+                        "Allow this Exo agent to modify itself for one turn?",
12234+                        Some(&detail),
12235+                        &["Authorize one turn", "Cancel"],
12236+                    )
12237+                    .await?;
12238+                if answer == 0 {
12239+                    exo.confirm_self_modification(request)?;
12240+                }
12241+                anyhow::Ok(())
12242+            })
12243+            .detach_and_log_err(cx);
12244+    }
12245+
12246+    fn exo_status_color(phase: crate::omega_exo_connection::ExoTurnPhase) -> Color {
12247+        use crate::omega_exo_connection::ExoTurnPhase;
12248+
12249+        match phase {
12250+            ExoTurnPhase::Idle | ExoTurnPhase::Completed => Color::Success,
12251+            ExoTurnPhase::Inspecting | ExoTurnPhase::Working => Color::Accent,
12252+            ExoTurnPhase::Cancelling => Color::Warning,
12253+            ExoTurnPhase::Cancelled => Color::Muted,
12254+            ExoTurnPhase::Refused | ExoTurnPhase::Failed => Color::Error,
12255+        }
12256+    }
12257+
12258+    fn exo_reference(value: &str) -> String {
12259+        const VISIBLE: usize = 12;
12260+        if value.chars().count() <= VISIBLE {
12261+            value.to_owned()
12262+        } else {
12263+            format!("{}…", value.chars().take(VISIBLE).collect::<String>())
12264+        }
12265+    }
12266+
12267+    fn render_exo_field(
12268+        label: &'static str,
12269+        value: impl Into<SharedString>,
12270+        tooltip: Option<SharedString>,
12271+        cx: &App,
12272+    ) -> AnyElement {
12273+        let value = value.into();
12274+        let field_id = format!("omega-exo-field-{label}-{value}");
12275+        h_flex()
12276+            .w_full()
12277+            .min_w_0()
12278+            .gap_1()
12279+            .child(
12280+                Label::new(label)
12281+                    .size(LabelSize::XSmall)
12282+                    .color(Color::Muted),
12283+            )
12284+            .child(div().flex_1())
12285+            .child(
12286+                div()
12287+                    .id(field_id)
12288+                    .min_w_0()
12289+                    .max_w(px(210.))
12290+                    .when_some(tooltip, |this, tooltip| {
12291+                        this.tooltip(Tooltip::text(tooltip))
12292+                    })
12293+                    .child(
12294+                        Label::new(value)
12295+                            .size(LabelSize::XSmall)
12296+                            .color(Color::Default)
12297+                            .truncate(),
12298+                    ),
12299+            )
12300+            .border_b_1()
12301+            .border_color(cx.theme().colors().border_variant.opacity(0.5))
12302+            .py_0p5()
12303+            .into_any_element()
12304+    }
12305+
12306+    fn render_exo_section(title: &'static str, rows: Vec<AnyElement>, cx: &App) -> AnyElement {
12307+        v_flex()
12308+            .w_full()
12309+            .gap_0p5()
12310+            .pb_2()
12311+            .child(
12312+                Label::new(title)
12313+                    .size(LabelSize::Small)
12314+                    .color(Color::Default),
12315+            )
12316+            .child(
12317+                v_flex()
12318+                    .w_full()
12319+                    .border_t_1()
12320+                    .border_color(cx.theme().colors().border_variant)
12321+                    .children(rows),
12322+            )
12323+            .into_any_element()
12324+    }
12325+
12326+    fn render_exo_header(
12327+        &self,
12328+        exo: Rc<crate::omega_exo_connection::ExoHarnessConnection>,
12329+        cx: &mut Context<Self>,
12330+    ) -> AnyElement {
12331+        let disclosure = self.executor_disclosure(cx);
12332+        let turn = exo.turn();
12333+        let is_generating = self.thread.read(cx).status() == ThreadStatus::Generating;
12334+        let inspector_open = self.exo_inspector_expanded;
12335+
12336+        h_flex()
12337+            .id("omega-exo-workspace-header")
12338+            .w_full()
12339+            .min_w_0()
12340+            .flex_wrap()
12341+            .px_2()
12342+            .py_1()
12343+            .gap_1()
12344+            .border_b_1()
12345+            .border_color(cx.theme().colors().border_variant)
12346+            .bg(cx.theme().colors().panel_background)
12347+            .child(
12348+                Icon::new(IconName::BoltOutlined)
12349+                    .size(IconSize::Small)
12350+                    .color(Color::Accent),
12351+            )
12352+            .child(
12353+                v_flex()
12354+                    .min_w_0()
12355+                    .gap_0p5()
12356+                    .child(
12357+                        Label::new("Exo workspace")
12358+                            .size(LabelSize::Small)
12359+                            .color(Color::Default),
12360+                    )
12361+                    .child(
12362+                        Label::new(disclosure.label())
12363+                            .size(LabelSize::XSmall)
12364+                            .color(Color::Muted)
12365+                            .truncate(),
12366+                    ),
12367+            )
12368+            .child(div().flex_1())
12369+            .child(
12370+                h_flex()
12371+                    .gap_0p5()
12372+                    .child(
12373+                        Icon::new(IconName::Circle)
12374+                            .size(IconSize::XSmall)
12375+                            .color(Self::exo_status_color(turn.phase)),
12376+                    )
12377+                    .child(
12378+                        Label::new(turn.phase.label())
12379+                            .size(LabelSize::XSmall)
12380+                            .color(Self::exo_status_color(turn.phase)),
12381+                    ),
12382+            )
12383+            .when(is_generating, |row| {
12384+                row.child(
12385+                    Button::new("omega-exo-cancel-turn", "Stop")
12386+                        .label_size(LabelSize::XSmall)
12387+                        .style(ButtonStyle::Tinted(TintColor::Error))
12388+                        .on_click(cx.listener(|this, _, _, cx| this.cancel_generation(cx))),
12389+                )
12390+            })
12391+            .child(
12392+                Button::new("omega-exo-toggle-inspector", "Inspector")
12393+                    .label_size(LabelSize::XSmall)
12394+                    .toggle_state(inspector_open)
12395+                    .selected_style(ButtonStyle::Outlined)
12396+                    .selected_label_color(Color::Default)
12397+                    .on_click(cx.listener(|this, _, _, cx| {
12398+                        this.exo_inspector_expanded = !this.exo_inspector_expanded;
12399+                        cx.notify();
12400+                    })),
12401+            )
12402+            .child(self.render_executor_pin(cx))
12403+            .into_any_element()
12404+    }
12405+
12406+    fn render_exo_inspector(
12407+        &self,
12408+        exo: Rc<crate::omega_exo_connection::ExoHarnessConnection>,
12409+        compact: bool,
12410+        cx: &mut Context<Self>,
12411+    ) -> AnyElement {
12412+        let inspection = exo.inspection();
12413+        let turn = exo.turn();
12414+        let config = exo.config();
12415+        let receipt = exo.tier_c_receipt();
12416+        let is_refreshing =
12417+            inspection.phase == crate::omega_exo_connection::ExoInspectionPhase::Refreshing;
12418+        let capabilities = inspection
12419+            .observed
12420+            .as_ref()
12421+            .map(ObservedExoCapabilityState::requested_capabilities)
12422+            .unwrap_or_default();
12423+        let needs_authority = !capabilities.is_empty();
12424+        let has_draft = self
12425+            .thread
12426+            .read(cx)
12427+            .draft_prompt()
12428+            .is_some_and(|prompt| !prompt.is_empty());
12429+        let exo_for_authorize = exo.clone();
12430+
12431+        let identity_rows = vec![
12432+            Self::render_exo_field("Agent", config.agent.clone(), None, cx),
12433+            Self::render_exo_field("Conversation", config.conversation.clone(), None, cx),
12434+            Self::render_exo_field(
12435+                "Executor",
12436+                inspection.identity.as_ref().map_or_else(
12437+                    || "Not observed".into(),
12438+                    |identity| identity.executor.clone(),
12439+                ),
12440+                None,
12441+                cx,
12442+            ),
12443+            Self::render_exo_field(
12444+                "Model",
12445+                inspection
12446+                    .identity
12447+                    .as_ref()
12448+                    .and_then(|identity| identity.model.clone())
12449+                    .unwrap_or_else(|| "Not observed".to_owned()),
12450+                None,
12451+                cx,
12452+            ),
12453+            Self::render_exo_field(
12454+                "Provider",
12455+                inspection
12456+                    .identity
12457+                    .as_ref()
12458+                    .and_then(|identity| identity.provider.clone())
12459+                    .unwrap_or_else(|| "Not disclosed".to_owned()),
12460+                None,
12461+                cx,
12462+            ),
12463+            Self::render_exo_field("Transport", "ACP v1 · stdio", None, cx),
12464+        ];
12465+
12466+        let mut runtime_rows = Vec::new();
12467+        runtime_rows.push(Self::render_exo_field(
12468+            "Turn",
12469+            turn.phase.label(),
12470+            turn.detail.clone().map(Into::into),
12471+            cx,
12472+        ));
12473+        for (label, reference) in [
12474+            ("Exo session", turn.exo_session_id.as_ref()),
12475+            ("Exo turn", turn.exo_turn_id.as_ref()),
12476+            ("Latest event", turn.latest_event_id.as_ref()),
12477+        ] {
12478+            if let Some(reference) = reference {
12479+                runtime_rows.push(Self::render_exo_field(
12480+                    label,
12481+                    Self::exo_reference(reference),
12482+                    Some(reference.clone().into()),
12483+                    cx,
12484+                ));
12485+            }
12486+        }
12487+        if let Some(observed) = inspection.observed.as_ref() {
12488+            runtime_rows.push(Self::render_exo_field(
12489+                "Source commit",
12490+                Self::exo_reference(&observed.source_commit),
12491+                Some(observed.source_commit.clone().into()),
12492+                cx,
12493+            ));
12494+            runtime_rows.push(Self::render_exo_field(
12495+                "Source tree",
12496+                Self::exo_reference(&observed.source_tree),
12497+                Some(observed.source_tree.clone().into()),
12498+                cx,
12499+            ));
12500+            runtime_rows.push(Self::render_exo_field(
12501+                "Binary",
12502+                Self::exo_reference(&observed.binary_digest),
12503+                Some(observed.binary_digest.clone().into()),
12504+                cx,
12505+            ));
12506+            runtime_rows.push(Self::render_exo_field(
12507+                "Generation",
12508+                observed.generation.to_string(),
12509+                None,
12510+                cx,
12511+            ));
12512+        } else {
12513+            runtime_rows.push(Self::render_exo_field(
12514+                "Inspection",
12515+                match inspection.phase {
12516+                    crate::omega_exo_connection::ExoInspectionPhase::NotLoaded => "Not loaded",
12517+                    crate::omega_exo_connection::ExoInspectionPhase::Refreshing => "Reading Exo",
12518+                    crate::omega_exo_connection::ExoInspectionPhase::Ready => "Ready",
12519+                    crate::omega_exo_connection::ExoInspectionPhase::Unavailable => "Unavailable",
12520+                },
12521+                inspection.error.clone().map(Into::into),
12522+                cx,
12523+            ));
12524+        }
12525+
12526+        let mut capability_rows =
12527+            vec![
12528+                Self::render_exo_field(
12529+                    "Agent-authored tools",
12530+                    inspection.observed.as_ref().map_or("Unknown", |observed| {
12531+                        if observed.agent_authored_tools {
12532+                            "Enabled"
12533+                        } else {
12534+                            "Off"
12535+                        }
12536+                    }),
12537+                    None,
12538+                    cx,
12539+                ),
12540+                Self::render_exo_field(
12541+                    "Network",
12542+                    inspection.networking.map_or("Unknown", |networking| {
12543+                        if networking { "Enabled" } else { "Off" }
12544+                    }),
12545+                    None,
12546+                    cx,
12547+                ),
12548+            ];
12549+        if let Some(observed) = inspection.observed.as_ref() {
12550+            capability_rows.push(Self::render_exo_field(
12551+                "Tool modules",
12552+                observed.tool_modules.len().to_string(),
12553+                None,
12554+                cx,
12555+            ));
12556+            capability_rows.extend(observed.tool_modules.iter().map(|module| {
12557+                Self::render_exo_field(
12558+                    "Module",
12559+                    module.path.clone(),
12560+                    Some(format!("{}\n{}", module.path, module.digest).into()),
12561+                    cx,
12562+                )
12563+            }));
12564+            capability_rows.push(Self::render_exo_field(
12565+                "Read-write mounts",
12566+                observed.read_write_mounts.len().to_string(),
12567+                None,
12568+                cx,
12569+            ));
12570+            capability_rows.extend(observed.read_write_mounts.iter().map(|mount| {
12571+                let mapping = format!("{} → {}", mount.host_path, mount.mount_path);
12572+                Self::render_exo_field("Mount", mapping.clone(), Some(mapping.into()), cx)
12573+            }));
12574+        }
12575+
12576+        let authority_rows = if let Some(receipt) = receipt {
12577+            vec![
12578+                Self::render_exo_field("Outcome", receipt.outcome, None, cx),
12579+                Self::render_exo_field(
12580+                    "Exo session",
12581+                    receipt
12582+                        .exo_session_id
12583+                        .as_deref()
12584+                        .map_or_else(|| "Pending".to_owned(), Self::exo_reference),
12585+                    receipt.exo_session_id.map(Into::into),
12586+                    cx,
12587+                ),
12588+                Self::render_exo_field(
12589+                    "Exo turn",
12590+                    receipt
12591+                        .exo_turn_id
12592+                        .as_deref()
12593+                        .map_or_else(|| "Pending".to_owned(), Self::exo_reference),
12594+                    receipt.exo_turn_id.map(Into::into),
12595+                    cx,
12596+                ),
12597+                Self::render_exo_field(
12598+                    "Latest event",
12599+                    receipt
12600+                        .latest_event_id
12601+                        .as_deref()
12602+                        .map_or_else(|| "Pending".to_owned(), Self::exo_reference),
12603+                    receipt.latest_event_id.map(Into::into),
12604+                    cx,
12605+                ),
12606+                Self::render_exo_field(
12607+                    "Verification",
12608+                    receipt.verification.clone(),
12609+                    Some(receipt.verification.into()),
12610+                    cx,
12611+                ),
12612+            ]
12613+        } else {
12614+            vec![Self::render_exo_field(
12615+                "Authority",
12616+                if needs_authority {
12617+                    "Required for this agent"
12618+                } else {
12619+                    "Not required"
12620+                },
12621+                None,
12622+                cx,
12623+            )]
12624+        };
12625+
12626+        v_flex()
12627+            .id("omega-exo-inspector")
12628+            .h_full()
12629+            .when(!compact, |this| this.w(px(336.)).min_w(px(288.)))
12630+            .when(compact, |this| this.w_full().max_h(px(300.)))
12631+            .border_l_1()
12632+            .when(compact, |this| this.border_l_0().border_b_1())
12633+            .border_color(cx.theme().colors().border_variant)
12634+            .bg(cx.theme().colors().surface_background)
12635+            .child(
12636+                h_flex()
12637+                    .w_full()
12638+                    .px_2()
12639+                    .py_1()
12640+                    .gap_1()
12641+                    .border_b_1()
12642+                    .border_color(cx.theme().colors().border_variant)
12643+                    .child(
12644+                        Label::new("Runtime inspector")
12645+                            .size(LabelSize::Small)
12646+                            .color(Color::Default),
12647+                    )
12648+                    .child(div().flex_1())
12649+                    .child(
12650+                        IconButton::new("omega-exo-refresh-inspection", IconName::RotateCw)
12651+                            .disabled(is_refreshing)
12652+                            .tooltip(Tooltip::text(if is_refreshing {
12653+                                "Reading Exo runtime"
12654+                            } else {
12655+                                "Refresh Exo runtime"
12656+                            }))
12657+                            .on_click(cx.listener(|this, _, _, cx| {
12658+                                this.refresh_exo_inspection(cx);
12659+                            })),
12660+                    ),
12661+            )
12662+            .child(
12663+                v_flex()
12664+                    .id("omega-exo-inspector-scroll")
12665+                    .flex_1()
12666+                    .overflow_y_scroll()
12667+                    .px_2()
12668+                    .pt_2()
12669+                    .children([
12670+                        Self::render_exo_section("Identity", identity_rows, cx),
12671+                        Self::render_exo_section("Runtime", runtime_rows, cx),
12672+                        Self::render_exo_section("Capabilities", capability_rows, cx),
12673+                        Self::render_exo_section("Authority receipt", authority_rows, cx),
12674+                    ])
12675+                    .child(
12676+                        v_flex()
12677+                            .w_full()
12678+                            .gap_1()
12679+                            .pb_2()
12680+                            .when(needs_authority, |this| {
12681+                                this.child(
12682+                                    Button::new(
12683+                                        "omega-exo-authorize-self-modification",
12684+                                        "Authorize this draft",
12685+                                    )
12686+                                    .style(ButtonStyle::Tinted(TintColor::Warning))
12687+                                    .label_size(LabelSize::XSmall)
12688+                                    .disabled(!has_draft)
12689+                                    .tooltip(Tooltip::text(if has_draft {
12690+                                        "Authorize one exact self-modifying Exo turn"
12691+                                    } else {
12692+                                        "Write the exact draft before authorizing it"
12693+                                    }))
12694+                                    .on_click(cx.listener(
12695+                                        move |this, _, window, cx| {
12696+                                            this.authorize_exo_self_modification(
12697+                                                exo_for_authorize.clone(),
12698+                                                window,
12699+                                                cx,
12700+                                            );
12701+                                        },
12702+                                    )),
12703+                                )
12704+                            })
12705+                            .when(!needs_authority, |this| {
12706+                                this.child(
12707+                                    Label::new(
12708+                                        "This agent has no observed self-modification capability. \
12709+                                         Normal turns need no additional grant.",
12710+                                    )
12711+                                    .size(LabelSize::XSmall)
12712+                                    .color(Color::Muted),
12713+                                )
12714+                            }),
12715+                    ),
12716+            )
12717+            .into_any_element()
12718+    }
12719+
1214912720     /// The executor line, rendered from the record.
1215012721     ///
1215112722     /// Unconditional, and above the entries rather than beside them, because
1215212723     /// omega#77's falsifier is a thread surface that shows work without naming
1215312724     /// its executor — including an empty thread, which is about to.
12154-    fn render_executor_disclosure(&self, cx: &mut Context<Self>) -> impl IntoElement {
12725+    fn render_executor_disclosure(&self, cx: &mut Context<Self>) -> AnyElement {
1215512726         let disclosure = self.executor_disclosure(cx);
12156-        let exo = self
12157-            .thread
12158-            .read(cx)
12159-            .connection()
12160-            .clone()
12161-            .downcast::<crate::omega_exo_connection::ExoHarnessConnection>();
12162-        let receipt = exo.as_ref().and_then(|exo| exo.tier_c_receipt());
12163-        let thread = self.thread.clone();
12727+        if let Some(exo) = self.exo_connection(cx) {
12728+            return self.render_exo_header(exo, cx);
12729+        }
12730+
1216412731         h_flex()
1216512732             .w_full()
1216612733             .px_2()
@@ -12179,77 +12746,8 @@ impl ThreadView {
1217912746                     .color(Color::Muted),
1218012747             )
1218112748             .child(div().flex_1())
12182-            .when_some(receipt, |row, receipt| {
12183-                row.child(
12184-                    Label::new(format!(
12185-                        "self-modification {} · generation {} · event {}",
12186-                        receipt.outcome,
12187-                        receipt.observed.generation,
12188-                        receipt.latest_event_id.as_deref().unwrap_or("pending")
12189-                    ))
12190-                    .size(LabelSize::XSmall)
12191-                    .color(Color::Muted),
12192-                )
12193-            })
12194-            .when_some(exo, |row, exo| {
12195-                row.child(
12196-                    Button::new(
12197-                        "omega-exo-authorize-self-modification",
12198-                        "Authorize one self-modifying turn",
12199-                    )
12200-                    .label_size(LabelSize::XSmall)
12201-                    .color(Color::Warning)
12202-                    .on_click(move |_, window, cx| {
12203-                        let (session_id, prompt) = {
12204-                            let thread = thread.read(cx);
12205-                            let prompt = thread
12206-                                .draft_prompt()
12207-                                .filter(|prompt| !prompt.is_empty())
12208-                                .map(<[acp::ContentBlock]>::to_vec);
12209-                            (thread.session_id().clone(), prompt)
12210-                        };
12211-                        let Some(prompt) = prompt else {
12212-                            log::warn!(
12213-                                "omega#87: a self-modification grant needs a non-empty draft"
12214-                            );
12215-                            return;
12216-                        };
12217-                        let objective = crate::omega_exo_connection::exo_prompt_objective(&prompt);
12218-                        let Ok(turn_ref) =
12219-                            crate::omega_exo_connection::exo_turn_ref(&session_id, &prompt)
12220-                        else {
12221-                            log::error!("omega#87: failed to bind the Exo draft to a turn");
12222-                            return;
12223-                        };
12224-                        let exo = Rc::clone(&exo);
12225-                        window
12226-                            .spawn(cx, async move |cx| {
12227-                                let request =
12228-                                    exo.self_modification_request(objective, turn_ref).await?;
12229-                                let detail = format!(
12230-                                    "This grant expires in 60 seconds and applies only to this \
12231-                                     exact draft, Exo generation, source tree, binary, tool \
12232-                                     modules, and read-write mounts.\n\nExact capabilities:\n{:#?}",
12233-                                    request.capabilities
12234-                                );
12235-                                let answer = cx
12236-                                    .prompt(
12237-                                        PromptLevel::Warning,
12238-                                        "Allow this Exo agent to modify itself for one turn?",
12239-                                        Some(&detail),
12240-                                        &["Authorize one turn", "Cancel"],
12241-                                    )
12242-                                    .await?;
12243-                                if answer == 0 {
12244-                                    exo.confirm_self_modification(request)?;
12245-                                }
12246-                                anyhow::Ok(())
12247-                            })
12248-                            .detach_and_log_err(cx);
12249-                    }),
12250-                )
12251-            })
1225212749             .child(self.render_executor_pin(cx))
12750+            .into_any_element()
1225312751     }
1225412752 
1225512753     /// `OMEGA-DELTA-0035`. The pin, as a gesture rather than a mode.
@@ -12341,13 +12839,14 @@ impl Render for ThreadView {
1234112839         // engine what its run is doing, because the engine is the only thing
1234212840         // entitled to say.
1234312841         self.ensure_omega_run_link_refresh(cx);
12842+        self.ensure_exo_inspection(cx);
1234412843 
1234512844         let has_messages = self.list_state.item_count() > 0;
1234612845         let list_state = self.list_state.clone();
12846+        let exo = self.exo_connection(cx);
12847+        let compact_exo_layout = window.viewport_size().width < px(960.);
1234712848 
1234812849         let conversation = v_flex()
12349-            // OMEGA-DELTA-0021. Before anything the thread produced.
12350-            .child(self.render_executor_disclosure(cx))
1235112850             // OMEGA-DELTA-0030. The run behind the line, where there is one.
1235212851             .children(self.render_omega_run_link(cx))
1235312852             .when(self.resumed_without_history, |this| {
@@ -12364,6 +12863,28 @@ impl Render for ThreadView {
1236412863                     this.into_any()
1236512864                 }
1236612865             });
12866+        let conversation = match (exo, self.exo_inspector_expanded) {
12867+            (Some(exo), true) => {
12868+                let inspector = self.render_exo_inspector(exo, compact_exo_layout, cx);
12869+                if compact_exo_layout {
12870+                    v_flex()
12871+                        .flex_1()
12872+                        .size_full()
12873+                        .child(inspector)
12874+                        .child(conversation)
12875+                        .into_any_element()
12876+                } else {
12877+                    h_flex()
12878+                        .flex_1()
12879+                        .size_full()
12880+                        .min_w_0()
12881+                        .child(conversation)
12882+                        .child(inspector)
12883+                        .into_any_element()
12884+                }
12885+            }
12886+            _ => conversation.into_any_element(),
12887+        };
1236712888 
1236812889         v_flex()
1236912890             .key_context("AcpThread")
@@ -12673,6 +13194,8 @@ impl Render for ThreadView {
1267313194                     .flatten(),
1267413195                 |this, bar| this.child(bar),
1267513196             )
13197+            // OMEGA-DELTA-0021. Before anything the thread produced.
13198+            .child(self.render_executor_disclosure(cx))
1267613199             .child(conversation)
1267713200             .children(self.render_multi_root_callout(cx))
1267813201             .children(self.render_activity_bar(window, cx))
@@ -12900,6 +13423,19 @@ mod tests {
1290013423         ))
1290113424     }
1290213425 
13426+    #[test]
13427+    fn exo_runtime_references_stay_distinguishable_in_the_inspector() {
13428+        assert_eq!(ThreadView::exo_reference("short"), "short");
13429+        assert_eq!(
13430+            ThreadView::exo_reference("cd7c0d29db869e953fb7261d8390ca93007d36a6"),
13431+            "cd7c0d29db86…"
13432+        );
13433+        assert_ne!(
13434+            ThreadView::exo_reference("cd7c0d29db869e953fb7261d8390ca93007d36a6"),
13435+            ThreadView::exo_reference("c61846e3f44daaf445930d1a499432ca9b069306")
13436+        );
13437+    }
13438+
1290313439     #[test]
1290413440     fn test_leading_native_command_matches_bare_and_with_remainder() {
1290513441         let commands = [native_command("compact"), mcp_command("deploy")];
diff --git a/crates/agent_ui/src/omega_exo_connection.rs b/crates/agent_ui/src/omega_exo_connection.rs
index 93beb3120c..9d4d76c652 100644
--- a/crates/agent_ui/src/omega_exo_connection.rs
+++ b/crates/agent_ui/src/omega_exo_connection.rs
@@ -166,6 +166,88 @@ struct ExoDriver {
166166     generation: u64,
167167 }
168168 
169+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
170+pub enum ExoInspectionPhase {
171+    NotLoaded,
172+    Refreshing,
173+    Ready,
174+    Unavailable,
175+}
176+
177+#[derive(Clone, Debug, PartialEq, Eq)]
178+pub struct ExoInspectionSnapshot {
179+    pub phase: ExoInspectionPhase,
180+    pub observed: Option<ObservedExoCapabilityState>,
181+    pub identity: Option<ExoLaneIdentity>,
182+    pub networking: Option<bool>,
183+    pub refreshed_at_ms: Option<u64>,
184+    pub error: Option<String>,
185+}
186+
187+impl Default for ExoInspectionSnapshot {
188+    fn default() -> Self {
189+        Self {
190+            phase: ExoInspectionPhase::NotLoaded,
191+            observed: None,
192+            identity: None,
193+            networking: None,
194+            refreshed_at_ms: None,
195+            error: None,
196+        }
197+    }
198+}
199+
200+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
201+pub enum ExoTurnPhase {
202+    Idle,
203+    Inspecting,
204+    Working,
205+    Cancelling,
206+    Completed,
207+    Cancelled,
208+    Refused,
209+    Failed,
210+}
211+
212+impl ExoTurnPhase {
213+    #[must_use]
214+    pub const fn label(self) -> &'static str {
215+        match self {
216+            Self::Idle => "Ready",
217+            Self::Inspecting => "Checking runtime",
218+            Self::Working => "Working",
219+            Self::Cancelling => "Cancelling",
220+            Self::Completed => "Completed",
221+            Self::Cancelled => "Cancelled",
222+            Self::Refused => "Refused",
223+            Self::Failed => "Failed",
224+        }
225+    }
226+}
227+
228+#[derive(Clone, Debug, PartialEq, Eq)]
229+pub struct ExoTurnSnapshot {
230+    pub phase: ExoTurnPhase,
231+    pub detail: Option<String>,
232+    pub exo_session_id: Option<String>,
233+    pub exo_turn_id: Option<String>,
234+    pub latest_event_id: Option<String>,
235+    pub updated_at_ms: u64,
236+}
237+
238+impl Default for ExoTurnSnapshot {
239+    fn default() -> Self {
240+        Self {
241+            phase: ExoTurnPhase::Idle,
242+            detail: None,
243+            exo_session_id: None,
244+            exo_turn_id: None,
245+            latest_event_id: None,
246+            updated_at_ms: now_ms(),
247+        }
248+    }
249+}
250+
169251 #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
170252 pub struct ExoTierCReceipt {
171253     pub schema: String,
@@ -190,6 +272,8 @@ pub struct ExoHarnessConnection {
190272     pending_grant: RefCell<Option<ExoSelfModificationGrant>>,
191273     tier_c_receipt: Rc<RefCell<Option<ExoTierCReceipt>>>,
192274     active_tier_c_turn: Rc<Cell<bool>>,
275+    inspection: Rc<RefCell<ExoInspectionSnapshot>>,
276+    turn: Rc<RefCell<ExoTurnSnapshot>>,
193277 }
194278 
195279 impl ExoHarnessConnection {
@@ -212,6 +296,8 @@ impl ExoHarnessConnection {
212296             pending_grant: RefCell::new(None),
213297             tier_c_receipt: Rc::new(RefCell::new(previous_receipt)),
214298             active_tier_c_turn: Rc::new(Cell::new(false)),
299+            inspection: Rc::new(RefCell::new(ExoInspectionSnapshot::default())),
300+            turn: Rc::new(RefCell::new(ExoTurnSnapshot::default())),
215301         }
216302     }
217303 
@@ -236,6 +322,58 @@ impl ExoHarnessConnection {
236322         self.tier_c_receipt.borrow().clone()
237323     }
238324 
325+    #[must_use]
326+    pub fn inspection(&self) -> ExoInspectionSnapshot {
327+        self.inspection.borrow().clone()
328+    }
329+
330+    #[must_use]
331+    pub fn turn(&self) -> ExoTurnSnapshot {
332+        self.turn.borrow().clone()
333+    }
334+
335+    fn set_turn(&self, phase: ExoTurnPhase, detail: Option<String>) {
336+        *self.turn.borrow_mut() = ExoTurnSnapshot {
337+            phase,
338+            detail,
339+            exo_session_id: None,
340+            exo_turn_id: None,
341+            latest_event_id: None,
342+            updated_at_ms: now_ms(),
343+        };
344+    }
345+
346+    pub fn refresh_inspection(&self, cx: &mut App) -> Task<Result<()>> {
347+        {
348+            let mut inspection = self.inspection.borrow_mut();
349+            inspection.phase = ExoInspectionPhase::Refreshing;
350+            inspection.error = None;
351+        }
352+        let driver = self.driver.clone();
353+        let inspection = self.inspection.clone();
354+        cx.spawn(async move |_| match driver.observe().await {
355+            Ok(observed) => {
356+                *inspection.borrow_mut() = ExoInspectionSnapshot {
357+                    phase: ExoInspectionPhase::Ready,
358+                    observed: Some(observed.observed),
359+                    identity: Some(observed.identity),
360+                    networking: Some(observed.networking),
361+                    refreshed_at_ms: Some(now_ms()),
362+                    error: None,
363+                };
364+                Ok(())
365+            }
366+            Err(error) => {
367+                let message = error.to_string();
368+                let mut snapshot = inspection.borrow_mut();
369+                snapshot.phase = ExoInspectionPhase::Unavailable;
370+                snapshot.refreshed_at_ms = Some(now_ms());
371+                snapshot.error = Some(message.clone());
372+                Err(anyhow!(message))
373+            }
374+        })
375+    }
376+
239377     /// Observe the exact Exo capability state for a dedicated confirmation
240378     /// dialog. This does not mint authority.
241379     pub async fn self_modification_request(
@@ -275,6 +413,8 @@ impl ExoHarnessConnection {
275413 
276414 struct ObservedTurn {
277415     observed: ObservedExoCapabilityState,
416+    identity: ExoLaneIdentity,
417+    networking: bool,
278418 }
279419 
280420 fn now_ms() -> u64 {
@@ -285,6 +425,34 @@ fn now_ms() -> u64 {
285425         })
286426 }
287427 
428+fn set_turn_snapshot(turn: &RefCell<ExoTurnSnapshot>, phase: ExoTurnPhase, detail: Option<String>) {
429+    *turn.borrow_mut() = ExoTurnSnapshot {
430+        phase,
431+        detail,
432+        exo_session_id: None,
433+        exo_turn_id: None,
434+        latest_event_id: None,
435+        updated_at_ms: now_ms(),
436+    };
437+}
438+
439+fn set_terminal_turn_snapshot(
440+    turn: &RefCell<ExoTurnSnapshot>,
441+    phase: ExoTurnPhase,
442+    detail: Option<String>,
443+    response: &acp::PromptResponse,
444+) {
445+    let meta = response.meta.as_ref();
446+    *turn.borrow_mut() = ExoTurnSnapshot {
447+        phase,
448+        detail,
449+        exo_session_id: meta_value(meta, "exo.session_id"),
450+        exo_turn_id: meta_value(meta, "exo.turn_id"),
451+        latest_event_id: meta_value(meta, "exo.latest_event_id"),
452+        updated_at_ms: now_ms(),
453+    };
454+}
455+
288456 fn resolve_module_host_path(module: &str, mounts: &[ExoMount]) -> Option<PathBuf> {
289457     let path = std::path::Path::new(module);
290458     if path.is_file() {
@@ -482,6 +650,8 @@ impl ExoDriver {
482650                 tool_modules,
483651                 read_write_mounts,
484652             },
653+            identity,
654+            networking: agent.networking,
485655         })
486656     }
487657 
@@ -686,14 +856,45 @@ impl AgentConnection for ExoHarnessConnection {
686856         params: acp::PromptRequest,
687857         cx: &mut App,
688858     ) -> Task<Result<acp::PromptResponse>> {
859+        self.set_turn(
860+            ExoTurnPhase::Inspecting,
861+            Some("Checking the pinned Exo runtime before this turn".to_owned()),
862+        );
689863         let driver = Rc::clone(&self.driver);
690864         let acp = Rc::clone(&self.acp);
691865         let pending_grant = self.pending_grant.take();
692866         let receipt_cell = self.tier_c_receipt.clone();
693867         let active_tier_c_turn = self.active_tier_c_turn.clone();
868+        let inspection_cell = self.inspection.clone();
869+        let turn_cell = self.turn.clone();
694870         cx.spawn(async move |cx| {
695871             let turn_ref = exo_turn_ref(&params.session_id, &params.prompt)?;
696-            let observed = driver.preflight().await?;
872+            let observed = match driver.preflight().await {
873+                Ok(observed) => observed,
874+                Err(error) => {
875+                    let message = error.to_string();
876+                    {
877+                        let mut inspection = inspection_cell.borrow_mut();
878+                        inspection.phase = ExoInspectionPhase::Unavailable;
879+                        inspection.refreshed_at_ms = Some(now_ms());
880+                        inspection.error = Some(message.clone());
881+                    }
882+                    set_turn_snapshot(
883+                        &turn_cell,
884+                        ExoTurnPhase::Failed,
885+                        Some(format!("Runtime check failed: {message}")),
886+                    );
887+                    return Err(error);
888+                }
889+            };
890+            *inspection_cell.borrow_mut() = ExoInspectionSnapshot {
891+                phase: ExoInspectionPhase::Ready,
892+                observed: Some(observed.observed.clone()),
893+                identity: Some(observed.identity.clone()),
894+                networking: Some(observed.networking),
895+                refreshed_at_ms: Some(now_ms()),
896+                error: None,
897+            };
697898             let capabilities = observed.observed.requested_capabilities();
698899             let authority_receipt = if capabilities.is_empty() {
699900                 None
@@ -708,6 +909,11 @@ impl AgentConnection for ExoHarnessConnection {
708909                     );
709910                     *receipt_cell.borrow_mut() = Some(receipt.clone());
710911                     persist_tier_c_receipt(receipt).await?;
912+                    set_turn_snapshot(
913+                        &turn_cell,
914+                        ExoTurnPhase::Refused,
915+                        Some(message.to_owned()),
916+                    );
711917                     bail!("{message}");
712918                 };
713919                 let requested_objective = Some(grant.request().objective.clone());
@@ -725,6 +931,11 @@ impl AgentConnection for ExoHarnessConnection {
725931                         );
726932                         *receipt_cell.borrow_mut() = Some(receipt.clone());
727933                         persist_tier_c_receipt(receipt).await?;
934+                        set_turn_snapshot(
935+                            &turn_cell,
936+                            ExoTurnPhase::Refused,
937+                            Some(message.clone()),
938+                        );
728939                         bail!("{message}");
729940                     }
730941                 }
@@ -755,8 +966,34 @@ impl AgentConnection for ExoHarnessConnection {
755966                     }
756967                 }
757968             }
969+            set_turn_snapshot(
970+                &turn_cell,
971+                ExoTurnPhase::Working,
972+                Some("Streaming this turn over ACP".to_owned()),
973+            );
758974             let prompt = cx.update(|cx| acp.prompt(params, cx));
759975             let response = prompt.await;
976+            match &response {
977+                Ok(response) => match response.stop_reason {
978+                    acp::StopReason::Cancelled => set_terminal_turn_snapshot(
979+                        &turn_cell,
980+                        ExoTurnPhase::Cancelled,
981+                        Some("Exo confirmed cancellation".to_owned()),
982+                        response,
983+                    ),
984+                    _ => set_terminal_turn_snapshot(
985+                        &turn_cell,
986+                        ExoTurnPhase::Completed,
987+                        Some("Exo completed the streamed turn".to_owned()),
988+                        response,
989+                    ),
990+                },
991+                Err(error) => set_turn_snapshot(
992+                    &turn_cell,
993+                    ExoTurnPhase::Failed,
994+                    Some(error.to_string()),
995+                ),
996+            }
760997             if is_tier_c_turn {
761998                 if let Some(receipt) = receipt_cell.borrow_mut().as_mut() {
762999                     match &response {
@@ -800,6 +1037,10 @@ impl AgentConnection for ExoHarnessConnection {
8001037     }
8011038 
8021039     fn cancel(&self, session_id: &acp::SessionId, cx: &mut App) {
1040+        self.set_turn(
1041+            ExoTurnPhase::Cancelling,
1042+            Some("Waiting for Exo to confirm cancellation".to_owned()),
1043+        );
8031044         let cancelled_pending_grant = self.pending_grant.borrow_mut().take().map(|grant| {
8041045             let request = grant.request();
8051046             refused_tier_c_receipt(
@@ -873,6 +1114,66 @@ mod tests {
8731114         assert_eq!(config.root.as_str(), "/opt/exo/.exo");
8741115     }
8751116 
1117+    #[test]
1118+    fn an_unread_inspector_claims_no_runtime_facts() {
1119+        let inspection = ExoInspectionSnapshot::default();
1120+        assert_eq!(inspection.phase, ExoInspectionPhase::NotLoaded);
1121+        assert_eq!(inspection.observed, None);
1122+        assert_eq!(inspection.identity, None);
1123+        assert_eq!(inspection.networking, None);
1124+        assert_eq!(inspection.refreshed_at_ms, None);
1125+        assert_eq!(inspection.error, None);
1126+    }
1127+
1128+    #[test]
1129+    fn every_exo_turn_phase_has_a_distinct_visible_label() {
1130+        let phases = [
1131+            ExoTurnPhase::Idle,
1132+            ExoTurnPhase::Inspecting,
1133+            ExoTurnPhase::Working,
1134+            ExoTurnPhase::Cancelling,
1135+            ExoTurnPhase::Completed,
1136+            ExoTurnPhase::Cancelled,
1137+            ExoTurnPhase::Refused,
1138+            ExoTurnPhase::Failed,
1139+        ];
1140+        let labels = phases.map(ExoTurnPhase::label);
1141+        for (index, label) in labels.iter().enumerate() {
1142+            assert!(!label.is_empty());
1143+            assert!(
1144+                !labels[..index].contains(label),
1145+                "duplicate Exo turn label: {label}"
1146+            );
1147+        }
1148+    }
1149+
1150+    #[test]
1151+    fn a_turn_snapshot_records_terminal_state_and_detail() {
1152+        let turn = RefCell::new(ExoTurnSnapshot::default());
1153+        let response = acp::PromptResponse::new(acp::StopReason::EndTurn).meta(
1154+            [
1155+                ("exo.session_id".into(), serde_json::json!("session-1")),
1156+                ("exo.turn_id".into(), serde_json::json!("turn-1")),
1157+                ("exo.latest_event_id".into(), serde_json::json!("event-1")),
1158+            ]
1159+            .into_iter()
1160+            .collect::<serde_json::Map<String, serde_json::Value>>(),
1161+        );
1162+        set_terminal_turn_snapshot(
1163+            &turn,
1164+            ExoTurnPhase::Completed,
1165+            Some("durable event observed".into()),
1166+            &response,
1167+        );
1168+        let turn = turn.borrow();
1169+        assert_eq!(turn.phase, ExoTurnPhase::Completed);
1170+        assert_eq!(turn.detail.as_deref(), Some("durable event observed"));
1171+        assert_eq!(turn.exo_session_id.as_deref(), Some("session-1"));
1172+        assert_eq!(turn.exo_turn_id.as_deref(), Some("turn-1"));
1173+        assert_eq!(turn.latest_event_id.as_deref(), Some("event-1"));
1174+        assert!(turn.updated_at_ms > 0);
1175+    }
1176+
8761177     /// A machine with no Exo has no lane. This is the ordinary case, and it
8771178     /// must not be an error.
8781179     #[test]
@@ -1057,11 +1358,12 @@ mod tests {
10571358             .expect("a session on the Exo lane");
10581359 
10591360         let session_id = thread.read_with(cx, |thread, _| thread.session_id().clone());
1060-        let marker = "OMEGA-EXO-ACP-STREAM";
1361+        let tool_marker = "OMEGA-EXO-TOOL";
1362+        let marker = "OMEGA-EXO-PANE-READY";
10611363         let request = acp::PromptRequest::new(
10621364             session_id,
10631365             vec![acp::ContentBlock::Text(acp::TextContent::new(format!(
1064-                "Reply with exactly the word {marker} and nothing else."
1366+                "Run one tool that prints {tool_marker}, then reply with exactly {marker}."
10651367             )))],
10661368         );
10671369         let response = cx
@@ -1075,6 +1377,10 @@ mod tests {
10751377             rendered.contains(marker),
10761378             "the Exo reply did not reach the thread: {rendered}"
10771379         );
1380+        assert!(
1381+            rendered.contains(tool_marker),
1382+            "the Exo tool result did not reach the thread: {rendered}"
1383+        );
10781384 
10791385         let disclosure = thread.read_with(cx, |thread, cx| thread.omega_executor_disclosure(cx));
10801386         assert!(disclosure.is_coherent(), "{disclosure:?}");
@@ -1085,6 +1391,18 @@ mod tests {
10851391             .downcast::<ExoHarnessConnection>()
10861392             .expect("the configured lane is Exo");
10871393         let identity = exo.identity().expect("Exo told the lane who it is");
1394+        let inspection = exo.inspection();
1395+        assert_eq!(inspection.phase, ExoInspectionPhase::Ready);
1396+        assert_eq!(inspection.identity.as_ref(), Some(&identity));
1397+        assert!(
1398+            inspection.observed.is_some(),
1399+            "the workspace inspector did not retain the turn's exact preflight"
1400+        );
1401+        let turn = exo.turn();
1402+        assert_eq!(turn.phase, ExoTurnPhase::Completed, "{turn:?}");
1403+        assert!(turn.exo_session_id.is_some(), "{turn:?}");
1404+        assert!(turn.exo_turn_id.is_some(), "{turn:?}");
1405+        assert!(turn.latest_event_id.is_some(), "{turn:?}");
10881406         assert!(disclosure.agent_id.starts_with("exo/"), "{disclosure:?}");
10891407         // The parts that only the Exo arm of `classify_connection` can supply.
10901408         // Without them this assertion set passes on the shared external-agent
diff --git a/crates/omega_deltas/src/omega_deltas.rs b/crates/omega_deltas/src/omega_deltas.rs
index ee58b45193..6eaa9e4e8c 100644
--- a/crates/omega_deltas/src/omega_deltas.rs
+++ b/crates/omega_deltas/src/omega_deltas.rs
@@ -73,6 +73,7 @@ pub const ENFORCED_DELTAS: &[&str] = &[
7373     "OMEGA-DELTA-0043",
7474     "OMEGA-DELTA-0044",
7575     "OMEGA-DELTA-0045",
76+    "OMEGA-DELTA-0046",
7677 ];
7778 
7879 /// OMEGA-DELTA-0036. The uninstall script embedded in the shipped `cli`.
@@ -218,6 +219,9 @@ pub const THREAD_ENTRY_PATH: &str = "crates/acp_thread/src/acp_thread.rs";
218219 pub const SYSTEM_NOTE_REFUSAL: &str =
219220     "Agent threads do not expose an owner-visible system-note authority.";
220221 
222+/// OMEGA-DELTA-0046. The native Metal proof for the Exo workspace.
223+pub const VISUAL_TEST_RUNNER_PATH: &str = "crates/zed/src/visual_test_runner.rs";
224+
221225 /// OMEGA-DELTA-0030. The typed start command sent to `omega-effectd`.
222226 pub const FULL_AUTO_DISPATCH_PATH: &str = "crates/full_auto_ui/src/dispatch.rs";
223227 
@@ -6703,6 +6707,77 @@ mod tests {
67036707             disclosure_path.display()
67046708         );
67056709     }
6710+
6711+    // ------ OMEGA-DELTA-0046
6712+
6713+    /// OMEGA-DELTA-0046. An Exo thread is a usable workspace, not one label.
6714+    ///
6715+    /// The workspace must keep Omega's standard transcript and composer. Its
6716+    /// inspector must project facts from the same preflight that gates a turn.
6717+    /// Its controls must reuse the existing cancel and exact one-turn authority
6718+    /// paths. This source check catches the cheap failure modes: a mock panel,
6719+    /// a second message implementation, or controls that only look active.
6720+    #[test]
6721+    fn an_exo_thread_has_a_live_workspace_and_exact_runtime_inspector() {
6722+        let thread_path = repository_path(THREAD_VIEW_PATH);
6723+        let thread = std::fs::read_to_string(&thread_path)
6724+            .unwrap_or_else(|error| panic!("cannot read {}: {error}", thread_path.display()));
6725+        for token in [
6726+            "omega-exo-workspace-header",
6727+            "omega-exo-inspector",
6728+            "Runtime inspector",
6729+            "render_entries(cx)",
6730+            "render_message_editor(window, cx)",
6731+            "cancel_generation",
6732+            "refresh_exo_inspection",
6733+            "authorize_exo_self_modification",
6734+            "ObservedExoCapabilityState::requested_capabilities",
6735+        ] {
6736+            assert!(
6737+                thread.contains(token),
6738+                "OMEGA-DELTA-0046: the Exo workspace lost `{token}`"
6739+            );
6740+        }
6741+
6742+        let connection_path = repository_path(EXO_CONNECTION_PATH);
6743+        let connection = std::fs::read_to_string(&connection_path)
6744+            .unwrap_or_else(|error| panic!("cannot read {}: {error}", connection_path.display()));
6745+        for token in [
6746+            "ExoInspectionSnapshot",
6747+            "ExoTurnPhase",
6748+            "driver.observe().await",
6749+            "driver.preflight().await",
6750+            "acp.prompt(params",
6751+            "meta_value(meta, \"exo.session_id\")",
6752+            "meta_value(meta, \"exo.turn_id\")",
6753+            "meta_value(meta, \"exo.latest_event_id\")",
6754+        ] {
6755+            assert!(
6756+                connection.contains(token),
6757+                "OMEGA-DELTA-0046: the Exo workspace state lost `{token}`"
6758+            );
6759+        }
6760+
6761+        let visual_path = repository_path(VISUAL_TEST_RUNNER_PATH);
6762+        let visual = std::fs::read_to_string(&visual_path)
6763+            .unwrap_or_else(|error| panic!("cannot read {}: {error}", visual_path.display()));
6764+        for token in [
6765+            "OMEGA_EXO_VISUAL_ONLY",
6766+            "run_omega_exo_visual_tests",
6767+            "omega_exo_workspace_wide",
6768+            "omega_exo_workspace_narrow",
6769+            "the real Exo visual turn failed",
6770+            "turn.exo_session_id.is_some()",
6771+            "turn.exo_turn_id.is_some()",
6772+            "turn.latest_event_id.is_some()",
6773+        ] {
6774+            assert!(
6775+                visual.contains(token),
6776+                "OMEGA-DELTA-0046: the real Exo visual proof lost `{token}`"
6777+            );
6778+        }
6779+    }
6780+
67066781     // ---------------------------------------------------------------------
67076782     // OMEGA-DELTA-0041 — Omega Agent served over ACP on a loopback socket
67086783     // ---------------------------------------------------------------------
diff --git a/crates/zed/src/visual_test_runner.rs b/crates/zed/src/visual_test_runner.rs
index 9e87966647..82b1dbcd4e 100644
--- a/crates/zed/src/visual_test_runner.rs
+++ b/crates/zed/src/visual_test_runner.rs
@@ -124,6 +124,7 @@ use {
124124     acp_thread::{AgentConnection, StubAgentConnection},
125125     agent_client_protocol::schema::v1 as acp,
126126     agent_servers::{AgentServer, AgentServerDelegate},
127+    agent_ui::Agent,
127128     anyhow::{Context as _, Result},
128129     assets::Assets,
129130     editor::display_map::DisplayRow,
@@ -420,6 +421,28 @@ fn run_visual_tests(project_path: PathBuf, update_baseline: bool) -> Result<()>
420421     let mut failed = 0;
421422     let mut updated = 0;
422423 
424+    // `OMEGA_EXO_VISUAL_ONLY=1` runs the real Exo conversation workspace
425+    // proof and nothing else. The lane file must name an isolated Exo install.
426+    // This path connects over the shipped ACP stdio transport and sends a real
427+    // turn before it captures the wide and narrow layouts.
428+    #[cfg(feature = "visual-tests")]
429+    if std::env::var("OMEGA_EXO_VISUAL_ONLY").is_ok() {
430+        println!("\n--- Omega: real Exo conversation workspace ---");
431+        let outcome = run_omega_exo_visual_tests(app_state.clone(), &mut cx, update_baseline);
432+        teardown_shared_window(workspace_window, &mut cx);
433+        return match outcome {
434+            Ok(TestResult::Passed) => {
435+                println!("\u{2713} omega_exo_workspace: PASSED");
436+                Ok(())
437+            }
438+            Ok(TestResult::BaselineUpdated(_)) => {
439+                println!("\u{2713} omega_exo_workspace: Baselines updated");
440+                Ok(())
441+            }
442+            Err(error) => Err(error.context("omega_exo_workspace")),
443+        };
444+    }
445+
423446     // `OMEGA_VISUAL_ONLY=1` runs Omega's own suite and nothing else.
424447     //
425448     // This runner has no committed baselines for any of the inherited Zed
@@ -728,7 +751,10 @@ fn run_visual_tests(project_path: PathBuf, update_baseline: bool) -> Result<()>
728751             updated += 1;
729752         }
730753         Err(e) => {
731-            eprintln!("\u{2717} external_agent_harness_maintenance: FAILED - {}", e);
754+            eprintln!(
755+                "\u{2717} external_agent_harness_maintenance: FAILED - {}",
756+                e
757+            );
732758             failed += 1;
733759         }
734760     }
@@ -2174,7 +2200,9 @@ fn run_omega_agent_visual_tests_inner(
21742200                     ..Default::default()
21752201                 },
21762202                 |window, cx| {
2177-                    cx.new(|cx| Workspace::new(None, project.clone(), app_state.clone(), window, cx))
2203+                    cx.new(|cx| {
2204+                        Workspace::new(None, project.clone(), app_state.clone(), window, cx)
2205+                    })
21782206                 },
21792207             )
21802208         })
@@ -2768,7 +2796,9 @@ fn run_omega_restart_visual_tests_inner(
27682796                     ..Default::default()
27692797                 },
27702798                 |window, cx| {
2771-                    cx.new(|cx| Workspace::new(None, project.clone(), app_state.clone(), window, cx))
2799+                    cx.new(|cx| {
2800+                        Workspace::new(None, project.clone(), app_state.clone(), window, cx)
2801+                    })
27722802                 },
27732803             )
27742804         })
@@ -2965,6 +2995,321 @@ impl AgentServer for StubAgentServer {
29652995     }
29662996 }
29672997 
2998+/// A visual-test server that connects to one real, explicitly named Exo lane.
2999+///
3000+/// The server stores only the lane path so it remains `Send`, as
3001+/// [`AgentServer`] requires. The connection itself stays on the GPUI thread.
3002+#[derive(Clone)]
3003+#[cfg(target_os = "macos")]
3004+struct ExoVisualAgentServer {
3005+    lane_path: PathBuf,
3006+}
3007+
3008+#[cfg(target_os = "macos")]
3009+impl AgentServer for ExoVisualAgentServer {
3010+    fn logo(&self) -> ui::IconName {
3011+        ui::IconName::OmegaAssistant
3012+    }
3013+
3014+    fn agent_id(&self) -> AgentId {
3015+        "Exo".into()
3016+    }
3017+
3018+    fn connect(
3019+        &self,
3020+        _delegate: AgentServerDelegate,
3021+        project: Entity<Project>,
3022+        cx: &mut App,
3023+    ) -> gpui::Task<gpui::Result<Rc<dyn AgentConnection>>> {
3024+        let lane_path = self.lane_path.clone();
3025+        let agent_server_store = project.read(cx).agent_server_store().downgrade();
3026+        cx.spawn(async move |cx| {
3027+            agent_ui::omega_exo_connection::connect_configured_lane(
3028+                &lane_path,
3029+                project,
3030+                agent_server_store,
3031+                cx,
3032+            )
3033+            .await?
3034+            .ok_or_else(|| anyhow::anyhow!("the Exo visual lane file is not configured"))
3035+        })
3036+    }
3037+
3038+    fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
3039+        self
3040+    }
3041+}
3042+
3043+#[cfg(all(target_os = "macos", feature = "visual-tests"))]
3044+fn run_omega_exo_visual_tests(
3045+    app_state: Arc<AppState>,
3046+    cx: &mut VisualTestAppContext,
3047+    update_baseline: bool,
3048+) -> Result<TestResult> {
3049+    let lane_path = PathBuf::from(
3050+        std::env::var("OMEGA_EXO_VISUAL_LANE_FILE")
3051+            .context("set OMEGA_EXO_VISUAL_LANE_FILE to an isolated Exo lane file")?,
3052+    );
3053+    anyhow::ensure!(
3054+        lane_path.is_file(),
3055+        "the Exo visual lane does not exist: {}",
3056+        lane_path.display()
3057+    );
3058+
3059+    // Use independent native windows instead of resizing one hidden Metal
3060+    // window. The macOS platform does not dispatch a bounds callback for that
3061+    // test-only resize, so a second screenshot can silently retain the first
3062+    // viewport. Two fresh windows prove both responsive branches directly.
3063+    let wide = run_omega_exo_visual_capture(
3064+        app_state.clone(),
3065+        cx,
3066+        lane_path.clone(),
3067+        "omega_exo_workspace_wide",
3068+        size(px(1320.), px(860.)),
3069+        update_baseline,
3070+    )?;
3071+    let narrow = run_omega_exo_visual_capture(
3072+        app_state,
3073+        cx,
3074+        lane_path,
3075+        "omega_exo_workspace_narrow",
3076+        size(px(720.), px(900.)),
3077+        update_baseline,
3078+    )?;
3079+
3080+    for result in [&wide, &narrow] {
3081+        if let TestResult::BaselineUpdated(path) = result {
3082+            return Ok(TestResult::BaselineUpdated(path.clone()));
3083+        }
3084+    }
3085+    Ok(TestResult::Passed)
3086+}
3087+
3088+#[cfg(all(target_os = "macos", feature = "visual-tests"))]
3089+fn run_omega_exo_visual_capture(
3090+    app_state: Arc<AppState>,
3091+    cx: &mut VisualTestAppContext,
3092+    lane_path: PathBuf,
3093+    test_name: &'static str,
3094+    window_size: gpui::Size<gpui::Pixels>,
3095+    update_baseline: bool,
3096+) -> Result<TestResult> {
3097+    use agent_ui::AgentPanel;
3098+
3099+    let project_dir = tempfile::tempdir()?.keep().canonicalize()?;
3100+    let project = cx.update(|cx| {
3101+        Project::local(
3102+            app_state.client.clone(),
3103+            app_state.node_runtime.clone(),
3104+            app_state.user_store.clone(),
3105+            app_state.languages.clone(),
3106+            app_state.fs.clone(),
3107+            None,
3108+            project::LocalProjectFlags {
3109+                init_worktree_trust: false,
3110+                ..Default::default()
3111+            },
3112+            cx,
3113+        )
3114+    });
3115+    let add_worktree = project.update(cx, |project, cx| {
3116+        project.find_or_create_worktree(&project_dir, true, cx)
3117+    });
3118+    cx.background_executor.allow_parking();
3119+    cx.foreground_executor
3120+        .block_test(add_worktree)
3121+        .context("failed to add the Exo visual worktree")?;
3122+    cx.background_executor.forbid_parking();
3123+    cx.run_until_parked();
3124+
3125+    let bounds = Bounds {
3126+        origin: point(px(-10_000.), px(-10_000.)),
3127+        size: window_size,
3128+    };
3129+    let workspace_window: WindowHandle<Workspace> = cx
3130+        .update(|cx| {
3131+            cx.open_window(
3132+                WindowOptions {
3133+                    window_bounds: Some(WindowBounds::Windowed(bounds)),
3134+                    focus: false,
3135+                    show: true,
3136+                    ..Default::default()
3137+                },
3138+                |window, cx| {
3139+                    cx.new(|cx| {
3140+                        Workspace::new(None, project.clone(), app_state.clone(), window, cx)
3141+                    })
3142+                },
3143+            )
3144+        })
3145+        .context("failed to open the Exo workspace window")?;
3146+    cx.run_until_parked();
3147+
3148+    let (weak_workspace, async_window_cx) = workspace_window
3149+        .update(cx, |workspace, window, cx| {
3150+            (workspace.weak_handle(), window.to_async(cx))
3151+        })
3152+        .context("failed to get the Exo workspace handle")?;
3153+    cx.background_executor.allow_parking();
3154+    let panel = cx
3155+        .foreground_executor
3156+        .block_test(AgentPanel::load(weak_workspace, async_window_cx))
3157+        .context("failed to load the Exo Agent panel")?;
3158+    cx.background_executor.forbid_parking();
3159+
3160+    workspace_window
3161+        .update(cx, |workspace, window, cx| {
3162+            workspace.add_panel(panel.clone(), window, cx);
3163+            workspace.open_panel::<AgentPanel>(window, cx);
3164+            panel.update(cx, |panel, cx| {
3165+                use workspace::dock::Panel as _;
3166+                panel.set_zoomed(true, window, cx);
3167+            });
3168+        })
3169+        .context("failed to open the Exo Agent panel")?;
3170+    cx.run_until_parked();
3171+
3172+    // Keep capture failures inside this closure so the real ACP connection is
3173+    // always removed below. Otherwise an early assertion or screenshot error
3174+    // masks its own cause with GPUI's leaked ElicitationStore check.
3175+    let capture = (|| -> Result<TestResult> {
3176+        let server: Rc<dyn AgentServer> = Rc::new(ExoVisualAgentServer { lane_path });
3177+        cx.update_window(workspace_window.into(), |_, window, cx| {
3178+            panel.update(cx, |panel, cx| {
3179+                panel.open_external_thread_with_server(server, window, cx);
3180+            });
3181+        })?;
3182+
3183+        // Connecting the child process and completing ACP initialization
3184+        // require real I/O. Poll the visible state for at most five seconds;
3185+        // one `run_until_parked` can return before stdio initialization wakes
3186+        // the foreground executor.
3187+        cx.background_executor.allow_parking();
3188+        for _ in 0..100 {
3189+            cx.run_until_parked();
3190+            if cx.read(|cx| {
3191+                panel
3192+                    .read(cx)
3193+                    .active_thread_view_for_tests()
3194+                    .is_some_and(|view| view.read(cx).active_thread().is_some())
3195+            }) {
3196+                break;
3197+            }
3198+            std::thread::sleep(Duration::from_millis(50));
3199+        }
3200+        cx.background_executor.forbid_parking();
3201+
3202+        let thread_view = cx
3203+            .read(|cx| panel.read(cx).active_thread_view_for_tests().cloned())
3204+            .ok_or_else(|| anyhow::anyhow!("the Exo workspace has no active thread"))?;
3205+        let thread = cx
3206+            .read(|cx| {
3207+                thread_view
3208+                    .read(cx)
3209+                    .active_thread()
3210+                    .map(|active| active.read(cx).thread.clone())
3211+            })
3212+            .ok_or_else(|| anyhow::anyhow!("the Exo workspace thread is not available"))?;
3213+
3214+        let marker = "OMEGA-EXO-PANE-READY";
3215+        let prompt =
3216+            format!("Run one tool that prints OMEGA-EXO-TOOL, then reply with exactly {marker}.");
3217+        let send = thread.update(cx, |thread, cx| thread.send(vec![prompt.into()], cx));
3218+        cx.background_executor.allow_parking();
3219+        let send_result = cx.foreground_executor.block_test(send);
3220+        cx.background_executor.forbid_parking();
3221+        send_result.context("the real Exo visual turn failed")?;
3222+        cx.run_until_parked();
3223+
3224+        let (transcript, turn) = cx.read(|cx| {
3225+            let thread = thread.read(cx);
3226+            let connection = thread
3227+                .connection()
3228+                .clone()
3229+                .downcast::<agent_ui::omega_exo_connection::ExoHarnessConnection>()
3230+                .expect("the visual thread must retain its Exo connection");
3231+            (thread.to_markdown(cx), connection.turn())
3232+        });
3233+        anyhow::ensure!(
3234+            transcript.contains(marker),
3235+            "the real Exo response did not reach the transcript:\n{transcript}"
3236+        );
3237+        anyhow::ensure!(
3238+            transcript.contains("Tool Call: shell"),
3239+            "the real Exo tool call did not reach the transcript:\n{transcript}"
3240+        );
3241+        anyhow::ensure!(
3242+            transcript.contains("OMEGA-EXO-TOOL"),
3243+            "the real Exo tool result did not reach the transcript:\n{transcript}"
3244+        );
3245+        anyhow::ensure!(
3246+            turn.phase == agent_ui::omega_exo_connection::ExoTurnPhase::Completed,
3247+            "the real Exo turn did not complete: {turn:?}"
3248+        );
3249+        anyhow::ensure!(
3250+            turn.exo_session_id.is_some()
3251+                && turn.exo_turn_id.is_some()
3252+                && turn.latest_event_id.is_some(),
3253+            "the real Exo turn did not return durable references: {turn:?}"
3254+        );
3255+
3256+        let actual_size = cx.update_window(workspace_window.into(), |_, window, _cx| {
3257+            window.viewport_size()
3258+        })?;
3259+        anyhow::ensure!(
3260+            actual_size == window_size,
3261+            "{test_name} opened at {actual_size:?}, expected {window_size:?}"
3262+        );
3263+
3264+        run_visual_test(test_name, workspace_window.into(), cx, update_baseline)
3265+    })();
3266+
3267+    // Drop every owner of the real ACP connection before the runner's leaked
3268+    // entity check. The panel's connection store retains a successful custom
3269+    // connection after its thread and window close. Replacing that cached
3270+    // entry first lets the Exo child and its ElicitationStore terminate.
3271+    panel.update(cx, |panel, cx| {
3272+        panel.connection_store().update(cx, |store, cx| {
3273+            store.restart_connection(
3274+                Agent::Custom { id: "Exo".into() },
3275+                Rc::new(StubAgentServer::new(StubAgentConnection::new())),
3276+                cx,
3277+            );
3278+        });
3279+    });
3280+    cx.run_until_parked();
3281+
3282+    workspace_window
3283+        .update(cx, |workspace, window, cx| {
3284+            workspace.remove_panel(&panel, window, cx);
3285+            let project = workspace.project().clone();
3286+            project.update(cx, |project, cx| {
3287+                let worktree_ids: Vec<_> = project
3288+                    .worktrees(cx)
3289+                    .map(|worktree| worktree.read(cx).id())
3290+                    .collect();
3291+                for id in worktree_ids {
3292+                    project.remove_worktree(id, cx);
3293+                }
3294+            });
3295+        })
3296+        .log_err();
3297+    cx.run_until_parked();
3298+    drop(panel);
3299+    drop(project);
3300+    cx.update_window(workspace_window.into(), |_, window, _cx| {
3301+        window.remove_window();
3302+    })
3303+    .log_err();
3304+    cx.run_until_parked();
3305+    for _ in 0..15 {
3306+        cx.advance_clock(Duration::from_millis(100));
3307+        cx.run_until_parked();
3308+    }
3309+
3310+    capture
3311+}
3312+
29683313 #[cfg(all(target_os = "macos", feature = "visual-tests"))]
29693314 fn run_agent_thread_view_test(
29703315     app_state: Arc<AppState>,
@@ -4578,11 +4923,11 @@ fn run_external_agent_maintenance_visual_tests(
45784923     update_baseline: bool,
45794924 ) -> Result<TestResult> {
45804925     use ::collections::HashMap;
4926+    use gpui::UpdateGlobal as _;
45814927     use project::agent_registry_store::{
45824928         AgentRegistryStore, RegistryAgent, RegistryAgentMetadata, RegistryBinaryAgent,
45834929         RegistryTargetConfig,
45844930     };
4585-    use gpui::UpdateGlobal as _;
45864931     use settings::SettingsStore;
45874932 
45884933     const HEALTHY: &str = "omega-visual-harness";
@@ -4741,8 +5086,7 @@ fn run_external_agent_maintenance_visual_tests(
47415086     // has to be allowed to park while it waits for them. This test runs last.
47425087     cx.executor().allow_parking();
47435088 
4744-    let outcome: Arc<std::sync::Mutex<Option<Result<()>>>> =
4745-        Arc::new(std::sync::Mutex::new(None));
5089+    let outcome: Arc<std::sync::Mutex<Option<Result<()>>>> = Arc::new(std::sync::Mutex::new(None));
47465090     let setup = cx.update(|cx| {
47475091         let outcome = outcome.clone();
47485092         cx.background_spawn(async move {
diff --git a/crates/zed/test_fixtures/visual_tests/omega_exo_workspace_narrow.png b/crates/zed/test_fixtures/visual_tests/omega_exo_workspace_narrow.png
new file mode 100644
index 0000000000..03437abecc
Binary files /dev/null and b/crates/zed/test_fixtures/visual_tests/omega_exo_workspace_narrow.png differ
diff --git a/crates/zed/test_fixtures/visual_tests/omega_exo_workspace_wide.png b/crates/zed/test_fixtures/visual_tests/omega_exo_workspace_wide.png
new file mode 100644
index 0000000000..7185c43886
Binary files /dev/null and b/crates/zed/test_fixtures/visual_tests/omega_exo_workspace_wide.png differ
Served at tenant.openagents/omega Member data and write actions are omitted.