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,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
- }