betterwithage commited on
Commit
37943a5
·
verified ·
1 Parent(s): 48be4c9

feat(operator-widget): Chaski operator widget across all served HTML — vendored 0-CDN, live-wired, no codenames

Browse files
static-vendor/a11oy-operator-widget.css ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* a11oy-operator-widget.css — floating governed-operator widget for SZL surfaces.
2
+ Version 3.0.0. The widget is fully self-styling: a11oy-operator-widget.js
3
+ injects all required CSS (prefixed .aow-) at runtime, so loading this file is
4
+ OPTIONAL. It is kept only as a stable asset path and for surfaces that prefer
5
+ to pin the palette tokens below. Neutral governed palette: deep navy glass,
6
+ signed-gold hairlines, emerald/rose status. No external dependencies, no CDN.
7
+ SPDX: Apache-2.0 */
8
+
9
+ :root {
10
+ --aow-gold: #c9b787;
11
+ --aow-gold-soft: rgba(201, 183, 135, 0.18);
12
+ --aow-border: rgba(201, 183, 135, 0.22);
13
+ --aow-glass: #0c1322;
14
+ --aow-glass-soft: rgba(22, 32, 58, 0.6);
15
+ --aow-navy-deep: #0a0f1e;
16
+ --aow-text: #e6edf6;
17
+ --aow-text-muted: #9fb0c8;
18
+ --aow-ok: #4ade80;
19
+ --aow-warn: #f87171;
20
+ --aow-font-mono: ui-monospace, 'JetBrains Mono', 'Fira Code', SFMono-Regular, Menlo, monospace;
21
+ --aow-font-body: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
22
+ --aow-z: 2147483647;
23
+ }
24
+
25
+ /* The runtime-injected styles in a11oy-operator-widget.js are authoritative.
26
+ These optional fallbacks only apply if the JS has not yet injected its <style>. */
27
+ .aow-root { position: fixed; right: 24px; bottom: 24px; z-index: var(--aow-z);
28
+ font-family: var(--aow-font-body); color: var(--aow-text); }
29
+ .aow-fab { width: 56px; height: 56px; border-radius: 50%;
30
+ background: linear-gradient(135deg, #16203a 0%, var(--aow-navy-deep) 100%);
31
+ border: 1px solid var(--aow-border); cursor: pointer; }
static-vendor/a11oy-operator-widget.js ADDED
@@ -0,0 +1,676 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* a11oy-operator-widget.js — floating governed-operator widget for SZL surfaces.
2
+ * Version: 3.0.0 — Chaski agent surface, wired to the LIVE a11oy/killinchu
3
+ * substrate (real SSE chat + agentic loop + v4 ledger). No character codenames.
4
+ *
5
+ * The operator widget is a persistent, honest interface to the substrate. It is
6
+ * not a destination page. It floats bottom-right, opens to a conversation panel,
7
+ * and surfaces notifications (receipt minted, gate denied, build status,
8
+ * doctrine drift). It ROUTES questions to the substrate backends — it has NO
9
+ * model of its own — and is HONEST when a backend is unreachable (it never
10
+ * fabricates an answer). The agent surface is named "Chaski" (the runner who
11
+ * carries the message), per SZL doctrine.
12
+ *
13
+ * Auto-detection: the widget detects the host organ from the page origin
14
+ * (killinchu -> /api/killinchu, otherwise -> /api/a11oy) and calls SAME-ORIGIN
15
+ * endpoints by default, so it works on every served surface with zero config.
16
+ *
17
+ * Optional overrides via data-* attributes on the script tag:
18
+ * data-a11oy-base -> absolute base URL for the a11oy substrate (cross-origin)
19
+ * data-organ -> force organ id ("a11oy" | "killinchu")
20
+ * data-surface -> name of the host surface (for attribution)
21
+ *
22
+ * Vanilla JS, no dependencies, no runtime CDN, embeddable anywhere.
23
+ *
24
+ * Author: Stephen P. Lutar Jr. <stephenlutar2@gmail.com>
25
+ * SPDX: Apache-2.0 · DCO · ADDITIVE
26
+ */
27
+ (function () {
28
+ 'use strict';
29
+ if (window.__a11oyOperatorWidgetLoaded) return;
30
+ window.__a11oyOperatorWidgetLoaded = true;
31
+
32
+ // ---- Resolve configuration from the script tag's data-* attributes ----
33
+ var script =
34
+ document.currentScript ||
35
+ (function () {
36
+ var s = document.querySelectorAll('script[src*="a11oy-operator-widget"],script[src*="rosie-widget"]');
37
+ return s.length ? s[s.length - 1] : null;
38
+ })();
39
+ var ds = (script && script.dataset) || {};
40
+
41
+ function trim(u) { return u ? u.replace(/\/+$/, '') : ''; }
42
+
43
+ // Detect the host organ from the page origin (same-origin by default).
44
+ function detectOrgan() {
45
+ if (ds.organ) return String(ds.organ).toLowerCase();
46
+ var h = (location.hostname || '').toLowerCase();
47
+ if (h.indexOf('killinchu') >= 0) return 'killinchu';
48
+ return 'a11oy';
49
+ }
50
+
51
+ var ORGAN = detectOrgan();
52
+ var CFG = {
53
+ organ: ORGAN,
54
+ // same-origin by default ('' => relative); cross-origin only if explicitly set
55
+ base: trim(ds.a11oyBase || ds.base || ''),
56
+ surface: ds.surface || (location.hostname || 'surface'),
57
+ };
58
+ // a11oy.code orchestrator (chat + agent) only lives on the a11oy organ.
59
+ // killinchu has its own v4 surface (ledger/inbox) but routes reasoning to a11oy.
60
+ CFG.codeBase = (ORGAN === 'killinchu' && !CFG.base)
61
+ ? 'https://szlholdings-a11oy.hf.space' // killinchu reasons via a11oy substrate
62
+ : CFG.base;
63
+ CFG.apiOrgan = ORGAN; // for v4 ledger/inbox on the host organ
64
+
65
+ var LS_KEY = 'a11oy.operator.state.v1';
66
+ var SS_KEY = 'a11oy.operator.thread.v1';
67
+
68
+ // ---- Inline portrait (no runtime CDN): governed-operator monogram SVG ----
69
+ var PORTRAIT = 'data:image/svg+xml;utf8,' + encodeURIComponent(
70
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">' +
71
+ '<defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1">' +
72
+ '<stop offset="0" stop-color="#1c2742"/><stop offset="1" stop-color="#0a0f1e"/>' +
73
+ '</linearGradient></defs>' +
74
+ '<rect width="64" height="64" rx="16" fill="url(#g)"/>' +
75
+ '<circle cx="32" cy="32" r="18" fill="none" stroke="#c9b787" stroke-width="2"/>' +
76
+ '<path d="M32 16 L32 48 M16 32 L48 32" stroke="#c9b787" stroke-width="1.4" opacity="0.55"/>' +
77
+ '<circle cx="32" cy="32" r="4.5" fill="#c9b787"/>' +
78
+ '<text x="32" y="59" font-family="ui-monospace,monospace" font-size="9" fill="#9fb4cc" ' +
79
+ 'text-anchor="middle" letter-spacing="1">CHASKI</text></svg>'
80
+ );
81
+
82
+ // ---- Small DOM helpers ----
83
+ function el(tag, attrs, kids) {
84
+ var n = document.createElement(tag);
85
+ if (attrs) {
86
+ for (var k in attrs) {
87
+ if (k === 'class') n.className = attrs[k];
88
+ else if (k === 'text') n.textContent = attrs[k];
89
+ else if (k === 'html') n.innerHTML = attrs[k];
90
+ else if (k.slice(0, 2) === 'on' && typeof attrs[k] === 'function')
91
+ n.addEventListener(k.slice(2), attrs[k]);
92
+ else if (attrs[k] != null) n.setAttribute(k, attrs[k]);
93
+ }
94
+ }
95
+ (kids || []).forEach(function (c) {
96
+ if (c == null) return;
97
+ n.appendChild(typeof c === 'string' ? document.createTextNode(c) : c);
98
+ });
99
+ return n;
100
+ }
101
+ function esc(s) {
102
+ return String(s).replace(/[&<>"']/g, function (c) {
103
+ return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
104
+ });
105
+ }
106
+
107
+ // ---- Persistent state ----
108
+ var state = { unread: 0, status: 'unknown' };
109
+ try {
110
+ var saved = JSON.parse(localStorage.getItem(LS_KEY) || '{}');
111
+ if (saved && typeof saved === 'object' && typeof saved.unread === 'number') state.unread = saved.unread;
112
+ } catch (e) {}
113
+ function persist() {
114
+ try { localStorage.setItem(LS_KEY, JSON.stringify({ unread: state.unread })); } catch (e) {}
115
+ }
116
+
117
+ var thread = [];
118
+ try {
119
+ var t = JSON.parse(sessionStorage.getItem(SS_KEY) || '[]');
120
+ if (Array.isArray(t)) thread = t;
121
+ } catch (e) {}
122
+ function persistThread() {
123
+ try { sessionStorage.setItem(SS_KEY, JSON.stringify(thread.slice(-50))); } catch (e) {}
124
+ }
125
+
126
+ // ---- CSS (neutral governed palette: deep navy + signed gold + emerald) ----
127
+ var css = [
128
+ '.aow-root{position:fixed;bottom:24px;right:24px;z-index:2147483647;font-family:"Inter",system-ui,sans-serif;font-size:14px;line-height:1.5;}',
129
+ '.aow-fab{width:56px;height:56px;border-radius:50%;border:1px solid rgba(201,183,135,0.45);cursor:pointer;',
130
+ 'background:linear-gradient(135deg,#16203a 0%,#0a0f1e 100%);',
131
+ 'box-shadow:0 4px 20px rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;',
132
+ 'position:relative;transition:transform .18s,box-shadow .18s;outline:none;}',
133
+ '.aow-fab:hover{transform:scale(1.08);box-shadow:0 6px 28px rgba(201,183,135,0.35);}',
134
+ '.aow-fab:focus-visible{outline:3px solid #c9b787;outline-offset:3px;}',
135
+ '.aow-fab img{width:40px;height:40px;border-radius:50%;object-fit:cover;}',
136
+ '.aow-fab-fallback{color:#c9b787;font-weight:700;font-size:20px;}',
137
+ '.aow-badge{position:absolute;top:-4px;right:-4px;min-width:18px;height:18px;',
138
+ 'border-radius:9px;background:#c8415d;color:#fff;font-size:11px;font-weight:700;',
139
+ 'display:none;align-items:center;justify-content:center;padding:0 4px;border:2px solid #0a0f1e;}',
140
+ '[data-unread="true"] .aow-badge{display:flex;}',
141
+ '.aow-panel{position:fixed;bottom:92px;right:24px;width:370px;max-width:calc(100vw - 48px);',
142
+ 'height:540px;max-height:calc(100vh - 120px);background:#0c1322;',
143
+ 'border:1px solid rgba(201,183,135,0.22);border-radius:16px;display:none;flex-direction:column;',
144
+ 'box-shadow:0 8px 40px rgba(0,0,0,0.65);overflow:hidden;}',
145
+ '[data-open="true"] .aow-panel{display:flex;}',
146
+ '.aow-head{display:flex;align-items:center;gap:10px;padding:12px 14px;',
147
+ 'border-bottom:1px solid rgba(201,183,135,0.16);background:rgba(22,32,58,0.6);}',
148
+ '.aow-head-avatar{width:34px;height:34px;border-radius:50%;object-fit:cover;flex-shrink:0;}',
149
+ '.aow-head-text{display:flex;flex-direction:column;flex:1;min-width:0;}',
150
+ '.aow-eyebrow{font-size:9px;letter-spacing:.12em;color:#c9b787;font-weight:700;text-transform:uppercase;}',
151
+ '.aow-head-name{font-size:13px;font-weight:600;color:#e6edf6;}',
152
+ '.aow-status{display:flex;align-items:center;gap:5px;margin-left:auto;flex-shrink:0;}',
153
+ '.aow-status-dot{width:8px;height:8px;border-radius:50%;background:#7d8aa3;transition:background .3s;}',
154
+ '[data-state="ok"] .aow-status-dot{background:#4ade80;}',
155
+ '[data-state="down"] .aow-status-dot{background:#f87171;}',
156
+ '[data-state="unknown"] .aow-status-dot{background:#7d8aa3;}',
157
+ '.aow-status-label{font-size:11px;color:#9fb0c8;}',
158
+ '.aow-close{background:none;border:none;color:#9fb0c8;font-size:20px;cursor:pointer;',
159
+ 'padding:0 4px;line-height:1;margin-left:6px;border-radius:4px;transition:color .15s;}',
160
+ '.aow-close:hover{color:#e6edf6;}',
161
+ /* Quick action buttons */
162
+ '.aow-quick-actions{display:flex;gap:6px;padding:8px 10px;border-bottom:1px solid rgba(201,183,135,0.12);',
163
+ 'background:rgba(12,19,34,0.85);flex-wrap:wrap;}',
164
+ '.aow-qa-btn{flex:1;min-width:80px;background:rgba(201,183,135,0.08);border:1px solid rgba(201,183,135,0.22);',
165
+ 'color:#cdb98a;font-size:11px;font-family:inherit;border-radius:6px;padding:5px 8px;',
166
+ 'cursor:pointer;transition:background .15s,border-color .15s;text-align:center;font-weight:500;',
167
+ 'white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',
168
+ '.aow-qa-btn:hover{background:rgba(201,183,135,0.18);border-color:rgba(201,183,135,0.5);color:#fff;}',
169
+ '.aow-qa-btn:active{background:rgba(201,183,135,0.28);}',
170
+ '.aow-qa-btn:disabled{opacity:0.4;cursor:not-allowed;}',
171
+ '.aow-thread{flex:1;overflow-y:auto;padding:12px 10px;display:flex;flex-direction:column;gap:8px;}',
172
+ '.aow-empty{color:#9fb0c8;font-size:13px;text-align:center;padding:20px 6px;}',
173
+ '.aow-msg{max-width:90%;padding:8px 12px;border-radius:10px;font-size:13px;word-break:break-word;white-space:pre-wrap;}',
174
+ '.aow-msg-user{align-self:flex-end;background:rgba(201,183,135,0.16);color:#e6edf6;border-bottom-right-radius:3px;}',
175
+ '.aow-msg-op{align-self:flex-start;background:rgba(22,32,58,0.85);color:#e6edf6;',
176
+ 'border-bottom-left-radius:3px;border:1px solid rgba(201,183,135,0.14);}',
177
+ '.aow-msg-meta{font-size:10px;color:#9fb0c8;margin-top:4px;font-family:ui-monospace,"JetBrains Mono",monospace;}',
178
+ '.aow-typing-wrap{align-self:flex-start;}',
179
+ '.aow-typing{display:flex;gap:4px;padding:10px 14px;background:rgba(22,32,58,0.85);',
180
+ 'border-radius:10px;border:1px solid rgba(201,183,135,0.14);}',
181
+ '.aow-typing span{width:6px;height:6px;border-radius:50%;background:#c9b787;animation:aow-bounce 1.2s infinite ease-in-out;}',
182
+ '.aow-typing span:nth-child(2){animation-delay:.2s;}',
183
+ '.aow-typing span:nth-child(3){animation-delay:.4s;}',
184
+ '@keyframes aow-bounce{0%,80%,100%{transform:translateY(0);}40%{transform:translateY(-6px);}}',
185
+ '.aow-form{display:flex;align-items:flex-end;gap:6px;padding:10px;border-top:1px solid rgba(201,183,135,0.14);}',
186
+ '.aow-input{flex:1;background:rgba(22,32,58,0.5);border:1px solid rgba(201,183,135,0.22);',
187
+ 'border-radius:8px;color:#e6edf6;font-family:inherit;font-size:13px;padding:7px 10px;',
188
+ 'resize:none;max-height:90px;outline:none;transition:border-color .15s;}',
189
+ '.aow-input:focus{border-color:rgba(201,183,135,0.55);}',
190
+ '.aow-input::placeholder{color:#7d8aa3;}',
191
+ '.aow-send{background:#c9b787;border:none;border-radius:8px;color:#0a0f1e;',
192
+ 'font-weight:700;font-size:16px;width:36px;height:36px;cursor:pointer;flex-shrink:0;',
193
+ 'display:flex;align-items:center;justify-content:center;transition:background .15s;}',
194
+ '.aow-send:hover{background:#dccca0;}',
195
+ '.aow-toasts{position:fixed;bottom:92px;right:24px;display:flex;flex-direction:column;gap:8px;',
196
+ 'z-index:2147483646;pointer-events:none;width:330px;max-width:calc(100vw - 48px);}',
197
+ '.aow-root[data-open="true"] .aow-toasts{bottom:calc(540px + 100px);}',
198
+ '.aow-toast{display:flex;align-items:flex-start;gap:10px;background:#0c1322;',
199
+ 'border:1px solid rgba(201,183,135,0.22);border-radius:12px;padding:10px 12px;',
200
+ 'box-shadow:0 4px 16px rgba(0,0,0,0.55);pointer-events:all;animation:aow-toast-in .2s ease-out;}',
201
+ '@keyframes aow-toast-in{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:none;}}',
202
+ '.aow-toast-out{animation:aow-toast-out .2s ease-in forwards;}',
203
+ '@keyframes aow-toast-out{to{opacity:0;transform:translateY(10px);}}',
204
+ '.aow-toast-avatar{width:28px;height:28px;border-radius:50%;object-fit:cover;flex-shrink:0;}',
205
+ '.aow-toast-body{flex:1;min-width:0;}',
206
+ '.aow-toast-title{font-size:13px;font-weight:600;color:#e6edf6;}',
207
+ '.aow-toast-text{font-size:12px;color:#9fb0c8;margin-top:2px;}',
208
+ '.aow-toast-meta{font-size:10px;color:#cdb98a;font-family:ui-monospace,"JetBrains Mono",monospace;margin-top:2px;}',
209
+ '.aow-toast-x{background:none;border:none;color:#9fb0c8;cursor:pointer;font-size:16px;padding:0 2px;line-height:1;flex-shrink:0;}',
210
+ '[data-kind="receipt"] .aow-toast-title::before{content:"\\1F4DC ";}',
211
+ '[data-kind="denial"] .aow-toast-title::before{content:"\\1F6AB ";}',
212
+ '[data-kind="build-green"] .aow-toast-title::before{content:"\\2705 ";}',
213
+ '[data-kind="build-red"] .aow-toast-title::before{content:"\\274C ";}',
214
+ '[data-kind="doctrine"] .aow-toast-title::before{content:"\\26A0\\FE0F ";}',
215
+ ].join('');
216
+
217
+ var style = document.createElement('style');
218
+ style.textContent = css;
219
+ (document.head || document.documentElement).appendChild(style);
220
+
221
+ // ---- Build the DOM ----
222
+ var root = el('div', { class: 'aow-root', 'data-open': 'false', 'data-unread': 'false' });
223
+ root.setAttribute('data-a11oy-operator', 'widget');
224
+
225
+ var fabImg = el('img', { src: PORTRAIT, alt: '', 'aria-hidden': 'true' });
226
+ fabImg.addEventListener('error', function () {
227
+ fabImg.style.display = 'none';
228
+ fab.appendChild(el('span', { class: 'aow-fab-fallback', text: 'a11' }));
229
+ });
230
+ var badge = el('span', { class: 'aow-badge', 'aria-hidden': 'true' });
231
+ var fab = el('button', {
232
+ class: 'aow-fab', type: 'button',
233
+ 'aria-label': 'Open the a11oy operator assistant',
234
+ 'aria-haspopup': 'dialog', 'aria-expanded': 'false',
235
+ }, [fabImg, badge]);
236
+
237
+ var headAvatar = el('img', { class: 'aow-head-avatar', src: PORTRAIT, alt: '' });
238
+ var statusDot = el('span', { class: 'aow-status-dot' });
239
+ var statusLabel = el('span', { class: 'aow-status-label', text: 'checking' });
240
+ var statusEl = el('span', { class: 'aow-status', 'data-state': 'unknown' }, [statusDot, statusLabel]);
241
+ var closeBtn = el('button', { class: 'aow-close', type: 'button', 'aria-label': 'Close the operator assistant', text: '\u00d7' });
242
+ var head = el('div', { class: 'aow-head' }, [
243
+ headAvatar,
244
+ el('div', { class: 'aow-head-text' }, [
245
+ el('span', { class: 'aow-eyebrow', text: 'a11oy operator' }),
246
+ el('span', { class: 'aow-head-name', text: 'Chaski \u2014 governed agent surface' }),
247
+ ]),
248
+ statusEl,
249
+ closeBtn,
250
+ ]);
251
+
252
+ // Quick action buttons — wired to REAL live endpoints
253
+ var qaReceipt = el('button', {
254
+ class: 'aow-qa-btn', type: 'button', title: 'Show the latest receipts from the ' + CFG.organ + ' v4 ledger',
255
+ text: '\uD83D\uDCDC Receipts',
256
+ onclick: function () { open(); quickAction('receipt'); }
257
+ });
258
+ var qaVerify = el('button', {
259
+ class: 'aow-qa-btn', type: 'button', title: 'Check substrate health + receipt-chain head',
260
+ text: '\uD83D\uDD10 Verify',
261
+ onclick: function () { open(); quickAction('verify'); }
262
+ });
263
+ var qaAgent = el('button', {
264
+ class: 'aow-qa-btn', type: 'button', title: 'Run the governed Chaski agentic loop on a task',
265
+ text: '\u26A1 Agent',
266
+ onclick: function () { open(); quickAction('agent'); }
267
+ });
268
+ var qaAsk = el('button', {
269
+ class: 'aow-qa-btn', type: 'button', title: 'Ask a11oy to explain this surface (governed reasoning)',
270
+ text: '\uD83E\uDDE0 Ask a11oy',
271
+ onclick: function () { open(); quickAction('ask'); }
272
+ });
273
+ var quickActions = el('div', { class: 'aow-quick-actions' }, [qaReceipt, qaVerify, qaAgent, qaAsk]);
274
+
275
+ var threadEl = el('div', { class: 'aow-thread', role: 'log', 'aria-live': 'polite', 'aria-label': 'Conversation with the a11oy operator' });
276
+
277
+ var input = el('textarea', {
278
+ class: 'aow-input', rows: '1', placeholder: 'Ask the a11oy substrate\u2026',
279
+ 'aria-label': 'Message to the a11oy operator',
280
+ });
281
+ var sendBtn = el('button', { class: 'aow-send', type: 'submit', 'aria-label': 'Send', html: '&#10148;' });
282
+ var form = el('form', { class: 'aow-form' }, [input, sendBtn]);
283
+
284
+ var panel = el('div', { class: 'aow-panel', role: 'dialog', 'aria-label': 'a11oy operator assistant', 'aria-modal': 'false' },
285
+ [head, quickActions, threadEl, form]);
286
+
287
+ var toasts = el('div', { class: 'aow-toasts', 'aria-live': 'polite', 'aria-label': 'a11oy operator notifications' });
288
+
289
+ root.appendChild(toasts);
290
+ root.appendChild(panel);
291
+ root.appendChild(fab);
292
+
293
+ function mount() {
294
+ if (!document.body) { document.addEventListener('DOMContentLoaded', mount); return; }
295
+ document.body.appendChild(root);
296
+ renderThread();
297
+ refreshBadge();
298
+ checkStatus();
299
+ }
300
+ mount();
301
+
302
+ // ---- Open / close ----
303
+ var isOpen = false;
304
+ function open() {
305
+ isOpen = true;
306
+ root.setAttribute('data-open', 'true');
307
+ fab.setAttribute('aria-expanded', 'true');
308
+ state.unread = 0; persist(); refreshBadge();
309
+ if (!thread.length) renderThread();
310
+ setTimeout(function () { input.focus(); }, 60);
311
+ scrollThread();
312
+ }
313
+ function close() {
314
+ isOpen = false;
315
+ root.setAttribute('data-open', 'false');
316
+ fab.setAttribute('aria-expanded', 'false');
317
+ fab.focus();
318
+ }
319
+ function toggle() { isOpen ? close() : open(); }
320
+
321
+ fab.addEventListener('click', toggle);
322
+ closeBtn.addEventListener('click', close);
323
+ document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && isOpen) close(); });
324
+
325
+ // ---- Thread rendering ----
326
+ function refreshBadge() {
327
+ root.setAttribute('data-unread', state.unread > 0 ? 'true' : 'false');
328
+ badge.textContent = state.unread > 9 ? '9+' : String(state.unread || '');
329
+ }
330
+ function scrollThread() { requestAnimationFrame(function () { threadEl.scrollTop = threadEl.scrollHeight; }); }
331
+ function renderThread() {
332
+ threadEl.innerHTML = '';
333
+ if (!thread.length) {
334
+ var em = el('div', { class: 'aow-empty' });
335
+ em.innerHTML =
336
+ '<strong>a11oy operator \u2014 Chaski</strong><br>' +
337
+ 'Ask the substrate anything, or use the buttons above. I route to the live ' +
338
+ esc(CFG.organ) + ' backend and stay honest when it is unreachable \u2014 I never fabricate.' +
339
+ '<br><br><small style="color:#6b7a96">Surface: <em>' + esc(CFG.surface) + '</em> \u2022 organ: <em>' + esc(CFG.organ) + '</em></small>';
340
+ threadEl.appendChild(em);
341
+ return;
342
+ }
343
+ thread.forEach(function (m) { threadEl.appendChild(renderMsg(m)); });
344
+ scrollThread();
345
+ }
346
+ function renderMsg(m) {
347
+ var cls = m.role === 'user' ? 'aow-msg aow-msg-user' : 'aow-msg aow-msg-op';
348
+ var node = el('div', { class: cls });
349
+ node.innerHTML = esc(m.text).replace(/\n/g, '<br>');
350
+ if (m.meta) node.appendChild(el('div', { class: 'aow-msg-meta', text: m.meta }));
351
+ if (m.dsse) {
352
+ var dsseNode = el('div', { class: 'aow-msg-meta' });
353
+ dsseNode.innerHTML = '<strong>receipt:</strong> ' + esc(m.dsse);
354
+ node.appendChild(dsseNode);
355
+ }
356
+ return node;
357
+ }
358
+ function pushMsg(role, text, meta, dsse) {
359
+ var m = { role: role, text: text, meta: meta || null, ts: Date.now(), dsse: dsse || null };
360
+ thread.push(m); persistThread();
361
+ if (threadEl.querySelector('.aow-empty')) threadEl.innerHTML = '';
362
+ threadEl.appendChild(renderMsg(m));
363
+ scrollThread();
364
+ return m;
365
+ }
366
+ function showTyping() {
367
+ var t = el('div', { class: 'aow-msg aow-msg-op aow-typing-wrap' }, [
368
+ el('div', { class: 'aow-typing' }, [el('span'), el('span'), el('span')]),
369
+ ]);
370
+ threadEl.appendChild(t); scrollThread();
371
+ return t;
372
+ }
373
+ // Append into an existing op message node (used for streaming).
374
+ function appendStream(node, chunk) {
375
+ node.dataset.acc = (node.dataset.acc || '') + chunk;
376
+ node.innerHTML = esc(node.dataset.acc).replace(/\n/g, '<br>');
377
+ scrollThread();
378
+ }
379
+
380
+ // ---- Endpoint helpers (REAL live contract) ----
381
+ function codeUrl(path) { return CFG.codeBase + '/api/a11oy/code' + path; } // chat/agent/health live on a11oy organ
382
+ function v4Url(path) { return CFG.base + '/api/' + CFG.apiOrgan + '/v4' + path; } // ledger/inbox on host organ
383
+ function healthUrl() { return CFG.base + '/healthz'; }
384
+
385
+ // ---- Backend status (honest) ----
386
+ function checkStatus() {
387
+ fetchJson(healthUrl(), { method: 'GET' }, 5000)
388
+ .then(function () { setStatus('ok', CFG.organ + ' online'); })
389
+ .catch(function () { setStatus('down', CFG.organ + ' unreachable'); });
390
+ }
391
+ function setStatus(s, label) {
392
+ state.status = s;
393
+ statusEl.setAttribute('data-state', s);
394
+ statusLabel.textContent = label;
395
+ }
396
+
397
+ // ---- Networking ----
398
+ function fetchJson(url, opts, timeoutMs) {
399
+ opts = opts || {};
400
+ var ctrl = typeof AbortController !== 'undefined' ? new AbortController() : null;
401
+ if (ctrl) opts.signal = ctrl.signal;
402
+ opts.headers = Object.assign({ 'Content-Type': 'application/json' }, opts.headers || {});
403
+ var timer = ctrl ? setTimeout(function () { ctrl.abort(); }, timeoutMs || 12000) : null;
404
+ return fetch(url, opts).then(function (r) {
405
+ if (timer) clearTimeout(timer);
406
+ if (!r.ok) { var e = new Error('HTTP ' + r.status); e.status = r.status; throw e; }
407
+ var ct = r.headers.get('content-type') || '';
408
+ return ct.indexOf('application/json') >= 0 ? r.json() : r.text();
409
+ }).catch(function (e) { if (timer) clearTimeout(timer); throw e; });
410
+ }
411
+
412
+ // Stream a Server-Sent-Events POST endpoint, calling onEvent({event,data}) per frame.
413
+ function streamSSE(url, body, onEvent, timeoutMs) {
414
+ var ctrl = typeof AbortController !== 'undefined' ? new AbortController() : null;
415
+ var timer = ctrl ? setTimeout(function () { ctrl.abort(); }, timeoutMs || 90000) : null;
416
+ return fetch(url, {
417
+ method: 'POST',
418
+ headers: { 'Content-Type': 'application/json', 'Accept': 'text/event-stream' },
419
+ body: JSON.stringify(body),
420
+ signal: ctrl ? ctrl.signal : undefined,
421
+ }).then(function (r) {
422
+ if (!r.ok) { if (timer) clearTimeout(timer); var e = new Error('HTTP ' + r.status); e.status = r.status; throw e; }
423
+ if (!r.body || !r.body.getReader) {
424
+ // No streaming support — read whole body and emit one event.
425
+ return r.text().then(function (txt) { if (timer) clearTimeout(timer); parseSSEBlock(txt, onEvent); });
426
+ }
427
+ var reader = r.body.getReader();
428
+ var dec = new TextDecoder();
429
+ var buf = '';
430
+ function pump() {
431
+ return reader.read().then(function (res) {
432
+ if (res.done) { if (timer) clearTimeout(timer); if (buf.trim()) parseSSEBlock(buf, onEvent); return; }
433
+ buf += dec.decode(res.value, { stream: true });
434
+ var parts = buf.split('\n\n');
435
+ buf = parts.pop();
436
+ parts.forEach(function (p) { parseSSEFrame(p, onEvent); });
437
+ return pump();
438
+ });
439
+ }
440
+ return pump();
441
+ }).catch(function (e) { if (timer) clearTimeout(timer); throw e; });
442
+ }
443
+ function parseSSEFrame(frame, onEvent) {
444
+ var ev = 'message', data = '';
445
+ frame.split('\n').forEach(function (line) {
446
+ if (line.indexOf('event:') === 0) ev = line.slice(6).trim();
447
+ else if (line.indexOf('data:') === 0) data += line.slice(5).trim();
448
+ });
449
+ if (data) { var parsed; try { parsed = JSON.parse(data); } catch (e) { parsed = data; } onEvent({ event: ev, data: parsed }); }
450
+ }
451
+ function parseSSEBlock(txt, onEvent) { txt.split('\n\n').forEach(function (f) { if (f.trim()) parseSSEFrame(f, onEvent); }); }
452
+
453
+ // ---- Quick Actions (REAL endpoints) ----
454
+ function disableQA(d) { [qaReceipt, qaVerify, qaAgent, qaAsk].forEach(function (b) { b.disabled = d; }); }
455
+
456
+ function quickAction(kind) {
457
+ if (kind === 'receipt') {
458
+ pushMsg('user', '\uD83D\uDCDC Latest receipts');
459
+ disableQA(true);
460
+ var typing = showTyping();
461
+ fetchJson(v4Url('/receipts?limit=10'), { method: 'GET' }, 14000)
462
+ .then(function (data) {
463
+ typing.remove(); disableQA(false);
464
+ var items = data.receipts || data.items || (Array.isArray(data) ? data : []);
465
+ if (!items.length) {
466
+ pushMsg('op', 'The ' + CFG.organ + ' v4 ledger is currently empty \u2014 no receipts minted yet. ' +
467
+ (data.note ? '\n\nNote from the substrate: ' + data.note : '') + '\n\nThat is an honest idle state, not an error.');
468
+ } else {
469
+ var top = items[0];
470
+ var hash = top.hash || top.receipt_id || top.id || top.digest;
471
+ var msg = items.length + ' receipt(s) in the chain.';
472
+ if (data.total != null) msg += '\nHead total: ' + data.total;
473
+ if (top.action) msg += '\nMost recent action: ' + top.action;
474
+ if (top.timestamp_utc || top.ts) msg += '\nTime: ' + (top.timestamp_utc || top.ts);
475
+ if (data.note) msg += '\n\n' + data.note;
476
+ pushMsg('op', msg, hash ? 'receipt ' + hash : null);
477
+ }
478
+ setStatus('ok', 'ledger \u2713');
479
+ })
480
+ .catch(function (err) { typing.remove(); disableQA(false); handleNetErr(err); });
481
+
482
+ } else if (kind === 'verify') {
483
+ pushMsg('user', '\uD83D\uDD10 Verify substrate + chain head');
484
+ disableQA(true);
485
+ var typing2 = showTyping();
486
+ fetchJson(healthUrl(), { method: 'GET' }, 8000)
487
+ .then(function (h) {
488
+ var lock = (h && (h.lock || h.counts)) || '?';
489
+ var commit = (h && h.commit) || (h && h.lean_sha) || '?';
490
+ return fetchJson(v4Url('/receipts?limit=1'), { method: 'GET' }, 10000)
491
+ .then(function (led) {
492
+ typing2.remove(); disableQA(false);
493
+ var items = led.receipts || led.items || [];
494
+ var head = items.length ? (items[0].hash || items[0].receipt_id || items[0].id) : null;
495
+ var msg = CFG.organ + ' substrate is online.\nKernel lock: ' + lock + '\nLean commit: ' + commit +
496
+ '\nLocked-proven formulas: 8 {F1,F4,F7,F11,F12,F18,F19,F22} \u2014 machine-enforced.';
497
+ if (head) msg += '\nReceipt-chain head: present.';
498
+ else msg += '\nReceipt chain: empty (honest idle).';
499
+ if (led.note) msg += '\n\n' + led.note;
500
+ pushMsg('op', msg, head ? 'head ' + head : null);
501
+ setStatus('ok', 'chain \u2713');
502
+ });
503
+ })
504
+ .catch(function (err) { typing2.remove(); disableQA(false); handleNetErr(err); });
505
+
506
+ } else if (kind === 'agent') {
507
+ var task = 'Summarise the governance posture of the ' + CFG.surface + ' surface: what is proven, what is experimental, what is roadmap. Be honest and cite.';
508
+ pushMsg('user', '\u26A1 Run Chaski agent: ' + task);
509
+ disableQA(true);
510
+ runAgent(task, function () { disableQA(false); });
511
+
512
+ } else if (kind === 'ask') {
513
+ var q = 'In two or three sentences, what is the a11oy substrate and what does it govern? What runs on this surface (' + CFG.surface + ')?';
514
+ pushMsg('user', '\uD83E\uDDE0 ' + q);
515
+ disableQA(true);
516
+ runChat(q, function () { disableQA(false); });
517
+ }
518
+ }
519
+
520
+ // ---- Governed chat (live SSE, real LLM router) ----
521
+ function runChat(q, doneCb) {
522
+ var typing = showTyping();
523
+ var opNode = null, routeMeta = null;
524
+ streamSSE(codeUrl('/chat/stream'), { message: q, thread: CFG.surface, surface: CFG.surface },
525
+ function (ev) {
526
+ if (ev.event === 'route' && ev.data && typeof ev.data === 'object') {
527
+ routeMeta = 'router: ' + (ev.data.tier || '?') + ' \u00b7 ' + (ev.data.model || '?') +
528
+ (ev.data.license_class ? ' \u00b7 ' + ev.data.license_class : '');
529
+ } else if (ev.event === 'token' || ev.event === 'delta' || ev.event === 'message') {
530
+ if (typing) { typing.remove(); typing = null; }
531
+ if (!opNode) { opNode = pushMsg('op', '') ; opNode = threadEl.lastChild; }
532
+ var chunk = (ev.data && (ev.data.text || ev.data.token || ev.data.delta || ev.data.content)) || (typeof ev.data === 'string' ? ev.data : '');
533
+ if (chunk) appendStream(opNode, chunk);
534
+ } else if (ev.event === 'done' || ev.event === 'final' || ev.event === 'end') {
535
+ if (typing) { typing.remove(); typing = null; }
536
+ if (ev.data && ev.data.text && opNode && !opNode.dataset.acc) appendStream(opNode, ev.data.text);
537
+ if (opNode && routeMeta) opNode.appendChild(el('div', { class: 'aow-msg-meta', text: routeMeta }));
538
+ // persist final accumulated text into thread store
539
+ if (opNode && opNode.dataset.acc) { thread[thread.length - 1] = { role: 'op', text: opNode.dataset.acc, meta: routeMeta, ts: Date.now() }; persistThread(); }
540
+ setStatus('ok', CFG.organ + ' online');
541
+ } else if (ev.event === 'error') {
542
+ if (typing) { typing.remove(); typing = null; }
543
+ pushMsg('op', 'The substrate reported an error: ' + (ev.data && ev.data.error ? ev.data.error : 'unknown') + '. I will not guess what it meant.');
544
+ }
545
+ }, 90000)
546
+ .then(function () { if (typing) { typing.remove(); } if (doneCb) doneCb(); })
547
+ .catch(function (err) { if (typing) typing.remove(); handleNetErr(err); if (doneCb) doneCb(); });
548
+ }
549
+
550
+ // ---- Governed agentic loop (live SSE FSM: INTAKE..FINALIZE) ----
551
+ function runAgent(task, doneCb) {
552
+ var typing = showTyping();
553
+ var stepNode = null, finalNode = null;
554
+ streamSSE(codeUrl('/agent/stream'), { task: task, thread: CFG.surface, surface: CFG.surface },
555
+ function (ev) {
556
+ var d = ev.data || {};
557
+ if (ev.event === 'state' || d.state) {
558
+ if (typing) { typing.remove(); typing = null; }
559
+ var st = d.state || ev.event;
560
+ if (!stepNode) stepNode = pushMsg('op', '');
561
+ var line = '\u2022 ' + st + (d.note ? ': ' + d.note : (d.summary ? ': ' + d.summary : ''));
562
+ var node = threadEl.lastChild;
563
+ node.dataset.acc = (node.dataset.acc ? node.dataset.acc + '\n' : '') + line;
564
+ node.innerHTML = esc(node.dataset.acc).replace(/\n/g, '<br>');
565
+ scrollThread();
566
+ } else if (ev.event === 'token' || ev.event === 'delta') {
567
+ if (typing) { typing.remove(); typing = null; }
568
+ if (!finalNode) { pushMsg('op', ''); finalNode = threadEl.lastChild; }
569
+ var chunk = d.text || d.token || d.delta || (typeof ev.data === 'string' ? ev.data : '');
570
+ if (chunk) appendStream(finalNode, chunk);
571
+ } else if (ev.event === 'final' || ev.event === 'done' || ev.event === 'finalize') {
572
+ if (typing) { typing.remove(); typing = null; }
573
+ var ans = d.answer || d.text || d.result || d.output;
574
+ var meta = (d.chain_verified != null ? 'chain_verified=' + d.chain_verified : null);
575
+ if (ans) pushMsg('op', String(ans), meta);
576
+ else if (meta) pushMsg('op', 'Agent run complete.', meta);
577
+ setStatus('ok', CFG.organ + ' online');
578
+ } else if (ev.event === 'error') {
579
+ if (typing) { typing.remove(); typing = null; }
580
+ pushMsg('op', 'The agent loop reported an error: ' + (d.error || 'unknown') + '. Honest stop \u2014 no fabricated result.');
581
+ }
582
+ }, 120000)
583
+ .then(function () { if (typing) typing.remove(); if (doneCb) doneCb(); })
584
+ .catch(function (err) { if (typing) typing.remove(); handleNetErr(err); if (doneCb) doneCb(); });
585
+ }
586
+
587
+ function handleNetErr(err) {
588
+ if (err && err.status) {
589
+ pushMsg('op', 'The ' + CFG.organ + ' substrate answered with an error (' + err.status + '). I will not guess what it meant \u2014 try rephrasing, or check the service.');
590
+ } else {
591
+ pushMsg('op', 'I cannot reach the ' + CFG.organ + ' substrate right now, so I will not make up an answer. Ask me again in a moment.');
592
+ setStatus('down', CFG.organ + ' unreachable');
593
+ }
594
+ }
595
+
596
+ // ---- Free-text routing: chat by default; explicit agent intent -> agent loop ----
597
+ function ask(q) {
598
+ pushMsg('user', q);
599
+ var s = q.toLowerCase();
600
+ if (/\b(agent|act|do|run|build|fix|investigate|plan and execute|carry out)\b/.test(s)) {
601
+ runAgent(q);
602
+ } else {
603
+ runChat(q);
604
+ }
605
+ }
606
+
607
+ function safeJson(o) {
608
+ try { var s = JSON.stringify(o, null, 2); return s.length > 800 ? s.slice(0, 800) + '\u2026' : s; }
609
+ catch (e) { return String(o); }
610
+ }
611
+
612
+ // ---- Input handling ----
613
+ form.addEventListener('submit', function (e) {
614
+ e.preventDefault();
615
+ var q = input.value.trim();
616
+ if (!q) return;
617
+ input.value = ''; input.style.height = 'auto';
618
+ ask(q);
619
+ });
620
+ input.addEventListener('input', function () {
621
+ input.style.height = 'auto';
622
+ input.style.height = Math.min(input.scrollHeight, 90) + 'px';
623
+ });
624
+ input.addEventListener('keydown', function (e) {
625
+ if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); form.requestSubmit ? form.requestSubmit() : form.dispatchEvent(new Event('submit', { cancelable: true })); }
626
+ });
627
+
628
+ // ---- Notifications API ----
629
+ function notify(opts) {
630
+ opts = opts || {};
631
+ var kind = opts.kind || 'info';
632
+ var avatar = el('img', { class: 'aow-toast-avatar', src: PORTRAIT, alt: '' });
633
+ var body = el('div', { class: 'aow-toast-body' });
634
+ body.appendChild(el('div', { class: 'aow-toast-title', html: '<strong>' + esc(opts.title || 'a11oy operator') + '</strong>' }));
635
+ if (opts.body) body.appendChild(el('div', { class: 'aow-toast-text', text: opts.body }));
636
+ if (opts.meta) body.appendChild(el('div', { class: 'aow-toast-meta', text: opts.meta }));
637
+ var toast;
638
+ function dismiss() {
639
+ if (!toast) return;
640
+ toast.classList.add('aow-toast-out');
641
+ setTimeout(function () { if (toast && toast.parentNode) toast.parentNode.removeChild(toast); }, 220);
642
+ }
643
+ var x = el('button', { class: 'aow-toast-x', type: 'button', 'aria-label': 'Dismiss', text: '\u00d7', onclick: dismiss });
644
+ toast = el('div', { class: 'aow-toast', 'data-kind': kind, role: 'status' }, [avatar, body, x]);
645
+ toasts.appendChild(toast);
646
+ pushMsg('op', (opts.title ? opts.title + (opts.body ? ' \u2014 ' + opts.body : '') : opts.body || ''), opts.meta);
647
+ if (!isOpen) { state.unread = (state.unread || 0) + 1; persist(); refreshBadge(); }
648
+ if (!opts.sticky) setTimeout(dismiss, opts.duration || 8000);
649
+ return { dismiss: dismiss };
650
+ }
651
+
652
+ // ---- Public API ----
653
+ var api = {
654
+ open: open, close: close, toggle: toggle,
655
+ ask: function (q) { open(); ask(q); },
656
+ agent: function (task) { open(); runAgent(task); },
657
+ chat: function (q) { open(); runChat(q); },
658
+ notify: notify,
659
+ config: CFG,
660
+ receiptMinted: function (hash, what) {
661
+ return notify({ kind: 'receipt', title: 'New receipt minted', body: what || 'A receipt was added to the ledger.', meta: hash ? 'receipt ' + hash : null });
662
+ },
663
+ gateDenied: function (gate, what) {
664
+ return notify({ kind: 'denial', title: 'Policy denied an action', body: what || 'A proposed action was rejected by the gates.', meta: gate ? 'gate ' + gate : null });
665
+ },
666
+ build: function (green, detail) {
667
+ return notify({ kind: green ? 'build-green' : 'build-red', title: green ? 'Build is green' : 'Build is red', body: detail || '' });
668
+ },
669
+ doctrineDrift: function (detail) {
670
+ return notify({ kind: 'doctrine', title: 'Doctrine drift detected', body: detail || 'Something diverged from doctrine. Worth a look.' });
671
+ },
672
+ };
673
+ window.A11oyOperator = api;
674
+ // Back-compat alias for any existing caller (NOT a user-visible name).
675
+ window.Rosie = api;
676
+ })();
web/index.html CHANGED
@@ -59,16 +59,13 @@
59
  </div>
60
  </div>
61
  <script type="module" src="/src/main.tsx"></script>
62
-
63
- <!-- Rosie floating operator widget (additive). Canonical source:
64
- SZLHOLDINGS/README assets/rosie. Set data-a11oy-base / data-amaru-base to the
65
- live substrate endpoints (or inject at build time) when available; until then
66
- Rosie appears and is honest that she is not wired to a backend on this surface. -->
67
- <link rel="stylesheet" href="https://szlholdings-readme.static.hf.space/assets/rosie/rosie-widget.css">
68
- <script src="https://szlholdings-readme.static.hf.space/assets/rosie/rosie-widget.js"
69
- data-surface="a11oy"
70
- data-a11oy-base=""
71
- data-amaru-base=""
72
- defer></script>
73
  </body>
74
  </html>
 
59
  </div>
60
  </div>
61
  <script type="module" src="/src/main.tsx"></script>
62
+
63
+ <!-- a11oy operator widget ("Chaski") floating governed-operator surface.
64
+ Self-hosted in-image (0 runtime CDN), same-origin, auto-detects the host
65
+ organ and routes to the LIVE substrate (/api/a11oy/code/chat|agent + v4
66
+ ledger). Honest when a backend is unreachable never fabricates. The
67
+ server-side injector in serve.py also appends this on every served HTML
68
+ surface; the tag here is idempotent (the injector skips if already present). -->
69
+ <script src="/vendor/a11oy-operator-widget.js" data-surface="a11oy" defer></script>
 
 
 
70
  </body>
71
  </html>