@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
@@ -6,8 +6,8 @@ import {
6
6
  type KernelExecuteResult,
7
7
  type PreludeHelper,
8
8
  PythonKernel,
9
- } from "./python-kernel";
10
- import { OutputSink } from "./streaming-output";
9
+ } from "$c/ipy/kernel";
10
+ import { OutputSink } from "$c/session/streaming-output";
11
11
  export type PythonKernelMode = "session" | "per-call";
12
12
 
13
13
  export interface PythonExecutorOptions {
@@ -27,6 +27,13 @@ export interface PythonExecutorOptions {
27
27
  reset?: boolean;
28
28
  /** Use shared gateway across pi instances (default: true) */
29
29
  useSharedGateway?: boolean;
30
+ /** Session file path for accessing task outputs */
31
+ sessionFile?: string;
32
+ /** Artifacts directory for $ARTIFACTS env var and artifact storage */
33
+ artifactsDir?: string;
34
+ /** Artifact path/id for full output storage */
35
+ artifactPath?: string;
36
+ artifactId?: string;
30
37
  }
31
38
 
32
39
  export interface PythonKernelExecutor {
@@ -42,8 +49,16 @@ export interface PythonResult {
42
49
  cancelled: boolean;
43
50
  /** Whether the output was truncated */
44
51
  truncated: boolean;
45
- /** Path to temp file containing full output (if output exceeded truncation threshold) */
46
- fullOutputPath?: string;
52
+ /** Artifact ID if full output was saved to artifact storage */
53
+ artifactId?: string;
54
+ /** Total number of lines in the output stream */
55
+ totalLines: number;
56
+ /** Total number of bytes in the output stream */
57
+ totalBytes: number;
58
+ /** Number of lines included in the output text */
59
+ outputLines: number;
60
+ /** Number of bytes included in the output text */
61
+ outputBytes: number;
47
62
  /** Rich display outputs captured from display_data/execute_result */
48
63
  displayOutputs: KernelDisplayOutput[];
49
64
  /** Whether stdin was requested */
@@ -79,6 +94,7 @@ export async function warmPythonEnvironment(
79
94
  cwd: string,
80
95
  sessionId?: string,
81
96
  useSharedGateway?: boolean,
97
+ sessionFile?: string,
82
98
  ): Promise<{ ok: boolean; reason?: string; docs: PreludeHelper[] }> {
83
99
  try {
84
100
  await ensureKernelAvailable(cwd);
@@ -97,6 +113,7 @@ export async function warmPythonEnvironment(
97
113
  cwd,
98
114
  async (kernel) => kernel.introspectPrelude(),
99
115
  useSharedGateway,
116
+ sessionFile,
100
117
  );
101
118
  cachedPreludeDocs = docs;
102
119
  return { ok: true, docs };
@@ -115,8 +132,21 @@ export function resetPreludeDocsCache(): void {
115
132
  cachedPreludeDocs = null;
116
133
  }
117
134
 
118
- async function createKernelSession(sessionId: string, cwd: string, useSharedGateway?: boolean): Promise<KernelSession> {
119
- const kernel = await PythonKernel.start({ cwd, useSharedGateway });
135
+ async function createKernelSession(
136
+ sessionId: string,
137
+ cwd: string,
138
+ useSharedGateway?: boolean,
139
+ sessionFile?: string,
140
+ artifactsDir?: string,
141
+ ): Promise<KernelSession> {
142
+ const env: Record<string, string> | undefined =
143
+ sessionFile || artifactsDir
144
+ ? {
145
+ ...(sessionFile ? { OMP_SESSION_FILE: sessionFile } : {}),
146
+ ...(artifactsDir ? { ARTIFACTS: artifactsDir } : {}),
147
+ }
148
+ : undefined;
149
+ const kernel = await PythonKernel.start({ cwd, useSharedGateway, env });
120
150
  const session: KernelSession = {
121
151
  id: sessionId,
122
152
  kernel,
@@ -137,7 +167,13 @@ async function createKernelSession(sessionId: string, cwd: string, useSharedGate
137
167
  return session;
138
168
  }
139
169
 
140
- async function restartKernelSession(session: KernelSession, cwd: string, useSharedGateway?: boolean): Promise<void> {
170
+ async function restartKernelSession(
171
+ session: KernelSession,
172
+ cwd: string,
173
+ useSharedGateway?: boolean,
174
+ sessionFile?: string,
175
+ artifactsDir?: string,
176
+ ): Promise<void> {
141
177
  session.restartCount += 1;
142
178
  if (session.restartCount > 1) {
143
179
  throw new Error("Python kernel restarted too many times in this session");
@@ -147,7 +183,14 @@ async function restartKernelSession(session: KernelSession, cwd: string, useShar
147
183
  } catch (err) {
148
184
  logger.warn("Failed to shutdown crashed kernel", { error: err instanceof Error ? err.message : String(err) });
149
185
  }
150
- const kernel = await PythonKernel.start({ cwd, useSharedGateway });
186
+ const env: Record<string, string> | undefined =
187
+ sessionFile || artifactsDir
188
+ ? {
189
+ ...(sessionFile ? { OMP_SESSION_FILE: sessionFile } : {}),
190
+ ...(artifactsDir ? { ARTIFACTS: artifactsDir } : {}),
191
+ }
192
+ : undefined;
193
+ const kernel = await PythonKernel.start({ cwd, useSharedGateway, env });
151
194
  session.kernel = kernel;
152
195
  session.dead = false;
153
196
  session.lastUsedAt = Date.now();
@@ -170,17 +213,19 @@ async function withKernelSession<T>(
170
213
  cwd: string,
171
214
  handler: (kernel: PythonKernel) => Promise<T>,
172
215
  useSharedGateway?: boolean,
216
+ sessionFile?: string,
217
+ artifactsDir?: string,
173
218
  ): Promise<T> {
174
219
  let session = kernelSessions.get(sessionId);
175
220
  if (!session) {
176
- session = await createKernelSession(sessionId, cwd, useSharedGateway);
221
+ session = await createKernelSession(sessionId, cwd, useSharedGateway, sessionFile, artifactsDir);
177
222
  kernelSessions.set(sessionId, session);
178
223
  }
179
224
 
180
225
  const run = async (): Promise<T> => {
181
226
  session!.lastUsedAt = Date.now();
182
227
  if (session!.dead || !session!.kernel.isAlive()) {
183
- await restartKernelSession(session!, cwd, useSharedGateway);
228
+ await restartKernelSession(session!, cwd, useSharedGateway, sessionFile, artifactsDir);
184
229
  }
185
230
  try {
186
231
  const result = await handler(session!.kernel);
@@ -190,7 +235,7 @@ async function withKernelSession<T>(
190
235
  if (!session!.dead && session!.kernel.isAlive()) {
191
236
  throw err;
192
237
  }
193
- await restartKernelSession(session!, cwd, useSharedGateway);
238
+ await restartKernelSession(session!, cwd, useSharedGateway, sessionFile, artifactsDir);
194
239
  const result = await handler(session!.kernel);
195
240
  session!.restartCount = 0;
196
241
  return result;
@@ -210,7 +255,11 @@ async function executeWithKernel(
210
255
  code: string,
211
256
  options: PythonExecutorOptions | undefined,
212
257
  ): Promise<PythonResult> {
213
- const sink = new OutputSink({ onChunk: options?.onChunk });
258
+ const sink = new OutputSink({
259
+ onChunk: options?.onChunk,
260
+ artifactPath: options?.artifactPath,
261
+ artifactId: options?.artifactId,
262
+ });
214
263
  const displayOutputs: KernelDisplayOutput[] = [];
215
264
 
216
265
  try {
@@ -273,8 +322,18 @@ export async function executePython(code: string, options?: PythonExecutorOption
273
322
 
274
323
  const kernelMode = options?.kernelMode ?? "session";
275
324
  const useSharedGateway = options?.useSharedGateway;
325
+ const sessionFile = options?.sessionFile;
326
+ const artifactsDir = options?.artifactsDir;
327
+
276
328
  if (kernelMode === "per-call") {
277
- const kernel = await PythonKernel.start({ cwd, useSharedGateway });
329
+ const env: Record<string, string> | undefined =
330
+ sessionFile || artifactsDir
331
+ ? {
332
+ ...(sessionFile ? { OMP_SESSION_FILE: sessionFile } : {}),
333
+ ...(artifactsDir ? { ARTIFACTS: artifactsDir } : {}),
334
+ }
335
+ : undefined;
336
+ const kernel = await PythonKernel.start({ cwd, useSharedGateway, env });
278
337
  try {
279
338
  return await executeWithKernel(kernel, code, options);
280
339
  } finally {
@@ -294,5 +353,7 @@ export async function executePython(code: string, options?: PythonExecutorOption
294
353
  cwd,
295
354
  async (kernel) => executeWithKernel(kernel, code, options),
296
355
  useSharedGateway,
356
+ sessionFile,
357
+ artifactsDir,
297
358
  );
298
359
  }
@@ -3,7 +3,6 @@ import {
3
3
  existsSync,
4
4
  mkdirSync,
5
5
  openSync,
6
- readdirSync,
7
6
  readFileSync,
8
7
  renameSync,
9
8
  statSync,
@@ -15,16 +14,14 @@ import { createServer } from "node:net";
15
14
  import { delimiter, join } from "node:path";
16
15
  import { logger } from "@oh-my-pi/pi-utils";
17
16
  import type { Subprocess } from "bun";
18
- import { getAgentDir } from "../config";
19
- import { getShellConfig, killProcessTree } from "../utils/shell";
20
- import { getOrCreateSnapshot } from "../utils/shell-snapshot";
17
+ import { getAgentDir } from "$c/config";
18
+ import { getShellConfig, killProcessTree } from "$c/utils/shell";
19
+ import { getOrCreateSnapshot } from "$c/utils/shell-snapshot";
21
20
 
22
21
  const GATEWAY_DIR_NAME = "python-gateway";
23
22
  const GATEWAY_INFO_FILE = "gateway.json";
24
23
  const GATEWAY_LOCK_FILE = "gateway.lock";
25
- const GATEWAY_CLIENT_PREFIX = "client-";
26
24
  const GATEWAY_STARTUP_TIMEOUT_MS = 30000;
27
- const GATEWAY_IDLE_TIMEOUT_MS = 30000;
28
25
  const GATEWAY_LOCK_TIMEOUT_MS = GATEWAY_STARTUP_TIMEOUT_MS + 5000;
29
26
  const GATEWAY_LOCK_RETRY_MS = 50;
30
27
  const GATEWAY_LOCK_STALE_MS = GATEWAY_STARTUP_TIMEOUT_MS * 2;
@@ -140,8 +137,6 @@ export interface GatewayInfo {
140
137
  url: string;
141
138
  pid: number;
142
139
  startedAt: number;
143
- refCount: number;
144
- cwd: string;
145
140
  pythonPath?: string;
146
141
  venvPath?: string | null;
147
142
  }
@@ -158,9 +153,7 @@ interface AcquireResult {
158
153
 
159
154
  let localGatewayProcess: Subprocess | null = null;
160
155
  let localGatewayUrl: string | null = null;
161
- let idleShutdownTimer: ReturnType<typeof setTimeout> | null = null;
162
156
  let isCoordinatorInitialized = false;
163
- let localClientFile: string | null = null;
164
157
 
165
158
  function filterEnv(env: Record<string, string | undefined>): Record<string, string | undefined> {
166
159
  const filtered: Record<string, string | undefined> = {};
@@ -346,6 +339,7 @@ async function withGatewayLock<T>(handler: () => Promise<T>): Promise<T> {
346
339
  function readGatewayInfo(): GatewayInfo | null {
347
340
  const infoPath = getGatewayInfoPath();
348
341
  if (!existsSync(infoPath)) return null;
342
+
349
343
  try {
350
344
  const content = readFileSync(infoPath, "utf-8");
351
345
  const parsed = JSON.parse(content) as Partial<GatewayInfo>;
@@ -353,16 +347,10 @@ function readGatewayInfo(): GatewayInfo | null {
353
347
  if (typeof parsed.url !== "string" || typeof parsed.pid !== "number" || typeof parsed.startedAt !== "number") {
354
348
  return null;
355
349
  }
356
- if (typeof parsed.cwd !== "string") return null;
357
- const clients = pruneStaleClientInfos(listClientInfos());
358
- const totalRefCount = clients.reduce((sum, client) => sum + client.info.refCount, 0);
359
- const recoveredRefCount = clients.length > 0 ? totalRefCount : 0;
360
350
  return {
361
351
  url: parsed.url,
362
352
  pid: parsed.pid,
363
353
  startedAt: parsed.startedAt,
364
- refCount: recoveredRefCount,
365
- cwd: parsed.cwd,
366
354
  pythonPath: typeof parsed.pythonPath === "string" ? parsed.pythonPath : undefined,
367
355
  venvPath: typeof parsed.venvPath === "string" || parsed.venvPath === null ? parsed.venvPath : undefined,
368
356
  };
@@ -398,103 +386,6 @@ function isPidRunning(pid: number): boolean {
398
386
  }
399
387
  }
400
388
 
401
- interface GatewayClientInfo {
402
- pid: number;
403
- refCount: number;
404
- updatedAt?: number;
405
- }
406
-
407
- function getClientFilePath(pid: number): string {
408
- return join(getGatewayDir(), `${GATEWAY_CLIENT_PREFIX}${pid}.json`);
409
- }
410
-
411
- function readClientInfo(path: string): GatewayClientInfo | null {
412
- try {
413
- const raw = readFileSync(path, "utf-8");
414
- const parsed = JSON.parse(raw) as GatewayClientInfo;
415
- if (typeof parsed.pid !== "number" || typeof parsed.refCount !== "number") return null;
416
- return parsed;
417
- } catch {
418
- return null;
419
- }
420
- }
421
-
422
- function listClientInfos(): Array<{ path: string; info: GatewayClientInfo }> {
423
- const dir = getGatewayDir();
424
- if (!existsSync(dir)) return [];
425
- const entries = readdirSync(dir);
426
- const results: Array<{ path: string; info: GatewayClientInfo }> = [];
427
- for (const entry of entries) {
428
- if (!entry.startsWith(GATEWAY_CLIENT_PREFIX)) continue;
429
- const path = join(dir, entry);
430
- const info = readClientInfo(path);
431
- if (!info) continue;
432
- results.push({ path, info });
433
- }
434
- return results;
435
- }
436
-
437
- function pruneStaleClientInfos(
438
- clients: Array<{ path: string; info: GatewayClientInfo }>,
439
- ): Array<{ path: string; info: GatewayClientInfo }> {
440
- const active: Array<{ path: string; info: GatewayClientInfo }> = [];
441
- for (const client of clients) {
442
- if (!isPidRunning(client.info.pid)) {
443
- try {
444
- unlinkSync(client.path);
445
- } catch {
446
- // Ignore cleanup errors
447
- }
448
- continue;
449
- }
450
- active.push(client);
451
- }
452
- return active;
453
- }
454
-
455
- function updateLocalClientRefCount(delta: number): { totalRefCount: number; localRefCount: number } {
456
- ensureGatewayDir();
457
- const clients = pruneStaleClientInfos(listClientInfos());
458
- const localPath = localClientFile ?? getClientFilePath(process.pid);
459
- const localEntry = clients.find((client) => client.info.pid === process.pid);
460
- const baseCount = localEntry?.info.refCount ?? 0;
461
- const nextCount = Math.max(0, baseCount + delta);
462
- const otherClients = clients.filter((client) => client.info.pid !== process.pid);
463
-
464
- if (nextCount <= 0) {
465
- if (localEntry) {
466
- try {
467
- unlinkSync(localEntry.path);
468
- } catch {
469
- // Ignore cleanup errors
470
- }
471
- }
472
- if (localClientFile === localPath) {
473
- localClientFile = null;
474
- }
475
- } else {
476
- const payload: GatewayClientInfo = { pid: process.pid, refCount: nextCount, updatedAt: Date.now() };
477
- writeFileSync(localPath, JSON.stringify(payload, null, 2));
478
- localClientFile = localPath;
479
- }
480
-
481
- const totalRefCount =
482
- otherClients.reduce((sum, client) => sum + client.info.refCount, 0) + (nextCount > 0 ? nextCount : 0);
483
- return { totalRefCount, localRefCount: nextCount };
484
- }
485
-
486
- function clearClientFiles(): void {
487
- const clients = listClientInfos();
488
- for (const client of clients) {
489
- try {
490
- unlinkSync(client.path);
491
- } catch {
492
- // Ignore cleanup errors
493
- }
494
- }
495
- localClientFile = null;
496
- }
497
-
498
389
  async function isGatewayHealthy(url: string): Promise<boolean> {
499
390
  try {
500
391
  const controller = new AbortController();
@@ -533,11 +424,6 @@ async function startGatewayProcess(
533
424
  OMP_SHELL_SNAPSHOT: snapshotPath ?? undefined,
534
425
  };
535
426
 
536
- const pythonPathParts = [cwd, kernelEnv.PYTHONPATH].filter(Boolean).join(delimiter);
537
- if (pythonPathParts) {
538
- kernelEnv.PYTHONPATH = pythonPathParts;
539
- }
540
-
541
427
  const gatewayPort = await allocatePort();
542
428
  const gatewayUrl = `http://127.0.0.1:${gatewayPort}`;
543
429
 
@@ -570,7 +456,6 @@ async function startGatewayProcess(
570
456
  exited = true;
571
457
  });
572
458
 
573
- // Wait for gateway to become healthy
574
459
  const startTime = Date.now();
575
460
  while (Date.now() - startTime < GATEWAY_STARTUP_TIMEOUT_MS) {
576
461
  if (exited) {
@@ -593,70 +478,15 @@ async function startGatewayProcess(
593
478
  throw new Error("Gateway startup timeout");
594
479
  }
595
480
 
596
- function scheduleIdleShutdown(): void {
597
- if (idleShutdownTimer) {
598
- clearTimeout(idleShutdownTimer);
599
- }
600
- idleShutdownTimer = setTimeout(async () => {
601
- try {
602
- await withGatewayLock(async () => {
603
- const info = readGatewayInfo();
604
- if (!info) {
605
- clearClientFiles();
606
- return;
607
- }
608
- const clients = pruneStaleClientInfos(listClientInfos());
609
- const totalRefCount = clients.reduce((sum, client) => sum + client.info.refCount, 0);
610
- if (totalRefCount > 0) {
611
- if (info.refCount !== totalRefCount) {
612
- writeGatewayInfo({ ...info, refCount: totalRefCount });
613
- }
614
- return;
615
- }
616
- logger.debug("Shutting down idle shared gateway", { pid: info.pid });
617
- if (localGatewayProcess) {
618
- await shutdownLocalGateway();
619
- } else if (isPidRunning(info.pid)) {
620
- try {
621
- await killProcessTree(info.pid);
622
- } catch (err) {
623
- logger.warn("Failed to kill idle shared gateway", {
624
- error: err instanceof Error ? err.message : String(err),
625
- pid: info.pid,
626
- });
627
- }
628
- }
629
- clearGatewayInfo();
630
- clearClientFiles();
631
- });
632
- } catch (err) {
633
- logger.warn("Failed to shutdown idle shared gateway", {
634
- error: err instanceof Error ? err.message : String(err),
635
- });
636
- } finally {
637
- idleShutdownTimer = null;
638
- }
639
- }, GATEWAY_IDLE_TIMEOUT_MS);
640
- }
641
-
642
- function cancelIdleShutdown(): void {
643
- if (idleShutdownTimer) {
644
- clearTimeout(idleShutdownTimer);
645
- idleShutdownTimer = null;
646
- }
647
- }
648
-
649
- async function shutdownLocalGateway(): Promise<void> {
650
- if (localGatewayProcess) {
651
- try {
652
- await killProcessTree(localGatewayProcess.pid);
653
- } catch (err) {
654
- logger.warn("Failed to kill shared gateway process", {
655
- error: err instanceof Error ? err.message : String(err),
656
- });
657
- }
658
- localGatewayProcess = null;
659
- localGatewayUrl = null;
481
+ async function killGateway(pid: number, context: string): Promise<void> {
482
+ try {
483
+ await killProcessTree(pid);
484
+ } catch (err) {
485
+ logger.warn("Failed to kill shared gateway process", {
486
+ error: err instanceof Error ? err.message : String(err),
487
+ pid,
488
+ context,
489
+ });
660
490
  }
661
491
  }
662
492
 
@@ -668,67 +498,32 @@ export async function acquireSharedGateway(cwd: string): Promise<AcquireResult |
668
498
  try {
669
499
  return await withGatewayLock(async () => {
670
500
  const existingInfo = readGatewayInfo();
671
- if (existingInfo && (await isGatewayAlive(existingInfo))) {
672
- const { env } = await getShellConfig();
673
- const filteredEnv = filterEnv(env);
674
- const runtime = await resolvePythonRuntime(cwd, filteredEnv);
675
- const existingVenv = existingInfo.venvPath ?? null;
676
- const runtimeVenv = runtime.venvPath ?? null;
677
- if (
678
- existingInfo.cwd !== cwd ||
679
- !existingInfo.pythonPath ||
680
- existingInfo.pythonPath !== runtime.pythonPath ||
681
- existingVenv !== runtimeVenv
682
- ) {
683
- logger.debug("Shared gateway metadata mismatch", {
684
- existingCwd: existingInfo.cwd,
685
- requestedCwd: cwd,
686
- existingPython: existingInfo.pythonPath,
687
- runtimePython: runtime.pythonPath,
688
- existingVenv,
689
- runtimeVenv,
690
- });
691
- return null;
501
+ if (existingInfo) {
502
+ if (await isGatewayAlive(existingInfo)) {
503
+ localGatewayUrl = existingInfo.url;
504
+ isCoordinatorInitialized = true;
505
+ logger.debug("Reusing global Python gateway", { url: existingInfo.url });
506
+ return { url: existingInfo.url, isShared: true };
692
507
  }
693
- const { totalRefCount } = updateLocalClientRefCount(1);
694
- const updatedInfo = { ...existingInfo, refCount: totalRefCount };
695
- writeGatewayInfo(updatedInfo);
696
- cancelIdleShutdown();
697
- logger.debug("Reusing shared gateway", { url: existingInfo.url, refCount: updatedInfo.refCount });
698
- isCoordinatorInitialized = true;
699
- return { url: existingInfo.url, isShared: true };
700
- }
701
508
 
702
- if (existingInfo) {
703
509
  logger.debug("Cleaning up stale gateway info", { pid: existingInfo.pid });
704
510
  if (isPidRunning(existingInfo.pid)) {
705
- try {
706
- await killProcessTree(existingInfo.pid);
707
- } catch (err) {
708
- logger.warn("Failed to kill stale shared gateway process", {
709
- error: err instanceof Error ? err.message : String(err),
710
- pid: existingInfo.pid,
711
- });
712
- }
511
+ await killGateway(existingInfo.pid, "stale");
713
512
  }
714
513
  clearGatewayInfo();
715
- clearClientFiles();
716
514
  }
717
515
 
718
516
  const { url, pid, pythonPath, venvPath } = await startGatewayProcess(cwd);
719
- const { totalRefCount } = updateLocalClientRefCount(1);
720
517
  const info: GatewayInfo = {
721
518
  url,
722
519
  pid,
723
520
  startedAt: Date.now(),
724
- refCount: totalRefCount,
725
- cwd,
726
521
  pythonPath,
727
522
  venvPath,
728
523
  };
729
524
  writeGatewayInfo(info);
730
525
  isCoordinatorInitialized = true;
731
- logger.debug("Started shared gateway", { url, pid });
526
+ logger.debug("Started global Python gateway", { url, pid });
732
527
  return { url, isShared: true };
733
528
  });
734
529
  } catch (err) {
@@ -741,48 +536,24 @@ export async function acquireSharedGateway(cwd: string): Promise<AcquireResult |
741
536
 
742
537
  export async function releaseSharedGateway(): Promise<void> {
743
538
  if (!isCoordinatorInitialized) return;
744
-
745
- try {
746
- await withGatewayLock(async () => {
747
- const { totalRefCount } = updateLocalClientRefCount(-1);
748
- const info = readGatewayInfo();
749
- if (!info) return;
750
-
751
- const newRefCount = Math.max(0, totalRefCount);
752
- if (newRefCount === 0) {
753
- const updatedInfo = { ...info, refCount: 0 };
754
- writeGatewayInfo(updatedInfo);
755
- scheduleIdleShutdown();
756
- logger.debug("Scheduled idle shutdown for shared gateway", { pid: info.pid });
757
- return;
758
- }
759
- const updatedInfo = { ...info, refCount: newRefCount };
760
- writeGatewayInfo(updatedInfo);
761
- logger.debug("Released shared gateway reference", { url: info.url, refCount: newRefCount });
762
- });
763
- } catch (err) {
764
- logger.warn("Failed to release shared gateway", {
765
- error: err instanceof Error ? err.message : String(err),
766
- });
767
- }
768
539
  }
769
540
 
770
541
  export function getSharedGatewayUrl(): string | null {
771
- return localGatewayUrl;
542
+ if (localGatewayUrl) return localGatewayUrl;
543
+ return readGatewayInfo()?.url ?? null;
772
544
  }
773
545
 
774
546
  export function isSharedGatewayActive(): boolean {
775
- return localGatewayProcess !== null && localGatewayUrl !== null;
547
+ return getGatewayStatus().active;
776
548
  }
777
549
 
778
550
  export interface GatewayStatus {
779
551
  active: boolean;
780
- shared: boolean;
781
552
  url: string | null;
782
553
  pid: number | null;
783
- refCount: number;
784
- cwd: string | null;
785
554
  uptime: number | null;
555
+ pythonPath: string | null;
556
+ venvPath: string | null;
786
557
  }
787
558
 
788
559
  export function getGatewayStatus(): GatewayStatus {
@@ -790,45 +561,44 @@ export function getGatewayStatus(): GatewayStatus {
790
561
  if (!info) {
791
562
  return {
792
563
  active: false,
793
- shared: false,
794
564
  url: null,
795
565
  pid: null,
796
- refCount: 0,
797
- cwd: null,
798
566
  uptime: null,
567
+ pythonPath: null,
568
+ venvPath: null,
799
569
  };
800
570
  }
801
571
  const active = isPidRunning(info.pid);
802
- const clients = pruneStaleClientInfos(listClientInfos());
803
- const clientRefCount = clients.reduce((sum, client) => sum + client.info.refCount, 0);
804
- const refCount = clientRefCount > 0 ? clientRefCount : info.refCount;
805
572
  return {
806
573
  active,
807
- shared: active && refCount > 1,
808
574
  url: info.url,
809
575
  pid: info.pid,
810
- refCount,
811
- cwd: info.cwd,
812
- uptime: Date.now() - info.startedAt,
576
+ uptime: active ? Date.now() - info.startedAt : null,
577
+ pythonPath: info.pythonPath ?? null,
578
+ venvPath: info.venvPath ?? null,
813
579
  };
814
580
  }
815
581
 
816
582
  export async function shutdownSharedGateway(): Promise<void> {
817
- cancelIdleShutdown();
818
583
  try {
819
584
  await withGatewayLock(async () => {
820
585
  const info = readGatewayInfo();
821
- if (info) {
822
- clearGatewayInfo();
586
+ if (!info) return;
587
+ if (isPidRunning(info.pid)) {
588
+ await killGateway(info.pid, "shutdown");
823
589
  }
824
- clearClientFiles();
590
+ clearGatewayInfo();
825
591
  });
826
592
  } catch (err) {
827
593
  logger.warn("Failed to shutdown shared gateway", {
828
594
  error: err instanceof Error ? err.message : String(err),
829
595
  });
830
596
  } finally {
831
- await shutdownLocalGateway();
597
+ if (localGatewayProcess) {
598
+ await killGateway(localGatewayProcess.pid, "shutdown-local");
599
+ }
600
+ localGatewayProcess = null;
601
+ localGatewayUrl = null;
832
602
  isCoordinatorInitialized = false;
833
603
  }
834
604
  }