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