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 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitRevision diff
b23a5d15 → a6f8285bdiff --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(¶ms.thread_ref, "threadRef")?;
11691194 validate_ref(¶ms.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 }