crewswarm 0.9.1 → 0.9.3

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 (210) hide show
  1. package/README.md +22 -9
  2. package/apps/dashboard/dist/assets/{chat-core-Cx4sTxDd.js → chat-core-3KirthZA.js} +1 -1
  3. package/apps/dashboard/dist/assets/index-GSWxxEPO.js +2 -0
  4. package/apps/dashboard/dist/assets/{tab-pm-loop-tab-Bfd449B4.js → tab-pm-loop-tab-DiAPTJXu.js} +1 -1
  5. package/apps/dashboard/dist/assets/{tab-projects-tab-DhNWnlzt.js → tab-projects-tab-SFH4E--a.js} +1 -1
  6. package/apps/dashboard/dist/assets/tab-settings-tab-BselH1c0.js +1 -0
  7. package/apps/dashboard/dist/index.html +82 -11
  8. package/apps/vibe/README.md +2 -2
  9. package/apps/vibe/package.json +1 -1
  10. package/apps/vibe/server.mjs +3 -3
  11. package/crew-lead.mjs +48 -5
  12. package/lib/bridges/gateway-ws.mjs +4 -0
  13. package/lib/bridges/tmux-bridge.mjs +200 -0
  14. package/lib/cli-process-tracker.mjs +2 -1
  15. package/lib/crew-lead/chat-handler.mjs +34 -0
  16. package/lib/crew-lead/http-server.mjs +340 -14
  17. package/lib/crew-lead/llm-caller.mjs +24 -8
  18. package/lib/crew-lead/prompts.mjs +7 -0
  19. package/lib/crew-lead/wave-dispatcher.mjs +53 -3
  20. package/lib/crew-lead/ws-router.mjs +219 -27
  21. package/lib/engines/engine-registry.mjs +9 -0
  22. package/lib/engines/rt-envelope.mjs +1 -0
  23. package/lib/engines/runners.mjs +26 -2
  24. package/lib/runtime/config.mjs +7 -0
  25. package/lib/runtime/paths.mjs +12 -8
  26. package/lib/sessions/session-manager.mjs +287 -0
  27. package/package.json +35 -15
  28. package/scripts/capture-build-flow.mjs +118 -0
  29. package/scripts/coverage-report.mjs +209 -0
  30. package/scripts/coverage-summary.mjs +47 -0
  31. package/scripts/dashboard-validation.mjs +74 -0
  32. package/scripts/dashboard.mjs +560 -70
  33. package/scripts/live-bridge-matrix.mjs +79 -0
  34. package/scripts/live-cli-matrix.mjs +166 -0
  35. package/scripts/live-crewchat-check.mjs +42 -0
  36. package/scripts/live-engine-matrix.mjs +50 -0
  37. package/scripts/live-provider-failover-matrix.mjs +107 -0
  38. package/scripts/live-provider-matrix.mjs +228 -0
  39. package/scripts/restart-all-from-repo.sh +4 -4
  40. package/scripts/smoke-dispatch.mjs +4 -1
  41. package/scripts/test-blast-radius.mjs +204 -0
  42. package/scripts/test-report-summary.mjs +88 -0
  43. package/scripts/test-reporter.mjs +651 -0
  44. package/scripts/test-rerun.mjs +136 -0
  45. package/scripts/tmux-bridge +130 -0
  46. package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js.br +0 -0
  47. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
  48. package/apps/dashboard/dist/assets/components-BS9fQjE_.js.br +0 -0
  49. package/apps/dashboard/dist/assets/core-utils-CmOkXgzi.js.br +0 -0
  50. package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
  51. package/apps/dashboard/dist/assets/index-DnClJ1ee.js +0 -2
  52. package/apps/dashboard/dist/assets/index-DnClJ1ee.js.br +0 -0
  53. package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js.br +0 -0
  54. package/apps/dashboard/dist/assets/setup-wizard-CA0Or47w.js.br +0 -0
  55. package/apps/dashboard/dist/assets/tab-agents-tab-BgpIsjkw.js.br +0 -0
  56. package/apps/dashboard/dist/assets/tab-comms-tab-kguqTIzD.js.br +0 -0
  57. package/apps/dashboard/dist/assets/tab-contacts-tab-DiOyMYth.js.br +0 -0
  58. package/apps/dashboard/dist/assets/tab-engines-tab-BsdZVvU0.js.br +0 -0
  59. package/apps/dashboard/dist/assets/tab-memory-tab-Cu6u13EQ.js.br +0 -0
  60. package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js.br +0 -0
  61. package/apps/dashboard/dist/assets/tab-pm-loop-tab-Bfd449B4.js.br +0 -0
  62. package/apps/dashboard/dist/assets/tab-projects-tab-DhNWnlzt.js.br +0 -0
  63. package/apps/dashboard/dist/assets/tab-prompts-tab-DVkUNaJd.js.br +0 -0
  64. package/apps/dashboard/dist/assets/tab-services-tab-DU_LH3uG.js.br +0 -0
  65. package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js +0 -1
  66. package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js.br +0 -0
  67. package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js.br +0 -0
  68. package/apps/dashboard/dist/assets/tab-spending-tab-DEccQHnt.js.br +0 -0
  69. package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BNrd88-r.js.br +0 -0
  70. package/apps/dashboard/dist/assets/tab-swarm-tab-B1AcjL1W.js.br +0 -0
  71. package/apps/dashboard/dist/assets/tab-usage-tab-BIOOnB-Y.js.br +0 -0
  72. package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js.br +0 -0
  73. package/apps/dashboard/dist/assets/tab-workflows-tab-B-soSy1k.js.br +0 -0
  74. package/apps/dashboard/dist/index.html.br +0 -0
  75. package/apps/dashboard/index.html +0 -6459
  76. package/apps/dashboard/package.json +0 -15
  77. package/apps/dashboard/src/app.js +0 -2823
  78. package/apps/dashboard/src/app.js.br +0 -0
  79. package/apps/dashboard/src/app.js.gz +0 -0
  80. package/apps/dashboard/src/chat/chat-actions.js +0 -1847
  81. package/apps/dashboard/src/chat/chat-actions.js.br +0 -0
  82. package/apps/dashboard/src/chat/unified-messages.js +0 -327
  83. package/apps/dashboard/src/chat/unified-messages.js.br +0 -0
  84. package/apps/dashboard/src/cli-process.js +0 -208
  85. package/apps/dashboard/src/cli-process.js.br +0 -0
  86. package/apps/dashboard/src/cli-process.js.gz +0 -0
  87. package/apps/dashboard/src/components/active-tasks-panel.js +0 -175
  88. package/apps/dashboard/src/components/active-tasks-panel.js.br +0 -0
  89. package/apps/dashboard/src/core/api.js +0 -18
  90. package/apps/dashboard/src/core/api.js.br +0 -0
  91. package/apps/dashboard/src/core/dom.js +0 -228
  92. package/apps/dashboard/src/core/dom.js.br +0 -0
  93. package/apps/dashboard/src/core/state.js +0 -91
  94. package/apps/dashboard/src/core/state.js.br +0 -0
  95. package/apps/dashboard/src/core/task-manager.js +0 -134
  96. package/apps/dashboard/src/core/task-manager.js.br +0 -0
  97. package/apps/dashboard/src/orchestration-status.js +0 -127
  98. package/apps/dashboard/src/orchestration-status.js.br +0 -0
  99. package/apps/dashboard/src/setup-wizard.js +0 -562
  100. package/apps/dashboard/src/setup-wizard.js.br +0 -0
  101. package/apps/dashboard/src/styles.css +0 -2085
  102. package/apps/dashboard/src/styles.css.br +0 -0
  103. package/apps/dashboard/src/styles.css.gz +0 -0
  104. package/apps/dashboard/src/tabs/agents-tab.js +0 -2237
  105. package/apps/dashboard/src/tabs/agents-tab.js.br +0 -0
  106. package/apps/dashboard/src/tabs/benchmarks-tab.js +0 -229
  107. package/apps/dashboard/src/tabs/benchmarks-tab.js.br +0 -0
  108. package/apps/dashboard/src/tabs/comms-tab.js +0 -955
  109. package/apps/dashboard/src/tabs/comms-tab.js.br +0 -0
  110. package/apps/dashboard/src/tabs/contacts-tab.js +0 -654
  111. package/apps/dashboard/src/tabs/contacts-tab.js.br +0 -0
  112. package/apps/dashboard/src/tabs/engines-tab.js +0 -175
  113. package/apps/dashboard/src/tabs/engines-tab.js.br +0 -0
  114. package/apps/dashboard/src/tabs/memory-tab.js +0 -182
  115. package/apps/dashboard/src/tabs/memory-tab.js.br +0 -0
  116. package/apps/dashboard/src/tabs/models-tab.js +0 -450
  117. package/apps/dashboard/src/tabs/models-tab.js.br +0 -0
  118. package/apps/dashboard/src/tabs/pm-loop-tab.js +0 -185
  119. package/apps/dashboard/src/tabs/pm-loop-tab.js.br +0 -0
  120. package/apps/dashboard/src/tabs/projects-tab.js +0 -663
  121. package/apps/dashboard/src/tabs/projects-tab.js.br +0 -0
  122. package/apps/dashboard/src/tabs/projects-tab.js.gz +0 -0
  123. package/apps/dashboard/src/tabs/prompts-tab.js +0 -160
  124. package/apps/dashboard/src/tabs/prompts-tab.js.br +0 -0
  125. package/apps/dashboard/src/tabs/services-tab.js +0 -202
  126. package/apps/dashboard/src/tabs/services-tab.js.br +0 -0
  127. package/apps/dashboard/src/tabs/settings-tab.js +0 -803
  128. package/apps/dashboard/src/tabs/settings-tab.js.br +0 -0
  129. package/apps/dashboard/src/tabs/skills-tab.js +0 -284
  130. package/apps/dashboard/src/tabs/skills-tab.js.br +0 -0
  131. package/apps/dashboard/src/tabs/spending-tab.js +0 -173
  132. package/apps/dashboard/src/tabs/spending-tab.js.br +0 -0
  133. package/apps/dashboard/src/tabs/swarm-chat-tab.js +0 -660
  134. package/apps/dashboard/src/tabs/swarm-chat-tab.js.br +0 -0
  135. package/apps/dashboard/src/tabs/swarm-tab.js +0 -538
  136. package/apps/dashboard/src/tabs/swarm-tab.js.br +0 -0
  137. package/apps/dashboard/src/tabs/usage-tab.js +0 -390
  138. package/apps/dashboard/src/tabs/usage-tab.js.br +0 -0
  139. package/apps/dashboard/src/tabs/waves-tab.js +0 -238
  140. package/apps/dashboard/src/tabs/waves-tab.js.br +0 -0
  141. package/apps/dashboard/src/tabs/workflows-tab.js +0 -747
  142. package/apps/dashboard/src/tabs/workflows-tab.js.br +0 -0
  143. package/apps/vibe/.crew/agent-memory/pipeline.json +0 -304
  144. package/apps/vibe/.crew/cost.json +0 -17
  145. package/apps/vibe/.crew/json-parse-metrics.jsonl +0 -27
  146. package/apps/vibe/.crew/pipeline-metrics.jsonl +0 -27
  147. package/apps/vibe/.crew/pipeline-runs/pipeline-0f90c392-2425-4ae5-850c-bd9d17b1d690.jsonl +0 -5
  148. package/apps/vibe/.crew/pipeline-runs/pipeline-1c269dd9-a63f-4fba-af81-5cf08048ef06.jsonl +0 -5
  149. package/apps/vibe/.crew/pipeline-runs/pipeline-288a7765-da24-4a22-89bc-1f3cc9b0562c.jsonl +0 -5
  150. package/apps/vibe/.crew/pipeline-runs/pipeline-2c78fd22-a657-4bd1-bc49-0679fb384409.jsonl +0 -5
  151. package/apps/vibe/.crew/pipeline-runs/pipeline-3da23550-22ed-4904-9a0a-8e79c1f3024c.jsonl +0 -5
  152. package/apps/vibe/.crew/pipeline-runs/pipeline-3e6fe08d-3264-404a-8df3-aab7efef10e7.jsonl +0 -5
  153. package/apps/vibe/.crew/pipeline-runs/pipeline-42eec610-57fe-4e09-9e7e-b315038495c2.jsonl +0 -5
  154. package/apps/vibe/.crew/pipeline-runs/pipeline-4438eb4c-ae13-42b1-90e2-b043d8983be8.jsonl +0 -5
  155. package/apps/vibe/.crew/pipeline-runs/pipeline-4740a9f5-86e7-44b6-a394-de433e291727.jsonl +0 -5
  156. package/apps/vibe/.crew/pipeline-runs/pipeline-49e1da6a-957e-48fd-9220-415019e4f8e2.jsonl +0 -5
  157. package/apps/vibe/.crew/pipeline-runs/pipeline-4c9251db-be68-427b-a3fc-a264f2b5778d.jsonl +0 -5
  158. package/apps/vibe/.crew/pipeline-runs/pipeline-6413fa33-a802-4b57-a8c0-a9056ad67842.jsonl +0 -5
  159. package/apps/vibe/.crew/pipeline-runs/pipeline-65e29a57-664d-4196-8109-017e364f182e.jsonl +0 -5
  160. package/apps/vibe/.crew/pipeline-runs/pipeline-6aa04bc5-9593-4b1f-b58d-3bf2978cb602.jsonl +0 -5
  161. package/apps/vibe/.crew/pipeline-runs/pipeline-6e1cba53-9b70-457e-99e0-59199149dd21.jsonl +0 -5
  162. package/apps/vibe/.crew/pipeline-runs/pipeline-749f41cc-4dac-4204-be64-873a6080a0d2.jsonl +0 -5
  163. package/apps/vibe/.crew/pipeline-runs/pipeline-74d68121-e181-4864-bd9a-c3211341dfaf.jsonl +0 -5
  164. package/apps/vibe/.crew/pipeline-runs/pipeline-8509bc24-142d-4e07-b44a-a50bf99d1103.jsonl +0 -5
  165. package/apps/vibe/.crew/pipeline-runs/pipeline-960339c6-07ca-43ce-9900-f6e1702b39b9.jsonl +0 -5
  166. package/apps/vibe/.crew/pipeline-runs/pipeline-9bef2dd2-6122-42e5-b3d9-19f4d80f9e40.jsonl +0 -5
  167. package/apps/vibe/.crew/pipeline-runs/pipeline-9c6480a9-7031-4146-b241-825b9a2d1de1.jsonl +0 -5
  168. package/apps/vibe/.crew/pipeline-runs/pipeline-9fd42426-8492-4157-9d5f-e1537c060489.jsonl +0 -2
  169. package/apps/vibe/.crew/pipeline-runs/pipeline-ad6d40a3-2f5e-46a9-a345-47caaccc51aa.jsonl +0 -5
  170. package/apps/vibe/.crew/pipeline-runs/pipeline-bc606133-8d5b-4535-8d85-f1a29cdaa981.jsonl +0 -5
  171. package/apps/vibe/.crew/pipeline-runs/pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2.jsonl +0 -5
  172. package/apps/vibe/.crew/pipeline-runs/pipeline-c1a13ccd-634a-4d01-a4a7-1177b8a752ff.jsonl +0 -5
  173. package/apps/vibe/.crew/pipeline-runs/pipeline-c7d27b42-249e-4bd4-8f26-6aa998110b8a.jsonl +0 -5
  174. package/apps/vibe/.crew/pipeline-runs/pipeline-cca2e9b9-4a34-4d25-a311-5c793fa7e91e.jsonl +0 -5
  175. package/apps/vibe/.crew/sandbox.json +0 -7
  176. package/apps/vibe/.crew/session.json +0 -330
  177. package/apps/vibe/.crew/training-data.jsonl +0 -0
  178. package/apps/vibe/.github/workflows/studio-quality.yml +0 -37
  179. package/apps/vibe/.studio-data/project-messages/chuck-norris.jsonl +0 -18
  180. package/apps/vibe/.studio-data/project-messages/general.jsonl +0 -81
  181. package/apps/vibe/.studio-data/project-messages/studio-local.jsonl +0 -18
  182. package/apps/vibe/ARCHITECTURE.md +0 -3393
  183. package/apps/vibe/QUICK-REFERENCE.md +0 -211
  184. package/apps/vibe/ROADMAP.md +0 -41
  185. package/apps/vibe/STUDIO-SETUP-COMPLETE.md +0 -35
  186. package/apps/vibe/VISUAL-GUIDE.md +0 -378
  187. package/apps/vibe/capture-demo.mjs +0 -160
  188. package/apps/vibe/capture-full-demo.mjs +0 -255
  189. package/apps/vibe/capture-quickstart.mjs +0 -256
  190. package/apps/vibe/capture-vibe-assets.mjs +0 -71
  191. package/apps/vibe/capture-vibe-video.mjs +0 -260
  192. package/apps/vibe/check-buttons.js +0 -41
  193. package/apps/vibe/diagnose.html +0 -106
  194. package/apps/vibe/fix-buttons.js +0 -103
  195. package/apps/vibe/index.html +0 -3404
  196. package/apps/vibe/package-lock.json +0 -920
  197. package/apps/vibe/scripts/studio-pty-host.py +0 -117
  198. package/apps/vibe/src/main.js +0 -2940
  199. package/apps/vibe/src/register-all-languages.js +0 -98
  200. package/apps/vibe/start-studio.sh +0 -11
  201. package/apps/vibe/test/accessibility-tests.js +0 -77
  202. package/apps/vibe/test/browser-performance-audit.mjs +0 -205
  203. package/apps/vibe/test/performance-tests.js +0 -120
  204. package/apps/vibe/test/security-tests.js +0 -213
  205. package/apps/vibe/tests/e2e.local.mjs +0 -54
  206. package/apps/vibe/tests/server.smoke.mjs +0 -106
  207. package/apps/vibe/update_website.mjs +0 -74
  208. package/apps/vibe/vite.config.js +0 -19
  209. package/lib/crew-lead/chat-handler.mjs.bak +0 -1274
  210. package/lib/engines/rt-envelope.mjs.backup-current +0 -870
@@ -1,538 +0,0 @@
1
- /**
2
- * Swarm (sessions), RT Messages, and DLQ tab — extracted from app.js
3
- * Deps: getJSON, postJSON (core/api), escHtml, showNotification, fmt, createdAt (core/dom)
4
- * Inject: initSwarmTab({ hideAllViews, setNavActive })
5
- */
6
-
7
- import { getJSON, postJSON } from '../core/api.js';
8
- import { escHtml, showNotification, fmt, createdAt } from '../core/dom.js';
9
- import { state, persistState, restoreScrollPosition } from '../core/state.js';
10
-
11
- let _hideAllViews = () => {};
12
- let _setNavActive = () => {};
13
-
14
- export function initSwarmTab({ hideAllViews, setNavActive } = {}) {
15
- _hideAllViews = hideAllViews || _hideAllViews;
16
- _setNavActive = setNavActive || _setNavActive;
17
- }
18
-
19
- // ── Swarm (Sessions) ───────────────────────────────────────────────────────────
20
-
21
- let _selected = state.selected || null;
22
- let _selectedEngine = state.selectedEngine || 'opencode'; // opencode, claude, codex, gemini, crew-cli
23
-
24
- export async function loadSessions() {
25
- const box = document.getElementById('sessions');
26
- if (box) box.innerHTML = '<div style="padding:20px;">Loading…</div>';
27
-
28
- // Add engine selector dropdown if not already present
29
- const container = box.parentElement;
30
- let engineSelector = document.getElementById('engine-selector');
31
- if (!engineSelector) {
32
- engineSelector = document.createElement('div');
33
- engineSelector.id = 'engine-selector';
34
- engineSelector.style.cssText = 'padding:12px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;';
35
- engineSelector.innerHTML = '<label style="font-size:13px;font-weight:500;color:var(--text-2);">CLI:</label>'
36
- + '<select id="engine-select" style="padding:6px 10px;border:1px solid var(--border);border-radius:6px;background:var(--bg-1);color:var(--text-1);font-size:13px;cursor:pointer;">'
37
- + '<option value="opencode">OpenCode</option>'
38
- + '<option value="claude">Claude Code</option>'
39
- + '<option value="codex">Codex CLI</option>'
40
- + '<option value="gemini">Gemini CLI</option>'
41
- + '<option value="crew-cli">crew-cli</option>'
42
- + '</select>'
43
- + '<span style="font-size:12px;color:var(--text-3);margin-left:8px;" id="session-count"></span>';
44
- container.insertBefore(engineSelector, box);
45
-
46
- document.getElementById('engine-select').addEventListener('change', (e) => {
47
- _selectedEngine = e.target.value;
48
- state.selectedEngine = _selectedEngine;
49
- persistState();
50
- _selected = null; // Reset selection when switching engines
51
- loadSessions();
52
- });
53
- }
54
-
55
- // Set dropdown to current engine
56
- const select = document.getElementById('engine-select');
57
- if (select) select.value = _selectedEngine;
58
-
59
- try {
60
- const activeProjectId = state.chatActiveProjectId || 'general';
61
- const endpoint =
62
- '/api/engine-sessions?engine=' + encodeURIComponent(_selectedEngine) +
63
- '&projectId=' + encodeURIComponent(activeProjectId);
64
- const result = await getJSON(endpoint);
65
- const data = result.sessions || result || [];
66
-
67
- const box2 = document.getElementById('sessions');
68
- const countEl = document.getElementById('session-count');
69
-
70
- box2.innerHTML = '';
71
-
72
- if (!data.length) {
73
- const engineNames = {
74
- 'opencode': 'OpenCode',
75
- 'claude': 'Claude Code',
76
- 'codex': 'Codex CLI',
77
- 'gemini': 'Gemini CLI',
78
- 'crew-cli': 'crew-cli'
79
- };
80
- const engineName = engineNames[_selectedEngine] || _selectedEngine;
81
-
82
- box2.innerHTML = '<div style="padding:20px 16px;">'
83
- + `<div style="font-size:13px;font-weight:600;margin-bottom:6px;">No ${engineName} sessions</div>`
84
- + '<div style="font-size:12px;color:var(--text-3);line-height:1.6;">'
85
- + `No session history found for <strong>${engineName}</strong>. `
86
- + 'Run a task using this engine to see sessions here.'
87
- + '</div></div>';
88
- if (countEl) countEl.textContent = '';
89
- return;
90
- }
91
-
92
- if (countEl) countEl.textContent = `${data.length} session${data.length !== 1 ? 's' : ''}`;
93
- if (!_selected && data[0]) _selected = data[0].id;
94
-
95
- function crewAgentFromTitle(title) {
96
- if (!title || typeof title !== 'string') return null;
97
- const m = title.match(/\[?(crew-\w+)\]?/);
98
- return m ? m[1] : null;
99
- }
100
- function inferAgentFromTitle(title) {
101
- if (!title || typeof title !== 'string') return null;
102
- if (/\bFixer\b|fixer\s+task|fix\s+.*\.py|syntax\s+error/i.test(title)) return 'fixer';
103
- if (/\bQA\b|qa\s+audit|audit:/i.test(title)) return 'qa';
104
- if (/\bPM\b|crew-pm|roadmap\b/i.test(title)) return 'pm';
105
- if (/\bCoder\b|coder\s+task|frontend\b|backend\b/i.test(title)) return 'coder';
106
- if (/\bSecurity\b|security\s+review/i.test(title)) return 'security';
107
- if (/\bCopywriter\b|copy\s+task/i.test(title)) return 'copywriter';
108
- return null;
109
- }
110
- function isOpencodeCodename(slug) {
111
- return slug && /^[a-z]+-[a-z]+$/.test(slug) && !slug.startsWith('crew-');
112
- }
113
-
114
- data.forEach(s => {
115
- const div = document.createElement('div');
116
- const sessionId = s.id || s.sessionId || '';
117
- div.className = 'row' + (sessionId === _selected ? ' active' : '');
118
- div.onclick = () => { _selected = sessionId; state.selected = sessionId; persistState(); loadSessions(); loadMessages(); };
119
-
120
- // Extract metadata based on engine
121
- let title = s.title || s.slug || sessionId;
122
- let meta = s.directory || '';
123
- let badge = '';
124
-
125
- if (_selectedEngine === 'opencode') {
126
- const crewAgent = crewAgentFromTitle(title);
127
- const inferred = inferAgentFromTitle(title);
128
- const slug = s.slug || '';
129
- const agent = crewAgent || (slug && !isOpencodeCodename(slug) ? slug : null) || inferred;
130
- const slugLabel = isOpencodeCodename(slug) ? ' (' + slug + ')' : '';
131
- badge = agent ? ('Assigned to: ' + agent + slugLabel) : (slug ? ('Assigned to: ' + slug + ' (OpenCode session)') : '');
132
- } else if (_selectedEngine === 'claude') {
133
- meta = s.file ? s.file.split('/').pop().replace('.jsonl', '') : '';
134
- } else if (_selectedEngine === 'codex') {
135
- meta = s.file || '';
136
- } else if (_selectedEngine === 'gemini') {
137
- meta = 'Project: ' + sessionId;
138
- } else if (_selectedEngine === 'crew-cli') {
139
- badge = s.engine + ' / ' + s.project;
140
- meta = s.file || '';
141
- }
142
-
143
- // Limit title length
144
- if (title.length > 80) title = title.slice(0, 77) + '...';
145
-
146
- div.innerHTML = '<div><strong>' + escHtml(title) + '</strong></div>'
147
- + (meta ? '<div class="meta">' + escHtml(meta) + '</div>' : '')
148
- + (badge ? '<div class="meta" style="font-size:11px;color:var(--accent);">' + escHtml(badge) + '</div>' : '');
149
- box2.appendChild(div);
150
- });
151
- } catch(e) {
152
- const box = document.getElementById('sessions');
153
- if (box) box.innerHTML = '<div class="meta" style="padding:20px; color:var(--red-hi);">Error loading sessions.</div>';
154
- }
155
- }
156
-
157
- export async function loadMessages() {
158
- const box = document.getElementById('messages');
159
- if (!_selected) { if (box) box.innerHTML = '<div class="meta">No session selected.</div>'; return; }
160
- try {
161
- // For OpenCode, use the old endpoint. For others, messages are embedded in the session data
162
- if (_selectedEngine === 'opencode') {
163
- const data = await getJSON('/api/messages?session=' + encodeURIComponent(_selected));
164
- box.innerHTML = '';
165
- data.slice(-40).forEach(m => {
166
- const text = (m.parts || []).filter(p => p.type === 'text').map(p => p.text).join('').trim();
167
- if (!text) return;
168
- const div = document.createElement('div');
169
- div.className = 'msg ' + ((m.info && m.info.role) === 'assistant' ? 'a' : 'u');
170
- div.innerHTML = '<div class="meta">' + (m.info && m.info.role) + ' • ' + fmt(createdAt(m.info)) + '</div><div class="t"></div>';
171
- div.querySelector('.t').textContent = text;
172
- box.appendChild(div);
173
- });
174
- } else {
175
- // For other engines, find the session in the cached data
176
- const apiMap = {
177
- 'claude': '/api/claude-sessions',
178
- 'codex': '/api/codex-sessions',
179
- 'gemini': '/api/gemini-sessions',
180
- 'crew-cli': '/api/crew-cli-sessions'
181
- };
182
- const endpoint = apiMap[_selectedEngine];
183
- if (!endpoint) {
184
- box.innerHTML = '<div class="meta">Engine not supported</div>';
185
- return;
186
- }
187
-
188
- const result = await getJSON(endpoint);
189
- const sessions = result.sessions || result || [];
190
- const session = sessions.find(s => s.id === _selected || s.sessionId === _selected);
191
-
192
- if (!session || !session.messages) {
193
- box.innerHTML = '<div class="meta">No messages found</div>';
194
- return;
195
- }
196
-
197
- box.innerHTML = '';
198
- session.messages.slice(-40).forEach(m => {
199
- const div = document.createElement('div');
200
- div.className = 'msg ' + (m.role === 'assistant' ? 'a' : 'u');
201
- const ts = m.ts ? new Date(m.ts).toLocaleString() : '';
202
- div.innerHTML = '<div class="meta">' + m.role + (ts ? ' • ' + ts : '') + '</div><div class="t"></div>';
203
- div.querySelector('.t').textContent = m.text || '';
204
- box.appendChild(div);
205
- });
206
- }
207
- box.scrollTop = box.scrollHeight;
208
- } catch(e) { if (box) box.innerHTML = '<div class="meta">Error: ' + e.message + '</div>'; }
209
- }
210
-
211
- export function showSwarm() {
212
- _hideAllViews();
213
- document.getElementById('sessionsView').classList.add('active');
214
- _setNavActive('navSwarm');
215
- state.activeTab = 'swarm';
216
- persistState();
217
-
218
- // Check if sessions are already rendered (DOM preserved from previous visit)
219
- const sessionsBox = document.getElementById('sessions');
220
- const alreadyLoaded = sessionsBox && sessionsBox.children.length > 1;
221
- if (!alreadyLoaded) {
222
- loadSessions(); loadMessages();
223
- } else {
224
- restoreScrollPosition('swarm');
225
- }
226
- }
227
-
228
- // ── RT Messages ────────────────────────────────────────────────────────────────
229
-
230
- let _rtPaused = false;
231
- let _rtFilter = 'tasks';
232
- let _rtSearch = '';
233
- let _rtSeenIds = new Set();
234
- const RT_SKIP = new Set(['agent.heartbeat','agent.online','agent.offline']);
235
- const RT_TASK_TYPES = new Set(['task.dispatched','task.done','task.completed','task.failed','task.cancelled','task.started','task.reply']);
236
-
237
- function _rtMatchesFilter(m) {
238
- if (RT_SKIP.has(m.type)) return false;
239
- const payload = m.payload || {};
240
- const text = payload.reply || payload.prompt || payload.message || payload.content || '';
241
- if (!text || text === 'run_task') return false;
242
- if (_rtFilter === 'tasks' && !RT_TASK_TYPES.has(m.type)) return false;
243
- if (_rtFilter === 'replies') {
244
- if (!(payload.reply || payload.message || payload.content)) return false;
245
- }
246
- if (_rtSearch) {
247
- const q = _rtSearch.toLowerCase();
248
- if (!(m.from||'').toLowerCase().includes(q) &&
249
- !(m.to ||'').toLowerCase().includes(q) &&
250
- !text.toLowerCase().includes(q) &&
251
- !(m.type||'').toLowerCase().includes(q)) return false;
252
- }
253
- return true;
254
- }
255
-
256
- const RT_PHASE_STYLE = {
257
- 'task.dispatched': { color: 'var(--purple)', label: 'dispatched' },
258
- 'task.started': { color: 'var(--amber)', label: 'started' },
259
- 'task.done': { color: 'var(--green-hi)', label: 'done' },
260
- 'task.completed': { color: 'var(--green-hi)', label: 'completed' },
261
- 'task.reply': { color: 'var(--accent)', label: 'reply' },
262
- 'task.failed': { color: 'var(--red-hi)', label: 'failed' },
263
- 'task.cancelled': { color: 'var(--text-3)', label: 'cancelled' },
264
- };
265
-
266
- function _rtBuildElement(m) {
267
- const payload = m.payload || {};
268
- const fullText = payload.reply || payload.prompt || payload.message || payload.content || '';
269
- const type = m.type || '';
270
- const phase = RT_PHASE_STYLE[type];
271
- const timeStr = m.ts ? new Date(m.ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : '';
272
- const firstLine = fullText.split('\n').map(l => l.trim()).find(l => l.length > 2) || fullText;
273
- const summary = firstLine.length > 90 ? firstLine.slice(0, 90) + '…' : firstLine;
274
- const hasMore = fullText.length > summary.length || fullText.split('\n').length > 1;
275
-
276
- const row = document.createElement('div');
277
- row.style.cssText = 'display:grid;grid-template-columns:auto auto 1fr auto;align-items:center;gap:10px;padding:7px 10px;border-radius:6px;cursor:' + (hasMore ? 'pointer' : 'default') + ';transition:background .12s;border-bottom:1px solid var(--border);';
278
- row.onmouseenter = () => { row.style.background = 'var(--bg-2)'; };
279
- row.onmouseleave = () => { row.style.background = ''; };
280
-
281
- const agentsEl = document.createElement('div');
282
- agentsEl.style.cssText = 'display:flex;align-items:center;gap:5px;white-space:nowrap;min-width:0;';
283
- const fromPill = document.createElement('span');
284
- fromPill.style.cssText = 'font-size:11px;font-weight:600;color:var(--text-1);max-width:110px;overflow:hidden;text-overflow:ellipsis;';
285
- fromPill.textContent = (m.from || '?').replace('crew-', '');
286
- fromPill.title = m.from || '';
287
- agentsEl.appendChild(fromPill);
288
- if (m.to && m.to !== m.from) {
289
- const arrow = document.createElement('span');
290
- arrow.style.cssText = 'font-size:10px;color:var(--text-3);flex-shrink:0;';
291
- arrow.textContent = '→';
292
- const toPill = document.createElement('span');
293
- toPill.style.cssText = 'font-size:11px;color:var(--text-2);max-width:110px;overflow:hidden;text-overflow:ellipsis;';
294
- toPill.textContent = (m.to || '').replace('crew-', '');
295
- toPill.title = m.to || '';
296
- agentsEl.appendChild(arrow);
297
- agentsEl.appendChild(toPill);
298
- }
299
-
300
- const badgeContainer = document.createElement('div');
301
- badgeContainer.style.cssText = 'display:flex;align-items:center;gap:4px;flex-shrink:0;';
302
-
303
- const badge = document.createElement('span');
304
- const ps = phase || { color: 'var(--text-3)', label: type.split('.').pop() || type };
305
- badge.style.cssText = 'font-size:10px;font-weight:600;padding:2px 7px;border-radius:20px;white-space:nowrap;flex-shrink:0;color:#fff;background:' + ps.color + ';letter-spacing:.03em;';
306
- badge.textContent = ps.label;
307
- badgeContainer.appendChild(badge);
308
-
309
- // Engine badge for task.done messages
310
- if (type === 'task.done' && payload.engineUsed) {
311
- const engineColors = {
312
- 'claude': '#e07a5f', // warm coral for Claude Code
313
- 'codex': '#8338ec', // purple for Codex
314
- 'cursor': '#3d405b', // dark gray for Cursor
315
- 'opencode': '#06d6a0', // teal for OpenCode
316
- 'gemini': '#4285f4', // Google blue for Gemini
317
- 'docker-sandbox': '#0db7ed' // Docker blue
318
- };
319
- const engineLabels = {
320
- 'claude': '🤖',
321
- 'codex': '🟣',
322
- 'cursor': '🖱',
323
- 'opencode': '⚡',
324
- 'gemini': '✨',
325
- 'docker-sandbox': '🐳'
326
- };
327
- const engine = payload.engineUsed;
328
- const engineBadge = document.createElement('span');
329
- engineBadge.style.cssText = 'font-size:10px;font-weight:600;padding:2px 6px;border-radius:20px;white-space:nowrap;flex-shrink:0;color:#fff;background:' + (engineColors[engine] || 'var(--text-3)') + ';';
330
- engineBadge.textContent = (engineLabels[engine] || '') + ' ' + engine;
331
- engineBadge.title = 'Executed by ' + engine;
332
- badgeContainer.appendChild(engineBadge);
333
- }
334
-
335
- const preview = document.createElement('span');
336
- preview.style.cssText = 'font-size:12px;color:var(--text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;';
337
- preview.textContent = summary;
338
-
339
- const right = document.createElement('div');
340
- right.style.cssText = 'display:flex;align-items:center;gap:6px;flex-shrink:0;';
341
- const timeEl = document.createElement('span');
342
- timeEl.style.cssText = 'font-size:10px;color:var(--text-3);white-space:nowrap;';
343
- timeEl.textContent = timeStr;
344
- right.appendChild(timeEl);
345
- if (hasMore) {
346
- const hint = document.createElement('span');
347
- hint.style.cssText = 'font-size:10px;color:var(--text-3);';
348
- hint.textContent = '▸';
349
- right.appendChild(hint);
350
- }
351
-
352
- row.appendChild(agentsEl);
353
- row.appendChild(badgeContainer);
354
- row.appendChild(preview);
355
- row.appendChild(right);
356
-
357
- if (hasMore) {
358
- const detail = document.createElement('div');
359
- detail.style.cssText = 'display:none;grid-column:1/-1;padding:8px 6px 4px;font-size:12px;color:var(--text-2);white-space:pre-wrap;word-break:break-word;max-height:300px;overflow-y:auto;border-top:1px solid var(--border);margin-top:4px;font-family:monospace;';
360
- detail.textContent = fullText;
361
- const wrap = document.createElement('div');
362
- wrap.style.cssText = 'display:grid;grid-template-columns:1fr;border-radius:6px;overflow:hidden;border-bottom:1px solid var(--border);';
363
- row.style.borderBottom = 'none';
364
- let open = false;
365
- row.onclick = () => {
366
- open = !open;
367
- detail.style.display = open ? 'block' : 'none';
368
- const hint = right.querySelector('span:last-child');
369
- if (hint) hint.textContent = open ? '▾' : '▸';
370
- };
371
- wrap.appendChild(row);
372
- wrap.appendChild(detail);
373
- return wrap;
374
- }
375
- return row;
376
- }
377
-
378
- export async function loadRTMessages() {
379
- if (_rtPaused) return;
380
- const box = document.getElementById('rtMessages');
381
- const rtView = document.getElementById('rtView');
382
- if (!box || !rtView) return;
383
-
384
- // Check if this is first load - only if box has no child elements at all
385
- const firstLoad = box.children.length === 0;
386
- if (firstLoad) {
387
- const loadingDiv = document.createElement('div');
388
- loadingDiv.style.cssText = 'padding:20px;';
389
- loadingDiv.textContent = 'Loading…';
390
- box.replaceChildren(loadingDiv);
391
- }
392
-
393
- const data = await getJSON('/api/rt-messages');
394
- const filtered = data.filter(_rtMatchesFilter);
395
-
396
- // PERFORMANCE FIX: Limit to last 100 messages to prevent DOM bloat
397
- const limited = filtered.slice(-100);
398
-
399
- // Use stable hash based on message content, not timestamp
400
- const newHash = limited.map(m => {
401
- const payload = m.payload || {};
402
- const text = payload.reply || payload.prompt || payload.message || payload.content || '';
403
- return `${m.type}|${m.from}|${m.to}|${text.slice(0, 100)}`;
404
- }).join('::');
405
-
406
- if (newHash === window._rtLastHash && !firstLoad) {
407
- return; // No changes, skip redraw
408
- }
409
- window._rtLastHash = newHash;
410
-
411
- const rtAtBottom = () => rtView.scrollHeight - rtView.scrollTop - rtView.clientHeight < 100;
412
- const wasAtBottom = rtAtBottom();
413
- const scrollPos = rtView.scrollTop; // Save scroll position
414
-
415
- // CRITICAL FIX: Use DocumentFragment + replaceChildren to avoid flash
416
- const fragment = document.createDocumentFragment();
417
-
418
- if (!limited.length) {
419
- const emptyDiv = document.createElement('div');
420
- emptyDiv.style.cssText = 'padding:24px;text-align:center;font-size:12px;color:var(--text-3);';
421
- emptyDiv.textContent = 'No events match the current filter.';
422
- fragment.appendChild(emptyDiv);
423
- } else {
424
- const header = document.createElement('div');
425
- header.style.cssText = 'display:grid;grid-template-columns:auto auto 1fr auto;gap:10px;padding:4px 10px 6px;font-size:10px;font-weight:600;color:var(--text-3);letter-spacing:.06em;text-transform:uppercase;border-bottom:2px solid var(--border);margin-bottom:2px;';
426
- ['Agent', 'Phase', 'Summary', 'Time'].forEach(label => {
427
- const th = document.createElement('span'); th.textContent = label; header.appendChild(th);
428
- });
429
- fragment.appendChild(header);
430
- limited.forEach(m => fragment.appendChild(_rtBuildElement(m)));
431
- }
432
-
433
- // CRITICAL: Use replaceChildren instead of innerHTML = '' to prevent flash
434
- box.replaceChildren(fragment);
435
-
436
- // Restore scroll position: if user was at bottom, stay at bottom; otherwise preserve their scroll position
437
- if (wasAtBottom) {
438
- rtView.scrollTop = rtView.scrollHeight;
439
- } else {
440
- // If saved position is now beyond the new content height, scroll to the bottom of new content minus viewport
441
- const maxScroll = Math.max(0, rtView.scrollHeight - rtView.clientHeight);
442
- rtView.scrollTop = Math.min(scrollPos, maxScroll);
443
- }
444
- const scrollBtn = document.getElementById('rtScrollBtn');
445
- if (scrollBtn) scrollBtn.style.display = rtAtBottom() ? 'none' : 'block';
446
-
447
- if (!rtView._scrollListenerBound) {
448
- rtView._scrollListenerBound = true;
449
- rtView.addEventListener('scroll', () => {
450
- if (scrollBtn) scrollBtn.style.display = rtAtBottom() ? 'none' : 'block';
451
- });
452
- }
453
- }
454
-
455
- export function toggleRTPause() {
456
- _rtPaused = !_rtPaused;
457
- const btn = document.getElementById('rtPauseBtn');
458
- if (btn) { btn.textContent = _rtPaused ? '▶ Resume' : '⏸ Pause'; btn.style.background = _rtPaused ? 'var(--accent)' : ''; btn.style.color = _rtPaused ? '#fff' : ''; }
459
- }
460
-
461
- export function clearRTMessages() {
462
- _rtSeenIds = new Set();
463
- const box = document.getElementById('rtMessages');
464
- if (box) box.innerHTML = '<div class="meta" style="padding:20px;text-align:center;opacity:.6;">Cleared. New messages will appear on next poll.</div>';
465
- }
466
-
467
- function _initRTFilters() {
468
- document.querySelectorAll('.rt-filter-chip').forEach(btn => {
469
- btn.addEventListener('click', () => {
470
- _rtFilter = btn.dataset.filter;
471
- _rtSeenIds = new Set();
472
- document.querySelectorAll('.rt-filter-chip').forEach(b => {
473
- const active = b === btn;
474
- b.style.background = active ? 'var(--accent)' : 'transparent';
475
- b.style.color = active ? '#fff' : 'var(--text-2)';
476
- b.classList.toggle('active', active);
477
- });
478
- loadRTMessages();
479
- });
480
- });
481
- const search = document.getElementById('rtSearch');
482
- if (search) {
483
- search.addEventListener('input', () => {
484
- _rtSearch = search.value.trim();
485
- _rtSeenIds = new Set();
486
- loadRTMessages();
487
- });
488
- }
489
- }
490
-
491
- export function showRT() {
492
- _hideAllViews();
493
- document.getElementById('rtView').classList.add('active');
494
- _setNavActive('navRT');
495
- _initRTFilters();
496
- loadRTMessages();
497
- const scrollBtn = document.getElementById('rtScrollBtn');
498
- if (scrollBtn) scrollBtn.style.display = 'none';
499
- }
500
-
501
- // ── DLQ ───────────────────────────────────────────────────────────────────────
502
-
503
- export async function loadDLQ() {
504
- const box = document.getElementById('dlqMessages');
505
- if (box) box.innerHTML = '<div style="padding:20px;">Loading…</div>';
506
- const data = await getJSON('/api/dlq');
507
- const dlqBadgeEl = document.getElementById('dlqBadge');
508
- if (dlqBadgeEl) { dlqBadgeEl.textContent = data.length; dlqBadgeEl.classList.toggle('hidden', !data.length); }
509
- if (!box) return;
510
- box.innerHTML = data.length ? data.map(entry => {
511
- const key = entry.key || (entry.filename || '').replace('.json', '') || '?';
512
- const keyAttr = escHtml(key);
513
- return '<div class="msg dlq-item"><div class="meta"><strong>⚠️ Failed</strong> | ' + (entry.agent || '?') + ' | ' + (entry.failedAt ? new Date(entry.failedAt).toLocaleString() : '') + ' <button class="replay-btn" data-action="replayDLQ" data-arg="' + keyAttr + '">Replay</button> <button data-action="deleteDLQ" data-arg="' + keyAttr + '" style="font-size:11px;padding:3px 8px;border-radius:4px;border:1px solid var(--red-hi);background:transparent;color:var(--red-hi);cursor:pointer;">Delete</button></div><div class="t">' + (entry.error || '') + '</div></div>';
514
- }).join('') : '<div class="meta" style="padding:20px; text-align:center;">✓ DLQ empty</div>';
515
- }
516
-
517
- export async function replayDLQ(key) {
518
- if (!confirm('Replay?')) return;
519
- await postJSON('/api/dlq/replay', { key });
520
- showNotification('Replayed');
521
- loadDLQ();
522
- }
523
-
524
- export async function deleteDLQ(key) {
525
- if (!confirm('Delete this DLQ entry?')) return;
526
- try {
527
- await fetch('/api/dlq/' + encodeURIComponent(key), { method: 'DELETE' });
528
- showNotification('DLQ entry deleted');
529
- loadDLQ();
530
- } catch(e) { showNotification('Failed: ' + e.message, true); }
531
- }
532
-
533
- export function showDLQ() {
534
- _hideAllViews();
535
- document.getElementById('dlqView').classList.add('active');
536
- _setNavActive('navDLQ');
537
- loadDLQ();
538
- }