@letrquan/book 0.2.0 → 0.3.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/CHANGELOG.md +2747 -40
- package/README.md +132 -1311
- package/dist/additional-roots-H7UNAEHS.js +31 -0
- package/dist/{app-MOXXWEOY.js → app-GE4OHJOC.js} +5740 -4045
- package/dist/app-GE4OHJOC.js.map +1 -0
- package/dist/{chunk-5GDZ22YP.js → chunk-224FJZLH.js} +99 -108
- package/dist/chunk-224FJZLH.js.map +1 -0
- package/dist/chunk-2AAOF5B5.js +28 -0
- package/dist/chunk-2AAOF5B5.js.map +1 -0
- package/dist/{chunk-CAGOB2N4.js → chunk-4AS7LKKQ.js} +6 -4
- package/dist/chunk-4AS7LKKQ.js.map +1 -0
- package/dist/chunk-525CAZPY.js +120 -0
- package/dist/chunk-525CAZPY.js.map +1 -0
- package/dist/{chunk-URLQAXTR.js → chunk-56SIEMDX.js} +2 -2
- package/dist/chunk-56SIEMDX.js.map +1 -0
- package/dist/{chunk-XIFFTATU.js → chunk-5YHTKWJN.js} +5420 -4031
- package/dist/chunk-5YHTKWJN.js.map +1 -0
- package/dist/chunk-62QL5QUP.js +283 -0
- package/dist/chunk-62QL5QUP.js.map +1 -0
- package/dist/{chunk-KWHQ2XXL.js → chunk-67UZEWWF.js} +3 -3
- package/dist/chunk-67UZEWWF.js.map +1 -0
- package/dist/chunk-6OQQOMZR.js +785 -0
- package/dist/chunk-6OQQOMZR.js.map +1 -0
- package/dist/{chunk-GTVB4HEM.js → chunk-AQEQX4FF.js} +344 -315
- package/dist/chunk-AQEQX4FF.js.map +1 -0
- package/dist/chunk-C7ADUNPR.js +273 -0
- package/dist/chunk-C7ADUNPR.js.map +1 -0
- package/dist/{chunk-E75IPST5.js → chunk-EFJ2NH6E.js} +12 -58
- package/dist/chunk-EFJ2NH6E.js.map +1 -0
- package/dist/{chunk-5KLVH3PY.js → chunk-HQI7NYGX.js} +5 -3
- package/dist/chunk-HQI7NYGX.js.map +1 -0
- package/dist/chunk-IME7R2UI.js +6541 -0
- package/dist/chunk-IME7R2UI.js.map +1 -0
- package/dist/chunk-J4F2QLM2.js +273 -0
- package/dist/chunk-J4F2QLM2.js.map +1 -0
- package/dist/{chunk-MAQXCUR4.js → chunk-J62VZYOB.js} +444 -112
- package/dist/chunk-J62VZYOB.js.map +1 -0
- package/dist/chunk-JTPOI3KU.js +826 -0
- package/dist/chunk-JTPOI3KU.js.map +1 -0
- package/dist/{chunk-WKBO5R4O.js → chunk-KBQS35OV.js} +2 -2
- package/dist/chunk-KBQS35OV.js.map +1 -0
- package/dist/chunk-KXAYUIKP.js +90 -0
- package/dist/chunk-KXAYUIKP.js.map +1 -0
- package/dist/{chunk-VZQXX3WW.js → chunk-QYBD22H5.js} +1 -1
- package/dist/chunk-QYBD22H5.js.map +1 -0
- package/dist/{chunk-V4BOYR52.js → chunk-RFJ6Q6LE.js} +763 -198
- package/dist/chunk-RFJ6Q6LE.js.map +1 -0
- package/dist/{chunk-7N4J557C.js → chunk-RT5SATF7.js} +1 -1
- package/dist/chunk-RT5SATF7.js.map +1 -0
- package/dist/{chunk-C7D4NJ2D.js → chunk-S4JMID4G.js} +68 -26
- package/dist/chunk-S4JMID4G.js.map +1 -0
- package/dist/{chunk-ANSJPYGI.js → chunk-SOW4WRQP.js} +3 -3
- package/dist/chunk-SOW4WRQP.js.map +1 -0
- package/dist/{chunk-J7MIZ5CA.js → chunk-TLX3H2CL.js} +518 -79
- package/dist/chunk-TLX3H2CL.js.map +1 -0
- package/dist/{chunk-DGMQEI6Y.js → chunk-VPNLCMVR.js} +22 -9
- package/dist/chunk-VPNLCMVR.js.map +1 -0
- package/dist/{chunk-BTZZUCLI.js → chunk-W3XFHEF4.js} +173 -29
- package/dist/chunk-W3XFHEF4.js.map +1 -0
- package/dist/{chunk-574C6MQR.js → chunk-X4NJT436.js} +130 -60
- package/dist/chunk-X4NJT436.js.map +1 -0
- package/dist/chunk-X66I2NQO.js +137 -0
- package/dist/chunk-X66I2NQO.js.map +1 -0
- package/dist/chunk-XHAO3K7I.js +18 -0
- package/dist/chunk-XHAO3K7I.js.map +1 -0
- package/dist/{chunk-3U5IBM24.js → chunk-Y2VHN7TC.js} +2 -2
- package/dist/chunk-Y2VHN7TC.js.map +1 -0
- package/dist/chunk-YC6W4EJH.js +947 -0
- package/dist/chunk-YC6W4EJH.js.map +1 -0
- package/dist/{chunk-24AXE6SP.js → chunk-ZPSZZUWF.js} +1 -1
- package/dist/chunk-ZPSZZUWF.js.map +1 -0
- package/dist/{command-approvals-6Y57VCWZ.js → command-approvals-VBIU6BLM.js} +6 -5
- package/dist/context-CBOBDY5Q.js +38 -0
- package/dist/{hook-approvals-CTUFKEJT.js → hook-approvals-ZVT4V7IM.js} +5 -5
- package/dist/index.js +996 -155
- package/dist/index.js.map +1 -1
- package/dist/{interactive-assets-CCAAWVKH.js → interactive-assets-T4I4SEHU.js} +13 -9
- package/dist/interactive-assets-T4I4SEHU.js.map +1 -0
- package/dist/job-runner.js +159 -111
- package/dist/job-runner.js.map +1 -1
- package/dist/job-supervisor.d.ts +2 -0
- package/dist/job-supervisor.js +112 -0
- package/dist/job-supervisor.js.map +1 -0
- package/dist/loader-S77OVTHH.js +18 -0
- package/dist/{mcp-approvals-WNPH65BK.js → mcp-approvals-HJ2VDYMC.js} +5 -5
- package/dist/{mcp-config-YIJIES2C.js → mcp-config-RNB5LEVE.js} +3 -3
- package/dist/memory-store-DZUT5HUH.js +75 -0
- package/dist/{model-window-store-YFVNXCIP.js → model-window-store-MTSQ5JDJ.js} +8 -4
- package/dist/openai-compatible-2OB54YST.js +14 -0
- package/dist/{permission-approvals-76PVREFD.js → permission-approvals-YLV6UC5W.js} +5 -5
- package/dist/permissions-DHOKF3W2.js +45 -0
- package/dist/sandbox-WRYTZH3D.js +41 -0
- package/dist/{scrollback-LUKLIBVI.js → scrollback-35ME2GFX.js} +32 -22
- package/dist/scrollback-35ME2GFX.js.map +1 -0
- package/dist/sdk.d.ts +2219 -2361
- package/dist/sdk.js +53 -38
- package/dist/sdk.js.map +1 -1
- package/dist/settings-loader-JUNPGAAM.js +35 -0
- package/dist/settings-loader-JUNPGAAM.js.map +1 -0
- package/dist/{shell-selection-V6QAYXPX.js → shell-selection-CDHGJ4GC.js} +2 -2
- package/dist/shell-selection-CDHGJ4GC.js.map +1 -0
- package/package.json +23 -22
- package/dist/app-MOXXWEOY.js.map +0 -1
- package/dist/chunk-24AXE6SP.js.map +0 -1
- package/dist/chunk-3U5IBM24.js.map +0 -1
- package/dist/chunk-574C6MQR.js.map +0 -1
- package/dist/chunk-5GDZ22YP.js.map +0 -1
- package/dist/chunk-5KLVH3PY.js.map +0 -1
- package/dist/chunk-7N4J557C.js.map +0 -1
- package/dist/chunk-A7JLBK2Y.js +0 -3506
- package/dist/chunk-A7JLBK2Y.js.map +0 -1
- package/dist/chunk-ANSJPYGI.js.map +0 -1
- package/dist/chunk-BTZZUCLI.js.map +0 -1
- package/dist/chunk-C7D4NJ2D.js.map +0 -1
- package/dist/chunk-CAGOB2N4.js.map +0 -1
- package/dist/chunk-DGMQEI6Y.js.map +0 -1
- package/dist/chunk-E75IPST5.js.map +0 -1
- package/dist/chunk-EYGD6X2T.js +0 -180
- package/dist/chunk-EYGD6X2T.js.map +0 -1
- package/dist/chunk-GTVB4HEM.js.map +0 -1
- package/dist/chunk-J7MIZ5CA.js.map +0 -1
- package/dist/chunk-KWHQ2XXL.js.map +0 -1
- package/dist/chunk-L6LK2QIQ.js +0 -657
- package/dist/chunk-L6LK2QIQ.js.map +0 -1
- package/dist/chunk-MAQXCUR4.js.map +0 -1
- package/dist/chunk-S4XL7HOM.js +0 -402
- package/dist/chunk-S4XL7HOM.js.map +0 -1
- package/dist/chunk-URLQAXTR.js.map +0 -1
- package/dist/chunk-V4BOYR52.js.map +0 -1
- package/dist/chunk-VZQXX3WW.js.map +0 -1
- package/dist/chunk-WKBO5R4O.js.map +0 -1
- package/dist/chunk-XIFFTATU.js.map +0 -1
- package/dist/context-ZC5IFEAX.js +0 -25
- package/dist/interactive-assets-CCAAWVKH.js.map +0 -1
- package/dist/loader-4IDS2RIE.js +0 -15
- package/dist/openai-compatible-RHZUBHV6.js +0 -10
- package/dist/permissions-CHCKTFQQ.js +0 -28
- package/dist/sandbox-CNVAQDA7.js +0 -23
- package/dist/scrollback-LUKLIBVI.js.map +0 -1
- package/dist/settings-loader-M2QP5TX2.js +0 -21
- package/patches/ink+6.8.0.patch +0 -13
- package/scripts/apply-ink-patch.mjs +0 -29
- package/scripts/ink-patch.mjs +0 -59
- package/scripts/verify-ink-patch.mjs +0 -11
- /package/dist/{command-approvals-6Y57VCWZ.js.map → additional-roots-H7UNAEHS.js.map} +0 -0
- /package/dist/{context-ZC5IFEAX.js.map → command-approvals-VBIU6BLM.js.map} +0 -0
- /package/dist/{hook-approvals-CTUFKEJT.js.map → context-CBOBDY5Q.js.map} +0 -0
- /package/dist/{loader-4IDS2RIE.js.map → hook-approvals-ZVT4V7IM.js.map} +0 -0
- /package/dist/{mcp-approvals-WNPH65BK.js.map → loader-S77OVTHH.js.map} +0 -0
- /package/dist/{mcp-config-YIJIES2C.js.map → mcp-approvals-HJ2VDYMC.js.map} +0 -0
- /package/dist/{model-window-store-YFVNXCIP.js.map → mcp-config-RNB5LEVE.js.map} +0 -0
- /package/dist/{openai-compatible-RHZUBHV6.js.map → memory-store-DZUT5HUH.js.map} +0 -0
- /package/dist/{permission-approvals-76PVREFD.js.map → model-window-store-MTSQ5JDJ.js.map} +0 -0
- /package/dist/{permissions-CHCKTFQQ.js.map → openai-compatible-2OB54YST.js.map} +0 -0
- /package/dist/{sandbox-CNVAQDA7.js.map → permission-approvals-YLV6UC5W.js.map} +0 -0
- /package/dist/{settings-loader-M2QP5TX2.js.map → permissions-DHOKF3W2.js.map} +0 -0
- /package/dist/{shell-selection-V6QAYXPX.js.map → sandbox-WRYTZH3D.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/jobs/persistent-store.ts","../src/agents/git-isolation.ts","../src/agents/git-signing.ts","../src/fs-contention.ts"],"sourcesContent":["import {\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n renameSync,\n rmSync,\n writeFileSync,\n} from 'node:fs';\nimport { createHash, randomUUID } from 'node:crypto';\nimport { dirname, join } from 'node:path';\nimport { repositoryHash } from '../agents/git-isolation.js';\nimport { systemClock, type Clock } from '../clock.js';\nimport { FILE_CONTENTION_CODES, sleepSync } from '../fs-contention.js';\nimport type {\n BackgroundShellNotify,\n BackgroundShellStatus,\n CommandExecution,\n} from '../types/runtime.js';\nimport { resolveBookHome } from '../book-home.js';\n\nexport interface PersistentShellSpec {\n version: 1;\n id: string;\n command: string;\n effectiveCommand: string;\n /**\n * Present only for sandboxed commands. Specs written before this field\n * existed simply lack it and fall back to the shell path, which is what they\n * were already doing.\n */\n exec?: CommandExecution;\n title: string;\n workdir: string;\n env: Record<string, string>;\n sandboxed: boolean;\n notify: BackgroundShellNotify;\n timeoutMs?: number;\n parentSessionId?: string;\n rootRunId?: string;\n parentRunId?: string;\n token: string;\n tokenHash: string;\n recordPath: string;\n controlPath: string;\n outputPath: string;\n maxLogBytes: number;\n}\n\nexport interface PersistentShellState {\n version: 1;\n revision: number;\n id: string;\n command: string;\n title: string;\n workdir: string;\n status: BackgroundShellStatus;\n notify: BackgroundShellNotify;\n sandboxed: boolean;\n runnerPid: number;\n childPid?: number;\n tokenHash: string;\n startedAt: number;\n finishedAt?: number;\n heartbeatAt: number;\n /** Incremented whenever the bounded log is rewritten to its tail. */\n outputRotationSequence?: number;\n truncatedBytes?: number;\n /**\n * Non-terminal record writes the runner could not make. The next write that lands carries\n * the count, so a lagging record can be diagnosed without the note landing in the job's log.\n */\n recordWriteFailures?: number;\n /** The latest of those failures, such as `heartbeat write failed (EPERM)`. */\n lastRecordWriteError?: string;\n timeoutMs?: number;\n deadlineAt?: number;\n exitCode?: number | null;\n signal?: NodeJS.Signals | string | null;\n stopReason?: string;\n parentSessionId?: string;\n rootRunId?: string;\n parentRunId?: string;\n outputPath: string;\n controlPath: string;\n completionSequence: number;\n completionDeliveredSequence: number;\n completionAcknowledgedSequence: number;\n}\n\nexport interface PersistentJobPaths {\n root: string;\n records: string;\n controls: string;\n logs: string;\n specs: string;\n}\n\nexport function persistentJobPaths(\n workspace: string,\n root = join(resolveBookHome(), 'jobs'),\n): PersistentJobPaths {\n const repositoryRoot = join(root, repositoryHash(workspace));\n return {\n root: repositoryRoot,\n records: join(repositoryRoot, 'records'),\n controls: join(repositoryRoot, 'controls'),\n logs: join(repositoryRoot, 'logs'),\n specs: join(repositoryRoot, 'specs'),\n };\n}\n\nexport function ensurePersistentJobPaths(paths: PersistentJobPaths): void {\n for (const path of [paths.root, paths.records, paths.controls, paths.logs, paths.specs]) {\n mkdirSync(path, { recursive: true });\n }\n}\n\n/**\n * How long a contended rename may block before it rethrows, unless the caller asks for longer.\n * The shell manager and memory extraction write from the TUI's own process, where a synchronous\n * wait freezes rendering, so the default is small. The detached runner has no UI and asks for\n * more.\n */\nconst DEFAULT_RENAME_RETRY_BUDGET_MS = 100;\nconst RENAME_RETRY_STEP_MS = 10;\n\ninterface RenameRetryOptions {\n budgetMs?: number;\n platform?: NodeJS.Platform;\n clock?: Clock;\n rename?: (from: string, to: string) => void;\n sleep?: (milliseconds: number) => void;\n}\n\n/** `renameSync`, retried while Windows reports the target as in use, for at most `budgetMs`. */\nexport function renameWithContentionRetry(\n from: string,\n to: string,\n options: RenameRetryOptions = {},\n): void {\n const rename = options.rename ?? renameSync;\n const platform = options.platform ?? process.platform;\n const clock = options.clock ?? systemClock;\n const sleep = options.sleep ?? sleepSync;\n const deadline = clock.monotonicNowMs() + (options.budgetMs ?? DEFAULT_RENAME_RETRY_BUDGET_MS);\n for (;;) {\n try {\n rename(from, to);\n return;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code ?? '';\n const remainingMs = deadline - clock.monotonicNowMs();\n if (platform !== 'win32' || !FILE_CONTENTION_CODES.has(code) || remainingMs <= 0) {\n throw error;\n }\n sleep(Math.min(RENAME_RETRY_STEP_MS, remainingMs));\n }\n }\n}\n\ninterface WriteJsonAtomicOptions {\n /** How long a contended rename may block; see `DEFAULT_RENAME_RETRY_BUDGET_MS`. */\n renameRetryBudgetMs?: number;\n}\n\nexport function writeJsonAtomic(\n path: string,\n value: unknown,\n options: WriteJsonAtomicOptions = {},\n): void {\n mkdirSync(dirname(path), { recursive: true });\n const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;\n try {\n writeFileSync(temporary, JSON.stringify(value, null, 2), { encoding: 'utf8', mode: 0o600 });\n renameWithContentionRetry(temporary, path, { budgetMs: options.renameRetryBudgetMs });\n } catch (error) {\n try {\n rmSync(temporary, { force: true });\n } catch {\n // The write's own error is the one to report; a stray temp file is harmless.\n }\n throw error;\n }\n}\n\nexport function readJsonFile<T>(path: string): T | undefined {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as T;\n } catch {\n return undefined;\n }\n}\n\n/**\n * The spec at `path`, when it is one a runner or supervisor may execute. Both read it from disk,\n * so both apply these checks: a spec whose token does not match its hash was not written by Book,\n * and a spec that claims `sandboxed` without a sandboxed argv would run the raw command unconfined\n * while the job panel said otherwise. `effectiveCommand` is the raw user command now that\n * sandboxing rides on `exec`, so that combination is refused rather than silently downgraded; the\n * reverse is legitimate, since an unsandboxed command also carries an `exec` when the session shell\n * is spawned as argv (Git Bash or PowerShell on Windows).\n */\nexport function loadPersistentShellSpec(\n path: string,\n): { spec: PersistentShellSpec } | { error: string } {\n const spec = readJsonFile<PersistentShellSpec>(path);\n if (\n !spec ||\n spec.version !== 1 ||\n typeof spec.token !== 'string' ||\n typeof spec.tokenHash !== 'string' ||\n createHash('sha256').update(spec.token).digest('hex') !== spec.tokenHash\n ) {\n return { error: 'Invalid persistent shell specification.' };\n }\n if (spec.sandboxed && !spec.exec) {\n return {\n error:\n 'Invalid persistent shell specification: sandboxed is set but no sandboxed argv is present.',\n };\n }\n return { spec };\n}\n\nexport function listPersistentStates(paths: PersistentJobPaths): PersistentShellState[] {\n if (!existsSync(paths.records)) return [];\n return readdirSync(paths.records)\n .filter((name) => name.endsWith('.json'))\n .map((name) => readJsonFile<PersistentShellState>(join(paths.records, name)))\n .filter((state): state is PersistentShellState => state?.version === 1);\n}\n\nexport function removePersistentJobFiles(paths: PersistentJobPaths, id: string): void {\n for (const path of [\n join(paths.records, `${id}.json`),\n join(paths.controls, `${id}.json`),\n join(paths.logs, `${id}.log`),\n join(paths.specs, `${id}.json`),\n ]) {\n rmSync(path, { force: true });\n }\n}\n\nexport function removePersistentRunnerFiles(paths: PersistentJobPaths, id: string): void {\n for (const path of [join(paths.controls, `${id}.json`), join(paths.specs, `${id}.json`)]) {\n rmSync(path, { force: true });\n }\n}\n","import { createHash, randomUUID } from 'crypto';\nimport { execFile, spawn } from 'child_process';\nimport { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from 'fs';\nimport { tmpdir } from 'os';\nimport { dirname, join, resolve } from 'path';\nimport type { AgentApplyResult, AgentRecord, AgentSnapshot, PatchCandidate } from './types.js';\nimport { buildChildEnv } from '../child-env.js';\nimport { resolveBookHome } from '../book-home.js';\nimport { hardenedGitArgs, hardenedGitEnv, HARDENED_DIFF_ARGS } from '../tools/git.js';\nimport {\n signingPinArgs,\n type HardenedRunner,\n type RunOptions,\n type RunResult,\n} from './git-signing.js';\n\n/**\n * Every git command this module runs: a synthetic snapshot, an agent's worktree, the agent's\n * commit, and the cherry-pick or patch that applies the result.\n *\n * The argv goes through {@link hardenedGitArgs} and the environment carries a pager and a\n * credential prompt it cannot wait on, here as in the read-only Git tools. What is different is\n * the one that matters for this module: several of these are writes — `commit`, `cherry-pick`,\n * `worktree add`, `update-ref` — and a checkout could own a hook, an `fsmonitor`, or a diff driver\n * for any of them. None of this is the operator's work, and none of it is a call anything asks\n * before, so a repository can run a program by delegating an agent (#348). Turning hooks off for\n * it is the decision here; the operator's own commits, `GitCommit` included, keep theirs.\n *\n * It is applied inside {@link run} rather than at each call site, so a new call site cannot forget\n * it, and `git-signing.ts` is given that function rather than a runner of its own for the same\n * reason: a fix here reaches every read that decides how a commit is signed.\n *\n * **What is still followed, deliberately (#357).** Hooks, `core.fsmonitor` and the diff drivers\n * are off, and the agent's own commit is unsigned, but three routes a checkout owns remain open,\n * because closing them would change the content Book manages:\n *\n * - **`.gitattributes` clean/smudge and process filters.** They rewrite the bytes on checkout and\n * on add, and a process filter is a program the checkout names. `git-lfs` needs them: without\n * them a pointer file is a pointer file rather than the large blob it stands for.\n * - **`.gitattributes` merge drivers.** The cherry-pick that applies an agent's result merges\n * through whatever the checkout names, for the same reason.\n * - **Lazy fetch in a partial clone.** `--filter=blob:none` means a missing blob is fetched on\n * demand, through the `credential.helper` and `core.sshCommand` that fetch needs.\n *\n * So a checkout can still have Book run a program through one of those three, and that is a known\n * gap rather than an oversight: it is tracked as issue 357, and the decision belongs to whoever\n * clones a repository that carries a `.gitattributes` naming a program.\n *\n * Three properties of a child are load-bearing for the reads this module makes, and all three\n * live here rather than at the call sites because each one has a way of failing *open*:\n *\n * - **A non-numeric error never resolves.** `allowExitCodes` names real exit codes, so only a real\n * exit code can satisfy it. A `maxBuffer` overrun, a missing `git`, a signal or a timeout all\n * arrive with a string code, a null code, or no code at all, and each of them used to be read as\n * exit 1 — which `git config --get-regexp` uses for \"no match\". A signing read that resolved on a\n * truncated answer would pin nothing and let a repository's configuration through untouched. A\n * child killed by an abort or by the timeout below has no exit code at all, which is a failure\n * and not a 1: `removeAgentWorktree` accepts 1 and 128 as \"already gone\", and reading a kill as\n * either would report a cleanup that never happened as one that did.\n * - **stdin is closed, not an open pipe.** A signer that reads stdin — an ssh key with a\n * passphrase and no agent, a loopback pinentry — otherwise waits on a pipe nobody will ever\n * write to, for as long as the session lasts. Only the calls that write a patch get one.\n * - **A call that can start a program is bounded, and can be stopped.** A signing program that\n * never returns is the same hang from the other side, so every call carries\n * {@link DEFAULT_GIT_TIMEOUT_MS} unless it names its own, and the agent controller's signal is\n * threaded through the calls that have one so a stopped agent stops its git too (#357).\n *\n * And the environment is not the operator's shell environment: an ambient `GIT_DIR` or\n * `GIT_INDEX_FILE` would redirect Book's own writes into another checkout, which is a write\n * nobody asked about in a repository nobody named. See {@link internalGitEnv}.\n */\nfunction run(command: string, args: string[], options: RunOptions): Promise<RunResult> {\n const env = internalGitEnv(options.env);\n const timeoutMs = options.timeoutMs ?? DEFAULT_GIT_TIMEOUT_MS;\n const signal = options.signal;\n // An already-aborted signal must not start the child at all: the work it was asked to do is over,\n // and a child started now would outlive the abort that was supposed to stop it — a `worktree\n // add` that keeps going after its agent was stopped writes a checkout and a branch nothing asked\n // for (#357).\n if (signal?.aborted) {\n return Promise.reject(new Error(stoppedDescription(command, args, 'aborted', null, timeoutMs)));\n }\n if (options.input !== undefined) {\n return new Promise((resolvePromise, reject) => {\n const child = spawn(command, args, {\n cwd: options.cwd,\n env,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n let stdout = '';\n let stderr = '';\n // `close` and a stdin write can both report the same failed command, and a spawn that\n // never happened reports twice. One outcome, whichever arrives first (#351).\n let settled = false;\n let cancelEscalation: (() => void) | undefined;\n const settle = (outcome: () => void) => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n cancelEscalation?.();\n outcome();\n };\n // How this child was stopped, when it was: the reason is what the message says, and a\n // `close` handler that only sees `signal: SIGTERM` cannot tell a timeout from an abort.\n let stoppedBy: 'timeout' | 'aborted' | undefined;\n const stopChild = (reason: 'timeout' | 'aborted') => {\n if (stoppedBy === undefined) stoppedBy = reason;\n cancelEscalation = stopEscalating(child, () =>\n settle(() =>\n reject(\n new Error(stoppedDescription(command, args, stoppedBy, child.signalCode, timeoutMs)),\n ),\n ),\n );\n };\n const onAbort = () => stopChild('aborted');\n const timer = timeoutMs > 0 ? setTimeout(() => stopChild('timeout'), timeoutMs) : undefined;\n timer?.unref?.();\n signal?.addEventListener('abort', onAbort, { once: true });\n // A stdin failure worth acting on is remembered rather than acted on, because `close` is\n // the only event that knows the exit code, and the exit code is what decides. Settling here\n // instead would reject before git had written the stderr that explains the failure.\n let stdinError: Error | undefined;\n child.stdout.on('data', (chunk) => (stdout += chunk.toString()));\n child.stderr.on('data', (chunk) => (stderr += chunk.toString()));\n // A read stream can fail on its own — a pipe closed as the child goes, a host under\n // pressure — and a stream with no `error` listener raises that on the host's event loop,\n // which takes down whatever session was mid-flow. Remembered and weighed by `close` for the\n // same reason the stdin error is: the exit code is the thing that decides.\n let outputError: Error | undefined;\n child.stdout.on('error', (error: Error) => (outputError ??= error));\n child.stderr.on('error', (error: Error) => (outputError ??= error));\n child.on('error', (error) => {\n if (timer) clearTimeout(timer);\n signal?.removeEventListener('abort', onAbort);\n settle(() => reject(error));\n });\n child.on('close', (codeValue, closedBySignal) => {\n if (timer) clearTimeout(timer);\n signal?.removeEventListener('abort', onAbort);\n settle(() => {\n // A child with no exit code has none to reason about, so it is a failure whatever\n // `allowExitCodes` names — including the timeout this call set for it and the abort\n // that stopped it. `codeValue ?? 1` made every kill indistinguishable from the exit 1\n // git uses for \"no such branch\" (#357).\n const code = typeof codeValue === 'number' ? codeValue : undefined;\n if (code === undefined) {\n reject(\n new Error(\n stderr.trim() ||\n stdout.trim() ||\n outputError?.message ||\n stdinError?.message ||\n stoppedDescription(command, args, stoppedBy, closedBySignal, timeoutMs),\n ),\n );\n return;\n }\n if (code === 0 || options.allowExitCodes?.includes(code)) {\n resolvePromise({ stdout, stderr, code });\n return;\n }\n // What git said comes first: a stdin error on a failed command is a consequence of it,\n // and its own message is the one with no information in it. The stdin error is the\n // fallback for a git that exited without a word.\n reject(\n new Error(\n stderr.trim() ||\n stdout.trim() ||\n stdinError?.message ||\n `${command} ${subcommandOf(args)} failed (${code})`,\n ),\n );\n });\n });\n // Never removed, and attached before the write: git that exits without reading the patch —\n // not a repository, a rejected argument — leaves that write to fail with EPIPE, and a\n // stream carrying no `error` listener raises the failure on the host's event loop instead\n // of on this promise. `EPIPE` and `EOF` mean git was gone before it read, which the exit\n // code has already accounted for and is not news; anything else is a real failure, and is\n // kept for `close` to weigh.\n child.stdin.on('error', (error: NodeJS.ErrnoException) => {\n if (error.code === 'EPIPE' || error.code === 'EOF') return;\n stdinError ??= error;\n });\n child.stdin.end(options.input);\n });\n }\n\n return new Promise((resolvePromise, reject) => {\n let settled = false;\n let cancelEscalation: (() => void) | undefined;\n // Both closures below read `timer`, which is assigned after `child`: `execFile`'s callback is\n // always asynchronous, so nothing settles before there is a timer to clear.\n const settle = (outcome: () => void) => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n signal?.removeEventListener('abort', onAbort);\n cancelEscalation?.();\n outcome();\n };\n let stoppedBy: 'timeout' | 'aborted' | undefined;\n // The abort is handled here rather than by `execFile`'s own `signal` option, because that one\n // sends SIGTERM, calls straight back, and is finished with the child: a git that traps or\n // ignores SIGTERM, or a grandchild of its own holding the output pipe, outlives the rejection\n // that was supposed to stop it (#357). Both paths stop a child the same way — see\n // {@link stopEscalating}.\n const stopCall = (reason: 'timeout' | 'aborted') => {\n if (stoppedBy === undefined) stoppedBy = reason;\n cancelEscalation = stopEscalating(child, () =>\n settle(() =>\n reject(\n new Error(\n failureDescription(\n { killed: true },\n '',\n command,\n args,\n timeoutMs,\n reason === 'aborted',\n ),\n ),\n ),\n ),\n );\n };\n const onAbort = () => stopCall('aborted');\n const child = execFile(\n command,\n args,\n {\n cwd: options.cwd,\n env,\n encoding: 'utf8',\n // One buffer for every call: a signing read that overruns it has to fail rather than\n // resolve on a prefix, and this size is what makes that impractical rather than what makes\n // it correct.\n maxBuffer: 50 * 1024 * 1024,\n // No `timeout` and no `signal` option here. `execFile`'s own timeout kills the child but\n // still waits for its output to close, and a grandchild holding that pipe — a shell script,\n // a `credential.helper` that started something of its own — means it never does, so the\n // callback that would report the timeout may never arrive; its `signal` sends SIGTERM and\n // then stops looking. The timer below bounds the call and `stopEscalating` ends the child.\n },\n (error, stdout, stderr) => {\n // Node reports a failure three ways and only one of them is an exit code: `killed` and\n // `signal` cover a timeout and a signal, and a `maxBuffer` overrun or a missing binary\n // arrives as a string in `code`. None of those may resolve as if it were the \"no match\"\n // that exit 1 means, so an exit code is used only where there is one.\n const code = exitCodeOf(error);\n if (code === undefined) {\n settle(() =>\n reject(\n new Error(\n failureDescription(\n error,\n stderr || stdout,\n command,\n args,\n timeoutMs,\n stoppedBy === 'aborted',\n ),\n ),\n ),\n );\n return;\n }\n if (code === 0 || options.allowExitCodes?.includes(code)) {\n settle(() => resolvePromise({ stdout, stderr, code }));\n return;\n }\n settle(() =>\n reject(\n new Error(\n // git's own words first, because they are the ones that say what went wrong. Then the\n // composed message, and not Node's: with no output to prefer, Node's is the whole argv\n // — hardening pairs included — which names `-c` where a reader needs the subcommand,\n // and says nothing that the exit code does not (#357).\n stderr.trim() || stdout.trim() || `${command} ${subcommandOf(args)} failed (${code})`,\n ),\n ),\n );\n },\n );\n const timer = timeoutMs > 0 ? setTimeout(() => stopCall('timeout'), timeoutMs) : undefined;\n timer?.unref?.();\n signal?.addEventListener('abort', onAbort, { once: true });\n // `execFile` leaves the child's stdin an open pipe that nothing will ever write to, and a\n // child that reads it waits for an end that never comes: an ssh signer asking for a passphrase,\n // a pinentry on a loopback socket. Closing it now is what makes such a read fail, which is\n // what a command Book did not choose to read input from should do. (Checked against a real\n // git: a program that reads stdin to the end otherwise holds its call open indefinitely.)\n child.stdin?.end();\n });\n}\n\n/**\n * How long any call in this module may take before it is killed.\n *\n * Generous on purpose: the longest call here is the cherry-pick that applies an agent's result, on\n * a large repository, and killing it costs the operator a re-apply. What this buys is that a git\n * which never exits — a signing program waiting on an agent that is not there, a filesystem that\n * has stopped answering, a `credential.helper` prompt Book cannot see — cannot hold a call, and\n * through it a session, open forever (#357). The cherry-pick still names its own, longer budget.\n */\nconst DEFAULT_GIT_TIMEOUT_MS = 120_000;\n\n/**\n * How long a signalled child has to be on its way out before it is killed outright.\n *\n * Two seconds is far longer than git needs to unwind — to delete the `index.lock` it took, to\n * roll a half-written ref back, to leave a worktree's administrative directory consistent — and\n * far shorter than a caller that has already been bounded by {@link DEFAULT_GIT_TIMEOUT_MS} should\n * then sit waiting. This is only the shape of the escalation, not the budget of the call.\n */\nconst KILL_GRACE_MS = 2_000;\n\n/** The part of a child process this module has to be able to stop and then ask about. */\ninterface StoppableChild {\n kill(signal: NodeJS.Signals): boolean;\n /** The exit code, or `null` while the child is running. */\n exitCode: number | null;\n /** The signal that killed it, or `null` while it has not been killed. */\n signalCode: NodeJS.Signals | null;\n}\n\n/**\n * Stop a child that has to end now — SIGTERM first, then SIGKILL only if it is still alive when\n * the grace is up — and hand back how to call that off.\n *\n * **SIGTERM first is the whole point, and skipping it was a bug of its own.** A mutating git takes\n * `.git/index.lock` for as long as it runs — `cherry-pick`, `worktree add`, `add -A`,\n * `update-ref` all do — and releases it on the way out, which is the only thing that releases it.\n * `SIGKILL` gives the process no way out, so a call bounded by being killed outright left a lock\n * behind on the operator's repository, and the next call in it — the cleanup, the operator's next\n * commit — failed on a lock no process is holding (#357). It is also what a program gets the\n * chance to be a program rather than be reaped: a signing helper or a filter that releases\n * something on its way out gets to.\n *\n * The escalation is what makes the stop a fact rather than a request. SIGTERM alone leaves a child\n * that traps it, or that has a grandchild holding the pipes open, running while the promise this\n * call is awaited by has already rejected — which is the hang the timeout exists to prevent, one\n * level down. So when the grace is up, anything still alive is killed, and `settled` runs whether\n * or not the child was ever going to close: a `close` that may never arrive cannot be the only way\n * this promise settles.\n *\n * The returned function cancels a grace that has not come due, for the callers that settle first —\n * a child that honoured SIGTERM settles from its own `close`, and nothing should kill anything\n * after that.\n */\nfunction stopEscalating(child: StoppableChild, settled: () => void): () => void {\n child.kill('SIGTERM');\n if (child.exitCode !== null || child.signalCode !== null) {\n // Already gone — a timeout that fired in the same tick as the exit — and there is nothing left\n // to escalate to. The caller settles from the exit it already has.\n return () => {};\n }\n const escalation = setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL');\n settled();\n }, KILL_GRACE_MS);\n escalation.unref?.();\n return () => clearTimeout(escalation);\n}\n\n/**\n * The subcommand an argv actually runs, for a message that has to name it.\n *\n * Every argv here arrives with the hardening's `-c key=value` pairs in front of it, and a message\n * built from `args[0]` therefore names `-c`: the manifest read reported `git -c failed`, which\n * names a flag and tells a reader nothing about which command died (#357).\n */\nfunction subcommandOf(args: readonly string[]): string {\n for (let index = 0; index < args.length; index++) {\n const arg = args[index]!;\n // `-c key=value` and `-C <path>` each take the next argument as their value, so the value is\n // not mistaken for the subcommand. `--key=value` carries its own.\n if (arg === '-c' || arg === '-C') {\n index++;\n continue;\n }\n if (arg.startsWith('-')) continue;\n return arg;\n }\n return '(no subcommand)';\n}\n\n/** What a child that produced no exit code is reported as, naming the command that produced it. */\nfunction stoppedDescription(\n command: string,\n args: readonly string[],\n stoppedBy: 'timeout' | 'aborted' | undefined,\n closedBySignal: NodeJS.Signals | null,\n timeoutMs: number,\n): string {\n const invocation = `${command} ${subcommandOf(args)}`;\n if (stoppedBy === 'timeout') {\n return `${invocation} timed out after ${timeoutMs}ms and was killed`;\n }\n if (stoppedBy === 'aborted') return `${invocation} was cancelled`;\n if (closedBySignal) return `${invocation} was killed with ${closedBySignal}`;\n return `${invocation} was killed and produced no result`;\n}\n\n/**\n * How a child process can fail without ever producing an exit code.\n *\n * Every field is optional because each shape arrives from somewhere different: Node's own failure\n * object, a spawn that never happened, and the placeholder this module builds when its own timer\n * fires and settles the promise before Node reports anything.\n */\ninterface ProcessFailure {\n name?: string;\n message?: string;\n code?: number | string;\n killed?: boolean;\n signal?: string | null;\n}\n\n/** A child's exit code, or `undefined` for anything that is not one — which is a failure, not a 1. */\nfunction exitCodeOf(error: ProcessFailure | null): number | undefined {\n // No error at all is the success case, and it is the one shape of \"no exit code\" that is not a\n // failure: `execFile` reports it by passing `null`, not by passing a code.\n if (!error) return 0;\n if (error.killed || error.signal) return undefined;\n // An abort arrives as an `AbortError` with no code at all, which is not an exit code either.\n if (isAbortError(error)) return undefined;\n return typeof error.code === 'number' ? error.code : undefined;\n}\n\n/** Whether a failure is the one Node raises for an aborted call, which carries no exit code. */\nfunction isAbortError(error: ProcessFailure | null): boolean {\n return error?.name === 'AbortError' || (error as { code?: unknown } | null)?.code === 'ABORT_ERR';\n}\n\nfunction failureDescription(\n error: ProcessFailure | null,\n output: string,\n command: string,\n args: readonly string[],\n timeoutMs: number,\n aborted: boolean,\n): string {\n const invocation = `${command} ${subcommandOf(args)}`;\n if (aborted || isAbortError(error)) return `${invocation} was cancelled`;\n if (error?.killed) return `${invocation} timed out after ${timeoutMs}ms and was killed`;\n if (error?.signal) return `${invocation} was killed with ${error.signal}`;\n if (typeof error?.code === 'string') return `${invocation} failed: ${error.code}`;\n return output.trim() || error?.message || `${invocation} failed`;\n}\n\n/**\n * The variables that say **which repository** git operates on, and the only ones this module\n * removes from the environment.\n *\n * Each of them outranks the directory a call is started in: a shell that exported `GIT_DIR` to work\n * inside a bare repository, a CI step, a hook, or a CI job that shares a runner is enough. Every\n * call in this module is *Book's* write to a repository the caller named — a snapshot object, a\n * worktree, a branch, a commit on the operator's branch — so an ambient one of those would send\n * those writes somewhere nobody asked about, and a stray `GIT_INDEX_FILE` would stage an agent's\n * work into the wrong index (#357).\n *\n * The list is deliberately this list and not \"everything beginning `GIT_`\". Deleting the rest took\n * away the operator's configuration for operations Book performs on their behalf: `GIT_SSH_COMMAND`\n * and `core.sshCommand`'s environment, `GIT_SSL_CAINFO` and `GIT_SSL_NO_VERIFY`, `GIT_ASKPASS`,\n * `GIT_EXEC_PATH`, `GIT_PROXY_COMMAND` — the settings a corporate network, an offline clone, or a\n * self-signed remote needs, none of which chooses a repository and all of which silently change a\n * call from working to failing when they are removed. `GIT_CEILING_DIRECTORIES` is kept for the same\n * reason and one more: it makes git's upward search *stop earlier*, which narrows where Book can be\n * pointed rather than redirecting it.\n */\nconst GIT_REPOSITORY_SELECTION_VARIABLES: ReadonlySet<string> = new Set([\n 'GIT_DIR',\n 'GIT_WORK_TREE',\n 'GIT_INDEX_FILE',\n // The index *format* rather than the index's location, and kept in this list for the same\n // reason as the index itself: a snapshot stages into a temporary index of its own, and an\n // ambient version would have git write that index in a format the operator's own git may refuse\n // to read back.\n 'GIT_INDEX_VERSION',\n 'GIT_OBJECT_DIRECTORY',\n 'GIT_ALTERNATE_OBJECT_DIRECTORIES',\n 'GIT_COMMON_DIR',\n 'GIT_NAMESPACE',\n 'GIT_PREFIX',\n]);\n\n/**\n * The environment Book's own git runs with, which is not the operator's shell environment.\n *\n * {@link GIT_REPOSITORY_SELECTION_VARIABLES} is what comes out; everything else the operator set is\n * theirs and is left alone, including the two kinds that are most obviously not repository\n * pointers:\n *\n * - **`GIT_AUTHOR_*` and `GIT_COMMITTER_*`**, which are identity rather than location. They are\n * how a caller names the author of a commit Book is about to make on its behalf, and\n * {@link gitIdentityEnv} sets the same four for Book's own commits.\n * - **`GIT_CONFIG_*`**, which choose *which configuration* git reads rather than *what it\n * operates on*. This module reads configuration on purpose — that is what decides how the\n * cherry-pick signs — so an operator who points `GIT_CONFIG_GLOBAL` at their own file, or a\n * hermetic test that points it at an empty one, is being obeyed rather than overridden.\n *\n * What is added is the hardening's own environment, spread **last** so that a pager and a\n * credential prompt cannot be turned back on by a call site or by an ambient variable: no call here\n * reads from a terminal Book is watching, so `GIT_TERMINAL_PROMPT=0` is what keeps a git that wants\n * a passphrase from waiting for one that will never be typed (#357).\n */\nfunction internalGitEnv(overrides: NodeJS.ProcessEnv | undefined): NodeJS.ProcessEnv {\n const ambient = buildChildEnv(process.env);\n for (const name of GIT_REPOSITORY_SELECTION_VARIABLES) delete ambient[name];\n // The call site's own environment is spread next, so a `GIT_INDEX_FILE` naming a temporary index\n // — the snapshot's, the pre-check's — is Book's own decision and outranks the ambient one it has\n // just removed.\n return { ...ambient, ...overrides, ...hardenedGitEnv() };\n}\n\n/** git, with the argv hardening that belongs to git and not to {@link run}. */\nfunction git(\n cwd: string,\n args: string[],\n options: Omit<RunOptions, 'cwd'> = {},\n): Promise<RunResult> {\n return run('git', hardenedGitArgs(args), { ...options, cwd });\n}\n\n/**\n * {@link git}, exported for tests only, and reached through `git-isolation-internal.ts` rather than\n * from here: see that module for why.\n *\n * No exported path reaches `git()`'s `input` branch without a real snapshot, and #351 needs a patch\n * git exits on before reading it.\n */\nexport const gitForTest = git;\n\n/** The runner `git-signing.ts` reads the signing configuration through; see {@link HardenedRunner}. */\nconst hardenedRunner: HardenedRunner = run;\n\n/**\n * What Book reports about a cherry-pick that failed, given what the pick left behind and what the\n * rollback itself did.\n *\n * Two questions have separate answers and used to share one sentence. **Was it rolled back?** only\n * the `cherry-pick --abort` that ran afterwards can say, and that call can fail — a repository\n * that is no longer one, a lock it cannot take — so a swallowed abort turned a pick that is still\n * in progress into \"rolled back\", and an operator reading that would commit their next change into\n * the agent's pick (#357). **Was it a conflict?** only the working tree can say: unmerged paths and\n * markers are a conflict, while a signing failure, a filter that would not run, or a repository\n * that is read-only is the machinery failing, and calling those conflicts sends the reader looking\n * for markers that are not there.\n *\n * The two answers cross on **status**, and which way they cross is the point of this signature.\n * `not_applied` means \"nothing was written, and running it again is a reasonable thing to try\" — a\n * rolled-back pick with no unmerged paths is exactly that. `conflicted` is the status every other\n * \"an operator has to look at this before anything else happens\" outcome in this module already\n * uses, and a repository left mid-cherry-pick is that outcome even when nothing conflicted: the\n * next `cherry-pick` in it fails outright, and `applyVerifiedCandidate` refuses to start one while\n * `CHERRY_PICK_HEAD` exists, so a status that reads as \"just try again\" sends a retry into a\n * repository that is waiting for a `--continue` or an `--abort` it was never told about. It is the\n * closest status the vocabulary has, and it is the closest to the truth.\n *\n * Reached through `git-isolation-internal.ts`, which is where the test-only exports live; see that\n * module for why this one is among them.\n */\nexport function cherryPickFailureResult(facts: {\n /** What the cherry-pick said when it failed. */\n pickError: string;\n /** What `cherry-pick --abort` said, when it failed rather than rolling the pick back. */\n abortError?: string;\n /** The paths the pick left unmerged, read before the abort removed them. */\n unmergedPaths: readonly string[];\n}): AgentApplyResult {\n const conflicted = facts.unmergedPaths.length > 0;\n if (facts.abortError) {\n return {\n // Not `conflicted ? 'conflicted' : 'not_applied'`, which is what the conflict claim alone\n // decides: a pick whose rollback did not run is not retryable whether or not it conflicted,\n // and reporting the narrower of the two claims is what made an operator commit into a\n // repository that was still mid-pick.\n status: 'conflicted',\n error: `Cherry-pick failed (${facts.pickError}) and the rollback did not complete (${facts.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.`,\n };\n }\n return {\n status: conflicted ? 'conflicted' : 'not_applied',\n error: `Cherry-pick failed and was rolled back: ${facts.pickError}`,\n };\n}\n\nfunction gitIdentityEnv(): NodeJS.ProcessEnv {\n return {\n GIT_AUTHOR_NAME: 'Book Agent',\n GIT_AUTHOR_EMAIL: 'agents@book.local',\n GIT_COMMITTER_NAME: 'Book Agent',\n GIT_COMMITTER_EMAIL: 'agents@book.local',\n };\n}\n\nfunction tempIndex(): { dir: string; path: string } {\n const dir = mkdtempSync(join(tmpdir(), 'book-index-'));\n return { dir, path: join(dir, 'index') };\n}\n\nexport async function findGitRoot(\n workspace: string,\n signal?: AbortSignal,\n): Promise<string | undefined> {\n try {\n return (await git(workspace, ['rev-parse', '--show-toplevel'], { signal })).stdout.trim();\n } catch {\n return undefined;\n }\n}\n\nexport function repositoryHash(repoRoot: string): string {\n return createHash('sha256').update(resolve(repoRoot).toLowerCase()).digest('hex').slice(0, 20);\n}\n\nasync function writeWorkspaceTree(\n repoRoot: string,\n includeUntracked: boolean,\n signal?: AbortSignal,\n): Promise<{ head: string; tree: string; status: string }> {\n const index = tempIndex();\n try {\n const env = { GIT_INDEX_FILE: index.path };\n const head = (await git(repoRoot, ['rev-parse', 'HEAD'], { signal })).stdout.trim();\n await git(repoRoot, ['read-tree', head], { env, signal });\n await git(repoRoot, includeUntracked ? ['add', '-A', '--', '.'] : ['add', '-u', '--', '.'], {\n env,\n signal,\n });\n const tree = (await git(repoRoot, ['write-tree'], { env, signal })).stdout.trim();\n const status = (\n await git(repoRoot, ['status', '--porcelain=v1', '--untracked-files=all'], { signal })\n ).stdout;\n return { head, tree, status };\n } finally {\n rmSync(index.dir, { recursive: true, force: true });\n }\n}\n\nexport async function currentWorkspaceFingerprint(\n repoRoot: string,\n includeUntracked: boolean,\n signal?: AbortSignal,\n): Promise<string> {\n const { head, tree } = await writeWorkspaceTree(repoRoot, includeUntracked, signal);\n return `${head}:${tree}`;\n}\n\n/**\n * The caller's own signal, threaded through every git this snapshot makes.\n *\n * A stopped agent still has this snapshot to finish, and `git status` over a large checkout is\n * exactly the call that takes minutes: without the signal the stop is a request that the snapshot\n * only hears when it is over. The per-call {@link DEFAULT_GIT_TIMEOUT_MS} remains the floor, for a\n * caller with no signal to pass.\n */\nexport async function createSyntheticSnapshot(\n workspace: string,\n includeUntracked = true,\n signal?: AbortSignal,\n): Promise<AgentSnapshot> {\n const repoRoot = await findGitRoot(workspace, signal);\n if (!repoRoot) {\n throw new Error(\n 'Managed agents require a Git workspace with at least one commit. Use --agents off for this workspace.',\n );\n }\n const id = randomUUID();\n const { head, tree, status } = await writeWorkspaceTree(repoRoot, includeUntracked, signal);\n const commit = (\n await git(repoRoot, ['commit-tree', tree, '-p', head, '-m', `book synthetic snapshot ${id}`], {\n env: gitIdentityEnv(),\n signal,\n })\n ).stdout.trim();\n const repoHash = repositoryHash(repoRoot);\n const ref = `refs/book/snapshots/${repoHash}/${id}`;\n await git(repoRoot, ['update-ref', ref, commit], { signal });\n\n const manifestOutput = (\n await git(\n repoRoot,\n [\n '-c',\n 'core.quotepath=false',\n 'diff-tree',\n '--no-commit-id',\n '--name-status',\n '-r',\n '-M',\n head,\n commit,\n ],\n { signal },\n )\n ).stdout;\n const manifest = manifestOutput\n .split(/\\r?\\n/)\n .filter(Boolean)\n .map((line) => {\n const [statusCode, ...paths] = line.split('\\t');\n return { status: statusCode, path: paths.join(' -> ') };\n });\n\n return {\n id,\n repoRoot,\n repoHash,\n baseHead: head,\n commit,\n tree,\n ref,\n fingerprint: `${head}:${tree}`,\n dirty: status.trim().length > 0,\n includeUntracked,\n manifest,\n createdAt: Date.now(),\n };\n}\n\n/** Where agent worktrees live when a host does not override the root. */\nexport function defaultWorktreeRoot(): string {\n return join(resolveBookHome(), 'worktrees');\n}\n\n/** Whether a branch is already in the repository, which decides who may delete it afterwards. */\nasync function branchExists(\n repoRoot: string,\n branch: string,\n signal?: AbortSignal,\n): Promise<boolean> {\n const result = await git(repoRoot, ['show-ref', '--verify', '--quiet', `refs/heads/${branch}`], {\n allowExitCodes: [1],\n signal,\n });\n return result.code === 0;\n}\n\n/**\n * Delete a branch Book created, best effort, and only one Book created.\n *\n * `worktree add -b` creates the branch before it checks out, so a checkout that is refused after\n * that point — a path that is already a file, a directory that is not empty, a start commit that\n * does not resolve — leaves the branch behind and every retry fails with `already exists`, which\n * reads like the agent's work is still there rather than like a branch Book leaked (#357). The\n * caller checks whether the branch was there first: a branch that already existed is the\n * operator's, with their commits on it, and `branch -D` on it would destroy work Book never made.\n *\n * Bound by its own timeout rather than by the caller's signal, because every caller of this is a\n * cleanup running *because* the add it belongs to failed or was aborted — see\n * {@link removeWorktree}.\n */\nasync function deleteBranchBookCreated(\n repoRoot: string,\n branch: string,\n existed: boolean,\n): Promise<void> {\n if (existed) return;\n await git(repoRoot, ['branch', '-D', branch], { allowExitCodes: [1, 128] }).catch(() => {});\n}\n\n/**\n * Whether the worktree at `path` is one an agent can work in, rather than a directory an\n * interrupted `worktree add` left behind.\n *\n * The pointer file is not the test, and treating it as one is what this replaces (#357).\n * `worktree add` writes `<path>/.git` before it checks anything out — before it has a branch worth\n * the name and before there is a working tree — so a run whose add was killed by this module's own\n * timeout, by the agent's stop, or by a signal that took the process with it, leaves exactly that\n * file behind. The old `existsSync(join(path, '.git'))` read it as \"a worktree Book already made\",\n * adopted it, and handed the agent a checkout with no files in it: a run that appears to work and\n * produces an empty patch, against a branch nobody can use.\n *\n * Two reads, because one is not enough. `rev-parse --verify HEAD` goes through the pointer, the\n * administrative directory and the object store at once, so it fails for every half-state that\n * pointer cannot reach — an administrative directory `worktree prune` already removed, a pointer\n * into another repository, a branch whose commit is gone. HEAD resolving on its own says less than\n * it looks: it resolves from the administrative directory alone, with no index and no checkout, so\n * the index is checked too. A checkout writes it as it goes, and a git killed partway through\n * leaves the administrative directory without one.\n */\nasync function worktreeIsComplete(path: string, signal?: AbortSignal): Promise<boolean> {\n if (!existsSync(join(path, '.git'))) return false;\n try {\n const administrative = (\n await git(path, ['rev-parse', '--absolute-git-dir'], { signal })\n ).stdout.trim();\n if (!existsSync(join(administrative, 'index'))) return false;\n await git(path, ['rev-parse', '--verify', 'HEAD'], { signal });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove the worktree at `path`, the record git keeps for it, and the directory itself.\n *\n * Two calls and then a directory, because each one is what lets the next one work. `worktree\n * remove --force` deletes the checkout and its administrative directory together, and it is the\n * only one of the three that refuses while a git still holds an index lock from the killed call\n * — which is why it runs first and why the others are allowed to fail. `worktree prune` then\n * clears an entry git could not remove.\n *\n * The directory is last because git is what normally removes it, and it is here because git\n * refuses a path it does not recognise as one of its worktrees — which is exactly what a killed\n * add has left. The `.git` at that path is the guard, and it is the whole claim: what goes is a\n * directory that presents itself as a git repository under Book's own agent path, which is\n * only thing this function is ever called on. A directory without one is not a worktree, and is\n * left for git's own refusal to report rather than removed on a guess.\n *\n * Deliberately **not** given the caller's signal. This runs because an add failed *or was aborted*,\n * and a cleanup bound to the very signal that stopped it would not run at all — which is the state\n * the retry then has to clean up instead. Each call is bounded by its own timeout, so the cleanup\n * cannot hang on the disk the add hung on.\n */\nasync function removeWorktree(repoRoot: string, path: string): Promise<void> {\n await git(repoRoot, ['worktree', 'remove', '--force', path], { allowExitCodes: [128] }).catch(\n () => {},\n );\n await git(repoRoot, ['worktree', 'prune'], { allowExitCodes: [128] }).catch(() => {});\n if (pointsAtWorktree(path)) rmSync(path, { recursive: true, force: true });\n}\n\n/** Whether `path` holds a linked worktree, which is what a worktree Book would be replacing. */\nfunction pointsAtWorktree(path: string): boolean {\n return existsSync(join(path, '.git'));\n}\n\nexport async function createAgentWorktree(\n snapshot: AgentSnapshot,\n agentId: string,\n worktreeRoot = defaultWorktreeRoot(),\n startCommit = snapshot.commit,\n signal?: AbortSignal,\n): Promise<{ path: string; branch: string }> {\n const path = join(worktreeRoot, snapshot.repoHash, agentId);\n const branch = `book-agent/${snapshot.repoHash}/${agentId}`;\n // A worktree Book already made, and made completely, is adopted as it is, so a run that finds its\n // own directory from a previous attempt does not fail on it. Anything else at that path is git's\n // business: a file, a directory with something in it, or the remains of an add that was killed —\n // each refused by `worktree add` with a real error, which is the only outcome that says the\n // worktree does not exist rather than reporting one that does (#357).\n if (await worktreeIsComplete(path, signal)) return { path, branch };\n // Read before the add, because it is what the add's own branch is worth cleaning up: only a\n // branch that did not exist before this call is one this call created.\n const existed = await branchExists(snapshot.repoRoot, branch, signal);\n // What a previous attempt left goes first, and the branch goes with the worktree rather than with\n // the decision above. An add killed after creating the branch leaves both, and the retry is then\n // refused by \"a branch named ... already exists\" before it ever reaches the checkout — so a\n // half-made worktree has to take its branch with it or the next run cannot make a new one.\n //\n // Tied to the worktree on purpose: a path that is not a worktree is not Book's, and a directory\n // somebody else put there must never cost an operator the branch they made under this name. The\n // failed-add path below is the opposite case and does delete the branch on its own, because there\n // Book has just been told it created it.\n if (pointsAtWorktree(path)) {\n await removeWorktree(snapshot.repoRoot, path);\n await deleteBranchBookCreated(snapshot.repoRoot, branch, false);\n }\n mkdirSync(dirname(path), { recursive: true });\n try {\n await git(snapshot.repoRoot, ['worktree', 'add', '-b', branch, path, startCommit], { signal });\n } catch (error) {\n // A failed or aborted add can have created the worktree, its administrative directory and the\n // branch before it got to the checkout. All three go, so the next attempt is not refused by\n // \"already exists\" on any of them.\n await removeWorktree(snapshot.repoRoot, path);\n await deleteBranchBookCreated(snapshot.repoRoot, branch, existed);\n throw error;\n }\n if (!(await worktreeIsComplete(path, signal))) {\n // An add that reported success and left nothing usable is not a worktree to hand an agent, and\n // is removed for the same reason the failed add above is: what is left behind would be adopted\n // as a finished checkout by the next run, which is the failure this check exists to catch.\n await removeWorktree(snapshot.repoRoot, path);\n await deleteBranchBookCreated(snapshot.repoRoot, branch, existed);\n throw new Error(\n `git worktree add reported success but ${path} is not a usable worktree; it was removed so the next attempt starts clean.`,\n );\n }\n return { path, branch };\n}\n\n/**\n * Where the cleanup git runs from, which is never the directory it is deleting.\n *\n * `worktree remove` deletes the directory it is told to, and a process whose working directory is\n * a directory that is being removed is in a state the platform does not always allow to finish:\n * Windows will not delete a directory that is a process's cwd, and a `branch -D` that follows from\n * inside it fails too, which leaves the branch behind on every cleanup (#357).\n *\n * So the repository is named, not guessed: the caller's own `repoRoot` when it has one, and\n * otherwise the repository the worktree belongs to, which is outside it — its `.git` file points at\n * `<repo>/.git/worktrees/<name>`, and git runs from there as well as from `<repo>/.git`, which is\n * the directory that survives the removal the cleanup is doing. Only a worktree with no pointer left\n * is a fallback to its parent, and that is the case this is for: a directory that is no longer a\n * repository at all, where running from inside it finds nothing and both calls fail silently.\n */\nfunction removalRoot(record: AgentRecord, repoRoot: string | undefined): string {\n if (repoRoot && resolve(repoRoot) !== resolve(record.worktree!)) return repoRoot;\n const pointed = repositoryOfWorktree(record.worktree!);\n if (pointed && existsSync(pointed)) return pointed;\n return dirname(record.worktree!);\n}\n\n/**\n * The repository a worktree belongs to, which lives outside the worktree.\n *\n * A linked worktree has no `.git` directory of its own: its `.git` is a file whose one line names\n * the administrative directory git keeps for it, `<repo>/.git/worktrees/<name>`. Reading that\n * pointer is what lets a cleanup run from the repository instead of from the checkout it is about\n * to delete. It is not the administrative directory itself that is used, because `worktree remove`\n * deletes that along with the checkout — a process whose working directory has just been removed\n * fails to start anything at all, on every platform, so the second call would never run. Two levels\n * up is `<repo>/.git` for an ordinary repository and the bare repository itself for a bare one, and\n * git resolves a repository from both.\n *\n * The pointer is not always absolute. git 2.48 writes it relative whenever the worktree is\n * somewhere the pointer can be written relative — a sibling directory, a checkout inside the\n * repository's own tree — so taking it as given resolved `<cwd>/<pointer>`, which on any other cwd\n * is a directory that does not exist, and the cleanup then ran from the worktree it was in the\n * middle of deleting (#357). git's own rule is the directory the `.git` file sits in, and\n * `resolve(worktree, gitdir)` is that rule for both forms: an absolute pointer is left exactly as\n * it is, a relative one is taken from the worktree root.\n */\nfunction repositoryOfWorktree(worktree: string): string | undefined {\n try {\n const pointer = readFileSync(join(worktree, '.git'), 'utf8').trim();\n const match = /^gitdir:\\s*(.+)$/m.exec(pointer);\n const gitdir = match?.[1]?.trim();\n if (!gitdir) return undefined;\n return dirname(dirname(resolve(worktree, gitdir)));\n } catch {\n return undefined;\n }\n}\n\nexport async function removeAgentWorktree(record: AgentRecord, repoRoot?: string): Promise<void> {\n if (!record.worktree || !record.branch) return;\n const root = removalRoot(record, repoRoot);\n await git(root, ['worktree', 'remove', '--force', record.worktree], {\n allowExitCodes: [128],\n }).catch(() => {});\n await git(root, ['branch', '-D', record.branch], { allowExitCodes: [1, 128] }).catch(() => {});\n}\n\nexport async function removeSnapshotRef(snapshot: AgentSnapshot): Promise<void> {\n await git(snapshot.repoRoot, ['update-ref', '-d', snapshot.ref], { allowExitCodes: [1] }).catch(\n () => {},\n );\n}\n\nexport async function checkoutAgentCommit(worktree: string, commit: string): Promise<void> {\n await git(worktree, ['reset', '--hard', commit]);\n}\n\nexport async function commitAgentWork(\n record: AgentRecord,\n snapshot: AgentSnapshot,\n): Promise<PatchCandidate | undefined> {\n if (!record.worktree || !record.branch) return undefined;\n await git(record.worktree, ['add', '-A', '--', '.']);\n const diff = await git(record.worktree, ['diff', ...HARDENED_DIFF_ARGS, '--cached', '--quiet'], {\n allowExitCodes: [1],\n });\n if (diff.code === 0) return undefined;\n // Belt and braces on both counts. `core.hooksPath=` already leaves `pre-commit` and `commit-msg`\n // nothing to find, and this commit is Book's work in Book's worktree rather than the\n // operator's own, so neither of them should decide whether it happens. `--no-gpg-sign` is not\n // belt and braces: `commit.gpgSign` and `gpg.program` are configuration this commit would\n // otherwise honor, and the identity it commits under — `Book Agent <agents@book.local>` — has\n // no key on any machine, so every patcher commit would fail outright, or hang a headless run on\n // pinentry, or start whatever program the configuration names. The cherry-pick that applies this\n // work to the operator's branch is a different question: that commit is the operator's own, in\n // their history, under their identity, so it still signs — and `git-signing.ts` is what decides\n // which configuration that signing reads.\n await git(\n record.worktree,\n ['commit', '--no-verify', '--no-gpg-sign', '-m', `book agent ${record.id}: ${record.name}`],\n {\n env: gitIdentityEnv(),\n },\n );\n const headCommit = (await git(record.worktree, ['rev-parse', 'HEAD'])).stdout.trim();\n return {\n baseCommit: snapshot.commit,\n headCommit,\n branch: record.branch,\n agentId: record.id,\n };\n}\n\nasync function candidateDelta(\n snapshot: AgentSnapshot,\n candidate: PatchCandidate,\n signal?: AbortSignal,\n): Promise<string> {\n return (\n await git(\n snapshot.repoRoot,\n [\n 'diff',\n ...HARDENED_DIFF_ARGS,\n '--binary',\n '--full-index',\n candidate.baseCommit,\n candidate.headCommit,\n '--',\n ],\n { signal },\n )\n ).stdout;\n}\n\nasync function precheckDelta(\n snapshot: AgentSnapshot,\n patch: string,\n signal?: AbortSignal,\n): Promise<void> {\n const index = tempIndex();\n try {\n const env = { GIT_INDEX_FILE: index.path };\n await git(snapshot.repoRoot, ['read-tree', snapshot.commit], { env, signal });\n await git(snapshot.repoRoot, ['apply', '--3way', '--check', '--cached', '-'], {\n env,\n input: patch,\n signal,\n });\n await git(snapshot.repoRoot, ['apply', '--check', '-'], { input: patch, signal });\n } finally {\n rmSync(index.dir, { recursive: true, force: true });\n }\n}\n\n/** How long the cherry-pick may take before it is killed and the pick it started is aborted. */\nconst CHERRY_PICK_TIMEOUT_MS = 300_000;\n\n/**\n * Whether the repository is in the middle of a cherry-pick somebody else started.\n *\n * `CHERRY_PICK_HEAD` is git's own record of one, and `rev-parse --verify` answers \"no\" with exit\n * 1 rather than an error, which is why this asks for it and reads the answer rather than trusting\n * a quiet repository to mean no pick. It is checked before anything is written, and the `--abort`\n * in {@link applyVerifiedCandidate} runs only for a pick that call started.\n */\nasync function hasPendingCherryPick(repoRoot: string, signal?: AbortSignal): Promise<boolean> {\n const result = await git(repoRoot, ['rev-parse', '-q', '--verify', 'CHERRY_PICK_HEAD'], {\n allowExitCodes: [1],\n signal,\n });\n return result.code === 0;\n}\n\n/**\n * The paths a failed cherry-pick left unmerged, which is what makes a failure a conflict.\n *\n * Read before the abort, because the abort is what removes them: afterwards there is nothing left\n * to distinguish a conflict from a signing failure that left the same `CHERRY_PICK_HEAD` behind.\n * Read with the diff drivers off, so this never starts the program a checkout names to produce it.\n */\nasync function unmergedPathsAfterFailure(repoRoot: string): Promise<string[]> {\n try {\n const result = await git(repoRoot, [\n 'diff',\n ...HARDENED_DIFF_ARGS,\n '--name-only',\n '--diff-filter=U',\n ]);\n return result.stdout.split(/\\r?\\n/).filter(Boolean);\n } catch {\n // A read that failed cannot say there was no conflict, and it must not turn a known failure\n // into an unknown status either: the caller reports the pick failure, and the conflict claim\n // is left out rather than invented.\n return [];\n }\n}\n\nexport async function applyVerifiedCandidate(\n snapshot: AgentSnapshot,\n candidate: PatchCandidate,\n signal?: AbortSignal,\n): Promise<AgentApplyResult> {\n if (candidate.baseCommit !== snapshot.commit) {\n return {\n status: 'conflicted',\n error: 'Patch candidate base does not match the task snapshot.',\n };\n }\n const actualHead = (\n await git(snapshot.repoRoot, ['rev-parse', candidate.headCommit])\n ).stdout.trim();\n if (actualHead !== candidate.headCommit) {\n return { status: 'conflicted', error: 'Patch candidate commit no longer resolves exactly.' };\n }\n\n // Somebody else's interrupted cherry-pick is the operator's, and it owns the working tree this\n // would write to. Book starts a cherry-pick only to be able to abort the one it started, so a\n // pick it did not start is a pick it must leave exactly as it found it.\n if (await hasPendingCherryPick(snapshot.repoRoot, signal)) {\n return {\n status: 'conflicted',\n error:\n 'A cherry-pick is already in progress in this repository. Book did not start it and will not abort it; finish it (git cherry-pick --continue) or undo it (git cherry-pick --abort), then apply the agent result again.',\n };\n }\n\n const current = await currentWorkspaceFingerprint(\n snapshot.repoRoot,\n snapshot.includeUntracked,\n signal,\n );\n if (current !== snapshot.fingerprint) {\n return {\n status: 'conflicted',\n error: 'Parent workspace drifted after the agent snapshot; no changes were applied.',\n };\n }\n\n const patch = await candidateDelta(snapshot, candidate, signal);\n try {\n await precheckDelta(snapshot, patch, signal);\n } catch (error) {\n return {\n status: 'conflicted',\n error: `Patch pre-check failed; no changes were applied: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n\n if (!snapshot.dirty) {\n // Signing is what this closes for the cherry-pick: it is the only call here that writes a\n // commit into the operator's history, so it is the only one that signs, and a\n // repository-local `commit.gpgSign` with a `gpg.program` of its own is a program Book would\n // start, in a flow nothing asked about (#348). The commit still signs, and signs as the\n // operator configured, because the pins carry their own value forward — see `git-signing.ts`\n // for what is read, from where, and the one signing shape that cannot be carried over.\n // Filters and merge drivers still run on this checkout, deliberately, because git-lfs depends\n // on them (#357); they are configuration a repository can own in a way signing now is not.\n //\n // The read is a separate step, before the `try` below, because it is not a cherry-pick: a\n // failure here means no pick has started, and reporting it as a rolled-back pick would be\n // wrong twice over — it would blame the pick for a read that never reached one, and the\n // `--abort` would follow to a repository that has nothing to abort.\n let signingPins: string[];\n try {\n signingPins = await signingPinArgs(snapshot.repoRoot, hardenedRunner);\n } catch (error) {\n return {\n status: 'conflicted',\n error: `Could not read the signing configuration for the cherry-pick: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n try {\n // Bounded because this is the one call that can start a program the operator named: a\n // signer waiting on a passphrase it will never be given is a hang, and an unbounded one\n // would outlive the session. The abort below then cleans up the pick this call started.\n await git(snapshot.repoRoot, [...signingPins, 'cherry-pick', candidate.headCommit], {\n timeoutMs: CHERRY_PICK_TIMEOUT_MS,\n signal,\n });\n const appliedCommit = (\n await git(snapshot.repoRoot, ['rev-parse', 'HEAD'], { signal })\n ).stdout.trim();\n return { status: 'applied', commit: appliedCommit };\n } catch (error) {\n // Read what the pick left behind before the rollback removes it, and report whether the\n // rollback actually ran: see {@link cherryPickFailureResult} for why those are two\n // questions and not one.\n const unmergedPaths = await unmergedPathsAfterFailure(snapshot.repoRoot);\n let abortError: string | undefined;\n try {\n // Exit 128 is `no cherry-pick in progress`, which for a pick that just failed is a\n // rollback that had nothing to do rather than one that failed. Anything else is a real\n // failure and is reported as one.\n await git(snapshot.repoRoot, ['cherry-pick', '--abort'], { allowExitCodes: [128] });\n } catch (abortFailure) {\n abortError = abortFailure instanceof Error ? abortFailure.message : String(abortFailure);\n }\n return cherryPickFailureResult({\n pickError: error instanceof Error ? error.message : String(error),\n abortError,\n unmergedPaths,\n });\n }\n }\n\n try {\n await git(snapshot.repoRoot, ['apply', '-'], { input: patch, signal });\n return { status: 'applied', commit: candidate.headCommit };\n } catch (error) {\n return {\n status: 'conflicted',\n error: `Patch application failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n","/**\n * What the cherry-pick that applies an agent's result is allowed to read as signing configuration\n * (#348).\n *\n * The decision is the owner's: that commit lands in the operator's branch under their committer\n * identity, so it keeps signing as they configured — but only from *their* configuration. A\n * checkout's `.git/config` is a file the checkout chooses, and it arrives with an archive, a shared\n * directory, or someone else's machine, so `commit.gpgSign` together with `gpg.program = <anything>`\n * is a way for a repository to have Book start a program while applying an agent's work, with no\n * prompt, in a flow nothing asked about. `hardenedGitArgs` closes the routes that need a command\n * of their own — hooks, `core.fsmonitor`, a diff driver — and a signing program is named by\n * configuration alone, so it needs this.\n *\n * The pins travel as `-c` overrides on the cherry-pick itself, which is where they belong: git\n * reads them in the order it reads anything else and the last one wins, so a value the operator\n * set is pinned *after* the value that would otherwise stand behind it.\n *\n * Every program and format key is pinned **unconditionally**, not only when a repository sets it.\n * Reading first and pinning only what was read makes safety depend on the read having been\n * complete and on the configuration not having changed between the read and the cherry-pick —\n * and both are properties of a checkout to decide. A pin costs nothing when nothing disagrees: for\n * a key the operator has set, the last `-c` is their own value, which is the configuration they\n * already had.\n */\n\n/** One `git config` entry, in the order git reported it. */\nexport interface SigningConfigEntry {\n /**\n * Git's own name for where the value came from: `system`, `global`, `local`, `worktree` or\n * `command`. An entry a repository pulled in with `include`/`includeIf` carries the scope of\n * the file that included it, so it is a repository value (checked on git 2.43).\n */\n scope: string;\n /**\n * The key as git canonicalized it: the section and the variable name lower-cased, a subsection\n * left exactly as written — `gpg.ssh.program`, and `gpg.SSH.program` for a key that is not\n * this one at all.\n */\n key: string;\n /** The value, or `true` for a key written without one — `[commit] gpgsign` means what it says. */\n value: string;\n}\n\n/** What {@link HardenedRunner} is given, and what it is asked for. */\nexport interface RunOptions {\n cwd: string;\n /** Exit codes to resolve rather than reject. Only ever a real, numeric exit code. */\n allowExitCodes?: number[];\n /** Kill the child and reject after this long. */\n timeoutMs?: number;\n /**\n * The caller's own cancellation, which kills the child with it.\n *\n * A timeout bounds a call that hangs by itself; this is for the caller that has stopped caring.\n * An agent that is stopped still has its snapshot to take and its result to apply, and those are\n * the longest git calls in the flow, so threading the run's signal is what makes a stop take\n * effect now rather than at the next timeout (#357).\n */\n signal?: AbortSignal;\n /** Write this to the child's stdin instead of leaving stdin closed. */\n input?: string;\n /** Added to Book's own environment and to the hardening every call carries. */\n env?: NodeJS.ProcessEnv;\n}\n\nexport interface RunResult {\n stdout: string;\n stderr: string;\n code: number;\n}\n\n/**\n * The one hardened way to run a command here, supplied by `git-isolation.ts` — the same argv and\n * environment hardening its own `git()` builds, applied in one place so a fix to it reaches every\n * call this module makes.\n *\n * It is a parameter rather than an import because that module imports this one, and a helper both\n * of them imported would be a cycle the architecture check rejects. It runs *commands*, not only\n * git: the operator's own ssh key command of {@link sshDefaultSigningKey} is not git, and runs\n * here for the same reason git would run it — the one program on this path that is not a\n * repository's.\n */\nexport type HardenedRunner = (\n command: string,\n args: string[],\n options: RunOptions,\n) => Promise<RunResult>;\n\n/**\n * The keys to ask git about: every setting that names or chooses a signing program, plus every\n * `gpg.*` key whatever it is called, so that a key this file has never heard of is still seen and\n * neutralized rather than passed through.\n */\nconst SIGNING_KEY_PATTERN = '^(commit\\\\.gpgsign|gpg\\\\..*|user\\\\.signingkey)$';\n\n/** Git before 2.26 has no `--show-scope`, and without it there is no way to tell the two apart. */\nconst MINIMUM_GIT = '2.26';\n\n/** A read, a `git var`, and the operator's key command are all cheap; a minute is generous. */\nconst READ_TIMEOUT_MS = 30_000;\n\n/** Scopes the operator's own machine supplies, `command` being `-c` and `GIT_CONFIG_PARAMETERS`. */\nconst USER_SCOPES: ReadonlySet<string> = new Set(['system', 'global', 'command']);\n\n/** Scopes a checkout supplies: its own config, and every file that config includes. */\nconst REPOSITORY_SCOPES: ReadonlySet<string> = new Set(['local', 'worktree']);\n\n/** Git's built-in value for a setting, for a key the operator has not set. */\nconst BUILT_IN = {\n 'commit.gpgsign': 'false',\n 'gpg.format': 'openpgp',\n gpg: 'gpg',\n 'gpg.x509.program': 'gpgsm',\n 'gpg.ssh.program': 'ssh-keygen',\n} as const;\n\n/**\n * One pin per program and format setting, always: its fallback first, then the operator's own\n * value where there is one.\n *\n * `fallback` is what stands behind the operator's value when they have none: git's built-in\n * default, or an empty value for a key that has none. The two `gpg.program` names are one setting\n * under two spellings, so they are one unit — one value for both, so the pair cannot disagree.\n *\n * Every fallback here was checked against a real git rather than read off the documentation. An\n * empty `gpg.ssh.defaultKeyCommand` never runs: where it is consulted the empty command is\n * `cannot run :` and nothing else, and git asks for a `user.signingKey` before it would consult\n * one. The ssh paths are only read to verify signatures, which a cherry-pick of an agent's own\n * commit never does.\n */\nconst PIN_UNITS: readonly { keys: readonly string[]; fallback: string }[] = [\n { keys: ['commit.gpgsign'], fallback: BUILT_IN['commit.gpgsign'] },\n { keys: ['gpg.format'], fallback: BUILT_IN['gpg.format'] },\n { keys: ['gpg.program', 'gpg.openpgp.program'], fallback: BUILT_IN.gpg },\n { keys: ['gpg.x509.program'], fallback: BUILT_IN['gpg.x509.program'] },\n { keys: ['gpg.ssh.program'], fallback: BUILT_IN['gpg.ssh.program'] },\n { keys: ['gpg.ssh.defaultkeycommand'], fallback: '' },\n { keys: ['gpg.ssh.allowedsignersfile'], fallback: '' },\n { keys: ['gpg.ssh.revocationfile'], fallback: '' },\n];\n\n/** Where a `user.signingkey` pin that the repository forced gets its value. */\ntype SigningKeySource = 'none' | 'operator' | 'committer' | 'ssh-default-key-command';\n\n/** The values the pins can only be built from, and which cost a process each to obtain. */\nexport interface SigningPinsInputs {\n /** The output of `git var GIT_COMMITTER_IDENT`, when a pin needs it. */\n committerIdent: string;\n /** The key the operator's `gpg.ssh.defaultKeyCommand` printed, when one was run. */\n sshDefaultKey: string;\n}\n\n/**\n * The signing configuration as git reports it, in config order: system, then global, then the\n * repository's own files in the order git read them, then `command`.\n *\n * `129` is resolved rather than left to reject, because that is the code a git too old for\n * `--show-scope` exits with, and the operator is better served by being told that than by being\n * handed a usage message.\n */\nexport async function readSigningConfig(\n repoRoot: string,\n run: HardenedRunner,\n): Promise<SigningConfigEntry[]> {\n const result = await run(\n 'git',\n ['config', '--show-scope', '-z', '--get-regexp', SIGNING_KEY_PATTERN],\n { cwd: repoRoot, allowExitCodes: [1, 129], timeoutMs: READ_TIMEOUT_MS },\n );\n if (result.code === 129 || /unknown option/i.test(result.stderr)) {\n throw new Error(\n `Applying an agent's result needs git ${MINIMUM_GIT} or later (for \\`git config --show-scope\\`); this git is ${await gitVersion(run, repoRoot)}`,\n );\n }\n return parseSigningConfig(result.stdout);\n}\n\nasync function gitVersion(run: HardenedRunner, repoRoot: string): Promise<string> {\n try {\n return (\n await run('git', ['--version'], { cwd: repoRoot, timeoutMs: READ_TIMEOUT_MS })\n ).stdout.trim();\n } catch {\n return 'of an unknown version';\n }\n}\n\n/**\n * Parse `git config --show-scope -z --get-regexp` output.\n *\n * The `-z` layout, read off a real git, is a NUL-separated stream of pairs: the scope, then the\n * key, then the value — `scope\\0key\\nvalue\\0` — and a key written without a value has neither the\n * newline nor the value, `scope\\0key\\0`, because git is about to read it as a boolean. A value may\n * itself contain newlines, so the key is what the first newline ends, and the rest is the value.\n */\nexport function parseSigningConfig(stdout: string): SigningConfigEntry[] {\n const fields = stdout.split('\\0');\n const entries: SigningConfigEntry[] = [];\n for (let index = 0; index + 1 < fields.length; index += 2) {\n const separator = fields[index + 1].indexOf('\\n');\n entries.push({\n scope: fields[index].toLowerCase(),\n key: canonicalConfigKey(\n separator === -1 ? fields[index + 1] : fields[index + 1].slice(0, separator),\n ),\n // Git's own reading of a key with no value: a boolean true.\n value: separator === -1 ? 'true' : fields[index + 1].slice(separator + 1),\n });\n }\n return entries;\n}\n\n/**\n * Canonicalize a config key the way git does, which is not by lower-casing all of it: the section\n * and the variable name are case-insensitive and git reports them folded, but a **subsection is\n * case-sensitive and git keeps it exactly as written** (checked on git 2.43: `[gpg \"SSH\"] program`\n * is reported as `gpg.SSH.program`).\n *\n * That matters because `gpg.SSH.program` is not `gpg.ssh.program`. Git does not use the former for\n * ssh signing, so treating it as the latter would read the operator's own key-setting program out\n * of their configuration and pin it where the repository's would otherwise have gone. As written\n * it is an unknown key, and an unknown key a repository sets is neutralized rather than honored.\n */\nexport function canonicalConfigKey(raw: string): string {\n const nameStart = raw.lastIndexOf('.');\n if (nameStart === -1) return raw.toLowerCase();\n const head = raw.slice(0, nameStart);\n const name = raw.slice(nameStart + 1).toLowerCase();\n const subsectionStart = head.indexOf('.');\n if (subsectionStart === -1) return `${head.toLowerCase()}.${name}`;\n // A subsection may itself contain dots, so only the section in front of the first one is folded.\n const section = head.slice(0, subsectionStart).toLowerCase();\n return `${section}.${head.slice(subsectionStart + 1)}.${name}`;\n}\n\n/** What the entries say, split the way every decision below needs it. Computed once, used once. */\nexport interface SigningFacts {\n /** The operator's own entries, in config order. */\n operatorEntries: SigningConfigEntry[];\n /** Every key a repository scope sets, first seen first. */\n repositoryKeys: Set<string>;\n /** Whether the operator has asked for commits to be signed at all. */\n signingOn: boolean;\n /** The format the applied commit will sign in, folded the way git compares it. */\n format: string;\n}\n\n/** Split the entries. A value from any other scope is a source for nothing, and a key for nothing. */\nexport function signingFacts(entries: readonly SigningConfigEntry[]): SigningFacts {\n const operatorEntries: SigningConfigEntry[] = [];\n const repositoryKeys = new Set<string>();\n for (const entry of entries) {\n if (USER_SCOPES.has(entry.scope)) operatorEntries.push(entry);\n else if (REPOSITORY_SCOPES.has(entry.scope)) repositoryKeys.add(entry.key);\n }\n return {\n operatorEntries,\n repositoryKeys,\n signingOn: configBool(operatorValue(operatorEntries, ['commit.gpgsign'])),\n format: (operatorValue(operatorEntries, ['gpg.format']) ?? BUILT_IN['gpg.format'])\n .trim()\n .toLowerCase(),\n };\n}\n\n/**\n * The operator's own value for a setting, where the setting has more than one name.\n *\n * The last entry in config order wins, not the first name in the list: git stores the two\n * `gpg.program` spellings in one slot, and whichever was written last is the one it reads\n * (checked on git 2.43 and 2.55). Asking for `gpg.program` first and falling back to\n * `gpg.openpgp.program` would hand back the wrong one whenever the operator wrote them the other\n * way round.\n */\nfunction operatorValue(\n operatorEntries: readonly SigningConfigEntry[],\n keys: readonly string[],\n): string | undefined {\n let value: string | undefined;\n for (const entry of operatorEntries) {\n if (keys.includes(entry.key)) value = entry.value;\n }\n return value;\n}\n\n/** {@link operatorValue} over facts that have already been split. */\nfunction lastOperatorValue(facts: SigningFacts, keys: readonly string[]): string | undefined {\n return operatorValue(facts.operatorEntries, keys);\n}\n\n/** Git reads these as booleans; anything it would not accept counts as off, which is the safe side. */\nfunction configBool(value: string | undefined): boolean {\n return ['true', 'yes', 'on', '1'].includes((value ?? '').trim().toLowerCase());\n}\n\n/**\n * Where a `user.signingkey` pin gets its value, which decides whether anything has to be run.\n *\n * `'none'` covers three cases that cost nothing: a repository that sets no key (nothing to pin),\n * signing that is effectively off (git never reads the key, so there is nothing to decide), and\n * an ssh operator who has neither a key nor a key command — the one shape that cannot be carried\n * across, pinned empty so that git fails rather than signing with a repository's key.\n */\nexport function signingKeySource(facts: SigningFacts): SigningKeySource {\n if (!facts.repositoryKeys.has('user.signingkey') || !facts.signingOn) return 'none';\n if (lastOperatorValue(facts, ['user.signingkey']) !== undefined) return 'operator';\n if (facts.format === 'ssh') {\n return lastOperatorValue(facts, ['gpg.ssh.defaultkeycommand']) === undefined\n ? 'none'\n : 'ssh-default-key-command';\n }\n return 'committer';\n}\n\n/**\n * The `-c` arguments that make the cherry-pick read signing configuration from the operator alone.\n *\n * Pure, and the whole of the decision: what git is told, in the order git will read it.\n * Neutralizers and defaults come first and the operator's own values after, so where both exist\n * the operator's is the one in force — the same ordering git applies to its own configuration,\n * where a later entry wins.\n */\nexport function signingPins(\n entries: readonly SigningConfigEntry[],\n inputs: SigningPinsInputs = { committerIdent: '', sshDefaultKey: '' },\n): string[] {\n return pinsFor(signingFacts(entries), inputs);\n}\n\nfunction pinsFor(facts: SigningFacts, inputs: SigningPinsInputs): string[] {\n const fallbacks: string[] = [];\n const operatorValues: string[] = [];\n const pin = (into: string[], key: string, value: string): void => {\n into.push('-c', `${key}=${value}`);\n };\n\n const pinned = new Set<string>();\n for (const unit of PIN_UNITS) {\n for (const key of unit.keys) pinned.add(key);\n const operatorValue = lastOperatorValue(facts, unit.keys);\n for (const key of unit.keys) {\n pin(fallbacks, key, unit.fallback);\n if (operatorValue !== undefined) pin(operatorValues, key, operatorValue);\n }\n }\n\n // Only a repository that sets a key forces a decision about one, and a key with no neutral value\n // is the reason the rest of this function pins unconditionally: there is nothing to say about it\n // unless the repository has an opinion worth overruling.\n if (facts.repositoryKeys.has('user.signingkey')) {\n pin(fallbacks, 'user.signingkey', derivedSigningKey(facts, inputs));\n const operatorValue = lastOperatorValue(facts, ['user.signingkey']);\n if (operatorValue !== undefined) pin(operatorValues, 'user.signingkey', operatorValue);\n }\n\n // Any other `gpg.*` key a repository sets: this file does not know what it selects, and an\n // unknown key is exactly the one a repository would add to reach a program, so the empty value\n // is all it gets. The operator's own value still stands after it.\n for (const key of facts.repositoryKeys) {\n if (pinned.has(key) || !key.startsWith('gpg.')) continue;\n pin(fallbacks, key, '');\n const operatorValue = lastOperatorValue(facts, [key]);\n if (operatorValue !== undefined) pin(operatorValues, key, operatorValue);\n }\n\n return [...fallbacks, ...operatorValues];\n}\n\nfunction derivedSigningKey(facts: SigningFacts, inputs: SigningPinsInputs): string {\n switch (signingKeySource(facts)) {\n case 'operator':\n // The operator's own value is pinned after this, so what stands in front of it only has to\n // be harmless — and empty names no key at all.\n return '';\n case 'committer':\n return committerSigningIdentity(inputs.committerIdent);\n case 'ssh-default-key-command':\n return inputs.sshDefaultKey;\n default:\n return '';\n }\n}\n\n/**\n * `Test <test@example.com> 1790626018 +0700` → `Test <test@example.com>`, the date dropped because\n * it is a property of this commit rather than of the key.\n *\n * This is what git signs an openpgp or x509 commit with when `user.signingKey` is unset, and it\n * is the identity the cherry-pick is about to commit under in any case: `user.name` and\n * `user.email` come from configuration a repository can also set, so this adds nothing such a\n * repository had not already decided about that commit. It names no program — the program this key\n * is paired with is pinned separately, to the operator's own.\n */\nexport function committerSigningIdentity(ident: string): string {\n return ident.trim().replace(/\\s+\\d+\\s+[+-]\\d{4}$/, '');\n}\n\n/**\n * The `-c` arguments for a cherry-pick in this repository: its signing configuration, read through\n * the hardened runner, turned into pins.\n */\nexport async function signingPinArgs(repoRoot: string, run: HardenedRunner): Promise<string[]> {\n const entries = await readSigningConfig(repoRoot, run);\n const facts = signingFacts(entries);\n // Each of these is a process, and most applies need neither: a repository that sets no signing\n // key has nothing to decide, and an operator who has a key of their own has already answered it.\n const inputs: SigningPinsInputs = { committerIdent: '', sshDefaultKey: '' };\n const source = signingKeySource(facts);\n if (source === 'committer') {\n inputs.committerIdent = (\n await run('git', ['var', 'GIT_COMMITTER_IDENT'], {\n cwd: repoRoot,\n timeoutMs: READ_TIMEOUT_MS,\n })\n ).stdout;\n } else if (source === 'ssh-default-key-command') {\n inputs.sshDefaultKey = await sshDefaultSigningKey(facts, repoRoot, run);\n }\n return pinsFor(facts, inputs);\n}\n\n/**\n * The key the operator's own `gpg.ssh.defaultKeyCommand` produces, in the form git accepts.\n *\n * Git looks for no key of its own for ssh, so an operator whose global config relies on the key\n * command rather than on `user.signingKey` used to fail here, and the honest empty pin turned that\n * into a failed apply. The command is the operator's own — it is the one git would have run for\n * this commit — so it is run here the way git runs it: split on whitespace with no shell, through\n * the same hardened runner, with stdin closed so a command that waits for input cannot hang the\n * apply, and bounded by the same timeout the other reads carry.\n *\n * Git's contract for the output is a public key on the first line prefixed with `key::`, and a\n * line that is not that is not used: the pin is empty instead, which fails the apply rather than\n * signing with a key this never established was the operator's.\n */\nexport async function sshDefaultSigningKey(\n facts: SigningFacts,\n repoRoot: string,\n run: HardenedRunner,\n): Promise<string> {\n const command = lastOperatorValue(facts, ['gpg.ssh.defaultkeycommand'])?.trim();\n if (!command) return '';\n // Git's own splitting: whitespace-separated words, the first the program, no shell.\n const [program, ...args] = command.split(/\\s+/);\n try {\n const result = await run(program, args, { cwd: repoRoot, timeoutMs: READ_TIMEOUT_MS });\n const first = result.stdout.split(/\\r?\\n/)[0]?.trim() ?? '';\n return first.startsWith('key::') ? first : '';\n } catch {\n return '';\n }\n}\n","/**\n * Filesystem-contention helpers shared by the agent state store and the persistent job store.\n *\n * Both write a small JSON document atomically, through a temporary file and a rename, and both can\n * find the target already held: Windows will not replace a file another process has open, so a\n * rename there fails with a code that means \"try again\" rather than \"never\". These are the shared\n * pieces for telling those codes apart and for waiting between retries, kept here so that neither\n * store has to reach into the other for them.\n */\n\n/** Codes Windows returns while another process has a file open: retried, never fatal on first sight. */\nexport const FILE_CONTENTION_CODES: ReadonlySet<string> = new Set(['EPERM', 'EBUSY', 'EACCES']);\n\n/** Block the calling thread for `milliseconds`. */\nexport function sleepSync(milliseconds: number): void {\n if (milliseconds <= 0) return;\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","readFileSync","rmSync","createHash","randomUUID","dirname","join","run","operatorValue","run","join","mkdirSync","dirname","randomUUID","rmSync","readFileSync","createHash","existsSync"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/book-home.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\n\n/** Resolve Book's user-global state root, allowing evaluation runs to isolate it explicitly. */\nexport function resolveBookHome(\n env: NodeJS.ProcessEnv = process.env,\n systemHome = homedir(),\n): string {\n const configured = env.BOOK_HOME?.trim();\n return configured ? resolve(configured) : join(systemHome, '.book');\n}\n"],"mappings":";AAAA,SAAS,eAAe;AACxB,SAAS,MAAM,eAAe;AAGvB,SAAS,gBACd,MAAyB,QAAQ,KACjC,aAAa,QAAQ,GACb;AACR,QAAM,aAAa,IAAI,WAAW,KAAK;AACvC,SAAO,aAAa,QAAQ,UAAU,IAAI,KAAK,YAAY,OAAO;AACpE;","names":[]}
|
|
@@ -9,10 +9,11 @@ import {
|
|
|
9
9
|
persistProjectCommandChoice,
|
|
10
10
|
projectCommandFingerprint,
|
|
11
11
|
shellSafeCommandName
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-HQI7NYGX.js";
|
|
13
|
+
import "./chunk-2AAOF5B5.js";
|
|
14
|
+
import "./chunk-VPNLCMVR.js";
|
|
15
|
+
import "./chunk-X4NJT436.js";
|
|
16
|
+
import "./chunk-ZPSZZUWF.js";
|
|
16
17
|
export {
|
|
17
18
|
ProjectCommandApprovalError,
|
|
18
19
|
assertProjectCommandApproved,
|
|
@@ -25,4 +26,4 @@ export {
|
|
|
25
26
|
projectCommandFingerprint,
|
|
26
27
|
shellSafeCommandName
|
|
27
28
|
};
|
|
28
|
-
//# sourceMappingURL=command-approvals-
|
|
29
|
+
//# sourceMappingURL=command-approvals-VBIU6BLM.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentContextCache,
|
|
3
|
+
buildMessages,
|
|
4
|
+
buildSystemPrompt,
|
|
5
|
+
buildSystemPromptZones
|
|
6
|
+
} from "./chunk-IME7R2UI.js";
|
|
7
|
+
import "./chunk-AQEQX4FF.js";
|
|
8
|
+
import "./chunk-67UZEWWF.js";
|
|
9
|
+
import "./chunk-W3XFHEF4.js";
|
|
10
|
+
import "./chunk-6OQQOMZR.js";
|
|
11
|
+
import "./chunk-J62VZYOB.js";
|
|
12
|
+
import "./chunk-JTPOI3KU.js";
|
|
13
|
+
import "./chunk-4AS7LKKQ.js";
|
|
14
|
+
import "./chunk-QYBD22H5.js";
|
|
15
|
+
import "./chunk-EFJ2NH6E.js";
|
|
16
|
+
import "./chunk-KXAYUIKP.js";
|
|
17
|
+
import "./chunk-HQI7NYGX.js";
|
|
18
|
+
import "./chunk-2AAOF5B5.js";
|
|
19
|
+
import "./chunk-C7ADUNPR.js";
|
|
20
|
+
import "./chunk-RT5SATF7.js";
|
|
21
|
+
import "./chunk-XHAO3K7I.js";
|
|
22
|
+
import "./chunk-525CAZPY.js";
|
|
23
|
+
import "./chunk-X66I2NQO.js";
|
|
24
|
+
import "./chunk-62QL5QUP.js";
|
|
25
|
+
import "./chunk-224FJZLH.js";
|
|
26
|
+
import "./chunk-KBQS35OV.js";
|
|
27
|
+
import "./chunk-SOW4WRQP.js";
|
|
28
|
+
import "./chunk-VPNLCMVR.js";
|
|
29
|
+
import "./chunk-X4NJT436.js";
|
|
30
|
+
import "./chunk-ZPSZZUWF.js";
|
|
31
|
+
import "./chunk-5RDRAO4B.js";
|
|
32
|
+
export {
|
|
33
|
+
AgentContextCache,
|
|
34
|
+
buildMessages,
|
|
35
|
+
buildSystemPrompt,
|
|
36
|
+
buildSystemPromptZones
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=context-CBOBDY5Q.js.map
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
partitionProjectHooks,
|
|
7
7
|
persistProjectHookChoice,
|
|
8
8
|
renderUntrustedText
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-SOW4WRQP.js";
|
|
10
|
+
import "./chunk-VPNLCMVR.js";
|
|
11
|
+
import "./chunk-X4NJT436.js";
|
|
12
|
+
import "./chunk-ZPSZZUWF.js";
|
|
13
13
|
export {
|
|
14
14
|
collectDeclaredHooks,
|
|
15
15
|
describeDeclaredHook,
|
|
@@ -19,4 +19,4 @@ export {
|
|
|
19
19
|
persistProjectHookChoice,
|
|
20
20
|
renderUntrustedText
|
|
21
21
|
};
|
|
22
|
-
//# sourceMappingURL=hook-approvals-
|
|
22
|
+
//# sourceMappingURL=hook-approvals-ZVT4V7IM.js.map
|