@nathapp/nax 0.80.0-canary.1 → 0.80.0-canary.2

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 +179 -89
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -17220,7 +17220,9 @@ var init_schemas_infra = __esm(() => {
17220
17220
  message: "idleTimeoutSeconds must be > 0 when mode is not 'off'"
17221
17221
  });
17222
17222
  AgentAcpConfigSchema = exports_external.object({
17223
- promptRetries: exports_external.number().int().min(0).max(5).default(0)
17223
+ promptRetries: exports_external.number().int().min(0).max(5).default(0),
17224
+ trackedSpawnDeadlineMs: exports_external.number().int().positive().default(1e4),
17225
+ trackedSpawnStartupDeadlineMs: exports_external.number().int().positive().default(30000)
17224
17226
  });
17225
17227
  AgentTimeoutRetryConfigSchema = exports_external.object({
17226
17228
  maxAttempts: exports_external.number().int().nonnegative().default(1),
@@ -17242,7 +17244,11 @@ var init_schemas_infra = __esm(() => {
17242
17244
  onQualityFailure: false,
17243
17245
  rebuildContext: true
17244
17246
  }),
17245
- acp: AgentAcpConfigSchema.default({ promptRetries: 0 }),
17247
+ acp: AgentAcpConfigSchema.default({
17248
+ promptRetries: 0,
17249
+ trackedSpawnDeadlineMs: 1e4,
17250
+ trackedSpawnStartupDeadlineMs: 30000
17251
+ }),
17246
17252
  idleWatchdog: AgentIdleWatchdogConfigSchema.default(DEFAULT_AGENT_IDLE_WATCHDOG_CONFIG),
17247
17253
  timeoutRetry: AgentTimeoutRetryConfigSchema.default(DEFAULT_AGENT_TIMEOUT_RETRY_CONFIG)
17248
17254
  });
@@ -17719,7 +17725,7 @@ var init_schemas3 = __esm(() => {
17719
17725
  maxInteractionTurns: 20,
17720
17726
  promptAudit: { enabled: false },
17721
17727
  fallback: { enabled: false, map: {}, maxHopsPerStory: 2, onQualityFailure: false, rebuildContext: true },
17722
- acp: { promptRetries: 0 },
17728
+ acp: { promptRetries: 0, trackedSpawnDeadlineMs: 1e4, trackedSpawnStartupDeadlineMs: 30000 },
17723
17729
  idleWatchdog: DEFAULT_AGENT_IDLE_WATCHDOG_CONFIG,
17724
17730
  timeoutRetry: DEFAULT_AGENT_TIMEOUT_RETRY_CONFIG
17725
17731
  }),
@@ -19875,6 +19881,15 @@ var init_selectors = __esm(() => {
19875
19881
  finishConfigSelector = pickSelector("finish", "finish", "interaction", "quality", "agent");
19876
19882
  });
19877
19883
 
19884
+ // src/config/tracked-spawn-deadlines.ts
19885
+ function trackedSpawnDeadlines(config2) {
19886
+ const acp = config2?.agent?.acp;
19887
+ return {
19888
+ trackedSpawnDeadlineMs: acp?.trackedSpawnDeadlineMs,
19889
+ trackedSpawnStartupDeadlineMs: acp?.trackedSpawnStartupDeadlineMs
19890
+ };
19891
+ }
19892
+
19878
19893
  // src/config/loader-runtime.ts
19879
19894
  function createConfigLoader(config2) {
19880
19895
  const memo = new Map;
@@ -20116,6 +20131,7 @@ __export(exports_config, {
20116
20131
  validateFilePath: () => validateFilePath,
20117
20132
  validateDirectory: () => validateDirectory,
20118
20133
  validateConfig: () => validateConfig,
20134
+ trackedSpawnDeadlines: () => trackedSpawnDeadlines,
20119
20135
  testPatternConfigSelector: () => testPatternConfigSelector,
20120
20136
  tddConfigSelector: () => tddConfigSelector,
20121
20137
  routingConfigSelector: () => routingConfigSelector,
@@ -20361,31 +20377,6 @@ var init_cost = __esm(() => {
20361
20377
  init_calculate();
20362
20378
  });
20363
20379
 
20364
- // src/agents/acp/token-mapper.ts
20365
- function toFiniteTokenCount2(value) {
20366
- return typeof value === "number" && Number.isFinite(value) ? value : 0;
20367
- }
20368
- function toFiniteOrUndefined(value) {
20369
- if (value === undefined)
20370
- return;
20371
- return typeof value === "number" && Number.isFinite(value) ? value : 0;
20372
- }
20373
-
20374
- class AcpTokenUsageMapper {
20375
- toInternal(wire) {
20376
- return {
20377
- inputTokens: toFiniteTokenCount2(wire.input_tokens),
20378
- outputTokens: toFiniteTokenCount2(wire.output_tokens),
20379
- cacheReadInputTokens: toFiniteOrUndefined(wire.cache_read_input_tokens),
20380
- cacheCreationInputTokens: toFiniteOrUndefined(wire.cache_creation_input_tokens)
20381
- };
20382
- }
20383
- }
20384
- var defaultAcpTokenUsageMapper;
20385
- var init_token_mapper = __esm(() => {
20386
- defaultAcpTokenUsageMapper = new AcpTokenUsageMapper;
20387
- });
20388
-
20389
20380
  // src/utils/bun-deps.ts
20390
20381
  var exports_bun_deps = {};
20391
20382
  __export(exports_bun_deps, {
@@ -20847,6 +20838,19 @@ function parseSessionIds(stdout) {
20847
20838
  return { sessionId: undefined, recordId: undefined };
20848
20839
  }
20849
20840
 
20841
+ // src/agents/acp/spawn-client-deps.ts
20842
+ var ACPX_STREAM_DRAIN_TIMEOUT_MS = 5000, KILL_TREE_GRACE_MS = 250, TRACKED_SPAWN_DEADLINE_MS = 1e4, TRACKED_SPAWN_STARTUP_DEADLINE_MS = 30000, _spawnClientDeps;
20843
+ var init_spawn_client_deps = __esm(() => {
20844
+ init_bun_deps();
20845
+ _spawnClientDeps = {
20846
+ spawn: typedSpawn,
20847
+ streamDrainTimeoutMs: ACPX_STREAM_DRAIN_TIMEOUT_MS,
20848
+ killTreeGraceMs: KILL_TREE_GRACE_MS,
20849
+ trackedSpawnDeadlineMs: TRACKED_SPAWN_DEADLINE_MS,
20850
+ trackedSpawnStartupDeadlineMs: TRACKED_SPAWN_STARTUP_DEADLINE_MS
20851
+ };
20852
+ });
20853
+
20850
20854
  // src/utils/process-kill.ts
20851
20855
  function killProcessGroup(pid, signal) {
20852
20856
  try {
@@ -21062,7 +21066,7 @@ var init_stdout_line_reader = __esm(() => {
21062
21066
  MAX_BUFFERED_STDERR_BYTES = 64 * 1024;
21063
21067
  });
21064
21068
 
21065
- // src/agents/acp/spawn-client.ts
21069
+ // src/agents/acp/spawn-client-session.ts
21066
21070
  import { randomUUID } from "crypto";
21067
21071
 
21068
21072
  class SpawnAcpSession {
@@ -21082,6 +21086,7 @@ class SpawnAcpSession {
21082
21086
  runId;
21083
21087
  storyId;
21084
21088
  stage;
21089
+ trackedSpawnDeadlineMs;
21085
21090
  activeProc = null;
21086
21091
  _externallyCancelled = false;
21087
21092
  id;
@@ -21105,6 +21110,7 @@ class SpawnAcpSession {
21105
21110
  this.stage = opts.stage;
21106
21111
  this.id = opts.id;
21107
21112
  this.recordId = opts.recordId;
21113
+ this.trackedSpawnDeadlineMs = opts.trackedSpawnDeadlineMs ?? _spawnClientDeps.trackedSpawnDeadlineMs;
21108
21114
  }
21109
21115
  async prompt(text) {
21110
21116
  const callId = randomUUID();
@@ -21297,7 +21303,7 @@ class SpawnAcpSession {
21297
21303
  }
21298
21304
  }
21299
21305
  async trackedSpawn(cmd, opts, signal) {
21300
- return runTrackedSpawn(_spawnClientDeps, cmd, opts, this.onPidSpawned, this.onPidExited, signal);
21306
+ return runTrackedSpawn({ ..._spawnClientDeps, trackedSpawnDeadlineMs: this.trackedSpawnDeadlineMs }, cmd, opts, this.onPidSpawned, this.onPidExited, signal);
21301
21307
  }
21302
21308
  killActiveProcTree() {
21303
21309
  if (!this.activeProc)
@@ -21337,7 +21343,15 @@ class SpawnAcpSession {
21337
21343
  await this.trackedSpawn(cmd);
21338
21344
  }
21339
21345
  }
21346
+ var init_spawn_client_session = __esm(() => {
21347
+ init_agents();
21348
+ init_logger2();
21349
+ init_spawn_client_deps();
21350
+ init_spawn_client_process();
21351
+ init_stdout_line_reader();
21352
+ });
21340
21353
 
21354
+ // src/agents/acp/spawn-client.ts
21341
21355
  class SpawnAcpClient {
21342
21356
  model;
21343
21357
  rawModel;
@@ -21353,6 +21367,8 @@ class SpawnAcpClient {
21353
21367
  runId;
21354
21368
  storyId;
21355
21369
  stage;
21370
+ trackedSpawnDeadlineMs;
21371
+ trackedSpawnStartupDeadlineMs;
21356
21372
  constructor(cmdStr, cwd, timeoutSeconds, onPidSpawned, promptRetries, onPidExited, opts) {
21357
21373
  const parts = cmdStr.split(/\s+/);
21358
21374
  const modelIdx = parts.indexOf("--model");
@@ -21379,10 +21395,12 @@ class SpawnAcpClient {
21379
21395
  this.runId = opts?.runId;
21380
21396
  this.storyId = opts?.storyId;
21381
21397
  this.stage = opts?.stage;
21398
+ this.trackedSpawnDeadlineMs = opts?.trackedSpawnDeadlineMs ?? _spawnClientDeps.trackedSpawnDeadlineMs;
21399
+ this.trackedSpawnStartupDeadlineMs = opts?.trackedSpawnStartupDeadlineMs ?? _spawnClientDeps.trackedSpawnStartupDeadlineMs;
21382
21400
  }
21383
21401
  async start() {}
21384
- async trackedSpawn(cmd, signal) {
21385
- return runTrackedSpawn(_spawnClientDeps, cmd, undefined, this.onPidSpawned, this.onPidExited, signal);
21402
+ async trackedSpawn(cmd, signal, deadlineMsOverride) {
21403
+ return runTrackedSpawn({ ..._spawnClientDeps, trackedSpawnDeadlineMs: deadlineMsOverride ?? this.trackedSpawnStartupDeadlineMs }, cmd, undefined, this.onPidSpawned, this.onPidExited, signal);
21386
21404
  }
21387
21405
  async createSession(opts) {
21388
21406
  const sessionName = opts.sessionName || `nax-${Date.now()}`;
@@ -21430,13 +21448,17 @@ class SpawnAcpClient {
21430
21448
  storyId: this.storyId,
21431
21449
  stage: this.stage,
21432
21450
  id: sessionId,
21433
- recordId
21451
+ recordId,
21452
+ trackedSpawnDeadlineMs: this.trackedSpawnDeadlineMs
21434
21453
  });
21435
21454
  }
21436
21455
  async loadSession(sessionName, agentName, permissionMode) {
21437
21456
  const cmd = ["acpx", "--cwd", this.cwd, "--format", "json", agentName, "sessions", "ensure", "--name", sessionName];
21438
21457
  const { exitCode, stdout } = await this.trackedSpawn(cmd);
21439
21458
  if (exitCode !== 0) {
21459
+ if (exitCode === -1) {
21460
+ getSafeLogger()?.warn("acp-adapter", "sessions ensure hit its trackedSpawn deadline \u2014 falling back to a context-less session", { sessionName, agentName });
21461
+ }
21440
21462
  return null;
21441
21463
  }
21442
21464
  const { sessionId, recordId } = parseSessionIds(stdout);
@@ -21466,12 +21488,13 @@ class SpawnAcpClient {
21466
21488
  storyId: this.storyId,
21467
21489
  stage: this.stage,
21468
21490
  id: sessionId,
21469
- recordId
21491
+ recordId,
21492
+ trackedSpawnDeadlineMs: this.trackedSpawnDeadlineMs
21470
21493
  });
21471
21494
  }
21472
21495
  async closeSession(sessionName, agentName, signal) {
21473
21496
  const cmd = ["acpx", "--cwd", this.cwd, agentName, "sessions", "close", sessionName];
21474
- const { exitCode, stderr } = await this.trackedSpawn(cmd, signal);
21497
+ const { exitCode, stderr } = await this.trackedSpawn(cmd, signal, this.trackedSpawnDeadlineMs);
21475
21498
  if (exitCode !== 0) {
21476
21499
  getSafeLogger()?.debug("acp-adapter", "Session close failed (ignored)", {
21477
21500
  sessionName,
@@ -21486,22 +21509,16 @@ class SpawnAcpClient {
21486
21509
  function createSpawnAcpClient(cmdStr, cwd, timeoutSeconds, onPidSpawned, promptRetries, onPidExited, opts) {
21487
21510
  return new SpawnAcpClient(cmdStr, cwd, timeoutSeconds, onPidSpawned, promptRetries, onPidExited, opts);
21488
21511
  }
21489
- var ACPX_STREAM_DRAIN_TIMEOUT_MS = 5000, KILL_TREE_GRACE_MS = 250, TRACKED_SPAWN_DEADLINE_MS = 1e4, _spawnClientDeps;
21490
21512
  var init_spawn_client = __esm(() => {
21491
- init_agents();
21492
21513
  init_logger2();
21493
- init_bun_deps();
21494
21514
  init_env();
21495
21515
  init_model_spec();
21496
21516
  init_reasoning_effort();
21517
+ init_spawn_client_deps();
21497
21518
  init_spawn_client_process();
21498
- init_stdout_line_reader();
21499
- _spawnClientDeps = {
21500
- spawn: typedSpawn,
21501
- streamDrainTimeoutMs: ACPX_STREAM_DRAIN_TIMEOUT_MS,
21502
- killTreeGraceMs: KILL_TREE_GRACE_MS,
21503
- trackedSpawnDeadlineMs: TRACKED_SPAWN_DEADLINE_MS
21504
- };
21519
+ init_spawn_client_session();
21520
+ init_spawn_client_deps();
21521
+ init_spawn_client_session();
21505
21522
  });
21506
21523
 
21507
21524
  // src/agents/acp/adapter-lifecycle.ts
@@ -21666,6 +21683,60 @@ var init_adapter_lifecycle = __esm(() => {
21666
21683
  };
21667
21684
  });
21668
21685
 
21686
+ // src/agents/acp/adapter-close-physical.ts
21687
+ async function closePhysicalSession(agentName, handle, workdir, options) {
21688
+ const cmdStr = `acpx ${agentName}`;
21689
+ const client = _acpAdapterDeps.createClient(cmdStr, workdir, undefined, undefined);
21690
+ try {
21691
+ await client.start();
21692
+ try {
21693
+ if (client.closeSession) {
21694
+ await client.closeSession(handle, agentName, options?.signal);
21695
+ if (options?.force) {
21696
+ await client.forceStop?.(agentName).catch(() => {});
21697
+ }
21698
+ } else if (client.loadSession) {
21699
+ const session = await client.loadSession(handle, agentName, "approve-reads");
21700
+ if (session)
21701
+ await session.close({ forceTerminate: options?.force, signal: options?.signal }).catch(() => {});
21702
+ }
21703
+ } catch (err) {
21704
+ getSafeLogger()?.warn("acp-adapter", `[close] Failed to close session ${handle}`, { error: String(err) });
21705
+ }
21706
+ } finally {
21707
+ await client.close().catch(() => {});
21708
+ }
21709
+ }
21710
+ var init_adapter_close_physical = __esm(() => {
21711
+ init_logger2();
21712
+ init_adapter_lifecycle();
21713
+ });
21714
+
21715
+ // src/agents/acp/token-mapper.ts
21716
+ function toFiniteTokenCount2(value) {
21717
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
21718
+ }
21719
+ function toFiniteOrUndefined(value) {
21720
+ if (value === undefined)
21721
+ return;
21722
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
21723
+ }
21724
+
21725
+ class AcpTokenUsageMapper {
21726
+ toInternal(wire) {
21727
+ return {
21728
+ inputTokens: toFiniteTokenCount2(wire.input_tokens),
21729
+ outputTokens: toFiniteTokenCount2(wire.output_tokens),
21730
+ cacheReadInputTokens: toFiniteOrUndefined(wire.cache_read_input_tokens),
21731
+ cacheCreationInputTokens: toFiniteOrUndefined(wire.cache_creation_input_tokens)
21732
+ };
21733
+ }
21734
+ }
21735
+ var defaultAcpTokenUsageMapper;
21736
+ var init_token_mapper = __esm(() => {
21737
+ defaultAcpTokenUsageMapper = new AcpTokenUsageMapper;
21738
+ });
21739
+
21669
21740
  // src/agents/acp/adapter-output.ts
21670
21741
  function extractOutput(response) {
21671
21742
  if (!response)
@@ -21881,7 +21952,9 @@ class AcpAgentAdapter {
21881
21952
  const timeoutSeconds = Math.ceil(timeoutMs / 1000);
21882
21953
  const client = _acpAdapterDeps.createClient(cmdStr, workdir, timeoutSeconds, _options.onPidSpawned, _options.promptRetries, _options.onPidExited, {
21883
21954
  onStreamActivity: _options.onStreamActivity,
21884
- onActiveCall: _options.onActiveCall
21955
+ onActiveCall: _options.onActiveCall,
21956
+ trackedSpawnDeadlineMs: _options.trackedSpawnDeadlineMs,
21957
+ trackedSpawnStartupDeadlineMs: _options.trackedSpawnStartupDeadlineMs
21885
21958
  });
21886
21959
  await client.start();
21887
21960
  let session = null;
@@ -21996,27 +22069,7 @@ class AcpAgentAdapter {
21996
22069
  }
21997
22070
  }
21998
22071
  async closePhysicalSession(handle, workdir, options) {
21999
- const cmdStr = `acpx ${this.name}`;
22000
- const client = _acpAdapterDeps.createClient(cmdStr, workdir, undefined, undefined);
22001
- try {
22002
- await client.start();
22003
- try {
22004
- if (client.closeSession) {
22005
- await client.closeSession(handle, this.name, options?.signal);
22006
- if (options?.force) {
22007
- await client.forceStop?.(this.name).catch(() => {});
22008
- }
22009
- } else if (client.loadSession) {
22010
- const session = await client.loadSession(handle, this.name, "approve-reads");
22011
- if (session)
22012
- await session.close({ forceTerminate: options?.force, signal: options?.signal }).catch(() => {});
22013
- }
22014
- } catch (err) {
22015
- getSafeLogger()?.warn("acp-adapter", `[close] Failed to close session ${handle}`, { error: String(err) });
22016
- }
22017
- } finally {
22018
- await client.close().catch(() => {});
22019
- }
22072
+ return closePhysicalSession(this.name, handle, workdir, options);
22020
22073
  }
22021
22074
  async openSession(name, opts) {
22022
22075
  const {
@@ -22035,7 +22088,9 @@ class AcpAgentAdapter {
22035
22088
  const cmdStr = `acpx --model ${modelDef.model} ${agentName}`;
22036
22089
  const client = _acpAdapterDeps.createClient(cmdStr, workdir, timeoutSeconds, onPidSpawned, promptRetries, onPidExited, {
22037
22090
  onStreamActivity: opts.onStreamActivity,
22038
- onActiveCall: opts.onActiveCall
22091
+ onActiveCall: opts.onActiveCall,
22092
+ trackedSpawnDeadlineMs: opts.trackedSpawnDeadlineMs,
22093
+ trackedSpawnStartupDeadlineMs: opts.trackedSpawnStartupDeadlineMs
22039
22094
  });
22040
22095
  let session;
22041
22096
  try {
@@ -22224,6 +22279,7 @@ var init_adapter = __esm(() => {
22224
22279
  init_logger2();
22225
22280
  init_cost();
22226
22281
  init_types3();
22282
+ init_adapter_close_physical();
22227
22283
  init_token_mapper();
22228
22284
  init_adapter_lifecycle();
22229
22285
  init_adapter_output();
@@ -23483,8 +23539,12 @@ class AgentManager {
23483
23539
  async completeAs(agentName, prompt, options) {
23484
23540
  const stage = options.pipelineStage ?? "complete";
23485
23541
  const resolvedPermissions = resolvePermissions(this._config, stage);
23486
- const promptRetries = this._config.agent?.acp?.promptRetries;
23487
- const augmented = { ...options, resolvedPermissions, promptRetries };
23542
+ const augmented = {
23543
+ ...options,
23544
+ resolvedPermissions,
23545
+ promptRetries: this._config.agent?.acp?.promptRetries,
23546
+ ...trackedSpawnDeadlines(this._config)
23547
+ };
23488
23548
  const sessionName = options.sessionName ?? formatSessionName({
23489
23549
  workdir: options.workdir ?? "",
23490
23550
  featureName: options.featureName,
@@ -23540,6 +23600,7 @@ class AgentManager {
23540
23600
  }
23541
23601
  var MAX_EMITTER_LISTENERS = 100, _agentManagerDeps;
23542
23602
  var init_manager = __esm(() => {
23603
+ init_config();
23543
23604
  init_errors();
23544
23605
  init_logger2();
23545
23606
  init_dispatch_events();
@@ -30226,11 +30287,17 @@ function setStoryPriority(prd, storyId, priority) {
30226
30287
  story.priority = priority;
30227
30288
  }
30228
30289
  }
30229
- function markStoryPaused(prd, storyId) {
30290
+ function markStoryPaused(prd, storyId, reason) {
30230
30291
  const story = prd.userStories.find((s) => s.id === storyId);
30231
30292
  if (story) {
30232
30293
  story.status = "paused";
30233
30294
  story.attempts = (story.attempts ?? 0) + 1;
30295
+ if (reason) {
30296
+ const entry = `PAUSED: ${reason}`;
30297
+ const prior = story.priorErrors ?? [];
30298
+ if (prior.at(-1) !== entry)
30299
+ story.priorErrors = [...prior, entry];
30300
+ }
30234
30301
  }
30235
30302
  }
30236
30303
  var PRD_MAX_FILE_SIZE;
@@ -37138,6 +37205,11 @@ var init_ac_quote_validator = __esm(() => {
37138
37205
  function findingKey(f) {
37139
37206
  return JSON.stringify([f.source, f.file ?? null, f.line ?? null, f.rule ?? null, f.message]);
37140
37207
  }
37208
+ function findingRecurrenceKey(f) {
37209
+ if (f.line == null && f.rule == null)
37210
+ return findingKey(f);
37211
+ return JSON.stringify([f.source, f.file ?? null, f.line ?? null, f.rule ?? null]);
37212
+ }
37141
37213
  var SEVERITY_ORDER;
37142
37214
  var init_types6 = __esm(() => {
37143
37215
  SEVERITY_ORDER = Object.freeze({
@@ -45688,6 +45760,8 @@ function recordIteration(cycle, input, ctx, logger) {
45688
45760
  const findingsAfterCount = input.findingsAfter.length;
45689
45761
  const findingKeysBefore = input.findingsBefore.map(findingKey);
45690
45762
  const findingKeysAfter = input.findingsAfter.map(findingKey);
45763
+ const findingRecurrenceKeysBefore = input.findingsBefore.map(findingRecurrenceKey);
45764
+ const findingRecurrenceKeysAfter = input.findingsAfter.map(findingRecurrenceKey);
45691
45765
  const costUsd = input.fixesApplied.reduce((sum, fa) => sum + (fa.costUsd ?? 0), 0);
45692
45766
  const seenTargetFiles = new Set;
45693
45767
  const fixTargetFiles = [];
@@ -45711,6 +45785,8 @@ function recordIteration(cycle, input, ctx, logger) {
45711
45785
  finishedAt: input.finishedAt,
45712
45786
  findingKeysBefore,
45713
45787
  findingKeysAfter,
45788
+ findingRecurrenceKeysBefore,
45789
+ findingRecurrenceKeysAfter,
45714
45790
  ...hasFixes ? { fixTargetFiles, fixSummaries } : {},
45715
45791
  ...costUsd > 0 ? { costUsd } : {}
45716
45792
  };
@@ -45726,6 +45802,8 @@ function recordIteration(cycle, input, ctx, logger) {
45726
45802
  findingsAfter: findingsAfterCount,
45727
45803
  findingKeysBefore,
45728
45804
  findingKeysAfter,
45805
+ findingRecurrenceKeysBefore,
45806
+ findingRecurrenceKeysAfter,
45729
45807
  ...hasFixes ? { fixTargetFiles, fixSummaries } : {},
45730
45808
  ...costUsd > 0 ? { costUsd } : {}
45731
45809
  });
@@ -45765,8 +45843,8 @@ function normalizeValidateResult(r) {
45765
45843
  return Array.isArray(r) ? { findings: r, shortCircuited: false } : r;
45766
45844
  }
45767
45845
  function classifySingleSource(before, after) {
45768
- const beforeKeys = new Set(before.map(findingKey));
45769
- const afterKeys = new Set(after.map(findingKey));
45846
+ const beforeKeys = new Set(before.map(findingRecurrenceKey));
45847
+ const afterKeys = new Set(after.map(findingRecurrenceKey));
45770
45848
  if (afterKeys.size === 0 && beforeKeys.size === 0)
45771
45849
  return "resolved";
45772
45850
  if (afterKeys.size === 0)
@@ -46685,7 +46763,7 @@ var package_default;
46685
46763
  var init_package = __esm(() => {
46686
46764
  package_default = {
46687
46765
  name: "@nathapp/nax",
46688
- version: "0.80.0-canary.1",
46766
+ version: "0.80.0-canary.2",
46689
46767
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
46690
46768
  type: "module",
46691
46769
  bin: {
@@ -46799,8 +46877,8 @@ var init_version = __esm(() => {
46799
46877
  NAX_VERSION = package_default.version;
46800
46878
  NAX_COMMIT = (() => {
46801
46879
  try {
46802
- if (/^[0-9a-f]{6,10}$/.test("aff12f95"))
46803
- return "aff12f95";
46880
+ if (/^[0-9a-f]{6,10}$/.test("7d7d11ff"))
46881
+ return "7d7d11ff";
46804
46882
  } catch {}
46805
46883
  try {
46806
46884
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -53432,7 +53510,8 @@ class SessionManager {
53432
53510
  signal: opts.signal,
53433
53511
  resume,
53434
53512
  onActiveCall: this._buildOnActiveCall(name),
53435
- onStreamActivity: this._onStreamActivity
53513
+ onStreamActivity: this._onStreamActivity,
53514
+ ...trackedSpawnDeadlines(this._config)
53436
53515
  });
53437
53516
  this._liveHandles.set(name, handle);
53438
53517
  const protocolIds = handle.protocolIds ?? NULL_PROTOCOL_IDS;
@@ -53585,6 +53664,7 @@ class SessionManager {
53585
53664
  var NULL_PROTOCOL_IDS;
53586
53665
  var init_manager2 = __esm(() => {
53587
53666
  init_types3();
53667
+ init_config();
53588
53668
  init_errors();
53589
53669
  init_logger2();
53590
53670
  init_dispatch_events();
@@ -63471,8 +63551,8 @@ function countOscillationOutcomes(iterations) {
63471
63551
  function madeNoProgress(iteration) {
63472
63552
  if (iteration.findingsBefore.length === 0)
63473
63553
  return false;
63474
- const after = new Set(iteration.findingsAfter.map(findingKey));
63475
- return iteration.findingsBefore.every((finding) => after.has(findingKey(finding)));
63554
+ const after = new Set(iteration.findingsAfter.map(findingRecurrenceKey));
63555
+ return iteration.findingsBefore.every((finding) => after.has(findingRecurrenceKey(finding)));
63476
63556
  }
63477
63557
  function withNoProgressBail(strategies, enabled, consecutiveNoProgress) {
63478
63558
  if (!enabled)
@@ -65220,7 +65300,7 @@ var DEFAULT_MAX_OSCILLATIONS = 2;
65220
65300
  var init_oscillation_breaker = () => {};
65221
65301
 
65222
65302
  // src/execution/session-manager-runtime.ts
65223
- async function closePhysicalSession(descriptor, agentGetFn, force, signal) {
65303
+ async function closePhysicalSession2(descriptor, agentGetFn, force, signal) {
65224
65304
  if (!descriptor.handle)
65225
65305
  return;
65226
65306
  const adapter = agentGetFn?.(descriptor.agent);
@@ -65239,7 +65319,7 @@ async function closeStorylessSession(sessionManager, descriptor, agentGetFn, opt
65239
65319
  } catch {}
65240
65320
  }
65241
65321
  const force = opts?.force === true || descriptor.state === "FAILED";
65242
- await closePhysicalSession(descriptor, agentGetFn, force, opts?.signal);
65322
+ await closePhysicalSession2(descriptor, agentGetFn, force, opts?.signal);
65243
65323
  return 1;
65244
65324
  }
65245
65325
  function getStorylessCloseChain(state) {
@@ -65262,7 +65342,7 @@ async function closeStorySessions(sessionManager, storyId, agentGetFn, opts) {
65262
65342
  const closedSessions = sessionManager.closeStory(storyId);
65263
65343
  for (const descriptor of closedSessions) {
65264
65344
  const force = opts?.force === true || descriptor.state === "FAILED";
65265
- await closePhysicalSession(descriptor, agentGetFn, force, opts?.signal);
65345
+ await closePhysicalSession2(descriptor, agentGetFn, force, opts?.signal);
65266
65346
  }
65267
65347
  return closedSessions.length;
65268
65348
  }
@@ -65279,7 +65359,7 @@ async function failAndClose(sessionManager, sessionId, agentGetFn) {
65279
65359
  }
65280
65360
  const failed = sessionManager.get(sessionId);
65281
65361
  if (failed) {
65282
- await closePhysicalSession(failed, agentGetFn, true);
65362
+ await closePhysicalSession2(failed, agentGetFn, true);
65283
65363
  }
65284
65364
  }
65285
65365
  async function closeAllRunSessions(sessionManager, agentGetFn, opts) {
@@ -74949,8 +75029,10 @@ async function handleNoTierAvailable(ctx, failureCategory) {
74949
75029
  const logger = getSafeLogger();
74950
75030
  const outcome = resolveMaxAttemptsOutcome(failureCategory);
74951
75031
  if (outcome === "pause") {
75032
+ const pipelineDetail = ctx.pipelineResult.reason ? `: ${ctx.pipelineResult.reason}` : "";
75033
+ const pauseReason = `Execution stopped (${failureCategory ?? "unknown"} requires human review)${pipelineDetail}`;
74952
75034
  const pausedPrd = { ...ctx.prd };
74953
- markStoryPaused(pausedPrd, ctx.story.id);
75035
+ markStoryPaused(pausedPrd, ctx.story.id, await verifyEscalationQuotes(pauseReason, ctx.workdir, ctx.story.id));
74954
75036
  await savePRD(pausedPrd, ctx.prdPath);
74955
75037
  logger?.warn("execution", "Story paused - no tier available (needs human review)", {
74956
75038
  storyId: ctx.story.id,
@@ -74962,7 +75044,7 @@ async function handleNoTierAvailable(ctx, failureCategory) {
74962
75044
  pipelineEventBus.emit({
74963
75045
  type: "story:paused",
74964
75046
  storyId: ctx.story.id,
74965
- reason: `Execution stopped (${failureCategory ?? "unknown"} requires human review)`,
75047
+ reason: pauseReason,
74966
75048
  cost: ctx.runtime?.costAggregator.byStory()[ctx.story.id]?.totalCostUsd ?? ctx.totalCost
74967
75049
  });
74968
75050
  return { outcome: "paused", prdDirty: true, prd: pausedPrd };
@@ -74990,8 +75072,10 @@ async function handleMaxAttemptsReached(ctx, failureCategory) {
74990
75072
  const logger = getSafeLogger();
74991
75073
  const outcome = resolveMaxAttemptsOutcome(failureCategory);
74992
75074
  if (outcome === "pause") {
75075
+ const pipelineDetail = ctx.pipelineResult.reason ? `: ${ctx.pipelineResult.reason}` : "";
75076
+ const pauseReason = `Max attempts reached (${failureCategory ?? "unknown"} requires human review)${pipelineDetail}`;
74993
75077
  const pausedPrd = { ...ctx.prd };
74994
- markStoryPaused(pausedPrd, ctx.story.id);
75078
+ markStoryPaused(pausedPrd, ctx.story.id, await verifyEscalationQuotes(pauseReason, ctx.workdir, ctx.story.id));
74995
75079
  await savePRD(pausedPrd, ctx.prdPath);
74996
75080
  logger?.warn("execution", "Story paused - max attempts reached (needs human review)", {
74997
75081
  storyId: ctx.story.id,
@@ -75003,7 +75087,7 @@ async function handleMaxAttemptsReached(ctx, failureCategory) {
75003
75087
  pipelineEventBus.emit({
75004
75088
  type: "story:paused",
75005
75089
  storyId: ctx.story.id,
75006
- reason: `Max attempts reached (${failureCategory ?? "unknown"} requires human review)`,
75090
+ reason: pauseReason,
75007
75091
  cost: ctx.runtime?.costAggregator.byStory()[ctx.story.id]?.totalCostUsd ?? ctx.totalCost
75008
75092
  });
75009
75093
  return { outcome: "paused", prdDirty: true, prd: pausedPrd };
@@ -75033,6 +75117,7 @@ var init_tier_outcome = __esm(() => {
75033
75117
  init_logger2();
75034
75118
  init_prd();
75035
75119
  init_progress();
75120
+ init_quote_integrity();
75036
75121
  init_tier_escalation();
75037
75122
  });
75038
75123
 
@@ -75333,7 +75418,9 @@ var init_tier_escalation = __esm(() => {
75333
75418
 
75334
75419
  // src/execution/escalation/index.ts
75335
75420
  var init_escalation = __esm(() => {
75421
+ init_quote_integrity();
75336
75422
  init_tier_escalation();
75423
+ init_tier_outcome();
75337
75424
  });
75338
75425
 
75339
75426
  // src/execution/executor-types.ts
@@ -76448,8 +76535,10 @@ async function handlePipelineFailure(ctx, pipelineResult) {
76448
76535
  let prdDirty = false;
76449
76536
  const costDelta = (pipelineResult.context.agentResult?.estimatedCostUsd ?? 0) + (pipelineResult.stageCost ?? 0);
76450
76537
  switch (pipelineResult.finalAction) {
76451
- case "pause":
76452
- markStoryPaused(prd, ctx.story.id);
76538
+ case "pause": {
76539
+ const rawPauseReason = pipelineResult.reason ?? "";
76540
+ const pauseReason = rawPauseReason ? await verifyEscalationQuotes(rawPauseReason, ctx.workdir, ctx.story.id) : rawPauseReason;
76541
+ markStoryPaused(prd, ctx.story.id, pauseReason || undefined);
76453
76542
  await savePRD(prd, ctx.prdPath);
76454
76543
  prdDirty = true;
76455
76544
  logger?.warn("pipeline", "Story paused", { storyId: ctx.story.id, reason: pipelineResult.reason });
@@ -76463,6 +76552,7 @@ async function handlePipelineFailure(ctx, pipelineResult) {
76463
76552
  cost: ctx.runtime.costAggregator.byStory()[ctx.story.id]?.totalCostUsd ?? ctx.totalCost
76464
76553
  });
76465
76554
  break;
76555
+ }
76466
76556
  case "skip":
76467
76557
  logger?.warn("pipeline", "Story skipped", { storyId: ctx.story.id, reason: pipelineResult.reason });
76468
76558
  pipelineEventBus.emit({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.80.0-canary.1",
3
+ "version": "0.80.0-canary.2",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {