ai-whisper 0.6.0 → 0.7.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 +2 -0
- package/dist/bin/broker-daemon.js +266 -18
- package/dist/bin/companion-agent.js +272 -49
- package/dist/bin/relay-monitor.js +231 -8
- package/dist/bin/whisper.js +446 -127
- package/package.json +4 -4
- package/skills/ai-whisper-deliberation/SKILL.md +167 -0
- package/skills/ai-whisper-deliberation-craft/SKILL.md +39 -0
package/README.md
CHANGED
|
@@ -61,6 +61,8 @@ You pair any two of three agents — `claude`, `codex`, and `ezio`. ai-whisper d
|
|
|
61
61
|
- **An LLM evaluator with credentials** — workflows are gated by it and refuse to start without it. See [Evaluator configuration](docs/evaluator-configuration.md).
|
|
62
62
|
- **tmux** *(optional)* — only for `whisper collab start`, which auto-arranges both agents into panes. The mount flow below does not need it.
|
|
63
63
|
|
|
64
|
+
> **Platform support:** ai-whisper is terminal-native and Unix-oriented — it drives interactive PTY sessions, so it runs on **macOS and Linux**. It is **not supported natively on Windows**: `whisper collab mount` / `reconnect` require a Unix tty-backed shell and will exit with an error pointing here. On Windows, run ai-whisper inside **[WSL2](https://learn.microsoft.com/windows/wsl/install)** — install Node, your agent CLI, and ai-whisper inside the WSL2 distro and run the commands there, where everything works as-is.
|
|
65
|
+
|
|
64
66
|
## Safety & permissions
|
|
65
67
|
|
|
66
68
|
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).
|
|
@@ -2211,11 +2211,51 @@ Findings: (omit this block entirely if none)
|
|
|
2211
2211
|
|
|
2212
2212
|
<verdict line: "Approved. <one or two sentences>" OR, when blocked/cannot-proceed, state you cannot proceed and why>
|
|
2213
2213
|
|
|
2214
|
+
Non-blocking risks:
|
|
2215
|
+
- <risk that does NOT block this gate, or "None.">
|
|
2216
|
+
--- end protocol ---`;
|
|
2217
|
+
var WORKFLOW_DELIBERATION_PROTOCOL = `--- ai-whisper deliberation review protocol ---
|
|
2218
|
+
You are the Challenger at an ai-whisper Deliberation layer gate. No human is in the loop. The Explorer has proposed this layer's output; your job is to produce GENUINE adversarial pressure, not to rubber-stamp. The gate stays shut until the layer's output survives real attack \u2014 OR the only remaining material questions are preference-dependent forks, which you RECORD rather than resolve.
|
|
2219
|
+
|
|
2220
|
+
Decision-materiality is the bar. Sort every finding four ways:
|
|
2221
|
+
- BLOCKING (drives not-approved): it would change a reasonable decision-maker's choice.
|
|
2222
|
+
- OPEN QUESTION (non-blocking): material but depends on the human's private preference \u2014 surface it, do not resolve it, do not block on it.
|
|
2223
|
+
- NON-BLOCKING RISK (non-blocking): a real but non-decision-changing risk \u2014 surface it under the Non-blocking risks section (always last), never gag it.
|
|
2224
|
+
- SUPPRESS: pure noise (style/taste) only.
|
|
2225
|
+
|
|
2226
|
+
Required procedure:
|
|
2227
|
+
1. Independent derivation (skin in the game): on this layer's FIRST review, derive your OWN candidate (your own objectives / approach set / tradeoff map) BEFORE reading the Explorer's, then diff. Report the material gap. On fix-rounds, re-audit the revision.
|
|
2228
|
+
2. Forbid recall-based verification: you may NOT clear a material claim with "that matches what I know" \u2014 you and the Explorer share priors and therefore blind spots. Verify every material claim against an EXTERNAL source (re-run the grep, open the cited file, fetch the source, check the actual API) and tag it "verified against <source>". A material claim you cannot externally verify becomes an Open Question marked "could not verify \u2014 you should".
|
|
2229
|
+
3. Verify by materiality and surface, not by felt uncertainty: verify what is load-bearing and what is a specific or suspiciously-convenient fact (version numbers, API signatures, file paths, citations, benchmarks), regardless of how confident it sounds.
|
|
2230
|
+
4. Steelman, then attack, with coverage: state the strongest version of the Explorer's position, then surface at least one unexamined assumption, one missing alternative, and one material risk \u2014 or explicitly certify that each lens was run and why none is decision-material. "Looks good" is not a legal handback.
|
|
2231
|
+
5. Generative mandate: beyond auditing the Explorer, independently GENERATE angles and alternatives it did not raise.
|
|
2232
|
+
6. Distrust your own agreement: treat your own "yeah, that's right" as a red flag to verify, not a green light.
|
|
2233
|
+
|
|
2234
|
+
Per-layer attack weighting: objectives -> framing + assumption (is the QUESTION itself right?); approaches -> alternative + evidence (is the set complete, is each grounded and feasible?); tradeoffs -> feasibility + second-order (honest, or cherry-picked / buried costs?); synthesis -> faithfulness (does the findings doc match the deliberation; are the Open Questions the right ones?).
|
|
2235
|
+
|
|
2236
|
+
You emit approved / not-approved + findings ONLY. You do NOT label the workflow outcome (advance / loop / escalate) \u2014 whether the gate result causes the run to advance, loop, or escalate is the evaluator's job. Approve only when the layer survived real attack with the work shown. If a required input is missing and the Explorer cannot supply it, do NOT approve \u2014 name the missing input and state you cannot proceed (so the gate escalates). If exploring this layer reveals that an EARLIER ratified layer is wrong, state you cannot proceed and why (the run escalates; it does not silently re-open the earlier layer).
|
|
2237
|
+
|
|
2238
|
+
Never reply with only a bare verdict; your full reply must be well over 100 characters.
|
|
2239
|
+
|
|
2240
|
+
Output format \u2014 the verdict line MUST come before the Non-blocking risks section, which is always LAST:
|
|
2241
|
+
Deliberation review matrix:
|
|
2242
|
+
| Claim / candidate | My independent derivation | Material gap | Verified against | Result |
|
|
2243
|
+
| ... |
|
|
2244
|
+
|
|
2245
|
+
Findings: (omit this block entirely if none)
|
|
2246
|
+
- <blocking finding tied to a decision-material gap, with external-verification evidence>
|
|
2247
|
+
|
|
2248
|
+
Open Questions: (omit if none)
|
|
2249
|
+
- <material but preference-dependent fork, or "could not verify X \u2014 you should">
|
|
2250
|
+
|
|
2251
|
+
<verdict line: "Approved. <one or two sentences>" OR, when blocked/cannot-proceed, state you cannot proceed and why>
|
|
2252
|
+
|
|
2214
2253
|
Non-blocking risks:
|
|
2215
2254
|
- <risk that does NOT block this gate, or "None.">
|
|
2216
2255
|
--- end protocol ---`;
|
|
2217
2256
|
var CODE_REVIEW_SKILL_GUIDANCE = "Use the ai-whisper-code-review skill to evaluate the delivered code. The workflow review protocol below controls your output format and evaluator semantics; the skill controls how you inspect code and decide which code-quality issues are blocking.\n\n";
|
|
2218
2257
|
var PLAN_EXECUTION_SKILL_GUIDANCE = "Use the ai-whisper-plan-execution skill to structure HOW you execute this plan: subagent fan-out with model allocation where your harness supports it, disciplined inline execution otherwise. The handback contract above remains authoritative \u2014 settle all delegated work before handing back, and state which execution mode you used.";
|
|
2258
|
+
var DELIBERATION_CRAFT_SKILL_GUIDANCE = "Use the ai-whisper-deliberation-craft skill for HOW to do the research and the attack \u2014 the Explorer's research contract and the Challenger's attack taxonomy. The deliberation review protocol below remains authoritative for the gate contract and output format; the craft skill is how-to only and does not restate it.\n\n";
|
|
2219
2259
|
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;
|
|
2220
2260
|
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" + CODE_REVIEW_SKILL_GUIDANCE + WORKFLOW_REVIEW_PROTOCOL;
|
|
2221
2261
|
var SDD_PLAN_EXECUTION = "Execute the plan at {planPath} now: make all changes it specifies, run the verification command the plan or spec defines and ensure it passes, then commit. This is an autonomous workflow \u2014 no human will respond. Do the work yourself; never ask for confirmation, permission, or clarification. Hand back the commit SHAs and the verification output, plus 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.\n\n" + PLAN_EXECUTION_SKILL_GUIDANCE;
|
|
@@ -2425,6 +2465,129 @@ var COMPLEX_BUG_FIXING = {
|
|
|
2425
2465
|
}
|
|
2426
2466
|
]
|
|
2427
2467
|
};
|
|
2468
|
+
var DELIB_REVIEW = "You are the Challenger reviewing the Explorer's output for the current Deliberation layer, grounded in the seed at {specPath}. The Explorer's working notes are under {deliberationDir}. This is an autonomous workflow with no human in the loop.\n\n" + DELIBERATION_CRAFT_SKILL_GUIDANCE + WORKFLOW_DELIBERATION_PROTOCOL;
|
|
2469
|
+
var DELIB_SYNTHESIS_REVIEW = "You are the Challenger reviewing the Explorer's SYNTHESIS layer \u2014 the committed findings document at {findingsPath}. Read {findingsPath}: it is the deliverable under review. It must faithfully collapse the ratified objectives/approaches/tradeoffs (the working notes under {deliberationDir}) into the findings skeleton \u2014 verify that faithfulness and that the Open Questions are the right ones (no material decision silently resolved, none invented). Grounded in the seed at {specPath}. This is an autonomous workflow with no human in the loop.\n\n" + DELIBERATION_CRAFT_SKILL_GUIDANCE + WORKFLOW_DELIBERATION_PROTOCOL;
|
|
2470
|
+
var DELIB_FIX = `Apply the Challenger's findings to your working notes under {deliberationDir} now, re-grounding against the seed at {specPath}. This is an autonomous workflow \u2014 no human will respond. Do the research/edits yourself; never ask for confirmation, permission, or clarification. Append one JSON line to {deliberationDir}/metrics.jsonl with the two signals spec \xA712.6 requires \u2014 "materialFindings" (the count of decision-material findings you are addressing this round) and "revisionMagnitude" (how much your output changed: "none"/"minor"/"moderate"/"major") \u2014 plus "layer" and "round". 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.
|
|
2471
|
+
|
|
2472
|
+
` + DELIBERATION_CRAFT_SKILL_GUIDANCE;
|
|
2473
|
+
var DELIB_OBJECTIVES = `You are the Explorer in an autonomous Deliberation workflow \u2014 no human is in the loop; never ask for confirmation, permission, or clarification, and do the work yourself.
|
|
2474
|
+
|
|
2475
|
+
LAYER 1 of 4: OBJECTIVES. Read the seed at {specPath}. Research the REAL situation it points at \u2014 current state, the actual pain, the constraints. Ground every claim in the project (code, tests, git history, docs) where the seed is project-anchored; use web research only where the question reaches beyond the project. Do NOT restate the seed \u2014 DERIVE the objectives and success criteria from evidence.
|
|
2476
|
+
|
|
2477
|
+
Write your objectives working notes to {deliberationDir}/objectives.md: each derived objective with why-it-matters and its grounding source; the interpretation you took (and any you discarded) when the seed was vague; and an explicit list of known-unknowns. Append one JSON line to {deliberationDir}/metrics.jsonl with the two signals spec \xA712.6 requires \u2014 "materialFindings" (0 on this initial proposal) and "revisionMagnitude" ("initial" on the first proposal) \u2014 plus "layer" and "round". Example: {"layer":"objectives","round":1,"materialFindings":0,"revisionMagnitude":"initial"}.
|
|
2478
|
+
|
|
2479
|
+
${DELIBERATION_CRAFT_SKILL_GUIDANCE}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.`;
|
|
2480
|
+
var DELIB_APPROACHES = `You are the Explorer in an autonomous Deliberation workflow \u2014 no human is in the loop; do the work yourself.
|
|
2481
|
+
|
|
2482
|
+
LAYER 2 of 4: APPROACHES. The objectives in {deliberationDir}/objectives.md are ratified \u2014 treat them as ground truth. Research and propose AT LEAST THREE genuinely distinct approaches to reach them. Survey the space first (prior art + in-repo idioms + candidate techniques) before evaluating any candidate; ground each approach in a real precedent. You MAY NOT pick a winner in this layer \u2014 that is the guard against premature convergence.
|
|
2483
|
+
|
|
2484
|
+
Write your approaches working notes to {deliberationDir}/approaches.md: one section per approach (gist, grounding/precedent, why it is distinct), plus the known-unknowns each carries. Append one JSON line to {deliberationDir}/metrics.jsonl with "materialFindings" and "revisionMagnitude" (plus "layer" and "round"), the two per-round signals spec \xA712.6 requires.
|
|
2485
|
+
|
|
2486
|
+
${DELIBERATION_CRAFT_SKILL_GUIDANCE}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.`;
|
|
2487
|
+
var DELIB_TRADEOFFS = `You are the Explorer in an autonomous Deliberation workflow \u2014 no human is in the loop; do the work yourself.
|
|
2488
|
+
|
|
2489
|
+
LAYER 3 of 4: TRADEOFFS & DIFFICULTY. The approaches in {deliberationDir}/approaches.md are ratified. For EACH surviving approach, map the tradeoffs, difficulty, blast radius, and material risks \u2014 stress-tested against reality (feasibility checks and blast radius in the ACTUAL codebase, named risks, not hand-waving). Do not bury costs or cherry-pick.
|
|
2490
|
+
|
|
2491
|
+
Write your tradeoffs working notes to {deliberationDir}/tradeoffs.md: a per-approach tradeoff/difficulty/risk breakdown, with the grounding for each claim. Append one JSON line to {deliberationDir}/metrics.jsonl with "materialFindings" and "revisionMagnitude" (plus "layer" and "round"), the two per-round signals spec \xA712.6 requires.
|
|
2492
|
+
|
|
2493
|
+
${DELIBERATION_CRAFT_SKILL_GUIDANCE}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.`;
|
|
2494
|
+
var DELIB_SYNTHESIS_FIX = 'Apply the Challenger\'s findings by RE-WRITING the corrected findings document to {findingsPath} (keep the \xA79 skeleton). Then RE-COMMIT it: `git add {findingsPath} && git commit` (do NOT commit {deliberationDir}; it is gitignored). Re-ground against the seed at {specPath} to confirm the revision is consistent. Append one JSON line to {deliberationDir}/metrics.jsonl with the two signals spec \xA712.6 requires \u2014 "materialFindings" (the count of decision-material findings you are addressing this round) and "revisionMagnitude" (how much your output changed: "none"/"minor"/"moderate"/"major") \u2014 plus "layer" and "round". 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.\n\n' + DELIBERATION_CRAFT_SKILL_GUIDANCE;
|
|
2495
|
+
var DELIB_SYNTHESIS = `You are the Explorer in an autonomous Deliberation workflow \u2014 no human is in the loop; do the work yourself.
|
|
2496
|
+
|
|
2497
|
+
LAYER 4 of 4: SYNTHESIS. The objectives, approaches, and tradeoffs working notes under {deliberationDir} are all ratified. Collapse them into a single findings document optimized for fast human review, and WRITE it to {findingsPath} (create the directory if needed). Use exactly this skeleton:
|
|
2498
|
+
|
|
2499
|
+
# Deliberation: <topic>
|
|
2500
|
+
seed: {specPath} \xB7 <date> \xB7 <implementer>/<challenger>
|
|
2501
|
+
|
|
2502
|
+
## TL;DR
|
|
2503
|
+
- Recommended direction: <one line> (PROPOSAL)
|
|
2504
|
+
- Decisions that need you: <the 1-2 top Open Questions>
|
|
2505
|
+
|
|
2506
|
+
## Objectives (ratified)
|
|
2507
|
+
- <objective> \u2014 why it matters [source]
|
|
2508
|
+
- interpretation taken: ...; discarded: ... (only when the seed was vague)
|
|
2509
|
+
|
|
2510
|
+
## Approaches considered
|
|
2511
|
+
| Approach | Gist | Grounding/precedent | Key tradeoff | Difficulty |
|
|
2512
|
+
(one short paragraph per surviving approach only)
|
|
2513
|
+
|
|
2514
|
+
## Recommendation (PROPOSAL \u2014 overridable)
|
|
2515
|
+
<which, why over the others, and what the Challenger attacked + how it held>
|
|
2516
|
+
|
|
2517
|
+
## Risks
|
|
2518
|
+
- <material risk to the recommended direction>
|
|
2519
|
+
|
|
2520
|
+
## Open Questions (for you)
|
|
2521
|
+
- <decision> \u2014 what is at stake / why it was not settled
|
|
2522
|
+
- <"could not verify X \u2014 you should">
|
|
2523
|
+
|
|
2524
|
+
## Grounding & confidence (footer)
|
|
2525
|
+
verified against source: ... \xB7 assumed (unverified): ... \xB7 couldn't verify: ...
|
|
2526
|
+
|
|
2527
|
+
The recommendation is a clearly-marked PROPOSAL the human may override \u2014 do NOT present it as settled. Then COMMIT the findings doc: \`git add {findingsPath} && git commit\` (do NOT commit {deliberationDir}; it is gitignored). Append one JSON line to {deliberationDir}/metrics.jsonl with "materialFindings" and "revisionMagnitude" (plus "layer" and "round"), the two per-round signals spec \xA712.6 requires.
|
|
2528
|
+
|
|
2529
|
+
${DELIBERATION_CRAFT_SKILL_GUIDANCE}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.`;
|
|
2530
|
+
var DELIBERATION = {
|
|
2531
|
+
type: "deliberation",
|
|
2532
|
+
displayName: "Deliberation",
|
|
2533
|
+
description: "Paired-agent pre-spec exploration: the Explorer proposes and the Challenger adversarially stress-tests across Objectives -> Approaches -> Tradeoffs -> Synthesis, producing a findings doc for human review",
|
|
2534
|
+
defaultImplementer: "claude",
|
|
2535
|
+
defaultReviewer: "codex",
|
|
2536
|
+
phases: [
|
|
2537
|
+
{
|
|
2538
|
+
name: "objectives",
|
|
2539
|
+
implementerRole: "implementer",
|
|
2540
|
+
reviewerRole: "reviewer",
|
|
2541
|
+
maxRounds: 6,
|
|
2542
|
+
initialHandoffStep: "implement",
|
|
2543
|
+
kickoffTemplate: DELIB_OBJECTIVES,
|
|
2544
|
+
stepTemplates: { implement: DELIB_OBJECTIVES, review: DELIB_REVIEW, fix: DELIB_FIX },
|
|
2545
|
+
reviewMode: "phase-review",
|
|
2546
|
+
evaluatorPromptKey: "deliberation-loop",
|
|
2547
|
+
artifactOut: { kind: "spec", pathTemplate: "{deliberationDir}/objectives.md" },
|
|
2548
|
+
renderFixTemplateOnFindings: true
|
|
2549
|
+
},
|
|
2550
|
+
{
|
|
2551
|
+
name: "approaches",
|
|
2552
|
+
implementerRole: "implementer",
|
|
2553
|
+
reviewerRole: "reviewer",
|
|
2554
|
+
maxRounds: 10,
|
|
2555
|
+
initialHandoffStep: "implement",
|
|
2556
|
+
kickoffTemplate: DELIB_APPROACHES,
|
|
2557
|
+
stepTemplates: { implement: DELIB_APPROACHES, review: DELIB_REVIEW, fix: DELIB_FIX },
|
|
2558
|
+
reviewMode: "phase-review",
|
|
2559
|
+
evaluatorPromptKey: "deliberation-loop",
|
|
2560
|
+
artifactOut: { kind: "spec", pathTemplate: "{deliberationDir}/approaches.md" },
|
|
2561
|
+
renderFixTemplateOnFindings: true
|
|
2562
|
+
},
|
|
2563
|
+
{
|
|
2564
|
+
name: "tradeoffs",
|
|
2565
|
+
implementerRole: "implementer",
|
|
2566
|
+
reviewerRole: "reviewer",
|
|
2567
|
+
maxRounds: 10,
|
|
2568
|
+
initialHandoffStep: "implement",
|
|
2569
|
+
kickoffTemplate: DELIB_TRADEOFFS,
|
|
2570
|
+
stepTemplates: { implement: DELIB_TRADEOFFS, review: DELIB_REVIEW, fix: DELIB_FIX },
|
|
2571
|
+
reviewMode: "phase-review",
|
|
2572
|
+
evaluatorPromptKey: "deliberation-loop",
|
|
2573
|
+
artifactOut: { kind: "spec", pathTemplate: "{deliberationDir}/tradeoffs.md" },
|
|
2574
|
+
renderFixTemplateOnFindings: true
|
|
2575
|
+
},
|
|
2576
|
+
{
|
|
2577
|
+
name: "synthesis",
|
|
2578
|
+
implementerRole: "implementer",
|
|
2579
|
+
reviewerRole: "reviewer",
|
|
2580
|
+
maxRounds: 5,
|
|
2581
|
+
initialHandoffStep: "implement",
|
|
2582
|
+
kickoffTemplate: DELIB_SYNTHESIS,
|
|
2583
|
+
stepTemplates: { implement: DELIB_SYNTHESIS, review: DELIB_SYNTHESIS_REVIEW, fix: DELIB_SYNTHESIS_FIX },
|
|
2584
|
+
reviewMode: "phase-review",
|
|
2585
|
+
evaluatorPromptKey: "deliberation-loop",
|
|
2586
|
+
artifactOut: { kind: "spec", pathTemplate: "{findingsPath}" },
|
|
2587
|
+
renderFixTemplateOnFindings: true
|
|
2588
|
+
}
|
|
2589
|
+
]
|
|
2590
|
+
};
|
|
2428
2591
|
function withOperatorControl(def) {
|
|
2429
2592
|
return {
|
|
2430
2593
|
...def,
|
|
@@ -2439,7 +2602,8 @@ ${WORKFLOW_OPERATOR_CONTROL}`
|
|
|
2439
2602
|
var REGISTRY = {
|
|
2440
2603
|
[SPEC_DRIVEN_DEVELOPMENT.type]: withOperatorControl(SPEC_DRIVEN_DEVELOPMENT),
|
|
2441
2604
|
[RALPH_LOOP.type]: withOperatorControl(RALPH_LOOP),
|
|
2442
|
-
[COMPLEX_BUG_FIXING.type]: withOperatorControl(COMPLEX_BUG_FIXING)
|
|
2605
|
+
[COMPLEX_BUG_FIXING.type]: withOperatorControl(COMPLEX_BUG_FIXING),
|
|
2606
|
+
[DELIBERATION.type]: withOperatorControl(DELIBERATION)
|
|
2443
2607
|
};
|
|
2444
2608
|
function ralphRunDir(workspaceRoot, workflowId) {
|
|
2445
2609
|
return join(workspaceRoot, ".ai-whisper", "ralph", workflowId);
|
|
@@ -2455,6 +2619,19 @@ function bugfixPaths(workspaceRoot, workflowId) {
|
|
|
2455
2619
|
postmortemPath: join(bugfixDir, "postmortem.md")
|
|
2456
2620
|
};
|
|
2457
2621
|
}
|
|
2622
|
+
function deliberationRunDir(workspaceRoot, workflowId) {
|
|
2623
|
+
return join(workspaceRoot, ".ai-whisper", "deliberation", workflowId);
|
|
2624
|
+
}
|
|
2625
|
+
function deriveFindingsPath(specPath, dateIso) {
|
|
2626
|
+
if (!/^\d{4}-\d{2}-\d{2}/.test(dateIso)) {
|
|
2627
|
+
throw new Error(`deriveFindingsPath: dateIso must start with YYYY-MM-DD, got "${dateIso}"`);
|
|
2628
|
+
}
|
|
2629
|
+
const basename3 = specPath.split("/").pop() ?? "";
|
|
2630
|
+
const withoutExt = basename3.replace(/\.[^.]+$/, "");
|
|
2631
|
+
const slug = withoutExt.replace(/^\d{4}-\d{2}-\d{2}-/, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "deliberation";
|
|
2632
|
+
const date = dateIso.slice(0, 10);
|
|
2633
|
+
return `docs/superpowers/deliberations/${date}-${slug}.md`;
|
|
2634
|
+
}
|
|
2458
2635
|
function getWorkflowDefinition(type) {
|
|
2459
2636
|
return REGISTRY[type];
|
|
2460
2637
|
}
|
|
@@ -2514,6 +2691,13 @@ function safeDerivePlanPath(specPath, createdAt) {
|
|
|
2514
2691
|
return `${dir}${stem}.plan.md`;
|
|
2515
2692
|
}
|
|
2516
2693
|
}
|
|
2694
|
+
function safeDeriveFindingsPath(specPath, createdAt) {
|
|
2695
|
+
try {
|
|
2696
|
+
return deriveFindingsPath(specPath, createdAt);
|
|
2697
|
+
} catch {
|
|
2698
|
+
return specPath;
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2517
2701
|
function composeResumeNotice(input) {
|
|
2518
2702
|
const hasFiles = input.changedFiles.length > 0;
|
|
2519
2703
|
const hasMessage = !!input.message && input.message.trim().length > 0;
|
|
@@ -2724,7 +2908,9 @@ function createWorkflowControl(deps) {
|
|
|
2724
2908
|
reviewMode,
|
|
2725
2909
|
bugfixDir: bf.bugfixDir,
|
|
2726
2910
|
diagnosisPath: bf.diagnosisPath,
|
|
2727
|
-
postmortemPath: bf.postmortemPath
|
|
2911
|
+
postmortemPath: bf.postmortemPath,
|
|
2912
|
+
deliberationDir: collab2 ? deliberationRunDir(collab2.workspaceRoot, input.workflow.workflowId) : "",
|
|
2913
|
+
findingsPath: safeDeriveFindingsPath(input.workflow.specPath, input.workflow.createdAt)
|
|
2728
2914
|
});
|
|
2729
2915
|
}
|
|
2730
2916
|
function consumeResumeNotice(input) {
|
|
@@ -2790,7 +2976,9 @@ ${requestText}` : requestText;
|
|
|
2790
2976
|
reviewMode: phase.reviewMode ?? "phase-review",
|
|
2791
2977
|
bugfixDir: bf.bugfixDir,
|
|
2792
2978
|
diagnosisPath: bf.diagnosisPath,
|
|
2793
|
-
postmortemPath: bf.postmortemPath
|
|
2979
|
+
postmortemPath: bf.postmortemPath,
|
|
2980
|
+
deliberationDir: collab2 ? deliberationRunDir(collab2.workspaceRoot, input.workflow.workflowId) : "",
|
|
2981
|
+
findingsPath: safeDeriveFindingsPath(input.workflow.specPath, input.workflow.createdAt)
|
|
2794
2982
|
});
|
|
2795
2983
|
const chainId = `relay_ch_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
2796
2984
|
const phaseRunId = `wfp_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
@@ -3062,7 +3250,9 @@ ${requestText}` : requestText;
|
|
|
3062
3250
|
ralphDir,
|
|
3063
3251
|
bugfixDir: bf.bugfixDir,
|
|
3064
3252
|
diagnosisPath: bf.diagnosisPath,
|
|
3065
|
-
postmortemPath: bf.postmortemPath
|
|
3253
|
+
postmortemPath: bf.postmortemPath,
|
|
3254
|
+
deliberationDir: collab2 ? deliberationRunDir(collab2.workspaceRoot, workflow.workflowId) : "",
|
|
3255
|
+
findingsPath: safeDeriveFindingsPath(workflow.specPath, workflow.createdAt)
|
|
3066
3256
|
});
|
|
3067
3257
|
fixRequestText = `${fixTmpl}
|
|
3068
3258
|
|
|
@@ -4386,7 +4576,7 @@ function enforceOneActiveCollabPerWorkspace(db, options = {}) {
|
|
|
4386
4576
|
|
|
4387
4577
|
// ../broker/dist/storage/clipboard-capture-lease.js
|
|
4388
4578
|
var LEASE_ID = 1;
|
|
4389
|
-
var DEFAULT_LEASE_TTL_MS =
|
|
4579
|
+
var DEFAULT_LEASE_TTL_MS = 25e3;
|
|
4390
4580
|
function defaultIsPidAlive2(pid) {
|
|
4391
4581
|
try {
|
|
4392
4582
|
process.kill(pid, 0);
|
|
@@ -5255,6 +5445,23 @@ function ensureBugfixWorkspace(workspaceRoot, workflowId) {
|
|
|
5255
5445
|
return dir;
|
|
5256
5446
|
}
|
|
5257
5447
|
|
|
5448
|
+
// ../broker/dist/runtime/deliberation-setup.js
|
|
5449
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, existsSync as existsSync4 } from "node:fs";
|
|
5450
|
+
import { join as join5 } from "node:path";
|
|
5451
|
+
function ensureDeliberationWorkspace(workspaceRoot, workflowId) {
|
|
5452
|
+
const dir = deliberationRunDir(workspaceRoot, workflowId);
|
|
5453
|
+
mkdirSync3(dir, { recursive: true });
|
|
5454
|
+
const ignorePath = join5(workspaceRoot, ".ai-whisper", ".gitignore");
|
|
5455
|
+
if (!existsSync4(ignorePath)) {
|
|
5456
|
+
writeFileSync3(ignorePath, "*\n");
|
|
5457
|
+
}
|
|
5458
|
+
const deliberationIgnorePath = join5(workspaceRoot, ".ai-whisper", "deliberation", ".gitignore");
|
|
5459
|
+
if (!existsSync4(deliberationIgnorePath)) {
|
|
5460
|
+
writeFileSync3(deliberationIgnorePath, "*\n");
|
|
5461
|
+
}
|
|
5462
|
+
return dir;
|
|
5463
|
+
}
|
|
5464
|
+
|
|
5258
5465
|
// ../broker/dist/runtime/workflow-driver.js
|
|
5259
5466
|
function createWorkflowDriver(deps) {
|
|
5260
5467
|
const { broker: broker2, headReader } = deps;
|
|
@@ -5357,6 +5564,20 @@ function createWorkflowDriver(deps) {
|
|
|
5357
5564
|
}
|
|
5358
5565
|
}
|
|
5359
5566
|
const bugfix = bugfixPaths(collab2.workspaceRoot, workflowId);
|
|
5567
|
+
if (workflow.workflowType === "deliberation") {
|
|
5568
|
+
try {
|
|
5569
|
+
ensureDeliberationWorkspace(collab2.workspaceRoot, workflowId);
|
|
5570
|
+
} catch (err) {
|
|
5571
|
+
broker2.control.haltWorkflow({ workflowId, reason: `deliberation setup failed: ${String(err)}`, now });
|
|
5572
|
+
return;
|
|
5573
|
+
}
|
|
5574
|
+
}
|
|
5575
|
+
const deliberationDir = deliberationRunDir(collab2.workspaceRoot, workflowId);
|
|
5576
|
+
let findingsPath = workflow.specPath;
|
|
5577
|
+
try {
|
|
5578
|
+
findingsPath = deriveFindingsPath(workflow.specPath, workflow.createdAt);
|
|
5579
|
+
} catch {
|
|
5580
|
+
}
|
|
5360
5581
|
const ctx = workflow.workflowContext;
|
|
5361
5582
|
let planPath = workflow.specPath;
|
|
5362
5583
|
try {
|
|
@@ -5371,7 +5592,9 @@ function createWorkflowDriver(deps) {
|
|
|
5371
5592
|
reviewMode: phase.reviewMode ?? "phase-review",
|
|
5372
5593
|
bugfixDir: bugfix.bugfixDir,
|
|
5373
5594
|
diagnosisPath: bugfix.diagnosisPath,
|
|
5374
|
-
postmortemPath: bugfix.postmortemPath
|
|
5595
|
+
postmortemPath: bugfix.postmortemPath,
|
|
5596
|
+
deliberationDir,
|
|
5597
|
+
findingsPath
|
|
5375
5598
|
});
|
|
5376
5599
|
try {
|
|
5377
5600
|
broker2.control.beginPhaseRun({
|
|
@@ -5536,7 +5759,7 @@ var EVENT_PROTOCOL_VERSION = "1";
|
|
|
5536
5759
|
|
|
5537
5760
|
// ../broker/dist/runtime/event-socket-server.js
|
|
5538
5761
|
import { createServer } from "node:net";
|
|
5539
|
-
import { existsSync as
|
|
5762
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "node:fs";
|
|
5540
5763
|
import { dirname as dirname2 } from "node:path";
|
|
5541
5764
|
var EVENT_NAME_TABLE = {
|
|
5542
5765
|
"chain.resolved": true,
|
|
@@ -5555,8 +5778,8 @@ var ALL_BROKER_EVENT_NAMES = Object.keys(EVENT_NAME_TABLE);
|
|
|
5555
5778
|
async function createEventSocketServer(input) {
|
|
5556
5779
|
const now = input.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
5557
5780
|
const clients = /* @__PURE__ */ new Set();
|
|
5558
|
-
|
|
5559
|
-
if (
|
|
5781
|
+
mkdirSync4(dirname2(input.socketPath), { recursive: true });
|
|
5782
|
+
if (existsSync5(input.socketPath)) {
|
|
5560
5783
|
try {
|
|
5561
5784
|
unlinkSync2(input.socketPath);
|
|
5562
5785
|
} catch {
|
|
@@ -5612,7 +5835,7 @@ async function createEventSocketServer(input) {
|
|
|
5612
5835
|
dropClient(conn);
|
|
5613
5836
|
server.close(() => {
|
|
5614
5837
|
try {
|
|
5615
|
-
if (
|
|
5838
|
+
if (existsSync5(input.socketPath))
|
|
5616
5839
|
unlinkSync2(input.socketPath);
|
|
5617
5840
|
} catch {
|
|
5618
5841
|
}
|
|
@@ -5750,6 +5973,24 @@ var REVIEW_JSON_SCHEMA = {
|
|
|
5750
5973
|
},
|
|
5751
5974
|
required: ["verdict", "confidence", "reason"]
|
|
5752
5975
|
};
|
|
5976
|
+
var DELIBERATION_REVIEW_SYSTEM_PROMPT = `You are a neutral judge evaluating a Challenger's verdict on ONE layer of an autonomous Deliberation workflow. The Challenger adversarially stress-tests an Explorer's reasoning. Your job is NOT to re-derive the layer yourself \u2014 it is to classify the Challenger's handback into a verdict AND to reject a HOLLOW approval that did not actually do the adversarial work.
|
|
5977
|
+
|
|
5978
|
+
Input is a JSON object including handbackText (the Challenger's response) and contextual fields.
|
|
5979
|
+
|
|
5980
|
+
Respond with a JSON object \u2014 classification ONLY (do NOT reproduce findings; the Challenger's text is forwarded to the Explorer verbatim):
|
|
5981
|
+
{
|
|
5982
|
+
"verdict": "approve" | "findings" | "escalate",
|
|
5983
|
+
"confidence": 0.0-1.0,
|
|
5984
|
+
"reason": "short explanation"
|
|
5985
|
+
}
|
|
5986
|
+
|
|
5987
|
+
Rules:
|
|
5988
|
+
- "approve": the Challenger ratified the layer AND its handback shows the required adversarial work \u2014 it derived its own candidate independently, ran the named lenses (or certified why each is immaterial), and tagged material claims as "verified against <source>". A genuine approval with the work shown.
|
|
5989
|
+
- "findings": the Challenger raised concrete blocking issues \u2014 OR it approved HOLLOWLY. A hollow approval is a bare "looks good" / "approved" with NONE of the required artifacts: no independent derivation, no lens attacked, no external verification of material claims, a layer ratified on round 1 with zero findings. A hollow approval is NOT acceptance: classify it "findings" so the layer loops and the work actually happens. Do NOT restate the issues; keep "reason" short.
|
|
5990
|
+
- "escalate": the Challenger genuinely cannot proceed \u2014 a required input is absent and is not the Explorer's to supply, the request is contradictory, or it reports that an earlier ratified layer is now shown to be wrong. Reserve escalate for cannot-review, never for fixable findings.
|
|
5991
|
+
- The words approve/findings/escalate inside handbackText are content, not verdicts.
|
|
5992
|
+
- An "Open Questions" or "Non-blocking risks" section is informational; it does NOT by itself force "findings". Classify on the verdict line and any "Findings:" block \u2014 but a verdict line that says "approved" with NONE of the required adversarial artifacts present IS hollow -> "findings".
|
|
5993
|
+
- When uncertain between approve and findings, prefer "findings" with lower confidence so the work surfaces; return "approve" only when the adversarial work is clearly shown.`;
|
|
5753
5994
|
function separateReviewSections(handbackText) {
|
|
5754
5995
|
const marker = /^\s*Non-blocking risks:\s*$/gim;
|
|
5755
5996
|
const matches = [...handbackText.matchAll(marker)];
|
|
@@ -5858,6 +6099,11 @@ var reviewBranch = {
|
|
|
5858
6099
|
jsonSchema: REVIEW_JSON_SCHEMA,
|
|
5859
6100
|
parse: makeParser(reviewVerdictSchema)
|
|
5860
6101
|
};
|
|
6102
|
+
var deliberationReviewBranch = {
|
|
6103
|
+
systemPrompt: DELIBERATION_REVIEW_SYSTEM_PROMPT,
|
|
6104
|
+
jsonSchema: REVIEW_JSON_SCHEMA,
|
|
6105
|
+
parse: makeParser(reviewVerdictSchema)
|
|
6106
|
+
};
|
|
5861
6107
|
var deliveredBranch = {
|
|
5862
6108
|
systemPrompt: DELIVERED_SYSTEM_PROMPT,
|
|
5863
6109
|
jsonSchema: DELIVERED_JSON_SCHEMA,
|
|
@@ -5876,7 +6122,9 @@ var executionBranch = {
|
|
|
5876
6122
|
function selectBranch(payload) {
|
|
5877
6123
|
if ("evaluatorPromptKey" in payload) {
|
|
5878
6124
|
if (payload.evaluatorPromptKey === "execution-gate") return executionBranch;
|
|
5879
|
-
if (payload.handoffStep === "review")
|
|
6125
|
+
if (payload.handoffStep === "review") {
|
|
6126
|
+
return payload.evaluatorPromptKey === "deliberation-loop" ? deliberationReviewBranch : reviewBranch;
|
|
6127
|
+
}
|
|
5880
6128
|
if (payload.handoffStep === "implement" || payload.handoffStep === "fix") {
|
|
5881
6129
|
return payload.evaluatorPromptKey === "ralph-loop" ? ralphDeliveredBranch : deliveredBranch;
|
|
5882
6130
|
}
|
|
@@ -6031,7 +6279,7 @@ function createRelayOrchestratorEvaluator(input) {
|
|
|
6031
6279
|
return async function evaluateRelayHandoff(call) {
|
|
6032
6280
|
const callGroupId = crypto3.randomUUID();
|
|
6033
6281
|
const branch = selectBranch(call.payload);
|
|
6034
|
-
const branchName = branch === legacyBranch ? "legacy" : branch === reviewBranch ? "review" : branch === deliveredBranch || branch === ralphDeliveredBranch ? "delivered" : "execution";
|
|
6282
|
+
const branchName = branch === legacyBranch ? "legacy" : branch === reviewBranch || branch === deliberationReviewBranch ? "review" : branch === deliveredBranch || branch === ralphDeliveredBranch ? "delivered" : "execution";
|
|
6035
6283
|
const effectivePayload = branch === reviewBranch && "handbackText" in call.payload ? {
|
|
6036
6284
|
...call.payload,
|
|
6037
6285
|
handbackText: separateReviewSections(call.payload.handbackText).body
|
|
@@ -6437,7 +6685,7 @@ function writeOwnPidToBrokerDaemon(db, input) {
|
|
|
6437
6685
|
|
|
6438
6686
|
// src/runtime/evaluator-config.ts
|
|
6439
6687
|
import { readFileSync as readFileSync2, statSync as statSync3 } from "node:fs";
|
|
6440
|
-
import { join as
|
|
6688
|
+
import { join as join6 } from "node:path";
|
|
6441
6689
|
|
|
6442
6690
|
// src/runtime/state-root.ts
|
|
6443
6691
|
import os from "node:os";
|
|
@@ -6499,15 +6747,15 @@ function loadEvaluatorConfig() {
|
|
|
6499
6747
|
const root = getStateRoot();
|
|
6500
6748
|
let dotenv = {};
|
|
6501
6749
|
try {
|
|
6502
|
-
dotenv = parseDotEnv(readFileSync2(
|
|
6750
|
+
dotenv = parseDotEnv(readFileSync2(join6(root, ".env"), "utf8"));
|
|
6503
6751
|
} catch (err) {
|
|
6504
6752
|
if (err.code !== "ENOENT") throw err;
|
|
6505
6753
|
}
|
|
6506
6754
|
const envGet = (k) => process.env[k] ?? dotenv[k];
|
|
6507
|
-
const authPath =
|
|
6755
|
+
const authPath = join6(root, "auth.json");
|
|
6508
6756
|
warnIfLoosePerms(authPath);
|
|
6509
6757
|
const auth = readJsonFile(authPath, "auth.json");
|
|
6510
|
-
const config = readJsonFile(
|
|
6758
|
+
const config = readJsonFile(join6(root, "config.json"), "config.json");
|
|
6511
6759
|
const evalCfg = config?.evaluator ?? {};
|
|
6512
6760
|
const evalOllama = evalCfg.ollama ?? {};
|
|
6513
6761
|
const provider = (envGet("AI_WHISPER_EVALUATOR_PROVIDER") ?? evalCfg.provider) === "ollama" ? "ollama" : "anthropic";
|
|
@@ -6554,7 +6802,7 @@ function recordEvaluatorStatus(db, input) {
|
|
|
6554
6802
|
|
|
6555
6803
|
// src/bin/broker-daemon.ts
|
|
6556
6804
|
import { execFile as execFile2 } from "node:child_process";
|
|
6557
|
-
import { join as
|
|
6805
|
+
import { join as join7 } from "node:path";
|
|
6558
6806
|
|
|
6559
6807
|
// src/runtime/cli-package-info.ts
|
|
6560
6808
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
@@ -6604,7 +6852,7 @@ if (collabId) {
|
|
|
6604
6852
|
});
|
|
6605
6853
|
workflowEventBridge.start();
|
|
6606
6854
|
eventSocket = await createEventSocketServer({
|
|
6607
|
-
socketPath:
|
|
6855
|
+
socketPath: join7(getStateSocketsDir(), `events-${collabId}.sock`),
|
|
6608
6856
|
events: [broker.events, externalEvents],
|
|
6609
6857
|
engineVersion: resolveCliVersion()
|
|
6610
6858
|
});
|