@oh-my-pi/pi-coding-agent 7.0.0 → 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 (501) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +1 -1
  3. package/docs/hooks.md +2 -2
  4. package/docs/sdk.md +1 -1
  5. package/package.json +10 -10
  6. package/scripts/format-prompts.ts +143 -0
  7. package/scripts/generate-template.ts +1 -1
  8. package/src/cli/args.ts +3 -3
  9. package/src/cli/config-cli.ts +4 -4
  10. package/src/cli/file-processor.ts +3 -3
  11. package/src/cli/list-models.ts +2 -2
  12. package/src/cli/plugin-cli.ts +3 -3
  13. package/src/cli/session-picker.ts +2 -2
  14. package/src/cli/setup-cli.ts +2 -2
  15. package/src/cli/stats-cli.ts +1 -1
  16. package/src/cli/update-cli.ts +2 -2
  17. package/src/{core → config}/keybindings.ts +1 -1
  18. package/src/{core → config}/model-registry.ts +1 -1
  19. package/src/{core → config}/model-resolver.ts +3 -3
  20. package/src/{core → config}/prompt-templates.ts +2 -2
  21. package/src/{core → config}/settings-manager.ts +6 -6
  22. package/src/{core/cursor/exec-bridge.ts → cursor.ts} +4 -4
  23. package/src/discovery/agents-md.ts +4 -4
  24. package/src/discovery/builtin.ts +17 -17
  25. package/src/discovery/claude.ts +12 -12
  26. package/src/discovery/cline.ts +6 -6
  27. package/src/discovery/codex.ts +21 -21
  28. package/src/discovery/cursor.ts +9 -9
  29. package/src/discovery/gemini.ts +9 -9
  30. package/src/discovery/github.ts +6 -6
  31. package/src/discovery/helpers.ts +4 -4
  32. package/src/discovery/index.ts +16 -16
  33. package/src/discovery/mcp-json.ts +4 -4
  34. package/src/discovery/ssh.ts +4 -4
  35. package/src/discovery/vscode.ts +4 -4
  36. package/src/discovery/windsurf.ts +6 -6
  37. package/src/{core/tools/exa → exa}/company.ts +2 -3
  38. package/src/{core/tools/exa → exa}/index.ts +2 -2
  39. package/src/{core/tools/exa → exa}/linkedin.ts +2 -3
  40. package/src/{core/tools/exa → exa}/mcp-client.ts +2 -2
  41. package/src/{core/tools/exa → exa}/render.ts +3 -3
  42. package/src/{core/tools/exa → exa}/researcher.ts +1 -1
  43. package/src/{core/tools/exa → exa}/search.ts +2 -10
  44. package/src/{core/tools/exa → exa}/websets.ts +1 -1
  45. package/src/{core → exec}/bash-executor.ts +22 -6
  46. package/src/{core → export}/custom-share.ts +1 -1
  47. package/src/{core/export-html → export/html}/index.ts +3 -3
  48. package/src/{core → export}/ttsr.ts +2 -2
  49. package/src/{core → extensibility}/custom-commands/bundled/review/index.ts +4 -4
  50. package/src/{core → extensibility}/custom-commands/loader.ts +3 -3
  51. package/src/{core → extensibility}/custom-commands/types.ts +1 -1
  52. package/src/{core → extensibility}/custom-tools/loader.ts +9 -9
  53. package/src/{core → extensibility}/custom-tools/types.ts +6 -6
  54. package/src/{core → extensibility}/custom-tools/wrapper.ts +1 -1
  55. package/src/{core → extensibility}/extensions/loader.ts +8 -8
  56. package/src/{core → extensibility}/extensions/runner.ts +3 -3
  57. package/src/{core → extensibility}/extensions/types.ts +15 -15
  58. package/src/{core → extensibility}/extensions/wrapper.ts +1 -1
  59. package/src/{core → extensibility}/hooks/index.ts +1 -1
  60. package/src/{core → extensibility}/hooks/loader.ts +7 -7
  61. package/src/{core → extensibility}/hooks/runner.ts +4 -4
  62. package/src/{core → extensibility}/hooks/types.ts +9 -9
  63. package/src/{core → extensibility}/plugins/doctor.ts +1 -1
  64. package/src/{core → extensibility}/plugins/installer.ts +2 -3
  65. package/src/{core → extensibility}/plugins/paths.ts +1 -1
  66. package/src/{core → extensibility}/skills.ts +8 -48
  67. package/src/{core → extensibility}/slash-commands.ts +6 -6
  68. package/src/index.ts +127 -128
  69. package/src/internal-urls/agent-protocol.ts +126 -0
  70. package/src/internal-urls/artifact-protocol.ts +93 -0
  71. package/src/internal-urls/index.ts +28 -0
  72. package/src/internal-urls/json-query.ts +126 -0
  73. package/src/internal-urls/router.ts +69 -0
  74. package/src/internal-urls/rule-protocol.ts +56 -0
  75. package/src/internal-urls/skill-protocol.ts +112 -0
  76. package/src/internal-urls/types.ts +48 -0
  77. package/src/{core/python-executor.ts → ipy/executor.ts} +51 -11
  78. package/src/{core/python-gateway-coordinator.ts → ipy/gateway-coordinator.ts} +41 -325
  79. package/src/{core/python-kernel.ts → ipy/kernel.ts} +38 -10
  80. package/src/ipy/prelude.ts +3 -0
  81. package/src/{core/tools/lsp → lsp}/client.ts +7 -6
  82. package/src/{core/tools/lsp → lsp}/clients/biome-client.ts +1 -1
  83. package/src/{core/tools/lsp → lsp}/clients/index.ts +1 -1
  84. package/src/{core/tools/lsp → lsp}/clients/lsp-linter-client.ts +4 -4
  85. package/src/{core/tools/lsp → lsp}/config.ts +1 -1
  86. package/src/{core/tools/lsp → lsp}/index.ts +16 -15
  87. package/src/{core/tools/lsp → lsp}/render.ts +2 -2
  88. package/src/{core/tools/lsp → lsp}/types.ts +14 -16
  89. package/src/{core/tools/lsp → lsp}/utils.ts +1 -1
  90. package/src/main.ts +12 -12
  91. package/src/{core/mcp → mcp}/config.ts +8 -8
  92. package/src/{core/mcp → mcp}/loader.ts +5 -6
  93. package/src/{core/mcp → mcp}/manager.ts +2 -2
  94. package/src/{core/mcp → mcp}/tool-bridge.ts +35 -6
  95. package/src/{core/mcp → mcp}/tool-cache.ts +1 -1
  96. package/src/{core/mcp → mcp}/transports/http.ts +7 -1
  97. package/src/{core/mcp → mcp}/transports/stdio.ts +1 -1
  98. package/src/{core/mcp → mcp}/types.ts +1 -1
  99. package/src/migrations.ts +2 -2
  100. package/src/modes/{interactive/components → components}/armin.ts +1 -1
  101. package/src/modes/{interactive/components → components}/assistant-message.ts +1 -1
  102. package/src/modes/{interactive/components → components}/bash-execution.ts +37 -29
  103. package/src/modes/{interactive/components → components}/bordered-loader.ts +1 -1
  104. package/src/modes/{interactive/components → components}/branch-summary-message.ts +2 -2
  105. package/src/modes/{interactive/components → components}/compaction-summary-message.ts +2 -2
  106. package/src/modes/{interactive/components → components}/custom-message.ts +3 -3
  107. package/src/modes/{interactive/components → components}/diff.ts +1 -1
  108. package/src/modes/{interactive/components → components}/dynamic-border.ts +1 -1
  109. package/src/modes/{interactive/components → components}/extensions/extension-dashboard.ts +3 -3
  110. package/src/modes/{interactive/components → components}/extensions/extension-list.ts +2 -2
  111. package/src/modes/{interactive/components → components}/extensions/inspector-panel.ts +1 -1
  112. package/src/modes/{interactive/components → components}/extensions/state-manager.ts +11 -17
  113. package/src/modes/{interactive/components → components}/extensions/types.ts +1 -1
  114. package/src/modes/{interactive/components → components}/footer.ts +3 -3
  115. package/src/modes/{interactive/components → components}/history-search.ts +2 -2
  116. package/src/modes/{interactive/components → components}/hook-editor.ts +1 -1
  117. package/src/modes/{interactive/components → components}/hook-input.ts +1 -1
  118. package/src/modes/{interactive/components → components}/hook-message.ts +3 -3
  119. package/src/modes/{interactive/components → components}/hook-selector.ts +1 -1
  120. package/src/modes/{interactive/components → components}/keybinding-hints.ts +2 -2
  121. package/src/modes/{interactive/components → components}/login-dialog.ts +1 -1
  122. package/src/modes/{interactive/components → components}/model-selector.ts +5 -5
  123. package/src/modes/{interactive/components → components}/oauth-selector.ts +2 -2
  124. package/src/modes/{interactive/components → components}/plugin-settings.ts +3 -3
  125. package/src/modes/{interactive/components → components}/python-execution.ts +35 -24
  126. package/src/modes/{interactive/components → components}/queue-mode-selector.ts +1 -1
  127. package/src/modes/{interactive/components → components}/read-tool-group.ts +2 -2
  128. package/src/modes/{interactive/components → components}/session-selector.ts +3 -3
  129. package/src/modes/{interactive/components → components}/settings-defs.ts +2 -2
  130. package/src/modes/{interactive/components → components}/settings-selector.ts +2 -2
  131. package/src/modes/{interactive/components → components}/show-images-selector.ts +1 -1
  132. package/src/modes/{interactive/components → components}/status-line/segments.ts +2 -2
  133. package/src/modes/{interactive/components → components}/status-line/separators.ts +1 -1
  134. package/src/modes/{interactive/components → components}/status-line/types.ts +2 -2
  135. package/src/modes/{interactive/components → components}/status-line-segment-editor.ts +2 -2
  136. package/src/modes/{interactive/components → components}/status-line.ts +3 -3
  137. package/src/modes/{interactive/components → components}/theme-selector.ts +1 -1
  138. package/src/modes/{interactive/components → components}/thinking-selector.ts +1 -1
  139. package/src/modes/{interactive/components → components}/todo-display.ts +3 -4
  140. package/src/modes/{interactive/components → components}/todo-reminder.ts +2 -2
  141. package/src/modes/{interactive/components → components}/tool-execution.ts +8 -8
  142. package/src/modes/{interactive/components → components}/tree-selector.ts +3 -3
  143. package/src/modes/{interactive/components → components}/ttsr-notification.ts +2 -2
  144. package/src/modes/{interactive/components → components}/user-message-selector.ts +1 -1
  145. package/src/modes/{interactive/components → components}/user-message.ts +1 -1
  146. package/src/modes/{interactive/components → components}/welcome.ts +2 -2
  147. package/src/modes/{interactive/controllers → controllers}/command-controller.ts +32 -30
  148. package/src/modes/{interactive/controllers → controllers}/event-controller.ts +9 -9
  149. package/src/modes/{interactive/controllers → controllers}/extension-ui-controller.ts +8 -8
  150. package/src/modes/{interactive/controllers → controllers}/input-controller.ts +6 -6
  151. package/src/modes/{interactive/controllers → controllers}/selector-controller.ts +16 -16
  152. package/src/modes/index.ts +1 -1
  153. package/src/modes/{interactive/interactive-mode.ts → interactive-mode.ts} +14 -14
  154. package/src/modes/print-mode.ts +1 -1
  155. package/src/modes/rpc/rpc-client.ts +3 -3
  156. package/src/modes/rpc/rpc-mode.ts +3 -3
  157. package/src/modes/rpc/rpc-types.ts +3 -3
  158. package/src/modes/{interactive/theme → theme}/theme.ts +1 -1
  159. package/src/modes/{interactive/types.ts → types.ts} +8 -9
  160. package/src/modes/{interactive/utils → utils}/ui-helpers.ts +20 -27
  161. package/src/{core/tools/patch → patch}/applicator.ts +1 -1
  162. package/src/{core/tools/patch → patch}/diff.ts +1 -1
  163. package/src/{core/tools/patch → patch}/index.ts +31 -36
  164. package/src/{core/tools/patch → patch}/shared.ts +9 -6
  165. package/src/prompts/agents/explore.md +83 -46
  166. package/src/prompts/agents/init.md +9 -4
  167. package/src/prompts/agents/plan.md +8 -7
  168. package/src/prompts/agents/reviewer.md +36 -18
  169. package/src/prompts/agents/task.md +4 -4
  170. package/src/prompts/compaction/branch-summary-preamble.md +0 -1
  171. package/src/prompts/review-request.md +0 -1
  172. package/src/prompts/system/custom-system-prompt.md +2 -14
  173. package/src/prompts/system/file-operations.md +0 -2
  174. package/src/prompts/system/system-prompt.md +147 -138
  175. package/src/prompts/system/web-search.md +26 -0
  176. package/src/prompts/tools/ask.md +31 -24
  177. package/src/prompts/tools/bash.md +20 -17
  178. package/src/prompts/tools/calculator.md +9 -5
  179. package/src/prompts/tools/fetch.md +16 -0
  180. package/src/prompts/tools/find.md +15 -5
  181. package/src/prompts/tools/gemini-image.md +21 -6
  182. package/src/prompts/tools/grep.md +28 -12
  183. package/src/prompts/tools/lsp.md +35 -14
  184. package/src/prompts/tools/patch.md +39 -41
  185. package/src/prompts/tools/python.md +59 -76
  186. package/src/prompts/tools/read.md +23 -22
  187. package/src/prompts/tools/replace.md +19 -12
  188. package/src/prompts/tools/ssh.md +21 -28
  189. package/src/prompts/tools/task.md +54 -44
  190. package/src/prompts/tools/todo-write.md +52 -163
  191. package/src/prompts/tools/web-search.md +16 -9
  192. package/src/prompts/tools/write.md +13 -2
  193. package/src/{core/sdk.ts → sdk.ts} +65 -34
  194. package/src/{core → session}/agent-session.ts +45 -37
  195. package/src/{core → session}/agent-storage.ts +2 -2
  196. package/src/session/artifacts.ts +110 -0
  197. package/src/{core → session}/auth-storage.ts +1 -1
  198. package/src/{core → session}/compaction/branch-summarization.ts +5 -5
  199. package/src/{core → session}/compaction/compaction.ts +6 -6
  200. package/src/{core → session}/compaction/utils.ts +3 -3
  201. package/src/{core → session}/history-storage.ts +1 -1
  202. package/src/{core → session}/messages.ts +6 -8
  203. package/src/{core → session}/session-manager.ts +2 -2
  204. package/src/{core → session}/storage-migration.ts +2 -2
  205. package/src/session/streaming-output.ts +177 -0
  206. package/src/{core/ssh → ssh}/connection-manager.ts +1 -1
  207. package/src/{core/ssh → ssh}/ssh-executor.ts +19 -4
  208. package/src/{core/ssh → ssh}/sshfs-mount.ts +1 -1
  209. package/src/{core/system-prompt.ts → system-prompt.ts} +8 -37
  210. package/src/{core/tools/task → task}/agents.ts +8 -8
  211. package/src/{core/tools/task → task}/commands.ts +5 -6
  212. package/src/{core/tools/task → task}/discovery.ts +3 -3
  213. package/src/{core/tools/task → task}/executor.ts +34 -44
  214. package/src/{core/tools/task → task}/index.ts +206 -50
  215. package/src/{core/tools/task → task}/render.ts +80 -23
  216. package/src/{core/tools/task → task}/subprocess-tool-registry.ts +1 -1
  217. package/src/task/template.ts +47 -0
  218. package/src/{core/tools/task → task}/types.ts +19 -27
  219. package/src/{core/tools/task → task}/worker-protocol.ts +8 -4
  220. package/src/{core/tools/task → task}/worker.ts +34 -29
  221. package/src/task/worktree.ts +166 -0
  222. package/src/{core/tools → tools}/ask.ts +13 -21
  223. package/src/{core/tools → tools}/bash-interceptor.ts +1 -1
  224. package/src/{core/tools → tools}/bash.ts +61 -63
  225. package/src/{core/tools → tools}/calculator.ts +4 -4
  226. package/src/{core/tools → tools}/complete.ts +1 -1
  227. package/src/{core/tools → tools}/context.ts +2 -2
  228. package/src/{core/tools/web-fetch.ts → tools/fetch.ts} +97 -76
  229. package/src/{core/tools → tools}/find.ts +80 -104
  230. package/src/{core/tools → tools}/gemini-image.ts +420 -29
  231. package/src/{core/tools → tools}/grep.ts +155 -164
  232. package/src/{core/tools → tools}/index.ts +63 -56
  233. package/src/tools/list-limit.ts +40 -0
  234. package/src/{core/tools → tools}/ls.ts +44 -35
  235. package/src/{core/tools → tools}/notebook.ts +3 -3
  236. package/src/tools/output-meta.ts +443 -0
  237. package/src/tools/output-utils.ts +63 -0
  238. package/src/{core/tools → tools}/python.ts +105 -89
  239. package/src/tools/read.ts +882 -0
  240. package/src/{core/tools → tools}/render-utils.ts +1 -1
  241. package/src/{core/tools → tools}/renderers.ts +8 -10
  242. package/src/{core/tools → tools}/review.ts +2 -2
  243. package/src/{core/tools → tools}/ssh.ts +56 -59
  244. package/src/{core/tools → tools}/todo-write.ts +12 -23
  245. package/src/tools/tool-errors.ts +95 -0
  246. package/src/tools/tool-result.ts +92 -0
  247. package/src/{core/tools → tools}/truncate.ts +2 -2
  248. package/src/{core/tools → tools}/write.ts +15 -13
  249. package/src/utils/changelog.ts +1 -1
  250. package/src/{core → utils}/file-mentions.ts +4 -4
  251. package/src/utils/image-convert.ts +1 -1
  252. package/src/utils/image-resize.ts +1 -1
  253. package/src/utils/shell.ts +1 -1
  254. package/src/{core → utils}/title-generator.ts +4 -4
  255. package/src/utils/tools-manager.ts +1 -1
  256. package/src/{core/tools/web-scrapers → web/scrapers}/choosealicense.ts +1 -1
  257. package/src/{core/tools/web-scrapers → web/scrapers}/twitter.ts +3 -2
  258. package/src/{core/tools/web-scrapers → web/scrapers}/types.ts +4 -2
  259. package/src/{core/tools/web-scrapers → web/scrapers}/utils.ts +1 -1
  260. package/src/{core/tools/web-scrapers → web/scrapers}/youtube.ts +14 -13
  261. package/src/{core/tools/web-search → web/search}/auth.ts +4 -4
  262. package/src/{core/tools/web-search → web/search}/index.ts +22 -71
  263. package/src/{core/tools/web-search → web/search}/providers/anthropic.ts +7 -10
  264. package/src/{core/tools/web-search → web/search}/providers/exa.ts +2 -2
  265. package/src/{core/tools/web-search → web/search}/providers/perplexity.ts +4 -16
  266. package/src/{core/tools/web-search → web/search}/render.ts +3 -3
  267. package/scripts/migrate-sessions.sh +0 -93
  268. package/src/core/index.ts +0 -56
  269. package/src/core/python-prelude.ts +0 -3
  270. package/src/core/ssh-executor.ts +0 -5
  271. package/src/core/streaming-output.ts +0 -115
  272. package/src/core/tools/output.ts +0 -519
  273. package/src/core/tools/read.ts +0 -717
  274. package/src/core/tools/task/template.ts +0 -37
  275. package/src/prompts/tools/output.md +0 -47
  276. package/src/prompts/tools/web-fetch.md +0 -9
  277. /package/src/{core/tools/exa → exa}/types.ts +0 -0
  278. /package/src/{core → exec}/exec.ts +0 -0
  279. /package/src/{core/export-html → export/html}/template.css +0 -0
  280. /package/src/{core/export-html → export/html}/template.generated.ts +0 -0
  281. /package/src/{core/export-html → export/html}/template.html +0 -0
  282. /package/src/{core/export-html → export/html}/template.js +0 -0
  283. /package/src/{core/export-html → export/html}/template.macro.ts +0 -0
  284. /package/src/{core/export-html → export/html}/vendor/highlight.min.js +0 -0
  285. /package/src/{core/export-html → export/html}/vendor/marked.min.js +0 -0
  286. /package/src/{core → extensibility}/custom-commands/index.ts +0 -0
  287. /package/src/{core → extensibility}/custom-tools/index.ts +0 -0
  288. /package/src/{core → extensibility}/extensions/index.ts +0 -0
  289. /package/src/{core → extensibility}/hooks/tool-wrapper.ts +0 -0
  290. /package/src/{core → extensibility}/plugins/index.ts +0 -0
  291. /package/src/{core → extensibility}/plugins/loader.ts +0 -0
  292. /package/src/{core → extensibility}/plugins/manager.ts +0 -0
  293. /package/src/{core → extensibility}/plugins/parser.ts +0 -0
  294. /package/src/{core → extensibility}/plugins/types.ts +0 -0
  295. /package/src/{core/python-modules.ts → ipy/modules.ts} +0 -0
  296. /package/src/{core/python-prelude.py → ipy/prelude.py} +0 -0
  297. /package/src/{core/tools/lsp → lsp}/defaults.json +0 -0
  298. /package/src/{core/tools/lsp → lsp}/edits.ts +0 -0
  299. /package/src/{core/tools/lsp → lsp}/lspmux.ts +0 -0
  300. /package/src/{core/tools/lsp → lsp}/rust-analyzer.ts +0 -0
  301. /package/src/{core/mcp → mcp}/client.ts +0 -0
  302. /package/src/{core/mcp → mcp}/index.ts +0 -0
  303. /package/src/{core/mcp → mcp}/json-rpc.ts +0 -0
  304. /package/src/{core/mcp → mcp}/transports/index.ts +0 -0
  305. /package/src/modes/{interactive/components → components}/countdown-timer.ts +0 -0
  306. /package/src/modes/{interactive/components → components}/custom-editor.ts +0 -0
  307. /package/src/modes/{interactive/components → components}/extensions/index.ts +0 -0
  308. /package/src/modes/{interactive/components → components}/index.ts +0 -0
  309. /package/src/modes/{interactive/components → components}/status-line/index.ts +0 -0
  310. /package/src/modes/{interactive/components → components}/status-line/presets.ts +0 -0
  311. /package/src/modes/{interactive/components → components}/visual-truncate.ts +0 -0
  312. /package/src/modes/{interactive/theme → theme}/dark.json +0 -0
  313. /package/src/modes/{interactive/theme → theme}/defaults/alabaster.json +0 -0
  314. /package/src/modes/{interactive/theme → theme}/defaults/amethyst.json +0 -0
  315. /package/src/modes/{interactive/theme → theme}/defaults/anthracite.json +0 -0
  316. /package/src/modes/{interactive/theme → theme}/defaults/basalt.json +0 -0
  317. /package/src/modes/{interactive/theme → theme}/defaults/birch.json +0 -0
  318. /package/src/modes/{interactive/theme → theme}/defaults/dark-abyss.json +0 -0
  319. /package/src/modes/{interactive/theme → theme}/defaults/dark-arctic.json +0 -0
  320. /package/src/modes/{interactive/theme → theme}/defaults/dark-aurora.json +0 -0
  321. /package/src/modes/{interactive/theme → theme}/defaults/dark-catppuccin.json +0 -0
  322. /package/src/modes/{interactive/theme → theme}/defaults/dark-cavern.json +0 -0
  323. /package/src/modes/{interactive/theme → theme}/defaults/dark-copper.json +0 -0
  324. /package/src/modes/{interactive/theme → theme}/defaults/dark-cosmos.json +0 -0
  325. /package/src/modes/{interactive/theme → theme}/defaults/dark-cyberpunk.json +0 -0
  326. /package/src/modes/{interactive/theme → theme}/defaults/dark-dracula.json +0 -0
  327. /package/src/modes/{interactive/theme → theme}/defaults/dark-eclipse.json +0 -0
  328. /package/src/modes/{interactive/theme → theme}/defaults/dark-ember.json +0 -0
  329. /package/src/modes/{interactive/theme → theme}/defaults/dark-equinox.json +0 -0
  330. /package/src/modes/{interactive/theme → theme}/defaults/dark-forest.json +0 -0
  331. /package/src/modes/{interactive/theme → theme}/defaults/dark-github.json +0 -0
  332. /package/src/modes/{interactive/theme → theme}/defaults/dark-gruvbox.json +0 -0
  333. /package/src/modes/{interactive/theme → theme}/defaults/dark-lavender.json +0 -0
  334. /package/src/modes/{interactive/theme → theme}/defaults/dark-lunar.json +0 -0
  335. /package/src/modes/{interactive/theme → theme}/defaults/dark-midnight.json +0 -0
  336. /package/src/modes/{interactive/theme → theme}/defaults/dark-monochrome.json +0 -0
  337. /package/src/modes/{interactive/theme → theme}/defaults/dark-monokai.json +0 -0
  338. /package/src/modes/{interactive/theme → theme}/defaults/dark-nebula.json +0 -0
  339. /package/src/modes/{interactive/theme → theme}/defaults/dark-nord.json +0 -0
  340. /package/src/modes/{interactive/theme → theme}/defaults/dark-ocean.json +0 -0
  341. /package/src/modes/{interactive/theme → theme}/defaults/dark-one.json +0 -0
  342. /package/src/modes/{interactive/theme → theme}/defaults/dark-rainforest.json +0 -0
  343. /package/src/modes/{interactive/theme → theme}/defaults/dark-reef.json +0 -0
  344. /package/src/modes/{interactive/theme → theme}/defaults/dark-retro.json +0 -0
  345. /package/src/modes/{interactive/theme → theme}/defaults/dark-rose-pine.json +0 -0
  346. /package/src/modes/{interactive/theme → theme}/defaults/dark-sakura.json +0 -0
  347. /package/src/modes/{interactive/theme → theme}/defaults/dark-slate.json +0 -0
  348. /package/src/modes/{interactive/theme → theme}/defaults/dark-solarized.json +0 -0
  349. /package/src/modes/{interactive/theme → theme}/defaults/dark-solstice.json +0 -0
  350. /package/src/modes/{interactive/theme → theme}/defaults/dark-starfall.json +0 -0
  351. /package/src/modes/{interactive/theme → theme}/defaults/dark-sunset.json +0 -0
  352. /package/src/modes/{interactive/theme → theme}/defaults/dark-swamp.json +0 -0
  353. /package/src/modes/{interactive/theme → theme}/defaults/dark-synthwave.json +0 -0
  354. /package/src/modes/{interactive/theme → theme}/defaults/dark-taiga.json +0 -0
  355. /package/src/modes/{interactive/theme → theme}/defaults/dark-terminal.json +0 -0
  356. /package/src/modes/{interactive/theme → theme}/defaults/dark-tokyo-night.json +0 -0
  357. /package/src/modes/{interactive/theme → theme}/defaults/dark-tundra.json +0 -0
  358. /package/src/modes/{interactive/theme → theme}/defaults/dark-twilight.json +0 -0
  359. /package/src/modes/{interactive/theme → theme}/defaults/dark-volcanic.json +0 -0
  360. /package/src/modes/{interactive/theme → theme}/defaults/graphite.json +0 -0
  361. /package/src/modes/{interactive/theme → theme}/defaults/index.ts +0 -0
  362. /package/src/modes/{interactive/theme → theme}/defaults/light-arctic.json +0 -0
  363. /package/src/modes/{interactive/theme → theme}/defaults/light-aurora-day.json +0 -0
  364. /package/src/modes/{interactive/theme → theme}/defaults/light-canyon.json +0 -0
  365. /package/src/modes/{interactive/theme → theme}/defaults/light-catppuccin.json +0 -0
  366. /package/src/modes/{interactive/theme → theme}/defaults/light-cirrus.json +0 -0
  367. /package/src/modes/{interactive/theme → theme}/defaults/light-coral.json +0 -0
  368. /package/src/modes/{interactive/theme → theme}/defaults/light-cyberpunk.json +0 -0
  369. /package/src/modes/{interactive/theme → theme}/defaults/light-dawn.json +0 -0
  370. /package/src/modes/{interactive/theme → theme}/defaults/light-dunes.json +0 -0
  371. /package/src/modes/{interactive/theme → theme}/defaults/light-eucalyptus.json +0 -0
  372. /package/src/modes/{interactive/theme → theme}/defaults/light-forest.json +0 -0
  373. /package/src/modes/{interactive/theme → theme}/defaults/light-frost.json +0 -0
  374. /package/src/modes/{interactive/theme → theme}/defaults/light-github.json +0 -0
  375. /package/src/modes/{interactive/theme → theme}/defaults/light-glacier.json +0 -0
  376. /package/src/modes/{interactive/theme → theme}/defaults/light-gruvbox.json +0 -0
  377. /package/src/modes/{interactive/theme → theme}/defaults/light-haze.json +0 -0
  378. /package/src/modes/{interactive/theme → theme}/defaults/light-honeycomb.json +0 -0
  379. /package/src/modes/{interactive/theme → theme}/defaults/light-lagoon.json +0 -0
  380. /package/src/modes/{interactive/theme → theme}/defaults/light-lavender.json +0 -0
  381. /package/src/modes/{interactive/theme → theme}/defaults/light-meadow.json +0 -0
  382. /package/src/modes/{interactive/theme → theme}/defaults/light-mint.json +0 -0
  383. /package/src/modes/{interactive/theme → theme}/defaults/light-monochrome.json +0 -0
  384. /package/src/modes/{interactive/theme → theme}/defaults/light-ocean.json +0 -0
  385. /package/src/modes/{interactive/theme → theme}/defaults/light-one.json +0 -0
  386. /package/src/modes/{interactive/theme → theme}/defaults/light-opal.json +0 -0
  387. /package/src/modes/{interactive/theme → theme}/defaults/light-orchard.json +0 -0
  388. /package/src/modes/{interactive/theme → theme}/defaults/light-paper.json +0 -0
  389. /package/src/modes/{interactive/theme → theme}/defaults/light-prism.json +0 -0
  390. /package/src/modes/{interactive/theme → theme}/defaults/light-retro.json +0 -0
  391. /package/src/modes/{interactive/theme → theme}/defaults/light-sand.json +0 -0
  392. /package/src/modes/{interactive/theme → theme}/defaults/light-savanna.json +0 -0
  393. /package/src/modes/{interactive/theme → theme}/defaults/light-solarized.json +0 -0
  394. /package/src/modes/{interactive/theme → theme}/defaults/light-soleil.json +0 -0
  395. /package/src/modes/{interactive/theme → theme}/defaults/light-sunset.json +0 -0
  396. /package/src/modes/{interactive/theme → theme}/defaults/light-synthwave.json +0 -0
  397. /package/src/modes/{interactive/theme → theme}/defaults/light-tokyo-night.json +0 -0
  398. /package/src/modes/{interactive/theme → theme}/defaults/light-wetland.json +0 -0
  399. /package/src/modes/{interactive/theme → theme}/defaults/light-zenith.json +0 -0
  400. /package/src/modes/{interactive/theme → theme}/defaults/limestone.json +0 -0
  401. /package/src/modes/{interactive/theme → theme}/defaults/mahogany.json +0 -0
  402. /package/src/modes/{interactive/theme → theme}/defaults/marble.json +0 -0
  403. /package/src/modes/{interactive/theme → theme}/defaults/obsidian.json +0 -0
  404. /package/src/modes/{interactive/theme → theme}/defaults/onyx.json +0 -0
  405. /package/src/modes/{interactive/theme → theme}/defaults/pearl.json +0 -0
  406. /package/src/modes/{interactive/theme → theme}/defaults/porcelain.json +0 -0
  407. /package/src/modes/{interactive/theme → theme}/defaults/quartz.json +0 -0
  408. /package/src/modes/{interactive/theme → theme}/defaults/sandstone.json +0 -0
  409. /package/src/modes/{interactive/theme → theme}/defaults/titanium.json +0 -0
  410. /package/src/modes/{interactive/theme → theme}/light.json +0 -0
  411. /package/src/modes/{interactive/theme → theme}/theme-schema.json +0 -0
  412. /package/src/{core/tools/patch → patch}/fuzzy.ts +0 -0
  413. /package/src/{core/tools/patch → patch}/normalize.ts +0 -0
  414. /package/src/{core/tools/patch → patch}/normative.ts +0 -0
  415. /package/src/{core/tools/patch → patch}/parser.ts +0 -0
  416. /package/src/{core/tools/patch → patch}/types.ts +0 -0
  417. /package/src/{core → session}/compaction/index.ts +0 -0
  418. /package/src/{core → session}/session-storage.ts +0 -0
  419. /package/src/{core/tools/task → task}/name-generator.ts +0 -0
  420. /package/src/{core/tools/task → task}/omp-command.ts +0 -0
  421. /package/src/{core/tools/task → task}/parallel.ts +0 -0
  422. /package/src/{core/tools → tools}/jtd-to-json-schema.ts +0 -0
  423. /package/src/{core/tools → tools}/path-utils.ts +0 -0
  424. /package/src/{core → utils}/event-bus.ts +0 -0
  425. /package/src/{core → utils}/frontmatter.ts +0 -0
  426. /package/src/{core → utils}/terminal-notify.ts +0 -0
  427. /package/src/{core → utils}/timings.ts +0 -0
  428. /package/src/{core → utils}/utils.ts +0 -0
  429. /package/src/{core/tools/web-scrapers → web/scrapers}/artifacthub.ts +0 -0
  430. /package/src/{core/tools/web-scrapers → web/scrapers}/arxiv.ts +0 -0
  431. /package/src/{core/tools/web-scrapers → web/scrapers}/aur.ts +0 -0
  432. /package/src/{core/tools/web-scrapers → web/scrapers}/biorxiv.ts +0 -0
  433. /package/src/{core/tools/web-scrapers → web/scrapers}/bluesky.ts +0 -0
  434. /package/src/{core/tools/web-scrapers → web/scrapers}/brew.ts +0 -0
  435. /package/src/{core/tools/web-scrapers → web/scrapers}/cheatsh.ts +0 -0
  436. /package/src/{core/tools/web-scrapers → web/scrapers}/chocolatey.ts +0 -0
  437. /package/src/{core/tools/web-scrapers → web/scrapers}/cisa-kev.ts +0 -0
  438. /package/src/{core/tools/web-scrapers → web/scrapers}/clojars.ts +0 -0
  439. /package/src/{core/tools/web-scrapers → web/scrapers}/coingecko.ts +0 -0
  440. /package/src/{core/tools/web-scrapers → web/scrapers}/crates-io.ts +0 -0
  441. /package/src/{core/tools/web-scrapers → web/scrapers}/crossref.ts +0 -0
  442. /package/src/{core/tools/web-scrapers → web/scrapers}/devto.ts +0 -0
  443. /package/src/{core/tools/web-scrapers → web/scrapers}/discogs.ts +0 -0
  444. /package/src/{core/tools/web-scrapers → web/scrapers}/discourse.ts +0 -0
  445. /package/src/{core/tools/web-scrapers → web/scrapers}/dockerhub.ts +0 -0
  446. /package/src/{core/tools/web-scrapers → web/scrapers}/fdroid.ts +0 -0
  447. /package/src/{core/tools/web-scrapers → web/scrapers}/firefox-addons.ts +0 -0
  448. /package/src/{core/tools/web-scrapers → web/scrapers}/flathub.ts +0 -0
  449. /package/src/{core/tools/web-scrapers → web/scrapers}/github-gist.ts +0 -0
  450. /package/src/{core/tools/web-scrapers → web/scrapers}/github.ts +0 -0
  451. /package/src/{core/tools/web-scrapers → web/scrapers}/gitlab.ts +0 -0
  452. /package/src/{core/tools/web-scrapers → web/scrapers}/go-pkg.ts +0 -0
  453. /package/src/{core/tools/web-scrapers → web/scrapers}/hackage.ts +0 -0
  454. /package/src/{core/tools/web-scrapers → web/scrapers}/hackernews.ts +0 -0
  455. /package/src/{core/tools/web-scrapers → web/scrapers}/hex.ts +0 -0
  456. /package/src/{core/tools/web-scrapers → web/scrapers}/huggingface.ts +0 -0
  457. /package/src/{core/tools/web-scrapers → web/scrapers}/iacr.ts +0 -0
  458. /package/src/{core/tools/web-scrapers → web/scrapers}/index.ts +0 -0
  459. /package/src/{core/tools/web-scrapers → web/scrapers}/jetbrains-marketplace.ts +0 -0
  460. /package/src/{core/tools/web-scrapers → web/scrapers}/lemmy.ts +0 -0
  461. /package/src/{core/tools/web-scrapers → web/scrapers}/lobsters.ts +0 -0
  462. /package/src/{core/tools/web-scrapers → web/scrapers}/mastodon.ts +0 -0
  463. /package/src/{core/tools/web-scrapers → web/scrapers}/maven.ts +0 -0
  464. /package/src/{core/tools/web-scrapers → web/scrapers}/mdn.ts +0 -0
  465. /package/src/{core/tools/web-scrapers → web/scrapers}/metacpan.ts +0 -0
  466. /package/src/{core/tools/web-scrapers → web/scrapers}/musicbrainz.ts +0 -0
  467. /package/src/{core/tools/web-scrapers → web/scrapers}/npm.ts +0 -0
  468. /package/src/{core/tools/web-scrapers → web/scrapers}/nuget.ts +0 -0
  469. /package/src/{core/tools/web-scrapers → web/scrapers}/nvd.ts +0 -0
  470. /package/src/{core/tools/web-scrapers → web/scrapers}/ollama.ts +0 -0
  471. /package/src/{core/tools/web-scrapers → web/scrapers}/open-vsx.ts +0 -0
  472. /package/src/{core/tools/web-scrapers → web/scrapers}/opencorporates.ts +0 -0
  473. /package/src/{core/tools/web-scrapers → web/scrapers}/openlibrary.ts +0 -0
  474. /package/src/{core/tools/web-scrapers → web/scrapers}/orcid.ts +0 -0
  475. /package/src/{core/tools/web-scrapers → web/scrapers}/osv.ts +0 -0
  476. /package/src/{core/tools/web-scrapers → web/scrapers}/packagist.ts +0 -0
  477. /package/src/{core/tools/web-scrapers → web/scrapers}/pub-dev.ts +0 -0
  478. /package/src/{core/tools/web-scrapers → web/scrapers}/pubmed.ts +0 -0
  479. /package/src/{core/tools/web-scrapers → web/scrapers}/pypi.ts +0 -0
  480. /package/src/{core/tools/web-scrapers → web/scrapers}/rawg.ts +0 -0
  481. /package/src/{core/tools/web-scrapers → web/scrapers}/readthedocs.ts +0 -0
  482. /package/src/{core/tools/web-scrapers → web/scrapers}/reddit.ts +0 -0
  483. /package/src/{core/tools/web-scrapers → web/scrapers}/repology.ts +0 -0
  484. /package/src/{core/tools/web-scrapers → web/scrapers}/rfc.ts +0 -0
  485. /package/src/{core/tools/web-scrapers → web/scrapers}/rubygems.ts +0 -0
  486. /package/src/{core/tools/web-scrapers → web/scrapers}/searchcode.ts +0 -0
  487. /package/src/{core/tools/web-scrapers → web/scrapers}/sec-edgar.ts +0 -0
  488. /package/src/{core/tools/web-scrapers → web/scrapers}/semantic-scholar.ts +0 -0
  489. /package/src/{core/tools/web-scrapers → web/scrapers}/snapcraft.ts +0 -0
  490. /package/src/{core/tools/web-scrapers → web/scrapers}/sourcegraph.ts +0 -0
  491. /package/src/{core/tools/web-scrapers → web/scrapers}/spdx.ts +0 -0
  492. /package/src/{core/tools/web-scrapers → web/scrapers}/spotify.ts +0 -0
  493. /package/src/{core/tools/web-scrapers → web/scrapers}/stackoverflow.ts +0 -0
  494. /package/src/{core/tools/web-scrapers → web/scrapers}/terraform.ts +0 -0
  495. /package/src/{core/tools/web-scrapers → web/scrapers}/tldr.ts +0 -0
  496. /package/src/{core/tools/web-scrapers → web/scrapers}/vimeo.ts +0 -0
  497. /package/src/{core/tools/web-scrapers → web/scrapers}/vscode-marketplace.ts +0 -0
  498. /package/src/{core/tools/web-scrapers → web/scrapers}/w3c.ts +0 -0
  499. /package/src/{core/tools/web-scrapers → web/scrapers}/wikidata.ts +0 -0
  500. /package/src/{core/tools/web-scrapers → web/scrapers}/wikipedia.ts +0 -0
  501. /package/src/{core/tools/web-search → web/search}/types.ts +0 -0
@@ -4,17 +4,22 @@ import type { ImageContent } from "@oh-my-pi/pi-ai";
4
4
  import type { Component } from "@oh-my-pi/pi-tui";
5
5
  import { Text, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
6
6
  import { type Static, Type } from "@sinclair/typebox";
7
- import { truncateToVisualLines } from "../../modes/interactive/components/visual-truncate";
8
- import { highlightCode, type Theme } from "../../modes/interactive/theme/theme";
9
- import pythonDescription from "../../prompts/tools/python.md" with { type: "text" };
10
- import type { RenderResultOptions } from "../custom-tools/types";
11
- import { renderPromptTemplate } from "../prompt-templates";
12
- import { executePython, getPreludeDocs, type PythonExecutorOptions } from "../python-executor";
13
- import type { PreludeHelper, PythonStatusEvent } from "../python-kernel";
7
+ import { renderPromptTemplate } from "$c/config/prompt-templates";
8
+ import type { RenderResultOptions } from "$c/extensibility/custom-tools/types";
9
+ import { executePython, getPreludeDocs, type PythonExecutorOptions } from "$c/ipy/executor";
10
+ import type { PreludeHelper, PythonStatusEvent } from "$c/ipy/kernel";
11
+ import { truncateToVisualLines } from "$c/modes/components/visual-truncate";
12
+ import { highlightCode, type Theme } from "$c/modes/theme/theme";
13
+ import pythonDescription from "$c/prompts/tools/python.md" with { type: "text" };
14
+ import { OutputSink, type OutputSummary } from "$c/session/streaming-output";
15
+ import type { OutputMeta } from "$c/tools/output-meta";
16
+ import { ToolAbortError, ToolError } from "$c/tools/tool-errors";
14
17
  import type { ToolSession } from "./index";
18
+ import { allocateOutputArtifact, createTailBuffer } from "./output-utils";
15
19
  import { resolveToCwd } from "./path-utils";
16
20
  import { getTreeBranch, getTreeContinuePrefix, shortenPath, ToolUIKit, truncate } from "./render-utils";
17
- import { DEFAULT_MAX_BYTES, formatTailTruncationNotice, type TruncationResult, truncateTail } from "./truncate";
21
+ import { toolResult } from "./tool-result";
22
+ import { DEFAULT_MAX_BYTES } from "./truncate";
18
23
 
19
24
  export const PYTHON_DEFAULT_PREVIEW_LINES = 10;
20
25
 
@@ -41,22 +46,14 @@ function groupPreludeHelpers(helpers: PreludeHelper[]): PreludeCategory[] {
41
46
  export const pythonSchema = Type.Object({
42
47
  cells: Type.Array(
43
48
  Type.Object({
44
- code: Type.String({
45
- description:
46
- "Python code for this cell. Keep it focused (imports, helper, test, use). No narrative text—put explanations in the assistant message or in the cell title.",
47
- }),
48
- title: Type.Optional(
49
- Type.String({ description: "Short label for the cell (e.g., 'imports', 'parse helper')." }),
50
- ),
49
+ code: Type.String({ description: "Python code to execute" }),
50
+ title: Type.Optional(Type.String({ description: "Cell label, e.g. 'imports', 'helper'" })),
51
51
  }),
52
- {
53
- description:
54
- "Python cells to execute sequentially. Each cell runs in the same kernel—imports and variables persist. Keep cells small: one logical step each (import, define, test, use). If a cell fails, fix only that cell; earlier cells' state remains.",
55
- },
52
+ { description: "Cells to execute sequentially in persistent kernel" },
56
53
  ),
57
- timeoutMs: Type.Optional(Type.Number({ description: "Timeout in milliseconds (default: 30000)" })),
58
- cwd: Type.Optional(Type.String({ description: "Working directory for the command (default: current directory)" })),
59
- reset: Type.Optional(Type.Boolean({ description: "Restart the kernel before executing this code" })),
54
+ timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (default: 30)" })),
55
+ cwd: Type.Optional(Type.String({ description: "Working directory (default: cwd)" })),
56
+ reset: Type.Optional(Type.Boolean({ description: "Restart kernel before execution" })),
60
57
  });
61
58
  export type PythonToolParams = Static<typeof pythonSchema>;
62
59
 
@@ -80,14 +77,13 @@ export interface PythonCellResult {
80
77
 
81
78
  export interface PythonToolDetails {
82
79
  cells?: PythonCellResult[];
83
- truncation?: TruncationResult;
84
- fullOutputPath?: string;
85
- fullOutput?: string;
86
80
  jsonOutputs?: unknown[];
87
81
  images?: ImageContent[];
88
82
  /** Structured status events from prelude helpers */
89
83
  statusEvents?: PythonStatusEvent[];
90
84
  isError?: boolean;
85
+ /** Structured output metadata for notices */
86
+ meta?: OutputMeta;
91
87
  }
92
88
 
93
89
  function formatJsonScalar(value: unknown): string {
@@ -171,17 +167,31 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
171
167
  }
172
168
 
173
169
  if (!this.session) {
174
- throw new Error("Python tool requires a session when not using proxy executor");
170
+ throw new ToolError("Python tool requires a session when not using proxy executor");
175
171
  }
176
172
 
177
- const { cells, timeoutMs = 30000, cwd, reset } = params;
173
+ const { cells, timeout: rawTimeout = 30, cwd, reset } = params;
174
+ // Auto-convert milliseconds to seconds if value > 1000 (16+ min is unreasonable)
175
+ let timeoutSec = rawTimeout > 1000 ? rawTimeout / 1000 : rawTimeout;
176
+ // Clamp to reasonable range: 1s - 600s (10 min)
177
+ timeoutSec = Math.max(1, Math.min(600, timeoutSec));
178
+ const timeoutMs = timeoutSec * 1000;
178
179
  const controller = new AbortController();
179
180
  const onAbort = () => controller.abort();
180
181
  signal?.addEventListener("abort", onAbort, { once: true });
182
+ let outputSink: OutputSink | undefined;
183
+ let outputSummary: OutputSummary | undefined;
184
+ let outputDumped = false;
185
+ const finalizeOutput = async (): Promise<OutputSummary | undefined> => {
186
+ if (outputDumped || !outputSink) return outputSummary;
187
+ outputSummary = await outputSink.dump();
188
+ outputDumped = true;
189
+ return outputSummary;
190
+ };
181
191
 
182
192
  try {
183
193
  if (signal?.aborted) {
184
- throw new Error("Aborted");
194
+ throw new ToolAbortError();
185
195
  }
186
196
 
187
197
  const commandCwd = cwd ? resolveToCwd(cwd, this.session.cwd) : this.session.cwd;
@@ -189,15 +199,13 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
189
199
  try {
190
200
  cwdStat = await Bun.file(commandCwd).stat();
191
201
  } catch {
192
- throw new Error(`Working directory does not exist: ${commandCwd}`);
202
+ throw new ToolError(`Working directory does not exist: ${commandCwd}`);
193
203
  }
194
204
  if (!cwdStat.isDirectory()) {
195
- throw new Error(`Working directory is not a directory: ${commandCwd}`);
205
+ throw new ToolError(`Working directory is not a directory: ${commandCwd}`);
196
206
  }
197
207
 
198
- const maxTailBytes = DEFAULT_MAX_BYTES * 2;
199
- const tailChunks: Array<{ text: string; bytes: number }> = [];
200
- let tailBytes = 0;
208
+ const tailBuffer = createTailBuffer(DEFAULT_MAX_BYTES * 2);
201
209
  const jsonOutputs: unknown[] = [];
202
210
  const images: ImageContent[] = [];
203
211
  const statusEvents: PythonStatusEvent[] = [];
@@ -210,34 +218,18 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
210
218
  status: "pending",
211
219
  }));
212
220
  const cellOutputs: string[] = [];
213
- let lastFullOutputPath: string | undefined;
214
221
 
215
222
  const appendTail = (text: string) => {
216
- if (!text) return;
217
- const chunkBytes = Buffer.byteLength(text, "utf-8");
218
- tailChunks.push({ text, bytes: chunkBytes });
219
- tailBytes += chunkBytes;
220
- while (tailBytes > maxTailBytes && tailChunks.length > 1) {
221
- const removed = tailChunks.shift();
222
- if (removed) {
223
- tailBytes -= removed.bytes;
224
- }
225
- }
223
+ tailBuffer.append(text);
226
224
  };
227
225
 
228
- const buildUpdateDetails = (truncation?: TruncationResult): PythonToolDetails => {
226
+ const buildUpdateDetails = (): PythonToolDetails => {
229
227
  const details: PythonToolDetails = {
230
228
  cells: cellResults.map((cell) => ({
231
229
  ...cell,
232
230
  statusEvents: cell.statusEvents ? [...cell.statusEvents] : undefined,
233
231
  })),
234
232
  };
235
- if (truncation) {
236
- details.truncation = truncation;
237
- }
238
- if (lastFullOutputPath) {
239
- details.fullOutputPath = lastFullOutputPath;
240
- }
241
233
  if (jsonOutputs.length > 0) {
242
234
  details.jsonOutputs = jsonOutputs;
243
235
  }
@@ -252,15 +244,24 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
252
244
 
253
245
  const pushUpdate = () => {
254
246
  if (!onUpdate) return;
255
- const tailText = tailChunks.map((entry) => entry.text).join("");
256
- const truncation = truncateTail(tailText);
247
+ const tailText = tailBuffer.text();
257
248
  onUpdate({
258
- content: [{ type: "text", text: truncation.content || "" }],
259
- details: buildUpdateDetails(truncation.truncated ? truncation : undefined),
249
+ content: [{ type: "text", text: tailText }],
250
+ details: buildUpdateDetails(),
260
251
  });
261
252
  };
262
253
 
263
254
  const sessionFile = this.session.getSessionFile?.() ?? undefined;
255
+ const artifactsDir = this.session.getArtifactsDir?.() ?? undefined;
256
+ const { artifactPath, artifactId } = await allocateOutputArtifact(this.session, "python");
257
+ outputSink = new OutputSink({
258
+ artifactPath,
259
+ artifactId,
260
+ onChunk: (chunk) => {
261
+ appendTail(chunk);
262
+ pushUpdate();
263
+ },
264
+ });
264
265
  const sessionId = sessionFile ? `session:${sessionFile}:cwd:${commandCwd}` : `cwd:${commandCwd}`;
265
266
  const baseExecutorOptions: Omit<PythonExecutorOptions, "reset"> = {
266
267
  cwd: commandCwd,
@@ -270,6 +271,7 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
270
271
  kernelMode: this.session.settings?.getPythonKernelMode?.() ?? "session",
271
272
  useSharedGateway: this.session.settings?.getPythonSharedGateway?.() ?? true,
272
273
  sessionFile: sessionFile ?? undefined,
274
+ artifactsDir: artifactsDir ?? undefined,
273
275
  };
274
276
 
275
277
  for (let i = 0; i < cells.length; i++) {
@@ -286,6 +288,9 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
286
288
  const executorOptions: PythonExecutorOptions = {
287
289
  ...baseExecutorOptions,
288
290
  reset: isFirstCell ? reset : false,
291
+ onChunk: async (chunk) => {
292
+ await outputSink!.push(chunk);
293
+ },
289
294
  };
290
295
 
291
296
  const startTime = Date.now();
@@ -306,10 +311,6 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
306
311
  }
307
312
  }
308
313
 
309
- if (result.fullOutputPath) {
310
- lastFullOutputPath = result.fullOutputPath;
311
- }
312
-
313
314
  const cellOutput = result.output.trim();
314
315
  cellResult.output = cellOutput;
315
316
  cellResult.exitCode = result.exitCode;
@@ -340,14 +341,14 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
340
341
  cellResult.status = "error";
341
342
  pushUpdate();
342
343
  const errorMsg = result.output || "Command aborted";
343
- throw new Error(cells.length > 1 ? `Cell ${i + 1} aborted: ${errorMsg}` : errorMsg);
344
+ throw new ToolError(cells.length > 1 ? `Cell ${i + 1} aborted: ${errorMsg}` : errorMsg);
344
345
  }
345
346
 
346
347
  if (result.exitCode !== 0 && result.exitCode !== undefined) {
347
348
  cellResult.status = "error";
348
349
  pushUpdate();
349
350
  const combinedOutput = cellOutputs.join("\n\n");
350
- throw new Error(
351
+ throw new ToolError(
351
352
  cells.length > 1
352
353
  ? `${combinedOutput}\n\nCell ${i + 1} failed (exit code ${result.exitCode}). Earlier cells succeeded—their state persists. Fix only cell ${i + 1}.`
353
354
  : `${combinedOutput}\n\nCommand exited with code ${result.exitCode}`,
@@ -359,29 +360,49 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
359
360
  }
360
361
 
361
362
  const combinedOutput = cellOutputs.join("\n\n");
362
- const truncation = truncateTail(combinedOutput);
363
- let outputText =
364
- truncation.content || (jsonOutputs.length > 0 || images.length > 0 ? "(no text output)" : "(no output)");
363
+ const outputText =
364
+ combinedOutput || (jsonOutputs.length > 0 || images.length > 0 ? "(no text output)" : "(no output)");
365
+ const rawSummary = (await finalizeOutput()) ?? {
366
+ output: "",
367
+ truncated: false,
368
+ totalLines: 0,
369
+ totalBytes: 0,
370
+ outputLines: 0,
371
+ outputBytes: 0,
372
+ };
373
+ const outputLines = combinedOutput.length > 0 ? combinedOutput.split("\n").length : 0;
374
+ const outputBytes = Buffer.byteLength(combinedOutput, "utf-8");
375
+ const missingLines = Math.max(0, rawSummary.totalLines - rawSummary.outputLines);
376
+ const missingBytes = Math.max(0, rawSummary.totalBytes - rawSummary.outputBytes);
377
+ const summaryForMeta: OutputSummary = {
378
+ output: combinedOutput,
379
+ truncated: rawSummary.truncated,
380
+ totalLines: outputLines + missingLines,
381
+ totalBytes: outputBytes + missingBytes,
382
+ outputLines,
383
+ outputBytes,
384
+ artifactId: rawSummary.artifactId,
385
+ };
365
386
 
366
387
  const details: PythonToolDetails = {
367
388
  cells: cellResults,
368
- fullOutputPath: lastFullOutputPath,
369
389
  jsonOutputs: jsonOutputs.length > 0 ? jsonOutputs : undefined,
370
390
  images: images.length > 0 ? images : undefined,
371
391
  statusEvents: statusEvents.length > 0 ? statusEvents : undefined,
372
392
  };
373
393
 
374
- if (truncation.truncated) {
375
- details.truncation = truncation;
376
- outputText += formatTailTruncationNotice(truncation, {
377
- fullOutputPath: lastFullOutputPath,
378
- originalContent: combinedOutput,
379
- });
380
- }
394
+ const resultBuilder = toolResult(details)
395
+ .text(outputText)
396
+ .truncationFromSummary(summaryForMeta, { direction: "tail" });
381
397
 
382
- return { content: [{ type: "text", text: outputText }], details };
398
+ return resultBuilder.done();
383
399
  } finally {
384
400
  signal?.removeEventListener("abort", onAbort);
401
+ if (!outputDumped) {
402
+ try {
403
+ await finalizeOutput();
404
+ } catch {}
405
+ }
385
406
  }
386
407
  }
387
408
  }
@@ -955,8 +976,6 @@ export const pythonToolRenderer = {
955
976
  const expanded = renderContext?.expanded ?? options.expanded;
956
977
  const previewLines = renderContext?.previewLines ?? PYTHON_DEFAULT_PREVIEW_LINES;
957
978
  const output = renderContext?.output ?? (result.content?.find((c) => c.type === "text")?.text ?? "").trim();
958
- const fullOutput = details?.fullOutput;
959
- const showingFullOutput = expanded && fullOutput !== undefined;
960
979
 
961
980
  const jsonOutputs = details?.jsonOutputs ?? [];
962
981
  const jsonLines = jsonOutputs.flatMap((value, index) => {
@@ -965,27 +984,24 @@ export const pythonToolRenderer = {
965
984
  return [header, ...treeLines];
966
985
  });
967
986
 
968
- const truncation = details?.truncation;
969
- const fullOutputPath = details?.fullOutputPath;
987
+ const truncation = details?.meta?.truncation;
970
988
  const timeoutSeconds = renderContext?.timeout;
971
989
  const timeoutLine =
972
990
  typeof timeoutSeconds === "number"
973
991
  ? uiTheme.fg("dim", ui.wrapBrackets(`Timeout: ${timeoutSeconds}s`))
974
992
  : undefined;
975
993
  let warningLine: string | undefined;
976
- if (fullOutputPath || (truncation?.truncated && !showingFullOutput)) {
994
+ if (truncation) {
977
995
  const warnings: string[] = [];
978
- if (fullOutputPath) {
979
- warnings.push(`Full output: ${fullOutputPath}`);
996
+ if (truncation.artifactId) {
997
+ warnings.push(`Full output: artifact://${truncation.artifactId}`);
980
998
  }
981
- if (truncation?.truncated && !showingFullOutput) {
982
- if (truncation.truncatedBy === "lines") {
983
- warnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);
984
- } else {
985
- warnings.push(
986
- `Truncated: ${truncation.outputLines} lines shown (${ui.formatBytes(truncation.maxBytes)} limit)`,
987
- );
988
- }
999
+ if (truncation.truncatedBy === "lines") {
1000
+ warnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);
1001
+ } else {
1002
+ warnings.push(
1003
+ `Truncated: ${truncation.outputLines} lines shown (${ui.formatBytes(truncation.outputBytes)} limit)`,
1004
+ );
989
1005
  }
990
1006
  if (warnings.length > 0) {
991
1007
  warningLine = uiTheme.fg("warning", ui.wrapBrackets(warnings.join(". ")));
@@ -1038,7 +1054,7 @@ export const pythonToolRenderer = {
1038
1054
  };
1039
1055
  }
1040
1056
 
1041
- const displayOutput = expanded ? (fullOutput ?? output) : output;
1057
+ const displayOutput = output;
1042
1058
  const combinedOutput = [displayOutput, ...jsonLines].filter(Boolean).join("\n");
1043
1059
 
1044
1060
  const statusEvents = details?.statusEvents ?? [];