@monoes/monomindcli 2.14.1 → 2.15.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.
Files changed (194) hide show
  1. package/.claude/helpers/control-start.cjs +10 -13
  2. package/.claude/skills/monodesign/scripts/context.mjs +4 -4
  3. package/.claude/skills/monodesign/scripts/critique-storage.mjs +1 -1
  4. package/.claude/skills/monodesign/scripts/detect-csp.mjs +1 -6
  5. package/.claude/skills/monodesign/scripts/detector/browser/injected/index.mjs +16 -18
  6. package/.claude/skills/monodesign/scripts/detector/cli/main.mjs +3 -3
  7. package/.claude/skills/monodesign/scripts/detector/detect-antipatterns-browser.js +67 -47
  8. package/.claude/skills/monodesign/scripts/detector/engines/regex/detect-text.mjs +6 -6
  9. package/.claude/skills/monodesign/scripts/detector/engines/static-html/css-cascade.mjs +6 -5
  10. package/.claude/skills/monodesign/scripts/detector/engines/static-html/detect-html.mjs +6 -6
  11. package/.claude/skills/monodesign/scripts/detector/fix/index.mjs +3 -3
  12. package/.claude/skills/monodesign/scripts/detector/node/file-system.mjs +1 -1
  13. package/.claude/skills/monodesign/scripts/detector/registry/antipatterns.mjs +1 -1
  14. package/.claude/skills/monodesign/scripts/detector/rules/checks.mjs +24 -25
  15. package/.claude/skills/monodesign/scripts/detector/shared/color.mjs +2 -2
  16. package/.claude/skills/monodesign/scripts/detector/shared/constants.mjs +1 -1
  17. package/.claude/skills/monodesign/scripts/detector/shared/inline-ignores.mjs +2 -2
  18. package/.claude/skills/monodesign/scripts/hook-admin.mjs +6 -6
  19. package/.claude/skills/monodesign/scripts/hook-before-edit.mjs +4 -4
  20. package/.claude/skills/monodesign/scripts/hook-lib.mjs +20 -11
  21. package/.claude/skills/monodesign/scripts/hook.mjs +1 -1
  22. package/.claude/skills/monodesign/scripts/lib/design-parser.mjs +9 -42
  23. package/.claude/skills/monodesign/scripts/lib/is-generated.mjs +1 -1
  24. package/.claude/skills/monodesign/scripts/lib/monodesign-config.mjs +5 -5
  25. package/.claude/skills/monodesign/scripts/lib/monodesign-paths.mjs +1 -1
  26. package/.claude/skills/monodesign/scripts/live/event-validation.mjs +13 -13
  27. package/.claude/skills/monodesign/scripts/live/manual-apply.mjs +4 -4
  28. package/.claude/skills/monodesign/scripts/live/manual-edits-buffer.mjs +1 -1
  29. package/.claude/skills/monodesign/scripts/live/session-store.mjs +5 -5
  30. package/.claude/skills/monodesign/scripts/live/svelte-component.mjs +13 -13
  31. package/.claude/skills/monodesign/scripts/live/sveltekit-adapter.mjs +4 -4
  32. package/.claude/skills/monodesign/scripts/live/ui-core.mjs +1 -1
  33. package/.claude/skills/monodesign/scripts/live-accept.mjs +24 -24
  34. package/.claude/skills/monodesign/scripts/live-browser-dom.js +8 -7
  35. package/.claude/skills/monodesign/scripts/live-browser-session.js +8 -7
  36. package/.claude/skills/monodesign/scripts/live-browser.js +405 -432
  37. package/.claude/skills/monodesign/scripts/live-commit-manual-edits.mjs +6 -6
  38. package/.claude/skills/monodesign/scripts/live-copy-edit-agent.mjs +2 -2
  39. package/.claude/skills/monodesign/scripts/live-discard-manual-edits.mjs +1 -1
  40. package/.claude/skills/monodesign/scripts/live-inject.mjs +7 -7
  41. package/.claude/skills/monodesign/scripts/live-insert.mjs +11 -11
  42. package/.claude/skills/monodesign/scripts/live-manual-edit-evidence.mjs +4 -4
  43. package/.claude/skills/monodesign/scripts/live-poll.mjs +5 -5
  44. package/.claude/skills/monodesign/scripts/live-server.mjs +10 -10
  45. package/.claude/skills/monodesign/scripts/live-wrap.mjs +31 -41
  46. package/.claude/skills/monodesign/scripts/live.mjs +5 -5
  47. package/.claude/skills/monodesign/scripts/palette.mjs +3 -1
  48. package/.claude/skills/monodesign/scripts/pin.mjs +1 -1
  49. package/dist/src/commands/doc-filters.d.ts.map +1 -1
  50. package/dist/src/commands/doc-filters.js +6 -0
  51. package/dist/src/commands/doc-filters.js.map +1 -1
  52. package/dist/src/commands/doc-library.d.ts +2 -1
  53. package/dist/src/commands/doc-library.d.ts.map +1 -1
  54. package/dist/src/commands/doc-library.js +66 -2
  55. package/dist/src/commands/doc-library.js.map +1 -1
  56. package/dist/src/commands/doc.d.ts.map +1 -1
  57. package/dist/src/commands/doc.js +2 -0
  58. package/dist/src/commands/doc.js.map +1 -1
  59. package/dist/src/commands/ui.d.ts.map +1 -1
  60. package/dist/src/commands/ui.js +60 -0
  61. package/dist/src/commands/ui.js.map +1 -1
  62. package/dist/src/knowledge/capture-envelope.d.ts +5 -0
  63. package/dist/src/knowledge/capture-envelope.d.ts.map +1 -1
  64. package/dist/src/knowledge/capture-envelope.js +2 -0
  65. package/dist/src/knowledge/capture-envelope.js.map +1 -1
  66. package/dist/src/knowledge/document-chunking.d.ts +42 -0
  67. package/dist/src/knowledge/document-chunking.d.ts.map +1 -0
  68. package/dist/src/knowledge/document-chunking.js +288 -0
  69. package/dist/src/knowledge/document-chunking.js.map +1 -0
  70. package/dist/src/knowledge/document-index.d.ts +124 -0
  71. package/dist/src/knowledge/document-index.d.ts.map +1 -0
  72. package/dist/src/knowledge/document-index.js +314 -0
  73. package/dist/src/knowledge/document-index.js.map +1 -0
  74. package/dist/src/knowledge/document-ingest.d.ts +23 -0
  75. package/dist/src/knowledge/document-ingest.d.ts.map +1 -0
  76. package/dist/src/knowledge/document-ingest.js +368 -0
  77. package/dist/src/knowledge/document-ingest.js.map +1 -0
  78. package/dist/src/knowledge/document-pipeline.d.ts +26 -208
  79. package/dist/src/knowledge/document-pipeline.d.ts.map +1 -1
  80. package/dist/src/knowledge/document-pipeline.js +25 -1104
  81. package/dist/src/knowledge/document-pipeline.js.map +1 -1
  82. package/dist/src/knowledge/document-search.d.ts +27 -0
  83. package/dist/src/knowledge/document-search.d.ts.map +1 -0
  84. package/dist/src/knowledge/document-search.js +138 -0
  85. package/dist/src/knowledge/document-search.js.map +1 -0
  86. package/dist/src/knowledge/document-store.d.ts +28 -0
  87. package/dist/src/knowledge/document-store.d.ts.map +1 -0
  88. package/dist/src/knowledge/document-store.js +61 -0
  89. package/dist/src/knowledge/document-store.js.map +1 -0
  90. package/dist/src/knowledge/document-types.d.ts +103 -0
  91. package/dist/src/knowledge/document-types.d.ts.map +1 -0
  92. package/dist/src/knowledge/document-types.js +12 -0
  93. package/dist/src/knowledge/document-types.js.map +1 -0
  94. package/dist/src/knowledge/highlights.d.ts +168 -0
  95. package/dist/src/knowledge/highlights.d.ts.map +1 -0
  96. package/dist/src/knowledge/highlights.js +311 -0
  97. package/dist/src/knowledge/highlights.js.map +1 -0
  98. package/dist/src/knowledge/lookup.d.ts +80 -0
  99. package/dist/src/knowledge/lookup.d.ts.map +1 -0
  100. package/dist/src/knowledge/lookup.js +156 -0
  101. package/dist/src/knowledge/lookup.js.map +1 -0
  102. package/dist/src/knowledge/okf-bundle.d.ts +19 -0
  103. package/dist/src/knowledge/okf-bundle.d.ts.map +1 -0
  104. package/dist/src/knowledge/okf-bundle.js +106 -0
  105. package/dist/src/knowledge/okf-bundle.js.map +1 -0
  106. package/dist/src/knowledge/profile-store.d.ts +134 -0
  107. package/dist/src/knowledge/profile-store.d.ts.map +1 -0
  108. package/dist/src/knowledge/profile-store.js +237 -0
  109. package/dist/src/knowledge/profile-store.js.map +1 -0
  110. package/dist/src/orgrt/agent-runner.d.ts +4 -0
  111. package/dist/src/orgrt/agent-runner.d.ts.map +1 -1
  112. package/dist/src/orgrt/agent-runner.js +22 -0
  113. package/dist/src/orgrt/agent-runner.js.map +1 -1
  114. package/dist/src/orgrt/antigravity-runner.d.ts.map +1 -1
  115. package/dist/src/orgrt/antigravity-runner.js +2 -1
  116. package/dist/src/orgrt/antigravity-runner.js.map +1 -1
  117. package/dist/src/orgrt/authority-mask.d.ts +32 -0
  118. package/dist/src/orgrt/authority-mask.d.ts.map +1 -0
  119. package/dist/src/orgrt/authority-mask.js +135 -0
  120. package/dist/src/orgrt/authority-mask.js.map +1 -0
  121. package/dist/src/orgrt/codex-runner.d.ts.map +1 -1
  122. package/dist/src/orgrt/codex-runner.js +2 -1
  123. package/dist/src/orgrt/codex-runner.js.map +1 -1
  124. package/dist/src/orgrt/copilot-runner.d.ts.map +1 -1
  125. package/dist/src/orgrt/copilot-runner.js +2 -1
  126. package/dist/src/orgrt/copilot-runner.js.map +1 -1
  127. package/dist/src/orgrt/crush-runner.d.ts.map +1 -1
  128. package/dist/src/orgrt/crush-runner.js +6 -1
  129. package/dist/src/orgrt/crush-runner.js.map +1 -1
  130. package/dist/src/orgrt/daemon.d.ts +4 -0
  131. package/dist/src/orgrt/daemon.d.ts.map +1 -1
  132. package/dist/src/orgrt/daemon.js +13 -2
  133. package/dist/src/orgrt/daemon.js.map +1 -1
  134. package/dist/src/orgrt/decisions.d.ts +9 -0
  135. package/dist/src/orgrt/decisions.d.ts.map +1 -1
  136. package/dist/src/orgrt/decisions.js +32 -5
  137. package/dist/src/orgrt/decisions.js.map +1 -1
  138. package/dist/src/orgrt/file-roots.d.ts +2 -1
  139. package/dist/src/orgrt/file-roots.d.ts.map +1 -1
  140. package/dist/src/orgrt/file-roots.js +7 -3
  141. package/dist/src/orgrt/file-roots.js.map +1 -1
  142. package/dist/src/orgrt/grok-runner.d.ts.map +1 -1
  143. package/dist/src/orgrt/grok-runner.js +2 -1
  144. package/dist/src/orgrt/grok-runner.js.map +1 -1
  145. package/dist/src/orgrt/hermes-runner.d.ts.map +1 -1
  146. package/dist/src/orgrt/hermes-runner.js +2 -1
  147. package/dist/src/orgrt/hermes-runner.js.map +1 -1
  148. package/dist/src/orgrt/inbox.d.ts.map +1 -1
  149. package/dist/src/orgrt/inbox.js +70 -4
  150. package/dist/src/orgrt/inbox.js.map +1 -1
  151. package/dist/src/orgrt/kimicode-runner.d.ts.map +1 -1
  152. package/dist/src/orgrt/kimicode-runner.js +2 -1
  153. package/dist/src/orgrt/kimicode-runner.js.map +1 -1
  154. package/dist/src/orgrt/opencode-runner.d.ts.map +1 -1
  155. package/dist/src/orgrt/opencode-runner.js +3 -2
  156. package/dist/src/orgrt/opencode-runner.js.map +1 -1
  157. package/dist/src/orgrt/pi-rpc-runner.d.ts.map +1 -1
  158. package/dist/src/orgrt/pi-rpc-runner.js +3 -2
  159. package/dist/src/orgrt/pi-rpc-runner.js.map +1 -1
  160. package/dist/src/orgrt/pi-runner.d.ts.map +1 -1
  161. package/dist/src/orgrt/pi-runner.js +2 -1
  162. package/dist/src/orgrt/pi-runner.js.map +1 -1
  163. package/dist/src/orgrt/policy.d.ts.map +1 -1
  164. package/dist/src/orgrt/policy.js +3 -0
  165. package/dist/src/orgrt/policy.js.map +1 -1
  166. package/dist/src/orgrt/qwen-rpc-runner.d.ts.map +1 -1
  167. package/dist/src/orgrt/qwen-rpc-runner.js +3 -2
  168. package/dist/src/orgrt/qwen-rpc-runner.js.map +1 -1
  169. package/dist/src/orgrt/qwen-runner.d.ts.map +1 -1
  170. package/dist/src/orgrt/qwen-runner.js +2 -1
  171. package/dist/src/orgrt/qwen-runner.js.map +1 -1
  172. package/dist/src/orgrt/role-sandbox.d.ts +21 -0
  173. package/dist/src/orgrt/role-sandbox.d.ts.map +1 -1
  174. package/dist/src/orgrt/role-sandbox.js +31 -6
  175. package/dist/src/orgrt/role-sandbox.js.map +1 -1
  176. package/dist/src/orgrt/session.d.ts.map +1 -1
  177. package/dist/src/orgrt/session.js +21 -2
  178. package/dist/src/orgrt/session.js.map +1 -1
  179. package/dist/src/orgrt/task-dag.d.ts +3 -0
  180. package/dist/src/orgrt/task-dag.d.ts.map +1 -1
  181. package/dist/src/orgrt/task-dag.js +1 -0
  182. package/dist/src/orgrt/task-dag.js.map +1 -1
  183. package/dist/src/orgrt/types.d.ts +5 -0
  184. package/dist/src/orgrt/types.d.ts.map +1 -1
  185. package/dist/src/orgrt/types.js +23 -1
  186. package/dist/src/orgrt/types.js.map +1 -1
  187. package/dist/src/ui/dashboard.html +28 -848
  188. package/dist/src/ui/human-auth.mjs +103 -0
  189. package/dist/src/ui/org-hil.mjs +86 -37
  190. package/dist/src/ui/org-runtime.mjs +21 -13
  191. package/dist/src/ui/routes-org.mjs +49 -445
  192. package/dist/src/ui/server.mjs +90 -2
  193. package/dist/tsconfig.tsbuildinfo +1 -1
  194. package/package.json +6 -6
@@ -421,12 +421,9 @@ html, body { height: 100%; background: var(--bg); color: var(--text-hi); font-fa
421
421
  .oad-doc strong { color: var(--text-hi); font-weight: 600; }
422
422
  .oad-doc pre { background: var(--surface-hi); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; overflow-x: auto; margin: 0 0 10px; }
423
423
  .oad-doc pre code { background: none; padding: 0; font-size: 11.5px; color: var(--text-mid); }
424
- /* clickable org chart nodes + role cards */
424
+ /* clickable org chart nodes */
425
425
  .v2oc-node { cursor: pointer; }
426
426
  .v2oc-node.selected > circle { stroke-width: 3.4; }
427
- .role-v2 { cursor: pointer; transition: border-color 0.12s, transform 0.12s; }
428
- .role-v2:hover { transform: translateY(-1px); }
429
- .rv2-more { margin-top: 9px; font-size: 10.5px; color: var(--accent); opacity: 0.85; }
430
427
 
431
428
  /* org meta strip (cells reused inside #org-meta-modal) */
432
429
  .oms-cell { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); padding: 10px 12px; }
@@ -513,20 +510,6 @@ html, body { height: 100%; background: var(--bg); color: var(--text-hi); font-fa
513
510
  .org-chart-legend { display: flex; gap: 14px; flex-wrap: wrap; padding: 0 2px; }
514
511
  .ocl-item { display: flex; align-items: center; gap: 5px; font-size: 10px; color: var(--text-lo); }
515
512
 
516
- /* roles in v2 */
517
- .roles-v2-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; }
518
- .role-v2 { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); padding: 12px 14px; }
519
- .rv2-head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 6px; }
520
- .rv2-avatar { width: 48px; height: 48px; border-radius: 50%; flex-shrink: 0; object-fit: cover; }
521
- .rv2-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; padding-top: 2px; }
522
- .rv2-title { font-size: 13px; font-weight: 500; color: var(--text-hi); line-height: 1.3; }
523
- .rv2-agent { font-size: 10px; padding: 1px 7px; border-radius: 8px; background: var(--accent-dim); color: var(--accent); white-space: nowrap; align-self: flex-start; }
524
- .rv2-id { font-size: 10px; color: var(--text-lo); font-family: var(--mono); margin-bottom: 4px; }
525
- .rv2-reports { font-size: 11px; color: var(--text-lo); margin-bottom: 6px; }
526
- .rv2-reports span { color: var(--text-mid); }
527
- .rv2-resps { }
528
- .rv2-resp { font-size: 11px; color: var(--text-lo); line-height: 1.6; display: flex; gap: 6px; }
529
- .rv2-resp::before { content: '–'; opacity: 0.4; flex-shrink: 0; }
530
513
  /* role avatars in org list */
531
514
  .oi-avatars { display: flex; margin-top: 5px; }
532
515
  .oi-av { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; margin-right: -5px; border: 1.5px solid var(--bg); }
@@ -542,15 +525,6 @@ html, body { height: 100%; background: var(--bg); color: var(--text-hi); font-fa
542
525
  .av2-type { color: var(--accent); font-weight: 500; }
543
526
  .av2-msg { color: var(--text-lo); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
544
527
 
545
- /* health in v2 */
546
- .health-v2-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 8px; margin-bottom: 12px; }
547
- .hv2-cell { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); padding: 10px 12px; }
548
- .hv2-lbl { font-size: 10px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-lo); margin-bottom: 4px; }
549
- .hv2-val { font-size: 20px; font-weight: 700; color: var(--text-hi); font-family: var(--mono); }
550
- .hv2-val.green { color: var(--green); }
551
- .hv2-val.amber { color: var(--accent); }
552
- .hv2-val.red { color: var(--red); }
553
-
554
528
  /* ── alerts rail ─────────────────────────────────────────── */
555
529
  #alerts-rail { display: none; }
556
530
  #alerts-rail.has-alerts { display: none; }
@@ -2804,7 +2778,6 @@ function switchProject(path) {
2804
2778
  _v2SelOrg = null;
2805
2779
  _v2OrgData = null;
2806
2780
  _v2Orgs = [];
2807
- Object.keys(_v2OrgEventLog).forEach(k => delete _v2OrgEventLog[k]);
2808
2781
  document.getElementById('org-detail-pane')?.classList.add('empty-state');
2809
2782
  document.getElementById('org-detail-inner') && (document.getElementById('org-detail-inner').style.display = 'none');
2810
2783
  const _noSel = document.querySelector('#org-detail-pane .orgs-no-sel');
@@ -4498,7 +4471,15 @@ async function renderHumanInputView() {
4498
4471
  let _hiReloadTimer = null;
4499
4472
  function _hiScheduleReload() {
4500
4473
  clearTimeout(_hiReloadTimer);
4501
- _hiReloadTimer = setTimeout(renderHumanInputView, 400);
4474
+ _hiReloadTimer = setTimeout(() => {
4475
+ // Drafts survive the rebuild (renderHiCurrentTab); still wait while one is
4476
+ // being typed, or while its row has focus (a press on its Answer button),
4477
+ // so the row is not replaced under the cursor or the click.
4478
+ const typing = [...document.querySelectorAll('#hi-content .hi-answer-input')]
4479
+ .some(i => i.value.trim() && i.closest('.sess-row')?.contains(document.activeElement));
4480
+ if (typing) _hiScheduleReload();
4481
+ else renderHumanInputView();
4482
+ }, 400);
4502
4483
  }
4503
4484
 
4504
4485
  function _hiSwitchTab(tab) {
@@ -4581,7 +4562,12 @@ function renderHiCurrentTab(projectCount, errors) {
4581
4562
  el.innerHTML = `${errHtml}<div class="empty"><div class="empty-ico">❓</div><div>${_hiTab === 'answered' ? 'Nothing resolved yet' : 'Nothing waiting on you'}</div></div>`;
4582
4563
  return;
4583
4564
  }
4565
+ // Rebuilding the list must not lose an answer or note typed into a row.
4566
+ const draftKey = i => { const b = i.closest('.sess-row')?.querySelector('[data-id]'); return b ? [b.dataset.kind, b.dataset.dir, b.dataset.org, b.dataset.id].join('\n') : null; };
4567
+ const drafts = new Map();
4568
+ el.querySelectorAll('.hi-answer-input').forEach(i => { const k = draftKey(i); if (k && i.value) drafts.set(k, i.value); });
4584
4569
  el.innerHTML = `${errHtml}<div class="sess-list">${entries.map(_hiEntryHtml).join('')}</div>`;
4570
+ if (drafts.size) el.querySelectorAll('.hi-answer-input').forEach(i => { const k = draftKey(i); if (k && drafts.has(k)) i.value = drafts.get(k); });
4585
4571
  }
4586
4572
 
4587
4573
  function updateHumanInputBadge(count) {
@@ -5805,7 +5791,6 @@ let _v2Orgs = [];
5805
5791
  let _v2SelOrg = null;
5806
5792
  let _v2OrgData = null;
5807
5793
  let _v2OrgTab = 'chart';
5808
- const _v2OrgEventLog = {};
5809
5794
  let _v2ChartLayout = { pos: {}, edgeIdByPair: new Map() };
5810
5795
 
5811
5796
  function _orgActivityMs(o) {
@@ -5880,7 +5865,6 @@ async function v2SelectOrg(name) {
5880
5865
  _v2SelOrg = name;
5881
5866
  _pushHash();
5882
5867
  if (typeof v2CloseAgent === 'function') v2CloseAgent();
5883
- if (_orgLiveInterval) { clearInterval(_orgLiveInterval); _orgLiveInterval = null; }
5884
5868
  const requested = name;
5885
5869
  const requestedDir = DIR;
5886
5870
  document.querySelectorAll('.org-item').forEach(el => {
@@ -5905,30 +5889,17 @@ async function v2SelectOrg(name) {
5905
5889
  _v2OrgData = null;
5906
5890
  const _enc = encodeURIComponent(name);
5907
5891
  try {
5908
- const [mainR, actR, healthR] = await Promise.all([
5909
- apiFetch(`/api/org/${_enc}${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`),
5910
- fetch(`/api/org/${_enc}/activity${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r=>r.ok?r.json():[]).catch(()=>[]),
5911
- fetch(`/api/org/${_enc}/health${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r=>r.ok?r.json():null).catch(()=>null),
5912
- ]);
5892
+ const mainR = await apiFetch(`/api/org/${_enc}${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`);
5913
5893
  if (_v2SelOrg !== requested || DIR !== requestedDir) return;
5914
5894
  // API returns {config, state, goals, ...} — flatten config; config wins for schema fields
5915
5895
  _v2OrgData = mainR.config ? { ...mainR, ...mainR.config, running: mainR.running } : mainR;
5916
- _v2OrgData._activity = Array.isArray(actR) ? actR : [];
5917
- _v2OrgData._health = healthR;
5918
- // Fetch supplemental data for new tabs in parallel
5919
- const [agentsR, membersR, issuesR] = await Promise.all([
5920
- fetch(`/api/org/${_enc}/agents${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r=>r.ok?r.json():[]).catch(()=>[]),
5921
- fetch(`/api/org/${_enc}/members${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r=>r.ok?r.json():[]).catch(()=>[]),
5922
- fetch(`/api/org/${_enc}/issues${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r=>r.ok?r.json():[]).catch(()=>[]),
5923
- ]);
5896
+ // The chart's running dots read the agent roster
5897
+ const agentsR = await fetch(`/api/org/${_enc}/agents${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
5924
5898
  if (_v2SelOrg !== requested || DIR !== requestedDir) return;
5925
5899
  _v2OrgData._agents = Array.isArray(agentsR) ? agentsR : (agentsR?.agents || []);
5926
- _v2OrgData._members = Array.isArray(membersR) ? membersR : (membersR?.members || []);
5927
- _v2OrgData._joinRequests = (membersR?.join_requests) || [];
5928
- _v2OrgData._issues = Array.isArray(issuesR) ? issuesR : (issuesR?.issues || []);
5929
5900
  } catch (_) {
5930
5901
  if (_v2SelOrg !== requested || DIR !== requestedDir) return;
5931
- _v2OrgData = { ...listOrg, communication: [], _activity: [], _health: null, _agents: [], _members: [], _issues: [] };
5902
+ _v2OrgData = { ...listOrg, communication: [], _agents: [] };
5932
5903
  }
5933
5904
 
5934
5905
  v2UpdateOrgHeader(listOrg, _v2OrgData);
@@ -5963,11 +5934,6 @@ window.v2MarkOrgComplete = async () => {
5963
5934
  };
5964
5935
 
5965
5936
  window.v2SwitchOrgTab = (tab) => {
5966
- // Clear live interval when switching away from live tab
5967
- if (tab !== 'live' && _orgLiveInterval) {
5968
- clearInterval(_orgLiveInterval);
5969
- _orgLiveInterval = null;
5970
- }
5971
5937
  _v2OrgTab = tab;
5972
5938
  document.querySelectorAll('#org-detail-tabs .odt-btn').forEach(b => {
5973
5939
  b.classList.toggle('active', b.dataset.tab === tab);
@@ -5986,30 +5952,8 @@ function v2RenderOrgTab(tab) {
5986
5952
  }
5987
5953
  if (tab === 'chart') v2RenderOrgChart();
5988
5954
  else if (tab === 'runtime') v2RenderOrgRuntime();
5989
- else if (tab === 'roles') v2RenderOrgRoles();
5990
- else if (tab === 'health') v2RenderOrgHealth();
5991
- else if (tab === 'heartbeats') v2RenderOrgHeartbeats();
5992
- else if (tab === 'tasks') v2RenderOrgTasks();
5993
- else if (tab === 'members') v2RenderOrgMembers();
5994
- else if (tab === 'goals') v2RenderOrgGoals();
5995
- else if (tab === 'board') v2RenderOrgBoard();
5996
- else if (tab === 'live') v2RenderOrgLive();
5997
5955
  else if (tab === 'approvals') v2RenderOrgApprovals();
5998
- else if (tab === 'secrets') v2RenderOrgSecrets();
5999
- else if (tab === 'settings') v2RenderOrgSettings();
6000
- else if (tab === 'routines') v2RenderOrgRoutines();
6001
- else if (tab === 'myissues') v2RenderOrgMyIssues();
6002
5956
  else if (tab === 'decisions') v2RenderOrgDecisions();
6003
- else if (tab === 'plugins') v2RenderOrgPlugins();
6004
- else if (tab === 'charts') v2RenderOrgCharts();
6005
- else if (tab === 'projects') v2RenderOrgProjects();
6006
- else if (tab === 'workspaces') v2RenderOrgWorkspaces();
6007
- else if (tab === 'invites') v2RenderOrgInvites();
6008
- else if (tab === 'environments') v2RenderOrgEnvironments();
6009
- else if (tab === 'access') v2RenderOrgAccess();
6010
- else if (tab === 'issues-full') v2RenderOrgIssuesFull();
6011
- else if (tab === 'join-requests') v2RenderOrgJoinRequests();
6012
- else if (tab === 'threads') v2RenderOrgThreads();
6013
5957
  else if (tab === 'chat') v2RenderOrgChat();
6014
5958
  else if (tab === 'config') v2RenderOrgConfig();
6015
5959
  else if (tab === 'files') v2RenderOrgFiles();
@@ -6758,47 +6702,6 @@ function v2RoleAvatar(role, avatarMap) {
6758
6702
  return `data/avatars/${_v2AvatarCandidates(role)[0]}.svg`;
6759
6703
  }
6760
6704
 
6761
- // ── org roles ───────────────────────────────
6762
- function v2RenderOrgRoles() {
6763
- if (!_v2SelOrg) return;
6764
- const roles = Array.isArray(_v2OrgData?.roles) ? _v2OrgData.roles : [];
6765
- const V2_ROLE_COLORS = ['oklch(72% 0.18 75)','oklch(65% 0.12 240)','oklch(65% 0.15 150)','oklch(65% 0.13 290)','oklch(65% 0.14 35)','oklch(62% 0.12 195)'];
6766
- const pane = document.getElementById('odt-roles');
6767
- if (!pane) return;
6768
- if (!roles.length) {
6769
- pane.innerHTML = '<div class="empty">No roles defined</div>';
6770
- return;
6771
- }
6772
- // Determine leader: explicit reports_to=undefined + type=planner/coordinator, or first role, or id=boss
6773
- const leaderIds = new Set(roles.filter(r => r.id === 'boss' || r.type === 'coordinator' || r.type === 'planner').map(r => r.id));
6774
- if (!leaderIds.size) leaderIds.add(roles[0]?.id);
6775
- // Overlay live running status from in-memory agent SSE state
6776
- const _rolesRunning = new Set((_v2OrgData?._agents || []).filter(a => a.status === 'running').map(a => a.id));
6777
- const _rolesAvMap = v2AvatarMapFor(roles);
6778
- pane.innerHTML = `<div class="roles-v2-grid">${roles.map((r, i) => {
6779
- const color = V2_ROLE_COLORS[i % V2_ROLE_COLORS.length];
6780
- const isLeader = leaderIds.has(r.id);
6781
- const isRunning = _rolesRunning.has(r.id);
6782
- const resps = Array.isArray(r.responsibilities) ? r.responsibilities : [];
6783
- const displayName = r.name || r.title || r.id;
6784
- const roleType = r.type || r.agent_type || '';
6785
- const avatarSrc = v2RoleAvatar(r, _rolesAvMap);
6786
- return `<div class="role-v2" style="border-top:2px solid ${color}" onclick="v2OpenAgent('${String(r.id).replace(/['\\]/g,'\\$&')}')" title="View agent characteristics, skills & instructions">
6787
- <div class="rv2-head">
6788
- <img class="rv2-avatar" src="${esc(avatarSrc)}" alt="${esc(displayName)}" loading="lazy" width="48" height="48" onerror="this.src='data/avatars/coder.svg'"/>
6789
- <div class="rv2-info">
6790
- <div class="rv2-title">${esc(displayName)}${isLeader ? ' <span style="font-size:10px;color:var(--accent);font-weight:400;opacity:0.8">lead</span>' : ''}${isRunning ? ' <span class="live-dot" style="display:inline-block;vertical-align:middle;margin-left:4px"></span>' : ''}</div>
6791
- ${roleType ? `<span class="rv2-agent">${esc(roleType)}</span>` : ''}
6792
- </div>
6793
- </div>
6794
- <div class="rv2-id">${esc(r.id)}</div>
6795
- ${r.reports_to ? `<div class="rv2-reports">reports to <span>${esc(r.reports_to)}</span></div>` : ''}
6796
- ${resps.length ? `<div class="rv2-resps">${resps.map(s=>`<div class="rv2-resp">${esc(s)}</div>`).join('')}</div>` : ''}
6797
- <div class="rv2-more">View characteristics, skills &amp; instructions →</div>
6798
- </div>`;
6799
- }).join('')}</div>`;
6800
- }
6801
-
6802
6705
  // ── agent detail drawer ──────────────────────
6803
6706
  function v2HighlightAgentNode(roleId) {
6804
6707
  document.querySelectorAll('#v2oc-nodes .v2oc-node').forEach(n =>
@@ -6915,7 +6818,6 @@ async function _v2HandleAvatarFile(inputEl) {
6915
6818
  const imgEl = document.getElementById('oad-avatar-img');
6916
6819
  if (imgEl) imgEl.src = dataUrl;
6917
6820
  showToast('Avatar updated', roleId, 'ok');
6918
- if (document.getElementById('odt-roles')) v2RenderOrgRoles();
6919
6821
  if (_v2SelOrg) v2RenderOrgChart();
6920
6822
  } catch (e) { showToast('Error', e.message, 'err'); }
6921
6823
  }
@@ -6977,226 +6879,6 @@ function v2RenderMarkdown(md) {
6977
6879
  return html;
6978
6880
  }
6979
6881
 
6980
- // ── org activity ────────────────────────────
6981
- function fmtOrgEvDetail(ev) {
6982
- if (ev.type === 'org:comms' && (ev.from || ev.role || ev.to || ev.msg)) {
6983
- const sender = ev.from || ev.role || '';
6984
- const route = [sender, ev.to].filter(Boolean).join(' → ');
6985
- return route ? route + (ev.msg ? `: ${ev.msg}` : '') : ev.msg || '';
6986
- }
6987
- if (ev.type === 'org:checkpoint') return ev.summary || ev.progress || '';
6988
- if (ev.type === 'agent:result') return (ev.agent || ev.from || '') + (ev.result || ev.msg || ev.message || ev.summary ? `: ${ev.result || ev.msg || ev.message || ev.summary}` : '');
6989
- if (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline') return ev.title || ev.role || ev.agent || ev.id || ev.from || '';
6990
- if (ev.type === 'org:error') return ev.error || ev.msg || ev.message || '';
6991
- if (ev.type === 'file:write') return ev.path || ev.file || ev.msg || '';
6992
- return ev.role||ev.result||ev.msg||ev.message||ev.progress||ev.agent||ev.status||'';
6993
- }
6994
-
6995
- // ── org health ──────────────────────────────
6996
- function v2RenderOrgHealth() {
6997
- if (!_v2SelOrg) return;
6998
- const listOrg = _v2Orgs.find(o => o.name === _v2SelOrg) || {};
6999
- const health = _v2OrgData?._health;
7000
- const roles = Array.isArray(_v2OrgData?.roles) ? _v2OrgData.roles.length : 0;
7001
- const pane = document.getElementById('odt-health');
7002
- if (!pane) return;
7003
- pane.innerHTML = `
7004
- <div class="health-v2-grid">
7005
- <div class="hv2-cell"><div class="hv2-lbl">Status</div><div class="hv2-val ${(()=>{const r=_v2OrgData?.running??listOrg.running;if(!r)return '';const a=_v2OrgData?.lastEventAt??listOrg.lastEventAt;return a&&(Date.now()-a)<1800000?'green':'amber';})()} ">${(()=>{const r=_v2OrgData?.running??listOrg.running;if(!r)return 'IDLE';const a=_v2OrgData?.lastEventAt??listOrg.lastEventAt;if(!a)return '🟡 QUIET';const d=Date.now()-a;return d<1800000?'🟢 LIVE':d<7200000?'🟡 QUIET':'🔴 STALE';})()}</div></div>
7006
- <div class="hv2-cell"><div class="hv2-lbl">Roles</div><div class="hv2-val">${roles}</div></div>
7007
- <div class="hv2-cell"><div class="hv2-lbl">Topology</div><div class="hv2-val" style="font-size:14px">${esc((_v2OrgData?.topology||'—').toUpperCase())}</div></div>
7008
- ${health?.agents_active!=null?`<div class="hv2-cell"><div class="hv2-lbl">Agents</div><div class="hv2-val ${health.agents_active>0?'green':''}">${health.agents_active}</div></div>`:''}
7009
- ${health?.tasks_pending!=null?`<div class="hv2-cell"><div class="hv2-lbl">Tasks</div><div class="hv2-val ${health.tasks_pending>5?'amber':''}">${health.tasks_pending}</div></div>`:''}
7010
- ${health?.run_success_rate_7d!=null?`<div class="hv2-cell"><div class="hv2-lbl">7d Success</div><div class="hv2-val ${health.run_success_rate_7d>=80?'green':health.run_success_rate_7d<50?'amber':''}">${health.run_success_rate_7d}%</div></div>`:''}
7011
- ${health?.total_runs_7d!=null?`<div class="hv2-cell"><div class="hv2-lbl">7d Runs</div><div class="hv2-val">${health.total_runs_7d}</div></div>`:''}
7012
- ${health?.budget_used_pct!=null?`<div class="hv2-cell"><div class="hv2-lbl">Budget Used</div><div class="hv2-val ${health.budget_used_pct>=90?'amber':''}">${health.budget_used_pct}%</div></div>`:''}
7013
- </div>
7014
- ${health?.budget_used_pct!=null&&health?.budget_max_tokens>0?`<div style="margin:10px 0 12px"><div style="height:4px;background:var(--border);border-radius:2px;overflow:hidden"><div style="width:${Math.min(100,health.budget_used_pct)}%;height:100%;background:${health.budget_used_pct>=90?'var(--red)':health.budget_used_pct>=70?'oklch(70% 0.18 60)':'var(--accent)'};border-radius:2px"></div></div><div style="font-size:10px;color:var(--text-lo);margin-top:4px">${(health.budget_used_tokens??0).toLocaleString()} / ${health.budget_max_tokens.toLocaleString()} tokens</div></div>`:''}
7015
- ${health?.errors?.length?`<div style="margin-top:12px"><div class="m-group-title">Errors</div>${health.errors.slice(0,5).map(e=>`<div style="font-size:11px;color:var(--red);padding:4px 0;border-bottom:1px solid var(--border)">${esc(e)}</div>`).join('')}</div>`:`<div style="margin-top:10px;font-size:12px;color:var(--text-lo)">${listOrg.running?'Running — no errors detected':'Org is idle'}</div>`}
7016
- `;
7017
- }
7018
-
7019
- // ── org heartbeats tab ─────────────────────────────────────
7020
- function v2RenderOrgHeartbeats() {
7021
- if (!_v2SelOrg) return;
7022
- const pane = document.getElementById('odt-heartbeats');
7023
- if (!pane) return;
7024
- // Derive from the agent roster — each agent carries status + lastHeartbeat
7025
- let hb = Array.isArray(_v2OrgData?.heartbeats) ? _v2OrgData.heartbeats : [];
7026
- if (!hb.length && Array.isArray(_v2OrgData?._agents)) {
7027
- hb = _v2OrgData._agents.map(a => ({
7028
- agent: a.title || a.id || '—',
7029
- ts: a.lastHeartbeat ?? a.last_heartbeat ?? a.ts ?? null,
7030
- status: a.status || 'idle',
7031
- }));
7032
- }
7033
- if (!hb.length) { pane.innerHTML = '<div class="empty">No agents to report</div>'; return; }
7034
- const cls = (st) => (st === 'active' || st === 'running') ? 'on' : ((st === 'error' || st === 'failed') ? 'warn' : '');
7035
- pane.innerHTML = `<div class="m-group-title">Agent Heartbeats</div>` +
7036
- hb.slice(0, 50).map(h => `<div style="display:flex;justify-content:space-between;align-items:center;gap:10px;padding:6px 0;border-bottom:1px solid var(--border);font-size:12px">
7037
- <span style="color:var(--text-hi);font-family:var(--mono);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(h.agent || '—')}</span>
7038
- <span style="color:var(--text-lo);white-space:nowrap">${h.ts ? relTime(h.ts) : 'never'}</span>
7039
- <span class="ss-pill ${cls(h.status)}">${esc(String(h.status || 'idle').toUpperCase())}</span>
7040
- </div>`).join('');
7041
- }
7042
-
7043
- // ── org tasks tab ──────────────────────────────────────────
7044
- function v2RenderOrgTasks() {
7045
- if (!_v2SelOrg) return;
7046
- const pane = document.getElementById('odt-tasks');
7047
- if (!pane) return;
7048
- // Normalize: API returns {todo:[],doing:[],done:[]} (columns) or a flat array
7049
- const raw = _v2OrgData?.tasks;
7050
- // Server puts completed/failed/cancelled in the 'done' column but keeps original status string
7051
- const _normTaskStatus = (st, col) => {
7052
- if (st === 'completed' || st === 'failed' || st === 'cancelled') return 'done';
7053
- if (st === 'in_progress') return 'doing';
7054
- return st || col;
7055
- };
7056
- let tasks = [];
7057
- if (Array.isArray(raw)) tasks = raw.map(t => ({ ...t, status: _normTaskStatus(t.status, 'todo') }));
7058
- else if (raw && typeof raw === 'object') {
7059
- for (const [col, items] of Object.entries(raw)) {
7060
- (Array.isArray(items) ? items : []).forEach(t => tasks.push({ ...t, status: _normTaskStatus(t.status, col) }));
7061
- }
7062
- }
7063
- if (!tasks.length) { pane.innerHTML = '<div class="empty">No tasks</div>'; return; }
7064
- const rank = { running: 0, doing: 0, todo: 1, pending: 1, done: 2 };
7065
- tasks.sort((a, b) => (rank[a.status] ?? 1) - (rank[b.status] ?? 1));
7066
- const pill = (st) => st === 'done' ? 'on' : (st === 'running' || st === 'doing' ? 'warn' : '');
7067
- pane.innerHTML = `<div class="m-group-title">Tasks (${tasks.length})</div>` +
7068
- tasks.slice(0, 80).map(t => `<div style="display:flex;gap:10px;align-items:baseline;padding:5px 0;border-bottom:1px solid var(--border);font-size:12px">
7069
- <span class="ss-pill ${pill(t.status)}">${esc(t.status || '?')}</span>
7070
- <span style="flex:1;color:var(--text-hi);font-family:var(--mono);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(t.description || t.title || t.id || '—')}</span>
7071
- <span style="color:var(--text-lo);white-space:nowrap">${relTime(t.ts || t.created_at || t.updated_at)}</span>
7072
- </div>`).join('');
7073
- }
7074
-
7075
- // ── org members tab ────────────────────────────────────────
7076
- function v2RenderOrgMembers() {
7077
- if (!_v2SelOrg) return;
7078
- const pane = document.getElementById('odt-members');
7079
- if (!pane) return;
7080
- // Prefer actual joined members from the members endpoint; fall back to defined roles
7081
- const members = Array.isArray(_v2OrgData?._members) ? _v2OrgData._members : [];
7082
- const roles = Array.isArray(_v2OrgData?.roles) ? _v2OrgData.roles : [];
7083
- const list = members.length ? members : roles;
7084
- const joinReqs = Array.isArray(_v2OrgData?._joinRequests) ? _v2OrgData._joinRequests : [];
7085
- if (!list.length) { pane.innerHTML = '<div class="empty">No members</div>'; return; }
7086
- const src = members.length ? 'joined members' : 'defined roles';
7087
- // Overlay live running status from in-memory agent SSE state (updated by org:agent:online/offline events)
7088
- const _liveRunning = new Set((_v2OrgData?._agents || []).filter(a => a.status === 'running').map(a => a.id));
7089
- const active = (r) => r.running || r.active || r.status === 'active' || _liveRunning.has(r.id || r.name || r.user);
7090
- pane.innerHTML = `<div class="m-group-title">Members (${list.length}) · ${src}</div>` +
7091
- list.map(r => `<div style="display:flex;gap:10px;align-items:center;padding:6px 0;border-bottom:1px solid var(--border);font-size:12px">
7092
- <span style="width:28px;height:28px;border-radius:50%;background:var(--surface-hi);display:inline-flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0">◈</span>
7093
- <div style="flex:1;min-width:0">
7094
- <div style="color:var(--text-hi);font-family:var(--mono);font-size:12px">${esc(r.name || r.id || r.user || '—')}</div>
7095
- <div style="color:var(--text-lo);font-size:10px">${esc(r.title || r.type || r.role || '')}</div>
7096
- </div>
7097
- <span class="ss-pill ${active(r) ? 'on' : ''}">${active(r) ? 'ACTIVE' : 'IDLE'}</span>
7098
- </div>`).join('') +
7099
- (joinReqs.length ? `<div class="m-group-title" style="margin-top:14px">Join Requests (${joinReqs.length})</div>` +
7100
- joinReqs.map(j => `<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--border);font-size:12px"><span style="color:var(--text-hi)">${esc(j.user || j.name || j.id || '—')}</span><span style="color:var(--text-lo)">${esc(j.status || 'pending')}</span></div>`).join('') : '');
7101
- }
7102
-
7103
- // ── GOALS ──────────────────────────────────────────────────
7104
- function v2RenderOrgGoals() {
7105
- const el = document.getElementById('odt-goals');
7106
- if (!el || !_v2OrgData) return;
7107
- const goals = _v2OrgData.goals || _v2OrgData.config?.goals || [];
7108
- if (!goals.length) { el.innerHTML = '<div class="empty">No goals defined</div>'; return; }
7109
- function renderGoal(g, depth) {
7110
- if (depth > 20) return ''; // Depth guard to prevent stack overflow
7111
- const indent = depth * 20;
7112
- const pct = g.total > 0 ? Math.min(100, Math.round(g.filled / g.total * 100)) : 0;
7113
- const status = g.status || 'pending';
7114
- const statusCls = status === 'done' ? 'on' : (status === 'blocked' || status === 'in_progress') ? 'warn' : '';
7115
- return `<div style="padding:8px 0;border-bottom:1px solid var(--border);padding-left:${indent}px">
7116
- <div style="display:flex;align-items:center;gap:10px">
7117
- <span style="flex:1;font-size:13px;color:var(--text-hi)">${esc(g.text || g.goal || g.title || '—')}</span>
7118
- <span class="ss-pill ${statusCls}">${esc(status)}</span>
7119
- </div>
7120
- ${g.total > 0 ? `<div style="margin-top:5px;height:4px;background:var(--border);border-radius:2px;overflow:hidden"><div style="width:${pct}%;height:100%;background:${pct >= 100 ? 'var(--green)' : 'var(--accent)'};border-radius:2px"></div></div>` : ''}
7121
- ${(g.children || []).map(c => renderGoal(c, depth + 1)).join('')}
7122
- </div>`;
7123
- }
7124
- el.innerHTML = goals.map(g => renderGoal(g, 0)).join('');
7125
- }
7126
-
7127
- // ── BOARD ──────────────────────────────────────────────────
7128
- function v2RenderOrgBoard() {
7129
- const el = document.getElementById('odt-board');
7130
- if (!el || !_v2OrgData) return;
7131
- const issues = _v2OrgData._issues || [];
7132
- if (!issues.length) { el.innerHTML = '<div class="empty">No issues</div>'; return; }
7133
- const cols = ['open', 'in_progress', 'blocked', 'done', 'cancelled'];
7134
- const PRIORITY = { urgent: '🔴', high: '🟠', medium: '🟡', low: '🟢' };
7135
- const _normIssueStatus = s => (s === 'closed' || s === 'resolved') ? 'done' : (s || 'open');
7136
- el.innerHTML = `<div style="display:flex;gap:10px;overflow-x:auto;padding-bottom:8px">` +
7137
- cols.map(col => {
7138
- const cards = issues.filter(i => _normIssueStatus(i.status || i.state) === col);
7139
- return `<div style="min-width:160px;flex:1">
7140
- <div style="font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:0.07em;color:var(--text-lo);padding:6px 0;border-bottom:1px solid var(--border);margin-bottom:8px">${esc(col.replace('_', ' '))} <span style="background:var(--surface-hi);padding:1px 6px;border-radius:8px">${cards.length}</span></div>
7141
- ${cards.slice(0, 15).map(i => `<div style="background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:7px 9px;margin-bottom:5px;font-size:12px">
7142
- <div style="color:var(--text-hi)">${PRIORITY[i.priority] || ''} ${esc((i.title || i.description || '—').slice(0, 60))}</div>
7143
- ${i.assignee ? `<div style="font-size:10px;color:var(--text-lo);margin-top:3px">${esc(i.assignee)}</div>` : ''}
7144
- </div>`).join('')}
7145
- </div>`;
7146
- }).join('') + `</div>`;
7147
- }
7148
-
7149
- // ── LIVE ───────────────────────────────────────────────────
7150
- let _orgLiveInterval = null;
7151
- function v2RenderOrgLive() {
7152
- const el = document.getElementById('odt-live');
7153
- if (!el || !_v2OrgData) return;
7154
- const agents = _v2OrgData._agents || [];
7155
- const running = agents.filter(a => a.status === 'running' || a.running);
7156
- el.innerHTML = `<div style="margin-bottom:14px">
7157
- ${running.length
7158
- ? running.map(a => `<div style="display:flex;align-items:center;gap:8px;padding:6px 0;border-bottom:1px solid var(--border)">
7159
- <span class="live-dot" style="flex-shrink:0"></span>
7160
- <span style="font-size:13px;color:var(--text-hi)">${esc(a.title || a.id || '—')}</span>${a.adapterType?`<span style="font-size:10px;color:var(--text-lo);margin-left:4px">[${esc(a.adapterType)}]</span>`:''}
7161
- <span style="margin-left:auto;font-size:11px;color:var(--text-lo)">${esc(a.adapterModel || a.adapter || '')}</span>
7162
- </div>`).join('')
7163
- : '<div style="color:var(--text-lo);font-size:13px;padding:10px 0">No agents currently running</div>'}
7164
- </div>
7165
- <div class="m-group-title" style="margin-bottom:6px">Activity Feed</div>
7166
- <div style="max-height:240px;overflow-y:auto;font-size:11px;font-family:var(--mono)">
7167
- ${(()=>{const _la=_v2OrgData._activity||[];const _ls=_v2OrgEventLog[_v2SelOrg]||[];const _lseen=new Set();const _lmerged=[..._la,..._ls].filter(e=>{const k=`${e.ts||''}|${e.type||''}|${e.runId||e.session||''}|${e.from||e.role||e.agent||''}|${(e.result||e.msg||e.message||e.summary||'').slice(0,20)}`;return _lseen.has(k)?false:(_lseen.add(k),true);}).sort((a,b)=>(a.ts||0)-(b.ts||0));return _lmerged.slice(-30).reverse().map(e=>`<div style="padding:3px 0;border-bottom:1px solid var(--border);color:var(--text-lo)">${esc(relTime(e.ts||e.timestamp||e.created_at))}<span style="color:var(--text-mid);margin-left:6px">${esc(e.type||e.kind||e.event||'—')}</span>${fmtOrgEvDetail(e)?`<span style="color:var(--text-hi);margin-left:6px">${esc(fmtOrgEvDetail(e).slice(0,80))}</span>`:''}</div>`).join('');})()}
7168
- </div>`;
7169
- // auto-refresh every 5s while tab is active
7170
- if (!_orgLiveInterval) {
7171
- _orgLiveInterval = setInterval(async () => {
7172
- if (_v2OrgTab !== 'live' || !_v2SelOrg || currentView !== 'orgs') { clearInterval(_orgLiveInterval); _orgLiveInterval = null; return; }
7173
- const _liveOrg = _v2SelOrg;
7174
- const _enc = encodeURIComponent(_liveOrg);
7175
- try {
7176
- const [actData, agentsData] = await Promise.all([
7177
- fetch(`/api/org/${_enc}/activity${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r => r.ok ? r.json() : []).catch(() => []),
7178
- fetch(`/api/org/${_enc}/agents${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r => r.ok ? r.json() : {}).catch(() => {}),
7179
- ]);
7180
- if (_v2SelOrg !== _liveOrg || !_v2OrgData) return;
7181
- // Merge fresh API data with SSE-pushed events to avoid clobbering live events
7182
- // that haven't flushed to the JSONL file yet when this poll ran.
7183
- const _freshAct = Array.isArray(actData) ? actData : [];
7184
- const _sseAct = (_v2OrgData._activity || []).filter(e => e._sseOnly);
7185
- const _mergeSeen = new Set(_freshAct.map(e => `${e.ts||''}|${e.type||''}|${e.runId||e.session||''}`));
7186
- _sseAct.forEach(e => { const k=`${e.ts||''}|${e.type||''}|${e.runId||e.session||''}`; if (!_mergeSeen.has(k)) _freshAct.push(e); });
7187
- _v2OrgData._activity = _freshAct.sort((a,b)=>(a.ts||0)-(b.ts||0));
7188
- const freshAgents = Array.isArray(agentsData) ? agentsData : (agentsData?.agents || []);
7189
- if (freshAgents.length) {
7190
- // Preserve SSE-driven running status: state file lags behind real-time SSE updates
7191
- const sseRunning = new Set((_v2OrgData._agents || []).filter(a => a.status === 'running').map(a => a.id));
7192
- _v2OrgData._agents = freshAgents.map(a => sseRunning.has(a.id) ? { ...a, status: 'running' } : a);
7193
- }
7194
- v2RenderOrgLive();
7195
- } catch (_) {}
7196
- }, 5000);
7197
- }
7198
- }
7199
-
7200
6882
  // ── APPROVALS ──────────────────────────────────────────────
7201
6883
  // Tool/action approvals (<org>/approvals.json): a role asked to run a
7202
6884
  // sensitive tool (Bash, WebFetch, org_complete, policy.approvalTools…).
@@ -7315,243 +6997,6 @@ async function _orgGateAction(id, approved) {
7315
6997
  if (ok) v2RenderOrgDecisions();
7316
6998
  }
7317
6999
 
7318
- // ── SECRETS ────────────────────────────────────────────────
7319
- async function v2RenderOrgSecrets() {
7320
- const el = document.getElementById('odt-secrets');
7321
- if (!el || !_v2SelOrg) return;
7322
- const _rendOrg = _v2SelOrg;
7323
- el.innerHTML = '<div class="loading-txt">Loading…</div>';
7324
- try {
7325
- const _enc = encodeURIComponent(_rendOrg);
7326
- const data = await fetch(`/api/org/${_enc}/secrets${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r => r.ok ? r.json() : []).catch(() => []);
7327
- if (_v2SelOrg !== _rendOrg) return;
7328
- const secrets = Array.isArray(data) ? data : (data.secrets || []);
7329
- el.innerHTML = '<div style="font-size:11px;color:var(--text-lo);margin-bottom:10px">Secret values are never transmitted or displayed.</div>' +
7330
- (secrets.length
7331
- ? secrets.map(s => `<div style="display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:1px solid var(--border);font-size:12px">
7332
- <span style="font-family:var(--mono);color:var(--text-hi)">${esc(s.name || s.key || '—')}</span>
7333
- ${s.purpose ? `<span style="color:var(--text-lo)">${esc(s.purpose)}</span>` : ''}
7334
- <span style="margin-left:auto;font-family:var(--mono);color:var(--border)">••••••••</span>
7335
- </div>`).join('')
7336
- : '<div class="empty">No secrets configured</div>');
7337
- } catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
7338
- }
7339
-
7340
- // ── SETTINGS ───────────────────────────────────────────────
7341
- function v2RenderOrgSettings() {
7342
- const el = document.getElementById('odt-settings');
7343
- if (!el || !_v2OrgData) return;
7344
- const d = _v2OrgData;
7345
- const topos = ['hierarchical', 'mesh', 'hierarchical-mesh', 'adaptive', 'centralized', 'hybrid'];
7346
- const govs = ['auto', 'board', 'strict'];
7347
- // governance may be a string or an object {policy,...}; budget lives under run_config
7348
- const govVal = (d.governance && typeof d.governance === 'object') ? (d.governance.policy || '') : (d.governance || '');
7349
- const budgetVal = d.run_config?.budget_tokens ?? d.budget_tokens ?? d.budgetTokens ?? '';
7350
- el.innerHTML = `
7351
- <div style="font-size:11px;color:var(--text-lo);margin-bottom:14px">Changes generate a CLI command — no direct writes from UI.</div>
7352
- <div style="display:flex;flex-direction:column;gap:12px;max-width:380px">
7353
- <div><div class="le-lbl">Goal</div><input id="os-goal" class="filter-input" value="${esc(d.goal || '')}"></div>
7354
- <div><div class="le-lbl">Topology</div>
7355
- <select id="os-topo" class="filter-input" style="cursor:pointer">
7356
- ${topos.map(t => `<option ${d.topology === t ? 'selected' : ''}>${esc(t)}</option>`).join('')}
7357
- </select>
7358
- </div>
7359
- <div><div class="le-lbl">Governance</div>
7360
- <select id="os-gov" class="filter-input" style="cursor:pointer">
7361
- ${govs.map(g => `<option ${govVal === g ? 'selected' : ''}>${esc(g)}</option>`).join('')}
7362
- </select>
7363
- </div>
7364
- <div><div class="le-lbl">Budget (tokens)</div><input id="os-budget" class="filter-input" type="number" value="${esc(String(budgetVal || ''))}"></div>
7365
- <button class="btn" style="width:fit-content;color:var(--accent);border-color:var(--accent)" onclick="generateOrgSettingsCmd()">Generate CLI Command</button>
7366
- <div id="os-cmd-out" style="display:none;font-family:var(--mono);font-size:11px;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:10px;word-break:break-all;cursor:pointer;color:var(--text-hi)" title="Click to copy" onclick="navigator.clipboard.writeText(this.textContent).then(()=>showToast('Copied','','ok'))"></div>
7367
- </div>`;
7368
- }
7369
-
7370
- function _generateOrgSettingsCmd() {
7371
- const org = _v2SelOrg || '';
7372
- const goal = document.getElementById('os-goal')?.value.trim() || '';
7373
- const topo = document.getElementById('os-topo')?.value || '';
7374
- const gov = document.getElementById('os-gov')?.value || '';
7375
- const budget = document.getElementById('os-budget')?.value.trim() || '';
7376
- const _shellEsc = s => s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/`/g, '\\`').replace(/\$/g, '\\$').replace(/\n/g, ' ');
7377
- const parts = [`/mastermind:org-settings --org "${_shellEsc(org)}"`];
7378
- if (goal) parts.push(`--goal "${_shellEsc(goal)}"`);
7379
- if (topo) parts.push(`--topology ${topo}`);
7380
- if (gov) parts.push(`--governance ${gov}`);
7381
- if (budget) parts.push(`--budget-tokens ${budget}`);
7382
- const out = document.getElementById('os-cmd-out');
7383
- if (out) { out.textContent = parts.join(' '); out.style.display = 'block'; }
7384
- }
7385
-
7386
- // ── ROUTINES ───────────────────────────────────────────────
7387
- async function v2RenderOrgRoutines() {
7388
- const el = document.getElementById('odt-routines');
7389
- if (!el || !_v2SelOrg) return;
7390
- const _rendOrg = _v2SelOrg;
7391
- el.innerHTML = '<div class="loading-txt">Loading…</div>';
7392
- try {
7393
- const _enc = encodeURIComponent(_rendOrg);
7394
- const data = await fetch(`/api/org/${_enc}/routines${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r => r.ok ? r.json() : []).catch(() => []);
7395
- if (_v2SelOrg !== _rendOrg) return;
7396
- const routines = Array.isArray(data) ? data : (data.routines || _v2OrgData?.config?.routines || []);
7397
- if (!routines.length) { el.innerHTML = '<div class="empty">No routines configured</div>'; return; }
7398
- el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
7399
- <thead><tr style="color:var(--text-xs);text-align:left">
7400
- <th style="padding:5px 8px">Name</th><th>Cron</th><th>Last Run</th><th>Status</th>
7401
- </tr></thead><tbody>` +
7402
- routines.map(r => `<tr style="border-top:1px solid var(--border)">
7403
- <td style="padding:6px 8px;color:var(--text-hi)">${esc(r.name || '—')}</td>
7404
- <td style="padding:6px 8px;font-family:var(--mono);color:var(--text-lo)">${esc(r.cron || r.schedule || '—')}</td>
7405
- <td style="padding:6px 8px;color:var(--text-lo)">${(r.lastRun || r.last_run) ? relTime(r.lastRun || r.last_run) : '—'}</td>
7406
- <td style="padding:6px 8px"><span class="ss-pill ${r.enabled || r.active || r.status === 'active' ? 'on' : ''}">${esc(r.status || (r.enabled != null ? (r.enabled ? 'active' : 'inactive') : '—'))}</span></td>
7407
- </tr>`).join('') + '</tbody></table>';
7408
- } catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
7409
- }
7410
-
7411
- // ── MY ISSUES ──────────────────────────────────────────────
7412
- async function v2RenderOrgMyIssues() {
7413
- const el = document.getElementById('odt-myissues');
7414
- if (!el || !_v2SelOrg) return;
7415
- const _rendOrg = _v2SelOrg;
7416
- el.innerHTML = '<div class="loading-txt">Loading…</div>';
7417
- try {
7418
- const _enc = encodeURIComponent(_rendOrg);
7419
- const data = await fetch(`/api/org/${_enc}/my-issues${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r => r.ok ? r.json() : []).catch(() => []);
7420
- if (_v2SelOrg !== _rendOrg) return;
7421
- const issues = Array.isArray(data) ? data : (data.issues || []);
7422
- if (!issues.length) { el.innerHTML = '<div class="empty">No issues assigned to you</div>'; return; }
7423
- const PRIORITY = { urgent: '🔴', high: '🟠', medium: '🟡', low: '🟢' };
7424
- el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
7425
- <thead><tr style="color:var(--text-xs);text-align:left">
7426
- <th style="padding:5px 4px">P</th><th style="padding:5px 8px">Title</th><th>Status</th><th>Updated</th>
7427
- </tr></thead><tbody>` +
7428
- issues.slice(0, 50).map(i => {
7429
- const cls = i.status === 'done' || i.status === 'closed' || i.status === 'resolved' ? 'on' : (i.status === 'blocked' || i.status === 'in_progress') ? 'warn' : '';
7430
- return `<tr style="border-top:1px solid var(--border)">
7431
- <td style="padding:5px 4px">${PRIORITY[i.priority] || '·'}</td>
7432
- <td style="padding:5px 8px;color:var(--text-hi);max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc((i.title || i.description || '—').slice(0, 80))}</td>
7433
- <td style="padding:5px 8px"><span class="ss-pill ${cls}">${esc(i.status || 'open')}</span></td>
7434
- <td style="padding:5px 8px;color:var(--text-xs);font-size:11px;font-family:var(--mono)">${relTime(i.updated_at || i.ts)}</td>
7435
- </tr>`;
7436
- }).join('') + '</tbody></table>';
7437
- } catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
7438
- }
7439
-
7440
- // ── PLUGINS ────────────────────────────────────────────────
7441
- async function v2RenderOrgPlugins() {
7442
- const el = document.getElementById('odt-plugins');
7443
- if (!el || !_v2SelOrg) return;
7444
- const _rendOrg = _v2SelOrg;
7445
- el.innerHTML = '<div class="loading-txt">Loading…</div>';
7446
- try {
7447
- const _enc = encodeURIComponent(_rendOrg);
7448
- const data = await fetch(`/api/org/${_enc}/plugins${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r => r.ok ? r.json() : []).catch(() => []);
7449
- if (_v2SelOrg !== _rendOrg) return;
7450
- const plugins = Array.isArray(data) ? data : (data.plugins || []);
7451
- if (!plugins.length) { el.innerHTML = '<div class="empty">No plugins installed</div>'; return; }
7452
- el.innerHTML = `<div class="proj-grid">` +
7453
- plugins.map(p => {
7454
- const status = p.status || 'installed';
7455
- const col = status === 'error' ? 'var(--red)' : status === 'installed' ? 'var(--accent)' : 'var(--text-lo)';
7456
- return `<div class="proj-card">
7457
- <div class="proj-card-name">${esc(p.name || '—')}</div>
7458
- <div class="proj-card-path">${esc((p.description || '').slice(0, 80))}</div>
7459
- <div style="margin-top:8px"><span class="ss-pill" style="color:${esc(col)};border-color:${esc(col)}44;background:${esc(col)}18">${esc(status)}</span></div>
7460
- </div>`;
7461
- }).join('') + `</div>`;
7462
- } catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
7463
- }
7464
-
7465
- // ── CHARTS ─────────────────────────────────────────────────
7466
- function v2RenderOrgCharts() {
7467
- const el = document.getElementById('odt-charts');
7468
- if (!el || !_v2OrgData) return;
7469
- const activity = _v2OrgData._activity || [];
7470
- const agents = _v2OrgData._agents || [];
7471
-
7472
- // Build 14-day buckets from activity
7473
- const now = Date.now();
7474
- const DAY = 86400000;
7475
- const buckets = Array.from({length: 14}, (_, i) => ({
7476
- day: new Date(now - (13 - i) * DAY).toLocaleDateString('en', {month:'short', day:'numeric'}),
7477
- ts: now - (14 - i) * DAY,
7478
- events: 0, errors: 0,
7479
- }));
7480
- activity.forEach(e => {
7481
- const ts = new Date(e.ts || e.timestamp || e.created_at || 0).getTime();
7482
- const idx = buckets.findIndex((b, i) =>
7483
- ts >= b.ts && (i === 13 || ts < buckets[i + 1].ts));
7484
- if (idx >= 0) {
7485
- buckets[idx].events++;
7486
- if (e.type === 'error' || e.status === 'error') buckets[idx].errors++;
7487
- }
7488
- });
7489
- const maxEvents = Math.max(...buckets.map(b => b.events), 1);
7490
-
7491
- // Heatmap bar chart
7492
- const heatmap = buckets.map(b => {
7493
- const h = Math.max(4, Math.round((b.events / maxEvents) * 80));
7494
- const col = b.errors > 0 ? 'var(--red)' : b.events > 0 ? 'var(--accent)' : 'var(--border)';
7495
- return `<div style="display:flex;flex-direction:column;align-items:center;gap:3px;flex:1;min-width:0">
7496
- <div style="height:80px;display:flex;align-items:flex-end">
7497
- <div style="width:100%;height:${h}px;background:${col};border-radius:2px 2px 0 0;min-width:8px" title="${b.events} events${b.errors ? `, ${b.errors} errors` : ''}"></div>
7498
- </div>
7499
- <div style="font-size:9px;color:var(--text-xs);transform:rotate(-45deg);transform-origin:top left;white-space:nowrap;margin-top:6px;margin-left:4px">${b.day}</div>
7500
- </div>`;
7501
- }).join('');
7502
-
7503
- // Per-agent run bars
7504
- const agentRuns = agents.slice(0, 10).map(a => {
7505
- const runs = activity.filter(e => e.agentId === a.id || e.agent === a.id).length;
7506
- return { name: (a.type || a.title || a.id || '—').toString().slice(0, 20), runs };
7507
- }).filter(a => a.runs > 0).sort((x, y) => y.runs - x.runs);
7508
- const maxRuns = Math.max(...agentRuns.map(a => a.runs), 1);
7509
-
7510
- const totalEvents = activity.length;
7511
- const errorCount = activity.filter(e => e.type === 'error' || e.status === 'error').length;
7512
- const successRate = totalEvents > 0 ? Math.round((1 - errorCount / totalEvents) * 100) : 100;
7513
- const activeCount = agents.filter(a => a.status === 'running' || a.running).length;
7514
-
7515
- el.innerHTML = `
7516
- <div style="display:flex;gap:20px;flex-wrap:wrap;margin-bottom:14px;padding-bottom:10px;border-bottom:1px solid var(--border)">
7517
- <div class="chunk-stat"><div class="chunk-stat-val">${totalEvents}</div><div class="chunk-stat-lbl">Events</div></div>
7518
- <div class="chunk-stat"><div class="chunk-stat-val" style="color:${errorCount>0?'var(--red)':'var(--green)'}">${errorCount}</div><div class="chunk-stat-lbl">Errors</div></div>
7519
- <div class="chunk-stat"><div class="chunk-stat-val" style="color:var(--green)">${successRate}%</div><div class="chunk-stat-lbl">Success</div></div>
7520
- <div class="chunk-stat"><div class="chunk-stat-val" style="color:${activeCount>0?'var(--green)':'var(--text-lo)'}">${activeCount}</div><div class="chunk-stat-lbl">Active</div></div>
7521
- </div>
7522
- <div class="m-group-title" style="margin-bottom:8px">14-Day Activity</div>
7523
- <div style="display:flex;gap:2px;align-items:flex-end;padding-bottom:28px;margin-bottom:16px;overflow-x:auto">${heatmap}</div>
7524
- ${agentRuns.length ? `<div class="m-group-title" style="margin-bottom:6px">Per-Agent Runs</div>
7525
- ${agentRuns.map(a => `<div style="display:flex;align-items:center;gap:8px;padding:3px 0;font-size:12px">
7526
- <div style="width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-hi);font-family:var(--mono);font-size:11px">${esc(a.name)}</div>
7527
- <div style="flex:1;height:7px;background:var(--border);border-radius:2px;overflow:hidden"><div style="width:${Math.round(a.runs/maxRuns*100)}%;height:100%;background:var(--accent);border-radius:2px"></div></div>
7528
- <div style="width:30px;text-align:right;color:var(--text-lo);font-family:var(--mono);font-size:11px">${a.runs}</div>
7529
- </div>`).join('')}` : ''}
7530
- `;
7531
- }
7532
-
7533
- // ── PROJECTS ───────────────────────────────────────────────
7534
- async function v2RenderOrgProjects() {
7535
- const el = document.getElementById('odt-projects');
7536
- if (!el || !_v2SelOrg) return;
7537
- const _rendOrg = _v2SelOrg;
7538
- el.innerHTML = '<div class="loading-txt">Loading…</div>';
7539
- try {
7540
- const _enc = encodeURIComponent(_rendOrg);
7541
- const data = await fetch(`/api/org/${_enc}/projects${DIR?`?dir=${encodeURIComponent(DIR)}`:''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
7542
- if (_v2SelOrg !== _rendOrg) return;
7543
- const projects = Array.isArray(data) ? data : (data.projects || []);
7544
- if (!projects.length) { el.innerHTML = '<div class="empty">No projects configured</div>'; return; }
7545
- el.innerHTML = `<div class="proj-grid">${projects.map(p => `<div class="proj-card">
7546
- <div class="proj-card-name">${esc(p.name || p.title || '—')}</div>
7547
- <div class="proj-card-path">${esc((p.description || p.path || '').slice(0,80))}</div>
7548
- <div class="proj-card-stats" style="margin-top:8px">
7549
- ${p.status ? `<span class="ss-pill ${p.status==='active'?'on':''}">${esc(p.status)}</span>` : ''}
7550
- </div>
7551
- </div>`).join('')}</div>`;
7552
- } catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
7553
- }
7554
-
7555
7000
  // ── ORG CHAT ───────────────────────────────────────────────
7556
7001
  let _odtChatSessions = [];
7557
7002
  let _odtChatCurrentId = null;
@@ -8041,6 +7486,7 @@ window.odtSendChatMessage = async () => {
8041
7486
  }
8042
7487
  } catch(e) {
8043
7488
  inp.value = text;
7489
+ showToast('Not delivered', e.message, 'err');
8044
7490
  } finally {
8045
7491
  btn.disabled = false;
8046
7492
  inp.focus();
@@ -8788,7 +8234,7 @@ async function v2RenderOrgRuntime() {
8788
8234
  const statusSub = v.live
8789
8235
  ? `${esc(v.run || '')}${v.startedAt ? ` · up ${fmtDur(Date.now() - v.startedAt)}` : ''}`
8790
8236
  : `${esc(v.run || 'no run yet')}${v.closedBy ? ` · ${esc(_RT_CLOSED_BY[v.closedBy] || v.closedBy)}` : ''}`;
8791
- let idle = '—', idleSub = s.idle_minutes === 0 ? 'watchdog disabled' : `window ${s.idle_minutes}m`;
8237
+ let idle = '—', idleSub = s.idle_minutes === 0 ? 'watchdog disabled' : `window ${esc(s.idle_minutes)}m`;
8792
8238
  if (v.idle) {
8793
8239
  if (v.idle.idle_hold && v.idle.idle_hold !== 'unknown') {
8794
8240
  idle = `held: ${esc(v.idle.idle_hold)}`;
@@ -8805,7 +8251,7 @@ async function v2RenderOrgRuntime() {
8805
8251
  const budgetSub = `${esc(b.basis)} basis${pct !== null ? ` · ${pct.toFixed(1)}%` : ''}${budgetWhy} · ${fmtCost(v.totals?.cost_usd)}`;
8806
8252
  const gate = s.completion_evidence ? 'evidence required' : 'off';
8807
8253
  const gateSub = s.completion_evidence
8808
- ? `escalate after ${s.max_evidence_attempts} refusals · org_complete: ${esc(s.completion)}`
8254
+ ? `escalate after ${esc(s.max_evidence_attempts)} refusals · org_complete: ${esc(s.completion)}`
8809
8255
  : `org_complete gate: ${esc(s.completion)}`;
8810
8256
 
8811
8257
  let html = '<div style="padding:16px">';
@@ -8816,7 +8262,7 @@ async function v2RenderOrgRuntime() {
8816
8262
  ${_rtCard('IDLE WATCHDOG', idle, idleSub)}
8817
8263
  ${_rtCard('BUDGET (THIS RUN)', budget, budgetSub)}
8818
8264
  ${_rtCard('COMPLETION GATE', gate, gateSub)}
8819
- ${_rtCard('SESSIONS', `per ${esc(s.session_scope)}`, `workspace: ${esc(s.workspace)} · max ${s.max_concurrent_agents} agents`)}
8265
+ ${_rtCard('SESSIONS', `per ${esc(s.session_scope)}`, `workspace: ${esc(s.workspace)} · max ${esc(s.max_concurrent_agents)} agents`)}
8820
8266
  </div>`;
8821
8267
 
8822
8268
  // Roles
@@ -8848,7 +8294,7 @@ async function v2RenderOrgRuntime() {
8848
8294
  `<span style="color:var(--text-hi)">${esc(t.title || t.id)}</span><div style="font-size:10px;color:var(--text-xs)">${esc(t.id)}</div>`,
8849
8295
  esc(t.assignee || '—'),
8850
8296
  `<span class="ss-pill ${t.status === 'done' ? 'on' : t.status === 'failed' || t.status === 'blocked' ? 'warn' : ''}">${esc(t.status)}</span>`,
8851
- t.evidenceFailures ? `<span style="color:${t.evidenceFailures >= s.max_evidence_attempts ? 'var(--red)' : 'var(--amber, #d29922)'}">${t.evidenceFailures} / ${s.max_evidence_attempts}</span>` : '—',
8297
+ t.evidenceFailures ? `<span style="color:${t.evidenceFailures >= s.max_evidence_attempts ? 'var(--red)' : 'var(--amber, #d29922)'}">${esc(t.evidenceFailures)} / ${esc(s.max_evidence_attempts)}</span>` : '—',
8852
8298
  esc(t.loadout || '—'),
8853
8299
  t.blockedUntil ? `until ${_rtClock(t.blockedUntil)}${t.blockedReason ? ` — ${esc(t.blockedReason)}` : ''}` : '—',
8854
8300
  ]), 'No task graph for this run (the boss has not used org_task)');
@@ -8872,7 +8318,7 @@ async function v2RenderOrgRuntime() {
8872
8318
  h.outcome
8873
8319
  ? `<span class="ss-pill ${h.outcome.status === 'achieved' ? 'on' : 'warn'}">${esc(h.outcome.status)}</span>${h.blocker ? ` blocker: ${esc(h.blocker)}` : ''}<div style="font-size:11px;color:var(--text-lo);max-width:320px;white-space:pre-wrap;max-height:80px;overflow:auto">${esc(h.blockerDetail || h.outcome.summary || '')}</div>`
8874
8320
  : '<span style="color:var(--text-xs)">none claimed</span>',
8875
- h.runnableTasksAtStop ? `<span style="color:var(--amber, #d29922)">${h.runnableTasksAtStop}</span>` : '0',
8321
+ h.runnableTasksAtStop ? `<span style="color:var(--amber, #d29922)">${esc(h.runnableTasksAtStop)}</span>` : '0',
8876
8322
  mono(_rtTok(h.totalTokens)),
8877
8323
  mono(fmtCost(h.totalCostUsd)),
8878
8324
  ]), 'No finished runs yet');
@@ -9177,177 +8623,6 @@ function _ofSaveImage(defaultName) {
9177
8623
  a.click();
9178
8624
  }
9179
8625
 
9180
- // ── WORKSPACES ─────────────────────────────────────────────
9181
- async function v2RenderOrgWorkspaces() {
9182
- const el = document.getElementById('odt-workspaces');
9183
- if (!el || !_v2SelOrg) return;
9184
- const _rendOrg = _v2SelOrg;
9185
- el.innerHTML = '<div class="loading-txt">Loading…</div>';
9186
- try {
9187
- const _enc = encodeURIComponent(_rendOrg);
9188
- const data = await fetch(`/api/org/${_enc}/workspaces${DIR?`?dir=${encodeURIComponent(DIR)}`:''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
9189
- if (_v2SelOrg !== _rendOrg) return;
9190
- const ws = Array.isArray(data) ? data : (data.workspaces || []);
9191
- if (!ws.length) { el.innerHTML = '<div class="empty">No workspaces configured</div>'; return; }
9192
- el.innerHTML = ws.map(w => `<div style="padding:10px 0;border-bottom:1px solid var(--border)">
9193
- <div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">
9194
- <span style="font-size:13px;color:var(--text-hi);font-weight:500">${esc(w.name || w.id || '—')}</span>
9195
- ${w.branch ? `<span class="ss-pill">${esc(w.branch)}</span>` : ''}
9196
- </div>
9197
- ${w.path ? `<div style="font-size:11px;color:var(--text-lo);font-family:var(--mono)">${esc(w.path)}</div>` : ''}
9198
- ${w.services?.length ? `<div style="font-size:11px;color:var(--text-lo);margin-top:3px">${w.services.map(s=>esc(s)).join(', ')}</div>` : ''}
9199
- </div>`).join('');
9200
- } catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
9201
- }
9202
-
9203
- // ── INVITES ────────────────────────────────────────────────
9204
- async function v2RenderOrgInvites() {
9205
- const el = document.getElementById('odt-invites');
9206
- if (!el || !_v2SelOrg) return;
9207
- const _rendOrg = _v2SelOrg;
9208
- el.innerHTML = '<div class="loading-txt">Loading…</div>';
9209
- try {
9210
- const _enc = encodeURIComponent(_rendOrg);
9211
- const data = await fetch(`/api/org/${_enc}/invites${DIR?`?dir=${encodeURIComponent(DIR)}`:''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
9212
- if (_v2SelOrg !== _rendOrg) return;
9213
- const invites = Array.isArray(data) ? data : (data.invites || []);
9214
- if (!invites.length) { el.innerHTML = '<div class="empty">No active invite tokens</div>'; return; }
9215
- el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
9216
- <thead><tr style="color:var(--text-xs);text-align:left">
9217
- <th style="padding:5px 8px">Token</th><th>Role</th><th>Expires</th><th>Created</th>
9218
- </tr></thead>
9219
- <tbody>${invites.map(i => `<tr style="border-top:1px solid var(--border)">
9220
- <td style="padding:5px 8px;color:var(--text-hi);font-family:var(--mono);font-size:11px">${esc((i.token||i.id||'—').slice(0,16))}…</td>
9221
- <td style="padding:5px 8px"><span class="ss-pill">${esc(i.role||'viewer')}</span></td>
9222
- <td style="padding:5px 8px;color:var(--text-lo)">${i.expiresAt ? relTime(i.expiresAt) : '—'}</td>
9223
- <td style="padding:5px 8px;color:var(--text-lo)">${i.createdAt ? relTime(i.createdAt) : '—'}</td>
9224
- </tr>`).join('')}</tbody>
9225
- </table>`;
9226
- } catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
9227
- }
9228
-
9229
- // ── ENVIRONMENTS ───────────────────────────────────────────
9230
- async function v2RenderOrgEnvironments() {
9231
- const el = document.getElementById('odt-environments');
9232
- if (!el || !_v2SelOrg) return;
9233
- const _rendOrg = _v2SelOrg;
9234
- el.innerHTML = '<div class="loading-txt">Loading…</div>';
9235
- try {
9236
- const _enc = encodeURIComponent(_rendOrg);
9237
- const data = await fetch(`/api/org/${_enc}/environments${DIR?`?dir=${encodeURIComponent(DIR)}`:''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
9238
- if (_v2SelOrg !== _rendOrg) return;
9239
- const envs = Array.isArray(data) ? data : (data.environments || []);
9240
- if (!envs.length) { el.innerHTML = '<div class="empty">No environments configured</div>'; return; }
9241
- el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
9242
- <thead><tr style="color:var(--text-xs);text-align:left">
9243
- <th style="padding:5px 8px">Name</th><th>Driver</th><th>Host</th><th>Status</th>
9244
- </tr></thead>
9245
- <tbody>${envs.map(e => `<tr style="border-top:1px solid var(--border)">
9246
- <td style="padding:5px 8px;color:var(--text-hi);font-weight:500">${esc(e.name||e.id||'—')}</td>
9247
- <td style="padding:5px 8px;color:var(--text-lo);font-family:var(--mono)">${esc(e.driver||e.type||'—')}</td>
9248
- <td style="padding:5px 8px;color:var(--text-lo);font-family:var(--mono)">${esc(e.host||'—')}</td>
9249
- <td style="padding:5px 8px"><span class="ss-pill ${e.status==='active'||e.active?'on':''}">${esc(e.status||'—')}</span></td>
9250
- </tr>`).join('')}</tbody>
9251
- </table>`;
9252
- } catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
9253
- }
9254
-
9255
- // ── ACCESS ─────────────────────────────────────────────────
9256
- function v2RenderOrgAccess() {
9257
- const el = document.getElementById('odt-access');
9258
- if (!el || !_v2OrgData) return;
9259
- const members = _v2OrgData._members || [];
9260
- if (!members.length) { el.innerHTML = '<div class="empty">No members found</div>'; return; }
9261
- const TIER = { owner: 0, admin: 1, operator: 2, viewer: 3 };
9262
- const byRole = {};
9263
- members.forEach(m => { const r = m.role || 'viewer'; (byRole[r] || (byRole[r] = [])).push(m); });
9264
- const sorted = Object.entries(byRole).sort((a,b) => (TIER[a[0]]??9) - (TIER[b[0]]??9));
9265
- el.innerHTML = sorted.map(([role, mems]) => `
9266
- <div style="margin-bottom:14px">
9267
- <div class="m-group-title" style="margin-bottom:6px;text-transform:capitalize">${esc(role)}</div>
9268
- ${mems.map(m => `<div style="display:flex;align-items:center;gap:10px;padding:6px 0;border-bottom:1px solid var(--border);font-size:12px">
9269
- <div style="width:28px;height:28px;border-radius:50%;background:var(--accent-dim);color:var(--accent);display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;flex-shrink:0">${esc((m.name||m.username||m.id||'?')[0].toUpperCase())}</div>
9270
- <span style="color:var(--text-hi);flex:1">${esc(m.name || m.username || m.id || '—')}</span>
9271
- ${m.grants?.length ? m.grants.map(g => `<span class="ss-pill" style="font-size:10px">${esc(g)}</span>`).join('') : ''}
9272
- ${m.suspended ? '<span class="ss-pill warn">suspended</span>' : ''}
9273
- </div>`).join('')}
9274
- </div>`).join('');
9275
- }
9276
-
9277
- // ── ISSUES (FULL) ──────────────────────────────────────────
9278
- function v2RenderOrgIssuesFull() {
9279
- const el = document.getElementById('odt-issues-full');
9280
- if (!el || !_v2OrgData) return;
9281
- const issues = _v2OrgData._issues || [];
9282
- if (!issues.length) { el.innerHTML = '<div class="empty">No issues found</div>'; return; }
9283
- const PRIORITY = { urgent:'🔴', high:'🟠', medium:'🟡', low:'🟢' };
9284
- el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
9285
- <thead><tr style="color:var(--text-xs);text-align:left">
9286
- <th style="padding:5px 4px">P</th><th style="padding:5px 8px">Title</th><th>Assignee</th><th>Status</th><th>Updated</th>
9287
- </tr></thead>
9288
- <tbody>${issues.slice(0,100).map(i => `<tr style="border-top:1px solid var(--border)">
9289
- <td style="padding:5px 4px">${PRIORITY[i.priority]||'·'}</td>
9290
- <td style="padding:5px 8px;color:var(--text-hi);max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc((i.title||i.description||'—').slice(0,80))}</td>
9291
- <td style="padding:5px 8px;color:var(--text-lo)">${esc(i.assignee||'—')}</td>
9292
- <td style="padding:5px 8px"><span class="ss-pill ${i.status==='done'||i.status==='closed'||i.status==='resolved'?'on':(i.status==='blocked'||i.status==='in_progress')?'warn':''}">${esc(i.status||'open')}</span></td>
9293
- <td style="padding:5px 8px;color:var(--text-xs);font-family:var(--mono);font-size:11px">${relTime(i.updated_at||i.ts)}</td>
9294
- </tr>`).join('')}</tbody>
9295
- </table>`;
9296
- }
9297
-
9298
- // ── JOIN REQUESTS ──────────────────────────────────────────
9299
- async function v2RenderOrgJoinRequests() {
9300
- const el = document.getElementById('odt-join-requests');
9301
- if (!el || !_v2SelOrg) return;
9302
- const _rendOrg = _v2SelOrg;
9303
- el.innerHTML = '<div class="loading-txt">Loading…</div>';
9304
- try {
9305
- const _enc = encodeURIComponent(_rendOrg);
9306
- const data = await fetch(`/api/org/${_enc}/join-requests${DIR?`?dir=${encodeURIComponent(DIR)}`:''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
9307
- if (_v2SelOrg !== _rendOrg) return;
9308
- const reqs = Array.isArray(data) ? data : (data.joinRequests || data.join_requests || []);
9309
- if (!reqs.length) { el.innerHTML = '<div class="empty">No pending join requests</div>'; return; }
9310
- el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
9311
- <thead><tr style="color:var(--text-xs);text-align:left">
9312
- <th style="padding:5px 8px">Requester</th><th>Type</th><th>Status</th><th>Created</th>
9313
- </tr></thead>
9314
- <tbody>${reqs.map(r => `<tr style="border-top:1px solid var(--border)">
9315
- <td style="padding:5px 8px;color:var(--text-hi)">${esc(r.name||r.username||r.id||'—')}</td>
9316
- <td style="padding:5px 8px;color:var(--text-lo)">${r.type==='agent'?'🤖 agent':'👤 human'}</td>
9317
- <td style="padding:5px 8px"><span class="ss-pill ${r.status==='approved'?'on':r.status==='rejected'?'warn':''}">${esc(r.status||'pending')}</span></td>
9318
- <td style="padding:5px 8px;color:var(--text-lo)">${r.createdAt ? relTime(r.createdAt) : '—'}</td>
9319
- </tr>`).join('')}</tbody>
9320
- </table>`;
9321
- } catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
9322
- }
9323
-
9324
- // ── THREADS ────────────────────────────────────────────────
9325
- async function v2RenderOrgThreads() {
9326
- const el = document.getElementById('odt-threads');
9327
- if (!el || !_v2SelOrg) return;
9328
- const _rendOrg = _v2SelOrg;
9329
- el.innerHTML = '<div class="loading-txt">Loading…</div>';
9330
- try {
9331
- const _enc = encodeURIComponent(_rendOrg);
9332
- const data = await fetch(`/api/org/${_enc}/threads${DIR?`?dir=${encodeURIComponent(DIR)}`:''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
9333
- if (_v2SelOrg !== _rendOrg) return;
9334
- const threads = Array.isArray(data) ? data : (data.threads || []);
9335
- if (!threads.length) { el.innerHTML = '<div class="empty">No threads found</div>'; return; }
9336
- el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
9337
- <thead><tr style="color:var(--text-xs);text-align:left">
9338
- <th style="padding:5px 8px">Subject</th><th>Author</th><th>Messages</th><th>Issue</th><th>Created</th>
9339
- </tr></thead>
9340
- <tbody>${threads.map(t => `<tr style="border-top:1px solid var(--border)">
9341
- <td style="padding:5px 8px;color:var(--text-hi);max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc((t.subject||t.title||'—').slice(0,50))}</td>
9342
- <td style="padding:5px 8px;color:var(--text-lo)">${esc(t.author||t.createdBy||'—')}</td>
9343
- <td style="padding:5px 8px;color:var(--text-lo);text-align:center">${t.messageCount ?? t.messages ?? '—'}</td>
9344
- <td style="padding:5px 8px;color:var(--text-lo);font-family:var(--mono);font-size:11px">${t.issueId ? `#${esc(t.issueId.toString().slice(0,8))}` : '—'}</td>
9345
- <td style="padding:5px 8px;color:var(--text-lo)">${t.createdAt ? relTime(t.createdAt) : '—'}</td>
9346
- </tr>`).join('')}</tbody>
9347
- </table>`;
9348
- } catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
9349
- }
9350
-
9351
8626
  // stop org
9352
8627
  window.v2StopOrg = async () => {
9353
8628
  if (!_v2SelOrg) return;
@@ -9497,7 +8772,7 @@ function _filterOrgList(q) {
9497
8772
  if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
9498
8773
  if (src) src.close();
9499
8774
  // Do NOT reset seenKeys here — the server replays ~50 events on every reconnect and
9500
- // clearing the set lets all replays through, duplicating _v2OrgEventLog and _activity entries.
8775
+ // clearing the set lets all replays through a second time.
9501
8776
  src = window.mmEventSource(`/api/mastermind-stream${DIR ? `?project=${encodeURIComponent(DIR)}` : ''}`);
9502
8777
  src.onmessage = e => {
9503
8778
  try {
@@ -9513,10 +8788,7 @@ function _filterOrgList(q) {
9513
8788
  // Events without a project field are treated as belonging to the current project.
9514
8789
  const evProject = ev.project || '';
9515
8790
  if (DIR && evProject && !evProject.startsWith(DIR) && !DIR.startsWith(evProject)) return;
9516
- if (!_v2OrgEventLog[n]) _v2OrgEventLog[n] = [];
9517
- _v2OrgEventLog[n].push(ev);
9518
- if (_v2OrgEventLog[n].length > 50) _v2OrgEventLog[n].shift();
9519
- // Update in-memory agent status so Live tab running-agents section reflects reality.
8791
+ // Update in-memory agent status so the org chart's running dots reflect reality.
9520
8792
  // org:agent:online → mark that agent running; org:complete/run:complete → reset all to idle.
9521
8793
  if (n === _v2SelOrg && _v2OrgData && Array.isArray(_v2OrgData._agents)) {
9522
8794
  if (ev.type === 'org:agent:online' && ev.role) {
@@ -9529,34 +8801,7 @@ function _filterOrgList(q) {
9529
8801
  } else if (ev.type === 'org:complete' || ev.type === 'run:complete') {
9530
8802
  _v2OrgData._agents.forEach(a => { a.status = 'idle'; });
9531
8803
  }
9532
- if (_v2OrgTab === 'live' && (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline' || ev.type === 'org:complete' || ev.type === 'run:complete')) v2RenderOrgLive();
9533
- else if (_v2OrgTab === 'heartbeats' && (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline' || ev.type === 'org:complete' || ev.type === 'run:complete')) v2RenderOrgHeartbeats();
9534
- else if (_v2OrgTab === 'members' && (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline' || ev.type === 'org:complete' || ev.type === 'run:complete')) v2RenderOrgMembers();
9535
- else if (_v2OrgTab === 'roles' && (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline' || ev.type === 'org:complete' || ev.type === 'run:complete')) v2RenderOrgRoles();
9536
- else if (_v2OrgTab === 'chart' && (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline' || ev.type === 'org:complete' || ev.type === 'run:complete')) v2UpdateChartRunningDots();
9537
- // Health tab: update KPI counters live from SSE events so agents_active and
9538
- // tasks_pending reflect current state without waiting for a run-event re-fetch.
9539
- if (_v2OrgTab === 'health' && _v2OrgData._health) {
9540
- if (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline' || ev.type === 'org:complete' || ev.type === 'run:complete') {
9541
- _v2OrgData._health.agents_active = _v2OrgData._agents.filter(a => a.status === 'running').length;
9542
- v2RenderOrgHealth();
9543
- }
9544
- if (ev.type === 'org:checkpoint' && ev.pending_tasks != null) {
9545
- _v2OrgData._health.tasks_pending = ev.pending_tasks;
9546
- v2RenderOrgHealth();
9547
- }
9548
- if (ev.type === 'run:cycle:complete' && ev.pending != null) {
9549
- _v2OrgData._health.tasks_pending = ev.pending;
9550
- v2RenderOrgHealth();
9551
- }
9552
- }
9553
- }
9554
- // Also push live org:comms / org:checkpoint events directly into the Live tab
9555
- // activity feed so they appear in real-time without waiting for the 5s poll.
9556
- if (n === _v2SelOrg && _v2OrgTab === 'live' && _v2OrgData) {
9557
- if (!Array.isArray(_v2OrgData._activity)) _v2OrgData._activity = [];
9558
- _v2OrgData._activity.push({ ...ev, _sseOnly: true });
9559
- v2RenderOrgLive();
8804
+ if (_v2OrgTab === 'chart' && (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline' || ev.type === 'org:complete' || ev.type === 'run:complete')) v2UpdateChartRunningDots();
9560
8805
  }
9561
8806
  // Pulse the org list dot on comms/checkpoint events so the user can see the org is active
9562
8807
  // without switching to the activity tab
@@ -9586,78 +8831,14 @@ function _filterOrgList(q) {
9586
8831
  const listOrg = _v2Orgs.find(o => o.name === _v2SelOrg) || {};
9587
8832
  if (_v2OrgData) _v2OrgData.running = listOrg.running;
9588
8833
  v2UpdateOrgHeader(listOrg, _v2OrgData);
9589
- if (_v2OrgTab === 'health') {
9590
- // Re-fetch health after run events so stats (agents_active, success_rate, total_runs) are current
9591
- const _hEnc = encodeURIComponent(_v2SelOrg);
9592
- fetch(`/api/org/${_hEnc}/health${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
9593
- .then(r => r.ok ? r.json() : null)
9594
- .then(h => { if (h && _v2OrgData && _v2SelOrg === n) { _v2OrgData._health = h; v2RenderOrgHealth(); } })
9595
- .catch(() => { v2RenderOrgHealth(); });
9596
- }
9597
- if (_v2OrgTab === 'tasks') {
9598
- // Re-fetch org data after run events so tasks (todo/doing/done) reflect current store state
9599
- const _tEnc = encodeURIComponent(_v2SelOrg);
9600
- fetch(`/api/org/${_tEnc}${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
9601
- .then(r => r.ok ? r.json() : null)
9602
- .then(d => { if (d && _v2OrgData && _v2SelOrg === n && d.tasks !== undefined) { _v2OrgData.tasks = d.tasks; v2RenderOrgTasks(); } })
9603
- .catch(() => { v2RenderOrgTasks(); });
9604
- }
9605
- if (_v2OrgTab === 'board' || _v2OrgTab === 'issues-full') {
9606
- // Re-fetch issues after run events so board/issues-full reflect current state
9607
- const _iEnc = encodeURIComponent(_v2SelOrg);
9608
- fetch(`/api/org/${_iEnc}/issues${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
9609
- .then(r => r.ok ? r.json() : null)
9610
- .then(d => { if (d && _v2OrgData && _v2SelOrg === n) { _v2OrgData._issues = Array.isArray(d) ? d : (d?.issues || []); _v2OrgTab === 'board' ? v2RenderOrgBoard() : v2RenderOrgIssuesFull(); } })
9611
- .catch(() => { _v2OrgTab === 'board' ? v2RenderOrgBoard() : v2RenderOrgIssuesFull(); });
9612
- }
9613
- if (_v2OrgTab === 'goals') {
9614
- // Re-fetch org data after run events so goal progress (filled/total/status) reflects current state
9615
- const _glEnc = encodeURIComponent(_v2SelOrg);
9616
- fetch(`/api/org/${_glEnc}${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
9617
- .then(r => r.ok ? r.json() : null)
9618
- .then(d => { if (d && _v2OrgData && _v2SelOrg === n && d.goals !== undefined) { _v2OrgData.goals = d.goals; v2RenderOrgGoals(); } })
9619
- .catch(() => { v2RenderOrgGoals(); });
9620
- }
9621
8834
  if (_v2OrgTab === 'chat') {
9622
8835
  // Re-load chat run list after run events so cycleCount/eventCount in dropdown reflect disk state
9623
8836
  _odtLoadChatSessions().catch(() => {});
9624
8837
  }
9625
- if (_v2OrgTab === 'heartbeats') {
9626
- // Re-fetch agents after run events to get fresh tokensIn/tokensOut/lastHeartbeat
9627
- // Heartbeats tab derives from _v2OrgData._agents.
9628
- const _agEnc = encodeURIComponent(_v2SelOrg);
9629
- fetch(`/api/org/${_agEnc}/agents${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
9630
- .then(r => r.ok ? r.json() : null)
9631
- .then(d => { if (d && _v2OrgData && _v2SelOrg === n) { _v2OrgData._agents = Array.isArray(d) ? d : (d?.agents || []); v2RenderOrgHeartbeats(); } })
9632
- .catch(() => { v2RenderOrgHeartbeats(); });
9633
- }
9634
- if (_v2OrgTab === 'myissues') {
9635
- // Re-render myissues after run events — it fetches its own data on every render
9636
- v2RenderOrgMyIssues();
9637
- }
9638
- if (_v2OrgTab === 'charts') {
9639
- // Re-fetch activity after run events so the 14-day heatmap reflects the completed run
9640
- const _chEnc = encodeURIComponent(_v2SelOrg);
9641
- fetch(`/api/org/${_chEnc}/activity${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
9642
- .then(r => r.ok ? r.json() : [])
9643
- .then(d => { if (_v2OrgData && _v2SelOrg === n) { _v2OrgData._activity = Array.isArray(d) ? d : []; v2RenderOrgCharts(); } })
9644
- .catch(() => { v2RenderOrgCharts(); });
9645
- }
9646
8838
  }
9647
8839
  }
9648
8840
  }, 400);
9649
8841
  }
9650
- // run:cycle:complete: lightweight re-fetch for tasks without renderOrgs()
9651
- // — cycles fire frequently and only affect task state, not org-list status.
9652
- if (ev.type === 'run:cycle:complete' && _v2SelOrg === n && _v2OrgData) {
9653
- if (_v2OrgTab === 'tasks') {
9654
- const _tcEnc = encodeURIComponent(_v2SelOrg);
9655
- fetch(`/api/org/${_tcEnc}${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
9656
- .then(r => r.ok ? r.json() : null)
9657
- .then(d => { if (d && _v2OrgData && _v2SelOrg === n && d.tasks !== undefined) { _v2OrgData.tasks = d.tasks; v2RenderOrgTasks(); } })
9658
- .catch(() => { v2RenderOrgTasks(); });
9659
- }
9660
- }
9661
8842
  } catch(_) {}
9662
8843
  };
9663
8844
  src.onerror = () => {
@@ -14513,7 +13694,6 @@ Object.assign(window, {
14513
13694
  v2HandleAvatarFile: _v2HandleAvatarFile,
14514
13695
  orgApprovalAction: _orgApprovalAction,
14515
13696
  orgGateAction: _orgGateAction,
14516
- generateOrgSettingsCmd: _generateOrgSettingsCmd,
14517
13697
  setOdtChatMode: _setOdtChatMode,
14518
13698
  v2SaveOrgConfig: _v2SaveOrgConfig,
14519
13699
  setHeatmapFilter: _setHeatmapFilter,