@oh-my-pi/pi-coding-agent 6.9.69 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (502) hide show
  1. package/CHANGELOG.md +219 -51
  2. package/README.md +1 -1
  3. package/docs/hooks.md +2 -2
  4. package/docs/sdk.md +1 -1
  5. package/examples/sdk/04-skills.ts +1 -1
  6. package/package.json +10 -10
  7. package/scripts/format-prompts.ts +143 -0
  8. package/scripts/generate-template.ts +1 -1
  9. package/src/cli/args.ts +3 -3
  10. package/src/cli/config-cli.ts +4 -4
  11. package/src/cli/file-processor.ts +3 -3
  12. package/src/cli/list-models.ts +2 -2
  13. package/src/cli/plugin-cli.ts +3 -3
  14. package/src/cli/session-picker.ts +2 -2
  15. package/src/cli/setup-cli.ts +2 -2
  16. package/src/cli/stats-cli.ts +1 -1
  17. package/src/cli/update-cli.ts +2 -2
  18. package/src/{core → config}/keybindings.ts +1 -1
  19. package/src/{core → config}/model-registry.ts +8 -1
  20. package/src/{core → config}/model-resolver.ts +3 -3
  21. package/src/{core → config}/prompt-templates.ts +2 -2
  22. package/src/{core → config}/settings-manager.ts +6 -6
  23. package/src/{core/cursor/exec-bridge.ts → cursor.ts} +4 -4
  24. package/src/discovery/agents-md.ts +4 -4
  25. package/src/discovery/builtin.ts +17 -17
  26. package/src/discovery/claude.ts +12 -12
  27. package/src/discovery/cline.ts +6 -6
  28. package/src/discovery/codex.ts +21 -21
  29. package/src/discovery/cursor.ts +9 -9
  30. package/src/discovery/gemini.ts +9 -9
  31. package/src/discovery/github.ts +6 -6
  32. package/src/discovery/helpers.ts +4 -4
  33. package/src/discovery/index.ts +16 -16
  34. package/src/discovery/mcp-json.ts +4 -4
  35. package/src/discovery/ssh.ts +4 -4
  36. package/src/discovery/vscode.ts +4 -4
  37. package/src/discovery/windsurf.ts +6 -6
  38. package/src/{core/tools/exa → exa}/company.ts +2 -3
  39. package/src/{core/tools/exa → exa}/index.ts +2 -2
  40. package/src/{core/tools/exa → exa}/linkedin.ts +2 -3
  41. package/src/{core/tools/exa → exa}/mcp-client.ts +2 -2
  42. package/src/{core/tools/exa → exa}/render.ts +3 -3
  43. package/src/{core/tools/exa → exa}/researcher.ts +1 -1
  44. package/src/{core/tools/exa → exa}/search.ts +2 -10
  45. package/src/{core/tools/exa → exa}/websets.ts +1 -1
  46. package/src/{core → exec}/bash-executor.ts +23 -7
  47. package/src/{core → export}/custom-share.ts +1 -1
  48. package/src/{core/export-html → export/html}/index.ts +3 -3
  49. package/src/{core → export}/ttsr.ts +2 -2
  50. package/src/{core → extensibility}/custom-commands/bundled/review/index.ts +4 -4
  51. package/src/{core → extensibility}/custom-commands/loader.ts +3 -3
  52. package/src/{core → extensibility}/custom-commands/types.ts +1 -1
  53. package/src/{core → extensibility}/custom-tools/loader.ts +9 -9
  54. package/src/{core → extensibility}/custom-tools/types.ts +6 -6
  55. package/src/{core → extensibility}/custom-tools/wrapper.ts +1 -1
  56. package/src/{core → extensibility}/extensions/loader.ts +8 -8
  57. package/src/{core → extensibility}/extensions/runner.ts +3 -3
  58. package/src/{core → extensibility}/extensions/types.ts +15 -15
  59. package/src/{core → extensibility}/extensions/wrapper.ts +1 -1
  60. package/src/{core → extensibility}/hooks/index.ts +1 -1
  61. package/src/{core → extensibility}/hooks/loader.ts +7 -7
  62. package/src/{core → extensibility}/hooks/runner.ts +4 -4
  63. package/src/{core → extensibility}/hooks/types.ts +9 -9
  64. package/src/{core → extensibility}/plugins/doctor.ts +1 -1
  65. package/src/{core → extensibility}/plugins/installer.ts +2 -3
  66. package/src/{core → extensibility}/plugins/paths.ts +1 -1
  67. package/src/{core → extensibility}/skills.ts +8 -48
  68. package/src/{core → extensibility}/slash-commands.ts +6 -6
  69. package/src/index.ts +127 -128
  70. package/src/internal-urls/agent-protocol.ts +126 -0
  71. package/src/internal-urls/artifact-protocol.ts +93 -0
  72. package/src/internal-urls/index.ts +28 -0
  73. package/src/internal-urls/json-query.ts +126 -0
  74. package/src/internal-urls/router.ts +69 -0
  75. package/src/internal-urls/rule-protocol.ts +56 -0
  76. package/src/internal-urls/skill-protocol.ts +112 -0
  77. package/src/internal-urls/types.ts +48 -0
  78. package/src/{core/python-executor.ts → ipy/executor.ts} +74 -13
  79. package/src/{core/python-gateway-coordinator.ts → ipy/gateway-coordinator.ts} +40 -270
  80. package/src/{core/python-kernel.ts → ipy/kernel.ts} +38 -10
  81. package/src/{core/python-prelude.py → ipy/prelude.py} +201 -8
  82. package/src/ipy/prelude.ts +3 -0
  83. package/src/{core/tools/lsp → lsp}/client.ts +7 -6
  84. package/src/{core/tools/lsp → lsp}/clients/biome-client.ts +1 -1
  85. package/src/{core/tools/lsp → lsp}/clients/index.ts +1 -1
  86. package/src/{core/tools/lsp → lsp}/clients/lsp-linter-client.ts +4 -4
  87. package/src/{core/tools/lsp → lsp}/config.ts +1 -1
  88. package/src/{core/tools/lsp → lsp}/index.ts +29 -17
  89. package/src/{core/tools/lsp → lsp}/render.ts +2 -2
  90. package/src/{core/tools/lsp → lsp}/types.ts +14 -16
  91. package/src/{core/tools/lsp → lsp}/utils.ts +1 -1
  92. package/src/main.ts +12 -12
  93. package/src/{core/mcp → mcp}/config.ts +8 -8
  94. package/src/{core/mcp → mcp}/loader.ts +5 -6
  95. package/src/{core/mcp → mcp}/manager.ts +2 -2
  96. package/src/{core/mcp → mcp}/tool-bridge.ts +35 -6
  97. package/src/{core/mcp → mcp}/tool-cache.ts +1 -1
  98. package/src/{core/mcp → mcp}/transports/http.ts +7 -1
  99. package/src/{core/mcp → mcp}/transports/stdio.ts +1 -1
  100. package/src/{core/mcp → mcp}/types.ts +1 -1
  101. package/src/migrations.ts +2 -2
  102. package/src/modes/{interactive/components → components}/armin.ts +1 -1
  103. package/src/modes/{interactive/components → components}/assistant-message.ts +1 -1
  104. package/src/modes/{interactive/components → components}/bash-execution.ts +37 -29
  105. package/src/modes/{interactive/components → components}/bordered-loader.ts +1 -1
  106. package/src/modes/{interactive/components → components}/branch-summary-message.ts +2 -2
  107. package/src/modes/{interactive/components → components}/compaction-summary-message.ts +2 -2
  108. package/src/modes/{interactive/components → components}/custom-message.ts +3 -3
  109. package/src/modes/{interactive/components → components}/diff.ts +1 -1
  110. package/src/modes/{interactive/components → components}/dynamic-border.ts +1 -1
  111. package/src/modes/{interactive/components → components}/extensions/extension-dashboard.ts +3 -3
  112. package/src/modes/{interactive/components → components}/extensions/extension-list.ts +2 -2
  113. package/src/modes/{interactive/components → components}/extensions/inspector-panel.ts +1 -1
  114. package/src/modes/{interactive/components → components}/extensions/state-manager.ts +11 -17
  115. package/src/modes/{interactive/components → components}/extensions/types.ts +1 -1
  116. package/src/modes/{interactive/components → components}/footer.ts +3 -3
  117. package/src/modes/{interactive/components → components}/history-search.ts +2 -2
  118. package/src/modes/{interactive/components → components}/hook-editor.ts +1 -1
  119. package/src/modes/{interactive/components → components}/hook-input.ts +1 -1
  120. package/src/modes/{interactive/components → components}/hook-message.ts +3 -3
  121. package/src/modes/{interactive/components → components}/hook-selector.ts +1 -1
  122. package/src/modes/{interactive/components → components}/keybinding-hints.ts +2 -2
  123. package/src/modes/{interactive/components → components}/login-dialog.ts +1 -1
  124. package/src/modes/{interactive/components → components}/model-selector.ts +5 -5
  125. package/src/modes/{interactive/components → components}/oauth-selector.ts +2 -2
  126. package/src/modes/{interactive/components → components}/plugin-settings.ts +3 -3
  127. package/src/modes/{interactive/components → components}/python-execution.ts +35 -24
  128. package/src/modes/{interactive/components → components}/queue-mode-selector.ts +1 -1
  129. package/src/modes/{interactive/components → components}/read-tool-group.ts +2 -2
  130. package/src/modes/{interactive/components → components}/session-selector.ts +3 -3
  131. package/src/modes/{interactive/components → components}/settings-defs.ts +2 -2
  132. package/src/modes/{interactive/components → components}/settings-selector.ts +2 -2
  133. package/src/modes/{interactive/components → components}/show-images-selector.ts +1 -1
  134. package/src/modes/{interactive/components → components}/status-line/segments.ts +2 -2
  135. package/src/modes/{interactive/components → components}/status-line/separators.ts +1 -1
  136. package/src/modes/{interactive/components → components}/status-line/types.ts +2 -2
  137. package/src/modes/{interactive/components → components}/status-line-segment-editor.ts +2 -2
  138. package/src/modes/{interactive/components → components}/status-line.ts +3 -3
  139. package/src/modes/{interactive/components → components}/theme-selector.ts +1 -1
  140. package/src/modes/{interactive/components → components}/thinking-selector.ts +1 -1
  141. package/src/modes/{interactive/components → components}/todo-display.ts +3 -4
  142. package/src/modes/{interactive/components → components}/todo-reminder.ts +2 -2
  143. package/src/modes/{interactive/components → components}/tool-execution.ts +8 -8
  144. package/src/modes/{interactive/components → components}/tree-selector.ts +3 -3
  145. package/src/modes/{interactive/components → components}/ttsr-notification.ts +2 -2
  146. package/src/modes/{interactive/components → components}/user-message-selector.ts +1 -1
  147. package/src/modes/{interactive/components → components}/user-message.ts +1 -1
  148. package/src/modes/{interactive/components → components}/welcome.ts +2 -2
  149. package/src/modes/{interactive/controllers → controllers}/command-controller.ts +381 -30
  150. package/src/modes/{interactive/controllers → controllers}/event-controller.ts +9 -9
  151. package/src/modes/{interactive/controllers → controllers}/extension-ui-controller.ts +8 -8
  152. package/src/modes/{interactive/controllers → controllers}/input-controller.ts +61 -13
  153. package/src/modes/{interactive/controllers → controllers}/selector-controller.ts +16 -16
  154. package/src/modes/index.ts +1 -1
  155. package/src/modes/{interactive/interactive-mode.ts → interactive-mode.ts} +20 -15
  156. package/src/modes/print-mode.ts +1 -1
  157. package/src/modes/rpc/rpc-client.ts +3 -3
  158. package/src/modes/rpc/rpc-mode.ts +3 -3
  159. package/src/modes/rpc/rpc-types.ts +3 -3
  160. package/src/modes/{interactive/theme → theme}/theme.ts +1 -1
  161. package/src/modes/{interactive/types.ts → types.ts} +10 -10
  162. package/src/modes/{interactive/utils → utils}/ui-helpers.ts +20 -27
  163. package/src/{core/tools/patch → patch}/applicator.ts +1 -1
  164. package/src/{core/tools/patch → patch}/diff.ts +1 -1
  165. package/src/{core/tools/patch → patch}/index.ts +31 -36
  166. package/src/{core/tools/patch → patch}/shared.ts +9 -6
  167. package/src/prompts/agents/explore.md +83 -46
  168. package/src/prompts/agents/init.md +9 -4
  169. package/src/prompts/agents/plan.md +8 -7
  170. package/src/prompts/agents/reviewer.md +36 -18
  171. package/src/prompts/agents/task.md +4 -4
  172. package/src/prompts/compaction/branch-summary-preamble.md +0 -1
  173. package/src/prompts/review-request.md +0 -1
  174. package/src/prompts/system/custom-system-prompt.md +2 -14
  175. package/src/prompts/system/file-operations.md +0 -2
  176. package/src/prompts/system/system-prompt.md +182 -171
  177. package/src/prompts/system/web-search.md +26 -0
  178. package/src/prompts/tools/ask.md +31 -24
  179. package/src/prompts/tools/bash.md +20 -17
  180. package/src/prompts/tools/calculator.md +9 -5
  181. package/src/prompts/tools/fetch.md +16 -0
  182. package/src/prompts/tools/find.md +15 -5
  183. package/src/prompts/tools/gemini-image.md +21 -6
  184. package/src/prompts/tools/grep.md +28 -12
  185. package/src/prompts/tools/lsp.md +35 -14
  186. package/src/prompts/tools/patch.md +39 -41
  187. package/src/prompts/tools/python.md +59 -77
  188. package/src/prompts/tools/read.md +23 -22
  189. package/src/prompts/tools/replace.md +19 -12
  190. package/src/prompts/tools/ssh.md +21 -28
  191. package/src/prompts/tools/task.md +54 -44
  192. package/src/prompts/tools/todo-write.md +52 -163
  193. package/src/prompts/tools/web-search.md +16 -9
  194. package/src/prompts/tools/write.md +13 -2
  195. package/src/{core/sdk.ts → sdk.ts} +65 -34
  196. package/src/{core → session}/agent-session.ts +157 -41
  197. package/src/{core → session}/agent-storage.ts +2 -2
  198. package/src/session/artifacts.ts +110 -0
  199. package/src/{core → session}/auth-storage.ts +525 -203
  200. package/src/{core → session}/compaction/branch-summarization.ts +5 -5
  201. package/src/{core → session}/compaction/compaction.ts +6 -6
  202. package/src/{core → session}/compaction/utils.ts +3 -3
  203. package/src/{core → session}/history-storage.ts +1 -1
  204. package/src/{core → session}/messages.ts +6 -8
  205. package/src/{core → session}/session-manager.ts +2 -2
  206. package/src/{core → session}/storage-migration.ts +2 -2
  207. package/src/session/streaming-output.ts +177 -0
  208. package/src/{core/ssh → ssh}/connection-manager.ts +1 -1
  209. package/src/{core/ssh → ssh}/ssh-executor.ts +19 -4
  210. package/src/{core/ssh → ssh}/sshfs-mount.ts +1 -1
  211. package/src/{core/system-prompt.ts → system-prompt.ts} +8 -37
  212. package/src/{core/tools/task → task}/agents.ts +8 -8
  213. package/src/{core/tools/task → task}/commands.ts +5 -6
  214. package/src/{core/tools/task → task}/discovery.ts +3 -3
  215. package/src/{core/tools/task → task}/executor.ts +34 -44
  216. package/src/{core/tools/task → task}/index.ts +206 -50
  217. package/src/{core/tools/task → task}/render.ts +80 -23
  218. package/src/{core/tools/task → task}/subprocess-tool-registry.ts +1 -1
  219. package/src/task/template.ts +47 -0
  220. package/src/{core/tools/task → task}/types.ts +19 -27
  221. package/src/{core/tools/task → task}/worker-protocol.ts +8 -4
  222. package/src/{core/tools/task → task}/worker.ts +34 -29
  223. package/src/task/worktree.ts +166 -0
  224. package/src/{core/tools → tools}/ask.ts +13 -21
  225. package/src/{core/tools → tools}/bash-interceptor.ts +1 -1
  226. package/src/{core/tools → tools}/bash.ts +61 -63
  227. package/src/{core/tools → tools}/calculator.ts +4 -4
  228. package/src/{core/tools → tools}/complete.ts +1 -1
  229. package/src/{core/tools → tools}/context.ts +2 -2
  230. package/src/{core/tools/web-fetch.ts → tools/fetch.ts} +97 -76
  231. package/src/{core/tools → tools}/find.ts +96 -107
  232. package/src/{core/tools → tools}/gemini-image.ts +420 -29
  233. package/src/{core/tools → tools}/grep.ts +155 -164
  234. package/src/{core/tools → tools}/index.ts +63 -56
  235. package/src/tools/list-limit.ts +40 -0
  236. package/src/{core/tools → tools}/ls.ts +44 -35
  237. package/src/{core/tools → tools}/notebook.ts +3 -3
  238. package/src/tools/output-meta.ts +443 -0
  239. package/src/tools/output-utils.ts +63 -0
  240. package/src/{core/tools → tools}/python.ts +106 -89
  241. package/src/tools/read.ts +882 -0
  242. package/src/{core/tools → tools}/render-utils.ts +1 -1
  243. package/src/{core/tools → tools}/renderers.ts +8 -10
  244. package/src/{core/tools → tools}/review.ts +2 -2
  245. package/src/{core/tools → tools}/ssh.ts +56 -59
  246. package/src/{core/tools → tools}/todo-write.ts +12 -23
  247. package/src/tools/tool-errors.ts +95 -0
  248. package/src/tools/tool-result.ts +92 -0
  249. package/src/{core/tools → tools}/truncate.ts +2 -2
  250. package/src/{core/tools → tools}/write.ts +15 -13
  251. package/src/utils/changelog.ts +1 -1
  252. package/src/{core → utils}/file-mentions.ts +4 -4
  253. package/src/utils/image-convert.ts +1 -1
  254. package/src/utils/image-resize.ts +1 -1
  255. package/src/utils/shell.ts +1 -1
  256. package/src/{core → utils}/title-generator.ts +4 -4
  257. package/src/utils/tools-manager.ts +1 -1
  258. package/src/{core/tools/web-scrapers → web/scrapers}/choosealicense.ts +1 -1
  259. package/src/{core/tools/web-scrapers → web/scrapers}/twitter.ts +3 -2
  260. package/src/{core/tools/web-scrapers → web/scrapers}/types.ts +4 -2
  261. package/src/{core/tools/web-scrapers → web/scrapers}/utils.ts +1 -1
  262. package/src/{core/tools/web-scrapers → web/scrapers}/youtube.ts +14 -13
  263. package/src/{core/tools/web-search → web/search}/auth.ts +4 -4
  264. package/src/{core/tools/web-search → web/search}/index.ts +22 -71
  265. package/src/{core/tools/web-search → web/search}/providers/anthropic.ts +7 -10
  266. package/src/{core/tools/web-search → web/search}/providers/exa.ts +2 -2
  267. package/src/{core/tools/web-search → web/search}/providers/perplexity.ts +4 -16
  268. package/src/{core/tools/web-search → web/search}/render.ts +3 -3
  269. package/scripts/migrate-sessions.sh +0 -93
  270. package/src/core/index.ts +0 -56
  271. package/src/core/python-prelude.ts +0 -3
  272. package/src/core/ssh-executor.ts +0 -5
  273. package/src/core/streaming-output.ts +0 -115
  274. package/src/core/tools/output.ts +0 -519
  275. package/src/core/tools/read.ts +0 -717
  276. package/src/core/tools/task/template.ts +0 -37
  277. package/src/prompts/tools/output.md +0 -47
  278. package/src/prompts/tools/web-fetch.md +0 -9
  279. /package/src/{core/tools/exa → exa}/types.ts +0 -0
  280. /package/src/{core → exec}/exec.ts +0 -0
  281. /package/src/{core/export-html → export/html}/template.css +0 -0
  282. /package/src/{core/export-html → export/html}/template.generated.ts +0 -0
  283. /package/src/{core/export-html → export/html}/template.html +0 -0
  284. /package/src/{core/export-html → export/html}/template.js +0 -0
  285. /package/src/{core/export-html → export/html}/template.macro.ts +0 -0
  286. /package/src/{core/export-html → export/html}/vendor/highlight.min.js +0 -0
  287. /package/src/{core/export-html → export/html}/vendor/marked.min.js +0 -0
  288. /package/src/{core → extensibility}/custom-commands/index.ts +0 -0
  289. /package/src/{core → extensibility}/custom-tools/index.ts +0 -0
  290. /package/src/{core → extensibility}/extensions/index.ts +0 -0
  291. /package/src/{core → extensibility}/hooks/tool-wrapper.ts +0 -0
  292. /package/src/{core → extensibility}/plugins/index.ts +0 -0
  293. /package/src/{core → extensibility}/plugins/loader.ts +0 -0
  294. /package/src/{core → extensibility}/plugins/manager.ts +0 -0
  295. /package/src/{core → extensibility}/plugins/parser.ts +0 -0
  296. /package/src/{core → extensibility}/plugins/types.ts +0 -0
  297. /package/src/{core/python-modules.ts → ipy/modules.ts} +0 -0
  298. /package/src/{core/tools/lsp → lsp}/defaults.json +0 -0
  299. /package/src/{core/tools/lsp → lsp}/edits.ts +0 -0
  300. /package/src/{core/tools/lsp → lsp}/lspmux.ts +0 -0
  301. /package/src/{core/tools/lsp → lsp}/rust-analyzer.ts +0 -0
  302. /package/src/{core/mcp → mcp}/client.ts +0 -0
  303. /package/src/{core/mcp → mcp}/index.ts +0 -0
  304. /package/src/{core/mcp → mcp}/json-rpc.ts +0 -0
  305. /package/src/{core/mcp → mcp}/transports/index.ts +0 -0
  306. /package/src/modes/{interactive/components → components}/countdown-timer.ts +0 -0
  307. /package/src/modes/{interactive/components → components}/custom-editor.ts +0 -0
  308. /package/src/modes/{interactive/components → components}/extensions/index.ts +0 -0
  309. /package/src/modes/{interactive/components → components}/index.ts +0 -0
  310. /package/src/modes/{interactive/components → components}/status-line/index.ts +0 -0
  311. /package/src/modes/{interactive/components → components}/status-line/presets.ts +0 -0
  312. /package/src/modes/{interactive/components → components}/visual-truncate.ts +0 -0
  313. /package/src/modes/{interactive/theme → theme}/dark.json +0 -0
  314. /package/src/modes/{interactive/theme → theme}/defaults/alabaster.json +0 -0
  315. /package/src/modes/{interactive/theme → theme}/defaults/amethyst.json +0 -0
  316. /package/src/modes/{interactive/theme → theme}/defaults/anthracite.json +0 -0
  317. /package/src/modes/{interactive/theme → theme}/defaults/basalt.json +0 -0
  318. /package/src/modes/{interactive/theme → theme}/defaults/birch.json +0 -0
  319. /package/src/modes/{interactive/theme → theme}/defaults/dark-abyss.json +0 -0
  320. /package/src/modes/{interactive/theme → theme}/defaults/dark-arctic.json +0 -0
  321. /package/src/modes/{interactive/theme → theme}/defaults/dark-aurora.json +0 -0
  322. /package/src/modes/{interactive/theme → theme}/defaults/dark-catppuccin.json +0 -0
  323. /package/src/modes/{interactive/theme → theme}/defaults/dark-cavern.json +0 -0
  324. /package/src/modes/{interactive/theme → theme}/defaults/dark-copper.json +0 -0
  325. /package/src/modes/{interactive/theme → theme}/defaults/dark-cosmos.json +0 -0
  326. /package/src/modes/{interactive/theme → theme}/defaults/dark-cyberpunk.json +0 -0
  327. /package/src/modes/{interactive/theme → theme}/defaults/dark-dracula.json +0 -0
  328. /package/src/modes/{interactive/theme → theme}/defaults/dark-eclipse.json +0 -0
  329. /package/src/modes/{interactive/theme → theme}/defaults/dark-ember.json +0 -0
  330. /package/src/modes/{interactive/theme → theme}/defaults/dark-equinox.json +0 -0
  331. /package/src/modes/{interactive/theme → theme}/defaults/dark-forest.json +0 -0
  332. /package/src/modes/{interactive/theme → theme}/defaults/dark-github.json +0 -0
  333. /package/src/modes/{interactive/theme → theme}/defaults/dark-gruvbox.json +0 -0
  334. /package/src/modes/{interactive/theme → theme}/defaults/dark-lavender.json +0 -0
  335. /package/src/modes/{interactive/theme → theme}/defaults/dark-lunar.json +0 -0
  336. /package/src/modes/{interactive/theme → theme}/defaults/dark-midnight.json +0 -0
  337. /package/src/modes/{interactive/theme → theme}/defaults/dark-monochrome.json +0 -0
  338. /package/src/modes/{interactive/theme → theme}/defaults/dark-monokai.json +0 -0
  339. /package/src/modes/{interactive/theme → theme}/defaults/dark-nebula.json +0 -0
  340. /package/src/modes/{interactive/theme → theme}/defaults/dark-nord.json +0 -0
  341. /package/src/modes/{interactive/theme → theme}/defaults/dark-ocean.json +0 -0
  342. /package/src/modes/{interactive/theme → theme}/defaults/dark-one.json +0 -0
  343. /package/src/modes/{interactive/theme → theme}/defaults/dark-rainforest.json +0 -0
  344. /package/src/modes/{interactive/theme → theme}/defaults/dark-reef.json +0 -0
  345. /package/src/modes/{interactive/theme → theme}/defaults/dark-retro.json +0 -0
  346. /package/src/modes/{interactive/theme → theme}/defaults/dark-rose-pine.json +0 -0
  347. /package/src/modes/{interactive/theme → theme}/defaults/dark-sakura.json +0 -0
  348. /package/src/modes/{interactive/theme → theme}/defaults/dark-slate.json +0 -0
  349. /package/src/modes/{interactive/theme → theme}/defaults/dark-solarized.json +0 -0
  350. /package/src/modes/{interactive/theme → theme}/defaults/dark-solstice.json +0 -0
  351. /package/src/modes/{interactive/theme → theme}/defaults/dark-starfall.json +0 -0
  352. /package/src/modes/{interactive/theme → theme}/defaults/dark-sunset.json +0 -0
  353. /package/src/modes/{interactive/theme → theme}/defaults/dark-swamp.json +0 -0
  354. /package/src/modes/{interactive/theme → theme}/defaults/dark-synthwave.json +0 -0
  355. /package/src/modes/{interactive/theme → theme}/defaults/dark-taiga.json +0 -0
  356. /package/src/modes/{interactive/theme → theme}/defaults/dark-terminal.json +0 -0
  357. /package/src/modes/{interactive/theme → theme}/defaults/dark-tokyo-night.json +0 -0
  358. /package/src/modes/{interactive/theme → theme}/defaults/dark-tundra.json +0 -0
  359. /package/src/modes/{interactive/theme → theme}/defaults/dark-twilight.json +0 -0
  360. /package/src/modes/{interactive/theme → theme}/defaults/dark-volcanic.json +0 -0
  361. /package/src/modes/{interactive/theme → theme}/defaults/graphite.json +0 -0
  362. /package/src/modes/{interactive/theme → theme}/defaults/index.ts +0 -0
  363. /package/src/modes/{interactive/theme → theme}/defaults/light-arctic.json +0 -0
  364. /package/src/modes/{interactive/theme → theme}/defaults/light-aurora-day.json +0 -0
  365. /package/src/modes/{interactive/theme → theme}/defaults/light-canyon.json +0 -0
  366. /package/src/modes/{interactive/theme → theme}/defaults/light-catppuccin.json +0 -0
  367. /package/src/modes/{interactive/theme → theme}/defaults/light-cirrus.json +0 -0
  368. /package/src/modes/{interactive/theme → theme}/defaults/light-coral.json +0 -0
  369. /package/src/modes/{interactive/theme → theme}/defaults/light-cyberpunk.json +0 -0
  370. /package/src/modes/{interactive/theme → theme}/defaults/light-dawn.json +0 -0
  371. /package/src/modes/{interactive/theme → theme}/defaults/light-dunes.json +0 -0
  372. /package/src/modes/{interactive/theme → theme}/defaults/light-eucalyptus.json +0 -0
  373. /package/src/modes/{interactive/theme → theme}/defaults/light-forest.json +0 -0
  374. /package/src/modes/{interactive/theme → theme}/defaults/light-frost.json +0 -0
  375. /package/src/modes/{interactive/theme → theme}/defaults/light-github.json +0 -0
  376. /package/src/modes/{interactive/theme → theme}/defaults/light-glacier.json +0 -0
  377. /package/src/modes/{interactive/theme → theme}/defaults/light-gruvbox.json +0 -0
  378. /package/src/modes/{interactive/theme → theme}/defaults/light-haze.json +0 -0
  379. /package/src/modes/{interactive/theme → theme}/defaults/light-honeycomb.json +0 -0
  380. /package/src/modes/{interactive/theme → theme}/defaults/light-lagoon.json +0 -0
  381. /package/src/modes/{interactive/theme → theme}/defaults/light-lavender.json +0 -0
  382. /package/src/modes/{interactive/theme → theme}/defaults/light-meadow.json +0 -0
  383. /package/src/modes/{interactive/theme → theme}/defaults/light-mint.json +0 -0
  384. /package/src/modes/{interactive/theme → theme}/defaults/light-monochrome.json +0 -0
  385. /package/src/modes/{interactive/theme → theme}/defaults/light-ocean.json +0 -0
  386. /package/src/modes/{interactive/theme → theme}/defaults/light-one.json +0 -0
  387. /package/src/modes/{interactive/theme → theme}/defaults/light-opal.json +0 -0
  388. /package/src/modes/{interactive/theme → theme}/defaults/light-orchard.json +0 -0
  389. /package/src/modes/{interactive/theme → theme}/defaults/light-paper.json +0 -0
  390. /package/src/modes/{interactive/theme → theme}/defaults/light-prism.json +0 -0
  391. /package/src/modes/{interactive/theme → theme}/defaults/light-retro.json +0 -0
  392. /package/src/modes/{interactive/theme → theme}/defaults/light-sand.json +0 -0
  393. /package/src/modes/{interactive/theme → theme}/defaults/light-savanna.json +0 -0
  394. /package/src/modes/{interactive/theme → theme}/defaults/light-solarized.json +0 -0
  395. /package/src/modes/{interactive/theme → theme}/defaults/light-soleil.json +0 -0
  396. /package/src/modes/{interactive/theme → theme}/defaults/light-sunset.json +0 -0
  397. /package/src/modes/{interactive/theme → theme}/defaults/light-synthwave.json +0 -0
  398. /package/src/modes/{interactive/theme → theme}/defaults/light-tokyo-night.json +0 -0
  399. /package/src/modes/{interactive/theme → theme}/defaults/light-wetland.json +0 -0
  400. /package/src/modes/{interactive/theme → theme}/defaults/light-zenith.json +0 -0
  401. /package/src/modes/{interactive/theme → theme}/defaults/limestone.json +0 -0
  402. /package/src/modes/{interactive/theme → theme}/defaults/mahogany.json +0 -0
  403. /package/src/modes/{interactive/theme → theme}/defaults/marble.json +0 -0
  404. /package/src/modes/{interactive/theme → theme}/defaults/obsidian.json +0 -0
  405. /package/src/modes/{interactive/theme → theme}/defaults/onyx.json +0 -0
  406. /package/src/modes/{interactive/theme → theme}/defaults/pearl.json +0 -0
  407. /package/src/modes/{interactive/theme → theme}/defaults/porcelain.json +0 -0
  408. /package/src/modes/{interactive/theme → theme}/defaults/quartz.json +0 -0
  409. /package/src/modes/{interactive/theme → theme}/defaults/sandstone.json +0 -0
  410. /package/src/modes/{interactive/theme → theme}/defaults/titanium.json +0 -0
  411. /package/src/modes/{interactive/theme → theme}/light.json +0 -0
  412. /package/src/modes/{interactive/theme → theme}/theme-schema.json +0 -0
  413. /package/src/{core/tools/patch → patch}/fuzzy.ts +0 -0
  414. /package/src/{core/tools/patch → patch}/normalize.ts +0 -0
  415. /package/src/{core/tools/patch → patch}/normative.ts +0 -0
  416. /package/src/{core/tools/patch → patch}/parser.ts +0 -0
  417. /package/src/{core/tools/patch → patch}/types.ts +0 -0
  418. /package/src/{core → session}/compaction/index.ts +0 -0
  419. /package/src/{core → session}/session-storage.ts +0 -0
  420. /package/src/{core/tools/task → task}/name-generator.ts +0 -0
  421. /package/src/{core/tools/task → task}/omp-command.ts +0 -0
  422. /package/src/{core/tools/task → task}/parallel.ts +0 -0
  423. /package/src/{core/tools → tools}/jtd-to-json-schema.ts +0 -0
  424. /package/src/{core/tools → tools}/path-utils.ts +0 -0
  425. /package/src/{core → utils}/event-bus.ts +0 -0
  426. /package/src/{core → utils}/frontmatter.ts +0 -0
  427. /package/src/{core → utils}/terminal-notify.ts +0 -0
  428. /package/src/{core → utils}/timings.ts +0 -0
  429. /package/src/{core → utils}/utils.ts +0 -0
  430. /package/src/{core/tools/web-scrapers → web/scrapers}/artifacthub.ts +0 -0
  431. /package/src/{core/tools/web-scrapers → web/scrapers}/arxiv.ts +0 -0
  432. /package/src/{core/tools/web-scrapers → web/scrapers}/aur.ts +0 -0
  433. /package/src/{core/tools/web-scrapers → web/scrapers}/biorxiv.ts +0 -0
  434. /package/src/{core/tools/web-scrapers → web/scrapers}/bluesky.ts +0 -0
  435. /package/src/{core/tools/web-scrapers → web/scrapers}/brew.ts +0 -0
  436. /package/src/{core/tools/web-scrapers → web/scrapers}/cheatsh.ts +0 -0
  437. /package/src/{core/tools/web-scrapers → web/scrapers}/chocolatey.ts +0 -0
  438. /package/src/{core/tools/web-scrapers → web/scrapers}/cisa-kev.ts +0 -0
  439. /package/src/{core/tools/web-scrapers → web/scrapers}/clojars.ts +0 -0
  440. /package/src/{core/tools/web-scrapers → web/scrapers}/coingecko.ts +0 -0
  441. /package/src/{core/tools/web-scrapers → web/scrapers}/crates-io.ts +0 -0
  442. /package/src/{core/tools/web-scrapers → web/scrapers}/crossref.ts +0 -0
  443. /package/src/{core/tools/web-scrapers → web/scrapers}/devto.ts +0 -0
  444. /package/src/{core/tools/web-scrapers → web/scrapers}/discogs.ts +0 -0
  445. /package/src/{core/tools/web-scrapers → web/scrapers}/discourse.ts +0 -0
  446. /package/src/{core/tools/web-scrapers → web/scrapers}/dockerhub.ts +0 -0
  447. /package/src/{core/tools/web-scrapers → web/scrapers}/fdroid.ts +0 -0
  448. /package/src/{core/tools/web-scrapers → web/scrapers}/firefox-addons.ts +0 -0
  449. /package/src/{core/tools/web-scrapers → web/scrapers}/flathub.ts +0 -0
  450. /package/src/{core/tools/web-scrapers → web/scrapers}/github-gist.ts +0 -0
  451. /package/src/{core/tools/web-scrapers → web/scrapers}/github.ts +0 -0
  452. /package/src/{core/tools/web-scrapers → web/scrapers}/gitlab.ts +0 -0
  453. /package/src/{core/tools/web-scrapers → web/scrapers}/go-pkg.ts +0 -0
  454. /package/src/{core/tools/web-scrapers → web/scrapers}/hackage.ts +0 -0
  455. /package/src/{core/tools/web-scrapers → web/scrapers}/hackernews.ts +0 -0
  456. /package/src/{core/tools/web-scrapers → web/scrapers}/hex.ts +0 -0
  457. /package/src/{core/tools/web-scrapers → web/scrapers}/huggingface.ts +0 -0
  458. /package/src/{core/tools/web-scrapers → web/scrapers}/iacr.ts +0 -0
  459. /package/src/{core/tools/web-scrapers → web/scrapers}/index.ts +0 -0
  460. /package/src/{core/tools/web-scrapers → web/scrapers}/jetbrains-marketplace.ts +0 -0
  461. /package/src/{core/tools/web-scrapers → web/scrapers}/lemmy.ts +0 -0
  462. /package/src/{core/tools/web-scrapers → web/scrapers}/lobsters.ts +0 -0
  463. /package/src/{core/tools/web-scrapers → web/scrapers}/mastodon.ts +0 -0
  464. /package/src/{core/tools/web-scrapers → web/scrapers}/maven.ts +0 -0
  465. /package/src/{core/tools/web-scrapers → web/scrapers}/mdn.ts +0 -0
  466. /package/src/{core/tools/web-scrapers → web/scrapers}/metacpan.ts +0 -0
  467. /package/src/{core/tools/web-scrapers → web/scrapers}/musicbrainz.ts +0 -0
  468. /package/src/{core/tools/web-scrapers → web/scrapers}/npm.ts +0 -0
  469. /package/src/{core/tools/web-scrapers → web/scrapers}/nuget.ts +0 -0
  470. /package/src/{core/tools/web-scrapers → web/scrapers}/nvd.ts +0 -0
  471. /package/src/{core/tools/web-scrapers → web/scrapers}/ollama.ts +0 -0
  472. /package/src/{core/tools/web-scrapers → web/scrapers}/open-vsx.ts +0 -0
  473. /package/src/{core/tools/web-scrapers → web/scrapers}/opencorporates.ts +0 -0
  474. /package/src/{core/tools/web-scrapers → web/scrapers}/openlibrary.ts +0 -0
  475. /package/src/{core/tools/web-scrapers → web/scrapers}/orcid.ts +0 -0
  476. /package/src/{core/tools/web-scrapers → web/scrapers}/osv.ts +0 -0
  477. /package/src/{core/tools/web-scrapers → web/scrapers}/packagist.ts +0 -0
  478. /package/src/{core/tools/web-scrapers → web/scrapers}/pub-dev.ts +0 -0
  479. /package/src/{core/tools/web-scrapers → web/scrapers}/pubmed.ts +0 -0
  480. /package/src/{core/tools/web-scrapers → web/scrapers}/pypi.ts +0 -0
  481. /package/src/{core/tools/web-scrapers → web/scrapers}/rawg.ts +0 -0
  482. /package/src/{core/tools/web-scrapers → web/scrapers}/readthedocs.ts +0 -0
  483. /package/src/{core/tools/web-scrapers → web/scrapers}/reddit.ts +0 -0
  484. /package/src/{core/tools/web-scrapers → web/scrapers}/repology.ts +0 -0
  485. /package/src/{core/tools/web-scrapers → web/scrapers}/rfc.ts +0 -0
  486. /package/src/{core/tools/web-scrapers → web/scrapers}/rubygems.ts +0 -0
  487. /package/src/{core/tools/web-scrapers → web/scrapers}/searchcode.ts +0 -0
  488. /package/src/{core/tools/web-scrapers → web/scrapers}/sec-edgar.ts +0 -0
  489. /package/src/{core/tools/web-scrapers → web/scrapers}/semantic-scholar.ts +0 -0
  490. /package/src/{core/tools/web-scrapers → web/scrapers}/snapcraft.ts +0 -0
  491. /package/src/{core/tools/web-scrapers → web/scrapers}/sourcegraph.ts +0 -0
  492. /package/src/{core/tools/web-scrapers → web/scrapers}/spdx.ts +0 -0
  493. /package/src/{core/tools/web-scrapers → web/scrapers}/spotify.ts +0 -0
  494. /package/src/{core/tools/web-scrapers → web/scrapers}/stackoverflow.ts +0 -0
  495. /package/src/{core/tools/web-scrapers → web/scrapers}/terraform.ts +0 -0
  496. /package/src/{core/tools/web-scrapers → web/scrapers}/tldr.ts +0 -0
  497. /package/src/{core/tools/web-scrapers → web/scrapers}/vimeo.ts +0 -0
  498. /package/src/{core/tools/web-scrapers → web/scrapers}/vscode-marketplace.ts +0 -0
  499. /package/src/{core/tools/web-scrapers → web/scrapers}/w3c.ts +0 -0
  500. /package/src/{core/tools/web-scrapers → web/scrapers}/wikidata.ts +0 -0
  501. /package/src/{core/tools/web-scrapers → web/scrapers}/wikipedia.ts +0 -0
  502. /package/src/{core/tools/web-search → web/search}/types.ts +0 -0
@@ -1,5 +1,4 @@
1
- import { rm } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
1
+ import { mkdir, rm } from "node:fs/promises";
3
2
  import * as path from "node:path";
4
3
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
5
4
  import type { Component } from "@oh-my-pi/pi-tui";
@@ -8,26 +7,26 @@ import { ptree } from "@oh-my-pi/pi-utils";
8
7
  import { type Static, Type } from "@sinclair/typebox";
9
8
  import { nanoid } from "nanoid";
10
9
  import { parse as parseHtml } from "node-html-parser";
11
- import { type Theme, theme } from "../../modes/interactive/theme/theme";
12
- import webFetchDescription from "../../prompts/tools/web-fetch.md" with { type: "text" };
13
- import { ensureTool } from "../../utils/tools-manager";
14
- import type { RenderResultOptions } from "../custom-tools/types";
15
- import { renderPromptTemplate } from "../prompt-templates";
10
+ import { renderPromptTemplate } from "$c/config/prompt-templates";
11
+ import type { RenderResultOptions } from "$c/extensibility/custom-tools/types";
12
+ import { type Theme, theme } from "$c/modes/theme/theme";
13
+ import fetchDescription from "$c/prompts/tools/fetch.md" with { type: "text" };
14
+ import type { OutputMeta } from "$c/tools/output-meta";
15
+ import { ToolAbortError } from "$c/tools/tool-errors";
16
+ import { ensureTool } from "$c/utils/tools-manager";
17
+ import { specialHandlers } from "$c/web/scrapers/index";
18
+ import type { RenderResult } from "$c/web/scrapers/types";
19
+ import { finalizeOutput, loadPage, MAX_OUTPUT_CHARS } from "$c/web/scrapers/types";
20
+ import { convertWithMarkitdown, fetchBinary } from "$c/web/scrapers/utils";
16
21
  import type { ToolSession } from "./index";
22
+ import { applyListLimit } from "./list-limit";
17
23
  import { formatExpandHint } from "./render-utils";
18
- import { specialHandlers } from "./web-scrapers/index";
19
- import type { RenderResult } from "./web-scrapers/types";
20
- import { finalizeOutput, loadPage } from "./web-scrapers/types";
21
- import { convertWithMarkitdown, fetchBinary } from "./web-scrapers/utils";
24
+ import { toolResult } from "./tool-result";
22
25
 
23
26
  // =============================================================================
24
27
  // Types and Constants
25
28
  // =============================================================================
26
29
 
27
- const MIN_TIMEOUT = 1_000;
28
- const DEFAULT_TIMEOUT = 20_000;
29
- const MAX_TIMEOUT = 45_000;
30
-
31
30
  // Convertible document types (markitdown supported)
32
31
  const CONVERTIBLE_MIMES = new Set([
33
32
  "application/pdf",
@@ -246,7 +245,7 @@ async function tryMdSuffix(url: string, timeout: number, signal?: AbortSignal):
246
245
  if (signal?.aborted) {
247
246
  return null;
248
247
  }
249
- const result = await loadPage(candidate, { timeout: Math.min(timeout, MAX_TIMEOUT), signal });
248
+ const result = await loadPage(candidate, { timeout, signal });
250
249
  if (result.ok && result.content.trim().length > 100 && !looksLikeHtml(result.content)) {
251
250
  return result.content;
252
251
  }
@@ -448,9 +447,10 @@ function parseFeedToMarkdown(content: string, maxItems = 10): string {
448
447
  async function renderHtmlToText(
449
448
  html: string,
450
449
  timeout: number,
450
+ scratchDir: string,
451
451
  ): Promise<{ content: string; ok: boolean; method: string }> {
452
- const tmpDir = tmpdir();
453
- const tmpFile = path.join(tmpDir, `omp-${nanoid()}.html`);
452
+ await mkdir(scratchDir, { recursive: true });
453
+ const tmpFile = path.join(scratchDir, `omp-${nanoid()}.html`);
454
454
 
455
455
  try {
456
456
  await Bun.write(tmpFile, html);
@@ -532,7 +532,7 @@ function formatJson(content: string): string {
532
532
  async function handleSpecialUrls(url: string, timeout: number, signal?: AbortSignal): Promise<RenderResult | null> {
533
533
  for (const handler of specialHandlers) {
534
534
  if (signal?.aborted) {
535
- throw new Error("Operation aborted");
535
+ throw new ToolAbortError();
536
536
  }
537
537
  const result = await handler(url, timeout, signal);
538
538
  if (result) return result;
@@ -550,13 +550,14 @@ async function handleSpecialUrls(url: string, timeout: number, signal?: AbortSig
550
550
  async function renderUrl(
551
551
  url: string,
552
552
  timeout: number,
553
- raw: boolean = false,
553
+ raw: boolean,
554
+ scratchDir: string,
554
555
  signal?: AbortSignal,
555
556
  ): Promise<RenderResult> {
556
557
  const notes: string[] = [];
557
558
  const fetchedAt = new Date().toISOString();
558
559
  if (signal?.aborted) {
559
- throw new Error("Operation aborted");
560
+ throw new ToolAbortError();
560
561
  }
561
562
 
562
563
  // Handle internal protocol URLs (e.g., pi-internal://) - return empty
@@ -586,7 +587,7 @@ async function renderUrl(
586
587
  // Step 2: Fetch page
587
588
  const response = await loadPage(url, { timeout, signal });
588
589
  if (signal?.aborted) {
589
- throw new Error("Operation aborted");
590
+ throw new ToolAbortError();
590
591
  }
591
592
  if (!response.ok) {
592
593
  return {
@@ -787,11 +788,11 @@ async function renderUrl(
787
788
  }
788
789
 
789
790
  if (signal?.aborted) {
790
- throw new Error("Operation aborted");
791
+ throw new ToolAbortError();
791
792
  }
792
793
 
793
794
  // Step 6: Render HTML with lynx or html2text
794
- const htmlResult = await renderHtmlToText(rawContent, timeout);
795
+ const htmlResult = await renderHtmlToText(rawContent, timeout, scratchDir);
795
796
  if (!htmlResult.ok) {
796
797
  notes.push("html rendering failed (lynx/html2text unavailable)");
797
798
  const output = finalizeOutput(rawContent);
@@ -871,66 +872,68 @@ async function renderUrl(
871
872
  // Tool Definition
872
873
  // =============================================================================
873
874
 
874
- const webFetchSchema = Type.Object({
875
- url: Type.String({ description: "The URL to fetch and render" }),
876
- timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (default: 20, max: 120)" })),
877
- raw: Type.Optional(
878
- Type.Boolean({ description: "Return raw content without site-specific rendering or LLM-friendly transforms" }),
879
- ),
875
+ const fetchSchema = Type.Object({
876
+ url: Type.String({ description: "URL to fetch" }),
877
+ timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (default: 20)" })),
878
+ raw: Type.Optional(Type.Boolean({ description: "Return raw HTML without transforms" })),
880
879
  });
881
880
 
882
- export interface WebFetchToolDetails {
881
+ export interface FetchToolDetails {
883
882
  url: string;
884
883
  finalUrl: string;
885
884
  contentType: string;
886
885
  method: string;
887
886
  truncated: boolean;
888
887
  notes: string[];
888
+ meta?: OutputMeta;
889
889
  }
890
890
 
891
- export class WebFetchTool implements AgentTool<typeof webFetchSchema, WebFetchToolDetails> {
892
- public readonly name = "web_fetch";
893
- public readonly label = "Web Fetch";
891
+ export class FetchTool implements AgentTool<typeof fetchSchema, FetchToolDetails> {
892
+ public readonly name = "fetch";
893
+ public readonly label = "Fetch";
894
894
  public readonly description: string;
895
- public readonly parameters = webFetchSchema;
895
+ public readonly parameters = fetchSchema;
896
+ private readonly session: ToolSession;
896
897
 
897
- constructor(_session: ToolSession) {
898
- this.description = renderPromptTemplate(webFetchDescription);
898
+ constructor(session: ToolSession) {
899
+ this.session = session;
900
+ this.description = renderPromptTemplate(fetchDescription);
899
901
  }
900
902
 
901
903
  public async execute(
902
904
  _toolCallId: string,
903
- params: Static<typeof webFetchSchema>,
905
+ params: Static<typeof fetchSchema>,
904
906
  signal?: AbortSignal,
905
- _onUpdate?: AgentToolUpdateCallback<WebFetchToolDetails>,
907
+ _onUpdate?: AgentToolUpdateCallback<FetchToolDetails>,
906
908
  _context?: AgentToolContext,
907
- ): Promise<AgentToolResult<WebFetchToolDetails>> {
908
- const { url, timeout = DEFAULT_TIMEOUT, raw = false } = params;
909
+ ): Promise<AgentToolResult<FetchToolDetails>> {
910
+ const { url, timeout: rawTimeout = 20, raw = false } = params;
911
+
912
+ // Auto-convert milliseconds to seconds if value > 1000 (16+ min is unreasonable)
913
+ const timeoutSec = rawTimeout > 1000 ? rawTimeout / 1000 : rawTimeout;
914
+
915
+ // Clamp to valid range (seconds)
916
+ const effectiveTimeout = Math.min(Math.max(timeoutSec, 1), 45);
909
917
 
910
918
  if (signal?.aborted) {
911
- throw new Error("Operation aborted");
919
+ throw new ToolAbortError();
912
920
  }
913
921
 
914
- // Clamp timeout
915
- const effectiveTimeout = Math.min(Math.max(timeout, MIN_TIMEOUT), MAX_TIMEOUT);
916
-
917
- const result = await renderUrl(url, effectiveTimeout, raw, signal);
922
+ const scratchDir = this.session.getArtifactsDir?.() ?? this.session.cwd;
923
+ const result = await renderUrl(url, effectiveTimeout, raw, scratchDir, signal);
918
924
 
919
925
  // Format output
920
926
  let output = "";
921
927
  output += `URL: ${result.finalUrl}\n`;
922
928
  output += `Content-Type: ${result.contentType}\n`;
923
929
  output += `Method: ${result.method}\n`;
924
- if (result.truncated) {
925
- output += `Warning: Output was truncated\n`;
926
- }
927
930
  if (result.notes.length > 0) {
928
931
  output += `Notes: ${result.notes.join("; ")}\n`;
929
932
  }
930
933
  output += `\n---\n\n`;
931
934
  output += result.content;
932
935
 
933
- const details: WebFetchToolDetails = {
936
+ const details: FetchToolDetails = {
934
937
  url: result.url,
935
938
  finalUrl: result.finalUrl,
936
939
  contentType: result.contentType,
@@ -939,10 +942,21 @@ export class WebFetchTool implements AgentTool<typeof webFetchSchema, WebFetchTo
939
942
  notes: result.notes,
940
943
  };
941
944
 
942
- return {
943
- content: [{ type: "text", text: output }],
944
- details,
945
- };
945
+ const resultBuilder = toolResult(details).text(output).sourceUrl(result.finalUrl);
946
+ if (result.truncated) {
947
+ const outputLines = result.content.split("\n").length;
948
+ const outputBytes = Buffer.byteLength(result.content, "utf-8");
949
+ const totalBytes = Math.max(outputBytes + 1, MAX_OUTPUT_CHARS + 1);
950
+ const totalLines = outputLines + 1;
951
+ resultBuilder.truncationFromText(result.content, {
952
+ direction: "tail",
953
+ totalLines,
954
+ totalBytes,
955
+ maxBytes: MAX_OUTPUT_CHARS,
956
+ });
957
+ }
958
+
959
+ return resultBuilder.done();
946
960
  }
947
961
  }
948
962
 
@@ -967,32 +981,26 @@ function getDomain(url: string): string {
967
981
  }
968
982
  }
969
983
 
970
- /** Get first N lines of text as preview */
971
- function getPreviewLines(text: string, maxLines: number, maxLineLen: number, ellipsis: string): string[] {
972
- const lines = text.split("\n").filter((l) => l.trim());
973
- return lines.slice(0, maxLines).map((l) => truncate(l.trim(), maxLineLen, ellipsis));
974
- }
975
-
976
984
  /** Count non-empty lines */
977
985
  function countNonEmptyLines(text: string): number {
978
986
  return text.split("\n").filter((l) => l.trim()).length;
979
987
  }
980
988
 
981
- /** Render web fetch call (URL preview) */
982
- export function renderWebFetchCall(
989
+ /** Render fetch call (URL preview) */
990
+ export function renderFetchCall(
983
991
  args: { url: string; timeout?: number; raw?: boolean },
984
992
  uiTheme: Theme = theme,
985
993
  ): Component {
986
994
  const domain = getDomain(args.url);
987
995
  const path = truncate(args.url.replace(/^https?:\/\/[^/]+/, ""), 50, uiTheme.format.ellipsis);
988
996
  const icon = uiTheme.styledSymbol("status.pending", "muted");
989
- const text = `${icon} ${uiTheme.fg("toolTitle", "Web Fetch")} ${uiTheme.fg("accent", domain)}${uiTheme.fg("dim", path)}`;
997
+ const text = `${icon} ${uiTheme.fg("toolTitle", "Fetch")} ${uiTheme.fg("accent", domain)}${uiTheme.fg("dim", path)}`;
990
998
  return new Text(text, 0, 0);
991
999
  }
992
1000
 
993
- /** Render web fetch result with tree-based layout */
994
- export function renderWebFetchResult(
995
- result: { content: Array<{ type: string; text?: string }>; details?: WebFetchToolDetails },
1001
+ /** Render fetch result with tree-based layout */
1002
+ export function renderFetchResult(
1003
+ result: { content: Array<{ type: string; text?: string }>; details?: FetchToolDetails },
996
1004
  options: RenderResultOptions,
997
1005
  uiTheme: Theme = theme,
998
1006
  ): Component {
@@ -1006,7 +1014,9 @@ export function renderWebFetchResult(
1006
1014
  const domain = getDomain(details.finalUrl);
1007
1015
  const hasRedirect = details.url !== details.finalUrl;
1008
1016
  const hasNotes = details.notes.length > 0;
1009
- const statusIcon = details.truncated
1017
+ const truncation = details.meta?.truncation;
1018
+ const truncated = Boolean(details.truncated || truncation);
1019
+ const statusIcon = truncated
1010
1020
  ? uiTheme.styledSymbol("status.warning", "warning")
1011
1021
  : uiTheme.styledSymbol("status.success", "success");
1012
1022
  const expandHint = formatExpandHint(uiTheme, expanded);
@@ -1021,6 +1031,7 @@ export function renderWebFetchResult(
1021
1031
  : contentText;
1022
1032
  const lineCount = countNonEmptyLines(contentBody);
1023
1033
  const charCount = contentBody.trim().length;
1034
+ const contentLines = contentBody.split("\n").filter((l) => l.trim());
1024
1035
 
1025
1036
  if (!expanded) {
1026
1037
  // Collapsed view: metadata + preview
@@ -1031,14 +1042,19 @@ export function renderWebFetchResult(
1031
1042
  if (hasRedirect) {
1032
1043
  metaLines.push(`${uiTheme.fg("muted", "Final URL:")} ${uiTheme.fg("mdLinkUrl", details.finalUrl)}`);
1033
1044
  }
1034
- if (details.truncated) {
1045
+ if (truncated) {
1035
1046
  metaLines.push(uiTheme.fg("warning", `${uiTheme.status.warning} Output truncated`));
1047
+ if (truncation?.artifactId) {
1048
+ metaLines.push(uiTheme.fg("warning", `Full output: artifact://${truncation.artifactId}`));
1049
+ }
1036
1050
  }
1037
1051
  if (hasNotes) {
1038
1052
  metaLines.push(`${uiTheme.fg("muted", "Notes:")} ${details.notes.join("; ")}`);
1039
1053
  }
1040
1054
 
1041
- const previewLines = getPreviewLines(contentBody, 3, 100, uiTheme.format.ellipsis);
1055
+ const previewLimit = 3;
1056
+ const previewList = applyListLimit(contentLines, { headLimit: previewLimit });
1057
+ const previewLines = previewList.items.map((line) => truncate(line.trim(), 100, uiTheme.format.ellipsis));
1042
1058
  const detailLines: string[] = [...metaLines];
1043
1059
 
1044
1060
  if (previewLines.length === 0) {
@@ -1049,7 +1065,7 @@ export function renderWebFetchResult(
1049
1065
  }
1050
1066
  }
1051
1067
 
1052
- const remaining = Math.max(0, lineCount - previewLines.length);
1068
+ const remaining = Math.max(0, contentLines.length - previewLines.length);
1053
1069
  if (remaining > 0) {
1054
1070
  detailLines.push(uiTheme.fg("muted", `${uiTheme.format.ellipsis} ${remaining} more lines`));
1055
1071
  } else {
@@ -1074,8 +1090,11 @@ export function renderWebFetchResult(
1074
1090
  const lineLabel = `${lineCount} line${lineCount === 1 ? "" : "s"}`;
1075
1091
  metaLines.push(`${uiTheme.fg("muted", "Lines:")} ${lineLabel}`);
1076
1092
  metaLines.push(`${uiTheme.fg("muted", "Chars:")} ${charCount}`);
1077
- if (details.truncated) {
1093
+ if (truncated) {
1078
1094
  metaLines.push(uiTheme.fg("warning", `${uiTheme.status.warning} Output truncated`));
1095
+ if (truncation?.artifactId) {
1096
+ metaLines.push(uiTheme.fg("warning", `Full output: artifact://${truncation.artifactId}`));
1097
+ }
1079
1098
  }
1080
1099
  if (hasNotes) {
1081
1100
  metaLines.push(`${uiTheme.fg("muted", "Notes:")} ${details.notes.join("; ")}`);
@@ -1089,8 +1108,10 @@ export function renderWebFetchResult(
1089
1108
  }
1090
1109
 
1091
1110
  text += `\n ${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("accent", "Content Preview")}`;
1092
- const previewLines = getPreviewLines(contentBody, 12, 120, uiTheme.format.ellipsis);
1093
- const remaining = Math.max(0, lineCount - previewLines.length);
1111
+ const previewLimit = 12;
1112
+ const previewList = applyListLimit(contentLines, { headLimit: previewLimit });
1113
+ const previewLines = previewList.items.map((line) => truncate(line.trim(), 120, uiTheme.format.ellipsis));
1114
+ const remaining = Math.max(0, contentLines.length - previewLines.length);
1094
1115
  const contentPrefix = uiTheme.fg("dim", " ");
1095
1116
 
1096
1117
  if (previewLines.length === 0) {
@@ -1109,7 +1130,7 @@ export function renderWebFetchResult(
1109
1130
  return new Text(text, 0, 0);
1110
1131
  }
1111
1132
 
1112
- export const webFetchToolRenderer = {
1113
- renderCall: renderWebFetchCall,
1114
- renderResult: renderWebFetchResult,
1133
+ export const fetchToolRenderer = {
1134
+ renderCall: renderFetchCall,
1135
+ renderResult: renderFetchResult,
1115
1136
  };