Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T00:54:51.422Z 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

b23a5d15 a6f8285b
diff --git a/OMEGA_DELTAS.md b/OMEGA_DELTAS.md
index 9bccab36ba..d80eb6f801 100644
--- a/OMEGA_DELTAS.md
+++ b/OMEGA_DELTAS.md
@@ -2315,3 +2315,63 @@ than it sounds, because the harness omega#81's acceptance sentence names —
23152315   literals fixed — failed the command-form test. Removing the rule from
23162316   `script/verify-omega-brand` while keeping it in Rust failed the parity
23172317   assertion. Each edit was probed in the file before its test ran.
2318+
2319+### OMEGA-DELTA-0045 — A provider handoff is visible in the thread the owner reads
2320+
2321+- **Upstream Zed:** `AgentThreadEntry` has six variants — a user message, an
2322+  assistant message, a tool call, an elicitation, a completed plan, a context
2323+  compaction. Every one of them is something a model or a user said. Correct for
2324+  Zed, which has no supervising host writing into a thread.
2325+- **Omega, before this:** Omega does have one. `omega-effectd` moves a Full Auto
2326+  run from one provider lane to another and emits a note naming both lanes,
2327+  addressed to the target thread. The host method it calls,
2328+  `omega_host_bridge::append_system_note`, validated its parameters and then
2329+  answered `unavailable("Agent threads do not expose an owner-visible
2330+  system-note authority.")` — because there was no entry kind a non-model
2331+  disclosure could be, so there was nowhere to put one.
2332+- **Why that is a defect and not a limitation.** `0.2.0-rc11` shipped a handoff
2333+  that changed **which model was spending the owner's budget** and left nothing
2334+  in the transcript the owner reads. The refusal is an improvement on rc11's
2335+  silent `() => {}` — it is typed, it is honest, and an operator reading the
2336+  wire can see the disclosure was dropped — but the owner reading the thread
2337+  cannot, and the owner reading the thread is the person the disclosure is for.
2338+  An independent reviewer confirmed the refusal string is in the shipped bytes
2339+  of `0.2.0-rc15` **and** `0.2.0-rc16`; it is in `rc17` too. No candidate to
2340+  date discloses a cross-provider handoff in the thread.
2341+- **Omega now:** a seventh variant, `AgentThreadEntry::SystemNote`, carrying an
2342+  engine-supplied id and plain text; `AcpThread::push_system_note`, idempotent
2343+  on that id; and `ThreadView::render_system_note`, which draws it as a
2344+  captioned rule in the transcript — the same shape the thread already uses for
2345+  "Subagent Output". `append_system_note` resolves the thread named by
2346+  `threadRef` and appends.
2347+- **Three properties the shape is chosen for.**
2348+  - **Unconditional.** No collapse, no expansion toggle, no hover. The gate is
2349+    owner *visibility*; anything the owner has to click to see is a disclosure
2350+    the rc11 handoff would also have passed.
2351+  - **Not Markdown.** The text is a `SharedString` the host wrote, drawn as a
2352+    `Label`. Nothing a provider emits can style one of these lines or pass
2353+    itself off as one.
2354+  - **Idempotent on the engine's id, not last-write-wins.** The engine may retry
2355+    after a response it never saw. A retry must not be able to rewrite a
2356+    disclosure the owner has already read, and must not double it either. The
2357+    idempotence is per live thread, which is the scope that matters: the note is
2358+    an entry in the thread, and a thread that is gone has no owner reading it.
2359+- **The cost, stated.** This is a variant added to `crates/acp_thread`, a shared
2360+  upstream crate, so it is a real rebase surface — unlike `OMEGA-DELTA-0021`,
2361+  which deliberately kept the executor record out of that crate behind an
2362+  extension trait. An extension trait cannot add an enum variant, and an
2363+  out-of-band side table keyed by entry index would not survive reordering, would
2364+  not appear in the thread's Markdown export, and would put the disclosure
2365+  somewhere other than where the transcript is read. The variant is the seam;
2366+  the rebase cost is accepted deliberately.
2367+- **Enforced by:** `a_host_authored_note_is_a_thread_entry_kind`,
2368+  `the_host_appends_a_provider_handoff_note_rather_than_refusing_it`, and
2369+  `the_thread_surface_draws_a_host_authored_note_unconditionally` in
2370+  `crates/omega_deltas/`. The three halves are separable and each one alone is
2371+  passable and useless: a variant nothing renders discloses nothing, a renderer
2372+  nothing dispatches to discloses nothing, and a host method that returns
2373+  `{"appended": true}` without writing anything is a refusal that lies rather
2374+  than a refusal that is honest.
2375+- **Scope.** This is the **source** half. `0.2.0-rc17` and every earlier
2376+  candidate carry the refusal in their shipped bytes; the packaged half needs
2377+  the next candidate.
diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs
index f457fe3afa..6b13f94ff8 100644
--- a/crates/acp_thread/src/acp_thread.rs
+++ b/crates/acp_thread/src/acp_thread.rs
@@ -393,6 +393,35 @@ pub enum AgentThreadEntry {
393393     Elicitation(ElicitationEntryId),
394394     CompletedPlan(Vec<PlanEntry>),
395395     ContextCompaction(ContextCompaction),
396+    /// OMEGA-DELTA-0045. A note the supervising host wrote into the thread so
397+    /// the owner reading it can see something that happened *to* the run
398+    /// rather than inside a model turn.
399+    SystemNote(SystemNote),
400+}
401+
402+/// OMEGA-DELTA-0045. Identity of a host-authored system note.
403+///
404+/// The engine supplies it, so a retried `append_system_note` — after a host
405+/// restart, or after a response the engine never saw — lands on the note that
406+/// is already there instead of writing a second copy of the same disclosure.
407+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
408+pub struct SystemNoteId(pub Arc<str>);
409+
410+/// OMEGA-DELTA-0045. A host-authored line in the thread.
411+///
412+/// This exists because of the rc11 defect: a cross-provider handoff changed
413+/// which model was spending the owner's budget and left nothing in the thread
414+/// the owner reads. `AgentThreadEntry` had no variant an owner-visible,
415+/// non-model disclosure could be, so the host had nowhere to put one and
416+/// refused instead. The refusal was honest; the missing seam was the defect.
417+///
418+/// The text is plain, not Markdown, and not model output: it is written by the
419+/// host and rendered as-is, so nothing a provider emits can style, hide, or
420+/// impersonate it.
421+#[derive(Debug, Clone)]
422+pub struct SystemNote {
423+    pub id: SystemNoteId,
424+    pub text: SharedString,
396425 }
397426 
398427 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -785,6 +814,7 @@ impl AgentThreadEntry {
785814             Self::Elicitation(_) => false,
786815             Self::CompletedPlan(_) => false,
787816             Self::ContextCompaction(_) => false,
817+            Self::SystemNote(_) => false,
788818         }
789819     }
790820 
@@ -803,6 +833,7 @@ impl AgentThreadEntry {
803833                 md
804834             }
805835             Self::ContextCompaction(_) => "--- Context Compacted ---\n\n".to_string(),
836+            Self::SystemNote(note) => format!("--- {} ---\n\n", note.text),
806837         }
807838     }
808839 
@@ -2485,7 +2516,8 @@ impl AcpThread {
24852516                 | AgentThreadEntry::Elicitation(_)
24862517                 | AgentThreadEntry::AssistantMessage(_)
24872518                 | AgentThreadEntry::CompletedPlan(_)
2488-                | AgentThreadEntry::ContextCompaction(_) => {}
2519+                | AgentThreadEntry::ContextCompaction(_)
2520+                | AgentThreadEntry::SystemNote(_) => {}
24892521             }
24902522         }
24912523         false
@@ -2515,7 +2547,8 @@ impl AcpThread {
25152547                 | AgentThreadEntry::Elicitation(_)
25162548                 | AgentThreadEntry::AssistantMessage(_)
25172549                 | AgentThreadEntry::CompletedPlan(_)
2518-                | AgentThreadEntry::ContextCompaction(_) => {}
2550+                | AgentThreadEntry::ContextCompaction(_)
2551+                | AgentThreadEntry::SystemNote(_) => {}
25192552             }
25202553         }
25212554 
@@ -2536,7 +2569,8 @@ impl AcpThread {
25362569                 | AgentThreadEntry::Elicitation(_)
25372570                 | AgentThreadEntry::AssistantMessage(_)
25382571                 | AgentThreadEntry::CompletedPlan(_)
2539-                | AgentThreadEntry::ContextCompaction(_) => {}
2572+                | AgentThreadEntry::ContextCompaction(_)
2573+                | AgentThreadEntry::SystemNote(_) => {}
25402574             }
25412575         }
25422576 
@@ -2550,6 +2584,7 @@ impl AcpThread {
25502584                 AgentThreadEntry::AssistantMessage(..)
25512585                 | AgentThreadEntry::CompletedPlan(..)
25522586                 | AgentThreadEntry::ContextCompaction(_)
2587+                | AgentThreadEntry::SystemNote(_)
25532588                 | AgentThreadEntry::Elicitation(_) => continue,
25542589                 AgentThreadEntry::ToolCall(..) => return true,
25552590             }
@@ -3015,6 +3050,24 @@ impl AcpThread {
30153050         cx.emit(AcpThreadEvent::NewEntry);
30163051     }
30173052 
3053+    /// OMEGA-DELTA-0045. Append a host-authored note to the thread the owner
3054+    /// reads. Returns `false` when a note with this id is already present.
3055+    ///
3056+    /// Idempotent on the id rather than last-write-wins: the note is a record
3057+    /// that something happened, so a retry must not be able to rewrite what
3058+    /// the owner was already shown, and must not double it either.
3059+    pub fn push_system_note(&mut self, note: SystemNote, cx: &mut Context<Self>) -> bool {
3060+        if self
3061+            .entries
3062+            .iter()
3063+            .any(|entry| matches!(entry, AgentThreadEntry::SystemNote(existing) if existing.id == note.id))
3064+        {
3065+            return false;
3066+        }
3067+        self.push_entry(AgentThreadEntry::SystemNote(note), cx);
3068+        true
3069+    }
3070+
30183071     pub fn push_context_compaction(
30193072         &mut self,
30203073         compaction: ContextCompaction,
@@ -10075,4 +10128,81 @@ mod tests {
1007510128             "running_turn must be cleared even when tx was dropped without send"
1007610129         );
1007710130     }
10131+
10132+    /// OMEGA-DELTA-0045. A host-authored note lands in the transcript, once.
10133+    ///
10134+    /// The source check in `crates/omega_deltas` reads the shape; this runs it.
10135+    /// Both halves matter: the shape check would pass an implementation that
10136+    /// keyed on the id and appended anyway, and a behavioural test alone would
10137+    /// pass an implementation nothing renders.
10138+    #[gpui::test]
10139+    async fn test_system_note_is_appended_once_per_note_id(cx: &mut TestAppContext) {
10140+        init_test(cx);
10141+
10142+        let fs = FakeFs::new(cx.executor());
10143+        let project = Project::test(fs, [], cx).await;
10144+        let connection = Rc::new(FakeAgentConnection::new());
10145+        let thread = cx
10146+            .update(|cx| {
10147+                connection.new_session(project, PathList::new(&[Path::new(path!("/test"))]), cx)
10148+            })
10149+            .await
10150+            .unwrap();
10151+
10152+        let note = |id: &str, text: &str| SystemNote {
10153+            id: SystemNoteId(id.into()),
10154+            text: text.into(),
10155+        };
10156+
10157+        thread.update(cx, |thread, cx| {
10158+            assert!(
10159+                thread.push_system_note(
10160+                    note("handoff.provider.1", "Provider handoff: codex-local to claude-local"),
10161+                    cx,
10162+                ),
10163+                "the first append of a note id must land"
10164+            );
10165+            assert!(
10166+                !thread.push_system_note(
10167+                    note("handoff.provider.1", "Provider handoff: rewritten"),
10168+                    cx,
10169+                ),
10170+                "a retry of the same note id must not append again, and must \
10171+                 not rewrite what the owner has already read"
10172+            );
10173+            assert!(
10174+                thread.push_system_note(
10175+                    note("handoff.provider.2", "Provider handoff: claude-local to codex-local"),
10176+                    cx,
10177+                ),
10178+                "a different note id is a different disclosure"
10179+            );
10180+        });
10181+
10182+        thread.read_with(cx, |thread, cx| {
10183+            let notes: Vec<&SystemNote> = thread
10184+                .entries()
10185+                .iter()
10186+                .filter_map(|entry| match entry {
10187+                    AgentThreadEntry::SystemNote(note) => Some(note),
10188+                    _ => None,
10189+                })
10190+                .collect();
10191+            assert_eq!(notes.len(), 2, "one entry per distinct note id");
10192+            assert_eq!(
10193+                notes[0].text.as_ref(),
10194+                "Provider handoff: codex-local to claude-local",
10195+                "the retry must not have rewritten the first disclosure"
10196+            );
10197+
10198+            // The transcript the owner copies out carries it too. A note that
10199+            // is on screen and absent from the export would let the silence
10200+            // rc11 shipped come back the moment anyone quotes a thread.
10201+            let markdown = thread.to_markdown(cx);
10202+            assert!(
10203+                markdown.contains("Provider handoff: codex-local to claude-local"),
10204+                "the exported transcript must carry the disclosure: {markdown}"
10205+            );
10206+        });
10207+    }
1007810208 }
diff --git a/crates/agent_servers/src/acp.rs b/crates/agent_servers/src/acp.rs
index 9d432d0895..28c8870e45 100644
--- a/crates/agent_servers/src/acp.rs
+++ b/crates/agent_servers/src/acp.rs
@@ -4158,6 +4158,7 @@ mod tests {
41584158                     acp_thread::AgentThreadEntry::Elicitation(_) => "elicitation",
41594159                     acp_thread::AgentThreadEntry::CompletedPlan(_) => "plan",
41604160                     acp_thread::AgentThreadEntry::ContextCompaction(_) => "compaction",
4161+                    acp_thread::AgentThreadEntry::SystemNote(_) => "system_note",
41614162                 })
41624163                 .collect::<Vec<_>>()
41634164         });
diff --git a/crates/agent_ui/src/conversation_view/thread_search_bar.rs b/crates/agent_ui/src/conversation_view/thread_search_bar.rs
index 643cceda36..6f1e58d129 100644
--- a/crates/agent_ui/src/conversation_view/thread_search_bar.rs
+++ b/crates/agent_ui/src/conversation_view/thread_search_bar.rs
@@ -948,6 +948,10 @@ fn collect_markdowns(
948948         }
949949         AgentThreadEntry::Elicitation(_) => {}
950950         AgentThreadEntry::ContextCompaction(_) => {}
951+        // OMEGA-DELTA-0045. A system note carries no `Entity<Markdown>`; its
952+        // text is a plain `SharedString` the host wrote, so there is nothing
953+        // here for the Markdown-backed search index to collect.
954+        AgentThreadEntry::SystemNote(_) => {}
951955     }
952956     out
953957 }
diff --git a/crates/agent_ui/src/conversation_view/thread_view.rs b/crates/agent_ui/src/conversation_view/thread_view.rs
index 7ebd4f03a9..353391c30a 100644
--- a/crates/agent_ui/src/conversation_view/thread_view.rs
+++ b/crates/agent_ui/src/conversation_view/thread_view.rs
@@ -3938,6 +3938,46 @@ impl ThreadView {
39383938             .into_any()
39393939     }
39403940 
3941+    /// OMEGA-DELTA-0045. Draw a host-authored note in the thread.
3942+    ///
3943+    /// The gate this satisfies is owner *visibility*, so the note is rendered
3944+    /// unconditionally: no collapse, no expansion toggle, no hover reveal.
3945+    /// Anything the owner has to click to see is a disclosure the rc11 handoff
3946+    /// would also have passed.
3947+    ///
3948+    /// `note.text` is a plain string written by the host and drawn as a
3949+    /// `Label`, not parsed as Markdown, so no provider-supplied content can
3950+    /// style it or pass itself off as one of these lines.
3951+    ///
3952+    /// The shape is the same rule-with-a-caption the thread already uses for
3953+    /// "Subagent Output": a line the eye reads as structure rather than as
3954+    /// something a model said.
3955+    fn render_system_note(&self, entry_ix: usize, note: &acp_thread::SystemNote) -> AnyElement {
3956+        h_flex()
3957+            .id(("system-note", entry_ix))
3958+            .w_full()
3959+            .px_5()
3960+            .py_1()
3961+            .gap_2()
3962+            .child(Divider::horizontal())
3963+            .child(
3964+                h_flex()
3965+                    .gap_1()
3966+                    .child(
3967+                        Icon::new(IconName::Info)
3968+                            .color(Color::Muted)
3969+                            .size(IconSize::Small),
3970+                    )
3971+                    .child(
3972+                        Label::new(note.text.clone())
3973+                            .size(LabelSize::Small)
3974+                            .color(Color::Muted),
3975+                    ),
3976+            )
3977+            .child(Divider::horizontal())
3978+            .into_any_element()
3979+    }
3980+
39413981     fn render_context_compaction(
39423982         &self,
39433983         entry_ix: usize,
@@ -6438,6 +6478,7 @@ impl ThreadView {
64386478             AgentThreadEntry::ContextCompaction(compaction) => {
64396479                 self.render_context_compaction(entry_ix, compaction, window, cx)
64406480             }
6481+            AgentThreadEntry::SystemNote(note) => self.render_system_note(entry_ix, note),
64416482         };
64426483 
64436484         let is_subagent_output = self.is_subagent()
@@ -7728,7 +7769,8 @@ impl ThreadView {
77287769                 | AgentThreadEntry::Elicitation(_)
77297770                 | AgentThreadEntry::AssistantMessage(_)
77307771                 | AgentThreadEntry::CompletedPlan(_)
7731-                | AgentThreadEntry::ContextCompaction(_) => {}
7772+                | AgentThreadEntry::ContextCompaction(_)
7773+                | AgentThreadEntry::SystemNote(_) => {}
77327774             }
77337775         }
77347776 
diff --git a/crates/agent_ui/src/entry_view_state.rs b/crates/agent_ui/src/entry_view_state.rs
index 092450b0fb..2bf17e5a3a 100644
--- a/crates/agent_ui/src/entry_view_state.rs
+++ b/crates/agent_ui/src/entry_view_state.rs
@@ -420,6 +420,13 @@ impl EntryViewState {
420420                     self.set_entry(index, Entry::ContextCompaction);
421421                 }
422422             }
423+            // OMEGA-DELTA-0045. A host-authored note has no editor, no tool
424+            // content and no focusable body — it is a rendered line of text.
425+            AgentThreadEntry::SystemNote(_) => {
426+                if !matches!(self.entries.get(index), Some(Entry::SystemNote)) {
427+                    self.set_entry(index, Entry::SystemNote);
428+                }
429+            }
423430         };
424431     }
425432 
@@ -467,7 +474,8 @@ impl EntryViewState {
467474                 | Entry::AssistantMessage { .. }
468475                 | Entry::Elicitation { .. }
469476                 | Entry::CompletedPlan
470-                | Entry::ContextCompaction => {}
477+                | Entry::ContextCompaction
478+                | Entry::SystemNote => {}
471479                 Entry::ToolCall(ToolCallEntry { content, .. }) => {
472480                     for view in content.values() {
473481                         if let Ok(diff_editor) = view.clone().downcast::<Editor>() {
@@ -538,6 +546,8 @@ pub enum Entry {
538546     Elicitation { focus_handle: FocusHandle },
539547     CompletedPlan,
540548     ContextCompaction,
549+    /// OMEGA-DELTA-0045. The view side of [`AgentThreadEntry::SystemNote`].
550+    SystemNote,
541551 }
542552 
543553 impl Entry {
@@ -547,7 +557,7 @@ impl Entry {
547557             Self::AssistantMessage(message) => Some(message.focus_handle.clone()),
548558             Self::ToolCall(tool_call) => Some(tool_call.focus_handle.clone()),
549559             Self::Elicitation { focus_handle } => Some(focus_handle.clone()),
550-            Self::CompletedPlan | Self::ContextCompaction => None,
560+            Self::CompletedPlan | Self::ContextCompaction | Self::SystemNote => None,
551561         }
552562     }
553563 
@@ -558,7 +568,8 @@ impl Entry {
558568             | Self::ToolCall(_)
559569             | Self::Elicitation { .. }
560570             | Self::CompletedPlan
561-            | Self::ContextCompaction => None,
571+            | Self::ContextCompaction
572+            | Self::SystemNote => None,
562573         }
563574     }
564575 
@@ -589,7 +600,8 @@ impl Entry {
589600             | Self::ToolCall(_)
590601             | Self::Elicitation { .. }
591602             | Self::CompletedPlan
592-            | Self::ContextCompaction => None,
603+            | Self::ContextCompaction
604+            | Self::SystemNote => None,
593605         }
594606     }
595607 
@@ -608,7 +620,8 @@ impl Entry {
608620             | Self::AssistantMessage(_)
609621             | Self::Elicitation { .. }
610622             | Self::CompletedPlan
611-            | Self::ContextCompaction => false,
623+            | Self::ContextCompaction
624+            | Self::SystemNote => false,
612625         }
613626     }
614627 }
@@ -626,7 +639,7 @@ impl Focusable for Entry {
626639             Self::AssistantMessage(message) => message.focus_handle.clone(),
627640             Self::ToolCall(tool_call) => tool_call.focus_handle.clone(),
628641             Self::Elicitation { focus_handle } => focus_handle.clone(),
629-            Self::CompletedPlan | Self::ContextCompaction => cx.focus_handle(),
642+            Self::CompletedPlan | Self::ContextCompaction | Self::SystemNote => cx.focus_handle(),
630643         }
631644     }
632645 }
diff --git a/crates/agent_ui/src/omega_host_bridge.rs b/crates/agent_ui/src/omega_host_bridge.rs
index 60555bac20..f0f7bc1c01 100644
--- a/crates/agent_ui/src/omega_host_bridge.rs
+++ b/crates/agent_ui/src/omega_host_bridge.rs
@@ -515,7 +515,7 @@ async fn handle_request(
515515             refresh_evidence(request.params, request.generation, &state, &cx)
516516         }
517517         HostMethod::InterruptTurn => interrupt_turn(request.params, &state, &mut cx).await,
518-        HostMethod::AppendSystemNote => append_system_note(request.params),
518+        HostMethod::AppendSystemNote => append_system_note(request.params, &state, &mut cx).await,
519519         HostMethod::SarahSessionStatus
520520         | HostMethod::SarahBootstrap
521521         | HostMethod::SarahRoomSnapshot
@@ -1163,16 +1163,61 @@ async fn interrupt_turn(
11631163     Ok(json!({ "interrupted": true }))
11641164 }
11651165 
1166-fn append_system_note(params: Value) -> Result<Value, HostResponseError> {
1166+/// OMEGA-DELTA-0045. Write a host-authored note into the thread the owner
1167+/// reads.
1168+///
1169+/// This used to refuse — `unavailable("Agent threads do not expose an
1170+/// owner-visible system-note authority.")` — because `AgentThreadEntry` had no
1171+/// variant a non-model disclosure could be. The refusal was typed and honest,
1172+/// and it was still the rc11 silence: the engine emits a provider-handoff note
1173+/// naming both lanes, the host dropped it, and a run that changed which model
1174+/// was spending the owner's budget left nothing in the transcript. FA-07 gate
1175+/// 5 exists to forbid exactly that.
1176+///
1177+/// The note goes to the thread named in `threadRef` and nowhere else, so a
1178+/// handoff addressed to the *target* thread cannot be filed against the source
1179+/// one. `noteRef` makes the append idempotent: the engine may retry after a
1180+/// response it never saw, and the owner must not be shown the same disclosure
1181+/// twice — nor a rewritten one, which is why the id wins over the newer text.
1182+///
1183+/// The idempotence is scoped to the live thread, not to the correlation
1184+/// journal. That is the scope that matters: the note is an entry in the
1185+/// thread, so a thread that is gone has no owner reading it and nothing to
1186+/// double.
1187+async fn append_system_note(
1188+    params: Value,
1189+    state: &Rc<RefCell<HostBridgeState>>,
1190+    cx: &mut AsyncApp,
1191+) -> Result<Value, HostResponseError> {
11671192     let params: AppendSystemNoteParams = decode_params(params)?;
11681193     validate_ref(&params.thread_ref, "threadRef")?;
11691194     validate_ref(&params.note_ref, "noteRef")?;
11701195     if params.text.is_empty() {
11711196         return Err(invalid("text must not be empty."));
11721197     }
1173-    Err(unavailable(
1174-        "Agent threads do not expose an owner-visible system-note authority.",
1175-    ))
1198+    let conversation = {
1199+        let bridge = state.borrow();
1200+        bridge
1201+            .threads
1202+            .iter()
1203+            .find(|thread| thread.thread_id.to_key_string() == params.thread_ref)
1204+            .ok_or_else(|| unavailable("The requested Agent thread is not bound."))?
1205+            .conversation
1206+            .as_ref()
1207+            .and_then(WeakEntity::upgrade)
1208+            .ok_or_else(|| unavailable("The requested Agent thread was closed."))?
1209+    };
1210+    let thread = wait_for_root_thread(&conversation, cx).await?;
1211+    let appended = thread.update(cx, |thread, cx| {
1212+        thread.push_system_note(
1213+            acp_thread::SystemNote {
1214+                id: acp_thread::SystemNoteId(params.note_ref.as_str().into()),
1215+                text: params.text.into(),
1216+            },
1217+            cx,
1218+        )
1219+    });
1220+    Ok(json!({ "appended": appended }))
11761221 }
11771222 
11781223 async fn wait_for_root_thread(
diff --git a/crates/omega_deltas/src/omega_deltas.rs b/crates/omega_deltas/src/omega_deltas.rs
index 05ead6f607..ee58b45193 100644
--- a/crates/omega_deltas/src/omega_deltas.rs
+++ b/crates/omega_deltas/src/omega_deltas.rs
@@ -72,6 +72,7 @@ pub const ENFORCED_DELTAS: &[&str] = &[
7272     "OMEGA-DELTA-0042",
7373     "OMEGA-DELTA-0043",
7474     "OMEGA-DELTA-0044",
75+    "OMEGA-DELTA-0045",
7576 ];
7677 
7778 /// OMEGA-DELTA-0036. The uninstall script embedded in the shipped `cli`.
@@ -201,6 +202,22 @@ pub const EXECUTOR_DISCLOSURE_BINDING_PATH: &str =
201202 /// OMEGA-DELTA-0021. The thread surface that has to render the line.
202203 pub const THREAD_VIEW_PATH: &str = "crates/agent_ui/src/conversation_view/thread_view.rs";
203204 
205+/// OMEGA-DELTA-0045. The host method the engine calls to disclose a handoff.
206+pub const HOST_BRIDGE_PATH: &str = "crates/agent_ui/src/omega_host_bridge.rs";
207+
208+/// OMEGA-DELTA-0045. The shared thread crate that carries the entry kinds.
209+pub const THREAD_ENTRY_PATH: &str = "crates/acp_thread/src/acp_thread.rs";
210+
211+/// OMEGA-DELTA-0045. The refusal that shipped in every candidate through
212+/// `0.2.0-rc17`.
213+///
214+/// Named as a literal so the check fails on the exact bytes an independent
215+/// reviewer found in the shipped binary, rather than on a paraphrase. The
216+/// prose in `omega_host_bridge.rs` still quotes it in order to say why it is
217+/// gone, so the scan reads `code_of` and not the raw file.
218+pub const SYSTEM_NOTE_REFUSAL: &str =
219+    "Agent threads do not expose an owner-visible system-note authority.";
220+
204221 /// OMEGA-DELTA-0030. The typed start command sent to `omega-effectd`.
205222 pub const FULL_AUTO_DISPATCH_PATH: &str = "crates/full_auto_ui/src/dispatch.rs";
206223 
@@ -6896,4 +6913,178 @@ mod tests {
68966913             "OMEGA-DELTA-0041: the served surface is started after the              packaged component is resolved, so whether it listens depends on              packaging as well as on the flag."
68976914         );
68986915     }
6916+
6917+    // ------------------------------------------------------ OMEGA-DELTA-0045
6918+
6919+    /// OMEGA-DELTA-0045. A host-authored note is an entry kind, not a caption.
6920+    ///
6921+    /// `AgentThreadEntry` had six variants and every one of them was something
6922+    /// a model or a user said. There was nowhere to put a line the *host*
6923+    /// wrote, so the host had nothing to write into and refused. The variant is
6924+    /// the seam; without it the refusal is the only honest answer, which is
6925+    /// exactly the state rc11 through rc17 shipped.
6926+    ///
6927+    /// `push_system_note` is asserted to return `bool` and to be keyed on the
6928+    /// engine-supplied id. Last-write-wins would let a retry rewrite a
6929+    /// disclosure the owner had already been shown; an unkeyed append would
6930+    /// show it twice.
6931+    #[test]
6932+    fn a_host_authored_note_is_a_thread_entry_kind() {
6933+        let path = repository_path(THREAD_ENTRY_PATH);
6934+        let source = std::fs::read_to_string(&path)
6935+            .unwrap_or_else(|error| panic!("cannot read {}: {error}", path.display()));
6936+
6937+        let (_, body, _) = next_enum_body(&source, "AgentThreadEntry").unwrap_or_else(|| {
6938+            panic!(
6939+                "OMEGA-DELTA-0045: no AgentThreadEntry enum found in {}. The \
6940+                 check would be vacuous, so it fails instead.",
6941+                path.display()
6942+            )
6943+        });
6944+        assert!(
6945+            body.lines()
6946+                .map(str::trim)
6947+                .any(|line| line == "SystemNote(SystemNote),"),
6948+            "OMEGA-DELTA-0045: AgentThreadEntry has no SystemNote variant in \
6949+             {}. Without an entry kind a non-model disclosure can be, the host \
6950+             has nowhere to write a provider handoff and the thread the owner \
6951+             reads goes silent — the rc11 defect FA-07 gate 5 forbids.",
6952+            path.display()
6953+        );
6954+
6955+        let code = code_of(&source);
6956+        assert!(
6957+            code.contains(
6958+                "pub fn push_system_note(&mut self, note: SystemNote, cx: &mut Context<Self>) -> bool"
6959+            ),
6960+            "OMEGA-DELTA-0045: {} must expose push_system_note returning \
6961+             whether it appended.",
6962+            path.display()
6963+        );
6964+        assert!(
6965+            code.contains("existing.id == note.id"),
6966+            "OMEGA-DELTA-0045: push_system_note in {} no longer keys on the \
6967+             engine-supplied note id. Unkeyed, a retried append shows the owner \
6968+             the same disclosure twice; last-write-wins lets a retry rewrite a \
6969+             disclosure the owner has already read.",
6970+            path.display()
6971+        );
6972+    }
6973+
6974+    /// OMEGA-DELTA-0045. The host writes the note instead of refusing it.
6975+    ///
6976+    /// The refusal was typed and honest — better than rc11's silent
6977+    /// `() => {}` — and it was still silence in the place that matters. An
6978+    /// independent reviewer found `SYSTEM_NOTE_REFUSAL` in the shipped bytes of
6979+    /// both `0.2.0-rc15` and `0.2.0-rc16`, so no candidate to date discloses a
6980+    /// cross-provider handoff to the owner reading the thread.
6981+    ///
6982+    /// Two halves, because either alone is passable and useless: the refusal is
6983+    /// gone, and the method reaches `push_system_note` on the thread named by
6984+    /// `threadRef`. A handoff is addressed to the *target* thread; filing it
6985+    /// against the source one would put the disclosure where the owner is no
6986+    /// longer reading.
6987+    #[test]
6988+    fn the_host_appends_a_provider_handoff_note_rather_than_refusing_it() {
6989+        let path = repository_path(HOST_BRIDGE_PATH);
6990+        let source = std::fs::read_to_string(&path)
6991+            .unwrap_or_else(|error| panic!("cannot read {}: {error}", path.display()));
6992+        let code = code_of(&source);
6993+
6994+        assert!(
6995+            !code.contains(SYSTEM_NOTE_REFUSAL),
6996+            "OMEGA-DELTA-0045: {} still refuses the system-note method with \
6997+             {SYSTEM_NOTE_REFUSAL:?}. That refusal is the rc11 silence in a \
6998+             typed wrapper: the engine emits a provider-handoff note naming \
6999+             both lanes and the host drops it, so a run that changed which \
7000+             model spends the owner's budget leaves no trace in the transcript.",
7001+            path.display()
7002+        );
7003+        assert!(
7004+            code.contains("HostMethod::AppendSystemNote => append_system_note("),
7005+            "OMEGA-DELTA-0045: {} no longer routes AppendSystemNote to \
7006+             append_system_note.",
7007+            path.display()
7008+        );
7009+        assert!(
7010+            code.contains("thread.push_system_note("),
7011+            "OMEGA-DELTA-0045: append_system_note in {} does not reach \
7012+             push_system_note. A method that validates its params and returns \
7013+             `{{\"appended\": true}}` without writing anything is a refusal \
7014+             that lies rather than a refusal that is honest.",
7015+            path.display()
7016+        );
7017+        assert!(
7018+            code.contains("thread.thread_id.to_key_string() == params.thread_ref"),
7019+            "OMEGA-DELTA-0045: append_system_note in {} no longer resolves the \
7020+             thread named by threadRef. A handoff is addressed to the target \
7021+             thread; filing it against whichever thread is nearest puts the \
7022+             disclosure where the owner has stopped reading.",
7023+            path.display()
7024+        );
7025+    }
7026+
7027+    /// OMEGA-DELTA-0045. The thread surface draws the note, unconditionally.
7028+    ///
7029+    /// An entry kind nothing renders satisfies every other check here and
7030+    /// discloses nothing, which is the same failure shape omega#77 pinned for
7031+    /// the executor line. So the call site is pinned as well as the variant.
7032+    ///
7033+    /// And it is pinned as an *unconditional* draw. The gate is owner
7034+    /// visibility; a note behind a disclosure triangle, a hover, or a collapsed
7035+    /// section is a note the rc11 handoff would also have passed. The body is
7036+    /// read for the expansion vocabulary the compaction entry uses, because
7037+    /// that is the nearest thing in this file to copy by accident.
7038+    #[test]
7039+    fn the_thread_surface_draws_a_host_authored_note_unconditionally() {
7040+        let path = repository_path(THREAD_VIEW_PATH);
7041+        let source = std::fs::read_to_string(&path)
7042+            .unwrap_or_else(|error| panic!("cannot read {}: {error}", path.display()));
7043+        let code = code_of(&source);
7044+
7045+        assert!(
7046+            code.contains("fn render_system_note("),
7047+            "OMEGA-DELTA-0045: {} must define the host-authored note line.",
7048+            path.display()
7049+        );
7050+        assert!(
7051+            code.contains(
7052+                "AgentThreadEntry::SystemNote(note) => self.render_system_note(entry_ix, note),"
7053+            ),
7054+            "OMEGA-DELTA-0045: {} must draw a SystemNote entry from the entry \
7055+             match. Defining the renderer without dispatching to it discloses \
7056+             nothing, and every other check in this delta would still pass.",
7057+            path.display()
7058+        );
7059+
7060+        let start = code
7061+            .find("fn render_system_note(")
7062+            .expect("the renderer was just asserted to exist");
7063+        let body = &code[start..];
7064+        let end = body.find("\n    fn ").expect(
7065+            "OMEGA-DELTA-0045: no method follows render_system_note, so its \
7066+             body cannot be bounded and the scan below would read the rest of \
7067+             the file",
7068+        );
7069+        let body = &body[..end];
7070+
7071+        assert!(
7072+            body.contains("Label::new(note.text.clone())"),
7073+            "OMEGA-DELTA-0045: the note line in {} must be drawn from the \
7074+             entry's own text as a Label. Rendering it as Markdown would let \
7075+             provider-supplied content style a host-authored disclosure or pass \
7076+             itself off as one.",
7077+            path.display()
7078+        );
7079+        for hideable in ["is_expanded", "expansion", "toggle", "tooltip", "hover"] {
7080+            assert!(
7081+                !body.contains(hideable),
7082+                "OMEGA-DELTA-0045: the note line in {} names {hideable:?}. The \
7083+                 gate is owner visibility, and anything the owner has to click, \
7084+                 expand, or hover to see is a disclosure the rc11 handoff would \
7085+                 also have passed.",
7086+                path.display()
7087+            );
7088+        }
7089+    }
68997090 }
Served at tenant.openagents/omega Member data and write actions are omitted.