@zenera/neo 1.1.11 → 1.1.14

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.
Files changed (68) hide show
  1. package/README.md +3 -3
  2. package/dist/agent.d.ts +8 -7
  3. package/dist/agent.js +15 -8
  4. package/dist/architecture.d.ts +7 -5
  5. package/dist/architecture.js +20 -12
  6. package/dist/embeddings/limiter.js +14 -3
  7. package/dist/index.d.ts +12 -2
  8. package/dist/index.js +12 -2
  9. package/dist/inspect/client.d.ts +1 -1
  10. package/dist/inspect/client.js +440 -26
  11. package/dist/inspect/css.d.ts +1 -1
  12. package/dist/inspect/css.js +49 -0
  13. package/dist/inspect/index.d.ts +3 -3
  14. package/dist/inspect/index.js +1 -1
  15. package/dist/inspect/page.js +16 -0
  16. package/dist/inspect/report.d.ts +25 -0
  17. package/dist/inspect/report.js +45 -0
  18. package/dist/kernel.d.ts +1 -1
  19. package/dist/kernel.js +40 -23
  20. package/dist/memory/files.d.ts +24 -0
  21. package/dist/memory/files.js +85 -0
  22. package/dist/memory/graph.d.ts +56 -0
  23. package/dist/memory/graph.js +159 -0
  24. package/dist/memory/index.d.ts +93 -0
  25. package/dist/memory/index.js +357 -0
  26. package/dist/memory/instructions.d.ts +15 -0
  27. package/dist/memory/instructions.js +103 -0
  28. package/dist/memory/page.d.ts +8 -0
  29. package/dist/memory/page.js +797 -0
  30. package/dist/memory/recall.d.ts +35 -0
  31. package/dist/memory/recall.js +161 -0
  32. package/dist/memory/render.d.ts +10 -0
  33. package/dist/memory/render.js +228 -0
  34. package/dist/memory/report.d.ts +69 -0
  35. package/dist/memory/report.js +131 -0
  36. package/dist/memory/store.d.ts +49 -0
  37. package/dist/memory/store.js +226 -0
  38. package/dist/memory/tools.d.ts +11 -0
  39. package/dist/memory/tools.js +337 -0
  40. package/dist/memory/types.d.ts +193 -0
  41. package/dist/memory/types.js +105 -0
  42. package/dist/memory/vectors.d.ts +29 -0
  43. package/dist/memory/vectors.js +152 -0
  44. package/dist/project/config.d.ts +49 -1
  45. package/dist/project/config.js +63 -0
  46. package/dist/project/index.d.ts +2 -2
  47. package/dist/project/index.js +1 -1
  48. package/dist/project/load.d.ts +16 -2
  49. package/dist/project/load.js +67 -1
  50. package/dist/runner.d.ts +4 -2
  51. package/dist/runner.js +29 -30
  52. package/dist/services.d.ts +23 -6
  53. package/dist/services.js +25 -15
  54. package/dist/tools/workspace.js +195 -17
  55. package/dist/trajectory.d.ts +14 -12
  56. package/dist/types.d.ts +5 -5
  57. package/dist/types.js +3 -3
  58. package/package.json +2 -5
  59. package/dist/memory-stores/factory.d.ts +0 -15
  60. package/dist/memory-stores/factory.js +0 -29
  61. package/dist/memory-stores/file.d.ts +0 -37
  62. package/dist/memory-stores/file.js +0 -174
  63. package/dist/memory-stores/in-memory.d.ts +0 -17
  64. package/dist/memory-stores/in-memory.js +0 -80
  65. package/dist/memory-stores/index.d.ts +0 -4
  66. package/dist/memory-stores/index.js +0 -5
  67. package/dist/memory.d.ts +0 -115
  68. package/dist/memory.js +0 -213
@@ -60,6 +60,15 @@ const ROOT = flatten(STATE.trajectory, 0, null);
60
60
 
61
61
  function names(list) { return list.join(', '); }
62
62
 
63
+ // The edges are what makes a recollection a subgraph rather than a hit list,
64
+ // so they are shown even though the injected block already renders them.
65
+ function edgeLines(edges) {
66
+ if (!edges || !edges.length) return '';
67
+ return '\\n\\n' + edges.map(function (e) {
68
+ return e.source + ' --' + e.relation + '--> ' + e.target;
69
+ }).join('\\n');
70
+ }
71
+
63
72
  function base(path) { return path.split('/').pop() || path; }
64
73
 
65
74
  function label(n) {
@@ -68,8 +77,8 @@ function label(n) {
68
77
  case 'system_prompt': return 'system prompt'
69
78
  + (n.sources && n.sources.length ? ': ' + names(n.sources.map(function (s) { return base(s.path); })) : '');
70
79
  case 'load_skills': return 'skills: ' + names(n.skills.map(function (s) { return s.name; }));
71
- case 'memory_recall': return 'memory recall: ' + n.scope;
72
- case 'memory_op': return 'memory ' + n.op;
80
+ case 'memory_recall': return 'memory recall: ' + n.nodes.length + ' nodes';
81
+ case 'memory_op': return 'memory ' + n.op + ': ' + n.nodes.length + ' nodes';
73
82
  case 'llm_call': return 'llm: ' + n.model + (n.toolCalls.length
74
83
  ? ' -> ' + names(n.toolCalls.map(function (c) { return c.name; })) : '');
75
84
  case 'tool_call': return 'call: ' + n.name;
@@ -512,18 +521,20 @@ function detailFor(e) {
512
521
  break;
513
522
 
514
523
  case 'memory_recall':
515
- out.appendChild(textBlock('Query', JSON.stringify(n.query, null, 2), n.store + ' · ' + n.scope));
516
- out.appendChild(textBlock('Hits', n.hits.map(function (hit) {
517
- return hit.id + ' score=' + hit.score + ' rev=' + hit.revision;
518
- }).join('\\n')));
524
+ out.appendChild(textBlock('Query', JSON.stringify(n.query, null, 2),
525
+ n.seeds.length + ' seed(s)'));
526
+ out.appendChild(textBlock('Subgraph', n.nodes.map(function (m) {
527
+ return m.id + ' ' + m.kind
528
+ + (n.seeds.indexOf(m.id) >= 0 ? ' score=' + m.score.toFixed(3) : ' (stitched)');
529
+ }).join('\\n') + edgeLines(n.edges)));
519
530
  out.appendChild(textBlock('Injected block', blob(n.content)));
520
531
  break;
521
532
 
522
533
  case 'memory_op':
523
- out.appendChild(textBlock('Operation',
524
- n.op + ' ' + n.recordId + ' (rev ' + n.revision + ')', n.store + ' · ' + n.scope));
525
- if (n.before) out.appendChild(textBlock('Before', blob(n.before)));
526
- if (n.after) out.appendChild(textBlock('After', blob(n.after)));
534
+ out.appendChild(textBlock('Operation', n.nodes.map(function (m) {
535
+ return n.op + ' ' + m.id + ' ' + m.kind + ' rev ' + m.revision;
536
+ }).join('\\n') + edgeLines(n.edges),
537
+ n.files ? n.files + ' file(s)' : undefined));
527
538
  break;
528
539
 
529
540
  case 'llm_call': {
@@ -792,11 +803,15 @@ const PANES = {};
792
803
  function show(view) {
793
804
  document.getElementById('graph').hidden = view !== 'graph';
794
805
  document.getElementById('agents').hidden = view !== 'agents';
806
+ document.getElementById('memory').hidden = view !== 'memory';
795
807
  detailEl.hidden = view !== 'detail';
796
808
  statsEl.hidden = view !== 'stats';
797
809
  Array.prototype.forEach.call(document.getElementById('viewtabs').children, function (b) {
798
810
  b.classList.toggle('on', b.dataset.view === view);
799
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(); }
800
815
  if (PANES[view]) PANES[view].refit();
801
816
  }
802
817
  Array.prototype.forEach.call(document.getElementById('viewtabs').children, function (b) {
@@ -913,6 +928,7 @@ function panZoom(paneId) {
913
928
 
914
929
  PANES.graph = panZoom('graph');
915
930
  PANES.agents = panZoom('agents');
931
+ PANES.memory = panZoom('memory');
916
932
 
917
933
  // --- diagram --------------------------------------------------------------
918
934
 
@@ -998,6 +1014,14 @@ function diagram() {
998
1014
  return lines.join('\\n');
999
1015
  }
1000
1016
 
1017
+ // Mermaid refuses a diagram past either of these (defaults 500 edges and
1018
+ // 50000 characters, which a long run passes easily) and the pane then shows
1019
+ // only its error. Both are "secure" options, so initialize() is the one place
1020
+ // they can be raised from. The report is a file on disk, not a live page —
1021
+ // a slow render beats a refused one.
1022
+ const MAX_EDGES = 20000;
1023
+ const MAX_TEXT_SIZE = 5000000;
1024
+
1001
1025
  // One Mermaid, two diagrams. Loading it is the only network the page does, so
1002
1026
  // it happens once and both panes wait on the same promise.
1003
1027
  let mermaidLib = null;
@@ -1006,6 +1030,7 @@ function mermaid() {
1006
1030
  mermaidLib = import(MERMAID_URL).then(function (m) {
1007
1031
  m.default.initialize({
1008
1032
  startOnLoad: false, theme: 'dark', securityLevel: 'strict',
1033
+ maxEdges: MAX_EDGES, maxTextSize: MAX_TEXT_SIZE,
1009
1034
  flowchart: { htmlLabels: false }
1010
1035
  });
1011
1036
  return m.default;
@@ -1098,7 +1123,7 @@ ENTRIES.forEach(function (e) {
1098
1123
  } else if (n.type === 'handoff') {
1099
1124
  bump(USED.handoffs, n.from + SEP + n.to, e.key);
1100
1125
  } else if (n.type === 'memory_recall' || n.type === 'memory_op') {
1101
- bump(USED.memory, n.agent + SEP + n.store + '/' + n.scope, e.key);
1126
+ bump(USED.memory, n.agent, e.key);
1102
1127
  } else if (n.type === 'fork') {
1103
1128
  n.branches.forEach(function (b) { bump(USED.forks, n.agent + SEP + b.agent, e.key); });
1104
1129
  }
@@ -1113,7 +1138,7 @@ function observedArchitecture() {
1113
1138
  const byName = new Map();
1114
1139
  function agentOf(name) {
1115
1140
  if (!byName.has(name)) {
1116
- byName.set(name, { name: name, tools: [], handoffs: [], memory: [] });
1141
+ byName.set(name, { name: name, tools: [], handoffs: [] });
1117
1142
  }
1118
1143
  return byName.get(name);
1119
1144
  }
@@ -1142,15 +1167,10 @@ function observedArchitecture() {
1142
1167
  agentOf(n.to);
1143
1168
  if (from.handoffs.indexOf(n.to) < 0) from.handoffs.push(n.to);
1144
1169
  } else if (n.type === 'memory_recall' || n.type === 'memory_op') {
1145
- const known = a.memory.some(function (m) {
1146
- return m.store === n.store && m.scope === n.scope;
1147
- });
1148
- if (!known) {
1149
- a.memory.push({
1150
- store: n.store, scope: n.scope,
1151
- access: n.type === 'memory_op' ? 'read-write' : 'read'
1152
- });
1153
- }
1170
+ // A run shows that an agent reached the memory, never how wide its mask
1171
+ // was: nothing it did not read leaves a trace to read the mask off.
1172
+ if (!a.memory) a.memory = { access: 'read', sees: [], writes: [] };
1173
+ if (n.type === 'memory_op' && n.op !== 'load') a.memory.access = 'read-write';
1154
1174
  } else if (n.type === 'fork') {
1155
1175
  if (!a.fork) a.fork = { agents: [] };
1156
1176
  n.branches.forEach(function (b) {
@@ -1255,15 +1275,16 @@ function archDiagram() {
1255
1275
  lines.push(' ' + self + ' --- ' + k);
1256
1276
  }
1257
1277
 
1258
- a.memory.forEach(function (m) {
1259
- const used = USED.memory.get(a.name + SEP + m.store + '/' + m.scope);
1278
+ if (a.memory) {
1279
+ const used = USED.memory.get(a.name);
1260
1280
  const k = archKey(used);
1261
- lines.push(' ' + k + '[("' + safe(m.store) + ' / ' + safe(m.scope)
1262
- + (m.access === 'read-write' ? ' (rw)' : ' (ro)') + '")]');
1281
+ const sees = Array.isArray(a.memory.sees) ? a.memory.sees : [a.memory.sees];
1282
+ lines.push(' ' + k + '[("memory ' + (a.memory.access === 'read' ? '(ro)' : '(rw)')
1283
+ + ' sees ' + safe(sees.join(' ') || '*') + '")]');
1263
1284
  lines.push(' class ' + k + ' mem;');
1264
1285
  if (!used) lines.push(' class ' + k + ' idle;');
1265
1286
  lines.push(' ' + self + ' --- ' + k);
1266
- });
1287
+ }
1267
1288
  lines.push(' end');
1268
1289
  // Mermaid's default subgraph is a light box, which on a dark page reads as
1269
1290
  // the foreground rather than as the container. An agent nobody entered is
@@ -1319,6 +1340,399 @@ async function drawArch() {
1319
1340
  });
1320
1341
  }
1321
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
+
1322
1736
  drawGraph().then(drawArch);
1323
1737
  buildStats();
1324
1738
  if (ENTRIES.length) select('n0', false);
@@ -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