@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/index.d.ts +104 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +170 -1
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +269 -8
- package/dist/react.js.map +1 -1
- package/dist/store.d.ts +5 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js.map +1 -1
- package/package.json +3 -3
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
|
|
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" })] }))] }),
|
|
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:
|
|
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 && (
|
|
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',
|