@oh-my-pi/pi-coding-agent 15.12.3 → 15.13.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 (457) hide show
  1. package/CHANGELOG.md +347 -7
  2. package/dist/cli.js +1615 -1231
  3. package/dist/types/async/job-manager.d.ts +15 -0
  4. package/dist/types/autolearn/controller.d.ts +25 -0
  5. package/dist/types/autolearn/managed-skills.d.ts +45 -0
  6. package/dist/types/autoresearch/state.d.ts +1 -1
  7. package/dist/types/autoresearch/tools/init-experiment.d.ts +1 -1
  8. package/dist/types/autoresearch/tools/log-experiment.d.ts +1 -1
  9. package/dist/types/autoresearch/tools/run-experiment.d.ts +1 -1
  10. package/dist/types/autoresearch/tools/update-notes.d.ts +1 -1
  11. package/dist/types/autoresearch/types.d.ts +1 -1
  12. package/dist/types/cli/args.d.ts +19 -2
  13. package/dist/types/cli/models-cli.d.ts +49 -0
  14. package/dist/types/cli/session-picker.d.ts +1 -1
  15. package/dist/types/cli/setup-cli.d.ts +1 -1
  16. package/dist/types/cli/setup-model-picker.d.ts +14 -0
  17. package/dist/types/collab/protocol.d.ts +1 -1
  18. package/dist/types/commands/launch.d.ts +0 -3
  19. package/dist/types/commands/models.d.ts +33 -0
  20. package/dist/types/commands/say.d.ts +24 -0
  21. package/dist/types/commands/token.d.ts +25 -0
  22. package/dist/types/commit/agentic/tools/analyze-file.d.ts +1 -1
  23. package/dist/types/commit/agentic/tools/git-file-diff.d.ts +1 -1
  24. package/dist/types/commit/agentic/tools/git-hunk.d.ts +1 -1
  25. package/dist/types/commit/agentic/tools/git-overview.d.ts +1 -1
  26. package/dist/types/commit/agentic/tools/propose-changelog.d.ts +1 -1
  27. package/dist/types/commit/agentic/tools/propose-commit.d.ts +1 -1
  28. package/dist/types/commit/agentic/tools/recent-commits.d.ts +1 -1
  29. package/dist/types/commit/agentic/tools/schemas.d.ts +1 -1
  30. package/dist/types/commit/agentic/tools/split-commit.d.ts +1 -1
  31. package/dist/types/commit/changelog/generate.d.ts +1 -1
  32. package/dist/types/commit/shared-llm.d.ts +1 -1
  33. package/dist/types/config/keybindings.d.ts +3 -3
  34. package/dist/types/config/model-registry.d.ts +17 -0
  35. package/dist/types/config/models-config-schema.d.ts +13 -1
  36. package/dist/types/config/models-config.d.ts +8 -2
  37. package/dist/types/config/settings-schema.d.ts +281 -58
  38. package/dist/types/edit/hashline/params.d.ts +1 -1
  39. package/dist/types/edit/modes/apply-patch.d.ts +1 -1
  40. package/dist/types/edit/modes/patch.d.ts +1 -1
  41. package/dist/types/edit/modes/replace.d.ts +1 -1
  42. package/dist/types/export/html/index.d.ts +2 -1
  43. package/dist/types/extensibility/custom-commands/types.d.ts +2 -2
  44. package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
  45. package/dist/types/extensibility/extensions/model-api.d.ts +17 -0
  46. package/dist/types/extensibility/extensions/runner.d.ts +3 -1
  47. package/dist/types/extensibility/extensions/types.d.ts +49 -3
  48. package/dist/types/extensibility/hooks/index.d.ts +2 -1
  49. package/dist/types/extensibility/hooks/types.d.ts +2 -2
  50. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +9 -0
  51. package/dist/types/extensibility/plugins/loader.d.ts +11 -0
  52. package/dist/types/extensibility/shared-events.d.ts +1 -1
  53. package/dist/types/extensibility/skills.d.ts +10 -0
  54. package/dist/types/goals/guided-setup.d.ts +18 -0
  55. package/dist/types/goals/state.d.ts +1 -1
  56. package/dist/types/goals/tools/goal-tool.d.ts +1 -1
  57. package/dist/types/hindsight/transcript.d.ts +1 -1
  58. package/dist/types/index.d.ts +5 -0
  59. package/dist/types/internal-urls/local-protocol.d.ts +4 -2
  60. package/dist/types/lsp/types.d.ts +1 -1
  61. package/dist/types/main.d.ts +4 -3
  62. package/dist/types/mcp/manager.d.ts +8 -0
  63. package/dist/types/mcp/startup-events.d.ts +11 -0
  64. package/dist/types/memories/index.d.ts +7 -0
  65. package/dist/types/memory-backend/local-backend.d.ts +4 -3
  66. package/dist/types/mnemopi/config.d.ts +28 -0
  67. package/dist/types/modes/acp/acp-agent.d.ts +1 -2
  68. package/dist/types/modes/components/agent-hub.d.ts +6 -0
  69. package/dist/types/modes/components/assistant-message.d.ts +1 -2
  70. package/dist/types/modes/components/compaction-summary-message.d.ts +15 -1
  71. package/dist/types/modes/components/custom-editor.d.ts +39 -1
  72. package/dist/types/modes/components/custom-editor.test.d.ts +1 -0
  73. package/dist/types/modes/components/index.d.ts +1 -0
  74. package/dist/types/modes/components/logout-account-selector.d.ts +8 -0
  75. package/dist/types/modes/components/session-selector.d.ts +1 -1
  76. package/dist/types/modes/components/status-line/component.d.ts +9 -5
  77. package/dist/types/modes/components/status-line/types.d.ts +2 -1
  78. package/dist/types/modes/components/tool-execution.d.ts +26 -16
  79. package/dist/types/modes/components/transcript-container.d.ts +23 -2
  80. package/dist/types/modes/components/tree-selector.d.ts +1 -1
  81. package/dist/types/modes/components/usage-row.d.ts +3 -0
  82. package/dist/types/modes/controllers/command-controller.d.ts +2 -2
  83. package/dist/types/modes/controllers/event-controller.d.ts +0 -17
  84. package/dist/types/modes/controllers/input-controller.d.ts +14 -0
  85. package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
  86. package/dist/types/modes/gradient-highlight.d.ts +9 -4
  87. package/dist/types/modes/image-references.d.ts +6 -0
  88. package/dist/types/modes/interactive-mode.d.ts +27 -6
  89. package/dist/types/modes/magic-keywords.d.ts +13 -1
  90. package/dist/types/modes/rpc/rpc-mode.d.ts +35 -1
  91. package/dist/types/modes/rpc/rpc-types.d.ts +9 -1
  92. package/dist/types/modes/runtime-init.d.ts +4 -0
  93. package/dist/types/modes/theme/theme.d.ts +13 -2
  94. package/dist/types/modes/types.d.ts +8 -7
  95. package/dist/types/modes/utils/ui-helpers.d.ts +1 -1
  96. package/dist/types/registry/agent-registry.d.ts +17 -0
  97. package/dist/types/secrets/obfuscator.d.ts +1 -1
  98. package/dist/types/session/agent-session.d.ts +28 -35
  99. package/dist/types/session/agent-storage.d.ts +2 -1
  100. package/dist/types/session/indexed-session-storage.d.ts +3 -3
  101. package/dist/types/session/messages.d.ts +8 -10
  102. package/dist/types/session/session-context.d.ts +39 -0
  103. package/dist/types/session/session-entries.d.ts +159 -0
  104. package/dist/types/session/session-listing.d.ts +69 -0
  105. package/dist/types/session/session-loader.d.ts +16 -0
  106. package/dist/types/session/session-manager.d.ts +85 -462
  107. package/dist/types/session/session-migrations.d.ts +12 -0
  108. package/dist/types/session/session-paths.d.ts +25 -0
  109. package/dist/types/session/session-persistence.d.ts +8 -0
  110. package/dist/types/session/session-storage.d.ts +11 -7
  111. package/dist/types/session/snapcompact-inline.d.ts +12 -1
  112. package/dist/types/session/snapcompact-savings-journal.d.ts +46 -0
  113. package/dist/types/session/tool-choice-queue.d.ts +6 -6
  114. package/dist/types/slash-commands/helpers/logout.d.ts +15 -0
  115. package/dist/types/stt/asr-client.d.ts +90 -0
  116. package/dist/types/stt/asr-protocol.d.ts +97 -0
  117. package/dist/types/stt/asr-worker.d.ts +2 -0
  118. package/dist/types/stt/downloader.d.ts +38 -0
  119. package/dist/types/stt/endpointer.d.ts +59 -0
  120. package/dist/types/stt/index.d.ts +5 -1
  121. package/dist/types/stt/models.d.ts +120 -0
  122. package/dist/types/stt/recorder.d.ts +17 -0
  123. package/dist/types/stt/stt-controller.d.ts +6 -0
  124. package/dist/types/stt/transcriber.d.ts +5 -7
  125. package/dist/types/stt/wav.d.ts +29 -0
  126. package/dist/types/system-prompt.d.ts +4 -0
  127. package/dist/types/task/executor.d.ts +2 -0
  128. package/dist/types/task/index.d.ts +9 -1
  129. package/dist/types/task/types.d.ts +37 -1
  130. package/dist/types/tools/ask.d.ts +1 -1
  131. package/dist/types/tools/ast-edit.d.ts +1 -1
  132. package/dist/types/tools/ast-grep.d.ts +1 -1
  133. package/dist/types/tools/bash.d.ts +3 -3
  134. package/dist/types/tools/browser/cmux/cmux-tab.d.ts +202 -0
  135. package/dist/types/tools/browser/cmux/rpc.d.ts +70 -0
  136. package/dist/types/tools/browser/cmux/socket-client.d.ts +19 -0
  137. package/dist/types/tools/browser/registry.d.ts +16 -3
  138. package/dist/types/tools/browser/render.d.ts +2 -0
  139. package/dist/types/tools/browser/tab-protocol.d.ts +2 -0
  140. package/dist/types/tools/browser/tab-supervisor.d.ts +16 -4
  141. package/dist/types/tools/browser.d.ts +3 -1
  142. package/dist/types/tools/checkpoint.d.ts +1 -1
  143. package/dist/types/tools/debug.d.ts +1 -1
  144. package/dist/types/tools/eval-render.d.ts +1 -1
  145. package/dist/types/tools/eval.d.ts +1 -1
  146. package/dist/types/tools/find.d.ts +1 -1
  147. package/dist/types/tools/gh.d.ts +1 -1
  148. package/dist/types/tools/image-gen.d.ts +1 -1
  149. package/dist/types/tools/index.d.ts +14 -2
  150. package/dist/types/tools/inspect-image.d.ts +1 -1
  151. package/dist/types/tools/irc.d.ts +2 -1
  152. package/dist/types/tools/job.d.ts +1 -1
  153. package/dist/types/tools/learn.d.ts +51 -0
  154. package/dist/types/tools/manage-skill.d.ts +40 -0
  155. package/dist/types/tools/memory-edit.d.ts +1 -1
  156. package/dist/types/tools/memory-recall.d.ts +1 -1
  157. package/dist/types/tools/memory-reflect.d.ts +1 -1
  158. package/dist/types/tools/memory-retain.d.ts +1 -1
  159. package/dist/types/tools/plan-mode-guard.d.ts +10 -0
  160. package/dist/types/tools/read.d.ts +1 -1
  161. package/dist/types/tools/render-mermaid.d.ts +1 -1
  162. package/dist/types/tools/renderers.d.ts +7 -11
  163. package/dist/types/tools/resolve.d.ts +1 -1
  164. package/dist/types/tools/review.d.ts +1 -1
  165. package/dist/types/tools/search-tool-bm25.d.ts +1 -1
  166. package/dist/types/tools/search.d.ts +1 -1
  167. package/dist/types/tools/ssh.d.ts +2 -2
  168. package/dist/types/tools/todo.d.ts +2 -2
  169. package/dist/types/tools/tts.d.ts +26 -1
  170. package/dist/types/tools/write.d.ts +2 -2
  171. package/dist/types/tts/downloader.d.ts +20 -0
  172. package/dist/types/tts/index.d.ts +8 -0
  173. package/dist/types/tts/models.d.ts +82 -0
  174. package/dist/types/tts/player.d.ts +32 -0
  175. package/dist/types/tts/runtime.d.ts +6 -0
  176. package/dist/types/tts/streaming-player.d.ts +41 -0
  177. package/dist/types/tts/tts-client.d.ts +93 -0
  178. package/dist/types/tts/tts-protocol.d.ts +95 -0
  179. package/dist/types/tts/tts-worker.d.ts +2 -0
  180. package/dist/types/tts/vocalizer.d.ts +41 -0
  181. package/dist/types/tts/wav.d.ts +8 -0
  182. package/dist/types/utils/clipboard.d.ts +4 -3
  183. package/dist/types/utils/image-loading.d.ts +18 -1
  184. package/dist/types/utils/thinking-display.d.ts +17 -0
  185. package/dist/types/utils/tool-choice.d.ts +8 -0
  186. package/dist/types/utils/tools-manager.d.ts +2 -1
  187. package/dist/types/utils/tools-manager.test.d.ts +1 -0
  188. package/dist/types/web/scrapers/github.d.ts +1 -1
  189. package/dist/types/web/search/index.d.ts +1 -1
  190. package/package.json +17 -16
  191. package/src/async/job-manager.ts +49 -0
  192. package/src/autolearn/controller.ts +139 -0
  193. package/src/autolearn/managed-skills.ts +257 -0
  194. package/src/autoresearch/state.ts +1 -1
  195. package/src/autoresearch/storage.ts +2 -1
  196. package/src/autoresearch/tools/init-experiment.ts +1 -1
  197. package/src/autoresearch/tools/log-experiment.ts +1 -1
  198. package/src/autoresearch/tools/run-experiment.ts +1 -1
  199. package/src/autoresearch/tools/update-notes.ts +1 -1
  200. package/src/autoresearch/types.ts +1 -1
  201. package/src/cli/args.ts +56 -10
  202. package/src/cli/auth-gateway-cli.ts +1 -1
  203. package/src/cli/bench-cli.ts +1 -1
  204. package/src/cli/dry-balance-cli.ts +1 -1
  205. package/src/cli/models-cli.ts +427 -0
  206. package/src/cli/session-picker.ts +2 -1
  207. package/src/cli/setup-cli.ts +148 -47
  208. package/src/cli/setup-model-picker.ts +43 -0
  209. package/src/cli-commands.ts +3 -0
  210. package/src/cli.ts +45 -13
  211. package/src/collab/host.ts +10 -13
  212. package/src/collab/protocol.ts +1 -1
  213. package/src/commands/launch.ts +0 -3
  214. package/src/commands/models.ts +61 -0
  215. package/src/commands/say.ts +102 -0
  216. package/src/commands/setup.ts +1 -1
  217. package/src/commands/token.ts +89 -0
  218. package/src/commit/agentic/tools/analyze-file.ts +4 -1
  219. package/src/commit/agentic/tools/git-file-diff.ts +1 -1
  220. package/src/commit/agentic/tools/git-hunk.ts +1 -1
  221. package/src/commit/agentic/tools/git-overview.ts +1 -1
  222. package/src/commit/agentic/tools/propose-changelog.ts +1 -1
  223. package/src/commit/agentic/tools/propose-commit.ts +1 -1
  224. package/src/commit/agentic/tools/recent-commits.ts +1 -1
  225. package/src/commit/agentic/tools/schemas.ts +1 -1
  226. package/src/commit/agentic/tools/split-commit.ts +1 -1
  227. package/src/commit/analysis/summary.ts +1 -1
  228. package/src/commit/changelog/generate.ts +1 -1
  229. package/src/commit/shared-llm.ts +1 -1
  230. package/src/config/keybindings.ts +2 -2
  231. package/src/config/model-discovery.ts +11 -5
  232. package/src/config/model-registry.ts +79 -21
  233. package/src/config/model-resolver.ts +2 -2
  234. package/src/config/models-config-schema.ts +5 -2
  235. package/src/config/models-config.ts +2 -1
  236. package/src/config/settings-schema.ts +266 -32
  237. package/src/config/settings.ts +10 -0
  238. package/src/discovery/builtin.ts +23 -1
  239. package/src/discovery/claude-plugins.ts +44 -5
  240. package/src/discovery/helpers.ts +41 -1
  241. package/src/edit/hashline/params.ts +1 -1
  242. package/src/edit/modes/apply-patch.ts +1 -1
  243. package/src/edit/modes/patch.ts +1 -1
  244. package/src/edit/modes/replace.ts +1 -1
  245. package/src/eval/__tests__/budget-bridge.test.ts +1 -1
  246. package/src/eval/agent-bridge.ts +1 -1
  247. package/src/eval/completion-bridge.ts +1 -1
  248. package/src/eval/js/shared/prelude.txt +69 -17
  249. package/src/export/html/index.ts +3 -6
  250. package/src/export/html/template.js +24 -2
  251. package/src/export/html/tool-views.generated.js +2 -2
  252. package/src/extensibility/custom-commands/loader.ts +1 -1
  253. package/src/extensibility/custom-commands/types.ts +2 -2
  254. package/src/extensibility/custom-tools/loader.ts +1 -1
  255. package/src/extensibility/custom-tools/types.ts +2 -2
  256. package/src/extensibility/extensions/loader.ts +2 -2
  257. package/src/extensibility/extensions/model-api.ts +41 -0
  258. package/src/extensibility/extensions/runner.ts +4 -0
  259. package/src/extensibility/extensions/types.ts +54 -3
  260. package/src/extensibility/extensions/wrapper.ts +41 -5
  261. package/src/extensibility/hooks/index.ts +2 -1
  262. package/src/extensibility/hooks/loader.ts +1 -1
  263. package/src/extensibility/hooks/types.ts +2 -2
  264. package/src/extensibility/plugins/legacy-pi-compat.ts +43 -13
  265. package/src/extensibility/plugins/loader.ts +30 -19
  266. package/src/extensibility/plugins/manager.ts +221 -90
  267. package/src/extensibility/shared-events.ts +1 -1
  268. package/src/extensibility/skills.ts +101 -5
  269. package/src/goals/guided-setup.ts +133 -0
  270. package/src/goals/state.ts +1 -1
  271. package/src/goals/tools/goal-tool.ts +1 -1
  272. package/src/hindsight/transcript.ts +1 -1
  273. package/src/index.ts +5 -0
  274. package/src/internal-urls/docs-index.generated.ts +13 -10
  275. package/src/internal-urls/history-protocol.ts +1 -1
  276. package/src/internal-urls/local-protocol.ts +29 -7
  277. package/src/lsp/types.ts +1 -1
  278. package/src/main.ts +27 -32
  279. package/src/mcp/config-writer.ts +7 -3
  280. package/src/mcp/manager.ts +11 -0
  281. package/src/mcp/startup-events.ts +21 -0
  282. package/src/mcp/transports/stdio.ts +2 -1
  283. package/src/memories/index.ts +149 -12
  284. package/src/memories/storage.ts +2 -1
  285. package/src/memory-backend/local-backend.ts +11 -5
  286. package/src/mnemopi/backend.ts +1 -0
  287. package/src/mnemopi/config.ts +112 -12
  288. package/src/modes/acp/acp-agent.ts +8 -53
  289. package/src/modes/acp/acp-event-mapper.ts +5 -1
  290. package/src/modes/components/agent-hub.ts +51 -5
  291. package/src/modes/components/assistant-message.ts +12 -44
  292. package/src/modes/components/compaction-summary-message.ts +125 -26
  293. package/src/modes/components/custom-editor.test.ts +96 -0
  294. package/src/modes/components/custom-editor.ts +164 -8
  295. package/src/modes/components/index.ts +1 -0
  296. package/src/modes/components/logout-account-selector.ts +130 -0
  297. package/src/modes/components/mcp-add-wizard.ts +1 -1
  298. package/src/modes/components/model-selector.ts +2 -2
  299. package/src/modes/components/session-selector.ts +1 -1
  300. package/src/modes/components/settings-defs.ts +7 -0
  301. package/src/modes/components/status-line/component.ts +54 -157
  302. package/src/modes/components/status-line/segments.ts +1 -1
  303. package/src/modes/components/status-line/types.ts +2 -1
  304. package/src/modes/components/tool-execution.ts +82 -43
  305. package/src/modes/components/transcript-container.ts +70 -1
  306. package/src/modes/components/tree-selector.ts +1 -1
  307. package/src/modes/components/usage-row.ts +18 -0
  308. package/src/modes/components/user-message.ts +4 -2
  309. package/src/modes/controllers/command-controller.ts +14 -16
  310. package/src/modes/controllers/event-controller.ts +101 -73
  311. package/src/modes/controllers/extension-ui-controller.ts +6 -0
  312. package/src/modes/controllers/input-controller.ts +311 -57
  313. package/src/modes/controllers/mcp-command-controller.ts +44 -3
  314. package/src/modes/controllers/selector-controller.ts +68 -12
  315. package/src/modes/controllers/streaming-reveal.ts +4 -3
  316. package/src/modes/gradient-highlight.ts +21 -9
  317. package/src/modes/image-references.ts +20 -0
  318. package/src/modes/interactive-mode.ts +288 -48
  319. package/src/modes/magic-keywords.ts +27 -5
  320. package/src/modes/rpc/rpc-mode.ts +146 -14
  321. package/src/modes/rpc/rpc-subagents.ts +2 -2
  322. package/src/modes/rpc/rpc-types.ts +8 -2
  323. package/src/modes/runtime-init.ts +28 -3
  324. package/src/modes/theme/theme.ts +99 -51
  325. package/src/modes/types.ts +6 -7
  326. package/src/modes/utils/hotkeys-markdown.ts +1 -1
  327. package/src/modes/utils/ui-helpers.ts +36 -7
  328. package/src/priority.json +5 -1
  329. package/src/prompts/agents/task.md +1 -0
  330. package/src/prompts/goals/guided-goal-interview.md +8 -0
  331. package/src/prompts/goals/guided-goal-system.md +12 -0
  332. package/src/prompts/memories/read-path.md +6 -0
  333. package/src/prompts/system/autolearn-guidance-learn.md +1 -0
  334. package/src/prompts/system/autolearn-guidance.md +7 -0
  335. package/src/prompts/system/autolearn-nudge.md +3 -0
  336. package/src/prompts/system/eager-task.md +7 -0
  337. package/src/prompts/system/eager-todo.md +11 -6
  338. package/src/prompts/system/empty-stop-retry.md +4 -6
  339. package/src/prompts/system/subagent-system-prompt.md +4 -0
  340. package/src/prompts/system/system-prompt.md +10 -5
  341. package/src/prompts/system/title-marker-instruction.md +1 -0
  342. package/src/prompts/system/title-system-marker.md +16 -0
  343. package/src/prompts/tools/job.md +1 -0
  344. package/src/prompts/tools/learn.md +7 -0
  345. package/src/prompts/tools/manage-skill.md +9 -0
  346. package/src/prompts/tools/task.md +3 -0
  347. package/src/registry/agent-registry.ts +30 -0
  348. package/src/sdk.ts +103 -43
  349. package/src/secrets/obfuscator.ts +1 -1
  350. package/src/session/agent-session.ts +331 -318
  351. package/src/session/agent-storage.ts +18 -9
  352. package/src/session/history-storage.ts +3 -2
  353. package/src/session/indexed-session-storage.ts +7 -10
  354. package/src/session/messages.ts +9 -11
  355. package/src/session/session-context.ts +352 -0
  356. package/src/session/session-dump-format.ts +4 -2
  357. package/src/session/session-entries.ts +194 -0
  358. package/src/session/session-listing.ts +588 -0
  359. package/src/session/session-loader.ts +106 -0
  360. package/src/session/session-manager.ts +968 -3064
  361. package/src/session/session-migrations.ts +78 -0
  362. package/src/session/session-paths.ts +193 -0
  363. package/src/session/session-persistence.ts +131 -0
  364. package/src/session/session-storage.ts +91 -30
  365. package/src/session/snapcompact-inline.ts +21 -1
  366. package/src/session/snapcompact-savings-journal.ts +113 -0
  367. package/src/session/tool-choice-queue.ts +23 -11
  368. package/src/slash-commands/builtin-registry.ts +40 -4
  369. package/src/slash-commands/helpers/logout.ts +88 -0
  370. package/src/stt/asr-client.ts +520 -0
  371. package/src/stt/asr-protocol.ts +65 -0
  372. package/src/stt/asr-worker.ts +790 -0
  373. package/src/stt/downloader.ts +107 -47
  374. package/src/stt/endpointer.ts +259 -0
  375. package/src/stt/index.ts +5 -1
  376. package/src/stt/models.ts +150 -0
  377. package/src/stt/recorder.ts +247 -60
  378. package/src/stt/stt-controller.ts +201 -22
  379. package/src/stt/transcriber.ts +37 -68
  380. package/src/stt/wav.ts +173 -0
  381. package/src/system-prompt.ts +8 -0
  382. package/src/task/agents.ts +1 -2
  383. package/src/task/executor.ts +49 -15
  384. package/src/task/index.ts +60 -6
  385. package/src/task/render.ts +83 -8
  386. package/src/task/types.ts +54 -1
  387. package/src/tools/ask.ts +9 -1
  388. package/src/tools/ast-edit.ts +1 -1
  389. package/src/tools/ast-grep.ts +1 -1
  390. package/src/tools/bash.ts +5 -4
  391. package/src/tools/browser/cmux/cmux-tab.ts +1264 -0
  392. package/src/tools/browser/cmux/rpc.ts +156 -0
  393. package/src/tools/browser/cmux/socket-client.ts +309 -0
  394. package/src/tools/browser/registry.ts +37 -3
  395. package/src/tools/browser/render.ts +6 -1
  396. package/src/tools/browser/tab-protocol.ts +2 -0
  397. package/src/tools/browser/tab-supervisor.ts +189 -18
  398. package/src/tools/browser/tab-worker.ts +1 -1
  399. package/src/tools/browser.ts +16 -1
  400. package/src/tools/checkpoint.ts +1 -1
  401. package/src/tools/debug.ts +1 -1
  402. package/src/tools/eval-render.ts +4 -3
  403. package/src/tools/eval.ts +11 -6
  404. package/src/tools/fetch.ts +13 -2
  405. package/src/tools/find.ts +1 -1
  406. package/src/tools/gh.ts +1 -1
  407. package/src/tools/github-cache.ts +2 -1
  408. package/src/tools/image-gen.ts +1 -1
  409. package/src/tools/index.ts +43 -5
  410. package/src/tools/inspect-image.ts +3 -1
  411. package/src/tools/irc.ts +11 -3
  412. package/src/tools/job.ts +15 -3
  413. package/src/tools/learn.ts +144 -0
  414. package/src/tools/manage-skill.ts +104 -0
  415. package/src/tools/memory-edit.ts +1 -1
  416. package/src/tools/memory-recall.ts +1 -1
  417. package/src/tools/memory-reflect.ts +1 -1
  418. package/src/tools/memory-retain.ts +1 -1
  419. package/src/tools/plan-mode-guard.ts +53 -19
  420. package/src/tools/read.ts +8 -2
  421. package/src/tools/render-mermaid.ts +1 -1
  422. package/src/tools/renderers.ts +7 -11
  423. package/src/tools/report-tool-issue.ts +3 -2
  424. package/src/tools/resolve.ts +1 -1
  425. package/src/tools/review.ts +1 -1
  426. package/src/tools/search-tool-bm25.ts +1 -1
  427. package/src/tools/search.ts +1 -1
  428. package/src/tools/ssh.ts +5 -4
  429. package/src/tools/todo.ts +2 -2
  430. package/src/tools/tts.ts +204 -93
  431. package/src/tools/write.ts +19 -3
  432. package/src/tts/downloader.ts +64 -0
  433. package/src/tts/index.ts +8 -0
  434. package/src/tts/models.ts +137 -0
  435. package/src/tts/player.ts +137 -0
  436. package/src/tts/runtime.ts +21 -0
  437. package/src/tts/streaming-player.ts +266 -0
  438. package/src/tts/tts-client.ts +647 -0
  439. package/src/tts/tts-protocol.ts +60 -0
  440. package/src/tts/tts-worker.ts +497 -0
  441. package/src/tts/vocalizer.ts +162 -0
  442. package/src/tts/wav.ts +58 -0
  443. package/src/utils/clipboard.ts +35 -18
  444. package/src/utils/image-loading.ts +35 -4
  445. package/src/utils/thinking-display.ts +37 -0
  446. package/src/utils/title-generator.ts +48 -5
  447. package/src/utils/tool-choice.ts +16 -0
  448. package/src/utils/tools-manager.test.ts +25 -0
  449. package/src/utils/tools-manager.ts +19 -1
  450. package/src/web/scrapers/github.ts +96 -0
  451. package/src/web/search/index.ts +14 -1
  452. package/src/web/search/providers/searxng.ts +13 -1
  453. package/dist/types/cli/list-models.d.ts +0 -30
  454. package/dist/types/stt/setup.d.ts +0 -18
  455. package/src/cli/list-models.ts +0 -194
  456. package/src/stt/setup.ts +0 -52
  457. package/src/stt/transcribe.py +0 -70
@@ -4,12 +4,18 @@
4
4
  * Handles `omp setup` for onboarding and `omp setup <component>` for optional dependencies.
5
5
  */
6
6
  import * as path from "node:path";
7
- import { $which, APP_NAME, getPythonEnvDir } from "@oh-my-pi/pi-utils";
7
+ import { $which, APP_NAME, getProjectDir, getPythonEnvDir } from "@oh-my-pi/pi-utils";
8
8
  import { $ } from "bun";
9
9
  import chalk from "chalk";
10
+ import { Settings, settings } from "../config/settings";
10
11
  import { theme } from "../modes/theme/theme";
12
+ import { downloadSttModel, isSttModelCached } from "../stt/downloader";
13
+ import { isSttModelKey, STT_MODEL_OPTIONS } from "../stt/models";
14
+ import { detectRecorder, ensureRecorder } from "../stt/recorder";
15
+ import { downloadTtsModel, isTtsLocalModelKey, isTtsModelCached, TTS_LOCAL_MODEL_OPTIONS } from "../tts";
16
+ import { selectSetupModel } from "./setup-model-picker";
11
17
 
12
- export type SetupComponent = "python" | "stt";
18
+ export type SetupComponent = "python" | "speech";
13
19
 
14
20
  export interface SetupCommandArgs {
15
21
  component: SetupComponent;
@@ -19,7 +25,7 @@ export interface SetupCommandArgs {
19
25
  };
20
26
  }
21
27
 
22
- const VALID_COMPONENTS: SetupComponent[] = ["python", "stt"];
28
+ const VALID_COMPONENTS: SetupComponent[] = ["python", "speech"];
23
29
 
24
30
  const MANAGED_PYTHON_ENV = getPythonEnvDir();
25
31
 
@@ -114,8 +120,8 @@ export async function runSetupCommand(cmd: SetupCommandArgs): Promise<void> {
114
120
  case "python":
115
121
  await handlePythonSetup(cmd.flags);
116
122
  break;
117
- case "stt":
118
- await handleSttSetup(cmd.flags);
123
+ case "speech":
124
+ await handleSpeechSetup(cmd.flags);
119
125
  break;
120
126
  }
121
127
  }
@@ -149,58 +155,153 @@ async function handlePythonSetup(flags: { json?: boolean; check?: boolean }): Pr
149
155
  process.exit(1);
150
156
  }
151
157
 
152
- async function handleSttSetup(flags: { json?: boolean; check?: boolean }): Promise<void> {
153
- const { checkDependencies, formatDependencyStatus } = await import("../stt/setup");
154
- const status = await checkDependencies();
158
+ /**
159
+ * One installable speech dependency. `isReady`/`status` are read-only probes;
160
+ * `pick` (optional) lets an interactive user choose + persist a model; `ensure`
161
+ * performs the download, streaming a normalized progress event.
162
+ */
163
+ interface SpeechComponent {
164
+ name: string;
165
+ isReady(): Promise<boolean>;
166
+ status(): Promise<string>;
167
+ pick?(): Promise<boolean>;
168
+ ensure(onProgress: (progress: { stage: string; percent?: number }) => void): Promise<void>;
169
+ }
155
170
 
156
- if (flags.json) {
157
- console.log(JSON.stringify(status, null, 2));
158
- if (!status.recorder.available || !status.python.available || !status.whisper.available) process.exit(1);
159
- return;
160
- }
171
+ function buildSpeechComponents(): SpeechComponent[] {
172
+ return [
173
+ {
174
+ name: "Recorder",
175
+ isReady: async () => detectRecorder() !== null,
176
+ status: async () => {
177
+ const recorder = detectRecorder();
178
+ return recorder ? `${recorder.tool} (${recorder.bin})` : "none — ffmpeg will be downloaded";
179
+ },
180
+ ensure: async onProgress => {
181
+ await ensureRecorder(onProgress);
182
+ },
183
+ },
184
+ {
185
+ name: "Speech-to-Text model",
186
+ isReady: () => isSttModelCached(settings.get("stt.modelName")),
187
+ status: async () => {
188
+ const key = settings.get("stt.modelName");
189
+ return (await isSttModelCached(key)) ? key : `${key} — not downloaded`;
190
+ },
191
+ pick: async () => {
192
+ const chosen = await selectSetupModel(
193
+ "Speech-to-Text model",
194
+ [...STT_MODEL_OPTIONS],
195
+ settings.get("stt.modelName"),
196
+ );
197
+ if (chosen === null) return false;
198
+ if (isSttModelKey(chosen)) {
199
+ settings.set("stt.modelName", chosen);
200
+ await settings.flush();
201
+ }
202
+ return true;
203
+ },
204
+ ensure: onProgress =>
205
+ downloadSttModel(settings.get("stt.modelName"), progress =>
206
+ onProgress({ stage: `Downloading ${progress.label} model`, percent: progress.percent }),
207
+ ),
208
+ },
209
+ {
210
+ name: "Text-to-Speech model",
211
+ isReady: () => isTtsModelCached(settings.get("tts.localModel")),
212
+ status: async () => {
213
+ const key = settings.get("tts.localModel");
214
+ return (await isTtsModelCached(key)) ? key : `${key} — model/runtime not installed`;
215
+ },
216
+ pick: async () => {
217
+ const chosen = await selectSetupModel(
218
+ "Text-to-Speech model",
219
+ [...TTS_LOCAL_MODEL_OPTIONS],
220
+ settings.get("tts.localModel"),
221
+ );
222
+ if (chosen === null) return false;
223
+ if (isTtsLocalModelKey(chosen)) {
224
+ settings.set("tts.localModel", chosen);
225
+ await settings.flush();
226
+ }
227
+ return true;
228
+ },
229
+ ensure: async onProgress => {
230
+ const ok = await downloadTtsModel(settings.get("tts.localModel"), progress =>
231
+ onProgress({ stage: progress.stage, percent: progress.percent }),
232
+ );
233
+ if (!ok) throw new Error("Failed to download the local text-to-speech model.");
234
+ },
235
+ },
236
+ ];
237
+ }
161
238
 
162
- console.log(formatDependencyStatus(status));
239
+ /**
240
+ * Unified `omp setup speech` flow. Drives every {@link SpeechComponent} through
241
+ * one path: report (`--json`/`--check`) or install (interactive pick + ensure
242
+ * with single-line progress; non-TTY skips pickers and installs configured
243
+ * values).
244
+ */
245
+ async function handleSpeechSetup(flags: { json?: boolean; check?: boolean }): Promise<void> {
246
+ await Settings.init({ cwd: getProjectDir() });
247
+ const components = buildSpeechComponents();
163
248
 
164
- if (status.recorder.available && status.python.available && status.whisper.available) {
165
- console.log(chalk.green(`\n${theme.status.success} Speech-to-text is ready`));
249
+ if (flags.json) {
250
+ const report: Record<string, { ready: boolean; status: string }> = {};
251
+ let allReady = true;
252
+ for (const component of components) {
253
+ const ready = await component.isReady();
254
+ if (!ready) allReady = false;
255
+ report[component.name] = { ready, status: await component.status() };
256
+ }
257
+ console.log(JSON.stringify(report, null, 2));
258
+ if (!allReady) process.exit(1);
166
259
  return;
167
260
  }
168
261
 
169
262
  if (flags.check) {
170
- process.exit(1);
171
- }
172
-
173
- if (!status.python.available) {
174
- console.error(chalk.red(`\n${theme.status.error} Python not found`));
175
- console.error(chalk.dim("Install Python 3.8+ and ensure it's in your PATH"));
176
- process.exit(1);
177
- }
178
-
179
- if (!status.recorder.available) {
180
- console.error(chalk.yellow(`\n${theme.status.warning} No recording tool found`));
181
- console.error(chalk.dim(status.recorder.installHint));
263
+ console.log(chalk.bold("Speech dependencies:"));
264
+ let allReady = true;
265
+ for (const component of components) {
266
+ const ready = await component.isReady();
267
+ if (!ready) allReady = false;
268
+ const mark = ready ? chalk.green("[ok]") : chalk.yellow("[missing]");
269
+ console.log(` ${mark} ${component.name}: ${await component.status()}`);
270
+ }
271
+ if (!allReady) process.exit(1);
272
+ return;
182
273
  }
183
274
 
184
- if (!status.whisper.available) {
185
- console.log(chalk.dim(`\nInstalling openai-whisper...`));
186
- const { resolvePython } = await import("../stt/transcriber");
187
- const pythonCmd = resolvePython()!;
188
- const result = await $`${pythonCmd} -m pip install -q openai-whisper`.nothrow();
189
- if (result.exitCode !== 0) {
190
- console.error(chalk.red(`\n${theme.status.error} Failed to install openai-whisper`));
191
- console.error(chalk.dim("Try manually: pip install openai-whisper"));
275
+ const interactive = Boolean(process.stdout.isTTY);
276
+ for (const component of components) {
277
+ if (interactive && component.pick) {
278
+ await component.pick();
279
+ }
280
+ if (await component.isReady()) {
281
+ console.log(chalk.green(`${theme.status.success} ${component.name} ready`));
282
+ continue;
283
+ }
284
+ console.log(chalk.dim(`Preparing ${component.name}...`));
285
+ try {
286
+ await component.ensure(progress => {
287
+ const percent = typeof progress.percent === "number" ? ` (${progress.percent}%)` : "";
288
+ process.stdout.write(`\r${chalk.dim(`${progress.stage}${percent}`)}\x1b[K`);
289
+ });
290
+ process.stdout.write("\n");
291
+ } catch (err) {
292
+ process.stdout.write("\n");
293
+ const msg = err instanceof Error ? err.message : `Failed to set up ${component.name}`;
294
+ console.error(chalk.red(`${theme.status.error} ${msg}`));
192
295
  process.exit(1);
193
296
  }
194
297
  }
195
298
 
196
- const recheck = await checkDependencies();
197
- if (recheck.recorder.available && recheck.python.available && recheck.whisper.available) {
198
- console.log(chalk.green(`\n${theme.status.success} Speech-to-text is ready`));
199
- } else {
200
- console.error(chalk.red(`\n${theme.status.error} Setup incomplete`));
201
- console.log(formatDependencyStatus(recheck));
202
- process.exit(1);
203
- }
299
+ console.log(chalk.green(`\n${theme.status.success} Speech is ready`));
300
+ console.log(
301
+ chalk.dim(
302
+ "Enable speech-to-text via stt.enabled, then hold Space to talk (or bind app.stt.toggle); enable the speech-generation tool via speechgen.enabled; speak replies aloud via speech.enabled.",
303
+ ),
304
+ );
204
305
  }
205
306
 
206
307
  /**
@@ -215,7 +316,7 @@ ${chalk.bold("Usage:")}
215
316
 
216
317
  ${chalk.bold("Components:")}
217
318
  python Verify a Python 3 interpreter is reachable for code execution
218
- stt Install speech-to-text dependencies (openai-whisper, recording tools)
319
+ speech Pick + download the speech-to-text and text-to-speech models and an audio recorder
219
320
 
220
321
  ${chalk.bold("Options:")}
221
322
  -c, --check Check if dependencies are installed without installing
@@ -224,8 +325,8 @@ ${chalk.bold("Options:")}
224
325
  ${chalk.bold("Examples:")}
225
326
  ${APP_NAME} setup Run the onboarding wizard
226
327
  ${APP_NAME} setup python Check Python execution dependencies
227
- ${APP_NAME} setup stt Install speech-to-text dependencies
228
- ${APP_NAME} setup stt --check Check if STT dependencies are available
328
+ ${APP_NAME} setup speech Set up speech (pick STT + TTS models, install a recorder)
329
+ ${APP_NAME} setup speech --check Check if speech dependencies are available
229
330
  ${APP_NAME} setup python --check Check if Python execution is available
230
331
  `);
231
332
  }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Standalone TUI model picker used by `omp setup speech`.
3
+ *
4
+ * Mirrors {@link ./session-picker.ts} for the standalone-TUI lifecycle: spin up
5
+ * a one-shot {@link TUI} over a {@link SelectList}, resolve on select/cancel, and
6
+ * tear the UI down. The standalone TUI auto-renders on input, so no manual
7
+ * render wiring is needed beyond `addChild`/`setFocus`/`start`.
8
+ */
9
+ import { ProcessTerminal, type SelectItem, SelectList, TUI } from "@oh-my-pi/pi-tui";
10
+ import { getSelectListTheme } from "../modes/theme/theme";
11
+
12
+ /**
13
+ * Show a single-column model picker and resolve with the chosen item's value,
14
+ * or `null` if the user cancelled. `currentValue` pre-selects the matching row.
15
+ */
16
+ export async function selectSetupModel(
17
+ title: string,
18
+ items: SelectItem[],
19
+ currentValue: string,
20
+ ): Promise<string | null> {
21
+ const { promise, resolve } = Promise.withResolvers<string | null>();
22
+ const ui = new TUI(new ProcessTerminal());
23
+ let resolved = false;
24
+
25
+ const finish = (value: string | null): void => {
26
+ if (resolved) return;
27
+ resolved = true;
28
+ ui.stop();
29
+ resolve(value);
30
+ };
31
+
32
+ const list = new SelectList(items, Math.min(items.length, 10), getSelectListTheme());
33
+ const currentIndex = items.findIndex(item => item.value === currentValue);
34
+ if (currentIndex >= 0) list.setSelectedIndex(currentIndex);
35
+ list.onSelect = item => finish(item.value);
36
+ list.onCancel = () => finish(null);
37
+
38
+ process.stdout.write(`${title}\n`);
39
+ ui.addChild(list);
40
+ ui.setFocus(list);
41
+ ui.start();
42
+ return promise;
43
+ }
@@ -27,7 +27,9 @@ export const commands: CommandEntry[] = [
27
27
  { name: "grievances", load: () => import("./commands/grievances").then(m => m.default) },
28
28
  { name: "install", load: () => import("./commands/install").then(m => m.default) },
29
29
  { name: "join", load: () => import("./commands/join").then(m => m.default) },
30
+ { name: "models", load: () => import("./commands/models").then(m => m.default) },
30
31
  { name: "plugin", load: () => import("./commands/plugin").then(m => m.default) },
32
+ { name: "say", load: () => import("./commands/say").then(m => m.default) },
31
33
  { name: "setup", load: () => import("./commands/setup").then(m => m.default) },
32
34
  { name: "shell", load: () => import("./commands/shell").then(m => m.default) },
33
35
  { name: "read", load: () => import("./commands/read").then(m => m.default) },
@@ -36,6 +38,7 @@ export const commands: CommandEntry[] = [
36
38
  { name: "update", load: () => import("./commands/update").then(m => m.default) },
37
39
  { name: "usage", load: () => import("./commands/usage").then(m => m.default) },
38
40
  { name: "tiny-models", load: () => import("./commands/tiny-models").then(m => m.default) },
41
+ { name: "token", load: () => import("./commands/token").then(m => m.default) },
39
42
  { name: "worktree", load: () => import("./commands/worktree").then(m => m.default), aliases: ["wt"] },
40
43
  { name: "search", load: () => import("./commands/web-search").then(m => m.default), aliases: ["q"] },
41
44
  ];
package/src/cli.ts CHANGED
@@ -56,6 +56,8 @@ async function showHelp(config: CliConfig): Promise<void> {
56
56
  async function runSmokeTest(): Promise<void> {
57
57
  const { smokeTestSyncWorker, startServer } = await import("@oh-my-pi/omp-stats");
58
58
  const { smokeTestTinyTitleWorker } = await import("./tiny/title-client");
59
+ const { smokeTestSttWorker } = await import("./stt/asr-client");
60
+ const { smokeTestTtsWorker } = await import("./tts/tts-client");
59
61
  await smokeTestSyncWorker();
60
62
 
61
63
  const statsServer = await startServer(0);
@@ -71,6 +73,8 @@ async function runSmokeTest(): Promise<void> {
71
73
  }
72
74
 
73
75
  await smokeTestTinyTitleWorker();
76
+ await smokeTestSttWorker();
77
+ await smokeTestTtsWorker();
74
78
  process.stdout.write("smoke-test: ok\n");
75
79
  }
76
80
 
@@ -78,6 +82,8 @@ const TINY_WORKER_ARGS = new Set(["--tiny-worker", "__tiny_worker"]);
78
82
  const STATS_SYNC_WORKER_ARG = "__omp_stats_sync_worker";
79
83
  const TAB_WORKER_ARG = "__omp_tab_worker";
80
84
  const JS_EVAL_WORKER_ARG = "__omp_js_eval_worker";
85
+ const STT_WORKER_ARG = "__omp_stt_worker";
86
+ const TTS_WORKER_ARG = "__omp_tts_worker";
81
87
 
82
88
  async function runWorkerEntrypoint(arg: string | undefined): Promise<boolean> {
83
89
  if (arg === STATS_SYNC_WORKER_ARG) {
@@ -110,21 +116,34 @@ async function runWorkerEntrypoint(arg: string | undefined): Promise<boolean> {
110
116
  await import("./eval/js/worker-entry");
111
117
  return true;
112
118
  }
119
+ if (arg === STT_WORKER_ARG) {
120
+ const { startSttWorker } = await import("./stt/asr-worker");
121
+ await runIpcSubprocessWorker(startSttWorker);
122
+ return true;
123
+ }
124
+ if (arg === TTS_WORKER_ARG) {
125
+ const { startTtsWorker } = await import("./tts/tts-worker");
126
+ await runIpcSubprocessWorker(startTtsWorker);
127
+ return true;
128
+ }
113
129
  return false;
114
130
  }
115
131
 
116
132
  /**
117
- * Hidden subcommand that boots the tiny-model worker inside this process
118
- * over the parent's IPC channel. The agent's main process spawns the same
119
- * binary with this flag so `onnxruntime-node` (loaded transitively by
120
- * `@huggingface/transformers`) lives in a child address space. The parent
121
- * `SIGKILL`s the child on shutdown so the NAPI finalizer never runs in
122
- * either process that finalizer segfaults Bun on Windows (issue #1606).
133
+ * Boot a subprocess-isolated transformers.js worker over the parent's IPC
134
+ * channel and block until the parent disconnects. The tiny-model, STT, and TTS
135
+ * workers each run `onnxruntime-node` (loaded transitively by
136
+ * `@huggingface/transformers`) in a child address space because its NAPI
137
+ * finalizer segfaults Bun on shutdown (issue #1606); the parent `SIGKILL`s the
138
+ * child so that finalizer never runs in either process. This wires `process`
139
+ * IPC to the worker's typed transport, keeps the event loop alive while the
140
+ * worker is idle, and hard-kills the process on parent `disconnect`.
123
141
  */
124
- async function runTinyWorker(): Promise<void> {
125
- const { startTinyTitleWorker } = await import("./tiny/worker");
142
+ async function runIpcSubprocessWorker<In, Out>(
143
+ start: (transport: { send(message: Out): void; onMessage(handler: (message: In) => void): () => void }) => void,
144
+ ): Promise<void> {
126
145
  const { promise: shuttingDown, resolve: shutdown } = Promise.withResolvers<void>();
127
- const send = (message: unknown): void => {
146
+ const send = (message: Out): void => {
128
147
  // `process.send` only exists when spawned with an IPC channel; the
129
148
  // parent always spawns us that way. If it's missing, the parent
130
149
  // vanished and there's no one to talk to.
@@ -139,10 +158,10 @@ async function runTinyWorker(): Promise<void> {
139
158
  shutdown();
140
159
  }
141
160
  };
142
- startTinyTitleWorker({
161
+ start({
143
162
  send,
144
163
  onMessage(handler) {
145
- const wrap = (data: unknown): void => handler(data as never);
164
+ const wrap = (data: unknown): void => handler(data as In);
146
165
  process.on("message", wrap);
147
166
  return () => {
148
167
  process.off("message", wrap);
@@ -151,8 +170,8 @@ async function runTinyWorker(): Promise<void> {
151
170
  });
152
171
  const keepalive = setInterval(() => {}, 2 ** 30);
153
172
  // Parent went away (crashed, SIGKILL, etc.) — commit suicide so we don't
154
- // linger as an orphan. SIGKILL via `process.kill` keeps us symmetrical
155
- // with the parent's hard-kill on shutdown: skip every JS/native finalizer.
173
+ // linger as an orphan. SIGKILL via `process.kill` keeps us symmetrical with
174
+ // the parent's hard-kill on shutdown: skip every JS/native finalizer.
156
175
  process.on("disconnect", () => shutdown());
157
176
  try {
158
177
  await shuttingDown;
@@ -162,6 +181,19 @@ async function runTinyWorker(): Promise<void> {
162
181
  process.kill(process.pid, "SIGKILL");
163
182
  }
164
183
 
184
+ /**
185
+ * Hidden subcommand that boots the tiny-model worker inside this process over
186
+ * the parent's IPC channel. The agent's main process spawns the same binary
187
+ * with this flag so `onnxruntime-node` (loaded transitively by
188
+ * `@huggingface/transformers`) lives in a child address space. The parent
189
+ * `SIGKILL`s the child on shutdown so the NAPI finalizer never runs in either
190
+ * process — that finalizer segfaults Bun on Windows (issue #1606).
191
+ */
192
+ async function runTinyWorker(): Promise<void> {
193
+ const { startTinyTitleWorker } = await import("./tiny/worker");
194
+ await runIpcSubprocessWorker(startTinyTitleWorker);
195
+ }
196
+
165
197
  /** Run the CLI with the given argv (no `process.argv` prefix). */
166
198
  export async function runCli(argv: string[]): Promise<void> {
167
199
  if (argv[0] === "--smoke-test") {
@@ -20,7 +20,7 @@ import { AgentLifecycleManager } from "../registry/agent-lifecycle";
20
20
  import { AgentRegistry } from "../registry/agent-registry";
21
21
  import type { AgentSessionEvent } from "../session/agent-session";
22
22
  import { stripImagesFromMessage, USER_INTERRUPT_LABEL } from "../session/messages";
23
- import type { SessionEntry as StoredSessionEntry } from "../session/session-manager";
23
+ import type { SessionEntry as StoredSessionEntry } from "../session/session-entries";
24
24
  import { TASK_SUBAGENT_LIFECYCLE_CHANNEL, TASK_SUBAGENT_PROGRESS_CHANNEL } from "../task";
25
25
  import { generateRoomKey, generateWriteToken, importRoomKey } from "./crypto";
26
26
  import {
@@ -365,13 +365,8 @@ export class CollabHost {
365
365
  const name = peer.name;
366
366
  const content: string | (TextContent | ImageContent)[] =
367
367
  images && images.length > 0 ? [{ type: "text", text }, ...images] : text;
368
- const details: CollabPromptDetails & { __pendingDisplayTag?: string } = { from: name };
368
+ const details: CollabPromptDetails = { from: name };
369
369
  if (this.#ctx.session.isStreaming) {
370
- // Mid-turn guest prompts are steered: register the pending-display twin
371
- // so queuedMessageCount reflects the queued steer (host pending bar +
372
- // guests' "queued ×N" badge). The tag dequeues the entry when the agent
373
- // consumes the message (mirrors the skill-prompt path).
374
- details.__pendingDisplayTag = this.#ctx.session.enqueueCustomMessageDisplay(text, "steer");
375
370
  this.#ctx.updatePendingMessagesDisplay();
376
371
  this.#ctx.ui.requestRender();
377
372
  this.#scheduleStateBroadcast();
@@ -385,7 +380,7 @@ export class CollabHost {
385
380
  details,
386
381
  attribution: "user",
387
382
  },
388
- { streamingBehavior: "steer" },
383
+ { streamingBehavior: "steer", queueChipText: text },
389
384
  )
390
385
  .catch(err => {
391
386
  logger.warn("collab guest prompt failed", { error: String(err) });
@@ -416,21 +411,23 @@ export class CollabHost {
416
411
 
417
412
  #buildState(): CollabSessionState {
418
413
  const session = this.#ctx.session;
419
- // Context numbers come from the status line's breakdown not
420
- // session.getContextUsage() so guests render exactly what the host's
421
- // own footer shows.
414
+ // Context numbers come from the status line's memoized breakdown so guests
415
+ // render exactly the same anchored, provider-real count the host's own
416
+ // status line shows.
422
417
  const breakdown = this.#ctx.statusLine.getCachedContextBreakdown();
418
+ const tokens = breakdown.usedTokens;
423
419
  return {
424
420
  isStreaming: session.isStreaming,
421
+ isAborting: session.isAborting,
425
422
  queuedMessageCount: session.queuedMessageCount,
426
423
  sessionName: session.sessionName,
427
424
  cwd: this.#ctx.sessionManager.getCwd(),
428
425
  model: session.model,
429
426
  thinkingLevel: session.thinkingLevel,
430
427
  contextUsage: {
431
- tokens: breakdown.usedTokens,
428
+ tokens,
432
429
  contextWindow: breakdown.contextWindow,
433
- percent: breakdown.contextWindow > 0 ? (breakdown.usedTokens / breakdown.contextWindow) * 100 : null,
430
+ percent: tokens !== null && breakdown.contextWindow > 0 ? (tokens / breakdown.contextWindow) * 100 : null,
434
431
  },
435
432
  participants: this.participants,
436
433
  };
@@ -25,7 +25,7 @@ import {
25
25
  } from "@oh-my-pi/pi-wire";
26
26
  import type { ContextUsage } from "../extensibility/extensions/types";
27
27
  import type { AgentSessionEvent } from "../session/agent-session";
28
- import type { SessionEntry, SessionHeader } from "../session/session-manager";
28
+ import type { SessionEntry, SessionHeader } from "../session/session-entries";
29
29
 
30
30
  export type {
31
31
  CollabPromptDetails,
@@ -124,9 +124,6 @@ export default class Index extends Command {
124
124
  export: Flags.string({
125
125
  description: "Export session file to HTML and exit",
126
126
  }),
127
- "list-models": Flags.string({
128
- description: "List available models (with optional fuzzy search)",
129
- }),
130
127
  "no-title": Flags.boolean({
131
128
  description: "Disable title auto-generation",
132
129
  }),
@@ -0,0 +1,61 @@
1
+ /**
2
+ * List, search, and refresh available models.
3
+ */
4
+ import { APP_NAME } from "@oh-my-pi/pi-utils";
5
+ import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
6
+ import { resolveModelsArgs, runModelsCommand } from "../cli/models-cli";
7
+
8
+ export default class Models extends Command {
9
+ static description = "List, search, and refresh available models";
10
+
11
+ static args = {
12
+ action: Args.string({
13
+ description: "ls (default) | find | refresh | canonical | <provider>",
14
+ required: false,
15
+ }),
16
+ pattern: Args.string({
17
+ description: "Filter/search substring, or provider name (required for find)",
18
+ required: false,
19
+ }),
20
+ };
21
+
22
+ static flags = {
23
+ json: Flags.boolean({ description: "Output JSON" }),
24
+ extension: Flags.string({
25
+ char: "e",
26
+ description: "Load an extension file before listing (repeatable)",
27
+ multiple: true,
28
+ }),
29
+ "no-extensions": Flags.boolean({
30
+ description: "Disable extension discovery (explicit -e paths still work)",
31
+ }),
32
+ config: Flags.string({
33
+ description: "Load an extra config.yml-style overlay for this run (repeatable)",
34
+ multiple: true,
35
+ }),
36
+ };
37
+
38
+ static examples = [
39
+ `# List every available model, grouped by provider\n ${APP_NAME} models`,
40
+ `# List one provider's models (any provider name works)\n ${APP_NAME} models openai-codex`,
41
+ `# Find models by substring\n ${APP_NAME} models find minimax`,
42
+ `# Force a fresh catalog fetch (replaces rm -rf ~/.omp/models.db)\n ${APP_NAME} models refresh`,
43
+ `# Show the coalesced canonical model view\n ${APP_NAME} models canonical`,
44
+ `# Machine-readable output\n ${APP_NAME} models --json`,
45
+ ];
46
+
47
+ async run(): Promise<void> {
48
+ const { args, flags } = await this.parse(Models);
49
+ const { action, pattern } = resolveModelsArgs(args.action, args.pattern);
50
+ await runModelsCommand({
51
+ action,
52
+ pattern,
53
+ flags: {
54
+ json: flags.json,
55
+ extensions: flags.extension,
56
+ noExtensions: flags["no-extensions"],
57
+ config: flags.config,
58
+ },
59
+ });
60
+ }
61
+ }