@letrquan/book 0.3.0 → 0.4.0-beta.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.
Files changed (148) hide show
  1. package/CHANGELOG.md +1024 -0
  2. package/README.md +6 -3
  3. package/dist/{additional-roots-H7UNAEHS.js → additional-roots-VAWRQ625.js} +8 -7
  4. package/dist/{app-GE4OHJOC.js → app-J7BVPSUH.js} +5397 -2953
  5. package/dist/app-J7BVPSUH.js.map +1 -0
  6. package/dist/chunk-32WBCUAN.js +660 -0
  7. package/dist/chunk-32WBCUAN.js.map +1 -0
  8. package/dist/{chunk-525CAZPY.js → chunk-4DKHLZRI.js} +6 -6
  9. package/dist/{chunk-4AS7LKKQ.js → chunk-62EEAUID.js} +2 -2
  10. package/dist/{chunk-AQEQX4FF.js → chunk-AHC7OU75.js} +385 -55
  11. package/dist/chunk-AHC7OU75.js.map +1 -0
  12. package/dist/{chunk-EFJ2NH6E.js → chunk-AJZWLKLQ.js} +63 -14
  13. package/dist/chunk-AJZWLKLQ.js.map +1 -0
  14. package/dist/chunk-B5FC3MYZ.js +395 -0
  15. package/dist/chunk-B5FC3MYZ.js.map +1 -0
  16. package/dist/{chunk-67UZEWWF.js → chunk-DNFQTSMJ.js} +53 -11
  17. package/dist/chunk-DNFQTSMJ.js.map +1 -0
  18. package/dist/{chunk-TLX3H2CL.js → chunk-E5WLQSKA.js} +231 -40
  19. package/dist/chunk-E5WLQSKA.js.map +1 -0
  20. package/dist/chunk-ECLC4CIJ.js +1181 -0
  21. package/dist/chunk-ECLC4CIJ.js.map +1 -0
  22. package/dist/chunk-EEX6QNXV.js +127 -0
  23. package/dist/chunk-EEX6QNXV.js.map +1 -0
  24. package/dist/chunk-EFXVSQ5J.js +931 -0
  25. package/dist/chunk-EFXVSQ5J.js.map +1 -0
  26. package/dist/{chunk-RFJ6Q6LE.js → chunk-ENLB7NYV.js} +577 -164
  27. package/dist/chunk-ENLB7NYV.js.map +1 -0
  28. package/dist/{chunk-IME7R2UI.js → chunk-FPR7SKIE.js} +1268 -883
  29. package/dist/chunk-FPR7SKIE.js.map +1 -0
  30. package/dist/{chunk-VPNLCMVR.js → chunk-HFB74CLM.js} +13 -5
  31. package/dist/chunk-HFB74CLM.js.map +1 -0
  32. package/dist/{chunk-5RDRAO4B.js → chunk-HOZXKOE3.js} +37 -1
  33. package/dist/chunk-HOZXKOE3.js.map +1 -0
  34. package/dist/chunk-IDYWN37T.js +36 -0
  35. package/dist/chunk-IDYWN37T.js.map +1 -0
  36. package/dist/{chunk-YC6W4EJH.js → chunk-JGF5HLSI.js} +193 -75
  37. package/dist/chunk-JGF5HLSI.js.map +1 -0
  38. package/dist/{chunk-5YHTKWJN.js → chunk-KDNWMKXV.js} +2447 -907
  39. package/dist/chunk-KDNWMKXV.js.map +1 -0
  40. package/dist/{chunk-Y2VHN7TC.js → chunk-MEOB2DZA.js} +11 -1
  41. package/dist/chunk-MEOB2DZA.js.map +1 -0
  42. package/dist/{chunk-KBQS35OV.js → chunk-MV5PQNIA.js} +2 -2
  43. package/dist/chunk-N2G4Q7ME.js +2672 -0
  44. package/dist/chunk-N2G4Q7ME.js.map +1 -0
  45. package/dist/{chunk-62QL5QUP.js → chunk-OJBMDM5H.js} +17 -1
  46. package/dist/chunk-OJBMDM5H.js.map +1 -0
  47. package/dist/{chunk-56SIEMDX.js → chunk-PTDC7ZUP.js} +2 -2
  48. package/dist/{chunk-X4NJT436.js → chunk-REASC2QG.js} +67 -28
  49. package/dist/chunk-REASC2QG.js.map +1 -0
  50. package/dist/chunk-SBTU5BEC.js +114 -0
  51. package/dist/chunk-SBTU5BEC.js.map +1 -0
  52. package/dist/{chunk-HQI7NYGX.js → chunk-SWPDKLNM.js} +2 -2
  53. package/dist/{chunk-W3XFHEF4.js → chunk-UCFJ43OS.js} +140 -28
  54. package/dist/chunk-UCFJ43OS.js.map +1 -0
  55. package/dist/{chunk-QYBD22H5.js → chunk-W7SLYS3M.js} +80 -16
  56. package/dist/chunk-W7SLYS3M.js.map +1 -0
  57. package/dist/{chunk-SOW4WRQP.js → chunk-WV3O5PGX.js} +3 -3
  58. package/dist/{chunk-JTPOI3KU.js → chunk-XJRJVXJR.js} +262 -38
  59. package/dist/chunk-XJRJVXJR.js.map +1 -0
  60. package/dist/{chunk-224FJZLH.js → chunk-YIL6HTJX.js} +228 -5
  61. package/dist/chunk-YIL6HTJX.js.map +1 -0
  62. package/dist/{command-approvals-VBIU6BLM.js → command-approvals-ZRY66LCW.js} +5 -4
  63. package/dist/context-C3RAP6K5.js +41 -0
  64. package/dist/{hook-approvals-ZVT4V7IM.js → hook-approvals-DPQD5H65.js} +5 -4
  65. package/dist/index.js +657 -754
  66. package/dist/index.js.map +1 -1
  67. package/dist/{interactive-assets-T4I4SEHU.js → interactive-assets-73AQO5WP.js} +11 -6
  68. package/dist/{interactive-assets-T4I4SEHU.js.map → interactive-assets-73AQO5WP.js.map} +1 -1
  69. package/dist/job-runner.js +10 -3
  70. package/dist/job-runner.js.map +1 -1
  71. package/dist/job-supervisor.js +13 -4
  72. package/dist/job-supervisor.js.map +1 -1
  73. package/dist/loader-DQNPDKOX.js +27 -0
  74. package/dist/{mcp-approvals-HJ2VDYMC.js → mcp-approvals-27LJYADQ.js} +5 -4
  75. package/dist/{mcp-config-RNB5LEVE.js → mcp-config-GXSODNCM.js} +3 -2
  76. package/dist/memory-store-I6Z54VUN.js +51 -0
  77. package/dist/{model-window-store-MTSQ5JDJ.js → model-window-store-6VKXXTPJ.js} +4 -3
  78. package/dist/{openai-compatible-2OB54YST.js → openai-compatible-IPUNBJ53.js} +5 -3
  79. package/dist/{permission-approvals-YLV6UC5W.js → permission-approvals-Q5IMFEZI.js} +5 -4
  80. package/dist/{permissions-DHOKF3W2.js → permissions-7Y35X6HZ.js} +26 -9
  81. package/dist/provider-approvals-5AGH3RVT.js +76 -0
  82. package/dist/{sandbox-WRYTZH3D.js → sandbox-N2RY72GN.js} +6 -5
  83. package/dist/{scrollback-35ME2GFX.js → scrollback-GD76GNM4.js} +36 -28
  84. package/dist/scrollback-GD76GNM4.js.map +1 -0
  85. package/dist/sdk.d.ts +876 -64
  86. package/dist/sdk.js +57 -37
  87. package/dist/sdk.js.map +1 -1
  88. package/dist/settings-loader-KDBH45AQ.js +43 -0
  89. package/dist/{settings-removed-JWWPT4WA.js → settings-removed-3ATWJEF3.js} +2 -2
  90. package/dist/shell-selection-Z5WORZU4.js +31 -0
  91. package/dist/shell-selection-Z5WORZU4.js.map +1 -0
  92. package/package.json +5 -5
  93. package/dist/app-GE4OHJOC.js.map +0 -1
  94. package/dist/chunk-224FJZLH.js.map +0 -1
  95. package/dist/chunk-5RDRAO4B.js.map +0 -1
  96. package/dist/chunk-5YHTKWJN.js.map +0 -1
  97. package/dist/chunk-62QL5QUP.js.map +0 -1
  98. package/dist/chunk-67UZEWWF.js.map +0 -1
  99. package/dist/chunk-6OQQOMZR.js +0 -785
  100. package/dist/chunk-6OQQOMZR.js.map +0 -1
  101. package/dist/chunk-AQEQX4FF.js.map +0 -1
  102. package/dist/chunk-C7ADUNPR.js +0 -273
  103. package/dist/chunk-C7ADUNPR.js.map +0 -1
  104. package/dist/chunk-EFJ2NH6E.js.map +0 -1
  105. package/dist/chunk-IME7R2UI.js.map +0 -1
  106. package/dist/chunk-J62VZYOB.js +0 -1339
  107. package/dist/chunk-J62VZYOB.js.map +0 -1
  108. package/dist/chunk-JTPOI3KU.js.map +0 -1
  109. package/dist/chunk-QYBD22H5.js.map +0 -1
  110. package/dist/chunk-RFJ6Q6LE.js.map +0 -1
  111. package/dist/chunk-S4JMID4G.js +0 -259
  112. package/dist/chunk-S4JMID4G.js.map +0 -1
  113. package/dist/chunk-TLX3H2CL.js.map +0 -1
  114. package/dist/chunk-VPNLCMVR.js.map +0 -1
  115. package/dist/chunk-W3XFHEF4.js.map +0 -1
  116. package/dist/chunk-X4NJT436.js.map +0 -1
  117. package/dist/chunk-X66I2NQO.js +0 -137
  118. package/dist/chunk-X66I2NQO.js.map +0 -1
  119. package/dist/chunk-Y2VHN7TC.js.map +0 -1
  120. package/dist/chunk-YC6W4EJH.js.map +0 -1
  121. package/dist/context-CBOBDY5Q.js +0 -38
  122. package/dist/loader-S77OVTHH.js +0 -18
  123. package/dist/memory-store-DZUT5HUH.js +0 -75
  124. package/dist/scrollback-35ME2GFX.js.map +0 -1
  125. package/dist/settings-loader-JUNPGAAM.js +0 -35
  126. package/dist/shell-selection-CDHGJ4GC.js +0 -17
  127. /package/dist/{additional-roots-H7UNAEHS.js.map → additional-roots-VAWRQ625.js.map} +0 -0
  128. /package/dist/{chunk-525CAZPY.js.map → chunk-4DKHLZRI.js.map} +0 -0
  129. /package/dist/{chunk-4AS7LKKQ.js.map → chunk-62EEAUID.js.map} +0 -0
  130. /package/dist/{chunk-KBQS35OV.js.map → chunk-MV5PQNIA.js.map} +0 -0
  131. /package/dist/{chunk-56SIEMDX.js.map → chunk-PTDC7ZUP.js.map} +0 -0
  132. /package/dist/{chunk-HQI7NYGX.js.map → chunk-SWPDKLNM.js.map} +0 -0
  133. /package/dist/{chunk-SOW4WRQP.js.map → chunk-WV3O5PGX.js.map} +0 -0
  134. /package/dist/{command-approvals-VBIU6BLM.js.map → command-approvals-ZRY66LCW.js.map} +0 -0
  135. /package/dist/{context-CBOBDY5Q.js.map → context-C3RAP6K5.js.map} +0 -0
  136. /package/dist/{hook-approvals-ZVT4V7IM.js.map → hook-approvals-DPQD5H65.js.map} +0 -0
  137. /package/dist/{loader-S77OVTHH.js.map → loader-DQNPDKOX.js.map} +0 -0
  138. /package/dist/{mcp-approvals-HJ2VDYMC.js.map → mcp-approvals-27LJYADQ.js.map} +0 -0
  139. /package/dist/{mcp-config-RNB5LEVE.js.map → mcp-config-GXSODNCM.js.map} +0 -0
  140. /package/dist/{memory-store-DZUT5HUH.js.map → memory-store-I6Z54VUN.js.map} +0 -0
  141. /package/dist/{model-window-store-MTSQ5JDJ.js.map → model-window-store-6VKXXTPJ.js.map} +0 -0
  142. /package/dist/{openai-compatible-2OB54YST.js.map → openai-compatible-IPUNBJ53.js.map} +0 -0
  143. /package/dist/{permission-approvals-YLV6UC5W.js.map → permission-approvals-Q5IMFEZI.js.map} +0 -0
  144. /package/dist/{permissions-DHOKF3W2.js.map → permissions-7Y35X6HZ.js.map} +0 -0
  145. /package/dist/{sandbox-WRYTZH3D.js.map → provider-approvals-5AGH3RVT.js.map} +0 -0
  146. /package/dist/{settings-loader-JUNPGAAM.js.map → sandbox-N2RY72GN.js.map} +0 -0
  147. /package/dist/{settings-removed-JWWPT4WA.js.map → settings-loader-KDBH45AQ.js.map} +0 -0
  148. /package/dist/{shell-selection-CDHGJ4GC.js.map → settings-removed-3ATWJEF3.js.map} +0 -0
@@ -1,14 +1,24 @@
1
1
  import {
2
2
  HARDENED_DIFF_ARGS,
3
3
  hardenedGitArgs,
4
- hardenedGitEnv
5
- } from "./chunk-C7ADUNPR.js";
4
+ hardenedGitEnv,
5
+ noFetchGitEnv,
6
+ parseGitConfig,
7
+ readRepositoryProgramPins
8
+ } from "./chunk-32WBCUAN.js";
6
9
  import {
7
10
  systemClock
8
11
  } from "./chunk-RT5SATF7.js";
9
12
  import {
10
13
  buildChildEnv
11
14
  } from "./chunk-XHAO3K7I.js";
15
+ import {
16
+ ignoresHomeRepository
17
+ } from "./chunk-SBTU5BEC.js";
18
+ import {
19
+ canonicalizePath,
20
+ isOutside
21
+ } from "./chunk-YIL6HTJX.js";
12
22
  import {
13
23
  resolveBookHome
14
24
  } from "./chunk-ZPSZZUWF.js";
@@ -31,7 +41,7 @@ import { createHash, randomUUID } from "crypto";
31
41
  import { execFile, spawn } from "child_process";
32
42
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "fs";
33
43
  import { tmpdir } from "os";
34
- import { dirname, join, resolve } from "path";
44
+ import { dirname, join, relative, resolve, sep } from "path";
35
45
 
36
46
  // src/agents/git-signing.ts
37
47
  var SIGNING_KEY_PATTERN = "^(commit\\.gpgsign|gpg\\..*|user\\.signingkey)$";
@@ -56,51 +66,29 @@ var PIN_UNITS = [
56
66
  { keys: ["gpg.ssh.allowedsignersfile"], fallback: "" },
57
67
  { keys: ["gpg.ssh.revocationfile"], fallback: "" }
58
68
  ];
59
- async function readSigningConfig(repoRoot, run2) {
69
+ async function readSigningConfig(repoRoot, run2, signal) {
60
70
  const result = await run2(
61
71
  "git",
62
72
  ["config", "--show-scope", "-z", "--get-regexp", SIGNING_KEY_PATTERN],
63
- { cwd: repoRoot, allowExitCodes: [1, 129], timeoutMs: READ_TIMEOUT_MS }
73
+ { cwd: repoRoot, allowExitCodes: [1, 129], timeoutMs: READ_TIMEOUT_MS, signal }
64
74
  );
65
75
  if (result.code === 129 || /unknown option/i.test(result.stderr)) {
66
76
  throw new Error(
67
- `Applying an agent's result needs git ${MINIMUM_GIT} or later (for \`git config --show-scope\`); this git is ${await gitVersion(run2, repoRoot)}`
77
+ `Applying an agent's result needs git ${MINIMUM_GIT} or later (for \`git config --show-scope\`); this git is ${await gitVersion(run2, repoRoot, signal)}`
68
78
  );
69
79
  }
70
80
  return parseSigningConfig(result.stdout);
71
81
  }
72
- async function gitVersion(run2, repoRoot) {
82
+ async function gitVersion(run2, repoRoot, signal) {
73
83
  try {
74
- return (await run2("git", ["--version"], { cwd: repoRoot, timeoutMs: READ_TIMEOUT_MS })).stdout.trim();
75
- } catch {
84
+ return (await run2("git", ["--version"], { cwd: repoRoot, timeoutMs: READ_TIMEOUT_MS, signal })).stdout.trim();
85
+ } catch (error) {
86
+ if (signal?.aborted) throw error;
76
87
  return "of an unknown version";
77
88
  }
78
89
  }
79
90
  function parseSigningConfig(stdout) {
80
- const fields = stdout.split("\0");
81
- const entries = [];
82
- for (let index = 0; index + 1 < fields.length; index += 2) {
83
- const separator = fields[index + 1].indexOf("\n");
84
- entries.push({
85
- scope: fields[index].toLowerCase(),
86
- key: canonicalConfigKey(
87
- separator === -1 ? fields[index + 1] : fields[index + 1].slice(0, separator)
88
- ),
89
- // Git's own reading of a key with no value: a boolean true.
90
- value: separator === -1 ? "true" : fields[index + 1].slice(separator + 1)
91
- });
92
- }
93
- return entries;
94
- }
95
- function canonicalConfigKey(raw) {
96
- const nameStart = raw.lastIndexOf(".");
97
- if (nameStart === -1) return raw.toLowerCase();
98
- const head = raw.slice(0, nameStart);
99
- const name = raw.slice(nameStart + 1).toLowerCase();
100
- const subsectionStart = head.indexOf(".");
101
- if (subsectionStart === -1) return `${head.toLowerCase()}.${name}`;
102
- const section = head.slice(0, subsectionStart).toLowerCase();
103
- return `${section}.${head.slice(subsectionStart + 1)}.${name}`;
91
+ return parseGitConfig(stdout);
104
92
  }
105
93
  function signingFacts(entries) {
106
94
  const operatorEntries = [];
@@ -180,36 +168,42 @@ function derivedSigningKey(facts, inputs) {
180
168
  function committerSigningIdentity(ident) {
181
169
  return ident.trim().replace(/\s+\d+\s+[+-]\d{4}$/, "");
182
170
  }
183
- async function signingPinArgs(repoRoot, run2) {
184
- const entries = await readSigningConfig(repoRoot, run2);
171
+ async function signingPinArgs(repoRoot, run2, signal) {
172
+ const entries = await readSigningConfig(repoRoot, run2, signal);
185
173
  const facts = signingFacts(entries);
186
174
  const inputs = { committerIdent: "", sshDefaultKey: "" };
187
175
  const source = signingKeySource(facts);
188
176
  if (source === "committer") {
189
177
  inputs.committerIdent = (await run2("git", ["var", "GIT_COMMITTER_IDENT"], {
190
178
  cwd: repoRoot,
191
- timeoutMs: READ_TIMEOUT_MS
179
+ timeoutMs: READ_TIMEOUT_MS,
180
+ signal
192
181
  })).stdout;
193
182
  } else if (source === "ssh-default-key-command") {
194
- inputs.sshDefaultKey = await sshDefaultSigningKey(facts, repoRoot, run2);
183
+ inputs.sshDefaultKey = await sshDefaultSigningKey(facts, repoRoot, run2, signal);
195
184
  }
196
185
  return pinsFor(facts, inputs);
197
186
  }
198
- async function sshDefaultSigningKey(facts, repoRoot, run2) {
187
+ async function sshDefaultSigningKey(facts, repoRoot, run2, signal) {
199
188
  const command = lastOperatorValue(facts, ["gpg.ssh.defaultkeycommand"])?.trim();
200
189
  if (!command) return "";
201
190
  const [program, ...args] = command.split(/\s+/);
202
191
  try {
203
- const result = await run2(program, args, { cwd: repoRoot, timeoutMs: READ_TIMEOUT_MS });
192
+ const result = await run2(program, args, {
193
+ cwd: repoRoot,
194
+ timeoutMs: READ_TIMEOUT_MS,
195
+ signal
196
+ });
204
197
  const first = result.stdout.split(/\r?\n/)[0]?.trim() ?? "";
205
198
  return first.startsWith("key::") ? first : "";
206
- } catch {
199
+ } catch (error) {
200
+ if (signal?.aborted) throw error;
207
201
  return "";
208
202
  }
209
203
  }
210
204
 
211
205
  // src/agents/git-isolation.ts
212
- function run(command, args, options) {
206
+ function run(command, args, options, maxBuffer = DEFAULT_MAX_BUFFER_BYTES) {
213
207
  const env = internalGitEnv(options.env);
214
208
  const timeoutMs = options.timeoutMs ?? DEFAULT_GIT_TIMEOUT_MS;
215
209
  const signal = options.signal;
@@ -280,7 +274,15 @@ function run(command, args, options) {
280
274
  }
281
275
  reject(
282
276
  new Error(
283
- stderr.trim() || stdout.trim() || stdinError?.message || `${command} ${subcommandOf(args)} failed (${code})`
277
+ failureDescription(
278
+ null,
279
+ stderr.trim() || stdout.trim() || stdinError?.message || outputError?.message || "",
280
+ command,
281
+ args,
282
+ timeoutMs,
283
+ stoppedBy === "aborted",
284
+ code
285
+ )
284
286
  )
285
287
  );
286
288
  });
@@ -332,10 +334,10 @@ function run(command, args, options) {
332
334
  cwd: options.cwd,
333
335
  env,
334
336
  encoding: "utf8",
335
- // One buffer for every call: a signing read that overruns it has to fail rather than
336
- // resolve on a prefix, and this size is what makes that impractical rather than what makes
337
- // it correct.
338
- maxBuffer: 50 * 1024 * 1024
337
+ // Each of stdout and stderr is capped here, and a call that overruns the cap fails rather
338
+ // than resolving on what had arrived. Only this branch is capped: the `input` branch above
339
+ // collects its output without a limit. See {@link DEFAULT_MAX_BUFFER_BYTES}.
340
+ maxBuffer
339
341
  // No `timeout` and no `signal` option here. `execFile`'s own timeout kills the child but
340
342
  // still waits for its output to close, and a grandchild holding that pipe — a shell script,
341
343
  // a `credential.helper` that started something of its own — means it never does, so the
@@ -350,7 +352,7 @@ function run(command, args, options) {
350
352
  new Error(
351
353
  failureDescription(
352
354
  error,
353
- stderr || stdout,
355
+ stderr.trim() || stdout.trim(),
354
356
  command,
355
357
  args,
356
358
  timeoutMs,
@@ -368,11 +370,15 @@ function run(command, args, options) {
368
370
  settle(
369
371
  () => reject(
370
372
  new Error(
371
- // git's own words first, because they are the ones that say what went wrong. Then the
372
- // composed message, and not Node's: with no output to prefer, Node's is the whole argv
373
- // — hardening pairs included — which names `-c` where a reader needs the subcommand,
374
- // and says nothing that the exit code does not (#357).
375
- stderr.trim() || stdout.trim() || `${command} ${subcommandOf(args)} failed (${code})`
373
+ failureDescription(
374
+ error,
375
+ stderr.trim() || stdout.trim(),
376
+ command,
377
+ args,
378
+ timeoutMs,
379
+ stoppedBy === "aborted",
380
+ code
381
+ )
376
382
  )
377
383
  )
378
384
  );
@@ -385,6 +391,7 @@ function run(command, args, options) {
385
391
  });
386
392
  }
387
393
  var DEFAULT_GIT_TIMEOUT_MS = 12e4;
394
+ var DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
388
395
  var KILL_GRACE_MS = 2e3;
389
396
  function stopEscalating(child, settled) {
390
397
  child.kill("SIGTERM");
@@ -429,13 +436,20 @@ function exitCodeOf(error) {
429
436
  function isAbortError(error) {
430
437
  return error?.name === "AbortError" || error?.code === "ABORT_ERR";
431
438
  }
432
- function failureDescription(error, output, command, args, timeoutMs, aborted) {
439
+ function failureDescription(error, output, command, args, timeoutMs, aborted, code) {
433
440
  const invocation = `${command} ${subcommandOf(args)}`;
434
441
  if (aborted || isAbortError(error)) return `${invocation} was cancelled`;
435
442
  if (error?.killed) return `${invocation} timed out after ${timeoutMs}ms and was killed`;
436
443
  if (error?.signal) return `${invocation} was killed with ${error.signal}`;
437
444
  if (typeof error?.code === "string") return `${invocation} failed: ${error.code}`;
438
- return output.trim() || error?.message || `${invocation} failed`;
445
+ const trimmed = output.trim();
446
+ if (/promisor remote|lazy fetching disabled/i.test(trimmed)) {
447
+ return "this repository is a partial clone; Book's agent isolation does not fetch missing objects, because the fetch would run the transport this checkout configures (`core.sshCommand`, `credential.helper`) with no prompt; fetch the missing objects yourself (for example `git fetch --refetch <remote>` after unsetting `remote.<remote>.partialclonefilter`), then retry.\n" + trimmed;
448
+ }
449
+ if (trimmed) return trimmed;
450
+ if (code !== void 0) return `${invocation} failed (${code})`;
451
+ if (error?.message) return error.message;
452
+ return `${invocation} failed`;
439
453
  }
440
454
  var GIT_REPOSITORY_SELECTION_VARIABLES = /* @__PURE__ */ new Set([
441
455
  "GIT_DIR",
@@ -455,12 +469,31 @@ var GIT_REPOSITORY_SELECTION_VARIABLES = /* @__PURE__ */ new Set([
455
469
  function internalGitEnv(overrides) {
456
470
  const ambient = buildChildEnv(process.env);
457
471
  for (const name of GIT_REPOSITORY_SELECTION_VARIABLES) delete ambient[name];
458
- return { ...ambient, ...overrides, ...hardenedGitEnv() };
472
+ return {
473
+ ...ambient,
474
+ ...overrides,
475
+ ...hardenedGitEnv(),
476
+ ...noFetchGitEnv()
477
+ };
459
478
  }
460
- function git(cwd, args, options = {}) {
461
- return run("git", hardenedGitArgs(args), { ...options, cwd });
479
+ async function git(cwd, args, options = {}) {
480
+ const pins = await readRepositoryProgramPins(
481
+ cwd,
482
+ (pinArgs, pinOpts) => run("git", pinArgs, {
483
+ signal: options.signal,
484
+ timeoutMs: options.timeoutMs,
485
+ env: options.env,
486
+ ...pinOpts
487
+ }),
488
+ {
489
+ signal: options.signal,
490
+ timeoutMs: options.timeoutMs,
491
+ env: options.env,
492
+ allowFsmonitor: false
493
+ }
494
+ );
495
+ return run("git", hardenedGitArgs([...pins, ...args]), { ...options, cwd });
462
496
  }
463
- var hardenedRunner = run;
464
497
  function cherryPickFailureResult(facts) {
465
498
  const conflicted = facts.unmergedPaths.length > 0;
466
499
  if (facts.abortError) {
@@ -491,28 +524,81 @@ function tempIndex() {
491
524
  return { dir, path: join(dir, "index") };
492
525
  }
493
526
  async function findGitRoot(workspace, signal) {
494
- try {
495
- return (await git(workspace, ["rev-parse", "--show-toplevel"], { signal })).stdout.trim();
496
- } catch {
527
+ if (!existsSync(workspace)) {
497
528
  return void 0;
498
529
  }
530
+ try {
531
+ const top = (await git(workspace, ["rev-parse", "--show-toplevel"], { signal })).stdout.trim();
532
+ return ignoresHomeRepository(top, workspace) ? void 0 : top;
533
+ } catch (error) {
534
+ const message = error instanceof Error ? error.message : String(error);
535
+ const code = error?.code;
536
+ if (/not a git repository/i.test(message) || code === "ENOENT" || /no such file or directory/i.test(message)) {
537
+ return void 0;
538
+ }
539
+ throw error;
540
+ }
499
541
  }
500
542
  function repositoryHash(repoRoot) {
501
543
  return createHash("sha256").update(resolve(repoRoot).toLowerCase()).digest("hex").slice(0, 20);
502
544
  }
545
+ function bookHomeInRepository(repoRoot) {
546
+ const rootReal = canonicalizePath(repoRoot);
547
+ const bookHomeReal = canonicalizePath(resolveBookHome());
548
+ if (isOutside(rootReal, bookHomeReal)) return void 0;
549
+ const inside = relative(rootReal, bookHomeReal);
550
+ return inside === "" ? void 0 : inside.split(sep).join("/");
551
+ }
503
552
  async function writeWorkspaceTree(repoRoot, includeUntracked, signal) {
504
553
  const index = tempIndex();
505
554
  try {
506
555
  const env = { GIT_INDEX_FILE: index.path };
556
+ const bookHome = bookHomeInRepository(repoRoot);
557
+ const magic = bookHome === void 0 ? [] : ["--no-literal-pathspecs"];
558
+ const exclusion = bookHome === void 0 ? [] : [`:(top,literal,exclude)${bookHome}`];
507
559
  const head = (await git(repoRoot, ["rev-parse", "HEAD"], { signal })).stdout.trim();
508
560
  await git(repoRoot, ["read-tree", head], { env, signal });
509
- await git(repoRoot, includeUntracked ? ["add", "-A", "--", "."] : ["add", "-u", "--", "."], {
510
- env,
511
- signal
512
- });
561
+ await git(
562
+ repoRoot,
563
+ [...magic, "add", includeUntracked ? "-A" : "-u", "--", ".", ...exclusion],
564
+ {
565
+ env,
566
+ signal
567
+ }
568
+ );
513
569
  const tree = (await git(repoRoot, ["write-tree"], { env, signal })).stdout.trim();
514
- const status = (await git(repoRoot, ["status", "--porcelain=v1", "--untracked-files=all"], { signal })).stdout;
515
- return { head, tree, status };
570
+ const status = [
571
+ (await git(
572
+ repoRoot,
573
+ [
574
+ ...magic,
575
+ "status",
576
+ "--porcelain=v1",
577
+ "--untracked-files=all",
578
+ "--ignore-submodules=dirty",
579
+ ...bookHome === void 0 ? [] : ["--", ".", ...exclusion]
580
+ ],
581
+ { signal }
582
+ )).stdout
583
+ ];
584
+ if (bookHome !== void 0) {
585
+ status.push(
586
+ (await git(
587
+ repoRoot,
588
+ [
589
+ ...magic,
590
+ "status",
591
+ "--porcelain=v1",
592
+ "--untracked-files=no",
593
+ "--ignore-submodules=dirty",
594
+ "--",
595
+ `:(top,literal)${bookHome}`
596
+ ],
597
+ { signal }
598
+ )).stdout
599
+ );
600
+ }
601
+ return { head, tree, status: status.join("") };
516
602
  } finally {
517
603
  rmSync(index.dir, { recursive: true, force: true });
518
604
  }
@@ -744,14 +830,22 @@ async function unmergedPathsAfterFailure(repoRoot) {
744
830
  return [];
745
831
  }
746
832
  }
747
- async function applyVerifiedCandidate(snapshot, candidate, signal) {
833
+ async function applyVerifiedCandidate(snapshot, candidate, signal, options) {
834
+ return applyCandidate(snapshot, candidate, signal, DEFAULT_MAX_BUFFER_BYTES, options);
835
+ }
836
+ function stoppedApplyError(signal, step, outcome) {
837
+ const reason = typeof signal?.reason === "string" ? ` (${signal.reason})` : "";
838
+ return new Error(`The apply was stopped${reason} during ${step}. ${outcome}`);
839
+ }
840
+ async function applyCandidate(snapshot, candidate, signal, signingBuffer, options) {
841
+ const mutationSignal = options?.mutationSignal ?? signal;
748
842
  if (candidate.baseCommit !== snapshot.commit) {
749
843
  return {
750
844
  status: "conflicted",
751
845
  error: "Patch candidate base does not match the task snapshot."
752
846
  };
753
847
  }
754
- const actualHead = (await git(snapshot.repoRoot, ["rev-parse", candidate.headCommit])).stdout.trim();
848
+ const actualHead = (await git(snapshot.repoRoot, ["rev-parse", candidate.headCommit], { signal })).stdout.trim();
755
849
  if (actualHead !== candidate.headCommit) {
756
850
  return { status: "conflicted", error: "Patch candidate commit no longer resolves exactly." };
757
851
  }
@@ -776,16 +870,22 @@ async function applyVerifiedCandidate(snapshot, candidate, signal) {
776
870
  try {
777
871
  await precheckDelta(snapshot, patch, signal);
778
872
  } catch (error) {
873
+ const stopped = signal?.aborted === true;
874
+ if (stopped) {
875
+ throw stoppedApplyError(signal, "the patch pre-check", "No changes were applied.");
876
+ }
779
877
  return {
780
878
  status: "conflicted",
781
879
  error: `Patch pre-check failed; no changes were applied: ${error instanceof Error ? error.message : String(error)}`
782
880
  };
783
881
  }
882
+ const signingRunner = (command, args, options2) => run(command, args, options2, signingBuffer);
784
883
  if (!snapshot.dirty) {
785
884
  let signingPins;
786
885
  try {
787
- signingPins = await signingPinArgs(snapshot.repoRoot, hardenedRunner);
886
+ signingPins = await signingPinArgs(snapshot.repoRoot, signingRunner, signal);
788
887
  } catch (error) {
888
+ if (signal?.aborted) throw error;
789
889
  return {
790
890
  status: "conflicted",
791
891
  error: `Could not read the signing configuration for the cherry-pick: ${error instanceof Error ? error.message : String(error)}`
@@ -794,11 +894,10 @@ async function applyVerifiedCandidate(snapshot, candidate, signal) {
794
894
  try {
795
895
  await git(snapshot.repoRoot, [...signingPins, "cherry-pick", candidate.headCommit], {
796
896
  timeoutMs: CHERRY_PICK_TIMEOUT_MS,
797
- signal
897
+ signal: mutationSignal
798
898
  });
799
- const appliedCommit = (await git(snapshot.repoRoot, ["rev-parse", "HEAD"], { signal })).stdout.trim();
800
- return { status: "applied", commit: appliedCommit };
801
899
  } catch (error) {
900
+ const stopped = mutationSignal?.aborted === true;
802
901
  const unmergedPaths = await unmergedPathsAfterFailure(snapshot.repoRoot);
803
902
  let abortError;
804
903
  try {
@@ -806,17 +905,36 @@ async function applyVerifiedCandidate(snapshot, candidate, signal) {
806
905
  } catch (abortFailure) {
807
906
  abortError = abortFailure instanceof Error ? abortFailure.message : String(abortFailure);
808
907
  }
908
+ if (stopped) {
909
+ throw stoppedApplyError(
910
+ mutationSignal,
911
+ "the cherry-pick",
912
+ [
913
+ abortError ? `The rollback did not complete (${abortError}), so this repository may be left mid-cherry-pick: finish it with git cherry-pick --continue or undo it with git cherry-pick --abort.` : "The cherry-pick was rolled back.",
914
+ unmergedPaths.length > 0 ? `Unmerged paths: ${unmergedPaths.join(", ")}.` : void 0
915
+ ].filter(Boolean).join(" ")
916
+ );
917
+ }
809
918
  return cherryPickFailureResult({
810
919
  pickError: error instanceof Error ? error.message : String(error),
811
920
  abortError,
812
921
  unmergedPaths
813
922
  });
814
923
  }
924
+ const appliedCommit = (await git(snapshot.repoRoot, ["rev-parse", "HEAD"])).stdout.trim();
925
+ return { status: "applied", commit: appliedCommit };
815
926
  }
816
927
  try {
817
- await git(snapshot.repoRoot, ["apply", "-"], { input: patch, signal });
928
+ await git(snapshot.repoRoot, ["apply", "-"], { input: patch, signal: mutationSignal });
818
929
  return { status: "applied", commit: candidate.headCommit };
819
930
  } catch (error) {
931
+ if (mutationSignal?.aborted) {
932
+ throw stoppedApplyError(
933
+ mutationSignal,
934
+ "git apply",
935
+ "git apply was cut short, so check git status before applying again: the working tree may be partly changed."
936
+ );
937
+ }
820
938
  return {
821
939
  status: "conflicted",
822
940
  error: `Patch application failed: ${error instanceof Error ? error.message : String(error)}`
@@ -944,4 +1062,4 @@ export {
944
1062
  removePersistentJobFiles,
945
1063
  removePersistentRunnerFiles
946
1064
  };
947
- //# sourceMappingURL=chunk-YC6W4EJH.js.map
1065
+ //# sourceMappingURL=chunk-JGF5HLSI.js.map