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
@@ -1,4 +1,14 @@
1
- import type { AiProvider } from "./providers";
1
+ import {
2
+ aiProviderFromRuntime,
3
+ isAiProviderId,
4
+ migrateLegacyAiProviderId,
5
+ setDetectedProviders,
6
+ type AiProvider,
7
+ type AiProviderId,
8
+ type AiProviderStatus,
9
+ } from "./providers";
10
+ import type { AiAgentHistoryMessage } from "./agent-history";
11
+ import { withDeadline } from "../../../utils/async-deadline";
2
12
 
3
13
  export class AiRunCancelledError extends Error {
4
14
  constructor() {
@@ -12,108 +22,301 @@ export interface AiRunController {
12
22
  cancel: () => void;
13
23
  }
14
24
 
25
+ export interface AiConversationMessage {
26
+ role: "user" | "assistant";
27
+ content: string;
28
+ }
29
+
30
+ export type AiRunOutputMode = "plain" | "structured" | "screener";
31
+ export type AiRuntimeConnectionState = "connected" | "not_connected" | "error";
32
+ export type AiRuntimeAuthType = "oauth" | "api_key";
33
+ export type AiCredentialOrigin = "stored" | "external";
34
+
35
+ export type AiAuthProgressEvent =
36
+ | {
37
+ type: "info";
38
+ message: string;
39
+ links?: readonly { url: string; label?: string }[];
40
+ }
41
+ | {
42
+ type: "auth_url";
43
+ url: string;
44
+ instructions?: string;
45
+ }
46
+ | {
47
+ type: "device_code";
48
+ userCode: string;
49
+ verificationUri: string;
50
+ intervalSeconds?: number;
51
+ expiresInSeconds?: number;
52
+ }
53
+ | {
54
+ type: "progress";
55
+ message: string;
56
+ };
57
+
58
+ export interface AiRuntimeModel {
59
+ id: string;
60
+ providerId: AiProviderId;
61
+ label: string;
62
+ available: boolean;
63
+ }
64
+
65
+ export interface AiRuntimeAuthMethod {
66
+ type: AiRuntimeAuthType;
67
+ label: string;
68
+ /**
69
+ * True only when the current renderer-neutral host can complete this method
70
+ * without exposing a credential in an unmasked field.
71
+ */
72
+ canLogin: boolean;
73
+ }
74
+
75
+ export interface AiRuntimeAccount {
76
+ providerId: AiProviderId;
77
+ providerLabel: string;
78
+ connectionState: AiRuntimeConnectionState;
79
+ connectionLabel: string;
80
+ credentialSource?: string;
81
+ credentialOrigin?: AiCredentialOrigin;
82
+ authMethods: AiRuntimeAuthMethod[];
83
+ canLogin: boolean;
84
+ canDisconnect: boolean;
85
+ loginType?: AiRuntimeAuthType;
86
+ }
87
+
88
+ export interface AiRuntimeProvider {
89
+ providerId: AiProviderId;
90
+ label: string;
91
+ status: AiProviderStatus;
92
+ unavailableReason?: string;
93
+ outputModes: AiRunOutputMode[];
94
+ defaultModelId?: string;
95
+ }
96
+
97
+ export interface AiRuntimeCatalog {
98
+ providers: AiRuntimeProvider[];
99
+ accounts: AiRuntimeAccount[];
100
+ models: AiRuntimeModel[];
101
+ }
102
+
15
103
  export interface AiRunHost {
16
104
  run(options: {
17
- provider: AiProvider;
105
+ providerId: AiProviderId;
18
106
  prompt: string;
19
- cwd?: string;
107
+ messages?: AiConversationMessage[];
108
+ agentMessages?: AiAgentHistoryMessage[];
109
+ modelId?: string;
20
110
  onChunk?: (output: string) => void;
111
+ onAgentMessages?: (messages: AiAgentHistoryMessage[]) => void;
112
+ outputMode?: AiRunOutputMode;
21
113
  }): AiRunController;
114
+ checkStatus?(providerId: AiProviderId): Promise<AiProviderStatusResult>;
115
+ getCatalog?(): Promise<AiRuntimeCatalog>;
116
+ connect?(
117
+ providerId: AiProviderId,
118
+ authType?: AiRuntimeAuthType,
119
+ onAuthEvent?: (event: AiAuthProgressEvent) => void,
120
+ ): Promise<AiRuntimeCatalog>;
121
+ disconnect?(providerId: AiProviderId): Promise<AiRuntimeCatalog>;
122
+ }
123
+
124
+ export interface AiProviderStatusResult {
125
+ available: boolean;
126
+ authenticated: boolean;
127
+ /** True when the provider exists but credential validation failed unexpectedly. */
128
+ inconclusive?: boolean;
129
+ message: string | null;
22
130
  }
23
131
 
132
+ type CatalogListener = () => void;
133
+
24
134
  let configuredHost: AiRunHost | null = null;
135
+ let runtimeCatalog: AiRuntimeCatalog = { providers: [], accounts: [], models: [] };
136
+ const catalogListeners = new Set<CatalogListener>();
137
+
138
+ function canonicalProviderId(providerId: string): AiProviderId {
139
+ const canonicalId = migrateLegacyAiProviderId(providerId);
140
+ if (!isAiProviderId(canonicalId)) {
141
+ throw new Error(`Unknown AI provider: ${providerId}`);
142
+ }
143
+ return canonicalId;
144
+ }
145
+
146
+ function cloneCatalog(catalog: AiRuntimeCatalog): AiRuntimeCatalog {
147
+ return {
148
+ providers: catalog.providers.map((provider) => ({
149
+ ...provider,
150
+ outputModes: [...provider.outputModes],
151
+ })),
152
+ accounts: catalog.accounts.map((account) => ({
153
+ ...account,
154
+ authMethods: account.authMethods.map((method) => ({ ...method })),
155
+ })),
156
+ models: catalog.models.map((model) => ({ ...model })),
157
+ };
158
+ }
159
+
160
+ function publishCatalog(catalog: AiRuntimeCatalog): void {
161
+ runtimeCatalog = cloneCatalog(catalog);
162
+ setDetectedProviders(runtimeCatalog.providers.map(aiProviderFromRuntime));
163
+ for (const listener of [...catalogListeners]) listener();
164
+ }
25
165
 
26
166
  export function setAiRunHost(host: AiRunHost | null): void {
27
167
  configuredHost = host;
28
168
  }
29
169
 
30
- export function isAiRunCancelled(error: unknown): boolean {
31
- return error instanceof AiRunCancelledError;
170
+ export async function installAiRunHost(
171
+ host: AiRunHost,
172
+ options: {
173
+ catalogTimeoutMs: number;
174
+ timeoutMessage: string;
175
+ onCatalogError?: (error: unknown) => void;
176
+ },
177
+ ): Promise<AiRuntimeCatalog> {
178
+ setAiRunHost(host);
179
+ const emptyCatalog: AiRuntimeCatalog = { providers: [], accounts: [], models: [] };
180
+ const catalog = await withDeadline(
181
+ Promise.resolve().then(() => host.getCatalog?.() ?? emptyCatalog),
182
+ options.catalogTimeoutMs,
183
+ options.timeoutMessage,
184
+ ).catch((error) => {
185
+ try {
186
+ options.onCatalogError?.(error);
187
+ } catch {
188
+ // Logging must not turn optional provider discovery into a startup failure.
189
+ }
190
+ return emptyCatalog;
191
+ });
192
+ setAiRuntimeCatalog(catalog);
193
+ return getAiRuntimeCatalog();
32
194
  }
33
195
 
34
- function runWithBun({
35
- provider,
36
- prompt,
37
- cwd = typeof process !== "undefined" ? process.cwd() : ".",
38
- onChunk,
39
- }: {
40
- provider: AiProvider;
41
- prompt: string;
42
- cwd?: string;
43
- onChunk?: (output: string) => void;
44
- }): AiRunController {
45
- type BunSubprocess = ReturnType<typeof Bun.spawn>;
46
- if (typeof Bun === "undefined" || typeof Bun.spawn !== "function") {
47
- return {
48
- done: Promise.reject(new Error("AI execution requires a native Bun host.")),
49
- cancel: () => {},
50
- };
51
- }
196
+ export function setAiRuntimeCatalog(catalog: AiRuntimeCatalog): void {
197
+ publishCatalog(catalog);
198
+ }
52
199
 
53
- let cancelled = false;
54
- let processRef: BunSubprocess | null = null;
55
-
56
- const done = (async () => {
57
- const proc = Bun.spawn([provider.command, ...provider.buildArgs(prompt)], {
58
- cwd,
59
- stdin: "ignore",
60
- stdout: "pipe",
61
- stderr: "pipe",
62
- });
63
- processRef = proc;
64
-
65
- const stderrPromise = new Response(proc.stderr).text().catch(() => "");
66
- const stdoutReader = proc.stdout.getReader();
67
- const decoder = new TextDecoder();
68
- let fullOutput = "";
69
-
70
- while (true) {
71
- const { done: streamDone, value } = await stdoutReader.read();
72
- if (streamDone) break;
73
- if (cancelled) throw new AiRunCancelledError();
74
- fullOutput += decoder.decode(value, { stream: true });
75
- onChunk?.(fullOutput);
76
- }
200
+ /**
201
+ * Stable snapshot for useSyncExternalStore. A new reference is published only
202
+ * when set/connect/disconnect replaces the catalog.
203
+ */
204
+ export function getAiRuntimeCatalogSnapshot(): AiRuntimeCatalog {
205
+ return runtimeCatalog;
206
+ }
77
207
 
78
- const exitCode = await proc.exited;
79
- const stderr = (await stderrPromise).trim();
208
+ export function subscribeAiRuntimeCatalog(listener: CatalogListener): () => void {
209
+ catalogListeners.add(listener);
210
+ return () => {
211
+ catalogListeners.delete(listener);
212
+ };
213
+ }
80
214
 
81
- if (cancelled) throw new AiRunCancelledError();
82
- if (exitCode !== 0) {
83
- throw new Error(stderr || fullOutput.trim() || `${provider.name} exited with status ${exitCode}.`);
84
- }
215
+ /** Defensive copy for imperative consumers. */
216
+ export function getAiRuntimeCatalog(): AiRuntimeCatalog {
217
+ return cloneCatalog(runtimeCatalog);
218
+ }
85
219
 
86
- const finalOutput = fullOutput.trim();
87
- if (!finalOutput) {
88
- throw new Error(stderr || `${provider.name} returned an empty response.`);
89
- }
220
+ export async function refreshAiRuntimeCatalog(): Promise<AiRuntimeCatalog> {
221
+ if (!configuredHost?.getCatalog) return getAiRuntimeCatalog();
222
+ publishCatalog(await configuredHost.getCatalog());
223
+ return getAiRuntimeCatalog();
224
+ }
225
+
226
+ export async function connectAiRuntimeProvider(
227
+ providerId: string,
228
+ authType?: AiRuntimeAuthType,
229
+ onAuthEvent?: (event: AiAuthProgressEvent) => void,
230
+ ): Promise<AiRuntimeCatalog> {
231
+ if (!configuredHost?.connect) {
232
+ throw new Error("In-app AI account connections are unavailable in this renderer.");
233
+ }
234
+ const canonicalId = canonicalProviderId(providerId);
235
+ publishCatalog(await configuredHost.connect(canonicalId, authType, onAuthEvent));
236
+ return getAiRuntimeCatalog();
237
+ }
238
+
239
+ export async function disconnectAiRuntimeProvider(providerId: string): Promise<AiRuntimeCatalog> {
240
+ if (!configuredHost?.disconnect) {
241
+ throw new Error("In-app AI account disconnection is unavailable in this renderer.");
242
+ }
243
+ publishCatalog(await configuredHost.disconnect(canonicalProviderId(providerId)));
244
+ return getAiRuntimeCatalog();
245
+ }
246
+
247
+ export function isAiRunCancelled(error: unknown): boolean {
248
+ return error instanceof AiRunCancelledError;
249
+ }
90
250
 
91
- return finalOutput;
92
- })();
251
+ export async function checkAiProviderStatus(
252
+ provider: AiProvider | AiProviderId,
253
+ ): Promise<AiProviderStatusResult> {
254
+ const providerId = canonicalProviderId(typeof provider === "string" ? provider : provider.id);
255
+ if (configuredHost?.checkStatus) return configuredHost.checkStatus(providerId);
93
256
 
257
+ const account = runtimeCatalog.accounts.find((candidate) => candidate.providerId === providerId);
258
+ if (account?.connectionState === "connected") {
259
+ return { available: true, authenticated: true, message: null };
260
+ }
261
+ if (account?.connectionState === "error") {
262
+ return {
263
+ available: false,
264
+ authenticated: false,
265
+ inconclusive: true,
266
+ message: account.connectionLabel,
267
+ };
268
+ }
94
269
  return {
95
- done,
96
- cancel: () => {
97
- cancelled = true;
98
- try {
99
- processRef?.kill();
100
- } catch {
101
- // ignore cleanup failures
102
- }
103
- },
270
+ available: false,
271
+ authenticated: false,
272
+ message: account?.connectionLabel ?? "AI provider is not connected.",
104
273
  };
105
274
  }
106
275
 
107
276
  export function runAiPrompt({
108
- provider,
277
+ providerId,
109
278
  prompt,
110
- cwd,
279
+ messages,
280
+ agentMessages,
281
+ modelId,
111
282
  onChunk,
283
+ onAgentMessages,
284
+ outputMode,
112
285
  }: {
113
- provider: AiProvider;
286
+ providerId: string;
114
287
  prompt: string;
115
- cwd?: string;
288
+ messages?: AiConversationMessage[];
289
+ agentMessages?: AiAgentHistoryMessage[];
290
+ modelId?: string;
116
291
  onChunk?: (output: string) => void;
292
+ onAgentMessages?: (messages: AiAgentHistoryMessage[]) => void;
293
+ outputMode?: AiRunOutputMode;
117
294
  }): AiRunController {
118
- return (configuredHost ?? { run: runWithBun }).run({ provider, prompt, cwd, onChunk });
295
+ if (!configuredHost) {
296
+ return {
297
+ done: Promise.reject(new Error("The native AI runtime is unavailable in this renderer.")),
298
+ cancel: () => {},
299
+ };
300
+ }
301
+
302
+ let canonicalId: AiProviderId;
303
+ try {
304
+ canonicalId = canonicalProviderId(providerId);
305
+ } catch (error) {
306
+ return {
307
+ done: Promise.reject(error),
308
+ cancel: () => {},
309
+ };
310
+ }
311
+
312
+ return configuredHost.run({
313
+ providerId: canonicalId,
314
+ prompt,
315
+ messages,
316
+ agentMessages,
317
+ modelId,
318
+ onChunk,
319
+ onAgentMessages,
320
+ outputMode,
321
+ });
119
322
  }
@@ -1,75 +1,44 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { buildGloomberbCliInstructions, resolveGloomberbCliCommand } from "../gloomberb-cli";
3
2
  import {
4
3
  buildScreenerPrompt,
5
- mergeScreenerResults,
4
+ getScreenerPromptSignature,
5
+ matchesScreenerPromptSignature,
6
6
  parseScreenerResponse,
7
7
  } from "./contract";
8
- import type { AiProvider } from "../providers";
9
-
10
- const TEST_PROVIDER: AiProvider = {
11
- id: "claude",
12
- name: "Claude",
13
- command: "claude",
14
- available: true,
15
- buildArgs: () => [],
16
- };
17
8
 
18
9
  describe("AI screener helpers", () => {
19
- test("prefers the installed gloomberb command when it exists", () => {
20
- const command = resolveGloomberbCliCommand({
21
- cwd: "/tmp/project",
22
- hasCommand: (value) => value === "gloomberb",
23
- fileExists: () => true,
24
- });
25
-
26
- expect(command).toEqual({
27
- argv: ["gloomberb"],
28
- display: "gloomberb",
29
- mode: "installed",
30
- });
31
- });
32
-
33
- test("falls back to bun source mode while developing locally", () => {
34
- const command = resolveGloomberbCliCommand({
35
- cwd: "/tmp/project",
36
- hasCommand: () => false,
37
- fileExists: (path) => path === "/tmp/project/src/index.tsx",
38
- });
39
-
40
- expect(command).toEqual({
41
- argv: ["bun", "/tmp/project/src/index.tsx"],
42
- display: "bun /tmp/project/src/index.tsx",
43
- mode: "bun-source",
44
- });
45
- expect(buildGloomberbCliInstructions(command)).toContain("Inspect a portfolio or watchlist with: bun /tmp/project/src/index.tsx portfolio [name]");
10
+ test("includes the model override in run identity", () => {
11
+ expect(getScreenerPromptSignature("quality", "codex", "gpt-a"))
12
+ .not.toBe(getScreenerPromptSignature("quality", "codex", "gpt-b"));
13
+ expect(getScreenerPromptSignature("quality", "codex", ""))
14
+ .toBe(getScreenerPromptSignature("quality", "codex", null));
15
+ expect(matchesScreenerPromptSignature(
16
+ JSON.stringify(["codex", "quality"]),
17
+ "quality",
18
+ "codex",
19
+ null,
20
+ )).toBe(true);
21
+ expect(matchesScreenerPromptSignature(
22
+ JSON.stringify(["codex", "quality"]),
23
+ "quality",
24
+ "codex",
25
+ "gpt-custom",
26
+ )).toBe(false);
46
27
  });
47
28
 
48
- test("builds a screener prompt with the current date and prior results", () => {
29
+ test("builds a fresh screener prompt with the current date", () => {
49
30
  const prompt = buildScreenerPrompt({
50
31
  currentDate: "2026-04-01",
51
32
  prompt: "Find profitable serial acquirers.",
52
- provider: TEST_PROVIDER,
53
- cliInstructions: [
54
- "Useful commands: gloomberb help",
55
- "Inspect a ticker with: gloomberb ticker <symbol>",
56
- ],
57
- previousResults: [
58
- {
59
- symbol: "CSU",
60
- exchange: "TSX",
61
- reason: "Recurring software roll-up.",
62
- resolvedName: "Constellation Software",
63
- },
64
- ],
65
- includePreviousResults: true,
66
33
  });
67
34
 
68
35
  expect(prompt).toContain("Today is 2026-04-01.");
69
- expect(prompt).toContain("You are running in the Claude CLI.");
70
- expect(prompt).toContain("Useful commands: gloomberb help");
71
- expect(prompt).toContain("CSU (TSX): Recurring software roll-up.");
72
- expect(prompt).toContain("Return raw JSON only.");
36
+ expect(prompt).toContain("Use the available Gloomberb data tools");
37
+ expect(prompt).toContain("Submit the final structured screener result");
38
+ expect(prompt).not.toContain("already found");
39
+ expect(prompt).not.toContain("Prefer new names");
40
+ expect(prompt).not.toContain("CLI");
41
+ expect(prompt).not.toContain("raw JSON");
73
42
  });
74
43
 
75
44
  test("parses fenced JSON responses and normalizes ticker fields", () => {
@@ -98,20 +67,4 @@ describe("AI screener helpers", () => {
98
67
  });
99
68
  });
100
69
 
101
- test("merges reruns by updating duplicates and appending new symbols", () => {
102
- const merged = mergeScreenerResults(
103
- [
104
- { symbol: "AAPL", exchange: "NASDAQ", reason: "Old reason", resolvedName: "Apple Inc." },
105
- ],
106
- [
107
- { symbol: "AAPL", exchange: "NASDAQ", reason: "Updated reason", resolvedName: "Apple Inc." },
108
- { symbol: "MSFT", exchange: "NASDAQ", reason: "New result", resolvedName: "Microsoft Corp." },
109
- ],
110
- );
111
-
112
- expect(merged).toEqual([
113
- { symbol: "AAPL", exchange: "NASDAQ", reason: "Updated reason", resolvedName: "Apple Inc." },
114
- { symbol: "MSFT", exchange: "NASDAQ", reason: "New result", resolvedName: "Microsoft Corp." },
115
- ]);
116
- });
117
70
  });
@@ -1,4 +1,3 @@
1
- import type { AiProvider } from "../providers";
2
1
  import { truncateWithEllipsis } from "../../../../utils/text-wrap";
3
2
 
4
3
  interface ScreenerCandidate {
@@ -57,49 +56,39 @@ export function deriveScreenerTitle(prompt: string): string {
57
56
  return truncateWithEllipsis(normalized || "New Screener", 22);
58
57
  }
59
58
 
60
- export function getScreenerPromptSignature(prompt: string, providerId: string): string {
61
- return JSON.stringify([providerId.trim(), prompt.trim()]);
59
+ export function getScreenerPromptSignature(prompt: string, providerId: string, modelId?: string | null): string {
60
+ return JSON.stringify([providerId.trim(), modelId?.trim() || null, prompt.trim()]);
61
+ }
62
+
63
+ export function matchesScreenerPromptSignature(
64
+ signature: string | null,
65
+ prompt: string,
66
+ providerId: string,
67
+ modelId?: string | null,
68
+ ): boolean {
69
+ if (signature === getScreenerPromptSignature(prompt, providerId, modelId)) return true;
70
+ return !modelId?.trim() && signature === JSON.stringify([providerId.trim(), prompt.trim()]);
62
71
  }
63
72
 
64
73
  export function buildScreenerPrompt({
65
74
  currentDate,
66
75
  prompt,
67
- provider,
68
- cliInstructions,
69
- previousResults,
70
- includePreviousResults,
71
76
  }: {
72
77
  currentDate: string;
73
78
  prompt: string;
74
- provider: AiProvider;
75
- cliInstructions: string[];
76
- previousResults: ValidatedScreenerResult[];
77
- includePreviousResults: boolean;
78
79
  }): string {
79
80
  const lines = [
80
81
  `Today is ${currentDate}.`,
81
- `You are running in the ${provider.name} CLI.`,
82
82
  "",
83
83
  "Find public-market tickers that match this screening prompt:",
84
84
  prompt.trim(),
85
85
  "",
86
- "You may use the local Gloomberb CLI to validate companies before returning them:",
87
- ...cliInstructions.map((instruction) => `- ${instruction}`),
86
+ "Use the available Gloomberb data tools to validate every company before submitting it.",
88
87
  "",
89
88
  ];
90
89
 
91
- if (includePreviousResults && previousResults.length > 0) {
92
- lines.push("These tickers were already found for this exact screener. Prefer new names if you can validate them:");
93
- for (const result of previousResults) {
94
- lines.push(`- ${result.symbol} (${result.exchange}): ${result.reason}`);
95
- }
96
- lines.push("");
97
- }
98
-
99
90
  lines.push(
100
- "Return raw JSON only. Do not wrap it in markdown or add commentary.",
101
- "Schema:",
102
- '{ "title": "optional short title", "summary": "optional one-line summary", "tickers": [{ "symbol": "AAPL", "exchange": "NASDAQ", "reason": "concise reason" }] }',
91
+ "Submit the final structured screener result with an optional short title, an optional one-line summary, and the validated tickers.",
103
92
  "Rules:",
104
93
  "- Return at most 25 unique ticker candidates.",
105
94
  "- Use uppercase symbols.",
@@ -145,30 +134,3 @@ export function parseScreenerResponse(raw: string): ParsedScreenerResponse {
145
134
  tickers,
146
135
  };
147
136
  }
148
-
149
- export function mergeScreenerResults(
150
- previous: ValidatedScreenerResult[],
151
- next: ValidatedScreenerResult[],
152
- ): ValidatedScreenerResult[] {
153
- const nextBySymbol = new Map(next.map((result) => [result.symbol, result] as const));
154
- const merged: ValidatedScreenerResult[] = [];
155
-
156
- for (const result of previous) {
157
- const replacement = nextBySymbol.get(result.symbol);
158
- if (replacement) {
159
- merged.push(replacement);
160
- nextBySymbol.delete(result.symbol);
161
- continue;
162
- }
163
- merged.push(result);
164
- }
165
-
166
- for (const result of next) {
167
- if (nextBySymbol.has(result.symbol)) {
168
- merged.push(result);
169
- nextBySymbol.delete(result.symbol);
170
- }
171
- }
172
-
173
- return merged;
174
- }