@nathapp/nax 0.45.0 → 0.46.0
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 +19 -0
- package/bin/nax.ts +7 -6
- package/dist/nax.js +181 -124
- package/package.json +1 -1
- package/src/agents/acp/adapter.ts +34 -6
- package/src/agents/acp/index.ts +0 -2
- package/src/agents/acp/parser.ts +57 -104
- package/src/agents/acp/spawn-client.ts +2 -1
- package/src/agents/{claude.ts → claude/adapter.ts} +15 -12
- package/src/agents/{claude-complete.ts → claude/complete.ts} +3 -3
- package/src/agents/{cost.ts → claude/cost.ts} +1 -1
- package/src/agents/{claude-execution.ts → claude/execution.ts} +5 -5
- 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/index.ts +5 -5
- 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 +8 -4
- package/src/cli/agents.ts +1 -1
- 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/utils/log-test-output.ts +25 -0
- /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/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.46.0] - 2026-03-16
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **ACP cost metric:** Cost was always `$0` for ACP sessions. `parseAcpxJsonOutput` now handles JSON-RPC envelope format (acpx v0.3+): extracts text from `agent_message_chunk`, captures exact USD cost from `usage_update` (`cost.amount`), and reads camelCase token breakdown (`inputTokens`, `outputTokens`, `cachedReadTokens`, `cachedWriteTokens`) from `result.usage`.
|
|
12
|
+
- **ACP `complete()` cost:** Now logs exact cost via `getSafeLogger()` — previously had zero cost tracking.
|
|
13
|
+
- **`run()` cost:** Prefers exact `cost.amount` from acpx over token-based estimation; falls back to `estimateCostFromTokenUsage` when unavailable.
|
|
14
|
+
|
|
15
|
+
### Refactored
|
|
16
|
+
- **`src/agents/` folder restructure:** Each adapter now lives in its own subfolder for consistency.
|
|
17
|
+
- `claude/` — Claude Code adapter (adapter, execution, complete, interactive, plan, cost)
|
|
18
|
+
- `acp/` — ACP protocol adapter (unchanged internals)
|
|
19
|
+
- `aider/`, `codex/`, `gemini/`, `opencode/` — per-adapter subfolders
|
|
20
|
+
- `shared/` — cross-adapter utilities: `decompose` (extracted from both claude + acp), `model-resolution`, `validation`, `version-detection`, `types-extended`
|
|
21
|
+
- **Dead code removal:** `streamJsonRpcEvents` (exported but never called), stale `estimateCostFromTokenUsage` re-export from `acp/index.ts`.
|
|
22
|
+
|
|
23
|
+
### Docs
|
|
24
|
+
- **ARCHITECTURE.md §1:** Updated `src/agents/` tree.
|
|
25
|
+
- **ARCHITECTURE.md §16:** New section — agent adapter folder conventions, `shared/` rules, ACP cost alignment.
|
|
26
|
+
|
|
8
27
|
## [0.43.0] - 2026-03-16
|
|
9
28
|
|
|
10
29
|
### Added
|
package/bin/nax.ts
CHANGED
|
@@ -366,10 +366,11 @@ program
|
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
try {
|
|
369
|
-
// Initialize plan logger before calling planCommand — writes to features/<feature>/plan
|
|
370
|
-
|
|
369
|
+
// Initialize plan logger before calling planCommand — writes to features/<feature>/plan/<ts>.jsonl
|
|
370
|
+
const planLogDir = join(featureDir, "plan");
|
|
371
|
+
mkdirSync(planLogDir, { recursive: true });
|
|
371
372
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
372
|
-
const planLogPath = join(
|
|
373
|
+
const planLogPath = join(planLogDir, `${planLogId}.jsonl`);
|
|
373
374
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
374
375
|
console.log(chalk.dim(` [Plan log: ${planLogPath}]`));
|
|
375
376
|
|
|
@@ -688,11 +689,11 @@ program
|
|
|
688
689
|
// Load config
|
|
689
690
|
const config = await loadConfig(workdir);
|
|
690
691
|
|
|
691
|
-
// Initialize logger — writes to nax/features/<feature>/plan
|
|
692
|
-
const featureLogDir = join(naxDir, "features", options.feature);
|
|
692
|
+
// Initialize logger — writes to nax/features/<feature>/plan/<timestamp>.jsonl
|
|
693
|
+
const featureLogDir = join(naxDir, "features", options.feature, "plan");
|
|
693
694
|
mkdirSync(featureLogDir, { recursive: true });
|
|
694
695
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
695
|
-
const planLogPath = join(featureLogDir,
|
|
696
|
+
const planLogPath = join(featureLogDir, `${planLogId}.jsonl`);
|
|
696
697
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
697
698
|
console.log(chalk.dim(` [Plan log: ${planLogPath}]`));
|
|
698
699
|
|
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,65 @@ 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/claude/cost.ts
|
|
3462
3462
|
function parseTokenUsage(output) {
|
|
3463
3463
|
try {
|
|
3464
3464
|
const jsonMatch = output.match(/\{[^}]*"usage"\s*:\s*\{[^}]*"input_tokens"\s*:\s*(\d+)[^}]*"output_tokens"\s*:\s*(\d+)[^}]*\}[^}]*\}/);
|
|
@@ -3561,7 +3561,7 @@ var init_cost = __esm(() => {
|
|
|
3561
3561
|
};
|
|
3562
3562
|
});
|
|
3563
3563
|
|
|
3564
|
-
// src/agents/claude
|
|
3564
|
+
// src/agents/claude/execution.ts
|
|
3565
3565
|
function buildCommand(binary, options) {
|
|
3566
3566
|
const model = options.modelDef.model;
|
|
3567
3567
|
const { skipPermissions } = resolvePermissions(options.config, options.pipelineStage ?? "run");
|
|
@@ -3663,7 +3663,7 @@ async function executeOnce(binary, options, pidRegistry) {
|
|
|
3663
3663
|
};
|
|
3664
3664
|
}
|
|
3665
3665
|
var MAX_AGENT_OUTPUT_CHARS = 5000, MAX_AGENT_STDERR_CHARS = 1000, SIGKILL_GRACE_PERIOD_MS = 5000, _runOnceDeps;
|
|
3666
|
-
var
|
|
3666
|
+
var init_execution = __esm(() => {
|
|
3667
3667
|
init_logger2();
|
|
3668
3668
|
init_cost();
|
|
3669
3669
|
_runOnceDeps = {
|
|
@@ -3676,7 +3676,7 @@ var init_claude_execution = __esm(() => {
|
|
|
3676
3676
|
};
|
|
3677
3677
|
});
|
|
3678
3678
|
|
|
3679
|
-
// src/agents/claude
|
|
3679
|
+
// src/agents/claude/interactive.ts
|
|
3680
3680
|
function runInteractiveMode(binary, options, pidRegistry) {
|
|
3681
3681
|
const model = options.modelDef.model;
|
|
3682
3682
|
const cmd = [binary, "--model", model, options.prompt];
|
|
@@ -3715,9 +3715,9 @@ function runInteractiveMode(binary, options, pidRegistry) {
|
|
|
3715
3715
|
pid: proc.pid
|
|
3716
3716
|
};
|
|
3717
3717
|
}
|
|
3718
|
-
var
|
|
3718
|
+
var init_interactive = __esm(() => {
|
|
3719
3719
|
init_logger2();
|
|
3720
|
-
|
|
3720
|
+
init_execution();
|
|
3721
3721
|
});
|
|
3722
3722
|
|
|
3723
3723
|
// src/config/schema-types.ts
|
|
@@ -18139,7 +18139,7 @@ var init_schema = __esm(() => {
|
|
|
18139
18139
|
init_defaults();
|
|
18140
18140
|
});
|
|
18141
18141
|
|
|
18142
|
-
// src/agents/model-resolution.ts
|
|
18142
|
+
// src/agents/shared/model-resolution.ts
|
|
18143
18143
|
var exports_model_resolution = {};
|
|
18144
18144
|
__export(exports_model_resolution, {
|
|
18145
18145
|
resolveBalancedModelDef: () => resolveBalancedModelDef
|
|
@@ -18160,7 +18160,7 @@ var init_model_resolution = __esm(() => {
|
|
|
18160
18160
|
init_schema();
|
|
18161
18161
|
});
|
|
18162
18162
|
|
|
18163
|
-
// src/agents/claude
|
|
18163
|
+
// src/agents/claude/plan.ts
|
|
18164
18164
|
import { mkdtempSync, rmSync } from "fs";
|
|
18165
18165
|
import { tmpdir } from "os";
|
|
18166
18166
|
import { join } from "path";
|
|
@@ -18279,12 +18279,12 @@ async function runPlan(binary, options, pidRegistry, buildAllowedEnv2) {
|
|
|
18279
18279
|
}
|
|
18280
18280
|
}
|
|
18281
18281
|
}
|
|
18282
|
-
var
|
|
18282
|
+
var init_plan = __esm(() => {
|
|
18283
18283
|
init_logger2();
|
|
18284
18284
|
init_model_resolution();
|
|
18285
18285
|
});
|
|
18286
18286
|
|
|
18287
|
-
// src/agents/claude.ts
|
|
18287
|
+
// src/agents/claude/adapter.ts
|
|
18288
18288
|
class ClaudeCodeAdapter {
|
|
18289
18289
|
name = "claude";
|
|
18290
18290
|
displayName = "Claude Code";
|
|
@@ -18435,14 +18435,14 @@ class ClaudeCodeAdapter {
|
|
|
18435
18435
|
}
|
|
18436
18436
|
}
|
|
18437
18437
|
var _decomposeDeps, _claudeAdapterDeps;
|
|
18438
|
-
var
|
|
18438
|
+
var init_adapter = __esm(() => {
|
|
18439
18439
|
init_pid_registry();
|
|
18440
18440
|
init_logger2();
|
|
18441
|
-
|
|
18442
|
-
|
|
18443
|
-
|
|
18444
|
-
|
|
18445
|
-
|
|
18441
|
+
init_decompose();
|
|
18442
|
+
init_complete();
|
|
18443
|
+
init_execution();
|
|
18444
|
+
init_interactive();
|
|
18445
|
+
init_plan();
|
|
18446
18446
|
_decomposeDeps = {
|
|
18447
18447
|
spawn(cmd, opts) {
|
|
18448
18448
|
return Bun.spawn(cmd, opts);
|
|
@@ -18453,6 +18453,12 @@ var init_claude = __esm(() => {
|
|
|
18453
18453
|
};
|
|
18454
18454
|
});
|
|
18455
18455
|
|
|
18456
|
+
// src/agents/claude/index.ts
|
|
18457
|
+
var init_claude = __esm(() => {
|
|
18458
|
+
init_adapter();
|
|
18459
|
+
init_execution();
|
|
18460
|
+
});
|
|
18461
|
+
|
|
18456
18462
|
// src/utils/errors.ts
|
|
18457
18463
|
function errorMessage(err) {
|
|
18458
18464
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -18977,11 +18983,40 @@ function parseAcpxJsonOutput(rawOutput) {
|
|
|
18977
18983
|
`).filter((l) => l.trim());
|
|
18978
18984
|
let text = "";
|
|
18979
18985
|
let tokenUsage;
|
|
18986
|
+
let exactCostUsd;
|
|
18980
18987
|
let stopReason;
|
|
18981
18988
|
let error48;
|
|
18982
18989
|
for (const line of lines) {
|
|
18983
18990
|
try {
|
|
18984
18991
|
const event = JSON.parse(line);
|
|
18992
|
+
if (event.jsonrpc === "2.0") {
|
|
18993
|
+
if (event.method === "session/update" && event.params?.update) {
|
|
18994
|
+
const update = event.params.update;
|
|
18995
|
+
if (update.sessionUpdate === "agent_message_chunk" && update.content?.type === "text" && update.content.text) {
|
|
18996
|
+
text += update.content.text;
|
|
18997
|
+
}
|
|
18998
|
+
if (update.sessionUpdate === "usage_update" && typeof update.cost?.amount === "number") {
|
|
18999
|
+
exactCostUsd = update.cost.amount;
|
|
19000
|
+
}
|
|
19001
|
+
}
|
|
19002
|
+
if (event.id !== undefined && event.result && typeof event.result === "object") {
|
|
19003
|
+
const result = event.result;
|
|
19004
|
+
if (result.stopReason)
|
|
19005
|
+
stopReason = result.stopReason;
|
|
19006
|
+
if (result.stop_reason)
|
|
19007
|
+
stopReason = result.stop_reason;
|
|
19008
|
+
if (result.usage && typeof result.usage === "object") {
|
|
19009
|
+
const u = result.usage;
|
|
19010
|
+
tokenUsage = {
|
|
19011
|
+
input_tokens: u.inputTokens ?? u.input_tokens ?? 0,
|
|
19012
|
+
output_tokens: u.outputTokens ?? u.output_tokens ?? 0,
|
|
19013
|
+
cache_read_input_tokens: u.cachedReadTokens ?? u.cache_read_input_tokens ?? 0,
|
|
19014
|
+
cache_creation_input_tokens: u.cachedWriteTokens ?? u.cache_creation_input_tokens ?? 0
|
|
19015
|
+
};
|
|
19016
|
+
}
|
|
19017
|
+
}
|
|
19018
|
+
continue;
|
|
19019
|
+
}
|
|
18985
19020
|
if (event.content && typeof event.content === "string")
|
|
18986
19021
|
text += event.content;
|
|
18987
19022
|
if (event.text && typeof event.text === "string")
|
|
@@ -19008,7 +19043,7 @@ function parseAcpxJsonOutput(rawOutput) {
|
|
|
19008
19043
|
text = line;
|
|
19009
19044
|
}
|
|
19010
19045
|
}
|
|
19011
|
-
return { text: text.trim(), tokenUsage, stopReason, error: error48 };
|
|
19046
|
+
return { text: text.trim(), tokenUsage, exactCostUsd, stopReason, error: error48 };
|
|
19012
19047
|
}
|
|
19013
19048
|
|
|
19014
19049
|
// src/agents/acp/spawn-client.ts
|
|
@@ -19107,8 +19142,9 @@ class SpawnAcpSession {
|
|
|
19107
19142
|
const parsed = parseAcpxJsonOutput(stdout);
|
|
19108
19143
|
return {
|
|
19109
19144
|
messages: [{ role: "assistant", content: parsed.text || "" }],
|
|
19110
|
-
stopReason: "end_turn",
|
|
19111
|
-
cumulative_token_usage: parsed.tokenUsage
|
|
19145
|
+
stopReason: parsed.stopReason ?? "end_turn",
|
|
19146
|
+
cumulative_token_usage: parsed.tokenUsage,
|
|
19147
|
+
exactCostUsd: parsed.exactCostUsd
|
|
19112
19148
|
};
|
|
19113
19149
|
} catch (err) {
|
|
19114
19150
|
getSafeLogger()?.warn("acp-adapter", "Failed to parse session prompt response", {
|
|
@@ -19569,7 +19605,13 @@ class AcpAgentAdapter {
|
|
|
19569
19605
|
let lastResponse = null;
|
|
19570
19606
|
let timedOut = false;
|
|
19571
19607
|
const runState = { succeeded: false };
|
|
19572
|
-
const totalTokenUsage = {
|
|
19608
|
+
const totalTokenUsage = {
|
|
19609
|
+
input_tokens: 0,
|
|
19610
|
+
output_tokens: 0,
|
|
19611
|
+
cache_read_input_tokens: 0,
|
|
19612
|
+
cache_creation_input_tokens: 0
|
|
19613
|
+
};
|
|
19614
|
+
let totalExactCostUsd;
|
|
19573
19615
|
try {
|
|
19574
19616
|
let currentPrompt = options.prompt;
|
|
19575
19617
|
let turnCount = 0;
|
|
@@ -19588,6 +19630,11 @@ class AcpAgentAdapter {
|
|
|
19588
19630
|
if (lastResponse.cumulative_token_usage) {
|
|
19589
19631
|
totalTokenUsage.input_tokens += lastResponse.cumulative_token_usage.input_tokens ?? 0;
|
|
19590
19632
|
totalTokenUsage.output_tokens += lastResponse.cumulative_token_usage.output_tokens ?? 0;
|
|
19633
|
+
totalTokenUsage.cache_read_input_tokens += lastResponse.cumulative_token_usage.cache_read_input_tokens ?? 0;
|
|
19634
|
+
totalTokenUsage.cache_creation_input_tokens += lastResponse.cumulative_token_usage.cache_creation_input_tokens ?? 0;
|
|
19635
|
+
}
|
|
19636
|
+
if (lastResponse.exactCostUsd !== undefined) {
|
|
19637
|
+
totalExactCostUsd = (totalExactCostUsd ?? 0) + lastResponse.exactCostUsd;
|
|
19591
19638
|
}
|
|
19592
19639
|
const outputText = extractOutput(lastResponse);
|
|
19593
19640
|
const question = extractQuestion(outputText);
|
|
@@ -19634,7 +19681,7 @@ class AcpAgentAdapter {
|
|
|
19634
19681
|
}
|
|
19635
19682
|
const success2 = lastResponse?.stopReason === "end_turn";
|
|
19636
19683
|
const output = extractOutput(lastResponse);
|
|
19637
|
-
const estimatedCost = totalTokenUsage.input_tokens > 0 || totalTokenUsage.output_tokens > 0 ? estimateCostFromTokenUsage(totalTokenUsage, options.modelDef.model) : 0;
|
|
19684
|
+
const estimatedCost = totalExactCostUsd ?? (totalTokenUsage.input_tokens > 0 || totalTokenUsage.output_tokens > 0 ? estimateCostFromTokenUsage(totalTokenUsage, options.modelDef.model) : 0);
|
|
19638
19685
|
return {
|
|
19639
19686
|
success: success2,
|
|
19640
19687
|
exitCode: success2 ? 0 : 1,
|
|
@@ -19684,6 +19731,12 @@ class AcpAgentAdapter {
|
|
|
19684
19731
|
if (!unwrapped) {
|
|
19685
19732
|
throw new CompleteError("complete() returned empty output");
|
|
19686
19733
|
}
|
|
19734
|
+
if (response.exactCostUsd !== undefined) {
|
|
19735
|
+
getSafeLogger()?.info("acp-adapter", "complete() cost", {
|
|
19736
|
+
costUsd: response.exactCostUsd,
|
|
19737
|
+
model
|
|
19738
|
+
});
|
|
19739
|
+
}
|
|
19687
19740
|
return unwrapped;
|
|
19688
19741
|
} catch (err) {
|
|
19689
19742
|
const error48 = err instanceof Error ? err : new Error(String(err));
|
|
@@ -19770,9 +19823,9 @@ class AcpAgentAdapter {
|
|
|
19770
19823
|
}
|
|
19771
19824
|
}
|
|
19772
19825
|
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
|
|
19826
|
+
var init_adapter2 = __esm(() => {
|
|
19774
19827
|
init_logger2();
|
|
19775
|
-
|
|
19828
|
+
init_decompose();
|
|
19776
19829
|
init_spawn_client();
|
|
19777
19830
|
init_types2();
|
|
19778
19831
|
init_cost2();
|
|
@@ -19817,7 +19870,7 @@ var init_adapter = __esm(() => {
|
|
|
19817
19870
|
MAX_SESSION_AGE_MS = 2 * 60 * 60 * 1000;
|
|
19818
19871
|
});
|
|
19819
19872
|
|
|
19820
|
-
// src/agents/
|
|
19873
|
+
// src/agents/aider/adapter.ts
|
|
19821
19874
|
class AiderAdapter {
|
|
19822
19875
|
name = "aider";
|
|
19823
19876
|
displayName = "Aider";
|
|
@@ -19882,7 +19935,7 @@ class AiderAdapter {
|
|
|
19882
19935
|
}
|
|
19883
19936
|
}
|
|
19884
19937
|
var _aiderCompleteDeps, MAX_AGENT_OUTPUT_CHARS3 = 5000;
|
|
19885
|
-
var
|
|
19938
|
+
var init_adapter3 = __esm(() => {
|
|
19886
19939
|
init_types2();
|
|
19887
19940
|
_aiderCompleteDeps = {
|
|
19888
19941
|
which(name) {
|
|
@@ -19894,7 +19947,7 @@ var init_aider = __esm(() => {
|
|
|
19894
19947
|
};
|
|
19895
19948
|
});
|
|
19896
19949
|
|
|
19897
|
-
// src/agents/
|
|
19950
|
+
// src/agents/codex/adapter.ts
|
|
19898
19951
|
class CodexAdapter {
|
|
19899
19952
|
name = "codex";
|
|
19900
19953
|
displayName = "Codex";
|
|
@@ -19957,7 +20010,7 @@ class CodexAdapter {
|
|
|
19957
20010
|
}
|
|
19958
20011
|
}
|
|
19959
20012
|
var _codexRunDeps, _codexCompleteDeps, MAX_AGENT_OUTPUT_CHARS4 = 5000;
|
|
19960
|
-
var
|
|
20013
|
+
var init_adapter4 = __esm(() => {
|
|
19961
20014
|
init_types2();
|
|
19962
20015
|
_codexRunDeps = {
|
|
19963
20016
|
which(name) {
|
|
@@ -19974,7 +20027,7 @@ var init_codex = __esm(() => {
|
|
|
19974
20027
|
};
|
|
19975
20028
|
});
|
|
19976
20029
|
|
|
19977
|
-
// src/agents/
|
|
20030
|
+
// src/agents/gemini/adapter.ts
|
|
19978
20031
|
class GeminiAdapter {
|
|
19979
20032
|
name = "gemini";
|
|
19980
20033
|
displayName = "Gemini CLI";
|
|
@@ -20057,7 +20110,7 @@ class GeminiAdapter {
|
|
|
20057
20110
|
}
|
|
20058
20111
|
}
|
|
20059
20112
|
var _geminiRunDeps, _geminiCompleteDeps, MAX_AGENT_OUTPUT_CHARS5 = 5000;
|
|
20060
|
-
var
|
|
20113
|
+
var init_adapter5 = __esm(() => {
|
|
20061
20114
|
init_types2();
|
|
20062
20115
|
_geminiRunDeps = {
|
|
20063
20116
|
which(name) {
|
|
@@ -20074,7 +20127,7 @@ var init_gemini = __esm(() => {
|
|
|
20074
20127
|
};
|
|
20075
20128
|
});
|
|
20076
20129
|
|
|
20077
|
-
// src/agents/
|
|
20130
|
+
// src/agents/opencode/adapter.ts
|
|
20078
20131
|
class OpenCodeAdapter {
|
|
20079
20132
|
name = "opencode";
|
|
20080
20133
|
displayName = "OpenCode";
|
|
@@ -20119,7 +20172,7 @@ class OpenCodeAdapter {
|
|
|
20119
20172
|
}
|
|
20120
20173
|
}
|
|
20121
20174
|
var _opencodeCompleteDeps;
|
|
20122
|
-
var
|
|
20175
|
+
var init_adapter6 = __esm(() => {
|
|
20123
20176
|
init_types2();
|
|
20124
20177
|
_opencodeCompleteDeps = {
|
|
20125
20178
|
which(name) {
|
|
@@ -20211,12 +20264,12 @@ function createAgentRegistry(config2) {
|
|
|
20211
20264
|
var ALL_AGENTS;
|
|
20212
20265
|
var init_registry = __esm(() => {
|
|
20213
20266
|
init_logger2();
|
|
20267
|
+
init_adapter2();
|
|
20268
|
+
init_adapter3();
|
|
20214
20269
|
init_adapter();
|
|
20215
|
-
|
|
20216
|
-
|
|
20217
|
-
|
|
20218
|
-
init_opencode();
|
|
20219
|
-
init_claude();
|
|
20270
|
+
init_adapter4();
|
|
20271
|
+
init_adapter5();
|
|
20272
|
+
init_adapter6();
|
|
20220
20273
|
ALL_AGENTS = [
|
|
20221
20274
|
new ClaudeCodeAdapter,
|
|
20222
20275
|
new CodexAdapter,
|
|
@@ -22085,7 +22138,7 @@ var package_default;
|
|
|
22085
22138
|
var init_package = __esm(() => {
|
|
22086
22139
|
package_default = {
|
|
22087
22140
|
name: "@nathapp/nax",
|
|
22088
|
-
version: "0.
|
|
22141
|
+
version: "0.46.0",
|
|
22089
22142
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
22090
22143
|
type: "module",
|
|
22091
22144
|
bin: {
|
|
@@ -22158,8 +22211,8 @@ var init_version = __esm(() => {
|
|
|
22158
22211
|
NAX_VERSION = package_default.version;
|
|
22159
22212
|
NAX_COMMIT = (() => {
|
|
22160
22213
|
try {
|
|
22161
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
22162
|
-
return "
|
|
22214
|
+
if (/^[0-9a-f]{6,10}$/.test("6a485b9"))
|
|
22215
|
+
return "6a485b9";
|
|
22163
22216
|
} catch {}
|
|
22164
22217
|
try {
|
|
22165
22218
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -23759,6 +23812,20 @@ var init_runner = __esm(() => {
|
|
|
23759
23812
|
init_logger2();
|
|
23760
23813
|
});
|
|
23761
23814
|
|
|
23815
|
+
// src/utils/log-test-output.ts
|
|
23816
|
+
function logTestOutput(logger, stage, output, opts = {}) {
|
|
23817
|
+
if (!logger || !output)
|
|
23818
|
+
return;
|
|
23819
|
+
const tailLines = opts.tailLines ?? 20;
|
|
23820
|
+
const lines = output.split(`
|
|
23821
|
+
`).slice(-tailLines).join(`
|
|
23822
|
+
`);
|
|
23823
|
+
logger.debug(stage, "Test output (tail)", {
|
|
23824
|
+
...opts.storyId !== undefined && { storyId: opts.storyId },
|
|
23825
|
+
output: lines
|
|
23826
|
+
});
|
|
23827
|
+
}
|
|
23828
|
+
|
|
23762
23829
|
// src/pipeline/stages/acceptance.ts
|
|
23763
23830
|
var exports_acceptance = {};
|
|
23764
23831
|
__export(exports_acceptance, {
|
|
@@ -23842,10 +23909,8 @@ ${stderr}`;
|
|
|
23842
23909
|
return { action: "continue" };
|
|
23843
23910
|
}
|
|
23844
23911
|
if (failedACs.length === 0 && exitCode !== 0) {
|
|
23845
|
-
logger.error("acceptance", "Tests errored with no AC failures parsed", {
|
|
23846
|
-
|
|
23847
|
-
output
|
|
23848
|
-
});
|
|
23912
|
+
logger.error("acceptance", "Tests errored with no AC failures parsed", { exitCode });
|
|
23913
|
+
logTestOutput(logger, "acceptance", output);
|
|
23849
23914
|
ctx.acceptanceFailures = {
|
|
23850
23915
|
failedACs: ["AC-ERROR"],
|
|
23851
23916
|
testOutput: output
|
|
@@ -23863,10 +23928,8 @@ ${stderr}`;
|
|
|
23863
23928
|
overrides: overriddenFailures.map((acId) => ({ acId, reason: overrides[acId] }))
|
|
23864
23929
|
});
|
|
23865
23930
|
}
|
|
23866
|
-
logger.error("acceptance", "Acceptance tests failed", {
|
|
23867
|
-
|
|
23868
|
-
output
|
|
23869
|
-
});
|
|
23931
|
+
logger.error("acceptance", "Acceptance tests failed", { failedACs: actualFailures });
|
|
23932
|
+
logTestOutput(logger, "acceptance", output);
|
|
23870
23933
|
ctx.acceptanceFailures = {
|
|
23871
23934
|
failedACs: actualFailures,
|
|
23872
23935
|
testOutput: output
|
|
@@ -25615,7 +25678,7 @@ ${pluginMarkdown}` : pluginMarkdown;
|
|
|
25615
25678
|
};
|
|
25616
25679
|
});
|
|
25617
25680
|
|
|
25618
|
-
// src/agents/validation.ts
|
|
25681
|
+
// src/agents/shared/validation.ts
|
|
25619
25682
|
function validateAgentForTier(agent, tier) {
|
|
25620
25683
|
return agent.capabilities.supportedTiers.includes(tier);
|
|
25621
25684
|
}
|
|
@@ -25629,7 +25692,7 @@ function describeAgentCapabilities(agent) {
|
|
|
25629
25692
|
return `${agent.name}: tiers=[${tiers}], maxTokens=${maxTokens}, features=[${features}]`;
|
|
25630
25693
|
}
|
|
25631
25694
|
|
|
25632
|
-
// src/agents/version-detection.ts
|
|
25695
|
+
// src/agents/shared/version-detection.ts
|
|
25633
25696
|
async function getAgentVersion(binaryName) {
|
|
25634
25697
|
try {
|
|
25635
25698
|
const proc = _versionDetectionDeps.spawn([binaryName, "--version"], {
|
|
@@ -27742,7 +27805,7 @@ function routeTddFailure(failureCategory, isLiteMode, ctx, reviewReason) {
|
|
|
27742
27805
|
};
|
|
27743
27806
|
}
|
|
27744
27807
|
var executionStage, _executionDeps;
|
|
27745
|
-
var
|
|
27808
|
+
var init_execution2 = __esm(() => {
|
|
27746
27809
|
init_agents();
|
|
27747
27810
|
init_config();
|
|
27748
27811
|
init_triggers();
|
|
@@ -29097,6 +29160,7 @@ var init_regression2 = __esm(() => {
|
|
|
29097
29160
|
storyId: ctx.story.id,
|
|
29098
29161
|
failCount: result.failCount
|
|
29099
29162
|
});
|
|
29163
|
+
logTestOutput(logger, "regression", result.rawOutput, { storyId: ctx.story.id });
|
|
29100
29164
|
pipelineEventBus.emit({
|
|
29101
29165
|
type: "regression:detected",
|
|
29102
29166
|
storyId: ctx.story.id,
|
|
@@ -29395,16 +29459,8 @@ var init_verify = __esm(() => {
|
|
|
29395
29459
|
storyId: ctx.story.id
|
|
29396
29460
|
});
|
|
29397
29461
|
}
|
|
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
|
-
}
|
|
29462
|
+
if (result.status !== "TIMEOUT") {
|
|
29463
|
+
logTestOutput(logger, "verify", result.output, { storyId: ctx.story.id });
|
|
29408
29464
|
}
|
|
29409
29465
|
return {
|
|
29410
29466
|
action: "escalate",
|
|
@@ -29449,7 +29505,7 @@ var init_stages = __esm(() => {
|
|
|
29449
29505
|
init_completion();
|
|
29450
29506
|
init_constitution2();
|
|
29451
29507
|
init_context2();
|
|
29452
|
-
|
|
29508
|
+
init_execution2();
|
|
29453
29509
|
init_optimizer2();
|
|
29454
29510
|
init_prompt();
|
|
29455
29511
|
init_queue_check();
|
|
@@ -29464,7 +29520,7 @@ var init_stages = __esm(() => {
|
|
|
29464
29520
|
init_context2();
|
|
29465
29521
|
init_prompt();
|
|
29466
29522
|
init_optimizer2();
|
|
29467
|
-
|
|
29523
|
+
init_execution2();
|
|
29468
29524
|
init_verify();
|
|
29469
29525
|
init_rectify();
|
|
29470
29526
|
init_review();
|
|
@@ -34454,7 +34510,7 @@ async function setupRun(options) {
|
|
|
34454
34510
|
} else {
|
|
34455
34511
|
logger?.warn("precheck", "Precheck validations skipped (--skip-precheck)");
|
|
34456
34512
|
}
|
|
34457
|
-
const { sweepStaleFeatureSessions: sweepStaleFeatureSessions2 } = await Promise.resolve().then(() => (
|
|
34513
|
+
const { sweepStaleFeatureSessions: sweepStaleFeatureSessions2 } = await Promise.resolve().then(() => (init_adapter2(), exports_adapter));
|
|
34458
34514
|
await sweepStaleFeatureSessions2(workdir, feature).catch(() => {});
|
|
34459
34515
|
const lockAcquired = await acquireLock(workdir);
|
|
34460
34516
|
if (!lockAcquired) {
|
|
@@ -69088,7 +69144,7 @@ async function unlockCommand(options) {
|
|
|
69088
69144
|
init_config();
|
|
69089
69145
|
|
|
69090
69146
|
// src/execution/runner.ts
|
|
69091
|
-
|
|
69147
|
+
init_adapter2();
|
|
69092
69148
|
init_registry();
|
|
69093
69149
|
init_hooks();
|
|
69094
69150
|
init_logger2();
|
|
@@ -77039,9 +77095,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
77039
77095
|
}
|
|
77040
77096
|
}
|
|
77041
77097
|
try {
|
|
77042
|
-
|
|
77098
|
+
const planLogDir = join43(featureDir, "plan");
|
|
77099
|
+
mkdirSync6(planLogDir, { recursive: true });
|
|
77043
77100
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
77044
|
-
const planLogPath = join43(
|
|
77101
|
+
const planLogPath = join43(planLogDir, `${planLogId}.jsonl`);
|
|
77045
77102
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
77046
77103
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
77047
77104
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -77286,10 +77343,10 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
77286
77343
|
process.exit(1);
|
|
77287
77344
|
}
|
|
77288
77345
|
const config2 = await loadConfig(workdir);
|
|
77289
|
-
const featureLogDir = join43(naxDir, "features", options.feature);
|
|
77346
|
+
const featureLogDir = join43(naxDir, "features", options.feature, "plan");
|
|
77290
77347
|
mkdirSync6(featureLogDir, { recursive: true });
|
|
77291
77348
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
77292
|
-
const planLogPath = join43(featureLogDir,
|
|
77349
|
+
const planLogPath = join43(featureLogDir, `${planLogId}.jsonl`);
|
|
77293
77350
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
77294
77351
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
77295
77352
|
try {
|