@bastani/atomic 0.9.61-alpha.1 → 0.9.61-alpha.3

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 (72) hide show
  1. package/CHANGELOG.md +68 -4
  2. package/dist/builtin/intercom/index.bundle.mjs +3 -3
  3. package/dist/builtin/intercom/package.json +1 -1
  4. package/dist/builtin/mcp/package.json +1 -1
  5. package/dist/builtin/subagents/CHANGELOG.md +12 -0
  6. package/dist/builtin/subagents/package.json +1 -1
  7. package/dist/builtin/web-access/package.json +1 -1
  8. package/dist/builtin/workflows/CHANGELOG.md +14 -0
  9. package/dist/builtin/workflows/README.md +9 -7
  10. package/dist/builtin/workflows/builtin/{chunk-h6bt0r0g.js → chunk-a2j0x1a6.js} +1 -1
  11. package/dist/builtin/workflows/builtin/{chunk-g3b2zf8r.js → chunk-vzbec50q.js} +1 -1
  12. package/dist/builtin/workflows/builtin/goal.js +1 -1
  13. package/dist/builtin/workflows/builtin/index.js +2 -2
  14. package/dist/builtin/workflows/builtin/ralph.js +1 -1
  15. package/dist/builtin/workflows/package.json +1 -1
  16. package/dist/builtin/workflows/src/extension/index.bundle.mjs +828 -418
  17. package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +905 -429
  18. package/dist/builtin/workflows/src/index.js +798 -393
  19. package/dist/builtin/workflows/src/local-worker-entry.mjs +905 -429
  20. package/dist/core/agent-session-retry.d.ts.map +1 -1
  21. package/dist/core/agent-session-retry.js +12 -3
  22. package/dist/core/agent-session-retry.js.map +1 -1
  23. package/dist/core/model-fallback-failures.d.ts +14 -4
  24. package/dist/core/model-fallback-failures.d.ts.map +1 -1
  25. package/dist/core/model-fallback-failures.js +43 -15
  26. package/dist/core/model-fallback-failures.js.map +1 -1
  27. package/dist/core/settings-defaults.d.ts +1 -0
  28. package/dist/core/settings-defaults.d.ts.map +1 -1
  29. package/dist/core/settings-defaults.js +1 -0
  30. package/dist/core/settings-defaults.js.map +1 -1
  31. package/dist/core/settings-manager-basic-accessors.d.ts +1 -0
  32. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  33. package/dist/core/settings-manager-basic-accessors.js +2 -0
  34. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  35. package/dist/core/settings-schema.d.ts +2 -0
  36. package/dist/core/settings-schema.d.ts.map +1 -1
  37. package/dist/core/settings-schema.js +4 -0
  38. package/dist/core/settings-schema.js.map +1 -1
  39. package/dist/core/slash-commands.js +2 -2
  40. package/dist/core/slash-commands.js.map +1 -1
  41. package/dist/index.d.ts +1 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js.map +1 -1
  44. package/dist/modes/interactive/interactive-startup-chat-container.d.ts +2 -0
  45. package/dist/modes/interactive/interactive-startup-chat-container.d.ts.map +1 -1
  46. package/dist/modes/interactive/interactive-startup-chat-container.js +6 -0
  47. package/dist/modes/interactive/interactive-startup-chat-container.js.map +1 -1
  48. package/dist/modes/interactive-engine/engine-render-service.d.ts +8 -0
  49. package/dist/modes/interactive-engine/engine-render-service.d.ts.map +1 -1
  50. package/dist/modes/interactive-engine/engine-render-service.js +28 -6
  51. package/dist/modes/interactive-engine/engine-render-service.js.map +1 -1
  52. package/dist/modes/interactive-engine/remote-renderer.d.ts +33 -5
  53. package/dist/modes/interactive-engine/remote-renderer.d.ts.map +1 -1
  54. package/dist/modes/interactive-engine/remote-renderer.js +91 -17
  55. package/dist/modes/interactive-engine/remote-renderer.js.map +1 -1
  56. package/dist/modes/rpc/queued-writer.d.ts +2 -0
  57. package/dist/modes/rpc/queued-writer.d.ts.map +1 -1
  58. package/dist/modes/rpc/queued-writer.js +8 -0
  59. package/dist/modes/rpc/queued-writer.js.map +1 -1
  60. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  61. package/dist/modes/rpc/rpc-client.js +4 -0
  62. package/dist/modes/rpc/rpc-client.js.map +1 -1
  63. package/docs/intercom/reference.md +1 -1
  64. package/docs/models/evals.md +30 -2
  65. package/docs/settings.md +3 -0
  66. package/docs/subagents/reference.md +3 -1
  67. package/docs/workflows/api-reference.md +9 -6
  68. package/docs/workflows/builtins.md +1 -1
  69. package/docs/workflows/operations.md +4 -4
  70. package/npm-shrinkwrap.json +32 -32
  71. package/package.json +3 -3
  72. package/schemas/settings.schema.json +5 -0
@@ -13456,9 +13456,104 @@ var init_workflow_artifacts = __esm(() => {
13456
13456
  pendingArtifactPrunes = new Map;
13457
13457
  });
13458
13458
 
13459
- // dist/builtin/workflows/src/runs/shared/worktree-generation.ts
13459
+ // dist/builtin/workflows/src/runs/shared/worktree-allocation.ts
13460
+ import { randomBytes } from "node:crypto";
13460
13461
  import * as fs from "node:fs";
13461
13462
  import * as path from "node:path";
13463
+ function nextBucketId() {
13464
+ return nextBucketIdOverride?.() ?? randomBytes(2).toString("hex");
13465
+ }
13466
+ function errorCode(error) {
13467
+ return error && typeof error === "object" && "code" in error ? String(error.code) : undefined;
13468
+ }
13469
+ function allocateWorktreeCheckoutPath(root, repositoryName) {
13470
+ for (let attempt = 0;attempt < MAX_BUCKET_ATTEMPTS; attempt++) {
13471
+ const bucket = path.join(root, nextBucketId());
13472
+ try {
13473
+ fs.mkdirSync(bucket);
13474
+ return path.join(bucket, repositoryName);
13475
+ } catch (error) {
13476
+ if (errorCode(error) === "EEXIST")
13477
+ continue;
13478
+ const message = error instanceof Error ? error.message : String(error);
13479
+ throw new Error(`Failed to create worktree bucket ${bucket} under gitWorktreeDir ${root}: ${message}`);
13480
+ }
13481
+ }
13482
+ throw new Error(`Every worktree bucket identifier under gitWorktreeDir ${root} is already in use.`);
13483
+ }
13484
+ function removeEmptyWorktreeBucket(checkout) {
13485
+ try {
13486
+ fs.rmdirSync(path.dirname(checkout));
13487
+ } catch {}
13488
+ }
13489
+ function linkedGitDirectory(checkout) {
13490
+ let pointer;
13491
+ try {
13492
+ if (!fs.lstatSync(path.join(checkout, ".git")).isFile())
13493
+ return;
13494
+ pointer = fs.readFileSync(path.join(checkout, ".git"), "utf8");
13495
+ } catch {
13496
+ return;
13497
+ }
13498
+ if (!pointer.startsWith("gitdir:"))
13499
+ return;
13500
+ const gitDir = pointer.slice("gitdir:".length).trim();
13501
+ return gitDir ? path.resolve(checkout, gitDir) : undefined;
13502
+ }
13503
+ function readWorktreeOwner(gitDir) {
13504
+ let contents;
13505
+ try {
13506
+ contents = fs.readFileSync(path.join(gitDir, WORKTREE_OWNER_FILENAME), "utf8");
13507
+ } catch {
13508
+ return;
13509
+ }
13510
+ try {
13511
+ const record = JSON.parse(contents);
13512
+ return record.version === WORKTREE_OWNER_VERSION && typeof record.ownerRunId === "string" && record.ownerRunId ? record.ownerRunId : undefined;
13513
+ } catch {
13514
+ return;
13515
+ }
13516
+ }
13517
+ function findOwnedWorktreeCheckouts(root, repositoryName, ownerRunId) {
13518
+ let entries;
13519
+ try {
13520
+ entries = fs.readdirSync(root, { withFileTypes: true });
13521
+ } catch {
13522
+ return [];
13523
+ }
13524
+ const owned = [];
13525
+ for (const entry of entries) {
13526
+ if (!entry.isDirectory() || !BUCKET_PATTERN.test(entry.name))
13527
+ continue;
13528
+ const checkout = path.join(root, entry.name, repositoryName);
13529
+ try {
13530
+ if (!fs.lstatSync(checkout).isDirectory())
13531
+ continue;
13532
+ } catch {
13533
+ continue;
13534
+ }
13535
+ const gitDir = linkedGitDirectory(checkout);
13536
+ if (gitDir !== undefined && readWorktreeOwner(gitDir) === ownerRunId)
13537
+ owned.push(checkout);
13538
+ }
13539
+ return owned.sort();
13540
+ }
13541
+ function bindWorktreeOwner(gitDir, ownerRunId) {
13542
+ const record = { version: WORKTREE_OWNER_VERSION, ownerRunId };
13543
+ fs.writeFileSync(path.join(gitDir, WORKTREE_OWNER_FILENAME), `${JSON.stringify(record)}
13544
+ `, {
13545
+ encoding: "utf8",
13546
+ flag: "wx"
13547
+ });
13548
+ }
13549
+ var WORKTREE_OWNER_FILENAME = "atomic-workflow-run.json", WORKTREE_OWNER_VERSION = 1, BUCKET_PATTERN, MAX_BUCKET_ATTEMPTS = 65536, nextBucketIdOverride;
13550
+ var init_worktree_allocation = __esm(() => {
13551
+ BUCKET_PATTERN = /^[0-9a-f]{4}$/;
13552
+ });
13553
+
13554
+ // dist/builtin/workflows/src/runs/shared/worktree-generation.ts
13555
+ import * as fs2 from "node:fs";
13556
+ import * as path2 from "node:path";
13462
13557
  function sameFileIdentity(left, right) {
13463
13558
  return left.dev === right.dev && left.ino === right.ino;
13464
13559
  }
@@ -13468,7 +13563,7 @@ function openMainCheckoutAnchor(gitDir) {
13468
13563
  assertCurrent() {
13469
13564
  if (disposed)
13470
13565
  throw new Error(`cached .git identity anchor is already disposed: ${gitDir}`);
13471
- if (!fs.lstatSync(gitDir).isDirectory()) {
13566
+ if (!fs2.lstatSync(gitDir).isDirectory()) {
13472
13567
  throw new Error(`cached main-checkout .git directory is no longer current: ${gitDir}`);
13473
13568
  }
13474
13569
  },
@@ -13478,17 +13573,17 @@ function openMainCheckoutAnchor(gitDir) {
13478
13573
  };
13479
13574
  }
13480
13575
  function openGitWorktreeGenerationAnchor(worktreeRoot) {
13481
- const gitFile = path.join(worktreeRoot, ".git");
13482
- if (fs.lstatSync(gitFile).isDirectory())
13576
+ const gitFile = path2.join(worktreeRoot, ".git");
13577
+ if (fs2.lstatSync(gitFile).isDirectory())
13483
13578
  return openMainCheckoutAnchor(gitFile);
13484
- const descriptor = fs.openSync(gitFile, "r");
13579
+ const descriptor = fs2.openSync(gitFile, "r");
13485
13580
  let disposed = false;
13486
13581
  const anchor = {
13487
13582
  assertCurrent() {
13488
13583
  if (disposed)
13489
13584
  throw new Error(`cached .git identity anchor is already disposed: ${gitFile}`);
13490
- const held = fs.fstatSync(descriptor, { bigint: true });
13491
- const current = fs.lstatSync(gitFile, { bigint: true });
13585
+ const held = fs2.fstatSync(descriptor, { bigint: true });
13586
+ const current = fs2.lstatSync(gitFile, { bigint: true });
13492
13587
  if (!current.isFile() || !sameFileIdentity(held, current)) {
13493
13588
  throw new Error(`cached .git identity anchor no longer matches the selected checkout: ${gitFile}`);
13494
13589
  }
@@ -13497,7 +13592,7 @@ function openGitWorktreeGenerationAnchor(worktreeRoot) {
13497
13592
  if (disposed)
13498
13593
  return;
13499
13594
  disposed = true;
13500
- fs.closeSync(descriptor);
13595
+ fs2.closeSync(descriptor);
13501
13596
  }
13502
13597
  };
13503
13598
  try {
@@ -13611,84 +13706,84 @@ var init_worktree_git_runner = __esm(() => {
13611
13706
  });
13612
13707
 
13613
13708
  // dist/builtin/workflows/src/runs/shared/worktree-root.ts
13614
- import * as fs2 from "node:fs";
13615
- import * as path2 from "node:path";
13709
+ import * as fs3 from "node:fs";
13710
+ import * as path3 from "node:path";
13616
13711
  function pathFromPointer(contents, prefix, base) {
13617
13712
  if (!contents.startsWith(prefix))
13618
13713
  return;
13619
13714
  const value2 = contents.slice(prefix.length).trim();
13620
13715
  if (!value2)
13621
13716
  return;
13622
- return path2.resolve(base, value2);
13717
+ return path3.resolve(base, value2);
13623
13718
  }
13624
13719
  function readText(file) {
13625
13720
  try {
13626
- return fs2.readFileSync(file, "utf8");
13721
+ return fs3.readFileSync(file, "utf8");
13627
13722
  } catch {
13628
13723
  return;
13629
13724
  }
13630
13725
  }
13631
13726
  function samePath(left, right) {
13632
13727
  try {
13633
- const a = fs2.realpathSync.native(left);
13634
- const b = fs2.realpathSync.native(right);
13728
+ const a = fs3.realpathSync.native(left);
13729
+ const b = fs3.realpathSync.native(right);
13635
13730
  return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
13636
13731
  } catch {
13637
13732
  return false;
13638
13733
  }
13639
13734
  }
13640
13735
  function findGitRoot(cwd) {
13641
- let current = path2.resolve(cwd);
13736
+ let current = path3.resolve(cwd);
13642
13737
  try {
13643
- if (!fs2.statSync(current).isDirectory())
13644
- current = path2.dirname(current);
13738
+ if (!fs3.statSync(current).isDirectory())
13739
+ current = path3.dirname(current);
13645
13740
  } catch {
13646
13741
  return;
13647
13742
  }
13648
13743
  while (true) {
13649
13744
  try {
13650
- const entry = fs2.statSync(path2.join(current, ".git"));
13745
+ const entry = fs3.statSync(path3.join(current, ".git"));
13651
13746
  if (entry.isDirectory() || entry.isFile())
13652
13747
  return current;
13653
13748
  } catch {}
13654
- const parent = path2.dirname(current);
13749
+ const parent = path3.dirname(current);
13655
13750
  if (parent === current)
13656
13751
  return;
13657
13752
  current = parent;
13658
13753
  }
13659
13754
  }
13660
13755
  function resolveMainRepoRoot(repoRoot) {
13661
- const gitEntry = path2.join(repoRoot, ".git");
13756
+ const gitEntry = path3.join(repoRoot, ".git");
13662
13757
  let stats;
13663
13758
  try {
13664
- stats = fs2.statSync(gitEntry);
13759
+ stats = fs3.statSync(gitEntry);
13665
13760
  } catch {
13666
13761
  return;
13667
13762
  }
13668
13763
  if (stats.isDirectory())
13669
- return path2.resolve(repoRoot);
13764
+ return path3.resolve(repoRoot);
13670
13765
  if (!stats.isFile())
13671
13766
  return;
13672
13767
  const pointer = readText(gitEntry);
13673
13768
  if (pointer === undefined)
13674
13769
  return;
13675
13770
  const gitDir = pathFromPointer(pointer, "gitdir:", repoRoot);
13676
- if (gitDir === undefined || path2.basename(path2.dirname(gitDir)) !== "worktrees")
13771
+ if (gitDir === undefined || path3.basename(path3.dirname(gitDir)) !== "worktrees")
13677
13772
  return;
13678
- const commonGitDir = path2.dirname(path2.dirname(gitDir));
13679
- if (path2.basename(commonGitDir) !== ".git")
13773
+ const commonGitDir = path3.dirname(path3.dirname(gitDir));
13774
+ if (path3.basename(commonGitDir) !== ".git")
13680
13775
  return;
13681
- const backPointer = readText(path2.join(gitDir, "gitdir"));
13682
- const commonPointer = readText(path2.join(gitDir, "commondir"));
13776
+ const backPointer = readText(path3.join(gitDir, "gitdir"));
13777
+ const commonPointer = readText(path3.join(gitDir, "commondir"));
13683
13778
  if (backPointer === undefined || commonPointer === undefined)
13684
13779
  return;
13685
- const linkedGitEntry = path2.resolve(gitDir, backPointer.trim());
13686
- const declaredCommonDir = path2.resolve(gitDir, commonPointer.trim());
13780
+ const linkedGitEntry = path3.resolve(gitDir, backPointer.trim());
13781
+ const declaredCommonDir = path3.resolve(gitDir, commonPointer.trim());
13687
13782
  if (!backPointer.trim() || !commonPointer.trim())
13688
13783
  return;
13689
13784
  if (!samePath(linkedGitEntry, gitEntry) || !samePath(declaredCommonDir, commonGitDir))
13690
13785
  return;
13691
- return path2.dirname(commonGitDir);
13786
+ return path3.dirname(commonGitDir);
13692
13787
  }
13693
13788
  function findCanonicalGitRoot(cwd) {
13694
13789
  const repoRoot = findGitRoot(cwd);
@@ -13697,33 +13792,31 @@ function findCanonicalGitRoot(cwd) {
13697
13792
  var init_worktree_root = () => {};
13698
13793
 
13699
13794
  // dist/builtin/workflows/src/runs/shared/worktree-git.ts
13700
- import * as fs3 from "node:fs";
13701
- import * as path3 from "node:path";
13702
- function gitWorktreeSetupCacheKey(options) {
13703
- const repoRoot = repositoryRootForGitWorktree(options.cwd);
13704
- const { logicalRepoRoot } = cwdWithinGitRepository(options.cwd, repoRoot);
13705
- const worktreeRoot = resolveGitWorktreePath(options.gitWorktreeDir, logicalRepoRoot);
13795
+ import * as fs4 from "node:fs";
13796
+ import * as path4 from "node:path";
13797
+ function gitWorktreeSetupCacheKey(request) {
13706
13798
  return JSON.stringify([
13707
- comparableRealPath(gitCommonDirForWorktree(repoRoot)),
13708
- comparablePathThroughExistingAncestor(worktreeRoot)
13799
+ comparablePathThroughExistingAncestor(request.options.cwd),
13800
+ comparablePathThroughExistingAncestor(request.target)
13709
13801
  ]);
13710
13802
  }
13711
- function createGitWorktreeSetupCache() {
13803
+ function createGitWorktreeSetupCache(ownerRunId) {
13712
13804
  const results = new Map;
13713
13805
  let disposed = false;
13714
13806
  return {
13715
13807
  get(options) {
13716
13808
  if (disposed)
13717
13809
  throw new Error("Git worktree setup cache is already disposed");
13718
- const key = gitWorktreeSetupCacheKey(options);
13810
+ const request = resolveGitWorktreeSetupRequest(options);
13811
+ const key = gitWorktreeSetupCacheKey(request);
13719
13812
  const cached = results.get(key);
13720
13813
  if (cached !== undefined) {
13721
13814
  assertCachedGitWorktreeIdentity(cached, options);
13722
13815
  return cached.result;
13723
13816
  }
13724
- const result = setupGitWorktree(options);
13725
- results.set(key, { result, identity: captureGitWorktreeIdentity(result) });
13726
- return result;
13817
+ const setup = setupGitWorktreeFromRequest(request, ownerRunId);
13818
+ results.set(key, { ...setup, identity: captureGitWorktreeIdentity(setup) });
13819
+ return setup.result;
13727
13820
  },
13728
13821
  dispose() {
13729
13822
  if (disposed)
@@ -13755,10 +13848,10 @@ function worktreeRecoveryCommand(repositoryRoot, worktreeDir) {
13755
13848
  return `git -C ${quoteShellArg(repositoryRoot)} worktree remove --force ${quoteShellArg(worktreeDir)}`;
13756
13849
  }
13757
13850
  function hasGrandparent(value2) {
13758
- const parent = path3.dirname(value2);
13851
+ const parent = path4.dirname(value2);
13759
13852
  if (parent === value2)
13760
13853
  return false;
13761
- const grandparent = path3.dirname(parent);
13854
+ const grandparent = path4.dirname(parent);
13762
13855
  return grandparent !== parent;
13763
13856
  }
13764
13857
  function pathAncestors(value2) {
@@ -13766,7 +13859,7 @@ function pathAncestors(value2) {
13766
13859
  let current = value2;
13767
13860
  while (true) {
13768
13861
  ancestors.push(current);
13769
- const parent = path3.dirname(current);
13862
+ const parent = path4.dirname(current);
13770
13863
  if (parent === current)
13771
13864
  return ancestors;
13772
13865
  current = parent;
@@ -13775,89 +13868,167 @@ function pathAncestors(value2) {
13775
13868
  function shouldPreserveLogicalPath(logicalPath) {
13776
13869
  return pathAncestors(logicalPath).some((ancestor) => {
13777
13870
  try {
13778
- return fs3.lstatSync(ancestor).isSymbolicLink() && hasGrandparent(ancestor);
13871
+ return fs4.lstatSync(ancestor).isSymbolicLink() && hasGrandparent(ancestor);
13779
13872
  } catch {
13780
13873
  return false;
13781
13874
  }
13782
13875
  });
13783
13876
  }
13784
13877
  function canonicalizePreservingSymlinks(value2) {
13785
- const logicalPath = path3.resolve(value2);
13878
+ const logicalPath = path4.resolve(value2);
13786
13879
  const preserveLogicalPath = shouldPreserveLogicalPath(logicalPath);
13787
13880
  try {
13788
- const canonical = fs3.realpathSync.native(logicalPath);
13881
+ const canonical = fs4.realpathSync.native(logicalPath);
13789
13882
  return preserveLogicalPath && canonical !== logicalPath ? logicalPath : canonical;
13790
13883
  } catch {
13791
13884
  return logicalPath;
13792
13885
  }
13793
13886
  }
13794
- function resolveGitWorktreePath(value2, repoRoot) {
13887
+ function resolveGitWorktreePath(value2, baseDir) {
13795
13888
  const trimmed = value2.trim();
13796
13889
  if (!trimmed)
13797
13890
  throw new Error("gitWorktreeDir cannot be empty");
13798
13891
  if (trimmed.includes("\x00")) {
13799
13892
  throw new Error("gitWorktreeDir contains an unusable null byte; provide a valid path or omit gitWorktreeDir.");
13800
13893
  }
13801
- return canonicalizePreservingSymlinks(path3.isAbsolute(trimmed) ? trimmed : path3.resolve(repoRoot, trimmed));
13894
+ return canonicalizePreservingSymlinks(path4.isAbsolute(trimmed) ? trimmed : path4.resolve(baseDir, trimmed));
13802
13895
  }
13803
13896
  function comparableRealPath(value2) {
13804
- const realpath = fs3.realpathSync.native(value2).replace(/\\/g, "/");
13897
+ const realpath = fs4.realpathSync.native(value2).replace(/\\/g, "/");
13805
13898
  return process.platform === "win32" ? realpath.toLowerCase() : realpath;
13806
13899
  }
13807
- function comparablePathThroughExistingAncestor(value2) {
13808
- let existing = path3.resolve(value2);
13900
+ function nearestExistingAncestor(value2) {
13901
+ let existing = path4.resolve(value2);
13809
13902
  const missing = [];
13810
13903
  while (!pathExistsSync(existing)) {
13811
- const parent = path3.dirname(existing);
13904
+ const parent = path4.dirname(existing);
13812
13905
  if (parent === existing)
13813
13906
  break;
13814
- missing.unshift(path3.basename(existing));
13907
+ missing.unshift(path4.basename(existing));
13815
13908
  existing = parent;
13816
13909
  }
13817
- const resolved = path3.resolve(comparableRealPath(existing), ...missing).replace(/\\/g, "/");
13910
+ return { existing, missing };
13911
+ }
13912
+ function comparablePathThroughExistingAncestor(value2) {
13913
+ const { existing, missing } = nearestExistingAncestor(value2);
13914
+ const resolved = path4.resolve(comparableRealPath(existing), ...missing).replace(/\\/g, "/");
13818
13915
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
13819
13916
  }
13917
+ function isFileOrLink(value2) {
13918
+ try {
13919
+ const entry = fs4.lstatSync(value2);
13920
+ return entry.isFile() || entry.isSymbolicLink();
13921
+ } catch {
13922
+ return false;
13923
+ }
13924
+ }
13925
+ function isDirectory(value2) {
13926
+ try {
13927
+ return fs4.statSync(value2).isDirectory();
13928
+ } catch {
13929
+ return false;
13930
+ }
13931
+ }
13932
+ function looksLikeGitDirectory(directory) {
13933
+ if (!isDirectory(directory))
13934
+ return false;
13935
+ if (path4.basename(directory) === ".git")
13936
+ return true;
13937
+ if (!isFileOrLink(path4.join(directory, "HEAD")))
13938
+ return false;
13939
+ if (isFileOrLink(path4.join(directory, "commondir")))
13940
+ return true;
13941
+ return isDirectory(path4.join(directory, "objects")) && isDirectory(path4.join(directory, "refs"));
13942
+ }
13943
+ function enclosingGitMetadataDirectory(target) {
13944
+ const { existing } = nearestExistingAncestor(target);
13945
+ let realExisting = existing;
13946
+ try {
13947
+ realExisting = fs4.realpathSync.native(existing);
13948
+ } catch {}
13949
+ for (const candidate of new Set([...pathAncestors(existing), ...pathAncestors(realExisting)])) {
13950
+ if (looksLikeGitDirectory(candidate))
13951
+ return candidate;
13952
+ }
13953
+ const insideGitDir = runGitReadOnlyProbe(existing, ["rev-parse", "--is-inside-git-dir"]);
13954
+ if (insideGitDir.status !== 0 && isGitTimeoutResult(insideGitDir)) {
13955
+ throw new Error(`Timed out while validating gitWorktreeDir ${target}. Git reported: ${gitFailureMessage(insideGitDir)}`);
13956
+ }
13957
+ return insideGitDir.status === 0 && insideGitDir.stdout.trim() === "true" ? existing : undefined;
13958
+ }
13959
+ function assertSafeGitWorktreeTarget(target, invokingRoot) {
13960
+ const gitDirectory = enclosingGitMetadataDirectory(target);
13961
+ if (gitDirectory !== undefined) {
13962
+ throw new Error(`gitWorktreeDir must not be inside Git metadata: ${target} is inside the Git directory ${gitDirectory}. Choose a worktree root or a plain directory outside Git metadata.`);
13963
+ }
13964
+ if (invokingRoot !== undefined)
13965
+ validateWorktreeOutsideInvokingCheckout(target, invokingRoot);
13966
+ const { existing, missing } = nearestExistingAncestor(target);
13967
+ const workingTree = gitTopLevel(existing);
13968
+ if (workingTree === undefined)
13969
+ return;
13970
+ if (missing.length === 0 && comparableRealPath(target) === comparableRealPath(workingTree))
13971
+ return;
13972
+ throw new Error(`gitWorktreeDir must not be inside another Git working tree: ${target} is inside ${workingTree}. Pass that checkout's root to reuse it, or a path outside every Git checkout.`);
13973
+ }
13820
13974
  function validateWorktreeOutsideInvokingCheckout(worktreeRoot, repoRoot) {
13821
13975
  const repository = comparableRealPath(repoRoot);
13822
13976
  const candidate = comparablePathThroughExistingAncestor(worktreeRoot);
13823
- const relativePath = path3.relative(repository, candidate);
13977
+ const relativePath = path4.relative(repository, candidate);
13824
13978
  if (relativePath === "") {
13825
- throw new Error("gitWorktreeDir must not resolve to the invoking checkout; provide a separate same-repository worktree path.");
13979
+ throw new Error("gitWorktreeDir must not resolve to the invoking checkout; provide a separate worktree path outside it.");
13826
13980
  }
13827
- if (!relativePath.startsWith(`..${path3.sep}`) && relativePath !== ".." && !path3.isAbsolute(relativePath)) {
13828
- throw new Error("gitWorktreeDir must be outside the invoking checkout; provide a separate same-repository worktree path.");
13981
+ if (!relativePath.startsWith(`..${path4.sep}`) && relativePath !== ".." && !path4.isAbsolute(relativePath)) {
13982
+ throw new Error("gitWorktreeDir must be outside the invoking checkout; provide a separate worktree path outside it.");
13829
13983
  }
13830
13984
  }
13831
13985
  function gitPathFromOutput(value2, cwd) {
13832
13986
  const trimmed = value2.trim();
13833
13987
  if (!trimmed)
13834
13988
  return;
13835
- return path3.isAbsolute(trimmed) ? path3.resolve(trimmed) : path3.resolve(cwd, trimmed);
13989
+ return path4.isAbsolute(trimmed) ? path4.resolve(trimmed) : path4.resolve(cwd, trimmed);
13836
13990
  }
13837
13991
  function pathExistsSync(value2) {
13992
+ return pathStatSync(value2) !== undefined;
13993
+ }
13994
+ function pathStatSync(value2) {
13838
13995
  try {
13839
- fs3.statSync(value2);
13840
- return true;
13996
+ return fs4.statSync(value2);
13841
13997
  } catch (error) {
13842
13998
  const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
13843
13999
  if (code === "ENOENT" || code === "ENOTDIR")
13844
- return false;
14000
+ return;
13845
14001
  throw error;
13846
14002
  }
13847
14003
  }
13848
- function repositoryRootForGitWorktree(cwd) {
14004
+ function probeInvokingRepository(cwd) {
13849
14005
  const result = runGitReadOnlyProbe(cwd, ["rev-parse", "--show-toplevel"]);
13850
14006
  if (result.status !== 0) {
13851
14007
  if (isGitTimeoutResult(result)) {
13852
14008
  throw new Error(`Timed out while checking the Git repository for gitWorktreeDir from ${cwd}. Git reported: ${gitFailureMessage(result)}`);
13853
14009
  }
13854
- throw new Error(`gitWorktreeDir requires the workflow to be invoked from inside a Git repository. Start from a Git checkout or omit gitWorktreeDir. Git reported: ${gitFailureMessage(result)}`);
14010
+ return { failure: gitFailureMessage(result) };
13855
14011
  }
13856
- const normalized = gitPathFromOutput(result.stdout, cwd);
13857
- if (normalized === undefined) {
14012
+ const root = gitPathFromOutput(result.stdout, cwd);
14013
+ if (root === undefined) {
13858
14014
  throw new Error(`gitWorktreeDir could not resolve the repository top level from ${cwd}: git returned an empty path.`);
13859
14015
  }
13860
- return normalized;
14016
+ return { repository: { root, ...cwdWithinGitRepository(cwd, root) } };
14017
+ }
14018
+ function resolveGitWorktreeSetupRequest(options) {
14019
+ const probe = probeInvokingRepository(options.cwd);
14020
+ if ("repository" in probe) {
14021
+ return {
14022
+ options,
14023
+ repository: probe.repository,
14024
+ target: resolveGitWorktreePath(options.gitWorktreeDir, probe.repository.logicalRoot)
14025
+ };
14026
+ }
14027
+ return {
14028
+ options,
14029
+ repositoryProbeFailure: probe.failure,
14030
+ target: resolveGitWorktreePath(options.gitWorktreeDir, canonicalizePreservingSymlinks(options.cwd))
14031
+ };
13861
14032
  }
13862
14033
  function gitTopLevelFromResult(result, cwd, description) {
13863
14034
  if (result.status !== 0) {
@@ -13893,25 +14064,25 @@ function dirnameForEachRelativeComponent(base, relativePath) {
13893
14064
  continue;
13894
14065
  if (component === "..")
13895
14066
  return;
13896
- current = path3.dirname(current);
14067
+ current = path4.dirname(current);
13897
14068
  }
13898
14069
  return current;
13899
14070
  }
13900
14071
  function cwdWithinGitRepository(cwd, repoRoot) {
13901
- const sourceCwd = fs3.realpathSync.native(cwd);
13902
- const sourceRepoRoot = fs3.realpathSync.native(repoRoot);
13903
- const relativeCwd = path3.relative(sourceRepoRoot, sourceCwd);
13904
- const safeRelativeCwd = relativeCwd === "" || relativeCwd.startsWith("..") || path3.isAbsolute(relativeCwd) ? "" : relativeCwd;
14072
+ const sourceCwd = fs4.realpathSync.native(cwd);
14073
+ const sourceRepoRoot = fs4.realpathSync.native(repoRoot);
14074
+ const relativeCwd = path4.relative(sourceRepoRoot, sourceCwd);
14075
+ const safeRelativeCwd = relativeCwd === "" || relativeCwd.startsWith("..") || path4.isAbsolute(relativeCwd) ? "" : relativeCwd;
13905
14076
  const logicalCwd = canonicalizePreservingSymlinks(cwd);
13906
14077
  return {
13907
14078
  relativeCwd: safeRelativeCwd,
13908
- logicalRepoRoot: dirnameForEachRelativeComponent(logicalCwd, safeRelativeCwd) ?? repoRoot
14079
+ logicalRoot: dirnameForEachRelativeComponent(logicalCwd, safeRelativeCwd) ?? repoRoot
13909
14080
  };
13910
14081
  }
13911
14082
  function workspaceCwdForGitWorktreeRoot(worktreeRoot, relativeCwd) {
13912
- return relativeCwd === "" ? worktreeRoot : path3.join(worktreeRoot, relativeCwd);
14083
+ return relativeCwd === "" ? worktreeRoot : path4.join(worktreeRoot, relativeCwd);
13913
14084
  }
13914
- function validateExistingGitWorktreeRoot(worktreeRoot, repoRoot) {
14085
+ function validateGitWorktreeRoot(worktreeRoot) {
13915
14086
  const topLevel = gitTopLevel(worktreeRoot);
13916
14087
  if (topLevel === undefined) {
13917
14088
  throw new Error(`gitWorktreeDir already exists but is not a Git worktree: ${worktreeRoot}`);
@@ -13919,8 +14090,18 @@ function validateExistingGitWorktreeRoot(worktreeRoot, repoRoot) {
13919
14090
  if (comparableRealPath(worktreeRoot) !== comparableRealPath(topLevel)) {
13920
14091
  throw new Error(`gitWorktreeDir already exists but is not a Git worktree root: ${worktreeRoot}. Git top-level checkout is ${topLevel}`);
13921
14092
  }
13922
- if (comparableRealPath(gitCommonDirForWorktree(repoRoot)) !== comparableRealPath(gitCommonDirForWorktree(topLevel))) {
13923
- throw new Error(`gitWorktreeDir already exists but does not belong to the invoking Git repository: ${worktreeRoot}`);
14093
+ return gitCommonDirForWorktree(topLevel);
14094
+ }
14095
+ function existingGitWorktreeCommonDir(directory) {
14096
+ return gitTopLevel(directory) === undefined ? undefined : validateGitWorktreeRoot(directory);
14097
+ }
14098
+ function sameGitCommonDir(left, right) {
14099
+ return comparableRealPath(left) === comparableRealPath(right);
14100
+ }
14101
+ function validateCreatedGitWorktree(worktreeRoot, repository) {
14102
+ assertSafeGitWorktreeTarget(worktreeRoot, repository.root);
14103
+ if (!sameGitCommonDir(validateGitWorktreeRoot(worktreeRoot), gitCommonDirForWorktree(repository.root))) {
14104
+ throw new Error(`gitWorktreeDir does not belong to the invoking Git repository: ${worktreeRoot}`);
13924
14105
  }
13925
14106
  }
13926
14107
  function gitDirectoryForWorktree(cwd) {
@@ -13934,26 +14115,27 @@ function gitDirectoryForWorktree(cwd) {
13934
14115
  return gitDir;
13935
14116
  }
13936
14117
  function captureGitWorktreeIdentitySnapshot(setup) {
13937
- validateWorktreeOutsideInvokingCheckout(setup.worktreeRoot, setup.repositoryRoot);
13938
- validateExistingGitWorktreeRoot(setup.worktreeRoot, setup.repositoryRoot);
13939
- const stats = fs3.statSync(setup.worktreeRoot);
14118
+ const { worktreeRoot, repositoryRoot } = setup.result;
14119
+ assertSafeGitWorktreeTarget(worktreeRoot, setup.invokingRoot);
14120
+ const commonDir = validateGitWorktreeRoot(worktreeRoot);
14121
+ const stats = fs4.statSync(worktreeRoot);
13940
14122
  return {
13941
- worktreeRealPath: comparableRealPath(setup.worktreeRoot),
13942
- repositoryRealPath: comparableRealPath(setup.repositoryRoot),
13943
- gitDirRealPath: comparableRealPath(gitDirectoryForWorktree(setup.worktreeRoot)),
13944
- commonDirRealPath: comparableRealPath(gitCommonDirForWorktree(setup.worktreeRoot)),
14123
+ worktreeRealPath: comparableRealPath(worktreeRoot),
14124
+ repositoryRealPath: comparableRealPath(repositoryRoot),
14125
+ gitDirRealPath: comparableRealPath(gitDirectoryForWorktree(worktreeRoot)),
14126
+ commonDirRealPath: comparableRealPath(commonDir),
13945
14127
  device: stats.dev,
13946
14128
  inode: stats.ino
13947
14129
  };
13948
14130
  }
13949
14131
  function captureGitWorktreeIdentity(setup) {
13950
14132
  const snapshot = captureGitWorktreeIdentitySnapshot(setup);
13951
- return { ...snapshot, generation: openGitWorktreeGenerationAnchor(setup.worktreeRoot) };
14133
+ return { ...snapshot, generation: openGitWorktreeGenerationAnchor(setup.result.worktreeRoot) };
13952
14134
  }
13953
14135
  function assertCachedGitWorktreeIdentity(cached, options) {
13954
14136
  let current;
13955
14137
  try {
13956
- current = captureGitWorktreeIdentitySnapshot(cached.result);
14138
+ current = captureGitWorktreeIdentitySnapshot(cached);
13957
14139
  cached.identity.generation.assertCurrent();
13958
14140
  } catch (error) {
13959
14141
  const message = error instanceof Error ? error.message : String(error);
@@ -13961,34 +14143,58 @@ function assertCachedGitWorktreeIdentity(cached, options) {
13961
14143
  }
13962
14144
  const expected = cached.identity;
13963
14145
  if (current.worktreeRealPath !== expected.worktreeRealPath || current.repositoryRealPath !== expected.repositoryRealPath || current.gitDirRealPath !== expected.gitDirRealPath || current.commonDirRealPath !== expected.commonDirRealPath || current.device !== expected.device || current.inode !== expected.inode) {
13964
- throw new Error(`Cached gitWorktreeDir changed before reuse: ${options.gitWorktreeDir}. Start a new run with the intended same-repository worktree.`);
13965
- }
14146
+ throw new Error(`Cached gitWorktreeDir changed before reuse: ${options.gitWorktreeDir}. Start a new run with the intended worktree.`);
14147
+ }
14148
+ }
14149
+ function requireInvokingRepository(request, action) {
14150
+ if (request.repository !== undefined)
14151
+ return request.repository;
14152
+ throw new Error([
14153
+ `gitWorktreeDir requires the workflow to be invoked from inside a Git repository to ${action}.`,
14154
+ "Start the workflow from a checkout of the repository the new worktree should come from, or set gitWorktreeDir to the root of an existing Git worktree to reuse it from any directory.",
14155
+ `Git reported: ${request.repositoryProbeFailure ?? "no repository"}`
14156
+ ].join(" "));
14157
+ }
14158
+ function setupGitWorktreeFromRequest(request, ownerRunId) {
14159
+ const { repository, target } = request;
14160
+ assertSafeGitWorktreeTarget(target, repository?.root);
14161
+ const entry = pathStatSync(target);
14162
+ if (entry === undefined)
14163
+ return createGitWorktreeAtPath(request, false);
14164
+ if (!entry.isDirectory())
14165
+ throw new Error(`gitWorktreeDir already exists but is not a directory: ${target}`);
14166
+ if (fs4.readdirSync(target).length === 0)
14167
+ return createGitWorktreeAtPath(request, true);
14168
+ const commonDir = existingGitWorktreeCommonDir(target);
14169
+ if (commonDir === undefined)
14170
+ return allocateGitWorktree(request, ownerRunId);
14171
+ const sameRepository = repository !== undefined && sameGitCommonDir(commonDir, gitCommonDirForWorktree(repository.root));
14172
+ return {
14173
+ result: {
14174
+ worktreeRoot: target,
14175
+ cwd: sameRepository ? workspaceCwdForGitWorktreeRoot(target, repository.relativeCwd) : target,
14176
+ repositoryRoot: sameRepository ? repository.root : target,
14177
+ created: false
14178
+ },
14179
+ ...repository !== undefined ? { invokingRoot: repository.root } : {}
14180
+ };
13966
14181
  }
13967
- function setupGitWorktree(options) {
13968
- const repoRoot = repositoryRootForGitWorktree(options.cwd);
13969
- const mainRoot = findCanonicalGitRoot(repoRoot);
14182
+ function createGitWorktreeAtPath(request, existingEmptyDirectory) {
14183
+ const { target: worktreeRoot } = request;
14184
+ const repository = requireInvokingRepository(request, `create a new worktree at ${worktreeRoot}`);
14185
+ const mainRoot = findCanonicalGitRoot(repository.root);
13970
14186
  if (mainRoot === undefined) {
13971
- throw new Error(`Unable to resolve the canonical main Git repository root from ${repoRoot}.`);
13972
- }
13973
- const { relativeCwd, logicalRepoRoot } = cwdWithinGitRepository(options.cwd, repoRoot);
13974
- const worktreeRoot = resolveGitWorktreePath(options.gitWorktreeDir, logicalRepoRoot);
13975
- validateWorktreeOutsideInvokingCheckout(worktreeRoot, repoRoot);
13976
- if (pathExistsSync(worktreeRoot)) {
13977
- validateExistingGitWorktreeRoot(worktreeRoot, repoRoot);
13978
- return {
13979
- worktreeRoot,
13980
- cwd: workspaceCwdForGitWorktreeRoot(worktreeRoot, relativeCwd),
13981
- repositoryRoot: repoRoot,
13982
- created: false
13983
- };
14187
+ throw new Error(`Unable to resolve the canonical main Git repository root from ${repository.root}.`);
13984
14188
  }
13985
- try {
13986
- fs3.mkdirSync(path3.dirname(worktreeRoot), { recursive: true });
13987
- } catch (error) {
13988
- const message = error instanceof Error ? error.message : String(error);
13989
- throw new Error(`Failed to create parent directory for requested gitWorktreeDir ${worktreeRoot}: ${message}`);
14189
+ if (!existingEmptyDirectory) {
14190
+ try {
14191
+ fs4.mkdirSync(path4.dirname(worktreeRoot), { recursive: true });
14192
+ } catch (error) {
14193
+ const message = error instanceof Error ? error.message : String(error);
14194
+ throw new Error(`Failed to create parent directory for requested gitWorktreeDir ${worktreeRoot}: ${message}`);
14195
+ }
13990
14196
  }
13991
- const baseRef = options.baseBranch?.trim() || "HEAD";
14197
+ const baseRef = request.options.baseBranch?.trim() || "HEAD";
13992
14198
  const result = runGit(mainRoot, ["worktree", "add", "--detach", worktreeRoot, baseRef]);
13993
14199
  if (result.status !== 0) {
13994
14200
  throw new Error([
@@ -13998,30 +14204,127 @@ function setupGitWorktree(options) {
13998
14204
  `));
13999
14205
  }
14000
14206
  try {
14001
- validateWorktreeOutsideInvokingCheckout(worktreeRoot, repoRoot);
14002
- validateExistingGitWorktreeRoot(worktreeRoot, repoRoot);
14207
+ validateCreatedGitWorktree(worktreeRoot, repository);
14003
14208
  } catch (error) {
14004
14209
  runGit(mainRoot, ["worktree", "remove", "--force", worktreeRoot]);
14005
14210
  const message = error instanceof Error ? error.message : String(error);
14006
14211
  throw new Error(`Created gitWorktreeDir failed post-creation validation: ${message}`);
14007
14212
  }
14008
14213
  return {
14009
- worktreeRoot,
14010
- cwd: workspaceCwdForGitWorktreeRoot(worktreeRoot, relativeCwd),
14011
- repositoryRoot: repoRoot,
14012
- created: true
14214
+ result: {
14215
+ worktreeRoot,
14216
+ cwd: workspaceCwdForGitWorktreeRoot(worktreeRoot, repository.relativeCwd),
14217
+ repositoryRoot: repository.root,
14218
+ created: true
14219
+ },
14220
+ invokingRoot: repository.root
14221
+ };
14222
+ }
14223
+ function containedWorktreeCwd(checkout, relativeCwd) {
14224
+ const cwd = workspaceCwdForGitWorktreeRoot(checkout, relativeCwd);
14225
+ let resolved;
14226
+ try {
14227
+ resolved = fs4.realpathSync.native(cwd);
14228
+ } catch {
14229
+ resolved = "";
14230
+ }
14231
+ const relative2 = resolved === "" ? undefined : path4.relative(fs4.realpathSync.native(checkout), resolved);
14232
+ if (relative2 === undefined || relative2 === ".." || relative2.startsWith(`..${path4.sep}`) || path4.isAbsolute(relative2) || !fs4.statSync(resolved).isDirectory()) {
14233
+ throw new Error(`The selected base does not contain a working directory ${relativeCwd || "."} inside the worktree ${checkout}.`);
14234
+ }
14235
+ return cwd;
14236
+ }
14237
+ function allocatedGitWorktreeSetup(checkout, repository, created) {
14238
+ return {
14239
+ result: {
14240
+ worktreeRoot: checkout,
14241
+ cwd: containedWorktreeCwd(checkout, repository.relativeCwd),
14242
+ repositoryRoot: repository.root,
14243
+ created
14244
+ },
14245
+ invokingRoot: repository.root
14013
14246
  };
14014
14247
  }
14248
+ function validateOwnedGitWorktree(checkout, repository) {
14249
+ validateCreatedGitWorktree(checkout, repository);
14250
+ const gitDir = gitDirectoryForWorktree(checkout);
14251
+ if (sameGitCommonDir(gitDir, gitCommonDirForWorktree(checkout))) {
14252
+ throw new Error(`${checkout} is not a linked worktree`);
14253
+ }
14254
+ const backlink = fs4.readFileSync(path4.join(gitDir, "gitdir"), "utf8").trim();
14255
+ if (!backlink || comparableRealPath(path4.resolve(gitDir, backlink)) !== comparableRealPath(path4.join(checkout, ".git"))) {
14256
+ throw new Error(`the Git directory of ${checkout} belongs to another checkout`);
14257
+ }
14258
+ }
14259
+ function findOwnedGitWorktree(root, repository, ownerRunId) {
14260
+ const [checkout] = findOwnedWorktreeCheckouts(root, path4.basename(repository.root), ownerRunId);
14261
+ if (checkout === undefined)
14262
+ return;
14263
+ try {
14264
+ validateOwnedGitWorktree(checkout, repository);
14265
+ return allocatedGitWorktreeSetup(checkout, repository, false);
14266
+ } catch (error) {
14267
+ const message = error instanceof Error ? error.message : String(error);
14268
+ throw new Error(`The worktree this workflow run allocated under gitWorktreeDir ${root} can no longer be reused: ${checkout}. ${message}`);
14269
+ }
14270
+ }
14271
+ function allocateGitWorktree(request, ownerRunId) {
14272
+ const { target: root } = request;
14273
+ const repository = requireInvokingRepository(request, `create a new worktree under ${root}`);
14274
+ if (ownerRunId !== undefined) {
14275
+ const owned = findOwnedGitWorktree(root, repository, ownerRunId);
14276
+ if (owned !== undefined)
14277
+ return owned;
14278
+ }
14279
+ const baseRef = request.options.baseBranch?.trim() || "HEAD";
14280
+ const revision = runGit(repository.root, ["rev-parse", "--verify", "--end-of-options", `${baseRef}^{commit}`]);
14281
+ const commit = revision.status === 0 ? revision.stdout.trim() : "";
14282
+ if (commit === "") {
14283
+ throw new Error(`Failed to resolve ${baseRef} to a commit for gitWorktreeDir ${root}. Git reported: ${gitFailureMessage(revision)}`);
14284
+ }
14285
+ const checkout = allocateWorktreeCheckoutPath(root, path4.basename(repository.root));
14286
+ const add = runGit(repository.root, ["worktree", "add", "--detach", "--no-checkout", checkout, commit]);
14287
+ if (add.status !== 0) {
14288
+ removeEmptyWorktreeBucket(checkout);
14289
+ throw new Error(`Failed to create git worktree at ${checkout} under gitWorktreeDir ${root} from ${baseRef}. Git reported: ${gitFailureMessage(add)}`);
14290
+ }
14291
+ try {
14292
+ const gitDir = gitDirectoryForWorktree(checkout);
14293
+ runGitOrThrow(checkout, ["config", "--file", path4.join(gitDir, "config.worktree"), "core.worktree", checkout]);
14294
+ runGitOrThrow(checkout, ["--work-tree=.", "reset", "--hard", "--no-recurse-submodules", commit]);
14295
+ validateCreatedGitWorktree(checkout, repository);
14296
+ const setup = allocatedGitWorktreeSetup(checkout, repository, true);
14297
+ if (ownerRunId !== undefined)
14298
+ bindWorktreeOwner(gitDir, ownerRunId);
14299
+ return setup;
14300
+ } catch (error) {
14301
+ const message = error instanceof Error ? error.message : String(error);
14302
+ const removal = runGit(repository.root, ["worktree", "remove", "--force", checkout]);
14303
+ removeEmptyWorktreeBucket(checkout);
14304
+ const rollback = removal.status === 0 ? "" : ` Rolling back the incomplete worktree also failed; remove it with: ${worktreeRecoveryCommand(repository.root, checkout)}`;
14305
+ throw new Error(`Failed to set up git worktree at ${checkout} under gitWorktreeDir ${root}: ${message}${rollback}`);
14306
+ }
14307
+ }
14308
+ function runGitOrThrow(cwd, args) {
14309
+ const result = runGit(cwd, args);
14310
+ if (result.status !== 0)
14311
+ throw new Error(gitFailureMessage(result));
14312
+ }
14313
+ function setupGitWorktree(options) {
14314
+ return setupGitWorktreeFromRequest(resolveGitWorktreeSetupRequest(options)).result;
14315
+ }
14015
14316
  var init_worktree_git = __esm(() => {
14317
+ init_worktree_allocation();
14016
14318
  init_worktree_generation();
14017
14319
  init_worktree_git_runner();
14018
14320
  init_worktree_root();
14321
+ init_worktree_allocation();
14019
14322
  init_worktree_git_runner();
14020
14323
  });
14021
14324
 
14022
14325
  // dist/builtin/workflows/src/runs/shared/worktree-cache-lifecycle.ts
14023
- function createGitWorktreeSetupCacheOwner(suppliedCache) {
14024
- const cache = suppliedCache ?? createGitWorktreeSetupCache();
14326
+ function createGitWorktreeSetupCacheOwner(suppliedCache, ownerRunId) {
14327
+ const cache = suppliedCache ?? createGitWorktreeSetupCache(ownerRunId);
14025
14328
  const ownsCache = suppliedCache === undefined;
14026
14329
  let released = false;
14027
14330
  return {
@@ -14042,20 +14345,20 @@ var init_worktree_cache_lifecycle = __esm(() => {
14042
14345
  });
14043
14346
 
14044
14347
  // dist/builtin/workflows/src/runs/shared/worktree-diff.ts
14045
- import * as fs4 from "node:fs";
14046
- import * as path4 from "node:path";
14348
+ import * as fs5 from "node:fs";
14349
+ import * as path5 from "node:path";
14047
14350
  function safePatchAgentName(agent) {
14048
14351
  return agent.replace(/[^\w.-]/g, "_");
14049
14352
  }
14050
14353
  function removeSyntheticPath(worktree, syntheticPath) {
14051
- const resolved = path4.resolve(worktree.path, syntheticPath);
14052
- const relative = path4.relative(worktree.path, resolved);
14053
- if (!relative || relative === "." || relative === ".." || relative.startsWith(`..${path4.sep}`) || path4.isAbsolute(relative)) {
14354
+ const resolved = path5.resolve(worktree.path, syntheticPath);
14355
+ const relative = path5.relative(worktree.path, resolved);
14356
+ if (!relative || relative === "." || relative === ".." || relative.startsWith(`..${path5.sep}`) || path5.isAbsolute(relative)) {
14054
14357
  return;
14055
14358
  }
14056
14359
  let stat;
14057
14360
  try {
14058
- stat = fs4.lstatSync(resolved);
14361
+ stat = fs5.lstatSync(resolved);
14059
14362
  } catch (error) {
14060
14363
  const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
14061
14364
  if (code === "ENOENT")
@@ -14063,14 +14366,14 @@ function removeSyntheticPath(worktree, syntheticPath) {
14063
14366
  throw error;
14064
14367
  }
14065
14368
  if (stat.isSymbolicLink()) {
14066
- fs4.unlinkSync(resolved);
14369
+ fs5.unlinkSync(resolved);
14067
14370
  return;
14068
14371
  }
14069
14372
  if (stat.isDirectory()) {
14070
- fs4.rmSync(resolved, { recursive: true, force: true });
14373
+ fs5.rmSync(resolved, { recursive: true, force: true });
14071
14374
  return;
14072
14375
  }
14073
- fs4.rmSync(resolved, { force: true });
14376
+ fs5.rmSync(resolved, { force: true });
14074
14377
  }
14075
14378
  function removeSyntheticPathsBeforeDiff(worktree) {
14076
14379
  if (worktree.syntheticPaths.length === 0)
@@ -14119,7 +14422,7 @@ function captureWorktreeDiff(setup, worktree, agent, patchPath) {
14119
14422
  const diffStat = runGitChecked(worktree.path, ["diff", "--cached", "--stat", setup.baseCommit]).trim();
14120
14423
  const patch = runGitChecked(worktree.path, ["diff", "--cached", setup.baseCommit]);
14121
14424
  const numstat = runGitChecked(worktree.path, ["diff", "--cached", "--numstat", setup.baseCommit]);
14122
- fs4.writeFileSync(patchPath, patch, "utf-8");
14425
+ fs5.writeFileSync(patchPath, patch, "utf-8");
14123
14426
  if (!patch.trim()) {
14124
14427
  return emptyDiff(worktree.index, agent, worktree.branch, patchPath);
14125
14428
  }
@@ -14137,7 +14440,7 @@ function captureWorktreeDiff(setup, worktree, agent, patchPath) {
14137
14440
  }
14138
14441
  function writeEmptyPatch(patchPath) {
14139
14442
  try {
14140
- fs4.writeFileSync(patchPath, "", "utf-8");
14443
+ fs5.writeFileSync(patchPath, "", "utf-8");
14141
14444
  } catch {}
14142
14445
  }
14143
14446
  function hasWorktreeChanges(diff) {
@@ -14145,7 +14448,7 @@ function hasWorktreeChanges(diff) {
14145
14448
  }
14146
14449
  function diffWorktrees(setup, agents, diffsDir) {
14147
14450
  try {
14148
- fs4.mkdirSync(diffsDir, { recursive: true });
14451
+ fs5.mkdirSync(diffsDir, { recursive: true });
14149
14452
  } catch {
14150
14453
  return [];
14151
14454
  }
@@ -14153,7 +14456,7 @@ function diffWorktrees(setup, agents, diffsDir) {
14153
14456
  for (let index = 0;index < setup.worktrees.length; index++) {
14154
14457
  const worktree = setup.worktrees[index];
14155
14458
  const agent = agents[index] ?? `task-${index + 1}`;
14156
- const patchPath = path4.join(diffsDir, `task-${index}-${safePatchAgentName(agent)}.patch`);
14459
+ const patchPath = path5.join(diffsDir, `task-${index}-${safePatchAgentName(agent)}.patch`);
14157
14460
  try {
14158
14461
  diffs.push(captureWorktreeDiff(setup, worktree, agent, patchPath));
14159
14462
  } catch {
@@ -14175,7 +14478,7 @@ function formatWorktreeDiffSummary(diffs) {
14175
14478
  }
14176
14479
  lines.push("");
14177
14480
  }
14178
- const patchesDir = path4.dirname(changed[0].patchPath);
14481
+ const patchesDir = path5.dirname(changed[0].patchPath);
14179
14482
  lines.push(`Full patches: ${patchesDir}`);
14180
14483
  return lines.join(`
14181
14484
  `).trimEnd();
@@ -14185,8 +14488,8 @@ var init_worktree_diff = __esm(() => {
14185
14488
  });
14186
14489
 
14187
14490
  // dist/builtin/workflows/src/runs/shared/worktree-post-create.ts
14188
- import * as fs5 from "node:fs";
14189
- import * as path5 from "node:path";
14491
+ import * as fs6 from "node:fs";
14492
+ import * as path6 from "node:path";
14190
14493
  function tracked(repo, relativePath) {
14191
14494
  const result = runGit(repo, ["ls-files", "--error-unmatch", "--", relativePath]);
14192
14495
  return result.status === 0;
@@ -14206,12 +14509,12 @@ function hasTrackedPathComponent(repo, relativePath) {
14206
14509
  return false;
14207
14510
  }
14208
14511
  function copyUntrackedFile(mainRoot, worktreeRoot, relativePath) {
14209
- const source = path5.join(mainRoot, relativePath);
14210
- const destination = path5.join(worktreeRoot, relativePath);
14211
- if (!fs5.existsSync(source) || fs5.existsSync(destination) || hasTrackedPathComponent(worktreeRoot, relativePath))
14512
+ const source = path6.join(mainRoot, relativePath);
14513
+ const destination = path6.join(worktreeRoot, relativePath);
14514
+ if (!fs6.existsSync(source) || fs6.existsSync(destination) || hasTrackedPathComponent(worktreeRoot, relativePath))
14212
14515
  return false;
14213
- fs5.mkdirSync(path5.dirname(destination), { recursive: true });
14214
- fs5.copyFileSync(source, destination);
14516
+ fs6.mkdirSync(path6.dirname(destination), { recursive: true });
14517
+ fs6.copyFileSync(source, destination);
14215
14518
  return true;
14216
14519
  }
14217
14520
  function propagateLocalSettings(mainRoot, worktreeRoot) {
@@ -14224,14 +14527,14 @@ function propagateLocalSettings(mainRoot, worktreeRoot) {
14224
14527
  return copied;
14225
14528
  }
14226
14529
  function populatedHooksDirectory(mainRoot) {
14227
- const husky = path5.join(mainRoot, ".husky");
14530
+ const husky = path6.join(mainRoot, ".husky");
14228
14531
  try {
14229
- if (fs5.statSync(husky).isDirectory())
14532
+ if (fs6.statSync(husky).isDirectory())
14230
14533
  return husky;
14231
14534
  } catch {}
14232
- const hooks = path5.join(mainRoot, ".git", "hooks");
14535
+ const hooks = path6.join(mainRoot, ".git", "hooks");
14233
14536
  try {
14234
- const populated = fs5.readdirSync(hooks, { withFileTypes: true }).some((entry) => entry.isFile() && !entry.name.endsWith(".sample"));
14537
+ const populated = fs6.readdirSync(hooks, { withFileTypes: true }).some((entry) => entry.isFile() && !entry.name.endsWith(".sample"));
14235
14538
  return populated ? hooks : undefined;
14236
14539
  } catch {
14237
14540
  return;
@@ -14242,7 +14545,7 @@ function configureSharedHooksPath(mainRoot) {
14242
14545
  if (desired === undefined)
14243
14546
  return;
14244
14547
  const current = runGitPlain(mainRoot, ["config", "--get", "core.hooksPath"]);
14245
- if (current.status === 0 && path5.resolve(mainRoot, current.stdout.trim()) === desired)
14548
+ if (current.status === 0 && path6.resolve(mainRoot, current.stdout.trim()) === desired)
14246
14549
  return;
14247
14550
  const configured = runGitPlain(mainRoot, ["config", "core.hooksPath", desired]);
14248
14551
  if (configured.status !== 0) {
@@ -14251,10 +14554,10 @@ function configureSharedHooksPath(mainRoot) {
14251
14554
  }
14252
14555
  function normalizeSymlinkDirectory(value2) {
14253
14556
  const trimmed = value2.trim();
14254
- if (!trimmed || path5.isAbsolute(trimmed))
14557
+ if (!trimmed || path6.isAbsolute(trimmed))
14255
14558
  return;
14256
- const normalized = path5.normalize(trimmed);
14257
- if (normalized === "." || normalized === ".." || normalized.startsWith(`..${path5.sep}`))
14559
+ const normalized = path6.normalize(trimmed);
14560
+ if (normalized === "." || normalized === ".." || normalized.startsWith(`..${path6.sep}`))
14258
14561
  return;
14259
14562
  return normalized;
14260
14563
  }
@@ -14264,20 +14567,20 @@ function symlinkConfiguredDirectories(mainRoot, worktreeRoot, directories) {
14264
14567
  const relativePath = normalizeSymlinkDirectory(rawDirectory);
14265
14568
  if (relativePath === undefined || synthetic.includes(relativePath))
14266
14569
  continue;
14267
- const source = path5.join(mainRoot, relativePath);
14268
- const destination = path5.join(worktreeRoot, relativePath);
14269
- if (!fs5.existsSync(source) || fs5.existsSync(destination) || hasTrackedPathComponent(worktreeRoot, relativePath))
14570
+ const source = path6.join(mainRoot, relativePath);
14571
+ const destination = path6.join(worktreeRoot, relativePath);
14572
+ if (!fs6.existsSync(source) || fs6.existsSync(destination) || hasTrackedPathComponent(worktreeRoot, relativePath))
14270
14573
  continue;
14271
- fs5.mkdirSync(path5.dirname(destination), { recursive: true });
14574
+ fs6.mkdirSync(path6.dirname(destination), { recursive: true });
14272
14575
  try {
14273
- fs5.symlinkSync(source, destination, process.platform === "win32" ? "junction" : undefined);
14576
+ fs6.symlinkSync(source, destination, process.platform === "win32" ? "junction" : undefined);
14274
14577
  synthetic.push(relativePath);
14275
14578
  } catch {}
14276
14579
  }
14277
14580
  return synthetic;
14278
14581
  }
14279
14582
  function copyWorktreeIncludes(mainRoot, worktreeRoot) {
14280
- if (!fs5.existsSync(path5.join(mainRoot, ".worktreeinclude")))
14583
+ if (!fs6.existsSync(path6.join(mainRoot, ".worktreeinclude")))
14281
14584
  return;
14282
14585
  const ignored = runGit(mainRoot, ["ls-files", "--others", "--ignored", "--exclude-standard"]);
14283
14586
  const included = runGit(mainRoot, ["ls-files", "--others", "--ignored", "--exclude-from=.worktreeinclude"]);
@@ -14287,13 +14590,13 @@ function copyWorktreeIncludes(mainRoot, worktreeRoot) {
14287
14590
  for (const relativePath of included.stdout.split(/\r?\n/).filter(Boolean)) {
14288
14591
  if (!ignoredPaths.has(relativePath) || hasTrackedPathComponent(worktreeRoot, relativePath))
14289
14592
  continue;
14290
- const source = path5.join(mainRoot, relativePath);
14291
- const destination = path5.join(worktreeRoot, relativePath);
14593
+ const source = path6.join(mainRoot, relativePath);
14594
+ const destination = path6.join(worktreeRoot, relativePath);
14292
14595
  try {
14293
- if (!fs5.statSync(source).isFile() || fs5.existsSync(destination))
14596
+ if (!fs6.statSync(source).isFile() || fs6.existsSync(destination))
14294
14597
  continue;
14295
- fs5.mkdirSync(path5.dirname(destination), { recursive: true });
14296
- fs5.copyFileSync(source, destination);
14598
+ fs6.mkdirSync(path6.dirname(destination), { recursive: true });
14599
+ fs6.copyFileSync(source, destination);
14297
14600
  } catch {}
14298
14601
  }
14299
14602
  }
@@ -14310,9 +14613,9 @@ var init_worktree_post_create = __esm(() => {
14310
14613
 
14311
14614
  // dist/builtin/workflows/src/runs/shared/worktree-setup.ts
14312
14615
  import { spawnSync as spawnSync2 } from "node:child_process";
14313
- import * as fs6 from "node:fs";
14616
+ import * as fs7 from "node:fs";
14314
14617
  import * as os from "node:os";
14315
- import * as path6 from "node:path";
14618
+ import * as path7 from "node:path";
14316
14619
  import { createChildProcessEnvironment as createChildProcessEnvironment2 } from "@bastani/atomic";
14317
14620
  function originDefaultBranch(mainRoot) {
14318
14621
  const symbolic = runGit(mainRoot, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"]);
@@ -14332,7 +14635,7 @@ function originDefaultBranch(mainRoot) {
14332
14635
  }
14333
14636
  function resolveRepoState(cwd, explicitBaseBranch) {
14334
14637
  const cwdRelative = resolveRepoCwdRelative(cwd);
14335
- const toplevel = path6.resolve(runGitChecked(cwd, ["rev-parse", "--show-toplevel"]).trim());
14638
+ const toplevel = path7.resolve(runGitChecked(cwd, ["rev-parse", "--show-toplevel"]).trim());
14336
14639
  const mainRoot = findCanonicalGitRoot(toplevel);
14337
14640
  if (mainRoot === undefined)
14338
14641
  throw new Error(`unable to resolve canonical main Git repository root from ${toplevel}`);
@@ -14345,9 +14648,9 @@ function resolveRepoState(cwd, explicitBaseBranch) {
14345
14648
  return { toplevel, mainRoot, cwdRelative, baseCommit, baseRef };
14346
14649
  }
14347
14650
  function normalizeComparableCwd(cwd) {
14348
- const resolved = path6.resolve(cwd);
14651
+ const resolved = path7.resolve(cwd);
14349
14652
  try {
14350
- return fs6.realpathSync(resolved);
14653
+ return fs7.realpathSync(resolved);
14351
14654
  } catch {
14352
14655
  return resolved;
14353
14656
  }
@@ -14358,7 +14661,7 @@ function findWorktreeTaskCwdConflict(tasks, sharedCwd) {
14358
14661
  const task = tasks[index];
14359
14662
  if (!task.cwd)
14360
14663
  continue;
14361
- const taskCwd = path6.isAbsolute(task.cwd) ? task.cwd : path6.resolve(sharedCwd, task.cwd);
14664
+ const taskCwd = path7.isAbsolute(task.cwd) ? task.cwd : path7.resolve(sharedCwd, task.cwd);
14362
14665
  if (normalizeComparableCwd(taskCwd) === normalizedSharedCwd)
14363
14666
  continue;
14364
14667
  return { index, agent: task.agent, cwd: task.cwd };
@@ -14375,12 +14678,12 @@ function buildWorktreeBranch(runId, index) {
14375
14678
  return `worktree-${flattenedWorktreeName(runId, index)}`;
14376
14679
  }
14377
14680
  function buildWorktreePath(mainRoot, runId, index) {
14378
- return path6.join(mainRoot, ".atomic", "worktrees", flattenedWorktreeName(runId, index));
14681
+ return path7.join(mainRoot, ".atomic", "worktrees", flattenedWorktreeName(runId, index));
14379
14682
  }
14380
14683
  function ensureWorktreeIgnore(mainRoot) {
14381
- const worktreesRoot = path6.join(mainRoot, ".atomic", "worktrees");
14382
- fs6.mkdirSync(worktreesRoot, { recursive: true });
14383
- fs6.writeFileSync(path6.join(worktreesRoot, ".gitignore"), `*
14684
+ const worktreesRoot = path7.join(mainRoot, ".atomic", "worktrees");
14685
+ fs7.mkdirSync(worktreesRoot, { recursive: true });
14686
+ fs7.writeFileSync(path7.join(worktreesRoot, ".gitignore"), `*
14384
14687
  `, "utf8");
14385
14688
  }
14386
14689
  function resolveRepoCwdRelative(cwd) {
@@ -14389,7 +14692,7 @@ function resolveRepoCwdRelative(cwd) {
14389
14692
  throw new Error("worktree isolation requires a git repository");
14390
14693
  }
14391
14694
  const rawPrefix = runGitChecked(cwd, ["rev-parse", "--show-prefix"]).trim();
14392
- const normalizedPrefix = rawPrefix ? path6.normalize(rawPrefix.replace(/[\\/]+$/, "")) : "";
14695
+ const normalizedPrefix = rawPrefix ? path7.normalize(rawPrefix.replace(/[\\/]+$/, "")) : "";
14393
14696
  return normalizedPrefix === "." ? "" : normalizedPrefix;
14394
14697
  }
14395
14698
  function parseHookTimeout(timeoutMs) {
@@ -14407,19 +14710,19 @@ function resolveWorktreeSetupHook(repoRoot, config) {
14407
14710
  if (!hookPath) {
14408
14711
  throw new Error("worktree setup hook path cannot be empty");
14409
14712
  }
14410
- const expandedHookPath = hookPath.startsWith("~/") ? path6.join(os.homedir(), hookPath.slice(2)) : hookPath;
14713
+ const expandedHookPath = hookPath.startsWith("~/") ? path7.join(os.homedir(), hookPath.slice(2)) : hookPath;
14411
14714
  let resolvedPath;
14412
- if (path6.isAbsolute(expandedHookPath)) {
14715
+ if (path7.isAbsolute(expandedHookPath)) {
14413
14716
  resolvedPath = expandedHookPath;
14414
14717
  } else if (expandedHookPath.includes("/") || expandedHookPath.includes("\\")) {
14415
- resolvedPath = path6.resolve(repoRoot, expandedHookPath);
14718
+ resolvedPath = path7.resolve(repoRoot, expandedHookPath);
14416
14719
  } else {
14417
14720
  throw new Error("worktree setup hook must be an absolute path or a repo-relative path");
14418
14721
  }
14419
- if (!fs6.existsSync(resolvedPath)) {
14722
+ if (!fs7.existsSync(resolvedPath)) {
14420
14723
  throw new Error(`worktree setup hook not found: ${resolvedPath}`);
14421
14724
  }
14422
- if (fs6.statSync(resolvedPath).isDirectory()) {
14725
+ if (fs7.statSync(resolvedPath).isDirectory()) {
14423
14726
  throw new Error(`worktree setup hook must be a file, got directory: ${resolvedPath}`);
14424
14727
  }
14425
14728
  return {
@@ -14431,17 +14734,17 @@ function normalizeSyntheticPath(worktreePath, rawPath) {
14431
14734
  const trimmed = rawPath.trim();
14432
14735
  if (!trimmed)
14433
14736
  throw new Error("synthetic path cannot be empty");
14434
- if (path6.isAbsolute(trimmed))
14737
+ if (path7.isAbsolute(trimmed))
14435
14738
  throw new Error(`synthetic path must be relative: ${rawPath}`);
14436
- const resolved = path6.resolve(worktreePath, trimmed);
14437
- const relative = path6.relative(worktreePath, resolved);
14739
+ const resolved = path7.resolve(worktreePath, trimmed);
14740
+ const relative = path7.relative(worktreePath, resolved);
14438
14741
  if (!relative || relative === ".") {
14439
14742
  throw new Error(`synthetic path cannot target the worktree root: ${rawPath}`);
14440
14743
  }
14441
- if (relative === ".." || relative.startsWith(`..${path6.sep}`) || path6.isAbsolute(relative)) {
14744
+ if (relative === ".." || relative.startsWith(`..${path7.sep}`) || path7.isAbsolute(relative)) {
14442
14745
  throw new Error(`synthetic path escapes the worktree root: ${rawPath}`);
14443
14746
  }
14444
- return path6.normalize(relative);
14747
+ return path7.normalize(relative);
14445
14748
  }
14446
14749
  function hasTrackedEntries(worktreePath, relativePath) {
14447
14750
  const result = runGit(worktreePath, ["ls-files", "--", relativePath]);
@@ -14513,16 +14816,16 @@ function waitForGitLockRelease() {
14513
14816
  }
14514
14817
  function unlinkWorktreeLinks(worktreePath, relativePaths) {
14515
14818
  for (const relativePath of new Set(relativePaths)) {
14516
- const target = path6.resolve(worktreePath, relativePath);
14517
- const relative = path6.relative(worktreePath, target);
14518
- if (!relative || relative === ".." || relative.startsWith(`..${path6.sep}`) || path6.isAbsolute(relative))
14819
+ const target = path7.resolve(worktreePath, relativePath);
14820
+ const relative = path7.relative(worktreePath, target);
14821
+ if (!relative || relative === ".." || relative.startsWith(`..${path7.sep}`) || path7.isAbsolute(relative))
14519
14822
  continue;
14520
14823
  let current = worktreePath;
14521
- for (const component of relative.split(path6.sep)) {
14522
- current = path6.join(current, component);
14824
+ for (const component of relative.split(path7.sep)) {
14825
+ current = path7.join(current, component);
14523
14826
  let stat;
14524
14827
  try {
14525
- stat = fs6.lstatSync(current);
14828
+ stat = fs7.lstatSync(current);
14526
14829
  } catch (error) {
14527
14830
  const code = error.code;
14528
14831
  if (code === "ENOENT" || code === "ENOTDIR")
@@ -14532,7 +14835,7 @@ function unlinkWorktreeLinks(worktreePath, relativePaths) {
14532
14835
  if (!stat.isSymbolicLink())
14533
14836
  continue;
14534
14837
  if (current === target)
14535
- fs6.unlinkSync(current);
14838
+ fs7.unlinkSync(current);
14536
14839
  break;
14537
14840
  }
14538
14841
  }
@@ -14560,7 +14863,7 @@ function createSingleWorktree(mainRoot, cwdRelative, runId, index, baseRef, base
14560
14863
  cleanupSingleWorktree(mainRoot, { path: worktreePath, branch }, []);
14561
14864
  throw new Error(message);
14562
14865
  }
14563
- const agentCwd = cwdRelative ? path6.join(worktreePath, cwdRelative) : worktreePath;
14866
+ const agentCwd = cwdRelative ? path7.join(worktreePath, cwdRelative) : worktreePath;
14564
14867
  const syntheticPaths = [];
14565
14868
  try {
14566
14869
  syntheticPaths.push(...performPostCreationSetup(mainRoot, worktreePath, symlinkDirectories));
@@ -14629,8 +14932,8 @@ var init_worktree = __esm(() => {
14629
14932
  });
14630
14933
 
14631
14934
  // dist/builtin/workflows/src/runs/shared/worktree-cwd.ts
14632
- import { lstatSync as lstatSync5, realpathSync as realpathSync4 } from "node:fs";
14633
- import { dirname as dirname8, isAbsolute as isAbsolute6, relative as relative4, resolve as resolve6, sep as sep5 } from "node:path";
14935
+ import { lstatSync as lstatSync6, realpathSync as realpathSync4 } from "node:fs";
14936
+ import { dirname as dirname9, isAbsolute as isAbsolute6, relative as relative4, resolve as resolve7, sep as sep5 } from "node:path";
14634
14937
  function relativePathWithin(root, candidate) {
14635
14938
  const path = relative4(root, candidate);
14636
14939
  return path === ".." || path.startsWith(`..${sep5}`) || isAbsolute6(path) ? undefined : path;
@@ -14649,14 +14952,14 @@ function resolveWorktreeStageCwd(cwd, setup) {
14649
14952
  if (cwd === undefined || cwd.length === 0)
14650
14953
  return;
14651
14954
  const absoluteInput = isAbsolute6(cwd);
14652
- const candidate = absoluteInput ? cwd : resolve6(setup.cwd, cwd);
14955
+ const candidate = absoluteInput ? cwd : resolve7(setup.cwd, cwd);
14653
14956
  if (!absoluteInput && relativePathWithin(setup.worktreeRoot, candidate) === undefined) {
14654
14957
  throw new Error(`atomic-workflows: relative cwd ${cwd} escapes gitWorktreeDir ${setup.worktreeRoot}; choose a cwd inside the worktree.`);
14655
14958
  }
14656
14959
  const candidateRealPath = existingRealPath(candidate, "cwd");
14657
14960
  const worktreeRelative = relativePathWithin(worktreeRoot, candidateRealPath);
14658
14961
  if (worktreeRelative !== undefined)
14659
- return resolve6(setup.worktreeRoot, worktreeRelative);
14962
+ return resolve7(setup.worktreeRoot, worktreeRelative);
14660
14963
  if (!isAbsolute6(cwd)) {
14661
14964
  throw new Error(`atomic-workflows: relative cwd ${cwd} escapes gitWorktreeDir ${setup.worktreeRoot}; choose a path that remains inside the selected worktree.`);
14662
14965
  }
@@ -14665,9 +14968,9 @@ function resolveWorktreeStageCwd(cwd, setup) {
14665
14968
  }
14666
14969
  const repositoryRelative = absoluteInput ? relativePathWithin(repositoryRoot, candidateRealPath) : undefined;
14667
14970
  if (repositoryRelative === undefined) {
14668
- throw new Error(`atomic-workflows: cwd ${cwd} is outside gitWorktreeDir ${setup.worktreeRoot}; use a cwd inside the invoking repository so Atomic can remap it, or omit cwd.`);
14971
+ throw new Error(repositoryRoot === worktreeRoot ? `atomic-workflows: cwd ${cwd} is outside gitWorktreeDir ${setup.worktreeRoot}, which belongs to a different repository than the invoking cwd; choose a cwd inside the worktree, or omit cwd.` : `atomic-workflows: cwd ${cwd} is outside gitWorktreeDir ${setup.worktreeRoot}; use a cwd inside the invoking repository so Atomic can remap it, or omit cwd.`);
14669
14972
  }
14670
- const mappedCwd = resolve6(setup.worktreeRoot, repositoryRelative);
14973
+ const mappedCwd = resolve7(setup.worktreeRoot, repositoryRelative);
14671
14974
  const mappedRealPath = existingRealPath(mappedCwd, "remapped worktree cwd");
14672
14975
  if (relativePathWithin(worktreeRoot, mappedRealPath) === undefined) {
14673
14976
  throw new Error(`atomic-workflows: cwd ${cwd} remaps through a path that resolves outside gitWorktreeDir ${setup.worktreeRoot}; remove the escaping symlink or omit cwd.`);
@@ -14678,7 +14981,7 @@ var init_worktree_cwd = () => {};
14678
14981
 
14679
14982
  // dist/builtin/workflows/src/runs/foreground/executor-task-prompts.ts
14680
14983
  import { existsSync as existsSync3 } from "node:fs";
14681
- import { isAbsolute as isAbsolute7, resolve as resolve7 } from "node:path";
14984
+ import { isAbsolute as isAbsolute7, resolve as resolve8 } from "node:path";
14682
14985
  function normalizeTaskContexts(previous) {
14683
14986
  if (previous === undefined)
14684
14987
  return [];
@@ -14731,7 +15034,7 @@ function taskPrevious(options) {
14731
15034
  function resolveWorkflowPath(filePath, baseDir) {
14732
15035
  if (isAbsolute7(filePath))
14733
15036
  return filePath;
14734
- return resolve7(baseDir ?? process.cwd(), filePath);
15037
+ return resolve8(baseDir ?? process.cwd(), filePath);
14735
15038
  }
14736
15039
  function taskBaseDir(options) {
14737
15040
  if (typeof options.chainDir === "string" && options.chainDir.trim().length > 0) {
@@ -14886,7 +15189,7 @@ function taskWithSharedDefaults(taskOptions, options) {
14886
15189
  var init_executor_task_prompts = () => {};
14887
15190
 
14888
15191
  // dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts
14889
- import { isAbsolute as isAbsolute8, join as join11, resolve as resolve8 } from "node:path";
15192
+ import { isAbsolute as isAbsolute8, join as join12, resolve as resolve9 } from "node:path";
14890
15193
  import { CONFIG_DIR_NAME } from "@bastani/atomic";
14891
15194
  function positiveConcurrency(value2) {
14892
15195
  if (typeof value2 !== "number" || !Number.isFinite(value2) || value2 < 1)
@@ -15039,10 +15342,10 @@ function workflowInvocationMetadata(inputDefaults, workflowInvocationCwd, cache,
15039
15342
  function resolvedTaskCwd(cwd, workflowInvocationCwd) {
15040
15343
  if (cwd === undefined)
15041
15344
  return;
15042
- return isAbsolute8(cwd) ? cwd : resolve8(workflowInvocationCwd, cwd);
15345
+ return isAbsolute8(cwd) ? cwd : resolve9(workflowInvocationCwd, cwd);
15043
15346
  }
15044
15347
  function taskWorktreeOutputsRoot(runId) {
15045
- return join11(workflowArtifactRunPath(runId), "task-outputs");
15348
+ return join12(workflowArtifactRunPath(runId), "task-outputs");
15046
15349
  }
15047
15350
  function prepareTaskWorktrees(tasks, options, runId, scope, workflowInvocationCwd = process.cwd(), symlinkDirectories) {
15048
15351
  if (options.worktree !== true && !tasks.some((task) => task.worktree === true)) {
@@ -15052,7 +15355,7 @@ function prepareTaskWorktrees(tasks, options, runId, scope, workflowInvocationCw
15052
15355
  throw new Error("atomic-workflows: worktree and gitWorktreeDir are mutually exclusive; use gitWorktreeDir for a reusable worktree or worktree:true for temporary isolated worktrees.");
15053
15356
  }
15054
15357
  const explicitCwd = tasks.find((task) => typeof task.cwd === "string")?.cwd;
15055
- const sharedCwd = explicitCwd === undefined ? workflowInvocationCwd : isAbsolute8(explicitCwd) ? explicitCwd : resolve8(workflowInvocationCwd, explicitCwd);
15358
+ const sharedCwd = explicitCwd === undefined ? workflowInvocationCwd : isAbsolute8(explicitCwd) ? explicitCwd : resolve9(workflowInvocationCwd, explicitCwd);
15056
15359
  const conflict = findWorktreeTaskCwdConflict(tasks.map((task) => ({ agent: task.name, cwd: resolvedTaskCwd(task.cwd, workflowInvocationCwd) })), sharedCwd);
15057
15360
  if (conflict !== undefined)
15058
15361
  throw new Error(formatWorktreeTaskCwdConflict(conflict, sharedCwd));
@@ -15067,9 +15370,9 @@ function prepareTaskWorktrees(tasks, options, runId, scope, workflowInvocationCw
15067
15370
  tasks: tasks.map((task, index) => ({ ...task, cwd: setup.worktrees[index].agentCwd })),
15068
15371
  setup,
15069
15372
  agents,
15070
- diffsDir: join11(setup.cwd, CONFIG_DIR_NAME, "workflows", "worktree-diffs", runId, scope),
15373
+ diffsDir: join12(setup.cwd, CONFIG_DIR_NAME, "workflows", "worktree-diffs", runId, scope),
15071
15374
  outputIsolations: tasks.map((_, index) => ({
15072
- baseDir: join11(trustedRoot, runId, scope, String(index)),
15375
+ baseDir: join12(trustedRoot, runId, scope, String(index)),
15073
15376
  trustedRoot
15074
15377
  }))
15075
15378
  };
@@ -16796,8 +17099,12 @@ function createStageControlRegistry() {
16796
17099
  }
16797
17100
  await disposeEntries(entries);
16798
17101
  },
16799
- async releaseIdle(runId) {
16800
- for (const [stageId, entry] of [..._byRun.get(runId) ?? []]) {
17102
+ async releaseIdle(runId, only) {
17103
+ const runMap = _byRun.get(runId);
17104
+ const entries = only === undefined ? [...runMap ?? []] : [[only, runMap?.get(only)]];
17105
+ for (const [stageId, entry] of entries) {
17106
+ if (entry === undefined)
17107
+ continue;
16801
17108
  try {
16802
17109
  await releaseIdleEntry(runId, stageId, entry);
16803
17110
  } catch (error) {
@@ -16858,8 +17165,8 @@ var init_possible_stages = __esm(() => {
16858
17165
 
16859
17166
  // dist/builtin/workflows/src/workers/pool-journal.ts
16860
17167
  import { createHash as createHash3, randomUUID as randomUUID6 } from "node:crypto";
16861
- import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
16862
- import { basename as basename5, dirname as dirname9, join as join12, resolve as resolve9 } from "node:path";
17168
+ import { existsSync as existsSync4, mkdirSync as mkdirSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
17169
+ import { basename as basename5, dirname as dirname10, join as join13, resolve as resolve10 } from "node:path";
16863
17170
  import { storageSelectionKey as storageSelectionKey2 } from "@bastani/atomic/storage";
16864
17171
  function encodeOption(value2) {
16865
17172
  if (typeof value2 === "bigint")
@@ -16881,7 +17188,7 @@ function isReopenableSelection(selection) {
16881
17188
  }
16882
17189
  function encodeSelection(selection) {
16883
17190
  if (selection.kind === "sqlite")
16884
- return JSON.stringify({ ...selection, path: resolve9(selection.path) });
17191
+ return JSON.stringify({ ...selection, path: resolve10(selection.path) });
16885
17192
  return JSON.stringify({
16886
17193
  ...selection,
16887
17194
  ...selection.options === undefined ? {} : {
@@ -16913,7 +17220,7 @@ function openDurableJournal(durable) {
16913
17220
  return openSelectedRunJournal(decodeSelection(durable.selection), durable.agentDir);
16914
17221
  }
16915
17222
  function poolStateDirectory(agentDir) {
16916
- return join12(dirname9(agentDir), "workflows", "pool");
17223
+ return join13(dirname10(agentDir), "workflows", "pool");
16917
17224
  }
16918
17225
  function databaseIdentity(selection, agentDir) {
16919
17226
  return storageSelectionKey2(selection.kind === "adapter" ? { ...selection, secrets: undefined } : selection, agentDir);
@@ -16921,16 +17228,16 @@ function databaseIdentity(selection, agentDir) {
16921
17228
  function coordinationPath(durable) {
16922
17229
  const selection = decodeSelection(durable.selection);
16923
17230
  const identity = createHash3("sha256").update(databaseIdentity(selection, durable.agentDir)).digest("hex");
16924
- return join12(poolStateDirectory(durable.agentDir), `${identity.slice(0, 24)}.sqlite`);
17231
+ return join13(poolStateDirectory(durable.agentDir), `${identity.slice(0, 24)}.sqlite`);
16925
17232
  }
16926
17233
  function coordinationDatabase(durable) {
16927
17234
  return basename5(coordinationPath(durable), ".sqlite");
16928
17235
  }
16929
17236
  function openCoordinationJournal(path) {
16930
- const file = resolve9(path);
17237
+ const file = resolve10(path);
16931
17238
  let opened = coordinationJournals.get(file);
16932
17239
  if (opened === undefined) {
16933
- mkdirSync5(dirname9(file), { recursive: true });
17240
+ mkdirSync6(dirname10(file), { recursive: true });
16934
17241
  opened = resolveRunJournal({ journal: { path: file } });
16935
17242
  coordinationJournals.set(file, opened);
16936
17243
  opened.catch(() => {
@@ -16947,20 +17254,20 @@ function poolJournalFor(journal) {
16947
17254
  return coordinations.get(journal) ?? journal;
16948
17255
  }
16949
17256
  function poolHostId(agentDir) {
16950
- const file = join12(poolStateDirectory(agentDir), "host-id");
17257
+ const file = join13(poolStateDirectory(agentDir), "host-id");
16951
17258
  try {
16952
- const existing = readFileSync2(file, "utf8").trim();
17259
+ const existing = readFileSync4(file, "utf8").trim();
16953
17260
  if (existing !== "")
16954
17261
  return existing;
16955
17262
  } catch {}
16956
- mkdirSync5(dirname9(file), { recursive: true });
17263
+ mkdirSync6(dirname10(file), { recursive: true });
16957
17264
  const id = randomUUID6();
16958
17265
  try {
16959
- writeFileSync3(file, `${id}
17266
+ writeFileSync4(file, `${id}
16960
17267
  `, { flag: "wx" });
16961
17268
  return id;
16962
17269
  } catch {
16963
- return readFileSync2(file, "utf8").trim();
17270
+ return readFileSync4(file, "utf8").trim();
16964
17271
  }
16965
17272
  }
16966
17273
  var POOL_DURABLE_JOURNAL_ENV = "ATOMIC_POOL_DURABLE_JOURNAL", coordinationJournals, coordinations;
@@ -16975,7 +17282,7 @@ var init_pool_journal = __esm(() => {
16975
17282
 
16976
17283
  // dist/builtin/workflows/src/workers/admission-host.ts
16977
17284
  import { execFile, execFileSync } from "node:child_process";
16978
- import { readdirSync as readdirSync2, readFileSync as readFileSync3 } from "node:fs";
17285
+ import { readdirSync as readdirSync4, readFileSync as readFileSync5 } from "node:fs";
16979
17286
  import { readFile } from "node:fs/promises";
16980
17287
  import { hostname } from "node:os";
16981
17288
  function admissionBootIdentity() {
@@ -17006,7 +17313,7 @@ function workerProcessExited(pid, final = false) {
17006
17313
  return true;
17007
17314
  if (process.platform === "linux") {
17008
17315
  try {
17009
- const stat = readFileSync3(`/proc/${pid}/stat`, "utf8");
17316
+ const stat = readFileSync5(`/proc/${pid}/stat`, "utf8");
17010
17317
  return stat.slice(stat.lastIndexOf(")") + 2).startsWith("Z");
17011
17318
  } catch {
17012
17319
  return admissionProcessStopped(pid);
@@ -17026,7 +17333,7 @@ function scanProcessGroup(group) {
17026
17333
  let complete = true;
17027
17334
  let entries;
17028
17335
  try {
17029
- entries = readdirSync2("/proc");
17336
+ entries = readdirSync4("/proc");
17030
17337
  } catch {
17031
17338
  return { members, complete: false };
17032
17339
  }
@@ -17035,7 +17342,7 @@ function scanProcessGroup(group) {
17035
17342
  continue;
17036
17343
  let stat;
17037
17344
  try {
17038
- stat = readFileSync3(`/proc/${entry}/stat`, "utf8");
17345
+ stat = readFileSync5(`/proc/${entry}/stat`, "utf8");
17039
17346
  } catch {
17040
17347
  complete = false;
17041
17348
  continue;
@@ -17266,6 +17573,7 @@ class SqlResourceAdmission {
17266
17573
  epoch: this.options.attempt.epoch
17267
17574
  }) : null);
17268
17575
  });
17576
+ let quarantinedSince;
17269
17577
  try {
17270
17578
  for (;; ) {
17271
17579
  signal?.throwIfAborted();
@@ -17275,7 +17583,9 @@ class SqlResourceAdmission {
17275
17583
  }
17276
17584
  const hostReason = this.hostReason();
17277
17585
  const pressure = this.stageStartPressure(kind, request?.delegated);
17586
+ let quarantined = undefined;
17278
17587
  const granted = await this.storage.transaction(async (tx) => {
17588
+ quarantined = undefined;
17279
17589
  await tx.run(`UPDATE ${this.owners} SET expires_at = CAST(unixepoch('subsec') * 1000 AS INTEGER) + ? WHERE id = ?`, OWNER_LEASE_MS, this.owner);
17280
17590
  const { root } = this.options;
17281
17591
  if (!await this.assertCurrentAttempt(tx)) {
@@ -17293,6 +17603,8 @@ class SqlResourceAdmission {
17293
17603
  if (control && control.state !== "running")
17294
17604
  reason ??= `root ${control.state}`;
17295
17605
  }
17606
+ const held = [];
17607
+ let blockedOtherwise = reason !== undefined;
17296
17608
  for (const scope of [`root:${root}`, "user"]) {
17297
17609
  const row = await tx.get(`SELECT config FROM ${this.configuration} WHERE scope = ?`, scope);
17298
17610
  const limit = row ? JSON.parse(String(row.config))[kind] : undefined;
@@ -17302,9 +17614,21 @@ class SqlResourceAdmission {
17302
17614
  const args = scope === "user" ? [kind] : [kind, root];
17303
17615
  const count = await tx.get(`SELECT COUNT(*) AS count FROM ${this.permits} WHERE kind = ? ${rootClause} AND state = 'granted'`, ...args);
17304
17616
  const older = await tx.get(`SELECT 1 AS waiting FROM ${this.permits} WHERE kind = ? ${rootClause} AND state = 'waiting' AND reason IS NOT ? AND ticket < (SELECT ticket FROM ${this.permits} WHERE operation = ?) LIMIT 1`, ...args, STAGE_START_PRESSURE_REASON, operation);
17305
- if (Number(count?.count) >= limit || older)
17306
- reason ??= `${scope} ${kind}`;
17617
+ const used = Number(count?.count);
17618
+ if (used < limit && !older)
17619
+ continue;
17620
+ reason ??= `${scope} ${kind}`;
17621
+ const quarantinedPermits = kind === "process" && used >= limit ? await tx.all(`SELECT process_identity FROM ${this.permits} WHERE kind = ? ${rootClause} AND state = 'granted' AND reason = ?`, ...args, QUARANTINED_REASON) : [];
17622
+ if (quarantinedPermits.length > 0 && used - quarantinedPermits.length < limit)
17623
+ held.push({
17624
+ limit: `${scope === "user" ? "admission.userLimits" : "admission.limits"}.${kind} (${limit})`,
17625
+ groups: quarantinedPermits.flatMap(({ process_identity }) => process_identity === null ? [] : [JSON.parse(process_identity).id])
17626
+ });
17627
+ else
17628
+ blockedOtherwise = true;
17307
17629
  }
17630
+ if (!blockedOtherwise && held.length > 0)
17631
+ quarantined = held;
17308
17632
  if (reason && request?.delegated)
17309
17633
  throw new Error(`Delegated session cannot be admitted without capacity headroom: ${reason}`);
17310
17634
  const stageStart = kind === "session" && request?.delegated !== true;
@@ -17317,6 +17641,14 @@ class SqlResourceAdmission {
17317
17641
  signal?.throwIfAborted();
17318
17642
  return release;
17319
17643
  }
17644
+ if (quarantined === undefined)
17645
+ quarantinedSince = undefined;
17646
+ else {
17647
+ const now = performance.now();
17648
+ quarantinedSince ??= now;
17649
+ if (now - quarantinedSince >= QUARANTINED_CAPACITY_WAIT_MS)
17650
+ throw new QuarantinedCapacityError(quarantined, now - quarantinedSince);
17651
+ }
17320
17652
  await delay(50 + Math.floor(Math.random() * 25), undefined, { signal });
17321
17653
  }
17322
17654
  } catch (error) {
@@ -17358,9 +17690,9 @@ class SqlResourceAdmission {
17358
17690
  await tx.run(`UPDATE ${this.permits} SET state = 'released', reason = 'host reboot confirmed' WHERE owner = ? AND state = 'granted'`, owner.id);
17359
17691
  } else if (stopped) {
17360
17692
  await tx.run(`UPDATE ${this.permits} SET state = 'released', reason = 'owner death confirmed' WHERE owner = ? AND kind = 'session' AND state = 'granted'`, owner.id);
17361
- await tx.run(`UPDATE ${this.permits} SET reason = ? WHERE owner = ? AND kind = 'process' AND state = 'granted' AND reason IS NOT ?`, UNCONFIRMED_REASON, owner.id, UNCONFIRMED_REASON);
17693
+ await tx.run(`UPDATE ${this.permits} SET reason = ? WHERE owner = ? AND kind = 'process' AND state = 'granted' AND reason IS NOT ? AND reason IS NOT ?`, UNCONFIRMED_REASON, owner.id, UNCONFIRMED_REASON, QUARANTINED_REASON);
17362
17694
  } else {
17363
- await tx.run(`UPDATE ${this.permits} SET reason = ? WHERE owner = ? AND state = 'granted' AND reason IS NOT ? AND EXISTS (SELECT 1 FROM ${this.owners} WHERE id = ? AND expires_at < ${NOW_MS})`, EXPIRED_REASON, owner.id, EXPIRED_REASON, owner.id);
17695
+ await tx.run(`UPDATE ${this.permits} SET reason = ? WHERE owner = ? AND state = 'granted' AND reason IS NOT ? AND reason IS NOT ? AND EXISTS (SELECT 1 FROM ${this.owners} WHERE id = ? AND expires_at < ${NOW_MS})`, EXPIRED_REASON, owner.id, EXPIRED_REASON, QUARANTINED_REASON, owner.id);
17364
17696
  }
17365
17697
  if (owner.host === admissionHost && process.platform !== "win32") {
17366
17698
  const processes = await tx.all(`SELECT * FROM ${this.permits} WHERE owner = ? AND state = 'granted' AND kind = 'process' AND process_identity IS NOT NULL`, owner.id);
@@ -17388,9 +17720,10 @@ class SqlResourceAdmission {
17388
17720
  return true;
17389
17721
  if (permit.state === "waiting")
17390
17722
  return stopped || !!permit.expired;
17391
- if (stopped && (permit.kind === "session" || permit.reason !== UNCONFIRMED_REASON))
17723
+ const quarantined = permit.reason === QUARANTINED_REASON;
17724
+ if (stopped && (permit.kind === "session" || permit.reason !== UNCONFIRMED_REASON && !quarantined))
17392
17725
  return true;
17393
- if (!stopped && permit.expired && permit.reason !== EXPIRED_REASON)
17726
+ if (!stopped && permit.expired && permit.reason !== EXPIRED_REASON && !quarantined)
17394
17727
  return true;
17395
17728
  return permit.kind === "process" && permit.host === admissionHost && process.platform !== "win32" && permit.process_identity !== null && processGroupStopped(permit.process_identity);
17396
17729
  }
@@ -17401,7 +17734,9 @@ class SqlResourceAdmission {
17401
17734
  await this.reconcile();
17402
17735
  return this.storage.transaction(async (tx) => {
17403
17736
  await tx.run(`UPDATE ${this.permits} SET state = 'released', reason = 'worker exit confirmed' WHERE root = ? AND holder = ? AND state IN ('waiting', 'granted') AND (kind = 'session' OR state = 'waiting')`, this.options.root, holder);
17404
- return (await tx.run(`UPDATE ${this.permits} SET reason = 'cleanup unconfirmed' WHERE root = ? AND holder = ? AND kind = 'process' AND state = 'granted'`, this.options.root, holder)).changes;
17737
+ await tx.run(`UPDATE ${this.permits} SET reason = ? WHERE root = ? AND holder = ? AND kind = 'process' AND state = 'granted' AND reason IS NOT ?`, UNCONFIRMED_REASON, this.options.root, holder, QUARANTINED_REASON);
17738
+ const held = await tx.get(`SELECT COUNT(*) AS count FROM ${this.permits} WHERE root = ? AND holder = ? AND kind = 'process' AND state = 'granted'`, this.options.root, holder);
17739
+ return Number(held?.count);
17405
17740
  });
17406
17741
  }
17407
17742
  async reapStoppedProcesses(holder) {
@@ -17448,10 +17783,13 @@ async function settleProcessGroups(storage, root, scope, reap) {
17448
17783
  survivors = await settle(REAP_KILL_GRACE_MS);
17449
17784
  }
17450
17785
  const confirmed = [...groups].flatMap(([group, operations]) => survivors.includes(group) ? [] : operations);
17451
- if (confirmed.length > 0)
17786
+ const quarantined = reap ? survivors.flatMap((group) => groups.get(group) ?? []) : [];
17787
+ if (confirmed.length > 0 || quarantined.length > 0)
17452
17788
  await storage.transaction(async (tx) => {
17453
17789
  for (const operation of confirmed)
17454
17790
  await tx.run(`UPDATE ${permits} SET state = 'released', reason = 'process group cleanup confirmed' WHERE operation = ? AND state = 'granted'`, operation);
17791
+ for (const operation of quarantined)
17792
+ await tx.run(`UPDATE ${permits} SET reason = ? WHERE operation = ? AND state = 'granted'`, QUARANTINED_REASON, operation);
17455
17793
  });
17456
17794
  return { survivors, unprobeable };
17457
17795
  }
@@ -17468,11 +17806,20 @@ async function reapProcessGroups(journal, root, scope) {
17468
17806
  return { survivors: [], unprobeable: [] };
17469
17807
  return settleProcessGroups(storage, root, scope, true);
17470
17808
  }
17471
- var OWNER_LEASE_MS = 30000, REAP_TERM_GRACE_MS = 5000, REAP_KILL_GRACE_MS = 1e4, REAP_POLL_MS = 50, RELEASED_PERMIT_RETENTION = 256, RELEASED_PERMIT_PRUNE_BATCH = 1000, EXPIRED_REASON = "owner expired; cleanup unconfirmed", UNCONFIRMED_REASON = "cleanup unconfirmed", ANONYMOUS_REASON = "owner identity unavailable; cleanup unconfirmed", NOW_MS = "CAST(unixepoch('subsec') * 1000 AS INTEGER)", LATE_ATTEMPT_POLL_MS = 100, LATE_ATTEMPT_REASON = "waiting for its attempt lease", STAGE_START_SPACING_MS = 5000, STAGE_START_PRESSURE_REASON = "paced for host load";
17809
+ var OWNER_LEASE_MS = 30000, REAP_TERM_GRACE_MS = 5000, REAP_KILL_GRACE_MS = 1e4, REAP_POLL_MS = 50, RELEASED_PERMIT_RETENTION = 256, RELEASED_PERMIT_PRUNE_BATCH = 1000, EXPIRED_REASON = "owner expired; cleanup unconfirmed", UNCONFIRMED_REASON = "cleanup unconfirmed", QUARANTINED_REASON = "cleanup unconfirmed; permit quarantined", ANONYMOUS_REASON = "owner identity unavailable; cleanup unconfirmed", QUARANTINED_CAPACITY_WAIT_MS, QuarantinedCapacityError, NOW_MS = "CAST(unixepoch('subsec') * 1000 AS INTEGER)", LATE_ATTEMPT_POLL_MS = 100, LATE_ATTEMPT_REASON = "waiting for its attempt lease", STAGE_START_SPACING_MS = 5000, STAGE_START_PRESSURE_REASON = "paced for host load";
17472
17810
  var init_resource_admission = __esm(() => {
17473
17811
  init_internal();
17474
17812
  init_sql_run_journal();
17813
+ init_workflow_infrastructure_error();
17475
17814
  init_admission_host();
17815
+ QUARANTINED_CAPACITY_WAIT_MS = REAP_TERM_GRACE_MS + REAP_KILL_GRACE_MS;
17816
+ QuarantinedCapacityError = class QuarantinedCapacityError extends WorkflowInfrastructureError {
17817
+ constructor(capacity, waitedMs) {
17818
+ const limits = capacity.map(({ limit }) => limit).join(" and ");
17819
+ const groups = [...new Set(capacity.flatMap(({ groups }) => groups))].join(", ");
17820
+ super("worker_cleanup_unconfirmed", `Local worker process cleanup unconfirmed; process permit refused after ${Math.round(waitedMs)} ms: ${limits} ${capacity.length === 1 ? "is" : "are"} held by quarantined permits of process groups ${groups} that survived SIGKILL`);
17821
+ }
17822
+ };
17476
17823
  });
17477
17824
 
17478
17825
  // dist/builtin/workflows/src/workers/admission-binding.ts
@@ -53774,10 +54121,10 @@ var init_verification_prompts = __esm(() => {
53774
54121
  // dist/builtin/workflows/builtin/pattern-artifact-root.ts
53775
54122
  import { randomUUID as randomUUID9 } from "node:crypto";
53776
54123
  import { mkdir as mkdir2 } from "node:fs/promises";
53777
- import { join as join13 } from "node:path";
54124
+ import { join as join14 } from "node:path";
53778
54125
  async function stableArtifactRoot(ctx, workflowName) {
53779
54126
  const cwd = ctx.cwd ?? process.cwd();
53780
- const artifactDir = await ctx.tool("artifact-root", { workflow: workflowName }, async () => join13(cwd, ".atomic", "workflows", "runs", `${workflowName}-${randomUUID9()}`));
54127
+ const artifactDir = await ctx.tool("artifact-root", { workflow: workflowName }, async () => join14(cwd, ".atomic", "workflows", "runs", `${workflowName}-${randomUUID9()}`));
53781
54128
  await mkdir2(artifactDir, { recursive: true });
53782
54129
  return artifactDir;
53783
54130
  }
@@ -53817,7 +54164,7 @@ function fold_usage(results) {
53817
54164
 
53818
54165
  // dist/builtin/workflows/builtin/adversarial-verification-runner.ts
53819
54166
  import { readFile as readFile2, writeFile } from "node:fs/promises";
53820
- import { join as join14 } from "node:path";
54167
+ import { join as join15 } from "node:path";
53821
54168
  import { Type as Type4 } from "typebox";
53822
54169
  function isRecord3(value2) {
53823
54170
  return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
@@ -53912,8 +54259,8 @@ async function writeInvalid(path, stage) {
53912
54259
  }
53913
54260
  async function runAdversarialVerification(ctx) {
53914
54261
  const root = await stableArtifactRoot(ctx, "adversarial-verification");
53915
- const criteriaPath = join14(root, "criteria.md");
53916
- const candidatePath = join14(root, "candidate.md");
54262
+ const criteriaPath = join15(root, "criteria.md");
54263
+ const candidatePath = join15(root, "candidate.md");
53917
54264
  const resolvedCriteria = resolveCriteria(ctx.inputs.criteria);
53918
54265
  await writeFile(criteriaPath, renderCriteriaMarkdown(resolvedCriteria));
53919
54266
  await ctx.task("worker", { prompt: renderWorkerPrompt(ctx.inputs.task), context: "fresh", output: candidatePath, outputMode: "file-only" });
@@ -53945,7 +54292,7 @@ async function runAdversarialVerification(ctx) {
53945
54292
  criterion,
53946
54293
  verifierIndex: index + 1,
53947
54294
  name,
53948
- artifactPath: join14(root, `verification-${round}-${criterion.id}-${index + 1}.json`)
54295
+ artifactPath: join15(root, `verification-${round}-${criterion.id}-${index + 1}.json`)
53949
54296
  });
53950
54297
  }
53951
54298
  }
@@ -53998,8 +54345,8 @@ async function runAdversarialVerification(ctx) {
53998
54345
  const decision = decide_verification(roundResult, { acceptMean, quorumFraction: QUORUM_FRACTION });
53999
54346
  finalDecision = decision;
54000
54347
  finalMean = meanScore(scoreReports);
54001
- scoreTablePath = join14(root, `verification-summary-${round}.json`);
54002
- reviewReportPath = join14(root, `review-${round}.json`);
54348
+ scoreTablePath = join15(root, `verification-summary-${round}.json`);
54349
+ reviewReportPath = join15(root, `review-${round}.json`);
54003
54350
  const summary = {
54004
54351
  scores: scoreReports,
54005
54352
  mean: finalMean,
@@ -54180,7 +54527,7 @@ var GROUNDED_REPORTING2 = "Before reporting progress, audit each claim against a
54180
54527
 
54181
54528
  // dist/builtin/workflows/builtin/classify-and-act-runner.ts
54182
54529
  import { writeFile as writeFile2 } from "node:fs/promises";
54183
- import { join as join15 } from "node:path";
54530
+ import { join as join16 } from "node:path";
54184
54531
  import { Type as Type6 } from "typebox";
54185
54532
  function safeName(value2) {
54186
54533
  const normalized = value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
@@ -54220,7 +54567,7 @@ async function runClassifyAndAct(ctx) {
54220
54567
  const needsFallback = exactCategory === undefined || confidence < ctx.inputs.confidence_threshold;
54221
54568
  const fallback = needsFallback ? await resolveFallbackCategory(ctx, exactCategory) : undefined;
54222
54569
  const category = fallback?.category ?? exactCategory;
54223
- const classificationPath = join15(artifactDir, "classification.json");
54570
+ const classificationPath = join16(artifactDir, "classification.json");
54224
54571
  await writeFile2(classificationPath, JSON.stringify({
54225
54572
  proposed_category: proposedCategory,
54226
54573
  selected_category: category,
@@ -54230,7 +54577,7 @@ async function runClassifyAndAct(ctx) {
54230
54577
  fallback_used: needsFallback,
54231
54578
  fallback_mode: fallback?.fallbackMode ?? "none"
54232
54579
  }, null, 2));
54233
- const actionPath = join15(artifactDir, `action-${safeName(category)}.md`);
54580
+ const actionPath = join16(artifactDir, `action-${safeName(category)}.md`);
54234
54581
  const action = await ctx.task(`action-${safeName(category)}`, {
54235
54582
  tools: actionTools(category),
54236
54583
  prompt: actionPrompt({ prompt: ctx.inputs.prompt, category, classificationPath }),
@@ -54370,7 +54717,7 @@ var GROUNDED_REPORTING3 = "Before reporting progress, audit each claim against a
54370
54717
 
54371
54718
  // dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts
54372
54719
  import { writeFile as writeFile3 } from "node:fs/promises";
54373
- import { join as join16 } from "node:path";
54720
+ import { join as join17 } from "node:path";
54374
54721
  import { Type as Type8 } from "typebox";
54375
54722
  function safeName2(value2, index) {
54376
54723
  const normalized = value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
@@ -54401,9 +54748,9 @@ async function runFanOutAndSynthesize(ctx) {
54401
54748
  context: "fresh"
54402
54749
  });
54403
54750
  const partitions = parsedPartitions(plan.structured, ctx.inputs.max_branches, ctx.inputs.prompt);
54404
- const partitionPath = join16(artifactDir, "partition-plan.json");
54751
+ const partitionPath = join17(artifactDir, "partition-plan.json");
54405
54752
  await writeFile3(partitionPath, JSON.stringify({ task: ctx.inputs.prompt, partitions }, null, 2));
54406
- const branchPaths = partitions.map((partition, index) => join16(artifactDir, `branch-${safeName2(partition.label, index)}.md`));
54753
+ const branchPaths = partitions.map((partition, index) => join17(artifactDir, `branch-${safeName2(partition.label, index)}.md`));
54407
54754
  const steps = partitions.map((partition, index) => ({
54408
54755
  name: `branch-${safeName2(partition.label, index)}`,
54409
54756
  prompt: branchPrompt({ prompt: ctx.inputs.prompt, ...partition }),
@@ -54413,13 +54760,13 @@ async function runFanOutAndSynthesize(ctx) {
54413
54760
  outputMode: "file-only"
54414
54761
  }));
54415
54762
  await ctx.parallel(steps, { concurrency: Math.min(ctx.inputs.max_concurrency, partitions.length), failFast: false });
54416
- const manifestPath = join16(artifactDir, "manifest.json");
54763
+ const manifestPath = join17(artifactDir, "manifest.json");
54417
54764
  await writeFile3(manifestPath, JSON.stringify({
54418
54765
  task: ctx.inputs.prompt,
54419
54766
  partition_plan: partitionPath,
54420
54767
  branches: partitions.map((partition, index) => ({ ...partition, artifact_path: branchPaths[index] }))
54421
54768
  }, null, 2));
54422
- const synthesisPath = join16(artifactDir, "synthesis.md");
54769
+ const synthesisPath = join17(artifactDir, "synthesis.md");
54423
54770
  const synthesis = await ctx.task("synthesize", {
54424
54771
  prompt: synthesisPrompt(ctx.inputs.prompt, manifestPath),
54425
54772
  context: "fresh",
@@ -54584,7 +54931,7 @@ var init_generate_and_filter_prompts = __esm(() => {
54584
54931
 
54585
54932
  // dist/builtin/workflows/builtin/generate-and-filter-runner.ts
54586
54933
  import { readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
54587
- import { join as join17 } from "node:path";
54934
+ import { join as join18 } from "node:path";
54588
54935
  import { Type as Type10 } from "typebox";
54589
54936
  function isRecord4(value2) {
54590
54937
  return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
@@ -54603,7 +54950,7 @@ function judgeDecision(value2) {
54603
54950
  }
54604
54951
  async function runGenerateAndFilter(ctx) {
54605
54952
  const root = await stableArtifactRoot(ctx, "generate-and-filter");
54606
- const candidatePaths = Array.from({ length: ctx.inputs.num_candidates }, (_, index) => join17(root, `candidate-${index + 1}.md`));
54953
+ const candidatePaths = Array.from({ length: ctx.inputs.num_candidates }, (_, index) => join18(root, `candidate-${index + 1}.md`));
54607
54954
  const shortlistLimit = Math.min(ctx.inputs.shortlist_size, ctx.inputs.num_candidates);
54608
54955
  await ctx.parallel(candidatePaths.map((path, index) => ({
54609
54956
  name: `generate-${index + 1}`,
@@ -54612,9 +54959,9 @@ async function runGenerateAndFilter(ctx) {
54612
54959
  output: path,
54613
54960
  outputMode: "file-only"
54614
54961
  })), { concurrency: ctx.inputs.max_concurrency, failFast: false });
54615
- const manifestPath = join17(root, "manifest.json");
54962
+ const manifestPath = join18(root, "manifest.json");
54616
54963
  await writeFile4(manifestPath, JSON.stringify({ task: ctx.inputs.prompt, candidate_artifact_paths: candidatePaths }, null, 2));
54617
- const filterPath = join17(root, "filter.json");
54964
+ const filterPath = join18(root, "filter.json");
54618
54965
  const filtered = await ctx.task("dedupe-and-filter", {
54619
54966
  prompt: renderFilterPrompt(ctx.inputs.prompt, candidatePaths, shortlistLimit),
54620
54967
  context: "fresh",
@@ -54631,7 +54978,7 @@ async function runGenerateAndFilter(ctx) {
54631
54978
  let judgePath = null;
54632
54979
  let decisionPath = filterPath;
54633
54980
  if (ctx.inputs.use_judge) {
54634
- judgePath = join17(root, "judge.json");
54981
+ judgePath = join18(root, "judge.json");
54635
54982
  const judgeCandidates = await Promise.all(shortlist.map(async (path) => ({ path, body: await readFile3(path, "utf8") })));
54636
54983
  const judgePrompt = renderJudgePrompt(ctx.inputs.prompt, filterPath, shortlistLimit, judgeCandidates);
54637
54984
  const judged = await ctx.task("judge", {
@@ -54647,7 +54994,7 @@ async function runGenerateAndFilter(ctx) {
54647
54994
  shortlist = judgedShortlist.length > 0 ? judgedShortlist : shortlist;
54648
54995
  decisionPath = judgePath;
54649
54996
  }
54650
- const finalPath = join17(root, "shortlist.md");
54997
+ const finalPath = join18(root, "shortlist.md");
54651
54998
  const finalShortlist = await ctx.task("final-shortlist", {
54652
54999
  prompt: renderFinalShortlistPrompt(ctx.inputs.prompt, decisionPath),
54653
55000
  context: "fresh",
@@ -55176,7 +55523,7 @@ var init_review_convergence = __esm(() => {
55176
55523
 
55177
55524
  // dist/builtin/workflows/builtin/goal-artifacts.ts
55178
55525
  import { writeFile as writeFile5 } from "node:fs/promises";
55179
- import { join as join18 } from "node:path";
55526
+ import { join as join19 } from "node:path";
55180
55527
  function artifactSafeName(value2) {
55181
55528
  const safe = value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
55182
55529
  return safe.length > 0 ? safe : "artifact";
@@ -55187,7 +55534,7 @@ function withoutTurn(value2) {
55187
55534
  return copy;
55188
55535
  }
55189
55536
  async function writeReviewArtifact(artifactDir, reviewer, decision, rawText, convergenceDecision) {
55190
- const artifactPath = join18(artifactDir, `review-${artifactSafeName(reviewer)}.json`);
55537
+ const artifactPath = join19(artifactDir, `review-${artifactSafeName(reviewer)}.json`);
55191
55538
  await writeFile5(artifactPath, `${JSON.stringify({ reviewer, decision, convergence_decision: convergenceDecision, raw_text: rawText }, null, 2)}
55192
55539
  `, { encoding: "utf8" });
55193
55540
  return artifactPath;
@@ -55196,7 +55543,7 @@ async function writeReviewRoundArtifact(artifactDir, reviews, consolidatedFindin
55196
55543
  reviewer: review.reviewer,
55197
55544
  findings: review.findings
55198
55545
  }))), reverification = []) {
55199
- const artifactPath = join18(artifactDir, "review-round-latest.json");
55546
+ const artifactPath = join19(artifactDir, "review-round-latest.json");
55200
55547
  const visibleReviews = reviews.map(withoutTurn);
55201
55548
  await writeFile5(artifactPath, `${JSON.stringify({ reviews: visibleReviews, consolidated_findings: consolidatedFindings, reverification }, null, 2)}
55202
55549
  `, { encoding: "utf8" });
@@ -55426,7 +55773,7 @@ var init_goal_review = __esm(() => {
55426
55773
  // dist/builtin/workflows/builtin/goal-ledger.ts
55427
55774
  import { randomUUID as randomUUID10 } from "node:crypto";
55428
55775
  import { readFile as readFile4, rename, rm as rm2, writeFile as writeFile6 } from "node:fs/promises";
55429
- import { dirname as dirname10, join as join19 } from "node:path";
55776
+ import { dirname as dirname11, join as join20 } from "node:path";
55430
55777
  import { isDeepStrictEqual } from "node:util";
55431
55778
  function withoutTurn2(value2) {
55432
55779
  const copy = { ...value2 };
@@ -55451,7 +55798,7 @@ function modelVisibleLedger(ledger) {
55451
55798
  };
55452
55799
  }
55453
55800
  function goalLedgerStatePath(ledgerPath) {
55454
- return join19(dirname10(ledgerPath), LEDGER_STATE_FILENAME);
55801
+ return join20(dirname11(ledgerPath), LEDGER_STATE_FILENAME);
55455
55802
  }
55456
55803
  function appendLifecycleEvent(ledger, event, summary, turn = ledger.turns) {
55457
55804
  ledger.lifecycle.push({
@@ -55527,7 +55874,7 @@ function recoverLegacyExecutionInterruption(ledger) {
55527
55874
  return true;
55528
55875
  }
55529
55876
  async function createGoalLedger(objective, acceptanceCriteria, artifactDir) {
55530
- const ledgerPath = join19(artifactDir, LEDGER_FILENAME);
55877
+ const ledgerPath = join20(artifactDir, LEDGER_FILENAME);
55531
55878
  const existing = await readExistingGoalLedger(ledgerPath);
55532
55879
  if (existing !== undefined) {
55533
55880
  if (recoverLegacyExecutionInterruption(existing))
@@ -56034,7 +56381,7 @@ var init_goal_orchestrator_prompts = __esm(() => {
56034
56381
  });
56035
56382
 
56036
56383
  // dist/builtin/workflows/builtin/goal-runner.ts
56037
- import { join as join20 } from "node:path";
56384
+ import { join as join21 } from "node:path";
56038
56385
  function positiveInteger(value2, fallback) {
56039
56386
  if (typeof value2 !== "number" || !Number.isFinite(value2) || value2 <= 0) {
56040
56387
  return fallback;
@@ -56085,7 +56432,7 @@ async function runGoalWorkflow(ctx, options) {
56085
56432
  appendLifecycleEvent(ledger, "work_turn_started", "Orchestrator started.", turn);
56086
56433
  await writeGoalLedger(ledgerPath, ledger);
56087
56434
  }
56088
- const orchestratorReceiptPath = join20(artifactDir, "orchestrator-receipt.md");
56435
+ const orchestratorReceiptPath = join21(artifactDir, "orchestrator-receipt.md");
56089
56436
  const orchestratorForkOptions = forkContinuationOptions(previousOrchestratorSessionId);
56090
56437
  const orchestratorPrompt = orchestratorForkOptions.forkFromSessionId === undefined ? renderGoalOrchestratorPrompt({
56091
56438
  ledger,
@@ -56156,7 +56503,7 @@ async function runGoalWorkflow(ctx, options) {
56156
56503
  const reviewerName = result.name ?? result.stageName;
56157
56504
  const normalizedReviewerName = reviewerName.replace(/-\d+$/u, "");
56158
56505
  const parsed = reviewerContractError === undefined ? parsedReviewDecisionFromResult(result, reviewerName) : { decision: reviewerErrorDecision(reviewerContractError.message), parsed: false, diagnostics: [reviewerContractError.message] };
56159
- const reviewArtifactPath = join20(artifactDir, `review-${artifactSafeName(normalizedReviewerName)}.json`);
56506
+ const reviewArtifactPath = join21(artifactDir, `review-${artifactSafeName(normalizedReviewerName)}.json`);
56160
56507
  const record = reviewDecisionToRecord({
56161
56508
  turn,
56162
56509
  reviewer: normalizedReviewerName,
@@ -56359,7 +56706,7 @@ var init_goal = __esm(() => {
56359
56706
  }),
56360
56707
  git_worktree_dir: Type15.String({
56361
56708
  default: "",
56362
- description: "Optional Git worktree path. Leave at the default unless the user explicitly requested worktree isolation — stages never create git worktrees on their own. Must start inside a Git repo; absolute paths are used as-is, relative paths resolve from the repo root, existing Git worktrees from the invoking repository are reused/shared as-is, and missing paths are created from base_branch."
56709
+ description: "Optional Git worktree path. Leave at the default unless the user explicitly requested worktree isolation — stages never create git worktrees on their own. Absolute paths are used as-is; relative paths resolve from the invoking repo root (or the invoking cwd outside a Git repo). An existing Git worktree root of any repository is reused as-is, even from another repository or a non-Git cwd. Creating a worktree requires starting inside the source Git repo: a missing path or an empty directory becomes a new worktree from base_branch, and a non-empty non-Git directory is treated as a worktree root that gets this run's own <dir>/<4-hex>/<repo-name> checkout, reused on resume."
56363
56710
  }),
56364
56711
  create_pr: Type15.Boolean({
56365
56712
  default: false,
@@ -56477,7 +56824,7 @@ var GROUNDED_REPORTING5 = "Before reporting progress, audit each claim against a
56477
56824
 
56478
56825
  // dist/builtin/workflows/builtin/loop-until-done-runner.ts
56479
56826
  import { mkdir as mkdir3, writeFile as writeFile7 } from "node:fs/promises";
56480
- import { join as join21 } from "node:path";
56827
+ import { join as join22 } from "node:path";
56481
56828
  import { Type as Type16 } from "typebox";
56482
56829
  function progressCurve(entries) {
56483
56830
  return entries.flatMap((entry) => entry.progress === undefined ? [] : [entry.progress.score]);
@@ -56560,18 +56907,18 @@ async function writeLedger(path, task, maxIterations, status, entries) {
56560
56907
  }
56561
56908
  async function runLoopUntilDone(ctx) {
56562
56909
  const artifactDir = await stableArtifactRoot(ctx, "loop-until-done");
56563
- const iterationsDir = join21(artifactDir, "iterations");
56564
- const evaluationsDir = join21(artifactDir, "evaluations");
56910
+ const iterationsDir = join22(artifactDir, "iterations");
56911
+ const evaluationsDir = join22(artifactDir, "evaluations");
56565
56912
  await mkdir3(iterationsDir, { recursive: true });
56566
56913
  await mkdir3(evaluationsDir, { recursive: true });
56567
- const ledgerPath = join21(artifactDir, "progress-ledger.json");
56914
+ const ledgerPath = join22(artifactDir, "progress-ledger.json");
56568
56915
  const entries = [];
56569
56916
  const iterationArtifactPaths = [];
56570
56917
  const evaluationArtifactPaths = [];
56571
56918
  await writeLedger(ledgerPath, ctx.inputs.prompt, ctx.inputs.max_iterations, "active", entries);
56572
56919
  for (let iteration = 1;iteration <= ctx.inputs.max_iterations; iteration += 1) {
56573
- const iterationPath = join21(iterationsDir, `iteration-${iteration}.md`);
56574
- const evaluationPath = join21(evaluationsDir, `evaluation-${iteration}.json`);
56920
+ const iterationPath = join22(iterationsDir, `iteration-${iteration}.md`);
56921
+ const evaluationPath = join22(evaluationsDir, `evaluation-${iteration}.json`);
56575
56922
  iterationArtifactPaths.push(iterationPath);
56576
56923
  evaluationArtifactPaths.push(evaluationPath);
56577
56924
  await ctx.task(`iteration-${iteration}`, {
@@ -56620,7 +56967,7 @@ async function runLoopUntilDone(ctx) {
56620
56967
  await writeLedger(ledgerPath, ctx.inputs.prompt, ctx.inputs.max_iterations, decision.done ? "complete" : "active", entries);
56621
56968
  if (decision.done) {
56622
56969
  const report = progressReport(entries);
56623
- const resultPath = join21(artifactDir, "result.md");
56970
+ const resultPath = join22(artifactDir, "result.md");
56624
56971
  const final = await ctx.task("completion-summary", {
56625
56972
  prompt: renderCompletionPrompt({ task: ctx.inputs.prompt, ledgerPath, iterationPath }),
56626
56973
  context: "fresh",
@@ -56737,9 +57084,9 @@ var init_loop_until_done = __esm(() => {
56737
57084
 
56738
57085
  // dist/builtin/workflows/builtin/open-claude-design-utils.ts
56739
57086
  import { spawnSync as spawnSync3 } from "node:child_process";
56740
- import { mkdirSync as mkdirSync6 } from "node:fs";
57087
+ import { mkdirSync as mkdirSync7 } from "node:fs";
56741
57088
  import { tmpdir, userInfo } from "node:os";
56742
- import { join as join22 } from "node:path";
57089
+ import { join as join23 } from "node:path";
56743
57090
  import { Type as Type18 } from "typebox";
56744
57091
  import { createChildProcessEnvironment as createChildProcessEnvironment3 } from "@bastani/atomic";
56745
57092
  function taggedPrompt3(sections) {
@@ -56782,32 +57129,32 @@ function tmpArtifactBase() {
56782
57129
  try {
56783
57130
  user = userInfo().username.replace(/[^A-Za-z0-9._-]/g, "_") || "default";
56784
57131
  } catch {}
56785
- return join22(tmpdir(), `open-claude-design-${user}`);
57132
+ return join23(tmpdir(), `open-claude-design-${user}`);
56786
57133
  }
56787
57134
  function prepareArtifactDir(cwd = process.cwd()) {
56788
57135
  const runId = `${new Date().toISOString().replace(/[:.]/g, "-")}-${Math.random().toString(36).slice(2, 8)}`;
56789
57136
  const tmpCandidates = [
56790
- join22(tmpArtifactBase(), runId),
56791
- join22(tmpdir(), "open-claude-design", runId)
57137
+ join23(tmpArtifactBase(), runId),
57138
+ join23(tmpdir(), "open-claude-design", runId)
56792
57139
  ];
56793
- const candidates = process.env.NODE_ENV === "test" ? tmpCandidates : [join22(cwd, "specs", "design", runId), ...tmpCandidates];
57140
+ const candidates = process.env.NODE_ENV === "test" ? tmpCandidates : [join23(cwd, "specs", "design", runId), ...tmpCandidates];
56794
57141
  for (const candidate of candidates) {
56795
57142
  try {
56796
- mkdirSync6(candidate, { recursive: true });
57143
+ mkdirSync7(candidate, { recursive: true });
56797
57144
  return {
56798
57145
  runId,
56799
57146
  artifactDir: candidate,
56800
- previewPath: join22(candidate, "preview.html"),
56801
- specPath: join22(candidate, "spec.html")
57147
+ previewPath: join23(candidate, "preview.html"),
57148
+ specPath: join23(candidate, "spec.html")
56802
57149
  };
56803
57150
  } catch {}
56804
57151
  }
56805
- const fallback = join22(tmpArtifactBase(), runId);
57152
+ const fallback = join23(tmpArtifactBase(), runId);
56806
57153
  return {
56807
57154
  runId,
56808
57155
  artifactDir: fallback,
56809
- previewPath: join22(fallback, "preview.html"),
56810
- specPath: join22(fallback, "spec.html")
57156
+ previewPath: join23(fallback, "preview.html"),
57157
+ specPath: join23(fallback, "spec.html")
56811
57158
  };
56812
57159
  }
56813
57160
  function ensureAgentBrowser() {
@@ -57141,8 +57488,8 @@ var init_open_claude_design_live_protocol = __esm(() => {
57141
57488
  });
57142
57489
 
57143
57490
  // dist/builtin/workflows/builtin/open-claude-design-setup.ts
57144
- import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync4 } from "node:fs";
57145
- import { join as join23 } from "node:path";
57491
+ import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync5 } from "node:fs";
57492
+ import { join as join24 } from "node:path";
57146
57493
  function renderDiscoveryContext(discovery) {
57147
57494
  return [
57148
57495
  `Confirmed design brief: ${discovery.brief}`,
@@ -57207,10 +57554,10 @@ async function runDiscoveryAndInit(args) {
57207
57554
  };
57208
57555
  }
57209
57556
  function designContextPath(artifactDir) {
57210
- return join23(artifactDir, DESIGN_CONTEXT_FILENAME);
57557
+ return join24(artifactDir, DESIGN_CONTEXT_FILENAME);
57211
57558
  }
57212
57559
  function referencesBriefPath(artifactDir) {
57213
- return join23(artifactDir, REFERENCES_BRIEF_FILENAME);
57560
+ return join24(artifactDir, REFERENCES_BRIEF_FILENAME);
57214
57561
  }
57215
57562
  function buildReferenceDiscoveryPrompt(args) {
57216
57563
  const siteList = REFERENCE_DESIGN_SITES.map((site, index) => `${index + 1}. ${site.name} — ${site.url}`).join(`
@@ -57236,8 +57583,8 @@ function buildReferenceDiscoveryPrompt(args) {
57236
57583
  [
57237
57584
  "Use the agent-browser skill to open each gallery; if `agent-browser` reports a missing browser executable, follow the bootstrap rules and retry once.",
57238
57585
  "From each gallery, choose 1-3 fitting designs and CLICK INTO each actual live or project-detail page; do not capture only the grid or thumbnail.",
57239
- `Capture motion across the entire page: start \`agent-browser record start ${join23(args.artifactDir, "ref-<site>-<n>.webm")}\`, scroll smoothly in small increments with waits (using repeated \`agent-browser scroll down 600\` or \`agent-browser mouse wheel 600\`) so animations fire and lazy content loads, then run \`agent-browser record stop\`.`,
57240
- `Also run \`agent-browser screenshot ${join23(args.artifactDir, "ref-<site>-<n>.png")} --full\`; this still is the minimum when video is unavailable.`,
57586
+ `Capture motion across the entire page: start \`agent-browser record start ${join24(args.artifactDir, "ref-<site>-<n>.webm")}\`, scroll smoothly in small increments with waits (using repeated \`agent-browser scroll down 600\` or \`agent-browser mouse wheel 600\`) so animations fire and lazy content loads, then run \`agent-browser record stop\`.`,
57587
+ `Also run \`agent-browser screenshot ${join24(args.artifactDir, "ref-<site>-<n>.png")} --full\`; this still is the minimum when video is unavailable.`,
57241
57588
  "Record the actual destination URL, title, and author. For each reference, cite observed layout, typography, color, spacing, and motion traits rather than inferred traits.",
57242
57589
  "Assess fit against DESIGN.md, PRODUCT.md, and the ds-* discovery evidence in the design-context file; prefer on-brand references and flag departures.",
57243
57590
  "Use ask_user_question to ask which reference direction they prefer, offering 2-4 strongest options plus `None of these fit`. If none fit, ask them to provide a reference image, screenshot, URL, or local file path and record the answer.",
@@ -57262,8 +57609,8 @@ function buildReferenceDiscoveryPrompt(args) {
57262
57609
  }
57263
57610
  function writeRequiredContextArtifact(artifactDir, filePath, content, label) {
57264
57611
  try {
57265
- mkdirSync7(artifactDir, { recursive: true });
57266
- writeFileSync4(filePath, `${content}
57612
+ mkdirSync8(artifactDir, { recursive: true });
57613
+ writeFileSync5(filePath, `${content}
57267
57614
  `);
57268
57615
  } catch (error) {
57269
57616
  const detail = error instanceof Error ? error.message : String(error);
@@ -57814,7 +58161,7 @@ var init_ralph_review_gate = __esm(() => {
57814
58161
 
57815
58162
  // dist/builtin/workflows/builtin/ralph-core.ts
57816
58163
  import { mkdir as mkdir4, writeFile as writeFile8 } from "node:fs/promises";
57817
- import { dirname as dirname11, join as join24 } from "node:path";
58164
+ import { dirname as dirname12, join as join25 } from "node:path";
57818
58165
  import { Type as Type20 } from "typebox";
57819
58166
  function taggedPrompt4(sections) {
57820
58167
  return sections.map(([tag, content]) => {
@@ -57854,11 +58201,11 @@ function slugifyResearchTopic(prompt) {
57854
58201
  }
57855
58202
  function defaultResearchPath(prompt, now = new Date) {
57856
58203
  const date = now.toISOString().slice(0, 10);
57857
- return join24(DEFAULT_RESEARCH_DIR, `${date}-${slugifyResearchTopic(prompt)}.md`);
58204
+ return join25(DEFAULT_RESEARCH_DIR, `${date}-${slugifyResearchTopic(prompt)}.md`);
57858
58205
  }
57859
58206
  async function createImplementationNotesFile(prompt, runId) {
57860
58207
  const notesDir = await createWorkflowArtifactDirectory(runId);
57861
- const notesPath = join24(notesDir, IMPLEMENTATION_NOTES_FILENAME);
58208
+ const notesPath = join25(notesDir, IMPLEMENTATION_NOTES_FILENAME);
57862
58209
  const initialNotes = [
57863
58210
  "# Implementation Notes",
57864
58211
  "",
@@ -57879,7 +58226,7 @@ async function createImplementationNotesFile(prompt, runId) {
57879
58226
  }
57880
58227
  async function createQaEvidenceVideoPath(runId) {
57881
58228
  const qaDir = await createWorkflowArtifactDirectory(runId);
57882
- return join24(qaDir, QA_E2E_VIDEO_FILENAME);
58229
+ return join25(qaDir, QA_E2E_VIDEO_FILENAME);
57883
58230
  }
57884
58231
  function renderQaE2eVideoGuidance(qaVideoPath) {
57885
58232
  return [
@@ -57945,7 +58292,7 @@ function artifactSafeName2(value2) {
57945
58292
  return safe.length > 0 ? safe : "artifact";
57946
58293
  }
57947
58294
  async function writeJsonArtifact(path, content) {
57948
- await mkdir4(dirname11(path), { recursive: true });
58295
+ await mkdir4(dirname12(path), { recursive: true });
57949
58296
  await writeFile8(path, `${JSON.stringify(content, null, 2)}
57950
58297
  `, {
57951
58298
  encoding: "utf8"
@@ -58283,7 +58630,7 @@ var init_ralph_models = __esm(() => {
58283
58630
 
58284
58631
  // dist/builtin/workflows/builtin/ralph-runner.ts
58285
58632
  import { existsSync as existsSync5 } from "node:fs";
58286
- import { join as join25, resolve as resolve10 } from "node:path";
58633
+ import { join as join26, resolve as resolve11 } from "node:path";
58287
58634
  async function runRalphWorkflow(ctx, options) {
58288
58635
  const { prompt, acceptanceCriteria, maxLoops, comparisonBaseBranch, workflowStartCwd, createPr, runId } = options;
58289
58636
  let latestReviewReportPath;
@@ -58295,7 +58642,7 @@ async function runRalphWorkflow(ctx, options) {
58295
58642
  let finalPrReport;
58296
58643
  const workflowCwdContext = workflowCwdContextSection(workflowStartCwd);
58297
58644
  const workflowPrompt = prompt;
58298
- const workflowResearchPath = resolve10(workflowStartCwd, defaultResearchPath(workflowPrompt));
58645
+ const workflowResearchPath = resolve11(workflowStartCwd, defaultResearchPath(workflowPrompt));
58299
58646
  const implementationNotesPath = await createImplementationNotesFile(workflowPrompt, runId);
58300
58647
  const qaVideoPath = await createQaEvidenceVideoPath(runId);
58301
58648
  const artifactDir = await createWorkflowArtifactDirectory(runId);
@@ -58344,7 +58691,7 @@ async function runRalphWorkflow(ctx, options) {
58344
58691
  const researchPath = workflowResearchPath;
58345
58692
  finalResearchPath = researchPath;
58346
58693
  finalPlanPath = researchPath;
58347
- const orchestratorReportPath = join25(artifactDir, "orchestrator-report.md");
58694
+ const orchestratorReportPath = join26(artifactDir, "orchestrator-report.md");
58348
58695
  const orchestratorForkOptions = forkContinuationOptions3(previousOrchestratorSessionId);
58349
58696
  const orchestratorPrompt = orchestratorForkOptions.forkFromSessionId === undefined ? taggedPrompt4([
58350
58697
  ["acceptance_criteria", keepContext(acceptanceCriteria)],
@@ -58480,7 +58827,7 @@ Read it before implementation or delegation because it is the primary current im
58480
58827
  diagnostics: parsed.diagnostics,
58481
58828
  allowFinalActionRemaining: createPr
58482
58829
  });
58483
- const artifactPath = join25(artifactDir, `review-${artifactSafeName2(reviewer)}.json`);
58830
+ const artifactPath = join26(artifactDir, `review-${artifactSafeName2(reviewer)}.json`);
58484
58831
  await writeJsonArtifact(artifactPath, {
58485
58832
  reviewer,
58486
58833
  decision: parsed.decision,
@@ -58536,7 +58883,7 @@ Read it before implementation or delegation because it is the primary current im
58536
58883
  usage: fold_usage([orchestrator, ...reviews, ...reverifyResults])
58537
58884
  }));
58538
58885
  }
58539
- latestReviewReportPath = await writeJsonArtifact(join25(artifactDir, "review-round-latest.json"), {
58886
+ latestReviewReportPath = await writeJsonArtifact(join26(artifactDir, "review-round-latest.json"), {
58540
58887
  convergence_decision: roundConvergenceDecision,
58541
58888
  convergence: convergenceEntries,
58542
58889
  consolidated_findings: reverified.batch,
@@ -58685,7 +59032,7 @@ var init_ralph = __esm(() => {
58685
59032
  }),
58686
59033
  git_worktree_dir: Type21.String({
58687
59034
  default: "",
58688
- description: "Optional Git worktree path. Leave at the default unless the user explicitly requested worktree isolation — stages never create git worktrees on their own. Must start inside a Git repo; absolute paths are used as-is, relative paths resolve from the repo root, existing Git worktrees from the invoking repository are reused/shared as-is, and missing paths are created from base_branch."
59035
+ description: "Optional Git worktree path. Leave at the default unless the user explicitly requested worktree isolation — stages never create git worktrees on their own. Absolute paths are used as-is; relative paths resolve from the invoking repo root (or the invoking cwd outside a Git repo). An existing Git worktree root of any repository is reused as-is, even from another repository or a non-Git cwd. Creating a worktree requires starting inside the source Git repo: a missing path or an empty directory becomes a new worktree from base_branch, and a non-empty non-Git directory is treated as a worktree root that gets this run's own <dir>/<4-hex>/<repo-name> checkout, reused on resume."
58689
59036
  }),
58690
59037
  create_pr: Type21.Boolean({
58691
59038
  default: false,
@@ -58905,7 +59252,7 @@ var init_tournament_prompts = __esm(() => {
58905
59252
 
58906
59253
  // dist/builtin/workflows/builtin/tournament-runner.ts
58907
59254
  import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile9 } from "node:fs/promises";
58908
- import { join as join26, isAbsolute as isAbsolute9, relative as relative5, resolve as resolve11 } from "node:path";
59255
+ import { join as join27, isAbsolute as isAbsolute9, relative as relative5, resolve as resolve12 } from "node:path";
58909
59256
  import { Type as Type22 } from "typebox";
58910
59257
  function serializableObject2(value2) {
58911
59258
  if (value2 === null || Array.isArray(value2) || typeof value2 !== "object")
@@ -58942,8 +59289,8 @@ function criterionSlug(id) {
58942
59289
  }
58943
59290
  function judgeArtifactPath(judgesDir, job, criterionIndex) {
58944
59291
  const filename = `judge-${job.a}-${job.b}-c${criterionIndex}-${criterionSlug(job.criterionId)}-r${job.rep}.json`;
58945
- const root = resolve11(judgesDir);
58946
- const path = resolve11(judgesDir, filename);
59292
+ const root = resolve12(judgesDir);
59293
+ const path = resolve12(judgesDir, filename);
58947
59294
  const fromRoot = relative5(root, path);
58948
59295
  if (fromRoot.startsWith("..") || isAbsolute9(fromRoot)) {
58949
59296
  throw new Error(`tournament: judge artifact escaped its directory: ${path}`);
@@ -58994,16 +59341,16 @@ function combineUsage(totals) {
58994
59341
  }
58995
59342
  async function runTournament(ctx) {
58996
59343
  const artifactDir = await stableArtifactRoot(ctx, "tournament");
58997
- const attemptsDir = join26(artifactDir, "attempts");
59344
+ const attemptsDir = join27(artifactDir, "attempts");
58998
59345
  const numAttempts = ctx.inputs.num_attempts ?? 4;
58999
59346
  const maxConcurrency = ctx.inputs.max_concurrency ?? 4;
59000
59347
  const nEvaluations = ctx.inputs.n_evaluations ?? 2;
59001
59348
  const pivotCount = ctx.inputs.pivots ?? 1;
59002
59349
  const seed = ctx.inputs.seed ?? 0;
59003
- const judgesDir = join26(artifactDir, "judges");
59350
+ const judgesDir = join27(artifactDir, "judges");
59004
59351
  await mkdir5(attemptsDir, { recursive: true });
59005
59352
  await mkdir5(judgesDir, { recursive: true });
59006
- const attemptArtifactPaths = Array.from({ length: numAttempts }, (_, index) => join26(attemptsDir, `attempt-${index + 1}.md`));
59353
+ const attemptArtifactPaths = Array.from({ length: numAttempts }, (_, index) => join27(attemptsDir, `attempt-${index + 1}.md`));
59007
59354
  const models = ctx.inputs.models;
59008
59355
  const attemptResults = await ctx.parallel(attemptArtifactPaths.map((path, index) => ({
59009
59356
  name: `attempt-${index + 1}`,
@@ -59168,7 +59515,7 @@ The previous report was invalid. Return the required JSON schema exactly once.`,
59168
59515
  const outputRanking = ranking.map(({ label, meanPreference }) => ({ label, meanPreference }));
59169
59516
  const budgetPivots = Math.min(pivotCount, entrants.length);
59170
59517
  const comparisonBudget = (entrants.length + budgetPivots * (entrants.length - budgetPivots) + budgetPivots * (budgetPivots - 1) / 2) * criteria.length * nEvaluations;
59171
- const comparisonPath = join26(artifactDir, "comparisons.json");
59518
+ const comparisonPath = join27(artifactDir, "comparisons.json");
59172
59519
  const assignment = modelAssignment(entrants, models);
59173
59520
  const phaseUsage = {
59174
59521
  attempts: fold_usage(attemptResults),
@@ -59204,7 +59551,7 @@ The previous report was invalid. Return the required JSON schema exactly once.`,
59204
59551
  await writeLedger(fold_usage([]));
59205
59552
  const winner = ranking[0];
59206
59553
  const winnerEntrant = entrants[winner.index];
59207
- const resultPath = join26(artifactDir, "winner.md");
59554
+ const resultPath = join27(artifactDir, "winner.md");
59208
59555
  const reducer = await ctx.task("comparisons-reducer", {
59209
59556
  prompt: renderComparisonsReducerPrompt({
59210
59557
  task: ctx.inputs.prompt,
@@ -60110,7 +60457,7 @@ class SqlPoolQueue {
60110
60457
  throw new Error("Stale pool coordinator authority", { cause: error });
60111
60458
  throw error;
60112
60459
  });
60113
- const row = await this.current(tx, lease, token);
60460
+ const row = await this.current(tx, lease, token, true);
60114
60461
  if (row.epoch !== lease.epoch)
60115
60462
  throw new Error(STALE_WORK_LEASE);
60116
60463
  assertSession();
@@ -62344,7 +62691,7 @@ function isBrokerStatusReport(value2) {
62344
62691
 
62345
62692
  // dist/builtin/intercom/broker/paths.ts
62346
62693
  import { homedir as homedir2 } from "os";
62347
- import { join as join27 } from "path";
62694
+ import { join as join28 } from "path";
62348
62695
  function getHomeDir() {
62349
62696
  if (process.platform === "win32") {
62350
62697
  if (process.env.USERPROFILE)
@@ -62361,7 +62708,7 @@ function expandTildePath(path) {
62361
62708
  if (path === "~")
62362
62709
  return getHomeDir();
62363
62710
  if (path.startsWith("~/") || process.platform === "win32" && path.startsWith("~\\")) {
62364
- return join27(getHomeDir(), path.slice(2));
62711
+ return join28(getHomeDir(), path.slice(2));
62365
62712
  }
62366
62713
  return path;
62367
62714
  }
@@ -62372,31 +62719,31 @@ function getAgentDir5() {
62372
62719
  const piAgentDir = process.env.PI_CODING_AGENT_DIR;
62373
62720
  if (piAgentDir)
62374
62721
  return expandTildePath(piAgentDir);
62375
- return join27(getHomeDir(), ".atomic", "agent");
62722
+ return join28(getHomeDir(), ".atomic", "agent");
62376
62723
  }
62377
62724
  function sanitizePipeSegment(value2) {
62378
62725
  return value2.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "default";
62379
62726
  }
62380
62727
  function getIntercomDirPath(agentDir = getAgentDir5()) {
62381
- return join27(agentDir, "intercom");
62728
+ return join28(agentDir, "intercom");
62382
62729
  }
62383
62730
  function getBrokerPidPath(agentDir = getAgentDir5()) {
62384
- return join27(getIntercomDirPath(agentDir), "broker.pid");
62731
+ return join28(getIntercomDirPath(agentDir), "broker.pid");
62385
62732
  }
62386
62733
  function getBrokerSpawnLockPath(agentDir = getAgentDir5()) {
62387
- return join27(getIntercomDirPath(agentDir), "broker.spawn.lock");
62734
+ return join28(getIntercomDirPath(agentDir), "broker.spawn.lock");
62388
62735
  }
62389
62736
  function getBrokerLogPath(agentDir = getAgentDir5()) {
62390
- return join27(getIntercomDirPath(agentDir), "broker.log");
62737
+ return join28(getIntercomDirPath(agentDir), "broker.log");
62391
62738
  }
62392
62739
  function getBrokerStartAnnouncementPath(agentDir = getAgentDir5()) {
62393
- return join27(getIntercomDirPath(agentDir), "broker.starting");
62740
+ return join28(getIntercomDirPath(agentDir), "broker.starting");
62394
62741
  }
62395
62742
  function getBrokerSocketPath(platform = process.platform, agentDir = getAgentDir5()) {
62396
62743
  if (platform === "win32") {
62397
62744
  return `\\\\.\\pipe\\pi-intercom-${sanitizePipeSegment(agentDir)}`;
62398
62745
  }
62399
- return join27(getIntercomDirPath(agentDir), "broker.sock");
62746
+ return join28(getIntercomDirPath(agentDir), "broker.sock");
62400
62747
  }
62401
62748
  var BROKER_STORAGE_ENV = "ATOMIC_STORAGE";
62402
62749
  var init_paths = () => {};
@@ -64080,11 +64427,11 @@ var init_client = __esm(() => {
64080
64427
  var BROKER_READY_TIMEOUT_MS = 30000, MIN_BROKER_STARTUP_TIMEOUT_MS = 1000;
64081
64428
 
64082
64429
  // dist/builtin/intercom/broker/start-announcement.ts
64083
- import { readFileSync as readFileSync4, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
64430
+ import { readFileSync as readFileSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "node:fs";
64084
64431
  function readBrokerStartAnnouncement(announcement) {
64085
64432
  let text;
64086
64433
  try {
64087
- text = readFileSync4(announcement.path, "utf-8");
64434
+ text = readFileSync6(announcement.path, "utf-8");
64088
64435
  } catch {
64089
64436
  return;
64090
64437
  }
@@ -64103,26 +64450,26 @@ var init_start_announcement = () => {};
64103
64450
  // dist/builtin/intercom/broker/spawn.ts
64104
64451
  import { spawn } from "child_process";
64105
64452
  import { randomUUID as randomUUID15 } from "crypto";
64106
- import { appendFileSync, chmodSync, closeSync as closeSync3, existsSync as existsSync6, mkdirSync as mkdirSync8, openSync as openSync3, readFileSync as readFileSync5, readSync as readSync2, statSync as statSync5, unlinkSync as unlinkSync4, writeFileSync as writeFileSync6 } from "fs";
64107
- import { join as join28, dirname as dirname12 } from "path";
64453
+ import { appendFileSync, chmodSync, closeSync as closeSync3, existsSync as existsSync6, mkdirSync as mkdirSync9, openSync as openSync3, readFileSync as readFileSync7, readSync as readSync2, statSync as statSync5, unlinkSync as unlinkSync4, writeFileSync as writeFileSync7 } from "fs";
64454
+ import { join as join29, dirname as dirname13 } from "path";
64108
64455
  import { fileURLToPath as fileURLToPath4 } from "url";
64109
64456
  import { createRequire as createRequire3 } from "module";
64110
64457
  import { createChildProcessEnvironment as createChildProcessEnvironment4, isBunBinary, processSharedStorageSelection } from "@bastani/atomic";
64111
64458
  function isIntercomPackageRoot(dir) {
64112
- return existsSync6(join28(dir, "package.json")) && existsSync6(join28(dir, "broker"));
64459
+ return existsSync6(join29(dir, "package.json")) && existsSync6(join29(dir, "broker"));
64113
64460
  }
64114
64461
  function resolveIntercomPackageRoot(moduleHref) {
64115
- const here = dirname12(fileURLToPath4(moduleHref));
64462
+ const here = dirname13(fileURLToPath4(moduleHref));
64116
64463
  if (isIntercomPackageRoot(here))
64117
64464
  return here;
64118
- return join28(here, "..");
64465
+ return join29(here, "..");
64119
64466
  }
64120
64467
  function resolveBrokerEntrypoint(packageRoot) {
64121
- const bundled = join28(packageRoot, "broker", "broker.bundle.mjs");
64122
- return existsSync6(bundled) ? bundled : join28(packageRoot, "broker", "broker.ts");
64468
+ const bundled = join29(packageRoot, "broker", "broker.bundle.mjs");
64469
+ return existsSync6(bundled) ? bundled : join29(packageRoot, "broker", "broker.ts");
64123
64470
  }
64124
64471
  function ensurePrivateDirectory(path) {
64125
- mkdirSync8(path, { recursive: true, mode: 448 });
64472
+ mkdirSync9(path, { recursive: true, mode: 448 });
64126
64473
  if (process.platform !== "win32")
64127
64474
  chmodSync(path, 448);
64128
64475
  }
@@ -64135,14 +64482,14 @@ function getCurrentBrokerRuntime() {
64135
64482
  return isBunBinary ? "bun-binary" : "bun-source";
64136
64483
  }
64137
64484
  function requireFromExtensionDir(extensionDir) {
64138
- return createRequire3(join28(extensionDir, "package.json"));
64485
+ return createRequire3(join29(extensionDir, "package.json"));
64139
64486
  }
64140
64487
  function getJitiCliPath(extensionDir = EXTENSION_DIR) {
64141
64488
  try {
64142
64489
  const jitiPackage = requireFromExtensionDir(extensionDir).resolve("jiti/package.json");
64143
- return join28(dirname12(jitiPackage), "lib", "jiti-cli.mjs");
64490
+ return join29(dirname13(jitiPackage), "lib", "jiti-cli.mjs");
64144
64491
  } catch {
64145
- return join28(extensionDir, "node_modules", "jiti", "lib", "jiti-cli.mjs");
64492
+ return join29(extensionDir, "node_modules", "jiti", "lib", "jiti-cli.mjs");
64146
64493
  }
64147
64494
  }
64148
64495
  function getDefaultBrokerRunnerPath(extensionDir) {
@@ -64164,7 +64511,7 @@ function quoteWindowsArg(value2) {
64164
64511
  return `"${value2.replace(/"/g, '""')}"`;
64165
64512
  }
64166
64513
  function getWindowsHiddenLauncherPath(intercomDir = INTERCOM_DIR) {
64167
- return join28(intercomDir, "broker-launch.vbs");
64514
+ return join29(intercomDir, "broker-launch.vbs");
64168
64515
  }
64169
64516
  function usesDefaultBrokerCommand(brokerCommand, brokerArgs) {
64170
64517
  return brokerCommand === "npx" && brokerArgs.length === 2 && brokerArgs[0] === "--no-install" && brokerArgs[1] === "tsx";
@@ -64191,8 +64538,8 @@ function getWindowsHiddenLauncherScript(commandLine) {
64191
64538
  `);
64192
64539
  }
64193
64540
  function writeWindowsHiddenLauncher(commandLine, launcherPath = getWindowsHiddenLauncherPath()) {
64194
- ensurePrivateDirectory(dirname12(launcherPath));
64195
- writeFileSync6(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
64541
+ ensurePrivateDirectory(dirname13(launcherPath));
64542
+ writeFileSync7(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
64196
64543
  return launcherPath;
64197
64544
  }
64198
64545
  function getBrokerLaunchSpec(brokerPath, brokerCommand, brokerArgs, extensionDir = EXTENSION_DIR, platform = process.platform, intercomDir = INTERCOM_DIR, nodePath = process.execPath, runtime = getCurrentBrokerRuntime(), logPath = BROKER_LOG) {
@@ -64235,14 +64582,14 @@ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, stderr = "ignore",
64235
64582
  };
64236
64583
  }
64237
64584
  function resetBrokerLog(logPath = BROKER_LOG) {
64238
- ensurePrivateDirectory(dirname12(logPath));
64585
+ ensurePrivateDirectory(dirname13(logPath));
64239
64586
  try {
64240
64587
  const previous = readBrokerLogTail(logPath);
64241
64588
  closeSync3(openSync3(logPath, "w"));
64242
64589
  if (previous.length > 0) {
64243
64590
  try {
64244
64591
  const previousPath = `${logPath}.previous`;
64245
- writeFileSync6(previousPath, Buffer.from(previous).subarray(-BROKER_LOG_TAIL_BYTES), { mode: 384 });
64592
+ writeFileSync7(previousPath, Buffer.from(previous).subarray(-BROKER_LOG_TAIL_BYTES), { mode: 384 });
64246
64593
  if (process.platform !== "win32")
64247
64594
  chmodSync(previousPath, 384);
64248
64595
  } catch {}
@@ -64386,7 +64733,7 @@ async function isBrokerRunning() {
64386
64733
  if (!existsSync6(BROKER_PID))
64387
64734
  return false;
64388
64735
  try {
64389
- const pid = parseInt(readFileSync5(BROKER_PID, "utf-8").trim(), 10);
64736
+ const pid = parseInt(readFileSync7(BROKER_PID, "utf-8").trim(), 10);
64390
64737
  if (!Number.isFinite(pid))
64391
64738
  return false;
64392
64739
  process.kill(pid, 0);
@@ -64411,7 +64758,7 @@ function handSpawnLockToBroker(pid, owner) {
64411
64758
  }
64412
64759
  function ownsSpawnLock(owner, lockPath = BROKER_SPAWN_LOCK) {
64413
64760
  try {
64414
- return readFileSync5(lockPath, "utf-8").startsWith(owner);
64761
+ return readFileSync7(lockPath, "utf-8").startsWith(owner);
64415
64762
  } catch {
64416
64763
  return false;
64417
64764
  }
@@ -64423,7 +64770,7 @@ function acquireSpawnLock() {
64423
64770
  ${Date.now()}
64424
64771
  `;
64425
64772
  try {
64426
- writeFileSync6(BROKER_SPAWN_LOCK, owner, { flag: "wx" });
64773
+ writeFileSync7(BROKER_SPAWN_LOCK, owner, { flag: "wx" });
64427
64774
  return owner;
64428
64775
  } catch (error) {
64429
64776
  if (!(error instanceof Error) || error.code !== "EEXIST") {
@@ -64453,7 +64800,7 @@ function isSpawnLockStale(lockPath = BROKER_SPAWN_LOCK, nowMs = Date.now()) {
64453
64800
  return false;
64454
64801
  }
64455
64802
  try {
64456
- const [spawnerLine = "", createdAtLine = "", brokerLine = ""] = readFileSync5(lockPath, "utf-8").trim().split(`
64803
+ const [spawnerLine = "", createdAtLine = "", brokerLine = ""] = readFileSync7(lockPath, "utf-8").trim().split(`
64457
64804
  `);
64458
64805
  const spawnerPid = Number.parseInt(spawnerLine, 10);
64459
64806
  const createdAt = Number.parseInt(createdAtLine, 10);
@@ -64531,14 +64878,14 @@ var init_reply_waiter = __esm(() => {
64531
64878
  });
64532
64879
 
64533
64880
  // dist/builtin/intercom/config.ts
64534
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
64881
+ import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
64535
64882
  import { getAgentConfigPaths } from "@bastani/atomic";
64536
64883
  function loadConfig() {
64537
64884
  if (!existsSync7(CONFIG_PATH)) {
64538
64885
  return { ...defaults };
64539
64886
  }
64540
64887
  try {
64541
- const raw = readFileSync6(CONFIG_PATH, "utf-8");
64888
+ const raw = readFileSync8(CONFIG_PATH, "utf-8");
64542
64889
  const parsed = JSON.parse(raw);
64543
64890
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
64544
64891
  throw new Error("Config must be a JSON object");
@@ -65121,12 +65468,12 @@ var init_sql_pool_usage = __esm(() => {
65121
65468
 
65122
65469
  // dist/builtin/workflows/src/workers/pooled-watch-trace.ts
65123
65470
  import { appendFileSync as appendFileSync2 } from "node:fs";
65124
- import { dirname as dirname13, join as join29, resolve as resolve12 } from "node:path";
65471
+ import { dirname as dirname14, join as join30, resolve as resolve13 } from "node:path";
65125
65472
  function configurePooledWatchTrace(journalPath, processRole) {
65126
65473
  if (setting === undefined || setting === "" || setting === "0")
65127
65474
  return;
65128
65475
  role = processRole;
65129
- file = setting === "1" ? join29(dirname13(resolve12(journalPath)), "pooled-watch-trace.jsonl") : resolve12(setting);
65476
+ file = setting === "1" ? join30(dirname14(resolve13(journalPath)), "pooled-watch-trace.jsonl") : resolve13(setting);
65130
65477
  }
65131
65478
  function tracePooledWatch(step, detail) {
65132
65479
  if (file === undefined)
@@ -65320,7 +65667,7 @@ import { fork } from "node:child_process";
65320
65667
  import { randomUUID as randomUUID17 } from "node:crypto";
65321
65668
  import { createWriteStream } from "node:fs";
65322
65669
  import { appendFile, mkdir as mkdir6, rm as rm3, rmdir } from "node:fs/promises";
65323
- import { dirname as dirname14, join as join30, resolve as resolve13 } from "node:path";
65670
+ import { dirname as dirname15, join as join31, resolve as resolve14 } from "node:path";
65324
65671
  import { setTimeout as delay2 } from "node:timers/promises";
65325
65672
  function isRootLeaseLost(error) {
65326
65673
  return error instanceof StaleLeaseError || error.message.startsWith("Stale lease for run ");
@@ -65329,7 +65676,7 @@ function unconfirmedCleanup(groups) {
65329
65676
  return new Error(`Local worker process cleanup unconfirmed; ${groups.length > 0 ? `process groups ${groups.join(", ")} still running after SIGKILL; ` : ""}permits quarantined`);
65330
65677
  }
65331
65678
  function workerLogDirectory(journalPath, root) {
65332
- return join30(dirname14(resolve13(journalPath)), "worker-logs", encodeURIComponent(root).replaceAll(".", "%2E"));
65679
+ return join31(dirname15(resolve14(journalPath)), "worker-logs", encodeURIComponent(root).replaceAll(".", "%2E"));
65333
65680
  }
65334
65681
  function token(work) {
65335
65682
  return {
@@ -65517,6 +65864,7 @@ class PoolCoordinator {
65517
65864
  cleanups = new Set;
65518
65865
  controlAcks = new Map;
65519
65866
  lastHeard = new Map;
65867
+ awaiting = new Map;
65520
65868
  pendingActivity = new Map;
65521
65869
  batches = new Map;
65522
65870
  publicationFailures = new Map;
@@ -66260,7 +66608,7 @@ class PoolCoordinator {
66260
66608
  this.slots.add(slot);
66261
66609
  let ready = false;
66262
66610
  let serial = Promise.resolve();
66263
- const logPath = join30(this.logDirectory, `${worker}.log`);
66611
+ const logPath = join31(this.logDirectory, `${worker}.log`);
66264
66612
  this.logs.set(child, logPath);
66265
66613
  let logFailure;
66266
66614
  const log = createWriteStream(logPath, { flags: "a" });
@@ -66316,6 +66664,7 @@ class PoolCoordinator {
66316
66664
  this.readyWorkers.delete(worker);
66317
66665
  this.controlAcks.delete(worker);
66318
66666
  this.lastHeard.delete(worker);
66667
+ this.awaiting.delete(worker);
66319
66668
  this.slots.delete(slot);
66320
66669
  this.endStageWatches((watch) => watch.slot === slot, "worker_exited");
66321
66670
  for (const settle of slot.stageCommands.values())
@@ -66573,8 +66922,19 @@ class PoolCoordinator {
66573
66922
  await this.sendResult(work.id);
66574
66923
  });
66575
66924
  const response = handled.then((value2) => routeReady ?? value2);
66576
- if (message.kind !== "delivery" && message.kind !== "delivery-ready")
66577
- serial = handled.then(() => {}, () => {});
66925
+ if (message.kind !== "delivery" && message.kind !== "delivery-ready") {
66926
+ this.awaiting.set(worker, (this.awaiting.get(worker) ?? 0) + 1);
66927
+ const answered = () => {
66928
+ const left = (this.awaiting.get(worker) ?? 1) - 1;
66929
+ if (left > 0)
66930
+ this.awaiting.set(worker, left);
66931
+ else
66932
+ this.awaiting.delete(worker);
66933
+ if (this.incarnations.get(worker) === child)
66934
+ this.lastHeard.set(worker, { at: performance.now(), stalled: this.stalledMs });
66935
+ };
66936
+ serial = handled.then(answered, answered);
66937
+ }
66578
66938
  response.then((value2) => child.send({ id: message.id, value: value2 }, replied), (error) => {
66579
66939
  const rootLeaseLost = isRootLeaseLost(error);
66580
66940
  if (rootLeaseLost)
@@ -66644,7 +67004,10 @@ class PoolCoordinator {
66644
67004
  if (work.epoch < lease.epoch)
66645
67005
  await this.releaseRetained(work);
66646
67006
  else
66647
- await queue.expire(lease, work);
67007
+ await queue.expire(lease, work).catch((error) => {
67008
+ if (error.message !== STALE_WORK_LEASE)
67009
+ throw error;
67010
+ });
66648
67011
  }
66649
67012
  })().finally(() => {
66650
67013
  this.recovering = undefined;
@@ -66662,7 +67025,7 @@ class PoolCoordinator {
66662
67025
  isLive(worker, child) {
66663
67026
  this.observeResponsiveness();
66664
67027
  const heard = this.lastHeard.get(worker);
66665
- return child.exitCode === null && child.signalCode === null && child.connected && heard !== undefined && performance.now() - heard.at - (this.stalledMs - heard.stalled) < this.options.leaseMs;
67028
+ return child.exitCode === null && child.signalCode === null && child.connected && heard !== undefined && ((this.awaiting.get(worker) ?? 0) > 0 || performance.now() - heard.at - (this.stalledMs - heard.stalled) < this.options.leaseMs);
66666
67029
  }
66667
67030
  async stop(child) {
66668
67031
  const pid = child.pid;
@@ -67014,7 +67377,7 @@ function createWorkflowPendingStageDelivery(activeStore, runId, stageId, stageNa
67014
67377
  };
67015
67378
  }
67016
67379
  async function deliverPendingStageMessages(activeStore, runId, stageId, stageName, deliver, commit, recipient) {
67017
- const stickyEntries = stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient);
67380
+ const stickyEntries = stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient, recipient?.receivedMessageIds);
67018
67381
  const candidateIds = new Set([
67019
67382
  ...activeStore.pendingStageMessagesFor(runId, stageId),
67020
67383
  ...activeStore.pendingStageMessagesFor(runId, stageName),
@@ -67070,7 +67433,7 @@ async function deliverPendingStageMessages(activeStore, runId, stageId, stageNam
67070
67433
  }
67071
67434
  }
67072
67435
  }
67073
- function stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient) {
67436
+ function stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient, receivedByEndedStage = []) {
67074
67437
  const runs = activeStore.runs();
67075
67438
  const rootRunId = durableRootRunIdForRun(runs, runId);
67076
67439
  if (rootRunId === undefined)
@@ -67079,22 +67442,28 @@ function stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageNam
67079
67442
  if (rootRun === undefined)
67080
67443
  return [];
67081
67444
  const stage = runs.find((run) => run.id === runId)?.stages.find((candidate) => candidate.id === stageId);
67082
- if (stage?.status === "completed" || stage?.status === "skipped")
67445
+ const ended = stage?.status === "completed" || stage?.status === "skipped";
67446
+ if (ended && receivedByEndedStage.length === 0)
67083
67447
  return [];
67084
67448
  const hops = workflowBoundaryHops(runs, runId);
67085
67449
  if (hops === undefined)
67086
67450
  return [];
67087
67451
  return (rootRun.pendingStageMessages ?? []).filter((entry) => {
67088
- if (entry.sticky !== true || entry.status !== "queued")
67089
- return false;
67090
- const parsed = entry.targetPath === undefined ? undefined : parseWorkflowStageTarget(entry.targetPath);
67091
- if (parsed === undefined || parsed.rootRunId !== rootRunId)
67092
- return false;
67093
67452
  if ((entry.deliveries ?? []).some((delivery) => delivery.runId === runId && delivery.stageId === stageId && (recipient === undefined || delivery.admission === "context" && delivery.sessionId === recipient.sessionId && (recipient.receivedMessageIds === undefined || recipient.receivedMessageIds.includes(entry.message.id)))))
67094
67453
  return false;
67095
- return stageMatchesPathPattern(parsed.segments, hops, [stageId, stageName]);
67454
+ if (ended && !receivedByEndedStage.includes(entry.message.id))
67455
+ return false;
67456
+ return stickyEntryTargetsStage(entry, rootRunId, hops, stageId, stageName);
67096
67457
  });
67097
67458
  }
67459
+ function stickyEntryTargetsStage(entry, rootRunId, hops, stageId, stageName) {
67460
+ if (entry.sticky !== true || entry.status !== "queued")
67461
+ return false;
67462
+ const parsed = entry.targetPath === undefined ? undefined : parseWorkflowStageTarget(entry.targetPath);
67463
+ if (parsed === undefined || parsed.rootRunId !== rootRunId)
67464
+ return false;
67465
+ return stageMatchesPathPattern(parsed.segments, hops, [stageId, stageName]);
67466
+ }
67098
67467
  function claimPendingDelivery(store, runId, stageKey, messageId) {
67099
67468
  let claims = pendingDeliveryClaims.get(store);
67100
67469
  if (claims === undefined) {
@@ -67158,6 +67527,7 @@ class PoolStageHandoff {
67158
67527
  owners = new Map;
67159
67528
  publications = new Map;
67160
67529
  drains = new Map;
67530
+ deliveryIssues = 0;
67161
67531
  constructor(backend, store, queue, lease, claimRun) {
67162
67532
  this.backend = backend;
67163
67533
  this.store = store;
@@ -67238,11 +67608,23 @@ class PoolStageHandoff {
67238
67608
  this.drains.set(stage, drain);
67239
67609
  try {
67240
67610
  await drain;
67611
+ } catch (error) {
67612
+ const current = this.attempts.get(work.id) === attempt && !attempt.fenced;
67613
+ if (current && attempt.sessions.get(stage) === recipient.sessionId && error.message !== STALE)
67614
+ this.recordDeliveryIssue(error);
67615
+ throw error;
67241
67616
  } finally {
67242
67617
  if (this.drains.get(stage) === drain)
67243
67618
  this.drains.delete(stage);
67244
67619
  }
67245
67620
  }
67621
+ recordDeliveryIssue(error) {
67622
+ this.deliveryIssues++;
67623
+ const drains = this.deliveryIssues === 1 ? "1 stage drain" : `${this.deliveryIssues} stage drains`;
67624
+ this.store.recordRunExecutionState(this.lease.runId, {
67625
+ intercomDeliveryIssue: `${drains} on local workers failed; undelivered messages stay queued and arrive live or on the stage's next drain (last: ${error.message})`
67626
+ });
67627
+ }
67246
67628
  async complete(work) {
67247
67629
  const root = this.lease.runId;
67248
67630
  const result = await this.queue.result(root, work.id);
@@ -68598,7 +68980,7 @@ var init_pooled_stage_registry = __esm(() => {
68598
68980
  // dist/builtin/workflows/src/workers/local-pool.ts
68599
68981
  import { fork as fork2 } from "node:child_process";
68600
68982
  import { randomUUID as randomUUID19 } from "node:crypto";
68601
- import { resolve as resolve14 } from "node:path";
68983
+ import { resolve as resolve15 } from "node:path";
68602
68984
  import { setTimeout as delay3 } from "node:timers/promises";
68603
68985
  import { fileURLToPath as fileURLToPath5 } from "node:url";
68604
68986
  import {
@@ -68832,7 +69214,7 @@ class LocalWorkerPool {
68832
69214
  async poolJournal() {
68833
69215
  const journal = this.backend.journal;
68834
69216
  if (this.options.durable === undefined) {
68835
- if (!(journal instanceof SqlRunJournal) || resolve14(this.options.journalPath) !== resolve14(journal.path) || sqlPoolStorage(journal)?.tables.runs !== "wf_runs")
69217
+ if (!(journal instanceof SqlRunJournal) || resolve15(this.options.journalPath) !== resolve15(journal.path) || sqlPoolStorage(journal)?.tables.runs !== "wf_runs")
68836
69218
  throw new Error("Local workers require the same default-prefix SQLite journal");
68837
69219
  return journal;
68838
69220
  }
@@ -69859,7 +70241,7 @@ var init_local_pool = __esm(() => {
69859
70241
  var DEFAULT_POOLED_STAGE_START_MAX_LOAD_PER_CPU = 3;
69860
70242
 
69861
70243
  // dist/builtin/workflows/src/workers/public-pool.ts
69862
- import { resolve as resolve15 } from "node:path";
70244
+ import { resolve as resolve16 } from "node:path";
69863
70245
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
69864
70246
  async function restoreWorkerOptions(backend, options) {
69865
70247
  const source = options.continuation?.source.id ?? options.runId;
@@ -69909,16 +70291,16 @@ async function activatePublicPool(backend, options) {
69909
70291
  startupTimeoutMs: undefined,
69910
70292
  leaseMs: undefined,
69911
70293
  maxLoadPerCpu: undefined,
69912
- definitions: options.localWorkers.definitions.map((source) => ({ ...source, path: resolve15(cwd, source.path) }))
70294
+ definitions: options.localWorkers.definitions.map((source) => ({ ...source, path: resolve16(cwd, source.path) }))
69913
70295
  };
69914
70296
  const admission = options.admission && {
69915
70297
  ...options.admission,
69916
- ...options.admission.userDatabase ? { userDatabase: resolve15(cwd, options.admission.userDatabase) } : {}
70298
+ ...options.admission.userDatabase ? { userDatabase: resolve16(cwd, options.admission.userDatabase) } : {}
69917
70299
  };
69918
70300
  if (durable !== undefined && admission?.userLimits) {
69919
- if (admission.userDatabase !== undefined && admission.userDatabase !== resolve15(poolPath))
69920
- throw new Error(`admission.userDatabase must be omitted, or ${resolve15(poolPath)}, for a journal that is not a local SQLite file`);
69921
- admission.userDatabase = resolve15(poolPath);
70301
+ if (admission.userDatabase !== undefined && admission.userDatabase !== resolve16(poolPath))
70302
+ throw new Error(`admission.userDatabase must be omitted, or ${resolve16(poolPath)}, for a journal that is not a local SQLite file`);
70303
+ admission.userDatabase = resolve16(poolPath);
69922
70304
  }
69923
70305
  const sources = localWorkers && await Promise.all(localWorkers.definitions.map(async (source) => ({ ...source, digest: await definitionDigest(source.path) })));
69924
70306
  const resources = JSON.parse(JSON.stringify({
@@ -70524,6 +70906,7 @@ function createTaskPrimitive(runtime) {
70524
70906
  }
70525
70907
  })();
70526
70908
  const stageMeta = stage.__modelFallbackMeta();
70909
+ stage.__releaseIfIdle?.();
70527
70910
  return {
70528
70911
  name,
70529
70912
  stageName: name,
@@ -71322,7 +71705,7 @@ function hasUsableContentBlock(block) {
71322
71705
 
71323
71706
  // dist/builtin/workflows/src/shared/stage-session-store.ts
71324
71707
  import { randomUUID as randomUUID21 } from "node:crypto";
71325
- import { dirname as dirname15, resolve as resolve16 } from "node:path";
71708
+ import { dirname as dirname16, resolve as resolve17 } from "node:path";
71326
71709
  import {
71327
71710
  JsonlSessionStore,
71328
71711
  resolveSessionStore,
@@ -71358,11 +71741,11 @@ async function forkStageSession(store, sessionId, cwd, options, destination = st
71358
71741
  async function migrateLegacyStageSession(store, sessionId, sessionFile) {
71359
71742
  if (sessionFile === undefined || await store.readHeader(sessionId) !== undefined)
71360
71743
  return;
71361
- const location = resolve16(sessionFile);
71362
- const legacy = await JsonlSessionStore.open(dirname15(location), { exactDirectory: true, scanExisting: false });
71744
+ const location = resolve17(sessionFile);
71745
+ const legacy = await JsonlSessionStore.open(dirname16(location), { exactDirectory: true, scanExisting: false });
71363
71746
  try {
71364
71747
  for await (const file of legacy.scanFiles()) {
71365
- if (resolve16(file.location) !== location || file.header.id !== sessionId)
71748
+ if (resolve17(file.location) !== location || file.header.id !== sessionId)
71366
71749
  continue;
71367
71750
  const snapshot = await file.read();
71368
71751
  if (await store.readHeader(sessionId) === undefined)
@@ -74188,6 +74571,8 @@ function createTrackedStageCaller(input) {
74188
74571
  await runtime.releaseLiveHandle().catch(() => {});
74189
74572
  }
74190
74573
  runtime.state.sessionCheckpointed = finalizationError === undefined;
74574
+ if (runtime.state.contextUnreachable)
74575
+ runtime.stageRegistry.releaseIdle(runtime.runId, runtime.stageId);
74191
74576
  input.limiter.release();
74192
74577
  if (finalizationError !== undefined)
74193
74578
  throwFinalizationError(finalizationError.error);
@@ -74351,7 +74736,8 @@ function createStageContext(input) {
74351
74736
  recordStageNotice({ kind: "abort", to: "interrupted" });
74352
74737
  await innerCtx.abort();
74353
74738
  },
74354
- __modelFallbackMeta: () => innerCtx.__modelFallbackMeta()
74739
+ __modelFallbackMeta: () => innerCtx.__modelFallbackMeta(),
74740
+ __releaseIfIdle: () => runtime.stageRegistry.releaseIdle(runtime.runId, runtime.stageId)
74355
74741
  };
74356
74742
  }
74357
74743
 
@@ -76040,10 +76426,10 @@ function isRetryableSameCandidateFailure(error) {
76040
76426
  return isTransientExtensionStartupFailure(error);
76041
76427
  return isRetryableSameModelFailure(error);
76042
76428
  }
76043
- function mayAdvanceToNextCandidate(error) {
76429
+ function mayAdvanceToNextCandidate(error, options) {
76044
76430
  if (isExtensionStartupFailure(error))
76045
76431
  return isTransientExtensionStartupFailure(error);
76046
- return isRetryableModelFailure(error);
76432
+ return isRetryableModelFailure(error, options);
76047
76433
  }
76048
76434
  function hasMeaningfulUsage(usage) {
76049
76435
  if (usage === undefined)
@@ -76135,6 +76521,7 @@ class StageSessionController {
76135
76521
  structuredOutputExecutionFailureValue;
76136
76522
  structuredOutputCycleAttemptMark;
76137
76523
  unsubscribeTerminateWatcher;
76524
+ unbindAbortSignal;
76138
76525
  sessionInternalRetries = 0;
76139
76526
  unresolvedContextOverflowMessage;
76140
76527
  generationSealed = false;
@@ -76624,6 +77011,8 @@ class StageSessionController {
76624
77011
  this.pendingListeners.clear();
76625
77012
  this.unsubscribeTerminateWatcher?.();
76626
77013
  this.unsubscribeTerminateWatcher = undefined;
77014
+ this.unbindAbortSignal?.();
77015
+ this.unbindAbortSignal = undefined;
76627
77016
  this.terminatingToolCallIds.clear();
76628
77017
  this.messageAdmission.dispose();
76629
77018
  this.deliveryActivity.dispose();
@@ -76718,8 +77107,10 @@ class StageSessionController {
76718
77107
  };
76719
77108
  if (signal.aborted)
76720
77109
  onAbort();
76721
- else
77110
+ else {
76722
77111
  signal.addEventListener("abort", onAbort, { once: true });
77112
+ this.unbindAbortSignal = () => signal.removeEventListener("abort", onAbort);
77113
+ }
76723
77114
  }
76724
77115
  markAbort(reason) {
76725
77116
  this.abortGeneration += 1;
@@ -76746,6 +77137,9 @@ class StageSessionController {
76746
77137
  state.controller.abort(reason);
76747
77138
  this.thrownErrorRetryStates.clear();
76748
77139
  }
77140
+ fallbackFailureOptions() {
77141
+ return { fallbackOnContentFilter: this.retrySettings()?.fallbackOnContentFilter !== false };
77142
+ }
76749
77143
  retrySettings() {
76750
77144
  const managers = [
76751
77145
  this.sessionSettingsManager,
@@ -77020,7 +77414,7 @@ class StageSessionController {
77020
77414
  const errorSettingsManager = retrySettingsManagerFromError(error);
77021
77415
  if (errorSettingsManager !== undefined)
77022
77416
  this.sessionSettingsManager = errorSettingsManager;
77023
- const retryableFailure = isRetryableModelFailure(error);
77417
+ const retryableFailure = isRetryableModelFailure(error, this.fallbackFailureOptions());
77024
77418
  const sameCandidateRetryable = isRetryableSameModelFailure(error) && !isUnresolvedContextOverflowFailure(error);
77025
77419
  recordProviderRateLimit(this.currentProvider(), error, modelIdForModel(this.currentModelValue()));
77026
77420
  if (this.hasExplicitModelFallbackConfig && isProviderRateLimit(error)) {
@@ -77645,7 +78039,7 @@ class StageSessionController {
77645
78039
  }
77646
78040
  throw new WorkflowPromptModelFailure(terminalFailure);
77647
78041
  } catch (err) {
77648
- if (this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err)) {
78042
+ if (this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err, this.fallbackFailureOptions())) {
77649
78043
  this.artifactCapture.settleAttempt(true);
77650
78044
  const usage = this.takeAttemptUsage(resumedSession);
77651
78045
  this.modelAttempts.push({
@@ -77667,7 +78061,7 @@ class StageSessionController {
77667
78061
  ...usage === undefined ? {} : { usage },
77668
78062
  error: message
77669
78063
  });
77670
- if (this.opts.signal?.aborted || !isRetryableModelFailure(err)) {
78064
+ if (this.opts.signal?.aborted || !isRetryableModelFailure(err, this.fallbackFailureOptions())) {
77671
78065
  this.modelWarnings.push(...this.pendingFallbackWarnings);
77672
78066
  this.pendingFallbackWarnings.length = 0;
77673
78067
  this.notifyModelFallbackMetaChange();
@@ -77716,7 +78110,7 @@ class StageSessionController {
77716
78110
  throw err;
77717
78111
  const message = errorMessage3(err);
77718
78112
  const terminalStageDelivery = isWorkflowPendingStageDeliveryFailure(err);
77719
- if (!terminalStageDelivery && this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err)) {
78113
+ if (!terminalStageDelivery && this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err, this.fallbackFailureOptions())) {
77720
78114
  this.recordSuccessfulAttempt(candidate);
77721
78115
  return "handled";
77722
78116
  }
@@ -77733,7 +78127,7 @@ class StageSessionController {
77733
78127
  if (walk !== undefined) {
77734
78128
  this.recordCandidateFailure(walk, index, err, candidates);
77735
78129
  nextIndex = undefined;
77736
- if (!this.opts.signal?.aborted && !this.startupWait.signal.aborted && !terminalStageDelivery && mayAdvanceToNextCandidate(err)) {
78130
+ if (!this.opts.signal?.aborted && !this.startupWait.signal.aborted && !terminalStageDelivery && mayAdvanceToNextCandidate(err, this.fallbackFailureOptions())) {
77737
78131
  try {
77738
78132
  nextIndex = await this.selectReadyCandidate(candidates, walk, index + 1);
77739
78133
  } catch (error) {
@@ -77746,7 +78140,7 @@ class StageSessionController {
77746
78140
  }
77747
78141
  }
77748
78142
  }
77749
- if (this.opts.signal?.aborted || this.startupWait.signal.aborted || terminalStageDelivery || !mayAdvanceToNextCandidate(err) || nextIndex === undefined) {
78143
+ if (this.opts.signal?.aborted || this.startupWait.signal.aborted || terminalStageDelivery || !mayAdvanceToNextCandidate(err, this.fallbackFailureOptions()) || nextIndex === undefined) {
77750
78144
  await this.settleFailedCandidate();
77751
78145
  return "throw";
77752
78146
  }
@@ -77841,7 +78235,7 @@ var init_stage_runner_controller = __esm(() => {
77841
78235
  // dist/builtin/workflows/src/runs/foreground/stage-runner-output.ts
77842
78236
  import { createHash as createHash6 } from "node:crypto";
77843
78237
  import { mkdir as mkdir7, writeFile as writeFile10 } from "node:fs/promises";
77844
- import { basename as basename6, dirname as dirname16, isAbsolute as isAbsolute11, join as join31, resolve as resolve17 } from "node:path";
78238
+ import { basename as basename6, dirname as dirname17, isAbsolute as isAbsolute11, join as join32, resolve as resolve18 } from "node:path";
77845
78239
  function normalizeMaxOutput2(maxOutput) {
77846
78240
  return {
77847
78241
  bytes: maxOutput?.bytes ?? DEFAULT_MAX_OUTPUT_BYTES,
@@ -77904,7 +78298,7 @@ function formatByteSize(bytes) {
77904
78298
  }
77905
78299
  function transcriptPath(runId, outputPath) {
77906
78300
  const digest = createHash6("sha256").update(outputPath).digest("hex").slice(0, 16);
77907
- return join31(workflowArtifactRunPath(runId), "transcripts", `${digest}-${basename6(outputPath)}.transcript.md`);
78301
+ return join32(workflowArtifactRunPath(runId), "transcripts", `${digest}-${basename6(outputPath)}.transcript.md`);
77908
78302
  }
77909
78303
  function appendContent(lines, content) {
77910
78304
  if (typeof content === "string") {
@@ -77949,7 +78343,7 @@ function renderTranscript(messages, fallbackOutput) {
77949
78343
  `);
77950
78344
  }
77951
78345
  function savedOutputReference(outputPath, fullOutput, transcriptPathValue, transcriptText, transcriptAvailable, warning) {
77952
- const absolutePath = resolve17(outputPath);
78346
+ const absolutePath = resolve18(outputPath);
77953
78347
  const bytes = Buffer.byteLength(fullOutput, "utf8");
77954
78348
  const lines = countLines(fullOutput);
77955
78349
  const transcriptBytes = Buffer.byteLength(transcriptText, "utf8");
@@ -77964,8 +78358,8 @@ function resolveOutputPath(output, runtimeCwd, requestedCwd) {
77964
78358
  return;
77965
78359
  if (isAbsolute11(output))
77966
78360
  return output;
77967
- const baseCwd = requestedCwd === undefined ? runtimeCwd : isAbsolute11(requestedCwd) ? requestedCwd : resolve17(runtimeCwd, requestedCwd);
77968
- return resolve17(baseCwd, output);
78361
+ const baseCwd = requestedCwd === undefined ? runtimeCwd : isAbsolute11(requestedCwd) ? requestedCwd : resolve18(runtimeCwd, requestedCwd);
78362
+ return resolve18(baseCwd, output);
77969
78363
  }
77970
78364
  function degenerateWarning(fullOutput) {
77971
78365
  return fullOutput.trim().length === 0 ? "WARNING: the stage artifact is empty; search the companion transcript for this stage's work." : undefined;
@@ -78024,7 +78418,7 @@ async function finalizePromptOutput(fullOutput, outputOptions, runtimeCwd, runId
78024
78418
  const transcriptFile = transcriptPath(runId, outputPath);
78025
78419
  const transcript = renderTranscript(messages, fullOutput);
78026
78420
  try {
78027
- await mkdir7(dirname16(outputPath), { recursive: true });
78421
+ await mkdir7(dirname17(outputPath), { recursive: true });
78028
78422
  await writeFile10(outputPath, fullOutput, "utf8");
78029
78423
  } catch (err) {
78030
78424
  return `${displayOutput}
@@ -78036,7 +78430,7 @@ ${err instanceof Error ? err.message : String(err)}`;
78036
78430
  let transcriptError;
78037
78431
  try {
78038
78432
  await ensureWorkflowArtifactRunDirectory(runId);
78039
- await mkdir7(dirname16(transcriptFile), { recursive: true });
78433
+ await mkdir7(dirname17(transcriptFile), { recursive: true });
78040
78434
  await writeFile10(transcriptFile, transcript, "utf8");
78041
78435
  transcriptAvailable = true;
78042
78436
  } catch (err) {
@@ -78547,7 +78941,8 @@ function createWorkflowStageFactory(input) {
78547
78941
  stageFinalized: false,
78548
78942
  sessionCheckpointed: false,
78549
78943
  skippedForParallelFailFast: false,
78550
- stageControlDropped: false
78944
+ stageControlDropped: false,
78945
+ contextUnreachable: false
78551
78946
  };
78552
78947
  const activeAskUserQuestionCalls = new Set;
78553
78948
  const hasActiveAskUserQuestion = () => activeAskUserQuestionCalls.size > 0 || state.activeAskUserQuestionAnonymousCalls > 0;
@@ -78793,9 +79188,18 @@ function createWorkflowStageFactory(input) {
78793
79188
  hasContinuation: input.opts.continuation !== undefined,
78794
79189
  hasScopedParents: stageFailFastScope?.parentIds !== undefined
78795
79190
  });
78796
- return createStageContext({ runtime, runTrackedStageCall });
79191
+ const context = createStageContext({ runtime, runTrackedStageCall });
79192
+ unreachableStageContexts.register(context, releaseWhenUnreachable(state, input.stageRegistry, input.runId, stageId));
79193
+ return context;
79194
+ };
79195
+ }
79196
+ function releaseWhenUnreachable(state, registry, runId, stageId) {
79197
+ return () => {
79198
+ state.contextUnreachable = true;
79199
+ registry.releaseIdle(runId, stageId);
78797
79200
  };
78798
79201
  }
79202
+ var unreachableStageContexts;
78799
79203
  var init_executor_stage_factory = __esm(() => {
78800
79204
  init_intercom_group();
78801
79205
  init_pending_stage_route_readiness();
@@ -78810,6 +79214,7 @@ var init_executor_stage_factory = __esm(() => {
78810
79214
  init_pending_stage_delivery2();
78811
79215
  init_stage_runner();
78812
79216
  init_stage_runner_session();
79217
+ unreachableStageContexts = new FinalizationRegistry((release) => release());
78813
79218
  });
78814
79219
 
78815
79220
  // dist/builtin/workflows/src/engine/runtime.ts
@@ -79151,7 +79556,7 @@ async function executeRun(def, resolvedInputs, opts) {
79151
79556
  const ownsCancellationRegistration = opts.signal === undefined && opts.cancellation !== undefined;
79152
79557
  const tracker = new GraphFrontierTracker;
79153
79558
  const inputConcurrency = resolveInputConcurrency(def.inputs, resolvedInputs);
79154
- const inputRuntimeDefaults = resolveInputRuntimeDefaults(def, resolvedInputs), gitWorktreeSetupCacheOwner = createGitWorktreeSetupCacheOwner(opts.gitWorktreeSetupCache);
79559
+ const inputRuntimeDefaults = resolveInputRuntimeDefaults(def, resolvedInputs), gitWorktreeSetupCacheOwner = createGitWorktreeSetupCacheOwner(opts.gitWorktreeSetupCache, runId);
79155
79560
  const gitWorktreeSetupCache = gitWorktreeSetupCacheOwner.cache;
79156
79561
  const workflowInvocationCwd = opts.cwd ?? process.cwd();
79157
79562
  let workflowCwd;