@nathapp/nax 0.83.6 → 0.84.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.
Files changed (2) hide show
  1. package/dist/nax.js +971 -699
  2. package/package.json +5 -6
package/dist/nax.js CHANGED
@@ -322,6 +322,31 @@ var init_approval_audit = __esm(() => {
322
322
  init_secret_spans();
323
323
  });
324
324
 
325
+ // ../nax-agent/src/internal/atomic-write.ts
326
+ import { randomUUID } from "crypto";
327
+ import { rename, rm, writeFile } from "fs/promises";
328
+ async function writeFileAtomic(path, content, options = {}) {
329
+ const staged = `${path}.${randomUUID()}.tmp`;
330
+ try {
331
+ await _atomicWriteDeps.writeFile(staged, content, {
332
+ encoding: "utf8",
333
+ ...options.mode !== undefined ? { mode: options.mode } : {}
334
+ });
335
+ await _atomicWriteDeps.rename(staged, path);
336
+ } catch (error) {
337
+ await _atomicWriteDeps.rm(staged, { force: true });
338
+ throw error;
339
+ }
340
+ }
341
+ var _atomicWriteDeps;
342
+ var init_atomic_write = __esm(() => {
343
+ _atomicWriteDeps = {
344
+ writeFile: (path, content, options) => writeFile(path, content, options),
345
+ rename,
346
+ rm
347
+ };
348
+ });
349
+
325
350
  // ../nax-agent/src/internal/process-alive.ts
326
351
  function isProcessAlive(pid) {
327
352
  if (!Number.isInteger(pid) || pid <= 0)
@@ -335,7 +360,7 @@ function isProcessAlive(pid) {
335
360
  }
336
361
 
337
362
  // ../nax-agent/src/internal/file-lock.ts
338
- import { readFile, stat, unlink, writeFile } from "fs/promises";
363
+ import { readFile, stat, unlink, writeFile as writeFile2 } from "fs/promises";
339
364
  function unlinkQuiet(path) {
340
365
  return _fileLockDeps.unlink(path).catch(() => {});
341
366
  }
@@ -428,7 +453,7 @@ var DEFAULT_RETRY_MS = 10, DEFAULT_TIMEOUT_MS = 5000, EMPTY_LOCK_EVICT_AGE_MS =
428
453
  var init_file_lock = __esm(() => {
429
454
  init_infra();
430
455
  _fileLockDeps = {
431
- writeFile,
456
+ writeFile: writeFile2,
432
457
  readFile,
433
458
  stat,
434
459
  unlink,
@@ -453,7 +478,7 @@ var init_path_file_lock = __esm(() => {
453
478
 
454
479
  // ../nax-agent/src/permissions/approvals-store.ts
455
480
  import { createHash } from "crypto";
456
- import { mkdir as mkdir2, readFile as readFile2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
481
+ import { mkdir as mkdir2, readFile as readFile2, stat as stat2 } from "fs/promises";
457
482
  import { dirname, isAbsolute, join as join2, relative, resolve } from "path";
458
483
  function approvalsPath(outputDir) {
459
484
  return join2(outputDir, "approvals.json");
@@ -529,14 +554,20 @@ async function readApprovals(path) {
529
554
  async function writeApprovalsFile(path, file) {
530
555
  const body = file.taint === undefined ? { entries: file.entries } : { taint: file.taint, entries: file.entries };
531
556
  await mkdir2(dirname(path), { recursive: true });
532
- await writeFile2(path, `${JSON.stringify(body, null, 2)}
533
- `, "utf8");
557
+ await writeFileAtomic(path, `${JSON.stringify(body, null, 2)}
558
+ `);
534
559
  }
535
560
  async function appendApproval(path, entry) {
536
561
  await mkdir2(dirname(path), { recursive: true });
537
562
  await withPathFileLock(path, async () => {
538
- const existing = await readApprovalsFile(path);
539
- await writeApprovalsFile(path, { taint: existing.taint, entries: [...existing.entries, entry] });
563
+ const read = await readApprovalsFileDetailed(path);
564
+ if (read.state === "unparseable") {
565
+ throw new NaxError("approvals.json could not be parsed; not rewriting it", "APPROVALS_STORE_UNPARSEABLE", {
566
+ stage: "permissions",
567
+ path
568
+ });
569
+ }
570
+ await writeApprovalsFile(path, { taint: read.file.taint, entries: [...read.file.entries, entry] });
540
571
  });
541
572
  }
542
573
  async function applyRemoval(path, decide) {
@@ -587,6 +618,8 @@ function isWithin(projectRoot, root) {
587
618
  }
588
619
  var EMPTY_FILE;
589
620
  var init_approvals_store = __esm(() => {
621
+ init_infra();
622
+ init_atomic_write();
590
623
  init_path_file_lock();
591
624
  EMPTY_FILE = { entries: [], taint: undefined };
592
625
  });
@@ -2165,6 +2198,266 @@ function killProcessGroup(pid, signal) {
2165
2198
  }
2166
2199
  }
2167
2200
 
2201
+ // ../nax-agent/src/terminal-auth/open-url.ts
2202
+ import { spawn } from "child_process";
2203
+ function spawnDetached(command) {
2204
+ const [file, ...args] = command;
2205
+ const child = spawn(file ?? "", args, { stdio: "ignore", detached: true, windowsHide: true });
2206
+ child.on("error", () => {});
2207
+ child.unref();
2208
+ return child;
2209
+ }
2210
+ function openerFor(platform, url) {
2211
+ if (platform === "darwin")
2212
+ return ["open", url];
2213
+ if (platform === "win32")
2214
+ return ["cmd", "/c", "start", "", url];
2215
+ return ["xdg-open", url];
2216
+ }
2217
+ function openUrl(url) {
2218
+ try {
2219
+ _openUrlDeps.spawn(openerFor(_openUrlDeps.platform(), url));
2220
+ } catch {}
2221
+ }
2222
+ var _openUrlDeps;
2223
+ var init_open_url = __esm(() => {
2224
+ _openUrlDeps = {
2225
+ spawn: (command) => {
2226
+ spawnDetached(command);
2227
+ },
2228
+ platform: () => process.platform
2229
+ };
2230
+ });
2231
+
2232
+ // ../nax-agent/src/terminal-auth/prompt.ts
2233
+ function stdinOf() {
2234
+ return _terminalPromptDeps.stdin ?? process.stdin;
2235
+ }
2236
+ function keyAction(char, bufferEmpty, hasEmptyHook) {
2237
+ if (char === ETX || char === EOT)
2238
+ return { kind: "cancel" };
2239
+ if (char === CR || char === LF) {
2240
+ return bufferEmpty && hasEmptyHook ? { kind: "empty-submit" } : { kind: "submit" };
2241
+ }
2242
+ if (char === BACKSPACE)
2243
+ return { kind: "erase" };
2244
+ return { kind: "append" };
2245
+ }
2246
+ function applyEdit(action, char, buffer, echo, onEmptySubmit) {
2247
+ if (action.kind === "empty-submit") {
2248
+ onEmptySubmit?.();
2249
+ return buffer;
2250
+ }
2251
+ if (action.kind === "erase") {
2252
+ if (echo)
2253
+ _terminalPromptDeps.write("\b \b");
2254
+ return buffer.slice(0, -1);
2255
+ }
2256
+ if (echo)
2257
+ _terminalPromptDeps.write(char);
2258
+ return buffer + char;
2259
+ }
2260
+ function read(message, echo, onEmptySubmit, style) {
2261
+ const stdin = stdinOf();
2262
+ if (stdin.isTTY !== true)
2263
+ return Promise.reject(new PromptCancelledError);
2264
+ _terminalPromptDeps.write(`${style.accent("?")} ${message} `);
2265
+ return new Promise((resolve3, reject) => {
2266
+ let buffer = "";
2267
+ let settled = false;
2268
+ const cleanup = () => {
2269
+ if (settled)
2270
+ return;
2271
+ settled = true;
2272
+ stdin.removeListener("data", onData);
2273
+ stdin.removeListener("end", onEnd);
2274
+ stdin.removeListener("error", onEnd);
2275
+ stdin.setRawMode(false);
2276
+ stdin.pause();
2277
+ _terminalPromptDeps.write(`
2278
+ `);
2279
+ };
2280
+ const onEnd = () => {
2281
+ cleanup();
2282
+ reject(new PromptCancelledError);
2283
+ };
2284
+ const onData = (chunk) => {
2285
+ for (const char of chunk) {
2286
+ const action = keyAction(char, buffer.length === 0, onEmptySubmit !== undefined);
2287
+ if (action.kind === "cancel") {
2288
+ onEnd();
2289
+ return;
2290
+ }
2291
+ if (action.kind === "submit") {
2292
+ cleanup();
2293
+ resolve3(buffer);
2294
+ return;
2295
+ }
2296
+ buffer = applyEdit(action, char, buffer, echo, onEmptySubmit);
2297
+ }
2298
+ };
2299
+ stdin.setRawMode(true);
2300
+ stdin.resume();
2301
+ stdin.setEncoding("utf8");
2302
+ stdin.on("data", onData);
2303
+ stdin.once("end", onEnd);
2304
+ stdin.once("error", onEnd);
2305
+ });
2306
+ }
2307
+ function promptForSecret(message, style = PLAIN_STYLE) {
2308
+ return read(message, false, undefined, style);
2309
+ }
2310
+ function promptForLine(message, onEmptySubmit, style = PLAIN_STYLE) {
2311
+ return read(message, true, onEmptySubmit, style);
2312
+ }
2313
+ function promptForSelect(message, choices, style = PLAIN_STYLE) {
2314
+ const stdin = stdinOf();
2315
+ if (stdin.isTTY !== true)
2316
+ return Promise.reject(new PromptCancelledError);
2317
+ if (choices.length === 0)
2318
+ return Promise.reject(new PromptCancelledError);
2319
+ _terminalPromptDeps.write(`${style.accent("?")} ${message}
2320
+ `);
2321
+ return new Promise((resolve3, reject) => {
2322
+ let index = 0;
2323
+ let settled = false;
2324
+ let drawn = false;
2325
+ const render = () => {
2326
+ if (drawn)
2327
+ _terminalPromptDeps.write(`\x1B[${choices.length}A`);
2328
+ drawn = true;
2329
+ for (const [i, choice] of choices.entries()) {
2330
+ const active = i === index;
2331
+ const marker = active ? style.accent(">") : " ";
2332
+ const label = active ? style.accent(choice.label) : choice.label;
2333
+ _terminalPromptDeps.write(`\r\x1B[2K${marker} ${label}
2334
+ `);
2335
+ }
2336
+ };
2337
+ const cleanup = () => {
2338
+ if (settled)
2339
+ return;
2340
+ settled = true;
2341
+ stdin.removeListener("data", onData);
2342
+ stdin.removeListener("end", onEnd);
2343
+ stdin.removeListener("error", onEnd);
2344
+ stdin.setRawMode(false);
2345
+ stdin.pause();
2346
+ };
2347
+ const onEnd = () => {
2348
+ cleanup();
2349
+ reject(new PromptCancelledError);
2350
+ };
2351
+ const onData = (chunk) => {
2352
+ if (chunk.includes(ETX) || chunk.includes(EOT)) {
2353
+ onEnd();
2354
+ return;
2355
+ }
2356
+ if (chunk.includes(ARROW_UP)) {
2357
+ index = (index - 1 + choices.length) % choices.length;
2358
+ render();
2359
+ return;
2360
+ }
2361
+ if (chunk.includes(ARROW_DOWN)) {
2362
+ index = (index + 1) % choices.length;
2363
+ render();
2364
+ return;
2365
+ }
2366
+ if (chunk.includes(CR) || chunk.includes(LF)) {
2367
+ const chosen = choices[index];
2368
+ cleanup();
2369
+ resolve3(chosen.id);
2370
+ }
2371
+ };
2372
+ stdin.setRawMode(true);
2373
+ stdin.resume();
2374
+ stdin.setEncoding("utf8");
2375
+ stdin.on("data", onData);
2376
+ stdin.once("end", onEnd);
2377
+ stdin.once("error", onEnd);
2378
+ render();
2379
+ });
2380
+ }
2381
+ var ETX = "\x03", EOT = "\x04", CR = "\r", LF = `
2382
+ `, BACKSPACE = "\x7F", ARROW_UP = "\x1B[A", ARROW_DOWN = "\x1B[B", identity = (text) => text, PLAIN_STYLE, PromptCancelledError, _terminalPromptDeps;
2383
+ var init_prompt = __esm(() => {
2384
+ PLAIN_STYLE = { accent: identity, dim: identity, bold: identity };
2385
+ PromptCancelledError = class PromptCancelledError extends Error {
2386
+ constructor() {
2387
+ super("Prompt cancelled");
2388
+ this.name = "PromptCancelledError";
2389
+ }
2390
+ };
2391
+ _terminalPromptDeps = {
2392
+ stdin: undefined,
2393
+ write: (text) => process.stdout.write(text)
2394
+ };
2395
+ });
2396
+
2397
+ // ../nax-agent/src/terminal-auth/interaction.ts
2398
+ function createTerminalAuthInteraction(options) {
2399
+ const style = options.style ?? PLAIN_STYLE;
2400
+ const open = options.openUrl ?? openUrl;
2401
+ let pendingUrl;
2402
+ const manualCode = (message) => {
2403
+ const url = pendingUrl;
2404
+ if (url === undefined)
2405
+ return promptForLine(message, undefined, style);
2406
+ pendingUrl = undefined;
2407
+ options.log(style.dim("Press Enter to open it in your browser."));
2408
+ return promptForLine(message, () => {
2409
+ options.log(style.dim("Opening your browser..."));
2410
+ open(url);
2411
+ }, style);
2412
+ };
2413
+ return {
2414
+ prompt: async (prompt) => {
2415
+ if (prompt.type === "manual-code")
2416
+ return manualCode(prompt.message);
2417
+ if (prompt.type === "text")
2418
+ return promptForLine(prompt.message, undefined, style);
2419
+ if (prompt.type === "select") {
2420
+ return promptForSelect(prompt.message, prompt.options.map((option) => ({ id: option.id, label: option.label })), style);
2421
+ }
2422
+ return promptForSecret(prompt.message, style);
2423
+ },
2424
+ notify: (event) => {
2425
+ switch (event.type) {
2426
+ case "auth-url":
2427
+ options.log(`
2428
+ ${style.bold("Open this URL to continue:")}
2429
+ ${event.url}`);
2430
+ pendingUrl = event.url;
2431
+ return;
2432
+ case "device-code":
2433
+ options.log(`
2434
+ Go to ${event.verificationUri} and enter code ${style.bold(event.userCode)}`);
2435
+ return;
2436
+ case "info":
2437
+ options.log(event.message);
2438
+ for (const link of event.links ?? [])
2439
+ options.log(` ${link.label ?? "Link"}: ${link.url}`);
2440
+ return;
2441
+ case "progress":
2442
+ options.log(style.dim(event.message));
2443
+ return;
2444
+ default:
2445
+ }
2446
+ }
2447
+ };
2448
+ }
2449
+ var init_interaction = __esm(() => {
2450
+ init_open_url();
2451
+ init_prompt();
2452
+ });
2453
+
2454
+ // ../nax-agent/src/terminal-auth/index.ts
2455
+ var init_terminal_auth = __esm(() => {
2456
+ init_interaction();
2457
+ init_open_url();
2458
+ init_prompt();
2459
+ });
2460
+
2168
2461
  // ../nax-agent/src/native/credentials/chained-store.ts
2169
2462
  function createChainedCredentialStore(options) {
2170
2463
  const served = new Map;
@@ -2212,8 +2505,8 @@ var init_chained_store = __esm(() => {
2212
2505
  });
2213
2506
 
2214
2507
  // ../nax-agent/src/native/credentials/fingerprint.ts
2215
- import { createHmac, randomBytes, randomUUID } from "crypto";
2216
- import { link, mkdir as mkdir4, readFile as readFile3, rm, writeFile as writeFile3 } from "fs/promises";
2508
+ import { createHmac, randomBytes, randomUUID as randomUUID2 } from "crypto";
2509
+ import { link, mkdir as mkdir4, readFile as readFile3, rm as rm2, writeFile as writeFile3 } from "fs/promises";
2217
2510
  import { dirname as dirname2, join as join5 } from "path";
2218
2511
  function _resetFingerprintSalt() {
2219
2512
  memoPath = undefined;
@@ -2235,7 +2528,7 @@ async function readSaltFile(path) {
2235
2528
  async function createSaltFile(path) {
2236
2529
  await mkdir4(dirname2(path), { recursive: true });
2237
2530
  const candidate = randomBytes(SALT_BYTES);
2238
- const staged = `${path}.${randomUUID()}.staged`;
2531
+ const staged = `${path}.${randomUUID2()}.staged`;
2239
2532
  try {
2240
2533
  await writeFile3(staged, candidate, { mode: 384 });
2241
2534
  try {
@@ -2247,7 +2540,7 @@ async function createSaltFile(path) {
2247
2540
  }
2248
2541
  return candidate;
2249
2542
  } finally {
2250
- await rm(staged, { force: true });
2543
+ await rm2(staged, { force: true });
2251
2544
  }
2252
2545
  }
2253
2546
  async function loadSalt(path) {
@@ -2315,37 +2608,37 @@ function classify2(previous, next) {
2315
2608
  return "renewed";
2316
2609
  return "changed";
2317
2610
  }
2318
- function identityData(providerId, identity) {
2611
+ function identityData(providerId, identity2) {
2319
2612
  return {
2320
2613
  providerId,
2321
- kind: identity.kind,
2322
- source: identity.source,
2323
- fingerprint: identity.fingerprint,
2324
- ...identity.account !== undefined ? { account: identity.account } : {}
2614
+ kind: identity2.kind,
2615
+ source: identity2.source,
2616
+ fingerprint: identity2.fingerprint,
2617
+ ...identity2.account !== undefined ? { account: identity2.account } : {}
2325
2618
  };
2326
2619
  }
2327
- function replacementData(providerId, identity, previous) {
2620
+ function replacementData(providerId, identity2, previous) {
2328
2621
  return {
2329
- ...identityData(providerId, identity),
2622
+ ...identityData(providerId, identity2),
2330
2623
  previousFingerprint: previous.fingerprint,
2331
2624
  ...previous.account !== undefined ? { previousAccount: previous.account } : {}
2332
2625
  };
2333
2626
  }
2334
- function toStamp(identity) {
2627
+ function toStamp(identity2) {
2335
2628
  return {
2336
- fingerprint: identity.fingerprint,
2337
- source: identity.source,
2338
- ...identity.account !== undefined ? { account: identity.account } : {}
2629
+ fingerprint: identity2.fingerprint,
2630
+ source: identity2.source,
2631
+ ...identity2.account !== undefined ? { account: identity2.account } : {}
2339
2632
  };
2340
2633
  }
2341
2634
  function createChangeGuard(inner, options) {
2342
2635
  const identities = new Map;
2343
- async function read(providerId) {
2636
+ async function read2(providerId) {
2344
2637
  const credential = await inner.read(providerId);
2345
2638
  if (credential === undefined)
2346
2639
  return;
2347
2640
  const described = options.describe(providerId);
2348
- const identity = {
2641
+ const identity2 = {
2349
2642
  kind: credential.kind,
2350
2643
  fingerprint: await fingerprintCredential(credential, options.salt),
2351
2644
  source: described?.source ?? "file",
@@ -2353,39 +2646,39 @@ function createChangeGuard(inner, options) {
2353
2646
  };
2354
2647
  const previous = identities.get(providerId);
2355
2648
  if (previous === undefined) {
2356
- getSafeLogger()?.info("credentials", "credential.resolved", identityData(providerId, identity));
2357
- identities.set(providerId, identity);
2649
+ getSafeLogger()?.info("credentials", "credential.resolved", identityData(providerId, identity2));
2650
+ identities.set(providerId, identity2);
2358
2651
  return credential;
2359
2652
  }
2360
- const verdict = classify2(previous, identity);
2653
+ const verdict = classify2(previous, identity2);
2361
2654
  if (verdict === "renewed") {
2362
- getSafeLogger()?.info("credentials", "credential.renewed", replacementData(providerId, identity, previous));
2655
+ getSafeLogger()?.info("credentials", "credential.renewed", replacementData(providerId, identity2, previous));
2363
2656
  } else if (verdict === "changed") {
2364
2657
  getSafeLogger()?.warn("credentials", "credential.changed", {
2365
- ...replacementData(providerId, identity, previous),
2658
+ ...replacementData(providerId, identity2, previous),
2366
2659
  onChange: options.onChange
2367
2660
  });
2368
2661
  if (options.onChange === "refuse") {
2369
- throw new NaxError(`[credentials] Credential for ${providerId} changed from ${previous.fingerprint} to ${identity.fingerprint}`, "CREDENTIAL_CHANGED", {
2662
+ throw new NaxError(`[credentials] Credential for ${providerId} changed from ${previous.fingerprint} to ${identity2.fingerprint}`, "CREDENTIAL_CHANGED", {
2370
2663
  stage: "credentials",
2371
2664
  providerId,
2372
- kind: identity.kind,
2373
- source: identity.source,
2374
- fingerprint: identity.fingerprint,
2665
+ kind: identity2.kind,
2666
+ source: identity2.source,
2667
+ fingerprint: identity2.fingerprint,
2375
2668
  previousFingerprint: previous.fingerprint
2376
2669
  });
2377
2670
  }
2378
2671
  }
2379
- identities.set(providerId, identity);
2672
+ identities.set(providerId, identity2);
2380
2673
  return credential;
2381
2674
  }
2382
2675
  return {
2383
- read,
2676
+ read: read2,
2384
2677
  modify: (providerId, fn) => inner.modify(providerId, fn),
2385
2678
  delete: (providerId) => inner.delete(providerId),
2386
2679
  servedAuth: (providerId) => {
2387
- const identity = identities.get(providerId);
2388
- return identity === undefined ? undefined : toStamp(identity);
2680
+ const identity2 = identities.get(providerId);
2681
+ return identity2 === undefined ? undefined : toStamp(identity2);
2389
2682
  }
2390
2683
  };
2391
2684
  }
@@ -2947,7 +3240,7 @@ function createExecCredentialSource(options) {
2947
3240
  async function callHelper(providerId) {
2948
3241
  return applyOutcome(providerId, outcomeOf(await runHelper(options.command, providerId, timeoutMs)));
2949
3242
  }
2950
- async function read(providerId) {
3243
+ async function read2(providerId) {
2951
3244
  if (declined.has(providerId))
2952
3245
  return;
2953
3246
  const lease = leases.get(providerId);
@@ -2969,7 +3262,7 @@ function createExecCredentialSource(options) {
2969
3262
  return new NaxError(`[credentials] Cannot ${operation} the credential for ${providerId}: the exec helper owns it.`, "CREDENTIAL_MANAGED_BY_HELPER", { stage: "credentials", providerId, operation });
2970
3263
  }
2971
3264
  return {
2972
- read,
3265
+ read: read2,
2973
3266
  modify(providerId) {
2974
3267
  throw managedByHelper(providerId, "modify");
2975
3268
  },
@@ -3170,11 +3463,16 @@ async function importPiCredentials(options) {
3170
3463
  cause: error
3171
3464
  });
3172
3465
  }
3466
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
3467
+ throw new NaxError(`The file at ${path} is not a provider credential map.`, "AUTH_IMPORT_SOURCE_UNREADABLE", {
3468
+ path
3469
+ });
3470
+ }
3173
3471
  const store = naxCredentialStore();
3174
3472
  const outcomes = [];
3175
3473
  for (const providerId of Object.keys(parsed).sort()) {
3176
3474
  const entry = parsed[providerId];
3177
- const credential = entry === undefined ? undefined : fromPiEntry(entry);
3475
+ const credential = typeof entry === "object" && entry !== null ? fromPiEntry(entry) : undefined;
3178
3476
  if (credential === undefined) {
3179
3477
  outcomes.push({ providerId, status: "unsupported" });
3180
3478
  continue;
@@ -3194,6 +3492,9 @@ async function importPiCredentials(options) {
3194
3492
  async function listStoredProviders() {
3195
3493
  return readStoredEntries();
3196
3494
  }
3495
+ async function loginProviderIds() {
3496
+ return _authDeps.providerIds();
3497
+ }
3197
3498
  async function removeStoredProvider(providerId) {
3198
3499
  await naxCredentialStore().delete(providerId);
3199
3500
  }
@@ -3543,12 +3844,12 @@ var init_errors = __esm(() => {
3543
3844
  });
3544
3845
 
3545
3846
  // ../nax-agent/src/native/session-affinity.ts
3546
- import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
3847
+ import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
3547
3848
  function nativeSessionId(sessionKey) {
3548
3849
  return createHash3("sha256").update(sessionKey).digest("hex").slice(0, ID_LENGTH);
3549
3850
  }
3550
3851
  function newSessionKey() {
3551
- return randomUUID2();
3852
+ return randomUUID3();
3552
3853
  }
3553
3854
  var ID_LENGTH = 32;
3554
3855
  var init_session_affinity = () => {};
@@ -3666,8 +3967,8 @@ function createMemoryTranscriptStore() {
3666
3967
  function transcriptModelIdentity(rawModel) {
3667
3968
  return rawModel === undefined ? undefined : parseModelSpec(rawModel).model;
3668
3969
  }
3669
- function isForeignTranscript(doc, identity, sessionName) {
3670
- const { owner, model } = identity;
3970
+ function isForeignTranscript(doc, identity2, sessionName) {
3971
+ const { owner, model } = identity2;
3671
3972
  if (owner !== undefined && doc.owner !== owner) {
3672
3973
  getLogger().debug("native-session", "Ignoring a transcript owned by another invocation", {
3673
3974
  sessionName,
@@ -3676,7 +3977,7 @@ function isForeignTranscript(doc, identity, sessionName) {
3676
3977
  });
3677
3978
  return true;
3678
3979
  }
3679
- if (model !== undefined && doc.model !== undefined && doc.model !== model) {
3980
+ if (identity2.carryAcrossModels !== true && model !== undefined && doc.model !== undefined && doc.model !== model) {
3680
3981
  getLogger().debug("native-session", "Ignoring a transcript written by another model", {
3681
3982
  sessionName,
3682
3983
  storedModel: doc.model,
@@ -3686,7 +3987,7 @@ function isForeignTranscript(doc, identity, sessionName) {
3686
3987
  }
3687
3988
  return false;
3688
3989
  }
3689
- function historyFromTranscript(doc, identity, sessionName) {
3990
+ function historyFromTranscript(doc, identity2, sessionName) {
3690
3991
  if (doc === null)
3691
3992
  return [];
3692
3993
  const version = doc.schemaVersion;
@@ -3696,14 +3997,14 @@ function historyFromTranscript(doc, identity, sessionName) {
3696
3997
  if (!Array.isArray(doc.messages)) {
3697
3998
  throw new NaxError(`transcript for session "${sessionName}" is unreadable: messages is not an array`, "TRANSCRIPT_CORRUPT", { stage: "native-session" });
3698
3999
  }
3699
- if (isForeignTranscript(doc, identity, sessionName))
4000
+ if (isForeignTranscript(doc, identity2, sessionName))
3700
4001
  return [];
3701
4002
  return doc.messages;
3702
4003
  }
3703
- function transcriptDocFor(messages, identity) {
4004
+ function transcriptDocFor(messages, identity2) {
3704
4005
  return {
3705
- ...identity.owner !== undefined ? { owner: identity.owner } : {},
3706
- ...identity.model !== undefined ? { model: identity.model } : {},
4006
+ ...identity2.owner !== undefined ? { owner: identity2.owner } : {},
4007
+ ...identity2.model !== undefined ? { model: identity2.model } : {},
3707
4008
  savedAt: new Date().toISOString(),
3708
4009
  messages: [...messages]
3709
4010
  };
@@ -3728,7 +4029,7 @@ __export(exports_transcript_store, {
3728
4029
  transcriptModelIdentity: () => transcriptModelIdentity,
3729
4030
  transcriptPath: () => transcriptPath
3730
4031
  });
3731
- import { mkdir as mkdir5, readdir, readFile as readFile6, rename, rm as rm2, stat as stat3, writeFile as writeFile4 } from "fs/promises";
4032
+ import { mkdir as mkdir5, readdir, readFile as readFile6, rename as rename2, rm as rm3, stat as stat3 } from "fs/promises";
3732
4033
  import { join as join9 } from "path";
3733
4034
  function transcriptPath(dir, sessionName) {
3734
4035
  return join9(dir, `${sessionName}.transcript.json`);
@@ -3762,7 +4063,7 @@ async function readTranscriptDoc(dir, sessionName) {
3762
4063
  }
3763
4064
  async function writeTranscriptDoc(dir, sessionName, doc) {
3764
4065
  await mkdir5(dir, { recursive: true });
3765
- await writeFile4(transcriptPath(dir, sessionName), JSON.stringify(doc, null, 2), "utf8");
4066
+ await writeFileAtomic(transcriptPath(dir, sessionName), JSON.stringify(doc, null, 2));
3766
4067
  }
3767
4068
  function createFileTranscriptStore(dir) {
3768
4069
  return {
@@ -3780,16 +4081,16 @@ function createFileTranscriptStore(dir) {
3780
4081
  }
3781
4082
  };
3782
4083
  }
3783
- async function loadTranscript(dir, sessionName, identity = {}) {
3784
- return [...historyFromTranscript(await readTranscriptDoc(dir, sessionName), identity, sessionName)];
4084
+ async function loadTranscript(dir, sessionName, identity2 = {}) {
4085
+ return [...historyFromTranscript(await readTranscriptDoc(dir, sessionName), identity2, sessionName)];
3785
4086
  }
3786
- async function saveTranscript(dir, sessionName, messages, identity = {}) {
3787
- await writeTranscriptDoc(dir, sessionName, transcriptDocFor(messages, identity));
4087
+ async function saveTranscript(dir, sessionName, messages, identity2 = {}) {
4088
+ await writeTranscriptDoc(dir, sessionName, transcriptDocFor(messages, identity2));
3788
4089
  }
3789
4090
  async function retainTranscript(dir, sessionName) {
3790
4091
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
3791
4092
  try {
3792
- await rename(transcriptPath(dir, sessionName), join9(dir, `${sessionName}.transcript.failed-${stamp}.json`));
4093
+ await rename2(transcriptPath(dir, sessionName), join9(dir, `${sessionName}.transcript.failed-${stamp}.json`));
3793
4094
  } catch (err) {
3794
4095
  if (err.code === "ENOENT")
3795
4096
  return;
@@ -3797,7 +4098,7 @@ async function retainTranscript(dir, sessionName) {
3797
4098
  }
3798
4099
  }
3799
4100
  async function deleteTranscript(dir, sessionName) {
3800
- await rm2(transcriptPath(dir, sessionName), { force: true });
4101
+ await rm3(transcriptPath(dir, sessionName), { force: true });
3801
4102
  }
3802
4103
  function _resetTranscriptTruncationWarningForTests() {
3803
4104
  hasWarnedAboutTranscriptTruncation = false;
@@ -3821,7 +4122,7 @@ async function pruneRetainedTranscripts(dir, maxRetained = MAX_RETAINED_TRANSCRI
3821
4122
  }));
3822
4123
  withMtimes.sort((a, b) => a.mtimeMs - b.mtimeMs);
3823
4124
  const toDelete = withMtimes.slice(0, withMtimes.length - maxRetained);
3824
- await Promise.all(toDelete.map((f) => rm2(f.path, { force: true })));
4125
+ await Promise.all(toDelete.map((f) => rm3(f.path, { force: true })));
3825
4126
  if (!hasWarnedAboutTranscriptTruncation) {
3826
4127
  hasWarnedAboutTranscriptTruncation = true;
3827
4128
  getLogger().warn("native-session", "Transcript retention cap reached \u2014 oldest kept-on-failure transcripts pruned", {
@@ -3835,6 +4136,7 @@ async function pruneRetainedTranscripts(dir, maxRetained = MAX_RETAINED_TRANSCRI
3835
4136
  var LEGACY_SAVED_AT, MAX_RETAINED_TRANSCRIPTS = 50, hasWarnedAboutTranscriptTruncation = false;
3836
4137
  var init_transcript_store = __esm(() => {
3837
4138
  init_infra();
4139
+ init_atomic_write();
3838
4140
  init_transcript_identity();
3839
4141
  init_transcript_identity();
3840
4142
  LEGACY_SAVED_AT = new Date(0).toISOString();
@@ -4504,7 +4806,7 @@ async function openRepositoryInstructions(name, opts, store) {
4504
4806
  if (doc === null || doc.schemaVersion !== undefined && doc.schemaVersion !== 1 || !Array.isArray(doc.messages))
4505
4807
  return instructions;
4506
4808
  const ownerMatches = opts.transcriptOwner === undefined || doc?.owner === opts.transcriptOwner;
4507
- const modelMatches = doc?.model === undefined || doc.model === transcriptModelIdentity(opts.modelDef?.model);
4809
+ const modelMatches = opts.carryHistoryAcrossModels === true || doc?.model === undefined || doc.model === transcriptModelIdentity(opts.modelDef?.model);
4508
4810
  if (ownerMatches && modelMatches) {
4509
4811
  const directories = doc?.instructionDirectories;
4510
4812
  if (Array.isArray(directories)) {
@@ -4557,6 +4859,7 @@ function createNativeSessionState() {
4557
4859
  streamHooks: new Map,
4558
4860
  failed: new Set,
4559
4861
  transcriptOwners: new Map,
4862
+ carryHistoryAcrossModels: new Set,
4560
4863
  compaction: new Map,
4561
4864
  transportRetry: new Map,
4562
4865
  spinBreakers: new Map,
@@ -4627,6 +4930,10 @@ async function openNativeSession(state, name, opts) {
4627
4930
  state.transcriptOwners.set(name, opts.transcriptOwner);
4628
4931
  else
4629
4932
  state.transcriptOwners.delete(name);
4933
+ if (opts.carryHistoryAcrossModels === true)
4934
+ state.carryHistoryAcrossModels.add(name);
4935
+ else
4936
+ state.carryHistoryAcrossModels.delete(name);
4630
4937
  recordSystemPrompt(state, name, opts.systemPrompt);
4631
4938
  const instructions = await openRepositoryInstructions(name, opts, store);
4632
4939
  state.repositoryInstructions.set(name, instructions);
@@ -4663,6 +4970,7 @@ function clearNativeSessionState(state, sessionName) {
4663
4970
  state.timeouts.delete(sessionName);
4664
4971
  state.scratchpadRoots.delete(sessionName);
4665
4972
  state.transcriptOwners.delete(sessionName);
4973
+ state.carryHistoryAcrossModels.delete(sessionName);
4666
4974
  state.streamHooks.delete(sessionName);
4667
4975
  state.failed.delete(sessionName);
4668
4976
  state.compaction.delete(sessionName);
@@ -4966,7 +5274,8 @@ function rewriteToolCallInput(messages, callId, input) {
4966
5274
  role: "assistant",
4967
5275
  content: lastAssistant.content,
4968
5276
  toolCalls,
4969
- ...lastAssistant.thinking !== undefined ? { thinking: lastAssistant.thinking } : {}
5277
+ ...lastAssistant.thinking !== undefined ? { thinking: lastAssistant.thinking } : {},
5278
+ ...lastAssistant.origin !== undefined ? { origin: lastAssistant.origin } : {}
4970
5279
  },
4971
5280
  ...messages.slice(lastAssistantIndex + 1)
4972
5281
  ];
@@ -19554,7 +19863,7 @@ var init_schemas_sandbox = __esm(() => {
19554
19863
  });
19555
19864
 
19556
19865
  // ../nax-agent/src/sandbox/git-guards.ts
19557
- import { lstat, readdir as readdir2, readFile as readFile7, rm as rm3 } from "fs/promises";
19866
+ import { lstat, readdir as readdir2, readFile as readFile7, rm as rm4 } from "fs/promises";
19558
19867
  import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute3, join as join11, resolve as resolve8 } from "path";
19559
19868
  function commonDirOf(git) {
19560
19869
  if (git.kind === "none")
@@ -19640,7 +19949,7 @@ var init_git_guards = __esm(() => {
19640
19949
  }
19641
19950
  },
19642
19951
  readText: (p) => readFile7(p, "utf8"),
19643
- remove: (p) => rm3(p, { recursive: true, force: true })
19952
+ remove: (p) => rm4(p, { recursive: true, force: true })
19644
19953
  };
19645
19954
  });
19646
19955
 
@@ -19671,7 +19980,7 @@ var init_messages = __esm(() => {
19671
19980
  });
19672
19981
 
19673
19982
  // ../nax-agent/src/sandbox/launcher.ts
19674
- import { randomUUID as randomUUID3 } from "crypto";
19983
+ import { randomUUID as randomUUID4 } from "crypto";
19675
19984
  import { mkdir as mkdir6 } from "fs/promises";
19676
19985
  function logicalArgv(req) {
19677
19986
  return req.spec.kind === "shell" ? [req.spec.shell, "-c", req.spec.command] : req.spec.argv;
@@ -19800,7 +20109,7 @@ var init_launcher = __esm(() => {
19800
20109
  DISABLED_SANDBOX_STATE = { kind: "disabled" };
19801
20110
  _launcherDeps = {
19802
20111
  runArgv,
19803
- newCommandId: () => randomUUID3(),
20112
+ newCommandId: () => randomUUID4(),
19804
20113
  mkdir: (path) => mkdir6(path, { recursive: true }).then(() => {
19805
20114
  return;
19806
20115
  })
@@ -19937,9 +20246,12 @@ function hardenedGitEnv(base) {
19937
20246
  function gitSpawnEnv(overlay) {
19938
20247
  return hardenedGitEnv(overlay === undefined ? process.env : { ...process.env, ...overlay });
19939
20248
  }
20249
+ function isGitProgram(token) {
20250
+ return token === "git" || (token?.endsWith("/git") ?? false);
20251
+ }
19940
20252
  function hardenedGitArgv(argv) {
19941
20253
  const out = [...argv];
19942
- let i = 1;
20254
+ let i = !isGitProgram(out[0]) && isGitProgram(out[1]) ? 2 : 1;
19943
20255
  while (i < out.length && out[i].startsWith("-")) {
19944
20256
  i += GLOBAL_OPTIONS_WITH_VALUE.has(out[i]) ? 2 : 1;
19945
20257
  }
@@ -20282,6 +20594,8 @@ function isUsableSharedParent() {
20282
20594
  const stats = _sessionTmpDeps.lstat(SHARED_TMP_PARENT);
20283
20595
  if (!stats.isDirectory() || stats.isSymbolicLink())
20284
20596
  return false;
20597
+ if (stats.uid !== _sessionTmpDeps.uid())
20598
+ return false;
20285
20599
  } catch (err) {
20286
20600
  return err.code === "ENOENT";
20287
20601
  }
@@ -20735,7 +21049,7 @@ var init_edit_region = __esm(() => {
20735
21049
  });
20736
21050
 
20737
21051
  // ../nax-agent/src/tools/edit.ts
20738
- import { readFile as readFile8, stat as stat5, writeFile as writeFile5 } from "fs/promises";
21052
+ import { readFile as readFile8, stat as stat5, writeFile as writeFile4 } from "fs/promises";
20739
21053
  function countOccurrences(haystack, needle) {
20740
21054
  if (needle === "")
20741
21055
  return 0;
@@ -20753,7 +21067,7 @@ var init_edit = __esm(() => {
20753
21067
  _editDeps = {
20754
21068
  stat: (path) => stat5(path),
20755
21069
  readFile: (path, encoding) => readFile8(path, encoding),
20756
- writeFile: (path, data, encoding) => writeFile5(path, data, encoding)
21070
+ writeFile: (path, data, encoding) => writeFile4(path, data, encoding)
20757
21071
  };
20758
21072
  editTool = {
20759
21073
  name: "Edit",
@@ -23713,7 +24027,7 @@ var init_denial_redirect = __esm(() => {
23713
24027
  });
23714
24028
 
23715
24029
  // ../nax-agent/src/tools/runtime-calltool.ts
23716
- import { randomUUID as randomUUID4 } from "crypto";
24030
+ import { randomUUID as randomUUID5 } from "crypto";
23717
24031
  function resolvePolicyIdentity(name, tool, input) {
23718
24032
  const argvField = tool.scope.argvField;
23719
24033
  const hasArgv = argvField !== undefined && input[argvField] !== undefined;
@@ -23724,7 +24038,7 @@ function openCallShadowTap(p) {
23724
24038
  if (p.shadow === undefined)
23725
24039
  return;
23726
24040
  return openShadowTap(p.shadow, {
23727
- key: randomUUID4(),
24041
+ key: randomUUID5(),
23728
24042
  identity: p.identity,
23729
24043
  command: p.tool.scope.commandField === undefined ? undefined : p.input[p.tool.scope.commandField],
23730
24044
  argv: p.hasArgv && p.argvField !== undefined ? p.input[p.argvField] : undefined,
@@ -23855,7 +24169,7 @@ var init_runtime_calltool = __esm(() => {
23855
24169
 
23856
24170
  // ../nax-agent/src/tools/scratchpad.ts
23857
24171
  import { existsSync as existsSync2 } from "fs";
23858
- import { mkdir as mkdir8, writeFile as writeFile6 } from "fs/promises";
24172
+ import { mkdir as mkdir8, writeFile as writeFile5 } from "fs/promises";
23859
24173
  import { dirname as dirname8, join as join18, sep as sep10 } from "path";
23860
24174
  var SCRATCHPAD_DIR = ".nax/scratchpad", MAX_MATCHES2 = 500, scratchpadWriteTool, scratchpadReadTool, scratchpadListTool;
23861
24175
  var init_scratchpad = __esm(() => {
@@ -23893,7 +24207,7 @@ var init_scratchpad = __esm(() => {
23893
24207
  }
23894
24208
  try {
23895
24209
  await mkdir8(dirname8(target), { recursive: true });
23896
- await writeFile6(target, content, "utf8");
24210
+ await writeFile5(target, content, "utf8");
23897
24211
  return { content: `wrote ${size} bytes` };
23898
24212
  } catch (err) {
23899
24213
  return { content: err instanceof Error ? err.message : String(err), isError: true };
@@ -23985,7 +24299,7 @@ ${content}`, resultBytesPreTruncation: fullBytes };
23985
24299
  });
23986
24300
 
23987
24301
  // ../nax-agent/src/tools/spill.ts
23988
- import { mkdir as mkdir9, writeFile as writeFile7 } from "fs/promises";
24302
+ import { mkdir as mkdir9, writeFile as writeFile6 } from "fs/promises";
23989
24303
  import { dirname as dirname9, join as join19 } from "path";
23990
24304
  function fileSafe(value) {
23991
24305
  return value.replace(/[^A-Za-z0-9_-]/g, "_");
@@ -24111,14 +24425,14 @@ var init_spill = __esm(() => {
24111
24425
  _spillDeps = {
24112
24426
  mkdir: (path) => mkdir9(path, { recursive: true }),
24113
24427
  writeFile: async (path, data) => {
24114
- await writeFile7(path, data, "utf8");
24428
+ await writeFile6(path, data, "utf8");
24115
24429
  return Buffer.byteLength(data);
24116
24430
  }
24117
24431
  };
24118
24432
  });
24119
24433
 
24120
24434
  // ../nax-agent/src/tools/tool-audit.ts
24121
- import { mkdir as mkdir10, writeFile as writeFile8 } from "fs/promises";
24435
+ import { mkdir as mkdir10, writeFile as writeFile7 } from "fs/promises";
24122
24436
  import { join as join20 } from "path";
24123
24437
  function registerToolAuditSink(runId, sink) {
24124
24438
  const sinks = openToolAuditSinks.get(runId);
@@ -24173,7 +24487,7 @@ function createToolAuditSink(opts) {
24173
24487
  calls: redactRowStrings(calls)
24174
24488
  }, null, 2);
24175
24489
  const prefix = runId !== undefined ? `${runId}-` : "";
24176
- await writeFile8(join20(opts.dir, `${prefix}${nextFileStamp()}-${opts.sessionName}.json`), body);
24490
+ await writeFile7(join20(opts.dir, `${prefix}${nextFileStamp()}-${opts.sessionName}.json`), body);
24177
24491
  };
24178
24492
  const sink = {
24179
24493
  record(entry) {
@@ -24214,7 +24528,7 @@ var init_tool_audit = __esm(() => {
24214
24528
  });
24215
24529
 
24216
24530
  // ../nax-agent/src/tools/write.ts
24217
- import { mkdir as mkdir11, writeFile as writeFile9 } from "fs/promises";
24531
+ import { mkdir as mkdir11, writeFile as writeFile8 } from "fs/promises";
24218
24532
  import { dirname as dirname10 } from "path";
24219
24533
  var writeTool;
24220
24534
  var init_write = __esm(() => {
@@ -24246,7 +24560,7 @@ var init_write = __esm(() => {
24246
24560
  }
24247
24561
  try {
24248
24562
  await mkdir11(dirname10(target), { recursive: true });
24249
- await writeFile9(target, content, "utf8");
24563
+ await writeFile8(target, content, "utf8");
24250
24564
  return { content: `wrote ${Buffer.byteLength(content, "utf8")} bytes` };
24251
24565
  } catch (err) {
24252
24566
  return { content: err instanceof Error ? err.message : String(err), isError: true };
@@ -24256,7 +24570,7 @@ var init_write = __esm(() => {
24256
24570
  });
24257
24571
 
24258
24572
  // ../nax-agent/src/tools/runtime.ts
24259
- import { randomUUID as randomUUID5 } from "crypto";
24573
+ import { randomUUID as randomUUID6 } from "crypto";
24260
24574
  function registerBuiltinCodingTools() {
24261
24575
  if (builtinsRegistered)
24262
24576
  return;
@@ -24340,7 +24654,7 @@ function createCodingToolRuntime(opts) {
24340
24654
  return body;
24341
24655
  return applyModelTruncationPolicy(body, {
24342
24656
  toolName,
24343
- callId: context?.toolCallId ?? randomUUID5(),
24657
+ callId: context?.toolCallId ?? randomUUID6(),
24344
24658
  root: opts.policy.root,
24345
24659
  maxBytes
24346
24660
  });
@@ -24802,7 +25116,7 @@ function usageEvent(round, usage, costUsd) {
24802
25116
  costUsd
24803
25117
  };
24804
25118
  }
24805
- function cappedInput(input) {
25119
+ function displayToolInput(input) {
24806
25120
  const redacted = redactSecrets(capStrings(input, REDACTION_SCAN_BYTES));
24807
25121
  let json2;
24808
25122
  try {
@@ -24814,7 +25128,7 @@ function cappedInput(input) {
24814
25128
  return redacted;
24815
25129
  return { truncated: true, preview: cutToByteCap(json2, TOOL_CALL_INPUT_BYTES) };
24816
25130
  }
24817
- function previewOf(content) {
25131
+ function toolResultPreview(content) {
24818
25132
  return cutToByteCap(redactSecrets(cutToByteCap(content, REDACTION_SCAN_BYTES)), TOOL_RESULT_PREVIEW_BYTES);
24819
25133
  }
24820
25134
  function createTurnEventEmitter(sink) {
@@ -24846,7 +25160,7 @@ function createTurnEventEmitter(sink) {
24846
25160
  emit,
24847
25161
  deltaSink: (round) => (delta) => emit({ ...delta, round }),
24848
25162
  toolCall(call, recordedInput) {
24849
- const input = cappedInput(recordedInput ?? call.input);
25163
+ const input = displayToolInput(recordedInput ?? call.input);
24850
25164
  outstanding.set(call.id, (outstanding.get(call.id) ?? 0) + 1);
24851
25165
  emit({ type: "tool_call", callId: call.id, name: call.name, input });
24852
25166
  },
@@ -24862,7 +25176,7 @@ function createTurnEventEmitter(sink) {
24862
25176
  type: "tool_result",
24863
25177
  callId: result.toolCallId,
24864
25178
  isError: result.isError === true,
24865
- preview: previewOf(result.content)
25179
+ preview: toolResultPreview(result.content)
24866
25180
  });
24867
25181
  },
24868
25182
  flushUnanswered() {
@@ -25491,6 +25805,15 @@ async function maybeCompact(state, params) {
25491
25805
  }
25492
25806
  return { summarizeFailed: step.summarizeFailed };
25493
25807
  }
25808
+ function originOf(raw) {
25809
+ if (raw === undefined)
25810
+ return;
25811
+ const spec = parseModelSpec(raw).model;
25812
+ const slash = spec.indexOf("/");
25813
+ if (slash <= 0 || slash === spec.length - 1)
25814
+ return;
25815
+ return { provider: spec.slice(0, slash), model: spec.slice(slash + 1) };
25816
+ }
25494
25817
  async function runModelRoundTrip(state, params, summarizeFailed) {
25495
25818
  const { handle, opts, deps, tools, loopEvents } = params;
25496
25819
  const step = await completeWithRecovery({
@@ -25516,7 +25839,6 @@ async function runModelRoundTrip(state, params, summarizeFailed) {
25516
25839
  }
25517
25840
  state.roundTrips += 1;
25518
25841
  params.usage.add(res.usage, res.costUsd, res.rates);
25519
- state.output = res.text;
25520
25842
  const promptTokens = inputClassTokens(res.usage);
25521
25843
  state.lastUsage = { promptTokens };
25522
25844
  state.anchorIndex = state.messages.length - 1;
@@ -25544,13 +25866,16 @@ async function runModelRoundTrip(state, params, summarizeFailed) {
25544
25866
  roundTrip: state.roundTrips
25545
25867
  });
25546
25868
  const assistantText = afterResponse.text ?? res.text;
25869
+ state.output = assistantText;
25547
25870
  const assistantToolCalls = afterResponse.toolCalls ?? res.toolCalls;
25548
25871
  const assistantThinking = afterResponse.thinking ?? res.thinking;
25872
+ const origin = originOf(handle.modelDef?.model);
25549
25873
  state.messages.push({
25550
25874
  role: "assistant",
25551
25875
  content: assistantText,
25552
25876
  ...assistantToolCalls !== undefined ? { toolCalls: assistantToolCalls } : {},
25553
- ...assistantThinking !== undefined ? { thinking: assistantThinking } : {}
25877
+ ...assistantThinking !== undefined ? { thinking: assistantThinking } : {},
25878
+ ...origin !== undefined ? { origin } : {}
25554
25879
  });
25555
25880
  if (assistantToolCalls === undefined || assistantToolCalls.length === 0) {
25556
25881
  state.completedNormally = true;
@@ -25639,6 +25964,7 @@ var MAX_FOLLOW_UPS_PER_TURN = 3;
25639
25964
  var init_turn_loop_round_trip = __esm(() => {
25640
25965
  init_core();
25641
25966
  init_infra();
25967
+ init_models();
25642
25968
  init_turn_accumulator();
25643
25969
  init_turn_compaction_step();
25644
25970
  init_turn_complete_step();
@@ -25728,7 +26054,8 @@ async function runNativeTurn(handle, prompt, opts, deps) {
25728
26054
  const { store } = transcript;
25729
26055
  const transcriptIdentity = {
25730
26056
  owner: deps.sessionState.transcriptOwners.get(handle.id),
25731
- model: transcriptModelIdentity(handle.modelDef?.model)
26057
+ model: transcriptModelIdentity(handle.modelDef?.model),
26058
+ ...deps.sessionState.carryHistoryAcrossModels.has(handle.id) ? { carryAcrossModels: true } : {}
25732
26059
  };
25733
26060
  let messages = [
25734
26061
  ...historyFromTranscript(await store.load(handle.id), transcriptIdentity, handle.id)
@@ -25910,7 +26237,7 @@ async function streamComplete(client, model, req, onDelta) {
25910
26237
  var init_stream_complete = () => {};
25911
26238
 
25912
26239
  // ../nax-agent/src/native/session-adapter.ts
25913
- import { randomUUID as randomUUID6 } from "crypto";
26240
+ import { randomUUID as randomUUID7 } from "crypto";
25914
26241
  function summaryPrompt(previousSummary) {
25915
26242
  const base = "Summarize the conversation above so it can be dropped from context. " + "Record what was attempted, what was rejected and why, any decisions that still bind, " + "and list the files read and the files modified. Be specific: this summary is the only " + "memory of this work that survives.";
25916
26243
  if (previousSummary === undefined)
@@ -25998,7 +26325,7 @@ class NativeSessionAdapter {
25998
26325
  const instructionInteraction = createInstructionInteraction(this.state.repositoryInstructions.get(handle.id), opts.interactionHandler);
25999
26326
  const deadline = createTurnDeadline(timeoutSeconds);
26000
26327
  const hooks = this.state.streamHooks.get(handle.id);
26001
- const callId = randomUUID6();
26328
+ const callId = randomUUID7();
26002
26329
  const owner = this.state.transcriptOwners.get(handle.id);
26003
26330
  const eventBase = {
26004
26331
  callId,
@@ -26194,8 +26521,8 @@ var init_native = __esm(() => {
26194
26521
  });
26195
26522
 
26196
26523
  // ../nax-agent/src/session/agent-session-deps.ts
26197
- import { randomUUID as randomUUID7 } from "crypto";
26198
- import { mkdtemp, rm as rm4, stat as stat6 } from "fs/promises";
26524
+ import { randomUUID as randomUUID8 } from "crypto";
26525
+ import { mkdtemp, rm as rm5, stat as stat6 } from "fs/promises";
26199
26526
  import { tmpdir as tmpdir4 } from "os";
26200
26527
  import { join as join21 } from "path";
26201
26528
  var MAX_UNDELIVERED_CONTROL_EVENTS = 1000, _agentSessionDeps;
@@ -26204,7 +26531,7 @@ var init_agent_session_deps = __esm(() => {
26204
26531
  now: () => Date.now(),
26205
26532
  setTimeout: (fn, ms) => setTimeout(fn, ms),
26206
26533
  clearTimeout: (handle) => clearTimeout(handle),
26207
- randomUUID: () => randomUUID7(),
26534
+ randomUUID: () => randomUUID8(),
26208
26535
  isDirectory: async (path) => {
26209
26536
  try {
26210
26537
  return (await stat6(path)).isDirectory();
@@ -26213,7 +26540,7 @@ var init_agent_session_deps = __esm(() => {
26213
26540
  }
26214
26541
  },
26215
26542
  makeScratchRoot: () => mkdtemp(join21(tmpdir4(), "nax-agent-session-")),
26216
- removeScratchRoot: (dir) => rm4(dir, { recursive: true, force: true }),
26543
+ removeScratchRoot: (dir) => rm5(dir, { recursive: true, force: true }),
26217
26544
  controlEventCap: MAX_UNDELIVERED_CONTROL_EVENTS
26218
26545
  };
26219
26546
  });
@@ -26365,7 +26692,8 @@ var init_native_backend_options = __esm(() => {
26365
26692
  commandInterceptor: exports_external.custom(hasMethods2(["intercept"]), "must implement intercept").optional()
26366
26693
  }).optional(),
26367
26694
  bashApproval: exports_external.enum(["raw", "gated", "escalate"]).optional(),
26368
- allowUnsandboxed: exports_external.boolean().optional()
26695
+ allowUnsandboxed: exports_external.boolean().optional(),
26696
+ carryHistoryAcrossModels: exports_external.boolean().optional()
26369
26697
  });
26370
26698
  });
26371
26699
 
@@ -26710,6 +27038,7 @@ var init_src = __esm(() => {
26710
27038
  init_stderr_tail();
26711
27039
  init_redact();
26712
27040
  init_nax_error();
27041
+ init_terminal_auth();
26713
27042
  init_native();
26714
27043
  init_turn_event_emitter();
26715
27044
  init_permissions();
@@ -29273,7 +29602,7 @@ function commandSpecIncludes(spec, fragment) {
29273
29602
  return normalizeCommandSpec(spec).some((entry) => entry.includes(fragment));
29274
29603
  }
29275
29604
  function replaceInCommandSpec(spec, searchValue, replacement) {
29276
- const replace = (command) => command.replaceAll(searchValue, replacement);
29605
+ const replace = (command) => command.replaceAll(searchValue, () => replacement);
29277
29606
  return typeof spec === "string" ? replace(spec) : spec.map(replace);
29278
29607
  }
29279
29608
  function renderCommandSpec(spec) {
@@ -29335,7 +29664,9 @@ __export(exports_internal, {
29335
29664
  NATIVE_AGENT_NAME: () => NATIVE_AGENT_NAME,
29336
29665
  NativeSessionUnsupportedError: () => NativeSessionUnsupportedError,
29337
29666
  NaxError: () => NaxError,
29667
+ PLAIN_STYLE: () => PLAIN_STYLE,
29338
29668
  PROVIDER_ID_RE: () => PROVIDER_ID_RE,
29669
+ PromptCancelledError: () => PromptCancelledError,
29339
29670
  READ_CEILING: () => READ_CEILING,
29340
29671
  RESERVED_TOOL_NAMES: () => RESERVED_TOOL_NAMES,
29341
29672
  SANDBOX_GLOB_CHARS: () => SANDBOX_GLOB_CHARS,
@@ -29352,6 +29683,7 @@ __export(exports_internal, {
29352
29683
  _agentSessionDeps: () => _agentSessionDeps,
29353
29684
  _approvalsTaintDeps: () => _approvalsTaintDeps,
29354
29685
  _argvExecDeps: () => _argvExecDeps,
29686
+ _atomicWriteDeps: () => _atomicWriteDeps,
29355
29687
  _authDeps: () => _authDeps,
29356
29688
  _bashToolDeps: () => _bashToolDeps,
29357
29689
  _clientDeps: () => _clientDeps,
@@ -29366,6 +29698,7 @@ __export(exports_internal, {
29366
29698
  _grepDeps: () => _grepDeps,
29367
29699
  _instructionAccessDeps: () => _instructionAccessDeps,
29368
29700
  _launcherDeps: () => _launcherDeps,
29701
+ _openUrlDeps: () => _openUrlDeps,
29369
29702
  _policyInputDeps: () => _policyInputDeps,
29370
29703
  _probeDeps: () => _probeDeps,
29371
29704
  _repositoryInstructionDeps: () => _repositoryInstructionDeps,
@@ -29383,6 +29716,7 @@ __export(exports_internal, {
29383
29716
  _spillDeps: () => _spillDeps,
29384
29717
  _srtBackendDeps: () => _srtBackendDeps,
29385
29718
  _systemOneClientDeps: () => _systemOneClientDeps,
29719
+ _terminalPromptDeps: () => _terminalPromptDeps,
29386
29720
  abortableSleep: () => abortableSleep,
29387
29721
  adaptProviderTool: () => adaptProviderTool,
29388
29722
  advertisedSchemaBytes: () => advertisedSchemaBytes,
@@ -29443,6 +29777,7 @@ __export(exports_internal, {
29443
29777
  createRunCommandTool: () => createRunCommandTool,
29444
29778
  createSpinBreaker: () => createSpinBreaker,
29445
29779
  createSrtBackend: () => createSrtBackend,
29780
+ createTerminalAuthInteraction: () => createTerminalAuthInteraction,
29446
29781
  createToolAuditSink: () => createToolAuditSink,
29447
29782
  createTurnAccumulator: () => createTurnAccumulator,
29448
29783
  credentialFaultCode: () => credentialFaultCode,
@@ -29508,6 +29843,7 @@ __export(exports_internal, {
29508
29843
  listNaxEntries: () => listNaxEntries,
29509
29844
  listStoredProviders: () => listStoredProviders,
29510
29845
  loadTranscript: () => loadTranscript,
29846
+ loginProviderIds: () => loginProviderIds,
29511
29847
  markNativeTurnOutcome: () => markNativeTurnOutcome,
29512
29848
  maskForPrompt: () => maskForPrompt,
29513
29849
  mcpRuleAdmits: () => mcpRuleAdmits,
@@ -29521,6 +29857,7 @@ __export(exports_internal, {
29521
29857
  normalizeExec: () => normalizeExec,
29522
29858
  normalizeManagerBinary: () => normalizeManagerBinary,
29523
29859
  openNativeSession: () => openNativeSession,
29860
+ openUrl: () => openUrl,
29524
29861
  parseGitlinks: () => parseGitlinks,
29525
29862
  parseModelSpec: () => parseModelSpec,
29526
29863
  parseNativeModel: () => parseNativeModel,
@@ -29533,6 +29870,9 @@ __export(exports_internal, {
29533
29870
  prepareCompaction: () => prepareCompaction,
29534
29871
  probeSandbox: () => probeSandbox,
29535
29872
  probeSandboxOnce: () => probeSandboxOnce,
29873
+ promptForLine: () => promptForLine,
29874
+ promptForSecret: () => promptForSecret,
29875
+ promptForSelect: () => promptForSelect,
29536
29876
  providerAttachesTo: () => providerAttachesTo,
29537
29877
  providersWithoutCredentials: () => providersWithoutCredentials,
29538
29878
  pruneRetainedTranscripts: () => pruneRetainedTranscripts,
@@ -29598,6 +29938,7 @@ __export(exports_internal, {
29598
29938
  setAgentLogger: () => setAgentLogger,
29599
29939
  shellQuoteArg: () => shellQuoteArg,
29600
29940
  shouldCompact: () => shouldCompact,
29941
+ spawnDetached: () => spawnDetached,
29601
29942
  spillRelativePath: () => spillRelativePath,
29602
29943
  spinTerminalNotice: () => spinTerminalNotice,
29603
29944
  strayCommonDirTripwire: () => strayCommonDirTripwire,
@@ -29632,6 +29973,7 @@ __export(exports_internal, {
29632
29973
  withPathFileLock: () => withPathFileLock,
29633
29974
  wrapExternalHandler: () => wrapExternalHandler,
29634
29975
  writeApprovalsFile: () => writeApprovalsFile,
29976
+ writeFileAtomic: () => writeFileAtomic,
29635
29977
  writeSpill: () => writeSpill,
29636
29978
  writeTool: () => writeTool
29637
29979
  });
@@ -29662,6 +30004,7 @@ var init_internal = __esm(() => {
29662
30004
  init_spin_breaker();
29663
30005
  init_agent_output_env();
29664
30006
  init_argv_exec();
30007
+ init_atomic_write();
29665
30008
  init_file_lock();
29666
30009
  init_git_env();
29667
30010
  init_git_exec();
@@ -29694,6 +30037,7 @@ var init_internal = __esm(() => {
29694
30037
  init_permissions();
29695
30038
  init_sandbox();
29696
30039
  init_agent_session_deps();
30040
+ init_terminal_auth();
29697
30041
  init_credential_read_deny();
29698
30042
  init_git();
29699
30043
  init_git_commit();
@@ -30348,9 +30692,9 @@ ${c.gray(JSON.stringify(filtered, null, 2))}`;
30348
30692
  }
30349
30693
  function buildDefaultMeta(data, mode, level) {
30350
30694
  const meta3 = [];
30351
- const identity = [data.agentName, data.model].filter((v) => typeof v === "string" && v.length > 0);
30352
- if (identity.length > 0)
30353
- meta3.push(`${EMOJI.agent} ${identity.join("\xB7")}`);
30695
+ const identity2 = [data.agentName, data.model].filter((v) => typeof v === "string" && v.length > 0);
30696
+ if (identity2.length > 0)
30697
+ meta3.push(`${EMOJI.agent} ${identity2.join("\xB7")}`);
30354
30698
  if (typeof data.phaseIndex === "number" && typeof data.totalPhases === "number") {
30355
30699
  meta3.push(`${data.phaseIndex}/${data.totalPhases}`);
30356
30700
  }
@@ -31022,7 +31366,7 @@ var init_inert_bash_stages = __esm(() => {
31022
31366
 
31023
31367
  // src/utils/json-file.ts
31024
31368
  import { existsSync as existsSync5 } from "fs";
31025
- import { rename as rename2, unlink as unlink3 } from "fs/promises";
31369
+ import { rename as rename3, unlink as unlink3 } from "fs/promises";
31026
31370
  async function loadJsonFile(path, context = "json-file") {
31027
31371
  if (!existsSync5(path)) {
31028
31372
  return null;
@@ -31057,7 +31401,7 @@ async function atomicWriteText(path, content, context = "json-file") {
31057
31401
  const tmpPath = `${path}.tmp-${crypto.randomUUID()}`;
31058
31402
  try {
31059
31403
  await Bun.write(tmpPath, content);
31060
- await rename2(tmpPath, path);
31404
+ await rename3(tmpPath, path);
31061
31405
  } catch (err) {
31062
31406
  await unlink3(tmpPath).catch(() => {});
31063
31407
  const logger = getLogger2();
@@ -31370,6 +31714,12 @@ function _applyFinishAutoFlowShim(conf, warn = defaultConfigWarn) {
31370
31714
  }
31371
31715
  return { ...conf, finish: { ...lifted, ...explicitFinish } };
31372
31716
  }
31717
+ function _applyTddStrategyAliasShim(conf) {
31718
+ const tdd = conf.tdd;
31719
+ if (!tdd || typeof tdd !== "object" || tdd.strategy !== "tdd-simple")
31720
+ return conf;
31721
+ return { ...conf, tdd: { ...tdd, strategy: "simple" } };
31722
+ }
31373
31723
  function applyConfigCompatShims(conf, logger, dedupe) {
31374
31724
  const log = dedupe.wrapLogger(logger);
31375
31725
  const warn = dedupe.warn;
@@ -31386,6 +31736,7 @@ function applyConfigCompatShims(conf, logger, dedupe) {
31386
31736
  out = _applyFinishAutoFlowShim(out, warn);
31387
31737
  out = _applyRemovedOptimizerKeysShim(out, warn);
31388
31738
  out = _applyRemovedCrossPackageDepthShim(out, warn);
31739
+ out = _applyTddStrategyAliasShim(out);
31389
31740
  return out;
31390
31741
  }
31391
31742
  var SECURITY_SENSITIVE_KEY_PATHS, REMOVED_FINISH_KEYS;
@@ -32913,7 +33264,7 @@ var init_schemas_execution = __esm(() => {
32913
33264
  });
32914
33265
  TddConfigSchema = exports_external.object({
32915
33266
  maxRetries: exports_external.number().int().nonnegative(),
32916
- strategy: exports_external.enum(["auto", "strict", "lite", "off"]).default("auto"),
33267
+ strategy: exports_external.enum(["auto", "strict", "lite", "simple", "off"]).default("auto"),
32917
33268
  sessionTiers: exports_external.object({
32918
33269
  testWriter: ConfiguredModelSchema.default("fast"),
32919
33270
  verifier: ConfiguredModelSchema.default("fast"),
@@ -36201,18 +36552,20 @@ var init_spec_structure_grammar = __esm(() => {
36201
36552
  function declaredStoryIds(lines) {
36202
36553
  const ids = new Set;
36203
36554
  let inScope = false;
36204
- let inSkippedSubsection = false;
36555
+ let skipLevel = 0;
36205
36556
  for (const line of lines) {
36206
36557
  if (/^##\s/.test(line)) {
36207
36558
  inScope = /^##\s+(Stories|Acceptance Criteria)\b/i.test(line);
36208
- inSkippedSubsection = false;
36559
+ skipLevel = 0;
36209
36560
  continue;
36210
36561
  }
36211
36562
  if (!inScope)
36212
36563
  continue;
36213
- if (/^#{3,6}\s/.test(line))
36214
- inSkippedSubsection = GROUPED_PATH_SUBSECTION.test(line);
36215
- if (inSkippedSubsection)
36564
+ const level = /^(#{3,6})\s/.exec(line)?.[1]?.length;
36565
+ if (level !== undefined && (skipLevel === 0 || level <= skipLevel)) {
36566
+ skipLevel = GROUPED_PATH_SUBSECTION.test(line) ? level : 0;
36567
+ }
36568
+ if (skipLevel > 0)
36216
36569
  continue;
36217
36570
  const heading = /^#{1,6}\s+(US-\d+)\b/i.exec(line);
36218
36571
  if (heading?.[1]) {
@@ -36259,9 +36612,9 @@ function extractSpecStructure(specContent) {
36259
36612
  const stories = [];
36260
36613
  const warnings = [];
36261
36614
  for (const declarations of collectStoryDeclarations(specContent)) {
36262
- const read = readDeclarations(declarations);
36263
- stories.push(read.story);
36264
- warnings.push(...read.warnings);
36615
+ const read2 = readDeclarations(declarations);
36616
+ stories.push(read2.story);
36617
+ warnings.push(...read2.warnings);
36265
36618
  }
36266
36619
  return { stories, warnings };
36267
36620
  }
@@ -38089,11 +38442,15 @@ import path2 from "path";
38089
38442
  async function acceptCommand(options) {
38090
38443
  const logger = getLogger2();
38091
38444
  const { feature, override, reason } = options;
38092
- if (!override.match(/^AC-\d+$/i)) {
38093
- logger.error("cli", "Invalid AC ID format", { override, expected: "AC-1, AC-2, etc." });
38094
- throw new NaxError("Invalid AC ID format", "INVALID_AC_ID", { override, expected: "AC-1, AC-2, etc." });
38095
- }
38096
- const acId = override.toUpperCase();
38445
+ const expected = "AC-1, or <package>::AC-1 (package path relative to the repo root)";
38446
+ const match = /^(?:(.+)::)?(AC-\d+)$/i.exec(override);
38447
+ const pkg = match?.[1];
38448
+ const ac = match?.[2];
38449
+ if (!ac || pkg === "") {
38450
+ logger.error("cli", "Invalid AC ID format", { override, expected });
38451
+ throw new NaxError("Invalid AC ID format", "INVALID_AC_ID", { override, expected });
38452
+ }
38453
+ const acId = pkg === undefined ? ac.toUpperCase() : `${pkg}::${ac.toUpperCase()}`;
38097
38454
  const projectDirResult = findProjectDir(process.cwd());
38098
38455
  if (!projectDirResult) {
38099
38456
  logger.error("cli", "Not in a nax project directory", { hint: "Run 'nax init' first" });
@@ -38494,7 +38851,7 @@ var package_default;
38494
38851
  var init_package = __esm(() => {
38495
38852
  package_default = {
38496
38853
  name: "@nathapp/nax",
38497
- version: "0.83.6",
38854
+ version: "0.84.0",
38498
38855
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
38499
38856
  type: "module",
38500
38857
  bin: {
@@ -38506,7 +38863,7 @@ var init_package = __esm(() => {
38506
38863
  typecheck: "bun x tsc --noEmit && bun x tsc --noEmit -p tsconfig.test.json",
38507
38864
  lint: "bun run lint:biome && bun run lint:checks",
38508
38865
  "lint:biome": "bun scripts/quiet-run.ts bun x biome check --error-on-warnings --diagnostic-level=warn src/ bin/ test/ scripts/",
38509
- "lint:checks": "bun run check:no-real-global-nax && bun run check:feature-dir-ssot && bun run check:alias-internals && bun run check:import-cycles && bun run check:nax-error && bun run check:logger-storyid && bun run check:log-format-layering && bun run check:file-sizes && bun run check:complexity && bun run check:no-control-bytes && bun run check:review-prompts && bun run check:nax-ai-imports && bun run check:sandbox-imports && bun run check:git-spawn-env && bun run check:bundle-externals && bun run check:op-tool-capability && bun run check:nax-artifacts-untracked && bun run check:story-workdir-access && bun run check:package-frame-derivation && bun run check:config-profile-threading && bun run check:dispatch-field-forwarding && bun run check:bash-dispatch-ask && bun run check:test-satellites && bun run check:nax-ai-pin && bun run check:package-boundaries && bun run check:usage-vocabulary",
38866
+ "lint:checks": "bun run check:no-real-global-nax && bun run check:feature-dir-ssot && bun run check:alias-internals && bun run check:import-cycles && bun run check:nax-error && bun run check:logger-storyid && bun run check:log-format-layering && bun run check:file-sizes && bun run check:complexity && bun run check:no-control-bytes && bun run check:review-prompts && bun run check:nax-ai-imports && bun run check:sandbox-imports && bun run check:git-spawn-env && bun run check:bundle-externals && bun run check:op-tool-capability && bun run check:nax-artifacts-untracked && bun run check:story-workdir-access && bun run check:package-frame-derivation && bun run check:config-profile-threading && bun run check:dispatch-field-forwarding && bun run check:bash-dispatch-ask && bun run check:test-satellites && bun run check:lockstep && bun run check:package-boundaries && bun run check:usage-vocabulary",
38510
38867
  "lint:json": "bun x biome check --error-on-warnings src/ bin/ test/ scripts/ --reporter json && bun run check:nax-error 1>&2 && bun run check:logger-storyid 1>&2",
38511
38868
  "lint:fix": "bun x biome check --write src/ bin/ test/ scripts/",
38512
38869
  "check:no-real-global-nax": "bun run ../repo-tooling/scripts/check-no-real-global-nax.ts",
@@ -38528,7 +38885,7 @@ var init_package = __esm(() => {
38528
38885
  "check:no-control-bytes": "bun run ../repo-tooling/scripts/check-no-control-bytes.ts",
38529
38886
  "check:review-prompts": "bun run scripts/check-review-prompts-generated.ts",
38530
38887
  "check:nax-ai-imports": "bun run ../repo-tooling/scripts/check-nax-ai-imports.ts",
38531
- "check:nax-ai-pin": "bun run scripts/check-nax-ai-pin.ts",
38888
+ "check:lockstep": "bun run ../repo-tooling/scripts/check-lockstep.ts",
38532
38889
  "check:sandbox-imports": "bun run ../repo-tooling/scripts/check-sandbox-imports.ts",
38533
38890
  "check:git-spawn-env": "bun run ../repo-tooling/scripts/check-git-spawn-env.ts",
38534
38891
  "check:bundle-externals": "bun run scripts/check-bundle-externals.ts",
@@ -38542,7 +38899,6 @@ var init_package = __esm(() => {
38542
38899
  "check:bash-dispatch-ask": "bun run scripts/check-bash-dispatch-ask.ts",
38543
38900
  "check:rules-drift": "bun scripts/quiet-run.ts bun run scripts/check-rules-drift.ts",
38544
38901
  "gen:review-prompts": "bun run scripts/generate-review-prompts.ts",
38545
- release: "bun scripts/release.ts",
38546
38902
  test: "AGENT=1 bun run scripts/run-tests.ts",
38547
38903
  "test:verbose": "bun run scripts/run-tests.ts",
38548
38904
  "test:bail": "AGENT=1 bun run scripts/run-tests.ts --bail",
@@ -38552,7 +38908,7 @@ var init_package = __esm(() => {
38552
38908
  "test:unit": "bun test ./test/unit/ --timeout=60000",
38553
38909
  "test:integration": "bun test ./test/integration/ --timeout=60000",
38554
38910
  "test:ui": "bun test ./test/ui/ --timeout=60000",
38555
- "test:e2e": "timeout -k 5s 180s bun test test/e2e/ --timeout=60000",
38911
+ "test:e2e": "bun run scripts/run-tests.ts --e2e",
38556
38912
  "test:coverage": "bun run ../repo-tooling/scripts/check-coverage.ts --require-all-files",
38557
38913
  "test:coverage:report": "bun run ../repo-tooling/scripts/check-coverage.ts --require-all-files --report",
38558
38914
  "test:coverage:update": "bun run ../repo-tooling/scripts/check-coverage.ts --require-all-files --update-baseline",
@@ -38582,7 +38938,7 @@ var init_package = __esm(() => {
38582
38938
  "@agentclientprotocol/sdk": "~1.7.0",
38583
38939
  "@anthropic-ai/sandbox-runtime": "0.0.77",
38584
38940
  "@modelcontextprotocol/sdk": "^1.30.0",
38585
- "@nathapp/nax-ai": "0.1.16",
38941
+ "@nathapp/nax-ai": "0.84.0",
38586
38942
  chalk: "^5.6.2",
38587
38943
  commander: "^13.1.0",
38588
38944
  ink: "^6.7.0",
@@ -38812,9 +39168,9 @@ function cancellableDelay(ms, signal) {
38812
39168
  signal?.addEventListener("abort", onAbort, { once: true });
38813
39169
  });
38814
39170
  }
38815
- var spawn;
39171
+ var spawn2;
38816
39172
  var init_bun_deps = __esm(() => {
38817
- spawn = Bun.spawn;
39173
+ spawn2 = Bun.spawn;
38818
39174
  });
38819
39175
 
38820
39176
  // src/agents/retry/failure-policy.ts
@@ -39781,19 +40137,24 @@ function isTestFileByPatterns(filePath2, patterns) {
39781
40137
  }
39782
40138
  function globsToPathspec(patterns) {
39783
40139
  const result = [];
39784
- const seen = new Set;
40140
+ const add = (pathspec) => {
40141
+ if (!result.includes(pathspec))
40142
+ result.push(pathspec);
40143
+ };
39785
40144
  for (const pattern of patterns) {
39786
- const lastStar = pattern.lastIndexOf("*");
39787
- if (lastStar === -1)
40145
+ const segment = pattern.slice(pattern.lastIndexOf("/") + 1);
40146
+ const firstStar = segment.indexOf("*");
40147
+ if (firstStar === -1)
39788
40148
  continue;
39789
- const suffix = pattern.slice(lastStar + 1);
39790
- if (suffix.length === 0)
40149
+ if (firstStar > 0) {
40150
+ add(`:!${segment}`);
40151
+ add(`:!*/${segment}`);
39791
40152
  continue;
39792
- const pathspec = `:!*${suffix}`;
39793
- if (!seen.has(pathspec)) {
39794
- result.push(pathspec);
39795
- seen.add(pathspec);
39796
40153
  }
40154
+ const suffix = segment.slice(segment.lastIndexOf("*") + 1);
40155
+ if (suffix.length === 0 || BARE_EXTENSION.test(suffix))
40156
+ continue;
40157
+ add(`:!*${suffix}`);
39797
40158
  }
39798
40159
  return result;
39799
40160
  }
@@ -39807,9 +40168,24 @@ function extractTestDirs(globs) {
39807
40168
  }
39808
40169
  return [...dirs];
39809
40170
  }
39810
- var DEFAULT_TEST_FILE_PATTERNS, DEFAULT_SCAN_TEST_DIRS, DEFAULT_TS_DERIVE_SUFFIXES, DEFAULT_SEPARATED_TEST_DIRS;
40171
+ function extractWholeTestDirs(globs) {
40172
+ const dirs = new Set;
40173
+ for (const glob2 of globs) {
40174
+ const segments = glob2.split("/");
40175
+ const first = segments[0];
40176
+ if (!first || first.includes("*") || segments.length < 2)
40177
+ continue;
40178
+ const filename = segments[segments.length - 1] ?? "";
40179
+ const coversEverything = filename === "*" || filename === "**" || filename.startsWith("*") && BARE_EXTENSION.test(filename.slice(1));
40180
+ if (coversEverything)
40181
+ dirs.add(first);
40182
+ }
40183
+ return [...dirs];
40184
+ }
40185
+ var DEFAULT_TEST_FILE_PATTERNS, BARE_EXTENSION, DEFAULT_SCAN_TEST_DIRS, DEFAULT_TS_DERIVE_SUFFIXES, DEFAULT_SEPARATED_TEST_DIRS;
39811
40186
  var init_conventions = __esm(() => {
39812
40187
  DEFAULT_TEST_FILE_PATTERNS = Object.freeze(["test/**/*.test.ts"]);
40188
+ BARE_EXTENSION = /^\.[A-Za-z0-9]+$/;
39813
40189
  DEFAULT_SCAN_TEST_DIRS = Object.freeze([
39814
40190
  "test",
39815
40191
  "tests",
@@ -40292,29 +40668,11 @@ async function detectFromFileScan(workdir) {
40292
40668
  const filtered = files.filter((f) => !isExcluded(f));
40293
40669
  if (filtered.length === 0)
40294
40670
  return null;
40295
- const counts = {};
40296
- for (const suffix of CANDIDATE_SUFFIXES) {
40297
- counts[suffix] = 0;
40298
- }
40299
- for (const file2 of filtered) {
40300
- for (const suffix of CANDIDATE_SUFFIXES) {
40301
- if (file2.endsWith(suffix)) {
40302
- counts[suffix] = (counts[suffix] ?? 0) + 1;
40303
- }
40304
- }
40305
- }
40306
40671
  const totalFiles = filtered.length;
40307
- const patterns = [];
40308
- for (const suffix of CANDIDATE_SUFFIXES) {
40309
- const count = counts[suffix] ?? 0;
40310
- if (count === 0)
40311
- continue;
40312
- if (count >= MIN_COUNT_THRESHOLD || count / totalFiles >= MIN_FRACTION_THRESHOLD) {
40313
- const glob2 = SUFFIX_TO_GLOB[suffix];
40314
- if (glob2)
40315
- patterns.push(glob2);
40316
- }
40317
- }
40672
+ const patterns = CANDIDATES.filter((candidate) => {
40673
+ const count = filtered.filter((file2) => candidate.matches(file2)).length;
40674
+ return count > 0 && (count >= MIN_COUNT_THRESHOLD || count / totalFiles >= MIN_FRACTION_THRESHOLD);
40675
+ }).map((candidate) => candidate.glob);
40318
40676
  if (patterns.length === 0)
40319
40677
  return null;
40320
40678
  return {
@@ -40323,41 +40681,35 @@ async function detectFromFileScan(workdir) {
40323
40681
  patterns
40324
40682
  };
40325
40683
  }
40326
- var DRAIN_TIMEOUT2, EXCLUDED_DIR_PREFIXES, MIN_COUNT_THRESHOLD = 5, MIN_FRACTION_THRESHOLD = 0.1, FILE_SCAN_GIT_TIMEOUT_MS = 4000, FILE_SCAN_DRAIN_TIMEOUT_MS = 2000, CANDIDATE_SUFFIXES, SUFFIX_TO_GLOB, _fileScanDeps;
40684
+ var DRAIN_TIMEOUT2, EXCLUDED_DIR_PREFIXES, MIN_COUNT_THRESHOLD = 5, MIN_FRACTION_THRESHOLD = 0.1, FILE_SCAN_GIT_TIMEOUT_MS = 4000, FILE_SCAN_DRAIN_TIMEOUT_MS = 2000, bySuffix = (suffix) => ({
40685
+ glob: `**/*${suffix}`,
40686
+ matches: (path3) => path3.endsWith(suffix)
40687
+ }), byBasenamePrefix = (prefix, extension) => ({
40688
+ glob: `**/${prefix}*${extension}`,
40689
+ matches: (path3) => {
40690
+ const base = path3.slice(path3.lastIndexOf("/") + 1);
40691
+ return base.startsWith(prefix) && base.endsWith(extension) && base.length > prefix.length + extension.length;
40692
+ }
40693
+ }), CANDIDATES, _fileScanDeps;
40327
40694
  var init_file_scan = __esm(() => {
40328
40695
  init_internal();
40329
40696
  DRAIN_TIMEOUT2 = Symbol("drain-timeout");
40330
40697
  EXCLUDED_DIR_PREFIXES = ["node_modules/", "dist/", "build/", ".nax/", "coverage/", ".git/"];
40331
- CANDIDATE_SUFFIXES = [
40332
- ".test.ts",
40333
- ".test.tsx",
40334
- ".test.js",
40335
- ".test.jsx",
40336
- ".spec.ts",
40337
- ".spec.tsx",
40338
- ".spec.js",
40339
- ".spec.jsx",
40340
- ".e2e-spec.ts",
40341
- ".e2e-spec.js",
40342
- "_test.go",
40343
- "_test.py",
40344
- "test_.py"
40698
+ CANDIDATES = [
40699
+ bySuffix(".test.ts"),
40700
+ bySuffix(".test.tsx"),
40701
+ bySuffix(".test.js"),
40702
+ bySuffix(".test.jsx"),
40703
+ bySuffix(".spec.ts"),
40704
+ bySuffix(".spec.tsx"),
40705
+ bySuffix(".spec.js"),
40706
+ bySuffix(".spec.jsx"),
40707
+ bySuffix(".e2e-spec.ts"),
40708
+ bySuffix(".e2e-spec.js"),
40709
+ bySuffix("_test.go"),
40710
+ bySuffix("_test.py"),
40711
+ byBasenamePrefix("test_", ".py")
40345
40712
  ];
40346
- SUFFIX_TO_GLOB = {
40347
- ".test.ts": "**/*.test.ts",
40348
- ".test.tsx": "**/*.test.tsx",
40349
- ".test.js": "**/*.test.js",
40350
- ".test.jsx": "**/*.test.jsx",
40351
- ".spec.ts": "**/*.spec.ts",
40352
- ".spec.tsx": "**/*.spec.tsx",
40353
- ".spec.js": "**/*.spec.js",
40354
- ".spec.jsx": "**/*.spec.jsx",
40355
- ".e2e-spec.ts": "**/*.e2e-spec.ts",
40356
- ".e2e-spec.js": "**/*.e2e-spec.js",
40357
- "_test.go": "**/*_test.go",
40358
- "_test.py": "**/*_test.py",
40359
- "test_.py": "**/test_*.py"
40360
- };
40361
40713
  _fileScanDeps = {
40362
40714
  spawn: Bun.spawn,
40363
40715
  killProcessGroup,
@@ -40839,7 +41191,7 @@ async function detectFromCargoToml(workdir) {
40839
41191
  const path3 = `${workdir}/Cargo.toml`;
40840
41192
  if (!await _frameworkDefaultsDeps.fileExists(path3))
40841
41193
  return null;
40842
- return { type: "manifest", framework: "rust", path: path3, patterns: ["tests/**/*.rs", "src/**/*.rs"] };
41194
+ return { type: "manifest", framework: "rust", path: path3, patterns: ["tests/**/*.rs"] };
40843
41195
  }
40844
41196
  async function detectFromPyprojectDeps(workdir) {
40845
41197
  const path3 = `${workdir}/pyproject.toml`;
@@ -41751,7 +42103,7 @@ function resolveReviewExcludePatterns(userExplicit, resolved) {
41751
42103
  const result = new Set;
41752
42104
  for (const p of resolved.pathspec)
41753
42105
  result.add(p);
41754
- for (const d of resolved.testDirs)
42106
+ for (const d of extractWholeTestDirs(resolved.globs))
41755
42107
  result.add(`:!${d}/`);
41756
42108
  for (const d of WELL_KNOWN_TEST_DIRS2)
41757
42109
  result.add(`:!${d}/`);
@@ -42371,6 +42723,7 @@ __export(exports_test_runners, {
42371
42723
  detectTestFilePatterns: () => detectTestFilePatterns,
42372
42724
  discoverWorkspacePackages: () => discoverWorkspacePackages,
42373
42725
  extractTestDirs: () => extractTestDirs,
42726
+ extractWholeTestDirs: () => extractWholeTestDirs,
42374
42727
  findPackageDir: () => findPackageDir,
42375
42728
  formatFailureSummary: () => formatFailureSummary,
42376
42729
  globsToPathspec: () => globsToPathspec,
@@ -42422,7 +42775,7 @@ var init_nax_owned_paths = __esm(() => {
42422
42775
  });
42423
42776
 
42424
42777
  // src/review/diff-utils.ts
42425
- var {spawn: spawn2 } = globalThis.Bun;
42778
+ var {spawn: spawn3 } = globalThis.Bun;
42426
42779
  async function resolveNaxIgnorePathspecExcludes(workdir, options) {
42427
42780
  if (options?.naxIgnoreIndex)
42428
42781
  return options.naxIgnoreIndex.toPathspecExcludes(options.packageDir);
@@ -42575,7 +42928,7 @@ var init_diff_utils = __esm(() => {
42575
42928
  init_path_filters();
42576
42929
  ALWAYS_EXCLUDED = [...NAX_OWNED_REVIEW_EXCLUDE_PATHSPECS];
42577
42930
  _diffUtilsDeps = {
42578
- spawn: spawn2,
42931
+ spawn: spawn3,
42579
42932
  isGitRefValid,
42580
42933
  getMergeBase,
42581
42934
  timeoutMs: GIT_TIMEOUT_MS
@@ -44648,7 +45001,7 @@ var init_hermetic = __esm(() => {
44648
45001
  });
44649
45002
 
44650
45003
  // src/prompts/sections/protocol-region.ts
44651
- import { randomUUID as randomUUID8 } from "crypto";
45004
+ import { randomUUID as randomUUID9 } from "crypto";
44652
45005
  function call(tool, input) {
44653
45006
  return `${tool} ${JSON.stringify(input)}`;
44654
45007
  }
@@ -44765,7 +45118,7 @@ var KIND_PATTERN, NONCE, PROTOCOL_REGION_MARKER_PREFIX = "<!--nax:", REGION, OWN
44765
45118
  var init_protocol_region = __esm(() => {
44766
45119
  init_errors4();
44767
45120
  KIND_PATTERN = /^[a-z][a-z-]*$/;
44768
- NONCE = randomUUID8().slice(0, 8);
45121
+ NONCE = randomUUID9().slice(0, 8);
44769
45122
  REGION = new RegExp(`<!--nax:([a-z][a-z-]*):([a-f0-9]+) (\\{.*?\\})-->\\n` + `((?:(?!<!--nax:[a-z][a-z-]*:[a-f0-9]+ )[\\s\\S])*?)` + `<!--\\/nax:\\1-->\\n?`, "g");
44770
45123
  OWN_OPEN = new RegExp(`<!--nax:[a-z][a-z-]*:${NONCE} `, "g");
44771
45124
  REGISTRY = {
@@ -47324,7 +47677,7 @@ ${section}`);
47324
47677
  const failingFiles = Array.from(new Set(findings.map((f) => f.file).filter((f) => !!f)));
47325
47678
  if (failingFiles.length > 0) {
47326
47679
  const perFileLines = failingFiles.map((file2) => {
47327
- const scopedCmd = opts.testScopedTemplate ? opts.testScopedTemplate.replace("{{files}}", file2) : `${opts.testCommand} ${file2}`;
47680
+ const scopedCmd = opts.testScopedTemplate ? opts.testScopedTemplate.replaceAll("{{files}}", () => file2) : `${opts.testCommand} ${file2}`;
47328
47681
  if (opts.testScopedTemplate && opts.fileScopeCommandName) {
47329
47682
  return ` ${wrapAffordance("run-test", { command: opts.fileScopeCommandName, files: file2 }, scopedCmd)}`;
47330
47683
  }
@@ -47754,7 +48107,7 @@ ${output}
47754
48107
  const cmd = testCommand ?? "";
47755
48108
  const failingFiles = Array.from(new Set(failures.map((f) => f.file)));
47756
48109
  const testCommands = failingFiles.map((file2) => {
47757
- const scopedCmd = testScopedTemplate ? testScopedTemplate.replace("{{files}}", file2) : cmd ? `${cmd} ${file2}` : file2;
48110
+ const scopedCmd = testScopedTemplate ? testScopedTemplate.replaceAll("{{files}}", () => file2) : cmd ? `${cmd} ${file2}` : file2;
47758
48111
  return ` ${scopedCmd}`;
47759
48112
  }).join(`
47760
48113
  `);
@@ -49845,7 +50198,7 @@ async function loadCanonicalRules(workdir, options = {}) {
49845
50198
  if (allViolations.length > 0) {
49846
50199
  throw new NeutralityLintError(allViolations);
49847
50200
  }
49848
- rules.sort((a, b) => (a.priority ?? FRONTMATTER_PRIORITY_DEFAULT) - (b.priority ?? FRONTMATTER_PRIORITY_DEFAULT) || (a.id ?? a.fileName).localeCompare(b.id ?? b.fileName));
50201
+ rules.sort((a, b) => (a.priority ?? FRONTMATTER_PRIORITY_DEFAULT) - (b.priority ?? FRONTMATTER_PRIORITY_DEFAULT) || byCodePoint(a.id ?? a.fileName, b.id ?? b.fileName));
49849
50202
  logger.debug("canonical-loader", "Scanned canonical rules store", {
49850
50203
  fileCount: rules.length,
49851
50204
  files: rules.map((r) => r.path)
@@ -49879,6 +50232,7 @@ async function loadCanonicalRules(workdir, options = {}) {
49879
50232
  }
49880
50233
  var CANONICAL_RULES_DIR = ".nax/rules", _canonicalLoaderDeps, NEUTRALITY_RULES, DEFAULT_CANONICAL_RULES_BUDGET_TOKENS = 8192, RULES_BUDGET_WARNING_RATIO = 0.75, RULE_ALLOW_MARKER, NeutralityLintError, RulesFrontmatterLintError;
49881
50234
  var init_canonical_loader = __esm(() => {
50235
+ init_internal();
49882
50236
  init_errors4();
49883
50237
  init_logger2();
49884
50238
  init_rules_frontmatter();
@@ -50557,7 +50911,7 @@ function applySectionBudget(sections2, budgetTokens) {
50557
50911
  overageTokens: totalTokens
50558
50912
  };
50559
50913
  }
50560
- const sorted = [...sections2].sort((a, b) => (a.priority ?? FRONTMATTER_PRIORITY_DEFAULT) - (b.priority ?? FRONTMATTER_PRIORITY_DEFAULT) || ownerIdentifier(a).localeCompare(ownerIdentifier(b)) || a.ordinal - b.ordinal);
50914
+ const sorted = [...sections2].sort((a, b) => (a.priority ?? FRONTMATTER_PRIORITY_DEFAULT) - (b.priority ?? FRONTMATTER_PRIORITY_DEFAULT) || byCodePoint(ownerIdentifier(a), ownerIdentifier(b)) || a.ordinal - b.ordinal);
50561
50915
  const kept = [];
50562
50916
  const droppedIds = [];
50563
50917
  const closedOwners = new Set;
@@ -50589,6 +50943,7 @@ function applySectionBudget(sections2, budgetTokens) {
50589
50943
  };
50590
50944
  }
50591
50945
  var init_rule_budget = __esm(() => {
50946
+ init_internal();
50592
50947
  init_rules_frontmatter();
50593
50948
  });
50594
50949
 
@@ -51687,7 +52042,7 @@ var init_loop_handler_forwarding = __esm(() => {
51687
52042
  });
51688
52043
 
51689
52044
  // src/session/manager-deps.ts
51690
- import { randomUUID as randomUUID9 } from "crypto";
52045
+ import { randomUUID as randomUUID10 } from "crypto";
51691
52046
  import { mkdir as mkdir14 } from "fs/promises";
51692
52047
  import { isAbsolute as isAbsolute13, join as join39, relative as relative18, sep as sep13 } from "path";
51693
52048
  function resolveProjectDirFromScratchDir(scratchDir) {
@@ -51729,7 +52084,7 @@ var init_manager_deps = __esm(() => {
51729
52084
  _sessionManagerDeps = {
51730
52085
  now: () => new Date().toISOString(),
51731
52086
  nowMs: () => Date.now(),
51732
- uuid: () => randomUUID9(),
52087
+ uuid: () => randomUUID10(),
51733
52088
  sessionScratchDir: (projectDir, featureName, sessionId) => join39(featureDir(projectDir, featureName), "sessions", sessionId),
51734
52089
  writeDescriptor: async (scratchDir, descriptor, projectDir) => {
51735
52090
  await mkdir14(scratchDir, { recursive: true });
@@ -52220,10 +52575,9 @@ class SessionManager {
52220
52575
  const reuse = decideReuse(liveHandle, this._findByName(name), opts);
52221
52576
  if (liveHandle && reuse === "reuse")
52222
52577
  return liveHandle;
52223
- if (liveHandle && reuse === "close-then-reopen") {
52224
- await this.closeSession(liveHandle);
52225
- this._busySessions.add(name);
52226
- } else if (liveHandle)
52578
+ if (liveHandle && reuse === "close-then-reopen")
52579
+ await this.closeSession(liveHandle, true);
52580
+ else if (liveHandle)
52227
52581
  this._liveHandles.delete(name);
52228
52582
  const adapter = this._getAdapter(opts.agentName);
52229
52583
  if (!adapter) {
@@ -52252,6 +52606,7 @@ class SessionManager {
52252
52606
  this._liveHandles.set(name, handle);
52253
52607
  const protocolIds = handle.protocolIds ?? NULL_PROTOCOL_IDS;
52254
52608
  if (!existingDescriptor) {
52609
+ this._cancelledSessions.delete(name);
52255
52610
  const created = this.create({
52256
52611
  role: opts.role ?? "main",
52257
52612
  agent: opts.agentName,
@@ -52287,7 +52642,7 @@ class SessionManager {
52287
52642
  });
52288
52643
  return handle;
52289
52644
  }
52290
- async closeSession(handle) {
52645
+ async closeSession(handle, keepOpenGuard = false) {
52291
52646
  const desc = this._findByName(handle.id);
52292
52647
  const adapter = this._getAdapter(handle.agentName);
52293
52648
  this._liveHandles.delete(handle.id);
@@ -52305,7 +52660,8 @@ class SessionManager {
52305
52660
  if (desc && desc.state === "RUNNING") {
52306
52661
  this.transition(desc.id, "COMPLETED");
52307
52662
  }
52308
- this._busySessions.delete(handle.id);
52663
+ if (!keepOpenGuard)
52664
+ this._busySessions.delete(handle.id);
52309
52665
  this._cancelledSessions.delete(handle.id);
52310
52666
  this._watchdogCancels.clear(handle.id);
52311
52667
  }
@@ -52414,7 +52770,7 @@ function recordAgentHandoff(sessionManager, sessionName, newAgent, reason) {
52414
52770
  }
52415
52771
 
52416
52772
  // src/session/scratch-purge.ts
52417
- import { mkdir as mkdir15, rename as rename3, rm as rm5 } from "fs/promises";
52773
+ import { mkdir as mkdir15, rename as rename4, rm as rm6 } from "fs/promises";
52418
52774
  import { dirname as dirname15, join as join40 } from "path";
52419
52775
  async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
52420
52776
  const sessionsDir = join40(featureDir(projectDir, featureName), "sessions");
@@ -52466,10 +52822,10 @@ var init_scratch_purge = __esm(() => {
52466
52822
  },
52467
52823
  fileExists: (path5) => Bun.file(path5).exists(),
52468
52824
  readFile: (path5) => Bun.file(path5).text(),
52469
- remove: (path5) => rm5(path5, { recursive: true, force: true }),
52825
+ remove: (path5) => rm6(path5, { recursive: true, force: true }),
52470
52826
  move: async (src, dest) => {
52471
52827
  await mkdir15(dirname15(dest), { recursive: true });
52472
- await rename3(src, dest);
52828
+ await rename4(src, dest);
52473
52829
  },
52474
52830
  now: () => Date.now()
52475
52831
  };
@@ -52557,7 +52913,7 @@ var init_session2 = __esm(() => {
52557
52913
  var DEFAULT_MAX_TOKENS_PER_CALL = 2048;
52558
52914
 
52559
52915
  // src/context/fragments/store.ts
52560
- import { mkdir as mkdir17, rm as rm6, stat as stat7 } from "fs/promises";
52916
+ import { mkdir as mkdir17, rm as rm7, stat as stat7 } from "fs/promises";
52561
52917
  import { dirname as dirname17, join as join41 } from "path";
52562
52918
  function fragmentPath(projectDir, featureId, storyId) {
52563
52919
  validatePathSegment(storyId, "storyId");
@@ -52671,7 +53027,7 @@ var init_store = __esm(() => {
52671
53027
  return files.sort(byCodePoint);
52672
53028
  },
52673
53029
  removeFile: async (path5) => {
52674
- await rm6(path5, { force: true });
53030
+ await rm7(path5, { force: true });
52675
53031
  }
52676
53032
  };
52677
53033
  });
@@ -54335,6 +54691,27 @@ var init_manifest_purge = __esm(() => {
54335
54691
  };
54336
54692
  });
54337
54693
 
54694
+ // src/context/engine/digest-carrier.ts
54695
+ function boostedDigestChunk(request) {
54696
+ const digest = request.priorStageDigest?.trim();
54697
+ return digest && (request.planDigestBoost ?? 1) > 1 ? request.priorStageDigest : undefined;
54698
+ }
54699
+ function resolveDigestCarrier(request, boostedDigest, packed) {
54700
+ if (boostedDigest === undefined) {
54701
+ return { digestAsChunk: false, renderPriorDigest: request.priorStageDigest, manifestRequest: request };
54702
+ }
54703
+ const landed = packed.some((c) => c.providerId === PLAN_DIGEST_PROVIDER_ID);
54704
+ if (landed) {
54705
+ return {
54706
+ digestAsChunk: true,
54707
+ renderPriorDigest: undefined,
54708
+ manifestRequest: { ...request, priorStageDigest: undefined }
54709
+ };
54710
+ }
54711
+ return { digestAsChunk: false, renderPriorDigest: request.priorStageDigest, manifestRequest: request };
54712
+ }
54713
+ var PLAN_DIGEST_PROVIDER_ID = "plan-digest";
54714
+
54338
54715
  // src/context/engine/orchestrator-rebuild-helpers.ts
54339
54716
  function toContextChunk(packed) {
54340
54717
  const providerId = packed.providerId ?? packed.id.split(":")[0] ?? "unknown";
@@ -54538,7 +54915,7 @@ var init_render = __esm(() => {
54538
54915
  });
54539
54916
 
54540
54917
  // src/context/engine/rebuild.ts
54541
- import { randomUUID as randomUUID10 } from "crypto";
54918
+ import { randomUUID as randomUUID11 } from "crypto";
54542
54919
  function toPackedChunks(prior, newAgentId, targetAgentId) {
54543
54920
  const priorAgentForNeutralize = prior.agentId ?? "";
54544
54921
  return prior.chunks.map((c) => {
@@ -54665,7 +55042,7 @@ var init_rebuild = __esm(() => {
54665
55042
  init_render();
54666
55043
  init_scratch_neutralizer();
54667
55044
  DEFAULT_REBUILD_DEPS = {
54668
- uuid: () => randomUUID10(),
55045
+ uuid: () => randomUUID11(),
54669
55046
  getLogger: () => getLogger2()
54670
55047
  };
54671
55048
  });
@@ -54679,13 +55056,13 @@ function roleMultiplier(chunkRoles, callerRole) {
54679
55056
  return ROLE_MISMATCH_WEIGHT;
54680
55057
  }
54681
55058
  function scoreChunk(chunk, callerRole, minScore = MIN_SCORE, stale = false, providerWeights) {
54682
- const rm7 = roleMultiplier(chunk.role, callerRole);
54683
- const roleFiltered = rm7 === 0;
55059
+ const rm8 = roleMultiplier(chunk.role, callerRole);
55060
+ const roleFiltered = rm8 === 0;
54684
55061
  const kindWeight = KIND_WEIGHTS[chunk.kind] ?? 0.5;
54685
55062
  const isStale = chunk.staleCandidate === true || stale;
54686
55063
  const freshnessMultiplier = isStale ? chunk.scoreMultiplier ?? STALENESS_PENALTY : 1;
54687
55064
  const effectivenessMultiplier = chunk.providerId !== undefined ? providerWeights?.[chunk.providerId] ?? 1 : 1;
54688
- const score = chunk.rawScore * rm7 * kindWeight * freshnessMultiplier * effectivenessMultiplier;
55065
+ const score = chunk.rawScore * rm8 * kindWeight * freshnessMultiplier * effectivenessMultiplier;
54689
55066
  const belowMinScore = !roleFiltered && score < minScore;
54690
55067
  return { ...chunk, score, roleFiltered, belowMinScore };
54691
55068
  }
@@ -54711,7 +55088,7 @@ var init_scoring = __esm(() => {
54711
55088
  });
54712
55089
 
54713
55090
  // src/context/engine/orchestrator.ts
54714
- import { createHash as createHash10, randomUUID as randomUUID11 } from "crypto";
55091
+ import { createHash as createHash10, randomUUID as randomUUID12 } from "crypto";
54715
55092
  function buildPullToolDescriptors(stageToolNames, pullConfig) {
54716
55093
  if (!pullConfig?.enabled || stageToolNames.length === 0)
54717
55094
  return [];
@@ -54720,6 +55097,28 @@ function buildPullToolDescriptors(stageToolNames, pullConfig) {
54720
55097
  const override = configured2 !== undefined && configured2 !== DEFAULT_MAX_CALLS_PER_SESSION ? configured2 : undefined;
54721
55098
  return stageToolNames.filter((name) => allowed.length === 0 || allowed.includes(name)).map((name) => PULL_TOOL_REGISTRY[name]).filter((d) => d !== undefined).map((d) => ({ ...d, maxCallsPerSession: override ?? d.maxCallsPerSession }));
54722
55099
  }
55100
+ function appendPlanDigestChunk(raw, results, digest, boost) {
55101
+ const hash2 = createHash10("sha256").update(digest).digest("hex").slice(0, 8);
55102
+ const tokens = Math.ceil(digest.length / 4);
55103
+ raw.push({
55104
+ id: `${PLAN_DIGEST_PROVIDER_ID}:${hash2}`,
55105
+ providerId: PLAN_DIGEST_PROVIDER_ID,
55106
+ kind: "session",
55107
+ scope: "session",
55108
+ role: ["all"],
55109
+ content: digest,
55110
+ tokens,
55111
+ rawScore: 0.9 * boost
55112
+ });
55113
+ results.push({
55114
+ providerId: PLAN_DIGEST_PROVIDER_ID,
55115
+ status: "ok",
55116
+ source: undefined,
55117
+ chunkCount: 1,
55118
+ durationMs: 0,
55119
+ tokensProduced: tokens
55120
+ });
55121
+ }
54723
55122
  async function fetchWithTimeout(provider, request, timeoutMs = PROVIDER_FETCH_TIMEOUT_MS) {
54724
55123
  const controller = new AbortController;
54725
55124
  let handle;
@@ -54731,7 +55130,7 @@ async function fetchWithTimeout(provider, request, timeoutMs = PROVIDER_FETCH_TI
54731
55130
  reject(new Error(`Provider "${provider.id}" timed out`));
54732
55131
  }, timeoutMs);
54733
55132
  });
54734
- const fetchPromise = provider.fetch(request, controller.signal).then((result) => result, (err) => {
55133
+ const fetchPromise = (async () => provider.fetch(request, controller.signal))().then((result) => result, (err) => {
54735
55134
  if (timedOut) {
54736
55135
  return new Promise(() => {});
54737
55136
  }
@@ -54797,8 +55196,9 @@ class ContextOrchestrator {
54797
55196
  }
54798
55197
  const profileBudget = agentProfile.caps.preferredPromptTokens;
54799
55198
  const stageCeiling = Math.min(request.budgetTokens, profileBudget, request.availableBudgetTokens ?? Number.POSITIVE_INFINITY);
55199
+ const boostedDigest = boostedDigestChunk(request);
54800
55200
  const trimmedPriorDigest = request.priorStageDigest?.trim();
54801
- const priorDigestTokens = trimmedPriorDigest ? Math.ceil(trimmedPriorDigest.length / 4) : 0;
55201
+ const priorDigestTokens = boostedDigest === undefined && trimmedPriorDigest ? Math.ceil(trimmedPriorDigest.length / 4) : 0;
54802
55202
  let effectiveBudgetTokens = Math.max(0, stageCeiling - DIGEST_RESERVE_TOKENS - priorDigestTokens - FIXED_RENDER_OVERHEAD_TOKENS);
54803
55203
  const providerSourceMap = request.providerIds === undefined ? buildProviderSourceMap(stageConfig.providerIds, request.extraProviderIds ?? []) : new Map;
54804
55204
  const allowedIds = request.providerIds ?? [...providerSourceMap.keys()];
@@ -54876,28 +55276,8 @@ class ContextOrchestrator {
54876
55276
  }));
54877
55277
  const allRaw = fetchResults.flatMap(({ provider, result }) => result.chunks.map((c) => enrichRaw(c, provider.id)));
54878
55278
  const providerResults = fetchResults.map(({ providerStatus }) => providerStatus);
54879
- if (request.priorStageDigest && (request.planDigestBoost ?? 1) > 1) {
54880
- const boost = request.planDigestBoost ?? 1;
54881
- const hash2 = createHash10("sha256").update(request.priorStageDigest).digest("hex").slice(0, 8);
54882
- const tokens = Math.ceil(request.priorStageDigest.length / 4);
54883
- allRaw.push({
54884
- id: `plan-digest:${hash2}`,
54885
- providerId: "plan-digest",
54886
- kind: "session",
54887
- scope: "session",
54888
- role: ["all"],
54889
- content: request.priorStageDigest,
54890
- tokens,
54891
- rawScore: 0.9 * boost
54892
- });
54893
- providerResults.push({
54894
- providerId: "plan-digest",
54895
- status: "ok",
54896
- source: undefined,
54897
- chunkCount: 1,
54898
- durationMs: 0,
54899
- tokensProduced: tokens
54900
- });
55279
+ if (boostedDigest !== undefined) {
55280
+ appendPlanDigestChunk(allRaw, providerResults, boostedDigest, request.planDigestBoost ?? 1);
54901
55281
  }
54902
55282
  assertUniqueChunkIds(allRaw, request);
54903
55283
  const allPullTools = agentProfile.caps.supportsToolCalls ? buildPullToolDescriptors(stageConfig.pullToolNames ?? [], request.pullConfig) : [];
@@ -54929,7 +55309,10 @@ class ContextOrchestrator {
54929
55309
  excludedNonFloorChunkCount: budgetExcludedIds.length
54930
55310
  });
54931
55311
  }
54932
- const renderOptions = { priorStageDigest: request.priorStageDigest };
55312
+ const digestCarrier = resolveDigestCarrier(request, boostedDigest, packed);
55313
+ const renderOptions = {
55314
+ priorStageDigest: digestCarrier.renderPriorDigest
55315
+ };
54933
55316
  const pushMarkdown = request.agentId !== undefined ? renderForAgent(packed, request.agentId, renderOptions) : renderChunks(packed, renderOptions);
54934
55317
  const digest = buildDigest(packed);
54935
55318
  const dTokens = digestTokens(digest);
@@ -54939,7 +55322,7 @@ class ContextOrchestrator {
54939
55322
  const staleIds = new Set(scored.filter((c) => c.staleCandidate === true).map((c) => c.id));
54940
55323
  const manifest = buildManifest({
54941
55324
  requestId,
54942
- request,
55325
+ request: digestCarrier.manifestRequest,
54943
55326
  packed,
54944
55327
  usedTokens,
54945
55328
  digestTokens: dTokens,
@@ -55014,7 +55397,7 @@ var init_orchestrator = __esm(() => {
55014
55397
  init_stage_config();
55015
55398
  _orchestratorDeps = {
55016
55399
  now: () => Date.now(),
55017
- uuid: () => randomUUID11(),
55400
+ uuid: () => randomUUID12(),
55018
55401
  getLogger: getLogger2,
55019
55402
  rebuild
55020
55403
  };
@@ -55607,7 +55990,7 @@ __export(exports_tracker, {
55607
55990
  saveRunMetrics: () => saveRunMetrics,
55608
55991
  toFallbackHops: () => toFallbackHops
55609
55992
  });
55610
- import { rename as rename4 } from "fs/promises";
55993
+ import { rename as rename5 } from "fs/promises";
55611
55994
  import path5 from "path";
55612
55995
  function tokensFromSnapshot(snapshot, divisor = 1) {
55613
55996
  if (!snapshot || snapshot.tokenUsageCount !== snapshot.callCount)
@@ -55829,7 +56212,7 @@ async function loadExistingMetricsOrQuarantine(metricsPath) {
55829
56212
  } catch (err) {
55830
56213
  const quarantinePath = `${metricsPath}.corrupt-${new Date().toISOString().replace(/[:.]/g, "-")}`;
55831
56214
  try {
55832
- await rename4(metricsPath, quarantinePath);
56215
+ await rename5(metricsPath, quarantinePath);
55833
56216
  } catch (renameErr) {
55834
56217
  getLogger2().warn("metrics", "Failed to quarantine corrupt metrics.json \u2014 leaving in place", {
55835
56218
  metricsPath,
@@ -56558,7 +56941,7 @@ function stripCarriageReturn(line) {
56558
56941
  return line.endsWith("\r") ? line.slice(0, -1) : line;
56559
56942
  }
56560
56943
  var CONSEQUENCE = "? nax will run this project's plugins, hooks, MCP servers and test commands.", _trustPromptDeps;
56561
- var init_prompt = __esm(() => {
56944
+ var init_prompt2 = __esm(() => {
56562
56945
  _trustPromptDeps = {
56563
56946
  ask: async (question) => {
56564
56947
  process.stderr.write(question);
@@ -56597,8 +56980,8 @@ var init_registry4 = __esm(() => {
56597
56980
  });
56598
56981
 
56599
56982
  // src/trust/store.ts
56600
- import { randomUUID as randomUUID12 } from "crypto";
56601
- import { mkdir as mkdir18, rename as rename5, stat as stat8, unlink as unlink4, writeFile as writeFile10 } from "fs/promises";
56983
+ import { randomUUID as randomUUID13 } from "crypto";
56984
+ import { mkdir as mkdir18, rename as rename6, stat as stat8, unlink as unlink4, writeFile as writeFile9 } from "fs/promises";
56602
56985
  import { dirname as dirname20, isAbsolute as isAbsolute15, join as join48 } from "path";
56603
56986
  function trustStorePath() {
56604
56987
  return join48(globalConfigDir(), "trust.json");
@@ -56663,18 +57046,18 @@ async function removeFromStore(storePath, normalized) {
56663
57046
  return { outcome: "removed", entry };
56664
57047
  }
56665
57048
  async function readFoldersForWrite(storePath) {
56666
- const read = await readTrustStore();
56667
- if (read.state === "unparseable") {
56668
- throw trustStoreUnreadableError(storePath, read.reason);
57049
+ const read2 = await readTrustStore();
57050
+ if (read2.state === "unparseable") {
57051
+ throw trustStoreUnreadableError(storePath, read2.reason);
56669
57052
  }
56670
- return read.state === "ok" ? read.file.folders : [];
57053
+ return read2.state === "ok" ? read2.file.folders : [];
56671
57054
  }
56672
57055
  async function writeTrustStore(storePath, file2) {
56673
- const tempPath = `${storePath}.${process.pid}.${randomUUID12()}.tmp`;
57056
+ const tempPath = `${storePath}.${process.pid}.${randomUUID13()}.tmp`;
56674
57057
  try {
56675
- await writeFile10(tempPath, `${JSON.stringify(file2, null, 2)}
57058
+ await writeFile9(tempPath, `${JSON.stringify(file2, null, 2)}
56676
57059
  `, { mode: 384 });
56677
- await rename5(tempPath, storePath);
57060
+ await rename6(tempPath, storePath);
56678
57061
  } finally {
56679
57062
  await unlink4(tempPath).catch(() => {});
56680
57063
  }
@@ -56746,7 +57129,7 @@ var _trustGateDeps;
56746
57129
  var init_gate = __esm(() => {
56747
57130
  init_errors4();
56748
57131
  init_match();
56749
- init_prompt();
57132
+ init_prompt2();
56750
57133
  init_registry4();
56751
57134
  init_store2();
56752
57135
  _trustGateDeps = {
@@ -56759,7 +57142,7 @@ var init_gate = __esm(() => {
56759
57142
  var init_trust = __esm(() => {
56760
57143
  init_gate();
56761
57144
  init_match();
56762
- init_prompt();
57145
+ init_prompt2();
56763
57146
  init_registry4();
56764
57147
  init_store2();
56765
57148
  });
@@ -56970,8 +57353,8 @@ var init_version = __esm(() => {
56970
57353
  NAX_AI_VERSION = CATALOG_VERSION;
56971
57354
  NAX_COMMIT = (() => {
56972
57355
  try {
56973
- if (/^[0-9a-f]{6,10}$/.test("eab634ebe"))
56974
- return "eab634ebe";
57356
+ if (/^[0-9a-f]{6,10}$/.test("eeb09eb"))
57357
+ return "eeb09eb";
56975
57358
  } catch {}
56976
57359
  try {
56977
57360
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -58178,9 +58561,9 @@ async function readProjectIdentity(projectKey) {
58178
58561
  return null;
58179
58562
  }
58180
58563
  }
58181
- async function writeProjectIdentity(projectKey, identity) {
58564
+ async function writeProjectIdentity(projectKey, identity2) {
58182
58565
  const p = identityPath(projectKey);
58183
- await Bun.write(p, JSON.stringify(identity, null, 2));
58566
+ await Bun.write(p, JSON.stringify(identity2, null, 2));
58184
58567
  }
58185
58568
  async function claimProjectIdentity(projectKey, workdir, remoteUrl) {
58186
58569
  const existing2 = await readProjectIdentity(projectKey);
@@ -68748,7 +69131,7 @@ var require_cross_spawn = __commonJS(function(exports, module) {
68748
69131
  var cp = __require("child_process");
68749
69132
  var parse6 = require_parse();
68750
69133
  var enoent = require_enoent();
68751
- function spawn3(command, args, options) {
69134
+ function spawn4(command, args, options) {
68752
69135
  const parsed = parse6(command, args, options);
68753
69136
  const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
68754
69137
  enoent.hookChildProcess(spawned, parsed);
@@ -68760,8 +69143,8 @@ var require_cross_spawn = __commonJS(function(exports, module) {
68760
69143
  result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
68761
69144
  return result;
68762
69145
  }
68763
- module.exports = spawn3;
68764
- module.exports.spawn = spawn3;
69146
+ module.exports = spawn4;
69147
+ module.exports.spawn = spawn4;
68765
69148
  module.exports.sync = spawnSync;
68766
69149
  module.exports._parse = parse6;
68767
69150
  module.exports._enoent = enoent;
@@ -69061,7 +69444,7 @@ var init_client4 = __esm(() => {
69061
69444
 
69062
69445
  // src/mcp/lock.ts
69063
69446
  import { createHash as createHash17 } from "crypto";
69064
- import { mkdir as mkdir22, readFile as readFile9, writeFile as writeFile11 } from "fs/promises";
69447
+ import { mkdir as mkdir22, readFile as readFile9, writeFile as writeFile10 } from "fs/promises";
69065
69448
  import { join as join55 } from "path";
69066
69449
  function mcpLockPath(projectRoot) {
69067
69450
  return join55(projectRoot, PROJECT_NAX_DIR, MCP_LOCK_FILENAME);
@@ -69093,7 +69476,7 @@ async function writeMcpLock(projectRoot, lock) {
69093
69476
  Object.fromEntries(Object.entries(tools2).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0))
69094
69477
  ]));
69095
69478
  await mkdir22(join55(projectRoot, PROJECT_NAX_DIR), { recursive: true });
69096
- await writeFile11(mcpLockPath(projectRoot), `${JSON.stringify({ version: 1, servers }, null, 2)}
69479
+ await writeFile10(mcpLockPath(projectRoot), `${JSON.stringify({ version: 1, servers }, null, 2)}
69097
69480
  `);
69098
69481
  }
69099
69482
  function applyLock(locked, discovered) {
@@ -69262,9 +69645,9 @@ function createMcpProviders(args) {
69262
69645
  return [];
69263
69646
  for (const [id] of servers)
69264
69647
  validateProviderId(id);
69265
- const read = args.readLock ?? readMcpLock;
69648
+ const read2 = args.readLock ?? readMcpLock;
69266
69649
  let lockPromise;
69267
- const lock = () => lockPromise ??= read(args.projectRoot);
69650
+ const lock = () => lockPromise ??= read2(args.projectRoot);
69268
69651
  return servers.map(([serverId, server]) => {
69269
69652
  return {
69270
69653
  id: serverId,
@@ -69314,7 +69697,7 @@ var init_provider = __esm(() => {
69314
69697
  });
69315
69698
 
69316
69699
  // src/mcp/rollup.ts
69317
- import { mkdir as mkdir23, writeFile as writeFile12 } from "fs/promises";
69700
+ import { mkdir as mkdir23, writeFile as writeFile11 } from "fs/promises";
69318
69701
  import { join as join56 } from "path";
69319
69702
  function buildMcpRollup(args) {
69320
69703
  const byServer = new Map;
@@ -69347,7 +69730,7 @@ async function writeMcpRollup(outputDir, rollup) {
69347
69730
  return;
69348
69731
  const dir = join56(outputDir, "mcp");
69349
69732
  await mkdir23(dir, { recursive: true });
69350
- await writeFile12(join56(dir, `${rollup.runId}-servers.json`), `${JSON.stringify(rollup, null, 2)}
69733
+ await writeFile11(join56(dir, `${rollup.runId}-servers.json`), `${JSON.stringify(rollup, null, 2)}
69351
69734
  `);
69352
69735
  }
69353
69736
  var init_rollup = () => {};
@@ -69510,7 +69893,7 @@ var init_executor = __esm(() => {
69510
69893
  init_bun_deps();
69511
69894
  init_trust();
69512
69895
  init_internal();
69513
- _executorDeps = { spawn };
69896
+ _executorDeps = { spawn: spawn2 };
69514
69897
  DRAIN_TIMEOUT3 = Symbol("drain-timeout");
69515
69898
  DEFAULT_STRIP_ENV_VARS = [];
69516
69899
  });
@@ -70576,7 +70959,7 @@ ${r.output}`).join(""),
70576
70959
  }
70577
70960
 
70578
70961
  // src/quality/runner.ts
70579
- var {spawn: spawn4 } = globalThis.Bun;
70962
+ var {spawn: spawn5 } = globalThis.Bun;
70580
70963
  function createDrainDeadline(deadlineMs) {
70581
70964
  let timeoutId;
70582
70965
  const promise3 = new Promise((resolve31) => {
@@ -70750,7 +71133,7 @@ var init_runner = __esm(() => {
70750
71133
  init_trust();
70751
71134
  init_executor();
70752
71135
  _qualityRunnerDeps = {
70753
- spawn: spawn4
71136
+ spawn: spawn5
70754
71137
  };
70755
71138
  });
70756
71139
 
@@ -73076,7 +73459,7 @@ var init_runners = __esm(() => {
73076
73459
 
73077
73460
  // src/verification/test-baseline.ts
73078
73461
  import { existsSync as existsSync10 } from "fs";
73079
- import { mkdir as mkdir24, readdir as readdir6, rm as rm7 } from "fs/promises";
73462
+ import { mkdir as mkdir24, readdir as readdir6, rm as rm8 } from "fs/promises";
73080
73463
  import { join as join65 } from "path";
73081
73464
  function runBaselinePath(root, featureId) {
73082
73465
  return join65(featureDir(root, featureId), RUN_BASELINE_FILENAME);
@@ -73106,7 +73489,7 @@ async function clearStoryBaselines(root, featureId) {
73106
73489
  return [];
73107
73490
  throw err;
73108
73491
  });
73109
- await Promise.all(entries.filter((entry) => entry.isDirectory()).map((entry) => rm7(join65(storiesDir, entry.name, STORY_BASELINE_FILENAME), { force: true })));
73492
+ await Promise.all(entries.filter((entry) => entry.isDirectory()).map((entry) => rm8(join65(storiesDir, entry.name, STORY_BASELINE_FILENAME), { force: true })));
73110
73493
  }
73111
73494
  async function resolveStoryBaseline(root, featureId, storyId, executionMode) {
73112
73495
  if (executionMode === "parallel") {
@@ -74378,12 +74761,13 @@ async function performAdversarialReground(turn, firstParsed, drops, ctx) {
74378
74761
  const secondParsed = validateAdversarialShape(tryParseLLMJson(secondTurn.output));
74379
74762
  const costUsd = (turn.estimatedCostUsd ?? 0) + (secondTurn.estimatedCostUsd ?? 0);
74380
74763
  const dropCount = drops.length;
74381
- if (!secondParsed) {
74382
- return {
74383
- ...turn,
74384
- output: withRepromptMarker(turn.output, { dropCount, outcome: "parse-failed", costUsd })
74385
- };
74386
- }
74764
+ const keepFirstTurn = (outcome) => ({
74765
+ ...turn,
74766
+ output: withRepromptMarker(turn.output, { dropCount, outcome, costUsd }),
74767
+ estimatedCostUsd: costUsd
74768
+ });
74769
+ if (!secondParsed)
74770
+ return keepFirstTurn("parse-failed");
74387
74771
  const { accepted: secondAccepted } = filterByAcQuote(secondParsed.findings, acceptanceCriteria);
74388
74772
  const secondBlocking = secondAccepted.filter((f) => isBlockingSeverity(f.severity, threshold));
74389
74773
  if (secondBlocking.length > 0) {
@@ -74411,10 +74795,7 @@ async function performAdversarialReground(turn, firstParsed, drops, ctx) {
74411
74795
  estimatedCostUsd: costUsd
74412
74796
  };
74413
74797
  }
74414
- return {
74415
- ...turn,
74416
- output: withRepromptMarker(turn.output, { dropCount, outcome: "still-dropped", costUsd })
74417
- };
74798
+ return keepFirstTurn("still-dropped");
74418
74799
  }
74419
74800
  async function maybeRepromptForInspection(turn, parsed, rawObject, ctx) {
74420
74801
  if (ctx.input.adversarialConfig.demandInspectionTrail === false)
@@ -77560,7 +77941,7 @@ ${detail}`,
77560
77941
  `);
77561
77942
  }
77562
77943
  var CLASSIFIER;
77563
- var init_prompt2 = __esm(() => {
77944
+ var init_prompt3 = __esm(() => {
77564
77945
  CLASSIFIER = [
77565
77946
  "Mark a finding for human judgment \u2014 and only such a finding \u2014 by adding a",
77566
77947
  "`Judgment: yes \u2014 <why>` line to its block. Use it when the finding is a spec",
@@ -77575,7 +77956,7 @@ var init_prompt2 = __esm(() => {
77575
77956
  var init_review = __esm(() => {
77576
77957
  init_audit_gaps();
77577
77958
  init_parse6();
77578
- init_prompt2();
77959
+ init_prompt3();
77579
77960
  });
77580
77961
 
77581
77962
  // src/operations/finish-fix.ts
@@ -77943,7 +78324,7 @@ var init_scope = __esm(() => {
77943
78324
  });
77944
78325
 
77945
78326
  // src/review/fix-review/tree-snapshot/index.ts
77946
- import { mkdtemp as mkdtemp2, rm as rm8 } from "fs/promises";
78327
+ import { mkdtemp as mkdtemp2, rm as rm9 } from "fs/promises";
77947
78328
  import { tmpdir as tmpdir6 } from "os";
77948
78329
  import { join as join69 } from "path";
77949
78330
  async function runGit(args, cwd, indexOverlay, timeoutMs = _treeSnapshotDeps.gitTimeoutMs) {
@@ -78030,7 +78411,7 @@ var init_tree_snapshot = __esm(() => {
78030
78411
  spawn: typedSpawn,
78031
78412
  gitTimeoutMs: TREE_SNAPSHOT_GIT_TIMEOUT_MS,
78032
78413
  mkdtemp: mkdtemp2,
78033
- rm: rm8,
78414
+ rm: rm9,
78034
78415
  tmpdir: tmpdir6
78035
78416
  };
78036
78417
  });
@@ -78266,7 +78647,7 @@ var init_new_package_setup = __esm(() => {
78266
78647
  init_trust();
78267
78648
  init_command_argv();
78268
78649
  registry4 = new WeakMap;
78269
- _newPackageSetupDeps = { spawn };
78650
+ _newPackageSetupDeps = { spawn: spawn2 };
78270
78651
  });
78271
78652
 
78272
78653
  // src/operations/gate-cwd.ts
@@ -78707,7 +79088,7 @@ var init_isolation2 = __esm(() => {
78707
79088
  init_errors4();
78708
79089
  init_logger2();
78709
79090
  init_test_runners();
78710
- _isolationDeps = { spawn, timeoutMs: GIT_TIMEOUT_MS3 };
79091
+ _isolationDeps = { spawn: spawn2, timeoutMs: GIT_TIMEOUT_MS3 };
78711
79092
  SRC_PATTERNS = [/^src\//, /^lib\//, /^packages\//];
78712
79093
  });
78713
79094
 
@@ -79147,33 +79528,62 @@ function parseHeaderPath(rawLine, precededByMinusHeader) {
79147
79528
  function isMinusHeader(rawLine) {
79148
79529
  return rawLine.startsWith("--- ");
79149
79530
  }
79150
- function extractDiffLineRanges(diff) {
79151
- const ranges = new Map;
79152
- if (!diff)
79153
- return ranges;
79154
- let currentPath = null;
79531
+ function bodyLineCost(line) {
79532
+ if (line === "" || line.startsWith(" "))
79533
+ return { old: 1, new: 1 };
79534
+ if (line.startsWith("-"))
79535
+ return { old: 1, new: 0 };
79536
+ if (line.startsWith("+"))
79537
+ return { old: 0, new: 1 };
79538
+ if (line.startsWith("\\"))
79539
+ return { old: 0, new: 0 };
79540
+ return null;
79541
+ }
79542
+ function* scanDiff(diff) {
79543
+ let oldLeft = 0;
79544
+ let newLeft = 0;
79155
79545
  let prevWasMinusHeader = false;
79156
79546
  for (const rawLine of diff.split(/\r?\n/)) {
79547
+ if (oldLeft > 0 || newLeft > 0) {
79548
+ const cost = bodyLineCost(rawLine);
79549
+ if (cost !== null) {
79550
+ oldLeft = Math.max(0, oldLeft - cost.old);
79551
+ newLeft = Math.max(0, newLeft - cost.new);
79552
+ continue;
79553
+ }
79554
+ oldLeft = 0;
79555
+ newLeft = 0;
79556
+ }
79157
79557
  const wasMinusHeader = prevWasMinusHeader;
79158
79558
  prevWasMinusHeader = isMinusHeader(rawLine);
79159
79559
  if (rawLine.startsWith(HEADER_PREFIX_NOPREFIX)) {
79160
79560
  const path9 = parseHeaderPath(rawLine, wasMinusHeader);
79161
79561
  if (path9 !== null || rawLine.startsWith(HEADER_PREFIX) || wasMinusHeader)
79162
- currentPath = path9;
79562
+ yield { kind: "header", path: path9 };
79163
79563
  continue;
79164
79564
  }
79165
- if (!currentPath)
79166
- continue;
79167
79565
  const match = HUNK_REGEX.exec(rawLine);
79168
79566
  if (!match)
79169
79567
  continue;
79170
- const newStart = Number(match[3]);
79171
- const newCount = match[4] === undefined ? 1 : Number(match[4]);
79172
- if (newCount <= 0)
79568
+ oldLeft = match[2] === undefined ? 1 : Number(match[2]);
79569
+ newLeft = match[4] === undefined ? 1 : Number(match[4]);
79570
+ yield { kind: "hunk", start: Number(match[3]), count: newLeft };
79571
+ }
79572
+ }
79573
+ function extractDiffLineRanges(diff) {
79574
+ const ranges = new Map;
79575
+ if (!diff)
79576
+ return ranges;
79577
+ let currentPath = null;
79578
+ for (const event of scanDiff(diff)) {
79579
+ if (event.kind === "header") {
79580
+ currentPath = event.path;
79581
+ continue;
79582
+ }
79583
+ if (!currentPath || event.count <= 0)
79173
79584
  continue;
79174
79585
  const entry = ranges.get(currentPath) ?? [];
79175
- entry.push({ start: newStart, end: newStart + newCount - 1 });
79176
- ranges.set(currentPath, entry);
79586
+ ranges.set(currentPath, [...entry, { start: event.start, end: event.start + event.count - 1 }]);
79177
79587
  }
79178
79588
  return ranges;
79179
79589
  }
@@ -80108,12 +80518,13 @@ async function performSemanticReground(turn, firstParsed, drops, ctx) {
80108
80518
  const secondParsed = validateLLMShape(tryParseLLMJson(secondTurn.output));
80109
80519
  const costUsd = (turn.estimatedCostUsd ?? 0) + (secondTurn.estimatedCostUsd ?? 0);
80110
80520
  const dropCount = drops.length;
80111
- if (!secondParsed) {
80112
- return {
80113
- ...turn,
80114
- output: withRepromptMarker2(turn.output, { dropCount, outcome: "parse-failed", costUsd })
80115
- };
80116
- }
80521
+ const keepFirstTurn = (outcome) => ({
80522
+ ...turn,
80523
+ output: withRepromptMarker2(turn.output, { dropCount, outcome, costUsd }),
80524
+ estimatedCostUsd: costUsd
80525
+ });
80526
+ if (!secondParsed)
80527
+ return keepFirstTurn("parse-failed");
80117
80528
  const { accepted: secondAccepted } = filterByAcGroundingMinimal(secondParsed.findings, acceptanceCriteria);
80118
80529
  const secondBlocking = secondAccepted.filter((f) => isBlockingSeverity(f.severity, threshold));
80119
80530
  if (secondBlocking.length > 0) {
@@ -80141,10 +80552,7 @@ async function performSemanticReground(turn, firstParsed, drops, ctx) {
80141
80552
  estimatedCostUsd: costUsd
80142
80553
  };
80143
80554
  }
80144
- return {
80145
- ...turn,
80146
- output: withRepromptMarker2(turn.output, { dropCount, outcome: "still-dropped", costUsd })
80147
- };
80555
+ return keepFirstTurn("still-dropped");
80148
80556
  }
80149
80557
  async function requoteBlockingFindings(findings, ctx) {
80150
80558
  const threshold = ctx.input.blockingThreshold ?? "error";
@@ -82313,9 +82721,10 @@ function resolveHopCompleteOptions(options, currentAgent, primaryAgent, tier, mo
82313
82721
  return { ...options, modelDef: resolveModel(model) };
82314
82722
  return { ...options, modelDef: options.modelDefFor?.(currentAgent, tier) ?? options.modelDef };
82315
82723
  }
82316
- function resolveFinalDispatch(options, primaryAgent, fallbacks, finalTier) {
82724
+ function resolveFinalDispatch(options, primaryAgent, outcome) {
82725
+ const { fallbacks, finalTier, finalTarget } = outcome;
82317
82726
  const agentName = fallbacks.at(-1)?.newAgent ?? primaryAgent;
82318
- const hopOptions = resolveHopCompleteOptions(options, agentName, primaryAgent, finalTier);
82727
+ const hopOptions = resolveHopCompleteOptions(options, agentName, primaryAgent, finalTier, finalTarget?.model);
82319
82728
  return { agentName, options: finalTier !== undefined ? { ...hopOptions, modelTier: finalTier } : hopOptions };
82320
82729
  }
82321
82730
  function buildFallbackRecord(input) {
@@ -83102,8 +83511,15 @@ function parseForm(request) {
83102
83511
  const fields = entries.map(([key, property]) => fieldOf(key, property, required2.has(key)));
83103
83512
  return fields.every((field) => field !== undefined) ? { ok: true, fields: withCompanions(fields) } : REFUSED;
83104
83513
  }
83514
+ function ownAnswerHint(field) {
83515
+ if (field.companion === undefined)
83516
+ return "";
83517
+ if (!field.required)
83518
+ return ", or type your own answer";
83519
+ return field.kind === "multi" ? "; you may add your own after at least one choice" : "";
83520
+ }
83105
83521
  function instruction(field) {
83106
- const own = field.companion === undefined ? "" : ", or type your own answer";
83522
+ const own = ownAnswerHint(field);
83107
83523
  const skip = field.required ? "" : " Leave empty to skip.";
83108
83524
  if (field.kind === "single")
83109
83525
  return `Reply with one number or choice${own}.${skip}`;
@@ -87893,7 +88309,7 @@ function contentText(content) {
87893
88309
  return content.map(contentPart).filter((part) => part !== "").join(`
87894
88310
  `);
87895
88311
  }
87896
- function previewOf2(call2, secrets) {
88312
+ function previewOf(call2, secrets) {
87897
88313
  const fromContent = contentText(call2.content);
87898
88314
  let raw = fromContent;
87899
88315
  if (raw === "" && typeof call2.rawOutput === "string")
@@ -87968,7 +88384,7 @@ function createToolEvents(emit, secrets, now = Date.now) {
87968
88384
  running.add(state.id);
87969
88385
  const shown2 = announce(state);
87970
88386
  if (status !== "in_progress")
87971
- resolve35(shown2, status === "failed", previewOf2(shown2, secrets), resultBytesOf(shown2));
88387
+ resolve35(shown2, status === "failed", previewOf(shown2, secrets), resultBytesOf(shown2));
87972
88388
  },
87973
88389
  announce(toolCall) {
87974
88390
  const state = merge2(toolCall);
@@ -88383,7 +88799,7 @@ function race(promise3, opts) {
88383
88799
  }
88384
88800
 
88385
88801
  // ../nax-agent-acp/src/client/launch.ts
88386
- import { spawn as spawn5 } from "child_process";
88802
+ import { spawn as spawn6 } from "child_process";
88387
88803
  import { accessSync as accessSync3, constants as constants3, statSync as statSync8 } from "fs";
88388
88804
  import { delimiter as delimiter2, isAbsolute as isAbsolute25, join as join73 } from "path";
88389
88805
  import { Readable as Readable2, Writable } from "stream";
@@ -88428,7 +88844,7 @@ function failedLaunch(stderr, error48) {
88428
88844
  }
88429
88845
  function trySpawn(request) {
88430
88846
  try {
88431
- return spawn5(request.command, [...request.args], {
88847
+ return spawn6(request.command, [...request.args], {
88432
88848
  cwd: request.cwd,
88433
88849
  env: { ...request.env },
88434
88850
  stdio: "pipe",
@@ -92293,7 +92709,7 @@ var init_turn_interactions = __esm(() => {
92293
92709
  });
92294
92710
 
92295
92711
  // src/agents/interaction/index.ts
92296
- var init_interaction = __esm(() => {
92712
+ var init_interaction2 = __esm(() => {
92297
92713
  init_output_parsing();
92298
92714
  init_turn_interactions();
92299
92715
  });
@@ -92350,7 +92766,7 @@ function createAskPort(slot, beatMs = AWAITING_HUMAN_BEAT_MS, audit) {
92350
92766
  var STAGE = "acp", AWAITING_HUMAN_BEAT_MS = 30000;
92351
92767
  var init_ask_port = __esm(() => {
92352
92768
  init_logger2();
92353
- init_interaction();
92769
+ init_interaction2();
92354
92770
  });
92355
92771
 
92356
92772
  // src/agents/acp/profile-map.ts
@@ -92762,7 +93178,7 @@ var init_prompt_retry = __esm(() => {
92762
93178
  });
92763
93179
 
92764
93180
  // src/agents/acp/stream-bridge.ts
92765
- import { randomUUID as randomUUID13 } from "crypto";
93181
+ import { randomUUID as randomUUID14 } from "crypto";
92766
93182
  function toolUpdate(toolName) {
92767
93183
  return toolName === undefined ? { kind: "agent.tool_call_update" } : { kind: "agent.tool_call_update", toolName };
92768
93184
  }
@@ -92815,7 +93231,7 @@ function sender(ctx) {
92815
93231
  };
92816
93232
  }
92817
93233
  function startCall(ctx, now = Date.now) {
92818
- const callId = randomUUID13();
93234
+ const callId = randomUUID14();
92819
93235
  const send = sender(ctx);
92820
93236
  const base = {
92821
93237
  callId,
@@ -92864,7 +93280,7 @@ var init_stream_bridge = __esm(() => {
92864
93280
  });
92865
93281
 
92866
93282
  // src/agents/acp/turn-loop.ts
92867
- import { randomUUID as randomUUID14 } from "crypto";
93283
+ import { randomUUID as randomUUID15 } from "crypto";
92868
93284
  function used(state) {
92869
93285
  return state.turnCount + state.asked;
92870
93286
  }
@@ -92955,7 +93371,7 @@ async function runIteration(loop) {
92955
93371
  }
92956
93372
  const timer = armDeadline(controller, loop.deadline.remainingMs());
92957
93373
  const call2 = startCall(session2.stream);
92958
- const turnId = opts.turnId ?? randomUUID14();
93374
+ const turnId = opts.turnId ?? randomUUID15();
92959
93375
  try {
92960
93376
  session2.opts.onActiveCall?.(call2.callId, async () => controller.abort(new WatchdogCancel));
92961
93377
  session2.slot.set({
@@ -93088,7 +93504,7 @@ var init_turn_loop2 = __esm(() => {
93088
93504
  init_src();
93089
93505
  init_errors4();
93090
93506
  init_logger2();
93091
- init_interaction();
93507
+ init_interaction2();
93092
93508
  init_turn();
93093
93509
  init_failure_map();
93094
93510
  init_pricing();
@@ -93908,7 +94324,7 @@ class AgentManager {
93908
94324
  sessionName,
93909
94325
  prompt,
93910
94326
  response: outcome.result.output,
93911
- ...resolveFinalDispatch(augmented, agentName, outcome.fallbacks, outcome.finalTier),
94327
+ ...resolveFinalDispatch(augmented, agentName, outcome),
93912
94328
  stage,
93913
94329
  resolvedPermissions,
93914
94330
  tokenUsage: outcome.result.tokenUsage,
@@ -94239,17 +94655,17 @@ function formatEntryBlock(entry2) {
94239
94655
  const commandLines = lines2.map((line, index) => index === 0 ? `${ENTRY_INDENT}$ ${line}` : `${COMMAND_CONTINUATION_INDENT}${line}`);
94240
94656
  return [header, rootLine, ...commandLines];
94241
94657
  }
94242
- function toListJson(path10, read) {
94243
- const taint = read.file.taint === undefined ? null : read.file.taint;
94244
- const entries = read.file.entries.map((entry2) => ({
94658
+ function toListJson(path10, read2) {
94659
+ const taint = read2.file.taint === undefined ? null : read2.file.taint;
94660
+ const entries = read2.file.entries.map((entry2) => ({
94245
94661
  ...entry2,
94246
94662
  id: approvalId(entry2)
94247
94663
  }));
94248
94664
  return {
94249
94665
  path: path10,
94250
- state: read.state,
94666
+ state: read2.state,
94251
94667
  taint,
94252
- droppedMalformed: read.droppedMalformed,
94668
+ droppedMalformed: read2.droppedMalformed,
94253
94669
  entries
94254
94670
  };
94255
94671
  }
@@ -94293,12 +94709,12 @@ async function promptForConfirmation(question) {
94293
94709
  resolve36(answer);
94294
94710
  };
94295
94711
  const onData = (char) => {
94296
- if (char === ETX) {
94712
+ if (char === ETX2) {
94297
94713
  finish(false);
94298
94714
  _confirmDeps.exit(SIGINT_EXIT_CODE);
94299
94715
  return;
94300
94716
  }
94301
- if (char === EOT) {
94717
+ if (char === EOT2) {
94302
94718
  finish(false);
94303
94719
  return;
94304
94720
  }
@@ -94310,7 +94726,7 @@ async function promptForConfirmation(question) {
94310
94726
  stdin.once("error", onClosed);
94311
94727
  });
94312
94728
  }
94313
- var ETX = "\x03", EOT = "\x04", SIGINT_EXIT_CODE = 130, _confirmDeps;
94729
+ var ETX2 = "\x03", EOT2 = "\x04", SIGINT_EXIT_CODE = 130, _confirmDeps;
94314
94730
  var init_confirm = __esm(() => {
94315
94731
  init_source();
94316
94732
  _confirmDeps = {
@@ -94331,26 +94747,26 @@ async function resolveApprovalsFile(workdir) {
94331
94747
  async function approvalsListCommand(opts, deps = _approvalsCliDeps) {
94332
94748
  try {
94333
94749
  const path10 = await resolveApprovalsFile(opts.workdir);
94334
- const read = await deps.readApprovalsFileDetailed(path10);
94750
+ const read2 = await deps.readApprovalsFileDetailed(path10);
94335
94751
  if (opts.json) {
94336
- deps.log(JSON.stringify(toListJson(path10, read)));
94337
- if (read.state === "unparseable")
94752
+ deps.log(JSON.stringify(toListJson(path10, read2)));
94753
+ if (read2.state === "unparseable")
94338
94754
  deps.logErr(PARSE_WARNING);
94339
94755
  return 0;
94340
94756
  }
94341
- if (read.state === "missing") {
94757
+ if (read2.state === "missing") {
94342
94758
  deps.log(missingNotice(path10));
94343
94759
  return 0;
94344
94760
  }
94345
- if (read.state === "unparseable") {
94761
+ if (read2.state === "unparseable") {
94346
94762
  deps.logErr(PARSE_WARNING);
94347
94763
  deps.log(missingNotice(path10));
94348
94764
  return 0;
94349
94765
  }
94350
- const entries = read.file.entries;
94351
- const taint = read.file.taint;
94352
- if (read.droppedMalformed > 0)
94353
- deps.logErr(malformedEntriesWarning(read.droppedMalformed));
94766
+ const entries = read2.file.entries;
94767
+ const taint = read2.file.taint;
94768
+ if (read2.droppedMalformed > 0)
94769
+ deps.logErr(malformedEntriesWarning(read2.droppedMalformed));
94354
94770
  deps.log(`Approvals store: ${path10}`);
94355
94771
  deps.log(formatTrustLine(taint, deps.isProcessAlive));
94356
94772
  deps.log(`${entries.length} remembered approvals`);
@@ -94372,12 +94788,12 @@ async function runPrecheck(opts, deps) {
94372
94788
  try {
94373
94789
  const path10 = await resolveApprovalsFile(opts.workdir);
94374
94790
  if (opts.all) {
94375
- const read = await deps.readApprovalsFileDetailed(path10);
94376
- if (read.state === "missing" || read.state === "ok" && read.file.entries.length === 0) {
94791
+ const read2 = await deps.readApprovalsFileDetailed(path10);
94792
+ if (read2.state === "missing" || read2.state === "ok" && read2.file.entries.length === 0) {
94377
94793
  deps.log(missingNotice(path10));
94378
94794
  return { kind: "empty", path: path10 };
94379
94795
  }
94380
- if (read.state === "unparseable") {
94796
+ if (read2.state === "unparseable") {
94381
94797
  deps.logErr("approvals.json could not be parsed; not rewriting it");
94382
94798
  return { kind: "unparseable", path: path10 };
94383
94799
  }
@@ -94672,227 +95088,9 @@ var init_auth_list = __esm(() => {
94672
95088
  init_config();
94673
95089
  });
94674
95090
 
94675
- // src/cli/auth-prompt.ts
94676
- function read(message, echo, onEmptySubmit) {
94677
- const { stdin } = _authPromptDeps;
94678
- if (stdin.isTTY !== true)
94679
- return Promise.reject(new PromptCancelledError);
94680
- _authPromptDeps.write(`${source_default.cyan("?")} ${message} `);
94681
- return new Promise((resolve36, reject) => {
94682
- let buffer = "";
94683
- let settled2 = false;
94684
- const cleanup = () => {
94685
- if (settled2)
94686
- return;
94687
- settled2 = true;
94688
- stdin.removeListener("data", onData);
94689
- stdin.removeListener("end", onEnd);
94690
- stdin.removeListener("error", onEnd);
94691
- stdin.setRawMode(false);
94692
- stdin.pause();
94693
- _authPromptDeps.write(`
94694
- `);
94695
- };
94696
- const onEnd = () => {
94697
- cleanup();
94698
- reject(new PromptCancelledError);
94699
- };
94700
- const onData = (chunk) => {
94701
- for (const char of chunk) {
94702
- if (char === ETX2 || char === EOT2) {
94703
- cleanup();
94704
- reject(new PromptCancelledError);
94705
- return;
94706
- }
94707
- if (char === CR || char === LF) {
94708
- if (buffer.length === 0 && onEmptySubmit !== undefined) {
94709
- onEmptySubmit();
94710
- continue;
94711
- }
94712
- cleanup();
94713
- resolve36(buffer);
94714
- return;
94715
- }
94716
- if (char === BACKSPACE) {
94717
- buffer = buffer.slice(0, -1);
94718
- if (echo)
94719
- _authPromptDeps.write("\b \b");
94720
- continue;
94721
- }
94722
- buffer += char;
94723
- if (echo)
94724
- _authPromptDeps.write(char);
94725
- }
94726
- };
94727
- stdin.setRawMode(true);
94728
- stdin.resume();
94729
- stdin.setEncoding("utf8");
94730
- stdin.on("data", onData);
94731
- stdin.once("end", onEnd);
94732
- stdin.once("error", onEnd);
94733
- });
94734
- }
94735
- function promptForSecret(message) {
94736
- return read(message, false);
94737
- }
94738
- function promptForLine(message, onEmptySubmit) {
94739
- return read(message, true, onEmptySubmit);
94740
- }
94741
- function promptForSelect(message, choices) {
94742
- const { stdin } = _authPromptDeps;
94743
- if (stdin.isTTY !== true)
94744
- return Promise.reject(new PromptCancelledError);
94745
- if (choices.length === 0)
94746
- return Promise.reject(new PromptCancelledError);
94747
- _authPromptDeps.write(`${source_default.cyan("?")} ${message}
94748
- `);
94749
- return new Promise((resolve36, reject) => {
94750
- let index = 0;
94751
- let settled2 = false;
94752
- let drawn = false;
94753
- const render3 = () => {
94754
- if (drawn)
94755
- _authPromptDeps.write(`\x1B[${choices.length}A`);
94756
- drawn = true;
94757
- for (const [i, choice] of choices.entries()) {
94758
- const active = i === index;
94759
- const marker = active ? source_default.cyan(">") : " ";
94760
- const label2 = active ? source_default.cyan(choice.label) : choice.label;
94761
- _authPromptDeps.write(`\r\x1B[2K${marker} ${label2}
94762
- `);
94763
- }
94764
- };
94765
- const cleanup = () => {
94766
- if (settled2)
94767
- return;
94768
- settled2 = true;
94769
- stdin.removeListener("data", onData);
94770
- stdin.removeListener("end", onEnd);
94771
- stdin.removeListener("error", onEnd);
94772
- stdin.setRawMode(false);
94773
- stdin.pause();
94774
- };
94775
- const onEnd = () => {
94776
- cleanup();
94777
- reject(new PromptCancelledError);
94778
- };
94779
- const onData = (chunk) => {
94780
- if (chunk.includes(ETX2) || chunk.includes(EOT2)) {
94781
- cleanup();
94782
- reject(new PromptCancelledError);
94783
- return;
94784
- }
94785
- if (chunk.includes(ARROW_UP)) {
94786
- index = (index - 1 + choices.length) % choices.length;
94787
- render3();
94788
- return;
94789
- }
94790
- if (chunk.includes(ARROW_DOWN)) {
94791
- index = (index + 1) % choices.length;
94792
- render3();
94793
- return;
94794
- }
94795
- if (chunk.includes(CR) || chunk.includes(LF)) {
94796
- const chosen = choices[index];
94797
- cleanup();
94798
- resolve36(chosen.id);
94799
- }
94800
- };
94801
- stdin.setRawMode(true);
94802
- stdin.resume();
94803
- stdin.setEncoding("utf8");
94804
- stdin.on("data", onData);
94805
- stdin.once("end", onEnd);
94806
- stdin.once("error", onEnd);
94807
- render3();
94808
- });
94809
- }
94810
- var ETX2 = "\x03", EOT2 = "\x04", CR = "\r", LF = `
94811
- `, BACKSPACE = "\x7F", ARROW_UP = "\x1B[A", ARROW_DOWN = "\x1B[B", PromptCancelledError, _authPromptDeps;
94812
- var init_auth_prompt = __esm(() => {
94813
- init_source();
94814
- PromptCancelledError = class PromptCancelledError extends Error {
94815
- constructor() {
94816
- super("Prompt cancelled");
94817
- this.name = "PromptCancelledError";
94818
- }
94819
- };
94820
- _authPromptDeps = {
94821
- stdin: process.stdin,
94822
- write: (text) => process.stdout.write(text)
94823
- };
94824
- });
94825
-
94826
- // src/cli/open-url.ts
94827
- function openerFor(platform, url2) {
94828
- if (platform === "darwin")
94829
- return ["open", url2];
94830
- if (platform === "win32")
94831
- return ["cmd", "/c", "start", "", url2];
94832
- return ["xdg-open", url2];
94833
- }
94834
- function openUrl(url2) {
94835
- try {
94836
- _openUrlDeps.spawn(openerFor(_openUrlDeps.platform(), url2));
94837
- } catch {}
94838
- }
94839
- var _openUrlDeps;
94840
- var init_open_url = __esm(() => {
94841
- _openUrlDeps = {
94842
- spawn: (command2) => {
94843
- Bun.spawn([...command2], { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
94844
- },
94845
- platform: () => process.platform
94846
- };
94847
- });
94848
-
94849
95091
  // src/cli/auth.ts
94850
95092
  function terminalInteraction() {
94851
- let pendingUrl;
94852
- return {
94853
- prompt: async (prompt) => {
94854
- if (prompt.type === "manual-code") {
94855
- const url2 = pendingUrl;
94856
- if (url2 === undefined)
94857
- return promptForLine(prompt.message);
94858
- pendingUrl = undefined;
94859
- _cliAuthDeps.log(source_default.dim("Press Enter to open it in your browser."));
94860
- return promptForLine(prompt.message, () => {
94861
- _cliAuthDeps.log(source_default.dim("Opening your browser..."));
94862
- openUrl(url2);
94863
- });
94864
- }
94865
- if (prompt.type === "text")
94866
- return promptForLine(prompt.message);
94867
- if (prompt.type === "select") {
94868
- return promptForSelect(prompt.message, prompt.options.map((option) => ({ id: option.id, label: option.label })));
94869
- }
94870
- return promptForSecret(prompt.message);
94871
- },
94872
- notify: (event) => {
94873
- switch (event.type) {
94874
- case "auth-url":
94875
- _cliAuthDeps.log(`
94876
- ${source_default.bold("Open this URL to continue:")}
94877
- ${event.url}`);
94878
- pendingUrl = event.url;
94879
- return;
94880
- case "device-code":
94881
- _cliAuthDeps.log(`
94882
- Go to ${event.verificationUri} and enter code ${source_default.bold(event.userCode)}`);
94883
- return;
94884
- case "info":
94885
- _cliAuthDeps.log(event.message);
94886
- for (const link2 of event.links ?? [])
94887
- _cliAuthDeps.log(` ${link2.label ?? "Link"}: ${link2.url}`);
94888
- return;
94889
- case "progress":
94890
- _cliAuthDeps.log(source_default.dim(event.message));
94891
- return;
94892
- default:
94893
- }
94894
- }
94895
- };
95093
+ return createTerminalAuthInteraction({ log: (text) => _cliAuthDeps.log(text), style: CHALK_STYLE });
94896
95094
  }
94897
95095
  async function authLoginCommand(providerId, method) {
94898
95096
  if (!_cliAuthDeps.isTTY()) {
@@ -94989,19 +95187,18 @@ async function authRmCommand(providerId) {
94989
95187
  return 1;
94990
95188
  }
94991
95189
  }
94992
- var _cliAuthDeps;
95190
+ var _cliAuthDeps, CHALK_STYLE;
94993
95191
  var init_auth3 = __esm(() => {
94994
95192
  init_src();
94995
95193
  init_source();
94996
95194
  init_config();
94997
95195
  init_auth_list();
94998
- init_auth_prompt();
94999
- init_open_url();
95000
95196
  _cliAuthDeps = {
95001
95197
  log: (text) => console.log(text),
95002
95198
  isTTY: () => process.stdin.isTTY === true,
95003
95199
  collectAuthList
95004
95200
  };
95201
+ CHALK_STYLE = { accent: source_default.cyan, dim: source_default.dim, bold: source_default.bold };
95005
95202
  });
95006
95203
 
95007
95204
  // src/cli/config-diff.ts
@@ -95168,7 +95365,7 @@ var init_config_descriptions = __esm(() => {
95168
95365
  "quality.stripEnvVars": "Environment variables to strip during verification",
95169
95366
  tdd: "Test-driven development configuration",
95170
95367
  "tdd.maxRetries": "Max retries per TDD session before escalating",
95171
- "tdd.strategy": "TDD strategy: auto | strict | lite | off",
95368
+ "tdd.strategy": 'TDD strategy: auto | strict | lite | off | simple ("tdd-simple" is accepted for simple)',
95172
95369
  "tdd.sessionTiers": "Per-session model tier overrides",
95173
95370
  "tdd.sessionTiers.testWriter": "Model tier for test-writer session",
95174
95371
  "tdd.sessionTiers.implementer": "Ignored by design \u2014 implementer follows story.routing.modelTier + escalation, not this field. Kept optional so legacy configs still parse.",
@@ -97921,7 +98118,7 @@ var init_triggers = __esm(() => {
97921
98118
  });
97922
98119
 
97923
98120
  // src/interaction/index.ts
97924
- var init_interaction2 = __esm(() => {
98121
+ var init_interaction3 = __esm(() => {
97925
98122
  init_ask_link();
97926
98123
  init_bridge_builder();
97927
98124
  init_chain();
@@ -97997,7 +98194,7 @@ var _configJsonDeps;
97997
98194
  var init_config_json = __esm(() => {
97998
98195
  init_internal();
97999
98196
  init_config();
98000
- init_interaction2();
98197
+ init_interaction3();
98001
98198
  init_config_display();
98002
98199
  init_config_profile();
98003
98200
  init_config_requirements();
@@ -98902,8 +99099,41 @@ var init_fix_diagnosis = __esm(() => {
98902
99099
  init_import_resolution();
98903
99100
  });
98904
99101
 
99102
+ // src/utils/drain-within.ts
99103
+ async function drainWithin(drain, deadlineMs) {
99104
+ let timer;
99105
+ const expired = new Promise((resolve37) => {
99106
+ timer = setTimeout(() => resolve37(""), deadlineMs);
99107
+ });
99108
+ try {
99109
+ return await Promise.race([drain, expired]);
99110
+ } finally {
99111
+ clearTimeout(timer);
99112
+ }
99113
+ }
99114
+
98905
99115
  // src/acceptance/hardening.ts
98906
99116
  import path12 from "path";
99117
+ async function awaitExitAndDrain(proc, timers) {
99118
+ const stdoutDrain = new Response(proc.stdout).text().catch(() => "");
99119
+ const stderrDrain = new Response(proc.stderr).text().catch(() => "");
99120
+ let exitCode;
99121
+ let output;
99122
+ try {
99123
+ exitCode = await proc.exited;
99124
+ const [stdout, stderr] = await Promise.all([
99125
+ drainWithin(stdoutDrain, _hardeningDeps.drainTimeoutMs),
99126
+ drainWithin(stderrDrain, _hardeningDeps.drainTimeoutMs)
99127
+ ]);
99128
+ output = `${stdout}
99129
+ ${stderr}`;
99130
+ } finally {
99131
+ clearTimeout(timers.killTimer);
99132
+ if (timers.sigkillTimer)
99133
+ clearTimeout(timers.sigkillTimer);
99134
+ }
99135
+ return { exitCode, output };
99136
+ }
98907
99137
  async function processPackageGroup(ctx, packageDir, groupStories, language, result) {
98908
99138
  await assertTrusted(packageDir, "acceptance-command");
98909
99139
  const logger = getSafeLogger2();
@@ -98986,30 +99216,23 @@ async function processPackageGroup(ctx, packageDir, groupStories, language, resu
98986
99216
  detached: true
98987
99217
  });
98988
99218
  let exitedBeforeSigkill = false;
98989
- let sigkillTimer;
98990
99219
  proc.exited.then(() => {
98991
99220
  exitedBeforeSigkill = true;
98992
99221
  }).catch(() => {});
98993
99222
  const timeoutMs = config2.acceptance?.timeoutMs ?? DEFAULT_HARDENING_TIMEOUT_MS;
98994
- const killTimer = setTimeout(() => {
98995
- killProcessGroup(proc.pid, "SIGTERM");
98996
- sigkillTimer = setTimeout(() => {
98997
- sigkillTimer = undefined;
98998
- if (!exitedBeforeSigkill) {
98999
- killProcessGroup(proc.pid, "SIGKILL");
99000
- }
99001
- }, HARDENING_SIGKILL_GRACE_PERIOD_MS);
99002
- }, timeoutMs);
99003
- const [exitCode, stdout, stderr] = await Promise.all([
99004
- proc.exited,
99005
- new Response(proc.stdout).text().catch(() => ""),
99006
- new Response(proc.stderr).text().catch(() => "")
99007
- ]);
99008
- clearTimeout(killTimer);
99009
- if (sigkillTimer)
99010
- clearTimeout(sigkillTimer);
99011
- const output = `${stdout}
99012
- ${stderr}`;
99223
+ const timers = {
99224
+ killTimer: setTimeout(() => {
99225
+ killProcessGroup(proc.pid, "SIGTERM");
99226
+ timers.sigkillTimer = setTimeout(() => {
99227
+ timers.sigkillTimer = undefined;
99228
+ if (!exitedBeforeSigkill) {
99229
+ killProcessGroup(proc.pid, "SIGKILL");
99230
+ }
99231
+ }, HARDENING_SIGKILL_GRACE_PERIOD_MS);
99232
+ }, timeoutMs),
99233
+ sigkillTimer: undefined
99234
+ };
99235
+ const { exitCode, output } = await awaitExitAndDrain(proc, timers);
99013
99236
  const failedACs = parseTestFailures(output);
99014
99237
  const failedSet = new Set(failedACs.map((ac) => ac.toUpperCase()));
99015
99238
  if (exitCode !== 0 && failedACs.length === 0) {
@@ -99114,19 +99337,59 @@ var init_hardening = __esm(() => {
99114
99337
  writeFile: async (p, c) => {
99115
99338
  await Bun.write(p, c);
99116
99339
  },
99117
- detectLanguage
99340
+ detectLanguage,
99341
+ drainTimeoutMs: 2000
99118
99342
  };
99119
99343
  });
99120
99344
 
99345
+ // src/acceptance/override-keys.ts
99346
+ import { relative as relative26 } from "path";
99347
+ function scopedOverrideKey(workdir, packageDir, acId) {
99348
+ return `${relative26(workdir, packageDir) || "."}${OVERRIDE_SCOPE_SEPARATOR}${acId}`;
99349
+ }
99350
+ function definingPackageCount(acId, counts) {
99351
+ const n = Number(NUMBERED_AC.exec(acId)?.[1] ?? 0);
99352
+ if (n < 1)
99353
+ return 0;
99354
+ return [...counts.values()].filter((count2) => count2 >= n).length;
99355
+ }
99356
+ function createOverrideLookup(opts) {
99357
+ const table = opts.overrides ?? {};
99358
+ const reported = new Set;
99359
+ return {
99360
+ reasonFor(packageDir, acId) {
99361
+ const scoped2 = table[scopedOverrideKey(opts.workdir, packageDir, acId)];
99362
+ if (scoped2 !== undefined)
99363
+ return scoped2;
99364
+ const bare = table[acId];
99365
+ if (bare === undefined || !opts.multiPackage)
99366
+ return bare;
99367
+ if (definingPackageCount(acId, opts.acCountByPackageDir) === 1)
99368
+ return bare;
99369
+ if (!reported.has(acId)) {
99370
+ reported.add(acId);
99371
+ opts.onIgnoredBareKey(acId);
99372
+ }
99373
+ return;
99374
+ }
99375
+ };
99376
+ }
99377
+ var OVERRIDE_SCOPE_SEPARATOR = "::", NUMBERED_AC;
99378
+ var init_override_keys = __esm(() => {
99379
+ NUMBERED_AC = /^AC-(\d+)$/;
99380
+ });
99381
+
99121
99382
  // src/acceptance/index.ts
99122
99383
  var exports_acceptance = {};
99123
99384
  __export(exports_acceptance, {
99385
+ OVERRIDE_SCOPE_SEPARATOR: () => OVERRIDE_SCOPE_SEPARATOR,
99124
99386
  _acceptanceExecutionDeps: () => _acceptanceExecutionDeps,
99125
99387
  _groupDeps: () => _groupDeps,
99126
99388
  acceptanceTestFilename: () => acceptanceTestFilename2,
99127
99389
  buildAcceptanceRunCommand: () => buildAcceptanceRunCommand,
99128
99390
  buildAcceptanceTestPrompt: () => buildAcceptanceTestPrompt,
99129
99391
  checkAcceptanceCoverage: () => checkAcceptanceCoverage,
99392
+ createOverrideLookup: () => createOverrideLookup,
99130
99393
  effectiveAcceptanceFramework: () => effectiveAcceptanceFramework,
99131
99394
  findExistingAcceptanceTestPath: () => findExistingAcceptanceTestPath,
99132
99395
  findMissingAcceptanceTestPaths: () => findMissingAcceptanceTestPaths,
@@ -99144,6 +99407,7 @@ __export(exports_acceptance, {
99144
99407
  resolveSuggestedPackageFeatureTestPath: () => resolveSuggestedPackageFeatureTestPath,
99145
99408
  resolveSuggestedTestFile: () => resolveSuggestedTestFile,
99146
99409
  runHardeningPass: () => runHardeningPass,
99410
+ scopedOverrideKey: () => scopedOverrideKey,
99147
99411
  substituteAcceptanceTestPath: () => substituteAcceptanceTestPath,
99148
99412
  suggestedTestFilename: () => suggestedTestFilename,
99149
99413
  warnMissingAcceptanceTests: () => warnMissingAcceptanceTests
@@ -99154,13 +99418,14 @@ var init_acceptance2 = __esm(() => {
99154
99418
  init_fix_diagnosis();
99155
99419
  init_generator();
99156
99420
  init_hardening();
99421
+ init_override_keys();
99157
99422
  init_refinement();
99158
99423
  init_test_path();
99159
99424
  });
99160
99425
 
99161
99426
  // src/cli/features-acceptance.ts
99162
99427
  import { existsSync as existsSync16 } from "fs";
99163
- import { join as join79, relative as relative26 } from "path";
99428
+ import { join as join79, relative as relative27 } from "path";
99164
99429
  async function resolveFeatureAcceptance(featureName, workdir) {
99165
99430
  let enabled = true;
99166
99431
  try {
@@ -99181,11 +99446,11 @@ async function resolveFeatureAcceptance(featureName, workdir) {
99181
99446
  const prd = await loadPRD(prdPath);
99182
99447
  const testGroups = await groupStoriesByPackage(prd, repoRoot, featureName, config2.acceptance?.testPath, config2.project?.language);
99183
99448
  const groups = await Promise.all(testGroups.map(async (g) => {
99184
- const packageDir = relative26(repoRoot, g.packageDir);
99449
+ const packageDir = relative27(repoRoot, g.packageDir);
99185
99450
  const command2 = await resolveGroupCommand(repoRoot, packageDir, config2.acceptance?.command);
99186
99451
  return {
99187
99452
  packageDir,
99188
- testPath: relative26(repoRoot, g.testPath),
99453
+ testPath: relative27(repoRoot, g.testPath),
99189
99454
  exists: await Bun.file(g.testPath).exists(),
99190
99455
  command: command2,
99191
99456
  cwd: packageDir,
@@ -99240,7 +99505,7 @@ var init_feature_name = __esm(() => {
99240
99505
 
99241
99506
  // src/cli/features-resolve.ts
99242
99507
  import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
99243
- import { join as join80, relative as relative27 } from "path";
99508
+ import { join as join80, relative as relative28 } from "path";
99244
99509
  async function resolveTestPatterns(workdir) {
99245
99510
  try {
99246
99511
  const config2 = await loadConfig(workdir);
@@ -99263,17 +99528,17 @@ async function searchSpecSource(naxDir, repoRoot, name) {
99263
99528
  ];
99264
99529
  const docsSpecExact = join80(repoRoot, "docs", "specs", `SPEC-${name}.md`);
99265
99530
  candidates.push({ abs: docsSpecExact, kind: "markdown" });
99266
- const checked = candidates.map((c) => relative27(repoRoot, c.abs));
99531
+ const checked = candidates.map((c) => relative28(repoRoot, c.abs));
99267
99532
  for (const { abs, kind } of candidates.slice(0, 2)) {
99268
99533
  if (kind === "markdown") {
99269
99534
  const nonEmpty2 = await isNonEmptyFile(abs);
99270
99535
  if (nonEmpty2) {
99271
- return { source: { kind, path: relative27(repoRoot, abs) }, checked };
99536
+ return { source: { kind, path: relative28(repoRoot, abs) }, checked };
99272
99537
  }
99273
99538
  }
99274
99539
  }
99275
99540
  if (await isNonEmptyFile(docsSpecExact)) {
99276
- return { source: { kind: "markdown", path: relative27(repoRoot, docsSpecExact) }, checked };
99541
+ return { source: { kind: "markdown", path: relative28(repoRoot, docsSpecExact) }, checked };
99277
99542
  }
99278
99543
  const docsSpecsDir = join80(repoRoot, "docs", "specs");
99279
99544
  if (existsSync17(docsSpecsDir)) {
@@ -99281,7 +99546,7 @@ async function searchSpecSource(naxDir, repoRoot, name) {
99281
99546
  for (const match of glob2.scanSync({ cwd: docsSpecsDir, absolute: false })) {
99282
99547
  const abs = join80(docsSpecsDir, match);
99283
99548
  if (await isNonEmptyFile(abs)) {
99284
- const relPath = relative27(repoRoot, abs);
99549
+ const relPath = relative28(repoRoot, abs);
99285
99550
  if (!checked.includes(relPath))
99286
99551
  checked.push(relPath);
99287
99552
  return { source: { kind: "markdown", path: relPath }, checked };
@@ -99289,7 +99554,7 @@ async function searchSpecSource(naxDir, repoRoot, name) {
99289
99554
  }
99290
99555
  }
99291
99556
  const prdAbs = join80(naxDir, "features", name, "prd.json");
99292
- const prdRel = relative27(repoRoot, prdAbs);
99557
+ const prdRel = relative28(repoRoot, prdAbs);
99293
99558
  if (!checked.includes(prdRel))
99294
99559
  checked.push(prdRel);
99295
99560
  if (existsSync17(prdAbs)) {
@@ -99341,8 +99606,8 @@ async function resolveFeatureSpec(name, workdir) {
99341
99606
  return {
99342
99607
  status: "ok",
99343
99608
  featureName: null,
99344
- specSource: { kind: "markdown", path: relative27(repoRoot, abs) },
99345
- message: `resolved spec: ${relative27(repoRoot, abs)}`
99609
+ specSource: { kind: "markdown", path: relative28(repoRoot, abs) },
99610
+ message: `resolved spec: ${relative28(repoRoot, abs)}`
99346
99611
  };
99347
99612
  }
99348
99613
  if (name !== undefined && name.trim() !== "") {
@@ -99791,7 +100056,7 @@ var init_windsurf = __esm(() => {
99791
100056
 
99792
100057
  // src/context/generator/index.ts
99793
100058
  import { existsSync as existsSync19 } from "fs";
99794
- import { join as join82, relative as relative28 } from "path";
100059
+ import { join as join82, relative as relative29 } from "path";
99795
100060
  async function loadContextContent(options, config2) {
99796
100061
  if (!_generatorDeps.existsSync(options.contextPath)) {
99797
100062
  throw new NaxError(`Context file not found: ${options.contextPath}`, "CONTEXT_FILE_NOT_FOUND", {
@@ -99922,7 +100187,7 @@ async function discoverWorkspacePackages2(repoRoot) {
99922
100187
  }
99923
100188
  async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
99924
100189
  const resolvedRepoRoot = repoRoot ?? packageDir;
99925
- const relativePkgPath = relative28(resolvedRepoRoot, packageDir);
100190
+ const relativePkgPath = relative29(resolvedRepoRoot, packageDir);
99926
100191
  const contextPath = join82(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
99927
100192
  if (!_generatorDeps.existsSync(contextPath)) {
99928
100193
  return [
@@ -100206,7 +100471,7 @@ var init_generate = __esm(() => {
100206
100471
 
100207
100472
  // src/cli/init-context.ts
100208
100473
  import { mkdir as mkdir25, readdir as readdir7 } from "fs/promises";
100209
- import { basename as basename11, join as join84, relative as relative29, sep as sep18 } from "path";
100474
+ import { basename as basename11, join as join84, relative as relative30, sep as sep18 } from "path";
100210
100475
  async function bunFileExists(path13) {
100211
100476
  return Bun.file(path13).exists();
100212
100477
  }
@@ -100233,7 +100498,7 @@ async function walkDir(root, current, out, maxFiles) {
100233
100498
  continue;
100234
100499
  await walkDir(root, join84(current, entry2.name), out, maxFiles);
100235
100500
  } else if (entry2.isFile()) {
100236
- out.push(relative29(root, join84(current, entry2.name)).replaceAll(sep18, "/"));
100501
+ out.push(relative30(root, join84(current, entry2.name)).replaceAll(sep18, "/"));
100237
100502
  }
100238
100503
  }
100239
100504
  }
@@ -100717,19 +100982,19 @@ function validateProjectName(name) {
100717
100982
  return { valid: true };
100718
100983
  }
100719
100984
  async function checkInitCollision(name, currentWorkdir, currentRemote) {
100720
- const identity = await readProjectIdentity(name);
100721
- if (!identity)
100985
+ const identity2 = await readProjectIdentity(name);
100986
+ if (!identity2)
100722
100987
  return { collision: false };
100723
- const sameRemote = isSameProject(currentRemote, identity.remoteUrl);
100724
- const sameWorkdir = !currentRemote && !identity.remoteUrl && currentWorkdir === identity.workdir;
100988
+ const sameRemote = isSameProject(currentRemote, identity2.remoteUrl);
100989
+ const sameWorkdir = !currentRemote && !identity2.remoteUrl && currentWorkdir === identity2.workdir;
100725
100990
  if (sameRemote || sameWorkdir)
100726
100991
  return { collision: false };
100727
100992
  return {
100728
100993
  collision: true,
100729
100994
  existing: {
100730
- workdir: identity.workdir,
100731
- remoteUrl: identity.remoteUrl,
100732
- lastSeen: identity.lastSeen
100995
+ workdir: identity2.workdir,
100996
+ remoteUrl: identity2.remoteUrl,
100997
+ lastSeen: identity2.lastSeen
100733
100998
  }
100734
100999
  };
100735
101000
  }
@@ -101244,7 +101509,7 @@ var init_checks_cli = __esm(() => {
101244
101509
  init_bun_deps();
101245
101510
  init_agents();
101246
101511
  _checkCliDeps = {
101247
- spawn
101512
+ spawn: spawn2
101248
101513
  };
101249
101514
  });
101250
101515
 
@@ -101437,7 +101702,7 @@ var init_resume_hydrate = __esm(() => {
101437
101702
  });
101438
101703
 
101439
101704
  // src/tdd/rollback.ts
101440
- import { rm as rm9 } from "fs/promises";
101705
+ import { rm as rm10 } from "fs/promises";
101441
101706
  import { join as join88 } from "path";
101442
101707
  async function runGitBounded2(args, workdir) {
101443
101708
  if (!workdir) {
@@ -101558,18 +101823,18 @@ var init_rollback = __esm(() => {
101558
101823
  autoCommitIfDirty,
101559
101824
  getUntrackedPaths,
101560
101825
  getGitRoot,
101561
- rm: rm9,
101826
+ rm: rm10,
101562
101827
  timeoutMs: ROLLBACK_GIT_TIMEOUT_MS,
101563
101828
  drainTimeoutMs: ROLLBACK_DRAIN_TIMEOUT_MS
101564
101829
  };
101565
101830
  });
101566
101831
 
101567
101832
  // src/utils/paths.ts
101568
- import { join as join89, relative as relative30, sep as sep19 } from "path";
101833
+ import { join as join89, relative as relative31, sep as sep19 } from "path";
101569
101834
  function packageDirRelative(projectDir, workdir) {
101570
101835
  if (!projectDir || !workdir || workdir === projectDir)
101571
101836
  return;
101572
- const rel = relative30(projectDir, workdir);
101837
+ const rel = relative31(projectDir, workdir);
101573
101838
  if (rel === ".." || rel.startsWith(`..${sep19}`))
101574
101839
  return;
101575
101840
  return rel && rel !== "." ? rel : undefined;
@@ -102923,7 +103188,7 @@ var init_cleanup = __esm(() => {
102923
103188
  init_bun_deps();
102924
103189
  init_logger2();
102925
103190
  _cleanupDeps = {
102926
- spawn,
103191
+ spawn: spawn2,
102927
103192
  sleep,
102928
103193
  kill: process.kill.bind(process),
102929
103194
  killProcessGroupFn: (pid, signal) => killProcessGroup(pid, signal)
@@ -106166,8 +106431,8 @@ var init_escalation = __esm(() => {
106166
106431
  });
106167
106432
 
106168
106433
  // src/execution/lock.ts
106169
- import { randomUUID as randomUUID15 } from "crypto";
106170
- import { rename as rename6, unlink as unlink6 } from "fs/promises";
106434
+ import { randomUUID as randomUUID16 } from "crypto";
106435
+ import { rename as rename7, unlink as unlink6 } from "fs/promises";
106171
106436
  import { hostname as hostname3 } from "os";
106172
106437
  import path14 from "path";
106173
106438
  async function serializeLockOperation(workdir, operation) {
@@ -106210,7 +106475,7 @@ function parseHolder(raw) {
106210
106475
  }
106211
106476
  }
106212
106477
  async function publishFileAtomically(targetPath, content) {
106213
- const tempPath = `${targetPath}.${randomUUID15()}.tmp`;
106478
+ const tempPath = `${targetPath}.${randomUUID16()}.tmp`;
106214
106479
  try {
106215
106480
  await Bun.write(tempPath, content);
106216
106481
  const fs = await import("fs");
@@ -106383,14 +106648,14 @@ var init_lock2 = __esm(() => {
106383
106648
  init_logger2();
106384
106649
  pendingLockOperations = new Map;
106385
106650
  _lockDeps = {
106386
- rename: rename6,
106651
+ rename: rename7,
106387
106652
  host: () => hostname3(),
106388
106653
  readLockText: (lockPath) => Bun.file(lockPath).text()
106389
106654
  };
106390
106655
  });
106391
106656
 
106392
106657
  // src/execution/feature-lock.ts
106393
- import { mkdir as mkdir28, rename as rename7, unlink as unlink7 } from "fs/promises";
106658
+ import { mkdir as mkdir28, rename as rename8, unlink as unlink7 } from "fs/promises";
106394
106659
  import { hostname as hostname4 } from "os";
106395
106660
  import path15 from "path";
106396
106661
  function validateFeatureId2(featureId) {
@@ -106629,7 +106894,7 @@ var init_feature_lock = __esm(() => {
106629
106894
  featureLockPath,
106630
106895
  host: () => hostname4(),
106631
106896
  isProcessAlive,
106632
- rename: rename7,
106897
+ rename: rename8,
106633
106898
  tryExclusiveCreate,
106634
106899
  unlink: unlink7
106635
106900
  };
@@ -107799,6 +108064,17 @@ var init_acceptance3 = __esm(() => {
107799
108064
  storiesByPackageDir.set(pkgDir, (storiesByPackageDir.get(pkgDir) ?? 0) + 1);
107800
108065
  acsByPackageDir.set(pkgDir, (acsByPackageDir.get(pkgDir) ?? 0) + s.acceptanceCriteria.length);
107801
108066
  }
108067
+ const overrideLookup = createOverrideLookup({
108068
+ overrides: ctx.prd.acceptanceOverrides,
108069
+ workdir: ctx.workdir,
108070
+ acCountByPackageDir: acsByPackageDir,
108071
+ multiPackage: testGroups.length > 1,
108072
+ onIgnoredBareKey: (acId) => logger.warn("acceptance", "Bare acceptance override ignored: several packages define this AC id", {
108073
+ storyId: ctx.story.id,
108074
+ acId,
108075
+ hint: `scope it to one package: "<package>::${acId}"`
108076
+ })
108077
+ });
107802
108078
  const allFailedACs = [];
107803
108079
  const seenAcKeys = new Set;
107804
108080
  const allFindings = [];
@@ -107856,14 +108132,14 @@ var init_acceptance3 = __esm(() => {
107856
108132
  const output = execution.output ?? "";
107857
108133
  allOutputParts.push(output);
107858
108134
  const { failedACs, taggedFailureCount } = parseTestFailuresDetailed(output);
107859
- const overrides = ctx.prd.acceptanceOverrides ?? {};
107860
- const actualFailures = failedACs.filter((acId) => !overrides[acId]);
107861
- const overriddenFailures = failedACs.filter((acId) => overrides[acId]);
108135
+ const reasonFor = (acId) => overrideLookup.reasonFor(packageDir, acId);
108136
+ const actualFailures = failedACs.filter((acId) => !reasonFor(acId));
108137
+ const overriddenFailures = failedACs.filter((acId) => reasonFor(acId));
107862
108138
  if (overriddenFailures.length > 0) {
107863
108139
  logger.warn("acceptance", "Skipped failures (overridden)", {
107864
108140
  storyId: ctx.story.id,
107865
108141
  overriddenFailures,
107866
- overrides: overriddenFailures.map((acId) => ({ acId, reason: overrides[acId] }))
108142
+ overrides: overriddenFailures.map((acId) => ({ acId, reason: reasonFor(acId) }))
107867
108143
  });
107868
108144
  }
107869
108145
  if (failedACs.length === 0 && exitCode !== 0) {
@@ -108687,7 +108963,7 @@ var init_completion = __esm(() => {
108687
108963
  init_engine();
108688
108964
  init_fragments();
108689
108965
  init_execution2();
108690
- init_interaction2();
108966
+ init_interaction3();
108691
108967
  init_logger2();
108692
108968
  init_metrics();
108693
108969
  init_prd();
@@ -108973,7 +109249,7 @@ var init_scope_files = __esm(() => {
108973
109249
  });
108974
109250
 
108975
109251
  // src/pipeline/stages/context.ts
108976
- import { randomUUID as randomUUID16 } from "crypto";
109252
+ import { randomUUID as randomUUID17 } from "crypto";
108977
109253
  import { join as join99 } from "path";
108978
109254
  async function runV2Path(ctx) {
108979
109255
  const logger = getLogger2();
@@ -109214,7 +109490,7 @@ var init_context3 = __esm(() => {
109214
109490
  createOrchestrator: createDefaultOrchestrator,
109215
109491
  loadPlugins: loadPluginProviders,
109216
109492
  v1FeatureProvider: () => new FeatureContextProvider,
109217
- uuid: () => randomUUID16(),
109493
+ uuid: () => randomUUID17(),
109218
109494
  readDigest: readDigestFile,
109219
109495
  writeDigest: writeDigestFile,
109220
109496
  loadFeatureManifests,
@@ -109298,12 +109574,12 @@ var init_execution = __esm(() => {
109298
109574
  init_engine();
109299
109575
  init_errors4();
109300
109576
  init_execution2();
109301
- init_interaction2();
109577
+ init_interaction3();
109302
109578
  init_logger2();
109303
109579
  init_git2();
109304
109580
  init_path_frame();
109305
109581
  init_scope_files();
109306
- init_interaction2();
109582
+ init_interaction3();
109307
109583
  init_execution_helpers();
109308
109584
  init_execution_helpers();
109309
109585
  RUNTIME_CRASH_CODES = new Set(["CALL_OP_NO_OUTPUT", "CALL_OP_MAX_RETRIES"]);
@@ -109500,7 +109776,7 @@ var init_optimizer2 = __esm(() => {
109500
109776
 
109501
109777
  // src/pipeline/stages/prompt.ts
109502
109778
  var _promptStageDeps, promptStage;
109503
- var init_prompt3 = __esm(() => {
109779
+ var init_prompt4 = __esm(() => {
109504
109780
  init_acceptance2();
109505
109781
  init_engine();
109506
109782
  init_logger2();
@@ -109858,7 +110134,7 @@ var init_stages = __esm(() => {
109858
110134
  init_context3();
109859
110135
  init_execution();
109860
110136
  init_optimizer2();
109861
- init_prompt3();
110137
+ init_prompt4();
109862
110138
  init_queue_check();
109863
110139
  init_routing2();
109864
110140
  init_acceptance3();
@@ -109868,7 +110144,7 @@ var init_stages = __esm(() => {
109868
110144
  init_context3();
109869
110145
  init_execution();
109870
110146
  init_optimizer2();
109871
- init_prompt3();
110147
+ init_prompt4();
109872
110148
  init_queue_check();
109873
110149
  init_routing2();
109874
110150
  defaultPipeline = new Proxy([], {
@@ -110495,7 +110771,7 @@ var init_pipeline_result_handler = __esm(() => {
110495
110771
  init_escalation();
110496
110772
  init_progress();
110497
110773
  _resultHandlerDeps = {
110498
- spawn,
110774
+ spawn: spawn2,
110499
110775
  existsSync: existsSync25,
110500
110776
  worktreeManager: new WorktreeManager,
110501
110777
  mergeEngine: new MergeEngine(new WorktreeManager),
@@ -110856,7 +111132,8 @@ async function executeHook(hookDef, ctx, workdir) {
110856
111132
  stdin: new Response(contextJson),
110857
111133
  stdout: "pipe",
110858
111134
  stderr: "pipe",
110859
- env: buildAllowedEnv({ env: env2 })
111135
+ env: buildAllowedEnv({ env: env2 }),
111136
+ detached: true
110860
111137
  });
110861
111138
  let timedOut = false;
110862
111139
  let killTimeoutId;
@@ -111245,7 +111522,7 @@ async function openAcceptanceFixScope(ctx, runtime2, storyId, packageDir) {
111245
111522
  }
111246
111523
  var _acceptanceFixScopeDeps;
111247
111524
  var init_acceptance_fix_scope = __esm(() => {
111248
- init_interaction2();
111525
+ init_interaction3();
111249
111526
  init_path_frame();
111250
111527
  _acceptanceFixScopeDeps = {
111251
111528
  buildRunDispatchAskWiring
@@ -111759,7 +112036,7 @@ var init_headless_formatter = __esm(() => {
111759
112036
 
111760
112037
  // src/execution/lifecycle/run-tmp-wipe.ts
111761
112038
  import { lstatSync as lstatSync4 } from "fs";
111762
- import { rm as rm10 } from "fs/promises";
112039
+ import { rm as rm11 } from "fs/promises";
111763
112040
  function inspect(path19) {
111764
112041
  try {
111765
112042
  return _runTmpWipeDeps.exists(path19) ? { state: "present" } : { state: "absent" };
@@ -111799,7 +112076,7 @@ var init_run_tmp_wipe = __esm(() => {
111799
112076
  init_internal();
111800
112077
  init_logger2();
111801
112078
  _runTmpWipeDeps = {
111802
- remove: (path19) => rm10(path19, { recursive: true, force: true }),
112079
+ remove: (path19) => rm11(path19, { recursive: true, force: true }),
111803
112080
  exists: (path19) => {
111804
112081
  try {
111805
112082
  lstatSync4(path19);
@@ -111814,7 +112091,7 @@ var init_run_tmp_wipe = __esm(() => {
111814
112091
  });
111815
112092
 
111816
112093
  // src/execution/lifecycle/scratchpad-wipe.ts
111817
- import { rm as rm11 } from "fs/promises";
112094
+ import { rm as rm12 } from "fs/promises";
111818
112095
  import { join as join102 } from "path";
111819
112096
  async function wipeScratchpad(workdir, opts = {}) {
111820
112097
  if (opts.dryRun)
@@ -111837,7 +112114,7 @@ var init_scratchpad_wipe = __esm(() => {
111837
112114
  init_internal();
111838
112115
  init_logger2();
111839
112116
  _scratchpadWipeDeps = {
111840
- remove: (path19) => rm11(path19, { recursive: true, force: true })
112117
+ remove: (path19) => rm12(path19, { recursive: true, force: true })
111841
112118
  };
111842
112119
  });
111843
112120
 
@@ -112992,7 +113269,7 @@ async function runDeferredRegression(options) {
112992
113269
  var _regressionDeps;
112993
113270
  var init_run_regression = __esm(() => {
112994
113271
  init_findings();
112995
- init_interaction2();
113272
+ init_interaction3();
112996
113273
  init_logger2();
112997
113274
  init_operations();
112998
113275
  init_event_bus();
@@ -113220,7 +113497,7 @@ var init_rtk = __esm(() => {
113220
113497
  });
113221
113498
 
113222
113499
  // src/execution/status-file.ts
113223
- import { rename as rename8, unlink as unlink8 } from "fs/promises";
113500
+ import { rename as rename9, unlink as unlink8 } from "fs/promises";
113224
113501
  import { resolve as resolve39 } from "path";
113225
113502
  function countProgress(prd) {
113226
113503
  const stories = prd.userStories;
@@ -113284,7 +113561,7 @@ async function writeStatusFile(filePath2, status) {
113284
113561
  await unlink8(tmpPath);
113285
113562
  } catch {}
113286
113563
  await Bun.write(tmpPath, JSON.stringify(status, null, 2));
113287
- await rename8(tmpPath, resolvedPath);
113564
+ await rename9(tmpPath, resolvedPath);
113288
113565
  }
113289
113566
  var init_status_file = __esm(() => {
113290
113567
  init_errors4();
@@ -114287,15 +114564,15 @@ function createOtelReporterPlugin(cfg, deps, workdir) {
114287
114564
  send: makeSendLogsBatch(resource)
114288
114565
  });
114289
114566
  const buildOrphanState = (startMs) => {
114290
- const identity = rootSpanIdentity();
114567
+ const identity2 = rootSpanIdentity();
114291
114568
  const orphanAttrs = buildResourceAttributes({ serviceName: cfg.serviceName, runId: "orphan" });
114292
114569
  return {
114293
- ...identity,
114570
+ ...identity2,
114294
114571
  startMs,
114295
114572
  feature: "",
114296
114573
  project: "",
114297
114574
  events: [],
114298
- spanTree: createSpanTree(identity.traceId, identity.spanId),
114575
+ spanTree: createSpanTree(identity2.traceId, identity2.spanId),
114299
114576
  spanQueue: makeSpanQueue(orphanAttrs),
114300
114577
  metrics: createPhaseMetricsAggregator(),
114301
114578
  storyBounds: new Map,
@@ -114373,7 +114650,7 @@ function createOtelReporterPlugin(cfg, deps, workdir) {
114373
114650
  const reporter = {
114374
114651
  name: STAGE8,
114375
114652
  async onRunStart(event) {
114376
- const identity = rootSpanIdentity();
114653
+ const identity2 = rootSpanIdentity();
114377
114654
  const runId = event.runId;
114378
114655
  let gitBranch;
114379
114656
  let gitSha;
@@ -114401,14 +114678,14 @@ function createOtelReporterPlugin(cfg, deps, workdir) {
114401
114678
  git: { branch: gitBranch, sha: gitSha }
114402
114679
  });
114403
114680
  const state = {
114404
- ...identity,
114681
+ ...identity2,
114405
114682
  startMs: Date.parse(event.startTime),
114406
114683
  feature: event.feature,
114407
114684
  project: event.project ?? "",
114408
114685
  gitBranch,
114409
114686
  gitSha,
114410
114687
  events: [],
114411
- spanTree: createSpanTree(identity.traceId, identity.spanId),
114688
+ spanTree: createSpanTree(identity2.traceId, identity2.spanId),
114412
114689
  spanQueue: makeSpanQueue(resourceAttrs),
114413
114690
  metrics: createPhaseMetricsAggregator(),
114414
114691
  storyBounds: new Map,
@@ -115160,7 +115437,7 @@ async function* streamJsonlLines(file2) {
115160
115437
  }
115161
115438
 
115162
115439
  // src/plugins/builtin/curator/rollup-prune.ts
115163
- import { appendFile as appendFile6, rename as rename9, unlink as unlink9, writeFile as writeFile13 } from "fs/promises";
115440
+ import { appendFile as appendFile6, rename as rename10, unlink as unlink9, writeFile as writeFile12 } from "fs/promises";
115164
115441
  async function scanProjectRunIds(rollupPath, projectKey) {
115165
115442
  const maxTsByRunId = new Map;
115166
115443
  for await (const line of streamJsonlLines(Bun.file(rollupPath))) {
@@ -115205,7 +115482,7 @@ async function pruneRollupUnlocked(input) {
115205
115482
  buffer = "";
115206
115483
  };
115207
115484
  try {
115208
- await writeFile13(tmpPath, "");
115485
+ await writeFile12(tmpPath, "");
115209
115486
  for await (const line of streamJsonlLines(Bun.file(rollupPath))) {
115210
115487
  if (!line.trim())
115211
115488
  continue;
@@ -115241,7 +115518,7 @@ async function pruneRollupUnlocked(input) {
115241
115518
  await flush();
115242
115519
  }
115243
115520
  await flush();
115244
- await rename9(tmpPath, rollupPath);
115521
+ await rename10(tmpPath, rollupPath);
115245
115522
  } catch (err) {
115246
115523
  await unlink9(tmpPath).catch(() => {});
115247
115524
  throw err;
@@ -115640,8 +115917,8 @@ function collectRectify(context, entry2, data) {
115640
115917
  ts: stringValue(entry2.timestamp ?? entry2.ts, now()),
115641
115918
  kind: "rectify-cycle",
115642
115919
  payload: {
115643
- iteration: numberValue(data.attempt ?? data.rectifyAttempt ?? data.iteration, 1),
115644
- status: stringValue(data.status, "started")
115920
+ iteration: numberValue(data.iterationCount, 1),
115921
+ status: data.exitReason === "resolved" ? "passed" : "failed"
115645
115922
  }
115646
115923
  };
115647
115924
  }
@@ -115733,7 +116010,7 @@ async function collectFromRunJsonl(context) {
115733
116010
  observations.push(collectPullCall(context, entry2, data));
115734
116011
  } else if (stage === "acceptance" && message === "verdict") {
115735
116012
  observations.push(collectAcceptanceVerdict(context, entry2, data));
115736
- } else if (stage === "rectify" && message === "Starting rectification loop") {
116013
+ } else if (stage === "story-orchestrator" && typeof data.iterationCount === "number") {
115737
116014
  observations.push(collectRectify(context, entry2, data));
115738
116015
  } else if (stage === "escalation" && message.includes("Escalating")) {
115739
116016
  observations.push(collectEscalation(context, entry2, data));
@@ -116098,7 +116375,7 @@ function renderProposals(proposals, runId, observationCount, provenance) {
116098
116375
  }
116099
116376
 
116100
116377
  // src/plugins/builtin/curator/rollup.ts
116101
- import { appendFile as appendFile7, mkdir as mkdir30, writeFile as writeFile14 } from "fs/promises";
116378
+ import { appendFile as appendFile7, mkdir as mkdir30, writeFile as writeFile13 } from "fs/promises";
116102
116379
  import * as path22 from "path";
116103
116380
  async function appendToRollup(observations, rollupPath) {
116104
116381
  const dir = path22.dirname(rollupPath);
@@ -116111,7 +116388,7 @@ async function appendToRollupUnlocked(observations, rollupPath) {
116111
116388
  if (observations.length === 0) {
116112
116389
  const f = Bun.file(rollupPath);
116113
116390
  if (!await f.exists()) {
116114
- await writeFile14(rollupPath, "");
116391
+ await writeFile13(rollupPath, "");
116115
116392
  }
116116
116393
  return;
116117
116394
  }
@@ -117183,7 +117460,7 @@ var init_language_commands = __esm(() => {
117183
117460
  });
117184
117461
 
117185
117462
  // src/review/scoped-lint.ts
117186
- import { join as join109, relative as relative32 } from "path";
117463
+ import { join as join109, relative as relative33 } from "path";
117187
117464
  function normalizePath3(path25) {
117188
117465
  return path25.replaceAll("\\", "/").replace(/^\.\//, "");
117189
117466
  }
@@ -117209,7 +117486,7 @@ async function listChangedFiles(workdir, baseRef) {
117209
117486
  function inferActivePackageDir(workdir, projectDir) {
117210
117487
  if (!projectDir)
117211
117488
  return;
117212
- const rel = normalizePath3(relative32(projectDir, workdir));
117489
+ const rel = normalizePath3(relative33(projectDir, workdir));
117213
117490
  if (!rel || rel === "." || rel.startsWith(".."))
117214
117491
  return;
117215
117492
  return rel;
@@ -117816,7 +118093,7 @@ var init_run_initialization = __esm(() => {
117816
118093
  },
117817
118094
  hasCommitsForStory: (workdir, storyId) => hasCommitsForStory(workdir, storyId),
117818
118095
  runReview: (reviewConfig, workdir, executionConfig) => runReview({ config: reviewConfig, workdir, executionConfig }),
117819
- spawn
118096
+ spawn: spawn2
117820
118097
  };
117821
118098
  });
117822
118099
 
@@ -118085,9 +118362,9 @@ async function resolveProjectAsync(options = {}) {
118085
118362
  const identityFile = Bun.file(registryIdentityPath);
118086
118363
  if (await identityFile.exists()) {
118087
118364
  try {
118088
- const identity = await identityFile.json();
118089
- if (typeof identity.workdir === "string") {
118090
- return resolveProject({ ...options, dir: identity.workdir });
118365
+ const identity2 = await identityFile.json();
118366
+ if (typeof identity2.workdir === "string") {
118367
+ return resolveProject({ ...options, dir: identity2.workdir });
118091
118368
  }
118092
118369
  } catch {}
118093
118370
  }
@@ -118294,8 +118571,8 @@ function resolveCanonicalTargetPath(projectDir, canonicalFile) {
118294
118571
  const root = resolve42(projectDir);
118295
118572
  if (target !== root && !target.startsWith(root + sep21))
118296
118573
  return null;
118297
- const relative33 = target.slice(root.length + 1).replaceAll(sep21, "/");
118298
- return CURATOR_TARGET_SHAPES.some((shape) => shape.test(relative33)) ? target : null;
118574
+ const relative34 = target.slice(root.length + 1).replaceAll(sep21, "/");
118575
+ return CURATOR_TARGET_SHAPES.some((shape) => shape.test(relative34)) ? target : null;
118299
118576
  }
118300
118577
  function isWithinCanonicalRulesDir(projectDir, targetPath) {
118301
118578
  const rulesRoot = resolve42(projectDir, CANONICAL_RULES_DIR);
@@ -118881,7 +119158,7 @@ __export(exports_migrate, {
118881
119158
  partitionTrackedCandidates: () => partitionTrackedCandidates
118882
119159
  });
118883
119160
  import { existsSync as existsSync30 } from "fs";
118884
- import { mkdir as mkdir32, readdir as readdir10, rename as rename10 } from "fs/promises";
119161
+ import { mkdir as mkdir32, readdir as readdir10, rename as rename11 } from "fs/promises";
118885
119162
  import path26 from "path";
118886
119163
  async function detectGeneratedContent(naxDir) {
118887
119164
  if (!existsSync30(naxDir))
@@ -119066,7 +119343,7 @@ async function migrateCommand(options) {
119066
119343
  const archiveBase = path26.join(globalConfigDir(), "_archive");
119067
119344
  const archiveDest = path26.join(archiveBase, `${options.reclaim}-${Date.now()}`);
119068
119345
  await mkdir32(archiveBase, { recursive: true });
119069
- await rename10(src, archiveDest);
119346
+ await rename11(src, archiveDest);
119070
119347
  logger.info("migrate", `Reclaimed: archived to ${archiveDest}`, { storyId: "_migrate" });
119071
119348
  return;
119072
119349
  }
@@ -119165,7 +119442,7 @@ async function migrateCommand(options) {
119165
119442
  Remove the destination or run nax migrate --dry-run to inspect.`, "MIGRATE_CONFLICT", { stage: "migrate", src: candidate.srcPath, dest });
119166
119443
  }
119167
119444
  try {
119168
- await rename10(candidate.srcPath, dest);
119445
+ await rename11(candidate.srcPath, dest);
119169
119446
  } catch (err) {
119170
119447
  const isXdev = err instanceof Error && "code" in err && err.code === "EXDEV";
119171
119448
  if (isXdev) {
@@ -120653,7 +120930,7 @@ var init_run_setup = __esm(() => {
120653
120930
  init_internal();
120654
120931
  init_errors4();
120655
120932
  init_rtk();
120656
- init_interaction2();
120933
+ init_interaction3();
120657
120934
  init_logger2();
120658
120935
  init_event_bus();
120659
120936
  init_prd();
@@ -121096,7 +121373,7 @@ async function cleanupSessionOnFailure(ctx, failAndClose2) {
121096
121373
  }
121097
121374
 
121098
121375
  // src/execution/deferred-review.ts
121099
- var {spawn: spawn6 } = globalThis.Bun;
121376
+ var {spawn: spawn7 } = globalThis.Bun;
121100
121377
  async function spawnGitWithDeadline(args, workdir) {
121101
121378
  const proc = _deferredReviewDeps.spawn({
121102
121379
  cmd: hardenedGitArgv(["git", ...args]),
@@ -121176,7 +121453,7 @@ var init_deferred_review = __esm(() => {
121176
121453
  init_internal();
121177
121454
  init_git2();
121178
121455
  init_path_filters();
121179
- _deferredReviewDeps = { spawn: spawn6 };
121456
+ _deferredReviewDeps = { spawn: spawn7 };
121180
121457
  });
121181
121458
 
121182
121459
  // src/execution/lifecycle/test-baseline-capture.ts
@@ -122019,7 +122296,7 @@ var init_queue_file_lock = __esm(() => {
122019
122296
  });
122020
122297
 
122021
122298
  // src/execution/queue-handler.ts
122022
- import { rename as rename11, unlink as unlink12 } from "fs/promises";
122299
+ import { rename as rename12, unlink as unlink12 } from "fs/promises";
122023
122300
  import path29 from "path";
122024
122301
  function getSafeLogger9() {
122025
122302
  try {
@@ -122039,7 +122316,7 @@ async function claimCommandsLocked(queuePath, processingPath, logger) {
122039
122316
  return null;
122040
122317
  }
122041
122318
  try {
122042
- await rename11(queuePath, processingPath);
122319
+ await rename12(queuePath, processingPath);
122043
122320
  } catch (error48) {
122044
122321
  logger?.warn("queue", "Failed to rename queue file for processing", {
122045
122322
  error: error48.message
@@ -122215,7 +122492,7 @@ var init_run_cost_reconcile = __esm(() => {
122215
122492
  });
122216
122493
 
122217
122494
  // src/finish/audit.ts
122218
- import { appendFile as appendFile8, mkdir as mkdir34, readFile as readFile10, writeFile as writeFile15 } from "fs/promises";
122495
+ import { appendFile as appendFile8, mkdir as mkdir34, readFile as readFile10, writeFile as writeFile14 } from "fs/promises";
122219
122496
  import { join as join120 } from "path";
122220
122497
  function roundsPath(t) {
122221
122498
  return join120(t.auditDir, `${t.runId}.jsonl`);
@@ -122243,7 +122520,7 @@ async function updateLedger(t, result) {
122243
122520
  };
122244
122521
  try {
122245
122522
  await mkdir34(t.auditDir, { recursive: true });
122246
- await writeFile15(ledgerPath(t.auditDir), `${JSON.stringify(entry2, null, 2)}
122523
+ await writeFile14(ledgerPath(t.auditDir), `${JSON.stringify(entry2, null, 2)}
122247
122524
  `, "utf8");
122248
122525
  } catch {}
122249
122526
  }
@@ -122293,7 +122570,7 @@ async function writeResult(t, result, options = {}) {
122293
122570
  const rounds = await readRounds(t);
122294
122571
  const withRounds = rounds.length > 0 ? { ...result, rounds } : result;
122295
122572
  await mkdir34(t.auditDir, { recursive: true });
122296
- await writeFile15(resultPath(t), `${JSON.stringify(withRounds, null, 2)}
122573
+ await writeFile14(resultPath(t), `${JSON.stringify(withRounds, null, 2)}
122297
122574
  `, "utf8");
122298
122575
  if (options.ledger === false)
122299
122576
  return;
@@ -122826,7 +123103,7 @@ function languageRunner(language) {
122826
123103
  function buildAcceptanceCommand(repoRoot, group) {
122827
123104
  const absFile = shellQuote(`${repoRoot}/${group.testPath}`);
122828
123105
  const template = group.command ?? `${languageRunner(group.language)} {{FILE}}`;
122829
- return template.replace(/\{\{FILE\}\}|\{\{file\}\}|\{\{files\}\}/g, absFile);
123106
+ return template.replace(/\{\{FILE\}\}|\{\{file\}\}|\{\{files\}\}/g, () => absFile);
122830
123107
  }
122831
123108
  async function runAcceptanceGate(repoRoot, groups, opts = {}) {
122832
123109
  const chunks = [];
@@ -124465,7 +124742,7 @@ var _finishPhaseDeps;
124465
124742
  var init_phase = __esm(() => {
124466
124743
  init_internal();
124467
124744
  init_forge();
124468
- init_interaction2();
124745
+ init_interaction3();
124469
124746
  init_logger2();
124470
124747
  init_pipeline2();
124471
124748
  init_runtime3();
@@ -125054,13 +125331,13 @@ function wireInteraction(bus, interactionChain, config2) {
125054
125331
  u();
125055
125332
  };
125056
125333
  }
125057
- var init_interaction3 = __esm(() => {
125058
- init_interaction2();
125334
+ var init_interaction4 = __esm(() => {
125335
+ init_interaction3();
125059
125336
  init_logger2();
125060
125337
  });
125061
125338
 
125062
125339
  // src/pipeline/subscribers/registry.ts
125063
- import { mkdir as mkdir36, writeFile as writeFile16 } from "fs/promises";
125340
+ import { mkdir as mkdir36, writeFile as writeFile15 } from "fs/promises";
125064
125341
  import { basename as basename16, join as join124 } from "path";
125065
125342
  function wireRegistry(bus, feature, runId, workdir, outputDir) {
125066
125343
  const logger = getSafeLogger2();
@@ -125080,7 +125357,7 @@ function wireRegistry(bus, feature, runId, workdir, outputDir) {
125080
125357
  eventsDir: join124(outputDir, "features", feature, "runs"),
125081
125358
  registeredAt: new Date().toISOString()
125082
125359
  };
125083
- await writeFile16(metaFile, JSON.stringify(meta3, null, 2));
125360
+ await writeFile15(metaFile, JSON.stringify(meta3, null, 2));
125084
125361
  } catch (err) {
125085
125362
  logger?.warn("registry-writer", "Failed to write meta.json (non-fatal)", {
125086
125363
  path: metaFile,
@@ -126356,7 +126633,7 @@ var init_unified_executor = __esm(() => {
126356
126633
  init_runner2();
126357
126634
  init_events_writer();
126358
126635
  init_hooks2();
126359
- init_interaction3();
126636
+ init_interaction4();
126360
126637
  init_registry8();
126361
126638
  init_reporters();
126362
126639
  init_prd();
@@ -128014,7 +128291,7 @@ __export(exports_story_size_gate, {
128014
128291
  function countBulletPoints(text) {
128015
128292
  const lines2 = text.split(`
128016
128293
  `);
128017
- const bulletPattern = /^\s*[-*\u2022]|\d+\./;
128294
+ const bulletPattern = /^\s*(?:[-*\u2022]|\d+\.)\s/;
128018
128295
  return lines2.filter((line) => bulletPattern.test(line)).length;
128019
128296
  }
128020
128297
  function analyzeStory(story, config2) {
@@ -128661,7 +128938,7 @@ var init_plan_runtime = __esm(() => {
128661
128938
  init_analyze();
128662
128939
  init_config();
128663
128940
  init_generator2();
128664
- init_interaction2();
128941
+ init_interaction3();
128665
128942
  init_logger2();
128666
128943
  init_runtime3();
128667
128944
  init_plan_helpers();
@@ -128817,7 +129094,7 @@ var init_context_builder = __esm(() => {
128817
129094
  init_plan_runtime();
128818
129095
  init_config();
128819
129096
  init_errors4();
128820
- init_interaction2();
129097
+ init_interaction3();
128821
129098
  init_logger2();
128822
129099
  init_feature_name();
128823
129100
  init_spec_lint_gate();
@@ -131834,9 +132111,7 @@ __export(exports_cli, {
131834
132111
  MAX_CANONICAL_RULE_GLOB_FILES: () => MAX_CANONICAL_RULE_GLOB_FILES,
131835
132112
  MAX_DEAD_GLOB_SCAN_FILES: () => MAX_DEAD_GLOB_SCAN_FILES,
131836
132113
  MAX_DEAD_GLOB_SCAN_TOTAL_ENTRIES: () => MAX_DEAD_GLOB_SCAN_TOTAL_ENTRIES,
131837
- PromptCancelledError: () => PromptCancelledError,
131838
132114
  _approvalsCliDeps: () => _approvalsCliDeps,
131839
- _authPromptDeps: () => _authPromptDeps,
131840
132115
  _cliAuthDeps: () => _cliAuthDeps,
131841
132116
  _cliTrustDeps: () => _cliTrustDeps,
131842
132117
  _confirmDeps: () => _confirmDeps,
@@ -131896,8 +132171,6 @@ __export(exports_cli, {
131896
132171
  planMigration: () => planMigration,
131897
132172
  pluginsListCommand: () => pluginsListCommand,
131898
132173
  promptForConfirmation: () => promptForConfirmation,
131899
- promptForLine: () => promptForLine,
131900
- promptForSecret: () => promptForSecret,
131901
132174
  promptsCommand: () => promptsCommand,
131902
132175
  promptsInitCommand: () => promptsInitCommand,
131903
132176
  registerApprovalsCommand: () => registerApprovalsCommand,
@@ -131937,7 +132210,6 @@ var init_cli2 = __esm(() => {
131937
132210
  init_approvals();
131938
132211
  init_approvals_format();
131939
132212
  init_auth3();
131940
- init_auth_prompt();
131941
132213
  init_config2();
131942
132214
  init_config_descriptions();
131943
132215
  init_confirm();
@@ -153098,9 +153370,9 @@ function parseSchedule(input, now3) {
153098
153370
  const trimmed = input.trim();
153099
153371
  if (trimmed === "")
153100
153372
  return { ok: false, error: `Empty schedule value. ${ACCEPTED}` };
153101
- const relative33 = parseRelative(trimmed, now3);
153102
- if (relative33)
153103
- return relative33;
153373
+ const relative34 = parseRelative(trimmed, now3);
153374
+ if (relative34)
153375
+ return relative34;
153104
153376
  const timeOfDay = parseTimeOfDay(trimmed, now3);
153105
153377
  if (timeOfDay)
153106
153378
  return timeOfDay;