@lloyal-labs/dev-tools 0.4.3 → 0.5.0-alpha.3

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 CHANGED
@@ -328,10 +328,13 @@ export function DevPane({ bridge, controls = [], title, runCommands = {}, framin
328
328
  } }), m.clarifying ? '?' : liveCount > 0 ? liveCount : '!'] })), _jsx("span", { children: summary })] }), toast && _jsx(Notice, { at: toastAt, text: toast }), _jsx("span", { style: { flex: 1 } }), _jsxs("span", { "aria-hidden": "true", style: { display: 'inline-flex', alignItems: 'center', gap: 14 }, children: [_jsx(MiniSpark, { heading: "context", color: C.agent, values: barStrip.map((p) => p.pct), value: barPct === null ? '—' : `${barPct}%` }), _jsx(MiniSpark, { heading: "cpu", color: HOST_CPU, values: barHost.map((h) => h.cpu), value: barLast ? `${barLast.cpu}%` : '—' }), _jsx(MiniSpark, { heading: "mem", color: "#9aa0a6", values: barHost.filter((h) => h.memUsedMb !== null).map((h) => h.memUsedMb), value: barLast && barLast.memUsedMb !== null && barLast.memTotalMb > 0
329
329
  ? `${(barLast.memUsedMb / 1024).toFixed(1)} / ${Math.round(barLast.memTotalMb / 1024)}G` : '—' }), _jsx(MiniSpark, { heading: "harness", color: "#5f6368", values: barHost.map((h) => h.rssMb), value: barLast ? `${(barLast.rssMb / 1024).toFixed(1)}G` : '—' })] }), _jsx("span", { style: { flex: 1 } }), _jsx("span", { style: { color: C.faint }, "aria-hidden": "true", children: "click to expand" })] }));
330
330
  }
331
- return shell(_jsx(Pane, { store: store, m: m, rev: rev, controls: controls, title: title, runCommands: runCommands, onClose: () => setOpen(false) }));
331
+ return shell(_jsx(Pane, { store: store, m: m, rev: rev, controls: controls, title: title, runCommands: runCommands, onClose: () => setOpen(false),
332
+ // Thumbnails resolve through the bridge when the harness exposes a
333
+ // content route; the pane never learns the transport, only the URL.
334
+ mediaUrl: bridge.representationUrl ? (d, i) => bridge.representationUrl(d, i) : undefined }));
332
335
  }
333
336
  // ═══ the docked pane ═══
334
- function Pane({ store, m, rev, controls, title, runCommands, onClose }) {
337
+ function Pane({ store, m, rev, controls, title, runCommands, onClose, mediaUrl }) {
335
338
  const [tab, setTab] = useState('timeline');
336
339
  const [selAgent, setSelAgent] = useState(null);
337
340
  const [feedW, setFeedW] = useState(feedWidthPref);
@@ -401,7 +404,7 @@ function Pane({ store, m, rev, controls, title, runCommands, onClose }) {
401
404
  : 'finish up — agents wrap up and report; the trajectory is kept', style: m.pausedAt !== null || m.windingDownAt !== null
402
405
  ? { ...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
403
406
  ? (m.pausedAt !== null ? 'paused' : m.windingDownAt !== null ? 'finishing' : 'live')
404
- : 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: {
407
+ : 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 === 'spine' && m.spine && (_jsxs(_Fragment, { children: [_jsx(FeedResizer, { width: feedW, onWidth: (w) => { feedWidthPref = w; setFeedW(w); } }), _jsx(SpineFeed, { m: m, onClose: () => setSelAgent(null), width: feedW, mediaUrl: mediaUrl, toolColor: toolColor })] })), selAgent === 'trunk' && m.trunk.length > 0 && (_jsxs(_Fragment, { children: [_jsx(FeedResizer, { width: feedW, onWidth: (w) => { feedWidthPref = w; setFeedW(w); } }), _jsx(TrunkFeed, { m: m, onClose: () => setSelAgent(null), width: feedW, mediaUrl: mediaUrl })] })), typeof selAgent === 'number' && 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: {
405
408
  height: 24, display: 'flex', alignItems: 'center', gap: 14, padding: '0 12px',
406
409
  borderTop: `1px solid ${C.border}`, background: '#f8f9fa', fontSize: 10.5, color: C.dim, flex: 'none',
407
410
  }, children: [_jsxs("span", { style: { fontFamily: mono }, children: [m.runStartAt === null
@@ -492,11 +495,121 @@ function Timeline({ m, rev, store, selAgent, onSelect, toolColor }) {
492
495
  };
493
496
  const onMouseUp = () => { drag.current = null; };
494
497
  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: "context", 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
495
- ? `${(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: {
498
+ ? `${(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', overflow: 'hidden', 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: {
496
499
  position: 'absolute', top: 6, right: 10, zIndex: 3, cursor: 'pointer',
497
500
  fontSize: 10.5, padding: '3px 10px', borderRadius: 12, border: `1px solid ${C.border}`,
498
501
  background: '#fff', color: C.agentDark, fontWeight: 600, boxShadow: '0 1px 4px rgba(32,33,36,.12)',
499
- }, 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)))] })] }));
502
+ }, 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" })] }))] }), (() => {
503
+ // Fork topology, drawn. Pure px()/ROW_H geometry — no DOM reads —
504
+ // and absolutely positioned INSIDE the scroll container, so curves
505
+ // ride with their rows through scroll, pan, and every resize.
506
+ const rows = [];
507
+ if (m.trunk.length > 0)
508
+ rows.push('session');
509
+ if (m.spine)
510
+ rows.push('spine');
511
+ for (const l of lanes)
512
+ rows.push(l.agentId);
513
+ const rowIdx = new Map(rows.map((r, i) => [r, i]));
514
+ const yMid = (r) => rowIdx.get(r) * ROW_H + 19;
515
+ const paths = [];
516
+ if (m.spine && rowIdx.has('spine')) {
517
+ const sx = px(secOf(m.spine.spineAt ?? m.spine.at));
518
+ // Warm lineage: the session's conversation feeds the spine.
519
+ if (rowIdx.has('session') && (m.spine.inherited ?? 0) > 0 && sx >= GUTTER) {
520
+ const sy = rowIdx.get('session') * ROW_H + 22;
521
+ const ty = rowIdx.get('spine') * ROW_H + 19;
522
+ const e = sx - 10;
523
+ const r = 4;
524
+ paths.push(_jsx("path", { d: `M ${sx} ${sy} L ${e + r} ${sy} Q ${e} ${sy} ${e} ${sy + r} L ${e} ${ty - r} Q ${e} ${ty} ${e + r} ${ty} L ${sx - 2} ${ty}`, stroke: "#9aa0a6", strokeWidth: "1.3", fill: "none" }, "warm"));
525
+ }
526
+ // Every lane hangs off the spine at its true spawn moment.
527
+ const spineRow = rowIdx.get('spine');
528
+ for (const l of lanes) {
529
+ // Only lanes that forked AFTER the spine existed hang off it —
530
+ // a planner forks from the session, and the pane draws no
531
+ // parentage it does not know.
532
+ if (m.spine.spineAt === null || l.spawnedAt + 250 < m.spine.spineAt)
533
+ continue;
534
+ const x = px(secOf(l.spawnedAt));
535
+ if (x < GUTTER || !on(secOf(l.spawnedAt)))
536
+ continue;
537
+ const laneRow = rowIdx.get(l.agentId);
538
+ // Nested elbows: drop at a small left offset (wider for deeper
539
+ // rows, so simultaneous forks read as nested guides), rounded
540
+ // corner, horizontal entry into the bar's left edge.
541
+ const off = 8 + (laneRow - spineRow - 1) * 5;
542
+ const sy = spineRow * ROW_H + 23;
543
+ const ty = laneRow * ROW_H + 19;
544
+ const e = x - off;
545
+ const r = 4;
546
+ paths.push(_jsx("path", { d: `M ${x} ${sy} L ${e + r} ${sy} Q ${e} ${sy} ${e} ${sy + r} L ${e} ${ty - r} Q ${e} ${ty} ${e + r} ${ty} L ${x - 2} ${ty}`, stroke: "#9aa0a6", strokeWidth: "1.3", fill: "none" }, `f${l.agentId}`));
547
+ }
548
+ }
549
+ // DAG dependency edges — declared by the orchestrator, never inferred.
550
+ for (const l of lanes) {
551
+ for (const dep of l.after ?? []) {
552
+ const d = m.lanes.get(dep);
553
+ if (!d || d.doneAt === null || !rowIdx.has(dep))
554
+ continue;
555
+ const x2 = px(secOf(l.spawnedAt));
556
+ if (x2 < GUTTER)
557
+ continue;
558
+ const x1 = Math.max(px(secOf(d.doneAt)), GUTTER);
559
+ const y1 = yMid(dep);
560
+ const y2 = yMid(l.agentId);
561
+ const e = Math.max(x2 - 8, x1 + 4);
562
+ const r = 4;
563
+ const vdir = y2 > y1 ? 1 : -1;
564
+ paths.push(_jsx("path", { d: `M ${x1} ${y1} L ${e - r} ${y1} Q ${e} ${y1} ${e} ${y1 + r * vdir} L ${e} ${y2 - r * vdir} Q ${e} ${y2} ${e + r} ${y2} L ${x2} ${y2}`, stroke: "#9aa0a6", strokeWidth: "1.2", strokeDasharray: "4 3", fill: "none" }, `d${dep}-${l.agentId}`));
565
+ }
566
+ }
567
+ if (paths.length === 0)
568
+ return null;
569
+ return (_jsx("svg", { width: "100%", height: rows.length * ROW_H, style: { position: 'absolute', left: 0, top: 0, pointerEvents: 'none', zIndex: 1 }, children: paths }));
570
+ })(), m.trunk.length > 0 && (_jsxs("div", { role: "button", tabIndex: 0, onClick: () => onSelect(selAgent === 'trunk' ? null : 'trunk'), onKeyDown: keyActivate(() => onSelect(selAgent === 'trunk' ? null : 'trunk')), style: {
571
+ position: 'relative', height: ROW_H, display: 'flex', cursor: 'pointer',
572
+ borderBottom: `1px solid ${C.hair}`,
573
+ background: selAgent === 'trunk' ? C.chromeBg : undefined,
574
+ boxShadow: selAgent === 'trunk' ? `inset 3px 0 0 ${C.text}` : undefined,
575
+ }, 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: "session" }), _jsxs("span", { style: { color: C.dim, fontSize: 10.5, fontFamily: mono }, children: [m.trunk.length, " turn", m.trunk.length === 1 ? '' : 's'] })] }), _jsx("div", { style: { flex: 1, position: 'relative', overflow: 'hidden' }, children: m.trunk.map((t, i) => {
576
+ const s = secOf(t.at);
577
+ if (!on(s))
578
+ return null;
579
+ // One diamond per side, colored by who spoke; a committed
580
+ // exchange ('turn') lands both at once and shows the pair.
581
+ const sides = t.speaker === 'turn' ? [TRUNK_QUERY, TRUNK_RESPONSE]
582
+ : t.speaker === 'user' ? [TRUNK_QUERY]
583
+ : t.speaker === 'tool' ? [TRUNK_TOOL]
584
+ : [TRUNK_RESPONSE];
585
+ return (_jsxs("span", { title: (t.query ?? t.content).slice(0, 200), children: [sides.map((c, j) => (_jsx("span", { style: {
586
+ position: 'absolute', left: px(s) - GUTTER - 5 + j * 9, top: 13, width: 10, height: 10,
587
+ borderRadius: 2, transform: 'rotate(45deg)', background: c, display: 'inline-block',
588
+ } }, j))), t.attachments.length > 0 && (_jsxs("span", { style: { position: 'absolute', left: px(s) - GUTTER + (sides.length > 1 ? 17 : 8), top: 11, fontSize: 9.5, color: C.dim }, children: ["\uD83D\uDCCE", t.attachments.length] }))] }, i));
589
+ }) })] })), m.spine && (_jsxs("div", { role: "button", tabIndex: 0, onClick: () => onSelect(selAgent === 'spine' ? null : 'spine'), onKeyDown: keyActivate(() => onSelect(selAgent === 'spine' ? null : 'spine')), style: {
590
+ position: 'relative', height: ROW_H, display: 'flex', cursor: 'pointer',
591
+ borderBottom: `1px solid ${C.hair}`,
592
+ background: selAgent === 'spine' ? C.chromeBg : undefined,
593
+ boxShadow: selAgent === 'spine' ? `inset 3px 0 0 ${C.text}` : undefined,
594
+ }, 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: "spine" }), _jsx("span", { style: { color: C.dim, fontSize: 10.5, fontFamily: mono }, children: (m.spine.spineCells || m.spine.headerTokens) > 0
595
+ ? `${(m.spine.spineCells || m.spine.headerTokens).toLocaleString()} tokens` : 'seeding…' })] }), _jsx("div", { style: { flex: 1, position: 'relative', overflow: 'hidden' }, children: (() => {
596
+ // The spine as a BAR — alive for the whole run — with a tick
597
+ // wherever it grew (the seed, then each extension).
598
+ const s0 = secOf(m.spine.spineAt ?? m.spine.at);
599
+ const x0 = Math.max(px(s0), GUTTER);
600
+ const x1 = Math.max(px(Math.min(endS, w1)), x0 + 4);
601
+ if (px(s0) > px(w1))
602
+ return null;
603
+ return (_jsxs("span", { title: (m.spine.query ?? '').slice(0, 200), children: [_jsx("span", { style: {
604
+ position: 'absolute', top: 15, height: 8, left: x0 - GUTTER, width: x1 - x0,
605
+ background: '#e3e6ea', borderRadius: 4, border: `1px solid ${C.hair}`, display: 'inline-block',
606
+ } }), m.spine.growth.map((g, gi) => {
607
+ const gs = secOf(g.at);
608
+ if (!on(gs))
609
+ return null;
610
+ return (_jsx("span", { title: `+${g.tokens.toLocaleString()} tokens`, style: { position: 'absolute', left: px(gs) - GUTTER, top: 13, width: 2, height: 12, background: C.text } }, gi));
611
+ }), m.spine.attachments.length > 0 && (_jsxs("span", { style: { position: 'absolute', left: x0 - GUTTER + 6, top: 11, fontSize: 9.5, color: C.dim }, children: ["\uD83D\uDCCE", m.spine.attachments.length] }))] }));
612
+ })() })] })), 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)))] })] }));
500
613
  }
501
614
  const label = {
502
615
  fontSize: 10, fontWeight: 600, letterSpacing: '.06em', textTransform: 'uppercase', color: '#b0b6c2',
@@ -542,7 +655,7 @@ function Lane({ m, l, px, on, secOf, nowS, live, selected, toolColor, onClick, g
542
655
  : l.outcome === 'failed' ? `${l.failReason ?? l.dropReason ?? 'failed'} · ${fmtS(e - s)}` : fmtS(e - s);
543
656
  const endColor = l.outcome === 'failed' ? C.fail : l.outcome === 'recovered' ? C.agent : '#3c4043';
544
657
  return (_jsxs("div", { onClick: onClick, role: "button", tabIndex: 0, onKeyDown: keyActivate(onClick), "aria-label": `open agent ${l.agentId}`, style: {
545
- display: 'flex', height: 38, borderTop: `1px solid ${C.hair}`, position: 'relative', cursor: 'pointer',
658
+ display: 'flex', height: ROW_H, borderTop: `1px solid ${C.hair}`, position: 'relative', cursor: 'pointer',
546
659
  background: selected ? C.chromeBg : cancelled ? 'rgba(179,38,30,.05)' : undefined,
547
660
  boxShadow: selected ? `inset 3px 0 0 ${C.text}` : undefined,
548
661
  }, children: [cancelled && (_jsx("span", { style: {
@@ -812,7 +925,125 @@ function AgentFeed({ m, lane, toolColor, onClose, onJump, nowMs, width, send, ca
812
925
  }, children: [_jsxs("div", { style: {
813
926
  height: 30, flex: 'none', display: 'flex', alignItems: 'center', gap: 8, padding: '0 12px',
814
927
  borderBottom: `1px solid ${C.border}`, background: '#f8f9fa',
815
- }, 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 }))] }))] })] }));
928
+ }, 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 && (() => {
929
+ // Raw <think> never renders — the report splits into the
930
+ // shared reasoning box + what the agent actually said.
931
+ const { reasoning, said } = splitThink(lane.report);
932
+ return (_jsxs("div", { style: { margin: '6px 0 2px 16px' }, children: [reasoning && _jsx(Reasoning, { text: reasoning, open: expanded.has('report-reasoning'), onToggle: () => toggle('report-reasoning') }), _jsx("div", { style: { lineHeight: 1.5, color: '#3c4043', whiteSpace: 'pre-wrap' }, children: said })] }));
933
+ })()] }))] })] }));
934
+ }
935
+ /** One timeline row — session, spine, and every lane share it. The
936
+ * connector overlay derives its geometry from this same constant, which is
937
+ * what makes curves survive any resize without DOM measurement. */
938
+ const ROW_H = 38;
939
+ /** Trunk marker colors — one per conversation side, same family as the
940
+ * lanes: the query is the deep blue everything forks from, the response the
941
+ * light one. Tools are grey. */
942
+ const TRUNK_QUERY = '#174ea6';
943
+ const TRUNK_RESPONSE = '#5f8fd9';
944
+ const TRUNK_TOOL = '#9aa0a6';
945
+ /** Split verbatim model text into its reasoning and its said text. The pane
946
+ * never renders raw `<think>` markup anywhere — reasoning is shown behind
947
+ * {@link Reasoning}. An unclosed block (a cut stream) still splits clean. */
948
+ function splitThink(text) {
949
+ const parts = text.split(/<think>([\s\S]*?)(?:<\/think>|$)/);
950
+ return {
951
+ reasoning: parts.filter((_, k) => k % 2 === 1).join('\n').trim(),
952
+ said: parts.filter((_, k) => k % 2 === 0).join('').trim(),
953
+ };
954
+ }
955
+ /** The expandable reasoning box — one look for every surface that carries
956
+ * model thinking (trunk turns, agent reports). Collapsed by default. */
957
+ function Reasoning({ text, open, onToggle }) {
958
+ return (_jsxs("div", { style: { margin: '0 0 4px' }, children: [_jsxs("div", { role: "button", tabIndex: 0, onClick: onToggle, onKeyDown: keyActivate(onToggle), style: { display: 'flex', alignItems: 'baseline', gap: 7, cursor: 'pointer', padding: '2px 0' }, children: [_jsx("span", { style: { color: C.faint, fontSize: 9, width: 9, flex: 'none' }, children: open ? '▾' : '▸' }), _jsx("span", { style: { color: C.dim }, children: "reasoning" })] }), open && (_jsx("div", { style: { whiteSpace: 'pre-wrap', lineHeight: 1.5, color: C.dim, fontFamily: mono, fontSize: 10.5, maxHeight: 240, overflowY: 'auto', margin: '2px 0 0 16px' }, children: text }))] }));
959
+ }
960
+ /** The spine's feed — the run's root: the user's instruction verbatim
961
+ * (only when the harness declared where it lives — see
962
+ * {@link RunFraming.instruction}), then the compiled header that seeded
963
+ * position 0. This is where a prompt survives when a run fails. */
964
+ function SpineFeed({ m, onClose, width, mediaUrl, toolColor }) {
965
+ // Substance visible by default — only the raw bytes stay behind a click.
966
+ const [headerOpen, setHeaderOpen] = useState(true);
967
+ const [compiledOpen, setCompiledOpen] = useState(false);
968
+ const k = m.spine;
969
+ if (!k)
970
+ return null;
971
+ return (_jsxs("div", { style: {
972
+ width, flex: 'none', borderLeft: '1px solid #d9dce1', display: 'flex',
973
+ flexDirection: 'column', minHeight: 0, background: C.panelBg,
974
+ }, children: [_jsxs("div", { style: {
975
+ height: 30, flex: 'none', display: 'flex', alignItems: 'center', gap: 8, padding: '0 12px',
976
+ borderBottom: `1px solid ${C.border}`, background: '#f8f9fa',
977
+ }, children: [_jsx("span", { style: { fontFamily: mono, fontWeight: 600, fontSize: 11 }, children: "spine" }), _jsx("span", { style: chip, children: "shared agent memory" }), k.inherited !== undefined && (_jsx("span", { style: chip, children: k.inherited > 0 ? `warm · ${k.inherited.toLocaleString()} tok from trunk` : 'cold start' })), k.headerTokens > 0 && (() => {
978
+ const nCtx = m.pressure.length > 0 ? m.pressure[m.pressure.length - 1].nCtx : 0;
979
+ const pct = nCtx > 0 ? Math.round((k.headerTokens / nCtx) * 1000) / 10 : null;
980
+ return _jsxs("span", { style: chip, children: [k.headerTokens.toLocaleString(), " tok", pct !== null ? ` · ${pct}% ctx` : ''] });
981
+ })(), _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: [k.query !== null && (_jsx("div", { style: feedItem, children: _jsxs("div", { style: { background: 'rgba(26,115,232,.05)', borderRadius: 4, padding: '6px 9px' }, children: [_jsxs("div", { style: { display: 'flex', gap: 7, alignItems: 'baseline', marginBottom: 3 }, children: [_jsx("b", { style: { fontSize: 11, color: TRUNK_QUERY }, children: "query" }), _jsx("span", { style: { fontSize: 10.5, color: C.faint }, children: "the user's instruction, as submitted" })] }), k.attachments.length > 0 && (_jsx("div", { style: { display: 'flex', gap: 6, flexWrap: 'wrap', margin: '2px 0 6px' }, children: k.attachments.map((a, j) => mediaUrl ? (_jsx("img", { src: mediaUrl(a.digest), alt: a.digest.slice(0, 12), title: `${a.digest.slice(0, 19)}\u2026`, style: { height: 72, maxWidth: 160, objectFit: 'cover', borderRadius: 4, border: `1px solid ${C.border}` } }, j)) : (_jsxs("span", { style: chip, title: a.digest, children: [a.digest.slice(0, 19), "\\u2026"] }, j))) })), _jsx("div", { style: { whiteSpace: 'pre-wrap', lineHeight: 1.5, color: '#3c4043', fontSize: 11.5, maxHeight: 200, overflowY: 'auto' }, children: k.query })] }) })), (k.tools && k.tools.length > 0) || k.systemText || k.headerText ? (_jsxs("div", { style: { borderBottom: `1px solid ${C.border}`, padding: '4px 12px 6px' }, children: [_jsxs("div", { role: "button", tabIndex: 0, onClick: () => setHeaderOpen((v) => !v), onKeyDown: keyActivate(() => setHeaderOpen((v) => !v)), style: { display: 'flex', alignItems: 'baseline', gap: 7, cursor: 'pointer', padding: '2px 0' }, title: "the shared header prefilled at position 0 \u2014 every agent inherits it via fork", children: [_jsx("span", { style: { color: C.faint, fontSize: 9, width: 9, flex: 'none' }, children: headerOpen ? '▾' : '▸' }), _jsx("span", { style: { color: C.dim }, children: "system + tools" })] }), headerOpen && (_jsxs("div", { style: { margin: '4px 0 2px 16px', display: 'flex', flexDirection: 'column', gap: 8 }, children: [k.tools && k.tools.length > 0 && (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: k.tools.map((t2) => (_jsxs("div", { style: { display: 'flex', gap: 8, alignItems: 'baseline' }, children: [_jsx("span", { style: { flex: 'none', transform: 'translateY(2px)' }, children: _jsx(Badge, { color: toolColor(t2.name), letter: letterOf(t2.name), size: 14 }) }), _jsx("b", { style: { fontFamily: mono, fontSize: 10.5, flex: 'none' }, children: t2.name }), _jsx("span", { style: { color: C.dim, fontSize: 11, lineHeight: 1.4, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }, title: t2.description, children: t2.description })] }, t2.name))) })), k.systemText && (_jsxs("div", { children: [_jsx("div", { style: { color: C.faint, fontSize: 10, marginBottom: 2 }, children: "system" }), _jsx("div", { style: { lineHeight: 1.5, color: '#3c4043', whiteSpace: 'pre-wrap', fontSize: 11.5, maxHeight: 220, overflowY: 'auto' }, children: k.systemText })] })), k.headerText && (_jsxs("div", { children: [_jsxs("div", { role: "button", tabIndex: 0, onClick: () => setCompiledOpen((v) => !v), onKeyDown: keyActivate(() => setCompiledOpen((v) => !v)), style: { display: 'flex', alignItems: 'baseline', gap: 7, cursor: 'pointer', padding: '2px 0' }, title: "the exact rendered template \u2014 what position 0 actually holds", children: [_jsx("span", { style: { color: C.faint, fontSize: 9, width: 9, flex: 'none' }, children: compiledOpen ? '▾' : '▸' }), _jsx("span", { style: { color: C.dim }, children: "compiled" })] }), compiledOpen && _jsx(JsonBlock, { text: k.headerText, raw: true })] }))] })), (() => {
982
+ // Full accrual: what every fork inherited for free, minus the
983
+ // one shared copy actually prefilled this run. Correct for
984
+ // chain extensions, warm trunk inheritance, and recursive
985
+ // forks — parallel degenerates to header × (n − 1).
986
+ // Only lanes that actually forked the spine count as sharers —
987
+ // the same attribution the connectors draw. A pre-spine lane
988
+ // (the planner) never inherited it and never shows here.
989
+ // Saved follows the cache-read convention: every token an agent
990
+ // forked instead of prefilling counts. The spine's own build is
991
+ // the cache write — never netted out of the headline.
992
+ const sharers = [...m.lanes.values()].filter((l2) => l2.inherited !== undefined);
993
+ const saved = sharers.reduce((n2, l2) => n2 + (l2.inherited ?? 0), 0);
994
+ if (saved <= 0 && sharers.length < 2)
995
+ return null;
996
+ return (_jsxs("div", { style: { padding: '5px 0 1px 16px', fontSize: 11, color: C.dim }, children: [sharers.length >= 2 && (_jsxs(_Fragment, { children: ["shared by ", _jsx("span", { style: { fontFamily: mono }, children: sharers.length }), " agents", saved > 0 ? ' · ' : ''] })), saved > 0 && (_jsxs(_Fragment, { children: ["tokens saved ", _jsx("span", { style: { fontFamily: mono }, children: saved.toLocaleString() })] }))] }));
997
+ })()] })) : null] })] }));
998
+ }
999
+ /** The trunk's feed — the same panel grammar as {@link AgentFeed}, for the
1000
+ * session's own conversation: each `warmDelta` turn verbatim, its KV cost,
1001
+ * and the media that entered with it (thumbnails when the bridge exposes a
1002
+ * content resolver, digest chips otherwise). */
1003
+ function TrunkFeed({ m, onClose, width, mediaUrl }) {
1004
+ const [thinkOpen, setThinkOpen] = useState(() => new Set());
1005
+ // Responses default COLLAPSED — stats stay visible, the body (reasoning +
1006
+ // text) expands on click. A committed exchange's response half can be a
1007
+ // whole brief; the feed must not pay its render until asked.
1008
+ const [respOpen, setRespOpen] = useState(() => new Set());
1009
+ const toggleResp = (i) => {
1010
+ setRespOpen((prev) => {
1011
+ const next = new Set(prev);
1012
+ if (next.has(i))
1013
+ next.delete(i);
1014
+ else
1015
+ next.add(i);
1016
+ return next;
1017
+ });
1018
+ };
1019
+ const toggleThink = (i) => {
1020
+ setThinkOpen((prev) => {
1021
+ const next = new Set(prev);
1022
+ if (next.has(i))
1023
+ next.delete(i);
1024
+ else
1025
+ next.add(i);
1026
+ return next;
1027
+ });
1028
+ };
1029
+ return (_jsxs("div", { style: {
1030
+ width, flex: 'none', borderLeft: '1px solid #d9dce1', display: 'flex',
1031
+ flexDirection: 'column', minHeight: 0, background: C.panelBg,
1032
+ }, children: [_jsxs("div", { style: {
1033
+ height: 30, flex: 'none', display: 'flex', alignItems: 'center', gap: 8, padding: '0 12px',
1034
+ borderBottom: `1px solid ${C.border}`, background: '#f8f9fa',
1035
+ }, children: [_jsx("span", { style: { fontFamily: mono, fontWeight: 600, fontSize: 11 }, children: "session" }), _jsx("span", { style: chip, children: "the conversation" }), _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" })] }), _jsx("div", { style: { overflowY: 'auto', flex: 1, fontSize: 11, paddingBottom: 8 }, children: m.trunk.map((t, i) => {
1036
+ // Query and response render as their OWN blocks — a committed
1037
+ // exchange (`speaker: 'turn'`) carries both halves structurally,
1038
+ // and single-sided deltas carry one. Raw <think> never renders:
1039
+ // the response side splits into the shared reasoning box.
1040
+ const isTurn = t.speaker === 'turn';
1041
+ const q = isTurn ? (t.query ?? '') : t.speaker === 'user' ? t.content : '';
1042
+ const rRaw = isTurn ? (t.response ?? '') : (t.speaker === 'assistant' || t.speaker === 'tool' || t.speaker === undefined) ? t.content : '';
1043
+ const { reasoning, said } = splitThink(rRaw);
1044
+ const meta = (_jsxs("span", { style: { fontFamily: mono, fontSize: 10, color: C.faint }, children: [t.cells.toLocaleString(), " tokens"] }));
1045
+ return (_jsxs("div", { style: feedItem, children: [(q || t.attachments.length > 0) && (_jsxs("div", { style: { background: 'rgba(26,115,232,.05)', borderRadius: 4, padding: '6px 9px', margin: '0 0 6px' }, children: [_jsxs("div", { style: { display: 'flex', gap: 7, alignItems: 'baseline', marginBottom: 3 }, children: [_jsx("b", { style: { fontSize: 11, color: C.agentDark }, children: "query" }), meta] }), t.attachments.length > 0 && (_jsx("div", { style: { display: 'flex', gap: 6, flexWrap: 'wrap', margin: '2px 0 6px' }, children: t.attachments.map((a, j) => mediaUrl ? (_jsx("img", { src: mediaUrl(a.digest), alt: a.mediaType ?? a.digest.slice(0, 12), title: `${a.mediaType ?? 'media'} · ${a.digest.slice(0, 19)}…`, style: { height: 72, maxWidth: 160, objectFit: 'cover', borderRadius: 4, border: `1px solid ${C.border}` } }, j)) : (_jsxs("span", { style: chip, title: a.digest, children: [a.mediaType ?? 'media', " \u00B7 ", a.digest.slice(0, 19), "\u2026"] }, j))) })), q && (_jsx("div", { style: { whiteSpace: 'pre-wrap', lineHeight: 1.5, color: '#3c4043', fontFamily: mono, fontSize: 10.5, maxHeight: 200, overflowY: 'auto' }, children: q }))] })), rRaw && (_jsxs("div", { children: [_jsxs("div", { role: "button", tabIndex: 0, onClick: () => toggleResp(i), onKeyDown: keyActivate(() => toggleResp(i)), style: { display: 'flex', gap: 7, alignItems: 'baseline', marginBottom: 3, cursor: 'pointer' }, children: [_jsx("span", { style: { color: C.faint, fontSize: 9, width: 9, flex: 'none' }, children: respOpen.has(i) ? '\u25be' : '\u25b8' }), _jsx("b", { style: { fontSize: 11, color: TRUNK_RESPONSE }, children: t.speaker === 'tool' ? 'tool' : 'response' }), !q && meta, (t.agentTokens ?? 0) > 0 && (_jsxs("span", { style: { fontFamily: mono, fontSize: 10, color: C.faint }, children: [t.agentTokens.toLocaleString(), " tokens"] })), t.wallMs !== undefined && (_jsx("span", { style: { fontFamily: mono, fontSize: 10, color: C.faint }, children: fmtS(t.wallMs / 1000) }))] }), respOpen.has(i) && (_jsxs("div", { style: { margin: '0 0 0 16px' }, children: [reasoning && _jsx(Reasoning, { text: reasoning, open: thinkOpen.has(i), onToggle: () => toggleThink(i) }), said && (_jsx("div", { style: { whiteSpace: 'pre-wrap', lineHeight: 1.5, color: '#3c4043', fontFamily: mono, fontSize: 10.5, maxHeight: 300, overflowY: 'auto' }, children: said }))] }))] }))] }, i));
1046
+ }) })] }));
816
1047
  }
817
1048
  const feedItem = { padding: '7px 12px', borderTop: '1px solid #eceef1' };
818
1049
  const chip = {
@@ -923,6 +1154,14 @@ const SETTING_META = {
923
1154
  desc: 'flat runs one research wave over the plan; deep lets agents recurse into sub-plans.',
924
1155
  how: 'Change it here; it applies to your next run.',
925
1156
  },
1157
+ 'model.imageMaxTokens': {
1158
+ desc: 'Ceiling on what ONE image costs in KV. Measured on Qwen3.5 with a 176 KB photo: 564 cells uncapped, 251 at 256. Lower it to fit more images into a context; auto lets the model metadata decide.',
1159
+ how: 'Change it here and the runtime reloads on the new value. It does NOT shrink the projector\u2019s warmup allocation, so it will not rescue a boot that runs out of GPU memory before any image arrives.',
1160
+ },
1161
+ 'model.imageMinTokens': {
1162
+ desc: 'Floor on per-image detail. Grounding tasks need it high \u2014 llama.cpp warns Qwen-VL wants at least 1024 to read positions reliably.',
1163
+ how: 'Change it here and the runtime reloads on the new value. Raise it if the model reads an image but places things wrongly in it.',
1164
+ },
926
1165
  'sources.outputDir': {
927
1166
  desc: 'Where per-query run-dirs and the session trace are written. Empty means where the harness started.',
928
1167
  how: 'Edit harness.yml → sources.outputDir; the next run picks it up.',
@@ -986,6 +1225,28 @@ function Settings({ m, controls, send }) {
986
1225
  ? _jsx(HarnessSettings, { m: m, controls: controls, send: send, selKey: selKey, onSelect: setSelKey })
987
1226
  : _jsx(AbilityPage, { m: m, name: cat, send: send })] }));
988
1227
  }
1228
+ /** A DevControl drawn as a stepped slider: the steps ARE `ctl.values`, in the
1229
+ * order the template declared them, so there is no numeric range to keep in
1230
+ * sync with the option list and no value can be selected that the command
1231
+ * would not accept. Commits on release, not on drag \u2014 each change reloads
1232
+ * the runtime, and every intermediate step would be a reload nobody asked
1233
+ * for. */
1234
+ function SteppedSlider({ ctl, value, onSelect, send }) {
1235
+ const at = Math.max(0, ctl.values.indexOf(value ?? ''));
1236
+ const [dragging, setDragging] = useState(null);
1237
+ const shown = dragging ?? at;
1238
+ const commit = (i) => {
1239
+ setDragging(null);
1240
+ if (ctl.values[i] !== value) {
1241
+ onSelect();
1242
+ send({ type: ctl.command, [ctl.field]: ctl.values[i] });
1243
+ }
1244
+ };
1245
+ return (_jsxs("span", { onClick: (e) => e.stopPropagation(), style: { display: 'flex', alignItems: 'center', gap: 10, width: 276, flex: 'none' }, children: [_jsx("input", { type: "range", min: 0, max: ctl.values.length - 1, step: 1, value: shown, "aria-label": ctl.key, "aria-valuetext": String(ctl.values[shown]), onChange: (e) => setDragging(Number(e.target.value)), onPointerUp: (e) => commit(Number(e.target.value)), onKeyUp: (e) => commit(Number(e.target.value)), onBlur: () => setDragging(null), style: { flex: 1, accentColor: C.text, cursor: 'pointer', minWidth: 0 } }), _jsx("span", { style: {
1246
+ fontFamily: mono, fontSize: 11, color: C.text, width: 46, flex: 'none',
1247
+ textAlign: 'right', fontVariantNumeric: 'tabular-nums',
1248
+ }, children: ctl.values[shown] })] }));
1249
+ }
989
1250
  function HarnessSettings({ m, controls, send, selKey, onSelect }) {
990
1251
  const config = m.config;
991
1252
  const byTier = (tier) => Object.entries(KEY_TIERS).filter(([, t]) => t === tier).map(([k]) => k);
@@ -1000,7 +1261,7 @@ function HarnessSettings({ m, controls, send, selKey, onSelect }) {
1000
1261
  display: 'flex', alignItems: 'center', gap: 8, padding: '5px 14px 5px 11px', minHeight: 36,
1001
1262
  borderLeft: selected ? `3px solid ${C.text}` : '3px solid transparent', cursor: 'pointer',
1002
1263
  background: selected ? C.chromeBg : undefined,
1003
- }, 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: {
1264
+ }, 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?.render === 'slider' ? (_jsx(SteppedSlider, { ctl: ctl, value: value === undefined || value === null ? undefined : String(value), onSelect: () => onSelect(key), send: send })) : 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: {
1004
1265
  flex: 1, fontSize: 11, padding: '5px 0', textAlign: 'center', cursor: 'pointer',
1005
1266
  background: v === value ? C.text : '#fff', color: v === value ? '#fff' : C.dim,
1006
1267
  fontWeight: v === value ? 500 : 400, borderLeft: '1px solid #e8eaed',