@opengeni/react 4.6.1 → 4.6.2-canary.39853438886001

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 (467) hide show
  1. package/dist/app/app-state.js +13 -13
  2. package/dist/app/components/Composer.js +14 -14
  3. package/dist/app/components/artifacts/artifact-library.js +5 -5
  4. package/dist/app/components/artifacts/chat-interactive-block.js +10 -10
  5. package/dist/app/components/artifacts/inline-chat-image.js +14 -14
  6. package/dist/app/components/artifacts/retained-file-preview.js +16 -16
  7. package/dist/app/components/artifacts/retained-text-preview.js +2 -2
  8. package/dist/app/components/artifacts/site-conversations.js +5 -4
  9. package/dist/app/components/artifacts/site-data.js +2 -2
  10. package/dist/app/components/artifacts/site-publishing.js +11 -11
  11. package/dist/app/components/artifacts/site-settings.js +15 -15
  12. package/dist/app/components/billing-class-mark.js +3 -3
  13. package/dist/app/components/capabilities/capability-detail-sheet.js +2 -2
  14. package/dist/app/components/capabilities/capability-logo-source.js +7 -7
  15. package/dist/app/components/capabilities/native-connect-readiness.js +1 -0
  16. package/dist/app/components/capabilities/native-connect-readiness.js.map +1 -1
  17. package/dist/app/components/capabilities/native-connect-setup.js +6 -6
  18. package/dist/app/components/capabilities/perform-capability-action.js +2 -1
  19. package/dist/app/components/capabilities/prepared-mcp-setup-card.js +8 -8
  20. package/dist/app/components/capabilities/session-capability-card.js +32 -31
  21. package/dist/app/components/capabilities/session-capability-card.js.map +1 -1
  22. package/dist/app/components/capabilities/session-chrome-card.js +14 -14
  23. package/dist/app/components/capabilities/session-custom-mcp-card.js +9 -9
  24. package/dist/app/components/capabilities/session-github-card.js +9 -9
  25. package/dist/app/components/capabilities/session-machine-card.js +14 -14
  26. package/dist/app/components/command-menu/chat-status.js +6 -5
  27. package/dist/app/components/command-menu/chat-switcher.d.ts +2 -0
  28. package/dist/app/components/command-menu/chat-switcher.js +26 -9
  29. package/dist/app/components/command-menu/chat-switcher.js.map +1 -1
  30. package/dist/app/components/command-menu/command-menu.js +16 -15
  31. package/dist/app/components/command-menu/command-menu.js.map +1 -1
  32. package/dist/app/components/command-menu/shortcut-sheet.js +8 -8
  33. package/dist/app/components/composer-mobile-plus-panel.js +5 -5
  34. package/dist/app/components/composer-mobile-plus.js +1 -1
  35. package/dist/app/components/credit-required-gate.js +1 -1
  36. package/dist/app/components/credit-required-prompt.js +4 -4
  37. package/dist/app/components/desktop/app-shots.js +5 -5
  38. package/dist/app/components/desktop/this-computer.js +4 -4
  39. package/dist/app/components/feedback.js +3 -3
  40. package/dist/app/components/github-app-connect-launcher.js +1 -1
  41. package/dist/app/components/machines/connect-machine-dialog.js +3 -3
  42. package/dist/app/components/markdown.js +4 -4
  43. package/dist/app/components/models/models-ui.js +2 -2
  44. package/dist/app/components/pickers.js +5 -5
  45. package/dist/app/components/rail/rail-context.js +3 -3
  46. package/dist/app/components/rail/rail-sections.js +28 -27
  47. package/dist/app/components/rail/session-header.d.ts +6 -5
  48. package/dist/app/components/rail/session-header.js +20 -19
  49. package/dist/app/components/rail/session-list.js +27 -26
  50. package/dist/app/components/rail/session-row-hover-details.js +7 -6
  51. package/dist/app/components/rail/session-shell.js +51 -53
  52. package/dist/app/components/rail/site-session-group-heading.js +2 -1
  53. package/dist/app/components/session/agent-configuration-panel.js +6 -6
  54. package/dist/app/components/session/banners.js +6 -6
  55. package/dist/app/components/session/conversation-find.js +3 -3
  56. package/dist/app/components/session/editable-artifacts-workspace.js +17 -17
  57. package/dist/app/components/session/failed-session-banner.js +5 -5
  58. package/dist/app/components/session/human-input.js +26 -26
  59. package/dist/app/components/session/inspector.js +37 -37
  60. package/dist/app/components/session/message-actions.js +3 -3
  61. package/dist/app/components/session/message-resource-attachments.js +5 -5
  62. package/dist/app/components/session/model-recovery-notice.js +3 -3
  63. package/dist/app/components/session/new-session-settings-menu.js +4 -4
  64. package/dist/app/components/session/sandbox-switcher.js +8 -8
  65. package/dist/app/components/session/sandbox-workspace.js +36 -36
  66. package/dist/app/components/session/search-markdown-highlight.js +5 -5
  67. package/dist/app/components/session/search-results-view.js +6 -6
  68. package/dist/app/components/session/session-admission-notice.js +4 -4
  69. package/dist/app/components/session/session-machine-access-notice.js +4 -4
  70. package/dist/app/components/session/session-search-dialog.js +6 -6
  71. package/dist/app/components/session/session-tenancy-control.js +4 -4
  72. package/dist/app/components/session/session-usage-limit-section.js +3 -3
  73. package/dist/app/components/session/session-wait-status.js +2 -1
  74. package/dist/app/components/session/session-wait-status.js.map +1 -1
  75. package/dist/app/components/session/subagents.js +6 -5
  76. package/dist/app/components/session/subscription-account-indicator.d.ts +35 -0
  77. package/dist/app/components/session/{codex-account-indicator.js → subscription-account-indicator.js} +23 -10
  78. package/dist/app/components/session/subscription-account-menu.d.ts +11 -12
  79. package/dist/app/components/session/subscription-account-menu.js +1 -1
  80. package/dist/app/components/session/subscription-account.d.ts +105 -0
  81. package/dist/app/components/usage/usage-entry.js +3 -3
  82. package/dist/app/components/workspace-composer-plus.js +10 -10
  83. package/dist/app/embed/session-app.js +154 -156
  84. package/dist/app/lib/actions/actions-provider.js +8 -8
  85. package/dist/app/lib/actions/chat-switcher.d.ts +19 -7
  86. package/dist/app/lib/codex-usage-query.js +2 -2
  87. package/dist/app/lib/composer-connectors.js +8 -8
  88. package/dist/app/lib/console-text.js +2 -2
  89. package/dist/app/lib/drive-connect-return.d.ts +26 -0
  90. package/dist/app/lib/drive-connect-return.js +16 -0
  91. package/dist/app/lib/events.js +7 -7
  92. package/dist/app/lib/failed-session-copy.js +3 -3
  93. package/dist/app/lib/model-access-onboarding.js +3 -3
  94. package/dist/app/lib/move-chat-to-machine.js +3 -3
  95. package/dist/app/lib/session-branch-cache.js +6 -5
  96. package/dist/app/lib/session-group-pagination.js +6 -5
  97. package/dist/app/lib/session-hover-facts.js +6 -5
  98. package/dist/app/lib/session-rail.js +2 -1
  99. package/dist/app/lib/sessions-group.js +5 -4
  100. package/dist/app/lib/turn-failure-analytics.js +5 -5
  101. package/dist/app/lib/use-workspace-machines.js +4 -4
  102. package/dist/app/lib/use-workspace-model-catalog.js +3 -3
  103. package/dist/app/lib/use-workspace-rigs.js +2 -2
  104. package/dist/app/lib/workspace-navigation-preference.js +4 -4
  105. package/dist/app/lib/workspace-scope-context.js +3 -3
  106. package/dist/app/routes/artifacts.js +50 -49
  107. package/dist/app/routes/artifacts.js.map +1 -1
  108. package/dist/app/routes/retained-artifact.js +23 -23
  109. package/dist/app/routes/session.js +92 -91
  110. package/dist/app/routes/sessions-index.js +52 -52
  111. package/dist/app/routes/site-view.js +35 -34
  112. package/dist/app/routes/site-view.js.map +1 -1
  113. package/dist/artifacts-document.js +4 -4
  114. package/dist/artifacts-presentation.js +4 -4
  115. package/dist/artifacts-spreadsheet.js +4 -4
  116. package/dist/artifacts.js +10 -10
  117. package/dist/{browser-viewer-YQC6BFMS.js → browser-viewer-VKIAULG7.js} +4 -4
  118. package/dist/{chat-interactive-block-Z22YHDRU.js → chat-interactive-block-JGYSW4KB.js} +5 -5
  119. package/dist/{chunk-WN2MIB4Q.js → chunk-22V2QCEM.js} +2 -2
  120. package/dist/{chunk-SJPAKWWP.js → chunk-2BISI664.js} +2 -2
  121. package/dist/{chunk-O5UOCMH4.js → chunk-2LPJVEWY.js} +3 -3
  122. package/dist/{chunk-NPDL4W4Y.js → chunk-2MLJHNKJ.js} +108 -109
  123. package/dist/chunk-2MLJHNKJ.js.map +1 -0
  124. package/dist/{chunk-JI6WPLZB.js → chunk-2SBCOGQN.js} +4 -4
  125. package/dist/{chunk-6JF7C3U3.js → chunk-2WERWWSS.js} +2 -2
  126. package/dist/{chunk-4XKT4ENZ.js → chunk-2Y3QL3DA.js} +4 -4
  127. package/dist/{chunk-KVMFT6FN.js → chunk-3FL3F4WF.js} +2 -2
  128. package/dist/{chunk-4VJOZJDV.js → chunk-3JPQAIYJ.js} +2 -2
  129. package/dist/{chunk-27EORKJ4.js → chunk-3N4LNBJF.js} +2 -2
  130. package/dist/{chunk-7JZSFJHB.js → chunk-3OJU3RUR.js} +2 -2
  131. package/dist/{chunk-NV5OYV3G.js → chunk-3U6K36QA.js} +5 -5
  132. package/dist/{chunk-MDRLP5DO.js → chunk-3W7ROHN4.js} +3 -3
  133. package/dist/{chunk-3MQM4ZOC.js → chunk-3Y6ZR2VN.js} +3 -3
  134. package/dist/{chunk-HXZIX7MR.js → chunk-47AB65IO.js} +4 -4
  135. package/dist/{chunk-DNYD7AUG.js → chunk-4BOLIZ7Y.js} +3 -3
  136. package/dist/{chunk-4XO346BW.js → chunk-4FBJFQFU.js} +2 -2
  137. package/dist/{chunk-BWKZEW6K.js → chunk-4T3E56NT.js} +3 -3
  138. package/dist/{chunk-4PC7LIQK.js → chunk-4ZMUWRFF.js} +3 -3
  139. package/dist/{chunk-TM2ZLY3Q.js → chunk-5OLFYSVG.js} +13 -13
  140. package/dist/{chunk-D4QOZXPM.js → chunk-5WBRM7V4.js} +5 -5
  141. package/dist/{chunk-GXK4CUAB.js → chunk-5WQKTT5Z.js} +2 -2
  142. package/dist/{chunk-6N7W3TOV.js → chunk-63BPMTBM.js} +2 -2
  143. package/dist/{chunk-R5YZ3ROM.js → chunk-63PQKTTT.js} +2 -2
  144. package/dist/{chunk-WLZVXCPZ.js → chunk-6GO6WN22.js} +3 -3
  145. package/dist/{chunk-6PQBKMUL.js → chunk-6IIA63VD.js} +11 -7
  146. package/dist/chunk-6IIA63VD.js.map +1 -0
  147. package/dist/{chunk-XDCZBOEY.js → chunk-6LGBGPBU.js} +1 -1
  148. package/dist/{chunk-FMATVQFW.js → chunk-6XXMG5YP.js} +9 -9
  149. package/dist/{chunk-M5E3CKSE.js → chunk-7QWWE5XL.js} +2 -2
  150. package/dist/{chunk-RABKQFTA.js → chunk-A2HCAMRH.js} +2 -2
  151. package/dist/{chunk-6K3WNTRI.js → chunk-AODISQO6.js} +2 -2
  152. package/dist/{chunk-C6JL6PBL.js → chunk-AQDRH6VF.js} +3 -3
  153. package/dist/{chunk-72XEFLMD.js → chunk-ASWINLSW.js} +2 -2
  154. package/dist/{chunk-ZPZYYSHW.js → chunk-BVMDON5T.js} +8 -8
  155. package/dist/{chunk-EURDMOXZ.js → chunk-BYJZUCOQ.js} +2 -2
  156. package/dist/{chunk-BQWUXC55.js → chunk-CAVAVT5S.js} +9 -9
  157. package/dist/{chunk-ZWACUOU5.js → chunk-CDBGAAJY.js} +3 -3
  158. package/dist/chunk-CDBGAAJY.js.map +1 -0
  159. package/dist/{chunk-BPD4IJBG.js → chunk-CG6CJMIS.js} +2 -2
  160. package/dist/{chunk-B254JG72.js → chunk-CQX24PT7.js} +2 -2
  161. package/dist/{chunk-65K5LV4L.js → chunk-CTCOVPZU.js} +51 -50
  162. package/dist/{chunk-65K5LV4L.js.map → chunk-CTCOVPZU.js.map} +1 -1
  163. package/dist/{chunk-HJTTDLF6.js → chunk-CY3NO4JN.js} +4 -4
  164. package/dist/{chunk-OLUG4IFF.js → chunk-D2RLPRSG.js} +2 -2
  165. package/dist/{chunk-74NIHMAT.js → chunk-D653JH6D.js} +3 -3
  166. package/dist/{chunk-2LUO22FR.js → chunk-DAI5CTQF.js} +4 -4
  167. package/dist/{chunk-HTKVYEVB.js → chunk-DBM4ZXDN.js} +2 -2
  168. package/dist/{chunk-MB2DTUEG.js → chunk-DNYKLU3K.js} +4 -4
  169. package/dist/{chunk-26P7B46A.js → chunk-DXYARXK3.js} +3 -3
  170. package/dist/{chunk-JQKURO3S.js → chunk-E4MQRQ6Z.js} +2 -2
  171. package/dist/{chunk-KW53DBY7.js → chunk-EBW2E32G.js} +2 -2
  172. package/dist/{chunk-O73DRA77.js → chunk-ENKBO7FC.js} +4 -4
  173. package/dist/{chunk-FVCLVASD.js → chunk-FI3KRVOF.js} +24 -24
  174. package/dist/{chunk-WWM7WVD2.js → chunk-GABFKHQE.js} +2 -2
  175. package/dist/chunk-GLCOKWLH.js +42 -0
  176. package/dist/chunk-GLCOKWLH.js.map +1 -0
  177. package/dist/{chunk-32HA7DXJ.js → chunk-GNBRB3DH.js} +25 -25
  178. package/dist/{chunk-CEQUNJJY.js → chunk-HPALK6LD.js} +2 -2
  179. package/dist/{chunk-LR7U3HCD.js → chunk-HPSATFLQ.js} +4 -4
  180. package/dist/{chunk-O4AWXTZQ.js → chunk-HUYLQW75.js} +6 -6
  181. package/dist/{chunk-PQCFW2KT.js → chunk-IDDHH5AQ.js} +29 -24
  182. package/dist/chunk-IDDHH5AQ.js.map +1 -0
  183. package/dist/{chunk-ZZUKJ5HQ.js → chunk-IG2ERMSB.js} +2 -2
  184. package/dist/{chunk-2SZ3KC5S.js → chunk-IMKIIS4Y.js} +2 -2
  185. package/dist/{chunk-CA64OZLZ.js → chunk-IRXPXDY5.js} +4 -4
  186. package/dist/{chunk-I57HZMMA.js → chunk-IXIE3H43.js} +2 -2
  187. package/dist/{chunk-OWJD3V4V.js → chunk-J3YQ3554.js} +2 -2
  188. package/dist/{chunk-BCFHMBMK.js → chunk-J5HZBHU4.js} +7 -7
  189. package/dist/{chunk-UHMMRQGU.js → chunk-KD7GNHOP.js} +3 -3
  190. package/dist/{chunk-X7Z4B5GK.js → chunk-KPL7EXS6.js} +2 -2
  191. package/dist/{chunk-GTR2WUBA.js → chunk-KX6M5CJI.js} +1 -1
  192. package/dist/{chunk-CYO5HL4C.js → chunk-KYL2BPS2.js} +2 -2
  193. package/dist/{chunk-QLDMF7MH.js → chunk-LC5HE7PZ.js} +2 -2
  194. package/dist/{chunk-3SMJ2BIQ.js → chunk-LGD5RESZ.js} +7 -33
  195. package/dist/chunk-LGD5RESZ.js.map +1 -0
  196. package/dist/{chunk-4ICPJZAB.js → chunk-LMYSYG6U.js} +2 -2
  197. package/dist/{chunk-3ZZOE4SN.js → chunk-LOCDYMD6.js} +4 -4
  198. package/dist/{chunk-DNWBLT4U.js → chunk-MASBMJ2M.js} +2 -2
  199. package/dist/{chunk-NZMQDUOD.js → chunk-MU6HYDNF.js} +5 -5
  200. package/dist/{chunk-GMVKCEBG.js → chunk-MY6QEDZ2.js} +2 -2
  201. package/dist/{chunk-SK57RWSA.js → chunk-MZGKQ5WV.js} +2 -2
  202. package/dist/{chunk-HSQRYJ4P.js → chunk-N52SFH2K.js} +2 -2
  203. package/dist/{chunk-MY6VZVCP.js → chunk-OAJX5CEL.js} +4 -4
  204. package/dist/{chunk-IWIMIRV3.js → chunk-OSJTLQXA.js} +9 -4
  205. package/dist/chunk-OSJTLQXA.js.map +1 -0
  206. package/dist/{chunk-FH75NNLB.js → chunk-OZ5NAXCP.js} +2 -2
  207. package/dist/{chunk-PEGNFZ3F.js → chunk-P5S777YI.js} +2 -2
  208. package/dist/{chunk-LY6727IF.js → chunk-PPSVFNEA.js} +1 -1
  209. package/dist/{chunk-K4FDZVMQ.js → chunk-PR42VIVY.js} +2 -2
  210. package/dist/{chunk-I74J2UZ2.js → chunk-PWMZCW4H.js} +8 -8
  211. package/dist/{chunk-6YDXUE5H.js → chunk-PYA2O3WS.js} +2 -2
  212. package/dist/{chunk-GUIZDI5T.js → chunk-Q2PA4KZS.js} +2 -2
  213. package/dist/{chunk-JJ7VMIEQ.js → chunk-QCANSTLU.js} +64 -64
  214. package/dist/{chunk-DZH2HTTB.js → chunk-R27GW5WV.js} +1 -1
  215. package/dist/chunk-R4U4EWZG.js +414 -0
  216. package/dist/chunk-R4U4EWZG.js.map +1 -0
  217. package/dist/{chunk-DAQG3C4W.js → chunk-R6JY22SG.js} +29 -29
  218. package/dist/{chunk-7OIBLMDP.js → chunk-RAQISW42.js} +2 -2
  219. package/dist/{chunk-DNLXQUBX.js → chunk-RLS6PTL4.js} +32 -44
  220. package/dist/chunk-RLS6PTL4.js.map +1 -0
  221. package/dist/{chunk-CPKD2YGK.js → chunk-RNU5CUPI.js} +2 -2
  222. package/dist/{chunk-467VRP4E.js → chunk-RO5YZQHZ.js} +3 -3
  223. package/dist/{chunk-SSZROOOI.js → chunk-ROLXYGT5.js} +2 -2
  224. package/dist/{chunk-6VITVQ23.js → chunk-SHJ5SQOW.js} +7 -7
  225. package/dist/{chunk-F4B2YYCN.js → chunk-TAWFXRXB.js} +3 -3
  226. package/dist/{chunk-G3FU2IF7.js → chunk-TB7UW5BB.js} +3 -3
  227. package/dist/{chunk-NF6Q4XTN.js → chunk-TQF2I5SY.js} +3 -3
  228. package/dist/{chunk-MM7OKOTE.js → chunk-UBXHP224.js} +43 -18
  229. package/dist/chunk-UBXHP224.js.map +1 -0
  230. package/dist/{chunk-M7A226EQ.js → chunk-UL44R3W2.js} +2 -2
  231. package/dist/{chunk-GTR5NLVP.js → chunk-VCRQPLKS.js} +2 -2
  232. package/dist/{chunk-MHIYYHCL.js → chunk-VJU3ET7N.js} +12 -12
  233. package/dist/{chunk-RXMIXKB3.js → chunk-WGJUARF3.js} +13 -13
  234. package/dist/{chunk-NWSAF6P5.js → chunk-WJDH7CH5.js} +22 -1
  235. package/dist/chunk-WJDH7CH5.js.map +1 -0
  236. package/dist/{chunk-LG67ZNRU.js → chunk-WK2ZCTUF.js} +2 -2
  237. package/dist/{chunk-XGE75VPK.js → chunk-WNARM3LY.js} +3 -3
  238. package/dist/{chunk-RNBH5TSM.js → chunk-WQ2RTQYO.js} +2 -2
  239. package/dist/chunk-X5XEYQ4B.js +84 -0
  240. package/dist/chunk-X5XEYQ4B.js.map +1 -0
  241. package/dist/{chunk-W57QFLQD.js → chunk-X6JIGNLI.js} +4 -4
  242. package/dist/{chunk-CKA4SJRK.js → chunk-XIQDCQX7.js} +2 -2
  243. package/dist/{chunk-AJ7PY4FJ.js → chunk-Y3GCG6CA.js} +2 -2
  244. package/dist/{chunk-2K27S6LK.js → chunk-Y565N5ZJ.js} +24 -7
  245. package/dist/chunk-Y565N5ZJ.js.map +1 -0
  246. package/dist/{chunk-ZRB4M3GG.js → chunk-YC5KH7ZB.js} +3 -3
  247. package/dist/{chunk-4RY64KQY.js → chunk-YJUKLLNP.js} +2 -2
  248. package/dist/{chunk-OHG2UQ2O.js → chunk-YWP2H7SX.js} +60 -22
  249. package/dist/chunk-YWP2H7SX.js.map +1 -0
  250. package/dist/{chunk-24POWVUY.js → chunk-ZDJNRRVS.js} +4 -4
  251. package/dist/{chunk-S6WJ6TUT.js → chunk-ZEVJGMNB.js} +2 -2
  252. package/dist/chunk-ZEVJGMNB.js.map +1 -0
  253. package/dist/{chunk-M7YEZR6X.js → chunk-ZITMDA4C.js} +2 -2
  254. package/dist/components/model-policy-picker.d.ts +2 -0
  255. package/dist/composer.js +6 -6
  256. package/dist/{computer-viewer-BWVNDFTO.js → computer-viewer-6CRLWQHP.js} +5 -5
  257. package/dist/connect.js +6 -6
  258. package/dist/connection-service-presentation.d.ts +1 -4
  259. package/dist/desktop.js +2 -2
  260. package/dist/editor.js +2 -2
  261. package/dist/{embedded-new-voice-GBXSZ3TO.js → embedded-new-voice-KILV3RYM.js} +5 -5
  262. package/dist/{embedded-voice-MGI7EK7C.js → embedded-voice-GHDRLCFX.js} +5 -5
  263. package/dist/{fleet-decision-row-35S3GAUN.js → fleet-decision-row-DQXRRBT7.js} +2 -2
  264. package/dist/index.js +44 -44
  265. package/dist/interaction.js +5 -5
  266. package/dist/machines.js +3 -3
  267. package/dist/messages/en.d.ts +1 -1
  268. package/dist/messages.js +1 -1
  269. package/dist/{model-policy-picker-menu-KP53N6UJ.js → model-policy-picker-menu-5ALKUJMR.js} +4 -4
  270. package/dist/{platform-activity-row-ISMXR7NJ.js → platform-activity-row-227C764Z.js} +2 -2
  271. package/dist/{queue-surface-implementation-ULW7DDJA.js → queue-surface-implementation-H3NOBXU5.js} +5 -5
  272. package/dist/realtime.js +4 -4
  273. package/dist/{sandbox-files-QYSD4XCZ.js → sandbox-files-5FEOHUMD.js} +6 -6
  274. package/dist/session-app.js +155 -157
  275. package/dist/{session-realtime-JXRLPNUH.js → session-realtime-OHBMODNX.js} +3 -3
  276. package/dist/session-realtime.js +2 -2
  277. package/dist/session-ui.js +26 -26
  278. package/dist/session.js +6 -6
  279. package/dist/sites.js +2 -2
  280. package/dist/terminal.js +2 -2
  281. package/dist/this-computer.js +3 -3
  282. package/dist/timeline/connector-logo-index.d.ts +19 -0
  283. package/dist/{timeline-annotation-markers-OJYKHBEO.js → timeline-annotation-markers-NDHS7MCK.js} +2 -2
  284. package/dist/{timeline-annotation-selection-6OAZLXFU.js → timeline-annotation-selection-DRQSTJOV.js} +2 -2
  285. package/dist/{timeline-annotations-dialog-5M6DP5FX.js → timeline-annotations-dialog-G6J5ZJKN.js} +3 -3
  286. package/dist/timeline-model.d.ts +2 -0
  287. package/dist/timeline-model.js +9 -1
  288. package/dist/ui/list-row.js +1 -1
  289. package/dist/usage.js +2 -2
  290. package/dist/workbench.js +4 -4
  291. package/package.json +4 -4
  292. package/src/app/components/capabilities/native-connect-readiness.tsx +1 -0
  293. package/src/app/components/capabilities/perform-capability-action.test.ts +67 -0
  294. package/src/app/components/capabilities/perform-capability-action.tsx +28 -0
  295. package/src/app/components/command-menu/chat-switcher.tsx +25 -4
  296. package/src/app/components/rail/session-header.test.tsx +73 -2
  297. package/src/app/components/rail/session-header.tsx +20 -12
  298. package/src/app/components/rail/session-shell.tsx +10 -18
  299. package/src/app/components/session/subscription-account-indicator.test.ts +239 -0
  300. package/src/app/components/session/subscription-account-indicator.tsx +361 -0
  301. package/src/app/components/session/subscription-account-menu.tsx +21 -43
  302. package/src/app/components/session/subscription-account.test.ts +314 -0
  303. package/src/app/components/session/subscription-account.ts +354 -0
  304. package/src/app/lib/actions/actions-provider.tsx +11 -0
  305. package/src/app/lib/actions/actions.test.ts +59 -4
  306. package/src/app/lib/actions/chat-switcher.ts +73 -20
  307. package/src/app/lib/drive-connect-return.test.ts +183 -0
  308. package/src/app/lib/drive-connect-return.tsx +116 -0
  309. package/src/app/routes/session.tsx +12 -14
  310. package/src/app/routes/sessions-index.tsx +1 -0
  311. package/src/components/model-policy-picker-menu.tsx +25 -4
  312. package/src/components/model-policy-picker.tsx +2 -0
  313. package/src/components/session-mcp-capability-card.tsx +13 -4
  314. package/src/connection-service-presentation.ts +6 -1
  315. package/src/messages/en.ts +1 -1
  316. package/src/timeline/connector-logo-index.test.ts +46 -0
  317. package/src/timeline/connector-logo-index.ts +68 -0
  318. package/src/timeline-model.ts +6 -0
  319. package/src/ui/list-row.tsx +38 -10
  320. package/styles/compiled.css +13 -0
  321. package/dist/app/components/session/codex-account-indicator.d.ts +0 -9
  322. package/dist/app/components/session/model-account-indicator.d.ts +0 -10
  323. package/dist/app/components/session/model-account-indicator.js +0 -34
  324. package/dist/app/components/session/model-account-usage.d.ts +0 -8
  325. package/dist/app/components/session/model-account-usage.js +0 -8
  326. package/dist/chunk-2K27S6LK.js.map +0 -1
  327. package/dist/chunk-3SMJ2BIQ.js.map +0 -1
  328. package/dist/chunk-6PQBKMUL.js.map +0 -1
  329. package/dist/chunk-7H2D4VNU.js +0 -150
  330. package/dist/chunk-7H2D4VNU.js.map +0 -1
  331. package/dist/chunk-DNLXQUBX.js.map +0 -1
  332. package/dist/chunk-HCCSAECI.js +0 -132
  333. package/dist/chunk-HCCSAECI.js.map +0 -1
  334. package/dist/chunk-IWIMIRV3.js.map +0 -1
  335. package/dist/chunk-M77ZQE5T.js +0 -58
  336. package/dist/chunk-M77ZQE5T.js.map +0 -1
  337. package/dist/chunk-MM7OKOTE.js.map +0 -1
  338. package/dist/chunk-NPDL4W4Y.js.map +0 -1
  339. package/dist/chunk-NWSAF6P5.js.map +0 -1
  340. package/dist/chunk-OHG2UQ2O.js.map +0 -1
  341. package/dist/chunk-PQCFW2KT.js.map +0 -1
  342. package/dist/chunk-S6WJ6TUT.js.map +0 -1
  343. package/dist/chunk-ZWACUOU5.js.map +0 -1
  344. package/dist/session-realtime-JXRLPNUH.js.map +0 -1
  345. package/src/app/components/session/codex-account-indicator.tsx +0 -207
  346. package/src/app/components/session/model-account-indicator.tsx +0 -189
  347. package/src/app/components/session/model-account-usage.test.ts +0 -59
  348. package/src/app/components/session/model-account-usage.tsx +0 -89
  349. /package/dist/app/components/session/{codex-account-indicator.js.map → subscription-account-indicator.js.map} +0 -0
  350. /package/dist/app/{components/session/model-account-indicator.js.map → lib/drive-connect-return.js.map} +0 -0
  351. /package/dist/{app/components/session/model-account-usage.js.map → browser-viewer-VKIAULG7.js.map} +0 -0
  352. /package/dist/{browser-viewer-YQC6BFMS.js.map → chat-interactive-block-JGYSW4KB.js.map} +0 -0
  353. /package/dist/{chunk-WN2MIB4Q.js.map → chunk-22V2QCEM.js.map} +0 -0
  354. /package/dist/{chunk-SJPAKWWP.js.map → chunk-2BISI664.js.map} +0 -0
  355. /package/dist/{chunk-O5UOCMH4.js.map → chunk-2LPJVEWY.js.map} +0 -0
  356. /package/dist/{chunk-JI6WPLZB.js.map → chunk-2SBCOGQN.js.map} +0 -0
  357. /package/dist/{chunk-6JF7C3U3.js.map → chunk-2WERWWSS.js.map} +0 -0
  358. /package/dist/{chunk-4XKT4ENZ.js.map → chunk-2Y3QL3DA.js.map} +0 -0
  359. /package/dist/{chunk-KVMFT6FN.js.map → chunk-3FL3F4WF.js.map} +0 -0
  360. /package/dist/{chunk-4VJOZJDV.js.map → chunk-3JPQAIYJ.js.map} +0 -0
  361. /package/dist/{chunk-27EORKJ4.js.map → chunk-3N4LNBJF.js.map} +0 -0
  362. /package/dist/{chunk-7JZSFJHB.js.map → chunk-3OJU3RUR.js.map} +0 -0
  363. /package/dist/{chunk-NV5OYV3G.js.map → chunk-3U6K36QA.js.map} +0 -0
  364. /package/dist/{chunk-MDRLP5DO.js.map → chunk-3W7ROHN4.js.map} +0 -0
  365. /package/dist/{chunk-3MQM4ZOC.js.map → chunk-3Y6ZR2VN.js.map} +0 -0
  366. /package/dist/{chunk-HXZIX7MR.js.map → chunk-47AB65IO.js.map} +0 -0
  367. /package/dist/{chunk-DNYD7AUG.js.map → chunk-4BOLIZ7Y.js.map} +0 -0
  368. /package/dist/{chunk-4XO346BW.js.map → chunk-4FBJFQFU.js.map} +0 -0
  369. /package/dist/{chunk-BWKZEW6K.js.map → chunk-4T3E56NT.js.map} +0 -0
  370. /package/dist/{chunk-4PC7LIQK.js.map → chunk-4ZMUWRFF.js.map} +0 -0
  371. /package/dist/{chunk-TM2ZLY3Q.js.map → chunk-5OLFYSVG.js.map} +0 -0
  372. /package/dist/{chunk-D4QOZXPM.js.map → chunk-5WBRM7V4.js.map} +0 -0
  373. /package/dist/{chunk-GXK4CUAB.js.map → chunk-5WQKTT5Z.js.map} +0 -0
  374. /package/dist/{chunk-6N7W3TOV.js.map → chunk-63BPMTBM.js.map} +0 -0
  375. /package/dist/{chunk-R5YZ3ROM.js.map → chunk-63PQKTTT.js.map} +0 -0
  376. /package/dist/{chunk-WLZVXCPZ.js.map → chunk-6GO6WN22.js.map} +0 -0
  377. /package/dist/{chunk-XDCZBOEY.js.map → chunk-6LGBGPBU.js.map} +0 -0
  378. /package/dist/{chunk-FMATVQFW.js.map → chunk-6XXMG5YP.js.map} +0 -0
  379. /package/dist/{chunk-M5E3CKSE.js.map → chunk-7QWWE5XL.js.map} +0 -0
  380. /package/dist/{chunk-RABKQFTA.js.map → chunk-A2HCAMRH.js.map} +0 -0
  381. /package/dist/{chunk-6K3WNTRI.js.map → chunk-AODISQO6.js.map} +0 -0
  382. /package/dist/{chunk-C6JL6PBL.js.map → chunk-AQDRH6VF.js.map} +0 -0
  383. /package/dist/{chunk-72XEFLMD.js.map → chunk-ASWINLSW.js.map} +0 -0
  384. /package/dist/{chunk-ZPZYYSHW.js.map → chunk-BVMDON5T.js.map} +0 -0
  385. /package/dist/{chunk-EURDMOXZ.js.map → chunk-BYJZUCOQ.js.map} +0 -0
  386. /package/dist/{chunk-BQWUXC55.js.map → chunk-CAVAVT5S.js.map} +0 -0
  387. /package/dist/{chunk-BPD4IJBG.js.map → chunk-CG6CJMIS.js.map} +0 -0
  388. /package/dist/{chunk-B254JG72.js.map → chunk-CQX24PT7.js.map} +0 -0
  389. /package/dist/{chunk-HJTTDLF6.js.map → chunk-CY3NO4JN.js.map} +0 -0
  390. /package/dist/{chunk-OLUG4IFF.js.map → chunk-D2RLPRSG.js.map} +0 -0
  391. /package/dist/{chunk-74NIHMAT.js.map → chunk-D653JH6D.js.map} +0 -0
  392. /package/dist/{chunk-2LUO22FR.js.map → chunk-DAI5CTQF.js.map} +0 -0
  393. /package/dist/{chunk-HTKVYEVB.js.map → chunk-DBM4ZXDN.js.map} +0 -0
  394. /package/dist/{chunk-MB2DTUEG.js.map → chunk-DNYKLU3K.js.map} +0 -0
  395. /package/dist/{chunk-26P7B46A.js.map → chunk-DXYARXK3.js.map} +0 -0
  396. /package/dist/{chunk-JQKURO3S.js.map → chunk-E4MQRQ6Z.js.map} +0 -0
  397. /package/dist/{chunk-KW53DBY7.js.map → chunk-EBW2E32G.js.map} +0 -0
  398. /package/dist/{chunk-O73DRA77.js.map → chunk-ENKBO7FC.js.map} +0 -0
  399. /package/dist/{chunk-FVCLVASD.js.map → chunk-FI3KRVOF.js.map} +0 -0
  400. /package/dist/{chunk-WWM7WVD2.js.map → chunk-GABFKHQE.js.map} +0 -0
  401. /package/dist/{chunk-32HA7DXJ.js.map → chunk-GNBRB3DH.js.map} +0 -0
  402. /package/dist/{chunk-CEQUNJJY.js.map → chunk-HPALK6LD.js.map} +0 -0
  403. /package/dist/{chunk-LR7U3HCD.js.map → chunk-HPSATFLQ.js.map} +0 -0
  404. /package/dist/{chunk-O4AWXTZQ.js.map → chunk-HUYLQW75.js.map} +0 -0
  405. /package/dist/{chunk-ZZUKJ5HQ.js.map → chunk-IG2ERMSB.js.map} +0 -0
  406. /package/dist/{chunk-2SZ3KC5S.js.map → chunk-IMKIIS4Y.js.map} +0 -0
  407. /package/dist/{chunk-CA64OZLZ.js.map → chunk-IRXPXDY5.js.map} +0 -0
  408. /package/dist/{chunk-I57HZMMA.js.map → chunk-IXIE3H43.js.map} +0 -0
  409. /package/dist/{chunk-OWJD3V4V.js.map → chunk-J3YQ3554.js.map} +0 -0
  410. /package/dist/{chunk-BCFHMBMK.js.map → chunk-J5HZBHU4.js.map} +0 -0
  411. /package/dist/{chunk-UHMMRQGU.js.map → chunk-KD7GNHOP.js.map} +0 -0
  412. /package/dist/{chunk-X7Z4B5GK.js.map → chunk-KPL7EXS6.js.map} +0 -0
  413. /package/dist/{chunk-GTR2WUBA.js.map → chunk-KX6M5CJI.js.map} +0 -0
  414. /package/dist/{chunk-CYO5HL4C.js.map → chunk-KYL2BPS2.js.map} +0 -0
  415. /package/dist/{chunk-QLDMF7MH.js.map → chunk-LC5HE7PZ.js.map} +0 -0
  416. /package/dist/{chunk-4ICPJZAB.js.map → chunk-LMYSYG6U.js.map} +0 -0
  417. /package/dist/{chunk-3ZZOE4SN.js.map → chunk-LOCDYMD6.js.map} +0 -0
  418. /package/dist/{chunk-DNWBLT4U.js.map → chunk-MASBMJ2M.js.map} +0 -0
  419. /package/dist/{chunk-NZMQDUOD.js.map → chunk-MU6HYDNF.js.map} +0 -0
  420. /package/dist/{chunk-GMVKCEBG.js.map → chunk-MY6QEDZ2.js.map} +0 -0
  421. /package/dist/{chunk-SK57RWSA.js.map → chunk-MZGKQ5WV.js.map} +0 -0
  422. /package/dist/{chunk-HSQRYJ4P.js.map → chunk-N52SFH2K.js.map} +0 -0
  423. /package/dist/{chunk-MY6VZVCP.js.map → chunk-OAJX5CEL.js.map} +0 -0
  424. /package/dist/{chunk-FH75NNLB.js.map → chunk-OZ5NAXCP.js.map} +0 -0
  425. /package/dist/{chunk-PEGNFZ3F.js.map → chunk-P5S777YI.js.map} +0 -0
  426. /package/dist/{chunk-LY6727IF.js.map → chunk-PPSVFNEA.js.map} +0 -0
  427. /package/dist/{chunk-K4FDZVMQ.js.map → chunk-PR42VIVY.js.map} +0 -0
  428. /package/dist/{chunk-I74J2UZ2.js.map → chunk-PWMZCW4H.js.map} +0 -0
  429. /package/dist/{chunk-6YDXUE5H.js.map → chunk-PYA2O3WS.js.map} +0 -0
  430. /package/dist/{chunk-GUIZDI5T.js.map → chunk-Q2PA4KZS.js.map} +0 -0
  431. /package/dist/{chunk-JJ7VMIEQ.js.map → chunk-QCANSTLU.js.map} +0 -0
  432. /package/dist/{chunk-DZH2HTTB.js.map → chunk-R27GW5WV.js.map} +0 -0
  433. /package/dist/{chunk-DAQG3C4W.js.map → chunk-R6JY22SG.js.map} +0 -0
  434. /package/dist/{chunk-7OIBLMDP.js.map → chunk-RAQISW42.js.map} +0 -0
  435. /package/dist/{chunk-CPKD2YGK.js.map → chunk-RNU5CUPI.js.map} +0 -0
  436. /package/dist/{chunk-467VRP4E.js.map → chunk-RO5YZQHZ.js.map} +0 -0
  437. /package/dist/{chunk-SSZROOOI.js.map → chunk-ROLXYGT5.js.map} +0 -0
  438. /package/dist/{chunk-6VITVQ23.js.map → chunk-SHJ5SQOW.js.map} +0 -0
  439. /package/dist/{chunk-F4B2YYCN.js.map → chunk-TAWFXRXB.js.map} +0 -0
  440. /package/dist/{chunk-G3FU2IF7.js.map → chunk-TB7UW5BB.js.map} +0 -0
  441. /package/dist/{chunk-NF6Q4XTN.js.map → chunk-TQF2I5SY.js.map} +0 -0
  442. /package/dist/{chunk-M7A226EQ.js.map → chunk-UL44R3W2.js.map} +0 -0
  443. /package/dist/{chunk-GTR5NLVP.js.map → chunk-VCRQPLKS.js.map} +0 -0
  444. /package/dist/{chunk-MHIYYHCL.js.map → chunk-VJU3ET7N.js.map} +0 -0
  445. /package/dist/{chunk-RXMIXKB3.js.map → chunk-WGJUARF3.js.map} +0 -0
  446. /package/dist/{chunk-LG67ZNRU.js.map → chunk-WK2ZCTUF.js.map} +0 -0
  447. /package/dist/{chunk-XGE75VPK.js.map → chunk-WNARM3LY.js.map} +0 -0
  448. /package/dist/{chunk-RNBH5TSM.js.map → chunk-WQ2RTQYO.js.map} +0 -0
  449. /package/dist/{chunk-W57QFLQD.js.map → chunk-X6JIGNLI.js.map} +0 -0
  450. /package/dist/{chunk-CKA4SJRK.js.map → chunk-XIQDCQX7.js.map} +0 -0
  451. /package/dist/{chunk-AJ7PY4FJ.js.map → chunk-Y3GCG6CA.js.map} +0 -0
  452. /package/dist/{chunk-ZRB4M3GG.js.map → chunk-YC5KH7ZB.js.map} +0 -0
  453. /package/dist/{chunk-4RY64KQY.js.map → chunk-YJUKLLNP.js.map} +0 -0
  454. /package/dist/{chunk-24POWVUY.js.map → chunk-ZDJNRRVS.js.map} +0 -0
  455. /package/dist/{chunk-M7YEZR6X.js.map → chunk-ZITMDA4C.js.map} +0 -0
  456. /package/dist/{chat-interactive-block-Z22YHDRU.js.map → computer-viewer-6CRLWQHP.js.map} +0 -0
  457. /package/dist/{computer-viewer-BWVNDFTO.js.map → embedded-new-voice-KILV3RYM.js.map} +0 -0
  458. /package/dist/{embedded-new-voice-GBXSZ3TO.js.map → embedded-voice-GHDRLCFX.js.map} +0 -0
  459. /package/dist/{fleet-decision-row-35S3GAUN.js.map → fleet-decision-row-DQXRRBT7.js.map} +0 -0
  460. /package/dist/{embedded-voice-MGI7EK7C.js.map → model-policy-picker-menu-5ALKUJMR.js.map} +0 -0
  461. /package/dist/{platform-activity-row-ISMXR7NJ.js.map → platform-activity-row-227C764Z.js.map} +0 -0
  462. /package/dist/{queue-surface-implementation-ULW7DDJA.js.map → queue-surface-implementation-H3NOBXU5.js.map} +0 -0
  463. /package/dist/{model-policy-picker-menu-KP53N6UJ.js.map → sandbox-files-5FEOHUMD.js.map} +0 -0
  464. /package/dist/{sandbox-files-QYSD4XCZ.js.map → session-realtime-OHBMODNX.js.map} +0 -0
  465. /package/dist/{timeline-annotation-markers-OJYKHBEO.js.map → timeline-annotation-markers-NDHS7MCK.js.map} +0 -0
  466. /package/dist/{timeline-annotation-selection-6OAZLXFU.js.map → timeline-annotation-selection-DRQSTJOV.js.map} +0 -0
  467. /package/dist/{timeline-annotations-dialog-5M6DP5FX.js.map → timeline-annotations-dialog-G6J5ZJKN.js.map} +0 -0
@@ -9,7 +9,13 @@ import {
9
9
  type Chord,
10
10
  } from "./chords";
11
11
  import type { ChatRecord } from "./chat-directory";
12
- import { isSwitcherKey, openChatSwitcher, stepChatSwitcher, SWITCHER_LIMIT } from "./chat-switcher";
12
+ import {
13
+ extendChatSwitcher,
14
+ isSwitcherKey,
15
+ openChatSwitcher,
16
+ stepChatSwitcher,
17
+ SWITCHER_LIMIT,
18
+ } from "./chat-switcher";
13
19
  import { LOOSE_MATCH_BELOW, rankLabel, rankWithKeywords } from "./rank";
14
20
  import { appActions, stepWorkspace } from "./registry";
15
21
 
@@ -296,9 +302,58 @@ describe("chat switcher", () => {
296
302
  expect(state.ids[state.index]).toBe("b");
297
303
  });
298
304
 
299
- test("the list stops at ten chats", () => {
300
- const ids = Array.from({ length: 15 }, (_, index) => `c${index}`);
301
- expect(openChatSwitcher(directory(ids), "c0", false)!.ids).toHaveLength(SWITCHER_LIMIT);
305
+ test("past the opened chats it carries on through other chats by latest activity", () => {
306
+ const rows = [
307
+ record("a", { updatedAt: 1 }),
308
+ record("b", { updatedAt: 2 }),
309
+ record("older", { updatedAt: 5 }),
310
+ record("newer", { updatedAt: 9, workspaceId: "other" }),
311
+ record("gone", { updatedAt: 8, archived: true }),
312
+ record("child", { updatedAt: 7, row: { parentSessionId: "a" } as ChatRecord["row"] }),
313
+ ];
314
+ const state = openChatSwitcher(directory(["a", "b"], rows), "a", false)!;
315
+ expect(state.ids).toEqual(["a", "b", "newer", "older"]);
316
+ expect(state.viewed).toBe(2);
317
+ // Tab keeps going into the older chats before it wraps.
318
+ let stepped = stepChatSwitcher(state, 1);
319
+ stepped = stepChatSwitcher(stepped, 1);
320
+ expect(stepped.ids[stepped.index]).toBe("older");
321
+ });
322
+
323
+ test("with no opened chats yet, the most recently active chat is the target", () => {
324
+ const rows = [record("a", { updatedAt: 1 }), record("b", { updatedAt: 2 })];
325
+ const state = openChatSwitcher(directory([], rows), null, false)!;
326
+ expect(state.ids).toEqual(["b", "a"]);
327
+ expect(state.viewed).toBe(0);
328
+ expect(state.ids[state.index]).toBe("b");
329
+ });
330
+
331
+ test("chats that load while it is open join at the end, and nothing else moves", () => {
332
+ const start = directory(["a", "b"], [record("a"), record("b"), record("c", { updatedAt: 3 })]);
333
+ const state = stepChatSwitcher(openChatSwitcher(start, "a", false)!, 1);
334
+ expect(extendChatSwitcher(state, start)).toBe(state);
335
+ const later = directory(
336
+ ["a", "b"],
337
+ [record("a"), record("b"), record("c", { updatedAt: 3 }), record("d", { updatedAt: 9 })],
338
+ );
339
+ const grown = extendChatSwitcher(state, later);
340
+ expect(grown.ids).toEqual(["a", "b", "c", "d"]);
341
+ expect(grown.index).toBe(state.index);
342
+ expect(grown.viewed).toBe(2);
343
+ });
344
+
345
+ test("the list stops at its limit", () => {
346
+ const ids = Array.from({ length: SWITCHER_LIMIT + 20 }, (_, index) => `c${index}`);
347
+ const rows = Array.from({ length: 30 }, (_, index) =>
348
+ record(`x${index}`, { updatedAt: index }),
349
+ );
350
+ const state = openChatSwitcher(
351
+ directory(ids, [...ids.map((id) => record(id)), ...rows]),
352
+ "c0",
353
+ false,
354
+ )!;
355
+ expect(state.ids).toHaveLength(SWITCHER_LIMIT);
356
+ expect(extendChatSwitcher(state, directory(ids, rows))).toBe(state);
302
357
  });
303
358
 
304
359
  test("only Control+Tab opens it", () => {
@@ -5,50 +5,103 @@
5
5
  * chat (press again to return); holding Control shows the list, each Tab moves
6
6
  * down it (Shift+Tab up), and releasing Control opens the highlighted chat.
7
7
  *
8
- * The list is read once when the switcher opens, so it holds still while the
9
- * person cycles through it.
8
+ * Past the chats someone opened, the list carries on through the other chats
9
+ * the app knows, most recently active first, so holding Tab keeps going back
10
+ * in time. The list is read when the switcher opens and only ever grows at
11
+ * the end (as more chats load), so it holds still while the person cycles.
10
12
  */
11
- import type { ChatDirectorySnapshot } from "./chat-directory";
13
+ import type { ChatDirectorySnapshot, ChatRecord } from "./chat-directory";
12
14
 
13
15
  /** At most this many chats; older ones are a search away in ⌘K. */
14
- export const SWITCHER_LIMIT = 10;
16
+ export const SWITCHER_LIMIT = 100;
15
17
  /** A press shorter than this switches without ever drawing the list. */
16
18
  export const SWITCHER_SHOW_DELAY_MS = 140;
17
19
 
18
20
  export type ChatSwitcherState = Readonly<{
19
- /** Chats in the order shown, most recently viewed first. */
21
+ /** Chats in the order shown: recently viewed first, then recently active. */
20
22
  ids: readonly string[];
23
+ /** How many of `ids`, from the front, are chats the person opened recently. */
24
+ viewed: number;
21
25
  /** The highlighted chat. */
22
26
  index: number;
23
27
  /** Whether the list is drawn (after the delay, or once Tab is pressed again). */
24
28
  visible: boolean;
25
29
  }>;
26
30
 
31
+ type SwitcherDirectory = Pick<ChatDirectorySnapshot, "recent" | "rows">;
32
+
33
+ function switchable(
34
+ record: ChatRecord | undefined,
35
+ reachable: (workspaceId: string) => boolean,
36
+ ): boolean {
37
+ if (!record || record.archived || !reachable(record.workspaceId)) return false;
38
+ // Subagent chats live under their parent, as in the sidebar.
39
+ return !record.row || record.row.parentSessionId === null;
40
+ }
41
+
42
+ /** Known chats not in `taken`, most recently active first. */
43
+ function moreChats(
44
+ directory: SwitcherDirectory,
45
+ taken: ReadonlySet<string>,
46
+ reachable: (workspaceId: string) => boolean,
47
+ room: number,
48
+ ): string[] {
49
+ if (room <= 0) return [];
50
+ const more: ChatRecord[] = [];
51
+ for (const record of directory.rows.values()) {
52
+ if (!taken.has(record.id) && switchable(record, reachable)) more.push(record);
53
+ }
54
+ more.sort((a, b) => b.updatedAt - a.updatedAt || (a.id < b.id ? -1 : 1));
55
+ return more.slice(0, room).map((record) => record.id);
56
+ }
57
+
27
58
  /**
28
- * The chats to switch between, most recently viewed first, with the open chat
29
- * (when there is one) at the front. Null when there is nowhere to switch to.
59
+ * The chats to switch between: the open chat (when there is one), the other
60
+ * recently viewed chats, then the rest the app knows by latest activity.
61
+ * Null when there is nowhere to switch to.
30
62
  */
31
63
  export function openChatSwitcher(
32
- directory: Pick<ChatDirectorySnapshot, "recent" | "rows">,
64
+ directory: SwitcherDirectory,
33
65
  currentChatId: string | null,
34
66
  backwards: boolean,
35
67
  /** Whether a workspace is still open to the person; chats elsewhere are left out. */
36
68
  reachable: (workspaceId: string) => boolean = () => true,
37
69
  ): ChatSwitcherState | null {
38
- const switchable = directory.recent.filter((id) => {
39
- const record = directory.rows.get(id);
40
- if (!record || record.archived || !reachable(record.workspaceId)) return false;
41
- // Subagent chats live under their parent, as in the sidebar.
42
- return !record.row || record.row.parentSessionId === null;
43
- });
44
- const ids = currentChatId
45
- ? [currentChatId, ...switchable.filter((id) => id !== currentChatId)]
46
- : switchable;
47
- const limited = ids.slice(0, SWITCHER_LIMIT);
70
+ const recent = directory.recent.filter(
71
+ (id) => id !== currentChatId && switchable(directory.rows.get(id), reachable),
72
+ );
73
+ const viewed = (currentChatId ? [currentChatId, ...recent] : recent).slice(0, SWITCHER_LIMIT);
74
+ const ids = [
75
+ ...viewed,
76
+ ...moreChats(directory, new Set(viewed), reachable, SWITCHER_LIMIT - viewed.length),
77
+ ];
48
78
  // Away from a chat (the inbox, a new chat) the most recent chat is the target.
49
79
  const first = currentChatId ? 1 : 0;
50
- if (limited.length <= first) return null;
51
- return { ids: limited, index: backwards ? limited.length - 1 : first, visible: false };
80
+ if (ids.length <= first) return null;
81
+ return {
82
+ ids,
83
+ viewed: viewed.length,
84
+ index: backwards ? ids.length - 1 : first,
85
+ visible: false,
86
+ };
87
+ }
88
+
89
+ /**
90
+ * Chats that loaded while the list is open join at its end, so nothing the
91
+ * person is looking at moves. The same state when there is nothing new.
92
+ */
93
+ export function extendChatSwitcher(
94
+ state: ChatSwitcherState,
95
+ directory: SwitcherDirectory,
96
+ reachable: (workspaceId: string) => boolean = () => true,
97
+ ): ChatSwitcherState {
98
+ const more = moreChats(
99
+ directory,
100
+ new Set(state.ids),
101
+ reachable,
102
+ SWITCHER_LIMIT - state.ids.length,
103
+ );
104
+ return more.length === 0 ? state : { ...state, ids: [...state.ids, ...more] };
52
105
  }
53
106
 
54
107
  /** Tab moves down the list and Shift+Tab up, wrapping at the ends. */
@@ -0,0 +1,183 @@
1
+ import { expect, mock, test } from "bun:test";
2
+ import type { OpenGeniBrowserClient } from "@opengeni/sdk/browser";
3
+ import {
4
+ beginDriveConnect,
5
+ resolveDriveConnectReturn,
6
+ completeDriveConnectReturn,
7
+ } from "./drive-connect-return";
8
+
9
+ function fixture() {
10
+ const values = new Map<string, string>();
11
+ const storage = {
12
+ getItem: (key: string) => values.get(key) ?? null,
13
+ setItem: (key: string, value: string) => {
14
+ values.set(key, value);
15
+ },
16
+ removeItem: (key: string) => {
17
+ values.delete(key);
18
+ },
19
+ };
20
+ let outcome = {
21
+ id: "attempt",
22
+ providerId: "google-drive",
23
+ ownership: "personal",
24
+ state: "complete",
25
+ credentialsCommitted: true,
26
+ account: { id: "exact-account", providerId: "google-drive", ownership: "personal" },
27
+ } as Record<string, unknown>;
28
+ const beginConnect = mock(async () => ({
29
+ id: "attempt",
30
+ revision: 1,
31
+ state: "credential_input",
32
+ }));
33
+ const advanceConnectAttempt = mock(async () => ({
34
+ id: "attempt",
35
+ nextAction: { type: "authorize", url: "https://accounts.google.com/consent" },
36
+ }));
37
+ const getConnectAttempt = mock(async () => outcome);
38
+ const client = {
39
+ beginConnect,
40
+ connectTransport: () => ({ advance: advanceConnectAttempt, get: getConnectAttempt }),
41
+ } as unknown as OpenGeniBrowserClient;
42
+ return {
43
+ storage,
44
+ values,
45
+ client,
46
+ beginConnect,
47
+ advanceConnectAttempt,
48
+ getConnectAttempt,
49
+ setOutcome: (value: Record<string, unknown>) => {
50
+ outcome = value;
51
+ },
52
+ };
53
+ }
54
+
55
+ test("Drive return reads the exact durable account, scoped to its original workspace and return nonce", async () => {
56
+ const f = fixture();
57
+ expect(
58
+ await beginDriveConnect({
59
+ client: f.client,
60
+ workspaceId: "display",
61
+ connectionWorkspaceId: "owner",
62
+ itemId: "drive",
63
+ reconnectAccountId: "existing",
64
+ returnUrl: "https://app.example/settings?connect_item=drive",
65
+ storage: f.storage,
66
+ }),
67
+ ).toBe("https://accounts.google.com/consent");
68
+ expect(f.beginConnect).toHaveBeenCalledWith(
69
+ "owner",
70
+ expect.objectContaining({
71
+ providerId: "google-drive",
72
+ ownership: "personal",
73
+ reconnectAccountId: "existing",
74
+ }),
75
+ );
76
+ expect(f.advanceConnectAttempt).toHaveBeenCalledWith(
77
+ "owner",
78
+ "attempt",
79
+ expect.objectContaining({ expectedRevision: 1, action: { type: "credentials", values: {} } }),
80
+ );
81
+ const receipt = JSON.parse([...f.values.values()][0]!);
82
+ await expect(
83
+ resolveDriveConnectReturn({
84
+ client: f.client,
85
+ workspaceId: "display",
86
+ nonce: "wrong",
87
+ storage: f.storage,
88
+ }),
89
+ ).rejects.toThrow("could not be matched");
90
+ expect(f.getConnectAttempt).not.toHaveBeenCalled();
91
+ await expect(
92
+ resolveDriveConnectReturn({
93
+ client: f.client,
94
+ workspaceId: "other",
95
+ nonce: receipt.nonce,
96
+ storage: f.storage,
97
+ }),
98
+ ).rejects.toThrow("could not be matched");
99
+ expect(
100
+ await resolveDriveConnectReturn({
101
+ client: f.client,
102
+ workspaceId: "display",
103
+ nonce: receipt.nonce,
104
+ storage: f.storage,
105
+ }),
106
+ ).toEqual({ itemId: "drive", connectionId: "exact-account" });
107
+ expect(f.getConnectAttempt).toHaveBeenCalledWith("owner", "attempt");
108
+ // StrictMode/effect cleanup can discard the first reader. The locator remains
109
+ // until its active consumer confirms the connector was enabled.
110
+ expect(
111
+ await resolveDriveConnectReturn({
112
+ client: f.client,
113
+ workspaceId: "display",
114
+ nonce: receipt.nonce,
115
+ storage: f.storage,
116
+ }),
117
+ ).toEqual({ itemId: "drive", connectionId: "exact-account" });
118
+ completeDriveConnectReturn("display", "different-return", f.storage);
119
+ expect(f.values.size).toBe(1);
120
+ completeDriveConnectReturn("display", receipt.nonce, f.storage);
121
+ expect(f.values.size).toBe(0);
122
+ });
123
+
124
+ test("failed or incomplete Drive returns never produce an account to auto-enable", async () => {
125
+ for (const state of ["failed", "requires_user_action", "complete"]) {
126
+ const f = fixture();
127
+ await beginDriveConnect({
128
+ client: f.client,
129
+ workspaceId: "workspace",
130
+ itemId: "drive",
131
+ returnUrl: "https://app.example/settings",
132
+ storage: f.storage,
133
+ });
134
+ const receipt = JSON.parse([...f.values.values()][0]!);
135
+ f.setOutcome({
136
+ id: "attempt",
137
+ providerId: "google-drive",
138
+ ownership: "personal",
139
+ state,
140
+ credentialsCommitted: false,
141
+ error: { message: "Provider rejected access" },
142
+ });
143
+ await expect(
144
+ resolveDriveConnectReturn({
145
+ client: f.client,
146
+ workspaceId: "workspace",
147
+ nonce: receipt.nonce,
148
+ storage: f.storage,
149
+ }),
150
+ ).rejects.toThrow();
151
+ expect(f.values.size).toBe(state === "failed" ? 0 : 1);
152
+ }
153
+ });
154
+
155
+ test("a different provider or ownership cannot satisfy a saved Drive return", async () => {
156
+ for (const patch of [{ providerId: "mcp-oauth" }, { ownership: "workspace" }]) {
157
+ const f = fixture();
158
+ await beginDriveConnect({
159
+ client: f.client,
160
+ workspaceId: "workspace",
161
+ itemId: "drive",
162
+ returnUrl: "https://app.example/settings",
163
+ storage: f.storage,
164
+ });
165
+ const receipt = JSON.parse([...f.values.values()][0]!);
166
+ f.setOutcome({
167
+ id: "attempt",
168
+ providerId: "google-drive",
169
+ ownership: "personal",
170
+ state: "complete",
171
+ credentialsCommitted: true,
172
+ ...patch,
173
+ });
174
+ await expect(
175
+ resolveDriveConnectReturn({
176
+ client: f.client,
177
+ workspaceId: "workspace",
178
+ nonce: receipt.nonce,
179
+ storage: f.storage,
180
+ }),
181
+ ).rejects.toThrow("account changed");
182
+ }
183
+ });
@@ -0,0 +1,116 @@
1
+ import type { OpenGeniBrowserClient } from "@opengeni/sdk/browser";
2
+
3
+ export const DRIVE_MCP_URL = "https://drivemcp.googleapis.com/mcp/v1";
4
+ export const DRIVE_CONNECT_RETURN_PARAM = "drive_connect_return";
5
+ const storageKey = (workspaceId: string) => `opengeni.drive-connect.v1:${workspaceId}`;
6
+ type Receipt = { nonce: string; attemptId: string; connectionWorkspaceId: string; itemId: string };
7
+ type Storage = Pick<globalThis.Storage, "getItem" | "setItem" | "removeItem">;
8
+
9
+ /** Browser state is only a locator. Completion and the account come from Connect. */
10
+ export async function beginDriveConnect(input: {
11
+ client: Pick<OpenGeniBrowserClient, "beginConnect" | "connectTransport">;
12
+ workspaceId: string;
13
+ connectionWorkspaceId?: string;
14
+ itemId: string;
15
+ reconnectAccountId?: string;
16
+ returnUrl: string;
17
+ storage: Storage;
18
+ }): Promise<string> {
19
+ const nonce = crypto.randomUUID();
20
+ const returnUrl = new URL(input.returnUrl);
21
+ returnUrl.searchParams.set(DRIVE_CONNECT_RETURN_PARAM, nonce);
22
+ const connectionWorkspaceId = input.connectionWorkspaceId ?? input.workspaceId;
23
+ const created = await input.client.beginConnect(connectionWorkspaceId, {
24
+ providerId: "google-drive",
25
+ ownership: "personal",
26
+ returnUrl: returnUrl.toString(),
27
+ idempotencyKey: crypto.randomUUID(),
28
+ ...(input.reconnectAccountId ? { reconnectAccountId: input.reconnectAccountId } : {}),
29
+ });
30
+ if (created.state !== "credential_input")
31
+ throw new Error("Google Drive setup did not start. Try again.");
32
+ const attempt = await input.client.connectTransport().advance(connectionWorkspaceId, created.id, {
33
+ expectedRevision: created.revision,
34
+ idempotencyKey: crypto.randomUUID(),
35
+ action: { type: "credentials", values: {} },
36
+ });
37
+ if (attempt.nextAction.type !== "authorize")
38
+ throw new Error(attempt.error?.message ?? "Google Drive did not return an authorization link.");
39
+ input.storage.setItem(
40
+ storageKey(input.workspaceId),
41
+ JSON.stringify({
42
+ nonce,
43
+ attemptId: attempt.id,
44
+ connectionWorkspaceId,
45
+ itemId: input.itemId,
46
+ } satisfies Receipt),
47
+ );
48
+ return attempt.nextAction.url;
49
+ }
50
+
51
+ export async function resolveDriveConnectReturn(input: {
52
+ client: Pick<OpenGeniBrowserClient, "connectTransport">;
53
+ workspaceId: string;
54
+ nonce: string;
55
+ storage: Storage;
56
+ }): Promise<{ itemId: string; connectionId: string }> {
57
+ const key = storageKey(input.workspaceId);
58
+ const raw = input.storage.getItem(key);
59
+ let receipt: Receipt;
60
+ try {
61
+ receipt = JSON.parse(raw ?? "null");
62
+ if (
63
+ !receipt ||
64
+ receipt.nonce !== input.nonce ||
65
+ ![receipt.attemptId, receipt.connectionWorkspaceId, receipt.itemId].every(
66
+ (value) => typeof value === "string" && value.length > 0 && value.length <= 4096,
67
+ )
68
+ )
69
+ throw new Error();
70
+ } catch {
71
+ throw new Error(
72
+ "The Google Drive setup return could not be matched. Open Drive to check your connection.",
73
+ );
74
+ }
75
+ const attempt = await input.client
76
+ .connectTransport()
77
+ .get(receipt.connectionWorkspaceId, receipt.attemptId);
78
+ if (
79
+ attempt.id !== receipt.attemptId ||
80
+ attempt.providerId !== "google-drive" ||
81
+ attempt.ownership !== "personal"
82
+ ) {
83
+ throw new Error("The Google Drive setup account changed. Open Drive to try again.");
84
+ }
85
+ if (["failed", "expired", "cancelled"].includes(attempt.state)) {
86
+ input.storage.removeItem(key);
87
+ throw new Error(
88
+ attempt.error?.message ??
89
+ "Google Drive connection was not completed. Open Drive to try again.",
90
+ );
91
+ }
92
+ if (
93
+ attempt.state !== "complete" ||
94
+ !attempt.credentialsCommitted ||
95
+ attempt.account?.providerId !== "google-drive" ||
96
+ attempt.account.ownership !== "personal" ||
97
+ !attempt.account.id
98
+ ) {
99
+ throw new Error("Google Drive setup is still incomplete. Reload to check it again.");
100
+ }
101
+ return { itemId: receipt.itemId, connectionId: attempt.account.id };
102
+ }
103
+
104
+ /** Clear only after the UI has finished enabling; never consume a newer attempt. */
105
+ export function completeDriveConnectReturn(
106
+ workspaceId: string,
107
+ nonce: string,
108
+ storage: Storage,
109
+ ): void {
110
+ const key = storageKey(workspaceId);
111
+ try {
112
+ if (JSON.parse(storage.getItem(key) ?? "null")?.nonce === nonce) storage.removeItem(key);
113
+ } catch {
114
+ /* An absent or replaced browser locator grants no authority. */
115
+ }
116
+ }
@@ -57,6 +57,7 @@ import {
57
57
  // composer. Enter queues and Cmd/Ctrl+Enter steers; failed sessions stay
58
58
  // honest (reason + retry history) and revivable from the same composer.
59
59
  import { LightboxProvider } from "../../timeline";
60
+ import { connectorLogo, connectorLogoIndex } from "../../timeline/connector-logo-index";
60
61
  import { sessionAdmissionBlocked, sessionDisplayStatus } from "../../session-status-model";
61
62
  import { useSessionSecrets } from "../../hooks/use-vaults";
62
63
  import type { WorkspaceTab } from "../../components/workspace-dock";
@@ -182,7 +183,6 @@ import type {
182
183
  SessionEditableArtifactsStatus,
183
184
  } from "../components/session/editable-artifacts-workspace";
184
185
  import {
185
- normalizeProviderDomain,
186
186
  oauthConnectionOwnership,
187
187
  oauthConnectionRef,
188
188
  catalogConnectionAccountSelection,
@@ -1159,20 +1159,15 @@ export function SessionRoute({
1159
1159
  // The workspace shell already needs the capability catalog for session tool
1160
1160
  // policy. Reuse that authoritative read for timeline logos instead of
1161
1161
  // downloading the same large catalog again from the session route.
1162
- const providerLogos = useMemo(() => {
1163
- const logos = new Map<string, string>();
1164
- for (const capability of context.workspaceCapabilityCatalog) {
1165
- const domain = capability.providerDomain ?? capability.connectionRef?.providerDomain ?? null;
1166
- const url = context.client.catalogAssetUrl(capability.logoAssetPath);
1167
- if (domain && url) {
1168
- const key = normalizeProviderDomain(domain);
1169
- if (!logos.has(key)) logos.set(key, url);
1170
- }
1171
- }
1172
- return logos;
1173
- }, [context.client, context.workspaceCapabilityCatalog]);
1162
+ const providerLogos = useMemo(
1163
+ () =>
1164
+ connectorLogoIndex(context.workspaceCapabilityCatalog, (path) =>
1165
+ context.client.catalogAssetUrl(path ?? null),
1166
+ ),
1167
+ [context.client, context.workspaceCapabilityCatalog],
1168
+ );
1174
1169
  const resolveProviderLogo = useCallback(
1175
- (domain: string) => providerLogos.get(normalizeProviderDomain(domain)) ?? null,
1170
+ (domain: string) => connectorLogo(providerLogos, domain),
1176
1171
  [providerLogos],
1177
1172
  );
1178
1173
  // One lineage read feeds the single composer-anchored agents surface. Events
@@ -4027,6 +4022,9 @@ function SessionChatPane(props: {
4027
4022
  sessionKey={props.session.id}
4028
4023
  menuSide="top"
4029
4024
  connectModelsHref={`/workspaces/${encodeURIComponent(props.session.workspaceId)}/settings?section=models`}
4025
+ claudeSubscriptionAvailable={
4026
+ context.clientConfig.claudeSubscriptionEnabled === true
4027
+ }
4030
4028
  codexOnly={props.session.codexCompactionMode === "remote_v2"}
4031
4029
  onModelChange={composer.setModel}
4032
4030
  onEffortChange={composer.setReasoningEffort}
@@ -2228,6 +2228,7 @@ function SessionModelControl({
2228
2228
  error={modelCatalog.error ?? policyError}
2229
2229
  menuSide="bottom"
2230
2230
  connectModelsHref={`/workspaces/${encodeURIComponent(workspaceId)}/settings?section=models`}
2231
+ claudeSubscriptionAvailable={context.clientConfig.claudeSubscriptionEnabled === true}
2231
2232
  onModelChange={(model) => {
2232
2233
  onPolicyChosen();
2233
2234
  context.setModel(model);
@@ -181,7 +181,11 @@ export function ModelPolicyPickerMenu(props: ModelPolicyPickerProps) {
181
181
  ) : null}
182
182
  </>
183
183
  ) : props.connectModelsHref || rows.length > 0 ? (
184
- <ConnectModelsPanel href={props.connectModelsHref} messages={messages} />
184
+ <ConnectModelsPanel
185
+ href={props.connectModelsHref}
186
+ claude={props.claudeSubscriptionAvailable === true}
187
+ messages={messages}
188
+ />
185
189
  ) : (
186
190
  <p className={MENU_NOTE_CLASS}>{messages.noModels}</p>
187
191
  )}
@@ -204,8 +208,14 @@ export function ModelPolicyPickerMenu(props: ModelPolicyPickerProps) {
204
208
 
205
209
  const CONNECT_MODEL_OPTIONS = (
206
210
  t: OpenGeniText,
211
+ claude: boolean,
207
212
  ): Array<{
208
- billingClass: "codex_subscription" | "supergrok_subscription" | "byok" | "external";
213
+ billingClass:
214
+ | "codex_subscription"
215
+ | "claude_subscription"
216
+ | "supergrok_subscription"
217
+ | "byok"
218
+ | "external";
209
219
  title: string;
210
220
  detail: string;
211
221
  }> => [
@@ -214,6 +224,15 @@ const CONNECT_MODEL_OPTIONS = (
214
224
  title: t("modelPolicyPickerMenu.codex"),
215
225
  detail: "ChatGPT",
216
226
  },
227
+ ...(claude
228
+ ? [
229
+ {
230
+ billingClass: "claude_subscription" as const,
231
+ title: t("modelPolicyPicker.claude"),
232
+ detail: "Anthropic",
233
+ },
234
+ ]
235
+ : []),
217
236
  { billingClass: "supergrok_subscription", title: "SuperGrok", detail: "xAI" },
218
237
  {
219
238
  billingClass: "byok",
@@ -225,6 +244,7 @@ const CONNECT_MODEL_OPTIONS = (
225
244
 
226
245
  function ConnectModelsPanel(props: {
227
246
  href: string | undefined;
247
+ claude: boolean;
228
248
  messages: ReturnType<typeof defaultModelPolicyPickerMessages>;
229
249
  }) {
230
250
  const t = useOpenGeniText();
@@ -239,7 +259,7 @@ function ConnectModelsPanel(props: {
239
259
  </p>
240
260
  </div>
241
261
  <div className="grid grid-cols-2 gap-1">
242
- {CONNECT_MODEL_OPTIONS(t).map((option) => {
262
+ {CONNECT_MODEL_OPTIONS(t, props.claude).map((option) => {
243
263
  const content = (
244
264
  <>
245
265
  <span className="flex size-8 shrink-0 items-center justify-center rounded-og-md bg-og-surface-2 text-og-fg">
@@ -260,7 +280,8 @@ function ConnectModelsPanel(props: {
260
280
  </>
261
281
  );
262
282
  const className =
263
- "flex min-w-0 items-center gap-2 rounded-og-md px-1.5 py-1.5 outline-hidden transition-colors hover:bg-og-hover focus-visible:ring-2 focus-visible:ring-og-accent/40";
283
+ // An odd last tile spans both columns instead of sitting alone.
284
+ "flex min-w-0 items-center gap-2 rounded-og-md px-1.5 py-1.5 outline-hidden transition-colors odd:last:col-span-2 hover:bg-og-hover focus-visible:ring-2 focus-visible:ring-og-accent/40";
264
285
  return props.href ? (
265
286
  <a key={option.title} href={props.href} className={className}>
266
287
  {content}
@@ -96,6 +96,8 @@ export type ModelPolicyPickerProps = {
96
96
  allowModelOptions?: boolean | undefined;
97
97
  /** Settings → Models. Shown when the catalog has no model that can run. */
98
98
  connectModelsHref?: string | undefined;
99
+ /** The deployment offers Claude subscriptions; the connect panel lists Claude too. */
100
+ claudeSubscriptionAvailable?: boolean | undefined;
99
101
  /** Classes for the portalled menu surface. Prefer --og-* tokens for theming. */
100
102
  contentClassName?: string | undefined;
101
103
  /** Inline styles for the portalled menu, applied after inherited --og-* tokens. */