@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/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { createRequire } from 'node:module';
2
- import { NodeContext_exports, NodeFileSystem_exports, formatErrorMessage } from './chunk-52CIJF45.js';
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, patchGitMountsForWindows, registerShutdown, SandboxFactory, PromptError, FileSystem_exports, SessionCaptureError, Clock_exports, Duration_exports, Option_exports, PromptExpansionTimeoutError, Deferred_exports, AgentError, Ref_exports, SilentDisplay, AgentIdleTimeoutError, Fiber_exports } from './chunk-5VM5QZ26.js';
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-72UVAC7B.js';
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.text(chunk));
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
- const output = await extractStructuredOutput(
1187
- baseResult.stdout,
1188
- options.output,
1189
- {
1190
- commits: baseResult.commits,
1191
- branch: baseResult.branch,
1192
- preservedWorktreePath: baseResult.preservedWorktreePath,
1193
- sessionId: lastIteration?.sessionId,
1194
- sessionFilePath: lastIteration?.sessionFilePath
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
- return { ...baseResult, output };
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 gitMounts = yield* resolveGitMounts(gitPath);
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: isHeadMode ? hostRepoDir : worktreeInfo.path,
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
- return {
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) => Effect_exports.tryPromise({
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) => Effect_exports.tryPromise({
1929
- try: () => patchGitMountsForWindows(
1930
- gitMounts,
1931
- worktreePath2,
1932
- SANDBOX_REPO_DIR
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 gitMounts = yield* resolveGitMounts(gitPath);
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
- branch: worktreeInfo.branch,
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 gitMounts = yield* resolveGitMounts(gitPath);
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
- branch: worktreeInfo.branch,
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
  };