@lloyal-labs/dev-tools 0.1.1 → 0.3.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/README.md +9 -5
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +15 -2
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +209 -57
- package/dist/react.js.map +1 -1
- package/dist/store.d.ts +7 -2
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +1 -0
- package/dist/store.js.map +1 -1
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
|
-
* The dev pane's web/desktop surface:
|
|
4
|
-
*
|
|
3
|
+
* The dev pane's web/desktop surface: the harness view's LAYOUT SHELL. The
|
|
4
|
+
* app renders inside the shell's scroll container and the pane docks BELOW
|
|
5
|
+
* it as its own flex region — Timeline · Sources · Settings. Opening the
|
|
6
|
+
* pane shrinks the app's viewport (the Chrome DevTools anchoring); it never
|
|
7
|
+
* covers the app.
|
|
5
8
|
*
|
|
6
|
-
*
|
|
9
|
+
* Wraps the app's view ONCE (the view is its children); it subscribes to the same bridge stream
|
|
7
10
|
* through {@link createDevStore} (wire-gated: a production stream folds
|
|
8
11
|
* nothing but `config:loaded`). Rendering is hand-rolled — no timeline
|
|
9
12
|
* library: the design's invariants (fixed-span sliding window, stepped live
|
|
@@ -26,6 +29,10 @@ const C = {
|
|
|
26
29
|
const HOST_CPU = '#00897b';
|
|
27
30
|
const TOOL_PALETTE = ['#e8710a', '#8430ce', '#00897b', '#d01884', '#827717', '#0097a7'];
|
|
28
31
|
const mono = 'ui-monospace, "SF Mono", Menlo, Consolas, monospace';
|
|
32
|
+
const runBtn = {
|
|
33
|
+
fontSize: 10.5, padding: '2px 10px', borderRadius: 11, cursor: 'pointer',
|
|
34
|
+
border: '1px solid #dadce0', background: '#fff', color: C.dim, fontWeight: 600,
|
|
35
|
+
};
|
|
29
36
|
/** Per-tool color, assigned first-seen — stable within a run. */
|
|
30
37
|
function useToolColors() {
|
|
31
38
|
const map = useRef(new Map());
|
|
@@ -37,6 +44,18 @@ function useToolColors() {
|
|
|
37
44
|
};
|
|
38
45
|
}
|
|
39
46
|
const letterOf = (name) => (name[0] || '?').toUpperCase();
|
|
47
|
+
/** Invisible companion that forces one re-render when a toast expires —
|
|
48
|
+
* the store stops repainting after the run ends, and a toast must never
|
|
49
|
+
* outlive its 8 seconds on a frozen clock. */
|
|
50
|
+
function ToastDismiss({ at }) {
|
|
51
|
+
const [, force] = useState(0);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
const left = Math.max(0, 8000 - (performance.now() - at)) + 50;
|
|
54
|
+
const t = setTimeout(() => force((n) => n + 1), left);
|
|
55
|
+
return () => clearTimeout(t);
|
|
56
|
+
}, [at]);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
40
59
|
/** Enter/Space activates a clickable — pairs with role="button" tabIndex={0}. */
|
|
41
60
|
const keyActivate = (fn) => (e) => {
|
|
42
61
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
@@ -136,9 +155,13 @@ function highlightJson(src) {
|
|
|
136
155
|
/** A tool result in full: pretty-printed and token-colored when it parses as
|
|
137
156
|
* JSON, scrollable past ~14 lines, and the copy control carries EVERY byte —
|
|
138
157
|
* the block never truncates. */
|
|
139
|
-
function JsonBlock({ text }) {
|
|
158
|
+
function JsonBlock({ text, raw = false }) {
|
|
140
159
|
const [copied, setCopied] = useState(false);
|
|
141
160
|
const { pretty, body } = useMemo(() => {
|
|
161
|
+
// raw: byte-for-byte — a compiled prompt that HAPPENS to be valid JSON
|
|
162
|
+
// must never be reformatted; what you copy is what the model saw.
|
|
163
|
+
if (raw)
|
|
164
|
+
return { pretty: text, body: null };
|
|
142
165
|
try {
|
|
143
166
|
const p = JSON.stringify(JSON.parse(text), null, 2);
|
|
144
167
|
return { pretty: p, body: highlightJson(p) };
|
|
@@ -146,7 +169,7 @@ function JsonBlock({ text }) {
|
|
|
146
169
|
catch {
|
|
147
170
|
return { pretty: text, body: null };
|
|
148
171
|
}
|
|
149
|
-
}, [text]);
|
|
172
|
+
}, [text, raw]);
|
|
150
173
|
const copy = () => {
|
|
151
174
|
navigator.clipboard.writeText(pretty).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1200); }, () => { });
|
|
152
175
|
};
|
|
@@ -199,7 +222,7 @@ function runEndS(m) {
|
|
|
199
222
|
return m.runStartAt === null ? 0 : Math.max(0, (end - m.runStartAt) / 1000);
|
|
200
223
|
}
|
|
201
224
|
// ═══════════════════════════════════════════════════════════════
|
|
202
|
-
export function DevPane({ bridge, controls = [], title }) {
|
|
225
|
+
export function DevPane({ bridge, controls = [], title, runCommands = {}, children }) {
|
|
203
226
|
// The store is a per-bridge SINGLETON: a remount reattaches to the running
|
|
204
227
|
// fold (full history intact) instead of restarting it and desyncing. It is
|
|
205
228
|
// deliberately NOT destroyed on unmount — it lives with the page, like the
|
|
@@ -208,64 +231,103 @@ export function DevPane({ bridge, controls = [], title }) {
|
|
|
208
231
|
const rev = useStore(store, (s) => s.rev);
|
|
209
232
|
const m = store.getState().model;
|
|
210
233
|
const [open, setOpen] = useState(false);
|
|
211
|
-
//
|
|
234
|
+
// While the pane is open, everything is seen live; the failure badge
|
|
235
|
+
// shows only failures newer than the last moment the pane was open.
|
|
236
|
+
const seenAtRef = useRef(0);
|
|
237
|
+
useEffect(() => { if (open)
|
|
238
|
+
seenAtRef.current = performance.now(); }, [open, rev]);
|
|
239
|
+
// The shell: app above, dev chrome below/over. Rendered in EVERY state —
|
|
240
|
+
// dev off, cog closed, pane open — so the app subtree keeps its position
|
|
241
|
+
// in the tree and never remounts when the dev flag or the pane toggles.
|
|
242
|
+
const shell = (content) => (_jsxs("div", { style: { height: '100dvh', display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: [_jsx("div", { style: { flex: 1, minHeight: 0, overflow: 'auto', position: 'relative' }, children: children }), content] }));
|
|
243
|
+
// The cog renders only when the wire said dev — production ships inert.
|
|
212
244
|
if (!m.dev)
|
|
213
|
-
return null;
|
|
245
|
+
return shell(null);
|
|
214
246
|
if (!open) {
|
|
215
247
|
// The closed cog still tells the story: amber ? = the planner is waiting
|
|
216
248
|
// on the USER (the one state that blocks everything), red = an agent
|
|
217
249
|
// failed this run, blue = agents live right now. Nothing = quiet.
|
|
218
|
-
|
|
219
|
-
const
|
|
250
|
+
// A stop can halt lanes without closing them — an ended run counts none.
|
|
251
|
+
const liveCount = isLive(m)
|
|
252
|
+
? [...m.lanes.values()].filter((l) => l.doneAt === null).length
|
|
253
|
+
: 0;
|
|
254
|
+
// A user cancel is a deliberate cull, not a failure — it never badges.
|
|
255
|
+
const failedLanes = [...m.lanes.values()].filter((l) => l.outcome === 'failed' && l.failReason !== 'user_cancel');
|
|
256
|
+
const failed = failedLanes.length > 0;
|
|
257
|
+
const lastFailAt = failedLanes.reduce((mx, l) => Math.max(mx, l.doneAt ?? 0), 0);
|
|
220
258
|
// iOS-style badge: always the one red, meaning carried by the glyph —
|
|
221
|
-
// ? = the planner waits on the user, ! = an
|
|
259
|
+
// ? = the planner waits on the user, ! = an unseen failure, n = agents live.
|
|
222
260
|
const badge = m.clarifying
|
|
223
261
|
? '?'
|
|
224
262
|
: liveCount > 0 ? String(liveCount)
|
|
225
|
-
: failed ? '!' : null;
|
|
263
|
+
: failed && lastFailAt > seenAtRef.current ? '!' : null;
|
|
226
264
|
const fabTitle = m.clarifying
|
|
227
265
|
? 'the planner is waiting on your answer'
|
|
228
266
|
: liveCount > 0
|
|
229
267
|
? `${liveCount} agent${liveCount === 1 ? '' : 's'} live${failed ? ' · one failed' : ''}`
|
|
230
268
|
: failed ? 'an agent failed — open for the reason' : 'dev pane (LLOYAL_DEV)';
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
269
|
+
// A park or failure in the last 8s surfaces as a one-line toast beside
|
|
270
|
+
// the cog. Age reads the REAL clock (paintedAt freezes when the run
|
|
271
|
+
// ends) and a timer forces the dismissal render — no immortal toasts.
|
|
272
|
+
const nowP = performance.now();
|
|
273
|
+
let toast = null;
|
|
274
|
+
let toastAt = 0;
|
|
275
|
+
for (const r of m.retrievals) {
|
|
276
|
+
if (r.retry && nowP - r.retry.at < 8000 && r.settledAt === null) {
|
|
277
|
+
toast = `${r.tool} rate-limited · retrying`;
|
|
278
|
+
toastAt = r.retry.at;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
for (const l of failedLanes) {
|
|
282
|
+
if (l.doneAt !== null && nowP - l.doneAt < 8000) {
|
|
283
|
+
toast = `agent #${l.agentId} failed — open for the reason`;
|
|
284
|
+
toastAt = l.doneAt;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return shell(_jsxs(_Fragment, { children: [toast && (_jsxs("div", { role: "status", "aria-live": "polite", style: {
|
|
288
|
+
position: 'fixed', right: 74, bottom: 28, zIndex: 40,
|
|
289
|
+
background: '#fff', border: `1px solid ${C.warnBorder}`, borderRadius: 8,
|
|
290
|
+
padding: '6px 12px', fontSize: 11.5, color: C.warn, boxShadow: '0 2px 8px rgba(32,33,36,.14)',
|
|
291
|
+
}, children: [toast, _jsx(ToastDismiss, { at: toastAt })] })), _jsxs("button", { onClick: () => setOpen(true), "aria-label": "open the dev pane", title: fabTitle, style: {
|
|
292
|
+
position: 'fixed', right: 20, bottom: 20, width: 44, height: 44,
|
|
293
|
+
borderRadius: '50%', background: '#fff', border: `1px solid #dadce0`,
|
|
294
|
+
display: 'grid', placeItems: 'center', color: C.dim, cursor: 'pointer',
|
|
295
|
+
boxShadow: '0 2px 8px rgba(32,33,36,.14)', zIndex: 40, padding: 0,
|
|
296
|
+
}, children: [badge && (_jsx("span", { style: {
|
|
297
|
+
position: 'absolute', top: -6, right: -6, minWidth: 20, height: 20,
|
|
298
|
+
borderRadius: 10, background: 'linear-gradient(180deg, #ff544a 0%, #ff2d1f 100%)',
|
|
299
|
+
color: '#fff', fontSize: 12, fontWeight: 600,
|
|
300
|
+
display: 'grid', placeItems: 'center', padding: '0 6px', boxSizing: 'border-box',
|
|
301
|
+
lineHeight: 1, boxShadow: '0 1px 3px rgba(0,0,0,.35)',
|
|
302
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
303
|
+
}, 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
304
|
}
|
|
245
|
-
return _jsx(Pane, { store: store, m: m, rev: rev, controls: controls, title: title, onClose: () => setOpen(false) });
|
|
305
|
+
return shell(_jsx(Pane, { store: store, m: m, rev: rev, controls: controls, title: title, runCommands: runCommands, onClose: () => setOpen(false) }));
|
|
246
306
|
}
|
|
247
307
|
// ═══ the docked pane ═══
|
|
248
|
-
function Pane({ store, m, rev, controls, title, onClose }) {
|
|
308
|
+
function Pane({ store, m, rev, controls, title, runCommands, onClose }) {
|
|
249
309
|
const [tab, setTab] = useState('timeline');
|
|
250
310
|
const [selAgent, setSelAgent] = useState(null);
|
|
251
311
|
const [feedW, setFeedW] = useState(feedWidthPref);
|
|
312
|
+
const [paneH, setPaneH] = useState(paneHeightPref);
|
|
252
313
|
const toolColor = useToolColors();
|
|
253
314
|
const paneRef = useRef(null);
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
//
|
|
315
|
+
// A dragged height outlives the viewport that fit it — re-clamp on window
|
|
316
|
+
// resize so the pane can never swallow the whole shell (the app area is
|
|
317
|
+
// minHeight: 0 and would collapse to nothing).
|
|
257
318
|
useEffect(() => {
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
document.body.style.paddingBottom = prev;
|
|
319
|
+
const onResize = () => {
|
|
320
|
+
setPaneH((h) => {
|
|
321
|
+
if (h === null)
|
|
322
|
+
return h;
|
|
323
|
+
const clamped = clampPaneH(h);
|
|
324
|
+
if (clamped !== h)
|
|
325
|
+
paneHeightPref = clamped;
|
|
326
|
+
return clamped;
|
|
327
|
+
});
|
|
268
328
|
};
|
|
329
|
+
window.addEventListener('resize', onResize);
|
|
330
|
+
return () => window.removeEventListener('resize', onResize);
|
|
269
331
|
}, []);
|
|
270
332
|
useEffect(() => {
|
|
271
333
|
const onKey = (e) => {
|
|
@@ -290,17 +352,30 @@ function Pane({ store, m, rev, controls, title, onClose }) {
|
|
|
290
352
|
boxShadow: on ? `inset 0 2px 0 ${C.text}` : undefined,
|
|
291
353
|
});
|
|
292
354
|
return (_jsxs("div", { ref: paneRef, style: {
|
|
293
|
-
position: '
|
|
355
|
+
position: 'relative', flex: 'none',
|
|
356
|
+
height: paneH !== null ? paneH : 'min(560px, 72vh)',
|
|
294
357
|
background: '#fff', borderTop: '1px solid #bdc1c6', display: 'flex',
|
|
295
|
-
flexDirection: 'column',
|
|
358
|
+
flexDirection: 'column', fontSize: 12, color: C.text,
|
|
296
359
|
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) => {
|
|
360
|
+
}, children: [_jsx(PaneResizer, { height: paneH, paneRef: paneRef, onHeight: (h) => { paneHeightPref = h; setPaneH(h); } }), _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
361
|
const settled = t === 'sources' ? m.retrievals.filter((r) => r.settledAt !== null).length : 0;
|
|
299
362
|
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
363
|
e.preventDefault();
|
|
301
364
|
setTab(t);
|
|
302
365
|
} }, 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))),
|
|
366
|
+
}), _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))), live && (runCommands.pause || runCommands.wrapUp || runCommands.stop) && (_jsxs("span", { style: { display: 'inline-flex', gap: 6 }, children: [runCommands.pause && (_jsx("button", { onClick: () => store.send({ type: m.pausedAt !== null ? 'resume' : 'pause' }), disabled: m.windingDownAt !== null, title: m.windingDownAt !== null
|
|
367
|
+
? 'finishing — the run is winding down'
|
|
368
|
+
: m.pausedAt !== null
|
|
369
|
+
? 'play — the next tick proceeds from the settled state'
|
|
370
|
+
: 'pause — hold at the tick boundary; branches stay resident', style: m.windingDownAt !== null ? { ...runBtn, opacity: 0.4, cursor: 'default' }
|
|
371
|
+
: m.pausedAt !== null ? { ...runBtn, color: C.ok, borderColor: '#c9e7d4' } : runBtn, children: m.pausedAt !== null ? '▶ play' : '⏸ pause' })), runCommands.wrapUp && (_jsx("button", { onClick: () => store.send({ type: 'wrap_up' }), disabled: m.pausedAt !== null || m.windingDownAt !== null, title: m.windingDownAt !== null
|
|
372
|
+
? 'agents are wrapping up and reporting'
|
|
373
|
+
: m.pausedAt !== null
|
|
374
|
+
? 'paused — press play first'
|
|
375
|
+
: 'finish up — agents wrap up and report; the trajectory is kept', style: m.pausedAt !== null || m.windingDownAt !== null
|
|
376
|
+
? { ...runBtn, opacity: 0.4, cursor: 'default' } : runBtn, children: m.windingDownAt !== null ? 'finishing…' : 'finish up' })), runCommands.stop && (_jsx("button", { onClick: () => store.send({ type: 'stop' }), title: "halt the run", style: { ...runBtn, color: C.fail, borderColor: '#f0c4c1' }, children: "stop" }))] })), _jsxs("span", { style: { fontFamily: mono, display: 'inline-flex', alignItems: 'center', gap: 7 }, children: [_jsx("span", { style: { width: 7, height: 7, borderRadius: '50%', background: live ? (m.pausedAt !== null ? C.warn : C.ok) : C.faint } }), live
|
|
377
|
+
? (m.pausedAt !== null ? 'paused' : m.windingDownAt !== null ? 'finishing' : 'live')
|
|
378
|
+
: 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, send: (c) => store.send(c), canCancel: !!runCommands.cancelAgent })] }))] })), 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
379
|
height: 24, display: 'flex', alignItems: 'center', gap: 14, padding: '0 12px',
|
|
305
380
|
borderTop: `1px solid ${C.border}`, background: '#f8f9fa', fontSize: 10.5, color: C.dim, flex: 'none',
|
|
306
381
|
}, children: [_jsxs("span", { style: { fontFamily: mono }, children: [m.runStartAt === null
|
|
@@ -391,7 +466,7 @@ function Timeline({ m, rev, store, selAgent, onSelect, toolColor }) {
|
|
|
391
466
|
};
|
|
392
467
|
const onMouseUp = () => { drag.current = null; };
|
|
393
468
|
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: {
|
|
469
|
+
? `${(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: [m.runStartAt === null && (_jsx("div", { style: { position: 'absolute', inset: 0, display: 'grid', placeItems: 'center', color: C.faint, fontSize: 11.5, pointerEvents: 'none' }, children: "run a query \u2014 agents appear here live" })), !follow && live && (_jsx("button", { onClick: (e) => { e.stopPropagation(); setFollow(true); setPanWindow(null); }, style: {
|
|
395
470
|
position: 'absolute', top: 6, right: 10, zIndex: 3, cursor: 'pointer',
|
|
396
471
|
fontSize: 10.5, padding: '3px 10px', borderRadius: 12, border: `1px solid ${C.border}`,
|
|
397
472
|
background: '#fff', color: C.agentDark, fontWeight: 600, boxShadow: '0 1px 4px rgba(32,33,36,.12)',
|
|
@@ -402,7 +477,11 @@ const label = {
|
|
|
402
477
|
};
|
|
403
478
|
function Lane({ m, l, px, on, secOf, nowS, live, selected, toolColor, onClick, gutter, windowEnd }) {
|
|
404
479
|
const s = secOf(l.spawnedAt);
|
|
405
|
-
|
|
480
|
+
// A paused run's lanes freeze at the hold — the widening gap to the
|
|
481
|
+
// now-line IS the pause, drawn honestly (no decode happened there).
|
|
482
|
+
const e = l.doneAt === null
|
|
483
|
+
? (m.pausedAt !== null ? Math.min(nowS, secOf(m.pausedAt)) : nowS)
|
|
484
|
+
: secOf(l.doneAt);
|
|
406
485
|
const capL = px(s);
|
|
407
486
|
const capR = px(Math.min(e, windowEnd));
|
|
408
487
|
const running = l.doneAt === null;
|
|
@@ -421,16 +500,24 @@ function Lane({ m, l, px, on, secOf, nowS, live, selected, toolColor, onClick, g
|
|
|
421
500
|
};
|
|
422
501
|
const glyph = l.outcome === 'failed' ? '✗' : l.outcome === 'recovered' ? '↻' : '✓';
|
|
423
502
|
const glyphBg = l.outcome === 'failed' ? C.fail : l.outcome === 'recovered' ? C.agent : C.ok;
|
|
503
|
+
// A user cancel is a deliberate cull, not a failure of the agent's own —
|
|
504
|
+
// the row wears it: faint red wash, red identity, a 'cancelled' pill.
|
|
505
|
+
const cancelled = l.outcome === 'failed' && l.failReason === 'user_cancel';
|
|
424
506
|
const endLabel = l.role === 'planner' && m.plan
|
|
425
507
|
? `plan · ${m.plan.tasks.length} tasks · ${fmtS(e - s)}`
|
|
426
508
|
: l.outcome === 'recovered' ? `recovered · ${fmtS(e - s)}`
|
|
427
|
-
:
|
|
509
|
+
: cancelled ? fmtS(e - s)
|
|
510
|
+
: l.outcome === 'failed' ? `${l.failReason ?? l.dropReason ?? 'failed'} · ${fmtS(e - s)}` : fmtS(e - s);
|
|
428
511
|
const endColor = l.outcome === 'failed' ? C.fail : l.outcome === 'recovered' ? C.agent : '#3c4043';
|
|
429
512
|
return (_jsxs("div", { onClick: onClick, role: "button", tabIndex: 0, onKeyDown: keyActivate(onClick), "aria-label": `open agent ${l.agentId}`, style: {
|
|
430
513
|
display: 'flex', height: 38, borderTop: `1px solid ${C.hair}`, position: 'relative', cursor: 'pointer',
|
|
431
|
-
background: selected ? C.chromeBg : undefined,
|
|
514
|
+
background: selected ? C.chromeBg : cancelled ? 'rgba(179,38,30,.05)' : undefined,
|
|
432
515
|
boxShadow: selected ? `inset 3px 0 0 ${C.text}` : undefined,
|
|
433
|
-
}, children: [
|
|
516
|
+
}, children: [cancelled && (_jsx("span", { style: {
|
|
517
|
+
position: 'absolute', right: 10, top: 10, zIndex: 2,
|
|
518
|
+
fontSize: 9.5, fontWeight: 600, padding: '2px 9px', borderRadius: 9,
|
|
519
|
+
color: C.fail, background: 'rgba(179,38,30,.08)', border: '1px solid #f0c4c1',
|
|
520
|
+
}, children: "cancelled" })), _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, color: cancelled ? C.fail : undefined }, children: l.role ?? 'agent' }), _jsxs("span", { style: { color: cancelled ? C.fail : 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
521
|
position: 'absolute', top: 12, height: 14,
|
|
435
522
|
left: Math.max(capL, gutter) - gutter,
|
|
436
523
|
width: Math.max(4, capR - Math.max(capL, gutter)),
|
|
@@ -490,21 +577,82 @@ function Lane({ m, l, px, on, secOf, nowS, live, selected, toolColor, onClick, g
|
|
|
490
577
|
* own border stays the visual line. Width persists for the page session. */
|
|
491
578
|
const FEED_W_DEFAULT = 420;
|
|
492
579
|
let feedWidthPref = FEED_W_DEFAULT;
|
|
580
|
+
const feedMax = () => Math.max(480, window.innerWidth - 380);
|
|
581
|
+
const clampFeedW = (w) => Math.round(Math.min(Math.max(w, 320), feedMax()));
|
|
493
582
|
function FeedResizer({ width, onWidth }) {
|
|
494
583
|
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); },
|
|
584
|
+
return (_jsx("div", { role: "separator", "aria-orientation": "vertical", "aria-label": "resize the agent feed", "aria-valuemin": 320, "aria-valuemax": feedMax(), "aria-valuenow": Math.round(width), tabIndex: 0, 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); }, onKeyDown: (e) => {
|
|
585
|
+
if (e.key === 'ArrowLeft')
|
|
586
|
+
onWidth(clampFeedW(width + 32));
|
|
587
|
+
else if (e.key === 'ArrowRight')
|
|
588
|
+
onWidth(clampFeedW(width - 32));
|
|
589
|
+
else if (e.key === 'Home')
|
|
590
|
+
onWidth(320);
|
|
591
|
+
else if (e.key === 'End')
|
|
592
|
+
onWidth(feedMax());
|
|
593
|
+
else if (e.key === 'Enter') {
|
|
594
|
+
feedWidthPref = FEED_W_DEFAULT;
|
|
595
|
+
onWidth(FEED_W_DEFAULT);
|
|
596
|
+
}
|
|
597
|
+
else
|
|
598
|
+
return;
|
|
599
|
+
e.preventDefault();
|
|
600
|
+
e.stopPropagation();
|
|
601
|
+
}, onPointerDown: (e) => {
|
|
496
602
|
e.preventDefault();
|
|
497
603
|
drag.current = { x: e.clientX, w: width };
|
|
498
604
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
499
605
|
}, onPointerMove: (e) => {
|
|
500
606
|
if (!drag.current)
|
|
501
607
|
return;
|
|
502
|
-
|
|
503
|
-
|
|
608
|
+
onWidth(clampFeedW(drag.current.w + (drag.current.x - e.clientX)));
|
|
609
|
+
}, onPointerUp: (e) => {
|
|
610
|
+
drag.current = null;
|
|
611
|
+
if (e.currentTarget.hasPointerCapture(e.pointerId))
|
|
612
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
613
|
+
}, onPointerCancel: () => { drag.current = null; }, onLostPointerCapture: () => { drag.current = null; } }));
|
|
614
|
+
}
|
|
615
|
+
/** Drag handle on the pane's TOP edge — pull up for a taller pane, exactly
|
|
616
|
+
* the Chrome DevTools gesture; double-click (or Enter) restores the
|
|
617
|
+
* responsive default (min(560px, 72vh)). Same invisible-strip affordance as
|
|
618
|
+
* FeedResizer: the cursor is the hint, the pane's own border stays the
|
|
619
|
+
* line. Height persists for the page session. Keyboard: a focusable
|
|
620
|
+
* horizontal separator — arrows step, Home/End jump to the bounds. */
|
|
621
|
+
let paneHeightPref = null;
|
|
622
|
+
const PANE_MIN = 180;
|
|
623
|
+
const paneMax = () => Math.max(PANE_MIN, window.innerHeight - 48);
|
|
624
|
+
const clampPaneH = (h) => Math.round(Math.min(Math.max(h, PANE_MIN), paneMax()));
|
|
625
|
+
function PaneResizer({ height, paneRef, onHeight }) {
|
|
626
|
+
const drag = useRef(null);
|
|
627
|
+
const current = () => height ?? (paneRef.current?.offsetHeight ?? 560);
|
|
628
|
+
return (_jsx("div", { role: "separator", "aria-orientation": "horizontal", "aria-label": "resize the pane", "aria-valuemin": PANE_MIN, "aria-valuemax": paneMax(), "aria-valuenow": Math.round(current()), tabIndex: 0, style: { position: 'absolute', top: -3.5, left: 0, right: 0, height: 7, cursor: 'ns-resize', zIndex: 3, userSelect: 'none', touchAction: 'none' }, title: "drag to resize \\u00b7 double-click to reset", onDoubleClick: () => onHeight(null), onKeyDown: (e) => {
|
|
629
|
+
if (e.key === 'ArrowUp')
|
|
630
|
+
onHeight(clampPaneH(current() + 32));
|
|
631
|
+
else if (e.key === 'ArrowDown')
|
|
632
|
+
onHeight(clampPaneH(current() - 32));
|
|
633
|
+
else if (e.key === 'Home')
|
|
634
|
+
onHeight(PANE_MIN);
|
|
635
|
+
else if (e.key === 'End')
|
|
636
|
+
onHeight(paneMax());
|
|
637
|
+
else if (e.key === 'Enter')
|
|
638
|
+
onHeight(null);
|
|
639
|
+
else
|
|
640
|
+
return;
|
|
641
|
+
e.preventDefault();
|
|
642
|
+
e.stopPropagation();
|
|
643
|
+
}, onPointerDown: (e) => {
|
|
644
|
+
e.preventDefault();
|
|
645
|
+
drag.current = { y: e.clientY, h: current() };
|
|
646
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
647
|
+
}, onPointerMove: (e) => {
|
|
648
|
+
if (!drag.current)
|
|
649
|
+
return;
|
|
650
|
+
onHeight(clampPaneH(drag.current.h + (drag.current.y - e.clientY)));
|
|
504
651
|
}, onPointerUp: (e) => {
|
|
505
652
|
drag.current = null;
|
|
506
|
-
e.currentTarget.
|
|
507
|
-
|
|
653
|
+
if (e.currentTarget.hasPointerCapture(e.pointerId))
|
|
654
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
655
|
+
}, onPointerCancel: () => { drag.current = null; }, onLostPointerCapture: () => { drag.current = null; } }));
|
|
508
656
|
}
|
|
509
657
|
/** The epistemics instrument: entropy (area) and surprisal (line) in nats
|
|
510
658
|
* over the agent's WHOLE span — x is anchored time, so samples never slide,
|
|
@@ -563,10 +711,14 @@ function EpistemicsChart({ m, lane, nowMs }) {
|
|
|
563
711
|
.filter((x) => x >= 0 && x <= B);
|
|
564
712
|
const last = e[e.length - 1];
|
|
565
713
|
const ppl = lanePpl(lane);
|
|
714
|
+
const tail = e.slice(-64);
|
|
715
|
+
const recentPpl = tail.length >= 16
|
|
716
|
+
? Math.exp(tail.reduce((a, x) => a + x.s, 0) / tail.length)
|
|
717
|
+
: null;
|
|
566
718
|
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: "
|
|
719
|
+
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: "ppl = exp of mean surprisal over ALL this agent's tokens (compare agents); recent = the same over the last ~64 \u2014 the live fluency signal, e.g. across quants", children: ["ppl ", ppl.toFixed(2), recentPpl !== null ? ` · recent ${recentPpl.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
720
|
}
|
|
569
|
-
function AgentFeed({ m, lane, toolColor, onClose, onJump, nowMs, width }) {
|
|
721
|
+
function AgentFeed({ m, lane, toolColor, onClose, onJump, nowMs, width, send, canCancel }) {
|
|
570
722
|
const [expanded, setExpanded] = useState(() => new Set(['report']));
|
|
571
723
|
const toggle = (id) => {
|
|
572
724
|
setExpanded((prev) => {
|
|
@@ -628,7 +780,7 @@ function AgentFeed({ m, lane, toolColor, onClose, onJump, nowMs, width }) {
|
|
|
628
780
|
}, children: [_jsxs("div", { style: {
|
|
629
781
|
height: 30, flex: 'none', display: 'flex', alignItems: 'center', gap: 8, padding: '0 12px',
|
|
630
782
|
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 }))] }))] })] }));
|
|
783
|
+
}, 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 } }), canCancel && lane.doneAt === null && (_jsx("button", { onClick: () => send({ type: 'cancel_agent', agentId: lane.agentId }), "aria-label": "cancel this agent", title: "cancel this agent \u2014 its branch is reclaimed; siblings continue. Works while paused: evaluate trajectories, cull, play", style: { ...runBtn, color: C.fail, borderColor: '#f0c4c1', padding: '2px 7px', display: 'inline-flex', alignItems: 'center' }, children: _jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("polyline", { points: "3 6 5 6 21 6" }), _jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), _jsx("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), _jsx("line", { x1: "14", y1: "11", x2: "14", y2: "17" })] }) })), _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.prompt && (_jsxs("div", { style: { borderBottom: `1px solid ${C.border}`, padding: '4px 12px 6px' }, children: [_jsxs("div", { role: "button", tabIndex: 0, onClick: () => toggle('prompt'), onKeyDown: keyActivate(() => toggle('prompt')), style: { display: 'flex', alignItems: 'baseline', gap: 7, cursor: 'pointer', padding: '2px 0' }, title: "the compiled prompt suffix that seeded this agent. In shared-spine mode the system + tool header lives on the spine PREFIX (inherited via fork) and is not repeated here; recursive agents inherit further parent context the same way", children: [_jsx("span", { style: { color: C.faint, fontSize: 9, width: 9, flex: 'none' }, children: expanded.has('prompt') ? '▾' : '▸' }), _jsx("span", { style: { color: C.dim }, children: "prompt" }), _jsxs("span", { style: { fontFamily: mono, fontSize: 10, color: C.faint }, children: [lane.prompt.tokenCount.toLocaleString(), " tok"] })] }), expanded.has('prompt') && _jsx(JsonBlock, { text: lane.prompt.text, raw: true })] })), (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
784
|
}
|
|
633
785
|
const feedItem = { padding: '7px 12px', borderTop: '1px solid #eceef1' };
|
|
634
786
|
const chip = {
|