@ai-hero/sandcastle 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -48
- package/dist/{chunk-72UVAC7B.js → chunk-62WN33RK.js} +10 -5
- package/dist/chunk-62WN33RK.js.map +1 -0
- package/dist/{chunk-NSFQW6ML.js → chunk-CP3TYXZA.js} +3 -3
- package/dist/{chunk-NSFQW6ML.js.map → chunk-CP3TYXZA.js.map} +1 -1
- package/dist/{chunk-52CIJF45.js → chunk-DJRHWPEH.js} +3 -3
- package/dist/{chunk-52CIJF45.js.map → chunk-DJRHWPEH.js.map} +1 -1
- package/dist/{chunk-5VM5QZ26.js → chunk-VOG34SRF.js} +72 -48
- package/dist/{chunk-5VM5QZ26.js.map → chunk-VOG34SRF.js.map} +1 -1
- package/dist/index.d.ts +102 -17
- package/dist/index.js +199 -55
- package/dist/index.js.map +1 -1
- package/dist/main.js +6 -6
- package/dist/main.js.map +1 -1
- package/dist/sandboxes/docker.d.ts +1 -1
- package/dist/sandboxes/docker.js +2 -2
- package/dist/sandboxes/no-sandbox.d.ts +1 -1
- package/dist/sandboxes/no-sandbox.js +1 -1
- package/dist/sandboxes/podman.d.ts +1 -1
- package/dist/sandboxes/podman.js +2 -2
- package/dist/sandboxes/podman.js.map +1 -1
- package/dist/sandboxes/vercel.d.ts +1 -1
- package/dist/sandboxes/vercel.js.map +1 -1
- package/dist/templates/blank/main.mts +1 -1
- package/dist/templates/parallel-planner/main.mts +1 -1
- package/dist/templates/parallel-planner-with-review/main.mts +1 -1
- package/dist/templates/simple-loop/main.mts +1 -1
- package/package.json +1 -1
- package/dist/chunk-72UVAC7B.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
-
import { NodeContext_exports, NodeFileSystem_exports, formatErrorMessage } from './chunk-
|
|
3
|
-
import { Context_exports, CwdError, Effect_exports, resolveCwd, getCurrentBranch, generateTempBranchName, Layer_exports, FileDisplay, ClackDisplay, WorktreeDockerSandboxFactory, SandboxConfig, Display, pruneStale, create, copyToWorktree, runHostHooks, startSandbox, resolveGitMounts, SANDBOX_REPO_DIR, remove, makeSandboxFromHandle, syncOut, withSandboxLifecycle, hasUncommittedChanges,
|
|
2
|
+
import { NodeContext_exports, NodeFileSystem_exports, formatErrorMessage } from './chunk-DJRHWPEH.js';
|
|
3
|
+
import { Context_exports, CwdError, Effect_exports, resolveCwd, getCurrentBranch, generateTempBranchName, Layer_exports, FileDisplay, ClackDisplay, WorktreeDockerSandboxFactory, SandboxConfig, Display, pruneStale, create, copyToWorktree, runHostHooks, startSandbox, resolveGitMounts, patchGitMountsForWindows, SANDBOX_REPO_DIR, remove, makeSandboxFromHandle, syncOut, withSandboxLifecycle, hasUncommittedChanges, registerShutdown, SandboxFactory, PromptError, FileSystem_exports, SessionCaptureError, Clock_exports, Duration_exports, Option_exports, PromptExpansionTimeoutError, Deferred_exports, AgentError, Ref_exports, SilentDisplay, AgentIdleTimeoutError, Fiber_exports } from './chunk-VOG34SRF.js';
|
|
4
4
|
export { createBindMountSandboxProvider, createIsolatedSandboxProvider } from './chunk-BIWNFKGV.js';
|
|
5
|
-
import { noSandbox } from './chunk-
|
|
5
|
+
import { noSandbox } from './chunk-62WN33RK.js';
|
|
6
6
|
import './chunk-NGBM7T3E.js';
|
|
7
7
|
import { mkdirSync, appendFileSync } from 'fs';
|
|
8
8
|
import path, { join, posix, dirname, relative } from 'path';
|
|
@@ -372,7 +372,8 @@ var orchestrate = (options) => {
|
|
|
372
372
|
hostWorktreePath,
|
|
373
373
|
applyToHost,
|
|
374
374
|
signal: options.signal,
|
|
375
|
-
timeouts: options.timeouts
|
|
375
|
+
timeouts: options.timeouts,
|
|
376
|
+
keepSourceBranch: options.keepSourceBranch
|
|
376
377
|
},
|
|
377
378
|
sandbox,
|
|
378
379
|
(ctx) => Effect_exports.gen(function* () {
|
|
@@ -400,7 +401,7 @@ var orchestrate = (options) => {
|
|
|
400
401
|
);
|
|
401
402
|
yield* display.status(label("Agent started"), "success");
|
|
402
403
|
const textBuffer = new TextDeltaBuffer((chunk) => {
|
|
403
|
-
Effect_exports.runPromise(display.
|
|
404
|
+
Effect_exports.runPromise(display.textChunk(chunk));
|
|
404
405
|
Effect_exports.runPromise(
|
|
405
406
|
streamEmitter.emit({
|
|
406
407
|
type: "text",
|
|
@@ -751,20 +752,30 @@ var Output = {
|
|
|
751
752
|
* Declare an object-typed structured output extracted from an XML tag in
|
|
752
753
|
* the agent's stdout. The tag contents are JSON-parsed (with fence-aware
|
|
753
754
|
* unwrapping) and validated against the provided Standard Schema validator.
|
|
755
|
+
*
|
|
756
|
+
* Set `maxRetries` to have `run()` automatically resume the failed session
|
|
757
|
+
* and ask the agent to re-emit corrected output when extraction or
|
|
758
|
+
* validation fails. Default: `0` (no retries).
|
|
754
759
|
*/
|
|
755
760
|
object: (opts) => ({
|
|
756
761
|
_tag: "object",
|
|
757
762
|
tag: opts.tag,
|
|
758
|
-
schema: opts.schema
|
|
763
|
+
schema: opts.schema,
|
|
764
|
+
maxRetries: opts.maxRetries
|
|
759
765
|
}),
|
|
760
766
|
/**
|
|
761
767
|
* Declare a string-typed structured output extracted from an XML tag in
|
|
762
768
|
* the agent's stdout. The tag contents are whitespace-trimmed and returned
|
|
763
769
|
* as a plain string — no JSON parsing, no schema validation.
|
|
770
|
+
*
|
|
771
|
+
* Set `maxRetries` to have `run()` automatically resume the failed session
|
|
772
|
+
* and ask the agent to re-emit corrected output when extraction fails.
|
|
773
|
+
* Default: `0` (no retries).
|
|
764
774
|
*/
|
|
765
775
|
string: (opts) => ({
|
|
766
776
|
_tag: "string",
|
|
767
|
-
tag: opts.tag
|
|
777
|
+
tag: opts.tag,
|
|
778
|
+
maxRetries: opts.maxRetries
|
|
768
779
|
})
|
|
769
780
|
};
|
|
770
781
|
var StructuredOutputError = class extends Error {
|
|
@@ -870,6 +881,24 @@ var unwrapFences = (text2) => {
|
|
|
870
881
|
};
|
|
871
882
|
|
|
872
883
|
// src/run.ts
|
|
884
|
+
var buildStructuredOutputRetryFeedback = (error, retriesRemaining) => {
|
|
885
|
+
const raw = error.rawMatched === void 0 ? "(no matching tag was emitted)" : error.rawMatched;
|
|
886
|
+
const cause = error.cause === void 0 ? "(no parser detail)" : typeof error.cause === "string" ? error.cause : JSON.stringify(error.cause, null, 2);
|
|
887
|
+
return `Your previous response did not produce valid structured output.
|
|
888
|
+
|
|
889
|
+
Retries remaining after this attempt: ${retriesRemaining}.
|
|
890
|
+
|
|
891
|
+
Problem:
|
|
892
|
+
${error.message}
|
|
893
|
+
|
|
894
|
+
Parser detail:
|
|
895
|
+
${cause}
|
|
896
|
+
|
|
897
|
+
Previous matched output:
|
|
898
|
+
${raw}
|
|
899
|
+
|
|
900
|
+
Emit only a corrected <${error.tag}> block. Do not change files or run commands.`;
|
|
901
|
+
};
|
|
873
902
|
var DEFAULT_MAX_ITERATIONS = 1;
|
|
874
903
|
var sanitizeBranchForFilename = (branch) => branch.replace(/[/\\:*?"<>|]/g, "-");
|
|
875
904
|
var printFileDisplayStartup = (options) => {
|
|
@@ -982,6 +1011,17 @@ async function run(options) {
|
|
|
982
1011
|
"output requires maxIterations to be 1. Structured output is only supported for single-iteration runs."
|
|
983
1012
|
);
|
|
984
1013
|
}
|
|
1014
|
+
const outputMaxRetries = options.output?.maxRetries ?? 0;
|
|
1015
|
+
if (outputMaxRetries < 0 || !Number.isInteger(outputMaxRetries)) {
|
|
1016
|
+
throw new Error(
|
|
1017
|
+
`output.maxRetries must be a non-negative integer. Received: ${outputMaxRetries}`
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
if (outputMaxRetries > 0 && !provider.sessionStorage) {
|
|
1021
|
+
throw new Error(
|
|
1022
|
+
`output.maxRetries requires an agent provider that supports session resumption. The "${provider.name}" provider does not. Use claudeCode, codex, or pi, or set maxRetries to 0.`
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
985
1025
|
const branch = branchStrategy.type === "branch" ? branchStrategy.branch : void 0;
|
|
986
1026
|
const hostRepoDir = await Effect_exports.runPromise(
|
|
987
1027
|
resolveCwd(options.cwd).pipe(Effect_exports.provide(NodeContext_exports.layer))
|
|
@@ -1183,18 +1223,41 @@ async function run(options) {
|
|
|
1183
1223
|
};
|
|
1184
1224
|
if (options.output) {
|
|
1185
1225
|
const lastIteration = baseResult.iterations.at(-1);
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1226
|
+
try {
|
|
1227
|
+
const output = await extractStructuredOutput(
|
|
1228
|
+
baseResult.stdout,
|
|
1229
|
+
options.output,
|
|
1230
|
+
{
|
|
1231
|
+
commits: baseResult.commits,
|
|
1232
|
+
branch: baseResult.branch,
|
|
1233
|
+
preservedWorktreePath: baseResult.preservedWorktreePath,
|
|
1234
|
+
sessionId: lastIteration?.sessionId,
|
|
1235
|
+
sessionFilePath: lastIteration?.sessionFilePath
|
|
1236
|
+
}
|
|
1237
|
+
);
|
|
1238
|
+
return { ...baseResult, output };
|
|
1239
|
+
} catch (error) {
|
|
1240
|
+
if (error instanceof StructuredOutputError && outputMaxRetries > 0 && error.sessionId !== void 0) {
|
|
1241
|
+
const retriesRemainingAfter = outputMaxRetries - 1;
|
|
1242
|
+
const retryOutput = {
|
|
1243
|
+
...options.output,
|
|
1244
|
+
maxRetries: retriesRemainingAfter
|
|
1245
|
+
};
|
|
1246
|
+
return run({
|
|
1247
|
+
...options,
|
|
1248
|
+
prompt: buildStructuredOutputRetryFeedback(
|
|
1249
|
+
error,
|
|
1250
|
+
retriesRemainingAfter
|
|
1251
|
+
),
|
|
1252
|
+
promptFile: void 0,
|
|
1253
|
+
promptArgs: void 0,
|
|
1254
|
+
resumeSession: error.sessionId,
|
|
1255
|
+
forkSession: false,
|
|
1256
|
+
output: retryOutput
|
|
1257
|
+
});
|
|
1195
1258
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1259
|
+
throw error;
|
|
1260
|
+
}
|
|
1198
1261
|
}
|
|
1199
1262
|
return baseResult;
|
|
1200
1263
|
}
|
|
@@ -1363,14 +1426,20 @@ var interactive = async (options) => {
|
|
|
1363
1426
|
return startResult.handle;
|
|
1364
1427
|
} else {
|
|
1365
1428
|
const gitPath = join(hostRepoDir, ".git");
|
|
1366
|
-
const
|
|
1429
|
+
const rawGitMounts = yield* resolveGitMounts(gitPath);
|
|
1430
|
+
const worktreeOrRepoPath = isHeadMode ? hostRepoDir : worktreeInfo.path;
|
|
1431
|
+
const gitMounts = yield* patchGitMountsForWindows(
|
|
1432
|
+
rawGitMounts,
|
|
1433
|
+
worktreeOrRepoPath,
|
|
1434
|
+
SANDBOX_REPO_DIR
|
|
1435
|
+
);
|
|
1367
1436
|
const startResult = yield* d.taskLog(
|
|
1368
1437
|
"Starting sandbox",
|
|
1369
1438
|
() => startSandbox({
|
|
1370
1439
|
provider: sandboxProvider,
|
|
1371
1440
|
hostRepoDir,
|
|
1372
1441
|
env: effectiveEnv,
|
|
1373
|
-
worktreeOrRepoPath
|
|
1442
|
+
worktreeOrRepoPath,
|
|
1374
1443
|
gitMounts,
|
|
1375
1444
|
repoDir: SANDBOX_REPO_DIR
|
|
1376
1445
|
})
|
|
@@ -1492,9 +1561,12 @@ var buildSandboxHandle = (ctx, close) => {
|
|
|
1492
1561
|
sandboxRepoDir,
|
|
1493
1562
|
sandbox,
|
|
1494
1563
|
providerHandle,
|
|
1564
|
+
bindMountHandle,
|
|
1495
1565
|
applyToHost,
|
|
1496
|
-
timeouts
|
|
1566
|
+
timeouts,
|
|
1567
|
+
branchStrategy
|
|
1497
1568
|
} = ctx;
|
|
1569
|
+
const mergeToHead = branchStrategy?.type === "merge-to-head";
|
|
1498
1570
|
const sandboxHandle = {
|
|
1499
1571
|
branch,
|
|
1500
1572
|
worktreePath,
|
|
@@ -1506,6 +1578,24 @@ var buildSandboxHandle = (ctx, close) => {
|
|
|
1506
1578
|
promptFile,
|
|
1507
1579
|
maxIterations = 1
|
|
1508
1580
|
} = runOptions;
|
|
1581
|
+
if (runOptions.resumeSession && maxIterations > 1) {
|
|
1582
|
+
throw new Error(
|
|
1583
|
+
"resumeSession cannot be combined with maxIterations > 1. Resume applies to iteration 1 only; multi-iteration resume semantics are not supported."
|
|
1584
|
+
);
|
|
1585
|
+
}
|
|
1586
|
+
if (runOptions.forkSession && !runOptions.resumeSession) {
|
|
1587
|
+
throw new Error(
|
|
1588
|
+
"forkSession requires resumeSession. Use sandboxRunResult.fork(prompt) to fork the most recent captured session."
|
|
1589
|
+
);
|
|
1590
|
+
}
|
|
1591
|
+
if (runOptions.resumeSession) {
|
|
1592
|
+
await assertResumeSessionExists({
|
|
1593
|
+
provider,
|
|
1594
|
+
sandboxTag: ctx.providerTag,
|
|
1595
|
+
hostRepoDir,
|
|
1596
|
+
resumeSession: runOptions.resumeSession
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1509
1599
|
const resolved = await Effect_exports.runPromise(
|
|
1510
1600
|
resolvePrompt({ prompt, promptFile }).pipe(
|
|
1511
1601
|
Effect_exports.provide(NodeContext_exports.layer)
|
|
@@ -1564,7 +1654,8 @@ var buildSandboxHandle = (ctx, close) => {
|
|
|
1564
1654
|
{
|
|
1565
1655
|
hostWorktreePath: worktreePath,
|
|
1566
1656
|
sandboxRepoPath: sandboxRepoDir,
|
|
1567
|
-
applyToHost
|
|
1657
|
+
applyToHost,
|
|
1658
|
+
bindMountHandle
|
|
1568
1659
|
},
|
|
1569
1660
|
sandbox
|
|
1570
1661
|
).pipe(
|
|
@@ -1592,15 +1683,18 @@ var buildSandboxHandle = (ctx, close) => {
|
|
|
1592
1683
|
hostRepoDir,
|
|
1593
1684
|
iterations: maxIterations,
|
|
1594
1685
|
prompt: resolvedPrompt,
|
|
1595
|
-
branch,
|
|
1686
|
+
branch: mergeToHead ? void 0 : branch,
|
|
1596
1687
|
provider,
|
|
1597
1688
|
completionSignal: runOptions.completionSignal,
|
|
1598
1689
|
idleTimeoutSeconds: runOptions.idleTimeoutSeconds,
|
|
1599
1690
|
completionTimeoutSeconds: runOptions.completionTimeoutSeconds,
|
|
1600
1691
|
name: runOptions.name,
|
|
1692
|
+
resumeSession: runOptions.resumeSession,
|
|
1693
|
+
forkSession: runOptions.forkSession,
|
|
1601
1694
|
signal: runOptions.signal,
|
|
1602
1695
|
skipPromptExpansion: isInlinePrompt,
|
|
1603
|
-
timeouts
|
|
1696
|
+
timeouts,
|
|
1697
|
+
keepSourceBranch: mergeToHead
|
|
1604
1698
|
});
|
|
1605
1699
|
const completion = buildCompletionMessage(
|
|
1606
1700
|
orchestrateResult.completionSignal,
|
|
@@ -1619,13 +1713,38 @@ var buildSandboxHandle = (ctx, close) => {
|
|
|
1619
1713
|
runOptions.signal?.throwIfAborted();
|
|
1620
1714
|
throw error;
|
|
1621
1715
|
}
|
|
1622
|
-
|
|
1716
|
+
const baseResult = {
|
|
1623
1717
|
iterations: result.iterations,
|
|
1624
1718
|
completionSignal: result.completionSignal,
|
|
1625
1719
|
stdout: result.stdout,
|
|
1626
1720
|
commits: result.commits,
|
|
1627
1721
|
logFilePath: resolvedLogging.type === "file" ? resolvedLogging.path : void 0
|
|
1628
1722
|
};
|
|
1723
|
+
const lastIteration = result.iterations.at(-1);
|
|
1724
|
+
if (provider.sessionStorage && lastIteration?.sessionId) {
|
|
1725
|
+
const capturedSessionId = lastIteration.sessionId;
|
|
1726
|
+
return {
|
|
1727
|
+
...baseResult,
|
|
1728
|
+
resume: (nextPrompt, resumeOptions) => sandboxHandle.run({
|
|
1729
|
+
...runOptions,
|
|
1730
|
+
...resumeOptions,
|
|
1731
|
+
prompt: nextPrompt,
|
|
1732
|
+
promptFile: void 0,
|
|
1733
|
+
maxIterations: 1,
|
|
1734
|
+
resumeSession: capturedSessionId
|
|
1735
|
+
}),
|
|
1736
|
+
fork: (nextPrompt, forkOptions) => sandboxHandle.run({
|
|
1737
|
+
...runOptions,
|
|
1738
|
+
...forkOptions,
|
|
1739
|
+
prompt: nextPrompt,
|
|
1740
|
+
promptFile: void 0,
|
|
1741
|
+
maxIterations: 1,
|
|
1742
|
+
resumeSession: capturedSessionId,
|
|
1743
|
+
forkSession: true
|
|
1744
|
+
})
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
return baseResult;
|
|
1629
1748
|
},
|
|
1630
1749
|
interactive: async (interactiveOptions) => {
|
|
1631
1750
|
interactiveOptions.signal?.throwIfAborted();
|
|
@@ -1674,10 +1793,11 @@ var buildSandboxHandle = (ctx, close) => {
|
|
|
1674
1793
|
{
|
|
1675
1794
|
hostRepoDir,
|
|
1676
1795
|
sandboxRepoDir,
|
|
1677
|
-
branch,
|
|
1796
|
+
branch: mergeToHead ? void 0 : branch,
|
|
1678
1797
|
hostWorktreePath: worktreePath,
|
|
1679
1798
|
applyToHost,
|
|
1680
|
-
timeouts
|
|
1799
|
+
timeouts,
|
|
1800
|
+
keepSourceBranch: mergeToHead
|
|
1681
1801
|
},
|
|
1682
1802
|
sandbox,
|
|
1683
1803
|
(ctx2) => Effect_exports.gen(function* () {
|
|
@@ -1736,6 +1856,13 @@ var buildSandboxHandle = (ctx, close) => {
|
|
|
1736
1856
|
exitCode: lifecycleResult.result
|
|
1737
1857
|
};
|
|
1738
1858
|
},
|
|
1859
|
+
exec: async (command, options) => {
|
|
1860
|
+
const mergedOptions = { cwd: sandboxRepoDir, ...options };
|
|
1861
|
+
if (providerHandle) {
|
|
1862
|
+
return providerHandle.exec(command, mergedOptions);
|
|
1863
|
+
}
|
|
1864
|
+
return Effect_exports.runPromise(sandbox.exec(command, mergedOptions));
|
|
1865
|
+
},
|
|
1739
1866
|
close: async () => close(),
|
|
1740
1867
|
[Symbol.asyncDispose]: async () => {
|
|
1741
1868
|
await sandboxHandle.close();
|
|
@@ -1763,6 +1890,7 @@ var createSandboxFromWorktree = async (options) => {
|
|
|
1763
1890
|
if (isTestMode) {
|
|
1764
1891
|
sandbox = options._test.buildSandbox(worktreePath);
|
|
1765
1892
|
sandboxRepoDir = worktreePath;
|
|
1893
|
+
providerHandle = options._test.bindMountHandle;
|
|
1766
1894
|
} else {
|
|
1767
1895
|
const resolvedEnv = await Effect_exports.runPromise(
|
|
1768
1896
|
resolveEnv(hostRepoDir).pipe(Effect_exports.provide(NodeContext_exports.layer))
|
|
@@ -1794,16 +1922,7 @@ var createSandboxFromWorktree = async (options) => {
|
|
|
1794
1922
|
Effect_exports.catchAll(() => Effect_exports.succeed([])),
|
|
1795
1923
|
// Patch git mounts for Windows worktree compatibility (ADR-0006)
|
|
1796
1924
|
Effect_exports.flatMap(
|
|
1797
|
-
(gitMounts) =>
|
|
1798
|
-
try: () => patchGitMountsForWindows(
|
|
1799
|
-
gitMounts,
|
|
1800
|
-
worktreePath,
|
|
1801
|
-
SANDBOX_REPO_DIR
|
|
1802
|
-
),
|
|
1803
|
-
catch: (e) => new Error(
|
|
1804
|
-
`Failed to patch git mounts: ${e instanceof Error ? e.message : String(e)}`
|
|
1805
|
-
)
|
|
1806
|
-
})
|
|
1925
|
+
(gitMounts) => patchGitMountsForWindows(gitMounts, worktreePath, SANDBOX_REPO_DIR)
|
|
1807
1926
|
),
|
|
1808
1927
|
Effect_exports.flatMap(
|
|
1809
1928
|
(gitMounts) => startSandbox({
|
|
@@ -1848,6 +1967,7 @@ var createSandboxFromWorktree = async (options) => {
|
|
|
1848
1967
|
}
|
|
1849
1968
|
const applyToHost = isIsolated && providerHandle ? () => syncOut(worktreePath, providerHandle) : () => Effect_exports.void;
|
|
1850
1969
|
let closed = false;
|
|
1970
|
+
const bindMountHandle = options.sandbox.tag === "bind-mount" ? providerHandle : void 0;
|
|
1851
1971
|
return buildSandboxHandle(
|
|
1852
1972
|
{
|
|
1853
1973
|
branch,
|
|
@@ -1856,8 +1976,11 @@ var createSandboxFromWorktree = async (options) => {
|
|
|
1856
1976
|
sandboxRepoDir,
|
|
1857
1977
|
sandbox,
|
|
1858
1978
|
providerHandle,
|
|
1979
|
+
bindMountHandle,
|
|
1980
|
+
providerTag: options.sandbox.tag,
|
|
1859
1981
|
applyToHost,
|
|
1860
|
-
timeouts: options.timeouts
|
|
1982
|
+
timeouts: options.timeouts,
|
|
1983
|
+
branchStrategy: options.branchStrategy
|
|
1861
1984
|
},
|
|
1862
1985
|
async () => {
|
|
1863
1986
|
if (closed) return { preservedWorktreePath: void 0 };
|
|
@@ -1902,6 +2025,7 @@ var createSandbox = async (options) => {
|
|
|
1902
2025
|
if (isTestMode) {
|
|
1903
2026
|
sandbox2 = options._test.buildSandbox(worktreePath2);
|
|
1904
2027
|
sandboxRepoDir2 = worktreePath2;
|
|
2028
|
+
providerHandle2 = options._test.bindMountHandle;
|
|
1905
2029
|
} else {
|
|
1906
2030
|
const resolvedEnv = yield* resolveEnv(hostRepoDir2);
|
|
1907
2031
|
const env = mergeProviderEnv({
|
|
@@ -1925,16 +2049,11 @@ var createSandbox = async (options) => {
|
|
|
1925
2049
|
Effect_exports.catchAll(() => Effect_exports.succeed([])),
|
|
1926
2050
|
// Patch git mounts for Windows worktree compatibility (ADR-0006)
|
|
1927
2051
|
Effect_exports.flatMap(
|
|
1928
|
-
(gitMounts) =>
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
),
|
|
1934
|
-
catch: (e) => new Error(
|
|
1935
|
-
`Failed to patch git mounts: ${e instanceof Error ? e.message : String(e)}`
|
|
1936
|
-
)
|
|
1937
|
-
})
|
|
2052
|
+
(gitMounts) => patchGitMountsForWindows(
|
|
2053
|
+
gitMounts,
|
|
2054
|
+
worktreePath2,
|
|
2055
|
+
SANDBOX_REPO_DIR
|
|
2056
|
+
)
|
|
1938
2057
|
),
|
|
1939
2058
|
Effect_exports.flatMap(
|
|
1940
2059
|
(gitMounts) => startSandbox({
|
|
@@ -2019,6 +2138,7 @@ Worktree preserved at ${worktreePath}`);
|
|
|
2019
2138
|
})
|
|
2020
2139
|
);
|
|
2021
2140
|
};
|
|
2141
|
+
const bindMountHandle = options.sandbox.tag === "bind-mount" ? providerHandle : void 0;
|
|
2022
2142
|
return buildSandboxHandle(
|
|
2023
2143
|
{
|
|
2024
2144
|
branch,
|
|
@@ -2027,6 +2147,8 @@ Worktree preserved at ${worktreePath}`);
|
|
|
2027
2147
|
sandboxRepoDir,
|
|
2028
2148
|
sandbox,
|
|
2029
2149
|
providerHandle,
|
|
2150
|
+
bindMountHandle,
|
|
2151
|
+
providerTag: options.sandbox.tag,
|
|
2030
2152
|
applyToHost,
|
|
2031
2153
|
timeouts: options.timeouts
|
|
2032
2154
|
},
|
|
@@ -2039,6 +2161,7 @@ Worktree preserved at ${worktreePath}`);
|
|
|
2039
2161
|
var createWorktree = async (options) => {
|
|
2040
2162
|
const branch = options.branchStrategy.type === "branch" ? options.branchStrategy.branch : void 0;
|
|
2041
2163
|
const baseBranch = options.branchStrategy.type === "branch" ? options.branchStrategy.baseBranch : void 0;
|
|
2164
|
+
const isMergeToHead = options.branchStrategy.type === "merge-to-head";
|
|
2042
2165
|
const { hostRepoDir, worktreeInfo } = await Effect_exports.gen(function* () {
|
|
2043
2166
|
const hostRepoDir2 = yield* resolveCwd(options.cwd);
|
|
2044
2167
|
yield* pruneStale(hostRepoDir2).pipe(
|
|
@@ -2144,7 +2267,12 @@ var createWorktree = async (options) => {
|
|
|
2144
2267
|
handle = startResult.handle;
|
|
2145
2268
|
} else {
|
|
2146
2269
|
const gitPath = join(hostRepoDir, ".git");
|
|
2147
|
-
const
|
|
2270
|
+
const rawGitMounts = yield* resolveGitMounts(gitPath);
|
|
2271
|
+
const gitMounts = yield* patchGitMountsForWindows(
|
|
2272
|
+
rawGitMounts,
|
|
2273
|
+
worktreeInfo.path,
|
|
2274
|
+
SANDBOX_REPO_DIR
|
|
2275
|
+
);
|
|
2148
2276
|
const startResult = yield* d.taskLog(
|
|
2149
2277
|
"Starting sandbox",
|
|
2150
2278
|
() => startSandbox({
|
|
@@ -2173,10 +2301,14 @@ var createWorktree = async (options) => {
|
|
|
2173
2301
|
hostRepoDir,
|
|
2174
2302
|
sandboxRepoDir: worktreePath,
|
|
2175
2303
|
hooks,
|
|
2176
|
-
|
|
2304
|
+
// merge-to-head: pass `undefined` so the lifecycle records the
|
|
2305
|
+
// host's current branch and merges the worktree's commits back into
|
|
2306
|
+
// it. branch strategy: pin to the worktree's branch.
|
|
2307
|
+
branch: isMergeToHead ? void 0 : worktreeInfo.branch,
|
|
2177
2308
|
hostWorktreePath: worktreeInfo.path,
|
|
2178
2309
|
applyToHost,
|
|
2179
|
-
timeouts: options.timeouts
|
|
2310
|
+
timeouts: options.timeouts,
|
|
2311
|
+
keepSourceBranch: isMergeToHead
|
|
2180
2312
|
},
|
|
2181
2313
|
sandbox,
|
|
2182
2314
|
(ctx) => Effect_exports.gen(function* () {
|
|
@@ -2304,7 +2436,12 @@ var createWorktree = async (options) => {
|
|
|
2304
2436
|
sandboxRepoDir = startResult.worktreePath;
|
|
2305
2437
|
} else {
|
|
2306
2438
|
const gitPath = join(hostRepoDir, ".git");
|
|
2307
|
-
const
|
|
2439
|
+
const rawGitMounts = yield* resolveGitMounts(gitPath);
|
|
2440
|
+
const gitMounts = yield* patchGitMountsForWindows(
|
|
2441
|
+
rawGitMounts,
|
|
2442
|
+
worktreeInfo.path,
|
|
2443
|
+
SANDBOX_REPO_DIR
|
|
2444
|
+
);
|
|
2308
2445
|
const startResult = yield* startSandbox({
|
|
2309
2446
|
provider: sandboxProvider,
|
|
2310
2447
|
hostRepoDir,
|
|
@@ -2338,12 +2475,14 @@ var createWorktree = async (options) => {
|
|
|
2338
2475
|
NodeFileSystem_exports.layer
|
|
2339
2476
|
);
|
|
2340
2477
|
})() : ClackDisplay.layer;
|
|
2478
|
+
const bindMountHandle = sandboxProvider.tag === "bind-mount" ? handle : void 0;
|
|
2341
2479
|
const reuseFactoryLayer = Layer_exports.succeed(SandboxFactory, {
|
|
2342
2480
|
withSandbox: (makeEffect) => makeEffect(
|
|
2343
2481
|
{
|
|
2344
2482
|
hostWorktreePath: worktreeInfo.path,
|
|
2345
2483
|
sandboxRepoPath: sandboxRepoDir,
|
|
2346
|
-
applyToHost
|
|
2484
|
+
applyToHost,
|
|
2485
|
+
bindMountHandle
|
|
2347
2486
|
},
|
|
2348
2487
|
sandbox
|
|
2349
2488
|
).pipe(
|
|
@@ -2369,7 +2508,10 @@ var createWorktree = async (options) => {
|
|
|
2369
2508
|
iterations: maxIterations,
|
|
2370
2509
|
hooks,
|
|
2371
2510
|
prompt: resolvedPrompt,
|
|
2372
|
-
|
|
2511
|
+
// merge-to-head: pass `undefined` so the lifecycle records the host's
|
|
2512
|
+
// current branch and routes through the merge step. branch strategy:
|
|
2513
|
+
// pin to the worktree's branch so commits stay there.
|
|
2514
|
+
branch: isMergeToHead ? void 0 : worktreeInfo.branch,
|
|
2373
2515
|
provider,
|
|
2374
2516
|
completionSignal: opts.completionSignal,
|
|
2375
2517
|
idleTimeoutSeconds: opts.idleTimeoutSeconds,
|
|
@@ -2378,7 +2520,8 @@ var createWorktree = async (options) => {
|
|
|
2378
2520
|
resumeSession: opts.resumeSession,
|
|
2379
2521
|
signal: opts.signal,
|
|
2380
2522
|
skipPromptExpansion: isInlinePrompt,
|
|
2381
|
-
timeouts: options.timeouts
|
|
2523
|
+
timeouts: options.timeouts,
|
|
2524
|
+
keepSourceBranch: isMergeToHead
|
|
2382
2525
|
});
|
|
2383
2526
|
const completion = buildCompletionMessage(
|
|
2384
2527
|
orchestrateResult.completionSignal,
|
|
@@ -2428,6 +2571,7 @@ var createWorktree = async (options) => {
|
|
|
2428
2571
|
hooks: opts.hooks,
|
|
2429
2572
|
copyToWorktree: opts.copyToWorktree,
|
|
2430
2573
|
timeouts: opts.timeouts,
|
|
2574
|
+
branchStrategy: options.branchStrategy,
|
|
2431
2575
|
_test: opts._test
|
|
2432
2576
|
});
|
|
2433
2577
|
};
|