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
@@ -17,13 +17,37 @@ function createRegistryHarness() {
17
17
  actions.push(action);
18
18
  state = appReducer(state, action);
19
19
  };
20
+ const invokedCapabilities: Array<{ capabilityId: string; operationId: string; payload: unknown }> = [];
21
+ const marketDataQueries: Array<{ operation: string; input: unknown }> = [];
20
22
  const registry = {
21
23
  panes: new Map(),
22
24
  paneTemplates: new Map(),
23
25
  commands: new Map(),
24
26
  capabilities: {
25
27
  manifests: () => [],
26
- invoke: async () => ({ invoked: true }),
28
+ invoke: async (capabilityId: string, operationId: string, payload: unknown) => {
29
+ invokedCapabilities.push({ capabilityId, operationId, payload });
30
+ return { invoked: true, capabilityId, operationId, payload };
31
+ },
32
+ },
33
+ marketData: {
34
+ search: async (query: string) => {
35
+ marketDataQueries.push({ operation: "search", input: { query } });
36
+ return [{
37
+ symbol: "NVDA",
38
+ name: "NVIDIA Corporation",
39
+ exchange: "NASDAQ",
40
+ type: "Equity",
41
+ }];
42
+ },
43
+ getQuote: async (symbol: string, exchange?: string) => {
44
+ marketDataQueries.push({ operation: "quote", input: { symbol, exchange } });
45
+ return { symbol, exchange, price: 180 };
46
+ },
47
+ getTickerFinancials: async (symbol: string, exchange?: string) => {
48
+ marketDataQueries.push({ operation: "financials", input: { symbol, exchange } });
49
+ return { quote: { symbol, exchange, price: 180 } };
50
+ },
27
51
  },
28
52
  resolvePaneSettings: () => null,
29
53
  showPane: () => {},
@@ -62,7 +86,9 @@ function createRegistryHarness() {
62
86
  actions,
63
87
  controller,
64
88
  getState: () => state,
89
+ invokedCapabilities,
65
90
  invokedUiActions,
91
+ marketDataQueries,
66
92
  setUiNodes: (nodes: RemoteUiNodeSnapshot[]) => {
67
93
  uiNodes = nodes;
68
94
  },
@@ -101,6 +127,47 @@ describe("createAppRemoteController", () => {
101
127
  }
102
128
  });
103
129
 
130
+ test("queries configured market data without dispatching app-control actions", async () => {
131
+ const { actions, controller, marketDataQueries } = createRegistryHarness();
132
+
133
+ const search = await controller.handle({
134
+ type: "data",
135
+ operation: "search",
136
+ query: "NVIDIA",
137
+ });
138
+ const quote = await controller.handle({
139
+ type: "data",
140
+ operation: "quote",
141
+ symbol: "nvda",
142
+ exchange: "nasdaq",
143
+ });
144
+
145
+ expect(search).toMatchObject({
146
+ ok: true,
147
+ data: [{ symbol: "NVDA", exchange: "NASDAQ" }],
148
+ });
149
+ expect(quote).toMatchObject({
150
+ ok: true,
151
+ data: { symbol: "NVDA", exchange: "NASDAQ", price: 180 },
152
+ });
153
+ expect(marketDataQueries).toEqual([
154
+ { operation: "search", input: { query: "NVIDIA" } },
155
+ { operation: "quote", input: { symbol: "NVDA", exchange: "NASDAQ" } },
156
+ ]);
157
+ expect(actions).toEqual([]);
158
+
159
+ const appControlAttempt = await controller.handle({
160
+ type: "data",
161
+ operation: "app.openCommandBar",
162
+ query: "NVDA",
163
+ } as never);
164
+ expect(appControlAttempt).toMatchObject({
165
+ ok: false,
166
+ error: { message: expect.stringContaining("Unknown market data operation") },
167
+ });
168
+ expect(actions).toEqual([]);
169
+ });
170
+
104
171
  test("dispatches semantic app operations", async () => {
105
172
  const { actions, controller, getState } = createRegistryHarness();
106
173
 
@@ -292,6 +359,40 @@ describe("createAppRemoteController", () => {
292
359
  }
293
360
  });
294
361
 
362
+ test("forwards plugin capability operations without narrowing their payload", async () => {
363
+ const { controller, invokedCapabilities } = createRegistryHarness();
364
+ const payload = {
365
+ instanceId: "broker-main",
366
+ operation: "placeOrder",
367
+ args: [{ symbol: "NVDA", side: "BUY", quantity: 5 }],
368
+ };
369
+
370
+ const response = await controller.handle({
371
+ type: "call",
372
+ operation: "capability.invoke",
373
+ input: {
374
+ capabilityId: "desktop.broker",
375
+ operationId: "invoke",
376
+ payload,
377
+ },
378
+ });
379
+
380
+ expect(response.ok).toBe(true);
381
+ expect(invokedCapabilities).toEqual([{
382
+ capabilityId: "desktop.broker",
383
+ operationId: "invoke",
384
+ payload,
385
+ }]);
386
+ if (response.ok) {
387
+ expect(response.data).toEqual({
388
+ invoked: true,
389
+ capabilityId: "desktop.broker",
390
+ operationId: "invoke",
391
+ payload,
392
+ });
393
+ }
394
+ });
395
+
295
396
  test("runs sequential batches with halt-on-error and final state", async () => {
296
397
  const { controller } = createRegistryHarness();
297
398
 
@@ -17,6 +17,7 @@ import type {
17
17
  RemoteControlRequest,
18
18
  RemoteControlResponse,
19
19
  RemoteJsonPatchOperation,
20
+ RemoteMarketDataRequest,
20
21
  RemoteStateInclude,
21
22
  } from "./types";
22
23
  import type { RemoteUiRegistry } from "./semantic-tree";
@@ -51,6 +52,26 @@ interface AppRemoteControllerOptions {
51
52
  }
52
53
 
53
54
  const DEFAULT_MUTATION_INCLUDE: RemoteStateInclude[] = ["app", "layout", "panes", "commandBar"];
55
+ const MAX_MARKET_DATA_SEARCH_RESULTS = 20;
56
+ const MAX_MARKET_DATA_FILINGS = 20;
57
+ const MAX_MARKET_DATA_EARNINGS_SYMBOLS = 25;
58
+ const MAX_MARKET_DATA_HOLDERS = 25;
59
+ const MAX_MARKET_DATA_EVENT_ROWS = 20;
60
+
61
+ function requiredMarketDataText(value: string, field: string): string {
62
+ const normalized = value.trim();
63
+ if (!normalized) throw new Error(`Market data ${field} is required.`);
64
+ return normalized;
65
+ }
66
+
67
+ function normalizedMarketDataSymbol(symbol: string): string {
68
+ return requiredMarketDataText(symbol, "symbol").toUpperCase();
69
+ }
70
+
71
+ function normalizedOptionalExchange(exchange: string | undefined): string | undefined {
72
+ const normalized = exchange?.trim().toUpperCase();
73
+ return normalized || undefined;
74
+ }
54
75
 
55
76
  export function createAppRemoteController({
56
77
  dispatch,
@@ -72,6 +93,105 @@ export function createAppRemoteController({
72
93
  return mutationSummary(getState(), extra);
73
94
  };
74
95
 
96
+ const queryMarketData = async (request: RemoteMarketDataRequest): Promise<unknown> => {
97
+ const marketData = pluginRegistry.marketData;
98
+ switch (request.operation) {
99
+ case "search":
100
+ return (await marketData.search(requiredMarketDataText(request.query, "query")))
101
+ .slice(0, MAX_MARKET_DATA_SEARCH_RESULTS)
102
+ .map((result) => ({
103
+ providerId: result.providerId,
104
+ symbol: result.symbol,
105
+ name: result.name,
106
+ exchange: result.exchange,
107
+ primaryExchange: result.primaryExchange,
108
+ type: result.type,
109
+ currency: result.currency,
110
+ }));
111
+ case "quote":
112
+ return marketData.getQuote(
113
+ normalizedMarketDataSymbol(request.symbol),
114
+ normalizedOptionalExchange(request.exchange),
115
+ );
116
+ case "financials": {
117
+ const financials = await marketData.getTickerFinancials(
118
+ normalizedMarketDataSymbol(request.symbol),
119
+ normalizedOptionalExchange(request.exchange),
120
+ );
121
+ return {
122
+ quote: financials.quote,
123
+ fundamentals: financials.fundamentals,
124
+ profile: financials.profile,
125
+ annualStatements: financials.annualStatements.slice(0, 5),
126
+ quarterlyStatements: financials.quarterlyStatements.slice(0, 8),
127
+ };
128
+ }
129
+ case "secFilings": {
130
+ if (!marketData.getSecFilings) throw new Error("The configured market data sources do not provide SEC filings.");
131
+ const count = Math.max(1, Math.min(
132
+ MAX_MARKET_DATA_FILINGS,
133
+ Number.isFinite(request.count) ? Math.trunc(request.count!) : 10,
134
+ ));
135
+ return (await marketData.getSecFilings(
136
+ normalizedMarketDataSymbol(request.symbol),
137
+ count,
138
+ normalizedOptionalExchange(request.exchange),
139
+ )).slice(0, count);
140
+ }
141
+ case "holders": {
142
+ if (!marketData.getHolders) throw new Error("The configured market data sources do not provide holder data.");
143
+ const holders = await marketData.getHolders(
144
+ normalizedMarketDataSymbol(request.symbol),
145
+ normalizedOptionalExchange(request.exchange),
146
+ );
147
+ return {
148
+ ...holders,
149
+ holders: holders.holders.slice(0, MAX_MARKET_DATA_HOLDERS),
150
+ };
151
+ }
152
+ case "analystResearch": {
153
+ if (!marketData.getAnalystResearch) throw new Error("The configured market data sources do not provide analyst research.");
154
+ const research = await marketData.getAnalystResearch(
155
+ normalizedMarketDataSymbol(request.symbol),
156
+ normalizedOptionalExchange(request.exchange),
157
+ );
158
+ return {
159
+ ...research,
160
+ recommendations: research.recommendations.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
161
+ ratings: research.ratings.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
162
+ earningsEstimates: research.earningsEstimates.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
163
+ revenueEstimates: research.revenueEstimates.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
164
+ };
165
+ }
166
+ case "corporateActions": {
167
+ if (!marketData.getCorporateActions) throw new Error("The configured market data sources do not provide corporate actions.");
168
+ const actions = await marketData.getCorporateActions(
169
+ normalizedMarketDataSymbol(request.symbol),
170
+ normalizedOptionalExchange(request.exchange),
171
+ );
172
+ return {
173
+ ...actions,
174
+ dividends: actions.dividends.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
175
+ splits: actions.splits.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
176
+ earnings: actions.earnings.slice(0, MAX_MARKET_DATA_EVENT_ROWS),
177
+ };
178
+ }
179
+ case "earningsCalendar": {
180
+ if (!marketData.getEarningsCalendar) throw new Error("The configured market data sources do not provide an earnings calendar.");
181
+ const symbols = request.symbols
182
+ .map((symbol) => normalizedMarketDataSymbol(symbol))
183
+ .filter((symbol, index, all) => all.indexOf(symbol) === index)
184
+ .slice(0, MAX_MARKET_DATA_EARNINGS_SYMBOLS);
185
+ if (symbols.length === 0) throw new Error("Market data symbols are required.");
186
+ return marketData.getEarningsCalendar(symbols);
187
+ }
188
+ default:
189
+ throw new Error(
190
+ `Unknown market data operation "${String((request as { operation?: unknown }).operation)}".`,
191
+ );
192
+ }
193
+ };
194
+
75
195
  const openCommandBar = async (input: Record<string, unknown>): Promise<unknown> => {
76
196
  const mode = optionalString(input, "mode") ?? "command";
77
197
  const query = optionalString(input, "query") ?? "";
@@ -370,6 +490,8 @@ export function createAppRemoteController({
370
490
  const data = getResource(request.resource);
371
491
  return ok(data, revisionFor(data), buildIncludedState(request.include));
372
492
  }
493
+ case "data":
494
+ return ok(await queryMarketData(request));
373
495
  case "call": {
374
496
  const data = await call(request.operation, request.input, request.dryRun);
375
497
  return ok(data, undefined, buildIncludedState(request.include, request.dryRun ? [] : DEFAULT_MUTATION_INCLUDE));
@@ -28,10 +28,21 @@ export type RemoteStateInclude =
28
28
  | "help"
29
29
  | "all";
30
30
 
31
+ export type RemoteMarketDataRequest =
32
+ | { type: "data"; operation: "search"; query: string }
33
+ | { type: "data"; operation: "quote"; symbol: string; exchange?: string }
34
+ | { type: "data"; operation: "financials"; symbol: string; exchange?: string }
35
+ | { type: "data"; operation: "secFilings"; symbol: string; exchange?: string; count?: number }
36
+ | { type: "data"; operation: "holders"; symbol: string; exchange?: string }
37
+ | { type: "data"; operation: "analystResearch"; symbol: string; exchange?: string }
38
+ | { type: "data"; operation: "corporateActions"; symbol: string; exchange?: string }
39
+ | { type: "data"; operation: "earningsCalendar"; symbols: string[] };
40
+
31
41
  export type RemoteControlRequest =
32
42
  | { type: "help"; topic?: string }
33
43
  | { type: "schema" }
34
44
  | { type: "get"; resource: string; include?: RemoteStateInclude[] }
45
+ | RemoteMarketDataRequest
35
46
  | { type: "call"; operation: string; input?: unknown; dryRun?: boolean; include?: RemoteStateInclude[] }
36
47
  | {
37
48
  type: "patch";
@@ -8,8 +8,20 @@ import {
8
8
  type PluginCapability,
9
9
  } from "../../../capabilities";
10
10
  import type { AppServices } from "../../../core/app-services";
11
- import { getAiProviderDefinitions } from "../../../plugins/builtin/ai/providers";
12
- import { isAiRunCancelled, runAiPrompt } from "../../../plugins/builtin/ai/runner";
11
+ import {
12
+ isAiProviderId,
13
+ type AiProviderId,
14
+ } from "../../../plugins/builtin/ai/providers";
15
+ import { createPiAiHost } from "../../../plugins/builtin/ai/pi";
16
+ import {
17
+ isAiRunCancelled,
18
+ type AiConversationMessage,
19
+ type AiRuntimeAuthType,
20
+ } from "../../../plugins/builtin/ai/runner";
21
+ import {
22
+ normalizeAiAgentHistory,
23
+ type AiAgentHistoryMessage,
24
+ } from "../../../plugins/builtin/ai/agent-history";
13
25
  import type { BrokerAdapter } from "../../../types/broker";
14
26
  import type { AppConfig, BrokerInstanceConfig } from "../../../types/config";
15
27
 
@@ -265,48 +277,132 @@ function createNotesFilesCapability(): PluginCapability {
265
277
  };
266
278
  }
267
279
 
268
- function getAiProviderAvailability(): Record<string, boolean> {
269
- const availability: Record<string, boolean> = {};
270
- const hasBunWhich = typeof Bun !== "undefined" && typeof Bun.which === "function";
271
- for (const definition of getAiProviderDefinitions()) {
272
- availability[definition.id] = hasBunWhich ? !!Bun.which(definition.command) : false;
273
- }
274
- return availability;
275
- }
276
-
277
280
  function createAiRunnerCapability(options: CoreCapabilityOptions): PluginCapability {
281
+ const aiHost = createPiAiHost({
282
+ appKind: "desktop",
283
+ dataDir: options.getConfig().dataDir,
284
+ });
285
+ const requireProviderId = (value: unknown): AiProviderId => {
286
+ const providerId = requireString(value, "AI provider");
287
+ if (!isAiProviderId(providerId)) {
288
+ throw new Error(`Unknown AI provider: ${providerId}`);
289
+ }
290
+ return providerId;
291
+ };
292
+ const requireCatalogProvider = async (value: unknown): Promise<AiProviderId> => {
293
+ const providerId = requireProviderId(value);
294
+ const catalog = await aiHost.getCatalog?.();
295
+ if (!catalog?.providers.some((provider) => provider.providerId === providerId)) {
296
+ throw new Error(`Unknown AI provider: ${providerId}`);
297
+ }
298
+ return providerId;
299
+ };
300
+ const optionalAuthType = (value: unknown): AiRuntimeAuthType | undefined => {
301
+ if (value === undefined || value === null || value === "") return undefined;
302
+ if (value === "oauth" || value === "api_key") return value;
303
+ throw new Error(`Unknown AI authentication method: ${String(value)}`);
304
+ };
305
+ const optionalMessages = (value: unknown): AiConversationMessage[] | undefined => {
306
+ if (value === undefined || value === null) return undefined;
307
+ if (!Array.isArray(value)) throw new Error("AI conversation messages must be an array.");
308
+ return value.map((message, index) => {
309
+ if (!message || typeof message !== "object") {
310
+ throw new Error(`AI conversation message ${index + 1} is invalid.`);
311
+ }
312
+ const role = (message as { role?: unknown }).role;
313
+ const content = (message as { content?: unknown }).content;
314
+ if ((role !== "user" && role !== "assistant") || typeof content !== "string") {
315
+ throw new Error(`AI conversation message ${index + 1} is invalid.`);
316
+ }
317
+ return { role, content };
318
+ });
319
+ };
320
+ const optionalAgentMessages = (value: unknown): AiAgentHistoryMessage[] | undefined => {
321
+ if (value === undefined || value === null) return undefined;
322
+ if (!Array.isArray(value)) throw new Error("AI agent messages must be an array.");
323
+ const messages = normalizeAiAgentHistory(value);
324
+ if (messages.length !== value.length) {
325
+ throw new Error("AI agent message history is invalid.");
326
+ }
327
+ return messages;
328
+ };
329
+
278
330
  return {
279
331
  id: AI_RUNNER_CAPABILITY_ID,
280
332
  kind: "ai-runner",
281
333
  name: "AI Runner",
282
334
  operations: {
283
- getProviderAvailability: op(() => getAiProviderAvailability()),
284
- run: stream((input: any, emit) => {
285
- const providerId = requireString(input.providerId, "AI provider");
286
- const prompt = requireString(input.prompt, "AI prompt");
287
- const providerDefinition = getAiProviderDefinitions().find((entry) => entry.id === providerId);
288
- if (!providerDefinition) {
289
- throw new Error(`Unknown AI provider: ${providerId}`);
335
+ getCatalog: op(async () => aiHost.getCatalog?.() ?? { providers: [], accounts: [], models: [] }),
336
+ connectProvider: stream(async (input: any, emit) => {
337
+ const providerId = await requireCatalogProvider(input.providerId);
338
+ const authType = optionalAuthType(input.authType);
339
+ if (!aiHost.connect) throw new Error("In-app AI sign-in is unavailable.");
340
+ let disposed = false;
341
+ aiHost.connect(providerId, authType, (event) => {
342
+ if (!disposed) emit({ kind: "account-auth", event });
343
+ }).then((catalog) => {
344
+ if (!disposed) emit({ kind: "account-connected", catalog });
345
+ }).catch((error) => {
346
+ if (!disposed) emit({
347
+ kind: "account-error",
348
+ error: error instanceof Error ? error.message : String(error),
349
+ });
350
+ });
351
+ return () => {
352
+ disposed = true;
353
+ };
354
+ }),
355
+ disconnectProvider: op(async (input: any) => {
356
+ const providerId = await requireCatalogProvider(input.providerId);
357
+ if (!aiHost.disconnect) throw new Error("In-app AI account disconnection is unavailable.");
358
+ return aiHost.disconnect(providerId);
359
+ }, "action"),
360
+ checkProviderStatus: op(async (input: any) => {
361
+ const providerId = await requireCatalogProvider(input.providerId);
362
+ if (!aiHost.checkStatus) {
363
+ throw new Error("AI provider status checks are unavailable.");
290
364
  }
291
- if (typeof Bun === "undefined" || typeof Bun.which !== "function" || !Bun.which(providerDefinition.command)) {
292
- throw new Error(`${providerDefinition.name} is not installed on this system.`);
365
+ return aiHost.checkStatus(providerId);
366
+ }),
367
+ run: stream(async (input: any, emit) => {
368
+ const providerId = await requireCatalogProvider(input.providerId);
369
+ const prompt = requireString(input.prompt, "AI prompt");
370
+ const messages = optionalMessages(input.messages);
371
+ const agentMessages = optionalAgentMessages(input.agentMessages);
372
+ let completedAgentMessages: AiAgentHistoryMessage[] | undefined;
373
+ const modelId = optionalString(input.modelId);
374
+ const providerStatus = await aiHost.checkStatus?.(providerId);
375
+ if (providerStatus && !providerStatus.authenticated) {
376
+ throw new Error(
377
+ providerStatus.message
378
+ ?? `${providerId} is not connected.`,
379
+ );
293
380
  }
294
381
 
295
382
  let disposed = false;
296
- const controller = runAiPrompt({
297
- provider: {
298
- ...providerDefinition,
299
- available: true,
300
- },
383
+ const controller = aiHost.run({
384
+ providerId,
301
385
  prompt,
302
- cwd: optionalString(input.cwd) ?? options.getConfig().dataDir,
386
+ messages,
387
+ agentMessages,
388
+ modelId: modelId ?? undefined,
389
+ outputMode: input.outputMode === "structured" || input.outputMode === "screener"
390
+ ? input.outputMode
391
+ : "plain",
303
392
  onChunk: (output) => {
304
393
  if (!disposed) emit({ kind: "chunk", output });
305
394
  },
395
+ onAgentMessages: (nextMessages) => {
396
+ completedAgentMessages = nextMessages;
397
+ },
306
398
  });
307
399
 
308
400
  controller.done.then((output) => {
309
- if (!disposed) emit({ kind: "done", output });
401
+ if (!disposed) emit({
402
+ kind: "done",
403
+ output,
404
+ ...(completedAgentMessages ? { agentMessages: completedAgentMessages } : {}),
405
+ });
310
406
  }).catch((error) => {
311
407
  if (disposed) return;
312
408
  if (isAiRunCancelled(error)) {
@@ -1,4 +1,5 @@
1
1
  import { createAppServices, type AppServices } from "../../../../core/app-services";
2
+ import { getDesktopBackendPlugins } from "../../../../plugins/catalog-backend";
2
3
  import type { AppSessionSnapshot } from "../../../../core/state/session-persistence";
3
4
  import {
4
5
  exportConfig,
@@ -80,7 +81,10 @@ async function importDesktopConfig({
80
81
  setDesktopWorkspace(null);
81
82
  teardownServices();
82
83
  setCurrentConfig(await importConfig(payload.dataDir as string, payload.srcPath as string));
83
- setServices(createAppServices({ config: getConfig(), externalPlugins: [] }));
84
+ setServices(createAppServices({
85
+ config: getConfig(),
86
+ plugins: getDesktopBackendPlugins(),
87
+ }));
84
88
  syncConfigAccessors();
85
89
  registerCoreCapabilities();
86
90
  const desktopWorkspace = createDesktopWorkspace(getConfig(), getSessionSnapshot());
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync } from "fs";
2
2
  import { homedir } from "os";
3
3
  import { join } from "path";
4
4
  import { createAppServices, type AppServices } from "../../../../core/app-services";
5
+ import { getDesktopBackendPlugins } from "../../../../plugins/catalog-backend";
5
6
  import type { AppSessionSnapshot } from "../../../../core/state/session-persistence";
6
7
  import {
7
8
  getDataDir,
@@ -126,7 +127,10 @@ export async function initializeDesktopBackend<TRpc>(
126
127
  const config = options.getCurrentConfig();
127
128
  if (!config) throw new Error("Desktop config failed to initialize.");
128
129
 
129
- const services = createAppServices({ config, externalPlugins: [] });
130
+ const services = createAppServices({
131
+ config,
132
+ plugins: getDesktopBackendPlugins(),
133
+ });
130
134
  options.setServices(services);
131
135
  options.syncConfigAccessors();
132
136
  options.registerCoreCapabilities();
@@ -0,0 +1,15 @@
1
+ import type { LiveStreamResolveRequest, ResolvedLiveStream } from "../../../../types/media";
2
+ import { getTvChannel, TV_CHANNELS } from "../../../../plugins/builtin/tv/channels";
3
+ import { resolveTvStream } from "../../../../plugins/builtin/tv/youtube-stream";
4
+
5
+ export async function resolveDesktopLiveStream(payload: Record<string, unknown>): Promise<ResolvedLiveStream> {
6
+ const request = payload as Partial<LiveStreamResolveRequest>;
7
+ if (request.provider !== "youtube") {
8
+ throw new Error("Unsupported live-stream provider.");
9
+ }
10
+ const channel = TV_CHANNELS.find((item) => item.id === request.sourceId);
11
+ if (!channel) {
12
+ throw new Error("Unknown TV channel.");
13
+ }
14
+ return resolveTvStream(getTvChannel(channel.id), { force: request.force === true });
15
+ }
@@ -15,7 +15,10 @@ export function desktopTitleBarStyle(): DesktopTitleBarStyle {
15
15
  }
16
16
 
17
17
  export function desktopWindowRenderer(): DesktopWindowRenderer {
18
- if (process.platform === "win32") return "cef";
18
+ if (process.platform === "win32") {
19
+ const hostArchitecture = process.env.PROCESSOR_ARCHITEW6432 ?? process.env.PROCESSOR_ARCHITECTURE;
20
+ return hostArchitecture?.toLowerCase() === "arm64" ? "native" : "cef";
21
+ }
19
22
  return "native";
20
23
  }
21
24
 
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { createDefaultConfig } from "../../../../../types/config";
2
+ import { cloneLayout, createDefaultConfig } from "../../../../../types/config";
3
3
  import { createDesktopWorkspace } from "./index";
4
4
 
5
5
  describe("desktop workspace", () => {
@@ -60,4 +60,35 @@ describe("desktop workspace", () => {
60
60
  { instanceId: "chat:main", x: 220, y: 260, width: 640, height: 480 },
61
61
  ]);
62
62
  });
63
+
64
+ test("ignores main-window state that arrives behind a newer layout revision", () => {
65
+ const config = createDefaultConfig("/tmp/gloomberb-desktop-layout-revision");
66
+ const workspace = createDesktopWorkspace(config, null);
67
+ const initialSnapshot = workspace.getSnapshot();
68
+ const monitorLayout = config.layouts[1]!;
69
+
70
+ const newerSnapshot = {
71
+ ...initialSnapshot,
72
+ config: {
73
+ ...initialSnapshot.config,
74
+ layout: cloneLayout(monitorLayout.layout),
75
+ activeLayoutIndex: 1,
76
+ },
77
+ paneState: monitorLayout.paneState ?? {},
78
+ focusedPaneId: monitorLayout.focusedPaneId ?? null,
79
+ activePanel: monitorLayout.activePanel ?? "left" as const,
80
+ mainStateRevision: 2,
81
+ };
82
+ const staleSnapshot = {
83
+ ...initialSnapshot,
84
+ mainStateRevision: 1,
85
+ };
86
+
87
+ workspace.syncMainState(newerSnapshot);
88
+ const result = workspace.syncMainState(staleSnapshot);
89
+
90
+ expect(result.mainStateRevision).toBe(2);
91
+ expect(result.config.activeLayoutIndex).toBe(1);
92
+ expect(result.config.layout).toEqual(monitorLayout.layout);
93
+ });
63
94
  });
@@ -62,6 +62,7 @@ export function createDesktopWorkspace(
62
62
  focusedPaneId: initialFocusedPaneId,
63
63
  activePanel: initialActivePanel,
64
64
  statusBarVisible: sessionSnapshot?.statusBarVisible !== false,
65
+ mainStateRevision: 0,
65
66
  };
66
67
 
67
68
  const updateConfig = (nextConfig: AppConfig, options?: { layoutChanged?: boolean }) => {
@@ -94,12 +95,20 @@ export function createDesktopWorkspace(
94
95
  focusedPaneId: sharedState.focusedPaneId,
95
96
  activePanel: sharedState.activePanel,
96
97
  statusBarVisible: sharedState.statusBarVisible,
98
+ mainStateRevision: sharedState.mainStateRevision,
97
99
  layoutChanged: sharedState.layoutChanged,
98
100
  });
99
101
 
100
102
  return {
101
103
  getSnapshot,
102
104
  syncMainState(snapshot) {
105
+ const currentRevision = sharedState.mainStateRevision ?? 0;
106
+ if (
107
+ typeof snapshot.mainStateRevision === "number"
108
+ && snapshot.mainStateRevision <= currentRevision
109
+ ) {
110
+ return getSnapshot();
111
+ }
103
112
  const syncedConfig = syncConfigActiveLayoutState(
104
113
  snapshot.config,
105
114
  snapshot.paneState,
@@ -116,6 +125,7 @@ export function createDesktopWorkspace(
116
125
  focusedPaneId: snapshot.focusedPaneId,
117
126
  activePanel: snapshot.activePanel,
118
127
  statusBarVisible: snapshot.statusBarVisible,
128
+ mainStateRevision: snapshot.mainStateRevision ?? currentRevision,
119
129
  layoutChanged: snapshot.layoutChanged,
120
130
  };
121
131
  return getSnapshot();
@@ -46,6 +46,7 @@ import { DesktopDetachedWindowManager } from "./desktop/detached-windows";
46
46
  import { handleDesktopHostRequest } from "./desktop/host-requests";
47
47
  import { handleDesktopWorkspaceRequest } from "./desktop/workspace/requests";
48
48
  import { handleDesktopBackendRequest } from "./desktop/backend-requests";
49
+ import { resolveDesktopLiveStream } from "./desktop/media";
49
50
  import { initializeDesktopBackend } from "./desktop/initialization";
50
51
  import { applyWindowsCustomChrome } from "./desktop/windows-custom-chrome";
51
52
  import { applyWindowsWindowIcon } from "./desktop/windows-icons";
@@ -429,6 +430,10 @@ async function handleBackendRequest(
429
430
 
430
431
  if (method === "init") return initialize(rpc, payload);
431
432
  if (method === "http.fetch") return handleHttpFetch(payload);
433
+ if (method === "media.resolveLiveStream") return resolveDesktopLiveStream(payload);
434
+ if (method === "remote.forward") {
435
+ return forwardRemoteControlRequest(payload.request as RemoteControlRequest);
436
+ }
432
437
  if (method.startsWith("capability.")) return capabilityBridge.handle(rpc, method, payload);
433
438
  if (method.startsWith("desktop.")) {
434
439
  return handleDesktopWorkspaceRequest({
@@ -1,6 +1 @@
1
- import { startOpenTuiApp } from "../../opentui/start";
2
-
3
- startOpenTuiApp().catch((err) => {
4
- console.error("Fatal error:", err);
5
- process.exitCode = 1;
6
- });
1
+ import "../../../cli/entry";