ai-whisper 0.1.3 → 0.2.0
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 +18 -0
- package/dist/bin/broker-daemon.js +182 -11
- package/dist/bin/companion-agent.js +190 -19
- package/dist/bin/relay-monitor.js +179 -8
- package/dist/bin/whisper.js +280 -35
- package/dist/skills/ai-whisper-bugfix/SKILL.md +136 -0
- package/package.json +19 -3
- package/skills/ai-whisper-bugfix/SKILL.md +136 -0
package/README.md
CHANGED
|
@@ -50,6 +50,20 @@ It is **not** for:
|
|
|
50
50
|
- invisible background automation you never watch.
|
|
51
51
|
- people new to coding agents looking for a guided, hand-holding experience.
|
|
52
52
|
|
|
53
|
+
## Prerequisites
|
|
54
|
+
|
|
55
|
+
ai-whisper drives the *real* Claude and Codex CLIs, so you need both installed and authenticated first:
|
|
56
|
+
|
|
57
|
+
- **[Claude Code CLI](https://claude.com/claude-code)** — the `claude` command, signed in.
|
|
58
|
+
- **[Codex CLI](https://github.com/openai/codex)** — the `codex` command, signed in.
|
|
59
|
+
- **Node.js 22+**.
|
|
60
|
+
- **An LLM evaluator with credentials** — workflows are gated by it and refuse to start without it. See [Evaluator configuration](docs/evaluator-configuration.md).
|
|
61
|
+
- **tmux** *(optional)* — only for `whisper collab start`, which auto-arranges both agents into panes. The mount flow below does not need it.
|
|
62
|
+
|
|
63
|
+
## Safety & permissions
|
|
64
|
+
|
|
65
|
+
ai-whisper launches each agent in **full-autonomy mode** so the relay can drive it unattended — `claude --dangerously-skip-permissions` and `codex --dangerously-bypass-approvals-and-sandbox`. Inside the mounted workspace the agents read, write, and run commands without prompting. Point it at code you're willing to let two agents change autonomously, watch the run on the dashboard, and remember you are the final gatekeeper — review the result before you ship it. The deeper rationale is in [Concepts](docs/concepts.md).
|
|
66
|
+
|
|
53
67
|
## Quickstart
|
|
54
68
|
|
|
55
69
|
Install from npm:
|
|
@@ -90,6 +104,10 @@ whisper collab dashboard
|
|
|
90
104
|
|
|
91
105
|
> Running from a repo checkout instead of a packaged install? Build first (`pnpm build`) and invoke the CLI as `node packages/cli/dist/bin/whisper.js ...` wherever these examples say `whisper ...`.
|
|
92
106
|
|
|
107
|
+
## What happens if it fails?
|
|
108
|
+
|
|
109
|
+
A run that stops short usually **escalates** — it does not crash. When the evaluator can't resolve a phase (the round budget is spent, an agent reports it's blocked, or confidence stays too low), the loop halts and turn ownership returns to you. That's a designed exit, not a failure: run state is durable, so you read the dashboard, fix the spec or unblock the agent, and `whisper workflow resume <id>` to pick up where it left off. Escalation is the system asking for a human exactly when it should — seeing it is normal, not a sign something broke.
|
|
110
|
+
|
|
93
111
|
## Core concepts
|
|
94
112
|
|
|
95
113
|
ai-whisper is **not a swarm**. The agents never type at once — work moves by a single baton, one owner at a time. Mounted sessions are *real* Claude and Codex sessions in your terminal, and those sessions are the source of truth. Autonomy is supervised: every handoff, verdict, and round is inspectable, and runs are resumable rather than fire-and-forget. Work is organized as structured workflows — explicit loops and state transitions, not a free-form chat.
|
|
@@ -2025,6 +2025,36 @@ Findings: (omit this block entirely if none)
|
|
|
2025
2025
|
Non-blocking risks:
|
|
2026
2026
|
- <quality risk that does NOT block this gate, or "None.">
|
|
2027
2027
|
--- end protocol ---`;
|
|
2028
|
+
var WORKFLOW_DIAGNOSIS_PROTOCOL = `--- ai-whisper diagnosis review protocol ---
|
|
2029
|
+
You are the gatekeeper for an ai-whisper complex-bug-fixing DIAGNOSIS gate. No human is in the loop. Do NOT trust the implementer's diagnosis \u2014 verify it yourself. The gate stays shut until YOU independently agree on the root cause and that the fix is net-safe.
|
|
2030
|
+
|
|
2031
|
+
Required procedure:
|
|
2032
|
+
1. Independently reproduce: re-run the implementer's reproduction yourself; do not trust pasted output. If YOU cannot reproduce it, that is a blocking finding (the repro is not real/reliable). Speculation from reading code paths is not a valid reproduction.
|
|
2033
|
+
2. Attack the causal claim: is the cause PROVEN by the evidence chain, or merely asserted? Could the named cause be a correlate, or a symptom of something deeper? Demand any missing link.
|
|
2034
|
+
3. Attack the fix (anti-whack-a-mole): does the proposed fix remove the root cause, or just this symptom's surface? Could the bug resurface through another path? Name it if so.
|
|
2035
|
+
4. Attack the blast radius: is every affected area/module/contract listed? Add what is missing.
|
|
2036
|
+
5. Attack residual risks: are the real foreseeable risks named, or hand-waved?
|
|
2037
|
+
6. Mutual-agreement gate: approve ONLY when you have independently confirmed the reproduction and agree the cause is proven and the fix is net-safe (fixes more than it risks). "Plausible" is not approval.
|
|
2038
|
+
|
|
2039
|
+
Severity: a blocking finding must tie to a concrete diagnosis-contract item (unreproducible repro, unproven cause, symptom-masking fix, incomplete blast radius, un-named risk). Suppress style/taste. Do NOT gag valuable non-contract risk signals \u2014 surface them under "Non-blocking risks:" (always last) so they reach the human at escalation/completion.
|
|
2040
|
+
|
|
2041
|
+
Findings vs escalation: a fixable defect in the diagnosis is a FINDING (loops back for revision) \u2014 do NOT say you "cannot proceed" for a defect you can describe. ESCALATE only when you genuinely cannot review (a required reproduction input is absent and is not the implementer's to supply).
|
|
2042
|
+
|
|
2043
|
+
Never reply with only a bare verdict; your full reply must be well over 100 characters.
|
|
2044
|
+
|
|
2045
|
+
Output format \u2014 the verdict line MUST come before the Non-blocking risks section, which is always LAST:
|
|
2046
|
+
Diagnosis review matrix:
|
|
2047
|
+
| Claim | Required evidence | Implementer evidence | Independently verified? | Result |
|
|
2048
|
+
| ... |
|
|
2049
|
+
|
|
2050
|
+
Findings: (omit this block entirely if none)
|
|
2051
|
+
- <blocking finding tied to an exact diagnosis-contract item, with evidence>
|
|
2052
|
+
|
|
2053
|
+
<verdict line: "Approved. <one or two sentences>" OR, when blocked/cannot-proceed, state you cannot proceed and why>
|
|
2054
|
+
|
|
2055
|
+
Non-blocking risks:
|
|
2056
|
+
- <risk that does NOT block this gate, or "None.">
|
|
2057
|
+
--- end protocol ---`;
|
|
2028
2058
|
var SDD_SPEC_REVIEW = "Review the spec at {specPath}. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_REVIEW_PROTOCOL;
|
|
2029
2059
|
var SDD_CODE_REVIEW = "Review the implementer's changes for this phase \u2014 the commits in {commitRange}. The upper bound is a LIVE `HEAD`: resolve it against the current repository at review time and INCLUDE any commits added during this review round (e.g. fixes for your prior findings); do not pin the review to an earlier tip. Verify against the spec's acceptance criteria and run the project's verification/tests. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_REVIEW_PROTOCOL;
|
|
2030
2060
|
var SPEC_DRIVEN_DEVELOPMENT = {
|
|
@@ -2145,13 +2175,113 @@ var RALPH_LOOP = {
|
|
|
2145
2175
|
}
|
|
2146
2176
|
]
|
|
2147
2177
|
};
|
|
2178
|
+
var BUGFIX_DIAGNOSIS_KICKOFF = `You are the implementer in an autonomous complex-bug-fixing workflow. No human is in the loop \u2014 never ask for confirmation, permission, or clarification; do the work yourself.
|
|
2179
|
+
|
|
2180
|
+
Read the bug report at {specPath}. A human (dev or QA) already observed this bug, so you must reproduce it too \u2014 do NOT theorize from reading code paths.
|
|
2181
|
+
|
|
2182
|
+
Write a diagnosis artifact to {diagnosisPath} (create the file) with these sections:
|
|
2183
|
+
1. Reproduction \u2014 an ACTUALLY OBSERVED reproduction you ran yourself. Strongly prefer a failing test (RED) failing for the right reason; commit that test in THIS phase. If the project supports e2e/real-browser (e.g. Playwright), use it. Only if no automated test can capture it, give command/log output WITH an explicit justification.
|
|
2184
|
+
2. Root cause \u2014 the causal chain symptom\u2192cause, each link backed by concrete evidence (stack trace, log line, failing assertion, bisect), not assertion.
|
|
2185
|
+
3. Proposed fix approach \u2014 what changes and WHY that removes the root cause rather than masking the symptom.
|
|
2186
|
+
4. Blast radius \u2014 every area/module/contract the fix could affect.
|
|
2187
|
+
5. Residual risks \u2014 foreseeable risks remaining after the fix.
|
|
2188
|
+
|
|
2189
|
+
Commit the RED reproduction test (do NOT commit {bugfixDir}; it is gitignored). End your handback with a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters \u2014 never hand back only a single word.`;
|
|
2190
|
+
var BUGFIX_DIAGNOSIS_REVIEW = "Review the diagnosis artifact at {diagnosisPath} for the bug reported at {specPath}. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_DIAGNOSIS_PROTOCOL;
|
|
2191
|
+
var BUGFIX_DIAGNOSIS_FIX = "Apply the reviewer's findings to the diagnosis at {diagnosisPath} now. This is an autonomous workflow \u2014 no human will respond. Make the edits yourself (re-reproduce if needed) and hand back the corrected diagnosis; never ask for confirmation, permission, or clarification. If you added or changed the RED reproduction test while addressing the findings, COMMIT it in this round (do NOT commit {bugfixDir}; it is gitignored) \u2014 the next phase reviews the committed change set, so an uncommitted repro update would be invisible to it. End your handback with a 1-2 sentence summary of what you changed; your reply must be at least two sentences, well over 100 characters \u2014 never hand back only a single word.";
|
|
2192
|
+
var BUGFIX_FIX_KICKOFF = `The diagnosis at {diagnosisPath} is APPROVED \u2014 treat it as ground truth (re-orient from it, not prior conversation). This is an autonomous workflow \u2014 no human will respond; do the work yourself.
|
|
2193
|
+
|
|
2194
|
+
1. Implement the fix per the APPROVED approach.
|
|
2195
|
+
2. Turn the reproduction GREEN \u2014 the failing test now passes for the right reason; if the repro was a non-test demonstration, re-run it and show the symptom is gone.
|
|
2196
|
+
3. Run the project's verification/test command PLUS targeted checks across the declared blast radius, including the full suite, to catch regressions.
|
|
2197
|
+
4. Commit the fix and any added happy-path/edge-case coverage tests (the RED test was committed in diagnosis). Do NOT commit {bugfixDir} (gitignored).
|
|
2198
|
+
|
|
2199
|
+
If you discover the approved cause was WRONG, do NOT silently switch to a different fix \u2014 the cause was the agreed premise of this run. Stop and hand back that you CANNOT PROCEED because the approved cause is wrong, explaining what the evidence now shows; do not deliver a fix built on a second, unreviewed theory. This halts the run and returns control to a human, who re-opens the diagnosis (a new run, or a resume after they adjust the report) \u2014 the workflow does not loop back to the diagnosis phase on its own. Otherwise, hand back the commit SHAs + verification output + a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters.`;
|
|
2200
|
+
var BUGFIX_FIX_REVIEW = "The implementer claims the fix for the bug at {specPath} is complete \u2014 the changes are commits {commitRange}; resolve the upper bound against LIVE HEAD and include fix-round commits. Verify against the APPROVED diagnosis at {diagnosisPath}. Independently re-run the reproduction (it must be GREEN) and the verification suite \u2014 do not trust pasted output. Confirm: the root cause is actually removed (not just relocated \u2014 anti-whack-a-mole); every declared blast-radius area is regression-free; residual risks are handled or explicitly accepted; and COVERAGE is adequate \u2014 every happy path has at least one covering test and edge cases are covered. A case that genuinely cannot be covered must be explicitly noted (not silently passed); thin coverage is a blocking finding. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_REVIEW_PROTOCOL;
|
|
2201
|
+
var BUGFIX_FIX_FIX = "Apply the reviewer's findings to commits {commitRange} now (amend or add commits). This is an autonomous workflow \u2014 no human will respond. Do the work yourself and hand back the updated commit SHAs and verification output; never ask for confirmation, permission, or clarification. End your handback with a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters.";
|
|
2202
|
+
var BUGFIX_POSTMORTEM_KICKOFF = "Write a post-mortem report to {postmortemPath} (create the file) for the bug fixed in this run. This is an autonomous workflow \u2014 no human will respond; do the work yourself. Recap: confirmed root cause; the fix applied; reproduction\u2192GREEN evidence; blast radius touched; coverage gaps explicitly listed (carried from the fix review); residual risks; and lessons learned. Do NOT commit {bugfixDir} (gitignored). End your handback with a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters.";
|
|
2203
|
+
var BUGFIX_POSTMORTEM_REVIEW = "Review the post-mortem report at {postmortemPath}. Confirm it faithfully reflects what actually happened in this run \u2014 confirmed cause, the fix, the noted coverage gaps, and residual risks are all present and honest. This is not a rubber stamp; gloss-overs or omissions are findings. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_REVIEW_PROTOCOL;
|
|
2204
|
+
var BUGFIX_POSTMORTEM_FIX = "Apply the reviewer's findings to the post-mortem at {postmortemPath} now. This is an autonomous workflow \u2014 no human will respond. Make the edits yourself and hand back the corrected report; never ask for confirmation, permission, or clarification. End your handback with a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters.";
|
|
2205
|
+
var COMPLEX_BUG_FIXING = {
|
|
2206
|
+
type: "complex-bug-fixing",
|
|
2207
|
+
displayName: "Complex Bug Fixing",
|
|
2208
|
+
description: "Reproduce \u2192 adversarially-gated diagnosis \u2192 fix & verify \u2192 post-mortem, for a reported bug whose root cause is unknown",
|
|
2209
|
+
defaultImplementer: "claude",
|
|
2210
|
+
defaultReviewer: "codex",
|
|
2211
|
+
phases: [
|
|
2212
|
+
{
|
|
2213
|
+
name: "diagnosis",
|
|
2214
|
+
implementerRole: "implementer",
|
|
2215
|
+
reviewerRole: "reviewer",
|
|
2216
|
+
maxRounds: 5,
|
|
2217
|
+
initialHandoffStep: "implement",
|
|
2218
|
+
kickoffTemplate: BUGFIX_DIAGNOSIS_KICKOFF,
|
|
2219
|
+
stepTemplates: {
|
|
2220
|
+
implement: BUGFIX_DIAGNOSIS_KICKOFF,
|
|
2221
|
+
review: BUGFIX_DIAGNOSIS_REVIEW,
|
|
2222
|
+
fix: BUGFIX_DIAGNOSIS_FIX
|
|
2223
|
+
},
|
|
2224
|
+
reviewMode: "phase-review",
|
|
2225
|
+
evaluatorPromptKey: "review-loop",
|
|
2226
|
+
artifactOut: { kind: "spec", pathTemplate: "{diagnosisPath}" },
|
|
2227
|
+
anchorCommitBaseOnEntry: true,
|
|
2228
|
+
renderFixTemplateOnFindings: true
|
|
2229
|
+
},
|
|
2230
|
+
{
|
|
2231
|
+
name: "fix-and-verify",
|
|
2232
|
+
implementerRole: "implementer",
|
|
2233
|
+
reviewerRole: "reviewer",
|
|
2234
|
+
maxRounds: 5,
|
|
2235
|
+
initialHandoffStep: "implement",
|
|
2236
|
+
kickoffTemplate: BUGFIX_FIX_KICKOFF,
|
|
2237
|
+
stepTemplates: {
|
|
2238
|
+
implement: BUGFIX_FIX_KICKOFF,
|
|
2239
|
+
review: BUGFIX_FIX_REVIEW,
|
|
2240
|
+
fix: BUGFIX_FIX_FIX
|
|
2241
|
+
},
|
|
2242
|
+
reviewMode: "acceptance-review",
|
|
2243
|
+
evaluatorPromptKey: "review-loop",
|
|
2244
|
+
artifactOut: { kind: "commit-range" },
|
|
2245
|
+
renderFixTemplateOnFindings: true
|
|
2246
|
+
},
|
|
2247
|
+
{
|
|
2248
|
+
name: "post-mortem",
|
|
2249
|
+
implementerRole: "implementer",
|
|
2250
|
+
reviewerRole: "reviewer",
|
|
2251
|
+
maxRounds: 3,
|
|
2252
|
+
initialHandoffStep: "implement",
|
|
2253
|
+
kickoffTemplate: BUGFIX_POSTMORTEM_KICKOFF,
|
|
2254
|
+
stepTemplates: {
|
|
2255
|
+
implement: BUGFIX_POSTMORTEM_KICKOFF,
|
|
2256
|
+
review: BUGFIX_POSTMORTEM_REVIEW,
|
|
2257
|
+
fix: BUGFIX_POSTMORTEM_FIX
|
|
2258
|
+
},
|
|
2259
|
+
reviewMode: "phase-review",
|
|
2260
|
+
evaluatorPromptKey: "review-loop",
|
|
2261
|
+
artifactOut: { kind: "spec", pathTemplate: "{postmortemPath}" },
|
|
2262
|
+
renderFixTemplateOnFindings: true
|
|
2263
|
+
}
|
|
2264
|
+
]
|
|
2265
|
+
};
|
|
2148
2266
|
var REGISTRY = {
|
|
2149
2267
|
[SPEC_DRIVEN_DEVELOPMENT.type]: SPEC_DRIVEN_DEVELOPMENT,
|
|
2150
|
-
[RALPH_LOOP.type]: RALPH_LOOP
|
|
2268
|
+
[RALPH_LOOP.type]: RALPH_LOOP,
|
|
2269
|
+
[COMPLEX_BUG_FIXING.type]: COMPLEX_BUG_FIXING
|
|
2151
2270
|
};
|
|
2152
2271
|
function ralphRunDir(workspaceRoot, workflowId) {
|
|
2153
2272
|
return join(workspaceRoot, ".ai-whisper", "ralph", workflowId);
|
|
2154
2273
|
}
|
|
2274
|
+
function bugfixRunDir(workspaceRoot, workflowId) {
|
|
2275
|
+
return join(workspaceRoot, ".ai-whisper", "bugfix", workflowId);
|
|
2276
|
+
}
|
|
2277
|
+
function bugfixPaths(workspaceRoot, workflowId) {
|
|
2278
|
+
const bugfixDir = bugfixRunDir(workspaceRoot, workflowId);
|
|
2279
|
+
return {
|
|
2280
|
+
bugfixDir,
|
|
2281
|
+
diagnosisPath: join(bugfixDir, "diagnosis.md"),
|
|
2282
|
+
postmortemPath: join(bugfixDir, "postmortem.md")
|
|
2283
|
+
};
|
|
2284
|
+
}
|
|
2155
2285
|
function getWorkflowDefinition(type) {
|
|
2156
2286
|
return REGISTRY[type];
|
|
2157
2287
|
}
|
|
@@ -2371,12 +2501,16 @@ function createWorkflowControl(deps) {
|
|
|
2371
2501
|
const collab2 = getCollab(db, input.workflow.collabId);
|
|
2372
2502
|
const ralphDir = collab2 ? ralphRunDir(collab2.workspaceRoot, input.workflow.workflowId) : "";
|
|
2373
2503
|
const reviewMode = useAcceptance ? "acceptance-review" : input.phase.reviewMode ?? "phase-review";
|
|
2504
|
+
const bf = collab2 ? bugfixPaths(collab2.workspaceRoot, input.workflow.workflowId) : { bugfixDir: "", diagnosisPath: "", postmortemPath: "" };
|
|
2374
2505
|
return renderTemplate(tmpl, {
|
|
2375
2506
|
specPath: input.workflow.specPath,
|
|
2376
2507
|
planPath: safeDerivePlanPath(input.workflow.specPath, input.workflow.createdAt),
|
|
2377
2508
|
commitRange: liveReviewCommitRange(ctx),
|
|
2378
2509
|
ralphDir,
|
|
2379
|
-
reviewMode
|
|
2510
|
+
reviewMode,
|
|
2511
|
+
bugfixDir: bf.bugfixDir,
|
|
2512
|
+
diagnosisPath: bf.diagnosisPath,
|
|
2513
|
+
postmortemPath: bf.postmortemPath
|
|
2380
2514
|
});
|
|
2381
2515
|
}
|
|
2382
2516
|
function createContinuationHandoff(input) {
|
|
@@ -2413,12 +2547,16 @@ function createWorkflowControl(deps) {
|
|
|
2413
2547
|
const ctx = input.workflow.workflowContext;
|
|
2414
2548
|
const collab2 = getCollab(db, input.workflow.collabId);
|
|
2415
2549
|
const ralphDir = collab2 ? ralphRunDir(collab2.workspaceRoot, input.workflow.workflowId) : "";
|
|
2550
|
+
const bf = collab2 ? bugfixPaths(collab2.workspaceRoot, input.workflow.workflowId) : { bugfixDir: "", diagnosisPath: "", postmortemPath: "" };
|
|
2416
2551
|
const kickoffText = renderTemplate(phase.kickoffTemplate, {
|
|
2417
2552
|
specPath: input.workflow.specPath,
|
|
2418
2553
|
planPath: safeDerivePlanPath(input.workflow.specPath, input.workflow.createdAt),
|
|
2419
2554
|
commitRange: liveReviewCommitRange(ctx),
|
|
2420
2555
|
ralphDir,
|
|
2421
|
-
reviewMode: phase.reviewMode ?? "phase-review"
|
|
2556
|
+
reviewMode: phase.reviewMode ?? "phase-review",
|
|
2557
|
+
bugfixDir: bf.bugfixDir,
|
|
2558
|
+
diagnosisPath: bf.diagnosisPath,
|
|
2559
|
+
postmortemPath: bf.postmortemPath
|
|
2422
2560
|
});
|
|
2423
2561
|
const chainId = `relay_ch_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
2424
2562
|
const phaseRunId = `wfp_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
@@ -2676,15 +2814,19 @@ function createWorkflowControl(deps) {
|
|
|
2676
2814
|
} else if (normalized.verdict === "findings") {
|
|
2677
2815
|
const findingsText = input.followUpMessage ?? "Address the reviewer's findings.";
|
|
2678
2816
|
let fixRequestText;
|
|
2679
|
-
if (phase.repeatUntilComplete && phase.stepTemplates.fix) {
|
|
2817
|
+
if ((phase.repeatUntilComplete || phase.renderFixTemplateOnFindings) && phase.stepTemplates.fix) {
|
|
2680
2818
|
const ctx = workflow.workflowContext;
|
|
2681
2819
|
const collab2 = getCollab(db, workflow.collabId);
|
|
2682
2820
|
const ralphDir = collab2 ? ralphRunDir(collab2.workspaceRoot, workflow.workflowId) : "";
|
|
2821
|
+
const bf = collab2 ? bugfixPaths(collab2.workspaceRoot, workflow.workflowId) : { bugfixDir: "", diagnosisPath: "", postmortemPath: "" };
|
|
2683
2822
|
const fixTmpl = renderTemplate(phase.stepTemplates.fix, {
|
|
2684
2823
|
specPath: workflow.specPath,
|
|
2685
2824
|
planPath: safeDerivePlanPath(workflow.specPath, workflow.createdAt),
|
|
2686
2825
|
commitRange: liveReviewCommitRange(ctx),
|
|
2687
|
-
ralphDir
|
|
2826
|
+
ralphDir,
|
|
2827
|
+
bugfixDir: bf.bugfixDir,
|
|
2828
|
+
diagnosisPath: bf.diagnosisPath,
|
|
2829
|
+
postmortemPath: bf.postmortemPath
|
|
2688
2830
|
});
|
|
2689
2831
|
fixRequestText = `${fixTmpl}
|
|
2690
2832
|
|
|
@@ -4512,6 +4654,23 @@ function ensureRalphWorkspace(workspaceRoot, workflowId) {
|
|
|
4512
4654
|
return dir;
|
|
4513
4655
|
}
|
|
4514
4656
|
|
|
4657
|
+
// ../broker/dist/runtime/bugfix-setup.js
|
|
4658
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "node:fs";
|
|
4659
|
+
import { join as join3 } from "node:path";
|
|
4660
|
+
function ensureBugfixWorkspace(workspaceRoot, workflowId) {
|
|
4661
|
+
const dir = bugfixRunDir(workspaceRoot, workflowId);
|
|
4662
|
+
mkdirSync2(dir, { recursive: true });
|
|
4663
|
+
const ignorePath = join3(workspaceRoot, ".ai-whisper", ".gitignore");
|
|
4664
|
+
if (!existsSync3(ignorePath)) {
|
|
4665
|
+
writeFileSync2(ignorePath, "*\n");
|
|
4666
|
+
}
|
|
4667
|
+
const bugfixIgnorePath = join3(workspaceRoot, ".ai-whisper", "bugfix", ".gitignore");
|
|
4668
|
+
if (!existsSync3(bugfixIgnorePath)) {
|
|
4669
|
+
writeFileSync2(bugfixIgnorePath, "*\n");
|
|
4670
|
+
}
|
|
4671
|
+
return dir;
|
|
4672
|
+
}
|
|
4673
|
+
|
|
4515
4674
|
// ../broker/dist/runtime/workflow-driver.js
|
|
4516
4675
|
function createWorkflowDriver(deps) {
|
|
4517
4676
|
const { broker: broker2, headReader } = deps;
|
|
@@ -4582,7 +4741,7 @@ function createWorkflowDriver(deps) {
|
|
|
4582
4741
|
const sender = phase.initialHandoffStep === "review" ? implementerAgent : reviewerAgent;
|
|
4583
4742
|
const target = phase.initialHandoffStep === "review" ? reviewerAgent : implementerAgent;
|
|
4584
4743
|
let executionBaseHeadSha;
|
|
4585
|
-
if (phase.initialHandoffStep === "execute") {
|
|
4744
|
+
if (phase.initialHandoffStep === "execute" || phase.anchorCommitBaseOnEntry === true) {
|
|
4586
4745
|
try {
|
|
4587
4746
|
executionBaseHeadSha = await headReader.readHead(collab2.workspaceRoot);
|
|
4588
4747
|
} catch (err) {
|
|
@@ -4605,6 +4764,15 @@ function createWorkflowDriver(deps) {
|
|
|
4605
4764
|
} else {
|
|
4606
4765
|
ralphDir = ralphRunDir(collab2.workspaceRoot, workflowId);
|
|
4607
4766
|
}
|
|
4767
|
+
if (workflow.workflowType === "complex-bug-fixing") {
|
|
4768
|
+
try {
|
|
4769
|
+
ensureBugfixWorkspace(collab2.workspaceRoot, workflowId);
|
|
4770
|
+
} catch (err) {
|
|
4771
|
+
broker2.control.haltWorkflow({ workflowId, reason: `bugfix setup failed: ${String(err)}`, now });
|
|
4772
|
+
return;
|
|
4773
|
+
}
|
|
4774
|
+
}
|
|
4775
|
+
const bugfix = bugfixPaths(collab2.workspaceRoot, workflowId);
|
|
4608
4776
|
const ctx = workflow.workflowContext;
|
|
4609
4777
|
let planPath = workflow.specPath;
|
|
4610
4778
|
try {
|
|
@@ -4616,7 +4784,10 @@ function createWorkflowDriver(deps) {
|
|
|
4616
4784
|
planPath,
|
|
4617
4785
|
commitRange: ctx.commitRange ?? "HEAD",
|
|
4618
4786
|
ralphDir,
|
|
4619
|
-
reviewMode: phase.reviewMode ?? "phase-review"
|
|
4787
|
+
reviewMode: phase.reviewMode ?? "phase-review",
|
|
4788
|
+
bugfixDir: bugfix.bugfixDir,
|
|
4789
|
+
diagnosisPath: bugfix.diagnosisPath,
|
|
4790
|
+
postmortemPath: bugfix.postmortemPath
|
|
4620
4791
|
});
|
|
4621
4792
|
try {
|
|
4622
4793
|
broker2.control.beginPhaseRun({
|
|
@@ -5548,7 +5719,7 @@ function writeOwnPidToBrokerDaemon(db, input) {
|
|
|
5548
5719
|
|
|
5549
5720
|
// src/runtime/evaluator-config.ts
|
|
5550
5721
|
import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
5551
|
-
import { join as
|
|
5722
|
+
import { join as join4 } from "node:path";
|
|
5552
5723
|
|
|
5553
5724
|
// src/runtime/state-root.ts
|
|
5554
5725
|
import os from "node:os";
|
|
@@ -5607,15 +5778,15 @@ function loadEvaluatorConfig() {
|
|
|
5607
5778
|
const root = getStateRoot();
|
|
5608
5779
|
let dotenv = {};
|
|
5609
5780
|
try {
|
|
5610
|
-
dotenv = parseDotEnv(readFileSync2(
|
|
5781
|
+
dotenv = parseDotEnv(readFileSync2(join4(root, ".env"), "utf8"));
|
|
5611
5782
|
} catch (err) {
|
|
5612
5783
|
if (err.code !== "ENOENT") throw err;
|
|
5613
5784
|
}
|
|
5614
5785
|
const envGet = (k) => process.env[k] ?? dotenv[k];
|
|
5615
|
-
const authPath =
|
|
5786
|
+
const authPath = join4(root, "auth.json");
|
|
5616
5787
|
warnIfLoosePerms(authPath);
|
|
5617
5788
|
const auth = readJsonFile(authPath, "auth.json");
|
|
5618
|
-
const config = readJsonFile(
|
|
5789
|
+
const config = readJsonFile(join4(root, "config.json"), "config.json");
|
|
5619
5790
|
const evalCfg = config?.evaluator ?? {};
|
|
5620
5791
|
const evalOllama = evalCfg.ollama ?? {};
|
|
5621
5792
|
const provider = (envGet("AI_WHISPER_EVALUATOR_PROVIDER") ?? evalCfg.provider) === "ollama" ? "ollama" : "anthropic";
|