@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.
- package/dist/app/app-state.js +13 -13
- package/dist/app/components/Composer.js +14 -14
- package/dist/app/components/artifacts/artifact-library.js +5 -5
- package/dist/app/components/artifacts/chat-interactive-block.js +10 -10
- package/dist/app/components/artifacts/inline-chat-image.js +14 -14
- package/dist/app/components/artifacts/retained-file-preview.js +16 -16
- package/dist/app/components/artifacts/retained-text-preview.js +2 -2
- package/dist/app/components/artifacts/site-conversations.js +5 -4
- package/dist/app/components/artifacts/site-data.js +2 -2
- package/dist/app/components/artifacts/site-publishing.js +11 -11
- package/dist/app/components/artifacts/site-settings.js +15 -15
- package/dist/app/components/billing-class-mark.js +3 -3
- package/dist/app/components/capabilities/capability-detail-sheet.js +2 -2
- package/dist/app/components/capabilities/capability-logo-source.js +7 -7
- package/dist/app/components/capabilities/native-connect-readiness.js +1 -0
- package/dist/app/components/capabilities/native-connect-readiness.js.map +1 -1
- package/dist/app/components/capabilities/native-connect-setup.js +6 -6
- package/dist/app/components/capabilities/perform-capability-action.js +2 -1
- package/dist/app/components/capabilities/prepared-mcp-setup-card.js +8 -8
- package/dist/app/components/capabilities/session-capability-card.js +32 -31
- package/dist/app/components/capabilities/session-capability-card.js.map +1 -1
- package/dist/app/components/capabilities/session-chrome-card.js +14 -14
- package/dist/app/components/capabilities/session-custom-mcp-card.js +9 -9
- package/dist/app/components/capabilities/session-github-card.js +9 -9
- package/dist/app/components/capabilities/session-machine-card.js +14 -14
- package/dist/app/components/command-menu/chat-status.js +6 -5
- package/dist/app/components/command-menu/chat-switcher.d.ts +2 -0
- package/dist/app/components/command-menu/chat-switcher.js +26 -9
- package/dist/app/components/command-menu/chat-switcher.js.map +1 -1
- package/dist/app/components/command-menu/command-menu.js +16 -15
- package/dist/app/components/command-menu/command-menu.js.map +1 -1
- package/dist/app/components/command-menu/shortcut-sheet.js +8 -8
- package/dist/app/components/composer-mobile-plus-panel.js +5 -5
- package/dist/app/components/composer-mobile-plus.js +1 -1
- package/dist/app/components/credit-required-gate.js +1 -1
- package/dist/app/components/credit-required-prompt.js +4 -4
- package/dist/app/components/desktop/app-shots.js +5 -5
- package/dist/app/components/desktop/this-computer.js +4 -4
- package/dist/app/components/feedback.js +3 -3
- package/dist/app/components/github-app-connect-launcher.js +1 -1
- package/dist/app/components/machines/connect-machine-dialog.js +3 -3
- package/dist/app/components/markdown.js +4 -4
- package/dist/app/components/models/models-ui.js +2 -2
- package/dist/app/components/pickers.js +5 -5
- package/dist/app/components/rail/rail-context.js +3 -3
- package/dist/app/components/rail/rail-sections.js +28 -27
- package/dist/app/components/rail/session-header.d.ts +6 -5
- package/dist/app/components/rail/session-header.js +20 -19
- package/dist/app/components/rail/session-list.js +27 -26
- package/dist/app/components/rail/session-row-hover-details.js +7 -6
- package/dist/app/components/rail/session-shell.js +51 -53
- package/dist/app/components/rail/site-session-group-heading.js +2 -1
- package/dist/app/components/session/agent-configuration-panel.js +6 -6
- package/dist/app/components/session/banners.js +6 -6
- package/dist/app/components/session/conversation-find.js +3 -3
- package/dist/app/components/session/editable-artifacts-workspace.js +17 -17
- package/dist/app/components/session/failed-session-banner.js +5 -5
- package/dist/app/components/session/human-input.js +26 -26
- package/dist/app/components/session/inspector.js +37 -37
- package/dist/app/components/session/message-actions.js +3 -3
- package/dist/app/components/session/message-resource-attachments.js +5 -5
- package/dist/app/components/session/model-recovery-notice.js +3 -3
- package/dist/app/components/session/new-session-settings-menu.js +4 -4
- package/dist/app/components/session/sandbox-switcher.js +8 -8
- package/dist/app/components/session/sandbox-workspace.js +36 -36
- package/dist/app/components/session/search-markdown-highlight.js +5 -5
- package/dist/app/components/session/search-results-view.js +6 -6
- package/dist/app/components/session/session-admission-notice.js +4 -4
- package/dist/app/components/session/session-machine-access-notice.js +4 -4
- package/dist/app/components/session/session-search-dialog.js +6 -6
- package/dist/app/components/session/session-tenancy-control.js +4 -4
- package/dist/app/components/session/session-usage-limit-section.js +3 -3
- package/dist/app/components/session/session-wait-status.js +2 -1
- package/dist/app/components/session/session-wait-status.js.map +1 -1
- package/dist/app/components/session/subagents.js +6 -5
- package/dist/app/components/session/subscription-account-indicator.d.ts +35 -0
- package/dist/app/components/session/{codex-account-indicator.js → subscription-account-indicator.js} +23 -10
- package/dist/app/components/session/subscription-account-menu.d.ts +11 -12
- package/dist/app/components/session/subscription-account-menu.js +1 -1
- package/dist/app/components/session/subscription-account.d.ts +105 -0
- package/dist/app/components/usage/usage-entry.js +3 -3
- package/dist/app/components/workspace-composer-plus.js +10 -10
- package/dist/app/embed/session-app.js +154 -156
- package/dist/app/lib/actions/actions-provider.js +8 -8
- package/dist/app/lib/actions/chat-switcher.d.ts +19 -7
- package/dist/app/lib/codex-usage-query.js +2 -2
- package/dist/app/lib/composer-connectors.js +8 -8
- package/dist/app/lib/console-text.js +2 -2
- package/dist/app/lib/drive-connect-return.d.ts +26 -0
- package/dist/app/lib/drive-connect-return.js +16 -0
- package/dist/app/lib/events.js +7 -7
- package/dist/app/lib/failed-session-copy.js +3 -3
- package/dist/app/lib/model-access-onboarding.js +3 -3
- package/dist/app/lib/move-chat-to-machine.js +3 -3
- package/dist/app/lib/session-branch-cache.js +6 -5
- package/dist/app/lib/session-group-pagination.js +6 -5
- package/dist/app/lib/session-hover-facts.js +6 -5
- package/dist/app/lib/session-rail.js +2 -1
- package/dist/app/lib/sessions-group.js +5 -4
- package/dist/app/lib/turn-failure-analytics.js +5 -5
- package/dist/app/lib/use-workspace-machines.js +4 -4
- package/dist/app/lib/use-workspace-model-catalog.js +3 -3
- package/dist/app/lib/use-workspace-rigs.js +2 -2
- package/dist/app/lib/workspace-navigation-preference.js +4 -4
- package/dist/app/lib/workspace-scope-context.js +3 -3
- package/dist/app/routes/artifacts.js +50 -49
- package/dist/app/routes/artifacts.js.map +1 -1
- package/dist/app/routes/retained-artifact.js +23 -23
- package/dist/app/routes/session.js +92 -91
- package/dist/app/routes/sessions-index.js +52 -52
- package/dist/app/routes/site-view.js +35 -34
- package/dist/app/routes/site-view.js.map +1 -1
- package/dist/artifacts-document.js +4 -4
- package/dist/artifacts-presentation.js +4 -4
- package/dist/artifacts-spreadsheet.js +4 -4
- package/dist/artifacts.js +10 -10
- package/dist/{browser-viewer-YQC6BFMS.js → browser-viewer-VKIAULG7.js} +4 -4
- package/dist/{chat-interactive-block-Z22YHDRU.js → chat-interactive-block-JGYSW4KB.js} +5 -5
- package/dist/{chunk-WN2MIB4Q.js → chunk-22V2QCEM.js} +2 -2
- package/dist/{chunk-SJPAKWWP.js → chunk-2BISI664.js} +2 -2
- package/dist/{chunk-O5UOCMH4.js → chunk-2LPJVEWY.js} +3 -3
- package/dist/{chunk-NPDL4W4Y.js → chunk-2MLJHNKJ.js} +108 -109
- package/dist/chunk-2MLJHNKJ.js.map +1 -0
- package/dist/{chunk-JI6WPLZB.js → chunk-2SBCOGQN.js} +4 -4
- package/dist/{chunk-6JF7C3U3.js → chunk-2WERWWSS.js} +2 -2
- package/dist/{chunk-4XKT4ENZ.js → chunk-2Y3QL3DA.js} +4 -4
- package/dist/{chunk-KVMFT6FN.js → chunk-3FL3F4WF.js} +2 -2
- package/dist/{chunk-4VJOZJDV.js → chunk-3JPQAIYJ.js} +2 -2
- package/dist/{chunk-27EORKJ4.js → chunk-3N4LNBJF.js} +2 -2
- package/dist/{chunk-7JZSFJHB.js → chunk-3OJU3RUR.js} +2 -2
- package/dist/{chunk-NV5OYV3G.js → chunk-3U6K36QA.js} +5 -5
- package/dist/{chunk-MDRLP5DO.js → chunk-3W7ROHN4.js} +3 -3
- package/dist/{chunk-3MQM4ZOC.js → chunk-3Y6ZR2VN.js} +3 -3
- package/dist/{chunk-HXZIX7MR.js → chunk-47AB65IO.js} +4 -4
- package/dist/{chunk-DNYD7AUG.js → chunk-4BOLIZ7Y.js} +3 -3
- package/dist/{chunk-4XO346BW.js → chunk-4FBJFQFU.js} +2 -2
- package/dist/{chunk-BWKZEW6K.js → chunk-4T3E56NT.js} +3 -3
- package/dist/{chunk-4PC7LIQK.js → chunk-4ZMUWRFF.js} +3 -3
- package/dist/{chunk-TM2ZLY3Q.js → chunk-5OLFYSVG.js} +13 -13
- package/dist/{chunk-D4QOZXPM.js → chunk-5WBRM7V4.js} +5 -5
- package/dist/{chunk-GXK4CUAB.js → chunk-5WQKTT5Z.js} +2 -2
- package/dist/{chunk-6N7W3TOV.js → chunk-63BPMTBM.js} +2 -2
- package/dist/{chunk-R5YZ3ROM.js → chunk-63PQKTTT.js} +2 -2
- package/dist/{chunk-WLZVXCPZ.js → chunk-6GO6WN22.js} +3 -3
- package/dist/{chunk-6PQBKMUL.js → chunk-6IIA63VD.js} +11 -7
- package/dist/chunk-6IIA63VD.js.map +1 -0
- package/dist/{chunk-XDCZBOEY.js → chunk-6LGBGPBU.js} +1 -1
- package/dist/{chunk-FMATVQFW.js → chunk-6XXMG5YP.js} +9 -9
- package/dist/{chunk-M5E3CKSE.js → chunk-7QWWE5XL.js} +2 -2
- package/dist/{chunk-RABKQFTA.js → chunk-A2HCAMRH.js} +2 -2
- package/dist/{chunk-6K3WNTRI.js → chunk-AODISQO6.js} +2 -2
- package/dist/{chunk-C6JL6PBL.js → chunk-AQDRH6VF.js} +3 -3
- package/dist/{chunk-72XEFLMD.js → chunk-ASWINLSW.js} +2 -2
- package/dist/{chunk-ZPZYYSHW.js → chunk-BVMDON5T.js} +8 -8
- package/dist/{chunk-EURDMOXZ.js → chunk-BYJZUCOQ.js} +2 -2
- package/dist/{chunk-BQWUXC55.js → chunk-CAVAVT5S.js} +9 -9
- package/dist/{chunk-ZWACUOU5.js → chunk-CDBGAAJY.js} +3 -3
- package/dist/chunk-CDBGAAJY.js.map +1 -0
- package/dist/{chunk-BPD4IJBG.js → chunk-CG6CJMIS.js} +2 -2
- package/dist/{chunk-B254JG72.js → chunk-CQX24PT7.js} +2 -2
- package/dist/{chunk-65K5LV4L.js → chunk-CTCOVPZU.js} +51 -50
- package/dist/{chunk-65K5LV4L.js.map → chunk-CTCOVPZU.js.map} +1 -1
- package/dist/{chunk-HJTTDLF6.js → chunk-CY3NO4JN.js} +4 -4
- package/dist/{chunk-OLUG4IFF.js → chunk-D2RLPRSG.js} +2 -2
- package/dist/{chunk-74NIHMAT.js → chunk-D653JH6D.js} +3 -3
- package/dist/{chunk-2LUO22FR.js → chunk-DAI5CTQF.js} +4 -4
- package/dist/{chunk-HTKVYEVB.js → chunk-DBM4ZXDN.js} +2 -2
- package/dist/{chunk-MB2DTUEG.js → chunk-DNYKLU3K.js} +4 -4
- package/dist/{chunk-26P7B46A.js → chunk-DXYARXK3.js} +3 -3
- package/dist/{chunk-JQKURO3S.js → chunk-E4MQRQ6Z.js} +2 -2
- package/dist/{chunk-KW53DBY7.js → chunk-EBW2E32G.js} +2 -2
- package/dist/{chunk-O73DRA77.js → chunk-ENKBO7FC.js} +4 -4
- package/dist/{chunk-FVCLVASD.js → chunk-FI3KRVOF.js} +24 -24
- package/dist/{chunk-WWM7WVD2.js → chunk-GABFKHQE.js} +2 -2
- package/dist/chunk-GLCOKWLH.js +42 -0
- package/dist/chunk-GLCOKWLH.js.map +1 -0
- package/dist/{chunk-32HA7DXJ.js → chunk-GNBRB3DH.js} +25 -25
- package/dist/{chunk-CEQUNJJY.js → chunk-HPALK6LD.js} +2 -2
- package/dist/{chunk-LR7U3HCD.js → chunk-HPSATFLQ.js} +4 -4
- package/dist/{chunk-O4AWXTZQ.js → chunk-HUYLQW75.js} +6 -6
- package/dist/{chunk-PQCFW2KT.js → chunk-IDDHH5AQ.js} +29 -24
- package/dist/chunk-IDDHH5AQ.js.map +1 -0
- package/dist/{chunk-ZZUKJ5HQ.js → chunk-IG2ERMSB.js} +2 -2
- package/dist/{chunk-2SZ3KC5S.js → chunk-IMKIIS4Y.js} +2 -2
- package/dist/{chunk-CA64OZLZ.js → chunk-IRXPXDY5.js} +4 -4
- package/dist/{chunk-I57HZMMA.js → chunk-IXIE3H43.js} +2 -2
- package/dist/{chunk-OWJD3V4V.js → chunk-J3YQ3554.js} +2 -2
- package/dist/{chunk-BCFHMBMK.js → chunk-J5HZBHU4.js} +7 -7
- package/dist/{chunk-UHMMRQGU.js → chunk-KD7GNHOP.js} +3 -3
- package/dist/{chunk-X7Z4B5GK.js → chunk-KPL7EXS6.js} +2 -2
- package/dist/{chunk-GTR2WUBA.js → chunk-KX6M5CJI.js} +1 -1
- package/dist/{chunk-CYO5HL4C.js → chunk-KYL2BPS2.js} +2 -2
- package/dist/{chunk-QLDMF7MH.js → chunk-LC5HE7PZ.js} +2 -2
- package/dist/{chunk-3SMJ2BIQ.js → chunk-LGD5RESZ.js} +7 -33
- package/dist/chunk-LGD5RESZ.js.map +1 -0
- package/dist/{chunk-4ICPJZAB.js → chunk-LMYSYG6U.js} +2 -2
- package/dist/{chunk-3ZZOE4SN.js → chunk-LOCDYMD6.js} +4 -4
- package/dist/{chunk-DNWBLT4U.js → chunk-MASBMJ2M.js} +2 -2
- package/dist/{chunk-NZMQDUOD.js → chunk-MU6HYDNF.js} +5 -5
- package/dist/{chunk-GMVKCEBG.js → chunk-MY6QEDZ2.js} +2 -2
- package/dist/{chunk-SK57RWSA.js → chunk-MZGKQ5WV.js} +2 -2
- package/dist/{chunk-HSQRYJ4P.js → chunk-N52SFH2K.js} +2 -2
- package/dist/{chunk-MY6VZVCP.js → chunk-OAJX5CEL.js} +4 -4
- package/dist/{chunk-IWIMIRV3.js → chunk-OSJTLQXA.js} +9 -4
- package/dist/chunk-OSJTLQXA.js.map +1 -0
- package/dist/{chunk-FH75NNLB.js → chunk-OZ5NAXCP.js} +2 -2
- package/dist/{chunk-PEGNFZ3F.js → chunk-P5S777YI.js} +2 -2
- package/dist/{chunk-LY6727IF.js → chunk-PPSVFNEA.js} +1 -1
- package/dist/{chunk-K4FDZVMQ.js → chunk-PR42VIVY.js} +2 -2
- package/dist/{chunk-I74J2UZ2.js → chunk-PWMZCW4H.js} +8 -8
- package/dist/{chunk-6YDXUE5H.js → chunk-PYA2O3WS.js} +2 -2
- package/dist/{chunk-GUIZDI5T.js → chunk-Q2PA4KZS.js} +2 -2
- package/dist/{chunk-JJ7VMIEQ.js → chunk-QCANSTLU.js} +64 -64
- package/dist/{chunk-DZH2HTTB.js → chunk-R27GW5WV.js} +1 -1
- package/dist/chunk-R4U4EWZG.js +414 -0
- package/dist/chunk-R4U4EWZG.js.map +1 -0
- package/dist/{chunk-DAQG3C4W.js → chunk-R6JY22SG.js} +29 -29
- package/dist/{chunk-7OIBLMDP.js → chunk-RAQISW42.js} +2 -2
- package/dist/{chunk-DNLXQUBX.js → chunk-RLS6PTL4.js} +32 -44
- package/dist/chunk-RLS6PTL4.js.map +1 -0
- package/dist/{chunk-CPKD2YGK.js → chunk-RNU5CUPI.js} +2 -2
- package/dist/{chunk-467VRP4E.js → chunk-RO5YZQHZ.js} +3 -3
- package/dist/{chunk-SSZROOOI.js → chunk-ROLXYGT5.js} +2 -2
- package/dist/{chunk-6VITVQ23.js → chunk-SHJ5SQOW.js} +7 -7
- package/dist/{chunk-F4B2YYCN.js → chunk-TAWFXRXB.js} +3 -3
- package/dist/{chunk-G3FU2IF7.js → chunk-TB7UW5BB.js} +3 -3
- package/dist/{chunk-NF6Q4XTN.js → chunk-TQF2I5SY.js} +3 -3
- package/dist/{chunk-MM7OKOTE.js → chunk-UBXHP224.js} +43 -18
- package/dist/chunk-UBXHP224.js.map +1 -0
- package/dist/{chunk-M7A226EQ.js → chunk-UL44R3W2.js} +2 -2
- package/dist/{chunk-GTR5NLVP.js → chunk-VCRQPLKS.js} +2 -2
- package/dist/{chunk-MHIYYHCL.js → chunk-VJU3ET7N.js} +12 -12
- package/dist/{chunk-RXMIXKB3.js → chunk-WGJUARF3.js} +13 -13
- package/dist/{chunk-NWSAF6P5.js → chunk-WJDH7CH5.js} +22 -1
- package/dist/chunk-WJDH7CH5.js.map +1 -0
- package/dist/{chunk-LG67ZNRU.js → chunk-WK2ZCTUF.js} +2 -2
- package/dist/{chunk-XGE75VPK.js → chunk-WNARM3LY.js} +3 -3
- package/dist/{chunk-RNBH5TSM.js → chunk-WQ2RTQYO.js} +2 -2
- package/dist/chunk-X5XEYQ4B.js +84 -0
- package/dist/chunk-X5XEYQ4B.js.map +1 -0
- package/dist/{chunk-W57QFLQD.js → chunk-X6JIGNLI.js} +4 -4
- package/dist/{chunk-CKA4SJRK.js → chunk-XIQDCQX7.js} +2 -2
- package/dist/{chunk-AJ7PY4FJ.js → chunk-Y3GCG6CA.js} +2 -2
- package/dist/{chunk-2K27S6LK.js → chunk-Y565N5ZJ.js} +24 -7
- package/dist/chunk-Y565N5ZJ.js.map +1 -0
- package/dist/{chunk-ZRB4M3GG.js → chunk-YC5KH7ZB.js} +3 -3
- package/dist/{chunk-4RY64KQY.js → chunk-YJUKLLNP.js} +2 -2
- package/dist/{chunk-OHG2UQ2O.js → chunk-YWP2H7SX.js} +60 -22
- package/dist/chunk-YWP2H7SX.js.map +1 -0
- package/dist/{chunk-24POWVUY.js → chunk-ZDJNRRVS.js} +4 -4
- package/dist/{chunk-S6WJ6TUT.js → chunk-ZEVJGMNB.js} +2 -2
- package/dist/chunk-ZEVJGMNB.js.map +1 -0
- package/dist/{chunk-M7YEZR6X.js → chunk-ZITMDA4C.js} +2 -2
- package/dist/components/model-policy-picker.d.ts +2 -0
- package/dist/composer.js +6 -6
- package/dist/{computer-viewer-BWVNDFTO.js → computer-viewer-6CRLWQHP.js} +5 -5
- package/dist/connect.js +6 -6
- package/dist/connection-service-presentation.d.ts +1 -4
- package/dist/desktop.js +2 -2
- package/dist/editor.js +2 -2
- package/dist/{embedded-new-voice-GBXSZ3TO.js → embedded-new-voice-KILV3RYM.js} +5 -5
- package/dist/{embedded-voice-MGI7EK7C.js → embedded-voice-GHDRLCFX.js} +5 -5
- package/dist/{fleet-decision-row-35S3GAUN.js → fleet-decision-row-DQXRRBT7.js} +2 -2
- package/dist/index.js +44 -44
- package/dist/interaction.js +5 -5
- package/dist/machines.js +3 -3
- package/dist/messages/en.d.ts +1 -1
- package/dist/messages.js +1 -1
- package/dist/{model-policy-picker-menu-KP53N6UJ.js → model-policy-picker-menu-5ALKUJMR.js} +4 -4
- package/dist/{platform-activity-row-ISMXR7NJ.js → platform-activity-row-227C764Z.js} +2 -2
- package/dist/{queue-surface-implementation-ULW7DDJA.js → queue-surface-implementation-H3NOBXU5.js} +5 -5
- package/dist/realtime.js +4 -4
- package/dist/{sandbox-files-QYSD4XCZ.js → sandbox-files-5FEOHUMD.js} +6 -6
- package/dist/session-app.js +155 -157
- package/dist/{session-realtime-JXRLPNUH.js → session-realtime-OHBMODNX.js} +3 -3
- package/dist/session-realtime.js +2 -2
- package/dist/session-ui.js +26 -26
- package/dist/session.js +6 -6
- package/dist/sites.js +2 -2
- package/dist/terminal.js +2 -2
- package/dist/this-computer.js +3 -3
- package/dist/timeline/connector-logo-index.d.ts +19 -0
- package/dist/{timeline-annotation-markers-OJYKHBEO.js → timeline-annotation-markers-NDHS7MCK.js} +2 -2
- package/dist/{timeline-annotation-selection-6OAZLXFU.js → timeline-annotation-selection-DRQSTJOV.js} +2 -2
- package/dist/{timeline-annotations-dialog-5M6DP5FX.js → timeline-annotations-dialog-G6J5ZJKN.js} +3 -3
- package/dist/timeline-model.d.ts +2 -0
- package/dist/timeline-model.js +9 -1
- package/dist/ui/list-row.js +1 -1
- package/dist/usage.js +2 -2
- package/dist/workbench.js +4 -4
- package/package.json +4 -4
- package/src/app/components/capabilities/native-connect-readiness.tsx +1 -0
- package/src/app/components/capabilities/perform-capability-action.test.ts +67 -0
- package/src/app/components/capabilities/perform-capability-action.tsx +28 -0
- package/src/app/components/command-menu/chat-switcher.tsx +25 -4
- package/src/app/components/rail/session-header.test.tsx +73 -2
- package/src/app/components/rail/session-header.tsx +20 -12
- package/src/app/components/rail/session-shell.tsx +10 -18
- package/src/app/components/session/subscription-account-indicator.test.ts +239 -0
- package/src/app/components/session/subscription-account-indicator.tsx +361 -0
- package/src/app/components/session/subscription-account-menu.tsx +21 -43
- package/src/app/components/session/subscription-account.test.ts +314 -0
- package/src/app/components/session/subscription-account.ts +354 -0
- package/src/app/lib/actions/actions-provider.tsx +11 -0
- package/src/app/lib/actions/actions.test.ts +59 -4
- package/src/app/lib/actions/chat-switcher.ts +73 -20
- package/src/app/lib/drive-connect-return.test.ts +183 -0
- package/src/app/lib/drive-connect-return.tsx +116 -0
- package/src/app/routes/session.tsx +12 -14
- package/src/app/routes/sessions-index.tsx +1 -0
- package/src/components/model-policy-picker-menu.tsx +25 -4
- package/src/components/model-policy-picker.tsx +2 -0
- package/src/components/session-mcp-capability-card.tsx +13 -4
- package/src/connection-service-presentation.ts +6 -1
- package/src/messages/en.ts +1 -1
- package/src/timeline/connector-logo-index.test.ts +46 -0
- package/src/timeline/connector-logo-index.ts +68 -0
- package/src/timeline-model.ts +6 -0
- package/src/ui/list-row.tsx +38 -10
- package/styles/compiled.css +13 -0
- package/dist/app/components/session/codex-account-indicator.d.ts +0 -9
- package/dist/app/components/session/model-account-indicator.d.ts +0 -10
- package/dist/app/components/session/model-account-indicator.js +0 -34
- package/dist/app/components/session/model-account-usage.d.ts +0 -8
- package/dist/app/components/session/model-account-usage.js +0 -8
- package/dist/chunk-2K27S6LK.js.map +0 -1
- package/dist/chunk-3SMJ2BIQ.js.map +0 -1
- package/dist/chunk-6PQBKMUL.js.map +0 -1
- package/dist/chunk-7H2D4VNU.js +0 -150
- package/dist/chunk-7H2D4VNU.js.map +0 -1
- package/dist/chunk-DNLXQUBX.js.map +0 -1
- package/dist/chunk-HCCSAECI.js +0 -132
- package/dist/chunk-HCCSAECI.js.map +0 -1
- package/dist/chunk-IWIMIRV3.js.map +0 -1
- package/dist/chunk-M77ZQE5T.js +0 -58
- package/dist/chunk-M77ZQE5T.js.map +0 -1
- package/dist/chunk-MM7OKOTE.js.map +0 -1
- package/dist/chunk-NPDL4W4Y.js.map +0 -1
- package/dist/chunk-NWSAF6P5.js.map +0 -1
- package/dist/chunk-OHG2UQ2O.js.map +0 -1
- package/dist/chunk-PQCFW2KT.js.map +0 -1
- package/dist/chunk-S6WJ6TUT.js.map +0 -1
- package/dist/chunk-ZWACUOU5.js.map +0 -1
- package/dist/session-realtime-JXRLPNUH.js.map +0 -1
- package/src/app/components/session/codex-account-indicator.tsx +0 -207
- package/src/app/components/session/model-account-indicator.tsx +0 -189
- package/src/app/components/session/model-account-usage.test.ts +0 -59
- package/src/app/components/session/model-account-usage.tsx +0 -89
- /package/dist/app/components/session/{codex-account-indicator.js.map → subscription-account-indicator.js.map} +0 -0
- /package/dist/app/{components/session/model-account-indicator.js.map → lib/drive-connect-return.js.map} +0 -0
- /package/dist/{app/components/session/model-account-usage.js.map → browser-viewer-VKIAULG7.js.map} +0 -0
- /package/dist/{browser-viewer-YQC6BFMS.js.map → chat-interactive-block-JGYSW4KB.js.map} +0 -0
- /package/dist/{chunk-WN2MIB4Q.js.map → chunk-22V2QCEM.js.map} +0 -0
- /package/dist/{chunk-SJPAKWWP.js.map → chunk-2BISI664.js.map} +0 -0
- /package/dist/{chunk-O5UOCMH4.js.map → chunk-2LPJVEWY.js.map} +0 -0
- /package/dist/{chunk-JI6WPLZB.js.map → chunk-2SBCOGQN.js.map} +0 -0
- /package/dist/{chunk-6JF7C3U3.js.map → chunk-2WERWWSS.js.map} +0 -0
- /package/dist/{chunk-4XKT4ENZ.js.map → chunk-2Y3QL3DA.js.map} +0 -0
- /package/dist/{chunk-KVMFT6FN.js.map → chunk-3FL3F4WF.js.map} +0 -0
- /package/dist/{chunk-4VJOZJDV.js.map → chunk-3JPQAIYJ.js.map} +0 -0
- /package/dist/{chunk-27EORKJ4.js.map → chunk-3N4LNBJF.js.map} +0 -0
- /package/dist/{chunk-7JZSFJHB.js.map → chunk-3OJU3RUR.js.map} +0 -0
- /package/dist/{chunk-NV5OYV3G.js.map → chunk-3U6K36QA.js.map} +0 -0
- /package/dist/{chunk-MDRLP5DO.js.map → chunk-3W7ROHN4.js.map} +0 -0
- /package/dist/{chunk-3MQM4ZOC.js.map → chunk-3Y6ZR2VN.js.map} +0 -0
- /package/dist/{chunk-HXZIX7MR.js.map → chunk-47AB65IO.js.map} +0 -0
- /package/dist/{chunk-DNYD7AUG.js.map → chunk-4BOLIZ7Y.js.map} +0 -0
- /package/dist/{chunk-4XO346BW.js.map → chunk-4FBJFQFU.js.map} +0 -0
- /package/dist/{chunk-BWKZEW6K.js.map → chunk-4T3E56NT.js.map} +0 -0
- /package/dist/{chunk-4PC7LIQK.js.map → chunk-4ZMUWRFF.js.map} +0 -0
- /package/dist/{chunk-TM2ZLY3Q.js.map → chunk-5OLFYSVG.js.map} +0 -0
- /package/dist/{chunk-D4QOZXPM.js.map → chunk-5WBRM7V4.js.map} +0 -0
- /package/dist/{chunk-GXK4CUAB.js.map → chunk-5WQKTT5Z.js.map} +0 -0
- /package/dist/{chunk-6N7W3TOV.js.map → chunk-63BPMTBM.js.map} +0 -0
- /package/dist/{chunk-R5YZ3ROM.js.map → chunk-63PQKTTT.js.map} +0 -0
- /package/dist/{chunk-WLZVXCPZ.js.map → chunk-6GO6WN22.js.map} +0 -0
- /package/dist/{chunk-XDCZBOEY.js.map → chunk-6LGBGPBU.js.map} +0 -0
- /package/dist/{chunk-FMATVQFW.js.map → chunk-6XXMG5YP.js.map} +0 -0
- /package/dist/{chunk-M5E3CKSE.js.map → chunk-7QWWE5XL.js.map} +0 -0
- /package/dist/{chunk-RABKQFTA.js.map → chunk-A2HCAMRH.js.map} +0 -0
- /package/dist/{chunk-6K3WNTRI.js.map → chunk-AODISQO6.js.map} +0 -0
- /package/dist/{chunk-C6JL6PBL.js.map → chunk-AQDRH6VF.js.map} +0 -0
- /package/dist/{chunk-72XEFLMD.js.map → chunk-ASWINLSW.js.map} +0 -0
- /package/dist/{chunk-ZPZYYSHW.js.map → chunk-BVMDON5T.js.map} +0 -0
- /package/dist/{chunk-EURDMOXZ.js.map → chunk-BYJZUCOQ.js.map} +0 -0
- /package/dist/{chunk-BQWUXC55.js.map → chunk-CAVAVT5S.js.map} +0 -0
- /package/dist/{chunk-BPD4IJBG.js.map → chunk-CG6CJMIS.js.map} +0 -0
- /package/dist/{chunk-B254JG72.js.map → chunk-CQX24PT7.js.map} +0 -0
- /package/dist/{chunk-HJTTDLF6.js.map → chunk-CY3NO4JN.js.map} +0 -0
- /package/dist/{chunk-OLUG4IFF.js.map → chunk-D2RLPRSG.js.map} +0 -0
- /package/dist/{chunk-74NIHMAT.js.map → chunk-D653JH6D.js.map} +0 -0
- /package/dist/{chunk-2LUO22FR.js.map → chunk-DAI5CTQF.js.map} +0 -0
- /package/dist/{chunk-HTKVYEVB.js.map → chunk-DBM4ZXDN.js.map} +0 -0
- /package/dist/{chunk-MB2DTUEG.js.map → chunk-DNYKLU3K.js.map} +0 -0
- /package/dist/{chunk-26P7B46A.js.map → chunk-DXYARXK3.js.map} +0 -0
- /package/dist/{chunk-JQKURO3S.js.map → chunk-E4MQRQ6Z.js.map} +0 -0
- /package/dist/{chunk-KW53DBY7.js.map → chunk-EBW2E32G.js.map} +0 -0
- /package/dist/{chunk-O73DRA77.js.map → chunk-ENKBO7FC.js.map} +0 -0
- /package/dist/{chunk-FVCLVASD.js.map → chunk-FI3KRVOF.js.map} +0 -0
- /package/dist/{chunk-WWM7WVD2.js.map → chunk-GABFKHQE.js.map} +0 -0
- /package/dist/{chunk-32HA7DXJ.js.map → chunk-GNBRB3DH.js.map} +0 -0
- /package/dist/{chunk-CEQUNJJY.js.map → chunk-HPALK6LD.js.map} +0 -0
- /package/dist/{chunk-LR7U3HCD.js.map → chunk-HPSATFLQ.js.map} +0 -0
- /package/dist/{chunk-O4AWXTZQ.js.map → chunk-HUYLQW75.js.map} +0 -0
- /package/dist/{chunk-ZZUKJ5HQ.js.map → chunk-IG2ERMSB.js.map} +0 -0
- /package/dist/{chunk-2SZ3KC5S.js.map → chunk-IMKIIS4Y.js.map} +0 -0
- /package/dist/{chunk-CA64OZLZ.js.map → chunk-IRXPXDY5.js.map} +0 -0
- /package/dist/{chunk-I57HZMMA.js.map → chunk-IXIE3H43.js.map} +0 -0
- /package/dist/{chunk-OWJD3V4V.js.map → chunk-J3YQ3554.js.map} +0 -0
- /package/dist/{chunk-BCFHMBMK.js.map → chunk-J5HZBHU4.js.map} +0 -0
- /package/dist/{chunk-UHMMRQGU.js.map → chunk-KD7GNHOP.js.map} +0 -0
- /package/dist/{chunk-X7Z4B5GK.js.map → chunk-KPL7EXS6.js.map} +0 -0
- /package/dist/{chunk-GTR2WUBA.js.map → chunk-KX6M5CJI.js.map} +0 -0
- /package/dist/{chunk-CYO5HL4C.js.map → chunk-KYL2BPS2.js.map} +0 -0
- /package/dist/{chunk-QLDMF7MH.js.map → chunk-LC5HE7PZ.js.map} +0 -0
- /package/dist/{chunk-4ICPJZAB.js.map → chunk-LMYSYG6U.js.map} +0 -0
- /package/dist/{chunk-3ZZOE4SN.js.map → chunk-LOCDYMD6.js.map} +0 -0
- /package/dist/{chunk-DNWBLT4U.js.map → chunk-MASBMJ2M.js.map} +0 -0
- /package/dist/{chunk-NZMQDUOD.js.map → chunk-MU6HYDNF.js.map} +0 -0
- /package/dist/{chunk-GMVKCEBG.js.map → chunk-MY6QEDZ2.js.map} +0 -0
- /package/dist/{chunk-SK57RWSA.js.map → chunk-MZGKQ5WV.js.map} +0 -0
- /package/dist/{chunk-HSQRYJ4P.js.map → chunk-N52SFH2K.js.map} +0 -0
- /package/dist/{chunk-MY6VZVCP.js.map → chunk-OAJX5CEL.js.map} +0 -0
- /package/dist/{chunk-FH75NNLB.js.map → chunk-OZ5NAXCP.js.map} +0 -0
- /package/dist/{chunk-PEGNFZ3F.js.map → chunk-P5S777YI.js.map} +0 -0
- /package/dist/{chunk-LY6727IF.js.map → chunk-PPSVFNEA.js.map} +0 -0
- /package/dist/{chunk-K4FDZVMQ.js.map → chunk-PR42VIVY.js.map} +0 -0
- /package/dist/{chunk-I74J2UZ2.js.map → chunk-PWMZCW4H.js.map} +0 -0
- /package/dist/{chunk-6YDXUE5H.js.map → chunk-PYA2O3WS.js.map} +0 -0
- /package/dist/{chunk-GUIZDI5T.js.map → chunk-Q2PA4KZS.js.map} +0 -0
- /package/dist/{chunk-JJ7VMIEQ.js.map → chunk-QCANSTLU.js.map} +0 -0
- /package/dist/{chunk-DZH2HTTB.js.map → chunk-R27GW5WV.js.map} +0 -0
- /package/dist/{chunk-DAQG3C4W.js.map → chunk-R6JY22SG.js.map} +0 -0
- /package/dist/{chunk-7OIBLMDP.js.map → chunk-RAQISW42.js.map} +0 -0
- /package/dist/{chunk-CPKD2YGK.js.map → chunk-RNU5CUPI.js.map} +0 -0
- /package/dist/{chunk-467VRP4E.js.map → chunk-RO5YZQHZ.js.map} +0 -0
- /package/dist/{chunk-SSZROOOI.js.map → chunk-ROLXYGT5.js.map} +0 -0
- /package/dist/{chunk-6VITVQ23.js.map → chunk-SHJ5SQOW.js.map} +0 -0
- /package/dist/{chunk-F4B2YYCN.js.map → chunk-TAWFXRXB.js.map} +0 -0
- /package/dist/{chunk-G3FU2IF7.js.map → chunk-TB7UW5BB.js.map} +0 -0
- /package/dist/{chunk-NF6Q4XTN.js.map → chunk-TQF2I5SY.js.map} +0 -0
- /package/dist/{chunk-M7A226EQ.js.map → chunk-UL44R3W2.js.map} +0 -0
- /package/dist/{chunk-GTR5NLVP.js.map → chunk-VCRQPLKS.js.map} +0 -0
- /package/dist/{chunk-MHIYYHCL.js.map → chunk-VJU3ET7N.js.map} +0 -0
- /package/dist/{chunk-RXMIXKB3.js.map → chunk-WGJUARF3.js.map} +0 -0
- /package/dist/{chunk-LG67ZNRU.js.map → chunk-WK2ZCTUF.js.map} +0 -0
- /package/dist/{chunk-XGE75VPK.js.map → chunk-WNARM3LY.js.map} +0 -0
- /package/dist/{chunk-RNBH5TSM.js.map → chunk-WQ2RTQYO.js.map} +0 -0
- /package/dist/{chunk-W57QFLQD.js.map → chunk-X6JIGNLI.js.map} +0 -0
- /package/dist/{chunk-CKA4SJRK.js.map → chunk-XIQDCQX7.js.map} +0 -0
- /package/dist/{chunk-AJ7PY4FJ.js.map → chunk-Y3GCG6CA.js.map} +0 -0
- /package/dist/{chunk-ZRB4M3GG.js.map → chunk-YC5KH7ZB.js.map} +0 -0
- /package/dist/{chunk-4RY64KQY.js.map → chunk-YJUKLLNP.js.map} +0 -0
- /package/dist/{chunk-24POWVUY.js.map → chunk-ZDJNRRVS.js.map} +0 -0
- /package/dist/{chunk-M7YEZR6X.js.map → chunk-ZITMDA4C.js.map} +0 -0
- /package/dist/{chat-interactive-block-Z22YHDRU.js.map → computer-viewer-6CRLWQHP.js.map} +0 -0
- /package/dist/{computer-viewer-BWVNDFTO.js.map → embedded-new-voice-KILV3RYM.js.map} +0 -0
- /package/dist/{embedded-new-voice-GBXSZ3TO.js.map → embedded-voice-GHDRLCFX.js.map} +0 -0
- /package/dist/{fleet-decision-row-35S3GAUN.js.map → fleet-decision-row-DQXRRBT7.js.map} +0 -0
- /package/dist/{embedded-voice-MGI7EK7C.js.map → model-policy-picker-menu-5ALKUJMR.js.map} +0 -0
- /package/dist/{platform-activity-row-ISMXR7NJ.js.map → platform-activity-row-227C764Z.js.map} +0 -0
- /package/dist/{queue-surface-implementation-ULW7DDJA.js.map → queue-surface-implementation-H3NOBXU5.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-KP53N6UJ.js.map → sandbox-files-5FEOHUMD.js.map} +0 -0
- /package/dist/{sandbox-files-QYSD4XCZ.js.map → session-realtime-OHBMODNX.js.map} +0 -0
- /package/dist/{timeline-annotation-markers-OJYKHBEO.js.map → timeline-annotation-markers-NDHS7MCK.js.map} +0 -0
- /package/dist/{timeline-annotation-selection-6OAZLXFU.js.map → timeline-annotation-selection-DRQSTJOV.js.map} +0 -0
- /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 {
|
|
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
|
|
300
|
-
const
|
|
301
|
-
|
|
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
|
-
*
|
|
9
|
-
*
|
|
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 =
|
|
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
|
|
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
|
|
29
|
-
*
|
|
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:
|
|
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
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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 (
|
|
51
|
-
return {
|
|
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
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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. */
|