@letrquan/book 0.3.0 → 0.4.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/CHANGELOG.md +1024 -0
  2. package/README.md +6 -3
  3. package/dist/{additional-roots-H7UNAEHS.js → additional-roots-VAWRQ625.js} +8 -7
  4. package/dist/{app-GE4OHJOC.js → app-J7BVPSUH.js} +5397 -2953
  5. package/dist/app-J7BVPSUH.js.map +1 -0
  6. package/dist/chunk-32WBCUAN.js +660 -0
  7. package/dist/chunk-32WBCUAN.js.map +1 -0
  8. package/dist/{chunk-525CAZPY.js → chunk-4DKHLZRI.js} +6 -6
  9. package/dist/{chunk-4AS7LKKQ.js → chunk-62EEAUID.js} +2 -2
  10. package/dist/{chunk-AQEQX4FF.js → chunk-AHC7OU75.js} +385 -55
  11. package/dist/chunk-AHC7OU75.js.map +1 -0
  12. package/dist/{chunk-EFJ2NH6E.js → chunk-AJZWLKLQ.js} +63 -14
  13. package/dist/chunk-AJZWLKLQ.js.map +1 -0
  14. package/dist/chunk-B5FC3MYZ.js +395 -0
  15. package/dist/chunk-B5FC3MYZ.js.map +1 -0
  16. package/dist/{chunk-67UZEWWF.js → chunk-DNFQTSMJ.js} +53 -11
  17. package/dist/chunk-DNFQTSMJ.js.map +1 -0
  18. package/dist/{chunk-TLX3H2CL.js → chunk-E5WLQSKA.js} +231 -40
  19. package/dist/chunk-E5WLQSKA.js.map +1 -0
  20. package/dist/chunk-ECLC4CIJ.js +1181 -0
  21. package/dist/chunk-ECLC4CIJ.js.map +1 -0
  22. package/dist/chunk-EEX6QNXV.js +127 -0
  23. package/dist/chunk-EEX6QNXV.js.map +1 -0
  24. package/dist/chunk-EFXVSQ5J.js +931 -0
  25. package/dist/chunk-EFXVSQ5J.js.map +1 -0
  26. package/dist/{chunk-RFJ6Q6LE.js → chunk-ENLB7NYV.js} +577 -164
  27. package/dist/chunk-ENLB7NYV.js.map +1 -0
  28. package/dist/{chunk-IME7R2UI.js → chunk-FPR7SKIE.js} +1268 -883
  29. package/dist/chunk-FPR7SKIE.js.map +1 -0
  30. package/dist/{chunk-VPNLCMVR.js → chunk-HFB74CLM.js} +13 -5
  31. package/dist/chunk-HFB74CLM.js.map +1 -0
  32. package/dist/{chunk-5RDRAO4B.js → chunk-HOZXKOE3.js} +37 -1
  33. package/dist/chunk-HOZXKOE3.js.map +1 -0
  34. package/dist/chunk-IDYWN37T.js +36 -0
  35. package/dist/chunk-IDYWN37T.js.map +1 -0
  36. package/dist/{chunk-YC6W4EJH.js → chunk-JGF5HLSI.js} +193 -75
  37. package/dist/chunk-JGF5HLSI.js.map +1 -0
  38. package/dist/{chunk-5YHTKWJN.js → chunk-KDNWMKXV.js} +2447 -907
  39. package/dist/chunk-KDNWMKXV.js.map +1 -0
  40. package/dist/{chunk-Y2VHN7TC.js → chunk-MEOB2DZA.js} +11 -1
  41. package/dist/chunk-MEOB2DZA.js.map +1 -0
  42. package/dist/{chunk-KBQS35OV.js → chunk-MV5PQNIA.js} +2 -2
  43. package/dist/chunk-N2G4Q7ME.js +2672 -0
  44. package/dist/chunk-N2G4Q7ME.js.map +1 -0
  45. package/dist/{chunk-62QL5QUP.js → chunk-OJBMDM5H.js} +17 -1
  46. package/dist/chunk-OJBMDM5H.js.map +1 -0
  47. package/dist/{chunk-56SIEMDX.js → chunk-PTDC7ZUP.js} +2 -2
  48. package/dist/{chunk-X4NJT436.js → chunk-REASC2QG.js} +67 -28
  49. package/dist/chunk-REASC2QG.js.map +1 -0
  50. package/dist/chunk-SBTU5BEC.js +114 -0
  51. package/dist/chunk-SBTU5BEC.js.map +1 -0
  52. package/dist/{chunk-HQI7NYGX.js → chunk-SWPDKLNM.js} +2 -2
  53. package/dist/{chunk-W3XFHEF4.js → chunk-UCFJ43OS.js} +140 -28
  54. package/dist/chunk-UCFJ43OS.js.map +1 -0
  55. package/dist/{chunk-QYBD22H5.js → chunk-W7SLYS3M.js} +80 -16
  56. package/dist/chunk-W7SLYS3M.js.map +1 -0
  57. package/dist/{chunk-SOW4WRQP.js → chunk-WV3O5PGX.js} +3 -3
  58. package/dist/{chunk-JTPOI3KU.js → chunk-XJRJVXJR.js} +262 -38
  59. package/dist/chunk-XJRJVXJR.js.map +1 -0
  60. package/dist/{chunk-224FJZLH.js → chunk-YIL6HTJX.js} +228 -5
  61. package/dist/chunk-YIL6HTJX.js.map +1 -0
  62. package/dist/{command-approvals-VBIU6BLM.js → command-approvals-ZRY66LCW.js} +5 -4
  63. package/dist/context-C3RAP6K5.js +41 -0
  64. package/dist/{hook-approvals-ZVT4V7IM.js → hook-approvals-DPQD5H65.js} +5 -4
  65. package/dist/index.js +657 -754
  66. package/dist/index.js.map +1 -1
  67. package/dist/{interactive-assets-T4I4SEHU.js → interactive-assets-73AQO5WP.js} +11 -6
  68. package/dist/{interactive-assets-T4I4SEHU.js.map → interactive-assets-73AQO5WP.js.map} +1 -1
  69. package/dist/job-runner.js +10 -3
  70. package/dist/job-runner.js.map +1 -1
  71. package/dist/job-supervisor.js +13 -4
  72. package/dist/job-supervisor.js.map +1 -1
  73. package/dist/loader-DQNPDKOX.js +27 -0
  74. package/dist/{mcp-approvals-HJ2VDYMC.js → mcp-approvals-27LJYADQ.js} +5 -4
  75. package/dist/{mcp-config-RNB5LEVE.js → mcp-config-GXSODNCM.js} +3 -2
  76. package/dist/memory-store-I6Z54VUN.js +51 -0
  77. package/dist/{model-window-store-MTSQ5JDJ.js → model-window-store-6VKXXTPJ.js} +4 -3
  78. package/dist/{openai-compatible-2OB54YST.js → openai-compatible-IPUNBJ53.js} +5 -3
  79. package/dist/{permission-approvals-YLV6UC5W.js → permission-approvals-Q5IMFEZI.js} +5 -4
  80. package/dist/{permissions-DHOKF3W2.js → permissions-7Y35X6HZ.js} +26 -9
  81. package/dist/provider-approvals-5AGH3RVT.js +76 -0
  82. package/dist/{sandbox-WRYTZH3D.js → sandbox-N2RY72GN.js} +6 -5
  83. package/dist/{scrollback-35ME2GFX.js → scrollback-GD76GNM4.js} +36 -28
  84. package/dist/scrollback-GD76GNM4.js.map +1 -0
  85. package/dist/sdk.d.ts +876 -64
  86. package/dist/sdk.js +57 -37
  87. package/dist/sdk.js.map +1 -1
  88. package/dist/settings-loader-KDBH45AQ.js +43 -0
  89. package/dist/{settings-removed-JWWPT4WA.js → settings-removed-3ATWJEF3.js} +2 -2
  90. package/dist/shell-selection-Z5WORZU4.js +31 -0
  91. package/dist/shell-selection-Z5WORZU4.js.map +1 -0
  92. package/package.json +5 -5
  93. package/dist/app-GE4OHJOC.js.map +0 -1
  94. package/dist/chunk-224FJZLH.js.map +0 -1
  95. package/dist/chunk-5RDRAO4B.js.map +0 -1
  96. package/dist/chunk-5YHTKWJN.js.map +0 -1
  97. package/dist/chunk-62QL5QUP.js.map +0 -1
  98. package/dist/chunk-67UZEWWF.js.map +0 -1
  99. package/dist/chunk-6OQQOMZR.js +0 -785
  100. package/dist/chunk-6OQQOMZR.js.map +0 -1
  101. package/dist/chunk-AQEQX4FF.js.map +0 -1
  102. package/dist/chunk-C7ADUNPR.js +0 -273
  103. package/dist/chunk-C7ADUNPR.js.map +0 -1
  104. package/dist/chunk-EFJ2NH6E.js.map +0 -1
  105. package/dist/chunk-IME7R2UI.js.map +0 -1
  106. package/dist/chunk-J62VZYOB.js +0 -1339
  107. package/dist/chunk-J62VZYOB.js.map +0 -1
  108. package/dist/chunk-JTPOI3KU.js.map +0 -1
  109. package/dist/chunk-QYBD22H5.js.map +0 -1
  110. package/dist/chunk-RFJ6Q6LE.js.map +0 -1
  111. package/dist/chunk-S4JMID4G.js +0 -259
  112. package/dist/chunk-S4JMID4G.js.map +0 -1
  113. package/dist/chunk-TLX3H2CL.js.map +0 -1
  114. package/dist/chunk-VPNLCMVR.js.map +0 -1
  115. package/dist/chunk-W3XFHEF4.js.map +0 -1
  116. package/dist/chunk-X4NJT436.js.map +0 -1
  117. package/dist/chunk-X66I2NQO.js +0 -137
  118. package/dist/chunk-X66I2NQO.js.map +0 -1
  119. package/dist/chunk-Y2VHN7TC.js.map +0 -1
  120. package/dist/chunk-YC6W4EJH.js.map +0 -1
  121. package/dist/context-CBOBDY5Q.js +0 -38
  122. package/dist/loader-S77OVTHH.js +0 -18
  123. package/dist/memory-store-DZUT5HUH.js +0 -75
  124. package/dist/scrollback-35ME2GFX.js.map +0 -1
  125. package/dist/settings-loader-JUNPGAAM.js +0 -35
  126. package/dist/shell-selection-CDHGJ4GC.js +0 -17
  127. /package/dist/{additional-roots-H7UNAEHS.js.map → additional-roots-VAWRQ625.js.map} +0 -0
  128. /package/dist/{chunk-525CAZPY.js.map → chunk-4DKHLZRI.js.map} +0 -0
  129. /package/dist/{chunk-4AS7LKKQ.js.map → chunk-62EEAUID.js.map} +0 -0
  130. /package/dist/{chunk-KBQS35OV.js.map → chunk-MV5PQNIA.js.map} +0 -0
  131. /package/dist/{chunk-56SIEMDX.js.map → chunk-PTDC7ZUP.js.map} +0 -0
  132. /package/dist/{chunk-HQI7NYGX.js.map → chunk-SWPDKLNM.js.map} +0 -0
  133. /package/dist/{chunk-SOW4WRQP.js.map → chunk-WV3O5PGX.js.map} +0 -0
  134. /package/dist/{command-approvals-VBIU6BLM.js.map → command-approvals-ZRY66LCW.js.map} +0 -0
  135. /package/dist/{context-CBOBDY5Q.js.map → context-C3RAP6K5.js.map} +0 -0
  136. /package/dist/{hook-approvals-ZVT4V7IM.js.map → hook-approvals-DPQD5H65.js.map} +0 -0
  137. /package/dist/{loader-S77OVTHH.js.map → loader-DQNPDKOX.js.map} +0 -0
  138. /package/dist/{mcp-approvals-HJ2VDYMC.js.map → mcp-approvals-27LJYADQ.js.map} +0 -0
  139. /package/dist/{mcp-config-RNB5LEVE.js.map → mcp-config-GXSODNCM.js.map} +0 -0
  140. /package/dist/{memory-store-DZUT5HUH.js.map → memory-store-I6Z54VUN.js.map} +0 -0
  141. /package/dist/{model-window-store-MTSQ5JDJ.js.map → model-window-store-6VKXXTPJ.js.map} +0 -0
  142. /package/dist/{openai-compatible-2OB54YST.js.map → openai-compatible-IPUNBJ53.js.map} +0 -0
  143. /package/dist/{permission-approvals-YLV6UC5W.js.map → permission-approvals-Q5IMFEZI.js.map} +0 -0
  144. /package/dist/{permissions-DHOKF3W2.js.map → permissions-7Y35X6HZ.js.map} +0 -0
  145. /package/dist/{sandbox-WRYTZH3D.js.map → provider-approvals-5AGH3RVT.js.map} +0 -0
  146. /package/dist/{settings-loader-JUNPGAAM.js.map → sandbox-N2RY72GN.js.map} +0 -0
  147. /package/dist/{settings-removed-JWWPT4WA.js.map → settings-loader-KDBH45AQ.js.map} +0 -0
  148. /package/dist/{shell-selection-CDHGJ4GC.js.map → settings-removed-3ATWJEF3.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/tools/aliases.ts","../src/tools/primary-arg.ts","../src/tools/result.ts","../src/tools/tool-capabilities.ts"],"sourcesContent":["/** Legacy tool-name aliases, resolved to their canonical PascalCase names. */\nexport const TOOL_ALIASES: Record<string, string> = {\n read_file: 'Read',\n write_file: 'Write',\n edit_file: 'Edit',\n multi_edit: 'MultiEdit',\n apply_patch: 'ApplyPatch',\n glob: 'Glob',\n glob_files: 'Glob',\n grep: 'Grep',\n bash: 'Bash',\n // Legacy snake_case git tool names — backward compatible.\n git_status: 'GitStatus',\n git_diff: 'GitDiff',\n git_log: 'GitLog',\n git_commit: 'GitCommit',\n git_branch: 'GitBranch',\n};\n\n/** Map a (possibly aliased) tool name to its canonical name for display / matching. */\nexport function canonicalToolName(name: string): string {\n return TOOL_ALIASES[name] ?? name;\n}\n","/**\n * Extract the primary argument from a tool call for display and rule matching.\n *\n * Priority order follows Claude Code's convention:\n * command → shell_id → shellId → taskId → task_id → filePath → file_path\n * → notebook_path → pattern → path → agent → skill → message → url → query → subject\n * → old_string → first string key.\n *\n * `pattern` outranks `path`: for search tools (Grep) the pattern is the\n * rule-matching identity, while `path` is only an optional scope.\n *\n * For command-type args, only the first line is returned.\n */\nexport function getPrimaryArg(args: Record<string, unknown>): string {\n if (typeof args.command === 'string') return args.command.split('\\n')[0];\n if (typeof args.shell_id === 'string') return args.shell_id;\n if (typeof args.shellId === 'string') return args.shellId;\n if (typeof args.taskId === 'string') return args.taskId;\n if (typeof args.task_id === 'string') return args.task_id;\n if (typeof args.filePath === 'string') return args.filePath;\n if (typeof args.file_path === 'string') return args.file_path;\n if (typeof args.patch === 'string') {\n const match = args.patch.match(/^\\*\\*\\* (?:Update|Add|Delete) File: (.+)$/m);\n if (match) return match[1].trim();\n }\n if (typeof args.notebook_path === 'string') return args.notebook_path;\n if (typeof args.pattern === 'string') return args.pattern;\n if (typeof args.path === 'string') return args.path;\n if (typeof args.agent === 'string') return args.agent;\n if (typeof args.skill === 'string') return args.skill;\n if (typeof args.message === 'string') return args.message;\n if (typeof args.url === 'string') return args.url;\n if (typeof args.query === 'string') return args.query;\n if (typeof args.subject === 'string') return args.subject;\n if (typeof args.old_string === 'string') return args.old_string.slice(0, 60);\n const keys = Object.keys(args);\n if (keys.length > 0) {\n const v = args[keys[0]];\n return typeof v === 'string' ? v : '';\n }\n return '';\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport type {\n ToolResult,\n ToolResultArtifacts,\n ToolResultError,\n ToolResultPresentation,\n ToolResultStatus,\n} from '../types/tools.js';\nimport { canonicalToolName } from './aliases.js';\nimport { getPrimaryArg } from './primary-arg.js';\nimport { isFileMutatingTool } from './tool-capabilities.js';\nimport { resolveBookHome } from '../book-home.js';\n\nexport const TOOL_RESULT_MAX_BYTES = 50 * 1024;\n/**\n * What `Read` returns for a file with no lines in it. It lives here because the\n * row a transcript draws for a Read is reconstructed from its text as well as\n * from Read's own metadata, and both have to agree on these words.\n */\nexport const READ_EMPTY_FILE_NOTICE = '[Empty file: 0 lines.]';\n/**\n * Where clipped tool output is saved in full. The loop lets `Read` open each file\n * it clipped a result into, never the whole directory.\n */\nconst TOOL_OUTPUT_DIRECTORY = join(resolveBookHome(), 'tool-output');\n\ninterface ToolResultOptions<TData> {\n toolCallId?: string;\n data?: TData;\n presentation?: Partial<ToolResultPresentation>;\n artifacts?: ToolResultArtifacts;\n pagination?: ToolResult['pagination'];\n}\n\ninterface LegacyToolResult {\n version?: 2;\n toolCallId: string;\n status?: ToolResultStatus;\n content?: string;\n output?: string;\n success?: boolean;\n data?: unknown;\n error?: string;\n structuredError?: ToolResultError;\n presentation?: ToolResultPresentation;\n metrics?: ToolResult['metrics'];\n artifacts?: ToolResultArtifacts;\n durationMs?: number;\n retryAttempt?: number;\n fileMutation?: ToolResultArtifacts['fileMutation'];\n fileMutations?: ToolResultArtifacts['fileMutations'];\n fileObservations?: ToolResultArtifacts['fileObservations'];\n eventRef?: string;\n outputPath?: string;\n pagination?: ToolResult['pagination'];\n}\n\nfunction presentationFor(\n content: string,\n override?: Partial<ToolResultPresentation>,\n): ToolResultPresentation {\n return {\n kind: override?.kind ?? 'text',\n summary: override?.summary ?? content.split('\\n')[0]?.trim() ?? '',\n details: override?.details ?? content,\n metadata: override?.metadata,\n target: override?.target,\n };\n}\n\nexport function toolSuccess<TData = unknown>(\n content: string,\n options: ToolResultOptions<TData> = {},\n): ToolResult<TData> {\n return {\n version: 2,\n toolCallId: options.toolCallId ?? '',\n status: 'success',\n content,\n data: options.data,\n presentation: options.presentation ? presentationFor(content, options.presentation) : undefined,\n artifacts: options.artifacts,\n pagination: options.pagination,\n };\n}\n\nexport function toolFailure(\n message: string,\n options: ToolResultOptions<unknown> & {\n code?: string;\n status?: Exclude<ToolResultStatus, 'success'>;\n retryable?: boolean;\n remediation?: string;\n details?: Record<string, unknown>;\n content?: string;\n } = {},\n): ToolResult {\n const error: ToolResultError = {\n code: options.code ?? 'tool_error',\n message,\n retryable: options.retryable ?? false,\n remediation: options.remediation,\n details: options.details,\n };\n const content = options.content ?? '';\n return {\n version: 2,\n toolCallId: options.toolCallId ?? '',\n status: options.status ?? 'error',\n content,\n data: options.data,\n structuredError: error,\n presentation: options.presentation\n ? presentationFor(message, {\n summary: message.split('\\n')[0]?.trim(),\n details: message,\n ...options.presentation,\n })\n : undefined,\n artifacts: options.artifacts,\n pagination: options.pagination,\n };\n}\n\nexport function toolResultSucceeded(result: ToolResult): boolean {\n return result.status === 'success';\n}\n\nexport function toolResultErrorMessage(result: ToolResult): string | undefined {\n return result.structuredError?.message;\n}\n\nfunction rawToolResultModelContent(result: ToolResult): string {\n const content = result.content;\n if (toolResultSucceeded(result)) {\n return content.trim().length === 0 ? '(no output)' : content;\n }\n const remediation = result.structuredError?.remediation;\n const fix = remediation ? `\\nFix: ${remediation}` : '';\n const detail = content ? `\\n${content}` : '';\n return `ERROR [${result.structuredError?.code ?? result.status}]: ${toolResultErrorMessage(result) ?? 'tool failed'}${fix}${detail}`;\n}\n\nexport function toolResultModelContent(result: ToolResult): string {\n if (result.maskedPlaceholder !== undefined) return result.maskedPlaceholder;\n const raw = rawToolResultModelContent(result);\n if (Buffer.byteLength(raw) <= TOOL_RESULT_MAX_BYTES) return raw;\n // Reached only by callers that skipped `boundToolResultOutput`, which is\n // where an agent-loop result is clipped. Both places clip a failure to its\n // head and its tail, through the same helper.\n if (!toolResultSucceeded(result)) {\n const prefix = `ERROR [${result.structuredError?.code ?? result.status}]: `;\n const fix = result.structuredError?.remediation\n ? `\\nFix: ${result.structuredError.remediation}`\n : '';\n const budget = Math.max(\n 1,\n TOOL_RESULT_MAX_BYTES - Buffer.byteLength(prefix) - Buffer.byteLength(fix),\n );\n return `${prefix}${headTailPreview(failureText(result), result.artifacts?.outputPath, budget)}${fix}`;\n }\n return clippedOutputPreview(raw, result.artifacts?.outputPath, TOOL_RESULT_MAX_BYTES);\n}\n\nexport function utf8Prefix(text: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n const bytes = Buffer.from(text);\n if (bytes.byteLength <= maxBytes) return text;\n return bytes\n .subarray(0, maxBytes)\n .toString('utf8')\n .replace(/\\uFFFD$/u, '');\n}\n\nfunction utf8Suffix(text: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n const bytes = Buffer.from(text);\n if (bytes.byteLength <= maxBytes) return text;\n // Advance past any continuation bytes so the tail starts on a character\n // boundary. Cutting inside a 4-byte sequence otherwise strands three\n // continuation bytes, each of which decodes to its own replacement char.\n let start = bytes.byteLength - maxBytes;\n while (start < bytes.byteLength && (bytes[start] & 0xc0) === 0x80) start++;\n return bytes.subarray(start).toString('utf8');\n}\n\n/**\n * How much of a clipped failure's head survives: at most 8 KB, and never more\n * than a fifth of the budget, so the tail always has room to be worth keeping.\n */\nconst FAILURE_HEAD_MAX_BYTES = 8 * 1024;\n\n/** A failure's message and its output, as the two are read when the pair is clipped. */\nfunction failureText(result: ToolResult): string {\n const message = toolResultErrorMessage(result) ?? 'tool failed';\n return result.content ? `${message}\\n${result.content}` : message;\n}\n\n/**\n * A failure is judged on both ends, so a clipped one keeps both. The head\n * carries the error line, whatever framed the output — the Task tool's\n * `Partial result (the child was stopped; nothing below is final):` — and the\n * first diagnostics. The tail carries the verdict a test runner prints last.\n * Clipping to one end loses the other: a non-zero exit puts all of stderr in\n * the message and stdout in the content, and the summary is in the stdout, so a\n * tail-only clip of a large stderr reported the head instead of the verdict\n * (#308).\n */\nfunction headTailPreview(text: string, outputPath: string | undefined, maxBytes: number): string {\n const totalBytes = Buffer.byteLength(text);\n const headWindow = Math.min(FAILURE_HEAD_MAX_BYTES, Math.floor(maxBytes * 0.2));\n const window = utf8Prefix(text, headWindow);\n if (Buffer.byteLength(window) >= totalBytes) return text;\n // End the head on a whole line when one falls inside the kept window; a\n // half-line of output is harder to read than one line fewer.\n const lastBreak = window.lastIndexOf('\\n');\n const head = lastBreak >= 0 ? window.slice(0, lastBreak + 1) : window;\n // The newline that closed that line is not shown — the one that follows the\n // head here is a joiner — so it is part of what the notice has to count.\n const headShown = head.trimEnd();\n const headBytes = Buffer.byteLength(headShown);\n const omittedNotice = (omitted: number): string =>\n outputPath\n ? `[... ${omitted} bytes omitted. Full output: ${outputPath}]`\n : `[... ${omitted} bytes omitted. Full output unavailable.]`;\n // How much sits between the head and the tail is not known until the tail is,\n // but the notice's own width is: the widest count is the whole text, so that\n // notice is the room reserved before the tail is measured. The two newlines\n // that join the three parts are reserved with it, so the result stays inside\n // the budget the caller treats as final.\n const reserve = Buffer.byteLength(omittedNotice(totalBytes)) + 2;\n const tailRoom = maxBytes - headBytes - reserve;\n if (tailRoom <= 0) return [headShown, omittedNotice(totalBytes - headBytes)].join('\\n');\n // `utf8Suffix` lands on a character boundary; skip the rest of the line it\n // landed in so the tail starts on a line of its own.\n const tailWindow = utf8Suffix(text, tailRoom);\n const breakAt = tailWindow.indexOf('\\n');\n const tail = breakAt >= 0 ? tailWindow.slice(breakAt + 1) : tailWindow;\n const notice = omittedNotice(totalBytes - headBytes - Buffer.byteLength(tail));\n return [headShown, notice, tail].join('\\n');\n}\n\nfunction clippedOutputPreview(\n content: string,\n outputPath: string | undefined,\n maxBytes: number,\n): string {\n const notice = outputPath\n ? `\\n\\n[Output truncated at ${maxBytes} bytes. Full output: ${outputPath}]`\n : `\\n\\n[Output truncated at ${maxBytes} bytes. Full output unavailable.]`;\n const previewBudget = Math.max(0, maxBytes - Buffer.byteLength(notice));\n return `${utf8Prefix(content, previewBudget).trimEnd()}${notice}`;\n}\n\n/**\n * Bound every provider-facing tool result at the execution boundary. Complete output is stored in\n * Book's user-local data directory when possible, never in the tracked workspace.\n */\nexport async function boundToolResultOutput(\n input: ToolResult,\n _workspaceRoot: string,\n maxBytes = TOOL_RESULT_MAX_BYTES,\n artifactRoot = TOOL_OUTPUT_DIRECTORY,\n): Promise<ToolResult> {\n const result = normalizeToolResult(input);\n const modelContent = rawToolResultModelContent(result);\n const modelContentBytes = Buffer.byteLength(modelContent);\n const contentBytes = Buffer.byteLength(result.content);\n const details = result.presentation?.details;\n const detailsBytes = details === undefined ? 0 : Buffer.byteLength(details);\n if (modelContentBytes <= maxBytes && detailsBytes <= maxBytes) return result;\n\n let outputPath = result.artifacts?.outputPath;\n if (!outputPath) {\n const outputFile = join(artifactRoot, `${crypto.randomUUID()}.txt`);\n try {\n await mkdir(dirname(outputFile), { recursive: true });\n await writeFile(\n outputFile,\n detailsBytes > modelContentBytes && details ? details : modelContent,\n 'utf8',\n );\n outputPath = outputFile.replace(/\\\\/g, '/');\n } catch {\n // Clipping must still succeed in read-only or otherwise restricted environments.\n }\n }\n\n const modelOverflow = modelContentBytes > maxBytes;\n const content = toolResultSucceeded(result)\n ? contentBytes > maxBytes\n ? clippedOutputPreview(result.content, outputPath, maxBytes)\n : result.content\n : modelOverflow\n ? ''\n : result.content;\n const errorPrefix = `ERROR [${result.structuredError?.code ?? result.status}]: `;\n // Reserve room for the \"\\nFix: <remediation>\" line appended at render time so\n // the remediation survives instead of being re-clipped off the tail.\n const fixReserve = result.structuredError?.remediation\n ? Buffer.byteLength(`\\nFix: ${result.structuredError.remediation}`)\n : 0;\n const errorBudget = Math.max(1, maxBytes - Buffer.byteLength(errorPrefix) - fixReserve);\n const structuredError = result.structuredError\n ? {\n ...result.structuredError,\n // The message and the output are clipped as one text, so a failure\n // keeps both ends: the error line and any framing at the top, and the\n // verdict at the bottom. The room the `Fix:` line needs is reserved\n // above, so the remediation is never what gets clipped.\n message: modelOverflow\n ? headTailPreview(failureText(result), outputPath, errorBudget)\n : result.structuredError.message,\n }\n : result.structuredError;\n const clippedDetails = details\n ? detailsBytes > maxBytes\n ? // The transcript row reads the same way the model does: a failure's\n // head and tail, not one end of it — the same reason as above.\n toolResultSucceeded(result)\n ? clippedOutputPreview(details, outputPath, maxBytes)\n : headTailPreview(details, outputPath, maxBytes)\n : details\n : toolResultSucceeded(result)\n ? content\n : structuredError?.message;\n\n return {\n ...result,\n content,\n structuredError,\n presentation: result.presentation\n ? { ...result.presentation, details: clippedDetails }\n : result.presentation,\n artifacts: outputPath ? { ...result.artifacts, outputPath } : result.artifacts,\n pagination: {\n ...result.pagination,\n truncated: true,\n omittedBytes: Math.max(\n result.pagination?.omittedBytes ?? 0,\n Math.max(modelContentBytes, contentBytes, detailsBytes) - maxBytes,\n ),\n },\n };\n}\n\nexport function replaceToolResult(\n result: ToolResult,\n patch: {\n status?: ToolResultStatus;\n content?: string;\n error?: ToolResultError;\n presentation?: Partial<ToolResultPresentation>;\n },\n): ToolResult {\n const status = patch.status ?? result.status;\n const content = patch.content ?? result.content;\n return {\n ...result,\n status,\n content,\n // New content replaces what the model read: a placeholder standing in for\n // the previous text would hide the rewrite (a PostToolUse hook's\n // `modifiedOutput`, the plan-approval note) behind the line the tool wrote.\n ...(patch.content !== undefined ? { maskedPlaceholder: undefined } : {}),\n structuredError: patch.error ?? (status === 'success' ? undefined : result.structuredError),\n presentation:\n result.presentation || patch.presentation\n ? presentationFor(content, {\n ...result.presentation,\n ...patch.presentation,\n })\n : undefined,\n };\n}\n\n/** Upgrade persisted pre-V2 results while keeping the runtime and SDK contract V2-only. */\nexport function normalizeToolResult(result: ToolResult | LegacyToolResult): ToolResult {\n const legacy = result as LegacyToolResult;\n const legacySuccess = legacy.success;\n const legacyError = typeof legacy.error === 'string' ? legacy.error : undefined;\n const status: ToolResultStatus = result.status\n ? result.status\n : legacySuccess\n ? 'success'\n : legacyError?.startsWith('SKIPPED')\n ? 'blocked'\n : legacyError?.startsWith('CANCELLED')\n ? 'cancelled'\n : legacyError?.startsWith('Tool timeout')\n ? 'timed_out'\n : 'error';\n const content = result.content ?? legacy.output ?? '';\n const error =\n status === 'success'\n ? undefined\n : {\n ...(result.structuredError ?? {}),\n code: result.structuredError?.code ?? (status === 'blocked' ? 'blocked' : status),\n message: legacyError ?? result.structuredError?.message ?? 'Tool failed',\n retryable: result.structuredError?.retryable ?? status === 'timed_out',\n };\n const artifacts: ToolResultArtifacts | undefined =\n result.artifacts ??\n (legacy.fileMutation ||\n legacy.fileMutations ||\n legacy.fileObservations ||\n legacy.eventRef ||\n legacy.outputPath\n ? {\n fileMutation: legacy.fileMutation,\n fileMutations: legacy.fileMutations,\n fileObservations: legacy.fileObservations,\n eventRef: legacy.eventRef,\n outputPath: legacy.outputPath,\n }\n : undefined);\n const metrics =\n result.metrics ??\n (legacy.durationMs !== undefined || legacy.retryAttempt !== undefined\n ? { durationMs: legacy.durationMs, retryAttempt: legacy.retryAttempt }\n : undefined);\n return {\n version: 2,\n toolCallId: result.toolCallId,\n status,\n content,\n // Masking's placeholder survives a resume, or a restored compaction's tail\n // would come back at full size.\n ...('maskedPlaceholder' in result && result.maskedPlaceholder !== undefined\n ? { maskedPlaceholder: result.maskedPlaceholder }\n : {}),\n data: result.data,\n structuredError: error,\n presentation: result.presentation,\n artifacts,\n metrics,\n pagination: result.pagination,\n };\n}\n\nfunction nonEmptyLines(content: string): number {\n return content.split('\\n').filter((line) => line.trim()).length;\n}\n\n/** A Read row's line metadata: how many lines of the file, and their range when the read started partway in. */\nexport function readLineMetadata(start: number, count: number): string[] {\n // A row counts whole lines: the values reach this from Read's own floor and\n // from a page reconstructed out of its text, and either can be fractional.\n const first = Math.max(1, Math.floor(start));\n const lines = Math.max(0, Math.floor(count));\n if (lines <= 0) return ['empty'];\n const size = lines === 1 ? '1 line' : `${lines} lines`;\n // `121 lines · 1-121` says the same thing twice. The range earns its place only when the read\n // started partway into the file.\n return first > 1 ? [size, `${first}-${first + lines - 1}`] : [size];\n}\n\n/**\n * A Read row's metadata reconstructed from its text, for results that carry none (outlines, and\n * results persisted before tool results had a presentation): the lines of the file it returned\n * and their range, or how many declarations an outline listed. A read that stops early ends with\n * a notice (`[Lines 3-6 of 20 shown. …]`, `[Line 1 (60000 bytes) was cut …]`), which is not a\n * line of the file, and an empty file is nothing but its notice. Read's own results carry exact\n * metadata (readLineMetadata). Without Read's own count, a result persisted by a build that\n * numbered the empty element after a final newline still reads one line long, so a trailing\n * `N: ` is read past — a file ending in a blank line now returns the same text, and its phantom\n * line is gone.\n */\nexport function readResultMetadata(args: Record<string, unknown>, content: string): string[] {\n // An outline lists declarations under a header, not lines of the file.\n if (args.outline === true) {\n const entries = content.split('\\n').filter((line) => /^\\d+: /.test(line)).length;\n return ['outline', entries === 1 ? '1 entry' : `${entries} entries`];\n }\n let body =\n content === READ_EMPTY_FILE_NOTICE ? '' : content.replace(/\\n\\[Lines? \\d[^\\n]*\\]$/, '');\n if (body === content) body = body.replace(/(?:^|\\n)\\d+: $/, '');\n const lineCount = body ? body.split('\\n').length : 0;\n const offset = Number(args.offset ?? 0);\n const start = Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 1;\n return readLineMetadata(start, lineCount);\n}\n\n/**\n * A Grep row's count re-derived from its text, for results that carry none: a\n * result persisted by a build that did not have Grep set its own (#311). Grep\n * itself counts from the data it collected, which is exact where this is not —\n * a context line's text can hold a `12:30` of its own, and a match spanning\n * lines is several lines. In the unit `output_mode` asks for: matches in\n * `content` and `count` mode, files in `files_with_matches`.\n */\nexport function grepResultMetadata(args: Record<string, unknown>, content: string): string {\n const noun = args.output_mode === 'files_with_matches' ? 'file' : 'match';\n if (/^No matches found$/i.test(content.trim()))\n return `0 ${noun === 'file' ? 'files' : 'matches'}`;\n const lines = content.split('\\n');\n let count: number;\n if (noun === 'file') count = lines.filter((line) => line.trim()).length;\n else if (args.output_mode === 'count') {\n count = lines.reduce((total, line) => total + (Number(/:(\\d+)\\s*$/.exec(line)?.[1]) || 0), 0);\n } else count = lines.filter((line) => /:\\d+:/.test(line)).length || nonEmptyLines(content);\n return `${count} ${count === 1 ? noun : noun === 'file' ? 'files' : 'matches'}`;\n}\n\n/** Attach stable UI data while execution still has the tool name and arguments. */\nexport function enrichToolResultPresentation(\n input: ToolResult,\n toolName: string,\n args: Record<string, unknown>,\n): ToolResult {\n const result = normalizeToolResult(input);\n const name = canonicalToolName(toolName);\n const content = result.content;\n const explicitPresentation = result.presentation;\n const target = explicitPresentation?.target ?? (getPrimaryArg(args) || undefined);\n const metadata: string[] = explicitPresentation?.metadata\n ? [...explicitPresentation.metadata]\n : [];\n const inferKind = explicitPresentation?.kind === undefined;\n const inferSummary = explicitPresentation?.summary === undefined;\n const inferMetadata = explicitPresentation?.metadata === undefined;\n let kind: ToolResultPresentation['kind'] = explicitPresentation?.kind ?? 'text';\n let summary = explicitPresentation?.summary ?? content.split('\\n')[0]?.trim() ?? '';\n\n if (isFileMutatingTool(name)) {\n if (inferKind) {\n kind = result.artifacts?.fileMutation ? 'file' : /^@@/m.test(content) ? 'diff' : 'file';\n }\n const mutation = result.artifacts?.fileMutation ?? result.artifacts?.fileMutations?.[0];\n if (mutation) {\n if (inferMetadata) {\n if (mutation.addedLines) metadata.push(`+${mutation.addedLines}`);\n if (mutation.removedLines) metadata.push(`-${mutation.removedLines}`);\n if (!mutation.addedLines && !mutation.removedLines) metadata.push('no changes');\n }\n if (inferSummary) {\n summary = `${mutation.kind === 'create' ? 'Created' : mutation.kind === 'delete' ? 'Deleted' : 'Updated'} ${mutation.filePath}`;\n }\n }\n } else if (name === 'Read') {\n if (inferKind) kind = 'file';\n if (inferMetadata && result.status === 'success')\n metadata.push(...readResultMetadata(args, content));\n if (inferSummary) summary = target ? `Read ${target}` : summary;\n } else if (name === 'Glob') {\n if (inferKind) kind = 'search';\n const count = /^(?:No files found|No matches found)$/i.test(content.trim())\n ? 0\n : nonEmptyLines(content.replace(/\\n\\.\\.\\. \\(truncated[\\s\\S]*$/, ''));\n if (inferMetadata) metadata.push(`${count} ${count === 1 ? 'file' : 'files'}`);\n if (inferSummary) summary = `Found ${count} ${count === 1 ? 'file' : 'files'}`;\n } else if (name === 'Grep') {\n if (inferKind) kind = 'search';\n // Only a search that ran has matches to count; a failed one is a row with\n // the error, the way Read's is.\n if (result.status === 'success') {\n const label = grepResultMetadata(args, content);\n if (inferMetadata) metadata.push(label);\n if (inferSummary) summary = `Found ${label}`;\n }\n } else if (name === 'Bash' || name === 'BashOutput' || name === 'KillShell') {\n if (inferKind) kind = 'command';\n if (inferSummary) summary = target ? `${name}: ${target}` : summary;\n } else if (name === 'GitDiff') {\n if (inferKind) kind = 'diff';\n } else if (name.startsWith('Web') || name === 'ToolSearch' || name.startsWith('SessionHistory')) {\n if (inferKind) kind = name === 'ToolSearch' ? 'search' : 'markdown';\n } else if (name.startsWith('Task') || name === 'TodoWrite') {\n if (inferKind) kind = 'task';\n } else if (name.startsWith('Agent') || name.startsWith('Evidence')) {\n if (inferKind) kind = 'agent';\n }\n\n return {\n ...result,\n presentation: {\n kind,\n summary,\n details: explicitPresentation?.details ?? content,\n metadata,\n target: explicitPresentation?.target ?? result.artifacts?.fileMutation?.filePath ?? target,\n },\n };\n}\n","export const FILE_MUTATING_TOOLS = new Set([\n 'ApplyPatch',\n 'Write',\n 'Edit',\n 'MultiEdit',\n 'NotebookEdit',\n]);\n\nexport function isFileMutatingTool(canonicalName: string): boolean {\n return FILE_MUTATING_TOOLS.has(canonicalName);\n}\n"],"mappings":";;;;;AACO,IAAM,eAAuC;AAAA,EAClD,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AAAA;AAAA,EAEN,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,YAAY;AACd;AAGO,SAAS,kBAAkB,MAAsB;AACtD,SAAO,aAAa,IAAI,KAAK;AAC/B;;;ACTO,SAAS,cAAc,MAAuC;AACnE,MAAI,OAAO,KAAK,YAAY,SAAU,QAAO,KAAK,QAAQ,MAAM,IAAI,EAAE,CAAC;AACvE,MAAI,OAAO,KAAK,aAAa,SAAU,QAAO,KAAK;AACnD,MAAI,OAAO,KAAK,YAAY,SAAU,QAAO,KAAK;AAClD,MAAI,OAAO,KAAK,WAAW,SAAU,QAAO,KAAK;AACjD,MAAI,OAAO,KAAK,YAAY,SAAU,QAAO,KAAK;AAClD,MAAI,OAAO,KAAK,aAAa,SAAU,QAAO,KAAK;AACnD,MAAI,OAAO,KAAK,cAAc,SAAU,QAAO,KAAK;AACpD,MAAI,OAAO,KAAK,UAAU,UAAU;AAClC,UAAM,QAAQ,KAAK,MAAM,MAAM,4CAA4C;AAC3E,QAAI,MAAO,QAAO,MAAM,CAAC,EAAE,KAAK;AAAA,EAClC;AACA,MAAI,OAAO,KAAK,kBAAkB,SAAU,QAAO,KAAK;AACxD,MAAI,OAAO,KAAK,YAAY,SAAU,QAAO,KAAK;AAClD,MAAI,OAAO,KAAK,SAAS,SAAU,QAAO,KAAK;AAC/C,MAAI,OAAO,KAAK,UAAU,SAAU,QAAO,KAAK;AAChD,MAAI,OAAO,KAAK,UAAU,SAAU,QAAO,KAAK;AAChD,MAAI,OAAO,KAAK,YAAY,SAAU,QAAO,KAAK;AAClD,MAAI,OAAO,KAAK,QAAQ,SAAU,QAAO,KAAK;AAC9C,MAAI,OAAO,KAAK,UAAU,SAAU,QAAO,KAAK;AAChD,MAAI,OAAO,KAAK,YAAY,SAAU,QAAO,KAAK;AAClD,MAAI,OAAO,KAAK,eAAe,SAAU,QAAO,KAAK,WAAW,MAAM,GAAG,EAAE;AAC3E,QAAM,OAAO,OAAO,KAAK,IAAI;AAC7B,MAAI,KAAK,SAAS,GAAG;AACnB,UAAM,IAAI,KAAK,KAAK,CAAC,CAAC;AACtB,WAAO,OAAO,MAAM,WAAW,IAAI;AAAA,EACrC;AACA,SAAO;AACT;;;ACzCA,SAAS,OAAO,iBAAiB;AACjC,SAAS,SAAS,YAAY;;;ACDvB,IAAM,sBAAsB,oBAAI,IAAI;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,SAAS,mBAAmB,eAAgC;AACjE,SAAO,oBAAoB,IAAI,aAAa;AAC9C;;;ADIO,IAAM,wBAAwB,KAAK;AAMnC,IAAM,yBAAyB;AAKtC,IAAM,wBAAwB,KAAK,gBAAgB,GAAG,aAAa;AAiCnE,SAAS,gBACP,SACA,UACwB;AACxB,SAAO;AAAA,IACL,MAAM,UAAU,QAAQ;AAAA,IACxB,SAAS,UAAU,WAAW,QAAQ,MAAM,IAAI,EAAE,CAAC,GAAG,KAAK,KAAK;AAAA,IAChE,SAAS,UAAU,WAAW;AAAA,IAC9B,UAAU,UAAU;AAAA,IACpB,QAAQ,UAAU;AAAA,EACpB;AACF;AAEO,SAAS,YACd,SACA,UAAoC,CAAC,GAClB;AACnB,SAAO;AAAA,IACL,SAAS;AAAA,IACT,YAAY,QAAQ,cAAc;AAAA,IAClC,QAAQ;AAAA,IACR;AAAA,IACA,MAAM,QAAQ;AAAA,IACd,cAAc,QAAQ,eAAe,gBAAgB,SAAS,QAAQ,YAAY,IAAI;AAAA,IACtF,WAAW,QAAQ;AAAA,IACnB,YAAY,QAAQ;AAAA,EACtB;AACF;AAEO,SAAS,YACd,SACA,UAOI,CAAC,GACO;AACZ,QAAM,QAAyB;AAAA,IAC7B,MAAM,QAAQ,QAAQ;AAAA,IACtB;AAAA,IACA,WAAW,QAAQ,aAAa;AAAA,IAChC,aAAa,QAAQ;AAAA,IACrB,SAAS,QAAQ;AAAA,EACnB;AACA,QAAM,UAAU,QAAQ,WAAW;AACnC,SAAO;AAAA,IACL,SAAS;AAAA,IACT,YAAY,QAAQ,cAAc;AAAA,IAClC,QAAQ,QAAQ,UAAU;AAAA,IAC1B;AAAA,IACA,MAAM,QAAQ;AAAA,IACd,iBAAiB;AAAA,IACjB,cAAc,QAAQ,eAClB,gBAAgB,SAAS;AAAA,MACvB,SAAS,QAAQ,MAAM,IAAI,EAAE,CAAC,GAAG,KAAK;AAAA,MACtC,SAAS;AAAA,MACT,GAAG,QAAQ;AAAA,IACb,CAAC,IACD;AAAA,IACJ,WAAW,QAAQ;AAAA,IACnB,YAAY,QAAQ;AAAA,EACtB;AACF;AAEO,SAAS,oBAAoB,QAA6B;AAC/D,SAAO,OAAO,WAAW;AAC3B;AAEO,SAAS,uBAAuB,QAAwC;AAC7E,SAAO,OAAO,iBAAiB;AACjC;AAEA,SAAS,0BAA0B,QAA4B;AAC7D,QAAM,UAAU,OAAO;AACvB,MAAI,oBAAoB,MAAM,GAAG;AAC/B,WAAO,QAAQ,KAAK,EAAE,WAAW,IAAI,gBAAgB;AAAA,EACvD;AACA,QAAM,cAAc,OAAO,iBAAiB;AAC5C,QAAM,MAAM,cAAc;AAAA,OAAU,WAAW,KAAK;AACpD,QAAM,SAAS,UAAU;AAAA,EAAK,OAAO,KAAK;AAC1C,SAAO,UAAU,OAAO,iBAAiB,QAAQ,OAAO,MAAM,MAAM,uBAAuB,MAAM,KAAK,aAAa,GAAG,GAAG,GAAG,MAAM;AACpI;AAEO,SAAS,uBAAuB,QAA4B;AACjE,MAAI,OAAO,sBAAsB,OAAW,QAAO,OAAO;AAC1D,QAAM,MAAM,0BAA0B,MAAM;AAC5C,MAAI,OAAO,WAAW,GAAG,KAAK,sBAAuB,QAAO;AAI5D,MAAI,CAAC,oBAAoB,MAAM,GAAG;AAChC,UAAM,SAAS,UAAU,OAAO,iBAAiB,QAAQ,OAAO,MAAM;AACtE,UAAM,MAAM,OAAO,iBAAiB,cAChC;AAAA,OAAU,OAAO,gBAAgB,WAAW,KAC5C;AACJ,UAAM,SAAS,KAAK;AAAA,MAClB;AAAA,MACA,wBAAwB,OAAO,WAAW,MAAM,IAAI,OAAO,WAAW,GAAG;AAAA,IAC3E;AACA,WAAO,GAAG,MAAM,GAAG,gBAAgB,YAAY,MAAM,GAAG,OAAO,WAAW,YAAY,MAAM,CAAC,GAAG,GAAG;AAAA,EACrG;AACA,SAAO,qBAAqB,KAAK,OAAO,WAAW,YAAY,qBAAqB;AACtF;AAEO,SAAS,WAAW,MAAc,UAA0B;AACjE,MAAI,YAAY,EAAG,QAAO;AAC1B,QAAM,QAAQ,OAAO,KAAK,IAAI;AAC9B,MAAI,MAAM,cAAc,SAAU,QAAO;AACzC,SAAO,MACJ,SAAS,GAAG,QAAQ,EACpB,SAAS,MAAM,EACf,QAAQ,YAAY,EAAE;AAC3B;AAEA,SAAS,WAAW,MAAc,UAA0B;AAC1D,MAAI,YAAY,EAAG,QAAO;AAC1B,QAAM,QAAQ,OAAO,KAAK,IAAI;AAC9B,MAAI,MAAM,cAAc,SAAU,QAAO;AAIzC,MAAI,QAAQ,MAAM,aAAa;AAC/B,SAAO,QAAQ,MAAM,eAAe,MAAM,KAAK,IAAI,SAAU,IAAM;AACnE,SAAO,MAAM,SAAS,KAAK,EAAE,SAAS,MAAM;AAC9C;AAMA,IAAM,yBAAyB,IAAI;AAGnC,SAAS,YAAY,QAA4B;AAC/C,QAAM,UAAU,uBAAuB,MAAM,KAAK;AAClD,SAAO,OAAO,UAAU,GAAG,OAAO;AAAA,EAAK,OAAO,OAAO,KAAK;AAC5D;AAYA,SAAS,gBAAgB,MAAc,YAAgC,UAA0B;AAC/F,QAAM,aAAa,OAAO,WAAW,IAAI;AACzC,QAAM,aAAa,KAAK,IAAI,wBAAwB,KAAK,MAAM,WAAW,GAAG,CAAC;AAC9E,QAAM,SAAS,WAAW,MAAM,UAAU;AAC1C,MAAI,OAAO,WAAW,MAAM,KAAK,WAAY,QAAO;AAGpD,QAAM,YAAY,OAAO,YAAY,IAAI;AACzC,QAAM,OAAO,aAAa,IAAI,OAAO,MAAM,GAAG,YAAY,CAAC,IAAI;AAG/D,QAAM,YAAY,KAAK,QAAQ;AAC/B,QAAM,YAAY,OAAO,WAAW,SAAS;AAC7C,QAAM,gBAAgB,CAAC,YACrB,aACI,QAAQ,OAAO,gCAAgC,UAAU,MACzD,QAAQ,OAAO;AAMrB,QAAM,UAAU,OAAO,WAAW,cAAc,UAAU,CAAC,IAAI;AAC/D,QAAM,WAAW,WAAW,YAAY;AACxC,MAAI,YAAY,EAAG,QAAO,CAAC,WAAW,cAAc,aAAa,SAAS,CAAC,EAAE,KAAK,IAAI;AAGtF,QAAM,aAAa,WAAW,MAAM,QAAQ;AAC5C,QAAM,UAAU,WAAW,QAAQ,IAAI;AACvC,QAAM,OAAO,WAAW,IAAI,WAAW,MAAM,UAAU,CAAC,IAAI;AAC5D,QAAM,SAAS,cAAc,aAAa,YAAY,OAAO,WAAW,IAAI,CAAC;AAC7E,SAAO,CAAC,WAAW,QAAQ,IAAI,EAAE,KAAK,IAAI;AAC5C;AAEA,SAAS,qBACP,SACA,YACA,UACQ;AACR,QAAM,SAAS,aACX;AAAA;AAAA,uBAA4B,QAAQ,wBAAwB,UAAU,MACtE;AAAA;AAAA,uBAA4B,QAAQ;AACxC,QAAM,gBAAgB,KAAK,IAAI,GAAG,WAAW,OAAO,WAAW,MAAM,CAAC;AACtE,SAAO,GAAG,WAAW,SAAS,aAAa,EAAE,QAAQ,CAAC,GAAG,MAAM;AACjE;AAMA,eAAsB,sBACpB,OACA,gBACA,WAAW,uBACX,eAAe,uBACM;AACrB,QAAM,SAAS,oBAAoB,KAAK;AACxC,QAAM,eAAe,0BAA0B,MAAM;AACrD,QAAM,oBAAoB,OAAO,WAAW,YAAY;AACxD,QAAM,eAAe,OAAO,WAAW,OAAO,OAAO;AACrD,QAAM,UAAU,OAAO,cAAc;AACrC,QAAM,eAAe,YAAY,SAAY,IAAI,OAAO,WAAW,OAAO;AAC1E,MAAI,qBAAqB,YAAY,gBAAgB,SAAU,QAAO;AAEtE,MAAI,aAAa,OAAO,WAAW;AACnC,MAAI,CAAC,YAAY;AACf,UAAM,aAAa,KAAK,cAAc,GAAG,OAAO,WAAW,CAAC,MAAM;AAClE,QAAI;AACF,YAAM,MAAM,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AACpD,YAAM;AAAA,QACJ;AAAA,QACA,eAAe,qBAAqB,UAAU,UAAU;AAAA,QACxD;AAAA,MACF;AACA,mBAAa,WAAW,QAAQ,OAAO,GAAG;AAAA,IAC5C,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,gBAAgB,oBAAoB;AAC1C,QAAM,UAAU,oBAAoB,MAAM,IACtC,eAAe,WACb,qBAAqB,OAAO,SAAS,YAAY,QAAQ,IACzD,OAAO,UACT,gBACE,KACA,OAAO;AACb,QAAM,cAAc,UAAU,OAAO,iBAAiB,QAAQ,OAAO,MAAM;AAG3E,QAAM,aAAa,OAAO,iBAAiB,cACvC,OAAO,WAAW;AAAA,OAAU,OAAO,gBAAgB,WAAW,EAAE,IAChE;AACJ,QAAM,cAAc,KAAK,IAAI,GAAG,WAAW,OAAO,WAAW,WAAW,IAAI,UAAU;AACtF,QAAM,kBAAkB,OAAO,kBAC3B;AAAA,IACE,GAAG,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAKV,SAAS,gBACL,gBAAgB,YAAY,MAAM,GAAG,YAAY,WAAW,IAC5D,OAAO,gBAAgB;AAAA,EAC7B,IACA,OAAO;AACX,QAAM,iBAAiB,UACnB,eAAe;AAAA;AAAA;AAAA,IAGb,oBAAoB,MAAM,IACxB,qBAAqB,SAAS,YAAY,QAAQ,IAClD,gBAAgB,SAAS,YAAY,QAAQ;AAAA,MAC/C,UACF,oBAAoB,MAAM,IACxB,UACA,iBAAiB;AAEvB,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IACA,cAAc,OAAO,eACjB,EAAE,GAAG,OAAO,cAAc,SAAS,eAAe,IAClD,OAAO;AAAA,IACX,WAAW,aAAa,EAAE,GAAG,OAAO,WAAW,WAAW,IAAI,OAAO;AAAA,IACrE,YAAY;AAAA,MACV,GAAG,OAAO;AAAA,MACV,WAAW;AAAA,MACX,cAAc,KAAK;AAAA,QACjB,OAAO,YAAY,gBAAgB;AAAA,QACnC,KAAK,IAAI,mBAAmB,cAAc,YAAY,IAAI;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,kBACd,QACA,OAMY;AACZ,QAAM,SAAS,MAAM,UAAU,OAAO;AACtC,QAAM,UAAU,MAAM,WAAW,OAAO;AACxC,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA;AAAA;AAAA;AAAA;AAAA,IAIA,GAAI,MAAM,YAAY,SAAY,EAAE,mBAAmB,OAAU,IAAI,CAAC;AAAA,IACtE,iBAAiB,MAAM,UAAU,WAAW,YAAY,SAAY,OAAO;AAAA,IAC3E,cACE,OAAO,gBAAgB,MAAM,eACzB,gBAAgB,SAAS;AAAA,MACvB,GAAG,OAAO;AAAA,MACV,GAAG,MAAM;AAAA,IACX,CAAC,IACD;AAAA,EACR;AACF;AAGO,SAAS,oBAAoB,QAAmD;AACrF,QAAM,SAAS;AACf,QAAM,gBAAgB,OAAO;AAC7B,QAAM,cAAc,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AACtE,QAAM,SAA2B,OAAO,SACpC,OAAO,SACP,gBACE,YACA,aAAa,WAAW,SAAS,IAC/B,YACA,aAAa,WAAW,WAAW,IACjC,cACA,aAAa,WAAW,cAAc,IACpC,cACA;AACZ,QAAM,UAAU,OAAO,WAAW,OAAO,UAAU;AACnD,QAAM,QACJ,WAAW,YACP,SACA;AAAA,IACE,GAAI,OAAO,mBAAmB,CAAC;AAAA,IAC/B,MAAM,OAAO,iBAAiB,SAAS,WAAW,YAAY,YAAY;AAAA,IAC1E,SAAS,eAAe,OAAO,iBAAiB,WAAW;AAAA,IAC3D,WAAW,OAAO,iBAAiB,aAAa,WAAW;AAAA,EAC7D;AACN,QAAM,YACJ,OAAO,cACN,OAAO,gBACR,OAAO,iBACP,OAAO,oBACP,OAAO,YACP,OAAO,aACH;AAAA,IACE,cAAc,OAAO;AAAA,IACrB,eAAe,OAAO;AAAA,IACtB,kBAAkB,OAAO;AAAA,IACzB,UAAU,OAAO;AAAA,IACjB,YAAY,OAAO;AAAA,EACrB,IACA;AACN,QAAM,UACJ,OAAO,YACN,OAAO,eAAe,UAAa,OAAO,iBAAiB,SACxD,EAAE,YAAY,OAAO,YAAY,cAAc,OAAO,aAAa,IACnE;AACN,SAAO;AAAA,IACL,SAAS;AAAA,IACT,YAAY,OAAO;AAAA,IACnB;AAAA,IACA;AAAA;AAAA;AAAA,IAGA,GAAI,uBAAuB,UAAU,OAAO,sBAAsB,SAC9D,EAAE,mBAAmB,OAAO,kBAAkB,IAC9C,CAAC;AAAA,IACL,MAAM,OAAO;AAAA,IACb,iBAAiB;AAAA,IACjB,cAAc,OAAO;AAAA,IACrB;AAAA,IACA;AAAA,IACA,YAAY,OAAO;AAAA,EACrB;AACF;AAEA,SAAS,cAAc,SAAyB;AAC9C,SAAO,QAAQ,MAAM,IAAI,EAAE,OAAO,CAAC,SAAS,KAAK,KAAK,CAAC,EAAE;AAC3D;AAGO,SAAS,iBAAiB,OAAe,OAAyB;AAGvE,QAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC;AAC3C,QAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC;AAC3C,MAAI,SAAS,EAAG,QAAO,CAAC,OAAO;AAC/B,QAAM,OAAO,UAAU,IAAI,WAAW,GAAG,KAAK;AAG9C,SAAO,QAAQ,IAAI,CAAC,MAAM,GAAG,KAAK,IAAI,QAAQ,QAAQ,CAAC,EAAE,IAAI,CAAC,IAAI;AACpE;AAaO,SAAS,mBAAmB,MAA+B,SAA2B;AAE3F,MAAI,KAAK,YAAY,MAAM;AACzB,UAAM,UAAU,QAAQ,MAAM,IAAI,EAAE,OAAO,CAAC,SAAS,SAAS,KAAK,IAAI,CAAC,EAAE;AAC1E,WAAO,CAAC,WAAW,YAAY,IAAI,YAAY,GAAG,OAAO,UAAU;AAAA,EACrE;AACA,MAAI,OACF,YAAY,yBAAyB,KAAK,QAAQ,QAAQ,0BAA0B,EAAE;AACxF,MAAI,SAAS,QAAS,QAAO,KAAK,QAAQ,kBAAkB,EAAE;AAC9D,QAAM,YAAY,OAAO,KAAK,MAAM,IAAI,EAAE,SAAS;AACnD,QAAM,SAAS,OAAO,KAAK,UAAU,CAAC;AACtC,QAAM,QAAQ,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,KAAK,MAAM,MAAM,IAAI;AAC3E,SAAO,iBAAiB,OAAO,SAAS;AAC1C;AAUO,SAAS,mBAAmB,MAA+B,SAAyB;AACzF,QAAM,OAAO,KAAK,gBAAgB,uBAAuB,SAAS;AAClE,MAAI,sBAAsB,KAAK,QAAQ,KAAK,CAAC;AAC3C,WAAO,KAAK,SAAS,SAAS,UAAU,SAAS;AACnD,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,MAAI;AACJ,MAAI,SAAS,OAAQ,SAAQ,MAAM,OAAO,CAAC,SAAS,KAAK,KAAK,CAAC,EAAE;AAAA,WACxD,KAAK,gBAAgB,SAAS;AACrC,YAAQ,MAAM,OAAO,CAAC,OAAO,SAAS,SAAS,OAAO,aAAa,KAAK,IAAI,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC;AAAA,EAC9F,MAAO,SAAQ,MAAM,OAAO,CAAC,SAAS,QAAQ,KAAK,IAAI,CAAC,EAAE,UAAU,cAAc,OAAO;AACzF,SAAO,GAAG,KAAK,IAAI,UAAU,IAAI,OAAO,SAAS,SAAS,UAAU,SAAS;AAC/E;AAGO,SAAS,6BACd,OACA,UACA,MACY;AACZ,QAAM,SAAS,oBAAoB,KAAK;AACxC,QAAM,OAAO,kBAAkB,QAAQ;AACvC,QAAM,UAAU,OAAO;AACvB,QAAM,uBAAuB,OAAO;AACpC,QAAM,SAAS,sBAAsB,WAAW,cAAc,IAAI,KAAK;AACvE,QAAM,WAAqB,sBAAsB,WAC7C,CAAC,GAAG,qBAAqB,QAAQ,IACjC,CAAC;AACL,QAAM,YAAY,sBAAsB,SAAS;AACjD,QAAM,eAAe,sBAAsB,YAAY;AACvD,QAAM,gBAAgB,sBAAsB,aAAa;AACzD,MAAI,OAAuC,sBAAsB,QAAQ;AACzE,MAAI,UAAU,sBAAsB,WAAW,QAAQ,MAAM,IAAI,EAAE,CAAC,GAAG,KAAK,KAAK;AAEjF,MAAI,mBAAmB,IAAI,GAAG;AAC5B,QAAI,WAAW;AACb,aAAO,OAAO,WAAW,eAAe,SAAS,OAAO,KAAK,OAAO,IAAI,SAAS;AAAA,IACnF;AACA,UAAM,WAAW,OAAO,WAAW,gBAAgB,OAAO,WAAW,gBAAgB,CAAC;AACtF,QAAI,UAAU;AACZ,UAAI,eAAe;AACjB,YAAI,SAAS,WAAY,UAAS,KAAK,IAAI,SAAS,UAAU,EAAE;AAChE,YAAI,SAAS,aAAc,UAAS,KAAK,IAAI,SAAS,YAAY,EAAE;AACpE,YAAI,CAAC,SAAS,cAAc,CAAC,SAAS,aAAc,UAAS,KAAK,YAAY;AAAA,MAChF;AACA,UAAI,cAAc;AAChB,kBAAU,GAAG,SAAS,SAAS,WAAW,YAAY,SAAS,SAAS,WAAW,YAAY,SAAS,IAAI,SAAS,QAAQ;AAAA,MAC/H;AAAA,IACF;AAAA,EACF,WAAW,SAAS,QAAQ;AAC1B,QAAI,UAAW,QAAO;AACtB,QAAI,iBAAiB,OAAO,WAAW;AACrC,eAAS,KAAK,GAAG,mBAAmB,MAAM,OAAO,CAAC;AACpD,QAAI,aAAc,WAAU,SAAS,QAAQ,MAAM,KAAK;AAAA,EAC1D,WAAW,SAAS,QAAQ;AAC1B,QAAI,UAAW,QAAO;AACtB,UAAM,QAAQ,yCAAyC,KAAK,QAAQ,KAAK,CAAC,IACtE,IACA,cAAc,QAAQ,QAAQ,gCAAgC,EAAE,CAAC;AACrE,QAAI,cAAe,UAAS,KAAK,GAAG,KAAK,IAAI,UAAU,IAAI,SAAS,OAAO,EAAE;AAC7E,QAAI,aAAc,WAAU,SAAS,KAAK,IAAI,UAAU,IAAI,SAAS,OAAO;AAAA,EAC9E,WAAW,SAAS,QAAQ;AAC1B,QAAI,UAAW,QAAO;AAGtB,QAAI,OAAO,WAAW,WAAW;AAC/B,YAAM,QAAQ,mBAAmB,MAAM,OAAO;AAC9C,UAAI,cAAe,UAAS,KAAK,KAAK;AACtC,UAAI,aAAc,WAAU,SAAS,KAAK;AAAA,IAC5C;AAAA,EACF,WAAW,SAAS,UAAU,SAAS,gBAAgB,SAAS,aAAa;AAC3E,QAAI,UAAW,QAAO;AACtB,QAAI,aAAc,WAAU,SAAS,GAAG,IAAI,KAAK,MAAM,KAAK;AAAA,EAC9D,WAAW,SAAS,WAAW;AAC7B,QAAI,UAAW,QAAO;AAAA,EACxB,WAAW,KAAK,WAAW,KAAK,KAAK,SAAS,gBAAgB,KAAK,WAAW,gBAAgB,GAAG;AAC/F,QAAI,UAAW,QAAO,SAAS,eAAe,WAAW;AAAA,EAC3D,WAAW,KAAK,WAAW,MAAM,KAAK,SAAS,aAAa;AAC1D,QAAI,UAAW,QAAO;AAAA,EACxB,WAAW,KAAK,WAAW,OAAO,KAAK,KAAK,WAAW,UAAU,GAAG;AAClE,QAAI,UAAW,QAAO;AAAA,EACxB;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,cAAc;AAAA,MACZ;AAAA,MACA;AAAA,MACA,SAAS,sBAAsB,WAAW;AAAA,MAC1C;AAAA,MACA,QAAQ,sBAAsB,UAAU,OAAO,WAAW,cAAc,YAAY;AAAA,IACtF;AAAA,EACF;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/settings-removed.ts"],"sourcesContent":["/**\n * Settings that used to exist and no longer do.\n *\n * Removing a feature leaves its configuration behind on real machines, and zod\n * strips unknown keys silently — so a user who configured a removed capability\n * gets no signal at all that their setting stopped being read. Worse, a removed\n * *value* of a surviving key (`compactStrategy: \"zero-mem\"`) fails the schema\n * and takes the whole document down with a raw validator dump naming no remedy.\n *\n * This module is the one place that knows what was removed, so both failure\n * modes become a sentence that names the feature, the file, and the edit to\n * make. Notices are advisory: a stale key must not brick a working install, so\n * nothing here throws. `book doctor` prints them, and the credential error\n * consults them when the removed block was the user's only credential.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\n\n/** A removed setting found in a document, and what to tell the user about it. */\nexport interface RemovedSettingNotice {\n /** Dotted path of the offending key, e.g. `auth` or `compactStrategy`. */\n key: string;\n message: string;\n}\n\n/** Removed top-level settings blocks, with the guidance for each. */\nconst REMOVED_BLOCKS: ReadonlyArray<readonly [string, string]> = [\n [\n 'auth',\n 'Subscription authentication was removed; Book authenticates with API keys only. ' +\n 'Delete the \"auth\" block, and set BOOK_API_KEY or provider.<id>.apiKey instead.',\n ],\n [\n 'harness',\n 'The adaptive harness was removed. Delete the \"harness\" block; it is no longer read.',\n ],\n [\n 'experimental',\n 'The experimental Zero-Mem capability was removed and with it the \"experimental\" block. ' +\n 'Delete it; compaction is always the production summary strategy now.',\n ],\n];\n\n/** The credential file `book auth login` used to write, which nothing reads any more. */\nexport const REMOVED_AUTH_STORE_FILENAME = 'auth.json';\n\n/**\n * Guidance for the orphaned credential store. The file is deliberately not\n * deleted for the user: it is theirs, it is mode 0600, and a tool that removes\n * credentials without being asked is worse than one that reports them.\n */\nexport function removedAuthStoreNotice(path: string): string {\n return (\n `${path} holds an OAuth refresh token from the removed subscription login. ` +\n 'Nothing reads it any more — delete the file, and revoke the token with the provider ' +\n 'if it was ever used.'\n );\n}\n\n/** Environment variables that no longer do anything, with their guidance. */\nconst REMOVED_ENV_VARS: ReadonlyArray<readonly [string, string]> = [\n ['BOOK_AUTH_PROFILE', 'Subscription authentication was removed; use BOOK_API_KEY.'],\n ['BOOK_AUTH_CLIENT_ID', 'Subscription authentication was removed; use BOOK_API_KEY.'],\n ['BOOK_AUTH_CLIENT_SECRET', 'Subscription authentication was removed; use BOOK_API_KEY.'],\n ['BOOK_EXPERIMENTAL_ZERO_MEM', 'Zero-Mem was removed; summary compaction is the only strategy.'],\n ['BOOK_ZERO_MEM_MODEL_CACHE', 'Zero-Mem was removed.'],\n ['BOOK_ZERO_MEM_LOCAL_FILES_ONLY', 'Zero-Mem was removed.'],\n [\n 'BOOK_COMPACT_STRATEGY',\n 'Compaction strategy is no longer selectable; summary is the only one.',\n ],\n];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Parse a settings file for inspection, or undefined if it is missing or unreadable. */\nexport function readSettingsDocumentForNotices(path: string): unknown {\n try {\n if (!existsSync(path)) return undefined;\n return JSON.parse(readFileSync(path, 'utf-8'));\n } catch {\n // A malformed file is a different problem, reported by the loader.\n return undefined;\n }\n}\n\n/**\n * Did this machine carry subscription-auth configuration that is no longer read?\n *\n * Consulted only on the \"no credential\" error path, so the file reads cost\n * nothing in the normal case.\n */\nexport function hadRemovedAuthConfiguration(\n settingsFilePaths: readonly string[],\n env: NodeJS.ProcessEnv,\n): boolean {\n if (Object.keys(env).some((key) => key.startsWith('BOOK_AUTH_'))) return true;\n return settingsFilePaths.some((path) => {\n const document = readSettingsDocumentForNotices(path);\n return isRecord(document) && document.auth !== undefined;\n });\n}\n\n/**\n * Removed settings present in one parsed settings document.\n *\n * Takes the *raw* parsed JSON rather than a validated document, because the\n * whole point is to see the keys validation is about to discard.\n */\nexport function collectRemovedSettingNotices(document: unknown): RemovedSettingNotice[] {\n if (!isRecord(document)) return [];\n const notices: RemovedSettingNotice[] = [];\n for (const [key, message] of REMOVED_BLOCKS) {\n if (document[key] !== undefined) notices.push({ key, message });\n }\n // A removed *value* of a surviving key. `compactStrategy` still exists and is\n // now `\"summary\"` only, so the old Zero-Mem selector has to be named\n // explicitly or the user is left reading a schema error about a literal type.\n if (document.compactStrategy !== undefined && document.compactStrategy !== 'summary') {\n notices.push({\n key: 'compactStrategy',\n message:\n `compactStrategy: ${JSON.stringify(document.compactStrategy)} is no longer a choice. ` +\n 'Zero-Mem was removed and summary compaction is the only strategy; delete the key.',\n });\n }\n return notices;\n}\n\n/** Removed environment variables that are set in `env`, with their guidance. */\nexport function collectRemovedEnvNotices(env: NodeJS.ProcessEnv): RemovedSettingNotice[] {\n const notices: RemovedSettingNotice[] = [];\n for (const [name, message] of REMOVED_ENV_VARS) {\n // The client-id variables are per-profile suffixed, so match by prefix.\n const matches =\n name.endsWith('CLIENT_ID') || name.endsWith('CLIENT_SECRET')\n ? Object.keys(env).filter((key) => key.startsWith(`${name}_`) || key === name)\n : env[name] !== undefined\n ? [name]\n : [];\n for (const key of matches) notices.push({ key, message });\n }\n return notices;\n}\n\n/**\n * Drop removed keys and coerce removed values so a stale document still loads.\n *\n * Unknown blocks are stripped by validation anyway; the value coercion is the\n * part that matters, because without it one obsolete string fails the whole\n * file and Book will not start. Returns a copy — the caller's document is left\n * alone so it can still be reported on.\n */\nexport function normalizeRemovedSettings(document: unknown): unknown {\n if (!isRecord(document)) return document;\n if (document.compactStrategy === undefined || document.compactStrategy === 'summary') {\n return document;\n }\n const normalized = { ...document };\n delete normalized.compactStrategy;\n return normalized;\n}\n"],"mappings":";AAgBA,SAAS,YAAY,oBAAoB;AAUzC,IAAM,iBAA2D;AAAA,EAC/D;AAAA,IACE;AAAA,IACA;AAAA,EAEF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,EAEF;AACF;AAGO,IAAM,8BAA8B;AAOpC,SAAS,uBAAuB,MAAsB;AAC3D,SACE,GAAG,IAAI;AAIX;AAGA,IAAM,mBAA6D;AAAA,EACjE,CAAC,qBAAqB,4DAA4D;AAAA,EAClF,CAAC,uBAAuB,4DAA4D;AAAA,EACpF,CAAC,2BAA2B,4DAA4D;AAAA,EACxF,CAAC,8BAA8B,gEAAgE;AAAA,EAC/F,CAAC,6BAA6B,uBAAuB;AAAA,EACrD,CAAC,kCAAkC,uBAAuB;AAAA,EAC1D;AAAA,IACE;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAGO,SAAS,+BAA+B,MAAuB;AACpE,MAAI;AACF,QAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,WAAO,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;AAAA,EAC/C,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,4BACd,mBACA,KACS;AACT,MAAI,OAAO,KAAK,GAAG,EAAE,KAAK,CAAC,QAAQ,IAAI,WAAW,YAAY,CAAC,EAAG,QAAO;AACzE,SAAO,kBAAkB,KAAK,CAAC,SAAS;AACtC,UAAM,WAAW,+BAA+B,IAAI;AACpD,WAAO,SAAS,QAAQ,KAAK,SAAS,SAAS;AAAA,EACjD,CAAC;AACH;AAQO,SAAS,6BAA6B,UAA2C;AACtF,MAAI,CAAC,SAAS,QAAQ,EAAG,QAAO,CAAC;AACjC,QAAM,UAAkC,CAAC;AACzC,aAAW,CAAC,KAAK,OAAO,KAAK,gBAAgB;AAC3C,QAAI,SAAS,GAAG,MAAM,OAAW,SAAQ,KAAK,EAAE,KAAK,QAAQ,CAAC;AAAA,EAChE;AAIA,MAAI,SAAS,oBAAoB,UAAa,SAAS,oBAAoB,WAAW;AACpF,YAAQ,KAAK;AAAA,MACX,KAAK;AAAA,MACL,SACE,oBAAoB,KAAK,UAAU,SAAS,eAAe,CAAC;AAAA,IAEhE,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAGO,SAAS,yBAAyB,KAAgD;AACvF,QAAM,UAAkC,CAAC;AACzC,aAAW,CAAC,MAAM,OAAO,KAAK,kBAAkB;AAE9C,UAAM,UACJ,KAAK,SAAS,WAAW,KAAK,KAAK,SAAS,eAAe,IACvD,OAAO,KAAK,GAAG,EAAE,OAAO,CAAC,QAAQ,IAAI,WAAW,GAAG,IAAI,GAAG,KAAK,QAAQ,IAAI,IAC3E,IAAI,IAAI,MAAM,SACZ,CAAC,IAAI,IACL,CAAC;AACT,eAAW,OAAO,QAAS,SAAQ,KAAK,EAAE,KAAK,QAAQ,CAAC;AAAA,EAC1D;AACA,SAAO;AACT;AAUO,SAAS,yBAAyB,UAA4B;AACnE,MAAI,CAAC,SAAS,QAAQ,EAAG,QAAO;AAChC,MAAI,SAAS,oBAAoB,UAAa,SAAS,oBAAoB,WAAW;AACpF,WAAO;AAAA,EACT;AACA,QAAM,aAAa,EAAE,GAAG,SAAS;AACjC,SAAO,WAAW;AAClB,SAAO;AACT;","names":[]}