@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
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Synthesize text with the local TTS engine and play it (or save it with --out).
3
+ *
4
+ * Demonstrates the on-device speech stack end to end: the first run downloads
5
+ * the configured local model, synthesis happens in the TTS worker subprocess,
6
+ * and the resulting WAV is either played through the speakers or written to disk.
7
+ */
8
+ import * as os from "node:os";
9
+ import * as path from "node:path";
10
+ import { getProjectDir, Snowflake } from "@oh-my-pi/pi-utils";
11
+ import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
12
+ import chalk from "chalk";
13
+ import { Settings, settings } from "../config/settings";
14
+ import { playAudioFile, removeTempFile } from "../tts/player";
15
+ import { shutdownTtsClient, ttsClient } from "../tts/tts-client";
16
+ import { encodeWav } from "../tts/wav";
17
+
18
+ export default class Say extends Command {
19
+ static description = "Synthesize text with the local TTS engine and play it through the speakers";
20
+
21
+ static args = {
22
+ text: Args.string({ required: true, description: "Text to speak" }),
23
+ };
24
+
25
+ static flags = {
26
+ voice: Flags.string({ description: "Voice id" }),
27
+ model: Flags.string({ description: "Local TTS model key" }),
28
+ out: Flags.string({ char: "o", description: "Write WAV to this path instead of playing" }),
29
+ };
30
+
31
+ static examples = [
32
+ 'omp say "hello world"',
33
+ 'omp say "hello world" --out /tmp/hello.wav',
34
+ 'omp say "bonjour" --voice af_heart --model kokoro',
35
+ ];
36
+
37
+ async run(): Promise<void> {
38
+ const { args, flags } = await this.parse(Say);
39
+ const text = args.text ?? "";
40
+
41
+ await Settings.init({ cwd: getProjectDir() });
42
+ const model = flags.model ?? settings.get("tts.localModel");
43
+ const voice = flags.voice ?? settings.get("tts.localVoice");
44
+
45
+ let exitCode = 0;
46
+ const unsubscribe = ttsClient.onProgress(event => {
47
+ if (event.status === "progress" && typeof event.progress === "number") {
48
+ process.stderr.write(
49
+ `\r${chalk.dim(`downloading ${event.file ?? model}: ${Math.round(event.progress)}%`)}`,
50
+ );
51
+ } else if (event.status === "done" || event.status === "ready") {
52
+ // Clear the progress line once the download finishes.
53
+ process.stderr.write("\r\x1b[K");
54
+ }
55
+ });
56
+
57
+ try {
58
+ const audio = await ttsClient.synthesize(model, text, { voice });
59
+ if (!audio) {
60
+ process.stderr.write(
61
+ chalk.red(
62
+ `error: could not synthesize with local TTS model "${model}". ` +
63
+ "Run `omp setup speech` to install it.\n",
64
+ ),
65
+ );
66
+ exitCode = 1;
67
+ return;
68
+ }
69
+
70
+ const wav = encodeWav(audio.pcm, audio.sampleRate);
71
+ const durationSec = audio.pcm.length / audio.sampleRate;
72
+
73
+ if (flags.out) {
74
+ await Bun.write(flags.out, wav);
75
+ process.stdout.write(
76
+ `${chalk.green("saved")} ${flags.out} ` +
77
+ `${chalk.dim(`(${voice}, ${model}, ${durationSec.toFixed(1)}s, ${wav.byteLength} bytes)`)}\n`,
78
+ );
79
+ return;
80
+ }
81
+
82
+ const tmp = path.join(os.tmpdir(), `omp-say-${Snowflake.next()}.wav`);
83
+ await Bun.write(tmp, wav);
84
+ try {
85
+ await playAudioFile(tmp);
86
+ process.stdout.write(
87
+ `${chalk.green("spoke")} ${chalk.dim(`(${voice}, ${model}, ${durationSec.toFixed(1)}s)`)}\n`,
88
+ );
89
+ } finally {
90
+ await removeTempFile(tmp);
91
+ }
92
+ } catch (err) {
93
+ process.stderr.write(chalk.red(`error: ${err instanceof Error ? err.message : String(err)}\n`));
94
+ exitCode = 1;
95
+ } finally {
96
+ unsubscribe();
97
+ await shutdownTtsClient();
98
+ }
99
+
100
+ if (exitCode !== 0) process.exit(exitCode);
101
+ }
102
+ }
@@ -7,7 +7,7 @@ import { runSetupCommand, type SetupCommandArgs, type SetupComponent } from "../
7
7
  import { runRootCommand } from "../main";
8
8
  import { initTheme } from "../modes/theme/theme";
9
9
 
10
- const COMPONENTS: SetupComponent[] = ["python", "stt"];
10
+ const COMPONENTS: SetupComponent[] = ["python", "speech"];
11
11
 
12
12
  export interface OnboardingSetupDependencies {
13
13
  runRoot?: typeof runRootCommand;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Get the API key or OAuth token for a provider.
3
+ */
4
+
5
+ import { PROVIDER_REGISTRY } from "@oh-my-pi/pi-ai";
6
+ import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
7
+ import chalk from "chalk";
8
+ import { isAuthenticated, ModelRegistry } from "../config/model-registry";
9
+ import { discoverAuthStorage } from "../sdk";
10
+
11
+ export default class Token extends Command {
12
+ static description = "Get the API key or OAuth token for a provider";
13
+
14
+ static args = {
15
+ provider: Args.string({
16
+ description: "Provider ID (e.g. anthropic, openai)",
17
+ required: true,
18
+ }),
19
+ };
20
+
21
+ static flags = {
22
+ raw: Flags.boolean({
23
+ description: "Output the raw credential value without parsing nested JSON structures",
24
+ default: false,
25
+ }),
26
+ "force-refresh": Flags.boolean({
27
+ description: "Force refresh the OAuth token even if it has not expired",
28
+ default: false,
29
+ }),
30
+ };
31
+
32
+ static examples = [
33
+ "# Get API key for Anthropic\n omp token anthropic",
34
+ "# Get raw Copilot credential JSON\n omp token github-copilot --raw",
35
+ "# Force refresh and get Gemini CLI token\n omp token google-gemini-cli --force-refresh",
36
+ ];
37
+
38
+ async run(): Promise<void> {
39
+ const { args, flags } = await this.parse(Token);
40
+ const providerName = args.provider ?? "";
41
+ const provider = providerName.toLowerCase();
42
+
43
+ const authStorage = await discoverAuthStorage();
44
+ const modelRegistry = new ModelRegistry(authStorage);
45
+
46
+ // Resolve the API key / token
47
+ const apiKey = await modelRegistry.getApiKeyForProvider(provider, undefined, {
48
+ forceRefresh: flags["force-refresh"],
49
+ });
50
+
51
+ if (!isAuthenticated(apiKey)) {
52
+ // Find all active/configured providers
53
+ const activeProviders = new Set<string>();
54
+ for (const p of PROVIDER_REGISTRY) {
55
+ if (authStorage.hasAuth(p.id)) {
56
+ activeProviders.add(p.id);
57
+ }
58
+ }
59
+ const all = authStorage.getAll();
60
+ for (const p in all) {
61
+ if (authStorage.hasAuth(p)) {
62
+ activeProviders.add(p);
63
+ }
64
+ }
65
+
66
+ const msg = `No active credential found for provider "${providerName}".`;
67
+ process.stderr.write(`${chalk.red(msg)}\n`);
68
+ if (activeProviders.size > 0) {
69
+ process.stderr.write(`Configured providers: ${Array.from(activeProviders).sort().join(", ")}\n`);
70
+ }
71
+ process.exitCode = 1;
72
+ return;
73
+ }
74
+
75
+ if (!flags.raw) {
76
+ try {
77
+ const parsed = JSON.parse(apiKey);
78
+ if (parsed && typeof parsed === "object" && typeof parsed.token === "string") {
79
+ process.stdout.write(`${parsed.token}\n`);
80
+ return;
81
+ }
82
+ } catch {
83
+ // Not a JSON string, print as-is
84
+ }
85
+ }
86
+
87
+ process.stdout.write(`${apiKey}\n`);
88
+ }
89
+ }
@@ -1,5 +1,5 @@
1
1
  import { prompt } from "@oh-my-pi/pi-utils";
2
- import * as z from "zod/v4";
2
+ import { z } from "zod/v4";
3
3
  import analyzeFilePrompt from "../../../commit/agentic/prompts/analyze-file.md" with { type: "text" };
4
4
  import type { CommitAgentState } from "../../../commit/agentic/state";
5
5
  import type { NumstatEntry } from "../../../commit/types";
@@ -38,6 +38,9 @@ function buildToolSession(
38
38
  return {
39
39
  cwd: options.cwd,
40
40
  hasUI: false,
41
+ // Programmatic fan-out: results feed the commit agent's evidence, not a
42
+ // model choosing further spawns, so the specialization nudge is noise here.
43
+ suppressSpawnAdvisory: true,
41
44
  getSessionFile: () => ctx.sessionManager.getSessionFile() ?? null,
42
45
  getSessionSpawns: () => options.spawns,
43
46
  settings: options.settings,
@@ -1,4 +1,4 @@
1
- import * as z from "zod/v4";
1
+ import { z } from "zod/v4";
2
2
  import type { CommitAgentState } from "../../../commit/agentic/state";
3
3
  import type { CustomTool } from "../../../extensibility/custom-tools/types";
4
4
  import * as git from "../../../utils/git";
@@ -1,4 +1,4 @@
1
- import * as z from "zod/v4";
1
+ import { z } from "zod/v4";
2
2
  import type { DiffHunk, FileHunks } from "../../../commit/types";
3
3
  import type { CustomTool } from "../../../extensibility/custom-tools/types";
4
4
  import * as git from "../../../utils/git";
@@ -1,4 +1,4 @@
1
- import * as z from "zod/v4";
1
+ import { z } from "zod/v4";
2
2
  import type { CommitAgentState, GitOverviewSnapshot } from "../../../commit/agentic/state";
3
3
  import { extractScopeCandidates } from "../../../commit/analysis/scope";
4
4
  import type { CustomTool } from "../../../extensibility/custom-tools/types";
@@ -1,4 +1,4 @@
1
- import * as z from "zod/v4";
1
+ import { z } from "zod/v4";
2
2
  import type { CommitAgentState } from "../../../commit/agentic/state";
3
3
  import { CHANGELOG_CATEGORIES, type ChangelogCategory } from "../../../commit/types";
4
4
  import type { CustomTool } from "../../../extensibility/custom-tools/types";
@@ -1,4 +1,4 @@
1
- import * as z from "zod/v4";
1
+ import { z } from "zod/v4";
2
2
  import type { CommitAgentState } from "../../../commit/agentic/state";
3
3
  import {
4
4
  capDetails,
@@ -1,4 +1,4 @@
1
- import * as z from "zod/v4";
1
+ import { z } from "zod/v4";
2
2
  import type { CustomTool } from "../../../extensibility/custom-tools/types";
3
3
  import * as git from "../../../utils/git";
4
4
 
@@ -1,4 +1,4 @@
1
- import * as z from "zod/v4";
1
+ import { z } from "zod/v4";
2
2
 
3
3
  export const commitTypeSchema = z.enum([
4
4
  "feat",
@@ -1,4 +1,4 @@
1
- import * as z from "zod/v4";
1
+ import { z } from "zod/v4";
2
2
  import type { CommitAgentState, SplitCommitGroup, SplitCommitPlan } from "../../../commit/agentic/state";
3
3
  import { computeDependencyOrder } from "../../../commit/agentic/topo-sort";
4
4
  import {
@@ -2,7 +2,7 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
2
2
  import type { Api, ApiKey, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
3
3
  import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
4
4
  import { prompt } from "@oh-my-pi/pi-utils";
5
- import * as z from "zod/v4";
5
+ import { z } from "zod/v4";
6
6
  import summarySystemPrompt from "../../commit/prompts/summary-system.md" with { type: "text" };
7
7
  import summaryUserPrompt from "../../commit/prompts/summary-user.md" with { type: "text" };
8
8
  import type { CommitSummary } from "../../commit/types";
@@ -2,7 +2,7 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
2
2
  import type { Api, ApiKey, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
3
3
  import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
4
4
  import { prompt } from "@oh-my-pi/pi-utils";
5
- import * as z from "zod/v4";
5
+ import { z } from "zod/v4";
6
6
  import changelogSystemPrompt from "../../commit/prompts/changelog-system.md" with { type: "text" };
7
7
  import changelogUserPrompt from "../../commit/prompts/changelog-user.md" with { type: "text" };
8
8
  import { CHANGELOG_CATEGORIES, type ChangelogCategory, type ChangelogGenerationResult } from "../../commit/types";
@@ -1,6 +1,6 @@
1
1
  import type { AssistantMessage } from "@oh-my-pi/pi-ai";
2
2
  import { validateToolCall } from "@oh-my-pi/pi-ai";
3
- import * as z from "zod/v4";
3
+ import { z } from "zod/v4";
4
4
  import type { ChangelogCategory, ConventionalAnalysis } from "./types";
5
5
  import { extractTextContent, extractToolCall, normalizeAnalysis, parseJsonPayload } from "./utils";
6
6
 
@@ -212,8 +212,8 @@ export const KEYBINDINGS = {
212
212
  description: "Search history",
213
213
  },
214
214
  "app.stt.toggle": {
215
- defaultKeys: "alt+h",
216
- description: "Toggle speech-to-text",
215
+ defaultKeys: [],
216
+ description: "Toggle speech-to-text (default gesture: hold Space)",
217
217
  },
218
218
  } as const satisfies KeybindingDefinitions;
219
219
 
@@ -393,12 +393,16 @@ export async function discoverOpenAIModelsList(
393
393
  const response = apiKey
394
394
  ? await withAuth(apiKey, key => attempt({ ...baseHeaders, Authorization: `Bearer ${key}` }))
395
395
  : await attempt(baseHeaders);
396
- const payload = (await response.json()) as { data?: Array<{ id: string }> };
396
+ const payload = (await response.json()) as {
397
+ data?: Array<{ id?: string; max_model_len?: unknown; context_length?: unknown }>;
398
+ };
397
399
  const models = payload.data ?? [];
398
400
  const discovered: Model<Api>[] = [];
399
401
  for (const item of models) {
400
402
  const id = item.id;
401
403
  if (!id) continue;
404
+ const contextWindow =
405
+ toPositiveNumberOrUndefined(item.max_model_len) ?? toPositiveNumberOrUndefined(item.context_length) ?? 128000;
402
406
  discovered.push(
403
407
  buildModel({
404
408
  id,
@@ -409,8 +413,8 @@ export async function discoverOpenAIModelsList(
409
413
  reasoning: false,
410
414
  input: ["text"],
411
415
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
412
- contextWindow: 128000,
413
- maxTokens: discoveryDefaultMaxTokens(providerConfig.api),
416
+ contextWindow,
417
+ maxTokens: Math.min(contextWindow, discoveryDefaultMaxTokens(providerConfig.api)),
414
418
  headers,
415
419
  compat: {
416
420
  supportsStore: false,
@@ -463,7 +467,7 @@ export async function discoverProxyModels(
463
467
  ? await withAuth(apiKey, key => attempt({ ...baseHeaders, Authorization: `Bearer ${key}` }))
464
468
  : await attempt(baseHeaders);
465
469
  const payload = (await response.json()) as {
466
- data?: Array<{ id?: string; name?: string; supported_endpoint_types?: string[] }>;
470
+ data?: Array<{ id?: string; name?: string; supported_endpoint_types?: string[]; context_length?: number }>;
467
471
  };
468
472
  const items = payload.data ?? [];
469
473
  const discovered: Model<Api>[] = [];
@@ -499,7 +503,9 @@ export async function discoverProxyModels(
499
503
  // upstream bundled catalogs, so keep costs local-unknown even when
500
504
  // we successfully recover the upstream model identity.
501
505
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
502
- contextWindow: reference?.contextWindow ?? 128000,
506
+ // Prefer the context_length the API reports for this model; fall
507
+ // back to the bundled reference, then a sane default.
508
+ contextWindow: toPositiveNumberOrUndefined(item.context_length) ?? reference?.contextWindow ?? 128000,
503
509
  maxTokens: reference?.maxTokens ?? discoveryDefaultMaxTokens(api),
504
510
  headers,
505
511
  // OpenAI-compat fields are no-ops on anthropic models; the
@@ -17,8 +17,6 @@ import {
17
17
  googleGeminiCliModelManagerOptions,
18
18
  openaiCodexModelManagerOptions,
19
19
  PROVIDER_DESCRIPTORS,
20
- UNK_CONTEXT_WINDOW,
21
- UNK_MAX_TOKENS,
22
20
  } from "@oh-my-pi/pi-catalog/provider-models";
23
21
  import {
24
22
  collapseBuiltModelVariants,
@@ -26,9 +24,11 @@ import {
26
24
  resolveVariantAlias,
27
25
  } from "@oh-my-pi/pi-catalog/variant-collapse";
28
26
 
29
- // Sentinel for local-only OAuth token (LM Studio, vLLM) — declared inline to avoid loading
30
- // any provider module at startup. Must match `DEFAULT_LOCAL_TOKEN` in oauth/lm-studio.ts.
27
+ // Sentinels for local-only OAuth tokens — declared inline to avoid loading
28
+ // provider modules at startup. Must match packages/ai/src/registry/lm-studio.ts
29
+ // and packages/ai/src/registry/vllm.ts.
31
30
  const DEFAULT_LOCAL_TOKEN = "lm-studio-local";
31
+ const DEFAULT_VLLM_LOCAL_TOKEN = "vllm-local";
32
32
 
33
33
  const SPECIAL_MODEL_MANAGER_PROVIDER_IDS: readonly string[] = [
34
34
  "google-antigravity",
@@ -84,6 +84,10 @@ export function isAuthenticated(apiKey: string | undefined | null): apiKey is st
84
84
  return Boolean(apiKey) && apiKey !== kNoAuth;
85
85
  }
86
86
 
87
+ function isDiscoveryBearerApiKey(apiKey: string | undefined | null): apiKey is string {
88
+ return isAuthenticated(apiKey) && apiKey !== DEFAULT_LOCAL_TOKEN && apiKey !== DEFAULT_VLLM_LOCAL_TOKEN;
89
+ }
90
+
87
91
  /** Provider override config (baseUrl, headers, apiKey, compat, transport) without custom models */
88
92
  interface ProviderOverride {
89
93
  baseUrl?: string;
@@ -104,9 +108,19 @@ interface ProviderOverride {
104
108
  * `token-plan-sgp.xiaomimimo.com` at discovery time)
105
109
  * 3. Existing bundled baseUrl (the host baked into `models.json`)
106
110
  *
111
+ * `transport` resolution priority:
112
+ * 1. `providerOverride.transport` (e.g. `pi-native` for auth-gateway users)
113
+ * 2. `existing.transport` (carried over from boot-time override application)
114
+ * 3. `model.transport` (rarely set — discovery defaults omit it)
115
+ *
107
116
  * Without (1), the user's override would lose to discovery; without (2)
108
117
  * preferred over (3), the bundled `api.xiaomimimo.com` would shadow the
109
118
  * tp- token-plan host and produce 401s on the first stream call.
119
+ * Without explicit transport propagation, an openrouter (or any) entry
120
+ * marked `transport: pi-native` in models.yml silently reverts to the
121
+ * default openai-completions transport after the background catalog
122
+ * refresh — so the first `/model` switch after boot hits the raw OpenAI
123
+ * chat-completions URL instead of the gateway's `/v1/pi/stream` (#2555).
110
124
  * See `xiaomi-tp-discovery-merge.test.ts` and the `refresh()` baseUrl-override
111
125
  * regression in `model-registry.test.ts`.
112
126
  */
@@ -120,6 +134,7 @@ export function mergeDiscoveredModel<TApi extends Api>(
120
134
  ...model,
121
135
  baseUrl: providerOverride?.baseUrl ?? model.baseUrl ?? existing.baseUrl,
122
136
  headers: existing.headers ? { ...existing.headers, ...model.headers } : model.headers,
137
+ transport: providerOverride?.transport ?? existing.transport ?? model.transport,
123
138
  compat: model.compatConfig,
124
139
  } as ModelSpec<TApi>);
125
140
  }
@@ -530,9 +545,8 @@ function finalizeCustomModel(model: CustomModelOverlay, options: CustomModelBuil
530
545
  thinking: resolvedModel.thinking ?? reference?.thinking,
531
546
  input: input as ("text" | "image")[],
532
547
  cost,
533
- contextWindow:
534
- resolvedModel.contextWindow ?? reference?.contextWindow ?? (options.useDefaults ? 128000 : undefined),
535
- maxTokens: resolvedModel.maxTokens ?? reference?.maxTokens ?? (options.useDefaults ? 16384 : undefined),
548
+ contextWindow: resolvedModel.contextWindow ?? reference?.contextWindow ?? (options.useDefaults ? 128000 : null),
549
+ maxTokens: resolvedModel.maxTokens ?? reference?.maxTokens ?? (options.useDefaults ? 16384 : null),
536
550
  headers: resolvedModel.headers,
537
551
  omitMaxOutputTokens: resolvedModel.omitMaxOutputTokens ?? reference?.omitMaxOutputTokens,
538
552
  compat: mergeCompat(reference?.compatConfig, resolvedModel.compat),
@@ -866,11 +880,8 @@ export class ModelRegistry {
866
880
  if (!existing) return replacementModel;
867
881
  return {
868
882
  ...replacementModel,
869
- contextWindow:
870
- replacementModel.contextWindow === UNK_CONTEXT_WINDOW
871
- ? existing.contextWindow
872
- : replacementModel.contextWindow,
873
- maxTokens: replacementModel.maxTokens === UNK_MAX_TOKENS ? existing.maxTokens : replacementModel.maxTokens,
883
+ contextWindow: replacementModel.contextWindow ?? existing.contextWindow,
884
+ maxTokens: replacementModel.maxTokens ?? existing.maxTokens,
874
885
  };
875
886
  });
876
887
  }
@@ -895,6 +906,18 @@ export class ModelRegistry {
895
906
  });
896
907
  }
897
908
 
909
+ #resolveStartupModelCacheProviderId(providerId: string): string {
910
+ const descriptor = PROVIDER_DESCRIPTORS.find(candidate => candidate.providerId === providerId);
911
+ if (!descriptor) {
912
+ return providerId;
913
+ }
914
+ const baseUrl =
915
+ this.#runtimeProviderOverrides.get(providerId)?.baseUrl ??
916
+ this.#providerOverrides.get(providerId)?.baseUrl ??
917
+ this.getProviderBaseUrl(providerId);
918
+ return descriptor.createModelManagerOptions({ baseUrl, fetch: this.#fetch }).cacheProviderId ?? providerId;
919
+ }
920
+
898
921
  #loadCachedStandardProviderModels(): { models: Model<Api>[]; authoritativeFreshProviders: Set<string> } {
899
922
  const configuredDiscoveryProviders = new Set(this.#discoverableProviders.map(provider => provider.provider));
900
923
  const cachedModels: Model<Api>[] = [];
@@ -903,7 +926,8 @@ export class ModelRegistry {
903
926
  if (configuredDiscoveryProviders.has(providerId)) {
904
927
  continue;
905
928
  }
906
- const cache = readModelCache<Api>(providerId, 24 * 60 * 60 * 1000, Date.now, this.#cacheDbPath);
929
+ const cacheProviderId = this.#resolveStartupModelCacheProviderId(providerId);
930
+ const cache = readModelCache<Api>(cacheProviderId, 24 * 60 * 60 * 1000, Date.now, this.#cacheDbPath);
907
931
  if (!cache) {
908
932
  continue;
909
933
  }
@@ -933,7 +957,12 @@ export class ModelRegistry {
933
957
  #loadCachedDiscoverableModels(): Model<Api>[] {
934
958
  const cachedModels: Model<Api>[] = [];
935
959
  for (const providerConfig of this.#discoverableProviders) {
936
- const cache = readModelCache<Api>(providerConfig.provider, 24 * 60 * 60 * 1000, Date.now, this.#cacheDbPath);
960
+ const cache = readModelCache<Api>(
961
+ this.#configuredDiscoveryCacheProviderId(providerConfig),
962
+ 24 * 60 * 60 * 1000,
963
+ Date.now,
964
+ this.#cacheDbPath,
965
+ );
937
966
  if (!cache) {
938
967
  this.#providerDiscoveryStates.set(providerConfig.provider, {
939
968
  provider: providerConfig.provider,
@@ -1195,11 +1224,19 @@ export class ModelRegistry {
1195
1224
  this.#rebuildCanonicalIndex();
1196
1225
  }
1197
1226
 
1227
+ #configuredDiscoveryCacheProviderId(providerConfig: DiscoveryProviderConfig): string {
1228
+ if (providerConfig.discovery.type === "openai-models-list") {
1229
+ return `${providerConfig.provider}:openai-models-list-context-v2`;
1230
+ }
1231
+ return providerConfig.provider;
1232
+ }
1233
+
1198
1234
  async #discoverProviderModels(
1199
1235
  providerConfig: DiscoveryProviderConfig,
1200
1236
  strategy: ModelRefreshStrategy,
1201
1237
  ): Promise<Model<Api>[]> {
1202
- const cached = readModelCache<Api>(providerConfig.provider, 24 * 60 * 60 * 1000, Date.now, this.#cacheDbPath);
1238
+ const cacheProviderId = this.#configuredDiscoveryCacheProviderId(providerConfig);
1239
+ const cached = readModelCache<Api>(cacheProviderId, 24 * 60 * 60 * 1000, Date.now, this.#cacheDbPath);
1203
1240
  const requiresAuth = !this.#keylessProviders.has(providerConfig.provider);
1204
1241
  if (requiresAuth) {
1205
1242
  const apiKey = await this.#peekApiKeyForProvider(providerConfig.provider);
@@ -1237,6 +1274,7 @@ export class ModelRegistry {
1237
1274
  providerId,
1238
1275
  staticModels: [],
1239
1276
  cacheDbPath: this.#cacheDbPath,
1277
+ cacheProviderId,
1240
1278
  cacheTtlMs: 24 * 60 * 60 * 1000,
1241
1279
  fetchDynamicModels,
1242
1280
  });
@@ -1278,7 +1316,9 @@ export class ModelRegistry {
1278
1316
  fetch: this.#fetch,
1279
1317
  getBearerApiKeyResolver: async provider => {
1280
1318
  const apiKey = await this.getApiKeyForProvider(provider);
1281
- if (!apiKey || apiKey === DEFAULT_LOCAL_TOKEN || apiKey === kNoAuth) return undefined;
1319
+ if (!isDiscoveryBearerApiKey(apiKey)) {
1320
+ return undefined;
1321
+ }
1282
1322
  return this.resolver(provider);
1283
1323
  },
1284
1324
  };
@@ -1383,11 +1423,20 @@ export class ModelRegistry {
1383
1423
  for (let i = 0; i < standardProviderDescriptors.length; i++) {
1384
1424
  const descriptor = standardProviderDescriptors[i];
1385
1425
  const apiKey = standardProviderKeys[i];
1386
- if (isAuthenticated(apiKey) || descriptor.allowUnauthenticated) {
1426
+ const hasExplicitVllmConfig =
1427
+ descriptor.providerId === "vllm" &&
1428
+ (this.#runtimeProviderOverrides.has(descriptor.providerId) ||
1429
+ this.#providerOverrides.has(descriptor.providerId) ||
1430
+ this.#keylessProviders.has(descriptor.providerId));
1431
+ if (isAuthenticated(apiKey) || descriptor.allowUnauthenticated || hasExplicitVllmConfig) {
1432
+ const discoveryBaseUrl =
1433
+ this.#runtimeProviderOverrides.get(descriptor.providerId)?.baseUrl ??
1434
+ this.#providerOverrides.get(descriptor.providerId)?.baseUrl ??
1435
+ this.getProviderBaseUrl(descriptor.providerId);
1387
1436
  options.push(
1388
1437
  descriptor.createModelManagerOptions({
1389
- apiKey: isAuthenticated(apiKey) ? apiKey : undefined,
1390
- baseUrl: this.getProviderBaseUrl(descriptor.providerId),
1438
+ apiKey: isDiscoveryBearerApiKey(apiKey) ? apiKey : undefined,
1439
+ baseUrl: discoveryBaseUrl,
1391
1440
  fetch: this.#fetch,
1392
1441
  }),
1393
1442
  );
@@ -1739,11 +1788,20 @@ export class ModelRegistry {
1739
1788
  * paths that pre-flight auth before model resolution) can probe a model
1740
1789
  * without resolving an API key. Returns true for keyless providers as well
1741
1790
  * as providers with stored credentials. See issue #993.
1791
+ *
1792
+ * Side-effect-free and synchronous: a command-backed key (`!cmd`) counts as
1793
+ * configured by its presence alone — the program is NOT executed — and OAuth
1794
+ * tokens are NOT refreshed (`authStorage.hasAuth`). This is what keeps the
1795
+ * model-switch pre-flight off the event loop's hot path; the real key
1796
+ * (command execution + OAuth refresh) is resolved lazily per request via
1797
+ * {@link ModelRegistry.resolver}.
1742
1798
  */
1743
1799
  hasConfiguredAuth(model: Model<Api>): boolean {
1744
- const commandKey = this.#resolveCommandBackedApiKey(model.provider);
1800
+ const keyConfig = this.#customProviderApiKeys.get(model.provider);
1745
1801
  return (
1746
- commandKey.configured || this.#keylessProviders.has(model.provider) || this.authStorage.hasAuth(model.provider)
1802
+ isCommandConfigValue(keyConfig) ||
1803
+ this.#keylessProviders.has(model.provider) ||
1804
+ this.authStorage.hasAuth(model.provider)
1747
1805
  );
1748
1806
  }
1749
1807
 
@@ -1247,7 +1247,7 @@ export function resolveCliModel(options: {
1247
1247
  model: undefined,
1248
1248
  selector: undefined,
1249
1249
  warning: undefined,
1250
- error: `Unknown provider "${cliProvider}". Use --list-models to see available providers/models.`,
1250
+ error: `Unknown provider "${cliProvider}". Run "omp models" to see available providers/models.`,
1251
1251
  };
1252
1252
  }
1253
1253
 
@@ -1337,7 +1337,7 @@ export function resolveCliModel(options: {
1337
1337
  selector: undefined,
1338
1338
  thinkingLevel: undefined,
1339
1339
  warning,
1340
- error: `Model "${display}" not found. Use --list-models to see available models.`,
1340
+ error: `Model "${display}" not found. Run "omp models" to see available models.`,
1341
1341
  };
1342
1342
  }
1343
1343
 
@@ -1,4 +1,4 @@
1
- import * as z from "zod/v4";
1
+ import { z } from "zod/v4";
2
2
 
3
3
  const OpenRouterRoutingSchema = z.object({
4
4
  only: z.array(z.string()).optional(),
@@ -35,6 +35,7 @@ const OpenAICompatFieldsSchema = z.object({
35
35
  allowsSyntheticReasoningContentForToolCalls: z.boolean().optional(),
36
36
  requiresAssistantContentForToolCalls: z.boolean().optional(),
37
37
  supportsToolChoice: z.boolean().optional(),
38
+ supportsForcedToolChoice: z.boolean().optional(),
38
39
  disableReasoningOnForcedToolChoice: z.boolean().optional(),
39
40
  disableReasoningOnToolChoice: z.boolean().optional(),
40
41
  thinkingFormat: z.enum(["openai", "openrouter", "zai", "qwen", "qwen-chat-template"]).optional(),
@@ -44,7 +45,7 @@ const OpenAICompatFieldsSchema = z.object({
44
45
  cacheControlFormat: z.enum(["anthropic"]).optional(),
45
46
  supportsStrictMode: z.boolean().optional(),
46
47
  toolStrictMode: z.enum(["all_strict", "none"]).optional(),
47
- streamIdleTimeoutMs: z.number().positive().optional(),
48
+ streamIdleTimeoutMs: z.number().nonnegative().optional(),
48
49
  supportsLongPromptCacheRetention: z.boolean().optional(),
49
50
  supportsReasoningParams: z.boolean().optional(),
50
51
  alwaysSendMaxTokens: z.boolean().optional(),
@@ -124,6 +125,7 @@ const ModelDefinitionSchema = z.object({
124
125
  "azure-openai-responses",
125
126
  "anthropic-messages",
126
127
  "google-generative-ai",
128
+ "google-gemini-cli",
127
129
  "google-vertex",
128
130
  ])
129
131
  .optional(),
@@ -192,6 +194,7 @@ const ProviderConfigSchema = z.object({
192
194
  "azure-openai-responses",
193
195
  "anthropic-messages",
194
196
  "google-generative-ai",
197
+ "google-gemini-cli",
195
198
  "google-vertex",
196
199
  ])
197
200
  .optional(),
@@ -50,12 +50,13 @@ export function validateProviderConfiguration(
50
50
  !config.headers &&
51
51
  !config.compat &&
52
52
  !config.apiKey &&
53
+ config.auth !== "none" &&
53
54
  !config.disableStrictTools &&
54
55
  !hasModelOverrides &&
55
56
  !config.discovery
56
57
  ) {
57
58
  throw new Error(
58
- `Provider ${providerName}: must specify "baseUrl", "headers", "apiKey", "compat", "disableStrictTools", "modelOverrides", "discovery", or "models"`,
59
+ `Provider ${providerName}: must specify "baseUrl", "headers", "apiKey", "auth: none", "compat", "disableStrictTools", "modelOverrides", "discovery", or "models"`,
59
60
  );
60
61
  }
61
62
  }