@letrquan/book 0.2.0 → 0.4.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/CHANGELOG.md +3771 -40
  2. package/README.md +135 -1311
  3. package/dist/additional-roots-VAWRQ625.js +32 -0
  4. package/dist/{app-MOXXWEOY.js → app-J7BVPSUH.js} +9648 -5509
  5. package/dist/app-J7BVPSUH.js.map +1 -0
  6. package/dist/chunk-2AAOF5B5.js +28 -0
  7. package/dist/chunk-2AAOF5B5.js.map +1 -0
  8. package/dist/chunk-32WBCUAN.js +660 -0
  9. package/dist/chunk-32WBCUAN.js.map +1 -0
  10. package/dist/chunk-4DKHLZRI.js +120 -0
  11. package/dist/chunk-4DKHLZRI.js.map +1 -0
  12. package/dist/{chunk-CAGOB2N4.js → chunk-62EEAUID.js} +6 -4
  13. package/dist/chunk-62EEAUID.js.map +1 -0
  14. package/dist/chunk-AHC7OU75.js +1124 -0
  15. package/dist/chunk-AHC7OU75.js.map +1 -0
  16. package/dist/{chunk-E75IPST5.js → chunk-AJZWLKLQ.js} +73 -70
  17. package/dist/chunk-AJZWLKLQ.js.map +1 -0
  18. package/dist/chunk-B5FC3MYZ.js +395 -0
  19. package/dist/chunk-B5FC3MYZ.js.map +1 -0
  20. package/dist/{chunk-KWHQ2XXL.js → chunk-DNFQTSMJ.js} +55 -13
  21. package/dist/chunk-DNFQTSMJ.js.map +1 -0
  22. package/dist/{chunk-J7MIZ5CA.js → chunk-E5WLQSKA.js} +719 -89
  23. package/dist/chunk-E5WLQSKA.js.map +1 -0
  24. package/dist/chunk-ECLC4CIJ.js +1181 -0
  25. package/dist/chunk-ECLC4CIJ.js.map +1 -0
  26. package/dist/chunk-EEX6QNXV.js +127 -0
  27. package/dist/chunk-EEX6QNXV.js.map +1 -0
  28. package/dist/chunk-EFXVSQ5J.js +931 -0
  29. package/dist/chunk-EFXVSQ5J.js.map +1 -0
  30. package/dist/{chunk-V4BOYR52.js → chunk-ENLB7NYV.js} +1304 -326
  31. package/dist/chunk-ENLB7NYV.js.map +1 -0
  32. package/dist/chunk-FPR7SKIE.js +6926 -0
  33. package/dist/chunk-FPR7SKIE.js.map +1 -0
  34. package/dist/{chunk-DGMQEI6Y.js → chunk-HFB74CLM.js} +30 -9
  35. package/dist/chunk-HFB74CLM.js.map +1 -0
  36. package/dist/{chunk-5RDRAO4B.js → chunk-HOZXKOE3.js} +37 -1
  37. package/dist/chunk-HOZXKOE3.js.map +1 -0
  38. package/dist/chunk-IDYWN37T.js +36 -0
  39. package/dist/chunk-IDYWN37T.js.map +1 -0
  40. package/dist/chunk-J4F2QLM2.js +273 -0
  41. package/dist/chunk-J4F2QLM2.js.map +1 -0
  42. package/dist/chunk-JGF5HLSI.js +1065 -0
  43. package/dist/chunk-JGF5HLSI.js.map +1 -0
  44. package/dist/{chunk-XIFFTATU.js → chunk-KDNWMKXV.js} +7166 -4237
  45. package/dist/chunk-KDNWMKXV.js.map +1 -0
  46. package/dist/chunk-KXAYUIKP.js +90 -0
  47. package/dist/chunk-KXAYUIKP.js.map +1 -0
  48. package/dist/{chunk-3U5IBM24.js → chunk-MEOB2DZA.js} +12 -2
  49. package/dist/chunk-MEOB2DZA.js.map +1 -0
  50. package/dist/{chunk-WKBO5R4O.js → chunk-MV5PQNIA.js} +2 -2
  51. package/dist/chunk-MV5PQNIA.js.map +1 -0
  52. package/dist/chunk-N2G4Q7ME.js +2672 -0
  53. package/dist/chunk-N2G4Q7ME.js.map +1 -0
  54. package/dist/chunk-OJBMDM5H.js +299 -0
  55. package/dist/chunk-OJBMDM5H.js.map +1 -0
  56. package/dist/{chunk-URLQAXTR.js → chunk-PTDC7ZUP.js} +2 -2
  57. package/dist/chunk-PTDC7ZUP.js.map +1 -0
  58. package/dist/{chunk-574C6MQR.js → chunk-REASC2QG.js} +173 -64
  59. package/dist/chunk-REASC2QG.js.map +1 -0
  60. package/dist/{chunk-7N4J557C.js → chunk-RT5SATF7.js} +1 -1
  61. package/dist/chunk-RT5SATF7.js.map +1 -0
  62. package/dist/chunk-SBTU5BEC.js +114 -0
  63. package/dist/chunk-SBTU5BEC.js.map +1 -0
  64. package/dist/{chunk-5KLVH3PY.js → chunk-SWPDKLNM.js} +5 -3
  65. package/dist/chunk-SWPDKLNM.js.map +1 -0
  66. package/dist/chunk-UCFJ43OS.js +520 -0
  67. package/dist/chunk-UCFJ43OS.js.map +1 -0
  68. package/dist/{chunk-VZQXX3WW.js → chunk-W7SLYS3M.js} +80 -16
  69. package/dist/chunk-W7SLYS3M.js.map +1 -0
  70. package/dist/{chunk-ANSJPYGI.js → chunk-WV3O5PGX.js} +3 -3
  71. package/dist/chunk-WV3O5PGX.js.map +1 -0
  72. package/dist/chunk-XHAO3K7I.js +18 -0
  73. package/dist/chunk-XHAO3K7I.js.map +1 -0
  74. package/dist/chunk-XJRJVXJR.js +1050 -0
  75. package/dist/chunk-XJRJVXJR.js.map +1 -0
  76. package/dist/chunk-YIL6HTJX.js +663 -0
  77. package/dist/chunk-YIL6HTJX.js.map +1 -0
  78. package/dist/{chunk-24AXE6SP.js → chunk-ZPSZZUWF.js} +1 -1
  79. package/dist/chunk-ZPSZZUWF.js.map +1 -0
  80. package/dist/{command-approvals-6Y57VCWZ.js → command-approvals-ZRY66LCW.js} +7 -5
  81. package/dist/context-C3RAP6K5.js +41 -0
  82. package/dist/{hook-approvals-CTUFKEJT.js → hook-approvals-DPQD5H65.js} +6 -5
  83. package/dist/index.js +947 -203
  84. package/dist/index.js.map +1 -1
  85. package/dist/{interactive-assets-CCAAWVKH.js → interactive-assets-73AQO5WP.js} +18 -9
  86. package/dist/interactive-assets-73AQO5WP.js.map +1 -0
  87. package/dist/job-runner.js +166 -111
  88. package/dist/job-runner.js.map +1 -1
  89. package/dist/job-supervisor.d.ts +2 -0
  90. package/dist/job-supervisor.js +121 -0
  91. package/dist/job-supervisor.js.map +1 -0
  92. package/dist/loader-DQNPDKOX.js +27 -0
  93. package/dist/{mcp-approvals-WNPH65BK.js → mcp-approvals-27LJYADQ.js} +6 -5
  94. package/dist/{mcp-config-YIJIES2C.js → mcp-config-GXSODNCM.js} +4 -3
  95. package/dist/memory-store-I6Z54VUN.js +51 -0
  96. package/dist/{model-window-store-YFVNXCIP.js → model-window-store-6VKXXTPJ.js} +9 -4
  97. package/dist/openai-compatible-IPUNBJ53.js +16 -0
  98. package/dist/permission-approvals-Q5IMFEZI.js +15 -0
  99. package/dist/permissions-7Y35X6HZ.js +62 -0
  100. package/dist/provider-approvals-5AGH3RVT.js +76 -0
  101. package/dist/sandbox-N2RY72GN.js +42 -0
  102. package/dist/{scrollback-LUKLIBVI.js → scrollback-GD76GNM4.js} +44 -26
  103. package/dist/scrollback-GD76GNM4.js.map +1 -0
  104. package/dist/sdk.d.ts +2739 -2069
  105. package/dist/sdk.js +77 -42
  106. package/dist/sdk.js.map +1 -1
  107. package/dist/settings-loader-KDBH45AQ.js +43 -0
  108. package/dist/settings-loader-KDBH45AQ.js.map +1 -0
  109. package/dist/{settings-removed-JWWPT4WA.js → settings-removed-3ATWJEF3.js} +2 -2
  110. package/dist/settings-removed-3ATWJEF3.js.map +1 -0
  111. package/dist/shell-selection-Z5WORZU4.js +31 -0
  112. package/dist/shell-selection-Z5WORZU4.js.map +1 -0
  113. package/package.json +25 -24
  114. package/dist/app-MOXXWEOY.js.map +0 -1
  115. package/dist/chunk-24AXE6SP.js.map +0 -1
  116. package/dist/chunk-3U5IBM24.js.map +0 -1
  117. package/dist/chunk-574C6MQR.js.map +0 -1
  118. package/dist/chunk-5GDZ22YP.js +0 -449
  119. package/dist/chunk-5GDZ22YP.js.map +0 -1
  120. package/dist/chunk-5KLVH3PY.js.map +0 -1
  121. package/dist/chunk-5RDRAO4B.js.map +0 -1
  122. package/dist/chunk-7N4J557C.js.map +0 -1
  123. package/dist/chunk-A7JLBK2Y.js +0 -3506
  124. package/dist/chunk-A7JLBK2Y.js.map +0 -1
  125. package/dist/chunk-ANSJPYGI.js.map +0 -1
  126. package/dist/chunk-BTZZUCLI.js +0 -264
  127. package/dist/chunk-BTZZUCLI.js.map +0 -1
  128. package/dist/chunk-C7D4NJ2D.js +0 -217
  129. package/dist/chunk-C7D4NJ2D.js.map +0 -1
  130. package/dist/chunk-CAGOB2N4.js.map +0 -1
  131. package/dist/chunk-DGMQEI6Y.js.map +0 -1
  132. package/dist/chunk-E75IPST5.js.map +0 -1
  133. package/dist/chunk-EYGD6X2T.js +0 -180
  134. package/dist/chunk-EYGD6X2T.js.map +0 -1
  135. package/dist/chunk-GTVB4HEM.js +0 -765
  136. package/dist/chunk-GTVB4HEM.js.map +0 -1
  137. package/dist/chunk-J7MIZ5CA.js.map +0 -1
  138. package/dist/chunk-KWHQ2XXL.js.map +0 -1
  139. package/dist/chunk-L6LK2QIQ.js +0 -657
  140. package/dist/chunk-L6LK2QIQ.js.map +0 -1
  141. package/dist/chunk-MAQXCUR4.js +0 -1007
  142. package/dist/chunk-MAQXCUR4.js.map +0 -1
  143. package/dist/chunk-S4XL7HOM.js +0 -402
  144. package/dist/chunk-S4XL7HOM.js.map +0 -1
  145. package/dist/chunk-URLQAXTR.js.map +0 -1
  146. package/dist/chunk-V4BOYR52.js.map +0 -1
  147. package/dist/chunk-VZQXX3WW.js.map +0 -1
  148. package/dist/chunk-WKBO5R4O.js.map +0 -1
  149. package/dist/chunk-XIFFTATU.js.map +0 -1
  150. package/dist/context-ZC5IFEAX.js +0 -25
  151. package/dist/interactive-assets-CCAAWVKH.js.map +0 -1
  152. package/dist/loader-4IDS2RIE.js +0 -15
  153. package/dist/openai-compatible-RHZUBHV6.js +0 -10
  154. package/dist/permission-approvals-76PVREFD.js +0 -14
  155. package/dist/permissions-CHCKTFQQ.js +0 -28
  156. package/dist/sandbox-CNVAQDA7.js +0 -23
  157. package/dist/scrollback-LUKLIBVI.js.map +0 -1
  158. package/dist/settings-loader-M2QP5TX2.js +0 -21
  159. package/dist/shell-selection-V6QAYXPX.js +0 -17
  160. package/patches/ink+6.8.0.patch +0 -13
  161. package/scripts/apply-ink-patch.mjs +0 -29
  162. package/scripts/ink-patch.mjs +0 -59
  163. package/scripts/verify-ink-patch.mjs +0 -11
  164. /package/dist/{command-approvals-6Y57VCWZ.js.map → additional-roots-VAWRQ625.js.map} +0 -0
  165. /package/dist/{context-ZC5IFEAX.js.map → command-approvals-ZRY66LCW.js.map} +0 -0
  166. /package/dist/{hook-approvals-CTUFKEJT.js.map → context-C3RAP6K5.js.map} +0 -0
  167. /package/dist/{loader-4IDS2RIE.js.map → hook-approvals-DPQD5H65.js.map} +0 -0
  168. /package/dist/{mcp-approvals-WNPH65BK.js.map → loader-DQNPDKOX.js.map} +0 -0
  169. /package/dist/{mcp-config-YIJIES2C.js.map → mcp-approvals-27LJYADQ.js.map} +0 -0
  170. /package/dist/{model-window-store-YFVNXCIP.js.map → mcp-config-GXSODNCM.js.map} +0 -0
  171. /package/dist/{openai-compatible-RHZUBHV6.js.map → memory-store-I6Z54VUN.js.map} +0 -0
  172. /package/dist/{permission-approvals-76PVREFD.js.map → model-window-store-6VKXXTPJ.js.map} +0 -0
  173. /package/dist/{permissions-CHCKTFQQ.js.map → openai-compatible-IPUNBJ53.js.map} +0 -0
  174. /package/dist/{sandbox-CNVAQDA7.js.map → permission-approvals-Q5IMFEZI.js.map} +0 -0
  175. /package/dist/{settings-loader-M2QP5TX2.js.map → permissions-7Y35X6HZ.js.map} +0 -0
  176. /package/dist/{settings-removed-JWWPT4WA.js.map → provider-approvals-5AGH3RVT.js.map} +0 -0
  177. /package/dist/{shell-selection-V6QAYXPX.js.map → sandbox-N2RY72GN.js.map} +0 -0
@@ -0,0 +1,663 @@
1
+ import {
2
+ reachesUncPath
3
+ } from "./chunk-EEX6QNXV.js";
4
+ import {
5
+ resolveBookHome
6
+ } from "./chunk-ZPSZZUWF.js";
7
+
8
+ // src/tools/aliases.ts
9
+ var TOOL_ALIASES = {
10
+ read_file: "Read",
11
+ write_file: "Write",
12
+ edit_file: "Edit",
13
+ multi_edit: "MultiEdit",
14
+ apply_patch: "ApplyPatch",
15
+ glob: "Glob",
16
+ glob_files: "Glob",
17
+ grep: "Grep",
18
+ bash: "Bash",
19
+ // Legacy snake_case git tool names — backward compatible.
20
+ git_status: "GitStatus",
21
+ git_diff: "GitDiff",
22
+ git_log: "GitLog",
23
+ git_commit: "GitCommit",
24
+ git_branch: "GitBranch"
25
+ };
26
+ function canonicalToolName(name) {
27
+ return TOOL_ALIASES[name] ?? name;
28
+ }
29
+ var PROVIDER_TOOL_PREFIXES = [
30
+ "parent:",
31
+ "default:",
32
+ "tool:",
33
+ "parent.",
34
+ "default.",
35
+ "tool.",
36
+ "functions."
37
+ ];
38
+ function unwrapProviderPrefix(name) {
39
+ const prefix = PROVIDER_TOOL_PREFIXES.find((candidate) => name.startsWith(candidate));
40
+ return prefix ? name.slice(prefix.length) : name;
41
+ }
42
+ function displayToolName(name) {
43
+ return canonicalToolName(unwrapProviderPrefix(name));
44
+ }
45
+
46
+ // src/tools/primary-arg.ts
47
+ function getPrimaryArg(args) {
48
+ if (typeof args.command === "string") return args.command.split("\n")[0];
49
+ if (typeof args.shell_id === "string") return args.shell_id;
50
+ if (typeof args.shellId === "string") return args.shellId;
51
+ if (typeof args.taskId === "string") return args.taskId;
52
+ if (typeof args.task_id === "string") return args.task_id;
53
+ if (typeof args.filePath === "string") return args.filePath;
54
+ if (typeof args.file_path === "string") return args.file_path;
55
+ if (typeof args.patch === "string") {
56
+ const match = args.patch.match(/^\*\*\* (?:Update|Add|Delete) File: (.+)$/m);
57
+ if (match) return match[1].trim();
58
+ }
59
+ if (typeof args.notebook_path === "string") return args.notebook_path;
60
+ if (typeof args.pattern === "string") return args.pattern;
61
+ if (typeof args.path === "string") return args.path;
62
+ if (typeof args.agent === "string") return args.agent;
63
+ if (typeof args.skill === "string") return args.skill;
64
+ if (typeof args.message === "string") return args.message;
65
+ if (typeof args.url === "string") return args.url;
66
+ if (typeof args.query === "string") return args.query;
67
+ if (typeof args.subject === "string") return args.subject;
68
+ if (typeof args.old_string === "string") return args.old_string.slice(0, 60);
69
+ const keys = Object.keys(args);
70
+ if (keys.length > 0) {
71
+ const v = args[keys[0]];
72
+ return typeof v === "string" ? v : "";
73
+ }
74
+ return "";
75
+ }
76
+
77
+ // src/tools/result.ts
78
+ import { mkdir, writeFile } from "fs/promises";
79
+ import { dirname, join } from "path";
80
+
81
+ // src/tools/tool-capabilities.ts
82
+ var FILE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
83
+ "ApplyPatch",
84
+ "Write",
85
+ "Edit",
86
+ "MultiEdit",
87
+ "NotebookEdit"
88
+ ]);
89
+ function isFileMutatingTool(canonicalName) {
90
+ return FILE_MUTATING_TOOLS.has(canonicalName);
91
+ }
92
+
93
+ // src/tools/result.ts
94
+ var TOOL_RESULT_MAX_BYTES = 50 * 1024;
95
+ var READ_EMPTY_FILE_NOTICE = "[Empty file: 0 lines.]";
96
+ function toolOutputDirectory() {
97
+ return join(resolveBookHome(), "tool-output");
98
+ }
99
+ function presentationFor(content, override) {
100
+ return {
101
+ kind: override?.kind ?? "text",
102
+ summary: override?.summary ?? content.split("\n")[0]?.trim() ?? "",
103
+ details: override?.details ?? content,
104
+ metadata: override?.metadata,
105
+ target: override?.target
106
+ };
107
+ }
108
+ function toolSuccess(content, options = {}) {
109
+ return {
110
+ version: 2,
111
+ toolCallId: options.toolCallId ?? "",
112
+ status: "success",
113
+ content,
114
+ data: options.data,
115
+ presentation: options.presentation ? presentationFor(content, options.presentation) : void 0,
116
+ artifacts: options.artifacts,
117
+ pagination: options.pagination
118
+ };
119
+ }
120
+ function toolFailure(message, options = {}) {
121
+ const error = {
122
+ code: options.code ?? "tool_error",
123
+ message,
124
+ retryable: options.retryable ?? false,
125
+ remediation: options.remediation,
126
+ details: options.details
127
+ };
128
+ const content = options.content ?? "";
129
+ return {
130
+ version: 2,
131
+ toolCallId: options.toolCallId ?? "",
132
+ status: options.status ?? "error",
133
+ content,
134
+ data: options.data,
135
+ structuredError: error,
136
+ presentation: options.presentation ? presentationFor(message, {
137
+ summary: message.split("\n")[0]?.trim(),
138
+ details: message,
139
+ ...options.presentation
140
+ }) : void 0,
141
+ artifacts: options.artifacts,
142
+ pagination: options.pagination
143
+ };
144
+ }
145
+ function toolResultSucceeded(result) {
146
+ return result.status === "success";
147
+ }
148
+ function toolResultErrorMessage(result) {
149
+ return result.structuredError?.message;
150
+ }
151
+ function rawToolResultModelContent(result) {
152
+ const content = result.content;
153
+ if (toolResultSucceeded(result)) {
154
+ return content.trim().length === 0 ? "(no output)" : content;
155
+ }
156
+ const remediation = result.structuredError?.remediation;
157
+ const fix = remediation ? `
158
+ Fix: ${remediation}` : "";
159
+ const detail = content ? `
160
+ ${content}` : "";
161
+ return `ERROR [${result.structuredError?.code ?? result.status}]: ${toolResultErrorMessage(result) ?? "tool failed"}${fix}${detail}`;
162
+ }
163
+ function toolResultModelContent(result) {
164
+ if (result.maskedPlaceholder !== void 0) return result.maskedPlaceholder;
165
+ const raw = rawToolResultModelContent(result);
166
+ if (Buffer.byteLength(raw) <= TOOL_RESULT_MAX_BYTES) return raw;
167
+ if (!toolResultSucceeded(result)) {
168
+ const prefix = `ERROR [${result.structuredError?.code ?? result.status}]: `;
169
+ const fix = result.structuredError?.remediation ? `
170
+ Fix: ${result.structuredError.remediation}` : "";
171
+ const budget = Math.max(
172
+ 1,
173
+ TOOL_RESULT_MAX_BYTES - Buffer.byteLength(prefix) - Buffer.byteLength(fix)
174
+ );
175
+ return `${prefix}${headTailPreview(failureText(result), result.artifacts?.outputPath, budget)}${fix}`;
176
+ }
177
+ return clippedOutputPreview(raw, result.artifacts?.outputPath, TOOL_RESULT_MAX_BYTES);
178
+ }
179
+ function utf8Prefix(text, maxBytes) {
180
+ if (maxBytes <= 0) return "";
181
+ const bytes = Buffer.from(text);
182
+ if (bytes.byteLength <= maxBytes) return text;
183
+ return bytes.subarray(0, maxBytes).toString("utf8").replace(/\uFFFD$/u, "");
184
+ }
185
+ function utf8Suffix(text, maxBytes) {
186
+ if (maxBytes <= 0) return "";
187
+ const bytes = Buffer.from(text);
188
+ if (bytes.byteLength <= maxBytes) return text;
189
+ let start = bytes.byteLength - maxBytes;
190
+ while (start < bytes.byteLength && (bytes[start] & 192) === 128) start++;
191
+ return bytes.subarray(start).toString("utf8");
192
+ }
193
+ var FAILURE_HEAD_MAX_BYTES = 8 * 1024;
194
+ function failureText(result) {
195
+ const message = toolResultErrorMessage(result) ?? "tool failed";
196
+ return result.content ? `${message}
197
+ ${result.content}` : message;
198
+ }
199
+ function headTailPreview(text, outputPath, maxBytes) {
200
+ const totalBytes = Buffer.byteLength(text);
201
+ const headWindow = Math.min(FAILURE_HEAD_MAX_BYTES, Math.floor(maxBytes * 0.2));
202
+ const window = utf8Prefix(text, headWindow);
203
+ if (Buffer.byteLength(window) >= totalBytes) return text;
204
+ const lastBreak = window.lastIndexOf("\n");
205
+ const head = lastBreak >= 0 ? window.slice(0, lastBreak + 1) : window;
206
+ const headShown = head.trimEnd();
207
+ const headBytes = Buffer.byteLength(headShown);
208
+ const omittedNotice = (omitted) => outputPath ? `[... ${omitted} bytes omitted. Full output: ${outputPath}]` : `[... ${omitted} bytes omitted. Full output unavailable.]`;
209
+ const reserve = Buffer.byteLength(omittedNotice(totalBytes)) + 2;
210
+ const tailRoom = maxBytes - headBytes - reserve;
211
+ if (tailRoom <= 0) return [headShown, omittedNotice(totalBytes - headBytes)].join("\n");
212
+ const tailWindow = utf8Suffix(text, tailRoom);
213
+ const breakAt = tailWindow.indexOf("\n");
214
+ const tail = breakAt >= 0 ? tailWindow.slice(breakAt + 1) : tailWindow;
215
+ const notice = omittedNotice(totalBytes - headBytes - Buffer.byteLength(tail));
216
+ return [headShown, notice, tail].join("\n");
217
+ }
218
+ function clippedOutputPreview(content, outputPath, maxBytes) {
219
+ const notice = outputPath ? `
220
+
221
+ [Output truncated at ${maxBytes} bytes. Full output: ${outputPath}]` : `
222
+
223
+ [Output truncated at ${maxBytes} bytes. Full output unavailable.]`;
224
+ const previewBudget = Math.max(0, maxBytes - Buffer.byteLength(notice));
225
+ return `${utf8Prefix(content, previewBudget).trimEnd()}${notice}`;
226
+ }
227
+ async function boundToolResultOutput(input, _workspaceRoot, maxBytes = TOOL_RESULT_MAX_BYTES, artifactRoot) {
228
+ const result = normalizeToolResult(input);
229
+ const modelContent = rawToolResultModelContent(result);
230
+ const modelContentBytes = Buffer.byteLength(modelContent);
231
+ const contentBytes = Buffer.byteLength(result.content);
232
+ const details = result.presentation?.details;
233
+ const detailsBytes = details === void 0 ? 0 : Buffer.byteLength(details);
234
+ if (modelContentBytes <= maxBytes && detailsBytes <= maxBytes) return result;
235
+ let outputPath = result.artifacts?.outputPath;
236
+ if (!outputPath) {
237
+ const outputFile = join(artifactRoot ?? toolOutputDirectory(), `${crypto.randomUUID()}.txt`);
238
+ try {
239
+ await mkdir(dirname(outputFile), { recursive: true });
240
+ await writeFile(
241
+ outputFile,
242
+ detailsBytes > modelContentBytes && details ? details : modelContent,
243
+ "utf8"
244
+ );
245
+ outputPath = outputFile.replace(/\\/g, "/");
246
+ } catch {
247
+ }
248
+ }
249
+ const modelOverflow = modelContentBytes > maxBytes;
250
+ const content = toolResultSucceeded(result) ? contentBytes > maxBytes ? clippedOutputPreview(result.content, outputPath, maxBytes) : result.content : modelOverflow ? "" : result.content;
251
+ const errorPrefix = `ERROR [${result.structuredError?.code ?? result.status}]: `;
252
+ const fixReserve = result.structuredError?.remediation ? Buffer.byteLength(`
253
+ Fix: ${result.structuredError.remediation}`) : 0;
254
+ const errorBudget = Math.max(1, maxBytes - Buffer.byteLength(errorPrefix) - fixReserve);
255
+ const structuredError = result.structuredError ? {
256
+ ...result.structuredError,
257
+ // The message and the output are clipped as one text, so a failure
258
+ // keeps both ends: the error line and any framing at the top, and the
259
+ // verdict at the bottom. The room the `Fix:` line needs is reserved
260
+ // above, so the remediation is never what gets clipped.
261
+ message: modelOverflow ? headTailPreview(failureText(result), outputPath, errorBudget) : result.structuredError.message
262
+ } : result.structuredError;
263
+ const clippedDetails = details ? detailsBytes > maxBytes ? (
264
+ // The transcript row reads the same way the model does: a failure's
265
+ // head and tail, not one end of it — the same reason as above.
266
+ toolResultSucceeded(result) ? clippedOutputPreview(details, outputPath, maxBytes) : headTailPreview(details, outputPath, maxBytes)
267
+ ) : details : toolResultSucceeded(result) ? content : structuredError?.message;
268
+ return {
269
+ ...result,
270
+ content,
271
+ structuredError,
272
+ presentation: result.presentation ? { ...result.presentation, details: clippedDetails } : result.presentation,
273
+ artifacts: outputPath ? { ...result.artifacts, outputPath } : result.artifacts,
274
+ pagination: {
275
+ ...result.pagination,
276
+ truncated: true,
277
+ omittedBytes: Math.max(
278
+ result.pagination?.omittedBytes ?? 0,
279
+ Math.max(modelContentBytes, contentBytes, detailsBytes) - maxBytes
280
+ )
281
+ }
282
+ };
283
+ }
284
+ function replaceToolResult(result, patch) {
285
+ const status = patch.status ?? result.status;
286
+ const content = patch.content ?? result.content;
287
+ return {
288
+ ...result,
289
+ status,
290
+ content,
291
+ // New content replaces what the model read: a placeholder standing in for
292
+ // the previous text would hide the rewrite (a PostToolUse hook's
293
+ // `modifiedOutput`, the plan-approval note) behind the line the tool wrote.
294
+ ...patch.content !== void 0 ? { maskedPlaceholder: void 0 } : {},
295
+ structuredError: patch.error ?? (status === "success" ? void 0 : result.structuredError),
296
+ presentation: result.presentation || patch.presentation ? presentationFor(content, {
297
+ ...result.presentation,
298
+ ...patch.presentation
299
+ }) : void 0
300
+ };
301
+ }
302
+ function normalizeToolResult(result) {
303
+ const legacy = result;
304
+ const legacySuccess = legacy.success;
305
+ const legacyError = typeof legacy.error === "string" ? legacy.error : void 0;
306
+ const status = result.status ? result.status : legacySuccess ? "success" : legacyError?.startsWith("SKIPPED") ? "blocked" : legacyError?.startsWith("CANCELLED") ? "cancelled" : legacyError?.startsWith("Tool timeout") ? "timed_out" : "error";
307
+ const content = result.content ?? legacy.output ?? "";
308
+ const error = status === "success" ? void 0 : {
309
+ ...result.structuredError ?? {},
310
+ code: result.structuredError?.code ?? (status === "blocked" ? "blocked" : status),
311
+ message: legacyError ?? result.structuredError?.message ?? "Tool failed",
312
+ retryable: result.structuredError?.retryable ?? status === "timed_out"
313
+ };
314
+ const artifacts = result.artifacts ?? (legacy.fileMutation || legacy.fileMutations || legacy.fileObservations || legacy.eventRef || legacy.outputPath ? {
315
+ fileMutation: legacy.fileMutation,
316
+ fileMutations: legacy.fileMutations,
317
+ fileObservations: legacy.fileObservations,
318
+ eventRef: legacy.eventRef,
319
+ outputPath: legacy.outputPath
320
+ } : void 0);
321
+ const metrics = result.metrics ?? (legacy.durationMs !== void 0 || legacy.retryAttempt !== void 0 ? { durationMs: legacy.durationMs, retryAttempt: legacy.retryAttempt } : void 0);
322
+ return {
323
+ version: 2,
324
+ toolCallId: result.toolCallId,
325
+ status,
326
+ content,
327
+ // Masking's placeholder survives a resume, or a restored compaction's tail
328
+ // would come back at full size.
329
+ ..."maskedPlaceholder" in result && result.maskedPlaceholder !== void 0 ? { maskedPlaceholder: result.maskedPlaceholder } : {},
330
+ data: result.data,
331
+ structuredError: error,
332
+ presentation: result.presentation,
333
+ artifacts,
334
+ metrics,
335
+ pagination: result.pagination
336
+ };
337
+ }
338
+ function nonEmptyLines(content) {
339
+ return content.split("\n").filter((line) => line.trim()).length;
340
+ }
341
+ function readLineMetadata(start, count) {
342
+ const first = Math.max(1, Math.floor(start));
343
+ const lines = Math.max(0, Math.floor(count));
344
+ if (lines <= 0) return ["empty"];
345
+ const size = lines === 1 ? "1 line" : `${lines} lines`;
346
+ return first > 1 ? [size, `${first}-${first + lines - 1}`] : [size];
347
+ }
348
+ function readResultMetadata(args, content) {
349
+ if (args.outline === true) {
350
+ const entries = content.split("\n").filter((line) => /^\d+: /.test(line)).length;
351
+ return ["outline", entries === 1 ? "1 entry" : `${entries} entries`];
352
+ }
353
+ let body = content === READ_EMPTY_FILE_NOTICE ? "" : content.replace(/\n\[Lines? \d[^\n]*\]$/, "");
354
+ if (body === content) body = body.replace(/(?:^|\n)\d+: $/, "");
355
+ const lineCount = body ? body.split("\n").length : 0;
356
+ const offset = Number(args.offset ?? 0);
357
+ const start = Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 1;
358
+ return readLineMetadata(start, lineCount);
359
+ }
360
+ function grepResultMetadata(args, content) {
361
+ const noun = args.output_mode === "files_with_matches" ? "file" : "match";
362
+ if (/^No matches found$/i.test(content.trim()))
363
+ return `0 ${noun === "file" ? "files" : "matches"}`;
364
+ const lines = content.split("\n");
365
+ let count;
366
+ if (noun === "file") count = lines.filter((line) => line.trim()).length;
367
+ else if (args.output_mode === "count") {
368
+ count = lines.reduce((total, line) => total + (Number(/:(\d+)\s*$/.exec(line)?.[1]) || 0), 0);
369
+ } else count = lines.filter((line) => /:\d+:/.test(line)).length || nonEmptyLines(content);
370
+ return `${count} ${count === 1 ? noun : noun === "file" ? "files" : "matches"}`;
371
+ }
372
+ function enrichToolResultPresentation(input, toolName, args) {
373
+ const result = normalizeToolResult(input);
374
+ const name = canonicalToolName(toolName);
375
+ const content = result.content;
376
+ const explicitPresentation = result.presentation;
377
+ const target = explicitPresentation?.target ?? (getPrimaryArg(args) || void 0);
378
+ const metadata = explicitPresentation?.metadata ? [...explicitPresentation.metadata] : [];
379
+ const inferKind = explicitPresentation?.kind === void 0;
380
+ const inferSummary = explicitPresentation?.summary === void 0;
381
+ const inferMetadata = explicitPresentation?.metadata === void 0;
382
+ let kind = explicitPresentation?.kind ?? "text";
383
+ let summary = explicitPresentation?.summary ?? content.split("\n")[0]?.trim() ?? "";
384
+ if (isFileMutatingTool(name)) {
385
+ if (inferKind) {
386
+ kind = result.artifacts?.fileMutation ? "file" : /^@@/m.test(content) ? "diff" : "file";
387
+ }
388
+ const mutation = result.artifacts?.fileMutation ?? result.artifacts?.fileMutations?.[0];
389
+ if (mutation) {
390
+ if (inferMetadata) {
391
+ if (mutation.addedLines) metadata.push(`+${mutation.addedLines}`);
392
+ if (mutation.removedLines) metadata.push(`-${mutation.removedLines}`);
393
+ if (!mutation.addedLines && !mutation.removedLines) metadata.push("no changes");
394
+ }
395
+ if (inferSummary) {
396
+ summary = `${mutation.kind === "create" ? "Created" : mutation.kind === "delete" ? "Deleted" : "Updated"} ${mutation.filePath}`;
397
+ }
398
+ }
399
+ } else if (name === "Read") {
400
+ if (inferKind) kind = "file";
401
+ if (inferMetadata && result.status === "success")
402
+ metadata.push(...readResultMetadata(args, content));
403
+ if (inferSummary) summary = target ? `Read ${target}` : summary;
404
+ } else if (name === "Glob") {
405
+ if (inferKind) kind = "search";
406
+ const count = /^(?:No files found|No matches found)$/i.test(content.trim()) ? 0 : nonEmptyLines(content.replace(/\n\.\.\. \(truncated[\s\S]*$/, ""));
407
+ if (inferMetadata) metadata.push(`${count} ${count === 1 ? "file" : "files"}`);
408
+ if (inferSummary) summary = `Found ${count} ${count === 1 ? "file" : "files"}`;
409
+ } else if (name === "Grep") {
410
+ if (inferKind) kind = "search";
411
+ if (result.status === "success") {
412
+ const label = grepResultMetadata(args, content);
413
+ if (inferMetadata) metadata.push(label);
414
+ if (inferSummary) summary = `Found ${label}`;
415
+ }
416
+ } else if (name === "Bash" || name === "BashOutput" || name === "KillShell") {
417
+ if (inferKind) kind = "command";
418
+ if (inferSummary) summary = target ? `${name}: ${target}` : summary;
419
+ } else if (name === "GitDiff") {
420
+ if (inferKind) kind = "diff";
421
+ } else if (name.startsWith("Web") || name === "ToolSearch" || name.startsWith("SessionHistory")) {
422
+ if (inferKind) kind = name === "ToolSearch" ? "search" : "markdown";
423
+ } else if (name.startsWith("Task") || name === "TodoWrite") {
424
+ if (inferKind) kind = "task";
425
+ } else if (name.startsWith("Agent") || name.startsWith("Evidence")) {
426
+ if (inferKind) kind = "agent";
427
+ }
428
+ return {
429
+ ...result,
430
+ presentation: {
431
+ kind,
432
+ summary,
433
+ details: explicitPresentation?.details ?? content,
434
+ metadata,
435
+ target: explicitPresentation?.target ?? result.artifacts?.fileMutation?.filePath ?? target
436
+ }
437
+ };
438
+ }
439
+
440
+ // src/tools/path-utils.ts
441
+ import { existsSync, lstatSync, realpathSync } from "fs";
442
+ import { basename, dirname as dirname2, isAbsolute, join as join2, relative, resolve, sep } from "path";
443
+ var REAL_PATH_COMPARISON = { relative, isAbsolute, sep };
444
+ function isOutside(root, candidate, path = REAL_PATH_COMPARISON) {
445
+ const rel = path.relative(root, candidate);
446
+ return rel === ".." || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel);
447
+ }
448
+ function nearestExistingPath(inputPath) {
449
+ let candidate = inputPath;
450
+ while (!existsSync(candidate)) {
451
+ const parent = dirname2(candidate);
452
+ if (parent === candidate) return null;
453
+ candidate = parent;
454
+ }
455
+ return candidate;
456
+ }
457
+ function resolveWorkspacePath(workspaceRoot, inputPath) {
458
+ const lexicalRoot = resolve(workspaceRoot);
459
+ const filePath = resolve(isAbsolute(inputPath) ? inputPath : resolve(lexicalRoot, inputPath));
460
+ const canonicalRoot = canonicalizePath(lexicalRoot);
461
+ const canonicalPath = canonicalizePath(filePath);
462
+ if (isOutside(canonicalRoot, canonicalPath)) return null;
463
+ const asWrittenInside = !isOutside(lexicalRoot, filePath);
464
+ const rel = asWrittenInside ? relative(lexicalRoot, filePath) : relative(canonicalRoot, canonicalPath);
465
+ return { filePath, canonicalPath, relativePath: rel.replace(/\\/g, "/") };
466
+ }
467
+ function resolveReadablePathDetail(context, inputPath) {
468
+ const wsMatch = resolveWorkspacePath(context.workspaceRoot, inputPath);
469
+ if (wsMatch) {
470
+ return { path: { ...wsMatch, root: context.workspaceRoot, inWorkspace: true } };
471
+ }
472
+ if (!isAbsolute(inputPath)) return { reason: "unreachable" };
473
+ for (const root of [...context.readOnlyRoots ?? [], ...context.additionalRoots ?? []]) {
474
+ const match = resolveWorkspacePath(root, inputPath);
475
+ if (match) return { path: { ...match, root, inWorkspace: false } };
476
+ }
477
+ const memory = context.memorySearchRoot ? resolveMemorySearchPath(context.memorySearchRoot, inputPath) : null;
478
+ return memory ? { path: memory } : { reason: "unreachable" };
479
+ }
480
+ function resolveMemorySearchPath(memoryRoot, inputPath) {
481
+ const match = resolveWorkspacePath(memoryRoot, inputPath);
482
+ if (!match) return null;
483
+ const fromRoot = relative(canonicalizePath(resolve(memoryRoot)), match.canonicalPath);
484
+ const segments = [...match.relativePath.split("/"), ...fromRoot.split(sep)];
485
+ if (segments.some((segment) => segment.startsWith(".") && segment !== "." && segment !== "..")) {
486
+ return null;
487
+ }
488
+ return { ...match, root: memoryRoot, inWorkspace: false };
489
+ }
490
+ function resolveMentionReadablePath(roots, absolutePath) {
491
+ if (reachesUncPath(absolutePath)) return { reason: "unc" };
492
+ return resolveReadablePathDetail(
493
+ { workspaceRoot: roots.workspaceRoot, additionalRoots: roots.additionalRoots },
494
+ absolutePath
495
+ );
496
+ }
497
+ function resolveReadablePath(context, inputPath) {
498
+ const detail = resolveReadablePathDetail(context, inputPath);
499
+ return "path" in detail ? detail.path : null;
500
+ }
501
+ function resolveMutationPath(context, inputPath) {
502
+ const memory = context.memoryWrite ? resolveMemoryWritePath(context.workspaceRoot, context.memoryWrite.root, inputPath) : null;
503
+ if (memory) return memory;
504
+ const inWorkspace = resolveWorkspacePath(context.workspaceRoot, inputPath);
505
+ if (inWorkspace) return { ...inWorkspace, root: context.workspaceRoot, inWorkspace: true };
506
+ if (!isAbsolute(inputPath)) return null;
507
+ for (const root of context.additionalRoots ?? []) {
508
+ const match = resolveWorkspacePath(root, inputPath);
509
+ if (!match) continue;
510
+ const rel = relative(resolve(context.workspaceRoot), match.filePath);
511
+ return { ...match, relativePath: rel.replace(/\\/g, "/"), root, inWorkspace: false };
512
+ }
513
+ return null;
514
+ }
515
+ function hasLinkBelow(root, filePath) {
516
+ let current = root;
517
+ for (const segment of relative(root, filePath).split(sep)) {
518
+ current = join2(current, segment);
519
+ try {
520
+ if (lstatSync(current).isSymbolicLink()) return true;
521
+ } catch (error) {
522
+ const code = error.code;
523
+ if (code === "ENOENT" || code === "ENOTDIR") return false;
524
+ return true;
525
+ }
526
+ }
527
+ return false;
528
+ }
529
+ function resolveMemoryWritePath(workspaceRoot, memoryRoot, inputPath) {
530
+ if (!isAbsolute(inputPath)) return null;
531
+ const filePath = resolve(inputPath);
532
+ if (!basename(filePath).toLowerCase().endsWith(".md")) return null;
533
+ const lexicalRoot = resolve(memoryRoot);
534
+ const canonicalRoot = canonicalizePath(lexicalRoot);
535
+ const canonicalPath = canonicalizePath(filePath);
536
+ if (canonicalPath === canonicalRoot || isOutside(canonicalRoot, canonicalPath)) return null;
537
+ const writtenRoot = [lexicalRoot, canonicalRoot].find((root) => !isOutside(root, filePath));
538
+ if (!writtenRoot) return null;
539
+ const below = relative(writtenRoot, filePath).split(sep);
540
+ if (below.some((segment) => segment.startsWith("."))) return null;
541
+ if (relative(canonicalRoot, canonicalPath).split(sep).some((s) => s.startsWith(".")))
542
+ return null;
543
+ if (hasLinkBelow(writtenRoot, filePath)) return null;
544
+ const rel = relative(resolve(workspaceRoot), filePath);
545
+ return {
546
+ filePath,
547
+ canonicalPath,
548
+ relativePath: rel.replace(/\\/g, "/"),
549
+ root: memoryRoot,
550
+ inWorkspace: false,
551
+ memory: true
552
+ };
553
+ }
554
+ function mutationDisplayPath(resolved) {
555
+ if (!resolved.memory) return resolved.relativePath;
556
+ return memoryDisplayName(resolved.root, resolved.canonicalPath);
557
+ }
558
+ function memoryDisplayName(memoryRoot, canonicalPath) {
559
+ const below = relative(canonicalizePath(resolve(memoryRoot)), canonicalPath);
560
+ return `memory/${below.split(sep).join("/")}`;
561
+ }
562
+ function memoryReadDisplayPath(roots, resolved) {
563
+ const memoryRoot = roots.memorySearchRoot ?? roots.memoryWrite?.root;
564
+ if (!memoryRoot) return void 0;
565
+ const canonicalRoot = canonicalizePath(resolve(memoryRoot));
566
+ if (resolved.canonicalPath === canonicalRoot || isOutside(canonicalRoot, resolved.canonicalPath)) {
567
+ return void 0;
568
+ }
569
+ return memoryDisplayName(memoryRoot, resolved.canonicalPath);
570
+ }
571
+ function messagePathFor(resolved) {
572
+ return resolved.memory ? resolved.filePath : resolved.relativePath;
573
+ }
574
+ function pathOutsideWorkspaceResult(inputPath) {
575
+ return toolFailure(`Path outside workspace and additionalDirectories: ${inputPath}`, {
576
+ code: "path_outside_workspace",
577
+ remediation: "Add its directory to additionalDirectories, or start Book in a directory that contains it. No permission rule and no permission mode can change which roots a read may cross."
578
+ });
579
+ }
580
+ function realWorkspaceRoot(root) {
581
+ try {
582
+ return realpathSync.native(root);
583
+ } catch {
584
+ return resolve(root);
585
+ }
586
+ }
587
+ function canonicalizePath(inputPath) {
588
+ const absolute = resolve(inputPath);
589
+ const existing = nearestExistingPath(absolute);
590
+ if (!existing) return absolute;
591
+ try {
592
+ return resolve(realpathSync.native(existing), relative(existing, absolute));
593
+ } catch {
594
+ return absolute;
595
+ }
596
+ }
597
+ function isUnderRoot(canonicalPath, root) {
598
+ return !isOutside(canonicalizePath(root), canonicalPath);
599
+ }
600
+ function comparablePath(value, platform = process.platform) {
601
+ if (platform !== "win32") return value;
602
+ return value.replaceAll("\\", "/").toLowerCase();
603
+ }
604
+ function hasBookLocalSettingsShape(canonicalPath, options) {
605
+ const parts = canonicalPath.toLowerCase().split(/[\\/]/);
606
+ if (parts[parts.length - 1] === ".book") return options.directory === true;
607
+ return parts[parts.length - 2] === ".book" && parts[parts.length - 1] === "settings.local.json";
608
+ }
609
+ function isBookLocalSettingsPath(path, root, options = {}) {
610
+ if (!hasBookLocalSettingsShape(path, options)) return false;
611
+ const target = canonicalizePath(path).toLowerCase();
612
+ const bookDir = join2(canonicalizePath(root), ".book").toLowerCase();
613
+ return target === join2(bookDir, "settings.local.json") || options.directory === true && target === bookDir;
614
+ }
615
+ function isBookLocalSettingsUnderRoots(path, roots, options = {}) {
616
+ if (!hasBookLocalSettingsShape(path, options)) return false;
617
+ return [
618
+ roots.workspaceRoot,
619
+ ...roots.readOnlyRoots ?? [],
620
+ ...roots.additionalRoots ?? []
621
+ ].some((root) => isBookLocalSettingsPath(path, root, options));
622
+ }
623
+
624
+ export {
625
+ TOOL_ALIASES,
626
+ canonicalToolName,
627
+ unwrapProviderPrefix,
628
+ displayToolName,
629
+ getPrimaryArg,
630
+ isFileMutatingTool,
631
+ TOOL_RESULT_MAX_BYTES,
632
+ READ_EMPTY_FILE_NOTICE,
633
+ toolSuccess,
634
+ toolFailure,
635
+ toolResultSucceeded,
636
+ toolResultErrorMessage,
637
+ toolResultModelContent,
638
+ utf8Prefix,
639
+ boundToolResultOutput,
640
+ replaceToolResult,
641
+ normalizeToolResult,
642
+ readLineMetadata,
643
+ readResultMetadata,
644
+ grepResultMetadata,
645
+ enrichToolResultPresentation,
646
+ isOutside,
647
+ resolveWorkspacePath,
648
+ resolveReadablePathDetail,
649
+ resolveMentionReadablePath,
650
+ resolveReadablePath,
651
+ resolveMutationPath,
652
+ mutationDisplayPath,
653
+ memoryReadDisplayPath,
654
+ messagePathFor,
655
+ pathOutsideWorkspaceResult,
656
+ realWorkspaceRoot,
657
+ canonicalizePath,
658
+ isUnderRoot,
659
+ comparablePath,
660
+ isBookLocalSettingsPath,
661
+ isBookLocalSettingsUnderRoots
662
+ };
663
+ //# sourceMappingURL=chunk-YIL6HTJX.js.map