@monoes/monomindcli 1.12.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/helpers/handlers/route-handler.cjs +11 -4
- package/.claude/helpers/handlers/session-restore-handler.cjs +14 -8
- package/.claude/helpers/hook-handler.cjs +40 -0
- package/.claude/helpers/intelligence.cjs +129 -57
- package/.claude/helpers/memory-palace.cjs +461 -0
- package/.claude/helpers/memory.cjs +134 -15
- package/.claude/helpers/metrics-db.mjs +87 -0
- package/.claude/helpers/router.cjs +296 -41
- package/.claude/helpers/session.cjs +89 -32
- package/.claude/helpers/statusline.cjs +138 -2
- package/.claude/helpers/toggle-statusline.cjs +73 -0
- package/.claude/helpers/token-tracker.cjs +934 -0
- package/.claude/helpers/utils/monograph.cjs +39 -4
- package/.claude/helpers/utils/telemetry.cjs +3 -3
- package/dist/src/commands/doctor.d.ts.map +1 -1
- package/dist/src/commands/doctor.js +96 -4
- package/dist/src/commands/doctor.js.map +1 -1
- package/dist/src/mcp-tools/monograph-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/monograph-tools.js +329 -37
- package/dist/src/mcp-tools/monograph-tools.js.map +1 -1
- package/dist/src/services/worker-daemon.d.ts.map +1 -1
- package/dist/src/services/worker-daemon.js +295 -5
- package/dist/src/services/worker-daemon.js.map +1 -1
- package/dist/src/transfer/serialization/cfp.js +1 -1
- package/dist/src/transfer/serialization/cfp.js.map +1 -1
- package/dist/src/ui/dashboard.html +673 -6
- package/dist/src/ui/server.mjs +144 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -312,6 +312,24 @@ html, body { height: 100%; background: var(--bg); color: var(--text-hi); font-fa
|
|
|
312
312
|
#org-detail-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
|
313
313
|
.odt-pane { display: none; }
|
|
314
314
|
.odt-pane.active { display: block; }
|
|
315
|
+
#odt-chat { display: none; }
|
|
316
|
+
#odt-chat.active { display: flex; flex-direction: column; }
|
|
317
|
+
#odt-chat-bar { display:flex; align-items:center; gap:8px; padding:10px 16px 8px; border-bottom:1px solid var(--border); flex-shrink:0; flex-wrap:wrap; }
|
|
318
|
+
#odt-chat-sess-lbl { font-size:8px; letter-spacing:2px; color:var(--text-xs); flex-shrink:0; }
|
|
319
|
+
#odt-chat-sel { background:var(--surface); color:var(--text-mid); border:1px solid var(--border); border-radius:4px; font-size:10px; font-family:var(--mono); padding:3px 7px; cursor:pointer; max-width:320px; }
|
|
320
|
+
#odt-chat-sel:focus { outline:none; border-color:var(--accent); }
|
|
321
|
+
#odt-chat-live-dot { width:5px; height:5px; border-radius:50%; background:var(--text-xs); flex-shrink:0; margin-left:auto; transition:background 0.4s; }
|
|
322
|
+
#odt-chat-live-dot.on { background:oklch(68% 0.20 150); animation:livepulse-cv 2.2s ease-in-out infinite; }
|
|
323
|
+
#odt-chat-live-lbl { font-size:9px; color:var(--text-lo); }
|
|
324
|
+
#odt-chat-agent-bar { display:flex; align-items:center; gap:6px; padding:5px 16px 6px; border-bottom:1px solid var(--border); flex-shrink:0; flex-wrap:wrap; }
|
|
325
|
+
#odt-chat-agent-lbl { font-size:8px; letter-spacing:2px; color:var(--text-xs); flex-shrink:0; }
|
|
326
|
+
.odt-agent-pill { font-size:8px; padding:2px 8px; border-radius:10px; border:1px solid var(--border); color:var(--text-lo); background:transparent; cursor:pointer; letter-spacing:0.3px; font-family:var(--mono); transition:border-color 0.15s,color 0.15s; }
|
|
327
|
+
.odt-agent-pill:hover { border-color:oklch(62% 0.20 186 / 0.4); color:var(--text-mid); }
|
|
328
|
+
.odt-agent-pill.active { border-color:oklch(62% 0.20 186); color:oklch(62% 0.20 186); background:oklch(62% 0.20 186 / 0.07); }
|
|
329
|
+
#odt-chat-feed { flex:1; overflow-y:auto; padding:10px 16px; display:flex; flex-direction:column; gap:5px; scrollbar-width:thin; scrollbar-color:var(--border) transparent; }
|
|
330
|
+
#odt-chat-feed::-webkit-scrollbar { width:4px; }
|
|
331
|
+
#odt-chat-feed::-webkit-scrollbar-thumb { background:var(--border); border-radius:2px; }
|
|
332
|
+
#odt-chat-empty { font-size:11px; color:var(--text-lo); text-align:center; padding:32px 0; line-height:2; }
|
|
315
333
|
|
|
316
334
|
/* agent detail drawer (slides in from right within the org detail pane) */
|
|
317
335
|
#org-agent-drawer {
|
|
@@ -1367,9 +1385,6 @@ textarea.sess-note-input:focus { border-color:var(--accent); }
|
|
|
1367
1385
|
<div class="nav-item" data-view="monograph">
|
|
1368
1386
|
<span class="ico">⬡</span><span class="lbl">Monograph</span>
|
|
1369
1387
|
</div>
|
|
1370
|
-
<div class="nav-item" data-view="chat">
|
|
1371
|
-
<span class="ico">⌘</span><span class="lbl">Agent Chat</span>
|
|
1372
|
-
</div>
|
|
1373
1388
|
</div>
|
|
1374
1389
|
</div>
|
|
1375
1390
|
<div class="nav-no-proj" id="nav-no-proj-hint">Select a project above</div>
|
|
@@ -1377,13 +1392,16 @@ textarea.sess-note-input:focus { border-color:var(--accent); }
|
|
|
1377
1392
|
<div class="nav-item" data-view="global">
|
|
1378
1393
|
<span class="ico">⊕</span><span class="lbl">Global Feed</span>
|
|
1379
1394
|
</div>
|
|
1380
|
-
<div class="nav-item" data-view="global-loops" title="
|
|
1395
|
+
<div class="nav-item" data-view="global-loops" title="Loops across all projects">
|
|
1381
1396
|
<span class="ico">↺</span><span class="lbl">Global Loops</span>
|
|
1382
1397
|
<span class="bdg" id="bdg-global-loops">—</span>
|
|
1383
1398
|
</div>
|
|
1384
|
-
<div class="nav-item" data-view="global-tokens" title="
|
|
1399
|
+
<div class="nav-item" data-view="global-tokens" title="Token usage across all projects">
|
|
1385
1400
|
<span class="ico">$</span><span class="lbl">Global Tokens</span>
|
|
1386
1401
|
</div>
|
|
1402
|
+
<div class="nav-item" data-view="chat" title="All agent sessions across projects">
|
|
1403
|
+
<span class="ico">⌘</span><span class="lbl">Global Agent Chat</span>
|
|
1404
|
+
</div>
|
|
1387
1405
|
</div>
|
|
1388
1406
|
</div>
|
|
1389
1407
|
<div id="sb-footer">
|
|
@@ -1808,6 +1826,9 @@ textarea.sess-note-input:focus { border-color:var(--accent); }
|
|
|
1808
1826
|
<button class="odt-btn" data-tab="budgets" onclick="v2SwitchOrgTab('budgets')">Budgets</button>
|
|
1809
1827
|
<button class="odt-btn" data-tab="charts" onclick="v2SwitchOrgTab('charts')">Charts</button>
|
|
1810
1828
|
<button class="odt-btn" data-tab="skills" onclick="v2SwitchOrgTab('skills')">Skills</button>
|
|
1829
|
+
<button class="odt-btn" data-tab="chat" onclick="v2SwitchOrgTab('chat')">Chat</button>
|
|
1830
|
+
<button class="odt-btn" data-tab="config" onclick="v2SwitchOrgTab('config')">Config</button>
|
|
1831
|
+
<button class="odt-btn" data-tab="files" onclick="v2SwitchOrgTab('files')">Files</button>
|
|
1811
1832
|
</div>
|
|
1812
1833
|
<div id="org-detail-body">
|
|
1813
1834
|
<div class="odt-pane active" id="odt-chart"></div>
|
|
@@ -1834,6 +1855,9 @@ textarea.sess-note-input:focus { border-color:var(--accent); }
|
|
|
1834
1855
|
<div class="odt-pane" id="odt-workspaces"></div>
|
|
1835
1856
|
<div class="odt-pane" id="odt-invites"></div>
|
|
1836
1857
|
<div class="odt-pane" id="odt-agents-full"></div>
|
|
1858
|
+
<div class="odt-pane" id="odt-chat" style="flex-direction:column;height:100%;overflow:hidden;"></div>
|
|
1859
|
+
<div class="odt-pane" id="odt-config" style="overflow-y:auto"></div>
|
|
1860
|
+
<div class="odt-pane" id="odt-files" style="overflow-y:auto"></div>
|
|
1837
1861
|
<div class="odt-pane" id="odt-environments"></div>
|
|
1838
1862
|
<div class="odt-pane" id="odt-access"></div>
|
|
1839
1863
|
<div class="odt-pane" id="odt-issues-full"></div>
|
|
@@ -2247,7 +2271,7 @@ function switchView(v) {
|
|
|
2247
2271
|
el.classList.toggle('active', el.dataset.view === v));
|
|
2248
2272
|
document.querySelectorAll('.view').forEach(el =>
|
|
2249
2273
|
el.classList.toggle('active', el.id === 'view-' + v));
|
|
2250
|
-
const titles = { now:'Now', projects:'Projects', sessions:'Sessions', loops:'Loops', tokens:'Tokens', memory:'Memory', orgs:'Orgs', monograph:'Monograph', global:'Global Feed', 'global-loops':'Global Loops', 'global-tokens':'Global Tokens', chat:'Agent Chat' };
|
|
2274
|
+
const titles = { now:'Now', projects:'Projects', sessions:'Sessions', loops:'Loops', tokens:'Tokens', memory:'Memory', orgs:'Orgs', monograph:'Monograph', global:'Global Feed', 'global-loops':'Global Loops', 'global-tokens':'Global Tokens', chat:'Global Agent Chat' };
|
|
2251
2275
|
document.getElementById('view-title').textContent = titles[v] || v;
|
|
2252
2276
|
const PROJECT = DIR ? shortPath(DIR) : 'monomind';
|
|
2253
2277
|
const VIEW_LABELS = { now: 'Now', sessions: 'Sessions', projects: 'Projects', loops: 'Loops', tokens: 'Tokens', memory: 'Memory', orgs: 'Orgs', monograph: 'Monograph', global: 'Global Feed', 'global-loops': 'Global Loops', 'global-tokens': 'Global Tokens' };
|
|
@@ -3985,6 +4009,100 @@ function appendChatViewEvent(ev, animate) {
|
|
|
3985
4009
|
feed.scrollTop = feed.scrollHeight;
|
|
3986
4010
|
}
|
|
3987
4011
|
|
|
4012
|
+
function _cvExcerptText(ev) {
|
|
4013
|
+
const t = ev.type || '';
|
|
4014
|
+
if (t === 'intercom' || t === 'org:comms') return (ev.from || '?') + ' → ' + (ev.to || '?') + ': ' + (ev.msg || ev.message || '').slice(0, 80);
|
|
4015
|
+
if (t === 'agent:message') return (ev.agent || ev.name || '?') + ': ' + (ev.msg || ev.message || '').slice(0, 80);
|
|
4016
|
+
if (t === 'agent:spawn' || t === 'org:agent:online') return 'spawned ' + (ev.agent || ev.role || ev.title || '?');
|
|
4017
|
+
if (t === 'org:checkpoint') return 'checkpoint: ' + (ev.progress || '').slice(0, 80) + (ev.pending_tasks != null ? ' · ' + ev.pending_tasks + ' pending' : '');
|
|
4018
|
+
if (t === 'org:start') return 'org started: ' + (ev.goal || '').slice(0, 80);
|
|
4019
|
+
if (t === 'org:complete') return 'org complete';
|
|
4020
|
+
if (t === 'session:start') return 'started: ' + (ev.prompt || '').replace(/^running org:\s*/i, '').slice(0, 80);
|
|
4021
|
+
if (t === 'session:complete') return 'completed' + (ev.status ? ' [' + ev.status + ']' : '');
|
|
4022
|
+
if (t === 'domain:dispatch') return '→ ' + (ev.domain || '') + (ev.cmd ? ': ' + ev.cmd.slice(0, 60) : '');
|
|
4023
|
+
if (t === 'domain:complete') return '✓ ' + (ev.domain || '');
|
|
4024
|
+
if (ev.msg) return (ev.msg || '').slice(0, 80);
|
|
4025
|
+
return t.slice(0, 60);
|
|
4026
|
+
}
|
|
4027
|
+
|
|
4028
|
+
function _cvExcerptTag(ev) {
|
|
4029
|
+
const t = ev.type || '';
|
|
4030
|
+
if (t === 'intercom' || t === 'org:comms') return 'IC';
|
|
4031
|
+
if (t === 'agent:message') return 'MSG';
|
|
4032
|
+
if (t === 'agent:spawn' || t === 'org:agent:online') return 'NEW';
|
|
4033
|
+
if (t === 'org:checkpoint') return 'CHK';
|
|
4034
|
+
if (t.startsWith('org:')) return 'ORG';
|
|
4035
|
+
if (t.startsWith('session:')) return 'SES';
|
|
4036
|
+
if (t.startsWith('domain:')) return 'DOM';
|
|
4037
|
+
return 'EVT';
|
|
4038
|
+
}
|
|
4039
|
+
|
|
4040
|
+
function renderCvExcerptBanner(bannerId, events, sessionMeta) {
|
|
4041
|
+
const banner = document.getElementById(bannerId);
|
|
4042
|
+
if (!banner) return;
|
|
4043
|
+
const SKIP = new Set(['session:start']);
|
|
4044
|
+
const meaningful = (events || []).filter(ev => !SKIP.has(ev.type)).slice(-5).reverse();
|
|
4045
|
+
if (!meaningful.length) {
|
|
4046
|
+
const prompt = sessionMeta && (sessionMeta.prompt || '');
|
|
4047
|
+
const clean = prompt.replace(/^running org:\s*/i, '').slice(0, 120);
|
|
4048
|
+
if (!clean) { banner.classList.remove('visible'); banner.innerHTML = ''; return; }
|
|
4049
|
+
banner.innerHTML = `<div class="cv-excerpt-label">SESSION GOAL</div><div class="cv-excerpt-items"><div class="cv-excerpt-item"><span class="cv-ex-tag">ORG</span>${esc(clean)}</div></div>`;
|
|
4050
|
+
banner.classList.add('visible');
|
|
4051
|
+
return;
|
|
4052
|
+
}
|
|
4053
|
+
const items = meaningful.slice(0, 3).map(ev => {
|
|
4054
|
+
const tag = _cvExcerptTag(ev);
|
|
4055
|
+
const txt = _cvExcerptText(ev);
|
|
4056
|
+
return `<div class="cv-excerpt-item"><span class="cv-ex-tag">${tag}</span>${esc(txt)}</div>`;
|
|
4057
|
+
}).join('');
|
|
4058
|
+
banner.innerHTML = `<div class="cv-excerpt-label">LAST ACTIVITY</div><div class="cv-excerpt-items">${items}</div>`;
|
|
4059
|
+
banner.classList.add('visible');
|
|
4060
|
+
}
|
|
4061
|
+
|
|
4062
|
+
function mkCVSpawn(from, to, task, ts) {
|
|
4063
|
+
const d = document.createElement('div');
|
|
4064
|
+
d.className = 'cv-msg cv-spawn';
|
|
4065
|
+
const short = task.replace(/\n/g,' ').slice(0,140);
|
|
4066
|
+
d.innerHTML = `<div class="cv-bub" style="border-left:2px solid oklch(65% 0.18 280)">`+
|
|
4067
|
+
`<span class="cv-tag" style="background:oklch(25% 0.10 280);color:oklch(80% 0.18 280)">${esc(from)}</span>`+
|
|
4068
|
+
`<span class="cv-arrow" style="color:oklch(65% 0.18 280)">→</span>`+
|
|
4069
|
+
`<span class="cv-tag" style="background:oklch(25% 0.15 160);color:oklch(75% 0.20 160)">${esc(to)}</span>`+
|
|
4070
|
+
`<span class="cv-etype" style="color:oklch(65% 0.18 280)">SPAWN</span>`+
|
|
4071
|
+
`<span class="cv-text">${esc(short)}${task.length>140?'…':''}</span>`+
|
|
4072
|
+
`<span class="cv-ts">${ts}</span></div>`;
|
|
4073
|
+
return d;
|
|
4074
|
+
}
|
|
4075
|
+
|
|
4076
|
+
function mkCVResult(agent, text, ts) {
|
|
4077
|
+
const d = document.createElement('div');
|
|
4078
|
+
d.className = 'cv-msg cv-result';
|
|
4079
|
+
const isLong = text.length > 300;
|
|
4080
|
+
const preview = isLong ? text.slice(0, 300) + '…' : text;
|
|
4081
|
+
const uid = 'res-' + Math.random().toString(36).slice(2,8);
|
|
4082
|
+
d.innerHTML = `<div class="cv-bub" style="border-left:2px solid oklch(68% 0.20 150)">`+
|
|
4083
|
+
`<span class="cv-tag" style="background:oklch(20% 0.10 150);color:oklch(72% 0.20 150)">${esc(agent)}</span>`+
|
|
4084
|
+
`<span class="cv-etype" style="color:oklch(68% 0.20 150)">REPORT</span>`+
|
|
4085
|
+
`<span class="cv-text" id="${uid}" style="white-space:pre-wrap;line-height:1.6">${esc(preview)}</span>`+
|
|
4086
|
+
(isLong ? `<button class="btn" style="font-size:9px;padding:1px 6px;margin-left:6px;flex-shrink:0" onclick="(function(b,id,full){const el=document.getElementById(id);const show=el.dataset.expanded;el.textContent=show?full.slice(0,300)+'…':full;el.dataset.expanded=show?'':'1';b.textContent=show?'Expand':'Collapse';})(this,'${uid}',${JSON.stringify(text)})">Expand</button>` : '')+
|
|
4087
|
+
`<span class="cv-ts">${ts}</span></div>`;
|
|
4088
|
+
return d;
|
|
4089
|
+
}
|
|
4090
|
+
|
|
4091
|
+
function mkCVFileCard(name, filePath, size, ts) {
|
|
4092
|
+
const d = document.createElement('div');
|
|
4093
|
+
d.className = 'cv-msg cv-file';
|
|
4094
|
+
const sizeStr = size > 1024*1024 ? (size/1024/1024).toFixed(1)+'MB' : size > 1024 ? (size/1024).toFixed(1)+'KB' : (size||'?')+'B';
|
|
4095
|
+
const isMd = /\.md$/i.test(name);
|
|
4096
|
+
d.innerHTML = `<div class="cv-bub" style="border-left:2px solid oklch(65% 0.18 50);align-items:center;gap:8px">`+
|
|
4097
|
+
`<span style="font-size:14px">${isMd?'📄':'📁'}</span>`+
|
|
4098
|
+
`<span class="cv-tag" style="background:oklch(22% 0.10 50);color:oklch(78% 0.18 50)">FILE</span>`+
|
|
4099
|
+
`<span class="cv-text" style="font-family:var(--mono);font-size:10px">${esc(name)}</span>`+
|
|
4100
|
+
`<span style="font-size:9px;color:var(--text-lo);flex-shrink:0">${sizeStr}</span>`+
|
|
4101
|
+
(filePath ? `<button class="btn" style="font-size:9px;padding:1px 6px;flex-shrink:0;border-color:oklch(65% 0.18 50);color:oklch(65% 0.18 50)" onclick="v2ViewOrgFile(${JSON.stringify(filePath)},${JSON.stringify(name)})">View</button>` : '')+
|
|
4102
|
+
`<span class="cv-ts">${ts}</span></div>`;
|
|
4103
|
+
return d;
|
|
4104
|
+
}
|
|
4105
|
+
|
|
3988
4106
|
function mkCVSys(html, ts) {
|
|
3989
4107
|
const d = document.createElement('div');
|
|
3990
4108
|
d.className = 'cv-msg cv-sys';
|
|
@@ -5228,6 +5346,9 @@ function v2RenderOrgTab(tab) {
|
|
|
5228
5346
|
else if (tab === 'issues-full') v2RenderOrgIssuesFull();
|
|
5229
5347
|
else if (tab === 'join-requests') v2RenderOrgJoinRequests();
|
|
5230
5348
|
else if (tab === 'threads') v2RenderOrgThreads();
|
|
5349
|
+
else if (tab === 'chat') v2RenderOrgChat();
|
|
5350
|
+
else if (tab === 'config') v2RenderOrgConfig();
|
|
5351
|
+
else if (tab === 'files') v2RenderOrgFiles();
|
|
5231
5352
|
}
|
|
5232
5353
|
|
|
5233
5354
|
// ── org chart ───────────────────────────────
|
|
@@ -6295,6 +6416,552 @@ async function v2RenderOrgSkills() {
|
|
|
6295
6416
|
</div>`).join('');
|
|
6296
6417
|
}
|
|
6297
6418
|
|
|
6419
|
+
// ── ORG CHAT ───────────────────────────────────────────────
|
|
6420
|
+
let _odtChatSessions = [];
|
|
6421
|
+
let _odtChatCurrentId = null;
|
|
6422
|
+
let _odtChatCurrentAgent = 'all';
|
|
6423
|
+
let _odtChatSseSource = null;
|
|
6424
|
+
|
|
6425
|
+
function _odtOrgSessionMatch(s) {
|
|
6426
|
+
if (!_v2SelOrg) return true;
|
|
6427
|
+
const orgLc = _v2SelOrg.toLowerCase();
|
|
6428
|
+
const prompt = (s.prompt || '').toLowerCase();
|
|
6429
|
+
if (prompt.includes('running org: ' + orgLc) || prompt.includes('org: ' + orgLc)) return true;
|
|
6430
|
+
return (s.events || []).some(ev => (ev.org || '').toLowerCase() === orgLc);
|
|
6431
|
+
}
|
|
6432
|
+
|
|
6433
|
+
function _odtChatAgentMatches(ev) {
|
|
6434
|
+
if (_odtChatCurrentAgent === 'all') return true;
|
|
6435
|
+
// Structural events always show regardless of agent filter
|
|
6436
|
+
const structural = new Set(['session:start','session:complete','file:write','run:start','run:complete','run:cycle:complete','org:start','org:complete','org:agent:online']);
|
|
6437
|
+
if (structural.has(ev.type)) return true;
|
|
6438
|
+
return ev.agent === _odtChatCurrentAgent || ev.from === _odtChatCurrentAgent || ev.to === _odtChatCurrentAgent || ev.role === _odtChatCurrentAgent;
|
|
6439
|
+
}
|
|
6440
|
+
|
|
6441
|
+
async function v2RenderOrgChat() {
|
|
6442
|
+
const pane = document.getElementById('odt-chat');
|
|
6443
|
+
if (!pane) return;
|
|
6444
|
+
|
|
6445
|
+
// Build the pane HTML once
|
|
6446
|
+
if (!pane.querySelector('#odt-chat-bar')) {
|
|
6447
|
+
pane.innerHTML = `
|
|
6448
|
+
<div id="odt-chat-bar">
|
|
6449
|
+
<span id="odt-chat-sess-lbl">RUN</span>
|
|
6450
|
+
<select id="odt-chat-sel" onchange="odtChatSelectSession(this.value)">
|
|
6451
|
+
<option value="">— loading runs —</option>
|
|
6452
|
+
</select>
|
|
6453
|
+
<div id="odt-chat-live-dot"></div>
|
|
6454
|
+
<span id="odt-chat-live-lbl">OFFLINE</span>
|
|
6455
|
+
</div>
|
|
6456
|
+
<div id="odt-chat-agent-bar" style="display:none">
|
|
6457
|
+
<span id="odt-chat-agent-lbl">AGENT</span>
|
|
6458
|
+
</div>
|
|
6459
|
+
<div id="odt-chat-excerpt" class="cv-excerpt-banner"></div>
|
|
6460
|
+
<div id="odt-chat-feed">
|
|
6461
|
+
<div id="odt-chat-empty">Select a run to see agent communications.<br><span style="font-size:10px;opacity:0.5">Runs appear after the first /mastermind:runorg</span></div>
|
|
6462
|
+
</div>`;
|
|
6463
|
+
}
|
|
6464
|
+
|
|
6465
|
+
// Reset state when org changes
|
|
6466
|
+
_odtChatCurrentId = null;
|
|
6467
|
+
_odtChatCurrentAgent = 'all';
|
|
6468
|
+
document.getElementById('odt-chat-agent-bar').style.display = 'none';
|
|
6469
|
+
document.getElementById('odt-chat-feed').querySelectorAll('.cv-msg').forEach(e => e.remove());
|
|
6470
|
+
const emptyEl = document.getElementById('odt-chat-empty');
|
|
6471
|
+
if (emptyEl) { emptyEl.style.display = 'block'; emptyEl.textContent = 'Select a run to see agent communications.'; }
|
|
6472
|
+
|
|
6473
|
+
await _odtLoadChatSessions();
|
|
6474
|
+
_odtConnectChatSSE();
|
|
6475
|
+
}
|
|
6476
|
+
|
|
6477
|
+
async function _odtLoadChatSessions() {
|
|
6478
|
+
if (!_v2SelOrg) return;
|
|
6479
|
+
try {
|
|
6480
|
+
const dirParam = (typeof DIR !== 'undefined' && DIR) ? '&dir='+encodeURIComponent(DIR) : '';
|
|
6481
|
+
// Primary: structured org run files
|
|
6482
|
+
const runs = await apiFetch('/api/org/'+encodeURIComponent(_v2SelOrg)+'/runs?x=1'+dirParam);
|
|
6483
|
+
if (Array.isArray(runs) && runs.length) {
|
|
6484
|
+
_odtChatSessions = runs.map(r => ({
|
|
6485
|
+
id: r.runId,
|
|
6486
|
+
ts: r.startedAt,
|
|
6487
|
+
prompt: r.goal || r.runId,
|
|
6488
|
+
status: r.status,
|
|
6489
|
+
_isOrgRun: true,
|
|
6490
|
+
_runMeta: r,
|
|
6491
|
+
events: [], // loaded lazily in odtChatSelectSession
|
|
6492
|
+
}));
|
|
6493
|
+
_odtPopulateChatSel();
|
|
6494
|
+
if (!_odtChatCurrentId && _odtChatSessions.length) {
|
|
6495
|
+
const first = _odtChatSessions[0];
|
|
6496
|
+
const sel = document.getElementById('odt-chat-sel');
|
|
6497
|
+
if (sel) { sel.value = first.id; odtChatSelectSession(first.id); }
|
|
6498
|
+
}
|
|
6499
|
+
return;
|
|
6500
|
+
}
|
|
6501
|
+
} catch(_) {}
|
|
6502
|
+
// Fallback: mastermind lifecycle events (pre-run-file era sessions)
|
|
6503
|
+
try {
|
|
6504
|
+
const r = await apiFetch('/api/mastermind/sessions');
|
|
6505
|
+
const all = Array.isArray(r) ? r : Object.values(r.sessions || {});
|
|
6506
|
+
_odtChatSessions = all.filter(_odtOrgSessionMatch);
|
|
6507
|
+
_odtPopulateChatSel();
|
|
6508
|
+
} catch(_) {}
|
|
6509
|
+
}
|
|
6510
|
+
|
|
6511
|
+
function _odtPopulateChatSel() {
|
|
6512
|
+
const sel = document.getElementById('odt-chat-sel');
|
|
6513
|
+
if (!sel) return;
|
|
6514
|
+
const prev = _odtChatCurrentId || sel.value;
|
|
6515
|
+
const count = _odtChatSessions.length;
|
|
6516
|
+
sel.innerHTML = `<option value="">${count ? `— ${count} run${count !== 1 ? 's' : ''} for ${_v2SelOrg} —` : '— no runs yet —'}</option>`;
|
|
6517
|
+
_odtChatSessions.forEach(s => {
|
|
6518
|
+
const d = new Date(s.ts || s.startedAt || 0);
|
|
6519
|
+
const ts = d.toLocaleTimeString([],{hour:'2-digit',minute:'2-digit',second:'2-digit'});
|
|
6520
|
+
const date = d.toLocaleDateString([],{month:'short',day:'numeric'});
|
|
6521
|
+
const prompt = (s.prompt || '').replace(/^running org:\s*/i, '').slice(0,40);
|
|
6522
|
+
const meta = s._runMeta;
|
|
6523
|
+
const extra = meta ? ` · ${meta.cycleCount||0}cyc · ${meta.eventCount||0}ev` : '';
|
|
6524
|
+
const opt = document.createElement('option');
|
|
6525
|
+
opt.value = s.id;
|
|
6526
|
+
opt.textContent = `${date} ${ts} ${prompt}${prompt.length>=40?'…':''}${extra} [${s.status||'?'}]`;
|
|
6527
|
+
if (s.id === prev) opt.selected = true;
|
|
6528
|
+
sel.appendChild(opt);
|
|
6529
|
+
});
|
|
6530
|
+
// Auto-select running session
|
|
6531
|
+
if (!_odtChatCurrentId) {
|
|
6532
|
+
const running = _odtChatSessions.find(s => s.status === 'running');
|
|
6533
|
+
if (running) { sel.value = running.id; odtChatSelectSession(running.id); }
|
|
6534
|
+
}
|
|
6535
|
+
}
|
|
6536
|
+
|
|
6537
|
+
window.odtChatSelectSession = async function(id) {
|
|
6538
|
+
_odtChatCurrentId = id;
|
|
6539
|
+
_odtChatCurrentAgent = 'all';
|
|
6540
|
+
const feed = document.getElementById('odt-chat-feed');
|
|
6541
|
+
const emptyEl = document.getElementById('odt-chat-empty');
|
|
6542
|
+
const bar = document.getElementById('odt-chat-agent-bar');
|
|
6543
|
+
feed.querySelectorAll('.cv-msg').forEach(e => e.remove());
|
|
6544
|
+
if (!id) {
|
|
6545
|
+
if (emptyEl) emptyEl.style.display = 'block';
|
|
6546
|
+
if (bar) bar.style.display = 'none';
|
|
6547
|
+
return;
|
|
6548
|
+
}
|
|
6549
|
+
const sess = _odtChatSessions.find(s => s.id === id);
|
|
6550
|
+
if (!sess) return;
|
|
6551
|
+
if (emptyEl) emptyEl.style.display = 'none';
|
|
6552
|
+
// Lazy-load events for org run sessions
|
|
6553
|
+
if (sess._isOrgRun && !sess._eventsLoaded) {
|
|
6554
|
+
const feed2 = document.getElementById('odt-chat-feed');
|
|
6555
|
+
const loadingEl = Object.assign(document.createElement('div'), { id: 'odt-chat-loading', style: 'text-align:center;padding:24px 0;font-size:11px;opacity:0.5;color:var(--text-mid)' });
|
|
6556
|
+
loadingEl.textContent = 'Loading run events…';
|
|
6557
|
+
if (feed2) feed2.appendChild(loadingEl);
|
|
6558
|
+
try {
|
|
6559
|
+
const dirParam = (typeof DIR !== 'undefined' && DIR) ? '?dir='+encodeURIComponent(DIR) : '';
|
|
6560
|
+
const evs = await apiFetch('/api/org/'+encodeURIComponent(_v2SelOrg)+'/runs/'+encodeURIComponent(id)+dirParam);
|
|
6561
|
+
sess.events = Array.isArray(evs) ? evs : [];
|
|
6562
|
+
sess._eventsLoaded = true;
|
|
6563
|
+
} catch(_) { sess.events = []; sess._eventsLoaded = true; }
|
|
6564
|
+
loadingEl.remove();
|
|
6565
|
+
}
|
|
6566
|
+
// Agent pills from org config roles (authoritative list, not derived from events)
|
|
6567
|
+
const orgRoles = Array.isArray(_v2OrgData?.roles) ? _v2OrgData.roles : [];
|
|
6568
|
+
_odtPopulateAgentBar(sess.events || [], orgRoles);
|
|
6569
|
+
// No excerpt banner for structured org run sessions
|
|
6570
|
+
const ex = document.getElementById('odt-chat-excerpt');
|
|
6571
|
+
if (ex) ex.innerHTML = '';
|
|
6572
|
+
(sess.events || []).forEach(ev => _odtAppendEvent(ev, false));
|
|
6573
|
+
feed.scrollTop = feed.scrollHeight;
|
|
6574
|
+
};
|
|
6575
|
+
|
|
6576
|
+
function _odtPopulateAgentBar(events, orgRoles) {
|
|
6577
|
+
const bar = document.getElementById('odt-chat-agent-bar');
|
|
6578
|
+
if (!bar) return;
|
|
6579
|
+
const agentSet = new Set();
|
|
6580
|
+
// Org config roles are the authoritative agent list — always use them when available
|
|
6581
|
+
const roles = Array.isArray(orgRoles) ? orgRoles : (Array.isArray(_v2OrgData?.roles) ? _v2OrgData.roles : []);
|
|
6582
|
+
roles.forEach(r => { const n = typeof r === 'string' ? r : (r.id || r.role || r.name || ''); if (n) agentSet.add(n); });
|
|
6583
|
+
// For runs without org config, derive from events (agent:online events are reliable)
|
|
6584
|
+
if (!agentSet.size) {
|
|
6585
|
+
(events || []).forEach(ev => {
|
|
6586
|
+
if (ev.type === 'org:agent:online' && ev.role) agentSet.add(ev.role);
|
|
6587
|
+
else if (ev.type === 'org:comms') {
|
|
6588
|
+
if (ev.from && ev.from.length < 40) agentSet.add(ev.from);
|
|
6589
|
+
if (ev.to && ev.to !== 'all' && ev.to.length < 40) agentSet.add(ev.to);
|
|
6590
|
+
}
|
|
6591
|
+
});
|
|
6592
|
+
}
|
|
6593
|
+
const agents = [...agentSet].sort();
|
|
6594
|
+
if (!agents.length) { bar.style.display = 'none'; return; }
|
|
6595
|
+
bar.style.display = 'flex';
|
|
6596
|
+
bar.innerHTML = '<span id="odt-chat-agent-lbl">AGENT</span>';
|
|
6597
|
+
['all', ...agents].forEach(name => {
|
|
6598
|
+
const pill = document.createElement('button');
|
|
6599
|
+
pill.className = 'odt-agent-pill' + (_odtChatCurrentAgent === name ? ' active' : '');
|
|
6600
|
+
pill.textContent = name === 'all' ? 'ALL' : name;
|
|
6601
|
+
pill.dataset.agent = name;
|
|
6602
|
+
pill.onclick = () => odtChatSelectAgent(name);
|
|
6603
|
+
bar.appendChild(pill);
|
|
6604
|
+
});
|
|
6605
|
+
}
|
|
6606
|
+
|
|
6607
|
+
window.odtChatSelectAgent = function(name) {
|
|
6608
|
+
_odtChatCurrentAgent = name;
|
|
6609
|
+
document.querySelectorAll('#odt-chat-agent-bar .odt-agent-pill').forEach(p => {
|
|
6610
|
+
p.classList.toggle('active', p.dataset.agent === name);
|
|
6611
|
+
});
|
|
6612
|
+
const feed = document.getElementById('odt-chat-feed');
|
|
6613
|
+
const emptyEl = document.getElementById('odt-chat-empty');
|
|
6614
|
+
const sess = _odtChatSessions.find(s => s.id === _odtChatCurrentId);
|
|
6615
|
+
feed.querySelectorAll('.cv-msg').forEach(e => e.remove());
|
|
6616
|
+
if (!sess) { if (emptyEl) emptyEl.style.display = 'block'; return; }
|
|
6617
|
+
const visible = (sess.events || []).filter(ev => _odtChatAgentMatches(ev));
|
|
6618
|
+
if (!visible.length) {
|
|
6619
|
+
if (emptyEl) { emptyEl.style.display = 'block'; emptyEl.textContent = name === 'all' ? 'No events.' : `No events for "${name}"`; }
|
|
6620
|
+
} else {
|
|
6621
|
+
if (emptyEl) emptyEl.style.display = 'none';
|
|
6622
|
+
visible.forEach(ev => _odtAppendEvent(ev, false));
|
|
6623
|
+
}
|
|
6624
|
+
feed.scrollTop = feed.scrollHeight;
|
|
6625
|
+
};
|
|
6626
|
+
|
|
6627
|
+
function _odtAppendEvent(ev, animate) {
|
|
6628
|
+
if (!_odtChatAgentMatches(ev)) return;
|
|
6629
|
+
const feed = document.getElementById('odt-chat-feed');
|
|
6630
|
+
if (!feed) return;
|
|
6631
|
+
const emptyEl = document.getElementById('odt-chat-empty');
|
|
6632
|
+
if (emptyEl) emptyEl.style.display = 'none';
|
|
6633
|
+
const ts = ev.ts ? new Date(ev.ts).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit',second:'2-digit'}) : '';
|
|
6634
|
+
let el;
|
|
6635
|
+
if (ev.type === 'run:start') {
|
|
6636
|
+
el = mkCVSys('▶ Run started' + (ev.goal ? ': ' + esc(ev.goal.slice(0,80)) : '') + (ev.bossRole ? ' · boss: ' + esc(ev.bossRole) : ''), ts);
|
|
6637
|
+
} else if (ev.type === 'run:cycle:complete') {
|
|
6638
|
+
el = mkCVSys('◎ Cycle complete' + (ev.pending != null ? ' · ' + ev.pending + ' tasks pending' : ''), ts);
|
|
6639
|
+
} else if (ev.type === 'run:complete') {
|
|
6640
|
+
el = mkCVSys('■ Run complete' + (ev.status ? ' [' + esc(ev.status) + ']' : ''), ts);
|
|
6641
|
+
} else if (ev.type === 'org:comms') {
|
|
6642
|
+
if (ev.to && ev.to !== 'all' && ev.from) el = mkCVIntercom(ev.from || 'boss', ev.to, ev.msg || '', ts);
|
|
6643
|
+
else el = mkCVAgent(ev.from || 'boss', ev.msg || '', ts, 'org:comms');
|
|
6644
|
+
} else if (ev.type === 'org:agent:online') {
|
|
6645
|
+
el = mkCVSys('◉ ' + esc(ev.title || ev.role || '?') + ' online', ts);
|
|
6646
|
+
} else if (ev.type === 'org:checkpoint') {
|
|
6647
|
+
el = mkCVAgent('boss', ev.progress || '', ts, 'org:checkpoint');
|
|
6648
|
+
} else if (ev.type === 'org:start' || ev.type === 'org:complete') {
|
|
6649
|
+
el = mkCVSys(ev.type === 'org:start' ? '▶ Org started' : '■ Org complete', ts);
|
|
6650
|
+
} else if (ev.type === 'agent:spawn') {
|
|
6651
|
+
el = mkCVSpawn(ev.from || 'orchestrator', ev.to || '?', ev.task || ev.briefing || '', ts);
|
|
6652
|
+
} else if (ev.type === 'agent:result') {
|
|
6653
|
+
el = mkCVResult(ev.agent || '?', ev.msg || '', ts);
|
|
6654
|
+
} else if (ev.type === 'file:write') {
|
|
6655
|
+
el = mkCVFileCard(ev.name || ev.path || '?', ev.path || '', ev.size || 0, ts);
|
|
6656
|
+
} else if (ev.type === 'intercom') {
|
|
6657
|
+
el = mkCVIntercom(ev.from, ev.to, ev.msg || '', ts);
|
|
6658
|
+
} else if (ev.type === 'agent:message') {
|
|
6659
|
+
el = mkCVAgent(ev.agent || ev.name || '?', ev.msg || ev.message || '', ts, ev.type);
|
|
6660
|
+
} else if (ev.type === 'session:start') {
|
|
6661
|
+
el = mkCVSys('▶ ' + esc((ev.prompt || '').replace(/^running org:\s*/i,'').slice(0,80) || 'Session started'), ts);
|
|
6662
|
+
} else if (ev.type === 'session:complete') {
|
|
6663
|
+
el = mkCVSys('■ Complete' + (ev.status ? ' — ' + esc(ev.status) : ''), ts);
|
|
6664
|
+
} else if (ev.type === 'domain:dispatch') {
|
|
6665
|
+
el = mkCVSys('→ ' + esc(ev.domain || '') + (ev.cmd ? ': ' + esc(ev.cmd.slice(0,80)) : ''), ts);
|
|
6666
|
+
} else if (ev.type === 'domain:complete') {
|
|
6667
|
+
el = mkCVSys('✓ ' + esc(ev.domain || '') + (ev.status ? ' [' + esc(ev.status) + ']' : ''), ts);
|
|
6668
|
+
} else if (ev.type === 'loop:start') {
|
|
6669
|
+
el = mkCVSys('Loop: ' + esc(ev.command || ''), ts);
|
|
6670
|
+
} else if (ev.type === 'loop:complete') {
|
|
6671
|
+
el = mkCVSys('Loop done: ' + esc(ev.command || '') + (ev.ranReps ? ' (' + ev.ranReps + ' runs)' : ''), ts);
|
|
6672
|
+
} else {
|
|
6673
|
+
return; // skip unknown event types silently
|
|
6674
|
+
}
|
|
6675
|
+
if (animate) el.classList.add('cv-new');
|
|
6676
|
+
feed.appendChild(el);
|
|
6677
|
+
feed.scrollTop = feed.scrollHeight;
|
|
6678
|
+
}
|
|
6679
|
+
|
|
6680
|
+
function _odtConnectChatSSE() {
|
|
6681
|
+
if (_odtChatSseSource) return;
|
|
6682
|
+
const dot = document.getElementById('odt-chat-live-dot');
|
|
6683
|
+
const lbl = document.getElementById('odt-chat-live-lbl');
|
|
6684
|
+
_odtChatSseSource = new EventSource('/api/mastermind-stream');
|
|
6685
|
+
_odtChatSseSource.onopen = () => { dot?.classList.add('on'); if (lbl) lbl.textContent = 'LIVE'; };
|
|
6686
|
+
_odtChatSseSource.onmessage = e => {
|
|
6687
|
+
try { _odtHandleLiveEvent(JSON.parse(e.data)); } catch(_) {}
|
|
6688
|
+
};
|
|
6689
|
+
_odtChatSseSource.onerror = () => {
|
|
6690
|
+
dot?.classList.remove('on');
|
|
6691
|
+
if (lbl) lbl.textContent = 'OFFLINE';
|
|
6692
|
+
_odtChatSseSource = null;
|
|
6693
|
+
setTimeout(_odtConnectChatSSE, 5000);
|
|
6694
|
+
};
|
|
6695
|
+
}
|
|
6696
|
+
|
|
6697
|
+
function _odtHandleLiveEvent(ev) {
|
|
6698
|
+
// Route org run events (have runId + org) to the active run session
|
|
6699
|
+
if (ev?.org && ev?.runId && ev.org === _v2SelOrg) {
|
|
6700
|
+
let runSess = _odtChatSessions.find(s => s.id === ev.runId);
|
|
6701
|
+
if (!runSess && ev.type === 'run:start') {
|
|
6702
|
+
runSess = { id: ev.runId, ts: ev.ts || Date.now(), prompt: ev.goal || '', status: 'running', _isOrgRun: true, _eventsLoaded: true, _runMeta: { cycleCount: 0, eventCount: 0 }, events: [ev] };
|
|
6703
|
+
_odtChatSessions.unshift(runSess);
|
|
6704
|
+
_odtPopulateChatSel();
|
|
6705
|
+
if (!_odtChatCurrentId && _v2OrgTab === 'chat') {
|
|
6706
|
+
const sel = document.getElementById('odt-chat-sel');
|
|
6707
|
+
if (sel) { sel.value = ev.runId; odtChatSelectSession(ev.runId); }
|
|
6708
|
+
}
|
|
6709
|
+
} else if (runSess) {
|
|
6710
|
+
(runSess.events = runSess.events || []).push(ev);
|
|
6711
|
+
if (runSess._runMeta) runSess._runMeta.eventCount = (runSess._runMeta.eventCount || 0) + 1;
|
|
6712
|
+
if (ev.type === 'run:cycle:complete') { if (runSess._runMeta) runSess._runMeta.cycleCount = (runSess._runMeta.cycleCount || 0) + 1; }
|
|
6713
|
+
if (ev.type === 'run:complete' || ev.type === 'org:complete') { runSess.status = 'complete'; _odtPopulateChatSel(); }
|
|
6714
|
+
if (_odtChatCurrentId === ev.runId && _v2OrgTab === 'chat') {
|
|
6715
|
+
_odtAppendEvent(ev, true);
|
|
6716
|
+
}
|
|
6717
|
+
}
|
|
6718
|
+
return;
|
|
6719
|
+
}
|
|
6720
|
+
|
|
6721
|
+
if (!ev?.session) return;
|
|
6722
|
+
const isOrgSess = _odtOrgSessionMatch({ id: ev.session, prompt: ev.prompt || '', events: [ev] });
|
|
6723
|
+
if (!isOrgSess) return;
|
|
6724
|
+
|
|
6725
|
+
let sess = _odtChatSessions.find(s => s.id === ev.session);
|
|
6726
|
+
if (!sess && ev.type === 'session:start') {
|
|
6727
|
+
sess = { id: ev.session, ts: ev.ts || Date.now(), prompt: ev.prompt || '', status: 'running', events: [ev] };
|
|
6728
|
+
_odtChatSessions.unshift(sess);
|
|
6729
|
+
_odtPopulateChatSel();
|
|
6730
|
+
// Auto-select if nothing selected
|
|
6731
|
+
if (!_odtChatCurrentId && _v2OrgTab === 'chat') {
|
|
6732
|
+
const sel = document.getElementById('odt-chat-sel');
|
|
6733
|
+
if (sel) { sel.value = ev.session; odtChatSelectSession(ev.session); }
|
|
6734
|
+
}
|
|
6735
|
+
return;
|
|
6736
|
+
}
|
|
6737
|
+
if (sess) {
|
|
6738
|
+
(sess.events = sess.events || []).push(ev);
|
|
6739
|
+
if (ev.type === 'session:complete') { sess.status = ev.status || 'complete'; _odtPopulateChatSel(); }
|
|
6740
|
+
if (_odtChatCurrentId === ev.session && _v2OrgTab === 'chat') {
|
|
6741
|
+
const hasNewAgent = (ev.agent && !document.querySelector(`#odt-chat-agent-bar [data-agent="${CSS.escape(ev.agent)}"]`))
|
|
6742
|
+
|| (ev.from && !document.querySelector(`#odt-chat-agent-bar [data-agent="${CSS.escape(ev.from)}"]`))
|
|
6743
|
+
|| (ev.to && !document.querySelector(`#odt-chat-agent-bar [data-agent="${CSS.escape(ev.to)}"]`));
|
|
6744
|
+
if (hasNewAgent) _odtPopulateAgentBar(sess.events, Array.isArray(_v2OrgData?.roles) ? _v2OrgData.roles : []);
|
|
6745
|
+
if (!sess._isJsonl) renderCvExcerptBanner('odt-chat-excerpt', sess.events, sess);
|
|
6746
|
+
_odtAppendEvent(ev, true);
|
|
6747
|
+
}
|
|
6748
|
+
}
|
|
6749
|
+
}
|
|
6750
|
+
|
|
6751
|
+
// ── ORG CONFIG ─────────────────────────────────────────────
|
|
6752
|
+
function v2RenderOrgConfig() {
|
|
6753
|
+
const el = document.getElementById('odt-config');
|
|
6754
|
+
if (!el || !_v2OrgData || !_v2SelOrg) return;
|
|
6755
|
+
const d = _v2OrgData;
|
|
6756
|
+
const rc = d.run_config || {};
|
|
6757
|
+
const gov = (d.governance && typeof d.governance === 'object') ? d.governance : { policy: d.governance || 'auto' };
|
|
6758
|
+
const topos = ['hierarchical','hierarchical-mesh','mesh','star','ring','adaptive','hybrid'];
|
|
6759
|
+
const modes = ['daemon','once','scheduled'];
|
|
6760
|
+
const statuses = ['active','paused','archived'];
|
|
6761
|
+
const govPolicies = ['auto','board','strict'];
|
|
6762
|
+
const inp = (id,val,type,extra) => '<input class="filter-input" id="'+id+'" type="'+(type||'text')+'" value="'+esc(String(val??''))+'" '+(extra||'')+' style="width:100%;box-sizing:border-box">';
|
|
6763
|
+
const sel = (id,opts,cur) => '<select class="filter-input" id="'+id+'" style="width:100%;box-sizing:border-box;cursor:pointer">'+opts.map(o=>'<option'+(cur===o?' selected':'')+'>'+esc(o)+'</option>').join('')+'</select>';
|
|
6764
|
+
const fld = (lbl,html) => '<div style="display:flex;flex-direction:column;gap:4px"><div style="font-size:10px;color:var(--text-lo)">'+lbl+'</div>'+html+'</div>';
|
|
6765
|
+
const sec = (title,inner) => '<div style="margin-bottom:22px"><div style="font-size:9px;letter-spacing:2px;color:var(--text-xs);margin-bottom:10px;padding-bottom:5px;border-bottom:1px solid var(--border)">'+title+'</div><div style="display:grid;grid-template-columns:1fr 1fr;gap:11px">'+inner+'</div></div>';
|
|
6766
|
+
el.innerHTML = '<div style="padding:18px;overflow-y:auto;max-width:660px">'
|
|
6767
|
+
+sec('GENERAL',
|
|
6768
|
+
fld('Name', inp('oc-name', d.name||'','text','readonly style="opacity:0.5"'))
|
|
6769
|
+
+fld('Status', sel('oc-status', statuses, d.status||'active'))
|
|
6770
|
+
+'<div style="grid-column:1/-1">'+fld('Goal', '<textarea id="oc-goal" class="filter-input" rows="3" style="width:100%;box-sizing:border-box;resize:vertical;line-height:1.5">'+esc(d.goal||'')+'</textarea>')+'</div>'
|
|
6771
|
+
+fld('Mode', sel('oc-mode', modes, d.mode||'daemon'))
|
|
6772
|
+
+fld('Topology', sel('oc-topology', topos, d.topology||'hierarchical'))
|
|
6773
|
+
+'<div style="grid-column:1/-1">'+fld('Schedule', inp('oc-schedule', d.schedule||''))+'</div>'
|
|
6774
|
+
)
|
|
6775
|
+
+sec('RUN CONFIG',
|
|
6776
|
+
fld('Max Concurrent Agents', inp('oc-max-agents', rc.max_concurrent_agents??6,'number','min="1" max="32"'))
|
|
6777
|
+
+fld('Checkpoint Interval (min)', inp('oc-checkpoint', rc.checkpoint_interval_min??30,'number','min="5"'))
|
|
6778
|
+
+fld('Budget Tokens (0=unlimited)', inp('oc-budget', rc.budget_tokens??0,'number','min="0"'))
|
|
6779
|
+
+fld('Alert Threshold (0-1)', inp('oc-alert', rc.alert_threshold??0.8,'number','step="0.05" min="0" max="1"'))
|
|
6780
|
+
+fld('Boss Role ID', inp('oc-boss', rc.boss_role||''))
|
|
6781
|
+
+fld('Memory Namespace', inp('oc-namespace', rc.memory_namespace||('org:'+d.name)))
|
|
6782
|
+
+'<div style="grid-column:1/-1;display:flex;align-items:center;gap:8px;margin-top:4px"><input type="checkbox" id="oc-spawn-all" '+(rc.spawn_all_roles?'checked':'')+' style="accent-color:var(--accent);width:14px;height:14px;cursor:pointer"><label for="oc-spawn-all" style="font-size:12px;color:var(--text-mid);cursor:pointer">Spawn all roles as separate agents</label></div>'
|
|
6783
|
+
)
|
|
6784
|
+
+sec('GOVERNANCE',
|
|
6785
|
+
fld('Approval Policy', sel('oc-gov-policy', govPolicies, gov.policy||'auto'))
|
|
6786
|
+
+fld('Approvals File', inp('oc-gov-file', gov.approvals_file||('.monomind/orgs/'+d.name+'-approvals.json')))
|
|
6787
|
+
)
|
|
6788
|
+
+'<div style="display:flex;align-items:center;gap:10px"><button class="btn" style="border-color:var(--accent);color:var(--accent)" onclick="v2SaveOrgConfig()">Save Config</button><span id="oc-status-msg" style="font-size:11px;color:var(--text-lo)"></span></div>'
|
|
6789
|
+
+'</div>';
|
|
6790
|
+
}
|
|
6791
|
+
|
|
6792
|
+
async function v2SaveOrgConfig() {
|
|
6793
|
+
if (!_v2SelOrg || !_v2OrgData) return;
|
|
6794
|
+
const g = id => document.getElementById(id);
|
|
6795
|
+
const cfg = {
|
|
6796
|
+
..._v2OrgData,
|
|
6797
|
+
status: (g('oc-status')?.value) || _v2OrgData.status,
|
|
6798
|
+
goal: (g('oc-goal')?.value) || _v2OrgData.goal,
|
|
6799
|
+
mode: (g('oc-mode')?.value) || _v2OrgData.mode,
|
|
6800
|
+
topology: (g('oc-topology')?.value) || _v2OrgData.topology,
|
|
6801
|
+
schedule: (g('oc-schedule')?.value||'').trim() || null,
|
|
6802
|
+
run_config: { ...(_v2OrgData.run_config||{}),
|
|
6803
|
+
max_concurrent_agents: parseInt(g('oc-max-agents')?.value)||6,
|
|
6804
|
+
checkpoint_interval_min: parseInt(g('oc-checkpoint')?.value)||30,
|
|
6805
|
+
budget_tokens: parseInt(g('oc-budget')?.value)||0,
|
|
6806
|
+
alert_threshold: parseFloat(g('oc-alert')?.value)||0.8,
|
|
6807
|
+
boss_role: (g('oc-boss')?.value||'').trim(),
|
|
6808
|
+
memory_namespace: (g('oc-namespace')?.value||'').trim()||('org:'+_v2SelOrg),
|
|
6809
|
+
spawn_all_roles: !!(g('oc-spawn-all')?.checked),
|
|
6810
|
+
},
|
|
6811
|
+
governance: {
|
|
6812
|
+
policy: (g('oc-gov-policy')?.value)||'auto',
|
|
6813
|
+
approvals_file: (g('oc-gov-file')?.value||'').trim()||('.monomind/orgs/'+_v2SelOrg+'-approvals.json'),
|
|
6814
|
+
},
|
|
6815
|
+
};
|
|
6816
|
+
const msg = document.getElementById('oc-status-msg');
|
|
6817
|
+
if (msg) { msg.style.color='var(--text-lo)'; msg.textContent='Saving...'; }
|
|
6818
|
+
try {
|
|
6819
|
+
const qs = (typeof DIR !== 'undefined' && DIR) ? '?dir='+encodeURIComponent(DIR) : '';
|
|
6820
|
+
const r = await fetch('/api/orgs'+qs, { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(cfg) });
|
|
6821
|
+
const json = await r.json();
|
|
6822
|
+
if (json.ok) {
|
|
6823
|
+
_v2OrgData = Object.assign({}, _v2OrgData, cfg);
|
|
6824
|
+
if (msg) { msg.style.color='oklch(68% 0.20 150)'; msg.textContent='Saved'; setTimeout(()=>{ if(msg) msg.textContent=''; }, 2500); }
|
|
6825
|
+
if (typeof showToast === 'function') showToast('Config saved','','ok');
|
|
6826
|
+
} else { throw new Error(json.error||'Save failed'); }
|
|
6827
|
+
} catch(e) {
|
|
6828
|
+
if (msg) { msg.style.color='oklch(60% 0.22 25)'; msg.textContent='Error: '+e.message; }
|
|
6829
|
+
}
|
|
6830
|
+
}
|
|
6831
|
+
|
|
6832
|
+
// ── ORG FILES ──────────────────────────────────────────────
|
|
6833
|
+
async function v2RenderOrgFiles() {
|
|
6834
|
+
const el = document.getElementById('odt-files');
|
|
6835
|
+
if (!el || !_v2SelOrg) return;
|
|
6836
|
+
el.innerHTML = '<div style="padding:20px;color:var(--text-lo)">Loading files...</div>';
|
|
6837
|
+
try {
|
|
6838
|
+
const enc = encodeURIComponent(_v2SelOrg);
|
|
6839
|
+
const qs = (typeof DIR !== 'undefined' && DIR) ? '?dir='+encodeURIComponent(DIR) : '';
|
|
6840
|
+
const files = await fetch('/api/org/'+enc+'/files'+qs).then(r => r.ok ? r.json() : []).catch(() => []);
|
|
6841
|
+
if (!files.length) { el.innerHTML = '<div style="padding:20px;color:var(--text-lo)">No files found.</div>'; return; }
|
|
6842
|
+
const fmtSize = b => b < 1024 ? b+'B' : b < 1048576 ? (b/1024).toFixed(1)+'K' : (b/1048576).toFixed(1)+'M';
|
|
6843
|
+
const fmtTime = s => { const d = new Date(s); return d.toLocaleDateString([],{month:'short',day:'numeric'})+' '+d.toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'}); };
|
|
6844
|
+
const typeColor = { config:'oklch(62% 0.20 186)', generated:'oklch(68% 0.20 150)', 'agent-definition':'oklch(65% 0.20 280)', approvals:'oklch(70% 0.18 60)', state:'oklch(70% 0.18 60)', goals:'oklch(70% 0.18 60)' };
|
|
6845
|
+
const groupOrder = ['config','generated','agent-definition','approvals','state','goals','routines','other'];
|
|
6846
|
+
const groups = {};
|
|
6847
|
+
groupOrder.forEach(k => { groups[k] = { label: k==='agent-definition'?'AGENT DEFINITIONS':k.toUpperCase().replace(/-/g,' '), items:[] }; });
|
|
6848
|
+
files.forEach(f => { const k = groups[f.type] ? f.type : 'other'; if (!groups[k]) groups[k]={label:f.type.toUpperCase(),items:[]}; groups[k].items.push(f); });
|
|
6849
|
+
const color = t => typeColor[t]||'var(--text-lo)';
|
|
6850
|
+
const badge = t => '<span style="font-size:9px;padding:1px 6px;border-radius:8px;border:1px solid '+color(t)+';color:'+color(t)+'">'+t+'</span>';
|
|
6851
|
+
el.innerHTML = '<div style="padding:16px">'+groupOrder.filter(k => groups[k]&&groups[k].items.length).map(k => {
|
|
6852
|
+
const g = groups[k];
|
|
6853
|
+
return '<div style="margin-bottom:20px"><div style="font-size:9px;letter-spacing:2px;color:var(--text-xs);margin-bottom:8px;padding-bottom:5px;border-bottom:1px solid var(--border)">'+g.label+' ('+g.items.length+')</div>'+
|
|
6854
|
+
'<table style="width:100%;border-collapse:collapse;font-size:12px">'+
|
|
6855
|
+
'<thead><tr style="color:var(--text-xs);font-size:9px;text-align:left"><th style="padding:4px 8px">File</th><th>Type</th><th>Size</th><th>Modified</th><th></th></tr></thead>'+
|
|
6856
|
+
'<tbody>'+g.items.map(f => {
|
|
6857
|
+
return '<tr style="border-top:1px solid var(--border)" data-fp="'+esc(f.path)+'" data-fn="'+esc(f.name)+'">'+
|
|
6858
|
+
'<td style="padding:6px 8px"><span style="font-family:var(--mono);font-size:11px;color:var(--text-hi);cursor:pointer" onclick="v2ViewOrgFile(this.closest(\'tr\').dataset.fp,this.closest(\'tr\').dataset.fn)">'+esc(f.name)+'</span></td>'+
|
|
6859
|
+
'<td style="padding:6px 8px">'+badge(f.type)+'</td>'+
|
|
6860
|
+
'<td style="padding:6px 8px;font-size:10px;color:var(--text-lo);font-family:var(--mono)">'+fmtSize(f.size||0)+'</td>'+
|
|
6861
|
+
'<td style="padding:6px 8px;font-size:10px;color:var(--text-lo)">'+fmtTime(f.mtime)+'</td>'+
|
|
6862
|
+
'<td style="padding:6px 8px"><button class="btn" style="font-size:9px;padding:2px 7px" onclick="v2ViewOrgFile(this.closest(\'tr\').dataset.fp,this.closest(\'tr\').dataset.fn)">View</button></td></tr>';
|
|
6863
|
+
}).join('')+'</tbody></table></div>';
|
|
6864
|
+
}).join('')+'</div>';
|
|
6865
|
+
} catch(e) { el.innerHTML = '<div style="padding:20px;color:oklch(60% 0.22 25)">Error: '+e.message+'</div>'; }
|
|
6866
|
+
}
|
|
6867
|
+
|
|
6868
|
+
let _ofModal = null;
|
|
6869
|
+
let _ofRawText = '';
|
|
6870
|
+
|
|
6871
|
+
function _ofMdToHtml(md) {
|
|
6872
|
+
return md
|
|
6873
|
+
.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
|
|
6874
|
+
.replace(/^#{6}\s+(.+)$/gm,'<h6 style="font-size:11px;font-weight:700;color:var(--text-hi);margin:10px 0 4px">$1</h6>')
|
|
6875
|
+
.replace(/^#{5}\s+(.+)$/gm,'<h5 style="font-size:12px;font-weight:700;color:var(--text-hi);margin:10px 0 4px">$1</h5>')
|
|
6876
|
+
.replace(/^#{4}\s+(.+)$/gm,'<h4 style="font-size:13px;font-weight:700;color:var(--text-hi);margin:12px 0 5px">$1</h4>')
|
|
6877
|
+
.replace(/^#{3}\s+(.+)$/gm,'<h3 style="font-size:14px;font-weight:700;color:var(--text-hi);margin:14px 0 6px">$1</h3>')
|
|
6878
|
+
.replace(/^#{2}\s+(.+)$/gm,'<h2 style="font-size:16px;font-weight:700;color:var(--accent);margin:16px 0 7px;padding-bottom:4px;border-bottom:1px solid var(--border)">$1</h2>')
|
|
6879
|
+
.replace(/^#{1}\s+(.+)$/gm,'<h1 style="font-size:18px;font-weight:700;color:var(--accent);margin:18px 0 8px;padding-bottom:5px;border-bottom:1px solid var(--border)">$1</h1>')
|
|
6880
|
+
.replace(/```[\w]*\n?([\s\S]*?)```/g,'<pre style="background:var(--surface);border:1px solid var(--border);border-radius:4px;padding:10px 12px;font-size:10px;overflow-x:auto;margin:8px 0;line-height:1.6">$1</pre>')
|
|
6881
|
+
.replace(/`([^`]+)`/g,'<code style="background:var(--surface);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-size:10px;font-family:var(--mono)">$1</code>')
|
|
6882
|
+
.replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>')
|
|
6883
|
+
.replace(/\*(.+?)\*/g,'<em>$1</em>')
|
|
6884
|
+
.replace(/^---+$/gm,'<hr style="border:none;border-top:1px solid var(--border);margin:12px 0">')
|
|
6885
|
+
.replace(/^\s*[-*]\s+(.+)$/gm,'<li style="margin:2px 0;padding-left:4px">$1</li>')
|
|
6886
|
+
.replace(/(<li.*<\/li>\n?)+/g,'<ul style="padding-left:18px;margin:6px 0">$&</ul>')
|
|
6887
|
+
.replace(/^\d+\.\s+(.+)$/gm,'<li style="margin:2px 0;padding-left:4px">$1</li>')
|
|
6888
|
+
.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" style="color:var(--accent);text-decoration:underline" target="_blank" rel="noopener">$1</a>')
|
|
6889
|
+
.replace(/\n\n+/g,'</p><p style="margin:0 0 8px">')
|
|
6890
|
+
.replace(/^(?!<[hupoli]|<pre|<hr)(.+)$/gm, '$1')
|
|
6891
|
+
.replace(/\n/g,'<br>');
|
|
6892
|
+
}
|
|
6893
|
+
|
|
6894
|
+
async function v2ViewOrgFile(filePath, fileName) {
|
|
6895
|
+
if (_ofModal) { _ofModal.remove(); _ofModal = null; }
|
|
6896
|
+
const isMd = /\.(md|markdown)$/i.test(fileName);
|
|
6897
|
+
const isJson = /\.json$/i.test(fileName);
|
|
6898
|
+
_ofRawText = '';
|
|
6899
|
+
_ofModal = document.createElement('div');
|
|
6900
|
+
_ofModal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:9999;display:flex;align-items:center;justify-content:center;padding:20px;box-sizing:border-box';
|
|
6901
|
+
_ofModal.onclick = e => { if (e.target===_ofModal) { _ofModal.remove(); _ofModal=null; } };
|
|
6902
|
+
_ofModal.innerHTML =
|
|
6903
|
+
'<div style="background:var(--bg);border:1px solid var(--border);border-radius:8px;max-width:900px;width:100%;max-height:88vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 8px 40px rgba(0,0,0,0.6)">' +
|
|
6904
|
+
'<div style="display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid var(--border);flex-shrink:0">' +
|
|
6905
|
+
'<span style="font-family:var(--mono);font-size:11px;color:var(--text-hi);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="'+esc(filePath)+'">'+esc(fileName)+'</span>' +
|
|
6906
|
+
(isMd ? '<button id="of-toggle" class="btn" style="font-size:9px;padding:2px 7px" onclick="_ofToggleRender()">Raw</button>' : '') +
|
|
6907
|
+
'<button class="btn" style="font-size:9px;padding:2px 7px" onclick="navigator.clipboard.writeText(_ofRawText).then(()=>{ if(typeof showToast===\'function\') showToast(\'Copied\',\'\',\'ok\'); })">Copy</button>' +
|
|
6908
|
+
'<button class="btn" style="font-size:9px;padding:2px 7px;border-color:oklch(68% 0.20 150);color:oklch(68% 0.20 150)" onclick="_ofSaveFile(\''+esc(fileName)+'\')">Save</button>' +
|
|
6909
|
+
'<button class="btn" style="font-size:9px;padding:2px 7px" onclick="_ofModal&&(_ofModal.remove(),_ofModal=null)">✕</button>' +
|
|
6910
|
+
'</div>' +
|
|
6911
|
+
'<div id="of-content" style="flex:1;overflow-y:auto;padding:16px 18px;font-size:12px;line-height:1.7;color:var(--text-mid);scrollbar-width:thin;scrollbar-color:var(--border) transparent">' +
|
|
6912
|
+
'<span style="color:var(--text-lo)">Loading…</span>' +
|
|
6913
|
+
'</div>' +
|
|
6914
|
+
'</div>';
|
|
6915
|
+
document.body.appendChild(_ofModal);
|
|
6916
|
+
try {
|
|
6917
|
+
const dirParam = (typeof DIR !== 'undefined' && DIR) ? '&dir='+encodeURIComponent(DIR) : '';
|
|
6918
|
+
const r = await fetch('/api/file-content?path='+encodeURIComponent(filePath)+dirParam);
|
|
6919
|
+
const el = document.getElementById('of-content');
|
|
6920
|
+
if (!el) return;
|
|
6921
|
+
if (!r.ok) { el.textContent = 'Error '+r.status+': '+r.statusText; return; }
|
|
6922
|
+
_ofRawText = await r.text();
|
|
6923
|
+
if (isMd) {
|
|
6924
|
+
el.innerHTML = '<div style="max-width:720px">'+_ofMdToHtml(_ofRawText)+'</div>';
|
|
6925
|
+
el.dataset.mode = 'rendered';
|
|
6926
|
+
} else if (isJson) {
|
|
6927
|
+
try { el.textContent = JSON.stringify(JSON.parse(_ofRawText), null, 2); } catch(_) { el.textContent = _ofRawText; }
|
|
6928
|
+
el.style.fontFamily = 'var(--mono)'; el.style.fontSize = '11px'; el.style.whiteSpace = 'pre-wrap';
|
|
6929
|
+
} else {
|
|
6930
|
+
el.textContent = _ofRawText;
|
|
6931
|
+
el.style.fontFamily = 'var(--mono)'; el.style.fontSize = '11px'; el.style.whiteSpace = 'pre-wrap';
|
|
6932
|
+
}
|
|
6933
|
+
} catch(e) {
|
|
6934
|
+
const el = document.getElementById('of-content');
|
|
6935
|
+
if (el) el.textContent = 'Error: '+e.message;
|
|
6936
|
+
}
|
|
6937
|
+
}
|
|
6938
|
+
|
|
6939
|
+
function _ofToggleRender() {
|
|
6940
|
+
const el = document.getElementById('of-content');
|
|
6941
|
+
const btn = document.getElementById('of-toggle');
|
|
6942
|
+
if (!el || !_ofRawText) return;
|
|
6943
|
+
if (el.dataset.mode === 'rendered') {
|
|
6944
|
+
el.textContent = _ofRawText;
|
|
6945
|
+
el.style.fontFamily = 'var(--mono)'; el.style.fontSize = '11px'; el.style.whiteSpace = 'pre-wrap';
|
|
6946
|
+
el.dataset.mode = 'raw';
|
|
6947
|
+
if (btn) btn.textContent = 'Preview';
|
|
6948
|
+
} else {
|
|
6949
|
+
el.innerHTML = '<div style="max-width:720px">'+_ofMdToHtml(_ofRawText)+'</div>';
|
|
6950
|
+
el.style.fontFamily = ''; el.style.fontSize = '12px'; el.style.whiteSpace = '';
|
|
6951
|
+
el.dataset.mode = 'rendered';
|
|
6952
|
+
if (btn) btn.textContent = 'Raw';
|
|
6953
|
+
}
|
|
6954
|
+
}
|
|
6955
|
+
|
|
6956
|
+
function _ofSaveFile(defaultName) {
|
|
6957
|
+
if (!_ofRawText) return;
|
|
6958
|
+
const a = document.createElement('a');
|
|
6959
|
+
a.href = URL.createObjectURL(new Blob([_ofRawText], { type: 'text/plain' }));
|
|
6960
|
+
a.download = defaultName || 'file.txt';
|
|
6961
|
+
a.click();
|
|
6962
|
+
URL.revokeObjectURL(a.href);
|
|
6963
|
+
}
|
|
6964
|
+
|
|
6298
6965
|
// ── WORKSPACES ─────────────────────────────────────────────
|
|
6299
6966
|
async function v2RenderOrgWorkspaces() {
|
|
6300
6967
|
const el = document.getElementById('odt-workspaces');
|