@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.
- package/CHANGELOG.md +80 -0
- package/dist/builtin/intercom/index.bundle.mjs +3 -3
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +12 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +14 -0
- package/dist/builtin/workflows/README.md +9 -7
- package/dist/builtin/workflows/builtin/{chunk-h6bt0r0g.js → chunk-a2j0x1a6.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-g3b2zf8r.js → chunk-vzbec50q.js} +1 -1
- package/dist/builtin/workflows/builtin/goal.js +1 -1
- package/dist/builtin/workflows/builtin/index.js +2 -2
- package/dist/builtin/workflows/builtin/ralph.js +1 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +922 -473
- package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +1006 -484
- package/dist/builtin/workflows/src/index.js +892 -448
- package/dist/builtin/workflows/src/local-worker-entry.mjs +1006 -484
- package/dist/builtin/workflows/src/shared/workflow-infrastructure-error.d.ts +6 -0
- package/dist/core/agent-session-retry.d.ts.map +1 -1
- package/dist/core/agent-session-retry.js +12 -3
- package/dist/core/agent-session-retry.js.map +1 -1
- package/dist/core/model-fallback-failures.d.ts +14 -4
- package/dist/core/model-fallback-failures.d.ts.map +1 -1
- package/dist/core/model-fallback-failures.js +43 -15
- package/dist/core/model-fallback-failures.js.map +1 -1
- package/dist/core/settings-defaults.d.ts +1 -0
- package/dist/core/settings-defaults.d.ts.map +1 -1
- package/dist/core/settings-defaults.js +1 -0
- package/dist/core/settings-defaults.js.map +1 -1
- package/dist/core/settings-manager-basic-accessors.d.ts +1 -0
- package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
- package/dist/core/settings-manager-basic-accessors.js +2 -0
- package/dist/core/settings-manager-basic-accessors.js.map +1 -1
- package/dist/core/settings-schema.d.ts +2 -0
- package/dist/core/settings-schema.d.ts.map +1 -1
- package/dist/core/settings-schema.js +4 -0
- package/dist/core/settings-schema.js.map +1 -1
- package/dist/core/slash-commands.js +2 -2
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/interactive-startup-chat-container.d.ts +2 -0
- package/dist/modes/interactive/interactive-startup-chat-container.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup-chat-container.js +6 -0
- package/dist/modes/interactive/interactive-startup-chat-container.js.map +1 -1
- package/dist/modes/interactive-engine/remote-renderer.d.ts +27 -5
- package/dist/modes/interactive-engine/remote-renderer.d.ts.map +1 -1
- package/dist/modes/interactive-engine/remote-renderer.js +76 -16
- package/dist/modes/interactive-engine/remote-renderer.js.map +1 -1
- package/docs/intercom/reference.md +1 -1
- package/docs/models/evals.md +30 -2
- package/docs/settings.md +3 -0
- package/docs/subagents/reference.md +3 -1
- package/docs/workflows/api-reference.md +9 -6
- package/docs/workflows/builtins.md +1 -1
- package/docs/workflows/operations.md +6 -4
- package/npm-shrinkwrap.json +32 -32
- package/package.json +3 -3
- package/schemas/settings.schema.json +5 -0
|
@@ -3058,8 +3058,12 @@ function createStageControlRegistry() {
|
|
|
3058
3058
|
}
|
|
3059
3059
|
await disposeEntries(entries);
|
|
3060
3060
|
},
|
|
3061
|
-
async releaseIdle(runId) {
|
|
3062
|
-
|
|
3061
|
+
async releaseIdle(runId, only) {
|
|
3062
|
+
const runMap = _byRun.get(runId);
|
|
3063
|
+
const entries = only === undefined ? [...runMap ?? []] : [[only, runMap?.get(only)]];
|
|
3064
|
+
for (const [stageId, entry] of entries) {
|
|
3065
|
+
if (entry === undefined)
|
|
3066
|
+
continue;
|
|
3063
3067
|
try {
|
|
3064
3068
|
await releaseIdleEntry(runId, stageId, entry);
|
|
3065
3069
|
} catch (error) {
|
|
@@ -15573,9 +15577,104 @@ var init_workflow_artifacts = __esm(() => {
|
|
|
15573
15577
|
pendingArtifactPrunes = new Map;
|
|
15574
15578
|
});
|
|
15575
15579
|
|
|
15576
|
-
// dist/builtin/workflows/src/runs/shared/worktree-
|
|
15580
|
+
// dist/builtin/workflows/src/runs/shared/worktree-allocation.ts
|
|
15581
|
+
import { randomBytes } from "node:crypto";
|
|
15577
15582
|
import * as fs from "node:fs";
|
|
15578
15583
|
import * as path from "node:path";
|
|
15584
|
+
function nextBucketId() {
|
|
15585
|
+
return nextBucketIdOverride?.() ?? randomBytes(2).toString("hex");
|
|
15586
|
+
}
|
|
15587
|
+
function errorCode(error) {
|
|
15588
|
+
return error && typeof error === "object" && "code" in error ? String(error.code) : undefined;
|
|
15589
|
+
}
|
|
15590
|
+
function allocateWorktreeCheckoutPath(root, repositoryName) {
|
|
15591
|
+
for (let attempt = 0;attempt < MAX_BUCKET_ATTEMPTS; attempt++) {
|
|
15592
|
+
const bucket = path.join(root, nextBucketId());
|
|
15593
|
+
try {
|
|
15594
|
+
fs.mkdirSync(bucket);
|
|
15595
|
+
return path.join(bucket, repositoryName);
|
|
15596
|
+
} catch (error) {
|
|
15597
|
+
if (errorCode(error) === "EEXIST")
|
|
15598
|
+
continue;
|
|
15599
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15600
|
+
throw new Error(`Failed to create worktree bucket ${bucket} under gitWorktreeDir ${root}: ${message}`);
|
|
15601
|
+
}
|
|
15602
|
+
}
|
|
15603
|
+
throw new Error(`Every worktree bucket identifier under gitWorktreeDir ${root} is already in use.`);
|
|
15604
|
+
}
|
|
15605
|
+
function removeEmptyWorktreeBucket(checkout) {
|
|
15606
|
+
try {
|
|
15607
|
+
fs.rmdirSync(path.dirname(checkout));
|
|
15608
|
+
} catch {}
|
|
15609
|
+
}
|
|
15610
|
+
function linkedGitDirectory(checkout) {
|
|
15611
|
+
let pointer;
|
|
15612
|
+
try {
|
|
15613
|
+
if (!fs.lstatSync(path.join(checkout, ".git")).isFile())
|
|
15614
|
+
return;
|
|
15615
|
+
pointer = fs.readFileSync(path.join(checkout, ".git"), "utf8");
|
|
15616
|
+
} catch {
|
|
15617
|
+
return;
|
|
15618
|
+
}
|
|
15619
|
+
if (!pointer.startsWith("gitdir:"))
|
|
15620
|
+
return;
|
|
15621
|
+
const gitDir = pointer.slice("gitdir:".length).trim();
|
|
15622
|
+
return gitDir ? path.resolve(checkout, gitDir) : undefined;
|
|
15623
|
+
}
|
|
15624
|
+
function readWorktreeOwner(gitDir) {
|
|
15625
|
+
let contents;
|
|
15626
|
+
try {
|
|
15627
|
+
contents = fs.readFileSync(path.join(gitDir, WORKTREE_OWNER_FILENAME), "utf8");
|
|
15628
|
+
} catch {
|
|
15629
|
+
return;
|
|
15630
|
+
}
|
|
15631
|
+
try {
|
|
15632
|
+
const record = JSON.parse(contents);
|
|
15633
|
+
return record.version === WORKTREE_OWNER_VERSION && typeof record.ownerRunId === "string" && record.ownerRunId ? record.ownerRunId : undefined;
|
|
15634
|
+
} catch {
|
|
15635
|
+
return;
|
|
15636
|
+
}
|
|
15637
|
+
}
|
|
15638
|
+
function findOwnedWorktreeCheckouts(root, repositoryName, ownerRunId) {
|
|
15639
|
+
let entries;
|
|
15640
|
+
try {
|
|
15641
|
+
entries = fs.readdirSync(root, { withFileTypes: true });
|
|
15642
|
+
} catch {
|
|
15643
|
+
return [];
|
|
15644
|
+
}
|
|
15645
|
+
const owned = [];
|
|
15646
|
+
for (const entry of entries) {
|
|
15647
|
+
if (!entry.isDirectory() || !BUCKET_PATTERN.test(entry.name))
|
|
15648
|
+
continue;
|
|
15649
|
+
const checkout = path.join(root, entry.name, repositoryName);
|
|
15650
|
+
try {
|
|
15651
|
+
if (!fs.lstatSync(checkout).isDirectory())
|
|
15652
|
+
continue;
|
|
15653
|
+
} catch {
|
|
15654
|
+
continue;
|
|
15655
|
+
}
|
|
15656
|
+
const gitDir = linkedGitDirectory(checkout);
|
|
15657
|
+
if (gitDir !== undefined && readWorktreeOwner(gitDir) === ownerRunId)
|
|
15658
|
+
owned.push(checkout);
|
|
15659
|
+
}
|
|
15660
|
+
return owned.sort();
|
|
15661
|
+
}
|
|
15662
|
+
function bindWorktreeOwner(gitDir, ownerRunId) {
|
|
15663
|
+
const record = { version: WORKTREE_OWNER_VERSION, ownerRunId };
|
|
15664
|
+
fs.writeFileSync(path.join(gitDir, WORKTREE_OWNER_FILENAME), `${JSON.stringify(record)}
|
|
15665
|
+
`, {
|
|
15666
|
+
encoding: "utf8",
|
|
15667
|
+
flag: "wx"
|
|
15668
|
+
});
|
|
15669
|
+
}
|
|
15670
|
+
var WORKTREE_OWNER_FILENAME = "atomic-workflow-run.json", WORKTREE_OWNER_VERSION = 1, BUCKET_PATTERN, MAX_BUCKET_ATTEMPTS = 65536, nextBucketIdOverride;
|
|
15671
|
+
var init_worktree_allocation = __esm(() => {
|
|
15672
|
+
BUCKET_PATTERN = /^[0-9a-f]{4}$/;
|
|
15673
|
+
});
|
|
15674
|
+
|
|
15675
|
+
// dist/builtin/workflows/src/runs/shared/worktree-generation.ts
|
|
15676
|
+
import * as fs2 from "node:fs";
|
|
15677
|
+
import * as path2 from "node:path";
|
|
15579
15678
|
function sameFileIdentity(left, right) {
|
|
15580
15679
|
return left.dev === right.dev && left.ino === right.ino;
|
|
15581
15680
|
}
|
|
@@ -15585,7 +15684,7 @@ function openMainCheckoutAnchor(gitDir) {
|
|
|
15585
15684
|
assertCurrent() {
|
|
15586
15685
|
if (disposed)
|
|
15587
15686
|
throw new Error(`cached .git identity anchor is already disposed: ${gitDir}`);
|
|
15588
|
-
if (!
|
|
15687
|
+
if (!fs2.lstatSync(gitDir).isDirectory()) {
|
|
15589
15688
|
throw new Error(`cached main-checkout .git directory is no longer current: ${gitDir}`);
|
|
15590
15689
|
}
|
|
15591
15690
|
},
|
|
@@ -15595,17 +15694,17 @@ function openMainCheckoutAnchor(gitDir) {
|
|
|
15595
15694
|
};
|
|
15596
15695
|
}
|
|
15597
15696
|
function openGitWorktreeGenerationAnchor(worktreeRoot) {
|
|
15598
|
-
const gitFile =
|
|
15599
|
-
if (
|
|
15697
|
+
const gitFile = path2.join(worktreeRoot, ".git");
|
|
15698
|
+
if (fs2.lstatSync(gitFile).isDirectory())
|
|
15600
15699
|
return openMainCheckoutAnchor(gitFile);
|
|
15601
|
-
const descriptor =
|
|
15700
|
+
const descriptor = fs2.openSync(gitFile, "r");
|
|
15602
15701
|
let disposed = false;
|
|
15603
15702
|
const anchor = {
|
|
15604
15703
|
assertCurrent() {
|
|
15605
15704
|
if (disposed)
|
|
15606
15705
|
throw new Error(`cached .git identity anchor is already disposed: ${gitFile}`);
|
|
15607
|
-
const held =
|
|
15608
|
-
const current =
|
|
15706
|
+
const held = fs2.fstatSync(descriptor, { bigint: true });
|
|
15707
|
+
const current = fs2.lstatSync(gitFile, { bigint: true });
|
|
15609
15708
|
if (!current.isFile() || !sameFileIdentity(held, current)) {
|
|
15610
15709
|
throw new Error(`cached .git identity anchor no longer matches the selected checkout: ${gitFile}`);
|
|
15611
15710
|
}
|
|
@@ -15614,7 +15713,7 @@ function openGitWorktreeGenerationAnchor(worktreeRoot) {
|
|
|
15614
15713
|
if (disposed)
|
|
15615
15714
|
return;
|
|
15616
15715
|
disposed = true;
|
|
15617
|
-
|
|
15716
|
+
fs2.closeSync(descriptor);
|
|
15618
15717
|
}
|
|
15619
15718
|
};
|
|
15620
15719
|
try {
|
|
@@ -15728,84 +15827,84 @@ var init_worktree_git_runner = __esm(() => {
|
|
|
15728
15827
|
});
|
|
15729
15828
|
|
|
15730
15829
|
// dist/builtin/workflows/src/runs/shared/worktree-root.ts
|
|
15731
|
-
import * as
|
|
15732
|
-
import * as
|
|
15830
|
+
import * as fs3 from "node:fs";
|
|
15831
|
+
import * as path3 from "node:path";
|
|
15733
15832
|
function pathFromPointer(contents, prefix, base) {
|
|
15734
15833
|
if (!contents.startsWith(prefix))
|
|
15735
15834
|
return;
|
|
15736
15835
|
const value2 = contents.slice(prefix.length).trim();
|
|
15737
15836
|
if (!value2)
|
|
15738
15837
|
return;
|
|
15739
|
-
return
|
|
15838
|
+
return path3.resolve(base, value2);
|
|
15740
15839
|
}
|
|
15741
15840
|
function readText(file) {
|
|
15742
15841
|
try {
|
|
15743
|
-
return
|
|
15842
|
+
return fs3.readFileSync(file, "utf8");
|
|
15744
15843
|
} catch {
|
|
15745
15844
|
return;
|
|
15746
15845
|
}
|
|
15747
15846
|
}
|
|
15748
15847
|
function samePath(left, right) {
|
|
15749
15848
|
try {
|
|
15750
|
-
const a =
|
|
15751
|
-
const b =
|
|
15849
|
+
const a = fs3.realpathSync.native(left);
|
|
15850
|
+
const b = fs3.realpathSync.native(right);
|
|
15752
15851
|
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
15753
15852
|
} catch {
|
|
15754
15853
|
return false;
|
|
15755
15854
|
}
|
|
15756
15855
|
}
|
|
15757
15856
|
function findGitRoot(cwd) {
|
|
15758
|
-
let current =
|
|
15857
|
+
let current = path3.resolve(cwd);
|
|
15759
15858
|
try {
|
|
15760
|
-
if (!
|
|
15761
|
-
current =
|
|
15859
|
+
if (!fs3.statSync(current).isDirectory())
|
|
15860
|
+
current = path3.dirname(current);
|
|
15762
15861
|
} catch {
|
|
15763
15862
|
return;
|
|
15764
15863
|
}
|
|
15765
15864
|
while (true) {
|
|
15766
15865
|
try {
|
|
15767
|
-
const entry =
|
|
15866
|
+
const entry = fs3.statSync(path3.join(current, ".git"));
|
|
15768
15867
|
if (entry.isDirectory() || entry.isFile())
|
|
15769
15868
|
return current;
|
|
15770
15869
|
} catch {}
|
|
15771
|
-
const parent =
|
|
15870
|
+
const parent = path3.dirname(current);
|
|
15772
15871
|
if (parent === current)
|
|
15773
15872
|
return;
|
|
15774
15873
|
current = parent;
|
|
15775
15874
|
}
|
|
15776
15875
|
}
|
|
15777
15876
|
function resolveMainRepoRoot(repoRoot) {
|
|
15778
|
-
const gitEntry =
|
|
15877
|
+
const gitEntry = path3.join(repoRoot, ".git");
|
|
15779
15878
|
let stats;
|
|
15780
15879
|
try {
|
|
15781
|
-
stats =
|
|
15880
|
+
stats = fs3.statSync(gitEntry);
|
|
15782
15881
|
} catch {
|
|
15783
15882
|
return;
|
|
15784
15883
|
}
|
|
15785
15884
|
if (stats.isDirectory())
|
|
15786
|
-
return
|
|
15885
|
+
return path3.resolve(repoRoot);
|
|
15787
15886
|
if (!stats.isFile())
|
|
15788
15887
|
return;
|
|
15789
15888
|
const pointer = readText(gitEntry);
|
|
15790
15889
|
if (pointer === undefined)
|
|
15791
15890
|
return;
|
|
15792
15891
|
const gitDir = pathFromPointer(pointer, "gitdir:", repoRoot);
|
|
15793
|
-
if (gitDir === undefined ||
|
|
15892
|
+
if (gitDir === undefined || path3.basename(path3.dirname(gitDir)) !== "worktrees")
|
|
15794
15893
|
return;
|
|
15795
|
-
const commonGitDir =
|
|
15796
|
-
if (
|
|
15894
|
+
const commonGitDir = path3.dirname(path3.dirname(gitDir));
|
|
15895
|
+
if (path3.basename(commonGitDir) !== ".git")
|
|
15797
15896
|
return;
|
|
15798
|
-
const backPointer = readText(
|
|
15799
|
-
const commonPointer = readText(
|
|
15897
|
+
const backPointer = readText(path3.join(gitDir, "gitdir"));
|
|
15898
|
+
const commonPointer = readText(path3.join(gitDir, "commondir"));
|
|
15800
15899
|
if (backPointer === undefined || commonPointer === undefined)
|
|
15801
15900
|
return;
|
|
15802
|
-
const linkedGitEntry =
|
|
15803
|
-
const declaredCommonDir =
|
|
15901
|
+
const linkedGitEntry = path3.resolve(gitDir, backPointer.trim());
|
|
15902
|
+
const declaredCommonDir = path3.resolve(gitDir, commonPointer.trim());
|
|
15804
15903
|
if (!backPointer.trim() || !commonPointer.trim())
|
|
15805
15904
|
return;
|
|
15806
15905
|
if (!samePath(linkedGitEntry, gitEntry) || !samePath(declaredCommonDir, commonGitDir))
|
|
15807
15906
|
return;
|
|
15808
|
-
return
|
|
15907
|
+
return path3.dirname(commonGitDir);
|
|
15809
15908
|
}
|
|
15810
15909
|
function findCanonicalGitRoot(cwd) {
|
|
15811
15910
|
const repoRoot = findGitRoot(cwd);
|
|
@@ -15814,33 +15913,31 @@ function findCanonicalGitRoot(cwd) {
|
|
|
15814
15913
|
var init_worktree_root = () => {};
|
|
15815
15914
|
|
|
15816
15915
|
// dist/builtin/workflows/src/runs/shared/worktree-git.ts
|
|
15817
|
-
import * as
|
|
15818
|
-
import * as
|
|
15819
|
-
function gitWorktreeSetupCacheKey(
|
|
15820
|
-
const repoRoot = repositoryRootForGitWorktree(options.cwd);
|
|
15821
|
-
const { logicalRepoRoot } = cwdWithinGitRepository(options.cwd, repoRoot);
|
|
15822
|
-
const worktreeRoot = resolveGitWorktreePath(options.gitWorktreeDir, logicalRepoRoot);
|
|
15916
|
+
import * as fs4 from "node:fs";
|
|
15917
|
+
import * as path4 from "node:path";
|
|
15918
|
+
function gitWorktreeSetupCacheKey(request) {
|
|
15823
15919
|
return JSON.stringify([
|
|
15824
|
-
|
|
15825
|
-
comparablePathThroughExistingAncestor(
|
|
15920
|
+
comparablePathThroughExistingAncestor(request.options.cwd),
|
|
15921
|
+
comparablePathThroughExistingAncestor(request.target)
|
|
15826
15922
|
]);
|
|
15827
15923
|
}
|
|
15828
|
-
function createGitWorktreeSetupCache() {
|
|
15924
|
+
function createGitWorktreeSetupCache(ownerRunId) {
|
|
15829
15925
|
const results = new Map;
|
|
15830
15926
|
let disposed = false;
|
|
15831
15927
|
return {
|
|
15832
15928
|
get(options) {
|
|
15833
15929
|
if (disposed)
|
|
15834
15930
|
throw new Error("Git worktree setup cache is already disposed");
|
|
15835
|
-
const
|
|
15931
|
+
const request = resolveGitWorktreeSetupRequest(options);
|
|
15932
|
+
const key = gitWorktreeSetupCacheKey(request);
|
|
15836
15933
|
const cached = results.get(key);
|
|
15837
15934
|
if (cached !== undefined) {
|
|
15838
15935
|
assertCachedGitWorktreeIdentity(cached, options);
|
|
15839
15936
|
return cached.result;
|
|
15840
15937
|
}
|
|
15841
|
-
const
|
|
15842
|
-
results.set(key, {
|
|
15843
|
-
return result;
|
|
15938
|
+
const setup = setupGitWorktreeFromRequest(request, ownerRunId);
|
|
15939
|
+
results.set(key, { ...setup, identity: captureGitWorktreeIdentity(setup) });
|
|
15940
|
+
return setup.result;
|
|
15844
15941
|
},
|
|
15845
15942
|
dispose() {
|
|
15846
15943
|
if (disposed)
|
|
@@ -15872,10 +15969,10 @@ function worktreeRecoveryCommand(repositoryRoot, worktreeDir) {
|
|
|
15872
15969
|
return `git -C ${quoteShellArg(repositoryRoot)} worktree remove --force ${quoteShellArg(worktreeDir)}`;
|
|
15873
15970
|
}
|
|
15874
15971
|
function hasGrandparent(value2) {
|
|
15875
|
-
const parent =
|
|
15972
|
+
const parent = path4.dirname(value2);
|
|
15876
15973
|
if (parent === value2)
|
|
15877
15974
|
return false;
|
|
15878
|
-
const grandparent =
|
|
15975
|
+
const grandparent = path4.dirname(parent);
|
|
15879
15976
|
return grandparent !== parent;
|
|
15880
15977
|
}
|
|
15881
15978
|
function pathAncestors(value2) {
|
|
@@ -15883,7 +15980,7 @@ function pathAncestors(value2) {
|
|
|
15883
15980
|
let current = value2;
|
|
15884
15981
|
while (true) {
|
|
15885
15982
|
ancestors.push(current);
|
|
15886
|
-
const parent =
|
|
15983
|
+
const parent = path4.dirname(current);
|
|
15887
15984
|
if (parent === current)
|
|
15888
15985
|
return ancestors;
|
|
15889
15986
|
current = parent;
|
|
@@ -15892,89 +15989,167 @@ function pathAncestors(value2) {
|
|
|
15892
15989
|
function shouldPreserveLogicalPath(logicalPath) {
|
|
15893
15990
|
return pathAncestors(logicalPath).some((ancestor) => {
|
|
15894
15991
|
try {
|
|
15895
|
-
return
|
|
15992
|
+
return fs4.lstatSync(ancestor).isSymbolicLink() && hasGrandparent(ancestor);
|
|
15896
15993
|
} catch {
|
|
15897
15994
|
return false;
|
|
15898
15995
|
}
|
|
15899
15996
|
});
|
|
15900
15997
|
}
|
|
15901
15998
|
function canonicalizePreservingSymlinks(value2) {
|
|
15902
|
-
const logicalPath =
|
|
15999
|
+
const logicalPath = path4.resolve(value2);
|
|
15903
16000
|
const preserveLogicalPath = shouldPreserveLogicalPath(logicalPath);
|
|
15904
16001
|
try {
|
|
15905
|
-
const canonical =
|
|
16002
|
+
const canonical = fs4.realpathSync.native(logicalPath);
|
|
15906
16003
|
return preserveLogicalPath && canonical !== logicalPath ? logicalPath : canonical;
|
|
15907
16004
|
} catch {
|
|
15908
16005
|
return logicalPath;
|
|
15909
16006
|
}
|
|
15910
16007
|
}
|
|
15911
|
-
function resolveGitWorktreePath(value2,
|
|
16008
|
+
function resolveGitWorktreePath(value2, baseDir) {
|
|
15912
16009
|
const trimmed = value2.trim();
|
|
15913
16010
|
if (!trimmed)
|
|
15914
16011
|
throw new Error("gitWorktreeDir cannot be empty");
|
|
15915
16012
|
if (trimmed.includes("\x00")) {
|
|
15916
16013
|
throw new Error("gitWorktreeDir contains an unusable null byte; provide a valid path or omit gitWorktreeDir.");
|
|
15917
16014
|
}
|
|
15918
|
-
return canonicalizePreservingSymlinks(
|
|
16015
|
+
return canonicalizePreservingSymlinks(path4.isAbsolute(trimmed) ? trimmed : path4.resolve(baseDir, trimmed));
|
|
15919
16016
|
}
|
|
15920
16017
|
function comparableRealPath(value2) {
|
|
15921
|
-
const realpath =
|
|
16018
|
+
const realpath = fs4.realpathSync.native(value2).replace(/\\/g, "/");
|
|
15922
16019
|
return process.platform === "win32" ? realpath.toLowerCase() : realpath;
|
|
15923
16020
|
}
|
|
15924
|
-
function
|
|
15925
|
-
let existing =
|
|
16021
|
+
function nearestExistingAncestor(value2) {
|
|
16022
|
+
let existing = path4.resolve(value2);
|
|
15926
16023
|
const missing = [];
|
|
15927
16024
|
while (!pathExistsSync(existing)) {
|
|
15928
|
-
const parent =
|
|
16025
|
+
const parent = path4.dirname(existing);
|
|
15929
16026
|
if (parent === existing)
|
|
15930
16027
|
break;
|
|
15931
|
-
missing.unshift(
|
|
16028
|
+
missing.unshift(path4.basename(existing));
|
|
15932
16029
|
existing = parent;
|
|
15933
16030
|
}
|
|
15934
|
-
|
|
16031
|
+
return { existing, missing };
|
|
16032
|
+
}
|
|
16033
|
+
function comparablePathThroughExistingAncestor(value2) {
|
|
16034
|
+
const { existing, missing } = nearestExistingAncestor(value2);
|
|
16035
|
+
const resolved = path4.resolve(comparableRealPath(existing), ...missing).replace(/\\/g, "/");
|
|
15935
16036
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
15936
16037
|
}
|
|
16038
|
+
function isFileOrLink(value2) {
|
|
16039
|
+
try {
|
|
16040
|
+
const entry = fs4.lstatSync(value2);
|
|
16041
|
+
return entry.isFile() || entry.isSymbolicLink();
|
|
16042
|
+
} catch {
|
|
16043
|
+
return false;
|
|
16044
|
+
}
|
|
16045
|
+
}
|
|
16046
|
+
function isDirectory(value2) {
|
|
16047
|
+
try {
|
|
16048
|
+
return fs4.statSync(value2).isDirectory();
|
|
16049
|
+
} catch {
|
|
16050
|
+
return false;
|
|
16051
|
+
}
|
|
16052
|
+
}
|
|
16053
|
+
function looksLikeGitDirectory(directory) {
|
|
16054
|
+
if (!isDirectory(directory))
|
|
16055
|
+
return false;
|
|
16056
|
+
if (path4.basename(directory) === ".git")
|
|
16057
|
+
return true;
|
|
16058
|
+
if (!isFileOrLink(path4.join(directory, "HEAD")))
|
|
16059
|
+
return false;
|
|
16060
|
+
if (isFileOrLink(path4.join(directory, "commondir")))
|
|
16061
|
+
return true;
|
|
16062
|
+
return isDirectory(path4.join(directory, "objects")) && isDirectory(path4.join(directory, "refs"));
|
|
16063
|
+
}
|
|
16064
|
+
function enclosingGitMetadataDirectory(target) {
|
|
16065
|
+
const { existing } = nearestExistingAncestor(target);
|
|
16066
|
+
let realExisting = existing;
|
|
16067
|
+
try {
|
|
16068
|
+
realExisting = fs4.realpathSync.native(existing);
|
|
16069
|
+
} catch {}
|
|
16070
|
+
for (const candidate of new Set([...pathAncestors(existing), ...pathAncestors(realExisting)])) {
|
|
16071
|
+
if (looksLikeGitDirectory(candidate))
|
|
16072
|
+
return candidate;
|
|
16073
|
+
}
|
|
16074
|
+
const insideGitDir = runGitReadOnlyProbe(existing, ["rev-parse", "--is-inside-git-dir"]);
|
|
16075
|
+
if (insideGitDir.status !== 0 && isGitTimeoutResult(insideGitDir)) {
|
|
16076
|
+
throw new Error(`Timed out while validating gitWorktreeDir ${target}. Git reported: ${gitFailureMessage(insideGitDir)}`);
|
|
16077
|
+
}
|
|
16078
|
+
return insideGitDir.status === 0 && insideGitDir.stdout.trim() === "true" ? existing : undefined;
|
|
16079
|
+
}
|
|
16080
|
+
function assertSafeGitWorktreeTarget(target, invokingRoot) {
|
|
16081
|
+
const gitDirectory = enclosingGitMetadataDirectory(target);
|
|
16082
|
+
if (gitDirectory !== undefined) {
|
|
16083
|
+
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.`);
|
|
16084
|
+
}
|
|
16085
|
+
if (invokingRoot !== undefined)
|
|
16086
|
+
validateWorktreeOutsideInvokingCheckout(target, invokingRoot);
|
|
16087
|
+
const { existing, missing } = nearestExistingAncestor(target);
|
|
16088
|
+
const workingTree = gitTopLevel(existing);
|
|
16089
|
+
if (workingTree === undefined)
|
|
16090
|
+
return;
|
|
16091
|
+
if (missing.length === 0 && comparableRealPath(target) === comparableRealPath(workingTree))
|
|
16092
|
+
return;
|
|
16093
|
+
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.`);
|
|
16094
|
+
}
|
|
15937
16095
|
function validateWorktreeOutsideInvokingCheckout(worktreeRoot, repoRoot) {
|
|
15938
16096
|
const repository = comparableRealPath(repoRoot);
|
|
15939
16097
|
const candidate = comparablePathThroughExistingAncestor(worktreeRoot);
|
|
15940
|
-
const relativePath =
|
|
16098
|
+
const relativePath = path4.relative(repository, candidate);
|
|
15941
16099
|
if (relativePath === "") {
|
|
15942
|
-
throw new Error("gitWorktreeDir must not resolve to the invoking checkout; provide a separate
|
|
16100
|
+
throw new Error("gitWorktreeDir must not resolve to the invoking checkout; provide a separate worktree path outside it.");
|
|
15943
16101
|
}
|
|
15944
|
-
if (!relativePath.startsWith(`..${
|
|
15945
|
-
throw new Error("gitWorktreeDir must be outside the invoking checkout; provide a separate
|
|
16102
|
+
if (!relativePath.startsWith(`..${path4.sep}`) && relativePath !== ".." && !path4.isAbsolute(relativePath)) {
|
|
16103
|
+
throw new Error("gitWorktreeDir must be outside the invoking checkout; provide a separate worktree path outside it.");
|
|
15946
16104
|
}
|
|
15947
16105
|
}
|
|
15948
16106
|
function gitPathFromOutput(value2, cwd) {
|
|
15949
16107
|
const trimmed = value2.trim();
|
|
15950
16108
|
if (!trimmed)
|
|
15951
16109
|
return;
|
|
15952
|
-
return
|
|
16110
|
+
return path4.isAbsolute(trimmed) ? path4.resolve(trimmed) : path4.resolve(cwd, trimmed);
|
|
15953
16111
|
}
|
|
15954
16112
|
function pathExistsSync(value2) {
|
|
16113
|
+
return pathStatSync(value2) !== undefined;
|
|
16114
|
+
}
|
|
16115
|
+
function pathStatSync(value2) {
|
|
15955
16116
|
try {
|
|
15956
|
-
|
|
15957
|
-
return true;
|
|
16117
|
+
return fs4.statSync(value2);
|
|
15958
16118
|
} catch (error) {
|
|
15959
16119
|
const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
|
|
15960
16120
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
15961
|
-
return
|
|
16121
|
+
return;
|
|
15962
16122
|
throw error;
|
|
15963
16123
|
}
|
|
15964
16124
|
}
|
|
15965
|
-
function
|
|
16125
|
+
function probeInvokingRepository(cwd) {
|
|
15966
16126
|
const result = runGitReadOnlyProbe(cwd, ["rev-parse", "--show-toplevel"]);
|
|
15967
16127
|
if (result.status !== 0) {
|
|
15968
16128
|
if (isGitTimeoutResult(result)) {
|
|
15969
16129
|
throw new Error(`Timed out while checking the Git repository for gitWorktreeDir from ${cwd}. Git reported: ${gitFailureMessage(result)}`);
|
|
15970
16130
|
}
|
|
15971
|
-
|
|
16131
|
+
return { failure: gitFailureMessage(result) };
|
|
15972
16132
|
}
|
|
15973
|
-
const
|
|
15974
|
-
if (
|
|
16133
|
+
const root = gitPathFromOutput(result.stdout, cwd);
|
|
16134
|
+
if (root === undefined) {
|
|
15975
16135
|
throw new Error(`gitWorktreeDir could not resolve the repository top level from ${cwd}: git returned an empty path.`);
|
|
15976
16136
|
}
|
|
15977
|
-
return
|
|
16137
|
+
return { repository: { root, ...cwdWithinGitRepository(cwd, root) } };
|
|
16138
|
+
}
|
|
16139
|
+
function resolveGitWorktreeSetupRequest(options) {
|
|
16140
|
+
const probe = probeInvokingRepository(options.cwd);
|
|
16141
|
+
if ("repository" in probe) {
|
|
16142
|
+
return {
|
|
16143
|
+
options,
|
|
16144
|
+
repository: probe.repository,
|
|
16145
|
+
target: resolveGitWorktreePath(options.gitWorktreeDir, probe.repository.logicalRoot)
|
|
16146
|
+
};
|
|
16147
|
+
}
|
|
16148
|
+
return {
|
|
16149
|
+
options,
|
|
16150
|
+
repositoryProbeFailure: probe.failure,
|
|
16151
|
+
target: resolveGitWorktreePath(options.gitWorktreeDir, canonicalizePreservingSymlinks(options.cwd))
|
|
16152
|
+
};
|
|
15978
16153
|
}
|
|
15979
16154
|
function gitTopLevelFromResult(result, cwd, description) {
|
|
15980
16155
|
if (result.status !== 0) {
|
|
@@ -16010,25 +16185,25 @@ function dirnameForEachRelativeComponent(base, relativePath) {
|
|
|
16010
16185
|
continue;
|
|
16011
16186
|
if (component === "..")
|
|
16012
16187
|
return;
|
|
16013
|
-
current =
|
|
16188
|
+
current = path4.dirname(current);
|
|
16014
16189
|
}
|
|
16015
16190
|
return current;
|
|
16016
16191
|
}
|
|
16017
16192
|
function cwdWithinGitRepository(cwd, repoRoot) {
|
|
16018
|
-
const sourceCwd =
|
|
16019
|
-
const sourceRepoRoot =
|
|
16020
|
-
const relativeCwd =
|
|
16021
|
-
const safeRelativeCwd = relativeCwd === "" || relativeCwd.startsWith("..") ||
|
|
16193
|
+
const sourceCwd = fs4.realpathSync.native(cwd);
|
|
16194
|
+
const sourceRepoRoot = fs4.realpathSync.native(repoRoot);
|
|
16195
|
+
const relativeCwd = path4.relative(sourceRepoRoot, sourceCwd);
|
|
16196
|
+
const safeRelativeCwd = relativeCwd === "" || relativeCwd.startsWith("..") || path4.isAbsolute(relativeCwd) ? "" : relativeCwd;
|
|
16022
16197
|
const logicalCwd = canonicalizePreservingSymlinks(cwd);
|
|
16023
16198
|
return {
|
|
16024
16199
|
relativeCwd: safeRelativeCwd,
|
|
16025
|
-
|
|
16200
|
+
logicalRoot: dirnameForEachRelativeComponent(logicalCwd, safeRelativeCwd) ?? repoRoot
|
|
16026
16201
|
};
|
|
16027
16202
|
}
|
|
16028
16203
|
function workspaceCwdForGitWorktreeRoot(worktreeRoot, relativeCwd) {
|
|
16029
|
-
return relativeCwd === "" ? worktreeRoot :
|
|
16204
|
+
return relativeCwd === "" ? worktreeRoot : path4.join(worktreeRoot, relativeCwd);
|
|
16030
16205
|
}
|
|
16031
|
-
function
|
|
16206
|
+
function validateGitWorktreeRoot(worktreeRoot) {
|
|
16032
16207
|
const topLevel = gitTopLevel(worktreeRoot);
|
|
16033
16208
|
if (topLevel === undefined) {
|
|
16034
16209
|
throw new Error(`gitWorktreeDir already exists but is not a Git worktree: ${worktreeRoot}`);
|
|
@@ -16036,8 +16211,18 @@ function validateExistingGitWorktreeRoot(worktreeRoot, repoRoot) {
|
|
|
16036
16211
|
if (comparableRealPath(worktreeRoot) !== comparableRealPath(topLevel)) {
|
|
16037
16212
|
throw new Error(`gitWorktreeDir already exists but is not a Git worktree root: ${worktreeRoot}. Git top-level checkout is ${topLevel}`);
|
|
16038
16213
|
}
|
|
16039
|
-
|
|
16040
|
-
|
|
16214
|
+
return gitCommonDirForWorktree(topLevel);
|
|
16215
|
+
}
|
|
16216
|
+
function existingGitWorktreeCommonDir(directory) {
|
|
16217
|
+
return gitTopLevel(directory) === undefined ? undefined : validateGitWorktreeRoot(directory);
|
|
16218
|
+
}
|
|
16219
|
+
function sameGitCommonDir(left, right) {
|
|
16220
|
+
return comparableRealPath(left) === comparableRealPath(right);
|
|
16221
|
+
}
|
|
16222
|
+
function validateCreatedGitWorktree(worktreeRoot, repository) {
|
|
16223
|
+
assertSafeGitWorktreeTarget(worktreeRoot, repository.root);
|
|
16224
|
+
if (!sameGitCommonDir(validateGitWorktreeRoot(worktreeRoot), gitCommonDirForWorktree(repository.root))) {
|
|
16225
|
+
throw new Error(`gitWorktreeDir does not belong to the invoking Git repository: ${worktreeRoot}`);
|
|
16041
16226
|
}
|
|
16042
16227
|
}
|
|
16043
16228
|
function gitDirectoryForWorktree(cwd) {
|
|
@@ -16051,26 +16236,27 @@ function gitDirectoryForWorktree(cwd) {
|
|
|
16051
16236
|
return gitDir;
|
|
16052
16237
|
}
|
|
16053
16238
|
function captureGitWorktreeIdentitySnapshot(setup) {
|
|
16054
|
-
|
|
16055
|
-
|
|
16056
|
-
const
|
|
16239
|
+
const { worktreeRoot, repositoryRoot } = setup.result;
|
|
16240
|
+
assertSafeGitWorktreeTarget(worktreeRoot, setup.invokingRoot);
|
|
16241
|
+
const commonDir = validateGitWorktreeRoot(worktreeRoot);
|
|
16242
|
+
const stats = fs4.statSync(worktreeRoot);
|
|
16057
16243
|
return {
|
|
16058
|
-
worktreeRealPath: comparableRealPath(
|
|
16059
|
-
repositoryRealPath: comparableRealPath(
|
|
16060
|
-
gitDirRealPath: comparableRealPath(gitDirectoryForWorktree(
|
|
16061
|
-
commonDirRealPath: comparableRealPath(
|
|
16244
|
+
worktreeRealPath: comparableRealPath(worktreeRoot),
|
|
16245
|
+
repositoryRealPath: comparableRealPath(repositoryRoot),
|
|
16246
|
+
gitDirRealPath: comparableRealPath(gitDirectoryForWorktree(worktreeRoot)),
|
|
16247
|
+
commonDirRealPath: comparableRealPath(commonDir),
|
|
16062
16248
|
device: stats.dev,
|
|
16063
16249
|
inode: stats.ino
|
|
16064
16250
|
};
|
|
16065
16251
|
}
|
|
16066
16252
|
function captureGitWorktreeIdentity(setup) {
|
|
16067
16253
|
const snapshot = captureGitWorktreeIdentitySnapshot(setup);
|
|
16068
|
-
return { ...snapshot, generation: openGitWorktreeGenerationAnchor(setup.worktreeRoot) };
|
|
16254
|
+
return { ...snapshot, generation: openGitWorktreeGenerationAnchor(setup.result.worktreeRoot) };
|
|
16069
16255
|
}
|
|
16070
16256
|
function assertCachedGitWorktreeIdentity(cached, options) {
|
|
16071
16257
|
let current;
|
|
16072
16258
|
try {
|
|
16073
|
-
current = captureGitWorktreeIdentitySnapshot(cached
|
|
16259
|
+
current = captureGitWorktreeIdentitySnapshot(cached);
|
|
16074
16260
|
cached.identity.generation.assertCurrent();
|
|
16075
16261
|
} catch (error) {
|
|
16076
16262
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -16078,34 +16264,58 @@ function assertCachedGitWorktreeIdentity(cached, options) {
|
|
|
16078
16264
|
}
|
|
16079
16265
|
const expected = cached.identity;
|
|
16080
16266
|
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) {
|
|
16081
|
-
throw new Error(`Cached gitWorktreeDir changed before reuse: ${options.gitWorktreeDir}. Start a new run with the intended
|
|
16082
|
-
}
|
|
16267
|
+
throw new Error(`Cached gitWorktreeDir changed before reuse: ${options.gitWorktreeDir}. Start a new run with the intended worktree.`);
|
|
16268
|
+
}
|
|
16269
|
+
}
|
|
16270
|
+
function requireInvokingRepository(request, action) {
|
|
16271
|
+
if (request.repository !== undefined)
|
|
16272
|
+
return request.repository;
|
|
16273
|
+
throw new Error([
|
|
16274
|
+
`gitWorktreeDir requires the workflow to be invoked from inside a Git repository to ${action}.`,
|
|
16275
|
+
"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.",
|
|
16276
|
+
`Git reported: ${request.repositoryProbeFailure ?? "no repository"}`
|
|
16277
|
+
].join(" "));
|
|
16278
|
+
}
|
|
16279
|
+
function setupGitWorktreeFromRequest(request, ownerRunId) {
|
|
16280
|
+
const { repository, target } = request;
|
|
16281
|
+
assertSafeGitWorktreeTarget(target, repository?.root);
|
|
16282
|
+
const entry = pathStatSync(target);
|
|
16283
|
+
if (entry === undefined)
|
|
16284
|
+
return createGitWorktreeAtPath(request, false);
|
|
16285
|
+
if (!entry.isDirectory())
|
|
16286
|
+
throw new Error(`gitWorktreeDir already exists but is not a directory: ${target}`);
|
|
16287
|
+
if (fs4.readdirSync(target).length === 0)
|
|
16288
|
+
return createGitWorktreeAtPath(request, true);
|
|
16289
|
+
const commonDir = existingGitWorktreeCommonDir(target);
|
|
16290
|
+
if (commonDir === undefined)
|
|
16291
|
+
return allocateGitWorktree(request, ownerRunId);
|
|
16292
|
+
const sameRepository = repository !== undefined && sameGitCommonDir(commonDir, gitCommonDirForWorktree(repository.root));
|
|
16293
|
+
return {
|
|
16294
|
+
result: {
|
|
16295
|
+
worktreeRoot: target,
|
|
16296
|
+
cwd: sameRepository ? workspaceCwdForGitWorktreeRoot(target, repository.relativeCwd) : target,
|
|
16297
|
+
repositoryRoot: sameRepository ? repository.root : target,
|
|
16298
|
+
created: false
|
|
16299
|
+
},
|
|
16300
|
+
...repository !== undefined ? { invokingRoot: repository.root } : {}
|
|
16301
|
+
};
|
|
16083
16302
|
}
|
|
16084
|
-
function
|
|
16085
|
-
const
|
|
16086
|
-
const
|
|
16303
|
+
function createGitWorktreeAtPath(request, existingEmptyDirectory) {
|
|
16304
|
+
const { target: worktreeRoot } = request;
|
|
16305
|
+
const repository = requireInvokingRepository(request, `create a new worktree at ${worktreeRoot}`);
|
|
16306
|
+
const mainRoot = findCanonicalGitRoot(repository.root);
|
|
16087
16307
|
if (mainRoot === undefined) {
|
|
16088
|
-
throw new Error(`Unable to resolve the canonical main Git repository root from ${
|
|
16308
|
+
throw new Error(`Unable to resolve the canonical main Git repository root from ${repository.root}.`);
|
|
16089
16309
|
}
|
|
16090
|
-
|
|
16091
|
-
|
|
16092
|
-
|
|
16093
|
-
|
|
16094
|
-
|
|
16095
|
-
|
|
16096
|
-
|
|
16097
|
-
cwd: workspaceCwdForGitWorktreeRoot(worktreeRoot, relativeCwd),
|
|
16098
|
-
repositoryRoot: repoRoot,
|
|
16099
|
-
created: false
|
|
16100
|
-
};
|
|
16101
|
-
}
|
|
16102
|
-
try {
|
|
16103
|
-
fs3.mkdirSync(path3.dirname(worktreeRoot), { recursive: true });
|
|
16104
|
-
} catch (error) {
|
|
16105
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
16106
|
-
throw new Error(`Failed to create parent directory for requested gitWorktreeDir ${worktreeRoot}: ${message}`);
|
|
16310
|
+
if (!existingEmptyDirectory) {
|
|
16311
|
+
try {
|
|
16312
|
+
fs4.mkdirSync(path4.dirname(worktreeRoot), { recursive: true });
|
|
16313
|
+
} catch (error) {
|
|
16314
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16315
|
+
throw new Error(`Failed to create parent directory for requested gitWorktreeDir ${worktreeRoot}: ${message}`);
|
|
16316
|
+
}
|
|
16107
16317
|
}
|
|
16108
|
-
const baseRef = options.baseBranch?.trim() || "HEAD";
|
|
16318
|
+
const baseRef = request.options.baseBranch?.trim() || "HEAD";
|
|
16109
16319
|
const result = runGit(mainRoot, ["worktree", "add", "--detach", worktreeRoot, baseRef]);
|
|
16110
16320
|
if (result.status !== 0) {
|
|
16111
16321
|
throw new Error([
|
|
@@ -16115,30 +16325,127 @@ function setupGitWorktree(options) {
|
|
|
16115
16325
|
`));
|
|
16116
16326
|
}
|
|
16117
16327
|
try {
|
|
16118
|
-
|
|
16119
|
-
validateExistingGitWorktreeRoot(worktreeRoot, repoRoot);
|
|
16328
|
+
validateCreatedGitWorktree(worktreeRoot, repository);
|
|
16120
16329
|
} catch (error) {
|
|
16121
16330
|
runGit(mainRoot, ["worktree", "remove", "--force", worktreeRoot]);
|
|
16122
16331
|
const message = error instanceof Error ? error.message : String(error);
|
|
16123
16332
|
throw new Error(`Created gitWorktreeDir failed post-creation validation: ${message}`);
|
|
16124
16333
|
}
|
|
16125
16334
|
return {
|
|
16126
|
-
|
|
16127
|
-
|
|
16128
|
-
|
|
16129
|
-
|
|
16335
|
+
result: {
|
|
16336
|
+
worktreeRoot,
|
|
16337
|
+
cwd: workspaceCwdForGitWorktreeRoot(worktreeRoot, repository.relativeCwd),
|
|
16338
|
+
repositoryRoot: repository.root,
|
|
16339
|
+
created: true
|
|
16340
|
+
},
|
|
16341
|
+
invokingRoot: repository.root
|
|
16130
16342
|
};
|
|
16131
16343
|
}
|
|
16344
|
+
function containedWorktreeCwd(checkout, relativeCwd) {
|
|
16345
|
+
const cwd = workspaceCwdForGitWorktreeRoot(checkout, relativeCwd);
|
|
16346
|
+
let resolved;
|
|
16347
|
+
try {
|
|
16348
|
+
resolved = fs4.realpathSync.native(cwd);
|
|
16349
|
+
} catch {
|
|
16350
|
+
resolved = "";
|
|
16351
|
+
}
|
|
16352
|
+
const relative2 = resolved === "" ? undefined : path4.relative(fs4.realpathSync.native(checkout), resolved);
|
|
16353
|
+
if (relative2 === undefined || relative2 === ".." || relative2.startsWith(`..${path4.sep}`) || path4.isAbsolute(relative2) || !fs4.statSync(resolved).isDirectory()) {
|
|
16354
|
+
throw new Error(`The selected base does not contain a working directory ${relativeCwd || "."} inside the worktree ${checkout}.`);
|
|
16355
|
+
}
|
|
16356
|
+
return cwd;
|
|
16357
|
+
}
|
|
16358
|
+
function allocatedGitWorktreeSetup(checkout, repository, created) {
|
|
16359
|
+
return {
|
|
16360
|
+
result: {
|
|
16361
|
+
worktreeRoot: checkout,
|
|
16362
|
+
cwd: containedWorktreeCwd(checkout, repository.relativeCwd),
|
|
16363
|
+
repositoryRoot: repository.root,
|
|
16364
|
+
created
|
|
16365
|
+
},
|
|
16366
|
+
invokingRoot: repository.root
|
|
16367
|
+
};
|
|
16368
|
+
}
|
|
16369
|
+
function validateOwnedGitWorktree(checkout, repository) {
|
|
16370
|
+
validateCreatedGitWorktree(checkout, repository);
|
|
16371
|
+
const gitDir = gitDirectoryForWorktree(checkout);
|
|
16372
|
+
if (sameGitCommonDir(gitDir, gitCommonDirForWorktree(checkout))) {
|
|
16373
|
+
throw new Error(`${checkout} is not a linked worktree`);
|
|
16374
|
+
}
|
|
16375
|
+
const backlink = fs4.readFileSync(path4.join(gitDir, "gitdir"), "utf8").trim();
|
|
16376
|
+
if (!backlink || comparableRealPath(path4.resolve(gitDir, backlink)) !== comparableRealPath(path4.join(checkout, ".git"))) {
|
|
16377
|
+
throw new Error(`the Git directory of ${checkout} belongs to another checkout`);
|
|
16378
|
+
}
|
|
16379
|
+
}
|
|
16380
|
+
function findOwnedGitWorktree(root, repository, ownerRunId) {
|
|
16381
|
+
const [checkout] = findOwnedWorktreeCheckouts(root, path4.basename(repository.root), ownerRunId);
|
|
16382
|
+
if (checkout === undefined)
|
|
16383
|
+
return;
|
|
16384
|
+
try {
|
|
16385
|
+
validateOwnedGitWorktree(checkout, repository);
|
|
16386
|
+
return allocatedGitWorktreeSetup(checkout, repository, false);
|
|
16387
|
+
} catch (error) {
|
|
16388
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16389
|
+
throw new Error(`The worktree this workflow run allocated under gitWorktreeDir ${root} can no longer be reused: ${checkout}. ${message}`);
|
|
16390
|
+
}
|
|
16391
|
+
}
|
|
16392
|
+
function allocateGitWorktree(request, ownerRunId) {
|
|
16393
|
+
const { target: root } = request;
|
|
16394
|
+
const repository = requireInvokingRepository(request, `create a new worktree under ${root}`);
|
|
16395
|
+
if (ownerRunId !== undefined) {
|
|
16396
|
+
const owned = findOwnedGitWorktree(root, repository, ownerRunId);
|
|
16397
|
+
if (owned !== undefined)
|
|
16398
|
+
return owned;
|
|
16399
|
+
}
|
|
16400
|
+
const baseRef = request.options.baseBranch?.trim() || "HEAD";
|
|
16401
|
+
const revision = runGit(repository.root, ["rev-parse", "--verify", "--end-of-options", `${baseRef}^{commit}`]);
|
|
16402
|
+
const commit = revision.status === 0 ? revision.stdout.trim() : "";
|
|
16403
|
+
if (commit === "") {
|
|
16404
|
+
throw new Error(`Failed to resolve ${baseRef} to a commit for gitWorktreeDir ${root}. Git reported: ${gitFailureMessage(revision)}`);
|
|
16405
|
+
}
|
|
16406
|
+
const checkout = allocateWorktreeCheckoutPath(root, path4.basename(repository.root));
|
|
16407
|
+
const add = runGit(repository.root, ["worktree", "add", "--detach", "--no-checkout", checkout, commit]);
|
|
16408
|
+
if (add.status !== 0) {
|
|
16409
|
+
removeEmptyWorktreeBucket(checkout);
|
|
16410
|
+
throw new Error(`Failed to create git worktree at ${checkout} under gitWorktreeDir ${root} from ${baseRef}. Git reported: ${gitFailureMessage(add)}`);
|
|
16411
|
+
}
|
|
16412
|
+
try {
|
|
16413
|
+
const gitDir = gitDirectoryForWorktree(checkout);
|
|
16414
|
+
runGitOrThrow(checkout, ["config", "--file", path4.join(gitDir, "config.worktree"), "core.worktree", checkout]);
|
|
16415
|
+
runGitOrThrow(checkout, ["--work-tree=.", "reset", "--hard", "--no-recurse-submodules", commit]);
|
|
16416
|
+
validateCreatedGitWorktree(checkout, repository);
|
|
16417
|
+
const setup = allocatedGitWorktreeSetup(checkout, repository, true);
|
|
16418
|
+
if (ownerRunId !== undefined)
|
|
16419
|
+
bindWorktreeOwner(gitDir, ownerRunId);
|
|
16420
|
+
return setup;
|
|
16421
|
+
} catch (error) {
|
|
16422
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16423
|
+
const removal = runGit(repository.root, ["worktree", "remove", "--force", checkout]);
|
|
16424
|
+
removeEmptyWorktreeBucket(checkout);
|
|
16425
|
+
const rollback = removal.status === 0 ? "" : ` Rolling back the incomplete worktree also failed; remove it with: ${worktreeRecoveryCommand(repository.root, checkout)}`;
|
|
16426
|
+
throw new Error(`Failed to set up git worktree at ${checkout} under gitWorktreeDir ${root}: ${message}${rollback}`);
|
|
16427
|
+
}
|
|
16428
|
+
}
|
|
16429
|
+
function runGitOrThrow(cwd, args) {
|
|
16430
|
+
const result = runGit(cwd, args);
|
|
16431
|
+
if (result.status !== 0)
|
|
16432
|
+
throw new Error(gitFailureMessage(result));
|
|
16433
|
+
}
|
|
16434
|
+
function setupGitWorktree(options) {
|
|
16435
|
+
return setupGitWorktreeFromRequest(resolveGitWorktreeSetupRequest(options)).result;
|
|
16436
|
+
}
|
|
16132
16437
|
var init_worktree_git = __esm(() => {
|
|
16438
|
+
init_worktree_allocation();
|
|
16133
16439
|
init_worktree_generation();
|
|
16134
16440
|
init_worktree_git_runner();
|
|
16135
16441
|
init_worktree_root();
|
|
16442
|
+
init_worktree_allocation();
|
|
16136
16443
|
init_worktree_git_runner();
|
|
16137
16444
|
});
|
|
16138
16445
|
|
|
16139
16446
|
// dist/builtin/workflows/src/runs/shared/worktree-cache-lifecycle.ts
|
|
16140
|
-
function createGitWorktreeSetupCacheOwner(suppliedCache) {
|
|
16141
|
-
const cache = suppliedCache ?? createGitWorktreeSetupCache();
|
|
16447
|
+
function createGitWorktreeSetupCacheOwner(suppliedCache, ownerRunId) {
|
|
16448
|
+
const cache = suppliedCache ?? createGitWorktreeSetupCache(ownerRunId);
|
|
16142
16449
|
const ownsCache = suppliedCache === undefined;
|
|
16143
16450
|
let released = false;
|
|
16144
16451
|
return {
|
|
@@ -16159,20 +16466,20 @@ var init_worktree_cache_lifecycle = __esm(() => {
|
|
|
16159
16466
|
});
|
|
16160
16467
|
|
|
16161
16468
|
// dist/builtin/workflows/src/runs/shared/worktree-diff.ts
|
|
16162
|
-
import * as
|
|
16163
|
-
import * as
|
|
16469
|
+
import * as fs5 from "node:fs";
|
|
16470
|
+
import * as path5 from "node:path";
|
|
16164
16471
|
function safePatchAgentName(agent) {
|
|
16165
16472
|
return agent.replace(/[^\w.-]/g, "_");
|
|
16166
16473
|
}
|
|
16167
16474
|
function removeSyntheticPath(worktree, syntheticPath) {
|
|
16168
|
-
const resolved =
|
|
16169
|
-
const relative =
|
|
16170
|
-
if (!relative || relative === "." || relative === ".." || relative.startsWith(`..${
|
|
16475
|
+
const resolved = path5.resolve(worktree.path, syntheticPath);
|
|
16476
|
+
const relative = path5.relative(worktree.path, resolved);
|
|
16477
|
+
if (!relative || relative === "." || relative === ".." || relative.startsWith(`..${path5.sep}`) || path5.isAbsolute(relative)) {
|
|
16171
16478
|
return;
|
|
16172
16479
|
}
|
|
16173
16480
|
let stat;
|
|
16174
16481
|
try {
|
|
16175
|
-
stat =
|
|
16482
|
+
stat = fs5.lstatSync(resolved);
|
|
16176
16483
|
} catch (error) {
|
|
16177
16484
|
const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
|
|
16178
16485
|
if (code === "ENOENT")
|
|
@@ -16180,14 +16487,14 @@ function removeSyntheticPath(worktree, syntheticPath) {
|
|
|
16180
16487
|
throw error;
|
|
16181
16488
|
}
|
|
16182
16489
|
if (stat.isSymbolicLink()) {
|
|
16183
|
-
|
|
16490
|
+
fs5.unlinkSync(resolved);
|
|
16184
16491
|
return;
|
|
16185
16492
|
}
|
|
16186
16493
|
if (stat.isDirectory()) {
|
|
16187
|
-
|
|
16494
|
+
fs5.rmSync(resolved, { recursive: true, force: true });
|
|
16188
16495
|
return;
|
|
16189
16496
|
}
|
|
16190
|
-
|
|
16497
|
+
fs5.rmSync(resolved, { force: true });
|
|
16191
16498
|
}
|
|
16192
16499
|
function removeSyntheticPathsBeforeDiff(worktree) {
|
|
16193
16500
|
if (worktree.syntheticPaths.length === 0)
|
|
@@ -16236,7 +16543,7 @@ function captureWorktreeDiff(setup, worktree, agent, patchPath) {
|
|
|
16236
16543
|
const diffStat = runGitChecked(worktree.path, ["diff", "--cached", "--stat", setup.baseCommit]).trim();
|
|
16237
16544
|
const patch = runGitChecked(worktree.path, ["diff", "--cached", setup.baseCommit]);
|
|
16238
16545
|
const numstat = runGitChecked(worktree.path, ["diff", "--cached", "--numstat", setup.baseCommit]);
|
|
16239
|
-
|
|
16546
|
+
fs5.writeFileSync(patchPath, patch, "utf-8");
|
|
16240
16547
|
if (!patch.trim()) {
|
|
16241
16548
|
return emptyDiff(worktree.index, agent, worktree.branch, patchPath);
|
|
16242
16549
|
}
|
|
@@ -16254,7 +16561,7 @@ function captureWorktreeDiff(setup, worktree, agent, patchPath) {
|
|
|
16254
16561
|
}
|
|
16255
16562
|
function writeEmptyPatch(patchPath) {
|
|
16256
16563
|
try {
|
|
16257
|
-
|
|
16564
|
+
fs5.writeFileSync(patchPath, "", "utf-8");
|
|
16258
16565
|
} catch {}
|
|
16259
16566
|
}
|
|
16260
16567
|
function hasWorktreeChanges(diff) {
|
|
@@ -16262,7 +16569,7 @@ function hasWorktreeChanges(diff) {
|
|
|
16262
16569
|
}
|
|
16263
16570
|
function diffWorktrees(setup, agents, diffsDir) {
|
|
16264
16571
|
try {
|
|
16265
|
-
|
|
16572
|
+
fs5.mkdirSync(diffsDir, { recursive: true });
|
|
16266
16573
|
} catch {
|
|
16267
16574
|
return [];
|
|
16268
16575
|
}
|
|
@@ -16270,7 +16577,7 @@ function diffWorktrees(setup, agents, diffsDir) {
|
|
|
16270
16577
|
for (let index = 0;index < setup.worktrees.length; index++) {
|
|
16271
16578
|
const worktree = setup.worktrees[index];
|
|
16272
16579
|
const agent = agents[index] ?? `task-${index + 1}`;
|
|
16273
|
-
const patchPath =
|
|
16580
|
+
const patchPath = path5.join(diffsDir, `task-${index}-${safePatchAgentName(agent)}.patch`);
|
|
16274
16581
|
try {
|
|
16275
16582
|
diffs.push(captureWorktreeDiff(setup, worktree, agent, patchPath));
|
|
16276
16583
|
} catch {
|
|
@@ -16292,7 +16599,7 @@ function formatWorktreeDiffSummary(diffs) {
|
|
|
16292
16599
|
}
|
|
16293
16600
|
lines.push("");
|
|
16294
16601
|
}
|
|
16295
|
-
const patchesDir =
|
|
16602
|
+
const patchesDir = path5.dirname(changed[0].patchPath);
|
|
16296
16603
|
lines.push(`Full patches: ${patchesDir}`);
|
|
16297
16604
|
return lines.join(`
|
|
16298
16605
|
`).trimEnd();
|
|
@@ -16302,8 +16609,8 @@ var init_worktree_diff = __esm(() => {
|
|
|
16302
16609
|
});
|
|
16303
16610
|
|
|
16304
16611
|
// dist/builtin/workflows/src/runs/shared/worktree-post-create.ts
|
|
16305
|
-
import * as
|
|
16306
|
-
import * as
|
|
16612
|
+
import * as fs6 from "node:fs";
|
|
16613
|
+
import * as path6 from "node:path";
|
|
16307
16614
|
function tracked(repo, relativePath) {
|
|
16308
16615
|
const result = runGit(repo, ["ls-files", "--error-unmatch", "--", relativePath]);
|
|
16309
16616
|
return result.status === 0;
|
|
@@ -16323,12 +16630,12 @@ function hasTrackedPathComponent(repo, relativePath) {
|
|
|
16323
16630
|
return false;
|
|
16324
16631
|
}
|
|
16325
16632
|
function copyUntrackedFile(mainRoot, worktreeRoot, relativePath) {
|
|
16326
|
-
const source =
|
|
16327
|
-
const destination =
|
|
16328
|
-
if (!
|
|
16633
|
+
const source = path6.join(mainRoot, relativePath);
|
|
16634
|
+
const destination = path6.join(worktreeRoot, relativePath);
|
|
16635
|
+
if (!fs6.existsSync(source) || fs6.existsSync(destination) || hasTrackedPathComponent(worktreeRoot, relativePath))
|
|
16329
16636
|
return false;
|
|
16330
|
-
|
|
16331
|
-
|
|
16637
|
+
fs6.mkdirSync(path6.dirname(destination), { recursive: true });
|
|
16638
|
+
fs6.copyFileSync(source, destination);
|
|
16332
16639
|
return true;
|
|
16333
16640
|
}
|
|
16334
16641
|
function propagateLocalSettings(mainRoot, worktreeRoot) {
|
|
@@ -16341,14 +16648,14 @@ function propagateLocalSettings(mainRoot, worktreeRoot) {
|
|
|
16341
16648
|
return copied;
|
|
16342
16649
|
}
|
|
16343
16650
|
function populatedHooksDirectory(mainRoot) {
|
|
16344
|
-
const husky =
|
|
16651
|
+
const husky = path6.join(mainRoot, ".husky");
|
|
16345
16652
|
try {
|
|
16346
|
-
if (
|
|
16653
|
+
if (fs6.statSync(husky).isDirectory())
|
|
16347
16654
|
return husky;
|
|
16348
16655
|
} catch {}
|
|
16349
|
-
const hooks =
|
|
16656
|
+
const hooks = path6.join(mainRoot, ".git", "hooks");
|
|
16350
16657
|
try {
|
|
16351
|
-
const populated =
|
|
16658
|
+
const populated = fs6.readdirSync(hooks, { withFileTypes: true }).some((entry) => entry.isFile() && !entry.name.endsWith(".sample"));
|
|
16352
16659
|
return populated ? hooks : undefined;
|
|
16353
16660
|
} catch {
|
|
16354
16661
|
return;
|
|
@@ -16359,7 +16666,7 @@ function configureSharedHooksPath(mainRoot) {
|
|
|
16359
16666
|
if (desired === undefined)
|
|
16360
16667
|
return;
|
|
16361
16668
|
const current = runGitPlain(mainRoot, ["config", "--get", "core.hooksPath"]);
|
|
16362
|
-
if (current.status === 0 &&
|
|
16669
|
+
if (current.status === 0 && path6.resolve(mainRoot, current.stdout.trim()) === desired)
|
|
16363
16670
|
return;
|
|
16364
16671
|
const configured = runGitPlain(mainRoot, ["config", "core.hooksPath", desired]);
|
|
16365
16672
|
if (configured.status !== 0) {
|
|
@@ -16368,10 +16675,10 @@ function configureSharedHooksPath(mainRoot) {
|
|
|
16368
16675
|
}
|
|
16369
16676
|
function normalizeSymlinkDirectory(value2) {
|
|
16370
16677
|
const trimmed = value2.trim();
|
|
16371
|
-
if (!trimmed ||
|
|
16678
|
+
if (!trimmed || path6.isAbsolute(trimmed))
|
|
16372
16679
|
return;
|
|
16373
|
-
const normalized =
|
|
16374
|
-
if (normalized === "." || normalized === ".." || normalized.startsWith(`..${
|
|
16680
|
+
const normalized = path6.normalize(trimmed);
|
|
16681
|
+
if (normalized === "." || normalized === ".." || normalized.startsWith(`..${path6.sep}`))
|
|
16375
16682
|
return;
|
|
16376
16683
|
return normalized;
|
|
16377
16684
|
}
|
|
@@ -16381,20 +16688,20 @@ function symlinkConfiguredDirectories(mainRoot, worktreeRoot, directories) {
|
|
|
16381
16688
|
const relativePath = normalizeSymlinkDirectory(rawDirectory);
|
|
16382
16689
|
if (relativePath === undefined || synthetic.includes(relativePath))
|
|
16383
16690
|
continue;
|
|
16384
|
-
const source =
|
|
16385
|
-
const destination =
|
|
16386
|
-
if (!
|
|
16691
|
+
const source = path6.join(mainRoot, relativePath);
|
|
16692
|
+
const destination = path6.join(worktreeRoot, relativePath);
|
|
16693
|
+
if (!fs6.existsSync(source) || fs6.existsSync(destination) || hasTrackedPathComponent(worktreeRoot, relativePath))
|
|
16387
16694
|
continue;
|
|
16388
|
-
|
|
16695
|
+
fs6.mkdirSync(path6.dirname(destination), { recursive: true });
|
|
16389
16696
|
try {
|
|
16390
|
-
|
|
16697
|
+
fs6.symlinkSync(source, destination, process.platform === "win32" ? "junction" : undefined);
|
|
16391
16698
|
synthetic.push(relativePath);
|
|
16392
16699
|
} catch {}
|
|
16393
16700
|
}
|
|
16394
16701
|
return synthetic;
|
|
16395
16702
|
}
|
|
16396
16703
|
function copyWorktreeIncludes(mainRoot, worktreeRoot) {
|
|
16397
|
-
if (!
|
|
16704
|
+
if (!fs6.existsSync(path6.join(mainRoot, ".worktreeinclude")))
|
|
16398
16705
|
return;
|
|
16399
16706
|
const ignored = runGit(mainRoot, ["ls-files", "--others", "--ignored", "--exclude-standard"]);
|
|
16400
16707
|
const included = runGit(mainRoot, ["ls-files", "--others", "--ignored", "--exclude-from=.worktreeinclude"]);
|
|
@@ -16404,13 +16711,13 @@ function copyWorktreeIncludes(mainRoot, worktreeRoot) {
|
|
|
16404
16711
|
for (const relativePath of included.stdout.split(/\r?\n/).filter(Boolean)) {
|
|
16405
16712
|
if (!ignoredPaths.has(relativePath) || hasTrackedPathComponent(worktreeRoot, relativePath))
|
|
16406
16713
|
continue;
|
|
16407
|
-
const source =
|
|
16408
|
-
const destination =
|
|
16714
|
+
const source = path6.join(mainRoot, relativePath);
|
|
16715
|
+
const destination = path6.join(worktreeRoot, relativePath);
|
|
16409
16716
|
try {
|
|
16410
|
-
if (!
|
|
16717
|
+
if (!fs6.statSync(source).isFile() || fs6.existsSync(destination))
|
|
16411
16718
|
continue;
|
|
16412
|
-
|
|
16413
|
-
|
|
16719
|
+
fs6.mkdirSync(path6.dirname(destination), { recursive: true });
|
|
16720
|
+
fs6.copyFileSync(source, destination);
|
|
16414
16721
|
} catch {}
|
|
16415
16722
|
}
|
|
16416
16723
|
}
|
|
@@ -16427,9 +16734,9 @@ var init_worktree_post_create = __esm(() => {
|
|
|
16427
16734
|
|
|
16428
16735
|
// dist/builtin/workflows/src/runs/shared/worktree-setup.ts
|
|
16429
16736
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
16430
|
-
import * as
|
|
16737
|
+
import * as fs7 from "node:fs";
|
|
16431
16738
|
import * as os from "node:os";
|
|
16432
|
-
import * as
|
|
16739
|
+
import * as path7 from "node:path";
|
|
16433
16740
|
import { createChildProcessEnvironment as createChildProcessEnvironment2 } from "@bastani/atomic";
|
|
16434
16741
|
function originDefaultBranch(mainRoot) {
|
|
16435
16742
|
const symbolic = runGit(mainRoot, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"]);
|
|
@@ -16449,7 +16756,7 @@ function originDefaultBranch(mainRoot) {
|
|
|
16449
16756
|
}
|
|
16450
16757
|
function resolveRepoState(cwd, explicitBaseBranch) {
|
|
16451
16758
|
const cwdRelative = resolveRepoCwdRelative(cwd);
|
|
16452
|
-
const toplevel =
|
|
16759
|
+
const toplevel = path7.resolve(runGitChecked(cwd, ["rev-parse", "--show-toplevel"]).trim());
|
|
16453
16760
|
const mainRoot = findCanonicalGitRoot(toplevel);
|
|
16454
16761
|
if (mainRoot === undefined)
|
|
16455
16762
|
throw new Error(`unable to resolve canonical main Git repository root from ${toplevel}`);
|
|
@@ -16462,9 +16769,9 @@ function resolveRepoState(cwd, explicitBaseBranch) {
|
|
|
16462
16769
|
return { toplevel, mainRoot, cwdRelative, baseCommit, baseRef };
|
|
16463
16770
|
}
|
|
16464
16771
|
function normalizeComparableCwd(cwd) {
|
|
16465
|
-
const resolved =
|
|
16772
|
+
const resolved = path7.resolve(cwd);
|
|
16466
16773
|
try {
|
|
16467
|
-
return
|
|
16774
|
+
return fs7.realpathSync(resolved);
|
|
16468
16775
|
} catch {
|
|
16469
16776
|
return resolved;
|
|
16470
16777
|
}
|
|
@@ -16475,7 +16782,7 @@ function findWorktreeTaskCwdConflict(tasks, sharedCwd) {
|
|
|
16475
16782
|
const task = tasks[index];
|
|
16476
16783
|
if (!task.cwd)
|
|
16477
16784
|
continue;
|
|
16478
|
-
const taskCwd =
|
|
16785
|
+
const taskCwd = path7.isAbsolute(task.cwd) ? task.cwd : path7.resolve(sharedCwd, task.cwd);
|
|
16479
16786
|
if (normalizeComparableCwd(taskCwd) === normalizedSharedCwd)
|
|
16480
16787
|
continue;
|
|
16481
16788
|
return { index, agent: task.agent, cwd: task.cwd };
|
|
@@ -16492,12 +16799,12 @@ function buildWorktreeBranch(runId, index) {
|
|
|
16492
16799
|
return `worktree-${flattenedWorktreeName(runId, index)}`;
|
|
16493
16800
|
}
|
|
16494
16801
|
function buildWorktreePath(mainRoot, runId, index) {
|
|
16495
|
-
return
|
|
16802
|
+
return path7.join(mainRoot, ".atomic", "worktrees", flattenedWorktreeName(runId, index));
|
|
16496
16803
|
}
|
|
16497
16804
|
function ensureWorktreeIgnore(mainRoot) {
|
|
16498
|
-
const worktreesRoot =
|
|
16499
|
-
|
|
16500
|
-
|
|
16805
|
+
const worktreesRoot = path7.join(mainRoot, ".atomic", "worktrees");
|
|
16806
|
+
fs7.mkdirSync(worktreesRoot, { recursive: true });
|
|
16807
|
+
fs7.writeFileSync(path7.join(worktreesRoot, ".gitignore"), `*
|
|
16501
16808
|
`, "utf8");
|
|
16502
16809
|
}
|
|
16503
16810
|
function resolveRepoCwdRelative(cwd) {
|
|
@@ -16506,7 +16813,7 @@ function resolveRepoCwdRelative(cwd) {
|
|
|
16506
16813
|
throw new Error("worktree isolation requires a git repository");
|
|
16507
16814
|
}
|
|
16508
16815
|
const rawPrefix = runGitChecked(cwd, ["rev-parse", "--show-prefix"]).trim();
|
|
16509
|
-
const normalizedPrefix = rawPrefix ?
|
|
16816
|
+
const normalizedPrefix = rawPrefix ? path7.normalize(rawPrefix.replace(/[\\/]+$/, "")) : "";
|
|
16510
16817
|
return normalizedPrefix === "." ? "" : normalizedPrefix;
|
|
16511
16818
|
}
|
|
16512
16819
|
function parseHookTimeout(timeoutMs) {
|
|
@@ -16524,19 +16831,19 @@ function resolveWorktreeSetupHook(repoRoot, config) {
|
|
|
16524
16831
|
if (!hookPath) {
|
|
16525
16832
|
throw new Error("worktree setup hook path cannot be empty");
|
|
16526
16833
|
}
|
|
16527
|
-
const expandedHookPath = hookPath.startsWith("~/") ?
|
|
16834
|
+
const expandedHookPath = hookPath.startsWith("~/") ? path7.join(os.homedir(), hookPath.slice(2)) : hookPath;
|
|
16528
16835
|
let resolvedPath;
|
|
16529
|
-
if (
|
|
16836
|
+
if (path7.isAbsolute(expandedHookPath)) {
|
|
16530
16837
|
resolvedPath = expandedHookPath;
|
|
16531
16838
|
} else if (expandedHookPath.includes("/") || expandedHookPath.includes("\\")) {
|
|
16532
|
-
resolvedPath =
|
|
16839
|
+
resolvedPath = path7.resolve(repoRoot, expandedHookPath);
|
|
16533
16840
|
} else {
|
|
16534
16841
|
throw new Error("worktree setup hook must be an absolute path or a repo-relative path");
|
|
16535
16842
|
}
|
|
16536
|
-
if (!
|
|
16843
|
+
if (!fs7.existsSync(resolvedPath)) {
|
|
16537
16844
|
throw new Error(`worktree setup hook not found: ${resolvedPath}`);
|
|
16538
16845
|
}
|
|
16539
|
-
if (
|
|
16846
|
+
if (fs7.statSync(resolvedPath).isDirectory()) {
|
|
16540
16847
|
throw new Error(`worktree setup hook must be a file, got directory: ${resolvedPath}`);
|
|
16541
16848
|
}
|
|
16542
16849
|
return {
|
|
@@ -16548,17 +16855,17 @@ function normalizeSyntheticPath(worktreePath, rawPath) {
|
|
|
16548
16855
|
const trimmed = rawPath.trim();
|
|
16549
16856
|
if (!trimmed)
|
|
16550
16857
|
throw new Error("synthetic path cannot be empty");
|
|
16551
|
-
if (
|
|
16858
|
+
if (path7.isAbsolute(trimmed))
|
|
16552
16859
|
throw new Error(`synthetic path must be relative: ${rawPath}`);
|
|
16553
|
-
const resolved =
|
|
16554
|
-
const relative =
|
|
16860
|
+
const resolved = path7.resolve(worktreePath, trimmed);
|
|
16861
|
+
const relative = path7.relative(worktreePath, resolved);
|
|
16555
16862
|
if (!relative || relative === ".") {
|
|
16556
16863
|
throw new Error(`synthetic path cannot target the worktree root: ${rawPath}`);
|
|
16557
16864
|
}
|
|
16558
|
-
if (relative === ".." || relative.startsWith(`..${
|
|
16865
|
+
if (relative === ".." || relative.startsWith(`..${path7.sep}`) || path7.isAbsolute(relative)) {
|
|
16559
16866
|
throw new Error(`synthetic path escapes the worktree root: ${rawPath}`);
|
|
16560
16867
|
}
|
|
16561
|
-
return
|
|
16868
|
+
return path7.normalize(relative);
|
|
16562
16869
|
}
|
|
16563
16870
|
function hasTrackedEntries(worktreePath, relativePath) {
|
|
16564
16871
|
const result = runGit(worktreePath, ["ls-files", "--", relativePath]);
|
|
@@ -16630,16 +16937,16 @@ function waitForGitLockRelease() {
|
|
|
16630
16937
|
}
|
|
16631
16938
|
function unlinkWorktreeLinks(worktreePath, relativePaths) {
|
|
16632
16939
|
for (const relativePath of new Set(relativePaths)) {
|
|
16633
|
-
const target =
|
|
16634
|
-
const relative =
|
|
16635
|
-
if (!relative || relative === ".." || relative.startsWith(`..${
|
|
16940
|
+
const target = path7.resolve(worktreePath, relativePath);
|
|
16941
|
+
const relative = path7.relative(worktreePath, target);
|
|
16942
|
+
if (!relative || relative === ".." || relative.startsWith(`..${path7.sep}`) || path7.isAbsolute(relative))
|
|
16636
16943
|
continue;
|
|
16637
16944
|
let current = worktreePath;
|
|
16638
|
-
for (const component of relative.split(
|
|
16639
|
-
current =
|
|
16945
|
+
for (const component of relative.split(path7.sep)) {
|
|
16946
|
+
current = path7.join(current, component);
|
|
16640
16947
|
let stat;
|
|
16641
16948
|
try {
|
|
16642
|
-
stat =
|
|
16949
|
+
stat = fs7.lstatSync(current);
|
|
16643
16950
|
} catch (error) {
|
|
16644
16951
|
const code = error.code;
|
|
16645
16952
|
if (code === "ENOENT" || code === "ENOTDIR")
|
|
@@ -16649,7 +16956,7 @@ function unlinkWorktreeLinks(worktreePath, relativePaths) {
|
|
|
16649
16956
|
if (!stat.isSymbolicLink())
|
|
16650
16957
|
continue;
|
|
16651
16958
|
if (current === target)
|
|
16652
|
-
|
|
16959
|
+
fs7.unlinkSync(current);
|
|
16653
16960
|
break;
|
|
16654
16961
|
}
|
|
16655
16962
|
}
|
|
@@ -16677,7 +16984,7 @@ function createSingleWorktree(mainRoot, cwdRelative, runId, index, baseRef, base
|
|
|
16677
16984
|
cleanupSingleWorktree(mainRoot, { path: worktreePath, branch }, []);
|
|
16678
16985
|
throw new Error(message);
|
|
16679
16986
|
}
|
|
16680
|
-
const agentCwd = cwdRelative ?
|
|
16987
|
+
const agentCwd = cwdRelative ? path7.join(worktreePath, cwdRelative) : worktreePath;
|
|
16681
16988
|
const syntheticPaths = [];
|
|
16682
16989
|
try {
|
|
16683
16990
|
syntheticPaths.push(...performPostCreationSetup(mainRoot, worktreePath, symlinkDirectories));
|
|
@@ -16746,8 +17053,8 @@ var init_worktree = __esm(() => {
|
|
|
16746
17053
|
});
|
|
16747
17054
|
|
|
16748
17055
|
// dist/builtin/workflows/src/runs/shared/worktree-cwd.ts
|
|
16749
|
-
import { lstatSync as
|
|
16750
|
-
import { dirname as
|
|
17056
|
+
import { lstatSync as lstatSync6, realpathSync as realpathSync4 } from "node:fs";
|
|
17057
|
+
import { dirname as dirname10, isAbsolute as isAbsolute6, relative as relative4, resolve as resolve8, sep as sep5 } from "node:path";
|
|
16751
17058
|
function relativePathWithin(root, candidate) {
|
|
16752
17059
|
const path = relative4(root, candidate);
|
|
16753
17060
|
return path === ".." || path.startsWith(`..${sep5}`) || isAbsolute6(path) ? undefined : path;
|
|
@@ -16766,14 +17073,14 @@ function resolveWorktreeStageCwd(cwd, setup) {
|
|
|
16766
17073
|
if (cwd === undefined || cwd.length === 0)
|
|
16767
17074
|
return;
|
|
16768
17075
|
const absoluteInput = isAbsolute6(cwd);
|
|
16769
|
-
const candidate = absoluteInput ? cwd :
|
|
17076
|
+
const candidate = absoluteInput ? cwd : resolve8(setup.cwd, cwd);
|
|
16770
17077
|
if (!absoluteInput && relativePathWithin(setup.worktreeRoot, candidate) === undefined) {
|
|
16771
17078
|
throw new Error(`atomic-workflows: relative cwd ${cwd} escapes gitWorktreeDir ${setup.worktreeRoot}; choose a cwd inside the worktree.`);
|
|
16772
17079
|
}
|
|
16773
17080
|
const candidateRealPath = existingRealPath(candidate, "cwd");
|
|
16774
17081
|
const worktreeRelative = relativePathWithin(worktreeRoot, candidateRealPath);
|
|
16775
17082
|
if (worktreeRelative !== undefined)
|
|
16776
|
-
return
|
|
17083
|
+
return resolve8(setup.worktreeRoot, worktreeRelative);
|
|
16777
17084
|
if (!isAbsolute6(cwd)) {
|
|
16778
17085
|
throw new Error(`atomic-workflows: relative cwd ${cwd} escapes gitWorktreeDir ${setup.worktreeRoot}; choose a path that remains inside the selected worktree.`);
|
|
16779
17086
|
}
|
|
@@ -16782,9 +17089,9 @@ function resolveWorktreeStageCwd(cwd, setup) {
|
|
|
16782
17089
|
}
|
|
16783
17090
|
const repositoryRelative = absoluteInput ? relativePathWithin(repositoryRoot, candidateRealPath) : undefined;
|
|
16784
17091
|
if (repositoryRelative === undefined) {
|
|
16785
|
-
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.`);
|
|
17092
|
+
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.`);
|
|
16786
17093
|
}
|
|
16787
|
-
const mappedCwd =
|
|
17094
|
+
const mappedCwd = resolve8(setup.worktreeRoot, repositoryRelative);
|
|
16788
17095
|
const mappedRealPath = existingRealPath(mappedCwd, "remapped worktree cwd");
|
|
16789
17096
|
if (relativePathWithin(worktreeRoot, mappedRealPath) === undefined) {
|
|
16790
17097
|
throw new Error(`atomic-workflows: cwd ${cwd} remaps through a path that resolves outside gitWorktreeDir ${setup.worktreeRoot}; remove the escaping symlink or omit cwd.`);
|
|
@@ -16795,7 +17102,7 @@ var init_worktree_cwd = () => {};
|
|
|
16795
17102
|
|
|
16796
17103
|
// dist/builtin/workflows/src/runs/foreground/executor-task-prompts.ts
|
|
16797
17104
|
import { existsSync as existsSync4 } from "node:fs";
|
|
16798
|
-
import { isAbsolute as isAbsolute7, resolve as
|
|
17105
|
+
import { isAbsolute as isAbsolute7, resolve as resolve9 } from "node:path";
|
|
16799
17106
|
function normalizeTaskContexts(previous) {
|
|
16800
17107
|
if (previous === undefined)
|
|
16801
17108
|
return [];
|
|
@@ -16848,7 +17155,7 @@ function taskPrevious(options) {
|
|
|
16848
17155
|
function resolveWorkflowPath(filePath, baseDir) {
|
|
16849
17156
|
if (isAbsolute7(filePath))
|
|
16850
17157
|
return filePath;
|
|
16851
|
-
return
|
|
17158
|
+
return resolve9(baseDir ?? process.cwd(), filePath);
|
|
16852
17159
|
}
|
|
16853
17160
|
function taskBaseDir(options) {
|
|
16854
17161
|
if (typeof options.chainDir === "string" && options.chainDir.trim().length > 0) {
|
|
@@ -17003,7 +17310,7 @@ function taskWithSharedDefaults(taskOptions, options) {
|
|
|
17003
17310
|
var init_executor_task_prompts = () => {};
|
|
17004
17311
|
|
|
17005
17312
|
// dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts
|
|
17006
|
-
import { isAbsolute as isAbsolute8, join as
|
|
17313
|
+
import { isAbsolute as isAbsolute8, join as join12, resolve as resolve10 } from "node:path";
|
|
17007
17314
|
import { CONFIG_DIR_NAME } from "@bastani/atomic";
|
|
17008
17315
|
function positiveConcurrency(value2) {
|
|
17009
17316
|
if (typeof value2 !== "number" || !Number.isFinite(value2) || value2 < 1)
|
|
@@ -17156,10 +17463,10 @@ function workflowInvocationMetadata(inputDefaults, workflowInvocationCwd, cache,
|
|
|
17156
17463
|
function resolvedTaskCwd(cwd, workflowInvocationCwd) {
|
|
17157
17464
|
if (cwd === undefined)
|
|
17158
17465
|
return;
|
|
17159
|
-
return isAbsolute8(cwd) ? cwd :
|
|
17466
|
+
return isAbsolute8(cwd) ? cwd : resolve10(workflowInvocationCwd, cwd);
|
|
17160
17467
|
}
|
|
17161
17468
|
function taskWorktreeOutputsRoot(runId) {
|
|
17162
|
-
return
|
|
17469
|
+
return join12(workflowArtifactRunPath(runId), "task-outputs");
|
|
17163
17470
|
}
|
|
17164
17471
|
function prepareTaskWorktrees(tasks, options, runId, scope, workflowInvocationCwd = process.cwd(), symlinkDirectories) {
|
|
17165
17472
|
if (options.worktree !== true && !tasks.some((task) => task.worktree === true)) {
|
|
@@ -17169,7 +17476,7 @@ function prepareTaskWorktrees(tasks, options, runId, scope, workflowInvocationCw
|
|
|
17169
17476
|
throw new Error("atomic-workflows: worktree and gitWorktreeDir are mutually exclusive; use gitWorktreeDir for a reusable worktree or worktree:true for temporary isolated worktrees.");
|
|
17170
17477
|
}
|
|
17171
17478
|
const explicitCwd = tasks.find((task) => typeof task.cwd === "string")?.cwd;
|
|
17172
|
-
const sharedCwd = explicitCwd === undefined ? workflowInvocationCwd : isAbsolute8(explicitCwd) ? explicitCwd :
|
|
17479
|
+
const sharedCwd = explicitCwd === undefined ? workflowInvocationCwd : isAbsolute8(explicitCwd) ? explicitCwd : resolve10(workflowInvocationCwd, explicitCwd);
|
|
17173
17480
|
const conflict = findWorktreeTaskCwdConflict(tasks.map((task) => ({ agent: task.name, cwd: resolvedTaskCwd(task.cwd, workflowInvocationCwd) })), sharedCwd);
|
|
17174
17481
|
if (conflict !== undefined)
|
|
17175
17482
|
throw new Error(formatWorktreeTaskCwdConflict(conflict, sharedCwd));
|
|
@@ -17184,9 +17491,9 @@ function prepareTaskWorktrees(tasks, options, runId, scope, workflowInvocationCw
|
|
|
17184
17491
|
tasks: tasks.map((task, index) => ({ ...task, cwd: setup.worktrees[index].agentCwd })),
|
|
17185
17492
|
setup,
|
|
17186
17493
|
agents,
|
|
17187
|
-
diffsDir:
|
|
17494
|
+
diffsDir: join12(setup.cwd, CONFIG_DIR_NAME, "workflows", "worktree-diffs", runId, scope),
|
|
17188
17495
|
outputIsolations: tasks.map((_, index) => ({
|
|
17189
|
-
baseDir:
|
|
17496
|
+
baseDir: join12(trustedRoot, runId, scope, String(index)),
|
|
17190
17497
|
trustedRoot
|
|
17191
17498
|
}))
|
|
17192
17499
|
};
|
|
@@ -18628,13 +18935,13 @@ function raceAbort2(operation, signal) {
|
|
|
18628
18935
|
}
|
|
18629
18936
|
|
|
18630
18937
|
// dist/builtin/workflows/src/shared/possible-stages.ts
|
|
18631
|
-
import { existsSync as existsSync5, readFileSync as
|
|
18632
|
-
import { dirname as
|
|
18938
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
|
|
18939
|
+
import { dirname as dirname11, join as join13, resolve as resolve11 } from "node:path";
|
|
18633
18940
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
18634
18941
|
function scanPossibleStagesFromSource(entrySourcePath, options = {}) {
|
|
18635
18942
|
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
18636
18943
|
const scanner = new PossibleStagesScanner(maxDepth);
|
|
18637
|
-
scanner.scanSubtree(
|
|
18944
|
+
scanner.scanSubtree(resolve11(entrySourcePath), "", 0, [resolve11(entrySourcePath)]);
|
|
18638
18945
|
return {
|
|
18639
18946
|
hasTrackedNodes: scanner.hasTrackedNodes,
|
|
18640
18947
|
stages: [...scanner.stages].sort(),
|
|
@@ -18645,7 +18952,7 @@ function resolveBuiltinDefinitionSource(name) {
|
|
|
18645
18952
|
const builtinDir = fileURLToPath3(new URL("../../builtin/", import.meta.url));
|
|
18646
18953
|
for (const stem of [name, kebabCase(name)]) {
|
|
18647
18954
|
for (const extension of [".ts", ".js"]) {
|
|
18648
|
-
const candidate =
|
|
18955
|
+
const candidate = join13(builtinDir, `${stem}${extension}`);
|
|
18649
18956
|
if (isFileSync(candidate))
|
|
18650
18957
|
return candidate;
|
|
18651
18958
|
}
|
|
@@ -18884,7 +19191,7 @@ function templateToPattern(raw) {
|
|
|
18884
19191
|
function buildFileUnit(path, warnings) {
|
|
18885
19192
|
let source;
|
|
18886
19193
|
try {
|
|
18887
|
-
source =
|
|
19194
|
+
source = readFileSync4(path, "utf-8");
|
|
18888
19195
|
} catch (error) {
|
|
18889
19196
|
warnings.push(`possible-stages: failed to read ${path}: ${errorMessage2(error)}`);
|
|
18890
19197
|
return;
|
|
@@ -20088,16 +20395,16 @@ function resolveRelativeSpecifier(specifier, importingFile) {
|
|
|
20088
20395
|
return;
|
|
20089
20396
|
const candidates = [];
|
|
20090
20397
|
if (specifier.endsWith(".js")) {
|
|
20091
|
-
candidates.push(
|
|
20398
|
+
candidates.push(join13(dirname11(importingFile), `${specifier.slice(0, -3)}.ts`));
|
|
20092
20399
|
}
|
|
20093
|
-
candidates.push(
|
|
20400
|
+
candidates.push(join13(dirname11(importingFile), specifier), join13(dirname11(importingFile), `${specifier}.ts`), join13(dirname11(importingFile), `${specifier}.js`), join13(dirname11(importingFile), specifier, "index.ts"), join13(dirname11(importingFile), specifier, "index.js"));
|
|
20094
20401
|
return candidates.find((candidate) => isFileSync(candidate));
|
|
20095
20402
|
}
|
|
20096
20403
|
function isFileSync(path) {
|
|
20097
20404
|
if (!existsSync5(path))
|
|
20098
20405
|
return false;
|
|
20099
20406
|
try {
|
|
20100
|
-
|
|
20407
|
+
readFileSync4(path, { flag: "r" });
|
|
20101
20408
|
return true;
|
|
20102
20409
|
} catch {
|
|
20103
20410
|
return false;
|
|
@@ -20125,7 +20432,7 @@ var init_possible_stages = __esm(() => {
|
|
|
20125
20432
|
|
|
20126
20433
|
// dist/builtin/workflows/src/workers/admission-host.ts
|
|
20127
20434
|
import { execFile, execFileSync } from "node:child_process";
|
|
20128
|
-
import { readdirSync as
|
|
20435
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync5 } from "node:fs";
|
|
20129
20436
|
import { readFile } from "node:fs/promises";
|
|
20130
20437
|
import { hostname } from "node:os";
|
|
20131
20438
|
function admissionBootIdentity() {
|
|
@@ -20151,13 +20458,32 @@ function admissionProcessStopped(pid) {
|
|
|
20151
20458
|
return error.code === "ESRCH";
|
|
20152
20459
|
}
|
|
20153
20460
|
}
|
|
20461
|
+
function workerProcessExited(pid, final = false) {
|
|
20462
|
+
if (admissionProcessStopped(pid))
|
|
20463
|
+
return true;
|
|
20464
|
+
if (process.platform === "linux") {
|
|
20465
|
+
try {
|
|
20466
|
+
const stat = readFileSync5(`/proc/${pid}/stat`, "utf8");
|
|
20467
|
+
return stat.slice(stat.lastIndexOf(")") + 2).startsWith("Z");
|
|
20468
|
+
} catch {
|
|
20469
|
+
return admissionProcessStopped(pid);
|
|
20470
|
+
}
|
|
20471
|
+
}
|
|
20472
|
+
if (!final)
|
|
20473
|
+
return false;
|
|
20474
|
+
try {
|
|
20475
|
+
return execFileSync("ps", ["-o", "stat=", "-p", String(pid)], { encoding: "utf8", timeout: 5000 }).trim().startsWith("Z");
|
|
20476
|
+
} catch {
|
|
20477
|
+
return admissionProcessStopped(pid);
|
|
20478
|
+
}
|
|
20479
|
+
}
|
|
20154
20480
|
function scanProcessGroup(group) {
|
|
20155
20481
|
const members = new Map;
|
|
20156
20482
|
if (process.platform === "linux") {
|
|
20157
20483
|
let complete = true;
|
|
20158
20484
|
let entries;
|
|
20159
20485
|
try {
|
|
20160
|
-
entries =
|
|
20486
|
+
entries = readdirSync4("/proc");
|
|
20161
20487
|
} catch {
|
|
20162
20488
|
return { members, complete: false };
|
|
20163
20489
|
}
|
|
@@ -20166,7 +20492,7 @@ function scanProcessGroup(group) {
|
|
|
20166
20492
|
continue;
|
|
20167
20493
|
let stat;
|
|
20168
20494
|
try {
|
|
20169
|
-
stat =
|
|
20495
|
+
stat = readFileSync5(`/proc/${entry}/stat`, "utf8");
|
|
20170
20496
|
} catch {
|
|
20171
20497
|
complete = false;
|
|
20172
20498
|
continue;
|
|
@@ -56905,10 +57231,10 @@ var init_verification_prompts = __esm(() => {
|
|
|
56905
57231
|
// dist/builtin/workflows/builtin/pattern-artifact-root.ts
|
|
56906
57232
|
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
56907
57233
|
import { mkdir as mkdir2 } from "node:fs/promises";
|
|
56908
|
-
import { join as
|
|
57234
|
+
import { join as join14 } from "node:path";
|
|
56909
57235
|
async function stableArtifactRoot(ctx, workflowName) {
|
|
56910
57236
|
const cwd = ctx.cwd ?? process.cwd();
|
|
56911
|
-
const artifactDir = await ctx.tool("artifact-root", { workflow: workflowName }, async () =>
|
|
57237
|
+
const artifactDir = await ctx.tool("artifact-root", { workflow: workflowName }, async () => join14(cwd, ".atomic", "workflows", "runs", `${workflowName}-${randomUUID9()}`));
|
|
56912
57238
|
await mkdir2(artifactDir, { recursive: true });
|
|
56913
57239
|
return artifactDir;
|
|
56914
57240
|
}
|
|
@@ -56948,7 +57274,7 @@ function fold_usage(results) {
|
|
|
56948
57274
|
|
|
56949
57275
|
// dist/builtin/workflows/builtin/adversarial-verification-runner.ts
|
|
56950
57276
|
import { readFile as readFile2, writeFile } from "node:fs/promises";
|
|
56951
|
-
import { join as
|
|
57277
|
+
import { join as join15 } from "node:path";
|
|
56952
57278
|
import { Type as Type4 } from "typebox";
|
|
56953
57279
|
function isRecord3(value2) {
|
|
56954
57280
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
@@ -57043,8 +57369,8 @@ async function writeInvalid(path, stage) {
|
|
|
57043
57369
|
}
|
|
57044
57370
|
async function runAdversarialVerification(ctx) {
|
|
57045
57371
|
const root = await stableArtifactRoot(ctx, "adversarial-verification");
|
|
57046
|
-
const criteriaPath =
|
|
57047
|
-
const candidatePath =
|
|
57372
|
+
const criteriaPath = join15(root, "criteria.md");
|
|
57373
|
+
const candidatePath = join15(root, "candidate.md");
|
|
57048
57374
|
const resolvedCriteria = resolveCriteria(ctx.inputs.criteria);
|
|
57049
57375
|
await writeFile(criteriaPath, renderCriteriaMarkdown(resolvedCriteria));
|
|
57050
57376
|
await ctx.task("worker", { prompt: renderWorkerPrompt(ctx.inputs.task), context: "fresh", output: candidatePath, outputMode: "file-only" });
|
|
@@ -57076,7 +57402,7 @@ async function runAdversarialVerification(ctx) {
|
|
|
57076
57402
|
criterion,
|
|
57077
57403
|
verifierIndex: index + 1,
|
|
57078
57404
|
name,
|
|
57079
|
-
artifactPath:
|
|
57405
|
+
artifactPath: join15(root, `verification-${round}-${criterion.id}-${index + 1}.json`)
|
|
57080
57406
|
});
|
|
57081
57407
|
}
|
|
57082
57408
|
}
|
|
@@ -57129,8 +57455,8 @@ async function runAdversarialVerification(ctx) {
|
|
|
57129
57455
|
const decision = decide_verification(roundResult, { acceptMean, quorumFraction: QUORUM_FRACTION });
|
|
57130
57456
|
finalDecision = decision;
|
|
57131
57457
|
finalMean = meanScore(scoreReports);
|
|
57132
|
-
scoreTablePath =
|
|
57133
|
-
reviewReportPath =
|
|
57458
|
+
scoreTablePath = join15(root, `verification-summary-${round}.json`);
|
|
57459
|
+
reviewReportPath = join15(root, `review-${round}.json`);
|
|
57134
57460
|
const summary = {
|
|
57135
57461
|
scores: scoreReports,
|
|
57136
57462
|
mean: finalMean,
|
|
@@ -57311,7 +57637,7 @@ var GROUNDED_REPORTING2 = "Before reporting progress, audit each claim against a
|
|
|
57311
57637
|
|
|
57312
57638
|
// dist/builtin/workflows/builtin/classify-and-act-runner.ts
|
|
57313
57639
|
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
57314
|
-
import { join as
|
|
57640
|
+
import { join as join16 } from "node:path";
|
|
57315
57641
|
import { Type as Type6 } from "typebox";
|
|
57316
57642
|
function safeName(value2) {
|
|
57317
57643
|
const normalized = value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -57351,7 +57677,7 @@ async function runClassifyAndAct(ctx) {
|
|
|
57351
57677
|
const needsFallback = exactCategory === undefined || confidence < ctx.inputs.confidence_threshold;
|
|
57352
57678
|
const fallback = needsFallback ? await resolveFallbackCategory(ctx, exactCategory) : undefined;
|
|
57353
57679
|
const category = fallback?.category ?? exactCategory;
|
|
57354
|
-
const classificationPath =
|
|
57680
|
+
const classificationPath = join16(artifactDir, "classification.json");
|
|
57355
57681
|
await writeFile2(classificationPath, JSON.stringify({
|
|
57356
57682
|
proposed_category: proposedCategory,
|
|
57357
57683
|
selected_category: category,
|
|
@@ -57361,7 +57687,7 @@ async function runClassifyAndAct(ctx) {
|
|
|
57361
57687
|
fallback_used: needsFallback,
|
|
57362
57688
|
fallback_mode: fallback?.fallbackMode ?? "none"
|
|
57363
57689
|
}, null, 2));
|
|
57364
|
-
const actionPath =
|
|
57690
|
+
const actionPath = join16(artifactDir, `action-${safeName(category)}.md`);
|
|
57365
57691
|
const action = await ctx.task(`action-${safeName(category)}`, {
|
|
57366
57692
|
tools: actionTools(category),
|
|
57367
57693
|
prompt: actionPrompt({ prompt: ctx.inputs.prompt, category, classificationPath }),
|
|
@@ -57501,7 +57827,7 @@ var GROUNDED_REPORTING3 = "Before reporting progress, audit each claim against a
|
|
|
57501
57827
|
|
|
57502
57828
|
// dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts
|
|
57503
57829
|
import { writeFile as writeFile3 } from "node:fs/promises";
|
|
57504
|
-
import { join as
|
|
57830
|
+
import { join as join17 } from "node:path";
|
|
57505
57831
|
import { Type as Type8 } from "typebox";
|
|
57506
57832
|
function safeName2(value2, index) {
|
|
57507
57833
|
const normalized = value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -57532,9 +57858,9 @@ async function runFanOutAndSynthesize(ctx) {
|
|
|
57532
57858
|
context: "fresh"
|
|
57533
57859
|
});
|
|
57534
57860
|
const partitions = parsedPartitions(plan.structured, ctx.inputs.max_branches, ctx.inputs.prompt);
|
|
57535
|
-
const partitionPath =
|
|
57861
|
+
const partitionPath = join17(artifactDir, "partition-plan.json");
|
|
57536
57862
|
await writeFile3(partitionPath, JSON.stringify({ task: ctx.inputs.prompt, partitions }, null, 2));
|
|
57537
|
-
const branchPaths = partitions.map((partition, index) =>
|
|
57863
|
+
const branchPaths = partitions.map((partition, index) => join17(artifactDir, `branch-${safeName2(partition.label, index)}.md`));
|
|
57538
57864
|
const steps = partitions.map((partition, index) => ({
|
|
57539
57865
|
name: `branch-${safeName2(partition.label, index)}`,
|
|
57540
57866
|
prompt: branchPrompt({ prompt: ctx.inputs.prompt, ...partition }),
|
|
@@ -57544,13 +57870,13 @@ async function runFanOutAndSynthesize(ctx) {
|
|
|
57544
57870
|
outputMode: "file-only"
|
|
57545
57871
|
}));
|
|
57546
57872
|
await ctx.parallel(steps, { concurrency: Math.min(ctx.inputs.max_concurrency, partitions.length), failFast: false });
|
|
57547
|
-
const manifestPath =
|
|
57873
|
+
const manifestPath = join17(artifactDir, "manifest.json");
|
|
57548
57874
|
await writeFile3(manifestPath, JSON.stringify({
|
|
57549
57875
|
task: ctx.inputs.prompt,
|
|
57550
57876
|
partition_plan: partitionPath,
|
|
57551
57877
|
branches: partitions.map((partition, index) => ({ ...partition, artifact_path: branchPaths[index] }))
|
|
57552
57878
|
}, null, 2));
|
|
57553
|
-
const synthesisPath =
|
|
57879
|
+
const synthesisPath = join17(artifactDir, "synthesis.md");
|
|
57554
57880
|
const synthesis = await ctx.task("synthesize", {
|
|
57555
57881
|
prompt: synthesisPrompt(ctx.inputs.prompt, manifestPath),
|
|
57556
57882
|
context: "fresh",
|
|
@@ -57715,7 +58041,7 @@ var init_generate_and_filter_prompts = __esm(() => {
|
|
|
57715
58041
|
|
|
57716
58042
|
// dist/builtin/workflows/builtin/generate-and-filter-runner.ts
|
|
57717
58043
|
import { readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
|
|
57718
|
-
import { join as
|
|
58044
|
+
import { join as join18 } from "node:path";
|
|
57719
58045
|
import { Type as Type10 } from "typebox";
|
|
57720
58046
|
function isRecord4(value2) {
|
|
57721
58047
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
@@ -57734,7 +58060,7 @@ function judgeDecision(value2) {
|
|
|
57734
58060
|
}
|
|
57735
58061
|
async function runGenerateAndFilter(ctx) {
|
|
57736
58062
|
const root = await stableArtifactRoot(ctx, "generate-and-filter");
|
|
57737
|
-
const candidatePaths = Array.from({ length: ctx.inputs.num_candidates }, (_, index) =>
|
|
58063
|
+
const candidatePaths = Array.from({ length: ctx.inputs.num_candidates }, (_, index) => join18(root, `candidate-${index + 1}.md`));
|
|
57738
58064
|
const shortlistLimit = Math.min(ctx.inputs.shortlist_size, ctx.inputs.num_candidates);
|
|
57739
58065
|
await ctx.parallel(candidatePaths.map((path, index) => ({
|
|
57740
58066
|
name: `generate-${index + 1}`,
|
|
@@ -57743,9 +58069,9 @@ async function runGenerateAndFilter(ctx) {
|
|
|
57743
58069
|
output: path,
|
|
57744
58070
|
outputMode: "file-only"
|
|
57745
58071
|
})), { concurrency: ctx.inputs.max_concurrency, failFast: false });
|
|
57746
|
-
const manifestPath =
|
|
58072
|
+
const manifestPath = join18(root, "manifest.json");
|
|
57747
58073
|
await writeFile4(manifestPath, JSON.stringify({ task: ctx.inputs.prompt, candidate_artifact_paths: candidatePaths }, null, 2));
|
|
57748
|
-
const filterPath =
|
|
58074
|
+
const filterPath = join18(root, "filter.json");
|
|
57749
58075
|
const filtered = await ctx.task("dedupe-and-filter", {
|
|
57750
58076
|
prompt: renderFilterPrompt(ctx.inputs.prompt, candidatePaths, shortlistLimit),
|
|
57751
58077
|
context: "fresh",
|
|
@@ -57762,7 +58088,7 @@ async function runGenerateAndFilter(ctx) {
|
|
|
57762
58088
|
let judgePath = null;
|
|
57763
58089
|
let decisionPath = filterPath;
|
|
57764
58090
|
if (ctx.inputs.use_judge) {
|
|
57765
|
-
judgePath =
|
|
58091
|
+
judgePath = join18(root, "judge.json");
|
|
57766
58092
|
const judgeCandidates = await Promise.all(shortlist.map(async (path) => ({ path, body: await readFile3(path, "utf8") })));
|
|
57767
58093
|
const judgePrompt = renderJudgePrompt(ctx.inputs.prompt, filterPath, shortlistLimit, judgeCandidates);
|
|
57768
58094
|
const judged = await ctx.task("judge", {
|
|
@@ -57778,7 +58104,7 @@ async function runGenerateAndFilter(ctx) {
|
|
|
57778
58104
|
shortlist = judgedShortlist.length > 0 ? judgedShortlist : shortlist;
|
|
57779
58105
|
decisionPath = judgePath;
|
|
57780
58106
|
}
|
|
57781
|
-
const finalPath =
|
|
58107
|
+
const finalPath = join18(root, "shortlist.md");
|
|
57782
58108
|
const finalShortlist = await ctx.task("final-shortlist", {
|
|
57783
58109
|
prompt: renderFinalShortlistPrompt(ctx.inputs.prompt, decisionPath),
|
|
57784
58110
|
context: "fresh",
|
|
@@ -58307,7 +58633,7 @@ var init_review_convergence = __esm(() => {
|
|
|
58307
58633
|
|
|
58308
58634
|
// dist/builtin/workflows/builtin/goal-artifacts.ts
|
|
58309
58635
|
import { writeFile as writeFile5 } from "node:fs/promises";
|
|
58310
|
-
import { join as
|
|
58636
|
+
import { join as join19 } from "node:path";
|
|
58311
58637
|
function artifactSafeName(value2) {
|
|
58312
58638
|
const safe = value2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
58313
58639
|
return safe.length > 0 ? safe : "artifact";
|
|
@@ -58318,7 +58644,7 @@ function withoutTurn(value2) {
|
|
|
58318
58644
|
return copy;
|
|
58319
58645
|
}
|
|
58320
58646
|
async function writeReviewArtifact(artifactDir, reviewer, decision, rawText, convergenceDecision) {
|
|
58321
|
-
const artifactPath =
|
|
58647
|
+
const artifactPath = join19(artifactDir, `review-${artifactSafeName(reviewer)}.json`);
|
|
58322
58648
|
await writeFile5(artifactPath, `${JSON.stringify({ reviewer, decision, convergence_decision: convergenceDecision, raw_text: rawText }, null, 2)}
|
|
58323
58649
|
`, { encoding: "utf8" });
|
|
58324
58650
|
return artifactPath;
|
|
@@ -58327,7 +58653,7 @@ async function writeReviewRoundArtifact(artifactDir, reviews, consolidatedFindin
|
|
|
58327
58653
|
reviewer: review.reviewer,
|
|
58328
58654
|
findings: review.findings
|
|
58329
58655
|
}))), reverification = []) {
|
|
58330
|
-
const artifactPath =
|
|
58656
|
+
const artifactPath = join19(artifactDir, "review-round-latest.json");
|
|
58331
58657
|
const visibleReviews = reviews.map(withoutTurn);
|
|
58332
58658
|
await writeFile5(artifactPath, `${JSON.stringify({ reviews: visibleReviews, consolidated_findings: consolidatedFindings, reverification }, null, 2)}
|
|
58333
58659
|
`, { encoding: "utf8" });
|
|
@@ -58557,7 +58883,7 @@ var init_goal_review = __esm(() => {
|
|
|
58557
58883
|
// dist/builtin/workflows/builtin/goal-ledger.ts
|
|
58558
58884
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
58559
58885
|
import { readFile as readFile4, rename, rm as rm2, writeFile as writeFile6 } from "node:fs/promises";
|
|
58560
|
-
import { dirname as
|
|
58886
|
+
import { dirname as dirname12, join as join20 } from "node:path";
|
|
58561
58887
|
import { isDeepStrictEqual } from "node:util";
|
|
58562
58888
|
function withoutTurn2(value2) {
|
|
58563
58889
|
const copy = { ...value2 };
|
|
@@ -58582,7 +58908,7 @@ function modelVisibleLedger(ledger) {
|
|
|
58582
58908
|
};
|
|
58583
58909
|
}
|
|
58584
58910
|
function goalLedgerStatePath(ledgerPath) {
|
|
58585
|
-
return
|
|
58911
|
+
return join20(dirname12(ledgerPath), LEDGER_STATE_FILENAME);
|
|
58586
58912
|
}
|
|
58587
58913
|
function appendLifecycleEvent(ledger, event, summary, turn = ledger.turns) {
|
|
58588
58914
|
ledger.lifecycle.push({
|
|
@@ -58658,7 +58984,7 @@ function recoverLegacyExecutionInterruption(ledger) {
|
|
|
58658
58984
|
return true;
|
|
58659
58985
|
}
|
|
58660
58986
|
async function createGoalLedger(objective, acceptanceCriteria, artifactDir) {
|
|
58661
|
-
const ledgerPath =
|
|
58987
|
+
const ledgerPath = join20(artifactDir, LEDGER_FILENAME);
|
|
58662
58988
|
const existing = await readExistingGoalLedger(ledgerPath);
|
|
58663
58989
|
if (existing !== undefined) {
|
|
58664
58990
|
if (recoverLegacyExecutionInterruption(existing))
|
|
@@ -59165,7 +59491,7 @@ var init_goal_orchestrator_prompts = __esm(() => {
|
|
|
59165
59491
|
});
|
|
59166
59492
|
|
|
59167
59493
|
// dist/builtin/workflows/builtin/goal-runner.ts
|
|
59168
|
-
import { join as
|
|
59494
|
+
import { join as join21 } from "node:path";
|
|
59169
59495
|
function positiveInteger(value2, fallback) {
|
|
59170
59496
|
if (typeof value2 !== "number" || !Number.isFinite(value2) || value2 <= 0) {
|
|
59171
59497
|
return fallback;
|
|
@@ -59216,7 +59542,7 @@ async function runGoalWorkflow(ctx, options) {
|
|
|
59216
59542
|
appendLifecycleEvent(ledger, "work_turn_started", "Orchestrator started.", turn);
|
|
59217
59543
|
await writeGoalLedger(ledgerPath, ledger);
|
|
59218
59544
|
}
|
|
59219
|
-
const orchestratorReceiptPath =
|
|
59545
|
+
const orchestratorReceiptPath = join21(artifactDir, "orchestrator-receipt.md");
|
|
59220
59546
|
const orchestratorForkOptions = forkContinuationOptions(previousOrchestratorSessionId);
|
|
59221
59547
|
const orchestratorPrompt = orchestratorForkOptions.forkFromSessionId === undefined ? renderGoalOrchestratorPrompt({
|
|
59222
59548
|
ledger,
|
|
@@ -59287,7 +59613,7 @@ async function runGoalWorkflow(ctx, options) {
|
|
|
59287
59613
|
const reviewerName = result.name ?? result.stageName;
|
|
59288
59614
|
const normalizedReviewerName = reviewerName.replace(/-\d+$/u, "");
|
|
59289
59615
|
const parsed = reviewerContractError === undefined ? parsedReviewDecisionFromResult(result, reviewerName) : { decision: reviewerErrorDecision(reviewerContractError.message), parsed: false, diagnostics: [reviewerContractError.message] };
|
|
59290
|
-
const reviewArtifactPath =
|
|
59616
|
+
const reviewArtifactPath = join21(artifactDir, `review-${artifactSafeName(normalizedReviewerName)}.json`);
|
|
59291
59617
|
const record = reviewDecisionToRecord({
|
|
59292
59618
|
turn,
|
|
59293
59619
|
reviewer: normalizedReviewerName,
|
|
@@ -59490,7 +59816,7 @@ var init_goal = __esm(() => {
|
|
|
59490
59816
|
}),
|
|
59491
59817
|
git_worktree_dir: Type15.String({
|
|
59492
59818
|
default: "",
|
|
59493
|
-
description: "Optional Git worktree path. Leave at the default unless the user explicitly requested worktree isolation — stages never create git worktrees on their own.
|
|
59819
|
+
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."
|
|
59494
59820
|
}),
|
|
59495
59821
|
create_pr: Type15.Boolean({
|
|
59496
59822
|
default: false,
|
|
@@ -59608,7 +59934,7 @@ var GROUNDED_REPORTING5 = "Before reporting progress, audit each claim against a
|
|
|
59608
59934
|
|
|
59609
59935
|
// dist/builtin/workflows/builtin/loop-until-done-runner.ts
|
|
59610
59936
|
import { mkdir as mkdir3, writeFile as writeFile7 } from "node:fs/promises";
|
|
59611
|
-
import { join as
|
|
59937
|
+
import { join as join22 } from "node:path";
|
|
59612
59938
|
import { Type as Type16 } from "typebox";
|
|
59613
59939
|
function progressCurve(entries) {
|
|
59614
59940
|
return entries.flatMap((entry) => entry.progress === undefined ? [] : [entry.progress.score]);
|
|
@@ -59691,18 +60017,18 @@ async function writeLedger(path, task, maxIterations, status, entries) {
|
|
|
59691
60017
|
}
|
|
59692
60018
|
async function runLoopUntilDone(ctx) {
|
|
59693
60019
|
const artifactDir = await stableArtifactRoot(ctx, "loop-until-done");
|
|
59694
|
-
const iterationsDir =
|
|
59695
|
-
const evaluationsDir =
|
|
60020
|
+
const iterationsDir = join22(artifactDir, "iterations");
|
|
60021
|
+
const evaluationsDir = join22(artifactDir, "evaluations");
|
|
59696
60022
|
await mkdir3(iterationsDir, { recursive: true });
|
|
59697
60023
|
await mkdir3(evaluationsDir, { recursive: true });
|
|
59698
|
-
const ledgerPath =
|
|
60024
|
+
const ledgerPath = join22(artifactDir, "progress-ledger.json");
|
|
59699
60025
|
const entries = [];
|
|
59700
60026
|
const iterationArtifactPaths = [];
|
|
59701
60027
|
const evaluationArtifactPaths = [];
|
|
59702
60028
|
await writeLedger(ledgerPath, ctx.inputs.prompt, ctx.inputs.max_iterations, "active", entries);
|
|
59703
60029
|
for (let iteration = 1;iteration <= ctx.inputs.max_iterations; iteration += 1) {
|
|
59704
|
-
const iterationPath =
|
|
59705
|
-
const evaluationPath =
|
|
60030
|
+
const iterationPath = join22(iterationsDir, `iteration-${iteration}.md`);
|
|
60031
|
+
const evaluationPath = join22(evaluationsDir, `evaluation-${iteration}.json`);
|
|
59706
60032
|
iterationArtifactPaths.push(iterationPath);
|
|
59707
60033
|
evaluationArtifactPaths.push(evaluationPath);
|
|
59708
60034
|
await ctx.task(`iteration-${iteration}`, {
|
|
@@ -59751,7 +60077,7 @@ async function runLoopUntilDone(ctx) {
|
|
|
59751
60077
|
await writeLedger(ledgerPath, ctx.inputs.prompt, ctx.inputs.max_iterations, decision.done ? "complete" : "active", entries);
|
|
59752
60078
|
if (decision.done) {
|
|
59753
60079
|
const report = progressReport(entries);
|
|
59754
|
-
const resultPath =
|
|
60080
|
+
const resultPath = join22(artifactDir, "result.md");
|
|
59755
60081
|
const final = await ctx.task("completion-summary", {
|
|
59756
60082
|
prompt: renderCompletionPrompt({ task: ctx.inputs.prompt, ledgerPath, iterationPath }),
|
|
59757
60083
|
context: "fresh",
|
|
@@ -59868,9 +60194,9 @@ var init_loop_until_done = __esm(() => {
|
|
|
59868
60194
|
|
|
59869
60195
|
// dist/builtin/workflows/builtin/open-claude-design-utils.ts
|
|
59870
60196
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
59871
|
-
import { mkdirSync as
|
|
60197
|
+
import { mkdirSync as mkdirSync6 } from "node:fs";
|
|
59872
60198
|
import { tmpdir, userInfo } from "node:os";
|
|
59873
|
-
import { join as
|
|
60199
|
+
import { join as join23 } from "node:path";
|
|
59874
60200
|
import { Type as Type18 } from "typebox";
|
|
59875
60201
|
import { createChildProcessEnvironment as createChildProcessEnvironment3 } from "@bastani/atomic";
|
|
59876
60202
|
function taggedPrompt3(sections) {
|
|
@@ -59913,32 +60239,32 @@ function tmpArtifactBase() {
|
|
|
59913
60239
|
try {
|
|
59914
60240
|
user = userInfo().username.replace(/[^A-Za-z0-9._-]/g, "_") || "default";
|
|
59915
60241
|
} catch {}
|
|
59916
|
-
return
|
|
60242
|
+
return join23(tmpdir(), `open-claude-design-${user}`);
|
|
59917
60243
|
}
|
|
59918
60244
|
function prepareArtifactDir(cwd = process.cwd()) {
|
|
59919
60245
|
const runId = `${new Date().toISOString().replace(/[:.]/g, "-")}-${Math.random().toString(36).slice(2, 8)}`;
|
|
59920
60246
|
const tmpCandidates = [
|
|
59921
|
-
|
|
59922
|
-
|
|
60247
|
+
join23(tmpArtifactBase(), runId),
|
|
60248
|
+
join23(tmpdir(), "open-claude-design", runId)
|
|
59923
60249
|
];
|
|
59924
|
-
const candidates = process.env.NODE_ENV === "test" ? tmpCandidates : [
|
|
60250
|
+
const candidates = process.env.NODE_ENV === "test" ? tmpCandidates : [join23(cwd, "specs", "design", runId), ...tmpCandidates];
|
|
59925
60251
|
for (const candidate of candidates) {
|
|
59926
60252
|
try {
|
|
59927
|
-
|
|
60253
|
+
mkdirSync6(candidate, { recursive: true });
|
|
59928
60254
|
return {
|
|
59929
60255
|
runId,
|
|
59930
60256
|
artifactDir: candidate,
|
|
59931
|
-
previewPath:
|
|
59932
|
-
specPath:
|
|
60257
|
+
previewPath: join23(candidate, "preview.html"),
|
|
60258
|
+
specPath: join23(candidate, "spec.html")
|
|
59933
60259
|
};
|
|
59934
60260
|
} catch {}
|
|
59935
60261
|
}
|
|
59936
|
-
const fallback =
|
|
60262
|
+
const fallback = join23(tmpArtifactBase(), runId);
|
|
59937
60263
|
return {
|
|
59938
60264
|
runId,
|
|
59939
60265
|
artifactDir: fallback,
|
|
59940
|
-
previewPath:
|
|
59941
|
-
specPath:
|
|
60266
|
+
previewPath: join23(fallback, "preview.html"),
|
|
60267
|
+
specPath: join23(fallback, "spec.html")
|
|
59942
60268
|
};
|
|
59943
60269
|
}
|
|
59944
60270
|
function ensureAgentBrowser() {
|
|
@@ -60272,8 +60598,8 @@ var init_open_claude_design_live_protocol = __esm(() => {
|
|
|
60272
60598
|
});
|
|
60273
60599
|
|
|
60274
60600
|
// dist/builtin/workflows/builtin/open-claude-design-setup.ts
|
|
60275
|
-
import { mkdirSync as
|
|
60276
|
-
import { join as
|
|
60601
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
60602
|
+
import { join as join24 } from "node:path";
|
|
60277
60603
|
function renderDiscoveryContext(discovery) {
|
|
60278
60604
|
return [
|
|
60279
60605
|
`Confirmed design brief: ${discovery.brief}`,
|
|
@@ -60338,10 +60664,10 @@ async function runDiscoveryAndInit(args) {
|
|
|
60338
60664
|
};
|
|
60339
60665
|
}
|
|
60340
60666
|
function designContextPath(artifactDir) {
|
|
60341
|
-
return
|
|
60667
|
+
return join24(artifactDir, DESIGN_CONTEXT_FILENAME);
|
|
60342
60668
|
}
|
|
60343
60669
|
function referencesBriefPath(artifactDir) {
|
|
60344
|
-
return
|
|
60670
|
+
return join24(artifactDir, REFERENCES_BRIEF_FILENAME);
|
|
60345
60671
|
}
|
|
60346
60672
|
function buildReferenceDiscoveryPrompt(args) {
|
|
60347
60673
|
const siteList = REFERENCE_DESIGN_SITES.map((site, index) => `${index + 1}. ${site.name} — ${site.url}`).join(`
|
|
@@ -60367,8 +60693,8 @@ function buildReferenceDiscoveryPrompt(args) {
|
|
|
60367
60693
|
[
|
|
60368
60694
|
"Use the agent-browser skill to open each gallery; if `agent-browser` reports a missing browser executable, follow the bootstrap rules and retry once.",
|
|
60369
60695
|
"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.",
|
|
60370
|
-
`Capture motion across the entire page: start \`agent-browser record start ${
|
|
60371
|
-
`Also run \`agent-browser screenshot ${
|
|
60696
|
+
`Capture motion across the entire page: start \`agent-browser record start ${join24(args.artifactDir, "ref-<site>-<n>.webm")}\`, scroll smoothly in small increments with waits (using repeated \`agent-browser scroll down 600\` or \`agent-browser mouse wheel 600\`) so animations fire and lazy content loads, then run \`agent-browser record stop\`.`,
|
|
60697
|
+
`Also run \`agent-browser screenshot ${join24(args.artifactDir, "ref-<site>-<n>.png")} --full\`; this still is the minimum when video is unavailable.`,
|
|
60372
60698
|
"Record the actual destination URL, title, and author. For each reference, cite observed layout, typography, color, spacing, and motion traits rather than inferred traits.",
|
|
60373
60699
|
"Assess fit against DESIGN.md, PRODUCT.md, and the ds-* discovery evidence in the design-context file; prefer on-brand references and flag departures.",
|
|
60374
60700
|
"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.",
|
|
@@ -60393,8 +60719,8 @@ function buildReferenceDiscoveryPrompt(args) {
|
|
|
60393
60719
|
}
|
|
60394
60720
|
function writeRequiredContextArtifact(artifactDir, filePath, content, label) {
|
|
60395
60721
|
try {
|
|
60396
|
-
|
|
60397
|
-
|
|
60722
|
+
mkdirSync7(artifactDir, { recursive: true });
|
|
60723
|
+
writeFileSync4(filePath, `${content}
|
|
60398
60724
|
`);
|
|
60399
60725
|
} catch (error) {
|
|
60400
60726
|
const detail = error instanceof Error ? error.message : String(error);
|
|
@@ -60945,7 +61271,7 @@ var init_ralph_review_gate = __esm(() => {
|
|
|
60945
61271
|
|
|
60946
61272
|
// dist/builtin/workflows/builtin/ralph-core.ts
|
|
60947
61273
|
import { mkdir as mkdir4, writeFile as writeFile8 } from "node:fs/promises";
|
|
60948
|
-
import { dirname as
|
|
61274
|
+
import { dirname as dirname13, join as join25 } from "node:path";
|
|
60949
61275
|
import { Type as Type20 } from "typebox";
|
|
60950
61276
|
function taggedPrompt4(sections) {
|
|
60951
61277
|
return sections.map(([tag, content]) => {
|
|
@@ -60985,11 +61311,11 @@ function slugifyResearchTopic(prompt) {
|
|
|
60985
61311
|
}
|
|
60986
61312
|
function defaultResearchPath(prompt, now = new Date) {
|
|
60987
61313
|
const date = now.toISOString().slice(0, 10);
|
|
60988
|
-
return
|
|
61314
|
+
return join25(DEFAULT_RESEARCH_DIR, `${date}-${slugifyResearchTopic(prompt)}.md`);
|
|
60989
61315
|
}
|
|
60990
61316
|
async function createImplementationNotesFile(prompt, runId) {
|
|
60991
61317
|
const notesDir = await createWorkflowArtifactDirectory(runId);
|
|
60992
|
-
const notesPath =
|
|
61318
|
+
const notesPath = join25(notesDir, IMPLEMENTATION_NOTES_FILENAME);
|
|
60993
61319
|
const initialNotes = [
|
|
60994
61320
|
"# Implementation Notes",
|
|
60995
61321
|
"",
|
|
@@ -61010,7 +61336,7 @@ async function createImplementationNotesFile(prompt, runId) {
|
|
|
61010
61336
|
}
|
|
61011
61337
|
async function createQaEvidenceVideoPath(runId) {
|
|
61012
61338
|
const qaDir = await createWorkflowArtifactDirectory(runId);
|
|
61013
|
-
return
|
|
61339
|
+
return join25(qaDir, QA_E2E_VIDEO_FILENAME);
|
|
61014
61340
|
}
|
|
61015
61341
|
function renderQaE2eVideoGuidance(qaVideoPath) {
|
|
61016
61342
|
return [
|
|
@@ -61076,7 +61402,7 @@ function artifactSafeName2(value2) {
|
|
|
61076
61402
|
return safe.length > 0 ? safe : "artifact";
|
|
61077
61403
|
}
|
|
61078
61404
|
async function writeJsonArtifact(path, content) {
|
|
61079
|
-
await mkdir4(
|
|
61405
|
+
await mkdir4(dirname13(path), { recursive: true });
|
|
61080
61406
|
await writeFile8(path, `${JSON.stringify(content, null, 2)}
|
|
61081
61407
|
`, {
|
|
61082
61408
|
encoding: "utf8"
|
|
@@ -61414,7 +61740,7 @@ var init_ralph_models = __esm(() => {
|
|
|
61414
61740
|
|
|
61415
61741
|
// dist/builtin/workflows/builtin/ralph-runner.ts
|
|
61416
61742
|
import { existsSync as existsSync6 } from "node:fs";
|
|
61417
|
-
import { join as
|
|
61743
|
+
import { join as join26, resolve as resolve12 } from "node:path";
|
|
61418
61744
|
async function runRalphWorkflow(ctx, options) {
|
|
61419
61745
|
const { prompt, acceptanceCriteria, maxLoops, comparisonBaseBranch, workflowStartCwd, createPr, runId } = options;
|
|
61420
61746
|
let latestReviewReportPath;
|
|
@@ -61426,7 +61752,7 @@ async function runRalphWorkflow(ctx, options) {
|
|
|
61426
61752
|
let finalPrReport;
|
|
61427
61753
|
const workflowCwdContext = workflowCwdContextSection(workflowStartCwd);
|
|
61428
61754
|
const workflowPrompt = prompt;
|
|
61429
|
-
const workflowResearchPath =
|
|
61755
|
+
const workflowResearchPath = resolve12(workflowStartCwd, defaultResearchPath(workflowPrompt));
|
|
61430
61756
|
const implementationNotesPath = await createImplementationNotesFile(workflowPrompt, runId);
|
|
61431
61757
|
const qaVideoPath = await createQaEvidenceVideoPath(runId);
|
|
61432
61758
|
const artifactDir = await createWorkflowArtifactDirectory(runId);
|
|
@@ -61475,7 +61801,7 @@ async function runRalphWorkflow(ctx, options) {
|
|
|
61475
61801
|
const researchPath = workflowResearchPath;
|
|
61476
61802
|
finalResearchPath = researchPath;
|
|
61477
61803
|
finalPlanPath = researchPath;
|
|
61478
|
-
const orchestratorReportPath =
|
|
61804
|
+
const orchestratorReportPath = join26(artifactDir, "orchestrator-report.md");
|
|
61479
61805
|
const orchestratorForkOptions = forkContinuationOptions3(previousOrchestratorSessionId);
|
|
61480
61806
|
const orchestratorPrompt = orchestratorForkOptions.forkFromSessionId === undefined ? taggedPrompt4([
|
|
61481
61807
|
["acceptance_criteria", keepContext(acceptanceCriteria)],
|
|
@@ -61611,7 +61937,7 @@ Read it before implementation or delegation because it is the primary current im
|
|
|
61611
61937
|
diagnostics: parsed.diagnostics,
|
|
61612
61938
|
allowFinalActionRemaining: createPr
|
|
61613
61939
|
});
|
|
61614
|
-
const artifactPath =
|
|
61940
|
+
const artifactPath = join26(artifactDir, `review-${artifactSafeName2(reviewer)}.json`);
|
|
61615
61941
|
await writeJsonArtifact(artifactPath, {
|
|
61616
61942
|
reviewer,
|
|
61617
61943
|
decision: parsed.decision,
|
|
@@ -61667,7 +61993,7 @@ Read it before implementation or delegation because it is the primary current im
|
|
|
61667
61993
|
usage: fold_usage([orchestrator, ...reviews, ...reverifyResults])
|
|
61668
61994
|
}));
|
|
61669
61995
|
}
|
|
61670
|
-
latestReviewReportPath = await writeJsonArtifact(
|
|
61996
|
+
latestReviewReportPath = await writeJsonArtifact(join26(artifactDir, "review-round-latest.json"), {
|
|
61671
61997
|
convergence_decision: roundConvergenceDecision,
|
|
61672
61998
|
convergence: convergenceEntries,
|
|
61673
61999
|
consolidated_findings: reverified.batch,
|
|
@@ -61816,7 +62142,7 @@ var init_ralph = __esm(() => {
|
|
|
61816
62142
|
}),
|
|
61817
62143
|
git_worktree_dir: Type21.String({
|
|
61818
62144
|
default: "",
|
|
61819
|
-
description: "Optional Git worktree path. Leave at the default unless the user explicitly requested worktree isolation — stages never create git worktrees on their own.
|
|
62145
|
+
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."
|
|
61820
62146
|
}),
|
|
61821
62147
|
create_pr: Type21.Boolean({
|
|
61822
62148
|
default: false,
|
|
@@ -62036,7 +62362,7 @@ var init_tournament_prompts = __esm(() => {
|
|
|
62036
62362
|
|
|
62037
62363
|
// dist/builtin/workflows/builtin/tournament-runner.ts
|
|
62038
62364
|
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile9 } from "node:fs/promises";
|
|
62039
|
-
import { join as
|
|
62365
|
+
import { join as join27, isAbsolute as isAbsolute9, relative as relative5, resolve as resolve13 } from "node:path";
|
|
62040
62366
|
import { Type as Type22 } from "typebox";
|
|
62041
62367
|
function serializableObject3(value2) {
|
|
62042
62368
|
if (value2 === null || Array.isArray(value2) || typeof value2 !== "object")
|
|
@@ -62073,8 +62399,8 @@ function criterionSlug(id) {
|
|
|
62073
62399
|
}
|
|
62074
62400
|
function judgeArtifactPath(judgesDir, job, criterionIndex) {
|
|
62075
62401
|
const filename = `judge-${job.a}-${job.b}-c${criterionIndex}-${criterionSlug(job.criterionId)}-r${job.rep}.json`;
|
|
62076
|
-
const root =
|
|
62077
|
-
const path =
|
|
62402
|
+
const root = resolve13(judgesDir);
|
|
62403
|
+
const path = resolve13(judgesDir, filename);
|
|
62078
62404
|
const fromRoot = relative5(root, path);
|
|
62079
62405
|
if (fromRoot.startsWith("..") || isAbsolute9(fromRoot)) {
|
|
62080
62406
|
throw new Error(`tournament: judge artifact escaped its directory: ${path}`);
|
|
@@ -62125,16 +62451,16 @@ function combineUsage(totals) {
|
|
|
62125
62451
|
}
|
|
62126
62452
|
async function runTournament(ctx) {
|
|
62127
62453
|
const artifactDir = await stableArtifactRoot(ctx, "tournament");
|
|
62128
|
-
const attemptsDir =
|
|
62454
|
+
const attemptsDir = join27(artifactDir, "attempts");
|
|
62129
62455
|
const numAttempts = ctx.inputs.num_attempts ?? 4;
|
|
62130
62456
|
const maxConcurrency = ctx.inputs.max_concurrency ?? 4;
|
|
62131
62457
|
const nEvaluations = ctx.inputs.n_evaluations ?? 2;
|
|
62132
62458
|
const pivotCount = ctx.inputs.pivots ?? 1;
|
|
62133
62459
|
const seed = ctx.inputs.seed ?? 0;
|
|
62134
|
-
const judgesDir =
|
|
62460
|
+
const judgesDir = join27(artifactDir, "judges");
|
|
62135
62461
|
await mkdir5(attemptsDir, { recursive: true });
|
|
62136
62462
|
await mkdir5(judgesDir, { recursive: true });
|
|
62137
|
-
const attemptArtifactPaths = Array.from({ length: numAttempts }, (_, index) =>
|
|
62463
|
+
const attemptArtifactPaths = Array.from({ length: numAttempts }, (_, index) => join27(attemptsDir, `attempt-${index + 1}.md`));
|
|
62138
62464
|
const models = ctx.inputs.models;
|
|
62139
62465
|
const attemptResults = await ctx.parallel(attemptArtifactPaths.map((path, index) => ({
|
|
62140
62466
|
name: `attempt-${index + 1}`,
|
|
@@ -62299,7 +62625,7 @@ The previous report was invalid. Return the required JSON schema exactly once.`,
|
|
|
62299
62625
|
const outputRanking = ranking.map(({ label, meanPreference }) => ({ label, meanPreference }));
|
|
62300
62626
|
const budgetPivots = Math.min(pivotCount, entrants.length);
|
|
62301
62627
|
const comparisonBudget = (entrants.length + budgetPivots * (entrants.length - budgetPivots) + budgetPivots * (budgetPivots - 1) / 2) * criteria.length * nEvaluations;
|
|
62302
|
-
const comparisonPath =
|
|
62628
|
+
const comparisonPath = join27(artifactDir, "comparisons.json");
|
|
62303
62629
|
const assignment = modelAssignment(entrants, models);
|
|
62304
62630
|
const phaseUsage = {
|
|
62305
62631
|
attempts: fold_usage(attemptResults),
|
|
@@ -62335,7 +62661,7 @@ The previous report was invalid. Return the required JSON schema exactly once.`,
|
|
|
62335
62661
|
await writeLedger(fold_usage([]));
|
|
62336
62662
|
const winner = ranking[0];
|
|
62337
62663
|
const winnerEntrant = entrants[winner.index];
|
|
62338
|
-
const resultPath =
|
|
62664
|
+
const resultPath = join27(artifactDir, "winner.md");
|
|
62339
62665
|
const reducer = await ctx.task("comparisons-reducer", {
|
|
62340
62666
|
prompt: renderComparisonsReducerPrompt({
|
|
62341
62667
|
task: ctx.inputs.prompt,
|
|
@@ -62685,17 +63011,26 @@ function isSameCheckpointValue(stored, next) {
|
|
|
62685
63011
|
const hash = (value2) => durableHash(value2);
|
|
62686
63012
|
return hash(stored) === hash(next);
|
|
62687
63013
|
}
|
|
63014
|
+
function withTimesOf(later, recorded) {
|
|
63015
|
+
const both = (key) => typeof recorded[key] === "number" && typeof later[key] === "number";
|
|
63016
|
+
return {
|
|
63017
|
+
...later,
|
|
63018
|
+
...both("startedAt") ? { startedAt: recorded.startedAt } : {},
|
|
63019
|
+
...both("endedAt") ? { endedAt: recorded.endedAt } : {},
|
|
63020
|
+
...both("completedAt") ? { completedAt: recorded.completedAt } : {}
|
|
63021
|
+
};
|
|
63022
|
+
}
|
|
62688
63023
|
function isRecordedCheckpointValue(stored, next) {
|
|
62689
63024
|
const recorded = stored;
|
|
62690
63025
|
if (isSameCheckpointValue(recorded, next))
|
|
62691
63026
|
return true;
|
|
62692
|
-
|
|
62693
|
-
if (typeof completedAt !== "number")
|
|
63027
|
+
if (typeof recorded.completedAt !== "number")
|
|
62694
63028
|
return false;
|
|
62695
63029
|
const later = next;
|
|
62696
|
-
if (startedAt === undefined !== (later.startedAt === undefined))
|
|
63030
|
+
if (recorded.startedAt === undefined !== (later.startedAt === undefined))
|
|
62697
63031
|
return false;
|
|
62698
|
-
|
|
63032
|
+
const retimed = withTimesOf(later, recorded);
|
|
63033
|
+
return isSameCheckpointValue(recorded.topology !== undefined && later.topology !== undefined ? { ...retimed, topology: withTimesOf(later.topology, recorded.topology) } : retimed, recorded);
|
|
62699
63034
|
}
|
|
62700
63035
|
var MIRROR_FRESH_MS = 2000;
|
|
62701
63036
|
var init_pool_remote_journal = __esm(() => {
|
|
@@ -63273,7 +63608,7 @@ class SqlPoolQueue {
|
|
|
63273
63608
|
throw new Error("Stale pool coordinator authority", { cause: error });
|
|
63274
63609
|
throw error;
|
|
63275
63610
|
});
|
|
63276
|
-
const row = await this.current(tx, lease, token);
|
|
63611
|
+
const row = await this.current(tx, lease, token, true);
|
|
63277
63612
|
if (row.epoch !== lease.epoch)
|
|
63278
63613
|
throw new Error(STALE_WORK_LEASE);
|
|
63279
63614
|
assertSession();
|
|
@@ -63731,15 +64066,7 @@ class SqlPoolQueue {
|
|
|
63731
64066
|
}
|
|
63732
64067
|
}
|
|
63733
64068
|
function isRecordedCheckpoint(stored, payload) {
|
|
63734
|
-
|
|
63735
|
-
return true;
|
|
63736
|
-
const { completedAt, startedAt } = JSON.parse(stored);
|
|
63737
|
-
if (typeof completedAt !== "number")
|
|
63738
|
-
return false;
|
|
63739
|
-
const next = JSON.parse(payload);
|
|
63740
|
-
if (startedAt === undefined !== (next.startedAt === undefined))
|
|
63741
|
-
return false;
|
|
63742
|
-
return serializeJson({ ...next, completedAt, ...startedAt === undefined ? {} : { startedAt } }) === stored;
|
|
64069
|
+
return stored === payload || isRecordedCheckpointValue(JSON.parse(stored), JSON.parse(payload));
|
|
63743
64070
|
}
|
|
63744
64071
|
function openSqlPoolQueue(journal, remote) {
|
|
63745
64072
|
const storage = sqlPoolStorage(journal);
|
|
@@ -65293,7 +65620,7 @@ function isBrokerStatusReport(value2) {
|
|
|
65293
65620
|
|
|
65294
65621
|
// dist/builtin/intercom/broker/paths.ts
|
|
65295
65622
|
import { homedir as homedir2 } from "os";
|
|
65296
|
-
import { join as
|
|
65623
|
+
import { join as join28 } from "path";
|
|
65297
65624
|
function getHomeDir() {
|
|
65298
65625
|
if (process.platform === "win32") {
|
|
65299
65626
|
if (process.env.USERPROFILE)
|
|
@@ -65310,7 +65637,7 @@ function expandTildePath(path) {
|
|
|
65310
65637
|
if (path === "~")
|
|
65311
65638
|
return getHomeDir();
|
|
65312
65639
|
if (path.startsWith("~/") || process.platform === "win32" && path.startsWith("~\\")) {
|
|
65313
|
-
return
|
|
65640
|
+
return join28(getHomeDir(), path.slice(2));
|
|
65314
65641
|
}
|
|
65315
65642
|
return path;
|
|
65316
65643
|
}
|
|
@@ -65321,31 +65648,31 @@ function getAgentDir5() {
|
|
|
65321
65648
|
const piAgentDir = process.env.PI_CODING_AGENT_DIR;
|
|
65322
65649
|
if (piAgentDir)
|
|
65323
65650
|
return expandTildePath(piAgentDir);
|
|
65324
|
-
return
|
|
65651
|
+
return join28(getHomeDir(), ".atomic", "agent");
|
|
65325
65652
|
}
|
|
65326
65653
|
function sanitizePipeSegment(value2) {
|
|
65327
65654
|
return value2.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "default";
|
|
65328
65655
|
}
|
|
65329
65656
|
function getIntercomDirPath(agentDir = getAgentDir5()) {
|
|
65330
|
-
return
|
|
65657
|
+
return join28(agentDir, "intercom");
|
|
65331
65658
|
}
|
|
65332
65659
|
function getBrokerPidPath(agentDir = getAgentDir5()) {
|
|
65333
|
-
return
|
|
65660
|
+
return join28(getIntercomDirPath(agentDir), "broker.pid");
|
|
65334
65661
|
}
|
|
65335
65662
|
function getBrokerSpawnLockPath(agentDir = getAgentDir5()) {
|
|
65336
|
-
return
|
|
65663
|
+
return join28(getIntercomDirPath(agentDir), "broker.spawn.lock");
|
|
65337
65664
|
}
|
|
65338
65665
|
function getBrokerLogPath(agentDir = getAgentDir5()) {
|
|
65339
|
-
return
|
|
65666
|
+
return join28(getIntercomDirPath(agentDir), "broker.log");
|
|
65340
65667
|
}
|
|
65341
65668
|
function getBrokerStartAnnouncementPath(agentDir = getAgentDir5()) {
|
|
65342
|
-
return
|
|
65669
|
+
return join28(getIntercomDirPath(agentDir), "broker.starting");
|
|
65343
65670
|
}
|
|
65344
65671
|
function getBrokerSocketPath(platform = process.platform, agentDir = getAgentDir5()) {
|
|
65345
65672
|
if (platform === "win32") {
|
|
65346
65673
|
return `\\\\.\\pipe\\pi-intercom-${sanitizePipeSegment(agentDir)}`;
|
|
65347
65674
|
}
|
|
65348
|
-
return
|
|
65675
|
+
return join28(getIntercomDirPath(agentDir), "broker.sock");
|
|
65349
65676
|
}
|
|
65350
65677
|
var BROKER_STORAGE_ENV = "ATOMIC_STORAGE";
|
|
65351
65678
|
var init_paths = () => {};
|
|
@@ -67029,11 +67356,11 @@ var init_client = __esm(() => {
|
|
|
67029
67356
|
var BROKER_READY_TIMEOUT_MS = 30000, MIN_BROKER_STARTUP_TIMEOUT_MS = 1000;
|
|
67030
67357
|
|
|
67031
67358
|
// dist/builtin/intercom/broker/start-announcement.ts
|
|
67032
|
-
import { readFileSync as
|
|
67359
|
+
import { readFileSync as readFileSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
67033
67360
|
function readBrokerStartAnnouncement(announcement) {
|
|
67034
67361
|
let text;
|
|
67035
67362
|
try {
|
|
67036
|
-
text =
|
|
67363
|
+
text = readFileSync6(announcement.path, "utf-8");
|
|
67037
67364
|
} catch {
|
|
67038
67365
|
return;
|
|
67039
67366
|
}
|
|
@@ -67052,26 +67379,26 @@ var init_start_announcement = () => {};
|
|
|
67052
67379
|
// dist/builtin/intercom/broker/spawn.ts
|
|
67053
67380
|
import { spawn } from "child_process";
|
|
67054
67381
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
67055
|
-
import { appendFileSync, chmodSync, closeSync as closeSync3, existsSync as existsSync7, mkdirSync as
|
|
67056
|
-
import { join as
|
|
67382
|
+
import { appendFileSync, chmodSync, closeSync as closeSync3, existsSync as existsSync7, mkdirSync as mkdirSync8, openSync as openSync3, readFileSync as readFileSync7, readSync as readSync2, statSync as statSync5, unlinkSync as unlinkSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
67383
|
+
import { join as join29, dirname as dirname14 } from "path";
|
|
67057
67384
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
67058
67385
|
import { createRequire as createRequire3 } from "module";
|
|
67059
67386
|
import { createChildProcessEnvironment as createChildProcessEnvironment4, isBunBinary, processSharedStorageSelection } from "@bastani/atomic";
|
|
67060
67387
|
function isIntercomPackageRoot(dir) {
|
|
67061
|
-
return existsSync7(
|
|
67388
|
+
return existsSync7(join29(dir, "package.json")) && existsSync7(join29(dir, "broker"));
|
|
67062
67389
|
}
|
|
67063
67390
|
function resolveIntercomPackageRoot(moduleHref) {
|
|
67064
|
-
const here =
|
|
67391
|
+
const here = dirname14(fileURLToPath5(moduleHref));
|
|
67065
67392
|
if (isIntercomPackageRoot(here))
|
|
67066
67393
|
return here;
|
|
67067
|
-
return
|
|
67394
|
+
return join29(here, "..");
|
|
67068
67395
|
}
|
|
67069
67396
|
function resolveBrokerEntrypoint(packageRoot) {
|
|
67070
|
-
const bundled =
|
|
67071
|
-
return existsSync7(bundled) ? bundled :
|
|
67397
|
+
const bundled = join29(packageRoot, "broker", "broker.bundle.mjs");
|
|
67398
|
+
return existsSync7(bundled) ? bundled : join29(packageRoot, "broker", "broker.ts");
|
|
67072
67399
|
}
|
|
67073
67400
|
function ensurePrivateDirectory(path) {
|
|
67074
|
-
|
|
67401
|
+
mkdirSync8(path, { recursive: true, mode: 448 });
|
|
67075
67402
|
if (process.platform !== "win32")
|
|
67076
67403
|
chmodSync(path, 448);
|
|
67077
67404
|
}
|
|
@@ -67084,14 +67411,14 @@ function getCurrentBrokerRuntime() {
|
|
|
67084
67411
|
return isBunBinary ? "bun-binary" : "bun-source";
|
|
67085
67412
|
}
|
|
67086
67413
|
function requireFromExtensionDir(extensionDir) {
|
|
67087
|
-
return createRequire3(
|
|
67414
|
+
return createRequire3(join29(extensionDir, "package.json"));
|
|
67088
67415
|
}
|
|
67089
67416
|
function getJitiCliPath(extensionDir = EXTENSION_DIR) {
|
|
67090
67417
|
try {
|
|
67091
67418
|
const jitiPackage = requireFromExtensionDir(extensionDir).resolve("jiti/package.json");
|
|
67092
|
-
return
|
|
67419
|
+
return join29(dirname14(jitiPackage), "lib", "jiti-cli.mjs");
|
|
67093
67420
|
} catch {
|
|
67094
|
-
return
|
|
67421
|
+
return join29(extensionDir, "node_modules", "jiti", "lib", "jiti-cli.mjs");
|
|
67095
67422
|
}
|
|
67096
67423
|
}
|
|
67097
67424
|
function getDefaultBrokerRunnerPath(extensionDir) {
|
|
@@ -67113,7 +67440,7 @@ function quoteWindowsArg(value2) {
|
|
|
67113
67440
|
return `"${value2.replace(/"/g, '""')}"`;
|
|
67114
67441
|
}
|
|
67115
67442
|
function getWindowsHiddenLauncherPath(intercomDir = INTERCOM_DIR) {
|
|
67116
|
-
return
|
|
67443
|
+
return join29(intercomDir, "broker-launch.vbs");
|
|
67117
67444
|
}
|
|
67118
67445
|
function usesDefaultBrokerCommand(brokerCommand, brokerArgs) {
|
|
67119
67446
|
return brokerCommand === "npx" && brokerArgs.length === 2 && brokerArgs[0] === "--no-install" && brokerArgs[1] === "tsx";
|
|
@@ -67140,8 +67467,8 @@ function getWindowsHiddenLauncherScript(commandLine) {
|
|
|
67140
67467
|
`);
|
|
67141
67468
|
}
|
|
67142
67469
|
function writeWindowsHiddenLauncher(commandLine, launcherPath = getWindowsHiddenLauncherPath()) {
|
|
67143
|
-
ensurePrivateDirectory(
|
|
67144
|
-
|
|
67470
|
+
ensurePrivateDirectory(dirname14(launcherPath));
|
|
67471
|
+
writeFileSync6(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
|
|
67145
67472
|
return launcherPath;
|
|
67146
67473
|
}
|
|
67147
67474
|
function getBrokerLaunchSpec(brokerPath, brokerCommand, brokerArgs, extensionDir = EXTENSION_DIR, platform = process.platform, intercomDir = INTERCOM_DIR, nodePath = process.execPath, runtime = getCurrentBrokerRuntime(), logPath = BROKER_LOG) {
|
|
@@ -67184,14 +67511,14 @@ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, stderr = "ignore",
|
|
|
67184
67511
|
};
|
|
67185
67512
|
}
|
|
67186
67513
|
function resetBrokerLog(logPath = BROKER_LOG) {
|
|
67187
|
-
ensurePrivateDirectory(
|
|
67514
|
+
ensurePrivateDirectory(dirname14(logPath));
|
|
67188
67515
|
try {
|
|
67189
67516
|
const previous = readBrokerLogTail(logPath);
|
|
67190
67517
|
closeSync3(openSync3(logPath, "w"));
|
|
67191
67518
|
if (previous.length > 0) {
|
|
67192
67519
|
try {
|
|
67193
67520
|
const previousPath = `${logPath}.previous`;
|
|
67194
|
-
|
|
67521
|
+
writeFileSync6(previousPath, Buffer.from(previous).subarray(-BROKER_LOG_TAIL_BYTES), { mode: 384 });
|
|
67195
67522
|
if (process.platform !== "win32")
|
|
67196
67523
|
chmodSync(previousPath, 384);
|
|
67197
67524
|
} catch {}
|
|
@@ -67335,7 +67662,7 @@ async function isBrokerRunning() {
|
|
|
67335
67662
|
if (!existsSync7(BROKER_PID))
|
|
67336
67663
|
return false;
|
|
67337
67664
|
try {
|
|
67338
|
-
const pid = parseInt(
|
|
67665
|
+
const pid = parseInt(readFileSync7(BROKER_PID, "utf-8").trim(), 10);
|
|
67339
67666
|
if (!Number.isFinite(pid))
|
|
67340
67667
|
return false;
|
|
67341
67668
|
process.kill(pid, 0);
|
|
@@ -67360,7 +67687,7 @@ function handSpawnLockToBroker(pid, owner) {
|
|
|
67360
67687
|
}
|
|
67361
67688
|
function ownsSpawnLock(owner, lockPath = BROKER_SPAWN_LOCK) {
|
|
67362
67689
|
try {
|
|
67363
|
-
return
|
|
67690
|
+
return readFileSync7(lockPath, "utf-8").startsWith(owner);
|
|
67364
67691
|
} catch {
|
|
67365
67692
|
return false;
|
|
67366
67693
|
}
|
|
@@ -67372,7 +67699,7 @@ function acquireSpawnLock() {
|
|
|
67372
67699
|
${Date.now()}
|
|
67373
67700
|
`;
|
|
67374
67701
|
try {
|
|
67375
|
-
|
|
67702
|
+
writeFileSync6(BROKER_SPAWN_LOCK, owner, { flag: "wx" });
|
|
67376
67703
|
return owner;
|
|
67377
67704
|
} catch (error) {
|
|
67378
67705
|
if (!(error instanceof Error) || error.code !== "EEXIST") {
|
|
@@ -67402,7 +67729,7 @@ function isSpawnLockStale(lockPath = BROKER_SPAWN_LOCK, nowMs = Date.now()) {
|
|
|
67402
67729
|
return false;
|
|
67403
67730
|
}
|
|
67404
67731
|
try {
|
|
67405
|
-
const [spawnerLine = "", createdAtLine = "", brokerLine = ""] =
|
|
67732
|
+
const [spawnerLine = "", createdAtLine = "", brokerLine = ""] = readFileSync7(lockPath, "utf-8").trim().split(`
|
|
67406
67733
|
`);
|
|
67407
67734
|
const spawnerPid = Number.parseInt(spawnerLine, 10);
|
|
67408
67735
|
const createdAt = Number.parseInt(createdAtLine, 10);
|
|
@@ -67480,14 +67807,14 @@ var init_reply_waiter = __esm(() => {
|
|
|
67480
67807
|
});
|
|
67481
67808
|
|
|
67482
67809
|
// dist/builtin/intercom/config.ts
|
|
67483
|
-
import { existsSync as existsSync8, readFileSync as
|
|
67810
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
67484
67811
|
import { getAgentConfigPaths } from "@bastani/atomic";
|
|
67485
67812
|
function loadConfig() {
|
|
67486
67813
|
if (!existsSync8(CONFIG_PATH)) {
|
|
67487
67814
|
return { ...defaults };
|
|
67488
67815
|
}
|
|
67489
67816
|
try {
|
|
67490
|
-
const raw =
|
|
67817
|
+
const raw = readFileSync8(CONFIG_PATH, "utf-8");
|
|
67491
67818
|
const parsed = JSON.parse(raw);
|
|
67492
67819
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
67493
67820
|
throw new Error("Config must be a JSON object");
|
|
@@ -68070,12 +68397,12 @@ var init_sql_pool_usage = __esm(() => {
|
|
|
68070
68397
|
|
|
68071
68398
|
// dist/builtin/workflows/src/workers/pooled-watch-trace.ts
|
|
68072
68399
|
import { appendFileSync as appendFileSync2 } from "node:fs";
|
|
68073
|
-
import { dirname as
|
|
68400
|
+
import { dirname as dirname15, join as join30, resolve as resolve14 } from "node:path";
|
|
68074
68401
|
function configurePooledWatchTrace(journalPath, processRole) {
|
|
68075
68402
|
if (setting === undefined || setting === "" || setting === "0")
|
|
68076
68403
|
return;
|
|
68077
68404
|
role = processRole;
|
|
68078
|
-
file = setting === "1" ?
|
|
68405
|
+
file = setting === "1" ? join30(dirname15(resolve14(journalPath)), "pooled-watch-trace.jsonl") : resolve14(setting);
|
|
68079
68406
|
}
|
|
68080
68407
|
function tracePooledWatch(step, detail) {
|
|
68081
68408
|
if (file === undefined)
|
|
@@ -68268,8 +68595,8 @@ var init_startup_timeout = () => {};
|
|
|
68268
68595
|
import { fork } from "node:child_process";
|
|
68269
68596
|
import { randomUUID as randomUUID17 } from "node:crypto";
|
|
68270
68597
|
import { createWriteStream } from "node:fs";
|
|
68271
|
-
import { mkdir as mkdir6, rm as rm3, rmdir } from "node:fs/promises";
|
|
68272
|
-
import { dirname as
|
|
68598
|
+
import { appendFile, mkdir as mkdir6, rm as rm3, rmdir } from "node:fs/promises";
|
|
68599
|
+
import { dirname as dirname16, join as join31, resolve as resolve15 } from "node:path";
|
|
68273
68600
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
68274
68601
|
function isRootLeaseLost(error) {
|
|
68275
68602
|
return error instanceof StaleLeaseError || error.message.startsWith("Stale lease for run ");
|
|
@@ -68278,7 +68605,7 @@ function unconfirmedCleanup(groups) {
|
|
|
68278
68605
|
return new Error(`Local worker process cleanup unconfirmed; ${groups.length > 0 ? `process groups ${groups.join(", ")} still running after SIGKILL; ` : ""}permits quarantined`);
|
|
68279
68606
|
}
|
|
68280
68607
|
function workerLogDirectory(journalPath, root) {
|
|
68281
|
-
return
|
|
68608
|
+
return join31(dirname16(resolve15(journalPath)), "worker-logs", encodeURIComponent(root).replaceAll(".", "%2E"));
|
|
68282
68609
|
}
|
|
68283
68610
|
function token(work) {
|
|
68284
68611
|
return {
|
|
@@ -68460,9 +68787,13 @@ class PoolCoordinator {
|
|
|
68460
68787
|
incarnations = new Map;
|
|
68461
68788
|
readyWorkers = new Set;
|
|
68462
68789
|
logs = new Map;
|
|
68790
|
+
keptLogs = new Set;
|
|
68791
|
+
unconfirmedExits = new WeakSet;
|
|
68792
|
+
failed = false;
|
|
68463
68793
|
cleanups = new Set;
|
|
68464
68794
|
controlAcks = new Map;
|
|
68465
68795
|
lastHeard = new Map;
|
|
68796
|
+
awaiting = new Map;
|
|
68466
68797
|
pendingActivity = new Map;
|
|
68467
68798
|
batches = new Map;
|
|
68468
68799
|
publicationFailures = new Map;
|
|
@@ -68507,7 +68838,6 @@ class PoolCoordinator {
|
|
|
68507
68838
|
nextRecoveryAt = 0;
|
|
68508
68839
|
cleanupFailure;
|
|
68509
68840
|
held = new Set;
|
|
68510
|
-
quarantined = new Set;
|
|
68511
68841
|
unreaped = [];
|
|
68512
68842
|
retained = new Map;
|
|
68513
68843
|
retainedTransitions = Promise.resolve();
|
|
@@ -68700,18 +69030,15 @@ class PoolCoordinator {
|
|
|
68700
69030
|
try {
|
|
68701
69031
|
const { survivors, unprobeable } = await reapProcessGroups(this.journal, this.root, { attempt: work });
|
|
68702
69032
|
if (survivors.length > 0) {
|
|
68703
|
-
this.quarantined.add(work.id);
|
|
68704
69033
|
this.unreaped.push({ attempt: work });
|
|
68705
|
-
this.
|
|
68706
|
-
return;
|
|
69034
|
+
this.reportHealth(undefined, `${unconfirmedCleanup(survivors).message}; child ${work.childRunId} of an earlier incarnation replays`);
|
|
68707
69035
|
}
|
|
68708
69036
|
if (unprobeable.length === 0)
|
|
68709
69037
|
await this.releaseRetained(work);
|
|
68710
69038
|
} catch (error) {
|
|
68711
69039
|
this.cleanupFailure ??= error instanceof Error ? error : new Error(String(error));
|
|
68712
69040
|
} finally {
|
|
68713
|
-
|
|
68714
|
-
this.held.delete(work.id);
|
|
69041
|
+
this.held.delete(work.id);
|
|
68715
69042
|
}
|
|
68716
69043
|
}
|
|
68717
69044
|
releaseRetained(work) {
|
|
@@ -68842,6 +69169,7 @@ class PoolCoordinator {
|
|
|
68842
69169
|
});
|
|
68843
69170
|
}
|
|
68844
69171
|
fail(error) {
|
|
69172
|
+
this.failed = true;
|
|
68845
69173
|
this.port.send({ kind: "failure", error: error.message });
|
|
68846
69174
|
}
|
|
68847
69175
|
failWork(work, error) {
|
|
@@ -69209,7 +69537,7 @@ class PoolCoordinator {
|
|
|
69209
69537
|
this.slots.add(slot);
|
|
69210
69538
|
let ready = false;
|
|
69211
69539
|
let serial = Promise.resolve();
|
|
69212
|
-
const logPath =
|
|
69540
|
+
const logPath = join31(this.logDirectory, `${worker}.log`);
|
|
69213
69541
|
this.logs.set(child, logPath);
|
|
69214
69542
|
let logFailure;
|
|
69215
69543
|
const log = createWriteStream(logPath, { flags: "a" });
|
|
@@ -69250,7 +69578,11 @@ class PoolCoordinator {
|
|
|
69250
69578
|
terminate();
|
|
69251
69579
|
});
|
|
69252
69580
|
const closed = new Promise((resolve) => child.once("close", () => resolve()));
|
|
69253
|
-
|
|
69581
|
+
let retired = false;
|
|
69582
|
+
const retire = () => {
|
|
69583
|
+
if (retired)
|
|
69584
|
+
return;
|
|
69585
|
+
retired = true;
|
|
69254
69586
|
Promise.race([closed, delay2(STDERR_DRAIN_GRACE_MS)]).then(() => reject(diagnosed(timedOut ? startupTimeoutMessage(startupTimeoutMs) : "Local worker exited before readiness")));
|
|
69255
69587
|
this.children.delete(child);
|
|
69256
69588
|
this.logs.delete(child);
|
|
@@ -69261,6 +69593,7 @@ class PoolCoordinator {
|
|
|
69261
69593
|
this.readyWorkers.delete(worker);
|
|
69262
69594
|
this.controlAcks.delete(worker);
|
|
69263
69595
|
this.lastHeard.delete(worker);
|
|
69596
|
+
this.awaiting.delete(worker);
|
|
69264
69597
|
this.slots.delete(slot);
|
|
69265
69598
|
this.endStageWatches((watch) => watch.slot === slot, "worker_exited");
|
|
69266
69599
|
for (const settle of slot.stageCommands.values())
|
|
@@ -69274,7 +69607,7 @@ class PoolCoordinator {
|
|
|
69274
69607
|
const held = slot.work;
|
|
69275
69608
|
if (held)
|
|
69276
69609
|
this.held.add(held.id);
|
|
69277
|
-
let
|
|
69610
|
+
let unconfirmed;
|
|
69278
69611
|
const cleanup = disconnected.then(() => serial).then(async () => {
|
|
69279
69612
|
try {
|
|
69280
69613
|
const admission = this.admission;
|
|
@@ -69287,18 +69620,13 @@ class PoolCoordinator {
|
|
|
69287
69620
|
throw unconfirmedCleanup(alive);
|
|
69288
69621
|
if (alive.length === 0)
|
|
69289
69622
|
return;
|
|
69290
|
-
|
|
69291
|
-
|
|
69292
|
-
this.quarantined.add(held.id);
|
|
69293
|
-
slot.work = undefined;
|
|
69294
|
-
childFailed = true;
|
|
69295
|
-
this.port.send({ kind: "work-failure", workId: held.id, error: error.message });
|
|
69296
|
-
}
|
|
69623
|
+
unconfirmed = unconfirmedCleanup(alive);
|
|
69624
|
+
this.reportHealth(logPath, `${unconfirmed.message}; the worker was retired and replaced`);
|
|
69297
69625
|
} catch (error) {
|
|
69298
69626
|
this.cleanupFailure = error instanceof Error ? error : new Error(String(error));
|
|
69299
69627
|
throw this.cleanupFailure;
|
|
69300
69628
|
} finally {
|
|
69301
|
-
if (held
|
|
69629
|
+
if (held)
|
|
69302
69630
|
this.held.delete(held.id);
|
|
69303
69631
|
}
|
|
69304
69632
|
}).then(async () => {
|
|
@@ -69319,8 +69647,8 @@ class PoolCoordinator {
|
|
|
69319
69647
|
const settled = attempt !== undefined && await this.whileRootLate(queue, () => exhausted ? queue.failExited(lease, attempt) : queue.releaseExited(lease, attempt));
|
|
69320
69648
|
const superseded = work !== undefined && !settled && await this.superseded(work);
|
|
69321
69649
|
if (work !== undefined && exhausted && !superseded)
|
|
69322
|
-
this.failWork(work, diagnosed(childRetryLimit(work, "lost its worker")));
|
|
69323
|
-
if (work === undefined &&
|
|
69650
|
+
this.failWork(work, diagnosed(`${childRetryLimit(work, "lost its worker")}${unconfirmed === undefined ? "" : `; ${unconfirmed.message}`}`));
|
|
69651
|
+
if (work === undefined && ++this.replacements > 3)
|
|
69324
69652
|
this.fail(diagnosed("Local worker retry limit"));
|
|
69325
69653
|
else
|
|
69326
69654
|
await this.spawn();
|
|
@@ -69329,7 +69657,9 @@ class PoolCoordinator {
|
|
|
69329
69657
|
});
|
|
69330
69658
|
this.cleanups.add(cleanup);
|
|
69331
69659
|
cleanup.finally(() => this.cleanups.delete(cleanup));
|
|
69332
|
-
}
|
|
69660
|
+
};
|
|
69661
|
+
slot.retire = retire;
|
|
69662
|
+
child.once("exit", retire);
|
|
69333
69663
|
child.on("message", (message) => {
|
|
69334
69664
|
this.observeResponsiveness();
|
|
69335
69665
|
this.lastHeard.set(worker, { at: performance.now(), stalled: this.stalledMs });
|
|
@@ -69403,10 +69733,11 @@ class PoolCoordinator {
|
|
|
69403
69733
|
const fenced = slot.work;
|
|
69404
69734
|
slot.work = undefined;
|
|
69405
69735
|
if (fenced && !await this.superseded(fenced)) {
|
|
69736
|
+
this.fence(fenced);
|
|
69406
69737
|
if (this.attempts(fenced) < MAX_CHILD_ATTEMPTS)
|
|
69407
69738
|
await leased(() => queue.releaseExited(lease, fenced));
|
|
69408
69739
|
else if (await leased(() => queue.failExited(lease, fenced)))
|
|
69409
|
-
this.failWork(fenced, new Error(childRetryLimit(fenced, "was refused by the root as stale on every attempt")));
|
|
69740
|
+
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})`)));
|
|
69410
69741
|
}
|
|
69411
69742
|
return;
|
|
69412
69743
|
}
|
|
@@ -69520,8 +69851,19 @@ class PoolCoordinator {
|
|
|
69520
69851
|
await this.sendResult(work.id);
|
|
69521
69852
|
});
|
|
69522
69853
|
const response = handled.then((value2) => routeReady ?? value2);
|
|
69523
|
-
if (message.kind !== "delivery" && message.kind !== "delivery-ready")
|
|
69524
|
-
|
|
69854
|
+
if (message.kind !== "delivery" && message.kind !== "delivery-ready") {
|
|
69855
|
+
this.awaiting.set(worker, (this.awaiting.get(worker) ?? 0) + 1);
|
|
69856
|
+
const answered = () => {
|
|
69857
|
+
const left = (this.awaiting.get(worker) ?? 1) - 1;
|
|
69858
|
+
if (left > 0)
|
|
69859
|
+
this.awaiting.set(worker, left);
|
|
69860
|
+
else
|
|
69861
|
+
this.awaiting.delete(worker);
|
|
69862
|
+
if (this.incarnations.get(worker) === child)
|
|
69863
|
+
this.lastHeard.set(worker, { at: performance.now(), stalled: this.stalledMs });
|
|
69864
|
+
};
|
|
69865
|
+
serial = handled.then(answered, answered);
|
|
69866
|
+
}
|
|
69525
69867
|
response.then((value2) => child.send({ id: message.id, value: value2 }, replied), (error) => {
|
|
69526
69868
|
const rootLeaseLost = isRootLeaseLost(error);
|
|
69527
69869
|
if (rootLeaseLost)
|
|
@@ -69567,8 +69909,6 @@ class PoolCoordinator {
|
|
|
69567
69909
|
return;
|
|
69568
69910
|
}
|
|
69569
69911
|
for (const work of expired) {
|
|
69570
|
-
if (this.quarantined.has(work.id))
|
|
69571
|
-
continue;
|
|
69572
69912
|
if (this.held.has(work.id)) {
|
|
69573
69913
|
await queue.extend(lease, work, leaseMs).catch((error) => {
|
|
69574
69914
|
if (error.message !== STALE_WORK_LEASE)
|
|
@@ -69584,16 +69924,19 @@ class PoolCoordinator {
|
|
|
69584
69924
|
});
|
|
69585
69925
|
continue;
|
|
69586
69926
|
}
|
|
69587
|
-
if (child && !await this.stop(child)
|
|
69588
|
-
|
|
69589
|
-
if (this.held.has(work.id)
|
|
69927
|
+
if (child && !await this.stop(child))
|
|
69928
|
+
this.retireUnconfirmed(child);
|
|
69929
|
+
if (this.held.has(work.id))
|
|
69590
69930
|
continue;
|
|
69591
69931
|
if ((await unconfirmedProcessGroups(this.journal, root, { attempt: work })).length > 0)
|
|
69592
69932
|
continue;
|
|
69593
69933
|
if (work.epoch < lease.epoch)
|
|
69594
69934
|
await this.releaseRetained(work);
|
|
69595
69935
|
else
|
|
69596
|
-
await queue.expire(lease, work)
|
|
69936
|
+
await queue.expire(lease, work).catch((error) => {
|
|
69937
|
+
if (error.message !== STALE_WORK_LEASE)
|
|
69938
|
+
throw error;
|
|
69939
|
+
});
|
|
69597
69940
|
}
|
|
69598
69941
|
})().finally(() => {
|
|
69599
69942
|
this.recovering = undefined;
|
|
@@ -69611,19 +69954,51 @@ class PoolCoordinator {
|
|
|
69611
69954
|
isLive(worker, child) {
|
|
69612
69955
|
this.observeResponsiveness();
|
|
69613
69956
|
const heard = this.lastHeard.get(worker);
|
|
69614
|
-
return child.exitCode === null && child.signalCode === null && child.connected && heard !== undefined && performance.now() - heard.at - (this.stalledMs - heard.stalled) < this.options.leaseMs;
|
|
69957
|
+
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);
|
|
69615
69958
|
}
|
|
69616
|
-
stop(child) {
|
|
69617
|
-
|
|
69618
|
-
|
|
69619
|
-
|
|
69620
|
-
|
|
69621
|
-
|
|
69622
|
-
|
|
69623
|
-
|
|
69624
|
-
|
|
69625
|
-
|
|
69626
|
-
|
|
69959
|
+
async stop(child) {
|
|
69960
|
+
const pid = child.pid;
|
|
69961
|
+
const exited = () => child.exitCode !== null || child.signalCode !== null;
|
|
69962
|
+
if (pid === undefined || exited())
|
|
69963
|
+
return true;
|
|
69964
|
+
const exit = new Promise((resolve) => child.once("exit", () => resolve()));
|
|
69965
|
+
const confirmed = async () => {
|
|
69966
|
+
await Promise.race([exit, delay2(WORKER_EXIT_CONFIRMATION_MS)]);
|
|
69967
|
+
return true;
|
|
69968
|
+
};
|
|
69969
|
+
this.observeResponsiveness();
|
|
69970
|
+
child.kill("SIGKILL");
|
|
69971
|
+
const since = performance.now();
|
|
69972
|
+
const stalledAt = this.stalledMs;
|
|
69973
|
+
for (;; ) {
|
|
69974
|
+
await Promise.race([exit, delay2(WORKER_EXIT_POLL_MS)]);
|
|
69975
|
+
if (exited())
|
|
69976
|
+
return true;
|
|
69977
|
+
if (workerProcessExited(pid))
|
|
69978
|
+
return confirmed();
|
|
69979
|
+
this.observeResponsiveness();
|
|
69980
|
+
if (performance.now() - since - (this.stalledMs - stalledAt) >= WORKER_EXIT_CONFIRMATION_MS)
|
|
69981
|
+
return exited() || workerProcessExited(pid, true) && confirmed();
|
|
69982
|
+
}
|
|
69983
|
+
}
|
|
69984
|
+
retireUnconfirmed(child) {
|
|
69985
|
+
if (this.unconfirmedExits.has(child))
|
|
69986
|
+
return;
|
|
69987
|
+
this.unconfirmedExits.add(child);
|
|
69988
|
+
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`);
|
|
69989
|
+
if (child.connected)
|
|
69990
|
+
child.disconnect();
|
|
69991
|
+
for (const slot of this.slots)
|
|
69992
|
+
if (slot.child === child)
|
|
69993
|
+
slot.retire?.();
|
|
69994
|
+
}
|
|
69995
|
+
reportHealth(log, message) {
|
|
69996
|
+
if (log !== undefined) {
|
|
69997
|
+
this.keptLogs.add(log);
|
|
69998
|
+
appendFile(log, `[atomic ${new Date().toISOString()}] ${message}
|
|
69999
|
+
`).catch(() => {});
|
|
70000
|
+
}
|
|
70001
|
+
this.port.send({ kind: "health", message: log === undefined ? message : `${message} (worker log: ${log})` });
|
|
69627
70002
|
}
|
|
69628
70003
|
async close() {
|
|
69629
70004
|
this.closed = true;
|
|
@@ -69633,10 +70008,15 @@ class PoolCoordinator {
|
|
|
69633
70008
|
this.rateLimitOutbox.close();
|
|
69634
70009
|
let failure;
|
|
69635
70010
|
let stopping = [];
|
|
70011
|
+
let confirmed = [];
|
|
69636
70012
|
try {
|
|
69637
70013
|
await this.recovering?.catch(() => {});
|
|
69638
70014
|
stopping = [...this.children].map((child) => ({ child, log: this.logs.get(child) }));
|
|
69639
|
-
await Promise.all(stopping.map(({ child }) => this.stop(child)));
|
|
70015
|
+
confirmed = await Promise.all(stopping.map(({ child }) => this.stop(child)));
|
|
70016
|
+
stopping.forEach(({ child }, index) => {
|
|
70017
|
+
if (!confirmed[index])
|
|
70018
|
+
this.retireUnconfirmed(child);
|
|
70019
|
+
});
|
|
69640
70020
|
await Promise.all([...this.cleanups]);
|
|
69641
70021
|
if (this.cleanupFailure)
|
|
69642
70022
|
throw this.cleanupFailure;
|
|
@@ -69648,15 +70028,15 @@ class PoolCoordinator {
|
|
|
69648
70028
|
if (alive.length > 0)
|
|
69649
70029
|
throw unconfirmedCleanup(alive);
|
|
69650
70030
|
if (this.desiredControl !== "quit" && this.queue !== undefined && this.lease !== undefined)
|
|
69651
|
-
await this.queue.releaseClosed(this.lease
|
|
70031
|
+
await this.queue.releaseClosed(this.lease).catch((error) => {
|
|
69652
70032
|
if (!(error instanceof StaleLeaseError))
|
|
69653
70033
|
throw error;
|
|
69654
70034
|
});
|
|
69655
70035
|
} catch (error) {
|
|
69656
70036
|
failure = error;
|
|
69657
70037
|
}
|
|
69658
|
-
if (failure === undefined) {
|
|
69659
|
-
const stopped = stopping.flatMap(({
|
|
70038
|
+
if (failure === undefined && !this.failed) {
|
|
70039
|
+
const stopped = stopping.flatMap(({ log }, index) => log !== undefined && !this.keptLogs.has(log) && confirmed[index] === true ? [log] : []);
|
|
69660
70040
|
await Promise.all(stopped.map((log) => rm3(log, { force: true }).catch(() => {})));
|
|
69661
70041
|
if (this.logDirectory !== undefined)
|
|
69662
70042
|
await rmdir(this.logDirectory).catch(() => {});
|
|
@@ -69682,7 +70062,7 @@ function childRetryLimit(work, reason) {
|
|
|
69682
70062
|
const exportName = work.definition.slice(work.definition.lastIndexOf("#") + 1);
|
|
69683
70063
|
return `Local worker retry limit: child ${work.childRunId} (${exportName}) ${reason} ${MAX_CHILD_ATTEMPTS} times`;
|
|
69684
70064
|
}
|
|
69685
|
-
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;
|
|
70065
|
+
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;
|
|
69686
70066
|
var init_pool_coordinator = __esm(() => {
|
|
69687
70067
|
init_internal();
|
|
69688
70068
|
init_pool_remote_journal();
|
|
@@ -69697,6 +70077,7 @@ var init_pool_coordinator = __esm(() => {
|
|
|
69697
70077
|
init_pending_stage_route_readiness();
|
|
69698
70078
|
init_store();
|
|
69699
70079
|
init_workflow_infrastructure_error();
|
|
70080
|
+
init_admission_host();
|
|
69700
70081
|
init_pool_journal();
|
|
69701
70082
|
init_pooled_watch_trace();
|
|
69702
70083
|
init_resource_admission();
|
|
@@ -69908,7 +70289,7 @@ function createWorkflowPendingStageDelivery(activeStore, runId, stageId, stageNa
|
|
|
69908
70289
|
};
|
|
69909
70290
|
}
|
|
69910
70291
|
async function deliverPendingStageMessages(activeStore, runId, stageId, stageName, deliver, commit, recipient) {
|
|
69911
|
-
const stickyEntries = stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient);
|
|
70292
|
+
const stickyEntries = stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient, recipient?.receivedMessageIds);
|
|
69912
70293
|
const candidateIds = new Set([
|
|
69913
70294
|
...activeStore.pendingStageMessagesFor(runId, stageId),
|
|
69914
70295
|
...activeStore.pendingStageMessagesFor(runId, stageName),
|
|
@@ -69964,7 +70345,7 @@ async function deliverPendingStageMessages(activeStore, runId, stageId, stageNam
|
|
|
69964
70345
|
}
|
|
69965
70346
|
}
|
|
69966
70347
|
}
|
|
69967
|
-
function stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient) {
|
|
70348
|
+
function stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageName, recipient, receivedByEndedStage = []) {
|
|
69968
70349
|
const runs = activeStore.runs();
|
|
69969
70350
|
const rootRunId = durableRootRunIdForRun(runs, runId);
|
|
69970
70351
|
if (rootRunId === undefined)
|
|
@@ -69973,22 +70354,28 @@ function stickyPendingStageEntriesForStage(activeStore, runId, stageId, stageNam
|
|
|
69973
70354
|
if (rootRun === undefined)
|
|
69974
70355
|
return [];
|
|
69975
70356
|
const stage = runs.find((run) => run.id === runId)?.stages.find((candidate) => candidate.id === stageId);
|
|
69976
|
-
|
|
70357
|
+
const ended = stage?.status === "completed" || stage?.status === "skipped";
|
|
70358
|
+
if (ended && receivedByEndedStage.length === 0)
|
|
69977
70359
|
return [];
|
|
69978
70360
|
const hops = workflowBoundaryHops(runs, runId);
|
|
69979
70361
|
if (hops === undefined)
|
|
69980
70362
|
return [];
|
|
69981
70363
|
return (rootRun.pendingStageMessages ?? []).filter((entry) => {
|
|
69982
|
-
if (entry.sticky !== true || entry.status !== "queued")
|
|
69983
|
-
return false;
|
|
69984
|
-
const parsed = entry.targetPath === undefined ? undefined : parseWorkflowStageTarget(entry.targetPath);
|
|
69985
|
-
if (parsed === undefined || parsed.rootRunId !== rootRunId)
|
|
69986
|
-
return false;
|
|
69987
70364
|
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)))))
|
|
69988
70365
|
return false;
|
|
69989
|
-
|
|
70366
|
+
if (ended && !receivedByEndedStage.includes(entry.message.id))
|
|
70367
|
+
return false;
|
|
70368
|
+
return stickyEntryTargetsStage(entry, rootRunId, hops, stageId, stageName);
|
|
69990
70369
|
});
|
|
69991
70370
|
}
|
|
70371
|
+
function stickyEntryTargetsStage(entry, rootRunId, hops, stageId, stageName) {
|
|
70372
|
+
if (entry.sticky !== true || entry.status !== "queued")
|
|
70373
|
+
return false;
|
|
70374
|
+
const parsed = entry.targetPath === undefined ? undefined : parseWorkflowStageTarget(entry.targetPath);
|
|
70375
|
+
if (parsed === undefined || parsed.rootRunId !== rootRunId)
|
|
70376
|
+
return false;
|
|
70377
|
+
return stageMatchesPathPattern(parsed.segments, hops, [stageId, stageName]);
|
|
70378
|
+
}
|
|
69992
70379
|
function claimPendingDelivery(store, runId, stageKey, messageId) {
|
|
69993
70380
|
let claims = pendingDeliveryClaims.get(store);
|
|
69994
70381
|
if (claims === undefined) {
|
|
@@ -70052,6 +70439,7 @@ class PoolStageHandoff {
|
|
|
70052
70439
|
owners = new Map;
|
|
70053
70440
|
publications = new Map;
|
|
70054
70441
|
drains = new Map;
|
|
70442
|
+
deliveryIssues = 0;
|
|
70055
70443
|
constructor(backend, store, queue, lease, claimRun) {
|
|
70056
70444
|
this.backend = backend;
|
|
70057
70445
|
this.store = store;
|
|
@@ -70132,11 +70520,23 @@ class PoolStageHandoff {
|
|
|
70132
70520
|
this.drains.set(stage, drain);
|
|
70133
70521
|
try {
|
|
70134
70522
|
await drain;
|
|
70523
|
+
} catch (error) {
|
|
70524
|
+
const current = this.attempts.get(work.id) === attempt && !attempt.fenced;
|
|
70525
|
+
if (current && attempt.sessions.get(stage) === recipient.sessionId && error.message !== STALE)
|
|
70526
|
+
this.recordDeliveryIssue(error);
|
|
70527
|
+
throw error;
|
|
70135
70528
|
} finally {
|
|
70136
70529
|
if (this.drains.get(stage) === drain)
|
|
70137
70530
|
this.drains.delete(stage);
|
|
70138
70531
|
}
|
|
70139
70532
|
}
|
|
70533
|
+
recordDeliveryIssue(error) {
|
|
70534
|
+
this.deliveryIssues++;
|
|
70535
|
+
const drains = this.deliveryIssues === 1 ? "1 stage drain" : `${this.deliveryIssues} stage drains`;
|
|
70536
|
+
this.store.recordRunExecutionState(this.lease.runId, {
|
|
70537
|
+
intercomDeliveryIssue: `${drains} on local workers failed; undelivered messages stay queued and arrive live or on the stage's next drain (last: ${error.message})`
|
|
70538
|
+
});
|
|
70539
|
+
}
|
|
70140
70540
|
async complete(work) {
|
|
70141
70541
|
const root = this.lease.runId;
|
|
70142
70542
|
const result = await this.queue.result(root, work.id);
|
|
@@ -71444,7 +71844,7 @@ var init_pooled_stage_registry = __esm(() => {
|
|
|
71444
71844
|
// dist/builtin/workflows/src/workers/local-pool.ts
|
|
71445
71845
|
import { fork as fork2 } from "node:child_process";
|
|
71446
71846
|
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
71447
|
-
import { resolve as
|
|
71847
|
+
import { resolve as resolve16 } from "node:path";
|
|
71448
71848
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
71449
71849
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
71450
71850
|
import {
|
|
@@ -71562,6 +71962,7 @@ class LocalWorkerPool {
|
|
|
71562
71962
|
works = new Map;
|
|
71563
71963
|
attempts = new Map;
|
|
71564
71964
|
results = new Map;
|
|
71965
|
+
healthEvents = [];
|
|
71565
71966
|
workFailures = new Map;
|
|
71566
71967
|
changed = Promise.withResolvers();
|
|
71567
71968
|
waiting = 0;
|
|
@@ -71619,6 +72020,9 @@ class LocalWorkerPool {
|
|
|
71619
72020
|
if (options.admission && options.rootId === undefined)
|
|
71620
72021
|
workflowResourceAdmission.set(backend, options.admission);
|
|
71621
72022
|
}
|
|
72023
|
+
get health() {
|
|
72024
|
+
return this.healthEvents;
|
|
72025
|
+
}
|
|
71622
72026
|
get workerPids() {
|
|
71623
72027
|
return [...this.pids];
|
|
71624
72028
|
}
|
|
@@ -71674,7 +72078,7 @@ class LocalWorkerPool {
|
|
|
71674
72078
|
async poolJournal() {
|
|
71675
72079
|
const journal = this.backend.journal;
|
|
71676
72080
|
if (this.options.durable === undefined) {
|
|
71677
|
-
if (!(journal instanceof SqlRunJournal) ||
|
|
72081
|
+
if (!(journal instanceof SqlRunJournal) || resolve16(this.options.journalPath) !== resolve16(journal.path) || sqlPoolStorage(journal)?.tables.runs !== "wf_runs")
|
|
71678
72082
|
throw new Error("Local workers require the same default-prefix SQLite journal");
|
|
71679
72083
|
return journal;
|
|
71680
72084
|
}
|
|
@@ -72212,14 +72616,8 @@ class LocalWorkerPool {
|
|
|
72212
72616
|
try {
|
|
72213
72617
|
return await this.dispatch(definition, inputs, options);
|
|
72214
72618
|
} catch (error) {
|
|
72215
|
-
if (
|
|
72216
|
-
|
|
72217
|
-
try {
|
|
72218
|
-
if (this.desiredControl !== "quit" || !findWorkflowGracefulQuit(error))
|
|
72219
|
-
await this.shutdown();
|
|
72220
|
-
} catch (closeError) {
|
|
72221
|
-
throw asWorkflowInfrastructureError(closeError) ?? closeError;
|
|
72222
|
-
}
|
|
72619
|
+
if (this.failure !== undefined || this.coordinatorLost !== undefined)
|
|
72620
|
+
await this.shutdown().catch(() => {});
|
|
72223
72621
|
throw asWorkflowInfrastructureError(error) ?? error;
|
|
72224
72622
|
}
|
|
72225
72623
|
}
|
|
@@ -72287,8 +72685,13 @@ class LocalWorkerPool {
|
|
|
72287
72685
|
try {
|
|
72288
72686
|
for (;; ) {
|
|
72289
72687
|
options.signal?.throwIfAborted();
|
|
72290
|
-
if (this.closed)
|
|
72291
|
-
|
|
72688
|
+
if (!this.results.has(work.id) && (this.closed || this.failure !== undefined)) {
|
|
72689
|
+
const recorded = await this.queue?.result(root, work.id).catch(() => {
|
|
72690
|
+
return;
|
|
72691
|
+
});
|
|
72692
|
+
if (recorded !== undefined)
|
|
72693
|
+
this.results.set(work.id, recorded);
|
|
72694
|
+
}
|
|
72292
72695
|
const result = this.results.get(work.id);
|
|
72293
72696
|
if (result) {
|
|
72294
72697
|
this.completing++;
|
|
@@ -72312,6 +72715,8 @@ class LocalWorkerPool {
|
|
|
72312
72715
|
this.endFailedChild(work, failed);
|
|
72313
72716
|
throw failed;
|
|
72314
72717
|
}
|
|
72718
|
+
if (this.closed)
|
|
72719
|
+
throw this.closedError(root);
|
|
72315
72720
|
if (this.failure)
|
|
72316
72721
|
throw this.failure;
|
|
72317
72722
|
if (this.budgetBlocked)
|
|
@@ -72346,6 +72751,19 @@ class LocalWorkerPool {
|
|
|
72346
72751
|
if (this.reconnectingViews.size > 0 || this.outboxes.size > 0)
|
|
72347
72752
|
this.reconnectStageViews();
|
|
72348
72753
|
}
|
|
72754
|
+
recordHealth(message) {
|
|
72755
|
+
this.healthEvents.push(message);
|
|
72756
|
+
const root = this.root;
|
|
72757
|
+
if (root === undefined || this.store === undefined)
|
|
72758
|
+
return;
|
|
72759
|
+
this.store.recordNotice({
|
|
72760
|
+
id: `local-worker-health:${root}:${this.healthEvents.length}`,
|
|
72761
|
+
runId: root,
|
|
72762
|
+
level: "warning",
|
|
72763
|
+
message: `Local worker pool: ${message}`,
|
|
72764
|
+
createdAt: Date.now()
|
|
72765
|
+
});
|
|
72766
|
+
}
|
|
72349
72767
|
evaluateBudget() {
|
|
72350
72768
|
const decision = this.rootBudget?.checkpoint();
|
|
72351
72769
|
const blocked = decision?.kind === "exhausted" || decision?.kind === "wrap_up";
|
|
@@ -72464,6 +72882,9 @@ class LocalWorkerPool {
|
|
|
72464
72882
|
this.failure ??= new Error(event.error);
|
|
72465
72883
|
this.signalChange();
|
|
72466
72884
|
return;
|
|
72885
|
+
case "health":
|
|
72886
|
+
this.recordHealth(event.message);
|
|
72887
|
+
return;
|
|
72467
72888
|
case "stage-events":
|
|
72468
72889
|
this.stageWatches.get(event.watch)?.({ kind: "events", events: event.events });
|
|
72469
72890
|
this.notice({ kind: "stage-events-applied", watch: event.watch });
|
|
@@ -72684,7 +73105,7 @@ var init_local_pool = __esm(() => {
|
|
|
72684
73105
|
var DEFAULT_POOLED_STAGE_START_MAX_LOAD_PER_CPU = 3;
|
|
72685
73106
|
|
|
72686
73107
|
// dist/builtin/workflows/src/workers/public-pool.ts
|
|
72687
|
-
import { resolve as
|
|
73108
|
+
import { resolve as resolve17 } from "node:path";
|
|
72688
73109
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
|
|
72689
73110
|
function withWorkerSettings(options, settings) {
|
|
72690
73111
|
const configured = settings?.workflows;
|
|
@@ -72744,16 +73165,16 @@ async function activatePublicPool(backend, options) {
|
|
|
72744
73165
|
startupTimeoutMs: undefined,
|
|
72745
73166
|
leaseMs: undefined,
|
|
72746
73167
|
maxLoadPerCpu: undefined,
|
|
72747
|
-
definitions: options.localWorkers.definitions.map((source) => ({ ...source, path:
|
|
73168
|
+
definitions: options.localWorkers.definitions.map((source) => ({ ...source, path: resolve17(cwd, source.path) }))
|
|
72748
73169
|
};
|
|
72749
73170
|
const admission = options.admission && {
|
|
72750
73171
|
...options.admission,
|
|
72751
|
-
...options.admission.userDatabase ? { userDatabase:
|
|
73172
|
+
...options.admission.userDatabase ? { userDatabase: resolve17(cwd, options.admission.userDatabase) } : {}
|
|
72752
73173
|
};
|
|
72753
73174
|
if (durable !== undefined && admission?.userLimits) {
|
|
72754
|
-
if (admission.userDatabase !== undefined && admission.userDatabase !==
|
|
72755
|
-
throw new Error(`admission.userDatabase must be omitted, or ${
|
|
72756
|
-
admission.userDatabase =
|
|
73175
|
+
if (admission.userDatabase !== undefined && admission.userDatabase !== resolve17(poolPath))
|
|
73176
|
+
throw new Error(`admission.userDatabase must be omitted, or ${resolve17(poolPath)}, for a journal that is not a local SQLite file`);
|
|
73177
|
+
admission.userDatabase = resolve17(poolPath);
|
|
72757
73178
|
}
|
|
72758
73179
|
const sources = localWorkers && await Promise.all(localWorkers.definitions.map(async (source) => ({ ...source, digest: await definitionDigest(source.path) })));
|
|
72759
73180
|
const resources = JSON.parse(JSON.stringify({
|
|
@@ -73359,6 +73780,7 @@ function createTaskPrimitive(runtime) {
|
|
|
73359
73780
|
}
|
|
73360
73781
|
})();
|
|
73361
73782
|
const stageMeta = stage.__modelFallbackMeta();
|
|
73783
|
+
stage.__releaseIfIdle?.();
|
|
73362
73784
|
return {
|
|
73363
73785
|
name,
|
|
73364
73786
|
stageName: name,
|
|
@@ -75829,6 +76251,8 @@ function createTrackedStageCaller(input) {
|
|
|
75829
76251
|
await runtime.releaseLiveHandle().catch(() => {});
|
|
75830
76252
|
}
|
|
75831
76253
|
runtime.state.sessionCheckpointed = finalizationError === undefined;
|
|
76254
|
+
if (runtime.state.contextUnreachable)
|
|
76255
|
+
runtime.stageRegistry.releaseIdle(runtime.runId, runtime.stageId);
|
|
75832
76256
|
input.limiter.release();
|
|
75833
76257
|
if (finalizationError !== undefined)
|
|
75834
76258
|
throwFinalizationError(finalizationError.error);
|
|
@@ -75992,7 +76416,8 @@ function createStageContext(input) {
|
|
|
75992
76416
|
recordStageNotice({ kind: "abort", to: "interrupted" });
|
|
75993
76417
|
await innerCtx.abort();
|
|
75994
76418
|
},
|
|
75995
|
-
__modelFallbackMeta: () => innerCtx.__modelFallbackMeta()
|
|
76419
|
+
__modelFallbackMeta: () => innerCtx.__modelFallbackMeta(),
|
|
76420
|
+
__releaseIfIdle: () => runtime.stageRegistry.releaseIdle(runtime.runId, runtime.stageId)
|
|
75996
76421
|
};
|
|
75997
76422
|
}
|
|
75998
76423
|
|
|
@@ -77681,10 +78106,10 @@ function isRetryableSameCandidateFailure(error) {
|
|
|
77681
78106
|
return isTransientExtensionStartupFailure(error);
|
|
77682
78107
|
return isRetryableSameModelFailure(error);
|
|
77683
78108
|
}
|
|
77684
|
-
function mayAdvanceToNextCandidate(error) {
|
|
78109
|
+
function mayAdvanceToNextCandidate(error, options) {
|
|
77685
78110
|
if (isExtensionStartupFailure(error))
|
|
77686
78111
|
return isTransientExtensionStartupFailure(error);
|
|
77687
|
-
return isRetryableModelFailure(error);
|
|
78112
|
+
return isRetryableModelFailure(error, options);
|
|
77688
78113
|
}
|
|
77689
78114
|
function hasMeaningfulUsage(usage) {
|
|
77690
78115
|
if (usage === undefined)
|
|
@@ -77776,6 +78201,7 @@ class StageSessionController {
|
|
|
77776
78201
|
structuredOutputExecutionFailureValue;
|
|
77777
78202
|
structuredOutputCycleAttemptMark;
|
|
77778
78203
|
unsubscribeTerminateWatcher;
|
|
78204
|
+
unbindAbortSignal;
|
|
77779
78205
|
sessionInternalRetries = 0;
|
|
77780
78206
|
unresolvedContextOverflowMessage;
|
|
77781
78207
|
generationSealed = false;
|
|
@@ -78265,6 +78691,8 @@ class StageSessionController {
|
|
|
78265
78691
|
this.pendingListeners.clear();
|
|
78266
78692
|
this.unsubscribeTerminateWatcher?.();
|
|
78267
78693
|
this.unsubscribeTerminateWatcher = undefined;
|
|
78694
|
+
this.unbindAbortSignal?.();
|
|
78695
|
+
this.unbindAbortSignal = undefined;
|
|
78268
78696
|
this.terminatingToolCallIds.clear();
|
|
78269
78697
|
this.messageAdmission.dispose();
|
|
78270
78698
|
this.deliveryActivity.dispose();
|
|
@@ -78359,8 +78787,10 @@ class StageSessionController {
|
|
|
78359
78787
|
};
|
|
78360
78788
|
if (signal.aborted)
|
|
78361
78789
|
onAbort();
|
|
78362
|
-
else
|
|
78790
|
+
else {
|
|
78363
78791
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
78792
|
+
this.unbindAbortSignal = () => signal.removeEventListener("abort", onAbort);
|
|
78793
|
+
}
|
|
78364
78794
|
}
|
|
78365
78795
|
markAbort(reason) {
|
|
78366
78796
|
this.abortGeneration += 1;
|
|
@@ -78387,6 +78817,9 @@ class StageSessionController {
|
|
|
78387
78817
|
state.controller.abort(reason);
|
|
78388
78818
|
this.thrownErrorRetryStates.clear();
|
|
78389
78819
|
}
|
|
78820
|
+
fallbackFailureOptions() {
|
|
78821
|
+
return { fallbackOnContentFilter: this.retrySettings()?.fallbackOnContentFilter !== false };
|
|
78822
|
+
}
|
|
78390
78823
|
retrySettings() {
|
|
78391
78824
|
const managers = [
|
|
78392
78825
|
this.sessionSettingsManager,
|
|
@@ -78661,7 +79094,7 @@ class StageSessionController {
|
|
|
78661
79094
|
const errorSettingsManager = retrySettingsManagerFromError(error);
|
|
78662
79095
|
if (errorSettingsManager !== undefined)
|
|
78663
79096
|
this.sessionSettingsManager = errorSettingsManager;
|
|
78664
|
-
const retryableFailure = isRetryableModelFailure(error);
|
|
79097
|
+
const retryableFailure = isRetryableModelFailure(error, this.fallbackFailureOptions());
|
|
78665
79098
|
const sameCandidateRetryable = isRetryableSameModelFailure(error) && !isUnresolvedContextOverflowFailure(error);
|
|
78666
79099
|
recordProviderRateLimit(this.currentProvider(), error, modelIdForModel(this.currentModelValue()));
|
|
78667
79100
|
if (this.hasExplicitModelFallbackConfig && isProviderRateLimit(error)) {
|
|
@@ -79286,7 +79719,7 @@ class StageSessionController {
|
|
|
79286
79719
|
}
|
|
79287
79720
|
throw new WorkflowPromptModelFailure(terminalFailure);
|
|
79288
79721
|
} catch (err) {
|
|
79289
|
-
if (this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err)) {
|
|
79722
|
+
if (this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err, this.fallbackFailureOptions())) {
|
|
79290
79723
|
this.artifactCapture.settleAttempt(true);
|
|
79291
79724
|
const usage = this.takeAttemptUsage(resumedSession);
|
|
79292
79725
|
this.modelAttempts.push({
|
|
@@ -79308,7 +79741,7 @@ class StageSessionController {
|
|
|
79308
79741
|
...usage === undefined ? {} : { usage },
|
|
79309
79742
|
error: message
|
|
79310
79743
|
});
|
|
79311
|
-
if (this.opts.signal?.aborted || !isRetryableModelFailure(err)) {
|
|
79744
|
+
if (this.opts.signal?.aborted || !isRetryableModelFailure(err, this.fallbackFailureOptions())) {
|
|
79312
79745
|
this.modelWarnings.push(...this.pendingFallbackWarnings);
|
|
79313
79746
|
this.pendingFallbackWarnings.length = 0;
|
|
79314
79747
|
this.notifyModelFallbackMetaChange();
|
|
@@ -79357,7 +79790,7 @@ class StageSessionController {
|
|
|
79357
79790
|
throw err;
|
|
79358
79791
|
const message = errorMessage4(err);
|
|
79359
79792
|
const terminalStageDelivery = isWorkflowPendingStageDeliveryFailure(err);
|
|
79360
|
-
if (!terminalStageDelivery && this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err)) {
|
|
79793
|
+
if (!terminalStageDelivery && this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err, this.fallbackFailureOptions())) {
|
|
79361
79794
|
this.recordSuccessfulAttempt(candidate);
|
|
79362
79795
|
return "handled";
|
|
79363
79796
|
}
|
|
@@ -79374,7 +79807,7 @@ class StageSessionController {
|
|
|
79374
79807
|
if (walk !== undefined) {
|
|
79375
79808
|
this.recordCandidateFailure(walk, index, err, candidates);
|
|
79376
79809
|
nextIndex = undefined;
|
|
79377
|
-
if (!this.opts.signal?.aborted && !this.startupWait.signal.aborted && !terminalStageDelivery && mayAdvanceToNextCandidate(err)) {
|
|
79810
|
+
if (!this.opts.signal?.aborted && !this.startupWait.signal.aborted && !terminalStageDelivery && mayAdvanceToNextCandidate(err, this.fallbackFailureOptions())) {
|
|
79378
79811
|
try {
|
|
79379
79812
|
nextIndex = await this.selectReadyCandidate(candidates, walk, index + 1);
|
|
79380
79813
|
} catch (error) {
|
|
@@ -79387,7 +79820,7 @@ class StageSessionController {
|
|
|
79387
79820
|
}
|
|
79388
79821
|
}
|
|
79389
79822
|
}
|
|
79390
|
-
if (this.opts.signal?.aborted || this.startupWait.signal.aborted || terminalStageDelivery || !mayAdvanceToNextCandidate(err) || nextIndex === undefined) {
|
|
79823
|
+
if (this.opts.signal?.aborted || this.startupWait.signal.aborted || terminalStageDelivery || !mayAdvanceToNextCandidate(err, this.fallbackFailureOptions()) || nextIndex === undefined) {
|
|
79391
79824
|
await this.settleFailedCandidate();
|
|
79392
79825
|
return "throw";
|
|
79393
79826
|
}
|
|
@@ -79482,7 +79915,7 @@ var init_stage_runner_controller = __esm(() => {
|
|
|
79482
79915
|
// dist/builtin/workflows/src/runs/foreground/stage-runner-output.ts
|
|
79483
79916
|
import { createHash as createHash5 } from "node:crypto";
|
|
79484
79917
|
import { mkdir as mkdir7, writeFile as writeFile10 } from "node:fs/promises";
|
|
79485
|
-
import { basename as basename5, dirname as
|
|
79918
|
+
import { basename as basename5, dirname as dirname17, isAbsolute as isAbsolute11, join as join32, resolve as resolve18 } from "node:path";
|
|
79486
79919
|
function normalizeMaxOutput2(maxOutput) {
|
|
79487
79920
|
return {
|
|
79488
79921
|
bytes: maxOutput?.bytes ?? DEFAULT_MAX_OUTPUT_BYTES,
|
|
@@ -79545,7 +79978,7 @@ function formatByteSize(bytes) {
|
|
|
79545
79978
|
}
|
|
79546
79979
|
function transcriptPath(runId, outputPath) {
|
|
79547
79980
|
const digest = createHash5("sha256").update(outputPath).digest("hex").slice(0, 16);
|
|
79548
|
-
return
|
|
79981
|
+
return join32(workflowArtifactRunPath(runId), "transcripts", `${digest}-${basename5(outputPath)}.transcript.md`);
|
|
79549
79982
|
}
|
|
79550
79983
|
function appendContent(lines, content) {
|
|
79551
79984
|
if (typeof content === "string") {
|
|
@@ -79590,7 +80023,7 @@ function renderTranscript(messages, fallbackOutput) {
|
|
|
79590
80023
|
`);
|
|
79591
80024
|
}
|
|
79592
80025
|
function savedOutputReference(outputPath, fullOutput, transcriptPathValue, transcriptText, transcriptAvailable, warning) {
|
|
79593
|
-
const absolutePath =
|
|
80026
|
+
const absolutePath = resolve18(outputPath);
|
|
79594
80027
|
const bytes = Buffer.byteLength(fullOutput, "utf8");
|
|
79595
80028
|
const lines = countLines(fullOutput);
|
|
79596
80029
|
const transcriptBytes = Buffer.byteLength(transcriptText, "utf8");
|
|
@@ -79605,8 +80038,8 @@ function resolveOutputPath(output, runtimeCwd, requestedCwd) {
|
|
|
79605
80038
|
return;
|
|
79606
80039
|
if (isAbsolute11(output))
|
|
79607
80040
|
return output;
|
|
79608
|
-
const baseCwd = requestedCwd === undefined ? runtimeCwd : isAbsolute11(requestedCwd) ? requestedCwd :
|
|
79609
|
-
return
|
|
80041
|
+
const baseCwd = requestedCwd === undefined ? runtimeCwd : isAbsolute11(requestedCwd) ? requestedCwd : resolve18(runtimeCwd, requestedCwd);
|
|
80042
|
+
return resolve18(baseCwd, output);
|
|
79610
80043
|
}
|
|
79611
80044
|
function degenerateWarning(fullOutput) {
|
|
79612
80045
|
return fullOutput.trim().length === 0 ? "WARNING: the stage artifact is empty; search the companion transcript for this stage's work." : undefined;
|
|
@@ -79665,7 +80098,7 @@ async function finalizePromptOutput(fullOutput, outputOptions, runtimeCwd, runId
|
|
|
79665
80098
|
const transcriptFile = transcriptPath(runId, outputPath);
|
|
79666
80099
|
const transcript = renderTranscript(messages, fullOutput);
|
|
79667
80100
|
try {
|
|
79668
|
-
await mkdir7(
|
|
80101
|
+
await mkdir7(dirname17(outputPath), { recursive: true });
|
|
79669
80102
|
await writeFile10(outputPath, fullOutput, "utf8");
|
|
79670
80103
|
} catch (err) {
|
|
79671
80104
|
return `${displayOutput}
|
|
@@ -79677,7 +80110,7 @@ ${err instanceof Error ? err.message : String(err)}`;
|
|
|
79677
80110
|
let transcriptError;
|
|
79678
80111
|
try {
|
|
79679
80112
|
await ensureWorkflowArtifactRunDirectory(runId);
|
|
79680
|
-
await mkdir7(
|
|
80113
|
+
await mkdir7(dirname17(transcriptFile), { recursive: true });
|
|
79681
80114
|
await writeFile10(transcriptFile, transcript, "utf8");
|
|
79682
80115
|
transcriptAvailable = true;
|
|
79683
80116
|
} catch (err) {
|
|
@@ -80188,7 +80621,8 @@ function createWorkflowStageFactory(input) {
|
|
|
80188
80621
|
stageFinalized: false,
|
|
80189
80622
|
sessionCheckpointed: false,
|
|
80190
80623
|
skippedForParallelFailFast: false,
|
|
80191
|
-
stageControlDropped: false
|
|
80624
|
+
stageControlDropped: false,
|
|
80625
|
+
contextUnreachable: false
|
|
80192
80626
|
};
|
|
80193
80627
|
const activeAskUserQuestionCalls = new Set;
|
|
80194
80628
|
const hasActiveAskUserQuestion = () => activeAskUserQuestionCalls.size > 0 || state.activeAskUserQuestionAnonymousCalls > 0;
|
|
@@ -80434,9 +80868,18 @@ function createWorkflowStageFactory(input) {
|
|
|
80434
80868
|
hasContinuation: input.opts.continuation !== undefined,
|
|
80435
80869
|
hasScopedParents: stageFailFastScope?.parentIds !== undefined
|
|
80436
80870
|
});
|
|
80437
|
-
|
|
80871
|
+
const context = createStageContext({ runtime, runTrackedStageCall });
|
|
80872
|
+
unreachableStageContexts.register(context, releaseWhenUnreachable(state, input.stageRegistry, input.runId, stageId));
|
|
80873
|
+
return context;
|
|
80874
|
+
};
|
|
80875
|
+
}
|
|
80876
|
+
function releaseWhenUnreachable(state, registry, runId, stageId) {
|
|
80877
|
+
return () => {
|
|
80878
|
+
state.contextUnreachable = true;
|
|
80879
|
+
registry.releaseIdle(runId, stageId);
|
|
80438
80880
|
};
|
|
80439
80881
|
}
|
|
80882
|
+
var unreachableStageContexts;
|
|
80440
80883
|
var init_executor_stage_factory = __esm(() => {
|
|
80441
80884
|
init_intercom_group();
|
|
80442
80885
|
init_pending_stage_route_readiness();
|
|
@@ -80451,6 +80894,7 @@ var init_executor_stage_factory = __esm(() => {
|
|
|
80451
80894
|
init_pending_stage_delivery2();
|
|
80452
80895
|
init_stage_runner();
|
|
80453
80896
|
init_stage_runner_session();
|
|
80897
|
+
unreachableStageContexts = new FinalizationRegistry((release) => release());
|
|
80454
80898
|
});
|
|
80455
80899
|
|
|
80456
80900
|
// dist/builtin/workflows/src/engine/runtime.ts
|
|
@@ -80792,7 +81236,7 @@ async function executeRun(def, resolvedInputs, opts) {
|
|
|
80792
81236
|
const ownsCancellationRegistration = opts.signal === undefined && opts.cancellation !== undefined;
|
|
80793
81237
|
const tracker = new GraphFrontierTracker;
|
|
80794
81238
|
const inputConcurrency = resolveInputConcurrency(def.inputs, resolvedInputs);
|
|
80795
|
-
const inputRuntimeDefaults = resolveInputRuntimeDefaults(def, resolvedInputs), gitWorktreeSetupCacheOwner = createGitWorktreeSetupCacheOwner(opts.gitWorktreeSetupCache);
|
|
81239
|
+
const inputRuntimeDefaults = resolveInputRuntimeDefaults(def, resolvedInputs), gitWorktreeSetupCacheOwner = createGitWorktreeSetupCacheOwner(opts.gitWorktreeSetupCache, runId);
|
|
80796
81240
|
const gitWorktreeSetupCache = gitWorktreeSetupCacheOwner.cache;
|
|
80797
81241
|
const workflowInvocationCwd = opts.cwd ?? process.cwd();
|
|
80798
81242
|
let workflowCwd;
|
|
@@ -81891,8 +82335,8 @@ var init_workflows_host_module = __esm(() => {
|
|
|
81891
82335
|
|
|
81892
82336
|
// dist/builtin/workflows/src/workers/pool-journal.ts
|
|
81893
82337
|
import { createHash as createHash6, randomUUID as randomUUID22 } from "node:crypto";
|
|
81894
|
-
import { existsSync as existsSync9, mkdirSync as
|
|
81895
|
-
import { basename as basename6, dirname as
|
|
82338
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
|
|
82339
|
+
import { basename as basename6, dirname as dirname18, join as join33, resolve as resolve19 } from "node:path";
|
|
81896
82340
|
import { storageSelectionKey as storageSelectionKey2 } from "@bastani/atomic/storage";
|
|
81897
82341
|
function encodeOption(value2) {
|
|
81898
82342
|
if (typeof value2 === "bigint")
|
|
@@ -81914,7 +82358,7 @@ function isReopenableSelection(selection) {
|
|
|
81914
82358
|
}
|
|
81915
82359
|
function encodeSelection(selection) {
|
|
81916
82360
|
if (selection.kind === "sqlite")
|
|
81917
|
-
return JSON.stringify({ ...selection, path:
|
|
82361
|
+
return JSON.stringify({ ...selection, path: resolve19(selection.path) });
|
|
81918
82362
|
return JSON.stringify({
|
|
81919
82363
|
...selection,
|
|
81920
82364
|
...selection.options === undefined ? {} : {
|
|
@@ -81946,7 +82390,7 @@ function openDurableJournal(durable) {
|
|
|
81946
82390
|
return openSelectedRunJournal(decodeSelection(durable.selection), durable.agentDir);
|
|
81947
82391
|
}
|
|
81948
82392
|
function poolStateDirectory(agentDir) {
|
|
81949
|
-
return
|
|
82393
|
+
return join33(dirname18(agentDir), "workflows", "pool");
|
|
81950
82394
|
}
|
|
81951
82395
|
function databaseIdentity(selection, agentDir) {
|
|
81952
82396
|
return storageSelectionKey2(selection.kind === "adapter" ? { ...selection, secrets: undefined } : selection, agentDir);
|
|
@@ -81954,16 +82398,16 @@ function databaseIdentity(selection, agentDir) {
|
|
|
81954
82398
|
function coordinationPath(durable) {
|
|
81955
82399
|
const selection = decodeSelection(durable.selection);
|
|
81956
82400
|
const identity = createHash6("sha256").update(databaseIdentity(selection, durable.agentDir)).digest("hex");
|
|
81957
|
-
return
|
|
82401
|
+
return join33(poolStateDirectory(durable.agentDir), `${identity.slice(0, 24)}.sqlite`);
|
|
81958
82402
|
}
|
|
81959
82403
|
function coordinationDatabase(durable) {
|
|
81960
82404
|
return basename6(coordinationPath(durable), ".sqlite");
|
|
81961
82405
|
}
|
|
81962
82406
|
function openCoordinationJournal(path) {
|
|
81963
|
-
const file =
|
|
82407
|
+
const file = resolve19(path);
|
|
81964
82408
|
let opened = coordinationJournals.get(file);
|
|
81965
82409
|
if (opened === undefined) {
|
|
81966
|
-
|
|
82410
|
+
mkdirSync9(dirname18(file), { recursive: true });
|
|
81967
82411
|
opened = resolveRunJournal({ journal: { path: file } });
|
|
81968
82412
|
coordinationJournals.set(file, opened);
|
|
81969
82413
|
opened.catch(() => {
|
|
@@ -81980,20 +82424,20 @@ function poolJournalFor(journal) {
|
|
|
81980
82424
|
return coordinations.get(journal) ?? journal;
|
|
81981
82425
|
}
|
|
81982
82426
|
function poolHostId(agentDir) {
|
|
81983
|
-
const file =
|
|
82427
|
+
const file = join33(poolStateDirectory(agentDir), "host-id");
|
|
81984
82428
|
try {
|
|
81985
|
-
const existing =
|
|
82429
|
+
const existing = readFileSync9(file, "utf8").trim();
|
|
81986
82430
|
if (existing !== "")
|
|
81987
82431
|
return existing;
|
|
81988
82432
|
} catch {}
|
|
81989
|
-
|
|
82433
|
+
mkdirSync9(dirname18(file), { recursive: true });
|
|
81990
82434
|
const id = randomUUID22();
|
|
81991
82435
|
try {
|
|
81992
|
-
|
|
82436
|
+
writeFileSync7(file, `${id}
|
|
81993
82437
|
`, { flag: "wx" });
|
|
81994
82438
|
return id;
|
|
81995
82439
|
} catch {
|
|
81996
|
-
return
|
|
82440
|
+
return readFileSync9(file, "utf8").trim();
|
|
81997
82441
|
}
|
|
81998
82442
|
}
|
|
81999
82443
|
async function poolJournalOf(journal) {
|
|
@@ -82794,9 +83238,9 @@ var init_sdk_owner_release = __esm(() => {
|
|
|
82794
83238
|
|
|
82795
83239
|
// dist/builtin/workflows/src/durable/sdk-recovery-scope.ts
|
|
82796
83240
|
import { realpathSync as realpathSync5 } from "node:fs";
|
|
82797
|
-
import { resolve as
|
|
83241
|
+
import { resolve as resolve20 } from "node:path";
|
|
82798
83242
|
function canonicalDirectory(path) {
|
|
82799
|
-
const absolute =
|
|
83243
|
+
const absolute = resolve20(path);
|
|
82800
83244
|
try {
|
|
82801
83245
|
const canonical = realpathSync5.native(absolute);
|
|
82802
83246
|
return process.platform === "win32" ? canonical.toLowerCase() : canonical;
|
|
@@ -84742,6 +85186,8 @@ function summaryRows(detail, now) {
|
|
|
84742
85186
|
rows.push(["last progress", formatTime(detail.lastProgressAt)]);
|
|
84743
85187
|
if (detail.dependencyError !== undefined)
|
|
84744
85188
|
rows.push(["dependency", detail.dependencyError]);
|
|
85189
|
+
if (detail.intercomDeliveryIssue !== undefined)
|
|
85190
|
+
rows.push(["intercom", detail.intercomDeliveryIssue]);
|
|
84745
85191
|
if (detail.controlPersistence !== undefined) {
|
|
84746
85192
|
rows.push(["control", `${detail.controlRequestedStatus} (${detail.controlPersistence})`]);
|
|
84747
85193
|
}
|
|
@@ -96480,7 +96926,8 @@ function observeRunExecution(run, now = Date.now()) {
|
|
|
96480
96926
|
dependencyError: run.dependencyError,
|
|
96481
96927
|
lastProgressAt,
|
|
96482
96928
|
controlPersistence: run.controlPersistence,
|
|
96483
|
-
controlRequestedStatus: run.controlRequestedStatus
|
|
96929
|
+
controlRequestedStatus: run.controlRequestedStatus,
|
|
96930
|
+
...run.intercomDeliveryIssue === undefined ? {} : { intercomDeliveryIssue: run.intercomDeliveryIssue }
|
|
96484
96931
|
};
|
|
96485
96932
|
}
|
|
96486
96933
|
|
|
@@ -97064,7 +97511,7 @@ function installCompactionHook(api, store) {
|
|
|
97064
97511
|
});
|
|
97065
97512
|
}
|
|
97066
97513
|
// dist/builtin/workflows/src/extension/config-loader.ts
|
|
97067
|
-
import { isAbsolute as isAbsolute12, join as
|
|
97514
|
+
import { isAbsolute as isAbsolute12, join as join34 } from "node:path";
|
|
97068
97515
|
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME2, getAgentDir as getAgentDir6 } from "@bastani/atomic";
|
|
97069
97516
|
// dist/builtin/workflows/src/extension/lifecycle-notifications.ts
|
|
97070
97517
|
init_returned_run_status();
|
|
@@ -97981,7 +98428,7 @@ function resolveWorkflowPaths(workflows, baseDir) {
|
|
|
97981
98428
|
}
|
|
97982
98429
|
return Object.fromEntries(Object.entries(workflows).map(([name, entry]) => [
|
|
97983
98430
|
name,
|
|
97984
|
-
isAbsolute12(entry.path) ? entry.path :
|
|
98431
|
+
isAbsolute12(entry.path) ? entry.path : join34(baseDir, entry.path)
|
|
97985
98432
|
]));
|
|
97986
98433
|
}
|
|
97987
98434
|
function workflowAgentDirs(opts) {
|
|
@@ -97989,12 +98436,12 @@ function workflowAgentDirs(opts) {
|
|
|
97989
98436
|
return opts.agentDirs;
|
|
97990
98437
|
if (opts.homeDir !== undefined) {
|
|
97991
98438
|
const homeDir = opts.homeDir;
|
|
97992
|
-
return [
|
|
98439
|
+
return [join34(homeDir, CONFIG_DIR_NAME2, "agent")];
|
|
97993
98440
|
}
|
|
97994
98441
|
return [getAgentDir6()];
|
|
97995
98442
|
}
|
|
97996
98443
|
function toScopedDiscoveryConfig(globalConfig, projectConfig, opts) {
|
|
97997
|
-
const globalBase = opts.agentDir ?? (opts.homeDir === undefined ? getAgentDir6() :
|
|
98444
|
+
const globalBase = opts.agentDir ?? (opts.homeDir === undefined ? getAgentDir6() : join34(opts.homeDir, CONFIG_DIR_NAME2, "agent"));
|
|
97998
98445
|
const projectBase = opts.projectRoot;
|
|
97999
98446
|
const result = {};
|
|
98000
98447
|
const projectWorkflows = resolveWorkflowPaths(projectConfig?.workflows, projectBase);
|
|
@@ -98004,7 +98451,7 @@ function toScopedDiscoveryConfig(globalConfig, projectConfig, opts) {
|
|
|
98004
98451
|
if (hasWorkflows(globalConfig)) {
|
|
98005
98452
|
const projectKeys = new Set(Object.keys(projectWorkflows ?? {}));
|
|
98006
98453
|
const globalEntries = Object.entries(globalConfig.workflows).filter(([name]) => !projectKeys.has(name)).map(([name, entry]) => {
|
|
98007
|
-
return [name, isAbsolute12(entry.path) ? entry.path :
|
|
98454
|
+
return [name, isAbsolute12(entry.path) ? entry.path : join34(globalBase, entry.path)];
|
|
98008
98455
|
});
|
|
98009
98456
|
if (globalEntries.length > 0) {
|
|
98010
98457
|
result.globalWorkflows = Object.fromEntries(globalEntries);
|
|
@@ -98015,8 +98462,8 @@ function toScopedDiscoveryConfig(globalConfig, projectConfig, opts) {
|
|
|
98015
98462
|
async function loadWorkflowConfig(opts = {}) {
|
|
98016
98463
|
const projectRoot = opts.projectRoot ?? process.cwd();
|
|
98017
98464
|
const diagnostics = [];
|
|
98018
|
-
const globalCandidates = workflowAgentDirs(opts).map((agentDir) =>
|
|
98019
|
-
const projectCandidates = [
|
|
98465
|
+
const globalCandidates = workflowAgentDirs(opts).map((agentDir) => join34(agentDir, "extensions", "workflow", "config.json"));
|
|
98466
|
+
const projectCandidates = [join34(projectRoot, CONFIG_DIR_NAME2, "extensions", "workflow", "config.json")];
|
|
98020
98467
|
let globalConfig = null;
|
|
98021
98468
|
for (let i = globalCandidates.length - 1;i >= 0; i--) {
|
|
98022
98469
|
const globalPath = globalCandidates[i];
|
|
@@ -99708,7 +100155,7 @@ init_possible_stages();
|
|
|
99708
100155
|
import { registerExtensionContextRetirementEffect as registerExtensionContextRetirementEffect2 } from "@bastani/atomic";
|
|
99709
100156
|
|
|
99710
100157
|
// dist/builtin/workflows/src/extension/discovery.ts
|
|
99711
|
-
import { join as
|
|
100158
|
+
import { join as join36 } from "node:path";
|
|
99712
100159
|
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME3, getAgentDir as getAgentDir7 } from "@bastani/atomic";
|
|
99713
100160
|
|
|
99714
100161
|
// dist/builtin/workflows/builtin/index.ts
|
|
@@ -99729,12 +100176,12 @@ init_registry();
|
|
|
99729
100176
|
// dist/builtin/workflows/src/extension/discovery-loaders.ts
|
|
99730
100177
|
init_workflow_module_loader();
|
|
99731
100178
|
import { readdir as readdir2, stat as stat2 } from "node:fs/promises";
|
|
99732
|
-
import { extname, isAbsolute as isAbsolute13, join as
|
|
100179
|
+
import { extname, isAbsolute as isAbsolute13, join as join35, resolve as resolve21 } from "node:path";
|
|
99733
100180
|
async function scanWorkflowDir(dir) {
|
|
99734
100181
|
try {
|
|
99735
100182
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
99736
100183
|
const WORKFLOW_EXTS = new Set([".ts", ".js", ".mjs", ".cjs"]);
|
|
99737
|
-
return entries.filter((e) => e.isFile() && WORKFLOW_EXTS.has(extname(e.name))).map((e) =>
|
|
100184
|
+
return entries.filter((e) => e.isFile() && WORKFLOW_EXTS.has(extname(e.name))).map((e) => join35(dir, e.name)).sort();
|
|
99738
100185
|
} catch {
|
|
99739
100186
|
return null;
|
|
99740
100187
|
}
|
|
@@ -99773,7 +100220,7 @@ async function loadFromPaths(pathsOrMap, kind, baseCwd, diagnostics, getWorkflow
|
|
|
99773
100220
|
const all = [];
|
|
99774
100221
|
const entries = Array.isArray(pathsOrMap) ? pathsOrMap.map((p) => ({ rawPath: p })) : Object.entries(pathsOrMap).map(([name, p]) => ({ rawPath: p, configuredName: name }));
|
|
99775
100222
|
for (const { rawPath, configuredName } of entries) {
|
|
99776
|
-
const absPath = isAbsolute13(rawPath) ? rawPath :
|
|
100223
|
+
const absPath = isAbsolute13(rawPath) ? rawPath : resolve21(baseCwd, rawPath);
|
|
99777
100224
|
let pathStats;
|
|
99778
100225
|
try {
|
|
99779
100226
|
pathStats = await stat2(absPath);
|
|
@@ -99804,7 +100251,7 @@ function workflowAgentDirs2(options) {
|
|
|
99804
100251
|
return options.agentDirs;
|
|
99805
100252
|
if (options?.homeDir !== undefined) {
|
|
99806
100253
|
const homeDir = options.homeDir;
|
|
99807
|
-
return [
|
|
100254
|
+
return [join36(homeDir, CONFIG_DIR_NAME3, "agent")];
|
|
99808
100255
|
}
|
|
99809
100256
|
return [getAgentDir7()];
|
|
99810
100257
|
}
|
|
@@ -99960,7 +100407,7 @@ async function discoverWorkflows(options) {
|
|
|
99960
100407
|
registry = await applyBatch(candidates, registry, sources, diagnostics);
|
|
99961
100408
|
}
|
|
99962
100409
|
}
|
|
99963
|
-
for (const dir of [
|
|
100410
|
+
for (const dir of [join36(cwd, CONFIG_DIR_NAME3, "workflows")]) {
|
|
99964
100411
|
const candidates = await loadFromDir(dir, "project-local", diagnostics, getWorkflowHostModules);
|
|
99965
100412
|
registry = await applyBatch(candidates, registry, sources, diagnostics);
|
|
99966
100413
|
}
|
|
@@ -99972,7 +100419,7 @@ async function discoverWorkflows(options) {
|
|
|
99972
100419
|
registry = await applyBatch(candidates, registry, sources, diagnostics);
|
|
99973
100420
|
}
|
|
99974
100421
|
}
|
|
99975
|
-
for (const dir of agentDirs.map((agentDir) =>
|
|
100422
|
+
for (const dir of agentDirs.map((agentDir) => join36(agentDir, "workflows")).reverse()) {
|
|
99976
100423
|
const candidates = await loadFromDir(dir, "user-global", diagnostics, getWorkflowHostModules);
|
|
99977
100424
|
registry = await applyBatch(candidates, registry, sources, diagnostics);
|
|
99978
100425
|
}
|
|
@@ -100847,17 +101294,17 @@ function createExtensionRuntime(opts = {}) {
|
|
|
100847
101294
|
|
|
100848
101295
|
// dist/builtin/workflows/src/extension/status-writer.ts
|
|
100849
101296
|
import { mkdir as mkdir8, rename as rename2, writeFile as writeFile11 } from "node:fs/promises";
|
|
100850
|
-
import { dirname as
|
|
101297
|
+
import { dirname as dirname19, join as join37 } from "node:path";
|
|
100851
101298
|
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME4 } from "@bastani/atomic";
|
|
100852
|
-
var DEFAULT_STATUS_SUBPATH =
|
|
101299
|
+
var DEFAULT_STATUS_SUBPATH = join37(CONFIG_DIR_NAME4, "workflows", "status.json");
|
|
100853
101300
|
function resolveStatusFilePath(config, opts = {}) {
|
|
100854
101301
|
if (config.statusFilePath)
|
|
100855
101302
|
return config.statusFilePath;
|
|
100856
101303
|
const root = opts.projectRoot ?? process.cwd();
|
|
100857
|
-
return
|
|
101304
|
+
return join37(root, DEFAULT_STATUS_SUBPATH);
|
|
100858
101305
|
}
|
|
100859
101306
|
async function atomicWriteJson(path, content) {
|
|
100860
|
-
const dir =
|
|
101307
|
+
const dir = dirname19(path);
|
|
100861
101308
|
await mkdir8(dir, { recursive: true });
|
|
100862
101309
|
const tmpPath = `${path}.tmp`;
|
|
100863
101310
|
await writeFile11(tmpPath, content, "utf8");
|
|
@@ -104999,6 +105446,8 @@ function renderStatusToolContent(result) {
|
|
|
104999
105446
|
}
|
|
105000
105447
|
if (run.dependencyError !== undefined)
|
|
105001
105448
|
lines.push(` dependency: ${run.dependencyError}`);
|
|
105449
|
+
if (run.intercomDeliveryIssue !== undefined)
|
|
105450
|
+
lines.push(` Intercom delivery: ${run.intercomDeliveryIssue}`);
|
|
105002
105451
|
if (run.controlPersistence !== undefined) {
|
|
105003
105452
|
lines.push(` control: ${run.controlRequestedStatus} (${run.controlPersistence})`);
|
|
105004
105453
|
}
|