@lloyal-labs/dev-tools 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.js ADDED
@@ -0,0 +1,899 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * The dev pane's web/desktop surface: a cog FAB that docks a full-width pane
4
+ * over the harness's own view — Timeline · Sources · Settings.
5
+ *
6
+ * Mounted ONCE beside the app's view; it subscribes to the same bridge stream
7
+ * through {@link createDevStore} (wire-gated: a production stream folds
8
+ * nothing but `config:loaded`). Rendering is hand-rolled — no timeline
9
+ * library: the design's invariants (fixed-span sliding window, stepped live
10
+ * edge, wait stripes CUT into capsules, letter badges with a collision rule)
11
+ * are cheaper to own than to fight a library for.
12
+ *
13
+ * Everything shown is a recorded event field; absence renders as absence.
14
+ */
15
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
16
+ import { useStore } from 'zustand';
17
+ import { pressureStrip, pressurePercent, readConfigPath, lanePpl, isLive, KEY_TIERS, } from './index.js';
18
+ import { devStoreFor, EDGE_STEP } from './store.js';
19
+ // ── palette: monochrome chrome, color reserved for data ──
20
+ const C = {
21
+ text: '#202124', dim: '#5f6368', faint: '#9aa0a6', border: '#e8eaed',
22
+ hair: '#f4f5f7', chromeBg: '#f1f3f4', panelBg: '#fafbfc',
23
+ agent: '#1a73e8', agentDark: '#174ea6', fail: '#b3261e', ok: '#188038',
24
+ warn: '#9a6700', warnBg: '#fef7e0', warnBorder: '#f9d67a',
25
+ };
26
+ const HOST_CPU = '#00897b';
27
+ const TOOL_PALETTE = ['#e8710a', '#8430ce', '#00897b', '#d01884', '#827717', '#0097a7'];
28
+ const mono = 'ui-monospace, "SF Mono", Menlo, Consolas, monospace';
29
+ /** Per-tool color, assigned first-seen — stable within a run. */
30
+ function useToolColors() {
31
+ const map = useRef(new Map());
32
+ return (name) => {
33
+ if (!map.current.has(name)) {
34
+ map.current.set(name, TOOL_PALETTE[map.current.size % TOOL_PALETTE.length]);
35
+ }
36
+ return map.current.get(name);
37
+ };
38
+ }
39
+ const letterOf = (name) => (name[0] || '?').toUpperCase();
40
+ /** Enter/Space activates a clickable — pairs with role="button" tabIndex={0}. */
41
+ const keyActivate = (fn) => (e) => {
42
+ if (e.key === 'Enter' || e.key === ' ') {
43
+ e.preventDefault();
44
+ fn();
45
+ }
46
+ };
47
+ /** One cell of the metric row: heading + magnitude on top, a min-max
48
+ * auto-scaled area spark filling the cell below — the shape carries the
49
+ * trend, the number carries the truth the auto-zoom hides. */
50
+ function MetricCell({ heading, value, values, color, title, last = false }) {
51
+ const gradId = React.useId();
52
+ let spark = null;
53
+ if (values.length >= 2) {
54
+ let min = Infinity, max = -Infinity;
55
+ for (const v of values) {
56
+ if (v < min)
57
+ min = v;
58
+ if (v > max)
59
+ max = v;
60
+ }
61
+ const spanV = Math.max(max - min, 1e-6);
62
+ const pts = values.map((v, i) => `${((i / (values.length - 1)) * 100).toFixed(2)},${(26 - ((v - min) / spanV) * 22).toFixed(2)}`).join(' ');
63
+ spark = (_jsxs("svg", { viewBox: "0 0 100 28", preserveAspectRatio: "none", style: { width: '100%', height: 28, display: 'block' }, children: [_jsx("defs", { children: _jsxs("linearGradient", { id: gradId, x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: color, stopOpacity: "0.35" }), _jsx("stop", { offset: "100%", stopColor: color, stopOpacity: "0.04" })] }) }), _jsx("polygon", { fill: `url(#${gradId})`, stroke: "none", points: `0,28 ${pts} 100,28` }), _jsx("polyline", { fill: "none", stroke: color, strokeWidth: "1.4", vectorEffect: "non-scaling-stroke", strokeLinejoin: "round", points: pts })] }));
64
+ }
65
+ return (_jsxs("div", { title: title, style: {
66
+ flex: 1, minWidth: 0, padding: '5px 12px 0',
67
+ borderRight: last ? undefined : `1px solid ${C.hair}`,
68
+ display: 'flex', flexDirection: 'column',
69
+ }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8 }, children: [_jsx("span", { style: label, children: heading }), _jsx("span", { style: { fontFamily: mono, fontSize: 10.5, color: C.dim, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }, children: value })] }), _jsx("div", { style: { flex: 1, display: 'flex', alignItems: 'flex-end' }, children: spark })] }));
70
+ }
71
+ /** Bars over reranker scores: log-odds go NEGATIVE, so score/max explodes
72
+ * past 100% when the max is negative. Min-max into [0.06, 1] — bars only
73
+ * ever rank WITHIN one retrieval, so the scale is local by design. */
74
+ function relScale(scores) {
75
+ const min = Math.min(...scores);
76
+ const max = Math.max(...scores);
77
+ const span = max - min;
78
+ return (v) => (span <= 0 ? 1 : 0.06 + 0.94 * ((v - min) / span));
79
+ }
80
+ /** The human argument out of a tool call's JSON args — the query or url the
81
+ * agent actually wrote, not the envelope around it. */
82
+ function argSummary(args) {
83
+ try {
84
+ const a = JSON.parse(args);
85
+ const v = a.query ?? a.url ?? Object.values(a).find((x) => typeof x === 'string');
86
+ return typeof v === 'string' ? v : args;
87
+ }
88
+ catch {
89
+ return args;
90
+ }
91
+ }
92
+ /** The launchable page out of a call's args, when the tool took one. */
93
+ function argUrl(args) {
94
+ try {
95
+ const a = JSON.parse(args);
96
+ return typeof a.url === 'string' && /^https?:\/\//.test(a.url) ? a.url : null;
97
+ }
98
+ catch {
99
+ return null;
100
+ }
101
+ }
102
+ /** ↗ beside a fetched page — opens it in a new tab without toggling the row. */
103
+ function LinkOut({ url }) {
104
+ return (_jsx("a", { href: url, target: "_blank", rel: "noopener noreferrer", title: url, onClick: (e) => e.stopPropagation(), style: { color: C.agent, textDecoration: 'none', flex: 'none', fontSize: 11, lineHeight: 1 }, children: "\u2197" }));
105
+ }
106
+ /** One tokenizer pass over pretty-printed JSON — keys, strings, numbers,
107
+ * and literals get the pane's own palette; everything else stays dim. */
108
+ const JSON_TOKEN = /("(?:[^"\\]|\\.)*")(\s*:)?|(-?\d+\.?\d*(?:[eE][+-]?\d+)?)|(\btrue\b|\bfalse\b|\bnull\b)/g;
109
+ function highlightJson(src) {
110
+ const out = [];
111
+ let last = 0;
112
+ let i = 0;
113
+ let match;
114
+ JSON_TOKEN.lastIndex = 0;
115
+ while ((match = JSON_TOKEN.exec(src)) !== null) {
116
+ if (match.index > last)
117
+ out.push(_jsx("span", { style: { color: C.dim }, children: src.slice(last, match.index) }, i++));
118
+ if (match[1] !== undefined) {
119
+ const isKey = match[2] !== undefined;
120
+ out.push(_jsx("span", { style: { color: isKey ? '#8430ce' : C.ok }, children: match[1] }, i++));
121
+ if (isKey)
122
+ out.push(_jsx("span", { style: { color: C.dim }, children: match[2] }, i++));
123
+ }
124
+ else if (match[3] !== undefined) {
125
+ out.push(_jsx("span", { style: { color: C.agent }, children: match[3] }, i++));
126
+ }
127
+ else {
128
+ out.push(_jsx("span", { style: { color: C.warn }, children: match[4] }, i++));
129
+ }
130
+ last = JSON_TOKEN.lastIndex;
131
+ }
132
+ if (last < src.length)
133
+ out.push(_jsx("span", { style: { color: C.dim }, children: src.slice(last) }, i++));
134
+ return out;
135
+ }
136
+ /** A tool result in full: pretty-printed and token-colored when it parses as
137
+ * JSON, scrollable past ~14 lines, and the copy control carries EVERY byte —
138
+ * the block never truncates. */
139
+ function JsonBlock({ text }) {
140
+ const [copied, setCopied] = useState(false);
141
+ const { pretty, body } = useMemo(() => {
142
+ try {
143
+ const p = JSON.stringify(JSON.parse(text), null, 2);
144
+ return { pretty: p, body: highlightJson(p) };
145
+ }
146
+ catch {
147
+ return { pretty: text, body: null };
148
+ }
149
+ }, [text]);
150
+ const copy = () => {
151
+ navigator.clipboard.writeText(pretty).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1200); }, () => { });
152
+ };
153
+ return (_jsxs("div", { style: { position: 'relative', margin: '4px 0' }, children: [_jsx("span", { onClick: copy, style: {
154
+ position: 'absolute', top: 5, right: 9, zIndex: 1, cursor: 'pointer',
155
+ fontSize: 9.5, color: copied ? C.ok : C.dim, background: '#f8f9fa',
156
+ border: `1px solid ${C.border}`, borderRadius: 3, padding: '1px 6px',
157
+ }, children: copied ? 'copied' : 'copy' }), _jsx("pre", { style: {
158
+ maxHeight: 240, overflow: 'auto', margin: 0, padding: '8px 10px',
159
+ background: '#f8f9fa', border: `1px solid ${C.border}`, borderRadius: 4,
160
+ fontFamily: mono, fontSize: 10.5, lineHeight: 1.55,
161
+ whiteSpace: 'pre-wrap', wordBreak: 'break-word',
162
+ }, children: body ?? pretty })] }));
163
+ }
164
+ function resultRows(parsed) {
165
+ const arr = Array.isArray(parsed) ? parsed
166
+ : parsed && typeof parsed === 'object'
167
+ ? (Array.isArray(parsed.results) ? parsed.results
168
+ : Array.isArray(parsed.hits) ? parsed.hits
169
+ : null)
170
+ : null;
171
+ if (!arr || arr.length === 0)
172
+ return null;
173
+ const rows = [];
174
+ for (const o of arr) {
175
+ if (!o || typeof o !== 'object')
176
+ return null;
177
+ const r = o;
178
+ const head = r.title ?? r.heading;
179
+ if (typeof head !== 'string')
180
+ return null;
181
+ rows.push({
182
+ head,
183
+ sub: typeof r.url === 'string' ? r.url : typeof r.file === 'string' ? r.file : undefined,
184
+ body: typeof r.snippet === 'string' ? r.snippet : typeof r.text === 'string' ? r.text : undefined,
185
+ ...(typeof r.score === 'number' ? { score: r.score } : {}),
186
+ });
187
+ }
188
+ return rows;
189
+ }
190
+ const fmtS = (s) => s >= 60 ? `${Math.floor(s / 60)}m${String(Math.round(s % 60)).padStart(2, '0')}s` : `${s.toFixed(1)}s`;
191
+ function runEndS(m) {
192
+ // The answer can land AFTER the last lane closes (synthesis emits it at
193
+ // the very end) — the recorded run end wins over lane completions.
194
+ let end = m.runEndedAt ?? 0;
195
+ for (const l of m.lanes.values()) {
196
+ if (l.doneAt !== null)
197
+ end = Math.max(end, l.doneAt);
198
+ }
199
+ return m.runStartAt === null ? 0 : Math.max(0, (end - m.runStartAt) / 1000);
200
+ }
201
+ // ═══════════════════════════════════════════════════════════════
202
+ export function DevPane({ bridge, controls = [], title }) {
203
+ // The store is a per-bridge SINGLETON: a remount reattaches to the running
204
+ // fold (full history intact) instead of restarting it and desyncing. It is
205
+ // deliberately NOT destroyed on unmount — it lives with the page, like the
206
+ // bridge itself (dev-gated; a production stream folds nothing).
207
+ const store = devStoreFor(bridge);
208
+ const rev = useStore(store, (s) => s.rev);
209
+ const m = store.getState().model;
210
+ const [open, setOpen] = useState(false);
211
+ // The FAB renders only when the wire said dev — production ships inert.
212
+ if (!m.dev)
213
+ return null;
214
+ if (!open) {
215
+ // The closed cog still tells the story: amber ? = the planner is waiting
216
+ // on the USER (the one state that blocks everything), red = an agent
217
+ // failed this run, blue = agents live right now. Nothing = quiet.
218
+ const liveCount = [...m.lanes.values()].filter((l) => l.doneAt === null).length;
219
+ const failed = [...m.lanes.values()].some((l) => l.outcome === 'failed');
220
+ // iOS-style badge: always the one red, meaning carried by the glyph —
221
+ // ? = the planner waits on the user, ! = an agent failed, n = agents live.
222
+ const badge = m.clarifying
223
+ ? '?'
224
+ : liveCount > 0 ? String(liveCount)
225
+ : failed ? '!' : null;
226
+ const fabTitle = m.clarifying
227
+ ? 'the planner is waiting on your answer'
228
+ : liveCount > 0
229
+ ? `${liveCount} agent${liveCount === 1 ? '' : 's'} live${failed ? ' · one failed' : ''}`
230
+ : failed ? 'an agent failed — open for the reason' : 'dev pane (LLOYAL_DEV)';
231
+ return (_jsxs("button", { onClick: () => setOpen(true), "aria-label": "open the dev pane", title: fabTitle, style: {
232
+ position: 'fixed', right: 20, bottom: 20, width: 44, height: 44,
233
+ borderRadius: '50%', background: '#fff', border: `1px solid #dadce0`,
234
+ display: 'grid', placeItems: 'center', color: C.dim, cursor: 'pointer',
235
+ boxShadow: '0 2px 8px rgba(32,33,36,.14)', zIndex: 40, padding: 0,
236
+ }, children: [badge && (_jsx("span", { style: {
237
+ position: 'absolute', top: -6, right: -6, minWidth: 20, height: 20,
238
+ borderRadius: 10, background: 'linear-gradient(180deg, #ff544a 0%, #ff2d1f 100%)',
239
+ color: '#fff', fontSize: 12, fontWeight: 600,
240
+ display: 'grid', placeItems: 'center', padding: '0 6px', boxSizing: 'border-box',
241
+ lineHeight: 1, boxShadow: '0 1px 3px rgba(0,0,0,.35)',
242
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
243
+ }, children: badge })), _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", children: [_jsx("circle", { cx: "12", cy: "12", r: "3" }), _jsx("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" })] })] }));
244
+ }
245
+ return _jsx(Pane, { store: store, m: m, rev: rev, controls: controls, title: title, onClose: () => setOpen(false) });
246
+ }
247
+ // ═══ the docked pane ═══
248
+ function Pane({ store, m, rev, controls, title, onClose }) {
249
+ const [tab, setTab] = useState('timeline');
250
+ const [selAgent, setSelAgent] = useState(null);
251
+ const [feedW, setFeedW] = useState(feedWidthPref);
252
+ const toolColor = useToolColors();
253
+ const paneRef = useRef(null);
254
+ // The pane is fixed, so the document doesn't know it's there — reserve its
255
+ // height as body padding while open, so the app's bottom content can always
256
+ // scroll clear of it. Restored on close/unmount.
257
+ useEffect(() => {
258
+ const prev = document.body.style.paddingBottom;
259
+ const apply = () => {
260
+ if (paneRef.current)
261
+ document.body.style.paddingBottom = `${paneRef.current.offsetHeight}px`;
262
+ };
263
+ apply();
264
+ window.addEventListener('resize', apply);
265
+ return () => {
266
+ window.removeEventListener('resize', apply);
267
+ document.body.style.paddingBottom = prev;
268
+ };
269
+ }, []);
270
+ useEffect(() => {
271
+ const onKey = (e) => {
272
+ if (e.key !== 'Escape')
273
+ return;
274
+ if (selAgent !== null)
275
+ setSelAgent(null);
276
+ else
277
+ onClose();
278
+ };
279
+ window.addEventListener('keydown', onKey);
280
+ return () => window.removeEventListener('keydown', onKey);
281
+ }, [selAgent, onClose]);
282
+ const live = isLive(m);
283
+ const lanes = [...m.lanes.values()];
284
+ const done = lanes.filter((l) => l.doneAt !== null).length;
285
+ const toolsSeen = [...new Set(m.retrievals.map((r) => r.tool))];
286
+ const tabStyle = (on) => ({
287
+ padding: '0 13px', display: 'flex', alignItems: 'center', gap: 6, fontSize: 12,
288
+ color: on ? C.text : C.dim, borderRight: `1px solid ${C.border}`, cursor: 'pointer',
289
+ background: on ? '#fff' : 'transparent', fontWeight: on ? 500 : 400,
290
+ boxShadow: on ? `inset 0 2px 0 ${C.text}` : undefined,
291
+ });
292
+ return (_jsxs("div", { ref: paneRef, style: {
293
+ position: 'fixed', left: 0, right: 0, bottom: 0, height: 'min(560px, 72vh)',
294
+ background: '#fff', borderTop: '1px solid #bdc1c6', display: 'flex',
295
+ flexDirection: 'column', zIndex: 50, fontSize: 12, color: C.text,
296
+ fontFamily: 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif',
297
+ }, children: [_jsxs("div", { role: "tablist", style: { height: 30, display: 'flex', alignItems: 'stretch', background: C.chromeBg, borderBottom: '1px solid #d9dce1', flex: 'none' }, children: [['timeline', 'sources', 'settings'].map((t) => {
298
+ const settled = t === 'sources' ? m.retrievals.filter((r) => r.settledAt !== null).length : 0;
299
+ return (_jsxs("div", { role: "tab", tabIndex: 0, "aria-selected": tab === t, style: tabStyle(tab === t), onClick: () => setTab(t), onKeyDown: (e) => { if (e.key === 'Enter' || e.key === ' ') {
300
+ e.preventDefault();
301
+ setTab(t);
302
+ } }, children: [t[0].toUpperCase() + t.slice(1), settled > 0 && _jsx("span", { style: { marginLeft: 5, fontFamily: mono, fontSize: 9.5, color: C.faint }, children: settled })] }, t));
303
+ }), _jsx("span", { style: { flex: 1 } }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 12, padding: '0 12px', fontSize: 10.5, color: C.dim }, children: [_jsxs("span", { style: { display: 'inline-flex', alignItems: 'center', gap: 5 }, children: [_jsx("span", { style: { width: 14, height: 5, borderRadius: 3, background: C.agent, display: 'inline-block' } }), " agent"] }), _jsxs("span", { style: { display: 'inline-flex', alignItems: 'center', gap: 5 }, children: [_jsx("span", { style: { width: 14, height: 5, display: 'inline-block', borderRadius: 3, background: 'repeating-linear-gradient(135deg, rgba(26,115,232,.45) 0 4px, rgba(26,115,232,.12) 4px 9px)' } }), " waiting"] }), toolsSeen.map((t) => (_jsxs("span", { style: { display: 'inline-flex', alignItems: 'center', gap: 5 }, children: [_jsx(Badge, { color: toolColor(t), letter: letterOf(t), size: 15 }), " ", t] }, t))), _jsxs("span", { style: { fontFamily: mono, display: 'inline-flex', alignItems: 'center', gap: 7 }, children: [_jsx("span", { style: { width: 7, height: 7, borderRadius: '50%', background: live ? C.ok : C.faint } }), live ? 'live' : m.runStartAt === null ? 'idle' : 'run complete'] }), _jsx("span", { style: { cursor: 'pointer', color: C.dim }, onClick: onClose, title: "collapse to the cog", children: "\u2715" })] })] }), tab === 'timeline' && (_jsxs("div", { style: { flex: 1, minHeight: 0, display: 'flex' }, children: [_jsx(Timeline, { m: m, rev: rev, store: store, selAgent: selAgent, onSelect: setSelAgent, toolColor: toolColor }), selAgent !== null && m.lanes.has(selAgent) && (_jsxs(_Fragment, { children: [_jsx(FeedResizer, { width: feedW, onWidth: (w) => { feedWidthPref = w; setFeedW(w); } }), _jsx(AgentFeed, { m: m, lane: m.lanes.get(selAgent), toolColor: toolColor, onClose: () => setSelAgent(null), onJump: setSelAgent, nowMs: store.getState().paintedAt, width: feedW })] }))] })), tab === 'sources' && _jsx(Sources, { m: m, toolColor: toolColor }), tab === 'settings' && _jsx(Settings, { m: m, controls: controls, send: (c) => store.send(c) }), _jsxs("div", { style: {
304
+ height: 24, display: 'flex', alignItems: 'center', gap: 14, padding: '0 12px',
305
+ borderTop: `1px solid ${C.border}`, background: '#f8f9fa', fontSize: 10.5, color: C.dim, flex: 'none',
306
+ }, children: [_jsxs("span", { style: { fontFamily: mono }, children: [m.runStartAt === null
307
+ ? 'no run yet'
308
+ : `run 0:00 – ${fmtS(live ? (performance.now() - m.runStartAt) / 1000 : runEndS(m))} · ${lanes.length} agents (${done} done) · ${m.retrievals.length} tool calls`, title ? ` · ${title}` : ''] }), _jsx("span", { style: { flex: 1 } }), _jsxs("span", { style: { color: C.faint }, children: [tab === 'timeline' && 'click a lane or badge → detail · esc closes · drag pans (detaches follow) · double-click re-follows', tab === 'sources' && 'what entered the context — every number is a recorded event field', tab === 'settings' && 'session-tier controls apply next run · boot rows are fixed for this run'] })] })] }));
309
+ }
310
+ // ═══ shared atoms ═══
311
+ function Badge({ color, letter, size = 18, hollow = false, title }) {
312
+ return (_jsx("span", { title: title, style: {
313
+ width: size, height: size, borderRadius: '50%', flex: 'none',
314
+ display: 'inline-grid', placeItems: 'center',
315
+ background: hollow ? '#fff' : color,
316
+ color: hollow ? color : '#fff',
317
+ border: hollow ? `2px solid ${color}` : '2px solid #fff',
318
+ boxShadow: '0 1px 2px rgba(0,0,0,.18)',
319
+ fontSize: size * 0.53, fontWeight: 700, fontFamily: 'system-ui, sans-serif',
320
+ }, children: letter }));
321
+ }
322
+ // ═══ Timeline ═══
323
+ const SPAN_LIVE = 75; // seconds visible while following
324
+ function Timeline({ m, rev, store, selAgent, onSelect, toolColor }) {
325
+ const [follow, setFollow] = useState(true);
326
+ const [panWindow, setPanWindow] = useState(null);
327
+ const trackRef = useRef(null);
328
+ // Observed, not read-at-render: a completed run stops repainting, so a
329
+ // layout change (the detail pane mounting) would leave a render-time
330
+ // measurement stale forever.
331
+ const [trackWidth, setTrackWidth] = useState(900);
332
+ useEffect(() => {
333
+ const el = trackRef.current;
334
+ if (!el)
335
+ return;
336
+ const ro = new ResizeObserver(() => setTrackWidth(el.clientWidth));
337
+ ro.observe(el);
338
+ setTrackWidth(el.clientWidth);
339
+ return () => ro.disconnect();
340
+ }, []);
341
+ const drag = useRef(null);
342
+ const t0 = m.runStartAt;
343
+ const live = isLive(m);
344
+ const paintedAt = store.getState().paintedAt;
345
+ // The stepped live edge: quantized to the repaint grid, never per-token.
346
+ const nowS = t0 === null ? 0 : Math.floor(((paintedAt - t0) / 1000) / (EDGE_STEP / 1000)) * (EDGE_STEP / 1000);
347
+ const endS = live ? nowS : runEndS(m);
348
+ // The WINDOW: fixed span; parked at the run start, then SLIDES — never grows.
349
+ let w0;
350
+ let w1;
351
+ if (panWindow && !follow)
352
+ ({ w0, w1 } = panWindow);
353
+ else if (live) {
354
+ if (nowS <= SPAN_LIVE) {
355
+ w0 = 0;
356
+ w1 = SPAN_LIVE;
357
+ }
358
+ else {
359
+ w0 = Math.floor(nowS - SPAN_LIVE * 0.85);
360
+ w1 = w0 + SPAN_LIVE;
361
+ }
362
+ }
363
+ else {
364
+ w0 = -2;
365
+ w1 = Math.max(SPAN_LIVE, endS + 6);
366
+ }
367
+ const GUTTER = 168;
368
+ const width = trackWidth;
369
+ const track = Math.max(50, width - GUTTER);
370
+ const px = (s) => GUTTER + ((s - w0) / (w1 - w0)) * track;
371
+ const on = (s) => s >= w0 && s <= w1;
372
+ const secOf = (at) => (t0 === null ? 0 : (at - t0) / 1000);
373
+ const span = w1 - w0;
374
+ const step = span > 240 ? 60 : span > 120 ? 30 : span > 60 ? 15 : 5;
375
+ const ticks = [];
376
+ for (let t = Math.ceil(w0 / step) * step; t <= w1; t += step)
377
+ ticks.push(t);
378
+ const lanes = [...m.lanes.values()];
379
+ const strip = pressureStrip(m, 200);
380
+ const pct = pressurePercent(m);
381
+ const host = m.host;
382
+ const lastHost = host[host.length - 1];
383
+ const onMouseDown = (e) => { drag.current = { x: e.clientX, w0, w1 }; };
384
+ const onMouseMove = (e) => {
385
+ if (!drag.current)
386
+ return;
387
+ if (Math.abs(e.clientX - drag.current.x) > 3 && follow)
388
+ setFollow(false);
389
+ const dt = ((drag.current.x - e.clientX) / track) * (drag.current.w1 - drag.current.w0);
390
+ setPanWindow({ w0: drag.current.w0 + dt, w1: drag.current.w1 + dt });
391
+ };
392
+ const onMouseUp = () => { drag.current = null; };
393
+ return (_jsxs("div", { style: { flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', minHeight: 0 }, children: [_jsxs("div", { style: { height: 54, borderBottom: `1px solid ${C.border}`, display: 'flex', flex: 'none' }, children: [_jsx(MetricCell, { heading: "pressure", color: C.agent, values: strip.slice(-60).map((p) => p.pct), value: pct === null ? '—' : `${pct}% · ${m.pressure[m.pressure.length - 1]?.cellsUsed.toLocaleString()} / ${m.pressure[m.pressure.length - 1]?.nCtx.toLocaleString()}`, title: "kv cells used \u2014 context pressure on the model side" }), _jsx(MetricCell, { heading: "cpu", color: HOST_CPU, values: host.slice(-60).map((h) => h.cpu), value: lastHost ? `${lastHost.cpu}%` : '—', title: "this process's cpu, % of the whole machine" }), _jsx(MetricCell, { heading: "mem", color: "#9aa0a6", values: host.slice(-60).filter((h) => h.memUsedMb !== null).map((h) => h.memUsedMb), value: lastHost && lastHost.memUsedMb !== null && lastHost.memTotalMb > 0
394
+ ? `${(lastHost.memUsedMb / 1024).toFixed(1)} / ${Math.round(lastHost.memTotalMb / 1024)}G` : '—', title: "machine memory in use, honestly counted (vm_stat / MemAvailable)" }), _jsx(MetricCell, { heading: "harness", color: "#5f6368", last: true, values: host.slice(-60).map((h) => h.rssMb), value: lastHost ? `${(lastHost.rssMb / 1024).toFixed(1)}G` : '—', title: "this process's resident memory \u2014 weights + KV + runtime" })] }), _jsxs("div", { style: { height: 20, display: 'flex', borderBottom: `1px solid ${C.border}`, flex: 'none' }, children: [_jsx("div", { style: { width: GUTTER, flex: 'none', padding: '4px 0 0 14px' }, children: _jsx("span", { style: label, children: lanes.length ? `${lanes.length} agents` : '' }) }), _jsx("div", { style: { flex: 1, position: 'relative', fontFamily: mono }, children: ticks.map((t) => (_jsx("span", { style: { position: 'absolute', left: px(t) - GUTTER, fontSize: 9, color: C.faint }, children: t >= 60 ? `${Math.floor(t / 60)}m${t % 60 ? String(t % 60).padStart(2, '0') : ''}` : `${t}s` }, t))) })] }), _jsxs("div", { ref: trackRef, style: { flex: 1, position: 'relative', overflowY: 'auto', overflowX: 'hidden', cursor: drag.current ? 'grabbing' : undefined }, onMouseDown: onMouseDown, onMouseMove: onMouseMove, onMouseUp: onMouseUp, onMouseLeave: onMouseUp, onDoubleClick: () => { setFollow(true); setPanWindow(null); }, children: [!follow && live && (_jsx("button", { onClick: (e) => { e.stopPropagation(); setFollow(true); setPanWindow(null); }, style: {
395
+ position: 'absolute', top: 6, right: 10, zIndex: 3, cursor: 'pointer',
396
+ fontSize: 10.5, padding: '3px 10px', borderRadius: 12, border: `1px solid ${C.border}`,
397
+ background: '#fff', color: C.agentDark, fontWeight: 600, boxShadow: '0 1px 4px rgba(32,33,36,.12)',
398
+ }, children: "\u27F3 follow live" })), _jsxs("div", { style: { position: 'absolute', left: GUTTER, right: 0, top: 0, bottom: 0, pointerEvents: 'none' }, children: [ticks.map((t) => (_jsx("div", { style: { position: 'absolute', left: px(t) - GUTTER, top: 0, bottom: 0, width: 1, background: t % (step * 3) === 0 ? C.border : C.hair } }, t))), live && on(nowS) && (_jsxs(_Fragment, { children: [_jsx("div", { style: { position: 'absolute', left: px(nowS) - GUTTER, right: 0, top: 0, bottom: 0, background: C.panelBg } }), _jsx("div", { style: { position: 'absolute', left: px(nowS) - GUTTER, top: 0, bottom: 0, width: 1, background: C.text, zIndex: 1 } }), _jsx("div", { style: { position: 'absolute', left: px(nowS) - GUTTER, top: 2, transform: 'translateX(-50%)', fontSize: 8.5, background: C.text, color: '#fff', padding: '0 5px', borderRadius: 2, zIndex: 2, fontFamily: mono }, children: "now" })] }))] }), lanes.map((l) => (_jsx(Lane, { m: m, l: l, px: px, on: on, secOf: secOf, nowS: nowS, live: live, selected: selAgent === l.agentId, toolColor: toolColor, onClick: () => onSelect(selAgent === l.agentId ? null : l.agentId), gutter: GUTTER, windowEnd: w1 }, l.agentId)))] })] }));
399
+ }
400
+ const label = {
401
+ fontSize: 10, fontWeight: 600, letterSpacing: '.06em', textTransform: 'uppercase', color: '#b0b6c2',
402
+ };
403
+ function Lane({ m, l, px, on, secOf, nowS, live, selected, toolColor, onClick, gutter, windowEnd }) {
404
+ const s = secOf(l.spawnedAt);
405
+ const e = l.doneAt === null ? nowS : secOf(l.doneAt);
406
+ const capL = px(s);
407
+ const capR = px(Math.min(e, windowEnd));
408
+ const running = l.doneAt === null;
409
+ const color = l.outcome === 'failed' ? C.fail : l.role === 'synth' ? C.agentDark : C.agent;
410
+ const rgb = l.outcome === 'failed' ? '179,38,30' : l.role === 'synth' ? '23,78,166' : '26,115,232';
411
+ const myCalls = m.retrievals.filter((r) => r.agentId === l.agentId);
412
+ const myGuards = m.interventions.filter((iv) => iv.agentId === l.agentId && iv.kind !== 'nudge');
413
+ const stripe = (x0, x1, grey = false) => {
414
+ if (x1 <= x0)
415
+ return null;
416
+ const g = grey ? '95,99,104' : rgb;
417
+ return (_jsx("div", { style: {
418
+ position: 'absolute', top: 12, height: 14, left: x0 - gutter, width: x1 - x0,
419
+ background: `repeating-linear-gradient(135deg, rgba(${g},.45) 0 4px, rgba(${g},.08) 4px 9px), #fff`,
420
+ } }, `w${x0}`));
421
+ };
422
+ const glyph = l.outcome === 'failed' ? '✗' : l.outcome === 'recovered' ? '↻' : '✓';
423
+ const glyphBg = l.outcome === 'failed' ? C.fail : l.outcome === 'recovered' ? C.agent : C.ok;
424
+ const endLabel = l.role === 'planner' && m.plan
425
+ ? `plan · ${m.plan.tasks.length} tasks · ${fmtS(e - s)}`
426
+ : l.outcome === 'recovered' ? `recovered · ${fmtS(e - s)}`
427
+ : l.outcome === 'failed' ? `${l.failReason ?? l.dropReason ?? 'failed'} · ${fmtS(e - s)}` : fmtS(e - s);
428
+ const endColor = l.outcome === 'failed' ? C.fail : l.outcome === 'recovered' ? C.agent : '#3c4043';
429
+ return (_jsxs("div", { onClick: onClick, role: "button", tabIndex: 0, onKeyDown: keyActivate(onClick), "aria-label": `open agent ${l.agentId}`, style: {
430
+ display: 'flex', height: 38, borderTop: `1px solid ${C.hair}`, position: 'relative', cursor: 'pointer',
431
+ background: selected ? C.chromeBg : undefined,
432
+ boxShadow: selected ? `inset 3px 0 0 ${C.text}` : undefined,
433
+ }, children: [_jsxs("div", { style: { width: gutter, flex: 'none', display: 'flex', alignItems: 'baseline', gap: 6, padding: '12px 0 0 14px', fontSize: 11.5 }, children: [_jsx("span", { style: { fontWeight: 600 }, children: l.role ?? 'agent' }), _jsxs("span", { style: { color: C.dim, fontSize: 10.5, fontFamily: mono }, children: ["#", l.agentId] })] }), _jsxs("div", { style: { flex: 1, position: 'relative', overflow: 'hidden' }, children: [capR > gutter && capL < px(windowEnd) && (_jsxs(_Fragment, { children: [_jsx("div", { style: {
434
+ position: 'absolute', top: 12, height: 14,
435
+ left: Math.max(capL, gutter) - gutter,
436
+ width: Math.max(4, capR - Math.max(capL, gutter)),
437
+ background: color,
438
+ borderRadius: running ? '7px 0 0 7px' : capL < gutter ? '0 7px 7px 0' : 7,
439
+ } }), myCalls.filter((r) => r.settledAt !== null).map((r) => stripe(Math.max(px(secOf(r.dispatchedAt)), Math.max(capL, gutter)), Math.min(px(Math.min(secOf(r.settledAt), e)), capR))), myCalls.filter((r) => r.settledAt === null).map((r) => {
440
+ const from = Math.max(px(secOf(r.dispatchedAt)), Math.max(capL, gutter));
441
+ if (!r.retry)
442
+ return stripe(from, capR);
443
+ const parkFrom = Math.max(px(secOf(r.retry.at)), Math.max(capL, gutter));
444
+ return (_jsxs(React.Fragment, { children: [stripe(from, Math.min(parkFrom, capR)), stripe(Math.min(parkFrom, capR), capR, true)] }, `lw${r.dispatchedAt}`));
445
+ }), l.clarify && stripe(Math.max(px(secOf(l.clarify.askedAt)), Math.max(capL, gutter)), Math.min(px(l.clarify.answeredAt === null ? e : secOf(l.clarify.answeredAt)), capR), true), running && live && (_jsx("span", { style: {
446
+ position: 'absolute', left: capR - gutter - 3, top: 15.5, width: 7, height: 7,
447
+ borderRadius: '50%', background: C.agentDark, zIndex: 2,
448
+ } })), l.prunedAt !== null && l.doneAt !== null && secOf(l.prunedAt) > e + 0.5 && (_jsx("div", { title: "the branch's KV stayed resident until the pool pruned it", style: {
449
+ position: 'absolute', top: 12, height: 14,
450
+ left: Math.max(px(e), gutter) - gutter,
451
+ width: Math.max(2, Math.min(px(Math.min(secOf(l.prunedAt), windowEnd)), px(windowEnd)) - Math.max(px(e), gutter)),
452
+ background: 'repeating-linear-gradient(135deg, rgba(95,99,104,.30) 0 4px, rgba(95,99,104,.06) 4px 9px)',
453
+ borderRadius: '0 7px 7px 0',
454
+ } }))] })), l.clarify && on(secOf(l.clarify.askedAt)) && (_jsx("span", { style: { position: 'absolute', left: px(secOf(l.clarify.askedAt)) - gutter - 9, top: 8.5, zIndex: 2 }, title: `asked the user — ${l.clarify.questions.join(' · ')}`, children: _jsx(Badge, { color: C.dim, letter: "?", hollow: true }) })), l.clarify?.answeredAt != null && on(secOf(l.clarify.answeredAt)) && (_jsx("span", { style: { position: 'absolute', left: px(secOf(l.clarify.answeredAt)) - gutter - 9, top: 8.5, zIndex: 2 }, title: "the user replied", children: _jsx(Badge, { color: C.dim, letter: "?" }) })), myGuards.filter((g) => g.kind === 'guard' || g.kind === 'auth').map((g, i) => on(secOf(g.at)) ? (_jsx("span", { style: { position: 'absolute', left: px(secOf(g.at)) - gutter - 9, top: 8.5, zIndex: 2 }, title: `blocked — ${g.guard ?? g.kind}: ${g.message ?? g.tool ?? ''}`, children: _jsx(Badge, { color: C.warn, letter: "\u2298", hollow: true }) }, `g${i}`)) : null), myCalls.map((r, i) => {
455
+ const cs = secOf(r.dispatchedAt);
456
+ const ce = r.settledAt === null ? null : secOf(r.settledAt);
457
+ const err = r.result !== null && r.result.includes('"error"');
458
+ const collides = ce !== null && px(ce) - px(cs) < 20;
459
+ return (_jsxs(React.Fragment, { children: [on(cs) && !collides && (_jsx("span", { style: { position: 'absolute', left: px(cs) - gutter - 9, top: 8.5, zIndex: 2 }, title: `${r.tool} · ${r.args.slice(0, 140)}`, children: _jsx(Badge, { color: toolColor(r.tool), letter: letterOf(r.tool), hollow: true }) })), ce !== null && on(ce) && (_jsx("span", { style: { position: 'absolute', left: px(ce) - gutter - 9, top: 8.5, zIndex: 2 }, title: `${r.tool} → ${fmtS(ce - cs)}${r.contextAvailablePercent != null ? ` · ctx ${r.contextAvailablePercent}%` : ''}`, children: _jsx(Badge, { color: err ? C.fail : toolColor(r.tool), letter: letterOf(r.tool) }) }))] }, `c${i}`));
460
+ }), l.doneAt !== null && on(e) && (_jsxs(_Fragment, { children: [_jsx("span", { style: { position: 'absolute', left: px(e) - gutter + 8, top: 8.5, zIndex: 2 }, children: _jsx("span", { style: {
461
+ width: 18, height: 18, borderRadius: '50%', display: 'inline-grid', placeItems: 'center',
462
+ background: glyphBg, color: '#fff', border: '2px solid #fff',
463
+ boxShadow: '0 1px 2px rgba(0,0,0,.18)', fontSize: 10, fontWeight: 700,
464
+ }, children: glyph }) }), _jsx("span", { style: {
465
+ position: 'absolute', left: px(e) - gutter + 33, top: 13, fontSize: 10.5,
466
+ fontFamily: mono, color: endColor, whiteSpace: 'nowrap', pointerEvents: 'none',
467
+ }, children: endLabel })] })), running && on(e) && (_jsx("span", { style: {
468
+ position: 'absolute', left: px(e) - gutter + 10, top: 13, fontSize: 10.5,
469
+ fontFamily: mono, color: C.agentDark, whiteSpace: 'nowrap', pointerEvents: 'none',
470
+ }, children: (() => {
471
+ if (l.clarify && l.clarify.answeredAt === null)
472
+ return 'waiting on you…';
473
+ if (l.inflightTool) {
474
+ const parked = m.retrievals.find((r) => r.agentId === l.agentId && r.settledAt === null && r.retry !== null);
475
+ if (parked?.retry) {
476
+ const left = Math.ceil((parked.retry.afterMs - (nowS - secOf(parked.retry.at)) * 1000) / 1000);
477
+ return left > 0
478
+ ? `${l.inflightTool} — rate-limited · retry in ${left}s`
479
+ : `${l.inflightTool} — retrying (attempt ${parked.retry.attempt + 1})…`;
480
+ }
481
+ return `${l.inflightTool}…`;
482
+ }
483
+ return l.role === 'synth' ? 'streaming report…' : 'thinking…';
484
+ })() }))] })] }));
485
+ }
486
+ // ═══ agent detail: the story feed ═══
487
+ /** Drag handle on the agent feed's left edge — pull to widen the panel
488
+ * (the chart stretches with it); double-click restores the default. The
489
+ * strip is invisible: the cursor change is the affordance, and the feed's
490
+ * own border stays the visual line. Width persists for the page session. */
491
+ const FEED_W_DEFAULT = 420;
492
+ let feedWidthPref = FEED_W_DEFAULT;
493
+ function FeedResizer({ width, onWidth }) {
494
+ const drag = useRef(null);
495
+ return (_jsx("div", { style: { width: 7, margin: '0 -3.5px', flex: 'none', cursor: 'col-resize', zIndex: 3, position: 'relative', userSelect: 'none', touchAction: 'none' }, title: "drag to resize \\u00b7 double-click to reset", onDoubleClick: () => { feedWidthPref = FEED_W_DEFAULT; onWidth(FEED_W_DEFAULT); }, onPointerDown: (e) => {
496
+ e.preventDefault();
497
+ drag.current = { x: e.clientX, w: width };
498
+ e.currentTarget.setPointerCapture(e.pointerId);
499
+ }, onPointerMove: (e) => {
500
+ if (!drag.current)
501
+ return;
502
+ const w = drag.current.w + (drag.current.x - e.clientX);
503
+ onWidth(Math.round(Math.min(Math.max(w, 320), Math.max(480, window.innerWidth - 380))));
504
+ }, onPointerUp: (e) => {
505
+ drag.current = null;
506
+ e.currentTarget.releasePointerCapture(e.pointerId);
507
+ } }));
508
+ }
509
+ /** The epistemics instrument: entropy (area) and surprisal (line) in nats
510
+ * over the agent's WHOLE span — x is anchored time, so samples never slide,
511
+ * and amber ticks mark where tool results landed (a spike right after one
512
+ * means the injected content destabilized the model). The y-ceiling floors
513
+ * at 4 nats and clips at p95: a calm run reads calm instead of auto-zooming
514
+ * its own noise into drama. Gaps are honest — a tool wait produces no
515
+ * tokens, so the chart breaks rather than bridging it. */
516
+ const SURPRISAL_COLOR = '#7c3aed';
517
+ function EpistemicsChart({ m, lane, nowMs }) {
518
+ const e = lane.epistemics;
519
+ if (e.length < 2)
520
+ return null;
521
+ const t0 = lane.spawnedAt;
522
+ const t1 = Math.max(lane.doneAt ?? nowMs, e[e.length - 1].at, t0 + 1000);
523
+ const B = 140;
524
+ const H = 54;
525
+ // bucket to pixel columns: mean entropy (the band), MAX surprisal (spikes
526
+ // are the signal — a mean would erase exactly what matters).
527
+ const hSum = Array(B).fill(0);
528
+ const hN = Array(B).fill(0);
529
+ const sMax = Array(B).fill(null);
530
+ for (const smp of e) {
531
+ const i = Math.min(B - 1, Math.max(0, Math.floor(((smp.at - t0) / (t1 - t0)) * B)));
532
+ hSum[i] += smp.h;
533
+ hN[i] += 1;
534
+ sMax[i] = sMax[i] === null ? smp.s : Math.max(sMax[i], smp.s);
535
+ }
536
+ const sorted = e.flatMap((x) => [x.h, x.s]).sort((a, b) => a - b);
537
+ const yMax = Math.max(4, sorted[Math.floor(sorted.length * 0.95)] ?? 4);
538
+ const y = (v) => H - (Math.min(v, yMax) / yMax) * (H - 2);
539
+ // runs of consecutive non-empty buckets → separate path segments
540
+ const segs = [];
541
+ let run = [];
542
+ for (let i = 0; i < B; i++) {
543
+ if (hN[i] > 0)
544
+ run.push(i);
545
+ else if (run.length) {
546
+ segs.push(run);
547
+ run = [];
548
+ }
549
+ }
550
+ if (run.length)
551
+ segs.push(run);
552
+ const entropyArea = segs.map((seg) => {
553
+ const pts = seg.map((i) => `${i + 0.5},${y(hSum[i] / hN[i]).toFixed(1)}`);
554
+ const x0 = seg[0] + 0.5;
555
+ const x1 = seg[seg.length - 1] + 0.5;
556
+ return `M ${x0},${H} L ${pts.join(' L ')} L ${x1},${H} Z`;
557
+ }).join(' ');
558
+ const entropyLine = segs.map((seg) => 'M ' + seg.map((i) => `${i + 0.5},${y(hSum[i] / hN[i]).toFixed(1)}`).join(' L ')).join(' ');
559
+ const surprisalLine = segs.map((seg) => 'M ' + seg.map((i) => `${i + 0.5},${y(sMax[i]).toFixed(1)}`).join(' L ')).join(' ');
560
+ const ticks = m.retrievals
561
+ .filter((r) => r.agentId === lane.agentId && r.settledAt !== null)
562
+ .map((r) => ((r.settledAt - t0) / (t1 - t0)) * B)
563
+ .filter((x) => x >= 0 && x <= B);
564
+ const last = e[e.length - 1];
565
+ const ppl = lanePpl(lane);
566
+ const chip = (color, label, value, title) => (_jsxs("span", { style: { display: 'inline-flex', alignItems: 'baseline', gap: 4, whiteSpace: 'nowrap' }, title: title, children: [_jsx("span", { style: { width: 8, height: 8, borderRadius: 2, background: color, alignSelf: 'center' } }), _jsx("span", { style: { color: C.dim }, children: label }), _jsx("span", { style: { fontFamily: mono, fontSize: 10, color: C.text }, children: value.toFixed(2) })] }));
567
+ return (_jsxs("div", { style: { borderBottom: `1px solid ${C.border}` }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'baseline', gap: 10, padding: '6px 12px 4px' }, children: [_jsx("span", { style: { color: C.dim }, children: "epistemics" }), _jsx("span", { style: { flex: 1 } }), chip(C.agent, 'entropy', last.h, 'how open the model\u2019s next-token choice was \u2014 nats, over the full vocabulary'), chip(SURPRISAL_COLOR, 'surprisal', last.s, 'how unexpected the picked token was \u2014 \u2212ln p, in nats'), ppl !== null && (_jsxs("span", { style: { fontFamily: mono, fontSize: 10, color: C.faint, whiteSpace: 'nowrap' }, title: "perplexity \\u2014 exp of mean surprisal over this agent\\u2019s tokens; compare agents, lower reads more fluent", children: ["ppl ", ppl.toFixed(2)] }))] }), _jsxs("div", { style: { position: 'relative', padding: '0 12px 7px' }, children: [_jsxs("svg", { viewBox: `0 0 ${B} ${H}`, preserveAspectRatio: "none", style: { width: '100%', height: H, display: 'block' }, children: [_jsx("line", { x1: 0, y1: y(yMax / 2), x2: B, y2: y(yMax / 2), stroke: C.hair, strokeWidth: 1, vectorEffect: "non-scaling-stroke" }), _jsx("path", { d: entropyArea, fill: "rgba(26,115,232,.16)" }), _jsx("path", { d: entropyLine, fill: "none", stroke: C.agent, strokeWidth: 1.1, vectorEffect: "non-scaling-stroke" }), _jsx("path", { d: surprisalLine, fill: "none", stroke: SURPRISAL_COLOR, strokeWidth: 1, strokeOpacity: 0.75, vectorEffect: "non-scaling-stroke" }), ticks.map((x, i) => (_jsx("line", { x1: x, y1: H - 5, x2: x, y2: H, stroke: "#e8710a", strokeWidth: 2, vectorEffect: "non-scaling-stroke", children: _jsx("title", { children: "a tool result landed" }) }, i)))] }), _jsxs("span", { style: { position: 'absolute', top: 0, left: 14, fontFamily: mono, fontSize: 8.5, color: C.faint }, children: [yMax.toFixed(0), " nats"] })] })] }));
568
+ }
569
+ function AgentFeed({ m, lane, toolColor, onClose, onJump, nowMs, width }) {
570
+ const [expanded, setExpanded] = useState(() => new Set(['report']));
571
+ const toggle = (id) => {
572
+ setExpanded((prev) => {
573
+ const next = new Set(prev);
574
+ if (next.has(id))
575
+ next.delete(id);
576
+ else
577
+ next.add(id);
578
+ return next;
579
+ });
580
+ };
581
+ const calls = m.retrievals.map((r, i) => ({ r, id: `c${i}` })).filter(({ r }) => r.agentId === lane.agentId);
582
+ const interventions = m.interventions.filter((iv) => iv.agentId === lane.agentId);
583
+ const items = [];
584
+ if (lane.clarify) {
585
+ items.push({
586
+ at: lane.clarify.askedAt,
587
+ el: (_jsxs("div", { style: feedItem, children: [_jsxs("div", { style: { display: 'flex', gap: 7, alignItems: 'baseline' }, children: [_jsx("b", { style: { color: C.dim }, children: "? asked the user" }), _jsxs("span", { style: { color: '#3c4043' }, children: ["\u201C", lane.clarify.questions.join(' · '), "\u201D"] })] }), _jsx("div", { style: { margin: '3px 0 0 16px', color: lane.clarify.answeredAt === null ? C.warn : C.dim }, children: lane.clarify.answeredAt === null
588
+ ? 'waiting on the user…'
589
+ : `the user replied · ${fmtS((lane.clarify.answeredAt - lane.clarify.askedAt) / 1000)}` })] }, "clarify")),
590
+ });
591
+ }
592
+ for (const iv of interventions) {
593
+ items.push({
594
+ at: iv.at,
595
+ el: (_jsxs("div", { style: { ...feedItem, color: C.warn }, children: [_jsxs("div", { style: { display: 'flex', gap: 7, alignItems: 'baseline' }, children: [_jsx("b", { children: iv.kind === 'nudge' ? '▲ harness nudge' : '⊘ blocked' }), iv.tool && _jsxs("span", { style: { fontFamily: mono, fontSize: 10.5 }, children: [iv.tool, iv.args ? ` · ${iv.args.slice(0, 80)}` : ''] })] }), _jsxs("div", { style: { margin: '2px 0 0 16px', color: C.dim }, children: [iv.guard ? `${iv.guard} guard — ` : iv.reason ? `${iv.reason} — ` : '', iv.message ? `“${iv.message}”` : ''] })] }, `iv${iv.at}`)),
596
+ });
597
+ }
598
+ for (const { r, id } of calls) {
599
+ const open = expanded.has(id);
600
+ const err = r.result !== null && r.result.includes('"error"');
601
+ const status = r.settledAt === null
602
+ ? (r.retry ? `rate-limited · parked ${Math.round(r.retry.afterMs / 1000)}s · attempt ${r.retry.attempt}` : 'in flight')
603
+ : r.admission
604
+ ? `${r.admission.selectedPassageCount}${r.admission.totalScored != null ? ` of ${r.admission.totalScored}` : ''} passages${r.admission.admittedTokens != null ? ` · ${r.admission.admittedTokens} tok` : ''}`
605
+ : err ? 'error' : 'done';
606
+ items.push({
607
+ at: r.dispatchedAt,
608
+ el: (_jsxs("div", { style: feedItem, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 7, cursor: 'pointer' }, onClick: () => toggle(id), role: "button", tabIndex: 0, onKeyDown: keyActivate(() => toggle(id)), children: [_jsx("span", { style: { color: C.faint, fontSize: 9, width: 9, flex: 'none' }, children: open ? '▾' : '▸' }), _jsx(Badge, { color: err ? C.fail : toolColor(r.tool), letter: letterOf(r.tool), size: 14 }), _jsx("span", { style: { fontFamily: mono, fontSize: 10.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: '0 1 auto', minWidth: 0 }, children: argSummary(r.args) }), argUrl(r.args) !== null && _jsx(LinkOut, { url: argUrl(r.args) }), _jsx("span", { style: { flex: 1 } }), _jsx("span", { style: { color: err ? C.fail : C.faint, fontSize: 10.5 }, children: status })] }), open && (_jsxs("div", { style: { margin: '6px 0 2px 16px', fontSize: 11 }, children: [r.explore === false && (_jsx("div", { style: { color: C.faint, marginBottom: 3 }, children: "exploit \u2014 re-ranked against the query" })), r.admission && r.admission.topResults.length > 0 && (_jsx("div", { style: { color: C.dim, margin: '2px 0 3px' }, children: "Sections read:" })), r.admission?.topResults.map((t, i, all) => (_jsxs("div", { style: {
609
+ padding: '3px 7px', borderRadius: 3, marginTop: 2,
610
+ // admission ORDER is the signal — rank 1 deepest, fading with rank
611
+ background: `rgba(26,115,232,${(0.03 + 0.13 * (1 - i / Math.max(1, all.length - 1))).toFixed(3)})`,
612
+ }, children: [_jsxs("b", { style: { fontSize: 11 }, children: [i + 1, " \u00B7 ", t.heading] }), t.textPreview && _jsxs("div", { style: { color: C.dim, fontSize: 10.5, lineHeight: 1.45 }, children: [t.textPreview, "\u2026"] })] }, i))), !r.admission && r.result && _jsx(JsonBlock, { text: r.result })] }))] }, id)),
613
+ });
614
+ }
615
+ items.sort((a, b) => a.at - b.at);
616
+ // The planner's report IS the plan — structure, with task→agent jumps.
617
+ const research = [...m.lanes.values()].filter((l) => l.role === 'research');
618
+ const planView = lane.role === 'planner' && m.plan && (_jsxs("div", { style: feedItem, children: [_jsxs("div", { style: { display: 'flex', gap: 7, alignItems: 'baseline', marginBottom: 3 }, children: [_jsx("b", { style: { fontSize: 11 }, children: "plan" }), _jsxs("span", { style: { color: C.faint }, children: [m.plan.tasks.length, " tasks \u2014 click one to follow its agent"] })] }), m.plan.tasks.map((task, i) => {
619
+ const ag = research[i]; // fanout order = flat-mode spawn order
620
+ const g = !ag ? '' : ag.outcome === 'failed' ? '✗' : ag.outcome === 'recovered' ? '↻' : ag.doneAt === null ? '…' : '✓';
621
+ const gc = !ag ? C.faint : ag.outcome === 'failed' ? C.fail : ag.outcome === 'recovered' ? C.agent : C.ok;
622
+ return (_jsxs("div", { onClick: ag ? () => onJump(ag.agentId) : undefined, style: { display: 'flex', alignItems: 'center', gap: 8, padding: '5px 0', borderTop: `1px solid ${C.hair}`, cursor: ag ? 'pointer' : 'default' }, children: [_jsx("span", { style: { width: 16, textAlign: 'right', fontFamily: mono, fontSize: 10, color: C.faint }, children: i + 1 }), _jsx("span", { style: { flex: 1, fontSize: 11, lineHeight: 1.4 }, children: task }), ag && _jsx("span", { style: chip, children: `research ${ag.agentId}` }), _jsx("b", { style: { color: gc, width: 14, textAlign: 'center' }, children: g })] }, i));
623
+ })] }));
624
+ const reportOpen = expanded.has('report');
625
+ return (_jsxs("div", { style: {
626
+ width, flex: 'none', borderLeft: '1px solid #d9dce1', display: 'flex',
627
+ flexDirection: 'column', minHeight: 0, background: C.panelBg,
628
+ }, children: [_jsxs("div", { style: {
629
+ height: 30, flex: 'none', display: 'flex', alignItems: 'center', gap: 8, padding: '0 12px',
630
+ borderBottom: `1px solid ${C.border}`, background: '#f8f9fa',
631
+ }, children: [_jsxs("span", { style: { fontFamily: mono, fontWeight: 600, fontSize: 11 }, children: [lane.role ?? 'agent', " #", lane.agentId] }), lane.parentAgentId !== null && m.lanes.has(lane.parentAgentId) && (_jsxs("span", { style: chip, children: ["forked from #", lane.parentAgentId] })), _jsxs("span", { style: chip, children: [lane.outcome, lane.doneAt !== null && m.runStartAt !== null ? ` · ${fmtS((lane.doneAt - lane.spawnedAt) / 1000)}` : ''] }), _jsx("span", { style: { flex: 1 } }), _jsx("span", { style: { cursor: 'pointer', color: C.dim }, onClick: onClose, title: "close \u2014 the timeline returns to full width", children: "\u2715" })] }), _jsxs("div", { style: { overflowY: 'auto', flex: 1, fontSize: 11, paddingBottom: 8 }, children: [_jsx(EpistemicsChart, { m: m, lane: lane, nowMs: nowMs }), (lane.failReason || lane.dropReason) && (_jsxs("div", { style: { display: 'flex', alignItems: 'baseline', padding: '6px 12px', gap: 8 }, children: [_jsx("span", { style: { color: C.dim, width: 92, flex: 'none' }, children: "pool said" }), _jsx("span", { style: { fontFamily: mono, fontSize: 11 }, children: lane.dropReason ?? lane.failReason })] })), items.map((it) => it.el), planView, lane.role !== 'planner' && (lane.report !== null || lane.outcome === 'failed') && (_jsxs("div", { style: feedItem, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 7, cursor: 'pointer' }, onClick: () => toggle('report'), children: [_jsx("span", { style: { color: C.faint, fontSize: 9, width: 9, flex: 'none' }, children: reportOpen ? '▾' : '▸' }), _jsx("b", { style: { fontSize: 11 }, children: "report" }), _jsx("span", { style: { color: C.faint }, children: lane.report === null ? 'not delivered' : lane.reportSource === 'recovery' ? 'extracted by recovery' : 'delivered' })] }), reportOpen && lane.report !== null && (_jsx("div", { style: { margin: '6px 0 2px 16px', lineHeight: 1.5, color: '#3c4043', whiteSpace: 'pre-wrap' }, children: lane.report }))] }))] })] }));
632
+ }
633
+ const feedItem = { padding: '7px 12px', borderTop: '1px solid #eceef1' };
634
+ const chip = {
635
+ fontSize: 10, fontWeight: 600, padding: '1px 6px', borderRadius: 2, background: C.chromeBg, color: C.dim,
636
+ };
637
+ // ═══ Sources: the admission view ═══
638
+ function Sources({ m, toolColor }) {
639
+ const settled = m.retrievals.filter((r) => r.settledAt !== null);
640
+ const [pinned, setPinned] = useState(null);
641
+ const sel = pinned !== null && pinned < settled.length ? pinned : settled.length - 1;
642
+ const r = settled[sel];
643
+ const blocked = m.interventions.filter((iv) => iv.kind === 'guard' || iv.kind === 'auth');
644
+ return (_jsxs("div", { style: { flex: 1, minHeight: 0, display: 'flex' }, children: [_jsxs("div", { style: { width: 300, flex: 'none', borderRight: '1px solid #d9dce1', overflowY: 'auto' }, children: [settled.length === 0 && blocked.length === 0 && (_jsx("div", { style: { padding: '16px 14px', color: C.faint, fontSize: 11 }, children: "no results yet" })), settled.map((x, i) => {
645
+ const err = x.result !== null && x.result.includes('"error"');
646
+ return (_jsxs("div", { onClick: () => setPinned(i), role: "button", tabIndex: 0, onKeyDown: keyActivate(() => setPinned(i)), style: {
647
+ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 12px', fontSize: 11,
648
+ borderBottom: `1px solid ${C.hair}`, cursor: 'pointer',
649
+ background: i === sel ? C.chromeBg : undefined,
650
+ boxShadow: i === sel ? `inset 3px 0 0 ${C.text}` : undefined,
651
+ }, children: [_jsx(Badge, { color: err ? C.fail : toolColor(x.tool), letter: letterOf(x.tool), size: 16 }), _jsx("span", { style: { fontFamily: mono, fontSize: 10.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: '0 1 auto', minWidth: 0 }, children: argSummary(x.args) }), argUrl(x.args) !== null && _jsx(LinkOut, { url: argUrl(x.args) }), _jsx("span", { style: { flex: 1 } }), _jsx("span", { style: { fontFamily: mono, fontSize: 10, color: C.faint }, children: x.settledAt !== null ? fmtS((x.settledAt - x.dispatchedAt) / 1000) : '' })] }, i));
652
+ }), blocked.map((g, i) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, padding: '8px 12px', fontSize: 11, borderBottom: `1px solid ${C.hair}`, opacity: 0.65 }, children: [_jsx(Badge, { color: C.warn, letter: "\u2298", size: 16, hollow: true }), _jsx("span", { style: { fontFamily: mono, fontSize: 10.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }, children: g.args ?? g.tool ?? '' }), _jsx("span", { style: { fontSize: 10.5, color: C.warn }, children: "blocked" })] }, `b${i}`)))] }), _jsx("div", { style: { flex: 1, minWidth: 0, overflowY: 'auto', fontSize: 11.5 }, children: r && _jsx(AdmissionView, { r: r }) })] }));
653
+ }
654
+ function AdmissionView({ r }) {
655
+ let parsed = null;
656
+ try {
657
+ parsed = r.result ? JSON.parse(r.result) : null;
658
+ }
659
+ catch { /* not JSON */ }
660
+ const error = parsed && typeof parsed.error === 'string' ? parsed.error : null;
661
+ const alsoOnPage = parsed && Array.isArray(parsed.alsoOnPage) ? parsed.alsoOnPage : null;
662
+ const a = r.admission;
663
+ return (_jsxs("div", { children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, padding: '10px 14px', borderBottom: `1px solid ${C.border}`, flexWrap: 'wrap' }, children: [_jsxs("span", { style: { fontFamily: mono, fontSize: 11 }, children: [r.tool, " \u00B7 ", argSummary(r.args).slice(0, 160)] }), argUrl(r.args) !== null && _jsx(LinkOut, { url: argUrl(r.args) }), _jsx("span", { style: { flex: 1 } }), r.explore === false && (_jsx("span", { style: { ...fchip, background: C.warnBg, color: C.warn }, children: "exploit \u2014 re-ranked against the query" })), r.explore === true && _jsx("span", { style: fchip, children: "explore \u2014 scored against the agent's task" }), _jsxs("span", { style: fchip, children: ["agent ", r.agentId] })] }), error && (_jsxs("div", { style: { padding: '8px 14px' }, children: [_jsx("b", { style: { color: C.fail }, children: error }), _jsx("p", { style: { margin: '3px 0 0', color: C.dim, fontSize: 11 }, children: "the agent was told and pivoted" })] })), !error && r.exploitChunks && r.exploitChunks.length > 0 && _jsx(SlopeChart, { r: r }), !error && a && a.topResults.length > 0 && (_jsxs("div", { style: { padding: '6px 16px 2px', maxWidth: 760 }, children: [a.topResults.map((t, i) => {
664
+ const rel = relScale(a.topResults.map((x) => x.score))(t.score);
665
+ return (_jsxs("div", { style: { padding: '6px 0', borderTop: i ? `1px solid ${C.hair}` : undefined }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 7 }, children: [_jsx("span", { style: { width: 14, textAlign: 'right', fontFamily: mono, fontSize: 10, color: C.faint }, children: i + 1 }), _jsx("span", { style: { width: 150, flex: 'none' }, children: _jsx("span", { style: { display: 'block', height: 5, borderRadius: 3, background: C.agent, width: `${Math.round(rel * 100)}%` } }) }), _jsx("b", { style: { fontSize: 11 }, children: t.heading }), _jsx("span", { style: { flex: 1 } })] }), t.textPreview && (_jsxs("p", { style: { margin: '2px 0 0 21px', color: C.dim, fontSize: 10.5, lineHeight: 1.45 }, children: [t.textPreview, "\u2026"] }))] }, i));
666
+ }), _jsx("div", { style: cutline, children: a.tokenBudget != null
667
+ ? `admitted ${a.selectedPassageCount}${a.totalScored != null ? ` of ${a.totalScored}` : ''} · ${a.admittedTokens?.toLocaleString() ?? '?'} of ${a.tokenBudget.toLocaleString()} token budget`
668
+ : a.threshold != null
669
+ ? `admitted ${a.selectedPassageCount}${a.totalScored != null ? ` of ${a.totalScored}` : ''} at the score floor`
670
+ : `admitted ${a.selectedPassageCount}` }), alsoOnPage && alsoOnPage.length > 0 && (_jsxs("div", { style: { padding: '2px 0 10px', display: 'flex', flexWrap: 'wrap', gap: 6, alignItems: 'center' }, children: [alsoOnPage.map((h) => _jsx("span", { style: { ...fchip, opacity: 0.7 }, children: h }, h)), _jsx("span", { style: { color: C.faint, fontSize: 10.5 }, children: "\u2014 left on the page, offered to the agent as topics" })] }))] })), !error && (() => {
671
+ if (a && a.topResults.length > 0)
672
+ return null; // the admission view above already rendered
673
+ const rows = resultRows(parsed);
674
+ if (rows) {
675
+ const scored = rows.some((x) => x.score !== undefined);
676
+ const rel = relScale(rows.map((x) => x.score ?? 0));
677
+ return (_jsxs("div", { style: { padding: '6px 16px 10px', maxWidth: 820 }, children: [rows.map((x, i) => (_jsxs("div", { style: { padding: '7px 0', borderTop: i ? `1px solid ${C.hair}` : undefined }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: { width: 14, textAlign: 'right', fontFamily: mono, fontSize: 10, color: C.faint }, children: i + 1 }), scored && (_jsx("span", { style: { width: 130, flex: 'none' }, children: _jsx("span", { style: { display: 'block', height: 5, borderRadius: 3, background: C.agent, width: `${Math.round(rel(x.score ?? 0) * 100)}%` } }) })), _jsx("b", { style: { fontSize: 11.5 }, children: x.head }), x.sub && _jsx("span", { style: { color: C.faint, fontSize: 10.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: x.sub })] }), x.body && _jsxs("p", { style: { margin: `2px 0 0 ${scored ? 160 : 22}px`, color: C.dim, fontSize: 11, lineHeight: 1.5 }, children: [x.body.slice(0, 280), x.body.length > 280 ? '…' : ''] })] }, i))), scored && _jsx("div", { style: { padding: '6px 0 0 22px', color: C.faint, fontSize: 10 }, children: "bars are relative to this retrieval's best match" })] }));
678
+ }
679
+ const content = parsed && typeof parsed === 'object' && typeof parsed.content === 'string'
680
+ ? parsed.content : null;
681
+ if (content !== null) {
682
+ const also = parsed && Array.isArray(parsed.alsoOnPage)
683
+ ? (parsed.alsoOnPage) : null;
684
+ return (_jsxs("div", { style: { padding: '8px 16px 10px', maxWidth: 820 }, children: [_jsxs("div", { style: { color: C.dim, fontSize: 11, whiteSpace: 'pre-wrap', lineHeight: 1.55 }, children: [content.slice(0, 1200), content.length > 1200 ? '…' : ''] }), also && also.length > 0 && (_jsxs("div", { style: { padding: '8px 0 0', display: 'flex', flexWrap: 'wrap', gap: 6, alignItems: 'center' }, children: [also.map((h) => _jsx("span", { style: { ...fchip, opacity: 0.7 }, children: h }, h)), _jsx("span", { style: { color: C.faint, fontSize: 10.5 }, children: "\u2014 left on the page, offered to the agent as topics" })] }))] }));
685
+ }
686
+ if (r.result) {
687
+ return (_jsx("div", { style: { padding: '8px 14px', maxWidth: 900 }, children: _jsx(JsonBlock, { text: r.result }) }));
688
+ }
689
+ return null;
690
+ })()] }));
691
+ }
692
+ /** Two rankings of the same chunks, side by side — each column HEADED by the
693
+ * question it ranks for; crossing lines ARE the re-rank. No legend. */
694
+ function SlopeChart({ r }) {
695
+ const chunks = r.exploitChunks;
696
+ const ROW = 26;
697
+ const W = 70;
698
+ const HEAD = 40;
699
+ const byTask = [...chunks].sort((a, b) => b.combinedScore - a.combinedScore);
700
+ const byTool = [...chunks].sort((a, b) => b.toolQueryScore - a.toolQueryScore);
701
+ const H = chunks.length * ROW;
702
+ let agentQ = '';
703
+ try {
704
+ agentQ = String(JSON.parse(r.args || '{}').query ?? '');
705
+ }
706
+ catch { /* raw args */ }
707
+ return (_jsxs("div", { style: { display: 'flex', alignItems: 'flex-start', padding: '14px 16px 4px', maxWidth: 900 }, children: [_jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsxs("div", { style: { height: HEAD, textAlign: 'right' }, children: [_jsx("div", { style: label, children: "for this call's query" }), agentQ && _jsxs("div", { style: { fontFamily: mono, fontSize: 10, color: C.faint }, children: ["\u201C", agentQ, "\u201D"] })] }), byTool.map((x, i) => (_jsxs("div", { style: { height: ROW, display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 7, color: '#3c4043' }, children: [_jsx("span", { style: { fontSize: 11 }, children: x.heading }), _jsx("span", { style: { width: 14, fontFamily: mono, fontSize: 10, color: C.faint }, children: i + 1 })] }, x.heading)))] }), _jsx("svg", { width: W, height: H, style: { flex: 'none', margin: `${HEAD}px 10px 0` }, children: byTask.map((x, ti) => {
708
+ const li = byTool.indexOf(x);
709
+ return _jsx("line", { x1: 0, y1: li * ROW + 13, x2: W, y2: ti * ROW + 13, stroke: C.agent, strokeWidth: 1.8 }, x.heading);
710
+ }) }), _jsxs("div", { style: { flex: 1.2, minWidth: 0 }, children: [_jsx("div", { style: { height: HEAD }, children: _jsx("div", { style: label, children: "re-ranked with the run query" }) }), byTask.map((x, i) => (_jsxs("div", { style: { height: ROW, display: 'flex', alignItems: 'center', gap: 7 }, children: [_jsx("span", { style: { width: 14, textAlign: 'right', fontFamily: mono, fontSize: 10, color: C.faint }, children: i + 1 }), _jsx("span", { style: { fontSize: 11, fontWeight: 600 }, children: x.heading })] }, x.heading)))] })] }));
711
+ }
712
+ const fchip = {
713
+ fontSize: 10, fontWeight: 600, padding: '2px 8px', borderRadius: 3, background: C.chromeBg, color: C.dim,
714
+ };
715
+ const cutline = {
716
+ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 0 4px', color: C.faint, fontSize: 10,
717
+ borderTop: '1px dashed #dadce0', marginTop: 6,
718
+ };
719
+ // ═══ Settings: category nav → harness (master list + detail) · ability pages ═══
720
+ /** What the detail panel knows about each well-known harness key: what it is,
721
+ * and how to change it. Prose is product copy — one clause per sentence. */
722
+ /** Config path → the ConfigOrigin field carrying its rung, so the
723
+ * exception note (env/cli overrode the manifest) fires for read-only rows
724
+ * too — full-rung provenance display stays demoted by design. */
725
+ const ORIGIN_KEYS = {
726
+ 'model.path': 'modelPath',
727
+ 'model.reranker': 'reranker',
728
+ 'model.nCtx': 'nCtx',
729
+ 'model.gpu': 'gpu',
730
+ 'sources.outputDir': 'outputDir',
731
+ 'defaults.reasoningMode': 'reasoningMode',
732
+ };
733
+ const SETTING_META = {
734
+ 'defaults.effort': {
735
+ desc: 'Run effort preset — agent budget, planner breadth, recovery cap.',
736
+ how: 'Change it here; it applies to your next run and is remembered locally. The committed default lives in harness.yml → defaults.effort.',
737
+ },
738
+ 'defaults.reasoningMode': {
739
+ desc: 'flat runs one research wave over the plan; deep lets agents recurse into sub-plans.',
740
+ how: 'Change it here; it applies to your next run.',
741
+ },
742
+ 'sources.outputDir': {
743
+ desc: 'Where per-query run-dirs and the session trace are written. Empty means where the harness started.',
744
+ how: 'Edit harness.yml → sources.outputDir; the next run picks it up.',
745
+ },
746
+ 'defaults.maxTurns': {
747
+ desc: 'Turn cap per agent run.',
748
+ how: 'Edit harness.yml → defaults.maxTurns; the next run picks it up.',
749
+ },
750
+ 'model.path': {
751
+ desc: 'Filesystem path or catalog id of the reasoning model.',
752
+ how: 'Saved changes load at the next start; this run keeps the model it booted with.',
753
+ },
754
+ 'model.reranker': {
755
+ desc: 'The admission judge — a pointwise yes/no reranker that gates what enters the context.',
756
+ how: 'Saved changes load at the next start.',
757
+ },
758
+ 'model.nCtx': {
759
+ desc: 'Context window of the one shared llama_context — every branch leases cells out of this budget.',
760
+ how: 'Edit harness.yml → model.llm.context, then restart.',
761
+ },
762
+ 'model.branches': {
763
+ desc: 'Concurrent sequences — createContext takes it as nSeqMax. Each sequence holds its own KV lease.',
764
+ how: 'Edit harness.yml → model.llm.branches, then restart.',
765
+ },
766
+ 'model.kvCache': {
767
+ desc: 'KV cache type for the attention layers — raise for precision, lower for memory.',
768
+ how: 'Edit harness.yml → model.llm.kvCache, then restart.',
769
+ },
770
+ 'model.gpu': {
771
+ desc: 'Which native backend the process loaded — picked once at start. A configured backend fails loud if unavailable, never silently CPU.',
772
+ how: 'A deploy choice: set harness.yml → model.llm.gpu (or LLOYAL_GPU), then restart.',
773
+ },
774
+ };
775
+ const TIER_NOTE = {
776
+ session: 'applies to the next run',
777
+ reload: 'saved now — a restart loads it',
778
+ boot: 'fixed for this run',
779
+ };
780
+ function Settings({ m, controls, send }) {
781
+ const [cat, setCat] = useState('harness');
782
+ const [selKey, setSelKey] = useState(controls[0]?.key ?? 'model.path');
783
+ // The nav lists INSTALLED abilities (`abilities:state` descriptors) — not
784
+ // merely configured ones, or the page you'd use to configure an ability
785
+ // could never appear. Harnesses that don't emit descriptors degrade to the
786
+ // redacted config keys.
787
+ const abilities = m.abilities
788
+ ? m.abilities.map((a) => a.name)
789
+ : m.config && typeof m.config.abilities === 'object' && m.config.abilities !== null
790
+ ? Object.keys(m.config.abilities)
791
+ : [];
792
+ if (!m.config) {
793
+ return (_jsx("div", { style: { flex: 1, display: 'grid', placeItems: 'center', color: C.faint, fontSize: 11.5 }, children: "this harness has not emitted config:loaded \u2014 the inspector has nothing to show" }));
794
+ }
795
+ const navItem = (name, on) => (_jsx("div", { onClick: () => setCat(name), role: "button", tabIndex: 0, onKeyDown: keyActivate(() => setCat(name)), style: {
796
+ padding: '7px 16px', fontSize: 12, cursor: 'pointer',
797
+ color: on ? C.text : C.dim, fontWeight: on ? 600 : 400,
798
+ background: on ? '#fff' : undefined,
799
+ borderLeft: on ? `3px solid ${C.text}` : '3px solid transparent',
800
+ }, children: name }, name));
801
+ return (_jsxs("div", { style: { flex: 1, minHeight: 0, display: 'flex' }, children: [_jsxs("div", { style: { width: 148, flex: 'none', borderRight: '1px solid #d9dce1', paddingTop: 10, background: C.panelBg, overflowY: 'auto' }, children: [navItem('harness', cat === 'harness'), abilities.length > 0 && _jsx("div", { style: { ...label, padding: '12px 16px 3px' }, children: "abilities" }), abilities.map((a) => navItem(a, cat === a))] }), cat === 'harness'
802
+ ? _jsx(HarnessSettings, { m: m, controls: controls, send: send, selKey: selKey, onSelect: setSelKey })
803
+ : _jsx(AbilityPage, { m: m, name: cat, send: send })] }));
804
+ }
805
+ function HarnessSettings({ m, controls, send, selKey, onSelect }) {
806
+ const config = m.config;
807
+ const byTier = (tier) => Object.entries(KEY_TIERS).filter(([, t]) => t === tier).map(([k]) => k);
808
+ const controlFor = (key) => controls.find((c) => c.key === key);
809
+ const row = (key) => {
810
+ const ctl = controlFor(key);
811
+ const value = ctl ? ctl.read(config) : readConfigPath(config, key);
812
+ if (value === undefined && !ctl)
813
+ return null; // skip-if-absent: basic has no defaults block
814
+ const selected = selKey === key;
815
+ return (_jsxs("div", { onClick: () => onSelect(key), role: "button", tabIndex: 0, onKeyDown: keyActivate(() => onSelect(key)), style: {
816
+ display: 'flex', alignItems: 'center', gap: 8, padding: '5px 14px 5px 11px', minHeight: 36,
817
+ borderLeft: selected ? `3px solid ${C.text}` : '3px solid transparent', cursor: 'pointer',
818
+ background: selected ? C.chromeBg : undefined,
819
+ }, children: [_jsx("span", { style: { fontFamily: mono, fontSize: 11.5, fontWeight: 500 }, children: key }), _jsx("span", { style: { flex: 1 } }), ctl?.note && _jsx("span", { style: { color: C.faint, fontSize: 10.5, marginRight: 10, flex: 'none' }, children: ctl.note }), ctl ? (_jsx("span", { style: { display: 'flex', border: '1px solid #dadce0', borderRadius: 4, overflow: 'hidden', width: 276, flex: 'none' }, children: ctl.values.map((v) => (_jsx("span", { onClick: (e) => { e.stopPropagation(); onSelect(key); send({ type: ctl.command, [ctl.field]: v }); }, role: "button", tabIndex: 0, "aria-pressed": v === value, onKeyDown: keyActivate(() => { onSelect(key); send({ type: ctl.command, [ctl.field]: v }); }), style: {
820
+ flex: 1, fontSize: 11, padding: '5px 0', textAlign: 'center', cursor: 'pointer',
821
+ background: v === value ? C.text : '#fff', color: v === value ? '#fff' : C.dim,
822
+ fontWeight: v === value ? 500 : 400, borderLeft: '1px solid #e8eaed',
823
+ }, children: v }, v))) })) : (_jsxs("span", { style: { fontFamily: mono, fontSize: 11, color: C.dim, maxWidth: 300, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: [value === undefined || value === null || value === '' ? '—' : String(value), key === 'model.branches' && _jsx("span", { style: { color: C.faint }, children: " \u2192 nSeqMax" })] }))] }, key));
824
+ };
825
+ const head = (t) => (_jsxs("div", { style: { display: 'flex', alignItems: 'baseline', gap: 8, padding: '13px 14px 3px' }, children: [_jsx("span", { style: label, children: t }), _jsx("span", { style: { color: C.faint, fontSize: 10.5 }, children: TIER_NOTE[t] })] }));
826
+ const meta = SETTING_META[selKey];
827
+ const tier = KEY_TIERS[selKey];
828
+ // The exception case, surfaced exactly when true: something outside the
829
+ // manifest set this value. No badges anywhere else.
830
+ const originKey = controlFor(selKey)?.originKey ?? ORIGIN_KEYS[selKey];
831
+ const origin = originKey && m.origin ? m.origin[originKey] : undefined;
832
+ const overridden = origin === 'env' || origin === 'cli';
833
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { style: { flex: 1, minWidth: 0, overflowY: 'auto', paddingBottom: 10 }, children: [head('session'), byTier('session').map(row), head('reload'), byTier('reload').map(row), head('boot'), byTier('boot').map(row)] }), _jsxs("div", { style: { width: 400, flex: 'none', borderLeft: '1px solid #d9dce1', overflowY: 'auto', padding: '16px 20px', background: C.panelBg }, children: [meta ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: { fontFamily: mono, fontSize: 13, fontWeight: 500 }, children: selKey }), tier && _jsx("span", { style: chip, children: TIER_NOTE[tier] })] }), _jsx("p", { style: { maxWidth: 340, margin: '8px 0 0', fontSize: 12, lineHeight: 1.55, color: '#3c4043' }, children: meta.desc }), _jsx("div", { style: { ...label, marginTop: 16 }, children: "changing it" }), _jsx("p", { style: { maxWidth: 340, margin: '6px 0 0', fontSize: 12, lineHeight: 1.55, color: '#3c4043' }, children: meta.how }), overridden && (_jsxs("div", { style: {
834
+ display: 'flex', alignItems: 'baseline', gap: 8, padding: '8px 11px', marginTop: 14,
835
+ background: C.warnBg, border: `1px solid ${C.warnBorder}`, borderRadius: 4, fontSize: 11, maxWidth: 360,
836
+ }, children: [_jsx("b", { children: "currently overridden" }), _jsxs("span", { style: { color: C.dim }, children: [origin === 'env' ? 'an environment variable' : 'a command-line flag', " is overriding the manifest \u2014 the value shown is the one in effect."] })] }))] })) : (_jsx("span", { style: { color: C.faint }, children: "select a setting" })), m.lastSavedTo !== undefined && (_jsx("div", { style: { marginTop: 18, fontFamily: mono, fontSize: 10.5, color: C.dim }, children: m.lastSavedTo === null
837
+ ? 'applied for this session — a served session has no local file'
838
+ : `saved → ${m.lastSavedTo} (local, not committed)` }))] })] }));
839
+ }
840
+ function fieldsOf(a) {
841
+ const props = a.configSchema?.properties;
842
+ if (!props)
843
+ return [];
844
+ const required = new Set(a.configSchema?.required ?? []);
845
+ return Object.entries(props).map(([key, raw]) => {
846
+ const prop = raw ?? {};
847
+ const secret = prop['x-secret'] === true;
848
+ return {
849
+ key,
850
+ badge: secret ? 'SECRET' : required.has(key) ? 'REQUIRED' : 'OPTIONAL',
851
+ secret,
852
+ description: prop.description,
853
+ stored: a.config[key] !== undefined,
854
+ };
855
+ });
856
+ }
857
+ /** One editable config field. Values are write-only on this wire — the input
858
+ * never prefills; the placeholder carries the set-state. Saving dispatches
859
+ * set_app_config with the entered field (whole-replace semantics until the
860
+ * per-key merge helper lands — exact for the shipped single-field schemas). */
861
+ function AbilityField({ name, field, send }) {
862
+ const [draft, setDraft] = useState('');
863
+ const [saved, setSaved] = useState(false);
864
+ const save = () => {
865
+ const v = draft.trim();
866
+ if (!v)
867
+ return;
868
+ send({ type: 'set_app_config', name, values: { [field.key]: v } });
869
+ setDraft('');
870
+ setSaved(true);
871
+ };
872
+ return (_jsxs("div", { style: { marginTop: 14 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: { fontFamily: mono, fontSize: 11.5, fontWeight: 500 }, children: field.key }), _jsx("span", { style: {
873
+ ...chip,
874
+ background: field.secret ? C.warnBg : C.chromeBg,
875
+ color: field.secret ? C.warn : C.dim,
876
+ }, children: field.badge }), (field.stored || saved) && _jsx("span", { style: { color: C.ok, fontSize: 10.5 }, children: "set \u2713" })] }), field.description && (_jsx("div", { style: { color: C.faint, fontSize: 10.5, marginTop: 2 }, children: field.description })), _jsxs("div", { style: { display: 'flex', gap: 6, marginTop: 6 }, children: [_jsx("input", { type: field.secret ? 'password' : 'text', value: draft, onChange: (e) => { setDraft(e.target.value); }, onKeyDown: (e) => { if (e.key === 'Enter')
877
+ save(); }, placeholder: field.stored || saved ? 'set ✓ — enter to replace' : 'not set', style: {
878
+ width: 320, fontSize: 11, fontFamily: mono, padding: '5px 8px',
879
+ border: '1px solid #dadce0', borderRadius: 4,
880
+ } }), _jsx("button", { type: "button", onClick: save, style: {
881
+ font: 'inherit', fontSize: 11, border: '1px solid #dadce0', background: C.text,
882
+ color: '#fff', borderRadius: 4, padding: '2px 12px', cursor: 'pointer',
883
+ }, children: "save" })] })] }));
884
+ }
885
+ /** An ability's page: schema-driven config form (the reference app's field
886
+ * grammar), write-only on this wire. Falls back to the redacted key-presence
887
+ * inspector when the harness never sent descriptors. */
888
+ function AbilityPage({ m, name, send }) {
889
+ const info = m.abilities?.find((a) => a.name === name);
890
+ const stored = info?.config
891
+ ?? m.config?.abilities?.[name]
892
+ ?? {};
893
+ const fields = info ? fieldsOf(info) : [];
894
+ const setCount = fields.length > 0
895
+ ? fields.filter((f) => f.stored).length
896
+ : Object.keys(stored).length;
897
+ return (_jsxs("div", { style: { flex: 1, minWidth: 0, overflowY: 'auto', padding: '16px 22px' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: { fontFamily: mono, fontSize: 13, fontWeight: 500 }, children: info?.title ?? name }), fields.length > 0 && (_jsxs("span", { style: { color: setCount === fields.length ? C.ok : C.dim, fontSize: 10.5 }, children: [setCount, " of ", fields.length, " set", setCount === fields.length ? ' ✓' : ''] })), _jsx("span", { style: chip, children: "applies to the next run" }), info && !info.enabled && (_jsx("span", { style: chip, title: "installed but not enabled \u2014 configure it here; it enables at the next session boot", children: "not enabled" }))] }), info?.description && (_jsx("p", { style: { maxWidth: 480, margin: '6px 0 0', fontSize: 11.5, color: C.dim }, children: info.description })), _jsx("p", { style: { maxWidth: 480, margin: '6px 0 0', fontSize: 11, color: C.faint }, children: "Values are write-only on this wire \u2014 the form shows which keys are set, never what they hold." }), fields.map((f) => _jsx(AbilityField, { name: name, field: f, send: send }, f.key)), fields.length === 0 && Object.keys(stored).map((k) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 10, marginTop: 12 }, children: [_jsx("span", { style: { width: 140, flex: 'none', fontFamily: mono, fontSize: 11.5 }, children: k }), _jsx("span", { style: { color: C.ok, fontSize: 11 }, children: "set \u2713" })] }, k))), fields.length === 0 && Object.keys(stored).length === 0 && (_jsx("p", { style: { marginTop: 12, color: C.faint, fontSize: 11 }, children: "this ability declares no config" }))] }));
898
+ }
899
+ //# sourceMappingURL=react.js.map