crewswarm 0.9.2 → 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 (207) 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 +34 -4
  12. package/lib/bridges/gateway-ws.mjs +4 -0
  13. package/lib/crew-lead/chat-handler.mjs +34 -0
  14. package/lib/crew-lead/http-server.mjs +55 -14
  15. package/lib/crew-lead/llm-caller.mjs +24 -8
  16. package/lib/crew-lead/prompts.mjs +7 -0
  17. package/lib/crew-lead/wave-dispatcher.mjs +15 -3
  18. package/lib/crew-lead/ws-router.mjs +219 -27
  19. package/lib/engines/engine-registry.mjs +9 -0
  20. package/lib/engines/rt-envelope.mjs +1 -0
  21. package/lib/engines/runners.mjs +5 -2
  22. package/lib/runtime/paths.mjs +12 -8
  23. package/package.json +35 -15
  24. package/scripts/capture-build-flow.mjs +118 -0
  25. package/scripts/coverage-report.mjs +209 -0
  26. package/scripts/coverage-summary.mjs +47 -0
  27. package/scripts/dashboard-validation.mjs +74 -0
  28. package/scripts/dashboard.mjs +560 -70
  29. package/scripts/live-bridge-matrix.mjs +79 -0
  30. package/scripts/live-cli-matrix.mjs +166 -0
  31. package/scripts/live-crewchat-check.mjs +42 -0
  32. package/scripts/live-engine-matrix.mjs +50 -0
  33. package/scripts/live-provider-failover-matrix.mjs +107 -0
  34. package/scripts/live-provider-matrix.mjs +228 -0
  35. package/scripts/restart-all-from-repo.sh +4 -4
  36. package/scripts/smoke-dispatch.mjs +4 -1
  37. package/scripts/test-blast-radius.mjs +204 -0
  38. package/scripts/test-report-summary.mjs +88 -0
  39. package/scripts/test-reporter.mjs +651 -0
  40. package/scripts/test-rerun.mjs +136 -0
  41. package/scripts/tmux-bridge +130 -0
  42. package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js.br +0 -0
  43. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
  44. package/apps/dashboard/dist/assets/components-BS9fQjE_.js.br +0 -0
  45. package/apps/dashboard/dist/assets/core-utils-CmOkXgzi.js.br +0 -0
  46. package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
  47. package/apps/dashboard/dist/assets/index-DnClJ1ee.js +0 -2
  48. package/apps/dashboard/dist/assets/index-DnClJ1ee.js.br +0 -0
  49. package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js.br +0 -0
  50. package/apps/dashboard/dist/assets/setup-wizard-CA0Or47w.js.br +0 -0
  51. package/apps/dashboard/dist/assets/tab-agents-tab-BgpIsjkw.js.br +0 -0
  52. package/apps/dashboard/dist/assets/tab-comms-tab-kguqTIzD.js.br +0 -0
  53. package/apps/dashboard/dist/assets/tab-contacts-tab-DiOyMYth.js.br +0 -0
  54. package/apps/dashboard/dist/assets/tab-engines-tab-BsdZVvU0.js.br +0 -0
  55. package/apps/dashboard/dist/assets/tab-memory-tab-Cu6u13EQ.js.br +0 -0
  56. package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js.br +0 -0
  57. package/apps/dashboard/dist/assets/tab-pm-loop-tab-Bfd449B4.js.br +0 -0
  58. package/apps/dashboard/dist/assets/tab-projects-tab-DhNWnlzt.js.br +0 -0
  59. package/apps/dashboard/dist/assets/tab-prompts-tab-DVkUNaJd.js.br +0 -0
  60. package/apps/dashboard/dist/assets/tab-services-tab-DU_LH3uG.js.br +0 -0
  61. package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js +0 -1
  62. package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js.br +0 -0
  63. package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js.br +0 -0
  64. package/apps/dashboard/dist/assets/tab-spending-tab-DEccQHnt.js.br +0 -0
  65. package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BNrd88-r.js.br +0 -0
  66. package/apps/dashboard/dist/assets/tab-swarm-tab-B1AcjL1W.js.br +0 -0
  67. package/apps/dashboard/dist/assets/tab-usage-tab-BIOOnB-Y.js.br +0 -0
  68. package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js.br +0 -0
  69. package/apps/dashboard/dist/assets/tab-workflows-tab-B-soSy1k.js.br +0 -0
  70. package/apps/dashboard/dist/index.html.br +0 -0
  71. package/apps/dashboard/dist/index.html.gz +0 -0
  72. package/apps/dashboard/index.html +0 -6529
  73. package/apps/dashboard/package.json +0 -15
  74. package/apps/dashboard/src/app.js +0 -2828
  75. package/apps/dashboard/src/app.js.br +0 -0
  76. package/apps/dashboard/src/app.js.gz +0 -0
  77. package/apps/dashboard/src/chat/chat-actions.js +0 -1847
  78. package/apps/dashboard/src/chat/chat-actions.js.br +0 -0
  79. package/apps/dashboard/src/chat/unified-messages.js +0 -327
  80. package/apps/dashboard/src/chat/unified-messages.js.br +0 -0
  81. package/apps/dashboard/src/cli-process.js +0 -208
  82. package/apps/dashboard/src/cli-process.js.br +0 -0
  83. package/apps/dashboard/src/cli-process.js.gz +0 -0
  84. package/apps/dashboard/src/components/active-tasks-panel.js +0 -175
  85. package/apps/dashboard/src/components/active-tasks-panel.js.br +0 -0
  86. package/apps/dashboard/src/core/api.js +0 -18
  87. package/apps/dashboard/src/core/api.js.br +0 -0
  88. package/apps/dashboard/src/core/dom.js +0 -228
  89. package/apps/dashboard/src/core/dom.js.br +0 -0
  90. package/apps/dashboard/src/core/state.js +0 -91
  91. package/apps/dashboard/src/core/state.js.br +0 -0
  92. package/apps/dashboard/src/core/task-manager.js +0 -134
  93. package/apps/dashboard/src/core/task-manager.js.br +0 -0
  94. package/apps/dashboard/src/orchestration-status.js +0 -127
  95. package/apps/dashboard/src/orchestration-status.js.br +0 -0
  96. package/apps/dashboard/src/setup-wizard.js +0 -562
  97. package/apps/dashboard/src/setup-wizard.js.br +0 -0
  98. package/apps/dashboard/src/styles.css +0 -2085
  99. package/apps/dashboard/src/styles.css.br +0 -0
  100. package/apps/dashboard/src/styles.css.gz +0 -0
  101. package/apps/dashboard/src/tabs/agents-tab.js +0 -2237
  102. package/apps/dashboard/src/tabs/agents-tab.js.br +0 -0
  103. package/apps/dashboard/src/tabs/benchmarks-tab.js +0 -229
  104. package/apps/dashboard/src/tabs/benchmarks-tab.js.br +0 -0
  105. package/apps/dashboard/src/tabs/comms-tab.js +0 -955
  106. package/apps/dashboard/src/tabs/comms-tab.js.br +0 -0
  107. package/apps/dashboard/src/tabs/contacts-tab.js +0 -654
  108. package/apps/dashboard/src/tabs/contacts-tab.js.br +0 -0
  109. package/apps/dashboard/src/tabs/engines-tab.js +0 -175
  110. package/apps/dashboard/src/tabs/engines-tab.js.br +0 -0
  111. package/apps/dashboard/src/tabs/memory-tab.js +0 -182
  112. package/apps/dashboard/src/tabs/memory-tab.js.br +0 -0
  113. package/apps/dashboard/src/tabs/models-tab.js +0 -450
  114. package/apps/dashboard/src/tabs/models-tab.js.br +0 -0
  115. package/apps/dashboard/src/tabs/pm-loop-tab.js +0 -185
  116. package/apps/dashboard/src/tabs/pm-loop-tab.js.br +0 -0
  117. package/apps/dashboard/src/tabs/projects-tab.js +0 -663
  118. package/apps/dashboard/src/tabs/projects-tab.js.br +0 -0
  119. package/apps/dashboard/src/tabs/projects-tab.js.gz +0 -0
  120. package/apps/dashboard/src/tabs/prompts-tab.js +0 -160
  121. package/apps/dashboard/src/tabs/prompts-tab.js.br +0 -0
  122. package/apps/dashboard/src/tabs/services-tab.js +0 -202
  123. package/apps/dashboard/src/tabs/services-tab.js.br +0 -0
  124. package/apps/dashboard/src/tabs/settings-tab.js +0 -861
  125. package/apps/dashboard/src/tabs/settings-tab.js.br +0 -0
  126. package/apps/dashboard/src/tabs/skills-tab.js +0 -284
  127. package/apps/dashboard/src/tabs/skills-tab.js.br +0 -0
  128. package/apps/dashboard/src/tabs/spending-tab.js +0 -173
  129. package/apps/dashboard/src/tabs/spending-tab.js.br +0 -0
  130. package/apps/dashboard/src/tabs/swarm-chat-tab.js +0 -660
  131. package/apps/dashboard/src/tabs/swarm-chat-tab.js.br +0 -0
  132. package/apps/dashboard/src/tabs/swarm-tab.js +0 -538
  133. package/apps/dashboard/src/tabs/swarm-tab.js.br +0 -0
  134. package/apps/dashboard/src/tabs/usage-tab.js +0 -390
  135. package/apps/dashboard/src/tabs/usage-tab.js.br +0 -0
  136. package/apps/dashboard/src/tabs/waves-tab.js +0 -238
  137. package/apps/dashboard/src/tabs/waves-tab.js.br +0 -0
  138. package/apps/dashboard/src/tabs/workflows-tab.js +0 -747
  139. package/apps/dashboard/src/tabs/workflows-tab.js.br +0 -0
  140. package/apps/vibe/.crew/agent-memory/pipeline.json +0 -304
  141. package/apps/vibe/.crew/cost.json +0 -17
  142. package/apps/vibe/.crew/json-parse-metrics.jsonl +0 -27
  143. package/apps/vibe/.crew/pipeline-metrics.jsonl +0 -27
  144. package/apps/vibe/.crew/pipeline-runs/pipeline-0f90c392-2425-4ae5-850c-bd9d17b1d690.jsonl +0 -5
  145. package/apps/vibe/.crew/pipeline-runs/pipeline-1c269dd9-a63f-4fba-af81-5cf08048ef06.jsonl +0 -5
  146. package/apps/vibe/.crew/pipeline-runs/pipeline-288a7765-da24-4a22-89bc-1f3cc9b0562c.jsonl +0 -5
  147. package/apps/vibe/.crew/pipeline-runs/pipeline-2c78fd22-a657-4bd1-bc49-0679fb384409.jsonl +0 -5
  148. package/apps/vibe/.crew/pipeline-runs/pipeline-3da23550-22ed-4904-9a0a-8e79c1f3024c.jsonl +0 -5
  149. package/apps/vibe/.crew/pipeline-runs/pipeline-3e6fe08d-3264-404a-8df3-aab7efef10e7.jsonl +0 -5
  150. package/apps/vibe/.crew/pipeline-runs/pipeline-42eec610-57fe-4e09-9e7e-b315038495c2.jsonl +0 -5
  151. package/apps/vibe/.crew/pipeline-runs/pipeline-4438eb4c-ae13-42b1-90e2-b043d8983be8.jsonl +0 -5
  152. package/apps/vibe/.crew/pipeline-runs/pipeline-4740a9f5-86e7-44b6-a394-de433e291727.jsonl +0 -5
  153. package/apps/vibe/.crew/pipeline-runs/pipeline-49e1da6a-957e-48fd-9220-415019e4f8e2.jsonl +0 -5
  154. package/apps/vibe/.crew/pipeline-runs/pipeline-4c9251db-be68-427b-a3fc-a264f2b5778d.jsonl +0 -5
  155. package/apps/vibe/.crew/pipeline-runs/pipeline-6413fa33-a802-4b57-a8c0-a9056ad67842.jsonl +0 -5
  156. package/apps/vibe/.crew/pipeline-runs/pipeline-65e29a57-664d-4196-8109-017e364f182e.jsonl +0 -5
  157. package/apps/vibe/.crew/pipeline-runs/pipeline-6aa04bc5-9593-4b1f-b58d-3bf2978cb602.jsonl +0 -5
  158. package/apps/vibe/.crew/pipeline-runs/pipeline-6e1cba53-9b70-457e-99e0-59199149dd21.jsonl +0 -5
  159. package/apps/vibe/.crew/pipeline-runs/pipeline-749f41cc-4dac-4204-be64-873a6080a0d2.jsonl +0 -5
  160. package/apps/vibe/.crew/pipeline-runs/pipeline-74d68121-e181-4864-bd9a-c3211341dfaf.jsonl +0 -5
  161. package/apps/vibe/.crew/pipeline-runs/pipeline-8509bc24-142d-4e07-b44a-a50bf99d1103.jsonl +0 -5
  162. package/apps/vibe/.crew/pipeline-runs/pipeline-960339c6-07ca-43ce-9900-f6e1702b39b9.jsonl +0 -5
  163. package/apps/vibe/.crew/pipeline-runs/pipeline-9bef2dd2-6122-42e5-b3d9-19f4d80f9e40.jsonl +0 -5
  164. package/apps/vibe/.crew/pipeline-runs/pipeline-9c6480a9-7031-4146-b241-825b9a2d1de1.jsonl +0 -5
  165. package/apps/vibe/.crew/pipeline-runs/pipeline-9fd42426-8492-4157-9d5f-e1537c060489.jsonl +0 -2
  166. package/apps/vibe/.crew/pipeline-runs/pipeline-ad6d40a3-2f5e-46a9-a345-47caaccc51aa.jsonl +0 -5
  167. package/apps/vibe/.crew/pipeline-runs/pipeline-bc606133-8d5b-4535-8d85-f1a29cdaa981.jsonl +0 -5
  168. package/apps/vibe/.crew/pipeline-runs/pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2.jsonl +0 -5
  169. package/apps/vibe/.crew/pipeline-runs/pipeline-c1a13ccd-634a-4d01-a4a7-1177b8a752ff.jsonl +0 -5
  170. package/apps/vibe/.crew/pipeline-runs/pipeline-c7d27b42-249e-4bd4-8f26-6aa998110b8a.jsonl +0 -5
  171. package/apps/vibe/.crew/pipeline-runs/pipeline-cca2e9b9-4a34-4d25-a311-5c793fa7e91e.jsonl +0 -5
  172. package/apps/vibe/.crew/sandbox.json +0 -7
  173. package/apps/vibe/.crew/session.json +0 -330
  174. package/apps/vibe/.crew/training-data.jsonl +0 -0
  175. package/apps/vibe/.github/workflows/studio-quality.yml +0 -37
  176. package/apps/vibe/.studio-data/project-messages/chuck-norris.jsonl +0 -18
  177. package/apps/vibe/.studio-data/project-messages/general.jsonl +0 -81
  178. package/apps/vibe/.studio-data/project-messages/studio-local.jsonl +0 -18
  179. package/apps/vibe/ARCHITECTURE.md +0 -3393
  180. package/apps/vibe/QUICK-REFERENCE.md +0 -211
  181. package/apps/vibe/ROADMAP.md +0 -41
  182. package/apps/vibe/STUDIO-SETUP-COMPLETE.md +0 -35
  183. package/apps/vibe/VISUAL-GUIDE.md +0 -378
  184. package/apps/vibe/capture-demo.mjs +0 -160
  185. package/apps/vibe/capture-full-demo.mjs +0 -255
  186. package/apps/vibe/capture-quickstart.mjs +0 -256
  187. package/apps/vibe/capture-vibe-assets.mjs +0 -71
  188. package/apps/vibe/capture-vibe-video.mjs +0 -260
  189. package/apps/vibe/check-buttons.js +0 -41
  190. package/apps/vibe/diagnose.html +0 -106
  191. package/apps/vibe/fix-buttons.js +0 -103
  192. package/apps/vibe/index.html +0 -3404
  193. package/apps/vibe/package-lock.json +0 -920
  194. package/apps/vibe/scripts/studio-pty-host.py +0 -117
  195. package/apps/vibe/src/main.js +0 -2940
  196. package/apps/vibe/src/register-all-languages.js +0 -98
  197. package/apps/vibe/start-studio.sh +0 -11
  198. package/apps/vibe/test/accessibility-tests.js +0 -77
  199. package/apps/vibe/test/browser-performance-audit.mjs +0 -205
  200. package/apps/vibe/test/performance-tests.js +0 -120
  201. package/apps/vibe/test/security-tests.js +0 -213
  202. package/apps/vibe/tests/e2e.local.mjs +0 -54
  203. package/apps/vibe/tests/server.smoke.mjs +0 -106
  204. package/apps/vibe/update_website.mjs +0 -74
  205. package/apps/vibe/vite.config.js +0 -19
  206. package/lib/crew-lead/chat-handler.mjs.bak +0 -1274
  207. 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
- }