@nathapp/nax 0.64.0 → 0.64.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.
Files changed (2) hide show
  1. package/dist/nax.js +94 -46
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -4309,28 +4309,20 @@ class AcpAgentAdapter {
4309
4309
  if (!unwrapped) {
4310
4310
  throw new CompleteError("complete() returned empty output");
4311
4311
  }
4312
- if (response.exactCostUsd !== undefined) {
4312
+ const tokenUsage = response.cumulative_token_usage ? this._mapper.toInternal(response.cumulative_token_usage) : { inputTokens: 0, outputTokens: 0 };
4313
+ const estimatedCostUsd = tokenUsage.inputTokens > 0 ? estimateCostFromTokenUsage(tokenUsage, _options.modelDef.model) : 0;
4314
+ const exactCostUsd = response.exactCostUsd;
4315
+ if (exactCostUsd !== undefined) {
4313
4316
  getSafeLogger()?.info("acp-adapter", "complete() cost", {
4314
- costUsd: response.exactCostUsd,
4317
+ costUsd: exactCostUsd,
4315
4318
  model: _options.modelDef.model
4316
4319
  });
4317
- return {
4318
- output: unwrapped,
4319
- costUsd: response.exactCostUsd,
4320
- source: "exact"
4321
- };
4322
- }
4323
- if (response.cumulative_token_usage) {
4324
- return {
4325
- output: unwrapped,
4326
- costUsd: estimateCostFromTokenUsage(this._mapper.toInternal(response.cumulative_token_usage), _options.modelDef.model),
4327
- source: "estimated"
4328
- };
4329
4320
  }
4330
4321
  return {
4331
4322
  output: unwrapped,
4332
- costUsd: 0,
4333
- source: "fallback"
4323
+ tokenUsage,
4324
+ estimatedCostUsd,
4325
+ exactCostUsd
4334
4326
  };
4335
4327
  } finally {
4336
4328
  if (session) {
@@ -4347,8 +4339,8 @@ class AcpAgentAdapter {
4347
4339
  if (parsed.type === "auth") {
4348
4340
  return {
4349
4341
  output: error.message,
4350
- costUsd: 0,
4351
- source: "fallback",
4342
+ tokenUsage: { inputTokens: 0, outputTokens: 0 },
4343
+ estimatedCostUsd: 0,
4352
4344
  adapterFailure: {
4353
4345
  category: "availability",
4354
4346
  outcome: "fail-auth",
@@ -4360,8 +4352,8 @@ class AcpAgentAdapter {
4360
4352
  if (parsed.type === "rate-limit") {
4361
4353
  return {
4362
4354
  output: error.message,
4363
- costUsd: 0,
4364
- source: "fallback",
4355
+ tokenUsage: { inputTokens: 0, outputTokens: 0 },
4356
+ estimatedCostUsd: 0,
4365
4357
  adapterFailure: {
4366
4358
  category: "availability",
4367
4359
  outcome: "fail-rate-limit",
@@ -4374,8 +4366,8 @@ class AcpAgentAdapter {
4374
4366
  if (parsed.type === "model-not-available") {
4375
4367
  return {
4376
4368
  output: error.message,
4377
- costUsd: 0,
4378
- source: "fallback",
4369
+ tokenUsage: { inputTokens: 0, outputTokens: 0 },
4370
+ estimatedCostUsd: 0,
4379
4371
  adapterFailure: {
4380
4372
  category: "quality",
4381
4373
  outcome: "fail-adapter-error",
@@ -5189,7 +5181,7 @@ class AgentManager {
5189
5181
  if (!adapter) {
5190
5182
  _finalStatus = "error";
5191
5183
  return {
5192
- result: { output: "", costUsd: 0, source: "fallback" },
5184
+ result: { output: "", tokenUsage: { inputTokens: 0, outputTokens: 0 }, estimatedCostUsd: 0 },
5193
5185
  fallbacks
5194
5186
  };
5195
5187
  }
@@ -5204,8 +5196,8 @@ class AgentManager {
5204
5196
  } catch (err) {
5205
5197
  result = {
5206
5198
  output: "",
5207
- costUsd: 0,
5208
- source: "fallback",
5199
+ tokenUsage: { inputTokens: 0, outputTokens: 0 },
5200
+ estimatedCostUsd: 0,
5209
5201
  adapterFailure: {
5210
5202
  category: "quality",
5211
5203
  outcome: "fail-unknown",
@@ -5214,7 +5206,7 @@ class AgentManager {
5214
5206
  }
5215
5207
  };
5216
5208
  }
5217
- _totalCostUsd += result.costUsd ?? 0;
5209
+ _totalCostUsd += result.estimatedCostUsd;
5218
5210
  if (!result.adapterFailure) {
5219
5211
  _finalStatus = "ok";
5220
5212
  return { result, fallbacks };
@@ -5237,7 +5229,7 @@ class AgentManager {
5237
5229
  outcome: result.adapterFailure.outcome,
5238
5230
  category: result.adapterFailure.category,
5239
5231
  timestamp: new Date().toISOString(),
5240
- costUsd: result.costUsd ?? 0
5232
+ costUsd: result.estimatedCostUsd
5241
5233
  };
5242
5234
  fallbacks.push(hop);
5243
5235
  this._emitter.emit("onSwapAttempt", hop);
@@ -5383,7 +5375,9 @@ class AgentManager {
5383
5375
  featureName: options.featureName,
5384
5376
  workdir: options.workdir,
5385
5377
  resolvedPermissions,
5386
- exactCostUsd: outcome.result.source === "exact" ? outcome.result.costUsd : undefined,
5378
+ tokenUsage: outcome.result.tokenUsage,
5379
+ estimatedCostUsd: outcome.result.estimatedCostUsd,
5380
+ exactCostUsd: outcome.result.exactCostUsd,
5387
5381
  durationMs: Date.now() - start,
5388
5382
  timestamp: Date.now()
5389
5383
  };
@@ -28560,6 +28554,10 @@ function buildDebateDiffSection(ctx) {
28560
28554
  if (ctx.mode === "ref") {
28561
28555
  const stat = ctx.stat ?? "(no stat available)";
28562
28556
  const ref = ctx.storyGitRef;
28557
+ const excludes = [
28558
+ ...new Set([...ctx.productionExcludePatterns ?? [], ":!.nax/", ":!**/.nax/", ":!.nax-pids", ":!**/.nax-pids"])
28559
+ ];
28560
+ const excludeArgs = excludes.map((p) => `'${p}'`).join(" ");
28563
28561
  return [
28564
28562
  "## Changed Files",
28565
28563
  "```",
@@ -28570,7 +28568,7 @@ function buildDebateDiffSection(ctx) {
28570
28568
  "",
28571
28569
  "To inspect the implementation:",
28572
28570
  `- Full diff: \`git diff --unified=3 ${ref}..HEAD\``,
28573
- `- Production diff: \`git diff --unified=3 ${ref}..HEAD -- . ':!test/' ':!tests/' ':!*.test.ts' ':!*.spec.ts' ':!.nax/' ':!.nax-pids'\``,
28571
+ `- Production diff: \`git diff --unified=3 ${ref}..HEAD -- . ${excludeArgs}\``,
28574
28572
  `- Commit history: \`git log --oneline ${ref}..HEAD\``,
28575
28573
  "",
28576
28574
  "Use these commands to inspect the code. Do NOT rely solely on the file list above."
@@ -28790,9 +28788,13 @@ If all ACs are correctly implemented, respond with { "passed": true, "findings":
28790
28788
  var init_review_builder = () => {};
28791
28789
 
28792
28790
  // src/prompts/builders/adversarial-review-builder.ts
28793
- function buildAdversarialRefDiffSection(storyGitRef, stat, excludePatterns = []) {
28794
- const merged = [...new Set([...excludePatterns, ":!.nax/", ":!.nax-pids"])];
28791
+ function buildAdversarialRefDiffSection(storyGitRef, stat, excludePatterns = [], testGlobs = [], refExcludePatterns = []) {
28792
+ const merged = [...new Set([...excludePatterns, ":!.nax/", ":!**/.nax/", ":!.nax-pids", ":!**/.nax-pids"])];
28795
28793
  const excludeArgs = merged.map((p) => `'${p}'`).join(" ");
28794
+ const productionExcludes = [
28795
+ ...new Set([...refExcludePatterns, ":!.nax/", ":!**/.nax/", ":!.nax-pids", ":!**/.nax-pids"])
28796
+ ];
28797
+ const productionExcludeArgs = productionExcludes.map((p) => `'${p}'`).join(" ");
28796
28798
  const statBlock = stat ? `## Changed Files Summary
28797
28799
 
28798
28800
  \`\`\`
@@ -28800,6 +28802,7 @@ ${stat}
28800
28802
  \`\`\`
28801
28803
 
28802
28804
  ` : "";
28805
+ const testPatternGuide = testGlobs.length > 0 ? testGlobs.map((glob) => `\`${glob}\``).join(", ") : "the resolved project test-file patterns";
28803
28806
  return `${statBlock}## Diff Access
28804
28807
 
28805
28808
  You have access to git commands. Fetch the diff yourself \u2014 do NOT ask for it to be provided.
@@ -28824,8 +28827,10 @@ cat path/to/file.ts
28824
28827
 
28825
28828
  **Test audit workflow:**
28826
28829
  1. Run: \`git diff --name-only --diff-filter=A ${storyGitRef}..HEAD -- . ${excludeArgs}\`
28827
- 2. For each new \`src/**.ts\` file, check whether a matching \`test/**/**.test.ts\` was also added.
28830
+ 2. For each new source file, check whether a matching test file was added (patterns: ${testPatternGuide}).
28828
28831
  3. If a new exported module has no test file, flag it as \`"test-gap"\`.
28832
+ 4. To focus only on production deltas while auditing test coverage, run:
28833
+ \`git diff --unified=3 ${storyGitRef}..HEAD -- . ${productionExcludeArgs}\`
28829
28834
 
28830
28835
  `;
28831
28836
  }
@@ -28872,7 +28877,18 @@ ${rows}
28872
28877
 
28873
28878
  class AdversarialReviewPromptBuilder {
28874
28879
  buildAdversarialReviewPrompt(story, config2, options) {
28875
- const { mode, diff, storyGitRef, stat, priorFailures, testInventory, excludePatterns, priorAdversarialFindings } = options;
28880
+ const {
28881
+ mode,
28882
+ diff,
28883
+ storyGitRef,
28884
+ stat,
28885
+ priorFailures,
28886
+ testInventory,
28887
+ excludePatterns,
28888
+ testGlobs,
28889
+ refExcludePatterns,
28890
+ priorAdversarialFindings
28891
+ } = options;
28876
28892
  const priorFindingsBlock = priorAdversarialFindings && priorAdversarialFindings.findings.length > 0 ? buildPriorFindingsBlock(priorAdversarialFindings.round, priorAdversarialFindings.findings) : "";
28877
28893
  const storyBlock = `## Story Under Review
28878
28894
 
@@ -28894,7 +28910,7 @@ ${config2.rules.map((r) => `- ${r}`).join(`
28894
28910
  const attemptBlock = buildAttemptContextBlock(priorFailures);
28895
28911
  let diffBlock;
28896
28912
  if (mode === "ref" && storyGitRef) {
28897
- diffBlock = buildAdversarialRefDiffSection(storyGitRef, stat, excludePatterns ?? []);
28913
+ diffBlock = buildAdversarialRefDiffSection(storyGitRef, stat, excludePatterns ?? [], testGlobs ?? [], refExcludePatterns ?? []);
28898
28914
  } else if (mode === "embedded" && diff) {
28899
28915
  diffBlock = buildAdversarialEmbeddedDiffSection(diff, testInventory);
28900
28916
  } else {
@@ -28957,7 +28973,7 @@ What did the implementer accept but not actually use?
28957
28973
 
28958
28974
  ### 4. Test Audit Gap
28959
28975
  What new exported units lack corresponding test files?
28960
- - New \`src/foo/bar.ts\` with exports but no \`test/**/bar.test.ts\`
28976
+ - New source modules with exports but no matching test file
28961
28977
  - New public functions that only appear in implementation, not in tests
28962
28978
  - Acceptance criteria that touch a code path with no test coverage
28963
28979
 
@@ -32181,6 +32197,8 @@ var init_adversarial_review = __esm(() => {
32181
32197
  priorFailures: input.priorFailures,
32182
32198
  testInventory: input.testInventory,
32183
32199
  excludePatterns: input.excludePatterns,
32200
+ testGlobs: input.testGlobs,
32201
+ refExcludePatterns: input.refExcludePatterns,
32184
32202
  priorAdversarialFindings: input.priorAdversarialFindings
32185
32203
  });
32186
32204
  const content = input.featureCtxBlock ? `${input.featureCtxBlock}${base}` : base;
@@ -33656,7 +33674,12 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
33656
33674
  description: "",
33657
33675
  acceptanceCriteria: resolverContext.story.acceptanceCriteria
33658
33676
  };
33659
- const diffContext = resolverContext.diffMode === "ref" ? { mode: "ref", storyGitRef: resolverContext.storyGitRef ?? "", stat: resolverContext.stat } : { mode: "embedded", diff: resolverContext.diff ?? "" };
33677
+ const diffContext = resolverContext.diffMode === "ref" ? {
33678
+ mode: "ref",
33679
+ storyGitRef: resolverContext.storyGitRef ?? "",
33680
+ stat: resolverContext.stat,
33681
+ productionExcludePatterns: resolverContext.productionExcludePatterns
33682
+ } : { mode: "embedded", diff: resolverContext.diff ?? "" };
33660
33683
  let dialogueResult;
33661
33684
  if (resolverContext.isReReview) {
33662
33685
  dialogueResult = await reviewerSession.reReviewDebate(resolverContext.labeledProposals, critiqueOutputs, diffContext, debateCtx);
@@ -33724,7 +33747,7 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
33724
33747
  });
33725
33748
  return {
33726
33749
  outcome: "passed",
33727
- resolverCostUsd: resolverResult.costUsd,
33750
+ resolverCostUsd: resolverResult.exactCostUsd ?? resolverResult.estimatedCostUsd,
33728
33751
  output: resolverResult.output
33729
33752
  };
33730
33753
  }
@@ -33767,7 +33790,7 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
33767
33790
  });
33768
33791
  return {
33769
33792
  outcome: "passed",
33770
- resolverCostUsd: resolverResult.costUsd,
33793
+ resolverCostUsd: resolverResult.exactCostUsd ?? resolverResult.estimatedCostUsd,
33771
33794
  output: resolverResult.output
33772
33795
  };
33773
33796
  }
@@ -37210,9 +37233,9 @@ function attachCostSubscriber(bus, aggregator, runId) {
37210
37233
  const offDispatch = bus.onDispatch((event) => {
37211
37234
  const tu = event.tokenUsage;
37212
37235
  const exactCostUsd = event.exactCostUsd;
37213
- if (!tu && exactCostUsd == null)
37236
+ const estimatedCostUsd = event.estimatedCostUsd ?? exactCostUsd ?? 0;
37237
+ if (!tu && exactCostUsd == null && estimatedCostUsd === 0)
37214
37238
  return;
37215
- const estimatedCostUsd = exactCostUsd ?? 0;
37216
37239
  const costUsd = exactCostUsd ?? estimatedCostUsd;
37217
37240
  const confidence = exactCostUsd != null ? "exact" : "estimated";
37218
37241
  const costEvent = {
@@ -42067,6 +42090,7 @@ var init_diff_utils = __esm(() => {
42067
42090
  });
42068
42091
 
42069
42092
  // src/review/adversarial.ts
42093
+ import { relative as relative10, sep as sep3 } from "path";
42070
42094
  function recordAdversarialAudit(opts) {
42071
42095
  opts.runtime?.reviewAuditor.recordDecision({
42072
42096
  reviewer: "adversarial",
@@ -42135,6 +42159,17 @@ async function runAdversarialReview(opts) {
42135
42159
  }
42136
42160
  let diff;
42137
42161
  let testInventory;
42162
+ const effectiveConfig = naxConfig ?? reviewConfigSelector.select(DEFAULT_CONFIG);
42163
+ const packageDirRelative = projectDir && workdir !== projectDir ? (() => {
42164
+ const rel = relative10(projectDir, workdir);
42165
+ if (rel === ".." || rel.startsWith(`..${sep3}`))
42166
+ return;
42167
+ return rel && rel !== "." ? rel : undefined;
42168
+ })() : undefined;
42169
+ const resolvedTestPatterns = await resolveTestFilePatterns(effectiveConfig, projectDir ?? workdir, packageDirRelative);
42170
+ const effectiveRefExcludePatterns = [
42171
+ ...resolveReviewExcludePatterns(adversarialConfig.excludePatterns, resolvedTestPatterns)
42172
+ ];
42138
42173
  if (diffMode === "embedded") {
42139
42174
  diff = await collectDiff(workdir, effectiveRef, adversarialConfig.excludePatterns ?? [], {
42140
42175
  naxIgnoreIndex,
@@ -42211,9 +42246,11 @@ async function runAdversarialReview(opts) {
42211
42246
  priorFailures,
42212
42247
  testInventory,
42213
42248
  excludePatterns: adversarialConfig.excludePatterns,
42249
+ testGlobs: resolvedTestPatterns.globs,
42214
42250
  featureCtxBlock,
42215
42251
  priorAdversarialFindings,
42216
- blockingThreshold
42252
+ blockingThreshold,
42253
+ refExcludePatterns: effectiveRefExcludePatterns
42217
42254
  });
42218
42255
  } catch (err) {
42219
42256
  logger?.warn("adversarial", "LLM call failed \u2014 fail-open", { storyId: story.id, cause: String(err) });
@@ -42412,11 +42449,13 @@ ${formatFindings(blockingFindings)}`,
42412
42449
  }
42413
42450
  var _adversarialDeps;
42414
42451
  var init_adversarial = __esm(() => {
42452
+ init_config();
42415
42453
  init_context();
42416
42454
  init_errors();
42417
42455
  init_logger2();
42418
42456
  init_adversarial_review();
42419
42457
  init_call();
42458
+ init_test_runners();
42420
42459
  init_adversarial_helpers();
42421
42460
  init_diff_utils();
42422
42461
  init_review_audit();
@@ -42582,6 +42621,7 @@ async function runSemanticDebate(opts) {
42582
42621
  effectiveRef,
42583
42622
  startTime,
42584
42623
  prompt,
42624
+ productionExcludePatterns,
42585
42625
  blockingThreshold,
42586
42626
  createDebateRunner
42587
42627
  } = opts;
@@ -42620,7 +42660,7 @@ async function runSemanticDebate(opts) {
42620
42660
  reviewerSession: resolverSession,
42621
42661
  resolverContextInput: resolverSession ? {
42622
42662
  diffMode,
42623
- ...diffMode === "ref" ? { storyGitRef: effectiveRef, stat } : { diff },
42663
+ ...diffMode === "ref" ? { storyGitRef: effectiveRef, stat, productionExcludePatterns } : { diff },
42624
42664
  story: { id: story.id, title: story.title, acceptanceCriteria: story.acceptanceCriteria },
42625
42665
  semanticConfig,
42626
42666
  resolverType: reviewStageConfig.resolver.type,
@@ -42862,6 +42902,7 @@ var init_semantic_evidence = __esm(() => {
42862
42902
  });
42863
42903
 
42864
42904
  // src/review/semantic.ts
42905
+ import { relative as relative11, sep as sep4 } from "path";
42865
42906
  function recordSemanticAudit(opts) {
42866
42907
  opts.runtime?.reviewAuditor.recordDecision({
42867
42908
  reviewer: "semantic",
@@ -42924,7 +42965,13 @@ async function runSemanticReview(opts) {
42924
42965
  const repoRoot = projectDir ?? workdir;
42925
42966
  const packageDir = workdir !== repoRoot ? workdir : undefined;
42926
42967
  const stat = await collectDiffStat(workdir, effectiveRef, { naxIgnoreIndex, packageDir });
42927
- const resolved = await resolveTestFilePatterns(naxConfig ?? reviewConfigSelector.select(DEFAULT_CONFIG), workdir);
42968
+ const packageDirRelative = projectDir && workdir !== projectDir ? (() => {
42969
+ const rel = relative11(projectDir, workdir);
42970
+ if (rel === ".." || rel.startsWith(`..${sep4}`))
42971
+ return;
42972
+ return rel && rel !== "." ? rel : undefined;
42973
+ })() : undefined;
42974
+ const resolved = await resolveTestFilePatterns(naxConfig ?? reviewConfigSelector.select(DEFAULT_CONFIG), projectDir ?? workdir, packageDirRelative);
42928
42975
  const excludePatterns = [...resolveReviewExcludePatterns(semanticConfig.excludePatterns, resolved)];
42929
42976
  let diff;
42930
42977
  if (diffMode === "embedded") {
@@ -43020,6 +43067,7 @@ async function runSemanticReview(opts) {
43020
43067
  effectiveRef,
43021
43068
  startTime,
43022
43069
  prompt,
43070
+ productionExcludePatterns: excludePatterns,
43023
43071
  blockingThreshold,
43024
43072
  createDebateRunner: _semanticDeps.createDebateRunner
43025
43073
  });
@@ -49813,7 +49861,7 @@ var package_default;
49813
49861
  var init_package = __esm(() => {
49814
49862
  package_default = {
49815
49863
  name: "@nathapp/nax",
49816
- version: "0.64.0",
49864
+ version: "0.64.1",
49817
49865
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
49818
49866
  type: "module",
49819
49867
  bin: {
@@ -49897,8 +49945,8 @@ var init_version = __esm(() => {
49897
49945
  NAX_VERSION = package_default.version;
49898
49946
  NAX_COMMIT = (() => {
49899
49947
  try {
49900
- if (/^[0-9a-f]{6,10}$/.test("a0e4443d"))
49901
- return "a0e4443d";
49948
+ if (/^[0-9a-f]{6,10}$/.test("8afbec51"))
49949
+ return "8afbec51";
49902
49950
  } catch {}
49903
49951
  try {
49904
49952
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.64.0",
3
+ "version": "0.64.1",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {