@nathapp/nax 0.61.1 → 0.61.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 +152 -108
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -2634,17 +2634,6 @@ function formatDuration(durationMs) {
|
|
|
2634
2634
|
function formatCost(cost) {
|
|
2635
2635
|
return `$${cost.toFixed(4)}`;
|
|
2636
2636
|
}
|
|
2637
|
-
function getStageEmoji(stage) {
|
|
2638
|
-
if (stage.includes("routing"))
|
|
2639
|
-
return EMOJI.routing;
|
|
2640
|
-
if (stage.includes("execution") || stage.includes("agent"))
|
|
2641
|
-
return EMOJI.execution;
|
|
2642
|
-
if (stage.includes("review"))
|
|
2643
|
-
return EMOJI.review;
|
|
2644
|
-
if (stage.includes("tdd"))
|
|
2645
|
-
return EMOJI.tdd;
|
|
2646
|
-
return EMOJI.info;
|
|
2647
|
-
}
|
|
2648
2637
|
function shouldDisplay(entry, mode) {
|
|
2649
2638
|
if (mode === "json")
|
|
2650
2639
|
return true;
|
|
@@ -2685,7 +2674,7 @@ function formatLogEntry(entry, options) {
|
|
|
2685
2674
|
}
|
|
2686
2675
|
return formatDefault(entry, colorize, timestamp, mode);
|
|
2687
2676
|
}
|
|
2688
|
-
function formatRunStart(entry, c, timestamp,
|
|
2677
|
+
function formatRunStart(entry, c, timestamp, _mode) {
|
|
2689
2678
|
const data = entry.data;
|
|
2690
2679
|
const lines = [];
|
|
2691
2680
|
lines.push("");
|
|
@@ -2704,7 +2693,7 @@ function formatRunStart(entry, c, timestamp, mode) {
|
|
|
2704
2693
|
shouldDisplay: true
|
|
2705
2694
|
};
|
|
2706
2695
|
}
|
|
2707
|
-
function formatStoryStart(entry, c,
|
|
2696
|
+
function formatStoryStart(entry, c, _timestamp, mode) {
|
|
2708
2697
|
const data = entry.data;
|
|
2709
2698
|
const storyId = String(data.storyId || entry.storyId || "unknown");
|
|
2710
2699
|
const title = String(data.storyTitle || data.title || "Untitled story");
|
|
@@ -2734,7 +2723,7 @@ function formatStoryStart(entry, c, timestamp, mode) {
|
|
|
2734
2723
|
shouldDisplay: true
|
|
2735
2724
|
};
|
|
2736
2725
|
}
|
|
2737
|
-
function formatStoryComplete(entry, c,
|
|
2726
|
+
function formatStoryComplete(entry, c, _timestamp, mode) {
|
|
2738
2727
|
const data = entry.data;
|
|
2739
2728
|
const storyId = String(data.storyId || entry.storyId || "unknown");
|
|
2740
2729
|
const success = data.success ?? true;
|
|
@@ -2766,7 +2755,7 @@ function formatStoryComplete(entry, c, timestamp, mode) {
|
|
|
2766
2755
|
shouldDisplay: true
|
|
2767
2756
|
};
|
|
2768
2757
|
}
|
|
2769
|
-
function formatTDDSession(entry, c,
|
|
2758
|
+
function formatTDDSession(entry, c, _timestamp, mode) {
|
|
2770
2759
|
if (mode === "quiet") {
|
|
2771
2760
|
return { output: "", shouldDisplay: false };
|
|
2772
2761
|
}
|
|
@@ -2781,7 +2770,6 @@ function formatTDDSession(entry, c, timestamp, mode) {
|
|
|
2781
2770
|
function formatDefault(entry, c, timestamp, mode) {
|
|
2782
2771
|
const levelEmoji = entry.level === "error" ? EMOJI.failure : entry.level === "warn" ? EMOJI.warning : EMOJI.info;
|
|
2783
2772
|
const levelColor = entry.level === "error" ? c.red : entry.level === "warn" ? c.yellow : c.gray;
|
|
2784
|
-
const stageEmoji = getStageEmoji(entry.stage);
|
|
2785
2773
|
const parts = [c.gray(`[${timestamp}]`), levelColor(`${levelEmoji} ${entry.stage}`)];
|
|
2786
2774
|
if (entry.storyId) {
|
|
2787
2775
|
parts.push(c.dim(`[${entry.storyId}]`));
|
|
@@ -3626,7 +3614,14 @@ var init_env = __esm(() => {
|
|
|
3626
3614
|
|
|
3627
3615
|
// src/agents/acp/parser.ts
|
|
3628
3616
|
function createParseState() {
|
|
3629
|
-
return {
|
|
3617
|
+
return {
|
|
3618
|
+
text: "",
|
|
3619
|
+
tokenUsage: undefined,
|
|
3620
|
+
exactCostUsd: undefined,
|
|
3621
|
+
stopReason: undefined,
|
|
3622
|
+
error: undefined,
|
|
3623
|
+
retryable: false
|
|
3624
|
+
};
|
|
3630
3625
|
}
|
|
3631
3626
|
function parseAcpxJsonLine(line, state) {
|
|
3632
3627
|
try {
|
|
@@ -3657,6 +3652,20 @@ function parseAcpxJsonLine(line, state) {
|
|
|
3657
3652
|
};
|
|
3658
3653
|
}
|
|
3659
3654
|
}
|
|
3655
|
+
if (event.error && typeof event.error === "object") {
|
|
3656
|
+
const err = event.error;
|
|
3657
|
+
let errorMsg = typeof err.message === "string" ? err.message : JSON.stringify(event.error);
|
|
3658
|
+
if (err.data && typeof err.data === "object") {
|
|
3659
|
+
const data = err.data;
|
|
3660
|
+
const suffix = [data.acpxCode, data.detailCode].filter(Boolean).join("/");
|
|
3661
|
+
if (suffix)
|
|
3662
|
+
errorMsg = `${errorMsg} [${suffix}]`;
|
|
3663
|
+
if (!state.error && data.retryable === true)
|
|
3664
|
+
state.retryable = true;
|
|
3665
|
+
}
|
|
3666
|
+
if (!state.error)
|
|
3667
|
+
state.error = errorMsg;
|
|
3668
|
+
}
|
|
3660
3669
|
return;
|
|
3661
3670
|
}
|
|
3662
3671
|
if (event.content && typeof event.content === "string")
|
|
@@ -3691,7 +3700,8 @@ function finalizeParseState(state) {
|
|
|
3691
3700
|
tokenUsage: state.tokenUsage,
|
|
3692
3701
|
exactCostUsd: state.exactCostUsd,
|
|
3693
3702
|
stopReason: state.stopReason,
|
|
3694
|
-
error: state.error
|
|
3703
|
+
error: state.error,
|
|
3704
|
+
retryable: state.retryable
|
|
3695
3705
|
};
|
|
3696
3706
|
}
|
|
3697
3707
|
|
|
@@ -3807,13 +3817,17 @@ class SpawnAcpSession {
|
|
|
3807
3817
|
Promise.race([stderrPromise, drainB.promise]).finally(() => drainB.cancel())
|
|
3808
3818
|
]);
|
|
3809
3819
|
if (exitCode !== 0) {
|
|
3820
|
+
const parsedOnError = finalizeParseState(parseState);
|
|
3821
|
+
const errorContent = parsedOnError.error || stderr || `Exit code ${exitCode}`;
|
|
3810
3822
|
getSafeLogger()?.warn("acp-adapter", `Session prompt exited with code ${exitCode}`, {
|
|
3811
3823
|
exitCode,
|
|
3812
|
-
|
|
3824
|
+
error: errorContent.slice(0, 500),
|
|
3825
|
+
...stderr && stderr !== errorContent ? { banner: stderr.trim().slice(0, 200) } : {}
|
|
3813
3826
|
});
|
|
3814
3827
|
return {
|
|
3815
|
-
messages: [{ role: "assistant", content:
|
|
3816
|
-
stopReason: "error"
|
|
3828
|
+
messages: [{ role: "assistant", content: errorContent }],
|
|
3829
|
+
stopReason: "error",
|
|
3830
|
+
retryable: parsedOnError.retryable
|
|
3817
3831
|
};
|
|
3818
3832
|
}
|
|
3819
3833
|
try {
|
|
@@ -3889,7 +3903,6 @@ class SpawnAcpSession {
|
|
|
3889
3903
|
}
|
|
3890
3904
|
|
|
3891
3905
|
class SpawnAcpClient {
|
|
3892
|
-
agentName;
|
|
3893
3906
|
model;
|
|
3894
3907
|
cwd;
|
|
3895
3908
|
timeoutSeconds;
|
|
@@ -3903,7 +3916,6 @@ class SpawnAcpClient {
|
|
|
3903
3916
|
if (!lastToken || lastToken.startsWith("-")) {
|
|
3904
3917
|
throw new Error(`[acp-adapter] Could not parse agentName from cmdStr: "${cmdStr}"`);
|
|
3905
3918
|
}
|
|
3906
|
-
this.agentName = lastToken;
|
|
3907
3919
|
this.cwd = cwd || process.cwd();
|
|
3908
3920
|
this.timeoutSeconds = timeoutSeconds || 1800;
|
|
3909
3921
|
this.env = buildAllowedEnv();
|
|
@@ -4117,17 +4129,6 @@ function estimateCostByDuration(modelTier, durationMs) {
|
|
|
4117
4129
|
confidence: "fallback"
|
|
4118
4130
|
};
|
|
4119
4131
|
}
|
|
4120
|
-
function formatCostWithConfidence(estimate) {
|
|
4121
|
-
const formattedCost = `$${estimate.cost.toFixed(2)}`;
|
|
4122
|
-
switch (estimate.confidence) {
|
|
4123
|
-
case "exact":
|
|
4124
|
-
return formattedCost;
|
|
4125
|
-
case "estimated":
|
|
4126
|
-
return `~${formattedCost}`;
|
|
4127
|
-
case "fallback":
|
|
4128
|
-
return `~${formattedCost} (duration-based)`;
|
|
4129
|
-
}
|
|
4130
|
-
}
|
|
4131
4132
|
function estimateCostFromTokenUsage(usage, model) {
|
|
4132
4133
|
const pricing = MODEL_PRICING[model];
|
|
4133
4134
|
if (!pricing) {
|
|
@@ -18162,6 +18163,8 @@ var init_schemas3 = __esm(() => {
|
|
|
18162
18163
|
iterationDelayMs: exports_external.number().int().nonnegative(),
|
|
18163
18164
|
costLimit: exports_external.number().positive({ message: "costLimit must be > 0" }),
|
|
18164
18165
|
sessionTimeoutSeconds: exports_external.number().int().positive({ message: "sessionTimeoutSeconds must be > 0" }).default(3600),
|
|
18166
|
+
sessionErrorMaxRetries: exports_external.number().int().min(0).max(5).default(1),
|
|
18167
|
+
sessionErrorRetryableMaxRetries: exports_external.number().int().min(0).max(10).default(3),
|
|
18165
18168
|
verificationTimeoutSeconds: exports_external.number().int().min(1).max(3600).default(300),
|
|
18166
18169
|
maxStoriesPerFeature: exports_external.number().int().positive(),
|
|
18167
18170
|
rectification: RectificationConfigSchema,
|
|
@@ -19080,19 +19083,28 @@ class AcpAgentAdapter {
|
|
|
19080
19083
|
});
|
|
19081
19084
|
let currentAgent = this.resolveCurrentAgent(config2);
|
|
19082
19085
|
const rateLimitedRetryAfter = new Map;
|
|
19083
|
-
let
|
|
19086
|
+
let sessionErrorRetries = 0;
|
|
19087
|
+
const SESSION_ERROR_MAX_RETRIES = config2?.execution?.sessionErrorMaxRetries ?? 1;
|
|
19088
|
+
const SESSION_ERROR_RETRYABLE_MAX_RETRIES = config2?.execution?.sessionErrorRetryableMaxRetries ?? 3;
|
|
19084
19089
|
let legacyAttempt = 0;
|
|
19085
19090
|
let retryCount = 0;
|
|
19086
19091
|
while (true) {
|
|
19087
19092
|
try {
|
|
19088
19093
|
const result = await this._runWithClient(options, startTime, currentAgent);
|
|
19089
19094
|
if (!result.success) {
|
|
19090
|
-
getSafeLogger()?.warn("acp-adapter", `Run failed for ${currentAgent}`, {
|
|
19091
|
-
|
|
19092
|
-
|
|
19095
|
+
getSafeLogger()?.warn("acp-adapter", `Run failed for ${currentAgent}`, {
|
|
19096
|
+
exitCode: result.exitCode,
|
|
19097
|
+
...result.output ? { output: result.output.slice(0, 500) } : {}
|
|
19098
|
+
});
|
|
19099
|
+
const maxSessionRetries = result.sessionErrorRetryable ? SESSION_ERROR_RETRYABLE_MAX_RETRIES : SESSION_ERROR_MAX_RETRIES;
|
|
19100
|
+
if (result.sessionError && _acpAdapterDeps.shouldRetrySessionError && sessionErrorRetries < maxSessionRetries) {
|
|
19101
|
+
sessionErrorRetries += 1;
|
|
19093
19102
|
getSafeLogger()?.warn("acp-adapter", "Session error \u2014 retrying with fresh session", {
|
|
19094
19103
|
storyId: options.storyId,
|
|
19095
|
-
featureName: options.featureName
|
|
19104
|
+
featureName: options.featureName,
|
|
19105
|
+
retryable: result.sessionErrorRetryable,
|
|
19106
|
+
attempt: sessionErrorRetries,
|
|
19107
|
+
maxAttempts: maxSessionRetries
|
|
19096
19108
|
});
|
|
19097
19109
|
if (options.featureName && options.storyId) {
|
|
19098
19110
|
await clearAcpSession(options.workdir, options.featureName, options.storyId, options.sessionRole);
|
|
@@ -19311,6 +19323,7 @@ class AcpAgentAdapter {
|
|
|
19311
19323
|
}
|
|
19312
19324
|
const success2 = lastResponse?.stopReason === "end_turn";
|
|
19313
19325
|
const isSessionError = lastResponse?.stopReason === "error";
|
|
19326
|
+
const isSessionErrorRetryable = isSessionError && lastResponse?.retryable === true;
|
|
19314
19327
|
const output = extractOutput(lastResponse);
|
|
19315
19328
|
const estimatedCost = totalExactCostUsd ?? (totalTokenUsage.input_tokens > 0 || totalTokenUsage.output_tokens > 0 ? estimateCostFromTokenUsage(totalTokenUsage, options.modelDef.model) : 0);
|
|
19316
19329
|
const tokenUsage = totalTokenUsage.input_tokens > 0 || totalTokenUsage.output_tokens > 0 ? {
|
|
@@ -19329,6 +19342,7 @@ class AcpAgentAdapter {
|
|
|
19329
19342
|
output: output.slice(-MAX_AGENT_OUTPUT_CHARS),
|
|
19330
19343
|
rateLimited: false,
|
|
19331
19344
|
sessionError: isSessionError,
|
|
19345
|
+
sessionErrorRetryable: isSessionErrorRetryable,
|
|
19332
19346
|
durationMs,
|
|
19333
19347
|
estimatedCost,
|
|
19334
19348
|
tokenUsage
|
|
@@ -23002,7 +23016,7 @@ class AutoInteractionPlugin {
|
|
|
23002
23016
|
};
|
|
23003
23017
|
}
|
|
23004
23018
|
async destroy() {}
|
|
23005
|
-
async send(
|
|
23019
|
+
async send(_request) {}
|
|
23006
23020
|
async receive(_requestId, _timeout = 60000) {
|
|
23007
23021
|
throw new Error("Auto plugin requires full request context (not just requestId)");
|
|
23008
23022
|
}
|
|
@@ -24500,7 +24514,7 @@ async function checkHomeEnvValid() {
|
|
|
24500
24514
|
message: passed ? `HOME env is valid: ${home}` : home === "" ? "HOME env is not set \u2014 agent may write files to unexpected locations" : `HOME env is not an absolute path ("${home}") \u2014 may cause literal "~" directories in repo`
|
|
24501
24515
|
};
|
|
24502
24516
|
}
|
|
24503
|
-
async function checkLanguageTools(profile,
|
|
24517
|
+
async function checkLanguageTools(profile, _workdir) {
|
|
24504
24518
|
if (!profile || !profile.language) {
|
|
24505
24519
|
return {
|
|
24506
24520
|
name: "language-tools-available",
|
|
@@ -25229,7 +25243,7 @@ function countStories(prd) {
|
|
|
25229
25243
|
decomposed: prd.userStories.filter((s) => s.status === "decomposed").length
|
|
25230
25244
|
};
|
|
25231
25245
|
}
|
|
25232
|
-
function markStoryPassed(prd, storyId,
|
|
25246
|
+
function markStoryPassed(prd, storyId, _statusWriter) {
|
|
25233
25247
|
const story = prd.userStories.find((s) => s.id === storyId);
|
|
25234
25248
|
if (story) {
|
|
25235
25249
|
story.passes = true;
|
|
@@ -26162,7 +26176,7 @@ function parseAcceptanceCriteria(specContent) {
|
|
|
26162
26176
|
}
|
|
26163
26177
|
return criteria;
|
|
26164
26178
|
}
|
|
26165
|
-
function buildAcceptanceTestPrompt(criteria, featureName,
|
|
26179
|
+
function buildAcceptanceTestPrompt(criteria, featureName, _codebaseContext, testPathConfig, language) {
|
|
26166
26180
|
const criteriaList = criteria.map((ac) => `${ac.id}: ${ac.text}`).join(`
|
|
26167
26181
|
`);
|
|
26168
26182
|
const resolvedTestPath = resolveAcceptanceTestFile2(language, testPathConfig);
|
|
@@ -26311,7 +26325,7 @@ ${tests || " // No acceptance criteria found"}
|
|
|
26311
26325
|
});
|
|
26312
26326
|
`;
|
|
26313
26327
|
}
|
|
26314
|
-
function generateGoSkeletonTests(
|
|
26328
|
+
function generateGoSkeletonTests(_featureName, criteria) {
|
|
26315
26329
|
const sanitize = (text) => text.replace(/[^a-zA-Z0-9 ]/g, "").split(" ").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
26316
26330
|
const tests = criteria.map((ac) => {
|
|
26317
26331
|
const funcName = `Test${sanitize(ac.text) || ac.id.replace("-", "")}`;
|
|
@@ -27496,40 +27510,8 @@ var init_claude = __esm(() => {
|
|
|
27496
27510
|
function validateAgentForTier(agent, tier) {
|
|
27497
27511
|
return agent.capabilities.supportedTiers.includes(tier);
|
|
27498
27512
|
}
|
|
27499
|
-
function validateAgentFeature(agent, feature) {
|
|
27500
|
-
return agent.capabilities.features.has(feature);
|
|
27501
|
-
}
|
|
27502
|
-
function describeAgentCapabilities(agent) {
|
|
27503
|
-
const tiers = agent.capabilities.supportedTiers.join(",");
|
|
27504
|
-
const features = Array.from(agent.capabilities.features).join(",");
|
|
27505
|
-
const maxTokens = agent.capabilities.maxContextTokens;
|
|
27506
|
-
return `${agent.name}: tiers=[${tiers}], maxTokens=${maxTokens}, features=[${features}]`;
|
|
27507
|
-
}
|
|
27508
27513
|
|
|
27509
27514
|
// src/agents/index.ts
|
|
27510
|
-
var exports_agents = {};
|
|
27511
|
-
__export(exports_agents, {
|
|
27512
|
-
validateAgentForTier: () => validateAgentForTier,
|
|
27513
|
-
validateAgentFeature: () => validateAgentFeature,
|
|
27514
|
-
parseTokenUsage: () => parseTokenUsage,
|
|
27515
|
-
getInstalledAgents: () => getInstalledAgents,
|
|
27516
|
-
getAllAgentNames: () => getAllAgentNames,
|
|
27517
|
-
getAgentVersions: () => getAgentVersions,
|
|
27518
|
-
getAgentVersion: () => getAgentVersion,
|
|
27519
|
-
getAgent: () => getAgent,
|
|
27520
|
-
formatCostWithConfidence: () => formatCostWithConfidence,
|
|
27521
|
-
estimateCostFromTokenUsage: () => estimateCostFromTokenUsage,
|
|
27522
|
-
estimateCostFromOutput: () => estimateCostFromOutput,
|
|
27523
|
-
estimateCostByDuration: () => estimateCostByDuration,
|
|
27524
|
-
estimateCost: () => estimateCost,
|
|
27525
|
-
describeAgentCapabilities: () => describeAgentCapabilities,
|
|
27526
|
-
checkAgentHealth: () => checkAgentHealth,
|
|
27527
|
-
MODEL_PRICING: () => MODEL_PRICING,
|
|
27528
|
-
CompleteError: () => CompleteError,
|
|
27529
|
-
ClaudeCodeAdapter: () => ClaudeCodeAdapter,
|
|
27530
|
-
COST_RATES: () => COST_RATES,
|
|
27531
|
-
AllAgentsUnavailableError: () => AllAgentsUnavailableError
|
|
27532
|
-
});
|
|
27533
27515
|
var init_agents = __esm(() => {
|
|
27534
27516
|
init_types2();
|
|
27535
27517
|
init_claude();
|
|
@@ -28096,7 +28078,7 @@ ${stat}
|
|
|
28096
28078
|
return `${statPreamble}${truncated}
|
|
28097
28079
|
... (truncated at ${DIFF_CAP_BYTES} bytes, showing ${visibleFiles}/${totalFiles} files)`;
|
|
28098
28080
|
}
|
|
28099
|
-
function buildPrompt(story, semanticConfig, diff,
|
|
28081
|
+
function buildPrompt(story, semanticConfig, diff, _stat) {
|
|
28100
28082
|
const acList = story.acceptanceCriteria.map((ac, i) => `${i + 1}. ${ac}`).join(`
|
|
28101
28083
|
`);
|
|
28102
28084
|
const customRulesSection = semanticConfig.rules.length > 0 ? `
|
|
@@ -29984,9 +29966,7 @@ function deriveTestPatterns(contextFiles) {
|
|
|
29984
29966
|
async function detectTestDir(workdir) {
|
|
29985
29967
|
for (const dir of COMMON_TEST_DIRS) {
|
|
29986
29968
|
const fullPath = path7.join(workdir, dir);
|
|
29987
|
-
const file3 = Bun.file(path7.join(fullPath, "."));
|
|
29988
29969
|
try {
|
|
29989
|
-
const dirStat = await Bun.file(fullPath).exists();
|
|
29990
29970
|
const proc = Bun.spawn(["test", "-d", fullPath], { stdout: "pipe", stderr: "pipe" });
|
|
29991
29971
|
const exitCode = await proc.exited;
|
|
29992
29972
|
if (exitCode === 0)
|
|
@@ -30787,7 +30767,7 @@ function globToRegex(pattern) {
|
|
|
30787
30767
|
const regexStr = filePattern.replace(/\./g, "\\.").replace(/\*/g, "[^/]*").replace(/\{([^}]+)\}/g, (_, group) => `(${group.replace(/,/g, "|")})`).replace(/\\\.\\\*/g, "\\.[^/]*");
|
|
30788
30768
|
return new RegExp(`${regexStr}$`);
|
|
30789
30769
|
}
|
|
30790
|
-
async function isGreenfieldStory(
|
|
30770
|
+
async function isGreenfieldStory(_story, workdir, testPattern = "**/*.{test,spec}.{ts,js,tsx,jsx}") {
|
|
30791
30771
|
try {
|
|
30792
30772
|
const regex = globToRegex(testPattern);
|
|
30793
30773
|
const testFiles = await scanForTestFiles(workdir, regex);
|
|
@@ -32381,7 +32361,8 @@ async function runTddSession(role, agent, story, config2, workdir, modelTier, be
|
|
|
32381
32361
|
role,
|
|
32382
32362
|
storyId: story.id,
|
|
32383
32363
|
durationMs: Date.now() - startTime,
|
|
32384
|
-
exitCode: result.exitCode
|
|
32364
|
+
exitCode: result.exitCode,
|
|
32365
|
+
...result.output ? { output: result.output.slice(0, 500) } : {}
|
|
32385
32366
|
});
|
|
32386
32367
|
}
|
|
32387
32368
|
await _sessionRunnerDeps.autoCommitIfDirty(workdir, "tdd", role, story.id);
|
|
@@ -33404,7 +33385,7 @@ var init_optimizer2 = __esm(() => {
|
|
|
33404
33385
|
init_optimizer();
|
|
33405
33386
|
optimizerStage = {
|
|
33406
33387
|
name: "optimizer",
|
|
33407
|
-
enabled: (
|
|
33388
|
+
enabled: (_ctx) => {
|
|
33408
33389
|
return true;
|
|
33409
33390
|
},
|
|
33410
33391
|
async execute(ctx) {
|
|
@@ -34754,7 +34735,7 @@ var init_classify = __esm(() => {
|
|
|
34754
34735
|
});
|
|
34755
34736
|
|
|
34756
34737
|
// src/routing/strategies/llm-prompts.ts
|
|
34757
|
-
function buildRoutingPrompt(story,
|
|
34738
|
+
function buildRoutingPrompt(story, _config) {
|
|
34758
34739
|
const { title, description, acceptanceCriteria, tags } = story;
|
|
34759
34740
|
const criteria = acceptanceCriteria.map((c, i) => `${i + 1}. ${c}`).join(`
|
|
34760
34741
|
`);
|
|
@@ -34788,7 +34769,7 @@ Respond with:
|
|
|
34788
34769
|
{"complexity":"simple|medium|complex|expert","modelTier":"fast|balanced|powerful","reasoning":"<one line>"}`;
|
|
34789
34770
|
return wrapJsonPrompt(core2);
|
|
34790
34771
|
}
|
|
34791
|
-
function buildBatchRoutingPrompt(stories,
|
|
34772
|
+
function buildBatchRoutingPrompt(stories, _config) {
|
|
34792
34773
|
const storyBlocks = stories.map((story, idx) => {
|
|
34793
34774
|
const criteria = story.acceptanceCriteria.map((c, i) => ` ${i + 1}. ${c}`).join(`
|
|
34794
34775
|
`);
|
|
@@ -36657,7 +36638,7 @@ var package_default;
|
|
|
36657
36638
|
var init_package = __esm(() => {
|
|
36658
36639
|
package_default = {
|
|
36659
36640
|
name: "@nathapp/nax",
|
|
36660
|
-
version: "0.61.
|
|
36641
|
+
version: "0.61.2",
|
|
36661
36642
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
36662
36643
|
type: "module",
|
|
36663
36644
|
bin: {
|
|
@@ -36737,8 +36718,8 @@ var init_version = __esm(() => {
|
|
|
36737
36718
|
NAX_VERSION = package_default.version;
|
|
36738
36719
|
NAX_COMMIT = (() => {
|
|
36739
36720
|
try {
|
|
36740
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
36741
|
-
return "
|
|
36721
|
+
if (/^[0-9a-f]{6,10}$/.test("16490524"))
|
|
36722
|
+
return "16490524";
|
|
36742
36723
|
} catch {}
|
|
36743
36724
|
try {
|
|
36744
36725
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -37085,7 +37066,7 @@ function parseImportStatements(content) {
|
|
|
37085
37066
|
}
|
|
37086
37067
|
return imports;
|
|
37087
37068
|
}
|
|
37088
|
-
function resolveImportPaths(imports,
|
|
37069
|
+
function resolveImportPaths(imports, _workdir) {
|
|
37089
37070
|
const resolved = [];
|
|
37090
37071
|
for (const imp of imports) {
|
|
37091
37072
|
if (imp.startsWith(".")) {
|
|
@@ -38338,7 +38319,7 @@ function wireHooks(bus, hooks, workdir, feature) {
|
|
|
38338
38319
|
return fn().catch((err) => logger?.warn("hooks-subscriber", `Hook "${name}" failed`, { error: String(err) })).catch(() => {});
|
|
38339
38320
|
};
|
|
38340
38321
|
const unsubs = [];
|
|
38341
|
-
unsubs.push(bus.on("run:started", (
|
|
38322
|
+
unsubs.push(bus.on("run:started", (_ev) => {
|
|
38342
38323
|
return safe("on-start", () => fireHook(hooks, "on-start", hookCtx(feature, { status: "running" }), workdir));
|
|
38343
38324
|
}));
|
|
38344
38325
|
unsubs.push(bus.on("story:started", (ev) => {
|
|
@@ -38359,7 +38340,7 @@ function wireHooks(bus, hooks, workdir, feature) {
|
|
|
38359
38340
|
unsubs.push(bus.on("run:completed", (ev) => {
|
|
38360
38341
|
return safe("on-complete", () => fireHook(hooks, "on-complete", hookCtx(feature, { status: "complete", cost: ev.totalCost ?? 0 }), workdir));
|
|
38361
38342
|
}));
|
|
38362
|
-
unsubs.push(bus.on("run:resumed", (
|
|
38343
|
+
unsubs.push(bus.on("run:resumed", (_ev) => {
|
|
38363
38344
|
return safe("on-resume", () => fireHook(hooks, "on-resume", hookCtx(feature, { status: "running" }), workdir));
|
|
38364
38345
|
}));
|
|
38365
38346
|
unsubs.push(bus.on("story:completed", (ev) => {
|
|
@@ -39112,7 +39093,6 @@ var init_pipeline_result_handler = __esm(() => {
|
|
|
39112
39093
|
// src/execution/iteration-runner.ts
|
|
39113
39094
|
import { join as join45 } from "path";
|
|
39114
39095
|
async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
|
|
39115
|
-
const logger = getSafeLogger();
|
|
39116
39096
|
const { story, storiesToExecute, routing, isBatchExecution } = selection;
|
|
39117
39097
|
if (ctx.dryRun) {
|
|
39118
39098
|
const dryRunResult = await handleDryRun({
|
|
@@ -39236,7 +39216,6 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
39236
39216
|
var _iterationRunnerDeps;
|
|
39237
39217
|
var init_iteration_runner = __esm(() => {
|
|
39238
39218
|
init_loader();
|
|
39239
|
-
init_logger2();
|
|
39240
39219
|
init_runner();
|
|
39241
39220
|
init_stages();
|
|
39242
39221
|
init_prd();
|
|
@@ -39354,7 +39333,7 @@ async function executeStoryInWorktree(story, worktreePath, context, routing, eve
|
|
|
39354
39333
|
};
|
|
39355
39334
|
}
|
|
39356
39335
|
}
|
|
39357
|
-
async function executeParallelBatch(stories,
|
|
39336
|
+
async function executeParallelBatch(stories, _projectRoot, config2, context, worktreePaths, maxConcurrency, eventEmitter, storyEffectiveConfigs) {
|
|
39358
39337
|
const logger = getSafeLogger();
|
|
39359
39338
|
const results = {
|
|
39360
39339
|
pipelinePassed: [],
|
|
@@ -40505,7 +40484,7 @@ function detectType(pkg) {
|
|
|
40505
40484
|
return "cli";
|
|
40506
40485
|
return;
|
|
40507
40486
|
}
|
|
40508
|
-
async function detectTestFramework(
|
|
40487
|
+
async function detectTestFramework(_workdir, language, pkg) {
|
|
40509
40488
|
if (language === "go")
|
|
40510
40489
|
return "go-test";
|
|
40511
40490
|
if (language === "rust")
|
|
@@ -40965,7 +40944,6 @@ import { join as join50 } from "path";
|
|
|
40965
40944
|
async function reconcileState(prd, prdPath, workdir, config2) {
|
|
40966
40945
|
const logger = getSafeLogger();
|
|
40967
40946
|
let reconciledCount = 0;
|
|
40968
|
-
let modified = false;
|
|
40969
40947
|
for (const story of prd.userStories) {
|
|
40970
40948
|
if (story.status !== "failed")
|
|
40971
40949
|
continue;
|
|
@@ -41000,7 +40978,6 @@ async function reconcileState(prd, prdPath, workdir, config2) {
|
|
|
41000
40978
|
});
|
|
41001
40979
|
markStoryPassed(prd, story.id);
|
|
41002
40980
|
reconciledCount++;
|
|
41003
|
-
modified = true;
|
|
41004
40981
|
}
|
|
41005
40982
|
if (reconciledCount > 0) {
|
|
41006
40983
|
logger?.info("reconciliation", `Reconciled ${reconciledCount} failed stories from git history`);
|
|
@@ -41012,7 +40989,6 @@ async function checkAgentInstalled(config2, dryRun, agentGetFn) {
|
|
|
41012
40989
|
if (dryRun)
|
|
41013
40990
|
return;
|
|
41014
40991
|
const logger = getSafeLogger();
|
|
41015
|
-
const { getAgent: getAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
41016
40992
|
const agent = (agentGetFn ?? _reconcileDeps.getAgent)(config2.autoMode.defaultAgent);
|
|
41017
40993
|
if (!agent) {
|
|
41018
40994
|
logger?.error("execution", "Agent not found", {
|
|
@@ -41081,6 +41057,68 @@ var init_run_initialization = __esm(() => {
|
|
|
41081
41057
|
};
|
|
41082
41058
|
});
|
|
41083
41059
|
|
|
41060
|
+
// src/execution/lifecycle/paused-story-prompts.ts
|
|
41061
|
+
var exports_paused_story_prompts = {};
|
|
41062
|
+
__export(exports_paused_story_prompts, {
|
|
41063
|
+
promptForPausedStories: () => promptForPausedStories
|
|
41064
|
+
});
|
|
41065
|
+
async function promptForPausedStories(prd, chain, featureName) {
|
|
41066
|
+
const logger = getSafeLogger();
|
|
41067
|
+
const summary = { resumed: [], skipped: [], kept: [] };
|
|
41068
|
+
const pausedStories = prd.userStories.filter((s) => s.status === "paused");
|
|
41069
|
+
for (const story of pausedStories) {
|
|
41070
|
+
const lastReason = (story.priorErrors?.slice(-1)[0] ?? "no reason recorded").replace(/\n/g, " ").slice(0, 200);
|
|
41071
|
+
logger?.info("run-initialization", "Paused story found \u2014 prompting user", {
|
|
41072
|
+
storyId: story.id,
|
|
41073
|
+
attempts: story.attempts
|
|
41074
|
+
});
|
|
41075
|
+
const response = await chain.prompt({
|
|
41076
|
+
id: `ix-${story.id}-paused-resume`,
|
|
41077
|
+
type: "choose",
|
|
41078
|
+
featureName,
|
|
41079
|
+
storyId: story.id,
|
|
41080
|
+
stage: "pre-flight",
|
|
41081
|
+
summary: `Story ${story.id} is paused \u2014 how to proceed?`,
|
|
41082
|
+
detail: `"${story.title}"
|
|
41083
|
+
Last reason: ${lastReason}
|
|
41084
|
+
Attempts so far: ${story.attempts}`,
|
|
41085
|
+
options: [
|
|
41086
|
+
{ key: "resume", label: "Resume", description: "Reset to pending and retry on this run" },
|
|
41087
|
+
{ key: "skip", label: "Skip", description: "Mark as skipped, won't be retried" },
|
|
41088
|
+
{ key: "keep", label: "Keep paused", description: "Leave paused, skip for this run" }
|
|
41089
|
+
],
|
|
41090
|
+
timeout: 300000,
|
|
41091
|
+
fallback: "continue",
|
|
41092
|
+
createdAt: Date.now()
|
|
41093
|
+
});
|
|
41094
|
+
const effectiveAction = chain.applyFallback(response, "continue");
|
|
41095
|
+
const resolvedKey = effectiveAction === "approve" ? "keep" : response.action;
|
|
41096
|
+
switch (resolvedKey) {
|
|
41097
|
+
case "resume": {
|
|
41098
|
+
story.status = "pending";
|
|
41099
|
+
summary.resumed.push(story.id);
|
|
41100
|
+
logger?.info("run-initialization", "User resumed paused story", { storyId: story.id });
|
|
41101
|
+
break;
|
|
41102
|
+
}
|
|
41103
|
+
case "skip": {
|
|
41104
|
+
story.status = "skipped";
|
|
41105
|
+
summary.skipped.push(story.id);
|
|
41106
|
+
logger?.info("run-initialization", "User skipped paused story", { storyId: story.id });
|
|
41107
|
+
break;
|
|
41108
|
+
}
|
|
41109
|
+
default: {
|
|
41110
|
+
summary.kept.push(story.id);
|
|
41111
|
+
logger?.info("run-initialization", "Keeping story paused", { storyId: story.id });
|
|
41112
|
+
break;
|
|
41113
|
+
}
|
|
41114
|
+
}
|
|
41115
|
+
}
|
|
41116
|
+
return summary;
|
|
41117
|
+
}
|
|
41118
|
+
var init_paused_story_prompts = __esm(() => {
|
|
41119
|
+
init_logger2();
|
|
41120
|
+
});
|
|
41121
|
+
|
|
41084
41122
|
// src/execution/lifecycle/run-setup.ts
|
|
41085
41123
|
var exports_run_setup = {};
|
|
41086
41124
|
__export(exports_run_setup, {
|
|
@@ -41209,7 +41247,16 @@ async function setupRun(options) {
|
|
|
41209
41247
|
agentGetFn: options.agentGetFn
|
|
41210
41248
|
});
|
|
41211
41249
|
prd = initResult.prd;
|
|
41212
|
-
|
|
41250
|
+
statusWriter.setPrd(prd);
|
|
41251
|
+
let counts = initResult.storyCounts;
|
|
41252
|
+
if (counts.paused > 0 && interactionChain !== null) {
|
|
41253
|
+
const { promptForPausedStories: promptForPausedStories2 } = await Promise.resolve().then(() => (init_paused_story_prompts(), exports_paused_story_prompts));
|
|
41254
|
+
const pausedSummary = await promptForPausedStories2(prd, interactionChain, feature);
|
|
41255
|
+
if (pausedSummary.resumed.length > 0 || pausedSummary.skipped.length > 0) {
|
|
41256
|
+
await savePRD(prd, prdPath);
|
|
41257
|
+
counts = countStories(prd);
|
|
41258
|
+
}
|
|
41259
|
+
}
|
|
41213
41260
|
return {
|
|
41214
41261
|
statusWriter,
|
|
41215
41262
|
pidRegistry,
|
|
@@ -74802,7 +74849,7 @@ import { existsSync as existsSync22, readdirSync as readdirSync6 } from "fs";
|
|
|
74802
74849
|
import { join as join30 } from "path";
|
|
74803
74850
|
|
|
74804
74851
|
// src/cli/diagnose-analysis.ts
|
|
74805
|
-
function detectFailurePattern(story,
|
|
74852
|
+
function detectFailurePattern(story, _prd, status) {
|
|
74806
74853
|
if (story.status === "passed" && story.priorErrors?.some((err) => err.toLowerCase().includes("greenfield-no-tests"))) {
|
|
74807
74854
|
return "AUTO_RECOVERED";
|
|
74808
74855
|
}
|
|
@@ -76748,7 +76795,6 @@ async function run(options) {
|
|
|
76748
76795
|
let totalCost = 0;
|
|
76749
76796
|
let runCompleted = false;
|
|
76750
76797
|
const allStoryMetrics = [];
|
|
76751
|
-
const logger = getSafeLogger();
|
|
76752
76798
|
const registry2 = createAgentRegistry(config2);
|
|
76753
76799
|
const agentGetFn = registry2.getAgent.bind(registry2);
|
|
76754
76800
|
let prd;
|
|
@@ -76849,20 +76895,20 @@ async function run(options) {
|
|
|
76849
76895
|
durationMs
|
|
76850
76896
|
};
|
|
76851
76897
|
} finally {
|
|
76852
|
-
const
|
|
76853
|
-
|
|
76898
|
+
const logger = getSafeLogger();
|
|
76899
|
+
logger?.debug("execution", "Runner finally block \u2014 starting cleanup");
|
|
76854
76900
|
stopHeartbeat();
|
|
76855
76901
|
cleanupCrashHandlers();
|
|
76856
|
-
|
|
76902
|
+
logger?.debug("execution", "Runner finally \u2014 sweeping ACP sessions");
|
|
76857
76903
|
await sweepFeatureSessions(workdir, feature).catch(() => {});
|
|
76858
|
-
|
|
76904
|
+
logger?.debug("execution", "Runner finally \u2014 ACP sweep done");
|
|
76859
76905
|
let branch = "";
|
|
76860
76906
|
try {
|
|
76861
76907
|
const { stdout, exitCode } = await gitWithTimeout(["branch", "--show-current"], workdir);
|
|
76862
76908
|
if (exitCode === 0)
|
|
76863
76909
|
branch = stdout.trim();
|
|
76864
76910
|
} catch {}
|
|
76865
|
-
|
|
76911
|
+
logger?.debug("execution", "Runner finally \u2014 running cleanupRun");
|
|
76866
76912
|
const { cleanupRun: cleanupRun2 } = await Promise.resolve().then(() => (init_run_cleanup(), exports_run_cleanup));
|
|
76867
76913
|
await cleanupRun2({
|
|
76868
76914
|
runId,
|
|
@@ -76879,7 +76925,7 @@ async function run(options) {
|
|
|
76879
76925
|
version: NAX_VERSION,
|
|
76880
76926
|
runCompleted
|
|
76881
76927
|
});
|
|
76882
|
-
|
|
76928
|
+
logger?.debug("execution", "Runner finally \u2014 cleanupRun done, run() returning");
|
|
76883
76929
|
}
|
|
76884
76930
|
}
|
|
76885
76931
|
|
|
@@ -83865,7 +83911,6 @@ function usePty(options) {
|
|
|
83865
83911
|
isRunning: false
|
|
83866
83912
|
}));
|
|
83867
83913
|
const [handle, setHandle] = import_react33.useState(null);
|
|
83868
|
-
const [ptyProcess, setPtyProcess] = import_react33.useState(null);
|
|
83869
83914
|
const command = options?.command;
|
|
83870
83915
|
const argsJson = JSON.stringify(options?.args);
|
|
83871
83916
|
const cwd2 = options?.cwd;
|
|
@@ -83881,7 +83926,6 @@ function usePty(options) {
|
|
|
83881
83926
|
stdout: "pipe",
|
|
83882
83927
|
stderr: "inherit"
|
|
83883
83928
|
});
|
|
83884
|
-
setPtyProcess(proc);
|
|
83885
83929
|
setState((prev) => ({ ...prev, isRunning: true }));
|
|
83886
83930
|
(async () => {
|
|
83887
83931
|
let currentLine = "";
|