chorus-codes 0.7.0 → 0.7.1
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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/cache/.previewinfo +1 -1
- package/.next/cache/.rscinfo +1 -1
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/new.html +1 -1
- package/.next/server/app/new.rsc +1 -1
- package/.next/server/app/new.segments/_full.segment.rsc +1 -1
- package/.next/server/app/new.segments/_head.segment.rsc +1 -1
- package/.next/server/app/new.segments/_index.segment.rsc +1 -1
- package/.next/server/app/new.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/new.segments/new/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/new.segments/new.segment.rsc +1 -1
- package/.next/server/app/onboarding.html +1 -1
- package/.next/server/app/onboarding.rsc +1 -1
- package/.next/server/app/onboarding.segments/_full.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/_head.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/_index.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/onboarding/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/onboarding.segment.rsc +1 -1
- package/.next/server/app/personas.html +1 -1
- package/.next/server/app/personas.rsc +1 -1
- package/.next/server/app/personas.segments/_full.segment.rsc +1 -1
- package/.next/server/app/personas.segments/_head.segment.rsc +1 -1
- package/.next/server/app/personas.segments/_index.segment.rsc +1 -1
- package/.next/server/app/personas.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/personas.segments/personas/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/personas.segments/personas.segment.rsc +1 -1
- package/.next/server/app/settings.html +1 -1
- package/.next/server/app/settings.rsc +1 -1
- package/.next/server/app/settings.segments/_full.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/server/app/templates.html +1 -1
- package/.next/server/app/templates.rsc +1 -1
- package/.next/server/app/templates.segments/_full.segment.rsc +1 -1
- package/.next/server/app/templates.segments/_head.segment.rsc +1 -1
- package/.next/server/app/templates.segments/_index.segment.rsc +1 -1
- package/.next/server/app/templates.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/templates.segments/templates/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/templates.segments/templates.segment.rsc +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/trace +1 -1
- package/.next/trace-build +1 -1
- package/dist/cli/commands/doctor.js +116 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/init.js +211 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/start.js +240 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.js +54 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.js +97 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/connect.js +108 -0
- package/dist/cli/connect.js.map +1 -0
- package/dist/cli/index.js +99 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/port-utils.js +260 -0
- package/dist/cli/port-utils.js.map +1 -0
- package/dist/cli/runtime-env.js +60 -0
- package/dist/cli/runtime-env.js.map +1 -0
- package/dist/cli/shared.js +54 -0
- package/dist/cli/shared.js.map +1 -0
- package/dist/cli/ui.js +60 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/daemon/agents/claude.js +98 -0
- package/dist/daemon/agents/claude.js.map +1 -0
- package/dist/daemon/agents/codex.js +160 -0
- package/dist/daemon/agents/codex.js.map +1 -0
- package/dist/daemon/agents/gemini.js +111 -0
- package/dist/daemon/agents/gemini.js.map +1 -0
- package/dist/daemon/agents/index.js +59 -0
- package/dist/daemon/agents/index.js.map +1 -0
- package/dist/daemon/agents/kimi.js +206 -0
- package/dist/daemon/agents/kimi.js.map +1 -0
- package/dist/daemon/agents/opencode.js +228 -0
- package/dist/daemon/agents/opencode.js.map +1 -0
- package/dist/daemon/agents/openrouter.js +274 -0
- package/dist/daemon/agents/openrouter.js.map +1 -0
- package/dist/daemon/agents/parsers/claude.js +63 -0
- package/dist/daemon/agents/parsers/claude.js.map +1 -0
- package/dist/daemon/agents/parsers/codex.js +51 -0
- package/dist/daemon/agents/parsers/codex.js.map +1 -0
- package/dist/daemon/agents/parsers/gemini.js +144 -0
- package/dist/daemon/agents/parsers/gemini.js.map +1 -0
- package/dist/daemon/agents/parsers/index.js +31 -0
- package/dist/daemon/agents/parsers/index.js.map +1 -0
- package/dist/daemon/agents/parsers/kimi.js +8 -0
- package/dist/daemon/agents/parsers/kimi.js.map +1 -0
- package/dist/daemon/agents/parsers/opencode.js +105 -0
- package/dist/daemon/agents/parsers/opencode.js.map +1 -0
- package/dist/daemon/agents/parsers/openrouter.js +69 -0
- package/dist/daemon/agents/parsers/openrouter.js.map +1 -0
- package/dist/daemon/agents/parsers/shared.js +17 -0
- package/dist/daemon/agents/parsers/shared.js.map +1 -0
- package/dist/daemon/agents/preflight.js +83 -0
- package/dist/daemon/agents/preflight.js.map +1 -0
- package/dist/daemon/agents/quote.js +45 -0
- package/dist/daemon/agents/quote.js.map +1 -0
- package/dist/daemon/agents/sandbox-guard.js +69 -0
- package/dist/daemon/agents/sandbox-guard.js.map +1 -0
- package/dist/daemon/agents/types.js +6 -0
- package/dist/daemon/agents/types.js.map +1 -0
- package/dist/daemon/api-response.js +65 -0
- package/dist/daemon/api-response.js.map +1 -0
- package/dist/daemon/error-detector.js +329 -0
- package/dist/daemon/error-detector.js.map +1 -0
- package/dist/daemon/headless.js +533 -0
- package/dist/daemon/headless.js.map +1 -0
- package/dist/daemon/index.js +333 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/openrouter.js +192 -0
- package/dist/daemon/openrouter.js.map +1 -0
- package/dist/daemon/orchestrators/claude.js +163 -0
- package/dist/daemon/orchestrators/claude.js.map +1 -0
- package/dist/daemon/orchestrators/codex.js +101 -0
- package/dist/daemon/orchestrators/codex.js.map +1 -0
- package/dist/daemon/orchestrators/cursor-windsurf.js +118 -0
- package/dist/daemon/orchestrators/cursor-windsurf.js.map +1 -0
- package/dist/daemon/orchestrators/gemini.js +108 -0
- package/dist/daemon/orchestrators/gemini.js.map +1 -0
- package/dist/daemon/orchestrators/index.js +90 -0
- package/dist/daemon/orchestrators/index.js.map +1 -0
- package/dist/daemon/orchestrators/kimi.js +108 -0
- package/dist/daemon/orchestrators/kimi.js.map +1 -0
- package/dist/daemon/orchestrators/opencode.js +152 -0
- package/dist/daemon/orchestrators/opencode.js.map +1 -0
- package/dist/daemon/orchestrators/shared.js +60 -0
- package/dist/daemon/orchestrators/shared.js.map +1 -0
- package/dist/daemon/output-watcher.js +131 -0
- package/dist/daemon/output-watcher.js.map +1 -0
- package/dist/daemon/participant-aborts.js +123 -0
- package/dist/daemon/participant-aborts.js.map +1 -0
- package/dist/daemon/reaper.js +46 -0
- package/dist/daemon/reaper.js.map +1 -0
- package/dist/daemon/routes/chats-events.js +62 -0
- package/dist/daemon/routes/chats-events.js.map +1 -0
- package/dist/daemon/routes/chats-stream.js +241 -0
- package/dist/daemon/routes/chats-stream.js.map +1 -0
- package/dist/daemon/routes/chats-validation.js +13 -0
- package/dist/daemon/routes/chats-validation.js.map +1 -0
- package/dist/daemon/routes/chats.js +545 -0
- package/dist/daemon/routes/chats.js.map +1 -0
- package/dist/daemon/routes/openrouter.js +103 -0
- package/dist/daemon/routes/openrouter.js.map +1 -0
- package/dist/daemon/routes/settings.js +199 -0
- package/dist/daemon/routes/settings.js.map +1 -0
- package/dist/daemon/routes/stats.js +155 -0
- package/dist/daemon/routes/stats.js.map +1 -0
- package/dist/daemon/routes/system.js +208 -0
- package/dist/daemon/routes/system.js.map +1 -0
- package/dist/daemon/routes/templates-personas.js +254 -0
- package/dist/daemon/routes/templates-personas.js.map +1 -0
- package/dist/daemon/routes/voices.js +139 -0
- package/dist/daemon/routes/voices.js.map +1 -0
- package/dist/daemon/runner/doer-driver.js +346 -0
- package/dist/daemon/runner/doer-driver.js.map +1 -0
- package/dist/daemon/runner/doer.js +336 -0
- package/dist/daemon/runner/doer.js.map +1 -0
- package/dist/daemon/runner/prior-round.js +140 -0
- package/dist/daemon/runner/prior-round.js.map +1 -0
- package/dist/daemon/runner/prompt-builder.js +292 -0
- package/dist/daemon/runner/prompt-builder.js.map +1 -0
- package/dist/daemon/runner/review-only-phase.js +103 -0
- package/dist/daemon/runner/review-only-phase.js.map +1 -0
- package/dist/daemon/runner/reviewer-driver.js +410 -0
- package/dist/daemon/runner/reviewer-driver.js.map +1 -0
- package/dist/daemon/runner/reviewer.js +384 -0
- package/dist/daemon/runner/reviewer.js.map +1 -0
- package/dist/daemon/runner/run-with-fallback.js +56 -0
- package/dist/daemon/runner/run-with-fallback.js.map +1 -0
- package/dist/daemon/runner/sanitize-name.js +8 -0
- package/dist/daemon/runner/sanitize-name.js.map +1 -0
- package/dist/daemon/runner/stream-file-writer.js +116 -0
- package/dist/daemon/runner/stream-file-writer.js.map +1 -0
- package/dist/daemon/runner/swap-sidecar.js +102 -0
- package/dist/daemon/runner/swap-sidecar.js.map +1 -0
- package/dist/daemon/runner/template-fallback.js +119 -0
- package/dist/daemon/runner/template-fallback.js.map +1 -0
- package/dist/daemon/runner/types.js +3 -0
- package/dist/daemon/runner/types.js.map +1 -0
- package/dist/daemon/runner/verdict.js +51 -0
- package/dist/daemon/runner/verdict.js.map +1 -0
- package/dist/daemon/runner-multiplex.js +364 -0
- package/dist/daemon/runner-multiplex.js.map +1 -0
- package/dist/daemon/runner.js +427 -0
- package/dist/daemon/runner.js.map +1 -0
- package/dist/daemon/ship.js +340 -0
- package/dist/daemon/ship.js.map +1 -0
- package/dist/daemon/template-cache.js +37 -0
- package/dist/daemon/template-cache.js.map +1 -0
- package/dist/daemon/tmux-types.js +9 -0
- package/dist/daemon/tmux-types.js.map +1 -0
- package/dist/daemon/tmux.js +341 -0
- package/dist/daemon/tmux.js.map +1 -0
- package/dist/lib/atomic-write.js +55 -0
- package/dist/lib/atomic-write.js.map +1 -0
- package/dist/lib/chat-events-bus.js +27 -0
- package/dist/lib/chat-events-bus.js.map +1 -0
- package/dist/lib/chat-slug.js +105 -0
- package/dist/lib/chat-slug.js.map +1 -0
- package/dist/lib/cli-detect.js +388 -0
- package/dist/lib/cli-detect.js.map +1 -0
- package/dist/lib/cli-health.js +156 -0
- package/dist/lib/cli-health.js.map +1 -0
- package/dist/lib/cli-paths.js +113 -0
- package/dist/lib/cli-paths.js.map +1 -0
- package/dist/lib/cli-precheck.js +141 -0
- package/dist/lib/cli-precheck.js.map +1 -0
- package/dist/lib/db/chats.js +244 -0
- package/dist/lib/db/chats.js.map +1 -0
- package/dist/lib/db/connection.js +254 -0
- package/dist/lib/db/connection.js.map +1 -0
- package/dist/lib/db/index.js +34 -0
- package/dist/lib/db/index.js.map +1 -0
- package/dist/lib/db/personas.js +65 -0
- package/dist/lib/db/personas.js.map +1 -0
- package/dist/lib/db/phase-events.js +172 -0
- package/dist/lib/db/phase-events.js.map +1 -0
- package/dist/lib/db/secrets.js +53 -0
- package/dist/lib/db/secrets.js.map +1 -0
- package/dist/lib/db/settings.js +47 -0
- package/dist/lib/db/settings.js.map +1 -0
- package/dist/lib/db/templates.js +75 -0
- package/dist/lib/db/templates.js.map +1 -0
- package/dist/lib/db/voices.js +184 -0
- package/dist/lib/db/voices.js.map +1 -0
- package/dist/lib/lineage-maps.js +200 -0
- package/dist/lib/lineage-maps.js.map +1 -0
- package/dist/lib/logger.js +186 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/personas.js +117 -0
- package/dist/lib/personas.js.map +1 -0
- package/dist/lib/runtime-path.js +222 -0
- package/dist/lib/runtime-path.js.map +1 -0
- package/dist/lib/settings/billing.js +58 -0
- package/dist/lib/settings/billing.js.map +1 -0
- package/dist/lib/settings/permissions.js +81 -0
- package/dist/lib/settings/permissions.js.map +1 -0
- package/dist/lib/settings/transport.js +113 -0
- package/dist/lib/settings/transport.js.map +1 -0
- package/dist/lib/telemetry.js +290 -0
- package/dist/lib/telemetry.js.map +1 -0
- package/dist/lib/template-schema.js +319 -0
- package/dist/lib/template-schema.js.map +1 -0
- package/dist/lib/template-validation.js +82 -0
- package/dist/lib/template-validation.js.map +1 -0
- package/dist/lib/voices.js +533 -0
- package/dist/lib/voices.js.map +1 -0
- package/dist/mcp/client.js +138 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/index.js +178 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/tools.js +355 -0
- package/dist/mcp/tools.js.map +1 -0
- package/package.json +2 -1
- package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0beh7rg._.js +0 -6077
- package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0beh7rg._.js.map +0 -69
- package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0pjsj.j._.js +0 -6318
- package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0pjsj.j._.js.map +0 -71
- package/.next/dev/types/cache-life.d.ts +0 -145
- package/.next/dev/types/routes.d.ts +0 -84
- package/.next/dev/types/validator.ts +0 -178
- /package/.next/static/{dJlbRLlhISA0JGtHKVqgQ → 8H2I8TTPlrKuWCV-SxY5f}/_buildManifest.js +0 -0
- /package/.next/static/{dJlbRLlhISA0JGtHKVqgQ → 8H2I8TTPlrKuWCV-SxY5f}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{dJlbRLlhISA0JGtHKVqgQ → 8H2I8TTPlrKuWCV-SxY5f}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Per-participant fallback-swap sidecar.
|
|
4
|
+
*
|
|
5
|
+
* Lives at `<participantDir>/_swaps.json` next to `answer.md` /
|
|
6
|
+
* `_stats.json`. Mirrors the existing sidecar pattern — JSON, read by
|
|
7
|
+
* the run-artifacts route at refresh time, no DB schema change.
|
|
8
|
+
*
|
|
9
|
+
* Why a sidecar and not the DB:
|
|
10
|
+
* - phase_events packs warnings as opaque text (`output: "[lineage_fallback]
|
|
11
|
+
* <message>"`). Reconstructing structured fromLineage/toModel/etc. on
|
|
12
|
+
* replay would mean parsing that string — fragile.
|
|
13
|
+
* - The SSE stream shuts off for terminal chats, so reload-after-done
|
|
14
|
+
* loses the cli_warning events entirely.
|
|
15
|
+
* - Sidecar survives indefinitely on disk and is cheap to read.
|
|
16
|
+
*
|
|
17
|
+
* Append-only — multiple swaps in a single slot's chain (rare, but
|
|
18
|
+
* possible: codex → openrouter-gpt → claude) all land in one file.
|
|
19
|
+
*/
|
|
20
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.appendSwapSidecar = appendSwapSidecar;
|
|
25
|
+
exports.readSwapSidecar = readSwapSidecar;
|
|
26
|
+
const fs_1 = __importDefault(require("fs"));
|
|
27
|
+
const path_1 = __importDefault(require("path"));
|
|
28
|
+
const SIDECAR_NAME = '_swaps.json';
|
|
29
|
+
/**
|
|
30
|
+
* Append a swap entry to the participant's sidecar. Reads the current
|
|
31
|
+
* file (treats missing / malformed as empty), pushes the entry, writes
|
|
32
|
+
* the result back. Synchronous + best-effort: a failed write is logged
|
|
33
|
+
* to console.error and otherwise swallowed — the SSE event still went
|
|
34
|
+
* out, so the live UI shows the swap; only the post-reload card is
|
|
35
|
+
* affected.
|
|
36
|
+
*/
|
|
37
|
+
function appendSwapSidecar(participantDir, entry) {
|
|
38
|
+
try {
|
|
39
|
+
const filePath = path_1.default.join(participantDir, SIDECAR_NAME);
|
|
40
|
+
let existing = [];
|
|
41
|
+
if (fs_1.default.existsSync(filePath)) {
|
|
42
|
+
try {
|
|
43
|
+
const raw = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
44
|
+
const parsed = JSON.parse(raw);
|
|
45
|
+
if (Array.isArray(parsed)) {
|
|
46
|
+
existing = parsed;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Non-array payload — preserve the bad file before overwriting
|
|
50
|
+
// so a manual recovery is possible (vs. silently wiping it).
|
|
51
|
+
renameToCorrupt(filePath);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Truncated / unparseable JSON, almost certainly from a crash
|
|
56
|
+
// mid-write of a prior version (pre-atomic). Side-band it instead
|
|
57
|
+
// of dropping; next append starts fresh.
|
|
58
|
+
renameToCorrupt(filePath);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
existing.push(entry);
|
|
62
|
+
// Atomic write: stage to <name>.tmp then renameSync. POSIX rename is
|
|
63
|
+
// atomic within the same directory — readers either see the old
|
|
64
|
+
// file or the new one, never a half-written intermediate.
|
|
65
|
+
const tmpPath = `${filePath}.tmp`;
|
|
66
|
+
fs_1.default.writeFileSync(tmpPath, JSON.stringify(existing, null, 2), 'utf-8');
|
|
67
|
+
fs_1.default.renameSync(tmpPath, filePath);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
console.error('[chorus] failed to write swap sidecar:', err instanceof Error ? err.message : String(err));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function renameToCorrupt(filePath) {
|
|
74
|
+
try {
|
|
75
|
+
fs_1.default.renameSync(filePath, `${filePath}.corrupt-${Date.now()}`);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
/* best-effort — if the rename fails, the next writeFileSync
|
|
79
|
+
* overwrites the bad file anyway. */
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Read all swap entries from a participant's sidecar. Used by the
|
|
84
|
+
* run-artifacts route to surface fallback swaps on the run page even
|
|
85
|
+
* after the SSE stream has closed.
|
|
86
|
+
*/
|
|
87
|
+
function readSwapSidecar(participantDir) {
|
|
88
|
+
const filePath = path_1.default.join(participantDir, SIDECAR_NAME);
|
|
89
|
+
if (!fs_1.default.existsSync(filePath))
|
|
90
|
+
return [];
|
|
91
|
+
try {
|
|
92
|
+
const raw = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
93
|
+
const parsed = JSON.parse(raw);
|
|
94
|
+
if (Array.isArray(parsed))
|
|
95
|
+
return parsed;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
/* malformed — treat as empty */
|
|
99
|
+
}
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=swap-sidecar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swap-sidecar.js","sourceRoot":"","sources":["../../../src/daemon/runner/swap-sidecar.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;;;AA6BH,8CAsCC;AAgBD,0CAWC;AA5FD,4CAAoB;AACpB,gDAAwB;AAgBxB,MAAM,YAAY,GAAG,aAAa,CAAC;AAEnC;;;;;;;GAOG;AACH,SAAgB,iBAAiB,CAC/B,cAAsB,EACtB,KAAgB;IAEhB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;QACzD,IAAI,QAAQ,GAAgB,EAAE,CAAC;QAC/B,IAAI,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC1B,QAAQ,GAAG,MAAqB,CAAC;gBACnC,CAAC;qBAAM,CAAC;oBACN,+DAA+D;oBAC/D,6DAA6D;oBAC7D,eAAe,CAAC,QAAQ,CAAC,CAAC;gBAC5B,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,8DAA8D;gBAC9D,kEAAkE;gBAClE,yCAAyC;gBACzC,eAAe,CAAC,QAAQ,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,qEAAqE;QACrE,gEAAgE;QAChE,0DAA0D;QAC1D,MAAM,OAAO,GAAG,GAAG,QAAQ,MAAM,CAAC;QAClC,YAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACtE,YAAE,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CACX,wCAAwC,EACxC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CACjD,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,QAAgB;IACvC,IAAI,CAAC;QACH,YAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,GAAG,QAAQ,YAAY,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP;6CACqC;IACvC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAgB,eAAe,CAAC,cAAsB;IACpD,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IACzD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,MAAqB,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,gCAAgC;IAClC,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Template-level fallback chain.
|
|
4
|
+
*
|
|
5
|
+
* Per-slot fallback (`candidate.models[]`) handles "if claude-opus fails, try
|
|
6
|
+
* claude-sonnet" — same lineage, same shim, same auth, just a different
|
|
7
|
+
* `--model` argv.
|
|
8
|
+
*
|
|
9
|
+
* Template-level fallback (`template.fallback[]`) is the catch-all that fires
|
|
10
|
+
* when ANY slot exhausts its per-slot chain. The user sets it once at the
|
|
11
|
+
* template root, and chorus applies it to every slot — cross-lineage swaps
|
|
12
|
+
* are first-class: a codex reviewer hitting quota can fall through to a
|
|
13
|
+
* claude or kimi fallback.
|
|
14
|
+
*
|
|
15
|
+
* Strict (lineage, model) dedup:
|
|
16
|
+
* - Skip a fallback row that matches the slot's own current model — would
|
|
17
|
+
* just fail again.
|
|
18
|
+
* - Skip a fallback row that matches ANOTHER active slot in the same
|
|
19
|
+
* phase. Example: reviewers=[kimi, deepseek] + fallback=[kimi]
|
|
20
|
+
* should NOT spawn a second kimi reviewer when deepseek fails.
|
|
21
|
+
* - Cross-lineage fallback dedup uses (lineage, model) tuples so two slots
|
|
22
|
+
* of different lineages on the same model name (rare) don't collide.
|
|
23
|
+
*
|
|
24
|
+
* Diversity-first ordering:
|
|
25
|
+
* When multiple fallbacks survive dedup, sort by lineage occurrence
|
|
26
|
+
* across active slots (least-represented first). With reviewers
|
|
27
|
+
* [openai, google, anthropic] and fallbacks [anthropic/haiku,
|
|
28
|
+
* moonshot/kimi], the kimi entry runs FIRST — moonshot has 0 active
|
|
29
|
+
* slots, anthropic has 1. Within a single lineage, user-declared
|
|
30
|
+
* order wins. Lets the user spec a long fallback list without having
|
|
31
|
+
* to manually micro-order it for diversity.
|
|
32
|
+
*
|
|
33
|
+
* Cross-lineage swap mechanics:
|
|
34
|
+
* When a fallback's lineage differs from the slot's, the runner re-resolves
|
|
35
|
+
* the shim from the agent registry (`pickShimForVoice(entry.lineage,
|
|
36
|
+
* entry.model)`) for that one attempt. The slot's identity (agentName,
|
|
37
|
+
* on-disk dir, participant key) stays bound to the slot's primary lineage
|
|
38
|
+
* so the cockpit card doesn't re-key mid-run; the runner emits a
|
|
39
|
+
* `cli_warning` with `reason: 'lineage_fallback'` so the UI can show
|
|
40
|
+
* "switched to claude-opus-4-7 (cross-lineage)".
|
|
41
|
+
*/
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.buildSlotFallbackChain = buildSlotFallbackChain;
|
|
44
|
+
/**
|
|
45
|
+
* Compose the slot's effective (lineage, model) chain by appending matching
|
|
46
|
+
* template fallbacks (deduped) onto the slot's per-slot chain. The chain
|
|
47
|
+
* mixes the slot's primary lineage with cross-lineage fallbacks at the
|
|
48
|
+
* tail; the runner walks it in order, picking the right shim per entry.
|
|
49
|
+
*
|
|
50
|
+
* Caller is responsible for passing a stable `lineage` value across all
|
|
51
|
+
* slots and template-fallback rows (don't mix cockpit-side and daemon-side
|
|
52
|
+
* names in the same call).
|
|
53
|
+
*
|
|
54
|
+
* @param slot The slot whose chain we're building (its primary +
|
|
55
|
+
* its per-slot fallbacks).
|
|
56
|
+
* @param activeSlots All slots in the same phase, including `slot`. Used
|
|
57
|
+
* to dedup template fallbacks that would duplicate an
|
|
58
|
+
* already-running voice.
|
|
59
|
+
* @param templateFallback The template-root `fallback` array (or undefined).
|
|
60
|
+
* @returns Extended (lineage, model) chain — slot.models first, then
|
|
61
|
+
* deduped template fallbacks (same-lineage and cross-lineage).
|
|
62
|
+
*/
|
|
63
|
+
function buildSlotFallbackChain(slot, activeSlots, templateFallback) {
|
|
64
|
+
const chain = (slot.models ?? []).map((m) => ({
|
|
65
|
+
lineage: slot.lineage,
|
|
66
|
+
model: m,
|
|
67
|
+
}));
|
|
68
|
+
// Slot with no models at all: emit one undefined entry so the runner makes
|
|
69
|
+
// exactly one attempt with the lineage default.
|
|
70
|
+
if (chain.length === 0) {
|
|
71
|
+
chain.push({ lineage: slot.lineage, model: undefined });
|
|
72
|
+
}
|
|
73
|
+
if (!templateFallback || templateFallback.length === 0)
|
|
74
|
+
return chain;
|
|
75
|
+
// Pre-compute the dedup set: every (lineage, model) currently active in
|
|
76
|
+
// this phase, including the slot's own per-slot fallbacks.
|
|
77
|
+
const skipKeys = new Set();
|
|
78
|
+
// Lineage-occurrence count across active slots — used to prefer
|
|
79
|
+
// cross-lineage fallbacks first ("most unique combination" = least
|
|
80
|
+
// represented lineage). A slot whose primary lineage is openai pulls
|
|
81
|
+
// the openai count up; a fallback into a lineage with count=0 wins
|
|
82
|
+
// ties over one with count=1.
|
|
83
|
+
const lineageCount = new Map();
|
|
84
|
+
for (const s of activeSlots) {
|
|
85
|
+
lineageCount.set(s.lineage, (lineageCount.get(s.lineage) ?? 0) + 1);
|
|
86
|
+
for (const m of s.models ?? []) {
|
|
87
|
+
skipKeys.add(`${s.lineage}:${m}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const candidates = [];
|
|
91
|
+
let declared = 0;
|
|
92
|
+
for (const fb of templateFallback) {
|
|
93
|
+
for (const m of fb.models ?? []) {
|
|
94
|
+
const key = `${fb.lineage}:${m}`;
|
|
95
|
+
if (skipKeys.has(key)) {
|
|
96
|
+
declared++;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
skipKeys.add(key);
|
|
100
|
+
candidates.push({ lineage: fb.lineage, model: m, declaredIdx: declared });
|
|
101
|
+
declared++;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Sort: lineages absent from active slots first (count=0), then less-
|
|
105
|
+
// represented lineages, then user-declared order within a lineage.
|
|
106
|
+
// Stable sort — equal keys preserve declared order.
|
|
107
|
+
candidates.sort((a, b) => {
|
|
108
|
+
const ca = lineageCount.get(a.lineage) ?? 0;
|
|
109
|
+
const cb = lineageCount.get(b.lineage) ?? 0;
|
|
110
|
+
if (ca !== cb)
|
|
111
|
+
return ca - cb;
|
|
112
|
+
return a.declaredIdx - b.declaredIdx;
|
|
113
|
+
});
|
|
114
|
+
for (const c of candidates) {
|
|
115
|
+
chain.push({ lineage: c.lineage, model: c.model });
|
|
116
|
+
}
|
|
117
|
+
return chain;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=template-fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-fallback.js","sourceRoot":"","sources":["../../../src/daemon/runner/template-fallback.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;;AA4CH,wDAuEC;AA1FD;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,sBAAsB,CACpC,IAAc,EACd,WAAgC,EAChC,gBAAoD;IAEpD,MAAM,KAAK,GAAiB,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1D,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,KAAK,EAAE,CAAC;KACT,CAAC,CAAC,CAAC;IAEJ,2EAA2E;IAC3E,gDAAgD;IAChD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,CAAC,gBAAgB,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAErE,wEAAwE;IACxE,2DAA2D;IAC3D,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,gEAAgE;IAChE,mEAAmE;IACnE,qEAAqE;IACrE,mEAAmE;IACnE,8BAA8B;IAC9B,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC/C,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACpE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YAC/B,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAUD,MAAM,UAAU,GAAc,EAAE,CAAC;IACjC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,EAAE,IAAI,gBAAgB,EAAE,CAAC;QAClC,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;YACjC,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,QAAQ,EAAE,CAAC;gBACX,SAAS;YACX,CAAC;YACD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAClB,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC1E,QAAQ,EAAE,CAAC;QACb,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,mEAAmE;IACnE,oDAAoD;IACpD,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,EAAE,GAAG,EAAE,CAAC;QAC9B,OAAO,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/daemon/runner/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verdictFromReviewerText = verdictFromReviewerText;
|
|
4
|
+
/**
|
|
5
|
+
* Reviewer-text → verdict heuristic.
|
|
6
|
+
*
|
|
7
|
+
* Reviewers don't return a structured boolean; they write English. This
|
|
8
|
+
* tries to extract `approve / request-changes / null` from their final
|
|
9
|
+
* answer.md, scanning the tail first (where verdicts typically live)
|
|
10
|
+
* before falling back to the whole text. Word-boundary regex matches
|
|
11
|
+
* avoid false positives like "approached" → approve.
|
|
12
|
+
*
|
|
13
|
+
* Returns:
|
|
14
|
+
* true = reviewer approved
|
|
15
|
+
* false = reviewer disagreed / requested changes
|
|
16
|
+
* null = ambiguous (caller should treat as failed/inconclusive)
|
|
17
|
+
*
|
|
18
|
+
* The 80-char floor protects against `## DONE`-only answers being
|
|
19
|
+
* counted as ambiguous-empty rather than auto-failures upstream.
|
|
20
|
+
*/
|
|
21
|
+
function verdictFromReviewerText(content) {
|
|
22
|
+
const stripped = content.replace(/##\s*DONE\s*$/i, '').trim();
|
|
23
|
+
// Contractions matter: a reviewer writing "don't approve" or "can't
|
|
24
|
+
// approve" would slip past the spaced-out forms below if we only matched
|
|
25
|
+
// `do not approve` / `cannot approve`. Both spellings are common in real
|
|
26
|
+
// reviews. The optional `['’]?t` segment catches both straight (') and
|
|
27
|
+
// typographic (’) apostrophes — LLMs emit the latter often.
|
|
28
|
+
const negatives = /\b(request changes|requesting changes|disagree|reject(?:ed|ing)?|blocker|(?:do not|don['’]?t) (?:approve|merge)|(?:cannot|can['’]?t) (?:approve|merge)|nack)\b/;
|
|
29
|
+
const positives = /\b(approve(?:d|s)?|lgtm|looks good to me|no concerns|ship it|ack)\b/;
|
|
30
|
+
// Check verdict keywords FIRST — a terse but explicit reply like
|
|
31
|
+
// "approve ## DONE" (15 chars after sentinel strip) is unambiguous and
|
|
32
|
+
// shouldn't be filtered out by the length floor. Tail wins over whole
|
|
33
|
+
// so an analytical review mentioning "good practice" mid-paragraph
|
|
34
|
+
// doesn't get auto-approved without an explicit verdict at the end.
|
|
35
|
+
const tail = stripped.slice(-400).toLowerCase();
|
|
36
|
+
if (negatives.test(tail))
|
|
37
|
+
return false;
|
|
38
|
+
if (positives.test(tail))
|
|
39
|
+
return true;
|
|
40
|
+
const whole = stripped.toLowerCase();
|
|
41
|
+
if (negatives.test(whole))
|
|
42
|
+
return false;
|
|
43
|
+
if (positives.test(whole))
|
|
44
|
+
return true;
|
|
45
|
+
// No verdict keyword anywhere — return null (ambiguous). The 20-char
|
|
46
|
+
// floor was previously applied BEFORE the regex, which dropped valid
|
|
47
|
+
// terse approvals like "approve ## DONE". It's no longer needed: short
|
|
48
|
+
// replies without a keyword still resolve to null here.
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=verdict.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verdict.js","sourceRoot":"","sources":["../../../src/daemon/runner/verdict.ts"],"names":[],"mappings":";;AAiBA,0DA+BC;AAhDD;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,uBAAuB,CAAC,OAAe;IACrD,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAE9D,oEAAoE;IACpE,yEAAyE;IACzE,yEAAyE;IACzE,uEAAuE;IACvE,4DAA4D;IAC5D,MAAM,SAAS,GACb,gKAAgK,CAAC;IACnK,MAAM,SAAS,GACb,qEAAqE,CAAC;IAExE,iEAAiE;IACjE,uEAAuE;IACvE,sEAAsE;IACtE,mEAAmE;IACnE,oEAAoE;IACpE,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAChD,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtC,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACrC,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,qEAAqE;IACrE,qEAAqE;IACrE,uEAAuE;IACvE,wDAAwD;IACxD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Multi-subscriber wrapper around runChat.
|
|
4
|
+
*
|
|
5
|
+
* Singleton runner registry — exactly one runChat per chatId, ever. SSE
|
|
6
|
+
* re-attachers (browser refresh, tab open, polling, MCP wait_for_chat)
|
|
7
|
+
* all subscribe to the same in-memory event bus instead of re-firing the
|
|
8
|
+
* runner. Without this, every refresh of the run page used to spawn a
|
|
9
|
+
* fresh doer + 2 reviewers, hammering the LLM CLIs and thrashing memory.
|
|
10
|
+
*
|
|
11
|
+
* onEvent fans out to every subscribed SSE and persists side effects
|
|
12
|
+
* exactly once, regardless of subscriber count.
|
|
13
|
+
*/
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.getActiveRun = getActiveRun;
|
|
49
|
+
exports.abortActiveRun = abortActiveRun;
|
|
50
|
+
exports.phaseEventToRunnerEvent = phaseEventToRunnerEvent;
|
|
51
|
+
exports.runWithMultiplex = runWithMultiplex;
|
|
52
|
+
exports.activeRunsSnapshot = activeRunsSnapshot;
|
|
53
|
+
exports.activeRunsCount = activeRunsCount;
|
|
54
|
+
const index_js_1 = require("../lib/db/index.js");
|
|
55
|
+
const logger_js_1 = require("../lib/logger.js");
|
|
56
|
+
const participantAborts = __importStar(require("./participant-aborts.js"));
|
|
57
|
+
const runner_js_1 = require("./runner.js");
|
|
58
|
+
const activeRuns = new Map();
|
|
59
|
+
function getActiveRun(chatId) {
|
|
60
|
+
return activeRuns.get(chatId);
|
|
61
|
+
}
|
|
62
|
+
function abortActiveRun(chatId) {
|
|
63
|
+
const active = activeRuns.get(chatId);
|
|
64
|
+
if (!active)
|
|
65
|
+
return false;
|
|
66
|
+
active.abortController.abort();
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Reconstruct a RunnerEvent from a persisted phase_events row. Used to
|
|
71
|
+
* replay past events to a freshly-attached SSE so the run page renders
|
|
72
|
+
* the history without waiting for the next live event. Returns null for
|
|
73
|
+
* rows we can't faithfully reconstruct.
|
|
74
|
+
*/
|
|
75
|
+
function phaseEventToRunnerEvent(chatId, ev) {
|
|
76
|
+
const baseType = ev.state === 'drafting'
|
|
77
|
+
? 'phase_start'
|
|
78
|
+
: ev.state === 'submitted'
|
|
79
|
+
? 'phase_done'
|
|
80
|
+
: ev.state === 'blocked'
|
|
81
|
+
? 'phase_failed'
|
|
82
|
+
: null;
|
|
83
|
+
if (!baseType) {
|
|
84
|
+
console.warn(`[chorus] phase event replay: unmapped state "${ev.state}" for chat ${chatId}`);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
chatId,
|
|
89
|
+
type: baseType,
|
|
90
|
+
payload: {
|
|
91
|
+
phaseIdx: ev.phase_idx,
|
|
92
|
+
kind: ev.phase_kind,
|
|
93
|
+
role: ev.role,
|
|
94
|
+
agent: ev.agent_id ?? undefined,
|
|
95
|
+
output: ev.output ?? undefined,
|
|
96
|
+
replay: true,
|
|
97
|
+
},
|
|
98
|
+
ts: ev.started_at,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const VALID_PHASE_KINDS = [
|
|
102
|
+
'plan',
|
|
103
|
+
'spec',
|
|
104
|
+
'tests',
|
|
105
|
+
'implement',
|
|
106
|
+
'review',
|
|
107
|
+
'verify',
|
|
108
|
+
'divergence',
|
|
109
|
+
'review_only',
|
|
110
|
+
];
|
|
111
|
+
const VALID_CHAT_STATUSES = [
|
|
112
|
+
'drafting',
|
|
113
|
+
'reviewing',
|
|
114
|
+
'approved',
|
|
115
|
+
'merged',
|
|
116
|
+
'blocked',
|
|
117
|
+
'cancelled',
|
|
118
|
+
'failed',
|
|
119
|
+
'no_review',
|
|
120
|
+
];
|
|
121
|
+
function parseAttachedFiles(raw) {
|
|
122
|
+
if (!raw)
|
|
123
|
+
return undefined;
|
|
124
|
+
try {
|
|
125
|
+
const parsed = JSON.parse(raw);
|
|
126
|
+
if (Array.isArray(parsed) && parsed.every((p) => typeof p === 'string')) {
|
|
127
|
+
return parsed;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
/* ignore */
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
function runWithMultiplex(args) {
|
|
136
|
+
const { chatId, template, chat, tmuxMgr, errorDetector } = args;
|
|
137
|
+
// Explicit cancellation goes through POST /chats/:id/cancel which calls
|
|
138
|
+
// entry.abortController.abort(). Closing an SSE does NOT abort.
|
|
139
|
+
const abortController = new AbortController();
|
|
140
|
+
const subscribers = new Set();
|
|
141
|
+
// Pending DB writes from onEvent. Fire-and-forget here would race
|
|
142
|
+
// against the activeRuns.delete in `.finally()` below — a reattaching
|
|
143
|
+
// SSE could see activeRuns empty (slot released) but read the stale
|
|
144
|
+
// chats row (status='reviewing') and start a duplicate run. Drain
|
|
145
|
+
// this set before releasing the slot.
|
|
146
|
+
const pendingWrites = new Set();
|
|
147
|
+
const trackWrite = (p) => {
|
|
148
|
+
pendingWrites.add(p);
|
|
149
|
+
p.finally(() => pendingWrites.delete(p));
|
|
150
|
+
return p;
|
|
151
|
+
};
|
|
152
|
+
const onEvent = (event) => {
|
|
153
|
+
const line = `data: ${JSON.stringify(event)}\n\n`;
|
|
154
|
+
const toRemove = [];
|
|
155
|
+
for (const sub of Array.from(subscribers)) {
|
|
156
|
+
try {
|
|
157
|
+
if (sub.paused) {
|
|
158
|
+
sub.queue.push(line);
|
|
159
|
+
if (sub.queue.length > 1000) {
|
|
160
|
+
// Queue cap exceeded; drop subscriber to prevent unbounded
|
|
161
|
+
// memory.
|
|
162
|
+
toRemove.push(sub);
|
|
163
|
+
sub.close();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const canContinue = sub.write(line);
|
|
168
|
+
if (!canContinue) {
|
|
169
|
+
// Buffer full; pause. Drain listener (set up by the SSE
|
|
170
|
+
// handler) flushes the queue when the kernel buffer recovers.
|
|
171
|
+
sub.paused = true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
/* dead subscriber */
|
|
177
|
+
toRemove.push(sub);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
for (const sub of toRemove) {
|
|
181
|
+
subscribers.delete(sub);
|
|
182
|
+
}
|
|
183
|
+
if (event.type === 'phase_start' ||
|
|
184
|
+
event.type === 'phase_done' ||
|
|
185
|
+
event.type === 'phase_failed') {
|
|
186
|
+
const payload = event.payload;
|
|
187
|
+
const kind = payload.kind;
|
|
188
|
+
const phaseKind = VALID_PHASE_KINDS.includes(kind)
|
|
189
|
+
? kind
|
|
190
|
+
: 'plan';
|
|
191
|
+
// Fire-and-forget — onEvent is typed `(e) => void` and is called
|
|
192
|
+
// synchronously from the runner; awaiting here would block the
|
|
193
|
+
// entire fan-out chain. SQLite serializes writes via WAL anyway.
|
|
194
|
+
// Tracked in pendingWrites so the .finally drain ensures DB state
|
|
195
|
+
// is consistent before activeRuns.delete fires.
|
|
196
|
+
void trackWrite(index_js_1.phaseEvents
|
|
197
|
+
.create({
|
|
198
|
+
chat_id: chatId,
|
|
199
|
+
phase_idx: payload.phaseIdx ?? 0,
|
|
200
|
+
phase_kind: phaseKind,
|
|
201
|
+
role: payload.role ?? 'doer',
|
|
202
|
+
agent_id: payload.agent ?? null,
|
|
203
|
+
state: event.type === 'phase_start'
|
|
204
|
+
? 'drafting'
|
|
205
|
+
: event.type === 'phase_done'
|
|
206
|
+
? 'submitted'
|
|
207
|
+
: 'blocked',
|
|
208
|
+
output: payload.output ?? null,
|
|
209
|
+
cost_usd: 0,
|
|
210
|
+
tokens_in: 0,
|
|
211
|
+
tokens_out: 0,
|
|
212
|
+
started_at: event.ts,
|
|
213
|
+
finished_at: event.type === 'phase_done' || event.type === 'phase_failed'
|
|
214
|
+
? Date.now()
|
|
215
|
+
: null,
|
|
216
|
+
})
|
|
217
|
+
.catch((err) => {
|
|
218
|
+
(0, logger_js_1.chatLogger)(chatId).error({ err: err instanceof Error ? err.message : String(err) }, 'phaseEvents.create failed');
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
// Persist per-CLI failure events (cli_error / cli_warning) so the
|
|
222
|
+
// cockpit can surface "this reviewer failed with <reason>" on the
|
|
223
|
+
// per-card UI AND post-mortem inspection (sqlite, /chats/:id) shows
|
|
224
|
+
// the failure even after chat-done has fired. Without this, transient
|
|
225
|
+
// subprocess crashes (opencode lock contention, codex quota, gemini
|
|
226
|
+
// rate-limit-with-empty-stdout) wrote 0-byte answer.md files and
|
|
227
|
+
// disappeared from the DB.
|
|
228
|
+
//
|
|
229
|
+
// cli_error ⇒ state='errored', cli_warning ⇒ state='warning'. The
|
|
230
|
+
// replay path (phaseEventToRunnerEvent) ignores both states the same
|
|
231
|
+
// way it always has, so live subscribers are unaffected. Pre-fix every
|
|
232
|
+
// cli_warning landed as state='errored', which made a successful
|
|
233
|
+
// per-slot model fallback look like a reviewer crash in the audit
|
|
234
|
+
// trail.
|
|
235
|
+
if (event.type === 'cli_error' || event.type === 'cli_warning') {
|
|
236
|
+
const payload = event.payload;
|
|
237
|
+
const kind = payload.phaseKind;
|
|
238
|
+
const phaseKind = kind && VALID_PHASE_KINDS.includes(kind)
|
|
239
|
+
? kind
|
|
240
|
+
: 'review';
|
|
241
|
+
const errorObj = payload.error ?? {};
|
|
242
|
+
const message = errorObj.message ??
|
|
243
|
+
payload.message ??
|
|
244
|
+
'unknown error';
|
|
245
|
+
const isWarning = event.type === 'cli_warning';
|
|
246
|
+
const persistedState = isWarning ? 'warning' : 'errored';
|
|
247
|
+
const tag = errorObj.kind ??
|
|
248
|
+
(isWarning ? 'cli_warning' : 'cli_error');
|
|
249
|
+
void trackWrite(index_js_1.phaseEvents
|
|
250
|
+
.create({
|
|
251
|
+
chat_id: chatId,
|
|
252
|
+
phase_idx: payload.phaseIdx ?? 0,
|
|
253
|
+
phase_kind: phaseKind,
|
|
254
|
+
role: payload.role ?? 'reviewer',
|
|
255
|
+
agent_id: payload.agent ?? null,
|
|
256
|
+
state: persistedState,
|
|
257
|
+
// Pack the failure / warning context into output so the
|
|
258
|
+
// cockpit's existing event-list rendering surfaces the
|
|
259
|
+
// message without a schema change.
|
|
260
|
+
output: `[${tag}] ${message}`,
|
|
261
|
+
cost_usd: 0,
|
|
262
|
+
tokens_in: 0,
|
|
263
|
+
tokens_out: 0,
|
|
264
|
+
started_at: event.ts,
|
|
265
|
+
finished_at: event.ts,
|
|
266
|
+
})
|
|
267
|
+
.catch((err) => {
|
|
268
|
+
(0, logger_js_1.chatLogger)(chatId).error({ err: err instanceof Error ? err.message : String(err) }, `phaseEvents.create (${persistedState}) failed`);
|
|
269
|
+
}));
|
|
270
|
+
}
|
|
271
|
+
// Update chats.status on terminal event. Runner emits status='completed'
|
|
272
|
+
// for the happy path; we map to 'approved' to fit the chats.status
|
|
273
|
+
// enum. Tracked so .finally drains before releasing the activeRuns
|
|
274
|
+
// slot — otherwise a reattaching SSE could see no active run + stale
|
|
275
|
+
// 'reviewing' status and start a dup run.
|
|
276
|
+
if (event.type === 'chat_done') {
|
|
277
|
+
const payload = event.payload;
|
|
278
|
+
const status = payload.status ?? 'completed';
|
|
279
|
+
// verdict is the reviewer-level outcome (separate from system-level
|
|
280
|
+
// status). Always persist when present so review-only chats with
|
|
281
|
+
// verdict='request_changes' are distinguishable from standard chats
|
|
282
|
+
// whose status='approved' implicitly means verdict='approved'. Cap
|
|
283
|
+
// defensively at 32 chars — verdicts are enum-shaped strings;
|
|
284
|
+
// anything longer is bogus.
|
|
285
|
+
const rawVerdict = payload.verdict;
|
|
286
|
+
const verdict = typeof rawVerdict === 'string' && rawVerdict.length > 0 && rawVerdict.length <= 32
|
|
287
|
+
? rawVerdict
|
|
288
|
+
: null;
|
|
289
|
+
void trackWrite(index_js_1.chats
|
|
290
|
+
.update(chatId, {
|
|
291
|
+
status: (status === 'completed' ? 'approved' : status),
|
|
292
|
+
...(verdict !== null ? { verdict } : {}),
|
|
293
|
+
...(typeof payload.prUrl === 'string' && payload.prUrl.length > 0
|
|
294
|
+
? { pr_url: payload.prUrl }
|
|
295
|
+
: {}),
|
|
296
|
+
...(typeof payload.shipError === 'string' && payload.shipError.length > 0
|
|
297
|
+
? { ship_error: payload.shipError }
|
|
298
|
+
: {}),
|
|
299
|
+
finished_at: Date.now(),
|
|
300
|
+
})
|
|
301
|
+
.catch((err) => {
|
|
302
|
+
(0, logger_js_1.chatLogger)(chatId).error({ err: err instanceof Error ? err.message : String(err) }, 'chats.update on chat_done failed');
|
|
303
|
+
}));
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
const promise = (0, runner_js_1.runChat)({
|
|
307
|
+
chatId,
|
|
308
|
+
template,
|
|
309
|
+
work: chat.work,
|
|
310
|
+
artifact: chat.artifact ?? undefined,
|
|
311
|
+
repoPath: chat.repo_path ?? undefined,
|
|
312
|
+
attachedFiles: parseAttachedFiles(chat.attached_files),
|
|
313
|
+
abortSignal: abortController.signal,
|
|
314
|
+
tmuxMgr,
|
|
315
|
+
errorDetector,
|
|
316
|
+
onEvent,
|
|
317
|
+
}).finally(async () => {
|
|
318
|
+
// Drain pending DB writes BEFORE releasing the slot. Without this,
|
|
319
|
+
// the chat_done chats.update can still be in flight when a
|
|
320
|
+
// reattaching SSE sees activeRuns empty + reads stale chats row →
|
|
321
|
+
// starts a duplicate run, burns subscription quota, writes duplicate
|
|
322
|
+
// phase events. allSettled so a failed write doesn't leak unhandled
|
|
323
|
+
// rejections — individual .catch handlers above already log.
|
|
324
|
+
if (pendingWrites.size > 0) {
|
|
325
|
+
await Promise.allSettled(Array.from(pendingWrites));
|
|
326
|
+
}
|
|
327
|
+
activeRuns.delete(chatId);
|
|
328
|
+
// Server-initiated subscriber close. The cockpit closes its
|
|
329
|
+
// EventSource on chat_done already, but a misbehaving / disconnected
|
|
330
|
+
// client can leave the subscriber object pinned in the set. Without
|
|
331
|
+
// this sweep the underlying hijacked socket lingers (held open by
|
|
332
|
+
// Fastify's raw.write reference) until the OS TCP keepalive reaps
|
|
333
|
+
// it. close() swallows its own errors.
|
|
334
|
+
for (const sub of Array.from(subscribers)) {
|
|
335
|
+
try {
|
|
336
|
+
sub.close();
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
/* dead socket — already closed by the client */
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
subscribers.clear();
|
|
343
|
+
// Drop any per-participant abort controllers left over by aborted /
|
|
344
|
+
// crashed runners. They should already have released themselves via
|
|
345
|
+
// their `finally` blocks, but a stale entry would leak across chats
|
|
346
|
+
// if a runner exited abnormally.
|
|
347
|
+
participantAborts.cleanupChat(chatId);
|
|
348
|
+
});
|
|
349
|
+
const entry = { promise, subscribers, abortController };
|
|
350
|
+
activeRuns.set(chatId, entry);
|
|
351
|
+
return entry;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Snapshot of all active runs — for graceful shutdown only. Don't use
|
|
355
|
+
* for steady-state route handling; getActiveRun(chatId) is the right
|
|
356
|
+
* accessor for the chat-keyed lookup.
|
|
357
|
+
*/
|
|
358
|
+
function activeRunsSnapshot() {
|
|
359
|
+
return Array.from(activeRuns.values());
|
|
360
|
+
}
|
|
361
|
+
function activeRunsCount() {
|
|
362
|
+
return activeRuns.size;
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=runner-multiplex.js.map
|