@nathapp/nax 0.54.2 → 0.54.3
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 +191 -108
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -18786,7 +18786,7 @@ async function refineAcceptanceCriteria(criteria, context) {
|
|
|
18786
18786
|
if (criteria.length === 0) {
|
|
18787
18787
|
return [];
|
|
18788
18788
|
}
|
|
18789
|
-
const { storyId, codebaseContext, config: config2, testStrategy, testFramework } = context;
|
|
18789
|
+
const { storyId, featureName, workdir, codebaseContext, config: config2, testStrategy, testFramework } = context;
|
|
18790
18790
|
const logger = getLogger();
|
|
18791
18791
|
const modelTier = config2.acceptance?.model ?? "fast";
|
|
18792
18792
|
const modelEntry = config2.models[modelTier] ?? config2.models.fast;
|
|
@@ -18801,7 +18801,11 @@ async function refineAcceptanceCriteria(criteria, context) {
|
|
|
18801
18801
|
jsonMode: true,
|
|
18802
18802
|
maxTokens: 4096,
|
|
18803
18803
|
model: modelDef.model,
|
|
18804
|
-
config: config2
|
|
18804
|
+
config: config2,
|
|
18805
|
+
featureName,
|
|
18806
|
+
storyId,
|
|
18807
|
+
workdir,
|
|
18808
|
+
sessionRole: "refine"
|
|
18805
18809
|
});
|
|
18806
18810
|
} catch (error48) {
|
|
18807
18811
|
const reason = errorMessage(error48);
|
|
@@ -18940,27 +18944,58 @@ Rules:
|
|
|
18940
18944
|
- **NEVER use placeholder assertions** \u2014 no always-passing or always-failing stubs, no TODO comments as the only content, no empty test bodies
|
|
18941
18945
|
- Every test MUST have real assertions that PASS when the feature is correctly implemented and FAIL when it is broken
|
|
18942
18946
|
- **Prefer behavioral tests** \u2014 import functions and call them rather than reading source files. For example, to verify "getPostRunActions() returns empty array", import PluginRegistry and call getPostRunActions(), don't grep the source file for the method name.
|
|
18943
|
-
-
|
|
18944
|
-
- **Path anchor (CRITICAL)**:
|
|
18947
|
+
- **File output (REQUIRED)**: Write the acceptance test file DIRECTLY to the path shown below. Do NOT output the test code in your response. After writing the file, reply with a brief confirmation.
|
|
18948
|
+
- **Path anchor (CRITICAL)**: Write the test file to this exact path: \`${options.featureDir}/${acceptanceTestFilename(options.language)}\`. Import from package sources using relative paths like \`./src/...\`. No deep \`../../../../\` traversal needed.`;
|
|
18945
18949
|
const prompt = basePrompt;
|
|
18946
18950
|
logger.info("acceptance", "Generating tests from PRD refined criteria", { count: refinedCriteria.length });
|
|
18947
18951
|
const rawOutput = await (options.adapter ?? _generatorPRDDeps.adapter).complete(prompt, {
|
|
18948
18952
|
model: options.modelDef.model,
|
|
18949
18953
|
config: options.config,
|
|
18950
18954
|
timeoutMs: options.config?.acceptance?.timeoutMs ?? 1800000,
|
|
18951
|
-
workdir: options.workdir
|
|
18955
|
+
workdir: options.workdir,
|
|
18956
|
+
featureName: options.featureName,
|
|
18957
|
+
sessionRole: "acceptance-gen"
|
|
18952
18958
|
});
|
|
18953
18959
|
let testCode = extractTestCode(rawOutput);
|
|
18960
|
+
logger.debug("acceptance", "Received raw output from LLM", {
|
|
18961
|
+
hasCode: testCode !== null,
|
|
18962
|
+
outputLength: rawOutput.length,
|
|
18963
|
+
outputPreview: rawOutput.slice(0, 300)
|
|
18964
|
+
});
|
|
18954
18965
|
if (!testCode) {
|
|
18955
18966
|
const targetPath = join2(options.featureDir, acceptanceTestFilename(options.language));
|
|
18967
|
+
let recoveryFailed = false;
|
|
18968
|
+
logger.debug("acceptance", "BUG-076 recovery: checking for agent-written file", { targetPath });
|
|
18956
18969
|
try {
|
|
18957
18970
|
const existing = await Bun.file(targetPath).text();
|
|
18958
18971
|
const recovered = extractTestCode(existing);
|
|
18972
|
+
logger.debug("acceptance", "BUG-076 recovery: file check result", {
|
|
18973
|
+
fileSize: existing.length,
|
|
18974
|
+
extractedCode: recovered !== null,
|
|
18975
|
+
filePreview: existing.slice(0, 300)
|
|
18976
|
+
});
|
|
18959
18977
|
if (recovered) {
|
|
18960
18978
|
logger.info("acceptance", "Acceptance test written directly by agent \u2014 using existing file", { targetPath });
|
|
18961
18979
|
testCode = recovered;
|
|
18980
|
+
} else {
|
|
18981
|
+
recoveryFailed = true;
|
|
18982
|
+
logger.error("acceptance", "BUG-076: ACP adapter wrote file but no code extractable \u2014 falling back to skeleton", {
|
|
18983
|
+
targetPath,
|
|
18984
|
+
filePreview: existing.slice(0, 300)
|
|
18985
|
+
});
|
|
18962
18986
|
}
|
|
18963
|
-
} catch {
|
|
18987
|
+
} catch {
|
|
18988
|
+
recoveryFailed = true;
|
|
18989
|
+
logger.debug("acceptance", "BUG-076 recovery: no file written by agent, falling back to skeleton", {
|
|
18990
|
+
targetPath,
|
|
18991
|
+
rawOutputPreview: rawOutput.slice(0, 500)
|
|
18992
|
+
});
|
|
18993
|
+
}
|
|
18994
|
+
if (recoveryFailed) {
|
|
18995
|
+
logger.error("acceptance", "BUG-076: LLM returned non-code output and no file was written by agent \u2014 falling back to skeleton", {
|
|
18996
|
+
rawOutputPreview: rawOutput.slice(0, 500)
|
|
18997
|
+
});
|
|
18998
|
+
}
|
|
18964
18999
|
}
|
|
18965
19000
|
if (!testCode) {
|
|
18966
19001
|
logger.warn("acceptance", "LLM returned non-code output for acceptance tests \u2014 falling back to skeleton", {
|
|
@@ -19061,7 +19096,9 @@ async function generateAcceptanceTests(adapter, options) {
|
|
|
19061
19096
|
model: options.modelDef.model,
|
|
19062
19097
|
config: options.config,
|
|
19063
19098
|
timeoutMs: options.config?.acceptance?.timeoutMs ?? 1800000,
|
|
19064
|
-
workdir: options.workdir
|
|
19099
|
+
workdir: options.workdir,
|
|
19100
|
+
featureName: options.featureName,
|
|
19101
|
+
sessionRole: "acceptance-gen"
|
|
19065
19102
|
});
|
|
19066
19103
|
const testCode = extractTestCode(output);
|
|
19067
19104
|
if (!testCode) {
|
|
@@ -19312,7 +19349,10 @@ async function generateFixStories(adapter, options) {
|
|
|
19312
19349
|
try {
|
|
19313
19350
|
const fixDescription = await adapter.complete(prompt, {
|
|
19314
19351
|
model: modelDef.model,
|
|
19315
|
-
config: options.config
|
|
19352
|
+
config: options.config,
|
|
19353
|
+
featureName: options.prd.feature,
|
|
19354
|
+
workdir: options.workdir,
|
|
19355
|
+
sessionRole: "fix-gen"
|
|
19316
19356
|
});
|
|
19317
19357
|
fixStories.push({
|
|
19318
19358
|
id: `US-FIX-${String(i + 1).padStart(3, "0")}`,
|
|
@@ -20101,10 +20141,11 @@ class AcpAgentAdapter {
|
|
|
20101
20141
|
let session = null;
|
|
20102
20142
|
let hadError = false;
|
|
20103
20143
|
try {
|
|
20144
|
+
const completeSessionName = _options?.sessionName ?? buildSessionName(workdir ?? process.cwd(), _options?.featureName, _options?.storyId, _options?.sessionRole);
|
|
20104
20145
|
session = await client.createSession({
|
|
20105
20146
|
agentName: this.name,
|
|
20106
20147
|
permissionMode,
|
|
20107
|
-
sessionName:
|
|
20148
|
+
sessionName: completeSessionName
|
|
20108
20149
|
});
|
|
20109
20150
|
let timeoutId;
|
|
20110
20151
|
const timeoutPromise = new Promise((_, reject) => {
|
|
@@ -20210,7 +20251,9 @@ class AcpAgentAdapter {
|
|
|
20210
20251
|
output = await this.complete(prompt, {
|
|
20211
20252
|
model,
|
|
20212
20253
|
jsonMode: true,
|
|
20213
|
-
config: options.config
|
|
20254
|
+
config: options.config,
|
|
20255
|
+
workdir: options.workdir,
|
|
20256
|
+
sessionRole: "decompose"
|
|
20214
20257
|
});
|
|
20215
20258
|
} catch (err) {
|
|
20216
20259
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -22305,7 +22348,7 @@ var package_default;
|
|
|
22305
22348
|
var init_package = __esm(() => {
|
|
22306
22349
|
package_default = {
|
|
22307
22350
|
name: "@nathapp/nax",
|
|
22308
|
-
version: "0.54.
|
|
22351
|
+
version: "0.54.3",
|
|
22309
22352
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
22310
22353
|
type: "module",
|
|
22311
22354
|
bin: {
|
|
@@ -22382,8 +22425,8 @@ var init_version = __esm(() => {
|
|
|
22382
22425
|
NAX_VERSION = package_default.version;
|
|
22383
22426
|
NAX_COMMIT = (() => {
|
|
22384
22427
|
try {
|
|
22385
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
22386
|
-
return "
|
|
22428
|
+
if (/^[0-9a-f]{6,10}$/.test("5acee1f"))
|
|
22429
|
+
return "5acee1f";
|
|
22387
22430
|
} catch {}
|
|
22388
22431
|
try {
|
|
22389
22432
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -23627,7 +23670,10 @@ class AutoInteractionPlugin {
|
|
|
23627
23670
|
const output = await adapter.complete(prompt, {
|
|
23628
23671
|
...modelArg && { model: modelArg },
|
|
23629
23672
|
jsonMode: true,
|
|
23630
|
-
...this.config.naxConfig && { config: this.config.naxConfig }
|
|
23673
|
+
...this.config.naxConfig && { config: this.config.naxConfig },
|
|
23674
|
+
featureName: request.featureName,
|
|
23675
|
+
storyId: request.storyId,
|
|
23676
|
+
sessionRole: "auto"
|
|
23631
23677
|
});
|
|
23632
23678
|
return this.parseResponse(output);
|
|
23633
23679
|
}
|
|
@@ -24368,31 +24414,33 @@ ${stderr}` };
|
|
|
24368
24414
|
}
|
|
24369
24415
|
let totalCriteria = 0;
|
|
24370
24416
|
let testableCount = 0;
|
|
24371
|
-
const
|
|
24372
|
-
const
|
|
24373
|
-
|
|
24374
|
-
|
|
24375
|
-
|
|
24376
|
-
|
|
24377
|
-
|
|
24378
|
-
|
|
24379
|
-
|
|
24380
|
-
|
|
24381
|
-
|
|
24382
|
-
|
|
24417
|
+
const fingerprint = computeACFingerprint(allCriteria);
|
|
24418
|
+
const meta3 = await _acceptanceSetupDeps.readMeta(metaPath);
|
|
24419
|
+
getSafeLogger()?.debug("acceptance-setup", "Fingerprint check", {
|
|
24420
|
+
currentFingerprint: fingerprint,
|
|
24421
|
+
storedFingerprint: meta3?.acFingerprint ?? "none",
|
|
24422
|
+
match: meta3?.acFingerprint === fingerprint
|
|
24423
|
+
});
|
|
24424
|
+
let shouldGenerate = false;
|
|
24425
|
+
if (!meta3 || meta3.acFingerprint !== fingerprint) {
|
|
24426
|
+
if (!meta3) {
|
|
24427
|
+
getSafeLogger()?.info("acceptance-setup", "No acceptance meta \u2014 generating acceptance tests");
|
|
24428
|
+
} else {
|
|
24383
24429
|
getSafeLogger()?.info("acceptance-setup", "ACs changed \u2014 regenerating acceptance tests", {
|
|
24384
|
-
reason:
|
|
24430
|
+
reason: "fingerprint mismatch",
|
|
24431
|
+
currentFingerprint: fingerprint,
|
|
24432
|
+
storedFingerprint: meta3.acFingerprint
|
|
24385
24433
|
});
|
|
24386
|
-
|
|
24387
|
-
|
|
24388
|
-
|
|
24389
|
-
|
|
24390
|
-
|
|
24434
|
+
}
|
|
24435
|
+
for (const { testPath } of testPaths) {
|
|
24436
|
+
if (await _acceptanceSetupDeps.fileExists(testPath)) {
|
|
24437
|
+
await _acceptanceSetupDeps.copyFile(testPath, `${testPath}.bak`);
|
|
24438
|
+
await _acceptanceSetupDeps.deleteFile(testPath);
|
|
24391
24439
|
}
|
|
24392
|
-
shouldGenerate = true;
|
|
24393
|
-
} else {
|
|
24394
|
-
getSafeLogger()?.info("acceptance-setup", "Reusing existing acceptance tests (fingerprint match)");
|
|
24395
24440
|
}
|
|
24441
|
+
shouldGenerate = true;
|
|
24442
|
+
} else {
|
|
24443
|
+
getSafeLogger()?.info("acceptance-setup", "Reusing existing acceptance tests (fingerprint match)");
|
|
24396
24444
|
}
|
|
24397
24445
|
if (shouldGenerate) {
|
|
24398
24446
|
totalCriteria = allCriteria.length;
|
|
@@ -24404,6 +24452,8 @@ ${stderr}` };
|
|
|
24404
24452
|
for (const story of nonFixStories) {
|
|
24405
24453
|
const storyRefined = await _acceptanceSetupDeps.refine(story.acceptanceCriteria, {
|
|
24406
24454
|
storyId: story.id,
|
|
24455
|
+
featureName: ctx.prd.feature,
|
|
24456
|
+
workdir: ctx.workdir,
|
|
24407
24457
|
codebaseContext: "",
|
|
24408
24458
|
config: ctx.config,
|
|
24409
24459
|
testStrategy: ctx.config.acceptance.testStrategy,
|
|
@@ -24439,10 +24489,10 @@ ${stderr}` };
|
|
|
24439
24489
|
});
|
|
24440
24490
|
await _acceptanceSetupDeps.writeFile(testPath, result.testCode);
|
|
24441
24491
|
}
|
|
24442
|
-
const
|
|
24492
|
+
const fingerprint2 = computeACFingerprint(allCriteria);
|
|
24443
24493
|
await _acceptanceSetupDeps.writeMeta(metaPath, {
|
|
24444
24494
|
generatedAt: new Date().toISOString(),
|
|
24445
|
-
acFingerprint:
|
|
24495
|
+
acFingerprint: fingerprint2,
|
|
24446
24496
|
storyCount: ctx.prd.userStories.length,
|
|
24447
24497
|
acCount: totalCriteria,
|
|
24448
24498
|
generator: "nax"
|
|
@@ -24907,6 +24957,16 @@ async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, mo
|
|
|
24907
24957
|
storyId: story.id,
|
|
24908
24958
|
durationMs: durationMs2
|
|
24909
24959
|
});
|
|
24960
|
+
logger?.debug("review", "Semantic review findings", {
|
|
24961
|
+
storyId: story.id,
|
|
24962
|
+
findings: parsed.findings.map((f) => ({
|
|
24963
|
+
severity: f.severity,
|
|
24964
|
+
file: f.file,
|
|
24965
|
+
line: f.line,
|
|
24966
|
+
issue: f.issue,
|
|
24967
|
+
suggestion: f.suggestion
|
|
24968
|
+
}))
|
|
24969
|
+
});
|
|
24910
24970
|
const output = `Semantic review failed:
|
|
24911
24971
|
|
|
24912
24972
|
${formatFindings(parsed.findings)}`;
|
|
@@ -32828,36 +32888,32 @@ import { appendFileSync as appendFileSync2 } from "fs";
|
|
|
32828
32888
|
function startHeartbeat(statusWriter, getTotalCost, getIterations, jsonlFilePath) {
|
|
32829
32889
|
const logger = getSafeLogger();
|
|
32830
32890
|
stopHeartbeat();
|
|
32831
|
-
heartbeatTimer = setInterval(
|
|
32832
|
-
|
|
32833
|
-
if (jsonlFilePath) {
|
|
32891
|
+
heartbeatTimer = setInterval(() => {
|
|
32892
|
+
(async () => {
|
|
32834
32893
|
try {
|
|
32835
|
-
|
|
32836
|
-
|
|
32837
|
-
|
|
32838
|
-
|
|
32839
|
-
|
|
32840
|
-
|
|
32841
|
-
|
|
32842
|
-
|
|
32843
|
-
|
|
32844
|
-
|
|
32845
|
-
|
|
32894
|
+
logger?.debug("crash-recovery", "Heartbeat");
|
|
32895
|
+
if (jsonlFilePath) {
|
|
32896
|
+
const heartbeatEntry = {
|
|
32897
|
+
timestamp: new Date().toISOString(),
|
|
32898
|
+
level: "debug",
|
|
32899
|
+
stage: "heartbeat",
|
|
32900
|
+
message: "Process alive",
|
|
32901
|
+
data: {
|
|
32902
|
+
pid: process.pid,
|
|
32903
|
+
memoryUsageMB: Math.round(process.memoryUsage().heapUsed / 1024 / 1024)
|
|
32904
|
+
}
|
|
32905
|
+
};
|
|
32906
|
+
const line = `${JSON.stringify(heartbeatEntry)}
|
|
32846
32907
|
`;
|
|
32847
|
-
|
|
32908
|
+
appendFileSync2(jsonlFilePath, line);
|
|
32909
|
+
}
|
|
32910
|
+
await statusWriter.update(getTotalCost(), getIterations(), {
|
|
32911
|
+
lastHeartbeat: new Date().toISOString()
|
|
32912
|
+
});
|
|
32848
32913
|
} catch (err) {
|
|
32849
|
-
logger?.warn("crash-recovery", "Failed
|
|
32914
|
+
logger?.warn("crash-recovery", "Failed during heartbeat", { error: err.message });
|
|
32850
32915
|
}
|
|
32851
|
-
}
|
|
32852
|
-
try {
|
|
32853
|
-
await statusWriter.update(getTotalCost(), getIterations(), {
|
|
32854
|
-
lastHeartbeat: new Date().toISOString()
|
|
32855
|
-
});
|
|
32856
|
-
} catch (err) {
|
|
32857
|
-
logger?.warn("crash-recovery", "Failed to update status during heartbeat", {
|
|
32858
|
-
error: err.message
|
|
32859
|
-
});
|
|
32860
|
-
}
|
|
32916
|
+
})().catch(() => {});
|
|
32861
32917
|
}, 60000);
|
|
32862
32918
|
logger?.debug("crash-recovery", "Heartbeat started (60s interval)");
|
|
32863
32919
|
}
|
|
@@ -33016,6 +33072,10 @@ function createSignalHandler(ctx) {
|
|
|
33016
33072
|
}
|
|
33017
33073
|
function createUncaughtExceptionHandler(ctx) {
|
|
33018
33074
|
return async (error48) => {
|
|
33075
|
+
process.stderr.write(`
|
|
33076
|
+
[nax crash] Uncaught exception: ${error48.message}
|
|
33077
|
+
${error48.stack ?? ""}
|
|
33078
|
+
`);
|
|
33019
33079
|
const logger = getSafeLogger();
|
|
33020
33080
|
logger?.error("crash-recovery", "Uncaught exception", {
|
|
33021
33081
|
error: error48.message,
|
|
@@ -33036,6 +33096,10 @@ function createUncaughtExceptionHandler(ctx) {
|
|
|
33036
33096
|
function createUnhandledRejectionHandler(ctx) {
|
|
33037
33097
|
return async (reason) => {
|
|
33038
33098
|
const error48 = reason instanceof Error ? reason : new Error(String(reason));
|
|
33099
|
+
process.stderr.write(`
|
|
33100
|
+
[nax crash] Unhandled rejection: ${error48.message}
|
|
33101
|
+
${error48.stack ?? ""}
|
|
33102
|
+
`);
|
|
33039
33103
|
const logger = getSafeLogger();
|
|
33040
33104
|
logger?.error("crash-recovery", "Unhandled promise rejection", {
|
|
33041
33105
|
error: error48.message,
|
|
@@ -34829,7 +34893,7 @@ function wireEventsWriter(bus, feature, runId, workdir) {
|
|
|
34829
34893
|
const eventsFile = join49(eventsDir, "events.jsonl");
|
|
34830
34894
|
let dirReady = false;
|
|
34831
34895
|
const write = (line) => {
|
|
34832
|
-
(async () => {
|
|
34896
|
+
return (async () => {
|
|
34833
34897
|
try {
|
|
34834
34898
|
if (!dirReady) {
|
|
34835
34899
|
await mkdir2(eventsDir, { recursive: true });
|
|
@@ -34847,16 +34911,30 @@ function wireEventsWriter(bus, feature, runId, workdir) {
|
|
|
34847
34911
|
};
|
|
34848
34912
|
const unsubs = [];
|
|
34849
34913
|
unsubs.push(bus.on("run:started", (_ev) => {
|
|
34850
|
-
write({ ts: new Date().toISOString(), event: "run:started", runId, feature, project });
|
|
34914
|
+
return write({ ts: new Date().toISOString(), event: "run:started", runId, feature, project });
|
|
34851
34915
|
}));
|
|
34852
34916
|
unsubs.push(bus.on("story:started", (ev) => {
|
|
34853
|
-
write({
|
|
34917
|
+
return write({
|
|
34918
|
+
ts: new Date().toISOString(),
|
|
34919
|
+
event: "story:started",
|
|
34920
|
+
runId,
|
|
34921
|
+
feature,
|
|
34922
|
+
project,
|
|
34923
|
+
storyId: ev.storyId
|
|
34924
|
+
});
|
|
34854
34925
|
}));
|
|
34855
34926
|
unsubs.push(bus.on("story:completed", (ev) => {
|
|
34856
|
-
write({
|
|
34927
|
+
return write({
|
|
34928
|
+
ts: new Date().toISOString(),
|
|
34929
|
+
event: "story:completed",
|
|
34930
|
+
runId,
|
|
34931
|
+
feature,
|
|
34932
|
+
project,
|
|
34933
|
+
storyId: ev.storyId
|
|
34934
|
+
});
|
|
34857
34935
|
}));
|
|
34858
34936
|
unsubs.push(bus.on("story:decomposed", (ev) => {
|
|
34859
|
-
write({
|
|
34937
|
+
return write({
|
|
34860
34938
|
ts: new Date().toISOString(),
|
|
34861
34939
|
event: "story:decomposed",
|
|
34862
34940
|
runId,
|
|
@@ -34867,13 +34945,20 @@ function wireEventsWriter(bus, feature, runId, workdir) {
|
|
|
34867
34945
|
});
|
|
34868
34946
|
}));
|
|
34869
34947
|
unsubs.push(bus.on("story:failed", (ev) => {
|
|
34870
|
-
write({
|
|
34948
|
+
return write({
|
|
34949
|
+
ts: new Date().toISOString(),
|
|
34950
|
+
event: "story:failed",
|
|
34951
|
+
runId,
|
|
34952
|
+
feature,
|
|
34953
|
+
project,
|
|
34954
|
+
storyId: ev.storyId
|
|
34955
|
+
});
|
|
34871
34956
|
}));
|
|
34872
34957
|
unsubs.push(bus.on("run:completed", (_ev) => {
|
|
34873
|
-
write({ ts: new Date().toISOString(), event: "on-complete", runId, feature, project });
|
|
34958
|
+
return write({ ts: new Date().toISOString(), event: "on-complete", runId, feature, project });
|
|
34874
34959
|
}));
|
|
34875
34960
|
unsubs.push(bus.on("run:paused", (ev) => {
|
|
34876
|
-
write({
|
|
34961
|
+
return write({
|
|
34877
34962
|
ts: new Date().toISOString(),
|
|
34878
34963
|
event: "run:paused",
|
|
34879
34964
|
runId,
|
|
@@ -34895,44 +34980,44 @@ var init_events_writer = __esm(() => {
|
|
|
34895
34980
|
function wireHooks(bus, hooks, workdir, feature) {
|
|
34896
34981
|
const logger = getSafeLogger();
|
|
34897
34982
|
const safe = (name, fn) => {
|
|
34898
|
-
fn().catch((err) => logger?.warn("hooks-subscriber", `Hook "${name}" failed`, { error: String(err) }));
|
|
34983
|
+
return fn().catch((err) => logger?.warn("hooks-subscriber", `Hook "${name}" failed`, { error: String(err) })).catch(() => {});
|
|
34899
34984
|
};
|
|
34900
34985
|
const unsubs = [];
|
|
34901
34986
|
unsubs.push(bus.on("run:started", (ev) => {
|
|
34902
|
-
safe("on-start", () => fireHook(hooks, "on-start", hookCtx(feature, { status: "running" }), workdir));
|
|
34987
|
+
return safe("on-start", () => fireHook(hooks, "on-start", hookCtx(feature, { status: "running" }), workdir));
|
|
34903
34988
|
}));
|
|
34904
34989
|
unsubs.push(bus.on("story:started", (ev) => {
|
|
34905
|
-
safe("on-story-start", () => fireHook(hooks, "on-story-start", hookCtx(feature, { storyId: ev.storyId, model: ev.modelTier, agent: ev.agent }), workdir));
|
|
34990
|
+
return safe("on-story-start", () => fireHook(hooks, "on-story-start", hookCtx(feature, { storyId: ev.storyId, model: ev.modelTier, agent: ev.agent }), workdir));
|
|
34906
34991
|
}));
|
|
34907
34992
|
unsubs.push(bus.on("story:completed", (ev) => {
|
|
34908
|
-
safe("on-story-complete", () => fireHook(hooks, "on-story-complete", hookCtx(feature, { storyId: ev.storyId, status: "passed", cost: ev.cost }), workdir));
|
|
34993
|
+
return safe("on-story-complete", () => fireHook(hooks, "on-story-complete", hookCtx(feature, { storyId: ev.storyId, status: "passed", cost: ev.cost }), workdir));
|
|
34909
34994
|
}));
|
|
34910
34995
|
unsubs.push(bus.on("story:decomposed", (ev) => {
|
|
34911
|
-
safe("on-story-complete (decomposed)", () => fireHook(hooks, "on-story-complete", hookCtx(feature, { storyId: ev.storyId, status: "decomposed", subStoryCount: ev.subStoryCount }), workdir));
|
|
34996
|
+
return safe("on-story-complete (decomposed)", () => fireHook(hooks, "on-story-complete", hookCtx(feature, { storyId: ev.storyId, status: "decomposed", subStoryCount: ev.subStoryCount }), workdir));
|
|
34912
34997
|
}));
|
|
34913
34998
|
unsubs.push(bus.on("story:failed", (ev) => {
|
|
34914
|
-
safe("on-story-fail", () => fireHook(hooks, "on-story-fail", hookCtx(feature, { storyId: ev.storyId, status: "failed", reason: ev.reason }), workdir));
|
|
34999
|
+
return safe("on-story-fail", () => fireHook(hooks, "on-story-fail", hookCtx(feature, { storyId: ev.storyId, status: "failed", reason: ev.reason }), workdir));
|
|
34915
35000
|
}));
|
|
34916
35001
|
unsubs.push(bus.on("story:paused", (ev) => {
|
|
34917
|
-
safe("on-pause (story)", () => fireHook(hooks, "on-pause", hookCtx(feature, { storyId: ev.storyId, reason: ev.reason, cost: ev.cost }), workdir));
|
|
35002
|
+
return safe("on-pause (story)", () => fireHook(hooks, "on-pause", hookCtx(feature, { storyId: ev.storyId, reason: ev.reason, cost: ev.cost }), workdir));
|
|
34918
35003
|
}));
|
|
34919
35004
|
unsubs.push(bus.on("run:paused", (ev) => {
|
|
34920
|
-
safe("on-pause (run)", () => fireHook(hooks, "on-pause", hookCtx(feature, { storyId: ev.storyId, reason: ev.reason, cost: ev.cost }), workdir));
|
|
35005
|
+
return safe("on-pause (run)", () => fireHook(hooks, "on-pause", hookCtx(feature, { storyId: ev.storyId, reason: ev.reason, cost: ev.cost }), workdir));
|
|
34921
35006
|
}));
|
|
34922
35007
|
unsubs.push(bus.on("run:completed", (ev) => {
|
|
34923
|
-
safe("on-complete", () => fireHook(hooks, "on-complete", hookCtx(feature, { status: "complete", cost: ev.totalCost ?? 0 }), workdir));
|
|
35008
|
+
return safe("on-complete", () => fireHook(hooks, "on-complete", hookCtx(feature, { status: "complete", cost: ev.totalCost ?? 0 }), workdir));
|
|
34924
35009
|
}));
|
|
34925
35010
|
unsubs.push(bus.on("run:resumed", (ev) => {
|
|
34926
|
-
safe("on-resume", () => fireHook(hooks, "on-resume", hookCtx(feature, { status: "running" }), workdir));
|
|
35011
|
+
return safe("on-resume", () => fireHook(hooks, "on-resume", hookCtx(feature, { status: "running" }), workdir));
|
|
34927
35012
|
}));
|
|
34928
35013
|
unsubs.push(bus.on("story:completed", (ev) => {
|
|
34929
|
-
safe("on-session-end (completed)", () => fireHook(hooks, "on-session-end", hookCtx(feature, { storyId: ev.storyId, status: "passed" }), workdir));
|
|
35014
|
+
return safe("on-session-end (completed)", () => fireHook(hooks, "on-session-end", hookCtx(feature, { storyId: ev.storyId, status: "passed" }), workdir));
|
|
34930
35015
|
}));
|
|
34931
35016
|
unsubs.push(bus.on("story:failed", (ev) => {
|
|
34932
|
-
safe("on-session-end (failed)", () => fireHook(hooks, "on-session-end", hookCtx(feature, { storyId: ev.storyId, status: "failed" }), workdir));
|
|
35017
|
+
return safe("on-session-end (failed)", () => fireHook(hooks, "on-session-end", hookCtx(feature, { storyId: ev.storyId, status: "failed" }), workdir));
|
|
34933
35018
|
}));
|
|
34934
35019
|
unsubs.push(bus.on("run:errored", (ev) => {
|
|
34935
|
-
safe("on-error", () => fireHook(hooks, "on-error", hookCtx(feature, { reason: ev.reason }), workdir));
|
|
35020
|
+
return safe("on-error", () => fireHook(hooks, "on-error", hookCtx(feature, { reason: ev.reason }), workdir));
|
|
34936
35021
|
}));
|
|
34937
35022
|
return () => {
|
|
34938
35023
|
for (const u of unsubs)
|
|
@@ -35007,7 +35092,7 @@ function wireRegistry(bus, feature, runId, workdir) {
|
|
|
35007
35092
|
const runDir = join50(homedir6(), ".nax", "runs", `${project}-${feature}-${runId}`);
|
|
35008
35093
|
const metaFile = join50(runDir, "meta.json");
|
|
35009
35094
|
const unsub = bus.on("run:started", (_ev) => {
|
|
35010
|
-
(async () => {
|
|
35095
|
+
return (async () => {
|
|
35011
35096
|
try {
|
|
35012
35097
|
await mkdir3(runDir, { recursive: true });
|
|
35013
35098
|
const meta3 = {
|
|
@@ -35038,11 +35123,11 @@ var init_registry3 = __esm(() => {
|
|
|
35038
35123
|
function wireReporters(bus, pluginRegistry, runId, startTime) {
|
|
35039
35124
|
const logger = getSafeLogger();
|
|
35040
35125
|
const safe = (name, fn) => {
|
|
35041
|
-
fn().catch((err) => logger?.warn("reporters-subscriber", `Reporter "${name}" error`, { error: String(err) }));
|
|
35126
|
+
return fn().catch((err) => logger?.warn("reporters-subscriber", `Reporter "${name}" error`, { error: String(err) })).catch(() => {});
|
|
35042
35127
|
};
|
|
35043
35128
|
const unsubs = [];
|
|
35044
35129
|
unsubs.push(bus.on("run:started", (ev) => {
|
|
35045
|
-
safe("onRunStart", async () => {
|
|
35130
|
+
return safe("onRunStart", async () => {
|
|
35046
35131
|
const reporters = pluginRegistry.getReporters();
|
|
35047
35132
|
for (const r of reporters) {
|
|
35048
35133
|
if (r.onRunStart) {
|
|
@@ -35061,7 +35146,7 @@ function wireReporters(bus, pluginRegistry, runId, startTime) {
|
|
|
35061
35146
|
});
|
|
35062
35147
|
}));
|
|
35063
35148
|
unsubs.push(bus.on("story:completed", (ev) => {
|
|
35064
|
-
safe("onStoryComplete(completed)", async () => {
|
|
35149
|
+
return safe("onStoryComplete(completed)", async () => {
|
|
35065
35150
|
const reporters = pluginRegistry.getReporters();
|
|
35066
35151
|
for (const r of reporters) {
|
|
35067
35152
|
if (r.onStoryComplete) {
|
|
@@ -35083,7 +35168,7 @@ function wireReporters(bus, pluginRegistry, runId, startTime) {
|
|
|
35083
35168
|
});
|
|
35084
35169
|
}));
|
|
35085
35170
|
unsubs.push(bus.on("story:failed", (ev) => {
|
|
35086
|
-
safe("onStoryComplete(failed)", async () => {
|
|
35171
|
+
return safe("onStoryComplete(failed)", async () => {
|
|
35087
35172
|
const reporters = pluginRegistry.getReporters();
|
|
35088
35173
|
for (const r of reporters) {
|
|
35089
35174
|
if (r.onStoryComplete) {
|
|
@@ -35105,7 +35190,7 @@ function wireReporters(bus, pluginRegistry, runId, startTime) {
|
|
|
35105
35190
|
});
|
|
35106
35191
|
}));
|
|
35107
35192
|
unsubs.push(bus.on("story:paused", (ev) => {
|
|
35108
|
-
safe("onStoryComplete(paused)", async () => {
|
|
35193
|
+
return safe("onStoryComplete(paused)", async () => {
|
|
35109
35194
|
const reporters = pluginRegistry.getReporters();
|
|
35110
35195
|
for (const r of reporters) {
|
|
35111
35196
|
if (r.onStoryComplete) {
|
|
@@ -35127,7 +35212,7 @@ function wireReporters(bus, pluginRegistry, runId, startTime) {
|
|
|
35127
35212
|
});
|
|
35128
35213
|
}));
|
|
35129
35214
|
unsubs.push(bus.on("run:completed", (ev) => {
|
|
35130
|
-
safe("onRunEnd", async () => {
|
|
35215
|
+
return safe("onRunEnd", async () => {
|
|
35131
35216
|
const reporters = pluginRegistry.getReporters();
|
|
35132
35217
|
for (const r of reporters) {
|
|
35133
35218
|
if (r.onRunEnd) {
|
|
@@ -68924,7 +69009,14 @@ async function planCommand(workdir, config2, options) {
|
|
|
68924
69009
|
if (entry)
|
|
68925
69010
|
autoModel = resolveModel2(entry).model;
|
|
68926
69011
|
} catch {}
|
|
68927
|
-
rawResponse = await cliAdapter.complete(prompt, {
|
|
69012
|
+
rawResponse = await cliAdapter.complete(prompt, {
|
|
69013
|
+
model: autoModel,
|
|
69014
|
+
jsonMode: true,
|
|
69015
|
+
workdir,
|
|
69016
|
+
config: config2,
|
|
69017
|
+
featureName: options.feature,
|
|
69018
|
+
sessionRole: "plan"
|
|
69019
|
+
});
|
|
68928
69020
|
try {
|
|
68929
69021
|
const envelope = JSON.parse(rawResponse);
|
|
68930
69022
|
if (envelope?.type === "result" && typeof envelope?.result === "string") {
|
|
@@ -71349,7 +71441,7 @@ Runs:
|
|
|
71349
71441
|
const summary = await extractRunSummary(filePath);
|
|
71350
71442
|
const timestamp = file2.replace(".jsonl", "");
|
|
71351
71443
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
71352
|
-
const duration3 = summary ?
|
|
71444
|
+
const duration3 = summary ? formatDuration(summary.durationMs) : "?";
|
|
71353
71445
|
const cost = summary ? `$${summary.totalCost.toFixed(4)}` : "$?.????";
|
|
71354
71446
|
const status = summary ? summary.failed === 0 ? source_default.green("\u2713") : source_default.red("\u2717") : "?";
|
|
71355
71447
|
console.log(` ${timestamp} ${stories.padEnd(7)} ${duration3.padEnd(8)} ${cost.padEnd(8)} ${status}`);
|
|
@@ -71443,17 +71535,6 @@ function shouldDisplayEntry(entry, options) {
|
|
|
71443
71535
|
}
|
|
71444
71536
|
return true;
|
|
71445
71537
|
}
|
|
71446
|
-
function formatDuration2(ms) {
|
|
71447
|
-
if (ms < 1000) {
|
|
71448
|
-
return `${ms}ms`;
|
|
71449
|
-
}
|
|
71450
|
-
if (ms < 60000) {
|
|
71451
|
-
return `${(ms / 1000).toFixed(1)}s`;
|
|
71452
|
-
}
|
|
71453
|
-
const minutes = Math.floor(ms / 60000);
|
|
71454
|
-
const seconds = Math.floor(ms % 60000 / 1000);
|
|
71455
|
-
return `${minutes}m${seconds}s`;
|
|
71456
|
-
}
|
|
71457
71538
|
|
|
71458
71539
|
// src/commands/logs.ts
|
|
71459
71540
|
async function logsCommand(options) {
|
|
@@ -71555,7 +71636,7 @@ var DEFAULT_LIMIT = 20;
|
|
|
71555
71636
|
var _runsCmdDeps = {
|
|
71556
71637
|
getRunsDir
|
|
71557
71638
|
};
|
|
71558
|
-
function
|
|
71639
|
+
function formatDuration2(ms) {
|
|
71559
71640
|
if (ms <= 0)
|
|
71560
71641
|
return "-";
|
|
71561
71642
|
const minutes = Math.floor(ms / 60000);
|
|
@@ -71668,7 +71749,7 @@ async function runsCommand(options = {}) {
|
|
|
71668
71749
|
pad3(row.feature, COL.feature),
|
|
71669
71750
|
pad3(colored, COL.status + (colored.length - visibleLength(colored))),
|
|
71670
71751
|
pad3(`${row.passed}/${row.total}`, COL.stories),
|
|
71671
|
-
pad3(
|
|
71752
|
+
pad3(formatDuration2(row.durationMs), COL.duration),
|
|
71672
71753
|
formatDate(row.registeredAt)
|
|
71673
71754
|
].join(" ");
|
|
71674
71755
|
console.log(line);
|
|
@@ -71991,7 +72072,8 @@ async function runExecutionPhase(options, prd, pluginRegistry) {
|
|
|
71991
72072
|
startTime: options.startTime,
|
|
71992
72073
|
batchPlan,
|
|
71993
72074
|
agentGetFn: options.agentGetFn,
|
|
71994
|
-
pidRegistry: options.pidRegistry
|
|
72075
|
+
pidRegistry: options.pidRegistry,
|
|
72076
|
+
interactionChain: options.interactionChain
|
|
71995
72077
|
}, prd);
|
|
71996
72078
|
prd = sequentialResult.prd;
|
|
71997
72079
|
iterations = sequentialResult.iterations;
|
|
@@ -72117,7 +72199,8 @@ async function run(options) {
|
|
|
72117
72199
|
parallel,
|
|
72118
72200
|
runParallelExecution: _runnerDeps.runParallelExecution ?? undefined,
|
|
72119
72201
|
agentGetFn,
|
|
72120
|
-
pidRegistry
|
|
72202
|
+
pidRegistry,
|
|
72203
|
+
interactionChain
|
|
72121
72204
|
}, prd, pluginRegistry);
|
|
72122
72205
|
prd = executionResult.prd;
|
|
72123
72206
|
iterations = executionResult.iterations;
|