gloomberb 0.9.9 → 0.10.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 (519) hide show
  1. package/README.md +41 -3
  2. package/README.zh-CN.md +294 -0
  3. package/package.json +10 -4
  4. package/patches/@opentui%2Fcore@0.3.2.patch +26 -69
  5. package/src/api-client/auth.ts +2 -0
  6. package/src/api-client/index.test.ts +4 -0
  7. package/src/api-client/types.ts +3 -0
  8. package/src/app/pane-runtime/pane-settings-runtime.tsx +1 -0
  9. package/src/app/pane-template-dialog-wizard.test.tsx +36 -0
  10. package/src/app/pane-template-dialog-wizard.tsx +10 -3
  11. package/src/app/runtime/desktop-deeplink.test.ts +15 -0
  12. package/src/app/runtime/desktop-deeplink.ts +21 -6
  13. package/src/app.tsx +40 -33
  14. package/src/capabilities/service-capabilities.ts +7 -2
  15. package/src/cli/commands/automation.test.ts +202 -0
  16. package/src/cli/commands/automation.ts +153 -36
  17. package/src/cli/commands/market.ts +1 -14
  18. package/src/cli/commands/system.ts +4 -1
  19. package/src/cli/context.ts +1 -1
  20. package/src/cli/desktop-pane-shot.ts +65 -4
  21. package/src/cli/entry.ts +1 -1
  22. package/src/cli/pane-functions/capabilities.ts +532 -0
  23. package/src/cli/pane-functions/catalog.ts +39 -4
  24. package/src/cli/pane-functions/data.test.ts +17 -0
  25. package/src/cli/pane-functions/data.ts +55 -13
  26. package/src/cli/pane-functions/discovery.ts +36 -9
  27. package/src/cli/pane-functions/index.test.ts +104 -1
  28. package/src/cli/pane-functions/index.ts +64 -5
  29. package/src/cli/pane-functions/options.ts +21 -6
  30. package/src/cli/pane-functions/report.ts +735 -72
  31. package/src/cli/pane-functions/resolver.ts +70 -6
  32. package/src/cli/pane-functions/screenshot.test.ts +431 -0
  33. package/src/cli/pane-functions/screenshot.ts +952 -8
  34. package/src/components/chart/composite/column-layout.ts +215 -0
  35. package/src/components/chart/composite/composite-chart.test.tsx +630 -0
  36. package/src/components/chart/composite/composite-chart.tsx +1148 -0
  37. package/src/components/chart/composite/format.test.ts +137 -0
  38. package/src/components/chart/composite/format.ts +135 -0
  39. package/src/components/chart/composite/index.ts +38 -0
  40. package/src/components/chart/composite/interactions.test.ts +109 -0
  41. package/src/components/chart/composite/interactions.ts +247 -0
  42. package/src/components/chart/composite/price-series.test.ts +39 -0
  43. package/src/components/chart/composite/price-series.ts +78 -0
  44. package/src/components/chart/composite/rasterizer.ts +359 -0
  45. package/src/components/chart/composite/renderers.test.ts +610 -0
  46. package/src/components/chart/composite/scene.test.ts +464 -0
  47. package/src/components/chart/composite/scene.ts +553 -0
  48. package/src/components/chart/composite/text-renderer.ts +285 -0
  49. package/src/components/chart/composite/time-scale.test.ts +88 -0
  50. package/src/components/chart/composite/time-scale.ts +217 -0
  51. package/src/components/chart/composite/types.ts +141 -0
  52. package/src/components/chart/core/data.ts +81 -5
  53. package/src/components/chart/core/index.test.ts +128 -43
  54. package/src/components/chart/core/resolution.ts +0 -6
  55. package/src/components/chart/native/chart-rasterizer.ts +1 -3
  56. package/src/components/chart/native/kitty/index.test.ts +99 -120
  57. package/src/components/chart/native/kitty/manager.ts +3 -0
  58. package/src/components/chart/native/kitty/protocol.ts +4 -1
  59. package/src/components/chart/native/raster/crosshair.ts +79 -46
  60. package/src/components/chart/native/raster/types.ts +2 -11
  61. package/src/components/chart/native/surface/manager.ts +48 -3
  62. package/src/components/chart/price-axis-labels.tsx +18 -4
  63. package/src/components/command-bar/commands/direct/index.ts +25 -0
  64. package/src/components/command-bar/commands/registry.ts +9 -0
  65. package/src/components/command-bar/helpers.test.ts +4 -1
  66. package/src/components/command-bar/list/view.tsx +18 -6
  67. package/src/components/command-bar/multi-select-picker.tsx +6 -5
  68. package/src/components/command-bar/pane-settings.test.ts +141 -0
  69. package/src/components/command-bar/pane-settings.ts +61 -1
  70. package/src/components/command-bar/panel/body.tsx +7 -6
  71. package/src/components/command-bar/routes/root/command-items.ts +4 -4
  72. package/src/components/command-bar/routes/root/results.ts +10 -1
  73. package/src/components/command-bar/routes/root/selection.test.ts +54 -0
  74. package/src/components/command-bar/routes/root/selection.ts +13 -0
  75. package/src/components/command-bar/routing/actions.ts +18 -2
  76. package/src/components/command-bar/routing/list-state.ts +2 -1
  77. package/src/components/command-bar/surface/index.test.tsx +36 -4
  78. package/src/components/command-bar/surface/pane.test.tsx +93 -0
  79. package/src/components/command-bar/theme-picker.tsx +2 -1
  80. package/src/components/command-bar/view-model.ts +19 -20
  81. package/src/components/command-bar/workflow/body.tsx +2 -1
  82. package/src/components/command-bar/workflow/field-row.tsx +3 -2
  83. package/src/components/command-bar/workflow/fields.ts +4 -2
  84. package/src/components/command-bar/workflow/ops.test.ts +161 -0
  85. package/src/components/command-bar/workflow/ops.ts +20 -50
  86. package/src/components/command-bar/workflow/route-actions.test.ts +34 -0
  87. package/src/components/command-bar/workflow/route-actions.ts +4 -0
  88. package/src/components/command-bar/workflow/types.ts +1 -0
  89. package/src/components/feed-data-table/stack-view.tsx +6 -5
  90. package/src/components/index.ts +13 -1
  91. package/src/components/layout/detached-pane-shell.tsx +2 -1
  92. package/src/components/layout/floating-pane.tsx +3 -0
  93. package/src/components/layout/header.tsx +15 -8
  94. package/src/components/layout/pane/footer/index.test.tsx +22 -3
  95. package/src/components/layout/pane/footer/index.tsx +3 -1
  96. package/src/components/layout/pane/footer/model.ts +9 -2
  97. package/src/components/layout/pane/header.tsx +4 -6
  98. package/src/components/layout/pane/index.tsx +3 -0
  99. package/src/components/layout/pane/sidebar.test.tsx +100 -0
  100. package/src/components/layout/pane/sidebar.tsx +239 -0
  101. package/src/components/layout/pane/title.ts +4 -3
  102. package/src/components/layout/shell/action-menu-overlay.tsx +5 -3
  103. package/src/components/layout/shell/drag/runtime.ts +2 -0
  104. package/src/components/layout/shell/menu.test.ts +19 -0
  105. package/src/components/layout/shell/menu.ts +8 -5
  106. package/src/components/layout/shell/native/pointer-runtime.ts +4 -1
  107. package/src/components/layout/shell/pane/layers.tsx +2 -2
  108. package/src/components/layout/status-bar.tsx +8 -7
  109. package/src/components/layout/window-controls.tsx +4 -3
  110. package/src/components/metric-treemap/index.tsx +3 -2
  111. package/src/components/onboarding/onboarding-steps.tsx +30 -29
  112. package/src/components/onboarding/onboarding-wizard.tsx +5 -4
  113. package/src/components/onboarding/portfolio-step/broker-fields-panel.tsx +15 -12
  114. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +17 -16
  115. package/src/components/onboarding/portfolio-step/broker-sync-panel.tsx +6 -5
  116. package/src/components/onboarding/portfolio-step/choose-panel.tsx +5 -4
  117. package/src/components/pane-settings-dialog/desktop.tsx +25 -3
  118. package/src/components/pane-settings-dialog/field-dialogs.tsx +5 -4
  119. package/src/components/pane-settings-dialog/tui.tsx +11 -9
  120. package/src/components/pane-settings-dialog/value.ts +8 -5
  121. package/src/components/pane-settings-dialog.test.tsx +235 -0
  122. package/src/components/pane-settings-dialog.tsx +24 -0
  123. package/src/components/pane-template-wizard.tsx +6 -2
  124. package/src/components/ticker/list-table-view.tsx +3 -2
  125. package/src/components/ui/button.tsx +3 -1
  126. package/src/components/ui/choice-dialog.tsx +6 -1
  127. package/src/components/ui/confirm-dialog.tsx +3 -2
  128. package/src/components/ui/fields.tsx +10 -1
  129. package/src/components/ui/frame.tsx +4 -1
  130. package/src/components/ui/index.ts +5 -0
  131. package/src/components/ui/inline-quick-add.tsx +104 -0
  132. package/src/components/ui/list-view.tsx +5 -4
  133. package/src/components/ui/loading.tsx +2 -1
  134. package/src/components/ui/multi-select/dialog.tsx +188 -44
  135. package/src/components/ui/popover.tsx +10 -0
  136. package/src/components/ui/shortcut-hint.tsx +2 -0
  137. package/src/components/ui/status.tsx +4 -3
  138. package/src/components/ui/tabs.tsx +19 -8
  139. package/src/components/ui/toggle.tsx +44 -3
  140. package/src/components/ui/ui.test.tsx +67 -5
  141. package/src/core/app-services.ts +4 -6
  142. package/src/core/state/session-persistence.ts +3 -2
  143. package/src/data/config/chart-settings.ts +712 -0
  144. package/src/data/config/layout.ts +24 -5
  145. package/src/data/config/store/index.test.ts +377 -14
  146. package/src/data/config/store/normalize.ts +79 -65
  147. package/src/data/fred-series.ts +137 -0
  148. package/src/i18n/index.test.ts +114 -0
  149. package/src/i18n/index.ts +147 -0
  150. package/src/i18n/ja.ts +1013 -0
  151. package/src/i18n/ko.ts +1011 -0
  152. package/src/i18n/languages.test.ts +31 -0
  153. package/src/i18n/languages.ts +72 -0
  154. package/src/i18n/zh-cn.ts +1013 -0
  155. package/src/i18n/zh-tw.ts +1013 -0
  156. package/src/plugins/builtin/account-management/footer.ts +3 -2
  157. package/src/plugins/builtin/account-management/form-components.tsx +3 -2
  158. package/src/plugins/builtin/account-management/keyboard.ts +6 -0
  159. package/src/plugins/builtin/account-management/model.ts +25 -21
  160. package/src/plugins/builtin/account-management/navigation.test.ts +27 -0
  161. package/src/plugins/builtin/account-management/navigation.ts +24 -0
  162. package/src/plugins/builtin/account-management/pane.tsx +120 -85
  163. package/src/plugins/builtin/account-management/password-dialog.tsx +10 -9
  164. package/src/plugins/builtin/ai/agent-history.ts +139 -0
  165. package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +133 -51
  166. package/src/plugins/builtin/ai/ask-ai.test.tsx +83 -22
  167. package/src/plugins/builtin/ai/index.test.ts +302 -0
  168. package/src/plugins/builtin/ai/index.tsx +374 -40
  169. package/src/plugins/builtin/ai/pane-settings.test.ts +155 -0
  170. package/src/plugins/builtin/ai/pane-settings.ts +319 -0
  171. package/src/plugins/builtin/ai/pi/credential-store.test.ts +80 -0
  172. package/src/plugins/builtin/ai/pi/credential-store.ts +138 -0
  173. package/src/plugins/builtin/ai/pi/file-store.ts +138 -0
  174. package/src/plugins/builtin/ai/pi/host.test.ts +500 -0
  175. package/src/plugins/builtin/ai/pi/host.ts +564 -0
  176. package/src/plugins/builtin/ai/pi/index.ts +5 -0
  177. package/src/plugins/builtin/ai/pi/models-store.test.ts +44 -0
  178. package/src/plugins/builtin/ai/pi/models-store.ts +141 -0
  179. package/src/plugins/builtin/ai/pi/providers.ts +68 -0
  180. package/src/plugins/builtin/ai/pi/runtime.test.ts +240 -0
  181. package/src/plugins/builtin/ai/pi/runtime.ts +635 -0
  182. package/src/plugins/builtin/ai/providers.test.ts +68 -0
  183. package/src/plugins/builtin/ai/providers.ts +163 -38
  184. package/src/plugins/builtin/ai/runner-selection.test.ts +174 -0
  185. package/src/plugins/builtin/ai/runner-selection.ts +193 -0
  186. package/src/plugins/builtin/ai/runner-selector.tsx +153 -0
  187. package/src/plugins/builtin/ai/runner.test.ts +198 -0
  188. package/src/plugins/builtin/ai/runner.ts +276 -73
  189. package/src/plugins/builtin/ai/screener/contract.test.ts +26 -73
  190. package/src/plugins/builtin/ai/screener/contract.ts +14 -52
  191. package/src/plugins/builtin/ai/screener/editor-runtime.ts +55 -5
  192. package/src/plugins/builtin/ai/screener/editor.tsx +42 -36
  193. package/src/plugins/builtin/ai/screener/footer.ts +82 -67
  194. package/src/plugins/builtin/ai/screener/keyboard.ts +27 -11
  195. package/src/plugins/builtin/ai/screener/model.test.ts +24 -0
  196. package/src/plugins/builtin/ai/screener/model.ts +9 -3
  197. package/src/plugins/builtin/ai/screener/pane.test.tsx +142 -55
  198. package/src/plugins/builtin/ai/screener/pane.tsx +95 -97
  199. package/src/plugins/builtin/ai/screener/results-view.tsx +8 -41
  200. package/src/plugins/builtin/ai/screener/runner.ts +41 -25
  201. package/src/plugins/builtin/ai/screener/tabs-bar.tsx +2 -2
  202. package/src/plugins/builtin/ai/use-runtime-providers.ts +28 -0
  203. package/src/plugins/builtin/ai/workspace/model.test.ts +227 -0
  204. package/src/plugins/builtin/ai/workspace/model.ts +308 -0
  205. package/src/plugins/builtin/ai/workspace/pane.test.tsx +197 -0
  206. package/src/plugins/builtin/ai/workspace/pane.tsx +896 -0
  207. package/src/plugins/builtin/analytics/index.test.tsx +3 -3
  208. package/src/plugins/builtin/analytics/index.tsx +3 -8
  209. package/src/plugins/builtin/broker-manager/detail.tsx +25 -24
  210. package/src/plugins/builtin/broker-manager/index.test.tsx +1 -1
  211. package/src/plugins/builtin/broker-manager/index.tsx +9 -11
  212. package/src/plugins/builtin/broker-manager/model.ts +11 -10
  213. package/src/plugins/builtin/broker-manager/pane-actions.tsx +22 -21
  214. package/src/plugins/builtin/broker-manager/table.ts +9 -8
  215. package/src/plugins/builtin/changelog/index.tsx +3 -8
  216. package/src/plugins/builtin/chart-composer/chart-spec.ts +89 -0
  217. package/src/plugins/builtin/chart-composer/cli-options.ts +98 -0
  218. package/src/plugins/builtin/chart-composer/date-window-dialog.tsx +120 -0
  219. package/src/plugins/builtin/chart-composer/editor.test.tsx +332 -0
  220. package/src/plugins/builtin/chart-composer/editor.tsx +1107 -0
  221. package/src/plugins/builtin/chart-composer/index.tsx +228 -0
  222. package/src/plugins/builtin/chart-composer/pane.tsx +645 -0
  223. package/src/plugins/builtin/chart-composer/presets.test.ts +561 -0
  224. package/src/plugins/builtin/chart-composer/presets.ts +759 -0
  225. package/src/plugins/builtin/chart-composer/quick-add.test.tsx +245 -0
  226. package/src/plugins/builtin/chart-composer/quick-add.tsx +409 -0
  227. package/src/plugins/builtin/chart-composer/semantic.ts +86 -0
  228. package/src/plugins/builtin/chart-composer/series-catalog.test.ts +65 -0
  229. package/src/plugins/builtin/chart-composer/series-catalog.ts +295 -0
  230. package/src/plugins/builtin/chart-composer/settings.test.ts +163 -0
  231. package/src/plugins/builtin/chart-composer/settings.ts +397 -0
  232. package/src/plugins/builtin/chart-composer/shortcuts.test.ts +44 -0
  233. package/src/plugins/builtin/chart-composer/shortcuts.ts +35 -0
  234. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +103 -0
  235. package/src/plugins/builtin/chat/channel-labels.ts +5 -4
  236. package/src/plugins/builtin/chat/channels.ts +12 -11
  237. package/src/plugins/builtin/chat/chat.test.tsx +32 -0
  238. package/src/plugins/builtin/chat/content/composer-runtime.ts +6 -1
  239. package/src/plugins/builtin/chat/content/composer.tsx +9 -8
  240. package/src/plugins/builtin/chat/content/index.tsx +36 -1
  241. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +8 -7
  242. package/src/plugins/builtin/chat/content/transcript.tsx +9 -3
  243. package/src/plugins/builtin/chat/controller/utils.ts +10 -9
  244. package/src/plugins/builtin/chat/message/desktop.tsx +17 -10
  245. package/src/plugins/builtin/chat/message/profile-popover.test.tsx +122 -3
  246. package/src/plugins/builtin/chat/message/profile-popover.tsx +38 -1
  247. package/src/plugins/builtin/chat/message/terminal.tsx +19 -10
  248. package/src/plugins/builtin/chat/pane-state.test.ts +31 -0
  249. package/src/plugins/builtin/chat/pane-state.ts +30 -0
  250. package/src/plugins/builtin/chat/pane.tsx +27 -4
  251. package/src/plugins/builtin/chat/profile-popover.ts +62 -5
  252. package/src/plugins/builtin/chat/sidebar.test.tsx +4 -7
  253. package/src/plugins/builtin/chat/sidebar.tsx +107 -168
  254. package/src/plugins/builtin/chat/test-harness.tsx +6 -0
  255. package/src/plugins/builtin/cloud/auth-actions.tsx +5 -4
  256. package/src/plugins/builtin/cloud/plugin.tsx +150 -127
  257. package/src/plugins/builtin/composite-plugins.ts +71 -0
  258. package/src/plugins/builtin/correlation/index.test.tsx +4 -4
  259. package/src/plugins/builtin/correlation/index.tsx +3 -8
  260. package/src/plugins/builtin/earnings/index.tsx +37 -21
  261. package/src/plugins/builtin/earnings/model.test.ts +52 -0
  262. package/src/plugins/builtin/earnings/model.ts +32 -0
  263. package/src/plugins/builtin/econ/detail-view.tsx +17 -4
  264. package/src/plugins/builtin/econ/fred-cache.test.ts +57 -11
  265. package/src/plugins/builtin/econ/index.tsx +16 -20
  266. package/src/plugins/builtin/fear-greed/index.tsx +2 -8
  267. package/src/plugins/builtin/fx-matrix/index.tsx +3 -8
  268. package/src/plugins/builtin/help/components.tsx +5 -4
  269. package/src/plugins/builtin/help/index.tsx +14 -17
  270. package/src/plugins/builtin/holders/index.tsx +2 -8
  271. package/src/plugins/builtin/insider/index.tsx +2 -7
  272. package/src/plugins/builtin/kelly-sizer/index.test.tsx +2 -2
  273. package/src/plugins/builtin/kelly-sizer/index.tsx +3 -7
  274. package/src/plugins/builtin/kelly-sizer/test-support.tsx +3 -3
  275. package/src/plugins/builtin/layout-manager/index.tsx +3 -7
  276. package/src/plugins/builtin/market-heatmap/index.tsx +3 -8
  277. package/src/plugins/builtin/market-movers/index.tsx +3 -8
  278. package/src/plugins/builtin/news/index.tsx +12 -16
  279. package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +3 -3
  280. package/src/plugins/builtin/news/wire/index.tsx +83 -73
  281. package/src/plugins/builtin/options/index.tsx +2 -8
  282. package/src/plugins/builtin/plugin-module.test.ts +89 -0
  283. package/src/plugins/builtin/plugin-module.ts +116 -0
  284. package/src/plugins/builtin/portfolio-list/header.tsx +4 -3
  285. package/src/plugins/builtin/portfolio-list/index.test.ts +17 -0
  286. package/src/plugins/builtin/portfolio-list/index.tsx +3 -5
  287. package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +3 -3
  288. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +53 -53
  289. package/src/plugins/builtin/portfolio-list/settings.ts +5 -3
  290. package/src/plugins/builtin/portfolio-list/summary/index.tsx +4 -3
  291. package/src/plugins/builtin/research/index.tsx +2 -8
  292. package/src/plugins/builtin/sec/index.tsx +2 -8
  293. package/src/plugins/builtin/sectors/index.tsx +3 -8
  294. package/src/plugins/builtin/thirteenf/index.tsx +3 -8
  295. package/src/plugins/builtin/thirteenf/pane.test.tsx +1 -1
  296. package/src/plugins/builtin/ticker-detail/chart-tab-switch.test.tsx +49 -23
  297. package/src/plugins/builtin/ticker-detail/index.test.tsx +15 -136
  298. package/src/plugins/builtin/ticker-detail/index.tsx +2 -93
  299. package/src/plugins/builtin/ticker-detail/overview/components.tsx +6 -5
  300. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +41 -17
  301. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -3
  302. package/src/plugins/builtin/ticker-detail/quote-monitor/index.test.tsx +1 -1
  303. package/src/plugins/builtin/ticker-detail/settings.ts +3 -50
  304. package/src/plugins/builtin/ticker-research-backend-plugin.ts +17 -0
  305. package/src/plugins/builtin/ticker-research-plugin.ts +27 -0
  306. package/src/plugins/builtin/tv/channels.ts +27 -0
  307. package/src/plugins/builtin/tv/index.tsx +40 -0
  308. package/src/plugins/builtin/tv/pane.tsx +286 -0
  309. package/src/plugins/builtin/tv/youtube-stream.ts +100 -0
  310. package/src/plugins/builtin/world-indices/index.tsx +3 -8
  311. package/src/plugins/builtin/yield-curve/index.tsx +8 -8
  312. package/src/plugins/catalog-backend.test.ts +21 -0
  313. package/src/plugins/catalog-backend.ts +42 -0
  314. package/src/plugins/catalog-ui.ts +6 -14
  315. package/src/plugins/ibkr/instance-selection.test.ts +0 -1
  316. package/src/plugins/ownership.ts +143 -0
  317. package/src/plugins/prediction-markets/backend-plugin.ts +19 -0
  318. package/src/plugins/prediction-markets/chart.test.tsx +7 -5
  319. package/src/plugins/prediction-markets/chart.tsx +25 -7
  320. package/src/plugins/registry/index.test.ts +51 -0
  321. package/src/plugins/registry/index.ts +8 -0
  322. package/src/plugins/registry/pane-settings.ts +1 -1
  323. package/src/plugins/runtime/index.test.tsx +2 -2
  324. package/src/remote/app-host.tsx +16 -3
  325. package/src/remote/controller.test.ts +102 -1
  326. package/src/remote/controller.ts +122 -0
  327. package/src/remote/types.ts +11 -0
  328. package/src/renderers/electrobun/bun/core-capabilities.ts +123 -27
  329. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +5 -1
  330. package/src/renderers/electrobun/bun/desktop/initialization.ts +5 -1
  331. package/src/renderers/electrobun/bun/desktop/media.ts +15 -0
  332. package/src/renderers/electrobun/bun/desktop/window-style.ts +4 -1
  333. package/src/renderers/electrobun/bun/desktop/workspace/index.test.ts +32 -1
  334. package/src/renderers/electrobun/bun/desktop/workspace/index.ts +10 -0
  335. package/src/renderers/electrobun/bun/index.ts +5 -0
  336. package/src/renderers/electrobun/bun/tui-entry.ts +1 -6
  337. package/src/renderers/electrobun/view/ai-host.ts +124 -19
  338. package/src/renderers/electrobun/view/backend-rpc.ts +4 -0
  339. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +151 -39
  340. package/src/renderers/electrobun/view/desktop/popover.tsx +166 -0
  341. package/src/renderers/electrobun/view/dialog-host.test.ts +25 -0
  342. package/src/renderers/electrobun/view/dialog-host.tsx +75 -12
  343. package/src/renderers/electrobun/view/host/box.tsx +5 -0
  344. package/src/renderers/electrobun/view/host/chart-surface.tsx +10 -9
  345. package/src/renderers/electrobun/view/host/input.tsx +9 -19
  346. package/src/renderers/electrobun/view/host/media-surface.tsx +170 -0
  347. package/src/renderers/electrobun/view/host/mouse.ts +1 -0
  348. package/src/renderers/electrobun/view/host/style.ts +1 -1
  349. package/src/renderers/electrobun/view/host/tabs.tsx +4 -3
  350. package/src/renderers/electrobun/view/main.tsx +10 -3
  351. package/src/renderers/electrobun/view/styles.css +17 -0
  352. package/src/renderers/electrobun/view/ui-host.tsx +7 -0
  353. package/src/renderers/opentui/chart-surface.test.tsx +52 -6
  354. package/src/renderers/opentui/chart-surface.tsx +95 -11
  355. package/src/renderers/opentui/dialog-host.tsx +36 -5
  356. package/src/renderers/opentui/host.tsx +39 -0
  357. package/src/renderers/opentui/start.tsx +30 -0
  358. package/src/renderers/opentui/ui-host.tsx +2 -0
  359. package/src/sources/provider-router/index.test.ts +43 -0
  360. package/src/sources/provider-router/index.ts +4 -0
  361. package/src/sources/provider-router/supplemental.ts +46 -0
  362. package/src/sources/sec-edgar.test.ts +203 -2
  363. package/src/sources/sec-edgar.ts +70 -15
  364. package/src/state/app/context/index.tsx +13 -1
  365. package/src/state/app/context/selector.test.tsx +62 -1
  366. package/src/state/session-persistence.test.ts +13 -2
  367. package/src/sync/controller.test.ts +190 -3
  368. package/src/sync/controller.ts +76 -45
  369. package/src/sync/core-contributors.test.ts +121 -0
  370. package/src/sync/core-contributors.ts +104 -27
  371. package/src/sync/react.ts +6 -8
  372. package/src/sync/types.ts +3 -0
  373. package/src/time-series/alignment.ts +158 -0
  374. package/src/time-series/economic.ts +49 -0
  375. package/src/time-series/field-catalog.ts +306 -0
  376. package/src/time-series/fundamentals.test.ts +505 -0
  377. package/src/time-series/fundamentals.ts +842 -0
  378. package/src/time-series/hooks.ts +45 -0
  379. package/src/time-series/index.ts +14 -0
  380. package/src/time-series/live-quotes.test.ts +219 -0
  381. package/src/time-series/live-quotes.ts +203 -0
  382. package/src/time-series/market-resolution.ts +53 -0
  383. package/src/time-series/market.ts +125 -0
  384. package/src/time-series/reporting.ts +98 -0
  385. package/src/time-series/resolve.test.ts +1281 -0
  386. package/src/time-series/resolve.ts +783 -0
  387. package/src/time-series/spec-studies.test.ts +400 -0
  388. package/src/time-series/spec.ts +487 -0
  389. package/src/time-series/studies.ts +563 -0
  390. package/src/time-series/transforms.test.ts +105 -0
  391. package/src/time-series/transforms.ts +152 -0
  392. package/src/time-series/types.ts +162 -0
  393. package/src/time-series/use-chart-resolution.ts +166 -0
  394. package/src/types/config.ts +9 -11
  395. package/src/types/desktop-window.ts +1 -0
  396. package/src/types/financials.ts +4 -0
  397. package/src/types/media.ts +17 -0
  398. package/src/types/plugin.ts +32 -1
  399. package/src/ui/context-menu.tsx +15 -3
  400. package/src/ui/dialog.tsx +14 -2
  401. package/src/ui/host.tsx +40 -1
  402. package/src/ui/index.tsx +9 -0
  403. package/src/utils/exchanges.test.ts +13 -1
  404. package/src/utils/exchanges.ts +10 -0
  405. package/src/utils/financial-statements.test.ts +138 -0
  406. package/src/utils/financial-statements.ts +115 -8
  407. package/src/utils/format.test.ts +11 -1
  408. package/src/utils/format.ts +7 -0
  409. package/src/version.ts +1 -1
  410. package/src/components/chart/bar-chart-renderer.test.ts +0 -233
  411. package/src/components/chart/bar-chart-renderer.ts +0 -445
  412. package/src/components/chart/comparison/data.test.ts +0 -159
  413. package/src/components/chart/comparison/data.ts +0 -302
  414. package/src/components/chart/comparison/renderer.test.ts +0 -187
  415. package/src/components/chart/comparison/renderer.ts +0 -282
  416. package/src/components/chart/comparison/scene.ts +0 -178
  417. package/src/components/chart/comparison-stock-chart/axis-width.ts +0 -99
  418. package/src/components/chart/comparison-stock-chart/canvas.ts +0 -148
  419. package/src/components/chart/comparison-stock-chart/controls.ts +0 -252
  420. package/src/components/chart/comparison-stock-chart/helpers.ts +0 -101
  421. package/src/components/chart/comparison-stock-chart/interactions.ts +0 -208
  422. package/src/components/chart/comparison-stock-chart/layout-metrics.ts +0 -62
  423. package/src/components/chart/comparison-stock-chart/layout.tsx +0 -155
  424. package/src/components/chart/comparison-stock-chart/legend.tsx +0 -74
  425. package/src/components/chart/comparison-stock-chart/native-surfaces.ts +0 -89
  426. package/src/components/chart/comparison-stock-chart/render-data.ts +0 -227
  427. package/src/components/chart/comparison-stock-chart/render-output.ts +0 -140
  428. package/src/components/chart/comparison-stock-chart/selection-runtime.ts +0 -128
  429. package/src/components/chart/comparison-stock-chart/sources.ts +0 -51
  430. package/src/components/chart/comparison-stock-chart/surface-runtime.ts +0 -159
  431. package/src/components/chart/comparison-stock-chart/toolbar.tsx +0 -117
  432. package/src/components/chart/comparison-stock-chart/types.ts +0 -22
  433. package/src/components/chart/comparison-stock-chart/view.tsx +0 -572
  434. package/src/components/chart/comparison-stock-chart/viewport-runtime.ts +0 -239
  435. package/src/components/chart/comparison-stock-chart.tsx +0 -24
  436. package/src/components/chart/core/axis-measure.ts +0 -33
  437. package/src/components/chart/core/controller.test.ts +0 -157
  438. package/src/components/chart/core/controller.ts +0 -349
  439. package/src/components/chart/core/indicator-selector.tsx +0 -75
  440. package/src/components/chart/core/keyboard.ts +0 -24
  441. package/src/components/chart/core/pane-settings.test.ts +0 -52
  442. package/src/components/chart/core/pane-settings.ts +0 -141
  443. package/src/components/chart/core/scroll.test.ts +0 -64
  444. package/src/components/chart/core/scroll.ts +0 -82
  445. package/src/components/chart/core/window-viewport.test.ts +0 -69
  446. package/src/components/chart/core/window-viewport.ts +0 -162
  447. package/src/components/chart/display-cursor.ts +0 -153
  448. package/src/components/chart/indicators/bands.test.ts +0 -36
  449. package/src/components/chart/indicators/bands.ts +0 -29
  450. package/src/components/chart/indicators/moving-averages.test.ts +0 -39
  451. package/src/components/chart/indicators/moving-averages.ts +0 -42
  452. package/src/components/chart/indicators/options.test.ts +0 -32
  453. package/src/components/chart/indicators/options.ts +0 -107
  454. package/src/components/chart/indicators/oscillators.test.ts +0 -58
  455. package/src/components/chart/indicators/oscillators.ts +0 -92
  456. package/src/components/chart/native/canvas-bitmaps.ts +0 -140
  457. package/src/components/chart/native/raster/comparison-chart.ts +0 -162
  458. package/src/components/chart/native/surface/hooks.ts +0 -144
  459. package/src/components/chart/native/surface/rendering.ts +0 -211
  460. package/src/components/chart/static/bar-chart-surface.test.tsx +0 -101
  461. package/src/components/chart/static/bar-chart-surface.tsx +0 -292
  462. package/src/components/chart/stock-chart/auto.ts +0 -216
  463. package/src/components/chart/stock-chart/axis-width.ts +0 -146
  464. package/src/components/chart/stock-chart/bitmaps.ts +0 -22
  465. package/src/components/chart/stock-chart/canvas.ts +0 -202
  466. package/src/components/chart/stock-chart/controls.tsx +0 -149
  467. package/src/components/chart/stock-chart/cursor.ts +0 -60
  468. package/src/components/chart/stock-chart/data-runtime.ts +0 -128
  469. package/src/components/chart/stock-chart/footer.test.ts +0 -65
  470. package/src/components/chart/stock-chart/footer.ts +0 -303
  471. package/src/components/chart/stock-chart/geometry.ts +0 -160
  472. package/src/components/chart/stock-chart/index.test.ts +0 -532
  473. package/src/components/chart/stock-chart/index.tsx +0 -29
  474. package/src/components/chart/stock-chart/indicators.ts +0 -226
  475. package/src/components/chart/stock-chart/interaction-runtime.ts +0 -44
  476. package/src/components/chart/stock-chart/interactions.ts +0 -300
  477. package/src/components/chart/stock-chart/keyboard.ts +0 -373
  478. package/src/components/chart/stock-chart/layout-metrics.ts +0 -62
  479. package/src/components/chart/stock-chart/layout.tsx +0 -177
  480. package/src/components/chart/stock-chart/native-surfaces.ts +0 -121
  481. package/src/components/chart/stock-chart/presentation.ts +0 -67
  482. package/src/components/chart/stock-chart/renderer-switch.test.tsx +0 -263
  483. package/src/components/chart/stock-chart/rendering/bounds.ts +0 -116
  484. package/src/components/chart/stock-chart/rendering/candidates.ts +0 -145
  485. package/src/components/chart/stock-chart/rendering/data.ts +0 -370
  486. package/src/components/chart/stock-chart/rendering/invalidation.ts +0 -68
  487. package/src/components/chart/stock-chart/rendering/output.ts +0 -304
  488. package/src/components/chart/stock-chart/rendering/planning.ts +0 -122
  489. package/src/components/chart/stock-chart/rendering/projection.test.tsx +0 -98
  490. package/src/components/chart/stock-chart/rendering/projection.ts +0 -112
  491. package/src/components/chart/stock-chart/rendering/state.test.ts +0 -70
  492. package/src/components/chart/stock-chart/rendering/state.ts +0 -283
  493. package/src/components/chart/stock-chart/requests.ts +0 -337
  494. package/src/components/chart/stock-chart/resolution-support.ts +0 -96
  495. package/src/components/chart/stock-chart/resolved/runtime.ts +0 -289
  496. package/src/components/chart/stock-chart/resolved.tsx +0 -359
  497. package/src/components/chart/stock-chart/settings.ts +0 -81
  498. package/src/components/chart/stock-chart/surface-runtime.ts +0 -62
  499. package/src/components/chart/stock-chart/types.ts +0 -25
  500. package/src/components/chart/stock-chart/view.tsx +0 -155
  501. package/src/components/chart/stock-chart/viewport/effects.ts +0 -293
  502. package/src/components/chart/stock-chart/viewport/index.ts +0 -144
  503. package/src/components/chart/stock-chart/viewport/runtime.ts +0 -375
  504. package/src/components/chart/time-axis-label.tsx +0 -175
  505. package/src/plugins/builtin/ai/gloomberb-cli.ts +0 -71
  506. package/src/plugins/builtin/ai/screener/action-bar.tsx +0 -58
  507. package/src/plugins/builtin/comparison-chart/index.test.tsx +0 -848
  508. package/src/plugins/builtin/comparison-chart/index.tsx +0 -194
  509. package/src/plugins/builtin/econ/fred-cache.ts +0 -106
  510. package/src/plugins/builtin/plugin-groups.ts +0 -130
  511. package/src/plugins/builtin/ticker-detail/chart-pane.tsx +0 -91
  512. package/src/plugins/builtin/ticker-detail/chart-tab.tsx +0 -100
  513. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/content.tsx +0 -296
  514. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/data.ts +0 -62
  515. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/model.ts +0 -608
  516. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/settings.ts +0 -65
  517. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/types.ts +0 -58
  518. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph.tsx +0 -138
  519. package/src/plugins/builtin/ticker-detail/data-panes.test.ts +0 -220
@@ -0,0 +1,141 @@
1
+ import type { Api, Model, ModelsStore, ModelsStoreEntry } from "@earendil-works/pi-ai";
2
+ import { join } from "node:path";
3
+ import { LockedJsonFile } from "./file-store";
4
+
5
+ const MODELS_FILE_VERSION = 1;
6
+ const PROVIDER_ID_PATTERN = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
7
+
8
+ interface ModelsFile {
9
+ version: typeof MODELS_FILE_VERSION;
10
+ providers: Record<string, ModelsStoreEntry>;
11
+ }
12
+
13
+ function assertProviderId(providerId: string): void {
14
+ if (!PROVIDER_ID_PATTERN.test(providerId)) throw new Error(`Invalid AI provider id: ${providerId}`);
15
+ }
16
+
17
+ function isFiniteNumber(value: unknown): value is number {
18
+ return typeof value === "number" && Number.isFinite(value);
19
+ }
20
+
21
+ function isStringRecord(value: unknown): value is Record<string, string> {
22
+ return !!value
23
+ && typeof value === "object"
24
+ && !Array.isArray(value)
25
+ && Object.values(value).every((entry) => typeof entry === "string");
26
+ }
27
+
28
+ function isCost(value: unknown): boolean {
29
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
30
+ const candidate = value as Record<string, unknown>;
31
+ if (!["input", "output", "cacheRead", "cacheWrite"].every((key) => isFiniteNumber(candidate[key]))) return false;
32
+ return candidate.tiers === undefined
33
+ || (Array.isArray(candidate.tiers) && candidate.tiers.every((tier) => {
34
+ if (!tier || typeof tier !== "object" || Array.isArray(tier)) return false;
35
+ const record = tier as Record<string, unknown>;
36
+ return ["input", "output", "cacheRead", "cacheWrite", "inputTokensAbove"]
37
+ .every((key) => isFiniteNumber(record[key]));
38
+ }));
39
+ }
40
+
41
+ function isModel(value: unknown): value is Model<Api> {
42
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
43
+ const model = value as Record<string, unknown>;
44
+ return typeof model.id === "string"
45
+ && typeof model.name === "string"
46
+ && typeof model.api === "string"
47
+ && typeof model.provider === "string"
48
+ && typeof model.baseUrl === "string"
49
+ && typeof model.reasoning === "boolean"
50
+ && Array.isArray(model.input)
51
+ && model.input.every((input) => input === "text" || input === "image")
52
+ && isCost(model.cost)
53
+ && isFiniteNumber(model.contextWindow)
54
+ && isFiniteNumber(model.maxTokens)
55
+ && (model.headers === undefined || isStringRecord(model.headers));
56
+ }
57
+
58
+ function validateEntry(value: unknown, providerId?: string): ModelsStoreEntry {
59
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
60
+ throw new Error("The cached AI model entry is invalid.");
61
+ }
62
+ const candidate = value as { models?: unknown; lastModified?: unknown; checkedAt?: unknown };
63
+ if (!Array.isArray(candidate.models) || !candidate.models.every(isModel)) {
64
+ throw new Error("The cached AI model entry contains invalid models.");
65
+ }
66
+ if (providerId && candidate.models.some((model) => model.provider !== providerId)) {
67
+ throw new Error(`The cached model provider does not match ${providerId}.`);
68
+ }
69
+ if (candidate.lastModified !== undefined && !isFiniteNumber(candidate.lastModified)) {
70
+ throw new Error("The cached AI model last-modified timestamp is invalid.");
71
+ }
72
+ if (candidate.checkedAt !== undefined && !isFiniteNumber(candidate.checkedAt)) {
73
+ throw new Error("The cached AI model check timestamp is invalid.");
74
+ }
75
+ return structuredClone(candidate) as ModelsStoreEntry;
76
+ }
77
+
78
+ function validateModelsFile(value: unknown): ModelsFile {
79
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
80
+ throw new Error("The AI model cache is not an object.");
81
+ }
82
+ const candidate = value as { version?: unknown; providers?: unknown };
83
+ if (candidate.version !== MODELS_FILE_VERSION) {
84
+ throw new Error(`Unsupported AI model cache version: ${String(candidate.version)}`);
85
+ }
86
+ if (!candidate.providers || typeof candidate.providers !== "object" || Array.isArray(candidate.providers)) {
87
+ throw new Error("The AI model cache does not contain a providers object.");
88
+ }
89
+
90
+ const providers: Record<string, ModelsStoreEntry> = Object.create(null) as Record<string, ModelsStoreEntry>;
91
+ for (const [providerId, entry] of Object.entries(candidate.providers)) {
92
+ assertProviderId(providerId);
93
+ providers[providerId] = validateEntry(entry, providerId);
94
+ }
95
+ return { version: MODELS_FILE_VERSION, providers };
96
+ }
97
+
98
+ function createModelsFile(): ModelsFile {
99
+ return {
100
+ version: MODELS_FILE_VERSION,
101
+ providers: Object.create(null) as Record<string, ModelsStoreEntry>,
102
+ };
103
+ }
104
+
105
+ export function resolvePiModelsPath(dataDir: string): string {
106
+ return join(dataDir, "ai", "models.json");
107
+ }
108
+
109
+ export class PiFileModelsStore implements ModelsStore {
110
+ readonly path: string;
111
+ private readonly file: LockedJsonFile<ModelsFile>;
112
+
113
+ constructor(dataDir: string) {
114
+ this.path = resolvePiModelsPath(dataDir);
115
+ this.file = new LockedJsonFile(this.path, {
116
+ create: createModelsFile,
117
+ validate: validateModelsFile,
118
+ });
119
+ }
120
+
121
+ async read(providerId: string): Promise<ModelsStoreEntry | undefined> {
122
+ assertProviderId(providerId);
123
+ const entry = (await this.file.read()).providers[providerId];
124
+ return entry ? structuredClone(entry) : undefined;
125
+ }
126
+
127
+ async write(providerId: string, entry: ModelsStoreEntry): Promise<void> {
128
+ assertProviderId(providerId);
129
+ const validated = validateEntry(entry, providerId);
130
+ await this.file.update((file) => {
131
+ file.providers[providerId] = structuredClone(validated);
132
+ });
133
+ }
134
+
135
+ async delete(providerId: string): Promise<void> {
136
+ assertProviderId(providerId);
137
+ await this.file.update((file) => {
138
+ delete file.providers[providerId];
139
+ });
140
+ }
141
+ }
@@ -0,0 +1,68 @@
1
+ import {
2
+ createModels,
3
+ type AuthContext,
4
+ type CredentialStore,
5
+ type ModelsStore,
6
+ type MutableModels,
7
+ type Provider,
8
+ } from "@earendil-works/pi-ai";
9
+ import { registerBunOAuthFlows } from "@earendil-works/pi-ai/bun-oauth";
10
+ import { anthropicProvider } from "@earendil-works/pi-ai/providers/anthropic";
11
+ import { githubCopilotProvider } from "@earendil-works/pi-ai/providers/github-copilot";
12
+ import { googleProvider } from "@earendil-works/pi-ai/providers/google";
13
+ import { openaiCodexProvider } from "@earendil-works/pi-ai/providers/openai-codex";
14
+ import { openaiProvider } from "@earendil-works/pi-ai/providers/openai";
15
+ import { openrouterProvider } from "@earendil-works/pi-ai/providers/openrouter";
16
+ import { xaiProvider } from "@earendil-works/pi-ai/providers/xai";
17
+ import { AI_PROVIDER_IDS, type AiProviderId } from "../providers";
18
+
19
+ export type PiProviderFactory = () => Provider;
20
+
21
+ /**
22
+ * Provider-specific imports keep Pi's other catalogs out of Gloomberb builds.
23
+ * This is the complete application AI provider catalog.
24
+ */
25
+ const FACTORIES_BY_PROVIDER_ID = {
26
+ anthropic: anthropicProvider,
27
+ "openai-codex": openaiCodexProvider,
28
+ openai: openaiProvider,
29
+ google: googleProvider,
30
+ "github-copilot": githubCopilotProvider,
31
+ xai: xaiProvider,
32
+ openrouter: openrouterProvider,
33
+ } as const satisfies Readonly<Record<AiProviderId, PiProviderFactory>>;
34
+
35
+ export const GLOOMBERB_PI_PROVIDER_IDS: readonly AiProviderId[] = AI_PROVIDER_IDS;
36
+
37
+ export const GLOOMBERB_PI_PROVIDER_FACTORIES: readonly PiProviderFactory[] =
38
+ GLOOMBERB_PI_PROVIDER_IDS.map((providerId) => FACTORIES_BY_PROVIDER_ID[providerId]);
39
+
40
+ let oauthFlowsRegistered = false;
41
+
42
+ export function registerPiOAuthFlows(): void {
43
+ if (oauthFlowsRegistered) return;
44
+ // Static registration is required by `bun build --compile`; Pi's default
45
+ // variable dynamic imports cannot be discovered by the standalone bundler.
46
+ registerBunOAuthFlows();
47
+ oauthFlowsRegistered = true;
48
+ }
49
+
50
+ export interface CreateGloomberbPiModelsOptions {
51
+ credentials: CredentialStore;
52
+ modelsStore?: ModelsStore;
53
+ authContext?: AuthContext;
54
+ providerFactories?: readonly PiProviderFactory[];
55
+ }
56
+
57
+ export function createGloomberbPiModels(options: CreateGloomberbPiModelsOptions): MutableModels {
58
+ registerPiOAuthFlows();
59
+ const models = createModels({
60
+ credentials: options.credentials,
61
+ modelsStore: options.modelsStore,
62
+ authContext: options.authContext,
63
+ });
64
+ for (const createProvider of options.providerFactories ?? GLOOMBERB_PI_PROVIDER_FACTORIES) {
65
+ models.setProvider(createProvider());
66
+ }
67
+ return models;
68
+ }
@@ -0,0 +1,240 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import {
3
+ InMemoryCredentialStore,
4
+ Type,
5
+ createModels,
6
+ createProvider,
7
+ fauxAssistantMessage,
8
+ fauxProvider,
9
+ fauxToolCall,
10
+ } from "@earendil-works/pi-ai";
11
+ import {
12
+ PiAiRuntime,
13
+ PiRunCancelledError,
14
+ PiRuntimeError,
15
+ } from "./runtime";
16
+
17
+ function createFauxRuntime(options: { tokensPerSecond?: number } = {}) {
18
+ const faux = fauxProvider({
19
+ provider: "anthropic",
20
+ models: [
21
+ { id: "claude-fable-5", name: "Fable" },
22
+ { id: "claude-opus-4-8", name: "Opus" },
23
+ ],
24
+ tokensPerSecond: options.tokensPerSecond,
25
+ });
26
+ const models = createModels({ credentials: new InMemoryCredentialStore() });
27
+ models.setProvider(faux.provider);
28
+ return { faux, runtime: new PiAiRuntime({ models }) };
29
+ }
30
+
31
+ describe("PiAiRuntime", () => {
32
+ test("exposes a serializable canonical connected catalog", async () => {
33
+ const { runtime } = createFauxRuntime();
34
+ const catalog = await runtime.getCatalog();
35
+
36
+ expect(catalog.providers).toHaveLength(1);
37
+ expect(catalog.providers[0]).toMatchObject({
38
+ id: "anthropic",
39
+ label: "Claude",
40
+ connection: {
41
+ state: "connected",
42
+ type: "api_key",
43
+ origin: "external",
44
+ disconnectable: false,
45
+ },
46
+ });
47
+ expect(catalog.providers[0]).not.toHaveProperty("uiId");
48
+ expect(catalog.providers[0]?.models.map((model) => model.id)).toEqual([
49
+ "claude-fable-5",
50
+ "claude-opus-4-8",
51
+ ]);
52
+ });
53
+
54
+ test("uses the provider default for a blank model and streams cumulative text", async () => {
55
+ const { faux, runtime } = createFauxRuntime();
56
+ let requestedModel = "";
57
+ faux.setResponses([
58
+ (context, _options, _state, model) => {
59
+ requestedModel = model.id;
60
+ expect(context.messages.at(-1)).toMatchObject({ role: "user", content: "hello" });
61
+ return fauxAssistantMessage("world");
62
+ },
63
+ ]);
64
+ const chunks: string[] = [];
65
+
66
+ const result = await runtime.runText({
67
+ providerId: "anthropic",
68
+ modelId: "",
69
+ prompt: "hello",
70
+ onChunk: (chunk) => chunks.push(chunk),
71
+ }).done;
72
+
73
+ expect(requestedModel).toBe("claude-opus-4-8");
74
+ expect(result).toBe("world");
75
+ expect(chunks.at(-1)).toBe("world");
76
+ });
77
+
78
+ test("rejects legacy provider aliases at the Pi runtime boundary", async () => {
79
+ const { runtime } = createFauxRuntime();
80
+
81
+ await expect(runtime.getProviderSummary("claude"))
82
+ .rejects.toMatchObject({ code: "provider_not_found" });
83
+ });
84
+
85
+ test("rejects an injected legacy provider instead of publishing a mismatched canonical entry", async () => {
86
+ const legacy = fauxProvider({
87
+ provider: "claude",
88
+ models: [{ id: "claude-opus-4-8", name: "Opus" }],
89
+ });
90
+ const models = createModels({ credentials: new InMemoryCredentialStore() });
91
+ models.setProvider(legacy.provider);
92
+ const runtime = new PiAiRuntime({ models });
93
+
94
+ await expect(runtime.getCatalog()).rejects.toMatchObject({
95
+ code: "provider_not_found",
96
+ message: "Unsupported AI provider: claude",
97
+ });
98
+ });
99
+
100
+ test("bridges OAuth events and prompts without returning credentials", async () => {
101
+ const credentials = new InMemoryCredentialStore();
102
+ const faux = fauxProvider({ provider: "anthropic", models: [{ id: "claude-opus-4-8" }] });
103
+ const provider = createProvider({
104
+ id: "anthropic",
105
+ name: "Anthropic",
106
+ auth: {
107
+ oauth: {
108
+ name: "Claude Pro/Max",
109
+ login: async (interaction) => {
110
+ interaction.notify({ type: "auth_url", url: "https://example.test/login" });
111
+ const code = await interaction.prompt({ type: "text", message: "Paste code" });
112
+ return { type: "oauth", access: `access-${code}`, refresh: "refresh-secret", expires: Date.now() + 60_000 };
113
+ },
114
+ refresh: async (credential) => credential,
115
+ toAuth: async (credential) => ({ apiKey: credential.access }),
116
+ },
117
+ },
118
+ models: faux.models,
119
+ api: { stream: faux.provider.stream, streamSimple: faux.provider.streamSimple },
120
+ });
121
+ const models = createModels({ credentials });
122
+ models.setProvider(provider);
123
+ const runtime = new PiAiRuntime({ models });
124
+ const events: unknown[] = [];
125
+ const prompts: unknown[] = [];
126
+
127
+ const connected = await runtime.login(
128
+ { providerId: "anthropic", type: "oauth" },
129
+ {
130
+ notify: (event) => events.push(event),
131
+ prompt: async (prompt) => {
132
+ prompts.push(prompt);
133
+ return "login-code";
134
+ },
135
+ },
136
+ );
137
+
138
+ expect(events).toEqual([{ type: "auth_url", url: "https://example.test/login" }]);
139
+ expect(prompts).toEqual([{ type: "text", message: "Paste code" }]);
140
+ expect(connected.connection).toEqual({
141
+ state: "connected",
142
+ type: "oauth",
143
+ source: "OAuth",
144
+ origin: "stored",
145
+ disconnectable: true,
146
+ });
147
+ expect(JSON.stringify(connected)).not.toContain("login-code");
148
+ expect((await runtime.logout("anthropic")).connection).toEqual({ state: "not_connected" });
149
+ });
150
+
151
+ test("does not report an expired OAuth credential as connected when refresh fails", async () => {
152
+ const credentials = new InMemoryCredentialStore();
153
+ await credentials.modify("anthropic", async () => ({
154
+ type: "oauth",
155
+ access: "expired-access",
156
+ refresh: "expired-refresh",
157
+ expires: Date.now() - 1,
158
+ }));
159
+ const faux = fauxProvider({ provider: "anthropic", models: [{ id: "claude-opus-4-8" }] });
160
+ const provider = createProvider({
161
+ id: "anthropic",
162
+ name: "Anthropic",
163
+ auth: {
164
+ oauth: {
165
+ name: "Claude Pro/Max",
166
+ login: async () => { throw new Error("unused"); },
167
+ refresh: async () => { throw new Error("refresh rejected"); },
168
+ toAuth: async (credential) => ({ apiKey: credential.access }),
169
+ },
170
+ },
171
+ models: faux.models,
172
+ api: { stream: faux.provider.stream, streamSimple: faux.provider.streamSimple },
173
+ });
174
+ const models = createModels({ credentials });
175
+ models.setProvider(provider);
176
+ const runtime = new PiAiRuntime({ models });
177
+
178
+ expect((await runtime.getProviderSummary("anthropic")).connection).toMatchObject({
179
+ state: "error",
180
+ message: expect.stringContaining("OAuth refresh failed"),
181
+ });
182
+ await expect(runtime.resolveModel({ providerId: "anthropic" }))
183
+ .rejects.toMatchObject({ code: "provider_not_configured" });
184
+ });
185
+
186
+ test("normalizes provider failures and cancellation", async () => {
187
+ const failed = createFauxRuntime();
188
+ failed.faux.setResponses([
189
+ fauxAssistantMessage([], {
190
+ stopReason: "error",
191
+ errorMessage: "Authorization: Bearer super-secret-token",
192
+ }),
193
+ ]);
194
+ const failure = failed.runtime.runText({ providerId: "anthropic", prompt: "fail" }).done;
195
+ await expect(failure).rejects.toBeInstanceOf(PiRuntimeError);
196
+ await expect(failure).rejects.not.toThrow("super-secret-token");
197
+
198
+ const slow = createFauxRuntime({ tokensPerSecond: 0.01 });
199
+ slow.faux.setResponses([fauxAssistantMessage("This response should be cancelled.")]);
200
+ const run = slow.runtime.runText({ providerId: "anthropic", prompt: "cancel" });
201
+ run.cancel();
202
+ await expect(run.done).rejects.toBeInstanceOf(PiRunCancelledError);
203
+ });
204
+
205
+ test("runs typed tools through pi-agent-core and returns the final agent response", async () => {
206
+ const { faux, runtime } = createFauxRuntime();
207
+ faux.setResponses([
208
+ fauxAssistantMessage(fauxToolCall("remote_control", { request: "schema" }), { stopReason: "toolUse" }),
209
+ fauxAssistantMessage("Remote schema loaded."),
210
+ ]);
211
+ const toolCalls: unknown[] = [];
212
+ const chunks: string[] = [];
213
+
214
+ const run = runtime.runAgent({
215
+ providerId: "anthropic",
216
+ prompt: "Inspect the app",
217
+ tools: [{
218
+ name: "remote_control",
219
+ label: "Gloomberb remote control",
220
+ description: "Send a request to the running Gloomberb app.",
221
+ parameters: Type.Object({ request: Type.String() }),
222
+ execute: async (_toolCallId, params) => {
223
+ const request = (params as { request: string }).request;
224
+ toolCalls.push({ request });
225
+ return {
226
+ content: [{ type: "text", text: '{"actions":[]}' }],
227
+ details: { request },
228
+ };
229
+ },
230
+ }],
231
+ onChunk: (chunk) => chunks.push(chunk),
232
+ });
233
+
234
+ const result = await run.done;
235
+ expect(toolCalls).toEqual([{ request: "schema" }]);
236
+ expect(result.text).toBe("Remote schema loaded.");
237
+ expect(chunks.at(-1)).toBe("Remote schema loaded.");
238
+ expect(result.messages.some((message) => message.role === "toolResult")).toBe(true);
239
+ });
240
+ });