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
@@ -6,13 +6,13 @@
6
6
  <title>crewswarm dashboard</title>
7
7
  <link rel="icon" type="image/png" href="/favicon.png" />
8
8
  <!-- Font: system stack only to avoid CORS when dashboard (4319) and studio (3333) both load Inter from Google -->
9
- <script type="module" crossorigin src="/assets/index-DnClJ1ee.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-BeVllEj_.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="/assets/core-utils-CmOkXgzi.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/setup-wizard-CA0Or47w.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/components-BS9fQjE_.js">
13
13
  <link rel="modulepreload" crossorigin href="/assets/orchestration-Ca2DLWN-.js">
14
- <link rel="modulepreload" crossorigin href="/assets/cli-process-COMRNPqr.js">
15
- <link rel="modulepreload" crossorigin href="/assets/chat-core-Cx4sTxDd.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/cli-process-CNZ_UBCt.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/chat-core-uXb_C0GM.js">
16
16
  <link rel="modulepreload" crossorigin href="/assets/tab-swarm-chat-tab-BNrd88-r.js">
17
17
  <link rel="modulepreload" crossorigin href="/assets/tab-waves-tab-SaJDkb4x.js">
18
18
  <link rel="modulepreload" crossorigin href="/assets/tab-workflows-tab-B-soSy1k.js">
@@ -20,18 +20,19 @@
20
20
  <link rel="modulepreload" crossorigin href="/assets/tab-services-tab-DU_LH3uG.js">
21
21
  <link rel="modulepreload" crossorigin href="/assets/tab-agents-tab-BgpIsjkw.js">
22
22
  <link rel="modulepreload" crossorigin href="/assets/tab-prompts-tab-DVkUNaJd.js">
23
- <link rel="modulepreload" crossorigin href="/assets/tab-skills-tab-BpY0uZHW.js">
23
+ <link rel="modulepreload" crossorigin href="/assets/tab-testing-tab-CezZOZcJ.js">
24
+ <link rel="modulepreload" crossorigin href="/assets/tab-skills-tab-DR7PJ7NB.js">
24
25
  <link rel="modulepreload" crossorigin href="/assets/tab-contacts-tab-DiOyMYth.js">
25
26
  <link rel="modulepreload" crossorigin href="/assets/tab-engines-tab-BsdZVvU0.js">
26
27
  <link rel="modulepreload" crossorigin href="/assets/tab-swarm-tab-B1AcjL1W.js">
27
- <link rel="modulepreload" crossorigin href="/assets/tab-models-tab-BLEjmd19.js">
28
- <link rel="modulepreload" crossorigin href="/assets/tab-projects-tab-DhNWnlzt.js">
29
- <link rel="modulepreload" crossorigin href="/assets/tab-settings-tab-Bn4nXtDe.js">
28
+ <link rel="modulepreload" crossorigin href="/assets/tab-models-tab-dNRgsTOO.js">
29
+ <link rel="modulepreload" crossorigin href="/assets/tab-projects-tab-SFH4E--a.js">
30
+ <link rel="modulepreload" crossorigin href="/assets/tab-settings-tab-CuvH_Fj_.js">
30
31
  <link rel="modulepreload" crossorigin href="/assets/tab-comms-tab-kguqTIzD.js">
31
32
  <link rel="modulepreload" crossorigin href="/assets/tab-usage-tab-BIOOnB-Y.js">
32
33
  <link rel="modulepreload" crossorigin href="/assets/tab-spending-tab-DEccQHnt.js">
33
- <link rel="modulepreload" crossorigin href="/assets/tab-pm-loop-tab-Bfd449B4.js">
34
- <link rel="stylesheet" crossorigin href="/assets/index-CF0aJRtC.css">
34
+ <link rel="modulepreload" crossorigin href="/assets/tab-pm-loop-tab-DiAPTJXu.js">
35
+ <link rel="stylesheet" crossorigin href="/assets/index-D-sRshvg.css">
35
36
  </head>
36
37
  <body>
37
38
  <!-- Skip link for keyboard navigation -->
@@ -112,6 +113,10 @@
112
113
  <button class="nav-item" id="navRunSkills" data-view="run-skills">
113
114
  <span class="nav-icon">⚡</span> Run skills
114
115
  </button>
116
+ <button class="nav-item" id="navTesting" data-view="testing">
117
+ <span class="nav-icon">🧪</span> Testing
118
+ <span class="nav-badge hidden" id="testingBadge">0</span>
119
+ </button>
115
120
  <button class="nav-item" id="navBenchmarks" data-view="benchmarks">
116
121
  <span class="nav-icon">📊</span> Benchmarks
117
122
  </button>
@@ -1177,6 +1182,8 @@
1177
1182
  <option value="deepseek-chat">DeepSeek Chat</option>
1178
1183
  <option value="llama-3.3-70b-versatile">Llama 3.3 70B (Groq)</option>
1179
1184
  <option value="gpt-4o">GPT-4o</option>
1185
+ <option value="claude-haiku-4-5-20251001">Claude Haiku 4.5 (OAuth)</option>
1186
+ <option value="claude-sonnet-4-6">Claude Sonnet 4.6 (OAuth)</option>
1180
1187
  <option value="moonshot-v1-128k">Moonshot V1 128K</option>
1181
1188
  <option value="grok-4.20-beta-0309-non-reasoning">Grok 4.2 Beta (non-reasoning)</option>
1182
1189
  <option value="grok-4.20-beta-0309-reasoning">Grok 4.2 Beta (reasoning)</option>
@@ -2405,6 +2412,22 @@
2405
2412
  </div>
2406
2413
  </div>
2407
2414
 
2415
+ <!-- OAuth / Subscription Providers (no API key needed) -->
2416
+ <div
2417
+ style="
2418
+ font-size: 11px;
2419
+ font-weight: 600;
2420
+ color: var(--text-2);
2421
+ text-transform: uppercase;
2422
+ letter-spacing: 0.08em;
2423
+ margin: 18px 0 10px;
2424
+ padding: 0 2px;
2425
+ "
2426
+ >
2427
+ Subscription Providers (no API key)
2428
+ </div>
2429
+ <div id="oauthProvidersList"></div>
2430
+
2408
2431
  <!-- Search & Research Tools -->
2409
2432
  <div
2410
2433
  style="
@@ -4427,6 +4450,76 @@
4427
4450
  ></div>
4428
4451
  </div>
4429
4452
 
4453
+ <div class="card" style="margin-top: 16px">
4454
+ <div
4455
+ style="
4456
+ display: flex;
4457
+ align-items: center;
4458
+ justify-content: space-between;
4459
+ flex-wrap: wrap;
4460
+ gap: 12px;
4461
+ "
4462
+ >
4463
+ <div>
4464
+ <div class="card-title" style="margin-bottom: 2px">
4465
+ 🔌 tmux-bridge Sessions
4466
+ </div>
4467
+ <div
4468
+ style="
4469
+ font-size: 11px;
4470
+ color: var(--text-3);
4471
+ line-height: 1.5;
4472
+ "
4473
+ >
4474
+ Enable persistent tmux sessions that survive across pipeline
4475
+ waves. Agents can hand off live execution context (running
4476
+ servers, env vars, cwd) to the next wave instead of
4477
+ cold-starting. Requires
4478
+ <code
4479
+ style="
4480
+ background: var(--bg-1);
4481
+ padding: 1px 4px;
4482
+ border-radius: 3px;
4483
+ "
4484
+ >tmux</code
4485
+ >
4486
+ +
4487
+ <code
4488
+ style="
4489
+ background: var(--bg-1);
4490
+ padding: 1px 4px;
4491
+ border-radius: 3px;
4492
+ "
4493
+ >smux</code
4494
+ >
4495
+ installed. One writer per session (lock enforced).
4496
+ </div>
4497
+ </div>
4498
+ <button
4499
+ id="tmuxBridgeBtn"
4500
+ data-action="toggleTmuxBridge"
4501
+ style="
4502
+ font-size: 12px;
4503
+ font-weight: 700;
4504
+ padding: 8px 18px;
4505
+ border-radius: 8px;
4506
+ cursor: pointer;
4507
+ border: 1px solid var(--border);
4508
+ background: var(--surface-2);
4509
+ color: var(--text-2);
4510
+ white-space: nowrap;
4511
+ min-width: 80px;
4512
+ "
4513
+ >
4514
+ Loading…
4515
+ </button>
4516
+ </div>
4517
+ <div
4518
+ id="tmuxBridgeStatus"
4519
+ style="margin-top: 8px; font-size: 12px; color: var(--text-3)"
4520
+ ></div>
4521
+ </div>
4522
+
4430
4523
  <div class="card" style="margin-top: 16px">
4431
4524
  <div
4432
4525
  style="
@@ -5466,6 +5559,32 @@
5466
5559
  </div>
5467
5560
  </div>
5468
5561
 
5562
+ <!-- Testing -->
5563
+ <div class="view" id="testingView">
5564
+ <div class="page-header">
5565
+ <div>
5566
+ <div class="page-title">Testing</div>
5567
+ <div class="page-sub">
5568
+ Test suite results, group breakdown, and run history
5569
+ </div>
5570
+ </div>
5571
+ <div style="display: flex; align-items: center; gap: 8px">
5572
+ <button
5573
+ data-action="refreshTesting"
5574
+ class="btn-ghost"
5575
+ style="font-size: 12px"
5576
+ >
5577
+ ↻ Refresh
5578
+ </button>
5579
+ </div>
5580
+ </div>
5581
+ <div id="testProgressBar"></div>
5582
+ <div id="testingContent">
5583
+ <div class="meta" style="padding: 20px">Loading test results...</div>
5584
+ </div>
5585
+ <div id="testingHistory"></div>
5586
+ </div>
5587
+
5469
5588
  <!-- Benchmarks — ZeroEval / llm-stats leaderboards -->
5470
5589
  <div class="view" id="benchmarksView">
5471
5590
  <div class="page-header">
@@ -5977,9 +6096,9 @@
5977
6096
  <button
5978
6097
  id="enhancePromptBtn"
5979
6098
  class="btn-purple"
5980
- title="AI-enhance your requirement"
6099
+ title="Plan the requirement with the configured crew-pm CLI engine"
5981
6100
  >
5982
- Enhance
6101
+ 🧭 Plan
5983
6102
  </button>
5984
6103
  <span class="meta" id="buildStatus"></span>
5985
6104
  </div>
@@ -5991,10 +6110,11 @@
5991
6110
  <div
5992
6111
  style="margin-top: 10px; font-size: 11px; color: var(--text-3)"
5993
6112
  >
5994
- <b>▶ Run Build</b> — one phased build (MVP→Ph1→Ph2), runs in
5995
- background. <b>🔁 Build Until Done</b> — loops continuously until
5996
- roadmap exhausted. <b>PM Loop ▶</b> — reads ROADMAP.md and
5997
- dispatches each item one at a time.
6113
+ <b>🧭 Plan</b> — turns a rough idea into a concrete build brief
6114
+ using the configured PM engine. <b>▶ Run Build</b> — one phased
6115
+ build (MVP→Ph1→Ph2), runs in background. <b>🔁 Build Until Done</b>
6116
+ loops continuously until roadmap exhausted. <b>PM Loop ▶</b> —
6117
+ reads ROADMAP.md and dispatches each item one at a time.
5998
6118
  </div>
5999
6119
  </div>
6000
6120
 
Binary file
Binary file
@@ -48,7 +48,7 @@ npm run perf:audit
48
48
  ```
49
49
 
50
50
  `npm run test` runs the standalone smoke, accessibility, performance, and security checks.
51
- `npm run test:e2e` runs a self-contained local HTTP/API end-to-end check for the shipped Studio bundle and server routes.
51
+ `npm run test:e2e` runs a self-contained local HTTP/API end-to-end check for the shipped Vibe bundle and server routes.
52
52
 
53
53
  ## Performance Tooling
54
54
 
@@ -60,7 +60,7 @@ python3 ../../scripts/bench/load_testing.py \
60
60
  --requests 40 \
61
61
  --concurrency 4 \
62
62
  --profile-command "npm start" \
63
- --profile-output /tmp/crewswarm-studio.speedscope.json
63
+ --profile-output /tmp/crewswarm-vibe.speedscope.json
64
64
  ```
65
65
 
66
66
  Use `npm run perf:audit` for a browser-level audit of the shipped Vibe bundle. It boots the local Vibe server on an isolated port, captures navigation timing, transfer size, long tasks, and heap usage through Playwright + the browser Performance APIs, then writes a report to `apps/vibe/output/performance-audit.json`.
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "crewswarm-studio",
2
+ "name": "crewswarm-vibe",
3
3
  "version": "1.0.0",
4
4
  "type": "module",
5
5
  "scripts": {
@@ -13,7 +13,7 @@ import http from "node:http";
13
13
  import os from "node:os";
14
14
  import path from "node:path";
15
15
  import fs from "node:fs";
16
- import { spawn } from "node:child_process";
16
+ import { spawn, execFileSync } from "node:child_process";
17
17
  import { fileURLToPath } from "node:url";
18
18
  import { WebSocketServer } from "ws";
19
19
  import { shouldSkipGeminiPassthroughLine } from "../../lib/gemini-cli-passthrough-noise.mjs";
@@ -40,9 +40,9 @@ const SHARED_PROJECTS_FILE = path.join(CREWSWARM_CFG_DIR, "projects.json");
40
40
  const UI_STATE_FILE = path.join(CREWSWARM_CFG_DIR, "ui-state.json");
41
41
  const DEFAULT_PROJECT = {
42
42
  id: DEFAULT_PROJECT_ID,
43
- name: "Studio Workspace",
43
+ name: "Vibe Workspace",
44
44
  outputDir: WORKSPACE_DIR,
45
- description: "Local Studio workspace",
45
+ description: "Local Vibe workspace",
46
46
  };
47
47
 
48
48
  const MIME_TYPES = {
@@ -752,60 +752,54 @@ function createCodexStreamRelay(onChunk) {
752
752
  }
753
753
 
754
754
  function createCrewCliStreamRelay(onChunk) {
755
- let transcript = "";
756
- let rawTranscript = "";
757
- let lineBuffer = "";
758
- let collectingAssistant = false;
759
-
760
- const appendAssistant = (text) => {
761
- const normalized = String(text || "").replace(/\r/g, "").trimEnd();
762
- if (!normalized) return;
763
- transcript = appendNormalizedChunk(transcript, normalized);
764
- onChunk?.(`${normalized}\n`);
765
- };
766
-
767
- const handleLine = (line) => {
768
- const cleaned = stripAnsi(line).replace(/\r/g, "");
769
- rawTranscript = appendNormalizedChunk(rawTranscript, cleaned);
770
- const trimmed = cleaned.trim();
771
- if (!trimmed) {
772
- if (collectingAssistant && transcript) {
773
- appendAssistant("");
774
- }
775
- return;
776
- }
777
-
778
- if (trimmed === "--- Agent Response ---") {
779
- collectingAssistant = true;
780
- return;
781
- }
782
-
783
- if (trimmed === "Pipeline timeline:") {
784
- collectingAssistant = false;
785
- return;
786
- }
787
-
788
- if (collectingAssistant) {
789
- appendAssistant(cleaned);
790
- }
791
- };
755
+ // Buffer ALL output — don't stream anything until we can extract the response.
756
+ // crew-cli emits logs + a JSON envelope; we only want the response field.
757
+ let rawOutput = "";
792
758
 
793
759
  return {
794
760
  push(chunk) {
795
- lineBuffer += chunk.toString("utf8");
796
- while (lineBuffer.includes("\n")) {
797
- const newlineIndex = lineBuffer.indexOf("\n");
798
- const line = lineBuffer.slice(0, newlineIndex);
799
- lineBuffer = lineBuffer.slice(newlineIndex + 1);
800
- handleLine(line);
801
- }
761
+ rawOutput += chunk.toString("utf8");
802
762
  },
803
763
  finish() {
804
- if (lineBuffer.trim()) {
805
- handleLine(lineBuffer);
806
- lineBuffer = "";
764
+ const cleaned = stripAnsi(rawOutput).replace(/\r/g, "");
765
+
766
+ // Strategy 1: Find JSON envelope with "response" field
767
+ const jsonMatch = cleaned.match(/\{[\s\S]*"kind":\s*"[^"]+\.result"[\s\S]*\}/);
768
+ if (jsonMatch) {
769
+ try {
770
+ const parsed = JSON.parse(jsonMatch[0]);
771
+ if (parsed.response) {
772
+ onChunk?.(parsed.response);
773
+ return parsed.response;
774
+ }
775
+ } catch { /* fall through to other strategies */ }
776
+ }
777
+
778
+ // Strategy 2: Extract "response" field via regex (handles malformed JSON)
779
+ const respMatch = cleaned.match(/"response":\s*"((?:[^"\\]|\\.)*)"/);
780
+ if (respMatch) {
781
+ const response = respMatch[1].replace(/\\n/g, "\n").replace(/\\"/g, '"').replace(/\\t/g, "\t");
782
+ onChunk?.(response);
783
+ return response;
784
+ }
785
+
786
+ // Strategy 3: Legacy "--- Agent Response ---" marker
787
+ const markerIdx = cleaned.indexOf("--- Agent Response ---");
788
+ if (markerIdx >= 0) {
789
+ let response = cleaned.slice(markerIdx + "--- Agent Response ---".length);
790
+ const timelineIdx = response.indexOf("Pipeline timeline:");
791
+ if (timelineIdx >= 0) response = response.slice(0, timelineIdx);
792
+ response = response.trim();
793
+ if (response) {
794
+ onChunk?.(response);
795
+ return response;
796
+ }
807
797
  }
808
- return transcript.trim() || summarizeCliFailure("crew-cli", rawTranscript);
798
+
799
+ // Fallback: send raw output (stripped of common log prefixes)
800
+ const fallback = summarizeCliFailure("crew-cli", cleaned);
801
+ onChunk?.(fallback);
802
+ return fallback;
809
803
  },
810
804
  };
811
805
  }
@@ -957,6 +951,20 @@ function createCliRelay(engine, onChunk, onDone) {
957
951
  return createDefaultCliRelay(onChunk);
958
952
  }
959
953
 
954
+ function isClaudeOauthAuthenticated() {
955
+ try {
956
+ const output = execFileSync("claude", ["auth", "status"], {
957
+ encoding: "utf8",
958
+ stdio: ["ignore", "pipe", "ignore"],
959
+ }).trim().toLowerCase();
960
+ if (!output) return false;
961
+ if (/"loggedin"\s*:\s*true/.test(output)) return true;
962
+ return output.includes("logged in");
963
+ } catch {
964
+ return false;
965
+ }
966
+ }
967
+
960
968
  function broadcastTerminalMessage(sessionId, payload) {
961
969
  const session = terminalSessions.get(sessionId);
962
970
  if (!session) return;
@@ -1093,12 +1101,13 @@ export function getCliCommand(engine, projectDir, message, modelOverride, resume
1093
1101
  command: binary,
1094
1102
  args: codexArgs,
1095
1103
  stdin: null,
1104
+ stripEnv: ["OPENAI_API_KEY"],
1096
1105
  };
1097
1106
  }
1098
1107
  case "claude":
1099
1108
  // Claude Code uses OAuth — no API key needed
1100
1109
  {
1101
- const args = ["-p", "--setting-sources", "user", "--output-format", "stream-json", "--verbose"];
1110
+ const args = ["-p", "--setting-sources", "user", "--output-format", "stream-json", "--verbose", "--permission-mode", "auto"];
1102
1111
  const model = modelOverride || process.env.CREWSWARM_CLAUDE_CODE_MODEL || "";
1103
1112
  // Add workspace directory context
1104
1113
  if (projectDir) args.push("--add-dir", projectDir);
@@ -1110,7 +1119,7 @@ export function getCliCommand(engine, projectDir, message, modelOverride, resume
1110
1119
  command: "claude",
1111
1120
  args,
1112
1121
  stdin: null,
1113
- stripEnv: ["CLAUDECODE", "CLAUDE_CODE"],
1122
+ stripEnv: ["CLAUDECODE", "CLAUDE_CODE", "ANTHROPIC_API_KEY"],
1114
1123
  };
1115
1124
  }
1116
1125
  case "cursor":
@@ -1153,6 +1162,7 @@ export function getCliCommand(engine, projectDir, message, modelOverride, resume
1153
1162
  command: "gemini",
1154
1163
  args,
1155
1164
  stdin: null,
1165
+ stripEnv: ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
1156
1166
  };
1157
1167
  }
1158
1168
  case "opencode":
@@ -1165,7 +1175,7 @@ export function getCliCommand(engine, projectDir, message, modelOverride, resume
1165
1175
  model = cfg.opencodeModel || "";
1166
1176
  } catch {}
1167
1177
  }
1168
- if (!model) model = "opencode/gpt-5.2";
1178
+ if (!model) model = "openai/gpt-5.2-codex";
1169
1179
  const args = ["run", "-m", model, message];
1170
1180
  // Add workspace directory context
1171
1181
  if (projectDir) args.push("--dir", projectDir);
@@ -1175,12 +1185,13 @@ export function getCliCommand(engine, projectDir, message, modelOverride, resume
1175
1185
  command: "opencode",
1176
1186
  args,
1177
1187
  stdin: null,
1188
+ stripEnv: ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY"],
1178
1189
  };
1179
1190
  }
1180
1191
  case "crew-cli": {
1181
1192
  const crewBin = path.join(__dirname, "..", "..", "crew-cli", "bin", "crew.js");
1182
1193
  const model = modelOverride || process.env.CREWSWARM_CREW_CLI_MODEL || "";
1183
- const crewArgs = [crewBin, "chat", message, ...(projectDir ? ["--project", projectDir] : []), ...(model ? ["--model", model] : [])];
1194
+ const crewArgs = [crewBin, "chat", message, "--apply", "--json", ...(projectDir ? ["--project", projectDir] : []), ...(model ? ["--model", model] : [])];
1184
1195
  // Resume: crew-cli supports --session for conversation continuity
1185
1196
  if (resumeSession?.sessionId) crewArgs.push("--session", resumeSession.sessionId);
1186
1197
  return {
@@ -1214,6 +1225,10 @@ export function runCli({
1214
1225
  resume = true,
1215
1226
  }) {
1216
1227
  return new Promise((resolve, reject) => {
1228
+ if (engine === "claude" && !isClaudeOauthAuthenticated()) {
1229
+ reject(new Error("Claude Code requires CLI OAuth login. Run `claude auth login` and try again."));
1230
+ return;
1231
+ }
1217
1232
  // Look up existing session for resume
1218
1233
  const resumeKey = getCliResumeKey(engine, projectDir);
1219
1234
  const resumeSession = resume ? cliResumeSessions.get(resumeKey) : undefined;
@@ -1857,12 +1872,42 @@ export const server = http.createServer(async (req, res) => {
1857
1872
  return;
1858
1873
  }
1859
1874
 
1875
+ // GET /api/studio/git-diff — return changed files with old/new content for diff preview
1876
+ if (parsedUrl.pathname === "/api/studio/git-diff" && req.method === "GET") {
1877
+ const projDir = parsedUrl.searchParams.get("projectDir") || WORKSPACE_DIR;
1878
+ const resolved = resolveStudioProjectPath(projDir, WORKSPACE_DIR);
1879
+ try {
1880
+ const { execSync } = await import("node:child_process");
1881
+ const hasGit = fs.existsSync(path.join(resolved, ".git"));
1882
+ if (!hasGit) {
1883
+ sendJson(res, 200, { ok: true, files: [], message: "Not a git repository" });
1884
+ return;
1885
+ }
1886
+ const filesRaw = execSync("git diff --name-only", { cwd: resolved, encoding: "utf8", timeout: 3000 }).trim();
1887
+ const files = filesRaw ? filesRaw.split("\n").filter(Boolean) : [];
1888
+ // For each changed file, get old (HEAD) and new (working tree) content
1889
+ const changes = [];
1890
+ for (const f of files.slice(0, 20)) {
1891
+ try {
1892
+ let oldContent = "";
1893
+ try { oldContent = execSync(`git show HEAD:${f}`, { cwd: resolved, encoding: "utf8", timeout: 3000 }); } catch { /* new file */ }
1894
+ const newContent = fs.readFileSync(path.join(resolved, f), "utf8");
1895
+ changes.push({ path: f, oldContent, newContent });
1896
+ } catch { /* skip unreadable */ }
1897
+ }
1898
+ sendJson(res, 200, { ok: true, files, changes });
1899
+ } catch (e) {
1900
+ sendJson(res, 200, { ok: true, files: [], changes: [], error: e.message });
1901
+ }
1902
+ return;
1903
+ }
1904
+
1860
1905
  if (parsedUrl.pathname === "/api/studio/chat/unified" && req.method === "POST") {
1861
1906
  try {
1862
1907
  const body = await readBody(req);
1863
1908
  if (body.mode !== "cli") {
1864
1909
  sendJson(res, 400, {
1865
- error: "Local Studio chat only supports CLI passthrough right now",
1910
+ error: "Local Vibe chat only supports CLI passthrough right now",
1866
1911
  });
1867
1912
  return;
1868
1913
  }
package/crew-lead.mjs CHANGED
@@ -101,6 +101,7 @@ import {
101
101
  } from "./lib/crew-lead/wave-dispatcher.mjs";
102
102
  import { normalizeRtAgentId } from "./lib/agent-registry.mjs";
103
103
  import { handleAutonomousMentions } from "./lib/chat/autonomous-mentions.mjs";
104
+ import { saveProjectMessage } from "./lib/chat/project-messages.mjs";
104
105
  import { initIntervalManagers } from "./lib/crew-lead/interval-manager.mjs";
105
106
 
106
107
  // ── Single instance + canonical PID (dashboard / restart-crew-lead.sh use this path) ──
@@ -128,6 +129,11 @@ const agentLastHeartbeat = new Map(); // agentId → timestamp (ms) — tracks
128
129
  // SSE message throttling to prevent dashboard flashing
129
130
  const SSE_THROTTLE_MS = 500; // Only send same agent_working/idle once per 500ms
130
131
  const sseThrottle = new Map(); // key → lastSentMs
132
+ // Clean stale throttle entries every 10 minutes (#17)
133
+ setInterval(() => {
134
+ const cutoff = Date.now() - 3600_000; // 1 hour TTL
135
+ for (const [k, v] of sseThrottle) { if (v < cutoff) sseThrottle.delete(k); }
136
+ }, 600_000);
131
137
 
132
138
 
133
139
 
@@ -581,11 +587,33 @@ process.on("unhandledRejection", (reason) => {
581
587
  });
582
588
 
583
589
  process.on("uncaughtException", (err) => {
584
- console.error("[crew-lead] uncaught exception:", err?.stack || err?.message);
590
+ const msg = String(err?.message || err || "");
591
+ console.error("[crew-lead] uncaught exception:", err?.stack || msg);
592
+
593
+ // Benign errors from engine passthrough / WebSocket streams — keep alive
594
+ if (
595
+ msg === "terminated" ||
596
+ msg === "aborted" ||
597
+ /client.*disconnect/i.test(msg) ||
598
+ /socket hang up/i.test(msg) ||
599
+ /ECONNRESET/i.test(msg) ||
600
+ /EPIPE/i.test(msg) ||
601
+ /fetch failed/i.test(msg) ||
602
+ /UND_ERR/i.test(msg) ||
603
+ /ECONNREFUSED/i.test(msg)
604
+ ) {
605
+ console.error("[crew-lead] Non-fatal uncaughtException — keeping alive");
606
+ return;
607
+ }
585
608
 
586
- // Always exit on uncaught exceptionsthey leave process in undefined state
587
- console.error("[crew-lead] FATAL exiting due to uncaught exception");
588
- process.exit(1);
609
+ // Fatal errors: port conflicts, permissions, OOM must exit
610
+ if (/EADDRINUSE|EACCES|out of memory|cannot allocate/i.test(msg)) {
611
+ console.error("[crew-lead] FATAL — exiting due to uncaught exception");
612
+ process.exit(1);
613
+ }
614
+
615
+ // Default: log but keep alive
616
+ console.error("[crew-lead] Unexpected uncaughtException — keeping alive (not fatal)");
589
617
  });
590
618
 
591
619
  // ── RT Bus listener — receives replies from agents ────────────────────────────
@@ -616,6 +644,7 @@ initWaveDispatcher({
616
644
  bumpOpsCounter,
617
645
  tryRead,
618
646
  _cursorWavesEnabled,
647
+ loadAgentList: loadAgentListFromConfig,
619
648
  getClaudeCodeEnabled: () => _claudeCodeEnabled,
620
649
  dispatchTimeoutMs: DISPATCH_TIMEOUT_MS,
621
650
  dispatchClaimedTimeoutMs: DISPATCH_CLAIMED_TIMEOUT_MS,
@@ -646,6 +675,7 @@ const connectRT = initWsRouter({
646
675
  appendHistory,
647
676
  pendingPipelines,
648
677
  handleAutonomousMentions,
678
+ saveProjectMessage,
649
679
  checkWaveQualityGate,
650
680
  failPipelineOnQualityGate,
651
681
  savePipelineState,
@@ -313,7 +313,7 @@ function existsSync(path) {
313
313
  function getDefaultModelForCLI(cliName) {
314
314
  switch (cliName) {
315
315
  case "opencode":
316
- return process.env.CREWSWARM_OPENCODE_MODEL || "groq/moonshotai/kimi-k2-instruct-0905";
316
+ return process.env.CREWSWARM_OPENCODE_MODEL || "openai/gpt-5.4";
317
317
  case "cursor":
318
318
  return process.env.CURSOR_DEFAULT_MODEL || "gemini-3-flash";
319
319
  case "claude":
@@ -43,6 +43,10 @@ export function initGatewayWs(deps) {
43
43
 
44
44
  const client = {
45
45
  publish({ channel, type, to = "broadcast", taskId, correlationId, priority = "medium", payload = {} }) {
46
+ if (!ready) {
47
+ console.warn(`[gateway-ws] Publish skipped — client not ready (channel=${channel}, to=${to})`);
48
+ return;
49
+ }
46
50
  sendFrame({
47
51
  type: "publish",
48
52
  channel,
@@ -14,6 +14,7 @@ export function shouldDropPassthroughStderrLine(engine, line) {
14
14
  if (/rmcp::/i.test(l)) return true;
15
15
  if (/error decoding response body.*initialized notification/i.test(l))
16
16
  return true;
17
+ if (/\[Executor\]\s+\w+\s+\((OAuth|API)\)/i.test(l)) return true;
17
18
  if (
18
19
  engine === "codex" &&
19
20
  /worker quit with fatal/i.test(l) &&
@@ -198,7 +198,7 @@ export function getProjectMessageStats(projectId) {
198
198
 
199
199
  const stats = {
200
200
  total: messages.length,
201
- bySo: {},
201
+ bySource: {},
202
202
  byAgent: {},
203
203
  oldestMessage: null,
204
204
  newestMessage: null,
@@ -206,8 +206,6 @@ export function getProjectMessageStats(projectId) {
206
206
  assistantMessages: 0
207
207
  };
208
208
 
209
- stats.bySource = {};
210
-
211
209
  for (const msg of messages) {
212
210
  // Count by source
213
211
  stats.bySource[msg.source] = (stats.bySource[msg.source] || 0) + 1;
@@ -381,8 +379,8 @@ export function buildMessageTree(projectId, rootId = null) {
381
379
  if (msg.parentId && messageMap.has(msg.parentId)) {
382
380
  // Add to parent's children
383
381
  messageMap.get(msg.parentId).children.push(node);
384
- } else if (!msg.parentId || rootId === msg.id) {
385
- // Root node (no parent) or requested root
382
+ } else {
383
+ // Root node (no parent), requested root, or orphan (parentId references missing message)
386
384
  roots.push(node);
387
385
  }
388
386
  }
@@ -19,7 +19,7 @@ import { homedir } from "node:os";
19
19
  const CLI_PROCESS_LOG = join(homedir(), ".crewswarm", "logs", "cli-processes.jsonl");
20
20
  const CLI_PROCESS_STATE = join(homedir(), ".crewswarm", "cli-process-state.json");
21
21
 
22
- mkdirSync(join(homedir(), ".crewswarm", "logs"), { recursive: true });
22
+ try { mkdirSync(join(homedir(), ".crewswarm", "logs"), { recursive: true }); } catch { /* read-only homedir */ }
23
23
 
24
24
  // Active processes: { processId: { pid, agent, cli, task, startTime, lastActivity, status, chatId, sessionId } }
25
25
  let activeProcesses = new Map();
@@ -121,10 +121,11 @@ export function completeCLIProcess(processId, result) {
121
121
  log("info", "CLI process completed", { processId, ...result });
122
122
 
123
123
  // Remove from active list after 30s (keep recent history visible)
124
- setTimeout(() => {
124
+ const timer = setTimeout(() => {
125
125
  activeProcesses.delete(processId);
126
126
  saveState();
127
127
  }, 30000);
128
+ timer.unref(); // Don't prevent process exit in tests
128
129
  }
129
130
 
130
131
  /**