@bubblebrain-ai/bubble 0.0.52 → 0.0.54

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 (483) hide show
  1. package/README.md +4 -12
  2. package/dist/agent/routing-catalog.js +3 -2
  3. package/dist/agent/subagent/router.js +5 -0
  4. package/dist/agent/subagent/runtime.d.ts +1 -0
  5. package/dist/agent/subagent/runtime.js +13 -1
  6. package/dist/agent/subagent-control.d.ts +4 -0
  7. package/dist/agent/subagent-control.js +1 -0
  8. package/dist/agent/subagent-store.js +2 -0
  9. package/dist/agent/workflow/control.d.ts +2 -0
  10. package/dist/agent/workflow/runs.d.ts +1 -0
  11. package/dist/agent/workflow/runs.js +3 -0
  12. package/dist/agent.d.ts +11 -1
  13. package/dist/agent.js +158 -57
  14. package/dist/bin.js +2 -44
  15. package/dist/config.d.ts +14 -1
  16. package/dist/config.js +45 -4
  17. package/dist/context/budget.d.ts +7 -1
  18. package/dist/context/budget.js +25 -2
  19. package/dist/context/compact-llm.d.ts +1 -1
  20. package/dist/context/compact-llm.js +5 -2
  21. package/dist/context/overflow.js +2 -0
  22. package/dist/context/projector.d.ts +8 -0
  23. package/dist/context/projector.js +55 -6
  24. package/dist/context/tool-output-truncate.d.ts +17 -1
  25. package/dist/context/tool-output-truncate.js +171 -49
  26. package/dist/feishu/agent-host/run-driver.js +3 -0
  27. package/dist/feishu/serve.js +2 -0
  28. package/dist/goal/command.d.ts +1 -1
  29. package/dist/goal/command.js +7 -2
  30. package/dist/main.d.ts +1 -1
  31. package/dist/main.js +38 -8
  32. package/dist/memory/db.js +0 -7
  33. package/dist/model-catalog.js +25 -6
  34. package/dist/model-config.js +3 -2
  35. package/dist/model-pricing.d.ts +24 -5
  36. package/dist/model-pricing.js +0 -0
  37. package/dist/model-selection.js +13 -4
  38. package/dist/network/chatgpt-transport.js +0 -34
  39. package/dist/network/errors.d.ts +19 -0
  40. package/dist/network/errors.js +24 -0
  41. package/dist/network/provider-transport.d.ts +0 -4
  42. package/dist/network/provider-transport.js +3 -35
  43. package/dist/network/retry.d.ts +6 -1
  44. package/dist/network/retry.js +9 -0
  45. package/dist/provider-model-policy.d.ts +16 -0
  46. package/dist/provider-model-policy.js +33 -0
  47. package/dist/provider-registry.d.ts +1 -1
  48. package/dist/provider-registry.js +73 -10
  49. package/dist/provider-transform.js +32 -4
  50. package/dist/provider.d.ts +6 -2
  51. package/dist/provider.js +95 -6
  52. package/dist/question/controller.js +5 -4
  53. package/dist/question/normalize.d.ts +6 -0
  54. package/dist/question/normalize.js +11 -0
  55. package/dist/rewind.d.ts +20 -0
  56. package/dist/rewind.js +29 -0
  57. package/dist/sdk/index.d.ts +1 -1
  58. package/dist/sdk/index.js +11 -3
  59. package/dist/session.d.ts +7 -0
  60. package/dist/session.js +20 -0
  61. package/dist/skills/discovery.js +18 -0
  62. package/dist/skills/registry.d.ts +12 -2
  63. package/dist/skills/registry.js +40 -5
  64. package/dist/skills/types.d.ts +2 -0
  65. package/dist/slash-commands/commands.js +120 -81
  66. package/dist/slash-commands/registry.d.ts +2 -1
  67. package/dist/slash-commands/registry.js +3 -0
  68. package/dist/slash-commands/types.d.ts +22 -4
  69. package/dist/stats/usage.d.ts +3 -1
  70. package/dist/stats/usage.js +22 -13
  71. package/dist/tasks/manager.d.ts +4 -2
  72. package/dist/tasks/manager.js +34 -9
  73. package/dist/tasks/promotion.d.ts +2 -2
  74. package/dist/tasks/promotion.js +2 -2
  75. package/dist/tools/agent-lifecycle.js +4 -0
  76. package/dist/tools/bash.d.ts +3 -1
  77. package/dist/tools/bash.js +13 -2
  78. package/dist/tools/grep.d.ts +5 -1
  79. package/dist/tools/grep.js +318 -69
  80. package/dist/tools/index.d.ts +3 -1
  81. package/dist/tools/index.js +1 -0
  82. package/dist/tools/question.js +5 -4
  83. package/dist/tools/read.js +40 -21
  84. package/dist/tui/animation-clock.d.ts +14 -0
  85. package/dist/tui/animation-clock.js +34 -0
  86. package/dist/tui/app.d.ts +176 -0
  87. package/dist/tui/app.js +1647 -0
  88. package/dist/tui/components/approval-dialog.d.ts +43 -0
  89. package/dist/tui/components/approval-dialog.js +247 -0
  90. package/dist/tui/components/bottom-sheet.d.ts +9 -0
  91. package/dist/tui/components/bottom-sheet.js +32 -0
  92. package/dist/tui/components/context-info.d.ts +54 -0
  93. package/dist/tui/components/context-info.js +388 -0
  94. package/dist/tui/components/feedback-dialog.d.ts +56 -0
  95. package/dist/tui/components/feedback-dialog.js +262 -0
  96. package/dist/tui/components/feishu-setup-dialog.d.ts +65 -0
  97. package/dist/tui/components/feishu-setup-dialog.js +332 -0
  98. package/dist/tui/components/image-preview.d.ts +32 -0
  99. package/dist/tui/components/image-preview.js +130 -0
  100. package/dist/tui/components/plan-dialog.d.ts +34 -0
  101. package/dist/tui/components/plan-dialog.js +196 -0
  102. package/dist/tui/components/provider-key-input.d.ts +18 -0
  103. package/dist/tui/components/provider-key-input.js +49 -0
  104. package/dist/tui/components/question-dialog.d.ts +25 -0
  105. package/dist/tui/components/question-dialog.js +263 -0
  106. package/dist/tui/components/responsive-transcript.d.ts +31 -0
  107. package/dist/tui/components/responsive-transcript.js +77 -0
  108. package/dist/tui/components/rewind-picker.d.ts +62 -0
  109. package/dist/tui/components/rewind-picker.js +317 -0
  110. package/dist/tui/components/session-picker.d.ts +49 -0
  111. package/dist/tui/components/session-picker.js +303 -0
  112. package/dist/tui/components/skills-panel.d.ts +59 -0
  113. package/dist/tui/components/skills-panel.js +568 -0
  114. package/dist/tui/components/stats-panel.d.ts +40 -0
  115. package/dist/tui/components/stats-panel.js +301 -0
  116. package/dist/tui/components/streaming-message.d.ts +69 -0
  117. package/dist/tui/components/streaming-message.js +393 -0
  118. package/dist/tui/components/subagent-inspector.d.ts +28 -0
  119. package/dist/tui/components/subagent-inspector.js +127 -0
  120. package/dist/tui/components/task-inspector.d.ts +26 -0
  121. package/dist/tui/components/task-inspector.js +70 -0
  122. package/dist/tui/components/tasks-pane.d.ts +88 -0
  123. package/dist/tui/components/tasks-pane.js +416 -0
  124. package/dist/tui/components/transcript.d.ts +96 -0
  125. package/dist/tui/components/transcript.js +771 -0
  126. package/dist/tui/components/welcome.d.ts +20 -0
  127. package/dist/tui/components/welcome.js +133 -0
  128. package/dist/tui/components/workflow-inspector.d.ts +30 -0
  129. package/dist/tui/components/workflow-inspector.js +136 -0
  130. package/dist/tui/composer-autocomplete.d.ts +61 -0
  131. package/dist/tui/composer-autocomplete.js +383 -0
  132. package/dist/tui/composer-style.d.ts +7 -0
  133. package/dist/tui/composer-style.js +29 -0
  134. package/dist/tui/controller/agent-event-reducer.d.ts +75 -0
  135. package/dist/tui/controller/agent-event-reducer.js +238 -0
  136. package/dist/tui/controller/composer-controller.d.ts +95 -0
  137. package/dist/tui/controller/composer-controller.js +502 -0
  138. package/dist/tui/controller/controller.d.ts +194 -0
  139. package/dist/tui/controller/controller.js +1035 -0
  140. package/dist/tui/controller/effects.d.ts +71 -0
  141. package/dist/tui/controller/effects.js +1 -0
  142. package/dist/tui/controller/goal-runtime-controller.d.ts +47 -0
  143. package/dist/tui/controller/goal-runtime-controller.js +202 -0
  144. package/dist/tui/controller/input-queue-machine.d.ts +71 -0
  145. package/dist/tui/controller/input-queue-machine.js +121 -0
  146. package/dist/tui/controller/intents.d.ts +56 -0
  147. package/dist/tui/controller/intents.js +6 -0
  148. package/dist/tui/controller/overlay-controller.d.ts +64 -0
  149. package/dist/tui/controller/overlay-controller.js +129 -0
  150. package/dist/tui/controller/ports.d.ts +57 -0
  151. package/dist/tui/controller/ports.js +9 -0
  152. package/dist/tui/controller/session-transition.d.ts +54 -0
  153. package/dist/tui/controller/session-transition.js +68 -0
  154. package/dist/tui/controller/state.d.ts +14 -0
  155. package/dist/tui/controller/state.js +36 -0
  156. package/dist/tui/controller/task-runtime-controller.d.ts +66 -0
  157. package/dist/tui/controller/task-runtime-controller.js +158 -0
  158. package/dist/tui/footer.d.ts +37 -0
  159. package/dist/tui/footer.js +80 -0
  160. package/dist/{tui-ink → tui/formatting}/edit-diff.d.ts +1 -1
  161. package/dist/{tui-ink → tui/formatting}/edit-diff.js +1 -1
  162. package/dist/{tui-ink/app-helpers.d.ts → tui/formatting/summary.d.ts} +4 -4
  163. package/dist/{tui-ink/app-helpers.js → tui/formatting/summary.js} +2 -2
  164. package/dist/tui/fullscreen.d.ts +48 -0
  165. package/dist/tui/fullscreen.js +255 -0
  166. package/dist/tui/image-display.d.ts +4 -0
  167. package/dist/tui/image-display.js +30 -12
  168. package/dist/tui/markdown-style.d.ts +5 -0
  169. package/dist/tui/markdown-style.js +23 -0
  170. package/dist/{tui-ink → tui/model}/composer-buffer.js +1 -1
  171. package/dist/tui/model/composer-types.d.ts +17 -0
  172. package/dist/tui/model/composer-types.js +1 -0
  173. package/dist/{tui-ink → tui/model}/display-history.d.ts +7 -1
  174. package/dist/{tui-ink → tui/model}/display-reconstruct.d.ts +1 -1
  175. package/dist/{tui-ink → tui/model}/display-reconstruct.js +54 -6
  176. package/dist/{tui-ink → tui/model}/file-mentions.d.ts +2 -0
  177. package/dist/{tui-ink → tui/model}/file-mentions.js +4 -0
  178. package/dist/tui/model/image-attachment.d.ts +15 -0
  179. package/dist/tui/model/image-attachment.js +1 -0
  180. package/dist/{tui-ink → tui/model}/image-paste.d.ts +7 -1
  181. package/dist/{tui-ink → tui/model}/image-paste.js +46 -0
  182. package/dist/tui/model/input-history.d.ts +1 -0
  183. package/dist/tui/model/input-history.js +1 -0
  184. package/dist/{tui-ink → tui/model}/input-queue.d.ts +1 -1
  185. package/dist/{tui-ink → tui/model}/recent-activity.js +1 -1
  186. package/dist/{tui-ink → tui/model}/subagent-view.d.ts +17 -2
  187. package/dist/{tui-ink → tui/model}/subagent-view.js +125 -4
  188. package/dist/{tui-ink → tui/model}/submit-dedupe.d.ts +1 -1
  189. package/dist/tui/model/theme-style.d.ts +10 -0
  190. package/dist/tui/model/theme-style.js +79 -0
  191. package/dist/{tui-ink → tui/model}/theme.d.ts +11 -23
  192. package/dist/{tui-ink → tui/model}/theme.js +23 -38
  193. package/dist/{tui-ink → tui/model}/trace-groups.d.ts +2 -0
  194. package/dist/{tui-ink → tui/model}/trace-groups.js +91 -30
  195. package/dist/tui/model/trace-interaction.d.ts +63 -0
  196. package/dist/tui/model/trace-interaction.js +87 -0
  197. package/dist/tui/model-picker-data.d.ts +1 -2
  198. package/dist/tui/model-picker-data.js +8 -1
  199. package/dist/tui/model-switch.js +2 -0
  200. package/dist/{tui-ink → tui}/run-session-picker.d.ts +2 -3
  201. package/dist/tui/run-session-picker.js +64 -0
  202. package/dist/tui/run.d.ts +72 -0
  203. package/dist/tui/run.js +139 -0
  204. package/dist/tui/terminal/multiplexer.d.ts +5 -0
  205. package/dist/tui/terminal/multiplexer.js +14 -0
  206. package/dist/tui/testing/fake-agent.d.ts +24 -0
  207. package/dist/tui/testing/fake-agent.js +22 -0
  208. package/dist/tui/testing/spy-host.d.ts +21 -0
  209. package/dist/tui/testing/spy-host.js +55 -0
  210. package/dist/tui/trace-groups.js +27 -6
  211. package/dist/types.d.ts +7 -0
  212. package/dist/update/index.d.ts +1 -0
  213. package/dist/update/index.js +22 -17
  214. package/node_modules/@bubblebrain-ai/pi-tui/LICENSE +21 -0
  215. package/node_modules/@bubblebrain-ai/pi-tui/README.md +854 -0
  216. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.d.ts +26 -0
  217. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.d.ts.map +1 -0
  218. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.js +121 -0
  219. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.js.map +1 -0
  220. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.d.ts +86 -0
  221. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.d.ts.map +1 -0
  222. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.js +649 -0
  223. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.js.map +1 -0
  224. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.d.ts +13 -0
  225. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.d.ts.map +1 -0
  226. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.js +36 -0
  227. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.js.map +1 -0
  228. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.d.ts +22 -0
  229. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.d.ts.map +1 -0
  230. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.js +99 -0
  231. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.js.map +1 -0
  232. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.d.ts +22 -0
  233. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.d.ts.map +1 -0
  234. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.js +36 -0
  235. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.js.map +1 -0
  236. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.d.ts +330 -0
  237. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.d.ts.map +1 -0
  238. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.js +2344 -0
  239. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.js.map +1 -0
  240. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.d.ts +7 -0
  241. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.d.ts.map +1 -0
  242. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.js +43 -0
  243. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.js.map +1 -0
  244. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.d.ts +28 -0
  245. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.d.ts.map +1 -0
  246. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.js +82 -0
  247. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.js.map +1 -0
  248. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.d.ts +48 -0
  249. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.d.ts.map +1 -0
  250. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.js +390 -0
  251. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.js.map +1 -0
  252. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.d.ts +31 -0
  253. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.d.ts.map +1 -0
  254. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.js +67 -0
  255. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.js.map +1 -0
  256. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.d.ts +103 -0
  257. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.d.ts.map +1 -0
  258. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.js +797 -0
  259. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.js.map +1 -0
  260. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.d.ts +56 -0
  261. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.d.ts.map +1 -0
  262. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.js +179 -0
  263. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.js.map +1 -0
  264. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.d.ts +53 -0
  265. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.d.ts.map +1 -0
  266. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.js +175 -0
  267. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.js.map +1 -0
  268. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.d.ts +50 -0
  269. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.d.ts.map +1 -0
  270. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.js +174 -0
  271. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.js.map +1 -0
  272. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.d.ts +12 -0
  273. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.d.ts.map +1 -0
  274. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.js +22 -0
  275. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.js.map +1 -0
  276. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.d.ts +32 -0
  277. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.d.ts.map +1 -0
  278. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.js +109 -0
  279. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.js.map +1 -0
  280. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.d.ts +19 -0
  281. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.d.ts.map +1 -0
  282. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.js +81 -0
  283. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.js.map +1 -0
  284. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.d.ts +13 -0
  285. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.d.ts.map +1 -0
  286. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.js +48 -0
  287. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.js.map +1 -0
  288. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.d.ts +8 -0
  289. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.d.ts.map +1 -0
  290. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.js +26 -0
  291. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.js.map +1 -0
  292. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.d.ts +39 -0
  293. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.d.ts.map +1 -0
  294. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.js +2 -0
  295. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.js.map +1 -0
  296. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.d.ts +16 -0
  297. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.d.ts.map +1 -0
  298. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.js +110 -0
  299. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.js.map +1 -0
  300. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.d.ts +31 -0
  301. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.d.ts.map +1 -0
  302. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.js +42 -0
  303. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.js.map +1 -0
  304. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.d.ts +273 -0
  305. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.d.ts.map +1 -0
  306. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.js +231 -0
  307. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.js.map +1 -0
  308. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.d.ts +184 -0
  309. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.d.ts.map +1 -0
  310. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.js +1174 -0
  311. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.js.map +1 -0
  312. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.d.ts +28 -0
  313. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.d.ts.map +1 -0
  314. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.js +46 -0
  315. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.js.map +1 -0
  316. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.d.ts +10 -0
  317. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.d.ts.map +1 -0
  318. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.js +1268 -0
  319. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.js.map +1 -0
  320. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.d.ts +40 -0
  321. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.d.ts.map +1 -0
  322. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.js +6 -0
  323. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.js.map +1 -0
  324. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.d.ts +42 -0
  325. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.d.ts.map +1 -0
  326. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.js +335 -0
  327. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.js.map +1 -0
  328. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.d.ts +3 -0
  329. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.d.ts.map +1 -0
  330. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.js +60 -0
  331. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.js.map +1 -0
  332. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.d.ts +56 -0
  333. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.d.ts.map +1 -0
  334. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.js +363 -0
  335. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.js.map +1 -0
  336. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.d.ts +10 -0
  337. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.d.ts.map +1 -0
  338. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.js +59 -0
  339. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.js.map +1 -0
  340. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.d.ts +116 -0
  341. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.d.ts.map +1 -0
  342. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.js +518 -0
  343. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.js.map +1 -0
  344. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.d.ts +142 -0
  345. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.d.ts.map +1 -0
  346. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.js +544 -0
  347. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.js.map +1 -0
  348. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.d.ts +11 -0
  349. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.d.ts.map +1 -0
  350. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.js +11 -0
  351. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.js.map +1 -0
  352. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts +140 -0
  353. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts.map +1 -0
  354. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js +1334 -0
  355. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js.map +1 -0
  356. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.d.ts +39 -0
  357. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.d.ts.map +1 -0
  358. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.js +536 -0
  359. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.js.map +1 -0
  360. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts +338 -0
  361. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts.map +1 -0
  362. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js +1016 -0
  363. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js.map +1 -0
  364. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.d.ts +17 -0
  365. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.d.ts.map +1 -0
  366. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.js +27 -0
  367. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.js.map +1 -0
  368. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.d.ts +96 -0
  369. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.d.ts.map +1 -0
  370. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.js +1193 -0
  371. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.js.map +1 -0
  372. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.d.ts +25 -0
  373. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.d.ts.map +1 -0
  374. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.js +96 -0
  375. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.js.map +1 -0
  376. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.d.ts +73 -0
  377. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.d.ts.map +1 -0
  378. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.js +188 -0
  379. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.js.map +1 -0
  380. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/README.md +20 -0
  381. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/build.sh +62 -0
  382. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
  383. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
  384. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/src/darwin-modifiers.c +70 -0
  385. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/README.md +22 -0
  386. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/build.mjs +229 -0
  387. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  388. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  389. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/src/win32-console-mode.c +119 -0
  390. package/node_modules/@bubblebrain-ai/pi-tui/package.json +66 -0
  391. package/node_modules/get-east-asian-width/index.d.ts +60 -0
  392. package/node_modules/get-east-asian-width/index.js +30 -0
  393. package/node_modules/get-east-asian-width/license +9 -0
  394. package/node_modules/get-east-asian-width/lookup-data.js +21 -0
  395. package/node_modules/get-east-asian-width/lookup.js +138 -0
  396. package/node_modules/get-east-asian-width/package.json +71 -0
  397. package/node_modules/get-east-asian-width/readme.md +65 -0
  398. package/node_modules/get-east-asian-width/utilities.js +24 -0
  399. package/node_modules/marked/LICENSE +44 -0
  400. package/node_modules/marked/README.md +115 -0
  401. package/node_modules/marked/bin/main.js +284 -0
  402. package/node_modules/marked/bin/marked.js +16 -0
  403. package/node_modules/marked/lib/marked.d.ts +760 -0
  404. package/node_modules/marked/lib/marked.esm.js +77 -0
  405. package/node_modules/marked/lib/marked.esm.js.map +7 -0
  406. package/node_modules/marked/lib/marked.umd.js +79 -0
  407. package/node_modules/marked/lib/marked.umd.js.map +7 -0
  408. package/node_modules/marked/man/marked.1 +113 -0
  409. package/node_modules/marked/man/marked.1.md +93 -0
  410. package/node_modules/marked/package.json +103 -0
  411. package/package.json +25 -10
  412. package/dist/tui-ink/app.d.ts +0 -76
  413. package/dist/tui-ink/app.js +0 -2377
  414. package/dist/tui-ink/approval/approval-dialog.d.ts +0 -13
  415. package/dist/tui-ink/approval/approval-dialog.js +0 -184
  416. package/dist/tui-ink/approval/diff-view.d.ts +0 -7
  417. package/dist/tui-ink/approval/diff-view.js +0 -44
  418. package/dist/tui-ink/approval/select.d.ts +0 -35
  419. package/dist/tui-ink/approval/select.js +0 -98
  420. package/dist/tui-ink/code-highlight.d.ts +0 -8
  421. package/dist/tui-ink/code-highlight.js +0 -121
  422. package/dist/tui-ink/command-palette.d.ts +0 -32
  423. package/dist/tui-ink/command-palette.js +0 -158
  424. package/dist/tui-ink/compaction-progress.d.ts +0 -19
  425. package/dist/tui-ink/compaction-progress.js +0 -74
  426. package/dist/tui-ink/detect-theme.d.ts +0 -1
  427. package/dist/tui-ink/detect-theme.js +0 -1
  428. package/dist/tui-ink/feedback-dialog.d.ts +0 -19
  429. package/dist/tui-ink/feedback-dialog.js +0 -133
  430. package/dist/tui-ink/feishu-setup-picker.d.ts +0 -5
  431. package/dist/tui-ink/feishu-setup-picker.js +0 -271
  432. package/dist/tui-ink/footer.d.ts +0 -26
  433. package/dist/tui-ink/footer.js +0 -51
  434. package/dist/tui-ink/input-box.d.ts +0 -189
  435. package/dist/tui-ink/input-box.js +0 -1279
  436. package/dist/tui-ink/input-history.d.ts +0 -1
  437. package/dist/tui-ink/input-history.js +0 -1
  438. package/dist/tui-ink/markdown.d.ts +0 -103
  439. package/dist/tui-ink/markdown.js +0 -747
  440. package/dist/tui-ink/message-list.d.ts +0 -49
  441. package/dist/tui-ink/message-list.js +0 -841
  442. package/dist/tui-ink/model-picker.d.ts +0 -143
  443. package/dist/tui-ink/model-picker.js +0 -966
  444. package/dist/tui-ink/plan-confirm.d.ts +0 -7
  445. package/dist/tui-ink/plan-confirm.js +0 -115
  446. package/dist/tui-ink/question-dialog.d.ts +0 -8
  447. package/dist/tui-ink/question-dialog.js +0 -135
  448. package/dist/tui-ink/rewind-picker.d.ts +0 -8
  449. package/dist/tui-ink/rewind-picker.js +0 -102
  450. package/dist/tui-ink/run-session-picker.js +0 -22
  451. package/dist/tui-ink/run.d.ts +0 -62
  452. package/dist/tui-ink/run.js +0 -103
  453. package/dist/tui-ink/session-picker.d.ts +0 -10
  454. package/dist/tui-ink/session-picker.js +0 -113
  455. package/dist/tui-ink/stats-panel.d.ts +0 -11
  456. package/dist/tui-ink/stats-panel.js +0 -65
  457. package/dist/tui-ink/subagent-inspector.d.ts +0 -21
  458. package/dist/tui-ink/subagent-inspector.js +0 -205
  459. package/dist/tui-ink/use-terminal-size.d.ts +0 -4
  460. package/dist/tui-ink/use-terminal-size.js +0 -48
  461. package/dist/tui-ink/waiting-indicator.d.ts +0 -12
  462. package/dist/tui-ink/waiting-indicator.js +0 -129
  463. package/dist/tui-ink/welcome.d.ts +0 -25
  464. package/dist/tui-ink/welcome.js +0 -88
  465. package/dist/tui-ink/wordmark.d.ts +0 -8
  466. package/dist/tui-ink/wordmark.js +0 -103
  467. /package/dist/{tui-ink → tui/model}/composer-buffer.d.ts +0 -0
  468. /package/dist/{tui-ink → tui/model}/display-history.js +0 -0
  469. /package/dist/{tui-ink → tui/model}/input-queue.js +0 -0
  470. /package/dist/{tui-ink → tui/model}/key-events.d.ts +0 -0
  471. /package/dist/{tui-ink → tui/model}/key-events.js +0 -0
  472. /package/dist/{tui-ink → tui/model}/paste-operation-tracker.d.ts +0 -0
  473. /package/dist/{tui-ink → tui/model}/paste-operation-tracker.js +0 -0
  474. /package/dist/{tui-ink → tui/model}/recent-activity.d.ts +0 -0
  475. /package/dist/{tui-ink → tui/model}/submit-dedupe.js +0 -0
  476. /package/dist/{tui-ink → tui/model}/sync-output.d.ts +0 -0
  477. /package/dist/{tui-ink → tui/model}/sync-output.js +0 -0
  478. /package/dist/{tui-ink → tui/model}/terminal-env.d.ts +0 -0
  479. /package/dist/{tui-ink → tui/model}/terminal-env.js +0 -0
  480. /package/dist/{tui-ink → tui/model}/terminal-mouse.d.ts +0 -0
  481. /package/dist/{tui-ink → tui/model}/terminal-mouse.js +0 -0
  482. /package/dist/{tui-ink → tui/model}/width.d.ts +0 -0
  483. /package/dist/{tui-ink → tui/model}/width.js +0 -0
@@ -1,3 +1,4 @@
1
+ import { OPENROUTER_MODEL_ID } from "./provider-model-policy.js";
1
2
  export const BUILTIN_PROVIDERS = [
2
3
  { id: "openrouter", name: "OpenRouter", baseURL: "https://openrouter.ai/api/v1" },
3
4
  { id: "openai", name: "OpenAI", baseURL: "https://api.openai.com/v1", supportsOAuth: true },
@@ -38,13 +39,16 @@ const GPT51_LEVELS = ["off", "low", "medium", "high"];
38
39
  const GPT51_CODEX_MAX_LEVELS = ["off", "low", "medium", "high", "xhigh"];
39
40
  const GPT51_CODEX_MINI_LEVELS = ["off", "medium", "high"];
40
41
  const OPENAI_CHAT_LEVELS = ["off"];
42
+ // Ox Alpha declares mandatory reasoning with exactly low/high/max through
43
+ // OpenRouter's live model metadata. The endpoint rejects disabled reasoning.
44
+ const OPENROUTER_OX_REASONING_LEVELS = ["low", "high", "max"];
41
45
  // Internal representation for APIs that expose thinking as enabled/disabled.
42
46
  // UI code must render supported toggle models as on/off, not as "medium" effort.
43
47
  const TOGGLE_THINKING_LEVELS = ["off", "medium"];
44
48
  // GLM-5.2 accepts OpenAI-style `reasoning_effort`. We expose high and max plus
45
49
  // "off", which disables thinking outright via `thinking: {type: "disabled"}`.
46
50
  const GLM_5_2_LEVELS = ["high", "max", "off"];
47
- // GLM-5.3 Coding Plan normalizes efforts into exactly three real tiers. Its
51
+ // The GLM-5.3 family normalizes efforts into exactly three real tiers. Its
48
52
  // default is max, and disabling thinking maps to low rather than turning it off.
49
53
  const GLM_5_3_LEVELS = ["low", "high", "max"];
50
54
  // Kimi K2.7 Code variants only support thinking mode (disabling it errors), so
@@ -56,7 +60,9 @@ const KIMI_THINKING_ONLY_LEVELS = ["medium"];
56
60
  // "disabled"); the 256k variant exposes effort only, so it has no "off".
57
61
  const KIMI_K3_LEVELS = ["off", "low", "high", "max"];
58
62
  const KIMI_K3_EFFORT_ONLY_LEVELS = ["low", "high", "max"];
59
- const DEEPSEEK_V4_LEVELS = ["high", "max"];
63
+ // DeepSeek V4 defaults to thinking/high, but the OpenAI-compatible API also
64
+ // supports disabling thinking and selecting low/high/max effort explicitly.
65
+ const DEEPSEEK_V4_LEVELS = ["off", "low", "high", "max"];
60
66
  // Bailian token plan grades, read off the endpoint's own 400 responses
61
67
  // ("'reasoning_effort' must be one of: ..."), which differ per model. "none"
62
68
  // maps to our "off". qwen3.6-flash / qwen3.7-* stop at xhigh; deepseek-v4-pro
@@ -84,6 +90,10 @@ const GEMINI_3_FLASH_LEVELS = ["minimal", "low", "medium", "high"];
84
90
  const GEMINI_25_PRO_LEVELS = ["low", "medium", "high"];
85
91
  const GEMINI_25_FLASH_LEVELS = ["off", "low", "medium", "high"];
86
92
  export const BUILTIN_MODELS = [
93
+ // OpenRouter exposes a very large catalog, but Bubble intentionally offers
94
+ // only Ox Alpha. Discovery refreshes this entry's public metadata without
95
+ // expanding provider membership.
96
+ { id: OPENROUTER_MODEL_ID, name: "Ox Alpha", providerId: "openrouter", tier: "strong", reasoningLevels: OPENROUTER_OX_REASONING_LEVELS, defaultReasoningLevel: "max", contextWindow: 1048576 },
87
97
  { id: "gpt-5.6-sol", name: "GPT-5.6-Sol", providerId: "openai-codex", tier: "balanced", reasoningLevels: GPT56_LEVELS, defaultReasoningLevel: "low", contextWindow: 372000, useResponsesLite: true, toolOutputTokenLimit: 10000 },
88
98
  { id: "gpt-5.6-terra", name: "GPT-5.6-Terra", providerId: "openai-codex", tier: "strong", reasoningLevels: GPT56_LEVELS, defaultReasoningLevel: "medium", contextWindow: 372000, useResponsesLite: true, toolOutputTokenLimit: 10000 },
89
99
  { id: "gpt-5.6-luna", name: "GPT-5.6-Luna", providerId: "openai-codex", tier: "strong", reasoningLevels: GPT56_LUNA_LEVELS, defaultReasoningLevel: "medium", contextWindow: 372000, useResponsesLite: true, toolOutputTokenLimit: 10000 },
@@ -108,8 +118,12 @@ export const BUILTIN_MODELS = [
108
118
  { id: "claude-opus-4-8", name: "Claude Opus 4.8", providerId: "anthropic", tier: "strong", reasoningLevels: ANTHROPIC_OPUS_EFFORT_LEVELS, defaultReasoningLevel: "high", contextWindow: 1000000 },
109
119
  { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", providerId: "anthropic", tier: "balanced", reasoningLevels: ANTHROPIC_SONNET_EFFORT_LEVELS, defaultReasoningLevel: "high", contextWindow: 1000000 },
110
120
  { id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5", providerId: "anthropic", tier: "fast", reasoningLevels: ANTHROPIC_CHAT_LEVELS, contextWindow: 200000 },
111
- { id: "deepseek-v4-flash", name: "deepseek-v4-flash", providerId: "deepseek", tier: "fast", reasoningLevels: DEEPSEEK_V4_LEVELS, contextWindow: 1048576 },
112
- { id: "deepseek-v4-pro", name: "deepseek-v4-pro", providerId: "deepseek", tier: "strong", reasoningLevels: DEEPSEEK_V4_LEVELS, contextWindow: 1048576 },
121
+ { id: "deepseek-v4-flash", name: "deepseek-v4-flash", providerId: "deepseek", tier: "fast", reasoningLevels: DEEPSEEK_V4_LEVELS, defaultReasoningLevel: "high", contextWindow: 1048576 },
122
+ // Experimental vision model stays explicit-only: it is text-capable, but
123
+ // automatic fast-tier routing must not silently switch ordinary subagents
124
+ // from the stable Flash model to an experimental multimodal endpoint.
125
+ { id: "deepseek-v4-flash-vision-exp", name: "DeepSeek-V4-Flash-Vision-Exp", providerId: "deepseek", reasoningLevels: DEEPSEEK_V4_LEVELS, defaultReasoningLevel: "high", contextWindow: 1048576 },
126
+ { id: "deepseek-v4-pro", name: "deepseek-v4-pro", providerId: "deepseek", tier: "strong", reasoningLevels: DEEPSEEK_V4_LEVELS, defaultReasoningLevel: "high", contextWindow: 1048576 },
113
127
  // Offline/no-key fallback only: with an API key the registry replaces this
114
128
  // list via fetchGeminiModels (GET /v1beta/models, newest five). Gemini 3
115
129
  // exposes thinking_level (minimal/low/medium/high); 2.5 Pro cannot disable
@@ -123,8 +137,13 @@ export const BUILTIN_MODELS = [
123
137
  { id: "glm-5.1", name: "GLM-5.1", providerId: "zhipuai", tier: "balanced", reasoningLevels: TOGGLE_THINKING_LEVELS, contextWindow: 200000 },
124
138
  { id: "glm-4.7", name: "GLM-4.7", providerId: "zhipuai", reasoningLevels: TOGGLE_THINKING_LEVELS, contextWindow: 204800 },
125
139
  { id: "glm-4.6", name: "GLM-4.6", providerId: "zhipuai", reasoningLevels: TOGGLE_THINKING_LEVELS, contextWindow: 204800 },
126
- // Coding Plan exposes GLM-5.3 before GET /models lists it (verified live
127
- // 2026-08-14). The standard API is still marked "coming soon" in the docs.
140
+ // GLM-5.3-Flash is a native multimodal model with the same mandatory
141
+ // low/high/max thinking contract as GLM-5.3. It is fully available on the
142
+ // Coding Plan and GET /models (verified live 2026-08-26). Official docs list
143
+ // a 1M context window and position it as the fast/low-cost plan model.
144
+ { id: "glm-5.3-flash", name: "GLM-5.3-Flash", providerId: "zhipuai-coding-plan", tier: "fast", reasoningLevels: GLM_5_3_LEVELS, defaultReasoningLevel: "max", contextWindow: 1000000 },
145
+ // Coding Plan exposed GLM-5.3 before GET /models listed it (verified live
146
+ // 2026-08-14). The standard API was still marked "coming soon" at that time.
128
147
  { id: "glm-5.3", name: "GLM-5.3", providerId: "zhipuai-coding-plan", tier: "strong", reasoningLevels: GLM_5_3_LEVELS, defaultReasoningLevel: "max", contextWindow: 1000000 },
129
148
  { id: "glm-5.2", name: "GLM-5.2", providerId: "zhipuai-coding-plan", tier: "strong", reasoningLevels: GLM_5_2_LEVELS, contextWindow: 1000000 },
130
149
  { id: "glm-5.1", name: "GLM-5.1", providerId: "zhipuai-coding-plan", tier: "balanced", reasoningLevels: TOGGLE_THINKING_LEVELS, contextWindow: 200000 },
@@ -7,6 +7,7 @@
7
7
  import { existsSync, readFileSync } from "node:fs";
8
8
  import { join } from "node:path";
9
9
  import { homedir } from "node:os";
10
+ import { filterProviderModels } from "./provider-model-policy.js";
10
11
  const MODELS_PATH = join(homedir(), ".bubble", "models.json");
11
12
  const MODEL_TIERS = ["fast", "balanced", "strong"];
12
13
  function sanitizeTier(value) {
@@ -53,13 +54,13 @@ export class ModelConfig {
53
54
  const cfg = this.data?.providers?.[providerId];
54
55
  if (!cfg?.models)
55
56
  return [];
56
- return cfg.models.map((m) => ({
57
+ return filterProviderModels(providerId, cfg.models.map((m) => ({
57
58
  id: m.id,
58
59
  name: m.name || m.id,
59
60
  providerId,
60
61
  tier: sanitizeTier(m.tier),
61
62
  routingPriority: sanitizeRoutingPriority(m.routingPriority),
62
- }));
63
+ })));
63
64
  }
64
65
  getApiKey(providerId) {
65
66
  return this.data?.providers?.[providerId]?.apiKey;
@@ -1,10 +1,7 @@
1
1
  import type { TokenUsage, UsageCost } from "./types.js";
2
2
  export type { UsageCost } from "./types.js";
3
3
  export type PricingCurrency = UsageCost["currency"];
4
- export interface ModelPricing {
5
- providerId: string;
6
- modelId: string;
7
- currency: PricingCurrency;
4
+ export interface ModelPricingRate {
8
5
  inputCacheHitPerMillion: number;
9
6
  inputCacheMissPerMillion: number;
10
7
  /**
@@ -15,6 +12,28 @@ export interface ModelPricing {
15
12
  */
16
13
  inputCacheWritePerMillion?: number;
17
14
  outputPerMillion: number;
15
+ }
16
+ export interface UtcPricingWindow {
17
+ /** Inclusive minute since 00:00 UTC. */
18
+ startMinute: number;
19
+ /** Exclusive minute since 00:00 UTC. */
20
+ endMinute: number;
21
+ }
22
+ export interface ModelPricing extends ModelPricingRate {
23
+ providerId: string;
24
+ modelId: string;
25
+ currency: PricingCurrency;
26
+ /**
27
+ * Optional recurring UTC time-of-use tariff. Top-level rates are the peak
28
+ * tariff; outside these windows, `offPeak` applies. DeepSeek introduced this
29
+ * pricing shape for the V4 family on 2026-08-16.
30
+ */
31
+ peakWindowsUtc?: UtcPricingWindow[];
32
+ offPeak?: ModelPricingRate;
33
+ /** When the recurring tariff became effective. */
34
+ effectiveFrom?: string;
35
+ /** Rate used before `effectiveFrom` (for historical session statistics). */
36
+ prior?: ModelPricingRate;
18
37
  effectiveUntil?: string;
19
38
  original?: {
20
39
  inputCacheHitPerMillion: number;
@@ -32,4 +51,4 @@ export interface ModelPricing {
32
51
  export declare const MODEL_PRICING_OVERRIDES: ModelPricing[];
33
52
  export declare const MODEL_PRICING: ModelPricing[];
34
53
  export declare function getModelPricing(providerId: string, modelId: string): ModelPricing | undefined;
35
- export declare function calculateUsageCost(providerId: string, modelId: string, usage: TokenUsage): UsageCost | undefined;
54
+ export declare function calculateUsageCost(providerId: string, modelId: string, usage: TokenUsage, at?: Date): UsageCost | undefined;
Binary file
@@ -1,9 +1,18 @@
1
- import { encodeModel } from "./provider-registry.js";
1
+ import { decodeModel, encodeModel } from "./provider-registry.js";
2
+ import { assertProviderModelAllowed } from "./provider-model-policy.js";
2
3
  export function resolveConfiguredModel(input) {
3
4
  const selected = input.cliModel ?? input.sessionModel ?? input.defaultModel;
4
5
  if (!selected)
5
6
  return "";
6
- if (selected.includes(":"))
7
- return selected;
8
- return input.fallbackProviderId ? encodeModel(input.fallbackProviderId, selected) : "";
7
+ const normalized = selected.includes(":")
8
+ ? selected
9
+ : input.fallbackProviderId
10
+ ? encodeModel(input.fallbackProviderId, selected)
11
+ : "";
12
+ if (!normalized)
13
+ return "";
14
+ const { providerId, modelId } = decodeModel(normalized);
15
+ if (providerId)
16
+ assertProviderModelAllowed(providerId, modelId);
17
+ return normalized;
9
18
  }
@@ -32,8 +32,6 @@ export function createChatGptFetch(options = {}) {
32
32
  };
33
33
  }
34
34
  export function createChatGptDispatcher(env = process.env, input) {
35
- if (isBunRuntime())
36
- return undefined;
37
35
  const ca = loadExtraCaCertificates(env);
38
36
  const proxy = input ? nodeProxyForUrl(input, env) : defaultNodeProxy(env);
39
37
  if (!proxy && ca.length === 0)
@@ -49,15 +47,6 @@ export function createChatGptDispatcher(env = process.env, input) {
49
47
  }
50
48
  export function withChatGptNetworkOptions(input, init, env = process.env, dispatcher = createChatGptDispatcher(env, input)) {
51
49
  const next = { ...(init ?? {}) };
52
- if (isBunRuntime()) {
53
- const proxy = bunProxyForUrl(input, env);
54
- if (proxy)
55
- next.proxy = proxy;
56
- const ca = bunExtraCaFiles(env);
57
- if (ca.length > 0)
58
- next.tls = { ...(next.tls ?? {}), ca };
59
- return next;
60
- }
61
50
  if (dispatcher)
62
51
  next.dispatcher = dispatcher;
63
52
  return next;
@@ -94,23 +83,6 @@ function hasProxyEnv(env) {
94
83
  function chatGptProxyOverride(env) {
95
84
  return env.BUBBLE_CHATGPT_PROXY ?? env.bubble_chatgpt_proxy;
96
85
  }
97
- function isBunRuntime() {
98
- return typeof globalThis.Bun !== "undefined";
99
- }
100
- function bunProxyForUrl(input, env) {
101
- const url = urlFromInput(input);
102
- if (!url || shouldBypassProxy(url, env))
103
- return undefined;
104
- const override = chatGptProxyOverride(env);
105
- if (override)
106
- return override;
107
- const allProxy = env.ALL_PROXY ?? env.all_proxy;
108
- if (url.protocol === "https:")
109
- return env.HTTPS_PROXY ?? env.https_proxy ?? allProxy ?? getSystemProxyForUrl(url, env);
110
- if (url.protocol === "http:")
111
- return env.HTTP_PROXY ?? env.http_proxy ?? allProxy ?? getSystemProxyForUrl(url, env);
112
- return undefined;
113
- }
114
86
  function nodeProxyForUrl(input, env) {
115
87
  const url = urlFromInput(input);
116
88
  if (!url || shouldBypassProxy(url, env))
@@ -129,12 +101,6 @@ function defaultNodeProxy(env) {
129
101
  ?? env.HTTPS_PROXY ?? env.https_proxy ?? env.HTTP_PROXY ?? env.http_proxy ?? env.ALL_PROXY ?? env.all_proxy
130
102
  ?? getSystemProxyForUrl(new URL("https://system-proxy-default.invalid/"), env);
131
103
  }
132
- function bunExtraCaFiles(env) {
133
- const bun = globalThis.Bun;
134
- if (!bun?.file)
135
- return [];
136
- return extraCaCertificatePaths(env).map((path) => bun.file(path));
137
- }
138
104
  function urlFromInput(input) {
139
105
  if (input instanceof URL)
140
106
  return input;
@@ -17,6 +17,25 @@ export declare class RateLimitError extends Error {
17
17
  });
18
18
  }
19
19
  export declare function isRateLimitError(error: unknown): error is RateLimitError;
20
+ /**
21
+ * A provider accepted the HTTP request but terminated generation with an
22
+ * in-band protocol error (for example OpenRouter's HTTP-200 SSE error chunk).
23
+ * Keeping the wire status/type structured prevents the agent from mistaking
24
+ * the terminal event for a successful empty response.
25
+ */
26
+ export declare class ProviderResponseError extends Error {
27
+ readonly isProviderResponseError = true;
28
+ readonly status?: number;
29
+ readonly errorType?: string;
30
+ readonly retryAfterMs?: number;
31
+ constructor(message: string, options?: {
32
+ status?: number;
33
+ errorType?: string;
34
+ retryAfterMs?: number;
35
+ cause?: unknown;
36
+ });
37
+ }
38
+ export declare function isProviderResponseError(error: unknown): error is ProviderResponseError;
20
39
  /**
21
40
  * How a provider transport should treat HTTP 429 responses.
22
41
  *
@@ -22,3 +22,27 @@ export function isRateLimitError(error) {
22
22
  && typeof error === "object"
23
23
  && error.isRateLimitError === true;
24
24
  }
25
+ /**
26
+ * A provider accepted the HTTP request but terminated generation with an
27
+ * in-band protocol error (for example OpenRouter's HTTP-200 SSE error chunk).
28
+ * Keeping the wire status/type structured prevents the agent from mistaking
29
+ * the terminal event for a successful empty response.
30
+ */
31
+ export class ProviderResponseError extends Error {
32
+ isProviderResponseError = true;
33
+ status;
34
+ errorType;
35
+ retryAfterMs;
36
+ constructor(message, options = {}) {
37
+ super(message, options.cause !== undefined ? { cause: options.cause } : undefined);
38
+ this.name = "ProviderResponseError";
39
+ this.status = options.status;
40
+ this.errorType = options.errorType;
41
+ this.retryAfterMs = options.retryAfterMs;
42
+ }
43
+ }
44
+ export function isProviderResponseError(error) {
45
+ return !!error
46
+ && typeof error === "object"
47
+ && error.isProviderResponseError === true;
48
+ }
@@ -8,10 +8,6 @@ export interface ProviderFetchOptions {
8
8
  }
9
9
  type RequestInitWithProviderOptions = RequestInit & {
10
10
  dispatcher?: Dispatcher;
11
- proxy?: string;
12
- tls?: {
13
- ca?: unknown[];
14
- };
15
11
  verbose?: boolean;
16
12
  };
17
13
  export declare function providerFetch(input: RequestInfo | URL, init: RequestInit | undefined, options: ProviderFetchOptions): Promise<Response>;
@@ -28,8 +28,6 @@ export function createProviderFetch(options) {
28
28
  };
29
29
  }
30
30
  export function createProviderDispatcher(env = process.env, input, providerName = "provider") {
31
- if (isBunRuntime())
32
- return undefined;
33
31
  const ca = loadExtraCaCertificates(env, providerName);
34
32
  const proxy = input ? nodeProxyForUrl(input, env) : defaultNodeProxy(env);
35
33
  if (!proxy && ca.length === 0)
@@ -47,19 +45,9 @@ export function withProviderNetworkOptions(input, init, options = {}) {
47
45
  const env = options.env ?? process.env;
48
46
  const providerName = options.providerName ?? "provider";
49
47
  const next = { ...(init ?? {}) };
50
- if (isBunRuntime()) {
51
- const proxy = bunProxyForUrl(input, env);
52
- if (proxy)
53
- next.proxy = proxy;
54
- const ca = bunExtraCaFiles(env);
55
- if (ca.length > 0)
56
- next.tls = { ...(next.tls ?? {}), ca };
57
- }
58
- else {
59
- const dispatcher = createProviderDispatcher(env, input, providerName);
60
- if (dispatcher)
61
- next.dispatcher = dispatcher;
62
- }
48
+ const dispatcher = createProviderDispatcher(env, input, providerName);
49
+ if (dispatcher)
50
+ next.dispatcher = dispatcher;
63
51
  if (shouldEnableFetchVerbose(env, options.verboseEnvVar)) {
64
52
  next.verbose = true;
65
53
  }
@@ -129,20 +117,6 @@ function hasProxyEnv(env) {
129
117
  function hasCustomCaEnv(env) {
130
118
  return Boolean(env.NODE_EXTRA_CA_CERTS?.trim() || env.BUBBLE_EXTRA_CA_CERTS?.trim());
131
119
  }
132
- function isBunRuntime() {
133
- return typeof globalThis.Bun !== "undefined";
134
- }
135
- function bunProxyForUrl(input, env) {
136
- const url = urlFromInput(input);
137
- if (!url || shouldBypassProxy(url, env))
138
- return undefined;
139
- const allProxy = env.ALL_PROXY ?? env.all_proxy;
140
- if (url.protocol === "https:")
141
- return env.HTTPS_PROXY ?? env.https_proxy ?? allProxy ?? getSystemProxyForUrl(url, env);
142
- if (url.protocol === "http:")
143
- return env.HTTP_PROXY ?? env.http_proxy ?? allProxy ?? getSystemProxyForUrl(url, env);
144
- return undefined;
145
- }
146
120
  function nodeProxyForUrl(input, env) {
147
121
  const url = urlFromInput(input);
148
122
  if (!url || shouldBypassProxy(url, env))
@@ -157,12 +131,6 @@ function defaultNodeProxy(env) {
157
131
  return env.HTTPS_PROXY ?? env.https_proxy ?? env.HTTP_PROXY ?? env.http_proxy ?? env.ALL_PROXY ?? env.all_proxy
158
132
  ?? getSystemProxyForUrl(new URL("https://system-proxy-default.invalid/"), env);
159
133
  }
160
- function bunExtraCaFiles(env) {
161
- const bun = globalThis.Bun;
162
- if (!bun?.file)
163
- return [];
164
- return extraCaCertificatePaths(env).map((path) => bun.file(path));
165
- }
166
134
  function urlFromInput(input) {
167
135
  if (input instanceof URL)
168
136
  return input;
@@ -10,11 +10,16 @@
10
10
  export declare const MAX_STREAM_INTERRUPTION_RETRIES = 2;
11
11
  export declare class ProviderStreamInterruptedError extends Error {
12
12
  readonly isProviderStreamInterruption = true;
13
+ readonly retryAfterMs?: number;
14
+ readonly maxRetries?: number;
13
15
  constructor(message: string, options?: {
14
16
  cause?: unknown;
17
+ retryAfterMs?: number;
18
+ maxRetries?: number;
15
19
  });
16
20
  }
17
- export declare function isProviderStreamInterruption(error: unknown): boolean;
21
+ export declare function isProviderStreamInterruption(error: unknown): error is ProviderStreamInterruptedError;
22
+ export declare function providerStreamRetryLimit(error: ProviderStreamInterruptedError): number;
18
23
  export declare function getProviderMaxRetries(env?: NodeJS.ProcessEnv): number;
19
24
  export declare function isRetryableHttpStatus(status: number): boolean;
20
25
  /**
@@ -16,9 +16,13 @@ export const MAX_STREAM_INTERRUPTION_RETRIES = 2;
16
16
  const RETRYABLE_HTTP_STATUSES = new Set([408, 429, 500, 502, 503, 504, 529]);
17
17
  export class ProviderStreamInterruptedError extends Error {
18
18
  isProviderStreamInterruption = true;
19
+ retryAfterMs;
20
+ maxRetries;
19
21
  constructor(message, options) {
20
22
  super(message, options);
21
23
  this.name = "ProviderStreamInterruptedError";
24
+ this.retryAfterMs = options?.retryAfterMs;
25
+ this.maxRetries = options?.maxRetries;
22
26
  }
23
27
  }
24
28
  export function isProviderStreamInterruption(error) {
@@ -26,6 +30,11 @@ export function isProviderStreamInterruption(error) {
26
30
  && typeof error === "object"
27
31
  && error.isProviderStreamInterruption === true;
28
32
  }
33
+ export function providerStreamRetryLimit(error) {
34
+ if (error.maxRetries === undefined)
35
+ return MAX_STREAM_INTERRUPTION_RETRIES;
36
+ return Math.max(0, Math.min(MAX_CONFIGURABLE_RETRIES, Math.floor(error.maxRetries)));
37
+ }
29
38
  export function getProviderMaxRetries(env = process.env) {
30
39
  const raw = env.BUBBLE_PROVIDER_MAX_RETRIES?.trim();
31
40
  if (!raw)
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Stable provider-level model constraints.
3
+ *
4
+ * Catalog discovery describes what a provider currently lists; it is not a
5
+ * runtime security boundary. A fixed policy is different: Bubble deliberately
6
+ * exposes and permits exactly one model for that provider, across every host
7
+ * and caller (TUI, CLI, SDK, Feishu, parent agents, and subagents).
8
+ */
9
+ export declare const OPENROUTER_MODEL_ID = "stealth/ox-alpha";
10
+ export declare function getFixedProviderModelId(providerId: string): string | undefined;
11
+ export declare function isProviderModelAllowed(providerId: string, modelId: string): boolean;
12
+ export declare function providerModelPolicyError(providerId: string, modelId: string): string | undefined;
13
+ export declare function assertProviderModelAllowed(providerId: string, modelId: string): void;
14
+ export declare function filterProviderModels<T extends {
15
+ id: string;
16
+ }>(providerId: string, models: readonly T[]): T[];
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Stable provider-level model constraints.
3
+ *
4
+ * Catalog discovery describes what a provider currently lists; it is not a
5
+ * runtime security boundary. A fixed policy is different: Bubble deliberately
6
+ * exposes and permits exactly one model for that provider, across every host
7
+ * and caller (TUI, CLI, SDK, Feishu, parent agents, and subagents).
8
+ */
9
+ export const OPENROUTER_MODEL_ID = "stealth/ox-alpha";
10
+ const FIXED_PROVIDER_MODELS = {
11
+ openrouter: OPENROUTER_MODEL_ID,
12
+ };
13
+ export function getFixedProviderModelId(providerId) {
14
+ return FIXED_PROVIDER_MODELS[providerId.toLowerCase()];
15
+ }
16
+ export function isProviderModelAllowed(providerId, modelId) {
17
+ const fixedModelId = getFixedProviderModelId(providerId);
18
+ return !fixedModelId || modelId === fixedModelId;
19
+ }
20
+ export function providerModelPolicyError(providerId, modelId) {
21
+ const fixedModelId = getFixedProviderModelId(providerId);
22
+ if (!fixedModelId || modelId === fixedModelId)
23
+ return undefined;
24
+ return `Provider "${providerId}" is fixed to model "${fixedModelId}"; received "${modelId}".`;
25
+ }
26
+ export function assertProviderModelAllowed(providerId, modelId) {
27
+ const error = providerModelPolicyError(providerId, modelId);
28
+ if (error)
29
+ throw new Error(error);
30
+ }
31
+ export function filterProviderModels(providerId, models) {
32
+ return models.filter((model) => isProviderModelAllowed(providerId, model.id));
33
+ }
@@ -10,7 +10,7 @@ import { ModelConfig } from "./model-config.js";
10
10
  import { AuthStorage } from "./oauth/index.js";
11
11
  import { type OpenAICodexAuthAdapter } from "./provider-openai-codex.js";
12
12
  import { type GrokAuthAdapter } from "./provider-grok.js";
13
- import type { ThinkingLevel } from "./types.js";
13
+ import { type ThinkingLevel } from "./types.js";
14
14
  export interface ProviderProfile {
15
15
  id: string;
16
16
  name: string;
@@ -16,6 +16,8 @@ import { extractChatGptAccountId, fetchOpenAICodexModelCatalog } from "./provide
16
16
  import { fetchGrokSubscriptionModels } from "./provider-grok.js";
17
17
  import { refreshOpenAICodex } from "./oauth/openai-codex.js";
18
18
  import { refreshGrok } from "./oauth/grok.js";
19
+ import { THINKING_LEVELS } from "./types.js";
20
+ import { filterProviderModels, OPENROUTER_MODEL_ID, } from "./provider-model-policy.js";
19
21
  /** True for providers speaking the OpenAI chat-completions protocol (the default). */
20
22
  export function isOpenAICompatibleProtocol(protocol) {
21
23
  return protocol === undefined || protocol === "openai-chat";
@@ -67,9 +69,56 @@ const MODEL_DISCOVERY_FAILURE_TTL_MS = 10_000;
67
69
  const MODEL_DISCOVERY_DISK_TTL_MS = 24 * 60 * 60 * 1000;
68
70
  /** Discovery must never delay startup or a model picker for long. */
69
71
  const MODEL_DISCOVERY_TIMEOUT_MS = 5_000;
72
+ // Included in the cache identity so a pre-single-model OpenRouter disk cache
73
+ // can never repopulate the picker with the old full remote catalog.
74
+ const OPENROUTER_CATALOG_SCOPE = "ox-alpha-only-v2";
75
+ const OPENROUTER_WIRE_EFFORTS = new Map([
76
+ ["none", "off"],
77
+ ["minimal", "minimal"],
78
+ ["low", "low"],
79
+ ["medium", "medium"],
80
+ ["high", "high"],
81
+ ["xhigh", "xhigh"],
82
+ ["max", "max"],
83
+ ]);
84
+ function openRouterReasoningMetadata(value, fallback) {
85
+ const fallbackMetadata = {
86
+ reasoningLevels: fallback.reasoningLevels,
87
+ defaultReasoningLevel: fallback.defaultReasoningLevel,
88
+ };
89
+ if (!value || typeof value !== "object" || Array.isArray(value))
90
+ return fallbackMetadata;
91
+ const raw = value;
92
+ if (!Array.isArray(raw.supported_efforts))
93
+ return fallbackMetadata;
94
+ const declared = new Set();
95
+ for (const effort of raw.supported_efforts) {
96
+ if (typeof effort !== "string")
97
+ continue;
98
+ const mapped = OPENROUTER_WIRE_EFFORTS.get(effort);
99
+ if (mapped)
100
+ declared.add(mapped);
101
+ }
102
+ if (raw.mandatory === true)
103
+ declared.delete("off");
104
+ const reasoningLevels = THINKING_LEVELS.filter((level) => declared.has(level));
105
+ if (reasoningLevels.length === 0)
106
+ return fallbackMetadata;
107
+ const rawDefault = typeof raw.default_effort === "string"
108
+ ? OPENROUTER_WIRE_EFFORTS.get(raw.default_effort)
109
+ : undefined;
110
+ const defaultReasoningLevel = rawDefault && reasoningLevels.includes(rawDefault)
111
+ ? rawDefault
112
+ : fallback.defaultReasoningLevel && reasoningLevels.includes(fallback.defaultReasoningLevel)
113
+ ? fallback.defaultReasoningLevel
114
+ : reasoningLevels.includes("medium")
115
+ ? "medium"
116
+ : reasoningLevels[0];
117
+ return { reasoningLevels, defaultReasoningLevel };
118
+ }
70
119
  export const BUILTIN_PROVIDERS = CATALOG_PROVIDERS;
71
120
  export const USER_VISIBLE_PROVIDER_IDS = BUILTIN_PROVIDERS
72
- .filter((provider) => !provider.hidden && provider.id !== "openrouter" && provider.id !== "openai-codex")
121
+ .filter((provider) => !provider.hidden && provider.id !== "openai-codex")
73
122
  .map((provider) => provider.id);
74
123
  export function isUserVisibleProvider(providerId) {
75
124
  return USER_VISIBLE_PROVIDER_IDS.includes(providerId);
@@ -289,7 +338,7 @@ export class ProviderRegistry {
289
338
  };
290
339
  }
291
340
  getDefaultModel(providerId, authType = "api") {
292
- const customModels = this.modelConfig.getCustomModels(providerId);
341
+ const customModels = filterProviderModels(providerId, this.modelConfig.getCustomModels(providerId));
293
342
  if (customModels.length > 0) {
294
343
  return customModels[0].id;
295
344
  }
@@ -533,22 +582,34 @@ export class ProviderRegistry {
533
582
  return pending;
534
583
  }
535
584
  async performModelDiscovery(provider) {
536
- const customModels = this.modelConfig.getCustomModels(provider.id);
585
+ const customModels = filterProviderModels(provider.id, this.modelConfig.getCustomModels(provider.id));
537
586
  if (customModels.length > 0) {
538
587
  return { models: customModels, source: "static", authoritative: true };
539
588
  }
540
589
  if (provider.id === "openrouter") {
541
590
  try {
542
- const response = await fetch("https://openrouter.ai/api/v1/models");
591
+ const response = await fetch("https://openrouter.ai/api/v1/models", {
592
+ signal: AbortSignal.timeout(MODEL_DISCOVERY_TIMEOUT_MS),
593
+ });
543
594
  if (!response.ok)
544
595
  throw new Error(`HTTP ${response.status}`);
545
596
  const data = (await response.json());
597
+ const local = this.localModelsForProvider(provider);
598
+ const fallback = local.find((model) => model.id === OPENROUTER_MODEL_ID);
599
+ const remote = (data.data ?? []).find((model) => model?.id === OPENROUTER_MODEL_ID);
600
+ if (!remote || !fallback) {
601
+ return { models: local, source: "static", authoritative: true };
602
+ }
603
+ const reasoning = openRouterReasoningMetadata(remote.reasoning, fallback);
546
604
  return {
547
- models: (data.data ?? []).map((model) => ({
548
- id: model.id,
549
- name: model.name || model.id,
550
- providerId: provider.id,
551
- })),
605
+ models: [{
606
+ ...fallback,
607
+ name: typeof remote.name === "string" && remote.name.trim() ? remote.name : fallback.name,
608
+ ...reasoning,
609
+ contextWindow: typeof remote.context_length === "number" && remote.context_length > 0
610
+ ? remote.context_length
611
+ : fallback.contextWindow,
612
+ }],
552
613
  source: "remote",
553
614
  authoritative: true,
554
615
  };
@@ -719,7 +780,7 @@ export class ProviderRegistry {
719
780
  };
720
781
  }
721
782
  localModelsForProvider(provider) {
722
- const customModels = this.modelConfig.getCustomModels(provider.id);
783
+ const customModels = filterProviderModels(provider.id, this.modelConfig.getCustomModels(provider.id));
723
784
  if (customModels.length > 0)
724
785
  return customModels;
725
786
  const catalogProviderId = provider.id === "openai" && provider.authType === "oauth"
@@ -732,6 +793,7 @@ export class ProviderRegistry {
732
793
  reasoningLevels: model.reasoningLevels,
733
794
  defaultReasoningLevel: model.defaultReasoningLevel,
734
795
  contextWindow: model.contextWindow,
796
+ tier: model.tier,
735
797
  useResponsesLite: model.useResponsesLite,
736
798
  toolOutputTokenLimit: model.toolOutputTokenLimit,
737
799
  }));
@@ -755,6 +817,7 @@ export class ProviderRegistry {
755
817
  provider.authType ?? "api",
756
818
  provider.protocol ?? "default",
757
819
  this.discoveryIdentity(provider),
820
+ provider.id === "openrouter" ? OPENROUTER_CATALOG_SCOPE : undefined,
758
821
  ]);
759
822
  }
760
823
  /**