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.
- package/README.md +22 -9
- package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js +1 -0
- package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js.br +0 -0
- package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js +1 -0
- package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js.br +0 -0
- package/apps/dashboard/dist/assets/index-BeVllEj_.js +2 -0
- package/apps/dashboard/dist/assets/index-BeVllEj_.js.br +0 -0
- package/apps/dashboard/dist/assets/{index-CF0aJRtC.css → index-D-sRshvg.css} +1 -1
- package/apps/dashboard/dist/assets/index-D-sRshvg.css.br +0 -0
- package/apps/dashboard/dist/assets/tab-benchmarks-tab-BHjKCPm3.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js +1 -0
- package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-pm-loop-tab-Bfd449B4.js → tab-pm-loop-tab-DiAPTJXu.js} +1 -1
- package/apps/dashboard/dist/assets/tab-pm-loop-tab-DiAPTJXu.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-projects-tab-DhNWnlzt.js → tab-projects-tab-SFH4E--a.js} +1 -1
- package/apps/dashboard/dist/assets/tab-projects-tab-SFH4E--a.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js +1 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js +1 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js +1 -0
- package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js.br +0 -0
- package/apps/dashboard/dist/index.html +135 -15
- package/apps/dashboard/dist/index.html.br +0 -0
- package/apps/dashboard/dist/index.html.gz +0 -0
- package/apps/vibe/README.md +2 -2
- package/apps/vibe/package.json +1 -1
- package/apps/vibe/server.mjs +101 -56
- package/crew-lead.mjs +34 -4
- package/lib/bridges/cli-executor.mjs +1 -1
- package/lib/bridges/gateway-ws.mjs +4 -0
- package/lib/browser/passthrough-stderr.js +1 -0
- package/lib/chat/project-messages.mjs +3 -5
- package/lib/cli-process-tracker.mjs +3 -2
- package/lib/contacts/identity-linker.mjs +1 -0
- package/lib/crew-judge/judge.mjs +19 -18
- package/lib/crew-lead/agent-manager.mjs +1 -1
- package/lib/crew-lead/background.mjs +14 -1
- package/lib/crew-lead/chat-handler.mjs +38 -1
- package/lib/crew-lead/http-server.mjs +106 -57
- package/lib/crew-lead/llm-caller.mjs +24 -8
- package/lib/crew-lead/prompts.mjs +14 -1
- package/lib/crew-lead/tools.mjs +3 -2
- package/lib/crew-lead/wave-dispatcher.mjs +19 -5
- package/lib/crew-lead/ws-router.mjs +219 -27
- package/lib/engines/crew-cli.mjs +1 -1
- package/lib/engines/engine-registry.mjs +14 -3
- package/lib/engines/rt-envelope.mjs +1 -0
- package/lib/engines/runners.mjs +28 -4
- package/lib/gemini-cli-passthrough-noise.mjs +1 -1
- package/lib/integrations/code-search.mjs +4 -3
- package/lib/memory/shared-adapter.mjs +23 -10
- package/lib/pipeline/manager.mjs +2 -1
- package/lib/runtime/config.mjs +1 -1
- package/lib/runtime/paths.mjs +12 -8
- package/lib/runtime/spending.mjs +2 -1
- package/package.json +42 -14
- package/scripts/capture-build-flow.mjs +118 -0
- package/scripts/coverage-report.mjs +209 -0
- package/scripts/coverage-summary.mjs +47 -0
- package/scripts/dashboard-validation.mjs +76 -0
- package/scripts/dashboard.mjs +1667 -551
- package/scripts/generate-openapi.mjs +683 -277
- package/scripts/live-bridge-matrix.mjs +79 -0
- package/scripts/live-cli-matrix.mjs +166 -0
- package/scripts/live-crewchat-check.mjs +42 -0
- package/scripts/live-engine-matrix.mjs +50 -0
- package/scripts/live-provider-failover-matrix.mjs +107 -0
- package/scripts/live-provider-matrix.mjs +228 -0
- package/scripts/restart-all-from-repo.sh +4 -4
- package/scripts/restart-service.sh +12 -9
- package/scripts/smoke-dispatch.mjs +4 -1
- package/scripts/test-blast-radius.mjs +204 -0
- package/scripts/test-report-summary.mjs +88 -0
- package/scripts/test-reporter.mjs +651 -0
- package/scripts/test-rerun.mjs +136 -0
- package/scripts/tmux-bridge +130 -0
- package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js +0 -1
- package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js.br +0 -0
- package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js +0 -1
- package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
- package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
- package/apps/dashboard/dist/assets/index-DnClJ1ee.js +0 -2
- package/apps/dashboard/dist/assets/index-DnClJ1ee.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js +0 -1
- package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-pm-loop-tab-Bfd449B4.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-projects-tab-DhNWnlzt.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js +0 -1
- package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js +0 -1
- package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js.br +0 -0
- package/apps/dashboard/index.html +0 -6529
- package/apps/dashboard/package.json +0 -15
- package/apps/dashboard/src/app.js +0 -2828
- package/apps/dashboard/src/app.js.br +0 -0
- package/apps/dashboard/src/app.js.gz +0 -0
- package/apps/dashboard/src/chat/chat-actions.js +0 -1847
- package/apps/dashboard/src/chat/chat-actions.js.br +0 -0
- package/apps/dashboard/src/chat/unified-messages.js +0 -327
- package/apps/dashboard/src/chat/unified-messages.js.br +0 -0
- package/apps/dashboard/src/cli-process.js +0 -208
- package/apps/dashboard/src/cli-process.js.br +0 -0
- package/apps/dashboard/src/cli-process.js.gz +0 -0
- package/apps/dashboard/src/components/active-tasks-panel.js +0 -175
- package/apps/dashboard/src/components/active-tasks-panel.js.br +0 -0
- package/apps/dashboard/src/core/api.js +0 -18
- package/apps/dashboard/src/core/api.js.br +0 -0
- package/apps/dashboard/src/core/dom.js +0 -228
- package/apps/dashboard/src/core/dom.js.br +0 -0
- package/apps/dashboard/src/core/state.js +0 -91
- package/apps/dashboard/src/core/state.js.br +0 -0
- package/apps/dashboard/src/core/task-manager.js +0 -134
- package/apps/dashboard/src/core/task-manager.js.br +0 -0
- package/apps/dashboard/src/orchestration-status.js +0 -127
- package/apps/dashboard/src/orchestration-status.js.br +0 -0
- package/apps/dashboard/src/setup-wizard.js +0 -562
- package/apps/dashboard/src/setup-wizard.js.br +0 -0
- package/apps/dashboard/src/styles.css +0 -2085
- package/apps/dashboard/src/styles.css.br +0 -0
- package/apps/dashboard/src/styles.css.gz +0 -0
- package/apps/dashboard/src/tabs/agents-tab.js +0 -2237
- package/apps/dashboard/src/tabs/agents-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/benchmarks-tab.js +0 -229
- package/apps/dashboard/src/tabs/benchmarks-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/comms-tab.js +0 -955
- package/apps/dashboard/src/tabs/comms-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/contacts-tab.js +0 -654
- package/apps/dashboard/src/tabs/contacts-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/engines-tab.js +0 -175
- package/apps/dashboard/src/tabs/engines-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/memory-tab.js +0 -182
- package/apps/dashboard/src/tabs/memory-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/models-tab.js +0 -450
- package/apps/dashboard/src/tabs/models-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/pm-loop-tab.js +0 -185
- package/apps/dashboard/src/tabs/pm-loop-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/projects-tab.js +0 -663
- package/apps/dashboard/src/tabs/projects-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/projects-tab.js.gz +0 -0
- package/apps/dashboard/src/tabs/prompts-tab.js +0 -160
- package/apps/dashboard/src/tabs/prompts-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/services-tab.js +0 -202
- package/apps/dashboard/src/tabs/services-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/settings-tab.js +0 -861
- package/apps/dashboard/src/tabs/settings-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/skills-tab.js +0 -284
- package/apps/dashboard/src/tabs/skills-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/spending-tab.js +0 -173
- package/apps/dashboard/src/tabs/spending-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/swarm-chat-tab.js +0 -660
- package/apps/dashboard/src/tabs/swarm-chat-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/swarm-tab.js +0 -538
- package/apps/dashboard/src/tabs/swarm-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/usage-tab.js +0 -390
- package/apps/dashboard/src/tabs/usage-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/waves-tab.js +0 -238
- package/apps/dashboard/src/tabs/waves-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/workflows-tab.js +0 -747
- package/apps/dashboard/src/tabs/workflows-tab.js.br +0 -0
- package/apps/vibe/.crew/agent-memory/pipeline.json +0 -304
- package/apps/vibe/.crew/cost.json +0 -17
- package/apps/vibe/.crew/json-parse-metrics.jsonl +0 -27
- package/apps/vibe/.crew/pipeline-metrics.jsonl +0 -27
- package/apps/vibe/.crew/pipeline-runs/pipeline-0f90c392-2425-4ae5-850c-bd9d17b1d690.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-1c269dd9-a63f-4fba-af81-5cf08048ef06.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-288a7765-da24-4a22-89bc-1f3cc9b0562c.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-2c78fd22-a657-4bd1-bc49-0679fb384409.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-3da23550-22ed-4904-9a0a-8e79c1f3024c.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-3e6fe08d-3264-404a-8df3-aab7efef10e7.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-42eec610-57fe-4e09-9e7e-b315038495c2.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-4438eb4c-ae13-42b1-90e2-b043d8983be8.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-4740a9f5-86e7-44b6-a394-de433e291727.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-49e1da6a-957e-48fd-9220-415019e4f8e2.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-4c9251db-be68-427b-a3fc-a264f2b5778d.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-6413fa33-a802-4b57-a8c0-a9056ad67842.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-65e29a57-664d-4196-8109-017e364f182e.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-6aa04bc5-9593-4b1f-b58d-3bf2978cb602.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-6e1cba53-9b70-457e-99e0-59199149dd21.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-749f41cc-4dac-4204-be64-873a6080a0d2.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-74d68121-e181-4864-bd9a-c3211341dfaf.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-8509bc24-142d-4e07-b44a-a50bf99d1103.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-960339c6-07ca-43ce-9900-f6e1702b39b9.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-9bef2dd2-6122-42e5-b3d9-19f4d80f9e40.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-9c6480a9-7031-4146-b241-825b9a2d1de1.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-9fd42426-8492-4157-9d5f-e1537c060489.jsonl +0 -2
- package/apps/vibe/.crew/pipeline-runs/pipeline-ad6d40a3-2f5e-46a9-a345-47caaccc51aa.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-bc606133-8d5b-4535-8d85-f1a29cdaa981.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-c1a13ccd-634a-4d01-a4a7-1177b8a752ff.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-c7d27b42-249e-4bd4-8f26-6aa998110b8a.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-cca2e9b9-4a34-4d25-a311-5c793fa7e91e.jsonl +0 -5
- package/apps/vibe/.crew/sandbox.json +0 -7
- package/apps/vibe/.crew/session.json +0 -330
- package/apps/vibe/.crew/training-data.jsonl +0 -0
- package/apps/vibe/.github/workflows/studio-quality.yml +0 -37
- package/apps/vibe/.studio-data/project-messages/chuck-norris.jsonl +0 -18
- package/apps/vibe/.studio-data/project-messages/general.jsonl +0 -81
- package/apps/vibe/.studio-data/project-messages/studio-local.jsonl +0 -18
- package/apps/vibe/ARCHITECTURE.md +0 -3393
- package/apps/vibe/QUICK-REFERENCE.md +0 -211
- package/apps/vibe/ROADMAP.md +0 -41
- package/apps/vibe/STUDIO-SETUP-COMPLETE.md +0 -35
- package/apps/vibe/VISUAL-GUIDE.md +0 -378
- package/apps/vibe/capture-demo.mjs +0 -160
- package/apps/vibe/capture-full-demo.mjs +0 -255
- package/apps/vibe/capture-quickstart.mjs +0 -256
- package/apps/vibe/capture-vibe-assets.mjs +0 -71
- package/apps/vibe/capture-vibe-video.mjs +0 -260
- package/apps/vibe/check-buttons.js +0 -41
- package/apps/vibe/diagnose.html +0 -106
- package/apps/vibe/fix-buttons.js +0 -103
- package/apps/vibe/index.html +0 -3404
- package/apps/vibe/package-lock.json +0 -920
- package/apps/vibe/scripts/studio-pty-host.py +0 -117
- package/apps/vibe/src/main.js +0 -2940
- package/apps/vibe/src/register-all-languages.js +0 -98
- package/apps/vibe/start-studio.sh +0 -11
- package/apps/vibe/test/accessibility-tests.js +0 -77
- package/apps/vibe/test/browser-performance-audit.mjs +0 -205
- package/apps/vibe/test/performance-tests.js +0 -120
- package/apps/vibe/test/security-tests.js +0 -213
- package/apps/vibe/tests/e2e.local.mjs +0 -54
- package/apps/vibe/tests/server.smoke.mjs +0 -106
- package/apps/vibe/update_website.mjs +0 -74
- package/apps/vibe/vite.config.js +0 -19
- package/lib/crew-lead/chat-handler.mjs.bak +0 -1274
- package/lib/engines/rt-envelope.mjs.backup-current +0 -870
package/lib/crew-judge/judge.mjs
CHANGED
|
@@ -150,25 +150,26 @@ function getJudgeModel() {
|
|
|
150
150
|
|
|
151
151
|
async function callJudgeModel(model, prompt) {
|
|
152
152
|
// Import LLM caller from crew-lead
|
|
153
|
-
const {
|
|
154
|
-
|
|
153
|
+
const { callLLM } = await import('../crew-lead/llm-caller.mjs');
|
|
154
|
+
const { loadProviderMap } = await import('../runtime/config.mjs');
|
|
155
|
+
|
|
155
156
|
try {
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
{
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
});
|
|
157
|
+
const providerKey = model.split('/')[0];
|
|
158
|
+
const modelId = model.split('/').slice(1).join('/');
|
|
159
|
+
const providerMap = loadProviderMap();
|
|
160
|
+
const provider = providerMap[providerKey] || null;
|
|
161
|
+
|
|
162
|
+
const messages = [
|
|
163
|
+
{
|
|
164
|
+
role: 'system',
|
|
165
|
+
content: 'You are crew-judge. Return ONLY valid JSON: {"decision": "CONTINUE"|"SHIP"|"RESET", "reasoning": "...", "confidence": 0.0-1.0}'
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
role: 'user',
|
|
169
|
+
content: prompt
|
|
170
|
+
}
|
|
171
|
+
];
|
|
172
|
+
const { reply: response } = await callLLM(messages, { provider, modelId, providerKey }, { temperature: 0.3, maxTokens: 500 });
|
|
172
173
|
|
|
173
174
|
// Parse JSON response
|
|
174
175
|
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
@@ -79,7 +79,7 @@ export function createAgent({ id, role, displayName, prompt, description, model
|
|
|
79
79
|
const defaultOcModel = (() => {
|
|
80
80
|
const existingCoder = swarm.agents.find(a => a.opencodeModel && a.useOpenCode);
|
|
81
81
|
if (existingCoder) return existingCoder.opencodeModel;
|
|
82
|
-
return process.env.CREWSWARM_OPENCODE_MODEL || "openai/gpt-5.
|
|
82
|
+
return process.env.CREWSWARM_OPENCODE_MODEL || "openai/gpt-5.4";
|
|
83
83
|
})();
|
|
84
84
|
|
|
85
85
|
const agentEntry = {
|
|
@@ -28,6 +28,19 @@ const _timeoutLog = [];
|
|
|
28
28
|
let _lastBgConsciousnessAt = 0;
|
|
29
29
|
let _bgLoopInterval = null;
|
|
30
30
|
|
|
31
|
+
/** Reset internal state for test isolation. */
|
|
32
|
+
export function resetForTesting() {
|
|
33
|
+
_agentTimeoutCounts.clear();
|
|
34
|
+
_timeoutLog.length = 0;
|
|
35
|
+
_lastBgConsciousnessAt = 0;
|
|
36
|
+
if (_bgLoopInterval) { clearInterval(_bgLoopInterval); _bgLoopInterval = null; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Stop the background loop interval (prevents process from hanging in tests). */
|
|
40
|
+
export function stopBackgroundLoop() {
|
|
41
|
+
if (_bgLoopInterval) { clearInterval(_bgLoopInterval); _bgLoopInterval = null; }
|
|
42
|
+
}
|
|
43
|
+
|
|
31
44
|
export function initBackground({
|
|
32
45
|
broadcastSSE,
|
|
33
46
|
appendHistory,
|
|
@@ -267,4 +280,4 @@ export function getRateLimitFallback(agentId) {
|
|
|
267
280
|
return "crew-main";
|
|
268
281
|
}
|
|
269
282
|
|
|
270
|
-
export const RATE_LIMIT_PATTERN = /429|rate\
|
|
283
|
+
export const RATE_LIMIT_PATTERN = /429|rate[\s_]*limit|throttl|quota[\s_]*exceeded|too[\s_]*many[\s_]*requests|resource_exhausted|overloaded/i;
|
|
@@ -750,7 +750,10 @@ Reply with your answers and I'll turn this into a concrete build plan with file
|
|
|
750
750
|
// IMPORTANT: Do not gate on keywords only. Casual replies ("yeah no shit", "ok cool")
|
|
751
751
|
// used to skip the snapshot while long contradictory assistant history stayed in context,
|
|
752
752
|
// so the LLM invented swarm-wide offline/online flips. Fetch for substantive messages.
|
|
753
|
-
|
|
753
|
+
// Custom personas (displayName set to something other than default "crew-lead") are
|
|
754
|
+
// personality/chat personas — they don't need operational system state, skip snapshot.
|
|
755
|
+
const isCustomPersona = cfg.displayName && cfg.displayName.toLowerCase() !== "crew-lead";
|
|
756
|
+
const needsHealth = !isCustomPersona && message.trim().length >= 6;
|
|
754
757
|
const needsBenchmarkCatalog =
|
|
755
758
|
message.length > 4 &&
|
|
756
759
|
/benchmark|zeroeval|leaderboard|llm-stats|swe-bench|livecodebench|mmlu|gpqa|humaneval|gsm8k|what\.tests?|which\.tests?|available\.tests?/i.test(
|
|
@@ -2577,6 +2580,40 @@ Reply with your answers and I'll turn this into a concrete build plan with file
|
|
|
2577
2580
|
...(usedFallback ? { fallbackReason } : {}),
|
|
2578
2581
|
},
|
|
2579
2582
|
});
|
|
2583
|
+
|
|
2584
|
+
// Route @mentions in assistant replies — crew-lead can autonomously dispatch
|
|
2585
|
+
if (replyMentions.length && channelMode) {
|
|
2586
|
+
try {
|
|
2587
|
+
const {
|
|
2588
|
+
handleAutonomousMentions: routeMentions,
|
|
2589
|
+
detectMentionTargets,
|
|
2590
|
+
} = await import("../chat/autonomous-mentions.mjs");
|
|
2591
|
+
const mentionTargets = detectMentionTargets(historyReply);
|
|
2592
|
+
const mentionRoute = classifySharedChatMention(historyReply);
|
|
2593
|
+
if (mentionTargets.length && mentionRoute.mode === "dispatch") {
|
|
2594
|
+
void routeMentions({
|
|
2595
|
+
message: { content: historyReply },
|
|
2596
|
+
sender: "crew-lead",
|
|
2597
|
+
channel: sharedChannel,
|
|
2598
|
+
projectId: sharedChannel,
|
|
2599
|
+
sessionId,
|
|
2600
|
+
projectDir: explicitProjectDir || activeProjectOutputDir || null,
|
|
2601
|
+
originMessageId: assistantMessageId,
|
|
2602
|
+
originThreadId: sharedThreadId,
|
|
2603
|
+
chatHistory: loadProjectMessages(sharedChannel, {
|
|
2604
|
+
limit: 10,
|
|
2605
|
+
threadId: sharedThreadId,
|
|
2606
|
+
excludeDirect: true,
|
|
2607
|
+
}),
|
|
2608
|
+
broadcastSSE: _deps.broadcastSSE,
|
|
2609
|
+
}).catch((err) => {
|
|
2610
|
+
console.warn(`[chat-handler] Assistant mention routing failed: ${err.message}`);
|
|
2611
|
+
});
|
|
2612
|
+
}
|
|
2613
|
+
} catch (mentionErr) {
|
|
2614
|
+
console.warn(`[chat-handler] Assistant mention dispatch error: ${mentionErr.message}`);
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2580
2617
|
} catch (e) {
|
|
2581
2618
|
console.warn(
|
|
2582
2619
|
`[chat-handler] Failed to save assistant message to project store: ${e.message}`,
|
|
@@ -8,7 +8,7 @@ import fs from "node:fs";
|
|
|
8
8
|
import crypto from "node:crypto";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import os from "node:os";
|
|
11
|
-
import { spawn } from "node:child_process";
|
|
11
|
+
import { spawn, execFileSync } from "node:child_process";
|
|
12
12
|
import { executeCLI } from "../bridges/cli-executor.mjs";
|
|
13
13
|
import { applySharedChatPromptOverlay } from "../chat/shared-chat-prompt-overlay.mjs";
|
|
14
14
|
import { classifySharedChatMention } from "../chat/mention-routing-intent.mjs";
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import { shouldSkipGeminiPassthroughLine } from "../gemini-cli-passthrough-noise.mjs";
|
|
21
21
|
import { applyProjectDirToPipelineSteps } from "../dispatch/parsers.mjs";
|
|
22
22
|
import { normalizeProjectDir } from "../runtime/project-dir.mjs";
|
|
23
|
-
import { CREWSWARM_REPO_ROOT } from "../runtime/config.mjs";
|
|
23
|
+
import { CREWSWARM_REPO_ROOT, loadSwarmConfig } from "../runtime/config.mjs";
|
|
24
24
|
import { resolveCursorLaunchSpec } from "../engines/cursor-launcher.mjs";
|
|
25
25
|
|
|
26
26
|
let _deps = {};
|
|
@@ -60,6 +60,25 @@ function resolveCliBinary(configured, candidates = []) {
|
|
|
60
60
|
return configured;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
function isClaudeOauthAuthenticated() {
|
|
64
|
+
try {
|
|
65
|
+
const bin = resolveCliBinary("claude", [
|
|
66
|
+
path.join(os.homedir(), ".local", "bin", "claude"),
|
|
67
|
+
"/usr/local/bin/claude",
|
|
68
|
+
"/opt/homebrew/bin/claude",
|
|
69
|
+
]);
|
|
70
|
+
const output = execFileSync(bin, ["auth", "status"], {
|
|
71
|
+
encoding: "utf8",
|
|
72
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
73
|
+
}).trim().toLowerCase();
|
|
74
|
+
if (!output) return false;
|
|
75
|
+
if (/"loggedin"\s*:\s*true/.test(output)) return true;
|
|
76
|
+
return output.includes("logged in");
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
63
82
|
/** Strip SGR / OSC ANSI sequences (OpenCode and CLIs often emit colored TTY junk on stdout). */
|
|
64
83
|
function stripAnsiPassthrough(text) {
|
|
65
84
|
return String(text || "")
|
|
@@ -94,6 +113,7 @@ function shouldDropPassthroughStderrLine(engine, line) {
|
|
|
94
113
|
if (/rmcp::/i.test(l)) return true;
|
|
95
114
|
if (/error decoding response body.*initialized notification/i.test(l))
|
|
96
115
|
return true;
|
|
116
|
+
if (/\[Executor\]\s+\w+\s+\((OAuth|API)\)/i.test(l)) return true;
|
|
97
117
|
if (
|
|
98
118
|
engine === "codex" &&
|
|
99
119
|
/worker quit with fatal/i.test(l) &&
|
|
@@ -3532,6 +3552,9 @@ export function createAndStartServer(PORT) {
|
|
|
3532
3552
|
sessionId: reqSessionId,
|
|
3533
3553
|
injectHistory,
|
|
3534
3554
|
model: reqModel,
|
|
3555
|
+
permissionMode: reqPermissionMode,
|
|
3556
|
+
sandbox: reqSandbox,
|
|
3557
|
+
forceL2: reqForceL2,
|
|
3535
3558
|
} = JSON.parse(body || "{}");
|
|
3536
3559
|
if (!message) {
|
|
3537
3560
|
json(res, 400, { ok: false, error: "message required" });
|
|
@@ -3730,7 +3753,7 @@ export function createAndStartServer(PORT) {
|
|
|
3730
3753
|
const ocModel =
|
|
3731
3754
|
reqModel ||
|
|
3732
3755
|
process.env.CREWSWARM_OPENCODE_MODEL ||
|
|
3733
|
-
"
|
|
3756
|
+
"openai/gpt-5.4";
|
|
3734
3757
|
args = ["run", finalMessage, "--model", ocModel];
|
|
3735
3758
|
// Add workspace directory context
|
|
3736
3759
|
if (projectDir) args.push("--dir", projectDir);
|
|
@@ -3753,7 +3776,7 @@ export function createAndStartServer(PORT) {
|
|
|
3753
3776
|
|
|
3754
3777
|
if (priorCodexThread) {
|
|
3755
3778
|
// `codex resume <thread_id> "prompt"` continues the prior session
|
|
3756
|
-
args = ["resume", priorCodexThread, finalMessage
|
|
3779
|
+
args = ["resume", priorCodexThread, finalMessage];
|
|
3757
3780
|
if (codexModel) args.push("--model", codexModel);
|
|
3758
3781
|
if (projectDir) args.push("-C", projectDir);
|
|
3759
3782
|
} else {
|
|
@@ -3762,7 +3785,7 @@ export function createAndStartServer(PORT) {
|
|
|
3762
3785
|
"never",
|
|
3763
3786
|
"exec",
|
|
3764
3787
|
"--sandbox",
|
|
3765
|
-
"danger-full-access",
|
|
3788
|
+
reqSandbox || "danger-full-access",
|
|
3766
3789
|
"--skip-git-repo-check",
|
|
3767
3790
|
"--color",
|
|
3768
3791
|
"never",
|
|
@@ -3803,10 +3826,10 @@ export function createAndStartServer(PORT) {
|
|
|
3803
3826
|
const crewCliModel =
|
|
3804
3827
|
reqModel || process.env.CREWSWARM_CREW_CLI_MODEL || null;
|
|
3805
3828
|
if (bin.endsWith(".js")) {
|
|
3806
|
-
args = [bin, "chat", finalMessage, "--direct", "--json"];
|
|
3829
|
+
args = [bin, "chat", finalMessage, "--direct", "--json", "--apply"];
|
|
3807
3830
|
bin = process.execPath;
|
|
3808
3831
|
} else {
|
|
3809
|
-
args = ["chat", finalMessage, "--direct", "--json"];
|
|
3832
|
+
args = ["chat", finalMessage, "--direct", "--json", "--apply"];
|
|
3810
3833
|
}
|
|
3811
3834
|
if (crewCliModel) args.push("--model", crewCliModel);
|
|
3812
3835
|
if (projectDir && projectDir !== process.cwd())
|
|
@@ -3859,12 +3882,16 @@ export function createAndStartServer(PORT) {
|
|
|
3859
3882
|
"-p",
|
|
3860
3883
|
"--setting-sources",
|
|
3861
3884
|
"user",
|
|
3862
|
-
"--dangerously-skip-permissions",
|
|
3863
3885
|
"--output-format",
|
|
3864
3886
|
"stream-json",
|
|
3865
3887
|
"--verbose",
|
|
3866
3888
|
"--include-partial-messages",
|
|
3867
3889
|
];
|
|
3890
|
+
if (reqPermissionMode) {
|
|
3891
|
+
args.push("--permission-mode", reqPermissionMode);
|
|
3892
|
+
} else {
|
|
3893
|
+
args.push("--dangerously-skip-permissions");
|
|
3894
|
+
}
|
|
3868
3895
|
if (projectDir) args.push("--add-dir", projectDir);
|
|
3869
3896
|
if (reqModel) args.push("--model", reqModel);
|
|
3870
3897
|
// Prefer --resume <session-id> for per-project isolation; fall back to --continue (most recent global)
|
|
@@ -3877,7 +3904,10 @@ export function createAndStartServer(PORT) {
|
|
|
3877
3904
|
} else if (continueSession) {
|
|
3878
3905
|
args.push("--continue");
|
|
3879
3906
|
}
|
|
3880
|
-
|
|
3907
|
+
// Skip user MCP servers to avoid 30s+ init hangs
|
|
3908
|
+
args.push("--strict-mcp-config", "--mcp-config", path.join(os.homedir(), ".crewswarm", "config", "empty-mcp.json"));
|
|
3909
|
+
// -- separates flags from prompt (--mcp-config is variadic and eats positional args)
|
|
3910
|
+
args.push("--", finalMessage);
|
|
3881
3911
|
}
|
|
3882
3912
|
|
|
3883
3913
|
send({ type: "start", engine, message: message.slice(0, 80) });
|
|
@@ -3901,9 +3931,41 @@ export function createAndStartServer(PORT) {
|
|
|
3901
3931
|
const spawnCwd =
|
|
3902
3932
|
engine === "claude" && projectDir ? "/tmp" : projectDir;
|
|
3903
3933
|
|
|
3934
|
+
// Merge crewswarm.json env vars into spawn env (crew-cli L2, model overrides, etc.)
|
|
3935
|
+
const spawnEnv = { ...process.env };
|
|
3936
|
+
try {
|
|
3937
|
+
const _sysCfg = loadSwarmConfig();
|
|
3938
|
+
if (_sysCfg?.env && typeof _sysCfg.env === "object") {
|
|
3939
|
+
for (const [k, v] of Object.entries(_sysCfg.env)) {
|
|
3940
|
+
if (!spawnEnv[k] && v != null) spawnEnv[k] = String(v);
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
} catch {}
|
|
3944
|
+
// Strip API keys AFTER config merge so CLI tools use OAuth (free) not API credits
|
|
3945
|
+
if (engine === "claude") {
|
|
3946
|
+
delete spawnEnv.ANTHROPIC_API_KEY;
|
|
3947
|
+
delete spawnEnv.CLAUDECODE;
|
|
3948
|
+
delete spawnEnv.CLAUDE_CODE;
|
|
3949
|
+
delete spawnEnv.CREWSWARM_CLAUDE_CODE;
|
|
3950
|
+
}
|
|
3951
|
+
if (engine === "codex") {
|
|
3952
|
+
delete spawnEnv.OPENAI_API_KEY;
|
|
3953
|
+
}
|
|
3954
|
+
if (engine === "gemini" || engine === "gemini-cli") {
|
|
3955
|
+
delete spawnEnv.GEMINI_API_KEY;
|
|
3956
|
+
delete spawnEnv.GOOGLE_API_KEY;
|
|
3957
|
+
}
|
|
3958
|
+
if (engine === "opencode" || engine === "antigravity") {
|
|
3959
|
+
delete spawnEnv.OPENAI_API_KEY;
|
|
3960
|
+
delete spawnEnv.ANTHROPIC_API_KEY;
|
|
3961
|
+
delete spawnEnv.GEMINI_API_KEY;
|
|
3962
|
+
}
|
|
3963
|
+
// Force L2 planner for crew-cli when requested (enhance-prompt planner path)
|
|
3964
|
+
if (reqForceL2) spawnEnv.CREW_FORCE_L2 = "true";
|
|
3965
|
+
|
|
3904
3966
|
const proc = _spawn(bin, args, {
|
|
3905
3967
|
cwd: spawnCwd,
|
|
3906
|
-
env:
|
|
3968
|
+
env: spawnEnv,
|
|
3907
3969
|
stdio: ["ignore", "pipe", "pipe"],
|
|
3908
3970
|
});
|
|
3909
3971
|
let lineBuffer = "";
|
|
@@ -3916,7 +3978,7 @@ export function createAndStartServer(PORT) {
|
|
|
3916
3978
|
let passthroughGracefulEnd = false;
|
|
3917
3979
|
|
|
3918
3980
|
const passthroughTimeoutMs = Number(
|
|
3919
|
-
process.env.CREWSWARM_PASSTHROUGH_TIMEOUT_MS || "
|
|
3981
|
+
process.env.CREWSWARM_PASSTHROUGH_TIMEOUT_MS || "300000",
|
|
3920
3982
|
);
|
|
3921
3983
|
const passthroughWatchdog = setTimeout(() => {
|
|
3922
3984
|
send({
|
|
@@ -4206,38 +4268,39 @@ export function createAndStartServer(PORT) {
|
|
|
4206
4268
|
|
|
4207
4269
|
// crew-cli special handling: extract JSON from output (skipping logs) and send only response field
|
|
4208
4270
|
if (engine === "crew-cli" && fullOutput.trim()) {
|
|
4271
|
+
let extracted = false;
|
|
4209
4272
|
try {
|
|
4210
|
-
//
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
console.error(
|
|
4224
|
-
`[crew-cli] ✅ Extracted response: ${responseText.slice(0, 80)}...`,
|
|
4225
|
-
);
|
|
4226
|
-
// NOW send the actual response as a chunk
|
|
4227
|
-
send({ type: "chunk", text: responseText });
|
|
4228
|
-
fullOutput = responseText; // Replace fullOutput for notifications
|
|
4229
|
-
} else {
|
|
4230
|
-
console.error(`[crew-cli] ⚠️ No response field in JSON`);
|
|
4231
|
-
send({ type: "chunk", text: fullOutput }); // Send raw output as fallback
|
|
4273
|
+
// Strategy 1: cross-line regex to find JSON envelope
|
|
4274
|
+
const jsonMatch = fullOutput.match(/\{[\s\S]*"kind":\s*"[^"]+\.result"[\s\S]*\}/);
|
|
4275
|
+
if (jsonMatch) {
|
|
4276
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
4277
|
+
if (parsed.response) {
|
|
4278
|
+
const responseText = typeof parsed.response === "string"
|
|
4279
|
+
? parsed.response
|
|
4280
|
+
: JSON.stringify(parsed.response, null, 2);
|
|
4281
|
+
console.log(`[crew-cli] ✅ Extracted response: ${responseText.slice(0, 80)}...`);
|
|
4282
|
+
send({ type: "chunk", text: responseText });
|
|
4283
|
+
fullOutput = responseText;
|
|
4284
|
+
extracted = true;
|
|
4285
|
+
}
|
|
4232
4286
|
}
|
|
4233
|
-
} catch
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4287
|
+
} catch { /* fall through */ }
|
|
4288
|
+
if (!extracted) {
|
|
4289
|
+
// Strategy 2: regex extract just the response field
|
|
4290
|
+
try {
|
|
4291
|
+
const respMatch = fullOutput.match(/"response":\s*"((?:[^"\\]|\\.)*)"/);
|
|
4292
|
+
if (respMatch) {
|
|
4293
|
+
const responseText = respMatch[1].replace(/\\n/g, "\n").replace(/\\"/g, '"').replace(/\\t/g, "\t");
|
|
4294
|
+
console.log(`[crew-cli] ✅ Extracted response (regex): ${responseText.slice(0, 80)}...`);
|
|
4295
|
+
send({ type: "chunk", text: responseText });
|
|
4296
|
+
fullOutput = responseText;
|
|
4297
|
+
extracted = true;
|
|
4298
|
+
}
|
|
4299
|
+
} catch { /* fall through */ }
|
|
4300
|
+
}
|
|
4301
|
+
if (!extracted) {
|
|
4302
|
+
console.error(`[crew-cli] ❌ Failed to extract response from crew-cli output`);
|
|
4303
|
+
console.error(`[crew-cli] First 200 chars: ${fullOutput.slice(0, 200)}`);
|
|
4241
4304
|
send({ type: "chunk", text: fullOutput });
|
|
4242
4305
|
}
|
|
4243
4306
|
}
|
|
@@ -4495,22 +4558,8 @@ export function createAndStartServer(PORT) {
|
|
|
4495
4558
|
);
|
|
4496
4559
|
enabled = cfg.claudeCode === true;
|
|
4497
4560
|
} catch { }
|
|
4498
|
-
const
|
|
4499
|
-
|
|
4500
|
-
(() => {
|
|
4501
|
-
try {
|
|
4502
|
-
return JSON.parse(
|
|
4503
|
-
fs.readFileSync(
|
|
4504
|
-
path.join(os.homedir(), ".crewswarm", "crewswarm.json"),
|
|
4505
|
-
"utf8",
|
|
4506
|
-
),
|
|
4507
|
-
)?.providers?.anthropic?.apiKey;
|
|
4508
|
-
} catch {
|
|
4509
|
-
return null;
|
|
4510
|
-
}
|
|
4511
|
-
})()
|
|
4512
|
-
);
|
|
4513
|
-
json(res, 200, { ok: true, enabled, hasKey });
|
|
4561
|
+
const hasAuth = isClaudeOauthAuthenticated();
|
|
4562
|
+
json(res, 200, { ok: true, enabled, hasKey: hasAuth, hasAuth });
|
|
4514
4563
|
return;
|
|
4515
4564
|
}
|
|
4516
4565
|
if (req.method === "POST") {
|
|
@@ -128,14 +128,11 @@ async function callGeminiCliForChat(messages) {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
export async function _callLLMOnce(baseUrl, apiKey, modelId, providerKey, messages, options = {}) {
|
|
131
|
-
|
|
132
|
-
const isOpenRouter = providerKey === "openrouter" || baseUrl.includes("openrouter.ai");
|
|
133
|
-
if (isOpenRouter && modelId && !modelId.startsWith("openrouter/")) {
|
|
134
|
-
modelId = "openrouter/" + modelId;
|
|
135
|
-
}
|
|
131
|
+
modelId = normalizeExternalModelId(modelId, providerKey, baseUrl);
|
|
136
132
|
const isAnthropic = providerKey === "anthropic" || baseUrl.includes("anthropic.com");
|
|
137
133
|
const enableStreaming = options.stream || false;
|
|
138
134
|
const onStreamToken = options.onStreamToken || null;
|
|
135
|
+
const requestTimeoutMs = Number(options.timeoutMs || _LLM_TIMEOUT) || _LLM_TIMEOUT;
|
|
139
136
|
|
|
140
137
|
const headers = { "content-type": "application/json" };
|
|
141
138
|
if (isAnthropic) {
|
|
@@ -150,9 +147,10 @@ export async function _callLLMOnce(baseUrl, apiKey, modelId, providerKey, messag
|
|
|
150
147
|
const isOpenAI = providerKey === "openai" || baseUrl.includes("openai.com");
|
|
151
148
|
|
|
152
149
|
// Gemini 2.5 Flash: input 1,048,576 tokens / output 65,536 tokens
|
|
153
|
-
// Anthropic Claude
|
|
150
|
+
// Anthropic Claude varies by model; Haiku is stricter at 4,096 output tokens
|
|
154
151
|
// All others: 4,096 safe default
|
|
155
|
-
const
|
|
152
|
+
const anthropicMaxOutputTokens = /haiku/i.test(modelId) ? 4096 : 8192;
|
|
153
|
+
const maxOutputTokens = isGemini ? 16384 : isAnthropic ? anthropicMaxOutputTokens : 4096;
|
|
156
154
|
|
|
157
155
|
let body, endpoint;
|
|
158
156
|
|
|
@@ -230,7 +228,7 @@ export async function _callLLMOnce(baseUrl, apiKey, modelId, providerKey, messag
|
|
|
230
228
|
method: "POST",
|
|
231
229
|
headers,
|
|
232
230
|
body: JSON.stringify(body),
|
|
233
|
-
signal: AbortSignal.timeout(
|
|
231
|
+
signal: AbortSignal.timeout(requestTimeoutMs),
|
|
234
232
|
});
|
|
235
233
|
|
|
236
234
|
if (!res.ok) {
|
|
@@ -321,6 +319,24 @@ export async function _callLLMOnce(baseUrl, apiKey, modelId, providerKey, messag
|
|
|
321
319
|
return reply;
|
|
322
320
|
}
|
|
323
321
|
|
|
322
|
+
export function normalizeExternalModelId(modelId, providerKey, baseUrl = "") {
|
|
323
|
+
let normalized = String(modelId || "").trim();
|
|
324
|
+
if (!normalized) return normalized;
|
|
325
|
+
|
|
326
|
+
const isOpenRouter = providerKey === "openrouter" || baseUrl.includes("openrouter.ai");
|
|
327
|
+
const isPerplexity = providerKey === "perplexity" || baseUrl.includes("api.perplexity.ai");
|
|
328
|
+
|
|
329
|
+
if (isOpenRouter) {
|
|
330
|
+
normalized = normalized.replace(/^openrouter\//i, "");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (isPerplexity) {
|
|
334
|
+
normalized = normalized.replace(/^perplexity\//i, "");
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return normalized;
|
|
338
|
+
}
|
|
339
|
+
|
|
324
340
|
function _recordCrewLeadTokens(modelId, providerKey, usage) {
|
|
325
341
|
if (!usage) return;
|
|
326
342
|
const p = Number(usage.prompt_tokens || usage.input_tokens || 0);
|
|
@@ -41,6 +41,12 @@ export function initPrompts({
|
|
|
41
41
|
let _sysPromptCache = null;
|
|
42
42
|
let _sysPromptKey = "";
|
|
43
43
|
|
|
44
|
+
/** Clear the system prompt memoization cache (for test isolation). */
|
|
45
|
+
export function resetPromptCache() {
|
|
46
|
+
_sysPromptCache = null;
|
|
47
|
+
_sysPromptKey = "";
|
|
48
|
+
}
|
|
49
|
+
|
|
44
50
|
export function buildSystemPrompt(cfg) {
|
|
45
51
|
// Memoize — only rebuild when config files or agent prompts change
|
|
46
52
|
const keyParts = [cfg.providerKey, cfg.modelId, cfg.displayName];
|
|
@@ -137,7 +143,7 @@ export function buildSystemPrompt(cfg) {
|
|
|
137
143
|
"DEFAULT: CHAT. You are a conversational assistant first.",
|
|
138
144
|
"- Questions, explanations, status, clarifications, follow-ups, 'how does X work', 'what is X', 'can you', 'show me' → ANSWER. Never dispatch.",
|
|
139
145
|
"- Short messages under ~8 words ('i mean X', 'no, X', 'about X') → corrections, not directives. ANSWER.",
|
|
140
|
-
"- Greetings ('hi', 'yo', 'hey') → reply
|
|
146
|
+
"- Greetings ('hi', 'yo', 'hey', 'hello', 'sup', 'what's up') → reply with 1-2 SHORT sentences. Do NOT report system status, agent counts, RT bus, skills, or health data. Just be conversational. Only report health/status when the user EXPLICITLY asks ('status', 'health', 'how is the system', 'what's running').",
|
|
141
147
|
"- Self-audit requests (find bugs, review, inspect) → DO IT YOURSELF with @@READ_FILE / @@RUN_CMD. No asking permission. Only dispatch to crew-qa/crew-coder if user explicitly says 'have X do it'.",
|
|
142
148
|
"",
|
|
143
149
|
"DISPATCH: when user gives explicit action language.",
|
|
@@ -150,6 +156,13 @@ export function buildSystemPrompt(cfg) {
|
|
|
150
156
|
`- With acceptance criteria: @@DISPATCH {"agent":"crew-coder","task":"Write JWT auth middleware","verify":"@@READ_FILE src/auth.ts","done":"exports verifyToken, returns 401 on invalid"}`,
|
|
151
157
|
"- You MUST emit the @@DISPATCH line. Describing a dispatch in prose without the line = NOTHING happens.",
|
|
152
158
|
"",
|
|
159
|
+
"ENGINE ROUTING: tasks are routed to CLI engines or direct-llm based on keyword matching.",
|
|
160
|
+
"- Tasks with coding keywords (create, build, implement, fix, refactor, add, update, modify, code, function, class, component, api, endpoint, route, test, bug, error, issue, file, script, module, package) → CLI engine (native file I/O, shell, full repo context).",
|
|
161
|
+
"- Tasks WITHOUT coding keywords → direct-llm (LLM API call; can still write files via @@WRITE_FILE markers, but no shell or repo context).",
|
|
162
|
+
"- CLI engines are better for complex multi-file coding. direct-llm is fine for research reports, single-file writes, and simple tasks.",
|
|
163
|
+
"- When dispatching build tasks, use verbs like 'Create', 'Build', 'Implement', 'Fix' to ensure CLI routing.",
|
|
164
|
+
"- This is automatic keyword matching — no LLM call. See docs/ORCHESTRATION-PROTOCOL.md for the full keyword list.",
|
|
165
|
+
"",
|
|
153
166
|
"PIPELINE: when user wants multi-agent coordinated work.",
|
|
154
167
|
"- Triggers: 'build me X', 'create X', 'kick off', 'rally the crew', 'dispatch the crew'.",
|
|
155
168
|
"- Before firing for complex tasks (3+ agents / 2+ waves): show plan in 1-3 lines, ask 'Fire it?'. Skip confirmation for single-agent or explicit 'go build'.",
|
package/lib/crew-lead/tools.mjs
CHANGED
|
@@ -154,10 +154,11 @@ export async function execCrewLeadTools(reply) {
|
|
|
154
154
|
} catch { return null; }
|
|
155
155
|
})();
|
|
156
156
|
|
|
157
|
-
// Fallback: check search-tools.json
|
|
157
|
+
// Fallback: check search-tools.json (same dir as config file)
|
|
158
158
|
if (!braveKey) {
|
|
159
159
|
try {
|
|
160
|
-
const
|
|
160
|
+
const cfgDir = _crewswarmCfgFile ? path.dirname(_crewswarmCfgFile) : path.join(os.homedir(), ".crewswarm");
|
|
161
|
+
const searchTools = JSON.parse(fs.readFileSync(path.join(cfgDir, "search-tools.json"), "utf8"));
|
|
161
162
|
braveKey = searchTools.brave?.apiKey;
|
|
162
163
|
} catch {}
|
|
163
164
|
}
|
|
@@ -377,7 +377,17 @@ export function dispatchPipelineWave(pipelineId) {
|
|
|
377
377
|
// this path: when Cursor CLI fails, orchestrator falls back to plain LLM text
|
|
378
378
|
// with @mentions — subagents never run and the pipeline advances on garbage.
|
|
379
379
|
// Direct per-agent dispatch is reliable for project pipelines.
|
|
380
|
-
if
|
|
380
|
+
// Only route through orchestrator if it has a CLI engine that can actually dispatch.
|
|
381
|
+
// Without a CLI engine, the orchestrator just describes dispatching in text — useless.
|
|
382
|
+
const orchestratorHasCliEngine = (() => {
|
|
383
|
+
try {
|
|
384
|
+
const agents = (_deps.loadAgentList || (() => []))();
|
|
385
|
+
const orch = agents.find(a => a.id === "crew-orchestrator");
|
|
386
|
+
if (!orch) return false;
|
|
387
|
+
return !!(orch.engine || orch.useClaudeCode || orch.useCursorCli || orch.useCursor || orch.useCodex || orch.useCrewCLI || orch.useGeminiCli || orch.useOpenCode);
|
|
388
|
+
} catch { return false; }
|
|
389
|
+
})();
|
|
390
|
+
if (_deps._cursorWavesEnabled && orchestratorHasCliEngine && waveSteps.length > 1 && !pipeline.projectDir) {
|
|
381
391
|
const waveManifest = {
|
|
382
392
|
wave: currentWave + 1,
|
|
383
393
|
projectDir: pipeline.projectDir || "",
|
|
@@ -525,10 +535,12 @@ export function checkWaveQualityGate(pipeline, pipelineId) {
|
|
|
525
535
|
const hasWriteFile = /@@WRITE_FILE/.test(result);
|
|
526
536
|
const wroteFile = /(?:wrote|created|saved|written|updated|enhanced|implemented|added)\s+(?:to\s+)?(?:\/\S+|[a-zA-Z][\w/.-]+\.(?:html|css|js|ts|py|json|md|txt|sh|yaml|yml))/i.test(result);
|
|
527
537
|
const opencodeWrote = /(?:←\s*Write|Wrote file|Write\s+\.\.[\w/.-]+\.(?:html|css|js|ts|py|json|md)|Created\s+`\/)/i.test(result);
|
|
538
|
+
// Cursor outputs "`filename` is in place at:" or "Created the file at:\n`/path`"
|
|
539
|
+
const cursorInPlace = /`[\w/.-]+`\s+is\s+in\s+place\s+at|Created\s+the\s+file\s+at:/i.test(result);
|
|
528
540
|
const explicitDone = /Done\.\s+(?:Created|Updated|Enhanced|Implemented|The\s+(?:file|prototype|component))/i.test(result);
|
|
529
541
|
// OpenCode agents write silently — check if any src files changed in last 20 min as fallback
|
|
530
542
|
let opencodeFilesChanged = false;
|
|
531
|
-
if (!hasWriteFile && !wroteFile && !opencodeWrote && !explicitDone) {
|
|
543
|
+
if (!hasWriteFile && !wroteFile && !opencodeWrote && !cursorInPlace && !explicitDone) {
|
|
532
544
|
try {
|
|
533
545
|
const cutoff = Date.now() - 20 * 60 * 1000;
|
|
534
546
|
// Use dynamic project directory from pipeline metadata or environment
|
|
@@ -555,7 +567,7 @@ export function checkWaveQualityGate(pipeline, pipelineId) {
|
|
|
555
567
|
opencodeFilesChanged = dirs.some(checkDir);
|
|
556
568
|
} catch {}
|
|
557
569
|
}
|
|
558
|
-
if (!hasWriteFile && !wroteFile && !opencodeWrote && !explicitDone && !opencodeFilesChanged) {
|
|
570
|
+
if (!hasWriteFile && !wroteFile && !opencodeWrote && !cursorInPlace && !explicitDone && !opencodeFilesChanged) {
|
|
559
571
|
issues.push(`${agent} (builder) did not write any files`);
|
|
560
572
|
}
|
|
561
573
|
}
|
|
@@ -702,8 +714,10 @@ export function dispatchTask(agent, task, sessionId = "owner", pipelineMeta = nu
|
|
|
702
714
|
task = _deps.writeTaskBrief?.(agent, task, projectDir) ?? task;
|
|
703
715
|
}
|
|
704
716
|
|
|
705
|
-
|
|
706
|
-
|
|
717
|
+
let rp = typeof _deps.getRtPublish === "function" ? _deps.getRtPublish() : null;
|
|
718
|
+
if (!rp && typeof _deps.getRtPublish === "function") {
|
|
719
|
+
console.warn(`[wave-dispatcher] RT publish not available for ${agent} — will use fallback`);
|
|
720
|
+
}
|
|
707
721
|
if (rp) {
|
|
708
722
|
try {
|
|
709
723
|
// Build extraFlags: start with global settings, override with pipeline-specific settings
|