gloomberb 0.16.2 → 0.16.4

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 (527) hide show
  1. package/package.json +4 -1
  2. package/src/api-client/askg.ts +43 -0
  3. package/src/api-client/auth.ts +15 -0
  4. package/src/api-client/beneficial-owners.ts +4 -0
  5. package/src/api-client/chat.ts +19 -0
  6. package/src/api-client/company-kpis.ts +14 -2
  7. package/src/api-client/data.ts +34 -16
  8. package/src/api-client/index.ts +12 -0
  9. package/src/api-client/normalizers.ts +27 -0
  10. package/src/api-client/paths.ts +88 -23
  11. package/src/api-client/request.ts +2 -1
  12. package/src/api-client/research-activity.ts +31 -0
  13. package/src/api-client/socket.ts +39 -8
  14. package/src/api-client/types.ts +57 -1
  15. package/src/api-client/upgrade-experiment.ts +90 -0
  16. package/src/app/global-shortcuts.ts +11 -1
  17. package/src/app/keybindings/actions.ts +10 -0
  18. package/src/app/pane-runtime/index.tsx +8 -0
  19. package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
  20. package/src/app/pane-runtime/plugin-bindings.ts +65 -25
  21. package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
  22. package/src/app/pane-runtime/ticker-open-runtime.ts +18 -1
  23. package/src/app/runtime/desktop-deeplink.ts +5 -0
  24. package/src/app/star-prompt/model.ts +98 -0
  25. package/src/app/star-prompt/runtime.ts +41 -0
  26. package/src/app/star-prompt/status-line.tsx +190 -0
  27. package/src/app.tsx +13 -7
  28. package/src/architecture/core-builtin-allowlist.json +70 -0
  29. package/src/brokers/account-cache.ts +45 -0
  30. package/src/brokers/signed-in/adapter.ts +33 -2
  31. package/src/brokers/signed-in/client.ts +10 -0
  32. package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
  33. package/src/brokers/signed-in/sign-in.ts +62 -5
  34. package/src/capabilities/registry.ts +8 -1
  35. package/src/capabilities/types.ts +2 -0
  36. package/src/cli/command-options.ts +74 -0
  37. package/src/cli/commands/command-utils.ts +29 -0
  38. package/src/cli/commands/crypto-hints.ts +12 -4
  39. package/src/cli/commands/market.ts +524 -148
  40. package/src/cli/commands/options-chain.ts +172 -0
  41. package/src/cli/commands/options-leaps.ts +422 -0
  42. package/src/cli/commands/overview.ts +223 -42
  43. package/src/cli/commands/search.ts +29 -8
  44. package/src/cli/commands/system.ts +99 -19
  45. package/src/cli/commands/themes.ts +64 -0
  46. package/src/cli/commands/ticker.ts +200 -73
  47. package/src/cli/desktop-pane-shot.ts +100 -16
  48. package/src/cli/errors.ts +25 -2
  49. package/src/cli/freshness.ts +7 -4
  50. package/src/cli/fx-pair.ts +57 -0
  51. package/src/cli/help.ts +40 -7
  52. package/src/cli/history-rows.ts +187 -15
  53. package/src/cli/index-roots.ts +51 -0
  54. package/src/cli/index.ts +21 -13
  55. package/src/cli/listing-arg.ts +211 -3
  56. package/src/cli/not-a-ticker.ts +37 -0
  57. package/src/cli/options.ts +55 -7
  58. package/src/cli/pane-functions/capabilities.ts +13 -2
  59. package/src/cli/pane-functions/catalog.ts +13 -2
  60. package/src/cli/pane-functions/data.ts +36 -5
  61. package/src/cli/pane-functions/dom.ts +89 -28
  62. package/src/cli/pane-functions/freshness.ts +205 -26
  63. package/src/cli/pane-functions/function-help.ts +71 -24
  64. package/src/cli/pane-functions/headless.ts +168 -35
  65. package/src/cli/pane-functions/index.ts +12 -2
  66. package/src/cli/pane-functions/market-session.ts +133 -0
  67. package/src/cli/pane-functions/options.ts +15 -4
  68. package/src/cli/pane-functions/report-notices.ts +63 -0
  69. package/src/cli/pane-functions/report.ts +12 -1
  70. package/src/cli/pane-functions/resolver.ts +32 -8
  71. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  72. package/src/cli/pane-functions/screenshot.ts +109 -23
  73. package/src/cli/registry.ts +8 -4
  74. package/src/cli/report-tables.ts +460 -0
  75. package/src/cli/result.ts +63 -16
  76. package/src/cli/row-window.ts +76 -0
  77. package/src/cli/scroll-truncation.ts +34 -0
  78. package/src/components/chart/composite/composite-chart.tsx +43 -4
  79. package/src/components/chart/composite/legend.tsx +39 -6
  80. package/src/components/chart/composite/rasterizer.ts +64 -4
  81. package/src/components/chart/composite/series-cues.ts +71 -0
  82. package/src/components/chart/composite/text-renderer.ts +16 -8
  83. package/src/components/chart/composite/types.ts +3 -0
  84. package/src/components/command-bar/commands/direct/index.ts +9 -1
  85. package/src/components/command-bar/commands/plugin/items.ts +3 -1
  86. package/src/components/command-bar/commands/registry.ts +7 -0
  87. package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
  88. package/src/components/command-bar/pane-templates/workflow.ts +5 -0
  89. package/src/components/command-bar/routes/root/command-items.ts +2 -0
  90. package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
  91. package/src/components/command-bar/routes/root/results.ts +40 -10
  92. package/src/components/command-bar/routes/root/runtime.ts +8 -0
  93. package/src/components/command-bar/routes/root/search-providers.ts +97 -14
  94. package/src/components/command-bar/surface/index.tsx +18 -4
  95. package/src/components/command-bar/theme-picker.tsx +48 -13
  96. package/src/components/command-bar/workflow/ops.ts +5 -3
  97. package/src/components/data-table/export.ts +6 -51
  98. package/src/components/data-table/view.tsx +31 -10
  99. package/src/components/layout/floating-pane.tsx +6 -2
  100. package/src/components/layout/market-summary.ts +23 -9
  101. package/src/components/layout/pane/content.tsx +46 -29
  102. package/src/components/layout/pane/cycle-order.ts +2 -11
  103. package/src/components/layout/pane/header.tsx +2 -1
  104. package/src/components/layout/pane/title.ts +1 -1
  105. package/src/components/layout/presentation.ts +22 -0
  106. package/src/components/layout/shell/active-drag.ts +67 -35
  107. package/src/components/layout/shell/cursor-occlusion.ts +6 -5
  108. package/src/components/layout/shell/drag/index.ts +16 -7
  109. package/src/components/layout/shell/drag/live.ts +193 -0
  110. package/src/components/layout/shell/drag/overlays.tsx +179 -35
  111. package/src/components/layout/shell/drag/runtime.ts +62 -50
  112. package/src/components/layout/shell/drag/slide.ts +10 -0
  113. package/src/components/layout/shell/index.tsx +84 -57
  114. package/src/components/layout/shell/layout-state.ts +5 -11
  115. package/src/components/layout/shell/menu.ts +28 -1
  116. package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
  117. package/src/components/layout/shell/native/surfaces.ts +30 -14
  118. package/src/components/layout/shell/pane/actions.ts +2 -1
  119. package/src/components/layout/shell/pane/layers.tsx +461 -180
  120. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  121. package/src/components/layout/shell/pane/new-layout.ts +116 -0
  122. package/src/components/layout/shell/shortcuts.ts +5 -2
  123. package/src/components/layout/shell/terminal-pointer-runtime.ts +18 -18
  124. package/src/components/layout/shell/visible-layout.ts +44 -1
  125. package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
  126. package/src/components/layout/status-bar.tsx +4 -1
  127. package/src/components/markdown-text.tsx +9 -5
  128. package/src/components/metric-treemap/heat-surface.tsx +27 -8
  129. package/src/components/metric-treemap/heat-tiles.ts +48 -17
  130. package/src/components/onboarding/onboarding-frame.tsx +33 -10
  131. package/src/components/onboarding/onboarding-wizard.tsx +49 -40
  132. package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
  133. package/src/components/onboarding/pro-step-layout.ts +86 -0
  134. package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
  135. package/src/components/ui/button.tsx +10 -0
  136. package/src/components/ui/data-table/index.tsx +38 -0
  137. package/src/components/ui/data-table/types.ts +8 -1
  138. package/src/components/ui/data-table/visible-range.ts +2 -0
  139. package/src/components/ui/icon.tsx +11 -2
  140. package/src/components/ui/index.ts +1 -1
  141. package/src/core/connection-health.ts +1 -1
  142. package/src/core/state/app/layout-reducer.ts +89 -3
  143. package/src/core/state/app/state.ts +8 -5
  144. package/src/core/state/app/types.ts +13 -2
  145. package/src/data/config/layout.ts +55 -21
  146. package/src/data/config/store/fold-panes.ts +149 -0
  147. package/src/data/config/store/migrations.ts +7 -0
  148. package/src/data/config/store/node.ts +27 -6
  149. package/src/data/config/store/normalize.ts +37 -3
  150. package/src/data/json-persistence.ts +17 -3
  151. package/src/data/json-storage.ts +2 -0
  152. package/src/data/memory-resource-store.ts +46 -0
  153. package/src/i18n/es.ts +14 -1
  154. package/src/i18n/ja.ts +14 -1
  155. package/src/i18n/ko.ts +14 -1
  156. package/src/i18n/zh-cn.ts +14 -1
  157. package/src/i18n/zh-tw.ts +14 -1
  158. package/src/layout/desks.ts +1 -2
  159. package/src/layout/pane-layout-move.ts +264 -0
  160. package/src/layout/pane-manager/hidden-panes.ts +160 -0
  161. package/src/layout/pane-manager/index.ts +1 -0
  162. package/src/market-data/coordinator/entries.ts +7 -1
  163. package/src/market-data/coordinator/quotes.ts +2 -2
  164. package/src/market-data/market/freshness.ts +156 -11
  165. package/src/market-data/market/status.ts +40 -0
  166. package/src/market-data/options-alternatives.ts +69 -0
  167. package/src/market-data/quotes/contributions.ts +19 -2
  168. package/src/market-data/quotes/freshness.ts +5 -0
  169. package/src/market-data/quotes/metadata.ts +16 -0
  170. package/src/market-data/quotes/screener-live-quotes.ts +56 -5
  171. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  172. package/src/plugins/builtin/account-management/model.ts +1 -0
  173. package/src/plugins/builtin/account-management/navigation.ts +1 -1
  174. package/src/plugins/builtin/account-management/pane.tsx +31 -4
  175. package/src/plugins/builtin/alerts/quotes.ts +3 -0
  176. package/src/plugins/builtin/analytics/pane-model.ts +10 -6
  177. package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
  178. package/src/plugins/builtin/analytics/risk-model.ts +37 -8
  179. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
  180. package/src/plugins/builtin/attention/pane.tsx +1 -1
  181. package/src/plugins/builtin/backtest/pane.tsx +1 -1
  182. package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
  183. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  184. package/src/plugins/builtin/broker-manager/model.ts +8 -0
  185. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
  186. package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
  187. package/src/plugins/builtin/broker-manager/table.ts +5 -0
  188. package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
  189. package/src/plugins/builtin/catalysts/detail.tsx +1 -1
  190. package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
  191. package/src/plugins/builtin/chat/channels.ts +34 -36
  192. package/src/plugins/builtin/chat/command-bar.ts +138 -0
  193. package/src/plugins/builtin/chat/composer-commands.ts +21 -1
  194. package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
  195. package/src/plugins/builtin/chat/content/footer.tsx +5 -3
  196. package/src/plugins/builtin/chat/content/index.tsx +21 -1
  197. package/src/plugins/builtin/chat/content/mentions.ts +2 -1
  198. package/src/plugins/builtin/chat/content/user-map.ts +3 -1
  199. package/src/plugins/builtin/chat/controller/index.ts +48 -0
  200. package/src/plugins/builtin/chat/conversations.ts +256 -0
  201. package/src/plugins/builtin/chat/discord-command.ts +91 -0
  202. package/src/plugins/builtin/chat/edit-window.ts +12 -6
  203. package/src/plugins/builtin/chat/ghost-user.ts +14 -0
  204. package/src/plugins/builtin/chat/index.tsx +40 -2
  205. package/src/plugins/builtin/chat/layout.ts +2 -0
  206. package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
  207. package/src/plugins/builtin/chat/message/header.tsx +24 -15
  208. package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
  209. package/src/plugins/builtin/chat/message/render-state.ts +3 -0
  210. package/src/plugins/builtin/chat/pane-requests.ts +60 -0
  211. package/src/plugins/builtin/chat/pane-state.ts +32 -2
  212. package/src/plugins/builtin/chat/pane.tsx +8 -0
  213. package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
  214. package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
  215. package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
  216. package/src/plugins/builtin/cloud/askg/model.ts +129 -70
  217. package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
  218. package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
  219. package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
  220. package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
  221. package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
  222. package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
  223. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
  224. package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
  225. package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
  226. package/src/plugins/builtin/cloud/plugin.tsx +36 -1
  227. package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
  228. package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
  229. package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
  230. package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
  231. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
  232. package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
  233. package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
  234. package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
  235. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
  236. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
  237. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
  238. package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
  239. package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
  240. package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
  241. package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
  242. package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
  243. package/src/plugins/builtin/comment-letters/client.ts +1 -3
  244. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  245. package/src/plugins/builtin/company-kpis/client.ts +23 -8
  246. package/src/plugins/builtin/company-kpis/headless.ts +3 -1
  247. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  248. package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
  249. package/src/plugins/builtin/composite-plugins.ts +107 -64
  250. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
  251. package/src/plugins/builtin/credit-boards/index.tsx +21 -21
  252. package/src/plugins/builtin/credit-boards/model.ts +0 -1
  253. package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
  254. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
  255. package/src/plugins/builtin/debt-maturities/client.ts +37 -9
  256. package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
  257. package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
  258. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  259. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  260. package/src/plugins/builtin/earnings-calls/data.ts +28 -7
  261. package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
  262. package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
  263. package/src/plugins/builtin/econ/calendar-model.ts +42 -17
  264. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  265. package/src/plugins/builtin/econ/index.tsx +68 -22
  266. package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
  267. package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
  268. package/src/plugins/builtin/econ-statistics/view.ts +17 -0
  269. package/src/plugins/builtin/executives/data.ts +17 -6
  270. package/src/plugins/builtin/executives/pane.tsx +16 -7
  271. package/src/plugins/builtin/filing-events/pane.tsx +22 -5
  272. package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
  273. package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
  274. package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
  275. package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
  276. package/src/plugins/builtin/gpu/headless.ts +9 -3
  277. package/src/plugins/builtin/gpu/model.ts +14 -5
  278. package/src/plugins/builtin/help/index.tsx +1 -0
  279. package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
  280. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  281. package/src/plugins/builtin/holders/client.ts +3 -6
  282. package/src/plugins/builtin/holders/format.ts +31 -1
  283. package/src/plugins/builtin/holders/headless.ts +32 -6
  284. package/src/plugins/builtin/holders/pane.tsx +25 -13
  285. package/src/plugins/builtin/holders/report-header.ts +46 -5
  286. package/src/plugins/builtin/insider/index.tsx +4 -4
  287. package/src/plugins/builtin/iv-history/charts.tsx +21 -7
  288. package/src/plugins/builtin/iv-history/client.ts +12 -2
  289. package/src/plugins/builtin/iv-history/format.ts +33 -2
  290. package/src/plugins/builtin/iv-history/headless.ts +22 -10
  291. package/src/plugins/builtin/iv-history/index.tsx +3 -3
  292. package/src/plugins/builtin/iv-history/model.ts +61 -13
  293. package/src/plugins/builtin/iv-history/pane.tsx +11 -3
  294. package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
  295. package/src/plugins/builtin/jobs/pane.tsx +1 -1
  296. package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
  297. package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
  298. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  299. package/src/plugins/builtin/kelly-sizer/pane.tsx +121 -45
  300. package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
  301. package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
  302. package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
  303. package/src/plugins/builtin/layout-manager/index.tsx +9 -1
  304. package/src/plugins/builtin/macro-day/client.ts +24 -0
  305. package/src/plugins/builtin/macro-day/headless.ts +11 -8
  306. package/src/plugins/builtin/macro-day/index.tsx +3 -0
  307. package/src/plugins/builtin/macro-day/model.ts +11 -6
  308. package/src/plugins/builtin/macro-day/pane.tsx +12 -8
  309. package/src/plugins/builtin/macro-day/release-table.ts +40 -0
  310. package/src/plugins/builtin/macro-day/releases.ts +62 -44
  311. package/src/plugins/builtin/market-heatmap/data.ts +25 -5
  312. package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
  313. package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
  314. package/src/plugins/builtin/market-heatmap/model.ts +94 -3
  315. package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
  316. package/src/plugins/builtin/market-movers/headless.ts +31 -4
  317. package/src/plugins/builtin/market-movers/index.tsx +8 -1
  318. package/src/plugins/builtin/market-movers/model.ts +71 -17
  319. package/src/plugins/builtin/market-movers/table.ts +50 -23
  320. package/src/plugins/builtin/members/pane.tsx +10 -0
  321. package/src/plugins/builtin/news/headless.ts +9 -2
  322. package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
  323. package/src/plugins/builtin/openfda/client.ts +1 -1
  324. package/src/plugins/builtin/options/carry.ts +95 -0
  325. package/src/plugins/builtin/options/index.tsx +58 -5
  326. package/src/plugins/builtin/options/strike-window.ts +183 -0
  327. package/src/plugins/builtin/options/table.ts +55 -3
  328. package/src/plugins/builtin/options/types.ts +8 -1
  329. package/src/plugins/builtin/options/view.tsx +161 -12
  330. package/src/plugins/builtin/options-positioning/headless.ts +4 -2
  331. package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
  332. package/src/plugins/builtin/options-scenario/client.ts +73 -17
  333. package/src/plugins/builtin/options-scenario/headless.ts +71 -24
  334. package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
  335. package/src/plugins/builtin/options-scenario/index.tsx +9 -2
  336. package/src/plugins/builtin/options-scenario/model.ts +33 -0
  337. package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
  338. package/src/plugins/builtin/pe-band/model.ts +2 -2
  339. package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
  340. package/src/plugins/builtin/perps/equity-row.tsx +8 -5
  341. package/src/plugins/builtin/perps/model.ts +16 -5
  342. package/src/plugins/builtin/perps/pane.tsx +1 -1
  343. package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
  344. package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
  345. package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
  346. package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
  347. package/src/plugins/builtin/plugin-module.ts +87 -4
  348. package/src/plugins/builtin/portfolio-list/account-metrics.ts +16 -1
  349. package/src/plugins/builtin/portfolio-list/allocation.ts +3 -0
  350. package/src/plugins/builtin/portfolio-list/cached-account.ts +18 -9
  351. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
  352. package/src/plugins/builtin/portfolio-list/cli/render.ts +89 -18
  353. package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
  354. package/src/plugins/builtin/portfolio-list/headless.ts +40 -7
  355. package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -4
  356. package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -4
  357. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
  358. package/src/plugins/builtin/realized-vol/headless.ts +3 -0
  359. package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
  360. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
  361. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
  362. package/src/plugins/builtin/research/relative-valuation-model.ts +7 -3
  363. package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
  364. package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
  365. package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
  366. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
  367. package/src/plugins/builtin/reverse-dcf/model.ts +2 -1
  368. package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
  369. package/src/plugins/builtin/risk-factors/data.ts +23 -9
  370. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  371. package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
  372. package/src/plugins/builtin/seasonality/pane.tsx +1 -1
  373. package/src/plugins/builtin/sec/index.tsx +5 -5
  374. package/src/plugins/builtin/sectors/client.ts +10 -2
  375. package/src/plugins/builtin/sectors/headless.ts +35 -6
  376. package/src/plugins/builtin/sectors/index.tsx +18 -1
  377. package/src/plugins/builtin/sectors/sector-model.ts +150 -41
  378. package/src/plugins/builtin/shared/command-tab.ts +51 -0
  379. package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
  380. package/src/plugins/builtin/shared/members-menu.tsx +260 -0
  381. package/src/plugins/builtin/shared/report-freshness.ts +50 -2
  382. package/src/plugins/builtin/shared/ticker-request.ts +25 -1
  383. package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
  384. package/src/plugins/builtin/shared/volatility/options.ts +1 -46
  385. package/src/plugins/builtin/short-interest/index.tsx +19 -4
  386. package/src/plugins/builtin/short-interest/surface.tsx +4 -4
  387. package/src/plugins/builtin/short-volume/index.tsx +1 -10
  388. package/src/plugins/builtin/short-volume/pane.tsx +1 -1
  389. package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
  390. package/src/plugins/builtin/supply-chain/model.ts +1 -1
  391. package/src/plugins/builtin/themes/pane.tsx +3 -0
  392. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
  393. package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
  394. package/src/plugins/builtin/ticker-detail/headless.ts +63 -25
  395. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -4
  396. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +3 -2
  397. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
  398. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
  399. package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
  400. package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
  401. package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
  402. package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
  403. package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
  404. package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
  405. package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
  406. package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
  407. package/src/plugins/builtin/vol-surface/client.ts +6 -2
  408. package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
  409. package/src/plugins/builtin/vol-surface/headless.ts +124 -20
  410. package/src/plugins/builtin/vol-surface/model.ts +23 -11
  411. package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
  412. package/src/plugins/builtin/vol-surface/tables.ts +124 -0
  413. package/src/plugins/builtin/world-indices/client.ts +22 -4
  414. package/src/plugins/builtin/world-indices/headless.ts +54 -27
  415. package/src/plugins/builtin/world-indices/model.ts +4 -2
  416. package/src/plugins/builtin/world-indices/table.tsx +13 -2
  417. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +2 -0
  418. package/src/plugins/builtin/world-venue-map/basemap.ts +18 -0
  419. package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
  420. package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
  421. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +2 -1
  422. package/src/plugins/builtin/world-venue-map/index.ts +6 -6
  423. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +27 -14
  424. package/src/plugins/builtin/world-venue-map/layers.ts +68 -10
  425. package/src/plugins/builtin/world-venue-map/map.tsx +198 -62
  426. package/src/plugins/builtin/world-venue-map/model.ts +16 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +3 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
  429. package/src/plugins/builtin/world-venue-map/pane.tsx +16 -10
  430. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  431. package/src/plugins/builtin-editorial.ts +126 -0
  432. package/src/plugins/catalog-backend.ts +16 -9
  433. package/src/plugins/catalog-browser.ts +127 -63
  434. package/src/plugins/catalog-manifest.ts +73 -40
  435. package/src/plugins/catalog-ui.ts +26 -6
  436. package/src/plugins/extracted-plugins.ts +7 -1
  437. package/src/plugins/ownership.ts +150 -7
  438. package/src/plugins/plugin-functions.ts +54 -0
  439. package/src/plugins/plugin-off.ts +36 -0
  440. package/src/plugins/registry/host-actions.ts +16 -0
  441. package/src/plugins/registry/index.ts +84 -24
  442. package/src/plugins/registry/plugin-state.ts +25 -1
  443. package/src/plugins/runtime/context.tsx +29 -0
  444. package/src/react/input.ts +48 -1
  445. package/src/remote/app-host.tsx +5 -0
  446. package/src/remote/controller.ts +3 -53
  447. package/src/remote/matching.ts +69 -0
  448. package/src/remote/schema.ts +1 -1
  449. package/src/remote/semantic-tree.tsx +14 -0
  450. package/src/remote/types.ts +2 -0
  451. package/src/renderers/browser/app-services.ts +3 -1
  452. package/src/renderers/dom/data-table/dom.ts +31 -0
  453. package/src/renderers/dom/data-table/index.tsx +5 -0
  454. package/src/renderers/dom/desktop/icons.tsx +14 -0
  455. package/src/renderers/dom/desktop/query-bar.tsx +59 -32
  456. package/src/renderers/dom/host/box.tsx +113 -17
  457. package/src/renderers/dom/host/drag-shield.ts +35 -0
  458. package/src/renderers/dom/host/live-frame.ts +62 -0
  459. package/src/renderers/dom/host/mouse.ts +8 -1
  460. package/src/renderers/dom/host/scroll-box.tsx +6 -5
  461. package/src/renderers/dom/host/tabs.tsx +3 -2
  462. package/src/renderers/dom/input-host.tsx +6 -1
  463. package/src/renderers/dom/styles.css +26 -5
  464. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
  465. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  466. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
  467. package/src/renderers/electrobun/bun/desktop/initialization.ts +15 -1
  468. package/src/renderers/electrobun/shared/protocol.ts +12 -1
  469. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -1
  470. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
  471. package/src/renderers/electrobun/view/host/native.ts +0 -10
  472. package/src/renderers/electrobun/view/host/window-move.ts +28 -0
  473. package/src/renderers/electrobun/view/main.tsx +4 -1
  474. package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
  475. package/src/renderers/electrobun/view/ui-host.tsx +2 -5
  476. package/src/renderers/opentui/input-host.tsx +6 -1
  477. package/src/renderers/opentui/start.tsx +9 -0
  478. package/src/renderers/opentui/ui-host.tsx +7 -1
  479. package/src/shares/portable-layout.ts +16 -6
  480. package/src/sources/gloomberb-cloud/index.ts +19 -10
  481. package/src/sources/gloomberb-cloud/normalizers.ts +20 -21
  482. package/src/sources/listing-symbols.ts +11 -5
  483. package/src/sources/provider-errors.ts +88 -3
  484. package/src/sources/provider-router/batches.ts +3 -3
  485. package/src/sources/provider-router/cached-routes.ts +8 -4
  486. package/src/sources/provider-router/financial-routes.ts +13 -7
  487. package/src/sources/provider-router/financials.ts +106 -16
  488. package/src/sources/provider-router/history.ts +27 -11
  489. package/src/sources/provider-router/index.ts +1 -1
  490. package/src/sources/provider-router/primary.ts +24 -6
  491. package/src/sources/sec-edgar.ts +68 -8
  492. package/src/state/pane-scroll-registry.tsx +67 -35
  493. package/src/sync/core-contributors.ts +4 -3
  494. package/src/test-support/cli-context.ts +2 -1
  495. package/src/test-support/data-provider.ts +34 -0
  496. package/src/test-support/margin-account.ts +108 -0
  497. package/src/test-support/shell-pane-keys.tsx +1 -0
  498. package/src/theme/series-colors.ts +46 -0
  499. package/src/theme/themes.ts +149 -0
  500. package/src/tickers/search/index.ts +22 -2
  501. package/src/tickers/search/ranking.ts +45 -1
  502. package/src/tickers/search/types.ts +3 -1
  503. package/src/time-series/resolution.ts +15 -0
  504. package/src/time-series/resolve.ts +1 -14
  505. package/src/types/config.ts +61 -1
  506. package/src/types/financials.ts +32 -3
  507. package/src/types/headless.ts +11 -0
  508. package/src/types/instrument.ts +8 -0
  509. package/src/types/plugin.ts +49 -3
  510. package/src/ui/host.tsx +16 -0
  511. package/src/ui/index.tsx +10 -3
  512. package/src/ui/scroll-box-axis.ts +26 -0
  513. package/src/utils/cli-output.ts +14 -1
  514. package/src/utils/currency-units.ts +8 -3
  515. package/src/utils/depositary-receipt.ts +54 -10
  516. package/src/utils/display-number.ts +63 -0
  517. package/src/utils/exchanges.ts +13 -0
  518. package/src/utils/format.ts +2 -1
  519. package/src/utils/fundamentals.ts +17 -1
  520. package/src/utils/fx-market-hours.ts +34 -0
  521. package/src/utils/option-expiry.ts +126 -0
  522. package/src/utils/price-history-integrity.ts +19 -6
  523. package/src/utils/price-history.ts +39 -6
  524. package/src/utils/rpc-timeout-error.ts +6 -2
  525. package/src/utils/sec-eps-basis.ts +159 -30
  526. package/src/utils/sec.ts +38 -34
  527. package/src/utils/throttled-fetch.ts +51 -22
@@ -38,6 +38,18 @@ function metadataExchange(value?: string): string {
38
38
  return exchange === "NY MERCANTILE" ? "NYMEX" : exchange;
39
39
  }
40
40
 
41
+ /**
42
+ * The pair an FX symbol names, base then quote: EURUSD for EUR/USD and
43
+ * EURUSD=X, USDJPY for USD/JPY, USDJPY=X and JPY=X, the dollar pair's short
44
+ * spelling.
45
+ */
46
+ function currencyPairCode(symbol: string): string | null {
47
+ const pair = /^([A-Z]{3})\/([A-Z]{3})$/.exec(symbol) ?? /^([A-Z]{3})([A-Z]{3})=X$/.exec(symbol);
48
+ if (pair) return `${pair[1]}${pair[2]}`;
49
+ const dollarPair = /^([A-Z]{3})=X$/.exec(symbol);
50
+ return dollarPair ? `USD${dollarPair[1]}` : null;
51
+ }
52
+
41
53
  /** Qualified listings must agree; provider suffix spellings use the existing exact-listing normalizer. */
42
54
  export function quoteMetadataMatchesTarget(metadata: QuoteMetadata, symbol: string, exchange?: string): boolean {
43
55
  const target = parsePublicTickerKey(symbol);
@@ -48,6 +60,10 @@ export function quoteMetadataMatchesTarget(metadata: QuoteMetadata, symbol: stri
48
60
  if (symbolExchange && actualExchange && symbolExchange !== actualExchange) return false;
49
61
  if (requestedExchange && actualExchange !== requestedExchange) return false;
50
62
  if (target.symbol === actual.symbol) return true;
63
+ // Gloom Cloud answers a currency pair in one spelling however it was asked
64
+ // for: EUR/USD comes back as EURUSD=X on its currency venue.
65
+ const pair = actualExchange === "CCY" ? currencyPairCode(actual.symbol) : null;
66
+ if (pair && pair === currencyPairCode(target.symbol)) return true;
51
67
  const listing = requestedExchange || actualExchange;
52
68
  if (!listing) return false;
53
69
  const candidates = getListingSymbolsToTry(target.symbol, listing, { exactExchange: true });
@@ -6,6 +6,7 @@ import type { QueryEntry } from "../result-types";
6
6
  import { resolveCurrencyUnit } from "../../utils/currency-units";
7
7
  import { isFiniteNumber } from "../../utils/guards";
8
8
  import { getCompletedRegularSessionDisplay, getSessionMoveDisplay, type ExtendedSession } from "../market/status";
9
+ import { quoteTradingDay } from "./day-range";
9
10
 
10
11
  const STREAM_FRESHNESS_MS = 2 * 60_000;
11
12
  const STREAM_CONNECTING_GRACE_MS = 15_000;
@@ -39,6 +40,21 @@ export interface ScreenerQuoteOverlayOptions {
39
40
  * two different days.
40
41
  */
41
42
  extendedSessions?: boolean;
43
+ /**
44
+ * Only the regular session, never an extended print, whatever
45
+ * `extendedSessions` says: the live quote while the session trades, then
46
+ * the completed session's close and its move, given as
47
+ * `regularChangePercent`. A quote that cannot say which close that is
48
+ * leaves its row as it was.
49
+ */
50
+ regularSession?: boolean;
51
+ /**
52
+ * With `regularSession`, the completed regular session the rows already
53
+ * are (a New York date) when they were taken outside the session. A quote
54
+ * replaces such a row only with that session's close or a later one, and a
55
+ * live quote only once a later session trades.
56
+ */
57
+ completedSessionDate?: string | null;
42
58
  }
43
59
 
44
60
  export interface ScreenerQuoteFreshness {
@@ -83,14 +99,18 @@ function overlayCurrency(row: ScreenerQuoteRow, quote: Quote): string {
83
99
  * entries map, but most rows' quotes did not move; returning the same row
84
100
  * object for those lets memoized table rows skip the render.
85
101
  */
86
- const overlayCache = new WeakMap<object, { quote: Quote; extended: boolean; row: ScreenerQuoteRow }>();
102
+ const overlayCache = new WeakMap<object, { quote: Quote; basis: string; row: ScreenerQuoteRow }>();
87
103
 
88
104
  export function overlayScreenerQuoteEntries<T extends ScreenerQuoteRow>(
89
105
  rows: readonly T[],
90
106
  entries: ReadonlyMap<string, QueryEntry<Quote>>,
91
107
  options: ScreenerQuoteOverlayOptions = {},
92
108
  ): T[] {
93
- const extended = options.extendedSessions === true;
109
+ const regular = options.regularSession === true;
110
+ const completedSessionDate = regular ? options.completedSessionDate ?? null : null;
111
+ const extended = !regular && options.extendedSessions === true;
112
+ // The cached row is reused only for the same quote under the same options.
113
+ const basis = regular ? `regular:${completedSessionDate ?? ""}` : extended ? "extended" : "plain";
94
114
  return rows.map((row) => {
95
115
  const quote = resolveEntryData(entries.get(quoteKey(row)));
96
116
  if (!quote || !isFiniteNumber(quote.price)) return row;
@@ -102,9 +122,11 @@ export function overlayScreenerQuoteEntries<T extends ScreenerQuoteRow>(
102
122
  return row;
103
123
  }
104
124
  const cached = overlayCache.get(row);
105
- if (cached?.quote === quote && cached.extended === extended) return cached.row as T;
106
- const overlaid = extended ? overlaySessionQuote(row, quote) : overlayQuote(row, quote);
107
- overlayCache.set(row, { quote, extended, row: overlaid });
125
+ if (cached?.quote === quote && cached.basis === basis) return cached.row as T;
126
+ const overlaid = regular
127
+ ? overlayRegularSessionQuote(row, quote, completedSessionDate)
128
+ : extended ? overlaySessionQuote(row, quote) : overlayQuote(row, quote);
129
+ overlayCache.set(row, { quote, basis, row: overlaid });
108
130
  return overlaid;
109
131
  });
110
132
  }
@@ -149,6 +171,35 @@ function overlaySessionQuote<T extends ScreenerQuoteRow>(row: T, quote: Quote):
149
171
  };
150
172
  }
151
173
 
174
+ function overlayRegularSessionQuote<T extends ScreenerQuoteRow>(row: T, quote: Quote, completedSessionDate: string | null): T {
175
+ const state = quote.marketState;
176
+ if (state == null || state === "REGULAR") {
177
+ // On a board of a completed session, only a session that trades after it is news.
178
+ if (completedSessionDate) {
179
+ const day = state === "REGULAR" ? quoteTradingDay(quote) : null;
180
+ if (day == null || day <= completedSessionDate) return row;
181
+ }
182
+ return overlayQuote(row, quote);
183
+ }
184
+ const display = getCompletedRegularSessionDisplay(quote);
185
+ const closeDate = quote.regularCloseSessionDate;
186
+ if (
187
+ !display
188
+ || !(isFiniteNumber(display.price) && display.price > 0)
189
+ || !isFiniteNumber(display.changePercent)
190
+ || (completedSessionDate != null && closeDate != null && closeDate < completedSessionDate)
191
+ ) {
192
+ return row;
193
+ }
194
+ return {
195
+ ...overlayQuote(row, quote),
196
+ price: display.price,
197
+ change: isFiniteNumber(display.change) ? display.change : null,
198
+ changePercent: display.changePercent,
199
+ regularChangePercent: display.changePercent,
200
+ };
201
+ }
202
+
152
203
  function freshQuote(
153
204
  entry: QueryEntry<Quote> | undefined,
154
205
  freshness: ScreenerQuoteFreshness,
@@ -22,6 +22,7 @@ export function useAccountManagementKeyboard({
22
22
  draftRef,
23
23
  fieldOrder,
24
24
  focused,
25
+ openAssistants,
25
26
  openPasswordDialog,
26
27
  openPortfolioDialog,
27
28
  openUpgrade,
@@ -38,6 +39,7 @@ export function useAccountManagementKeyboard({
38
39
  /** The active tab's ring, in reading order. */
39
40
  fieldOrder: readonly AccountFieldKey[];
40
41
  focused: boolean;
42
+ openAssistants: () => void;
41
43
  openPasswordDialog: () => void;
42
44
  openPortfolioDialog: () => Promise<void>;
43
45
  openUpgrade: () => void;
@@ -52,6 +54,7 @@ export function useAccountManagementKeyboard({
52
54
  sharedPortfolioId: () => { void openPortfolioDialog(); },
53
55
  passwordAction: openPasswordDialog,
54
56
  upgradeAction: openUpgrade,
57
+ assistantsAction: openAssistants,
55
58
  deleteAccountAction: () => { void deleteAccount(); },
56
59
  emailAlertsOffAction: () => { void turnOffEmailAlerts(); },
57
60
  };
@@ -21,6 +21,7 @@ export type AccountFieldKey =
21
21
  | "chatEmailNotificationsEnabled"
22
22
  | "emailAlertsOffAction"
23
23
  | "upgradeAction"
24
+ | "assistantsAction"
24
25
  | "passwordAction"
25
26
  | "deleteAccountAction";
26
27
 
@@ -1,6 +1,6 @@
1
1
  import { createPaneRequestChannel } from "../shared/pane-request";
2
2
 
3
- export type AccountManagementTab = "profile" | "emails" | "calendar" | "pro" | "teams" | "advanced";
3
+ export type AccountManagementTab = "profile" | "emails" | "calendar" | "pro" | "teams" | "agents" | "advanced";
4
4
 
5
5
  const tabRequests = createPaneRequestChannel<AccountManagementTab>();
6
6
 
@@ -12,6 +12,8 @@ import {
12
12
  type StatItem,
13
13
  } from "../../../components";
14
14
  import { useAppSelector, usePaneAppConfig } from "../../../state/app/context";
15
+ import { useViewport } from "../../../react/input";
16
+ import { displayWidth } from "../../../utils/format";
15
17
  import { useChartQueries, useFxRatesMap } from "../../../market-data/hooks";
16
18
  import { buildPortfolioFinancialsMap } from "../../../market-data/portfolio-financials";
17
19
  import { useLiveTickerFinancialsMap, useSampledValue } from "../../../state/hooks/live-ticker-financials";
@@ -33,7 +35,9 @@ import { apiClient, type AccountProfile, type CloudPricing } from "../../../api-
33
35
  import { chatController } from "../chat/controller";
34
36
  import { SignInWall } from "../cloud/auth-actions";
35
37
  import { loadUpgradeOffer } from "../cloud/upgrade-dialog";
38
+ import { openMcpConnectDialog } from "../cloud/mcp-connect/dialog";
36
39
  import { TeamsAccountTab } from "../cloud/team/acm-tab";
40
+ import { AgentsAccountTab } from "../cloud/terminal-relay/acm-tab";
37
41
  import {
38
42
  CheckboxRow,
39
43
  FieldRow,
@@ -87,6 +91,7 @@ const ACCOUNT_TAB_DEFS: Array<{ label: string; value: AccountManagementTab }> =
87
91
  { label: "Calendar", value: "calendar" },
88
92
  { label: "Pro", value: "pro" },
89
93
  { label: "Teams", value: "teams" },
94
+ { label: "Agents", value: "agents" },
90
95
  { label: "Advanced", value: "advanced" },
91
96
  ];
92
97
 
@@ -114,7 +119,9 @@ const ACCOUNT_TAB_FIELD_ORDER: Record<AccountManagementTab, AccountFieldKey[]> =
114
119
  pro: ["upgradeAction"],
115
120
  // The Teams tab owns its own keyboard handling (a list, not form fields).
116
121
  teams: [],
117
- advanced: ["passwordAction", "deleteAccountAction"],
122
+ // Remote assistants allowed to drive this terminal; a list with its own footer.
123
+ agents: [],
124
+ advanced: ["assistantsAction", "passwordAction", "deleteAccountAction"],
118
125
  };
119
126
 
120
127
  const PROFILE_PREVIEW_SAMPLE_MS = 10_000;
@@ -562,6 +569,12 @@ export function AccountManagementPane({ focused, width, height }: PaneProps) {
562
569
  }).catch(() => {});
563
570
  }, [busy, dialog]);
564
571
 
572
+ const viewport = useViewport();
573
+ const openAssistants = useCallback(() => {
574
+ setActiveField("assistantsAction");
575
+ void openMcpConnectDialog(dialog, { viewportWidth: viewport.width });
576
+ }, [dialog, viewport.width]);
577
+
565
578
  const openPortfolioPicker = useCallback(async () => {
566
579
  setActiveField("sharedPortfolioId");
567
580
  portfolioSelectRef.current?.open();
@@ -705,8 +718,8 @@ export function AccountManagementPane({ focused, width, height }: PaneProps) {
705
718
 
706
719
  useAccountManagementFooter({
707
720
  busy,
708
- // The Calendar tab saves nothing and shows its own status.
709
- enabled: activeTab !== "calendar",
721
+ // The Calendar and Agents tabs save nothing and show their own status.
722
+ enabled: activeTab !== "calendar" && activeTab !== "agents",
710
723
  hasSession,
711
724
  message,
712
725
  saveProfile,
@@ -719,7 +732,8 @@ export function AccountManagementPane({ focused, width, height }: PaneProps) {
719
732
  draftRef,
720
733
  fieldOrder,
721
734
  // Behind the sign-in wall the fields are not there, and Enter is the wall's.
722
- focused: focused && activeTab !== "teams" && activeTab !== "calendar" && (hasSession || apiClient.isSignedIn()),
735
+ focused: focused && activeTab !== "teams" && activeTab !== "calendar" && activeTab !== "agents" && (hasSession || apiClient.isSignedIn()),
736
+ openAssistants,
723
737
  openPasswordDialog,
724
738
  openPortfolioDialog: openPortfolioPicker,
725
739
  openUpgrade,
@@ -927,8 +941,21 @@ export function AccountManagementPane({ focused, width, height }: PaneProps) {
927
941
  {activeTab === "teams" ? (
928
942
  <TeamsAccountTab focused={focused} width={contentWidth} />
929
943
  ) : null}
944
+ {activeTab === "agents" ? (
945
+ <AgentsAccountTab focused={focused} width={contentWidth} />
946
+ ) : null}
930
947
  {activeTab === "advanced" ? (
931
948
  <>
949
+ <Box ref={fieldNodeRef("assistantsAction")} flexDirection="row" gap={1} alignItems="center">
950
+ {/* The only label on the tab, so it takes the room it needs, with the terminal's "> " marker. */}
951
+ <FieldLabel label={t("Assistants (MCP)")} active={activeField === "assistantsAction"} width={displayWidth(t("Assistants (MCP)")) + 3} />
952
+ <Button
953
+ label={t("Connect an AI Assistant")}
954
+ active={activeField === "assistantsAction"}
955
+ onPress={openAssistants}
956
+ disabled={!!busy}
957
+ />
958
+ </Box>
932
959
  <Box flexDirection="row" gap={1}>
933
960
  <Button
934
961
  label={t("Change Password")}
@@ -1,6 +1,7 @@
1
1
  import type { DataProvider } from "../../../types/data-provider";
2
2
  import type { Quote } from "../../../types/financials";
3
3
  import { getActiveQuoteDisplay } from "../../../market-data/market/status";
4
+ import { isQuoteStaleForCurrentSession } from "../../../market-data/quotes/freshness";
4
5
  import type { AlertRule } from "./types";
5
6
 
6
7
  export function normalizeAlertSymbol(value: string | null | undefined): string {
@@ -21,6 +22,8 @@ export async function resolveAlertQuote(
21
22
  exchange = "",
22
23
  ): Promise<Quote> {
23
24
  const quote = await marketData.getQuote(symbol, exchange);
25
+ // A last known price is not the price now: it must neither trigger an alert nor set its baseline.
26
+ if (isQuoteStaleForCurrentSession(quote)) throw new Error(`The quote for "${symbol}" is stale.`);
24
27
  const display = getActiveQuoteDisplay(quote);
25
28
  if (!display || !Number.isFinite(display.price)) {
26
29
  throw new Error(`No quote found for "${symbol}".`);
@@ -12,6 +12,7 @@ import { instrumentFromTicker, type ChartRequest, type TickerInstrumentOptions }
12
12
  import { buildChartKey } from "../../../market-data/selectors";
13
13
  import {
14
14
  resolvePortfolioAccountMetrics,
15
+ resolvePortfolioLeverage,
15
16
  resolvePortfolioMarketValue,
16
17
  resolvePortfolioNetLiquidation,
17
18
  } from "../portfolio-list/account-metrics";
@@ -69,8 +70,8 @@ function formatAccountFreshness(account: ResolvedPortfolioAccountState["account"
69
70
  }
70
71
 
71
72
  function formatMarginLeverage(netLiquidation: number | undefined, totalMarketValue: number): string | null {
72
- if (!isFiniteNumber(netLiquidation) || netLiquidation <= 0 || !isFiniteNumber(totalMarketValue) || totalMarketValue < 0) return null;
73
- return `${(totalMarketValue / netLiquidation).toFixed(1)}x`;
73
+ const leverage = resolvePortfolioLeverage(totalMarketValue, netLiquidation);
74
+ return leverage == null ? null : `${leverage.toFixed(1)}x`;
74
75
  }
75
76
 
76
77
  export function buildPortfolioChartTargets(
@@ -205,6 +206,7 @@ export function buildPortfolioBetaResult(portfolio: PortfolioReturnSeriesResult,
205
206
 
206
207
  export function buildAnalyticsSummaryRows({
207
208
  accountState,
209
+ activePortfolio,
208
210
  brokerPerformance,
209
211
  portfolioStats,
210
212
  convertAccountValue = (value) => value,
@@ -232,6 +234,8 @@ export function buildAnalyticsSummaryRows({
232
234
  const totalMarketValue = resolvePortfolioMarketValue(portfolioStats, account, convertAccountValue, basis);
233
235
  const netLiquidation = resolvePortfolioNetLiquidation(portfolioStats, account, convertAccountValue, basis);
234
236
  const hasMarketValue = portfolioStats.hasPositions || isFiniteNumber(account?.grossPositionValue);
237
+ // As in the portfolio header: a broker account is worth its equity, and its positions alone are its gross.
238
+ const broker = !!account || !!activePortfolio?.brokerInstanceId;
235
239
 
236
240
  if (portfolioStats.unavailableConversions?.length || (account && !Number.isFinite(convertAccountValue(1)))) {
237
241
  rows.push({
@@ -241,19 +245,19 @@ export function buildAnalyticsSummaryRows({
241
245
  });
242
246
  }
243
247
 
244
- if (netLiquidation != null) {
248
+ if (netLiquidation != null || (broker && hasMarketValue)) {
245
249
  rows.push({
246
250
  id: "net-liquidation",
247
251
  label: "Net Liq",
248
- value: money(netLiquidation),
249
- color: colors.text,
252
+ value: money(netLiquidation ?? Number.NaN),
253
+ color: netLiquidation != null ? colors.text : colors.textDim,
250
254
  });
251
255
  }
252
256
 
253
257
  if (hasMarketValue) {
254
258
  rows.push({
255
259
  id: "total-value",
256
- label: "Val",
260
+ label: broker || portfolioStats.hasShorts ? "Gross" : "Val",
257
261
  value: money(totalMarketValue),
258
262
  color: colors.text,
259
263
  });
@@ -11,6 +11,7 @@ import {
11
11
  riskCoverageShortfall,
12
12
  riskCoverageText,
13
13
  RISK_VIEWS,
14
+ riskEvidence,
14
15
  riskPercentile,
15
16
  riskValue,
16
17
  type PortfolioRiskModel,
@@ -39,7 +40,8 @@ function displayRow(row: RiskDisplayRow) {
39
40
  value: riskValue(row),
40
41
  percentile: riskPercentile(row),
41
42
  asOf: row.asOf?.slice(0, 10) ?? null,
42
- detail: row.detail,
43
+ detail: riskEvidence(row),
44
+ ...(row.amount != null ? { amount: row.amount, currency: row.amountCurrency ?? null } : {}),
43
45
  };
44
46
  }
45
47
 
@@ -1,6 +1,6 @@
1
1
  import { portfolioOptionGreeks } from "./risk-options";
2
2
  import type { Portfolio, TickerRecord } from "../../../types/ticker";
3
- import { formatNumber } from "../../../utils/format";
3
+ import { formatCurrency, formatNumber } from "../../../utils/format";
4
4
  import { resolveCurrencyUnit } from "../../../utils/currency-units";
5
5
  import {
6
6
  getPortfolioPositionMetrics,
@@ -97,7 +97,13 @@ export interface RiskDisplayRow {
97
97
  interaction?: number;
98
98
  /** Holdings rows only: why the holding is outside the covered basket. */
99
99
  leftOut?: string;
100
+ /** VaR and expected shortfall only: the loss `value` is on the covered basket's current value (`amountBase`), in `amountCurrency`. */
101
+ amount?: number;
102
+ amountCurrency?: string;
103
+ amountBase?: number;
100
104
  }
105
+ /** The tail-loss rows, which also read in money on the covered basket. */
106
+ const TAIL_ROW_IDS: ReadonlySet<string> = new Set(["var", "es"]);
101
107
  /**
102
108
  * Below half of market value the qualifying holdings are not most of the
103
109
  * account, so a basket estimate would mostly describe something other than it;
@@ -565,12 +571,19 @@ export function buildPortfolioRisk(
565
571
  : null;
566
572
  warnings.push(...(greeks?.warnings ?? []));
567
573
  const rows: Record<RiskView, RiskDisplayRow[]> = {
568
- risk: metrics.map((row) => ({
569
- ...row,
570
- percentile: row.rank.percentile,
571
- // The method (price returns at fixed current weights) is in docs; the footer keeps room for coverage.
572
- detail: `${row.samples} sessions`,
573
- })),
574
+ risk: metrics.map((row) => {
575
+ // VaR and expected shortfall are a one-day loss on the covered basket's current value, in the portfolio currency.
576
+ const tail = TAIL_ROW_IDS.has(row.id) && row.value != null && coverage.coveredValue > 0;
577
+ return {
578
+ ...row,
579
+ percentile: row.rank.percentile,
580
+ // The method (price returns at fixed current weights) is in docs; the footer keeps room for coverage.
581
+ detail: `${row.samples} sessions`,
582
+ ...(tail
583
+ ? { amount: (row.value! / 100) * coverage.coveredValue, amountCurrency: coverage.currency, amountBase: coverage.coveredValue }
584
+ : {}),
585
+ };
586
+ }),
574
587
  factors: factors.map((row) => ({
575
588
  ...row,
576
589
  percentile: row.rank.percentile,
@@ -770,6 +783,22 @@ export const riskValue = (row: RiskDisplayRow) =>
770
783
  ? "left out"
771
784
  : row.value == null
772
785
  ? "--"
773
- : `${row.value.toFixed(2)}${row.unit === "%" ? "%" : ` ${row.unit}`}`;
786
+ : `${row.value.toFixed(2)}${row.unit === "%" ? "%" : ` ${row.unit}`}${riskAmount(row)}`;
787
+ /** "220,476 USD basket": the value a tail loss in money is taken on, or null for a row without one. */
788
+ export function riskAmountBase(row: Pick<RiskDisplayRow, "amountBase" | "amountCurrency">): string | null {
789
+ return row.amountBase != null && Number.isFinite(row.amountBase) && row.amountCurrency
790
+ ? `${formatNumber(row.amountBase, 0)} ${row.amountCurrency} basket`
791
+ : null;
792
+ }
793
+ /** A row's evidence with the basket a tail loss in money is taken on. */
794
+ export function riskEvidence(row: RiskDisplayRow): string {
795
+ const base = riskAmountBase(row);
796
+ return base ? `${row.detail}; of ${base}` : row.detail;
797
+ }
798
+ /** " ($5,735)": a tail loss in money beside its percent. */
799
+ function riskAmount(row: RiskDisplayRow): string {
800
+ if (row.amount == null || !Number.isFinite(row.amount) || !row.amountCurrency) return "";
801
+ return ` (${formatCurrency(row.amount, row.amountCurrency, 0)})`;
802
+ }
774
803
  export const riskPercentile = (row: RiskDisplayRow) =>
775
804
  row.percentile == null ? "--" : String(Math.round(row.percentile));
@@ -56,6 +56,8 @@ import {
56
56
  riskCoverageShortfall,
57
57
  riskCoverageText,
58
58
  riskPercentile,
59
+ riskAmountBase,
60
+ riskEvidence,
59
61
  riskValue,
60
62
  RISK_VIEWS,
61
63
  type PortfolioRiskModel,
@@ -178,7 +180,7 @@ function RiskDetail({
178
180
  detail: row.asOf?.slice(0, 10) ?? "Source date unavailable",
179
181
  },
180
182
  ...(row.percentile != null ? [{ id: "percentile", label: "Pctl 1Y", value: riskPercentile(row) }] : []),
181
- { id: "evidence", label: "Evidence", value: row.detail, wide: true },
183
+ { id: "evidence", label: "Evidence", value: riskEvidence(row), wide: true },
182
184
  ];
183
185
  const statRows = statGridRows(items, width);
184
186
  const points = (row.history ?? []).map((point, index) => ({
@@ -458,7 +460,8 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
458
460
  : [
459
461
  ...(basketBody ? [] : [
460
462
  ...(asOfDate && sharedEvidence ? [sharedEvidence] : []),
461
- model.portfolio.currency,
463
+ // The value the tail losses in money are taken on names the currency too.
464
+ rows.map(riskAmountBase).find(Boolean) ?? model.portfolio.currency,
462
465
  ...(asOfDate ? [asOfDate] : []),
463
466
  ...(EVIDENCE_VIEWS.has(view) && model.evidence ? [model.evidence.source] : []),
464
467
  ]),
@@ -576,7 +579,8 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
576
579
  {
577
580
  id: "value",
578
581
  label: valueHeader,
579
- width: sharedUnit || attributionParts ? Math.max(9, valueHeader.length) : 16,
582
+ // A tail loss reads in money beside its percent: 2.60% ($5,735).
583
+ width: sharedUnit || attributionParts ? Math.max(9, valueHeader.length) : rows.some((row) => row.amount != null) ? 18 : 16,
580
584
  align: "right",
581
585
  },
582
586
  ...(attributionParts
@@ -601,7 +605,7 @@ export function PortfolioRiskPane({ focused, width, height }: PaneProps) {
601
605
  ]
602
606
  : []),
603
607
  ],
604
- [width, view, valueHeader, sharedUnit, attributionParts, sharedEvidence, showPercentile, sharedDate],
608
+ [width, view, valueHeader, sharedUnit, attributionParts, sharedEvidence, showPercentile, sharedDate, rows],
605
609
  );
606
610
  const selectView = (value: string) => {
607
611
  setView(value);
@@ -58,7 +58,7 @@ function Evidence({ row, data, width, height }: { row: DetailRow; data: Attentio
58
58
  const sameDay = row.evidence.periodStart.slice(0, 10) === row.evidence.periodEnd.slice(0, 10);
59
59
  const period = sameDay ? `${hour(row.evidence.periodStart)} to ${row.evidence.periodEnd.slice(11, 16)} UTC` : `${hour(row.evidence.periodStart)} to ${date(row.evidence.periodEnd)}`;
60
60
  const latest = point?.bucketStart === row.history.at(-1)?.bucketStart;
61
- return <ScrollBox width={width} height={desktop ? undefined : height} flexGrow={1} flexBasis={0} contentOptions={{ paddingX: 1 }}>
61
+ return <ScrollBox scrollY width={width} height={desktop ? undefined : height} flexGrow={1} flexBasis={0} contentOptions={{ paddingX: 1 }}>
62
62
  <KeyValueRow label="Listing" value={row.symbol} detail={row.name ?? undefined} labelWidth={20} />
63
63
  <SectionHeading title="Publication" />
64
64
  <KeyValueRow label="Research hours" value={number(row.researchUnits)} detail={`rounded to ${data.privacy.rounding}`} labelWidth={20} />
@@ -146,7 +146,7 @@ export function BacktestPane({ width, height, focused }: PaneProps) {
146
146
  }), [history.loading, result, view, paneId, symbol]);
147
147
 
148
148
  const { strip: tabStrip, rows: tabRows } = usePaneTabs(symbol ? { tabs: TABS, activeValue: view, onSelect: setView, focused, dense: true } : null);
149
- if (!symbol) return <EmptyState title="Choose a ticker." hint="Open BT with a symbol, for example BT AAPL." />;
149
+ if (!symbol) return <EmptyState title="Select a ticker." hint="Open BT with a symbol, for example BT AAPL." />;
150
150
  const bodyHeight = Math.max(4, height - 1 - tabRows);
151
151
  const wide = width - SUMMARY_WIDTH - 1 >= MIN_WIDE_CHART_COLS;
152
152
  const summaryTable = (tableWidth: number, tableHeight: number, before?: ReactNode) => (
@@ -14,6 +14,7 @@ import { formatCurrency, truncateToDisplayWidth } from "../../../utils/format";
14
14
  import { t, tf } from "../../../i18n";
15
15
  import type { BrokerProfileRow } from "./model";
16
16
  import type { BrokerManagerMessage } from "./pane-actions";
17
+ import { signInEndNote } from "./sign-in-end";
17
18
  import { stateColor } from "./table";
18
19
 
19
20
  export type BrokerEditKey = "label" | "enabled" | string;
@@ -277,6 +278,9 @@ export function BrokerDetailContent({
277
278
  >
278
279
  {detailStatusMessage}
279
280
  </Text>
281
+ {row.signInEndsAt !== null && (
282
+ <Text fg={colors.warning} width={width} wrapText>{signInEndNote(row.signInEndsAt)}</Text>
283
+ )}
280
284
  <Text fg={colors.textMuted}>{tf("Last sync {time}", { time: formatRelativeAge(row.lastSyncedAt) })}</Text>
281
285
  <Text fg={colors.textMuted}>{tf("Status updated {time}", { time: formatRelativeAge(row.updatedAt) })}</Text>
282
286
  <Box height={1} />
@@ -37,6 +37,7 @@ import {
37
37
  type BrokerProfileRow,
38
38
  } from "./model";
39
39
  import { useBrokerManagerActions } from "./pane-actions";
40
+ import { useSignInEnds } from "./sign-in-end";
40
41
  import {
41
42
  buildBrokerColumns,
42
43
  renderBrokerCell,
@@ -78,9 +79,10 @@ export function BrokersPane({ focused, width, height }: PaneProps) {
78
79
 
79
80
  // Signed-in profiles take their broker's name from the connector list.
80
81
  const signedInBrokers = useSyncExternalStore(subscribeSignedInBrokers, getSignedInBrokers, getSignedInBrokers);
82
+ const { ends: signInEnds, reload: reloadSignInEnds } = useSignInEnds(config.brokerInstances);
81
83
  const rows = useMemo(
82
- () => buildBrokerProfileRows(config, adapters, brokerAccounts),
83
- [adapters, brokerAccounts, config, language, signedInBrokers, statusVersion],
84
+ () => buildBrokerProfileRows(config, adapters, brokerAccounts, signInEnds),
85
+ [adapters, brokerAccounts, config, language, signInEnds, signedInBrokers, statusVersion],
84
86
  );
85
87
  const selectedIndex = Math.max(0, rows.findIndex((row) => row.id === selectedId));
86
88
  const selectedRow = rows[selectedIndex] ?? null;
@@ -107,9 +109,11 @@ export function BrokersPane({ focused, width, height }: PaneProps) {
107
109
  if (!editKeys.includes(activeEditKey)) setActiveEditKey(editKeys[0] ?? "label");
108
110
  }, [activeEditKey, editDraft, editKeys]);
109
111
 
112
+ // After a connect or sync, which can renew a sign-in or find it ended.
110
113
  const refreshStatuses = useCallback(() => {
111
114
  setStatusVersion((version) => version + 1);
112
- }, []);
115
+ reloadSignInEnds();
116
+ }, [reloadSignInEnds]);
113
117
 
114
118
  const {
115
119
  busy,
@@ -3,6 +3,7 @@ import type { AppConfig, BrokerInstanceConfig } from "../../../types/config";
3
3
  import type { BrokerAccount } from "../../../types/trading";
4
4
  import { formatCurrency } from "../../../utils/format";
5
5
  import { t } from "../../../i18n";
6
+ import type { SignInEnds } from "./sign-in-end";
6
7
 
7
8
  export type BrokerDisplayState =
8
9
  | "disabled"
@@ -24,6 +25,8 @@ export interface BrokerProfileRow {
24
25
  accountCount: number;
25
26
  accountSummary: string;
26
27
  accountIds: string[];
28
+ /** When the broker ends this profile's sign-in, once Gloom says it is time to connect again. */
29
+ signInEndsAt: number | null;
27
30
  instance: BrokerInstanceConfig;
28
31
  adapter: BrokerAdapter | null;
29
32
  }
@@ -107,6 +110,7 @@ export function buildBrokerProfileRows(
107
110
  config: AppConfig,
108
111
  adapters: ReadonlyMap<string, BrokerAdapter | null>,
109
112
  brokerAccounts: Record<string, BrokerAccount[]>,
113
+ signInEnds: SignInEnds = new Map(),
110
114
  ): BrokerProfileRow[] {
111
115
  return config.brokerInstances.map((instance) => {
112
116
  const adapter = adapters.get(instance.brokerType) ?? null;
@@ -139,6 +143,10 @@ export function buildBrokerProfileRows(
139
143
  accountCount: accounts.length,
140
144
  accountSummary: summarizeBrokerAccounts(accounts),
141
145
  accountIds: accounts.map((account) => account.accountId),
146
+ // Say nothing next to a failure or a profile that does not connect.
147
+ signInEndsAt: state.state === "error" || state.state === "disabled" || state.state === "unavailable"
148
+ ? null
149
+ : signInEnds.get(instance.id) ?? null,
142
150
  instance,
143
151
  adapter,
144
152
  };
@@ -114,8 +114,14 @@ export function useBrokerManagerActions({
114
114
  const broker = signedInBrokerForProfile(selectedRow.instance, selectedRow.brokerName);
115
115
  try {
116
116
  setBusy(t("Connecting…"));
117
- if (!await requestBrokerSignIn(broker)) {
118
- setMessage(infoMessage(tf("{broker} was not connected.", { broker: broker.name })));
117
+ // A connected broker is connected again only by a new sign-in: this renews it before the broker ends it.
118
+ // A row that says when the sign-in ends is renewable before this session's first sync too.
119
+ const renew = selectedRow.state === "connected" || selectedRow.signInEndsAt !== null;
120
+ if (!await requestBrokerSignIn(broker, { renew })) {
121
+ // Backing out of a renewal leaves the broker connected, for now.
122
+ setMessage(infoMessage(renew
123
+ ? tf("{broker} sign-in was not renewed.", { broker: broker.name })
124
+ : tf("{broker} was not connected.", { broker: broker.name })));
119
125
  return;
120
126
  }
121
127
  await syncBrokerInstance(selectedRow.id);