crewswarm 0.9.2 → 0.9.4

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 (228) hide show
  1. package/README.md +22 -9
  2. package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js +1 -0
  3. package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js.br +0 -0
  4. package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js +1 -0
  5. package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js.br +0 -0
  6. package/apps/dashboard/dist/assets/index-BeVllEj_.js +2 -0
  7. package/apps/dashboard/dist/assets/index-BeVllEj_.js.br +0 -0
  8. package/apps/dashboard/dist/assets/{index-CF0aJRtC.css → index-D-sRshvg.css} +1 -1
  9. package/apps/dashboard/dist/assets/index-D-sRshvg.css.br +0 -0
  10. package/apps/dashboard/dist/assets/tab-benchmarks-tab-BHjKCPm3.js.br +0 -0
  11. package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js +1 -0
  12. package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js.br +0 -0
  13. package/apps/dashboard/dist/assets/{tab-pm-loop-tab-Bfd449B4.js → tab-pm-loop-tab-DiAPTJXu.js} +1 -1
  14. package/apps/dashboard/dist/assets/tab-pm-loop-tab-DiAPTJXu.js.br +0 -0
  15. package/apps/dashboard/dist/assets/{tab-projects-tab-DhNWnlzt.js → tab-projects-tab-SFH4E--a.js} +1 -1
  16. package/apps/dashboard/dist/assets/tab-projects-tab-SFH4E--a.js.br +0 -0
  17. package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js +1 -0
  18. package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js.br +0 -0
  19. package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js +1 -0
  20. package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js.br +0 -0
  21. package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js +1 -0
  22. package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js.br +0 -0
  23. package/apps/dashboard/dist/index.html +135 -15
  24. package/apps/dashboard/dist/index.html.br +0 -0
  25. package/apps/dashboard/dist/index.html.gz +0 -0
  26. package/apps/vibe/README.md +2 -2
  27. package/apps/vibe/package.json +1 -1
  28. package/apps/vibe/server.mjs +101 -56
  29. package/crew-lead.mjs +34 -4
  30. package/lib/bridges/cli-executor.mjs +1 -1
  31. package/lib/bridges/gateway-ws.mjs +4 -0
  32. package/lib/browser/passthrough-stderr.js +1 -0
  33. package/lib/chat/project-messages.mjs +3 -5
  34. package/lib/cli-process-tracker.mjs +3 -2
  35. package/lib/contacts/identity-linker.mjs +1 -0
  36. package/lib/crew-judge/judge.mjs +19 -18
  37. package/lib/crew-lead/agent-manager.mjs +1 -1
  38. package/lib/crew-lead/background.mjs +14 -1
  39. package/lib/crew-lead/chat-handler.mjs +38 -1
  40. package/lib/crew-lead/http-server.mjs +106 -57
  41. package/lib/crew-lead/llm-caller.mjs +24 -8
  42. package/lib/crew-lead/prompts.mjs +14 -1
  43. package/lib/crew-lead/tools.mjs +3 -2
  44. package/lib/crew-lead/wave-dispatcher.mjs +19 -5
  45. package/lib/crew-lead/ws-router.mjs +219 -27
  46. package/lib/engines/crew-cli.mjs +1 -1
  47. package/lib/engines/engine-registry.mjs +14 -3
  48. package/lib/engines/rt-envelope.mjs +1 -0
  49. package/lib/engines/runners.mjs +28 -4
  50. package/lib/gemini-cli-passthrough-noise.mjs +1 -1
  51. package/lib/integrations/code-search.mjs +4 -3
  52. package/lib/memory/shared-adapter.mjs +23 -10
  53. package/lib/pipeline/manager.mjs +2 -1
  54. package/lib/runtime/config.mjs +1 -1
  55. package/lib/runtime/paths.mjs +12 -8
  56. package/lib/runtime/spending.mjs +2 -1
  57. package/package.json +42 -14
  58. package/scripts/capture-build-flow.mjs +118 -0
  59. package/scripts/coverage-report.mjs +209 -0
  60. package/scripts/coverage-summary.mjs +47 -0
  61. package/scripts/dashboard-validation.mjs +76 -0
  62. package/scripts/dashboard.mjs +1667 -551
  63. package/scripts/generate-openapi.mjs +683 -277
  64. package/scripts/live-bridge-matrix.mjs +79 -0
  65. package/scripts/live-cli-matrix.mjs +166 -0
  66. package/scripts/live-crewchat-check.mjs +42 -0
  67. package/scripts/live-engine-matrix.mjs +50 -0
  68. package/scripts/live-provider-failover-matrix.mjs +107 -0
  69. package/scripts/live-provider-matrix.mjs +228 -0
  70. package/scripts/restart-all-from-repo.sh +4 -4
  71. package/scripts/restart-service.sh +12 -9
  72. package/scripts/smoke-dispatch.mjs +4 -1
  73. package/scripts/test-blast-radius.mjs +204 -0
  74. package/scripts/test-report-summary.mjs +88 -0
  75. package/scripts/test-reporter.mjs +651 -0
  76. package/scripts/test-rerun.mjs +136 -0
  77. package/scripts/tmux-bridge +130 -0
  78. package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js +0 -1
  79. package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js.br +0 -0
  80. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js +0 -1
  81. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
  82. package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
  83. package/apps/dashboard/dist/assets/index-DnClJ1ee.js +0 -2
  84. package/apps/dashboard/dist/assets/index-DnClJ1ee.js.br +0 -0
  85. package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js +0 -1
  86. package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js.br +0 -0
  87. package/apps/dashboard/dist/assets/tab-pm-loop-tab-Bfd449B4.js.br +0 -0
  88. package/apps/dashboard/dist/assets/tab-projects-tab-DhNWnlzt.js.br +0 -0
  89. package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js +0 -1
  90. package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js.br +0 -0
  91. package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js +0 -1
  92. package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js.br +0 -0
  93. package/apps/dashboard/index.html +0 -6529
  94. package/apps/dashboard/package.json +0 -15
  95. package/apps/dashboard/src/app.js +0 -2828
  96. package/apps/dashboard/src/app.js.br +0 -0
  97. package/apps/dashboard/src/app.js.gz +0 -0
  98. package/apps/dashboard/src/chat/chat-actions.js +0 -1847
  99. package/apps/dashboard/src/chat/chat-actions.js.br +0 -0
  100. package/apps/dashboard/src/chat/unified-messages.js +0 -327
  101. package/apps/dashboard/src/chat/unified-messages.js.br +0 -0
  102. package/apps/dashboard/src/cli-process.js +0 -208
  103. package/apps/dashboard/src/cli-process.js.br +0 -0
  104. package/apps/dashboard/src/cli-process.js.gz +0 -0
  105. package/apps/dashboard/src/components/active-tasks-panel.js +0 -175
  106. package/apps/dashboard/src/components/active-tasks-panel.js.br +0 -0
  107. package/apps/dashboard/src/core/api.js +0 -18
  108. package/apps/dashboard/src/core/api.js.br +0 -0
  109. package/apps/dashboard/src/core/dom.js +0 -228
  110. package/apps/dashboard/src/core/dom.js.br +0 -0
  111. package/apps/dashboard/src/core/state.js +0 -91
  112. package/apps/dashboard/src/core/state.js.br +0 -0
  113. package/apps/dashboard/src/core/task-manager.js +0 -134
  114. package/apps/dashboard/src/core/task-manager.js.br +0 -0
  115. package/apps/dashboard/src/orchestration-status.js +0 -127
  116. package/apps/dashboard/src/orchestration-status.js.br +0 -0
  117. package/apps/dashboard/src/setup-wizard.js +0 -562
  118. package/apps/dashboard/src/setup-wizard.js.br +0 -0
  119. package/apps/dashboard/src/styles.css +0 -2085
  120. package/apps/dashboard/src/styles.css.br +0 -0
  121. package/apps/dashboard/src/styles.css.gz +0 -0
  122. package/apps/dashboard/src/tabs/agents-tab.js +0 -2237
  123. package/apps/dashboard/src/tabs/agents-tab.js.br +0 -0
  124. package/apps/dashboard/src/tabs/benchmarks-tab.js +0 -229
  125. package/apps/dashboard/src/tabs/benchmarks-tab.js.br +0 -0
  126. package/apps/dashboard/src/tabs/comms-tab.js +0 -955
  127. package/apps/dashboard/src/tabs/comms-tab.js.br +0 -0
  128. package/apps/dashboard/src/tabs/contacts-tab.js +0 -654
  129. package/apps/dashboard/src/tabs/contacts-tab.js.br +0 -0
  130. package/apps/dashboard/src/tabs/engines-tab.js +0 -175
  131. package/apps/dashboard/src/tabs/engines-tab.js.br +0 -0
  132. package/apps/dashboard/src/tabs/memory-tab.js +0 -182
  133. package/apps/dashboard/src/tabs/memory-tab.js.br +0 -0
  134. package/apps/dashboard/src/tabs/models-tab.js +0 -450
  135. package/apps/dashboard/src/tabs/models-tab.js.br +0 -0
  136. package/apps/dashboard/src/tabs/pm-loop-tab.js +0 -185
  137. package/apps/dashboard/src/tabs/pm-loop-tab.js.br +0 -0
  138. package/apps/dashboard/src/tabs/projects-tab.js +0 -663
  139. package/apps/dashboard/src/tabs/projects-tab.js.br +0 -0
  140. package/apps/dashboard/src/tabs/projects-tab.js.gz +0 -0
  141. package/apps/dashboard/src/tabs/prompts-tab.js +0 -160
  142. package/apps/dashboard/src/tabs/prompts-tab.js.br +0 -0
  143. package/apps/dashboard/src/tabs/services-tab.js +0 -202
  144. package/apps/dashboard/src/tabs/services-tab.js.br +0 -0
  145. package/apps/dashboard/src/tabs/settings-tab.js +0 -861
  146. package/apps/dashboard/src/tabs/settings-tab.js.br +0 -0
  147. package/apps/dashboard/src/tabs/skills-tab.js +0 -284
  148. package/apps/dashboard/src/tabs/skills-tab.js.br +0 -0
  149. package/apps/dashboard/src/tabs/spending-tab.js +0 -173
  150. package/apps/dashboard/src/tabs/spending-tab.js.br +0 -0
  151. package/apps/dashboard/src/tabs/swarm-chat-tab.js +0 -660
  152. package/apps/dashboard/src/tabs/swarm-chat-tab.js.br +0 -0
  153. package/apps/dashboard/src/tabs/swarm-tab.js +0 -538
  154. package/apps/dashboard/src/tabs/swarm-tab.js.br +0 -0
  155. package/apps/dashboard/src/tabs/usage-tab.js +0 -390
  156. package/apps/dashboard/src/tabs/usage-tab.js.br +0 -0
  157. package/apps/dashboard/src/tabs/waves-tab.js +0 -238
  158. package/apps/dashboard/src/tabs/waves-tab.js.br +0 -0
  159. package/apps/dashboard/src/tabs/workflows-tab.js +0 -747
  160. package/apps/dashboard/src/tabs/workflows-tab.js.br +0 -0
  161. package/apps/vibe/.crew/agent-memory/pipeline.json +0 -304
  162. package/apps/vibe/.crew/cost.json +0 -17
  163. package/apps/vibe/.crew/json-parse-metrics.jsonl +0 -27
  164. package/apps/vibe/.crew/pipeline-metrics.jsonl +0 -27
  165. package/apps/vibe/.crew/pipeline-runs/pipeline-0f90c392-2425-4ae5-850c-bd9d17b1d690.jsonl +0 -5
  166. package/apps/vibe/.crew/pipeline-runs/pipeline-1c269dd9-a63f-4fba-af81-5cf08048ef06.jsonl +0 -5
  167. package/apps/vibe/.crew/pipeline-runs/pipeline-288a7765-da24-4a22-89bc-1f3cc9b0562c.jsonl +0 -5
  168. package/apps/vibe/.crew/pipeline-runs/pipeline-2c78fd22-a657-4bd1-bc49-0679fb384409.jsonl +0 -5
  169. package/apps/vibe/.crew/pipeline-runs/pipeline-3da23550-22ed-4904-9a0a-8e79c1f3024c.jsonl +0 -5
  170. package/apps/vibe/.crew/pipeline-runs/pipeline-3e6fe08d-3264-404a-8df3-aab7efef10e7.jsonl +0 -5
  171. package/apps/vibe/.crew/pipeline-runs/pipeline-42eec610-57fe-4e09-9e7e-b315038495c2.jsonl +0 -5
  172. package/apps/vibe/.crew/pipeline-runs/pipeline-4438eb4c-ae13-42b1-90e2-b043d8983be8.jsonl +0 -5
  173. package/apps/vibe/.crew/pipeline-runs/pipeline-4740a9f5-86e7-44b6-a394-de433e291727.jsonl +0 -5
  174. package/apps/vibe/.crew/pipeline-runs/pipeline-49e1da6a-957e-48fd-9220-415019e4f8e2.jsonl +0 -5
  175. package/apps/vibe/.crew/pipeline-runs/pipeline-4c9251db-be68-427b-a3fc-a264f2b5778d.jsonl +0 -5
  176. package/apps/vibe/.crew/pipeline-runs/pipeline-6413fa33-a802-4b57-a8c0-a9056ad67842.jsonl +0 -5
  177. package/apps/vibe/.crew/pipeline-runs/pipeline-65e29a57-664d-4196-8109-017e364f182e.jsonl +0 -5
  178. package/apps/vibe/.crew/pipeline-runs/pipeline-6aa04bc5-9593-4b1f-b58d-3bf2978cb602.jsonl +0 -5
  179. package/apps/vibe/.crew/pipeline-runs/pipeline-6e1cba53-9b70-457e-99e0-59199149dd21.jsonl +0 -5
  180. package/apps/vibe/.crew/pipeline-runs/pipeline-749f41cc-4dac-4204-be64-873a6080a0d2.jsonl +0 -5
  181. package/apps/vibe/.crew/pipeline-runs/pipeline-74d68121-e181-4864-bd9a-c3211341dfaf.jsonl +0 -5
  182. package/apps/vibe/.crew/pipeline-runs/pipeline-8509bc24-142d-4e07-b44a-a50bf99d1103.jsonl +0 -5
  183. package/apps/vibe/.crew/pipeline-runs/pipeline-960339c6-07ca-43ce-9900-f6e1702b39b9.jsonl +0 -5
  184. package/apps/vibe/.crew/pipeline-runs/pipeline-9bef2dd2-6122-42e5-b3d9-19f4d80f9e40.jsonl +0 -5
  185. package/apps/vibe/.crew/pipeline-runs/pipeline-9c6480a9-7031-4146-b241-825b9a2d1de1.jsonl +0 -5
  186. package/apps/vibe/.crew/pipeline-runs/pipeline-9fd42426-8492-4157-9d5f-e1537c060489.jsonl +0 -2
  187. package/apps/vibe/.crew/pipeline-runs/pipeline-ad6d40a3-2f5e-46a9-a345-47caaccc51aa.jsonl +0 -5
  188. package/apps/vibe/.crew/pipeline-runs/pipeline-bc606133-8d5b-4535-8d85-f1a29cdaa981.jsonl +0 -5
  189. package/apps/vibe/.crew/pipeline-runs/pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2.jsonl +0 -5
  190. package/apps/vibe/.crew/pipeline-runs/pipeline-c1a13ccd-634a-4d01-a4a7-1177b8a752ff.jsonl +0 -5
  191. package/apps/vibe/.crew/pipeline-runs/pipeline-c7d27b42-249e-4bd4-8f26-6aa998110b8a.jsonl +0 -5
  192. package/apps/vibe/.crew/pipeline-runs/pipeline-cca2e9b9-4a34-4d25-a311-5c793fa7e91e.jsonl +0 -5
  193. package/apps/vibe/.crew/sandbox.json +0 -7
  194. package/apps/vibe/.crew/session.json +0 -330
  195. package/apps/vibe/.crew/training-data.jsonl +0 -0
  196. package/apps/vibe/.github/workflows/studio-quality.yml +0 -37
  197. package/apps/vibe/.studio-data/project-messages/chuck-norris.jsonl +0 -18
  198. package/apps/vibe/.studio-data/project-messages/general.jsonl +0 -81
  199. package/apps/vibe/.studio-data/project-messages/studio-local.jsonl +0 -18
  200. package/apps/vibe/ARCHITECTURE.md +0 -3393
  201. package/apps/vibe/QUICK-REFERENCE.md +0 -211
  202. package/apps/vibe/ROADMAP.md +0 -41
  203. package/apps/vibe/STUDIO-SETUP-COMPLETE.md +0 -35
  204. package/apps/vibe/VISUAL-GUIDE.md +0 -378
  205. package/apps/vibe/capture-demo.mjs +0 -160
  206. package/apps/vibe/capture-full-demo.mjs +0 -255
  207. package/apps/vibe/capture-quickstart.mjs +0 -256
  208. package/apps/vibe/capture-vibe-assets.mjs +0 -71
  209. package/apps/vibe/capture-vibe-video.mjs +0 -260
  210. package/apps/vibe/check-buttons.js +0 -41
  211. package/apps/vibe/diagnose.html +0 -106
  212. package/apps/vibe/fix-buttons.js +0 -103
  213. package/apps/vibe/index.html +0 -3404
  214. package/apps/vibe/package-lock.json +0 -920
  215. package/apps/vibe/scripts/studio-pty-host.py +0 -117
  216. package/apps/vibe/src/main.js +0 -2940
  217. package/apps/vibe/src/register-all-languages.js +0 -98
  218. package/apps/vibe/start-studio.sh +0 -11
  219. package/apps/vibe/test/accessibility-tests.js +0 -77
  220. package/apps/vibe/test/browser-performance-audit.mjs +0 -205
  221. package/apps/vibe/test/performance-tests.js +0 -120
  222. package/apps/vibe/test/security-tests.js +0 -213
  223. package/apps/vibe/tests/e2e.local.mjs +0 -54
  224. package/apps/vibe/tests/server.smoke.mjs +0 -106
  225. package/apps/vibe/update_website.mjs +0 -74
  226. package/apps/vibe/vite.config.js +0 -19
  227. package/lib/crew-lead/chat-handler.mjs.bak +0 -1274
  228. package/lib/engines/rt-envelope.mjs.backup-current +0 -870
@@ -1,660 +0,0 @@
1
- import { getJSON, postJSON } from "../core/api.js";
2
- import { escHtml, showNotification, showEmpty, showError } from "../core/dom.js";
3
- import { state, persistState } from "../core/state.js";
4
-
5
- let hideAllViews = () => {};
6
- let setNavActive = () => {};
7
- let currentSwarmMessages = [];
8
- let swarmBindingsReady = false;
9
- let swarmMentionAgents = [];
10
- let lastSwarmMentionLoadAt = 0;
11
- let lastSwarmUserContent = null;
12
- let lastSwarmAssistantContent = null;
13
- let pendingSwarmResponder = null;
14
-
15
- function getPendingSwarmResponder(message = "") {
16
- const mentions = Array.from(String(message || "").matchAll(/@([a-zA-Z0-9_-]+)/g))
17
- .map((match) => match[1]);
18
- if (mentions.length !== 1) return null;
19
- const mentionId = mentions[0];
20
- const agent =
21
- swarmMentionAgents.find((entry) => entry.id === mentionId) ||
22
- swarmMentionAgents.find((entry) => entry.id === `crew-${mentionId}`);
23
- if (!agent) {
24
- return {
25
- id: mentionId,
26
- name: mentionId,
27
- emoji: "🤖",
28
- };
29
- }
30
- return {
31
- id: agent.id,
32
- name: agent.name || agent.displayName || agent.id,
33
- emoji: agent.emoji || "🤖",
34
- };
35
- }
36
-
37
- function appendSwarmMessage(message) {
38
- const box = document.getElementById("swarmChatMessages");
39
- if (!box) return;
40
- if (
41
- box.firstElementChild &&
42
- box.firstElementChild.textContent?.includes("No shared channel messages yet.")
43
- ) {
44
- box.innerHTML = "";
45
- }
46
- currentSwarmMessages.push(message);
47
- box.insertAdjacentHTML("beforeend", renderSwarmMessage(message));
48
- box.scrollTop = box.scrollHeight;
49
- }
50
-
51
- function removeSwarmTyping() {
52
- document.getElementById("swarm-typing-wrapper")?.remove();
53
- }
54
-
55
- function showSwarmTyping(responder = null) {
56
- const box = document.getElementById("swarmChatMessages");
57
- if (!box || document.getElementById("swarm-typing-wrapper")) return;
58
- const speaker =
59
- responder ||
60
- pendingSwarmResponder ||
61
- window._crewLeadInfo || { emoji: "🧠", name: "crew-lead" };
62
- const wrapper = document.createElement("div");
63
- wrapper.id = "swarm-typing-wrapper";
64
- wrapper.style.cssText =
65
- "display:flex;flex-direction:column;align-items:flex-start;gap:4px;margin-bottom:10px;";
66
- wrapper.innerHTML = `
67
- <div style="font-size:11px;color:var(--text-3);padding:0 6px;">${escHtml(speaker.emoji)} ${escHtml(speaker.name)}</div>
68
- <div style="max-width:84%;padding:10px 14px;border-radius:14px 14px 14px 4px;background:var(--surface-2);color:var(--text-2);font-size:14px;line-height:1.5;border:1px solid var(--border);font-style:italic;opacity:0.8;">thinking...</div>
69
- `;
70
- box.appendChild(wrapper);
71
- box.scrollTop = box.scrollHeight;
72
- }
73
-
74
- const SOURCE_META = {
75
- dashboard: { emoji: "🧠", label: "crew-lead" },
76
- agent: { emoji: "🤖", label: "agent" },
77
- "sub-agent": { emoji: "👷", label: "sub-agent" },
78
- cli: { emoji: "⚡", label: "cli" },
79
- discord: { emoji: "🎮", label: "discord" },
80
- system: { emoji: "🛰", label: "system" },
81
- };
82
-
83
- export function initSwarmChatTab(deps = {}) {
84
- hideAllViews = deps.hideAllViews || hideAllViews;
85
- setNavActive = deps.setNavActive || setNavActive;
86
- ensureSwarmBindings();
87
- }
88
-
89
- function ensureSwarmBindings() {
90
- if (swarmBindingsReady) return;
91
- const projectSelect = document.getElementById("swarmChatProject");
92
- const refreshBtn = document.getElementById("swarmChatRefresh");
93
- const autonomyBtn = document.getElementById("swarmAutonomyBtn");
94
- const form = document.getElementById("swarmChatForm");
95
- const input = document.getElementById("swarmChatInput");
96
- const sendBtn = document.getElementById("swarmChatSend");
97
- const messagesBox = document.getElementById("swarmChatMessages");
98
- if (!projectSelect || !refreshBtn || !form || !input || !sendBtn || !messagesBox) return;
99
-
100
- swarmBindingsReady = true;
101
-
102
- projectSelect.addEventListener("change", async () => {
103
- state.swarmChatProjectId = projectSelect.value || "general";
104
- persistState();
105
- await loadSwarmHistory();
106
- });
107
-
108
- refreshBtn.addEventListener("click", () => {
109
- loadSwarmProjects().then(loadSwarmHistory).catch((error) => {
110
- showNotification(`Failed to refresh Swarm: ${error.message}`, "error");
111
- });
112
- });
113
- autonomyBtn?.addEventListener("click", () => {
114
- toggleSwarmAutonomy().catch((error) => {
115
- showNotification(`Failed to toggle autonomy: ${error.message}`, "error");
116
- });
117
- });
118
-
119
- form.addEventListener("submit", (event) => {
120
- event.preventDefault();
121
- event.stopPropagation();
122
- sendSwarmMessage();
123
- });
124
- sendBtn.addEventListener("click", (event) => {
125
- event.preventDefault();
126
- event.stopPropagation();
127
- sendSwarmMessage();
128
- });
129
- input.addEventListener("keydown", (event) => {
130
- const menu = document.getElementById("swarmMentionMenu");
131
- if (
132
- menu &&
133
- menu.style.display === "block" &&
134
- (event.key === "Enter" || event.key === "Tab")
135
- ) {
136
- const first = menu.firstElementChild;
137
- if (first) {
138
- event.preventDefault();
139
- first.click();
140
- return;
141
- }
142
- }
143
- if (event.key === "Enter" && !event.shiftKey) {
144
- event.preventDefault();
145
- sendSwarmMessage();
146
- }
147
- });
148
- input.addEventListener("input", () => {
149
- renderSwarmMentionAutocomplete().catch(() => {});
150
- });
151
- messagesBox.addEventListener("click", (event) => {
152
- const row = event.target.closest("[data-swarm-message-id]");
153
- if (!row) return;
154
- renderTracePanel(row.dataset.swarmMessageId);
155
- });
156
- }
157
-
158
- async function loadSwarmMentionAgents(force = false) {
159
- const now = Date.now();
160
- if (!force && swarmMentionAgents.length && now - lastSwarmMentionLoadAt < 30_000) {
161
- return swarmMentionAgents;
162
- }
163
- const data = await getJSON("/api/chat-participants");
164
- const participants = (data.participants || [])
165
- .filter((participant) => participant.id)
166
- .sort((a, b) => a.id.localeCompare(b.id))
167
- .map((participant) => ({
168
- id: participant.id,
169
- name:
170
- participant.kind === "cli"
171
- ? `${participant.runtime} runtime`
172
- : participant.kind,
173
- role: participant.kind,
174
- kind: participant.kind,
175
- }));
176
- swarmMentionAgents = participants;
177
- lastSwarmMentionLoadAt = now;
178
- return participants;
179
- }
180
-
181
- async function renderSwarmMentionAutocomplete() {
182
- const input = document.getElementById("swarmChatInput");
183
- const menu = document.getElementById("swarmMentionMenu");
184
- const hint = document.getElementById("swarmMentionHint");
185
- if (!input || !menu || !hint) return;
186
-
187
- const value = input.value || "";
188
- const caret = input.selectionStart || 0;
189
- const before = value.slice(0, caret);
190
- const match = before.match(/(^|\s)@([a-zA-Z0-9_-]*)$/);
191
- if (!match) {
192
- menu.style.display = "none";
193
- hint.style.display = "none";
194
- return;
195
- }
196
-
197
- const prefix = (match[2] || "").toLowerCase();
198
- const agents = await loadSwarmMentionAgents();
199
- const filtered = agents.filter((agent) => agent.id.toLowerCase().includes(prefix)).slice(0, 8);
200
- if (!filtered.length) {
201
- menu.style.display = "none";
202
- hint.style.display = "none";
203
- return;
204
- }
205
-
206
- menu.style.display = "block";
207
- menu.innerHTML = "";
208
- filtered.forEach((agent) => {
209
- const row = document.createElement("div");
210
- row.style.cssText =
211
- "padding:8px 12px;cursor:pointer;font-size:13px;border-bottom:1px solid var(--border);";
212
- row.onmouseenter = () => {
213
- row.style.background = "var(--bg-hover)";
214
- };
215
- row.onmouseleave = () => {
216
- row.style.background = "";
217
- };
218
- row.innerHTML = `<span style="color:var(--accent);font-weight:600;">@${agent.id}</span> <span style="color:var(--text-3);">${agent.name || agent.role || "agent"}</span>`;
219
- row.onclick = () => {
220
- const tokenStart = caret - match[0].length + match[1].length;
221
- const insert = `@${agent.id} `;
222
- input.value = value.slice(0, tokenStart) + insert + value.slice(caret);
223
- const nextCaret = tokenStart + insert.length;
224
- input.selectionStart = input.selectionEnd = nextCaret;
225
- input.focus();
226
- menu.style.display = "none";
227
- hint.style.display = "block";
228
- hint.textContent =
229
- agent.id === "crew-lead"
230
- ? "Mention target: @crew-lead. Use this for notes or routing guidance."
231
- : `Mention target: @${agent.id}. Use a specific work order if you want execution.`;
232
- };
233
- menu.appendChild(row);
234
- });
235
-
236
- hint.style.display = "block";
237
- hint.textContent = prefix
238
- ? `Matching participants for @${prefix}`
239
- : "Type a participant, e.g. @crew-lead for notes or @crew-coder with a specific work order.";
240
- }
241
-
242
- export async function showSwarmChat() {
243
- ensureSwarmBindings();
244
- hideAllViews();
245
- document.getElementById("swarmChatView")?.classList.add("active");
246
- setNavActive("navSwarmChat");
247
- state.activeTab = "swarm-chat";
248
- if (!state.swarmChatProjectId) {
249
- state.swarmChatProjectId = state.chatActiveProjectId || "general";
250
- }
251
- persistState();
252
- await loadSwarmProjects();
253
- await loadSwarmAutonomy();
254
- await loadSwarmHistory();
255
- // No polling needed - SSE handles real-time updates
256
- // Tab loads fresh history on switch (event-driven)
257
- document.getElementById("swarmChatInput")?.focus();
258
- }
259
-
260
- async function loadSwarmAutonomy() {
261
- const btn = document.getElementById("swarmAutonomyBtn");
262
- const status = document.getElementById("swarmAutonomyStatus");
263
- const input = document.getElementById("swarmChatInput");
264
- const data = await getJSON("/api/settings/autonomous-mentions");
265
- const enabled = data.enabled !== false;
266
- if (btn) {
267
- btn.textContent = enabled ? "🕸 Auto ON" : "⚫ Auto OFF";
268
- btn.style.background = enabled ? "rgba(52,211,153,0.15)" : "";
269
- btn.style.borderColor = enabled ? "rgba(52,211,153,0.3)" : "";
270
- btn.style.color = enabled ? "var(--green)" : "";
271
- }
272
- if (status) {
273
- status.textContent = enabled
274
- ? "Autonomous routing is live in this room. @mentions can dispatch agents or run CLI participants."
275
- : "Autonomous routing is off. @mentions stay visible in chat, but nothing auto-runs.";
276
- }
277
- if (input) {
278
- input.placeholder = enabled
279
- ? "Talk in-channel. Use @crew-* or @codex/@cursor/@claude/@opencode/@gemini/@crew-cli to route work."
280
- : "Autonomy is off. @mentions are informational until you turn routing back on.";
281
- }
282
- }
283
-
284
- async function toggleSwarmAutonomy() {
285
- const current = await getJSON("/api/settings/autonomous-mentions");
286
- const next = await postJSON("/api/settings/autonomous-mentions", {
287
- enabled: !current.enabled,
288
- });
289
- showNotification(
290
- "Swarm autonomy " + (next.enabled ? "ENABLED 🕸" : "DISABLED"),
291
- );
292
- await loadSwarmAutonomy();
293
- }
294
-
295
- // Removed polling - swarm chat now purely event-driven via SSE
296
- // History loads when switching to tab, real-time updates via SSE connection
297
-
298
- async function loadSwarmProjects() {
299
- const select = document.getElementById("swarmChatProject");
300
- if (!select) return;
301
-
302
- const activeProjectId = state.swarmChatProjectId || state.chatActiveProjectId || "general";
303
- const projectOptions = [{ id: "general", name: "General" }];
304
-
305
- try {
306
- const response = await getJSON("/api/projects");
307
- for (const project of response.projects || []) {
308
- projectOptions.push({ id: project.id, name: project.name || project.id });
309
- }
310
- } catch {}
311
-
312
- select.innerHTML = projectOptions
313
- .map((project) => {
314
- const selected = project.id === activeProjectId ? " selected" : "";
315
- return `<option value="${escHtml(project.id)}"${selected}>${escHtml(project.name)}</option>`;
316
- })
317
- .join("");
318
- state.swarmChatProjectId = select.value || "general";
319
- persistState();
320
-
321
- const hint = document.getElementById("swarmChatHint");
322
- if (hint) {
323
- const activeProject = projectOptions.find((project) => project.id === state.swarmChatProjectId);
324
- hint.textContent =
325
- state.swarmChatProjectId === "general"
326
- ? "Shared global room. Same unified history store as Chat."
327
- : `Project channel for ${activeProject?.name || state.swarmChatProjectId}. Same history store as Chat.`;
328
- }
329
- }
330
-
331
- async function loadSwarmHistory() {
332
- const box = document.getElementById("swarmChatMessages");
333
- if (!box) return;
334
-
335
- const projectId = state.swarmChatProjectId || "general";
336
- box.innerHTML = "";
337
-
338
- try {
339
- const response = await getJSON(
340
- `/api/crew-lead/project-messages?projectId=${encodeURIComponent(projectId)}&limit=300&excludeDirect=true`,
341
- );
342
- const messages = response.messages || [];
343
- currentSwarmMessages = messages;
344
- if (!messages.length) {
345
- hideTracePanel();
346
- showEmpty(box, "No shared channel messages yet.");
347
- return;
348
- }
349
- box.innerHTML = messages.map(renderSwarmMessage).join("");
350
- box.scrollTop = box.scrollHeight;
351
- } catch (error) {
352
- showError(box, `Failed to load shared channel: ${error.message}`);
353
- }
354
- }
355
-
356
- function renderSwarmMessage(message) {
357
- const meta = SOURCE_META[message.source] || { emoji: "📝", label: message.source || "message" };
358
- const isUser = message.role === "user";
359
- const whoEmoji =
360
- message.metadata?.agentEmoji ||
361
- (isUser ? "👤" : meta.emoji);
362
- const who =
363
- message.metadata?.agentName ||
364
- message.agent ||
365
- (isUser ? "You" : meta.label);
366
- const timestamp = new Date(message.ts || Date.now()).toLocaleTimeString();
367
- const chips = [];
368
-
369
- if (message.metadata?.channel) chips.push(`#${message.metadata.channel}`);
370
- if (message.metadata?.directChat) chips.push("direct");
371
- if (message.metadata?.engine) chips.push(message.metadata.engine);
372
- if (Array.isArray(message.metadata?.mentions)) {
373
- message.metadata.mentions.forEach((mention) => chips.push(`@${mention}`));
374
- }
375
- if (message.metadata?.triggeredBy === "mention" || message.metadata?.autonomous) {
376
- chips.push("@mention");
377
- }
378
- if (message.parentId || message.metadata?.originMessageId) chips.push("linked");
379
- if (message.threadId || message.metadata?.originThreadId) chips.push("thread");
380
-
381
- return `
382
- <div data-swarm-message-id="${escHtml(message.id || "")}" style="display:flex;flex-direction:column;align-items:${isUser ? "flex-end" : "flex-start"};gap:4px;margin-bottom:10px;cursor:pointer;">
383
- <div style="font-size:11px;color:var(--text-3);padding:0 6px;display:flex;gap:6px;align-items:center;flex-wrap:wrap;">
384
- <span>${escHtml(whoEmoji)} ${escHtml(who)}</span>
385
- <span style="opacity:0.7;">${escHtml(timestamp)}</span>
386
- ${chips.map((chip) => `<span style="padding:1px 6px;border-radius:999px;background:var(--bg-card2);border:1px solid var(--border);">${escHtml(chip)}</span>`).join("")}
387
- </div>
388
- <div style="max-width:84%;padding:10px 14px;border-radius:${isUser ? "14px 14px 4px 14px" : "14px 14px 14px 4px"};background:${isUser ? "var(--purple)" : "var(--surface-2)"};color:${isUser ? "#fff" : "var(--text-2)"};font-size:14px;line-height:1.5;white-space:pre-wrap;word-break:break-word;border:1px solid var(--border);">${escHtml(message.content || "")}</div>
389
- </div>
390
- `;
391
- }
392
-
393
- export function handleSwarmSSEEvent(event) {
394
- const box = document.getElementById("swarmChatMessages");
395
- if (!box) return false;
396
-
397
- const activeProjectId = state.swarmChatProjectId || "general";
398
- const expectedSessionId = `swarm-${activeProjectId}`;
399
- const normalizeProjectId = (value) =>
400
- !value || value === "general" ? "general" : value;
401
-
402
- if (
403
- normalizeProjectId(event.projectId) !== normalizeProjectId(activeProjectId) ||
404
- event.sessionId !== expectedSessionId
405
- ) {
406
- return false;
407
- }
408
-
409
- if (event.type === "chat_stream") {
410
- removeSwarmTyping();
411
- let streamBubble = document.getElementById("swarm-streaming-bubble");
412
- if (!streamBubble) {
413
- const wrapper = document.createElement("div");
414
- wrapper.id = "swarm-streaming-wrapper";
415
- wrapper.style.cssText =
416
- "display:flex;flex-direction:column;align-items:flex-start;gap:4px;margin-bottom:10px;";
417
-
418
- const label = document.createElement("div");
419
- label.style.cssText =
420
- "font-size:11px;color:var(--text-3);padding:0 6px;display:flex;gap:6px;align-items:center;flex-wrap:wrap;";
421
- const speaker =
422
- (event.agentName || event.agentEmoji)
423
- ? {
424
- name: event.agentName || event.agent || "agent",
425
- emoji: event.agentEmoji || "🤖",
426
- }
427
- : pendingSwarmResponder || window._crewLeadInfo || { emoji: "🧠", name: "crew-lead" };
428
- label.textContent = `${speaker.emoji} ${speaker.name}`;
429
-
430
- streamBubble = document.createElement("div");
431
- streamBubble.id = "swarm-streaming-bubble";
432
- streamBubble.style.cssText =
433
- "max-width:84%;padding:10px 14px;border-radius:14px 14px 14px 4px;background:var(--surface-2);color:var(--text-2);font-size:14px;line-height:1.5;white-space:pre-wrap;word-break:break-word;border:1px solid var(--border);";
434
- streamBubble._textNode = document.createTextNode("");
435
- streamBubble.appendChild(streamBubble._textNode);
436
-
437
- wrapper.appendChild(label);
438
- wrapper.appendChild(streamBubble);
439
- box.appendChild(wrapper);
440
- }
441
-
442
- streamBubble._textNode.textContent += event.token || "";
443
- box.scrollTop = box.scrollHeight;
444
- return true;
445
- }
446
-
447
- if (event.type !== "chat_message") return false;
448
-
449
- const streamWrapper = document.getElementById("swarm-streaming-wrapper");
450
- if (streamWrapper && event.role === "assistant") streamWrapper.remove();
451
- if (event.role === "assistant") {
452
- removeSwarmTyping();
453
- pendingSwarmResponder = null;
454
- }
455
-
456
- if (event.role === "user") {
457
- if (event.content === lastSwarmUserContent) return true;
458
- lastSwarmUserContent = event.content;
459
- } else if (event.role === "assistant") {
460
- if (event.content === lastSwarmAssistantContent) return true;
461
- lastSwarmAssistantContent = event.content;
462
- }
463
-
464
- const assistantAgent =
465
- event.role === "assistant"
466
- ? event.agent ||
467
- (event.source === "agent" || event.source === "cli" ? null : "crew-lead")
468
- : null;
469
-
470
- const message = {
471
- id: `live-${event.role}-${Date.now()}`,
472
- ts: Date.now(),
473
- source: event.source || "dashboard",
474
- role: event.role,
475
- content: event.content || "",
476
- agent: assistantAgent,
477
- metadata:
478
- event.role === "assistant"
479
- ? {
480
- agentName:
481
- event.agentName ||
482
- (assistantAgent === "crew-lead"
483
- ? (window._crewLeadInfo || {}).name || "crew-lead"
484
- : assistantAgent || "agent"),
485
- agentEmoji:
486
- event.agentEmoji ||
487
- (assistantAgent === "crew-lead"
488
- ? (window._crewLeadInfo || {}).emoji || "🧠"
489
- : "🤖"),
490
- model: event.model || null,
491
- ...(event.directChat ? { directChat: true } : {}),
492
- ...(event.multiDirect ? { multiDirect: true } : {}),
493
- }
494
- : {
495
- agentName: "You",
496
- agentEmoji: "👤",
497
- },
498
- };
499
-
500
- appendSwarmMessage(message);
501
- return true;
502
- }
503
-
504
- function renderTracePanel(messageId) {
505
- const panel = document.getElementById("swarmTracePanel");
506
- if (!panel) return;
507
- const message = currentSwarmMessages.find((entry) => entry.id === messageId);
508
- if (!message) return;
509
- const metadata = message.metadata || {};
510
- const rows = [
511
- ["messageId", message.id],
512
- ["source", message.source],
513
- ["agent", message.agent || null],
514
- ["taskId", metadata.taskId || null],
515
- ["threadId", message.threadId || metadata.originThreadId || null],
516
- ["parentId", message.parentId || metadata.originMessageId || null],
517
- ["originProjectId", metadata.originProjectId || null],
518
- ["originChannel", metadata.originChannel || metadata.channel || null],
519
- ["triggeredBy", metadata.triggeredBy || null],
520
- ["mentionedBy", metadata.mentionedBy || null],
521
- ].filter(([, value]) => value);
522
-
523
- panel.style.display = "block";
524
- panel.innerHTML = `
525
- <div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:8px;">
526
- <strong style="color:var(--text-1);">Trace</strong>
527
- <button type="button" id="swarmTraceClose" class="btn-ghost" style="font-size:11px;padding:4px 8px;">Close</button>
528
- </div>
529
- <div style="display:grid;grid-template-columns:140px 1fr;gap:6px 10px;">
530
- ${rows
531
- .map(
532
- ([label, value]) =>
533
- `<div style="color:var(--text-3);">${escHtml(label)}</div><div style="font-family:monospace;word-break:break-all;">${escHtml(String(value))}</div>`,
534
- )
535
- .join("")}
536
- </div>
537
- `;
538
- panel.querySelector("#swarmTraceClose")?.addEventListener("click", hideTracePanel, { once: true });
539
- }
540
-
541
- function hideTracePanel() {
542
- const panel = document.getElementById("swarmTracePanel");
543
- if (!panel) return;
544
- panel.style.display = "none";
545
- panel.innerHTML = "";
546
- }
547
-
548
- async function sendSwarmMessage() {
549
- const input = document.getElementById("swarmChatInput");
550
- const sendBtn = document.getElementById("swarmChatSend");
551
- if (!input || !sendBtn) return;
552
-
553
- const message = input.value.trim();
554
- if (!message) return;
555
-
556
- const projectId = state.swarmChatProjectId || "general";
557
- sendBtn.disabled = true;
558
- input.value = "";
559
- pendingSwarmResponder = getPendingSwarmResponder(message);
560
-
561
- appendSwarmMessage({
562
- id: `local-user-${Date.now()}`,
563
- ts: Date.now(),
564
- source: "dashboard",
565
- role: "user",
566
- content: message,
567
- agent: null,
568
- metadata: {
569
- agentName: "You",
570
- agentEmoji: "👤",
571
- },
572
- });
573
- lastSwarmUserContent = message;
574
- showSwarmTyping(pendingSwarmResponder);
575
-
576
- try {
577
- const response = await postJSON("/api/chat/unified", {
578
- message,
579
- sessionId: `swarm-${projectId}`,
580
- projectId,
581
- channelMode: true,
582
- });
583
- if (Array.isArray(response?.replies) && response.replies.length) {
584
- removeSwarmTyping();
585
- for (const entry of response.replies) {
586
- appendSwarmMessage({
587
- id: `local-assistant-${entry.agent}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
588
- ts: Date.now(),
589
- source: "agent",
590
- role: "assistant",
591
- content: entry.reply || "",
592
- agent: entry.agent || null,
593
- metadata: {
594
- agentName: entry.agentName || entry.agent || "agent",
595
- agentEmoji: entry.agentEmoji || "🤖",
596
- directChat: true,
597
- multiDirect: true,
598
- },
599
- });
600
- }
601
- lastSwarmAssistantContent = response.replies[response.replies.length - 1]?.reply || null;
602
- pendingSwarmResponder = null;
603
- return;
604
- }
605
- if (
606
- response?.reply &&
607
- response.reply !== lastSwarmAssistantContent &&
608
- !document.getElementById("swarm-streaming-wrapper")
609
- ) {
610
- removeSwarmTyping();
611
- const replyAgent = response.agent || response.routedTo || "crew-lead";
612
- const replyAgentName =
613
- response.agentName ||
614
- pendingSwarmResponder?.name ||
615
- (replyAgent === "crew-lead"
616
- ? (window._crewLeadInfo || {}).name || "crew-lead"
617
- : replyAgent);
618
- const replyAgentEmoji =
619
- response.agentEmoji ||
620
- pendingSwarmResponder?.emoji ||
621
- (replyAgent === "crew-lead"
622
- ? (window._crewLeadInfo || {}).emoji || "🧠"
623
- : "🤖");
624
- appendSwarmMessage({
625
- id: `local-assistant-${Date.now()}`,
626
- ts: Date.now(),
627
- source: response.directChat ? "agent" : "dashboard",
628
- role: "assistant",
629
- content: response.reply,
630
- agent: replyAgent,
631
- metadata: {
632
- agentName: replyAgentName,
633
- agentEmoji: replyAgentEmoji,
634
- ...(response.directChat ? { directChat: true } : {}),
635
- },
636
- });
637
- lastSwarmAssistantContent = response.reply;
638
- pendingSwarmResponder = null;
639
- }
640
- } catch (error) {
641
- removeSwarmTyping();
642
- pendingSwarmResponder = null;
643
- showNotification(`Swarm send failed: ${error.message}`, "error");
644
- appendSwarmMessage({
645
- id: `local-error-${Date.now()}`,
646
- ts: Date.now(),
647
- source: "system",
648
- role: "assistant",
649
- content: `Failed to send: ${error.message}`,
650
- agent: "crew-lead",
651
- metadata: {
652
- agentName: "system",
653
- agentEmoji: "🛰",
654
- },
655
- });
656
- } finally {
657
- sendBtn.disabled = false;
658
- input.focus();
659
- }
660
- }