@bastani/atomic 0.9.60 → 0.9.61-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +80 -0
  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 +922 -473
  17. package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +1006 -484
  18. package/dist/builtin/workflows/src/index.js +892 -448
  19. package/dist/builtin/workflows/src/local-worker-entry.mjs +1006 -484
  20. package/dist/builtin/workflows/src/shared/workflow-infrastructure-error.d.ts +6 -0
  21. package/dist/core/agent-session-retry.d.ts.map +1 -1
  22. package/dist/core/agent-session-retry.js +12 -3
  23. package/dist/core/agent-session-retry.js.map +1 -1
  24. package/dist/core/model-fallback-failures.d.ts +14 -4
  25. package/dist/core/model-fallback-failures.d.ts.map +1 -1
  26. package/dist/core/model-fallback-failures.js +43 -15
  27. package/dist/core/model-fallback-failures.js.map +1 -1
  28. package/dist/core/settings-defaults.d.ts +1 -0
  29. package/dist/core/settings-defaults.d.ts.map +1 -1
  30. package/dist/core/settings-defaults.js +1 -0
  31. package/dist/core/settings-defaults.js.map +1 -1
  32. package/dist/core/settings-manager-basic-accessors.d.ts +1 -0
  33. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  34. package/dist/core/settings-manager-basic-accessors.js +2 -0
  35. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  36. package/dist/core/settings-schema.d.ts +2 -0
  37. package/dist/core/settings-schema.d.ts.map +1 -1
  38. package/dist/core/settings-schema.js +4 -0
  39. package/dist/core/settings-schema.js.map +1 -1
  40. package/dist/core/slash-commands.js +2 -2
  41. package/dist/core/slash-commands.js.map +1 -1
  42. package/dist/index.d.ts +1 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js.map +1 -1
  45. package/dist/modes/interactive/interactive-startup-chat-container.d.ts +2 -0
  46. package/dist/modes/interactive/interactive-startup-chat-container.d.ts.map +1 -1
  47. package/dist/modes/interactive/interactive-startup-chat-container.js +6 -0
  48. package/dist/modes/interactive/interactive-startup-chat-container.js.map +1 -1
  49. package/dist/modes/interactive-engine/remote-renderer.d.ts +27 -5
  50. package/dist/modes/interactive-engine/remote-renderer.d.ts.map +1 -1
  51. package/dist/modes/interactive-engine/remote-renderer.js +76 -16
  52. package/dist/modes/interactive-engine/remote-renderer.js.map +1 -1
  53. package/docs/intercom/reference.md +1 -1
  54. package/docs/models/evals.md +30 -2
  55. package/docs/settings.md +3 -0
  56. package/docs/subagents/reference.md +3 -1
  57. package/docs/workflows/api-reference.md +9 -6
  58. package/docs/workflows/builtins.md +1 -1
  59. package/docs/workflows/operations.md +6 -4
  60. package/npm-shrinkwrap.json +32 -32
  61. package/package.json +3 -3
  62. package/schemas/settings.schema.json +5 -0
@@ -5135,6 +5135,18 @@ function failedOnInfrastructureFault(result) {
5135
5135
  return true;
5136
5136
  return Array.isArray(result.stages) && result.stages.some((stage) => stage?.status === "failed" && (names(stage.failureCode) || names(stage.error)));
5137
5137
  }
5138
+ function infrastructureFaultMessage(result) {
5139
+ const names = (value2) => typeof value2 === "string" && (WORKFLOW_INFRASTRUCTURE_FAULTS.has(value2) || workflowInfrastructureFault(value2) !== undefined);
5140
+ if (names(result.error))
5141
+ return result.error;
5142
+ const stage = Array.isArray(result.stages) ? result.stages.find((candidate) => candidate?.status === "failed" && (names(candidate.failureCode) || names(candidate.error))) : undefined;
5143
+ if (typeof stage?.error === "string")
5144
+ return stage.error;
5145
+ if (typeof stage?.failureCode === "string")
5146
+ return stage.failureCode;
5147
+ const code = result.failure?.code;
5148
+ return typeof code === "string" ? code : typeof result.error === "string" ? result.error : "pool fault";
5149
+ }
5138
5150
  var WORKFLOW_INFRASTRUCTURE_ERROR_NAME = "WorkflowInfrastructureError", WORKFLOW_INFRASTRUCTURE_FAULTS, MAX_CAUSE_DEPTH = 8, FAULT_PATTERNS, WorkflowInfrastructureError;
5139
5151
  var init_workflow_infrastructure_error = __esm(() => {
5140
5152
  WORKFLOW_INFRASTRUCTURE_FAULTS = new Set([
@@ -13450,9 +13462,104 @@ var init_workflow_artifacts = __esm(() => {
13450
13462
  pendingArtifactPrunes = new Map;
13451
13463
  });
13452
13464
 
13453
- // dist/builtin/workflows/src/runs/shared/worktree-generation.ts
13465
+ // dist/builtin/workflows/src/runs/shared/worktree-allocation.ts
13466
+ import { randomBytes } from "node:crypto";
13454
13467
  import * as fs from "node:fs";
13455
13468
  import * as path from "node:path";
13469
+ function nextBucketId() {
13470
+ return nextBucketIdOverride?.() ?? randomBytes(2).toString("hex");
13471
+ }
13472
+ function errorCode(error) {
13473
+ return error && typeof error === "object" && "code" in error ? String(error.code) : undefined;
13474
+ }
13475
+ function allocateWorktreeCheckoutPath(root, repositoryName) {
13476
+ for (let attempt = 0;attempt < MAX_BUCKET_ATTEMPTS; attempt++) {
13477
+ const bucket = path.join(root, nextBucketId());
13478
+ try {
13479
+ fs.mkdirSync(bucket);
13480
+ return path.join(bucket, repositoryName);
13481
+ } catch (error) {
13482
+ if (errorCode(error) === "EEXIST")
13483
+ continue;
13484
+ const message = error instanceof Error ? error.message : String(error);
13485
+ throw new Error(`Failed to create worktree bucket ${bucket} under gitWorktreeDir ${root}: ${message}`);
13486
+ }
13487
+ }
13488
+ throw new Error(`Every worktree bucket identifier under gitWorktreeDir ${root} is already in use.`);
13489
+ }
13490
+ function removeEmptyWorktreeBucket(checkout) {
13491
+ try {
13492
+ fs.rmdirSync(path.dirname(checkout));
13493
+ } catch {}
13494
+ }
13495
+ function linkedGitDirectory(checkout) {
13496
+ let pointer;
13497
+ try {
13498
+ if (!fs.lstatSync(path.join(checkout, ".git")).isFile())
13499
+ return;
13500
+ pointer = fs.readFileSync(path.join(checkout, ".git"), "utf8");
13501
+ } catch {
13502
+ return;
13503
+ }
13504
+ if (!pointer.startsWith("gitdir:"))
13505
+ return;
13506
+ const gitDir = pointer.slice("gitdir:".length).trim();
13507
+ return gitDir ? path.resolve(checkout, gitDir) : undefined;
13508
+ }
13509
+ function readWorktreeOwner(gitDir) {
13510
+ let contents;
13511
+ try {
13512
+ contents = fs.readFileSync(path.join(gitDir, WORKTREE_OWNER_FILENAME), "utf8");
13513
+ } catch {
13514
+ return;
13515
+ }
13516
+ try {
13517
+ const record = JSON.parse(contents);
13518
+ return record.version === WORKTREE_OWNER_VERSION && typeof record.ownerRunId === "string" && record.ownerRunId ? record.ownerRunId : undefined;
13519
+ } catch {
13520
+ return;
13521
+ }
13522
+ }
13523
+ function findOwnedWorktreeCheckouts(root, repositoryName, ownerRunId) {
13524
+ let entries;
13525
+ try {
13526
+ entries = fs.readdirSync(root, { withFileTypes: true });
13527
+ } catch {
13528
+ return [];
13529
+ }
13530
+ const owned = [];
13531
+ for (const entry of entries) {
13532
+ if (!entry.isDirectory() || !BUCKET_PATTERN.test(entry.name))
13533
+ continue;
13534
+ const checkout = path.join(root, entry.name, repositoryName);
13535
+ try {
13536
+ if (!fs.lstatSync(checkout).isDirectory())
13537
+ continue;
13538
+ } catch {
13539
+ continue;
13540
+ }
13541
+ const gitDir = linkedGitDirectory(checkout);
13542
+ if (gitDir !== undefined && readWorktreeOwner(gitDir) === ownerRunId)
13543
+ owned.push(checkout);
13544
+ }
13545
+ return owned.sort();
13546
+ }
13547
+ function bindWorktreeOwner(gitDir, ownerRunId) {
13548
+ const record = { version: WORKTREE_OWNER_VERSION, ownerRunId };
13549
+ fs.writeFileSync(path.join(gitDir, WORKTREE_OWNER_FILENAME), `${JSON.stringify(record)}
13550
+ `, {
13551
+ encoding: "utf8",
13552
+ flag: "wx"
13553
+ });
13554
+ }
13555
+ var WORKTREE_OWNER_FILENAME = "atomic-workflow-run.json", WORKTREE_OWNER_VERSION = 1, BUCKET_PATTERN, MAX_BUCKET_ATTEMPTS = 65536, nextBucketIdOverride;
13556
+ var init_worktree_allocation = __esm(() => {
13557
+ BUCKET_PATTERN = /^[0-9a-f]{4}$/;
13558
+ });
13559
+
13560
+ // dist/builtin/workflows/src/runs/shared/worktree-generation.ts
13561
+ import * as fs2 from "node:fs";
13562
+ import * as path2 from "node:path";
13456
13563
  function sameFileIdentity(left, right) {
13457
13564
  return left.dev === right.dev && left.ino === right.ino;
13458
13565
  }
@@ -13462,7 +13569,7 @@ function openMainCheckoutAnchor(gitDir) {
13462
13569
  assertCurrent() {
13463
13570
  if (disposed)
13464
13571
  throw new Error(`cached .git identity anchor is already disposed: ${gitDir}`);
13465
- if (!fs.lstatSync(gitDir).isDirectory()) {
13572
+ if (!fs2.lstatSync(gitDir).isDirectory()) {
13466
13573
  throw new Error(`cached main-checkout .git directory is no longer current: ${gitDir}`);
13467
13574
  }
13468
13575
  },
@@ -13472,17 +13579,17 @@ function openMainCheckoutAnchor(gitDir) {
13472
13579
  };
13473
13580
  }
13474
13581
  function openGitWorktreeGenerationAnchor(worktreeRoot) {
13475
- const gitFile = path.join(worktreeRoot, ".git");
13476
- if (fs.lstatSync(gitFile).isDirectory())
13582
+ const gitFile = path2.join(worktreeRoot, ".git");
13583
+ if (fs2.lstatSync(gitFile).isDirectory())
13477
13584
  return openMainCheckoutAnchor(gitFile);
13478
- const descriptor = fs.openSync(gitFile, "r");
13585
+ const descriptor = fs2.openSync(gitFile, "r");
13479
13586
  let disposed = false;
13480
13587
  const anchor = {
13481
13588
  assertCurrent() {
13482
13589
  if (disposed)
13483
13590
  throw new Error(`cached .git identity anchor is already disposed: ${gitFile}`);
13484
- const held = fs.fstatSync(descriptor, { bigint: true });
13485
- const current = fs.lstatSync(gitFile, { bigint: true });
13591
+ const held = fs2.fstatSync(descriptor, { bigint: true });
13592
+ const current = fs2.lstatSync(gitFile, { bigint: true });
13486
13593
  if (!current.isFile() || !sameFileIdentity(held, current)) {
13487
13594
  throw new Error(`cached .git identity anchor no longer matches the selected checkout: ${gitFile}`);
13488
13595
  }
@@ -13491,7 +13598,7 @@ function openGitWorktreeGenerationAnchor(worktreeRoot) {
13491
13598
  if (disposed)
13492
13599
  return;
13493
13600
  disposed = true;
13494
- fs.closeSync(descriptor);
13601
+ fs2.closeSync(descriptor);
13495
13602
  }
13496
13603
  };
13497
13604
  try {
@@ -13605,84 +13712,84 @@ var init_worktree_git_runner = __esm(() => {
13605
13712
  });
13606
13713
 
13607
13714
  // dist/builtin/workflows/src/runs/shared/worktree-root.ts
13608
- import * as fs2 from "node:fs";
13609
- import * as path2 from "node:path";
13715
+ import * as fs3 from "node:fs";
13716
+ import * as path3 from "node:path";
13610
13717
  function pathFromPointer(contents, prefix, base) {
13611
13718
  if (!contents.startsWith(prefix))
13612
13719
  return;
13613
13720
  const value2 = contents.slice(prefix.length).trim();
13614
13721
  if (!value2)
13615
13722
  return;
13616
- return path2.resolve(base, value2);
13723
+ return path3.resolve(base, value2);
13617
13724
  }
13618
13725
  function readText(file) {
13619
13726
  try {
13620
- return fs2.readFileSync(file, "utf8");
13727
+ return fs3.readFileSync(file, "utf8");
13621
13728
  } catch {
13622
13729
  return;
13623
13730
  }
13624
13731
  }
13625
13732
  function samePath(left, right) {
13626
13733
  try {
13627
- const a = fs2.realpathSync.native(left);
13628
- const b = fs2.realpathSync.native(right);
13734
+ const a = fs3.realpathSync.native(left);
13735
+ const b = fs3.realpathSync.native(right);
13629
13736
  return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
13630
13737
  } catch {
13631
13738
  return false;
13632
13739
  }
13633
13740
  }
13634
13741
  function findGitRoot(cwd) {
13635
- let current = path2.resolve(cwd);
13742
+ let current = path3.resolve(cwd);
13636
13743
  try {
13637
- if (!fs2.statSync(current).isDirectory())
13638
- current = path2.dirname(current);
13744
+ if (!fs3.statSync(current).isDirectory())
13745
+ current = path3.dirname(current);
13639
13746
  } catch {
13640
13747
  return;
13641
13748
  }
13642
13749
  while (true) {
13643
13750
  try {
13644
- const entry = fs2.statSync(path2.join(current, ".git"));
13751
+ const entry = fs3.statSync(path3.join(current, ".git"));
13645
13752
  if (entry.isDirectory() || entry.isFile())
13646
13753
  return current;
13647
13754
  } catch {}
13648
- const parent = path2.dirname(current);
13755
+ const parent = path3.dirname(current);
13649
13756
  if (parent === current)
13650
13757
  return;
13651
13758
  current = parent;
13652
13759
  }
13653
13760
  }
13654
13761
  function resolveMainRepoRoot(repoRoot) {
13655
- const gitEntry = path2.join(repoRoot, ".git");
13762
+ const gitEntry = path3.join(repoRoot, ".git");
13656
13763
  let stats;
13657
13764
  try {
13658
- stats = fs2.statSync(gitEntry);
13765
+ stats = fs3.statSync(gitEntry);
13659
13766
  } catch {
13660
13767
  return;
13661
13768
  }
13662
13769
  if (stats.isDirectory())
13663
- return path2.resolve(repoRoot);
13770
+ return path3.resolve(repoRoot);
13664
13771
  if (!stats.isFile())
13665
13772
  return;
13666
13773
  const pointer = readText(gitEntry);
13667
13774
  if (pointer === undefined)
13668
13775
  return;
13669
13776
  const gitDir = pathFromPointer(pointer, "gitdir:", repoRoot);
13670
- if (gitDir === undefined || path2.basename(path2.dirname(gitDir)) !== "worktrees")
13777
+ if (gitDir === undefined || path3.basename(path3.dirname(gitDir)) !== "worktrees")
13671
13778
  return;
13672
- const commonGitDir = path2.dirname(path2.dirname(gitDir));
13673
- if (path2.basename(commonGitDir) !== ".git")
13779
+ const commonGitDir = path3.dirname(path3.dirname(gitDir));
13780
+ if (path3.basename(commonGitDir) !== ".git")
13674
13781
  return;
13675
- const backPointer = readText(path2.join(gitDir, "gitdir"));
13676
- const commonPointer = readText(path2.join(gitDir, "commondir"));
13782
+ const backPointer = readText(path3.join(gitDir, "gitdir"));
13783
+ const commonPointer = readText(path3.join(gitDir, "commondir"));
13677
13784
  if (backPointer === undefined || commonPointer === undefined)
13678
13785
  return;
13679
- const linkedGitEntry = path2.resolve(gitDir, backPointer.trim());
13680
- const declaredCommonDir = path2.resolve(gitDir, commonPointer.trim());
13786
+ const linkedGitEntry = path3.resolve(gitDir, backPointer.trim());
13787
+ const declaredCommonDir = path3.resolve(gitDir, commonPointer.trim());
13681
13788
  if (!backPointer.trim() || !commonPointer.trim())
13682
13789
  return;
13683
13790
  if (!samePath(linkedGitEntry, gitEntry) || !samePath(declaredCommonDir, commonGitDir))
13684
13791
  return;
13685
- return path2.dirname(commonGitDir);
13792
+ return path3.dirname(commonGitDir);
13686
13793
  }
13687
13794
  function findCanonicalGitRoot(cwd) {
13688
13795
  const repoRoot = findGitRoot(cwd);
@@ -13691,33 +13798,31 @@ function findCanonicalGitRoot(cwd) {
13691
13798
  var init_worktree_root = () => {};
13692
13799
 
13693
13800
  // dist/builtin/workflows/src/runs/shared/worktree-git.ts
13694
- import * as fs3 from "node:fs";
13695
- import * as path3 from "node:path";
13696
- function gitWorktreeSetupCacheKey(options) {
13697
- const repoRoot = repositoryRootForGitWorktree(options.cwd);
13698
- const { logicalRepoRoot } = cwdWithinGitRepository(options.cwd, repoRoot);
13699
- const worktreeRoot = resolveGitWorktreePath(options.gitWorktreeDir, logicalRepoRoot);
13801
+ import * as fs4 from "node:fs";
13802
+ import * as path4 from "node:path";
13803
+ function gitWorktreeSetupCacheKey(request) {
13700
13804
  return JSON.stringify([
13701
- comparableRealPath(gitCommonDirForWorktree(repoRoot)),
13702
- comparablePathThroughExistingAncestor(worktreeRoot)
13805
+ comparablePathThroughExistingAncestor(request.options.cwd),
13806
+ comparablePathThroughExistingAncestor(request.target)
13703
13807
  ]);
13704
13808
  }
13705
- function createGitWorktreeSetupCache() {
13809
+ function createGitWorktreeSetupCache(ownerRunId) {
13706
13810
  const results = new Map;
13707
13811
  let disposed = false;
13708
13812
  return {
13709
13813
  get(options) {
13710
13814
  if (disposed)
13711
13815
  throw new Error("Git worktree setup cache is already disposed");
13712
- const key = gitWorktreeSetupCacheKey(options);
13816
+ const request = resolveGitWorktreeSetupRequest(options);
13817
+ const key = gitWorktreeSetupCacheKey(request);
13713
13818
  const cached = results.get(key);
13714
13819
  if (cached !== undefined) {
13715
13820
  assertCachedGitWorktreeIdentity(cached, options);
13716
13821
  return cached.result;
13717
13822
  }
13718
- const result = setupGitWorktree(options);
13719
- results.set(key, { result, identity: captureGitWorktreeIdentity(result) });
13720
- return result;
13823
+ const setup = setupGitWorktreeFromRequest(request, ownerRunId);
13824
+ results.set(key, { ...setup, identity: captureGitWorktreeIdentity(setup) });
13825
+ return setup.result;
13721
13826
  },
13722
13827
  dispose() {
13723
13828
  if (disposed)
@@ -13749,10 +13854,10 @@ function worktreeRecoveryCommand(repositoryRoot, worktreeDir) {
13749
13854
  return `git -C ${quoteShellArg(repositoryRoot)} worktree remove --force ${quoteShellArg(worktreeDir)}`;
13750
13855
  }
13751
13856
  function hasGrandparent(value2) {
13752
- const parent = path3.dirname(value2);
13857
+ const parent = path4.dirname(value2);
13753
13858
  if (parent === value2)
13754
13859
  return false;
13755
- const grandparent = path3.dirname(parent);
13860
+ const grandparent = path4.dirname(parent);
13756
13861
  return grandparent !== parent;
13757
13862
  }
13758
13863
  function pathAncestors(value2) {
@@ -13760,7 +13865,7 @@ function pathAncestors(value2) {
13760
13865
  let current = value2;
13761
13866
  while (true) {
13762
13867
  ancestors.push(current);
13763
- const parent = path3.dirname(current);
13868
+ const parent = path4.dirname(current);
13764
13869
  if (parent === current)
13765
13870
  return ancestors;
13766
13871
  current = parent;
@@ -13769,89 +13874,167 @@ function pathAncestors(value2) {
13769
13874
  function shouldPreserveLogicalPath(logicalPath) {
13770
13875
  return pathAncestors(logicalPath).some((ancestor) => {
13771
13876
  try {
13772
- return fs3.lstatSync(ancestor).isSymbolicLink() && hasGrandparent(ancestor);
13877
+ return fs4.lstatSync(ancestor).isSymbolicLink() && hasGrandparent(ancestor);
13773
13878
  } catch {
13774
13879
  return false;
13775
13880
  }
13776
13881
  });
13777
13882
  }
13778
13883
  function canonicalizePreservingSymlinks(value2) {
13779
- const logicalPath = path3.resolve(value2);
13884
+ const logicalPath = path4.resolve(value2);
13780
13885
  const preserveLogicalPath = shouldPreserveLogicalPath(logicalPath);
13781
13886
  try {
13782
- const canonical = fs3.realpathSync.native(logicalPath);
13887
+ const canonical = fs4.realpathSync.native(logicalPath);
13783
13888
  return preserveLogicalPath && canonical !== logicalPath ? logicalPath : canonical;
13784
13889
  } catch {
13785
13890
  return logicalPath;
13786
13891
  }
13787
13892
  }
13788
- function resolveGitWorktreePath(value2, repoRoot) {
13893
+ function resolveGitWorktreePath(value2, baseDir) {
13789
13894
  const trimmed = value2.trim();
13790
13895
  if (!trimmed)
13791
13896
  throw new Error("gitWorktreeDir cannot be empty");
13792
13897
  if (trimmed.includes("\x00")) {
13793
13898
  throw new Error("gitWorktreeDir contains an unusable null byte; provide a valid path or omit gitWorktreeDir.");
13794
13899
  }
13795
- return canonicalizePreservingSymlinks(path3.isAbsolute(trimmed) ? trimmed : path3.resolve(repoRoot, trimmed));
13900
+ return canonicalizePreservingSymlinks(path4.isAbsolute(trimmed) ? trimmed : path4.resolve(baseDir, trimmed));
13796
13901
  }
13797
13902
  function comparableRealPath(value2) {
13798
- const realpath = fs3.realpathSync.native(value2).replace(/\\/g, "/");
13903
+ const realpath = fs4.realpathSync.native(value2).replace(/\\/g, "/");
13799
13904
  return process.platform === "win32" ? realpath.toLowerCase() : realpath;
13800
13905
  }
13801
- function comparablePathThroughExistingAncestor(value2) {
13802
- let existing = path3.resolve(value2);
13906
+ function nearestExistingAncestor(value2) {
13907
+ let existing = path4.resolve(value2);
13803
13908
  const missing = [];
13804
13909
  while (!pathExistsSync(existing)) {
13805
- const parent = path3.dirname(existing);
13910
+ const parent = path4.dirname(existing);
13806
13911
  if (parent === existing)
13807
13912
  break;
13808
- missing.unshift(path3.basename(existing));
13913
+ missing.unshift(path4.basename(existing));
13809
13914
  existing = parent;
13810
13915
  }
13811
- const resolved = path3.resolve(comparableRealPath(existing), ...missing).replace(/\\/g, "/");
13916
+ return { existing, missing };
13917
+ }
13918
+ function comparablePathThroughExistingAncestor(value2) {
13919
+ const { existing, missing } = nearestExistingAncestor(value2);
13920
+ const resolved = path4.resolve(comparableRealPath(existing), ...missing).replace(/\\/g, "/");
13812
13921
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
13813
13922
  }
13923
+ function isFileOrLink(value2) {
13924
+ try {
13925
+ const entry = fs4.lstatSync(value2);
13926
+ return entry.isFile() || entry.isSymbolicLink();
13927
+ } catch {
13928
+ return false;
13929
+ }
13930
+ }
13931
+ function isDirectory(value2) {
13932
+ try {
13933
+ return fs4.statSync(value2).isDirectory();
13934
+ } catch {
13935
+ return false;
13936
+ }
13937
+ }
13938
+ function looksLikeGitDirectory(directory) {
13939
+ if (!isDirectory(directory))
13940
+ return false;
13941
+ if (path4.basename(directory) === ".git")
13942
+ return true;
13943
+ if (!isFileOrLink(path4.join(directory, "HEAD")))
13944
+ return false;
13945
+ if (isFileOrLink(path4.join(directory, "commondir")))
13946
+ return true;
13947
+ return isDirectory(path4.join(directory, "objects")) && isDirectory(path4.join(directory, "refs"));
13948
+ }
13949
+ function enclosingGitMetadataDirectory(target) {
13950
+ const { existing } = nearestExistingAncestor(target);
13951
+ let realExisting = existing;
13952
+ try {
13953
+ realExisting = fs4.realpathSync.native(existing);
13954
+ } catch {}
13955
+ for (const candidate of new Set([...pathAncestors(existing), ...pathAncestors(realExisting)])) {
13956
+ if (looksLikeGitDirectory(candidate))
13957
+ return candidate;
13958
+ }
13959
+ const insideGitDir = runGitReadOnlyProbe(existing, ["rev-parse", "--is-inside-git-dir"]);
13960
+ if (insideGitDir.status !== 0 && isGitTimeoutResult(insideGitDir)) {
13961
+ throw new Error(`Timed out while validating gitWorktreeDir ${target}. Git reported: ${gitFailureMessage(insideGitDir)}`);
13962
+ }
13963
+ return insideGitDir.status === 0 && insideGitDir.stdout.trim() === "true" ? existing : undefined;
13964
+ }
13965
+ function assertSafeGitWorktreeTarget(target, invokingRoot) {
13966
+ const gitDirectory = enclosingGitMetadataDirectory(target);
13967
+ if (gitDirectory !== undefined) {
13968
+ 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.`);
13969
+ }
13970
+ if (invokingRoot !== undefined)
13971
+ validateWorktreeOutsideInvokingCheckout(target, invokingRoot);
13972
+ const { existing, missing } = nearestExistingAncestor(target);
13973
+ const workingTree = gitTopLevel(existing);
13974
+ if (workingTree === undefined)
13975
+ return;
13976
+ if (missing.length === 0 && comparableRealPath(target) === comparableRealPath(workingTree))
13977
+ return;
13978
+ 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.`);
13979
+ }
13814
13980
  function validateWorktreeOutsideInvokingCheckout(worktreeRoot, repoRoot) {
13815
13981
  const repository = comparableRealPath(repoRoot);
13816
13982
  const candidate = comparablePathThroughExistingAncestor(worktreeRoot);
13817
- const relativePath = path3.relative(repository, candidate);
13983
+ const relativePath = path4.relative(repository, candidate);
13818
13984
  if (relativePath === "") {
13819
- throw new Error("gitWorktreeDir must not resolve to the invoking checkout; provide a separate same-repository worktree path.");
13985
+ throw new Error("gitWorktreeDir must not resolve to the invoking checkout; provide a separate worktree path outside it.");
13820
13986
  }
13821
- if (!relativePath.startsWith(`..${path3.sep}`) && relativePath !== ".." && !path3.isAbsolute(relativePath)) {
13822
- throw new Error("gitWorktreeDir must be outside the invoking checkout; provide a separate same-repository worktree path.");
13987
+ if (!relativePath.startsWith(`..${path4.sep}`) && relativePath !== ".." && !path4.isAbsolute(relativePath)) {
13988
+ throw new Error("gitWorktreeDir must be outside the invoking checkout; provide a separate worktree path outside it.");
13823
13989
  }
13824
13990
  }
13825
13991
  function gitPathFromOutput(value2, cwd) {
13826
13992
  const trimmed = value2.trim();
13827
13993
  if (!trimmed)
13828
13994
  return;
13829
- return path3.isAbsolute(trimmed) ? path3.resolve(trimmed) : path3.resolve(cwd, trimmed);
13995
+ return path4.isAbsolute(trimmed) ? path4.resolve(trimmed) : path4.resolve(cwd, trimmed);
13830
13996
  }
13831
13997
  function pathExistsSync(value2) {
13998
+ return pathStatSync(value2) !== undefined;
13999
+ }
14000
+ function pathStatSync(value2) {
13832
14001
  try {
13833
- fs3.statSync(value2);
13834
- return true;
14002
+ return fs4.statSync(value2);
13835
14003
  } catch (error) {
13836
14004
  const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
13837
14005
  if (code === "ENOENT" || code === "ENOTDIR")
13838
- return false;
14006
+ return;
13839
14007
  throw error;
13840
14008
  }
13841
14009
  }
13842
- function repositoryRootForGitWorktree(cwd) {
14010
+ function probeInvokingRepository(cwd) {
13843
14011
  const result = runGitReadOnlyProbe(cwd, ["rev-parse", "--show-toplevel"]);
13844
14012
  if (result.status !== 0) {
13845
14013
  if (isGitTimeoutResult(result)) {
13846
14014
  throw new Error(`Timed out while checking the Git repository for gitWorktreeDir from ${cwd}. Git reported: ${gitFailureMessage(result)}`);
13847
14015
  }
13848
- 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)}`);
14016
+ return { failure: gitFailureMessage(result) };
13849
14017
  }
13850
- const normalized = gitPathFromOutput(result.stdout, cwd);
13851
- if (normalized === undefined) {
14018
+ const root = gitPathFromOutput(result.stdout, cwd);
14019
+ if (root === undefined) {
13852
14020
  throw new Error(`gitWorktreeDir could not resolve the repository top level from ${cwd}: git returned an empty path.`);
13853
14021
  }
13854
- return normalized;
14022
+ return { repository: { root, ...cwdWithinGitRepository(cwd, root) } };
14023
+ }
14024
+ function resolveGitWorktreeSetupRequest(options) {
14025
+ const probe = probeInvokingRepository(options.cwd);
14026
+ if ("repository" in probe) {
14027
+ return {
14028
+ options,
14029
+ repository: probe.repository,
14030
+ target: resolveGitWorktreePath(options.gitWorktreeDir, probe.repository.logicalRoot)
14031
+ };
14032
+ }
14033
+ return {
14034
+ options,
14035
+ repositoryProbeFailure: probe.failure,
14036
+ target: resolveGitWorktreePath(options.gitWorktreeDir, canonicalizePreservingSymlinks(options.cwd))
14037
+ };
13855
14038
  }
13856
14039
  function gitTopLevelFromResult(result, cwd, description) {
13857
14040
  if (result.status !== 0) {
@@ -13887,25 +14070,25 @@ function dirnameForEachRelativeComponent(base, relativePath) {
13887
14070
  continue;
13888
14071
  if (component === "..")
13889
14072
  return;
13890
- current = path3.dirname(current);
14073
+ current = path4.dirname(current);
13891
14074
  }
13892
14075
  return current;
13893
14076
  }
13894
14077
  function cwdWithinGitRepository(cwd, repoRoot) {
13895
- const sourceCwd = fs3.realpathSync.native(cwd);
13896
- const sourceRepoRoot = fs3.realpathSync.native(repoRoot);
13897
- const relativeCwd = path3.relative(sourceRepoRoot, sourceCwd);
13898
- const safeRelativeCwd = relativeCwd === "" || relativeCwd.startsWith("..") || path3.isAbsolute(relativeCwd) ? "" : relativeCwd;
14078
+ const sourceCwd = fs4.realpathSync.native(cwd);
14079
+ const sourceRepoRoot = fs4.realpathSync.native(repoRoot);
14080
+ const relativeCwd = path4.relative(sourceRepoRoot, sourceCwd);
14081
+ const safeRelativeCwd = relativeCwd === "" || relativeCwd.startsWith("..") || path4.isAbsolute(relativeCwd) ? "" : relativeCwd;
13899
14082
  const logicalCwd = canonicalizePreservingSymlinks(cwd);
13900
14083
  return {
13901
14084
  relativeCwd: safeRelativeCwd,
13902
- logicalRepoRoot: dirnameForEachRelativeComponent(logicalCwd, safeRelativeCwd) ?? repoRoot
14085
+ logicalRoot: dirnameForEachRelativeComponent(logicalCwd, safeRelativeCwd) ?? repoRoot
13903
14086
  };
13904
14087
  }
13905
14088
  function workspaceCwdForGitWorktreeRoot(worktreeRoot, relativeCwd) {
13906
- return relativeCwd === "" ? worktreeRoot : path3.join(worktreeRoot, relativeCwd);
14089
+ return relativeCwd === "" ? worktreeRoot : path4.join(worktreeRoot, relativeCwd);
13907
14090
  }
13908
- function validateExistingGitWorktreeRoot(worktreeRoot, repoRoot) {
14091
+ function validateGitWorktreeRoot(worktreeRoot) {
13909
14092
  const topLevel = gitTopLevel(worktreeRoot);
13910
14093
  if (topLevel === undefined) {
13911
14094
  throw new Error(`gitWorktreeDir already exists but is not a Git worktree: ${worktreeRoot}`);
@@ -13913,8 +14096,18 @@ function validateExistingGitWorktreeRoot(worktreeRoot, repoRoot) {
13913
14096
  if (comparableRealPath(worktreeRoot) !== comparableRealPath(topLevel)) {
13914
14097
  throw new Error(`gitWorktreeDir already exists but is not a Git worktree root: ${worktreeRoot}. Git top-level checkout is ${topLevel}`);
13915
14098
  }
13916
- if (comparableRealPath(gitCommonDirForWorktree(repoRoot)) !== comparableRealPath(gitCommonDirForWorktree(topLevel))) {
13917
- throw new Error(`gitWorktreeDir already exists but does not belong to the invoking Git repository: ${worktreeRoot}`);
14099
+ return gitCommonDirForWorktree(topLevel);
14100
+ }
14101
+ function existingGitWorktreeCommonDir(directory) {
14102
+ return gitTopLevel(directory) === undefined ? undefined : validateGitWorktreeRoot(directory);
14103
+ }
14104
+ function sameGitCommonDir(left, right) {
14105
+ return comparableRealPath(left) === comparableRealPath(right);
14106
+ }
14107
+ function validateCreatedGitWorktree(worktreeRoot, repository) {
14108
+ assertSafeGitWorktreeTarget(worktreeRoot, repository.root);
14109
+ if (!sameGitCommonDir(validateGitWorktreeRoot(worktreeRoot), gitCommonDirForWorktree(repository.root))) {
14110
+ throw new Error(`gitWorktreeDir does not belong to the invoking Git repository: ${worktreeRoot}`);
13918
14111
  }
13919
14112
  }
13920
14113
  function gitDirectoryForWorktree(cwd) {
@@ -13928,26 +14121,27 @@ function gitDirectoryForWorktree(cwd) {
13928
14121
  return gitDir;
13929
14122
  }
13930
14123
  function captureGitWorktreeIdentitySnapshot(setup) {
13931
- validateWorktreeOutsideInvokingCheckout(setup.worktreeRoot, setup.repositoryRoot);
13932
- validateExistingGitWorktreeRoot(setup.worktreeRoot, setup.repositoryRoot);
13933
- const stats = fs3.statSync(setup.worktreeRoot);
14124
+ const { worktreeRoot, repositoryRoot } = setup.result;
14125
+ assertSafeGitWorktreeTarget(worktreeRoot, setup.invokingRoot);
14126
+ const commonDir = validateGitWorktreeRoot(worktreeRoot);
14127
+ const stats = fs4.statSync(worktreeRoot);
13934
14128
  return {
13935
- worktreeRealPath: comparableRealPath(setup.worktreeRoot),
13936
- repositoryRealPath: comparableRealPath(setup.repositoryRoot),
13937
- gitDirRealPath: comparableRealPath(gitDirectoryForWorktree(setup.worktreeRoot)),
13938
- commonDirRealPath: comparableRealPath(gitCommonDirForWorktree(setup.worktreeRoot)),
14129
+ worktreeRealPath: comparableRealPath(worktreeRoot),
14130
+ repositoryRealPath: comparableRealPath(repositoryRoot),
14131
+ gitDirRealPath: comparableRealPath(gitDirectoryForWorktree(worktreeRoot)),
14132
+ commonDirRealPath: comparableRealPath(commonDir),
13939
14133
  device: stats.dev,
13940
14134
  inode: stats.ino
13941
14135
  };
13942
14136
  }
13943
14137
  function captureGitWorktreeIdentity(setup) {
13944
14138
  const snapshot = captureGitWorktreeIdentitySnapshot(setup);
13945
- return { ...snapshot, generation: openGitWorktreeGenerationAnchor(setup.worktreeRoot) };
14139
+ return { ...snapshot, generation: openGitWorktreeGenerationAnchor(setup.result.worktreeRoot) };
13946
14140
  }
13947
14141
  function assertCachedGitWorktreeIdentity(cached, options) {
13948
14142
  let current;
13949
14143
  try {
13950
- current = captureGitWorktreeIdentitySnapshot(cached.result);
14144
+ current = captureGitWorktreeIdentitySnapshot(cached);
13951
14145
  cached.identity.generation.assertCurrent();
13952
14146
  } catch (error) {
13953
14147
  const message = error instanceof Error ? error.message : String(error);
@@ -13955,34 +14149,58 @@ function assertCachedGitWorktreeIdentity(cached, options) {
13955
14149
  }
13956
14150
  const expected = cached.identity;
13957
14151
  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) {
13958
- throw new Error(`Cached gitWorktreeDir changed before reuse: ${options.gitWorktreeDir}. Start a new run with the intended same-repository worktree.`);
13959
- }
14152
+ throw new Error(`Cached gitWorktreeDir changed before reuse: ${options.gitWorktreeDir}. Start a new run with the intended worktree.`);
14153
+ }
14154
+ }
14155
+ function requireInvokingRepository(request, action) {
14156
+ if (request.repository !== undefined)
14157
+ return request.repository;
14158
+ throw new Error([
14159
+ `gitWorktreeDir requires the workflow to be invoked from inside a Git repository to ${action}.`,
14160
+ "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.",
14161
+ `Git reported: ${request.repositoryProbeFailure ?? "no repository"}`
14162
+ ].join(" "));
14163
+ }
14164
+ function setupGitWorktreeFromRequest(request, ownerRunId) {
14165
+ const { repository, target } = request;
14166
+ assertSafeGitWorktreeTarget(target, repository?.root);
14167
+ const entry = pathStatSync(target);
14168
+ if (entry === undefined)
14169
+ return createGitWorktreeAtPath(request, false);
14170
+ if (!entry.isDirectory())
14171
+ throw new Error(`gitWorktreeDir already exists but is not a directory: ${target}`);
14172
+ if (fs4.readdirSync(target).length === 0)
14173
+ return createGitWorktreeAtPath(request, true);
14174
+ const commonDir = existingGitWorktreeCommonDir(target);
14175
+ if (commonDir === undefined)
14176
+ return allocateGitWorktree(request, ownerRunId);
14177
+ const sameRepository = repository !== undefined && sameGitCommonDir(commonDir, gitCommonDirForWorktree(repository.root));
14178
+ return {
14179
+ result: {
14180
+ worktreeRoot: target,
14181
+ cwd: sameRepository ? workspaceCwdForGitWorktreeRoot(target, repository.relativeCwd) : target,
14182
+ repositoryRoot: sameRepository ? repository.root : target,
14183
+ created: false
14184
+ },
14185
+ ...repository !== undefined ? { invokingRoot: repository.root } : {}
14186
+ };
13960
14187
  }
13961
- function setupGitWorktree(options) {
13962
- const repoRoot = repositoryRootForGitWorktree(options.cwd);
13963
- const mainRoot = findCanonicalGitRoot(repoRoot);
14188
+ function createGitWorktreeAtPath(request, existingEmptyDirectory) {
14189
+ const { target: worktreeRoot } = request;
14190
+ const repository = requireInvokingRepository(request, `create a new worktree at ${worktreeRoot}`);
14191
+ const mainRoot = findCanonicalGitRoot(repository.root);
13964
14192
  if (mainRoot === undefined) {
13965
- throw new Error(`Unable to resolve the canonical main Git repository root from ${repoRoot}.`);
13966
- }
13967
- const { relativeCwd, logicalRepoRoot } = cwdWithinGitRepository(options.cwd, repoRoot);
13968
- const worktreeRoot = resolveGitWorktreePath(options.gitWorktreeDir, logicalRepoRoot);
13969
- validateWorktreeOutsideInvokingCheckout(worktreeRoot, repoRoot);
13970
- if (pathExistsSync(worktreeRoot)) {
13971
- validateExistingGitWorktreeRoot(worktreeRoot, repoRoot);
13972
- return {
13973
- worktreeRoot,
13974
- cwd: workspaceCwdForGitWorktreeRoot(worktreeRoot, relativeCwd),
13975
- repositoryRoot: repoRoot,
13976
- created: false
13977
- };
14193
+ throw new Error(`Unable to resolve the canonical main Git repository root from ${repository.root}.`);
13978
14194
  }
13979
- try {
13980
- fs3.mkdirSync(path3.dirname(worktreeRoot), { recursive: true });
13981
- } catch (error) {
13982
- const message = error instanceof Error ? error.message : String(error);
13983
- throw new Error(`Failed to create parent directory for requested gitWorktreeDir ${worktreeRoot}: ${message}`);
14195
+ if (!existingEmptyDirectory) {
14196
+ try {
14197
+ fs4.mkdirSync(path4.dirname(worktreeRoot), { recursive: true });
14198
+ } catch (error) {
14199
+ const message = error instanceof Error ? error.message : String(error);
14200
+ throw new Error(`Failed to create parent directory for requested gitWorktreeDir ${worktreeRoot}: ${message}`);
14201
+ }
13984
14202
  }
13985
- const baseRef = options.baseBranch?.trim() || "HEAD";
14203
+ const baseRef = request.options.baseBranch?.trim() || "HEAD";
13986
14204
  const result = runGit(mainRoot, ["worktree", "add", "--detach", worktreeRoot, baseRef]);
13987
14205
  if (result.status !== 0) {
13988
14206
  throw new Error([
@@ -13992,30 +14210,127 @@ function setupGitWorktree(options) {
13992
14210
  `));
13993
14211
  }
13994
14212
  try {
13995
- validateWorktreeOutsideInvokingCheckout(worktreeRoot, repoRoot);
13996
- validateExistingGitWorktreeRoot(worktreeRoot, repoRoot);
14213
+ validateCreatedGitWorktree(worktreeRoot, repository);
13997
14214
  } catch (error) {
13998
14215
  runGit(mainRoot, ["worktree", "remove", "--force", worktreeRoot]);
13999
14216
  const message = error instanceof Error ? error.message : String(error);
14000
14217
  throw new Error(`Created gitWorktreeDir failed post-creation validation: ${message}`);
14001
14218
  }
14002
14219
  return {
14003
- worktreeRoot,
14004
- cwd: workspaceCwdForGitWorktreeRoot(worktreeRoot, relativeCwd),
14005
- repositoryRoot: repoRoot,
14006
- created: true
14220
+ result: {
14221
+ worktreeRoot,
14222
+ cwd: workspaceCwdForGitWorktreeRoot(worktreeRoot, repository.relativeCwd),
14223
+ repositoryRoot: repository.root,
14224
+ created: true
14225
+ },
14226
+ invokingRoot: repository.root
14007
14227
  };
14008
14228
  }
14229
+ function containedWorktreeCwd(checkout, relativeCwd) {
14230
+ const cwd = workspaceCwdForGitWorktreeRoot(checkout, relativeCwd);
14231
+ let resolved;
14232
+ try {
14233
+ resolved = fs4.realpathSync.native(cwd);
14234
+ } catch {
14235
+ resolved = "";
14236
+ }
14237
+ const relative2 = resolved === "" ? undefined : path4.relative(fs4.realpathSync.native(checkout), resolved);
14238
+ if (relative2 === undefined || relative2 === ".." || relative2.startsWith(`..${path4.sep}`) || path4.isAbsolute(relative2) || !fs4.statSync(resolved).isDirectory()) {
14239
+ throw new Error(`The selected base does not contain a working directory ${relativeCwd || "."} inside the worktree ${checkout}.`);
14240
+ }
14241
+ return cwd;
14242
+ }
14243
+ function allocatedGitWorktreeSetup(checkout, repository, created) {
14244
+ return {
14245
+ result: {
14246
+ worktreeRoot: checkout,
14247
+ cwd: containedWorktreeCwd(checkout, repository.relativeCwd),
14248
+ repositoryRoot: repository.root,
14249
+ created
14250
+ },
14251
+ invokingRoot: repository.root
14252
+ };
14253
+ }
14254
+ function validateOwnedGitWorktree(checkout, repository) {
14255
+ validateCreatedGitWorktree(checkout, repository);
14256
+ const gitDir = gitDirectoryForWorktree(checkout);
14257
+ if (sameGitCommonDir(gitDir, gitCommonDirForWorktree(checkout))) {
14258
+ throw new Error(`${checkout} is not a linked worktree`);
14259
+ }
14260
+ const backlink = fs4.readFileSync(path4.join(gitDir, "gitdir"), "utf8").trim();
14261
+ if (!backlink || comparableRealPath(path4.resolve(gitDir, backlink)) !== comparableRealPath(path4.join(checkout, ".git"))) {
14262
+ throw new Error(`the Git directory of ${checkout} belongs to another checkout`);
14263
+ }
14264
+ }
14265
+ function findOwnedGitWorktree(root, repository, ownerRunId) {
14266
+ const [checkout] = findOwnedWorktreeCheckouts(root, path4.basename(repository.root), ownerRunId);
14267
+ if (checkout === undefined)
14268
+ return;
14269
+ try {
14270
+ validateOwnedGitWorktree(checkout, repository);
14271
+ return allocatedGitWorktreeSetup(checkout, repository, false);
14272
+ } catch (error) {
14273
+ const message = error instanceof Error ? error.message : String(error);
14274
+ throw new Error(`The worktree this workflow run allocated under gitWorktreeDir ${root} can no longer be reused: ${checkout}. ${message}`);
14275
+ }
14276
+ }
14277
+ function allocateGitWorktree(request, ownerRunId) {
14278
+ const { target: root } = request;
14279
+ const repository = requireInvokingRepository(request, `create a new worktree under ${root}`);
14280
+ if (ownerRunId !== undefined) {
14281
+ const owned = findOwnedGitWorktree(root, repository, ownerRunId);
14282
+ if (owned !== undefined)
14283
+ return owned;
14284
+ }
14285
+ const baseRef = request.options.baseBranch?.trim() || "HEAD";
14286
+ const revision = runGit(repository.root, ["rev-parse", "--verify", "--end-of-options", `${baseRef}^{commit}`]);
14287
+ const commit = revision.status === 0 ? revision.stdout.trim() : "";
14288
+ if (commit === "") {
14289
+ throw new Error(`Failed to resolve ${baseRef} to a commit for gitWorktreeDir ${root}. Git reported: ${gitFailureMessage(revision)}`);
14290
+ }
14291
+ const checkout = allocateWorktreeCheckoutPath(root, path4.basename(repository.root));
14292
+ const add = runGit(repository.root, ["worktree", "add", "--detach", "--no-checkout", checkout, commit]);
14293
+ if (add.status !== 0) {
14294
+ removeEmptyWorktreeBucket(checkout);
14295
+ throw new Error(`Failed to create git worktree at ${checkout} under gitWorktreeDir ${root} from ${baseRef}. Git reported: ${gitFailureMessage(add)}`);
14296
+ }
14297
+ try {
14298
+ const gitDir = gitDirectoryForWorktree(checkout);
14299
+ runGitOrThrow(checkout, ["config", "--file", path4.join(gitDir, "config.worktree"), "core.worktree", checkout]);
14300
+ runGitOrThrow(checkout, ["--work-tree=.", "reset", "--hard", "--no-recurse-submodules", commit]);
14301
+ validateCreatedGitWorktree(checkout, repository);
14302
+ const setup = allocatedGitWorktreeSetup(checkout, repository, true);
14303
+ if (ownerRunId !== undefined)
14304
+ bindWorktreeOwner(gitDir, ownerRunId);
14305
+ return setup;
14306
+ } catch (error) {
14307
+ const message = error instanceof Error ? error.message : String(error);
14308
+ const removal = runGit(repository.root, ["worktree", "remove", "--force", checkout]);
14309
+ removeEmptyWorktreeBucket(checkout);
14310
+ const rollback = removal.status === 0 ? "" : ` Rolling back the incomplete worktree also failed; remove it with: ${worktreeRecoveryCommand(repository.root, checkout)}`;
14311
+ throw new Error(`Failed to set up git worktree at ${checkout} under gitWorktreeDir ${root}: ${message}${rollback}`);
14312
+ }
14313
+ }
14314
+ function runGitOrThrow(cwd, args) {
14315
+ const result = runGit(cwd, args);
14316
+ if (result.status !== 0)
14317
+ throw new Error(gitFailureMessage(result));
14318
+ }
14319
+ function setupGitWorktree(options) {
14320
+ return setupGitWorktreeFromRequest(resolveGitWorktreeSetupRequest(options)).result;
14321
+ }
14009
14322
  var init_worktree_git = __esm(() => {
14323
+ init_worktree_allocation();
14010
14324
  init_worktree_generation();
14011
14325
  init_worktree_git_runner();
14012
14326
  init_worktree_root();
14327
+ init_worktree_allocation();
14013
14328
  init_worktree_git_runner();
14014
14329
  });
14015
14330
 
14016
14331
  // dist/builtin/workflows/src/runs/shared/worktree-cache-lifecycle.ts
14017
- function createGitWorktreeSetupCacheOwner(suppliedCache) {
14018
- const cache = suppliedCache ?? createGitWorktreeSetupCache();
14332
+ function createGitWorktreeSetupCacheOwner(suppliedCache, ownerRunId) {
14333
+ const cache = suppliedCache ?? createGitWorktreeSetupCache(ownerRunId);
14019
14334
  const ownsCache = suppliedCache === undefined;
14020
14335
  let released = false;
14021
14336
  return {
@@ -14036,20 +14351,20 @@ var init_worktree_cache_lifecycle = __esm(() => {
14036
14351
  });
14037
14352
 
14038
14353
  // dist/builtin/workflows/src/runs/shared/worktree-diff.ts
14039
- import * as fs4 from "node:fs";
14040
- import * as path4 from "node:path";
14354
+ import * as fs5 from "node:fs";
14355
+ import * as path5 from "node:path";
14041
14356
  function safePatchAgentName(agent) {
14042
14357
  return agent.replace(/[^\w.-]/g, "_");
14043
14358
  }
14044
14359
  function removeSyntheticPath(worktree, syntheticPath) {
14045
- const resolved = path4.resolve(worktree.path, syntheticPath);
14046
- const relative = path4.relative(worktree.path, resolved);
14047
- if (!relative || relative === "." || relative === ".." || relative.startsWith(`..${path4.sep}`) || path4.isAbsolute(relative)) {
14360
+ const resolved = path5.resolve(worktree.path, syntheticPath);
14361
+ const relative = path5.relative(worktree.path, resolved);
14362
+ if (!relative || relative === "." || relative === ".." || relative.startsWith(`..${path5.sep}`) || path5.isAbsolute(relative)) {
14048
14363
  return;
14049
14364
  }
14050
14365
  let stat;
14051
14366
  try {
14052
- stat = fs4.lstatSync(resolved);
14367
+ stat = fs5.lstatSync(resolved);
14053
14368
  } catch (error) {
14054
14369
  const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
14055
14370
  if (code === "ENOENT")
@@ -14057,14 +14372,14 @@ function removeSyntheticPath(worktree, syntheticPath) {
14057
14372
  throw error;
14058
14373
  }
14059
14374
  if (stat.isSymbolicLink()) {
14060
- fs4.unlinkSync(resolved);
14375
+ fs5.unlinkSync(resolved);
14061
14376
  return;
14062
14377
  }
14063
14378
  if (stat.isDirectory()) {
14064
- fs4.rmSync(resolved, { recursive: true, force: true });
14379
+ fs5.rmSync(resolved, { recursive: true, force: true });
14065
14380
  return;
14066
14381
  }
14067
- fs4.rmSync(resolved, { force: true });
14382
+ fs5.rmSync(resolved, { force: true });
14068
14383
  }
14069
14384
  function removeSyntheticPathsBeforeDiff(worktree) {
14070
14385
  if (worktree.syntheticPaths.length === 0)
@@ -14113,7 +14428,7 @@ function captureWorktreeDiff(setup, worktree, agent, patchPath) {
14113
14428
  const diffStat = runGitChecked(worktree.path, ["diff", "--cached", "--stat", setup.baseCommit]).trim();
14114
14429
  const patch = runGitChecked(worktree.path, ["diff", "--cached", setup.baseCommit]);
14115
14430
  const numstat = runGitChecked(worktree.path, ["diff", "--cached", "--numstat", setup.baseCommit]);
14116
- fs4.writeFileSync(patchPath, patch, "utf-8");
14431
+ fs5.writeFileSync(patchPath, patch, "utf-8");
14117
14432
  if (!patch.trim()) {
14118
14433
  return emptyDiff(worktree.index, agent, worktree.branch, patchPath);
14119
14434
  }
@@ -14131,7 +14446,7 @@ function captureWorktreeDiff(setup, worktree, agent, patchPath) {
14131
14446
  }
14132
14447
  function writeEmptyPatch(patchPath) {
14133
14448
  try {
14134
- fs4.writeFileSync(patchPath, "", "utf-8");
14449
+ fs5.writeFileSync(patchPath, "", "utf-8");
14135
14450
  } catch {}
14136
14451
  }
14137
14452
  function hasWorktreeChanges(diff) {
@@ -14139,7 +14454,7 @@ function hasWorktreeChanges(diff) {
14139
14454
  }
14140
14455
  function diffWorktrees(setup, agents, diffsDir) {
14141
14456
  try {
14142
- fs4.mkdirSync(diffsDir, { recursive: true });
14457
+ fs5.mkdirSync(diffsDir, { recursive: true });
14143
14458
  } catch {
14144
14459
  return [];
14145
14460
  }
@@ -14147,7 +14462,7 @@ function diffWorktrees(setup, agents, diffsDir) {
14147
14462
  for (let index = 0;index < setup.worktrees.length; index++) {
14148
14463
  const worktree = setup.worktrees[index];
14149
14464
  const agent = agents[index] ?? `task-${index + 1}`;
14150
- const patchPath = path4.join(diffsDir, `task-${index}-${safePatchAgentName(agent)}.patch`);
14465
+ const patchPath = path5.join(diffsDir, `task-${index}-${safePatchAgentName(agent)}.patch`);
14151
14466
  try {
14152
14467
  diffs.push(captureWorktreeDiff(setup, worktree, agent, patchPath));
14153
14468
  } catch {
@@ -14169,7 +14484,7 @@ function formatWorktreeDiffSummary(diffs) {
14169
14484
  }
14170
14485
  lines.push("");
14171
14486
  }
14172
- const patchesDir = path4.dirname(changed[0].patchPath);
14487
+ const patchesDir = path5.dirname(changed[0].patchPath);
14173
14488
  lines.push(`Full patches: ${patchesDir}`);
14174
14489
  return lines.join(`
14175
14490
  `).trimEnd();
@@ -14179,8 +14494,8 @@ var init_worktree_diff = __esm(() => {
14179
14494
  });
14180
14495
 
14181
14496
  // dist/builtin/workflows/src/runs/shared/worktree-post-create.ts
14182
- import * as fs5 from "node:fs";
14183
- import * as path5 from "node:path";
14497
+ import * as fs6 from "node:fs";
14498
+ import * as path6 from "node:path";
14184
14499
  function tracked(repo, relativePath) {
14185
14500
  const result = runGit(repo, ["ls-files", "--error-unmatch", "--", relativePath]);
14186
14501
  return result.status === 0;
@@ -14200,12 +14515,12 @@ function hasTrackedPathComponent(repo, relativePath) {
14200
14515
  return false;
14201
14516
  }
14202
14517
  function copyUntrackedFile(mainRoot, worktreeRoot, relativePath) {
14203
- const source = path5.join(mainRoot, relativePath);
14204
- const destination = path5.join(worktreeRoot, relativePath);
14205
- if (!fs5.existsSync(source) || fs5.existsSync(destination) || hasTrackedPathComponent(worktreeRoot, relativePath))
14518
+ const source = path6.join(mainRoot, relativePath);
14519
+ const destination = path6.join(worktreeRoot, relativePath);
14520
+ if (!fs6.existsSync(source) || fs6.existsSync(destination) || hasTrackedPathComponent(worktreeRoot, relativePath))
14206
14521
  return false;
14207
- fs5.mkdirSync(path5.dirname(destination), { recursive: true });
14208
- fs5.copyFileSync(source, destination);
14522
+ fs6.mkdirSync(path6.dirname(destination), { recursive: true });
14523
+ fs6.copyFileSync(source, destination);
14209
14524
  return true;
14210
14525
  }
14211
14526
  function propagateLocalSettings(mainRoot, worktreeRoot) {
@@ -14218,14 +14533,14 @@ function propagateLocalSettings(mainRoot, worktreeRoot) {
14218
14533
  return copied;
14219
14534
  }
14220
14535
  function populatedHooksDirectory(mainRoot) {
14221
- const husky = path5.join(mainRoot, ".husky");
14536
+ const husky = path6.join(mainRoot, ".husky");
14222
14537
  try {
14223
- if (fs5.statSync(husky).isDirectory())
14538
+ if (fs6.statSync(husky).isDirectory())
14224
14539
  return husky;
14225
14540
  } catch {}
14226
- const hooks = path5.join(mainRoot, ".git", "hooks");
14541
+ const hooks = path6.join(mainRoot, ".git", "hooks");
14227
14542
  try {
14228
- const populated = fs5.readdirSync(hooks, { withFileTypes: true }).some((entry) => entry.isFile() && !entry.name.endsWith(".sample"));
14543
+ const populated = fs6.readdirSync(hooks, { withFileTypes: true }).some((entry) => entry.isFile() && !entry.name.endsWith(".sample"));
14229
14544
  return populated ? hooks : undefined;
14230
14545
  } catch {
14231
14546
  return;
@@ -14236,7 +14551,7 @@ function configureSharedHooksPath(mainRoot) {
14236
14551
  if (desired === undefined)
14237
14552
  return;
14238
14553
  const current = runGitPlain(mainRoot, ["config", "--get", "core.hooksPath"]);
14239
- if (current.status === 0 && path5.resolve(mainRoot, current.stdout.trim()) === desired)
14554
+ if (current.status === 0 && path6.resolve(mainRoot, current.stdout.trim()) === desired)
14240
14555
  return;
14241
14556
  const configured = runGitPlain(mainRoot, ["config", "core.hooksPath", desired]);
14242
14557
  if (configured.status !== 0) {
@@ -14245,10 +14560,10 @@ function configureSharedHooksPath(mainRoot) {
14245
14560
  }
14246
14561
  function normalizeSymlinkDirectory(value2) {
14247
14562
  const trimmed = value2.trim();
14248
- if (!trimmed || path5.isAbsolute(trimmed))
14563
+ if (!trimmed || path6.isAbsolute(trimmed))
14249
14564
  return;
14250
- const normalized = path5.normalize(trimmed);
14251
- if (normalized === "." || normalized === ".." || normalized.startsWith(`..${path5.sep}`))
14565
+ const normalized = path6.normalize(trimmed);
14566
+ if (normalized === "." || normalized === ".." || normalized.startsWith(`..${path6.sep}`))
14252
14567
  return;
14253
14568
  return normalized;
14254
14569
  }
@@ -14258,20 +14573,20 @@ function symlinkConfiguredDirectories(mainRoot, worktreeRoot, directories) {
14258
14573
  const relativePath = normalizeSymlinkDirectory(rawDirectory);
14259
14574
  if (relativePath === undefined || synthetic.includes(relativePath))
14260
14575
  continue;
14261
- const source = path5.join(mainRoot, relativePath);
14262
- const destination = path5.join(worktreeRoot, relativePath);
14263
- if (!fs5.existsSync(source) || fs5.existsSync(destination) || hasTrackedPathComponent(worktreeRoot, relativePath))
14576
+ const source = path6.join(mainRoot, relativePath);
14577
+ const destination = path6.join(worktreeRoot, relativePath);
14578
+ if (!fs6.existsSync(source) || fs6.existsSync(destination) || hasTrackedPathComponent(worktreeRoot, relativePath))
14264
14579
  continue;
14265
- fs5.mkdirSync(path5.dirname(destination), { recursive: true });
14580
+ fs6.mkdirSync(path6.dirname(destination), { recursive: true });
14266
14581
  try {
14267
- fs5.symlinkSync(source, destination, process.platform === "win32" ? "junction" : undefined);
14582
+ fs6.symlinkSync(source, destination, process.platform === "win32" ? "junction" : undefined);
14268
14583
  synthetic.push(relativePath);
14269
14584
  } catch {}
14270
14585
  }
14271
14586
  return synthetic;
14272
14587
  }
14273
14588
  function copyWorktreeIncludes(mainRoot, worktreeRoot) {
14274
- if (!fs5.existsSync(path5.join(mainRoot, ".worktreeinclude")))
14589
+ if (!fs6.existsSync(path6.join(mainRoot, ".worktreeinclude")))
14275
14590
  return;
14276
14591
  const ignored = runGit(mainRoot, ["ls-files", "--others", "--ignored", "--exclude-standard"]);
14277
14592
  const included = runGit(mainRoot, ["ls-files", "--others", "--ignored", "--exclude-from=.worktreeinclude"]);
@@ -14281,13 +14596,13 @@ function copyWorktreeIncludes(mainRoot, worktreeRoot) {
14281
14596
  for (const relativePath of included.stdout.split(/\r?\n/).filter(Boolean)) {
14282
14597
  if (!ignoredPaths.has(relativePath) || hasTrackedPathComponent(worktreeRoot, relativePath))
14283
14598
  continue;
14284
- const source = path5.join(mainRoot, relativePath);
14285
- const destination = path5.join(worktreeRoot, relativePath);
14599
+ const source = path6.join(mainRoot, relativePath);
14600
+ const destination = path6.join(worktreeRoot, relativePath);
14286
14601
  try {
14287
- if (!fs5.statSync(source).isFile() || fs5.existsSync(destination))
14602
+ if (!fs6.statSync(source).isFile() || fs6.existsSync(destination))
14288
14603
  continue;
14289
- fs5.mkdirSync(path5.dirname(destination), { recursive: true });
14290
- fs5.copyFileSync(source, destination);
14604
+ fs6.mkdirSync(path6.dirname(destination), { recursive: true });
14605
+ fs6.copyFileSync(source, destination);
14291
14606
  } catch {}
14292
14607
  }
14293
14608
  }
@@ -14304,9 +14619,9 @@ var init_worktree_post_create = __esm(() => {
14304
14619
 
14305
14620
  // dist/builtin/workflows/src/runs/shared/worktree-setup.ts
14306
14621
  import { spawnSync as spawnSync2 } from "node:child_process";
14307
- import * as fs6 from "node:fs";
14622
+ import * as fs7 from "node:fs";
14308
14623
  import * as os from "node:os";
14309
- import * as path6 from "node:path";
14624
+ import * as path7 from "node:path";
14310
14625
  import { createChildProcessEnvironment as createChildProcessEnvironment2 } from "@bastani/atomic";
14311
14626
  function originDefaultBranch(mainRoot) {
14312
14627
  const symbolic = runGit(mainRoot, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"]);
@@ -14326,7 +14641,7 @@ function originDefaultBranch(mainRoot) {
14326
14641
  }
14327
14642
  function resolveRepoState(cwd, explicitBaseBranch) {
14328
14643
  const cwdRelative = resolveRepoCwdRelative(cwd);
14329
- const toplevel = path6.resolve(runGitChecked(cwd, ["rev-parse", "--show-toplevel"]).trim());
14644
+ const toplevel = path7.resolve(runGitChecked(cwd, ["rev-parse", "--show-toplevel"]).trim());
14330
14645
  const mainRoot = findCanonicalGitRoot(toplevel);
14331
14646
  if (mainRoot === undefined)
14332
14647
  throw new Error(`unable to resolve canonical main Git repository root from ${toplevel}`);
@@ -14339,9 +14654,9 @@ function resolveRepoState(cwd, explicitBaseBranch) {
14339
14654
  return { toplevel, mainRoot, cwdRelative, baseCommit, baseRef };
14340
14655
  }
14341
14656
  function normalizeComparableCwd(cwd) {
14342
- const resolved = path6.resolve(cwd);
14657
+ const resolved = path7.resolve(cwd);
14343
14658
  try {
14344
- return fs6.realpathSync(resolved);
14659
+ return fs7.realpathSync(resolved);
14345
14660
  } catch {
14346
14661
  return resolved;
14347
14662
  }
@@ -14352,7 +14667,7 @@ function findWorktreeTaskCwdConflict(tasks, sharedCwd) {
14352
14667
  const task = tasks[index];
14353
14668
  if (!task.cwd)
14354
14669
  continue;
14355
- const taskCwd = path6.isAbsolute(task.cwd) ? task.cwd : path6.resolve(sharedCwd, task.cwd);
14670
+ const taskCwd = path7.isAbsolute(task.cwd) ? task.cwd : path7.resolve(sharedCwd, task.cwd);
14356
14671
  if (normalizeComparableCwd(taskCwd) === normalizedSharedCwd)
14357
14672
  continue;
14358
14673
  return { index, agent: task.agent, cwd: task.cwd };
@@ -14369,12 +14684,12 @@ function buildWorktreeBranch(runId, index) {
14369
14684
  return `worktree-${flattenedWorktreeName(runId, index)}`;
14370
14685
  }
14371
14686
  function buildWorktreePath(mainRoot, runId, index) {
14372
- return path6.join(mainRoot, ".atomic", "worktrees", flattenedWorktreeName(runId, index));
14687
+ return path7.join(mainRoot, ".atomic", "worktrees", flattenedWorktreeName(runId, index));
14373
14688
  }
14374
14689
  function ensureWorktreeIgnore(mainRoot) {
14375
- const worktreesRoot = path6.join(mainRoot, ".atomic", "worktrees");
14376
- fs6.mkdirSync(worktreesRoot, { recursive: true });
14377
- fs6.writeFileSync(path6.join(worktreesRoot, ".gitignore"), `*
14690
+ const worktreesRoot = path7.join(mainRoot, ".atomic", "worktrees");
14691
+ fs7.mkdirSync(worktreesRoot, { recursive: true });
14692
+ fs7.writeFileSync(path7.join(worktreesRoot, ".gitignore"), `*
14378
14693
  `, "utf8");
14379
14694
  }
14380
14695
  function resolveRepoCwdRelative(cwd) {
@@ -14383,7 +14698,7 @@ function resolveRepoCwdRelative(cwd) {
14383
14698
  throw new Error("worktree isolation requires a git repository");
14384
14699
  }
14385
14700
  const rawPrefix = runGitChecked(cwd, ["rev-parse", "--show-prefix"]).trim();
14386
- const normalizedPrefix = rawPrefix ? path6.normalize(rawPrefix.replace(/[\\/]+$/, "")) : "";
14701
+ const normalizedPrefix = rawPrefix ? path7.normalize(rawPrefix.replace(/[\\/]+$/, "")) : "";
14387
14702
  return normalizedPrefix === "." ? "" : normalizedPrefix;
14388
14703
  }
14389
14704
  function parseHookTimeout(timeoutMs) {
@@ -14401,19 +14716,19 @@ function resolveWorktreeSetupHook(repoRoot, config) {
14401
14716
  if (!hookPath) {
14402
14717
  throw new Error("worktree setup hook path cannot be empty");
14403
14718
  }
14404
- const expandedHookPath = hookPath.startsWith("~/") ? path6.join(os.homedir(), hookPath.slice(2)) : hookPath;
14719
+ const expandedHookPath = hookPath.startsWith("~/") ? path7.join(os.homedir(), hookPath.slice(2)) : hookPath;
14405
14720
  let resolvedPath;
14406
- if (path6.isAbsolute(expandedHookPath)) {
14721
+ if (path7.isAbsolute(expandedHookPath)) {
14407
14722
  resolvedPath = expandedHookPath;
14408
14723
  } else if (expandedHookPath.includes("/") || expandedHookPath.includes("\\")) {
14409
- resolvedPath = path6.resolve(repoRoot, expandedHookPath);
14724
+ resolvedPath = path7.resolve(repoRoot, expandedHookPath);
14410
14725
  } else {
14411
14726
  throw new Error("worktree setup hook must be an absolute path or a repo-relative path");
14412
14727
  }
14413
- if (!fs6.existsSync(resolvedPath)) {
14728
+ if (!fs7.existsSync(resolvedPath)) {
14414
14729
  throw new Error(`worktree setup hook not found: ${resolvedPath}`);
14415
14730
  }
14416
- if (fs6.statSync(resolvedPath).isDirectory()) {
14731
+ if (fs7.statSync(resolvedPath).isDirectory()) {
14417
14732
  throw new Error(`worktree setup hook must be a file, got directory: ${resolvedPath}`);
14418
14733
  }
14419
14734
  return {
@@ -14425,17 +14740,17 @@ function normalizeSyntheticPath(worktreePath, rawPath) {
14425
14740
  const trimmed = rawPath.trim();
14426
14741
  if (!trimmed)
14427
14742
  throw new Error("synthetic path cannot be empty");
14428
- if (path6.isAbsolute(trimmed))
14743
+ if (path7.isAbsolute(trimmed))
14429
14744
  throw new Error(`synthetic path must be relative: ${rawPath}`);
14430
- const resolved = path6.resolve(worktreePath, trimmed);
14431
- const relative = path6.relative(worktreePath, resolved);
14745
+ const resolved = path7.resolve(worktreePath, trimmed);
14746
+ const relative = path7.relative(worktreePath, resolved);
14432
14747
  if (!relative || relative === ".") {
14433
14748
  throw new Error(`synthetic path cannot target the worktree root: ${rawPath}`);
14434
14749
  }
14435
- if (relative === ".." || relative.startsWith(`..${path6.sep}`) || path6.isAbsolute(relative)) {
14750
+ if (relative === ".." || relative.startsWith(`..${path7.sep}`) || path7.isAbsolute(relative)) {
14436
14751
  throw new Error(`synthetic path escapes the worktree root: ${rawPath}`);
14437
14752
  }
14438
- return path6.normalize(relative);
14753
+ return path7.normalize(relative);
14439
14754
  }
14440
14755
  function hasTrackedEntries(worktreePath, relativePath) {
14441
14756
  const result = runGit(worktreePath, ["ls-files", "--", relativePath]);
@@ -14507,16 +14822,16 @@ function waitForGitLockRelease() {
14507
14822
  }
14508
14823
  function unlinkWorktreeLinks(worktreePath, relativePaths) {
14509
14824
  for (const relativePath of new Set(relativePaths)) {
14510
- const target = path6.resolve(worktreePath, relativePath);
14511
- const relative = path6.relative(worktreePath, target);
14512
- if (!relative || relative === ".." || relative.startsWith(`..${path6.sep}`) || path6.isAbsolute(relative))
14825
+ const target = path7.resolve(worktreePath, relativePath);
14826
+ const relative = path7.relative(worktreePath, target);
14827
+ if (!relative || relative === ".." || relative.startsWith(`..${path7.sep}`) || path7.isAbsolute(relative))
14513
14828
  continue;
14514
14829
  let current = worktreePath;
14515
- for (const component of relative.split(path6.sep)) {
14516
- current = path6.join(current, component);
14830
+ for (const component of relative.split(path7.sep)) {
14831
+ current = path7.join(current, component);
14517
14832
  let stat;
14518
14833
  try {
14519
- stat = fs6.lstatSync(current);
14834
+ stat = fs7.lstatSync(current);
14520
14835
  } catch (error) {
14521
14836
  const code = error.code;
14522
14837
  if (code === "ENOENT" || code === "ENOTDIR")
@@ -14526,7 +14841,7 @@ function unlinkWorktreeLinks(worktreePath, relativePaths) {
14526
14841
  if (!stat.isSymbolicLink())
14527
14842
  continue;
14528
14843
  if (current === target)
14529
- fs6.unlinkSync(current);
14844
+ fs7.unlinkSync(current);
14530
14845
  break;
14531
14846
  }
14532
14847
  }
@@ -14554,7 +14869,7 @@ function createSingleWorktree(mainRoot, cwdRelative, runId, index, baseRef, base
14554
14869
  cleanupSingleWorktree(mainRoot, { path: worktreePath, branch }, []);
14555
14870
  throw new Error(message);
14556
14871
  }
14557
- const agentCwd = cwdRelative ? path6.join(worktreePath, cwdRelative) : worktreePath;
14872
+ const agentCwd = cwdRelative ? path7.join(worktreePath, cwdRelative) : worktreePath;
14558
14873
  const syntheticPaths = [];
14559
14874
  try {
14560
14875
  syntheticPaths.push(...performPostCreationSetup(mainRoot, worktreePath, symlinkDirectories));
@@ -14623,8 +14938,8 @@ var init_worktree = __esm(() => {
14623
14938
  });
14624
14939
 
14625
14940
  // dist/builtin/workflows/src/runs/shared/worktree-cwd.ts
14626
- import { lstatSync as lstatSync5, realpathSync as realpathSync4 } from "node:fs";
14627
- import { dirname as dirname8, isAbsolute as isAbsolute6, relative as relative4, resolve as resolve6, sep as sep5 } from "node:path";
14941
+ import { lstatSync as lstatSync6, realpathSync as realpathSync4 } from "node:fs";
14942
+ import { dirname as dirname9, isAbsolute as isAbsolute6, relative as relative4, resolve as resolve7, sep as sep5 } from "node:path";
14628
14943
  function relativePathWithin(root, candidate) {
14629
14944
  const path = relative4(root, candidate);
14630
14945
  return path === ".." || path.startsWith(`..${sep5}`) || isAbsolute6(path) ? undefined : path;
@@ -14643,14 +14958,14 @@ function resolveWorktreeStageCwd(cwd, setup) {
14643
14958
  if (cwd === undefined || cwd.length === 0)
14644
14959
  return;
14645
14960
  const absoluteInput = isAbsolute6(cwd);
14646
- const candidate = absoluteInput ? cwd : resolve6(setup.cwd, cwd);
14961
+ const candidate = absoluteInput ? cwd : resolve7(setup.cwd, cwd);
14647
14962
  if (!absoluteInput && relativePathWithin(setup.worktreeRoot, candidate) === undefined) {
14648
14963
  throw new Error(`atomic-workflows: relative cwd ${cwd} escapes gitWorktreeDir ${setup.worktreeRoot}; choose a cwd inside the worktree.`);
14649
14964
  }
14650
14965
  const candidateRealPath = existingRealPath(candidate, "cwd");
14651
14966
  const worktreeRelative = relativePathWithin(worktreeRoot, candidateRealPath);
14652
14967
  if (worktreeRelative !== undefined)
14653
- return resolve6(setup.worktreeRoot, worktreeRelative);
14968
+ return resolve7(setup.worktreeRoot, worktreeRelative);
14654
14969
  if (!isAbsolute6(cwd)) {
14655
14970
  throw new Error(`atomic-workflows: relative cwd ${cwd} escapes gitWorktreeDir ${setup.worktreeRoot}; choose a path that remains inside the selected worktree.`);
14656
14971
  }
@@ -14659,9 +14974,9 @@ function resolveWorktreeStageCwd(cwd, setup) {
14659
14974
  }
14660
14975
  const repositoryRelative = absoluteInput ? relativePathWithin(repositoryRoot, candidateRealPath) : undefined;
14661
14976
  if (repositoryRelative === undefined) {
14662
- 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.`);
14977
+ 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.`);
14663
14978
  }
14664
- const mappedCwd = resolve6(setup.worktreeRoot, repositoryRelative);
14979
+ const mappedCwd = resolve7(setup.worktreeRoot, repositoryRelative);
14665
14980
  const mappedRealPath = existingRealPath(mappedCwd, "remapped worktree cwd");
14666
14981
  if (relativePathWithin(worktreeRoot, mappedRealPath) === undefined) {
14667
14982
  throw new Error(`atomic-workflows: cwd ${cwd} remaps through a path that resolves outside gitWorktreeDir ${setup.worktreeRoot}; remove the escaping symlink or omit cwd.`);
@@ -14672,7 +14987,7 @@ var init_worktree_cwd = () => {};
14672
14987
 
14673
14988
  // dist/builtin/workflows/src/runs/foreground/executor-task-prompts.ts
14674
14989
  import { existsSync as existsSync3 } from "node:fs";
14675
- import { isAbsolute as isAbsolute7, resolve as resolve7 } from "node:path";
14990
+ import { isAbsolute as isAbsolute7, resolve as resolve8 } from "node:path";
14676
14991
  function normalizeTaskContexts(previous) {
14677
14992
  if (previous === undefined)
14678
14993
  return [];
@@ -14725,7 +15040,7 @@ function taskPrevious(options) {
14725
15040
  function resolveWorkflowPath(filePath, baseDir) {
14726
15041
  if (isAbsolute7(filePath))
14727
15042
  return filePath;
14728
- return resolve7(baseDir ?? process.cwd(), filePath);
15043
+ return resolve8(baseDir ?? process.cwd(), filePath);
14729
15044
  }
14730
15045
  function taskBaseDir(options) {
14731
15046
  if (typeof options.chainDir === "string" && options.chainDir.trim().length > 0) {
@@ -14880,7 +15195,7 @@ function taskWithSharedDefaults(taskOptions, options) {
14880
15195
  var init_executor_task_prompts = () => {};
14881
15196
 
14882
15197
  // dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts
14883
- import { isAbsolute as isAbsolute8, join as join11, resolve as resolve8 } from "node:path";
15198
+ import { isAbsolute as isAbsolute8, join as join12, resolve as resolve9 } from "node:path";
14884
15199
  import { CONFIG_DIR_NAME } from "@bastani/atomic";
14885
15200
  function positiveConcurrency(value2) {
14886
15201
  if (typeof value2 !== "number" || !Number.isFinite(value2) || value2 < 1)
@@ -15033,10 +15348,10 @@ function workflowInvocationMetadata(inputDefaults, workflowInvocationCwd, cache,
15033
15348
  function resolvedTaskCwd(cwd, workflowInvocationCwd) {
15034
15349
  if (cwd === undefined)
15035
15350
  return;
15036
- return isAbsolute8(cwd) ? cwd : resolve8(workflowInvocationCwd, cwd);
15351
+ return isAbsolute8(cwd) ? cwd : resolve9(workflowInvocationCwd, cwd);
15037
15352
  }
15038
15353
  function taskWorktreeOutputsRoot(runId) {
15039
- return join11(workflowArtifactRunPath(runId), "task-outputs");
15354
+ return join12(workflowArtifactRunPath(runId), "task-outputs");
15040
15355
  }
15041
15356
  function prepareTaskWorktrees(tasks, options, runId, scope, workflowInvocationCwd = process.cwd(), symlinkDirectories) {
15042
15357
  if (options.worktree !== true && !tasks.some((task) => task.worktree === true)) {
@@ -15046,7 +15361,7 @@ function prepareTaskWorktrees(tasks, options, runId, scope, workflowInvocationCw
15046
15361
  throw new Error("atomic-workflows: worktree and gitWorktreeDir are mutually exclusive; use gitWorktreeDir for a reusable worktree or worktree:true for temporary isolated worktrees.");
15047
15362
  }
15048
15363
  const explicitCwd = tasks.find((task) => typeof task.cwd === "string")?.cwd;
15049
- const sharedCwd = explicitCwd === undefined ? workflowInvocationCwd : isAbsolute8(explicitCwd) ? explicitCwd : resolve8(workflowInvocationCwd, explicitCwd);
15364
+ const sharedCwd = explicitCwd === undefined ? workflowInvocationCwd : isAbsolute8(explicitCwd) ? explicitCwd : resolve9(workflowInvocationCwd, explicitCwd);
15050
15365
  const conflict = findWorktreeTaskCwdConflict(tasks.map((task) => ({ agent: task.name, cwd: resolvedTaskCwd(task.cwd, workflowInvocationCwd) })), sharedCwd);
15051
15366
  if (conflict !== undefined)
15052
15367
  throw new Error(formatWorktreeTaskCwdConflict(conflict, sharedCwd));
@@ -15061,9 +15376,9 @@ function prepareTaskWorktrees(tasks, options, runId, scope, workflowInvocationCw
15061
15376
  tasks: tasks.map((task, index) => ({ ...task, cwd: setup.worktrees[index].agentCwd })),
15062
15377
  setup,
15063
15378
  agents,
15064
- diffsDir: join11(setup.cwd, CONFIG_DIR_NAME, "workflows", "worktree-diffs", runId, scope),
15379
+ diffsDir: join12(setup.cwd, CONFIG_DIR_NAME, "workflows", "worktree-diffs", runId, scope),
15065
15380
  outputIsolations: tasks.map((_, index) => ({
15066
- baseDir: join11(trustedRoot, runId, scope, String(index)),
15381
+ baseDir: join12(trustedRoot, runId, scope, String(index)),
15067
15382
  trustedRoot
15068
15383
  }))
15069
15384
  };
@@ -16790,8 +17105,12 @@ function createStageControlRegistry() {
16790
17105
  }
16791
17106
  await disposeEntries(entries);
16792
17107
  },
16793
- async releaseIdle(runId) {
16794
- for (const [stageId, entry] of [..._byRun.get(runId) ?? []]) {
17108
+ async releaseIdle(runId, only) {
17109
+ const runMap = _byRun.get(runId);
17110
+ const entries = only === undefined ? [...runMap ?? []] : [[only, runMap?.get(only)]];
17111
+ for (const [stageId, entry] of entries) {
17112
+ if (entry === undefined)
17113
+ continue;
16795
17114
  try {
16796
17115
  await releaseIdleEntry(runId, stageId, entry);
16797
17116
  } catch (error) {
@@ -16852,7 +17171,7 @@ var init_possible_stages = __esm(() => {
16852
17171
 
16853
17172
  // dist/builtin/workflows/src/workers/admission-host.ts
16854
17173
  import { execFile, execFileSync } from "node:child_process";
16855
- import { readdirSync as readdirSync2, readFileSync as readFileSync2 } from "node:fs";
17174
+ import { readdirSync as readdirSync4, readFileSync as readFileSync4 } from "node:fs";
16856
17175
  import { readFile } from "node:fs/promises";
16857
17176
  import { hostname } from "node:os";
16858
17177
  function admissionBootIdentity() {
@@ -16878,13 +17197,32 @@ function admissionProcessStopped(pid) {
16878
17197
  return error.code === "ESRCH";
16879
17198
  }
16880
17199
  }
17200
+ function workerProcessExited(pid, final = false) {
17201
+ if (admissionProcessStopped(pid))
17202
+ return true;
17203
+ if (process.platform === "linux") {
17204
+ try {
17205
+ const stat = readFileSync4(`/proc/${pid}/stat`, "utf8");
17206
+ return stat.slice(stat.lastIndexOf(")") + 2).startsWith("Z");
17207
+ } catch {
17208
+ return admissionProcessStopped(pid);
17209
+ }
17210
+ }
17211
+ if (!final)
17212
+ return false;
17213
+ try {
17214
+ return execFileSync("ps", ["-o", "stat=", "-p", String(pid)], { encoding: "utf8", timeout: 5000 }).trim().startsWith("Z");
17215
+ } catch {
17216
+ return admissionProcessStopped(pid);
17217
+ }
17218
+ }
16881
17219
  function scanProcessGroup(group) {
16882
17220
  const members = new Map;
16883
17221
  if (process.platform === "linux") {
16884
17222
  let complete = true;
16885
17223
  let entries;
16886
17224
  try {
16887
- entries = readdirSync2("/proc");
17225
+ entries = readdirSync4("/proc");
16888
17226
  } catch {
16889
17227
  return { members, complete: false };
16890
17228
  }
@@ -16893,7 +17231,7 @@ function scanProcessGroup(group) {
16893
17231
  continue;
16894
17232
  let stat;
16895
17233
  try {
16896
- stat = readFileSync2(`/proc/${entry}/stat`, "utf8");
17234
+ stat = readFileSync4(`/proc/${entry}/stat`, "utf8");
16897
17235
  } catch {
16898
17236
  complete = false;
16899
17237
  continue;
@@ -53632,10 +53970,10 @@ var init_verification_prompts = __esm(() => {
53632
53970
  // dist/builtin/workflows/builtin/pattern-artifact-root.ts
53633
53971
  import { randomUUID as randomUUID8 } from "node:crypto";
53634
53972
  import { mkdir as mkdir2 } from "node:fs/promises";
53635
- import { join as join12 } from "node:path";
53973
+ import { join as join13 } from "node:path";
53636
53974
  async function stableArtifactRoot(ctx, workflowName) {
53637
53975
  const cwd = ctx.cwd ?? process.cwd();
53638
- const artifactDir = await ctx.tool("artifact-root", { workflow: workflowName }, async () => join12(cwd, ".atomic", "workflows", "runs", `${workflowName}-${randomUUID8()}`));
53976
+ const artifactDir = await ctx.tool("artifact-root", { workflow: workflowName }, async () => join13(cwd, ".atomic", "workflows", "runs", `${workflowName}-${randomUUID8()}`));
53639
53977
  await mkdir2(artifactDir, { recursive: true });
53640
53978
  return artifactDir;
53641
53979
  }
@@ -53675,7 +54013,7 @@ function fold_usage(results) {
53675
54013
 
53676
54014
  // dist/builtin/workflows/builtin/adversarial-verification-runner.ts
53677
54015
  import { readFile as readFile2, writeFile } from "node:fs/promises";
53678
- import { join as join13 } from "node:path";
54016
+ import { join as join14 } from "node:path";
53679
54017
  import { Type as Type4 } from "typebox";
53680
54018
  function isRecord3(value2) {
53681
54019
  return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
@@ -53770,8 +54108,8 @@ async function writeInvalid(path, stage) {
53770
54108
  }
53771
54109
  async function runAdversarialVerification(ctx) {
53772
54110
  const root = await stableArtifactRoot(ctx, "adversarial-verification");
53773
- const criteriaPath = join13(root, "criteria.md");
53774
- const candidatePath = join13(root, "candidate.md");
54111
+ const criteriaPath = join14(root, "criteria.md");
54112
+ const candidatePath = join14(root, "candidate.md");
53775
54113
  const resolvedCriteria = resolveCriteria(ctx.inputs.criteria);
53776
54114
  await writeFile(criteriaPath, renderCriteriaMarkdown(resolvedCriteria));
53777
54115
  await ctx.task("worker", { prompt: renderWorkerPrompt(ctx.inputs.task), context: "fresh", output: candidatePath, outputMode: "file-only" });
@@ -53803,7 +54141,7 @@ async function runAdversarialVerification(ctx) {
53803
54141
  criterion,
53804
54142
  verifierIndex: index + 1,
53805
54143
  name,
53806
- artifactPath: join13(root, `verification-${round}-${criterion.id}-${index + 1}.json`)
54144
+ artifactPath: join14(root, `verification-${round}-${criterion.id}-${index + 1}.json`)
53807
54145
  });
53808
54146
  }
53809
54147
  }
@@ -53856,8 +54194,8 @@ async function runAdversarialVerification(ctx) {
53856
54194
  const decision = decide_verification(roundResult, { acceptMean, quorumFraction: QUORUM_FRACTION });
53857
54195
  finalDecision = decision;
53858
54196
  finalMean = meanScore(scoreReports);
53859
- scoreTablePath = join13(root, `verification-summary-${round}.json`);
53860
- reviewReportPath = join13(root, `review-${round}.json`);
54197
+ scoreTablePath = join14(root, `verification-summary-${round}.json`);
54198
+ reviewReportPath = join14(root, `review-${round}.json`);
53861
54199
  const summary = {
53862
54200
  scores: scoreReports,
53863
54201
  mean: finalMean,
@@ -54038,7 +54376,7 @@ var GROUNDED_REPORTING2 = "Before reporting progress, audit each claim against a
54038
54376
 
54039
54377
  // dist/builtin/workflows/builtin/classify-and-act-runner.ts
54040
54378
  import { writeFile as writeFile2 } from "node:fs/promises";
54041
- import { join as join14 } from "node:path";
54379
+ import { join as join15 } from "node:path";
54042
54380
  import { Type as Type6 } from "typebox";
54043
54381
  function safeName(value2) {
54044
54382
  const normalized = value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
@@ -54078,7 +54416,7 @@ async function runClassifyAndAct(ctx) {
54078
54416
  const needsFallback = exactCategory === undefined || confidence < ctx.inputs.confidence_threshold;
54079
54417
  const fallback = needsFallback ? await resolveFallbackCategory(ctx, exactCategory) : undefined;
54080
54418
  const category = fallback?.category ?? exactCategory;
54081
- const classificationPath = join14(artifactDir, "classification.json");
54419
+ const classificationPath = join15(artifactDir, "classification.json");
54082
54420
  await writeFile2(classificationPath, JSON.stringify({
54083
54421
  proposed_category: proposedCategory,
54084
54422
  selected_category: category,
@@ -54088,7 +54426,7 @@ async function runClassifyAndAct(ctx) {
54088
54426
  fallback_used: needsFallback,
54089
54427
  fallback_mode: fallback?.fallbackMode ?? "none"
54090
54428
  }, null, 2));
54091
- const actionPath = join14(artifactDir, `action-${safeName(category)}.md`);
54429
+ const actionPath = join15(artifactDir, `action-${safeName(category)}.md`);
54092
54430
  const action = await ctx.task(`action-${safeName(category)}`, {
54093
54431
  tools: actionTools(category),
54094
54432
  prompt: actionPrompt({ prompt: ctx.inputs.prompt, category, classificationPath }),
@@ -54228,7 +54566,7 @@ var GROUNDED_REPORTING3 = "Before reporting progress, audit each claim against a
54228
54566
 
54229
54567
  // dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts
54230
54568
  import { writeFile as writeFile3 } from "node:fs/promises";
54231
- import { join as join15 } from "node:path";
54569
+ import { join as join16 } from "node:path";
54232
54570
  import { Type as Type8 } from "typebox";
54233
54571
  function safeName2(value2, index) {
54234
54572
  const normalized = value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
@@ -54259,9 +54597,9 @@ async function runFanOutAndSynthesize(ctx) {
54259
54597
  context: "fresh"
54260
54598
  });
54261
54599
  const partitions = parsedPartitions(plan.structured, ctx.inputs.max_branches, ctx.inputs.prompt);
54262
- const partitionPath = join15(artifactDir, "partition-plan.json");
54600
+ const partitionPath = join16(artifactDir, "partition-plan.json");
54263
54601
  await writeFile3(partitionPath, JSON.stringify({ task: ctx.inputs.prompt, partitions }, null, 2));
54264
- const branchPaths = partitions.map((partition, index) => join15(artifactDir, `branch-${safeName2(partition.label, index)}.md`));
54602
+ const branchPaths = partitions.map((partition, index) => join16(artifactDir, `branch-${safeName2(partition.label, index)}.md`));
54265
54603
  const steps = partitions.map((partition, index) => ({
54266
54604
  name: `branch-${safeName2(partition.label, index)}`,
54267
54605
  prompt: branchPrompt({ prompt: ctx.inputs.prompt, ...partition }),
@@ -54271,13 +54609,13 @@ async function runFanOutAndSynthesize(ctx) {
54271
54609
  outputMode: "file-only"
54272
54610
  }));
54273
54611
  await ctx.parallel(steps, { concurrency: Math.min(ctx.inputs.max_concurrency, partitions.length), failFast: false });
54274
- const manifestPath = join15(artifactDir, "manifest.json");
54612
+ const manifestPath = join16(artifactDir, "manifest.json");
54275
54613
  await writeFile3(manifestPath, JSON.stringify({
54276
54614
  task: ctx.inputs.prompt,
54277
54615
  partition_plan: partitionPath,
54278
54616
  branches: partitions.map((partition, index) => ({ ...partition, artifact_path: branchPaths[index] }))
54279
54617
  }, null, 2));
54280
- const synthesisPath = join15(artifactDir, "synthesis.md");
54618
+ const synthesisPath = join16(artifactDir, "synthesis.md");
54281
54619
  const synthesis = await ctx.task("synthesize", {
54282
54620
  prompt: synthesisPrompt(ctx.inputs.prompt, manifestPath),
54283
54621
  context: "fresh",
@@ -54442,7 +54780,7 @@ var init_generate_and_filter_prompts = __esm(() => {
54442
54780
 
54443
54781
  // dist/builtin/workflows/builtin/generate-and-filter-runner.ts
54444
54782
  import { readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
54445
- import { join as join16 } from "node:path";
54783
+ import { join as join17 } from "node:path";
54446
54784
  import { Type as Type10 } from "typebox";
54447
54785
  function isRecord4(value2) {
54448
54786
  return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
@@ -54461,7 +54799,7 @@ function judgeDecision(value2) {
54461
54799
  }
54462
54800
  async function runGenerateAndFilter(ctx) {
54463
54801
  const root = await stableArtifactRoot(ctx, "generate-and-filter");
54464
- const candidatePaths = Array.from({ length: ctx.inputs.num_candidates }, (_, index) => join16(root, `candidate-${index + 1}.md`));
54802
+ const candidatePaths = Array.from({ length: ctx.inputs.num_candidates }, (_, index) => join17(root, `candidate-${index + 1}.md`));
54465
54803
  const shortlistLimit = Math.min(ctx.inputs.shortlist_size, ctx.inputs.num_candidates);
54466
54804
  await ctx.parallel(candidatePaths.map((path, index) => ({
54467
54805
  name: `generate-${index + 1}`,
@@ -54470,9 +54808,9 @@ async function runGenerateAndFilter(ctx) {
54470
54808
  output: path,
54471
54809
  outputMode: "file-only"
54472
54810
  })), { concurrency: ctx.inputs.max_concurrency, failFast: false });
54473
- const manifestPath = join16(root, "manifest.json");
54811
+ const manifestPath = join17(root, "manifest.json");
54474
54812
  await writeFile4(manifestPath, JSON.stringify({ task: ctx.inputs.prompt, candidate_artifact_paths: candidatePaths }, null, 2));
54475
- const filterPath = join16(root, "filter.json");
54813
+ const filterPath = join17(root, "filter.json");
54476
54814
  const filtered = await ctx.task("dedupe-and-filter", {
54477
54815
  prompt: renderFilterPrompt(ctx.inputs.prompt, candidatePaths, shortlistLimit),
54478
54816
  context: "fresh",
@@ -54489,7 +54827,7 @@ async function runGenerateAndFilter(ctx) {
54489
54827
  let judgePath = null;
54490
54828
  let decisionPath = filterPath;
54491
54829
  if (ctx.inputs.use_judge) {
54492
- judgePath = join16(root, "judge.json");
54830
+ judgePath = join17(root, "judge.json");
54493
54831
  const judgeCandidates = await Promise.all(shortlist.map(async (path) => ({ path, body: await readFile3(path, "utf8") })));
54494
54832
  const judgePrompt = renderJudgePrompt(ctx.inputs.prompt, filterPath, shortlistLimit, judgeCandidates);
54495
54833
  const judged = await ctx.task("judge", {
@@ -54505,7 +54843,7 @@ async function runGenerateAndFilter(ctx) {
54505
54843
  shortlist = judgedShortlist.length > 0 ? judgedShortlist : shortlist;
54506
54844
  decisionPath = judgePath;
54507
54845
  }
54508
- const finalPath = join16(root, "shortlist.md");
54846
+ const finalPath = join17(root, "shortlist.md");
54509
54847
  const finalShortlist = await ctx.task("final-shortlist", {
54510
54848
  prompt: renderFinalShortlistPrompt(ctx.inputs.prompt, decisionPath),
54511
54849
  context: "fresh",
@@ -55034,7 +55372,7 @@ var init_review_convergence = __esm(() => {
55034
55372
 
55035
55373
  // dist/builtin/workflows/builtin/goal-artifacts.ts
55036
55374
  import { writeFile as writeFile5 } from "node:fs/promises";
55037
- import { join as join17 } from "node:path";
55375
+ import { join as join18 } from "node:path";
55038
55376
  function artifactSafeName(value2) {
55039
55377
  const safe = value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
55040
55378
  return safe.length > 0 ? safe : "artifact";
@@ -55045,7 +55383,7 @@ function withoutTurn(value2) {
55045
55383
  return copy;
55046
55384
  }
55047
55385
  async function writeReviewArtifact(artifactDir, reviewer, decision, rawText, convergenceDecision) {
55048
- const artifactPath = join17(artifactDir, `review-${artifactSafeName(reviewer)}.json`);
55386
+ const artifactPath = join18(artifactDir, `review-${artifactSafeName(reviewer)}.json`);
55049
55387
  await writeFile5(artifactPath, `${JSON.stringify({ reviewer, decision, convergence_decision: convergenceDecision, raw_text: rawText }, null, 2)}
55050
55388
  `, { encoding: "utf8" });
55051
55389
  return artifactPath;
@@ -55054,7 +55392,7 @@ async function writeReviewRoundArtifact(artifactDir, reviews, consolidatedFindin
55054
55392
  reviewer: review.reviewer,
55055
55393
  findings: review.findings
55056
55394
  }))), reverification = []) {
55057
- const artifactPath = join17(artifactDir, "review-round-latest.json");
55395
+ const artifactPath = join18(artifactDir, "review-round-latest.json");
55058
55396
  const visibleReviews = reviews.map(withoutTurn);
55059
55397
  await writeFile5(artifactPath, `${JSON.stringify({ reviews: visibleReviews, consolidated_findings: consolidatedFindings, reverification }, null, 2)}
55060
55398
  `, { encoding: "utf8" });
@@ -55284,7 +55622,7 @@ var init_goal_review = __esm(() => {
55284
55622
  // dist/builtin/workflows/builtin/goal-ledger.ts
55285
55623
  import { randomUUID as randomUUID9 } from "node:crypto";
55286
55624
  import { readFile as readFile4, rename, rm as rm2, writeFile as writeFile6 } from "node:fs/promises";
55287
- import { dirname as dirname9, join as join18 } from "node:path";
55625
+ import { dirname as dirname10, join as join19 } from "node:path";
55288
55626
  import { isDeepStrictEqual } from "node:util";
55289
55627
  function withoutTurn2(value2) {
55290
55628
  const copy = { ...value2 };
@@ -55309,7 +55647,7 @@ function modelVisibleLedger(ledger) {
55309
55647
  };
55310
55648
  }
55311
55649
  function goalLedgerStatePath(ledgerPath) {
55312
- return join18(dirname9(ledgerPath), LEDGER_STATE_FILENAME);
55650
+ return join19(dirname10(ledgerPath), LEDGER_STATE_FILENAME);
55313
55651
  }
55314
55652
  function appendLifecycleEvent(ledger, event, summary, turn = ledger.turns) {
55315
55653
  ledger.lifecycle.push({
@@ -55385,7 +55723,7 @@ function recoverLegacyExecutionInterruption(ledger) {
55385
55723
  return true;
55386
55724
  }
55387
55725
  async function createGoalLedger(objective, acceptanceCriteria, artifactDir) {
55388
- const ledgerPath = join18(artifactDir, LEDGER_FILENAME);
55726
+ const ledgerPath = join19(artifactDir, LEDGER_FILENAME);
55389
55727
  const existing = await readExistingGoalLedger(ledgerPath);
55390
55728
  if (existing !== undefined) {
55391
55729
  if (recoverLegacyExecutionInterruption(existing))
@@ -55892,7 +56230,7 @@ var init_goal_orchestrator_prompts = __esm(() => {
55892
56230
  });
55893
56231
 
55894
56232
  // dist/builtin/workflows/builtin/goal-runner.ts
55895
- import { join as join19 } from "node:path";
56233
+ import { join as join20 } from "node:path";
55896
56234
  function positiveInteger(value2, fallback) {
55897
56235
  if (typeof value2 !== "number" || !Number.isFinite(value2) || value2 <= 0) {
55898
56236
  return fallback;
@@ -55943,7 +56281,7 @@ async function runGoalWorkflow(ctx, options) {
55943
56281
  appendLifecycleEvent(ledger, "work_turn_started", "Orchestrator started.", turn);
55944
56282
  await writeGoalLedger(ledgerPath, ledger);
55945
56283
  }
55946
- const orchestratorReceiptPath = join19(artifactDir, "orchestrator-receipt.md");
56284
+ const orchestratorReceiptPath = join20(artifactDir, "orchestrator-receipt.md");
55947
56285
  const orchestratorForkOptions = forkContinuationOptions(previousOrchestratorSessionId);
55948
56286
  const orchestratorPrompt = orchestratorForkOptions.forkFromSessionId === undefined ? renderGoalOrchestratorPrompt({
55949
56287
  ledger,
@@ -56014,7 +56352,7 @@ async function runGoalWorkflow(ctx, options) {
56014
56352
  const reviewerName = result.name ?? result.stageName;
56015
56353
  const normalizedReviewerName = reviewerName.replace(/-\d+$/u, "");
56016
56354
  const parsed = reviewerContractError === undefined ? parsedReviewDecisionFromResult(result, reviewerName) : { decision: reviewerErrorDecision(reviewerContractError.message), parsed: false, diagnostics: [reviewerContractError.message] };
56017
- const reviewArtifactPath = join19(artifactDir, `review-${artifactSafeName(normalizedReviewerName)}.json`);
56355
+ const reviewArtifactPath = join20(artifactDir, `review-${artifactSafeName(normalizedReviewerName)}.json`);
56018
56356
  const record = reviewDecisionToRecord({
56019
56357
  turn,
56020
56358
  reviewer: normalizedReviewerName,
@@ -56217,7 +56555,7 @@ var init_goal = __esm(() => {
56217
56555
  }),
56218
56556
  git_worktree_dir: Type15.String({
56219
56557
  default: "",
56220
- 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."
56558
+ 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."
56221
56559
  }),
56222
56560
  create_pr: Type15.Boolean({
56223
56561
  default: false,
@@ -56335,7 +56673,7 @@ var GROUNDED_REPORTING5 = "Before reporting progress, audit each claim against a
56335
56673
 
56336
56674
  // dist/builtin/workflows/builtin/loop-until-done-runner.ts
56337
56675
  import { mkdir as mkdir3, writeFile as writeFile7 } from "node:fs/promises";
56338
- import { join as join20 } from "node:path";
56676
+ import { join as join21 } from "node:path";
56339
56677
  import { Type as Type16 } from "typebox";
56340
56678
  function progressCurve(entries) {
56341
56679
  return entries.flatMap((entry) => entry.progress === undefined ? [] : [entry.progress.score]);
@@ -56418,18 +56756,18 @@ async function writeLedger(path, task, maxIterations, status, entries) {
56418
56756
  }
56419
56757
  async function runLoopUntilDone(ctx) {
56420
56758
  const artifactDir = await stableArtifactRoot(ctx, "loop-until-done");
56421
- const iterationsDir = join20(artifactDir, "iterations");
56422
- const evaluationsDir = join20(artifactDir, "evaluations");
56759
+ const iterationsDir = join21(artifactDir, "iterations");
56760
+ const evaluationsDir = join21(artifactDir, "evaluations");
56423
56761
  await mkdir3(iterationsDir, { recursive: true });
56424
56762
  await mkdir3(evaluationsDir, { recursive: true });
56425
- const ledgerPath = join20(artifactDir, "progress-ledger.json");
56763
+ const ledgerPath = join21(artifactDir, "progress-ledger.json");
56426
56764
  const entries = [];
56427
56765
  const iterationArtifactPaths = [];
56428
56766
  const evaluationArtifactPaths = [];
56429
56767
  await writeLedger(ledgerPath, ctx.inputs.prompt, ctx.inputs.max_iterations, "active", entries);
56430
56768
  for (let iteration = 1;iteration <= ctx.inputs.max_iterations; iteration += 1) {
56431
- const iterationPath = join20(iterationsDir, `iteration-${iteration}.md`);
56432
- const evaluationPath = join20(evaluationsDir, `evaluation-${iteration}.json`);
56769
+ const iterationPath = join21(iterationsDir, `iteration-${iteration}.md`);
56770
+ const evaluationPath = join21(evaluationsDir, `evaluation-${iteration}.json`);
56433
56771
  iterationArtifactPaths.push(iterationPath);
56434
56772
  evaluationArtifactPaths.push(evaluationPath);
56435
56773
  await ctx.task(`iteration-${iteration}`, {
@@ -56478,7 +56816,7 @@ async function runLoopUntilDone(ctx) {
56478
56816
  await writeLedger(ledgerPath, ctx.inputs.prompt, ctx.inputs.max_iterations, decision.done ? "complete" : "active", entries);
56479
56817
  if (decision.done) {
56480
56818
  const report = progressReport(entries);
56481
- const resultPath = join20(artifactDir, "result.md");
56819
+ const resultPath = join21(artifactDir, "result.md");
56482
56820
  const final = await ctx.task("completion-summary", {
56483
56821
  prompt: renderCompletionPrompt({ task: ctx.inputs.prompt, ledgerPath, iterationPath }),
56484
56822
  context: "fresh",
@@ -56595,9 +56933,9 @@ var init_loop_until_done = __esm(() => {
56595
56933
 
56596
56934
  // dist/builtin/workflows/builtin/open-claude-design-utils.ts
56597
56935
  import { spawnSync as spawnSync3 } from "node:child_process";
56598
- import { mkdirSync as mkdirSync5 } from "node:fs";
56936
+ import { mkdirSync as mkdirSync6 } from "node:fs";
56599
56937
  import { tmpdir, userInfo } from "node:os";
56600
- import { join as join21 } from "node:path";
56938
+ import { join as join22 } from "node:path";
56601
56939
  import { Type as Type18 } from "typebox";
56602
56940
  import { createChildProcessEnvironment as createChildProcessEnvironment3 } from "@bastani/atomic";
56603
56941
  function taggedPrompt3(sections) {
@@ -56640,32 +56978,32 @@ function tmpArtifactBase() {
56640
56978
  try {
56641
56979
  user = userInfo().username.replace(/[^A-Za-z0-9._-]/g, "_") || "default";
56642
56980
  } catch {}
56643
- return join21(tmpdir(), `open-claude-design-${user}`);
56981
+ return join22(tmpdir(), `open-claude-design-${user}`);
56644
56982
  }
56645
56983
  function prepareArtifactDir(cwd = process.cwd()) {
56646
56984
  const runId = `${new Date().toISOString().replace(/[:.]/g, "-")}-${Math.random().toString(36).slice(2, 8)}`;
56647
56985
  const tmpCandidates = [
56648
- join21(tmpArtifactBase(), runId),
56649
- join21(tmpdir(), "open-claude-design", runId)
56986
+ join22(tmpArtifactBase(), runId),
56987
+ join22(tmpdir(), "open-claude-design", runId)
56650
56988
  ];
56651
- const candidates = process.env.NODE_ENV === "test" ? tmpCandidates : [join21(cwd, "specs", "design", runId), ...tmpCandidates];
56989
+ const candidates = process.env.NODE_ENV === "test" ? tmpCandidates : [join22(cwd, "specs", "design", runId), ...tmpCandidates];
56652
56990
  for (const candidate of candidates) {
56653
56991
  try {
56654
- mkdirSync5(candidate, { recursive: true });
56992
+ mkdirSync6(candidate, { recursive: true });
56655
56993
  return {
56656
56994
  runId,
56657
56995
  artifactDir: candidate,
56658
- previewPath: join21(candidate, "preview.html"),
56659
- specPath: join21(candidate, "spec.html")
56996
+ previewPath: join22(candidate, "preview.html"),
56997
+ specPath: join22(candidate, "spec.html")
56660
56998
  };
56661
56999
  } catch {}
56662
57000
  }
56663
- const fallback = join21(tmpArtifactBase(), runId);
57001
+ const fallback = join22(tmpArtifactBase(), runId);
56664
57002
  return {
56665
57003
  runId,
56666
57004
  artifactDir: fallback,
56667
- previewPath: join21(fallback, "preview.html"),
56668
- specPath: join21(fallback, "spec.html")
57005
+ previewPath: join22(fallback, "preview.html"),
57006
+ specPath: join22(fallback, "spec.html")
56669
57007
  };
56670
57008
  }
56671
57009
  function ensureAgentBrowser() {
@@ -56999,8 +57337,8 @@ var init_open_claude_design_live_protocol = __esm(() => {
56999
57337
  });
57000
57338
 
57001
57339
  // dist/builtin/workflows/builtin/open-claude-design-setup.ts
57002
- import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync3 } from "node:fs";
57003
- import { join as join22 } from "node:path";
57340
+ import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync4 } from "node:fs";
57341
+ import { join as join23 } from "node:path";
57004
57342
  function renderDiscoveryContext(discovery) {
57005
57343
  return [
57006
57344
  `Confirmed design brief: ${discovery.brief}`,
@@ -57065,10 +57403,10 @@ async function runDiscoveryAndInit(args) {
57065
57403
  };
57066
57404
  }
57067
57405
  function designContextPath(artifactDir) {
57068
- return join22(artifactDir, DESIGN_CONTEXT_FILENAME);
57406
+ return join23(artifactDir, DESIGN_CONTEXT_FILENAME);
57069
57407
  }
57070
57408
  function referencesBriefPath(artifactDir) {
57071
- return join22(artifactDir, REFERENCES_BRIEF_FILENAME);
57409
+ return join23(artifactDir, REFERENCES_BRIEF_FILENAME);
57072
57410
  }
57073
57411
  function buildReferenceDiscoveryPrompt(args) {
57074
57412
  const siteList = REFERENCE_DESIGN_SITES.map((site, index) => `${index + 1}. ${site.name} — ${site.url}`).join(`
@@ -57094,8 +57432,8 @@ function buildReferenceDiscoveryPrompt(args) {
57094
57432
  [
57095
57433
  "Use the agent-browser skill to open each gallery; if `agent-browser` reports a missing browser executable, follow the bootstrap rules and retry once.",
57096
57434
  "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.",
57097
- `Capture motion across the entire page: start \`agent-browser record start ${join22(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\`.`,
57098
- `Also run \`agent-browser screenshot ${join22(args.artifactDir, "ref-<site>-<n>.png")} --full\`; this still is the minimum when video is unavailable.`,
57435
+ `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\`.`,
57436
+ `Also run \`agent-browser screenshot ${join23(args.artifactDir, "ref-<site>-<n>.png")} --full\`; this still is the minimum when video is unavailable.`,
57099
57437
  "Record the actual destination URL, title, and author. For each reference, cite observed layout, typography, color, spacing, and motion traits rather than inferred traits.",
57100
57438
  "Assess fit against DESIGN.md, PRODUCT.md, and the ds-* discovery evidence in the design-context file; prefer on-brand references and flag departures.",
57101
57439
  "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.",
@@ -57120,8 +57458,8 @@ function buildReferenceDiscoveryPrompt(args) {
57120
57458
  }
57121
57459
  function writeRequiredContextArtifact(artifactDir, filePath, content, label) {
57122
57460
  try {
57123
- mkdirSync6(artifactDir, { recursive: true });
57124
- writeFileSync3(filePath, `${content}
57461
+ mkdirSync7(artifactDir, { recursive: true });
57462
+ writeFileSync4(filePath, `${content}
57125
57463
  `);
57126
57464
  } catch (error) {
57127
57465
  const detail = error instanceof Error ? error.message : String(error);
@@ -57672,7 +58010,7 @@ var init_ralph_review_gate = __esm(() => {
57672
58010
 
57673
58011
  // dist/builtin/workflows/builtin/ralph-core.ts
57674
58012
  import { mkdir as mkdir4, writeFile as writeFile8 } from "node:fs/promises";
57675
- import { dirname as dirname10, join as join23 } from "node:path";
58013
+ import { dirname as dirname11, join as join24 } from "node:path";
57676
58014
  import { Type as Type20 } from "typebox";
57677
58015
  function taggedPrompt4(sections) {
57678
58016
  return sections.map(([tag, content]) => {
@@ -57712,11 +58050,11 @@ function slugifyResearchTopic(prompt) {
57712
58050
  }
57713
58051
  function defaultResearchPath(prompt, now = new Date) {
57714
58052
  const date = now.toISOString().slice(0, 10);
57715
- return join23(DEFAULT_RESEARCH_DIR, `${date}-${slugifyResearchTopic(prompt)}.md`);
58053
+ return join24(DEFAULT_RESEARCH_DIR, `${date}-${slugifyResearchTopic(prompt)}.md`);
57716
58054
  }
57717
58055
  async function createImplementationNotesFile(prompt, runId) {
57718
58056
  const notesDir = await createWorkflowArtifactDirectory(runId);
57719
- const notesPath = join23(notesDir, IMPLEMENTATION_NOTES_FILENAME);
58057
+ const notesPath = join24(notesDir, IMPLEMENTATION_NOTES_FILENAME);
57720
58058
  const initialNotes = [
57721
58059
  "# Implementation Notes",
57722
58060
  "",
@@ -57737,7 +58075,7 @@ async function createImplementationNotesFile(prompt, runId) {
57737
58075
  }
57738
58076
  async function createQaEvidenceVideoPath(runId) {
57739
58077
  const qaDir = await createWorkflowArtifactDirectory(runId);
57740
- return join23(qaDir, QA_E2E_VIDEO_FILENAME);
58078
+ return join24(qaDir, QA_E2E_VIDEO_FILENAME);
57741
58079
  }
57742
58080
  function renderQaE2eVideoGuidance(qaVideoPath) {
57743
58081
  return [
@@ -57803,7 +58141,7 @@ function artifactSafeName2(value2) {
57803
58141
  return safe.length > 0 ? safe : "artifact";
57804
58142
  }
57805
58143
  async function writeJsonArtifact(path, content) {
57806
- await mkdir4(dirname10(path), { recursive: true });
58144
+ await mkdir4(dirname11(path), { recursive: true });
57807
58145
  await writeFile8(path, `${JSON.stringify(content, null, 2)}
57808
58146
  `, {
57809
58147
  encoding: "utf8"
@@ -58141,7 +58479,7 @@ var init_ralph_models = __esm(() => {
58141
58479
 
58142
58480
  // dist/builtin/workflows/builtin/ralph-runner.ts
58143
58481
  import { existsSync as existsSync4 } from "node:fs";
58144
- import { join as join24, resolve as resolve9 } from "node:path";
58482
+ import { join as join25, resolve as resolve10 } from "node:path";
58145
58483
  async function runRalphWorkflow(ctx, options) {
58146
58484
  const { prompt, acceptanceCriteria, maxLoops, comparisonBaseBranch, workflowStartCwd, createPr, runId } = options;
58147
58485
  let latestReviewReportPath;
@@ -58153,7 +58491,7 @@ async function runRalphWorkflow(ctx, options) {
58153
58491
  let finalPrReport;
58154
58492
  const workflowCwdContext = workflowCwdContextSection(workflowStartCwd);
58155
58493
  const workflowPrompt = prompt;
58156
- const workflowResearchPath = resolve9(workflowStartCwd, defaultResearchPath(workflowPrompt));
58494
+ const workflowResearchPath = resolve10(workflowStartCwd, defaultResearchPath(workflowPrompt));
58157
58495
  const implementationNotesPath = await createImplementationNotesFile(workflowPrompt, runId);
58158
58496
  const qaVideoPath = await createQaEvidenceVideoPath(runId);
58159
58497
  const artifactDir = await createWorkflowArtifactDirectory(runId);
@@ -58202,7 +58540,7 @@ async function runRalphWorkflow(ctx, options) {
58202
58540
  const researchPath = workflowResearchPath;
58203
58541
  finalResearchPath = researchPath;
58204
58542
  finalPlanPath = researchPath;
58205
- const orchestratorReportPath = join24(artifactDir, "orchestrator-report.md");
58543
+ const orchestratorReportPath = join25(artifactDir, "orchestrator-report.md");
58206
58544
  const orchestratorForkOptions = forkContinuationOptions3(previousOrchestratorSessionId);
58207
58545
  const orchestratorPrompt = orchestratorForkOptions.forkFromSessionId === undefined ? taggedPrompt4([
58208
58546
  ["acceptance_criteria", keepContext(acceptanceCriteria)],
@@ -58338,7 +58676,7 @@ Read it before implementation or delegation because it is the primary current im
58338
58676
  diagnostics: parsed.diagnostics,
58339
58677
  allowFinalActionRemaining: createPr
58340
58678
  });
58341
- const artifactPath = join24(artifactDir, `review-${artifactSafeName2(reviewer)}.json`);
58679
+ const artifactPath = join25(artifactDir, `review-${artifactSafeName2(reviewer)}.json`);
58342
58680
  await writeJsonArtifact(artifactPath, {
58343
58681
  reviewer,
58344
58682
  decision: parsed.decision,
@@ -58394,7 +58732,7 @@ Read it before implementation or delegation because it is the primary current im
58394
58732
  usage: fold_usage([orchestrator, ...reviews, ...reverifyResults])
58395
58733
  }));
58396
58734
  }
58397
- latestReviewReportPath = await writeJsonArtifact(join24(artifactDir, "review-round-latest.json"), {
58735
+ latestReviewReportPath = await writeJsonArtifact(join25(artifactDir, "review-round-latest.json"), {
58398
58736
  convergence_decision: roundConvergenceDecision,
58399
58737
  convergence: convergenceEntries,
58400
58738
  consolidated_findings: reverified.batch,
@@ -58543,7 +58881,7 @@ var init_ralph = __esm(() => {
58543
58881
  }),
58544
58882
  git_worktree_dir: Type21.String({
58545
58883
  default: "",
58546
- 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."
58884
+ 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."
58547
58885
  }),
58548
58886
  create_pr: Type21.Boolean({
58549
58887
  default: false,
@@ -58763,7 +59101,7 @@ var init_tournament_prompts = __esm(() => {
58763
59101
 
58764
59102
  // dist/builtin/workflows/builtin/tournament-runner.ts
58765
59103
  import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile9 } from "node:fs/promises";
58766
- import { join as join25, isAbsolute as isAbsolute9, relative as relative5, resolve as resolve10 } from "node:path";
59104
+ import { join as join26, isAbsolute as isAbsolute9, relative as relative5, resolve as resolve11 } from "node:path";
58767
59105
  import { Type as Type22 } from "typebox";
58768
59106
  function serializableObject2(value2) {
58769
59107
  if (value2 === null || Array.isArray(value2) || typeof value2 !== "object")
@@ -58800,8 +59138,8 @@ function criterionSlug(id) {
58800
59138
  }
58801
59139
  function judgeArtifactPath(judgesDir, job, criterionIndex) {
58802
59140
  const filename = `judge-${job.a}-${job.b}-c${criterionIndex}-${criterionSlug(job.criterionId)}-r${job.rep}.json`;
58803
- const root = resolve10(judgesDir);
58804
- const path = resolve10(judgesDir, filename);
59141
+ const root = resolve11(judgesDir);
59142
+ const path = resolve11(judgesDir, filename);
58805
59143
  const fromRoot = relative5(root, path);
58806
59144
  if (fromRoot.startsWith("..") || isAbsolute9(fromRoot)) {
58807
59145
  throw new Error(`tournament: judge artifact escaped its directory: ${path}`);
@@ -58852,16 +59190,16 @@ function combineUsage(totals) {
58852
59190
  }
58853
59191
  async function runTournament(ctx) {
58854
59192
  const artifactDir = await stableArtifactRoot(ctx, "tournament");
58855
- const attemptsDir = join25(artifactDir, "attempts");
59193
+ const attemptsDir = join26(artifactDir, "attempts");
58856
59194
  const numAttempts = ctx.inputs.num_attempts ?? 4;
58857
59195
  const maxConcurrency = ctx.inputs.max_concurrency ?? 4;
58858
59196
  const nEvaluations = ctx.inputs.n_evaluations ?? 2;
58859
59197
  const pivotCount = ctx.inputs.pivots ?? 1;
58860
59198
  const seed = ctx.inputs.seed ?? 0;
58861
- const judgesDir = join25(artifactDir, "judges");
59199
+ const judgesDir = join26(artifactDir, "judges");
58862
59200
  await mkdir5(attemptsDir, { recursive: true });
58863
59201
  await mkdir5(judgesDir, { recursive: true });
58864
- const attemptArtifactPaths = Array.from({ length: numAttempts }, (_, index) => join25(attemptsDir, `attempt-${index + 1}.md`));
59202
+ const attemptArtifactPaths = Array.from({ length: numAttempts }, (_, index) => join26(attemptsDir, `attempt-${index + 1}.md`));
58865
59203
  const models = ctx.inputs.models;
58866
59204
  const attemptResults = await ctx.parallel(attemptArtifactPaths.map((path, index) => ({
58867
59205
  name: `attempt-${index + 1}`,
@@ -59026,7 +59364,7 @@ The previous report was invalid. Return the required JSON schema exactly once.`,
59026
59364
  const outputRanking = ranking.map(({ label, meanPreference }) => ({ label, meanPreference }));
59027
59365
  const budgetPivots = Math.min(pivotCount, entrants.length);
59028
59366
  const comparisonBudget = (entrants.length + budgetPivots * (entrants.length - budgetPivots) + budgetPivots * (budgetPivots - 1) / 2) * criteria.length * nEvaluations;
59029
- const comparisonPath = join25(artifactDir, "comparisons.json");
59367
+ const comparisonPath = join26(artifactDir, "comparisons.json");
59030
59368
  const assignment = modelAssignment(entrants, models);
59031
59369
  const phaseUsage = {
59032
59370
  attempts: fold_usage(attemptResults),
@@ -59062,7 +59400,7 @@ The previous report was invalid. Return the required JSON schema exactly once.`,
59062
59400
  await writeLedger(fold_usage([]));
59063
59401
  const winner = ranking[0];
59064
59402
  const winnerEntrant = entrants[winner.index];
59065
- const resultPath = join25(artifactDir, "winner.md");
59403
+ const resultPath = join26(artifactDir, "winner.md");
59066
59404
  const reducer = await ctx.task("comparisons-reducer", {
59067
59405
  prompt: renderComparisonsReducerPrompt({
59068
59406
  task: ctx.inputs.prompt,
@@ -59371,17 +59709,26 @@ function isSameCheckpointValue(stored, next) {
59371
59709
  const hash = (value2) => durableHash(value2);
59372
59710
  return hash(stored) === hash(next);
59373
59711
  }
59712
+ function withTimesOf(later, recorded) {
59713
+ const both = (key) => typeof recorded[key] === "number" && typeof later[key] === "number";
59714
+ return {
59715
+ ...later,
59716
+ ...both("startedAt") ? { startedAt: recorded.startedAt } : {},
59717
+ ...both("endedAt") ? { endedAt: recorded.endedAt } : {},
59718
+ ...both("completedAt") ? { completedAt: recorded.completedAt } : {}
59719
+ };
59720
+ }
59374
59721
  function isRecordedCheckpointValue(stored, next) {
59375
59722
  const recorded = stored;
59376
59723
  if (isSameCheckpointValue(recorded, next))
59377
59724
  return true;
59378
- const { completedAt, startedAt } = recorded;
59379
- if (typeof completedAt !== "number")
59725
+ if (typeof recorded.completedAt !== "number")
59380
59726
  return false;
59381
59727
  const later = next;
59382
- if (startedAt === undefined !== (later.startedAt === undefined))
59728
+ if (recorded.startedAt === undefined !== (later.startedAt === undefined))
59383
59729
  return false;
59384
- return isSameCheckpointValue({ ...later, completedAt, ...startedAt === undefined ? {} : { startedAt } }, recorded);
59730
+ const retimed = withTimesOf(later, recorded);
59731
+ return isSameCheckpointValue(recorded.topology !== undefined && later.topology !== undefined ? { ...retimed, topology: withTimesOf(later.topology, recorded.topology) } : retimed, recorded);
59385
59732
  }
59386
59733
  var MIRROR_FRESH_MS = 2000;
59387
59734
  var init_pool_remote_journal = __esm(() => {
@@ -59959,7 +60306,7 @@ class SqlPoolQueue {
59959
60306
  throw new Error("Stale pool coordinator authority", { cause: error });
59960
60307
  throw error;
59961
60308
  });
59962
- const row = await this.current(tx, lease, token);
60309
+ const row = await this.current(tx, lease, token, true);
59963
60310
  if (row.epoch !== lease.epoch)
59964
60311
  throw new Error(STALE_WORK_LEASE);
59965
60312
  assertSession();
@@ -60417,15 +60764,7 @@ class SqlPoolQueue {
60417
60764
  }
60418
60765
  }
60419
60766
  function isRecordedCheckpoint(stored, payload) {
60420
- if (stored === payload)
60421
- return true;
60422
- const { completedAt, startedAt } = JSON.parse(stored);
60423
- if (typeof completedAt !== "number")
60424
- return false;
60425
- const next = JSON.parse(payload);
60426
- if (startedAt === undefined !== (next.startedAt === undefined))
60427
- return false;
60428
- return serializeJson({ ...next, completedAt, ...startedAt === undefined ? {} : { startedAt } }) === stored;
60767
+ return stored === payload || isRecordedCheckpointValue(JSON.parse(stored), JSON.parse(payload));
60429
60768
  }
60430
60769
  function openSqlPoolQueue(journal, remote) {
60431
60770
  const storage = sqlPoolStorage(journal);
@@ -62201,7 +62540,7 @@ function isBrokerStatusReport(value2) {
62201
62540
 
62202
62541
  // dist/builtin/intercom/broker/paths.ts
62203
62542
  import { homedir as homedir2 } from "os";
62204
- import { join as join26 } from "path";
62543
+ import { join as join27 } from "path";
62205
62544
  function getHomeDir() {
62206
62545
  if (process.platform === "win32") {
62207
62546
  if (process.env.USERPROFILE)
@@ -62218,7 +62557,7 @@ function expandTildePath(path) {
62218
62557
  if (path === "~")
62219
62558
  return getHomeDir();
62220
62559
  if (path.startsWith("~/") || process.platform === "win32" && path.startsWith("~\\")) {
62221
- return join26(getHomeDir(), path.slice(2));
62560
+ return join27(getHomeDir(), path.slice(2));
62222
62561
  }
62223
62562
  return path;
62224
62563
  }
@@ -62229,31 +62568,31 @@ function getAgentDir5() {
62229
62568
  const piAgentDir = process.env.PI_CODING_AGENT_DIR;
62230
62569
  if (piAgentDir)
62231
62570
  return expandTildePath(piAgentDir);
62232
- return join26(getHomeDir(), ".atomic", "agent");
62571
+ return join27(getHomeDir(), ".atomic", "agent");
62233
62572
  }
62234
62573
  function sanitizePipeSegment(value2) {
62235
62574
  return value2.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "default";
62236
62575
  }
62237
62576
  function getIntercomDirPath(agentDir = getAgentDir5()) {
62238
- return join26(agentDir, "intercom");
62577
+ return join27(agentDir, "intercom");
62239
62578
  }
62240
62579
  function getBrokerPidPath(agentDir = getAgentDir5()) {
62241
- return join26(getIntercomDirPath(agentDir), "broker.pid");
62580
+ return join27(getIntercomDirPath(agentDir), "broker.pid");
62242
62581
  }
62243
62582
  function getBrokerSpawnLockPath(agentDir = getAgentDir5()) {
62244
- return join26(getIntercomDirPath(agentDir), "broker.spawn.lock");
62583
+ return join27(getIntercomDirPath(agentDir), "broker.spawn.lock");
62245
62584
  }
62246
62585
  function getBrokerLogPath(agentDir = getAgentDir5()) {
62247
- return join26(getIntercomDirPath(agentDir), "broker.log");
62586
+ return join27(getIntercomDirPath(agentDir), "broker.log");
62248
62587
  }
62249
62588
  function getBrokerStartAnnouncementPath(agentDir = getAgentDir5()) {
62250
- return join26(getIntercomDirPath(agentDir), "broker.starting");
62589
+ return join27(getIntercomDirPath(agentDir), "broker.starting");
62251
62590
  }
62252
62591
  function getBrokerSocketPath(platform = process.platform, agentDir = getAgentDir5()) {
62253
62592
  if (platform === "win32") {
62254
62593
  return `\\\\.\\pipe\\pi-intercom-${sanitizePipeSegment(agentDir)}`;
62255
62594
  }
62256
- return join26(getIntercomDirPath(agentDir), "broker.sock");
62595
+ return join27(getIntercomDirPath(agentDir), "broker.sock");
62257
62596
  }
62258
62597
  var BROKER_STORAGE_ENV = "ATOMIC_STORAGE";
62259
62598
  var init_paths = () => {};
@@ -63937,11 +64276,11 @@ var init_client = __esm(() => {
63937
64276
  var BROKER_READY_TIMEOUT_MS = 30000, MIN_BROKER_STARTUP_TIMEOUT_MS = 1000;
63938
64277
 
63939
64278
  // dist/builtin/intercom/broker/start-announcement.ts
63940
- import { readFileSync as readFileSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
64279
+ import { readFileSync as readFileSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
63941
64280
  function readBrokerStartAnnouncement(announcement) {
63942
64281
  let text;
63943
64282
  try {
63944
- text = readFileSync3(announcement.path, "utf-8");
64283
+ text = readFileSync5(announcement.path, "utf-8");
63945
64284
  } catch {
63946
64285
  return;
63947
64286
  }
@@ -63960,26 +64299,26 @@ var init_start_announcement = () => {};
63960
64299
  // dist/builtin/intercom/broker/spawn.ts
63961
64300
  import { spawn } from "child_process";
63962
64301
  import { randomUUID as randomUUID14 } from "crypto";
63963
- import { appendFileSync, chmodSync, closeSync as closeSync3, existsSync as existsSync5, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync4, readSync as readSync2, statSync as statSync5, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "fs";
63964
- import { join as join27, dirname as dirname11 } from "path";
64302
+ import { appendFileSync, chmodSync, closeSync as closeSync3, existsSync as existsSync5, mkdirSync as mkdirSync8, openSync as openSync3, readFileSync as readFileSync6, readSync as readSync2, statSync as statSync5, unlinkSync as unlinkSync4, writeFileSync as writeFileSync6 } from "fs";
64303
+ import { join as join28, dirname as dirname12 } from "path";
63965
64304
  import { fileURLToPath as fileURLToPath4 } from "url";
63966
64305
  import { createRequire as createRequire3 } from "module";
63967
64306
  import { createChildProcessEnvironment as createChildProcessEnvironment4, isBunBinary, processSharedStorageSelection } from "@bastani/atomic";
63968
64307
  function isIntercomPackageRoot(dir) {
63969
- return existsSync5(join27(dir, "package.json")) && existsSync5(join27(dir, "broker"));
64308
+ return existsSync5(join28(dir, "package.json")) && existsSync5(join28(dir, "broker"));
63970
64309
  }
63971
64310
  function resolveIntercomPackageRoot(moduleHref) {
63972
- const here = dirname11(fileURLToPath4(moduleHref));
64311
+ const here = dirname12(fileURLToPath4(moduleHref));
63973
64312
  if (isIntercomPackageRoot(here))
63974
64313
  return here;
63975
- return join27(here, "..");
64314
+ return join28(here, "..");
63976
64315
  }
63977
64316
  function resolveBrokerEntrypoint(packageRoot) {
63978
- const bundled = join27(packageRoot, "broker", "broker.bundle.mjs");
63979
- return existsSync5(bundled) ? bundled : join27(packageRoot, "broker", "broker.ts");
64317
+ const bundled = join28(packageRoot, "broker", "broker.bundle.mjs");
64318
+ return existsSync5(bundled) ? bundled : join28(packageRoot, "broker", "broker.ts");
63980
64319
  }
63981
64320
  function ensurePrivateDirectory(path) {
63982
- mkdirSync7(path, { recursive: true, mode: 448 });
64321
+ mkdirSync8(path, { recursive: true, mode: 448 });
63983
64322
  if (process.platform !== "win32")
63984
64323
  chmodSync(path, 448);
63985
64324
  }
@@ -63992,14 +64331,14 @@ function getCurrentBrokerRuntime() {
63992
64331
  return isBunBinary ? "bun-binary" : "bun-source";
63993
64332
  }
63994
64333
  function requireFromExtensionDir(extensionDir) {
63995
- return createRequire3(join27(extensionDir, "package.json"));
64334
+ return createRequire3(join28(extensionDir, "package.json"));
63996
64335
  }
63997
64336
  function getJitiCliPath(extensionDir = EXTENSION_DIR) {
63998
64337
  try {
63999
64338
  const jitiPackage = requireFromExtensionDir(extensionDir).resolve("jiti/package.json");
64000
- return join27(dirname11(jitiPackage), "lib", "jiti-cli.mjs");
64339
+ return join28(dirname12(jitiPackage), "lib", "jiti-cli.mjs");
64001
64340
  } catch {
64002
- return join27(extensionDir, "node_modules", "jiti", "lib", "jiti-cli.mjs");
64341
+ return join28(extensionDir, "node_modules", "jiti", "lib", "jiti-cli.mjs");
64003
64342
  }
64004
64343
  }
64005
64344
  function getDefaultBrokerRunnerPath(extensionDir) {
@@ -64021,7 +64360,7 @@ function quoteWindowsArg(value2) {
64021
64360
  return `"${value2.replace(/"/g, '""')}"`;
64022
64361
  }
64023
64362
  function getWindowsHiddenLauncherPath(intercomDir = INTERCOM_DIR) {
64024
- return join27(intercomDir, "broker-launch.vbs");
64363
+ return join28(intercomDir, "broker-launch.vbs");
64025
64364
  }
64026
64365
  function usesDefaultBrokerCommand(brokerCommand, brokerArgs) {
64027
64366
  return brokerCommand === "npx" && brokerArgs.length === 2 && brokerArgs[0] === "--no-install" && brokerArgs[1] === "tsx";
@@ -64048,8 +64387,8 @@ function getWindowsHiddenLauncherScript(commandLine) {
64048
64387
  `);
64049
64388
  }
64050
64389
  function writeWindowsHiddenLauncher(commandLine, launcherPath = getWindowsHiddenLauncherPath()) {
64051
- ensurePrivateDirectory(dirname11(launcherPath));
64052
- writeFileSync5(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
64390
+ ensurePrivateDirectory(dirname12(launcherPath));
64391
+ writeFileSync6(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
64053
64392
  return launcherPath;
64054
64393
  }
64055
64394
  function getBrokerLaunchSpec(brokerPath, brokerCommand, brokerArgs, extensionDir = EXTENSION_DIR, platform = process.platform, intercomDir = INTERCOM_DIR, nodePath = process.execPath, runtime = getCurrentBrokerRuntime(), logPath = BROKER_LOG) {
@@ -64092,14 +64431,14 @@ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, stderr = "ignore",
64092
64431
  };
64093
64432
  }
64094
64433
  function resetBrokerLog(logPath = BROKER_LOG) {
64095
- ensurePrivateDirectory(dirname11(logPath));
64434
+ ensurePrivateDirectory(dirname12(logPath));
64096
64435
  try {
64097
64436
  const previous = readBrokerLogTail(logPath);
64098
64437
  closeSync3(openSync3(logPath, "w"));
64099
64438
  if (previous.length > 0) {
64100
64439
  try {
64101
64440
  const previousPath = `${logPath}.previous`;
64102
- writeFileSync5(previousPath, Buffer.from(previous).subarray(-BROKER_LOG_TAIL_BYTES), { mode: 384 });
64441
+ writeFileSync6(previousPath, Buffer.from(previous).subarray(-BROKER_LOG_TAIL_BYTES), { mode: 384 });
64103
64442
  if (process.platform !== "win32")
64104
64443
  chmodSync(previousPath, 384);
64105
64444
  } catch {}
@@ -64243,7 +64582,7 @@ async function isBrokerRunning() {
64243
64582
  if (!existsSync5(BROKER_PID))
64244
64583
  return false;
64245
64584
  try {
64246
- const pid = parseInt(readFileSync4(BROKER_PID, "utf-8").trim(), 10);
64585
+ const pid = parseInt(readFileSync6(BROKER_PID, "utf-8").trim(), 10);
64247
64586
  if (!Number.isFinite(pid))
64248
64587
  return false;
64249
64588
  process.kill(pid, 0);
@@ -64268,7 +64607,7 @@ function handSpawnLockToBroker(pid, owner) {
64268
64607
  }
64269
64608
  function ownsSpawnLock(owner, lockPath = BROKER_SPAWN_LOCK) {
64270
64609
  try {
64271
- return readFileSync4(lockPath, "utf-8").startsWith(owner);
64610
+ return readFileSync6(lockPath, "utf-8").startsWith(owner);
64272
64611
  } catch {
64273
64612
  return false;
64274
64613
  }
@@ -64280,7 +64619,7 @@ function acquireSpawnLock() {
64280
64619
  ${Date.now()}
64281
64620
  `;
64282
64621
  try {
64283
- writeFileSync5(BROKER_SPAWN_LOCK, owner, { flag: "wx" });
64622
+ writeFileSync6(BROKER_SPAWN_LOCK, owner, { flag: "wx" });
64284
64623
  return owner;
64285
64624
  } catch (error) {
64286
64625
  if (!(error instanceof Error) || error.code !== "EEXIST") {
@@ -64310,7 +64649,7 @@ function isSpawnLockStale(lockPath = BROKER_SPAWN_LOCK, nowMs = Date.now()) {
64310
64649
  return false;
64311
64650
  }
64312
64651
  try {
64313
- const [spawnerLine = "", createdAtLine = "", brokerLine = ""] = readFileSync4(lockPath, "utf-8").trim().split(`
64652
+ const [spawnerLine = "", createdAtLine = "", brokerLine = ""] = readFileSync6(lockPath, "utf-8").trim().split(`
64314
64653
  `);
64315
64654
  const spawnerPid = Number.parseInt(spawnerLine, 10);
64316
64655
  const createdAt = Number.parseInt(createdAtLine, 10);
@@ -64388,14 +64727,14 @@ var init_reply_waiter = __esm(() => {
64388
64727
  });
64389
64728
 
64390
64729
  // dist/builtin/intercom/config.ts
64391
- import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
64730
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
64392
64731
  import { getAgentConfigPaths } from "@bastani/atomic";
64393
64732
  function loadConfig() {
64394
64733
  if (!existsSync6(CONFIG_PATH)) {
64395
64734
  return { ...defaults };
64396
64735
  }
64397
64736
  try {
64398
- const raw = readFileSync5(CONFIG_PATH, "utf-8");
64737
+ const raw = readFileSync7(CONFIG_PATH, "utf-8");
64399
64738
  const parsed = JSON.parse(raw);
64400
64739
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
64401
64740
  throw new Error("Config must be a JSON object");
@@ -64978,12 +65317,12 @@ var init_sql_pool_usage = __esm(() => {
64978
65317
 
64979
65318
  // dist/builtin/workflows/src/workers/pooled-watch-trace.ts
64980
65319
  import { appendFileSync as appendFileSync2 } from "node:fs";
64981
- import { dirname as dirname12, join as join28, resolve as resolve11 } from "node:path";
65320
+ import { dirname as dirname13, join as join29, resolve as resolve12 } from "node:path";
64982
65321
  function configurePooledWatchTrace(journalPath, processRole) {
64983
65322
  if (setting === undefined || setting === "" || setting === "0")
64984
65323
  return;
64985
65324
  role = processRole;
64986
- file = setting === "1" ? join28(dirname12(resolve11(journalPath)), "pooled-watch-trace.jsonl") : resolve11(setting);
65325
+ file = setting === "1" ? join29(dirname13(resolve12(journalPath)), "pooled-watch-trace.jsonl") : resolve12(setting);
64987
65326
  }
64988
65327
  function tracePooledWatch(step, detail) {
64989
65328
  if (file === undefined)
@@ -65176,8 +65515,8 @@ var init_startup_timeout = () => {};
65176
65515
  import { fork } from "node:child_process";
65177
65516
  import { randomUUID as randomUUID16 } from "node:crypto";
65178
65517
  import { createWriteStream } from "node:fs";
65179
- import { mkdir as mkdir6, rm as rm3, rmdir } from "node:fs/promises";
65180
- import { dirname as dirname13, join as join29, resolve as resolve12 } from "node:path";
65518
+ import { appendFile, mkdir as mkdir6, rm as rm3, rmdir } from "node:fs/promises";
65519
+ import { dirname as dirname14, join as join30, resolve as resolve13 } from "node:path";
65181
65520
  import { setTimeout as delay2 } from "node:timers/promises";
65182
65521
  function isRootLeaseLost(error) {
65183
65522
  return error instanceof StaleLeaseError || error.message.startsWith("Stale lease for run ");
@@ -65186,7 +65525,7 @@ function unconfirmedCleanup(groups) {
65186
65525
  return new Error(`Local worker process cleanup unconfirmed; ${groups.length > 0 ? `process groups ${groups.join(", ")} still running after SIGKILL; ` : ""}permits quarantined`);
65187
65526
  }
65188
65527
  function workerLogDirectory(journalPath, root) {
65189
- return join29(dirname13(resolve12(journalPath)), "worker-logs", encodeURIComponent(root).replaceAll(".", "%2E"));
65528
+ return join30(dirname14(resolve13(journalPath)), "worker-logs", encodeURIComponent(root).replaceAll(".", "%2E"));
65190
65529
  }
65191
65530
  function token(work) {
65192
65531
  return {
@@ -65368,9 +65707,13 @@ class PoolCoordinator {
65368
65707
  incarnations = new Map;
65369
65708
  readyWorkers = new Set;
65370
65709
  logs = new Map;
65710
+ keptLogs = new Set;
65711
+ unconfirmedExits = new WeakSet;
65712
+ failed = false;
65371
65713
  cleanups = new Set;
65372
65714
  controlAcks = new Map;
65373
65715
  lastHeard = new Map;
65716
+ awaiting = new Map;
65374
65717
  pendingActivity = new Map;
65375
65718
  batches = new Map;
65376
65719
  publicationFailures = new Map;
@@ -65415,7 +65758,6 @@ class PoolCoordinator {
65415
65758
  nextRecoveryAt = 0;
65416
65759
  cleanupFailure;
65417
65760
  held = new Set;
65418
- quarantined = new Set;
65419
65761
  unreaped = [];
65420
65762
  retained = new Map;
65421
65763
  retainedTransitions = Promise.resolve();
@@ -65608,18 +65950,15 @@ class PoolCoordinator {
65608
65950
  try {
65609
65951
  const { survivors, unprobeable } = await reapProcessGroups(this.journal, this.root, { attempt: work });
65610
65952
  if (survivors.length > 0) {
65611
- this.quarantined.add(work.id);
65612
65953
  this.unreaped.push({ attempt: work });
65613
- this.port.send({ kind: "work-failure", workId: work.id, error: unconfirmedCleanup(survivors).message });
65614
- return;
65954
+ this.reportHealth(undefined, `${unconfirmedCleanup(survivors).message}; child ${work.childRunId} of an earlier incarnation replays`);
65615
65955
  }
65616
65956
  if (unprobeable.length === 0)
65617
65957
  await this.releaseRetained(work);
65618
65958
  } catch (error) {
65619
65959
  this.cleanupFailure ??= error instanceof Error ? error : new Error(String(error));
65620
65960
  } finally {
65621
- if (!this.quarantined.has(work.id))
65622
- this.held.delete(work.id);
65961
+ this.held.delete(work.id);
65623
65962
  }
65624
65963
  }
65625
65964
  releaseRetained(work) {
@@ -65750,6 +66089,7 @@ class PoolCoordinator {
65750
66089
  });
65751
66090
  }
65752
66091
  fail(error) {
66092
+ this.failed = true;
65753
66093
  this.port.send({ kind: "failure", error: error.message });
65754
66094
  }
65755
66095
  failWork(work, error) {
@@ -66117,7 +66457,7 @@ class PoolCoordinator {
66117
66457
  this.slots.add(slot);
66118
66458
  let ready = false;
66119
66459
  let serial = Promise.resolve();
66120
- const logPath = join29(this.logDirectory, `${worker}.log`);
66460
+ const logPath = join30(this.logDirectory, `${worker}.log`);
66121
66461
  this.logs.set(child, logPath);
66122
66462
  let logFailure;
66123
66463
  const log = createWriteStream(logPath, { flags: "a" });
@@ -66158,7 +66498,11 @@ class PoolCoordinator {
66158
66498
  terminate();
66159
66499
  });
66160
66500
  const closed = new Promise((resolve) => child.once("close", () => resolve()));
66161
- child.once("exit", () => {
66501
+ let retired = false;
66502
+ const retire = () => {
66503
+ if (retired)
66504
+ return;
66505
+ retired = true;
66162
66506
  Promise.race([closed, delay2(STDERR_DRAIN_GRACE_MS)]).then(() => reject(diagnosed(timedOut ? startupTimeoutMessage(startupTimeoutMs) : "Local worker exited before readiness")));
66163
66507
  this.children.delete(child);
66164
66508
  this.logs.delete(child);
@@ -66169,6 +66513,7 @@ class PoolCoordinator {
66169
66513
  this.readyWorkers.delete(worker);
66170
66514
  this.controlAcks.delete(worker);
66171
66515
  this.lastHeard.delete(worker);
66516
+ this.awaiting.delete(worker);
66172
66517
  this.slots.delete(slot);
66173
66518
  this.endStageWatches((watch) => watch.slot === slot, "worker_exited");
66174
66519
  for (const settle of slot.stageCommands.values())
@@ -66182,7 +66527,7 @@ class PoolCoordinator {
66182
66527
  const held = slot.work;
66183
66528
  if (held)
66184
66529
  this.held.add(held.id);
66185
- let childFailed = false;
66530
+ let unconfirmed;
66186
66531
  const cleanup = disconnected.then(() => serial).then(async () => {
66187
66532
  try {
66188
66533
  const admission = this.admission;
@@ -66195,18 +66540,13 @@ class PoolCoordinator {
66195
66540
  throw unconfirmedCleanup(alive);
66196
66541
  if (alive.length === 0)
66197
66542
  return;
66198
- const error = unconfirmedCleanup(alive);
66199
- if (held && await this.queue.result(this.root, held.id) === undefined) {
66200
- this.quarantined.add(held.id);
66201
- slot.work = undefined;
66202
- childFailed = true;
66203
- this.port.send({ kind: "work-failure", workId: held.id, error: error.message });
66204
- }
66543
+ unconfirmed = unconfirmedCleanup(alive);
66544
+ this.reportHealth(logPath, `${unconfirmed.message}; the worker was retired and replaced`);
66205
66545
  } catch (error) {
66206
66546
  this.cleanupFailure = error instanceof Error ? error : new Error(String(error));
66207
66547
  throw this.cleanupFailure;
66208
66548
  } finally {
66209
- if (held && !this.quarantined.has(held.id))
66549
+ if (held)
66210
66550
  this.held.delete(held.id);
66211
66551
  }
66212
66552
  }).then(async () => {
@@ -66227,8 +66567,8 @@ class PoolCoordinator {
66227
66567
  const settled = attempt !== undefined && await this.whileRootLate(queue, () => exhausted ? queue.failExited(lease, attempt) : queue.releaseExited(lease, attempt));
66228
66568
  const superseded = work !== undefined && !settled && await this.superseded(work);
66229
66569
  if (work !== undefined && exhausted && !superseded)
66230
- this.failWork(work, diagnosed(childRetryLimit(work, "lost its worker")));
66231
- if (work === undefined && !childFailed && ++this.replacements > 3)
66570
+ this.failWork(work, diagnosed(`${childRetryLimit(work, "lost its worker")}${unconfirmed === undefined ? "" : `; ${unconfirmed.message}`}`));
66571
+ if (work === undefined && ++this.replacements > 3)
66232
66572
  this.fail(diagnosed("Local worker retry limit"));
66233
66573
  else
66234
66574
  await this.spawn();
@@ -66237,7 +66577,9 @@ class PoolCoordinator {
66237
66577
  });
66238
66578
  this.cleanups.add(cleanup);
66239
66579
  cleanup.finally(() => this.cleanups.delete(cleanup));
66240
- });
66580
+ };
66581
+ slot.retire = retire;
66582
+ child.once("exit", retire);
66241
66583
  child.on("message", (message) => {
66242
66584
  this.observeResponsiveness();
66243
66585
  this.lastHeard.set(worker, { at: performance.now(), stalled: this.stalledMs });
@@ -66311,10 +66653,11 @@ class PoolCoordinator {
66311
66653
  const fenced = slot.work;
66312
66654
  slot.work = undefined;
66313
66655
  if (fenced && !await this.superseded(fenced)) {
66656
+ this.fence(fenced);
66314
66657
  if (this.attempts(fenced) < MAX_CHILD_ATTEMPTS)
66315
66658
  await leased(() => queue.releaseExited(lease, fenced));
66316
66659
  else if (await leased(() => queue.failExited(lease, fenced)))
66317
- this.failWork(fenced, new Error(childRetryLimit(fenced, "was refused by the root as stale on every attempt")));
66660
+ this.failWork(fenced, new Error(childRetryLimit(fenced, message.fault === undefined ? "was refused by the root as stale on every attempt" : `failed on a fault of the pool on every attempt (last: ${message.fault})`)));
66318
66661
  }
66319
66662
  return;
66320
66663
  }
@@ -66428,8 +66771,19 @@ class PoolCoordinator {
66428
66771
  await this.sendResult(work.id);
66429
66772
  });
66430
66773
  const response = handled.then((value2) => routeReady ?? value2);
66431
- if (message.kind !== "delivery" && message.kind !== "delivery-ready")
66432
- serial = handled.then(() => {}, () => {});
66774
+ if (message.kind !== "delivery" && message.kind !== "delivery-ready") {
66775
+ this.awaiting.set(worker, (this.awaiting.get(worker) ?? 0) + 1);
66776
+ const answered = () => {
66777
+ const left = (this.awaiting.get(worker) ?? 1) - 1;
66778
+ if (left > 0)
66779
+ this.awaiting.set(worker, left);
66780
+ else
66781
+ this.awaiting.delete(worker);
66782
+ if (this.incarnations.get(worker) === child)
66783
+ this.lastHeard.set(worker, { at: performance.now(), stalled: this.stalledMs });
66784
+ };
66785
+ serial = handled.then(answered, answered);
66786
+ }
66433
66787
  response.then((value2) => child.send({ id: message.id, value: value2 }, replied), (error) => {
66434
66788
  const rootLeaseLost = isRootLeaseLost(error);
66435
66789
  if (rootLeaseLost)
@@ -66475,8 +66829,6 @@ class PoolCoordinator {
66475
66829
  return;
66476
66830
  }
66477
66831
  for (const work of expired) {
66478
- if (this.quarantined.has(work.id))
66479
- continue;
66480
66832
  if (this.held.has(work.id)) {
66481
66833
  await queue.extend(lease, work, leaseMs).catch((error) => {
66482
66834
  if (error.message !== STALE_WORK_LEASE)
@@ -66492,16 +66844,19 @@ class PoolCoordinator {
66492
66844
  });
66493
66845
  continue;
66494
66846
  }
66495
- if (child && !await this.stop(child).then(() => true, () => false))
66496
- continue;
66497
- if (this.held.has(work.id) || this.quarantined.has(work.id))
66847
+ if (child && !await this.stop(child))
66848
+ this.retireUnconfirmed(child);
66849
+ if (this.held.has(work.id))
66498
66850
  continue;
66499
66851
  if ((await unconfirmedProcessGroups(this.journal, root, { attempt: work })).length > 0)
66500
66852
  continue;
66501
66853
  if (work.epoch < lease.epoch)
66502
66854
  await this.releaseRetained(work);
66503
66855
  else
66504
- await queue.expire(lease, work);
66856
+ await queue.expire(lease, work).catch((error) => {
66857
+ if (error.message !== STALE_WORK_LEASE)
66858
+ throw error;
66859
+ });
66505
66860
  }
66506
66861
  })().finally(() => {
66507
66862
  this.recovering = undefined;
@@ -66519,19 +66874,51 @@ class PoolCoordinator {
66519
66874
  isLive(worker, child) {
66520
66875
  this.observeResponsiveness();
66521
66876
  const heard = this.lastHeard.get(worker);
66522
- return child.exitCode === null && child.signalCode === null && child.connected && heard !== undefined && performance.now() - heard.at - (this.stalledMs - heard.stalled) < this.options.leaseMs;
66877
+ 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);
66523
66878
  }
66524
- stop(child) {
66525
- if (child.pid === undefined || child.exitCode !== null || child.signalCode !== null)
66526
- return Promise.resolve();
66527
- return new Promise((resolve, reject) => {
66528
- const timeout = setTimeout(() => reject(new Error("Local worker cleanup was not confirmed")), 5000);
66529
- child.once("exit", () => {
66530
- clearTimeout(timeout);
66531
- resolve();
66532
- });
66533
- child.kill("SIGKILL");
66534
- });
66879
+ async stop(child) {
66880
+ const pid = child.pid;
66881
+ const exited = () => child.exitCode !== null || child.signalCode !== null;
66882
+ if (pid === undefined || exited())
66883
+ return true;
66884
+ const exit = new Promise((resolve) => child.once("exit", () => resolve()));
66885
+ const confirmed = async () => {
66886
+ await Promise.race([exit, delay2(WORKER_EXIT_CONFIRMATION_MS)]);
66887
+ return true;
66888
+ };
66889
+ this.observeResponsiveness();
66890
+ child.kill("SIGKILL");
66891
+ const since = performance.now();
66892
+ const stalledAt = this.stalledMs;
66893
+ for (;; ) {
66894
+ await Promise.race([exit, delay2(WORKER_EXIT_POLL_MS)]);
66895
+ if (exited())
66896
+ return true;
66897
+ if (workerProcessExited(pid))
66898
+ return confirmed();
66899
+ this.observeResponsiveness();
66900
+ if (performance.now() - since - (this.stalledMs - stalledAt) >= WORKER_EXIT_CONFIRMATION_MS)
66901
+ return exited() || workerProcessExited(pid, true) && confirmed();
66902
+ }
66903
+ }
66904
+ retireUnconfirmed(child) {
66905
+ if (this.unconfirmedExits.has(child))
66906
+ return;
66907
+ this.unconfirmedExits.add(child);
66908
+ this.reportHealth(this.logs.get(child), `Local worker cleanup was not confirmed: worker process ${child.pid} was still running ${WORKER_EXIT_CONFIRMATION_MS} ms after SIGKILL; it was retired and replaced`);
66909
+ if (child.connected)
66910
+ child.disconnect();
66911
+ for (const slot of this.slots)
66912
+ if (slot.child === child)
66913
+ slot.retire?.();
66914
+ }
66915
+ reportHealth(log, message) {
66916
+ if (log !== undefined) {
66917
+ this.keptLogs.add(log);
66918
+ appendFile(log, `[atomic ${new Date().toISOString()}] ${message}
66919
+ `).catch(() => {});
66920
+ }
66921
+ this.port.send({ kind: "health", message: log === undefined ? message : `${message} (worker log: ${log})` });
66535
66922
  }
66536
66923
  async close() {
66537
66924
  this.closed = true;
@@ -66541,10 +66928,15 @@ class PoolCoordinator {
66541
66928
  this.rateLimitOutbox.close();
66542
66929
  let failure;
66543
66930
  let stopping = [];
66931
+ let confirmed = [];
66544
66932
  try {
66545
66933
  await this.recovering?.catch(() => {});
66546
66934
  stopping = [...this.children].map((child) => ({ child, log: this.logs.get(child) }));
66547
- await Promise.all(stopping.map(({ child }) => this.stop(child)));
66935
+ confirmed = await Promise.all(stopping.map(({ child }) => this.stop(child)));
66936
+ stopping.forEach(({ child }, index) => {
66937
+ if (!confirmed[index])
66938
+ this.retireUnconfirmed(child);
66939
+ });
66548
66940
  await Promise.all([...this.cleanups]);
66549
66941
  if (this.cleanupFailure)
66550
66942
  throw this.cleanupFailure;
@@ -66556,15 +66948,15 @@ class PoolCoordinator {
66556
66948
  if (alive.length > 0)
66557
66949
  throw unconfirmedCleanup(alive);
66558
66950
  if (this.desiredControl !== "quit" && this.queue !== undefined && this.lease !== undefined)
66559
- await this.queue.releaseClosed(this.lease, [...this.quarantined]).catch((error) => {
66951
+ await this.queue.releaseClosed(this.lease).catch((error) => {
66560
66952
  if (!(error instanceof StaleLeaseError))
66561
66953
  throw error;
66562
66954
  });
66563
66955
  } catch (error) {
66564
66956
  failure = error;
66565
66957
  }
66566
- if (failure === undefined) {
66567
- const stopped = stopping.flatMap(({ child, log }) => log !== undefined && (child.exitCode !== null || child.signalCode !== null) ? [log] : []);
66958
+ if (failure === undefined && !this.failed) {
66959
+ const stopped = stopping.flatMap(({ log }, index) => log !== undefined && !this.keptLogs.has(log) && confirmed[index] === true ? [log] : []);
66568
66960
  await Promise.all(stopped.map((log) => rm3(log, { force: true }).catch(() => {})));
66569
66961
  if (this.logDirectory !== undefined)
66570
66962
  await rmdir(this.logDirectory).catch(() => {});
@@ -66616,7 +67008,7 @@ function runPoolCoordinator() {
66616
67008
  port.onClose(() => process.exit(0));
66617
67009
  return coordinator;
66618
67010
  }
66619
- var CONTROL_ACKNOWLEDGEMENT_STALL_MS = 5000, RECOVERY_INTERVAL_MS = 500, TICK_INTERVAL_MS = 100, STALL_THRESHOLD_MS = 500, COORDINATOR_LEASE_MS, LATE_ROOT_POLL_MS, MIRROR_INTERVAL_MS = 1e4, STDERR_TAIL_CHARACTERS = 8192, STDERR_DRAIN_GRACE_MS = 1000, MAX_CHILD_ATTEMPTS = 3, ROUTE_REGISTRATION_WAIT_MS = 120000, STAGE_WATCH_BACKLOG_EVENTS = 2000, STAGE_WATCH_BACKLOG_BYTES, PENDING_STAGE_MESSAGE_EVENT2 = "atomic:workflow-pending-stage-message", STICKY_LIVE_DELIVERED_EVENT2 = "atomic:workflow-sticky-live-delivered", PENDING_STAGE_UNDELIVERABLE_EVENT2 = "atomic:workflow-pending-stage-undeliverable", ENDED_WATCHES_REMEMBERED = 1024;
67011
+ var CONTROL_ACKNOWLEDGEMENT_STALL_MS = 5000, RECOVERY_INTERVAL_MS = 500, TICK_INTERVAL_MS = 100, STALL_THRESHOLD_MS = 500, COORDINATOR_LEASE_MS, LATE_ROOT_POLL_MS, MIRROR_INTERVAL_MS = 1e4, STDERR_TAIL_CHARACTERS = 8192, STDERR_DRAIN_GRACE_MS = 1000, WORKER_EXIT_CONFIRMATION_MS = 5000, WORKER_EXIT_POLL_MS = 50, MAX_CHILD_ATTEMPTS = 3, ROUTE_REGISTRATION_WAIT_MS = 120000, STAGE_WATCH_BACKLOG_EVENTS = 2000, STAGE_WATCH_BACKLOG_BYTES, PENDING_STAGE_MESSAGE_EVENT2 = "atomic:workflow-pending-stage-message", STICKY_LIVE_DELIVERED_EVENT2 = "atomic:workflow-sticky-live-delivered", PENDING_STAGE_UNDELIVERABLE_EVENT2 = "atomic:workflow-pending-stage-undeliverable", ENDED_WATCHES_REMEMBERED = 1024;
66620
67012
  var init_pool_coordinator = __esm(() => {
66621
67013
  init_internal();
66622
67014
  init_pool_remote_journal();
@@ -66631,6 +67023,7 @@ var init_pool_coordinator = __esm(() => {
66631
67023
  init_pending_stage_route_readiness();
66632
67024
  init_store();
66633
67025
  init_workflow_infrastructure_error();
67026
+ init_admission_host();
66634
67027
  init_pool_journal();
66635
67028
  init_pooled_watch_trace();
66636
67029
  init_resource_admission();
@@ -66667,26 +67060,6 @@ function isWorkflowPendingStageDeliveryFailure(error) {
66667
67060
  return true;
66668
67061
  return error?.code === "pending_stage_delivery_failed";
66669
67062
  }
66670
- function untilDrainedOrStalled(drain) {
66671
- let lastProgressAt = Date.now();
66672
- const running = drain(() => {
66673
- lastProgressAt = Date.now();
66674
- });
66675
- return new Promise((resolve, reject) => {
66676
- let timer;
66677
- const check = () => {
66678
- const remaining = lastProgressAt + PENDING_STAGE_READY_STALL_MS - Date.now();
66679
- if (remaining <= 0) {
66680
- resolve();
66681
- return;
66682
- }
66683
- timer = setTimeout(check, remaining);
66684
- timer.unref?.();
66685
- };
66686
- running.then(resolve, reject).finally(() => clearTimeout(timer));
66687
- check();
66688
- });
66689
- }
66690
67063
  function createWorkflowPendingStageDelivery(activeStore, runId, stageId, stageName, commit = (action) => action(), options = {}) {
66691
67064
  let resolveReady;
66692
67065
  let rejectReady;
@@ -66879,7 +67252,7 @@ function createWorkflowPendingStageDelivery(activeStore, runId, stageId, stageNa
66879
67252
  };
66880
67253
  }
66881
67254
  async function deliverPendingStageMessages(activeStore, runId, stageId, stageName, deliver, commit, recipient) {
66882
- const stickyEntries = stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient);
67255
+ const stickyEntries = stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient, recipient?.receivedMessageIds);
66883
67256
  const candidateIds = new Set([
66884
67257
  ...activeStore.pendingStageMessagesFor(runId, stageId),
66885
67258
  ...activeStore.pendingStageMessagesFor(runId, stageName),
@@ -66935,7 +67308,7 @@ async function deliverPendingStageMessages(activeStore, runId, stageId, stageNam
66935
67308
  }
66936
67309
  }
66937
67310
  }
66938
- function stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient) {
67311
+ function stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient, receivedByEndedStage = []) {
66939
67312
  const runs = activeStore.runs();
66940
67313
  const rootRunId = durableRootRunIdForRun(runs, runId);
66941
67314
  if (rootRunId === undefined)
@@ -66944,22 +67317,28 @@ function stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageNam
66944
67317
  if (rootRun === undefined)
66945
67318
  return [];
66946
67319
  const stage = runs.find((run) => run.id === runId)?.stages.find((candidate) => candidate.id === stageId);
66947
- if (stage?.status === "completed" || stage?.status === "skipped")
67320
+ const ended = stage?.status === "completed" || stage?.status === "skipped";
67321
+ if (ended && receivedByEndedStage.length === 0)
66948
67322
  return [];
66949
67323
  const hops = workflowBoundaryHops(runs, runId);
66950
67324
  if (hops === undefined)
66951
67325
  return [];
66952
67326
  return (rootRun.pendingStageMessages ?? []).filter((entry) => {
66953
- if (entry.sticky !== true || entry.status !== "queued")
66954
- return false;
66955
- const parsed = entry.targetPath === undefined ? undefined : parseWorkflowStageTarget(entry.targetPath);
66956
- if (parsed === undefined || parsed.rootRunId !== rootRunId)
66957
- return false;
66958
67327
  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)))))
66959
67328
  return false;
66960
- return stageMatchesPathPattern(parsed.segments, hops, [stageId, stageName]);
67329
+ if (ended && !receivedByEndedStage.includes(entry.message.id))
67330
+ return false;
67331
+ return stickyEntryTargetsStage(entry, rootRunId, hops, stageId, stageName);
66961
67332
  });
66962
67333
  }
67334
+ function stickyEntryTargetsStage(entry, rootRunId, hops, stageId, stageName) {
67335
+ if (entry.sticky !== true || entry.status !== "queued")
67336
+ return false;
67337
+ const parsed = entry.targetPath === undefined ? undefined : parseWorkflowStageTarget(entry.targetPath);
67338
+ if (parsed === undefined || parsed.rootRunId !== rootRunId)
67339
+ return false;
67340
+ return stageMatchesPathPattern(parsed.segments, hops, [stageId, stageName]);
67341
+ }
66963
67342
  function claimPendingDelivery(store, runId, stageKey, messageId) {
66964
67343
  let claims = pendingDeliveryClaims.get(store);
66965
67344
  if (claims === undefined) {
@@ -67023,6 +67402,7 @@ class PoolStageHandoff {
67023
67402
  owners = new Map;
67024
67403
  publications = new Map;
67025
67404
  drains = new Map;
67405
+ deliveryIssues = 0;
67026
67406
  constructor(backend, store, queue, lease, claimRun) {
67027
67407
  this.backend = backend;
67028
67408
  this.store = store;
@@ -67103,11 +67483,23 @@ class PoolStageHandoff {
67103
67483
  this.drains.set(stage, drain);
67104
67484
  try {
67105
67485
  await drain;
67486
+ } catch (error) {
67487
+ const current = this.attempts.get(work.id) === attempt && !attempt.fenced;
67488
+ if (current && attempt.sessions.get(stage) === recipient.sessionId && error.message !== STALE)
67489
+ this.recordDeliveryIssue(error);
67490
+ throw error;
67106
67491
  } finally {
67107
67492
  if (this.drains.get(stage) === drain)
67108
67493
  this.drains.delete(stage);
67109
67494
  }
67110
67495
  }
67496
+ recordDeliveryIssue(error) {
67497
+ this.deliveryIssues++;
67498
+ const drains = this.deliveryIssues === 1 ? "1 stage drain" : `${this.deliveryIssues} stage drains`;
67499
+ this.store.recordRunExecutionState(this.lease.runId, {
67500
+ intercomDeliveryIssue: `${drains} on local workers failed; undelivered messages stay queued and arrive live or on the stage's next drain (last: ${error.message})`
67501
+ });
67502
+ }
67111
67503
  async complete(work) {
67112
67504
  const root = this.lease.runId;
67113
67505
  const result = await this.queue.result(root, work.id);
@@ -68463,7 +68855,7 @@ var init_pooled_stage_registry = __esm(() => {
68463
68855
  // dist/builtin/workflows/src/workers/local-pool.ts
68464
68856
  import { fork as fork2 } from "node:child_process";
68465
68857
  import { randomUUID as randomUUID18 } from "node:crypto";
68466
- import { resolve as resolve13 } from "node:path";
68858
+ import { resolve as resolve14 } from "node:path";
68467
68859
  import { setTimeout as delay3 } from "node:timers/promises";
68468
68860
  import { fileURLToPath as fileURLToPath5 } from "node:url";
68469
68861
  import {
@@ -68581,6 +68973,7 @@ class LocalWorkerPool {
68581
68973
  works = new Map;
68582
68974
  attempts = new Map;
68583
68975
  results = new Map;
68976
+ healthEvents = [];
68584
68977
  workFailures = new Map;
68585
68978
  changed = Promise.withResolvers();
68586
68979
  waiting = 0;
@@ -68638,6 +69031,9 @@ class LocalWorkerPool {
68638
69031
  if (options.admission && options.rootId === undefined)
68639
69032
  workflowResourceAdmission.set(backend, options.admission);
68640
69033
  }
69034
+ get health() {
69035
+ return this.healthEvents;
69036
+ }
68641
69037
  get workerPids() {
68642
69038
  return [...this.pids];
68643
69039
  }
@@ -68693,7 +69089,7 @@ class LocalWorkerPool {
68693
69089
  async poolJournal() {
68694
69090
  const journal = this.backend.journal;
68695
69091
  if (this.options.durable === undefined) {
68696
- if (!(journal instanceof SqlRunJournal) || resolve13(this.options.journalPath) !== resolve13(journal.path) || sqlPoolStorage(journal)?.tables.runs !== "wf_runs")
69092
+ if (!(journal instanceof SqlRunJournal) || resolve14(this.options.journalPath) !== resolve14(journal.path) || sqlPoolStorage(journal)?.tables.runs !== "wf_runs")
68697
69093
  throw new Error("Local workers require the same default-prefix SQLite journal");
68698
69094
  return journal;
68699
69095
  }
@@ -69231,14 +69627,8 @@ class LocalWorkerPool {
69231
69627
  try {
69232
69628
  return await this.dispatch(definition, inputs, options);
69233
69629
  } catch (error) {
69234
- if (error instanceof WorkflowInfrastructureError && [...this.workFailures.values()].includes(error))
69235
- throw error;
69236
- try {
69237
- if (this.desiredControl !== "quit" || !findWorkflowGracefulQuit(error))
69238
- await this.shutdown();
69239
- } catch (closeError) {
69240
- throw asWorkflowInfrastructureError(closeError) ?? closeError;
69241
- }
69630
+ if (this.failure !== undefined || this.coordinatorLost !== undefined)
69631
+ await this.shutdown().catch(() => {});
69242
69632
  throw asWorkflowInfrastructureError(error) ?? error;
69243
69633
  }
69244
69634
  }
@@ -69306,8 +69696,13 @@ class LocalWorkerPool {
69306
69696
  try {
69307
69697
  for (;; ) {
69308
69698
  options.signal?.throwIfAborted();
69309
- if (this.closed)
69310
- throw this.closedError(root);
69699
+ if (!this.results.has(work.id) && (this.closed || this.failure !== undefined)) {
69700
+ const recorded = await this.queue?.result(root, work.id).catch(() => {
69701
+ return;
69702
+ });
69703
+ if (recorded !== undefined)
69704
+ this.results.set(work.id, recorded);
69705
+ }
69311
69706
  const result = this.results.get(work.id);
69312
69707
  if (result) {
69313
69708
  this.completing++;
@@ -69331,6 +69726,8 @@ class LocalWorkerPool {
69331
69726
  this.endFailedChild(work, failed);
69332
69727
  throw failed;
69333
69728
  }
69729
+ if (this.closed)
69730
+ throw this.closedError(root);
69334
69731
  if (this.failure)
69335
69732
  throw this.failure;
69336
69733
  if (this.budgetBlocked)
@@ -69365,6 +69762,19 @@ class LocalWorkerPool {
69365
69762
  if (this.reconnectingViews.size > 0 || this.outboxes.size > 0)
69366
69763
  this.reconnectStageViews();
69367
69764
  }
69765
+ recordHealth(message) {
69766
+ this.healthEvents.push(message);
69767
+ const root = this.root;
69768
+ if (root === undefined || this.store === undefined)
69769
+ return;
69770
+ this.store.recordNotice({
69771
+ id: `local-worker-health:${root}:${this.healthEvents.length}`,
69772
+ runId: root,
69773
+ level: "warning",
69774
+ message: `Local worker pool: ${message}`,
69775
+ createdAt: Date.now()
69776
+ });
69777
+ }
69368
69778
  evaluateBudget() {
69369
69779
  const decision = this.rootBudget?.checkpoint();
69370
69780
  const blocked = decision?.kind === "exhausted" || decision?.kind === "wrap_up";
@@ -69483,6 +69893,9 @@ class LocalWorkerPool {
69483
69893
  this.failure ??= new Error(event.error);
69484
69894
  this.signalChange();
69485
69895
  return;
69896
+ case "health":
69897
+ this.recordHealth(event.message);
69898
+ return;
69486
69899
  case "stage-events":
69487
69900
  this.stageWatches.get(event.watch)?.({ kind: "events", events: event.events });
69488
69901
  this.notice({ kind: "stage-events-applied", watch: event.watch });
@@ -69703,7 +70116,7 @@ var init_local_pool = __esm(() => {
69703
70116
  var DEFAULT_POOLED_STAGE_START_MAX_LOAD_PER_CPU = 3;
69704
70117
 
69705
70118
  // dist/builtin/workflows/src/workers/public-pool.ts
69706
- import { resolve as resolve14 } from "node:path";
70119
+ import { resolve as resolve15 } from "node:path";
69707
70120
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
69708
70121
  async function restoreWorkerOptions(backend, options) {
69709
70122
  const source = options.continuation?.source.id ?? options.runId;
@@ -69753,16 +70166,16 @@ async function activatePublicPool(backend, options) {
69753
70166
  startupTimeoutMs: undefined,
69754
70167
  leaseMs: undefined,
69755
70168
  maxLoadPerCpu: undefined,
69756
- definitions: options.localWorkers.definitions.map((source) => ({ ...source, path: resolve14(cwd, source.path) }))
70169
+ definitions: options.localWorkers.definitions.map((source) => ({ ...source, path: resolve15(cwd, source.path) }))
69757
70170
  };
69758
70171
  const admission = options.admission && {
69759
70172
  ...options.admission,
69760
- ...options.admission.userDatabase ? { userDatabase: resolve14(cwd, options.admission.userDatabase) } : {}
70173
+ ...options.admission.userDatabase ? { userDatabase: resolve15(cwd, options.admission.userDatabase) } : {}
69761
70174
  };
69762
70175
  if (durable !== undefined && admission?.userLimits) {
69763
- if (admission.userDatabase !== undefined && admission.userDatabase !== resolve14(poolPath))
69764
- throw new Error(`admission.userDatabase must be omitted, or ${resolve14(poolPath)}, for a journal that is not a local SQLite file`);
69765
- admission.userDatabase = resolve14(poolPath);
70176
+ if (admission.userDatabase !== undefined && admission.userDatabase !== resolve15(poolPath))
70177
+ throw new Error(`admission.userDatabase must be omitted, or ${resolve15(poolPath)}, for a journal that is not a local SQLite file`);
70178
+ admission.userDatabase = resolve15(poolPath);
69766
70179
  }
69767
70180
  const sources = localWorkers && await Promise.all(localWorkers.definitions.map(async (source) => ({ ...source, digest: await definitionDigest(source.path) })));
69768
70181
  const resources = JSON.parse(JSON.stringify({
@@ -70368,6 +70781,7 @@ function createTaskPrimitive(runtime) {
70368
70781
  }
70369
70782
  })();
70370
70783
  const stageMeta = stage.__modelFallbackMeta();
70784
+ stage.__releaseIfIdle?.();
70371
70785
  return {
70372
70786
  name,
70373
70787
  stageName: name,
@@ -71166,7 +71580,7 @@ function hasUsableContentBlock(block) {
71166
71580
 
71167
71581
  // dist/builtin/workflows/src/shared/stage-session-store.ts
71168
71582
  import { randomUUID as randomUUID20 } from "node:crypto";
71169
- import { dirname as dirname14, resolve as resolve15 } from "node:path";
71583
+ import { dirname as dirname15, resolve as resolve16 } from "node:path";
71170
71584
  import {
71171
71585
  JsonlSessionStore,
71172
71586
  resolveSessionStore,
@@ -71202,11 +71616,11 @@ async function forkStageSession(store, sessionId, cwd, options, destination = st
71202
71616
  async function migrateLegacyStageSession(store, sessionId, sessionFile) {
71203
71617
  if (sessionFile === undefined || await store.readHeader(sessionId) !== undefined)
71204
71618
  return;
71205
- const location = resolve15(sessionFile);
71206
- const legacy = await JsonlSessionStore.open(dirname14(location), { exactDirectory: true, scanExisting: false });
71619
+ const location = resolve16(sessionFile);
71620
+ const legacy = await JsonlSessionStore.open(dirname15(location), { exactDirectory: true, scanExisting: false });
71207
71621
  try {
71208
71622
  for await (const file of legacy.scanFiles()) {
71209
- if (resolve15(file.location) !== location || file.header.id !== sessionId)
71623
+ if (resolve16(file.location) !== location || file.header.id !== sessionId)
71210
71624
  continue;
71211
71625
  const snapshot = await file.read();
71212
71626
  if (await store.readHeader(sessionId) === undefined)
@@ -74032,6 +74446,8 @@ function createTrackedStageCaller(input) {
74032
74446
  await runtime.releaseLiveHandle().catch(() => {});
74033
74447
  }
74034
74448
  runtime.state.sessionCheckpointed = finalizationError === undefined;
74449
+ if (runtime.state.contextUnreachable)
74450
+ runtime.stageRegistry.releaseIdle(runtime.runId, runtime.stageId);
74035
74451
  input.limiter.release();
74036
74452
  if (finalizationError !== undefined)
74037
74453
  throwFinalizationError(finalizationError.error);
@@ -74195,7 +74611,8 @@ function createStageContext(input) {
74195
74611
  recordStageNotice({ kind: "abort", to: "interrupted" });
74196
74612
  await innerCtx.abort();
74197
74613
  },
74198
- __modelFallbackMeta: () => innerCtx.__modelFallbackMeta()
74614
+ __modelFallbackMeta: () => innerCtx.__modelFallbackMeta(),
74615
+ __releaseIfIdle: () => runtime.stageRegistry.releaseIdle(runtime.runId, runtime.stageId)
74199
74616
  };
74200
74617
  }
74201
74618
 
@@ -75884,10 +76301,10 @@ function isRetryableSameCandidateFailure(error) {
75884
76301
  return isTransientExtensionStartupFailure(error);
75885
76302
  return isRetryableSameModelFailure(error);
75886
76303
  }
75887
- function mayAdvanceToNextCandidate(error) {
76304
+ function mayAdvanceToNextCandidate(error, options) {
75888
76305
  if (isExtensionStartupFailure(error))
75889
76306
  return isTransientExtensionStartupFailure(error);
75890
- return isRetryableModelFailure(error);
76307
+ return isRetryableModelFailure(error, options);
75891
76308
  }
75892
76309
  function hasMeaningfulUsage(usage) {
75893
76310
  if (usage === undefined)
@@ -75979,6 +76396,7 @@ class StageSessionController {
75979
76396
  structuredOutputExecutionFailureValue;
75980
76397
  structuredOutputCycleAttemptMark;
75981
76398
  unsubscribeTerminateWatcher;
76399
+ unbindAbortSignal;
75982
76400
  sessionInternalRetries = 0;
75983
76401
  unresolvedContextOverflowMessage;
75984
76402
  generationSealed = false;
@@ -76468,6 +76886,8 @@ class StageSessionController {
76468
76886
  this.pendingListeners.clear();
76469
76887
  this.unsubscribeTerminateWatcher?.();
76470
76888
  this.unsubscribeTerminateWatcher = undefined;
76889
+ this.unbindAbortSignal?.();
76890
+ this.unbindAbortSignal = undefined;
76471
76891
  this.terminatingToolCallIds.clear();
76472
76892
  this.messageAdmission.dispose();
76473
76893
  this.deliveryActivity.dispose();
@@ -76562,8 +76982,10 @@ class StageSessionController {
76562
76982
  };
76563
76983
  if (signal.aborted)
76564
76984
  onAbort();
76565
- else
76985
+ else {
76566
76986
  signal.addEventListener("abort", onAbort, { once: true });
76987
+ this.unbindAbortSignal = () => signal.removeEventListener("abort", onAbort);
76988
+ }
76567
76989
  }
76568
76990
  markAbort(reason) {
76569
76991
  this.abortGeneration += 1;
@@ -76590,6 +77012,9 @@ class StageSessionController {
76590
77012
  state.controller.abort(reason);
76591
77013
  this.thrownErrorRetryStates.clear();
76592
77014
  }
77015
+ fallbackFailureOptions() {
77016
+ return { fallbackOnContentFilter: this.retrySettings()?.fallbackOnContentFilter !== false };
77017
+ }
76593
77018
  retrySettings() {
76594
77019
  const managers = [
76595
77020
  this.sessionSettingsManager,
@@ -76864,7 +77289,7 @@ class StageSessionController {
76864
77289
  const errorSettingsManager = retrySettingsManagerFromError(error);
76865
77290
  if (errorSettingsManager !== undefined)
76866
77291
  this.sessionSettingsManager = errorSettingsManager;
76867
- const retryableFailure = isRetryableModelFailure(error);
77292
+ const retryableFailure = isRetryableModelFailure(error, this.fallbackFailureOptions());
76868
77293
  const sameCandidateRetryable = isRetryableSameModelFailure(error) && !isUnresolvedContextOverflowFailure(error);
76869
77294
  recordProviderRateLimit(this.currentProvider(), error, modelIdForModel(this.currentModelValue()));
76870
77295
  if (this.hasExplicitModelFallbackConfig && isProviderRateLimit(error)) {
@@ -77489,7 +77914,7 @@ class StageSessionController {
77489
77914
  }
77490
77915
  throw new WorkflowPromptModelFailure(terminalFailure);
77491
77916
  } catch (err) {
77492
- if (this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err)) {
77917
+ if (this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err, this.fallbackFailureOptions())) {
77493
77918
  this.artifactCapture.settleAttempt(true);
77494
77919
  const usage = this.takeAttemptUsage(resumedSession);
77495
77920
  this.modelAttempts.push({
@@ -77511,7 +77936,7 @@ class StageSessionController {
77511
77936
  ...usage === undefined ? {} : { usage },
77512
77937
  error: message
77513
77938
  });
77514
- if (this.opts.signal?.aborted || !isRetryableModelFailure(err)) {
77939
+ if (this.opts.signal?.aborted || !isRetryableModelFailure(err, this.fallbackFailureOptions())) {
77515
77940
  this.modelWarnings.push(...this.pendingFallbackWarnings);
77516
77941
  this.pendingFallbackWarnings.length = 0;
77517
77942
  this.notifyModelFallbackMetaChange();
@@ -77560,7 +77985,7 @@ class StageSessionController {
77560
77985
  throw err;
77561
77986
  const message = errorMessage3(err);
77562
77987
  const terminalStageDelivery = isWorkflowPendingStageDeliveryFailure(err);
77563
- if (!terminalStageDelivery && this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err)) {
77988
+ if (!terminalStageDelivery && this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err, this.fallbackFailureOptions())) {
77564
77989
  this.recordSuccessfulAttempt(candidate);
77565
77990
  return "handled";
77566
77991
  }
@@ -77577,7 +78002,7 @@ class StageSessionController {
77577
78002
  if (walk !== undefined) {
77578
78003
  this.recordCandidateFailure(walk, index, err, candidates);
77579
78004
  nextIndex = undefined;
77580
- if (!this.opts.signal?.aborted && !this.startupWait.signal.aborted && !terminalStageDelivery && mayAdvanceToNextCandidate(err)) {
78005
+ if (!this.opts.signal?.aborted && !this.startupWait.signal.aborted && !terminalStageDelivery && mayAdvanceToNextCandidate(err, this.fallbackFailureOptions())) {
77581
78006
  try {
77582
78007
  nextIndex = await this.selectReadyCandidate(candidates, walk, index + 1);
77583
78008
  } catch (error) {
@@ -77590,7 +78015,7 @@ class StageSessionController {
77590
78015
  }
77591
78016
  }
77592
78017
  }
77593
- if (this.opts.signal?.aborted || this.startupWait.signal.aborted || terminalStageDelivery || !mayAdvanceToNextCandidate(err) || nextIndex === undefined) {
78018
+ if (this.opts.signal?.aborted || this.startupWait.signal.aborted || terminalStageDelivery || !mayAdvanceToNextCandidate(err, this.fallbackFailureOptions()) || nextIndex === undefined) {
77594
78019
  await this.settleFailedCandidate();
77595
78020
  return "throw";
77596
78021
  }
@@ -77685,7 +78110,7 @@ var init_stage_runner_controller = __esm(() => {
77685
78110
  // dist/builtin/workflows/src/runs/foreground/stage-runner-output.ts
77686
78111
  import { createHash as createHash5 } from "node:crypto";
77687
78112
  import { mkdir as mkdir7, writeFile as writeFile10 } from "node:fs/promises";
77688
- import { basename as basename5, dirname as dirname15, isAbsolute as isAbsolute11, join as join30, resolve as resolve16 } from "node:path";
78113
+ import { basename as basename5, dirname as dirname16, isAbsolute as isAbsolute11, join as join31, resolve as resolve17 } from "node:path";
77689
78114
  function normalizeMaxOutput2(maxOutput) {
77690
78115
  return {
77691
78116
  bytes: maxOutput?.bytes ?? DEFAULT_MAX_OUTPUT_BYTES,
@@ -77748,7 +78173,7 @@ function formatByteSize(bytes) {
77748
78173
  }
77749
78174
  function transcriptPath(runId, outputPath) {
77750
78175
  const digest = createHash5("sha256").update(outputPath).digest("hex").slice(0, 16);
77751
- return join30(workflowArtifactRunPath(runId), "transcripts", `${digest}-${basename5(outputPath)}.transcript.md`);
78176
+ return join31(workflowArtifactRunPath(runId), "transcripts", `${digest}-${basename5(outputPath)}.transcript.md`);
77752
78177
  }
77753
78178
  function appendContent(lines, content) {
77754
78179
  if (typeof content === "string") {
@@ -77793,7 +78218,7 @@ function renderTranscript(messages, fallbackOutput) {
77793
78218
  `);
77794
78219
  }
77795
78220
  function savedOutputReference(outputPath, fullOutput, transcriptPathValue, transcriptText, transcriptAvailable, warning) {
77796
- const absolutePath = resolve16(outputPath);
78221
+ const absolutePath = resolve17(outputPath);
77797
78222
  const bytes = Buffer.byteLength(fullOutput, "utf8");
77798
78223
  const lines = countLines(fullOutput);
77799
78224
  const transcriptBytes = Buffer.byteLength(transcriptText, "utf8");
@@ -77808,8 +78233,8 @@ function resolveOutputPath(output, runtimeCwd, requestedCwd) {
77808
78233
  return;
77809
78234
  if (isAbsolute11(output))
77810
78235
  return output;
77811
- const baseCwd = requestedCwd === undefined ? runtimeCwd : isAbsolute11(requestedCwd) ? requestedCwd : resolve16(runtimeCwd, requestedCwd);
77812
- return resolve16(baseCwd, output);
78236
+ const baseCwd = requestedCwd === undefined ? runtimeCwd : isAbsolute11(requestedCwd) ? requestedCwd : resolve17(runtimeCwd, requestedCwd);
78237
+ return resolve17(baseCwd, output);
77813
78238
  }
77814
78239
  function degenerateWarning(fullOutput) {
77815
78240
  return fullOutput.trim().length === 0 ? "WARNING: the stage artifact is empty; search the companion transcript for this stage's work." : undefined;
@@ -77868,7 +78293,7 @@ async function finalizePromptOutput(fullOutput, outputOptions, runtimeCwd, runId
77868
78293
  const transcriptFile = transcriptPath(runId, outputPath);
77869
78294
  const transcript = renderTranscript(messages, fullOutput);
77870
78295
  try {
77871
- await mkdir7(dirname15(outputPath), { recursive: true });
78296
+ await mkdir7(dirname16(outputPath), { recursive: true });
77872
78297
  await writeFile10(outputPath, fullOutput, "utf8");
77873
78298
  } catch (err) {
77874
78299
  return `${displayOutput}
@@ -77880,7 +78305,7 @@ ${err instanceof Error ? err.message : String(err)}`;
77880
78305
  let transcriptError;
77881
78306
  try {
77882
78307
  await ensureWorkflowArtifactRunDirectory(runId);
77883
- await mkdir7(dirname15(transcriptFile), { recursive: true });
78308
+ await mkdir7(dirname16(transcriptFile), { recursive: true });
77884
78309
  await writeFile10(transcriptFile, transcript, "utf8");
77885
78310
  transcriptAvailable = true;
77886
78311
  } catch (err) {
@@ -78391,7 +78816,8 @@ function createWorkflowStageFactory(input) {
78391
78816
  stageFinalized: false,
78392
78817
  sessionCheckpointed: false,
78393
78818
  skippedForParallelFailFast: false,
78394
- stageControlDropped: false
78819
+ stageControlDropped: false,
78820
+ contextUnreachable: false
78395
78821
  };
78396
78822
  const activeAskUserQuestionCalls = new Set;
78397
78823
  const hasActiveAskUserQuestion = () => activeAskUserQuestionCalls.size > 0 || state.activeAskUserQuestionAnonymousCalls > 0;
@@ -78637,9 +79063,18 @@ function createWorkflowStageFactory(input) {
78637
79063
  hasContinuation: input.opts.continuation !== undefined,
78638
79064
  hasScopedParents: stageFailFastScope?.parentIds !== undefined
78639
79065
  });
78640
- return createStageContext({ runtime, runTrackedStageCall });
79066
+ const context = createStageContext({ runtime, runTrackedStageCall });
79067
+ unreachableStageContexts.register(context, releaseWhenUnreachable(state, input.stageRegistry, input.runId, stageId));
79068
+ return context;
79069
+ };
79070
+ }
79071
+ function releaseWhenUnreachable(state, registry, runId, stageId) {
79072
+ return () => {
79073
+ state.contextUnreachable = true;
79074
+ registry.releaseIdle(runId, stageId);
78641
79075
  };
78642
79076
  }
79077
+ var unreachableStageContexts;
78643
79078
  var init_executor_stage_factory = __esm(() => {
78644
79079
  init_intercom_group();
78645
79080
  init_pending_stage_route_readiness();
@@ -78654,6 +79089,7 @@ var init_executor_stage_factory = __esm(() => {
78654
79089
  init_pending_stage_delivery2();
78655
79090
  init_stage_runner();
78656
79091
  init_stage_runner_session();
79092
+ unreachableStageContexts = new FinalizationRegistry((release) => release());
78657
79093
  });
78658
79094
 
78659
79095
  // dist/builtin/workflows/src/engine/runtime.ts
@@ -78995,7 +79431,7 @@ async function executeRun(def, resolvedInputs, opts) {
78995
79431
  const ownsCancellationRegistration = opts.signal === undefined && opts.cancellation !== undefined;
78996
79432
  const tracker = new GraphFrontierTracker;
78997
79433
  const inputConcurrency = resolveInputConcurrency(def.inputs, resolvedInputs);
78998
- const inputRuntimeDefaults = resolveInputRuntimeDefaults(def, resolvedInputs), gitWorktreeSetupCacheOwner = createGitWorktreeSetupCacheOwner(opts.gitWorktreeSetupCache);
79434
+ const inputRuntimeDefaults = resolveInputRuntimeDefaults(def, resolvedInputs), gitWorktreeSetupCacheOwner = createGitWorktreeSetupCacheOwner(opts.gitWorktreeSetupCache, runId);
78999
79435
  const gitWorktreeSetupCache = gitWorktreeSetupCacheOwner.cache;
79000
79436
  const workflowInvocationCwd = opts.cwd ?? process.cwd();
79001
79437
  let workflowCwd;
@@ -80094,8 +80530,8 @@ var init_workflows_host_module = __esm(() => {
80094
80530
 
80095
80531
  // dist/builtin/workflows/src/workers/pool-journal.ts
80096
80532
  import { createHash as createHash6, randomUUID as randomUUID22 } from "node:crypto";
80097
- import { existsSync as existsSync7, mkdirSync as mkdirSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
80098
- import { basename as basename6, dirname as dirname16, join as join31, resolve as resolve17 } from "node:path";
80533
+ import { existsSync as existsSync7, mkdirSync as mkdirSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "node:fs";
80534
+ import { basename as basename6, dirname as dirname17, join as join32, resolve as resolve18 } from "node:path";
80099
80535
  import { storageSelectionKey as storageSelectionKey2 } from "@bastani/atomic/storage";
80100
80536
  function encodeOption(value2) {
80101
80537
  if (typeof value2 === "bigint")
@@ -80117,7 +80553,7 @@ function isReopenableSelection(selection) {
80117
80553
  }
80118
80554
  function encodeSelection(selection) {
80119
80555
  if (selection.kind === "sqlite")
80120
- return JSON.stringify({ ...selection, path: resolve17(selection.path) });
80556
+ return JSON.stringify({ ...selection, path: resolve18(selection.path) });
80121
80557
  return JSON.stringify({
80122
80558
  ...selection,
80123
80559
  ...selection.options === undefined ? {} : {
@@ -80149,7 +80585,7 @@ function openDurableJournal(durable) {
80149
80585
  return openSelectedRunJournal(decodeSelection(durable.selection), durable.agentDir);
80150
80586
  }
80151
80587
  function poolStateDirectory(agentDir) {
80152
- return join31(dirname16(agentDir), "workflows", "pool");
80588
+ return join32(dirname17(agentDir), "workflows", "pool");
80153
80589
  }
80154
80590
  function databaseIdentity(selection, agentDir) {
80155
80591
  return storageSelectionKey2(selection.kind === "adapter" ? { ...selection, secrets: undefined } : selection, agentDir);
@@ -80157,16 +80593,16 @@ function databaseIdentity(selection, agentDir) {
80157
80593
  function coordinationPath(durable) {
80158
80594
  const selection = decodeSelection(durable.selection);
80159
80595
  const identity = createHash6("sha256").update(databaseIdentity(selection, durable.agentDir)).digest("hex");
80160
- return join31(poolStateDirectory(durable.agentDir), `${identity.slice(0, 24)}.sqlite`);
80596
+ return join32(poolStateDirectory(durable.agentDir), `${identity.slice(0, 24)}.sqlite`);
80161
80597
  }
80162
80598
  function coordinationDatabase(durable) {
80163
80599
  return basename6(coordinationPath(durable), ".sqlite");
80164
80600
  }
80165
80601
  function openCoordinationJournal(path) {
80166
- const file = resolve17(path);
80602
+ const file = resolve18(path);
80167
80603
  let opened = coordinationJournals.get(file);
80168
80604
  if (opened === undefined) {
80169
- mkdirSync8(dirname16(file), { recursive: true });
80605
+ mkdirSync9(dirname17(file), { recursive: true });
80170
80606
  opened = resolveRunJournal({ journal: { path: file } });
80171
80607
  coordinationJournals.set(file, opened);
80172
80608
  opened.catch(() => {
@@ -80183,20 +80619,20 @@ function poolJournalFor(journal) {
80183
80619
  return coordinations.get(journal) ?? journal;
80184
80620
  }
80185
80621
  function poolHostId(agentDir) {
80186
- const file = join31(poolStateDirectory(agentDir), "host-id");
80622
+ const file = join32(poolStateDirectory(agentDir), "host-id");
80187
80623
  try {
80188
- const existing = readFileSync6(file, "utf8").trim();
80624
+ const existing = readFileSync8(file, "utf8").trim();
80189
80625
  if (existing !== "")
80190
80626
  return existing;
80191
80627
  } catch {}
80192
- mkdirSync8(dirname16(file), { recursive: true });
80628
+ mkdirSync9(dirname17(file), { recursive: true });
80193
80629
  const id = randomUUID22();
80194
80630
  try {
80195
- writeFileSync6(file, `${id}
80631
+ writeFileSync7(file, `${id}
80196
80632
  `, { flag: "wx" });
80197
80633
  return id;
80198
80634
  } catch {
80199
- return readFileSync6(file, "utf8").trim();
80635
+ return readFileSync8(file, "utf8").trim();
80200
80636
  }
80201
80637
  }
80202
80638
  var POOL_DURABLE_JOURNAL_ENV = "ATOMIC_POOL_DURABLE_JOURNAL", coordinationJournals, coordinations;
@@ -80219,7 +80655,6 @@ init_run();
80219
80655
  init_run_tool_control_registry();
80220
80656
  init_workflow_tool_abort();
80221
80657
  init_wiring();
80222
- init_pending_stage_delivery2();
80223
80658
  init_stage_control_registry();
80224
80659
  init_stage_queued_user_messages();
80225
80660
  init_stage_ui_broker();
@@ -80245,6 +80680,94 @@ function poolWorkerCommit(input) {
80245
80680
  return receipt;
80246
80681
  };
80247
80682
  }
80683
+ // dist/builtin/workflows/src/workers/pooled-stage-delivery.ts
80684
+ init_pending_stage_delivery2();
80685
+ init_pending_stage_delivery();
80686
+ init_store_pending_stage_delivery_methods();
80687
+ function pooledStageQueuedEntries(journal, store, root, stage) {
80688
+ const messages = journal.getWorkflow(root)?.pendingStageMessages ?? [];
80689
+ const runs = store.runs();
80690
+ const run = runs.find((candidate) => candidate.id === stage.runId);
80691
+ if (run === undefined)
80692
+ return [];
80693
+ const exact = new Set;
80694
+ for (const key of new Set([stage.stageId, stage.stageName]))
80695
+ for (const entry of pendingStageMessagesFor(messages, stage.runId, key, resolvePendingStageIdentity(run, key)))
80696
+ exact.add(entry);
80697
+ const hops = pooledStageHops(journal, runs, root, stage.runId);
80698
+ const sticky = hops === undefined ? [] : messages.filter((entry) => entry.runId === root && stickyEntryTargetsStage(entry, root, hops, stage.stageId, stage.stageName));
80699
+ return [
80700
+ ...[...exact].map((entry) => ({ entry, index: messages.indexOf(entry) })),
80701
+ ...sticky.map((entry) => ({ entry, index: Number.MAX_SAFE_INTEGER }))
80702
+ ].map(({ entry, index }) => ({ entry, index, order: pendingStageMessageAdmissionOrder(entry) })).sort((left, right) => left.order - right.order || left.index - right.index).map(({ entry }) => entry);
80703
+ }
80704
+ function pooledStageHops(journal, runs, root, runId) {
80705
+ const byId = new Map(runs.map((run) => [run.id, run]));
80706
+ const hops = [];
80707
+ let current = byId.get(runId);
80708
+ while (current !== undefined && current.parentRunId !== root) {
80709
+ const child = current;
80710
+ const parent = child.parentRunId === undefined ? undefined : byId.get(child.parentRunId);
80711
+ const boundary = parent?.stages.find((stage) => stage.id === child.parentStageId);
80712
+ if (parent === undefined || boundary === undefined)
80713
+ return;
80714
+ hops.unshift({ name: boundary.name, runId: child.id });
80715
+ current = parent;
80716
+ }
80717
+ if (current === undefined)
80718
+ return;
80719
+ const childRunId = current.id;
80720
+ const boundary = journal.listCheckpoints(root).find((checkpoint) => checkpoint.kind === "stage" && checkpoint.topology?.boundary?.child.runId === childRunId);
80721
+ if (boundary === undefined)
80722
+ return;
80723
+ hops.unshift({ name: boundary.name, runId: childRunId });
80724
+ return hops;
80725
+ }
80726
+ function createPooledStageDelivery(options) {
80727
+ const admissions = new Map;
80728
+ const sinks = new Map;
80729
+ return {
80730
+ routeCapability: options.routeCapability,
80731
+ async deliverPending(deliver, recipient) {
80732
+ const sessionId = recipient?.sessionId ?? "";
80733
+ sinks.set(sessionId, deliver);
80734
+ let admitted = admissions.get(sessionId);
80735
+ if (admitted === undefined) {
80736
+ admitted = new Map;
80737
+ admissions.set(sessionId, admitted);
80738
+ }
80739
+ const held = admitted;
80740
+ for (const id of recipient?.receivedMessageIds ?? [])
80741
+ if (!held.has(id))
80742
+ held.set(id, Promise.resolve());
80743
+ const admit = (from, message) => {
80744
+ const existing = held.get(message.id);
80745
+ if (existing !== undefined)
80746
+ return existing;
80747
+ const sink = sinks.get(sessionId) ?? deliver;
80748
+ const delivery = Promise.resolve().then(() => sink(from, message));
80749
+ held.set(message.id, delivery);
80750
+ delivery.catch(() => {
80751
+ if (held.get(message.id) === delivery)
80752
+ held.delete(message.id);
80753
+ });
80754
+ return delivery;
80755
+ };
80756
+ try {
80757
+ for (const entry of await options.queued())
80758
+ await admit(toPendingStageSender(entry), entry.message);
80759
+ } catch {}
80760
+ const deliveries = [...held];
80761
+ const settled = await Promise.allSettled(deliveries.map(([, delivery]) => delivery));
80762
+ const receivedMessageIds = deliveries.flatMap(([id], index) => settled[index]?.status === "fulfilled" ? [id] : []);
80763
+ options.forward({ sessionId, receivedMessageIds }, admit).catch(() => {});
80764
+ },
80765
+ ready() {
80766
+ return;
80767
+ },
80768
+ fail: options.fail
80769
+ };
80770
+ }
80248
80771
 
80249
80772
  // dist/builtin/workflows/src/workers/local-worker.ts
80250
80773
  init_pooled_watch_trace();
@@ -81314,6 +81837,7 @@ async function runWorker(start) {
81314
81837
  await reader.catchUpWorkflow(start.root);
81315
81838
  const work = await request({ kind: "claim" });
81316
81839
  attemptFenced = false;
81840
+ let poolFault;
81317
81841
  if (!work) {
81318
81842
  await delay4(25 + Math.random() * 25);
81319
81843
  continue;
@@ -81378,25 +81902,18 @@ async function runWorker(start) {
81378
81902
  const capability = await request({ kind: "route", runId: meta.runId });
81379
81903
  if (typeof capability !== "string")
81380
81904
  throw new Error("Worker root route is unavailable");
81381
- const pendingStageDelivery = {
81905
+ const pendingStageDelivery = createPooledStageDelivery({
81382
81906
  routeCapability: capability,
81383
- async deliverPending(deliver, recipient) {
81384
- await publication;
81385
- await untilDrainedOrStalled((progress) => request({ kind: "delivery", ...identity, recipient }, async (from, message) => {
81386
- progress();
81387
- try {
81388
- await deliver(from, message);
81389
- } finally {
81390
- progress();
81391
- }
81392
- }).then(() => {}));
81907
+ queued: async () => {
81908
+ await reader.catchUpWorkflow(start.root);
81909
+ return pooledStageQueuedEntries(reader, store, start.root, identity);
81393
81910
  },
81394
- async ready(sessionId) {
81911
+ forward: async (recipient, deliver) => {
81395
81912
  await publication;
81396
- await request({ kind: "delivery-ready", ...identity, sessionId });
81913
+ await request({ kind: "delivery", ...identity, recipient }, deliver);
81397
81914
  },
81398
81915
  fail: options.orchestrationContext.pendingStageDelivery.fail
81399
- };
81916
+ });
81400
81917
  return baseAdapter.create({ ...options, orchestrationContext: { ...options.orchestrationContext, pendingStageDelivery } }, meta);
81401
81918
  }
81402
81919
  };
@@ -81486,6 +82003,11 @@ async function runWorker(start) {
81486
82003
  }
81487
82004
  if (attemptFenced)
81488
82005
  throw new WorkAttemptFencedError("Work attempt fenced before completion");
82006
+ if (failedOnInfrastructureFault(result)) {
82007
+ poolFault = infrastructureFaultMessage(result);
82008
+ attemptFenced = true;
82009
+ throw new WorkAttemptFencedError(poolFault);
82010
+ }
81489
82011
  await request({ kind: "complete", result });
81490
82012
  } catch (error) {
81491
82013
  if (!attemptFenced)
@@ -81503,7 +82025,7 @@ async function runWorker(start) {
81503
82025
  await renewal;
81504
82026
  await report;
81505
82027
  await controlPulse;
81506
- await request({ kind: "fenced" });
82028
+ await request({ kind: "fenced", ...poolFault === undefined ? {} : { fault: poolFault } });
81507
82029
  }
81508
82030
  if (rootLeaseLost)
81509
82031
  break;