@nathapp/nax 0.45.0 → 0.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/bin/nax.ts +7 -6
- package/dist/nax.js +340 -202
- package/package.json +1 -1
- package/src/acceptance/generator.ts +1 -1
- package/src/acceptance/types.ts +2 -0
- package/src/agents/acp/adapter.ts +34 -6
- package/src/agents/acp/cost.ts +5 -75
- package/src/agents/acp/index.ts +0 -2
- package/src/agents/acp/parser.ts +57 -104
- package/src/agents/acp/spawn-client.ts +13 -2
- package/src/agents/{claude.ts → claude/adapter.ts} +15 -12
- package/src/agents/{claude-complete.ts → claude/complete.ts} +3 -3
- package/src/agents/claude/cost.ts +16 -0
- package/src/agents/{claude-execution.ts → claude/execution.ts} +17 -6
- package/src/agents/claude/index.ts +3 -0
- package/src/agents/{claude-interactive.ts → claude/interactive.ts} +4 -4
- package/src/agents/{claude-plan.ts → claude/plan.ts} +12 -9
- package/src/agents/cost/calculate.ts +154 -0
- package/src/agents/cost/index.ts +10 -0
- package/src/agents/cost/parse.ts +97 -0
- package/src/agents/cost/pricing.ts +59 -0
- package/src/agents/cost/types.ts +45 -0
- package/src/agents/index.ts +6 -4
- package/src/agents/registry.ts +5 -5
- package/src/agents/{claude-decompose.ts → shared/decompose.ts} +2 -2
- package/src/agents/{model-resolution.ts → shared/model-resolution.ts} +2 -2
- package/src/agents/{types-extended.ts → shared/types-extended.ts} +4 -4
- package/src/agents/{validation.ts → shared/validation.ts} +2 -2
- package/src/agents/{version-detection.ts → shared/version-detection.ts} +3 -3
- package/src/agents/types.ts +11 -4
- package/src/cli/agents.ts +1 -1
- package/src/cli/init.ts +15 -1
- package/src/pipeline/stages/acceptance-setup.ts +1 -0
- package/src/pipeline/stages/acceptance.ts +5 -8
- package/src/pipeline/stages/regression.ts +2 -0
- package/src/pipeline/stages/verify.ts +5 -10
- package/src/precheck/checks-agents.ts +1 -1
- package/src/precheck/checks-git.ts +28 -2
- package/src/precheck/checks-warnings.ts +30 -2
- package/src/precheck/checks.ts +1 -0
- package/src/precheck/index.ts +2 -0
- package/src/utils/log-test-output.ts +25 -0
- package/src/agents/cost.ts +0 -268
- /package/src/agents/{adapters/aider.ts → aider/adapter.ts} +0 -0
- /package/src/agents/{adapters/codex.ts → codex/adapter.ts} +0 -0
- /package/src/agents/{adapters/gemini.ts → gemini/adapter.ts} +0 -0
- /package/src/agents/{adapters/opencode.ts → opencode/adapter.ts} +0 -0
package/dist/nax.js
CHANGED
|
@@ -3240,60 +3240,6 @@ async function withProcessTimeout(proc, timeoutMs, opts) {
|
|
|
3240
3240
|
return { exitCode, timedOut };
|
|
3241
3241
|
}
|
|
3242
3242
|
|
|
3243
|
-
// src/agents/types.ts
|
|
3244
|
-
var CompleteError;
|
|
3245
|
-
var init_types2 = __esm(() => {
|
|
3246
|
-
CompleteError = class CompleteError extends Error {
|
|
3247
|
-
exitCode;
|
|
3248
|
-
constructor(message, exitCode) {
|
|
3249
|
-
super(message);
|
|
3250
|
-
this.exitCode = exitCode;
|
|
3251
|
-
this.name = "CompleteError";
|
|
3252
|
-
}
|
|
3253
|
-
};
|
|
3254
|
-
});
|
|
3255
|
-
|
|
3256
|
-
// src/agents/claude-complete.ts
|
|
3257
|
-
async function executeComplete(binary, prompt, options) {
|
|
3258
|
-
const cmd = [binary, "-p", prompt];
|
|
3259
|
-
if (options?.model) {
|
|
3260
|
-
cmd.push("--model", options.model);
|
|
3261
|
-
}
|
|
3262
|
-
if (options?.jsonMode) {
|
|
3263
|
-
cmd.push("--output-format", "json");
|
|
3264
|
-
}
|
|
3265
|
-
const { skipPermissions } = resolvePermissions(options?.config, "complete");
|
|
3266
|
-
if (skipPermissions) {
|
|
3267
|
-
cmd.push("--dangerously-skip-permissions");
|
|
3268
|
-
}
|
|
3269
|
-
const spawnOpts = { stdout: "pipe", stderr: "pipe" };
|
|
3270
|
-
if (options?.workdir)
|
|
3271
|
-
spawnOpts.cwd = options.workdir;
|
|
3272
|
-
const proc = _completeDeps.spawn(cmd, spawnOpts);
|
|
3273
|
-
const exitCode = await proc.exited;
|
|
3274
|
-
const stdout = await new Response(proc.stdout).text();
|
|
3275
|
-
const stderr = await new Response(proc.stderr).text();
|
|
3276
|
-
const trimmed = stdout.trim();
|
|
3277
|
-
if (exitCode !== 0) {
|
|
3278
|
-
const errorDetails = stderr.trim() || trimmed;
|
|
3279
|
-
const errorMessage = errorDetails || `complete() failed with exit code ${exitCode}`;
|
|
3280
|
-
throw new CompleteError(errorMessage, exitCode);
|
|
3281
|
-
}
|
|
3282
|
-
if (!trimmed) {
|
|
3283
|
-
throw new CompleteError("complete() returned empty output");
|
|
3284
|
-
}
|
|
3285
|
-
return trimmed;
|
|
3286
|
-
}
|
|
3287
|
-
var _completeDeps;
|
|
3288
|
-
var init_claude_complete = __esm(() => {
|
|
3289
|
-
init_types2();
|
|
3290
|
-
_completeDeps = {
|
|
3291
|
-
spawn(cmd, opts) {
|
|
3292
|
-
return Bun.spawn(cmd, opts);
|
|
3293
|
-
}
|
|
3294
|
-
};
|
|
3295
|
-
});
|
|
3296
|
-
|
|
3297
3243
|
// src/config/test-strategy.ts
|
|
3298
3244
|
function resolveTestStrategy(raw) {
|
|
3299
3245
|
if (!raw)
|
|
@@ -3343,7 +3289,7 @@ var init_test_strategy = __esm(() => {
|
|
|
3343
3289
|
];
|
|
3344
3290
|
});
|
|
3345
3291
|
|
|
3346
|
-
// src/agents/
|
|
3292
|
+
// src/agents/shared/decompose.ts
|
|
3347
3293
|
function buildDecomposePrompt(options) {
|
|
3348
3294
|
return `You are a requirements analyst. Break down the following feature specification into user stories and classify each story's complexity.
|
|
3349
3295
|
|
|
@@ -3454,11 +3400,104 @@ function coerceComplexity(value) {
|
|
|
3454
3400
|
}
|
|
3455
3401
|
return "medium";
|
|
3456
3402
|
}
|
|
3457
|
-
var
|
|
3403
|
+
var init_decompose = __esm(() => {
|
|
3458
3404
|
init_test_strategy();
|
|
3459
3405
|
});
|
|
3460
3406
|
|
|
3461
|
-
// src/agents/
|
|
3407
|
+
// src/agents/types.ts
|
|
3408
|
+
var CompleteError;
|
|
3409
|
+
var init_types2 = __esm(() => {
|
|
3410
|
+
CompleteError = class CompleteError extends Error {
|
|
3411
|
+
exitCode;
|
|
3412
|
+
constructor(message, exitCode) {
|
|
3413
|
+
super(message);
|
|
3414
|
+
this.exitCode = exitCode;
|
|
3415
|
+
this.name = "CompleteError";
|
|
3416
|
+
}
|
|
3417
|
+
};
|
|
3418
|
+
});
|
|
3419
|
+
|
|
3420
|
+
// src/agents/claude/complete.ts
|
|
3421
|
+
async function executeComplete(binary, prompt, options) {
|
|
3422
|
+
const cmd = [binary, "-p", prompt];
|
|
3423
|
+
if (options?.model) {
|
|
3424
|
+
cmd.push("--model", options.model);
|
|
3425
|
+
}
|
|
3426
|
+
if (options?.jsonMode) {
|
|
3427
|
+
cmd.push("--output-format", "json");
|
|
3428
|
+
}
|
|
3429
|
+
const { skipPermissions } = resolvePermissions(options?.config, "complete");
|
|
3430
|
+
if (skipPermissions) {
|
|
3431
|
+
cmd.push("--dangerously-skip-permissions");
|
|
3432
|
+
}
|
|
3433
|
+
const spawnOpts = { stdout: "pipe", stderr: "pipe" };
|
|
3434
|
+
if (options?.workdir)
|
|
3435
|
+
spawnOpts.cwd = options.workdir;
|
|
3436
|
+
const proc = _completeDeps.spawn(cmd, spawnOpts);
|
|
3437
|
+
const exitCode = await proc.exited;
|
|
3438
|
+
const stdout = await new Response(proc.stdout).text();
|
|
3439
|
+
const stderr = await new Response(proc.stderr).text();
|
|
3440
|
+
const trimmed = stdout.trim();
|
|
3441
|
+
if (exitCode !== 0) {
|
|
3442
|
+
const errorDetails = stderr.trim() || trimmed;
|
|
3443
|
+
const errorMessage = errorDetails || `complete() failed with exit code ${exitCode}`;
|
|
3444
|
+
throw new CompleteError(errorMessage, exitCode);
|
|
3445
|
+
}
|
|
3446
|
+
if (!trimmed) {
|
|
3447
|
+
throw new CompleteError("complete() returned empty output");
|
|
3448
|
+
}
|
|
3449
|
+
return trimmed;
|
|
3450
|
+
}
|
|
3451
|
+
var _completeDeps;
|
|
3452
|
+
var init_complete = __esm(() => {
|
|
3453
|
+
init_types2();
|
|
3454
|
+
_completeDeps = {
|
|
3455
|
+
spawn(cmd, opts) {
|
|
3456
|
+
return Bun.spawn(cmd, opts);
|
|
3457
|
+
}
|
|
3458
|
+
};
|
|
3459
|
+
});
|
|
3460
|
+
|
|
3461
|
+
// src/agents/cost/pricing.ts
|
|
3462
|
+
var COST_RATES, MODEL_PRICING;
|
|
3463
|
+
var init_pricing = __esm(() => {
|
|
3464
|
+
COST_RATES = {
|
|
3465
|
+
fast: {
|
|
3466
|
+
inputPer1M: 0.8,
|
|
3467
|
+
outputPer1M: 4
|
|
3468
|
+
},
|
|
3469
|
+
balanced: {
|
|
3470
|
+
inputPer1M: 3,
|
|
3471
|
+
outputPer1M: 15
|
|
3472
|
+
},
|
|
3473
|
+
powerful: {
|
|
3474
|
+
inputPer1M: 15,
|
|
3475
|
+
outputPer1M: 75
|
|
3476
|
+
}
|
|
3477
|
+
};
|
|
3478
|
+
MODEL_PRICING = {
|
|
3479
|
+
sonnet: { input: 3, output: 15 },
|
|
3480
|
+
haiku: { input: 0.8, output: 4, cacheRead: 0.1, cacheCreation: 1 },
|
|
3481
|
+
opus: { input: 15, output: 75 },
|
|
3482
|
+
"claude-sonnet-4": { input: 3, output: 15 },
|
|
3483
|
+
"claude-sonnet-4-5": { input: 3, output: 15 },
|
|
3484
|
+
"claude-sonnet-4-6": { input: 3, output: 15 },
|
|
3485
|
+
"claude-haiku": { input: 0.8, output: 4, cacheRead: 0.1, cacheCreation: 1 },
|
|
3486
|
+
"claude-haiku-4-5": { input: 0.8, output: 4, cacheRead: 0.1, cacheCreation: 1 },
|
|
3487
|
+
"claude-opus": { input: 15, output: 75 },
|
|
3488
|
+
"claude-opus-4": { input: 15, output: 75 },
|
|
3489
|
+
"claude-opus-4-6": { input: 15, output: 75 },
|
|
3490
|
+
"gpt-4.1": { input: 10, output: 30 },
|
|
3491
|
+
"gpt-4": { input: 30, output: 60 },
|
|
3492
|
+
"gpt-3.5-turbo": { input: 0.5, output: 1.5 },
|
|
3493
|
+
"gemini-2.5-pro": { input: 0.075, output: 0.3 },
|
|
3494
|
+
"gemini-2-pro": { input: 0.075, output: 0.3 },
|
|
3495
|
+
codex: { input: 0.02, output: 0.06 },
|
|
3496
|
+
"code-davinci-002": { input: 0.02, output: 0.06 }
|
|
3497
|
+
};
|
|
3498
|
+
});
|
|
3499
|
+
|
|
3500
|
+
// src/agents/cost/parse.ts
|
|
3462
3501
|
function parseTokenUsage(output) {
|
|
3463
3502
|
try {
|
|
3464
3503
|
const jsonMatch = output.match(/\{[^}]*"usage"\s*:\s*\{[^}]*"input_tokens"\s*:\s*(\d+)[^}]*"output_tokens"\s*:\s*(\d+)[^}]*\}[^}]*\}/);
|
|
@@ -3502,6 +3541,8 @@ function parseTokenUsage(output) {
|
|
|
3502
3541
|
}
|
|
3503
3542
|
return null;
|
|
3504
3543
|
}
|
|
3544
|
+
|
|
3545
|
+
// src/agents/cost/calculate.ts
|
|
3505
3546
|
function estimateCost(modelTier, inputTokens, outputTokens, customRates) {
|
|
3506
3547
|
const rates = customRates ?? COST_RATES[modelTier];
|
|
3507
3548
|
const inputCost = inputTokens / 1e6 * rates.inputPer1M;
|
|
@@ -3543,25 +3584,45 @@ function formatCostWithConfidence(estimate) {
|
|
|
3543
3584
|
return `~${formattedCost} (duration-based)`;
|
|
3544
3585
|
}
|
|
3545
3586
|
}
|
|
3546
|
-
|
|
3587
|
+
function estimateCostFromTokenUsage(usage, model) {
|
|
3588
|
+
const pricing = MODEL_PRICING[model];
|
|
3589
|
+
if (!pricing) {
|
|
3590
|
+
const fallbackInputRate = 3 / 1e6;
|
|
3591
|
+
const fallbackOutputRate = 15 / 1e6;
|
|
3592
|
+
const inputCost2 = (usage.input_tokens ?? 0) * fallbackInputRate;
|
|
3593
|
+
const outputCost2 = (usage.output_tokens ?? 0) * fallbackOutputRate;
|
|
3594
|
+
const cacheReadCost2 = (usage.cache_read_input_tokens ?? 0) * (0.5 / 1e6);
|
|
3595
|
+
const cacheCreationCost2 = (usage.cache_creation_input_tokens ?? 0) * (2 / 1e6);
|
|
3596
|
+
return inputCost2 + outputCost2 + cacheReadCost2 + cacheCreationCost2;
|
|
3597
|
+
}
|
|
3598
|
+
const inputRate = pricing.input / 1e6;
|
|
3599
|
+
const outputRate = pricing.output / 1e6;
|
|
3600
|
+
const cacheReadRate = (pricing.cacheRead ?? pricing.input * 0.1) / 1e6;
|
|
3601
|
+
const cacheCreationRate = (pricing.cacheCreation ?? pricing.input * 0.33) / 1e6;
|
|
3602
|
+
const inputCost = (usage.input_tokens ?? 0) * inputRate;
|
|
3603
|
+
const outputCost = (usage.output_tokens ?? 0) * outputRate;
|
|
3604
|
+
const cacheReadCost = (usage.cache_read_input_tokens ?? 0) * cacheReadRate;
|
|
3605
|
+
const cacheCreationCost = (usage.cache_creation_input_tokens ?? 0) * cacheCreationRate;
|
|
3606
|
+
return inputCost + outputCost + cacheReadCost + cacheCreationCost;
|
|
3607
|
+
}
|
|
3608
|
+
var init_calculate = __esm(() => {
|
|
3609
|
+
init_pricing();
|
|
3610
|
+
});
|
|
3611
|
+
|
|
3612
|
+
// src/agents/cost/index.ts
|
|
3547
3613
|
var init_cost = __esm(() => {
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
inputPer1M: 0.8,
|
|
3551
|
-
outputPer1M: 4
|
|
3552
|
-
},
|
|
3553
|
-
balanced: {
|
|
3554
|
-
inputPer1M: 3,
|
|
3555
|
-
outputPer1M: 15
|
|
3556
|
-
},
|
|
3557
|
-
powerful: {
|
|
3558
|
-
inputPer1M: 15,
|
|
3559
|
-
outputPer1M: 75
|
|
3560
|
-
}
|
|
3561
|
-
};
|
|
3614
|
+
init_pricing();
|
|
3615
|
+
init_calculate();
|
|
3562
3616
|
});
|
|
3563
3617
|
|
|
3564
|
-
// src/agents/claude
|
|
3618
|
+
// src/agents/claude/cost.ts
|
|
3619
|
+
var init_cost2 = __esm(() => {
|
|
3620
|
+
init_cost();
|
|
3621
|
+
});
|
|
3622
|
+
|
|
3623
|
+
// src/agents/claude/execution.ts
|
|
3624
|
+
import { homedir } from "os";
|
|
3625
|
+
import { isAbsolute } from "path";
|
|
3565
3626
|
function buildCommand(binary, options) {
|
|
3566
3627
|
const model = options.modelDef.model;
|
|
3567
3628
|
const { skipPermissions } = resolvePermissions(options.config, options.pipelineStage ?? "run");
|
|
@@ -3570,12 +3631,19 @@ function buildCommand(binary, options) {
|
|
|
3570
3631
|
}
|
|
3571
3632
|
function buildAllowedEnv(options) {
|
|
3572
3633
|
const allowed = {};
|
|
3573
|
-
const essentialVars = ["PATH", "
|
|
3634
|
+
const essentialVars = ["PATH", "TMPDIR", "NODE_ENV", "USER", "LOGNAME"];
|
|
3574
3635
|
for (const varName of essentialVars) {
|
|
3575
3636
|
if (process.env[varName]) {
|
|
3576
3637
|
allowed[varName] = process.env[varName];
|
|
3577
3638
|
}
|
|
3578
3639
|
}
|
|
3640
|
+
const rawHome = process.env.HOME ?? "";
|
|
3641
|
+
const safeHome = rawHome && isAbsolute(rawHome) ? rawHome : homedir();
|
|
3642
|
+
if (rawHome !== safeHome) {
|
|
3643
|
+
const logger = getLogger();
|
|
3644
|
+
logger.warn("env", `HOME env is not absolute ("${rawHome}"), falling back to os.homedir(): ${safeHome}`);
|
|
3645
|
+
}
|
|
3646
|
+
allowed.HOME = safeHome;
|
|
3579
3647
|
const apiKeyVars = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY"];
|
|
3580
3648
|
for (const varName of apiKeyVars) {
|
|
3581
3649
|
if (process.env[varName]) {
|
|
@@ -3663,9 +3731,9 @@ async function executeOnce(binary, options, pidRegistry) {
|
|
|
3663
3731
|
};
|
|
3664
3732
|
}
|
|
3665
3733
|
var MAX_AGENT_OUTPUT_CHARS = 5000, MAX_AGENT_STDERR_CHARS = 1000, SIGKILL_GRACE_PERIOD_MS = 5000, _runOnceDeps;
|
|
3666
|
-
var
|
|
3734
|
+
var init_execution = __esm(() => {
|
|
3667
3735
|
init_logger2();
|
|
3668
|
-
|
|
3736
|
+
init_cost2();
|
|
3669
3737
|
_runOnceDeps = {
|
|
3670
3738
|
killProc(proc, signal) {
|
|
3671
3739
|
proc.kill(signal);
|
|
@@ -3676,7 +3744,7 @@ var init_claude_execution = __esm(() => {
|
|
|
3676
3744
|
};
|
|
3677
3745
|
});
|
|
3678
3746
|
|
|
3679
|
-
// src/agents/claude
|
|
3747
|
+
// src/agents/claude/interactive.ts
|
|
3680
3748
|
function runInteractiveMode(binary, options, pidRegistry) {
|
|
3681
3749
|
const model = options.modelDef.model;
|
|
3682
3750
|
const cmd = [binary, "--model", model, options.prompt];
|
|
@@ -3715,9 +3783,9 @@ function runInteractiveMode(binary, options, pidRegistry) {
|
|
|
3715
3783
|
pid: proc.pid
|
|
3716
3784
|
};
|
|
3717
3785
|
}
|
|
3718
|
-
var
|
|
3786
|
+
var init_interactive = __esm(() => {
|
|
3719
3787
|
init_logger2();
|
|
3720
|
-
|
|
3788
|
+
init_execution();
|
|
3721
3789
|
});
|
|
3722
3790
|
|
|
3723
3791
|
// src/config/schema-types.ts
|
|
@@ -18139,7 +18207,7 @@ var init_schema = __esm(() => {
|
|
|
18139
18207
|
init_defaults();
|
|
18140
18208
|
});
|
|
18141
18209
|
|
|
18142
|
-
// src/agents/model-resolution.ts
|
|
18210
|
+
// src/agents/shared/model-resolution.ts
|
|
18143
18211
|
var exports_model_resolution = {};
|
|
18144
18212
|
__export(exports_model_resolution, {
|
|
18145
18213
|
resolveBalancedModelDef: () => resolveBalancedModelDef
|
|
@@ -18160,7 +18228,7 @@ var init_model_resolution = __esm(() => {
|
|
|
18160
18228
|
init_schema();
|
|
18161
18229
|
});
|
|
18162
18230
|
|
|
18163
|
-
// src/agents/claude
|
|
18231
|
+
// src/agents/claude/plan.ts
|
|
18164
18232
|
import { mkdtempSync, rmSync } from "fs";
|
|
18165
18233
|
import { tmpdir } from "os";
|
|
18166
18234
|
import { join } from "path";
|
|
@@ -18279,12 +18347,12 @@ async function runPlan(binary, options, pidRegistry, buildAllowedEnv2) {
|
|
|
18279
18347
|
}
|
|
18280
18348
|
}
|
|
18281
18349
|
}
|
|
18282
|
-
var
|
|
18350
|
+
var init_plan = __esm(() => {
|
|
18283
18351
|
init_logger2();
|
|
18284
18352
|
init_model_resolution();
|
|
18285
18353
|
});
|
|
18286
18354
|
|
|
18287
|
-
// src/agents/claude.ts
|
|
18355
|
+
// src/agents/claude/adapter.ts
|
|
18288
18356
|
class ClaudeCodeAdapter {
|
|
18289
18357
|
name = "claude";
|
|
18290
18358
|
displayName = "Claude Code";
|
|
@@ -18435,14 +18503,14 @@ class ClaudeCodeAdapter {
|
|
|
18435
18503
|
}
|
|
18436
18504
|
}
|
|
18437
18505
|
var _decomposeDeps, _claudeAdapterDeps;
|
|
18438
|
-
var
|
|
18506
|
+
var init_adapter = __esm(() => {
|
|
18439
18507
|
init_pid_registry();
|
|
18440
18508
|
init_logger2();
|
|
18441
|
-
|
|
18442
|
-
|
|
18443
|
-
|
|
18444
|
-
|
|
18445
|
-
|
|
18509
|
+
init_decompose();
|
|
18510
|
+
init_complete();
|
|
18511
|
+
init_execution();
|
|
18512
|
+
init_interactive();
|
|
18513
|
+
init_plan();
|
|
18446
18514
|
_decomposeDeps = {
|
|
18447
18515
|
spawn(cmd, opts) {
|
|
18448
18516
|
return Bun.spawn(cmd, opts);
|
|
@@ -18453,6 +18521,12 @@ var init_claude = __esm(() => {
|
|
|
18453
18521
|
};
|
|
18454
18522
|
});
|
|
18455
18523
|
|
|
18524
|
+
// src/agents/claude/index.ts
|
|
18525
|
+
var init_claude = __esm(() => {
|
|
18526
|
+
init_adapter();
|
|
18527
|
+
init_execution();
|
|
18528
|
+
});
|
|
18529
|
+
|
|
18456
18530
|
// src/utils/errors.ts
|
|
18457
18531
|
function errorMessage(err) {
|
|
18458
18532
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -18665,7 +18739,7 @@ Respond with ONLY the TypeScript test code (no markdown code fences, no explanat
|
|
|
18665
18739
|
testable: c.testable,
|
|
18666
18740
|
storyId: c.storyId
|
|
18667
18741
|
})), null, 2);
|
|
18668
|
-
await _generatorPRDDeps.writeFile(join2(options.
|
|
18742
|
+
await _generatorPRDDeps.writeFile(join2(options.featureDir, "acceptance-refined.json"), refinedJsonContent);
|
|
18669
18743
|
return { testCode, criteria };
|
|
18670
18744
|
}
|
|
18671
18745
|
function buildStrategyInstructions(strategy, framework) {
|
|
@@ -18977,11 +19051,40 @@ function parseAcpxJsonOutput(rawOutput) {
|
|
|
18977
19051
|
`).filter((l) => l.trim());
|
|
18978
19052
|
let text = "";
|
|
18979
19053
|
let tokenUsage;
|
|
19054
|
+
let exactCostUsd;
|
|
18980
19055
|
let stopReason;
|
|
18981
19056
|
let error48;
|
|
18982
19057
|
for (const line of lines) {
|
|
18983
19058
|
try {
|
|
18984
19059
|
const event = JSON.parse(line);
|
|
19060
|
+
if (event.jsonrpc === "2.0") {
|
|
19061
|
+
if (event.method === "session/update" && event.params?.update) {
|
|
19062
|
+
const update = event.params.update;
|
|
19063
|
+
if (update.sessionUpdate === "agent_message_chunk" && update.content?.type === "text" && update.content.text) {
|
|
19064
|
+
text += update.content.text;
|
|
19065
|
+
}
|
|
19066
|
+
if (update.sessionUpdate === "usage_update" && typeof update.cost?.amount === "number") {
|
|
19067
|
+
exactCostUsd = update.cost.amount;
|
|
19068
|
+
}
|
|
19069
|
+
}
|
|
19070
|
+
if (event.id !== undefined && event.result && typeof event.result === "object") {
|
|
19071
|
+
const result = event.result;
|
|
19072
|
+
if (result.stopReason)
|
|
19073
|
+
stopReason = result.stopReason;
|
|
19074
|
+
if (result.stop_reason)
|
|
19075
|
+
stopReason = result.stop_reason;
|
|
19076
|
+
if (result.usage && typeof result.usage === "object") {
|
|
19077
|
+
const u = result.usage;
|
|
19078
|
+
tokenUsage = {
|
|
19079
|
+
input_tokens: u.inputTokens ?? u.input_tokens ?? 0,
|
|
19080
|
+
output_tokens: u.outputTokens ?? u.output_tokens ?? 0,
|
|
19081
|
+
cache_read_input_tokens: u.cachedReadTokens ?? u.cache_read_input_tokens ?? 0,
|
|
19082
|
+
cache_creation_input_tokens: u.cachedWriteTokens ?? u.cache_creation_input_tokens ?? 0
|
|
19083
|
+
};
|
|
19084
|
+
}
|
|
19085
|
+
}
|
|
19086
|
+
continue;
|
|
19087
|
+
}
|
|
18985
19088
|
if (event.content && typeof event.content === "string")
|
|
18986
19089
|
text += event.content;
|
|
18987
19090
|
if (event.text && typeof event.text === "string")
|
|
@@ -19008,17 +19111,25 @@ function parseAcpxJsonOutput(rawOutput) {
|
|
|
19008
19111
|
text = line;
|
|
19009
19112
|
}
|
|
19010
19113
|
}
|
|
19011
|
-
return { text: text.trim(), tokenUsage, stopReason, error: error48 };
|
|
19114
|
+
return { text: text.trim(), tokenUsage, exactCostUsd, stopReason, error: error48 };
|
|
19012
19115
|
}
|
|
19013
19116
|
|
|
19014
19117
|
// src/agents/acp/spawn-client.ts
|
|
19118
|
+
import { homedir as homedir2 } from "os";
|
|
19119
|
+
import { isAbsolute as isAbsolute2 } from "path";
|
|
19015
19120
|
function buildAllowedEnv2(extraEnv) {
|
|
19016
19121
|
const allowed = {};
|
|
19017
|
-
const essentialVars = ["PATH", "
|
|
19122
|
+
const essentialVars = ["PATH", "TMPDIR", "NODE_ENV", "USER", "LOGNAME"];
|
|
19018
19123
|
for (const varName of essentialVars) {
|
|
19019
19124
|
if (process.env[varName])
|
|
19020
19125
|
allowed[varName] = process.env[varName];
|
|
19021
19126
|
}
|
|
19127
|
+
const rawHome = process.env.HOME ?? "";
|
|
19128
|
+
const safeHome = rawHome && isAbsolute2(rawHome) ? rawHome : homedir2();
|
|
19129
|
+
if (rawHome !== safeHome) {
|
|
19130
|
+
getSafeLogger()?.warn("env", `HOME env is not absolute ("${rawHome}"), falling back to os.homedir(): ${safeHome}`);
|
|
19131
|
+
}
|
|
19132
|
+
allowed.HOME = safeHome;
|
|
19022
19133
|
const apiKeyVars = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY", "GOOGLE_API_KEY", "CLAUDE_API_KEY"];
|
|
19023
19134
|
for (const varName of apiKeyVars) {
|
|
19024
19135
|
if (process.env[varName])
|
|
@@ -19107,8 +19218,9 @@ class SpawnAcpSession {
|
|
|
19107
19218
|
const parsed = parseAcpxJsonOutput(stdout);
|
|
19108
19219
|
return {
|
|
19109
19220
|
messages: [{ role: "assistant", content: parsed.text || "" }],
|
|
19110
|
-
stopReason: "end_turn",
|
|
19111
|
-
cumulative_token_usage: parsed.tokenUsage
|
|
19221
|
+
stopReason: parsed.stopReason ?? "end_turn",
|
|
19222
|
+
cumulative_token_usage: parsed.tokenUsage,
|
|
19223
|
+
exactCostUsd: parsed.exactCostUsd
|
|
19112
19224
|
};
|
|
19113
19225
|
} catch (err) {
|
|
19114
19226
|
getSafeLogger()?.warn("acp-adapter", "Failed to parse session prompt response", {
|
|
@@ -19234,44 +19346,8 @@ var init_spawn_client = __esm(() => {
|
|
|
19234
19346
|
});
|
|
19235
19347
|
|
|
19236
19348
|
// src/agents/acp/cost.ts
|
|
19237
|
-
|
|
19238
|
-
|
|
19239
|
-
if (!pricing) {
|
|
19240
|
-
const fallbackInputRate = 3 / 1e6;
|
|
19241
|
-
const fallbackOutputRate = 15 / 1e6;
|
|
19242
|
-
const inputCost2 = (usage.input_tokens ?? 0) * fallbackInputRate;
|
|
19243
|
-
const outputCost2 = (usage.output_tokens ?? 0) * fallbackOutputRate;
|
|
19244
|
-
const cacheReadCost2 = (usage.cache_read_input_tokens ?? 0) * (0.5 / 1e6);
|
|
19245
|
-
const cacheCreationCost2 = (usage.cache_creation_input_tokens ?? 0) * (2 / 1e6);
|
|
19246
|
-
return inputCost2 + outputCost2 + cacheReadCost2 + cacheCreationCost2;
|
|
19247
|
-
}
|
|
19248
|
-
const inputRate = pricing.input / 1e6;
|
|
19249
|
-
const outputRate = pricing.output / 1e6;
|
|
19250
|
-
const cacheReadRate = (pricing.cacheRead ?? pricing.input * 0.1) / 1e6;
|
|
19251
|
-
const cacheCreationRate = (pricing.cacheCreation ?? pricing.input * 0.33) / 1e6;
|
|
19252
|
-
const inputCost = (usage.input_tokens ?? 0) * inputRate;
|
|
19253
|
-
const outputCost = (usage.output_tokens ?? 0) * outputRate;
|
|
19254
|
-
const cacheReadCost = (usage.cache_read_input_tokens ?? 0) * cacheReadRate;
|
|
19255
|
-
const cacheCreationCost = (usage.cache_creation_input_tokens ?? 0) * cacheCreationRate;
|
|
19256
|
-
return inputCost + outputCost + cacheReadCost + cacheCreationCost;
|
|
19257
|
-
}
|
|
19258
|
-
var MODEL_PRICING;
|
|
19259
|
-
var init_cost2 = __esm(() => {
|
|
19260
|
-
MODEL_PRICING = {
|
|
19261
|
-
"claude-sonnet-4": { input: 3, output: 15 },
|
|
19262
|
-
"claude-sonnet-4-5": { input: 3, output: 15 },
|
|
19263
|
-
"claude-haiku": { input: 0.8, output: 4, cacheRead: 0.1, cacheCreation: 1 },
|
|
19264
|
-
"claude-haiku-4-5": { input: 0.8, output: 4, cacheRead: 0.1, cacheCreation: 1 },
|
|
19265
|
-
"claude-opus": { input: 15, output: 75 },
|
|
19266
|
-
"claude-opus-4": { input: 15, output: 75 },
|
|
19267
|
-
"gpt-4.1": { input: 10, output: 30 },
|
|
19268
|
-
"gpt-4": { input: 30, output: 60 },
|
|
19269
|
-
"gpt-3.5-turbo": { input: 0.5, output: 1.5 },
|
|
19270
|
-
"gemini-2.5-pro": { input: 0.075, output: 0.3 },
|
|
19271
|
-
"gemini-2-pro": { input: 0.075, output: 0.3 },
|
|
19272
|
-
codex: { input: 0.02, output: 0.06 },
|
|
19273
|
-
"code-davinci-002": { input: 0.02, output: 0.06 }
|
|
19274
|
-
};
|
|
19349
|
+
var init_cost3 = __esm(() => {
|
|
19350
|
+
init_cost();
|
|
19275
19351
|
});
|
|
19276
19352
|
|
|
19277
19353
|
// src/agents/acp/adapter.ts
|
|
@@ -19569,7 +19645,13 @@ class AcpAgentAdapter {
|
|
|
19569
19645
|
let lastResponse = null;
|
|
19570
19646
|
let timedOut = false;
|
|
19571
19647
|
const runState = { succeeded: false };
|
|
19572
|
-
const totalTokenUsage = {
|
|
19648
|
+
const totalTokenUsage = {
|
|
19649
|
+
input_tokens: 0,
|
|
19650
|
+
output_tokens: 0,
|
|
19651
|
+
cache_read_input_tokens: 0,
|
|
19652
|
+
cache_creation_input_tokens: 0
|
|
19653
|
+
};
|
|
19654
|
+
let totalExactCostUsd;
|
|
19573
19655
|
try {
|
|
19574
19656
|
let currentPrompt = options.prompt;
|
|
19575
19657
|
let turnCount = 0;
|
|
@@ -19588,6 +19670,11 @@ class AcpAgentAdapter {
|
|
|
19588
19670
|
if (lastResponse.cumulative_token_usage) {
|
|
19589
19671
|
totalTokenUsage.input_tokens += lastResponse.cumulative_token_usage.input_tokens ?? 0;
|
|
19590
19672
|
totalTokenUsage.output_tokens += lastResponse.cumulative_token_usage.output_tokens ?? 0;
|
|
19673
|
+
totalTokenUsage.cache_read_input_tokens += lastResponse.cumulative_token_usage.cache_read_input_tokens ?? 0;
|
|
19674
|
+
totalTokenUsage.cache_creation_input_tokens += lastResponse.cumulative_token_usage.cache_creation_input_tokens ?? 0;
|
|
19675
|
+
}
|
|
19676
|
+
if (lastResponse.exactCostUsd !== undefined) {
|
|
19677
|
+
totalExactCostUsd = (totalExactCostUsd ?? 0) + lastResponse.exactCostUsd;
|
|
19591
19678
|
}
|
|
19592
19679
|
const outputText = extractOutput(lastResponse);
|
|
19593
19680
|
const question = extractQuestion(outputText);
|
|
@@ -19634,7 +19721,7 @@ class AcpAgentAdapter {
|
|
|
19634
19721
|
}
|
|
19635
19722
|
const success2 = lastResponse?.stopReason === "end_turn";
|
|
19636
19723
|
const output = extractOutput(lastResponse);
|
|
19637
|
-
const estimatedCost = totalTokenUsage.input_tokens > 0 || totalTokenUsage.output_tokens > 0 ? estimateCostFromTokenUsage(totalTokenUsage, options.modelDef.model) : 0;
|
|
19724
|
+
const estimatedCost = totalExactCostUsd ?? (totalTokenUsage.input_tokens > 0 || totalTokenUsage.output_tokens > 0 ? estimateCostFromTokenUsage(totalTokenUsage, options.modelDef.model) : 0);
|
|
19638
19725
|
return {
|
|
19639
19726
|
success: success2,
|
|
19640
19727
|
exitCode: success2 ? 0 : 1,
|
|
@@ -19684,6 +19771,12 @@ class AcpAgentAdapter {
|
|
|
19684
19771
|
if (!unwrapped) {
|
|
19685
19772
|
throw new CompleteError("complete() returned empty output");
|
|
19686
19773
|
}
|
|
19774
|
+
if (response.exactCostUsd !== undefined) {
|
|
19775
|
+
getSafeLogger()?.info("acp-adapter", "complete() cost", {
|
|
19776
|
+
costUsd: response.exactCostUsd,
|
|
19777
|
+
model
|
|
19778
|
+
});
|
|
19779
|
+
}
|
|
19687
19780
|
return unwrapped;
|
|
19688
19781
|
} catch (err) {
|
|
19689
19782
|
const error48 = err instanceof Error ? err : new Error(String(err));
|
|
@@ -19770,12 +19863,12 @@ class AcpAgentAdapter {
|
|
|
19770
19863
|
}
|
|
19771
19864
|
}
|
|
19772
19865
|
var MAX_AGENT_OUTPUT_CHARS2 = 5000, MAX_RATE_LIMIT_RETRIES = 3, INTERACTION_TIMEOUT_MS, AGENT_REGISTRY, DEFAULT_ENTRY, _acpAdapterDeps, MAX_SESSION_AGE_MS;
|
|
19773
|
-
var
|
|
19866
|
+
var init_adapter2 = __esm(() => {
|
|
19774
19867
|
init_logger2();
|
|
19775
|
-
|
|
19868
|
+
init_decompose();
|
|
19776
19869
|
init_spawn_client();
|
|
19777
19870
|
init_types2();
|
|
19778
|
-
|
|
19871
|
+
init_cost3();
|
|
19779
19872
|
INTERACTION_TIMEOUT_MS = 5 * 60 * 1000;
|
|
19780
19873
|
AGENT_REGISTRY = {
|
|
19781
19874
|
claude: {
|
|
@@ -19817,7 +19910,7 @@ var init_adapter = __esm(() => {
|
|
|
19817
19910
|
MAX_SESSION_AGE_MS = 2 * 60 * 60 * 1000;
|
|
19818
19911
|
});
|
|
19819
19912
|
|
|
19820
|
-
// src/agents/
|
|
19913
|
+
// src/agents/aider/adapter.ts
|
|
19821
19914
|
class AiderAdapter {
|
|
19822
19915
|
name = "aider";
|
|
19823
19916
|
displayName = "Aider";
|
|
@@ -19882,7 +19975,7 @@ class AiderAdapter {
|
|
|
19882
19975
|
}
|
|
19883
19976
|
}
|
|
19884
19977
|
var _aiderCompleteDeps, MAX_AGENT_OUTPUT_CHARS3 = 5000;
|
|
19885
|
-
var
|
|
19978
|
+
var init_adapter3 = __esm(() => {
|
|
19886
19979
|
init_types2();
|
|
19887
19980
|
_aiderCompleteDeps = {
|
|
19888
19981
|
which(name) {
|
|
@@ -19894,7 +19987,7 @@ var init_aider = __esm(() => {
|
|
|
19894
19987
|
};
|
|
19895
19988
|
});
|
|
19896
19989
|
|
|
19897
|
-
// src/agents/
|
|
19990
|
+
// src/agents/codex/adapter.ts
|
|
19898
19991
|
class CodexAdapter {
|
|
19899
19992
|
name = "codex";
|
|
19900
19993
|
displayName = "Codex";
|
|
@@ -19957,7 +20050,7 @@ class CodexAdapter {
|
|
|
19957
20050
|
}
|
|
19958
20051
|
}
|
|
19959
20052
|
var _codexRunDeps, _codexCompleteDeps, MAX_AGENT_OUTPUT_CHARS4 = 5000;
|
|
19960
|
-
var
|
|
20053
|
+
var init_adapter4 = __esm(() => {
|
|
19961
20054
|
init_types2();
|
|
19962
20055
|
_codexRunDeps = {
|
|
19963
20056
|
which(name) {
|
|
@@ -19974,7 +20067,7 @@ var init_codex = __esm(() => {
|
|
|
19974
20067
|
};
|
|
19975
20068
|
});
|
|
19976
20069
|
|
|
19977
|
-
// src/agents/
|
|
20070
|
+
// src/agents/gemini/adapter.ts
|
|
19978
20071
|
class GeminiAdapter {
|
|
19979
20072
|
name = "gemini";
|
|
19980
20073
|
displayName = "Gemini CLI";
|
|
@@ -20057,7 +20150,7 @@ class GeminiAdapter {
|
|
|
20057
20150
|
}
|
|
20058
20151
|
}
|
|
20059
20152
|
var _geminiRunDeps, _geminiCompleteDeps, MAX_AGENT_OUTPUT_CHARS5 = 5000;
|
|
20060
|
-
var
|
|
20153
|
+
var init_adapter5 = __esm(() => {
|
|
20061
20154
|
init_types2();
|
|
20062
20155
|
_geminiRunDeps = {
|
|
20063
20156
|
which(name) {
|
|
@@ -20074,7 +20167,7 @@ var init_gemini = __esm(() => {
|
|
|
20074
20167
|
};
|
|
20075
20168
|
});
|
|
20076
20169
|
|
|
20077
|
-
// src/agents/
|
|
20170
|
+
// src/agents/opencode/adapter.ts
|
|
20078
20171
|
class OpenCodeAdapter {
|
|
20079
20172
|
name = "opencode";
|
|
20080
20173
|
displayName = "OpenCode";
|
|
@@ -20119,7 +20212,7 @@ class OpenCodeAdapter {
|
|
|
20119
20212
|
}
|
|
20120
20213
|
}
|
|
20121
20214
|
var _opencodeCompleteDeps;
|
|
20122
|
-
var
|
|
20215
|
+
var init_adapter6 = __esm(() => {
|
|
20123
20216
|
init_types2();
|
|
20124
20217
|
_opencodeCompleteDeps = {
|
|
20125
20218
|
which(name) {
|
|
@@ -20211,12 +20304,12 @@ function createAgentRegistry(config2) {
|
|
|
20211
20304
|
var ALL_AGENTS;
|
|
20212
20305
|
var init_registry = __esm(() => {
|
|
20213
20306
|
init_logger2();
|
|
20307
|
+
init_adapter2();
|
|
20308
|
+
init_adapter3();
|
|
20214
20309
|
init_adapter();
|
|
20215
|
-
|
|
20216
|
-
|
|
20217
|
-
|
|
20218
|
-
init_opencode();
|
|
20219
|
-
init_claude();
|
|
20310
|
+
init_adapter4();
|
|
20311
|
+
init_adapter5();
|
|
20312
|
+
init_adapter6();
|
|
20220
20313
|
ALL_AGENTS = [
|
|
20221
20314
|
new ClaudeCodeAdapter,
|
|
20222
20315
|
new CodexAdapter,
|
|
@@ -20374,7 +20467,7 @@ var init_chain = __esm(() => {
|
|
|
20374
20467
|
|
|
20375
20468
|
// src/utils/path-security.ts
|
|
20376
20469
|
import { realpathSync } from "fs";
|
|
20377
|
-
import { dirname, isAbsolute, join as join5, normalize, resolve } from "path";
|
|
20470
|
+
import { dirname, isAbsolute as isAbsolute3, join as join5, normalize, resolve } from "path";
|
|
20378
20471
|
function safeRealpath(p) {
|
|
20379
20472
|
try {
|
|
20380
20473
|
return realpathSync(p);
|
|
@@ -20392,7 +20485,7 @@ function validateModulePath(modulePath, allowedRoots) {
|
|
|
20392
20485
|
return { valid: false, error: "Module path is empty" };
|
|
20393
20486
|
}
|
|
20394
20487
|
const normalizedRoots = allowedRoots.map((r) => safeRealpath(resolve(r)));
|
|
20395
|
-
if (
|
|
20488
|
+
if (isAbsolute3(modulePath)) {
|
|
20396
20489
|
const absoluteTarget = safeRealpath(normalize(modulePath));
|
|
20397
20490
|
const isWithin = normalizedRoots.some((root) => {
|
|
20398
20491
|
return absoluteTarget.startsWith(`${root}/`) || absoluteTarget === root;
|
|
@@ -20683,7 +20776,7 @@ function isPlainObject2(value) {
|
|
|
20683
20776
|
|
|
20684
20777
|
// src/config/path-security.ts
|
|
20685
20778
|
import { existsSync as existsSync4, lstatSync, realpathSync as realpathSync2 } from "fs";
|
|
20686
|
-
import { isAbsolute as
|
|
20779
|
+
import { isAbsolute as isAbsolute4, normalize as normalize2, resolve as resolve3 } from "path";
|
|
20687
20780
|
function validateDirectory(dirPath, baseDir) {
|
|
20688
20781
|
const resolved = resolve3(dirPath);
|
|
20689
20782
|
if (!existsSync4(resolved)) {
|
|
@@ -20715,7 +20808,7 @@ function validateDirectory(dirPath, baseDir) {
|
|
|
20715
20808
|
function isWithinDirectory(targetPath, basePath) {
|
|
20716
20809
|
const normalizedTarget = normalize2(targetPath);
|
|
20717
20810
|
const normalizedBase = normalize2(basePath);
|
|
20718
|
-
if (!
|
|
20811
|
+
if (!isAbsolute4(normalizedTarget) || !isAbsolute4(normalizedBase)) {
|
|
20719
20812
|
return false;
|
|
20720
20813
|
}
|
|
20721
20814
|
const baseWithSlash = normalizedBase.endsWith("/") ? normalizedBase : `${normalizedBase}/`;
|
|
@@ -20751,10 +20844,10 @@ var MAX_DIRECTORY_DEPTH = 10;
|
|
|
20751
20844
|
var init_path_security2 = () => {};
|
|
20752
20845
|
|
|
20753
20846
|
// src/config/paths.ts
|
|
20754
|
-
import { homedir } from "os";
|
|
20847
|
+
import { homedir as homedir3 } from "os";
|
|
20755
20848
|
import { join as join6, resolve as resolve4 } from "path";
|
|
20756
20849
|
function globalConfigDir() {
|
|
20757
|
-
return join6(
|
|
20850
|
+
return join6(homedir3(), ".nax");
|
|
20758
20851
|
}
|
|
20759
20852
|
var init_paths = () => {};
|
|
20760
20853
|
|
|
@@ -22085,7 +22178,7 @@ var package_default;
|
|
|
22085
22178
|
var init_package = __esm(() => {
|
|
22086
22179
|
package_default = {
|
|
22087
22180
|
name: "@nathapp/nax",
|
|
22088
|
-
version: "0.
|
|
22181
|
+
version: "0.46.1",
|
|
22089
22182
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
22090
22183
|
type: "module",
|
|
22091
22184
|
bin: {
|
|
@@ -22158,8 +22251,8 @@ var init_version = __esm(() => {
|
|
|
22158
22251
|
NAX_VERSION = package_default.version;
|
|
22159
22252
|
NAX_COMMIT = (() => {
|
|
22160
22253
|
try {
|
|
22161
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
22162
|
-
return "
|
|
22254
|
+
if (/^[0-9a-f]{6,10}$/.test("405c88a"))
|
|
22255
|
+
return "405c88a";
|
|
22163
22256
|
} catch {}
|
|
22164
22257
|
try {
|
|
22165
22258
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -23759,6 +23852,20 @@ var init_runner = __esm(() => {
|
|
|
23759
23852
|
init_logger2();
|
|
23760
23853
|
});
|
|
23761
23854
|
|
|
23855
|
+
// src/utils/log-test-output.ts
|
|
23856
|
+
function logTestOutput(logger, stage, output, opts = {}) {
|
|
23857
|
+
if (!logger || !output)
|
|
23858
|
+
return;
|
|
23859
|
+
const tailLines = opts.tailLines ?? 20;
|
|
23860
|
+
const lines = output.split(`
|
|
23861
|
+
`).slice(-tailLines).join(`
|
|
23862
|
+
`);
|
|
23863
|
+
logger.debug(stage, "Test output (tail)", {
|
|
23864
|
+
...opts.storyId !== undefined && { storyId: opts.storyId },
|
|
23865
|
+
output: lines
|
|
23866
|
+
});
|
|
23867
|
+
}
|
|
23868
|
+
|
|
23762
23869
|
// src/pipeline/stages/acceptance.ts
|
|
23763
23870
|
var exports_acceptance = {};
|
|
23764
23871
|
__export(exports_acceptance, {
|
|
@@ -23842,10 +23949,8 @@ ${stderr}`;
|
|
|
23842
23949
|
return { action: "continue" };
|
|
23843
23950
|
}
|
|
23844
23951
|
if (failedACs.length === 0 && exitCode !== 0) {
|
|
23845
|
-
logger.error("acceptance", "Tests errored with no AC failures parsed", {
|
|
23846
|
-
|
|
23847
|
-
output
|
|
23848
|
-
});
|
|
23952
|
+
logger.error("acceptance", "Tests errored with no AC failures parsed", { exitCode });
|
|
23953
|
+
logTestOutput(logger, "acceptance", output);
|
|
23849
23954
|
ctx.acceptanceFailures = {
|
|
23850
23955
|
failedACs: ["AC-ERROR"],
|
|
23851
23956
|
testOutput: output
|
|
@@ -23863,10 +23968,8 @@ ${stderr}`;
|
|
|
23863
23968
|
overrides: overriddenFailures.map((acId) => ({ acId, reason: overrides[acId] }))
|
|
23864
23969
|
});
|
|
23865
23970
|
}
|
|
23866
|
-
logger.error("acceptance", "Acceptance tests failed", {
|
|
23867
|
-
|
|
23868
|
-
output
|
|
23869
|
-
});
|
|
23971
|
+
logger.error("acceptance", "Acceptance tests failed", { failedACs: actualFailures });
|
|
23972
|
+
logTestOutput(logger, "acceptance", output);
|
|
23870
23973
|
ctx.acceptanceFailures = {
|
|
23871
23974
|
failedACs: actualFailures,
|
|
23872
23975
|
testOutput: output
|
|
@@ -23955,6 +24058,7 @@ ${stderr}` };
|
|
|
23955
24058
|
const result = await _acceptanceSetupDeps.generate(ctx.prd.userStories, refinedCriteria, {
|
|
23956
24059
|
featureName: ctx.prd.feature,
|
|
23957
24060
|
workdir: ctx.workdir,
|
|
24061
|
+
featureDir: ctx.featureDir,
|
|
23958
24062
|
codebaseContext: "",
|
|
23959
24063
|
modelTier: ctx.config.acceptance.model ?? "fast",
|
|
23960
24064
|
modelDef: resolveModel(ctx.config.models[ctx.config.acceptance.model ?? "fast"]),
|
|
@@ -25615,7 +25719,7 @@ ${pluginMarkdown}` : pluginMarkdown;
|
|
|
25615
25719
|
};
|
|
25616
25720
|
});
|
|
25617
25721
|
|
|
25618
|
-
// src/agents/validation.ts
|
|
25722
|
+
// src/agents/shared/validation.ts
|
|
25619
25723
|
function validateAgentForTier(agent, tier) {
|
|
25620
25724
|
return agent.capabilities.supportedTiers.includes(tier);
|
|
25621
25725
|
}
|
|
@@ -25629,7 +25733,7 @@ function describeAgentCapabilities(agent) {
|
|
|
25629
25733
|
return `${agent.name}: tiers=[${tiers}], maxTokens=${maxTokens}, features=[${features}]`;
|
|
25630
25734
|
}
|
|
25631
25735
|
|
|
25632
|
-
// src/agents/version-detection.ts
|
|
25736
|
+
// src/agents/shared/version-detection.ts
|
|
25633
25737
|
async function getAgentVersion(binaryName) {
|
|
25634
25738
|
try {
|
|
25635
25739
|
const proc = _versionDetectionDeps.spawn([binaryName, "--version"], {
|
|
@@ -25689,11 +25793,13 @@ __export(exports_agents, {
|
|
|
25689
25793
|
getAgentVersion: () => getAgentVersion,
|
|
25690
25794
|
getAgent: () => getAgent,
|
|
25691
25795
|
formatCostWithConfidence: () => formatCostWithConfidence,
|
|
25796
|
+
estimateCostFromTokenUsage: () => estimateCostFromTokenUsage,
|
|
25692
25797
|
estimateCostFromOutput: () => estimateCostFromOutput,
|
|
25693
25798
|
estimateCostByDuration: () => estimateCostByDuration,
|
|
25694
25799
|
estimateCost: () => estimateCost,
|
|
25695
25800
|
describeAgentCapabilities: () => describeAgentCapabilities,
|
|
25696
25801
|
checkAgentHealth: () => checkAgentHealth,
|
|
25802
|
+
MODEL_PRICING: () => MODEL_PRICING,
|
|
25697
25803
|
CompleteError: () => CompleteError,
|
|
25698
25804
|
ClaudeCodeAdapter: () => ClaudeCodeAdapter,
|
|
25699
25805
|
COST_RATES: () => COST_RATES
|
|
@@ -27742,7 +27848,7 @@ function routeTddFailure(failureCategory, isLiteMode, ctx, reviewReason) {
|
|
|
27742
27848
|
};
|
|
27743
27849
|
}
|
|
27744
27850
|
var executionStage, _executionDeps;
|
|
27745
|
-
var
|
|
27851
|
+
var init_execution2 = __esm(() => {
|
|
27746
27852
|
init_agents();
|
|
27747
27853
|
init_config();
|
|
27748
27854
|
init_triggers();
|
|
@@ -29097,6 +29203,7 @@ var init_regression2 = __esm(() => {
|
|
|
29097
29203
|
storyId: ctx.story.id,
|
|
29098
29204
|
failCount: result.failCount
|
|
29099
29205
|
});
|
|
29206
|
+
logTestOutput(logger, "regression", result.rawOutput, { storyId: ctx.story.id });
|
|
29100
29207
|
pipelineEventBus.emit({
|
|
29101
29208
|
type: "regression:detected",
|
|
29102
29209
|
storyId: ctx.story.id,
|
|
@@ -29395,16 +29502,8 @@ var init_verify = __esm(() => {
|
|
|
29395
29502
|
storyId: ctx.story.id
|
|
29396
29503
|
});
|
|
29397
29504
|
}
|
|
29398
|
-
if (result.
|
|
29399
|
-
|
|
29400
|
-
`).slice(-20);
|
|
29401
|
-
if (outputLines.length > 0) {
|
|
29402
|
-
logger.debug("verify", "Test output preview", {
|
|
29403
|
-
storyId: ctx.story.id,
|
|
29404
|
-
output: outputLines.join(`
|
|
29405
|
-
`)
|
|
29406
|
-
});
|
|
29407
|
-
}
|
|
29505
|
+
if (result.status !== "TIMEOUT") {
|
|
29506
|
+
logTestOutput(logger, "verify", result.output, { storyId: ctx.story.id });
|
|
29408
29507
|
}
|
|
29409
29508
|
return {
|
|
29410
29509
|
action: "escalate",
|
|
@@ -29449,7 +29548,7 @@ var init_stages = __esm(() => {
|
|
|
29449
29548
|
init_completion();
|
|
29450
29549
|
init_constitution2();
|
|
29451
29550
|
init_context2();
|
|
29452
|
-
|
|
29551
|
+
init_execution2();
|
|
29453
29552
|
init_optimizer2();
|
|
29454
29553
|
init_prompt();
|
|
29455
29554
|
init_queue_check();
|
|
@@ -29464,7 +29563,7 @@ var init_stages = __esm(() => {
|
|
|
29464
29563
|
init_context2();
|
|
29465
29564
|
init_prompt();
|
|
29466
29565
|
init_optimizer2();
|
|
29467
|
-
|
|
29566
|
+
init_execution2();
|
|
29468
29567
|
init_verify();
|
|
29469
29568
|
init_rectify();
|
|
29470
29569
|
init_review();
|
|
@@ -30013,12 +30112,15 @@ async function checkWorkingTreeClean(workdir) {
|
|
|
30013
30112
|
});
|
|
30014
30113
|
const output = await new Response(proc.stdout).text();
|
|
30015
30114
|
const exitCode = await proc.exited;
|
|
30016
|
-
const
|
|
30115
|
+
const lines = output.trim() === "" ? [] : output.split(`
|
|
30116
|
+
`).filter(Boolean);
|
|
30117
|
+
const nonNaxDirtyFiles = lines.filter((line) => !NAX_RUNTIME_PATTERNS.some((pattern) => pattern.test(line)));
|
|
30118
|
+
const passed = exitCode === 0 && nonNaxDirtyFiles.length === 0;
|
|
30017
30119
|
return {
|
|
30018
30120
|
name: "working-tree-clean",
|
|
30019
30121
|
tier: "blocker",
|
|
30020
30122
|
passed,
|
|
30021
|
-
message: passed ? "Working tree is clean" :
|
|
30123
|
+
message: passed ? "Working tree is clean" : `Uncommitted changes detected: ${nonNaxDirtyFiles.map((l) => l.slice(3)).join(", ")}`
|
|
30022
30124
|
};
|
|
30023
30125
|
}
|
|
30024
30126
|
async function checkGitUserConfigured(workdir) {
|
|
@@ -30043,7 +30145,23 @@ async function checkGitUserConfigured(workdir) {
|
|
|
30043
30145
|
message: passed ? "Git user is configured" : !hasName && !hasEmail ? "Git user.name and user.email not configured" : !hasName ? "Git user.name not configured" : "Git user.email not configured"
|
|
30044
30146
|
};
|
|
30045
30147
|
}
|
|
30046
|
-
var
|
|
30148
|
+
var NAX_RUNTIME_PATTERNS;
|
|
30149
|
+
var init_checks_git = __esm(() => {
|
|
30150
|
+
NAX_RUNTIME_PATTERNS = [
|
|
30151
|
+
/^.{2} nax\.lock$/,
|
|
30152
|
+
/^.{2} nax\/metrics\.json$/,
|
|
30153
|
+
/^.{2} nax\/features\/[^/]+\/status\.json$/,
|
|
30154
|
+
/^.{2} nax\/features\/[^/]+\/runs\//,
|
|
30155
|
+
/^.{2} nax\/features\/[^/]+\/plan\//,
|
|
30156
|
+
/^.{2} nax\/features\/[^/]+\/acp-sessions\.json$/,
|
|
30157
|
+
/^.{2} nax\/features\/[^/]+\/interactions\//,
|
|
30158
|
+
/^.{2} nax\/features\/[^/]+\/progress\.txt$/,
|
|
30159
|
+
/^.{2} nax\/features\/[^/]+\/acceptance-refined\.json$/,
|
|
30160
|
+
/^.{2} \.nax-verifier-verdict\.json$/,
|
|
30161
|
+
/^.{2} \.nax-pids$/,
|
|
30162
|
+
/^.{2} \.nax-wt\//
|
|
30163
|
+
];
|
|
30164
|
+
});
|
|
30047
30165
|
|
|
30048
30166
|
// src/precheck/checks-config.ts
|
|
30049
30167
|
import { existsSync as existsSync26, statSync as statSync3 } from "fs";
|
|
@@ -30304,6 +30422,7 @@ var init_checks_blockers = __esm(() => {
|
|
|
30304
30422
|
|
|
30305
30423
|
// src/precheck/checks-warnings.ts
|
|
30306
30424
|
import { existsSync as existsSync28 } from "fs";
|
|
30425
|
+
import { isAbsolute as isAbsolute6 } from "path";
|
|
30307
30426
|
async function checkClaudeMdExists(workdir) {
|
|
30308
30427
|
const claudeMdPath = `${workdir}/CLAUDE.md`;
|
|
30309
30428
|
const passed = existsSync28(claudeMdPath);
|
|
@@ -30397,7 +30516,14 @@ async function checkGitignoreCoversNax(workdir) {
|
|
|
30397
30516
|
}
|
|
30398
30517
|
const file2 = Bun.file(gitignorePath);
|
|
30399
30518
|
const content = await file2.text();
|
|
30400
|
-
const patterns = [
|
|
30519
|
+
const patterns = [
|
|
30520
|
+
"nax.lock",
|
|
30521
|
+
"nax/**/runs/",
|
|
30522
|
+
"nax/metrics.json",
|
|
30523
|
+
"nax/features/*/status.json",
|
|
30524
|
+
".nax-pids",
|
|
30525
|
+
".nax-wt/"
|
|
30526
|
+
];
|
|
30401
30527
|
const missing = patterns.filter((pattern) => !content.includes(pattern));
|
|
30402
30528
|
const passed = missing.length === 0;
|
|
30403
30529
|
return {
|
|
@@ -30426,6 +30552,16 @@ async function checkPromptOverrideFiles(config2, workdir) {
|
|
|
30426
30552
|
}
|
|
30427
30553
|
return checks3;
|
|
30428
30554
|
}
|
|
30555
|
+
async function checkHomeEnvValid() {
|
|
30556
|
+
const home = process.env.HOME ?? "";
|
|
30557
|
+
const passed = home !== "" && isAbsolute6(home);
|
|
30558
|
+
return {
|
|
30559
|
+
name: "home-env-valid",
|
|
30560
|
+
tier: "warning",
|
|
30561
|
+
passed,
|
|
30562
|
+
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`
|
|
30563
|
+
};
|
|
30564
|
+
}
|
|
30429
30565
|
var init_checks_warnings = () => {};
|
|
30430
30566
|
|
|
30431
30567
|
// src/precheck/checks-agents.ts
|
|
@@ -30606,6 +30742,7 @@ function getEnvironmentWarnings(config2, workdir) {
|
|
|
30606
30742
|
() => checkDiskSpace(),
|
|
30607
30743
|
() => checkOptionalCommands(config2, workdir),
|
|
30608
30744
|
() => checkGitignoreCoversNax(workdir),
|
|
30745
|
+
() => checkHomeEnvValid(),
|
|
30609
30746
|
() => checkPromptOverrideFiles(config2, workdir),
|
|
30610
30747
|
() => checkMultiAgentHealth()
|
|
30611
30748
|
];
|
|
@@ -32846,12 +32983,12 @@ var init_parallel_executor = __esm(() => {
|
|
|
32846
32983
|
|
|
32847
32984
|
// src/pipeline/subscribers/events-writer.ts
|
|
32848
32985
|
import { appendFile as appendFile2, mkdir } from "fs/promises";
|
|
32849
|
-
import { homedir as
|
|
32986
|
+
import { homedir as homedir7 } from "os";
|
|
32850
32987
|
import { basename as basename3, join as join40 } from "path";
|
|
32851
32988
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
32852
32989
|
const logger = getSafeLogger();
|
|
32853
32990
|
const project = basename3(workdir);
|
|
32854
|
-
const eventsDir = join40(
|
|
32991
|
+
const eventsDir = join40(homedir7(), ".nax", "events", project);
|
|
32855
32992
|
const eventsFile = join40(eventsDir, "events.jsonl");
|
|
32856
32993
|
let dirReady = false;
|
|
32857
32994
|
const write = (line) => {
|
|
@@ -33011,12 +33148,12 @@ var init_interaction2 = __esm(() => {
|
|
|
33011
33148
|
|
|
33012
33149
|
// src/pipeline/subscribers/registry.ts
|
|
33013
33150
|
import { mkdir as mkdir2, writeFile } from "fs/promises";
|
|
33014
|
-
import { homedir as
|
|
33151
|
+
import { homedir as homedir8 } from "os";
|
|
33015
33152
|
import { basename as basename4, join as join41 } from "path";
|
|
33016
33153
|
function wireRegistry(bus, feature, runId, workdir) {
|
|
33017
33154
|
const logger = getSafeLogger();
|
|
33018
33155
|
const project = basename4(workdir);
|
|
33019
|
-
const runDir = join41(
|
|
33156
|
+
const runDir = join41(homedir8(), ".nax", "runs", `${project}-${feature}-${runId}`);
|
|
33020
33157
|
const metaFile = join41(runDir, "meta.json");
|
|
33021
33158
|
const unsub = bus.on("run:started", (_ev) => {
|
|
33022
33159
|
(async () => {
|
|
@@ -34454,7 +34591,7 @@ async function setupRun(options) {
|
|
|
34454
34591
|
} else {
|
|
34455
34592
|
logger?.warn("precheck", "Precheck validations skipped (--skip-precheck)");
|
|
34456
34593
|
}
|
|
34457
|
-
const { sweepStaleFeatureSessions: sweepStaleFeatureSessions2 } = await Promise.resolve().then(() => (
|
|
34594
|
+
const { sweepStaleFeatureSessions: sweepStaleFeatureSessions2 } = await Promise.resolve().then(() => (init_adapter2(), exports_adapter));
|
|
34458
34595
|
await sweepStaleFeatureSessions2(workdir, feature).catch(() => {});
|
|
34459
34596
|
const lockAcquired = await acquireLock(workdir);
|
|
34460
34597
|
if (!lockAcquired) {
|
|
@@ -65416,7 +65553,7 @@ var require_jsx_dev_runtime = __commonJS((exports, module) => {
|
|
|
65416
65553
|
// bin/nax.ts
|
|
65417
65554
|
init_source();
|
|
65418
65555
|
import { existsSync as existsSync32, mkdirSync as mkdirSync6 } from "fs";
|
|
65419
|
-
import { homedir as
|
|
65556
|
+
import { homedir as homedir10 } from "os";
|
|
65420
65557
|
import { join as join43 } from "path";
|
|
65421
65558
|
|
|
65422
65559
|
// node_modules/commander/esm.mjs
|
|
@@ -68572,10 +68709,10 @@ import { join as join32 } from "path";
|
|
|
68572
68709
|
// src/commands/logs-reader.ts
|
|
68573
68710
|
import { existsSync as existsSync23, readdirSync as readdirSync6 } from "fs";
|
|
68574
68711
|
import { readdir as readdir3 } from "fs/promises";
|
|
68575
|
-
import { homedir as
|
|
68712
|
+
import { homedir as homedir5 } from "os";
|
|
68576
68713
|
import { join as join31 } from "path";
|
|
68577
68714
|
var _deps6 = {
|
|
68578
|
-
getRunsDir: () => process.env.NAX_RUNS_DIR ?? join31(
|
|
68715
|
+
getRunsDir: () => process.env.NAX_RUNS_DIR ?? join31(homedir5(), ".nax", "runs")
|
|
68579
68716
|
};
|
|
68580
68717
|
async function resolveRunFileFromRegistry(runId) {
|
|
68581
68718
|
const runsDir = _deps6.getRunsDir();
|
|
@@ -68901,11 +69038,11 @@ async function precheckCommand(options) {
|
|
|
68901
69038
|
// src/commands/runs.ts
|
|
68902
69039
|
init_source();
|
|
68903
69040
|
import { readdir as readdir4 } from "fs/promises";
|
|
68904
|
-
import { homedir as
|
|
69041
|
+
import { homedir as homedir6 } from "os";
|
|
68905
69042
|
import { join as join35 } from "path";
|
|
68906
69043
|
var DEFAULT_LIMIT = 20;
|
|
68907
69044
|
var _deps8 = {
|
|
68908
|
-
getRunsDir: () => join35(
|
|
69045
|
+
getRunsDir: () => join35(homedir6(), ".nax", "runs")
|
|
68909
69046
|
};
|
|
68910
69047
|
function formatDuration3(ms) {
|
|
68911
69048
|
if (ms <= 0)
|
|
@@ -69088,7 +69225,7 @@ async function unlockCommand(options) {
|
|
|
69088
69225
|
init_config();
|
|
69089
69226
|
|
|
69090
69227
|
// src/execution/runner.ts
|
|
69091
|
-
|
|
69228
|
+
init_adapter2();
|
|
69092
69229
|
init_registry();
|
|
69093
69230
|
init_hooks();
|
|
69094
69231
|
init_logger2();
|
|
@@ -77039,9 +77176,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
77039
77176
|
}
|
|
77040
77177
|
}
|
|
77041
77178
|
try {
|
|
77042
|
-
|
|
77179
|
+
const planLogDir = join43(featureDir, "plan");
|
|
77180
|
+
mkdirSync6(planLogDir, { recursive: true });
|
|
77043
77181
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
77044
|
-
const planLogPath = join43(
|
|
77182
|
+
const planLogPath = join43(planLogDir, `${planLogId}.jsonl`);
|
|
77045
77183
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
77046
77184
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
77047
77185
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -77098,7 +77236,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
77098
77236
|
config2.autoMode.defaultAgent = options.agent;
|
|
77099
77237
|
}
|
|
77100
77238
|
config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
|
|
77101
|
-
const globalNaxDir = join43(
|
|
77239
|
+
const globalNaxDir = join43(homedir10(), ".nax");
|
|
77102
77240
|
const hooks = await loadHooksConfig(naxDir, globalNaxDir);
|
|
77103
77241
|
const eventEmitter = new PipelineEventEmitter;
|
|
77104
77242
|
let tuiInstance;
|
|
@@ -77286,10 +77424,10 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
77286
77424
|
process.exit(1);
|
|
77287
77425
|
}
|
|
77288
77426
|
const config2 = await loadConfig(workdir);
|
|
77289
|
-
const featureLogDir = join43(naxDir, "features", options.feature);
|
|
77427
|
+
const featureLogDir = join43(naxDir, "features", options.feature, "plan");
|
|
77290
77428
|
mkdirSync6(featureLogDir, { recursive: true });
|
|
77291
77429
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
77292
|
-
const planLogPath = join43(featureLogDir,
|
|
77430
|
+
const planLogPath = join43(featureLogDir, `${planLogId}.jsonl`);
|
|
77293
77431
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
77294
77432
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
77295
77433
|
try {
|