@nathapp/nax 0.59.0 → 0.59.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nax.js +598 -200
- 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;
|
|
@@ -21521,25 +21543,89 @@ function pipelineStageForDebate(stage) {
|
|
|
21521
21543
|
}
|
|
21522
21544
|
}
|
|
21523
21545
|
function resolveModelDefForDebater(debater, tier, config2) {
|
|
21524
|
-
|
|
21525
|
-
|
|
21546
|
+
const modelOverride = debater.model;
|
|
21547
|
+
let effectiveTier = tier;
|
|
21548
|
+
if (modelOverride) {
|
|
21549
|
+
const aliasedTier = MODEL_SHORTHAND_TIERS[modelOverride.toLowerCase()];
|
|
21550
|
+
if (aliasedTier) {
|
|
21551
|
+
effectiveTier = aliasedTier;
|
|
21552
|
+
} else if (!isTierLabel(modelOverride)) {
|
|
21553
|
+
return resolveModel(modelOverride);
|
|
21554
|
+
}
|
|
21526
21555
|
}
|
|
21527
21556
|
const configModels = config2?.models ?? DEFAULT_CONFIG.models;
|
|
21528
21557
|
const configDefaultAgent = config2?.autoMode?.defaultAgent ?? DEFAULT_CONFIG.autoMode.defaultAgent;
|
|
21529
21558
|
try {
|
|
21530
|
-
return resolveModelForAgent(configModels, debater.agent,
|
|
21559
|
+
return resolveModelForAgent(configModels, debater.agent, effectiveTier, configDefaultAgent);
|
|
21531
21560
|
} catch {}
|
|
21532
21561
|
try {
|
|
21533
|
-
return resolveModelForAgent(DEFAULT_CONFIG.models, DEFAULT_CONFIG.autoMode.defaultAgent,
|
|
21562
|
+
return resolveModelForAgent(DEFAULT_CONFIG.models, DEFAULT_CONFIG.autoMode.defaultAgent, effectiveTier, DEFAULT_CONFIG.autoMode.defaultAgent);
|
|
21534
21563
|
} catch {
|
|
21535
21564
|
return resolveModelForAgent(configModels, debater.agent, "fast", configDefaultAgent);
|
|
21536
21565
|
}
|
|
21537
21566
|
}
|
|
21538
|
-
async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, config2, storyId, timeoutMs, workdir, featureName) {
|
|
21567
|
+
async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, config2, storyId, timeoutMs, workdir, featureName, reviewerSession, resolverContext) {
|
|
21539
21568
|
const resolverConfig = stageConfig.resolver;
|
|
21540
21569
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
21570
|
+
if (reviewerSession && resolverContext) {
|
|
21571
|
+
try {
|
|
21572
|
+
const debateCtx = {
|
|
21573
|
+
resolverType: resolverConfig.type
|
|
21574
|
+
};
|
|
21575
|
+
if (resolverConfig.type === "majority-fail-closed" || resolverConfig.type === "majority-fail-open") {
|
|
21576
|
+
const failOpen = resolverConfig.type === "majority-fail-open";
|
|
21577
|
+
const rawOutcome = majorityResolver(proposalOutputs, failOpen);
|
|
21578
|
+
let passCount = 0;
|
|
21579
|
+
let failCount = 0;
|
|
21580
|
+
for (const proposal of proposalOutputs) {
|
|
21581
|
+
try {
|
|
21582
|
+
const stripped = proposal.trim().replace(/^```(?:json)?\s*\n?/, "").replace(/\n?```\s*$/, "");
|
|
21583
|
+
const parsed = JSON.parse(stripped);
|
|
21584
|
+
if (typeof parsed.passed === "boolean" && parsed.passed)
|
|
21585
|
+
passCount++;
|
|
21586
|
+
else if (failOpen)
|
|
21587
|
+
passCount++;
|
|
21588
|
+
else
|
|
21589
|
+
failCount++;
|
|
21590
|
+
} catch {
|
|
21591
|
+
if (failOpen)
|
|
21592
|
+
passCount++;
|
|
21593
|
+
else
|
|
21594
|
+
failCount++;
|
|
21595
|
+
}
|
|
21596
|
+
}
|
|
21597
|
+
debateCtx.majorityVote = { passed: rawOutcome === "passed", passCount, failCount };
|
|
21598
|
+
}
|
|
21599
|
+
const story = {
|
|
21600
|
+
id: resolverContext.story.id,
|
|
21601
|
+
title: resolverContext.story.title,
|
|
21602
|
+
description: "",
|
|
21603
|
+
acceptanceCriteria: resolverContext.story.acceptanceCriteria
|
|
21604
|
+
};
|
|
21605
|
+
let dialogueResult;
|
|
21606
|
+
if (resolverContext.isReReview) {
|
|
21607
|
+
dialogueResult = await reviewerSession.reReviewDebate(resolverContext.labeledProposals, critiqueOutputs, resolverContext.diff, debateCtx);
|
|
21608
|
+
} else {
|
|
21609
|
+
dialogueResult = await reviewerSession.resolveDebate(resolverContext.labeledProposals, critiqueOutputs, resolverContext.diff, story, resolverContext.semanticConfig, debateCtx);
|
|
21610
|
+
}
|
|
21611
|
+
const outcome = dialogueResult.checkResult.success ? "passed" : "failed";
|
|
21612
|
+
return {
|
|
21613
|
+
outcome,
|
|
21614
|
+
resolverCostUsd: dialogueResult.cost ?? 0,
|
|
21615
|
+
dialogueResult
|
|
21616
|
+
};
|
|
21617
|
+
} catch (err) {
|
|
21618
|
+
logger?.warn("debate", "ReviewerSession.resolveDebate() failed \u2014 falling back to stateless resolver", {
|
|
21619
|
+
storyId,
|
|
21620
|
+
error: err instanceof Error ? err.message : String(err)
|
|
21621
|
+
});
|
|
21622
|
+
}
|
|
21623
|
+
}
|
|
21624
|
+
if (reviewerSession && !resolverContext) {
|
|
21625
|
+
logger?.warn("debate", "ReviewerSession provided but resolverContext is undefined \u2014 falling back to stateless resolver", { storyId });
|
|
21626
|
+
}
|
|
21541
21627
|
if (resolverConfig.type === "majority-fail-closed" || resolverConfig.type === "majority-fail-open") {
|
|
21542
|
-
if (workdir !== undefined) {
|
|
21628
|
+
if (workdir !== undefined && !reviewerSession) {
|
|
21543
21629
|
logger?.warn("debate", "majority resolver does not support implementer session resumption \u2014 switch to synthesis or custom resolver for context-aware semantic review");
|
|
21544
21630
|
}
|
|
21545
21631
|
return {
|
|
@@ -21565,7 +21651,8 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
|
|
|
21565
21651
|
});
|
|
21566
21652
|
return {
|
|
21567
21653
|
outcome: "passed",
|
|
21568
|
-
resolverCostUsd: resolverResult.costUsd
|
|
21654
|
+
resolverCostUsd: resolverResult.costUsd,
|
|
21655
|
+
output: resolverResult.output
|
|
21569
21656
|
};
|
|
21570
21657
|
}
|
|
21571
21658
|
return { outcome: "passed", resolverCostUsd: 0 };
|
|
@@ -21586,12 +21673,13 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
|
|
|
21586
21673
|
});
|
|
21587
21674
|
return {
|
|
21588
21675
|
outcome: "passed",
|
|
21589
|
-
resolverCostUsd: resolverResult.costUsd
|
|
21676
|
+
resolverCostUsd: resolverResult.costUsd,
|
|
21677
|
+
output: resolverResult.output
|
|
21590
21678
|
};
|
|
21591
21679
|
}
|
|
21592
21680
|
return { outcome: "passed", resolverCostUsd: 0 };
|
|
21593
21681
|
}
|
|
21594
|
-
var RESOLVER_FALLBACK_AGENT = "synthesis", _debateSessionDeps;
|
|
21682
|
+
var RESOLVER_FALLBACK_AGENT = "synthesis", _debateSessionDeps, MODEL_SHORTHAND_TIERS;
|
|
21595
21683
|
var init_session_helpers = __esm(() => {
|
|
21596
21684
|
init_adapter();
|
|
21597
21685
|
init_registry();
|
|
@@ -21603,6 +21691,11 @@ var init_session_helpers = __esm(() => {
|
|
|
21603
21691
|
getSafeLogger,
|
|
21604
21692
|
readFile: (path) => Bun.file(path).text()
|
|
21605
21693
|
};
|
|
21694
|
+
MODEL_SHORTHAND_TIERS = {
|
|
21695
|
+
haiku: "fast",
|
|
21696
|
+
sonnet: "balanced",
|
|
21697
|
+
opus: "powerful"
|
|
21698
|
+
};
|
|
21606
21699
|
});
|
|
21607
21700
|
|
|
21608
21701
|
// src/debate/concurrency.ts
|
|
@@ -21788,7 +21881,11 @@ async function runStateful(ctx, prompt) {
|
|
|
21788
21881
|
critiqueOutputs = critiqueSettled.filter((r) => r.status === "fulfilled").map((r) => r.value.output);
|
|
21789
21882
|
}
|
|
21790
21883
|
const proposalOutputs = successfulProposals.map((s) => s.output);
|
|
21791
|
-
const
|
|
21884
|
+
const fullResolverContext = ctx.resolverContextInput ? {
|
|
21885
|
+
...ctx.resolverContextInput,
|
|
21886
|
+
labeledProposals: successfulProposals.map((s) => ({ debater: s.debater.agent, output: s.output }))
|
|
21887
|
+
} : undefined;
|
|
21888
|
+
const outcome = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.storyId, ctx.timeoutSeconds * 1000, ctx.workdir, ctx.featureName, ctx.reviewerSession, fullResolverContext);
|
|
21792
21889
|
totalCostUsd += outcome.resolverCostUsd;
|
|
21793
21890
|
const proposals = successfulProposals.map((s) => ({
|
|
21794
21891
|
debater: s.debater,
|
|
@@ -21815,6 +21912,50 @@ var init_session_stateful = __esm(() => {
|
|
|
21815
21912
|
});
|
|
21816
21913
|
|
|
21817
21914
|
// src/debate/session-hybrid.ts
|
|
21915
|
+
async function runRebuttalLoop(ctx, proposals, originalPrompt, sessionRolePrefix) {
|
|
21916
|
+
const logger = _debateSessionDeps.getSafeLogger();
|
|
21917
|
+
const config2 = ctx.stageConfig;
|
|
21918
|
+
const rebuttals = [];
|
|
21919
|
+
let costUsd = 0;
|
|
21920
|
+
const proposalList = proposals.map((s) => ({ debater: s.debater, output: s.output }));
|
|
21921
|
+
try {
|
|
21922
|
+
for (let round = 1;round <= config2.rounds; round++) {
|
|
21923
|
+
const priorRebuttals = rebuttals.filter((r) => r.round < round).map((r) => r.output);
|
|
21924
|
+
for (let debaterIdx = 0;debaterIdx < proposals.length; debaterIdx++) {
|
|
21925
|
+
const proposal = proposals[debaterIdx];
|
|
21926
|
+
const sessionRole = `${sessionRolePrefix}-${debaterIdx}`;
|
|
21927
|
+
logger?.info("debate:rebuttal-start", "debate:rebuttal-start", {
|
|
21928
|
+
storyId: ctx.storyId,
|
|
21929
|
+
round,
|
|
21930
|
+
debaterIndex: debaterIdx
|
|
21931
|
+
});
|
|
21932
|
+
const rebuttalPrompt = buildRebuttalContext(originalPrompt, proposalList, priorRebuttals, debaterIdx);
|
|
21933
|
+
try {
|
|
21934
|
+
const turnResult = await runStatefulTurn(ctx, proposal.adapter, proposal.debater, rebuttalPrompt, sessionRole, true);
|
|
21935
|
+
costUsd += turnResult.cost;
|
|
21936
|
+
rebuttals.push({ debater: proposal.debater, round, output: turnResult.output });
|
|
21937
|
+
} catch (err) {
|
|
21938
|
+
logger?.warn("debate", "debate:rebuttal-failed", {
|
|
21939
|
+
storyId: ctx.storyId,
|
|
21940
|
+
round,
|
|
21941
|
+
debaterIndex: debaterIdx,
|
|
21942
|
+
error: err instanceof Error ? err.message : String(err)
|
|
21943
|
+
});
|
|
21944
|
+
}
|
|
21945
|
+
}
|
|
21946
|
+
}
|
|
21947
|
+
} finally {
|
|
21948
|
+
for (let debaterIdx = 0;debaterIdx < proposals.length; debaterIdx++) {
|
|
21949
|
+
const proposal = proposals[debaterIdx];
|
|
21950
|
+
const sessionRole = `${sessionRolePrefix}-${debaterIdx}`;
|
|
21951
|
+
try {
|
|
21952
|
+
const closeCost = await closeStatefulSession(ctx, proposal.adapter, proposal.debater, sessionRole);
|
|
21953
|
+
costUsd += closeCost;
|
|
21954
|
+
} catch {}
|
|
21955
|
+
}
|
|
21956
|
+
}
|
|
21957
|
+
return { rebuttals, costUsd };
|
|
21958
|
+
}
|
|
21818
21959
|
async function runHybrid(ctx, prompt) {
|
|
21819
21960
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
21820
21961
|
const config2 = ctx.stageConfig;
|
|
@@ -21883,45 +22024,14 @@ async function runHybrid(ctx, prompt) {
|
|
|
21883
22024
|
}
|
|
21884
22025
|
const proposalOutputs = successfulProposals.map((s) => s.output);
|
|
21885
22026
|
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
|
-
}
|
|
22027
|
+
const { rebuttals, costUsd: rebuttalCost } = await runRebuttalLoop(ctx, successfulProposals, prompt, "debate-hybrid");
|
|
22028
|
+
totalCostUsd += rebuttalCost;
|
|
21923
22029
|
const critiqueOutputs = rebuttals.map((r) => r.output);
|
|
21924
|
-
const
|
|
22030
|
+
const fullResolverContext = ctx.resolverContextInput ? {
|
|
22031
|
+
...ctx.resolverContextInput,
|
|
22032
|
+
labeledProposals: successfulProposals.map((s) => ({ debater: s.debater.agent, output: s.output }))
|
|
22033
|
+
} : undefined;
|
|
22034
|
+
const resolveResult = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.storyId, ctx.timeoutSeconds * 1000, ctx.workdir, ctx.featureName, ctx.reviewerSession, fullResolverContext);
|
|
21925
22035
|
totalCostUsd += resolveResult.resolverCostUsd;
|
|
21926
22036
|
return {
|
|
21927
22037
|
storyId: ctx.storyId,
|
|
@@ -22064,7 +22174,11 @@ async function runOneShot(ctx, prompt) {
|
|
|
22064
22174
|
critiqueOutputs = critiqueSettled.filter((r) => r.status === "fulfilled").map((r) => r.value.output);
|
|
22065
22175
|
}
|
|
22066
22176
|
const proposalOutputs = successful.map((p) => p.output);
|
|
22067
|
-
const
|
|
22177
|
+
const fullResolverContext = ctx.resolverContextInput ? {
|
|
22178
|
+
...ctx.resolverContextInput,
|
|
22179
|
+
labeledProposals: successful.map((p) => ({ debater: p.debater.agent, output: p.output }))
|
|
22180
|
+
} : undefined;
|
|
22181
|
+
const outcome = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.storyId, ctx.timeoutMs, ctx.workdir, ctx.featureName, ctx.reviewerSession, fullResolverContext);
|
|
22068
22182
|
totalCostUsd += outcome.resolverCostUsd;
|
|
22069
22183
|
const proposals = successful.map((p) => ({
|
|
22070
22184
|
debater: p.debater,
|
|
@@ -22096,7 +22210,7 @@ async function runPlan2(ctx, basePrompt, opts) {
|
|
|
22096
22210
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
22097
22211
|
const config2 = ctx.stageConfig;
|
|
22098
22212
|
const debaters = config2.debaters ?? [];
|
|
22099
|
-
|
|
22213
|
+
let totalCostUsd = 0;
|
|
22100
22214
|
const resolved = [];
|
|
22101
22215
|
for (const debater of debaters) {
|
|
22102
22216
|
const adapter = _debateSessionDeps.getAgent(debater.agent, ctx.config);
|
|
@@ -22119,13 +22233,16 @@ async function runPlan2(ctx, basePrompt, opts) {
|
|
|
22119
22233
|
|
|
22120
22234
|
Write the PRD JSON directly to this file path: ${tempOutputPath}
|
|
22121
22235
|
Do NOT output the JSON to the conversation. Write the file, then reply with a brief confirmation.`;
|
|
22122
|
-
|
|
22236
|
+
const modelTier = modelTierFromDebater(debater);
|
|
22237
|
+
const modelDef = resolveModelDefForDebater(debater, modelTier, ctx.config);
|
|
22238
|
+
const planResult = await adapter.plan({
|
|
22123
22239
|
prompt: debaterPrompt,
|
|
22124
22240
|
workdir: opts.workdir,
|
|
22125
22241
|
interactive: false,
|
|
22126
22242
|
timeoutSeconds: opts.timeoutSeconds,
|
|
22127
22243
|
config: ctx.config,
|
|
22128
|
-
modelTier
|
|
22244
|
+
modelTier,
|
|
22245
|
+
modelDef,
|
|
22129
22246
|
dangerouslySkipPermissions: opts.dangerouslySkipPermissions,
|
|
22130
22247
|
maxInteractionTurns: opts.maxInteractionTurns,
|
|
22131
22248
|
featureName: opts.feature,
|
|
@@ -22133,13 +22250,14 @@ Do NOT output the JSON to the conversation. Write the file, then reply with a br
|
|
|
22133
22250
|
sessionRole: `plan-${i}`
|
|
22134
22251
|
});
|
|
22135
22252
|
const output = await _debateSessionDeps.readFile(tempOutputPath);
|
|
22136
|
-
return { debater, adapter, output, cost: 0 };
|
|
22253
|
+
return { debater, adapter, output, cost: planResult.costUsd ?? 0 };
|
|
22137
22254
|
}), concurrencyLimit);
|
|
22138
22255
|
const successful = [];
|
|
22139
22256
|
for (let i = 0;i < settled.length; i++) {
|
|
22140
22257
|
const res = settled[i];
|
|
22141
22258
|
if (res.status === "fulfilled") {
|
|
22142
22259
|
successful.push(res.value);
|
|
22260
|
+
totalCostUsd += res.value.cost;
|
|
22143
22261
|
} else {
|
|
22144
22262
|
const { debater } = resolved[i];
|
|
22145
22263
|
logger?.warn("debate", "debate:debater-failed", {
|
|
@@ -22191,9 +22309,30 @@ Do NOT output the JSON to the conversation. Write the file, then reply with a br
|
|
|
22191
22309
|
};
|
|
22192
22310
|
}
|
|
22193
22311
|
const proposalOutputs = successful.map((p) => p.output);
|
|
22312
|
+
const mode = ctx.stageConfig.mode ?? "panel";
|
|
22313
|
+
const sessionMode = ctx.stageConfig.sessionMode ?? "one-shot";
|
|
22314
|
+
let critiqueOutputs = [];
|
|
22315
|
+
let rebuttalList;
|
|
22316
|
+
if (mode === "hybrid" && sessionMode === "stateful") {
|
|
22317
|
+
const hybridCtx = {
|
|
22318
|
+
storyId: ctx.storyId,
|
|
22319
|
+
stage: ctx.stage,
|
|
22320
|
+
stageConfig: ctx.stageConfig,
|
|
22321
|
+
config: ctx.config,
|
|
22322
|
+
workdir: opts.workdir,
|
|
22323
|
+
featureName: opts.feature,
|
|
22324
|
+
timeoutSeconds: opts.timeoutSeconds ?? 600
|
|
22325
|
+
};
|
|
22326
|
+
const { rebuttals, costUsd } = await runRebuttalLoop(hybridCtx, successful, basePrompt, "plan-hybrid");
|
|
22327
|
+
critiqueOutputs = rebuttals.map((r) => r.output);
|
|
22328
|
+
rebuttalList = rebuttals;
|
|
22329
|
+
totalCostUsd += costUsd;
|
|
22330
|
+
} else if (mode === "hybrid") {
|
|
22331
|
+
logger?.warn("debate", "hybrid mode requires sessionMode: stateful for plan \u2014 running as panel");
|
|
22332
|
+
}
|
|
22194
22333
|
const resolverTimeoutMs = (ctx.stageConfig.timeoutSeconds ?? 600) * 1000;
|
|
22195
|
-
const outcome = await resolveOutcome(proposalOutputs,
|
|
22196
|
-
const winningOutput = successful[0].output;
|
|
22334
|
+
const outcome = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.storyId, resolverTimeoutMs, opts.workdir, opts.feature);
|
|
22335
|
+
const winningOutput = outcome.output ?? successful[0].output;
|
|
22197
22336
|
const proposals = successful.map((p) => ({ debater: p.debater, output: p.output }));
|
|
22198
22337
|
logger?.info("debate", "debate:result", {
|
|
22199
22338
|
storyId: ctx.storyId,
|
|
@@ -22204,16 +22343,18 @@ Do NOT output the JSON to the conversation. Write the file, then reply with a br
|
|
|
22204
22343
|
storyId: ctx.storyId,
|
|
22205
22344
|
stage: ctx.stage,
|
|
22206
22345
|
outcome: outcome.outcome,
|
|
22207
|
-
rounds: 1,
|
|
22346
|
+
rounds: rebuttalList ? config2.rounds : 1,
|
|
22208
22347
|
debaters: successful.map((p) => p.debater.agent),
|
|
22209
22348
|
resolverType: config2.resolver.type,
|
|
22210
22349
|
proposals,
|
|
22350
|
+
rebuttals: rebuttalList,
|
|
22211
22351
|
output: winningOutput,
|
|
22212
22352
|
totalCostUsd
|
|
22213
22353
|
};
|
|
22214
22354
|
}
|
|
22215
22355
|
var init_session_plan = __esm(() => {
|
|
22216
22356
|
init_session_helpers();
|
|
22357
|
+
init_session_hybrid();
|
|
22217
22358
|
});
|
|
22218
22359
|
|
|
22219
22360
|
// src/debate/session.ts
|
|
@@ -22225,6 +22366,8 @@ class DebateSession {
|
|
|
22225
22366
|
workdir;
|
|
22226
22367
|
featureName;
|
|
22227
22368
|
timeoutSeconds;
|
|
22369
|
+
reviewerSession;
|
|
22370
|
+
resolverContextInput;
|
|
22228
22371
|
get timeoutMs() {
|
|
22229
22372
|
return this.timeoutSeconds * 1000;
|
|
22230
22373
|
}
|
|
@@ -22236,6 +22379,8 @@ class DebateSession {
|
|
|
22236
22379
|
this.workdir = opts.workdir ?? process.cwd();
|
|
22237
22380
|
this.featureName = opts.featureName ?? opts.stage;
|
|
22238
22381
|
this.timeoutSeconds = opts.timeoutSeconds ?? opts.stageConfig.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS;
|
|
22382
|
+
this.reviewerSession = opts.reviewerSession;
|
|
22383
|
+
this.resolverContextInput = opts.resolverContextInput;
|
|
22239
22384
|
}
|
|
22240
22385
|
async run(prompt) {
|
|
22241
22386
|
const sessionMode = this.stageConfig.sessionMode ?? "one-shot";
|
|
@@ -22249,7 +22394,9 @@ class DebateSession {
|
|
|
22249
22394
|
config: this.config,
|
|
22250
22395
|
workdir: this.workdir,
|
|
22251
22396
|
featureName: this.featureName,
|
|
22252
|
-
timeoutSeconds: this.timeoutSeconds
|
|
22397
|
+
timeoutSeconds: this.timeoutSeconds,
|
|
22398
|
+
reviewerSession: this.reviewerSession,
|
|
22399
|
+
resolverContextInput: this.resolverContextInput
|
|
22253
22400
|
}, prompt);
|
|
22254
22401
|
}
|
|
22255
22402
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
@@ -22259,7 +22406,11 @@ class DebateSession {
|
|
|
22259
22406
|
stage: this.stage,
|
|
22260
22407
|
stageConfig: this.stageConfig,
|
|
22261
22408
|
config: this.config,
|
|
22262
|
-
timeoutMs: this.timeoutMs
|
|
22409
|
+
timeoutMs: this.timeoutMs,
|
|
22410
|
+
workdir: this.workdir,
|
|
22411
|
+
featureName: this.featureName,
|
|
22412
|
+
reviewerSession: this.reviewerSession,
|
|
22413
|
+
resolverContextInput: this.resolverContextInput
|
|
22263
22414
|
}, prompt);
|
|
22264
22415
|
}
|
|
22265
22416
|
if (sessionMode === "stateful") {
|
|
@@ -22270,7 +22421,9 @@ class DebateSession {
|
|
|
22270
22421
|
config: this.config,
|
|
22271
22422
|
workdir: this.workdir,
|
|
22272
22423
|
featureName: this.featureName,
|
|
22273
|
-
timeoutSeconds: this.timeoutSeconds
|
|
22424
|
+
timeoutSeconds: this.timeoutSeconds,
|
|
22425
|
+
reviewerSession: this.reviewerSession,
|
|
22426
|
+
resolverContextInput: this.resolverContextInput
|
|
22274
22427
|
}, prompt);
|
|
22275
22428
|
}
|
|
22276
22429
|
return runOneShot({
|
|
@@ -22278,7 +22431,11 @@ class DebateSession {
|
|
|
22278
22431
|
stage: this.stage,
|
|
22279
22432
|
stageConfig: this.stageConfig,
|
|
22280
22433
|
config: this.config,
|
|
22281
|
-
timeoutMs: this.timeoutMs
|
|
22434
|
+
timeoutMs: this.timeoutMs,
|
|
22435
|
+
workdir: this.workdir,
|
|
22436
|
+
featureName: this.featureName,
|
|
22437
|
+
reviewerSession: this.reviewerSession,
|
|
22438
|
+
resolverContextInput: this.resolverContextInput
|
|
22282
22439
|
}, prompt);
|
|
22283
22440
|
}
|
|
22284
22441
|
async runPlan(basePrompt, opts) {
|
|
@@ -22613,30 +22770,41 @@ class CLIInteractionPlugin {
|
|
|
22613
22770
|
}
|
|
22614
22771
|
async send(request) {
|
|
22615
22772
|
this.pendingRequests.set(request.id, request);
|
|
22616
|
-
|
|
22617
|
-
${"=".repeat(80)}
|
|
22618
|
-
|
|
22619
|
-
|
|
22620
|
-
|
|
22773
|
+
process.stdout.write(`
|
|
22774
|
+
${"=".repeat(80)}
|
|
22775
|
+
`);
|
|
22776
|
+
process.stdout.write(`[INTERACTION] ${request.stage.toUpperCase()} \u2014 ${request.type.toUpperCase()}
|
|
22777
|
+
`);
|
|
22778
|
+
process.stdout.write(`${"=".repeat(80)}
|
|
22779
|
+
`);
|
|
22780
|
+
process.stdout.write(`
|
|
22621
22781
|
${request.summary}
|
|
22782
|
+
|
|
22622
22783
|
`);
|
|
22623
22784
|
if (request.detail) {
|
|
22624
|
-
|
|
22625
|
-
|
|
22785
|
+
process.stdout.write(`${request.detail}
|
|
22786
|
+
`);
|
|
22787
|
+
process.stdout.write(`
|
|
22788
|
+
`);
|
|
22626
22789
|
}
|
|
22627
22790
|
if (request.options && request.options.length > 0) {
|
|
22628
|
-
|
|
22791
|
+
process.stdout.write(`Options:
|
|
22792
|
+
`);
|
|
22629
22793
|
for (const opt of request.options) {
|
|
22630
22794
|
const desc = opt.description ? ` \u2014 ${opt.description}` : "";
|
|
22631
|
-
|
|
22795
|
+
process.stdout.write(` [${opt.key}] ${opt.label}${desc}
|
|
22796
|
+
`);
|
|
22632
22797
|
}
|
|
22633
|
-
|
|
22798
|
+
process.stdout.write(`
|
|
22799
|
+
`);
|
|
22634
22800
|
}
|
|
22635
22801
|
if (request.timeout) {
|
|
22636
22802
|
const timeoutSec = Math.floor(request.timeout / 1000);
|
|
22637
|
-
|
|
22803
|
+
process.stdout.write(`[Timeout: ${timeoutSec}s | Fallback: ${request.fallback}]
|
|
22804
|
+
`);
|
|
22638
22805
|
}
|
|
22639
|
-
|
|
22806
|
+
process.stdout.write(`${"=".repeat(80)}
|
|
22807
|
+
|
|
22640
22808
|
`);
|
|
22641
22809
|
}
|
|
22642
22810
|
async receive(requestId, timeout = 60000) {
|
|
@@ -26414,59 +26582,6 @@ ${stderr}` };
|
|
|
26414
26582
|
};
|
|
26415
26583
|
});
|
|
26416
26584
|
|
|
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
26585
|
// src/quality/runner.ts
|
|
26471
26586
|
var {spawn: spawn2 } = globalThis.Bun;
|
|
26472
26587
|
async function runQualityCommand(opts) {
|
|
@@ -26769,7 +26884,60 @@ Do NOT add new features \u2014 only fix the identified issues.
|
|
|
26769
26884
|
Commit your fixes when done.${scopeConstraint}`;
|
|
26770
26885
|
}
|
|
26771
26886
|
|
|
26772
|
-
// src/
|
|
26887
|
+
// src/agents/claude/index.ts
|
|
26888
|
+
var init_claude = __esm(() => {
|
|
26889
|
+
init_adapter3();
|
|
26890
|
+
init_execution();
|
|
26891
|
+
});
|
|
26892
|
+
|
|
26893
|
+
// src/agents/shared/validation.ts
|
|
26894
|
+
function validateAgentForTier(agent, tier) {
|
|
26895
|
+
return agent.capabilities.supportedTiers.includes(tier);
|
|
26896
|
+
}
|
|
26897
|
+
function validateAgentFeature(agent, feature) {
|
|
26898
|
+
return agent.capabilities.features.has(feature);
|
|
26899
|
+
}
|
|
26900
|
+
function describeAgentCapabilities(agent) {
|
|
26901
|
+
const tiers = agent.capabilities.supportedTiers.join(",");
|
|
26902
|
+
const features = Array.from(agent.capabilities.features).join(",");
|
|
26903
|
+
const maxTokens = agent.capabilities.maxContextTokens;
|
|
26904
|
+
return `${agent.name}: tiers=[${tiers}], maxTokens=${maxTokens}, features=[${features}]`;
|
|
26905
|
+
}
|
|
26906
|
+
|
|
26907
|
+
// src/agents/index.ts
|
|
26908
|
+
var exports_agents = {};
|
|
26909
|
+
__export(exports_agents, {
|
|
26910
|
+
validateAgentForTier: () => validateAgentForTier,
|
|
26911
|
+
validateAgentFeature: () => validateAgentFeature,
|
|
26912
|
+
parseTokenUsage: () => parseTokenUsage,
|
|
26913
|
+
getInstalledAgents: () => getInstalledAgents,
|
|
26914
|
+
getAllAgentNames: () => getAllAgentNames,
|
|
26915
|
+
getAgentVersions: () => getAgentVersions,
|
|
26916
|
+
getAgentVersion: () => getAgentVersion,
|
|
26917
|
+
getAgent: () => getAgent,
|
|
26918
|
+
formatCostWithConfidence: () => formatCostWithConfidence,
|
|
26919
|
+
estimateCostFromTokenUsage: () => estimateCostFromTokenUsage,
|
|
26920
|
+
estimateCostFromOutput: () => estimateCostFromOutput,
|
|
26921
|
+
estimateCostByDuration: () => estimateCostByDuration,
|
|
26922
|
+
estimateCost: () => estimateCost,
|
|
26923
|
+
describeAgentCapabilities: () => describeAgentCapabilities,
|
|
26924
|
+
checkAgentHealth: () => checkAgentHealth,
|
|
26925
|
+
MODEL_PRICING: () => MODEL_PRICING,
|
|
26926
|
+
CompleteError: () => CompleteError,
|
|
26927
|
+
ClaudeCodeAdapter: () => ClaudeCodeAdapter,
|
|
26928
|
+
COST_RATES: () => COST_RATES,
|
|
26929
|
+
AllAgentsUnavailableError: () => AllAgentsUnavailableError
|
|
26930
|
+
});
|
|
26931
|
+
var init_agents = __esm(() => {
|
|
26932
|
+
init_types2();
|
|
26933
|
+
init_claude();
|
|
26934
|
+
init_registry();
|
|
26935
|
+
init_cost();
|
|
26936
|
+
init_version_detection();
|
|
26937
|
+
init_errors();
|
|
26938
|
+
});
|
|
26939
|
+
|
|
26940
|
+
// src/review/dialogue-prompts.ts
|
|
26773
26941
|
function buildReviewPrompt(diff, story, _semanticConfig) {
|
|
26774
26942
|
const criteria = story.acceptanceCriteria.map((c) => `- ${c}`).join(`
|
|
26775
26943
|
`);
|
|
@@ -26803,6 +26971,98 @@ function buildReReviewPrompt(updatedDiff, previousFindings) {
|
|
|
26803
26971
|
].join(`
|
|
26804
26972
|
`);
|
|
26805
26973
|
}
|
|
26974
|
+
function buildProposalsSection(proposals) {
|
|
26975
|
+
return proposals.map((p) => `### ${p.debater}
|
|
26976
|
+
${p.output}`).join(`
|
|
26977
|
+
|
|
26978
|
+
`);
|
|
26979
|
+
}
|
|
26980
|
+
function buildCritiquesSection(critiques) {
|
|
26981
|
+
if (critiques.length === 0)
|
|
26982
|
+
return "";
|
|
26983
|
+
return `
|
|
26984
|
+
|
|
26985
|
+
## Critiques
|
|
26986
|
+
${critiques.map((c, i) => `### Critique ${i + 1}
|
|
26987
|
+
${c}`).join(`
|
|
26988
|
+
|
|
26989
|
+
`)}`;
|
|
26990
|
+
}
|
|
26991
|
+
function buildVoteTallyLine(ctx) {
|
|
26992
|
+
if (!ctx.majorityVote)
|
|
26993
|
+
return "";
|
|
26994
|
+
const { passCount, failCount } = ctx.majorityVote;
|
|
26995
|
+
const failOpenNote = ctx.resolverType === "majority-fail-open" ? " (unparseable proposals count as pass)" : " (unparseable proposals count as fail)";
|
|
26996
|
+
return `
|
|
26997
|
+
|
|
26998
|
+
The preliminary majority vote is: **${passCount} passed, ${failCount} failed**${failOpenNote}. Verify the failing findings with tools before giving your authoritative verdict.`;
|
|
26999
|
+
}
|
|
27000
|
+
function buildResolverFraming(ctx) {
|
|
27001
|
+
switch (ctx.resolverType) {
|
|
27002
|
+
case "majority-fail-closed":
|
|
27003
|
+
case "majority-fail-open":
|
|
27004
|
+
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.";
|
|
27005
|
+
case "synthesis":
|
|
27006
|
+
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.";
|
|
27007
|
+
case "custom":
|
|
27008
|
+
return "You are the judge. Evaluate the debater proposals independently. Verify claims with tools (READ, GREP) and give your final authoritative verdict.";
|
|
27009
|
+
default:
|
|
27010
|
+
return "You are the reviewer. Evaluate the debater proposals and give your final authoritative verdict.";
|
|
27011
|
+
}
|
|
27012
|
+
}
|
|
27013
|
+
function buildDebateResolverPrompt(proposals, critiques, diff, story, _semanticConfig, resolverContext) {
|
|
27014
|
+
const criteria = story.acceptanceCriteria.map((c) => `- ${c}`).join(`
|
|
27015
|
+
`);
|
|
27016
|
+
const framing = buildResolverFraming(resolverContext);
|
|
27017
|
+
const voteTally = buildVoteTallyLine(resolverContext);
|
|
27018
|
+
const proposalsSection = buildProposalsSection(proposals);
|
|
27019
|
+
const critiquesSection = buildCritiquesSection(critiques);
|
|
27020
|
+
return [
|
|
27021
|
+
framing,
|
|
27022
|
+
"",
|
|
27023
|
+
`## Story ${story.id}: ${story.title}`,
|
|
27024
|
+
"",
|
|
27025
|
+
"## Acceptance Criteria",
|
|
27026
|
+
criteria,
|
|
27027
|
+
"",
|
|
27028
|
+
"## Debater Proposals",
|
|
27029
|
+
proposalsSection,
|
|
27030
|
+
critiquesSection,
|
|
27031
|
+
"",
|
|
27032
|
+
"## Diff",
|
|
27033
|
+
diff,
|
|
27034
|
+
voteTally,
|
|
27035
|
+
"",
|
|
27036
|
+
"Respond with JSON: { passed: boolean, findings: [...], findingReasoning: { [id]: string } }"
|
|
27037
|
+
].filter((line) => line !== undefined).join(`
|
|
27038
|
+
`);
|
|
27039
|
+
}
|
|
27040
|
+
function buildDebateReReviewPrompt(proposals, critiques, updatedDiff, previousFindings, resolverContext) {
|
|
27041
|
+
const framing = buildResolverFraming(resolverContext);
|
|
27042
|
+
const findingsList = previousFindings.length > 0 ? previousFindings.map((f) => `- ${f.ruleId}: ${f.message}`).join(`
|
|
27043
|
+
`) : "(none)";
|
|
27044
|
+
const proposalsSection = buildProposalsSection(proposals);
|
|
27045
|
+
const critiquesSection = buildCritiquesSection(critiques);
|
|
27046
|
+
return [
|
|
27047
|
+
`${framing} This is a re-review after implementer changes.`,
|
|
27048
|
+
"",
|
|
27049
|
+
"## Previous Findings",
|
|
27050
|
+
findingsList,
|
|
27051
|
+
"",
|
|
27052
|
+
"## Updated Debater Proposals",
|
|
27053
|
+
proposalsSection,
|
|
27054
|
+
critiquesSection,
|
|
27055
|
+
"",
|
|
27056
|
+
"## Updated Diff",
|
|
27057
|
+
updatedDiff,
|
|
27058
|
+
"",
|
|
27059
|
+
"Respond with JSON: { passed: boolean, findings: [...], findingReasoning: { [id]: string }, deltaSummary: string }",
|
|
27060
|
+
"deltaSummary should describe which previous findings are resolved vs still present."
|
|
27061
|
+
].filter((line) => line !== undefined).join(`
|
|
27062
|
+
`);
|
|
27063
|
+
}
|
|
27064
|
+
|
|
27065
|
+
// src/review/dialogue.ts
|
|
26806
27066
|
function extractDeltaSummary(rawOutput, previousFindings, newFindings) {
|
|
26807
27067
|
try {
|
|
26808
27068
|
const parsed = JSON.parse(rawOutput);
|
|
@@ -26874,6 +27134,7 @@ function createReviewerSession(agent, storyId, workdir, featureName, _config) {
|
|
|
26874
27134
|
let lastCheckResult = null;
|
|
26875
27135
|
let lastStory = null;
|
|
26876
27136
|
let lastSemanticConfig = null;
|
|
27137
|
+
let lastWasDebateResolve = false;
|
|
26877
27138
|
const sessionState = {
|
|
26878
27139
|
generation: 1,
|
|
26879
27140
|
pendingCompactionContext: null
|
|
@@ -26936,6 +27197,7 @@ ${prompt}`,
|
|
|
26936
27197
|
lastCheckResult = reviewResult;
|
|
26937
27198
|
lastStory = story;
|
|
26938
27199
|
lastSemanticConfig = semanticConfig;
|
|
27200
|
+
lastWasDebateResolve = false;
|
|
26939
27201
|
return reviewResult;
|
|
26940
27202
|
},
|
|
26941
27203
|
async reReview(updatedDiff) {
|
|
@@ -27005,6 +27267,76 @@ ${prompt}`,
|
|
|
27005
27267
|
history.push({ role: "reviewer", content: result.output });
|
|
27006
27268
|
return result.output;
|
|
27007
27269
|
},
|
|
27270
|
+
async resolveDebate(proposals, critiques, diff, story, semanticConfig, resolverContext) {
|
|
27271
|
+
if (!active) {
|
|
27272
|
+
throw new NaxError(`[dialogue] ReviewerSession for story ${storyId} has been destroyed`, "REVIEWER_SESSION_DESTROYED", { stage: "review", storyId, featureName });
|
|
27273
|
+
}
|
|
27274
|
+
const prompt = buildDebateResolverPrompt(proposals, critiques, diff, story, semanticConfig, resolverContext);
|
|
27275
|
+
const { modelTier, modelDef, timeoutSeconds } = resolveRunParams(semanticConfig);
|
|
27276
|
+
const { effectivePrompt, acpSessionName } = buildEffectiveRunArgs(prompt);
|
|
27277
|
+
const result = await agent.run({
|
|
27278
|
+
prompt: effectivePrompt,
|
|
27279
|
+
workdir,
|
|
27280
|
+
modelTier,
|
|
27281
|
+
modelDef,
|
|
27282
|
+
timeoutSeconds,
|
|
27283
|
+
sessionRole: "reviewer",
|
|
27284
|
+
keepSessionOpen: true,
|
|
27285
|
+
pipelineStage: "review",
|
|
27286
|
+
config: _config,
|
|
27287
|
+
storyId,
|
|
27288
|
+
featureName,
|
|
27289
|
+
acpSessionName
|
|
27290
|
+
});
|
|
27291
|
+
history.push({ role: "implementer", content: prompt });
|
|
27292
|
+
history.push({ role: "reviewer", content: result.output });
|
|
27293
|
+
const parsed = parseReviewResponse(result.output);
|
|
27294
|
+
const reviewResult = { ...parsed, cost: result.estimatedCost ?? 0 };
|
|
27295
|
+
lastCheckResult = reviewResult;
|
|
27296
|
+
lastStory = story;
|
|
27297
|
+
lastSemanticConfig = semanticConfig;
|
|
27298
|
+
lastWasDebateResolve = true;
|
|
27299
|
+
return reviewResult;
|
|
27300
|
+
},
|
|
27301
|
+
async reReviewDebate(proposals, critiques, updatedDiff, resolverContext) {
|
|
27302
|
+
if (!active) {
|
|
27303
|
+
throw new NaxError(`[dialogue] ReviewerSession for story ${storyId} has been destroyed`, "REVIEWER_SESSION_DESTROYED", { stage: "review", storyId, featureName });
|
|
27304
|
+
}
|
|
27305
|
+
if (!lastCheckResult || !lastSemanticConfig || !lastWasDebateResolve) {
|
|
27306
|
+
throw new NaxError(`[dialogue] reReviewDebate() called before any resolveDebate() on story ${storyId}`, "NO_REVIEW_RESULT", { stage: "review", storyId });
|
|
27307
|
+
}
|
|
27308
|
+
const previousFindings = lastCheckResult.checkResult.findings;
|
|
27309
|
+
const prompt = buildDebateReReviewPrompt(proposals, critiques, updatedDiff, previousFindings, resolverContext);
|
|
27310
|
+
const { modelTier, modelDef, timeoutSeconds } = resolveRunParams(lastSemanticConfig);
|
|
27311
|
+
const { effectivePrompt, acpSessionName } = buildEffectiveRunArgs(prompt);
|
|
27312
|
+
const result = await agent.run({
|
|
27313
|
+
prompt: effectivePrompt,
|
|
27314
|
+
workdir,
|
|
27315
|
+
modelTier,
|
|
27316
|
+
modelDef,
|
|
27317
|
+
timeoutSeconds,
|
|
27318
|
+
sessionRole: "reviewer",
|
|
27319
|
+
keepSessionOpen: true,
|
|
27320
|
+
pipelineStage: "review",
|
|
27321
|
+
config: _config,
|
|
27322
|
+
storyId,
|
|
27323
|
+
featureName,
|
|
27324
|
+
acpSessionName
|
|
27325
|
+
});
|
|
27326
|
+
history.push({ role: "implementer", content: prompt });
|
|
27327
|
+
history.push({ role: "reviewer", content: result.output });
|
|
27328
|
+
const parsed = parseReviewResponse(result.output);
|
|
27329
|
+
const deltaSummary = extractDeltaSummary(result.output, previousFindings, parsed.checkResult.findings);
|
|
27330
|
+
const dialogueResult = { ...parsed, deltaSummary, cost: result.estimatedCost ?? 0 };
|
|
27331
|
+
lastCheckResult = dialogueResult;
|
|
27332
|
+
const maxMessages = _config.review?.dialogue?.maxDialogueMessages ?? 20;
|
|
27333
|
+
if (history.length > maxMessages) {
|
|
27334
|
+
const compactedSummary = compactHistory(history);
|
|
27335
|
+
sessionState.generation++;
|
|
27336
|
+
sessionState.pendingCompactionContext = compactedSummary;
|
|
27337
|
+
}
|
|
27338
|
+
return dialogueResult;
|
|
27339
|
+
},
|
|
27008
27340
|
getVerdict() {
|
|
27009
27341
|
if (!lastCheckResult || !lastStory) {
|
|
27010
27342
|
throw new NaxError(`[dialogue] getVerdict() called before any review() on story ${storyId}`, "NO_REVIEW_RESULT", { stage: "review", storyId });
|
|
@@ -27240,10 +27572,8 @@ var init_language_commands = __esm(() => {
|
|
|
27240
27572
|
// src/review/semantic.ts
|
|
27241
27573
|
var {spawn: spawn3 } = globalThis.Bun;
|
|
27242
27574
|
async function collectDiff(workdir, storyGitRef, excludePatterns) {
|
|
27243
|
-
const
|
|
27244
|
-
|
|
27245
|
-
cmd.push("--", ".", ...excludePatterns);
|
|
27246
|
-
}
|
|
27575
|
+
const merged = [...new Set([...excludePatterns, ...ALWAYS_EXCLUDED])];
|
|
27576
|
+
const cmd = ["git", "diff", "--unified=3", `${storyGitRef}..HEAD`, "--", ".", ...merged];
|
|
27247
27577
|
const proc = _semanticDeps.spawn({
|
|
27248
27578
|
cmd,
|
|
27249
27579
|
cwd: workdir,
|
|
@@ -27403,7 +27733,7 @@ function toReviewFindings(findings) {
|
|
|
27403
27733
|
source: "semantic-review"
|
|
27404
27734
|
}));
|
|
27405
27735
|
}
|
|
27406
|
-
async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, modelResolver, naxConfig, featureName) {
|
|
27736
|
+
async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, modelResolver, naxConfig, featureName, resolverSession) {
|
|
27407
27737
|
const startTime = Date.now();
|
|
27408
27738
|
const logger = getSafeLogger();
|
|
27409
27739
|
if (featureName === undefined) {
|
|
@@ -27473,6 +27803,7 @@ async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, mo
|
|
|
27473
27803
|
const reviewDebateEnabled = naxConfig?.debate?.enabled && naxConfig?.debate?.stages?.review?.enabled;
|
|
27474
27804
|
if (reviewDebateEnabled) {
|
|
27475
27805
|
const reviewStageConfig = naxConfig?.debate?.stages.review;
|
|
27806
|
+
const isReReview = resolverSession !== undefined && resolverSession.history.length > 0;
|
|
27476
27807
|
const debateSession = _semanticDeps.createDebateSession({
|
|
27477
27808
|
storyId: story.id,
|
|
27478
27809
|
stage: "review",
|
|
@@ -27480,26 +27811,69 @@ async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, mo
|
|
|
27480
27811
|
config: naxConfig ?? DEFAULT_CONFIG,
|
|
27481
27812
|
workdir,
|
|
27482
27813
|
featureName,
|
|
27483
|
-
timeoutSeconds: naxConfig?.execution?.sessionTimeoutSeconds
|
|
27814
|
+
timeoutSeconds: naxConfig?.execution?.sessionTimeoutSeconds,
|
|
27815
|
+
reviewerSession: resolverSession,
|
|
27816
|
+
resolverContextInput: resolverSession ? {
|
|
27817
|
+
diff,
|
|
27818
|
+
story: { id: story.id, title: story.title, acceptanceCriteria: story.acceptanceCriteria },
|
|
27819
|
+
semanticConfig,
|
|
27820
|
+
resolverType: reviewStageConfig.resolver.type,
|
|
27821
|
+
isReReview
|
|
27822
|
+
} : undefined
|
|
27484
27823
|
});
|
|
27824
|
+
const historyLenBefore = resolverSession?.history.length ?? 0;
|
|
27485
27825
|
const debateResult = await debateSession.run(prompt);
|
|
27486
27826
|
const debateCost = debateResult.totalCostUsd ?? 0;
|
|
27487
|
-
|
|
27488
|
-
|
|
27827
|
+
const sessionUsed = resolverSession && resolverSession.history.length > historyLenBefore;
|
|
27828
|
+
if (sessionUsed) {
|
|
27829
|
+
const durationMs3 = Date.now() - startTime;
|
|
27830
|
+
try {
|
|
27831
|
+
const verdict = resolverSession.getVerdict();
|
|
27832
|
+
const findings = verdict.findings ?? [];
|
|
27833
|
+
if (!verdict.passed && findings.length > 0) {
|
|
27834
|
+
logger?.warn("review", `Semantic review failed (debate+dialogue): ${findings.length} findings`, {
|
|
27835
|
+
storyId: story.id,
|
|
27836
|
+
durationMs: durationMs3
|
|
27837
|
+
});
|
|
27838
|
+
return {
|
|
27839
|
+
check: "semantic",
|
|
27840
|
+
success: false,
|
|
27841
|
+
command: "",
|
|
27842
|
+
exitCode: 1,
|
|
27843
|
+
output: `Semantic review failed:
|
|
27844
|
+
|
|
27845
|
+
${findings.map((f) => `${f.ruleId}: ${f.message}`).join(`
|
|
27846
|
+
`)}`,
|
|
27847
|
+
durationMs: durationMs3,
|
|
27848
|
+
findings,
|
|
27849
|
+
cost: debateCost
|
|
27850
|
+
};
|
|
27851
|
+
}
|
|
27852
|
+
const label = verdict.passed ? "Semantic review passed (debate+dialogue)" : "Semantic review passed (debate+dialogue, all findings non-blocking)";
|
|
27853
|
+
logger?.info("review", label, { storyId: story.id, durationMs: durationMs3 });
|
|
27854
|
+
return {
|
|
27855
|
+
check: "semantic",
|
|
27856
|
+
success: true,
|
|
27857
|
+
command: "",
|
|
27858
|
+
exitCode: 0,
|
|
27859
|
+
output: label,
|
|
27860
|
+
durationMs: durationMs3,
|
|
27861
|
+
cost: debateCost
|
|
27862
|
+
};
|
|
27863
|
+
} catch {
|
|
27864
|
+
logger?.warn("review", "getVerdict() failed after debate+dialogue \u2014 falling back to stateless verdict", {
|
|
27865
|
+
storyId: story.id
|
|
27866
|
+
});
|
|
27867
|
+
}
|
|
27868
|
+
}
|
|
27869
|
+
const resolverPassed = debateResult.outcome === "passed";
|
|
27489
27870
|
const allFindings = [];
|
|
27490
27871
|
for (const p of debateResult.proposals) {
|
|
27491
27872
|
const parsed2 = parseLLMResponse(p.output);
|
|
27492
27873
|
if (parsed2) {
|
|
27493
|
-
if (parsed2.passed)
|
|
27494
|
-
passCount++;
|
|
27495
|
-
else
|
|
27496
|
-
failCount++;
|
|
27497
27874
|
allFindings.push(...parsed2.findings);
|
|
27498
|
-
} else {
|
|
27499
|
-
failCount++;
|
|
27500
27875
|
}
|
|
27501
27876
|
}
|
|
27502
|
-
const majorityPassed = passCount > failCount;
|
|
27503
27877
|
const seen = new Set;
|
|
27504
27878
|
const deduped = [];
|
|
27505
27879
|
for (const f of allFindings) {
|
|
@@ -27511,7 +27885,7 @@ async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, mo
|
|
|
27511
27885
|
}
|
|
27512
27886
|
const debateBlocking = deduped.filter((f) => isBlockingSeverity(f.severity));
|
|
27513
27887
|
const durationMs2 = Date.now() - startTime;
|
|
27514
|
-
if (!
|
|
27888
|
+
if (!resolverPassed) {
|
|
27515
27889
|
if (debateBlocking.length > 0) {
|
|
27516
27890
|
logger?.warn("review", `Semantic review failed (debate): ${debateBlocking.length} findings`, {
|
|
27517
27891
|
storyId: story.id,
|
|
@@ -27710,7 +28084,7 @@ ${formatFindings(blockingFindings)}`;
|
|
|
27710
28084
|
cost: llmCost
|
|
27711
28085
|
};
|
|
27712
28086
|
}
|
|
27713
|
-
var _semanticDeps, DIFF_CAP_BYTES = 51200;
|
|
28087
|
+
var _semanticDeps, DIFF_CAP_BYTES = 51200, ALWAYS_EXCLUDED;
|
|
27714
28088
|
var init_semantic = __esm(() => {
|
|
27715
28089
|
init_adapter();
|
|
27716
28090
|
init_config();
|
|
@@ -27724,6 +28098,7 @@ var init_semantic = __esm(() => {
|
|
|
27724
28098
|
createDebateSession: (opts) => new DebateSession(opts),
|
|
27725
28099
|
readAcpSession
|
|
27726
28100
|
};
|
|
28101
|
+
ALWAYS_EXCLUDED = [":!.nax/", ":!.nax-pids"];
|
|
27727
28102
|
});
|
|
27728
28103
|
|
|
27729
28104
|
// src/review/runner.ts
|
|
@@ -27808,7 +28183,7 @@ async function getUncommittedFilesImpl(workdir) {
|
|
|
27808
28183
|
return [];
|
|
27809
28184
|
}
|
|
27810
28185
|
}
|
|
27811
|
-
async function runReview(config2, workdir, executionConfig, qualityCommands, storyId, storyGitRef, story, modelResolver, naxConfig, retrySkipChecks, featureName) {
|
|
28186
|
+
async function runReview(config2, workdir, executionConfig, qualityCommands, storyId, storyGitRef, story, modelResolver, naxConfig, retrySkipChecks, featureName, resolverSession) {
|
|
27812
28187
|
const startTime = Date.now();
|
|
27813
28188
|
const logger = getSafeLogger();
|
|
27814
28189
|
const checks3 = [];
|
|
@@ -27865,10 +28240,19 @@ Stage and commit these files before running review.`
|
|
|
27865
28240
|
modelTier: "balanced",
|
|
27866
28241
|
rules: [],
|
|
27867
28242
|
timeoutMs: 600000,
|
|
27868
|
-
excludePatterns: [
|
|
28243
|
+
excludePatterns: [
|
|
28244
|
+
":!test/",
|
|
28245
|
+
":!tests/",
|
|
28246
|
+
":!*_test.go",
|
|
28247
|
+
":!*.test.ts",
|
|
28248
|
+
":!*.spec.ts",
|
|
28249
|
+
":!**/__tests__/",
|
|
28250
|
+
":!.nax/",
|
|
28251
|
+
":!.nax-pids"
|
|
28252
|
+
]
|
|
27869
28253
|
};
|
|
27870
28254
|
const runSemantic = _reviewSemanticDeps.runSemanticReview;
|
|
27871
|
-
const result2 =
|
|
28255
|
+
const result2 = await runSemantic(workdir, storyGitRef, semanticStory, semanticCfg, modelResolver ?? (() => null), naxConfig, featureName, resolverSession);
|
|
27872
28256
|
checks3.push(result2);
|
|
27873
28257
|
if (!result2.success && !firstFailure) {
|
|
27874
28258
|
firstFailure = `${checkName} failed`;
|
|
@@ -27950,9 +28334,9 @@ async function getChangedFiles(workdir, baseRef) {
|
|
|
27950
28334
|
}
|
|
27951
28335
|
|
|
27952
28336
|
class ReviewOrchestrator {
|
|
27953
|
-
async review(reviewConfig, workdir, executionConfig, plugins, storyGitRef, scopePrefix, qualityCommands, storyId, story, modelResolver, naxConfig, retrySkipChecks, featureName) {
|
|
28337
|
+
async review(reviewConfig, workdir, executionConfig, plugins, storyGitRef, scopePrefix, qualityCommands, storyId, story, modelResolver, naxConfig, retrySkipChecks, featureName, resolverSession) {
|
|
27954
28338
|
const logger = getSafeLogger();
|
|
27955
|
-
const builtIn = await runReview(reviewConfig, workdir, executionConfig, qualityCommands, storyId, storyGitRef, story, modelResolver, naxConfig, retrySkipChecks, featureName);
|
|
28339
|
+
const builtIn = await runReview(reviewConfig, workdir, executionConfig, qualityCommands, storyId, storyGitRef, story, modelResolver, naxConfig, retrySkipChecks, featureName, resolverSession);
|
|
27956
28340
|
if (!builtIn.success) {
|
|
27957
28341
|
return { builtIn, success: false, failureReason: builtIn.failureReason, pluginFailed: false };
|
|
27958
28342
|
}
|
|
@@ -28019,12 +28403,14 @@ class ReviewOrchestrator {
|
|
|
28019
28403
|
const agentResolver = ctx.agentGetFn ?? undefined;
|
|
28020
28404
|
const agentName = ctx.rootConfig.autoMode?.defaultAgent;
|
|
28021
28405
|
const modelResolver = agentName ? (_tier) => agentResolver ? agentResolver(agentName) ?? null : null : undefined;
|
|
28406
|
+
const reviewDebateEnabled = ctx.rootConfig?.debate?.enabled && ctx.rootConfig?.debate?.stages?.review?.enabled;
|
|
28407
|
+
const resolverSession = reviewDebateEnabled ? ctx.reviewerSession : undefined;
|
|
28022
28408
|
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
28409
|
id: ctx.story.id,
|
|
28024
28410
|
title: ctx.story.title,
|
|
28025
28411
|
description: ctx.story.description,
|
|
28026
28412
|
acceptanceCriteria: ctx.story.acceptanceCriteria
|
|
28027
|
-
}, modelResolver, ctx.config, retrySkipChecks, ctx.prd.feature);
|
|
28413
|
+
}, modelResolver, ctx.config, retrySkipChecks, ctx.prd.feature, resolverSession);
|
|
28028
28414
|
}
|
|
28029
28415
|
}
|
|
28030
28416
|
var _orchestratorDeps, reviewOrchestrator;
|
|
@@ -28053,11 +28439,12 @@ var init_review = __esm(() => {
|
|
|
28053
28439
|
enabled: (ctx) => ctx.config.review.enabled,
|
|
28054
28440
|
async execute(ctx) {
|
|
28055
28441
|
const logger = getLogger();
|
|
28442
|
+
const reviewDebateEnabled = ctx.rootConfig?.debate?.enabled && ctx.rootConfig?.debate?.stages?.review?.enabled;
|
|
28056
28443
|
const dialogueEnabled = ctx.config.review?.dialogue?.enabled ?? false;
|
|
28057
28444
|
logger.info("review", "Running review phase", { storyId: ctx.story.id });
|
|
28058
28445
|
const agentResolver = ctx.agentGetFn ?? getAgent;
|
|
28059
28446
|
const agentName = ctx.rootConfig.autoMode?.defaultAgent;
|
|
28060
|
-
if (dialogueEnabled && ctx.reviewerSession) {
|
|
28447
|
+
if (dialogueEnabled && !reviewDebateEnabled && ctx.reviewerSession) {
|
|
28061
28448
|
try {
|
|
28062
28449
|
const diff = ctx.storyGitRef ?? "";
|
|
28063
28450
|
const reReviewResult = await ctx.reviewerSession.reReview(diff);
|
|
@@ -28095,47 +28482,49 @@ var init_review = __esm(() => {
|
|
|
28095
28482
|
if (dialogueEnabled && !ctx.reviewerSession) {
|
|
28096
28483
|
const agent = agentName ? agentResolver(agentName) ?? null : null;
|
|
28097
28484
|
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
|
-
|
|
28485
|
+
if (!reviewDebateEnabled) {
|
|
28486
|
+
const semanticConfig = ctx.config.review?.semantic;
|
|
28487
|
+
if (semanticConfig && agent) {
|
|
28488
|
+
try {
|
|
28489
|
+
const diff = ctx.storyGitRef ?? "";
|
|
28490
|
+
const story = {
|
|
28491
|
+
id: ctx.story.id,
|
|
28492
|
+
title: ctx.story.title,
|
|
28493
|
+
description: ctx.story.description,
|
|
28494
|
+
acceptanceCriteria: ctx.story.acceptanceCriteria
|
|
28495
|
+
};
|
|
28496
|
+
const sessionResult = await ctx.reviewerSession.review(diff, story, semanticConfig);
|
|
28497
|
+
const passed = sessionResult.checkResult.success;
|
|
28498
|
+
ctx.reviewResult = {
|
|
28499
|
+
success: passed,
|
|
28500
|
+
checks: passed ? [] : [
|
|
28501
|
+
{
|
|
28502
|
+
check: "semantic",
|
|
28503
|
+
success: false,
|
|
28504
|
+
command: "reviewer-session-review",
|
|
28505
|
+
exitCode: 1,
|
|
28506
|
+
output: sessionResult.checkResult.findings.map((f) => f.message).join(`
|
|
28119
28507
|
`),
|
|
28120
|
-
|
|
28121
|
-
|
|
28122
|
-
|
|
28123
|
-
|
|
28124
|
-
|
|
28125
|
-
|
|
28126
|
-
|
|
28127
|
-
|
|
28128
|
-
|
|
28129
|
-
|
|
28130
|
-
|
|
28508
|
+
durationMs: 0,
|
|
28509
|
+
findings: sessionResult.checkResult.findings
|
|
28510
|
+
}
|
|
28511
|
+
],
|
|
28512
|
+
totalDurationMs: 0
|
|
28513
|
+
};
|
|
28514
|
+
const dialogueCost = sessionResult.cost ?? 0;
|
|
28515
|
+
if (passed) {
|
|
28516
|
+
logger.info("review", "Review passed (dialogue session)", { storyId: ctx.story.id });
|
|
28517
|
+
} else {
|
|
28518
|
+
logger.warn("review", "Review failed (dialogue session) \u2014 handing off to autofix", {
|
|
28519
|
+
storyId: ctx.story.id
|
|
28520
|
+
});
|
|
28521
|
+
}
|
|
28522
|
+
return { action: "continue", cost: dialogueCost || undefined };
|
|
28523
|
+
} catch (err) {
|
|
28524
|
+
logger.warn("review", "ReviewerSession.review() failed \u2014 falling back to one-shot review", {
|
|
28131
28525
|
storyId: ctx.story.id
|
|
28132
28526
|
});
|
|
28133
28527
|
}
|
|
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
28528
|
}
|
|
28140
28529
|
}
|
|
28141
28530
|
}
|
|
@@ -28239,7 +28628,7 @@ async function runAgentRectification(ctx, lintFixCmd, formatFixCmd, effectiveWor
|
|
|
28239
28628
|
}
|
|
28240
28629
|
const remainingBudget = maxTotal - consumed;
|
|
28241
28630
|
const maxAttempts = Math.min(maxPerCycle, remainingBudget);
|
|
28242
|
-
const agentGetFn = ctx.agentGetFn ?? _autofixDeps.getAgent;
|
|
28631
|
+
const agentGetFn = ctx.agentGetFn ?? ((name) => _autofixDeps.getAgent(name, ctx.rootConfig));
|
|
28243
28632
|
const loopState = {
|
|
28244
28633
|
attempt: 0,
|
|
28245
28634
|
failedChecks
|
|
@@ -28382,7 +28771,7 @@ async function runAgentRectification(ctx, lintFixCmd, formatFixCmd, effectiveWor
|
|
|
28382
28771
|
}
|
|
28383
28772
|
var CLARIFY_REGEX, autofixStage, _autofixDeps;
|
|
28384
28773
|
var init_autofix = __esm(() => {
|
|
28385
|
-
|
|
28774
|
+
init_registry();
|
|
28386
28775
|
init_config();
|
|
28387
28776
|
init_loader();
|
|
28388
28777
|
init_logger2();
|
|
@@ -28458,6 +28847,14 @@ var init_autofix = __esm(() => {
|
|
|
28458
28847
|
const recheckPassed = await _autofixDeps.recheckReview(ctx);
|
|
28459
28848
|
pipelineEventBus.emit({ type: "autofix:completed", storyId: ctx.story.id, fixed: recheckPassed });
|
|
28460
28849
|
if (recheckPassed) {
|
|
28850
|
+
const passedChecks = (ctx.reviewResult?.checks ?? []).filter((c) => c.success).map((c) => c.check);
|
|
28851
|
+
if (passedChecks.length > 0) {
|
|
28852
|
+
ctx.retrySkipChecks = new Set(passedChecks);
|
|
28853
|
+
logger.debug("autofix", "Skipping already-passed checks on retry", {
|
|
28854
|
+
storyId: ctx.story.id,
|
|
28855
|
+
skippedChecks: passedChecks
|
|
28856
|
+
});
|
|
28857
|
+
}
|
|
28461
28858
|
logger.info("autofix", "Mechanical autofix succeeded \u2014 retrying review", { storyId: ctx.story.id });
|
|
28462
28859
|
return { action: "retry", fromStage: "review" };
|
|
28463
28860
|
}
|
|
@@ -28489,7 +28886,7 @@ var init_autofix = __esm(() => {
|
|
|
28489
28886
|
}
|
|
28490
28887
|
};
|
|
28491
28888
|
_autofixDeps = {
|
|
28492
|
-
getAgent,
|
|
28889
|
+
getAgent: (name, config2) => createAgentRegistry(config2).getAgent(name),
|
|
28493
28890
|
runQualityCommand,
|
|
28494
28891
|
recheckReview,
|
|
28495
28892
|
runAgentRectification: (ctx, lintFixCmd, formatFixCmd, effectiveWorkdir) => runAgentRectification(ctx, lintFixCmd, formatFixCmd, effectiveWorkdir),
|
|
@@ -28583,7 +28980,7 @@ var init_completion = __esm(() => {
|
|
|
28583
28980
|
const logger = getLogger();
|
|
28584
28981
|
const isBatch = ctx.stories.length > 1;
|
|
28585
28982
|
const sessionCost = ctx.agentResult?.estimatedCost || 0;
|
|
28586
|
-
const prdPath = ctx.featureDir ? `${ctx.featureDir}/prd.json` : `${ctx.workdir}/nax/features/unknown/prd.json
|
|
28983
|
+
const prdPath = ctx.prdPath ?? (ctx.featureDir ? `${ctx.featureDir}/prd.json` : `${ctx.workdir}/nax/features/unknown/prd.json`);
|
|
28587
28984
|
const storyStartTime = ctx.storyStartTime || new Date().toISOString();
|
|
28588
28985
|
if (isBatch) {
|
|
28589
28986
|
ctx.storyMetrics = collectBatchMetrics(ctx, storyStartTime);
|
|
@@ -28634,7 +29031,7 @@ var init_completion = __esm(() => {
|
|
|
28634
29031
|
};
|
|
28635
29032
|
await _completionDeps.persistSemanticVerdict(ctx.featureDir, ctx.story.id, verdict);
|
|
28636
29033
|
}
|
|
28637
|
-
await savePRD(ctx.prd, prdPath);
|
|
29034
|
+
await _completionDeps.savePRD(ctx.prd, prdPath);
|
|
28638
29035
|
const updatedCounts = countStories(ctx.prd);
|
|
28639
29036
|
logger.info("completion", "Progress update", {
|
|
28640
29037
|
storyId: ctx.story.id,
|
|
@@ -28653,7 +29050,8 @@ var init_completion = __esm(() => {
|
|
|
28653
29050
|
};
|
|
28654
29051
|
_completionDeps = {
|
|
28655
29052
|
checkReviewGate,
|
|
28656
|
-
persistSemanticVerdict
|
|
29053
|
+
persistSemanticVerdict,
|
|
29054
|
+
savePRD
|
|
28657
29055
|
};
|
|
28658
29056
|
});
|
|
28659
29057
|
|
|
@@ -35735,7 +36133,7 @@ var package_default;
|
|
|
35735
36133
|
var init_package = __esm(() => {
|
|
35736
36134
|
package_default = {
|
|
35737
36135
|
name: "@nathapp/nax",
|
|
35738
|
-
version: "0.59.
|
|
36136
|
+
version: "0.59.1",
|
|
35739
36137
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
35740
36138
|
type: "module",
|
|
35741
36139
|
bin: {
|
|
@@ -35815,8 +36213,8 @@ var init_version = __esm(() => {
|
|
|
35815
36213
|
NAX_VERSION = package_default.version;
|
|
35816
36214
|
NAX_COMMIT = (() => {
|
|
35817
36215
|
try {
|
|
35818
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
35819
|
-
return "
|
|
36216
|
+
if (/^[0-9a-f]{6,10}$/.test("b8492d03"))
|
|
36217
|
+
return "b8492d03";
|
|
35820
36218
|
} catch {}
|
|
35821
36219
|
try {
|
|
35822
36220
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -40496,7 +40894,7 @@ function validateStoryCount(counts, config2) {
|
|
|
40496
40894
|
}
|
|
40497
40895
|
function logActiveProtocol(config2) {
|
|
40498
40896
|
const logger = getSafeLogger();
|
|
40499
|
-
const protocol = config2.agent?.protocol
|
|
40897
|
+
const protocol = config2.agent?.protocol;
|
|
40500
40898
|
logger?.info("run-initialization", `Agent protocol: ${protocol}`, { protocol });
|
|
40501
40899
|
}
|
|
40502
40900
|
async function initializeRun(ctx) {
|