@nathapp/nax 0.54.2 → 0.54.4
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 +281 -147
- 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.4",
|
|
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("d0da600"))
|
|
22429
|
+
return "d0da600";
|
|
22387
22430
|
} catch {}
|
|
22388
22431
|
try {
|
|
22389
22432
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -23101,28 +23144,38 @@ class TelegramInteractionPlugin {
|
|
|
23101
23144
|
if (!this.botToken || !this.chatId) {
|
|
23102
23145
|
throw new Error("Telegram plugin not initialized");
|
|
23103
23146
|
}
|
|
23104
|
-
const
|
|
23147
|
+
const header = this.buildHeader(request);
|
|
23105
23148
|
const keyboard = this.buildKeyboard(request);
|
|
23149
|
+
const body = this.buildBody(request);
|
|
23150
|
+
const chunks = this.splitText(body, MAX_MESSAGE_CHARS - header.length - 10);
|
|
23106
23151
|
try {
|
|
23107
|
-
const
|
|
23108
|
-
|
|
23109
|
-
|
|
23110
|
-
|
|
23111
|
-
|
|
23112
|
-
|
|
23113
|
-
|
|
23114
|
-
|
|
23115
|
-
|
|
23116
|
-
|
|
23117
|
-
|
|
23118
|
-
|
|
23119
|
-
|
|
23120
|
-
|
|
23121
|
-
|
|
23122
|
-
|
|
23123
|
-
|
|
23152
|
+
const sentIds = [];
|
|
23153
|
+
for (let i = 0;i < chunks.length; i++) {
|
|
23154
|
+
const isLast = i === chunks.length - 1;
|
|
23155
|
+
const partLabel = chunks.length > 1 ? `[${i + 1}/${chunks.length}] ` : "";
|
|
23156
|
+
const text = `${header}
|
|
23157
|
+
${partLabel}${chunks[i]}`;
|
|
23158
|
+
const response = await fetch(`https://api.telegram.org/bot${this.botToken}/sendMessage`, {
|
|
23159
|
+
method: "POST",
|
|
23160
|
+
headers: { "Content-Type": "application/json" },
|
|
23161
|
+
body: JSON.stringify({
|
|
23162
|
+
chat_id: this.chatId,
|
|
23163
|
+
text,
|
|
23164
|
+
reply_markup: isLast && keyboard ? { inline_keyboard: keyboard } : undefined,
|
|
23165
|
+
parse_mode: "Markdown"
|
|
23166
|
+
})
|
|
23167
|
+
});
|
|
23168
|
+
if (!response.ok) {
|
|
23169
|
+
const errorBody = await response.text().catch(() => "");
|
|
23170
|
+
throw new Error(`Telegram API error (${response.status}): ${errorBody || response.statusText}`);
|
|
23171
|
+
}
|
|
23172
|
+
const data = await response.json();
|
|
23173
|
+
if (!data.ok) {
|
|
23174
|
+
throw new Error(`Telegram API returned ok=false: ${JSON.stringify(data)}`);
|
|
23175
|
+
}
|
|
23176
|
+
sentIds.push(data.result.message_id);
|
|
23124
23177
|
}
|
|
23125
|
-
this.pendingMessages.set(request.id,
|
|
23178
|
+
this.pendingMessages.set(request.id, sentIds);
|
|
23126
23179
|
} catch (err) {
|
|
23127
23180
|
const msg = err instanceof Error ? err.message : String(err);
|
|
23128
23181
|
throw new Error(`Failed to send Telegram message: ${msg}`);
|
|
@@ -23159,10 +23212,9 @@ class TelegramInteractionPlugin {
|
|
|
23159
23212
|
await this.sendTimeoutMessage(requestId);
|
|
23160
23213
|
this.pendingMessages.delete(requestId);
|
|
23161
23214
|
}
|
|
23162
|
-
|
|
23215
|
+
buildHeader(request) {
|
|
23163
23216
|
const emoji3 = this.getStageEmoji(request.stage);
|
|
23164
23217
|
let text = `${emoji3} *${request.stage.toUpperCase()}*
|
|
23165
|
-
|
|
23166
23218
|
`;
|
|
23167
23219
|
text += `*Feature:* ${request.featureName}
|
|
23168
23220
|
`;
|
|
@@ -23171,11 +23223,15 @@ class TelegramInteractionPlugin {
|
|
|
23171
23223
|
`;
|
|
23172
23224
|
}
|
|
23173
23225
|
text += `
|
|
23174
|
-
|
|
23226
|
+
`;
|
|
23227
|
+
return text;
|
|
23228
|
+
}
|
|
23229
|
+
buildBody(request) {
|
|
23230
|
+
let text = `${this.sanitizeMarkdown(request.summary)}
|
|
23175
23231
|
`;
|
|
23176
23232
|
if (request.detail) {
|
|
23177
23233
|
text += `
|
|
23178
|
-
${request.detail}
|
|
23234
|
+
${this.sanitizeMarkdown(request.detail)}
|
|
23179
23235
|
`;
|
|
23180
23236
|
}
|
|
23181
23237
|
if (request.options && request.options.length > 0) {
|
|
@@ -23183,8 +23239,8 @@ ${request.detail}
|
|
|
23183
23239
|
*Options:*
|
|
23184
23240
|
`;
|
|
23185
23241
|
for (const opt of request.options) {
|
|
23186
|
-
const desc = opt.description ? `
|
|
23187
|
-
text += `
|
|
23242
|
+
const desc = opt.description ? ` - ${this.sanitizeMarkdown(opt.description)}` : "";
|
|
23243
|
+
text += ` - ${opt.label}${desc}
|
|
23188
23244
|
`;
|
|
23189
23245
|
}
|
|
23190
23246
|
}
|
|
@@ -23195,6 +23251,30 @@ ${request.detail}
|
|
|
23195
23251
|
}
|
|
23196
23252
|
return text;
|
|
23197
23253
|
}
|
|
23254
|
+
sanitizeMarkdown(text) {
|
|
23255
|
+
return text.replace(/\\(?=[_*`\[])/g, "\\\\").replace(/_/g, "\\_").replace(/`/g, "\\`").replace(/\*/g, "\\*").replace(/\[/g, "\\[");
|
|
23256
|
+
}
|
|
23257
|
+
splitText(text, maxChars) {
|
|
23258
|
+
if (text.length <= maxChars)
|
|
23259
|
+
return [text];
|
|
23260
|
+
const chunks = [];
|
|
23261
|
+
let remaining = text;
|
|
23262
|
+
while (remaining.length > maxChars) {
|
|
23263
|
+
const slice = remaining.slice(0, maxChars);
|
|
23264
|
+
const lastNewline = slice.lastIndexOf(`
|
|
23265
|
+
`);
|
|
23266
|
+
if (lastNewline > maxChars * 0.5) {
|
|
23267
|
+
chunks.push(remaining.slice(0, lastNewline));
|
|
23268
|
+
remaining = remaining.slice(lastNewline + 1);
|
|
23269
|
+
} else {
|
|
23270
|
+
chunks.push(slice);
|
|
23271
|
+
remaining = remaining.slice(maxChars);
|
|
23272
|
+
}
|
|
23273
|
+
}
|
|
23274
|
+
if (remaining.length > 0)
|
|
23275
|
+
chunks.push(remaining);
|
|
23276
|
+
return chunks;
|
|
23277
|
+
}
|
|
23198
23278
|
buildKeyboard(request) {
|
|
23199
23279
|
switch (request.type) {
|
|
23200
23280
|
case "confirm":
|
|
@@ -23302,8 +23382,11 @@ ${request.detail}
|
|
|
23302
23382
|
};
|
|
23303
23383
|
}
|
|
23304
23384
|
if (update.message?.text) {
|
|
23305
|
-
const
|
|
23306
|
-
if (!
|
|
23385
|
+
const messageIds = this.pendingMessages.get(requestId);
|
|
23386
|
+
if (!messageIds)
|
|
23387
|
+
return null;
|
|
23388
|
+
const replyToId = update.message.reply_to_message?.message_id;
|
|
23389
|
+
if (replyToId !== undefined && !messageIds.includes(replyToId))
|
|
23307
23390
|
return null;
|
|
23308
23391
|
return {
|
|
23309
23392
|
requestId,
|
|
@@ -23329,20 +23412,20 @@ ${request.detail}
|
|
|
23329
23412
|
} catch {}
|
|
23330
23413
|
}
|
|
23331
23414
|
async sendTimeoutMessage(requestId) {
|
|
23332
|
-
const
|
|
23333
|
-
if (!
|
|
23415
|
+
const messageIds = this.pendingMessages.get(requestId);
|
|
23416
|
+
if (!messageIds || !this.botToken || !this.chatId) {
|
|
23334
23417
|
this.pendingMessages.delete(requestId);
|
|
23335
23418
|
return;
|
|
23336
23419
|
}
|
|
23420
|
+
const lastId = messageIds[messageIds.length - 1];
|
|
23337
23421
|
try {
|
|
23338
23422
|
await fetch(`https://api.telegram.org/bot${this.botToken}/editMessageText`, {
|
|
23339
23423
|
method: "POST",
|
|
23340
23424
|
headers: { "Content-Type": "application/json" },
|
|
23341
23425
|
body: JSON.stringify({
|
|
23342
23426
|
chat_id: this.chatId,
|
|
23343
|
-
message_id:
|
|
23344
|
-
text: "\u23F1
|
|
23345
|
-
parse_mode: "Markdown"
|
|
23427
|
+
message_id: lastId,
|
|
23428
|
+
text: "\u23F1 EXPIRED \u2014 Interaction timed out"
|
|
23346
23429
|
})
|
|
23347
23430
|
});
|
|
23348
23431
|
} catch {} finally {
|
|
@@ -23350,7 +23433,7 @@ ${request.detail}
|
|
|
23350
23433
|
}
|
|
23351
23434
|
}
|
|
23352
23435
|
}
|
|
23353
|
-
var TelegramConfigSchema;
|
|
23436
|
+
var MAX_MESSAGE_CHARS = 4000, TelegramConfigSchema;
|
|
23354
23437
|
var init_telegram = __esm(() => {
|
|
23355
23438
|
init_zod();
|
|
23356
23439
|
TelegramConfigSchema = exports_external.object({
|
|
@@ -23627,7 +23710,10 @@ class AutoInteractionPlugin {
|
|
|
23627
23710
|
const output = await adapter.complete(prompt, {
|
|
23628
23711
|
...modelArg && { model: modelArg },
|
|
23629
23712
|
jsonMode: true,
|
|
23630
|
-
...this.config.naxConfig && { config: this.config.naxConfig }
|
|
23713
|
+
...this.config.naxConfig && { config: this.config.naxConfig },
|
|
23714
|
+
featureName: request.featureName,
|
|
23715
|
+
storyId: request.storyId,
|
|
23716
|
+
sessionRole: "auto"
|
|
23631
23717
|
});
|
|
23632
23718
|
return this.parseResponse(output);
|
|
23633
23719
|
}
|
|
@@ -24368,31 +24454,33 @@ ${stderr}` };
|
|
|
24368
24454
|
}
|
|
24369
24455
|
let totalCriteria = 0;
|
|
24370
24456
|
let testableCount = 0;
|
|
24371
|
-
const
|
|
24372
|
-
const
|
|
24373
|
-
|
|
24374
|
-
|
|
24375
|
-
|
|
24376
|
-
|
|
24377
|
-
|
|
24378
|
-
|
|
24379
|
-
|
|
24380
|
-
|
|
24381
|
-
|
|
24382
|
-
|
|
24457
|
+
const fingerprint = computeACFingerprint(allCriteria);
|
|
24458
|
+
const meta3 = await _acceptanceSetupDeps.readMeta(metaPath);
|
|
24459
|
+
getSafeLogger()?.debug("acceptance-setup", "Fingerprint check", {
|
|
24460
|
+
currentFingerprint: fingerprint,
|
|
24461
|
+
storedFingerprint: meta3?.acFingerprint ?? "none",
|
|
24462
|
+
match: meta3?.acFingerprint === fingerprint
|
|
24463
|
+
});
|
|
24464
|
+
let shouldGenerate = false;
|
|
24465
|
+
if (!meta3 || meta3.acFingerprint !== fingerprint) {
|
|
24466
|
+
if (!meta3) {
|
|
24467
|
+
getSafeLogger()?.info("acceptance-setup", "No acceptance meta \u2014 generating acceptance tests");
|
|
24468
|
+
} else {
|
|
24383
24469
|
getSafeLogger()?.info("acceptance-setup", "ACs changed \u2014 regenerating acceptance tests", {
|
|
24384
|
-
reason:
|
|
24470
|
+
reason: "fingerprint mismatch",
|
|
24471
|
+
currentFingerprint: fingerprint,
|
|
24472
|
+
storedFingerprint: meta3.acFingerprint
|
|
24385
24473
|
});
|
|
24386
|
-
|
|
24387
|
-
|
|
24388
|
-
|
|
24389
|
-
|
|
24390
|
-
|
|
24474
|
+
}
|
|
24475
|
+
for (const { testPath } of testPaths) {
|
|
24476
|
+
if (await _acceptanceSetupDeps.fileExists(testPath)) {
|
|
24477
|
+
await _acceptanceSetupDeps.copyFile(testPath, `${testPath}.bak`);
|
|
24478
|
+
await _acceptanceSetupDeps.deleteFile(testPath);
|
|
24391
24479
|
}
|
|
24392
|
-
shouldGenerate = true;
|
|
24393
|
-
} else {
|
|
24394
|
-
getSafeLogger()?.info("acceptance-setup", "Reusing existing acceptance tests (fingerprint match)");
|
|
24395
24480
|
}
|
|
24481
|
+
shouldGenerate = true;
|
|
24482
|
+
} else {
|
|
24483
|
+
getSafeLogger()?.info("acceptance-setup", "Reusing existing acceptance tests (fingerprint match)");
|
|
24396
24484
|
}
|
|
24397
24485
|
if (shouldGenerate) {
|
|
24398
24486
|
totalCriteria = allCriteria.length;
|
|
@@ -24404,6 +24492,8 @@ ${stderr}` };
|
|
|
24404
24492
|
for (const story of nonFixStories) {
|
|
24405
24493
|
const storyRefined = await _acceptanceSetupDeps.refine(story.acceptanceCriteria, {
|
|
24406
24494
|
storyId: story.id,
|
|
24495
|
+
featureName: ctx.prd.feature,
|
|
24496
|
+
workdir: ctx.workdir,
|
|
24407
24497
|
codebaseContext: "",
|
|
24408
24498
|
config: ctx.config,
|
|
24409
24499
|
testStrategy: ctx.config.acceptance.testStrategy,
|
|
@@ -24439,10 +24529,10 @@ ${stderr}` };
|
|
|
24439
24529
|
});
|
|
24440
24530
|
await _acceptanceSetupDeps.writeFile(testPath, result.testCode);
|
|
24441
24531
|
}
|
|
24442
|
-
const
|
|
24532
|
+
const fingerprint2 = computeACFingerprint(allCriteria);
|
|
24443
24533
|
await _acceptanceSetupDeps.writeMeta(metaPath, {
|
|
24444
24534
|
generatedAt: new Date().toISOString(),
|
|
24445
|
-
acFingerprint:
|
|
24535
|
+
acFingerprint: fingerprint2,
|
|
24446
24536
|
storyCount: ctx.prd.userStories.length,
|
|
24447
24537
|
acCount: totalCriteria,
|
|
24448
24538
|
generator: "nax"
|
|
@@ -24907,6 +24997,16 @@ async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, mo
|
|
|
24907
24997
|
storyId: story.id,
|
|
24908
24998
|
durationMs: durationMs2
|
|
24909
24999
|
});
|
|
25000
|
+
logger?.debug("review", "Semantic review findings", {
|
|
25001
|
+
storyId: story.id,
|
|
25002
|
+
findings: parsed.findings.map((f) => ({
|
|
25003
|
+
severity: f.severity,
|
|
25004
|
+
file: f.file,
|
|
25005
|
+
line: f.line,
|
|
25006
|
+
issue: f.issue,
|
|
25007
|
+
suggestion: f.suggestion
|
|
25008
|
+
}))
|
|
25009
|
+
});
|
|
24910
25010
|
const output = `Semantic review failed:
|
|
24911
25011
|
|
|
24912
25012
|
${formatFindings(parsed.findings)}`;
|
|
@@ -32828,36 +32928,32 @@ import { appendFileSync as appendFileSync2 } from "fs";
|
|
|
32828
32928
|
function startHeartbeat(statusWriter, getTotalCost, getIterations, jsonlFilePath) {
|
|
32829
32929
|
const logger = getSafeLogger();
|
|
32830
32930
|
stopHeartbeat();
|
|
32831
|
-
heartbeatTimer = setInterval(
|
|
32832
|
-
|
|
32833
|
-
if (jsonlFilePath) {
|
|
32931
|
+
heartbeatTimer = setInterval(() => {
|
|
32932
|
+
(async () => {
|
|
32834
32933
|
try {
|
|
32835
|
-
|
|
32836
|
-
|
|
32837
|
-
|
|
32838
|
-
|
|
32839
|
-
|
|
32840
|
-
|
|
32841
|
-
|
|
32842
|
-
|
|
32843
|
-
|
|
32844
|
-
|
|
32845
|
-
|
|
32934
|
+
logger?.debug("crash-recovery", "Heartbeat");
|
|
32935
|
+
if (jsonlFilePath) {
|
|
32936
|
+
const heartbeatEntry = {
|
|
32937
|
+
timestamp: new Date().toISOString(),
|
|
32938
|
+
level: "debug",
|
|
32939
|
+
stage: "heartbeat",
|
|
32940
|
+
message: "Process alive",
|
|
32941
|
+
data: {
|
|
32942
|
+
pid: process.pid,
|
|
32943
|
+
memoryUsageMB: Math.round(process.memoryUsage().heapUsed / 1024 / 1024)
|
|
32944
|
+
}
|
|
32945
|
+
};
|
|
32946
|
+
const line = `${JSON.stringify(heartbeatEntry)}
|
|
32846
32947
|
`;
|
|
32847
|
-
|
|
32948
|
+
appendFileSync2(jsonlFilePath, line);
|
|
32949
|
+
}
|
|
32950
|
+
await statusWriter.update(getTotalCost(), getIterations(), {
|
|
32951
|
+
lastHeartbeat: new Date().toISOString()
|
|
32952
|
+
});
|
|
32848
32953
|
} catch (err) {
|
|
32849
|
-
logger?.warn("crash-recovery", "Failed
|
|
32954
|
+
logger?.warn("crash-recovery", "Failed during heartbeat", { error: err.message });
|
|
32850
32955
|
}
|
|
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
|
-
}
|
|
32956
|
+
})().catch(() => {});
|
|
32861
32957
|
}, 60000);
|
|
32862
32958
|
logger?.debug("crash-recovery", "Heartbeat started (60s interval)");
|
|
32863
32959
|
}
|
|
@@ -33016,6 +33112,10 @@ function createSignalHandler(ctx) {
|
|
|
33016
33112
|
}
|
|
33017
33113
|
function createUncaughtExceptionHandler(ctx) {
|
|
33018
33114
|
return async (error48) => {
|
|
33115
|
+
process.stderr.write(`
|
|
33116
|
+
[nax crash] Uncaught exception: ${error48.message}
|
|
33117
|
+
${error48.stack ?? ""}
|
|
33118
|
+
`);
|
|
33019
33119
|
const logger = getSafeLogger();
|
|
33020
33120
|
logger?.error("crash-recovery", "Uncaught exception", {
|
|
33021
33121
|
error: error48.message,
|
|
@@ -33036,6 +33136,10 @@ function createUncaughtExceptionHandler(ctx) {
|
|
|
33036
33136
|
function createUnhandledRejectionHandler(ctx) {
|
|
33037
33137
|
return async (reason) => {
|
|
33038
33138
|
const error48 = reason instanceof Error ? reason : new Error(String(reason));
|
|
33139
|
+
process.stderr.write(`
|
|
33140
|
+
[nax crash] Unhandled rejection: ${error48.message}
|
|
33141
|
+
${error48.stack ?? ""}
|
|
33142
|
+
`);
|
|
33039
33143
|
const logger = getSafeLogger();
|
|
33040
33144
|
logger?.error("crash-recovery", "Unhandled promise rejection", {
|
|
33041
33145
|
error: error48.message,
|
|
@@ -34417,7 +34521,7 @@ __export(exports_parallel_executor_rectify, {
|
|
|
34417
34521
|
});
|
|
34418
34522
|
import path15 from "path";
|
|
34419
34523
|
async function rectifyConflictedStory(options) {
|
|
34420
|
-
const { storyId, workdir, config: config2, hooks, pluginRegistry, prd, eventEmitter } = options;
|
|
34524
|
+
const { storyId, workdir, config: config2, hooks, pluginRegistry, prd, eventEmitter, agentGetFn } = options;
|
|
34421
34525
|
const logger = getSafeLogger();
|
|
34422
34526
|
logger?.info("parallel", "Rectifying story on updated base", { storyId, attempt: "rectification" });
|
|
34423
34527
|
try {
|
|
@@ -34449,7 +34553,8 @@ async function rectifyConflictedStory(options) {
|
|
|
34449
34553
|
hooks,
|
|
34450
34554
|
plugins: pluginRegistry,
|
|
34451
34555
|
storyStartTime: new Date().toISOString(),
|
|
34452
|
-
routing
|
|
34556
|
+
routing,
|
|
34557
|
+
agentGetFn
|
|
34453
34558
|
};
|
|
34454
34559
|
const pipelineResult = await runPipeline2(defaultPipeline2, pipelineContext, eventEmitter);
|
|
34455
34560
|
const cost = pipelineResult.context.agentResult?.estimatedCost ?? 0;
|
|
@@ -34485,7 +34590,7 @@ var init_parallel_executor_rectify = __esm(() => {
|
|
|
34485
34590
|
// src/execution/parallel-executor-rectification-pass.ts
|
|
34486
34591
|
async function runRectificationPass(conflictedStories, options, prd, rectifyConflictedStory2) {
|
|
34487
34592
|
const logger = getSafeLogger();
|
|
34488
|
-
const { workdir, config: config2, hooks, pluginRegistry, eventEmitter } = options;
|
|
34593
|
+
const { workdir, config: config2, hooks, pluginRegistry, eventEmitter, agentGetFn } = options;
|
|
34489
34594
|
const rectify = rectifyConflictedStory2 || (async (opts) => {
|
|
34490
34595
|
const { rectifyConflictedStory: importedRectify } = await Promise.resolve().then(() => (init_parallel_executor_rectify(), exports_parallel_executor_rectify));
|
|
34491
34596
|
return importedRectify(opts);
|
|
@@ -34506,7 +34611,8 @@ async function runRectificationPass(conflictedStories, options, prd, rectifyConf
|
|
|
34506
34611
|
hooks,
|
|
34507
34612
|
pluginRegistry,
|
|
34508
34613
|
prd,
|
|
34509
|
-
eventEmitter
|
|
34614
|
+
eventEmitter,
|
|
34615
|
+
agentGetFn
|
|
34510
34616
|
});
|
|
34511
34617
|
additionalCost += result.cost;
|
|
34512
34618
|
if (result.success) {
|
|
@@ -34829,7 +34935,7 @@ function wireEventsWriter(bus, feature, runId, workdir) {
|
|
|
34829
34935
|
const eventsFile = join49(eventsDir, "events.jsonl");
|
|
34830
34936
|
let dirReady = false;
|
|
34831
34937
|
const write = (line) => {
|
|
34832
|
-
(async () => {
|
|
34938
|
+
return (async () => {
|
|
34833
34939
|
try {
|
|
34834
34940
|
if (!dirReady) {
|
|
34835
34941
|
await mkdir2(eventsDir, { recursive: true });
|
|
@@ -34847,16 +34953,30 @@ function wireEventsWriter(bus, feature, runId, workdir) {
|
|
|
34847
34953
|
};
|
|
34848
34954
|
const unsubs = [];
|
|
34849
34955
|
unsubs.push(bus.on("run:started", (_ev) => {
|
|
34850
|
-
write({ ts: new Date().toISOString(), event: "run:started", runId, feature, project });
|
|
34956
|
+
return write({ ts: new Date().toISOString(), event: "run:started", runId, feature, project });
|
|
34851
34957
|
}));
|
|
34852
34958
|
unsubs.push(bus.on("story:started", (ev) => {
|
|
34853
|
-
write({
|
|
34959
|
+
return write({
|
|
34960
|
+
ts: new Date().toISOString(),
|
|
34961
|
+
event: "story:started",
|
|
34962
|
+
runId,
|
|
34963
|
+
feature,
|
|
34964
|
+
project,
|
|
34965
|
+
storyId: ev.storyId
|
|
34966
|
+
});
|
|
34854
34967
|
}));
|
|
34855
34968
|
unsubs.push(bus.on("story:completed", (ev) => {
|
|
34856
|
-
write({
|
|
34969
|
+
return write({
|
|
34970
|
+
ts: new Date().toISOString(),
|
|
34971
|
+
event: "story:completed",
|
|
34972
|
+
runId,
|
|
34973
|
+
feature,
|
|
34974
|
+
project,
|
|
34975
|
+
storyId: ev.storyId
|
|
34976
|
+
});
|
|
34857
34977
|
}));
|
|
34858
34978
|
unsubs.push(bus.on("story:decomposed", (ev) => {
|
|
34859
|
-
write({
|
|
34979
|
+
return write({
|
|
34860
34980
|
ts: new Date().toISOString(),
|
|
34861
34981
|
event: "story:decomposed",
|
|
34862
34982
|
runId,
|
|
@@ -34867,13 +34987,20 @@ function wireEventsWriter(bus, feature, runId, workdir) {
|
|
|
34867
34987
|
});
|
|
34868
34988
|
}));
|
|
34869
34989
|
unsubs.push(bus.on("story:failed", (ev) => {
|
|
34870
|
-
write({
|
|
34990
|
+
return write({
|
|
34991
|
+
ts: new Date().toISOString(),
|
|
34992
|
+
event: "story:failed",
|
|
34993
|
+
runId,
|
|
34994
|
+
feature,
|
|
34995
|
+
project,
|
|
34996
|
+
storyId: ev.storyId
|
|
34997
|
+
});
|
|
34871
34998
|
}));
|
|
34872
34999
|
unsubs.push(bus.on("run:completed", (_ev) => {
|
|
34873
|
-
write({ ts: new Date().toISOString(), event: "on-complete", runId, feature, project });
|
|
35000
|
+
return write({ ts: new Date().toISOString(), event: "on-complete", runId, feature, project });
|
|
34874
35001
|
}));
|
|
34875
35002
|
unsubs.push(bus.on("run:paused", (ev) => {
|
|
34876
|
-
write({
|
|
35003
|
+
return write({
|
|
34877
35004
|
ts: new Date().toISOString(),
|
|
34878
35005
|
event: "run:paused",
|
|
34879
35006
|
runId,
|
|
@@ -34895,44 +35022,44 @@ var init_events_writer = __esm(() => {
|
|
|
34895
35022
|
function wireHooks(bus, hooks, workdir, feature) {
|
|
34896
35023
|
const logger = getSafeLogger();
|
|
34897
35024
|
const safe = (name, fn) => {
|
|
34898
|
-
fn().catch((err) => logger?.warn("hooks-subscriber", `Hook "${name}" failed`, { error: String(err) }));
|
|
35025
|
+
return fn().catch((err) => logger?.warn("hooks-subscriber", `Hook "${name}" failed`, { error: String(err) })).catch(() => {});
|
|
34899
35026
|
};
|
|
34900
35027
|
const unsubs = [];
|
|
34901
35028
|
unsubs.push(bus.on("run:started", (ev) => {
|
|
34902
|
-
safe("on-start", () => fireHook(hooks, "on-start", hookCtx(feature, { status: "running" }), workdir));
|
|
35029
|
+
return safe("on-start", () => fireHook(hooks, "on-start", hookCtx(feature, { status: "running" }), workdir));
|
|
34903
35030
|
}));
|
|
34904
35031
|
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));
|
|
35032
|
+
return safe("on-story-start", () => fireHook(hooks, "on-story-start", hookCtx(feature, { storyId: ev.storyId, model: ev.modelTier, agent: ev.agent }), workdir));
|
|
34906
35033
|
}));
|
|
34907
35034
|
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));
|
|
35035
|
+
return safe("on-story-complete", () => fireHook(hooks, "on-story-complete", hookCtx(feature, { storyId: ev.storyId, status: "passed", cost: ev.cost }), workdir));
|
|
34909
35036
|
}));
|
|
34910
35037
|
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));
|
|
35038
|
+
return safe("on-story-complete (decomposed)", () => fireHook(hooks, "on-story-complete", hookCtx(feature, { storyId: ev.storyId, status: "decomposed", subStoryCount: ev.subStoryCount }), workdir));
|
|
34912
35039
|
}));
|
|
34913
35040
|
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));
|
|
35041
|
+
return safe("on-story-fail", () => fireHook(hooks, "on-story-fail", hookCtx(feature, { storyId: ev.storyId, status: "failed", reason: ev.reason }), workdir));
|
|
34915
35042
|
}));
|
|
34916
35043
|
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));
|
|
35044
|
+
return safe("on-pause (story)", () => fireHook(hooks, "on-pause", hookCtx(feature, { storyId: ev.storyId, reason: ev.reason, cost: ev.cost }), workdir));
|
|
34918
35045
|
}));
|
|
34919
35046
|
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));
|
|
35047
|
+
return safe("on-pause (run)", () => fireHook(hooks, "on-pause", hookCtx(feature, { storyId: ev.storyId, reason: ev.reason, cost: ev.cost }), workdir));
|
|
34921
35048
|
}));
|
|
34922
35049
|
unsubs.push(bus.on("run:completed", (ev) => {
|
|
34923
|
-
safe("on-complete", () => fireHook(hooks, "on-complete", hookCtx(feature, { status: "complete", cost: ev.totalCost ?? 0 }), workdir));
|
|
35050
|
+
return safe("on-complete", () => fireHook(hooks, "on-complete", hookCtx(feature, { status: "complete", cost: ev.totalCost ?? 0 }), workdir));
|
|
34924
35051
|
}));
|
|
34925
35052
|
unsubs.push(bus.on("run:resumed", (ev) => {
|
|
34926
|
-
safe("on-resume", () => fireHook(hooks, "on-resume", hookCtx(feature, { status: "running" }), workdir));
|
|
35053
|
+
return safe("on-resume", () => fireHook(hooks, "on-resume", hookCtx(feature, { status: "running" }), workdir));
|
|
34927
35054
|
}));
|
|
34928
35055
|
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));
|
|
35056
|
+
return safe("on-session-end (completed)", () => fireHook(hooks, "on-session-end", hookCtx(feature, { storyId: ev.storyId, status: "passed" }), workdir));
|
|
34930
35057
|
}));
|
|
34931
35058
|
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));
|
|
35059
|
+
return safe("on-session-end (failed)", () => fireHook(hooks, "on-session-end", hookCtx(feature, { storyId: ev.storyId, status: "failed" }), workdir));
|
|
34933
35060
|
}));
|
|
34934
35061
|
unsubs.push(bus.on("run:errored", (ev) => {
|
|
34935
|
-
safe("on-error", () => fireHook(hooks, "on-error", hookCtx(feature, { reason: ev.reason }), workdir));
|
|
35062
|
+
return safe("on-error", () => fireHook(hooks, "on-error", hookCtx(feature, { reason: ev.reason }), workdir));
|
|
34936
35063
|
}));
|
|
34937
35064
|
return () => {
|
|
34938
35065
|
for (const u of unsubs)
|
|
@@ -35007,7 +35134,7 @@ function wireRegistry(bus, feature, runId, workdir) {
|
|
|
35007
35134
|
const runDir = join50(homedir6(), ".nax", "runs", `${project}-${feature}-${runId}`);
|
|
35008
35135
|
const metaFile = join50(runDir, "meta.json");
|
|
35009
35136
|
const unsub = bus.on("run:started", (_ev) => {
|
|
35010
|
-
(async () => {
|
|
35137
|
+
return (async () => {
|
|
35011
35138
|
try {
|
|
35012
35139
|
await mkdir3(runDir, { recursive: true });
|
|
35013
35140
|
const meta3 = {
|
|
@@ -35038,11 +35165,11 @@ var init_registry3 = __esm(() => {
|
|
|
35038
35165
|
function wireReporters(bus, pluginRegistry, runId, startTime) {
|
|
35039
35166
|
const logger = getSafeLogger();
|
|
35040
35167
|
const safe = (name, fn) => {
|
|
35041
|
-
fn().catch((err) => logger?.warn("reporters-subscriber", `Reporter "${name}" error`, { error: String(err) }));
|
|
35168
|
+
return fn().catch((err) => logger?.warn("reporters-subscriber", `Reporter "${name}" error`, { error: String(err) })).catch(() => {});
|
|
35042
35169
|
};
|
|
35043
35170
|
const unsubs = [];
|
|
35044
35171
|
unsubs.push(bus.on("run:started", (ev) => {
|
|
35045
|
-
safe("onRunStart", async () => {
|
|
35172
|
+
return safe("onRunStart", async () => {
|
|
35046
35173
|
const reporters = pluginRegistry.getReporters();
|
|
35047
35174
|
for (const r of reporters) {
|
|
35048
35175
|
if (r.onRunStart) {
|
|
@@ -35061,7 +35188,7 @@ function wireReporters(bus, pluginRegistry, runId, startTime) {
|
|
|
35061
35188
|
});
|
|
35062
35189
|
}));
|
|
35063
35190
|
unsubs.push(bus.on("story:completed", (ev) => {
|
|
35064
|
-
safe("onStoryComplete(completed)", async () => {
|
|
35191
|
+
return safe("onStoryComplete(completed)", async () => {
|
|
35065
35192
|
const reporters = pluginRegistry.getReporters();
|
|
35066
35193
|
for (const r of reporters) {
|
|
35067
35194
|
if (r.onStoryComplete) {
|
|
@@ -35083,7 +35210,7 @@ function wireReporters(bus, pluginRegistry, runId, startTime) {
|
|
|
35083
35210
|
});
|
|
35084
35211
|
}));
|
|
35085
35212
|
unsubs.push(bus.on("story:failed", (ev) => {
|
|
35086
|
-
safe("onStoryComplete(failed)", async () => {
|
|
35213
|
+
return safe("onStoryComplete(failed)", async () => {
|
|
35087
35214
|
const reporters = pluginRegistry.getReporters();
|
|
35088
35215
|
for (const r of reporters) {
|
|
35089
35216
|
if (r.onStoryComplete) {
|
|
@@ -35105,7 +35232,7 @@ function wireReporters(bus, pluginRegistry, runId, startTime) {
|
|
|
35105
35232
|
});
|
|
35106
35233
|
}));
|
|
35107
35234
|
unsubs.push(bus.on("story:paused", (ev) => {
|
|
35108
|
-
safe("onStoryComplete(paused)", async () => {
|
|
35235
|
+
return safe("onStoryComplete(paused)", async () => {
|
|
35109
35236
|
const reporters = pluginRegistry.getReporters();
|
|
35110
35237
|
for (const r of reporters) {
|
|
35111
35238
|
if (r.onStoryComplete) {
|
|
@@ -35127,7 +35254,7 @@ function wireReporters(bus, pluginRegistry, runId, startTime) {
|
|
|
35127
35254
|
});
|
|
35128
35255
|
}));
|
|
35129
35256
|
unsubs.push(bus.on("run:completed", (ev) => {
|
|
35130
|
-
safe("onRunEnd", async () => {
|
|
35257
|
+
return safe("onRunEnd", async () => {
|
|
35131
35258
|
const reporters = pluginRegistry.getReporters();
|
|
35132
35259
|
for (const r of reporters) {
|
|
35133
35260
|
if (r.onRunEnd) {
|
|
@@ -68822,11 +68949,19 @@ function validateStory(raw, index, allIds) {
|
|
|
68822
68949
|
...contextFiles.length > 0 ? { contextFiles } : {}
|
|
68823
68950
|
};
|
|
68824
68951
|
}
|
|
68952
|
+
function sanitizeInvalidEscapes(text) {
|
|
68953
|
+
let result = text.replace(/\\x([0-9a-fA-F]{1,2})/g, (_, hex3) => `\\u00${hex3.padStart(2, "0")}`);
|
|
68954
|
+
result = result.replace(/\\u([0-9a-fA-F]{1,3})(?![0-9a-fA-F])/g, (_, digits) => `\\u${digits.padStart(4, "0")}`);
|
|
68955
|
+
result = result.replace(/\\u(?![0-9a-fA-F])/g, "\\");
|
|
68956
|
+
result = result.replace(/\\([^"\\\/bfnrtu])/g, "$1");
|
|
68957
|
+
return result;
|
|
68958
|
+
}
|
|
68825
68959
|
function parseRawString(text) {
|
|
68826
68960
|
const extracted = extractJsonFromMarkdown(text);
|
|
68827
68961
|
const cleaned = stripTrailingCommas(extracted);
|
|
68962
|
+
const sanitized = sanitizeInvalidEscapes(cleaned);
|
|
68828
68963
|
try {
|
|
68829
|
-
return JSON.parse(
|
|
68964
|
+
return JSON.parse(sanitized);
|
|
68830
68965
|
} catch (err) {
|
|
68831
68966
|
const parseErr = err;
|
|
68832
68967
|
throw new Error(`[schema] Failed to parse JSON: ${parseErr.message}`, { cause: parseErr });
|
|
@@ -68924,7 +69059,14 @@ async function planCommand(workdir, config2, options) {
|
|
|
68924
69059
|
if (entry)
|
|
68925
69060
|
autoModel = resolveModel2(entry).model;
|
|
68926
69061
|
} catch {}
|
|
68927
|
-
rawResponse = await cliAdapter.complete(prompt, {
|
|
69062
|
+
rawResponse = await cliAdapter.complete(prompt, {
|
|
69063
|
+
model: autoModel,
|
|
69064
|
+
jsonMode: true,
|
|
69065
|
+
workdir,
|
|
69066
|
+
config: config2,
|
|
69067
|
+
featureName: options.feature,
|
|
69068
|
+
sessionRole: "plan"
|
|
69069
|
+
});
|
|
68928
69070
|
try {
|
|
68929
69071
|
const envelope = JSON.parse(rawResponse);
|
|
68930
69072
|
if (envelope?.type === "result" && typeof envelope?.result === "string") {
|
|
@@ -71349,7 +71491,7 @@ Runs:
|
|
|
71349
71491
|
const summary = await extractRunSummary(filePath);
|
|
71350
71492
|
const timestamp = file2.replace(".jsonl", "");
|
|
71351
71493
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
71352
|
-
const duration3 = summary ?
|
|
71494
|
+
const duration3 = summary ? formatDuration(summary.durationMs) : "?";
|
|
71353
71495
|
const cost = summary ? `$${summary.totalCost.toFixed(4)}` : "$?.????";
|
|
71354
71496
|
const status = summary ? summary.failed === 0 ? source_default.green("\u2713") : source_default.red("\u2717") : "?";
|
|
71355
71497
|
console.log(` ${timestamp} ${stories.padEnd(7)} ${duration3.padEnd(8)} ${cost.padEnd(8)} ${status}`);
|
|
@@ -71443,17 +71585,6 @@ function shouldDisplayEntry(entry, options) {
|
|
|
71443
71585
|
}
|
|
71444
71586
|
return true;
|
|
71445
71587
|
}
|
|
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
71588
|
|
|
71458
71589
|
// src/commands/logs.ts
|
|
71459
71590
|
async function logsCommand(options) {
|
|
@@ -71555,7 +71686,7 @@ var DEFAULT_LIMIT = 20;
|
|
|
71555
71686
|
var _runsCmdDeps = {
|
|
71556
71687
|
getRunsDir
|
|
71557
71688
|
};
|
|
71558
|
-
function
|
|
71689
|
+
function formatDuration2(ms) {
|
|
71559
71690
|
if (ms <= 0)
|
|
71560
71691
|
return "-";
|
|
71561
71692
|
const minutes = Math.floor(ms / 60000);
|
|
@@ -71668,7 +71799,7 @@ async function runsCommand(options = {}) {
|
|
|
71668
71799
|
pad3(row.feature, COL.feature),
|
|
71669
71800
|
pad3(colored, COL.status + (colored.length - visibleLength(colored))),
|
|
71670
71801
|
pad3(`${row.passed}/${row.total}`, COL.stories),
|
|
71671
|
-
pad3(
|
|
71802
|
+
pad3(formatDuration2(row.durationMs), COL.duration),
|
|
71672
71803
|
formatDate(row.registeredAt)
|
|
71673
71804
|
].join(" ");
|
|
71674
71805
|
console.log(line);
|
|
@@ -71955,7 +72086,8 @@ async function runExecutionPhase(options, prd, pluginRegistry) {
|
|
|
71955
72086
|
allStoryMetrics,
|
|
71956
72087
|
pluginRegistry,
|
|
71957
72088
|
formatterMode: options.formatterMode,
|
|
71958
|
-
headless: options.headless
|
|
72089
|
+
headless: options.headless,
|
|
72090
|
+
agentGetFn: options.agentGetFn
|
|
71959
72091
|
}, prd);
|
|
71960
72092
|
prd = parallelResult.prd;
|
|
71961
72093
|
totalCost = parallelResult.totalCost;
|
|
@@ -71991,7 +72123,8 @@ async function runExecutionPhase(options, prd, pluginRegistry) {
|
|
|
71991
72123
|
startTime: options.startTime,
|
|
71992
72124
|
batchPlan,
|
|
71993
72125
|
agentGetFn: options.agentGetFn,
|
|
71994
|
-
pidRegistry: options.pidRegistry
|
|
72126
|
+
pidRegistry: options.pidRegistry,
|
|
72127
|
+
interactionChain: options.interactionChain
|
|
71995
72128
|
}, prd);
|
|
71996
72129
|
prd = sequentialResult.prd;
|
|
71997
72130
|
iterations = sequentialResult.iterations;
|
|
@@ -72117,7 +72250,8 @@ async function run(options) {
|
|
|
72117
72250
|
parallel,
|
|
72118
72251
|
runParallelExecution: _runnerDeps.runParallelExecution ?? undefined,
|
|
72119
72252
|
agentGetFn,
|
|
72120
|
-
pidRegistry
|
|
72253
|
+
pidRegistry,
|
|
72254
|
+
interactionChain
|
|
72121
72255
|
}, prd, pluginRegistry);
|
|
72122
72256
|
prd = executionResult.prd;
|
|
72123
72257
|
iterations = executionResult.iterations;
|