chorus-codes 0.8.24 → 0.8.26
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/app-path-routes-manifest.json +1 -1
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +3 -3
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- 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/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error.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/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- 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 +2 -2
- package/.next/server/app/connect/page.js +1 -1
- package/.next/server/app/connect/page.js.nft.json +1 -1
- package/.next/server/app/connect/page_client-reference-manifest.js +1 -1
- package/.next/server/app/demo/[scenario]/page.js +1 -1
- package/.next/server/app/demo/[scenario]/page.js.nft.json +1 -1
- package/.next/server/app/demo/[scenario]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/new/page.js +1 -1
- package/.next/server/app/new/page.js.nft.json +1 -1
- package/.next/server/app/new/page_client-reference-manifest.js +1 -1
- package/.next/server/app/new.html +1 -1
- package/.next/server/app/new.rsc +3 -3
- package/.next/server/app/new.segments/_full.segment.rsc +3 -3
- package/.next/server/app/new.segments/_head.segment.rsc +1 -1
- package/.next/server/app/new.segments/_index.segment.rsc +2 -2
- package/.next/server/app/new.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/new.segments/new/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/new.segments/new.segment.rsc +1 -1
- package/.next/server/app/onboarding/page.js +1 -1
- package/.next/server/app/onboarding/page.js.nft.json +1 -1
- package/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
- package/.next/server/app/onboarding.html +1 -1
- package/.next/server/app/onboarding.rsc +3 -3
- package/.next/server/app/onboarding.segments/_full.segment.rsc +3 -3
- package/.next/server/app/onboarding.segments/_head.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/_index.segment.rsc +2 -2
- package/.next/server/app/onboarding.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/onboarding.segments/onboarding/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/onboarding.segments/onboarding.segment.rsc +1 -1
- package/.next/server/app/page.js +2 -2
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/personas/page.js +1 -1
- package/.next/server/app/personas/page.js.nft.json +1 -1
- package/.next/server/app/personas/page_client-reference-manifest.js +1 -1
- package/.next/server/app/personas.html +1 -1
- package/.next/server/app/personas.rsc +3 -3
- package/.next/server/app/personas.segments/_full.segment.rsc +3 -3
- package/.next/server/app/personas.segments/_head.segment.rsc +1 -1
- package/.next/server/app/personas.segments/_index.segment.rsc +2 -2
- package/.next/server/app/personas.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/personas.segments/personas/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/personas.segments/personas.segment.rsc +1 -1
- package/.next/server/app/runs/[runId]/page.js +2 -2
- package/.next/server/app/runs/[runId]/page.js.nft.json +1 -1
- package/.next/server/app/runs/[runId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/runs/page.js +2 -2
- package/.next/server/app/runs/page.js.nft.json +1 -1
- package/.next/server/app/runs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/page.js +3 -3
- package/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/permissions/page.js +1 -1
- package/.next/server/app/settings/permissions/page.js.nft.json +1 -1
- package/.next/server/app/settings/permissions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings.html +1 -1
- package/.next/server/app/settings.rsc +3 -3
- package/.next/server/app/settings.segments/_full.segment.rsc +3 -3
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +2 -2
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/server/app/templates/page.js +2 -2
- package/.next/server/app/templates/page.js.nft.json +1 -1
- package/.next/server/app/templates/page_client-reference-manifest.js +1 -1
- package/.next/server/app/templates.html +1 -1
- package/.next/server/app/templates.rsc +3 -3
- package/.next/server/app/templates.segments/_full.segment.rsc +3 -3
- package/.next/server/app/templates.segments/_head.segment.rsc +1 -1
- package/.next/server/app/templates.segments/_index.segment.rsc +2 -2
- package/.next/server/app/templates.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/templates.segments/templates/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/templates.segments/templates.segment.rsc +1 -1
- package/.next/server/app-paths-manifest.json +1 -1
- package/.next/server/chunks/189.js +1 -0
- package/.next/server/chunks/{144.js → 21.js} +1 -1
- package/.next/server/chunks/{668.js → 313.js} +1 -1
- package/.next/server/chunks/681.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/249-2e840495c38ee022.js +25 -0
- package/.next/static/chunks/641-2908cb9553b8753a.js +1 -0
- package/.next/static/chunks/690-092c26db4082d49a.js +1 -0
- package/.next/static/chunks/app/connect/{page-a3a0af374f90ad4c.js → page-ad4409761e870bd0.js} +1 -1
- package/.next/static/chunks/app/demo/[scenario]/{page-6a0e4aec4bb96fee.js → page-39673968f543c473.js} +1 -1
- package/.next/static/chunks/app/new/{page-b96d75506030acf8.js → page-b5f609ab9413ac00.js} +1 -1
- package/.next/static/chunks/app/onboarding/{page-4be5a1d944e32672.js → page-8b0850fef487abdc.js} +1 -1
- package/.next/static/chunks/app/{page-35375a7c8b3d117a.js → page-9bdbaad592d0ce56.js} +1 -1
- package/.next/static/chunks/app/personas/{page-3884f8907107a4e6.js → page-440c6033a773100c.js} +1 -1
- package/.next/static/chunks/app/runs/[runId]/{page-b5bcf0c093389207.js → page-ffc36f12f1b63ebe.js} +1 -1
- package/.next/static/chunks/app/runs/{page-376175c1ac803558.js → page-6962ea572c9e4b74.js} +1 -1
- package/.next/static/chunks/app/settings/page-ad7180ee0d142704.js +25 -0
- package/.next/static/chunks/app/settings/permissions/{page-c90795aa9299bbe8.js → page-cd767401ac71a29c.js} +1 -1
- package/.next/static/chunks/app/templates/page-0112ab3c7ab5185d.js +1 -0
- package/.next/static/css/df4972a256406ec7.css +3 -0
- package/.next/trace +20 -20
- package/.next/trace-build +1 -1
- package/dist/daemon/cli-semaphore.js +266 -0
- package/dist/daemon/cli-semaphore.js.map +1 -0
- package/dist/daemon/routes/settings.js +39 -0
- package/dist/daemon/routes/settings.js.map +1 -1
- package/dist/daemon/runner/doer-driver.js +243 -214
- package/dist/daemon/runner/doer-driver.js.map +1 -1
- package/dist/daemon/runner/doer.js +24 -1
- package/dist/daemon/runner/doer.js.map +1 -1
- package/dist/daemon/runner/reviewer-driver.js +299 -255
- package/dist/daemon/runner/reviewer-driver.js.map +1 -1
- package/dist/daemon/runner/reviewer.js +27 -1
- package/dist/daemon/runner/reviewer.js.map +1 -1
- package/dist/daemon/runner.js +19 -0
- package/dist/daemon/runner.js.map +1 -1
- package/dist/lib/db/chats.js +28 -0
- package/dist/lib/db/chats.js.map +1 -1
- package/dist/lib/db/connection.js +6 -0
- package/dist/lib/db/connection.js.map +1 -1
- package/dist/lib/db/schema.sql +6 -0
- package/dist/lib/model-pricing.js +306 -0
- package/dist/lib/model-pricing.js.map +1 -0
- package/dist/lib/settings/concurrency.js +101 -0
- package/dist/lib/settings/concurrency.js.map +1 -0
- package/package.json +1 -1
- package/.next/server/chunks/946.js +0 -1
- package/.next/static/chunks/116-8bf7e014066cedde.js +0 -25
- package/.next/static/chunks/15-d438a2b057302bed.js +0 -1
- package/.next/static/chunks/641-60721f44faf711b9.js +0 -1
- package/.next/static/chunks/app/settings/page-1792a3e289409b2d.js +0 -25
- package/.next/static/chunks/app/templates/page-1449b0aea2e7cb68.js +0 -1
- package/.next/static/css/d2bb161eb5bee944.css +0 -3
- /package/.next/static/{jdUcCHB2b5lVrf4v8iQjl → eOeXty5cBGWg7xnmtF6ST}/_buildManifest.js +0 -0
- /package/.next/static/{jdUcCHB2b5lVrf4v8iQjl → eOeXty5cBGWg7xnmtF6ST}/_ssgManifest.js +0 -0
|
@@ -45,6 +45,8 @@ const cli_precheck_js_1 = require("../../lib/cli-precheck.js");
|
|
|
45
45
|
const index_js_1 = require("../../lib/db/index.js");
|
|
46
46
|
const permissions_js_1 = require("../../lib/settings/permissions.js");
|
|
47
47
|
const transport_js_1 = require("../../lib/settings/transport.js");
|
|
48
|
+
const concurrency_js_1 = require("../../lib/settings/concurrency.js");
|
|
49
|
+
const cli_semaphore_js_1 = require("../cli-semaphore.js");
|
|
48
50
|
const index_js_2 = require("../agents/index.js");
|
|
49
51
|
const output_watcher_js_1 = require("../output-watcher.js");
|
|
50
52
|
const participantAborts = __importStar(require("../participant-aborts.js"));
|
|
@@ -58,12 +60,13 @@ async function runDoer(chatDir, chatId, phase, phaseIdx, round, work, filesBlock
|
|
|
58
60
|
const doerModel = phase.doer.models?.[0];
|
|
59
61
|
const shim = (0, index_js_2.pickShimForVoice)(phase.doer.lineage, doerModel);
|
|
60
62
|
const agentName = shim.name;
|
|
63
|
+
const isHttp = (0, index_js_2.isHttpDispatchedShim)(shim);
|
|
61
64
|
// Pre-spawn precheck: short-circuit doomed runs without paying the spawn
|
|
62
65
|
// tax. Two cheap layers: (1) recent quota_exhausted with future resetAt,
|
|
63
66
|
// (2) credential file missing → user not logged in. HTTP-dispatched shims
|
|
64
67
|
// (openrouter) skip this — their auth is the secrets table, checked
|
|
65
68
|
// inside the shim itself.
|
|
66
|
-
if (!
|
|
69
|
+
if (!isHttp) {
|
|
67
70
|
const preDoer = await (0, cli_precheck_js_1.precheckLineage)(phase.doer.lineage);
|
|
68
71
|
if (!preDoer.ok) {
|
|
69
72
|
onEvent({
|
|
@@ -85,6 +88,24 @@ async function runDoer(chatDir, chatId, phase, phaseIdx, round, work, filesBlock
|
|
|
85
88
|
return null;
|
|
86
89
|
}
|
|
87
90
|
}
|
|
91
|
+
// Acquire daemon-wide CLI slot (global cap + per-lineage cap). Local
|
|
92
|
+
// CLI only — HTTP-dispatched shims (openrouter) bypass. The slot is
|
|
93
|
+
// held until the doer returns; cross-lineage fallback within the slot
|
|
94
|
+
// doesn't refresh the slot (conservative — see reviewer-driver for
|
|
95
|
+
// the same trade-off). The abortSignal lets a cancelled chat unwind
|
|
96
|
+
// a queued doer without blocking the semaphore head forever.
|
|
97
|
+
// The outer try/finally below guarantees release on every exit path.
|
|
98
|
+
let releaseSlot = null;
|
|
99
|
+
if (!isHttp && concurrency_js_1.CLI_LINEAGES.includes(agentName)) {
|
|
100
|
+
try {
|
|
101
|
+
releaseSlot = await (0, cli_semaphore_js_1.acquire)(agentName, abortSignal);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Aborted while queued — bail without spawning. Phase loop
|
|
105
|
+
// already treats null doer return as "doer failed".
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
88
109
|
const roundDir = path_1.default.join(chatDir, `round-${round}`);
|
|
89
110
|
const doerDir = path_1.default.join(roundDir, `doer-${agentName}`);
|
|
90
111
|
if (!fs_1.default.existsSync(doerDir)) {
|
|
@@ -92,20 +113,42 @@ async function runDoer(chatDir, chatId, phase, phaseIdx, round, work, filesBlock
|
|
|
92
113
|
}
|
|
93
114
|
const askFile = path_1.default.join(doerDir, 'ask.md');
|
|
94
115
|
const answerFile = path_1.default.join(doerDir, 'answer.md');
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
116
|
+
// Outer try/finally guarantees the cli-semaphore slot is released on
|
|
117
|
+
// every exit path (return null, throw, headless return, tmux return).
|
|
118
|
+
// releaseSlot is null for HTTP shims; the optional-call is the guard.
|
|
119
|
+
try {
|
|
120
|
+
// Resolve doer persona. Falls back to no-persona prompt when the id can't
|
|
121
|
+
// be resolved — emits cli_warning so the cockpit can surface the
|
|
122
|
+
// misconfiguration. Without the warning, retroactive PR #17 review
|
|
123
|
+
// (gemini + opencode-deepseek + opencode-kimi) flagged that a user
|
|
124
|
+
// typoing a persona id silently runs the chat with a generic prompt.
|
|
125
|
+
let doerPersonaPrompt;
|
|
126
|
+
if ('persona' in phase.doer && phase.doer.persona) {
|
|
127
|
+
const personaId = phase.doer.persona;
|
|
128
|
+
try {
|
|
129
|
+
const row = await index_js_1.personas.getById(personaId);
|
|
130
|
+
if (row) {
|
|
131
|
+
doerPersonaPrompt = row.system_prompt;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
onEvent({
|
|
135
|
+
chatId,
|
|
136
|
+
type: 'cli_warning',
|
|
137
|
+
payload: {
|
|
138
|
+
phaseId: phase.id,
|
|
139
|
+
phaseIdx,
|
|
140
|
+
round,
|
|
141
|
+
role: 'doer',
|
|
142
|
+
agent: agentName,
|
|
143
|
+
kind: 'persona_missing',
|
|
144
|
+
message: `Doer persona "${personaId}" not found in personas table — running with generic prompt. Check the template's doer.persona field.`,
|
|
145
|
+
},
|
|
146
|
+
ts: Date.now(),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
107
149
|
}
|
|
108
|
-
|
|
150
|
+
catch (err) {
|
|
151
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
109
152
|
onEvent({
|
|
110
153
|
chatId,
|
|
111
154
|
type: 'cli_warning',
|
|
@@ -115,179 +158,65 @@ async function runDoer(chatDir, chatId, phase, phaseIdx, round, work, filesBlock
|
|
|
115
158
|
round,
|
|
116
159
|
role: 'doer',
|
|
117
160
|
agent: agentName,
|
|
118
|
-
kind: '
|
|
119
|
-
message: `Doer persona "${personaId}"
|
|
161
|
+
kind: 'persona_lookup_failed',
|
|
162
|
+
message: `Doer persona lookup for "${personaId}" failed: ${message} — running with generic prompt.`,
|
|
120
163
|
},
|
|
121
164
|
ts: Date.now(),
|
|
122
165
|
});
|
|
123
166
|
}
|
|
124
167
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
try {
|
|
163
|
-
const doerSlot = {
|
|
164
|
-
lineage: phase.doer.lineage,
|
|
165
|
-
models: phase.doer.models ?? [],
|
|
166
|
-
};
|
|
167
|
-
const chain = (0, template_fallback_js_1.buildSlotFallbackChain)(doerSlot, [doerSlot], templateFallbackDoer);
|
|
168
|
-
return await (0, run_with_fallback_js_1.runWithChainFallback)(chain, async (entry) => {
|
|
169
|
-
// Cross-lineage swap: when the entry's lineage differs from the
|
|
170
|
-
// doer's primary, re-resolve the shim. Slot identity (agentName,
|
|
171
|
-
// doerDir) stays bound to the primary lineage; cli_warning below
|
|
172
|
-
// surfaces the swap to the cockpit.
|
|
173
|
-
const entryShim = entry.lineage === phase.doer.lineage
|
|
174
|
-
? shim
|
|
175
|
-
: (0, index_js_2.pickShimForVoice)(entry.lineage, entry.model);
|
|
176
|
-
return (0, doer_js_1.runDoerHeadless)({
|
|
177
|
-
shim: entryShim,
|
|
178
|
-
chatId,
|
|
179
|
-
phase,
|
|
180
|
-
round,
|
|
181
|
-
agentName,
|
|
182
|
-
askContent: ask,
|
|
183
|
-
answerFile,
|
|
184
|
-
doerCwd,
|
|
185
|
-
abortSignal: handle.signal,
|
|
186
|
-
onEvent,
|
|
187
|
-
modelOverride: entry.model,
|
|
188
|
-
});
|
|
189
|
-
}, (from, to, fromIdx) => {
|
|
190
|
-
const sameLineage = from.lineage === to.lineage;
|
|
191
|
-
const reason = sameLineage ? 'model_fallback' : 'lineage_fallback';
|
|
192
|
-
const message = sameLineage
|
|
193
|
-
? `Doer model "${from.model ?? '(default)'}" produced no answer; retrying with "${to.model ?? '(default)'}".`
|
|
194
|
-
: `Doer ${from.lineage}/${from.model ?? '(default)'} failed; switching to ${to.lineage}/${to.model ?? '(default)'} (cross-lineage fallback).`;
|
|
195
|
-
onEvent({
|
|
196
|
-
chatId,
|
|
197
|
-
type: 'cli_warning',
|
|
198
|
-
payload: {
|
|
199
|
-
phaseId: phase.id,
|
|
168
|
+
const ask = (0, prompt_builder_js_1.buildAsk)(phase, phaseIdx, round, work, phase.inputs, filesBlock, doerPersonaPrompt, priorRoundFeedback);
|
|
169
|
+
fs_1.default.writeFileSync(askFile, ask);
|
|
170
|
+
// When the chat was created with a repoPath, the doer's working tree
|
|
171
|
+
// becomes the user's repo (so it can read files + make real edits the
|
|
172
|
+
// ship phase will commit). Reviewers always stay in scratch — they're
|
|
173
|
+
// not allowed to write to the user's repo. ask.md/answer.md still live
|
|
174
|
+
// in the chat dir for artifact viewing.
|
|
175
|
+
const doerCwd = repoPath ?? doerDir;
|
|
176
|
+
// Transport branch: headless when settings + shim support it; else fall
|
|
177
|
+
// through to tmux. Mixed-mode in a single chat is OK — Claude can run
|
|
178
|
+
// headless while a Gemini reviewer falls back to tmux.
|
|
179
|
+
//
|
|
180
|
+
// Per-slot model fallback: phase.doer.models can list multiple models.
|
|
181
|
+
// The chain extends with template.fallback.doer (same lineage, dedup'd).
|
|
182
|
+
// Doer has only one slot, so the dedup just guards against re-trying
|
|
183
|
+
// the slot's own model.
|
|
184
|
+
const transport = await (0, transport_js_1.getTransport)();
|
|
185
|
+
if (transport === 'headless' && shim.runHeadless) {
|
|
186
|
+
const handle = participantAborts.register(chatId, participantAborts.participantKey('doer', agentName), abortSignal);
|
|
187
|
+
try {
|
|
188
|
+
const doerSlot = {
|
|
189
|
+
lineage: phase.doer.lineage,
|
|
190
|
+
models: phase.doer.models ?? [],
|
|
191
|
+
};
|
|
192
|
+
const chain = (0, template_fallback_js_1.buildSlotFallbackChain)(doerSlot, [doerSlot], templateFallbackDoer);
|
|
193
|
+
return await (0, run_with_fallback_js_1.runWithChainFallback)(chain, async (entry) => {
|
|
194
|
+
// Cross-lineage swap: when the entry's lineage differs from the
|
|
195
|
+
// doer's primary, re-resolve the shim. Slot identity (agentName,
|
|
196
|
+
// doerDir) stays bound to the primary lineage; cli_warning below
|
|
197
|
+
// surfaces the swap to the cockpit.
|
|
198
|
+
const entryShim = entry.lineage === phase.doer.lineage
|
|
199
|
+
? shim
|
|
200
|
+
: (0, index_js_2.pickShimForVoice)(entry.lineage, entry.model);
|
|
201
|
+
return (0, doer_js_1.runDoerHeadless)({
|
|
202
|
+
shim: entryShim,
|
|
203
|
+
chatId,
|
|
204
|
+
phase,
|
|
200
205
|
round,
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
// doerCwd was overridden to the user's repo.
|
|
216
|
-
(0, swap_sidecar_js_1.appendSwapSidecar)(doerDir, {
|
|
217
|
-
round,
|
|
218
|
-
phaseId: phase.id,
|
|
219
|
-
role: 'doer',
|
|
220
|
-
agent: agentName,
|
|
221
|
-
reason,
|
|
222
|
-
fromLineage: from.lineage,
|
|
223
|
-
toLineage: to.lineage,
|
|
224
|
-
fromModel: from.model ?? '(default)',
|
|
225
|
-
toModel: to.model ?? '(default)',
|
|
226
|
-
fallbackIdx: fromIdx,
|
|
227
|
-
ts: Date.now(),
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
finally {
|
|
232
|
-
handle.release();
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
// Acquire session — fresh per chat by default; reuses across rounds when
|
|
236
|
-
// template policy says so (shareSessionAcrossRounds, default true).
|
|
237
|
-
const perms = await (0, permissions_js_1.getPermissions)();
|
|
238
|
-
const sessionName = (0, sanitize_name_js_1.sanitizeName)(`chorus-${chatId}-${phase.id}-doer-${agentName}`);
|
|
239
|
-
const session = await tmuxMgr.acquire({
|
|
240
|
-
chatId,
|
|
241
|
-
phaseId: phase.id,
|
|
242
|
-
role: 'doer',
|
|
243
|
-
round,
|
|
244
|
-
shareSessionAcrossRounds: phase.iterate.shareSessionAcrossRounds,
|
|
245
|
-
shareSessionAcrossPhases: phase.iterate.shareSessionAcrossPhases,
|
|
246
|
-
shim,
|
|
247
|
-
spawnOpts: {
|
|
248
|
-
sessionName,
|
|
249
|
-
cwd: doerCwd,
|
|
250
|
-
model: phase.doer.models?.[0],
|
|
251
|
-
sandbox: perms.sandboxProfile,
|
|
252
|
-
autoApprove: perms.autoApprovePrompts,
|
|
253
|
-
networkAccess: perms.networkAccess,
|
|
254
|
-
},
|
|
255
|
-
agentName,
|
|
256
|
-
});
|
|
257
|
-
if (shim.clearKeys && shim.clearKeys.length > 0) {
|
|
258
|
-
tmuxMgr.sendKeys(session.name, [...shim.clearKeys]);
|
|
259
|
-
}
|
|
260
|
-
if (shim.preNudge)
|
|
261
|
-
shim.preNudge(session.name);
|
|
262
|
-
const prompt = shim.formatPrompt({
|
|
263
|
-
promptFile: askFile,
|
|
264
|
-
answerFile,
|
|
265
|
-
task: phase.title,
|
|
266
|
-
expectDoneSentinel: true,
|
|
267
|
-
});
|
|
268
|
-
// Wait for the CLI's TUI to finish cold-start before pasting. 6s covers
|
|
269
|
-
// Codex's slow cold-start (it auths + paints panels); shorter and the
|
|
270
|
-
// Enter we send below races against the input box being ready and gets
|
|
271
|
-
// eaten. Raise if a slower box still misses the prompt.
|
|
272
|
-
await new Promise((r) => setTimeout(r, 6000));
|
|
273
|
-
tmuxMgr.pasteBuffer(session.name, prompt);
|
|
274
|
-
// Small gap between paste and Enter so the TUI registers the paste before
|
|
275
|
-
// we submit.
|
|
276
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
277
|
-
tmuxMgr.sendKeys(session.name, ['Enter']);
|
|
278
|
-
// Poll capture-pane every 2s to surface known CLI failure modes while we
|
|
279
|
-
// wait for the answer file. The detector is stateful for opencode's
|
|
280
|
-
// sustained-error pattern.
|
|
281
|
-
const pollHandle = setInterval(() => {
|
|
282
|
-
try {
|
|
283
|
-
const pane = tmuxMgr.capturePane(session.name);
|
|
284
|
-
const err = errorDetector.inspect(session.name, phase.doer.lineage, pane);
|
|
285
|
-
if (err) {
|
|
286
|
-
const recoveryKeys = err.kind === 'permission_prompt' ? shim.recoverKeys?.permission_prompt : undefined;
|
|
287
|
-
if (recoveryKeys && recoveryKeys.length > 0) {
|
|
288
|
-
// Layer 2 recovery: navigate the dialog, emit a warning (not error),
|
|
289
|
-
// skip health recording — we recovered, no degradation.
|
|
290
|
-
tmuxMgr.sendKeys(session.name, [...recoveryKeys]);
|
|
206
|
+
agentName,
|
|
207
|
+
askContent: ask,
|
|
208
|
+
answerFile,
|
|
209
|
+
doerCwd,
|
|
210
|
+
abortSignal: handle.signal,
|
|
211
|
+
onEvent,
|
|
212
|
+
modelOverride: entry.model,
|
|
213
|
+
});
|
|
214
|
+
}, (from, to, fromIdx) => {
|
|
215
|
+
const sameLineage = from.lineage === to.lineage;
|
|
216
|
+
const reason = sameLineage ? 'model_fallback' : 'lineage_fallback';
|
|
217
|
+
const message = sameLineage
|
|
218
|
+
? `Doer model "${from.model ?? '(default)'}" produced no answer; retrying with "${to.model ?? '(default)'}".`
|
|
219
|
+
: `Doer ${from.lineage}/${from.model ?? '(default)'} failed; switching to ${to.lineage}/${to.model ?? '(default)'} (cross-lineage fallback).`;
|
|
291
220
|
onEvent({
|
|
292
221
|
chatId,
|
|
293
222
|
type: 'cli_warning',
|
|
@@ -296,51 +225,151 @@ async function runDoer(chatDir, chatId, phase, phaseIdx, round, work, filesBlock
|
|
|
296
225
|
round,
|
|
297
226
|
role: 'doer',
|
|
298
227
|
agent: agentName,
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
228
|
+
reason,
|
|
229
|
+
fromLineage: from.lineage,
|
|
230
|
+
toLineage: to.lineage,
|
|
231
|
+
fromModel: from.model ?? '(default)',
|
|
232
|
+
toModel: to.model ?? '(default)',
|
|
233
|
+
fallbackIdx: fromIdx,
|
|
234
|
+
message,
|
|
302
235
|
},
|
|
303
236
|
ts: Date.now(),
|
|
304
237
|
});
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
//
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
});
|
|
320
|
-
onEvent({
|
|
321
|
-
chatId,
|
|
322
|
-
type: 'cli_error',
|
|
323
|
-
payload: { phaseId: phase.id, round, role: 'doer', agent: agentName, error: err },
|
|
238
|
+
// Persist to sidecar (see reviewer-driver.ts for rationale).
|
|
239
|
+
// doerDir is the chat-scoped scratch dir, used here even when
|
|
240
|
+
// doerCwd was overridden to the user's repo.
|
|
241
|
+
(0, swap_sidecar_js_1.appendSwapSidecar)(doerDir, {
|
|
242
|
+
round,
|
|
243
|
+
phaseId: phase.id,
|
|
244
|
+
role: 'doer',
|
|
245
|
+
agent: agentName,
|
|
246
|
+
reason,
|
|
247
|
+
fromLineage: from.lineage,
|
|
248
|
+
toLineage: to.lineage,
|
|
249
|
+
fromModel: from.model ?? '(default)',
|
|
250
|
+
toModel: to.model ?? '(default)',
|
|
251
|
+
fallbackIdx: fromIdx,
|
|
324
252
|
ts: Date.now(),
|
|
325
253
|
});
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
finally {
|
|
257
|
+
handle.release();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Acquire session — fresh per chat by default; reuses across rounds when
|
|
261
|
+
// template policy says so (shareSessionAcrossRounds, default true).
|
|
262
|
+
const perms = await (0, permissions_js_1.getPermissions)();
|
|
263
|
+
const sessionName = (0, sanitize_name_js_1.sanitizeName)(`chorus-${chatId}-${phase.id}-doer-${agentName}`);
|
|
264
|
+
const session = await tmuxMgr.acquire({
|
|
265
|
+
chatId,
|
|
266
|
+
phaseId: phase.id,
|
|
267
|
+
role: 'doer',
|
|
268
|
+
round,
|
|
269
|
+
shareSessionAcrossRounds: phase.iterate.shareSessionAcrossRounds,
|
|
270
|
+
shareSessionAcrossPhases: phase.iterate.shareSessionAcrossPhases,
|
|
271
|
+
shim,
|
|
272
|
+
spawnOpts: {
|
|
273
|
+
sessionName,
|
|
274
|
+
cwd: doerCwd,
|
|
275
|
+
model: phase.doer.models?.[0],
|
|
276
|
+
sandbox: perms.sandboxProfile,
|
|
277
|
+
autoApprove: perms.autoApprovePrompts,
|
|
278
|
+
networkAccess: perms.networkAccess,
|
|
279
|
+
},
|
|
280
|
+
agentName,
|
|
281
|
+
});
|
|
282
|
+
if (shim.clearKeys && shim.clearKeys.length > 0) {
|
|
283
|
+
tmuxMgr.sendKeys(session.name, [...shim.clearKeys]);
|
|
284
|
+
}
|
|
285
|
+
if (shim.preNudge)
|
|
286
|
+
shim.preNudge(session.name);
|
|
287
|
+
const prompt = shim.formatPrompt({
|
|
288
|
+
promptFile: askFile,
|
|
289
|
+
answerFile,
|
|
290
|
+
task: phase.title,
|
|
291
|
+
expectDoneSentinel: true,
|
|
292
|
+
});
|
|
293
|
+
// Wait for the CLI's TUI to finish cold-start before pasting. 6s covers
|
|
294
|
+
// Codex's slow cold-start (it auths + paints panels); shorter and the
|
|
295
|
+
// Enter we send below races against the input box being ready and gets
|
|
296
|
+
// eaten. Raise if a slower box still misses the prompt.
|
|
297
|
+
await new Promise((r) => setTimeout(r, 6000));
|
|
298
|
+
tmuxMgr.pasteBuffer(session.name, prompt);
|
|
299
|
+
// Small gap between paste and Enter so the TUI registers the paste before
|
|
300
|
+
// we submit.
|
|
301
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
302
|
+
tmuxMgr.sendKeys(session.name, ['Enter']);
|
|
303
|
+
// Poll capture-pane every 2s to surface known CLI failure modes while we
|
|
304
|
+
// wait for the answer file. The detector is stateful for opencode's
|
|
305
|
+
// sustained-error pattern.
|
|
306
|
+
const pollHandle = setInterval(() => {
|
|
307
|
+
try {
|
|
308
|
+
const pane = tmuxMgr.capturePane(session.name);
|
|
309
|
+
const err = errorDetector.inspect(session.name, phase.doer.lineage, pane);
|
|
310
|
+
if (err) {
|
|
311
|
+
const recoveryKeys = err.kind === 'permission_prompt' ? shim.recoverKeys?.permission_prompt : undefined;
|
|
312
|
+
if (recoveryKeys && recoveryKeys.length > 0) {
|
|
313
|
+
// Layer 2 recovery: navigate the dialog, emit a warning (not error),
|
|
314
|
+
// skip health recording — we recovered, no degradation.
|
|
315
|
+
tmuxMgr.sendKeys(session.name, [...recoveryKeys]);
|
|
316
|
+
onEvent({
|
|
317
|
+
chatId,
|
|
318
|
+
type: 'cli_warning',
|
|
319
|
+
payload: {
|
|
320
|
+
phaseId: phase.id,
|
|
321
|
+
round,
|
|
322
|
+
role: 'doer',
|
|
323
|
+
agent: agentName,
|
|
324
|
+
recovered: err.kind,
|
|
325
|
+
keys: [...recoveryKeys],
|
|
326
|
+
detail: err.detail,
|
|
327
|
+
},
|
|
328
|
+
ts: Date.now(),
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
// Fire-and-forget — recordHealth became async in the libsql
|
|
333
|
+
// migration. Inside a setInterval callback we can't await without
|
|
334
|
+
// changing the callback shape; explicit .catch keeps unhandled
|
|
335
|
+
// rejections off the process and preserves the pre-migration
|
|
336
|
+
// semantics (non-blocking health record).
|
|
337
|
+
(0, cli_health_js_1.recordHealth)({
|
|
338
|
+
lineage: phase.doer.lineage,
|
|
339
|
+
status: (0, cli_health_js_1.kindToStatus)(err.kind),
|
|
340
|
+
message: err.message,
|
|
341
|
+
resetAt: err.resetAt,
|
|
342
|
+
}).catch((healthErr) => {
|
|
343
|
+
console.error(`[chorus] recordHealth failed for ${phase.doer.lineage}:`, healthErr);
|
|
344
|
+
});
|
|
345
|
+
onEvent({
|
|
346
|
+
chatId,
|
|
347
|
+
type: 'cli_error',
|
|
348
|
+
payload: { phaseId: phase.id, round, role: 'doer', agent: agentName, error: err },
|
|
349
|
+
ts: Date.now(),
|
|
350
|
+
});
|
|
351
|
+
}
|
|
326
352
|
}
|
|
327
353
|
}
|
|
354
|
+
catch {
|
|
355
|
+
// ignore — the watcher will time out independently
|
|
356
|
+
}
|
|
357
|
+
}, 2000);
|
|
358
|
+
try {
|
|
359
|
+
return await (0, output_watcher_js_1.waitForAnswer)(answerFile, {
|
|
360
|
+
timeoutMs: phase.timeoutMs ?? template_schema_js_1.DEFAULT_TMUX_PHASE_TIMEOUT_MS,
|
|
361
|
+
doneSentinel: '## DONE',
|
|
362
|
+
});
|
|
328
363
|
}
|
|
329
364
|
catch {
|
|
330
|
-
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
finally {
|
|
368
|
+
clearInterval(pollHandle);
|
|
331
369
|
}
|
|
332
|
-
}, 2000);
|
|
333
|
-
try {
|
|
334
|
-
return await (0, output_watcher_js_1.waitForAnswer)(answerFile, {
|
|
335
|
-
timeoutMs: phase.timeoutMs ?? template_schema_js_1.DEFAULT_TMUX_PHASE_TIMEOUT_MS,
|
|
336
|
-
doneSentinel: '## DONE',
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
catch {
|
|
340
|
-
return null;
|
|
341
370
|
}
|
|
342
371
|
finally {
|
|
343
|
-
|
|
372
|
+
releaseSlot?.();
|
|
344
373
|
}
|
|
345
374
|
}
|
|
346
375
|
//# sourceMappingURL=doer-driver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doer-driver.js","sourceRoot":"","sources":["../../../src/daemon/runner/doer-driver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"doer-driver.js","sourceRoot":"","sources":["../../../src/daemon/runner/doer-driver.ts"],"names":[],"mappings":"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"}
|
|
@@ -56,6 +56,7 @@ const path = __importStar(require("path"));
|
|
|
56
56
|
const template_schema_js_1 = require("../../lib/template-schema.js");
|
|
57
57
|
const permissions_js_1 = require("../../lib/settings/permissions.js");
|
|
58
58
|
const cli_health_js_1 = require("../../lib/cli-health.js");
|
|
59
|
+
const model_pricing_js_1 = require("../../lib/model-pricing.js");
|
|
59
60
|
const stream_file_writer_js_1 = require("./stream-file-writer.js");
|
|
60
61
|
async function runDoerHeadless(args) {
|
|
61
62
|
const { shim, chatId, phase, round, agentName, askContent, answerFile, doerCwd, abortSignal, onEvent, modelOverride, } = args;
|
|
@@ -161,11 +162,33 @@ async function runDoerHeadless(args) {
|
|
|
161
162
|
// Persist runtime stats next to answer.md — see reviewer.ts for
|
|
162
163
|
// rationale. Use path.dirname + path.join (clearer than the
|
|
163
164
|
// anchored regex; flagged in retroactive PR #16 review).
|
|
165
|
+
//
|
|
166
|
+
// Cost synthesis: see same block in reviewer.ts. Doer's model is
|
|
167
|
+
// `modelOverride ?? phase.doer.models?.[0]` which we already
|
|
168
|
+
// resolved at top of function and passed into shim.runHeadless;
|
|
169
|
+
// re-resolve the same string here for the lookup.
|
|
170
|
+
let usageForStats = capturedUsage;
|
|
171
|
+
const doerModel = modelOverride ?? phase.doer.models?.[0];
|
|
172
|
+
if (usageForStats &&
|
|
173
|
+
usageForStats.costUsd === undefined &&
|
|
174
|
+
(usageForStats.inputTokens ||
|
|
175
|
+
usageForStats.outputTokens ||
|
|
176
|
+
usageForStats.cachedInputTokens)) {
|
|
177
|
+
try {
|
|
178
|
+
const synth = await (0, model_pricing_js_1.synthesizeCostUsd)(doerModel, usageForStats);
|
|
179
|
+
if (synth !== undefined) {
|
|
180
|
+
usageForStats = { ...usageForStats, costUsd: synth };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
/* synthesis is informational — leave costUsd unset on failure */
|
|
185
|
+
}
|
|
186
|
+
}
|
|
164
187
|
try {
|
|
165
188
|
const statsPath = path.join(path.dirname(answerFile), '_stats.json');
|
|
166
189
|
fs.writeFileSync(statsPath, JSON.stringify({
|
|
167
190
|
durationMs: Date.now() - startedAt,
|
|
168
|
-
...(
|
|
191
|
+
...(usageForStats ? { usage: usageForStats } : {}),
|
|
169
192
|
}), 'utf-8');
|
|
170
193
|
}
|
|
171
194
|
catch {
|