@nathapp/nax 0.59.0 → 0.59.2
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/dist/nax.js +612 -204
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -18269,7 +18269,16 @@ var init_schemas3 = __esm(() => {
|
|
|
18269
18269
|
modelTier: ModelTierSchema.default("balanced"),
|
|
18270
18270
|
rules: exports_external.array(exports_external.string()).default([]),
|
|
18271
18271
|
timeoutMs: exports_external.number().int().positive().default(600000),
|
|
18272
|
-
excludePatterns: exports_external.array(exports_external.string()).default([
|
|
18272
|
+
excludePatterns: exports_external.array(exports_external.string()).default([
|
|
18273
|
+
":!test/",
|
|
18274
|
+
":!tests/",
|
|
18275
|
+
":!*_test.go",
|
|
18276
|
+
":!*.test.ts",
|
|
18277
|
+
":!*.spec.ts",
|
|
18278
|
+
":!**/__tests__/",
|
|
18279
|
+
":!.nax/",
|
|
18280
|
+
":!.nax-pids"
|
|
18281
|
+
])
|
|
18273
18282
|
});
|
|
18274
18283
|
ReviewDialogueConfigSchema = exports_external.object({
|
|
18275
18284
|
enabled: exports_external.boolean().default(false),
|
|
@@ -18614,7 +18623,16 @@ var init_schemas3 = __esm(() => {
|
|
|
18614
18623
|
modelTier: "balanced",
|
|
18615
18624
|
rules: [],
|
|
18616
18625
|
timeoutMs: 600000,
|
|
18617
|
-
excludePatterns: [
|
|
18626
|
+
excludePatterns: [
|
|
18627
|
+
":!test/",
|
|
18628
|
+
":!tests/",
|
|
18629
|
+
":!*_test.go",
|
|
18630
|
+
":!*.test.ts",
|
|
18631
|
+
":!*.spec.ts",
|
|
18632
|
+
":!**/__tests__/",
|
|
18633
|
+
":!.nax/",
|
|
18634
|
+
":!.nax-pids"
|
|
18635
|
+
]
|
|
18618
18636
|
},
|
|
18619
18637
|
dialogue: {
|
|
18620
18638
|
enabled: false,
|
|
@@ -19242,11 +19260,15 @@ class AcpAgentAdapter {
|
|
|
19242
19260
|
}
|
|
19243
19261
|
runState.succeeded = !timedOut && lastResponse?.stopReason === "end_turn";
|
|
19244
19262
|
} finally {
|
|
19263
|
+
const isSessionBroken = !runState.succeeded && lastResponse?.stopReason === "error";
|
|
19245
19264
|
if (runState.succeeded && !options.keepSessionOpen) {
|
|
19246
19265
|
await closeAcpSession(session);
|
|
19247
19266
|
if (options.featureName && options.storyId) {
|
|
19248
19267
|
await clearAcpSession(options.workdir, options.featureName, options.storyId, options.sessionRole);
|
|
19249
19268
|
}
|
|
19269
|
+
} else if (isSessionBroken) {
|
|
19270
|
+
getSafeLogger()?.debug("acp-adapter", "Closing broken session for retry", { sessionName });
|
|
19271
|
+
await closeAcpSession(session);
|
|
19250
19272
|
} else if (!runState.succeeded) {
|
|
19251
19273
|
getSafeLogger()?.info("acp-adapter", "Keeping session open for retry", { sessionName });
|
|
19252
19274
|
} else {
|
|
@@ -19495,7 +19517,7 @@ class AcpAgentAdapter {
|
|
|
19495
19517
|
this.markUnavailable(this.name);
|
|
19496
19518
|
throw new Error("[acp-adapter] plan() returned empty spec content");
|
|
19497
19519
|
}
|
|
19498
|
-
return { specContent };
|
|
19520
|
+
return { specContent, costUsd: result.estimatedCost };
|
|
19499
19521
|
}
|
|
19500
19522
|
async decompose(options) {
|
|
19501
19523
|
const model = options.modelDef?.model;
|
|
@@ -21453,7 +21475,10 @@ function majorityResolver(proposals, failOpen) {
|
|
|
21453
21475
|
return passCount > failCount ? "passed" : "failed";
|
|
21454
21476
|
}
|
|
21455
21477
|
async function synthesisResolver(proposals, critiques, opts) {
|
|
21456
|
-
const
|
|
21478
|
+
const base = buildSynthesisPrompt(proposals, critiques);
|
|
21479
|
+
const prompt = opts.promptSuffix ? `${base}
|
|
21480
|
+
|
|
21481
|
+
${opts.promptSuffix}` : base;
|
|
21457
21482
|
return opts.adapter.complete(prompt, opts.completeOptions);
|
|
21458
21483
|
}
|
|
21459
21484
|
async function judgeResolver(proposals, critiques, resolverConfig, opts) {
|
|
@@ -21521,25 +21546,89 @@ function pipelineStageForDebate(stage) {
|
|
|
21521
21546
|
}
|
|
21522
21547
|
}
|
|
21523
21548
|
function resolveModelDefForDebater(debater, tier, config2) {
|
|
21524
|
-
|
|
21525
|
-
|
|
21549
|
+
const modelOverride = debater.model;
|
|
21550
|
+
let effectiveTier = tier;
|
|
21551
|
+
if (modelOverride) {
|
|
21552
|
+
const aliasedTier = MODEL_SHORTHAND_TIERS[modelOverride.toLowerCase()];
|
|
21553
|
+
if (aliasedTier) {
|
|
21554
|
+
effectiveTier = aliasedTier;
|
|
21555
|
+
} else if (!isTierLabel(modelOverride)) {
|
|
21556
|
+
return resolveModel(modelOverride);
|
|
21557
|
+
}
|
|
21526
21558
|
}
|
|
21527
21559
|
const configModels = config2?.models ?? DEFAULT_CONFIG.models;
|
|
21528
21560
|
const configDefaultAgent = config2?.autoMode?.defaultAgent ?? DEFAULT_CONFIG.autoMode.defaultAgent;
|
|
21529
21561
|
try {
|
|
21530
|
-
return resolveModelForAgent(configModels, debater.agent,
|
|
21562
|
+
return resolveModelForAgent(configModels, debater.agent, effectiveTier, configDefaultAgent);
|
|
21531
21563
|
} catch {}
|
|
21532
21564
|
try {
|
|
21533
|
-
return resolveModelForAgent(DEFAULT_CONFIG.models, DEFAULT_CONFIG.autoMode.defaultAgent,
|
|
21565
|
+
return resolveModelForAgent(DEFAULT_CONFIG.models, DEFAULT_CONFIG.autoMode.defaultAgent, effectiveTier, DEFAULT_CONFIG.autoMode.defaultAgent);
|
|
21534
21566
|
} catch {
|
|
21535
21567
|
return resolveModelForAgent(configModels, debater.agent, "fast", configDefaultAgent);
|
|
21536
21568
|
}
|
|
21537
21569
|
}
|
|
21538
|
-
async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, config2, storyId, timeoutMs, workdir, featureName) {
|
|
21570
|
+
async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, config2, storyId, timeoutMs, workdir, featureName, reviewerSession, resolverContext, promptSuffix) {
|
|
21539
21571
|
const resolverConfig = stageConfig.resolver;
|
|
21540
21572
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
21573
|
+
if (reviewerSession && resolverContext) {
|
|
21574
|
+
try {
|
|
21575
|
+
const debateCtx = {
|
|
21576
|
+
resolverType: resolverConfig.type
|
|
21577
|
+
};
|
|
21578
|
+
if (resolverConfig.type === "majority-fail-closed" || resolverConfig.type === "majority-fail-open") {
|
|
21579
|
+
const failOpen = resolverConfig.type === "majority-fail-open";
|
|
21580
|
+
const rawOutcome = majorityResolver(proposalOutputs, failOpen);
|
|
21581
|
+
let passCount = 0;
|
|
21582
|
+
let failCount = 0;
|
|
21583
|
+
for (const proposal of proposalOutputs) {
|
|
21584
|
+
try {
|
|
21585
|
+
const stripped = proposal.trim().replace(/^```(?:json)?\s*\n?/, "").replace(/\n?```\s*$/, "");
|
|
21586
|
+
const parsed = JSON.parse(stripped);
|
|
21587
|
+
if (typeof parsed.passed === "boolean" && parsed.passed)
|
|
21588
|
+
passCount++;
|
|
21589
|
+
else if (failOpen)
|
|
21590
|
+
passCount++;
|
|
21591
|
+
else
|
|
21592
|
+
failCount++;
|
|
21593
|
+
} catch {
|
|
21594
|
+
if (failOpen)
|
|
21595
|
+
passCount++;
|
|
21596
|
+
else
|
|
21597
|
+
failCount++;
|
|
21598
|
+
}
|
|
21599
|
+
}
|
|
21600
|
+
debateCtx.majorityVote = { passed: rawOutcome === "passed", passCount, failCount };
|
|
21601
|
+
}
|
|
21602
|
+
const story = {
|
|
21603
|
+
id: resolverContext.story.id,
|
|
21604
|
+
title: resolverContext.story.title,
|
|
21605
|
+
description: "",
|
|
21606
|
+
acceptanceCriteria: resolverContext.story.acceptanceCriteria
|
|
21607
|
+
};
|
|
21608
|
+
let dialogueResult;
|
|
21609
|
+
if (resolverContext.isReReview) {
|
|
21610
|
+
dialogueResult = await reviewerSession.reReviewDebate(resolverContext.labeledProposals, critiqueOutputs, resolverContext.diff, debateCtx);
|
|
21611
|
+
} else {
|
|
21612
|
+
dialogueResult = await reviewerSession.resolveDebate(resolverContext.labeledProposals, critiqueOutputs, resolverContext.diff, story, resolverContext.semanticConfig, debateCtx);
|
|
21613
|
+
}
|
|
21614
|
+
const outcome = dialogueResult.checkResult.success ? "passed" : "failed";
|
|
21615
|
+
return {
|
|
21616
|
+
outcome,
|
|
21617
|
+
resolverCostUsd: dialogueResult.cost ?? 0,
|
|
21618
|
+
dialogueResult
|
|
21619
|
+
};
|
|
21620
|
+
} catch (err) {
|
|
21621
|
+
logger?.warn("debate", "ReviewerSession.resolveDebate() failed \u2014 falling back to stateless resolver", {
|
|
21622
|
+
storyId,
|
|
21623
|
+
error: err instanceof Error ? err.message : String(err)
|
|
21624
|
+
});
|
|
21625
|
+
}
|
|
21626
|
+
}
|
|
21627
|
+
if (reviewerSession && !resolverContext) {
|
|
21628
|
+
logger?.warn("debate", "ReviewerSession provided but resolverContext is undefined \u2014 falling back to stateless resolver", { storyId });
|
|
21629
|
+
}
|
|
21541
21630
|
if (resolverConfig.type === "majority-fail-closed" || resolverConfig.type === "majority-fail-open") {
|
|
21542
|
-
if (workdir !== undefined) {
|
|
21631
|
+
if (workdir !== undefined && !reviewerSession) {
|
|
21543
21632
|
logger?.warn("debate", "majority resolver does not support implementer session resumption \u2014 switch to synthesis or custom resolver for context-aware semantic review");
|
|
21544
21633
|
}
|
|
21545
21634
|
return {
|
|
@@ -21547,31 +21636,36 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
|
|
|
21547
21636
|
resolverCostUsd: 0
|
|
21548
21637
|
};
|
|
21549
21638
|
}
|
|
21550
|
-
const implementerSessionName = workdir !== undefined ? buildSessionName(workdir, featureName, storyId, "implementer") : undefined;
|
|
21551
21639
|
if (resolverConfig.type === "synthesis") {
|
|
21552
21640
|
const agentName = resolverConfig.agent ?? RESOLVER_FALLBACK_AGENT;
|
|
21553
21641
|
const adapter = _debateSessionDeps.getAgent(agentName, config2);
|
|
21554
21642
|
if (adapter) {
|
|
21643
|
+
const synthesisSessionName = workdir !== undefined ? buildSessionName(workdir, featureName, storyId, "synthesis") : undefined;
|
|
21555
21644
|
const resolverResult = await synthesisResolver(proposalOutputs, critiqueOutputs, {
|
|
21556
21645
|
adapter,
|
|
21646
|
+
promptSuffix,
|
|
21557
21647
|
completeOptions: {
|
|
21558
21648
|
model: resolveDebaterModel({ agent: agentName }, config2),
|
|
21559
21649
|
config: config2,
|
|
21560
21650
|
storyId,
|
|
21651
|
+
featureName,
|
|
21652
|
+
workdir,
|
|
21561
21653
|
sessionRole: "synthesis",
|
|
21562
21654
|
timeoutMs,
|
|
21563
|
-
...
|
|
21655
|
+
...synthesisSessionName !== undefined && { sessionName: synthesisSessionName }
|
|
21564
21656
|
}
|
|
21565
21657
|
});
|
|
21566
21658
|
return {
|
|
21567
21659
|
outcome: "passed",
|
|
21568
|
-
resolverCostUsd: resolverResult.costUsd
|
|
21660
|
+
resolverCostUsd: resolverResult.costUsd,
|
|
21661
|
+
output: resolverResult.output
|
|
21569
21662
|
};
|
|
21570
21663
|
}
|
|
21571
21664
|
return { outcome: "passed", resolverCostUsd: 0 };
|
|
21572
21665
|
}
|
|
21573
21666
|
if (resolverConfig.type === "custom") {
|
|
21574
21667
|
const agentName = resolverConfig.agent ?? RESOLVER_FALLBACK_AGENT;
|
|
21668
|
+
const judgeSessionName = workdir !== undefined ? buildSessionName(workdir, featureName, storyId, "judge") : undefined;
|
|
21575
21669
|
const resolverResult = await judgeResolver(proposalOutputs, critiqueOutputs, resolverConfig, {
|
|
21576
21670
|
getAgent: (name) => _debateSessionDeps.getAgent(name, config2),
|
|
21577
21671
|
defaultAgentName: RESOLVER_FALLBACK_AGENT,
|
|
@@ -21579,19 +21673,22 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
|
|
|
21579
21673
|
model: resolveDebaterModel({ agent: agentName }, config2),
|
|
21580
21674
|
config: config2,
|
|
21581
21675
|
storyId,
|
|
21676
|
+
featureName,
|
|
21677
|
+
workdir,
|
|
21582
21678
|
sessionRole: "judge",
|
|
21583
21679
|
timeoutMs,
|
|
21584
|
-
...
|
|
21680
|
+
...judgeSessionName !== undefined && { sessionName: judgeSessionName }
|
|
21585
21681
|
}
|
|
21586
21682
|
});
|
|
21587
21683
|
return {
|
|
21588
21684
|
outcome: "passed",
|
|
21589
|
-
resolverCostUsd: resolverResult.costUsd
|
|
21685
|
+
resolverCostUsd: resolverResult.costUsd,
|
|
21686
|
+
output: resolverResult.output
|
|
21590
21687
|
};
|
|
21591
21688
|
}
|
|
21592
21689
|
return { outcome: "passed", resolverCostUsd: 0 };
|
|
21593
21690
|
}
|
|
21594
|
-
var RESOLVER_FALLBACK_AGENT = "synthesis", _debateSessionDeps;
|
|
21691
|
+
var RESOLVER_FALLBACK_AGENT = "synthesis", _debateSessionDeps, MODEL_SHORTHAND_TIERS;
|
|
21595
21692
|
var init_session_helpers = __esm(() => {
|
|
21596
21693
|
init_adapter();
|
|
21597
21694
|
init_registry();
|
|
@@ -21603,6 +21700,11 @@ var init_session_helpers = __esm(() => {
|
|
|
21603
21700
|
getSafeLogger,
|
|
21604
21701
|
readFile: (path) => Bun.file(path).text()
|
|
21605
21702
|
};
|
|
21703
|
+
MODEL_SHORTHAND_TIERS = {
|
|
21704
|
+
haiku: "fast",
|
|
21705
|
+
sonnet: "balanced",
|
|
21706
|
+
opus: "powerful"
|
|
21707
|
+
};
|
|
21606
21708
|
});
|
|
21607
21709
|
|
|
21608
21710
|
// src/debate/concurrency.ts
|
|
@@ -21788,7 +21890,11 @@ async function runStateful(ctx, prompt) {
|
|
|
21788
21890
|
critiqueOutputs = critiqueSettled.filter((r) => r.status === "fulfilled").map((r) => r.value.output);
|
|
21789
21891
|
}
|
|
21790
21892
|
const proposalOutputs = successfulProposals.map((s) => s.output);
|
|
21791
|
-
const
|
|
21893
|
+
const fullResolverContext = ctx.resolverContextInput ? {
|
|
21894
|
+
...ctx.resolverContextInput,
|
|
21895
|
+
labeledProposals: successfulProposals.map((s) => ({ debater: s.debater.agent, output: s.output }))
|
|
21896
|
+
} : undefined;
|
|
21897
|
+
const outcome = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.storyId, ctx.timeoutSeconds * 1000, ctx.workdir, ctx.featureName, ctx.reviewerSession, fullResolverContext);
|
|
21792
21898
|
totalCostUsd += outcome.resolverCostUsd;
|
|
21793
21899
|
const proposals = successfulProposals.map((s) => ({
|
|
21794
21900
|
debater: s.debater,
|
|
@@ -21815,6 +21921,50 @@ var init_session_stateful = __esm(() => {
|
|
|
21815
21921
|
});
|
|
21816
21922
|
|
|
21817
21923
|
// src/debate/session-hybrid.ts
|
|
21924
|
+
async function runRebuttalLoop(ctx, proposals, originalPrompt, sessionRolePrefix) {
|
|
21925
|
+
const logger = _debateSessionDeps.getSafeLogger();
|
|
21926
|
+
const config2 = ctx.stageConfig;
|
|
21927
|
+
const rebuttals = [];
|
|
21928
|
+
let costUsd = 0;
|
|
21929
|
+
const proposalList = proposals.map((s) => ({ debater: s.debater, output: s.output }));
|
|
21930
|
+
try {
|
|
21931
|
+
for (let round = 1;round <= config2.rounds; round++) {
|
|
21932
|
+
const priorRebuttals = rebuttals.filter((r) => r.round < round).map((r) => r.output);
|
|
21933
|
+
for (let debaterIdx = 0;debaterIdx < proposals.length; debaterIdx++) {
|
|
21934
|
+
const proposal = proposals[debaterIdx];
|
|
21935
|
+
const sessionRole = `${sessionRolePrefix}-${debaterIdx}`;
|
|
21936
|
+
logger?.info("debate:rebuttal-start", "debate:rebuttal-start", {
|
|
21937
|
+
storyId: ctx.storyId,
|
|
21938
|
+
round,
|
|
21939
|
+
debaterIndex: debaterIdx
|
|
21940
|
+
});
|
|
21941
|
+
const rebuttalPrompt = buildRebuttalContext(originalPrompt, proposalList, priorRebuttals, debaterIdx);
|
|
21942
|
+
try {
|
|
21943
|
+
const turnResult = await runStatefulTurn(ctx, proposal.adapter, proposal.debater, rebuttalPrompt, sessionRole, true);
|
|
21944
|
+
costUsd += turnResult.cost;
|
|
21945
|
+
rebuttals.push({ debater: proposal.debater, round, output: turnResult.output });
|
|
21946
|
+
} catch (err) {
|
|
21947
|
+
logger?.warn("debate", "debate:rebuttal-failed", {
|
|
21948
|
+
storyId: ctx.storyId,
|
|
21949
|
+
round,
|
|
21950
|
+
debaterIndex: debaterIdx,
|
|
21951
|
+
error: err instanceof Error ? err.message : String(err)
|
|
21952
|
+
});
|
|
21953
|
+
}
|
|
21954
|
+
}
|
|
21955
|
+
}
|
|
21956
|
+
} finally {
|
|
21957
|
+
for (let debaterIdx = 0;debaterIdx < proposals.length; debaterIdx++) {
|
|
21958
|
+
const proposal = proposals[debaterIdx];
|
|
21959
|
+
const sessionRole = `${sessionRolePrefix}-${debaterIdx}`;
|
|
21960
|
+
try {
|
|
21961
|
+
const closeCost = await closeStatefulSession(ctx, proposal.adapter, proposal.debater, sessionRole);
|
|
21962
|
+
costUsd += closeCost;
|
|
21963
|
+
} catch {}
|
|
21964
|
+
}
|
|
21965
|
+
}
|
|
21966
|
+
return { rebuttals, costUsd };
|
|
21967
|
+
}
|
|
21818
21968
|
async function runHybrid(ctx, prompt) {
|
|
21819
21969
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
21820
21970
|
const config2 = ctx.stageConfig;
|
|
@@ -21883,45 +22033,14 @@ async function runHybrid(ctx, prompt) {
|
|
|
21883
22033
|
}
|
|
21884
22034
|
const proposalOutputs = successfulProposals.map((s) => s.output);
|
|
21885
22035
|
const proposalList = successfulProposals.map((s) => ({ debater: s.debater, output: s.output }));
|
|
21886
|
-
const rebuttals =
|
|
21887
|
-
|
|
21888
|
-
for (let round = 1;round <= config2.rounds; round++) {
|
|
21889
|
-
const priorRebuttals = rebuttals.filter((r) => r.round < round).map((r) => r.output);
|
|
21890
|
-
for (let debaterIdx = 0;debaterIdx < successfulProposals.length; debaterIdx++) {
|
|
21891
|
-
const proposal = successfulProposals[debaterIdx];
|
|
21892
|
-
const sessionRole = `debate-hybrid-${debaterIdx}`;
|
|
21893
|
-
logger?.info("debate:rebuttal-start", "debate:rebuttal-start", {
|
|
21894
|
-
storyId: ctx.storyId,
|
|
21895
|
-
round,
|
|
21896
|
-
debaterIndex: debaterIdx
|
|
21897
|
-
});
|
|
21898
|
-
const rebuttalPrompt = buildRebuttalContext(prompt, proposalList, priorRebuttals, debaterIdx);
|
|
21899
|
-
try {
|
|
21900
|
-
const turnResult = await runStatefulTurn(ctx, proposal.adapter, proposal.debater, rebuttalPrompt, sessionRole, true);
|
|
21901
|
-
totalCostUsd += turnResult.cost;
|
|
21902
|
-
rebuttals.push({ debater: proposal.debater, round, output: turnResult.output });
|
|
21903
|
-
} catch (err) {
|
|
21904
|
-
logger?.warn("debate", "debate:rebuttal-failed", {
|
|
21905
|
-
storyId: ctx.storyId,
|
|
21906
|
-
round,
|
|
21907
|
-
debaterIndex: debaterIdx,
|
|
21908
|
-
error: err instanceof Error ? err.message : String(err)
|
|
21909
|
-
});
|
|
21910
|
-
}
|
|
21911
|
-
}
|
|
21912
|
-
}
|
|
21913
|
-
} finally {
|
|
21914
|
-
for (let debaterIdx = 0;debaterIdx < successfulProposals.length; debaterIdx++) {
|
|
21915
|
-
const proposal = successfulProposals[debaterIdx];
|
|
21916
|
-
const sessionRole = `debate-hybrid-${debaterIdx}`;
|
|
21917
|
-
try {
|
|
21918
|
-
const closeCost = await closeStatefulSession(ctx, proposal.adapter, proposal.debater, sessionRole);
|
|
21919
|
-
totalCostUsd += closeCost;
|
|
21920
|
-
} catch {}
|
|
21921
|
-
}
|
|
21922
|
-
}
|
|
22036
|
+
const { rebuttals, costUsd: rebuttalCost } = await runRebuttalLoop(ctx, successfulProposals, prompt, "debate-hybrid");
|
|
22037
|
+
totalCostUsd += rebuttalCost;
|
|
21923
22038
|
const critiqueOutputs = rebuttals.map((r) => r.output);
|
|
21924
|
-
const
|
|
22039
|
+
const fullResolverContext = ctx.resolverContextInput ? {
|
|
22040
|
+
...ctx.resolverContextInput,
|
|
22041
|
+
labeledProposals: successfulProposals.map((s) => ({ debater: s.debater.agent, output: s.output }))
|
|
22042
|
+
} : undefined;
|
|
22043
|
+
const resolveResult = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.storyId, ctx.timeoutSeconds * 1000, ctx.workdir, ctx.featureName, ctx.reviewerSession, fullResolverContext);
|
|
21925
22044
|
totalCostUsd += resolveResult.resolverCostUsd;
|
|
21926
22045
|
return {
|
|
21927
22046
|
storyId: ctx.storyId,
|
|
@@ -22064,7 +22183,11 @@ async function runOneShot(ctx, prompt) {
|
|
|
22064
22183
|
critiqueOutputs = critiqueSettled.filter((r) => r.status === "fulfilled").map((r) => r.value.output);
|
|
22065
22184
|
}
|
|
22066
22185
|
const proposalOutputs = successful.map((p) => p.output);
|
|
22067
|
-
const
|
|
22186
|
+
const fullResolverContext = ctx.resolverContextInput ? {
|
|
22187
|
+
...ctx.resolverContextInput,
|
|
22188
|
+
labeledProposals: successful.map((p) => ({ debater: p.debater.agent, output: p.output }))
|
|
22189
|
+
} : undefined;
|
|
22190
|
+
const outcome = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.storyId, ctx.timeoutMs, ctx.workdir, ctx.featureName, ctx.reviewerSession, fullResolverContext);
|
|
22068
22191
|
totalCostUsd += outcome.resolverCostUsd;
|
|
22069
22192
|
const proposals = successful.map((p) => ({
|
|
22070
22193
|
debater: p.debater,
|
|
@@ -22096,7 +22219,7 @@ async function runPlan2(ctx, basePrompt, opts) {
|
|
|
22096
22219
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
22097
22220
|
const config2 = ctx.stageConfig;
|
|
22098
22221
|
const debaters = config2.debaters ?? [];
|
|
22099
|
-
|
|
22222
|
+
let totalCostUsd = 0;
|
|
22100
22223
|
const resolved = [];
|
|
22101
22224
|
for (const debater of debaters) {
|
|
22102
22225
|
const adapter = _debateSessionDeps.getAgent(debater.agent, ctx.config);
|
|
@@ -22119,13 +22242,16 @@ async function runPlan2(ctx, basePrompt, opts) {
|
|
|
22119
22242
|
|
|
22120
22243
|
Write the PRD JSON directly to this file path: ${tempOutputPath}
|
|
22121
22244
|
Do NOT output the JSON to the conversation. Write the file, then reply with a brief confirmation.`;
|
|
22122
|
-
|
|
22245
|
+
const modelTier = modelTierFromDebater(debater);
|
|
22246
|
+
const modelDef = resolveModelDefForDebater(debater, modelTier, ctx.config);
|
|
22247
|
+
const planResult = await adapter.plan({
|
|
22123
22248
|
prompt: debaterPrompt,
|
|
22124
22249
|
workdir: opts.workdir,
|
|
22125
22250
|
interactive: false,
|
|
22126
22251
|
timeoutSeconds: opts.timeoutSeconds,
|
|
22127
22252
|
config: ctx.config,
|
|
22128
|
-
modelTier
|
|
22253
|
+
modelTier,
|
|
22254
|
+
modelDef,
|
|
22129
22255
|
dangerouslySkipPermissions: opts.dangerouslySkipPermissions,
|
|
22130
22256
|
maxInteractionTurns: opts.maxInteractionTurns,
|
|
22131
22257
|
featureName: opts.feature,
|
|
@@ -22133,13 +22259,14 @@ Do NOT output the JSON to the conversation. Write the file, then reply with a br
|
|
|
22133
22259
|
sessionRole: `plan-${i}`
|
|
22134
22260
|
});
|
|
22135
22261
|
const output = await _debateSessionDeps.readFile(tempOutputPath);
|
|
22136
|
-
return { debater, adapter, output, cost: 0 };
|
|
22262
|
+
return { debater, adapter, output, cost: planResult.costUsd ?? 0 };
|
|
22137
22263
|
}), concurrencyLimit);
|
|
22138
22264
|
const successful = [];
|
|
22139
22265
|
for (let i = 0;i < settled.length; i++) {
|
|
22140
22266
|
const res = settled[i];
|
|
22141
22267
|
if (res.status === "fulfilled") {
|
|
22142
22268
|
successful.push(res.value);
|
|
22269
|
+
totalCostUsd += res.value.cost;
|
|
22143
22270
|
} else {
|
|
22144
22271
|
const { debater } = resolved[i];
|
|
22145
22272
|
logger?.warn("debate", "debate:debater-failed", {
|
|
@@ -22191,9 +22318,31 @@ Do NOT output the JSON to the conversation. Write the file, then reply with a br
|
|
|
22191
22318
|
};
|
|
22192
22319
|
}
|
|
22193
22320
|
const proposalOutputs = successful.map((p) => p.output);
|
|
22321
|
+
const mode = ctx.stageConfig.mode ?? "panel";
|
|
22322
|
+
const sessionMode = ctx.stageConfig.sessionMode ?? "one-shot";
|
|
22323
|
+
let critiqueOutputs = [];
|
|
22324
|
+
let rebuttalList;
|
|
22325
|
+
if (mode === "hybrid" && sessionMode === "stateful") {
|
|
22326
|
+
const hybridCtx = {
|
|
22327
|
+
storyId: ctx.storyId,
|
|
22328
|
+
stage: ctx.stage,
|
|
22329
|
+
stageConfig: ctx.stageConfig,
|
|
22330
|
+
config: ctx.config,
|
|
22331
|
+
workdir: opts.workdir,
|
|
22332
|
+
featureName: opts.feature,
|
|
22333
|
+
timeoutSeconds: opts.timeoutSeconds ?? 600
|
|
22334
|
+
};
|
|
22335
|
+
const { rebuttals, costUsd } = await runRebuttalLoop(hybridCtx, successful, basePrompt, "plan-hybrid");
|
|
22336
|
+
critiqueOutputs = rebuttals.map((r) => r.output);
|
|
22337
|
+
rebuttalList = rebuttals;
|
|
22338
|
+
totalCostUsd += costUsd;
|
|
22339
|
+
} else if (mode === "hybrid") {
|
|
22340
|
+
logger?.warn("debate", "hybrid mode requires sessionMode: stateful for plan \u2014 running as panel");
|
|
22341
|
+
}
|
|
22194
22342
|
const resolverTimeoutMs = (ctx.stageConfig.timeoutSeconds ?? 600) * 1000;
|
|
22195
|
-
const
|
|
22196
|
-
const
|
|
22343
|
+
const planSynthesisSuffix = "IMPORTANT: Your response must be a single valid JSON object in PRD format (with project, feature, branchName, userStories array, etc.). Do NOT wrap it in markdown fences. Output raw JSON only.";
|
|
22344
|
+
const outcome = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.storyId, resolverTimeoutMs, opts.workdir, opts.feature, undefined, undefined, planSynthesisSuffix);
|
|
22345
|
+
const winningOutput = outcome.output ?? successful[0].output;
|
|
22197
22346
|
const proposals = successful.map((p) => ({ debater: p.debater, output: p.output }));
|
|
22198
22347
|
logger?.info("debate", "debate:result", {
|
|
22199
22348
|
storyId: ctx.storyId,
|
|
@@ -22204,16 +22353,18 @@ Do NOT output the JSON to the conversation. Write the file, then reply with a br
|
|
|
22204
22353
|
storyId: ctx.storyId,
|
|
22205
22354
|
stage: ctx.stage,
|
|
22206
22355
|
outcome: outcome.outcome,
|
|
22207
|
-
rounds: 1,
|
|
22356
|
+
rounds: rebuttalList ? config2.rounds : 1,
|
|
22208
22357
|
debaters: successful.map((p) => p.debater.agent),
|
|
22209
22358
|
resolverType: config2.resolver.type,
|
|
22210
22359
|
proposals,
|
|
22360
|
+
rebuttals: rebuttalList,
|
|
22211
22361
|
output: winningOutput,
|
|
22212
22362
|
totalCostUsd
|
|
22213
22363
|
};
|
|
22214
22364
|
}
|
|
22215
22365
|
var init_session_plan = __esm(() => {
|
|
22216
22366
|
init_session_helpers();
|
|
22367
|
+
init_session_hybrid();
|
|
22217
22368
|
});
|
|
22218
22369
|
|
|
22219
22370
|
// src/debate/session.ts
|
|
@@ -22225,6 +22376,8 @@ class DebateSession {
|
|
|
22225
22376
|
workdir;
|
|
22226
22377
|
featureName;
|
|
22227
22378
|
timeoutSeconds;
|
|
22379
|
+
reviewerSession;
|
|
22380
|
+
resolverContextInput;
|
|
22228
22381
|
get timeoutMs() {
|
|
22229
22382
|
return this.timeoutSeconds * 1000;
|
|
22230
22383
|
}
|
|
@@ -22236,6 +22389,8 @@ class DebateSession {
|
|
|
22236
22389
|
this.workdir = opts.workdir ?? process.cwd();
|
|
22237
22390
|
this.featureName = opts.featureName ?? opts.stage;
|
|
22238
22391
|
this.timeoutSeconds = opts.timeoutSeconds ?? opts.stageConfig.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS;
|
|
22392
|
+
this.reviewerSession = opts.reviewerSession;
|
|
22393
|
+
this.resolverContextInput = opts.resolverContextInput;
|
|
22239
22394
|
}
|
|
22240
22395
|
async run(prompt) {
|
|
22241
22396
|
const sessionMode = this.stageConfig.sessionMode ?? "one-shot";
|
|
@@ -22249,7 +22404,9 @@ class DebateSession {
|
|
|
22249
22404
|
config: this.config,
|
|
22250
22405
|
workdir: this.workdir,
|
|
22251
22406
|
featureName: this.featureName,
|
|
22252
|
-
timeoutSeconds: this.timeoutSeconds
|
|
22407
|
+
timeoutSeconds: this.timeoutSeconds,
|
|
22408
|
+
reviewerSession: this.reviewerSession,
|
|
22409
|
+
resolverContextInput: this.resolverContextInput
|
|
22253
22410
|
}, prompt);
|
|
22254
22411
|
}
|
|
22255
22412
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
@@ -22259,7 +22416,11 @@ class DebateSession {
|
|
|
22259
22416
|
stage: this.stage,
|
|
22260
22417
|
stageConfig: this.stageConfig,
|
|
22261
22418
|
config: this.config,
|
|
22262
|
-
timeoutMs: this.timeoutMs
|
|
22419
|
+
timeoutMs: this.timeoutMs,
|
|
22420
|
+
workdir: this.workdir,
|
|
22421
|
+
featureName: this.featureName,
|
|
22422
|
+
reviewerSession: this.reviewerSession,
|
|
22423
|
+
resolverContextInput: this.resolverContextInput
|
|
22263
22424
|
}, prompt);
|
|
22264
22425
|
}
|
|
22265
22426
|
if (sessionMode === "stateful") {
|
|
@@ -22270,7 +22431,9 @@ class DebateSession {
|
|
|
22270
22431
|
config: this.config,
|
|
22271
22432
|
workdir: this.workdir,
|
|
22272
22433
|
featureName: this.featureName,
|
|
22273
|
-
timeoutSeconds: this.timeoutSeconds
|
|
22434
|
+
timeoutSeconds: this.timeoutSeconds,
|
|
22435
|
+
reviewerSession: this.reviewerSession,
|
|
22436
|
+
resolverContextInput: this.resolverContextInput
|
|
22274
22437
|
}, prompt);
|
|
22275
22438
|
}
|
|
22276
22439
|
return runOneShot({
|
|
@@ -22278,7 +22441,11 @@ class DebateSession {
|
|
|
22278
22441
|
stage: this.stage,
|
|
22279
22442
|
stageConfig: this.stageConfig,
|
|
22280
22443
|
config: this.config,
|
|
22281
|
-
timeoutMs: this.timeoutMs
|
|
22444
|
+
timeoutMs: this.timeoutMs,
|
|
22445
|
+
workdir: this.workdir,
|
|
22446
|
+
featureName: this.featureName,
|
|
22447
|
+
reviewerSession: this.reviewerSession,
|
|
22448
|
+
resolverContextInput: this.resolverContextInput
|
|
22282
22449
|
}, prompt);
|
|
22283
22450
|
}
|
|
22284
22451
|
async runPlan(basePrompt, opts) {
|
|
@@ -22613,30 +22780,41 @@ class CLIInteractionPlugin {
|
|
|
22613
22780
|
}
|
|
22614
22781
|
async send(request) {
|
|
22615
22782
|
this.pendingRequests.set(request.id, request);
|
|
22616
|
-
|
|
22617
|
-
${"=".repeat(80)}
|
|
22618
|
-
|
|
22619
|
-
|
|
22620
|
-
|
|
22783
|
+
process.stdout.write(`
|
|
22784
|
+
${"=".repeat(80)}
|
|
22785
|
+
`);
|
|
22786
|
+
process.stdout.write(`[INTERACTION] ${request.stage.toUpperCase()} \u2014 ${request.type.toUpperCase()}
|
|
22787
|
+
`);
|
|
22788
|
+
process.stdout.write(`${"=".repeat(80)}
|
|
22789
|
+
`);
|
|
22790
|
+
process.stdout.write(`
|
|
22621
22791
|
${request.summary}
|
|
22792
|
+
|
|
22622
22793
|
`);
|
|
22623
22794
|
if (request.detail) {
|
|
22624
|
-
|
|
22625
|
-
|
|
22795
|
+
process.stdout.write(`${request.detail}
|
|
22796
|
+
`);
|
|
22797
|
+
process.stdout.write(`
|
|
22798
|
+
`);
|
|
22626
22799
|
}
|
|
22627
22800
|
if (request.options && request.options.length > 0) {
|
|
22628
|
-
|
|
22801
|
+
process.stdout.write(`Options:
|
|
22802
|
+
`);
|
|
22629
22803
|
for (const opt of request.options) {
|
|
22630
22804
|
const desc = opt.description ? ` \u2014 ${opt.description}` : "";
|
|
22631
|
-
|
|
22805
|
+
process.stdout.write(` [${opt.key}] ${opt.label}${desc}
|
|
22806
|
+
`);
|
|
22632
22807
|
}
|
|
22633
|
-
|
|
22808
|
+
process.stdout.write(`
|
|
22809
|
+
`);
|
|
22634
22810
|
}
|
|
22635
22811
|
if (request.timeout) {
|
|
22636
22812
|
const timeoutSec = Math.floor(request.timeout / 1000);
|
|
22637
|
-
|
|
22813
|
+
process.stdout.write(`[Timeout: ${timeoutSec}s | Fallback: ${request.fallback}]
|
|
22814
|
+
`);
|
|
22638
22815
|
}
|
|
22639
|
-
|
|
22816
|
+
process.stdout.write(`${"=".repeat(80)}
|
|
22817
|
+
|
|
22640
22818
|
`);
|
|
22641
22819
|
}
|
|
22642
22820
|
async receive(requestId, timeout = 60000) {
|
|
@@ -26414,59 +26592,6 @@ ${stderr}` };
|
|
|
26414
26592
|
};
|
|
26415
26593
|
});
|
|
26416
26594
|
|
|
26417
|
-
// src/agents/claude/index.ts
|
|
26418
|
-
var init_claude = __esm(() => {
|
|
26419
|
-
init_adapter3();
|
|
26420
|
-
init_execution();
|
|
26421
|
-
});
|
|
26422
|
-
|
|
26423
|
-
// src/agents/shared/validation.ts
|
|
26424
|
-
function validateAgentForTier(agent, tier) {
|
|
26425
|
-
return agent.capabilities.supportedTiers.includes(tier);
|
|
26426
|
-
}
|
|
26427
|
-
function validateAgentFeature(agent, feature) {
|
|
26428
|
-
return agent.capabilities.features.has(feature);
|
|
26429
|
-
}
|
|
26430
|
-
function describeAgentCapabilities(agent) {
|
|
26431
|
-
const tiers = agent.capabilities.supportedTiers.join(",");
|
|
26432
|
-
const features = Array.from(agent.capabilities.features).join(",");
|
|
26433
|
-
const maxTokens = agent.capabilities.maxContextTokens;
|
|
26434
|
-
return `${agent.name}: tiers=[${tiers}], maxTokens=${maxTokens}, features=[${features}]`;
|
|
26435
|
-
}
|
|
26436
|
-
|
|
26437
|
-
// src/agents/index.ts
|
|
26438
|
-
var exports_agents = {};
|
|
26439
|
-
__export(exports_agents, {
|
|
26440
|
-
validateAgentForTier: () => validateAgentForTier,
|
|
26441
|
-
validateAgentFeature: () => validateAgentFeature,
|
|
26442
|
-
parseTokenUsage: () => parseTokenUsage,
|
|
26443
|
-
getInstalledAgents: () => getInstalledAgents,
|
|
26444
|
-
getAllAgentNames: () => getAllAgentNames,
|
|
26445
|
-
getAgentVersions: () => getAgentVersions,
|
|
26446
|
-
getAgentVersion: () => getAgentVersion,
|
|
26447
|
-
getAgent: () => getAgent,
|
|
26448
|
-
formatCostWithConfidence: () => formatCostWithConfidence,
|
|
26449
|
-
estimateCostFromTokenUsage: () => estimateCostFromTokenUsage,
|
|
26450
|
-
estimateCostFromOutput: () => estimateCostFromOutput,
|
|
26451
|
-
estimateCostByDuration: () => estimateCostByDuration,
|
|
26452
|
-
estimateCost: () => estimateCost,
|
|
26453
|
-
describeAgentCapabilities: () => describeAgentCapabilities,
|
|
26454
|
-
checkAgentHealth: () => checkAgentHealth,
|
|
26455
|
-
MODEL_PRICING: () => MODEL_PRICING,
|
|
26456
|
-
CompleteError: () => CompleteError,
|
|
26457
|
-
ClaudeCodeAdapter: () => ClaudeCodeAdapter,
|
|
26458
|
-
COST_RATES: () => COST_RATES,
|
|
26459
|
-
AllAgentsUnavailableError: () => AllAgentsUnavailableError
|
|
26460
|
-
});
|
|
26461
|
-
var init_agents = __esm(() => {
|
|
26462
|
-
init_types2();
|
|
26463
|
-
init_claude();
|
|
26464
|
-
init_registry();
|
|
26465
|
-
init_cost();
|
|
26466
|
-
init_version_detection();
|
|
26467
|
-
init_errors();
|
|
26468
|
-
});
|
|
26469
|
-
|
|
26470
26595
|
// src/quality/runner.ts
|
|
26471
26596
|
var {spawn: spawn2 } = globalThis.Bun;
|
|
26472
26597
|
async function runQualityCommand(opts) {
|
|
@@ -26769,7 +26894,60 @@ Do NOT add new features \u2014 only fix the identified issues.
|
|
|
26769
26894
|
Commit your fixes when done.${scopeConstraint}`;
|
|
26770
26895
|
}
|
|
26771
26896
|
|
|
26772
|
-
// src/
|
|
26897
|
+
// src/agents/claude/index.ts
|
|
26898
|
+
var init_claude = __esm(() => {
|
|
26899
|
+
init_adapter3();
|
|
26900
|
+
init_execution();
|
|
26901
|
+
});
|
|
26902
|
+
|
|
26903
|
+
// src/agents/shared/validation.ts
|
|
26904
|
+
function validateAgentForTier(agent, tier) {
|
|
26905
|
+
return agent.capabilities.supportedTiers.includes(tier);
|
|
26906
|
+
}
|
|
26907
|
+
function validateAgentFeature(agent, feature) {
|
|
26908
|
+
return agent.capabilities.features.has(feature);
|
|
26909
|
+
}
|
|
26910
|
+
function describeAgentCapabilities(agent) {
|
|
26911
|
+
const tiers = agent.capabilities.supportedTiers.join(",");
|
|
26912
|
+
const features = Array.from(agent.capabilities.features).join(",");
|
|
26913
|
+
const maxTokens = agent.capabilities.maxContextTokens;
|
|
26914
|
+
return `${agent.name}: tiers=[${tiers}], maxTokens=${maxTokens}, features=[${features}]`;
|
|
26915
|
+
}
|
|
26916
|
+
|
|
26917
|
+
// src/agents/index.ts
|
|
26918
|
+
var exports_agents = {};
|
|
26919
|
+
__export(exports_agents, {
|
|
26920
|
+
validateAgentForTier: () => validateAgentForTier,
|
|
26921
|
+
validateAgentFeature: () => validateAgentFeature,
|
|
26922
|
+
parseTokenUsage: () => parseTokenUsage,
|
|
26923
|
+
getInstalledAgents: () => getInstalledAgents,
|
|
26924
|
+
getAllAgentNames: () => getAllAgentNames,
|
|
26925
|
+
getAgentVersions: () => getAgentVersions,
|
|
26926
|
+
getAgentVersion: () => getAgentVersion,
|
|
26927
|
+
getAgent: () => getAgent,
|
|
26928
|
+
formatCostWithConfidence: () => formatCostWithConfidence,
|
|
26929
|
+
estimateCostFromTokenUsage: () => estimateCostFromTokenUsage,
|
|
26930
|
+
estimateCostFromOutput: () => estimateCostFromOutput,
|
|
26931
|
+
estimateCostByDuration: () => estimateCostByDuration,
|
|
26932
|
+
estimateCost: () => estimateCost,
|
|
26933
|
+
describeAgentCapabilities: () => describeAgentCapabilities,
|
|
26934
|
+
checkAgentHealth: () => checkAgentHealth,
|
|
26935
|
+
MODEL_PRICING: () => MODEL_PRICING,
|
|
26936
|
+
CompleteError: () => CompleteError,
|
|
26937
|
+
ClaudeCodeAdapter: () => ClaudeCodeAdapter,
|
|
26938
|
+
COST_RATES: () => COST_RATES,
|
|
26939
|
+
AllAgentsUnavailableError: () => AllAgentsUnavailableError
|
|
26940
|
+
});
|
|
26941
|
+
var init_agents = __esm(() => {
|
|
26942
|
+
init_types2();
|
|
26943
|
+
init_claude();
|
|
26944
|
+
init_registry();
|
|
26945
|
+
init_cost();
|
|
26946
|
+
init_version_detection();
|
|
26947
|
+
init_errors();
|
|
26948
|
+
});
|
|
26949
|
+
|
|
26950
|
+
// src/review/dialogue-prompts.ts
|
|
26773
26951
|
function buildReviewPrompt(diff, story, _semanticConfig) {
|
|
26774
26952
|
const criteria = story.acceptanceCriteria.map((c) => `- ${c}`).join(`
|
|
26775
26953
|
`);
|
|
@@ -26803,6 +26981,98 @@ function buildReReviewPrompt(updatedDiff, previousFindings) {
|
|
|
26803
26981
|
].join(`
|
|
26804
26982
|
`);
|
|
26805
26983
|
}
|
|
26984
|
+
function buildProposalsSection(proposals) {
|
|
26985
|
+
return proposals.map((p) => `### ${p.debater}
|
|
26986
|
+
${p.output}`).join(`
|
|
26987
|
+
|
|
26988
|
+
`);
|
|
26989
|
+
}
|
|
26990
|
+
function buildCritiquesSection(critiques) {
|
|
26991
|
+
if (critiques.length === 0)
|
|
26992
|
+
return "";
|
|
26993
|
+
return `
|
|
26994
|
+
|
|
26995
|
+
## Critiques
|
|
26996
|
+
${critiques.map((c, i) => `### Critique ${i + 1}
|
|
26997
|
+
${c}`).join(`
|
|
26998
|
+
|
|
26999
|
+
`)}`;
|
|
27000
|
+
}
|
|
27001
|
+
function buildVoteTallyLine(ctx) {
|
|
27002
|
+
if (!ctx.majorityVote)
|
|
27003
|
+
return "";
|
|
27004
|
+
const { passCount, failCount } = ctx.majorityVote;
|
|
27005
|
+
const failOpenNote = ctx.resolverType === "majority-fail-open" ? " (unparseable proposals count as pass)" : " (unparseable proposals count as fail)";
|
|
27006
|
+
return `
|
|
27007
|
+
|
|
27008
|
+
The preliminary majority vote is: **${passCount} passed, ${failCount} failed**${failOpenNote}. Verify the failing findings with tools before giving your authoritative verdict.`;
|
|
27009
|
+
}
|
|
27010
|
+
function buildResolverFraming(ctx) {
|
|
27011
|
+
switch (ctx.resolverType) {
|
|
27012
|
+
case "majority-fail-closed":
|
|
27013
|
+
case "majority-fail-open":
|
|
27014
|
+
return "You are the authoritative reviewer resolving a debate. A preliminary vote was taken \u2014 see tally below. Verify disputed findings using tools (READ files, GREP for usage) and give your final verdict.";
|
|
27015
|
+
case "synthesis":
|
|
27016
|
+
return "You are a synthesis reviewer. Synthesize the debater proposals into a single, coherent, tool-verified verdict. Use READ and GREP to verify claims before ruling.";
|
|
27017
|
+
case "custom":
|
|
27018
|
+
return "You are the judge. Evaluate the debater proposals independently. Verify claims with tools (READ, GREP) and give your final authoritative verdict.";
|
|
27019
|
+
default:
|
|
27020
|
+
return "You are the reviewer. Evaluate the debater proposals and give your final authoritative verdict.";
|
|
27021
|
+
}
|
|
27022
|
+
}
|
|
27023
|
+
function buildDebateResolverPrompt(proposals, critiques, diff, story, _semanticConfig, resolverContext) {
|
|
27024
|
+
const criteria = story.acceptanceCriteria.map((c) => `- ${c}`).join(`
|
|
27025
|
+
`);
|
|
27026
|
+
const framing = buildResolverFraming(resolverContext);
|
|
27027
|
+
const voteTally = buildVoteTallyLine(resolverContext);
|
|
27028
|
+
const proposalsSection = buildProposalsSection(proposals);
|
|
27029
|
+
const critiquesSection = buildCritiquesSection(critiques);
|
|
27030
|
+
return [
|
|
27031
|
+
framing,
|
|
27032
|
+
"",
|
|
27033
|
+
`## Story ${story.id}: ${story.title}`,
|
|
27034
|
+
"",
|
|
27035
|
+
"## Acceptance Criteria",
|
|
27036
|
+
criteria,
|
|
27037
|
+
"",
|
|
27038
|
+
"## Debater Proposals",
|
|
27039
|
+
proposalsSection,
|
|
27040
|
+
critiquesSection,
|
|
27041
|
+
"",
|
|
27042
|
+
"## Diff",
|
|
27043
|
+
diff,
|
|
27044
|
+
voteTally,
|
|
27045
|
+
"",
|
|
27046
|
+
"Respond with JSON: { passed: boolean, findings: [...], findingReasoning: { [id]: string } }"
|
|
27047
|
+
].filter((line) => line !== undefined).join(`
|
|
27048
|
+
`);
|
|
27049
|
+
}
|
|
27050
|
+
function buildDebateReReviewPrompt(proposals, critiques, updatedDiff, previousFindings, resolverContext) {
|
|
27051
|
+
const framing = buildResolverFraming(resolverContext);
|
|
27052
|
+
const findingsList = previousFindings.length > 0 ? previousFindings.map((f) => `- ${f.ruleId}: ${f.message}`).join(`
|
|
27053
|
+
`) : "(none)";
|
|
27054
|
+
const proposalsSection = buildProposalsSection(proposals);
|
|
27055
|
+
const critiquesSection = buildCritiquesSection(critiques);
|
|
27056
|
+
return [
|
|
27057
|
+
`${framing} This is a re-review after implementer changes.`,
|
|
27058
|
+
"",
|
|
27059
|
+
"## Previous Findings",
|
|
27060
|
+
findingsList,
|
|
27061
|
+
"",
|
|
27062
|
+
"## Updated Debater Proposals",
|
|
27063
|
+
proposalsSection,
|
|
27064
|
+
critiquesSection,
|
|
27065
|
+
"",
|
|
27066
|
+
"## Updated Diff",
|
|
27067
|
+
updatedDiff,
|
|
27068
|
+
"",
|
|
27069
|
+
"Respond with JSON: { passed: boolean, findings: [...], findingReasoning: { [id]: string }, deltaSummary: string }",
|
|
27070
|
+
"deltaSummary should describe which previous findings are resolved vs still present."
|
|
27071
|
+
].filter((line) => line !== undefined).join(`
|
|
27072
|
+
`);
|
|
27073
|
+
}
|
|
27074
|
+
|
|
27075
|
+
// src/review/dialogue.ts
|
|
26806
27076
|
function extractDeltaSummary(rawOutput, previousFindings, newFindings) {
|
|
26807
27077
|
try {
|
|
26808
27078
|
const parsed = JSON.parse(rawOutput);
|
|
@@ -26874,6 +27144,7 @@ function createReviewerSession(agent, storyId, workdir, featureName, _config) {
|
|
|
26874
27144
|
let lastCheckResult = null;
|
|
26875
27145
|
let lastStory = null;
|
|
26876
27146
|
let lastSemanticConfig = null;
|
|
27147
|
+
let lastWasDebateResolve = false;
|
|
26877
27148
|
const sessionState = {
|
|
26878
27149
|
generation: 1,
|
|
26879
27150
|
pendingCompactionContext: null
|
|
@@ -26936,6 +27207,7 @@ ${prompt}`,
|
|
|
26936
27207
|
lastCheckResult = reviewResult;
|
|
26937
27208
|
lastStory = story;
|
|
26938
27209
|
lastSemanticConfig = semanticConfig;
|
|
27210
|
+
lastWasDebateResolve = false;
|
|
26939
27211
|
return reviewResult;
|
|
26940
27212
|
},
|
|
26941
27213
|
async reReview(updatedDiff) {
|
|
@@ -27005,6 +27277,76 @@ ${prompt}`,
|
|
|
27005
27277
|
history.push({ role: "reviewer", content: result.output });
|
|
27006
27278
|
return result.output;
|
|
27007
27279
|
},
|
|
27280
|
+
async resolveDebate(proposals, critiques, diff, story, semanticConfig, resolverContext) {
|
|
27281
|
+
if (!active) {
|
|
27282
|
+
throw new NaxError(`[dialogue] ReviewerSession for story ${storyId} has been destroyed`, "REVIEWER_SESSION_DESTROYED", { stage: "review", storyId, featureName });
|
|
27283
|
+
}
|
|
27284
|
+
const prompt = buildDebateResolverPrompt(proposals, critiques, diff, story, semanticConfig, resolverContext);
|
|
27285
|
+
const { modelTier, modelDef, timeoutSeconds } = resolveRunParams(semanticConfig);
|
|
27286
|
+
const { effectivePrompt, acpSessionName } = buildEffectiveRunArgs(prompt);
|
|
27287
|
+
const result = await agent.run({
|
|
27288
|
+
prompt: effectivePrompt,
|
|
27289
|
+
workdir,
|
|
27290
|
+
modelTier,
|
|
27291
|
+
modelDef,
|
|
27292
|
+
timeoutSeconds,
|
|
27293
|
+
sessionRole: "reviewer",
|
|
27294
|
+
keepSessionOpen: true,
|
|
27295
|
+
pipelineStage: "review",
|
|
27296
|
+
config: _config,
|
|
27297
|
+
storyId,
|
|
27298
|
+
featureName,
|
|
27299
|
+
acpSessionName
|
|
27300
|
+
});
|
|
27301
|
+
history.push({ role: "implementer", content: prompt });
|
|
27302
|
+
history.push({ role: "reviewer", content: result.output });
|
|
27303
|
+
const parsed = parseReviewResponse(result.output);
|
|
27304
|
+
const reviewResult = { ...parsed, cost: result.estimatedCost ?? 0 };
|
|
27305
|
+
lastCheckResult = reviewResult;
|
|
27306
|
+
lastStory = story;
|
|
27307
|
+
lastSemanticConfig = semanticConfig;
|
|
27308
|
+
lastWasDebateResolve = true;
|
|
27309
|
+
return reviewResult;
|
|
27310
|
+
},
|
|
27311
|
+
async reReviewDebate(proposals, critiques, updatedDiff, resolverContext) {
|
|
27312
|
+
if (!active) {
|
|
27313
|
+
throw new NaxError(`[dialogue] ReviewerSession for story ${storyId} has been destroyed`, "REVIEWER_SESSION_DESTROYED", { stage: "review", storyId, featureName });
|
|
27314
|
+
}
|
|
27315
|
+
if (!lastCheckResult || !lastSemanticConfig || !lastWasDebateResolve) {
|
|
27316
|
+
throw new NaxError(`[dialogue] reReviewDebate() called before any resolveDebate() on story ${storyId}`, "NO_REVIEW_RESULT", { stage: "review", storyId });
|
|
27317
|
+
}
|
|
27318
|
+
const previousFindings = lastCheckResult.checkResult.findings;
|
|
27319
|
+
const prompt = buildDebateReReviewPrompt(proposals, critiques, updatedDiff, previousFindings, resolverContext);
|
|
27320
|
+
const { modelTier, modelDef, timeoutSeconds } = resolveRunParams(lastSemanticConfig);
|
|
27321
|
+
const { effectivePrompt, acpSessionName } = buildEffectiveRunArgs(prompt);
|
|
27322
|
+
const result = await agent.run({
|
|
27323
|
+
prompt: effectivePrompt,
|
|
27324
|
+
workdir,
|
|
27325
|
+
modelTier,
|
|
27326
|
+
modelDef,
|
|
27327
|
+
timeoutSeconds,
|
|
27328
|
+
sessionRole: "reviewer",
|
|
27329
|
+
keepSessionOpen: true,
|
|
27330
|
+
pipelineStage: "review",
|
|
27331
|
+
config: _config,
|
|
27332
|
+
storyId,
|
|
27333
|
+
featureName,
|
|
27334
|
+
acpSessionName
|
|
27335
|
+
});
|
|
27336
|
+
history.push({ role: "implementer", content: prompt });
|
|
27337
|
+
history.push({ role: "reviewer", content: result.output });
|
|
27338
|
+
const parsed = parseReviewResponse(result.output);
|
|
27339
|
+
const deltaSummary = extractDeltaSummary(result.output, previousFindings, parsed.checkResult.findings);
|
|
27340
|
+
const dialogueResult = { ...parsed, deltaSummary, cost: result.estimatedCost ?? 0 };
|
|
27341
|
+
lastCheckResult = dialogueResult;
|
|
27342
|
+
const maxMessages = _config.review?.dialogue?.maxDialogueMessages ?? 20;
|
|
27343
|
+
if (history.length > maxMessages) {
|
|
27344
|
+
const compactedSummary = compactHistory(history);
|
|
27345
|
+
sessionState.generation++;
|
|
27346
|
+
sessionState.pendingCompactionContext = compactedSummary;
|
|
27347
|
+
}
|
|
27348
|
+
return dialogueResult;
|
|
27349
|
+
},
|
|
27008
27350
|
getVerdict() {
|
|
27009
27351
|
if (!lastCheckResult || !lastStory) {
|
|
27010
27352
|
throw new NaxError(`[dialogue] getVerdict() called before any review() on story ${storyId}`, "NO_REVIEW_RESULT", { stage: "review", storyId });
|
|
@@ -27240,10 +27582,8 @@ var init_language_commands = __esm(() => {
|
|
|
27240
27582
|
// src/review/semantic.ts
|
|
27241
27583
|
var {spawn: spawn3 } = globalThis.Bun;
|
|
27242
27584
|
async function collectDiff(workdir, storyGitRef, excludePatterns) {
|
|
27243
|
-
const
|
|
27244
|
-
|
|
27245
|
-
cmd.push("--", ".", ...excludePatterns);
|
|
27246
|
-
}
|
|
27585
|
+
const merged = [...new Set([...excludePatterns, ...ALWAYS_EXCLUDED])];
|
|
27586
|
+
const cmd = ["git", "diff", "--unified=3", `${storyGitRef}..HEAD`, "--", ".", ...merged];
|
|
27247
27587
|
const proc = _semanticDeps.spawn({
|
|
27248
27588
|
cmd,
|
|
27249
27589
|
cwd: workdir,
|
|
@@ -27403,7 +27743,7 @@ function toReviewFindings(findings) {
|
|
|
27403
27743
|
source: "semantic-review"
|
|
27404
27744
|
}));
|
|
27405
27745
|
}
|
|
27406
|
-
async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, modelResolver, naxConfig, featureName) {
|
|
27746
|
+
async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, modelResolver, naxConfig, featureName, resolverSession) {
|
|
27407
27747
|
const startTime = Date.now();
|
|
27408
27748
|
const logger = getSafeLogger();
|
|
27409
27749
|
if (featureName === undefined) {
|
|
@@ -27473,6 +27813,7 @@ async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, mo
|
|
|
27473
27813
|
const reviewDebateEnabled = naxConfig?.debate?.enabled && naxConfig?.debate?.stages?.review?.enabled;
|
|
27474
27814
|
if (reviewDebateEnabled) {
|
|
27475
27815
|
const reviewStageConfig = naxConfig?.debate?.stages.review;
|
|
27816
|
+
const isReReview = resolverSession !== undefined && resolverSession.history.length > 0;
|
|
27476
27817
|
const debateSession = _semanticDeps.createDebateSession({
|
|
27477
27818
|
storyId: story.id,
|
|
27478
27819
|
stage: "review",
|
|
@@ -27480,26 +27821,69 @@ async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, mo
|
|
|
27480
27821
|
config: naxConfig ?? DEFAULT_CONFIG,
|
|
27481
27822
|
workdir,
|
|
27482
27823
|
featureName,
|
|
27483
|
-
timeoutSeconds: naxConfig?.execution?.sessionTimeoutSeconds
|
|
27824
|
+
timeoutSeconds: naxConfig?.execution?.sessionTimeoutSeconds,
|
|
27825
|
+
reviewerSession: resolverSession,
|
|
27826
|
+
resolverContextInput: resolverSession ? {
|
|
27827
|
+
diff,
|
|
27828
|
+
story: { id: story.id, title: story.title, acceptanceCriteria: story.acceptanceCriteria },
|
|
27829
|
+
semanticConfig,
|
|
27830
|
+
resolverType: reviewStageConfig.resolver.type,
|
|
27831
|
+
isReReview
|
|
27832
|
+
} : undefined
|
|
27484
27833
|
});
|
|
27834
|
+
const historyLenBefore = resolverSession?.history.length ?? 0;
|
|
27485
27835
|
const debateResult = await debateSession.run(prompt);
|
|
27486
27836
|
const debateCost = debateResult.totalCostUsd ?? 0;
|
|
27487
|
-
|
|
27488
|
-
|
|
27837
|
+
const sessionUsed = resolverSession && resolverSession.history.length > historyLenBefore;
|
|
27838
|
+
if (sessionUsed) {
|
|
27839
|
+
const durationMs3 = Date.now() - startTime;
|
|
27840
|
+
try {
|
|
27841
|
+
const verdict = resolverSession.getVerdict();
|
|
27842
|
+
const findings = verdict.findings ?? [];
|
|
27843
|
+
if (!verdict.passed && findings.length > 0) {
|
|
27844
|
+
logger?.warn("review", `Semantic review failed (debate+dialogue): ${findings.length} findings`, {
|
|
27845
|
+
storyId: story.id,
|
|
27846
|
+
durationMs: durationMs3
|
|
27847
|
+
});
|
|
27848
|
+
return {
|
|
27849
|
+
check: "semantic",
|
|
27850
|
+
success: false,
|
|
27851
|
+
command: "",
|
|
27852
|
+
exitCode: 1,
|
|
27853
|
+
output: `Semantic review failed:
|
|
27854
|
+
|
|
27855
|
+
${findings.map((f) => `${f.ruleId}: ${f.message}`).join(`
|
|
27856
|
+
`)}`,
|
|
27857
|
+
durationMs: durationMs3,
|
|
27858
|
+
findings,
|
|
27859
|
+
cost: debateCost
|
|
27860
|
+
};
|
|
27861
|
+
}
|
|
27862
|
+
const label = verdict.passed ? "Semantic review passed (debate+dialogue)" : "Semantic review passed (debate+dialogue, all findings non-blocking)";
|
|
27863
|
+
logger?.info("review", label, { storyId: story.id, durationMs: durationMs3 });
|
|
27864
|
+
return {
|
|
27865
|
+
check: "semantic",
|
|
27866
|
+
success: true,
|
|
27867
|
+
command: "",
|
|
27868
|
+
exitCode: 0,
|
|
27869
|
+
output: label,
|
|
27870
|
+
durationMs: durationMs3,
|
|
27871
|
+
cost: debateCost
|
|
27872
|
+
};
|
|
27873
|
+
} catch {
|
|
27874
|
+
logger?.warn("review", "getVerdict() failed after debate+dialogue \u2014 falling back to stateless verdict", {
|
|
27875
|
+
storyId: story.id
|
|
27876
|
+
});
|
|
27877
|
+
}
|
|
27878
|
+
}
|
|
27879
|
+
const resolverPassed = debateResult.outcome === "passed";
|
|
27489
27880
|
const allFindings = [];
|
|
27490
27881
|
for (const p of debateResult.proposals) {
|
|
27491
27882
|
const parsed2 = parseLLMResponse(p.output);
|
|
27492
27883
|
if (parsed2) {
|
|
27493
|
-
if (parsed2.passed)
|
|
27494
|
-
passCount++;
|
|
27495
|
-
else
|
|
27496
|
-
failCount++;
|
|
27497
27884
|
allFindings.push(...parsed2.findings);
|
|
27498
|
-
} else {
|
|
27499
|
-
failCount++;
|
|
27500
27885
|
}
|
|
27501
27886
|
}
|
|
27502
|
-
const majorityPassed = passCount > failCount;
|
|
27503
27887
|
const seen = new Set;
|
|
27504
27888
|
const deduped = [];
|
|
27505
27889
|
for (const f of allFindings) {
|
|
@@ -27511,7 +27895,7 @@ async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, mo
|
|
|
27511
27895
|
}
|
|
27512
27896
|
const debateBlocking = deduped.filter((f) => isBlockingSeverity(f.severity));
|
|
27513
27897
|
const durationMs2 = Date.now() - startTime;
|
|
27514
|
-
if (!
|
|
27898
|
+
if (!resolverPassed) {
|
|
27515
27899
|
if (debateBlocking.length > 0) {
|
|
27516
27900
|
logger?.warn("review", `Semantic review failed (debate): ${debateBlocking.length} findings`, {
|
|
27517
27901
|
storyId: story.id,
|
|
@@ -27710,7 +28094,7 @@ ${formatFindings(blockingFindings)}`;
|
|
|
27710
28094
|
cost: llmCost
|
|
27711
28095
|
};
|
|
27712
28096
|
}
|
|
27713
|
-
var _semanticDeps, DIFF_CAP_BYTES = 51200;
|
|
28097
|
+
var _semanticDeps, DIFF_CAP_BYTES = 51200, ALWAYS_EXCLUDED;
|
|
27714
28098
|
var init_semantic = __esm(() => {
|
|
27715
28099
|
init_adapter();
|
|
27716
28100
|
init_config();
|
|
@@ -27724,6 +28108,7 @@ var init_semantic = __esm(() => {
|
|
|
27724
28108
|
createDebateSession: (opts) => new DebateSession(opts),
|
|
27725
28109
|
readAcpSession
|
|
27726
28110
|
};
|
|
28111
|
+
ALWAYS_EXCLUDED = [":!.nax/", ":!.nax-pids"];
|
|
27727
28112
|
});
|
|
27728
28113
|
|
|
27729
28114
|
// src/review/runner.ts
|
|
@@ -27808,7 +28193,7 @@ async function getUncommittedFilesImpl(workdir) {
|
|
|
27808
28193
|
return [];
|
|
27809
28194
|
}
|
|
27810
28195
|
}
|
|
27811
|
-
async function runReview(config2, workdir, executionConfig, qualityCommands, storyId, storyGitRef, story, modelResolver, naxConfig, retrySkipChecks, featureName) {
|
|
28196
|
+
async function runReview(config2, workdir, executionConfig, qualityCommands, storyId, storyGitRef, story, modelResolver, naxConfig, retrySkipChecks, featureName, resolverSession) {
|
|
27812
28197
|
const startTime = Date.now();
|
|
27813
28198
|
const logger = getSafeLogger();
|
|
27814
28199
|
const checks3 = [];
|
|
@@ -27865,10 +28250,19 @@ Stage and commit these files before running review.`
|
|
|
27865
28250
|
modelTier: "balanced",
|
|
27866
28251
|
rules: [],
|
|
27867
28252
|
timeoutMs: 600000,
|
|
27868
|
-
excludePatterns: [
|
|
28253
|
+
excludePatterns: [
|
|
28254
|
+
":!test/",
|
|
28255
|
+
":!tests/",
|
|
28256
|
+
":!*_test.go",
|
|
28257
|
+
":!*.test.ts",
|
|
28258
|
+
":!*.spec.ts",
|
|
28259
|
+
":!**/__tests__/",
|
|
28260
|
+
":!.nax/",
|
|
28261
|
+
":!.nax-pids"
|
|
28262
|
+
]
|
|
27869
28263
|
};
|
|
27870
28264
|
const runSemantic = _reviewSemanticDeps.runSemanticReview;
|
|
27871
|
-
const result2 =
|
|
28265
|
+
const result2 = await runSemantic(workdir, storyGitRef, semanticStory, semanticCfg, modelResolver ?? (() => null), naxConfig, featureName, resolverSession);
|
|
27872
28266
|
checks3.push(result2);
|
|
27873
28267
|
if (!result2.success && !firstFailure) {
|
|
27874
28268
|
firstFailure = `${checkName} failed`;
|
|
@@ -27950,9 +28344,9 @@ async function getChangedFiles(workdir, baseRef) {
|
|
|
27950
28344
|
}
|
|
27951
28345
|
|
|
27952
28346
|
class ReviewOrchestrator {
|
|
27953
|
-
async review(reviewConfig, workdir, executionConfig, plugins, storyGitRef, scopePrefix, qualityCommands, storyId, story, modelResolver, naxConfig, retrySkipChecks, featureName) {
|
|
28347
|
+
async review(reviewConfig, workdir, executionConfig, plugins, storyGitRef, scopePrefix, qualityCommands, storyId, story, modelResolver, naxConfig, retrySkipChecks, featureName, resolverSession) {
|
|
27954
28348
|
const logger = getSafeLogger();
|
|
27955
|
-
const builtIn = await runReview(reviewConfig, workdir, executionConfig, qualityCommands, storyId, storyGitRef, story, modelResolver, naxConfig, retrySkipChecks, featureName);
|
|
28349
|
+
const builtIn = await runReview(reviewConfig, workdir, executionConfig, qualityCommands, storyId, storyGitRef, story, modelResolver, naxConfig, retrySkipChecks, featureName, resolverSession);
|
|
27956
28350
|
if (!builtIn.success) {
|
|
27957
28351
|
return { builtIn, success: false, failureReason: builtIn.failureReason, pluginFailed: false };
|
|
27958
28352
|
}
|
|
@@ -28019,12 +28413,14 @@ class ReviewOrchestrator {
|
|
|
28019
28413
|
const agentResolver = ctx.agentGetFn ?? undefined;
|
|
28020
28414
|
const agentName = ctx.rootConfig.autoMode?.defaultAgent;
|
|
28021
28415
|
const modelResolver = agentName ? (_tier) => agentResolver ? agentResolver(agentName) ?? null : null : undefined;
|
|
28416
|
+
const reviewDebateEnabled = ctx.rootConfig?.debate?.enabled && ctx.rootConfig?.debate?.stages?.review?.enabled;
|
|
28417
|
+
const resolverSession = reviewDebateEnabled ? ctx.reviewerSession : undefined;
|
|
28022
28418
|
return this.review(ctx.config.review, ctx.workdir, ctx.config.execution, ctx.plugins, ctx.storyGitRef, ctx.story.workdir, ctx.config.quality?.commands, ctx.story.id, {
|
|
28023
28419
|
id: ctx.story.id,
|
|
28024
28420
|
title: ctx.story.title,
|
|
28025
28421
|
description: ctx.story.description,
|
|
28026
28422
|
acceptanceCriteria: ctx.story.acceptanceCriteria
|
|
28027
|
-
}, modelResolver, ctx.config, retrySkipChecks, ctx.prd.feature);
|
|
28423
|
+
}, modelResolver, ctx.config, retrySkipChecks, ctx.prd.feature, resolverSession);
|
|
28028
28424
|
}
|
|
28029
28425
|
}
|
|
28030
28426
|
var _orchestratorDeps, reviewOrchestrator;
|
|
@@ -28053,11 +28449,12 @@ var init_review = __esm(() => {
|
|
|
28053
28449
|
enabled: (ctx) => ctx.config.review.enabled,
|
|
28054
28450
|
async execute(ctx) {
|
|
28055
28451
|
const logger = getLogger();
|
|
28452
|
+
const reviewDebateEnabled = ctx.rootConfig?.debate?.enabled && ctx.rootConfig?.debate?.stages?.review?.enabled;
|
|
28056
28453
|
const dialogueEnabled = ctx.config.review?.dialogue?.enabled ?? false;
|
|
28057
28454
|
logger.info("review", "Running review phase", { storyId: ctx.story.id });
|
|
28058
28455
|
const agentResolver = ctx.agentGetFn ?? getAgent;
|
|
28059
28456
|
const agentName = ctx.rootConfig.autoMode?.defaultAgent;
|
|
28060
|
-
if (dialogueEnabled && ctx.reviewerSession) {
|
|
28457
|
+
if (dialogueEnabled && !reviewDebateEnabled && ctx.reviewerSession) {
|
|
28061
28458
|
try {
|
|
28062
28459
|
const diff = ctx.storyGitRef ?? "";
|
|
28063
28460
|
const reReviewResult = await ctx.reviewerSession.reReview(diff);
|
|
@@ -28095,47 +28492,49 @@ var init_review = __esm(() => {
|
|
|
28095
28492
|
if (dialogueEnabled && !ctx.reviewerSession) {
|
|
28096
28493
|
const agent = agentName ? agentResolver(agentName) ?? null : null;
|
|
28097
28494
|
ctx.reviewerSession = _reviewDeps.createReviewerSession(agent ?? null, ctx.story.id, ctx.workdir, ctx.prd.feature ?? "", ctx.config);
|
|
28098
|
-
|
|
28099
|
-
|
|
28100
|
-
|
|
28101
|
-
|
|
28102
|
-
|
|
28103
|
-
|
|
28104
|
-
|
|
28105
|
-
|
|
28106
|
-
|
|
28107
|
-
|
|
28108
|
-
|
|
28109
|
-
|
|
28110
|
-
|
|
28111
|
-
|
|
28112
|
-
|
|
28113
|
-
|
|
28114
|
-
|
|
28115
|
-
|
|
28116
|
-
|
|
28117
|
-
|
|
28118
|
-
|
|
28495
|
+
if (!reviewDebateEnabled) {
|
|
28496
|
+
const semanticConfig = ctx.config.review?.semantic;
|
|
28497
|
+
if (semanticConfig && agent) {
|
|
28498
|
+
try {
|
|
28499
|
+
const diff = ctx.storyGitRef ?? "";
|
|
28500
|
+
const story = {
|
|
28501
|
+
id: ctx.story.id,
|
|
28502
|
+
title: ctx.story.title,
|
|
28503
|
+
description: ctx.story.description,
|
|
28504
|
+
acceptanceCriteria: ctx.story.acceptanceCriteria
|
|
28505
|
+
};
|
|
28506
|
+
const sessionResult = await ctx.reviewerSession.review(diff, story, semanticConfig);
|
|
28507
|
+
const passed = sessionResult.checkResult.success;
|
|
28508
|
+
ctx.reviewResult = {
|
|
28509
|
+
success: passed,
|
|
28510
|
+
checks: passed ? [] : [
|
|
28511
|
+
{
|
|
28512
|
+
check: "semantic",
|
|
28513
|
+
success: false,
|
|
28514
|
+
command: "reviewer-session-review",
|
|
28515
|
+
exitCode: 1,
|
|
28516
|
+
output: sessionResult.checkResult.findings.map((f) => f.message).join(`
|
|
28119
28517
|
`),
|
|
28120
|
-
|
|
28121
|
-
|
|
28122
|
-
|
|
28123
|
-
|
|
28124
|
-
|
|
28125
|
-
|
|
28126
|
-
|
|
28127
|
-
|
|
28128
|
-
|
|
28129
|
-
|
|
28130
|
-
|
|
28518
|
+
durationMs: 0,
|
|
28519
|
+
findings: sessionResult.checkResult.findings
|
|
28520
|
+
}
|
|
28521
|
+
],
|
|
28522
|
+
totalDurationMs: 0
|
|
28523
|
+
};
|
|
28524
|
+
const dialogueCost = sessionResult.cost ?? 0;
|
|
28525
|
+
if (passed) {
|
|
28526
|
+
logger.info("review", "Review passed (dialogue session)", { storyId: ctx.story.id });
|
|
28527
|
+
} else {
|
|
28528
|
+
logger.warn("review", "Review failed (dialogue session) \u2014 handing off to autofix", {
|
|
28529
|
+
storyId: ctx.story.id
|
|
28530
|
+
});
|
|
28531
|
+
}
|
|
28532
|
+
return { action: "continue", cost: dialogueCost || undefined };
|
|
28533
|
+
} catch (err) {
|
|
28534
|
+
logger.warn("review", "ReviewerSession.review() failed \u2014 falling back to one-shot review", {
|
|
28131
28535
|
storyId: ctx.story.id
|
|
28132
28536
|
});
|
|
28133
28537
|
}
|
|
28134
|
-
return { action: "continue", cost: dialogueCost || undefined };
|
|
28135
|
-
} catch (err) {
|
|
28136
|
-
logger.warn("review", "ReviewerSession.review() failed \u2014 falling back to one-shot review", {
|
|
28137
|
-
storyId: ctx.story.id
|
|
28138
|
-
});
|
|
28139
28538
|
}
|
|
28140
28539
|
}
|
|
28141
28540
|
}
|
|
@@ -28239,7 +28638,7 @@ async function runAgentRectification(ctx, lintFixCmd, formatFixCmd, effectiveWor
|
|
|
28239
28638
|
}
|
|
28240
28639
|
const remainingBudget = maxTotal - consumed;
|
|
28241
28640
|
const maxAttempts = Math.min(maxPerCycle, remainingBudget);
|
|
28242
|
-
const agentGetFn = ctx.agentGetFn ?? _autofixDeps.getAgent;
|
|
28641
|
+
const agentGetFn = ctx.agentGetFn ?? ((name) => _autofixDeps.getAgent(name, ctx.rootConfig));
|
|
28243
28642
|
const loopState = {
|
|
28244
28643
|
attempt: 0,
|
|
28245
28644
|
failedChecks
|
|
@@ -28382,7 +28781,7 @@ async function runAgentRectification(ctx, lintFixCmd, formatFixCmd, effectiveWor
|
|
|
28382
28781
|
}
|
|
28383
28782
|
var CLARIFY_REGEX, autofixStage, _autofixDeps;
|
|
28384
28783
|
var init_autofix = __esm(() => {
|
|
28385
|
-
|
|
28784
|
+
init_registry();
|
|
28386
28785
|
init_config();
|
|
28387
28786
|
init_loader();
|
|
28388
28787
|
init_logger2();
|
|
@@ -28458,6 +28857,14 @@ var init_autofix = __esm(() => {
|
|
|
28458
28857
|
const recheckPassed = await _autofixDeps.recheckReview(ctx);
|
|
28459
28858
|
pipelineEventBus.emit({ type: "autofix:completed", storyId: ctx.story.id, fixed: recheckPassed });
|
|
28460
28859
|
if (recheckPassed) {
|
|
28860
|
+
const passedChecks = (ctx.reviewResult?.checks ?? []).filter((c) => c.success).map((c) => c.check);
|
|
28861
|
+
if (passedChecks.length > 0) {
|
|
28862
|
+
ctx.retrySkipChecks = new Set(passedChecks);
|
|
28863
|
+
logger.debug("autofix", "Skipping already-passed checks on retry", {
|
|
28864
|
+
storyId: ctx.story.id,
|
|
28865
|
+
skippedChecks: passedChecks
|
|
28866
|
+
});
|
|
28867
|
+
}
|
|
28461
28868
|
logger.info("autofix", "Mechanical autofix succeeded \u2014 retrying review", { storyId: ctx.story.id });
|
|
28462
28869
|
return { action: "retry", fromStage: "review" };
|
|
28463
28870
|
}
|
|
@@ -28489,7 +28896,7 @@ var init_autofix = __esm(() => {
|
|
|
28489
28896
|
}
|
|
28490
28897
|
};
|
|
28491
28898
|
_autofixDeps = {
|
|
28492
|
-
getAgent,
|
|
28899
|
+
getAgent: (name, config2) => createAgentRegistry(config2).getAgent(name),
|
|
28493
28900
|
runQualityCommand,
|
|
28494
28901
|
recheckReview,
|
|
28495
28902
|
runAgentRectification: (ctx, lintFixCmd, formatFixCmd, effectiveWorkdir) => runAgentRectification(ctx, lintFixCmd, formatFixCmd, effectiveWorkdir),
|
|
@@ -28583,7 +28990,7 @@ var init_completion = __esm(() => {
|
|
|
28583
28990
|
const logger = getLogger();
|
|
28584
28991
|
const isBatch = ctx.stories.length > 1;
|
|
28585
28992
|
const sessionCost = ctx.agentResult?.estimatedCost || 0;
|
|
28586
|
-
const prdPath = ctx.featureDir ? `${ctx.featureDir}/prd.json` : `${ctx.workdir}/nax/features/unknown/prd.json
|
|
28993
|
+
const prdPath = ctx.prdPath ?? (ctx.featureDir ? `${ctx.featureDir}/prd.json` : `${ctx.workdir}/nax/features/unknown/prd.json`);
|
|
28587
28994
|
const storyStartTime = ctx.storyStartTime || new Date().toISOString();
|
|
28588
28995
|
if (isBatch) {
|
|
28589
28996
|
ctx.storyMetrics = collectBatchMetrics(ctx, storyStartTime);
|
|
@@ -28634,7 +29041,7 @@ var init_completion = __esm(() => {
|
|
|
28634
29041
|
};
|
|
28635
29042
|
await _completionDeps.persistSemanticVerdict(ctx.featureDir, ctx.story.id, verdict);
|
|
28636
29043
|
}
|
|
28637
|
-
await savePRD(ctx.prd, prdPath);
|
|
29044
|
+
await _completionDeps.savePRD(ctx.prd, prdPath);
|
|
28638
29045
|
const updatedCounts = countStories(ctx.prd);
|
|
28639
29046
|
logger.info("completion", "Progress update", {
|
|
28640
29047
|
storyId: ctx.story.id,
|
|
@@ -28653,7 +29060,8 @@ var init_completion = __esm(() => {
|
|
|
28653
29060
|
};
|
|
28654
29061
|
_completionDeps = {
|
|
28655
29062
|
checkReviewGate,
|
|
28656
|
-
persistSemanticVerdict
|
|
29063
|
+
persistSemanticVerdict,
|
|
29064
|
+
savePRD
|
|
28657
29065
|
};
|
|
28658
29066
|
});
|
|
28659
29067
|
|
|
@@ -35735,7 +36143,7 @@ var package_default;
|
|
|
35735
36143
|
var init_package = __esm(() => {
|
|
35736
36144
|
package_default = {
|
|
35737
36145
|
name: "@nathapp/nax",
|
|
35738
|
-
version: "0.59.
|
|
36146
|
+
version: "0.59.2",
|
|
35739
36147
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
35740
36148
|
type: "module",
|
|
35741
36149
|
bin: {
|
|
@@ -35815,8 +36223,8 @@ var init_version = __esm(() => {
|
|
|
35815
36223
|
NAX_VERSION = package_default.version;
|
|
35816
36224
|
NAX_COMMIT = (() => {
|
|
35817
36225
|
try {
|
|
35818
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
35819
|
-
return "
|
|
36226
|
+
if (/^[0-9a-f]{6,10}$/.test("d42d47be"))
|
|
36227
|
+
return "d42d47be";
|
|
35820
36228
|
} catch {}
|
|
35821
36229
|
try {
|
|
35822
36230
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -40496,7 +40904,7 @@ function validateStoryCount(counts, config2) {
|
|
|
40496
40904
|
}
|
|
40497
40905
|
function logActiveProtocol(config2) {
|
|
40498
40906
|
const logger = getSafeLogger();
|
|
40499
|
-
const protocol = config2.agent?.protocol
|
|
40907
|
+
const protocol = config2.agent?.protocol;
|
|
40500
40908
|
logger?.info("run-initialization", `Agent protocol: ${protocol}`, { protocol });
|
|
40501
40909
|
}
|
|
40502
40910
|
async function initializeRun(ctx) {
|