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
|
@@ -1,870 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Real-time envelope handler — processes incoming RT bus commands and tasks.
|
|
3
|
-
* All dependencies are injected via initRtEnvelope(deps).
|
|
4
|
-
*/
|
|
5
|
-
import crypto from "node:crypto";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import os from "node:os";
|
|
8
|
-
import fs from "node:fs";
|
|
9
|
-
import { recordTaskMemory, isSharedMemoryAvailable } from "../memory/shared-adapter.mjs";
|
|
10
|
-
import { selectEngine } from "./runners.mjs";
|
|
11
|
-
import { saveProjectMessage } from "../chat/project-messages.mjs";
|
|
12
|
-
|
|
13
|
-
let _deps = {};
|
|
14
|
-
|
|
15
|
-
export function initRtEnvelope(deps) {
|
|
16
|
-
_deps = deps;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function handleRealtimeEnvelope(envelope, client, bridge) {
|
|
20
|
-
const payload = envelope?.payload && typeof envelope.payload === "object" ? envelope.payload : {};
|
|
21
|
-
const taskId = envelope?.taskId || "";
|
|
22
|
-
const incomingType = envelope?.type || "event";
|
|
23
|
-
const from = envelope?.from || "unknown";
|
|
24
|
-
const to = envelope?.to || "broadcast";
|
|
25
|
-
const correlationId = envelope?.id || undefined;
|
|
26
|
-
|
|
27
|
-
const {
|
|
28
|
-
CREWSWARM_RT_AGENT,
|
|
29
|
-
CREWSWARM_RT_COMMAND_TYPES,
|
|
30
|
-
pendingCmdApprovals,
|
|
31
|
-
resolveSpawnTargets,
|
|
32
|
-
spawnAgentDaemon,
|
|
33
|
-
isAgentDaemonRunning,
|
|
34
|
-
readPid,
|
|
35
|
-
dispatchKeyForTask,
|
|
36
|
-
shouldUseDispatchGuard,
|
|
37
|
-
acquireTaskLease,
|
|
38
|
-
renewTaskLease,
|
|
39
|
-
releaseTaskLease,
|
|
40
|
-
markTaskDone,
|
|
41
|
-
telemetry,
|
|
42
|
-
buildTaskPrompt,
|
|
43
|
-
getOpencodeProjectDir,
|
|
44
|
-
assertTaskPromptProtocol,
|
|
45
|
-
selectEngine,
|
|
46
|
-
runGenericEngineTask,
|
|
47
|
-
loadGenericEngines,
|
|
48
|
-
progress,
|
|
49
|
-
getAgentOpenCodeConfig,
|
|
50
|
-
buildMiniTaskForOpenCode,
|
|
51
|
-
runOuroborosStyleLoop,
|
|
52
|
-
runCursorCliTask,
|
|
53
|
-
runClaudeCodeTask,
|
|
54
|
-
runCodexTask,
|
|
55
|
-
runDockerSandboxTask,
|
|
56
|
-
runGeminiCliTask,
|
|
57
|
-
runCrewCLITask,
|
|
58
|
-
runOpenCodeTask,
|
|
59
|
-
callLLMDirect,
|
|
60
|
-
extractProjectDirFromTask,
|
|
61
|
-
loadAgentPrompts,
|
|
62
|
-
stripThink,
|
|
63
|
-
executeToolCalls,
|
|
64
|
-
validateCodingArtifacts,
|
|
65
|
-
isCodingTask,
|
|
66
|
-
shouldRetryTaskFailure,
|
|
67
|
-
CREWSWARM_RT_DISPATCH_LEASE_MS,
|
|
68
|
-
CREWSWARM_RT_DISPATCH_HEARTBEAT_MS,
|
|
69
|
-
CREWSWARM_RT_DISPATCH_MAX_RETRIES,
|
|
70
|
-
CREWSWARM_RT_DISPATCH_MAX_RETRIES_CODING,
|
|
71
|
-
CREWSWARM_RT_DISPATCH_RETRY_BACKOFF_MS,
|
|
72
|
-
CREWSWARM_OPENCODE_AGENT,
|
|
73
|
-
CREWSWARM_OPENCODE_MODEL,
|
|
74
|
-
OPENCODE_FREE_MODEL_CHAIN,
|
|
75
|
-
RT_TO_GATEWAY_AGENT_MAP,
|
|
76
|
-
SHARED_MEMORY_DIR,
|
|
77
|
-
SWARM_DLQ_DIR,
|
|
78
|
-
COORDINATOR_AGENT_IDS,
|
|
79
|
-
} = _deps;
|
|
80
|
-
|
|
81
|
-
// Per-agent routing: skip tasks not addressed to us (unless broadcast)
|
|
82
|
-
if (to !== "broadcast" && to !== CREWSWARM_RT_AGENT) {
|
|
83
|
-
client.ack({ messageId: envelope.id, status: "skipped", note: `not for us (to=${to}, we=${CREWSWARM_RT_AGENT})` });
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (!CREWSWARM_RT_COMMAND_TYPES.has(incomingType)) {
|
|
88
|
-
client.ack({ messageId: envelope.id, status: "skipped", note: `unsupported type ${incomingType}` });
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// ── cmd approval resolution (from crew-lead via RT bus) ───────────────────
|
|
93
|
-
if (incomingType === "cmd.approved" || incomingType === "cmd.rejected") {
|
|
94
|
-
const approvalId = payload?.approvalId;
|
|
95
|
-
if (approvalId && pendingCmdApprovals.has(approvalId)) {
|
|
96
|
-
const pending = pendingCmdApprovals.get(approvalId);
|
|
97
|
-
clearTimeout(pending.timer);
|
|
98
|
-
pendingCmdApprovals.delete(approvalId);
|
|
99
|
-
pending.resolve(incomingType === "cmd.approved");
|
|
100
|
-
console.log(`[${CREWSWARM_RT_AGENT}] cmd ${incomingType === "cmd.approved" ? "✅ approved" : "⛔ rejected"}: ${approvalId}`);
|
|
101
|
-
}
|
|
102
|
-
try { client.ack({ messageId: envelope.id, status: "done", note: `cmd ${incomingType}` }); } catch {}
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const action = String(payload.action || payload.command || "run_task").trim().toLowerCase();
|
|
107
|
-
if (incomingType === "command.spawn_agent") {
|
|
108
|
-
const targets = resolveSpawnTargets(payload);
|
|
109
|
-
const results = targets.map((agent) => spawnAgentDaemon(agent));
|
|
110
|
-
client.publish({
|
|
111
|
-
channel: "done",
|
|
112
|
-
type: "task.done",
|
|
113
|
-
to: from,
|
|
114
|
-
taskId,
|
|
115
|
-
correlationId,
|
|
116
|
-
priority: "high",
|
|
117
|
-
payload: {
|
|
118
|
-
source: CREWSWARM_RT_AGENT,
|
|
119
|
-
incomingType,
|
|
120
|
-
action: "spawn_agent",
|
|
121
|
-
results,
|
|
122
|
-
},
|
|
123
|
-
});
|
|
124
|
-
client.ack({ messageId: envelope.id, status: "done", note: `spawned ${results.length} agent(s)` });
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (incomingType === "command.collect_status") {
|
|
129
|
-
const targets = resolveSpawnTargets(payload);
|
|
130
|
-
const status = targets.map((agent) => ({ agent, running: isAgentDaemonRunning(agent), pid: readPid(agent) || null }));
|
|
131
|
-
client.publish({
|
|
132
|
-
channel: "done",
|
|
133
|
-
type: "task.done",
|
|
134
|
-
to: from,
|
|
135
|
-
taskId,
|
|
136
|
-
correlationId,
|
|
137
|
-
priority: "medium",
|
|
138
|
-
payload: {
|
|
139
|
-
source: CREWSWARM_RT_AGENT,
|
|
140
|
-
incomingType,
|
|
141
|
-
action: "collect_status",
|
|
142
|
-
status,
|
|
143
|
-
},
|
|
144
|
-
});
|
|
145
|
-
client.ack({ messageId: envelope.id, status: "done", note: `status for ${status.length} agent(s)` });
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (incomingType.startsWith("command.") && action !== "run_task" && action !== "collect_status") {
|
|
150
|
-
client.publish({
|
|
151
|
-
channel: "issues",
|
|
152
|
-
type: "command.unsupported",
|
|
153
|
-
to: from,
|
|
154
|
-
taskId,
|
|
155
|
-
correlationId,
|
|
156
|
-
priority: "medium",
|
|
157
|
-
payload: {
|
|
158
|
-
source: CREWSWARM_RT_AGENT,
|
|
159
|
-
action,
|
|
160
|
-
note: "Legacy bridge supports run_task and collect_status command actions",
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
client.ack({ messageId: envelope.id, status: "failed", note: `unsupported action ${action}` });
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const prompt = payload.prompt || payload.message || payload.description || [payload.title, payload.description].filter(Boolean).join("\n\n");
|
|
168
|
-
if (!prompt || typeof prompt !== "string") {
|
|
169
|
-
client.ack({ messageId: envelope.id, status: "failed", note: "missing prompt/message" });
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const dispatchAttempt = Number(payload?._dispatchAttempt || 0);
|
|
174
|
-
const dispatchKey = dispatchKeyForTask({
|
|
175
|
-
taskId,
|
|
176
|
-
incomingType,
|
|
177
|
-
prompt,
|
|
178
|
-
idempotencyKey: payload?._dispatchIdempotencyKey || payload?.idempotencyKey,
|
|
179
|
-
});
|
|
180
|
-
const dispatchGuardEnabled = shouldUseDispatchGuard(incomingType);
|
|
181
|
-
let dispatchClaim = null;
|
|
182
|
-
let dispatchHeartbeat = null;
|
|
183
|
-
|
|
184
|
-
if (dispatchGuardEnabled) {
|
|
185
|
-
try {
|
|
186
|
-
dispatchClaim = acquireTaskLease({
|
|
187
|
-
key: dispatchKey,
|
|
188
|
-
source: incomingType,
|
|
189
|
-
incomingType,
|
|
190
|
-
from,
|
|
191
|
-
leaseMs: CREWSWARM_RT_DISPATCH_LEASE_MS,
|
|
192
|
-
});
|
|
193
|
-
} catch (err) {
|
|
194
|
-
telemetry("dispatch_claim_error", {
|
|
195
|
-
key: dispatchKey,
|
|
196
|
-
taskId,
|
|
197
|
-
incomingType,
|
|
198
|
-
error: err?.message ?? String(err),
|
|
199
|
-
});
|
|
200
|
-
client.ack({ messageId: envelope.id, status: "failed", note: "dispatch claim error" });
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (!dispatchClaim?.acquired) {
|
|
205
|
-
const reason = dispatchClaim?.reason || "claimed";
|
|
206
|
-
telemetry("dispatch_claim_skipped", {
|
|
207
|
-
key: dispatchKey,
|
|
208
|
-
taskId,
|
|
209
|
-
incomingType,
|
|
210
|
-
reason,
|
|
211
|
-
claimedBy: dispatchClaim?.claimedBy || null,
|
|
212
|
-
});
|
|
213
|
-
const note = reason === "already_done"
|
|
214
|
-
? "duplicate task already completed"
|
|
215
|
-
: `task claimed by ${dispatchClaim?.claimedBy || "another agent"}`;
|
|
216
|
-
if (reason === "already_done" && dispatchClaim?.doneRecord?.reply) {
|
|
217
|
-
client.publish({
|
|
218
|
-
channel: "done",
|
|
219
|
-
type: "task.done",
|
|
220
|
-
to: from,
|
|
221
|
-
taskId,
|
|
222
|
-
correlationId,
|
|
223
|
-
priority: "medium",
|
|
224
|
-
payload: {
|
|
225
|
-
source: CREWSWARM_RT_AGENT,
|
|
226
|
-
incomingType,
|
|
227
|
-
reply: dispatchClaim.doneRecord.reply,
|
|
228
|
-
duplicate: true,
|
|
229
|
-
idempotencyKey: dispatchKey,
|
|
230
|
-
completedBy: dispatchClaim.doneRecord.agent || null,
|
|
231
|
-
completedAt: dispatchClaim.doneRecord.doneAt || null,
|
|
232
|
-
},
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
client.ack({ messageId: envelope.id, status: "skipped", note });
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
dispatchHeartbeat = setInterval(() => {
|
|
240
|
-
const renewed = renewTaskLease({
|
|
241
|
-
key: dispatchKey,
|
|
242
|
-
claimId: dispatchClaim.claimId,
|
|
243
|
-
leaseMs: CREWSWARM_RT_DISPATCH_LEASE_MS,
|
|
244
|
-
});
|
|
245
|
-
if (!renewed) {
|
|
246
|
-
telemetry("dispatch_lease_lost", {
|
|
247
|
-
key: dispatchKey,
|
|
248
|
-
taskId,
|
|
249
|
-
incomingType,
|
|
250
|
-
claimId: dispatchClaim?.claimId,
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
}, CREWSWARM_RT_DISPATCH_HEARTBEAT_MS);
|
|
254
|
-
|
|
255
|
-
telemetry("dispatch_claim_acquired", {
|
|
256
|
-
key: dispatchKey,
|
|
257
|
-
taskId,
|
|
258
|
-
incomingType,
|
|
259
|
-
claimId: dispatchClaim.claimId,
|
|
260
|
-
attempt: dispatchAttempt,
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
client.ack({ messageId: envelope.id, status: "received", note: `crewswarm accepted ${incomingType}` });
|
|
265
|
-
client.publish({
|
|
266
|
-
channel: "status",
|
|
267
|
-
type: "task.in_progress",
|
|
268
|
-
to: from,
|
|
269
|
-
taskId,
|
|
270
|
-
correlationId,
|
|
271
|
-
priority: "high",
|
|
272
|
-
payload: {
|
|
273
|
-
source: CREWSWARM_RT_AGENT,
|
|
274
|
-
note: `Processing ${incomingType}`,
|
|
275
|
-
action,
|
|
276
|
-
idempotencyKey: dispatchKey,
|
|
277
|
-
attempt: dispatchAttempt,
|
|
278
|
-
},
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
try {
|
|
282
|
-
const taskProjectDir = payload?.projectDir || getOpencodeProjectDir() || null;
|
|
283
|
-
const { finalPrompt, sharedMemory } = buildTaskPrompt(prompt, `Realtime task from ${from} (${incomingType})`, CREWSWARM_RT_AGENT, { projectDir: taskProjectDir });
|
|
284
|
-
if (sharedMemory.loadFailed || finalPrompt === "MEMORY_LOAD_FAILED") {
|
|
285
|
-
throw new Error("MEMORY_LOAD_FAILED");
|
|
286
|
-
}
|
|
287
|
-
assertTaskPromptProtocol(finalPrompt, "realtime");
|
|
288
|
-
|
|
289
|
-
// ── Dynamic Engine Selection (registry-based) ──────────────────────────────
|
|
290
|
-
const selectedEngine = selectEngine(payload, incomingType);
|
|
291
|
-
|
|
292
|
-
if (selectedEngine) {
|
|
293
|
-
progress(`Routing to ${selectedEngine.label || selectedEngine.id} (priority ${selectedEngine.priority})...`);
|
|
294
|
-
telemetry(`realtime_route_${selectedEngine.id}`, { taskId, incomingType, from, agent: CREWSWARM_RT_AGENT });
|
|
295
|
-
} else {
|
|
296
|
-
// Fall back to OpenCode or direct LLM if no engine matches
|
|
297
|
-
progress(`No engine matched — falling back to OpenCode or direct LLM`);
|
|
298
|
-
telemetry("realtime_route_fallback", { taskId, incomingType, from, agent: CREWSWARM_RT_AGENT });
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// Emit working indicator for ALL tasks
|
|
302
|
-
client?.publish({ channel: "events", type: "agent_working", to: "broadcast", payload: { agent: CREWSWARM_RT_AGENT, ts: Date.now() } });
|
|
303
|
-
|
|
304
|
-
let reply;
|
|
305
|
-
let ocAgentId = null;
|
|
306
|
-
let agentSysPrompt = null;
|
|
307
|
-
let projectDir = taskProjectDir || null;
|
|
308
|
-
let engineUsed = selectedEngine?.id || null;
|
|
309
|
-
let modelUsed = null;
|
|
310
|
-
|
|
311
|
-
// Token budget tracking (progressive disclosure pattern)
|
|
312
|
-
const estimateTokens = (text) => Math.ceil((text || '').length / 4);
|
|
313
|
-
const contextTokens = estimateTokens(finalPrompt);
|
|
314
|
-
const maxContextTokens = 100000; // Conservative default, can be model-specific
|
|
315
|
-
const tokenBudgetWarning = contextTokens > (maxContextTokens * 0.7)
|
|
316
|
-
? `\n\n⏰ **Context Budget:** ${contextTokens.toLocaleString()} / ${maxContextTokens.toLocaleString()} tokens used (${Math.round(contextTokens/maxContextTokens*100)}%). Prioritize completing current work before adding more context.`
|
|
317
|
-
: '';
|
|
318
|
-
|
|
319
|
-
// Inject warning into prompt if near limit
|
|
320
|
-
let finalPromptWithBudget = finalPrompt;
|
|
321
|
-
if (tokenBudgetWarning) {
|
|
322
|
-
finalPromptWithBudget = finalPrompt + tokenBudgetWarning;
|
|
323
|
-
telemetry("token_budget_warning", { taskId, contextTokens, maxContextTokens, utilization: contextTokens/maxContextTokens });
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Adaptive reasoning budget (LangChain pattern: xhigh-high-xhigh sandwich)
|
|
327
|
-
const isPlanning = /plan|design|architect|scope|roadmap|strategy/i.test(prompt);
|
|
328
|
-
const isVerification = /verify|test|check|validate|review|audit/i.test(prompt);
|
|
329
|
-
const reasoningBudget = isPlanning ? 'xhigh' : isVerification ? 'xhigh' : 'high';
|
|
330
|
-
|
|
331
|
-
const adaptivePayload = {
|
|
332
|
-
...payload,
|
|
333
|
-
reasoningBudget,
|
|
334
|
-
contextTokens,
|
|
335
|
-
tokenBudgetWarning: !!tokenBudgetWarning
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
telemetry("task_start", {
|
|
339
|
-
taskId,
|
|
340
|
-
incomingType,
|
|
341
|
-
contextTokens,
|
|
342
|
-
reasoningBudget,
|
|
343
|
-
isPlanning,
|
|
344
|
-
isVerification
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
// ── Execute via selected engine ─────────────────────────────────────────────
|
|
348
|
-
if (selectedEngine && selectedEngine.run) {
|
|
349
|
-
projectDir = payload?.projectDir || getOpencodeProjectDir() || process.cwd();
|
|
350
|
-
projectDir = String(projectDir).replace(/[.,;!?]+$/, "");
|
|
351
|
-
const enginePrompt = await buildMiniTaskForOpenCode(prompt, CREWSWARM_RT_AGENT, projectDir);
|
|
352
|
-
const agentCfg = getAgentOpenCodeConfig(CREWSWARM_RT_AGENT);
|
|
353
|
-
modelUsed = payload?.model || agentCfg.model || 'unknown';
|
|
354
|
-
|
|
355
|
-
try {
|
|
356
|
-
// Check for ouroboros-style loop mode
|
|
357
|
-
if (agentCfg.loop && (selectedEngine.id === 'cursor' || selectedEngine.id === 'claude-code')) {
|
|
358
|
-
progress(`${selectedEngine.label} loop mode: LLM ↔ Engine until DONE…`);
|
|
359
|
-
try {
|
|
360
|
-
reply = await runOuroborosStyleLoop(prompt, CREWSWARM_RT_AGENT, projectDir, payload, progress, selectedEngine.id);
|
|
361
|
-
} catch (e) {
|
|
362
|
-
progress(`${selectedEngine.label} loop failed: ${e?.message?.slice(0, 80)} — falling back to single shot`);
|
|
363
|
-
reply = await selectedEngine.run(enginePrompt, { ...payload, agentId: CREWSWARM_RT_AGENT, projectDir });
|
|
364
|
-
}
|
|
365
|
-
} else {
|
|
366
|
-
reply = await selectedEngine.run(enginePrompt, { ...payload, agentId: CREWSWARM_RT_AGENT, projectDir });
|
|
367
|
-
}
|
|
368
|
-
} catch (e) {
|
|
369
|
-
const msg = e?.message ?? String(e);
|
|
370
|
-
const isUsageLimit = /usage.*limit|hit.*limit|quota.*exceeded|limit.*reset/i.test(msg);
|
|
371
|
-
if (isUsageLimit) {
|
|
372
|
-
progress(`${selectedEngine.label} usage limit hit: ${msg.slice(0, 120)}`);
|
|
373
|
-
telemetry(`${selectedEngine.id}_usage_limit`, { taskId, error: msg });
|
|
374
|
-
reply = `❌ ${selectedEngine.label} usage limit reached:\n\n${msg}\n\n(Fallback disabled to show you the error)`;
|
|
375
|
-
} else {
|
|
376
|
-
progress(`${selectedEngine.label} failed: ${msg.slice(0, 120)} — falling back to direct LLM`);
|
|
377
|
-
telemetry(`${selectedEngine.id}_fallback`, { taskId, error: msg });
|
|
378
|
-
reply = await callLLMDirect(finalPrompt, CREWSWARM_RT_AGENT, null);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
} else {
|
|
382
|
-
// No engine matched — fall back to OpenCode or direct LLM
|
|
383
|
-
engineUsed = "opencode";
|
|
384
|
-
projectDir = payload?.projectDir || getOpencodeProjectDir() || null;
|
|
385
|
-
if (!projectDir || projectDir === process.cwd()) {
|
|
386
|
-
const fromTask = extractProjectDirFromTask(prompt);
|
|
387
|
-
if (fromTask) projectDir = fromTask;
|
|
388
|
-
}
|
|
389
|
-
projectDir = projectDir || process.cwd();
|
|
390
|
-
const ocAgentCfg = getAgentOpenCodeConfig(CREWSWARM_RT_AGENT);
|
|
391
|
-
modelUsed = payload?.model || ocAgentCfg.model || CREWSWARM_OPENCODE_MODEL;
|
|
392
|
-
let opencodeErr;
|
|
393
|
-
|
|
394
|
-
if (ocAgentCfg.loop) {
|
|
395
|
-
// Ouroboros-style: LLM decomposes → OpenCode executes each step → repeat until DONE
|
|
396
|
-
progress("OpenCode loop mode: LLM ↔ OpenCode until DONE...");
|
|
397
|
-
try {
|
|
398
|
-
reply = await runOuroborosStyleLoop(prompt, CREWSWARM_RT_AGENT, projectDir, payload, progress, "opencode");
|
|
399
|
-
} catch (e) {
|
|
400
|
-
opencodeErr = e;
|
|
401
|
-
progress(`OpenCode loop failed: ${e?.message?.slice(0, 80)} — falling back to single shot`);
|
|
402
|
-
const ocPrompt = await buildMiniTaskForOpenCode(prompt, CREWSWARM_RT_AGENT, projectDir);
|
|
403
|
-
reply = await runOpenCodeTask(ocPrompt, payload);
|
|
404
|
-
}
|
|
405
|
-
} else {
|
|
406
|
-
// Single-shot: mini task only (now includes shared memory context)
|
|
407
|
-
const ocPrompt = await buildMiniTaskForOpenCode(prompt, CREWSWARM_RT_AGENT, projectDir);
|
|
408
|
-
try {
|
|
409
|
-
reply = await runOpenCodeTask(ocPrompt, payload);
|
|
410
|
-
} catch (e) {
|
|
411
|
-
opencodeErr = e;
|
|
412
|
-
const msg = e?.message ?? String(e);
|
|
413
|
-
const isRateLimit = /429|rate\s*limit|usage.*limit|quota.*exceeded|too\s*many\s*requests|banner-only/i.test(msg);
|
|
414
|
-
const isTimeout = /timeout|timed\s*out|stall/i.test(msg);
|
|
415
|
-
if (isRateLimit || isTimeout) {
|
|
416
|
-
// Build rotation chain: free models first, then configured fallback, deduplicated
|
|
417
|
-
// Track ALL tried models (primary + each fallback attempt) to avoid re-trying failed ones
|
|
418
|
-
const primaryModel = String(payload?.model || CREWSWARM_OPENCODE_MODEL);
|
|
419
|
-
const configFallback = getAgentOpenCodeConfig(CREWSWARM_RT_AGENT).fallbackModel;
|
|
420
|
-
const triedModels = new Set([primaryModel]);
|
|
421
|
-
// Per-agent opencodeFallbackModel goes FIRST, then global free chain as safety net
|
|
422
|
-
const chain = [...(configFallback ? [configFallback] : []), ...OPENCODE_FREE_MODEL_CHAIN]
|
|
423
|
-
.filter((m, i, arr) => m !== primaryModel && arr.indexOf(m) === i);
|
|
424
|
-
for (const fbModel of chain) {
|
|
425
|
-
if (triedModels.has(fbModel)) continue; // skip already-tried models
|
|
426
|
-
triedModels.add(fbModel);
|
|
427
|
-
const reason = isTimeout ? "timed out" : "rate limited";
|
|
428
|
-
progress(`OpenCode ${primaryModel} ${reason} — rotating to ${fbModel}`);
|
|
429
|
-
telemetry("realtime_opencode_fallback", { taskId, incomingType, error: msg, fallbackModel: fbModel });
|
|
430
|
-
try {
|
|
431
|
-
reply = await runOpenCodeTask(ocPrompt, { ...payload, model: fbModel });
|
|
432
|
-
if (reply) break;
|
|
433
|
-
} catch (fbErr) {
|
|
434
|
-
opencodeErr = fbErr;
|
|
435
|
-
const fbMsg = fbErr?.message ?? String(fbErr);
|
|
436
|
-
const fbRateLimit = /429|rate\s*limit|usage.*limit|quota.*exceeded|banner-only|stall/i.test(fbMsg);
|
|
437
|
-
if (!fbRateLimit) break; // non-rate-limit/stall error — stop rotating
|
|
438
|
-
// rate-limited/stalled on this fallback too — continue to next in chain
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
if (!reply && bridge?.kind === "gateway") {
|
|
443
|
-
telemetry("realtime_opencode_fallback", { taskId, incomingType, error: opencodeErr?.message || msg });
|
|
444
|
-
progress(`OpenCode failed, falling back to legacy gateway: ${(opencodeErr?.message || msg).slice(0, 120)}`);
|
|
445
|
-
const gatewayAgentId = RT_TO_GATEWAY_AGENT_MAP[CREWSWARM_RT_AGENT] || "main";
|
|
446
|
-
reply = await bridge.chat(finalPrompt, gatewayAgentId, { idempotencyKey: dispatchKey });
|
|
447
|
-
} else if (!reply) {
|
|
448
|
-
// Try direct LLM call (uses agent's configured model/provider from crewswarm.json)
|
|
449
|
-
engineUsed = "direct-llm";
|
|
450
|
-
const ocAgentId = RT_TO_GATEWAY_AGENT_MAP[CREWSWARM_RT_AGENT] || "main";
|
|
451
|
-
const agentSysPrompt = loadAgentPrompts()[ocAgentId] || null;
|
|
452
|
-
const agentCfg = getAgentOpenCodeConfig(CREWSWARM_RT_AGENT);
|
|
453
|
-
modelUsed = agentCfg?.model || 'unknown';
|
|
454
|
-
progress(`Trying direct LLM for ${CREWSWARM_RT_AGENT} (mapped: ${ocAgentId})...`);
|
|
455
|
-
reply = await callLLMDirect(finalPrompt, ocAgentId, agentSysPrompt);
|
|
456
|
-
|
|
457
|
-
if (!reply) {
|
|
458
|
-
// Fall through to legacy gateway (uses its default model)
|
|
459
|
-
progress(`No direct LLM config for ${ocAgentId}, falling back to legacy gateway...`);
|
|
460
|
-
telemetry("realtime_direct_llm_fallback", { taskId, ocAgentId, incomingType });
|
|
461
|
-
assertTaskPromptProtocol(finalPrompt, "realtime-gateway-chat");
|
|
462
|
-
reply = await bridge.chat(finalPrompt, ocAgentId, { idempotencyKey: dispatchKey });
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
if (!reply || reply === "(timeout - no reply)") {
|
|
469
|
-
throw new Error("Chat timeout while processing realtime task");
|
|
470
|
-
}
|
|
471
|
-
reply = stripThink(reply);
|
|
472
|
-
|
|
473
|
-
// Execute any tool calls — suppress @@WRITE_FILE if searches are pending in the same reply
|
|
474
|
-
const toolResults = await executeToolCalls(reply, CREWSWARM_RT_AGENT, { suppressWriteIfSearchPending: true });
|
|
475
|
-
if (toolResults.length > 0) {
|
|
476
|
-
reply = reply + "\n\n---\n**Tool execution results:**\n" + toolResults.join("\n");
|
|
477
|
-
telemetry("agent_tools_executed", { taskId, agent: CREWSWARM_RT_AGENT, count: toolResults.length });
|
|
478
|
-
|
|
479
|
-
// Do a follow-up LLM call whenever:
|
|
480
|
-
// (a) searches ran (agent needs to see results before writing), OR
|
|
481
|
-
// (b) write was suppressed (agent tried to write before searching)
|
|
482
|
-
const hasSearchResults = toolResults.some(r => r.includes("[tool:web_search]") || r.includes("[tool:web_fetch]") || r.includes("[tool:read_file]"));
|
|
483
|
-
const writeSuppressed = toolResults.some(r => r.includes("⏸ Write suppressed"));
|
|
484
|
-
const didWriteFile = toolResults.some(r => r.includes("[tool:write_file] ✅"));
|
|
485
|
-
|
|
486
|
-
if (hasSearchResults && (!didWriteFile || writeSuppressed)) {
|
|
487
|
-
try {
|
|
488
|
-
const followUpPrompt = `${agentSysPrompt || ""}\n\n[Original task]:\n${finalPrompt}\n\n[Tool results from your searches]:\n${toolResults.join("\n")}\n\nUsing ONLY the search results above (not your training data), write the complete output now using @@WRITE_FILE. Do not search again — just synthesize and write.`;
|
|
489
|
-
let followUpReply = ocAgentId
|
|
490
|
-
? await callLLMDirect(followUpPrompt, ocAgentId, agentSysPrompt)
|
|
491
|
-
: null;
|
|
492
|
-
if (!followUpReply) followUpReply = await bridge.chat(followUpPrompt, ocAgentId || "main", { idempotencyKey: dispatchKey + "-followup" });
|
|
493
|
-
followUpReply = stripThink(followUpReply);
|
|
494
|
-
const followUpTools = await executeToolCalls(followUpReply, CREWSWARM_RT_AGENT);
|
|
495
|
-
reply = reply + "\n\n" + followUpReply;
|
|
496
|
-
if (followUpTools.length > 0) {
|
|
497
|
-
reply = reply + "\n\n---\n**Follow-up tool results:**\n" + followUpTools.join("\n");
|
|
498
|
-
}
|
|
499
|
-
} catch (err) {
|
|
500
|
-
console.warn(`[bridge] Follow-up synthesis call failed: ${err.message}`);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// Append original task spec for self-verification (LangChain pattern)
|
|
506
|
-
// Helps agent confirm implementation matches ALL requirements
|
|
507
|
-
if (reply && prompt && !reply.includes('[ORIGINAL TASK]')) {
|
|
508
|
-
const taskSpecReminder = `\n\n---\n**[ORIGINAL TASK]:**\n${prompt.slice(0, 500)}${prompt.length > 500 ? '...' : ''}\n\nDoes your implementation address ALL requirements above?`;
|
|
509
|
-
reply = reply + taskSpecReminder;
|
|
510
|
-
telemetry("task_spec_injected", { taskId, promptLength: prompt.length });
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// Validate coding artifacts for coding tasks
|
|
514
|
-
const validation = validateCodingArtifacts(reply, incomingType, prompt, payload);
|
|
515
|
-
if (!validation.valid) {
|
|
516
|
-
telemetry("coding_artifact_validation_failed", {
|
|
517
|
-
taskId,
|
|
518
|
-
incomingType,
|
|
519
|
-
reason: validation.reason,
|
|
520
|
-
replyLength: reply.length,
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
// Send feedback to agent before retrying
|
|
524
|
-
client.publish({
|
|
525
|
-
channel: "issues",
|
|
526
|
-
type: "task.artifact_missing",
|
|
527
|
-
to: CREWSWARM_RT_AGENT, // Send feedback to self for learning
|
|
528
|
-
taskId,
|
|
529
|
-
correlationId,
|
|
530
|
-
priority: "high",
|
|
531
|
-
payload: {
|
|
532
|
-
source: "gateway",
|
|
533
|
-
error: `CODING_ARTIFACT_MISSING: ${validation.reason}`,
|
|
534
|
-
feedback: "Your reply must include: (1) Files changed with paths, (2) What changed in each file, (3) Command outputs (build/test/lint), (4) Verification steps. Do not reply with only suggestions or 'Done' without evidence.",
|
|
535
|
-
originalPrompt: String(prompt).slice(0, 500),
|
|
536
|
-
replyPreview: String(reply).slice(0, 500),
|
|
537
|
-
},
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
throw new Error(`CODING_ARTIFACT_MISSING: ${validation.reason}`);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
if (dispatchGuardEnabled && dispatchClaim?.acquired) {
|
|
544
|
-
markTaskDone({
|
|
545
|
-
key: dispatchKey,
|
|
546
|
-
claimId: dispatchClaim.claimId,
|
|
547
|
-
taskId,
|
|
548
|
-
incomingType,
|
|
549
|
-
from,
|
|
550
|
-
attempt: dispatchAttempt,
|
|
551
|
-
idempotencyKey: dispatchKey,
|
|
552
|
-
reply,
|
|
553
|
-
});
|
|
554
|
-
telemetry("dispatch_task_done", {
|
|
555
|
-
key: dispatchKey,
|
|
556
|
-
taskId,
|
|
557
|
-
incomingType,
|
|
558
|
-
claimId: dispatchClaim.claimId,
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
// Parse @@LESSON: tags — write to project brain (if projectDir) or global lessons.md
|
|
563
|
-
// This is how agents contribute durable knowledge without polluting system prompts
|
|
564
|
-
const lessonMatches = [...reply.matchAll(/@@LESSON:\s*([^\n]+)/g)];
|
|
565
|
-
if (lessonMatches.length > 0) {
|
|
566
|
-
const date = new Date().toISOString().slice(0, 10);
|
|
567
|
-
for (const m of lessonMatches) {
|
|
568
|
-
const entry = m[1].trim();
|
|
569
|
-
if (!entry) continue;
|
|
570
|
-
try {
|
|
571
|
-
if (projectDir) {
|
|
572
|
-
const projectMemDir = path.join(projectDir, ".crewswarm");
|
|
573
|
-
fs.mkdirSync(projectMemDir, { recursive: true });
|
|
574
|
-
const projectBrainPath = path.join(projectMemDir, "brain.md");
|
|
575
|
-
if (!fs.existsSync(projectBrainPath)) {
|
|
576
|
-
fs.writeFileSync(projectBrainPath, "# Project Brain\n\nAccumulated knowledge for this project.\n", "utf8");
|
|
577
|
-
}
|
|
578
|
-
fs.appendFileSync(projectBrainPath, `\n## [${date}] ${CREWSWARM_RT_AGENT}: ${entry}\n`, "utf8");
|
|
579
|
-
} else {
|
|
580
|
-
const lessonsPath = path.join(SHARED_MEMORY_DIR, "lessons.md");
|
|
581
|
-
fs.appendFileSync(lessonsPath, `\n## [${date}] ${CREWSWARM_RT_AGENT}: ${entry}\n`, "utf8");
|
|
582
|
-
}
|
|
583
|
-
console.log(`[bridge:${CREWSWARM_RT_AGENT}] @@LESSON → ${projectDir ? path.basename(projectDir) + "/.crewswarm/brain.md" : "lessons.md"}: ${entry.slice(0, 80)}`);
|
|
584
|
-
} catch (e) {
|
|
585
|
-
console.warn(`[bridge:${CREWSWARM_RT_AGENT}] @@LESSON write failed: ${e.message}`);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// Parse and execute @@DISPATCH commands from coordinator agents only.
|
|
591
|
-
// Canonical format: @@DISPATCH {"agent":"crew-coder","task":"..."}
|
|
592
|
-
// Legacy format also supported: @@DISPATCH:agent-id|task description
|
|
593
|
-
// Non-coordinator agents are blocked from dispatching to prevent loops.
|
|
594
|
-
const COORDINATOR_AGENTS = new Set(COORDINATOR_AGENT_IDS);
|
|
595
|
-
const rawDispatches = COORDINATOR_AGENTS.has(CREWSWARM_RT_AGENT)
|
|
596
|
-
? (() => {
|
|
597
|
-
const results = [];
|
|
598
|
-
// Canonical JSON format
|
|
599
|
-
for (const m of reply.matchAll(/@@DISPATCH\s+(\{[^}]+\})/g)) {
|
|
600
|
-
try {
|
|
601
|
-
const d = JSON.parse(m[1]);
|
|
602
|
-
if (d.agent && d.task) results.push({ targetAgent: d.agent.trim(), taskText: d.task.trim() });
|
|
603
|
-
} catch {}
|
|
604
|
-
}
|
|
605
|
-
// Legacy pipe format (still supported, normalized here)
|
|
606
|
-
for (const m of reply.matchAll(/@@DISPATCH:([a-z0-9_-]+)\|([^\n@@]+)/g)) {
|
|
607
|
-
results.push({ targetAgent: m[1].trim(), taskText: m[2].trim() });
|
|
608
|
-
}
|
|
609
|
-
return results;
|
|
610
|
-
})()
|
|
611
|
-
: [];
|
|
612
|
-
if (rawDispatches.length > 0) {
|
|
613
|
-
for (const { targetAgent, taskText } of rawDispatches) {
|
|
614
|
-
// Block self-dispatch and empty targets
|
|
615
|
-
if (!targetAgent || !taskText || targetAgent === CREWSWARM_RT_AGENT) continue;
|
|
616
|
-
try {
|
|
617
|
-
// For audit/QA tasks, inject file contents so the agent can actually read them
|
|
618
|
-
let enrichedTask = taskText;
|
|
619
|
-
const filePaths = [...taskText.matchAll(/([~/\w.-]+\.(?:html|css|js|mjs|ts|md|json))/g)].map(m => m[1]);
|
|
620
|
-
if (filePaths.length > 0) {
|
|
621
|
-
const fileSnippets = [];
|
|
622
|
-
for (const fp of filePaths.slice(0, 3)) {
|
|
623
|
-
try {
|
|
624
|
-
const absPath = fp.startsWith("~") ? fp.replace("~", os.homedir()) : fp;
|
|
625
|
-
const content = fs.readFileSync(absPath, "utf8");
|
|
626
|
-
const lines = content.split("\n");
|
|
627
|
-
// Include full file for small files, truncated for large ones
|
|
628
|
-
const snippet = lines.length <= 600
|
|
629
|
-
? content
|
|
630
|
-
: lines.slice(0, 300).join("\n") + `\n\n... (${lines.length - 300} more lines truncated) ...\n` + lines.slice(-100).join("\n");
|
|
631
|
-
fileSnippets.push(`\n\n--- FILE: ${absPath} (${lines.length} lines) ---\n${snippet}\n--- END FILE ---`);
|
|
632
|
-
} catch { /* file not readable, skip */ }
|
|
633
|
-
}
|
|
634
|
-
if (fileSnippets.length > 0) {
|
|
635
|
-
enrichedTask = taskText + "\n\nFile contents for your audit:" + fileSnippets.join("");
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
const dispatchTaskId = "dispatch-" + Date.now() + "-" + Math.random().toString(36).slice(2, 6);
|
|
639
|
-
client.publish({
|
|
640
|
-
channel: "command",
|
|
641
|
-
type: "command.run_task",
|
|
642
|
-
to: targetAgent,
|
|
643
|
-
taskId: dispatchTaskId,
|
|
644
|
-
priority: "high",
|
|
645
|
-
payload: {
|
|
646
|
-
action: "run_task",
|
|
647
|
-
prompt: enrichedTask,
|
|
648
|
-
dispatchedBy: CREWSWARM_RT_AGENT,
|
|
649
|
-
parentTaskId: taskId,
|
|
650
|
-
},
|
|
651
|
-
});
|
|
652
|
-
telemetry("crew_dispatch_forwarded", { from: CREWSWARM_RT_AGENT, to: targetAgent, taskId: dispatchTaskId });
|
|
653
|
-
progress(`Dispatched task to ${targetAgent}: ${taskText.slice(0, 60)}`);
|
|
654
|
-
} catch (dispErr) {
|
|
655
|
-
console.error(`[bridge] CREW_DISPATCH to ${targetAgent} failed:`, dispErr?.message);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
client.publish({
|
|
661
|
-
channel: "done",
|
|
662
|
-
type: "task.done",
|
|
663
|
-
to: from,
|
|
664
|
-
taskId,
|
|
665
|
-
correlationId,
|
|
666
|
-
priority: "high",
|
|
667
|
-
payload: {
|
|
668
|
-
source: CREWSWARM_RT_AGENT,
|
|
669
|
-
reply,
|
|
670
|
-
incomingType,
|
|
671
|
-
idempotencyKey: dispatchKey,
|
|
672
|
-
engineUsed, // Track which coding engine handled the task (claude, codex, cursor, opencode, etc.)
|
|
673
|
-
},
|
|
674
|
-
});
|
|
675
|
-
|
|
676
|
-
// Save to unified project messages store
|
|
677
|
-
if (payload?.projectId && payload.projectId !== 'general') {
|
|
678
|
-
try {
|
|
679
|
-
saveProjectMessage(payload.projectId, {
|
|
680
|
-
source: 'agent',
|
|
681
|
-
role: 'assistant',
|
|
682
|
-
content: reply,
|
|
683
|
-
agent: CREWSWARM_RT_AGENT,
|
|
684
|
-
metadata: {
|
|
685
|
-
taskId,
|
|
686
|
-
engineUsed,
|
|
687
|
-
incomingType,
|
|
688
|
-
model: modelUsed || 'unknown'
|
|
689
|
-
}
|
|
690
|
-
});
|
|
691
|
-
} catch (e) {
|
|
692
|
-
console.warn(`[rt-envelope] Failed to save agent reply to project store: ${e.message}`);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
// Record to shared memory (AgentKeeper) for future recall
|
|
697
|
-
if (isSharedMemoryAvailable()) {
|
|
698
|
-
const projectDir = extractProjectDirFromTask(prompt) || getOpencodeProjectDir() || process.cwd();
|
|
699
|
-
recordTaskMemory(projectDir, {
|
|
700
|
-
runId: taskId,
|
|
701
|
-
tier: 'worker',
|
|
702
|
-
task: prompt,
|
|
703
|
-
result: reply,
|
|
704
|
-
agent: CREWSWARM_RT_AGENT,
|
|
705
|
-
model: modelUsed || 'unknown',
|
|
706
|
-
metadata: {
|
|
707
|
-
engineUsed,
|
|
708
|
-
incomingType,
|
|
709
|
-
success: true,
|
|
710
|
-
timestamp: new Date().toISOString()
|
|
711
|
-
}
|
|
712
|
-
}).catch(err => {
|
|
713
|
-
console.warn(`[${CREWSWARM_RT_AGENT}] Failed to record task memory: ${err.message}`);
|
|
714
|
-
});
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
client?.publish({ channel: "events", type: "agent_idle", to: "broadcast", payload: { agent: CREWSWARM_RT_AGENT, ts: Date.now() } });
|
|
718
|
-
client.ack({ messageId: envelope.id, status: "done", note: "task completed" });
|
|
719
|
-
} catch (err) {
|
|
720
|
-
const message = err?.message ?? String(err);
|
|
721
|
-
const isCoding = isCodingTask(incomingType, prompt, payload);
|
|
722
|
-
const maxRetries = isCoding ? CREWSWARM_RT_DISPATCH_MAX_RETRIES_CODING : CREWSWARM_RT_DISPATCH_MAX_RETRIES;
|
|
723
|
-
const shouldRetry = dispatchGuardEnabled
|
|
724
|
-
&& dispatchClaim?.acquired
|
|
725
|
-
&& shouldRetryTaskFailure(err)
|
|
726
|
-
&& dispatchAttempt < maxRetries;
|
|
727
|
-
|
|
728
|
-
if (shouldRetry) {
|
|
729
|
-
const retryAttempt = dispatchAttempt + 1;
|
|
730
|
-
const retryAfterMs = CREWSWARM_RT_DISPATCH_RETRY_BACKOFF_MS * (2 ** dispatchAttempt);
|
|
731
|
-
telemetry("dispatch_retry_scheduled", {
|
|
732
|
-
key: dispatchKey,
|
|
733
|
-
taskId,
|
|
734
|
-
incomingType,
|
|
735
|
-
attempt: retryAttempt,
|
|
736
|
-
retryAfterMs,
|
|
737
|
-
error: message,
|
|
738
|
-
});
|
|
739
|
-
client.publish({
|
|
740
|
-
channel: "status",
|
|
741
|
-
type: "task.retrying",
|
|
742
|
-
to: from,
|
|
743
|
-
taskId,
|
|
744
|
-
correlationId,
|
|
745
|
-
priority: "high",
|
|
746
|
-
payload: {
|
|
747
|
-
source: CREWSWARM_RT_AGENT,
|
|
748
|
-
incomingType,
|
|
749
|
-
attempt: retryAttempt,
|
|
750
|
-
retryAfterMs,
|
|
751
|
-
error: message,
|
|
752
|
-
idempotencyKey: dispatchKey,
|
|
753
|
-
},
|
|
754
|
-
});
|
|
755
|
-
|
|
756
|
-
setTimeout(() => {
|
|
757
|
-
try {
|
|
758
|
-
client.publish({
|
|
759
|
-
channel: "command",
|
|
760
|
-
type: incomingType,
|
|
761
|
-
to: CREWSWARM_RT_AGENT, // Retry to SELF, not broadcast (prevents 7x amplification)
|
|
762
|
-
taskId,
|
|
763
|
-
priority: "high",
|
|
764
|
-
payload: {
|
|
765
|
-
...payload,
|
|
766
|
-
_dispatchAttempt: retryAttempt,
|
|
767
|
-
_dispatchIdempotencyKey: dispatchKey,
|
|
768
|
-
_dispatchRetryOf: dispatchAttempt,
|
|
769
|
-
_dispatchLastError: message,
|
|
770
|
-
},
|
|
771
|
-
});
|
|
772
|
-
} catch (publishErr) {
|
|
773
|
-
telemetry("dispatch_retry_publish_error", {
|
|
774
|
-
key: dispatchKey,
|
|
775
|
-
taskId,
|
|
776
|
-
incomingType,
|
|
777
|
-
attempt: retryAttempt,
|
|
778
|
-
error: publishErr?.message ?? String(publishErr),
|
|
779
|
-
});
|
|
780
|
-
}
|
|
781
|
-
}, retryAfterMs);
|
|
782
|
-
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
// Write to DLQ if all retries exhausted
|
|
787
|
-
if (dispatchGuardEnabled && dispatchClaim?.acquired && dispatchAttempt >= maxRetries) {
|
|
788
|
-
const dlqPath = path.join(SWARM_DLQ_DIR, `${dispatchKey}.json`);
|
|
789
|
-
const dlqEntry = {
|
|
790
|
-
key: dispatchKey,
|
|
791
|
-
taskId,
|
|
792
|
-
incomingType,
|
|
793
|
-
from,
|
|
794
|
-
agent: CREWSWARM_RT_AGENT,
|
|
795
|
-
attempt: dispatchAttempt,
|
|
796
|
-
error: message,
|
|
797
|
-
prompt: String(prompt).slice(0, 2000),
|
|
798
|
-
payload,
|
|
799
|
-
failedAt: new Date().toISOString(),
|
|
800
|
-
envelope,
|
|
801
|
-
};
|
|
802
|
-
try {
|
|
803
|
-
fs.writeFileSync(dlqPath, JSON.stringify(dlqEntry, null, 2), "utf8");
|
|
804
|
-
telemetry("dlq_write", { key: dispatchKey, taskId, incomingType });
|
|
805
|
-
} catch (dlqErr) {
|
|
806
|
-
telemetry("dlq_write_error", { key: dispatchKey, error: dlqErr?.message });
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
// ── Auto-escalate to crew-fixer when coding agents exhaust retries ─────
|
|
810
|
-
const ESCALATABLE_AGENTS = new Set([
|
|
811
|
-
"crew-coder", "crew-coder-front", "crew-coder-back", "crew-frontend", "crew-copywriter",
|
|
812
|
-
]);
|
|
813
|
-
const isSelf = CREWSWARM_RT_AGENT === "crew-fixer"; // prevent fixer→fixer loop
|
|
814
|
-
if (ESCALATABLE_AGENTS.has(CREWSWARM_RT_AGENT) && !isSelf) {
|
|
815
|
-
const fixerTaskId = `fixer-escalation-${Date.now()}`;
|
|
816
|
-
const fixerPrompt =
|
|
817
|
-
`⚠️ Auto-escalation from ${CREWSWARM_RT_AGENT} (failed after ${dispatchAttempt + 1} attempts).\n\n` +
|
|
818
|
-
`**Original task:**\n${String(prompt).slice(0, 1500)}\n\n` +
|
|
819
|
-
`**Error:**\n${message.slice(0, 500)}\n\n` +
|
|
820
|
-
`Use @@READ_FILE to inspect any relevant files, identify the root cause, and fix it.`;
|
|
821
|
-
try {
|
|
822
|
-
client.publish({
|
|
823
|
-
channel: "command",
|
|
824
|
-
type: "command.run_task",
|
|
825
|
-
to: "crew-fixer",
|
|
826
|
-
taskId: fixerTaskId,
|
|
827
|
-
priority: "high",
|
|
828
|
-
payload: { action: "run_task", prompt: fixerPrompt, escalatedFrom: CREWSWARM_RT_AGENT, parentTaskId: taskId },
|
|
829
|
-
});
|
|
830
|
-
telemetry("task_escalated_to_fixer", { fromAgent: CREWSWARM_RT_AGENT, taskId, fixerTaskId });
|
|
831
|
-
console.log(`[${CREWSWARM_RT_AGENT}] ⬆️ Escalated failed task to crew-fixer (${fixerTaskId})`);
|
|
832
|
-
} catch (escErr) {
|
|
833
|
-
console.error(`[${CREWSWARM_RT_AGENT}] Escalation to crew-fixer failed:`, escErr?.message);
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
client.publish({
|
|
840
|
-
channel: "issues",
|
|
841
|
-
type: "task.failed",
|
|
842
|
-
to: from,
|
|
843
|
-
taskId,
|
|
844
|
-
correlationId,
|
|
845
|
-
priority: "high",
|
|
846
|
-
payload: {
|
|
847
|
-
source: CREWSWARM_RT_AGENT,
|
|
848
|
-
error: message,
|
|
849
|
-
idempotencyKey: dispatchKey,
|
|
850
|
-
attempt: dispatchAttempt,
|
|
851
|
-
},
|
|
852
|
-
});
|
|
853
|
-
client.ack({ messageId: envelope.id, status: "failed", note: message.slice(0, 240) });
|
|
854
|
-
} finally {
|
|
855
|
-
if (dispatchHeartbeat) {
|
|
856
|
-
clearInterval(dispatchHeartbeat);
|
|
857
|
-
dispatchHeartbeat = null;
|
|
858
|
-
}
|
|
859
|
-
if (dispatchGuardEnabled && dispatchClaim?.acquired) {
|
|
860
|
-
const released = releaseTaskLease({ key: dispatchKey, claimId: dispatchClaim.claimId });
|
|
861
|
-
telemetry("dispatch_claim_released", {
|
|
862
|
-
key: dispatchKey,
|
|
863
|
-
taskId,
|
|
864
|
-
incomingType,
|
|
865
|
-
claimId: dispatchClaim.claimId,
|
|
866
|
-
released,
|
|
867
|
-
});
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
}
|