@jmanuelcorral/openteam 0.1.40 → 0.1.42

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 (113) hide show
  1. package/AGENTS.md +15 -0
  2. package/README.es.md +1 -1
  3. package/README.md +1 -1
  4. package/dist/cli.js +457 -37
  5. package/dist/commands/graph.d.ts +73 -0
  6. package/dist/commands/graph.d.ts.map +1 -0
  7. package/dist/config/load.d.ts.map +1 -1
  8. package/dist/config/migrations.d.ts +27 -0
  9. package/dist/config/migrations.d.ts.map +1 -0
  10. package/dist/config/resolve.d.ts +34 -0
  11. package/dist/config/resolve.d.ts.map +1 -0
  12. package/dist/config/schema.d.ts +57 -0
  13. package/dist/config/schema.d.ts.map +1 -1
  14. package/dist/console/assets.d.ts +9 -1
  15. package/dist/console/assets.d.ts.map +1 -1
  16. package/dist/console/opencodeClient.d.ts +6 -0
  17. package/dist/console/opencodeClient.d.ts.map +1 -1
  18. package/dist/console/protocol.d.ts +8 -0
  19. package/dist/console/protocol.d.ts.map +1 -1
  20. package/dist/console/render.d.ts +25 -0
  21. package/dist/console/render.d.ts.map +1 -1
  22. package/dist/context/redaction.d.ts +48 -0
  23. package/dist/context/redaction.d.ts.map +1 -0
  24. package/dist/context/schema.d.ts +115 -0
  25. package/dist/context/schema.d.ts.map +1 -0
  26. package/dist/context/types.d.ts +8 -0
  27. package/dist/context/types.d.ts.map +1 -0
  28. package/dist/graph/events.d.ts +25 -0
  29. package/dist/graph/events.d.ts.map +1 -0
  30. package/dist/graph/ids.d.ts +5 -0
  31. package/dist/graph/ids.d.ts.map +1 -0
  32. package/dist/graph/planner.d.ts +15 -0
  33. package/dist/graph/planner.d.ts.map +1 -0
  34. package/dist/graph/policies.d.ts +34 -0
  35. package/dist/graph/policies.d.ts.map +1 -0
  36. package/dist/graph/privacyPolicy.d.ts +29 -0
  37. package/dist/graph/privacyPolicy.d.ts.map +1 -0
  38. package/dist/graph/reducer.d.ts +27 -0
  39. package/dist/graph/reducer.d.ts.map +1 -0
  40. package/dist/graph/replay.d.ts +20 -0
  41. package/dist/graph/replay.d.ts.map +1 -0
  42. package/dist/graph/reviewerPolicy.d.ts +27 -0
  43. package/dist/graph/reviewerPolicy.d.ts.map +1 -0
  44. package/dist/graph/schema.d.ts +220 -0
  45. package/dist/graph/schema.d.ts.map +1 -0
  46. package/dist/graph/types.d.ts +40 -0
  47. package/dist/graph/types.d.ts.map +1 -0
  48. package/dist/graph/validate.d.ts +19 -0
  49. package/dist/graph/validate.d.ts.map +1 -0
  50. package/dist/index.js +46 -3
  51. package/dist/orchestrator/graphCancellation.d.ts +84 -0
  52. package/dist/orchestrator/graphCancellation.d.ts.map +1 -0
  53. package/dist/orchestrator/graphEffects.d.ts +63 -0
  54. package/dist/orchestrator/graphEffects.d.ts.map +1 -0
  55. package/dist/orchestrator/graphReview.d.ts +84 -0
  56. package/dist/orchestrator/graphReview.d.ts.map +1 -0
  57. package/dist/orchestrator/graphRuntime.d.ts +69 -0
  58. package/dist/orchestrator/graphRuntime.d.ts.map +1 -0
  59. package/dist/orchestrator/opencodeSessionAdapter.d.ts +117 -0
  60. package/dist/orchestrator/opencodeSessionAdapter.d.ts.map +1 -0
  61. package/dist/orchestrator/sdd.d.ts +38 -2
  62. package/dist/orchestrator/sdd.d.ts.map +1 -1
  63. package/dist/orchestrator/sddCompiler.d.ts +44 -0
  64. package/dist/orchestrator/sddCompiler.d.ts.map +1 -0
  65. package/dist/orchestrator/sessionReconciler.d.ts +74 -0
  66. package/dist/orchestrator/sessionReconciler.d.ts.map +1 -0
  67. package/dist/orchestrator/shadowComparator.d.ts +62 -0
  68. package/dist/orchestrator/shadowComparator.d.ts.map +1 -0
  69. package/dist/orchestrator/worktreeAdapter.d.ts +39 -0
  70. package/dist/orchestrator/worktreeAdapter.d.ts.map +1 -0
  71. package/dist/orchestrator/worktreeDispatch.d.ts +84 -0
  72. package/dist/orchestrator/worktreeDispatch.d.ts.map +1 -0
  73. package/dist/orchestrator/worktreeReconciler.d.ts +74 -0
  74. package/dist/orchestrator/worktreeReconciler.d.ts.map +1 -0
  75. package/dist/storage/graph/codec.d.ts +52 -0
  76. package/dist/storage/graph/codec.d.ts.map +1 -0
  77. package/dist/storage/graph/fsGraphJournal.d.ts +3 -0
  78. package/dist/storage/graph/fsGraphJournal.d.ts.map +1 -0
  79. package/dist/storage/graph/importLegacy.d.ts +62 -0
  80. package/dist/storage/graph/importLegacy.d.ts.map +1 -0
  81. package/dist/storage/graph/memoryGraphJournal.d.ts +11 -0
  82. package/dist/storage/graph/memoryGraphJournal.d.ts.map +1 -0
  83. package/dist/storage/graph/migrateLegacyBriefs.d.ts +32 -0
  84. package/dist/storage/graph/migrateLegacyBriefs.d.ts.map +1 -0
  85. package/dist/storage/graph/projections.d.ts +31 -0
  86. package/dist/storage/graph/projections.d.ts.map +1 -0
  87. package/dist/storage/graph/provider.d.ts +52 -0
  88. package/dist/storage/graph/provider.d.ts.map +1 -0
  89. package/dist/storage/graph/recovery.d.ts +38 -0
  90. package/dist/storage/graph/recovery.d.ts.map +1 -0
  91. package/dist/storage/graph/runRegistry.d.ts +17 -0
  92. package/dist/storage/graph/runRegistry.d.ts.map +1 -0
  93. package/dist/storage/graph/snapshot.d.ts +44 -0
  94. package/dist/storage/graph/snapshot.d.ts.map +1 -0
  95. package/dist/storage/graph/workspaceLease.d.ts +36 -0
  96. package/dist/storage/graph/workspaceLease.d.ts.map +1 -0
  97. package/dist/storage/graph/worktreeRegistry.d.ts +29 -0
  98. package/dist/storage/graph/worktreeRegistry.d.ts.map +1 -0
  99. package/dist/storage/graph/writer.d.ts +20 -0
  100. package/dist/storage/graph/writer.d.ts.map +1 -0
  101. package/dist/telemetry/graphProjection.d.ts +36 -0
  102. package/dist/telemetry/graphProjection.d.ts.map +1 -0
  103. package/dist/telemetry/graphView.d.ts +75 -0
  104. package/dist/telemetry/graphView.d.ts.map +1 -0
  105. package/dist/web/console.d.ts +6 -0
  106. package/dist/web/console.d.ts.map +1 -1
  107. package/dist/web/graphRoute.d.ts +36 -0
  108. package/dist/web/graphRoute.d.ts.map +1 -0
  109. package/dist/web/server.d.ts +21 -2
  110. package/dist/web/server.d.ts.map +1 -1
  111. package/dist/web/start.d.ts +11 -0
  112. package/dist/web/start.d.ts.map +1 -1
  113. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -33,6 +33,50 @@ function eventSessionID(event) {
33
33
  }
34
34
  return str(props.sessionID) ?? str(asRecord(props.part)?.sessionID) ?? str(asRecord(props.info)?.sessionID);
35
35
  }
36
+ function partToFrame(part) {
37
+ if (part.type === "text") {
38
+ const text = str(part.text);
39
+ return text === undefined ? undefined : { kind: "text", text };
40
+ }
41
+ if (part.type === "tool") {
42
+ const tool = str(part.tool) ?? "tool";
43
+ const status = str(asRecord(part.state)?.status) ?? "run";
44
+ return { kind: "tool", text: `⚙ ${tool} · ${status}` };
45
+ }
46
+ return;
47
+ }
48
+ function messagesToFrames(raw, sessionID) {
49
+ if (!Array.isArray(raw)) {
50
+ return [];
51
+ }
52
+ const frames = [];
53
+ for (const entry of raw) {
54
+ const record = asRecord(entry);
55
+ if (record === undefined) {
56
+ continue;
57
+ }
58
+ const info = asRecord(record.info);
59
+ const entrySession = str(info?.sessionID) ?? str(record.sessionID);
60
+ if (entrySession !== undefined && entrySession !== sessionID) {
61
+ continue;
62
+ }
63
+ const parts = record.parts;
64
+ if (!Array.isArray(parts)) {
65
+ continue;
66
+ }
67
+ for (const rawPart of parts) {
68
+ const part = asRecord(rawPart);
69
+ if (part === undefined) {
70
+ continue;
71
+ }
72
+ const frame = partToFrame(part);
73
+ if (frame !== undefined) {
74
+ frames.push(frame);
75
+ }
76
+ }
77
+ }
78
+ return frames;
79
+ }
36
80
  function eventToSessionFrame(event, sessionID) {
37
81
  if (eventSessionID(event) !== sessionID) {
38
82
  return;
@@ -41,19 +85,7 @@ function eventToSessionFrame(event, sessionID) {
41
85
  switch (event.type) {
42
86
  case "message.part.updated": {
43
87
  const part = asRecord(props.part);
44
- if (part === undefined) {
45
- return;
46
- }
47
- if (part.type === "text") {
48
- const text = str(part.text);
49
- return text === undefined ? undefined : { kind: "text", text };
50
- }
51
- if (part.type === "tool") {
52
- const tool = str(part.tool) ?? "tool";
53
- const status = str(asRecord(part.state)?.status) ?? "run";
54
- return { kind: "tool", text: `⚙ ${tool} · ${status}` };
55
- }
56
- return;
88
+ return part === undefined ? undefined : partToFrame(part);
57
89
  }
58
90
  case "session.idle":
59
91
  return { kind: "status", text: "● sesión lista" };
@@ -163,6 +195,16 @@ function createConsoleClient(deps) {
163
195
  async respondPermission(sessionID, permissionID, response) {
164
196
  await post(`/session/${encodeURIComponent(sessionID)}/permissions/${encodeURIComponent(permissionID)}`, { response });
165
197
  },
198
+ async listMessages(sessionID) {
199
+ const res = await doFetch(`${base}/session/${encodeURIComponent(sessionID)}/message`, { headers: { accept: "application/json", ...authHeaders } });
200
+ let raw;
201
+ try {
202
+ raw = await res.json();
203
+ } catch {
204
+ return [];
205
+ }
206
+ return messagesToFrames(raw, sessionID);
207
+ },
166
208
  async streamFrames(sessionID, onFrame, signal) {
167
209
  const res = await doFetch(`${base}/global/event`, {
168
210
  headers: { accept: "text/event-stream", ...authHeaders },
@@ -628,6 +670,14 @@ var PrivacyModeSchema = z3.enum([
628
670
  "off"
629
671
  ]);
630
672
  var BaselineModeSchema = z3.enum(["auto", "pinned"]);
673
+ var GraphModeSchema = z3.enum(["off", "shadow", "active"]);
674
+ var GraphConfigSchema = z3.object({
675
+ mode: GraphModeSchema.default("off"),
676
+ killSwitch: z3.boolean().default(false),
677
+ worktrees: z3.object({
678
+ enabled: z3.boolean().default(false)
679
+ }).default({ enabled: false })
680
+ });
631
681
  var ConsoleHostSchema = z3.enum(["127.0.0.1", "localhost"]);
632
682
  var ConsoleConfigSchema = z3.object({
633
683
  host: ConsoleHostSchema.default("127.0.0.1"),
@@ -640,7 +690,10 @@ var ConsoleConfigSchema = z3.object({
640
690
  terminal: z3.object({
641
691
  enabled: z3.boolean().default(true),
642
692
  pty: z3.boolean().optional()
643
- }).default({ enabled: true })
693
+ }).default({ enabled: true }),
694
+ graphView: z3.object({
695
+ enabled: z3.boolean().default(false)
696
+ }).optional()
644
697
  }).default({
645
698
  host: "127.0.0.1",
646
699
  port: 4599,
@@ -769,7 +822,8 @@ var OpenTeamConfigObjectSchema = z3.object({
769
822
  privacyMode: PrivacyModeSchema.default("forceLocalOnSensitive"),
770
823
  console: ConsoleConfigSchema,
771
824
  storage: StorageConfigSchema,
772
- memory: MemoryConfigSchema.optional()
825
+ memory: MemoryConfigSchema.optional(),
826
+ graph: GraphConfigSchema.optional()
773
827
  });
774
828
  var OpenTeamConfigSchema = OpenTeamConfigObjectSchema;
775
829
  function resolveMemorySemantic(config) {
@@ -1293,6 +1347,51 @@ code{font-family:ui-monospace,Consolas,monospace;background:#0b0d11;padding:1px
1293
1347
  .pty form.ptyinput button,.pty .pty-start{background:var(--frontier);color:#1a1200;border:none;border-radius:8px;padding:8px 16px;cursor:pointer;font:inherit;font-weight:600;margin-top:8px}
1294
1348
  .pty .pty-start:disabled{opacity:.5;cursor:default}
1295
1349
  `;
1350
+ var GRAPH_STYLE = `
1351
+ .graph-wrap{overflow:auto;background:#0b0d11;border:1px solid var(--line);border-radius:8px;padding:8px}
1352
+ svg.graph{display:block}
1353
+ .gedge{stroke:#3a4250;stroke-width:1.5;marker-end:url(#garrow)}
1354
+ .gnode rect{fill:var(--panel);stroke:var(--line);stroke-width:1.5;rx:8}
1355
+ .gnode .gname{fill:var(--fg);font:600 12px ui-monospace,Consolas,monospace}
1356
+ .gnode .grole{fill:var(--muted);font-size:11px}
1357
+ .gnode .gstate{font-size:10px;text-transform:uppercase;letter-spacing:.05em}
1358
+ .gnode.state-pending rect{stroke:#6e7681}
1359
+ .gnode.state-pending .gstate{fill:#6e7681}
1360
+ .gnode.state-ready rect{stroke:var(--local)}
1361
+ .gnode.state-ready .gstate{fill:var(--local)}
1362
+ .gnode.state-active rect{stroke:var(--frontier)}
1363
+ .gnode.state-active .gstate{fill:var(--frontier)}
1364
+ .gnode.state-succeeded rect{stroke:var(--good)}
1365
+ .gnode.state-succeeded .gstate{fill:var(--good)}
1366
+ .gnode.state-failed rect{stroke:#f85149}
1367
+ .gnode.state-failed .gstate{fill:#f85149}
1368
+ .gnode.state-cancelled rect{stroke:#484f58}
1369
+ .gnode.state-cancelled .gstate{fill:#484f58}
1370
+ .gbadge{font-size:10px;font-family:ui-monospace,Consolas,monospace}
1371
+ .gbadge.badge-retry{fill:var(--frontier)}
1372
+ .gbadge.badge-error{fill:#f85149}
1373
+ .gbadge.badge-cancelled{fill:#484f58}
1374
+ .gbadge.badge-verdict.verdict-approved{fill:var(--good)}
1375
+ .gbadge.badge-verdict.verdict-rejected{fill:#f85149}
1376
+ .gbadge.badge-verdict.verdict-inconclusive{fill:var(--frontier)}
1377
+ .gbadge.badge-lineage{fill:var(--local)}
1378
+ .gbadge.badge-artifact{fill:var(--muted)}
1379
+ .gnode[data-session]{cursor:pointer}
1380
+ .gnode[data-session]:hover rect{stroke-width:2.5}
1381
+ .graph-health{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 12px}
1382
+ .gchip{font-size:11px;padding:2px 10px;border-radius:10px;border:1px solid var(--line);color:var(--muted)}
1383
+ .gchip.on{font-weight:600}
1384
+ .gchip.gh-recovery.on,.gchip.gh-unknown.on{color:var(--frontier);border-color:var(--frontier)}
1385
+ .gchip.gh-conflict.on,.gchip.gh-kill.on{color:#f85149;border-color:#f85149}
1386
+ .graph-drill{margin-top:14px;border-top:1px solid var(--line);padding-top:12px}
1387
+ .graph-drill h3{font-size:13px;margin:0 0 8px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
1388
+ .graph-drill .drilllog{background:#0b0d11;border:1px solid var(--line);border-radius:8px;padding:10px;max-height:280px;overflow:auto;font-family:ui-monospace,Consolas,monospace;font-size:12px;white-space:pre-wrap}
1389
+ .graph-drill .drilllog .tl{padding:1px 0}
1390
+ .graph-drill .drilllog .tl.tool{color:var(--frontier)}
1391
+ .graph-drill .drilllog .tl.status{color:var(--muted)}
1392
+ .graph-drill .drilllog .tl.permission{color:var(--frontier);font-weight:600}
1393
+ .graph-drill .drilllog .tl.error{color:#f85149}
1394
+ `;
1296
1395
  var CONSOLE_CLIENT_JS = `
1297
1396
  (function(){
1298
1397
  var authTok='';
@@ -1418,6 +1517,47 @@ var CONSOLE_CLIENT_JS = `
1418
1517
  ');startBtn.disabled=false}
1419
1518
  }
1420
1519
  }
1520
+ function wireGraphDrill(){
1521
+ var root=document.documentElement;
1522
+ if(root.getAttribute('data-drilldown')!=='1'){return}
1523
+ var token=root.getAttribute('data-token');
1524
+ if(!token){return}
1525
+ var drill=document.querySelector('.graph-drill');
1526
+ if(!drill){return}
1527
+ var gview=drill.closest('.view');
1528
+ if(!gview){return}
1529
+ var log=drill.querySelector('.drilllog');
1530
+ var title=drill.querySelector('.drill-title');
1531
+ if(!log){return}
1532
+ var es=null;
1533
+ function append(cls,text){
1534
+ var line=document.createElement('div');
1535
+ line.className='tl '+cls;line.textContent=text;
1536
+ log.appendChild(line);log.scrollTop=log.scrollHeight;
1537
+ }
1538
+ function open(id){
1539
+ if(es){try{es.close()}catch(_){}}
1540
+ log.textContent='';drill.hidden=false;
1541
+ if(title){title.textContent='Conversación · '+id}
1542
+ var tk='token='+encodeURIComponent(token);
1543
+ var base='/console/session/'+encodeURIComponent(id);
1544
+ fetch(base+'/messages?'+tk).then(function(r){return r.ok?r.json():Promise.reject(r.status)})
1545
+ .then(function(d){(d&&d.frames||[]).forEach(function(f){append(f.kind,f.text)})})
1546
+ .catch(function(s){append('error','✖ no se pudo cargar la conversación ('+s+')')});
1547
+ if('EventSource' in window){
1548
+ try{
1549
+ es=new EventSource(base+'/stream?'+tk);
1550
+ es.addEventListener('frame',function(e){try{var f=JSON.parse(e.data);append(f.kind,f.text)}catch(_){}});
1551
+ es.onerror=function(){/* reconecta solo */};
1552
+ }catch(_){/* ignore */}
1553
+ }
1554
+ }
1555
+ gview.addEventListener('click',function(e){
1556
+ var t=e.target;
1557
+ while(t&&t!==gview&&!(t.getAttribute&&t.getAttribute('data-session'))){t=t.parentNode}
1558
+ if(t&&t.getAttribute&&t.getAttribute('data-session')){open(t.getAttribute('data-session'))}
1559
+ });
1560
+ }
1421
1561
  var bar=document.getElementById('tabbar');
1422
1562
  if(bar){
1423
1563
  bar.addEventListener('click',function(e){
@@ -1429,6 +1569,7 @@ var CONSOLE_CLIENT_JS = `
1429
1569
  var saved='all';
1430
1570
  try{saved=sessionStorage.getItem('openteam.tab')||'all'}catch(e){}
1431
1571
  if(!selectTab(saved)){selectTab('all')}
1572
+ wireGraphDrill();
1432
1573
  })();
1433
1574
  `;
1434
1575
 
@@ -1678,14 +1819,15 @@ var STATUS_LABELS = {
1678
1819
  function statusBadge(status) {
1679
1820
  return `<span class="badge status-${status}">${STATUS_LABELS[status]}</span>`;
1680
1821
  }
1681
- function tabBar(sessions) {
1822
+ function tabBar(sessions, graphEnabled) {
1682
1823
  const allTab = '<button type="button" class="tab" data-tab="all">Todas</button>';
1824
+ const graphTab = graphEnabled ? '<button type="button" class="tab" data-tab="graph">Grafo</button>' : "";
1683
1825
  const tabs = sessions.map((tab) => {
1684
1826
  const id = escapeHtml(tab.sessionID);
1685
1827
  const short = escapeHtml(shortSessionID(tab.sessionID));
1686
1828
  return `<button type="button" class="tab" data-tab="${id}"><span class="dot status-${tab.status}"></span>${short}</button>`;
1687
1829
  }).join("");
1688
- return `<nav class="tabs" id="tabbar">${allTab}${tabs}</nav>`;
1830
+ return `<nav class="tabs" id="tabbar">${allTab}${graphTab}${tabs}</nav>`;
1689
1831
  }
1690
1832
  function shortSessionID(sessionID) {
1691
1833
  return sessionID.length <= 12 ? sessionID : `${sessionID.slice(0, 12)}…`;
@@ -1747,10 +1889,182 @@ function sessionView(tab, terminalEnabled, ptyEnabled) {
1747
1889
  ].join("");
1748
1890
  return `<div class="view" data-view="${escapeHtml(tab.sessionID)}" hidden>${body}</div>`;
1749
1891
  }
1892
+ var GRAPH_COL_W = 220;
1893
+ var GRAPH_ROW_H = 96;
1894
+ var GRAPH_NODE_W = 180;
1895
+ var GRAPH_NODE_H = 64;
1896
+ var GRAPH_PAD = 20;
1897
+ function computeLayers(projection) {
1898
+ const layer = new Map;
1899
+ for (const node of projection.nodes) {
1900
+ layer.set(node.nodeID, 0);
1901
+ }
1902
+ for (let pass = 0;pass < projection.nodes.length; pass += 1) {
1903
+ let changed = false;
1904
+ for (const edge of projection.edges) {
1905
+ const from = layer.get(edge.from);
1906
+ const to = layer.get(edge.to);
1907
+ if (from === undefined || to === undefined) {
1908
+ continue;
1909
+ }
1910
+ if (from + 1 > to) {
1911
+ layer.set(edge.to, from + 1);
1912
+ changed = true;
1913
+ }
1914
+ }
1915
+ if (!changed) {
1916
+ break;
1917
+ }
1918
+ }
1919
+ return layer;
1920
+ }
1921
+ function layoutNodes(projection) {
1922
+ const layers = computeLayers(projection);
1923
+ const byLayer = [];
1924
+ for (const node of projection.nodes) {
1925
+ const l = layers.get(node.nodeID) ?? 0;
1926
+ let bucket = byLayer[l];
1927
+ if (bucket === undefined) {
1928
+ bucket = [];
1929
+ byLayer[l] = bucket;
1930
+ }
1931
+ bucket.push(node.nodeID);
1932
+ }
1933
+ const pos = new Map;
1934
+ let maxLayer = 0;
1935
+ let maxRows = 0;
1936
+ byLayer.forEach((ids, l) => {
1937
+ if (l > maxLayer) {
1938
+ maxLayer = l;
1939
+ }
1940
+ if (ids.length > maxRows) {
1941
+ maxRows = ids.length;
1942
+ }
1943
+ ids.forEach((id, row) => {
1944
+ pos.set(id, {
1945
+ x: GRAPH_PAD + l * GRAPH_COL_W,
1946
+ y: GRAPH_PAD + row * GRAPH_ROW_H
1947
+ });
1948
+ });
1949
+ });
1950
+ const width = GRAPH_PAD * 2 + maxLayer * GRAPH_COL_W + GRAPH_NODE_W;
1951
+ const height = GRAPH_PAD * 2 + Math.max(0, maxRows - 1) * GRAPH_ROW_H + GRAPH_NODE_H;
1952
+ return { pos, width, height };
1953
+ }
1954
+ function nodeBadges(node) {
1955
+ const badges = [];
1956
+ if (node.attempts > 1) {
1957
+ badges.push({ cls: "badge-retry", text: `⟳${node.attempts}` });
1958
+ }
1959
+ if (node.lastErrorClass !== undefined) {
1960
+ badges.push({
1961
+ cls: `badge-error err-${node.lastErrorClass}`,
1962
+ text: `⚠${node.lastErrorClass}`
1963
+ });
1964
+ }
1965
+ if (node.state === "cancelled") {
1966
+ badges.push({ cls: "badge-cancelled", text: "⊘" });
1967
+ }
1968
+ if (node.review !== undefined) {
1969
+ const symbol = node.review.verdict === "approved" ? "✓" : node.review.verdict === "rejected" ? "✗" : "?";
1970
+ badges.push({
1971
+ cls: `badge-verdict verdict-${node.review.verdict}`,
1972
+ text: node.review.reassigned ? `${symbol}↔` : symbol
1973
+ });
1974
+ }
1975
+ if (node.revisionOf !== undefined) {
1976
+ badges.push({
1977
+ cls: "badge-lineage",
1978
+ text: `↺${escapeHtml(node.revisionOf)}`
1979
+ });
1980
+ }
1981
+ if (node.artifact !== undefined) {
1982
+ badges.push({ cls: "badge-artifact", text: "⧉" });
1983
+ }
1984
+ return badges;
1985
+ }
1986
+ function nodeSvg(node, at) {
1987
+ const badges = nodeBadges(node).map((badge, index) => `<text class="gbadge ${badge.cls}" x="${10 + index * 34}" y="58">${badge.text}</text>`).join("");
1988
+ const sessionAttr = node.sessionRef === undefined ? "" : ` data-session="${escapeHtml(node.sessionRef)}"`;
1989
+ return [
1990
+ `<g class="gnode state-${node.state}" data-node="${escapeHtml(node.nodeID)}" data-state="${node.state}" data-attempts="${node.attempts}"${sessionAttr} transform="translate(${at.x},${at.y})">`,
1991
+ `<rect width="${GRAPH_NODE_W}" height="${GRAPH_NODE_H}"></rect>`,
1992
+ `<text class="gname" x="10" y="20">${escapeHtml(node.nodeID)}</text>`,
1993
+ `<text class="grole" x="10" y="36">${escapeHtml(node.role)}</text>`,
1994
+ `<text class="gstate" x="10" y="50">${node.state}</text>`,
1995
+ badges,
1996
+ "</g>"
1997
+ ].join("");
1998
+ }
1999
+ function edgeSvg(pos, from, to) {
2000
+ const a = pos.get(from);
2001
+ const b = pos.get(to);
2002
+ if (a === undefined || b === undefined) {
2003
+ return "";
2004
+ }
2005
+ const x1 = a.x + GRAPH_NODE_W;
2006
+ const y1 = a.y + GRAPH_NODE_H / 2;
2007
+ const x2 = b.x;
2008
+ const y2 = b.y + GRAPH_NODE_H / 2;
2009
+ return `<line class="gedge" x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" data-from="${escapeHtml(from)}" data-to="${escapeHtml(to)}"></line>`;
2010
+ }
2011
+ function renderGraphView(projection) {
2012
+ if (projection === null) {
2013
+ return '<p class="muted">Sin run de grafo activo.</p>';
2014
+ }
2015
+ if (projection.nodes.length === 0) {
2016
+ return '<p class="muted">El run no tiene nodos.</p>';
2017
+ }
2018
+ const { pos, width, height } = layoutNodes(projection);
2019
+ const edges = projection.edges.map((edge) => edgeSvg(pos, edge.from, edge.to)).join("");
2020
+ const nodes = projection.nodes.map((node) => {
2021
+ const at = pos.get(node.nodeID);
2022
+ return at === undefined ? "" : nodeSvg(node, at);
2023
+ }).join("");
2024
+ return [
2025
+ `<div class="graph-wrap"><svg class="graph" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}" role="img" aria-label="Grafo de agentes">`,
2026
+ '<defs><marker id="garrow" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0 L8 4 L0 8 z" fill="#3a4250"></path></marker></defs>',
2027
+ edges,
2028
+ nodes,
2029
+ "</svg></div>"
2030
+ ].join("");
2031
+ }
2032
+ function healthBanner(health) {
2033
+ const chip = (cls, on, label) => `<span class="gchip ${cls} ${on ? "on" : "off"}">${label}</span>`;
2034
+ return [
2035
+ '<div class="graph-health" role="status">',
2036
+ chip("gh-recovery", health.recoveryRequired, health.recoveryRequired ? "recuperación requerida" : "sin recuperación"),
2037
+ chip("gh-unknown", health.unknownEffects > 0, `efectos desconocidos: ${health.unknownEffects}`),
2038
+ chip("gh-conflict", health.conflicts > 0, `conflictos lease/worktree: ${health.conflicts}`),
2039
+ chip("gh-kill", health.killSwitch, health.killSwitch ? "kill switch activo" : "kill switch inactivo"),
2040
+ "</div>"
2041
+ ].join("");
2042
+ }
2043
+ function drillPanel() {
2044
+ return [
2045
+ '<aside class="graph-drill" hidden>',
2046
+ '<h3 class="drill-title">Conversación</h3>',
2047
+ '<div class="drilllog" aria-live="polite"></div>',
2048
+ "</aside>"
2049
+ ].join("");
2050
+ }
2051
+ function graphViewPanel(projection, drilldownEnabled) {
2052
+ return [
2053
+ '<div class="view" data-view="graph" hidden>',
2054
+ '<section class="panel" id="panel-graph">',
2055
+ "<h2>Grafo de agentes</h2>",
2056
+ projection === null ? "" : healthBanner(projection.health),
2057
+ renderGraphView(projection),
2058
+ drilldownEnabled ? drillPanel() : "",
2059
+ "</section>",
2060
+ "</div>"
2061
+ ].join("");
2062
+ }
1750
2063
  function renderConsoleHtml(state, options = {}) {
1751
2064
  const refreshMs = options.refreshMs ?? 2000;
1752
2065
  const terminalEnabled = options.terminalEnabled === true;
1753
2066
  const ptyEnabled = options.ptyEnabled === true;
2067
+ const graphEnabled = options.graphView !== undefined;
1754
2068
  const allBody = [
1755
2069
  summaryPanel(state.cost),
1756
2070
  totalsPanel(state.totals),
@@ -1764,10 +2078,12 @@ function renderConsoleHtml(state, options = {}) {
1764
2078
  activityPanel(state.activity)
1765
2079
  ].join("");
1766
2080
  const allView = `<div class="view" data-view="all">${allBody}</div>`;
2081
+ const graphDrilldown = graphEnabled && options.drilldownEnabled === true;
2082
+ const graphView = graphEnabled ? graphViewPanel(options.graphView?.projection ?? null, graphDrilldown) : "";
1767
2083
  const sessionViews = state.sessionsDetail.map((tab) => sessionView(tab, terminalEnabled, ptyEnabled)).join("");
1768
- const body = allView + sessionViews;
2084
+ const body = allView + graphView + sessionViews;
1769
2085
  const session = state.session.id === undefined ? "" : ` · sesión ${escapeHtml(state.session.id)}`;
1770
- const tokenAttr = terminalEnabled && options.consoleToken !== undefined ? ` data-token="${escapeHtml(options.consoleToken)}" data-terminal="1"${ptyEnabled ? ' data-pty="1"' : ""}` : "";
2086
+ const tokenAttr = (terminalEnabled || graphDrilldown) && options.consoleToken !== undefined ? ` data-token="${escapeHtml(options.consoleToken)}"${terminalEnabled ? ' data-terminal="1"' : ""}${ptyEnabled ? ' data-pty="1"' : ""}${graphDrilldown ? ' data-drilldown="1"' : ""}` : "";
1771
2087
  return [
1772
2088
  "<!doctype html>",
1773
2089
  `<html lang="es" data-generated="${escapeHtml(state.generatedAt)}" data-refresh="${refreshMs}"${tokenAttr}>`,
@@ -1775,14 +2091,14 @@ function renderConsoleHtml(state, options = {}) {
1775
2091
  '<meta charset="utf-8">',
1776
2092
  '<meta name="viewport" content="width=device-width,initial-scale=1">',
1777
2093
  "<title>openteam console</title>",
1778
- `<style>${CONSOLE_STYLE}</style>`,
2094
+ `<style>${CONSOLE_STYLE}${graphEnabled ? GRAPH_STYLE : ""}</style>`,
1779
2095
  "</head>",
1780
2096
  "<body>",
1781
2097
  "<header>",
1782
2098
  "<h1>openteam console</h1>",
1783
2099
  `<span class="meta">actualizado ${escapeHtml(state.generatedAt)}${session}</span>`,
1784
2100
  "</header>",
1785
- tabBar(state.sessionsDetail),
2101
+ tabBar(state.sessionsDetail, graphEnabled),
1786
2102
  `<main>${body}</main>`,
1787
2103
  `<script>${CONSOLE_CLIENT_JS}</script>`,
1788
2104
  "</body>",
@@ -2114,7 +2430,7 @@ async function readJsonBody(request, limit) {
2114
2430
  });
2115
2431
  }
2116
2432
  function parseConsolePath(path) {
2117
- const match = /^\/console\/session\/([^/]+)\/(stream|input|permission|pty)$/.exec(path);
2433
+ const match = /^\/console\/session\/([^/]+)\/(stream|input|permission|pty|messages)$/.exec(path);
2118
2434
  if (match === null) {
2119
2435
  return;
2120
2436
  }
@@ -2144,6 +2460,15 @@ data: {}
2144
2460
  response.end();
2145
2461
  }
2146
2462
  }
2463
+ async function handleMessages(_request, response, sessionID, deps) {
2464
+ const endpoint = await deps.resolveEndpoint(sessionID);
2465
+ if (endpoint === undefined) {
2466
+ response.writeHead(404, JSON_HEADERS).end(JSON.stringify({ error: "no endpoint" }));
2467
+ return;
2468
+ }
2469
+ const frames = await deps.createClient(endpoint).listMessages(sessionID);
2470
+ response.writeHead(200, JSON_HEADERS).end(JSON.stringify({ frames }));
2471
+ }
2147
2472
  async function handleInput(request, response, sessionID, deps) {
2148
2473
  const endpoint = await deps.resolveEndpoint(sessionID);
2149
2474
  if (endpoint === undefined) {
@@ -2211,7 +2536,9 @@ async function handleConsoleRoute(request, response, url, deps) {
2211
2536
  response.writeHead(404, JSON_HEADERS).end(JSON.stringify({ error: "not found" }));
2212
2537
  return true;
2213
2538
  }
2214
- if (!deps.terminalEnabled) {
2539
+ const readOnlyAction = route.action === "stream" || route.action === "messages";
2540
+ const channelEnabled = deps.terminalEnabled || readOnlyAction && deps.drilldownEnabled === true;
2541
+ if (!channelEnabled) {
2215
2542
  response.writeHead(403, JSON_HEADERS).end(JSON.stringify({ error: "terminal disabled" }));
2216
2543
  return true;
2217
2544
  }
@@ -2223,6 +2550,10 @@ async function handleConsoleRoute(request, response, url, deps) {
2223
2550
  await handleStream(request, response, route.sessionID, deps);
2224
2551
  return true;
2225
2552
  }
2553
+ if (route.action === "messages" && request.method === "GET") {
2554
+ await handleMessages(request, response, route.sessionID, deps);
2555
+ return true;
2556
+ }
2226
2557
  if (route.action === "input" && request.method === "POST") {
2227
2558
  await handleInput(request, response, route.sessionID, deps);
2228
2559
  return true;
@@ -2239,10 +2570,15 @@ async function handleConsoleRoute(request, response, url, deps) {
2239
2570
  return true;
2240
2571
  }
2241
2572
 
2242
- // src/web/storageRoute.ts
2573
+ // src/web/graphRoute.ts
2243
2574
  var JSON_HEADERS2 = { "content-type": "application/json" };
2244
- var READ_OPS = new Set(["read", "exists", "list", "isDirectory", "stat"]);
2245
- var WRITE_OPS = new Set(["write", "append", "mkdir", "delete"]);
2575
+ var GRAPH_SSE_EVENT = "graph";
2576
+ function graphSseMessage(json) {
2577
+ return `event: ${GRAPH_SSE_EVENT}
2578
+ data: ${json}
2579
+
2580
+ `;
2581
+ }
2246
2582
  function bearerFromHeader2(request) {
2247
2583
  const header = request.headers.authorization;
2248
2584
  if (typeof header !== "string" || !header.startsWith("Bearer ")) {
@@ -2255,6 +2591,39 @@ function authorized2(request, query, token) {
2255
2591
  const provided = tokenFromQuery(query) ?? bearerFromHeader2(request);
2256
2592
  return provided !== undefined && safeEqualToken(provided, token);
2257
2593
  }
2594
+ async function handleGraphRoute(request, response, url, deps) {
2595
+ if (url.pathname !== "/api/graph") {
2596
+ return false;
2597
+ }
2598
+ if (request.method !== "GET") {
2599
+ response.writeHead(405, JSON_HEADERS2).end(JSON.stringify({ error: "method not allowed" }));
2600
+ return true;
2601
+ }
2602
+ if (deps.remoteToken !== undefined && !authorized2(request, url.searchParams, deps.remoteToken)) {
2603
+ response.writeHead(401, JSON_HEADERS2).end(JSON.stringify({ error: "unauthorized" }));
2604
+ return true;
2605
+ }
2606
+ const view = await deps.readView();
2607
+ response.writeHead(200, JSON_HEADERS2).end(JSON.stringify(view));
2608
+ return true;
2609
+ }
2610
+
2611
+ // src/web/storageRoute.ts
2612
+ var JSON_HEADERS3 = { "content-type": "application/json" };
2613
+ var READ_OPS = new Set(["read", "exists", "list", "isDirectory", "stat"]);
2614
+ var WRITE_OPS = new Set(["write", "append", "mkdir", "delete"]);
2615
+ function bearerFromHeader3(request) {
2616
+ const header = request.headers.authorization;
2617
+ if (typeof header !== "string" || !header.startsWith("Bearer ")) {
2618
+ return;
2619
+ }
2620
+ const value = header.slice("Bearer ".length).trim();
2621
+ return value.length === 0 ? undefined : value;
2622
+ }
2623
+ function authorized3(request, query, token) {
2624
+ const provided = tokenFromQuery(query) ?? bearerFromHeader3(request);
2625
+ return provided !== undefined && safeEqualToken(provided, token);
2626
+ }
2258
2627
  function pathAllowed(path, allowPrefixes) {
2259
2628
  if (allowPrefixes === undefined) {
2260
2629
  return true;
@@ -2294,7 +2663,7 @@ async function readJsonBody2(request, limit) {
2294
2663
  });
2295
2664
  }
2296
2665
  function sendJson(response, status, body) {
2297
- response.writeHead(status, JSON_HEADERS2).end(JSON.stringify(body));
2666
+ response.writeHead(status, JSON_HEADERS3).end(JSON.stringify(body));
2298
2667
  }
2299
2668
  async function handleRead(response, op, path, storage) {
2300
2669
  if (op === "read") {
@@ -2368,7 +2737,7 @@ async function handleStorageRoute(request, response, url, deps) {
2368
2737
  sendJson(response, 404, { error: "not found" });
2369
2738
  return true;
2370
2739
  }
2371
- if (!authorized2(request, url.searchParams, deps.token)) {
2740
+ if (!authorized3(request, url.searchParams, deps.token)) {
2372
2741
  sendJson(response, 401, { error: "unauthorized" });
2373
2742
  return true;
2374
2743
  }
@@ -2409,12 +2778,12 @@ var SSE_HEADERS2 = {
2409
2778
  "cache-control": "no-cache",
2410
2779
  connection: "keep-alive"
2411
2780
  };
2412
- var JSON_HEADERS3 = { "content-type": "application/json" };
2781
+ var JSON_HEADERS4 = { "content-type": "application/json" };
2413
2782
  var HTML_HEADERS = { "content-type": "text/html; charset=utf-8" };
2414
2783
  function isAddressInUse(error) {
2415
2784
  return typeof error === "object" && error !== null && "code" in error && error.code === "EADDRINUSE";
2416
2785
  }
2417
- function bearerFromHeader3(request) {
2786
+ function bearerFromHeader4(request) {
2418
2787
  const header = request.headers.authorization;
2419
2788
  if (typeof header !== "string" || !header.startsWith("Bearer ")) {
2420
2789
  return;
@@ -2423,16 +2792,19 @@ function bearerFromHeader3(request) {
2423
2792
  return value.length === 0 ? undefined : value;
2424
2793
  }
2425
2794
  function readAuthorized(request, url, token) {
2426
- const provided = tokenFromQuery(url.searchParams) ?? bearerFromHeader3(request);
2795
+ const provided = tokenFromQuery(url.searchParams) ?? bearerFromHeader4(request);
2427
2796
  return provided !== undefined && safeEqualToken(provided, token);
2428
2797
  }
2429
2798
  async function renderState(deps) {
2799
+ const graphView = deps.graphApi === undefined ? undefined : { projection: await deps.graphApi.readView() };
2430
2800
  return renderConsoleHtml(buildConsoleState(await deps.readSnapshot()), {
2431
2801
  refreshMs: deps.config.refreshMs,
2802
+ ...graphView !== undefined ? { graphView } : {},
2432
2803
  ...deps.console !== undefined ? {
2433
2804
  consoleToken: deps.console.token,
2434
2805
  terminalEnabled: deps.console.terminalEnabled,
2435
- ...deps.console.ptyEnabled !== undefined ? { ptyEnabled: deps.console.ptyEnabled } : {}
2806
+ ...deps.console.ptyEnabled !== undefined ? { ptyEnabled: deps.console.ptyEnabled } : {},
2807
+ ...deps.console.drilldownEnabled !== undefined ? { drilldownEnabled: deps.console.drilldownEnabled } : {}
2436
2808
  } : {}
2437
2809
  });
2438
2810
  }
@@ -2478,13 +2850,21 @@ async function createConsoleServer(deps) {
2478
2850
  resolveEndpoint,
2479
2851
  createClient: deps.console.createClient,
2480
2852
  ...deps.console.ptyEnabled !== undefined ? { ptyEnabled: deps.console.ptyEnabled } : {},
2853
+ ...deps.console.drilldownEnabled !== undefined ? { drilldownEnabled: deps.console.drilldownEnabled } : {},
2481
2854
  ...deps.console.createPtyClient !== undefined ? { createPtyClient: deps.console.createPtyClient } : {}
2482
2855
  };
2856
+ const graphDeps = deps.graphApi === undefined ? undefined : {
2857
+ readView: deps.graphApi.readView,
2858
+ ...deps.storageApi?.token !== undefined ? { remoteToken: deps.storageApi.token } : {}
2859
+ };
2483
2860
  const handle = async (request, response) => {
2484
2861
  const url2 = new URL(request.url ?? "/", "http://localhost");
2485
2862
  if (consoleDeps !== undefined && await handleConsoleRoute(request, response, url2, consoleDeps)) {
2486
2863
  return;
2487
2864
  }
2865
+ if (graphDeps !== undefined && await handleGraphRoute(request, response, url2, graphDeps)) {
2866
+ return;
2867
+ }
2488
2868
  if (deps.storageApi !== undefined && await handleStorageRoute(request, response, url2, deps.storageApi)) {
2489
2869
  return;
2490
2870
  }
@@ -2495,7 +2875,7 @@ async function createConsoleServer(deps) {
2495
2875
  const path = url2.pathname;
2496
2876
  const remoteToken = deps.storageApi?.token;
2497
2877
  if (remoteToken !== undefined && (path === "/" || path === "/index.html" || path === "/api/state" || path === "/events") && !readAuthorized(request, url2, remoteToken)) {
2498
- response.writeHead(401, JSON_HEADERS3).end(JSON.stringify({ error: "unauthorized" }));
2878
+ response.writeHead(401, JSON_HEADERS4).end(JSON.stringify({ error: "unauthorized" }));
2499
2879
  return;
2500
2880
  }
2501
2881
  if (path === "/" || path === "/index.html") {
@@ -2503,11 +2883,11 @@ async function createConsoleServer(deps) {
2503
2883
  return;
2504
2884
  }
2505
2885
  if (path === "/api/state") {
2506
- response.writeHead(200, JSON_HEADERS3).end(await stateJson(deps));
2886
+ response.writeHead(200, JSON_HEADERS4).end(await stateJson(deps));
2507
2887
  return;
2508
2888
  }
2509
2889
  if (path === "/healthz") {
2510
- response.writeHead(200, JSON_HEADERS3).end(JSON.stringify({ ok: true }));
2890
+ response.writeHead(200, JSON_HEADERS4).end(JSON.stringify({ ok: true }));
2511
2891
  return;
2512
2892
  }
2513
2893
  if (path === "/favicon.ico") {
@@ -2517,6 +2897,9 @@ async function createConsoleServer(deps) {
2517
2897
  if (path === "/events") {
2518
2898
  response.writeHead(200, SSE_HEADERS2);
2519
2899
  response.write(sseMessage(await stateJson(deps)));
2900
+ if (deps.graphApi !== undefined) {
2901
+ response.write(graphSseMessage(JSON.stringify(await deps.graphApi.readView())));
2902
+ }
2520
2903
  clients.add(response);
2521
2904
  request.on("close", () => {
2522
2905
  clients.delete(response);
@@ -2557,8 +2940,12 @@ async function createConsoleServer(deps) {
2557
2940
  return;
2558
2941
  }
2559
2942
  const message = sseMessage(await stateJson(deps));
2943
+ const graphMessage = deps.graphApi !== undefined ? graphSseMessage(JSON.stringify(await deps.graphApi.readView())) : undefined;
2560
2944
  for (const response of clients) {
2561
2945
  response.write(message);
2946
+ if (graphMessage !== undefined) {
2947
+ response.write(graphMessage);
2948
+ }
2562
2949
  }
2563
2950
  };
2564
2951
  const close = () => {
@@ -2889,12 +3276,14 @@ async function createConsoleRuntime(deps) {
2889
3276
  ...deps.session !== undefined ? { session: deps.session } : {}
2890
3277
  });
2891
3278
  const createServer = deps.serve ?? createConsoleServer;
3279
+ const graphApi = deps.config.graphView?.enabled === true && deps.graphView !== undefined ? { readView: deps.graphView.readView } : undefined;
2892
3280
  const server = await createServer({
2893
3281
  readSnapshot,
2894
3282
  config: deps.config,
2895
3283
  ...deps.log !== undefined ? { log: deps.log } : {},
2896
3284
  ...deps.console !== undefined ? { console: deps.console } : {},
2897
- ...deps.storageApi !== undefined ? { storageApi: deps.storageApi } : {}
3285
+ ...deps.storageApi !== undefined ? { storageApi: deps.storageApi } : {},
3286
+ ...graphApi !== undefined ? { graphApi } : {}
2898
3287
  });
2899
3288
  const watchFn = deps.watch ?? ((path, listener) => fsWatch(path, { persistent: false }, listener));
2900
3289
  const watcher = watchSources([deps.sessionsDir, decisionsPath, backlogPath, deps.agentDir], () => {
@@ -5123,9 +5512,40 @@ async function runSetup(deps) {
5123
5512
  }
5124
5513
  }
5125
5514
 
5515
+ // src/config/migrations.ts
5516
+ var CURRENT_CONFIG_VERSION = 1;
5517
+
5518
+ class ConfigMigrationError extends Error {
5519
+ code;
5520
+ detail;
5521
+ constructor(code, detail) {
5522
+ super(`${code}: ${detail}`);
5523
+ this.name = "ConfigMigrationError";
5524
+ this.code = code;
5525
+ this.detail = detail;
5526
+ }
5527
+ }
5528
+ function migrateConfig(raw) {
5529
+ if (typeof raw !== "object" || raw === null) {
5530
+ return raw;
5531
+ }
5532
+ const record = raw;
5533
+ const version = record.configVersion;
5534
+ if (version === undefined) {
5535
+ return { ...record, configVersion: CURRENT_CONFIG_VERSION };
5536
+ }
5537
+ if (typeof version !== "number" || !Number.isInteger(version) || version < 1) {
5538
+ throw new ConfigMigrationError("unknown-version", String(version));
5539
+ }
5540
+ if (version > CURRENT_CONFIG_VERSION) {
5541
+ throw new ConfigMigrationError("unsupported-version", String(version));
5542
+ }
5543
+ return record;
5544
+ }
5545
+
5126
5546
  // src/config/load.ts
5127
5547
  function loadOpenTeamConfig(raw) {
5128
- return OpenTeamConfigSchema.parse(raw ?? {});
5548
+ return OpenTeamConfigSchema.parse(migrateConfig(raw ?? {}));
5129
5549
  }
5130
5550
 
5131
5551
  // src/memory/types.ts