@opengeni/react 1.0.1 → 1.0.2-canary.37202341988001

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 (632) hide show
  1. package/README.md +660 -21
  2. package/dist/accounts.d.ts +64 -0
  3. package/dist/accounts.js +822 -0
  4. package/dist/accounts.js.map +1 -0
  5. package/dist/approvals.d.ts +2 -1
  6. package/dist/artifacts-document.js +3 -2
  7. package/dist/artifacts-presentation.js +3 -2
  8. package/dist/artifacts-spreadsheet.js +2 -2
  9. package/dist/artifacts.d.ts +4 -1
  10. package/dist/artifacts.js +518 -42
  11. package/dist/artifacts.js.map +1 -1
  12. package/dist/browser-viewer-SHYKQRNX.js +16 -0
  13. package/dist/capability-catalog-row.d.ts +20 -0
  14. package/dist/capability-logo-fallback.d.ts +3 -0
  15. package/dist/chat-interactive-block-UGBTNB3L.js +11 -0
  16. package/dist/chat-interactive-block-UGBTNB3L.js.map +1 -0
  17. package/dist/chunk-26ZNDDLZ.js +453 -0
  18. package/dist/chunk-26ZNDDLZ.js.map +1 -0
  19. package/dist/chunk-3CTRFMAK.js +359 -0
  20. package/dist/chunk-3CTRFMAK.js.map +1 -0
  21. package/dist/chunk-447JLCBU.js +209 -0
  22. package/dist/chunk-447JLCBU.js.map +1 -0
  23. package/dist/chunk-4RVYOY4O.js +379 -0
  24. package/dist/chunk-4RVYOY4O.js.map +1 -0
  25. package/dist/chunk-5CVXZA3Y.js +292 -0
  26. package/dist/chunk-5CVXZA3Y.js.map +1 -0
  27. package/dist/chunk-5J4FOW6Q.js +108 -0
  28. package/dist/chunk-5J4FOW6Q.js.map +1 -0
  29. package/dist/chunk-742CZ3L3.js +481 -0
  30. package/dist/chunk-742CZ3L3.js.map +1 -0
  31. package/dist/chunk-A2PI6UMY.js +41 -0
  32. package/dist/chunk-A2PI6UMY.js.map +1 -0
  33. package/dist/{chunk-7WCLAEFV.js → chunk-A4UJB2MM.js} +37 -27
  34. package/dist/chunk-A4UJB2MM.js.map +1 -0
  35. package/dist/chunk-B3V6TG77.js +41 -0
  36. package/dist/chunk-B3V6TG77.js.map +1 -0
  37. package/dist/chunk-BRM6FVZA.js +302 -0
  38. package/dist/chunk-BRM6FVZA.js.map +1 -0
  39. package/dist/chunk-C5X5AJXK.js +182 -0
  40. package/dist/chunk-C5X5AJXK.js.map +1 -0
  41. package/dist/{chunk-6KLX2RH5.js → chunk-DO544OSV.js} +1075 -79
  42. package/dist/chunk-DO544OSV.js.map +1 -0
  43. package/dist/{chunk-R3AZOAB5.js → chunk-DYWBUX5Q.js} +29 -123
  44. package/dist/chunk-DYWBUX5Q.js.map +1 -0
  45. package/dist/{chunk-HA46CYQK.js → chunk-EESD3PY3.js} +792 -537
  46. package/dist/chunk-EESD3PY3.js.map +1 -0
  47. package/dist/{chunk-5UQPNLIO.js → chunk-EN3YTHKR.js} +4117 -3896
  48. package/dist/chunk-EN3YTHKR.js.map +1 -0
  49. package/dist/chunk-ERGYJIBB.js +21 -0
  50. package/dist/chunk-ERGYJIBB.js.map +1 -0
  51. package/dist/chunk-FZCP527Y.js +116 -0
  52. package/dist/chunk-FZCP527Y.js.map +1 -0
  53. package/dist/chunk-GIMRELBW.js +14 -0
  54. package/dist/chunk-GIMRELBW.js.map +1 -0
  55. package/dist/chunk-GYVRFZDY.js +2733 -0
  56. package/dist/chunk-GYVRFZDY.js.map +1 -0
  57. package/dist/{chunk-LLOLUHVG.js → chunk-HDJWDK7W.js} +1358 -2746
  58. package/dist/chunk-HDJWDK7W.js.map +1 -0
  59. package/dist/chunk-I7IWRQLN.js +1608 -0
  60. package/dist/chunk-I7IWRQLN.js.map +1 -0
  61. package/dist/chunk-IT4BJUGF.js +13055 -0
  62. package/dist/chunk-IT4BJUGF.js.map +1 -0
  63. package/dist/chunk-IWIDV7SB.js +1 -0
  64. package/dist/chunk-IWIDV7SB.js.map +1 -0
  65. package/dist/chunk-J2QWVSQY.js +312 -0
  66. package/dist/chunk-J2QWVSQY.js.map +1 -0
  67. package/dist/chunk-J6DBS4T3.js +371 -0
  68. package/dist/chunk-J6DBS4T3.js.map +1 -0
  69. package/dist/{chunk-B6WPZSTT.js → chunk-JIQYUGVL.js} +1331 -1163
  70. package/dist/chunk-JIQYUGVL.js.map +1 -0
  71. package/dist/chunk-JT4JW7BC.js +54 -0
  72. package/dist/chunk-JT4JW7BC.js.map +1 -0
  73. package/dist/chunk-K3HQAKQX.js +24 -0
  74. package/dist/chunk-K3HQAKQX.js.map +1 -0
  75. package/dist/{chunk-PEI6RANI.js → chunk-PNY3JKBJ.js} +309 -74
  76. package/dist/chunk-PNY3JKBJ.js.map +1 -0
  77. package/dist/chunk-RHREZ7BO.js +11 -0
  78. package/dist/chunk-RHREZ7BO.js.map +1 -0
  79. package/dist/chunk-RLUSC2WA.js +299 -0
  80. package/dist/chunk-RLUSC2WA.js.map +1 -0
  81. package/dist/{chunk-IGC6A2ZI.js → chunk-RSI7XYXL.js} +777 -246
  82. package/dist/chunk-RSI7XYXL.js.map +1 -0
  83. package/dist/chunk-SCOTPE2I.js +589 -0
  84. package/dist/chunk-SCOTPE2I.js.map +1 -0
  85. package/dist/{chunk-C5ZPSSS5.js → chunk-TKOTKRWY.js} +5 -4
  86. package/dist/{chunk-C5ZPSSS5.js.map → chunk-TKOTKRWY.js.map} +1 -1
  87. package/dist/chunk-TMYGL6X5.js +742 -0
  88. package/dist/chunk-TMYGL6X5.js.map +1 -0
  89. package/dist/chunk-UGLEYEMP.js +33 -0
  90. package/dist/chunk-UGLEYEMP.js.map +1 -0
  91. package/dist/chunk-UK4TINKT.js +27 -0
  92. package/dist/chunk-UK4TINKT.js.map +1 -0
  93. package/dist/chunk-UTRTG6LH.js +220 -0
  94. package/dist/chunk-UTRTG6LH.js.map +1 -0
  95. package/dist/chunk-WEHMPOXK.js +39 -0
  96. package/dist/chunk-WEHMPOXK.js.map +1 -0
  97. package/dist/chunk-WGXVEJO5.js +319 -0
  98. package/dist/chunk-WGXVEJO5.js.map +1 -0
  99. package/dist/chunk-WRHIZYZF.js +988 -0
  100. package/dist/chunk-WRHIZYZF.js.map +1 -0
  101. package/dist/{chunk-HUIKIDM5.js → chunk-WUISZ3WU.js} +223 -115
  102. package/dist/chunk-WUISZ3WU.js.map +1 -0
  103. package/dist/chunk-XCHWDJ22.js +107 -0
  104. package/dist/chunk-XCHWDJ22.js.map +1 -0
  105. package/dist/{chunk-KCGXPP5V.js → chunk-Y2PRMYLC.js} +11 -6
  106. package/dist/chunk-Y2PRMYLC.js.map +1 -0
  107. package/dist/chunk-YBXQDQFX.js +1772 -0
  108. package/dist/chunk-YBXQDQFX.js.map +1 -0
  109. package/dist/chunk-YM2DE7BQ.js +3124 -0
  110. package/dist/chunk-YM2DE7BQ.js.map +1 -0
  111. package/dist/{chunk-KZ73RL76.js → chunk-YQEAA6LT.js} +7 -3
  112. package/dist/chunk-YQEAA6LT.js.map +1 -0
  113. package/dist/chunk-YV4L3ILF.js +9 -0
  114. package/dist/chunk-YV4L3ILF.js.map +1 -0
  115. package/dist/chunk-ZYRGEKWS.js +216 -0
  116. package/dist/chunk-ZYRGEKWS.js.map +1 -0
  117. package/dist/client.d.ts +7 -4
  118. package/dist/clipboard.js +1 -0
  119. package/dist/commands/types.d.ts +2 -0
  120. package/dist/components/anthropic-mark.d.ts +3 -0
  121. package/dist/components/approval-surface.d.ts +15 -0
  122. package/dist/components/artifacts/artifact-chrome.d.ts +120 -0
  123. package/dist/components/artifacts/artifact-sandbox.d.ts +27 -0
  124. package/dist/components/artifacts/artifact-surface.d.ts +3 -1
  125. package/dist/components/artifacts/chat-interactive-block.d.ts +29 -0
  126. package/dist/components/artifacts/deferred-chat-media.d.ts +15 -0
  127. package/dist/components/artifacts/document-editor.d.ts +4 -2
  128. package/dist/components/artifacts/editable-artifact-ui.d.ts +2 -1
  129. package/dist/components/artifacts/editable-artifact-view.d.ts +46 -0
  130. package/dist/components/artifacts/editable-document.d.ts +1 -1
  131. package/dist/components/artifacts/editable-presentation.d.ts +2 -2
  132. package/dist/components/artifacts/editable-spreadsheet.d.ts +2 -1
  133. package/dist/components/artifacts/index.d.ts +8 -1
  134. package/dist/components/artifacts/inline-html-document.d.ts +2 -0
  135. package/dist/components/artifacts/latest-task-runner.d.ts +15 -0
  136. package/dist/components/artifacts/presentation-editor.d.ts +6 -2
  137. package/dist/components/artifacts/published-html-artifact-frame.d.ts +48 -3
  138. package/dist/components/artifacts/session-artifact-viewer.d.ts +39 -0
  139. package/dist/components/artifacts/site-view.d.ts +23 -0
  140. package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
  141. package/dist/components/artifacts/visualization-assets.gen.d.ts +2 -0
  142. package/dist/components/browser-select-control.d.ts +16 -0
  143. package/dist/components/browser-viewer.d.ts +10 -1
  144. package/dist/components/chat-composer.d.ts +21 -3
  145. package/dist/components/child-session-link.d.ts +5 -0
  146. package/dist/components/claude-mark.d.ts +3 -0
  147. package/dist/components/code-editor.d.ts +1 -4
  148. package/dist/components/composer.d.ts +45 -4
  149. package/dist/components/computer-viewer.d.ts +4 -1
  150. package/dist/components/copy-button.d.ts +3 -1
  151. package/dist/components/desktop-viewer.d.ts +1 -1
  152. package/dist/components/enrollment-consent.d.ts +6 -3
  153. package/dist/components/file-browser.d.ts +10 -1
  154. package/dist/components/fleet-tile.d.ts +2 -2
  155. package/dist/components/grok-mark.d.ts +26 -0
  156. package/dist/components/human-input-form.d.ts +21 -2
  157. package/dist/components/human-input-surface.d.ts +6 -3
  158. package/dist/components/machine-card.d.ts +4 -1
  159. package/dist/components/machines/machine-detail.d.ts +4 -1
  160. package/dist/components/machines/machine-operation-policy.d.ts +11 -0
  161. package/dist/components/machines-dashboard.d.ts +3 -1
  162. package/dist/components/markdown-table-layout.d.ts +12 -0
  163. package/dist/components/markdown.d.ts +47 -3
  164. package/dist/components/message-timeline.d.ts +72 -9
  165. package/dist/components/model-mark.d.ts +29 -0
  166. package/dist/components/model-policy-picker-menu.d.ts +9 -0
  167. package/dist/components/model-policy-picker.d.ts +62 -13
  168. package/dist/components/open-geni-chat.d.ts +58 -0
  169. package/dist/components/open-geni-links.d.ts +68 -0
  170. package/dist/components/openrouter-mark.d.ts +6 -0
  171. package/dist/components/pierre-file.d.ts +1 -1
  172. package/dist/components/preview-loading.d.ts +2 -0
  173. package/dist/components/queue-item-content.d.ts +9 -0
  174. package/dist/components/sandbox-files.d.ts +13 -2
  175. package/dist/components/sandbox-workspace.d.ts +43 -3
  176. package/dist/components/session-capability-frame.d.ts +35 -0
  177. package/dist/components/session-chrome.d.ts +66 -8
  178. package/dist/components/session-commands-panel.d.ts +6 -0
  179. package/dist/components/session-commands.d.ts +6 -0
  180. package/dist/components/session-connection-request.d.ts +8 -0
  181. package/dist/components/session-conversation.d.ts +89 -0
  182. package/dist/components/session-list.d.ts +36 -0
  183. package/dist/components/session-mcp-capability-card.d.ts +41 -0
  184. package/dist/components/session-proxy-scope.d.ts +21 -0
  185. package/dist/components/timeline-anchor.d.ts +24 -0
  186. package/dist/components/timeline-annotation-chrome.d.ts +25 -0
  187. package/dist/components/timeline-annotation-layout.d.ts +42 -0
  188. package/dist/components/timeline-annotation-markers.d.ts +8 -0
  189. package/dist/components/timeline-annotation-reveal-context.d.ts +4 -0
  190. package/dist/components/timeline-annotation-shared.d.ts +31 -0
  191. package/dist/components/timeline-annotations-dialog.d.ts +3 -2
  192. package/dist/components/timeline-annotations.d.ts +13 -3
  193. package/dist/components/timeline-reading-progress.d.ts +11 -0
  194. package/dist/components/timeline-search.d.ts +34 -0
  195. package/dist/components/timeline-settlement.d.ts +16 -0
  196. package/dist/components/tip-follow.d.ts +16 -0
  197. package/dist/components/usage-limit-notice.d.ts +49 -0
  198. package/dist/components/usage-member-list.d.ts +91 -0
  199. package/dist/components/usage-meter.d.ts +73 -0
  200. package/dist/components/use-viewer-menu-dismiss.d.ts +2 -0
  201. package/dist/components/user-message-body.d.ts +10 -1
  202. package/dist/components/workspace-dock.d.ts +6 -2
  203. package/dist/composer.d.ts +5 -4
  204. package/dist/composer.js +15 -9
  205. package/dist/computer-viewer-6SRGWRUX.js +19 -0
  206. package/dist/computer-viewer-6SRGWRUX.js.map +1 -0
  207. package/dist/connect-account-identity.d.ts +8 -0
  208. package/dist/connect-accounts.d.ts +15 -0
  209. package/dist/connect-chooser.d.ts +18 -0
  210. package/dist/connect-panel.d.ts +18 -0
  211. package/dist/connect-setup.d.ts +16 -0
  212. package/dist/connect.d.ts +21 -0
  213. package/dist/connect.js +2129 -0
  214. package/dist/connect.js.map +1 -0
  215. package/dist/connection-catalog.d.ts +38 -0
  216. package/dist/connection-discovery.d.ts +12 -0
  217. package/dist/connection-installed.d.ts +17 -0
  218. package/dist/connection-logo.d.ts +9 -0
  219. package/dist/connection-service-logo.d.ts +8 -0
  220. package/dist/connection-service-presentation.d.ts +13 -0
  221. package/dist/connection-skeleton.d.ts +5 -0
  222. package/dist/connection-type-picker.d.ts +6 -0
  223. package/dist/conversation-timeline.d.ts +5 -0
  224. package/dist/desktop.d.ts +7 -0
  225. package/dist/desktop.js +26 -0
  226. package/dist/desktop.js.map +1 -0
  227. package/dist/device-authorization.d.ts +23 -0
  228. package/dist/diffs.d.ts +8 -0
  229. package/dist/diffs.js +14 -0
  230. package/dist/diffs.js.map +1 -0
  231. package/dist/discovery-cache.d.ts +5 -0
  232. package/dist/editor.d.ts +7 -0
  233. package/dist/editor.js +21 -0
  234. package/dist/editor.js.map +1 -0
  235. package/dist/embedded-session-client.d.ts +23 -0
  236. package/dist/file-node-visibility.d.ts +15 -0
  237. package/dist/{fleet-decision-row-GGCAT4E4.js → fleet-decision-row-YZ3ZKWMM.js} +2 -1
  238. package/dist/fleet-decision-row-YZ3ZKWMM.js.map +1 -0
  239. package/dist/hooks/channel-cache.d.ts +23 -0
  240. package/dist/hooks/latest-question.d.ts +25 -0
  241. package/dist/hooks/shared-machines-list.d.ts +14 -0
  242. package/dist/hooks/use-attached-browsers.d.ts +2 -1
  243. package/dist/hooks/use-browser-frame-stream.d.ts +1 -1
  244. package/dist/hooks/use-browser-session.d.ts +12 -1
  245. package/dist/hooks/use-channels.d.ts +1 -0
  246. package/dist/hooks/use-codex-accounts.d.ts +25 -7
  247. package/dist/hooks/use-composer.d.ts +45 -9
  248. package/dist/hooks/use-computer-frame-stream.d.ts +1 -1
  249. package/dist/hooks/use-computer-input-posture.d.ts +13 -0
  250. package/dist/hooks/use-computer-session.d.ts +2 -0
  251. package/dist/hooks/use-connect.d.ts +22 -0
  252. package/dist/hooks/use-desktop-stream.d.ts +1 -1
  253. package/dist/hooks/use-file-attachments.d.ts +21 -2
  254. package/dist/hooks/use-machine-chip.d.ts +3 -3
  255. package/dist/hooks/use-machines.d.ts +19 -1
  256. package/dist/hooks/use-sandbox-files.d.ts +5 -1
  257. package/dist/hooks/use-session-background-commands.d.ts +14 -0
  258. package/dist/hooks/use-session-events.d.ts +30 -8
  259. package/dist/hooks/use-session-lineage.d.ts +4 -0
  260. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  261. package/dist/hooks/use-session.d.ts +6 -0
  262. package/dist/hooks/use-turn-queue.d.ts +12 -0
  263. package/dist/hooks/use-usage.d.ts +40 -0
  264. package/dist/hooks/use-workspace-edit.d.ts +4 -0
  265. package/dist/hooks/use-workspace-sessions.d.ts +27 -5
  266. package/dist/human-input.d.ts +3 -1
  267. package/dist/identity-link-accounts.d.ts +11 -0
  268. package/dist/identity-link-consent.d.ts +18 -0
  269. package/dist/index.d.ts +60 -17
  270. package/dist/index.js +2238 -6201
  271. package/dist/index.js.map +1 -1
  272. package/dist/interaction.d.ts +1 -1
  273. package/dist/interaction.js +35 -184
  274. package/dist/interaction.js.map +1 -1
  275. package/dist/latest-question-HTRIS4KJ.js +117 -0
  276. package/dist/latest-question-HTRIS4KJ.js.map +1 -0
  277. package/dist/lib/error-message.d.ts +7 -0
  278. package/dist/lib/format.d.ts +36 -2
  279. package/dist/lib/host-theme.d.ts +70 -0
  280. package/dist/lib/interaction-errors.d.ts +6 -0
  281. package/dist/lib/menu-styles.d.ts +22 -0
  282. package/dist/lib/pierre-diffs-loader.d.ts +20 -0
  283. package/dist/lib/workbench-peers.d.ts +36 -0
  284. package/dist/lib/xterm-theme.d.ts +7 -5
  285. package/dist/machines.d.ts +4 -2
  286. package/dist/machines.js +14 -4
  287. package/dist/markdown-table-layout-QXSLK4BY.js +106 -0
  288. package/dist/markdown-table-layout-QXSLK4BY.js.map +1 -0
  289. package/dist/mcp-connection-status.d.ts +14 -0
  290. package/dist/model-policy-picker-menu-M54ISBMN.js +14 -0
  291. package/dist/model-policy-picker-menu-M54ISBMN.js.map +1 -0
  292. package/dist/model-policy.d.ts +19 -3
  293. package/dist/model-policy.js +9 -1
  294. package/dist/older-history.d.ts +30 -0
  295. package/dist/platform-activity-row-627BFZ46.js +182 -0
  296. package/dist/platform-activity-row-627BFZ46.js.map +1 -0
  297. package/dist/plugin-details.d.ts +14 -0
  298. package/dist/plugin-discovery.d.ts +25 -0
  299. package/dist/provider.d.ts +13 -1
  300. package/dist/{queue-surface-implementation-2KVJTYXH.js → queue-surface-implementation-ZHFI5JBP.js} +131 -44
  301. package/dist/queue-surface-implementation-ZHFI5JBP.js.map +1 -0
  302. package/dist/realtime/realtime-control.d.ts +6 -3
  303. package/dist/realtime.js +133 -74
  304. package/dist/realtime.js.map +1 -1
  305. package/dist/sandbox-files-GORZOUUJ.js +20 -0
  306. package/dist/sandbox-files-GORZOUUJ.js.map +1 -0
  307. package/dist/session-auth-recommendation.d.ts +5 -0
  308. package/dist/session-capability-policy.d.ts +12 -0
  309. package/dist/session-ui.d.ts +30 -2
  310. package/dist/session-ui.js +85 -7
  311. package/dist/session-ui.js.map +1 -1
  312. package/dist/session.d.ts +8 -2
  313. package/dist/session.js +30 -19
  314. package/dist/sites-ui.d.ts +34 -0
  315. package/dist/sites.d.ts +3 -0
  316. package/dist/sites.js +11 -0
  317. package/dist/sites.js.map +1 -0
  318. package/dist/skill-discovery.d.ts +47 -0
  319. package/dist/terminal.d.ts +8 -0
  320. package/dist/terminal.js +31 -0
  321. package/dist/terminal.js.map +1 -0
  322. package/dist/timeline/activity-rail.d.ts +11 -2
  323. package/dist/timeline/allowance-exhausted-row.d.ts +24 -0
  324. package/dist/timeline/compaction-copy.d.ts +10 -0
  325. package/dist/timeline/entrance.d.ts +13 -1
  326. package/dist/timeline/genie-loading.d.ts +34 -0
  327. package/dist/timeline/index.d.ts +6 -2
  328. package/dist/timeline/knowledge-receipt.d.ts +17 -0
  329. package/dist/timeline/platform-activity-row.d.ts +14 -0
  330. package/dist/timeline/presented-image.d.ts +6 -0
  331. package/dist/timeline/projection.d.ts +31 -2
  332. package/dist/timeline/registry.d.ts +2 -0
  333. package/dist/timeline/retained-image.d.ts +18 -0
  334. package/dist/timeline/rolling-activity.d.ts +11 -0
  335. package/dist/timeline/screenshot-lightbox.d.ts +16 -1
  336. package/dist/timeline/shared.d.ts +8 -3
  337. package/dist/timeline/startup-preference.d.ts +3 -0
  338. package/dist/timeline/startup-timings.d.ts +4 -0
  339. package/dist/timeline/tool-display-name.d.ts +2 -1
  340. package/dist/timeline/turn-summary.d.ts +34 -3
  341. package/dist/timeline/types.d.ts +147 -7
  342. package/dist/timeline-annotation-markers-GNDHTGBD.js +165 -0
  343. package/dist/timeline-annotation-markers-GNDHTGBD.js.map +1 -0
  344. package/dist/timeline-annotation-selection-DMLMM2TX.js +241 -0
  345. package/dist/timeline-annotation-selection-DMLMM2TX.js.map +1 -0
  346. package/dist/timeline-annotations-dialog-ELRDBHTS.js +273 -0
  347. package/dist/timeline-annotations-dialog-ELRDBHTS.js.map +1 -0
  348. package/dist/types/machines.d.ts +2 -2
  349. package/dist/usage/allowance-copy.d.ts +25 -0
  350. package/dist/usage/summary.d.ts +35 -0
  351. package/dist/usage.d.ts +13 -0
  352. package/dist/usage.js +1074 -0
  353. package/dist/usage.js.map +1 -0
  354. package/package.json +44 -4
  355. package/src/accounts.tsx +1072 -0
  356. package/src/approvals.ts +8 -1
  357. package/src/artifacts.ts +42 -0
  358. package/src/assets/README.md +8 -0
  359. package/src/assets/gmail.ico +0 -0
  360. package/src/assets/outlook.ico +0 -0
  361. package/src/capability-catalog-row.tsx +94 -0
  362. package/src/capability-logo-fallback.ts +22 -0
  363. package/src/client.ts +22 -13
  364. package/src/commands/registry.ts +18 -15
  365. package/src/commands/types.ts +2 -0
  366. package/src/components/anthropic-mark.tsx +10 -0
  367. package/src/components/approval-surface.tsx +171 -37
  368. package/src/components/artifacts/artifact-chrome.tsx +413 -0
  369. package/src/components/artifacts/artifact-sandbox.tsx +185 -0
  370. package/src/components/artifacts/artifact-surface.tsx +39 -19
  371. package/src/components/artifacts/chat-interactive-block.tsx +260 -0
  372. package/src/components/artifacts/deferred-chat-media.tsx +67 -0
  373. package/src/components/artifacts/document-editor.tsx +141 -16
  374. package/src/components/artifacts/editable-artifact-ui.tsx +4 -2
  375. package/src/components/artifacts/editable-artifact-view.tsx +187 -0
  376. package/src/components/artifacts/editable-artifact-workbench.tsx +5 -2
  377. package/src/components/artifacts/editable-document.tsx +37 -36
  378. package/src/components/artifacts/editable-presentation.tsx +36 -18
  379. package/src/components/artifacts/editable-spreadsheet.tsx +5 -2
  380. package/src/components/artifacts/index.ts +44 -0
  381. package/src/components/artifacts/inline-html-document.ts +24 -0
  382. package/src/components/artifacts/latest-task-runner.ts +49 -0
  383. package/src/components/artifacts/presentation-editor.tsx +323 -67
  384. package/src/components/artifacts/published-html-artifact-frame.tsx +338 -5
  385. package/src/components/artifacts/session-artifact-viewer.tsx +376 -0
  386. package/src/components/artifacts/site-view.tsx +116 -0
  387. package/src/components/artifacts/spreadsheet-canvas.ts +5 -10
  388. package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
  389. package/src/components/artifacts/spreadsheet-grid.tsx +12 -13
  390. package/src/components/artifacts/visualization-assets/base.css +1015 -0
  391. package/src/components/artifacts/visualization-assets/helpers.html +349 -0
  392. package/src/components/artifacts/visualization-assets.gen.ts +3 -0
  393. package/src/components/browser-select-control.tsx +312 -0
  394. package/src/components/browser-viewer.tsx +1054 -314
  395. package/src/components/chat-composer.tsx +131 -85
  396. package/src/components/child-session-link.tsx +27 -0
  397. package/src/components/claude-mark.tsx +10 -0
  398. package/src/components/code-editor.tsx +13 -16
  399. package/src/components/composer-transcription-control.tsx +7 -5
  400. package/src/components/composer.tsx +376 -49
  401. package/src/components/computer-viewer.tsx +1041 -329
  402. package/src/components/copy-button.tsx +13 -5
  403. package/src/components/desktop-viewer.tsx +1 -1
  404. package/src/components/enrollment-consent.tsx +65 -7
  405. package/src/components/enrollment-device-flow.tsx +1 -1
  406. package/src/components/file-browser.tsx +158 -34
  407. package/src/components/fleet-tile.tsx +8 -16
  408. package/src/components/generated-video-player.tsx +4 -2
  409. package/src/components/grok-mark.tsx +33 -0
  410. package/src/components/human-input-form.tsx +455 -114
  411. package/src/components/human-input-surface.tsx +18 -7
  412. package/src/components/interaction-intervention-banner.tsx +2 -2
  413. package/src/components/machine-card.tsx +119 -4
  414. package/src/components/machine-dock-bar.tsx +1 -1
  415. package/src/components/machine-input-display.ts +23 -2
  416. package/src/components/machines/machine-detail.tsx +52 -2
  417. package/src/components/machines/machine-operation-policy.tsx +179 -0
  418. package/src/components/machines-dashboard.tsx +8 -17
  419. package/src/components/markdown-table-layout.ts +132 -0
  420. package/src/components/markdown.tsx +473 -44
  421. package/src/components/message-timeline.tsx +2754 -665
  422. package/src/components/model-mark.tsx +90 -0
  423. package/src/components/model-picker.tsx +15 -4
  424. package/src/components/model-policy-picker-menu.tsx +424 -0
  425. package/src/components/model-policy-picker.tsx +337 -381
  426. package/src/components/open-geni-chat.tsx +338 -0
  427. package/src/components/open-geni-links.tsx +179 -0
  428. package/src/components/openrouter-mark.tsx +13 -0
  429. package/src/components/pierre-diff.tsx +41 -16
  430. package/src/components/pierre-file.tsx +36 -11
  431. package/src/components/preview-loading.tsx +154 -0
  432. package/src/components/queue-item-content.ts +14 -0
  433. package/src/components/queue-surface-implementation.tsx +132 -42
  434. package/src/components/queue-surface-state.tsx +3 -1
  435. package/src/components/sandbox-files.tsx +267 -35
  436. package/src/components/sandbox-terminal.tsx +48 -11
  437. package/src/components/sandbox-workspace.tsx +337 -135
  438. package/src/components/session-capability-frame.tsx +192 -0
  439. package/src/components/session-chrome.tsx +1027 -328
  440. package/src/components/session-commands-panel.tsx +126 -0
  441. package/src/components/session-commands.tsx +18 -0
  442. package/src/components/session-connection-request.tsx +97 -0
  443. package/src/components/session-conversation.tsx +526 -0
  444. package/src/components/session-list.tsx +250 -0
  445. package/src/components/session-mcp-capability-card.tsx +680 -0
  446. package/src/components/session-proxy-scope.tsx +92 -0
  447. package/src/components/session-status.tsx +1 -1
  448. package/src/components/timeline-anchor.tsx +119 -0
  449. package/src/components/timeline-annotation-chrome.tsx +196 -0
  450. package/src/components/timeline-annotation-layout.ts +268 -0
  451. package/src/components/timeline-annotation-markers.tsx +185 -0
  452. package/src/components/timeline-annotation-reveal-context.ts +9 -0
  453. package/src/components/timeline-annotation-selection.tsx +106 -54
  454. package/src/components/timeline-annotation-shared.ts +245 -0
  455. package/src/components/timeline-annotations-dialog.tsx +185 -128
  456. package/src/components/timeline-annotations.tsx +189 -45
  457. package/src/components/timeline-reading-progress.ts +80 -0
  458. package/src/components/timeline-search.tsx +168 -0
  459. package/src/components/timeline-settlement.ts +60 -0
  460. package/src/components/tip-follow.ts +70 -7
  461. package/src/components/tooltip.tsx +38 -5
  462. package/src/components/usage-limit-notice.tsx +179 -0
  463. package/src/components/usage-member-list.tsx +792 -0
  464. package/src/components/usage-meter.tsx +382 -0
  465. package/src/components/use-viewer-menu-dismiss.ts +33 -0
  466. package/src/components/user-message-body.tsx +194 -49
  467. package/src/components/workspace-dock.tsx +141 -175
  468. package/src/composer.ts +6 -1
  469. package/src/connect-account-identity.tsx +34 -0
  470. package/src/connect-accounts.tsx +233 -0
  471. package/src/connect-chooser.tsx +336 -0
  472. package/src/connect-panel.tsx +99 -0
  473. package/src/connect-setup.tsx +307 -0
  474. package/src/connect.ts +60 -0
  475. package/src/connection-assets.d.ts +4 -0
  476. package/src/connection-catalog.tsx +162 -0
  477. package/src/connection-discovery.tsx +235 -0
  478. package/src/connection-installed.tsx +51 -0
  479. package/src/connection-logo.tsx +37 -0
  480. package/src/connection-service-logo.tsx +55 -0
  481. package/src/connection-service-presentation.ts +46 -0
  482. package/src/connection-skeleton.tsx +16 -0
  483. package/src/connection-type-picker.tsx +36 -0
  484. package/src/conversation-timeline.ts +92 -0
  485. package/src/desktop.ts +24 -0
  486. package/src/device-authorization.tsx +98 -0
  487. package/src/diffs.ts +16 -0
  488. package/src/discovery-cache.ts +42 -0
  489. package/src/editor.ts +13 -0
  490. package/src/embedded-session-client.ts +104 -0
  491. package/src/file-node-visibility.ts +66 -0
  492. package/src/hooks/channel-cache.ts +104 -0
  493. package/src/hooks/internal.ts +3 -2
  494. package/src/hooks/latest-question.ts +164 -0
  495. package/src/hooks/shared-machines-list.ts +239 -0
  496. package/src/hooks/use-attached-browsers.ts +5 -1
  497. package/src/hooks/use-available-models.ts +20 -14
  498. package/src/hooks/use-browser-frame-stream.ts +25 -3
  499. package/src/hooks/use-browser-session.ts +271 -71
  500. package/src/hooks/use-browser-sessions.ts +7 -1
  501. package/src/hooks/use-channels.ts +32 -10
  502. package/src/hooks/use-codex-accounts.ts +78 -21
  503. package/src/hooks/use-composer.ts +1034 -320
  504. package/src/hooks/use-computer-frame-stream.ts +39 -16
  505. package/src/hooks/use-computer-input-posture.ts +94 -0
  506. package/src/hooks/use-computer-session.ts +110 -32
  507. package/src/hooks/use-computer-sessions.ts +8 -1
  508. package/src/hooks/use-connect.ts +23 -0
  509. package/src/hooks/use-desktop-stream.ts +19 -28
  510. package/src/hooks/use-file-attachments.ts +118 -10
  511. package/src/hooks/use-goal.ts +36 -9
  512. package/src/hooks/use-human-input.ts +31 -4
  513. package/src/hooks/use-last-started-turn-policy.ts +15 -13
  514. package/src/hooks/use-machine-chip.ts +23 -6
  515. package/src/hooks/use-machines.ts +104 -6
  516. package/src/hooks/use-sandbox-files.ts +253 -150
  517. package/src/hooks/use-sandbox-git.ts +58 -29
  518. package/src/hooks/use-session-background-commands.ts +67 -0
  519. package/src/hooks/use-session-capabilities.ts +21 -2
  520. package/src/hooks/use-session-events.ts +759 -433
  521. package/src/hooks/use-session-lineage.ts +28 -6
  522. package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
  523. package/src/hooks/use-session.ts +75 -16
  524. package/src/hooks/use-slash-commands.ts +6 -11
  525. package/src/hooks/use-turn-queue.ts +113 -13
  526. package/src/hooks/use-usage.ts +100 -0
  527. package/src/hooks/use-video-artifact-playback.ts +1 -1
  528. package/src/hooks/use-voice-input.ts +12 -1
  529. package/src/hooks/use-workspace-capture.ts +3 -2
  530. package/src/hooks/use-workspace-edit.ts +23 -5
  531. package/src/hooks/use-workspace-sessions.ts +99 -11
  532. package/src/human-input.ts +12 -1
  533. package/src/identity-link-accounts.tsx +148 -0
  534. package/src/identity-link-consent.tsx +192 -0
  535. package/src/index.ts +157 -12
  536. package/src/interaction.ts +1 -0
  537. package/src/lib/error-message.ts +37 -0
  538. package/src/lib/format.ts +150 -14
  539. package/src/lib/host-theme.ts +511 -0
  540. package/src/lib/interaction-errors.ts +33 -0
  541. package/src/lib/menu-styles.ts +44 -0
  542. package/src/lib/pierre-diffs-loader.ts +53 -0
  543. package/src/lib/workbench-peers.ts +62 -0
  544. package/src/lib/xterm-theme.ts +23 -21
  545. package/src/machines.ts +9 -1
  546. package/src/mcp-connection-status.ts +35 -0
  547. package/src/model-policy.ts +223 -69
  548. package/src/older-history.ts +87 -0
  549. package/src/plugin-details.tsx +185 -0
  550. package/src/plugin-discovery.tsx +196 -0
  551. package/src/provider.tsx +55 -14
  552. package/src/realtime/dropdown-menu.tsx +19 -12
  553. package/src/realtime/realtime-control.tsx +133 -46
  554. package/src/session-auth-recommendation.ts +48 -0
  555. package/src/session-capability-policy.ts +83 -0
  556. package/src/session-context.ts +4 -3
  557. package/src/session-ui.ts +66 -2
  558. package/src/session.ts +18 -1
  559. package/src/sites-ui.tsx +402 -0
  560. package/src/sites.ts +3 -0
  561. package/src/skill-discovery.tsx +208 -0
  562. package/src/terminal.ts +26 -0
  563. package/src/timeline/activity-rail.tsx +171 -80
  564. package/src/timeline/allowance-exhausted-row.tsx +86 -0
  565. package/src/timeline/compaction-copy.ts +42 -0
  566. package/src/timeline/entrance.tsx +28 -3
  567. package/src/timeline/fleet-decision-projection.ts +2 -1
  568. package/src/timeline/fleet-decision-row.tsx +1 -0
  569. package/src/timeline/genie-loading.tsx +136 -0
  570. package/src/timeline/index.ts +11 -0
  571. package/src/timeline/knowledge-receipt.tsx +100 -0
  572. package/src/timeline/parsers.ts +2 -2
  573. package/src/timeline/platform-activity-row.tsx +216 -0
  574. package/src/timeline/presented-image.ts +49 -0
  575. package/src/timeline/projection.ts +1609 -84
  576. package/src/timeline/registry.ts +15 -3
  577. package/src/timeline/retained-image.ts +97 -0
  578. package/src/timeline/rolling-activity.tsx +92 -0
  579. package/src/timeline/screenshot-lightbox.tsx +115 -17
  580. package/src/timeline/shared.tsx +47 -15
  581. package/src/timeline/startup-preference.ts +33 -0
  582. package/src/timeline/startup-timings.tsx +145 -0
  583. package/src/timeline/tool-display-name.ts +8 -6
  584. package/src/timeline/tool-renderers.tsx +497 -128
  585. package/src/timeline/turn-summary.tsx +218 -44
  586. package/src/timeline/types.ts +192 -7
  587. package/src/types/machines.ts +10 -0
  588. package/src/usage/allowance-copy.ts +64 -0
  589. package/src/usage/summary.ts +93 -0
  590. package/src/usage.ts +50 -0
  591. package/styles/compiled.css +2098 -757
  592. package/styles/connect.css +284 -0
  593. package/styles/connect.d.ts +1 -0
  594. package/styles/effective-tokens.css +1 -0
  595. package/styles/index.css +201 -9
  596. package/styles/responsive.css +27 -1
  597. package/styles/session-capability.css +44 -0
  598. package/styles/tokens.css +135 -65
  599. package/dist/chunk-5UQPNLIO.js.map +0 -1
  600. package/dist/chunk-6KLX2RH5.js.map +0 -1
  601. package/dist/chunk-7WCLAEFV.js.map +0 -1
  602. package/dist/chunk-B6WPZSTT.js.map +0 -1
  603. package/dist/chunk-FK6VG4LL.js +0 -90
  604. package/dist/chunk-FK6VG4LL.js.map +0 -1
  605. package/dist/chunk-HA46CYQK.js.map +0 -1
  606. package/dist/chunk-HUIKIDM5.js.map +0 -1
  607. package/dist/chunk-IGC6A2ZI.js.map +0 -1
  608. package/dist/chunk-KCGXPP5V.js.map +0 -1
  609. package/dist/chunk-KZ73RL76.js.map +0 -1
  610. package/dist/chunk-LAKLF4PA.js +0 -141
  611. package/dist/chunk-LAKLF4PA.js.map +0 -1
  612. package/dist/chunk-LLOLUHVG.js.map +0 -1
  613. package/dist/chunk-MKPIAGVL.js +0 -465
  614. package/dist/chunk-MKPIAGVL.js.map +0 -1
  615. package/dist/chunk-PEI6RANI.js.map +0 -1
  616. package/dist/chunk-R3AZOAB5.js.map +0 -1
  617. package/dist/chunk-WSK7G5LE.js +0 -170
  618. package/dist/chunk-WSK7G5LE.js.map +0 -1
  619. package/dist/chunk-XBZWOM2K.js +0 -218
  620. package/dist/chunk-XBZWOM2K.js.map +0 -1
  621. package/dist/chunk-Y7OEDUJ5.js +0 -8810
  622. package/dist/chunk-Y7OEDUJ5.js.map +0 -1
  623. package/dist/fleet-decision-row-GGCAT4E4.js.map +0 -1
  624. package/dist/hooks/use-packs.d.ts +0 -32
  625. package/dist/interactive-workbench-viewers-TOHBXJEE.js +0 -15
  626. package/dist/queue-surface-implementation-2KVJTYXH.js.map +0 -1
  627. package/dist/timeline-annotation-selection-3L7LHGG2.js +0 -187
  628. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +0 -1
  629. package/dist/timeline-annotations-dialog-U7EVLR75.js +0 -196
  630. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +0 -1
  631. package/src/hooks/use-packs.ts +0 -157
  632. /package/dist/{interactive-workbench-viewers-TOHBXJEE.js.map → browser-viewer-SHYKQRNX.js.map} +0 -0
@@ -1,56 +1,82 @@
1
+ import { ClaudeMark } from "./claude-mark";
2
+ import { GrokMark } from "./grok-mark";
3
+ import { ChatGptMark, ModelMark, modelHasMark } from "./model-mark";
4
+ import { modelDisplayName } from "@opengeni/sdk/model-display";
1
5
  import type { ClientModel, LatencyMode, ReasoningEffort } from "@opengeni/sdk";
2
6
  import {
3
- CheckIcon,
4
7
  ChevronDownIcon,
5
8
  ChevronLeftIcon,
6
9
  ChevronRightIcon,
10
+ Globe2Icon,
7
11
  KeyRoundIcon,
12
+ SparklesIcon,
8
13
  ZapIcon,
9
14
  } from "lucide-react";
10
15
  import { motion, useReducedMotion } from "motion/react";
11
- import { DropdownMenu } from "radix-ui";
12
16
  import {
13
- useEffect,
17
+ Suspense,
18
+ lazy,
14
19
  useMemo,
20
+ useId,
15
21
  useState,
16
22
  type CSSProperties,
17
23
  type ReactNode,
18
- type SVGProps,
19
24
  } from "react";
20
25
  import { cn } from "../lib/cn";
26
+ import { MENU_CHEVRON_CLASS } from "../lib/menu-styles";
21
27
  import { usePortalTokenSource, usePortalTokenStyle } from "../lib/use-portal-token-style";
22
28
  import {
23
- coerceReasoningEffortForModel,
24
29
  effortOptionsForModel,
25
30
  findPickerRow,
26
- groupPickerRowsByBillingClass,
27
31
  labelReasoningEffort,
28
32
  projectClientModelRows,
29
- runnableLatencyModesForModel,
33
+ scopedBillingClassLabel,
30
34
  type PickerBillingClass,
31
35
  type PickerModelRow,
32
36
  } from "../model-policy";
33
37
 
34
- type ClientPickerModelRow = PickerModelRow<ClientModel>;
35
-
36
- type NavLevel = "providers" | "models" | "thinking";
38
+ const LazyModelPolicyPickerMenu = lazy(() =>
39
+ import("./model-policy-picker-menu").then(({ ModelPolicyPickerPopover }) => ({
40
+ default: ModelPolicyPickerPopover,
41
+ })),
42
+ );
37
43
 
38
- type PickerNavState = {
39
- level: NavLevel;
40
- rail: PickerBillingClass | null;
41
- modelId: string | null;
42
- };
44
+ type ClientPickerModelRow = PickerModelRow<ClientModel>;
43
45
 
44
- type NavUpdater = PickerNavState | ((previous: PickerNavState) => PickerNavState);
46
+ /** Presentation only; never changes model identity, billing or availability. */
47
+ export type ModelPolicyPickerGroupPresentation = Partial<
48
+ Record<
49
+ PickerBillingClass,
50
+ {
51
+ label?: string | undefined;
52
+ /** Omit to preserve the default; null hides the supporting text. */
53
+ description?: string | null | undefined;
54
+ /** Decorative, non-interactive content. Omit for the default; null hides it. */
55
+ icon?: ReactNode | undefined;
56
+ }
57
+ >
58
+ >;
45
59
 
46
60
  export type ModelPolicyPickerMessages = {
47
61
  label: string;
48
62
  loading: string;
49
63
  noModels: string;
64
+ connectTitle: string;
65
+ connectBody: string;
66
+ connectAction: string;
50
67
  thinking: string;
51
68
  fast: string;
52
69
  fastRateHint: string;
53
70
  codexOnly: string;
71
+ searchLabel?: string;
72
+ searchPlaceholder?: string;
73
+ currentModel?: string;
74
+ noMatches?: string;
75
+ unsupportedAttachments?: string;
76
+ thinkingEffort?: string;
77
+ selected?: string;
78
+ free?: string;
79
+
54
80
  billingHints: Record<PickerBillingClass, string>;
55
81
  };
56
82
 
@@ -58,21 +84,40 @@ export const defaultModelPolicyPickerMessages: ModelPolicyPickerMessages = {
58
84
  label: "Model and effort",
59
85
  loading: "Loading model catalog…",
60
86
  noModels: "No models available.",
87
+ connectTitle: "Connect a model",
88
+ connectBody: "Use a subscription or a provider key you already have.",
89
+ connectAction: "Open Models",
61
90
  thinking: "Thinking",
62
91
  fast: "Fast",
63
92
  fastRateHint: "2× rate",
64
93
  codexOnly: "Codex-only session",
94
+ searchLabel: "Search models or providers",
95
+ searchPlaceholder: "Search models or providers…",
96
+ currentModel: "Current model",
97
+ noMatches: "No matching models. Try a model or provider name.",
98
+ unsupportedAttachments: "This model cannot view the attached images.",
99
+ thinkingEffort: "Thinking effort",
100
+ selected: "Selected",
101
+ free: "Free",
102
+
65
103
  billingHints: {
66
- opengeni_credits: "Will use credits",
104
+ opengeni_credits: "Provided by Opengeni",
105
+ external: "Provider terms and limits apply",
67
106
  codex_subscription: "ChatGPT / Codex plan",
68
107
  supergrok_subscription: "SuperGrok / xAI plan",
69
- byok: "Billed to your AI Gateway",
108
+ claude_subscription: "Claude plan",
109
+ byok: "Billed to the connected provider account",
110
+ organization_byok: "Billed to the connected provider account",
70
111
  },
71
112
  };
72
113
 
73
114
  export type ModelPolicyPickerProps = {
115
+ /** Host branding for payment-source groups, shared by menu and trigger. */
116
+ groupPresentation?: ModelPolicyPickerGroupPresentation | undefined;
74
117
  /** Lightweight deployment models. Catalog rows take precedence when supplied. */
75
118
  models?: ClientModel[] | undefined;
119
+ /** Warn only when the draft actually contains images this model cannot view. */
120
+ hasImageAttachments?: boolean | undefined;
76
121
  /** Catalog-backed rows with availability and billing-class truth. */
77
122
  rows?: PickerModelRow[] | undefined;
78
123
  model: string;
@@ -88,15 +133,35 @@ export type ModelPolicyPickerProps = {
88
133
  onOpenChange?: ((open: boolean) => void) | undefined;
89
134
  /** Non-Codex models stay visible but cannot be selected. */
90
135
  codexOnly?: boolean | undefined;
91
- /** Resets drill-down navigation when the session scope changes. */
136
+ /** Identity of the current session scope. */
92
137
  sessionKey?: string | undefined;
93
138
  /** Prefer bottom on new-chat surfaces and top for bottom-docked composers. */
94
139
  menuSide?: "top" | "bottom" | undefined;
140
+ /**
141
+ * Default true: show models the way people choose them, with clean names,
142
+ * maker marks, one "API keys" group for organization- and workspace-connected
143
+ * keys, and one row for identical copies. Models settings passes false to
144
+ * keep its scope-aware presentation (raw catalog labels, scoped groups).
145
+ */
146
+ collapseScopes?: boolean | undefined;
147
+ /** Hide latency controls on surfaces whose saved policy does not include latency. */
148
+ allowLatencyMode?: boolean | undefined;
149
+ /** Settings → Models. Shown when the catalog has no model that can run. */
150
+ connectModelsHref?: string | undefined;
95
151
  /** Classes for the portalled menu surface. Prefer --og-* tokens for theming. */
96
152
  contentClassName?: string | undefined;
97
153
  /** Inline styles for the portalled menu, applied after inherited --og-* tokens. */
98
154
  contentStyle?: CSSProperties | undefined;
99
155
  className?: string | undefined;
156
+ /**
157
+ * "pill" (default) is the composer's quiet rounded trigger with the effort.
158
+ * "field" is a settings control: a bordered rectangle with the model name
159
+ * and `triggerMeta` (for example the payer) in muted text. The effort stays
160
+ * in the menu.
161
+ */
162
+ triggerStyle?: "pill" | "field" | undefined;
163
+ /** Muted text after the model name in the "field" trigger. */
164
+ triggerMeta?: ReactNode;
100
165
  messages?: Partial<ModelPolicyPickerMessages> | undefined;
101
166
  onModelChange: (modelId: string) => void;
102
167
  onEffortChange: (effort: ReasoningEffort) => void;
@@ -105,63 +170,29 @@ export type ModelPolicyPickerProps = {
105
170
 
106
171
  const SLIDE_EASE = [0.22, 1, 0.36, 1] as const;
107
172
 
108
- function OpenGeniMark(props: SVGProps<SVGSVGElement>) {
109
- return (
110
- <svg
111
- viewBox="0 0 140 133"
112
- fill="none"
113
- stroke="currentColor"
114
- strokeWidth={1.2}
115
- strokeLinejoin="round"
116
- aria-hidden="true"
117
- {...props}
118
- >
119
- <g transform="translate(-17.5,-20.999893188476562) scale(1.75)">
120
- <g transform="translate(0,-952.36218)">
121
- <path d="m 60.7828,964.36215 27.1809,0.8834 -27.1809,25.9958 z m -1.9745,1.4513 0,26.7845 -25.2681,0 c 8.6166,-8.7334 16.8796,-17.8103 25.2681,-26.7845 z m 27.7053,3.628 3.4864,1.1989 -12.5877,7.4768 z m -68.1835,2.9656 5.5226,0 12.8654,14.0705 -5.9854,6.1204 -12.4026,0 c 9e-4,-6.7347 0,-13.4597 0,-20.1909 z m -1.9746,1.2304 0,5.8364 -6.3555,0 z m 3.363,20.9796 38.627,0 -10.7675,29.43465 z m 39.0898,4.54286 0,41.20229 -12.5878,-6.8775 c 4.1972,-11.443 8.3886,-22.879 12.5878,-34.32479 z" />
122
- </g>
123
- </g>
124
- </svg>
125
- );
126
- }
127
-
128
- function ChatGptMark(props: SVGProps<SVGSVGElement>) {
129
- return (
130
- <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" {...props}>
131
- <path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3653-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8414 3.3698-2.02 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.783-2.7622a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" />
132
- </svg>
133
- );
134
- }
135
-
136
- function XaiMark(props: SVGProps<SVGSVGElement>) {
137
- return (
138
- <svg viewBox="0 0 466.04 516.93" fill="currentColor" aria-hidden="true" {...props}>
139
- <polygon points="0.12 182.71 234.14 516.92 338.15 516.92 104.13 182.71 0.12 182.71" />
140
- <polygon points="0 516.92 104.08 516.92 156.08 442.67 104.04 368.34 0 516.92" />
141
- <polygon points="466.04 0 361.96 0 182.1 256.86 234.15 331.18 466.04 0" />
142
- <polygon points="380.78 516.92 466.04 516.92 466.04 37.16 380.78 158.92 380.78 516.92" />
143
- </svg>
144
- );
145
- }
146
-
147
173
  export function BillingClassMark(props: {
148
174
  billingClass: PickerBillingClass;
175
+ presentation?: ModelPolicyPickerGroupPresentation[PickerBillingClass] | undefined;
149
176
  className?: string | undefined;
150
177
  "aria-label"?: string | undefined;
151
178
  }) {
152
179
  const labels: Record<PickerBillingClass, string> = {
153
- opengeni_credits: "OpenGeni",
180
+ opengeni_credits: "Models",
181
+ external: "External provider",
154
182
  codex_subscription: "Codex",
155
183
  supergrok_subscription: "SuperGrok",
156
- byok: "Bring your own key",
184
+ claude_subscription: "Claude",
185
+ byok: "API key",
186
+ organization_byok: "API key",
157
187
  };
158
- const label = props["aria-label"] ?? labels[props.billingClass];
188
+ if (props.presentation?.icon === null) return null;
189
+ const label = props["aria-label"] ?? props.presentation?.label ?? labels[props.billingClass];
159
190
  const accessibility =
160
191
  label.length === 0
161
192
  ? { "aria-hidden": true as const }
162
193
  : { role: "img" as const, "aria-label": label };
163
194
  const shell = cn(
164
- "inline-flex size-3.5 shrink-0 items-center justify-center overflow-hidden text-og-fg-subtle",
195
+ "inline-flex size-3.5 shrink-0 items-center justify-center overflow-hidden text-og-fg-subtle [&>svg]:size-full [&>img]:size-full",
165
196
  props.className,
166
197
  );
167
198
  const mark = "size-3.5";
@@ -171,12 +202,18 @@ export function BillingClassMark(props: {
171
202
  data-testid={`billing-class-icon-${props.billingClass}`}
172
203
  {...accessibility}
173
204
  >
174
- {props.billingClass === "opengeni_credits" ? (
175
- <OpenGeniMark className={mark} />
205
+ {props.presentation?.icon !== undefined ? (
206
+ props.presentation.icon
207
+ ) : props.billingClass === "opengeni_credits" ? (
208
+ <SparklesIcon className={mark} aria-hidden />
209
+ ) : props.billingClass === "external" ? (
210
+ <Globe2Icon className={mark} aria-hidden />
176
211
  ) : props.billingClass === "codex_subscription" ? (
177
212
  <ChatGptMark className={mark} />
213
+ ) : props.billingClass === "claude_subscription" ? (
214
+ <ClaudeMark className={mark} />
178
215
  ) : props.billingClass === "supergrok_subscription" ? (
179
- <XaiMark className={mark} />
216
+ <GrokMark className={mark} />
180
217
  ) : (
181
218
  <KeyRoundIcon className={mark} aria-hidden />
182
219
  )}
@@ -188,6 +225,22 @@ function isCodexModel(model: ClientModel): boolean {
188
225
  return model.id.startsWith("codex/") || model.source === "codex";
189
226
  }
190
227
 
228
+ function billingClassForMissingSelection(modelId: string): PickerBillingClass {
229
+ if (modelId.startsWith("workspace-claude-subscription/")) return "claude_subscription";
230
+ if (modelId.startsWith("workspace-anthropic/")) return "byok";
231
+ if (modelId.startsWith("organization-claude-subscription/")) return "claude_subscription";
232
+ if (modelId.startsWith("organization-anthropic/")) return "organization_byok";
233
+ if (modelId.startsWith("workspace-gateway/")) return "byok";
234
+ if (modelId.startsWith("workspace-openrouter/")) return "byok";
235
+ // A deployment OpenRouter ID does not encode its workspace-facing cost.
236
+ // Missing rows therefore use the credits-safe rail instead of falsely
237
+ // claiming that an unknown former selection was externally funded.
238
+ if (modelId.startsWith("openrouter/")) return "opengeni_credits";
239
+ if (modelId.startsWith("codex/")) return "codex_subscription";
240
+ if (modelId.startsWith("supergrok/")) return "supergrok_subscription";
241
+ return "opengeni_credits";
242
+ }
243
+
191
244
  function applyCodexOnly(
192
245
  rows: ClientPickerModelRow[],
193
246
  codexOnly: boolean,
@@ -205,36 +258,102 @@ function applyCodexOnly(
205
258
  );
206
259
  }
207
260
 
208
- function effectiveRows(props: ModelPolicyPickerProps): ClientPickerModelRow[] {
209
- const rows = props.rows !== undefined ? props.rows : projectClientModelRows(props.models ?? []);
210
- const messages = { ...defaultModelPolicyPickerMessages, ...props.messages };
211
- return applyCodexOnly(rows, props.codexOnly === true, messages.codexOnly);
261
+ /**
262
+ * The trigger shows who makes the chosen model. Subscription rails already
263
+ * carry their maker's mark (ChatGPT, Claude, Grok), and explicit host branding
264
+ * for a payment group always wins; otherwise API-key and deployment models show
265
+ * the maker's logo instead of a generic key, falling back to the group mark.
266
+ */
267
+ function SelectedModelMark(input: {
268
+ props: ModelPolicyPickerProps;
269
+ selected: ClientPickerModelRow | null;
270
+ }) {
271
+ const billingClass =
272
+ input.selected?.billingClass ?? billingClassForMissingSelection(input.props.model);
273
+ const presentation = groupPresentationFor(input.props, billingClass);
274
+ const model = input.selected?.catalog ?? input.props.model;
275
+ if (input.props.collapseScopes === false) {
276
+ return (
277
+ <BillingClassMark
278
+ billingClass={billingClass}
279
+ presentation={presentation}
280
+ aria-label={presentation?.label ?? SCOPED_MARK_LABELS[billingClass]}
281
+ className="text-og-fg"
282
+ />
283
+ );
284
+ }
285
+ if (
286
+ presentation?.icon !== undefined ||
287
+ BRANDED_BILLING_CLASSES.has(billingClass) ||
288
+ !modelHasMark(model)
289
+ ) {
290
+ return (
291
+ <BillingClassMark
292
+ billingClass={billingClass}
293
+ presentation={presentation}
294
+ className="text-og-fg"
295
+ />
296
+ );
297
+ }
298
+ return <ModelMark model={model} className="text-og-fg" />;
212
299
  }
213
300
 
214
- function defaultNavState(rows: ClientPickerModelRow[], modelId: string): PickerNavState {
215
- const selected = findPickerRow(rows, modelId);
216
- if (!selected) {
217
- const rails = new Set(rows.map((row) => row.billingClass));
218
- const onlyRail = rails.size === 1 ? rows[0]?.billingClass : null;
219
- return onlyRail
220
- ? { level: "models", rail: onlyRail, modelId: null }
221
- : { level: "providers", rail: null, modelId: null };
222
- }
223
- return { level: "thinking", rail: selected.billingClass, modelId: selected.id };
301
+ const SCOPED_MARK_LABELS: Partial<Record<PickerBillingClass, string>> = {
302
+ byok: "Workspace provider account",
303
+ organization_byok: "Organization provider account",
304
+ };
305
+
306
+ /** Models settings keeps naming who is billed for a key. */
307
+ export const SCOPED_BILLING_HINTS: Partial<Record<PickerBillingClass, string>> = {
308
+ byok: "Billed to the workspace provider account",
309
+ organization_byok: "Billed to the organization provider account",
310
+ };
311
+
312
+ /**
313
+ * Host branding for a payment group. While scopes are collapsed, API keys are
314
+ * one group, so either API-key presentation serves both.
315
+ */
316
+ export function groupPresentationFor(
317
+ props: Pick<ModelPolicyPickerProps, "groupPresentation" | "collapseScopes">,
318
+ billingClass: PickerBillingClass,
319
+ ): ModelPolicyPickerGroupPresentation[PickerBillingClass] {
320
+ const own = props.groupPresentation?.[billingClass];
321
+ if (own !== undefined || props.collapseScopes === false) return own;
322
+ if (billingClass === "byok") return props.groupPresentation?.organization_byok;
323
+ if (billingClass === "organization_byok") return props.groupPresentation?.byok;
324
+ return undefined;
224
325
  }
225
326
 
226
- function usePickerNavState(
227
- sessionKey: string | undefined,
228
- rows: ClientPickerModelRow[],
229
- model: string,
230
- ): [PickerNavState, (next: NavUpdater) => void] {
231
- const [state, setState] = useState<PickerNavState>(() => defaultNavState(rows, model));
232
- useEffect(() => {
233
- setState(defaultNavState(rows, model));
234
- // The menu deliberately preserves its drill-down while the same session is mounted.
235
- // eslint-disable-next-line react-hooks/exhaustive-deps
236
- }, [sessionKey]);
237
- return [state, setState];
327
+ /** A selection the catalog no longer lists: its clean name (settings keep the id). */
328
+ function fallbackName(props: ModelPolicyPickerProps): string {
329
+ return props.collapseScopes === false ? props.model : modelDisplayName(props.model);
330
+ }
331
+
332
+ const BRANDED_BILLING_CLASSES: ReadonlySet<PickerBillingClass> = new Set([
333
+ "codex_subscription",
334
+ "claude_subscription",
335
+ "supergrok_subscription",
336
+ ]);
337
+
338
+ export function effectiveRows(props: ModelPolicyPickerProps): ClientPickerModelRow[] {
339
+ const rows = props.rows !== undefined ? props.rows : projectClientModelRows(props.models ?? []);
340
+ const messages = { ...defaultModelPolicyPickerMessages, ...props.messages };
341
+ return applyCodexOnly(rows, props.codexOnly === true, messages.codexOnly).map((row) => {
342
+ const scoped =
343
+ props.collapseScopes === false
344
+ ? {
345
+ ...row,
346
+ label: row.catalog.label,
347
+ billingClassLabel: scopedBillingClassLabel(row.billingClass),
348
+ }
349
+ : row;
350
+ if (props.collapseScopes === false) {
351
+ if (row.catalog.shortLabel) scoped.shortLabel = row.catalog.shortLabel;
352
+ else delete scoped.shortLabel;
353
+ }
354
+ const label = groupPresentationFor(props, row.billingClass)?.label;
355
+ return label === undefined ? scoped : { ...scoped, billingClassLabel: label };
356
+ });
238
357
  }
239
358
 
240
359
  export function PickerNavRow(props: {
@@ -245,6 +364,7 @@ export function PickerNavRow(props: {
245
364
  showChevron?: boolean | undefined;
246
365
  disabled?: boolean | undefined;
247
366
  title?: string | undefined;
367
+ description?: string | undefined;
248
368
  active?: boolean | undefined;
249
369
  testId?: string | undefined;
250
370
  onClick: () => void;
@@ -254,28 +374,24 @@ export function PickerNavRow(props: {
254
374
  type="button"
255
375
  disabled={props.disabled}
256
376
  title={props.title}
377
+ aria-description={props.description}
257
378
  onClick={props.onClick}
258
379
  data-testid={props.testId}
259
380
  className={cn(
260
- "flex w-full cursor-pointer items-center gap-2 rounded-og-sm px-[var(--og-model-picker-row-padding-x)] py-[var(--og-model-picker-row-padding-y)] text-left text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40",
381
+ // The one menu row (lib/menu-styles.ts), with density tokens for embedders.
382
+ "flex min-h-8 w-full cursor-pointer items-center gap-2.5 rounded-og-md px-[var(--og-model-picker-row-padding-x)] py-[var(--og-model-picker-row-padding-y)] text-left text-og-fg outline-hidden transition-colors duration-[120ms] hover:bg-og-hover focus-visible:bg-og-hover focus-visible:outline-2 focus-visible:-outline-offset-2! focus-visible:outline-og-accent/55 pointer-coarse:min-h-11",
261
383
  props.disabled && "cursor-not-allowed opacity-50",
262
384
  )}
263
385
  >
264
386
  {props.icon}
265
387
  <span className="min-w-0 flex-1">
266
- <span className={cn("block truncate text-og-menu", props.active && "font-medium")}>
267
- {props.label}
268
- </span>
388
+ <span className="block truncate text-og-menu">{props.label}</span>
269
389
  {props.hint ? (
270
- <span className="mt-0.5 block truncate text-og-control text-og-fg-subtle">
271
- {props.hint}
272
- </span>
390
+ <span className="mt-0.5 block truncate text-og-sm text-og-fg-muted">{props.hint}</span>
273
391
  ) : null}
274
392
  </span>
275
393
  {props.trailing ? <span className="ml-auto shrink-0">{props.trailing}</span> : null}
276
- {props.showChevron === false ? null : (
277
- <ChevronRightIcon className="size-3.5 shrink-0 text-og-fg-subtle" />
278
- )}
394
+ {props.showChevron === false ? null : <ChevronRightIcon className={MENU_CHEVRON_CLASS} />}
279
395
  </button>
280
396
  );
281
397
  }
@@ -287,14 +403,16 @@ export function PickerBackHeader(props: {
287
403
  trailing?: ReactNode;
288
404
  }) {
289
405
  return (
290
- <div className="mb-1 flex items-center gap-0.5 border-b border-og-border/70 px-0.5 pb-1.5">
406
+ <div className="mb-1.5 flex min-h-9 items-center gap-1 border-b border-og-border pb-1.5">
291
407
  <button
292
408
  type="button"
293
409
  onClick={props.onBack}
294
410
  data-testid="model-picker-back"
295
- className="flex min-w-0 flex-1 cursor-pointer items-center gap-1.5 rounded-og-sm px-1.5 py-1.5 text-left text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40"
411
+ className="flex min-h-8 min-w-0 flex-1 cursor-pointer items-center gap-1 rounded-og-md pr-2.5 text-left text-og-fg outline-hidden transition-colors duration-[120ms] hover:bg-og-hover focus-visible:outline-2 focus-visible:-outline-offset-2! focus-visible:outline-og-accent/55 pointer-coarse:min-h-11"
296
412
  >
297
- <ChevronLeftIcon className="size-3.5 shrink-0 text-og-fg-subtle" />
413
+ <span className="flex size-8 shrink-0 items-center justify-center text-og-fg-muted">
414
+ <ChevronLeftIcon className="size-4" />
415
+ </span>
298
416
  {props.icon}
299
417
  <span className="min-w-0 flex-1 truncate text-og-menu font-medium">{props.label}</span>
300
418
  </button>
@@ -322,255 +440,36 @@ export function PickerAnimatedPage(props: {
322
440
  );
323
441
  }
324
442
 
325
- export function ModelPolicyPickerMenu(
326
- props: ModelPolicyPickerProps & {
327
- nav?: PickerNavState | undefined;
328
- onNavChange?: ((next: NavUpdater) => void) | undefined;
329
- },
330
- ) {
331
- const messages = useMemo(
332
- () => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),
333
- [props.messages],
334
- );
335
- const rows = effectiveRows(props);
336
- const groups = useMemo(() => groupPickerRowsByBillingClass(rows), [rows]);
337
- const [localNav, setLocalNav] = usePickerNavState(
338
- props.nav === undefined ? props.sessionKey : undefined,
339
- rows,
340
- props.model,
341
- );
342
- const nav = props.nav ?? localNav;
343
- const setNav = props.onNavChange ?? setLocalNav;
344
- const [direction, setDirection] = useState<1 | -1>(1);
345
-
346
- const effectiveNav =
347
- nav.level === "providers" && groups.length === 1
348
- ? { level: "models" as const, rail: groups[0]!.billingClass, modelId: null }
349
- : nav;
350
- const activeGroup =
351
- effectiveNav.rail === null
352
- ? undefined
353
- : groups.find((group) => group.billingClass === effectiveNav.rail);
354
- const focusModel =
355
- effectiveNav.modelId === null ? undefined : findPickerRow(rows, effectiveNav.modelId);
356
- const selectedRow = findPickerRow(rows, props.model);
357
- const { latencyMode, loading, onLatencyModeChange } = props;
358
-
359
- useEffect(() => {
360
- if (loading || !selectedRow?.selectable) return;
361
- const supportsCurrentMode = runnableLatencyModesForModel(selectedRow.catalog).includes(
362
- latencyMode,
363
- );
364
- if (!supportsCurrentMode && latencyMode !== "standard") {
365
- onLatencyModeChange("standard");
366
- }
367
- }, [latencyMode, loading, onLatencyModeChange, selectedRow]);
368
-
369
- const go = (next: PickerNavState, nextDirection: 1 | -1) => {
370
- setDirection(nextDirection);
371
- setNav(next);
372
- };
373
-
374
- const selectEffort = (row: ClientPickerModelRow, effort: ReasoningEffort) => {
375
- if (!row.selectable) return;
376
- if (row.id !== props.model) props.onModelChange(row.id);
377
- props.onEffortChange(coerceReasoningEffortForModel(row.catalog, effort));
378
- };
379
-
380
- const pageKey =
381
- effectiveNav.level === "providers"
382
- ? "providers"
383
- : effectiveNav.level === "models"
384
- ? `models:${effectiveNav.rail ?? ""}`
385
- : `thinking:${effectiveNav.modelId ?? ""}`;
386
-
387
- let body: ReactNode;
388
- if (props.loading) {
389
- body = <p className="px-2 py-3 text-og-control text-og-fg-subtle">{messages.loading}</p>;
390
- } else if (groups.length === 0) {
391
- body = <p className="px-2 py-3 text-og-control text-og-fg-subtle">{messages.noModels}</p>;
392
- } else if (effectiveNav.level === "providers") {
393
- body = (
394
- <div className="flex flex-col gap-0.5" data-testid="model-picker-providers">
395
- {groups.map((group) => (
396
- <PickerNavRow
397
- key={group.billingClass}
398
- label={group.label}
399
- hint={messages.billingHints[group.billingClass]}
400
- icon={<BillingClassMark billingClass={group.billingClass} />}
401
- active={selectedRow?.billingClass === group.billingClass}
402
- testId={`model-picker-rail-${group.billingClass}`}
403
- onClick={() => go({ level: "models", rail: group.billingClass, modelId: null }, 1)}
404
- />
405
- ))}
406
- </div>
407
- );
408
- } else if (effectiveNav.level === "models" && activeGroup) {
409
- body = (
410
- <div data-testid="model-picker-models">
411
- {groups.length > 1 ? (
412
- <PickerBackHeader
413
- label={activeGroup.label}
414
- icon={<BillingClassMark billingClass={activeGroup.billingClass} />}
415
- onBack={() => go({ level: "providers", rail: null, modelId: null }, -1)}
416
- />
417
- ) : null}
418
- <div className="flex flex-col gap-0.5">
419
- {activeGroup.rows.map((row) => (
420
- <PickerNavRow
421
- key={`${row.billingClass}:${row.id}`}
422
- label={row.label}
423
- hint={row.unavailableReason ?? undefined}
424
- disabled={!row.selectable}
425
- title={row.unavailableReason ?? undefined}
426
- active={row.id === props.model}
427
- testId={`model-picker-choice-${row.id}`}
428
- onClick={() => {
429
- if (row.selectable) {
430
- go({ level: "thinking", rail: row.billingClass, modelId: row.id }, 1);
431
- }
432
- }}
433
- />
434
- ))}
435
- </div>
436
- </div>
437
- );
438
- } else if (effectiveNav.level === "thinking" && focusModel) {
439
- const activeModel = focusModel.id === props.model;
440
- const supportsFast = runnableLatencyModesForModel(focusModel.catalog).includes("fast");
441
- body = (
442
- <div data-testid="model-picker-reasoning">
443
- <PickerBackHeader
444
- label={focusModel.label}
445
- icon={<BillingClassMark billingClass={focusModel.billingClass} />}
446
- onBack={() => go({ level: "models", rail: focusModel.billingClass, modelId: null }, -1)}
447
- trailing={
448
- supportsFast ? (
449
- <button
450
- type="button"
451
- disabled={!focusModel.selectable}
452
- aria-pressed={activeModel && props.latencyMode === "fast"}
453
- aria-label={
454
- activeModel && props.latencyMode === "fast"
455
- ? `Disable ${messages.fast}`
456
- : `Enable ${messages.fast}`
457
- }
458
- title={`${messages.fast} · ${messages.fastRateHint}`}
459
- data-testid="model-picker-fast"
460
- onClick={() => {
461
- if (!activeModel) {
462
- props.onModelChange(focusModel.id);
463
- props.onEffortChange(
464
- coerceReasoningEffortForModel(focusModel.catalog, props.effort),
465
- );
466
- }
467
- props.onLatencyModeChange(
468
- activeModel && props.latencyMode === "fast" ? "standard" : "fast",
469
- );
470
- }}
471
- className={cn(
472
- "flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-og-sm text-og-fg-subtle outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50",
473
- activeModel && props.latencyMode === "fast" && "text-og-fg",
474
- )}
475
- >
476
- <ZapIcon
477
- className={cn(
478
- "size-3.5",
479
- activeModel && props.latencyMode === "fast" && "fill-current",
480
- )}
481
- />
482
- </button>
483
- ) : null
484
- }
485
- />
486
- <p className="px-2.5 pt-1 pb-1 text-og-control font-medium tracking-wide text-og-fg-subtle uppercase">
487
- {messages.thinking}
488
- </p>
489
- {focusModel.catalog.capabilities?.inputModalities.includes("image") === false ? (
490
- <p className="px-2.5 pb-1.5 text-og-control leading-relaxed text-og-fg-subtle">
491
- Unsupported attachments stay in the session but are hidden from this model.
492
- </p>
493
- ) : null}
494
- <div className="flex flex-col gap-0.5">
495
- {effortOptionsForModel(focusModel.catalog).map((effort) => {
496
- const selected = activeModel && effort === props.effort;
497
- return (
498
- <button
499
- key={effort}
500
- type="button"
501
- disabled={!focusModel.selectable}
502
- onClick={() => selectEffort(focusModel, effort)}
503
- className={cn(
504
- "flex h-[var(--og-model-picker-effort-height)] w-full cursor-pointer items-center rounded-og-sm px-[var(--og-model-picker-row-padding-x)] text-left text-og-menu text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50",
505
- selected && "bg-og-surface-2",
506
- )}
507
- >
508
- <span className="min-w-0 flex-1 truncate">{labelReasoningEffort(effort)}</span>
509
- {selected ? (
510
- <CheckIcon
511
- className="ml-auto size-3.5 shrink-0"
512
- data-testid="model-picker-effort-check"
513
- />
514
- ) : null}
515
- </button>
516
- );
517
- })}
518
- </div>
519
- </div>
520
- );
521
- } else {
522
- body = (
523
- <div className="flex flex-col gap-0.5" data-testid="model-picker-providers">
524
- {groups.map((group) => (
525
- <PickerNavRow
526
- key={group.billingClass}
527
- label={group.label}
528
- hint={messages.billingHints[group.billingClass]}
529
- icon={<BillingClassMark billingClass={group.billingClass} />}
530
- testId={`model-picker-rail-${group.billingClass}`}
531
- onClick={() => go({ level: "models", rail: group.billingClass, modelId: null }, 1)}
532
- />
533
- ))}
534
- </div>
535
- );
536
- }
537
-
538
- return (
539
- <div data-testid="model-picker-menu" className="relative overflow-hidden">
540
- {props.error ? (
541
- <p className="px-2 py-1 text-og-control text-og-status-failed" role="alert">
542
- {props.error}
543
- </p>
544
- ) : null}
545
- <PickerAnimatedPage pageKey={pageKey} direction={direction}>
546
- {body}
547
- </PickerAnimatedPage>
548
- </div>
549
- );
550
- }
551
-
552
- export function ModelPolicyPicker(props: ModelPolicyPickerProps) {
443
+ /** @internal Shared controlled/uncontrolled state seam. */
444
+ export function useModelPolicyPickerState(props: ModelPolicyPickerProps) {
553
445
  const [uncontrolledOpen, setUncontrolledOpen] = useState(props.defaultOpen ?? false);
554
446
  const open = props.open ?? uncontrolledOpen;
555
447
  const setOpen = (next: boolean) => {
556
448
  if (props.open === undefined) setUncontrolledOpen(next);
557
449
  props.onOpenChange?.(next);
558
450
  };
451
+ return { open, setOpen, rows: effectiveRows(props) };
452
+ }
453
+
454
+ export function ModelPolicyPicker(props: ModelPolicyPickerProps) {
455
+ const contentId = useId();
559
456
  const trigger = usePortalTokenSource<HTMLButtonElement>();
560
457
  const portalStyle = usePortalTokenStyle(trigger.source);
561
458
  const messages = useMemo(
562
459
  () => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),
563
460
  [props.messages],
564
461
  );
565
- const rows = effectiveRows(props);
566
- const [nav, setNav] = usePickerNavState(props.sessionKey, rows, props.model);
462
+ const { open, setOpen, rows } = useModelPolicyPickerState(props);
567
463
  const selected = findPickerRow(rows, props.model);
464
+ const needsModel =
465
+ !rows.some((row) => row.selectable) && (rows.length > 0 || Boolean(props.connectModelsHref));
568
466
 
569
467
  if (props.loading) {
570
468
  return (
571
469
  <span
572
470
  className={cn(
573
471
  "og-root inline-flex h-8 w-40 shrink-0 animate-pulse rounded-full bg-og-surface-2",
472
+ props.triggerStyle === "field" && "w-[180px] rounded-og-md",
574
473
  props.className,
575
474
  )}
576
475
  aria-label={messages.loading}
@@ -578,64 +477,121 @@ export function ModelPolicyPicker(props: ModelPolicyPickerProps) {
578
477
  />
579
478
  );
580
479
  }
581
- return (
582
- <DropdownMenu.Root open={open} onOpenChange={setOpen}>
583
- <DropdownMenu.Trigger asChild>
480
+ if (props.triggerStyle === "field") {
481
+ return (
482
+ <>
584
483
  <button
585
484
  ref={trigger.ref}
586
485
  type="button"
486
+ aria-haspopup="dialog"
487
+ aria-expanded={open}
488
+ aria-controls={open ? contentId : undefined}
489
+ data-state={open ? "open" : "closed"}
490
+ data-trigger-style="field"
491
+ onClick={() => setOpen(!open)}
587
492
  disabled={props.disabled}
588
493
  aria-label={messages.label}
589
494
  className={cn(
590
- "og-root og-model-policy-trigger inline-flex h-[var(--og-model-picker-trigger-height)] min-w-0 max-w-64 items-center gap-1 rounded-full border border-transparent px-2.5 text-og-control text-og-fg-muted outline-hidden transition-colors hover:border-og-border hover:bg-og-surface-2 hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50 max-sm:h-11 max-sm:max-w-[7.5rem] max-sm:px-2",
495
+ "og-root inline-flex h-8 min-w-[180px] max-w-full items-center gap-2 rounded-og-md border border-og-border bg-og-surface px-2.5 text-sm text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50",
591
496
  props.className,
592
497
  )}
593
498
  >
594
- <BillingClassMark
595
- billingClass={
596
- selected?.billingClass ??
597
- (props.model.startsWith("codex/") ? "codex_subscription" : "opengeni_credits")
598
- }
599
- className="text-og-fg"
600
- />
601
- <span className="og-model-policy-label-full min-w-0 truncate font-medium text-og-fg max-sm:hidden">
602
- {selected?.label ?? props.model}
603
- </span>
604
- <span className="og-model-policy-label-short min-w-0 truncate font-medium text-og-fg sm:hidden">
605
- {selected?.shortLabel ?? selected?.label ?? props.model}
606
- </span>
607
- <span className="og-model-policy-effort max-sm:hidden">
608
- {labelReasoningEffort(props.effort)}
499
+ {needsModel ? (
500
+ <SparklesIcon className="size-3.5 shrink-0" aria-hidden />
501
+ ) : (
502
+ <SelectedModelMark props={props} selected={selected} />
503
+ )}
504
+ <span className="min-w-0 truncate font-medium">
505
+ {needsModel ? messages.connectTitle : (selected?.label ?? fallbackName(props))}
609
506
  </span>
610
- {props.latencyMode === "fast" ? (
611
- <ZapIcon
612
- className="size-3.5 shrink-0 fill-current stroke-current text-og-fg"
613
- aria-label={messages.fast}
614
- data-testid="model-picker-fast-icon"
615
- />
507
+ {props.triggerMeta && !needsModel ? (
508
+ <span className="min-w-0 shrink-[9999] truncate text-og-fg-muted">
509
+ {props.triggerMeta}
510
+ </span>
616
511
  ) : null}
617
- <ChevronDownIcon className="size-3 shrink-0" />
512
+ <ChevronDownIcon className="ml-auto size-3.5 shrink-0 text-og-fg-muted" />
618
513
  </button>
619
- </DropdownMenu.Trigger>
620
- <DropdownMenu.Portal>
621
- <DropdownMenu.Content
622
- align="start"
623
- side={props.menuSide ?? "bottom"}
624
- sideOffset={8}
625
- collisionPadding={12}
626
- onCloseAutoFocus={(event) => event.preventDefault()}
627
- className={cn(
628
- "og-root og-model-policy-menu z-50 flex max-h-[min(20rem,var(--radix-dropdown-menu-content-available-height))] w-[var(--og-model-picker-menu-width)] max-w-[calc(100vw-16px)] flex-col overflow-hidden rounded-og-lg border border-og-border bg-og-surface-1 p-[var(--og-model-picker-menu-padding)] text-og-fg shadow-og-lg",
629
- props.contentClassName,
630
- )}
631
- style={{ ...portalStyle, ...props.contentStyle }}
632
- data-testid="model-picker-content"
633
- >
634
- <div className="min-h-0 flex-1 overflow-y-auto overscroll-contain">
635
- <ModelPolicyPickerMenu {...props} nav={nav} onNavChange={setNav} />
636
- </div>
637
- </DropdownMenu.Content>
638
- </DropdownMenu.Portal>
639
- </DropdownMenu.Root>
514
+ {open ? (
515
+ <Suspense fallback={null}>
516
+ <LazyModelPolicyPickerMenu
517
+ {...props}
518
+ anchor={trigger.currentRef}
519
+ contentId={contentId}
520
+ portalStyle={portalStyle}
521
+ onOpenChange={setOpen}
522
+ />
523
+ </Suspense>
524
+ ) : null}
525
+ </>
526
+ );
527
+ }
528
+ return (
529
+ <>
530
+ <button
531
+ ref={trigger.ref}
532
+ type="button"
533
+ aria-haspopup="dialog"
534
+ aria-expanded={open}
535
+ aria-controls={open ? contentId : undefined}
536
+ data-state={open ? "open" : "closed"}
537
+ onClick={() => setOpen(!open)}
538
+ disabled={props.disabled}
539
+ aria-label={messages.label}
540
+ className={cn(
541
+ "og-root og-model-policy-trigger inline-flex h-[var(--og-model-picker-trigger-height)] min-w-0 max-w-64 items-center gap-1 rounded-full border px-2.5 text-og-control outline-hidden transition-colors focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50 max-sm:h-11 max-sm:max-w-[7.5rem] max-sm:px-2",
542
+ // With no usable model the pill is the one thing that unblocks the
543
+ // composer, so it takes the primary wash.
544
+ needsModel
545
+ ? "border-og-primary-border bg-og-primary text-og-primary-fg hover:bg-og-primary-hover"
546
+ : "border-transparent text-og-fg-muted hover:border-og-border hover:bg-og-surface-2 hover:text-og-fg",
547
+ props.className,
548
+ )}
549
+ >
550
+ {needsModel ? (
551
+ <SparklesIcon className="size-3.5 shrink-0" aria-hidden />
552
+ ) : (
553
+ <SelectedModelMark props={props} selected={selected} />
554
+ )}
555
+ <span className="og-model-policy-label-full min-w-0 truncate font-medium text-og-fg max-sm:hidden @max-[20rem]/model-controls:hidden">
556
+ {needsModel ? messages.connectTitle : (selected?.label ?? fallbackName(props))}
557
+ </span>
558
+ <span className="og-model-policy-label-short min-w-0 truncate font-medium text-og-fg sm:hidden @max-[20rem]/model-controls:block">
559
+ {needsModel
560
+ ? messages.connectTitle
561
+ : (selected?.shortLabel ?? selected?.label ?? fallbackName(props))}
562
+ </span>
563
+ {selected &&
564
+ !needsModel &&
565
+ effortOptionsForModel(selected.catalog).length > 1 &&
566
+ selected.catalog.capabilities?.reasoning.runnable !== false ? (
567
+ <span
568
+ className="og-model-policy-effort min-w-0 shrink-[9999] truncate"
569
+ title={labelReasoningEffort(props.effort)}
570
+ >
571
+ {labelReasoningEffort(props.effort)}
572
+ </span>
573
+ ) : null}
574
+ {props.latencyMode === "fast" && !needsModel ? (
575
+ <ZapIcon
576
+ className="size-3.5 shrink-0 fill-current stroke-current text-og-fg"
577
+ aria-label={messages.fast}
578
+ data-testid="model-picker-fast-icon"
579
+ />
580
+ ) : null}
581
+ <ChevronDownIcon className="size-3 shrink-0" />
582
+ </button>
583
+
584
+ {open ? (
585
+ <Suspense fallback={null}>
586
+ <LazyModelPolicyPickerMenu
587
+ {...props}
588
+ anchor={trigger.currentRef}
589
+ contentId={contentId}
590
+ portalStyle={portalStyle}
591
+ onOpenChange={setOpen}
592
+ />
593
+ </Suspense>
594
+ ) : null}
595
+ </>
640
596
  );
641
597
  }