apex-code 0.5.0 → 0.5.1

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 (509) hide show
  1. package/CHANGELOG.md +257 -5
  2. package/README.md +624 -0
  3. package/dist/bun/cli.d.ts +3 -1
  4. package/dist/bun/cli.d.ts.map +1 -1
  5. package/dist/bun/cli.js +3 -9
  6. package/dist/bun/cli.js.map +1 -1
  7. package/dist/bun/runtime-setup.d.ts +2 -0
  8. package/dist/bun/runtime-setup.d.ts.map +1 -0
  9. package/dist/bun/runtime-setup.js +9 -0
  10. package/dist/bun/runtime-setup.js.map +1 -0
  11. package/dist/bun/sandbox-env-setup.d.ts +2 -0
  12. package/dist/bun/sandbox-env-setup.d.ts.map +1 -0
  13. package/dist/bun/sandbox-env-setup.js +4 -0
  14. package/dist/bun/sandbox-env-setup.js.map +1 -0
  15. package/dist/cli/args.d.ts.map +1 -1
  16. package/dist/cli/args.js +16 -5
  17. package/dist/cli/args.js.map +1 -1
  18. package/dist/cli/config-selector.d.ts.map +1 -1
  19. package/dist/cli/config-selector.js +2 -1
  20. package/dist/cli/config-selector.js.map +1 -1
  21. package/dist/cli/file-processor.d.ts +1 -1
  22. package/dist/cli/file-processor.d.ts.map +1 -1
  23. package/dist/cli/file-processor.js.map +1 -1
  24. package/dist/cli/session-picker.d.ts +1 -1
  25. package/dist/cli/session-picker.d.ts.map +1 -1
  26. package/dist/cli/session-picker.js.map +1 -1
  27. package/dist/cli/setup.d.ts +2 -0
  28. package/dist/cli/setup.d.ts.map +1 -0
  29. package/dist/cli/setup.js +13 -0
  30. package/dist/cli/setup.js.map +1 -0
  31. package/dist/config.d.ts +1 -0
  32. package/dist/config.d.ts.map +1 -1
  33. package/dist/config.js +1 -0
  34. package/dist/config.js.map +1 -1
  35. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  36. package/dist/core/agent-session-runtime.js +16 -8
  37. package/dist/core/agent-session-runtime.js.map +1 -1
  38. package/dist/core/agent-session.d.ts +85 -15
  39. package/dist/core/agent-session.d.ts.map +1 -1
  40. package/dist/core/agent-session.js +685 -266
  41. package/dist/core/agent-session.js.map +1 -1
  42. package/dist/core/auth-storage.d.ts +4 -0
  43. package/dist/core/auth-storage.d.ts.map +1 -1
  44. package/dist/core/auth-storage.js +55 -0
  45. package/dist/core/auth-storage.js.map +1 -1
  46. package/dist/core/bug-report.d.ts +176 -0
  47. package/dist/core/bug-report.d.ts.map +1 -0
  48. package/dist/core/bug-report.js +291 -0
  49. package/dist/core/bug-report.js.map +1 -0
  50. package/dist/core/cache-stats.d.ts.map +1 -1
  51. package/dist/core/cache-stats.js +12 -1
  52. package/dist/core/cache-stats.js.map +1 -1
  53. package/dist/core/cache-warmer.d.ts +103 -0
  54. package/dist/core/cache-warmer.d.ts.map +1 -0
  55. package/dist/core/cache-warmer.js +356 -0
  56. package/dist/core/cache-warmer.js.map +1 -0
  57. package/dist/core/compaction/branch-summarization.d.ts +1 -1
  58. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  59. package/dist/core/compaction/branch-summarization.js +4 -3
  60. package/dist/core/compaction/branch-summarization.js.map +1 -1
  61. package/dist/core/compaction/compaction.d.ts +5 -3
  62. package/dist/core/compaction/compaction.d.ts.map +1 -1
  63. package/dist/core/compaction/compaction.js +163 -66
  64. package/dist/core/compaction/compaction.js.map +1 -1
  65. package/dist/core/context/pipeline.d.ts +8 -8
  66. package/dist/core/context/pipeline.d.ts.map +1 -1
  67. package/dist/core/context/pipeline.js +21 -12
  68. package/dist/core/context/pipeline.js.map +1 -1
  69. package/dist/core/crash-log.d.ts +27 -0
  70. package/dist/core/crash-log.d.ts.map +1 -0
  71. package/dist/core/crash-log.js +139 -0
  72. package/dist/core/crash-log.js.map +1 -0
  73. package/dist/core/export-html/template.js +6 -1
  74. package/dist/core/extensions/index.d.ts +1 -1
  75. package/dist/core/extensions/index.d.ts.map +1 -1
  76. package/dist/core/extensions/index.js.map +1 -1
  77. package/dist/core/extensions/jiti-loader.d.ts +2 -0
  78. package/dist/core/extensions/jiti-loader.d.ts.map +1 -0
  79. package/dist/core/extensions/jiti-loader.js +4 -0
  80. package/dist/core/extensions/jiti-loader.js.map +1 -0
  81. package/dist/core/extensions/jiti-static-loader.d.ts +2 -0
  82. package/dist/core/extensions/jiti-static-loader.d.ts.map +1 -0
  83. package/dist/core/extensions/jiti-static-loader.js +4 -0
  84. package/dist/core/extensions/jiti-static-loader.js.map +1 -0
  85. package/dist/core/extensions/loader.d.ts.map +1 -1
  86. package/dist/core/extensions/loader.js +40 -59
  87. package/dist/core/extensions/loader.js.map +1 -1
  88. package/dist/core/extensions/runner.d.ts +24 -8
  89. package/dist/core/extensions/runner.d.ts.map +1 -1
  90. package/dist/core/extensions/runner.js +195 -72
  91. package/dist/core/extensions/runner.js.map +1 -1
  92. package/dist/core/extensions/types.d.ts +142 -55
  93. package/dist/core/extensions/types.d.ts.map +1 -1
  94. package/dist/core/extensions/types.js.map +1 -1
  95. package/dist/core/extensions/virtual-modules.d.ts +3 -0
  96. package/dist/core/extensions/virtual-modules.d.ts.map +1 -0
  97. package/dist/core/extensions/virtual-modules.js +41 -0
  98. package/dist/core/extensions/virtual-modules.js.map +1 -0
  99. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  100. package/dist/core/extensions/wrapper.js +1 -20
  101. package/dist/core/extensions/wrapper.js.map +1 -1
  102. package/dist/core/http-dispatcher.d.ts.map +1 -1
  103. package/dist/core/http-dispatcher.js +2 -0
  104. package/dist/core/http-dispatcher.js.map +1 -1
  105. package/dist/core/index.d.ts +2 -1
  106. package/dist/core/index.d.ts.map +1 -1
  107. package/dist/core/index.js.map +1 -1
  108. package/dist/core/keybindings.d.ts +6 -1
  109. package/dist/core/keybindings.d.ts.map +1 -1
  110. package/dist/core/keybindings.js +5 -1
  111. package/dist/core/keybindings.js.map +1 -1
  112. package/dist/core/messages.d.ts +1 -1
  113. package/dist/core/messages.d.ts.map +1 -1
  114. package/dist/core/messages.js +1 -0
  115. package/dist/core/messages.js.map +1 -1
  116. package/dist/core/model-config.d.ts +168 -20
  117. package/dist/core/model-config.d.ts.map +1 -1
  118. package/dist/core/model-config.js +44 -18
  119. package/dist/core/model-config.js.map +1 -1
  120. package/dist/core/model-registry.d.ts +5 -1
  121. package/dist/core/model-registry.d.ts.map +1 -1
  122. package/dist/core/model-registry.js +8 -0
  123. package/dist/core/model-registry.js.map +1 -1
  124. package/dist/core/model-resolver.d.ts.map +1 -1
  125. package/dist/core/model-resolver.js +4 -3
  126. package/dist/core/model-resolver.js.map +1 -1
  127. package/dist/core/model-runtime.d.ts +1 -0
  128. package/dist/core/model-runtime.d.ts.map +1 -1
  129. package/dist/core/model-runtime.js +12 -6
  130. package/dist/core/model-runtime.js.map +1 -1
  131. package/dist/core/permissions/store.d.ts.map +1 -1
  132. package/dist/core/permissions/store.js +11 -8
  133. package/dist/core/permissions/store.js.map +1 -1
  134. package/dist/core/prompt-templates.d.ts +6 -1
  135. package/dist/core/prompt-templates.d.ts.map +1 -1
  136. package/dist/core/prompt-templates.js +61 -35
  137. package/dist/core/prompt-templates.js.map +1 -1
  138. package/dist/core/provider-composer.d.ts +4 -2
  139. package/dist/core/provider-composer.d.ts.map +1 -1
  140. package/dist/core/provider-composer.js +29 -2
  141. package/dist/core/provider-composer.js.map +1 -1
  142. package/dist/core/radius.d.ts +3 -0
  143. package/dist/core/radius.d.ts.map +1 -1
  144. package/dist/core/radius.js +6 -0
  145. package/dist/core/radius.js.map +1 -1
  146. package/dist/core/resource-loader.d.ts.map +1 -1
  147. package/dist/core/resource-loader.js +6 -2
  148. package/dist/core/resource-loader.js.map +1 -1
  149. package/dist/core/sdk.d.ts.map +1 -1
  150. package/dist/core/sdk.js +63 -42
  151. package/dist/core/sdk.js.map +1 -1
  152. package/dist/core/session-export.d.ts +6 -2
  153. package/dist/core/session-export.d.ts.map +1 -1
  154. package/dist/core/session-export.js +14 -13
  155. package/dist/core/session-export.js.map +1 -1
  156. package/dist/core/session-manager.d.ts +66 -15
  157. package/dist/core/session-manager.d.ts.map +1 -1
  158. package/dist/core/session-manager.js +295 -115
  159. package/dist/core/session-manager.js.map +1 -1
  160. package/dist/core/settings-manager.d.ts +27 -4
  161. package/dist/core/settings-manager.d.ts.map +1 -1
  162. package/dist/core/settings-manager.js +55 -7
  163. package/dist/core/settings-manager.js.map +1 -1
  164. package/dist/core/skills.d.ts +1 -1
  165. package/dist/core/skills.d.ts.map +1 -1
  166. package/dist/core/skills.js +5 -4
  167. package/dist/core/skills.js.map +1 -1
  168. package/dist/core/slash-commands.d.ts.map +1 -1
  169. package/dist/core/slash-commands.js +1 -0
  170. package/dist/core/slash-commands.js.map +1 -1
  171. package/dist/core/system-prompt.d.ts +49 -6
  172. package/dist/core/system-prompt.d.ts.map +1 -1
  173. package/dist/core/system-prompt.js +140 -79
  174. package/dist/core/system-prompt.js.map +1 -1
  175. package/dist/core/tools/bash.d.ts +7 -8
  176. package/dist/core/tools/bash.d.ts.map +1 -1
  177. package/dist/core/tools/bash.js +20 -115
  178. package/dist/core/tools/bash.js.map +1 -1
  179. package/dist/core/tools/edit.d.ts +1 -12
  180. package/dist/core/tools/edit.d.ts.map +1 -1
  181. package/dist/core/tools/edit.js +6 -204
  182. package/dist/core/tools/edit.js.map +1 -1
  183. package/dist/core/tools/find.d.ts.map +1 -1
  184. package/dist/core/tools/find.js +4 -55
  185. package/dist/core/tools/find.js.map +1 -1
  186. package/dist/core/tools/grep.d.ts.map +1 -1
  187. package/dist/core/tools/grep.js +4 -60
  188. package/dist/core/tools/grep.js.map +1 -1
  189. package/dist/core/tools/ls.d.ts.map +1 -1
  190. package/dist/core/tools/ls.js +4 -49
  191. package/dist/core/tools/ls.js.map +1 -1
  192. package/dist/core/tools/read.d.ts +4 -1
  193. package/dist/core/tools/read.d.ts.map +1 -1
  194. package/dist/core/tools/read.js +10 -125
  195. package/dist/core/tools/read.js.map +1 -1
  196. package/dist/core/tools/renderers/bash.d.ts +18 -0
  197. package/dist/core/tools/renderers/bash.d.ts.map +1 -0
  198. package/dist/core/tools/renderers/bash.js +126 -0
  199. package/dist/core/tools/renderers/bash.js.map +1 -0
  200. package/dist/core/tools/renderers/edit.d.ts +23 -0
  201. package/dist/core/tools/renderers/edit.d.ts.map +1 -0
  202. package/dist/core/tools/renderers/edit.js +207 -0
  203. package/dist/core/tools/renderers/edit.js.map +1 -0
  204. package/dist/core/tools/renderers/find.d.ts +10 -0
  205. package/dist/core/tools/renderers/find.d.ts.map +1 -0
  206. package/dist/core/tools/renderers/find.js +64 -0
  207. package/dist/core/tools/renderers/find.js.map +1 -0
  208. package/dist/core/tools/renderers/grep.d.ts +10 -0
  209. package/dist/core/tools/renderers/grep.d.ts.map +1 -0
  210. package/dist/core/tools/renderers/grep.js +69 -0
  211. package/dist/core/tools/renderers/grep.js.map +1 -0
  212. package/dist/core/tools/renderers/index.d.ts +34 -0
  213. package/dist/core/tools/renderers/index.d.ts.map +1 -0
  214. package/dist/core/tools/renderers/index.js +47 -0
  215. package/dist/core/tools/renderers/index.js.map +1 -0
  216. package/dist/core/tools/renderers/ls.d.ts +10 -0
  217. package/dist/core/tools/renderers/ls.d.ts.map +1 -0
  218. package/dist/core/tools/renderers/ls.js +58 -0
  219. package/dist/core/tools/renderers/ls.js.map +1 -0
  220. package/dist/core/tools/renderers/read.d.ts +11 -0
  221. package/dist/core/tools/renderers/read.d.ts.map +1 -0
  222. package/dist/core/tools/renderers/read.js +132 -0
  223. package/dist/core/tools/renderers/read.js.map +1 -0
  224. package/dist/core/tools/renderers/write.d.ts +10 -0
  225. package/dist/core/tools/renderers/write.d.ts.map +1 -0
  226. package/dist/core/tools/renderers/write.js +152 -0
  227. package/dist/core/tools/renderers/write.js.map +1 -0
  228. package/dist/core/tools/write.d.ts.map +1 -1
  229. package/dist/core/tools/write.js +6 -146
  230. package/dist/core/tools/write.js.map +1 -1
  231. package/dist/core/usage-totals.d.ts +1 -1
  232. package/dist/core/usage-totals.d.ts.map +1 -1
  233. package/dist/core/usage-totals.js +5 -1
  234. package/dist/core/usage-totals.js.map +1 -1
  235. package/dist/extensions/llama/client.d.ts +2 -0
  236. package/dist/extensions/llama/client.d.ts.map +1 -1
  237. package/dist/extensions/llama/client.js +7 -3
  238. package/dist/extensions/llama/client.js.map +1 -1
  239. package/dist/extensions/llama/provider.d.ts.map +1 -1
  240. package/dist/extensions/llama/provider.js +20 -5
  241. package/dist/extensions/llama/provider.js.map +1 -1
  242. package/dist/index.d.ts +5 -4
  243. package/dist/index.d.ts.map +1 -1
  244. package/dist/index.js +1 -1
  245. package/dist/index.js.map +1 -1
  246. package/dist/main.d.ts.map +1 -1
  247. package/dist/main.js +21 -13
  248. package/dist/main.js.map +1 -1
  249. package/dist/modes/interactive/bug-report.d.ts +16 -0
  250. package/dist/modes/interactive/bug-report.d.ts.map +1 -0
  251. package/dist/modes/interactive/bug-report.js +167 -0
  252. package/dist/modes/interactive/bug-report.js.map +1 -0
  253. package/dist/modes/interactive/chat-viewport.d.ts +20 -0
  254. package/dist/modes/interactive/chat-viewport.d.ts.map +1 -0
  255. package/dist/modes/interactive/chat-viewport.js +28 -0
  256. package/dist/modes/interactive/chat-viewport.js.map +1 -0
  257. package/dist/modes/interactive/components/assistant-message.d.ts +7 -0
  258. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  259. package/dist/modes/interactive/components/assistant-message.js +63 -17
  260. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  261. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  262. package/dist/modes/interactive/components/branch-summary-message.js +12 -5
  263. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  264. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  265. package/dist/modes/interactive/components/compaction-summary-message.js +12 -5
  266. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  267. package/dist/modes/interactive/components/custom-editor.d.ts +12 -0
  268. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  269. package/dist/modes/interactive/components/custom-editor.js +38 -0
  270. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  271. package/dist/modes/interactive/components/error-summary.d.ts +11 -0
  272. package/dist/modes/interactive/components/error-summary.d.ts.map +1 -0
  273. package/dist/modes/interactive/components/error-summary.js +31 -0
  274. package/dist/modes/interactive/components/error-summary.js.map +1 -0
  275. package/dist/modes/interactive/components/extension-editor.d.ts +4 -1
  276. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  277. package/dist/modes/interactive/components/extension-editor.js +6 -1
  278. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  279. package/dist/modes/interactive/components/extension-input.d.ts +2 -0
  280. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  281. package/dist/modes/interactive/components/extension-input.js +6 -0
  282. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  283. package/dist/modes/interactive/components/extension-selector.d.ts +2 -0
  284. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  285. package/dist/modes/interactive/components/extension-selector.js +4 -0
  286. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  287. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  288. package/dist/modes/interactive/components/footer.js +4 -1
  289. package/dist/modes/interactive/components/footer.js.map +1 -1
  290. package/dist/modes/interactive/components/index.d.ts +1 -1
  291. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  292. package/dist/modes/interactive/components/index.js.map +1 -1
  293. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  294. package/dist/modes/interactive/components/model-selector.js +3 -3
  295. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  296. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  297. package/dist/modes/interactive/components/scoped-models-selector.js +13 -15
  298. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  299. package/dist/modes/interactive/components/session-selector.d.ts +5 -5
  300. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  301. package/dist/modes/interactive/components/session-selector.js +74 -48
  302. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  303. package/dist/modes/interactive/components/settings-selector.d.ts +5 -1
  304. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  305. package/dist/modes/interactive/components/settings-selector.js +35 -10
  306. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  307. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  308. package/dist/modes/interactive/components/skill-invocation-message.js +11 -4
  309. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  310. package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
  311. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  312. package/dist/modes/interactive/components/status-indicator.js +10 -3
  313. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  314. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  315. package/dist/modes/interactive/components/thinking-selector.js +6 -6
  316. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  317. package/dist/modes/interactive/components/tool-execution.d.ts +27 -5
  318. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  319. package/dist/modes/interactive/components/tool-execution.js +64 -42
  320. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  321. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  322. package/dist/modes/interactive/components/tree-selector.js +9 -0
  323. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  324. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  325. package/dist/modes/interactive/components/trust-selector.js +2 -2
  326. package/dist/modes/interactive/components/trust-selector.js.map +1 -1
  327. package/dist/modes/interactive/interactive-mode.d.ts +43 -29
  328. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  329. package/dist/modes/interactive/interactive-mode.js +431 -166
  330. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  331. package/dist/modes/interactive/theme/dark.json +2 -1
  332. package/dist/modes/interactive/theme/light.json +2 -1
  333. package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
  334. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  335. package/dist/modes/interactive/theme/theme-controller.js +5 -0
  336. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  337. package/dist/modes/interactive/theme/theme-json.d.ts +84 -0
  338. package/dist/modes/interactive/theme/theme-json.d.ts.map +1 -0
  339. package/dist/modes/interactive/theme/theme-json.js +130 -0
  340. package/dist/modes/interactive/theme/theme-json.js.map +1 -0
  341. package/dist/modes/interactive/theme/theme-schema.json +6 -2
  342. package/dist/modes/interactive/theme/theme.d.ts +14 -5
  343. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  344. package/dist/modes/interactive/theme/theme.js +18 -119
  345. package/dist/modes/interactive/theme/theme.js.map +1 -1
  346. package/dist/modes/interactive/tui-renderer.d.ts +21 -0
  347. package/dist/modes/interactive/tui-renderer.d.ts.map +1 -0
  348. package/dist/modes/interactive/tui-renderer.js +66 -0
  349. package/dist/modes/interactive/tui-renderer.js.map +1 -0
  350. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  351. package/dist/modes/rpc/rpc-mode.js +2 -2
  352. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  353. package/dist/testing/replay/recorded-provider.d.ts +2 -2
  354. package/dist/testing/replay/recorded-provider.d.ts.map +1 -1
  355. package/dist/testing/replay/recorded-provider.js +2 -5
  356. package/dist/testing/replay/recorded-provider.js.map +1 -1
  357. package/dist/testing/replay/runner.d.ts.map +1 -1
  358. package/dist/testing/replay/runner.js +0 -1
  359. package/dist/testing/replay/runner.js.map +1 -1
  360. package/dist/utils/clipboard-command.d.ts +7 -0
  361. package/dist/utils/clipboard-command.d.ts.map +1 -0
  362. package/dist/utils/clipboard-command.js +45 -0
  363. package/dist/utils/clipboard-command.js.map +1 -0
  364. package/dist/utils/clipboard-image.d.ts.map +1 -1
  365. package/dist/utils/clipboard-image.js +53 -81
  366. package/dist/utils/clipboard-image.js.map +1 -1
  367. package/dist/utils/clipboard.d.ts.map +1 -1
  368. package/dist/utils/clipboard.js +111 -121
  369. package/dist/utils/clipboard.js.map +1 -1
  370. package/dist/utils/exif-orientation.d.ts.map +1 -1
  371. package/dist/utils/exif-orientation.js +2 -3
  372. package/dist/utils/exif-orientation.js.map +1 -1
  373. package/dist/utils/mime.d.ts.map +1 -1
  374. package/dist/utils/mime.js +1 -1
  375. package/dist/utils/mime.js.map +1 -1
  376. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  377. package/dist/utils/syntax-highlight.js +21 -21
  378. package/dist/utils/syntax-highlight.js.map +1 -1
  379. package/dist/utils/tool-result-images.d.ts +3 -1
  380. package/dist/utils/tool-result-images.d.ts.map +1 -1
  381. package/dist/utils/tool-result-images.js +4 -1
  382. package/dist/utils/tool-result-images.js.map +1 -1
  383. package/dist/utils/tools-manager.d.ts.map +1 -1
  384. package/dist/utils/tools-manager.js +11 -2
  385. package/dist/utils/tools-manager.js.map +1 -1
  386. package/dist/utils/wsl.d.ts +3 -0
  387. package/dist/utils/wsl.d.ts.map +1 -0
  388. package/dist/utils/wsl.js +15 -0
  389. package/dist/utils/wsl.js.map +1 -0
  390. package/dist/utils/zip.d.ts +7 -0
  391. package/dist/utils/zip.d.ts.map +1 -0
  392. package/dist/utils/zip.js +60 -0
  393. package/dist/utils/zip.js.map +1 -0
  394. package/docs/cli-integration.md +107 -0
  395. package/docs/cli.md +269 -0
  396. package/docs/compaction.md +73 -24
  397. package/docs/configuration.md +45 -0
  398. package/docs/containerization.md +22 -21
  399. package/docs/custom-provider.md +21 -12
  400. package/docs/development.md +19 -0
  401. package/docs/docs.json +139 -95
  402. package/docs/extensions.md +243 -371
  403. package/docs/how-pi-works.md +49 -0
  404. package/docs/images/interactive-mode.png +0 -0
  405. package/docs/index.md +3 -3
  406. package/docs/json.md +7 -3
  407. package/docs/keybindings.md +57 -54
  408. package/docs/llama-cpp.md +2 -2
  409. package/docs/message-types.md +261 -0
  410. package/docs/models.md +103 -49
  411. package/docs/packages.md +49 -47
  412. package/docs/prompt-templates.md +29 -56
  413. package/docs/providers.md +91 -139
  414. package/docs/quickstart.md +42 -14
  415. package/docs/rpc-commands.md +854 -0
  416. package/docs/rpc-extension-ui.md +200 -0
  417. package/docs/rpc.md +121 -223
  418. package/docs/sdk.md +99 -124
  419. package/docs/security.md +7 -7
  420. package/docs/session-format.md +85 -82
  421. package/docs/sessions.md +41 -57
  422. package/docs/settings.md +94 -87
  423. package/docs/shell-aliases.md +68 -3
  424. package/docs/skills.md +62 -49
  425. package/docs/slash-commands.md +60 -0
  426. package/docs/terminal-setup.md +86 -55
  427. package/docs/termux.md +65 -75
  428. package/docs/themes.md +64 -86
  429. package/docs/tmux.md +29 -7
  430. package/docs/tui.md +67 -122
  431. package/docs/usage.md +31 -42
  432. package/docs/windows.md +41 -13
  433. package/examples/README.md +16 -2
  434. package/examples/extensions/README.md +0 -1
  435. package/examples/extensions/custom-provider-anthropic/index.ts +18 -12
  436. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  437. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  438. package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -2
  439. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  440. package/examples/extensions/dynamic-resources/dynamic.json +2 -0
  441. package/examples/extensions/gondolin/package-lock.json +2 -2
  442. package/examples/extensions/gondolin/package.json +1 -1
  443. package/examples/extensions/prompt-customizer.ts +19 -67
  444. package/examples/extensions/sandbox/package-lock.json +2 -2
  445. package/examples/extensions/sandbox/package.json +1 -1
  446. package/examples/extensions/with-deps/package-lock.json +2 -2
  447. package/examples/extensions/with-deps/package.json +1 -1
  448. package/examples/plugins/pi-example-plugin/README.md +38 -0
  449. package/examples/plugins/pi-example-plugin/package.json +10 -0
  450. package/examples/plugins/pi-example-plugin/src/contract.ts +13 -0
  451. package/examples/plugins/pi-example-plugin/src/session.ts +24 -0
  452. package/examples/plugins/pi-example-plugin/src/tui.ts +31 -0
  453. package/examples/rpc-client.ts +35 -0
  454. package/examples/rpc-extension-ui.ts +25 -5
  455. package/examples/sdk/README.md +1 -1
  456. package/npm-shrinkwrap.json +861 -513
  457. package/package.json +27 -18
  458. package/dist/bun/register-bedrock.d.ts +0 -2
  459. package/dist/bun/register-bedrock.d.ts.map +0 -1
  460. package/dist/bun/register-bedrock.js +0 -4
  461. package/dist/bun/register-bedrock.js.map +0 -1
  462. package/dist/cli/experimental/auth.d.ts +0 -16
  463. package/dist/cli/experimental/auth.d.ts.map +0 -1
  464. package/dist/cli/experimental/auth.js +0 -13
  465. package/dist/cli/experimental/auth.js.map +0 -1
  466. package/dist/cli/experimental/cli.d.ts +0 -6
  467. package/dist/cli/experimental/cli.d.ts.map +0 -1
  468. package/dist/cli/experimental/cli.js +0 -5
  469. package/dist/cli/experimental/cli.js.map +0 -1
  470. package/dist/cli/experimental/command-options.d.ts +0 -17
  471. package/dist/cli/experimental/command-options.d.ts.map +0 -1
  472. package/dist/cli/experimental/command-options.js +0 -35
  473. package/dist/cli/experimental/command-options.js.map +0 -1
  474. package/dist/cli/experimental/command.d.ts +0 -63
  475. package/dist/cli/experimental/command.d.ts.map +0 -1
  476. package/dist/cli/experimental/command.js +0 -130
  477. package/dist/cli/experimental/command.js.map +0 -1
  478. package/dist/cli/experimental/commands/client.d.ts +0 -13
  479. package/dist/cli/experimental/commands/client.d.ts.map +0 -1
  480. package/dist/cli/experimental/commands/client.js +0 -25
  481. package/dist/cli/experimental/commands/client.js.map +0 -1
  482. package/dist/cli/experimental/commands/pi.d.ts +0 -15
  483. package/dist/cli/experimental/commands/pi.d.ts.map +0 -1
  484. package/dist/cli/experimental/commands/pi.js +0 -28
  485. package/dist/cli/experimental/commands/pi.js.map +0 -1
  486. package/dist/cli/experimental/commands/server.d.ts +0 -13
  487. package/dist/cli/experimental/commands/server.d.ts.map +0 -1
  488. package/dist/cli/experimental/commands/server.js +0 -25
  489. package/dist/cli/experimental/commands/server.js.map +0 -1
  490. package/dist/cli/experimental/transport-address.d.ts +0 -10
  491. package/dist/cli/experimental/transport-address.d.ts.map +0 -1
  492. package/dist/cli/experimental/transport-address.js +0 -38
  493. package/dist/cli/experimental/transport-address.js.map +0 -1
  494. package/dist/client/index.d.ts +0 -3
  495. package/dist/client/index.d.ts.map +0 -1
  496. package/dist/client/index.js +0 -3
  497. package/dist/client/index.js.map +0 -1
  498. package/dist/client/remote-session.d.ts +0 -53
  499. package/dist/client/remote-session.d.ts.map +0 -1
  500. package/dist/client/remote-session.js +0 -340
  501. package/dist/client/remote-session.js.map +0 -1
  502. package/dist/client/transcript.d.ts +0 -12
  503. package/dist/client/transcript.d.ts.map +0 -1
  504. package/dist/client/transcript.js +0 -98
  505. package/dist/client/transcript.js.map +0 -1
  506. package/dist/utils/clipboard-native.d.ts +0 -11
  507. package/dist/utils/clipboard-native.d.ts.map +0 -1
  508. package/dist/utils/clipboard-native.js +0 -20
  509. package/dist/utils/clipboard-native.js.map +0 -1
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Presentation for the write tool.
3
+ *
4
+ * Renderers live apart from the implementation so a process that only displays tool output does not
5
+ * load the execution path or its typebox parameter schema. `write.ts` spreads these into its
6
+ * definition, so the tool's public shape is unchanged.
7
+ */
8
+ import { Container, Text } from "@earendil-works/pi-tui";
9
+ import { formatHiddenLines, previewLineCount } from "../../../modes/interactive/components/keybinding-hints.js";
10
+ import { getLanguageFromPath, highlightCode } from "../../../modes/interactive/theme/theme.js";
11
+ import { normalizeDisplayText, renderToolPath, replaceTabs, str } from "../render-utils.js";
12
+ class WriteCallRenderComponent extends Text {
13
+ cache;
14
+ constructor() {
15
+ super("", 0, 0);
16
+ }
17
+ }
18
+ const WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;
19
+ function highlightSingleLine(line, lang) {
20
+ const highlighted = highlightCode(line, lang);
21
+ return highlighted[0] ?? "";
22
+ }
23
+ function refreshWriteHighlightPrefix(cache) {
24
+ const prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);
25
+ if (prefixCount === 0)
26
+ return;
27
+ const prefixSource = cache.normalizedLines.slice(0, prefixCount).join("\n");
28
+ const prefixHighlighted = highlightCode(prefixSource, cache.lang);
29
+ for (let i = 0; i < prefixCount; i++) {
30
+ cache.highlightedLines[i] =
31
+ prefixHighlighted[i] ?? highlightSingleLine(cache.normalizedLines[i] ?? "", cache.lang);
32
+ }
33
+ }
34
+ function rebuildWriteHighlightCacheFull(rawPath, fileContent) {
35
+ const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
36
+ if (!lang)
37
+ return undefined;
38
+ const displayContent = normalizeDisplayText(fileContent);
39
+ const normalized = replaceTabs(displayContent);
40
+ return {
41
+ rawPath,
42
+ lang,
43
+ rawContent: fileContent,
44
+ normalizedLines: normalized.split("\n"),
45
+ highlightedLines: highlightCode(normalized, lang),
46
+ };
47
+ }
48
+ function updateWriteHighlightCacheIncremental(cache, rawPath, fileContent) {
49
+ const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
50
+ if (!lang)
51
+ return undefined;
52
+ if (!cache)
53
+ return rebuildWriteHighlightCacheFull(rawPath, fileContent);
54
+ if (cache.lang !== lang || cache.rawPath !== rawPath)
55
+ return rebuildWriteHighlightCacheFull(rawPath, fileContent);
56
+ if (!fileContent.startsWith(cache.rawContent))
57
+ return rebuildWriteHighlightCacheFull(rawPath, fileContent);
58
+ if (fileContent.length === cache.rawContent.length)
59
+ return cache;
60
+ const deltaRaw = fileContent.slice(cache.rawContent.length);
61
+ const deltaDisplay = normalizeDisplayText(deltaRaw);
62
+ const deltaNormalized = replaceTabs(deltaDisplay);
63
+ cache.rawContent = fileContent;
64
+ if (cache.normalizedLines.length === 0) {
65
+ cache.normalizedLines.push("");
66
+ cache.highlightedLines.push("");
67
+ }
68
+ const segments = deltaNormalized.split("\n");
69
+ const lastIndex = cache.normalizedLines.length - 1;
70
+ cache.normalizedLines[lastIndex] += segments[0];
71
+ cache.highlightedLines[lastIndex] = highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);
72
+ for (let i = 1; i < segments.length; i++) {
73
+ cache.normalizedLines.push(segments[i]);
74
+ cache.highlightedLines.push(highlightSingleLine(segments[i], cache.lang));
75
+ }
76
+ refreshWriteHighlightPrefix(cache);
77
+ return cache;
78
+ }
79
+ function trimTrailingEmptyLines(lines) {
80
+ let end = lines.length;
81
+ while (end > 0 && lines[end - 1] === "") {
82
+ end--;
83
+ }
84
+ return lines.slice(0, end);
85
+ }
86
+ function formatWriteCall(args, options, theme, cache, cwd) {
87
+ const rawPath = str(args?.file_path ?? args?.path);
88
+ const fileContent = str(args?.content);
89
+ const pathDisplay = renderToolPath(rawPath, theme, cwd);
90
+ let text = `${theme.fg("toolTitle", theme.bold("write"))} ${pathDisplay}`;
91
+ if (fileContent === null) {
92
+ text += `\n\n${theme.fg("error", "[invalid content arg - expected string]")}`;
93
+ }
94
+ else if (fileContent) {
95
+ const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
96
+ const renderedLines = lang
97
+ ? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))
98
+ : normalizeDisplayText(fileContent).split("\n");
99
+ const lines = trimTrailingEmptyLines(renderedLines);
100
+ const maxLines = options.expanded ? lines.length : previewLineCount(lines.length, 10);
101
+ const displayLines = lines.slice(0, maxLines);
102
+ const remaining = lines.length - displayLines.length;
103
+ text += `\n\n${displayLines.map((line) => (lang ? line : theme.fg("toolOutput", replaceTabs(line)))).join("\n")}`;
104
+ if (remaining > 0) {
105
+ text += `\n${formatHiddenLines(remaining, "more")}`;
106
+ }
107
+ }
108
+ return text;
109
+ }
110
+ function formatWriteResult(result, theme) {
111
+ if (!result.isError) {
112
+ return undefined;
113
+ }
114
+ const output = result.content
115
+ .filter((c) => c.type === "text")
116
+ .map((c) => c.text || "")
117
+ .join("\n");
118
+ if (!output) {
119
+ return undefined;
120
+ }
121
+ return `\n${theme.fg("error", output)}`;
122
+ }
123
+ export const writeRenderers = {
124
+ renderCall(args, theme, context) {
125
+ const renderArgs = args;
126
+ const rawPath = str(renderArgs?.file_path ?? renderArgs?.path);
127
+ const fileContent = str(renderArgs?.content);
128
+ const component = context.lastComponent ?? new WriteCallRenderComponent();
129
+ if (fileContent !== null) {
130
+ component.cache = context.argsComplete
131
+ ? rebuildWriteHighlightCacheFull(rawPath, fileContent)
132
+ : updateWriteHighlightCacheIncremental(component.cache, rawPath, fileContent);
133
+ }
134
+ else {
135
+ component.cache = undefined;
136
+ }
137
+ component.setText(formatWriteCall(renderArgs, { expanded: context.expanded, isPartial: context.isPartial }, theme, component.cache, context.cwd));
138
+ return component;
139
+ },
140
+ renderResult(result, _options, theme, context) {
141
+ const output = formatWriteResult({ ...result, isError: context.isError }, theme);
142
+ if (!output) {
143
+ const component = context.lastComponent ?? new Container();
144
+ component.clear();
145
+ return component;
146
+ }
147
+ const text = context.lastComponent ?? new Text("", 0, 0);
148
+ text.setText(output);
149
+ return text;
150
+ },
151
+ };
152
+ //# sourceMappingURL=write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../../../src/core/tools/renderers/write.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Presentation for the write tool.\n *\n * Renderers live apart from the implementation so a process that only displays tool output does not\n * load the execution path or its typebox parameter schema. `write.ts` spreads these into its\n * definition, so the tool's public shape is unchanged.\n */\n\nimport { Container, Text } from \"@earendil-works/pi-tui\";\nimport { formatHiddenLines, previewLineCount } from \"../../../modes/interactive/components/keybinding-hints.ts\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../../extensions/types.ts\";\nimport { normalizeDisplayText, renderToolPath, replaceTabs, str } from \"../render-utils.ts\";\n\ntype WriteHighlightCache = {\n\trawPath: string | null;\n\tlang: string;\n\trawContent: string;\n\tnormalizedLines: string[];\n\thighlightedLines: string[];\n};\nclass WriteCallRenderComponent extends Text {\n\tcache?: WriteHighlightCache;\n\n\tconstructor() {\n\t\tsuper(\"\", 0, 0);\n\t}\n}\nconst WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;\nfunction highlightSingleLine(line: string, lang: string): string {\n\tconst highlighted = highlightCode(line, lang);\n\treturn highlighted[0] ?? \"\";\n}\nfunction refreshWriteHighlightPrefix(cache: WriteHighlightCache): void {\n\tconst prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);\n\tif (prefixCount === 0) return;\n\tconst prefixSource = cache.normalizedLines.slice(0, prefixCount).join(\"\\n\");\n\tconst prefixHighlighted = highlightCode(prefixSource, cache.lang);\n\tfor (let i = 0; i < prefixCount; i++) {\n\t\tcache.highlightedLines[i] =\n\t\t\tprefixHighlighted[i] ?? highlightSingleLine(cache.normalizedLines[i] ?? \"\", cache.lang);\n\t}\n}\nfunction rebuildWriteHighlightCacheFull(rawPath: string | null, fileContent: string): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tconst displayContent = normalizeDisplayText(fileContent);\n\tconst normalized = replaceTabs(displayContent);\n\treturn {\n\t\trawPath,\n\t\tlang,\n\t\trawContent: fileContent,\n\t\tnormalizedLines: normalized.split(\"\\n\"),\n\t\thighlightedLines: highlightCode(normalized, lang),\n\t};\n}\nfunction updateWriteHighlightCacheIncremental(\n\tcache: WriteHighlightCache | undefined,\n\trawPath: string | null,\n\tfileContent: string,\n): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tif (!cache) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (cache.lang !== lang || cache.rawPath !== rawPath) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (!fileContent.startsWith(cache.rawContent)) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (fileContent.length === cache.rawContent.length) return cache;\n\n\tconst deltaRaw = fileContent.slice(cache.rawContent.length);\n\tconst deltaDisplay = normalizeDisplayText(deltaRaw);\n\tconst deltaNormalized = replaceTabs(deltaDisplay);\n\tcache.rawContent = fileContent;\n\tif (cache.normalizedLines.length === 0) {\n\t\tcache.normalizedLines.push(\"\");\n\t\tcache.highlightedLines.push(\"\");\n\t}\n\n\tconst segments = deltaNormalized.split(\"\\n\");\n\tconst lastIndex = cache.normalizedLines.length - 1;\n\tcache.normalizedLines[lastIndex] += segments[0];\n\tcache.highlightedLines[lastIndex] = highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tcache.normalizedLines.push(segments[i]);\n\t\tcache.highlightedLines.push(highlightSingleLine(segments[i], cache.lang));\n\t}\n\trefreshWriteHighlightPrefix(cache);\n\treturn cache;\n}\nfunction trimTrailingEmptyLines(lines: string[]): string[] {\n\tlet end = lines.length;\n\twhile (end > 0 && lines[end - 1] === \"\") {\n\t\tend--;\n\t}\n\treturn lines.slice(0, end);\n}\nfunction formatWriteCall(\n\targs: { path?: string; file_path?: string; content?: string } | undefined,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n\tcwd: string,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst fileContent = str(args?.content);\n\tconst pathDisplay = renderToolPath(rawPath, theme, cwd);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"write\"))} ${pathDisplay}`;\n\n\tif (fileContent === null) {\n\t\ttext += `\\n\\n${theme.fg(\"error\", \"[invalid content arg - expected string]\")}`;\n\t} else if (fileContent) {\n\t\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\t\tconst renderedLines = lang\n\t\t\t? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))\n\t\t\t: normalizeDisplayText(fileContent).split(\"\\n\");\n\t\tconst lines = trimTrailingEmptyLines(renderedLines);\n\t\tconst maxLines = options.expanded ? lines.length : previewLineCount(lines.length, 10);\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - displayLines.length;\n\t\ttext += `\\n\\n${displayLines.map((line) => (lang ? line : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `\\n${formatHiddenLines(remaining, \"more\")}`;\n\t\t}\n\t}\n\n\treturn text;\n}\nfunction formatWriteResult(\n\tresult: { content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>; isError?: boolean },\n\ttheme: Theme,\n): string | undefined {\n\tif (!result.isError) {\n\t\treturn undefined;\n\t}\n\tconst output = result.content\n\t\t.filter((c) => c.type === \"text\")\n\t\t.map((c) => c.text || \"\")\n\t\t.join(\"\\n\");\n\tif (!output) {\n\t\treturn undefined;\n\t}\n\treturn `\\n${theme.fg(\"error\", output)}`;\n}\n\nexport const writeRenderers: Pick<ToolDefinition<any, any>, \"renderCall\" | \"renderResult\"> = {\n\trenderCall(args, theme, context) {\n\t\tconst renderArgs = args as { path?: string; file_path?: string; content?: string } | undefined;\n\t\tconst rawPath = str(renderArgs?.file_path ?? renderArgs?.path);\n\t\tconst fileContent = str(renderArgs?.content);\n\t\tconst component =\n\t\t\t(context.lastComponent as WriteCallRenderComponent | undefined) ?? new WriteCallRenderComponent();\n\t\tif (fileContent !== null) {\n\t\t\tcomponent.cache = context.argsComplete\n\t\t\t\t? rebuildWriteHighlightCacheFull(rawPath, fileContent)\n\t\t\t\t: updateWriteHighlightCacheIncremental(component.cache, rawPath, fileContent);\n\t\t} else {\n\t\t\tcomponent.cache = undefined;\n\t\t}\n\t\tcomponent.setText(\n\t\t\tformatWriteCall(\n\t\t\t\trenderArgs,\n\t\t\t\t{ expanded: context.expanded, isPartial: context.isPartial },\n\t\t\t\ttheme,\n\t\t\t\tcomponent.cache,\n\t\t\t\tcontext.cwd,\n\t\t\t),\n\t\t);\n\t\treturn component;\n\t},\n\trenderResult(result, _options, theme, context) {\n\t\tconst output = formatWriteResult({ ...result, isError: context.isError }, theme);\n\t\tif (!output) {\n\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\tcomponent.clear();\n\t\t\treturn component;\n\t\t}\n\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\ttext.setText(output);\n\t\treturn text;\n\t},\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAK5C,OAAO,EAA4B,KAAK,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAEpG,OAAO,KAAK,EAAE,kBAAkB,EAAkB,MAAM,eAAe,CAAC;AACxE,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAQlF,QAAA,MAAM,WAAW;;;EAGf,CAAC;AAEH,eAAO,MAAM,iCAAiC;;;CAGpC,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,WAAW,CAAC,EAAE,kBAAkB,CAAC;CACjC;AAMD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC/B,8BAA8B;IAC9B,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,mCAAmC;IACnC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5E;AAQD,MAAM,WAAW,gBAAgB;IAChC,oEAAoE;IACpE,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;CAC9C;AAuLD,wBAAgB,yBAAyB,CACxC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,gBAAgB,GACxB,kBAAkB,CAAC,OAAO,WAAW,EAAE,gBAAgB,CAAC,CA6H1D;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC,OAAO,WAAW,CAAC,CAEtG","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { Container, Text } from \"@earendil-works/pi-tui\";\nimport type { AgentTool } from \"apex-code-agent-core\";\nimport { mkdir as fsMkdir } from \"fs/promises\";\nimport { dirname } from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { formatHiddenLines, previewLineCount } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { getExperimentalToolSampling } from \"../experimental.ts\";\nimport type { ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { getPreparedPathOperation, type PreparedPathOperation } from \"../permissions/operations.ts\";\nimport type { PermissionPreview } from \"../permissions/responder.ts\";\nimport type { ApexToolDefinition, EvidenceRecord } from \"./contract.ts\";\nimport type { DiagnosticsOperations, DiagnosticsOutcome } from \"./diagnostics.ts\";\nimport { diagnosticEvidenceForPath, formatDiagnosticsOutcome } from \"./diagnostics.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport { createPathPermissionSpec } from \"./path-permission.ts\";\nimport { readPreparedPath, resolveToCwd, writePathAtomically, writePreparedPath } from \"./path-utils.ts\";\nimport { normalizeDisplayText, renderToolPath, replaceTabs, str } from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst writeSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to write (relative or absolute)\" }),\n\tcontent: Type.String({ description: \"Content to write to the file\" }),\n});\n\nexport const writeToolSystemPromptContribution = {\n\tsnippet: \"Create or overwrite files\",\n\tguidelines: [\"Use write only for new files or complete rewrites.\"],\n} as const;\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\nexport interface WriteToolDetails {\n\tbyteCount: number;\n\tcontentHash: string;\n\t/** Present only when `WriteToolOptions.diagnosticsOperations` is configured (LSP.4). */\n\tdiagnostics?: DiagnosticsOutcome;\n}\n\nfunction sha256(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\n/**\n * Pluggable operations for the write tool.\n * Override these to delegate file writing to remote systems (for example SSH).\n */\nexport interface WriteOperations {\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Create directory recursively */\n\tmkdir: (dir: string) => Promise<void>;\n\t/**\n\t * Execute a gate-prepared write no-follow against the authorized target.\n\t * Required for gated calls: when a prepared operation exists, pathname writes\n\t * are never used, so an unset implementation fails closed.\n\t */\n\twritePrepared?: (operation: PreparedPathOperation, content: string) => void;\n}\n\nconst defaultWriteOperations: WriteOperations = {\n\twriteFile: async (path, content) => writePathAtomically(path, content),\n\tmkdir: (dir) => fsMkdir(dir, { recursive: true }).then(() => {}),\n\twritePrepared: (operation, content) => writePreparedPath(operation, content),\n};\n\nexport interface WriteToolOptions {\n\t/** Custom operations for file writing. Default: local filesystem */\n\toperations?: WriteOperations;\n\t/**\n\t * Optional post-mutation diagnostics (LSP.4). Absent by default -- byte-identical\n\t * existing behavior. When present, called inside `withFileMutationQueue` after the\n\t * write succeeds and before the queue releases, matching `TestOperations`/\n\t * `GrepOperations`' injection pattern (`test.ts:28`, `grep.ts:58`).\n\t */\n\tdiagnosticsOperations?: DiagnosticsOperations;\n}\n\ntype WriteHighlightCache = {\n\trawPath: string | null;\n\tlang: string;\n\trawContent: string;\n\tnormalizedLines: string[];\n\thighlightedLines: string[];\n};\n\nclass WriteCallRenderComponent extends Text {\n\tcache?: WriteHighlightCache;\n\n\tconstructor() {\n\t\tsuper(\"\", 0, 0);\n\t}\n}\n\nconst WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;\n\nfunction highlightSingleLine(line: string, lang: string): string {\n\tconst highlighted = highlightCode(line, lang);\n\treturn highlighted[0] ?? \"\";\n}\n\nfunction refreshWriteHighlightPrefix(cache: WriteHighlightCache): void {\n\tconst prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);\n\tif (prefixCount === 0) return;\n\tconst prefixSource = cache.normalizedLines.slice(0, prefixCount).join(\"\\n\");\n\tconst prefixHighlighted = highlightCode(prefixSource, cache.lang);\n\tfor (let i = 0; i < prefixCount; i++) {\n\t\tcache.highlightedLines[i] =\n\t\t\tprefixHighlighted[i] ?? highlightSingleLine(cache.normalizedLines[i] ?? \"\", cache.lang);\n\t}\n}\n\nfunction rebuildWriteHighlightCacheFull(rawPath: string | null, fileContent: string): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tconst displayContent = normalizeDisplayText(fileContent);\n\tconst normalized = replaceTabs(displayContent);\n\treturn {\n\t\trawPath,\n\t\tlang,\n\t\trawContent: fileContent,\n\t\tnormalizedLines: normalized.split(\"\\n\"),\n\t\thighlightedLines: highlightCode(normalized, lang),\n\t};\n}\n\nfunction updateWriteHighlightCacheIncremental(\n\tcache: WriteHighlightCache | undefined,\n\trawPath: string | null,\n\tfileContent: string,\n): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tif (!cache) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (cache.lang !== lang || cache.rawPath !== rawPath) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (!fileContent.startsWith(cache.rawContent)) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (fileContent.length === cache.rawContent.length) return cache;\n\n\tconst deltaRaw = fileContent.slice(cache.rawContent.length);\n\tconst deltaDisplay = normalizeDisplayText(deltaRaw);\n\tconst deltaNormalized = replaceTabs(deltaDisplay);\n\tcache.rawContent = fileContent;\n\tif (cache.normalizedLines.length === 0) {\n\t\tcache.normalizedLines.push(\"\");\n\t\tcache.highlightedLines.push(\"\");\n\t}\n\n\tconst segments = deltaNormalized.split(\"\\n\");\n\tconst lastIndex = cache.normalizedLines.length - 1;\n\tcache.normalizedLines[lastIndex] += segments[0];\n\tcache.highlightedLines[lastIndex] = highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tcache.normalizedLines.push(segments[i]);\n\t\tcache.highlightedLines.push(highlightSingleLine(segments[i], cache.lang));\n\t}\n\trefreshWriteHighlightPrefix(cache);\n\treturn cache;\n}\n\nfunction trimTrailingEmptyLines(lines: string[]): string[] {\n\tlet end = lines.length;\n\twhile (end > 0 && lines[end - 1] === \"\") {\n\t\tend--;\n\t}\n\treturn lines.slice(0, end);\n}\n\nfunction formatWriteCall(\n\targs: { path?: string; file_path?: string; content?: string } | undefined,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n\tcwd: string,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst fileContent = str(args?.content);\n\tconst pathDisplay = renderToolPath(rawPath, theme, cwd);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"write\"))} ${pathDisplay}`;\n\n\tif (fileContent === null) {\n\t\ttext += `\\n\\n${theme.fg(\"error\", \"[invalid content arg - expected string]\")}`;\n\t} else if (fileContent) {\n\t\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\t\tconst renderedLines = lang\n\t\t\t? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))\n\t\t\t: normalizeDisplayText(fileContent).split(\"\\n\");\n\t\tconst lines = trimTrailingEmptyLines(renderedLines);\n\t\tconst maxLines = options.expanded ? lines.length : previewLineCount(lines.length, 10);\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - displayLines.length;\n\t\ttext += `\\n\\n${displayLines.map((line) => (lang ? line : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `\\n${formatHiddenLines(remaining, \"more\")}`;\n\t\t}\n\t}\n\n\treturn text;\n}\n\nfunction formatWriteResult(\n\tresult: { content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>; isError?: boolean },\n\ttheme: Theme,\n): string | undefined {\n\tif (!result.isError) {\n\t\treturn undefined;\n\t}\n\tconst output = result.content\n\t\t.filter((c) => c.type === \"text\")\n\t\t.map((c) => c.text || \"\")\n\t\t.join(\"\\n\");\n\tif (!output) {\n\t\treturn undefined;\n\t}\n\treturn `\\n${theme.fg(\"error\", output)}`;\n}\n\nconst WRITE_PREVIEW_MAX_BYTES = 512 * 1024;\n\n/** A trailing newline terminates the last line rather than starting an empty one. */\nconst countLines = (text: string): number => {\n\tif (text === \"\") return 0;\n\tconst body = text.endsWith(\"\\n\") ? text.slice(0, -1) : text;\n\treturn body.split(\"\\n\").length;\n};\n\nconst lineCount = (text: string): string => {\n\tconst n = countLines(text);\n\treturn `${n} ${n === 1 ? \"line\" : \"lines\"}`;\n};\n\n/**\n * Describe a whole-file replacement without drawing the whole file.\n *\n * A diff of a write is the file twice over, which is not what a person needs in\n * order to decide. Sizes on both sides answer the question that matters, which is\n * whether this replaces something substantial. Read through the prepared\n * operation, so a swapped target fails here as it fails the write (ADR 0029).\n */\nfunction previewWrite(params: { path: string; content: string }): PermissionPreview {\n\tconst incoming = `${lineCount(params.content)}, ${Buffer.byteLength(params.content)} bytes`;\n\tconst prepared = getPreparedPathOperation(params);\n\tif (!prepared) return { kind: \"unavailable\", reason: \"No authorized target was prepared for this call\" };\n\tif (prepared.kind === \"path-new\") {\n\t\treturn { kind: \"summary\", lines: [`Create ${params.path} with ${incoming}`] };\n\t}\n\n\tconst buffer = readPreparedPath(prepared, WRITE_PREVIEW_MAX_BYTES + 1);\n\tif (buffer.byteLength > WRITE_PREVIEW_MAX_BYTES) {\n\t\treturn {\n\t\t\tkind: \"summary\",\n\t\t\tlines: [`Replace ${params.path}, over ${WRITE_PREVIEW_MAX_BYTES / 1024}KB, with ${incoming}`],\n\t\t};\n\t}\n\tconst existing = buffer.includes(0)\n\t\t? `${buffer.byteLength} bytes of binary`\n\t\t: `${lineCount(buffer.toString(\"utf-8\"))}, ${buffer.byteLength} bytes`;\n\treturn { kind: \"summary\", lines: [`Replace ${params.path}`, `${existing} becomes ${incoming}`] };\n}\n\nexport function createWriteToolDefinition(\n\tcwd: string,\n\toptions?: WriteToolOptions,\n): ApexToolDefinition<typeof writeSchema, WriteToolDetails> {\n\tconst ops = options?.operations ?? defaultWriteOperations;\n\tconst diagnosticsOperations = options?.diagnosticsOperations;\n\treturn {\n\t\tname: \"write\",\n\t\tlabel: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tpromptSnippet: writeToolSystemPromptContribution.snippet,\n\t\tpromptGuidelines: [...writeToolSystemPromptContribution.guidelines],\n\t\tparameters: writeSchema,\n\t\tcontract: {\n\t\t\tcapabilities: new Set([\"fs.write\"]),\n\t\t\tpermission: createPathPermissionSpec({\n\t\t\t\tcwd,\n\t\t\t\tdefaultBehavior: \"ask\",\n\t\t\t\tverb: \"Write\",\n\t\t\t\tgetPath: (params) => params.path,\n\t\t\t\tpreviewCall: (params) => previewWrite(params as { path: string; content: string }),\n\t\t\t}),\n\t\t\tcontext: { resultRecoverable: true, deferSchema: false },\n\t\t\tevidence: {\n\t\t\t\temits: new Set(diagnosticsOperations ? [\"diff\", \"diagnostic\"] : [\"diff\"]),\n\t\t\t\tcapture: (params, result) => {\n\t\t\t\t\tconst details = result.details;\n\t\t\t\t\tconst records: EvidenceRecord[] = details\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tkind: \"diff\",\n\t\t\t\t\t\t\t\t\tpath: params.path,\n\t\t\t\t\t\t\t\t\tbyteCount: details.byteCount,\n\t\t\t\t\t\t\t\t\tcontentHash: details.contentHash,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: [{ kind: \"diff\", path: params.path }];\n\t\t\t\t\tif (details?.diagnostics) records.push(diagnosticEvidenceForPath(params.path, details.diagnostics));\n\t\t\t\t\treturn records;\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tconstrainedSampling: getExperimentalToolSampling(),\n\t\tasync execute(_toolCallId, input: { path: string; content: string }, signal?: AbortSignal, _onUpdate?, _ctx?) {\n\t\t\tconst { path, content } = input;\n\t\t\tconst prepared = getPreparedPathOperation(input);\n\t\t\tconst absolutePath = prepared?.path.value ?? resolveToCwd(path, cwd);\n\t\t\tif (prepared && !ops.writePrepared) {\n\t\t\t\tthrow new Error(\"No safe prepared write operation is available for this gated write\");\n\t\t\t}\n\t\t\tconst dir = dirname(absolutePath);\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\t// Do not reject from an abort event listener here: that would release the\n\t\t\t\t// mutation queue while an in-flight filesystem operation may still finish.\n\t\t\t\t// Checking signal.aborted after each await observes the same aborts while\n\t\t\t\t// keeping the queue locked until the current operation has settled.\n\t\t\t\tconst throwIfAborted = (): void => {\n\t\t\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\t\t};\n\n\t\t\t\tthrowIfAborted();\n\t\t\t\tif (prepared) {\n\t\t\t\t\t// Gated path: no-follow execution against the exact authorized target.\n\t\t\t\t\tops.writePrepared!(prepared, content);\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t} else {\n\t\t\t\t\t// Create parent directories if needed.\n\t\t\t\t\tawait ops.mkdir(dir);\n\t\t\t\t\tthrowIfAborted();\n\n\t\t\t\t\t// Write the file contents.\n\t\t\t\t\tawait ops.writeFile(absolutePath, content);\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t}\n\n\t\t\t\tconst successText = `Successfully wrote ${content.length} bytes to ${path}`;\n\n\t\t\t\tif (diagnosticsOperations) {\n\t\t\t\t\tconst diagnostics = await diagnosticsOperations.afterMutation(absolutePath, signal);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `${successText}\\n\\n${formatDiagnosticsOutcome(diagnostics)}` }],\n\t\t\t\t\t\tdetails: { byteCount: Buffer.byteLength(content), contentHash: sha256(content), diagnostics },\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: successText }],\n\t\t\t\t\tdetails: { byteCount: Buffer.byteLength(content), contentHash: sha256(content) },\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst renderArgs = args as { path?: string; file_path?: string; content?: string } | undefined;\n\t\t\tconst rawPath = str(renderArgs?.file_path ?? renderArgs?.path);\n\t\t\tconst fileContent = str(renderArgs?.content);\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as WriteCallRenderComponent | undefined) ?? new WriteCallRenderComponent();\n\t\t\tif (fileContent !== null) {\n\t\t\t\tcomponent.cache = context.argsComplete\n\t\t\t\t\t? rebuildWriteHighlightCacheFull(rawPath, fileContent)\n\t\t\t\t\t: updateWriteHighlightCacheIncremental(component.cache, rawPath, fileContent);\n\t\t\t} else {\n\t\t\t\tcomponent.cache = undefined;\n\t\t\t}\n\t\t\tcomponent.setText(\n\t\t\t\tformatWriteCall(\n\t\t\t\t\trenderArgs,\n\t\t\t\t\t{ expanded: context.expanded, isPartial: context.isPartial },\n\t\t\t\t\ttheme,\n\t\t\t\t\tcomponent.cache,\n\t\t\t\t\tcontext.cwd,\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn component;\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst output = formatWriteResult({ ...result, isError: context.isError }, theme);\n\t\t\tif (!output) {\n\t\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\t\tcomponent.clear();\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(output);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createWriteTool(cwd: string, options?: WriteToolOptions): AgentTool<typeof writeSchema> {\n\treturn wrapToolDefinition(createWriteToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,EAA4B,KAAK,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAEpG,OAAO,KAAK,EAAE,kBAAkB,EAAkB,MAAM,eAAe,CAAC;AACxE,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAQlF,QAAA,MAAM,WAAW;;;EAGf,CAAC;AAEH,eAAO,MAAM,iCAAiC;;;CAGpC,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,WAAW,CAAC,EAAE,kBAAkB,CAAC;CACjC;AAMD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC/B,8BAA8B;IAC9B,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,mCAAmC;IACnC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5E;AAQD,MAAM,WAAW,gBAAgB;IAChC,oEAAoE;IACpE,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;CAC9C;AA6CD,wBAAgB,yBAAyB,CACxC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,gBAAgB,GACxB,kBAAkB,CAAC,OAAO,WAAW,EAAE,gBAAgB,CAAC,CAiG1D;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC,OAAO,WAAW,CAAC,CAEtG","sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { AgentTool } from \"apex-code-agent-core\";\nimport { mkdir as fsMkdir } from \"fs/promises\";\nimport { dirname } from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { getExperimentalToolSampling } from \"../experimental.ts\";\nimport type { ExtensionContext } from \"../extensions/types.ts\";\nimport { getPreparedPathOperation, type PreparedPathOperation } from \"../permissions/operations.ts\";\nimport type { PermissionPreview } from \"../permissions/responder.ts\";\nimport type { ApexToolDefinition, EvidenceRecord } from \"./contract.ts\";\nimport type { DiagnosticsOperations, DiagnosticsOutcome } from \"./diagnostics.ts\";\nimport { diagnosticEvidenceForPath, formatDiagnosticsOutcome } from \"./diagnostics.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport { createPathPermissionSpec } from \"./path-permission.ts\";\nimport { readPreparedPath, resolveToCwd, writePathAtomically, writePreparedPath } from \"./path-utils.ts\";\nimport { writeRenderers } from \"./renderers/write.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst writeSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to write (relative or absolute)\" }),\n\tcontent: Type.String({ description: \"Content to write to the file\" }),\n});\n\nexport const writeToolSystemPromptContribution = {\n\tsnippet: \"Create or overwrite files\",\n\tguidelines: [\"Use write only for new files or complete rewrites.\"],\n} as const;\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\nexport interface WriteToolDetails {\n\tbyteCount: number;\n\tcontentHash: string;\n\t/** Present only when `WriteToolOptions.diagnosticsOperations` is configured (LSP.4). */\n\tdiagnostics?: DiagnosticsOutcome;\n}\n\nfunction sha256(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\n/**\n * Pluggable operations for the write tool.\n * Override these to delegate file writing to remote systems (for example SSH).\n */\nexport interface WriteOperations {\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Create directory recursively */\n\tmkdir: (dir: string) => Promise<void>;\n\t/**\n\t * Execute a gate-prepared write no-follow against the authorized target.\n\t * Required for gated calls: when a prepared operation exists, pathname writes\n\t * are never used, so an unset implementation fails closed.\n\t */\n\twritePrepared?: (operation: PreparedPathOperation, content: string) => void;\n}\n\nconst defaultWriteOperations: WriteOperations = {\n\twriteFile: async (path, content) => writePathAtomically(path, content),\n\tmkdir: (dir) => fsMkdir(dir, { recursive: true }).then(() => {}),\n\twritePrepared: (operation, content) => writePreparedPath(operation, content),\n};\n\nexport interface WriteToolOptions {\n\t/** Custom operations for file writing. Default: local filesystem */\n\toperations?: WriteOperations;\n\t/**\n\t * Optional post-mutation diagnostics (LSP.4). Absent by default -- byte-identical\n\t * existing behavior. When present, called inside `withFileMutationQueue` after the\n\t * write succeeds and before the queue releases, matching `TestOperations`/\n\t * `GrepOperations`' injection pattern (`test.ts:28`, `grep.ts:58`).\n\t */\n\tdiagnosticsOperations?: DiagnosticsOperations;\n}\n\nconst WRITE_PREVIEW_MAX_BYTES = 512 * 1024;\n\n/** A trailing newline terminates the last line rather than starting an empty one. */\nconst countLines = (text: string): number => {\n\tif (text === \"\") return 0;\n\tconst body = text.endsWith(\"\\n\") ? text.slice(0, -1) : text;\n\treturn body.split(\"\\n\").length;\n};\n\nconst lineCount = (text: string): string => {\n\tconst n = countLines(text);\n\treturn `${n} ${n === 1 ? \"line\" : \"lines\"}`;\n};\n\n/**\n * Describe a whole-file replacement without drawing the whole file.\n *\n * A diff of a write is the file twice over, which is not what a person needs in\n * order to decide. Sizes on both sides answer the question that matters, which is\n * whether this replaces something substantial. Read through the prepared\n * operation, so a swapped target fails here as it fails the write (ADR 0029).\n */\nfunction previewWrite(params: { path: string; content: string }): PermissionPreview {\n\tconst incoming = `${lineCount(params.content)}, ${Buffer.byteLength(params.content)} bytes`;\n\tconst prepared = getPreparedPathOperation(params);\n\tif (!prepared) return { kind: \"unavailable\", reason: \"No authorized target was prepared for this call\" };\n\tif (prepared.kind === \"path-new\") {\n\t\treturn { kind: \"summary\", lines: [`Create ${params.path} with ${incoming}`] };\n\t}\n\n\tconst buffer = readPreparedPath(prepared, WRITE_PREVIEW_MAX_BYTES + 1);\n\tif (buffer.byteLength > WRITE_PREVIEW_MAX_BYTES) {\n\t\treturn {\n\t\t\tkind: \"summary\",\n\t\t\tlines: [`Replace ${params.path}, over ${WRITE_PREVIEW_MAX_BYTES / 1024}KB, with ${incoming}`],\n\t\t};\n\t}\n\tconst existing = buffer.includes(0)\n\t\t? `${buffer.byteLength} bytes of binary`\n\t\t: `${lineCount(buffer.toString(\"utf-8\"))}, ${buffer.byteLength} bytes`;\n\treturn { kind: \"summary\", lines: [`Replace ${params.path}`, `${existing} becomes ${incoming}`] };\n}\n\nexport function createWriteToolDefinition(\n\tcwd: string,\n\toptions?: WriteToolOptions,\n): ApexToolDefinition<typeof writeSchema, WriteToolDetails> {\n\tconst ops = options?.operations ?? defaultWriteOperations;\n\tconst diagnosticsOperations = options?.diagnosticsOperations;\n\treturn {\n\t\tname: \"write\",\n\t\tlabel: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tpromptSnippet: writeToolSystemPromptContribution.snippet,\n\t\tpromptGuidelines: [...writeToolSystemPromptContribution.guidelines],\n\t\tparameters: writeSchema,\n\t\tcontract: {\n\t\t\tcapabilities: new Set([\"fs.write\"]),\n\t\t\tpermission: createPathPermissionSpec({\n\t\t\t\tcwd,\n\t\t\t\tdefaultBehavior: \"ask\",\n\t\t\t\tverb: \"Write\",\n\t\t\t\tgetPath: (params) => params.path,\n\t\t\t\tpreviewCall: (params) => previewWrite(params as { path: string; content: string }),\n\t\t\t}),\n\t\t\tcontext: { resultRecoverable: true, deferSchema: false },\n\t\t\tevidence: {\n\t\t\t\temits: new Set(diagnosticsOperations ? [\"diff\", \"diagnostic\"] : [\"diff\"]),\n\t\t\t\tcapture: (params, result) => {\n\t\t\t\t\tconst details = result.details;\n\t\t\t\t\tconst records: EvidenceRecord[] = details\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tkind: \"diff\",\n\t\t\t\t\t\t\t\t\tpath: params.path,\n\t\t\t\t\t\t\t\t\tbyteCount: details.byteCount,\n\t\t\t\t\t\t\t\t\tcontentHash: details.contentHash,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: [{ kind: \"diff\", path: params.path }];\n\t\t\t\t\tif (details?.diagnostics) records.push(diagnosticEvidenceForPath(params.path, details.diagnostics));\n\t\t\t\t\treturn records;\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tconstrainedSampling: getExperimentalToolSampling() ?? { type: \"json_schema\", strict: \"prefer\" },\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\tinput: { path: string; content: string },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\tctx?: ExtensionContext,\n\t\t) {\n\t\t\tconst { path, content } = input;\n\t\t\tconst prepared = getPreparedPathOperation(input);\n\t\t\tconst absolutePath = prepared?.path.value ?? resolveToCwd(path, ctx?.cwd || cwd);\n\t\t\tif (prepared && !ops.writePrepared) {\n\t\t\t\tthrow new Error(\"No safe prepared write operation is available for this gated write\");\n\t\t\t}\n\t\t\tconst dir = dirname(absolutePath);\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\t// Do not reject from an abort event listener here: that would release the\n\t\t\t\t// mutation queue while an in-flight filesystem operation may still finish.\n\t\t\t\t// Checking signal.aborted after each await observes the same aborts while\n\t\t\t\t// keeping the queue locked until the current operation has settled.\n\t\t\t\tconst throwIfAborted = (): void => {\n\t\t\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\t\t};\n\n\t\t\t\tthrowIfAborted();\n\t\t\t\tif (prepared) {\n\t\t\t\t\t// Gated path: no-follow execution against the exact authorized target.\n\t\t\t\t\tops.writePrepared!(prepared, content);\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t} else {\n\t\t\t\t\t// Create parent directories if needed.\n\t\t\t\t\tawait ops.mkdir(dir);\n\t\t\t\t\tthrowIfAborted();\n\n\t\t\t\t\t// Write the file contents.\n\t\t\t\t\tawait ops.writeFile(absolutePath, content);\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t}\n\n\t\t\t\tconst successText = `Successfully wrote to ${path}`;\n\n\t\t\t\tif (diagnosticsOperations) {\n\t\t\t\t\tconst diagnostics = await diagnosticsOperations.afterMutation(absolutePath, signal);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `${successText}\\n\\n${formatDiagnosticsOutcome(diagnostics)}` }],\n\t\t\t\t\t\tdetails: { byteCount: Buffer.byteLength(content), contentHash: sha256(content), diagnostics },\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: successText }],\n\t\t\t\t\tdetails: { byteCount: Buffer.byteLength(content), contentHash: sha256(content) },\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t\t...writeRenderers,\n\t};\n}\n\nexport function createWriteTool(cwd: string, options?: WriteToolOptions): AgentTool<typeof writeSchema> {\n\treturn wrapToolDefinition(createWriteToolDefinition(cwd, options));\n}\n"]}
@@ -1,17 +1,14 @@
1
1
  import { createHash } from "node:crypto";
2
- import { Container, Text } from "@earendil-works/pi-tui";
3
2
  import { mkdir as fsMkdir } from "fs/promises";
4
3
  import { dirname } from "path";
5
4
  import { Type } from "typebox";
6
- import { formatHiddenLines, previewLineCount } from "../../modes/interactive/components/keybinding-hints.js";
7
- import { getLanguageFromPath, highlightCode } from "../../modes/interactive/theme/theme.js";
8
5
  import { getExperimentalToolSampling } from "../experimental.js";
9
6
  import { getPreparedPathOperation } from "../permissions/operations.js";
10
7
  import { diagnosticEvidenceForPath, formatDiagnosticsOutcome } from "./diagnostics.js";
11
8
  import { withFileMutationQueue } from "./file-mutation-queue.js";
12
9
  import { createPathPermissionSpec } from "./path-permission.js";
13
10
  import { readPreparedPath, resolveToCwd, writePathAtomically, writePreparedPath } from "./path-utils.js";
14
- import { normalizeDisplayText, renderToolPath, replaceTabs, str } from "./render-utils.js";
11
+ import { writeRenderers } from "./renderers/write.js";
15
12
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
16
13
  const writeSchema = Type.Object({
17
14
  path: Type.String({ description: "Path to the file to write (relative or absolute)" }),
@@ -29,117 +26,6 @@ const defaultWriteOperations = {
29
26
  mkdir: (dir) => fsMkdir(dir, { recursive: true }).then(() => { }),
30
27
  writePrepared: (operation, content) => writePreparedPath(operation, content),
31
28
  };
32
- class WriteCallRenderComponent extends Text {
33
- cache;
34
- constructor() {
35
- super("", 0, 0);
36
- }
37
- }
38
- const WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;
39
- function highlightSingleLine(line, lang) {
40
- const highlighted = highlightCode(line, lang);
41
- return highlighted[0] ?? "";
42
- }
43
- function refreshWriteHighlightPrefix(cache) {
44
- const prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);
45
- if (prefixCount === 0)
46
- return;
47
- const prefixSource = cache.normalizedLines.slice(0, prefixCount).join("\n");
48
- const prefixHighlighted = highlightCode(prefixSource, cache.lang);
49
- for (let i = 0; i < prefixCount; i++) {
50
- cache.highlightedLines[i] =
51
- prefixHighlighted[i] ?? highlightSingleLine(cache.normalizedLines[i] ?? "", cache.lang);
52
- }
53
- }
54
- function rebuildWriteHighlightCacheFull(rawPath, fileContent) {
55
- const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
56
- if (!lang)
57
- return undefined;
58
- const displayContent = normalizeDisplayText(fileContent);
59
- const normalized = replaceTabs(displayContent);
60
- return {
61
- rawPath,
62
- lang,
63
- rawContent: fileContent,
64
- normalizedLines: normalized.split("\n"),
65
- highlightedLines: highlightCode(normalized, lang),
66
- };
67
- }
68
- function updateWriteHighlightCacheIncremental(cache, rawPath, fileContent) {
69
- const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
70
- if (!lang)
71
- return undefined;
72
- if (!cache)
73
- return rebuildWriteHighlightCacheFull(rawPath, fileContent);
74
- if (cache.lang !== lang || cache.rawPath !== rawPath)
75
- return rebuildWriteHighlightCacheFull(rawPath, fileContent);
76
- if (!fileContent.startsWith(cache.rawContent))
77
- return rebuildWriteHighlightCacheFull(rawPath, fileContent);
78
- if (fileContent.length === cache.rawContent.length)
79
- return cache;
80
- const deltaRaw = fileContent.slice(cache.rawContent.length);
81
- const deltaDisplay = normalizeDisplayText(deltaRaw);
82
- const deltaNormalized = replaceTabs(deltaDisplay);
83
- cache.rawContent = fileContent;
84
- if (cache.normalizedLines.length === 0) {
85
- cache.normalizedLines.push("");
86
- cache.highlightedLines.push("");
87
- }
88
- const segments = deltaNormalized.split("\n");
89
- const lastIndex = cache.normalizedLines.length - 1;
90
- cache.normalizedLines[lastIndex] += segments[0];
91
- cache.highlightedLines[lastIndex] = highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);
92
- for (let i = 1; i < segments.length; i++) {
93
- cache.normalizedLines.push(segments[i]);
94
- cache.highlightedLines.push(highlightSingleLine(segments[i], cache.lang));
95
- }
96
- refreshWriteHighlightPrefix(cache);
97
- return cache;
98
- }
99
- function trimTrailingEmptyLines(lines) {
100
- let end = lines.length;
101
- while (end > 0 && lines[end - 1] === "") {
102
- end--;
103
- }
104
- return lines.slice(0, end);
105
- }
106
- function formatWriteCall(args, options, theme, cache, cwd) {
107
- const rawPath = str(args?.file_path ?? args?.path);
108
- const fileContent = str(args?.content);
109
- const pathDisplay = renderToolPath(rawPath, theme, cwd);
110
- let text = `${theme.fg("toolTitle", theme.bold("write"))} ${pathDisplay}`;
111
- if (fileContent === null) {
112
- text += `\n\n${theme.fg("error", "[invalid content arg - expected string]")}`;
113
- }
114
- else if (fileContent) {
115
- const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
116
- const renderedLines = lang
117
- ? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))
118
- : normalizeDisplayText(fileContent).split("\n");
119
- const lines = trimTrailingEmptyLines(renderedLines);
120
- const maxLines = options.expanded ? lines.length : previewLineCount(lines.length, 10);
121
- const displayLines = lines.slice(0, maxLines);
122
- const remaining = lines.length - displayLines.length;
123
- text += `\n\n${displayLines.map((line) => (lang ? line : theme.fg("toolOutput", replaceTabs(line)))).join("\n")}`;
124
- if (remaining > 0) {
125
- text += `\n${formatHiddenLines(remaining, "more")}`;
126
- }
127
- }
128
- return text;
129
- }
130
- function formatWriteResult(result, theme) {
131
- if (!result.isError) {
132
- return undefined;
133
- }
134
- const output = result.content
135
- .filter((c) => c.type === "text")
136
- .map((c) => c.text || "")
137
- .join("\n");
138
- if (!output) {
139
- return undefined;
140
- }
141
- return `\n${theme.fg("error", output)}`;
142
- }
143
29
  const WRITE_PREVIEW_MAX_BYTES = 512 * 1024;
144
30
  /** A trailing newline terminates the last line rather than starting an empty one. */
145
31
  const countLines = (text) => {
@@ -220,11 +106,11 @@ export function createWriteToolDefinition(cwd, options) {
220
106
  },
221
107
  },
222
108
  },
223
- constrainedSampling: getExperimentalToolSampling(),
224
- async execute(_toolCallId, input, signal, _onUpdate, _ctx) {
109
+ constrainedSampling: getExperimentalToolSampling() ?? { type: "json_schema", strict: "prefer" },
110
+ async execute(_toolCallId, input, signal, _onUpdate, ctx) {
225
111
  const { path, content } = input;
226
112
  const prepared = getPreparedPathOperation(input);
227
- const absolutePath = prepared?.path.value ?? resolveToCwd(path, cwd);
113
+ const absolutePath = prepared?.path.value ?? resolveToCwd(path, ctx?.cwd || cwd);
228
114
  if (prepared && !ops.writePrepared) {
229
115
  throw new Error("No safe prepared write operation is available for this gated write");
230
116
  }
@@ -252,7 +138,7 @@ export function createWriteToolDefinition(cwd, options) {
252
138
  await ops.writeFile(absolutePath, content);
253
139
  throwIfAborted();
254
140
  }
255
- const successText = `Successfully wrote ${content.length} bytes to ${path}`;
141
+ const successText = `Successfully wrote to ${path}`;
256
142
  if (diagnosticsOperations) {
257
143
  const diagnostics = await diagnosticsOperations.afterMutation(absolutePath, signal);
258
144
  return {
@@ -266,33 +152,7 @@ export function createWriteToolDefinition(cwd, options) {
266
152
  };
267
153
  });
268
154
  },
269
- renderCall(args, theme, context) {
270
- const renderArgs = args;
271
- const rawPath = str(renderArgs?.file_path ?? renderArgs?.path);
272
- const fileContent = str(renderArgs?.content);
273
- const component = context.lastComponent ?? new WriteCallRenderComponent();
274
- if (fileContent !== null) {
275
- component.cache = context.argsComplete
276
- ? rebuildWriteHighlightCacheFull(rawPath, fileContent)
277
- : updateWriteHighlightCacheIncremental(component.cache, rawPath, fileContent);
278
- }
279
- else {
280
- component.cache = undefined;
281
- }
282
- component.setText(formatWriteCall(renderArgs, { expanded: context.expanded, isPartial: context.isPartial }, theme, component.cache, context.cwd));
283
- return component;
284
- },
285
- renderResult(result, _options, theme, context) {
286
- const output = formatWriteResult({ ...result, isError: context.isError }, theme);
287
- if (!output) {
288
- const component = context.lastComponent ?? new Container();
289
- component.clear();
290
- return component;
291
- }
292
- const text = context.lastComponent ?? new Text("", 0, 0);
293
- text.setText(output);
294
- return text;
295
- },
155
+ ...writeRenderers,
296
156
  };
297
157
  }
298
158
  export function createWriteTool(cwd, options) {
@@ -1 +1 @@
1
- {"version":3,"file":"write.js","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { Container, Text } from \"@earendil-works/pi-tui\";\nimport type { AgentTool } from \"apex-code-agent-core\";\nimport { mkdir as fsMkdir } from \"fs/promises\";\nimport { dirname } from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { formatHiddenLines, previewLineCount } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { getExperimentalToolSampling } from \"../experimental.ts\";\nimport type { ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { getPreparedPathOperation, type PreparedPathOperation } from \"../permissions/operations.ts\";\nimport type { PermissionPreview } from \"../permissions/responder.ts\";\nimport type { ApexToolDefinition, EvidenceRecord } from \"./contract.ts\";\nimport type { DiagnosticsOperations, DiagnosticsOutcome } from \"./diagnostics.ts\";\nimport { diagnosticEvidenceForPath, formatDiagnosticsOutcome } from \"./diagnostics.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport { createPathPermissionSpec } from \"./path-permission.ts\";\nimport { readPreparedPath, resolveToCwd, writePathAtomically, writePreparedPath } from \"./path-utils.ts\";\nimport { normalizeDisplayText, renderToolPath, replaceTabs, str } from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst writeSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to write (relative or absolute)\" }),\n\tcontent: Type.String({ description: \"Content to write to the file\" }),\n});\n\nexport const writeToolSystemPromptContribution = {\n\tsnippet: \"Create or overwrite files\",\n\tguidelines: [\"Use write only for new files or complete rewrites.\"],\n} as const;\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\nexport interface WriteToolDetails {\n\tbyteCount: number;\n\tcontentHash: string;\n\t/** Present only when `WriteToolOptions.diagnosticsOperations` is configured (LSP.4). */\n\tdiagnostics?: DiagnosticsOutcome;\n}\n\nfunction sha256(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\n/**\n * Pluggable operations for the write tool.\n * Override these to delegate file writing to remote systems (for example SSH).\n */\nexport interface WriteOperations {\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Create directory recursively */\n\tmkdir: (dir: string) => Promise<void>;\n\t/**\n\t * Execute a gate-prepared write no-follow against the authorized target.\n\t * Required for gated calls: when a prepared operation exists, pathname writes\n\t * are never used, so an unset implementation fails closed.\n\t */\n\twritePrepared?: (operation: PreparedPathOperation, content: string) => void;\n}\n\nconst defaultWriteOperations: WriteOperations = {\n\twriteFile: async (path, content) => writePathAtomically(path, content),\n\tmkdir: (dir) => fsMkdir(dir, { recursive: true }).then(() => {}),\n\twritePrepared: (operation, content) => writePreparedPath(operation, content),\n};\n\nexport interface WriteToolOptions {\n\t/** Custom operations for file writing. Default: local filesystem */\n\toperations?: WriteOperations;\n\t/**\n\t * Optional post-mutation diagnostics (LSP.4). Absent by default -- byte-identical\n\t * existing behavior. When present, called inside `withFileMutationQueue` after the\n\t * write succeeds and before the queue releases, matching `TestOperations`/\n\t * `GrepOperations`' injection pattern (`test.ts:28`, `grep.ts:58`).\n\t */\n\tdiagnosticsOperations?: DiagnosticsOperations;\n}\n\ntype WriteHighlightCache = {\n\trawPath: string | null;\n\tlang: string;\n\trawContent: string;\n\tnormalizedLines: string[];\n\thighlightedLines: string[];\n};\n\nclass WriteCallRenderComponent extends Text {\n\tcache?: WriteHighlightCache;\n\n\tconstructor() {\n\t\tsuper(\"\", 0, 0);\n\t}\n}\n\nconst WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;\n\nfunction highlightSingleLine(line: string, lang: string): string {\n\tconst highlighted = highlightCode(line, lang);\n\treturn highlighted[0] ?? \"\";\n}\n\nfunction refreshWriteHighlightPrefix(cache: WriteHighlightCache): void {\n\tconst prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);\n\tif (prefixCount === 0) return;\n\tconst prefixSource = cache.normalizedLines.slice(0, prefixCount).join(\"\\n\");\n\tconst prefixHighlighted = highlightCode(prefixSource, cache.lang);\n\tfor (let i = 0; i < prefixCount; i++) {\n\t\tcache.highlightedLines[i] =\n\t\t\tprefixHighlighted[i] ?? highlightSingleLine(cache.normalizedLines[i] ?? \"\", cache.lang);\n\t}\n}\n\nfunction rebuildWriteHighlightCacheFull(rawPath: string | null, fileContent: string): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tconst displayContent = normalizeDisplayText(fileContent);\n\tconst normalized = replaceTabs(displayContent);\n\treturn {\n\t\trawPath,\n\t\tlang,\n\t\trawContent: fileContent,\n\t\tnormalizedLines: normalized.split(\"\\n\"),\n\t\thighlightedLines: highlightCode(normalized, lang),\n\t};\n}\n\nfunction updateWriteHighlightCacheIncremental(\n\tcache: WriteHighlightCache | undefined,\n\trawPath: string | null,\n\tfileContent: string,\n): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tif (!cache) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (cache.lang !== lang || cache.rawPath !== rawPath) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (!fileContent.startsWith(cache.rawContent)) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (fileContent.length === cache.rawContent.length) return cache;\n\n\tconst deltaRaw = fileContent.slice(cache.rawContent.length);\n\tconst deltaDisplay = normalizeDisplayText(deltaRaw);\n\tconst deltaNormalized = replaceTabs(deltaDisplay);\n\tcache.rawContent = fileContent;\n\tif (cache.normalizedLines.length === 0) {\n\t\tcache.normalizedLines.push(\"\");\n\t\tcache.highlightedLines.push(\"\");\n\t}\n\n\tconst segments = deltaNormalized.split(\"\\n\");\n\tconst lastIndex = cache.normalizedLines.length - 1;\n\tcache.normalizedLines[lastIndex] += segments[0];\n\tcache.highlightedLines[lastIndex] = highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tcache.normalizedLines.push(segments[i]);\n\t\tcache.highlightedLines.push(highlightSingleLine(segments[i], cache.lang));\n\t}\n\trefreshWriteHighlightPrefix(cache);\n\treturn cache;\n}\n\nfunction trimTrailingEmptyLines(lines: string[]): string[] {\n\tlet end = lines.length;\n\twhile (end > 0 && lines[end - 1] === \"\") {\n\t\tend--;\n\t}\n\treturn lines.slice(0, end);\n}\n\nfunction formatWriteCall(\n\targs: { path?: string; file_path?: string; content?: string } | undefined,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n\tcwd: string,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst fileContent = str(args?.content);\n\tconst pathDisplay = renderToolPath(rawPath, theme, cwd);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"write\"))} ${pathDisplay}`;\n\n\tif (fileContent === null) {\n\t\ttext += `\\n\\n${theme.fg(\"error\", \"[invalid content arg - expected string]\")}`;\n\t} else if (fileContent) {\n\t\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\t\tconst renderedLines = lang\n\t\t\t? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))\n\t\t\t: normalizeDisplayText(fileContent).split(\"\\n\");\n\t\tconst lines = trimTrailingEmptyLines(renderedLines);\n\t\tconst maxLines = options.expanded ? lines.length : previewLineCount(lines.length, 10);\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - displayLines.length;\n\t\ttext += `\\n\\n${displayLines.map((line) => (lang ? line : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `\\n${formatHiddenLines(remaining, \"more\")}`;\n\t\t}\n\t}\n\n\treturn text;\n}\n\nfunction formatWriteResult(\n\tresult: { content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>; isError?: boolean },\n\ttheme: Theme,\n): string | undefined {\n\tif (!result.isError) {\n\t\treturn undefined;\n\t}\n\tconst output = result.content\n\t\t.filter((c) => c.type === \"text\")\n\t\t.map((c) => c.text || \"\")\n\t\t.join(\"\\n\");\n\tif (!output) {\n\t\treturn undefined;\n\t}\n\treturn `\\n${theme.fg(\"error\", output)}`;\n}\n\nconst WRITE_PREVIEW_MAX_BYTES = 512 * 1024;\n\n/** A trailing newline terminates the last line rather than starting an empty one. */\nconst countLines = (text: string): number => {\n\tif (text === \"\") return 0;\n\tconst body = text.endsWith(\"\\n\") ? text.slice(0, -1) : text;\n\treturn body.split(\"\\n\").length;\n};\n\nconst lineCount = (text: string): string => {\n\tconst n = countLines(text);\n\treturn `${n} ${n === 1 ? \"line\" : \"lines\"}`;\n};\n\n/**\n * Describe a whole-file replacement without drawing the whole file.\n *\n * A diff of a write is the file twice over, which is not what a person needs in\n * order to decide. Sizes on both sides answer the question that matters, which is\n * whether this replaces something substantial. Read through the prepared\n * operation, so a swapped target fails here as it fails the write (ADR 0029).\n */\nfunction previewWrite(params: { path: string; content: string }): PermissionPreview {\n\tconst incoming = `${lineCount(params.content)}, ${Buffer.byteLength(params.content)} bytes`;\n\tconst prepared = getPreparedPathOperation(params);\n\tif (!prepared) return { kind: \"unavailable\", reason: \"No authorized target was prepared for this call\" };\n\tif (prepared.kind === \"path-new\") {\n\t\treturn { kind: \"summary\", lines: [`Create ${params.path} with ${incoming}`] };\n\t}\n\n\tconst buffer = readPreparedPath(prepared, WRITE_PREVIEW_MAX_BYTES + 1);\n\tif (buffer.byteLength > WRITE_PREVIEW_MAX_BYTES) {\n\t\treturn {\n\t\t\tkind: \"summary\",\n\t\t\tlines: [`Replace ${params.path}, over ${WRITE_PREVIEW_MAX_BYTES / 1024}KB, with ${incoming}`],\n\t\t};\n\t}\n\tconst existing = buffer.includes(0)\n\t\t? `${buffer.byteLength} bytes of binary`\n\t\t: `${lineCount(buffer.toString(\"utf-8\"))}, ${buffer.byteLength} bytes`;\n\treturn { kind: \"summary\", lines: [`Replace ${params.path}`, `${existing} becomes ${incoming}`] };\n}\n\nexport function createWriteToolDefinition(\n\tcwd: string,\n\toptions?: WriteToolOptions,\n): ApexToolDefinition<typeof writeSchema, WriteToolDetails> {\n\tconst ops = options?.operations ?? defaultWriteOperations;\n\tconst diagnosticsOperations = options?.diagnosticsOperations;\n\treturn {\n\t\tname: \"write\",\n\t\tlabel: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tpromptSnippet: writeToolSystemPromptContribution.snippet,\n\t\tpromptGuidelines: [...writeToolSystemPromptContribution.guidelines],\n\t\tparameters: writeSchema,\n\t\tcontract: {\n\t\t\tcapabilities: new Set([\"fs.write\"]),\n\t\t\tpermission: createPathPermissionSpec({\n\t\t\t\tcwd,\n\t\t\t\tdefaultBehavior: \"ask\",\n\t\t\t\tverb: \"Write\",\n\t\t\t\tgetPath: (params) => params.path,\n\t\t\t\tpreviewCall: (params) => previewWrite(params as { path: string; content: string }),\n\t\t\t}),\n\t\t\tcontext: { resultRecoverable: true, deferSchema: false },\n\t\t\tevidence: {\n\t\t\t\temits: new Set(diagnosticsOperations ? [\"diff\", \"diagnostic\"] : [\"diff\"]),\n\t\t\t\tcapture: (params, result) => {\n\t\t\t\t\tconst details = result.details;\n\t\t\t\t\tconst records: EvidenceRecord[] = details\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tkind: \"diff\",\n\t\t\t\t\t\t\t\t\tpath: params.path,\n\t\t\t\t\t\t\t\t\tbyteCount: details.byteCount,\n\t\t\t\t\t\t\t\t\tcontentHash: details.contentHash,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: [{ kind: \"diff\", path: params.path }];\n\t\t\t\t\tif (details?.diagnostics) records.push(diagnosticEvidenceForPath(params.path, details.diagnostics));\n\t\t\t\t\treturn records;\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tconstrainedSampling: getExperimentalToolSampling(),\n\t\tasync execute(_toolCallId, input: { path: string; content: string }, signal?: AbortSignal, _onUpdate?, _ctx?) {\n\t\t\tconst { path, content } = input;\n\t\t\tconst prepared = getPreparedPathOperation(input);\n\t\t\tconst absolutePath = prepared?.path.value ?? resolveToCwd(path, cwd);\n\t\t\tif (prepared && !ops.writePrepared) {\n\t\t\t\tthrow new Error(\"No safe prepared write operation is available for this gated write\");\n\t\t\t}\n\t\t\tconst dir = dirname(absolutePath);\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\t// Do not reject from an abort event listener here: that would release the\n\t\t\t\t// mutation queue while an in-flight filesystem operation may still finish.\n\t\t\t\t// Checking signal.aborted after each await observes the same aborts while\n\t\t\t\t// keeping the queue locked until the current operation has settled.\n\t\t\t\tconst throwIfAborted = (): void => {\n\t\t\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\t\t};\n\n\t\t\t\tthrowIfAborted();\n\t\t\t\tif (prepared) {\n\t\t\t\t\t// Gated path: no-follow execution against the exact authorized target.\n\t\t\t\t\tops.writePrepared!(prepared, content);\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t} else {\n\t\t\t\t\t// Create parent directories if needed.\n\t\t\t\t\tawait ops.mkdir(dir);\n\t\t\t\t\tthrowIfAborted();\n\n\t\t\t\t\t// Write the file contents.\n\t\t\t\t\tawait ops.writeFile(absolutePath, content);\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t}\n\n\t\t\t\tconst successText = `Successfully wrote ${content.length} bytes to ${path}`;\n\n\t\t\t\tif (diagnosticsOperations) {\n\t\t\t\t\tconst diagnostics = await diagnosticsOperations.afterMutation(absolutePath, signal);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `${successText}\\n\\n${formatDiagnosticsOutcome(diagnostics)}` }],\n\t\t\t\t\t\tdetails: { byteCount: Buffer.byteLength(content), contentHash: sha256(content), diagnostics },\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: successText }],\n\t\t\t\t\tdetails: { byteCount: Buffer.byteLength(content), contentHash: sha256(content) },\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst renderArgs = args as { path?: string; file_path?: string; content?: string } | undefined;\n\t\t\tconst rawPath = str(renderArgs?.file_path ?? renderArgs?.path);\n\t\t\tconst fileContent = str(renderArgs?.content);\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as WriteCallRenderComponent | undefined) ?? new WriteCallRenderComponent();\n\t\t\tif (fileContent !== null) {\n\t\t\t\tcomponent.cache = context.argsComplete\n\t\t\t\t\t? rebuildWriteHighlightCacheFull(rawPath, fileContent)\n\t\t\t\t\t: updateWriteHighlightCacheIncremental(component.cache, rawPath, fileContent);\n\t\t\t} else {\n\t\t\t\tcomponent.cache = undefined;\n\t\t\t}\n\t\t\tcomponent.setText(\n\t\t\t\tformatWriteCall(\n\t\t\t\t\trenderArgs,\n\t\t\t\t\t{ expanded: context.expanded, isPartial: context.isPartial },\n\t\t\t\t\ttheme,\n\t\t\t\t\tcomponent.cache,\n\t\t\t\t\tcontext.cwd,\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn component;\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst output = formatWriteResult({ ...result, isError: context.isError }, theme);\n\t\t\tif (!output) {\n\t\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\t\tcomponent.clear();\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(output);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createWriteTool(cwd: string, options?: WriteToolOptions): AgentTool<typeof writeSchema> {\n\treturn wrapToolDefinition(createWriteToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { AgentTool } from \"apex-code-agent-core\";\nimport { mkdir as fsMkdir } from \"fs/promises\";\nimport { dirname } from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { getExperimentalToolSampling } from \"../experimental.ts\";\nimport type { ExtensionContext } from \"../extensions/types.ts\";\nimport { getPreparedPathOperation, type PreparedPathOperation } from \"../permissions/operations.ts\";\nimport type { PermissionPreview } from \"../permissions/responder.ts\";\nimport type { ApexToolDefinition, EvidenceRecord } from \"./contract.ts\";\nimport type { DiagnosticsOperations, DiagnosticsOutcome } from \"./diagnostics.ts\";\nimport { diagnosticEvidenceForPath, formatDiagnosticsOutcome } from \"./diagnostics.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport { createPathPermissionSpec } from \"./path-permission.ts\";\nimport { readPreparedPath, resolveToCwd, writePathAtomically, writePreparedPath } from \"./path-utils.ts\";\nimport { writeRenderers } from \"./renderers/write.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst writeSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to write (relative or absolute)\" }),\n\tcontent: Type.String({ description: \"Content to write to the file\" }),\n});\n\nexport const writeToolSystemPromptContribution = {\n\tsnippet: \"Create or overwrite files\",\n\tguidelines: [\"Use write only for new files or complete rewrites.\"],\n} as const;\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\nexport interface WriteToolDetails {\n\tbyteCount: number;\n\tcontentHash: string;\n\t/** Present only when `WriteToolOptions.diagnosticsOperations` is configured (LSP.4). */\n\tdiagnostics?: DiagnosticsOutcome;\n}\n\nfunction sha256(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\n/**\n * Pluggable operations for the write tool.\n * Override these to delegate file writing to remote systems (for example SSH).\n */\nexport interface WriteOperations {\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Create directory recursively */\n\tmkdir: (dir: string) => Promise<void>;\n\t/**\n\t * Execute a gate-prepared write no-follow against the authorized target.\n\t * Required for gated calls: when a prepared operation exists, pathname writes\n\t * are never used, so an unset implementation fails closed.\n\t */\n\twritePrepared?: (operation: PreparedPathOperation, content: string) => void;\n}\n\nconst defaultWriteOperations: WriteOperations = {\n\twriteFile: async (path, content) => writePathAtomically(path, content),\n\tmkdir: (dir) => fsMkdir(dir, { recursive: true }).then(() => {}),\n\twritePrepared: (operation, content) => writePreparedPath(operation, content),\n};\n\nexport interface WriteToolOptions {\n\t/** Custom operations for file writing. Default: local filesystem */\n\toperations?: WriteOperations;\n\t/**\n\t * Optional post-mutation diagnostics (LSP.4). Absent by default -- byte-identical\n\t * existing behavior. When present, called inside `withFileMutationQueue` after the\n\t * write succeeds and before the queue releases, matching `TestOperations`/\n\t * `GrepOperations`' injection pattern (`test.ts:28`, `grep.ts:58`).\n\t */\n\tdiagnosticsOperations?: DiagnosticsOperations;\n}\n\nconst WRITE_PREVIEW_MAX_BYTES = 512 * 1024;\n\n/** A trailing newline terminates the last line rather than starting an empty one. */\nconst countLines = (text: string): number => {\n\tif (text === \"\") return 0;\n\tconst body = text.endsWith(\"\\n\") ? text.slice(0, -1) : text;\n\treturn body.split(\"\\n\").length;\n};\n\nconst lineCount = (text: string): string => {\n\tconst n = countLines(text);\n\treturn `${n} ${n === 1 ? \"line\" : \"lines\"}`;\n};\n\n/**\n * Describe a whole-file replacement without drawing the whole file.\n *\n * A diff of a write is the file twice over, which is not what a person needs in\n * order to decide. Sizes on both sides answer the question that matters, which is\n * whether this replaces something substantial. Read through the prepared\n * operation, so a swapped target fails here as it fails the write (ADR 0029).\n */\nfunction previewWrite(params: { path: string; content: string }): PermissionPreview {\n\tconst incoming = `${lineCount(params.content)}, ${Buffer.byteLength(params.content)} bytes`;\n\tconst prepared = getPreparedPathOperation(params);\n\tif (!prepared) return { kind: \"unavailable\", reason: \"No authorized target was prepared for this call\" };\n\tif (prepared.kind === \"path-new\") {\n\t\treturn { kind: \"summary\", lines: [`Create ${params.path} with ${incoming}`] };\n\t}\n\n\tconst buffer = readPreparedPath(prepared, WRITE_PREVIEW_MAX_BYTES + 1);\n\tif (buffer.byteLength > WRITE_PREVIEW_MAX_BYTES) {\n\t\treturn {\n\t\t\tkind: \"summary\",\n\t\t\tlines: [`Replace ${params.path}, over ${WRITE_PREVIEW_MAX_BYTES / 1024}KB, with ${incoming}`],\n\t\t};\n\t}\n\tconst existing = buffer.includes(0)\n\t\t? `${buffer.byteLength} bytes of binary`\n\t\t: `${lineCount(buffer.toString(\"utf-8\"))}, ${buffer.byteLength} bytes`;\n\treturn { kind: \"summary\", lines: [`Replace ${params.path}`, `${existing} becomes ${incoming}`] };\n}\n\nexport function createWriteToolDefinition(\n\tcwd: string,\n\toptions?: WriteToolOptions,\n): ApexToolDefinition<typeof writeSchema, WriteToolDetails> {\n\tconst ops = options?.operations ?? defaultWriteOperations;\n\tconst diagnosticsOperations = options?.diagnosticsOperations;\n\treturn {\n\t\tname: \"write\",\n\t\tlabel: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tpromptSnippet: writeToolSystemPromptContribution.snippet,\n\t\tpromptGuidelines: [...writeToolSystemPromptContribution.guidelines],\n\t\tparameters: writeSchema,\n\t\tcontract: {\n\t\t\tcapabilities: new Set([\"fs.write\"]),\n\t\t\tpermission: createPathPermissionSpec({\n\t\t\t\tcwd,\n\t\t\t\tdefaultBehavior: \"ask\",\n\t\t\t\tverb: \"Write\",\n\t\t\t\tgetPath: (params) => params.path,\n\t\t\t\tpreviewCall: (params) => previewWrite(params as { path: string; content: string }),\n\t\t\t}),\n\t\t\tcontext: { resultRecoverable: true, deferSchema: false },\n\t\t\tevidence: {\n\t\t\t\temits: new Set(diagnosticsOperations ? [\"diff\", \"diagnostic\"] : [\"diff\"]),\n\t\t\t\tcapture: (params, result) => {\n\t\t\t\t\tconst details = result.details;\n\t\t\t\t\tconst records: EvidenceRecord[] = details\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tkind: \"diff\",\n\t\t\t\t\t\t\t\t\tpath: params.path,\n\t\t\t\t\t\t\t\t\tbyteCount: details.byteCount,\n\t\t\t\t\t\t\t\t\tcontentHash: details.contentHash,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: [{ kind: \"diff\", path: params.path }];\n\t\t\t\t\tif (details?.diagnostics) records.push(diagnosticEvidenceForPath(params.path, details.diagnostics));\n\t\t\t\t\treturn records;\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tconstrainedSampling: getExperimentalToolSampling() ?? { type: \"json_schema\", strict: \"prefer\" },\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\tinput: { path: string; content: string },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\tctx?: ExtensionContext,\n\t\t) {\n\t\t\tconst { path, content } = input;\n\t\t\tconst prepared = getPreparedPathOperation(input);\n\t\t\tconst absolutePath = prepared?.path.value ?? resolveToCwd(path, ctx?.cwd || cwd);\n\t\t\tif (prepared && !ops.writePrepared) {\n\t\t\t\tthrow new Error(\"No safe prepared write operation is available for this gated write\");\n\t\t\t}\n\t\t\tconst dir = dirname(absolutePath);\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\t// Do not reject from an abort event listener here: that would release the\n\t\t\t\t// mutation queue while an in-flight filesystem operation may still finish.\n\t\t\t\t// Checking signal.aborted after each await observes the same aborts while\n\t\t\t\t// keeping the queue locked until the current operation has settled.\n\t\t\t\tconst throwIfAborted = (): void => {\n\t\t\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\t\t};\n\n\t\t\t\tthrowIfAborted();\n\t\t\t\tif (prepared) {\n\t\t\t\t\t// Gated path: no-follow execution against the exact authorized target.\n\t\t\t\t\tops.writePrepared!(prepared, content);\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t} else {\n\t\t\t\t\t// Create parent directories if needed.\n\t\t\t\t\tawait ops.mkdir(dir);\n\t\t\t\t\tthrowIfAborted();\n\n\t\t\t\t\t// Write the file contents.\n\t\t\t\t\tawait ops.writeFile(absolutePath, content);\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t}\n\n\t\t\t\tconst successText = `Successfully wrote to ${path}`;\n\n\t\t\t\tif (diagnosticsOperations) {\n\t\t\t\t\tconst diagnostics = await diagnosticsOperations.afterMutation(absolutePath, signal);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `${successText}\\n\\n${formatDiagnosticsOutcome(diagnostics)}` }],\n\t\t\t\t\t\tdetails: { byteCount: Buffer.byteLength(content), contentHash: sha256(content), diagnostics },\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: successText }],\n\t\t\t\t\tdetails: { byteCount: Buffer.byteLength(content), contentHash: sha256(content) },\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t\t...writeRenderers,\n\t};\n}\n\nexport function createWriteTool(cwd: string, options?: WriteToolOptions): AgentTool<typeof writeSchema> {\n\treturn wrapToolDefinition(createWriteToolDefinition(cwd, options));\n}\n"]}
@@ -14,6 +14,6 @@ export interface UsageCostBreakdownEntry {
14
14
  cost: number;
15
15
  tokens: number;
16
16
  }
17
- /** Group attributable assistant usage by model and all other usage into a separate bucket. */
17
+ /** Group model-attributed usage by model and all other usage into a separate bucket. */
18
18
  export declare function getUsageCostBreakdown(entries: SessionEntry[]): UsageCostBreakdownEntry[];
19
19
  //# sourceMappingURL=usage-totals.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"usage-totals.d.ts","sourceRoot":"","sources":["../../src/core/usage-totals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAQ/C;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAMxE;AAED,MAAM,WAAW,uBAAuB;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CACf;AAED,8FAA8F;AAC9F,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,uBAAuB,EAAE,CAiCxF","sourcesContent":["import type { Usage } from \"@earendil-works/pi-ai/compat\";\nimport type { SessionEntry } from \"./session-manager.ts\";\n\nexport interface UsageTotals {\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\tcost: number;\n}\n\nexport function createUsageTotals(): UsageTotals {\n\treturn {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheRead: 0,\n\t\tcacheWrite: 0,\n\t\tcost: 0,\n\t};\n}\n\nexport function addUsageToTotals(totals: UsageTotals, usage: Usage): void {\n\ttotals.input += usage.input;\n\ttotals.output += usage.output;\n\ttotals.cacheRead += usage.cacheRead;\n\ttotals.cacheWrite += usage.cacheWrite;\n\ttotals.cost += usage.cost.total;\n}\n\nexport interface UsageCostBreakdownEntry {\n\tkey: string;\n\tcost: number;\n\ttokens: number;\n}\n\n/** Group attributable assistant usage by model and all other usage into a separate bucket. */\nexport function getUsageCostBreakdown(entries: SessionEntry[]): UsageCostBreakdownEntry[] {\n\tconst totalsByKey = new Map<string, UsageTotals>();\n\n\tfor (const entry of entries) {\n\t\tlet key: string | undefined;\n\t\tlet usage: Usage | undefined;\n\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tkey = `${entry.message.provider}/${entry.message.responseModel ?? entry.message.model}`;\n\t\t\tusage = entry.message.usage;\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"toolResult\" && entry.message.usage) {\n\t\t\tkey = \"Tools/summaries\";\n\t\t\tusage = entry.message.usage;\n\t\t} else if ((entry.type === \"branch_summary\" || entry.type === \"compaction\") && entry.usage) {\n\t\t\tkey = \"Tools/summaries\";\n\t\t\tusage = entry.usage;\n\t\t}\n\t\tif (!key || !usage) continue;\n\n\t\tlet totals = totalsByKey.get(key);\n\t\tif (!totals) {\n\t\t\ttotals = createUsageTotals();\n\t\t\ttotalsByKey.set(key, totals);\n\t\t}\n\t\taddUsageToTotals(totals, usage);\n\t}\n\n\treturn Array.from(totalsByKey, ([key, totals]) => ({\n\t\tkey,\n\t\tcost: totals.cost,\n\t\ttokens: totals.input + totals.output + totals.cacheRead + totals.cacheWrite,\n\t}))\n\t\t.filter((entry) => entry.cost > 0 || entry.tokens > 0)\n\t\t.sort((a, b) => b.cost - a.cost);\n}\n"]}
1
+ {"version":3,"file":"usage-totals.d.ts","sourceRoot":"","sources":["../../src/core/usage-totals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAQ/C;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAMxE;AAED,MAAM,WAAW,uBAAuB;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CACf;AAED,wFAAwF;AACxF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,uBAAuB,EAAE,CAoCxF","sourcesContent":["import type { Usage } from \"@earendil-works/pi-ai/compat\";\nimport type { SessionEntry } from \"./session-manager.ts\";\n\nexport interface UsageTotals {\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite: number;\n\tcost: number;\n}\n\nexport function createUsageTotals(): UsageTotals {\n\treturn {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheRead: 0,\n\t\tcacheWrite: 0,\n\t\tcost: 0,\n\t};\n}\n\nexport function addUsageToTotals(totals: UsageTotals, usage: Usage): void {\n\ttotals.input += usage.input;\n\ttotals.output += usage.output;\n\ttotals.cacheRead += usage.cacheRead;\n\ttotals.cacheWrite += usage.cacheWrite;\n\ttotals.cost += usage.cost.total;\n}\n\nexport interface UsageCostBreakdownEntry {\n\tkey: string;\n\tcost: number;\n\ttokens: number;\n}\n\n/** Group model-attributed usage by model and all other usage into a separate bucket. */\nexport function getUsageCostBreakdown(entries: SessionEntry[]): UsageCostBreakdownEntry[] {\n\tconst totalsByKey = new Map<string, UsageTotals>();\n\n\tfor (const entry of entries) {\n\t\tlet key: string | undefined;\n\t\tlet usage: Usage | undefined;\n\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tkey = `${entry.message.provider}/${entry.message.responseModel ?? entry.message.model}`;\n\t\t\tusage = entry.message.usage;\n\t\t} else if (entry.type === \"usage\") {\n\t\t\tkey = `${entry.provider}/${entry.model}`;\n\t\t\tusage = entry.usage;\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"toolResult\" && entry.message.usage) {\n\t\t\tkey = \"Tools/summaries\";\n\t\t\tusage = entry.message.usage;\n\t\t} else if ((entry.type === \"branch_summary\" || entry.type === \"compaction\") && entry.usage) {\n\t\t\tkey = \"Tools/summaries\";\n\t\t\tusage = entry.usage;\n\t\t}\n\t\tif (!key || !usage) continue;\n\n\t\tlet totals = totalsByKey.get(key);\n\t\tif (!totals) {\n\t\t\ttotals = createUsageTotals();\n\t\t\ttotalsByKey.set(key, totals);\n\t\t}\n\t\taddUsageToTotals(totals, usage);\n\t}\n\n\treturn Array.from(totalsByKey, ([key, totals]) => ({\n\t\tkey,\n\t\tcost: totals.cost,\n\t\ttokens: totals.input + totals.output + totals.cacheRead + totals.cacheWrite,\n\t}))\n\t\t.filter((entry) => entry.cost > 0 || entry.tokens > 0)\n\t\t.sort((a, b) => b.cost - a.cost);\n}\n"]}
@@ -14,7 +14,7 @@ export function addUsageToTotals(totals, usage) {
14
14
  totals.cacheWrite += usage.cacheWrite;
15
15
  totals.cost += usage.cost.total;
16
16
  }
17
- /** Group attributable assistant usage by model and all other usage into a separate bucket. */
17
+ /** Group model-attributed usage by model and all other usage into a separate bucket. */
18
18
  export function getUsageCostBreakdown(entries) {
19
19
  const totalsByKey = new Map();
20
20
  for (const entry of entries) {
@@ -24,6 +24,10 @@ export function getUsageCostBreakdown(entries) {
24
24
  key = `${entry.message.provider}/${entry.message.responseModel ?? entry.message.model}`;
25
25
  usage = entry.message.usage;
26
26
  }
27
+ else if (entry.type === "usage") {
28
+ key = `${entry.provider}/${entry.model}`;
29
+ usage = entry.usage;
30
+ }
27
31
  else if (entry.type === "message" && entry.message.role === "toolResult" && entry.message.usage) {
28
32
  key = "Tools/summaries";
29
33
  usage = entry.message.usage;