Skip to repository content264 lines · 9.2 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T01:58:32.543Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
render.rs
1//! Render public-safe inspector and room-header field rows.
2//!
3//! Output is structured label/value pairs for GPUI or tests. Values never
4//! include raw tokens, private paths, or unbounded tool content.
5
6use crate::public_ref::PublicRef;
7use crate::receipt::AuthorityReceiptDetail;
8use crate::room_header::RoomAuthorityHeader;
9
10/// Conversation header title per spec §9.6 — name only, no authority details.
11pub const CONVERSATION_HEADER_TITLE: &str = "Sarah";
12
13/// One label/value row in the inspector or room header strip.
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct InspectorField {
16 pub label: &'static str,
17 pub value: String,
18}
19
20impl InspectorField {
21 pub fn new(label: &'static str, value: impl Into<String>) -> Self {
22 Self {
23 label,
24 value: value.into(),
25 }
26 }
27
28 pub fn line(&self) -> String {
29 format!("{}: {}", self.label, self.value)
30 }
31}
32
33/// Details view for one activity/receipt row.
34#[derive(Clone, Debug, PartialEq, Eq)]
35pub struct ReceiptInspectorView {
36 pub fields: Vec<InspectorField>,
37 pub allowed: bool,
38}
39
40impl ReceiptInspectorView {
41 pub fn lines(&self) -> Vec<String> {
42 self.fields.iter().map(InspectorField::line).collect()
43 }
44
45 pub fn joined_text(&self) -> String {
46 self.lines().join("\n")
47 }
48
49 pub fn field_value(&self, label: &str) -> Option<&str> {
50 self.fields
51 .iter()
52 .find(|f| f.label == label)
53 .map(|f| f.value.as_str())
54 }
55}
56
57/// Room header area authority strip (separate from conversation header).
58#[derive(Clone, Debug, PartialEq, Eq)]
59pub struct RoomHeaderAuthorityView {
60 /// Always `Sarah` — must not include profile or revision.
61 pub conversation_header: &'static str,
62 pub fields: Vec<InspectorField>,
63}
64
65impl RoomHeaderAuthorityView {
66 pub fn lines(&self) -> Vec<String> {
67 self.fields.iter().map(InspectorField::line).collect()
68 }
69
70 pub fn joined_text(&self) -> String {
71 self.lines().join("\n")
72 }
73}
74
75/// Render the details view for one receipt row.
76pub fn render_receipt_detail(detail: &AuthorityReceiptDetail) -> ReceiptInspectorView {
77 let mut fields = vec![
78 InspectorField::new("tool_ref", detail.tool_ref.as_str()),
79 InspectorField::new("allowed", if detail.allowed { "true" } else { "false" }),
80 InspectorField::new(
81 "authority_receipt_ref",
82 detail.authority_receipt_ref.as_str(),
83 ),
84 InspectorField::new("decision_ref", detail.decision_ref.as_str()),
85 InspectorField::new(
86 "bounded_result_ref",
87 detail
88 .bounded_result_ref
89 .as_ref()
90 .map(PublicRef::as_str)
91 .unwrap_or("—"),
92 ),
93 ];
94
95 if let Some(refusal) = &detail.refusal {
96 fields.push(InspectorField::new(
97 "reason_class",
98 refusal.reason_class.as_label(),
99 ));
100 if let Some(category) = &refusal.reserved_category {
101 fields.push(InspectorField::new(
102 "reserved_category",
103 category.as_str(),
104 ));
105 }
106 }
107
108 ReceiptInspectorView {
109 fields,
110 allowed: detail.allowed,
111 }
112}
113
114/// Render the room header authority strip.
115///
116/// Conversation header stays `Sarah`. Profile ref and revision live here only.
117pub fn render_room_header_authority(header: &RoomAuthorityHeader) -> RoomHeaderAuthorityView {
118 RoomHeaderAuthorityView {
119 conversation_header: CONVERSATION_HEADER_TITLE,
120 fields: vec![
121 InspectorField::new(
122 "authority_profile_ref",
123 header.authority_profile_ref.as_str(),
124 ),
125 InspectorField::new(
126 "authority_revision",
127 header.authority_revision.to_string(),
128 ),
129 ],
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use crate::public_ref::PublicRef;
137 use crate::receipt::{
138 AuthorityReceiptDetail, RefusalDetail, RefusalReasonClass, RawAuthorityBlock,
139 };
140 use crate::room_header::RoomAuthorityHeader;
141
142 fn allowed_detail() -> AuthorityReceiptDetail {
143 AuthorityReceiptDetail::from_raw(RawAuthorityBlock {
144 tool_ref: "tool.sarah.list_full_auto_runs",
145 allowed: true,
146 authority_receipt_ref: "receipt.authority.sarah.tool.ok.1",
147 decision_ref: "decision.sarah.tool.call-ok",
148 result_refs: &["result.full_auto.list.page1"],
149 bounded_result_ref: None,
150 refusal_reason: None,
151 reserved_category: None,
152 })
153 .expect("allowed")
154 }
155
156 fn refused_detail() -> AuthorityReceiptDetail {
157 AuthorityReceiptDetail::from_raw(RawAuthorityBlock {
158 tool_ref: "tool.sarah.export_secret",
159 allowed: false,
160 authority_receipt_ref: "receipt.authority.sarah.tool.refuse.1",
161 decision_ref: "decision.sarah.tool.call-refuse",
162 result_refs: &["blocker.sarah.authority_refused"],
163 bounded_result_ref: None,
164 refusal_reason: Some("reserved_action"),
165 reserved_category: Some("reserved.secret_export"),
166 })
167 .expect("refused")
168 }
169
170 #[test]
171 fn renders_allowed_receipt_fields() {
172 let view = render_receipt_detail(&allowed_detail());
173 assert!(view.allowed);
174 assert_eq!(
175 view.field_value("tool_ref"),
176 Some("tool.sarah.list_full_auto_runs")
177 );
178 assert_eq!(view.field_value("allowed"), Some("true"));
179 assert_eq!(
180 view.field_value("authority_receipt_ref"),
181 Some("receipt.authority.sarah.tool.ok.1")
182 );
183 assert_eq!(
184 view.field_value("decision_ref"),
185 Some("decision.sarah.tool.call-ok")
186 );
187 assert_eq!(
188 view.field_value("bounded_result_ref"),
189 Some("result.full_auto.list.page1")
190 );
191 assert!(view.field_value("reason_class").is_none());
192 assert!(view.field_value("reserved_category").is_none());
193 let text = view.joined_text();
194 assert!(!text.contains("sk-"));
195 assert!(!text.contains("/Users/"));
196 }
197
198 #[test]
199 fn renders_refused_receipt_with_reason_class_and_reserved_category() {
200 let view = render_receipt_detail(&refused_detail());
201 assert!(!view.allowed);
202 assert_eq!(view.field_value("allowed"), Some("false"));
203 assert_eq!(view.field_value("reason_class"), Some("reserved_action"));
204 assert_eq!(
205 view.field_value("reserved_category"),
206 Some("reserved.secret_export")
207 );
208 // Refusals are not errors in the label set — reason_class is explicit.
209 let labels: Vec<_> = view.fields.iter().map(|f| f.label).collect();
210 assert!(!labels.contains(&"error"));
211 assert!(labels.contains(&"reason_class"));
212 }
213
214 #[test]
215 fn room_header_holds_profile_conversation_header_is_sarah_only() {
216 let header = RoomAuthorityHeader::from_raw("openagents.sarah-owner-orchestrator", 6)
217 .expect("header");
218 let view = render_room_header_authority(&header);
219 assert_eq!(view.conversation_header, "Sarah");
220 assert_eq!(view.conversation_header, CONVERSATION_HEADER_TITLE);
221 assert_eq!(
222 view.fields
223 .iter()
224 .find(|f| f.label == "authority_profile_ref")
225 .map(|f| f.value.as_str()),
226 Some("openagents.sarah-owner-orchestrator")
227 );
228 assert_eq!(
229 view.fields
230 .iter()
231 .find(|f| f.label == "authority_revision")
232 .map(|f| f.value.as_str()),
233 Some("6")
234 );
235 // Conversation header must not carry authority text.
236 assert!(!view.conversation_header.contains("openagents"));
237 assert!(!view.conversation_header.contains("revision"));
238 assert!(!view.conversation_header.contains("6"));
239 }
240
241 #[test]
242 fn rendering_never_includes_dropped_unsafe_material() {
243 let detail = AuthorityReceiptDetail {
244 tool_ref: PublicRef::new("tool.sarah.safe").unwrap(),
245 allowed: false,
246 authority_receipt_ref: PublicRef::new("receipt.authority.sarah.tool.x").unwrap(),
247 decision_ref: PublicRef::new("decision.sarah.tool.x").unwrap(),
248 bounded_result_ref: Some(PublicRef::new("blocker.sarah.authority_refused").unwrap()),
249 bounded_result_refs: vec![PublicRef::new("blocker.sarah.authority_refused").unwrap()],
250 refusal: Some(RefusalDetail::new(
251 RefusalReasonClass::ReservedAction,
252 Some(PublicRef::new("reserved.secret_export").unwrap()),
253 )),
254 };
255 let text = render_receipt_detail(&detail).joined_text();
256 assert!(!text.contains("Bearer"));
257 assert!(!text.contains("sk-"));
258 assert!(!text.contains("/Users/"));
259 assert!(!text.contains("OPENAGENTS_AGENT_TOKEN"));
260 assert!(text.contains("reserved_action"));
261 assert!(text.contains("reserved.secret_export"));
262 }
263}
264