@nathapp/nax 0.56.0 → 0.56.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 +221 -77
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -18275,7 +18275,7 @@ var init_schemas3 = __esm(() => {
|
|
|
18275
18275
|
maxIterations: exports_external.number().int().positive({ message: "maxIterations must be > 0" }),
|
|
18276
18276
|
iterationDelayMs: exports_external.number().int().nonnegative(),
|
|
18277
18277
|
costLimit: exports_external.number().positive({ message: "costLimit must be > 0" }),
|
|
18278
|
-
sessionTimeoutSeconds: exports_external.number().int().positive({ message: "sessionTimeoutSeconds must be > 0" }),
|
|
18278
|
+
sessionTimeoutSeconds: exports_external.number().int().positive({ message: "sessionTimeoutSeconds must be > 0" }).default(3600),
|
|
18279
18279
|
verificationTimeoutSeconds: exports_external.number().int().min(1).max(3600).default(300),
|
|
18280
18280
|
maxStoriesPerFeature: exports_external.number().int().positive(),
|
|
18281
18281
|
rectification: RectificationConfigSchema,
|
|
@@ -18472,9 +18472,9 @@ var init_schemas3 = __esm(() => {
|
|
|
18472
18472
|
});
|
|
18473
18473
|
StorySizeGateConfigSchema = exports_external.object({
|
|
18474
18474
|
enabled: exports_external.boolean().default(true),
|
|
18475
|
-
maxAcCount: exports_external.number().int().min(1).max(50).default(
|
|
18476
|
-
maxDescriptionLength: exports_external.number().int().min(100).max(1e4).default(
|
|
18477
|
-
maxBulletPoints: exports_external.number().int().min(1).max(100).default(
|
|
18475
|
+
maxAcCount: exports_external.number().int().min(1).max(50).default(10),
|
|
18476
|
+
maxDescriptionLength: exports_external.number().int().min(100).max(1e4).default(3000),
|
|
18477
|
+
maxBulletPoints: exports_external.number().int().min(1).max(100).default(12),
|
|
18478
18478
|
action: exports_external.enum(["block", "warn", "skip"]).default("block"),
|
|
18479
18479
|
maxReplanAttempts: exports_external.number().int().min(1).default(3)
|
|
18480
18480
|
});
|
|
@@ -22056,7 +22056,7 @@ var package_default;
|
|
|
22056
22056
|
var init_package = __esm(() => {
|
|
22057
22057
|
package_default = {
|
|
22058
22058
|
name: "@nathapp/nax",
|
|
22059
|
-
version: "0.56.
|
|
22059
|
+
version: "0.56.2",
|
|
22060
22060
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
22061
22061
|
type: "module",
|
|
22062
22062
|
bin: {
|
|
@@ -22135,8 +22135,8 @@ var init_version = __esm(() => {
|
|
|
22135
22135
|
NAX_VERSION = package_default.version;
|
|
22136
22136
|
NAX_COMMIT = (() => {
|
|
22137
22137
|
try {
|
|
22138
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
22139
|
-
return "
|
|
22138
|
+
if (/^[0-9a-f]{6,10}$/.test("b590070f"))
|
|
22139
|
+
return "b590070f";
|
|
22140
22140
|
} catch {}
|
|
22141
22141
|
try {
|
|
22142
22142
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -22389,6 +22389,18 @@ var DEFAULT_FALLBACK_AGENT = "claude";
|
|
|
22389
22389
|
var init_resolvers = () => {};
|
|
22390
22390
|
|
|
22391
22391
|
// src/debate/session.ts
|
|
22392
|
+
function resolveDebaterModel(debater, config2) {
|
|
22393
|
+
const tier = debater.model ?? "fast";
|
|
22394
|
+
if (!config2?.models)
|
|
22395
|
+
return debater.model;
|
|
22396
|
+
try {
|
|
22397
|
+
const defaultAgent = config2.autoMode?.defaultAgent ?? debater.agent;
|
|
22398
|
+
const modelDef = resolveModelForAgent(config2.models, debater.agent, tier, defaultAgent);
|
|
22399
|
+
return modelDef.model;
|
|
22400
|
+
} catch {
|
|
22401
|
+
return debater.model;
|
|
22402
|
+
}
|
|
22403
|
+
}
|
|
22392
22404
|
function buildFailedResult(storyId, stage, stageConfig, totalCostUsd = 0) {
|
|
22393
22405
|
return {
|
|
22394
22406
|
storyId,
|
|
@@ -22411,10 +22423,12 @@ class DebateSession {
|
|
|
22411
22423
|
storyId;
|
|
22412
22424
|
stage;
|
|
22413
22425
|
stageConfig;
|
|
22426
|
+
config;
|
|
22414
22427
|
constructor(opts) {
|
|
22415
22428
|
this.storyId = opts.storyId;
|
|
22416
22429
|
this.stage = opts.stage;
|
|
22417
22430
|
this.stageConfig = opts.stageConfig;
|
|
22431
|
+
this.config = opts.config;
|
|
22418
22432
|
}
|
|
22419
22433
|
async run(prompt) {
|
|
22420
22434
|
const sessionMode = this.stageConfig.sessionMode ?? "one-shot";
|
|
@@ -22437,11 +22451,17 @@ class DebateSession {
|
|
|
22437
22451
|
}
|
|
22438
22452
|
resolved.push({ debater, adapter });
|
|
22439
22453
|
}
|
|
22454
|
+
logger?.info("debate", "debate:start", {
|
|
22455
|
+
storyId: this.storyId,
|
|
22456
|
+
stage: this.stage,
|
|
22457
|
+
debaters: resolved.map((r) => r.debater.agent)
|
|
22458
|
+
});
|
|
22440
22459
|
const sessions = [];
|
|
22441
22460
|
try {
|
|
22442
22461
|
for (let i = 0;i < resolved.length; i++) {
|
|
22443
22462
|
const { debater, adapter } = resolved[i];
|
|
22444
|
-
const
|
|
22463
|
+
const resolvedModel = resolveDebaterModel(debater, this.config);
|
|
22464
|
+
const cmdStr = resolvedModel ? `acpx --model ${resolvedModel} ${debater.agent}` : `acpx ${debater.agent}`;
|
|
22445
22465
|
const client = _debateSessionDeps.createSpawnAcpClient(cmdStr);
|
|
22446
22466
|
const sessionName = `nax-debate-${this.storyId}-${i}`;
|
|
22447
22467
|
try {
|
|
@@ -22457,9 +22477,19 @@ class DebateSession {
|
|
|
22457
22477
|
}
|
|
22458
22478
|
if (sessions.length < 2) {
|
|
22459
22479
|
if (sessions.length === 1) {
|
|
22480
|
+
logger?.warn("debate", "debate:fallback", {
|
|
22481
|
+
storyId: this.storyId,
|
|
22482
|
+
stage: this.stage,
|
|
22483
|
+
reason: "only 1 session created"
|
|
22484
|
+
});
|
|
22460
22485
|
const solo = sessions[0];
|
|
22461
22486
|
const response = await solo.session.prompt(prompt);
|
|
22462
22487
|
const output = extractSessionOutput(response);
|
|
22488
|
+
logger?.info("debate", "debate:result", {
|
|
22489
|
+
storyId: this.storyId,
|
|
22490
|
+
stage: this.stage,
|
|
22491
|
+
outcome: "passed"
|
|
22492
|
+
});
|
|
22463
22493
|
return {
|
|
22464
22494
|
storyId: this.storyId,
|
|
22465
22495
|
stage: this.stage,
|
|
@@ -22471,6 +22501,11 @@ class DebateSession {
|
|
|
22471
22501
|
totalCostUsd
|
|
22472
22502
|
};
|
|
22473
22503
|
}
|
|
22504
|
+
logger?.warn("debate", "debate:fallback", {
|
|
22505
|
+
storyId: this.storyId,
|
|
22506
|
+
stage: this.stage,
|
|
22507
|
+
reason: "no sessions created"
|
|
22508
|
+
});
|
|
22474
22509
|
return buildFailedResult(this.storyId, this.stage, config2, totalCostUsd);
|
|
22475
22510
|
}
|
|
22476
22511
|
const proposalSettled = await Promise.allSettled(sessions.map(({ session }) => session.prompt(prompt)));
|
|
@@ -22486,8 +22521,22 @@ class DebateSession {
|
|
|
22486
22521
|
}
|
|
22487
22522
|
}
|
|
22488
22523
|
if (successfulSessions.length < 2) {
|
|
22524
|
+
logger?.warn("debate", "debate:fallback", {
|
|
22525
|
+
storyId: this.storyId,
|
|
22526
|
+
stage: this.stage,
|
|
22527
|
+
reason: "fewer than 2 proposal rounds succeeded"
|
|
22528
|
+
});
|
|
22489
22529
|
return buildFailedResult(this.storyId, this.stage, config2, totalCostUsd);
|
|
22490
22530
|
}
|
|
22531
|
+
for (let i = 0;i < successfulSessions.length; i++) {
|
|
22532
|
+
const s = successfulSessions[i];
|
|
22533
|
+
logger?.info("debate", "debate:proposal", {
|
|
22534
|
+
storyId: this.storyId,
|
|
22535
|
+
stage: this.stage,
|
|
22536
|
+
debaterIndex: i,
|
|
22537
|
+
agent: s.entry.debater.agent
|
|
22538
|
+
});
|
|
22539
|
+
}
|
|
22491
22540
|
let critiqueOutputs = [];
|
|
22492
22541
|
if (config2.rounds > 1) {
|
|
22493
22542
|
const proposalOutputs2 = successfulSessions.map((s) => s.output);
|
|
@@ -22506,6 +22555,11 @@ class DebateSession {
|
|
|
22506
22555
|
debater: s.entry.debater,
|
|
22507
22556
|
output: s.output
|
|
22508
22557
|
}));
|
|
22558
|
+
logger?.info("debate", "debate:result", {
|
|
22559
|
+
storyId: this.storyId,
|
|
22560
|
+
stage: this.stage,
|
|
22561
|
+
outcome
|
|
22562
|
+
});
|
|
22509
22563
|
return {
|
|
22510
22564
|
storyId: this.storyId,
|
|
22511
22565
|
stage: this.stage,
|
|
@@ -22534,16 +22588,40 @@ class DebateSession {
|
|
|
22534
22588
|
}
|
|
22535
22589
|
resolved.push({ debater, adapter });
|
|
22536
22590
|
}
|
|
22537
|
-
|
|
22591
|
+
logger?.info("debate", "debate:start", {
|
|
22592
|
+
storyId: this.storyId,
|
|
22593
|
+
stage: this.stage,
|
|
22594
|
+
debaters: resolved.map((r) => r.debater.agent)
|
|
22595
|
+
});
|
|
22596
|
+
const proposalSettled = await Promise.allSettled(resolved.map(({ debater, adapter }) => adapter.complete(prompt, { model: resolveDebaterModel(debater, this.config) }).then((output) => ({ debater, adapter, output, cost: 0 }))));
|
|
22538
22597
|
const successful = proposalSettled.filter((r) => r.status === "fulfilled").map((r) => r.value);
|
|
22539
22598
|
for (const r of proposalSettled) {
|
|
22540
22599
|
if (r.status === "fulfilled") {
|
|
22541
22600
|
totalCostUsd += r.value.cost;
|
|
22542
22601
|
}
|
|
22543
22602
|
}
|
|
22603
|
+
for (let i = 0;i < successful.length; i++) {
|
|
22604
|
+
logger?.info("debate", "debate:proposal", {
|
|
22605
|
+
storyId: this.storyId,
|
|
22606
|
+
stage: this.stage,
|
|
22607
|
+
debaterIndex: i,
|
|
22608
|
+
agent: successful[i].debater.agent,
|
|
22609
|
+
model: resolveDebaterModel(successful[i].debater, this.config)
|
|
22610
|
+
});
|
|
22611
|
+
}
|
|
22544
22612
|
if (successful.length < 2) {
|
|
22545
22613
|
if (successful.length === 1) {
|
|
22614
|
+
logger?.warn("debate", "debate:fallback", {
|
|
22615
|
+
storyId: this.storyId,
|
|
22616
|
+
stage: this.stage,
|
|
22617
|
+
reason: "only 1 debater succeeded"
|
|
22618
|
+
});
|
|
22546
22619
|
const solo = successful[0];
|
|
22620
|
+
logger?.info("debate", "debate:result", {
|
|
22621
|
+
storyId: this.storyId,
|
|
22622
|
+
stage: this.stage,
|
|
22623
|
+
outcome: "passed"
|
|
22624
|
+
});
|
|
22547
22625
|
return {
|
|
22548
22626
|
storyId: this.storyId,
|
|
22549
22627
|
stage: this.stage,
|
|
@@ -22557,8 +22635,20 @@ class DebateSession {
|
|
|
22557
22635
|
}
|
|
22558
22636
|
if (resolved.length > 0) {
|
|
22559
22637
|
const { adapter: fallbackAdapter, debater: fallbackDebater } = resolved[0];
|
|
22638
|
+
logger?.warn("debate", "debate:fallback", {
|
|
22639
|
+
storyId: this.storyId,
|
|
22640
|
+
stage: this.stage,
|
|
22641
|
+
reason: "all debaters failed \u2014 retrying with first adapter"
|
|
22642
|
+
});
|
|
22560
22643
|
try {
|
|
22561
|
-
const fallbackOutput = await fallbackAdapter.complete(prompt, {
|
|
22644
|
+
const fallbackOutput = await fallbackAdapter.complete(prompt, {
|
|
22645
|
+
model: resolveDebaterModel(fallbackDebater, this.config)
|
|
22646
|
+
});
|
|
22647
|
+
logger?.info("debate", "debate:result", {
|
|
22648
|
+
storyId: this.storyId,
|
|
22649
|
+
stage: this.stage,
|
|
22650
|
+
outcome: "passed"
|
|
22651
|
+
});
|
|
22562
22652
|
return {
|
|
22563
22653
|
storyId: this.storyId,
|
|
22564
22654
|
stage: this.stage,
|
|
@@ -22577,7 +22667,7 @@ class DebateSession {
|
|
|
22577
22667
|
if (config2.rounds > 1) {
|
|
22578
22668
|
const proposalOutputs2 = successful.map((p) => p.output);
|
|
22579
22669
|
const critiqueSettled = await Promise.allSettled(successful.map(({ debater, adapter }, i) => adapter.complete(buildCritiquePrompt(prompt, proposalOutputs2, i), {
|
|
22580
|
-
model: debater.
|
|
22670
|
+
model: resolveDebaterModel(debater, this.config)
|
|
22581
22671
|
})));
|
|
22582
22672
|
for (const r of critiqueSettled) {
|
|
22583
22673
|
if (r.status === "fulfilled") {
|
|
@@ -22593,6 +22683,11 @@ class DebateSession {
|
|
|
22593
22683
|
debater: p.debater,
|
|
22594
22684
|
output: p.output
|
|
22595
22685
|
}));
|
|
22686
|
+
logger?.info("debate", "debate:result", {
|
|
22687
|
+
storyId: this.storyId,
|
|
22688
|
+
stage: this.stage,
|
|
22689
|
+
outcome
|
|
22690
|
+
});
|
|
22596
22691
|
return {
|
|
22597
22692
|
storyId: this.storyId,
|
|
22598
22693
|
stage: this.stage,
|
|
@@ -22604,7 +22699,7 @@ class DebateSession {
|
|
|
22604
22699
|
totalCostUsd
|
|
22605
22700
|
};
|
|
22606
22701
|
}
|
|
22607
|
-
async resolve(proposalOutputs, critiqueOutputs,
|
|
22702
|
+
async resolve(proposalOutputs, critiqueOutputs, _successful) {
|
|
22608
22703
|
const resolverConfig = this.stageConfig.resolver;
|
|
22609
22704
|
if (resolverConfig.type === "majority-fail-closed" || resolverConfig.type === "majority-fail-open") {
|
|
22610
22705
|
return majorityResolver(proposalOutputs, resolverConfig.type === "majority-fail-open");
|
|
@@ -22631,6 +22726,7 @@ var RESOLVER_FALLBACK_AGENT = "synthesis", _debateSessionDeps;
|
|
|
22631
22726
|
var init_session = __esm(() => {
|
|
22632
22727
|
init_spawn_client();
|
|
22633
22728
|
init_registry();
|
|
22729
|
+
init_config();
|
|
22634
22730
|
init_logger2();
|
|
22635
22731
|
init_resolvers();
|
|
22636
22732
|
_debateSessionDeps = {
|
|
@@ -23500,6 +23596,11 @@ class WebhookInteractionPlugin {
|
|
|
23500
23596
|
serverStartPromise = null;
|
|
23501
23597
|
pendingResponses = new Map;
|
|
23502
23598
|
receiveCallbacks = new Map;
|
|
23599
|
+
get callbackServerPort() {
|
|
23600
|
+
if (!this.server)
|
|
23601
|
+
return null;
|
|
23602
|
+
return this.server.port;
|
|
23603
|
+
}
|
|
23503
23604
|
async init(config2) {
|
|
23504
23605
|
const cfg = WebhookConfigSchema.parse(config2);
|
|
23505
23606
|
this.config = {
|
|
@@ -23675,7 +23776,9 @@ var init_webhook = __esm(() => {
|
|
|
23675
23776
|
init_bun_deps();
|
|
23676
23777
|
WebhookConfigSchema = exports_external.object({
|
|
23677
23778
|
url: exports_external.string().url().optional(),
|
|
23678
|
-
callbackPort: exports_external.number().int().
|
|
23779
|
+
callbackPort: exports_external.number().int().refine((p) => p === 0 || p >= 1024 && p <= 65535, {
|
|
23780
|
+
message: "Port must be 0 (auto-assign) or between 1024 and 65535"
|
|
23781
|
+
}).optional(),
|
|
23679
23782
|
secret: exports_external.string().optional(),
|
|
23680
23783
|
maxPayloadBytes: exports_external.number().int().positive().optional()
|
|
23681
23784
|
});
|
|
@@ -26269,7 +26372,8 @@ async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, mo
|
|
|
26269
26372
|
const debateSession = _semanticDeps.createDebateSession({
|
|
26270
26373
|
storyId: story.id,
|
|
26271
26374
|
stage: "review",
|
|
26272
|
-
stageConfig: reviewStageConfig
|
|
26375
|
+
stageConfig: reviewStageConfig,
|
|
26376
|
+
config: naxConfig
|
|
26273
26377
|
});
|
|
26274
26378
|
const debateResult = await debateSession.run(prompt);
|
|
26275
26379
|
let passCount = 0;
|
|
@@ -28788,11 +28892,26 @@ function shouldRetryRectification(state, config2) {
|
|
|
28788
28892
|
return true;
|
|
28789
28893
|
}
|
|
28790
28894
|
function buildEscalationPreamble(attempt, config2) {
|
|
28895
|
+
const logger = getSafeLogger();
|
|
28791
28896
|
const rethinkAt = Math.min(config2.rethinkAtAttempt ?? 2, config2.maxRetries);
|
|
28792
28897
|
const urgencyAt = Math.min(config2.urgencyAtAttempt ?? 3, config2.maxRetries);
|
|
28793
28898
|
if (attempt < rethinkAt)
|
|
28794
28899
|
return "";
|
|
28795
28900
|
const isUrgent = attempt >= urgencyAt;
|
|
28901
|
+
if (isUrgent) {
|
|
28902
|
+
logger?.info("rectification", "Progressive prompt escalation: urgency + rethink injected", {
|
|
28903
|
+
attempt,
|
|
28904
|
+
urgencyAtAttempt: urgencyAt,
|
|
28905
|
+
rethinkAtAttempt: rethinkAt,
|
|
28906
|
+
maxRetries: config2.maxRetries
|
|
28907
|
+
});
|
|
28908
|
+
} else {
|
|
28909
|
+
logger?.info("rectification", "Progressive prompt escalation: rethink injected", {
|
|
28910
|
+
attempt,
|
|
28911
|
+
rethinkAtAttempt: rethinkAt,
|
|
28912
|
+
maxRetries: config2.maxRetries
|
|
28913
|
+
});
|
|
28914
|
+
}
|
|
28796
28915
|
const rethinkSection = `## \u26A0\uFE0F Previous Attempt Did Not Fix the Failures
|
|
28797
28916
|
|
|
28798
28917
|
Your previous fix attempt (attempt ${attempt}) did not resolve all failures. **Step back and reconsider your approach.**
|
|
@@ -28934,7 +29053,9 @@ ${testCommands}
|
|
|
28934
29053
|
- When running tests, run ONLY the failing test files shown above \u2014 NEVER run \`bun test\` without a file filter.
|
|
28935
29054
|
`;
|
|
28936
29055
|
}
|
|
28937
|
-
var init_rectification = () => {
|
|
29056
|
+
var init_rectification = __esm(() => {
|
|
29057
|
+
init_logger2();
|
|
29058
|
+
});
|
|
28938
29059
|
|
|
28939
29060
|
// src/verification/index.ts
|
|
28940
29061
|
var init_verification = __esm(() => {
|
|
@@ -30552,6 +30673,10 @@ Category: ${tddResult.failureCategory ?? "unknown"}`,
|
|
|
30552
30673
|
});
|
|
30553
30674
|
}
|
|
30554
30675
|
}
|
|
30676
|
+
return {
|
|
30677
|
+
action: "pause",
|
|
30678
|
+
reason: tddResult.reviewReason || `Human review needed: ${tddResult.failureCategory ?? "unknown"}`
|
|
30679
|
+
};
|
|
30555
30680
|
}
|
|
30556
30681
|
return routeTddFailure(tddResult.failureCategory, isLiteMode, ctx, tddResult.reviewReason);
|
|
30557
30682
|
}
|
|
@@ -69425,78 +69550,75 @@ async function planCommand(workdir, config2, options) {
|
|
|
69425
69550
|
autoModel = resolveModelForAgent2(config2.models, defaultAgent, planTier, defaultAgent).model;
|
|
69426
69551
|
}
|
|
69427
69552
|
} catch {}
|
|
69428
|
-
|
|
69429
|
-
|
|
69430
|
-
|
|
69431
|
-
|
|
69432
|
-
|
|
69433
|
-
feature: options.feature,
|
|
69434
|
-
timeoutSeconds
|
|
69435
|
-
});
|
|
69436
|
-
const pidRegistry = new PidRegistry(workdir);
|
|
69437
|
-
try {
|
|
69438
|
-
await adapter.plan({
|
|
69439
|
-
prompt,
|
|
69553
|
+
const runSingleAgentPlan = async () => {
|
|
69554
|
+
if (isAcp) {
|
|
69555
|
+
logger?.info("plan", "Starting ACP auto planning session", {
|
|
69556
|
+
agent: agentName,
|
|
69557
|
+
model: autoModel ?? config2?.plan?.model ?? "balanced",
|
|
69440
69558
|
workdir,
|
|
69441
|
-
|
|
69442
|
-
timeoutSeconds
|
|
69443
|
-
config: config2,
|
|
69444
|
-
modelTier: config2?.plan?.model ?? "balanced",
|
|
69445
|
-
dangerouslySkipPermissions: resolvePermissions(config2, "plan").skipPermissions,
|
|
69446
|
-
maxInteractionTurns: config2?.agent?.maxInteractionTurns,
|
|
69447
|
-
featureName: options.feature,
|
|
69448
|
-
pidRegistry,
|
|
69449
|
-
sessionRole: "plan"
|
|
69450
|
-
});
|
|
69451
|
-
} finally {
|
|
69452
|
-
await pidRegistry.killAll().catch(() => {});
|
|
69453
|
-
}
|
|
69454
|
-
if (!_planDeps.existsSync(outputPath)) {
|
|
69455
|
-
throw new Error(`[plan] ACP agent did not write PRD to ${outputPath}. Check agent logs for errors.`);
|
|
69456
|
-
}
|
|
69457
|
-
rawResponse = await _planDeps.readFile(outputPath);
|
|
69458
|
-
} else {
|
|
69459
|
-
const debateEnabled = config2?.debate?.enabled && config2?.debate?.stages?.plan?.enabled;
|
|
69460
|
-
if (debateEnabled) {
|
|
69461
|
-
const planStageConfig = config2.debate?.stages.plan;
|
|
69462
|
-
const debateSession = _planDeps.createDebateSession({
|
|
69463
|
-
storyId: options.feature,
|
|
69464
|
-
stage: "plan",
|
|
69465
|
-
stageConfig: planStageConfig
|
|
69559
|
+
feature: options.feature,
|
|
69560
|
+
timeoutSeconds
|
|
69466
69561
|
});
|
|
69467
|
-
const
|
|
69468
|
-
|
|
69469
|
-
|
|
69470
|
-
|
|
69471
|
-
logger?.warn("debate", "All debaters failed \u2014 falling back to single agent", {
|
|
69472
|
-
stage: "debate",
|
|
69473
|
-
event: "fallback"
|
|
69474
|
-
});
|
|
69475
|
-
rawResponse = await adapter.complete(prompt, {
|
|
69476
|
-
model: autoModel,
|
|
69477
|
-
jsonMode: true,
|
|
69562
|
+
const pidRegistry = new PidRegistry(workdir);
|
|
69563
|
+
try {
|
|
69564
|
+
await adapter.plan({
|
|
69565
|
+
prompt,
|
|
69478
69566
|
workdir,
|
|
69567
|
+
interactive: false,
|
|
69568
|
+
timeoutSeconds,
|
|
69479
69569
|
config: config2,
|
|
69570
|
+
modelTier: config2?.plan?.model ?? "balanced",
|
|
69571
|
+
dangerouslySkipPermissions: resolvePermissions(config2, "plan").skipPermissions,
|
|
69572
|
+
maxInteractionTurns: config2?.agent?.maxInteractionTurns,
|
|
69480
69573
|
featureName: options.feature,
|
|
69574
|
+
pidRegistry,
|
|
69481
69575
|
sessionRole: "plan"
|
|
69482
69576
|
});
|
|
69577
|
+
} finally {
|
|
69578
|
+
await pidRegistry.killAll().catch(() => {});
|
|
69483
69579
|
}
|
|
69484
|
-
|
|
69485
|
-
|
|
69486
|
-
|
|
69487
|
-
|
|
69488
|
-
workdir,
|
|
69489
|
-
config: config2,
|
|
69490
|
-
featureName: options.feature,
|
|
69491
|
-
sessionRole: "plan"
|
|
69492
|
-
});
|
|
69580
|
+
if (!_planDeps.existsSync(outputPath)) {
|
|
69581
|
+
throw new Error(`[plan] ACP agent did not write PRD to ${outputPath}. Check agent logs for errors.`);
|
|
69582
|
+
}
|
|
69583
|
+
return await _planDeps.readFile(outputPath);
|
|
69493
69584
|
}
|
|
69585
|
+
let result = await adapter.complete(prompt, {
|
|
69586
|
+
model: autoModel,
|
|
69587
|
+
jsonMode: true,
|
|
69588
|
+
workdir,
|
|
69589
|
+
config: config2,
|
|
69590
|
+
featureName: options.feature,
|
|
69591
|
+
sessionRole: "plan"
|
|
69592
|
+
});
|
|
69494
69593
|
try {
|
|
69495
|
-
const envelope = JSON.parse(
|
|
69594
|
+
const envelope = JSON.parse(result);
|
|
69496
69595
|
if (envelope?.type === "result" && typeof envelope?.result === "string") {
|
|
69497
|
-
|
|
69596
|
+
result = envelope.result;
|
|
69498
69597
|
}
|
|
69499
69598
|
} catch {}
|
|
69599
|
+
return result;
|
|
69600
|
+
};
|
|
69601
|
+
const debateEnabled = config2?.debate?.enabled && config2?.debate?.stages?.plan?.enabled;
|
|
69602
|
+
if (debateEnabled) {
|
|
69603
|
+
const planStageConfig = config2?.debate?.stages.plan;
|
|
69604
|
+
const debateSession = _planDeps.createDebateSession({
|
|
69605
|
+
storyId: options.feature,
|
|
69606
|
+
stage: "plan",
|
|
69607
|
+
stageConfig: planStageConfig,
|
|
69608
|
+
config: config2
|
|
69609
|
+
});
|
|
69610
|
+
const debateResult = await debateSession.run(prompt);
|
|
69611
|
+
if (debateResult.outcome !== "failed" && debateResult.output) {
|
|
69612
|
+
rawResponse = debateResult.output;
|
|
69613
|
+
} else {
|
|
69614
|
+
logger?.warn("debate", "All debaters failed \u2014 falling back to single agent", {
|
|
69615
|
+
stage: "debate",
|
|
69616
|
+
event: "fallback"
|
|
69617
|
+
});
|
|
69618
|
+
rawResponse = await runSingleAgentPlan();
|
|
69619
|
+
}
|
|
69620
|
+
} else {
|
|
69621
|
+
rawResponse = await runSingleAgentPlan();
|
|
69500
69622
|
}
|
|
69501
69623
|
} else {
|
|
69502
69624
|
const prompt = buildPlanningPrompt(specContent, codebaseContext, outputPath, relativePackages, packageDetails, config2?.project);
|
|
@@ -69868,12 +69990,34 @@ async function planDecomposeCommand(workdir, config2, options) {
|
|
|
69868
69990
|
if (debateResult.outcome !== "failed" && debateResult.output) {
|
|
69869
69991
|
rawResponse = debateResult.output;
|
|
69870
69992
|
} else {
|
|
69871
|
-
rawResponse = await adapter.complete(prompt, {
|
|
69993
|
+
rawResponse = await adapter.complete(prompt, {
|
|
69994
|
+
jsonMode: true,
|
|
69995
|
+
workdir,
|
|
69996
|
+
sessionRole: "decompose",
|
|
69997
|
+
featureName: options.feature,
|
|
69998
|
+
storyId: options.storyId
|
|
69999
|
+
});
|
|
69872
70000
|
}
|
|
69873
70001
|
} else {
|
|
69874
|
-
rawResponse = await adapter.complete(prompt, {
|
|
70002
|
+
rawResponse = await adapter.complete(prompt, {
|
|
70003
|
+
jsonMode: true,
|
|
70004
|
+
workdir,
|
|
70005
|
+
sessionRole: "decompose",
|
|
70006
|
+
featureName: options.feature,
|
|
70007
|
+
storyId: options.storyId
|
|
70008
|
+
});
|
|
70009
|
+
}
|
|
70010
|
+
const jsonMatch = rawResponse.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
70011
|
+
const cleanedResponse = jsonMatch ? jsonMatch[1] : rawResponse;
|
|
70012
|
+
let parsed;
|
|
70013
|
+
try {
|
|
70014
|
+
parsed = JSON.parse(cleanedResponse.trim());
|
|
70015
|
+
} catch (err) {
|
|
70016
|
+
throw new NaxError(`Failed to parse decompose response as JSON: ${err.message}
|
|
70017
|
+
|
|
70018
|
+
Response (first 500 chars):
|
|
70019
|
+
${rawResponse.slice(0, 500)}`, "DECOMPOSE_PARSE_FAILED", { stage: "decompose", storyId: options.storyId });
|
|
69875
70020
|
}
|
|
69876
|
-
const parsed = JSON.parse(rawResponse);
|
|
69877
70021
|
const subStories = parsed.subStories;
|
|
69878
70022
|
const maxAcCount = config2?.precheck?.storySizeGate?.maxAcCount ?? Number.POSITIVE_INFINITY;
|
|
69879
70023
|
for (const sub of subStories) {
|