@zenera/neo 1.1.12 → 1.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -4
- package/dist/embeddings/limiter.js +14 -3
- package/dist/inspect/client.d.ts +1 -1
- package/dist/inspect/client.js +399 -1
- package/dist/inspect/css.d.ts +1 -1
- package/dist/inspect/css.js +49 -0
- package/dist/inspect/index.d.ts +3 -3
- package/dist/inspect/index.js +1 -1
- package/dist/inspect/page.js +16 -0
- package/dist/inspect/report.d.ts +25 -0
- package/dist/inspect/report.js +45 -0
- package/dist/memory/report.d.ts +12 -0
- package/dist/memory/report.js +32 -14
- package/dist/memory/tools.js +30 -3
- package/dist/memory/types.d.ts +3 -2
- package/dist/trajectory.d.ts +1 -1
- package/package.json +1 -1
package/dist/inspect/client.js
CHANGED
|
@@ -803,11 +803,15 @@ const PANES = {};
|
|
|
803
803
|
function show(view) {
|
|
804
804
|
document.getElementById('graph').hidden = view !== 'graph';
|
|
805
805
|
document.getElementById('agents').hidden = view !== 'agents';
|
|
806
|
+
document.getElementById('memory').hidden = view !== 'memory';
|
|
806
807
|
detailEl.hidden = view !== 'detail';
|
|
807
808
|
statsEl.hidden = view !== 'stats';
|
|
808
809
|
Array.prototype.forEach.call(document.getElementById('viewtabs').children, function (b) {
|
|
809
810
|
b.classList.toggle('on', b.dataset.view === view);
|
|
810
811
|
});
|
|
812
|
+
// The memory graph is the one view that costs a render nobody asked for, so
|
|
813
|
+
// it waits until somebody asks.
|
|
814
|
+
if (view === 'memory' && !memDrawn) { memDrawn = true; drawMemory(); }
|
|
811
815
|
if (PANES[view]) PANES[view].refit();
|
|
812
816
|
}
|
|
813
817
|
Array.prototype.forEach.call(document.getElementById('viewtabs').children, function (b) {
|
|
@@ -924,6 +928,7 @@ function panZoom(paneId) {
|
|
|
924
928
|
|
|
925
929
|
PANES.graph = panZoom('graph');
|
|
926
930
|
PANES.agents = panZoom('agents');
|
|
931
|
+
PANES.memory = panZoom('memory');
|
|
927
932
|
|
|
928
933
|
// --- diagram --------------------------------------------------------------
|
|
929
934
|
|
|
@@ -1165,7 +1170,7 @@ function observedArchitecture() {
|
|
|
1165
1170
|
// A run shows that an agent reached the memory, never how wide its mask
|
|
1166
1171
|
// was: nothing it did not read leaves a trace to read the mask off.
|
|
1167
1172
|
if (!a.memory) a.memory = { access: 'read', sees: [], writes: [] };
|
|
1168
|
-
if (n.type === 'memory_op') a.memory.access = 'read-write';
|
|
1173
|
+
if (n.type === 'memory_op' && n.op !== 'load') a.memory.access = 'read-write';
|
|
1169
1174
|
} else if (n.type === 'fork') {
|
|
1170
1175
|
if (!a.fork) a.fork = { agents: [] };
|
|
1171
1176
|
n.branches.forEach(function (b) {
|
|
@@ -1335,6 +1340,399 @@ async function drawArch() {
|
|
|
1335
1340
|
});
|
|
1336
1341
|
}
|
|
1337
1342
|
|
|
1343
|
+
// --- memory ---------------------------------------------------------------
|
|
1344
|
+
//
|
|
1345
|
+
// Two questions in one picture: what the run took out of the memory, and what
|
|
1346
|
+
// it put back. The trajectory records ids, kinds and edges but no bodies, so
|
|
1347
|
+
// the text comes from the graph as the report was built — which is later than
|
|
1348
|
+
// the run, and the panel says so where it matters: a node can have moved on a
|
|
1349
|
+
// revision since, or be gone from the memory entirely.
|
|
1350
|
+
|
|
1351
|
+
const MEM = new Map();
|
|
1352
|
+
const MEM_EDGES = [];
|
|
1353
|
+
const MEM_EDGE_SEEN = new Set();
|
|
1354
|
+
|
|
1355
|
+
function memOf(m) {
|
|
1356
|
+
let x = MEM.get(m.id);
|
|
1357
|
+
if (!x) {
|
|
1358
|
+
x = { id: m.id, kind: m.kind, read: false, added: false, gone: false, full: false,
|
|
1359
|
+
seed: false, score: null, revision: null, where: [] };
|
|
1360
|
+
MEM.set(m.id, x);
|
|
1361
|
+
}
|
|
1362
|
+
return x;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
// The same edge comes back with every recall that crossed it; drawing it once
|
|
1366
|
+
// is the difference between a graph and a bundle of parallel arrows.
|
|
1367
|
+
function memEdge(e) {
|
|
1368
|
+
const k = e.source + '|' + e.relation + '|' + e.target;
|
|
1369
|
+
if (MEM_EDGE_SEEN.has(k)) return;
|
|
1370
|
+
MEM_EDGE_SEEN.add(k);
|
|
1371
|
+
MEM_EDGES.push(e);
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
ENTRIES.forEach(function (entry) {
|
|
1375
|
+
const n = entry.node;
|
|
1376
|
+
if (n.type !== 'memory_recall' && n.type !== 'memory_op') return;
|
|
1377
|
+
n.nodes.forEach(function (m) {
|
|
1378
|
+
const x = memOf(m);
|
|
1379
|
+
if (x.where.indexOf(entry.key) < 0) x.where.push(entry.key);
|
|
1380
|
+
if (n.type === 'memory_recall') {
|
|
1381
|
+
x.read = true;
|
|
1382
|
+
if (n.seeds.indexOf(m.id) >= 0) {
|
|
1383
|
+
x.seed = true;
|
|
1384
|
+
if (x.score === null || m.score > x.score) x.score = m.score;
|
|
1385
|
+
}
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
x.revision = m.revision;
|
|
1389
|
+
if (n.op === 'commit') x.added = true;
|
|
1390
|
+
else if (n.op === 'forget') x.gone = true;
|
|
1391
|
+
else x.full = true;
|
|
1392
|
+
});
|
|
1393
|
+
(n.edges || []).forEach(memEdge);
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
const MEM_SNAP = new Map();
|
|
1397
|
+
((DATA.memory && DATA.memory.nodes) || []).forEach(function (n) { MEM_SNAP.set(n.id, n); });
|
|
1398
|
+
// The graph knows joins the run never travelled — the edge between a task it
|
|
1399
|
+
// read and the artifact it then wrote is exactly the one worth seeing.
|
|
1400
|
+
((DATA.memory && DATA.memory.edges) || []).forEach(memEdge);
|
|
1401
|
+
|
|
1402
|
+
const MEM_ADJ = new Map();
|
|
1403
|
+
function memLink(id, other, rel, out) {
|
|
1404
|
+
if (!MEM_ADJ.has(id)) MEM_ADJ.set(id, []);
|
|
1405
|
+
MEM_ADJ.get(id).push({ other: other, rel: rel, out: out });
|
|
1406
|
+
}
|
|
1407
|
+
MEM_EDGES.forEach(function (e) {
|
|
1408
|
+
memLink(e.source, e.target, e.relation, true);
|
|
1409
|
+
memLink(e.target, e.source, e.relation, false);
|
|
1410
|
+
});
|
|
1411
|
+
|
|
1412
|
+
// --- the memory diagram ---------------------------------------------------
|
|
1413
|
+
|
|
1414
|
+
const MEM_MAX = 300;
|
|
1415
|
+
const MEM_SHAPES = { file: ['[/', '/]'], plan: ['{{', '}}'], operation: ['([', '])'] };
|
|
1416
|
+
const MEM_PAINTED = new Set(
|
|
1417
|
+
['task', 'plan', 'fact', 'snippet', 'file', 'operation', 'preference']);
|
|
1418
|
+
const MEM_KEY = new Map();
|
|
1419
|
+
const MEM_ID = new Map();
|
|
1420
|
+
|
|
1421
|
+
function shortId(id) {
|
|
1422
|
+
return id.length > 12 ? id.slice(0, 4) + '\\u2026' + id.slice(-4) : id;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
function clipText(text, n) {
|
|
1426
|
+
const flat = String(text === undefined || text === null ? '' : text).replace(/\\s+/g, ' ').trim();
|
|
1427
|
+
return flat.length > n ? flat.slice(0, n - 1) + '...' : flat;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
/**
|
|
1431
|
+
* Text as at most 'max' lines of roughly 'width' characters. Mermaid will not
|
|
1432
|
+
* break a label, so a node is otherwise as wide as its sentence and the graph
|
|
1433
|
+
* is unreadable at any zoom that fits.
|
|
1434
|
+
*/
|
|
1435
|
+
function memWrap(text, width, max) {
|
|
1436
|
+
const words = String(text === undefined || text === null ? '' : text)
|
|
1437
|
+
.replace(/\\s+/g, ' ').trim().split(' ');
|
|
1438
|
+
const out = [];
|
|
1439
|
+
let cur = '';
|
|
1440
|
+
let i = 0;
|
|
1441
|
+
for (; i < words.length; i++) {
|
|
1442
|
+
const w = words[i].length > width ? words[i].slice(0, width - 1) + '...' : words[i];
|
|
1443
|
+
if (!w) continue;
|
|
1444
|
+
if (!cur) cur = w;
|
|
1445
|
+
else if (cur.length + 1 + w.length <= width) cur += ' ' + w;
|
|
1446
|
+
else if (out.length + 1 < max) { out.push(cur); cur = w; }
|
|
1447
|
+
else break;
|
|
1448
|
+
}
|
|
1449
|
+
if (cur) out.push(cur);
|
|
1450
|
+
const last = out.length - 1;
|
|
1451
|
+
if (i < words.length && last >= 0 && out[last].slice(-3) !== '...') out[last] += '...';
|
|
1452
|
+
return out;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// Built from a closed-vocabulary kind, an id and a clip of text put through the
|
|
1456
|
+
// same sanitizer the run graph uses: nothing a model wrote reaches Mermaid.
|
|
1457
|
+
// Shaped like the memory page's labels — what it is on the first line, what it
|
|
1458
|
+
// says underneath — because run together there is no telling the three apart.
|
|
1459
|
+
function memLabel(x) {
|
|
1460
|
+
const snap = MEM_SNAP.get(x.id);
|
|
1461
|
+
const head = safe(x.kind || 'node') + ' \\u00b7 ' + shortId(safe(x.id));
|
|
1462
|
+
// Drawing the stored text under a node the model never opened would read as
|
|
1463
|
+
// context it had.
|
|
1464
|
+
if (memSaw(x) === 'id') return [head, '(id only)'].join('<br/>');
|
|
1465
|
+
return [head].concat(snap ? memWrap(snap.text, 28, 3).map(safe) : []).join('<br/>');
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
// How much of the node reached the model: a recall block carries one clipped
|
|
1469
|
+
// line each, memory_load returns whole bodies, and what the run wrote it wrote
|
|
1470
|
+
// in its own words. A node it only forgot was named by id and nothing more.
|
|
1471
|
+
function memSaw(x) {
|
|
1472
|
+
if (x.full || x.added) return 'full';
|
|
1473
|
+
if (x.read) return 'summary';
|
|
1474
|
+
return 'id';
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
const MEM_SAW = {
|
|
1478
|
+
full: 'the whole text',
|
|
1479
|
+
summary: 'a clipped line, in the recall block',
|
|
1480
|
+
id: 'the id only \\u2014 never the content'
|
|
1481
|
+
};
|
|
1482
|
+
|
|
1483
|
+
// Fill says what the node IS, outline says what the run DID with it. Painting
|
|
1484
|
+
// the fill by role instead made the whole graph one colour: nearly every node
|
|
1485
|
+
// in a run was read.
|
|
1486
|
+
function memRole(x, withAdded) {
|
|
1487
|
+
if (x.gone) return 'gone';
|
|
1488
|
+
if (x.added && withAdded) return 'added';
|
|
1489
|
+
return x.seed ? 'seed' : '';
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
/** A node the run only wrote is not part of "what it read", so it waits. */
|
|
1493
|
+
function memShown(withAdded) {
|
|
1494
|
+
const out = [];
|
|
1495
|
+
MEM.forEach(function (x) {
|
|
1496
|
+
if (!withAdded && x.added && !x.read && !x.gone) return;
|
|
1497
|
+
out.push(x);
|
|
1498
|
+
});
|
|
1499
|
+
return out;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
function memDiagram(shown, withAdded) {
|
|
1503
|
+
const lines = ['flowchart LR'];
|
|
1504
|
+
lines.push(' classDef task fill:#1e3357,stroke:#3d5f9e,color:#cfe0ff;');
|
|
1505
|
+
lines.push(' classDef plan fill:#3a2450,stroke:#6b4b90,color:#e6d3ff;');
|
|
1506
|
+
lines.push(' classDef fact fill:#14392c,stroke:#2f7a5c,color:#c6f5e0;');
|
|
1507
|
+
lines.push(' classDef snippet fill:#1e3d33,stroke:#3d7a66,color:#c8f0e2;');
|
|
1508
|
+
lines.push(' classDef file fill:#40331a,stroke:#7a6535,color:#f0dfb4;');
|
|
1509
|
+
lines.push(' classDef operation fill:#3d2a1b,stroke:#7a5535,color:#f2d9c2;');
|
|
1510
|
+
lines.push(' classDef preference fill:#3d1f2c,stroke:#7a3b56,color:#f5b8cd;');
|
|
1511
|
+
// Defined last so the outline wins over the kind's own stroke.
|
|
1512
|
+
lines.push(' classDef seed stroke:#6ea8fe,stroke-width:3px;');
|
|
1513
|
+
lines.push(' classDef added stroke:#4ec9a0,stroke-width:3px;');
|
|
1514
|
+
lines.push(' classDef gone stroke:#f0776c,stroke-width:2px,stroke-dasharray:4 3,opacity:0.6;');
|
|
1515
|
+
MEM_KEY.clear();
|
|
1516
|
+
MEM_ID.clear();
|
|
1517
|
+
shown.forEach(function (x, i) {
|
|
1518
|
+
const key = 'm' + i;
|
|
1519
|
+
MEM_KEY.set(x.id, key);
|
|
1520
|
+
MEM_ID.set(key, x.id);
|
|
1521
|
+
const shape = MEM_SHAPES[x.kind] || ['(', ')'];
|
|
1522
|
+
lines.push(' ' + key + shape[0] + '"' + memLabel(x) + '"' + shape[1]);
|
|
1523
|
+
// Kinds are configurable, so naming a class that was never defined would
|
|
1524
|
+
// fail the whole render.
|
|
1525
|
+
if (MEM_PAINTED.has(x.kind)) lines.push(' class ' + key + ' ' + x.kind + ';');
|
|
1526
|
+
const role = memRole(x, withAdded);
|
|
1527
|
+
if (role) lines.push(' class ' + key + ' ' + role + ';');
|
|
1528
|
+
});
|
|
1529
|
+
MEM_EDGES.forEach(function (e) {
|
|
1530
|
+
const a = MEM_KEY.get(e.source), b = MEM_KEY.get(e.target);
|
|
1531
|
+
if (a && b) lines.push(' ' + a + ' -->|' + safe(e.relation.toLowerCase()) + '| ' + b);
|
|
1532
|
+
});
|
|
1533
|
+
return lines.join('\\n');
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
// The two counts that have an outline colour in the graph wear it here too.
|
|
1537
|
+
function memLegend(drawn) {
|
|
1538
|
+
let read = 0, added = 0, gone = 0;
|
|
1539
|
+
MEM.forEach(function (x) {
|
|
1540
|
+
if (x.read) read++;
|
|
1541
|
+
if (x.added) added++;
|
|
1542
|
+
if (x.gone) gone++;
|
|
1543
|
+
});
|
|
1544
|
+
const dot = ' \\u00b7 ';
|
|
1545
|
+
const out = [el('span', null, read + ' read'), document.createTextNode(dot),
|
|
1546
|
+
el('span', 'ok', added + ' written'), document.createTextNode(dot),
|
|
1547
|
+
el('span', 'bad', gone + ' forgotten')];
|
|
1548
|
+
if (drawn < MEM.size) out.push(document.createTextNode(dot + drawn + ' drawn'));
|
|
1549
|
+
if (!DATA.memory) out.push(document.createTextNode(dot + 'no memory attached: ids only'));
|
|
1550
|
+
return out;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
const memAddedEl = document.getElementById('madded');
|
|
1554
|
+
const mdetailEl = document.getElementById('mdetail');
|
|
1555
|
+
const memTab = document.querySelector('#viewtabs button[data-view="memory"]');
|
|
1556
|
+
let memDrawn = false;
|
|
1557
|
+
let memSeq = 0;
|
|
1558
|
+
let memSel = null;
|
|
1559
|
+
|
|
1560
|
+
if (MEM.size) memTab.hidden = false;
|
|
1561
|
+
memAddedEl.addEventListener('change', function () { drawMemory(); });
|
|
1562
|
+
|
|
1563
|
+
function memKeyOf(g) {
|
|
1564
|
+
const m = /-(m\\d+)-\\d+$/.exec(g.id || '');
|
|
1565
|
+
return m ? m[1] : null;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
function memMark() {
|
|
1569
|
+
const key = memSel === null ? null : MEM_KEY.get(memSel);
|
|
1570
|
+
PANES.memory.canvas.querySelectorAll('.node').forEach(function (g) {
|
|
1571
|
+
g.classList.toggle('picked', Boolean(key) && memKeyOf(g) === key);
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
async function drawMemory() {
|
|
1576
|
+
const withAdded = memAddedEl.checked;
|
|
1577
|
+
const shown = memShown(withAdded).slice(0, MEM_MAX);
|
|
1578
|
+
const count = document.getElementById('mcount');
|
|
1579
|
+
count.replaceChildren.apply(count, memLegend(shown.length));
|
|
1580
|
+
if (!shown.length) {
|
|
1581
|
+
PANES.memory.canvas.replaceChildren(el('div', 'hint pad',
|
|
1582
|
+
'This run neither read nor wrote memory.'));
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
// A fresh render id each time: the checkbox redraws this pane, and Mermaid
|
|
1586
|
+
// keeps the element it was given.
|
|
1587
|
+
await draw(PANES.memory, 'mem-graph-' + (++memSeq), memDiagram(shown, withAdded), function (g) {
|
|
1588
|
+
const id = MEM_ID.get(memKeyOf(g));
|
|
1589
|
+
if (!id) return;
|
|
1590
|
+
g.addEventListener('click', function () { if (!PANES.memory.moved()) memSelect(id); });
|
|
1591
|
+
});
|
|
1592
|
+
memMark();
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
// --- the memory panel -----------------------------------------------------
|
|
1596
|
+
|
|
1597
|
+
function memProp(dl, key, value) {
|
|
1598
|
+
if (value === undefined || value === null || value === '') return;
|
|
1599
|
+
dl.appendChild(el('dt', null, key));
|
|
1600
|
+
dl.appendChild(el('dd', null, value));
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
const MEM_EXCUSE = {
|
|
1604
|
+
'too-big': 'too big to inline \\u2014 read it from the memory directory',
|
|
1605
|
+
binary: 'binary, so there is nothing to show',
|
|
1606
|
+
missing: 'the graph records this file but it is not on disk',
|
|
1607
|
+
unreadable: 'on disk but could not be read'
|
|
1608
|
+
};
|
|
1609
|
+
|
|
1610
|
+
function memSelect(id) {
|
|
1611
|
+
memSel = id;
|
|
1612
|
+
mdetailEl.replaceChildren(memDetail(id));
|
|
1613
|
+
mdetailEl.scrollTop = 0;
|
|
1614
|
+
memMark();
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
function memDetail(id) {
|
|
1618
|
+
const x = MEM.get(id) || { id: id, kind: '', where: [], score: null, revision: null };
|
|
1619
|
+
const snap = MEM_SNAP.get(id);
|
|
1620
|
+
const out = document.createDocumentFragment();
|
|
1621
|
+
|
|
1622
|
+
const h = el('div', 'dt');
|
|
1623
|
+
const kind = x.kind || (snap && snap.kind) || '';
|
|
1624
|
+
h.appendChild(el('span', 'kind ' + kind, kind || 'node'));
|
|
1625
|
+
if (x.read) h.appendChild(el('span', 'badge', 'read'));
|
|
1626
|
+
if (x.full) h.appendChild(el('span', 'badge', 'loaded'));
|
|
1627
|
+
if (x.added) h.appendChild(el('span', 'badge ok', 'written'));
|
|
1628
|
+
if (x.gone) h.appendChild(el('span', 'badge bad', 'forgotten'));
|
|
1629
|
+
if (snap && snap.stale) h.appendChild(el('span', 'badge', 'superseded'));
|
|
1630
|
+
out.appendChild(h);
|
|
1631
|
+
|
|
1632
|
+
// The id is what you carry to 'zen memory show', so it is whole and takeable.
|
|
1633
|
+
const idrow = el('div', 'idrow');
|
|
1634
|
+
idrow.appendChild(el('code', null, id));
|
|
1635
|
+
const copy = el('button', null, 'copy');
|
|
1636
|
+
copy.addEventListener('click', function () {
|
|
1637
|
+
navigator.clipboard.writeText(id).then(function () {
|
|
1638
|
+
copy.textContent = 'copied';
|
|
1639
|
+
setTimeout(function () { copy.textContent = 'copy'; }, 1200);
|
|
1640
|
+
}, function () { copy.textContent = 'blocked'; });
|
|
1641
|
+
});
|
|
1642
|
+
idrow.appendChild(copy);
|
|
1643
|
+
out.appendChild(idrow);
|
|
1644
|
+
|
|
1645
|
+
const props = el('dl', 'props');
|
|
1646
|
+
memProp(props, 'model saw', MEM_SAW[memSaw(x)]);
|
|
1647
|
+
if (x.score !== null) memProp(props, 'score', x.score.toFixed(3) + (x.seed ? ' match' : ''));
|
|
1648
|
+
else if (x.read) memProp(props, 'reached', 'stitched in, not matched');
|
|
1649
|
+
if (snap) {
|
|
1650
|
+
memProp(props, 'audience', snap.audience.join(', ') || '\\u2014');
|
|
1651
|
+
// The run's revision and the current one are the same number until
|
|
1652
|
+
// something wrote this node again after the run.
|
|
1653
|
+
memProp(props, 'revision', x.revision !== null && x.revision !== snap.revision
|
|
1654
|
+
? x.revision + ' then, ' + snap.revision + ' now' : String(snap.revision));
|
|
1655
|
+
memProp(props, 'recalled', snap.useCount + 'x \\u00b7 last ' + snap.lastUsedAt);
|
|
1656
|
+
memProp(props, 'created', snap.createdAt);
|
|
1657
|
+
if (snap.updatedAt !== snap.createdAt) memProp(props, 'updated', snap.updatedAt);
|
|
1658
|
+
} else if (x.revision !== null) {
|
|
1659
|
+
memProp(props, 'revision', String(x.revision));
|
|
1660
|
+
}
|
|
1661
|
+
out.appendChild(props);
|
|
1662
|
+
|
|
1663
|
+
if (snap) {
|
|
1664
|
+
out.appendChild(textBlock('Text', snap.text));
|
|
1665
|
+
if (snap.metadata) {
|
|
1666
|
+
out.appendChild(textBlock('Metadata', JSON.stringify(snap.metadata, null, 2)));
|
|
1667
|
+
}
|
|
1668
|
+
if (snap.file) out.appendChild(memFile(snap));
|
|
1669
|
+
} else {
|
|
1670
|
+
const s = block('Text', DATA.memory ? 'gone' : 'not attached');
|
|
1671
|
+
s.appendChild(el('div', 'empty', DATA.memory
|
|
1672
|
+
? 'no longer in the memory graph \\u2014 the run recorded its id, kind and edges, not its text'
|
|
1673
|
+
: 'this report carries no memory, so only what the run itself recorded is here'));
|
|
1674
|
+
out.appendChild(s);
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
const links = MEM_ADJ.get(id) || [];
|
|
1678
|
+
const ls = block('Links', String(links.length));
|
|
1679
|
+
if (!links.length) {
|
|
1680
|
+
ls.appendChild(el('div', 'empty', 'nothing points here and it points nowhere'));
|
|
1681
|
+
} else {
|
|
1682
|
+
const ul = el('ul', 'links');
|
|
1683
|
+
// Outgoing first: PRODUCED read forwards is the story of the node.
|
|
1684
|
+
links.slice().sort(function (a, b) { return (a.out ? 0 : 1) - (b.out ? 0 : 1); })
|
|
1685
|
+
.forEach(function (l) {
|
|
1686
|
+
const other = MEM_SNAP.get(l.other);
|
|
1687
|
+
const li = el('li');
|
|
1688
|
+
li.appendChild(el('span', 'rel', (l.out ? '\\u2192 ' : '\\u2190 ') + l.rel));
|
|
1689
|
+
li.appendChild(el('span', 'kind ' + (other ? other.kind : ''), other ? other.kind : '?'));
|
|
1690
|
+
const b = el('button', 'link',
|
|
1691
|
+
(other && clipText(other.text, 60)) || shortId(l.other));
|
|
1692
|
+
b.title = l.other;
|
|
1693
|
+
b.addEventListener('click', function () { memSelect(l.other); });
|
|
1694
|
+
li.appendChild(b);
|
|
1695
|
+
ul.appendChild(li);
|
|
1696
|
+
});
|
|
1697
|
+
ls.appendChild(ul);
|
|
1698
|
+
}
|
|
1699
|
+
out.appendChild(ls);
|
|
1700
|
+
|
|
1701
|
+
// The way back to the trace: which turn reached for this, and what it did.
|
|
1702
|
+
const ws = block('In this run', x.where.length + ' node(s)');
|
|
1703
|
+
const line = el('div', 'empty', '');
|
|
1704
|
+
x.where.forEach(function (key) {
|
|
1705
|
+
const hit = BY_KEY.get(key);
|
|
1706
|
+
if (!hit) return;
|
|
1707
|
+
const b = el('button', 'link', label(hit.node));
|
|
1708
|
+
b.addEventListener('click', function () { select(key, true); });
|
|
1709
|
+
line.appendChild(b);
|
|
1710
|
+
});
|
|
1711
|
+
ws.appendChild(line);
|
|
1712
|
+
out.appendChild(ws);
|
|
1713
|
+
return out;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
function memFile(snap) {
|
|
1717
|
+
const tag = snap.file.path + ' \\u00b7 ' + snap.file.bytes + ' B';
|
|
1718
|
+
const src = snap.image ? imageSrc({ type: 'image', url: snap.image }) : null;
|
|
1719
|
+
if (src) {
|
|
1720
|
+
const s = block('File', tag);
|
|
1721
|
+
const img = el('img', 'media');
|
|
1722
|
+
img.src = src;
|
|
1723
|
+
img.alt = snap.file.path;
|
|
1724
|
+
img.loading = 'lazy';
|
|
1725
|
+
s.appendChild(img);
|
|
1726
|
+
return s;
|
|
1727
|
+
}
|
|
1728
|
+
if (snap.content !== undefined && snap.content !== null) {
|
|
1729
|
+
return textBlock('File', snap.content, tag);
|
|
1730
|
+
}
|
|
1731
|
+
const s = block('File', tag);
|
|
1732
|
+
s.appendChild(el('div', 'empty', MEM_EXCUSE[snap.omitted] || 'not shown'));
|
|
1733
|
+
return s;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1338
1736
|
drawGraph().then(drawArch);
|
|
1339
1737
|
buildStats();
|
|
1340
1738
|
if (ENTRIES.length) select('n0', false);
|
package/dist/inspect/css.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CSS = "\n:root {\n --bg: #0f1115; --panel: #161922; --line: #262b36; --fg: #d7dbe3; --dim: #8a92a6;\n --accent: #6ea8fe; --ok: #4ec9a0; --err: #f0776c; --warn: #e2b341; --mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n* { box-sizing: border-box; }\nbody { margin: 0; background: var(--bg); color: var(--fg);\n font: 14px/1.5 system-ui, -apple-system, Segoe UI, sans-serif; }\nheader { padding: 12px 16px; border-bottom: 1px solid var(--line); background: var(--panel);\n display: flex; flex-wrap: wrap; gap: 8px 20px; align-items: baseline; }\nheader h1 { font-size: 15px; margin: 0 12px 0 0; font-weight: 600; }\nheader .kv { color: var(--dim); font-size: 12px; }\nheader .kv b { color: var(--fg); font-weight: 600; }\nmain { display: grid; grid-template-columns: 340px 1fr; height: calc(100vh - 49px); }\naside { border-right: 1px solid var(--line); overflow: auto; background: var(--panel); }\n.pad { padding: 10px; position: sticky; top: 0; background: var(--panel); border-bottom: 1px solid var(--line); }\n#filter { width: 100%; padding: 6px 8px; background: var(--bg); color: var(--fg);\n border: 1px solid var(--line); border-radius: 6px; }\n#timeline { list-style: none; margin: 0; padding: 4px 0; }\n#timeline li { padding: 6px 10px; border-left: 3px solid transparent; cursor: pointer; }\n#timeline li:hover { background: #1c2130; }\n#timeline li.sel { background: #1f2637; border-left-color: var(--accent); }\n#timeline li.covered { opacity: .45; }\n#timeline .row { display: flex; gap: 6px; align-items: baseline; }\n#timeline .t { color: var(--fg); font: 11px var(--mono); min-width: 38px; text-align: right; }\n#timeline .t.same { color: #3d4453; }\n#timeline .dur { margin-left: auto; color: var(--dim); font: 11px var(--mono); }\n#timeline .idx { color: var(--dim); font: 11px var(--mono); min-width: 22px; }\n#timeline .prev { color: var(--dim); font-size: 11px; white-space: nowrap;\n overflow: hidden; text-overflow: ellipsis; padding-left: 44px; }\n/* Branch histories are contiguous in this list, so a coloured edge plus a\n header is enough to stop ten parallel branches reading as one long run. */\n#timeline li.sep { cursor: default; padding: 10px 10px 4px; display: flex; gap: 7px;\n align-items: center; font-size: 11px; letter-spacing: .06em; text-transform: uppercase;\n color: var(--dim); border-left-color: transparent; }\n#timeline li.sep:hover { background: none; }\n#timeline li.sep .dot { width: 8px; height: 8px; border-radius: 2px;\n background: var(--bc, var(--dim)); }\n/* The group header shouts in uppercase; its elapsed time should not. */\n#timeline li.sep .dur { text-transform: none; letter-spacing: 0; }\n/* The branch the join actually waited for. */\n#timeline li.sep.slow { background: #232a38; color: var(--fg); }\n#timeline li.sep.slow .dur { color: var(--fg); }\n#timeline li.branch { padding-left: 20px; border-left-color: var(--bc, var(--line)); }\n#timeline li.branch.sel { border-left-color: var(--bc, var(--accent)); }\n#middle { display: flex; flex-direction: column; overflow: hidden; }\n.tabs { display: flex; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--line); }\n.tabs button { background: none; border: 1px solid transparent; color: var(--dim);\n padding: 4px 12px; border-radius: 6px; cursor: pointer; font-size: 13px; }\n.tabs button.on { color: var(--fg); background: var(--panel); border-color: var(--line); }\n#detail { overflow: auto; padding: 16px; flex: 1; }\n#stats { overflow: auto; padding: 16px; flex: 1; }\ntable.st { width: 100%; border-collapse: collapse; font: 12px var(--mono); }\ntable.st th { text-align: right; color: var(--dim); font-weight: 600; padding: 6px 12px;\n border-bottom: 1px solid var(--line); white-space: nowrap; }\ntable.st td { text-align: right; padding: 5px 12px; border-bottom: 1px solid #1d2230;\n white-space: nowrap; }\ntable.st th:first-child, table.st td:first-child { text-align: left; }\ntable.st tbody tr:hover { background: #1c2130; }\ntable.st tbody tr:last-child td { border-bottom: none; }\n/* The total is a different kind of row, not one more of the same: it gets its\n own band so nobody reads it as another model or another agent. */\ntable.st tfoot td { color: var(--fg); font-weight: 600; background: #191e2a;\n border-top: 1px solid #2b3346; border-bottom: none; padding: 7px 12px; }\ntable.st tfoot td:first-child { color: var(--dim); text-transform: uppercase;\n letter-spacing: .07em; font-size: 11px; }\n/* Two panes hold a zoomable diagram (the run graph and the architecture), so\n everything about one is written for both. */\n.pane { display: flex; flex-direction: column; overflow: hidden; flex: 1; }\n/* An explicit display on the pane would otherwise beat the hidden attribute\n and both views would show at once. */\n.pane[hidden], #detail[hidden], #stats[hidden] { display: none; }\n.pane svg { max-width: none; display: block; }\n.pane .node { cursor: pointer; }\n/* A node with nothing behind it in this run is not clickable, and says so. */\n.pane .node.idle { cursor: default; }\n.gtools { display: flex; align-items: center; gap: 6px; padding: 6px 10px;\n border-bottom: 1px solid var(--line); background: var(--panel); }\n.gtools button { background: var(--bg); border: 1px solid var(--line); color: var(--fg);\n border-radius: 6px; padding: 2px 10px; cursor: pointer; font: 12px var(--mono); }\n.gtools button:hover { border-color: var(--accent); }\n.gtools .hint:first-of-type { margin-left: 8px; }\n.gtools .lvl { margin-left: auto; font-family: var(--mono); }\n/* The canvas is transformed, not scrolled: panning has to work past the edges\n of the diagram, which overflow:auto would forbid. */\n.viewport { flex: 1; overflow: hidden; position: relative; cursor: grab; touch-action: none;\n user-select: none; -webkit-user-select: none; }\n.viewport.drag { cursor: grabbing; }\n.canvas { position: absolute; top: 0; left: 0; transform-origin: 0 0; }\n.hint { color: var(--dim); font-size: 12px; }\n.hint.pad { padding: 16px; }\n.type { font-size: 11px; letter-spacing: .04em; text-transform: uppercase;\n padding: 1px 6px; border-radius: 4px; background: #232a38; color: var(--dim); }\n.type.llm_call { background: #1e3357; color: #9dc1ff; }\n.type.tool_call, .type.tool_result { background: #1e3d33; color: #8ee0c2; }\n.type.compaction { background: #40331a; color: var(--warn); }\n.type.fork, .type.join { background: #3a2450; color: #d1a6ff; }\n.type.final_output { background: #14392c; color: var(--ok); }\n.type.err { background: #4a1f1c; color: var(--err); }\nh2.dt { font-size: 16px; margin: 0 0 4px; display: flex; gap: 10px; align-items: center; }\n.meta { color: var(--dim); font-size: 12px; font-family: var(--mono); margin-bottom: 14px;\n display: flex; flex-wrap: wrap; gap: 4px 14px; }\nsection.blk { border: 1px solid var(--line); border-radius: 8px; margin-bottom: 12px;\n background: var(--panel); overflow: hidden; }\nsection.blk > h3 { margin: 0; padding: 7px 12px; font-size: 12px; font-weight: 600;\n color: var(--dim); border-bottom: 1px solid var(--line); letter-spacing: .03em; }\nsection.blk > h3 .tag { float: right; font-weight: 400; }\nsection.blk.sys { border-left: 3px solid var(--warn); }\n/* An LLM call has three things a reader wants to tell apart at a glance: what\n went in, what came back, and what it cost. Groups carry the first two. */\n.group { border: 1px solid var(--line); border-radius: 10px; margin-bottom: 14px;\n background: #12151d; overflow: hidden; }\n.group > .ghdr { display: flex; align-items: center; gap: 10px; padding: 8px 12px;\n font-size: 11px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;\n background: #1b2130; border-bottom: 1px solid var(--line); }\n.group > .ghdr .tag { margin-left: auto; font: 11px var(--mono); font-weight: 400;\n letter-spacing: 0; text-transform: none; color: var(--dim); }\n.group > .body { padding: 10px; }\n.group > .body > .blk:last-child { margin-bottom: 0; }\n.group.req { border-left: 3px solid var(--accent); }\n.group.req > .ghdr { color: #9dc1ff; }\n.group.res { border-left: 3px solid var(--ok); }\n.group.res > .ghdr { color: #8ee0c2; }\n.stats { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }\n.stat { background: var(--panel); border: 1px solid var(--line); border-radius: 999px;\n padding: 3px 12px; font: 12px var(--mono); color: var(--dim); }\n.stat b { color: var(--fg); font-weight: 600; }\npre { margin: 0; padding: 10px 12px; font: 12px/1.55 var(--mono); white-space: pre-wrap;\n word-break: break-word; overflow-wrap: anywhere; }\npre.text { white-space: pre-wrap; }\nimg.media { display: block; max-width: 100%; max-height: 420px; margin: 10px 12px;\n border: 1px solid var(--line); border-radius: 6px; background: #0d1017; }\nimg.media.thumb { max-height: 88px; margin: 8px 12px; cursor: zoom-in; }\n.msg { border-top: 1px solid var(--line); border-left: 3px solid transparent; }\n.msg:first-of-type { border-top: none; }\n.msg > .role { padding: 5px 12px; font-size: 11px; text-transform: uppercase;\n letter-spacing: .05em; color: var(--dim); background: #1b202c;\n display: flex; justify-content: space-between; }\n.msg.user > .role { color: #9dc1ff; }\n.msg.user { border-left-color: #35507f; }\n.msg.assistant > .role { color: #c3a6ff; }\n.msg.assistant { border-left-color: #5a4380; }\n.msg.tool > .role { color: #8ee0c2; }\n.msg.tool { border-left-color: #2f6b58; }\n.msg.system > .role { color: var(--warn); }\n.msg.system { border-left-color: #7a6535; }\n.msg.err > .role { color: var(--err); }\n.msg.err { border-left-color: #8a3a33; }\n.call { border-top: 1px dashed var(--line); }\n.call .name { padding: 5px 12px; font: 12px var(--mono); color: #8ee0c2; }\ndetails > summary { cursor: pointer; padding: 6px 12px; color: var(--dim); font-size: 12px; }\n.badge { font: 11px var(--mono); color: var(--dim); }\n.badge.ok { color: var(--ok); } .badge.bad { color: var(--err); }\n.empty { padding: 10px 12px; color: var(--dim); font-size: 12px; font-style: italic; }\nbutton.link { background: none; border: none; color: var(--accent); cursor: pointer;\n font: 12px var(--mono); padding: 0 6px 0 0; }\n";
|
|
1
|
+
export declare const CSS = "\n:root {\n --bg: #0f1115; --panel: #161922; --line: #262b36; --fg: #d7dbe3; --dim: #8a92a6;\n --accent: #6ea8fe; --ok: #4ec9a0; --err: #f0776c; --warn: #e2b341; --mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n* { box-sizing: border-box; }\nbody { margin: 0; background: var(--bg); color: var(--fg);\n font: 14px/1.5 system-ui, -apple-system, Segoe UI, sans-serif; }\nheader { padding: 12px 16px; border-bottom: 1px solid var(--line); background: var(--panel);\n display: flex; flex-wrap: wrap; gap: 8px 20px; align-items: baseline; }\nheader h1 { font-size: 15px; margin: 0 12px 0 0; font-weight: 600; }\nheader .kv { color: var(--dim); font-size: 12px; }\nheader .kv b { color: var(--fg); font-weight: 600; }\nmain { display: grid; grid-template-columns: 340px 1fr; height: calc(100vh - 49px); }\naside { border-right: 1px solid var(--line); overflow: auto; background: var(--panel); }\n.pad { padding: 10px; position: sticky; top: 0; background: var(--panel); border-bottom: 1px solid var(--line); }\n#filter { width: 100%; padding: 6px 8px; background: var(--bg); color: var(--fg);\n border: 1px solid var(--line); border-radius: 6px; }\n#timeline { list-style: none; margin: 0; padding: 4px 0; }\n#timeline li { padding: 6px 10px; border-left: 3px solid transparent; cursor: pointer; }\n#timeline li:hover { background: #1c2130; }\n#timeline li.sel { background: #1f2637; border-left-color: var(--accent); }\n#timeline li.covered { opacity: .45; }\n#timeline .row { display: flex; gap: 6px; align-items: baseline; }\n#timeline .t { color: var(--fg); font: 11px var(--mono); min-width: 38px; text-align: right; }\n#timeline .t.same { color: #3d4453; }\n#timeline .dur { margin-left: auto; color: var(--dim); font: 11px var(--mono); }\n#timeline .idx { color: var(--dim); font: 11px var(--mono); min-width: 22px; }\n#timeline .prev { color: var(--dim); font-size: 11px; white-space: nowrap;\n overflow: hidden; text-overflow: ellipsis; padding-left: 44px; }\n/* Branch histories are contiguous in this list, so a coloured edge plus a\n header is enough to stop ten parallel branches reading as one long run. */\n#timeline li.sep { cursor: default; padding: 10px 10px 4px; display: flex; gap: 7px;\n align-items: center; font-size: 11px; letter-spacing: .06em; text-transform: uppercase;\n color: var(--dim); border-left-color: transparent; }\n#timeline li.sep:hover { background: none; }\n#timeline li.sep .dot { width: 8px; height: 8px; border-radius: 2px;\n background: var(--bc, var(--dim)); }\n/* The group header shouts in uppercase; its elapsed time should not. */\n#timeline li.sep .dur { text-transform: none; letter-spacing: 0; }\n/* The branch the join actually waited for. */\n#timeline li.sep.slow { background: #232a38; color: var(--fg); }\n#timeline li.sep.slow .dur { color: var(--fg); }\n#timeline li.branch { padding-left: 20px; border-left-color: var(--bc, var(--line)); }\n#timeline li.branch.sel { border-left-color: var(--bc, var(--accent)); }\n#middle { display: flex; flex-direction: column; overflow: hidden; }\n.tabs { display: flex; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--line); }\n.tabs button { background: none; border: 1px solid transparent; color: var(--dim);\n padding: 4px 12px; border-radius: 6px; cursor: pointer; font-size: 13px; }\n.tabs button.on { color: var(--fg); background: var(--panel); border-color: var(--line); }\n#detail { overflow: auto; padding: 16px; flex: 1; }\n#stats { overflow: auto; padding: 16px; flex: 1; }\ntable.st { width: 100%; border-collapse: collapse; font: 12px var(--mono); }\ntable.st th { text-align: right; color: var(--dim); font-weight: 600; padding: 6px 12px;\n border-bottom: 1px solid var(--line); white-space: nowrap; }\ntable.st td { text-align: right; padding: 5px 12px; border-bottom: 1px solid #1d2230;\n white-space: nowrap; }\ntable.st th:first-child, table.st td:first-child { text-align: left; }\ntable.st tbody tr:hover { background: #1c2130; }\ntable.st tbody tr:last-child td { border-bottom: none; }\n/* The total is a different kind of row, not one more of the same: it gets its\n own band so nobody reads it as another model or another agent. */\ntable.st tfoot td { color: var(--fg); font-weight: 600; background: #191e2a;\n border-top: 1px solid #2b3346; border-bottom: none; padding: 7px 12px; }\ntable.st tfoot td:first-child { color: var(--dim); text-transform: uppercase;\n letter-spacing: .07em; font-size: 11px; }\n/* Two panes hold a zoomable diagram (the run graph and the architecture), so\n everything about one is written for both. */\n.pane { display: flex; flex-direction: column; overflow: hidden; flex: 1; }\n/* An explicit display on the pane would otherwise beat the hidden attribute\n and both views would show at once. */\n.pane[hidden], #detail[hidden], #stats[hidden] { display: none; }\n.pane svg { max-width: none; display: block; }\n.pane .node { cursor: pointer; }\n/* A node with nothing behind it in this run is not clickable, and says so. */\n.pane .node.idle { cursor: default; }\n.gtools { display: flex; align-items: center; gap: 6px; padding: 6px 10px;\n border-bottom: 1px solid var(--line); background: var(--panel); }\n.gtools button { background: var(--bg); border: 1px solid var(--line); color: var(--fg);\n border-radius: 6px; padding: 2px 10px; cursor: pointer; font: 12px var(--mono); }\n.gtools button:hover { border-color: var(--accent); }\n.gtools .hint:first-of-type { margin-left: 8px; }\n.gtools .lvl { margin-left: auto; font-family: var(--mono); }\n.gtools label.check { display: flex; align-items: center; gap: 5px; margin-left: 8px;\n color: var(--dim); font-size: 12px; cursor: pointer; user-select: none; }\n/* The memory view reads left to right: the graph is the index, the panel is\n the page. Putting the body under a tab of its own would mean losing the\n picture every time you asked what a node actually says. */\n#memory .split { display: flex; flex: 1; overflow: hidden; }\n#mdetail { width: 380px; flex: none; padding: 16px; overflow: auto;\n border-right: none; border-left: 1px solid var(--line); }\n#mdetail .dt { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 0 0 10px; }\n#mdetail .idrow { display: flex; gap: 8px; align-items: center; margin: 0 0 14px; }\n#mdetail .idrow code { flex: 1; min-width: 0; font: 12px var(--mono); color: var(--fg);\n background: var(--bg); border: 1px solid var(--line); border-radius: 6px;\n padding: 4px 8px; word-break: break-all; }\n#mdetail .idrow button { flex: 0 0 auto; background: var(--bg); border: 1px solid var(--line);\n color: var(--dim); border-radius: 6px; padding: 4px 9px; font: 11px var(--mono);\n cursor: pointer; }\n#mdetail .idrow button:hover { border-color: var(--accent); color: var(--fg); }\n#mdetail dl.props { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 5px 14px;\n margin: 0 0 14px; font: 12px var(--mono); }\n#mdetail dl.props dt { color: var(--dim); }\n#mdetail dl.props dd { margin: 0; word-break: break-word; }\n/* A block that shares the panel's own background has no edges to show. */\n#mdetail section.blk { background: var(--bg); }\n#mdetail ul.links { list-style: none; margin: 0; padding: 0; }\n#mdetail ul.links li { display: grid; grid-template-columns: 96px auto minmax(0, 1fr);\n gap: 8px; align-items: baseline; padding: 6px 12px; }\n#mdetail ul.links li + li { border-top: 1px solid var(--line); }\n#mdetail ul.links .rel { font: 11px var(--mono); color: var(--warn); white-space: nowrap; }\n#mdetail button.link { font: 12px/1.4 system-ui, sans-serif; padding: 0; text-align: left;\n overflow-wrap: anywhere; }\n#mdetail button.link:hover { text-decoration: underline; }\n#mcount .ok { color: var(--ok); }\n#mcount .bad { color: var(--err); }\n/* The kind palette is the memory page's, so a node reads the same in both. */\n.kind { font-size: 11px; letter-spacing: .04em; text-transform: uppercase;\n padding: 1px 6px; border-radius: 4px; background: #232a38; color: var(--dim); }\n.kind.task { background: #1e3357; color: #9dc1ff; }\n.kind.plan { background: #3a2450; color: #d1a6ff; }\n.kind.fact { background: #14392c; color: #8ee0c2; }\n.kind.snippet { background: #1e3d33; color: #8ee0c2; }\n.kind.file { background: #40331a; color: #f0dfb4; }\n.kind.operation { background: #3d2a1b; color: #f2d9c2; }\n.kind.preference { background: #3d1f2c; color: #f5b8cd; }\n/* Selection is a class rather than attributes written onto the shapes: the\n previous pick has to lose its outline without a redraw. White, because the\n accent already means \"the recall matched this one\". */\n#memory .node.picked rect, #memory .node.picked polygon,\n#memory .node.picked path, #memory .node.picked circle {\n stroke: var(--fg) !important; stroke-width: 4px !important; }\n/* The canvas is transformed, not scrolled: panning has to work past the edges\n of the diagram, which overflow:auto would forbid. */\n.viewport { flex: 1; overflow: hidden; position: relative; cursor: grab; touch-action: none;\n user-select: none; -webkit-user-select: none; }\n.viewport.drag { cursor: grabbing; }\n.canvas { position: absolute; top: 0; left: 0; transform-origin: 0 0; }\n.hint { color: var(--dim); font-size: 12px; }\n.hint.pad { padding: 16px; }\n.type { font-size: 11px; letter-spacing: .04em; text-transform: uppercase;\n padding: 1px 6px; border-radius: 4px; background: #232a38; color: var(--dim); }\n.type.llm_call { background: #1e3357; color: #9dc1ff; }\n.type.tool_call, .type.tool_result { background: #1e3d33; color: #8ee0c2; }\n.type.compaction { background: #40331a; color: var(--warn); }\n.type.fork, .type.join { background: #3a2450; color: #d1a6ff; }\n.type.final_output { background: #14392c; color: var(--ok); }\n.type.err { background: #4a1f1c; color: var(--err); }\nh2.dt { font-size: 16px; margin: 0 0 4px; display: flex; gap: 10px; align-items: center; }\n.meta { color: var(--dim); font-size: 12px; font-family: var(--mono); margin-bottom: 14px;\n display: flex; flex-wrap: wrap; gap: 4px 14px; }\nsection.blk { border: 1px solid var(--line); border-radius: 8px; margin-bottom: 12px;\n background: var(--panel); overflow: hidden; }\nsection.blk > h3 { margin: 0; padding: 7px 12px; font-size: 12px; font-weight: 600;\n color: var(--dim); border-bottom: 1px solid var(--line); letter-spacing: .03em; }\nsection.blk > h3 .tag { float: right; font-weight: 400; }\nsection.blk.sys { border-left: 3px solid var(--warn); }\n/* An LLM call has three things a reader wants to tell apart at a glance: what\n went in, what came back, and what it cost. Groups carry the first two. */\n.group { border: 1px solid var(--line); border-radius: 10px; margin-bottom: 14px;\n background: #12151d; overflow: hidden; }\n.group > .ghdr { display: flex; align-items: center; gap: 10px; padding: 8px 12px;\n font-size: 11px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;\n background: #1b2130; border-bottom: 1px solid var(--line); }\n.group > .ghdr .tag { margin-left: auto; font: 11px var(--mono); font-weight: 400;\n letter-spacing: 0; text-transform: none; color: var(--dim); }\n.group > .body { padding: 10px; }\n.group > .body > .blk:last-child { margin-bottom: 0; }\n.group.req { border-left: 3px solid var(--accent); }\n.group.req > .ghdr { color: #9dc1ff; }\n.group.res { border-left: 3px solid var(--ok); }\n.group.res > .ghdr { color: #8ee0c2; }\n.stats { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }\n.stat { background: var(--panel); border: 1px solid var(--line); border-radius: 999px;\n padding: 3px 12px; font: 12px var(--mono); color: var(--dim); }\n.stat b { color: var(--fg); font-weight: 600; }\npre { margin: 0; padding: 10px 12px; font: 12px/1.55 var(--mono); white-space: pre-wrap;\n word-break: break-word; overflow-wrap: anywhere; }\npre.text { white-space: pre-wrap; }\nimg.media { display: block; max-width: 100%; max-height: 420px; margin: 10px 12px;\n border: 1px solid var(--line); border-radius: 6px; background: #0d1017; }\nimg.media.thumb { max-height: 88px; margin: 8px 12px; cursor: zoom-in; }\n.msg { border-top: 1px solid var(--line); border-left: 3px solid transparent; }\n.msg:first-of-type { border-top: none; }\n.msg > .role { padding: 5px 12px; font-size: 11px; text-transform: uppercase;\n letter-spacing: .05em; color: var(--dim); background: #1b202c;\n display: flex; justify-content: space-between; }\n.msg.user > .role { color: #9dc1ff; }\n.msg.user { border-left-color: #35507f; }\n.msg.assistant > .role { color: #c3a6ff; }\n.msg.assistant { border-left-color: #5a4380; }\n.msg.tool > .role { color: #8ee0c2; }\n.msg.tool { border-left-color: #2f6b58; }\n.msg.system > .role { color: var(--warn); }\n.msg.system { border-left-color: #7a6535; }\n.msg.err > .role { color: var(--err); }\n.msg.err { border-left-color: #8a3a33; }\n.call { border-top: 1px dashed var(--line); }\n.call .name { padding: 5px 12px; font: 12px var(--mono); color: #8ee0c2; }\ndetails > summary { cursor: pointer; padding: 6px 12px; color: var(--dim); font-size: 12px; }\n.badge { font: 11px var(--mono); color: var(--dim); }\n.badge.ok { color: var(--ok); } .badge.bad { color: var(--err); }\n.empty { padding: 10px 12px; color: var(--dim); font-size: 12px; font-style: italic; }\nbutton.link { background: none; border: none; color: var(--accent); cursor: pointer;\n font: 12px var(--mono); padding: 0 6px 0 0; }\n";
|
|
2
2
|
//# sourceMappingURL=css.d.ts.map
|
package/dist/inspect/css.js
CHANGED
|
@@ -87,6 +87,55 @@ table.st tfoot td:first-child { color: var(--dim); text-transform: uppercase;
|
|
|
87
87
|
.gtools button:hover { border-color: var(--accent); }
|
|
88
88
|
.gtools .hint:first-of-type { margin-left: 8px; }
|
|
89
89
|
.gtools .lvl { margin-left: auto; font-family: var(--mono); }
|
|
90
|
+
.gtools label.check { display: flex; align-items: center; gap: 5px; margin-left: 8px;
|
|
91
|
+
color: var(--dim); font-size: 12px; cursor: pointer; user-select: none; }
|
|
92
|
+
/* The memory view reads left to right: the graph is the index, the panel is
|
|
93
|
+
the page. Putting the body under a tab of its own would mean losing the
|
|
94
|
+
picture every time you asked what a node actually says. */
|
|
95
|
+
#memory .split { display: flex; flex: 1; overflow: hidden; }
|
|
96
|
+
#mdetail { width: 380px; flex: none; padding: 16px; overflow: auto;
|
|
97
|
+
border-right: none; border-left: 1px solid var(--line); }
|
|
98
|
+
#mdetail .dt { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 0 0 10px; }
|
|
99
|
+
#mdetail .idrow { display: flex; gap: 8px; align-items: center; margin: 0 0 14px; }
|
|
100
|
+
#mdetail .idrow code { flex: 1; min-width: 0; font: 12px var(--mono); color: var(--fg);
|
|
101
|
+
background: var(--bg); border: 1px solid var(--line); border-radius: 6px;
|
|
102
|
+
padding: 4px 8px; word-break: break-all; }
|
|
103
|
+
#mdetail .idrow button { flex: 0 0 auto; background: var(--bg); border: 1px solid var(--line);
|
|
104
|
+
color: var(--dim); border-radius: 6px; padding: 4px 9px; font: 11px var(--mono);
|
|
105
|
+
cursor: pointer; }
|
|
106
|
+
#mdetail .idrow button:hover { border-color: var(--accent); color: var(--fg); }
|
|
107
|
+
#mdetail dl.props { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 5px 14px;
|
|
108
|
+
margin: 0 0 14px; font: 12px var(--mono); }
|
|
109
|
+
#mdetail dl.props dt { color: var(--dim); }
|
|
110
|
+
#mdetail dl.props dd { margin: 0; word-break: break-word; }
|
|
111
|
+
/* A block that shares the panel's own background has no edges to show. */
|
|
112
|
+
#mdetail section.blk { background: var(--bg); }
|
|
113
|
+
#mdetail ul.links { list-style: none; margin: 0; padding: 0; }
|
|
114
|
+
#mdetail ul.links li { display: grid; grid-template-columns: 96px auto minmax(0, 1fr);
|
|
115
|
+
gap: 8px; align-items: baseline; padding: 6px 12px; }
|
|
116
|
+
#mdetail ul.links li + li { border-top: 1px solid var(--line); }
|
|
117
|
+
#mdetail ul.links .rel { font: 11px var(--mono); color: var(--warn); white-space: nowrap; }
|
|
118
|
+
#mdetail button.link { font: 12px/1.4 system-ui, sans-serif; padding: 0; text-align: left;
|
|
119
|
+
overflow-wrap: anywhere; }
|
|
120
|
+
#mdetail button.link:hover { text-decoration: underline; }
|
|
121
|
+
#mcount .ok { color: var(--ok); }
|
|
122
|
+
#mcount .bad { color: var(--err); }
|
|
123
|
+
/* The kind palette is the memory page's, so a node reads the same in both. */
|
|
124
|
+
.kind { font-size: 11px; letter-spacing: .04em; text-transform: uppercase;
|
|
125
|
+
padding: 1px 6px; border-radius: 4px; background: #232a38; color: var(--dim); }
|
|
126
|
+
.kind.task { background: #1e3357; color: #9dc1ff; }
|
|
127
|
+
.kind.plan { background: #3a2450; color: #d1a6ff; }
|
|
128
|
+
.kind.fact { background: #14392c; color: #8ee0c2; }
|
|
129
|
+
.kind.snippet { background: #1e3d33; color: #8ee0c2; }
|
|
130
|
+
.kind.file { background: #40331a; color: #f0dfb4; }
|
|
131
|
+
.kind.operation { background: #3d2a1b; color: #f2d9c2; }
|
|
132
|
+
.kind.preference { background: #3d1f2c; color: #f5b8cd; }
|
|
133
|
+
/* Selection is a class rather than attributes written onto the shapes: the
|
|
134
|
+
previous pick has to lose its outline without a redraw. White, because the
|
|
135
|
+
accent already means "the recall matched this one". */
|
|
136
|
+
#memory .node.picked rect, #memory .node.picked polygon,
|
|
137
|
+
#memory .node.picked path, #memory .node.picked circle {
|
|
138
|
+
stroke: var(--fg) !important; stroke-width: 4px !important; }
|
|
90
139
|
/* The canvas is transformed, not scrolled: panning has to work past the edges
|
|
91
140
|
of the diagram, which overflow:auto would forbid. */
|
|
92
141
|
.viewport { flex: 1; overflow: hidden; position: relative; cursor: grab; touch-action: none;
|
package/dist/inspect/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type ReportOptions, type RunReport } from './report.ts';
|
|
2
|
-
import type { AgentState } from '../state.ts';
|
|
3
1
|
import type { PayloadResolver } from '../payload.ts';
|
|
4
|
-
|
|
2
|
+
import type { AgentState } from '../state.ts';
|
|
3
|
+
import { type ReportOptions, type RunReport } from './report.ts';
|
|
4
|
+
export { buildRunReport, MAX_BLOB_BYTES, MAX_MEMORY_FILE_BYTES, MERMAID_URL, type ReportOptions, type RunMemory, type RunReport, } from './report.ts';
|
|
5
5
|
/** Report → one standalone HTML document. */
|
|
6
6
|
export declare function renderReportHtml(report: RunReport, opts?: ReportOptions): string;
|
|
7
7
|
/** The usual entry point: state in, HTML out. */
|
package/dist/inspect/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { page } from "./page.js";
|
|
2
2
|
import { buildRunReport, MERMAID_URL } from "./report.js";
|
|
3
|
-
export { buildRunReport, MAX_BLOB_BYTES, MERMAID_URL, } from "./report.js";
|
|
3
|
+
export { buildRunReport, MAX_BLOB_BYTES, MAX_MEMORY_FILE_BYTES, MERMAID_URL, } from "./report.js";
|
|
4
4
|
/** Report → one standalone HTML document. */
|
|
5
5
|
export function renderReportHtml(report, opts = {}) {
|
|
6
6
|
return page(report, opts.mermaidUrl ?? MERMAID_URL);
|
package/dist/inspect/page.js
CHANGED
|
@@ -29,6 +29,7 @@ export function page(report, mermaidUrl) {
|
|
|
29
29
|
<button data-view="detail">Detail</button>
|
|
30
30
|
<button data-view="stats">Stats</button>
|
|
31
31
|
<button data-view="agents">Agents</button>
|
|
32
|
+
<button data-view="memory" hidden>Memory</button>
|
|
32
33
|
</div>
|
|
33
34
|
<div id="graph" class="pane">
|
|
34
35
|
<div class="gtools">
|
|
@@ -54,6 +55,21 @@ export function page(report, mermaidUrl) {
|
|
|
54
55
|
</div>
|
|
55
56
|
<div class="viewport"><div class="canvas"><div class="hint pad">rendering…</div></div></div>
|
|
56
57
|
</div>
|
|
58
|
+
<div id="memory" class="pane" hidden>
|
|
59
|
+
<div class="gtools">
|
|
60
|
+
<button data-zoom="out" title="zoom out">−</button>
|
|
61
|
+
<button data-zoom="in" title="zoom in">+</button>
|
|
62
|
+
<button data-zoom="fit" title="fit to window (double-click the canvas)">fit</button>
|
|
63
|
+
<button data-zoom="reset" title="actual size">1:1</button>
|
|
64
|
+
<label class="check"><input type="checkbox" id="madded"> show what it wrote</label>
|
|
65
|
+
<span class="hint" id="mcount"></span>
|
|
66
|
+
<span class="hint lvl">100%</span>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="split">
|
|
69
|
+
<div class="viewport"><div class="canvas"><div class="hint pad">rendering…</div></div></div>
|
|
70
|
+
<aside id="mdetail"><div class="hint pad">Pick a node.</div></aside>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
57
73
|
</section>
|
|
58
74
|
</main>
|
|
59
75
|
<script id="run-data" type="application/json">${embedJson(report)}</script>
|
package/dist/inspect/report.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import type { Architecture } from '../architecture.ts';
|
|
2
|
+
import { type ReportNode } from '../memory/report.ts';
|
|
3
|
+
import type { MemoryStore } from '../memory/store.ts';
|
|
4
|
+
import type { MemoryEdge } from '../memory/types.ts';
|
|
2
5
|
import { type PayloadResolver } from '../payload.ts';
|
|
3
6
|
import type { AgentState } from '../state.ts';
|
|
4
7
|
/** Default per-payload inline cap. Keeps one runaway tool result from turning
|
|
5
8
|
* a report into a file no editor will open. */
|
|
6
9
|
export declare const MAX_BLOB_BYTES: number;
|
|
7
10
|
export declare const MERMAID_URL = "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
|
|
11
|
+
/** Deliberately meaner than the memory page's own cap: this file already holds
|
|
12
|
+
* a whole run, and one remembered artifact should not decide whether it opens. */
|
|
13
|
+
export declare const MAX_MEMORY_FILE_BYTES: number;
|
|
8
14
|
export interface ReportOptions {
|
|
9
15
|
title?: string;
|
|
10
16
|
/** per-payload inline cap; larger values are cut, with a visible marker */
|
|
@@ -22,6 +28,14 @@ export interface ReportOptions {
|
|
|
22
28
|
* strictly smaller picture but never a wrong one.
|
|
23
29
|
*/
|
|
24
30
|
architecture?: Architecture;
|
|
31
|
+
/**
|
|
32
|
+
* The memory to resolve recalled and committed nodes against. A trajectory
|
|
33
|
+
* records ids, kinds and edges but no bodies, so without this the memory
|
|
34
|
+
* view can draw the shape of what was read and not a word of it.
|
|
35
|
+
*/
|
|
36
|
+
memory?: MemoryStore;
|
|
37
|
+
/** ceiling on an inlined remembered file; 0 to inline none */
|
|
38
|
+
maxMemoryFileBytes?: number;
|
|
25
39
|
}
|
|
26
40
|
/** Everything the page needs, and nothing that needs a store to interpret. */
|
|
27
41
|
export interface RunReport {
|
|
@@ -44,6 +58,17 @@ export interface RunReport {
|
|
|
44
58
|
media: string[];
|
|
45
59
|
/** the declared wiring, when the caller had a runner to ask */
|
|
46
60
|
architecture?: Architecture;
|
|
61
|
+
/**
|
|
62
|
+
* The memory nodes this run read or wrote, as the graph holds them *now* —
|
|
63
|
+
* which is later than the run. The page says so where it matters: a node
|
|
64
|
+
* can have moved on a revision since, or be gone from the graph entirely.
|
|
65
|
+
*/
|
|
66
|
+
memory?: RunMemory;
|
|
67
|
+
}
|
|
68
|
+
export interface RunMemory {
|
|
69
|
+
dir: string;
|
|
70
|
+
nodes: ReportNode[];
|
|
71
|
+
edges: MemoryEdge[];
|
|
47
72
|
}
|
|
48
73
|
/**
|
|
49
74
|
* Resolves a state into a self-describing report. Separate from rendering so a
|