@opengeni/react 6.5.2-canary.41607687019001 → 6.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/app-state.d.ts +4 -0
- package/dist/app/app-state.js +19 -14
- package/dist/app/components/Composer.js +25 -23
- package/dist/app/components/agent/agent-capability-picker.js +10 -8
- package/dist/app/components/artifacts/artifact-browse-controls.js +4 -4
- package/dist/app/components/artifacts/artifact-library.js +21 -19
- package/dist/app/components/artifacts/artifact-page-chrome.js +3 -3
- package/dist/app/components/artifacts/artifact-viewer.js +23 -22
- package/dist/app/components/artifacts/artifact-viewer.js.map +1 -1
- package/dist/app/components/artifacts/chat-interactive-block.js +12 -12
- package/dist/app/components/artifacts/copy-artifact-link.js +7 -5
- package/dist/app/components/artifacts/file-reach-dialog.js +11 -9
- package/dist/app/components/artifacts/inline-chat-image.js +18 -18
- package/dist/app/components/artifacts/retained-file-preview.js +24 -23
- package/dist/app/components/artifacts/retained-text-preview.js +3 -3
- package/dist/app/components/artifacts/site-audience.js +6 -5
- package/dist/app/components/artifacts/site-conversations.js +16 -14
- package/dist/app/components/artifacts/site-data.js +18 -16
- package/dist/app/components/artifacts/site-public-link.js +3 -2
- package/dist/app/components/artifacts/site-publishing-page.js +39 -37
- package/dist/app/components/artifacts/site-publishing.js +34 -32
- package/dist/app/components/artifacts/site-settings.js +38 -36
- package/dist/app/components/artifacts/site-version-menu.js +8 -7
- package/dist/app/components/billing-class-mark.js +3 -3
- package/dist/app/components/bots/bot-launch-strip.js +17 -17
- package/dist/app/components/bots/bot-template-dialog.js +30 -28
- package/dist/app/components/bots/bot-template-shortcuts.js +19 -19
- package/dist/app/components/bots/bot-templates-page.js +38 -36
- package/dist/app/components/bots/bot-templates-page.js.map +1 -1
- package/dist/app/components/capabilities/capability-detail-sheet.js +13 -11
- package/dist/app/components/capabilities/capability-logo-source.js +13 -13
- package/dist/app/components/capabilities/capability-logo.js +4 -3
- package/dist/app/components/capabilities/connector-tool-permissions.js +8 -6
- package/dist/app/components/capabilities/native-connect-setup.js +14 -14
- package/dist/app/components/capabilities/own-logins-shared-heads-up.js +7 -6
- package/dist/app/components/capabilities/perform-capability-action.js +7 -6
- package/dist/app/components/capabilities/prepared-mcp-setup-card.js +17 -17
- package/dist/app/components/capabilities/session-capability-card.js +72 -70
- package/dist/app/components/capabilities/session-capability-card.js.map +1 -1
- package/dist/app/components/capabilities/session-chrome-card.js +28 -26
- package/dist/app/components/capabilities/session-connection-accounts.js +4 -3
- package/dist/app/components/capabilities/session-custom-mcp-card.js +20 -19
- package/dist/app/components/capabilities/session-github-card.js +22 -20
- package/dist/app/components/capabilities/session-machine-card.js +27 -25
- package/dist/app/components/capabilities/session-personal-github-card.js +16 -16
- package/dist/app/components/capabilities/session-plugin-card.js +10 -9
- package/dist/app/components/capabilities/use-capabilities-catalog.js +4 -3
- package/dist/app/components/capabilities/use-connection-accounts.js +5 -4
- package/dist/app/components/command-menu/chat-status.js +15 -13
- package/dist/app/components/command-menu/chat-switcher.js +35 -33
- package/dist/app/components/command-menu/chat-switcher.js.map +1 -1
- package/dist/app/components/command-menu/command-menu.js +53 -50
- package/dist/app/components/command-menu/command-menu.js.map +1 -1
- package/dist/app/components/command-menu/shortcut-sheet.js +18 -16
- package/dist/app/components/command-menu/shortcut-sheet.js.map +1 -1
- package/dist/app/components/common.js +4 -3
- package/dist/app/components/composer-capabilities-menu-body.js +4 -4
- package/dist/app/components/composer-mobile-plus-panel.js +8 -8
- package/dist/app/components/composer-mobile-plus.js +1 -1
- package/dist/app/components/credit-required-gate.js +3 -3
- package/dist/app/components/credit-required-prompt.js +10 -8
- package/dist/app/components/credit-required-prompt.js.map +1 -1
- package/dist/app/components/credits/checkout-credits-celebration.js +3 -2
- package/dist/app/components/credits/checkout-return.js +3 -2
- package/dist/app/components/credits/checkout-return.js.map +1 -1
- package/dist/app/components/credits/coupon-redeem.js +5 -4
- package/dist/app/components/credits/credit-refusal-notice.js +7 -5
- package/dist/app/components/cross-slot-deep-link-recovery.js +5 -4
- package/dist/app/components/desktop/app-shots.js +13 -11
- package/dist/app/components/desktop/computer-use.js +6 -4
- package/dist/app/components/desktop/this-computer.js +9 -7
- package/dist/app/components/feedback.js +5 -4
- package/dist/app/components/follow-up-repository-menu-body.js +8 -6
- package/dist/app/components/follow-up-repository-menu-body.js.map +1 -1
- package/dist/app/components/follow-up-repository-picker.js +1 -1
- package/dist/app/components/github-app-connect-launcher.js +5 -4
- package/dist/app/components/in-app-help-link.js +3 -3
- package/dist/app/components/knowledge/agent-learning-settings.js +3 -2
- package/dist/app/components/knowledge/agent-learning-settings.js.map +1 -1
- package/dist/app/components/machines/connect-machine-dialog.js +18 -16
- package/dist/app/components/manual-repository-editor.js +3 -2
- package/dist/app/components/markdown.js +6 -6
- package/dist/app/components/models/models-ui.js +12 -10
- package/dist/app/components/my-things.js +12 -10
- package/dist/app/components/my-things.js.map +1 -1
- package/dist/app/components/new-session-starters.js +3 -3
- package/dist/app/components/own-where-it-works.js +9 -7
- package/dist/app/components/personal-resource-attachment-surface.js +4 -4
- package/dist/app/components/pickers.js +5 -5
- package/dist/app/components/rail/channel-create-dialog.js +7 -5
- package/dist/app/components/rail/project-glyph.js +17 -17
- package/dist/app/components/rail/project-profile.js +31 -29
- package/dist/app/components/rail/rail-context.js +3 -3
- package/dist/app/components/rail/rail-sections.js +68 -66
- package/dist/app/components/rail/session-header.js +59 -57
- package/dist/app/components/rail/session-list.js +66 -64
- package/dist/app/components/rail/session-row-content.js +4 -4
- package/dist/app/components/rail/session-row-hover-details.js +14 -12
- package/dist/app/components/rail/session-shell.js +90 -88
- package/dist/app/components/rail/site-session-group-heading.js +8 -8
- package/dist/app/components/repository-picker.js +8 -6
- package/dist/app/components/secrets/session-secrets-panel.js +4 -3
- package/dist/app/components/secrets/vault-model.js +3 -2
- package/dist/app/components/session/agent-configuration-panel.js +25 -23
- package/dist/app/components/session/agent-configuration-panel.js.map +1 -1
- package/dist/app/components/session/artifact-link-boundary.js +3 -3
- package/dist/app/components/session/artifact-session-page.js +11 -9
- package/dist/app/components/session/banners.js +8 -8
- package/dist/app/components/session/codex-usage-notice.js +5 -3
- package/dist/app/components/session/console-new-conversation.js +42 -40
- package/dist/app/components/session/context-text-reader.js +3 -2
- package/dist/app/components/session/descendant-requests.js +39 -37
- package/dist/app/components/session/descendant-requests.js.map +1 -1
- package/dist/app/components/session/editable-artifacts-workspace.js +36 -34
- package/dist/app/components/session/editable-artifacts-workspace.js.map +1 -1
- package/dist/app/components/session/failed-session-banner.js +13 -11
- package/dist/app/components/session/failed-session-banner.js.map +1 -1
- package/dist/app/components/session/failure-recovery-boundary.js +3 -3
- package/dist/app/components/session/human-input-delegation-record.js +2 -2
- package/dist/app/components/session/human-input.js +33 -31
- package/dist/app/components/session/inspector.js +57 -55
- package/dist/app/components/session/inspector.js.map +1 -1
- package/dist/app/components/session/message-actions.js +5 -4
- package/dist/app/components/session/message-actions.js.map +1 -1
- package/dist/app/components/session/message-resource-attachments.js +12 -11
- package/dist/app/components/session/message-resource-attachments.js.map +1 -1
- package/dist/app/components/session/model-context-inspector.js +6 -4
- package/dist/app/components/session/model-recovery-notice.js +3 -3
- package/dist/app/components/session/new-session-settings-menu.js +11 -9
- package/dist/app/components/session/new-session-setup-strip.js +18 -18
- package/dist/app/components/session/sandbox-recovery-actions.js +3 -3
- package/dist/app/components/session/sandbox-switcher.js +10 -9
- package/dist/app/components/session/sandbox-workspace.js +47 -46
- package/dist/app/components/session/search-markdown-highlight.js +7 -7
- package/dist/app/components/session/search-results-view.js +8 -8
- package/dist/app/components/session/session-admin-access.js +13 -11
- package/dist/app/components/session/session-admission-notice.js +7 -5
- package/dist/app/components/session/session-avatar-picker.js +22 -20
- package/dist/app/components/session/session-avatar.js +16 -16
- package/dist/app/components/session/session-machine-access-notice.js +7 -5
- package/dist/app/components/session/session-profile-dialog.js +29 -27
- package/dist/app/components/session/session-profile-editor.js +30 -28
- package/dist/app/components/session/session-retention.js +10 -8
- package/dist/app/components/session/session-search-dialog.js +10 -10
- package/dist/app/components/session/session-skill-reviews.js +2 -1
- package/dist/app/components/session/session-skill-reviews.js.map +1 -1
- package/dist/app/components/session/session-tenancy-control.js +13 -11
- package/dist/app/components/session/session-tenancy-control.js.map +1 -1
- package/dist/app/components/session/session-unattended-setting.js +7 -5
- package/dist/app/components/session/session-usage-limit-section.js +11 -9
- package/dist/app/components/session/share-chat-dialog.js +9 -7
- package/dist/app/components/session/site-origin-link.js +3 -3
- package/dist/app/components/session/subagents.js +8 -8
- package/dist/app/components/session/subscription-account-indicator.js +6 -6
- package/dist/app/components/session-connectors-menu-body.js +4 -3
- package/dist/app/components/session-connectors-menu-body.js.map +1 -1
- package/dist/app/components/settings/row-select.js +7 -5
- package/dist/app/components/toaster.js +3 -2
- package/dist/app/components/transcription-settings.js +11 -9
- package/dist/app/components/ui/destructive-confirm.js +11 -9
- package/dist/app/components/usage/usage-entry.js +5 -5
- package/dist/app/components/window-bar.js +16 -14
- package/dist/app/components/workspace-composer-plus.js +22 -21
- package/dist/app/components/workspace-reach.js +6 -4
- package/dist/app/components/workspace-tenant-boundary.js +5 -4
- package/dist/app/embed/memory-navigation.js +3 -3
- package/dist/app/embed/session-app.js +211 -209
- package/dist/app/lib/actions/actions-provider.js +15 -13
- package/dist/app/lib/agent-capabilities.js +3 -3
- package/dist/app/lib/app-surfaces.js +7 -6
- package/dist/app/lib/archive-session.d.ts +9 -4
- package/dist/app/lib/archive-session.js +4 -2
- package/dist/app/lib/bot-templates.js +3 -2
- package/dist/app/lib/capabilities.js +3 -2
- package/dist/app/lib/codex-usage-query.js +2 -2
- package/dist/app/lib/composer-connectors.js +17 -16
- package/dist/app/lib/console-site.js +2 -2
- package/dist/app/lib/console-text.js +2 -2
- package/dist/app/lib/events.js +9 -9
- package/dist/app/lib/failed-session-copy.js +3 -3
- package/dist/app/lib/inbox.js +4 -2
- 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/navigation.js +2 -2
- package/dist/app/lib/session-branch-cache.js +6 -6
- package/dist/app/lib/session-create.js +4 -4
- package/dist/app/lib/session-group-pagination.js +6 -6
- package/dist/app/lib/session-hover-facts.js +13 -11
- package/dist/app/lib/session-model.js +2 -2
- package/dist/app/lib/session-rail.js +3 -3
- package/dist/app/lib/session-rename.js +3 -3
- package/dist/app/lib/session-restart-operation-controller.js +4 -3
- package/dist/app/lib/session-tenancy.js +3 -2
- package/dist/app/lib/session-usage-limit.js +3 -2
- package/dist/app/lib/sessions-group.js +5 -5
- package/dist/app/lib/site-publishing.js +3 -2
- package/dist/app/lib/site-tool-consent-dialog.js +9 -7
- package/dist/app/lib/site-tool-consent-dialog.js.map +1 -1
- package/dist/app/lib/site-tool-consent.js +1 -1
- package/dist/app/lib/subscription-plans.js +3 -2
- package/dist/app/lib/toast.js +2 -1
- package/dist/app/lib/turn-failure-analytics.js +4 -4
- package/dist/app/lib/use-duplicate-site.js +6 -5
- package/dist/app/lib/use-follow-up-repositories.js +6 -5
- package/dist/app/lib/use-workspace-integrations.js +4 -3
- package/dist/app/lib/use-workspace-machines.js +4 -4
- package/dist/app/lib/use-workspace-model-catalog.js +5 -4
- 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 +67 -66
- package/dist/app/routes/artifacts.js.map +1 -1
- package/dist/app/routes/retained-artifact.js +42 -40
- package/dist/app/routes/retained-artifact.js.map +1 -1
- package/dist/app/routes/session-unavailable.js +8 -7
- package/dist/app/routes/session.js +119 -117
- package/dist/app/routes/sessions-index-hydration.js +5 -5
- package/dist/app/routes/sessions-index.js +104 -102
- package/dist/app/routes/site-view.js +67 -65
- 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/automations.js +17 -15
- package/dist/automations.js.map +1 -1
- package/dist/browser-viewer-TC27OCIP.js +23 -0
- package/dist/chat-interactive-block-RWPQBHVS.js +18 -0
- package/dist/chunk-2BDT3UZQ.js +229 -0
- package/dist/chunk-2CLNIEK4.js +373 -0
- package/dist/chunk-2FSBSCZJ.js +451 -0
- package/dist/chunk-2I7HDP53.js +199 -0
- package/dist/chunk-2INLDVK3.js +15 -0
- package/dist/chunk-2KALVELR.js +126 -0
- package/dist/chunk-2M3J7ZWQ.js +418 -0
- package/dist/chunk-2OP6WLGV.js +708 -0
- package/dist/chunk-2RG7NIAS.js +2936 -0
- package/dist/chunk-2STUPWEL.js +135 -0
- package/dist/chunk-2YUOVM3G.js +271 -0
- package/dist/chunk-2Z3NQNTM.js +3670 -0
- package/dist/chunk-3CPWWGNK.js +920 -0
- package/dist/chunk-3ED2U6XE.js +37 -0
- package/dist/chunk-3GZA4BUW.js +216 -0
- package/dist/chunk-3IK6UYO6.js +1022 -0
- package/dist/chunk-3PZ7JZI2.js +431 -0
- package/dist/chunk-4DVJ6ONS.js +50 -0
- package/dist/chunk-4DVNUCRR.js +2386 -0
- package/dist/chunk-4DVNUCRR.js.map +1 -0
- package/dist/chunk-4E3ZJRTZ.js +41 -0
- package/dist/chunk-4SAWSRAA.js +141 -0
- package/dist/chunk-53AGXXZM.js +307 -0
- package/dist/chunk-54YW6SZZ.js +818 -0
- package/dist/chunk-57AZS3IE.js +2317 -0
- package/dist/chunk-5CPEEAFD.js +143 -0
- package/dist/chunk-5EWSRZ2Y.js +340 -0
- package/dist/chunk-5IE76VCY.js +387 -0
- package/dist/chunk-5NWEKLDZ.js +170 -0
- package/dist/chunk-5O2FCCHZ.js +198 -0
- package/dist/chunk-5QAY6K77.js +399 -0
- package/dist/chunk-5YKW2K6I.js +625 -0
- package/dist/chunk-5YKW2K6I.js.map +1 -0
- package/dist/chunk-6BCXNDEI.js +4116 -0
- package/dist/chunk-6BCXNDEI.js.map +1 -0
- package/dist/chunk-6DK6Z4UN.js +2599 -0
- package/dist/chunk-6DVCC7QS.js +702 -0
- package/dist/chunk-6JHU2UBS.js +2058 -0
- package/dist/chunk-6PZ7OBYU.js +490 -0
- package/dist/chunk-6WD2INST.js +539 -0
- package/dist/chunk-6WD2INST.js.map +1 -0
- package/dist/chunk-6WGRDRMA.js +163 -0
- package/dist/chunk-77WAKEAH.js +186 -0
- package/dist/chunk-7T2ZE3VT.js +5558 -0
- package/dist/chunk-7T2ZE3VT.js.map +1 -0
- package/dist/chunk-7TDO3CUM.js +393 -0
- package/dist/chunk-7TIX2UAR.js +247 -0
- package/dist/chunk-7V5REBCP.js +148 -0
- package/dist/chunk-7VMZGLYI.js +339 -0
- package/dist/chunk-7W37NS3I.js +236 -0
- package/dist/chunk-7X6SDK2C.js +111 -0
- package/dist/chunk-7ZNUHRP5.js +95 -0
- package/dist/chunk-A36PZALD.js +84 -0
- package/dist/chunk-AUI6VA63.js +107 -0
- package/dist/chunk-AW2MQB37.js +24 -0
- package/dist/chunk-AWKES45Y.js +198 -0
- package/dist/chunk-AXHZYXUV.js +132 -0
- package/dist/chunk-BHNSJVBC.js +129 -0
- package/dist/chunk-BJEJFISW.js +50 -0
- package/dist/chunk-BJFH6JI5.js +422 -0
- package/dist/chunk-BMICEQZQ.js +960 -0
- package/dist/chunk-BTLBYBLU.js +29 -0
- package/dist/chunk-BXBDYNRP.js +89 -0
- package/dist/chunk-C372BFOV.js +357 -0
- package/dist/chunk-C43KRXGA.js +234 -0
- package/dist/chunk-C43KRXGA.js.map +1 -0
- package/dist/chunk-CMKGILJK.js +4824 -0
- package/dist/chunk-CRAOSVHC.js +50 -0
- package/dist/chunk-CRU35QQH.js +281 -0
- package/dist/chunk-CUIWCGMA.js +172 -0
- package/dist/chunk-D5TWNFFE.js +38 -0
- package/dist/chunk-DF5EAHDN.js +4461 -0
- package/dist/chunk-DJAXC5OK.js +56 -0
- package/dist/chunk-DXBYWSL5.js +3550 -0
- package/dist/chunk-DXR5PDFP.js +84 -0
- package/dist/chunk-DZWARC4M.js +267 -0
- package/dist/chunk-DZXEDSQI.js +106 -0
- package/dist/chunk-E7XERZWC.js +48 -0
- package/dist/chunk-ECJBYNLJ.js +79 -0
- package/dist/chunk-EDI6SJ2N.js +43 -0
- package/dist/chunk-EWSOC6WF.js +114 -0
- package/dist/chunk-F2OPHAUN.js +103 -0
- package/dist/chunk-F4ISQGGM.js +81 -0
- package/dist/chunk-FO4LYKJL.js +197 -0
- package/dist/chunk-FPXASDG5.js +73 -0
- package/dist/chunk-FSOQDUCG.js +117 -0
- package/dist/chunk-FSOQDUCG.js.map +1 -0
- package/dist/chunk-FVXBNBHO.js +157 -0
- package/dist/chunk-FYWT2O76.js +34 -0
- package/dist/chunk-FYWT2O76.js.map +1 -0
- package/dist/chunk-FZC5N55X.js +981 -0
- package/dist/chunk-GAVYGBFI.js +99 -0
- package/dist/chunk-GFIMOUDT.js +846 -0
- package/dist/chunk-GFIMOUDT.js.map +1 -0
- package/dist/chunk-GKVJIDHF.js +33 -0
- package/dist/chunk-GVN5SZLX.js +212 -0
- package/dist/chunk-H3T7IWNA.js +134 -0
- package/dist/chunk-HAX4LCR5.js +261 -0
- package/dist/chunk-HC4JFZVU.js +121 -0
- package/dist/chunk-HEFPPMBQ.js +581 -0
- package/dist/chunk-HGKTHF2U.js +1356 -0
- package/dist/chunk-HGKTHF2U.js.map +1 -0
- package/dist/chunk-HNLR7Q4Z.js +83 -0
- package/dist/chunk-HSOXMJOT.js +12 -0
- package/dist/chunk-I4MRDSUF.js +281 -0
- package/dist/chunk-I4QEJ62Y.js +202 -0
- package/dist/chunk-I5B5L6Q4.js +56 -0
- package/dist/chunk-I5QZJODL.js +264 -0
- package/dist/chunk-I7ITSXSJ.js +603 -0
- package/dist/chunk-I7XIZRFD.js +390 -0
- package/dist/chunk-IDANQPRH.js +82 -0
- package/dist/chunk-IFB3WJ36.js +212 -0
- package/dist/chunk-IHTAKKTK.js +264 -0
- package/dist/chunk-IKXNLWAL.js +205 -0
- package/dist/chunk-IKXNLWAL.js.map +1 -0
- package/dist/chunk-ILDXWY37.js +173 -0
- package/dist/chunk-IMDQUHBJ.js +570 -0
- package/dist/chunk-IMZCQBOT.js +119 -0
- package/dist/chunk-IXPTECSC.js +403 -0
- package/dist/chunk-J255A3N5.js +71 -0
- package/dist/chunk-J5F5HPPW.js +141 -0
- package/dist/chunk-JDTGRRNU.js +5045 -0
- package/dist/chunk-JDTGRRNU.js.map +1 -0
- package/dist/chunk-JEA3QDX7.js +2469 -0
- package/dist/chunk-JEUPAVYY.js +156 -0
- package/dist/chunk-JKHKLL5W.js +194 -0
- package/dist/chunk-JQL7ZH6C.js +650 -0
- package/dist/chunk-JWB44UGJ.js +57 -0
- package/dist/chunk-JZ4NLCAP.js +333 -0
- package/dist/chunk-K2A5PXQ5.js +412 -0
- package/dist/chunk-K56XV3C3.js +623 -0
- package/dist/chunk-K6GAXXVA.js +23 -0
- package/dist/chunk-KCBMFSMS.js +199 -0
- package/dist/chunk-KF2FV3ZJ.js +268 -0
- package/dist/chunk-KJD65EJP.js +278 -0
- package/dist/chunk-KLEUWIMZ.js +386 -0
- package/dist/chunk-KODELWYT.js +309 -0
- package/dist/chunk-KRDR5RJD.js +83 -0
- package/dist/chunk-KUDGXAF7.js +914 -0
- package/dist/chunk-KY2R73CG.js +171 -0
- package/dist/chunk-KY2R73CG.js.map +1 -0
- package/dist/chunk-KYS77RPN.js +85 -0
- package/dist/chunk-L7YTNTIK.js +368 -0
- package/dist/chunk-LHPNABDH.js +686 -0
- package/dist/chunk-MFROMEKA.js +401 -0
- package/dist/chunk-MIHN76LX.js +2656 -0
- package/dist/chunk-MIYFS2XN.js +42 -0
- package/dist/chunk-MJUCFQMS.js +65 -0
- package/dist/chunk-MM4FNWI4.js +3064 -0
- package/dist/chunk-MNZG4OHU.js +790 -0
- package/dist/chunk-MVW6HNPI.js +250 -0
- package/dist/chunk-MVW6HNPI.js.map +1 -0
- package/dist/chunk-N6AACDUW.js +200 -0
- package/dist/chunk-NC55IFN3.js +781 -0
- package/dist/chunk-NC7PVOEK.js +98 -0
- package/dist/chunk-NCYMNCJO.js +70 -0
- package/dist/chunk-ND2BXV3P.js +259 -0
- package/dist/chunk-NDBTFUO6.js +272 -0
- package/dist/chunk-NDQJZ7HF.js +206 -0
- package/dist/chunk-NGJJJPZZ.js +162 -0
- package/dist/chunk-NJTSNNE7.js +251 -0
- package/dist/chunk-NPGISCU6.js +4159 -0
- package/dist/chunk-NPGISCU6.js.map +1 -0
- package/dist/chunk-NRTE7DNV.js +1270 -0
- package/dist/chunk-NULUNM7H.js +290 -0
- package/dist/chunk-NUNZS36W.js +227 -0
- package/dist/chunk-NW53H5RW.js +2424 -0
- package/dist/chunk-NXCMHIKI.js +263 -0
- package/dist/chunk-O3LKCBP5.js +159 -0
- package/dist/chunk-O4EA4F3W.js +103 -0
- package/dist/chunk-O4EA4F3W.js.map +1 -0
- package/dist/chunk-O4OOG3D6.js +116 -0
- package/dist/chunk-O4OOG3D6.js.map +1 -0
- package/dist/chunk-O6N4ABCA.js +376 -0
- package/dist/chunk-O732GAFC.js +112 -0
- package/dist/chunk-OF4TIKYC.js +690 -0
- package/dist/chunk-OMPVOMPP.js +70 -0
- package/dist/chunk-OPJJSKUK.js +118 -0
- package/dist/chunk-OPWCMA4D.js +196 -0
- package/dist/chunk-ORJPS5VN.js +1193 -0
- package/dist/chunk-OSY5K7L7.js +187 -0
- package/dist/chunk-OV6QJRT4.js +45 -0
- package/dist/chunk-OVKYVZMW.js +108 -0
- package/dist/chunk-OZCBM3UC.js +171 -0
- package/dist/chunk-P3A2EINJ.js +61 -0
- package/dist/chunk-PTAG4PDC.js +27 -0
- package/dist/chunk-PTF5B7TJ.js +298 -0
- package/dist/chunk-Q3DPEYX4.js +848 -0
- package/dist/chunk-Q6CGOPJD.js +215 -0
- package/dist/chunk-Q6CGOPJD.js.map +1 -0
- package/dist/chunk-QB2KMIDP.js +100 -0
- package/dist/chunk-QCSJ4CWE.js +1067 -0
- package/dist/chunk-QIOQ2NPQ.js +414 -0
- package/dist/chunk-QK5EFEUT.js +353 -0
- package/dist/chunk-QPQBLYQJ.js +41 -0
- package/dist/chunk-QPSGCC5D.js +184 -0
- package/dist/chunk-QZAAUQRN.js +474 -0
- package/dist/chunk-QZM6YDJW.js +489 -0
- package/dist/chunk-QZM6YDJW.js.map +1 -0
- package/dist/chunk-RGBI63KJ.js +188 -0
- package/dist/chunk-RLBKLAEY.js +138 -0
- package/dist/chunk-RMFKVOM7.js +304 -0
- package/dist/chunk-ROKQU7WG.js +258 -0
- package/dist/chunk-RPVREAU7.js +251 -0
- package/dist/chunk-RQKLF42C.js +723 -0
- package/dist/chunk-SC6RRD2D.js +38 -0
- package/dist/chunk-SC6RRD2D.js.map +1 -0
- package/dist/chunk-SEO7TICN.js +94 -0
- package/dist/chunk-SHHJCL7X.js +432 -0
- package/dist/chunk-SIRLACNZ.js +114 -0
- package/dist/chunk-SKPHMYAA.js +237 -0
- package/dist/chunk-SODC7T6L.js +1583 -0
- package/dist/chunk-SQJIKMN3.js +366 -0
- package/dist/chunk-SVINDKVW.js +26 -0
- package/dist/chunk-SYCDHPZF.js +335 -0
- package/dist/chunk-T2QX3FDP.js +133 -0
- package/dist/chunk-T3OWFVMS.js +1187 -0
- package/dist/chunk-T6XI5FLY.js +61 -0
- package/dist/chunk-T7OXJZCX.js +62 -0
- package/dist/chunk-TAT5USZN.js +353 -0
- package/dist/chunk-TPQTRNER.js +172 -0
- package/dist/chunk-TUIN4MB7.js +266 -0
- package/dist/chunk-TX3VECZU.js +71 -0
- package/dist/chunk-TXLIEOJX.js +9624 -0
- package/dist/chunk-U3DN4HEY.js +27 -0
- package/dist/chunk-U7XM7CB4.js +191 -0
- package/dist/chunk-UJK53QFL.js +140 -0
- package/dist/chunk-UMTXA2TH.js +319 -0
- package/dist/chunk-UPDSXFK5.js +199 -0
- package/dist/chunk-UTIAXH52.js +269 -0
- package/dist/chunk-UVN5TMHU.js +1184 -0
- package/dist/chunk-V4Z4B2E3.js +52 -0
- package/dist/chunk-V4Z4B2E3.js.map +1 -0
- package/dist/chunk-VGYXW3VG.js +246 -0
- package/dist/chunk-VS7AHPCZ.js +255 -0
- package/dist/chunk-VTTAJOQ7.js +121 -0
- package/dist/chunk-VWPOLUAO.js +99 -0
- package/dist/chunk-X3MWB2QU.js +3851 -0
- package/dist/chunk-X6KAJI3V.js +211 -0
- package/dist/chunk-XBLRHV5R.js +50 -0
- package/dist/chunk-XDFP2LMW.js +605 -0
- package/dist/chunk-XEQMZB3C.js +525 -0
- package/dist/chunk-XEYLT5VA.js +223 -0
- package/dist/chunk-XHIQ5I6N.js +918 -0
- package/dist/chunk-XLH7QY2R.js +378 -0
- package/dist/chunk-XWFAMUZQ.js +113 -0
- package/dist/chunk-Y4BB2KS2.js +294 -0
- package/dist/chunk-Y4BB2KS2.js.map +1 -0
- package/dist/chunk-Y4OBB2VN.js +84 -0
- package/dist/chunk-YCVD6CI3.js +234 -0
- package/dist/chunk-YENLNISG.js +270 -0
- package/dist/chunk-YIRVLKAX.js +351 -0
- package/dist/chunk-YL73CVNK.js +390 -0
- package/dist/chunk-YLS6QE5G.js +288 -0
- package/dist/chunk-YNF2OC54.js +366 -0
- package/dist/chunk-YNF2OC54.js.map +1 -0
- package/dist/chunk-YPWU5KPE.js +147 -0
- package/dist/chunk-YQXTDZSU.js +2984 -0
- package/dist/chunk-YSJEAAOL.js +178 -0
- package/dist/chunk-YWIFM7D7.js +499 -0
- package/dist/chunk-YWVI6ZMK.js +103 -0
- package/dist/chunk-Z4FMAFNH.js +847 -0
- package/dist/chunk-Z654FWNU.js +136 -0
- package/dist/chunk-Z7Y523PS.js +130 -0
- package/dist/chunk-ZD67RM6G.js +60 -0
- package/dist/chunk-ZEG4VPBL.js +187 -0
- package/dist/chunk-ZHDFQITM.js +283 -0
- package/dist/chunk-ZHDFQITM.js.map +1 -0
- package/dist/chunk-ZLYCOBHW.js +75 -0
- package/dist/chunk-ZNJ5MTXV.js +89 -0
- package/dist/chunk-ZSGY3H53.js +155 -0
- package/dist/chunk-ZTPXACWB.js +1788 -0
- package/dist/chunk-ZUA3JMI6.js +107 -0
- package/dist/chunk-ZYAZUPPV.js +559 -0
- package/dist/composer.js +8 -8
- package/dist/computer-viewer-TDDO25AV.js +21 -0
- package/dist/connect.js +12 -12
- package/dist/desktop-dictation.js +12 -10
- package/dist/desktop-dictation.js.map +1 -1
- package/dist/desktop.js +2 -2
- package/dist/editor.js +2 -2
- package/dist/embedded-new-voice-2G3VLPVQ.js +24 -0
- package/dist/embedded-voice-T5IWOWGD.js +24 -0
- package/dist/fleet-decision-row-WM64KXP3.js +361 -0
- package/dist/in-chat-connect-model.js +5 -5
- package/dist/index.js +51 -51
- package/dist/interaction.js +5 -5
- package/dist/machines.js +3 -3
- package/dist/meetings-recorder.js +1 -1
- package/dist/meetings.js +40 -32
- package/dist/meetings.js.map +1 -1
- package/dist/messages/en.d.ts +3 -0
- package/dist/messages.js +1 -1
- package/dist/model-policy-picker-menu-SMC6J6QN.js +22 -0
- package/dist/payment-page-card-G4U63KUV.js +223 -0
- package/dist/platform-activity-row-BUZGZAKZ.js +128 -0
- package/dist/queue-surface-implementation-4KUZ4PJV.js +908 -0
- package/dist/realtime.js +4 -4
- package/dist/recorder-host-WF2RRR56.js +69 -0
- package/dist/recorder-host-WF2RRR56.js.map +1 -0
- package/dist/recordings.js +3 -3
- package/dist/sandbox-files-ESDMOFSD.js +27 -0
- package/dist/session-app.js +211 -209
- package/dist/session-connect-cards-Q53KMMYY.js +25 -0
- package/dist/session-list-model.js +2 -2
- package/dist/session-realtime-CF3I35WJ.js +30 -0
- package/dist/session-realtime.js +2 -2
- package/dist/session-ui.js +33 -31
- package/dist/session.js +5 -5
- package/dist/sites.js +2 -2
- package/dist/terminal.js +2 -2
- package/dist/this-computer.js +3 -3
- package/dist/timeline-annotation-markers-CJK4RYZ4.js +157 -0
- package/dist/timeline-annotation-selection-67GF5H26.js +208 -0
- package/dist/timeline-annotations-dialog-ONJPQDJQ.js +283 -0
- package/dist/ui/access-list.js +11 -9
- package/dist/ui/access-list.js.map +1 -1
- package/dist/ui/api-error.d.ts +6 -3
- package/dist/ui/api-error.js +2 -1
- package/dist/ui/cadence-picker.js +6 -4
- package/dist/ui/choice-cards.js +5 -3
- package/dist/ui/confirm-dialog.js +2 -2
- package/dist/ui/copy-field.js +5 -3
- package/dist/ui/detail-page.js +2 -2
- package/dist/ui/detail-sheet.js +2 -2
- package/dist/ui/diff-view.js +4 -3
- package/dist/ui/disclosure.js +3 -3
- package/dist/ui/email-chips-input.js +5 -3
- package/dist/ui/error-message.js +2 -1
- package/dist/ui/error-reference.d.ts +15 -0
- package/dist/ui/error-text.d.ts +10 -0
- package/dist/ui/error-text.js +9 -0
- package/dist/ui/error-text.js.map +1 -0
- package/dist/ui/field.js +4 -2
- package/dist/ui/flush-form-page.js +8 -6
- package/dist/ui/form-dialog.js +7 -5
- package/dist/ui/list-row.js +3 -3
- package/dist/ui/load-failure.js +3 -2
- package/dist/ui/load-failure.js.map +1 -1
- package/dist/ui/notice.js +3 -1
- package/dist/ui/relative-time.js +2 -2
- package/dist/ui/revision-history.js +9 -7
- package/dist/ui/revision-history.js.map +1 -1
- package/dist/ui/role-select.js +10 -8
- package/dist/ui/segmented-control.js +4 -2
- package/dist/ui/select-menu.js +5 -3
- package/dist/ui/setting-row.js +3 -1
- package/dist/ui/switch.js +4 -2
- package/dist/ui/usage-meter.js +3 -3
- package/dist/ui/word-list-row.js +4 -2
- package/dist/usage.js +2 -2
- package/dist/workbench.js +4 -4
- package/package.json +5 -6
- package/src/app/app-state.tsx +126 -14
- package/src/app/components/artifacts/site-data.tsx +2 -2
- package/src/app/components/artifacts/site-settings.tsx +2 -2
- package/src/app/components/rail/session-list.tsx +7 -8
- package/src/app/components/rail/session-shell.tsx +3 -3
- package/src/app/components/repository-picker.tsx +8 -1
- package/src/app/components/session/model-context-inspector.tsx +2 -1
- package/src/app/components/session/session-admin-access.tsx +2 -2
- package/src/app/lib/actions/chat-operations.ts +4 -4
- package/src/app/lib/archive-session.test.tsx +265 -2
- package/src/app/lib/archive-session.tsx +175 -9
- package/src/app/lib/copy-link.ts +2 -2
- package/src/app/lib/inbox.tsx +2 -2
- package/src/app/lib/session-failure-toasts.ts +3 -2
- package/src/app/lib/toast.test.tsx +215 -0
- package/src/app/lib/toast.tsx +67 -2
- package/src/app/personal-github-reconnect.test.tsx +356 -0
- package/src/app/routes/artifacts.tsx +1 -2
- package/src/components/tool-action-review.tsx +14 -1
- package/src/meetings/delete-meeting-dialog.tsx +2 -1
- package/src/meetings/meeting-ask.tsx +2 -1
- package/src/meetings/meeting-detail-page.tsx +2 -1
- package/src/meetings/meeting-notes.tsx +2 -1
- package/src/meetings/meeting-settings.tsx +4 -1
- package/src/meetings/meeting-transcript.tsx +4 -1
- package/src/meetings/recorder-context.tsx +10 -0
- package/src/meetings/recorder-host.test.tsx +60 -2
- package/src/meetings/recorder-host.tsx +16 -3
- package/src/meetings/recorder-panel.tsx +2 -1
- package/src/meetings/recorder-workspace.test.tsx +26 -1
- package/src/messages/en.ts +6 -0
- package/src/ui/api-error.test.ts +29 -5
- package/src/ui/api-error.tsx +10 -4
- package/src/ui/error-message.tsx +8 -0
- package/src/ui/error-reference.test.ts +54 -0
- package/src/ui/error-reference.ts +51 -0
- package/src/ui/error-text.test.tsx +66 -0
- package/src/ui/error-text.tsx +49 -0
- package/src/ui/field.tsx +4 -1
- package/src/ui/notice.tsx +7 -2
- package/src/ui/setting-row.tsx +4 -1
- package/styles/app.css +12 -0
- package/styles/compiled.css +9 -0
- package/dist/browser-viewer-NOP5EAUO.js +0 -23
- package/dist/chat-interactive-block-G7O44JHR.js +0 -18
- package/dist/chunk-26RWVLRD.js +0 -135
- package/dist/chunk-2AHTY7OT.js +0 -247
- package/dist/chunk-2GDNWKSC.js +0 -3670
- package/dist/chunk-2JENKGCE.js +0 -339
- package/dist/chunk-2KYQC4T5.js +0 -155
- package/dist/chunk-2RREGDQH.js +0 -708
- package/dist/chunk-2T7DX6KR.js +0 -353
- package/dist/chunk-2VB2O4FQ.js +0 -920
- package/dist/chunk-2VTTKH23.js +0 -94
- package/dist/chunk-2XNG2KNT.js +0 -172
- package/dist/chunk-2YC6JQRO.js +0 -399
- package/dist/chunk-2ZVRJRXC.js +0 -27
- package/dist/chunk-35737UT4.js +0 -162
- package/dist/chunk-366XZ4PM.js +0 -41
- package/dist/chunk-36MIDGAH.js +0 -199
- package/dist/chunk-3FD76LXM.js +0 -212
- package/dist/chunk-3OVIXY4O.js +0 -213
- package/dist/chunk-3OVIXY4O.js.map +0 -1
- package/dist/chunk-3QNRBEYH.js +0 -432
- package/dist/chunk-3QX55WLC.js +0 -15
- package/dist/chunk-3USJPT2S.js +0 -1022
- package/dist/chunk-42CMQ5VR.js +0 -251
- package/dist/chunk-4LPOWQJY.js +0 -42
- package/dist/chunk-4NBTZRKT.js +0 -79
- package/dist/chunk-4OXI6HCV.js +0 -118
- package/dist/chunk-4SZZAFYQ.js +0 -2317
- package/dist/chunk-4TQL3WP2.js +0 -140
- package/dist/chunk-4Y6BK7IU.js +0 -196
- package/dist/chunk-4YD7YZC5.js +0 -373
- package/dist/chunk-5DTST3TP.js +0 -81
- package/dist/chunk-5FH5LQVS.js +0 -130
- package/dist/chunk-5KWMYPJV.js +0 -847
- package/dist/chunk-5MUJDC6C.js +0 -84
- package/dist/chunk-5QM2WPKV.js +0 -41
- package/dist/chunk-5WUN7XPD.js +0 -1184
- package/dist/chunk-64PO2WXU.js +0 -1067
- package/dist/chunk-6AHATAAY.js +0 -431
- package/dist/chunk-6ED2YWBT.js +0 -56
- package/dist/chunk-6GXVYAGR.js +0 -170
- package/dist/chunk-6H4SJBPE.js +0 -85
- package/dist/chunk-6MT3HHUB.js +0 -84
- package/dist/chunk-6UHCBH4G.js +0 -198
- package/dist/chunk-6WLX3NR3.js +0 -198
- package/dist/chunk-73NXYYWE.js +0 -112
- package/dist/chunk-76ERNKIZ.js +0 -603
- package/dist/chunk-776QZ573.js +0 -57
- package/dist/chunk-7BYWOFVH.js +0 -171
- package/dist/chunk-7O5CGGIQ.js +0 -108
- package/dist/chunk-7QOHRZ67.js +0 -251
- package/dist/chunk-7WIDB4PL.js +0 -2058
- package/dist/chunk-7WKP3DYJ.js +0 -37
- package/dist/chunk-7XOVTPDI.js +0 -227
- package/dist/chunk-AB5MBIXY.js +0 -281
- package/dist/chunk-ADNMXBCX.js +0 -1193
- package/dist/chunk-AFBJTUFK.js +0 -202
- package/dist/chunk-ALVRWCXE.js +0 -61
- package/dist/chunk-AOHU6O3M.js +0 -247
- package/dist/chunk-AOHU6O3M.js.map +0 -1
- package/dist/chunk-APW4T4SW.js +0 -270
- package/dist/chunk-AXBSYWKX.js +0 -2656
- package/dist/chunk-B2I5ZW35.js +0 -422
- package/dist/chunk-B3LMRTRF.js +0 -223
- package/dist/chunk-BE2XENG2.js +0 -292
- package/dist/chunk-BE2XENG2.js.map +0 -1
- package/dist/chunk-BF4SADKX.js +0 -29
- package/dist/chunk-BKP43KK4.js +0 -1788
- package/dist/chunk-BM3NEZ6X.js +0 -781
- package/dist/chunk-BT3BAGB5.js +0 -216
- package/dist/chunk-BTSYDIVJ.js +0 -357
- package/dist/chunk-BUBNGKWG.js +0 -70
- package/dist/chunk-BVUJV5WR.js +0 -126
- package/dist/chunk-BZO5ZMUP.js +0 -27
- package/dist/chunk-C3HUF3M2.js +0 -157
- package/dist/chunk-C5QCAOES.js +0 -264
- package/dist/chunk-CGK6ALQU.js +0 -159
- package/dist/chunk-CPYGO76Q.js +0 -335
- package/dist/chunk-CQH4FWYA.js +0 -490
- package/dist/chunk-CZP5QXNB.js +0 -234
- package/dist/chunk-D2TOWEYH.js +0 -266
- package/dist/chunk-D6L4GHRH.js +0 -340
- package/dist/chunk-DKT3N37I.js +0 -50
- package/dist/chunk-DMMJIFKA.js +0 -84
- package/dist/chunk-DO74DQC7.js +0 -26
- package/dist/chunk-DOFJZ2JU.js +0 -83
- package/dist/chunk-DSXVY5WZ.js +0 -119
- package/dist/chunk-E337C2VK.js +0 -82
- package/dist/chunk-E6NRSRBE.js +0 -114
- package/dist/chunk-E6SQKHAU.js +0 -114
- package/dist/chunk-E7UIVD7F.js +0 -236
- package/dist/chunk-EGBII4CP.js +0 -229
- package/dist/chunk-EILSAQVJ.js +0 -200
- package/dist/chunk-ELL6HSYR.js +0 -307
- package/dist/chunk-EUBGLFFR.js +0 -73
- package/dist/chunk-EW6RNWSH.js +0 -212
- package/dist/chunk-EYUT5Y6V.js +0 -2599
- package/dist/chunk-F7U3QPZ3.js +0 -304
- package/dist/chunk-FBTJKSTM.js +0 -1270
- package/dist/chunk-FC2FTSK3.js +0 -2424
- package/dist/chunk-FOGNA3BU.js +0 -148
- package/dist/chunk-FQFKHNC7.js +0 -650
- package/dist/chunk-FTCHM755.js +0 -50
- package/dist/chunk-FXSHP4JZ.js +0 -3550
- package/dist/chunk-G6ZZC543.js +0 -818
- package/dist/chunk-G7FU6PUA.js +0 -100
- package/dist/chunk-GA4YEAMX.js +0 -3851
- package/dist/chunk-GE33BGB7.js +0 -12
- package/dist/chunk-GQ67HSJY.js +0 -570
- package/dist/chunk-GXXZ3WAK.js +0 -106
- package/dist/chunk-H2TCRX3H.js +0 -237
- package/dist/chunk-HLL4PBO7.js +0 -60
- package/dist/chunk-HLOEPV5C.js +0 -259
- package/dist/chunk-HUAGBS4O.js +0 -56
- package/dist/chunk-I36GSRUJ.js +0 -211
- package/dist/chunk-I6VBHT7S.js +0 -261
- package/dist/chunk-ICNNNOXE.js +0 -333
- package/dist/chunk-ICX64J4U.js +0 -172
- package/dist/chunk-ID36Y46W.js +0 -5046
- package/dist/chunk-ID36Y46W.js.map +0 -1
- package/dist/chunk-IGVVXLKJ.js +0 -559
- package/dist/chunk-IKJLSBUI.js +0 -2984
- package/dist/chunk-IKMUFDVS.js +0 -50
- package/dist/chunk-IKUNOX7F.js +0 -255
- package/dist/chunk-ILKDYBON.js +0 -288
- package/dist/chunk-IVYJX3OY.js +0 -187
- package/dist/chunk-JCY4CMDH.js +0 -107
- package/dist/chunk-JJZPLGIP.js +0 -474
- package/dist/chunk-JW33RV4V.js +0 -143
- package/dist/chunk-JWPA6ZPZ.js +0 -351
- package/dist/chunk-JZ4QXS6D.js +0 -50
- package/dist/chunk-K42OUZQ3.js +0 -386
- package/dist/chunk-K5TOVX5A.js +0 -206
- package/dist/chunk-K63HGHZX.js +0 -99
- package/dist/chunk-K6R5T44I.js +0 -75
- package/dist/chunk-KHJ5PXFM.js +0 -914
- package/dist/chunk-KJZRQ55O.js +0 -2319
- package/dist/chunk-KJZRQ55O.js.map +0 -1
- package/dist/chunk-KKI55ZMW.js +0 -65
- package/dist/chunk-KRPLAAFP.js +0 -199
- package/dist/chunk-KVFEZDBO.js +0 -134
- package/dist/chunk-L4I665WK.js +0 -45
- package/dist/chunk-L4OF77LE.js +0 -110
- package/dist/chunk-L4OF77LE.js.map +0 -1
- package/dist/chunk-LA5T52RM.js +0 -70
- package/dist/chunk-LM2W5YAT.js +0 -401
- package/dist/chunk-LQ62ECE6.js +0 -390
- package/dist/chunk-LQDUHU3Y.js +0 -71
- package/dist/chunk-LRJIYTZW.js +0 -4157
- package/dist/chunk-LRJIYTZW.js.map +0 -1
- package/dist/chunk-LRN6GYAZ.js +0 -138
- package/dist/chunk-M4SDVTKJ.js +0 -790
- package/dist/chunk-MA6O7CM6.js +0 -99
- package/dist/chunk-MBIBB6GI.js +0 -197
- package/dist/chunk-MF3ZFB4H.js +0 -199
- package/dist/chunk-MJJGJMYJ.js +0 -98
- package/dist/chunk-MLJADQFI.js +0 -4824
- package/dist/chunk-MQG7RV3B.js +0 -62
- package/dist/chunk-MTTGSQA5.js +0 -186
- package/dist/chunk-MW7Y3T55.js +0 -525
- package/dist/chunk-MYUFMVCH.js +0 -121
- package/dist/chunk-N3OARGH2.js +0 -61
- package/dist/chunk-NB2HMRT2.js +0 -353
- package/dist/chunk-NFDFYWMV.js +0 -141
- package/dist/chunk-NWGNFN3N.js +0 -1187
- package/dist/chunk-NWJRPDIO.js +0 -840
- package/dist/chunk-NWJRPDIO.js.map +0 -1
- package/dist/chunk-NXWBBZJV.js +0 -267
- package/dist/chunk-NY2QWROL.js +0 -71
- package/dist/chunk-O2IOPP3R.js +0 -848
- package/dist/chunk-O2UWH5RG.js +0 -141
- package/dist/chunk-O5YWDLYF.js +0 -393
- package/dist/chunk-OBMCZO7E.js +0 -121
- package/dist/chunk-OD3DCCCB.js +0 -132
- package/dist/chunk-OMH7R6I3.js +0 -390
- package/dist/chunk-OSA5EQG5.js +0 -113
- package/dist/chunk-OSJ3MUTZ.js +0 -2936
- package/dist/chunk-OWAYJ2OV.js +0 -368
- package/dist/chunk-OYXBFSGK.js +0 -269
- package/dist/chunk-PC2EBA5G.js +0 -4116
- package/dist/chunk-PC2EBA5G.js.map +0 -1
- package/dist/chunk-PC2MVXIP.js +0 -363
- package/dist/chunk-PC2MVXIP.js.map +0 -1
- package/dist/chunk-PENGLDR5.js +0 -309
- package/dist/chunk-PSMAWRPL.js +0 -191
- package/dist/chunk-PW4IMTYR.js +0 -702
- package/dist/chunk-PW54V7T5.js +0 -387
- package/dist/chunk-Q2EXYSMO.js +0 -24
- package/dist/chunk-Q2XJK5I4.js +0 -51
- package/dist/chunk-Q2XJK5I4.js.map +0 -1
- package/dist/chunk-Q4EFRI6X.js +0 -258
- package/dist/chunk-Q52CXW6C.js +0 -48
- package/dist/chunk-Q6XQ2R37.js +0 -184
- package/dist/chunk-QAJ53XBW.js +0 -23
- package/dist/chunk-QDTKECT4.js +0 -129
- package/dist/chunk-QE3ZFGI3.js +0 -43
- package/dist/chunk-QHHSUR2Y.js +0 -298
- package/dist/chunk-QHNVNR7B.js +0 -173
- package/dist/chunk-QIK2Q6N5.js +0 -376
- package/dist/chunk-QWL4L366.js +0 -38
- package/dist/chunk-R3J2PTVQ.js +0 -690
- package/dist/chunk-RBRX5ZS3.js +0 -319
- package/dist/chunk-REX6JIC7.js +0 -33
- package/dist/chunk-RGZ7EEC3.js +0 -537
- package/dist/chunk-RGZ7EEC3.js.map +0 -1
- package/dist/chunk-RIPS7GFR.js +0 -960
- package/dist/chunk-RTBZNFNV.js +0 -1354
- package/dist/chunk-RTBZNFNV.js.map +0 -1
- package/dist/chunk-SKMNW4SW.js +0 -187
- package/dist/chunk-SRFARKXC.js +0 -623
- package/dist/chunk-SWIHWCTW.js +0 -4461
- package/dist/chunk-TA5CFTO4.js +0 -103
- package/dist/chunk-TF4ZVJLI.js +0 -156
- package/dist/chunk-TG7DADJR.js +0 -9624
- package/dist/chunk-TI4R2ZD5.js +0 -623
- package/dist/chunk-TI4R2ZD5.js.map +0 -1
- package/dist/chunk-TNKWOUWP.js +0 -412
- package/dist/chunk-TNUSPXPW.js +0 -5555
- package/dist/chunk-TNUSPXPW.js.map +0 -1
- package/dist/chunk-TNYFHKVV.js +0 -366
- package/dist/chunk-TPMZBOFH.js +0 -178
- package/dist/chunk-TW3LKRJF.js +0 -263
- package/dist/chunk-TZAO65I4.js +0 -83
- package/dist/chunk-U5EFEMYU.js +0 -89
- package/dist/chunk-U6M4LS3M.js +0 -378
- package/dist/chunk-UI4HI5QK.js +0 -918
- package/dist/chunk-UJDOWCQ3.js +0 -272
- package/dist/chunk-UJUQ2QOX.js +0 -581
- package/dist/chunk-UQ3IM6FY.js +0 -133
- package/dist/chunk-UYE47LO6.js +0 -246
- package/dist/chunk-VADHIT26.js +0 -89
- package/dist/chunk-VFQ3H6CO.js +0 -163
- package/dist/chunk-VGUN6VB2.js +0 -188
- package/dist/chunk-VGWWEVSD.js +0 -723
- package/dist/chunk-VIEFJ2DM.js +0 -2469
- package/dist/chunk-VLXM36VS.js +0 -268
- package/dist/chunk-W3BFRR2G.js +0 -499
- package/dist/chunk-W5MPXR3A.js +0 -981
- package/dist/chunk-W5OWKVZG.js +0 -136
- package/dist/chunk-W6FRORMX.js +0 -486
- package/dist/chunk-W6FRORMX.js.map +0 -1
- package/dist/chunk-W7XROSZO.js +0 -232
- package/dist/chunk-W7XROSZO.js.map +0 -1
- package/dist/chunk-WEZ5PXZP.js +0 -403
- package/dist/chunk-WHWUWN2X.js +0 -197
- package/dist/chunk-WHWUWN2X.js.map +0 -1
- package/dist/chunk-WLEDGIVS.js +0 -114
- package/dist/chunk-WLEDGIVS.js.map +0 -1
- package/dist/chunk-WNDAKLHG.js +0 -281
- package/dist/chunk-WTJ4PLMU.js +0 -103
- package/dist/chunk-WV2KPJUW.js +0 -107
- package/dist/chunk-X3VY5EGY.js +0 -277
- package/dist/chunk-X3VY5EGY.js.map +0 -1
- package/dist/chunk-XDBYWVCW.js +0 -418
- package/dist/chunk-XF7MA4U5.js +0 -36
- package/dist/chunk-XF7MA4U5.js.map +0 -1
- package/dist/chunk-XOSQP3YA.js +0 -605
- package/dist/chunk-XYBV4ZJQ.js +0 -271
- package/dist/chunk-YBKVGLQR.js +0 -147
- package/dist/chunk-YDCUBUWH.js +0 -1583
- package/dist/chunk-YG6NARQY.js +0 -51
- package/dist/chunk-YG6NARQY.js.map +0 -1
- package/dist/chunk-YSVD5HI4.js +0 -278
- package/dist/chunk-YT3WGNG6.js +0 -290
- package/dist/chunk-YYICQDK4.js +0 -414
- package/dist/chunk-YYLMZLI6.js +0 -686
- package/dist/chunk-ZAMTKG5U.js +0 -95
- package/dist/chunk-ZDLYSXKN.js +0 -111
- package/dist/chunk-ZG447FSS.js +0 -194
- package/dist/chunk-ZIT5KD4Q.js +0 -264
- package/dist/chunk-ZUDOVDKR.js +0 -3064
- package/dist/chunk-ZW2ECJ3A.js +0 -451
- package/dist/computer-viewer-LPZ763KF.js +0 -21
- package/dist/embedded-new-voice-YVMKSLKJ.js +0 -24
- package/dist/embedded-voice-BJPQFXSU.js +0 -24
- package/dist/fleet-decision-row-AA4H5CDT.js +0 -361
- package/dist/model-policy-picker-menu-HSMWVO4P.js +0 -22
- package/dist/payment-page-card-3LTSFJGN.js +0 -223
- package/dist/platform-activity-row-5VYV6TX4.js +0 -128
- package/dist/queue-surface-implementation-NXHZUKD2.js +0 -908
- package/dist/recorder-host-ZKRWTDBM.js +0 -57
- package/dist/recorder-host-ZKRWTDBM.js.map +0 -1
- package/dist/sandbox-files-YCMLQUFU.js +0 -27
- package/dist/session-connect-cards-KSQLMRM5.js +0 -25
- package/dist/session-realtime-ZR6JLG3Y.js +0 -30
- package/dist/timeline-annotation-markers-YAHDPWVG.js +0 -157
- package/dist/timeline-annotation-selection-KHXNCEJP.js +0 -208
- package/dist/timeline-annotations-dialog-Y4XD3N7B.js +0 -283
- /package/dist/{browser-viewer-NOP5EAUO.js.map → browser-viewer-TC27OCIP.js.map} +0 -0
- /package/dist/{chat-interactive-block-G7O44JHR.js.map → chat-interactive-block-RWPQBHVS.js.map} +0 -0
- /package/dist/{chunk-EGBII4CP.js.map → chunk-2BDT3UZQ.js.map} +0 -0
- /package/dist/{chunk-4YD7YZC5.js.map → chunk-2CLNIEK4.js.map} +0 -0
- /package/dist/{chunk-ZW2ECJ3A.js.map → chunk-2FSBSCZJ.js.map} +0 -0
- /package/dist/{chunk-KRPLAAFP.js.map → chunk-2I7HDP53.js.map} +0 -0
- /package/dist/{chunk-3QX55WLC.js.map → chunk-2INLDVK3.js.map} +0 -0
- /package/dist/{chunk-BVUJV5WR.js.map → chunk-2KALVELR.js.map} +0 -0
- /package/dist/{chunk-XDBYWVCW.js.map → chunk-2M3J7ZWQ.js.map} +0 -0
- /package/dist/{chunk-2RREGDQH.js.map → chunk-2OP6WLGV.js.map} +0 -0
- /package/dist/{chunk-OSJ3MUTZ.js.map → chunk-2RG7NIAS.js.map} +0 -0
- /package/dist/{chunk-26RWVLRD.js.map → chunk-2STUPWEL.js.map} +0 -0
- /package/dist/{chunk-XYBV4ZJQ.js.map → chunk-2YUOVM3G.js.map} +0 -0
- /package/dist/{chunk-2GDNWKSC.js.map → chunk-2Z3NQNTM.js.map} +0 -0
- /package/dist/{chunk-2VB2O4FQ.js.map → chunk-3CPWWGNK.js.map} +0 -0
- /package/dist/{chunk-7WKP3DYJ.js.map → chunk-3ED2U6XE.js.map} +0 -0
- /package/dist/{chunk-BT3BAGB5.js.map → chunk-3GZA4BUW.js.map} +0 -0
- /package/dist/{chunk-3USJPT2S.js.map → chunk-3IK6UYO6.js.map} +0 -0
- /package/dist/{chunk-6AHATAAY.js.map → chunk-3PZ7JZI2.js.map} +0 -0
- /package/dist/{chunk-JZ4QXS6D.js.map → chunk-4DVJ6ONS.js.map} +0 -0
- /package/dist/{chunk-5QM2WPKV.js.map → chunk-4E3ZJRTZ.js.map} +0 -0
- /package/dist/{chunk-NFDFYWMV.js.map → chunk-4SAWSRAA.js.map} +0 -0
- /package/dist/{chunk-ELL6HSYR.js.map → chunk-53AGXXZM.js.map} +0 -0
- /package/dist/{chunk-G6ZZC543.js.map → chunk-54YW6SZZ.js.map} +0 -0
- /package/dist/{chunk-4SZZAFYQ.js.map → chunk-57AZS3IE.js.map} +0 -0
- /package/dist/{chunk-JW33RV4V.js.map → chunk-5CPEEAFD.js.map} +0 -0
- /package/dist/{chunk-D6L4GHRH.js.map → chunk-5EWSRZ2Y.js.map} +0 -0
- /package/dist/{chunk-PW54V7T5.js.map → chunk-5IE76VCY.js.map} +0 -0
- /package/dist/{chunk-6GXVYAGR.js.map → chunk-5NWEKLDZ.js.map} +0 -0
- /package/dist/{chunk-6UHCBH4G.js.map → chunk-5O2FCCHZ.js.map} +0 -0
- /package/dist/{chunk-2YC6JQRO.js.map → chunk-5QAY6K77.js.map} +0 -0
- /package/dist/{chunk-EYUT5Y6V.js.map → chunk-6DK6Z4UN.js.map} +0 -0
- /package/dist/{chunk-PW4IMTYR.js.map → chunk-6DVCC7QS.js.map} +0 -0
- /package/dist/{chunk-7WIDB4PL.js.map → chunk-6JHU2UBS.js.map} +0 -0
- /package/dist/{chunk-CQH4FWYA.js.map → chunk-6PZ7OBYU.js.map} +0 -0
- /package/dist/{chunk-VFQ3H6CO.js.map → chunk-6WGRDRMA.js.map} +0 -0
- /package/dist/{chunk-MTTGSQA5.js.map → chunk-77WAKEAH.js.map} +0 -0
- /package/dist/{chunk-O5YWDLYF.js.map → chunk-7TDO3CUM.js.map} +0 -0
- /package/dist/{chunk-2AHTY7OT.js.map → chunk-7TIX2UAR.js.map} +0 -0
- /package/dist/{chunk-FOGNA3BU.js.map → chunk-7V5REBCP.js.map} +0 -0
- /package/dist/{chunk-2JENKGCE.js.map → chunk-7VMZGLYI.js.map} +0 -0
- /package/dist/{chunk-E7UIVD7F.js.map → chunk-7W37NS3I.js.map} +0 -0
- /package/dist/{chunk-ZDLYSXKN.js.map → chunk-7X6SDK2C.js.map} +0 -0
- /package/dist/{chunk-ZAMTKG5U.js.map → chunk-7ZNUHRP5.js.map} +0 -0
- /package/dist/{chunk-DMMJIFKA.js.map → chunk-A36PZALD.js.map} +0 -0
- /package/dist/{chunk-WV2KPJUW.js.map → chunk-AUI6VA63.js.map} +0 -0
- /package/dist/{chunk-Q2EXYSMO.js.map → chunk-AW2MQB37.js.map} +0 -0
- /package/dist/{chunk-6WLX3NR3.js.map → chunk-AWKES45Y.js.map} +0 -0
- /package/dist/{chunk-OD3DCCCB.js.map → chunk-AXHZYXUV.js.map} +0 -0
- /package/dist/{chunk-QDTKECT4.js.map → chunk-BHNSJVBC.js.map} +0 -0
- /package/dist/{chunk-FTCHM755.js.map → chunk-BJEJFISW.js.map} +0 -0
- /package/dist/{chunk-B2I5ZW35.js.map → chunk-BJFH6JI5.js.map} +0 -0
- /package/dist/{chunk-RIPS7GFR.js.map → chunk-BMICEQZQ.js.map} +0 -0
- /package/dist/{chunk-BF4SADKX.js.map → chunk-BTLBYBLU.js.map} +0 -0
- /package/dist/{chunk-U5EFEMYU.js.map → chunk-BXBDYNRP.js.map} +0 -0
- /package/dist/{chunk-BTSYDIVJ.js.map → chunk-C372BFOV.js.map} +0 -0
- /package/dist/{chunk-MLJADQFI.js.map → chunk-CMKGILJK.js.map} +0 -0
- /package/dist/{chunk-DKT3N37I.js.map → chunk-CRAOSVHC.js.map} +0 -0
- /package/dist/{chunk-WNDAKLHG.js.map → chunk-CRU35QQH.js.map} +0 -0
- /package/dist/{chunk-2XNG2KNT.js.map → chunk-CUIWCGMA.js.map} +0 -0
- /package/dist/{chunk-QWL4L366.js.map → chunk-D5TWNFFE.js.map} +0 -0
- /package/dist/{chunk-SWIHWCTW.js.map → chunk-DF5EAHDN.js.map} +0 -0
- /package/dist/{chunk-HUAGBS4O.js.map → chunk-DJAXC5OK.js.map} +0 -0
- /package/dist/{chunk-FXSHP4JZ.js.map → chunk-DXBYWSL5.js.map} +0 -0
- /package/dist/{chunk-5MUJDC6C.js.map → chunk-DXR5PDFP.js.map} +0 -0
- /package/dist/{chunk-NXWBBZJV.js.map → chunk-DZWARC4M.js.map} +0 -0
- /package/dist/{chunk-GXXZ3WAK.js.map → chunk-DZXEDSQI.js.map} +0 -0
- /package/dist/{chunk-Q52CXW6C.js.map → chunk-E7XERZWC.js.map} +0 -0
- /package/dist/{chunk-4NBTZRKT.js.map → chunk-ECJBYNLJ.js.map} +0 -0
- /package/dist/{chunk-QE3ZFGI3.js.map → chunk-EDI6SJ2N.js.map} +0 -0
- /package/dist/{chunk-E6NRSRBE.js.map → chunk-EWSOC6WF.js.map} +0 -0
- /package/dist/{chunk-TA5CFTO4.js.map → chunk-F2OPHAUN.js.map} +0 -0
- /package/dist/{chunk-5DTST3TP.js.map → chunk-F4ISQGGM.js.map} +0 -0
- /package/dist/{chunk-MBIBB6GI.js.map → chunk-FO4LYKJL.js.map} +0 -0
- /package/dist/{chunk-EUBGLFFR.js.map → chunk-FPXASDG5.js.map} +0 -0
- /package/dist/{chunk-C3HUF3M2.js.map → chunk-FVXBNBHO.js.map} +0 -0
- /package/dist/{chunk-W5MPXR3A.js.map → chunk-FZC5N55X.js.map} +0 -0
- /package/dist/{chunk-MA6O7CM6.js.map → chunk-GAVYGBFI.js.map} +0 -0
- /package/dist/{chunk-REX6JIC7.js.map → chunk-GKVJIDHF.js.map} +0 -0
- /package/dist/{chunk-EW6RNWSH.js.map → chunk-GVN5SZLX.js.map} +0 -0
- /package/dist/{chunk-KVFEZDBO.js.map → chunk-H3T7IWNA.js.map} +0 -0
- /package/dist/{chunk-I6VBHT7S.js.map → chunk-HAX4LCR5.js.map} +0 -0
- /package/dist/{chunk-OBMCZO7E.js.map → chunk-HC4JFZVU.js.map} +0 -0
- /package/dist/{chunk-UJUQ2QOX.js.map → chunk-HEFPPMBQ.js.map} +0 -0
- /package/dist/{chunk-DOFJZ2JU.js.map → chunk-HNLR7Q4Z.js.map} +0 -0
- /package/dist/{chunk-GE33BGB7.js.map → chunk-HSOXMJOT.js.map} +0 -0
- /package/dist/{chunk-AB5MBIXY.js.map → chunk-I4MRDSUF.js.map} +0 -0
- /package/dist/{chunk-AFBJTUFK.js.map → chunk-I4QEJ62Y.js.map} +0 -0
- /package/dist/{chunk-6ED2YWBT.js.map → chunk-I5B5L6Q4.js.map} +0 -0
- /package/dist/{chunk-C5QCAOES.js.map → chunk-I5QZJODL.js.map} +0 -0
- /package/dist/{chunk-76ERNKIZ.js.map → chunk-I7ITSXSJ.js.map} +0 -0
- /package/dist/{chunk-OMH7R6I3.js.map → chunk-I7XIZRFD.js.map} +0 -0
- /package/dist/{chunk-E337C2VK.js.map → chunk-IDANQPRH.js.map} +0 -0
- /package/dist/{chunk-3FD76LXM.js.map → chunk-IFB3WJ36.js.map} +0 -0
- /package/dist/{chunk-ZIT5KD4Q.js.map → chunk-IHTAKKTK.js.map} +0 -0
- /package/dist/{chunk-QHNVNR7B.js.map → chunk-ILDXWY37.js.map} +0 -0
- /package/dist/{chunk-GQ67HSJY.js.map → chunk-IMDQUHBJ.js.map} +0 -0
- /package/dist/{chunk-DSXVY5WZ.js.map → chunk-IMZCQBOT.js.map} +0 -0
- /package/dist/{chunk-WEZ5PXZP.js.map → chunk-IXPTECSC.js.map} +0 -0
- /package/dist/{chunk-NY2QWROL.js.map → chunk-J255A3N5.js.map} +0 -0
- /package/dist/{chunk-O2UWH5RG.js.map → chunk-J5F5HPPW.js.map} +0 -0
- /package/dist/{chunk-VIEFJ2DM.js.map → chunk-JEA3QDX7.js.map} +0 -0
- /package/dist/{chunk-TF4ZVJLI.js.map → chunk-JEUPAVYY.js.map} +0 -0
- /package/dist/{chunk-ZG447FSS.js.map → chunk-JKHKLL5W.js.map} +0 -0
- /package/dist/{chunk-FQFKHNC7.js.map → chunk-JQL7ZH6C.js.map} +0 -0
- /package/dist/{chunk-776QZ573.js.map → chunk-JWB44UGJ.js.map} +0 -0
- /package/dist/{chunk-ICNNNOXE.js.map → chunk-JZ4NLCAP.js.map} +0 -0
- /package/dist/{chunk-TNKWOUWP.js.map → chunk-K2A5PXQ5.js.map} +0 -0
- /package/dist/{chunk-SRFARKXC.js.map → chunk-K56XV3C3.js.map} +0 -0
- /package/dist/{chunk-QAJ53XBW.js.map → chunk-K6GAXXVA.js.map} +0 -0
- /package/dist/{chunk-MF3ZFB4H.js.map → chunk-KCBMFSMS.js.map} +0 -0
- /package/dist/{chunk-VLXM36VS.js.map → chunk-KF2FV3ZJ.js.map} +0 -0
- /package/dist/{chunk-YSVD5HI4.js.map → chunk-KJD65EJP.js.map} +0 -0
- /package/dist/{chunk-K42OUZQ3.js.map → chunk-KLEUWIMZ.js.map} +0 -0
- /package/dist/{chunk-PENGLDR5.js.map → chunk-KODELWYT.js.map} +0 -0
- /package/dist/{chunk-TZAO65I4.js.map → chunk-KRDR5RJD.js.map} +0 -0
- /package/dist/{chunk-KHJ5PXFM.js.map → chunk-KUDGXAF7.js.map} +0 -0
- /package/dist/{chunk-6H4SJBPE.js.map → chunk-KYS77RPN.js.map} +0 -0
- /package/dist/{chunk-OWAYJ2OV.js.map → chunk-L7YTNTIK.js.map} +0 -0
- /package/dist/{chunk-YYLMZLI6.js.map → chunk-LHPNABDH.js.map} +0 -0
- /package/dist/{chunk-LM2W5YAT.js.map → chunk-MFROMEKA.js.map} +0 -0
- /package/dist/{chunk-AXBSYWKX.js.map → chunk-MIHN76LX.js.map} +0 -0
- /package/dist/{chunk-4LPOWQJY.js.map → chunk-MIYFS2XN.js.map} +0 -0
- /package/dist/{chunk-KKI55ZMW.js.map → chunk-MJUCFQMS.js.map} +0 -0
- /package/dist/{chunk-ZUDOVDKR.js.map → chunk-MM4FNWI4.js.map} +0 -0
- /package/dist/{chunk-M4SDVTKJ.js.map → chunk-MNZG4OHU.js.map} +0 -0
- /package/dist/{chunk-EILSAQVJ.js.map → chunk-N6AACDUW.js.map} +0 -0
- /package/dist/{chunk-BM3NEZ6X.js.map → chunk-NC55IFN3.js.map} +0 -0
- /package/dist/{chunk-MJJGJMYJ.js.map → chunk-NC7PVOEK.js.map} +0 -0
- /package/dist/{chunk-LA5T52RM.js.map → chunk-NCYMNCJO.js.map} +0 -0
- /package/dist/{chunk-HLOEPV5C.js.map → chunk-ND2BXV3P.js.map} +0 -0
- /package/dist/{chunk-UJDOWCQ3.js.map → chunk-NDBTFUO6.js.map} +0 -0
- /package/dist/{chunk-K5TOVX5A.js.map → chunk-NDQJZ7HF.js.map} +0 -0
- /package/dist/{chunk-35737UT4.js.map → chunk-NGJJJPZZ.js.map} +0 -0
- /package/dist/{chunk-42CMQ5VR.js.map → chunk-NJTSNNE7.js.map} +0 -0
- /package/dist/{chunk-FBTJKSTM.js.map → chunk-NRTE7DNV.js.map} +0 -0
- /package/dist/{chunk-YT3WGNG6.js.map → chunk-NULUNM7H.js.map} +0 -0
- /package/dist/{chunk-7XOVTPDI.js.map → chunk-NUNZS36W.js.map} +0 -0
- /package/dist/{chunk-FC2FTSK3.js.map → chunk-NW53H5RW.js.map} +0 -0
- /package/dist/{chunk-TW3LKRJF.js.map → chunk-NXCMHIKI.js.map} +0 -0
- /package/dist/{chunk-CGK6ALQU.js.map → chunk-O3LKCBP5.js.map} +0 -0
- /package/dist/{chunk-QIK2Q6N5.js.map → chunk-O6N4ABCA.js.map} +0 -0
- /package/dist/{chunk-73NXYYWE.js.map → chunk-O732GAFC.js.map} +0 -0
- /package/dist/{chunk-R3J2PTVQ.js.map → chunk-OF4TIKYC.js.map} +0 -0
- /package/dist/{chunk-BUBNGKWG.js.map → chunk-OMPVOMPP.js.map} +0 -0
- /package/dist/{chunk-4OXI6HCV.js.map → chunk-OPJJSKUK.js.map} +0 -0
- /package/dist/{chunk-4Y6BK7IU.js.map → chunk-OPWCMA4D.js.map} +0 -0
- /package/dist/{chunk-ADNMXBCX.js.map → chunk-ORJPS5VN.js.map} +0 -0
- /package/dist/{chunk-SKMNW4SW.js.map → chunk-OSY5K7L7.js.map} +0 -0
- /package/dist/{chunk-L4I665WK.js.map → chunk-OV6QJRT4.js.map} +0 -0
- /package/dist/{chunk-7O5CGGIQ.js.map → chunk-OVKYVZMW.js.map} +0 -0
- /package/dist/{chunk-7BYWOFVH.js.map → chunk-OZCBM3UC.js.map} +0 -0
- /package/dist/{chunk-ALVRWCXE.js.map → chunk-P3A2EINJ.js.map} +0 -0
- /package/dist/{chunk-BZO5ZMUP.js.map → chunk-PTAG4PDC.js.map} +0 -0
- /package/dist/{chunk-QHHSUR2Y.js.map → chunk-PTF5B7TJ.js.map} +0 -0
- /package/dist/{chunk-O2IOPP3R.js.map → chunk-Q3DPEYX4.js.map} +0 -0
- /package/dist/{chunk-G7FU6PUA.js.map → chunk-QB2KMIDP.js.map} +0 -0
- /package/dist/{chunk-64PO2WXU.js.map → chunk-QCSJ4CWE.js.map} +0 -0
- /package/dist/{chunk-YYICQDK4.js.map → chunk-QIOQ2NPQ.js.map} +0 -0
- /package/dist/{chunk-NB2HMRT2.js.map → chunk-QK5EFEUT.js.map} +0 -0
- /package/dist/{chunk-366XZ4PM.js.map → chunk-QPQBLYQJ.js.map} +0 -0
- /package/dist/{chunk-Q6XQ2R37.js.map → chunk-QPSGCC5D.js.map} +0 -0
- /package/dist/{chunk-JJZPLGIP.js.map → chunk-QZAAUQRN.js.map} +0 -0
- /package/dist/{chunk-VGUN6VB2.js.map → chunk-RGBI63KJ.js.map} +0 -0
- /package/dist/{chunk-LRN6GYAZ.js.map → chunk-RLBKLAEY.js.map} +0 -0
- /package/dist/{chunk-F7U3QPZ3.js.map → chunk-RMFKVOM7.js.map} +0 -0
- /package/dist/{chunk-Q4EFRI6X.js.map → chunk-ROKQU7WG.js.map} +0 -0
- /package/dist/{chunk-7QOHRZ67.js.map → chunk-RPVREAU7.js.map} +0 -0
- /package/dist/{chunk-VGWWEVSD.js.map → chunk-RQKLF42C.js.map} +0 -0
- /package/dist/{chunk-2VTTKH23.js.map → chunk-SEO7TICN.js.map} +0 -0
- /package/dist/{chunk-3QNRBEYH.js.map → chunk-SHHJCL7X.js.map} +0 -0
- /package/dist/{chunk-E6SQKHAU.js.map → chunk-SIRLACNZ.js.map} +0 -0
- /package/dist/{chunk-H2TCRX3H.js.map → chunk-SKPHMYAA.js.map} +0 -0
- /package/dist/{chunk-YDCUBUWH.js.map → chunk-SODC7T6L.js.map} +0 -0
- /package/dist/{chunk-TNYFHKVV.js.map → chunk-SQJIKMN3.js.map} +0 -0
- /package/dist/{chunk-DO74DQC7.js.map → chunk-SVINDKVW.js.map} +0 -0
- /package/dist/{chunk-CPYGO76Q.js.map → chunk-SYCDHPZF.js.map} +0 -0
- /package/dist/{chunk-UQ3IM6FY.js.map → chunk-T2QX3FDP.js.map} +0 -0
- /package/dist/{chunk-NWGNFN3N.js.map → chunk-T3OWFVMS.js.map} +0 -0
- /package/dist/{chunk-N3OARGH2.js.map → chunk-T6XI5FLY.js.map} +0 -0
- /package/dist/{chunk-MQG7RV3B.js.map → chunk-T7OXJZCX.js.map} +0 -0
- /package/dist/{chunk-2T7DX6KR.js.map → chunk-TAT5USZN.js.map} +0 -0
- /package/dist/{chunk-ICX64J4U.js.map → chunk-TPQTRNER.js.map} +0 -0
- /package/dist/{chunk-D2TOWEYH.js.map → chunk-TUIN4MB7.js.map} +0 -0
- /package/dist/{chunk-LQDUHU3Y.js.map → chunk-TX3VECZU.js.map} +0 -0
- /package/dist/{chunk-TG7DADJR.js.map → chunk-TXLIEOJX.js.map} +0 -0
- /package/dist/{chunk-2ZVRJRXC.js.map → chunk-U3DN4HEY.js.map} +0 -0
- /package/dist/{chunk-PSMAWRPL.js.map → chunk-U7XM7CB4.js.map} +0 -0
- /package/dist/{chunk-4TQL3WP2.js.map → chunk-UJK53QFL.js.map} +0 -0
- /package/dist/{chunk-RBRX5ZS3.js.map → chunk-UMTXA2TH.js.map} +0 -0
- /package/dist/{chunk-36MIDGAH.js.map → chunk-UPDSXFK5.js.map} +0 -0
- /package/dist/{chunk-OYXBFSGK.js.map → chunk-UTIAXH52.js.map} +0 -0
- /package/dist/{chunk-5WUN7XPD.js.map → chunk-UVN5TMHU.js.map} +0 -0
- /package/dist/{chunk-UYE47LO6.js.map → chunk-VGYXW3VG.js.map} +0 -0
- /package/dist/{chunk-IKUNOX7F.js.map → chunk-VS7AHPCZ.js.map} +0 -0
- /package/dist/{chunk-MYUFMVCH.js.map → chunk-VTTAJOQ7.js.map} +0 -0
- /package/dist/{chunk-K63HGHZX.js.map → chunk-VWPOLUAO.js.map} +0 -0
- /package/dist/{chunk-GA4YEAMX.js.map → chunk-X3MWB2QU.js.map} +0 -0
- /package/dist/{chunk-I36GSRUJ.js.map → chunk-X6KAJI3V.js.map} +0 -0
- /package/dist/{chunk-IKMUFDVS.js.map → chunk-XBLRHV5R.js.map} +0 -0
- /package/dist/{chunk-XOSQP3YA.js.map → chunk-XDFP2LMW.js.map} +0 -0
- /package/dist/{chunk-MW7Y3T55.js.map → chunk-XEQMZB3C.js.map} +0 -0
- /package/dist/{chunk-B3LMRTRF.js.map → chunk-XEYLT5VA.js.map} +0 -0
- /package/dist/{chunk-UI4HI5QK.js.map → chunk-XHIQ5I6N.js.map} +0 -0
- /package/dist/{chunk-U6M4LS3M.js.map → chunk-XLH7QY2R.js.map} +0 -0
- /package/dist/{chunk-OSA5EQG5.js.map → chunk-XWFAMUZQ.js.map} +0 -0
- /package/dist/{chunk-6MT3HHUB.js.map → chunk-Y4OBB2VN.js.map} +0 -0
- /package/dist/{chunk-CZP5QXNB.js.map → chunk-YCVD6CI3.js.map} +0 -0
- /package/dist/{chunk-APW4T4SW.js.map → chunk-YENLNISG.js.map} +0 -0
- /package/dist/{chunk-JWPA6ZPZ.js.map → chunk-YIRVLKAX.js.map} +0 -0
- /package/dist/{chunk-LQ62ECE6.js.map → chunk-YL73CVNK.js.map} +0 -0
- /package/dist/{chunk-ILKDYBON.js.map → chunk-YLS6QE5G.js.map} +0 -0
- /package/dist/{chunk-YBKVGLQR.js.map → chunk-YPWU5KPE.js.map} +0 -0
- /package/dist/{chunk-IKJLSBUI.js.map → chunk-YQXTDZSU.js.map} +0 -0
- /package/dist/{chunk-TPMZBOFH.js.map → chunk-YSJEAAOL.js.map} +0 -0
- /package/dist/{chunk-W3BFRR2G.js.map → chunk-YWIFM7D7.js.map} +0 -0
- /package/dist/{chunk-WTJ4PLMU.js.map → chunk-YWVI6ZMK.js.map} +0 -0
- /package/dist/{chunk-5KWMYPJV.js.map → chunk-Z4FMAFNH.js.map} +0 -0
- /package/dist/{chunk-W5OWKVZG.js.map → chunk-Z654FWNU.js.map} +0 -0
- /package/dist/{chunk-5FH5LQVS.js.map → chunk-Z7Y523PS.js.map} +0 -0
- /package/dist/{chunk-HLL4PBO7.js.map → chunk-ZD67RM6G.js.map} +0 -0
- /package/dist/{chunk-IVYJX3OY.js.map → chunk-ZEG4VPBL.js.map} +0 -0
- /package/dist/{chunk-K6R5T44I.js.map → chunk-ZLYCOBHW.js.map} +0 -0
- /package/dist/{chunk-VADHIT26.js.map → chunk-ZNJ5MTXV.js.map} +0 -0
- /package/dist/{chunk-2KYQC4T5.js.map → chunk-ZSGY3H53.js.map} +0 -0
- /package/dist/{chunk-BKP43KK4.js.map → chunk-ZTPXACWB.js.map} +0 -0
- /package/dist/{chunk-JCY4CMDH.js.map → chunk-ZUA3JMI6.js.map} +0 -0
- /package/dist/{chunk-IGVVXLKJ.js.map → chunk-ZYAZUPPV.js.map} +0 -0
- /package/dist/{computer-viewer-LPZ763KF.js.map → computer-viewer-TDDO25AV.js.map} +0 -0
- /package/dist/{embedded-new-voice-YVMKSLKJ.js.map → embedded-new-voice-2G3VLPVQ.js.map} +0 -0
- /package/dist/{embedded-voice-BJPQFXSU.js.map → embedded-voice-T5IWOWGD.js.map} +0 -0
- /package/dist/{fleet-decision-row-AA4H5CDT.js.map → fleet-decision-row-WM64KXP3.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-HSMWVO4P.js.map → model-policy-picker-menu-SMC6J6QN.js.map} +0 -0
- /package/dist/{payment-page-card-3LTSFJGN.js.map → payment-page-card-G4U63KUV.js.map} +0 -0
- /package/dist/{platform-activity-row-5VYV6TX4.js.map → platform-activity-row-BUZGZAKZ.js.map} +0 -0
- /package/dist/{queue-surface-implementation-NXHZUKD2.js.map → queue-surface-implementation-4KUZ4PJV.js.map} +0 -0
- /package/dist/{sandbox-files-YCMLQUFU.js.map → sandbox-files-ESDMOFSD.js.map} +0 -0
- /package/dist/{session-connect-cards-KSQLMRM5.js.map → session-connect-cards-Q53KMMYY.js.map} +0 -0
- /package/dist/{session-realtime-ZR6JLG3Y.js.map → session-realtime-CF3I35WJ.js.map} +0 -0
- /package/dist/{timeline-annotation-markers-YAHDPWVG.js.map → timeline-annotation-markers-CJK4RYZ4.js.map} +0 -0
- /package/dist/{timeline-annotation-selection-KHXNCEJP.js.map → timeline-annotation-selection-67GF5H26.js.map} +0 -0
- /package/dist/{timeline-annotations-dialog-Y4XD3N7B.js.map → timeline-annotations-dialog-ONJPQDJQ.js.map} +0 -0
|
@@ -0,0 +1,4461 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
useSessionStartupState
|
|
4
|
+
} from "./chunk-EMIVOS73.js";
|
|
5
|
+
import {
|
|
6
|
+
deniesRetainedView,
|
|
7
|
+
useRetainedSessionView
|
|
8
|
+
} from "./chunk-YUNVW3I2.js";
|
|
9
|
+
import {
|
|
10
|
+
admissionRecheckControl
|
|
11
|
+
} from "./chunk-JWKGPA6Q.js";
|
|
12
|
+
import {
|
|
13
|
+
projectPendingApprovals
|
|
14
|
+
} from "./chunk-VXORMNU4.js";
|
|
15
|
+
import {
|
|
16
|
+
buildTimeline,
|
|
17
|
+
groupTimeline,
|
|
18
|
+
isToolOutputPreviewEvent,
|
|
19
|
+
sessionStatusFromEvents,
|
|
20
|
+
useToolOutputReads
|
|
21
|
+
} from "./chunk-4YCA2Y73.js";
|
|
22
|
+
import {
|
|
23
|
+
useFileAttachments
|
|
24
|
+
} from "./chunk-I6FIXEL4.js";
|
|
25
|
+
import {
|
|
26
|
+
forgetSessionHumanInputDrafts,
|
|
27
|
+
pruneHumanInputDrafts
|
|
28
|
+
} from "./chunk-X62OJCQW.js";
|
|
29
|
+
import {
|
|
30
|
+
useSharedQuery
|
|
31
|
+
} from "./chunk-IWUGGOXA.js";
|
|
32
|
+
import {
|
|
33
|
+
isActionableHumanInputRequest
|
|
34
|
+
} from "./chunk-57MB2QGY.js";
|
|
35
|
+
import {
|
|
36
|
+
readAcknowledgementFrontier,
|
|
37
|
+
sessionAttentionReadThroughSequence
|
|
38
|
+
} from "./chunk-C5ZCSVZ7.js";
|
|
39
|
+
import {
|
|
40
|
+
promptSendDestination,
|
|
41
|
+
sessionTurnKeepsRunControl
|
|
42
|
+
} from "./chunk-TSENLSW6.js";
|
|
43
|
+
import {
|
|
44
|
+
useClientConfigFlags
|
|
45
|
+
} from "./chunk-XESA3B7W.js";
|
|
46
|
+
import {
|
|
47
|
+
joinClientConfigRead
|
|
48
|
+
} from "./chunk-IGVL3ELX.js";
|
|
49
|
+
import {
|
|
50
|
+
FILE_ONLY_MESSAGE_TEXT,
|
|
51
|
+
notifyObserver,
|
|
52
|
+
useComposer
|
|
53
|
+
} from "./chunk-MIHN76LX.js";
|
|
54
|
+
import {
|
|
55
|
+
DEFAULT_TRANSIENT_READ_RETRY,
|
|
56
|
+
projectionReadThrough,
|
|
57
|
+
transientReadRetryStep,
|
|
58
|
+
useDebouncedCallback,
|
|
59
|
+
useMutationRunner,
|
|
60
|
+
usePageLiveActivity,
|
|
61
|
+
usePolledValue,
|
|
62
|
+
useSessionEventTrigger
|
|
63
|
+
} from "./chunk-RV3O5OCQ.js";
|
|
64
|
+
import {
|
|
65
|
+
normalizeError
|
|
66
|
+
} from "./chunk-CM7LVUKC.js";
|
|
67
|
+
import {
|
|
68
|
+
useEmbeddedGoal,
|
|
69
|
+
useEmbeddedHumanInputSession,
|
|
70
|
+
useEmbeddedSession,
|
|
71
|
+
useEmbeddedSessionRead,
|
|
72
|
+
useOpenGeni,
|
|
73
|
+
useOpenGeniClient
|
|
74
|
+
} from "./chunk-TVQCDDSZ.js";
|
|
75
|
+
import {
|
|
76
|
+
coerceReasoningEffortForModel,
|
|
77
|
+
defaultEffortForModel,
|
|
78
|
+
projectPickerRows,
|
|
79
|
+
runnableLatencyModesForModel,
|
|
80
|
+
sortPickerRows
|
|
81
|
+
} from "./chunk-ZHC47M2B.js";
|
|
82
|
+
import {
|
|
83
|
+
useOpenGeniText
|
|
84
|
+
} from "./chunk-7T2ZE3VT.js";
|
|
85
|
+
|
|
86
|
+
// src/hooks/use-session.ts
|
|
87
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
88
|
+
|
|
89
|
+
// src/lib/session-detail-read.ts
|
|
90
|
+
var lastOrdinal = 0;
|
|
91
|
+
var reads = /* @__PURE__ */ new WeakMap();
|
|
92
|
+
function readKey(workspaceId, sessionId) {
|
|
93
|
+
return `${workspaceId}\0${sessionId}`;
|
|
94
|
+
}
|
|
95
|
+
function sessionDetailReadMark() {
|
|
96
|
+
return lastOrdinal;
|
|
97
|
+
}
|
|
98
|
+
function readFreshSessionDetail(client, workspaceId, sessionId, options = {}) {
|
|
99
|
+
const promise = client.getSession(workspaceId, sessionId, { ...options, fresh: true });
|
|
100
|
+
const read = { promise, ordinal: ++lastOrdinal };
|
|
101
|
+
let scoped = reads.get(client);
|
|
102
|
+
if (!scoped) {
|
|
103
|
+
scoped = /* @__PURE__ */ new Map();
|
|
104
|
+
reads.set(client, scoped);
|
|
105
|
+
}
|
|
106
|
+
const key = readKey(workspaceId, sessionId);
|
|
107
|
+
scoped.set(key, read);
|
|
108
|
+
promise.catch(() => {
|
|
109
|
+
if (scoped.get(key) === read) scoped.delete(key);
|
|
110
|
+
});
|
|
111
|
+
return promise;
|
|
112
|
+
}
|
|
113
|
+
function joinSessionDetailRead(client, workspaceId, sessionId, mark) {
|
|
114
|
+
const read = reads.get(client)?.get(readKey(workspaceId, sessionId));
|
|
115
|
+
return read && read.ordinal > mark ? read.promise : null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/hooks/use-session.ts
|
|
119
|
+
function isTitleEvent(event) {
|
|
120
|
+
return event.type === "session.title_set";
|
|
121
|
+
}
|
|
122
|
+
function isSessionDetailEvent(event) {
|
|
123
|
+
return isTitleEvent(event) || // Description or avatar edits (by a person or the agent) refresh the header.
|
|
124
|
+
event.type === "session.profile_set" || // The chat's model, effort or speed changed (for example by the agent).
|
|
125
|
+
event.type === "session.model_settings.updated" || event.type.startsWith("session.command.") || event.type.startsWith("session.wait.") || event.type === "session.status.changed" || event.type === "system.update.pending";
|
|
126
|
+
}
|
|
127
|
+
function useSession(sessionId, options = {}) {
|
|
128
|
+
const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } = useEmbeddedSessionRead(options);
|
|
129
|
+
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
130
|
+
const [override, setOverride] = useState(null);
|
|
131
|
+
const nextReadRevision = useRef(0);
|
|
132
|
+
const nextReadGeneration = useRef(0);
|
|
133
|
+
const beginRead = options.beginRead;
|
|
134
|
+
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
135
|
+
const load = useCallback(
|
|
136
|
+
async (signal) => {
|
|
137
|
+
if (!sessionId) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
let readGeneration2 = 0;
|
|
141
|
+
const fetched = await readFreshSessionDetail(client, workspaceId, sessionId, {
|
|
142
|
+
signal,
|
|
143
|
+
onRequestStart: () => {
|
|
144
|
+
readGeneration2 = beginRead?.() ?? ++nextReadGeneration.current;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
setOverride(null);
|
|
148
|
+
return {
|
|
149
|
+
session: fetched,
|
|
150
|
+
revision: ++nextReadRevision.current,
|
|
151
|
+
readGeneration: readGeneration2
|
|
152
|
+
};
|
|
153
|
+
},
|
|
154
|
+
[beginRead, client, workspaceId, sessionId]
|
|
155
|
+
);
|
|
156
|
+
const { data, loading, error, refresh } = usePolledValue(load, {
|
|
157
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
158
|
+
enabled
|
|
159
|
+
});
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (enabled && workspaceControlEvent) void refresh();
|
|
162
|
+
}, [enabled, refresh, workspaceControlEvent]);
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if (!sessionId || !enabled) return;
|
|
165
|
+
return registerSessionReconciler(sessionId, "session", refresh);
|
|
166
|
+
}, [enabled, refresh, registerSessionReconciler, sessionId]);
|
|
167
|
+
const retained = useRetainedSessionView(
|
|
168
|
+
client,
|
|
169
|
+
workspaceId,
|
|
170
|
+
sessionId,
|
|
171
|
+
"detail",
|
|
172
|
+
data?.session ?? null,
|
|
173
|
+
{ enabled, discard: deniesRetainedView(error) }
|
|
174
|
+
);
|
|
175
|
+
const base = data?.session ?? null;
|
|
176
|
+
const readRevision = data?.revision ?? 0;
|
|
177
|
+
const readGeneration = data?.readGeneration ?? 0;
|
|
178
|
+
const session = useMemo(
|
|
179
|
+
() => base && override && override.id === base.id ? { ...base, title: override.title, titleSource: override.titleSource } : base ?? retained,
|
|
180
|
+
[base, override, retained]
|
|
181
|
+
);
|
|
182
|
+
const sharedEvents = options.events;
|
|
183
|
+
const refreshDetail = useCallback(() => void refresh(), [refresh]);
|
|
184
|
+
const onTitleEvent = useCallback(
|
|
185
|
+
(event) => {
|
|
186
|
+
if (sharedEvents?.some(
|
|
187
|
+
(item) => item.sessionId === sessionId && !isTitleEvent(item) && isSessionDetailEvent(item) && item.sequence > (base?.lastSequence ?? -1)
|
|
188
|
+
)) {
|
|
189
|
+
void refresh();
|
|
190
|
+
} else if (!isTitleEvent(event)) {
|
|
191
|
+
if (!base || event.sequence > base.lastSequence) void refresh();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (!base || event.sequence <= base.lastSequence) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const payload = event.payload ?? {};
|
|
198
|
+
const title = payload.title;
|
|
199
|
+
if (typeof title !== "string") {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const source = payload.source === "user" || payload.source === "agent" ? payload.source : null;
|
|
203
|
+
setOverride((current) => {
|
|
204
|
+
const next = current ?? base;
|
|
205
|
+
return { ...next, title, titleSource: source };
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
[base, refresh, sharedEvents, sessionId]
|
|
209
|
+
);
|
|
210
|
+
useSessionEventTrigger(client, workspaceId, sessionId, isSessionDetailEvent, onTitleEvent, {
|
|
211
|
+
enabled,
|
|
212
|
+
...options.events !== void 0 ? { events: options.events } : {},
|
|
213
|
+
// The detail row is authoritative through its lastSequence: only a later
|
|
214
|
+
// event can make it stale, and one that arrives first waits for the read.
|
|
215
|
+
readThrough: base ? base.lastSequence : loading ? "reading" : null,
|
|
216
|
+
refresh: refreshDetail
|
|
217
|
+
});
|
|
218
|
+
const updateTitle = useCallback(
|
|
219
|
+
async (title) => {
|
|
220
|
+
if (!sessionId) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
const result = await run(() => client.updateSession(workspaceId, sessionId, { title }));
|
|
224
|
+
if (result) {
|
|
225
|
+
setOverride(result);
|
|
226
|
+
}
|
|
227
|
+
return result;
|
|
228
|
+
},
|
|
229
|
+
[client, workspaceId, sessionId, run]
|
|
230
|
+
);
|
|
231
|
+
return {
|
|
232
|
+
session,
|
|
233
|
+
loading: loading && retained === null,
|
|
234
|
+
error,
|
|
235
|
+
readRevision,
|
|
236
|
+
readGeneration,
|
|
237
|
+
refresh,
|
|
238
|
+
updateTitle,
|
|
239
|
+
updating: mutating,
|
|
240
|
+
mutationError,
|
|
241
|
+
clearMutationError
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// src/older-history.ts
|
|
246
|
+
var activeReceiptCapture;
|
|
247
|
+
var preserveTailForAutomaticLoad = false;
|
|
248
|
+
function invokeOlderHistoryLoaderWithReceiptCapture(load, capture, preserveTail = false) {
|
|
249
|
+
const previousCapture = activeReceiptCapture;
|
|
250
|
+
const previousPreserveTail = preserveTailForAutomaticLoad;
|
|
251
|
+
activeReceiptCapture = capture;
|
|
252
|
+
preserveTailForAutomaticLoad = preserveTail;
|
|
253
|
+
try {
|
|
254
|
+
return load();
|
|
255
|
+
} finally {
|
|
256
|
+
activeReceiptCapture = previousCapture;
|
|
257
|
+
preserveTailForAutomaticLoad = previousPreserveTail;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function createOlderHistoryLoadReceipt(load) {
|
|
261
|
+
let resolveResult;
|
|
262
|
+
let rejectResult;
|
|
263
|
+
const result = new Promise((resolve, reject) => {
|
|
264
|
+
resolveResult = resolve;
|
|
265
|
+
rejectResult = reject;
|
|
266
|
+
});
|
|
267
|
+
result.committed = false;
|
|
268
|
+
result.tailPreserved = false;
|
|
269
|
+
activeReceiptCapture?.(result);
|
|
270
|
+
try {
|
|
271
|
+
const loaded = load(
|
|
272
|
+
() => {
|
|
273
|
+
result.committed = true;
|
|
274
|
+
},
|
|
275
|
+
preserveTailForAutomaticLoad,
|
|
276
|
+
() => {
|
|
277
|
+
result.tailPreserved = true;
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
Promise.resolve(loaded).then(resolveResult, rejectResult);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
rejectResult(error);
|
|
283
|
+
}
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/hooks/use-session-events.ts
|
|
288
|
+
import {
|
|
289
|
+
isPresentedSessionToolCall,
|
|
290
|
+
isRetryableStreamError,
|
|
291
|
+
isSessionModelResponsePhase,
|
|
292
|
+
isTransientRequestError,
|
|
293
|
+
SESSION_EVENT_COMPAT_EXCLUDED_STEP_TYPES,
|
|
294
|
+
SESSION_EVENT_CONTENT_STEP_TYPES,
|
|
295
|
+
SESSION_EVENT_CONVERSATION_FILTER_VERSION,
|
|
296
|
+
SESSION_EVENT_STEP_TYPES,
|
|
297
|
+
sessionEventStreamCoveredThrough
|
|
298
|
+
} from "@opengeni/sdk";
|
|
299
|
+
import { startTransition, useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
300
|
+
|
|
301
|
+
// src/hooks/page-wake.ts
|
|
302
|
+
var PAGE_WAKE_CHECK_INTERVAL_MS = 2e3;
|
|
303
|
+
var PAGE_WAKE_GAP_MS = 15e3;
|
|
304
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
305
|
+
var stopDetector = null;
|
|
306
|
+
function subscribePageWake(listener) {
|
|
307
|
+
listeners.add(listener);
|
|
308
|
+
stopDetector ??= startDetector();
|
|
309
|
+
return () => {
|
|
310
|
+
listeners.delete(listener);
|
|
311
|
+
if (listeners.size === 0 && stopDetector) {
|
|
312
|
+
stopDetector();
|
|
313
|
+
stopDetector = null;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function emitPageWake() {
|
|
318
|
+
for (const listener of [...listeners]) {
|
|
319
|
+
try {
|
|
320
|
+
listener();
|
|
321
|
+
} catch {
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function startDetector() {
|
|
326
|
+
if (typeof window === "undefined" || typeof document === "undefined") return () => {
|
|
327
|
+
};
|
|
328
|
+
let lastTick = Date.now();
|
|
329
|
+
const check = () => {
|
|
330
|
+
const now = Date.now();
|
|
331
|
+
const elapsed = now - lastTick;
|
|
332
|
+
lastTick = now;
|
|
333
|
+
if (elapsed > PAGE_WAKE_GAP_MS) emitPageWake();
|
|
334
|
+
};
|
|
335
|
+
const onVisibility = () => {
|
|
336
|
+
if (document.visibilityState !== "hidden") check();
|
|
337
|
+
};
|
|
338
|
+
const onPageShow = (event) => {
|
|
339
|
+
lastTick = Date.now();
|
|
340
|
+
if (event.persisted) emitPageWake();
|
|
341
|
+
};
|
|
342
|
+
const onResume = () => {
|
|
343
|
+
lastTick = Date.now();
|
|
344
|
+
emitPageWake();
|
|
345
|
+
};
|
|
346
|
+
const timer = setInterval(check, PAGE_WAKE_CHECK_INTERVAL_MS);
|
|
347
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
348
|
+
document.addEventListener("resume", onResume);
|
|
349
|
+
window.addEventListener("pageshow", onPageShow);
|
|
350
|
+
window.addEventListener("online", onResume);
|
|
351
|
+
return () => {
|
|
352
|
+
clearInterval(timer);
|
|
353
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
354
|
+
document.removeEventListener("resume", onResume);
|
|
355
|
+
window.removeEventListener("pageshow", onPageShow);
|
|
356
|
+
window.removeEventListener("online", onResume);
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/hooks/turnless-output-gaps.ts
|
|
361
|
+
import { useCallback as useCallback2, useSyncExternalStore } from "react";
|
|
362
|
+
var MAX_IDLE_ENTRIES = 256;
|
|
363
|
+
var entries = /* @__PURE__ */ new Map();
|
|
364
|
+
function gapKey(workspaceId, sessionId) {
|
|
365
|
+
return `${workspaceId}\0${sessionId}`;
|
|
366
|
+
}
|
|
367
|
+
function gapEntry(key) {
|
|
368
|
+
let entry = entries.get(key);
|
|
369
|
+
if (!entry) {
|
|
370
|
+
entry = { generation: 0, listeners: /* @__PURE__ */ new Set() };
|
|
371
|
+
entries.set(key, entry);
|
|
372
|
+
if (entries.size > MAX_IDLE_ENTRIES) {
|
|
373
|
+
for (const [candidate, held] of entries) {
|
|
374
|
+
if (entries.size <= MAX_IDLE_ENTRIES) break;
|
|
375
|
+
if (candidate !== key && held.listeners.size === 0) entries.delete(candidate);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return entry;
|
|
380
|
+
}
|
|
381
|
+
function markTurnlessOutputGap(workspaceId, sessionId) {
|
|
382
|
+
const entry = gapEntry(gapKey(workspaceId, sessionId));
|
|
383
|
+
entry.generation += 1;
|
|
384
|
+
for (const listener of [...entry.listeners]) listener();
|
|
385
|
+
}
|
|
386
|
+
function useTurnlessOutputGap(workspaceId, sessionId) {
|
|
387
|
+
const key = sessionId ? gapKey(workspaceId, sessionId) : null;
|
|
388
|
+
const subscribe = useCallback2(
|
|
389
|
+
(listener) => {
|
|
390
|
+
if (!key) return () => {
|
|
391
|
+
};
|
|
392
|
+
const entry = gapEntry(key);
|
|
393
|
+
entry.listeners.add(listener);
|
|
394
|
+
return () => {
|
|
395
|
+
entry.listeners.delete(listener);
|
|
396
|
+
};
|
|
397
|
+
},
|
|
398
|
+
[key]
|
|
399
|
+
);
|
|
400
|
+
const read = useCallback2(() => key ? entries.get(key)?.generation ?? 0 : 0, [key]);
|
|
401
|
+
return useSyncExternalStore(subscribe, read, read);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/hooks/use-session-events.ts
|
|
405
|
+
import { errorMessage } from "@opengeni/contracts/values";
|
|
406
|
+
var SESSION_FIRST_WINDOW_PAGE_SIZE = 255;
|
|
407
|
+
var SESSION_HISTORY_PAGE_SIZE = 1e3;
|
|
408
|
+
var INITIAL_FETCH_CAP = 1;
|
|
409
|
+
var OLDER_GROUP_TARGET = 32;
|
|
410
|
+
var OLDER_FETCH_CAP = 2;
|
|
411
|
+
var NEWER_GROUP_TARGET = 32;
|
|
412
|
+
var NEWER_FETCH_CAP = 2;
|
|
413
|
+
var OLDEST_GROUP_TARGET = 32;
|
|
414
|
+
var OLDEST_FETCH_CAP = 2;
|
|
415
|
+
var BOUNDARY_PAGE_CAP = 1;
|
|
416
|
+
var FILTERED_PAGE_CAP = 32;
|
|
417
|
+
var DIRECT_REPLAY_MAX_SEQUENCES = 16;
|
|
418
|
+
var CATCHUP_MAX_PAGES = 2;
|
|
419
|
+
var STRICT_REJOIN_ATTEMPTS = 5;
|
|
420
|
+
var REJOIN_STALL_TIMEOUT_MS = 3e4;
|
|
421
|
+
var FOREGROUND_REJOIN_RETRY_MS = 500;
|
|
422
|
+
var FOREGROUND_REJOIN_RETRY_MAX_MS = 8e3;
|
|
423
|
+
var EMPTY_EVENTS = [];
|
|
424
|
+
var encoder = new TextEncoder();
|
|
425
|
+
var SESSION_EVENT_BROWSER_MAX_BYTES = 16 * 1024 * 1024;
|
|
426
|
+
var SESSION_EVENT_BROWSER_MAX_COUNT = 2e4;
|
|
427
|
+
var SESSION_EVENT_BROWSER_PENDING_MAX_BYTES = 1024 * 1024;
|
|
428
|
+
var SESSION_EVENT_BROWSER_PENDING_MAX_COUNT = 256;
|
|
429
|
+
var EMPTY_EVENT_WINDOW = {
|
|
430
|
+
events: EMPTY_EVENTS,
|
|
431
|
+
bytes: 2,
|
|
432
|
+
truncated: false
|
|
433
|
+
};
|
|
434
|
+
function useSessionEvents(sessionId, options = {}) {
|
|
435
|
+
const t = useOpenGeniText();
|
|
436
|
+
const { client, workspaceId, reconcileSession } = useEmbeddedSession(options);
|
|
437
|
+
const enabled = options.enabled ?? true;
|
|
438
|
+
const pageLive = usePageLiveActivity();
|
|
439
|
+
const streamEnabled = enabled && pageLive;
|
|
440
|
+
const after = options.after ?? 0;
|
|
441
|
+
const replay = options.replay ?? "windowed";
|
|
442
|
+
const fullReplay = replay === "full" || after !== 0;
|
|
443
|
+
const streamKey = `${workspaceId}\0${sessionId ?? ""}\0${after}\0${fullReplay ? "full" : "windowed"}`;
|
|
444
|
+
const navigationIdentityRef = useRef2({ client, streamKey, enabled });
|
|
445
|
+
if (navigationIdentityRef.current.client !== client || navigationIdentityRef.current.streamKey !== streamKey || navigationIdentityRef.current.enabled !== enabled) {
|
|
446
|
+
navigationIdentityRef.current = { client, streamKey, enabled };
|
|
447
|
+
}
|
|
448
|
+
const navigationIdentity = navigationIdentityRef.current;
|
|
449
|
+
const ownsNavigation = useCallback3(
|
|
450
|
+
() => navigationIdentityRef.current === navigationIdentity,
|
|
451
|
+
[navigationIdentity]
|
|
452
|
+
);
|
|
453
|
+
const [eventWindow, setEventWindow] = useState2(EMPTY_EVENT_WINDOW);
|
|
454
|
+
const [questionEvidence, setQuestionEvidence] = useState2(null);
|
|
455
|
+
const [connectionState, setConnectionState] = useState2("idle");
|
|
456
|
+
const [error, setError] = useState2(null);
|
|
457
|
+
const [newerError, setNewerError] = useState2(null);
|
|
458
|
+
const [hasOlder, setHasOlder] = useState2(false);
|
|
459
|
+
const [hasNewer, setHasNewer] = useState2(false);
|
|
460
|
+
const [sessionStatusProjection, setSessionStatusProjection] = useState2(
|
|
461
|
+
null
|
|
462
|
+
);
|
|
463
|
+
const [initialLoading, setInitialLoading] = useState2(true);
|
|
464
|
+
const [initialHistoryReady, setInitialHistoryReady] = useState2(false);
|
|
465
|
+
const [loadingOlder, setLoadingOlder] = useState2(false);
|
|
466
|
+
const [loadingNewer, setLoadingNewer] = useState2(false);
|
|
467
|
+
const [loadingOldest, setLoadingOldest] = useState2(false);
|
|
468
|
+
const [loadingLatest, setLoadingLatest] = useState2(false);
|
|
469
|
+
const [loadingTarget, setLoadingTarget] = useState2(false);
|
|
470
|
+
const loadingTargetRef = useRef2(false);
|
|
471
|
+
const [streamEpoch, setStreamEpoch] = useState2(0);
|
|
472
|
+
const [viewMode, setViewMode] = useState2("live");
|
|
473
|
+
const lastSequenceRef = useRef2(after);
|
|
474
|
+
const streamResumeSequenceRef = useRef2(after);
|
|
475
|
+
const skippedStepsThroughRef = useRef2(null);
|
|
476
|
+
const oldestSequenceRef = useRef2(null);
|
|
477
|
+
const newestSequenceRef = useRef2(null);
|
|
478
|
+
const hasOlderRef = useRef2(false);
|
|
479
|
+
const hasNewerRef = useRef2(false);
|
|
480
|
+
const loadingOlderRef = useRef2(false);
|
|
481
|
+
const loadingNewerRef = useRef2(false);
|
|
482
|
+
const loadingOldestRef = useRef2(false);
|
|
483
|
+
const loadingLatestRef = useRef2(false);
|
|
484
|
+
const viewModeRef = useRef2("live");
|
|
485
|
+
const initialWindowLoadedRef = useRef2(false);
|
|
486
|
+
const openedStreamKeyRef = useRef2(null);
|
|
487
|
+
const reconcileAfterPageResumeRef = useRef2(false);
|
|
488
|
+
const streamAbortRef = useRef2(null);
|
|
489
|
+
const streamKeyRef = useRef2(null);
|
|
490
|
+
const generationRef = useRef2(0);
|
|
491
|
+
const navigationGenerationRef = useRef2(0);
|
|
492
|
+
const eventWindowRef = useRef2(EMPTY_EVENT_WINDOW);
|
|
493
|
+
const sessionStatusRef = useRef2({
|
|
494
|
+
sequence: after,
|
|
495
|
+
status: null
|
|
496
|
+
});
|
|
497
|
+
const [stateStreamKey, setStateStreamKey] = useState2(streamKey);
|
|
498
|
+
const historyFilter = fullReplay ? void 0 : CONVERSATION_FILTER;
|
|
499
|
+
const [stepStores, setStepStores] = useState2(EMPTY_STEP_STORES);
|
|
500
|
+
const stepStoresRef = useRef2(EMPTY_STEP_STORES);
|
|
501
|
+
const [turnlessSteps, setTurnlessSteps] = useState2(EMPTY_TURNLESS_STEPS);
|
|
502
|
+
const turnlessStepsRef = useRef2(EMPTY_TURNLESS_STEPS);
|
|
503
|
+
const renderedTurnlessRef = useRef2({ appended: 0, marked: false });
|
|
504
|
+
useEffect2(() => {
|
|
505
|
+
renderedTurnlessRef.current = { appended: turnlessSteps.appended, marked: false };
|
|
506
|
+
}, [turnlessSteps]);
|
|
507
|
+
const liveThroughRef = useRef2(0);
|
|
508
|
+
const stepGenerationRef = useRef2(0);
|
|
509
|
+
const stepRequestRef = useRef2(0);
|
|
510
|
+
const retainedTurnsRef = useRef2(/* @__PURE__ */ new Map());
|
|
511
|
+
const turnStepHandlesRef = useRef2(/* @__PURE__ */ new Map());
|
|
512
|
+
const presentedCallIdsRef = useRef2(/* @__PURE__ */ new Set());
|
|
513
|
+
const presentedScanRef = useRef2(null);
|
|
514
|
+
const startupRef = useRef2(emptyStartupTurns());
|
|
515
|
+
const [presentedOutputs, setPresentedOutputs] = useState2(EMPTY_PRESENTED_OUTPUTS);
|
|
516
|
+
const presentedRequestedRef = useRef2(/* @__PURE__ */ new Map());
|
|
517
|
+
const presentedFailuresRef = useRef2(/* @__PURE__ */ new Map());
|
|
518
|
+
const [presentedPass, setPresentedPass] = useState2(0);
|
|
519
|
+
const rejoinPendingRef = useRef2(false);
|
|
520
|
+
const streamTransientStreakRef = useRef2(null);
|
|
521
|
+
const commitStepStores = useCallback3((next) => {
|
|
522
|
+
stepStoresRef.current = next;
|
|
523
|
+
setStepStores(next);
|
|
524
|
+
}, []);
|
|
525
|
+
const pruneStepStores = useCallback3(
|
|
526
|
+
(window2, sealedBy, rereading = null) => {
|
|
527
|
+
const present = windowTurns(window2);
|
|
528
|
+
const settled = sealedBy ? settledTurns(sealedBy) : null;
|
|
529
|
+
const next = /* @__PURE__ */ new Map();
|
|
530
|
+
for (const [turnId, store] of stepStoresRef.current) {
|
|
531
|
+
if (present.has(turnId) && (!settled || settled.has(turnId) || turnId === rereading)) {
|
|
532
|
+
next.set(turnId, store);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (next.size !== stepStoresRef.current.size) commitStepStores(next);
|
|
536
|
+
if (sealedBy) {
|
|
537
|
+
presentedRequestedRef.current = /* @__PURE__ */ new Map();
|
|
538
|
+
} else {
|
|
539
|
+
for (const [callId, request] of presentedRequestedRef.current) {
|
|
540
|
+
if (request.turnId && !present.has(request.turnId)) {
|
|
541
|
+
presentedRequestedRef.current.delete(callId);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
setPresentedOutputs((current) => {
|
|
546
|
+
let kept = null;
|
|
547
|
+
for (const [callId, output] of current) {
|
|
548
|
+
if (output.turnId && present.has(output.turnId)) continue;
|
|
549
|
+
kept ??= new Map(current);
|
|
550
|
+
kept.delete(callId);
|
|
551
|
+
}
|
|
552
|
+
return kept ?? current;
|
|
553
|
+
});
|
|
554
|
+
},
|
|
555
|
+
[commitStepStores]
|
|
556
|
+
);
|
|
557
|
+
const presentedCallIds = useCallback3((event) => {
|
|
558
|
+
const ids = presentedCallIdsRef.current;
|
|
559
|
+
if (event.type === "agent.toolCall.created") {
|
|
560
|
+
notePresentedCalls([event], ids);
|
|
561
|
+
} else if (event.type === "agent.toolCall.output") {
|
|
562
|
+
const payload = event.payload;
|
|
563
|
+
const windowEvents = eventWindowRef.current.events;
|
|
564
|
+
if (typeof payload?.id === "string" && !ids.has(payload.id) && presentedScanRef.current !== windowEvents) {
|
|
565
|
+
presentedScanRef.current = windowEvents;
|
|
566
|
+
notePresentedCalls(windowEvents, ids);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return ids;
|
|
570
|
+
}, []);
|
|
571
|
+
const startupTurns = useCallback3(() => {
|
|
572
|
+
const windowEvents = eventWindowRef.current.events;
|
|
573
|
+
if (startupRef.current.window !== windowEvents) {
|
|
574
|
+
const state = emptyStartupTurns();
|
|
575
|
+
noteStartupTurns(windowEvents, state);
|
|
576
|
+
state.window = windowEvents;
|
|
577
|
+
startupRef.current = state;
|
|
578
|
+
}
|
|
579
|
+
return startupRef.current;
|
|
580
|
+
}, []);
|
|
581
|
+
const appendLiveSteps = useCallback3(
|
|
582
|
+
(steps) => {
|
|
583
|
+
if (steps.some((event) => !event.turnId)) {
|
|
584
|
+
const incoming = steps.filter((event) => !event.turnId);
|
|
585
|
+
const turnless = appendTurnlessSteps(turnlessStepsRef.current, incoming);
|
|
586
|
+
const rendered = renderedTurnlessRef.current;
|
|
587
|
+
if (!rendered.marked && turnless.appended - rendered.appended > turnless.events.length) {
|
|
588
|
+
rendered.marked = true;
|
|
589
|
+
if (sessionId) markTurnlessOutputGap(workspaceId, sessionId);
|
|
590
|
+
}
|
|
591
|
+
turnlessStepsRef.current = turnless;
|
|
592
|
+
setTurnlessSteps(turnless);
|
|
593
|
+
}
|
|
594
|
+
const windowEvents = withTurnlessSteps(
|
|
595
|
+
eventWindowRef.current.events,
|
|
596
|
+
turnlessStepsRef.current
|
|
597
|
+
);
|
|
598
|
+
const next = new Map(stepStoresRef.current);
|
|
599
|
+
const now = Date.now();
|
|
600
|
+
for (const [turnId, events] of groupByTurn(steps)) {
|
|
601
|
+
const existing = next.get(turnId);
|
|
602
|
+
const store = existing ?? {
|
|
603
|
+
events: [],
|
|
604
|
+
bytes: 2,
|
|
605
|
+
// Earlier steps exist when the window skips sequences between the
|
|
606
|
+
// turn's start and this first live step.
|
|
607
|
+
hasOlder: turnHasHiddenSequences(windowEvents, turnId, events[0].sequence),
|
|
608
|
+
cursor: null,
|
|
609
|
+
floor: null,
|
|
610
|
+
pinned: null,
|
|
611
|
+
loading: false,
|
|
612
|
+
error: false,
|
|
613
|
+
touched: now,
|
|
614
|
+
requestId: 0
|
|
615
|
+
};
|
|
616
|
+
let updated = { ...store, ...appendStepEvents(store, events), touched: now };
|
|
617
|
+
if (!retainedTurnsRef.current.get(turnId)) updated = trimLiveStepTail(updated);
|
|
618
|
+
next.set(turnId, updated);
|
|
619
|
+
}
|
|
620
|
+
commitStepStores(boundStepStores(next, retainedTurnsRef.current));
|
|
621
|
+
},
|
|
622
|
+
[commitStepStores, workspaceId, sessionId]
|
|
623
|
+
);
|
|
624
|
+
const loadTurnSteps = useCallback3(
|
|
625
|
+
(turnId, before) => {
|
|
626
|
+
if (!ownsNavigation() || !sessionId || fullReplay) return;
|
|
627
|
+
const current = stepStoresRef.current.get(turnId);
|
|
628
|
+
const pinnedUpper = before ?? (current?.error && current.pinned !== null ? current.pinned : void 0);
|
|
629
|
+
const pinned = pinnedUpper !== void 0;
|
|
630
|
+
if (!pinned && current?.loading) return;
|
|
631
|
+
const bounds = conversationTurnBounds(eventWindowRef.current.events).get(turnId);
|
|
632
|
+
if (!bounds) return;
|
|
633
|
+
if (!pinned && current && !stepStoreHasOlder(current, bounds) && !current.error) return;
|
|
634
|
+
const upper = pinnedUpper ?? current?.cursor ?? current?.events[0]?.sequence ?? (bounds.terminal !== null ? bounds.terminal + 1 : Number.MAX_SAFE_INTEGER);
|
|
635
|
+
const exactStart = bounds.started !== null;
|
|
636
|
+
const lower = turnStepLowerBound(bounds);
|
|
637
|
+
const generation = stepGenerationRef.current;
|
|
638
|
+
const requestId = ++stepRequestRef.current;
|
|
639
|
+
const isCurrent = () => ownsNavigation() && generation === stepGenerationRef.current && stepStoresRef.current.get(turnId)?.requestId === requestId;
|
|
640
|
+
const base = current ?? {
|
|
641
|
+
events: [],
|
|
642
|
+
bytes: 2,
|
|
643
|
+
hasOlder: true,
|
|
644
|
+
cursor: null,
|
|
645
|
+
floor: null,
|
|
646
|
+
pinned: null,
|
|
647
|
+
loading: false,
|
|
648
|
+
error: false,
|
|
649
|
+
touched: 0,
|
|
650
|
+
requestId
|
|
651
|
+
};
|
|
652
|
+
commitStepStores(
|
|
653
|
+
new Map(stepStoresRef.current).set(turnId, {
|
|
654
|
+
...base,
|
|
655
|
+
pinned: pinned ? upper : null,
|
|
656
|
+
loading: true,
|
|
657
|
+
error: false,
|
|
658
|
+
touched: Date.now(),
|
|
659
|
+
requestId
|
|
660
|
+
})
|
|
661
|
+
);
|
|
662
|
+
void readEventPage(client, workspaceId, sessionId, {
|
|
663
|
+
// Bookkeeping too, whichever read built this window: rows it already
|
|
664
|
+
// holds (a fallback read keeps them) merge once by id.
|
|
665
|
+
includeTypes: [...TURN_STEP_READ_TYPES],
|
|
666
|
+
after: Math.max(0, lower),
|
|
667
|
+
before: upper,
|
|
668
|
+
direction: "before",
|
|
669
|
+
limit: SESSION_TURN_STEP_PAGE_SIZE,
|
|
670
|
+
compact: true,
|
|
671
|
+
payloadMode: "full",
|
|
672
|
+
// Large tool outputs arrive as bounded previews; a row reads its
|
|
673
|
+
// exact output when the reader needs it (see useToolOutputReads).
|
|
674
|
+
toolOutputs: "preview"
|
|
675
|
+
}).then(
|
|
676
|
+
(page) => {
|
|
677
|
+
if (!isCurrent()) return;
|
|
678
|
+
const own = page.events.filter((event) => event.turnId === turnId);
|
|
679
|
+
const latest = stepStoresRef.current.get(turnId);
|
|
680
|
+
const merged = pinned ? appendStepEvents(
|
|
681
|
+
{ events: [], bytes: 2 },
|
|
682
|
+
[...own, ...latest.events.filter((event) => event.sequence >= upper)].sort(
|
|
683
|
+
(left, right) => left.sequence - right.sequence
|
|
684
|
+
)
|
|
685
|
+
) : appendStepEvents(latest, own);
|
|
686
|
+
const oldestOwn = merged.events[0]?.sequence ?? upper;
|
|
687
|
+
const passedStart = !exactStart && page.events.some(
|
|
688
|
+
(event) => !!event.turnId && event.turnId !== turnId && event.sequence < oldestOwn
|
|
689
|
+
);
|
|
690
|
+
const moreBelow = !passedStart && (page.hasMore ?? page.events.length > 0);
|
|
691
|
+
const reachedFloor = !moreBelow && !passedStart && !exactStart;
|
|
692
|
+
const next = new Map(stepStoresRef.current).set(turnId, {
|
|
693
|
+
...merged,
|
|
694
|
+
// A page read without a continuation fact (a client without
|
|
695
|
+
// listEventPage) offers one more page until a read comes back empty.
|
|
696
|
+
hasOlder: moreBelow,
|
|
697
|
+
// The next page continues below this page's oldest row, even when
|
|
698
|
+
// the page held only other turns' rows, or below the floor.
|
|
699
|
+
cursor: reachedFloor ? lower + 1 : page.events.reduce((oldest, event) => Math.min(oldest, event.sequence), upper),
|
|
700
|
+
floor: reachedFloor ? lower : null,
|
|
701
|
+
pinned: null,
|
|
702
|
+
loading: false,
|
|
703
|
+
error: false,
|
|
704
|
+
touched: Date.now(),
|
|
705
|
+
requestId
|
|
706
|
+
});
|
|
707
|
+
commitStepStores(boundStepStores(next, retainedTurnsRef.current));
|
|
708
|
+
},
|
|
709
|
+
() => {
|
|
710
|
+
if (!isCurrent()) return;
|
|
711
|
+
const latest = stepStoresRef.current.get(turnId);
|
|
712
|
+
commitStepStores(
|
|
713
|
+
new Map(stepStoresRef.current).set(turnId, { ...latest, loading: false, error: true })
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
);
|
|
717
|
+
},
|
|
718
|
+
[client, workspaceId, sessionId, fullReplay, ownsNavigation, commitStepStores]
|
|
719
|
+
);
|
|
720
|
+
const loadTurnStepsRef = useRef2(loadTurnSteps);
|
|
721
|
+
useEffect2(() => {
|
|
722
|
+
loadTurnStepsRef.current = loadTurnSteps;
|
|
723
|
+
}, [loadTurnSteps]);
|
|
724
|
+
const turnStepHandle = useCallback3((turnId) => {
|
|
725
|
+
let handle = turnStepHandlesRef.current.get(turnId);
|
|
726
|
+
if (!handle) {
|
|
727
|
+
const retained = retainedTurnsRef.current;
|
|
728
|
+
handle = {
|
|
729
|
+
load: () => loadTurnStepsRef.current(turnId),
|
|
730
|
+
retain: () => {
|
|
731
|
+
retained.set(turnId, (retained.get(turnId) ?? 0) + 1);
|
|
732
|
+
let released = false;
|
|
733
|
+
return () => {
|
|
734
|
+
if (released) return;
|
|
735
|
+
released = true;
|
|
736
|
+
const count = (retained.get(turnId) ?? 1) - 1;
|
|
737
|
+
if (count > 0) retained.set(turnId, count);
|
|
738
|
+
else retained.delete(turnId);
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
turnStepHandlesRef.current.set(turnId, handle);
|
|
743
|
+
}
|
|
744
|
+
return handle;
|
|
745
|
+
}, []);
|
|
746
|
+
useEffect2(() => {
|
|
747
|
+
navigationGenerationRef.current += 1;
|
|
748
|
+
setNewerError(null);
|
|
749
|
+
loadingOlderRef.current = false;
|
|
750
|
+
loadingNewerRef.current = false;
|
|
751
|
+
loadingOldestRef.current = false;
|
|
752
|
+
loadingTargetRef.current = false;
|
|
753
|
+
setQuestionEvidence(null);
|
|
754
|
+
setLoadingTarget(false);
|
|
755
|
+
setLoadingOlder(false);
|
|
756
|
+
setLoadingNewer(false);
|
|
757
|
+
setLoadingOldest(false);
|
|
758
|
+
return () => {
|
|
759
|
+
navigationGenerationRef.current += 1;
|
|
760
|
+
};
|
|
761
|
+
}, [client, workspaceId, sessionId, after, enabled, fullReplay]);
|
|
762
|
+
useEffect2(() => {
|
|
763
|
+
if (streamKeyRef.current !== streamKey) {
|
|
764
|
+
streamKeyRef.current = streamKey;
|
|
765
|
+
setStateStreamKey(streamKey);
|
|
766
|
+
eventWindowRef.current = EMPTY_EVENT_WINDOW;
|
|
767
|
+
setEventWindow(EMPTY_EVENT_WINDOW);
|
|
768
|
+
setError(null);
|
|
769
|
+
setHasOlder(false);
|
|
770
|
+
setHasNewer(false);
|
|
771
|
+
sessionStatusRef.current = { sequence: after, status: null };
|
|
772
|
+
setSessionStatusProjection(null);
|
|
773
|
+
setLoadingOlder(false);
|
|
774
|
+
setLoadingNewer(false);
|
|
775
|
+
setLoadingOldest(false);
|
|
776
|
+
setLoadingLatest(false);
|
|
777
|
+
setInitialLoading(true);
|
|
778
|
+
setInitialHistoryReady(false);
|
|
779
|
+
lastSequenceRef.current = after;
|
|
780
|
+
streamResumeSequenceRef.current = after;
|
|
781
|
+
liveThroughRef.current = after;
|
|
782
|
+
skippedStepsThroughRef.current = null;
|
|
783
|
+
oldestSequenceRef.current = null;
|
|
784
|
+
newestSequenceRef.current = null;
|
|
785
|
+
hasOlderRef.current = false;
|
|
786
|
+
hasNewerRef.current = false;
|
|
787
|
+
loadingOlderRef.current = false;
|
|
788
|
+
loadingNewerRef.current = false;
|
|
789
|
+
loadingOldestRef.current = false;
|
|
790
|
+
loadingLatestRef.current = false;
|
|
791
|
+
viewModeRef.current = "live";
|
|
792
|
+
setViewMode("live");
|
|
793
|
+
initialWindowLoadedRef.current = false;
|
|
794
|
+
reconcileAfterPageResumeRef.current = false;
|
|
795
|
+
rejoinPendingRef.current = false;
|
|
796
|
+
streamTransientStreakRef.current = null;
|
|
797
|
+
stepGenerationRef.current += 1;
|
|
798
|
+
stepStoresRef.current = EMPTY_STEP_STORES;
|
|
799
|
+
setStepStores(EMPTY_STEP_STORES);
|
|
800
|
+
turnlessStepsRef.current = EMPTY_TURNLESS_STEPS;
|
|
801
|
+
setTurnlessSteps(EMPTY_TURNLESS_STEPS);
|
|
802
|
+
turnStepHandlesRef.current = /* @__PURE__ */ new Map();
|
|
803
|
+
presentedCallIdsRef.current = /* @__PURE__ */ new Set();
|
|
804
|
+
presentedScanRef.current = null;
|
|
805
|
+
startupRef.current = emptyStartupTurns();
|
|
806
|
+
presentedRequestedRef.current = /* @__PURE__ */ new Map();
|
|
807
|
+
presentedFailuresRef.current = /* @__PURE__ */ new Map();
|
|
808
|
+
setPresentedOutputs(EMPTY_PRESENTED_OUTPUTS);
|
|
809
|
+
}
|
|
810
|
+
const generation = generationRef.current + 1;
|
|
811
|
+
generationRef.current = generation;
|
|
812
|
+
if (!sessionId || !streamEnabled) {
|
|
813
|
+
streamTransientStreakRef.current = null;
|
|
814
|
+
if (sessionId && enabled && !pageLive && !fullReplay && viewModeRef.current === "live" && initialWindowLoadedRef.current) {
|
|
815
|
+
reconcileAfterPageResumeRef.current = true;
|
|
816
|
+
}
|
|
817
|
+
setConnectionState("idle");
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if (viewMode === "history") {
|
|
821
|
+
reconcileAfterPageResumeRef.current = false;
|
|
822
|
+
setConnectionState("idle");
|
|
823
|
+
setInitialLoading(false);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
const reconcileForegroundResume = reconcileAfterPageResumeRef.current && !fullReplay;
|
|
827
|
+
reconcileAfterPageResumeRef.current = false;
|
|
828
|
+
const controller = new AbortController();
|
|
829
|
+
const detailReadMark = sessionDetailReadMark();
|
|
830
|
+
const isCurrent = () => generationRef.current === generation && !controller.signal.aborted && navigationIdentityRef.current.client === client && navigationIdentityRef.current.streamKey === streamKey && navigationIdentityRef.current.enabled === enabled;
|
|
831
|
+
streamAbortRef.current = controller;
|
|
832
|
+
let transientRetryTimer = null;
|
|
833
|
+
const scheduleTransientRetry = (delayMs, retry) => {
|
|
834
|
+
if (transientRetryTimer !== null) clearTimeout(transientRetryTimer);
|
|
835
|
+
transientRetryTimer = setTimeout(() => {
|
|
836
|
+
transientRetryTimer = null;
|
|
837
|
+
if (isCurrent()) retry();
|
|
838
|
+
}, delayMs);
|
|
839
|
+
};
|
|
840
|
+
let pending = [];
|
|
841
|
+
let pendingBytes = 2;
|
|
842
|
+
let flushTimer = null;
|
|
843
|
+
const flush = () => {
|
|
844
|
+
if (flushTimer !== null) {
|
|
845
|
+
clearTimeout(flushTimer);
|
|
846
|
+
flushTimer = null;
|
|
847
|
+
}
|
|
848
|
+
if (!isCurrent()) {
|
|
849
|
+
pending = [];
|
|
850
|
+
pendingBytes = 2;
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
if (pending.length === 0) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
const batch = pending;
|
|
857
|
+
pending = [];
|
|
858
|
+
pendingBytes = 2;
|
|
859
|
+
const lastInBatch = batch[batch.length - 1];
|
|
860
|
+
if (lastInBatch) {
|
|
861
|
+
const batchResumeSequence = Math.max(...batch.map(eventResumeSequence));
|
|
862
|
+
lastSequenceRef.current = Math.max(lastSequenceRef.current, batchResumeSequence);
|
|
863
|
+
streamResumeSequenceRef.current = Math.max(
|
|
864
|
+
streamResumeSequenceRef.current,
|
|
865
|
+
batchResumeSequence
|
|
866
|
+
);
|
|
867
|
+
liveThroughRef.current = Math.max(liveThroughRef.current, batchResumeSequence);
|
|
868
|
+
}
|
|
869
|
+
const status = observeSessionStatus(batch, sessionStatusRef);
|
|
870
|
+
if (status !== void 0) {
|
|
871
|
+
setSessionStatusProjection(status);
|
|
872
|
+
}
|
|
873
|
+
let conversation = batch;
|
|
874
|
+
const steps = [];
|
|
875
|
+
if (!fullReplay) {
|
|
876
|
+
const windowNewest = newestSequenceRef.current ?? 0;
|
|
877
|
+
const startup = startupTurns();
|
|
878
|
+
conversation = [];
|
|
879
|
+
for (const event of batch) {
|
|
880
|
+
if (!isConversationHistoryEvent(event, presentedCallIds(event), startup.open)) {
|
|
881
|
+
steps.push(event);
|
|
882
|
+
} else if (event.sequence > windowNewest) conversation.push(event);
|
|
883
|
+
noteStartupTurns([event], startup);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
if (conversation.length > 0) {
|
|
887
|
+
const current = eventWindowRef.current;
|
|
888
|
+
const next = appendBrowserSessionEventWindow(current, conversation);
|
|
889
|
+
const retained = {
|
|
890
|
+
...next,
|
|
891
|
+
truncated: current.truncated || next.truncated
|
|
892
|
+
};
|
|
893
|
+
eventWindowRef.current = retained;
|
|
894
|
+
setEventWindow(retained);
|
|
895
|
+
if (!fullReplay) startupRef.current.window = retained.events;
|
|
896
|
+
oldestSequenceRef.current = retained.events[0]?.sequence ?? null;
|
|
897
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
|
|
898
|
+
if (retained.truncated) {
|
|
899
|
+
hasOlderRef.current = true;
|
|
900
|
+
setHasOlder(true);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
if (steps.length > 0) appendLiveSteps(steps);
|
|
904
|
+
};
|
|
905
|
+
const scheduleFlush = () => {
|
|
906
|
+
if (!isCurrent()) return;
|
|
907
|
+
flushTimer ??= setTimeout(flush, 16);
|
|
908
|
+
};
|
|
909
|
+
const installCatchup = (events, head) => {
|
|
910
|
+
const current = eventWindowRef.current;
|
|
911
|
+
const windowNewest = newestSequenceRef.current ?? 0;
|
|
912
|
+
const fresh = events.filter((event) => event.sequence > windowNewest);
|
|
913
|
+
const status = observeSessionStatus(fresh, sessionStatusRef);
|
|
914
|
+
const next = appendBrowserSessionEventWindow(current, fresh);
|
|
915
|
+
const retained = { ...next, truncated: current.truncated || next.truncated };
|
|
916
|
+
eventWindowRef.current = retained;
|
|
917
|
+
oldestSequenceRef.current = retained.events[0]?.sequence ?? oldestSequenceRef.current;
|
|
918
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events) ?? newestSequenceRef.current;
|
|
919
|
+
lastSequenceRef.current = Math.max(lastSequenceRef.current, head);
|
|
920
|
+
streamResumeSequenceRef.current = Math.max(streamResumeSequenceRef.current, head);
|
|
921
|
+
if (skippedStepsThroughRef.current !== null) {
|
|
922
|
+
skippedStepsThroughRef.current = Math.max(skippedStepsThroughRef.current, head);
|
|
923
|
+
}
|
|
924
|
+
if (status !== void 0) setSessionStatusProjection(status);
|
|
925
|
+
if (fresh.length > 0) setEventWindow(retained);
|
|
926
|
+
if (retained.truncated) {
|
|
927
|
+
hasOlderRef.current = true;
|
|
928
|
+
setHasOlder(true);
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
const effectScope = { signal: controller.signal, isCurrent };
|
|
932
|
+
const readSessionHead = async (mark, scope) => {
|
|
933
|
+
const joined = joinSessionDetailRead(client, workspaceId, sessionId, mark);
|
|
934
|
+
if (joined) {
|
|
935
|
+
try {
|
|
936
|
+
return await joined;
|
|
937
|
+
} catch (cause) {
|
|
938
|
+
if (scope.signal.aborted) throw cause;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return await client.getSession(workspaceId, sessionId, {
|
|
942
|
+
fresh: true,
|
|
943
|
+
signal: scope.signal
|
|
944
|
+
});
|
|
945
|
+
};
|
|
946
|
+
const rejoinLive = async (force, {
|
|
947
|
+
strict = false,
|
|
948
|
+
mark = detailReadMark,
|
|
949
|
+
scope = effectScope
|
|
950
|
+
} = {}) => {
|
|
951
|
+
const activeTurn = activeConversationTurn(eventWindowRef.current.events);
|
|
952
|
+
if (!force && !activeTurn) return "joined";
|
|
953
|
+
const cursor = streamResumeSequenceRef.current;
|
|
954
|
+
let head;
|
|
955
|
+
let sessionActiveTurn = null;
|
|
956
|
+
try {
|
|
957
|
+
const session = await readSessionHead(mark, scope);
|
|
958
|
+
scope.progress?.();
|
|
959
|
+
head = session.lastSequence;
|
|
960
|
+
sessionActiveTurn = session.activeTurnId;
|
|
961
|
+
} catch (cause) {
|
|
962
|
+
if (!scope.isCurrent() || strict && isRetryableStreamError(cause)) throw cause;
|
|
963
|
+
return "joined";
|
|
964
|
+
}
|
|
965
|
+
if (!scope.isCurrent()) throw abortError();
|
|
966
|
+
if (!Number.isSafeInteger(head) || head - cursor <= DIRECT_REPLAY_MAX_SEQUENCES) {
|
|
967
|
+
return "joined";
|
|
968
|
+
}
|
|
969
|
+
let catchup;
|
|
970
|
+
try {
|
|
971
|
+
catchup = await loadConversationCatchup(
|
|
972
|
+
client,
|
|
973
|
+
workspaceId,
|
|
974
|
+
sessionId,
|
|
975
|
+
cursor,
|
|
976
|
+
head,
|
|
977
|
+
scope
|
|
978
|
+
);
|
|
979
|
+
} catch (cause) {
|
|
980
|
+
if (!scope.isCurrent() || strict && isRetryableStreamError(cause)) throw cause;
|
|
981
|
+
return "joined";
|
|
982
|
+
}
|
|
983
|
+
if (!scope.isCurrent()) throw abortError();
|
|
984
|
+
if (catchup === null) return "reload";
|
|
985
|
+
const previous = eventWindowRef.current.events;
|
|
986
|
+
installCatchup(catchup, head);
|
|
987
|
+
const working = sessionActiveTurn ?? activeConversationTurn(eventWindowRef.current.events);
|
|
988
|
+
pruneStepStores(eventWindowRef.current.events, previous, working);
|
|
989
|
+
if (working) loadTurnStepsRef.current(working, head + 1);
|
|
990
|
+
if (working && conversationTurnBounds(eventWindowRef.current.events).has(working)) {
|
|
991
|
+
skippedStepsThroughRef.current = null;
|
|
992
|
+
}
|
|
993
|
+
return "joined";
|
|
994
|
+
};
|
|
995
|
+
const loadLatestWindow = async (scope = effectScope) => {
|
|
996
|
+
const window2 = await loadEventWindow(t, client, workspaceId, sessionId, {
|
|
997
|
+
before: Number.MAX_SAFE_INTEGER,
|
|
998
|
+
pageSize: SESSION_FIRST_WINDOW_PAGE_SIZE,
|
|
999
|
+
targetGroups: Number.POSITIVE_INFINITY,
|
|
1000
|
+
maxFetches: INITIAL_FETCH_CAP,
|
|
1001
|
+
boundaryPageCap: BOUNDARY_PAGE_CAP,
|
|
1002
|
+
signal: scope.signal,
|
|
1003
|
+
...scope.progress ? { progress: scope.progress } : {},
|
|
1004
|
+
filter: CONVERSATION_FILTER
|
|
1005
|
+
});
|
|
1006
|
+
if (!scope.isCurrent()) {
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
const status = observeSessionStatus(window2.events, sessionStatusRef);
|
|
1010
|
+
const retained = boundBrowserSessionEventWindow(window2.events);
|
|
1011
|
+
navigationGenerationRef.current += 1;
|
|
1012
|
+
loadingOlderRef.current = false;
|
|
1013
|
+
loadingNewerRef.current = false;
|
|
1014
|
+
loadingOldestRef.current = false;
|
|
1015
|
+
setLoadingOlder(false);
|
|
1016
|
+
setLoadingNewer(false);
|
|
1017
|
+
setLoadingOldest(false);
|
|
1018
|
+
const previous = eventWindowRef.current.events;
|
|
1019
|
+
eventWindowRef.current = retained;
|
|
1020
|
+
pruneStepStores(retained.events, previous);
|
|
1021
|
+
oldestSequenceRef.current = retained.events[0]?.sequence ?? window2.oldestSequence ?? window2.scanCursor;
|
|
1022
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events) ?? window2.newestSequence;
|
|
1023
|
+
hasOlderRef.current = window2.hasOlder || retained.truncated;
|
|
1024
|
+
hasNewerRef.current = false;
|
|
1025
|
+
lastSequenceRef.current = Math.max(lastSequenceRef.current, window2.newestSequence);
|
|
1026
|
+
streamResumeSequenceRef.current = window2.newestSequence;
|
|
1027
|
+
skippedStepsThroughRef.current = retained.events.length === 0 && window2.newestSequence > 0 ? window2.newestSequence : null;
|
|
1028
|
+
initialWindowLoadedRef.current = true;
|
|
1029
|
+
streamTransientStreakRef.current = null;
|
|
1030
|
+
setInitialHistoryReady(true);
|
|
1031
|
+
setError(null);
|
|
1032
|
+
if (status !== void 0) {
|
|
1033
|
+
setSessionStatusProjection(status);
|
|
1034
|
+
}
|
|
1035
|
+
setEventWindow(retained);
|
|
1036
|
+
setHasOlder(window2.hasOlder || retained.truncated);
|
|
1037
|
+
setHasNewer(false);
|
|
1038
|
+
setInitialLoading(false);
|
|
1039
|
+
return true;
|
|
1040
|
+
};
|
|
1041
|
+
const noteStreamResume = () => {
|
|
1042
|
+
if (!fullReplay && streamResumeSequenceRef.current > liveThroughRef.current) {
|
|
1043
|
+
markTurnlessOutputGap(workspaceId, sessionId);
|
|
1044
|
+
}
|
|
1045
|
+
liveThroughRef.current = Math.max(liveThroughRef.current, streamResumeSequenceRef.current);
|
|
1046
|
+
};
|
|
1047
|
+
const reconcileAfterOpen = (initialOpen, streak = null) => {
|
|
1048
|
+
void Promise.resolve().then(() => reconcileSession(sessionId, { initialOpen })).catch((cause) => {
|
|
1049
|
+
if (!isCurrent()) return;
|
|
1050
|
+
if (isTransientRequestError(cause)) {
|
|
1051
|
+
const now = Date.now();
|
|
1052
|
+
const current = streak ?? { since: now, attempts: 0, surfaced: false };
|
|
1053
|
+
const step = transientReadRetryStep(current, now, DEFAULT_TRANSIENT_READ_RETRY, cause);
|
|
1054
|
+
if (step.quiet) {
|
|
1055
|
+
scheduleTransientRetry(step.delayMs, () => reconcileAfterOpen(initialOpen, current));
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
setError(normalizeError(cause));
|
|
1060
|
+
});
|
|
1061
|
+
};
|
|
1062
|
+
const boundedRejoin = async (strict, join) => {
|
|
1063
|
+
if (!isCurrent()) throw abortError();
|
|
1064
|
+
const attempt = new AbortController();
|
|
1065
|
+
let timedOut = false;
|
|
1066
|
+
const abortAttempt = () => attempt.abort();
|
|
1067
|
+
controller.signal.addEventListener("abort", abortAttempt, { once: true });
|
|
1068
|
+
const stalled = () => {
|
|
1069
|
+
timedOut = true;
|
|
1070
|
+
attempt.abort();
|
|
1071
|
+
};
|
|
1072
|
+
let timer = setTimeout(stalled, REJOIN_STALL_TIMEOUT_MS);
|
|
1073
|
+
let stopWaiting;
|
|
1074
|
+
const stopped = new Promise((_resolve, reject) => {
|
|
1075
|
+
stopWaiting = () => reject(abortError());
|
|
1076
|
+
attempt.signal.addEventListener("abort", stopWaiting, { once: true });
|
|
1077
|
+
});
|
|
1078
|
+
const scope = {
|
|
1079
|
+
signal: attempt.signal,
|
|
1080
|
+
isCurrent: () => isCurrent() && !attempt.signal.aborted,
|
|
1081
|
+
progress: () => {
|
|
1082
|
+
if (attempt.signal.aborted) return;
|
|
1083
|
+
clearTimeout(timer);
|
|
1084
|
+
timer = setTimeout(stalled, REJOIN_STALL_TIMEOUT_MS);
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
try {
|
|
1088
|
+
return await Promise.race([join(scope), stopped]);
|
|
1089
|
+
} catch (cause) {
|
|
1090
|
+
if (!isCurrent()) throw cause;
|
|
1091
|
+
if (timedOut) {
|
|
1092
|
+
return strict ? {
|
|
1093
|
+
retry: new TypeError(
|
|
1094
|
+
`rejoining the live session stalled for ${REJOIN_STALL_TIMEOUT_MS}ms`
|
|
1095
|
+
)
|
|
1096
|
+
} : "joined";
|
|
1097
|
+
}
|
|
1098
|
+
if (strict && isRetryableStreamError(cause)) return { retry: cause };
|
|
1099
|
+
throw cause;
|
|
1100
|
+
} finally {
|
|
1101
|
+
clearTimeout(timer);
|
|
1102
|
+
controller.signal.removeEventListener("abort", abortAttempt);
|
|
1103
|
+
if (stopWaiting) attempt.signal.removeEventListener("abort", stopWaiting);
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
const leniently = async (join) => {
|
|
1107
|
+
const result = await boundedRejoin(false, join);
|
|
1108
|
+
return typeof result === "string" ? result : "joined";
|
|
1109
|
+
};
|
|
1110
|
+
let strictFailures = 0;
|
|
1111
|
+
const beforeReconnect = async (cursor) => {
|
|
1112
|
+
flush();
|
|
1113
|
+
if (!isCurrent()) throw abortError();
|
|
1114
|
+
const strict = strictFailures < STRICT_REJOIN_ATTEMPTS;
|
|
1115
|
+
const mark = sessionDetailReadMark();
|
|
1116
|
+
let result;
|
|
1117
|
+
try {
|
|
1118
|
+
result = await boundedRejoin(strict, async (scope) => {
|
|
1119
|
+
if (await rejoinLive(true, { strict, mark, scope }) === "joined") return "joined";
|
|
1120
|
+
let loaded;
|
|
1121
|
+
try {
|
|
1122
|
+
loaded = await loadLatestWindow(scope);
|
|
1123
|
+
} catch (cause) {
|
|
1124
|
+
if (!scope.isCurrent() || strict && isRetryableStreamError(cause)) throw cause;
|
|
1125
|
+
return "joined";
|
|
1126
|
+
}
|
|
1127
|
+
if (!loaded) throw abortError();
|
|
1128
|
+
if (await rejoinLive(true, { strict, mark, scope }) === "reload" && strict) {
|
|
1129
|
+
throw new TypeError("the session outran a fresh window while rejoining it");
|
|
1130
|
+
}
|
|
1131
|
+
return "joined";
|
|
1132
|
+
});
|
|
1133
|
+
} catch (cause) {
|
|
1134
|
+
if (!isCurrent()) throw abortError();
|
|
1135
|
+
if (!isRetryableStreamError(cause)) throw cause;
|
|
1136
|
+
throw new Error(`rejoining the live session failed: ${errorMessage(cause)}`, { cause });
|
|
1137
|
+
}
|
|
1138
|
+
if (!isCurrent()) throw abortError();
|
|
1139
|
+
if (typeof result === "object") {
|
|
1140
|
+
strictFailures += 1;
|
|
1141
|
+
throw result.retry;
|
|
1142
|
+
}
|
|
1143
|
+
noteStreamResume();
|
|
1144
|
+
return Math.max(cursor, streamResumeSequenceRef.current);
|
|
1145
|
+
};
|
|
1146
|
+
void (async () => {
|
|
1147
|
+
try {
|
|
1148
|
+
if (reconcileForegroundResume) {
|
|
1149
|
+
setConnectionState("connecting");
|
|
1150
|
+
let outcome;
|
|
1151
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
1152
|
+
const strict = attempt < STRICT_REJOIN_ATTEMPTS;
|
|
1153
|
+
const result = await boundedRejoin(
|
|
1154
|
+
strict,
|
|
1155
|
+
(scope) => rejoinLive(true, { strict, scope })
|
|
1156
|
+
);
|
|
1157
|
+
if (typeof result === "string") {
|
|
1158
|
+
outcome = result;
|
|
1159
|
+
break;
|
|
1160
|
+
}
|
|
1161
|
+
await abortableDelay(
|
|
1162
|
+
Math.min(FOREGROUND_REJOIN_RETRY_MS * 2 ** attempt, FOREGROUND_REJOIN_RETRY_MAX_MS),
|
|
1163
|
+
controller.signal
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
if (outcome === "reload") {
|
|
1167
|
+
initialWindowLoadedRef.current = false;
|
|
1168
|
+
setError(null);
|
|
1169
|
+
}
|
|
1170
|
+
} else if (!fullReplay && initialWindowLoadedRef.current && rejoinPendingRef.current) {
|
|
1171
|
+
rejoinPendingRef.current = false;
|
|
1172
|
+
if (await leniently((scope) => rejoinLive(false, { scope })) === "reload") {
|
|
1173
|
+
initialWindowLoadedRef.current = false;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
rejoinPendingRef.current = false;
|
|
1177
|
+
const windowPasses = fullReplay ? 0 : 2;
|
|
1178
|
+
for (let attempt = 0; attempt < windowPasses && !initialWindowLoadedRef.current; attempt += 1) {
|
|
1179
|
+
setConnectionState("connecting");
|
|
1180
|
+
if (!await loadLatestWindow()) {
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
if (await leniently((scope) => rejoinLive(true, { scope })) === "reload" && attempt === 0) {
|
|
1184
|
+
initialWindowLoadedRef.current = false;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
if (fullReplay) {
|
|
1188
|
+
setInitialLoading(false);
|
|
1189
|
+
}
|
|
1190
|
+
noteStreamResume();
|
|
1191
|
+
const stream = client.streamEvents(workspaceId, sessionId, {
|
|
1192
|
+
after: streamResumeSequenceRef.current,
|
|
1193
|
+
signal: controller.signal,
|
|
1194
|
+
// Full replay keeps the SDK's exactly-once reconnects.
|
|
1195
|
+
...fullReplay ? {} : { beforeReconnect },
|
|
1196
|
+
onOpen: () => {
|
|
1197
|
+
const initialOpen = openedStreamKeyRef.current !== streamKey;
|
|
1198
|
+
openedStreamKeyRef.current = streamKey;
|
|
1199
|
+
strictFailures = 0;
|
|
1200
|
+
if (streamTransientStreakRef.current?.surfaced) setError(null);
|
|
1201
|
+
streamTransientStreakRef.current = null;
|
|
1202
|
+
reconcileAfterOpen(initialOpen);
|
|
1203
|
+
},
|
|
1204
|
+
onStateChange: (state) => {
|
|
1205
|
+
if (isCurrent()) {
|
|
1206
|
+
setConnectionState(state);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
});
|
|
1210
|
+
for await (const event of stream) {
|
|
1211
|
+
if (!isCurrent()) break;
|
|
1212
|
+
const boundedEvent = event;
|
|
1213
|
+
const boundedEventBytes = browserJsonBytes(boundedEvent);
|
|
1214
|
+
const separatorBytes = pending.length === 0 ? 0 : 1;
|
|
1215
|
+
if (pending.length > 0 && (pending.length >= SESSION_EVENT_BROWSER_PENDING_MAX_COUNT || pendingBytes + separatorBytes + boundedEventBytes > SESSION_EVENT_BROWSER_PENDING_MAX_BYTES)) {
|
|
1216
|
+
flush();
|
|
1217
|
+
}
|
|
1218
|
+
pending.push(boundedEvent);
|
|
1219
|
+
pendingBytes += (pending.length === 1 ? 0 : 1) + boundedEventBytes;
|
|
1220
|
+
if (pending.length >= SESSION_EVENT_BROWSER_PENDING_MAX_COUNT || pendingBytes >= SESSION_EVENT_BROWSER_PENDING_MAX_BYTES) {
|
|
1221
|
+
flush();
|
|
1222
|
+
} else {
|
|
1223
|
+
scheduleFlush();
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
if (isCurrent()) {
|
|
1227
|
+
flush();
|
|
1228
|
+
setConnectionState("ended");
|
|
1229
|
+
}
|
|
1230
|
+
} catch (cause) {
|
|
1231
|
+
if (isCurrent()) {
|
|
1232
|
+
flush();
|
|
1233
|
+
let announce = true;
|
|
1234
|
+
if (isTransientRequestError(cause)) {
|
|
1235
|
+
const now = Date.now();
|
|
1236
|
+
const streak = streamTransientStreakRef.current ??= {
|
|
1237
|
+
since: now,
|
|
1238
|
+
attempts: 0,
|
|
1239
|
+
surfaced: false
|
|
1240
|
+
};
|
|
1241
|
+
const step = transientReadRetryStep(streak, now, DEFAULT_TRANSIENT_READ_RETRY, cause);
|
|
1242
|
+
scheduleTransientRetry(step.delayMs, () => setStreamEpoch((epoch) => epoch + 1));
|
|
1243
|
+
if (step.quiet) {
|
|
1244
|
+
setConnectionState("connecting");
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
announce = !streak.surfaced;
|
|
1248
|
+
streak.surfaced = true;
|
|
1249
|
+
} else {
|
|
1250
|
+
streamTransientStreakRef.current = null;
|
|
1251
|
+
}
|
|
1252
|
+
if (announce) setError(normalizeError(cause));
|
|
1253
|
+
setConnectionState("error");
|
|
1254
|
+
setInitialLoading(false);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
})();
|
|
1258
|
+
return () => {
|
|
1259
|
+
controller.abort();
|
|
1260
|
+
if (transientRetryTimer !== null) clearTimeout(transientRetryTimer);
|
|
1261
|
+
if (generationRef.current === generation) {
|
|
1262
|
+
generationRef.current = generation + 1;
|
|
1263
|
+
}
|
|
1264
|
+
if (streamAbortRef.current === controller) {
|
|
1265
|
+
streamAbortRef.current = null;
|
|
1266
|
+
}
|
|
1267
|
+
if (flushTimer !== null) {
|
|
1268
|
+
clearTimeout(flushTimer);
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
}, [
|
|
1272
|
+
client,
|
|
1273
|
+
workspaceId,
|
|
1274
|
+
sessionId,
|
|
1275
|
+
after,
|
|
1276
|
+
enabled,
|
|
1277
|
+
pageLive,
|
|
1278
|
+
streamEnabled,
|
|
1279
|
+
fullReplay,
|
|
1280
|
+
streamKey,
|
|
1281
|
+
streamEpoch,
|
|
1282
|
+
viewMode,
|
|
1283
|
+
reconcileSession,
|
|
1284
|
+
t,
|
|
1285
|
+
pruneStepStores,
|
|
1286
|
+
appendLiveSteps,
|
|
1287
|
+
presentedCallIds,
|
|
1288
|
+
startupTurns
|
|
1289
|
+
]);
|
|
1290
|
+
useEffect2(() => {
|
|
1291
|
+
if (!sessionId || !streamEnabled) return;
|
|
1292
|
+
return subscribePageWake(() => {
|
|
1293
|
+
const stream = streamAbortRef.current;
|
|
1294
|
+
if (!stream || stream.signal.aborted || viewModeRef.current !== "live") return;
|
|
1295
|
+
if (!fullReplay && initialWindowLoadedRef.current) {
|
|
1296
|
+
reconcileAfterPageResumeRef.current = true;
|
|
1297
|
+
}
|
|
1298
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
1299
|
+
});
|
|
1300
|
+
}, [sessionId, streamEnabled, fullReplay]);
|
|
1301
|
+
const navigationBusy = () => loadingTargetRef.current || loadingOlderRef.current || loadingNewerRef.current || loadingOldestRef.current || loadingLatestRef.current;
|
|
1302
|
+
const loadOlder = useCallback3(
|
|
1303
|
+
() => createOlderHistoryLoadReceipt(async (markCommitted, preserveTail, markTailPreserved) => {
|
|
1304
|
+
if (!ownsNavigation() || !sessionId || navigationBusy() || !hasOlderRef.current) {
|
|
1305
|
+
return false;
|
|
1306
|
+
}
|
|
1307
|
+
const before = oldestSequenceRef.current;
|
|
1308
|
+
if (before === null) {
|
|
1309
|
+
oldestSequenceRef.current = null;
|
|
1310
|
+
hasOlderRef.current = false;
|
|
1311
|
+
setHasOlder(false);
|
|
1312
|
+
return false;
|
|
1313
|
+
}
|
|
1314
|
+
const generation = navigationGenerationRef.current;
|
|
1315
|
+
const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
|
|
1316
|
+
loadingOlderRef.current = true;
|
|
1317
|
+
setLoadingOlder(true);
|
|
1318
|
+
let published = false;
|
|
1319
|
+
try {
|
|
1320
|
+
const window2 = await loadEventWindow(t, client, workspaceId, sessionId, {
|
|
1321
|
+
before,
|
|
1322
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
1323
|
+
targetGroups: OLDER_GROUP_TARGET,
|
|
1324
|
+
maxFetches: OLDER_FETCH_CAP,
|
|
1325
|
+
filteredPageCap: FILTERED_PAGE_CAP,
|
|
1326
|
+
isCurrent,
|
|
1327
|
+
filter: historyFilter
|
|
1328
|
+
});
|
|
1329
|
+
if (!isCurrent()) {
|
|
1330
|
+
return false;
|
|
1331
|
+
}
|
|
1332
|
+
if (window2.events.length === 0) {
|
|
1333
|
+
if (window2.hasOlder && window2.scanCursor < before) {
|
|
1334
|
+
oldestSequenceRef.current = window2.scanCursor;
|
|
1335
|
+
return true;
|
|
1336
|
+
}
|
|
1337
|
+
oldestSequenceRef.current = null;
|
|
1338
|
+
hasOlderRef.current = false;
|
|
1339
|
+
setHasOlder(false);
|
|
1340
|
+
return false;
|
|
1341
|
+
}
|
|
1342
|
+
const current = eventWindowRef.current;
|
|
1343
|
+
assertPrependOrder(current.events, window2.events);
|
|
1344
|
+
const next = boundBrowserSessionEventWindow([...window2.events, ...current.events], {
|
|
1345
|
+
direction: "oldest"
|
|
1346
|
+
});
|
|
1347
|
+
if (preserveTail && maxResumeSequence(next.events) < maxResumeSequence(current.events)) {
|
|
1348
|
+
markTailPreserved();
|
|
1349
|
+
return false;
|
|
1350
|
+
}
|
|
1351
|
+
const status = observeSessionStatus(window2.events, sessionStatusRef);
|
|
1352
|
+
const retained = {
|
|
1353
|
+
...next,
|
|
1354
|
+
truncated: current.truncated || next.truncated
|
|
1355
|
+
};
|
|
1356
|
+
const retainedOldest = retained.events[0]?.sequence ?? null;
|
|
1357
|
+
if (retainedOldest === null || retainedOldest >= before) {
|
|
1358
|
+
throw new Error("@opengeni/react: loadOlder made no durable sequence progress");
|
|
1359
|
+
}
|
|
1360
|
+
const retainedNewest = maxResumeSequenceOrNull(retained.events);
|
|
1361
|
+
const previousNewest = maxResumeSequenceOrNull(current.events);
|
|
1362
|
+
const evictedLiveTail = previousNewest !== null && retainedNewest !== null && retainedNewest < previousNewest;
|
|
1363
|
+
if (evictedLiveTail && viewModeRef.current === "live") {
|
|
1364
|
+
streamAbortRef.current?.abort();
|
|
1365
|
+
}
|
|
1366
|
+
eventWindowRef.current = retained;
|
|
1367
|
+
if (evictedLiveTail) {
|
|
1368
|
+
skippedStepsThroughRef.current = null;
|
|
1369
|
+
pruneStepStores(retained.events, current.events);
|
|
1370
|
+
} else {
|
|
1371
|
+
const skippedThrough = skippedStepsThroughRef.current;
|
|
1372
|
+
if (skippedThrough !== null && windowTurns(window2.events).size > 0) {
|
|
1373
|
+
skippedStepsThroughRef.current = null;
|
|
1374
|
+
const working = activeConversationTurn(retained.events);
|
|
1375
|
+
const bounds = working ? conversationTurnBounds(retained.events).get(working) : void 0;
|
|
1376
|
+
if (working && bounds && bounds.first <= skippedThrough) {
|
|
1377
|
+
loadTurnStepsRef.current(working, skippedThrough + 1);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
markCommitted();
|
|
1382
|
+
oldestSequenceRef.current = retainedOldest;
|
|
1383
|
+
newestSequenceRef.current = retainedNewest;
|
|
1384
|
+
if (evictedLiveTail) {
|
|
1385
|
+
streamResumeSequenceRef.current = maxResumeSequence(retained.events);
|
|
1386
|
+
}
|
|
1387
|
+
const olderStillAvailable = window2.hasOlder;
|
|
1388
|
+
hasOlderRef.current = olderStillAvailable;
|
|
1389
|
+
let newer = null;
|
|
1390
|
+
let enterHistory = false;
|
|
1391
|
+
if (viewModeRef.current === "history" || evictedLiveTail) {
|
|
1392
|
+
const highWater = lastSequenceRef.current;
|
|
1393
|
+
newer = retainedNewest !== null && (retainedNewest < highWater || retained.truncated);
|
|
1394
|
+
hasNewerRef.current = newer;
|
|
1395
|
+
if (evictedLiveTail) {
|
|
1396
|
+
viewModeRef.current = "history";
|
|
1397
|
+
enterHistory = true;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
loadingOlderRef.current = false;
|
|
1401
|
+
startTransition(() => {
|
|
1402
|
+
if (status !== void 0) {
|
|
1403
|
+
setSessionStatusProjection(status);
|
|
1404
|
+
}
|
|
1405
|
+
setEventWindow(retained);
|
|
1406
|
+
setHasOlder(olderStillAvailable);
|
|
1407
|
+
if (newer !== null) {
|
|
1408
|
+
setHasNewer(newer);
|
|
1409
|
+
}
|
|
1410
|
+
if (enterHistory) {
|
|
1411
|
+
setViewMode("history");
|
|
1412
|
+
}
|
|
1413
|
+
setLoadingOlder(false);
|
|
1414
|
+
});
|
|
1415
|
+
published = true;
|
|
1416
|
+
return olderStillAvailable;
|
|
1417
|
+
} catch (reason) {
|
|
1418
|
+
if (!isCurrent()) return false;
|
|
1419
|
+
throw reason;
|
|
1420
|
+
} finally {
|
|
1421
|
+
if (!published && isCurrent()) {
|
|
1422
|
+
loadingOlderRef.current = false;
|
|
1423
|
+
setLoadingOlder(false);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
}),
|
|
1427
|
+
[client, workspaceId, sessionId, ownsNavigation, t, historyFilter, pruneStepStores]
|
|
1428
|
+
);
|
|
1429
|
+
const loadOldest = useCallback3(async () => {
|
|
1430
|
+
if (!ownsNavigation() || !sessionId || navigationBusy() || !hasOlderRef.current) {
|
|
1431
|
+
return false;
|
|
1432
|
+
}
|
|
1433
|
+
const generation = ++navigationGenerationRef.current;
|
|
1434
|
+
const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
|
|
1435
|
+
setQuestionEvidence(null);
|
|
1436
|
+
loadingOldestRef.current = true;
|
|
1437
|
+
setLoadingOldest(true);
|
|
1438
|
+
let published = false;
|
|
1439
|
+
try {
|
|
1440
|
+
const window2 = await loadForwardEventWindow(t, client, workspaceId, sessionId, {
|
|
1441
|
+
after: 0,
|
|
1442
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
1443
|
+
targetGroups: OLDEST_GROUP_TARGET,
|
|
1444
|
+
maxFetches: OLDEST_FETCH_CAP,
|
|
1445
|
+
isCurrent,
|
|
1446
|
+
filter: historyFilter
|
|
1447
|
+
});
|
|
1448
|
+
if (!isCurrent()) {
|
|
1449
|
+
return false;
|
|
1450
|
+
}
|
|
1451
|
+
if (window2.events.length === 0) {
|
|
1452
|
+
hasOlderRef.current = false;
|
|
1453
|
+
setHasOlder(false);
|
|
1454
|
+
return false;
|
|
1455
|
+
}
|
|
1456
|
+
streamAbortRef.current?.abort();
|
|
1457
|
+
const status = observeSessionStatus(window2.events, sessionStatusRef);
|
|
1458
|
+
const retained = boundBrowserSessionEventWindow(window2.events, { direction: "oldest" });
|
|
1459
|
+
const retainedOldest = retained.events[0]?.sequence ?? null;
|
|
1460
|
+
const retainedNewest = maxResumeSequenceOrNull(retained.events);
|
|
1461
|
+
const previous = eventWindowRef.current.events;
|
|
1462
|
+
eventWindowRef.current = retained;
|
|
1463
|
+
skippedStepsThroughRef.current = null;
|
|
1464
|
+
pruneStepStores(retained.events, previous);
|
|
1465
|
+
oldestSequenceRef.current = retainedOldest;
|
|
1466
|
+
newestSequenceRef.current = retainedNewest;
|
|
1467
|
+
hasOlderRef.current = false;
|
|
1468
|
+
const highWater = Math.max(lastSequenceRef.current, retainedNewest ?? 0);
|
|
1469
|
+
lastSequenceRef.current = highWater;
|
|
1470
|
+
const newer = window2.hasNewer || retained.truncated || retainedNewest !== null && retainedNewest < highWater;
|
|
1471
|
+
hasNewerRef.current = newer;
|
|
1472
|
+
initialWindowLoadedRef.current = true;
|
|
1473
|
+
setInitialHistoryReady(true);
|
|
1474
|
+
setError(null);
|
|
1475
|
+
viewModeRef.current = "history";
|
|
1476
|
+
loadingOldestRef.current = false;
|
|
1477
|
+
setNewerError(null);
|
|
1478
|
+
startTransition(() => {
|
|
1479
|
+
if (status !== void 0) {
|
|
1480
|
+
setSessionStatusProjection(status);
|
|
1481
|
+
}
|
|
1482
|
+
setEventWindow(retained);
|
|
1483
|
+
setHasOlder(false);
|
|
1484
|
+
setHasNewer(newer);
|
|
1485
|
+
setViewMode("history");
|
|
1486
|
+
setLoadingOldest(false);
|
|
1487
|
+
});
|
|
1488
|
+
published = true;
|
|
1489
|
+
return newer;
|
|
1490
|
+
} catch (reason) {
|
|
1491
|
+
if (!isCurrent()) return false;
|
|
1492
|
+
throw reason;
|
|
1493
|
+
} finally {
|
|
1494
|
+
if (!published && isCurrent()) {
|
|
1495
|
+
loadingOldestRef.current = false;
|
|
1496
|
+
setLoadingOldest(false);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
}, [client, workspaceId, sessionId, ownsNavigation, t, historyFilter, pruneStepStores]);
|
|
1500
|
+
const loadNewer = useCallback3(async () => {
|
|
1501
|
+
if (!ownsNavigation() || !sessionId || navigationBusy() || !hasNewerRef.current) {
|
|
1502
|
+
return false;
|
|
1503
|
+
}
|
|
1504
|
+
const afterSequence = newestSequenceRef.current;
|
|
1505
|
+
if (afterSequence === null) {
|
|
1506
|
+
hasNewerRef.current = false;
|
|
1507
|
+
setHasNewer(false);
|
|
1508
|
+
return false;
|
|
1509
|
+
}
|
|
1510
|
+
const generation = navigationGenerationRef.current;
|
|
1511
|
+
const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
|
|
1512
|
+
loadingNewerRef.current = true;
|
|
1513
|
+
setLoadingNewer(true);
|
|
1514
|
+
let published = false;
|
|
1515
|
+
try {
|
|
1516
|
+
const window2 = await loadForwardEventWindow(t, client, workspaceId, sessionId, {
|
|
1517
|
+
after: afterSequence,
|
|
1518
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
1519
|
+
targetGroups: NEWER_GROUP_TARGET,
|
|
1520
|
+
maxFetches: NEWER_FETCH_CAP,
|
|
1521
|
+
filteredPageCap: FILTERED_PAGE_CAP,
|
|
1522
|
+
isCurrent,
|
|
1523
|
+
filter: historyFilter
|
|
1524
|
+
});
|
|
1525
|
+
if (!isCurrent()) {
|
|
1526
|
+
return false;
|
|
1527
|
+
}
|
|
1528
|
+
if (window2.events.length === 0) {
|
|
1529
|
+
if (window2.hasNewer && window2.scanCursor > afterSequence) {
|
|
1530
|
+
newestSequenceRef.current = window2.scanCursor;
|
|
1531
|
+
return true;
|
|
1532
|
+
}
|
|
1533
|
+
setNewerError(null);
|
|
1534
|
+
hasNewerRef.current = false;
|
|
1535
|
+
setHasNewer(false);
|
|
1536
|
+
streamResumeSequenceRef.current = afterSequence;
|
|
1537
|
+
rejoinPendingRef.current = true;
|
|
1538
|
+
viewModeRef.current = "live";
|
|
1539
|
+
setViewMode("live");
|
|
1540
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
1541
|
+
return false;
|
|
1542
|
+
}
|
|
1543
|
+
const current = eventWindowRef.current;
|
|
1544
|
+
assertAppendOrder(current.events, window2.events);
|
|
1545
|
+
const status = observeSessionStatus(window2.events, sessionStatusRef);
|
|
1546
|
+
const previousOldest = current.events[0]?.sequence ?? null;
|
|
1547
|
+
const next = appendBrowserSessionEventWindow(current, window2.events);
|
|
1548
|
+
const retained = {
|
|
1549
|
+
...next,
|
|
1550
|
+
truncated: current.truncated || next.truncated || window2.hasNewer
|
|
1551
|
+
};
|
|
1552
|
+
const retainedOldest = retained.events[0]?.sequence ?? null;
|
|
1553
|
+
const retainedNewest = maxResumeSequenceOrNull(retained.events);
|
|
1554
|
+
if (retainedNewest === null || retainedNewest <= afterSequence) {
|
|
1555
|
+
throw new Error("@opengeni/react: loadNewer made no durable sequence progress");
|
|
1556
|
+
}
|
|
1557
|
+
eventWindowRef.current = retained;
|
|
1558
|
+
pruneStepStores(retained.events, null);
|
|
1559
|
+
oldestSequenceRef.current = retainedOldest;
|
|
1560
|
+
newestSequenceRef.current = retainedNewest;
|
|
1561
|
+
if (retainedOldest !== null && (previousOldest === null || retainedOldest > previousOldest || retained.truncated && retained.events[0]?.type !== "session.created" && retainedOldest > 1)) {
|
|
1562
|
+
hasOlderRef.current = true;
|
|
1563
|
+
}
|
|
1564
|
+
const rearmedOlder = hasOlderRef.current;
|
|
1565
|
+
const highWater = Math.max(lastSequenceRef.current, retainedNewest);
|
|
1566
|
+
lastSequenceRef.current = highWater;
|
|
1567
|
+
const newer = window2.hasNewer || retainedNewest < highWater;
|
|
1568
|
+
hasNewerRef.current = newer;
|
|
1569
|
+
let resumeLive = false;
|
|
1570
|
+
if (!newer) {
|
|
1571
|
+
streamResumeSequenceRef.current = retainedNewest;
|
|
1572
|
+
rejoinPendingRef.current = true;
|
|
1573
|
+
viewModeRef.current = "live";
|
|
1574
|
+
resumeLive = true;
|
|
1575
|
+
}
|
|
1576
|
+
loadingNewerRef.current = false;
|
|
1577
|
+
setNewerError(null);
|
|
1578
|
+
startTransition(() => {
|
|
1579
|
+
if (status !== void 0) {
|
|
1580
|
+
setSessionStatusProjection(status);
|
|
1581
|
+
}
|
|
1582
|
+
setEventWindow(retained);
|
|
1583
|
+
setHasOlder(rearmedOlder);
|
|
1584
|
+
setHasNewer(newer);
|
|
1585
|
+
if (resumeLive) {
|
|
1586
|
+
setViewMode("live");
|
|
1587
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
1588
|
+
}
|
|
1589
|
+
setLoadingNewer(false);
|
|
1590
|
+
});
|
|
1591
|
+
published = true;
|
|
1592
|
+
return newer;
|
|
1593
|
+
} catch (reason) {
|
|
1594
|
+
if (!isCurrent()) {
|
|
1595
|
+
return false;
|
|
1596
|
+
}
|
|
1597
|
+
setNewerError(normalizeError(reason));
|
|
1598
|
+
throw reason;
|
|
1599
|
+
} finally {
|
|
1600
|
+
if (!published && isCurrent()) {
|
|
1601
|
+
loadingNewerRef.current = false;
|
|
1602
|
+
setLoadingNewer(false);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
}, [client, workspaceId, sessionId, ownsNavigation, t, historyFilter, pruneStepStores]);
|
|
1606
|
+
const jumpToSequence = useCallback3(
|
|
1607
|
+
async (sequence, navigationOptions) => {
|
|
1608
|
+
const signal = navigationOptions?.signal;
|
|
1609
|
+
if (!ownsNavigation() || !sessionId || !Number.isSafeInteger(sequence) || sequence < 1)
|
|
1610
|
+
return false;
|
|
1611
|
+
if (signal?.aborted) return false;
|
|
1612
|
+
const generation = ++navigationGenerationRef.current;
|
|
1613
|
+
setQuestionEvidence(null);
|
|
1614
|
+
const current = () => generation === navigationGenerationRef.current && ownsNavigation();
|
|
1615
|
+
const previousMode = viewModeRef.current;
|
|
1616
|
+
streamAbortRef.current?.abort();
|
|
1617
|
+
generationRef.current += 1;
|
|
1618
|
+
viewModeRef.current = "history";
|
|
1619
|
+
setViewMode("history");
|
|
1620
|
+
loadingOlderRef.current = loadingNewerRef.current = loadingOldestRef.current = false;
|
|
1621
|
+
setLoadingOlder(false);
|
|
1622
|
+
setLoadingNewer(false);
|
|
1623
|
+
setLoadingOldest(false);
|
|
1624
|
+
loadingTargetRef.current = true;
|
|
1625
|
+
setLoadingTarget(true);
|
|
1626
|
+
let published = false;
|
|
1627
|
+
try {
|
|
1628
|
+
const around = (filter) => Promise.all([
|
|
1629
|
+
loadPreviousPage(client, workspaceId, sessionId, sequence + 1, {
|
|
1630
|
+
pageSize: 128,
|
|
1631
|
+
filter
|
|
1632
|
+
}),
|
|
1633
|
+
loadNextPage(client, workspaceId, sessionId, sequence, { pageSize: 128, filter })
|
|
1634
|
+
]);
|
|
1635
|
+
let [before, following] = await around(historyFilter);
|
|
1636
|
+
if (historyFilter && current() && !before.some((event) => event.sequence === sequence)) {
|
|
1637
|
+
[before, following] = await around(void 0);
|
|
1638
|
+
}
|
|
1639
|
+
if (signal?.aborted) return false;
|
|
1640
|
+
if (!current()) return false;
|
|
1641
|
+
if (!before.some((event) => event.sequence === sequence)) return false;
|
|
1642
|
+
const prefix = boundBrowserSessionEventWindow(before, {
|
|
1643
|
+
direction: "newest",
|
|
1644
|
+
maxBytes: SESSION_EVENT_BROWSER_MAX_BYTES / 2
|
|
1645
|
+
});
|
|
1646
|
+
const retained = boundBrowserSessionEventWindow([...prefix.events, ...following], {
|
|
1647
|
+
direction: "oldest"
|
|
1648
|
+
});
|
|
1649
|
+
if (!retained.events.some((event) => event.sequence === sequence)) return false;
|
|
1650
|
+
streamAbortRef.current?.abort();
|
|
1651
|
+
generationRef.current += 1;
|
|
1652
|
+
const status = observeSessionStatus(retained.events, sessionStatusRef);
|
|
1653
|
+
const previous = eventWindowRef.current.events;
|
|
1654
|
+
eventWindowRef.current = retained;
|
|
1655
|
+
pruneStepStores(retained.events, previous);
|
|
1656
|
+
oldestSequenceRef.current = retained.events[0].sequence;
|
|
1657
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
|
|
1658
|
+
lastSequenceRef.current = Math.max(lastSequenceRef.current, newestSequenceRef.current ?? 0);
|
|
1659
|
+
hasOlderRef.current = !isLogStart(retained.events[0]);
|
|
1660
|
+
hasNewerRef.current = following.length > 0 || retained.truncated || (newestSequenceRef.current ?? 0) < lastSequenceRef.current;
|
|
1661
|
+
initialWindowLoadedRef.current = true;
|
|
1662
|
+
setInitialHistoryReady(true);
|
|
1663
|
+
setError(null);
|
|
1664
|
+
viewModeRef.current = "history";
|
|
1665
|
+
setEventWindow(retained);
|
|
1666
|
+
setHasOlder(hasOlderRef.current);
|
|
1667
|
+
setHasNewer(hasNewerRef.current);
|
|
1668
|
+
setInitialLoading(false);
|
|
1669
|
+
setViewMode("history");
|
|
1670
|
+
setNewerError(null);
|
|
1671
|
+
if (status !== void 0) setSessionStatusProjection(status);
|
|
1672
|
+
published = true;
|
|
1673
|
+
return true;
|
|
1674
|
+
} catch (reason) {
|
|
1675
|
+
if (!current() || signal?.aborted) return false;
|
|
1676
|
+
throw reason;
|
|
1677
|
+
} finally {
|
|
1678
|
+
if (current()) {
|
|
1679
|
+
loadingTargetRef.current = false;
|
|
1680
|
+
setLoadingTarget(false);
|
|
1681
|
+
if (!published && previousMode === "live") {
|
|
1682
|
+
viewModeRef.current = "live";
|
|
1683
|
+
setViewMode("live");
|
|
1684
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
},
|
|
1689
|
+
[client, workspaceId, sessionId, ownsNavigation, historyFilter, pruneStepStores]
|
|
1690
|
+
);
|
|
1691
|
+
const jumpToLatestQuestion = useCallback3(
|
|
1692
|
+
async (questionOptions) => {
|
|
1693
|
+
if (!sessionId || !ownsNavigation()) return null;
|
|
1694
|
+
let generation = navigationGenerationRef.current;
|
|
1695
|
+
const current = () => ownsNavigation() && generation === navigationGenerationRef.current;
|
|
1696
|
+
try {
|
|
1697
|
+
const { resolveLatestQuestion } = await import("./latest-question-OX56OVQ5.js");
|
|
1698
|
+
if (!current()) return null;
|
|
1699
|
+
const destination = await resolveLatestQuestion(t, {
|
|
1700
|
+
client,
|
|
1701
|
+
workspaceId,
|
|
1702
|
+
sessionId,
|
|
1703
|
+
isCurrent: current,
|
|
1704
|
+
resumeSequence: maxResumeSequence,
|
|
1705
|
+
options: questionOptions
|
|
1706
|
+
});
|
|
1707
|
+
if (!destination || !current()) return null;
|
|
1708
|
+
if (!eventWindowRef.current.events.some((event) => event.sequence === destination.anchor)) {
|
|
1709
|
+
const navigation = jumpToSequence(destination.anchor);
|
|
1710
|
+
generation = navigationGenerationRef.current;
|
|
1711
|
+
if (!await navigation || !current()) return null;
|
|
1712
|
+
}
|
|
1713
|
+
setQuestionEvidence({
|
|
1714
|
+
client,
|
|
1715
|
+
streamKey,
|
|
1716
|
+
anchor: destination.anchor,
|
|
1717
|
+
events: destination.events
|
|
1718
|
+
});
|
|
1719
|
+
return destination.questionSequence;
|
|
1720
|
+
} catch (reason) {
|
|
1721
|
+
if (!current()) return null;
|
|
1722
|
+
throw reason;
|
|
1723
|
+
}
|
|
1724
|
+
},
|
|
1725
|
+
[client, workspaceId, sessionId, streamKey, ownsNavigation, jumpToSequence, t]
|
|
1726
|
+
);
|
|
1727
|
+
const jumpToLatest = useCallback3(async () => {
|
|
1728
|
+
if (!ownsNavigation()) return;
|
|
1729
|
+
if (loadingTargetRef.current) {
|
|
1730
|
+
navigationGenerationRef.current += 1;
|
|
1731
|
+
loadingTargetRef.current = false;
|
|
1732
|
+
setLoadingTarget(false);
|
|
1733
|
+
}
|
|
1734
|
+
if (!sessionId || navigationBusy()) {
|
|
1735
|
+
return;
|
|
1736
|
+
}
|
|
1737
|
+
navigationGenerationRef.current += 1;
|
|
1738
|
+
setQuestionEvidence(null);
|
|
1739
|
+
loadingLatestRef.current = true;
|
|
1740
|
+
setLoadingLatest(true);
|
|
1741
|
+
setNewerError(null);
|
|
1742
|
+
setError(null);
|
|
1743
|
+
let published = false;
|
|
1744
|
+
try {
|
|
1745
|
+
streamAbortRef.current?.abort();
|
|
1746
|
+
hasNewerRef.current = false;
|
|
1747
|
+
hasOlderRef.current = false;
|
|
1748
|
+
newestSequenceRef.current = null;
|
|
1749
|
+
oldestSequenceRef.current = null;
|
|
1750
|
+
eventWindowRef.current = EMPTY_EVENT_WINDOW;
|
|
1751
|
+
initialWindowLoadedRef.current = false;
|
|
1752
|
+
streamResumeSequenceRef.current = after;
|
|
1753
|
+
viewModeRef.current = "live";
|
|
1754
|
+
loadingLatestRef.current = false;
|
|
1755
|
+
startTransition(() => {
|
|
1756
|
+
setHasNewer(false);
|
|
1757
|
+
setHasOlder(false);
|
|
1758
|
+
setEventWindow(EMPTY_EVENT_WINDOW);
|
|
1759
|
+
setInitialLoading(true);
|
|
1760
|
+
setViewMode("live");
|
|
1761
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
1762
|
+
setLoadingLatest(false);
|
|
1763
|
+
});
|
|
1764
|
+
published = true;
|
|
1765
|
+
await Promise.resolve();
|
|
1766
|
+
} finally {
|
|
1767
|
+
if (!published) {
|
|
1768
|
+
loadingLatestRef.current = false;
|
|
1769
|
+
setLoadingLatest(false);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}, [after, sessionId, ownsNavigation]);
|
|
1773
|
+
const identityMatches = stateStreamKey === streamKey;
|
|
1774
|
+
const freshTail = identityMatches && initialHistoryReady && viewMode === "live" && !fullReplay ? {
|
|
1775
|
+
events: eventWindow.events,
|
|
1776
|
+
hasOlder,
|
|
1777
|
+
truncated: eventWindow.truncated,
|
|
1778
|
+
bytes: eventWindow.bytes,
|
|
1779
|
+
sessionStatus: sessionStatusProjection
|
|
1780
|
+
} : null;
|
|
1781
|
+
const retainedTail = useRetainedSessionView(client, workspaceId, sessionId, "events", freshTail, {
|
|
1782
|
+
enabled: !fullReplay,
|
|
1783
|
+
bytes: (tail) => tail.bytes,
|
|
1784
|
+
budgetBytes: SESSION_EVENT_BROWSER_MAX_BYTES,
|
|
1785
|
+
discard: identityMatches && deniesRetainedView(error)
|
|
1786
|
+
});
|
|
1787
|
+
const showRetained = retainedTail !== null && !(identityMatches && initialHistoryReady);
|
|
1788
|
+
const visibleEvents = showRetained ? retainedTail.events : identityMatches ? eventWindow.events : EMPTY_EVENTS;
|
|
1789
|
+
const visibleHasOlder = showRetained ? retainedTail.hasOlder : identityMatches && hasOlder;
|
|
1790
|
+
const visibleTruncated = showRetained ? retainedTail.truncated : eventWindow.truncated;
|
|
1791
|
+
const liveSteps = identityMatches && !showRetained;
|
|
1792
|
+
const visibleStepStores = liveSteps ? stepStores : EMPTY_STEP_STORES;
|
|
1793
|
+
const visiblePresentedOutputs = liveSteps ? presentedOutputs : EMPTY_PRESENTED_OUTPUTS;
|
|
1794
|
+
const visibleTurnless = liveSteps ? turnlessSteps : EMPTY_TURNLESS_STEPS;
|
|
1795
|
+
const readToolOutput = useCallback3(
|
|
1796
|
+
async (sequence) => sessionId ? (await readEventPage(client, workspaceId, sessionId, {
|
|
1797
|
+
includeTypes: ["agent.toolCall.output"],
|
|
1798
|
+
after: Math.max(0, sequence - 1),
|
|
1799
|
+
before: sequence + 1,
|
|
1800
|
+
direction: "after",
|
|
1801
|
+
limit: 1,
|
|
1802
|
+
payloadMode: "full"
|
|
1803
|
+
})).events : [],
|
|
1804
|
+
[client, workspaceId, sessionId]
|
|
1805
|
+
);
|
|
1806
|
+
const toolOutputs = useToolOutputReads(readToolOutput, stepStores);
|
|
1807
|
+
const visibleToolOutputs = liveSteps ? toolOutputs.exact : EMPTY_PRESENTED_OUTPUTS;
|
|
1808
|
+
const loadedEvents = useMemo2(
|
|
1809
|
+
() => mergeLoadedEvents(
|
|
1810
|
+
visibleEvents,
|
|
1811
|
+
visibleStepStores,
|
|
1812
|
+
visiblePresentedOutputs,
|
|
1813
|
+
visibleTurnless.events,
|
|
1814
|
+
visibleToolOutputs
|
|
1815
|
+
),
|
|
1816
|
+
[
|
|
1817
|
+
visibleEvents,
|
|
1818
|
+
visibleStepStores,
|
|
1819
|
+
visiblePresentedOutputs,
|
|
1820
|
+
visibleTurnless,
|
|
1821
|
+
visibleToolOutputs
|
|
1822
|
+
]
|
|
1823
|
+
);
|
|
1824
|
+
const turnSteps = useMemo2(() => {
|
|
1825
|
+
if (fullReplay) return void 0;
|
|
1826
|
+
const markers = /* @__PURE__ */ new Map();
|
|
1827
|
+
const bounded = withTurnlessSteps(visibleEvents, visibleTurnless);
|
|
1828
|
+
for (const [turnId, bounds] of conversationTurnBounds(bounded)) {
|
|
1829
|
+
const store = visibleStepStores.get(turnId);
|
|
1830
|
+
if (store && !store.loading && !store.error && !stepStoreHasOlder(store, bounds)) {
|
|
1831
|
+
if (!store.events.some((event) => CONTENT_STEP_TYPES.has(event.type))) continue;
|
|
1832
|
+
}
|
|
1833
|
+
const state = store ? store.loading ? "loading" : store.error ? "error" : stepStoreHasOlder(store, bounds) ? "partial" : "complete" : bounds.hidden ? "idle" : null;
|
|
1834
|
+
if (state === null) continue;
|
|
1835
|
+
markers.set(turnId, { state, ...liveSteps ? turnStepHandle(turnId) : RETAINED_VIEW_STEPS });
|
|
1836
|
+
}
|
|
1837
|
+
return markers;
|
|
1838
|
+
}, [fullReplay, visibleEvents, visibleTurnless, visibleStepStores, liveSteps, turnStepHandle]);
|
|
1839
|
+
const timeline = useMemo2(() => {
|
|
1840
|
+
const witness = questionEvidence?.client === client && questionEvidence.streamKey === streamKey && visibleEvents.some((event) => event.sequence === questionEvidence.anchor) ? questionEvidence.events : EMPTY_EVENTS;
|
|
1841
|
+
const timelineOptions = {
|
|
1842
|
+
partialStart: visibleHasOlder || visibleTruncated || after > 0,
|
|
1843
|
+
...turnSteps ? { turnSteps } : {},
|
|
1844
|
+
...liveSteps ? { toolOutputs: toolOutputs.reader } : {}
|
|
1845
|
+
};
|
|
1846
|
+
if (witness.length === 0) return buildTimeline(t, loadedEvents, timelineOptions);
|
|
1847
|
+
const ids = new Set(loadedEvents.map((event) => event.id));
|
|
1848
|
+
return buildTimeline(
|
|
1849
|
+
t,
|
|
1850
|
+
[...loadedEvents, ...witness.filter((event) => !ids.has(event.id))],
|
|
1851
|
+
timelineOptions
|
|
1852
|
+
);
|
|
1853
|
+
}, [
|
|
1854
|
+
loadedEvents,
|
|
1855
|
+
visibleEvents,
|
|
1856
|
+
visibleHasOlder,
|
|
1857
|
+
visibleTruncated,
|
|
1858
|
+
after,
|
|
1859
|
+
questionEvidence,
|
|
1860
|
+
client,
|
|
1861
|
+
streamKey,
|
|
1862
|
+
turnSteps,
|
|
1863
|
+
liveSteps,
|
|
1864
|
+
toolOutputs.reader,
|
|
1865
|
+
t
|
|
1866
|
+
]);
|
|
1867
|
+
useEffect2(() => {
|
|
1868
|
+
if (fullReplay || !sessionId || !identityMatches || !initialHistoryReady) return;
|
|
1869
|
+
const resolved = new Set(presentedOutputs.keys());
|
|
1870
|
+
const collect = (events) => {
|
|
1871
|
+
for (const event of events) {
|
|
1872
|
+
if (event.type !== "agent.toolCall.output" || isToolOutputPreviewEvent(event)) continue;
|
|
1873
|
+
const id = event.payload?.id;
|
|
1874
|
+
if (typeof id === "string") resolved.add(id);
|
|
1875
|
+
}
|
|
1876
|
+
};
|
|
1877
|
+
collect(eventWindow.events);
|
|
1878
|
+
for (const store of stepStores.values()) collect(store.events);
|
|
1879
|
+
const requested = presentedRequestedRef.current;
|
|
1880
|
+
let inFlight = 0;
|
|
1881
|
+
for (const request of requested.values()) if (request.pending) inFlight += 1;
|
|
1882
|
+
const room = PRESENTED_OUTPUT_READS_PER_PASS - inFlight;
|
|
1883
|
+
if (room <= 0) return;
|
|
1884
|
+
const missing = [];
|
|
1885
|
+
for (const event of eventWindow.events) {
|
|
1886
|
+
if (event.type !== "agent.toolCall.created") continue;
|
|
1887
|
+
const payload = event.payload;
|
|
1888
|
+
const callId = payload?.id;
|
|
1889
|
+
if (typeof callId !== "string" || resolved.has(callId) || requested.has(callId) || !isPresentedSessionToolCall(payload)) {
|
|
1890
|
+
continue;
|
|
1891
|
+
}
|
|
1892
|
+
missing.push({ callId, sequence: event.sequence, turnId: event.turnId ?? null });
|
|
1893
|
+
}
|
|
1894
|
+
if (missing.length === 0) return;
|
|
1895
|
+
const bounds = conversationTurnBounds(eventWindow.events);
|
|
1896
|
+
for (const { callId, sequence, turnId } of missing.slice(-room)) {
|
|
1897
|
+
const request = { turnId, pending: true };
|
|
1898
|
+
requested.set(callId, request);
|
|
1899
|
+
const isCurrent = () => presentedRequestedRef.current === requested && requested.get(callId) === request;
|
|
1900
|
+
const terminal = turnId ? bounds.get(turnId)?.terminal ?? null : null;
|
|
1901
|
+
void findPresentedOutput(client, workspaceId, sessionId, callId, sequence, terminal).then(
|
|
1902
|
+
(output) => {
|
|
1903
|
+
if (!ownsNavigation() || !isCurrent()) return;
|
|
1904
|
+
request.pending = false;
|
|
1905
|
+
if (output) setPresentedOutputs((current) => new Map(current).set(callId, output));
|
|
1906
|
+
setPresentedPass((pass) => pass + 1);
|
|
1907
|
+
},
|
|
1908
|
+
() => {
|
|
1909
|
+
if (!isCurrent()) return;
|
|
1910
|
+
requested.delete(callId);
|
|
1911
|
+
const failures = (presentedFailuresRef.current.get(callId) ?? 0) + 1;
|
|
1912
|
+
presentedFailuresRef.current.set(callId, failures);
|
|
1913
|
+
if (failures > PRESENTED_OUTPUT_RETRIES) return;
|
|
1914
|
+
setTimeout(() => {
|
|
1915
|
+
if (ownsNavigation()) setPresentedPass((pass) => pass + 1);
|
|
1916
|
+
}, PRESENTED_OUTPUT_RETRY_MS * failures);
|
|
1917
|
+
}
|
|
1918
|
+
);
|
|
1919
|
+
}
|
|
1920
|
+
}, [
|
|
1921
|
+
fullReplay,
|
|
1922
|
+
sessionId,
|
|
1923
|
+
identityMatches,
|
|
1924
|
+
initialHistoryReady,
|
|
1925
|
+
eventWindow,
|
|
1926
|
+
stepStores,
|
|
1927
|
+
presentedOutputs,
|
|
1928
|
+
presentedPass,
|
|
1929
|
+
client,
|
|
1930
|
+
workspaceId,
|
|
1931
|
+
ownsNavigation
|
|
1932
|
+
]);
|
|
1933
|
+
return {
|
|
1934
|
+
events: loadedEvents,
|
|
1935
|
+
timeline,
|
|
1936
|
+
sessionStatus: showRetained ? retainedTail.sessionStatus : identityMatches ? sessionStatusProjection : null,
|
|
1937
|
+
sessionStatusSequence: identityMatches ? sessionStatusRef.current.sequence : 0,
|
|
1938
|
+
connectionState: identityMatches ? connectionState : "idle",
|
|
1939
|
+
lastSequence: identityMatches ? lastSequenceRef.current : after,
|
|
1940
|
+
windowBytes: identityMatches ? eventWindow.bytes : 2,
|
|
1941
|
+
windowTruncated: identityMatches ? eventWindow.truncated : false,
|
|
1942
|
+
initialLoading: fullReplay || showRetained ? false : identityMatches ? initialLoading : true,
|
|
1943
|
+
initialHistoryReady: identityMatches && initialHistoryReady,
|
|
1944
|
+
hasOlder: visibleHasOlder,
|
|
1945
|
+
loadingOlder: !identityMatches ? false : loadingOlder,
|
|
1946
|
+
loadOlder,
|
|
1947
|
+
hasNewer: !identityMatches ? false : hasNewer,
|
|
1948
|
+
loadingNewer: !identityMatches ? false : loadingNewer,
|
|
1949
|
+
loadNewer,
|
|
1950
|
+
loadingOldest: !identityMatches ? false : loadingOldest,
|
|
1951
|
+
loadOldest,
|
|
1952
|
+
loadingLatest: !identityMatches ? false : loadingLatest,
|
|
1953
|
+
jumpToLatest,
|
|
1954
|
+
jumpToLatestQuestion,
|
|
1955
|
+
jumpToSequence,
|
|
1956
|
+
loadingTarget: identityMatches && loadingTarget,
|
|
1957
|
+
error: identityMatches ? newerError ?? error : null
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
function boundBrowserSessionEventWindow(events, options = {}) {
|
|
1961
|
+
const maxBytes = Math.max(1024, options.maxBytes ?? SESSION_EVENT_BROWSER_MAX_BYTES);
|
|
1962
|
+
const maxCount = Math.max(1, Math.floor(options.maxCount ?? SESSION_EVENT_BROWSER_MAX_COUNT));
|
|
1963
|
+
const safe = events;
|
|
1964
|
+
const selected = [];
|
|
1965
|
+
let bytes = 2;
|
|
1966
|
+
const direction = options.direction ?? "newest";
|
|
1967
|
+
const start = direction === "newest" ? safe.length - 1 : 0;
|
|
1968
|
+
const end = direction === "newest" ? Math.max(-1, safe.length - maxCount - 1) : safe.length;
|
|
1969
|
+
const step = direction === "newest" ? -1 : 1;
|
|
1970
|
+
for (let index = start; index !== end && selected.length < maxCount; index += step) {
|
|
1971
|
+
const event = safe[index];
|
|
1972
|
+
const eventBytes = browserJsonBytes(event);
|
|
1973
|
+
const separator = selected.length === 0 ? 0 : 1;
|
|
1974
|
+
if (selected.length > 0 && bytes + separator + eventBytes > maxBytes) break;
|
|
1975
|
+
selected.push(event);
|
|
1976
|
+
bytes += separator + eventBytes;
|
|
1977
|
+
}
|
|
1978
|
+
if (direction === "newest") selected.reverse();
|
|
1979
|
+
return {
|
|
1980
|
+
events: selected,
|
|
1981
|
+
bytes,
|
|
1982
|
+
truncated: selected.length < safe.length
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
function appendBrowserSessionEventWindow(current, batch, options = {}) {
|
|
1986
|
+
const maxBytes = Math.max(1024, options.maxBytes ?? SESSION_EVENT_BROWSER_MAX_BYTES);
|
|
1987
|
+
const maxCount = Math.max(1, Math.floor(options.maxCount ?? SESSION_EVENT_BROWSER_MAX_COUNT));
|
|
1988
|
+
const events = [...current.events, ...batch];
|
|
1989
|
+
let bytes = current.bytes;
|
|
1990
|
+
for (let index = 0; index < batch.length; index += 1) {
|
|
1991
|
+
bytes += browserJsonBytes(batch[index]) + (current.events.length + index > 0 ? 1 : 0);
|
|
1992
|
+
}
|
|
1993
|
+
let start = 0;
|
|
1994
|
+
while (events.length - start > 1 && (bytes > maxBytes || events.length - start > maxCount)) {
|
|
1995
|
+
bytes -= browserJsonBytes(events[start]) + 1;
|
|
1996
|
+
start += 1;
|
|
1997
|
+
}
|
|
1998
|
+
return {
|
|
1999
|
+
events: start === 0 ? events : events.slice(start),
|
|
2000
|
+
bytes,
|
|
2001
|
+
truncated: current.truncated || start > 0
|
|
2002
|
+
};
|
|
2003
|
+
}
|
|
2004
|
+
function browserJsonBytes(value) {
|
|
2005
|
+
return encoder.encode(JSON.stringify(value)).byteLength;
|
|
2006
|
+
}
|
|
2007
|
+
function observeSessionStatus(events, ref) {
|
|
2008
|
+
let latest = null;
|
|
2009
|
+
for (const event of events) {
|
|
2010
|
+
const status = sessionStatusFromEvents([event]);
|
|
2011
|
+
if (status && (!latest || event.sequence >= latest.sequence)) {
|
|
2012
|
+
latest = { sequence: event.sequence, status };
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
if (!latest || latest.sequence < ref.current.sequence) return void 0;
|
|
2016
|
+
ref.current = latest;
|
|
2017
|
+
return latest.status;
|
|
2018
|
+
}
|
|
2019
|
+
async function loadConversationCatchup(client, workspaceId, sessionId, cursor, head, options) {
|
|
2020
|
+
const events = [];
|
|
2021
|
+
let after = cursor;
|
|
2022
|
+
for (let fetch = 0; fetch < CATCHUP_MAX_PAGES; fetch += 1) {
|
|
2023
|
+
if (options.signal?.aborted) throw abortError();
|
|
2024
|
+
const page = await readConversationPage(client, workspaceId, sessionId, {
|
|
2025
|
+
after,
|
|
2026
|
+
before: head + 1,
|
|
2027
|
+
direction: "after",
|
|
2028
|
+
limit: SESSION_HISTORY_PAGE_SIZE,
|
|
2029
|
+
compact: true,
|
|
2030
|
+
payloadMode: "full"
|
|
2031
|
+
});
|
|
2032
|
+
if (!options.isCurrent()) throw abortError();
|
|
2033
|
+
options.progress?.();
|
|
2034
|
+
const fresh = page.filter((event) => eventResumeSequence(event) > after);
|
|
2035
|
+
assertAscending(fresh);
|
|
2036
|
+
events.push(...fresh);
|
|
2037
|
+
if (page.raw.newest === null || page.raw.newest <= after) return events;
|
|
2038
|
+
after = page.raw.newest;
|
|
2039
|
+
if (after >= head || page.hasMore === false) return events;
|
|
2040
|
+
}
|
|
2041
|
+
return null;
|
|
2042
|
+
}
|
|
2043
|
+
async function loadEventWindow(t, client, workspaceId, sessionId, options) {
|
|
2044
|
+
let cursor = options.before;
|
|
2045
|
+
let buffer = [];
|
|
2046
|
+
let reachedStart = false;
|
|
2047
|
+
let fetches = 0;
|
|
2048
|
+
let filteredPages = 0;
|
|
2049
|
+
let rawNewest = 0;
|
|
2050
|
+
const counts = (page) => {
|
|
2051
|
+
rawNewest = Math.max(rawNewest, page.raw.newest ?? 0);
|
|
2052
|
+
return page.length > 0 || (filteredPages += 1) > (options.filteredPageCap ?? 0);
|
|
2053
|
+
};
|
|
2054
|
+
while (fetches < options.maxFetches) {
|
|
2055
|
+
if (buffer.length > 0 && groupCount(t, buffer) >= options.targetGroups) {
|
|
2056
|
+
break;
|
|
2057
|
+
}
|
|
2058
|
+
const page = await loadPreviousPage(client, workspaceId, sessionId, cursor, {
|
|
2059
|
+
pageSize: options.pageSize,
|
|
2060
|
+
filter: options.filter,
|
|
2061
|
+
...options.signal ? { signal: options.signal } : {}
|
|
2062
|
+
});
|
|
2063
|
+
if (options.isCurrent?.() === false) throw abortError();
|
|
2064
|
+
options.progress?.();
|
|
2065
|
+
if (page.raw.oldest === null) {
|
|
2066
|
+
reachedStart = true;
|
|
2067
|
+
break;
|
|
2068
|
+
}
|
|
2069
|
+
if (counts(page)) fetches += 1;
|
|
2070
|
+
assertAscending(page);
|
|
2071
|
+
buffer = [...page, ...buffer];
|
|
2072
|
+
cursor = page.raw.oldest;
|
|
2073
|
+
if (page.raw.oldest <= 1 || page[0] !== void 0 && isLogStart(page[0])) {
|
|
2074
|
+
reachedStart = true;
|
|
2075
|
+
break;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
let snapPages = 0;
|
|
2079
|
+
while (!reachedStart && findBoundaryIndex(buffer) === -1 && snapPages < (options.boundaryPageCap ?? 0)) {
|
|
2080
|
+
const page = await loadPreviousPage(client, workspaceId, sessionId, cursor, {
|
|
2081
|
+
pageSize: options.pageSize,
|
|
2082
|
+
filter: options.filter,
|
|
2083
|
+
...options.signal ? { signal: options.signal } : {}
|
|
2084
|
+
});
|
|
2085
|
+
if (options.isCurrent?.() === false) throw abortError();
|
|
2086
|
+
options.progress?.();
|
|
2087
|
+
if (page.raw.oldest === null) {
|
|
2088
|
+
reachedStart = true;
|
|
2089
|
+
break;
|
|
2090
|
+
}
|
|
2091
|
+
if (counts(page)) {
|
|
2092
|
+
fetches += 1;
|
|
2093
|
+
snapPages += 1;
|
|
2094
|
+
}
|
|
2095
|
+
assertAscending(page);
|
|
2096
|
+
buffer = [...page, ...buffer];
|
|
2097
|
+
cursor = page.raw.oldest;
|
|
2098
|
+
if (page.raw.oldest <= 1 || page[0] !== void 0 && isLogStart(page[0])) {
|
|
2099
|
+
reachedStart = true;
|
|
2100
|
+
break;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
if (!reachedStart) {
|
|
2104
|
+
const boundary = findBoundaryIndex(buffer);
|
|
2105
|
+
if (boundary > 0) {
|
|
2106
|
+
buffer = buffer.slice(boundary);
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
const densityWindow = trimBackwardToGroupTarget(t, buffer, options.targetGroups);
|
|
2110
|
+
buffer = densityWindow.events;
|
|
2111
|
+
const oldest = buffer[0] ?? null;
|
|
2112
|
+
const newest = buffer[buffer.length - 1] ?? null;
|
|
2113
|
+
return {
|
|
2114
|
+
events: buffer,
|
|
2115
|
+
oldestSequence: oldest?.sequence ?? null,
|
|
2116
|
+
// A window that kept nothing resumes a live stream above the rows it read,
|
|
2117
|
+
// not at the start of history. Otherwise rows dropped above its newest
|
|
2118
|
+
// kept row are replayed (or caught up) into their turn's steps, so a
|
|
2119
|
+
// working turn's latest tool calls still show.
|
|
2120
|
+
newestSequence: newest ? maxResumeSequence(buffer) : rawNewest,
|
|
2121
|
+
// An empty window that stopped before the start is still pageable.
|
|
2122
|
+
hasOlder: buffer.length > 0 ? densityWindow.trimmed || !reachedStart && oldest?.type !== "session.created" : !reachedStart,
|
|
2123
|
+
scanCursor: cursor
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
function findBoundaryIndex(events) {
|
|
2127
|
+
for (let index = 0; index < events.length; index += 1) {
|
|
2128
|
+
if (isTurnBoundary(events[index])) {
|
|
2129
|
+
return index;
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
return -1;
|
|
2133
|
+
}
|
|
2134
|
+
function isTurnBoundary(event) {
|
|
2135
|
+
return event.type === "session.created" || event.type === "user.message";
|
|
2136
|
+
}
|
|
2137
|
+
async function loadForwardEventWindow(t, client, workspaceId, sessionId, options) {
|
|
2138
|
+
let cursor = options.after;
|
|
2139
|
+
let buffer = [];
|
|
2140
|
+
let fetches = 0;
|
|
2141
|
+
let reachedEnd = false;
|
|
2142
|
+
let filteredPages = 0;
|
|
2143
|
+
while (fetches < options.maxFetches) {
|
|
2144
|
+
if (buffer.length > 0 && groupCount(t, buffer) >= options.targetGroups) {
|
|
2145
|
+
break;
|
|
2146
|
+
}
|
|
2147
|
+
const page = await loadNextPage(client, workspaceId, sessionId, cursor, {
|
|
2148
|
+
pageSize: options.pageSize,
|
|
2149
|
+
filter: options.filter,
|
|
2150
|
+
...options.signal ? { signal: options.signal } : {}
|
|
2151
|
+
});
|
|
2152
|
+
if (options.isCurrent?.() === false) throw abortError();
|
|
2153
|
+
if (page.raw.newest === null) {
|
|
2154
|
+
reachedEnd = true;
|
|
2155
|
+
break;
|
|
2156
|
+
}
|
|
2157
|
+
if (page.length > 0 || (filteredPages += 1) > (options.filteredPageCap ?? 0)) fetches += 1;
|
|
2158
|
+
assertAscending(page);
|
|
2159
|
+
buffer = [...buffer, ...page];
|
|
2160
|
+
cursor = page.raw.newest;
|
|
2161
|
+
}
|
|
2162
|
+
const densityWindow = trimForwardToGroupTarget(t, buffer, options.targetGroups);
|
|
2163
|
+
buffer = densityWindow.events;
|
|
2164
|
+
const oldest = buffer[0] ?? null;
|
|
2165
|
+
const newest = buffer[buffer.length - 1] ?? null;
|
|
2166
|
+
return {
|
|
2167
|
+
events: buffer,
|
|
2168
|
+
oldestSequence: oldest?.sequence ?? null,
|
|
2169
|
+
newestSequence: newest ? maxResumeSequence(buffer) : 0,
|
|
2170
|
+
// An empty window that stopped before the end is still pageable.
|
|
2171
|
+
hasNewer: buffer.length > 0 ? densityWindow.trimmed || !reachedEnd : !reachedEnd,
|
|
2172
|
+
scanCursor: cursor
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
function trimBackwardToGroupTarget(t, events, targetGroups) {
|
|
2176
|
+
if (!Number.isFinite(targetGroups) || targetGroups <= 0 || events.length === 0) {
|
|
2177
|
+
return { events, trimmed: false };
|
|
2178
|
+
}
|
|
2179
|
+
for (let index = events.length - 1; index > 0; index -= 1) {
|
|
2180
|
+
if (!isTurnBoundary(events[index])) continue;
|
|
2181
|
+
const candidate = events.slice(index);
|
|
2182
|
+
if (groupCount(t, candidate) >= targetGroups) {
|
|
2183
|
+
return { events: candidate, trimmed: true };
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
return { events, trimmed: false };
|
|
2187
|
+
}
|
|
2188
|
+
function trimForwardToGroupTarget(t, events, targetGroups) {
|
|
2189
|
+
if (!Number.isFinite(targetGroups) || targetGroups <= 0 || events.length === 0) {
|
|
2190
|
+
return { events, trimmed: false };
|
|
2191
|
+
}
|
|
2192
|
+
for (let index = 1; index < events.length; index += 1) {
|
|
2193
|
+
if (!isTurnBoundary(events[index])) continue;
|
|
2194
|
+
const candidate = events.slice(0, index);
|
|
2195
|
+
if (groupCount(t, candidate) >= targetGroups) {
|
|
2196
|
+
return { events: candidate, trimmed: true };
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
return { events, trimmed: false };
|
|
2200
|
+
}
|
|
2201
|
+
function rawPageExtent(events) {
|
|
2202
|
+
return events.length === 0 ? { oldest: null, newest: null } : { oldest: events[0].sequence, newest: maxResumeSequence(events) };
|
|
2203
|
+
}
|
|
2204
|
+
function historyPage(events, requested, raw = rawPageExtent(events)) {
|
|
2205
|
+
return Object.assign(events, { requested, raw });
|
|
2206
|
+
}
|
|
2207
|
+
async function loadPreviousPage(client, workspaceId, sessionId, before, options) {
|
|
2208
|
+
if (options.signal?.aborted) {
|
|
2209
|
+
throw abortError();
|
|
2210
|
+
}
|
|
2211
|
+
const requested = options.pageSize;
|
|
2212
|
+
if (requested === 0) {
|
|
2213
|
+
return historyPage([], requested);
|
|
2214
|
+
}
|
|
2215
|
+
const read = {
|
|
2216
|
+
before,
|
|
2217
|
+
limit: requested,
|
|
2218
|
+
compact: true,
|
|
2219
|
+
// Monitoring summaries omit correlation fields once a bounded durable
|
|
2220
|
+
// payload exceeds their preview threshold. The browser timeline needs the
|
|
2221
|
+
// exact stored payload so persisted tool outputs still match their
|
|
2222
|
+
// calls and expose truthful truncation telemetry after a reload.
|
|
2223
|
+
payloadMode: "full"
|
|
2224
|
+
};
|
|
2225
|
+
const page = options.filter ? await readConversationPage(client, workspaceId, sessionId, read) : historyPage(await client.listEvents(workspaceId, sessionId, read), requested);
|
|
2226
|
+
if (options.signal?.aborted) {
|
|
2227
|
+
throw abortError();
|
|
2228
|
+
}
|
|
2229
|
+
return Object.assign(page, { requested });
|
|
2230
|
+
}
|
|
2231
|
+
async function loadNextPage(client, workspaceId, sessionId, after, options) {
|
|
2232
|
+
if (options.signal?.aborted) {
|
|
2233
|
+
throw abortError();
|
|
2234
|
+
}
|
|
2235
|
+
const requested = options.pageSize;
|
|
2236
|
+
if (requested === 0) {
|
|
2237
|
+
return historyPage([], requested);
|
|
2238
|
+
}
|
|
2239
|
+
const read = {
|
|
2240
|
+
...options.before !== void 0 ? { before: options.before } : {},
|
|
2241
|
+
after,
|
|
2242
|
+
limit: requested,
|
|
2243
|
+
compact: true,
|
|
2244
|
+
direction: "after",
|
|
2245
|
+
payloadMode: "full"
|
|
2246
|
+
};
|
|
2247
|
+
const page = options.filter ? await readConversationPage(client, workspaceId, sessionId, read) : historyPage(await client.listEvents(workspaceId, sessionId, read), requested);
|
|
2248
|
+
if (options.signal?.aborted) {
|
|
2249
|
+
throw abortError();
|
|
2250
|
+
}
|
|
2251
|
+
return Object.assign(page, { requested });
|
|
2252
|
+
}
|
|
2253
|
+
function groupCount(t, events) {
|
|
2254
|
+
if (events.length === 0) {
|
|
2255
|
+
return 0;
|
|
2256
|
+
}
|
|
2257
|
+
return groupTimeline(t, buildTimeline(t, events)).length;
|
|
2258
|
+
}
|
|
2259
|
+
function isLogStart(event) {
|
|
2260
|
+
return event.type === "session.created" || event.sequence <= 1;
|
|
2261
|
+
}
|
|
2262
|
+
function maxResumeSequence(events) {
|
|
2263
|
+
return events.reduce((max, event) => Math.max(max, eventResumeSequence(event)), 0);
|
|
2264
|
+
}
|
|
2265
|
+
function maxResumeSequenceOrNull(events) {
|
|
2266
|
+
return events.length > 0 ? maxResumeSequence(events) : null;
|
|
2267
|
+
}
|
|
2268
|
+
function eventResumeSequence(event) {
|
|
2269
|
+
const streamedCoverage = sessionEventStreamCoveredThrough(event);
|
|
2270
|
+
if (streamedCoverage !== null) return streamedCoverage;
|
|
2271
|
+
return typeof event.coveredThrough === "number" && Number.isSafeInteger(event.coveredThrough) && event.coveredThrough >= event.sequence ? event.coveredThrough : event.sequence;
|
|
2272
|
+
}
|
|
2273
|
+
function assertAscending(events) {
|
|
2274
|
+
for (let index = 1; index < events.length; index += 1) {
|
|
2275
|
+
if (events[index - 1].sequence >= events[index].sequence) {
|
|
2276
|
+
throw new Error("@opengeni/react: session events must be ordered by ascending sequence");
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
function assertPrependOrder(existing, older) {
|
|
2281
|
+
if (!shouldAssertDevelopment() || existing.length === 0 || older.length === 0) {
|
|
2282
|
+
return;
|
|
2283
|
+
}
|
|
2284
|
+
if (older[older.length - 1].sequence >= existing[0].sequence) {
|
|
2285
|
+
throw new Error("@opengeni/react: loadOlder returned overlapping session events");
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
function assertAppendOrder(existing, newer) {
|
|
2289
|
+
if (!shouldAssertDevelopment() || existing.length === 0 || newer.length === 0) {
|
|
2290
|
+
return;
|
|
2291
|
+
}
|
|
2292
|
+
if (newer[0].sequence <= existing[existing.length - 1].sequence) {
|
|
2293
|
+
throw new Error("@opengeni/react: loadNewer returned overlapping session events");
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
function shouldAssertDevelopment() {
|
|
2297
|
+
const processEnv = globalThis.process?.env;
|
|
2298
|
+
return processEnv !== void 0 && processEnv.NODE_ENV !== "production";
|
|
2299
|
+
}
|
|
2300
|
+
function abortError() {
|
|
2301
|
+
const error = new Error("The operation was aborted.");
|
|
2302
|
+
error.name = "AbortError";
|
|
2303
|
+
return error;
|
|
2304
|
+
}
|
|
2305
|
+
function abortableDelay(ms, signal) {
|
|
2306
|
+
return new Promise((resolve, reject) => {
|
|
2307
|
+
if (signal.aborted) {
|
|
2308
|
+
reject(abortError());
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
const onAbort = () => {
|
|
2312
|
+
clearTimeout(timer);
|
|
2313
|
+
reject(abortError());
|
|
2314
|
+
};
|
|
2315
|
+
const timer = setTimeout(() => {
|
|
2316
|
+
signal.removeEventListener("abort", onAbort);
|
|
2317
|
+
resolve();
|
|
2318
|
+
}, ms);
|
|
2319
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2320
|
+
});
|
|
2321
|
+
}
|
|
2322
|
+
var CONTENT_STEP_TYPES = new Set(SESSION_EVENT_CONTENT_STEP_TYPES);
|
|
2323
|
+
var COMPAT_EXCLUDED_TYPES = new Set(
|
|
2324
|
+
SESSION_EVENT_COMPAT_EXCLUDED_STEP_TYPES
|
|
2325
|
+
);
|
|
2326
|
+
var CONVERSATION_FILTER = "conversation";
|
|
2327
|
+
var SESSION_TURN_STEP_PAGE_SIZE = 200;
|
|
2328
|
+
var SESSION_TURN_LIVE_STEP_TAIL = 200;
|
|
2329
|
+
var SESSION_TURN_LIVE_STEP_TAIL_BYTES = 2 * 1024 * 1024;
|
|
2330
|
+
var TURNLESS_STEP_TAIL = 1e3;
|
|
2331
|
+
var TURNLESS_STEP_TAIL_BYTES = 4 * 1024 * 1024;
|
|
2332
|
+
var TURN_STEP_READ_TYPES = SESSION_EVENT_STEP_TYPES.filter(
|
|
2333
|
+
(type) => type !== "terminal.pty.output.delta"
|
|
2334
|
+
);
|
|
2335
|
+
var SESSION_STEP_BROWSER_MAX_BYTES = 16 * 1024 * 1024;
|
|
2336
|
+
var TERMINAL_TURN_TYPES = /* @__PURE__ */ new Set([
|
|
2337
|
+
"turn.completed",
|
|
2338
|
+
"turn.failed",
|
|
2339
|
+
"turn.cancelled",
|
|
2340
|
+
"turn.superseded"
|
|
2341
|
+
]);
|
|
2342
|
+
var EMPTY_STEP_STORES = /* @__PURE__ */ new Map();
|
|
2343
|
+
var EMPTY_TURNLESS_STEPS = { events: [], bytes: 2, ranges: [], appended: 0 };
|
|
2344
|
+
var TURNLESS_STEP_MAX_RANGES = 4096;
|
|
2345
|
+
function appendTurnlessSteps(current, incoming) {
|
|
2346
|
+
const ranges = current.ranges.map((range) => [range[0], range[1]]);
|
|
2347
|
+
for (const event of incoming) {
|
|
2348
|
+
const sequence = event.sequence;
|
|
2349
|
+
const last = ranges.at(-1);
|
|
2350
|
+
if (last && sequence >= last[0] && sequence <= last[1]) continue;
|
|
2351
|
+
if (last && sequence === last[1] + 1) last[1] = sequence;
|
|
2352
|
+
else if (!last || sequence > last[1]) ranges.push([sequence, sequence]);
|
|
2353
|
+
else if (!ranges.some(([first, end]) => sequence >= first && sequence <= end)) {
|
|
2354
|
+
ranges.push([sequence, sequence]);
|
|
2355
|
+
ranges.sort((left, right) => left[0] - right[0]);
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
if (ranges.length > TURNLESS_STEP_MAX_RANGES) {
|
|
2359
|
+
ranges.splice(0, ranges.length - TURNLESS_STEP_MAX_RANGES);
|
|
2360
|
+
}
|
|
2361
|
+
const next = appendStepEvents(current, incoming);
|
|
2362
|
+
const appended = current.appended + incoming.length;
|
|
2363
|
+
if (next.events.length <= TURNLESS_STEP_TAIL && next.bytes <= TURNLESS_STEP_TAIL_BYTES) {
|
|
2364
|
+
return { ...next, ranges, appended };
|
|
2365
|
+
}
|
|
2366
|
+
const kept = boundBrowserSessionEventWindow(next.events, {
|
|
2367
|
+
maxCount: TURNLESS_STEP_TAIL,
|
|
2368
|
+
maxBytes: TURNLESS_STEP_TAIL_BYTES
|
|
2369
|
+
});
|
|
2370
|
+
return { events: kept.events, bytes: kept.bytes, ranges, appended };
|
|
2371
|
+
}
|
|
2372
|
+
function withTurnlessSteps(window2, turnless) {
|
|
2373
|
+
const first = window2[0]?.sequence;
|
|
2374
|
+
if (first === void 0 || turnless.ranges.length === 0) return window2;
|
|
2375
|
+
const placeholders = [];
|
|
2376
|
+
for (const [start, end] of turnless.ranges) {
|
|
2377
|
+
if (end < first) continue;
|
|
2378
|
+
placeholders.push({
|
|
2379
|
+
id: `turnless-steps-${start}`,
|
|
2380
|
+
workspaceId: window2[0].workspaceId,
|
|
2381
|
+
sessionId: window2[0].sessionId,
|
|
2382
|
+
sequence: start,
|
|
2383
|
+
coveredThrough: end,
|
|
2384
|
+
type: "terminal.pty.output.delta",
|
|
2385
|
+
payload: null,
|
|
2386
|
+
occurredAt: window2[0].occurredAt,
|
|
2387
|
+
turnId: null
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
if (placeholders.length === 0) return window2;
|
|
2391
|
+
return [...window2, ...placeholders].sort((left, right) => left.sequence - right.sequence);
|
|
2392
|
+
}
|
|
2393
|
+
function turnStepLowerBound(bounds) {
|
|
2394
|
+
return bounds.started !== null ? bounds.started - 1 : bounds.previous;
|
|
2395
|
+
}
|
|
2396
|
+
function stepStoreHasOlder(store, bounds) {
|
|
2397
|
+
return store.hasOlder || store.floor !== null && turnStepLowerBound(bounds) < store.floor;
|
|
2398
|
+
}
|
|
2399
|
+
function isConversationHistoryEvent(event, presentedCallIds, startupTurns) {
|
|
2400
|
+
if (event.turnId) {
|
|
2401
|
+
if (event.type === "agent.model.usage") return startupTurns === void 0;
|
|
2402
|
+
if (event.type === "agent.model.request") {
|
|
2403
|
+
return startupTurns === void 0 || startupTurns.has(event.turnId);
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
if (!CONTENT_STEP_TYPES.has(event.type)) return true;
|
|
2407
|
+
const payload = event.payload;
|
|
2408
|
+
if (event.type === "agent.toolCall.created") return isPresentedSessionToolCall(payload);
|
|
2409
|
+
if (event.type === "agent.toolCall.output") {
|
|
2410
|
+
return typeof payload?.id === "string" && presentedCallIds.has(payload.id);
|
|
2411
|
+
}
|
|
2412
|
+
return false;
|
|
2413
|
+
}
|
|
2414
|
+
function emptyStartupTurns() {
|
|
2415
|
+
return { window: null, open: /* @__PURE__ */ new Set(), responded: /* @__PURE__ */ new Set() };
|
|
2416
|
+
}
|
|
2417
|
+
function noteStartupTurns(events, state) {
|
|
2418
|
+
for (const event of events) {
|
|
2419
|
+
const turnId = event.turnId;
|
|
2420
|
+
if (!turnId) continue;
|
|
2421
|
+
if (event.type === "turn.started") {
|
|
2422
|
+
if (!state.responded.has(turnId)) state.open.add(turnId);
|
|
2423
|
+
} else if (event.type === "agent.model.request" && isSessionModelResponsePhase(event.payload?.phase)) {
|
|
2424
|
+
state.open.delete(turnId);
|
|
2425
|
+
state.responded.add(turnId);
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
var conversationFilterModes = /* @__PURE__ */ new WeakMap();
|
|
2430
|
+
var CONVERSATION_READS = {
|
|
2431
|
+
v2: { excludeTypes: [...SESSION_EVENT_STEP_TYPES], presentedToolCalls: "include" },
|
|
2432
|
+
compat: {
|
|
2433
|
+
excludeTypes: [...SESSION_EVENT_COMPAT_EXCLUDED_STEP_TYPES],
|
|
2434
|
+
presentedToolCalls: "include"
|
|
2435
|
+
}
|
|
2436
|
+
};
|
|
2437
|
+
function conversationFilterMode(client) {
|
|
2438
|
+
if (typeof client.listEventPage !== "function") {
|
|
2439
|
+
return "compat";
|
|
2440
|
+
}
|
|
2441
|
+
return conversationFilterModes.get(client) ?? "v2";
|
|
2442
|
+
}
|
|
2443
|
+
async function readConversationPage(client, workspaceId, sessionId, options) {
|
|
2444
|
+
let mode = conversationFilterMode(client);
|
|
2445
|
+
for (; ; ) {
|
|
2446
|
+
const read = await readEventPage(client, workspaceId, sessionId, {
|
|
2447
|
+
...CONVERSATION_READS[mode],
|
|
2448
|
+
...options
|
|
2449
|
+
});
|
|
2450
|
+
const announced = (read.conversationFilter ?? 0) >= SESSION_EVENT_CONVERSATION_FILTER_VERSION;
|
|
2451
|
+
if (read.conversationFilter !== void 0) {
|
|
2452
|
+
conversationFilterModes.set(client, announced ? "v2" : "compat");
|
|
2453
|
+
}
|
|
2454
|
+
if (mode === "v2" && !announced) {
|
|
2455
|
+
mode = "compat";
|
|
2456
|
+
continue;
|
|
2457
|
+
}
|
|
2458
|
+
const events = mode === "compat" ? read.events.filter(isCompatConversationEvent) : read.events;
|
|
2459
|
+
return Object.assign(events === read.events ? [...events] : events, {
|
|
2460
|
+
requested: options.limit,
|
|
2461
|
+
raw: rawPageExtent(read.events),
|
|
2462
|
+
hasMore: read.hasMore
|
|
2463
|
+
});
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
function isCompatConversationEvent(event) {
|
|
2467
|
+
if (COMPAT_EXCLUDED_TYPES.has(event.type)) return false;
|
|
2468
|
+
if (event.type !== "agent.toolCall.created") return true;
|
|
2469
|
+
return isPresentedSessionToolCall(event.payload);
|
|
2470
|
+
}
|
|
2471
|
+
function notePresentedCalls(events, ids) {
|
|
2472
|
+
for (const event of events) {
|
|
2473
|
+
if (event.type !== "agent.toolCall.created") continue;
|
|
2474
|
+
const payload = event.payload;
|
|
2475
|
+
if (typeof payload?.id === "string" && isPresentedSessionToolCall(payload)) {
|
|
2476
|
+
ids.add(payload.id);
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
function conversationTurnBounds(events) {
|
|
2481
|
+
const bounds = /* @__PURE__ */ new Map();
|
|
2482
|
+
const gaps = [];
|
|
2483
|
+
let expected = null;
|
|
2484
|
+
let previous = 0;
|
|
2485
|
+
for (const event of events) {
|
|
2486
|
+
if (expected !== null && event.sequence > expected) gaps.push(expected);
|
|
2487
|
+
const resume = eventResumeSequence(event);
|
|
2488
|
+
expected = Math.max(expected ?? 0, resume + 1);
|
|
2489
|
+
const turnId = event.turnId;
|
|
2490
|
+
if (!turnId) continue;
|
|
2491
|
+
const current = bounds.get(turnId);
|
|
2492
|
+
if (!current) {
|
|
2493
|
+
bounds.set(turnId, {
|
|
2494
|
+
first: event.sequence,
|
|
2495
|
+
started: event.type === "turn.started" ? event.sequence : null,
|
|
2496
|
+
previous,
|
|
2497
|
+
last: resume,
|
|
2498
|
+
terminal: TERMINAL_TURN_TYPES.has(event.type) ? event.sequence : null,
|
|
2499
|
+
hidden: false
|
|
2500
|
+
});
|
|
2501
|
+
} else {
|
|
2502
|
+
current.last = Math.max(current.last, resume);
|
|
2503
|
+
if (event.type === "turn.started" && current.started === null) {
|
|
2504
|
+
current.started = event.sequence;
|
|
2505
|
+
}
|
|
2506
|
+
if (TERMINAL_TURN_TYPES.has(event.type)) current.terminal = event.sequence;
|
|
2507
|
+
}
|
|
2508
|
+
if (!PRE_START_TURN_TYPES.has(event.type)) previous = resume;
|
|
2509
|
+
}
|
|
2510
|
+
for (const turn of bounds.values()) {
|
|
2511
|
+
let low = 0;
|
|
2512
|
+
let high = gaps.length;
|
|
2513
|
+
while (low < high) {
|
|
2514
|
+
const middle = low + high >> 1;
|
|
2515
|
+
if (gaps[middle] <= turn.first) low = middle + 1;
|
|
2516
|
+
else high = middle;
|
|
2517
|
+
}
|
|
2518
|
+
turn.hidden = low < gaps.length && gaps[low] < turn.last;
|
|
2519
|
+
}
|
|
2520
|
+
return bounds;
|
|
2521
|
+
}
|
|
2522
|
+
function activeConversationTurn(events) {
|
|
2523
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
2524
|
+
const event = events[index];
|
|
2525
|
+
if (!event.turnId) continue;
|
|
2526
|
+
if (TERMINAL_TURN_TYPES.has(event.type)) return null;
|
|
2527
|
+
if (event.type === "turn.started") return event.turnId;
|
|
2528
|
+
}
|
|
2529
|
+
return null;
|
|
2530
|
+
}
|
|
2531
|
+
function settledTurns(events) {
|
|
2532
|
+
const settled = /* @__PURE__ */ new Set();
|
|
2533
|
+
for (const event of events) {
|
|
2534
|
+
if (event.turnId && TERMINAL_TURN_TYPES.has(event.type)) settled.add(event.turnId);
|
|
2535
|
+
}
|
|
2536
|
+
return settled;
|
|
2537
|
+
}
|
|
2538
|
+
function windowTurns(events) {
|
|
2539
|
+
const turns = /* @__PURE__ */ new Set();
|
|
2540
|
+
for (const event of events) if (event.turnId) turns.add(event.turnId);
|
|
2541
|
+
return turns;
|
|
2542
|
+
}
|
|
2543
|
+
var EMPTY_PRESENTED_OUTPUTS = /* @__PURE__ */ new Map();
|
|
2544
|
+
var PRE_START_TURN_TYPES = /* @__PURE__ */ new Set(["turn.queued", "turn.capacity_waiting"]);
|
|
2545
|
+
var PRESENTED_OUTPUT_READS_PER_PASS = 8;
|
|
2546
|
+
var PRESENTED_OUTPUT_PAGE_SIZE = 16;
|
|
2547
|
+
var PRESENTED_OUTPUT_MAX_PAGES = 16;
|
|
2548
|
+
var PRESENTED_OUTPUT_RETRIES = 3;
|
|
2549
|
+
var PRESENTED_OUTPUT_RETRY_MS = 2e3;
|
|
2550
|
+
var RETAINED_VIEW_STEPS = {
|
|
2551
|
+
load: () => {
|
|
2552
|
+
},
|
|
2553
|
+
retain: () => () => {
|
|
2554
|
+
}
|
|
2555
|
+
};
|
|
2556
|
+
function mergeLoadedEvents(conversation, stores, presented, turnless, exactOutputs = EMPTY_PRESENTED_OUTPUTS) {
|
|
2557
|
+
if (stores.size === 0 && presented.size === 0 && turnless.length === 0 && exactOutputs.size === 0) {
|
|
2558
|
+
return conversation;
|
|
2559
|
+
}
|
|
2560
|
+
const turns = windowTurns(conversation);
|
|
2561
|
+
const ids = new Set(conversation.map((event) => event.id));
|
|
2562
|
+
const extra = [];
|
|
2563
|
+
const add = (event) => {
|
|
2564
|
+
if (ids.has(event.id) || !event.turnId || !turns.has(event.turnId)) return;
|
|
2565
|
+
ids.add(event.id);
|
|
2566
|
+
extra.push(event);
|
|
2567
|
+
};
|
|
2568
|
+
presented.forEach(add);
|
|
2569
|
+
exactOutputs.forEach(add);
|
|
2570
|
+
for (const [turnId, store] of stores) if (turns.has(turnId)) store.events.forEach(add);
|
|
2571
|
+
for (const event of turnless) {
|
|
2572
|
+
if (ids.has(event.id)) continue;
|
|
2573
|
+
ids.add(event.id);
|
|
2574
|
+
extra.push(event);
|
|
2575
|
+
}
|
|
2576
|
+
if (extra.length === 0) return conversation;
|
|
2577
|
+
return [...conversation, ...extra].sort((left, right) => left.sequence - right.sequence);
|
|
2578
|
+
}
|
|
2579
|
+
function groupByTurn(events) {
|
|
2580
|
+
const turns = /* @__PURE__ */ new Map();
|
|
2581
|
+
for (const event of events) {
|
|
2582
|
+
if (!event.turnId) continue;
|
|
2583
|
+
const list = turns.get(event.turnId);
|
|
2584
|
+
if (list) list.push(event);
|
|
2585
|
+
else turns.set(event.turnId, [event]);
|
|
2586
|
+
}
|
|
2587
|
+
return turns;
|
|
2588
|
+
}
|
|
2589
|
+
function appendStepEvents(store, incoming) {
|
|
2590
|
+
if (incoming.length === 0) return { events: store.events, bytes: store.bytes };
|
|
2591
|
+
const ids = new Set(store.events.map((event) => event.id));
|
|
2592
|
+
let bytes = store.bytes;
|
|
2593
|
+
const fresh = [];
|
|
2594
|
+
for (const event of incoming) {
|
|
2595
|
+
if (ids.has(event.id)) continue;
|
|
2596
|
+
ids.add(event.id);
|
|
2597
|
+
fresh.push(event);
|
|
2598
|
+
bytes += browserJsonBytes(event) + (store.events.length + fresh.length > 1 ? 1 : 0);
|
|
2599
|
+
}
|
|
2600
|
+
if (fresh.length === 0) return { events: store.events, bytes: store.bytes };
|
|
2601
|
+
const last = store.events[store.events.length - 1];
|
|
2602
|
+
const events = [...store.events, ...fresh];
|
|
2603
|
+
if (last && fresh.some((event) => event.sequence < last.sequence)) {
|
|
2604
|
+
events.sort((left, right) => left.sequence - right.sequence);
|
|
2605
|
+
} else if (!last) {
|
|
2606
|
+
events.sort((left, right) => left.sequence - right.sequence);
|
|
2607
|
+
}
|
|
2608
|
+
return { events, bytes };
|
|
2609
|
+
}
|
|
2610
|
+
function turnHasHiddenSequences(events, turnId, before) {
|
|
2611
|
+
const start = events.findIndex((event) => event.turnId === turnId);
|
|
2612
|
+
if (start < 0) return true;
|
|
2613
|
+
let expected = eventResumeSequence(events[start]) + 1;
|
|
2614
|
+
for (let index = start + 1; index < events.length; index += 1) {
|
|
2615
|
+
const event = events[index];
|
|
2616
|
+
if (event.sequence >= before) break;
|
|
2617
|
+
if (event.sequence > expected) return true;
|
|
2618
|
+
expected = Math.max(expected, eventResumeSequence(event) + 1);
|
|
2619
|
+
}
|
|
2620
|
+
return expected < before;
|
|
2621
|
+
}
|
|
2622
|
+
function trimLiveStepTail(store) {
|
|
2623
|
+
if (store.events.length <= SESSION_TURN_LIVE_STEP_TAIL && store.bytes <= SESSION_TURN_LIVE_STEP_TAIL_BYTES) {
|
|
2624
|
+
return store;
|
|
2625
|
+
}
|
|
2626
|
+
const kept = boundBrowserSessionEventWindow(store.events, {
|
|
2627
|
+
maxCount: SESSION_TURN_LIVE_STEP_TAIL,
|
|
2628
|
+
maxBytes: SESSION_TURN_LIVE_STEP_TAIL_BYTES
|
|
2629
|
+
});
|
|
2630
|
+
return { ...store, events: kept.events, bytes: kept.bytes, hasOlder: true, cursor: null };
|
|
2631
|
+
}
|
|
2632
|
+
function boundStepStores(stores, retained) {
|
|
2633
|
+
let total = 0;
|
|
2634
|
+
for (const [turnId, store] of stores) {
|
|
2635
|
+
if (!retained.get(turnId)) total += store.bytes;
|
|
2636
|
+
}
|
|
2637
|
+
if (total <= SESSION_STEP_BROWSER_MAX_BYTES) return stores;
|
|
2638
|
+
const candidates = [...stores].filter(([turnId, store]) => !retained.get(turnId) && !store.loading).sort((left, right) => left[1].touched - right[1].touched);
|
|
2639
|
+
for (const [turnId, store] of candidates) {
|
|
2640
|
+
if (total <= SESSION_STEP_BROWSER_MAX_BYTES) break;
|
|
2641
|
+
stores.delete(turnId);
|
|
2642
|
+
total -= store.bytes;
|
|
2643
|
+
}
|
|
2644
|
+
return stores;
|
|
2645
|
+
}
|
|
2646
|
+
async function findPresentedOutput(client, workspaceId, sessionId, callId, after, terminal) {
|
|
2647
|
+
let cursor = after;
|
|
2648
|
+
for (let page = 0; page < PRESENTED_OUTPUT_MAX_PAGES; page += 1) {
|
|
2649
|
+
const read = await readEventPage(client, workspaceId, sessionId, {
|
|
2650
|
+
includeTypes: ["agent.toolCall.output"],
|
|
2651
|
+
after: cursor,
|
|
2652
|
+
...terminal !== null ? { before: terminal + 1 } : {},
|
|
2653
|
+
direction: "after",
|
|
2654
|
+
limit: PRESENTED_OUTPUT_PAGE_SIZE,
|
|
2655
|
+
payloadMode: "full"
|
|
2656
|
+
});
|
|
2657
|
+
const output = read.events.find(
|
|
2658
|
+
(event) => event.payload?.id === callId
|
|
2659
|
+
);
|
|
2660
|
+
if (output) return output;
|
|
2661
|
+
const last = read.events.at(-1);
|
|
2662
|
+
if (!last || !(read.hasMore ?? read.events.length >= PRESENTED_OUTPUT_PAGE_SIZE)) return null;
|
|
2663
|
+
cursor = last.sequence;
|
|
2664
|
+
}
|
|
2665
|
+
return null;
|
|
2666
|
+
}
|
|
2667
|
+
async function readEventPage(client, workspaceId, sessionId, options) {
|
|
2668
|
+
const pageClient = client;
|
|
2669
|
+
if (typeof pageClient.listEventPage === "function") {
|
|
2670
|
+
const page = await pageClient.listEventPage(workspaceId, sessionId, options);
|
|
2671
|
+
return {
|
|
2672
|
+
events: page.events,
|
|
2673
|
+
hasMore: page.hasMore,
|
|
2674
|
+
// An SDK that predates the announcement reports nothing: treat it as none.
|
|
2675
|
+
conversationFilter: page.conversationFilter ?? null
|
|
2676
|
+
};
|
|
2677
|
+
}
|
|
2678
|
+
return { events: await client.listEvents(workspaceId, sessionId, options), hasMore: null };
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
// src/hooks/use-turn-queue.ts
|
|
2682
|
+
import { isTransientRequestError as isTransientRequestError2 } from "@opengeni/sdk";
|
|
2683
|
+
import { useCallback as useCallback4, useEffect as useEffect3, useLayoutEffect, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
|
|
2684
|
+
function isTurnQueueEvent(event) {
|
|
2685
|
+
return event.type.startsWith("turn.") || event.type.startsWith("session.queue.") || event.type.startsWith("session.control.") || event.type.startsWith("system.update.") || event.type.startsWith("workspace.inference.");
|
|
2686
|
+
}
|
|
2687
|
+
function queueSnapshotCovers(candidate, observed) {
|
|
2688
|
+
return Boolean(
|
|
2689
|
+
candidate && candidate.version >= observed.version && candidate.effectiveControl.controlVersion >= observed.effectiveControl.controlVersion
|
|
2690
|
+
);
|
|
2691
|
+
}
|
|
2692
|
+
var EMPTY_PENDING_BY_TURN = {};
|
|
2693
|
+
function useTurnQueue(sessionId, options = {}) {
|
|
2694
|
+
const t = useOpenGeniText();
|
|
2695
|
+
const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } = useEmbeddedSession(options);
|
|
2696
|
+
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
2697
|
+
const targetKey = `${workspaceId}\0${sessionId ?? ""}`;
|
|
2698
|
+
const pageLive = usePageLiveActivity();
|
|
2699
|
+
const pageLiveRef = useRef3(pageLive);
|
|
2700
|
+
pageLiveRef.current = pageLive;
|
|
2701
|
+
const [snapshot, setSnapshot] = useState3(null);
|
|
2702
|
+
const [stateTargetKey, setStateTargetKey] = useState3(targetKey);
|
|
2703
|
+
const [loading, setLoading] = useState3(enabled);
|
|
2704
|
+
const [error, setError] = useState3(null);
|
|
2705
|
+
const [mutationError, setMutationError] = useState3(null);
|
|
2706
|
+
const [pendingByTurn, setPendingByTurn] = useState3({});
|
|
2707
|
+
const [acceptedSteers, setAcceptedSteers] = useState3([]);
|
|
2708
|
+
const pendingRef = useRef3({});
|
|
2709
|
+
const readGeneration = useRef3(0);
|
|
2710
|
+
const targetKeyRef = useRef3(targetKey);
|
|
2711
|
+
const snapshotRef = useRef3(null);
|
|
2712
|
+
const transientStreakRef = useRef3(
|
|
2713
|
+
null
|
|
2714
|
+
);
|
|
2715
|
+
const transientRetryTimerRef = useRef3(null);
|
|
2716
|
+
const cancelTransientRetry = useCallback4(() => {
|
|
2717
|
+
if (transientRetryTimerRef.current !== null) {
|
|
2718
|
+
clearTimeout(transientRetryTimerRef.current);
|
|
2719
|
+
transientRetryTimerRef.current = null;
|
|
2720
|
+
}
|
|
2721
|
+
}, []);
|
|
2722
|
+
useLayoutEffect(() => {
|
|
2723
|
+
if (targetKeyRef.current === targetKey) return;
|
|
2724
|
+
targetKeyRef.current = targetKey;
|
|
2725
|
+
readGeneration.current += 1;
|
|
2726
|
+
snapshotRef.current = null;
|
|
2727
|
+
pendingRef.current = {};
|
|
2728
|
+
setStateTargetKey(targetKey);
|
|
2729
|
+
setSnapshot(null);
|
|
2730
|
+
setLoading(enabled);
|
|
2731
|
+
setError(null);
|
|
2732
|
+
transientStreakRef.current = null;
|
|
2733
|
+
cancelTransientRetry();
|
|
2734
|
+
setMutationError(null);
|
|
2735
|
+
setPendingByTurn({});
|
|
2736
|
+
setAcceptedSteers([]);
|
|
2737
|
+
}, [cancelTransientRetry, enabled, targetKey]);
|
|
2738
|
+
useEffect3(() => {
|
|
2739
|
+
if (acceptedSteers.length === 0 || options.events === void 0) return;
|
|
2740
|
+
const durablyConsumedTurnIds = new Set(
|
|
2741
|
+
options.events.flatMap(
|
|
2742
|
+
(event) => [
|
|
2743
|
+
"turn.started",
|
|
2744
|
+
"turn.superseded",
|
|
2745
|
+
"turn.cancelled",
|
|
2746
|
+
"turn.failed",
|
|
2747
|
+
"turn.completed"
|
|
2748
|
+
].includes(event.type) && event.turnId ? [event.turnId] : []
|
|
2749
|
+
)
|
|
2750
|
+
);
|
|
2751
|
+
if (durablyConsumedTurnIds.size === 0) return;
|
|
2752
|
+
setAcceptedSteers(
|
|
2753
|
+
(current) => current.filter((steer) => !durablyConsumedTurnIds.has(steer.turnId))
|
|
2754
|
+
);
|
|
2755
|
+
}, [acceptedSteers.length, options.events]);
|
|
2756
|
+
const acceptSnapshot = useCallback4(
|
|
2757
|
+
(ownedTargetKey, next) => {
|
|
2758
|
+
if (targetKeyRef.current !== ownedTargetKey) return false;
|
|
2759
|
+
const current = snapshotRef.current;
|
|
2760
|
+
if (next && current && (next.version < current.version || next.effectiveControl.controlVersion < current.effectiveControl.controlVersion)) {
|
|
2761
|
+
return false;
|
|
2762
|
+
}
|
|
2763
|
+
snapshotRef.current = next;
|
|
2764
|
+
setStateTargetKey(ownedTargetKey);
|
|
2765
|
+
setSnapshot(next);
|
|
2766
|
+
return true;
|
|
2767
|
+
},
|
|
2768
|
+
[]
|
|
2769
|
+
);
|
|
2770
|
+
const load = useCallback4(
|
|
2771
|
+
async (rejectOnFailure = false, fresh = false) => {
|
|
2772
|
+
if (!sessionId) return;
|
|
2773
|
+
const ownedTargetKey = `${workspaceId}\0${sessionId}`;
|
|
2774
|
+
const ticket = ++readGeneration.current;
|
|
2775
|
+
try {
|
|
2776
|
+
const fetched = fresh ? await client.getQueue(workspaceId, sessionId, { fresh: true }) : await client.getQueue(workspaceId, sessionId);
|
|
2777
|
+
if (targetKeyRef.current !== ownedTargetKey) return;
|
|
2778
|
+
transientStreakRef.current = null;
|
|
2779
|
+
cancelTransientRetry();
|
|
2780
|
+
const ownsLatestRead = ticket === readGeneration.current;
|
|
2781
|
+
if (rejectOnFailure) {
|
|
2782
|
+
const committed = acceptSnapshot(ownedTargetKey, fetched);
|
|
2783
|
+
if (!committed && !queueSnapshotCovers(snapshotRef.current, fetched)) {
|
|
2784
|
+
throw new TypeError("Queue reconciliation did not commit authoritative state");
|
|
2785
|
+
}
|
|
2786
|
+
setError(null);
|
|
2787
|
+
if (ownsLatestRead) setLoading(false);
|
|
2788
|
+
} else if (ownsLatestRead) {
|
|
2789
|
+
acceptSnapshot(ownedTargetKey, fetched);
|
|
2790
|
+
setError(null);
|
|
2791
|
+
setLoading(false);
|
|
2792
|
+
}
|
|
2793
|
+
} catch (cause) {
|
|
2794
|
+
if (targetKeyRef.current === ownedTargetKey && (ticket === readGeneration.current || rejectOnFailure)) {
|
|
2795
|
+
if (isTransientRequestError2(cause)) {
|
|
2796
|
+
const now = Date.now();
|
|
2797
|
+
const streak = transientStreakRef.current ??= {
|
|
2798
|
+
since: now,
|
|
2799
|
+
attempts: 0,
|
|
2800
|
+
surfaced: false
|
|
2801
|
+
};
|
|
2802
|
+
const step = transientReadRetryStep(streak, now, DEFAULT_TRANSIENT_READ_RETRY, cause);
|
|
2803
|
+
if (!step.quiet) {
|
|
2804
|
+
if (!streak.surfaced) setError(asError(cause));
|
|
2805
|
+
streak.surfaced = true;
|
|
2806
|
+
if (ticket === readGeneration.current) setLoading(false);
|
|
2807
|
+
}
|
|
2808
|
+
if (!rejectOnFailure && pageLiveRef.current) {
|
|
2809
|
+
cancelTransientRetry();
|
|
2810
|
+
transientRetryTimerRef.current = setTimeout(() => {
|
|
2811
|
+
transientRetryTimerRef.current = null;
|
|
2812
|
+
if (targetKeyRef.current === ownedTargetKey) void loadRef.current(false, fresh);
|
|
2813
|
+
}, step.delayMs);
|
|
2814
|
+
}
|
|
2815
|
+
} else {
|
|
2816
|
+
transientStreakRef.current = null;
|
|
2817
|
+
setError(asError(cause));
|
|
2818
|
+
if (ticket === readGeneration.current) setLoading(false);
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
if (rejectOnFailure) throw cause;
|
|
2822
|
+
}
|
|
2823
|
+
},
|
|
2824
|
+
[client, workspaceId, sessionId, acceptSnapshot, cancelTransientRetry]
|
|
2825
|
+
);
|
|
2826
|
+
const loadRef = useRef3(load);
|
|
2827
|
+
loadRef.current = load;
|
|
2828
|
+
useEffect3(() => cancelTransientRetry, [cancelTransientRetry]);
|
|
2829
|
+
useEffect3(() => {
|
|
2830
|
+
if (!enabled || !pageLive) {
|
|
2831
|
+
cancelTransientRetry();
|
|
2832
|
+
setLoading(false);
|
|
2833
|
+
return;
|
|
2834
|
+
}
|
|
2835
|
+
setLoading(true);
|
|
2836
|
+
const pollIntervalMs = options.pollIntervalMs;
|
|
2837
|
+
if (pollIntervalMs === void 0 || pollIntervalMs <= 0) {
|
|
2838
|
+
void load();
|
|
2839
|
+
return () => {
|
|
2840
|
+
readGeneration.current += 1;
|
|
2841
|
+
};
|
|
2842
|
+
}
|
|
2843
|
+
let cancelled = false;
|
|
2844
|
+
let timer = null;
|
|
2845
|
+
const schedule = () => {
|
|
2846
|
+
if (cancelled) return;
|
|
2847
|
+
timer = setTimeout(() => {
|
|
2848
|
+
timer = null;
|
|
2849
|
+
void load().finally(schedule);
|
|
2850
|
+
}, pollIntervalMs);
|
|
2851
|
+
};
|
|
2852
|
+
void load().finally(schedule);
|
|
2853
|
+
return () => {
|
|
2854
|
+
cancelled = true;
|
|
2855
|
+
if (timer !== null) clearTimeout(timer);
|
|
2856
|
+
readGeneration.current += 1;
|
|
2857
|
+
};
|
|
2858
|
+
}, [cancelTransientRetry, load, enabled, pageLive, options.pollIntervalMs]);
|
|
2859
|
+
const loadFresh = useCallback4(() => load(false, true), [load]);
|
|
2860
|
+
useEffect3(() => {
|
|
2861
|
+
if (enabled && workspaceControlEvent) void loadFresh();
|
|
2862
|
+
}, [enabled, loadFresh, workspaceControlEvent]);
|
|
2863
|
+
useEffect3(() => {
|
|
2864
|
+
if (!sessionId || !enabled) return;
|
|
2865
|
+
return registerSessionReconciler(sessionId, "queue", loadFresh);
|
|
2866
|
+
}, [enabled, loadFresh, registerSessionReconciler, sessionId]);
|
|
2867
|
+
const scheduleRefresh = useDebouncedCallback(() => void loadFresh());
|
|
2868
|
+
const freshSnapshot = stateTargetKey === targetKey ? snapshot : null;
|
|
2869
|
+
const freshLoading = stateTargetKey === targetKey ? loading : enabled;
|
|
2870
|
+
useSessionEventTrigger(
|
|
2871
|
+
client,
|
|
2872
|
+
workspaceId,
|
|
2873
|
+
sessionId,
|
|
2874
|
+
isTurnQueueEvent,
|
|
2875
|
+
scheduleRefresh,
|
|
2876
|
+
{
|
|
2877
|
+
enabled,
|
|
2878
|
+
...options.events !== void 0 ? { events: options.events } : {},
|
|
2879
|
+
readThrough: projectionReadThrough(freshSnapshot, freshLoading),
|
|
2880
|
+
refresh: scheduleRefresh
|
|
2881
|
+
},
|
|
2882
|
+
async () => await load(true, true)
|
|
2883
|
+
);
|
|
2884
|
+
const retainedSnapshot = useRetainedSessionView(
|
|
2885
|
+
client,
|
|
2886
|
+
workspaceId,
|
|
2887
|
+
sessionId,
|
|
2888
|
+
"queue",
|
|
2889
|
+
freshSnapshot,
|
|
2890
|
+
{ enabled, discard: stateTargetKey === targetKey && deniesRetainedView(error) }
|
|
2891
|
+
);
|
|
2892
|
+
const mutate = useCallback4(
|
|
2893
|
+
async (turnId, kind, command, failure = {}) => {
|
|
2894
|
+
const ownedTargetKey = targetKey;
|
|
2895
|
+
if (!sessionId || targetKeyRef.current !== ownedTargetKey || pendingRef.current[turnId]) {
|
|
2896
|
+
return null;
|
|
2897
|
+
}
|
|
2898
|
+
const current = snapshotRef.current;
|
|
2899
|
+
const turn = current?.items.find((candidate) => candidate.id === turnId);
|
|
2900
|
+
if (!current || !turn) return null;
|
|
2901
|
+
pendingRef.current = { ...pendingRef.current, [turnId]: kind };
|
|
2902
|
+
setStateTargetKey(ownedTargetKey);
|
|
2903
|
+
setPendingByTurn(pendingRef.current);
|
|
2904
|
+
setMutationError(null);
|
|
2905
|
+
const clientEventId = operationKey();
|
|
2906
|
+
try {
|
|
2907
|
+
let result;
|
|
2908
|
+
try {
|
|
2909
|
+
result = await command(current, turn, clientEventId);
|
|
2910
|
+
} catch (cause) {
|
|
2911
|
+
if (!isOutcomeUnknownError(cause)) throw cause;
|
|
2912
|
+
result = await command(current, turn, clientEventId);
|
|
2913
|
+
}
|
|
2914
|
+
if (targetKeyRef.current !== ownedTargetKey) return null;
|
|
2915
|
+
acceptSnapshot(ownedTargetKey, result.snapshot);
|
|
2916
|
+
return result;
|
|
2917
|
+
} catch (cause) {
|
|
2918
|
+
if (targetKeyRef.current === ownedTargetKey) {
|
|
2919
|
+
if (failure.quietWhenGone) {
|
|
2920
|
+
await loadFresh();
|
|
2921
|
+
const stillWaiting = snapshotRef.current?.items.some(
|
|
2922
|
+
(candidate) => candidate.id === turnId
|
|
2923
|
+
);
|
|
2924
|
+
if (stillWaiting !== false && targetKeyRef.current === ownedTargetKey) {
|
|
2925
|
+
setMutationError(asError(cause));
|
|
2926
|
+
}
|
|
2927
|
+
} else {
|
|
2928
|
+
setMutationError(asError(cause));
|
|
2929
|
+
await loadFresh();
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
return null;
|
|
2933
|
+
} finally {
|
|
2934
|
+
if (targetKeyRef.current === ownedTargetKey && turnId in pendingRef.current) {
|
|
2935
|
+
const next = { ...pendingRef.current };
|
|
2936
|
+
delete next[turnId];
|
|
2937
|
+
pendingRef.current = next;
|
|
2938
|
+
setPendingByTurn(next);
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
},
|
|
2942
|
+
[acceptSnapshot, loadFresh, sessionId, targetKey]
|
|
2943
|
+
);
|
|
2944
|
+
const moveTurn = useCallback4(
|
|
2945
|
+
async (turnId, beforeTurnId) => {
|
|
2946
|
+
const result = await mutate(
|
|
2947
|
+
turnId,
|
|
2948
|
+
"move",
|
|
2949
|
+
(current, _turn, clientEventId) => client.moveQueueItem(workspaceId, sessionId, turnId, {
|
|
2950
|
+
clientEventId,
|
|
2951
|
+
expectedQueueVersion: current.version,
|
|
2952
|
+
beforeTurnId
|
|
2953
|
+
})
|
|
2954
|
+
);
|
|
2955
|
+
return result !== null;
|
|
2956
|
+
},
|
|
2957
|
+
[client, mutate, sessionId, workspaceId]
|
|
2958
|
+
);
|
|
2959
|
+
const editTurn = useCallback4(
|
|
2960
|
+
async (turnId, edit) => {
|
|
2961
|
+
const result = await mutate(
|
|
2962
|
+
turnId,
|
|
2963
|
+
"edit",
|
|
2964
|
+
(_current, turn, clientEventId) => client.editQueueItem(workspaceId, sessionId, turnId, {
|
|
2965
|
+
clientEventId,
|
|
2966
|
+
expectedTurnVersion: turn.version,
|
|
2967
|
+
expectedDraftRevision: edit.expectedDraftRevision,
|
|
2968
|
+
replaceDraft: edit.replaceDraft
|
|
2969
|
+
})
|
|
2970
|
+
);
|
|
2971
|
+
return result?.draft ?? null;
|
|
2972
|
+
},
|
|
2973
|
+
[client, mutate, sessionId, workspaceId]
|
|
2974
|
+
);
|
|
2975
|
+
const recallTurn = useCallback4(
|
|
2976
|
+
async (turnId, recall) => {
|
|
2977
|
+
const ownedTargetKey = targetKey;
|
|
2978
|
+
const known = snapshotRef.current?.items.some((candidate) => candidate.id === turnId);
|
|
2979
|
+
if (known === false) return { status: "already_sent" };
|
|
2980
|
+
const result = await mutate(
|
|
2981
|
+
turnId,
|
|
2982
|
+
"edit",
|
|
2983
|
+
(_current, turn, clientEventId) => client.editQueueItem(workspaceId, sessionId, turnId, {
|
|
2984
|
+
clientEventId,
|
|
2985
|
+
expectedTurnVersion: turn.version,
|
|
2986
|
+
expectedDraftRevision: recall.expectedDraftRevision,
|
|
2987
|
+
replaceDraft: false
|
|
2988
|
+
}),
|
|
2989
|
+
{ quietWhenGone: true }
|
|
2990
|
+
);
|
|
2991
|
+
if (targetKeyRef.current !== ownedTargetKey) return { status: "failed" };
|
|
2992
|
+
if (result?.draft) return { status: "recalled", draft: result.draft };
|
|
2993
|
+
const stillWaiting = snapshotRef.current?.items.some((candidate) => candidate.id === turnId);
|
|
2994
|
+
return result === null && stillWaiting === false ? { status: "already_sent" } : { status: "failed" };
|
|
2995
|
+
},
|
|
2996
|
+
[client, mutate, sessionId, targetKey, workspaceId]
|
|
2997
|
+
);
|
|
2998
|
+
const steerTurn = useCallback4(
|
|
2999
|
+
async (turnId) => {
|
|
3000
|
+
const ownedTargetKey = targetKey;
|
|
3001
|
+
const turn = snapshotRef.current?.items.find((candidate) => candidate.id === turnId);
|
|
3002
|
+
if (targetKeyRef.current !== ownedTargetKey) return false;
|
|
3003
|
+
if (!turn) {
|
|
3004
|
+
setMutationError(new Error(t("hooks.useTurnQueue.thatQueuedPromptChangedBefore")));
|
|
3005
|
+
await loadFresh();
|
|
3006
|
+
return false;
|
|
3007
|
+
}
|
|
3008
|
+
const optimistic = {
|
|
3009
|
+
turnId: turn.id,
|
|
3010
|
+
triggerEventId: turn.triggerEventId,
|
|
3011
|
+
text: turn.prompt,
|
|
3012
|
+
annotations: turn.annotations ?? [],
|
|
3013
|
+
resources: turn.resources,
|
|
3014
|
+
tools: turn.tools,
|
|
3015
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3016
|
+
state: "sending"
|
|
3017
|
+
};
|
|
3018
|
+
setAcceptedSteers((current) => [
|
|
3019
|
+
...current.filter((steer) => steer.turnId !== turnId),
|
|
3020
|
+
optimistic
|
|
3021
|
+
]);
|
|
3022
|
+
const result = await mutate(
|
|
3023
|
+
turnId,
|
|
3024
|
+
"steer",
|
|
3025
|
+
(current, queuedTurn, clientEventId) => client.steerQueueItem(workspaceId, sessionId, turnId, {
|
|
3026
|
+
clientEventId,
|
|
3027
|
+
expectedTurnVersion: queuedTurn.version,
|
|
3028
|
+
controlEtag: current.effectiveControl.controlEtag
|
|
3029
|
+
})
|
|
3030
|
+
);
|
|
3031
|
+
if (targetKeyRef.current !== ownedTargetKey) return false;
|
|
3032
|
+
const advancedWithoutSteer = result === null && snapshotRef.current !== null && !snapshotRef.current.items.some((candidate) => candidate.id === turnId);
|
|
3033
|
+
if (advancedWithoutSteer) {
|
|
3034
|
+
setMutationError(null);
|
|
3035
|
+
setAcceptedSteers((current) => current.filter((steer) => steer.turnId !== turnId));
|
|
3036
|
+
return true;
|
|
3037
|
+
}
|
|
3038
|
+
const accepted = result !== null || snapshotRef.current?.items.some(
|
|
3039
|
+
(candidate) => candidate.id === turnId && candidate.metadata.delivery === "steer"
|
|
3040
|
+
) === true;
|
|
3041
|
+
setAcceptedSteers(
|
|
3042
|
+
(current) => accepted ? current.map(
|
|
3043
|
+
(steer) => steer.turnId === turnId ? { ...steer, state: "queued" } : steer
|
|
3044
|
+
) : current.filter((steer) => steer.turnId !== turnId)
|
|
3045
|
+
);
|
|
3046
|
+
return accepted;
|
|
3047
|
+
},
|
|
3048
|
+
[client, loadFresh, mutate, sessionId, targetKey, workspaceId, t]
|
|
3049
|
+
);
|
|
3050
|
+
const removeTurn = useCallback4(
|
|
3051
|
+
async (turnId) => {
|
|
3052
|
+
const result = await mutate(
|
|
3053
|
+
turnId,
|
|
3054
|
+
"delete",
|
|
3055
|
+
(_current, turn, clientEventId) => client.deleteQueueItem(workspaceId, sessionId, turnId, {
|
|
3056
|
+
clientEventId,
|
|
3057
|
+
expectedTurnVersion: turn.version,
|
|
3058
|
+
reason: "Deleted from the prompt queue"
|
|
3059
|
+
})
|
|
3060
|
+
);
|
|
3061
|
+
return result !== null;
|
|
3062
|
+
},
|
|
3063
|
+
[client, mutate, sessionId, workspaceId]
|
|
3064
|
+
);
|
|
3065
|
+
const identityMatches = stateTargetKey === targetKey;
|
|
3066
|
+
const visibleSnapshot = freshSnapshot ?? retainedSnapshot;
|
|
3067
|
+
const visiblePendingByTurn = identityMatches ? pendingByTurn : EMPTY_PENDING_BY_TURN;
|
|
3068
|
+
const mutating = useMemo3(
|
|
3069
|
+
() => Object.keys(visiblePendingByTurn).length > 0,
|
|
3070
|
+
[visiblePendingByTurn]
|
|
3071
|
+
);
|
|
3072
|
+
const mutationFor = useCallback4(
|
|
3073
|
+
(turnId) => visiblePendingByTurn[turnId] ?? null,
|
|
3074
|
+
[visiblePendingByTurn]
|
|
3075
|
+
);
|
|
3076
|
+
return {
|
|
3077
|
+
snapshot: visibleSnapshot,
|
|
3078
|
+
queue: visibleSnapshot?.items ?? [],
|
|
3079
|
+
pendingInputs: visibleSnapshot?.pendingInputs ?? [],
|
|
3080
|
+
pendingInputAttachment: visibleSnapshot?.pendingInputAttachment ?? null,
|
|
3081
|
+
activePersonalConnections: visibleSnapshot?.activePersonalConnections ?? [],
|
|
3082
|
+
effectiveControl: visibleSnapshot?.effectiveControl ?? null,
|
|
3083
|
+
stoppingPreviousAttempt: visibleSnapshot?.stoppingPreviousAttempt ?? false,
|
|
3084
|
+
loading: freshLoading && retainedSnapshot === null,
|
|
3085
|
+
error: identityMatches ? error : null,
|
|
3086
|
+
refresh: load,
|
|
3087
|
+
moveTurn,
|
|
3088
|
+
editTurn,
|
|
3089
|
+
recallTurn,
|
|
3090
|
+
steerTurn,
|
|
3091
|
+
removeTurn,
|
|
3092
|
+
pendingByTurn: visiblePendingByTurn,
|
|
3093
|
+
mutationFor,
|
|
3094
|
+
mutating,
|
|
3095
|
+
mutationError: identityMatches ? mutationError : null,
|
|
3096
|
+
clearMutationError: useCallback4(() => {
|
|
3097
|
+
if (targetKeyRef.current === targetKey) setMutationError(null);
|
|
3098
|
+
}, [targetKey]),
|
|
3099
|
+
acceptedSteers: identityMatches ? acceptedSteers : []
|
|
3100
|
+
};
|
|
3101
|
+
}
|
|
3102
|
+
function operationKey() {
|
|
3103
|
+
return globalThis.crypto.randomUUID();
|
|
3104
|
+
}
|
|
3105
|
+
function asError(cause) {
|
|
3106
|
+
return normalizeError(cause);
|
|
3107
|
+
}
|
|
3108
|
+
function isOutcomeUnknownError(cause) {
|
|
3109
|
+
return typeof cause === "object" && cause !== null && cause.outcomeUnknown === true;
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
// src/hooks/use-goal.ts
|
|
3113
|
+
import {
|
|
3114
|
+
OpenGeniApiError,
|
|
3115
|
+
observedSessionSequence
|
|
3116
|
+
} from "@opengeni/sdk";
|
|
3117
|
+
import { useCallback as useCallback5, useEffect as useEffect4, useLayoutEffect as useLayoutEffect2, useRef as useRef4, useState as useState4 } from "react";
|
|
3118
|
+
function isGoalEvent(event) {
|
|
3119
|
+
return event.type.startsWith("goal.");
|
|
3120
|
+
}
|
|
3121
|
+
function isGoalRefreshEvent(event) {
|
|
3122
|
+
const type = event.type;
|
|
3123
|
+
return isGoalEvent(event) || type.startsWith("turn.") || type.startsWith("session.") || type.startsWith("system.update.") || type.startsWith("workspace.inference.") || type.startsWith("user.");
|
|
3124
|
+
}
|
|
3125
|
+
function useGoal(sessionId, options = {}) {
|
|
3126
|
+
const { client, workspaceId } = useEmbeddedGoal(options);
|
|
3127
|
+
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
3128
|
+
const sharedEvents = options.events;
|
|
3129
|
+
const sharedFeed = sharedEvents !== void 0;
|
|
3130
|
+
const pageLive = usePageLiveActivity();
|
|
3131
|
+
const [goal, setGoal] = useState4(null);
|
|
3132
|
+
const [loading, setLoading] = useState4(enabled);
|
|
3133
|
+
const [error, setError] = useState4(null);
|
|
3134
|
+
const [readAbsent, setReadAbsent] = useState4(false);
|
|
3135
|
+
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
3136
|
+
const generation = useRef4(0);
|
|
3137
|
+
const targetKeyRef = useRef4(null);
|
|
3138
|
+
const goalAbsentRef = useRef4(false);
|
|
3139
|
+
const pendingRefreshRef = useRef4("none");
|
|
3140
|
+
const sharedEventsRef = useRef4(sharedEvents);
|
|
3141
|
+
useLayoutEffect2(() => {
|
|
3142
|
+
sharedEventsRef.current = sharedEvents;
|
|
3143
|
+
}, [sharedEvents]);
|
|
3144
|
+
const loadAbort = useRef4(null);
|
|
3145
|
+
const scheduleRefreshRef = useRef4(() => void 0);
|
|
3146
|
+
const retireLoads = useCallback5(() => {
|
|
3147
|
+
generation.current += 1;
|
|
3148
|
+
loadAbort.current?.abort();
|
|
3149
|
+
loadAbort.current = null;
|
|
3150
|
+
}, []);
|
|
3151
|
+
const load = useCallback5(async () => {
|
|
3152
|
+
if (!sessionId) {
|
|
3153
|
+
return;
|
|
3154
|
+
}
|
|
3155
|
+
const ticket = ++generation.current;
|
|
3156
|
+
pendingRefreshRef.current = "none";
|
|
3157
|
+
loadAbort.current?.abort();
|
|
3158
|
+
const controller = new AbortController();
|
|
3159
|
+
loadAbort.current = controller;
|
|
3160
|
+
let fetchedGoal = null;
|
|
3161
|
+
try {
|
|
3162
|
+
const fetched = typeof client.findGoal === "function" ? await client.findGoal(workspaceId, sessionId, { signal: controller.signal }) : await client.getGoal(workspaceId, sessionId, { signal: controller.signal });
|
|
3163
|
+
fetchedGoal = fetched;
|
|
3164
|
+
if (ticket === generation.current) {
|
|
3165
|
+
goalAbsentRef.current = fetched === null;
|
|
3166
|
+
setReadAbsent(fetched === null);
|
|
3167
|
+
setGoal(fetched);
|
|
3168
|
+
setError(null);
|
|
3169
|
+
setLoading(false);
|
|
3170
|
+
}
|
|
3171
|
+
} catch (cause) {
|
|
3172
|
+
if (ticket !== generation.current) {
|
|
3173
|
+
return;
|
|
3174
|
+
}
|
|
3175
|
+
if (cause instanceof OpenGeniApiError && cause.status === 404) {
|
|
3176
|
+
goalAbsentRef.current = true;
|
|
3177
|
+
setReadAbsent(true);
|
|
3178
|
+
setGoal(null);
|
|
3179
|
+
setError(null);
|
|
3180
|
+
} else {
|
|
3181
|
+
setError(normalizeError(cause));
|
|
3182
|
+
}
|
|
3183
|
+
setLoading(false);
|
|
3184
|
+
} finally {
|
|
3185
|
+
if (loadAbort.current === controller) loadAbort.current = null;
|
|
3186
|
+
}
|
|
3187
|
+
if (ticket !== generation.current || pendingRefreshRef.current === "none") {
|
|
3188
|
+
return;
|
|
3189
|
+
}
|
|
3190
|
+
const readThrough = fetchedGoal ? observedSessionSequence(fetchedGoal) : void 0;
|
|
3191
|
+
if (readThrough !== void 0 && sharedEventsRef.current !== void 0) {
|
|
3192
|
+
const events = sharedEventsRef.current;
|
|
3193
|
+
const newest = events[events.length - 1];
|
|
3194
|
+
if (!newest || newest.sequence <= readThrough) {
|
|
3195
|
+
pendingRefreshRef.current = "none";
|
|
3196
|
+
return;
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
if (goalAbsentRef.current && pendingRefreshRef.current === "refresh") {
|
|
3200
|
+
const events = sharedEventsRef.current ?? [];
|
|
3201
|
+
let latestGoalEvent;
|
|
3202
|
+
for (let index = events.length - 1; index >= 0 && !latestGoalEvent; index -= 1) {
|
|
3203
|
+
const event = events[index];
|
|
3204
|
+
if (event && isGoalEvent(event)) latestGoalEvent = event;
|
|
3205
|
+
}
|
|
3206
|
+
if (!latestGoalEvent || latestGoalEvent.type === "goal.cleared") {
|
|
3207
|
+
pendingRefreshRef.current = "none";
|
|
3208
|
+
return;
|
|
3209
|
+
}
|
|
3210
|
+
pendingRefreshRef.current = "goal";
|
|
3211
|
+
}
|
|
3212
|
+
scheduleRefreshRef.current();
|
|
3213
|
+
}, [client, workspaceId, sessionId]);
|
|
3214
|
+
useEffect4(() => {
|
|
3215
|
+
const targetKey = `${workspaceId} ${sessionId ?? ""}`;
|
|
3216
|
+
if (targetKeyRef.current !== targetKey) {
|
|
3217
|
+
targetKeyRef.current = targetKey;
|
|
3218
|
+
goalAbsentRef.current = false;
|
|
3219
|
+
pendingRefreshRef.current = "none";
|
|
3220
|
+
setReadAbsent(false);
|
|
3221
|
+
setGoal(null);
|
|
3222
|
+
setError(null);
|
|
3223
|
+
}
|
|
3224
|
+
if (!enabled || !pageLive) {
|
|
3225
|
+
retireLoads();
|
|
3226
|
+
setLoading(false);
|
|
3227
|
+
return;
|
|
3228
|
+
}
|
|
3229
|
+
if (sharedFeed) {
|
|
3230
|
+
setLoading(true);
|
|
3231
|
+
void load();
|
|
3232
|
+
return () => {
|
|
3233
|
+
retireLoads();
|
|
3234
|
+
};
|
|
3235
|
+
}
|
|
3236
|
+
setLoading(true);
|
|
3237
|
+
const pollIntervalMs = options.pollIntervalMs;
|
|
3238
|
+
if (pollIntervalMs === void 0 || pollIntervalMs <= 0) {
|
|
3239
|
+
void load();
|
|
3240
|
+
return () => {
|
|
3241
|
+
retireLoads();
|
|
3242
|
+
};
|
|
3243
|
+
}
|
|
3244
|
+
let cancelled = false;
|
|
3245
|
+
let timer = null;
|
|
3246
|
+
const schedule = () => {
|
|
3247
|
+
if (cancelled) return;
|
|
3248
|
+
timer = setTimeout(() => {
|
|
3249
|
+
timer = null;
|
|
3250
|
+
void load().finally(schedule);
|
|
3251
|
+
}, pollIntervalMs);
|
|
3252
|
+
};
|
|
3253
|
+
void load().finally(schedule);
|
|
3254
|
+
return () => {
|
|
3255
|
+
cancelled = true;
|
|
3256
|
+
if (timer !== null) clearTimeout(timer);
|
|
3257
|
+
retireLoads();
|
|
3258
|
+
};
|
|
3259
|
+
}, [
|
|
3260
|
+
load,
|
|
3261
|
+
enabled,
|
|
3262
|
+
pageLive,
|
|
3263
|
+
workspaceId,
|
|
3264
|
+
sessionId,
|
|
3265
|
+
options.pollIntervalMs,
|
|
3266
|
+
retireLoads,
|
|
3267
|
+
sharedFeed
|
|
3268
|
+
]);
|
|
3269
|
+
const scheduleRefresh = useDebouncedCallback(() => {
|
|
3270
|
+
const pending = pendingRefreshRef.current;
|
|
3271
|
+
if (pending === "none" || loadAbort.current !== null) return;
|
|
3272
|
+
if (pending === "refresh" && goalAbsentRef.current) {
|
|
3273
|
+
pendingRefreshRef.current = "none";
|
|
3274
|
+
return;
|
|
3275
|
+
}
|
|
3276
|
+
void load();
|
|
3277
|
+
});
|
|
3278
|
+
useLayoutEffect2(() => {
|
|
3279
|
+
scheduleRefreshRef.current = scheduleRefresh;
|
|
3280
|
+
}, [scheduleRefresh]);
|
|
3281
|
+
const isRefreshEvent = useCallback5(
|
|
3282
|
+
(event) => goalAbsentRef.current ? isGoalEvent(event) : isGoalRefreshEvent(event),
|
|
3283
|
+
[]
|
|
3284
|
+
);
|
|
3285
|
+
const refreshGoal = useCallback5(() => {
|
|
3286
|
+
pendingRefreshRef.current = "goal";
|
|
3287
|
+
scheduleRefresh();
|
|
3288
|
+
}, [scheduleRefresh]);
|
|
3289
|
+
const onRefreshEvent = useCallback5(
|
|
3290
|
+
(event) => {
|
|
3291
|
+
if (isGoalEvent(event)) pendingRefreshRef.current = "goal";
|
|
3292
|
+
else if (pendingRefreshRef.current === "none") pendingRefreshRef.current = "refresh";
|
|
3293
|
+
scheduleRefresh();
|
|
3294
|
+
},
|
|
3295
|
+
[scheduleRefresh]
|
|
3296
|
+
);
|
|
3297
|
+
useSessionEventTrigger(client, workspaceId, sessionId, isRefreshEvent, onRefreshEvent, {
|
|
3298
|
+
enabled,
|
|
3299
|
+
...sharedEvents !== void 0 ? { events: sharedEvents } : {},
|
|
3300
|
+
// The load settles events that arrive during its read itself (above).
|
|
3301
|
+
readThrough: projectionReadThrough(goal, false),
|
|
3302
|
+
refresh: refreshGoal
|
|
3303
|
+
});
|
|
3304
|
+
const retainedGoal = useRetainedSessionView(
|
|
3305
|
+
client,
|
|
3306
|
+
workspaceId,
|
|
3307
|
+
sessionId,
|
|
3308
|
+
"goal",
|
|
3309
|
+
loading ? null : goal,
|
|
3310
|
+
{ enabled, discard: readAbsent || deniesRetainedView(error) }
|
|
3311
|
+
);
|
|
3312
|
+
const visibleGoal = loading ? goal ?? retainedGoal : goal;
|
|
3313
|
+
const goalRef = useRef4(goal);
|
|
3314
|
+
const shownRetainedRef = useRef4(null);
|
|
3315
|
+
const readWaitersRef = useRef4([]);
|
|
3316
|
+
useLayoutEffect2(() => {
|
|
3317
|
+
goalRef.current = goal;
|
|
3318
|
+
shownRetainedRef.current = loading && goal === null ? retainedGoal : null;
|
|
3319
|
+
});
|
|
3320
|
+
useEffect4(() => {
|
|
3321
|
+
if (loading) return;
|
|
3322
|
+
const waiters = readWaitersRef.current;
|
|
3323
|
+
readWaitersRef.current = [];
|
|
3324
|
+
for (const resolve of waiters) resolve();
|
|
3325
|
+
}, [loading]);
|
|
3326
|
+
useEffect4(
|
|
3327
|
+
() => () => {
|
|
3328
|
+
goalRef.current = null;
|
|
3329
|
+
const waiters = readWaitersRef.current;
|
|
3330
|
+
readWaitersRef.current = [];
|
|
3331
|
+
for (const resolve of waiters) resolve();
|
|
3332
|
+
},
|
|
3333
|
+
[workspaceId, sessionId]
|
|
3334
|
+
);
|
|
3335
|
+
const aimedAtCurrentGoal = useCallback5(async () => {
|
|
3336
|
+
const shown = shownRetainedRef.current;
|
|
3337
|
+
if (!shown) return true;
|
|
3338
|
+
await new Promise((resolve) => readWaitersRef.current.push(resolve));
|
|
3339
|
+
const fresh = goalRef.current;
|
|
3340
|
+
return fresh !== null && fresh.id === shown.id && fresh.objectiveRevision === shown.objectiveRevision;
|
|
3341
|
+
}, []);
|
|
3342
|
+
const pause = useCallback5(
|
|
3343
|
+
async (rationale) => {
|
|
3344
|
+
if (!sessionId || !await aimedAtCurrentGoal()) {
|
|
3345
|
+
return null;
|
|
3346
|
+
}
|
|
3347
|
+
const result = await run(
|
|
3348
|
+
() => client.updateGoal(workspaceId, sessionId, {
|
|
3349
|
+
status: "paused",
|
|
3350
|
+
...rationale !== void 0 ? { rationale } : {}
|
|
3351
|
+
})
|
|
3352
|
+
);
|
|
3353
|
+
if (result) {
|
|
3354
|
+
setGoal(result);
|
|
3355
|
+
}
|
|
3356
|
+
return result;
|
|
3357
|
+
},
|
|
3358
|
+
[client, workspaceId, sessionId, run, aimedAtCurrentGoal]
|
|
3359
|
+
);
|
|
3360
|
+
const resume = useCallback5(async () => {
|
|
3361
|
+
if (!sessionId || !await aimedAtCurrentGoal()) {
|
|
3362
|
+
return null;
|
|
3363
|
+
}
|
|
3364
|
+
const result = await run(() => client.updateGoal(workspaceId, sessionId, { status: "active" }));
|
|
3365
|
+
if (result) {
|
|
3366
|
+
setGoal(result);
|
|
3367
|
+
}
|
|
3368
|
+
return result;
|
|
3369
|
+
}, [client, workspaceId, sessionId, run, aimedAtCurrentGoal]);
|
|
3370
|
+
const clearGoal = useCallback5(async () => {
|
|
3371
|
+
if (!sessionId || !await aimedAtCurrentGoal()) {
|
|
3372
|
+
return;
|
|
3373
|
+
}
|
|
3374
|
+
const ok = await run(async () => {
|
|
3375
|
+
await client.deleteGoal(workspaceId, sessionId);
|
|
3376
|
+
return true;
|
|
3377
|
+
});
|
|
3378
|
+
if (ok) {
|
|
3379
|
+
retireLoads();
|
|
3380
|
+
setReadAbsent(true);
|
|
3381
|
+
setGoal(null);
|
|
3382
|
+
setError(null);
|
|
3383
|
+
}
|
|
3384
|
+
}, [client, workspaceId, sessionId, run, retireLoads, aimedAtCurrentGoal]);
|
|
3385
|
+
return {
|
|
3386
|
+
goal: visibleGoal,
|
|
3387
|
+
isActive: visibleGoal?.status === "active",
|
|
3388
|
+
isPaused: visibleGoal?.status === "paused",
|
|
3389
|
+
isCompleted: visibleGoal?.status === "completed",
|
|
3390
|
+
loading: loading && retainedGoal === null,
|
|
3391
|
+
error,
|
|
3392
|
+
refresh: load,
|
|
3393
|
+
pause,
|
|
3394
|
+
resume,
|
|
3395
|
+
clearGoal,
|
|
3396
|
+
deleteGoal: clearGoal,
|
|
3397
|
+
updating: mutating,
|
|
3398
|
+
mutationError,
|
|
3399
|
+
clearMutationError
|
|
3400
|
+
};
|
|
3401
|
+
}
|
|
3402
|
+
|
|
3403
|
+
// src/hooks/use-session-control.ts
|
|
3404
|
+
import { useCallback as useCallback6, useLayoutEffect as useLayoutEffect3, useRef as useRef5 } from "react";
|
|
3405
|
+
function useSessionControl(sessionId, options = {}) {
|
|
3406
|
+
const { client, workspaceId } = useEmbeddedSession(options);
|
|
3407
|
+
const approvalTargetKey = `${workspaceId}\0${sessionId ?? ""}`;
|
|
3408
|
+
const pendingApproval = useRef5(null);
|
|
3409
|
+
useLayoutEffect3(() => {
|
|
3410
|
+
if (pendingApproval.current?.targetKey !== approvalTargetKey) {
|
|
3411
|
+
pendingApproval.current = null;
|
|
3412
|
+
}
|
|
3413
|
+
}, [approvalTargetKey]);
|
|
3414
|
+
const {
|
|
3415
|
+
run: runControl,
|
|
3416
|
+
mutating: controlling,
|
|
3417
|
+
mutationError: controlError,
|
|
3418
|
+
clearMutationError: clearControlError
|
|
3419
|
+
} = useMutationRunner(approvalTargetKey);
|
|
3420
|
+
const {
|
|
3421
|
+
run: runApproval,
|
|
3422
|
+
mutating: responding,
|
|
3423
|
+
mutationError: approvalError,
|
|
3424
|
+
clearMutationError: clearApprovalError
|
|
3425
|
+
} = useMutationRunner(approvalTargetKey);
|
|
3426
|
+
const pause = useCallback6(
|
|
3427
|
+
async (reason) => {
|
|
3428
|
+
if (!sessionId) {
|
|
3429
|
+
return null;
|
|
3430
|
+
}
|
|
3431
|
+
return await runControl(
|
|
3432
|
+
() => client.pauseSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
|
|
3433
|
+
);
|
|
3434
|
+
},
|
|
3435
|
+
[client, workspaceId, sessionId, runControl]
|
|
3436
|
+
);
|
|
3437
|
+
const resume = useCallback6(
|
|
3438
|
+
async (reason) => {
|
|
3439
|
+
if (!sessionId) return null;
|
|
3440
|
+
return await runControl(
|
|
3441
|
+
() => client.resumeSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
|
|
3442
|
+
);
|
|
3443
|
+
},
|
|
3444
|
+
[client, workspaceId, sessionId, runControl]
|
|
3445
|
+
);
|
|
3446
|
+
const decide = useCallback6(
|
|
3447
|
+
async (approvalId, decision, message, answer) => {
|
|
3448
|
+
if (!sessionId) {
|
|
3449
|
+
return null;
|
|
3450
|
+
}
|
|
3451
|
+
const decisionKey = JSON.stringify([approvalId, decision, message ?? null, answer ?? null]);
|
|
3452
|
+
if (pendingApproval.current?.decisionKey !== decisionKey) {
|
|
3453
|
+
pendingApproval.current = {
|
|
3454
|
+
targetKey: approvalTargetKey,
|
|
3455
|
+
decisionKey,
|
|
3456
|
+
clientEventId: crypto.randomUUID()
|
|
3457
|
+
};
|
|
3458
|
+
}
|
|
3459
|
+
const clientEventId = pendingApproval.current.clientEventId;
|
|
3460
|
+
const accepted = await runApproval(
|
|
3461
|
+
() => client.sendApprovalDecision(workspaceId, sessionId, {
|
|
3462
|
+
approvalId,
|
|
3463
|
+
decision,
|
|
3464
|
+
...message !== void 0 ? { message } : {},
|
|
3465
|
+
...answer !== void 0 ? answer : {},
|
|
3466
|
+
clientEventId
|
|
3467
|
+
})
|
|
3468
|
+
);
|
|
3469
|
+
if (accepted !== null && pendingApproval.current?.targetKey === approvalTargetKey && pendingApproval.current.clientEventId === clientEventId) {
|
|
3470
|
+
pendingApproval.current = null;
|
|
3471
|
+
}
|
|
3472
|
+
return accepted;
|
|
3473
|
+
},
|
|
3474
|
+
[approvalTargetKey, client, workspaceId, sessionId, runApproval]
|
|
3475
|
+
);
|
|
3476
|
+
const approve = useCallback6(
|
|
3477
|
+
async (approvalId, message, answer) => await decide(approvalId, "approve", message, answer),
|
|
3478
|
+
[decide]
|
|
3479
|
+
);
|
|
3480
|
+
const reject = useCallback6(
|
|
3481
|
+
async (approvalId, message) => await decide(approvalId, "reject", message),
|
|
3482
|
+
[decide]
|
|
3483
|
+
);
|
|
3484
|
+
const error = approvalError ?? controlError;
|
|
3485
|
+
const clearError = useCallback6(() => {
|
|
3486
|
+
clearControlError();
|
|
3487
|
+
clearApprovalError();
|
|
3488
|
+
}, [clearControlError, clearApprovalError]);
|
|
3489
|
+
return {
|
|
3490
|
+
pause,
|
|
3491
|
+
resume,
|
|
3492
|
+
controlling,
|
|
3493
|
+
approve,
|
|
3494
|
+
reject,
|
|
3495
|
+
responding,
|
|
3496
|
+
error,
|
|
3497
|
+
clearError
|
|
3498
|
+
};
|
|
3499
|
+
}
|
|
3500
|
+
|
|
3501
|
+
// src/hooks/use-available-models.ts
|
|
3502
|
+
import { useCallback as useCallback7 } from "react";
|
|
3503
|
+
function useAvailableModels(options = {}) {
|
|
3504
|
+
const client = useOpenGeniClient(options);
|
|
3505
|
+
const load = useCallback7(
|
|
3506
|
+
async (signal) => await client.getClientConfig({ signal }),
|
|
3507
|
+
[client]
|
|
3508
|
+
);
|
|
3509
|
+
const state = useSharedQuery(client, ["available-models"], load, {
|
|
3510
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
3511
|
+
enabled: options.enabled
|
|
3512
|
+
});
|
|
3513
|
+
return {
|
|
3514
|
+
models: state.data?.models ?? [],
|
|
3515
|
+
defaultModel: state.data?.defaultModel ?? null,
|
|
3516
|
+
loading: state.loading,
|
|
3517
|
+
error: state.error,
|
|
3518
|
+
refresh: state.refresh
|
|
3519
|
+
};
|
|
3520
|
+
}
|
|
3521
|
+
function useWorkspaceModelCatalog(options) {
|
|
3522
|
+
const t = useOpenGeniText();
|
|
3523
|
+
const client = useOpenGeniClient(options);
|
|
3524
|
+
const load = useCallback7(
|
|
3525
|
+
async (signal) => {
|
|
3526
|
+
if (!options.workspaceId) {
|
|
3527
|
+
return { models: [], defaultModel: null };
|
|
3528
|
+
}
|
|
3529
|
+
const [catalog, config] = await Promise.all([
|
|
3530
|
+
client.getWorkspaceModelCatalog(options.workspaceId, { signal }),
|
|
3531
|
+
// Joins the same workspace's config read already in flight (for
|
|
3532
|
+
// example the composer's voice availability).
|
|
3533
|
+
joinClientConfigRead(client, options.workspaceId)
|
|
3534
|
+
]);
|
|
3535
|
+
return {
|
|
3536
|
+
models: catalog.models,
|
|
3537
|
+
defaultModel: config.defaultModel
|
|
3538
|
+
};
|
|
3539
|
+
},
|
|
3540
|
+
[client, options.workspaceId]
|
|
3541
|
+
);
|
|
3542
|
+
const state = useSharedQuery(client, ["workspace-model-catalog", options.workspaceId], load, {
|
|
3543
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
3544
|
+
enabled: options.enabled !== false && Boolean(options.workspaceId)
|
|
3545
|
+
});
|
|
3546
|
+
const rows = sortPickerRows(projectPickerRows(t, state.data?.models ?? []));
|
|
3547
|
+
return {
|
|
3548
|
+
models: state.data?.models ?? [],
|
|
3549
|
+
rows,
|
|
3550
|
+
defaultModel: state.data?.defaultModel ?? null,
|
|
3551
|
+
loading: state.loading,
|
|
3552
|
+
error: state.error,
|
|
3553
|
+
refresh: state.refresh
|
|
3554
|
+
};
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
// src/hooks/use-human-input.ts
|
|
3558
|
+
import { useCallback as useCallback8, useEffect as useEffect5, useRef as useRef6, useState as useState5 } from "react";
|
|
3559
|
+
function isHumanInputEvent(event) {
|
|
3560
|
+
return event.type === "session.humanInput.requested" || event.type === "user.humanInputResponse" || event.type === "turn.completed" || event.type === "turn.failed" || event.type === "turn.cancelled";
|
|
3561
|
+
}
|
|
3562
|
+
function useHumanInputRequests(sessionId, options = {}) {
|
|
3563
|
+
const { client, workspaceId, registerSessionReconciler } = useEmbeddedHumanInputSession(options);
|
|
3564
|
+
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
3565
|
+
const load = useCallback8(async () => {
|
|
3566
|
+
if (!sessionId) return [];
|
|
3567
|
+
const readStartedAt = Date.now();
|
|
3568
|
+
const requests = await client.listHumanInputRequests(workspaceId, sessionId, {
|
|
3569
|
+
status: "pending"
|
|
3570
|
+
});
|
|
3571
|
+
const now2 = Date.now();
|
|
3572
|
+
pruneHumanInputDrafts(
|
|
3573
|
+
workspaceId,
|
|
3574
|
+
sessionId,
|
|
3575
|
+
requests.filter((request) => isActionableHumanInputRequest(request, now2)).map((request) => request.id),
|
|
3576
|
+
readStartedAt
|
|
3577
|
+
);
|
|
3578
|
+
return requests;
|
|
3579
|
+
}, [client, sessionId, workspaceId]);
|
|
3580
|
+
const state = usePolledValue(load, {
|
|
3581
|
+
enabled,
|
|
3582
|
+
pollIntervalMs: options.pollIntervalMs
|
|
3583
|
+
});
|
|
3584
|
+
const loadedRequests = state.data;
|
|
3585
|
+
const refreshRequests = state.refresh;
|
|
3586
|
+
const readDenied = enabled && Boolean(sessionId) && deniesRetainedView(state.error);
|
|
3587
|
+
useEffect5(() => {
|
|
3588
|
+
if (readDenied && sessionId) forgetSessionHumanInputDrafts(workspaceId, sessionId);
|
|
3589
|
+
}, [readDenied, sessionId, workspaceId]);
|
|
3590
|
+
const mutation = useMutationRunner(`${workspaceId}\0${sessionId ?? ""}`);
|
|
3591
|
+
const [respondingRequestId, setRespondingRequestId] = useState5(null);
|
|
3592
|
+
const [deadlineVersion, setDeadlineVersion] = useState5(0);
|
|
3593
|
+
const respondingRequestRef = useRef6(null);
|
|
3594
|
+
useEffect5(() => {
|
|
3595
|
+
respondingRequestRef.current = null;
|
|
3596
|
+
setRespondingRequestId(null);
|
|
3597
|
+
}, [sessionId, workspaceId]);
|
|
3598
|
+
useEffect5(() => {
|
|
3599
|
+
if (!sessionId || !enabled) return;
|
|
3600
|
+
return registerSessionReconciler(sessionId, "human-input", state.refresh);
|
|
3601
|
+
}, [enabled, registerSessionReconciler, sessionId, state.refresh]);
|
|
3602
|
+
const scheduleRefresh = useDebouncedCallback(() => void state.refresh());
|
|
3603
|
+
useSessionEventTrigger(client, workspaceId, sessionId, isHumanInputEvent, scheduleRefresh, {
|
|
3604
|
+
enabled,
|
|
3605
|
+
...options.events !== void 0 ? { events: options.events } : {},
|
|
3606
|
+
readThrough: projectionReadThrough(state.data, state.loading),
|
|
3607
|
+
refresh: scheduleRefresh
|
|
3608
|
+
});
|
|
3609
|
+
const retainedRequests = useRetainedSessionView(
|
|
3610
|
+
client,
|
|
3611
|
+
workspaceId,
|
|
3612
|
+
sessionId,
|
|
3613
|
+
"human-input",
|
|
3614
|
+
state.data,
|
|
3615
|
+
{ enabled, discard: deniesRetainedView(state.error) }
|
|
3616
|
+
);
|
|
3617
|
+
useEffect5(() => {
|
|
3618
|
+
const now2 = Date.now();
|
|
3619
|
+
const nextDeadline = (loadedRequests ?? []).reduce((next, request) => {
|
|
3620
|
+
if (request.status !== "pending" || !request.expiresAt) return next;
|
|
3621
|
+
const deadline = Date.parse(request.expiresAt);
|
|
3622
|
+
if (!Number.isFinite(deadline) || deadline <= now2) return next;
|
|
3623
|
+
return next === null ? deadline : Math.min(next, deadline);
|
|
3624
|
+
}, null);
|
|
3625
|
+
if (nextDeadline === null) return;
|
|
3626
|
+
const timer = globalThis.setTimeout(
|
|
3627
|
+
() => {
|
|
3628
|
+
setDeadlineVersion((current) => current + 1);
|
|
3629
|
+
void refreshRequests();
|
|
3630
|
+
},
|
|
3631
|
+
Math.min(2147483647, Math.max(0, nextDeadline - now2 + 1))
|
|
3632
|
+
);
|
|
3633
|
+
return () => globalThis.clearTimeout(timer);
|
|
3634
|
+
}, [loadedRequests, refreshRequests]);
|
|
3635
|
+
const respond = useCallback8(
|
|
3636
|
+
async (requestId, response) => {
|
|
3637
|
+
if (!sessionId || respondingRequestRef.current !== null) return null;
|
|
3638
|
+
respondingRequestRef.current = requestId;
|
|
3639
|
+
setRespondingRequestId(requestId);
|
|
3640
|
+
try {
|
|
3641
|
+
const event = await mutation.run(
|
|
3642
|
+
() => client.submitHumanInputResponse(workspaceId, sessionId, requestId, response, {
|
|
3643
|
+
clientEventId: globalThis.crypto.randomUUID()
|
|
3644
|
+
})
|
|
3645
|
+
);
|
|
3646
|
+
if (event) await state.refresh();
|
|
3647
|
+
return event;
|
|
3648
|
+
} finally {
|
|
3649
|
+
if (respondingRequestRef.current === requestId) {
|
|
3650
|
+
respondingRequestRef.current = null;
|
|
3651
|
+
setRespondingRequestId(null);
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
},
|
|
3655
|
+
[client, mutation, sessionId, state, workspaceId]
|
|
3656
|
+
);
|
|
3657
|
+
const now = Date.now();
|
|
3658
|
+
void deadlineVersion;
|
|
3659
|
+
return {
|
|
3660
|
+
requests: [...state.data ?? retainedRequests ?? []].filter((request) => isActionableHumanInputRequest(request, now)).sort(
|
|
3661
|
+
(left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id)
|
|
3662
|
+
),
|
|
3663
|
+
loading: state.loading && retainedRequests === null,
|
|
3664
|
+
error: state.error,
|
|
3665
|
+
refresh: state.refresh,
|
|
3666
|
+
respond,
|
|
3667
|
+
respondingRequestId,
|
|
3668
|
+
mutationError: mutation.mutationError,
|
|
3669
|
+
clearMutationError: mutation.clearMutationError
|
|
3670
|
+
};
|
|
3671
|
+
}
|
|
3672
|
+
|
|
3673
|
+
// src/conversation-timeline.ts
|
|
3674
|
+
function conversationTimeline(items, queue, composer) {
|
|
3675
|
+
const queued = new Set(
|
|
3676
|
+
queue.queue.filter((turn) => turn.metadata.delivery !== "steer").map((turn) => turn.triggerEventId)
|
|
3677
|
+
);
|
|
3678
|
+
const pending = composer.optimisticMessages ?? [];
|
|
3679
|
+
const pendingQueue = new Set(
|
|
3680
|
+
pending.filter(
|
|
3681
|
+
(message) => message.destination === "queue" && !(message.turnId && message.appliedQueueVersion != null && queue.snapshot && queue.snapshot.version >= message.appliedQueueVersion && !queue.queue.some((turn) => turn.id === message.turnId))
|
|
3682
|
+
).map((message) => `user-message:${message.clientEventId}`)
|
|
3683
|
+
);
|
|
3684
|
+
const visible = items.filter(
|
|
3685
|
+
(item) => item.kind !== "user-message" || !queued.has(item.id) && !pendingQueue.has(item.reconciliationKey ?? "")
|
|
3686
|
+
);
|
|
3687
|
+
const keys = new Set(
|
|
3688
|
+
visible.flatMap((item) => item.kind === "user-message" ? [item.reconciliationKey] : [])
|
|
3689
|
+
);
|
|
3690
|
+
const optimistic = pending.filter(
|
|
3691
|
+
(message) => (message.destination === "chat" || message.state === "failed") && !keys.has(`user-message:${message.clientEventId}`) && !queue.queue.some((turn) => turn.id === message.turnId)
|
|
3692
|
+
).map((message) => ({
|
|
3693
|
+
kind: "user-message",
|
|
3694
|
+
id: `optimistic:${message.clientEventId}`,
|
|
3695
|
+
reconciliationKey: `user-message:${message.clientEventId}`,
|
|
3696
|
+
text: message.text,
|
|
3697
|
+
annotations: message.annotations.map((annotation, ordinal) => ({ ...annotation, ordinal })),
|
|
3698
|
+
resources: message.resources,
|
|
3699
|
+
tools: [],
|
|
3700
|
+
occurredAt: message.occurredAt,
|
|
3701
|
+
delivery: {
|
|
3702
|
+
state: message.state,
|
|
3703
|
+
...message.error ? { error: message.error } : {},
|
|
3704
|
+
...message.state === "failed" ? {
|
|
3705
|
+
...message.retryable !== false && composer.retryOptimisticMessage ? { onRetry: () => composer.retryOptimisticMessage?.(message.clientEventId) } : {},
|
|
3706
|
+
...message.retryable === false && composer.restoreOptimisticMessage ? { onEdit: () => composer.restoreOptimisticMessage?.(message.clientEventId) } : {},
|
|
3707
|
+
onRemove: () => composer.removeOptimisticMessage?.(message.clientEventId)
|
|
3708
|
+
} : {}
|
|
3709
|
+
}
|
|
3710
|
+
}));
|
|
3711
|
+
const failed = optimistic.filter((message) => message.delivery?.state === "failed");
|
|
3712
|
+
const unsent = optimistic.filter((message) => message.delivery?.state !== "failed");
|
|
3713
|
+
const ids = new Set(visible.map((item) => item.id));
|
|
3714
|
+
const steers = (queue.acceptedSteers ?? []).filter((steer) => !ids.has(steer.triggerEventId)).map((steer) => ({
|
|
3715
|
+
kind: "user-message",
|
|
3716
|
+
id: steer.triggerEventId,
|
|
3717
|
+
text: steer.text,
|
|
3718
|
+
annotations: steer.annotations,
|
|
3719
|
+
resources: steer.resources,
|
|
3720
|
+
tools: steer.tools,
|
|
3721
|
+
occurredAt: steer.occurredAt,
|
|
3722
|
+
delivery: { state: steer.state }
|
|
3723
|
+
}));
|
|
3724
|
+
return [...placeFailedSends(visible, failed), ...unsent, ...steers];
|
|
3725
|
+
}
|
|
3726
|
+
function placeFailedSends(items, failed) {
|
|
3727
|
+
if (failed.length === 0) return items;
|
|
3728
|
+
const before = /* @__PURE__ */ new Map();
|
|
3729
|
+
const last = [];
|
|
3730
|
+
for (const message of failed) {
|
|
3731
|
+
const sentAt = Date.parse(message.occurredAt);
|
|
3732
|
+
const newer = items.findIndex(
|
|
3733
|
+
(item) => item.kind === "user-message" && Date.parse(item.occurredAt) > sentAt
|
|
3734
|
+
);
|
|
3735
|
+
if (newer === -1) {
|
|
3736
|
+
last.push(message);
|
|
3737
|
+
} else {
|
|
3738
|
+
before.set(newer, [...before.get(newer) ?? [], message]);
|
|
3739
|
+
}
|
|
3740
|
+
}
|
|
3741
|
+
return [...items.flatMap((item, index) => [...before.get(index) ?? [], item]), ...last];
|
|
3742
|
+
}
|
|
3743
|
+
|
|
3744
|
+
// src/lib/session-startup-timeline.tsx
|
|
3745
|
+
import { useRef as useRef7 } from "react";
|
|
3746
|
+
function currentEvents(events) {
|
|
3747
|
+
return events.filter(
|
|
3748
|
+
(event) => !event.duplicateOfEventId && (!event.turnAssociation || event.turnAssociation === "current")
|
|
3749
|
+
).sort((a, b) => a.sequence - b.sequence);
|
|
3750
|
+
}
|
|
3751
|
+
function payloadOf(event) {
|
|
3752
|
+
return event.payload && typeof event.payload === "object" ? event.payload : {};
|
|
3753
|
+
}
|
|
3754
|
+
function sessionStartupTimeline(items, input) {
|
|
3755
|
+
const { session, optimisticMessages, hasNewer } = input;
|
|
3756
|
+
if (hasNewer || session.effectiveControl.state !== "active" || !["idle", "queued", "running"].includes(session.status) || session.status === "idle" && session.inputWait)
|
|
3757
|
+
return items;
|
|
3758
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
3759
|
+
const pending = /* @__PURE__ */ new Map();
|
|
3760
|
+
let queueVersion = session.queueVersion;
|
|
3761
|
+
let newerProgress = false;
|
|
3762
|
+
let observedActiveTurn = null;
|
|
3763
|
+
let previousTurnEndedAt = null;
|
|
3764
|
+
for (const event of currentEvents(input.events)) {
|
|
3765
|
+
const payload = payloadOf(event);
|
|
3766
|
+
if (event.turnId && ["turn.completed", "turn.failed", "turn.cancelled", "turn.superseded"].includes(event.type))
|
|
3767
|
+
previousTurnEndedAt = event.occurredAt;
|
|
3768
|
+
if (event.sequence > session.lastSequence && ([
|
|
3769
|
+
"turn.started",
|
|
3770
|
+
"turn.completed",
|
|
3771
|
+
"turn.failed",
|
|
3772
|
+
"turn.cancelled",
|
|
3773
|
+
"turn.superseded"
|
|
3774
|
+
].includes(event.type) || event.type === "session.status.changed" && payload.status !== "queued" || event.type === "session.queue.changed" && ["edit", "delete"].includes(String(payload.operation))))
|
|
3775
|
+
newerProgress = true;
|
|
3776
|
+
if (event.type === "session.queue.changed" && typeof payload.queueVersion === "number")
|
|
3777
|
+
queueVersion = Math.max(queueVersion, payload.queueVersion);
|
|
3778
|
+
const turnId2 = event.turnId ?? (typeof payload.turnId === "string" ? payload.turnId : null);
|
|
3779
|
+
if (!turnId2) continue;
|
|
3780
|
+
if (event.sequence > session.lastSequence) {
|
|
3781
|
+
if (event.type === "turn.started") observedActiveTurn = turnId2;
|
|
3782
|
+
else if (observedActiveTurn === turnId2 && ["turn.completed", "turn.failed", "turn.cancelled", "turn.superseded"].includes(event.type))
|
|
3783
|
+
observedActiveTurn = null;
|
|
3784
|
+
}
|
|
3785
|
+
if ([
|
|
3786
|
+
"turn.started",
|
|
3787
|
+
"turn.completed",
|
|
3788
|
+
"turn.failed",
|
|
3789
|
+
"turn.cancelled",
|
|
3790
|
+
"turn.superseded"
|
|
3791
|
+
].includes(event.type) || event.type === "session.queue.changed" && ["edit", "delete"].includes(String(payload.operation)))
|
|
3792
|
+
claimed.add(turnId2);
|
|
3793
|
+
if ((event.type === "turn.queued" || event.type === "user.message") && payload.routing === "accepted_for_execution" && (session.status !== "idle" || event.sequence > session.lastSequence))
|
|
3794
|
+
pending.set(turnId2, event.occurredAt);
|
|
3795
|
+
}
|
|
3796
|
+
for (const message of optimisticMessages ?? []) {
|
|
3797
|
+
if (message.state === "queued" && message.destination === "chat" && message.delivery === "send" && message.turnId && !pending.has(message.turnId) && (session.status !== "idle" || message.appliedQueueVersion != null && message.appliedQueueVersion > session.queueVersion))
|
|
3798
|
+
pending.set(message.turnId, message.occurredAt);
|
|
3799
|
+
}
|
|
3800
|
+
const head = input.queue && input.queue.version >= queueVersion ? input.queue.items[0] : null;
|
|
3801
|
+
if (session.status === "queued" && !session.activeTurnId && head && !pending.has(head.id))
|
|
3802
|
+
pending.set(
|
|
3803
|
+
head.id,
|
|
3804
|
+
previousTurnEndedAt && previousTurnEndedAt > head.createdAt ? previousTurnEndedAt : head.createdAt
|
|
3805
|
+
);
|
|
3806
|
+
const candidate = [...pending].find(
|
|
3807
|
+
([turnId2]) => !claimed.has(turnId2) && (!session.activeTurnId || session.activeTurnId === turnId2) && (!observedActiveTurn || observedActiveTurn === turnId2) && !items.some((item) => "turnId" in item && item.turnId === turnId2)
|
|
3808
|
+
);
|
|
3809
|
+
const fallback = !candidate && !newerProgress && session.status === "queued" && !session.activeTurnId && input.fallbackStartedAt;
|
|
3810
|
+
if (!candidate && !fallback) return items;
|
|
3811
|
+
const [turnId, startedAt] = candidate ?? [
|
|
3812
|
+
`pending-startup:${session.id}`,
|
|
3813
|
+
input.fallbackStartedAt
|
|
3814
|
+
];
|
|
3815
|
+
return [
|
|
3816
|
+
...items,
|
|
3817
|
+
{
|
|
3818
|
+
kind: "startup-phase",
|
|
3819
|
+
id: `${turnId}-queue`,
|
|
3820
|
+
turnId,
|
|
3821
|
+
phase: "queue",
|
|
3822
|
+
status: "running",
|
|
3823
|
+
startedAt,
|
|
3824
|
+
occurredAt: startedAt,
|
|
3825
|
+
completedAt: null,
|
|
3826
|
+
durationMs: null,
|
|
3827
|
+
outcome: null,
|
|
3828
|
+
...session.activeTurnId ? {} : { dispatchWait: session.status === "queued" ? session.dispatchWait ?? null : null }
|
|
3829
|
+
}
|
|
3830
|
+
];
|
|
3831
|
+
}
|
|
3832
|
+
var PENDING_SCOPE = "pending-prompt";
|
|
3833
|
+
var PENDING_PROMPT_STARTUP_ID = "pending-prompt-startup";
|
|
3834
|
+
function pendingPromptItems(prompt) {
|
|
3835
|
+
return [
|
|
3836
|
+
{
|
|
3837
|
+
kind: "user-message",
|
|
3838
|
+
id: "pending-prompt-message",
|
|
3839
|
+
text: prompt.text,
|
|
3840
|
+
resources: prompt.resources ?? [],
|
|
3841
|
+
tools: [],
|
|
3842
|
+
occurredAt: prompt.sentAt
|
|
3843
|
+
},
|
|
3844
|
+
{
|
|
3845
|
+
kind: "startup-phase",
|
|
3846
|
+
id: PENDING_PROMPT_STARTUP_ID,
|
|
3847
|
+
turnId: PENDING_SCOPE,
|
|
3848
|
+
phase: "queue",
|
|
3849
|
+
status: "running",
|
|
3850
|
+
startedAt: prompt.sentAt,
|
|
3851
|
+
occurredAt: prompt.sentAt,
|
|
3852
|
+
completedAt: null,
|
|
3853
|
+
durationMs: null,
|
|
3854
|
+
outcome: null
|
|
3855
|
+
}
|
|
3856
|
+
];
|
|
3857
|
+
}
|
|
3858
|
+
function useSessionStartupTimeline(items, input) {
|
|
3859
|
+
const startup = useSessionStartupState(input.session);
|
|
3860
|
+
const bridgeRef = useRef7(null);
|
|
3861
|
+
const { pendingPrompt } = input;
|
|
3862
|
+
if (pendingPrompt && (!input.session || input.historyReady === false)) {
|
|
3863
|
+
bridgeRef.current = {
|
|
3864
|
+
scope: PENDING_SCOPE,
|
|
3865
|
+
id: PENDING_PROMPT_STARTUP_ID,
|
|
3866
|
+
turnId: null,
|
|
3867
|
+
since: pendingPrompt.sentAt,
|
|
3868
|
+
sequence: 0
|
|
3869
|
+
};
|
|
3870
|
+
return [...items, ...pendingPromptItems(pendingPrompt)];
|
|
3871
|
+
}
|
|
3872
|
+
if (!input.session) {
|
|
3873
|
+
bridgeRef.current = null;
|
|
3874
|
+
return items;
|
|
3875
|
+
}
|
|
3876
|
+
const scope = `${input.session.workspaceId}:${input.session.id}`;
|
|
3877
|
+
if (bridgeRef.current?.scope === PENDING_SCOPE)
|
|
3878
|
+
bridgeRef.current = { ...bridgeRef.current, scope };
|
|
3879
|
+
if (bridgeRef.current?.scope !== scope || input.hasNewer || input.session.effectiveControl.state !== "active" || !["queued", "running", "idle"].includes(input.session.status))
|
|
3880
|
+
bridgeRef.current = null;
|
|
3881
|
+
const boundTurn = bridgeRef.current?.turnId;
|
|
3882
|
+
const settled = boundTurn && currentEvents(input.events).find(
|
|
3883
|
+
(event) => event.turnId === boundTurn && ["turn.completed", "turn.failed", "turn.cancelled", "turn.superseded"].includes(event.type)
|
|
3884
|
+
);
|
|
3885
|
+
let fallbackStartedAt = bridgeRef.current?.turnId === null ? bridgeRef.current.since : startup?.startedAt;
|
|
3886
|
+
if (settled) {
|
|
3887
|
+
bridgeRef.current = null;
|
|
3888
|
+
fallbackStartedAt = input.session.status === "queued" && input.session.lastSequence >= settled.sequence ? input.session.updatedAt : void 0;
|
|
3889
|
+
}
|
|
3890
|
+
const projected = sessionStartupTimeline(items, {
|
|
3891
|
+
...input,
|
|
3892
|
+
session: input.session,
|
|
3893
|
+
fallbackStartedAt
|
|
3894
|
+
});
|
|
3895
|
+
if (projected !== items) {
|
|
3896
|
+
const phase2 = projected.at(-1);
|
|
3897
|
+
if (phase2.kind !== "startup-phase") return projected;
|
|
3898
|
+
const turnId2 = phase2.turnId === `pending-startup:${input.session.id}` ? null : phase2.turnId;
|
|
3899
|
+
let bridge2 = bridgeRef.current;
|
|
3900
|
+
if (!bridge2 || bridge2.turnId && turnId2 && bridge2.turnId !== turnId2) {
|
|
3901
|
+
bridge2 = {
|
|
3902
|
+
scope,
|
|
3903
|
+
id: phase2.id,
|
|
3904
|
+
turnId: turnId2,
|
|
3905
|
+
since: phase2.startedAt,
|
|
3906
|
+
sequence: input.session.lastSequence
|
|
3907
|
+
};
|
|
3908
|
+
} else
|
|
3909
|
+
bridge2 = {
|
|
3910
|
+
...bridge2,
|
|
3911
|
+
turnId: turnId2 ?? bridge2.turnId,
|
|
3912
|
+
since: bridge2.since < phase2.startedAt ? bridge2.since : phase2.startedAt
|
|
3913
|
+
};
|
|
3914
|
+
bridgeRef.current = bridge2;
|
|
3915
|
+
return [
|
|
3916
|
+
...projected.slice(0, -1),
|
|
3917
|
+
{ ...phase2, id: bridge2.id, startedAt: bridge2.since, occurredAt: bridge2.since }
|
|
3918
|
+
];
|
|
3919
|
+
}
|
|
3920
|
+
const bridge = bridgeRef.current;
|
|
3921
|
+
if (!bridge) return items;
|
|
3922
|
+
if (input.session.status === "idle") {
|
|
3923
|
+
bridgeRef.current = null;
|
|
3924
|
+
return items;
|
|
3925
|
+
}
|
|
3926
|
+
const started = currentEvents(input.events).find(
|
|
3927
|
+
(event) => event.type === "turn.started" && (bridge.turnId ? event.turnId === bridge.turnId : event.sequence > bridge.sequence)
|
|
3928
|
+
);
|
|
3929
|
+
const turnId = bridge.turnId ?? started?.turnId ?? input.session.activeTurnId;
|
|
3930
|
+
const phase = items.find((item) => item.kind === "startup-phase" && item.turnId === turnId);
|
|
3931
|
+
if (phase?.kind === "startup-phase") {
|
|
3932
|
+
bridgeRef.current = { ...bridge, turnId };
|
|
3933
|
+
return items.map(
|
|
3934
|
+
(item) => item === phase ? {
|
|
3935
|
+
...phase,
|
|
3936
|
+
id: bridge.id,
|
|
3937
|
+
loadingStartedAt: bridge.since < phase.startedAt ? bridge.since : phase.startedAt
|
|
3938
|
+
} : item
|
|
3939
|
+
);
|
|
3940
|
+
}
|
|
3941
|
+
if (turnId && (started || input.session.activeTurnId === turnId)) {
|
|
3942
|
+
bridgeRef.current = { ...bridge, turnId };
|
|
3943
|
+
return [
|
|
3944
|
+
...items,
|
|
3945
|
+
{
|
|
3946
|
+
kind: "startup-phase",
|
|
3947
|
+
id: bridge.id,
|
|
3948
|
+
turnId,
|
|
3949
|
+
phase: "queue",
|
|
3950
|
+
status: "complete",
|
|
3951
|
+
startedAt: bridge.since,
|
|
3952
|
+
occurredAt: bridge.since,
|
|
3953
|
+
completedAt: started?.occurredAt ?? input.session.updatedAt,
|
|
3954
|
+
durationMs: null,
|
|
3955
|
+
outcome: null
|
|
3956
|
+
}
|
|
3957
|
+
];
|
|
3958
|
+
}
|
|
3959
|
+
if (turnId) bridgeRef.current = null;
|
|
3960
|
+
return items;
|
|
3961
|
+
}
|
|
3962
|
+
|
|
3963
|
+
// src/hooks/use-session-conversation.ts
|
|
3964
|
+
import { useLayoutEffect as useLayoutEffect4, useMemo as useMemo4, useRef as useRef8, useState as useState6 } from "react";
|
|
3965
|
+
function useSessionConversation(sessionId, options = {}) {
|
|
3966
|
+
const [initialPrompt] = useState6(() => options.pendingPrompt ?? null);
|
|
3967
|
+
const context = useOpenGeni(options);
|
|
3968
|
+
const scope = { client: context.client, workspaceId: context.workspaceId };
|
|
3969
|
+
const config = useClientConfigFlags(context.client);
|
|
3970
|
+
const showModelPicker = options.modelPicker ?? config.modelSelection;
|
|
3971
|
+
const catalog = useWorkspaceModelCatalog({ ...scope, enabled: showModelPicker });
|
|
3972
|
+
const feed = useSessionEvents(sessionId, scope);
|
|
3973
|
+
const pendingPrompt = usePendingPrompt(initialPrompt, sessionId, feed.initialHistoryReady);
|
|
3974
|
+
const eventOptions = { ...scope, events: feed.events };
|
|
3975
|
+
const detail = useSession(sessionId, eventOptions);
|
|
3976
|
+
const queue = useTurnQueue(sessionId, eventOptions);
|
|
3977
|
+
const human = useHumanInputRequests(sessionId, eventOptions);
|
|
3978
|
+
const control = useSessionControl(sessionId, scope);
|
|
3979
|
+
const approvals = useMemo4(() => projectPendingApprovals(feed.events), [feed.events]);
|
|
3980
|
+
const files = useFileAttachments(scope);
|
|
3981
|
+
const clearFiles = files.clear;
|
|
3982
|
+
const attachmentSession = useRef8(sessionId);
|
|
3983
|
+
useLayoutEffect4(() => {
|
|
3984
|
+
if (attachmentSession.current === sessionId) return;
|
|
3985
|
+
const created = attachmentSession.current === null;
|
|
3986
|
+
attachmentSession.current = sessionId;
|
|
3987
|
+
if (created) return;
|
|
3988
|
+
clearFiles();
|
|
3989
|
+
}, [clearFiles, sessionId]);
|
|
3990
|
+
const uploadsEnabled = (options.attachments ?? true) && config.uploads;
|
|
3991
|
+
const status = feed.sessionStatus ?? detail.session?.status;
|
|
3992
|
+
const terminal = status === "cancelled";
|
|
3993
|
+
const importedArchive = detail.session?.importedArchive?.readOnly === true;
|
|
3994
|
+
const host = options.composerOptions;
|
|
3995
|
+
const resolveExtras = () => typeof host?.sendExtras === "function" ? host.sendExtras() : host?.sendExtras;
|
|
3996
|
+
const releaseSentFiles = (input) => files.removeReadyFiles(
|
|
3997
|
+
(input.resources ?? []).flatMap(
|
|
3998
|
+
(resource) => resource.kind === "file" ? [resource.fileId] : []
|
|
3999
|
+
)
|
|
4000
|
+
);
|
|
4001
|
+
const composer = useComposer(sessionId, {
|
|
4002
|
+
...eventOptions,
|
|
4003
|
+
initialDraft: host?.initialDraft,
|
|
4004
|
+
busySend: host?.busySend,
|
|
4005
|
+
offerQueue: host?.offerQueue,
|
|
4006
|
+
effectiveControl: queue.effectiveControl ?? detail.session?.effectiveControl,
|
|
4007
|
+
// As in the console and the native screen: a turn that is still current
|
|
4008
|
+
// (running or recovering) means a Send waits. A turn parked on provider
|
|
4009
|
+
// capacity does not: the Send replaces it and starts right away.
|
|
4010
|
+
sendDestination: () => promptSendDestination({
|
|
4011
|
+
status,
|
|
4012
|
+
activeTurnId: detail.session?.activeTurnId,
|
|
4013
|
+
queuedPrompts: queue.queue.length,
|
|
4014
|
+
admissionBlock: detail.session?.admissionBlock
|
|
4015
|
+
}),
|
|
4016
|
+
sendExtras: () => {
|
|
4017
|
+
const extras = resolveExtras();
|
|
4018
|
+
return {
|
|
4019
|
+
...extras,
|
|
4020
|
+
resources: [...extras?.resources ?? [], ...uploadsEnabled ? files.readyResources : []]
|
|
4021
|
+
};
|
|
4022
|
+
},
|
|
4023
|
+
sendBlocked: () => sessionId === null || terminal || importedArchive || uploadsEnabled && files.hasUnresolved || host?.sendBlocked?.() === true,
|
|
4024
|
+
onSubmitted: (text, input) => {
|
|
4025
|
+
releaseSentFiles(input);
|
|
4026
|
+
return host?.onSubmitted?.(text, input);
|
|
4027
|
+
},
|
|
4028
|
+
onSent: (text, input) => {
|
|
4029
|
+
releaseSentFiles(input);
|
|
4030
|
+
return host?.onSent?.(text, input);
|
|
4031
|
+
},
|
|
4032
|
+
onDeliveryError: host?.onDeliveryError
|
|
4033
|
+
});
|
|
4034
|
+
const conversation = useMemo4(
|
|
4035
|
+
() => conversationTimeline(feed.timeline, queue, composer),
|
|
4036
|
+
[feed.timeline, queue, composer]
|
|
4037
|
+
);
|
|
4038
|
+
const session = detail.session;
|
|
4039
|
+
const sessionControl = session?.effectiveControl;
|
|
4040
|
+
const timeline = useSessionStartupTimeline(conversation, {
|
|
4041
|
+
session: session && sessionControl ? {
|
|
4042
|
+
...session,
|
|
4043
|
+
effectiveControl: admissionRecheckControl(
|
|
4044
|
+
sessionControl,
|
|
4045
|
+
queue.effectiveControl,
|
|
4046
|
+
composer.effectiveControl
|
|
4047
|
+
)
|
|
4048
|
+
} : null,
|
|
4049
|
+
events: feed.events,
|
|
4050
|
+
optimisticMessages: composer.optimisticMessages,
|
|
4051
|
+
hasNewer: feed.hasNewer,
|
|
4052
|
+
queue: queue.snapshot,
|
|
4053
|
+
pendingPrompt,
|
|
4054
|
+
historyReady: sessionId !== null && feed.initialHistoryReady
|
|
4055
|
+
});
|
|
4056
|
+
const error = detail.error ?? feed.error ?? human.error;
|
|
4057
|
+
const loadFailed = Boolean(feed.error) && !feed.initialHistoryReady;
|
|
4058
|
+
const retry = async () => {
|
|
4059
|
+
await Promise.all([
|
|
4060
|
+
...detail.error ? [detail.refresh()] : [],
|
|
4061
|
+
...human.error ? [human.refresh()] : [],
|
|
4062
|
+
...feed.error ? [feed.jumpToLatest()] : []
|
|
4063
|
+
]);
|
|
4064
|
+
};
|
|
4065
|
+
return {
|
|
4066
|
+
...scope,
|
|
4067
|
+
sessionId,
|
|
4068
|
+
config,
|
|
4069
|
+
catalog,
|
|
4070
|
+
showModelPicker,
|
|
4071
|
+
feed,
|
|
4072
|
+
detail,
|
|
4073
|
+
queue,
|
|
4074
|
+
human,
|
|
4075
|
+
control,
|
|
4076
|
+
approvals,
|
|
4077
|
+
files,
|
|
4078
|
+
uploadsEnabled,
|
|
4079
|
+
composer,
|
|
4080
|
+
/** The same live application context for text and durable voice messages. */
|
|
4081
|
+
getModelContext: () => resolveExtras()?.modelContext,
|
|
4082
|
+
timeline,
|
|
4083
|
+
status,
|
|
4084
|
+
terminal,
|
|
4085
|
+
importedArchive,
|
|
4086
|
+
running: sessionTurnKeepsRunControl(status),
|
|
4087
|
+
error,
|
|
4088
|
+
loadFailed,
|
|
4089
|
+
retry
|
|
4090
|
+
};
|
|
4091
|
+
}
|
|
4092
|
+
function usePendingPrompt(prompt, sessionId, historyReady) {
|
|
4093
|
+
const owner = useRef8({
|
|
4094
|
+
sessionId,
|
|
4095
|
+
retired: prompt === null
|
|
4096
|
+
});
|
|
4097
|
+
const current = owner.current;
|
|
4098
|
+
if (!current.retired && current.sessionId !== sessionId) {
|
|
4099
|
+
if (current.sessionId === null) current.sessionId = sessionId;
|
|
4100
|
+
else current.retired = true;
|
|
4101
|
+
}
|
|
4102
|
+
if (!current.retired && sessionId !== null && historyReady) current.retired = true;
|
|
4103
|
+
return current.retired ? null : prompt;
|
|
4104
|
+
}
|
|
4105
|
+
|
|
4106
|
+
// src/hooks/use-session-read-acknowledgement.ts
|
|
4107
|
+
import { useEffect as useEffect6, useMemo as useMemo5, useRef as useRef9 } from "react";
|
|
4108
|
+
var SESSION_READ_ACKNOWLEDGEMENT_RETRY_MS = 1e3;
|
|
4109
|
+
function isPermanentRejection(error) {
|
|
4110
|
+
const status = error && typeof error === "object" ? error.status : void 0;
|
|
4111
|
+
return typeof status === "number" && status >= 400 && status < 500 && status !== 408 && status !== 429;
|
|
4112
|
+
}
|
|
4113
|
+
function useSessionReadAcknowledgement(input) {
|
|
4114
|
+
const { client, workspaceId, sessionId, events, active } = input;
|
|
4115
|
+
const session = input.session && (input.session.id ?? sessionId) === sessionId ? input.session : null;
|
|
4116
|
+
const attentionThrough = useMemo5(() => sessionAttentionReadThroughSequence(events), [events]);
|
|
4117
|
+
const { readThrough, unread } = readAcknowledgementFrontier(session, attentionThrough);
|
|
4118
|
+
const hasSession = session !== null;
|
|
4119
|
+
const receipt = useRef9(null);
|
|
4120
|
+
const refused = useRef9(/* @__PURE__ */ new Set());
|
|
4121
|
+
const retryTimer = useRef9(null);
|
|
4122
|
+
useEffect6(
|
|
4123
|
+
() => () => {
|
|
4124
|
+
if (retryTimer.current !== null) clearTimeout(retryTimer.current);
|
|
4125
|
+
retryTimer.current = null;
|
|
4126
|
+
receipt.current = null;
|
|
4127
|
+
},
|
|
4128
|
+
[]
|
|
4129
|
+
);
|
|
4130
|
+
useEffect6(() => {
|
|
4131
|
+
if (!active || !hasSession || !unread || readThrough <= 0) return;
|
|
4132
|
+
if (!workspaceId || !sessionId || typeof client.updateSessionAttention !== "function") return;
|
|
4133
|
+
const scope = `${workspaceId}:${sessionId}`;
|
|
4134
|
+
if (refused.current.has(scope)) return;
|
|
4135
|
+
const key = `${scope}:${readThrough}`;
|
|
4136
|
+
if (receipt.current === key) return;
|
|
4137
|
+
receipt.current = key;
|
|
4138
|
+
if (retryTimer.current !== null) clearTimeout(retryTimer.current);
|
|
4139
|
+
retryTimer.current = null;
|
|
4140
|
+
const attempt = (retried) => {
|
|
4141
|
+
void Promise.resolve().then(
|
|
4142
|
+
() => client.updateSessionAttention(workspaceId, sessionId, {
|
|
4143
|
+
unread: false,
|
|
4144
|
+
acknowledgedThroughSequence: readThrough
|
|
4145
|
+
})
|
|
4146
|
+
).catch((error) => {
|
|
4147
|
+
if (receipt.current !== key) return;
|
|
4148
|
+
if (isPermanentRejection(error)) {
|
|
4149
|
+
refused.current.add(scope);
|
|
4150
|
+
return;
|
|
4151
|
+
}
|
|
4152
|
+
if (!retried) {
|
|
4153
|
+
retryTimer.current = setTimeout(() => {
|
|
4154
|
+
retryTimer.current = null;
|
|
4155
|
+
if (receipt.current === key) attempt(true);
|
|
4156
|
+
}, SESSION_READ_ACKNOWLEDGEMENT_RETRY_MS);
|
|
4157
|
+
return;
|
|
4158
|
+
}
|
|
4159
|
+
receipt.current = null;
|
|
4160
|
+
});
|
|
4161
|
+
};
|
|
4162
|
+
attempt(false);
|
|
4163
|
+
}, [active, client, hasSession, readThrough, sessionId, unread, workspaceId]);
|
|
4164
|
+
}
|
|
4165
|
+
|
|
4166
|
+
// src/hooks/use-new-conversation.ts
|
|
4167
|
+
import {
|
|
4168
|
+
OpenGeniApiError as OpenGeniApiError2
|
|
4169
|
+
} from "@opengeni/sdk";
|
|
4170
|
+
import { useLayoutEffect as useLayoutEffect5, useMemo as useMemo6, useRef as useRef10, useState as useState7 } from "react";
|
|
4171
|
+
var NOOP_ASYNC = async () => {
|
|
4172
|
+
};
|
|
4173
|
+
function useNewConversation(options = {}) {
|
|
4174
|
+
const t = useOpenGeniText();
|
|
4175
|
+
const context = useOpenGeni(options);
|
|
4176
|
+
const scope = { client: context.client, workspaceId: context.workspaceId };
|
|
4177
|
+
const config = useClientConfigFlags(context.client);
|
|
4178
|
+
const showModelPicker = options.modelPicker ?? config.modelSelection;
|
|
4179
|
+
const catalog = useWorkspaceModelCatalog({ ...scope, enabled: showModelPicker });
|
|
4180
|
+
const files = useFileAttachments(scope);
|
|
4181
|
+
const uploadsEnabled = (options.attachments ?? true) && config.uploads;
|
|
4182
|
+
const available = options.enabled !== false && (options.createSession !== void 0 || config.sessionCreation);
|
|
4183
|
+
const [text, setText] = useState7("");
|
|
4184
|
+
const [choice, setChoice] = useState7({});
|
|
4185
|
+
const [sending, setSending] = useState7(false);
|
|
4186
|
+
const [error, setError] = useState7(null);
|
|
4187
|
+
const [pending, setPending] = useState7(null);
|
|
4188
|
+
const [completion, setCompletion] = useState7(null);
|
|
4189
|
+
const textRef = useRef10(text);
|
|
4190
|
+
const textRevision = useRef10(0);
|
|
4191
|
+
const choiceRef = useRef10(choice);
|
|
4192
|
+
const filesRef = useRef10(files);
|
|
4193
|
+
filesRef.current = files;
|
|
4194
|
+
const pendingRef = useRef10(null);
|
|
4195
|
+
const completionRef = useRef10(null);
|
|
4196
|
+
const inFlight = useRef10(false);
|
|
4197
|
+
const lifetime = useRef10({ mounted: true, generation: 0 }).current;
|
|
4198
|
+
const identity = useMemo6(
|
|
4199
|
+
() => ({
|
|
4200
|
+
client: context.client,
|
|
4201
|
+
workspaceId: context.workspaceId,
|
|
4202
|
+
scopeKey: options.scopeKey
|
|
4203
|
+
}),
|
|
4204
|
+
[context.client, context.workspaceId, options.scopeKey]
|
|
4205
|
+
);
|
|
4206
|
+
const committedIdentity = useRef10(identity);
|
|
4207
|
+
useLayoutEffect5(() => {
|
|
4208
|
+
lifetime.mounted = true;
|
|
4209
|
+
return () => {
|
|
4210
|
+
lifetime.mounted = false;
|
|
4211
|
+
lifetime.generation++;
|
|
4212
|
+
};
|
|
4213
|
+
}, [lifetime]);
|
|
4214
|
+
useLayoutEffect5(() => {
|
|
4215
|
+
if (committedIdentity.current === identity) return;
|
|
4216
|
+
committedIdentity.current = identity;
|
|
4217
|
+
lifetime.generation++;
|
|
4218
|
+
inFlight.current = false;
|
|
4219
|
+
pendingRef.current = null;
|
|
4220
|
+
completionRef.current = null;
|
|
4221
|
+
textRef.current = "";
|
|
4222
|
+
textRevision.current = 0;
|
|
4223
|
+
choiceRef.current = {};
|
|
4224
|
+
setText("");
|
|
4225
|
+
setChoice({});
|
|
4226
|
+
setSending(false);
|
|
4227
|
+
setError(null);
|
|
4228
|
+
setPending(null);
|
|
4229
|
+
setCompletion(null);
|
|
4230
|
+
filesRef.current.clear();
|
|
4231
|
+
}, [identity, lifetime]);
|
|
4232
|
+
const ownsScope = () => lifetime.mounted && committedIdentity.current === identity;
|
|
4233
|
+
const updateText = (value) => {
|
|
4234
|
+
if (!ownsScope()) return;
|
|
4235
|
+
if (value !== textRef.current) textRevision.current++;
|
|
4236
|
+
textRef.current = value;
|
|
4237
|
+
setText(value);
|
|
4238
|
+
};
|
|
4239
|
+
const updateChoice = (patch) => {
|
|
4240
|
+
if (!ownsScope()) return;
|
|
4241
|
+
const next = { ...choiceRef.current, ...patch };
|
|
4242
|
+
const model = catalog.models.find((row) => row.id === (next.model ?? catalog.defaultModel));
|
|
4243
|
+
if (model) {
|
|
4244
|
+
if (next.reasoningEffort)
|
|
4245
|
+
next.reasoningEffort = coerceReasoningEffortForModel(model, next.reasoningEffort);
|
|
4246
|
+
if (next.latencyMode && !runnableLatencyModesForModel(model).includes(next.latencyMode))
|
|
4247
|
+
next.latencyMode = "standard";
|
|
4248
|
+
}
|
|
4249
|
+
choiceRef.current = next;
|
|
4250
|
+
setChoice(choiceRef.current);
|
|
4251
|
+
};
|
|
4252
|
+
const effectivePolicy = () => {
|
|
4253
|
+
const model = choiceRef.current.model ?? catalog.defaultModel;
|
|
4254
|
+
const definition = catalog.models.find((row) => row.id === model);
|
|
4255
|
+
const effort = choiceRef.current.reasoningEffort ?? (definition ? defaultEffortForModel(definition) : config.defaultReasoningEffort ?? "medium");
|
|
4256
|
+
return showModelPicker && model ? {
|
|
4257
|
+
model,
|
|
4258
|
+
reasoningEffort: definition ? coerceReasoningEffortForModel(definition, effort) : effort,
|
|
4259
|
+
latencyMode: choiceRef.current.latencyMode ?? "standard"
|
|
4260
|
+
} : void 0;
|
|
4261
|
+
};
|
|
4262
|
+
const create = options.createSession ?? (async (initialMessage, idempotencyKey, input) => {
|
|
4263
|
+
const creator = context.client.createSession;
|
|
4264
|
+
if (typeof creator !== "function")
|
|
4265
|
+
throw new Error(t("hooks.useNewConversation.newChatsAreNotEnabled"));
|
|
4266
|
+
const created = await creator.call(context.client, context.workspaceId, {
|
|
4267
|
+
...input.startMode === "realtime" ? {} : { initialMessage },
|
|
4268
|
+
idempotencyKey,
|
|
4269
|
+
...input
|
|
4270
|
+
});
|
|
4271
|
+
return created.id;
|
|
4272
|
+
});
|
|
4273
|
+
const finishCreation = (accepted) => {
|
|
4274
|
+
if (!lifetime.mounted || lifetime.generation !== accepted.generation) return;
|
|
4275
|
+
const { attempt, sessionId } = accepted;
|
|
4276
|
+
const sentFiles = new Set(attempt.options.resources?.map((resource) => resource.fileId) ?? []);
|
|
4277
|
+
const policyChanged = ["model", "reasoningEffort", "latencyMode"].some(
|
|
4278
|
+
(field) => choiceRef.current[field] !== attempt.options[field]
|
|
4279
|
+
);
|
|
4280
|
+
const draft = {
|
|
4281
|
+
text: attempt.clearDraft && textRevision.current === attempt.textRevision ? "" : textRef.current,
|
|
4282
|
+
resources: filesRef.current.readyResources.filter(
|
|
4283
|
+
(resource) => !sentFiles.has(resource.fileId)
|
|
4284
|
+
),
|
|
4285
|
+
// Unchanged choices are already part of the created session. Do not
|
|
4286
|
+
// replace server-owned defaults with a browser's catalog estimate.
|
|
4287
|
+
...policyChanged ? { policy: effectivePolicy() } : {}
|
|
4288
|
+
};
|
|
4289
|
+
pendingRef.current = null;
|
|
4290
|
+
completionRef.current = null;
|
|
4291
|
+
setPending(null);
|
|
4292
|
+
setCompletion(null);
|
|
4293
|
+
updateText(draft.text);
|
|
4294
|
+
filesRef.current.removeReadyFiles(sentFiles);
|
|
4295
|
+
notifyObserver(options.onCreated, {
|
|
4296
|
+
sessionId,
|
|
4297
|
+
initialMessage: attempt.initialMessage,
|
|
4298
|
+
sentAt: attempt.sentAt,
|
|
4299
|
+
draft,
|
|
4300
|
+
...attempt.realtimeModel ? { realtimeModel: attempt.realtimeModel } : {}
|
|
4301
|
+
});
|
|
4302
|
+
};
|
|
4303
|
+
const finishRef = useRef10(finishCreation);
|
|
4304
|
+
finishRef.current = finishCreation;
|
|
4305
|
+
useLayoutEffect5(() => {
|
|
4306
|
+
if (completion && !files.hasUnresolved) finishRef.current(completion);
|
|
4307
|
+
}, [completion, files.hasUnresolved]);
|
|
4308
|
+
const deliver = async (attempt) => {
|
|
4309
|
+
if (!ownsScope() || inFlight.current || completionRef.current || !available) return false;
|
|
4310
|
+
const ownedGeneration = lifetime.generation;
|
|
4311
|
+
inFlight.current = true;
|
|
4312
|
+
pendingRef.current = attempt;
|
|
4313
|
+
setSending(true);
|
|
4314
|
+
setError(null);
|
|
4315
|
+
setPending(null);
|
|
4316
|
+
try {
|
|
4317
|
+
const sessionId = await attempt.create(
|
|
4318
|
+
attempt.initialMessage,
|
|
4319
|
+
attempt.key,
|
|
4320
|
+
structuredClone(attempt.options)
|
|
4321
|
+
);
|
|
4322
|
+
if (!lifetime.mounted || lifetime.generation !== ownedGeneration) return true;
|
|
4323
|
+
const accepted = { sessionId, attempt, generation: ownedGeneration };
|
|
4324
|
+
completionRef.current = accepted;
|
|
4325
|
+
if (filesRef.current.hasUnresolved) setCompletion(accepted);
|
|
4326
|
+
else finishCreation(accepted);
|
|
4327
|
+
return true;
|
|
4328
|
+
} catch (cause) {
|
|
4329
|
+
if (!lifetime.mounted || lifetime.generation !== ownedGeneration) return false;
|
|
4330
|
+
setError({ cause });
|
|
4331
|
+
if (cause instanceof OpenGeniApiError2 && cause.outcomeUnknown === false) {
|
|
4332
|
+
pendingRef.current = null;
|
|
4333
|
+
} else {
|
|
4334
|
+
setPending(attempt);
|
|
4335
|
+
}
|
|
4336
|
+
return false;
|
|
4337
|
+
} finally {
|
|
4338
|
+
if (lifetime.mounted && lifetime.generation === ownedGeneration) {
|
|
4339
|
+
inFlight.current = false;
|
|
4340
|
+
setSending(false);
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
};
|
|
4344
|
+
const submit = async (explicit) => {
|
|
4345
|
+
if (!ownsScope() || !available || inFlight.current || pendingRef.current || uploadsEnabled && filesRef.current.hasUnresolved)
|
|
4346
|
+
return false;
|
|
4347
|
+
const resources = uploadsEnabled ? filesRef.current.readyResources : [];
|
|
4348
|
+
const draftText = explicit ?? textRef.current;
|
|
4349
|
+
const initialMessage = draftText.trim() || (resources.length > 0 ? FILE_ONLY_MESSAGE_TEXT(t) : "");
|
|
4350
|
+
if (!initialMessage) return false;
|
|
4351
|
+
return deliver({
|
|
4352
|
+
key: crypto.randomUUID(),
|
|
4353
|
+
initialMessage,
|
|
4354
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4355
|
+
textRevision: textRevision.current,
|
|
4356
|
+
clearDraft: explicit === void 0,
|
|
4357
|
+
create,
|
|
4358
|
+
options: {
|
|
4359
|
+
...resources.length ? { resources: structuredClone(resources) } : {},
|
|
4360
|
+
...showModelPicker ? { ...choiceRef.current } : {}
|
|
4361
|
+
}
|
|
4362
|
+
});
|
|
4363
|
+
};
|
|
4364
|
+
const startRealtime = async (model) => {
|
|
4365
|
+
if (!ownsScope() || !available || inFlight.current || pendingRef.current || !config.realtimeVoice || !(options.realtimeVoice ?? config.realtimeVoiceOffered))
|
|
4366
|
+
return false;
|
|
4367
|
+
if (filesRef.current.hasUnresolved) return false;
|
|
4368
|
+
return deliver({
|
|
4369
|
+
key: crypto.randomUUID(),
|
|
4370
|
+
initialMessage: "",
|
|
4371
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4372
|
+
textRevision: textRevision.current,
|
|
4373
|
+
clearDraft: false,
|
|
4374
|
+
create,
|
|
4375
|
+
options: { ...showModelPicker ? { ...choiceRef.current } : {}, startMode: "realtime" },
|
|
4376
|
+
realtimeModel: model
|
|
4377
|
+
});
|
|
4378
|
+
};
|
|
4379
|
+
const composer = {
|
|
4380
|
+
value: text,
|
|
4381
|
+
setValue: updateText,
|
|
4382
|
+
hasDraftContent: () => textRef.current.length > 0 || filesRef.current.attachments.length > 0,
|
|
4383
|
+
send: submit,
|
|
4384
|
+
steer: submit,
|
|
4385
|
+
// Nothing runs before the conversation exists: Send just sends.
|
|
4386
|
+
working: false,
|
|
4387
|
+
sending,
|
|
4388
|
+
canSend: available && !sending && !pending && !completion && (text.trim().length > 0 || uploadsEnabled && files.readyResources.length > 0) && !(uploadsEnabled && files.hasUnresolved),
|
|
4389
|
+
pause: NOOP_ASYNC,
|
|
4390
|
+
pausing: false,
|
|
4391
|
+
resume: NOOP_ASYNC,
|
|
4392
|
+
resumeScope: NOOP_ASYNC,
|
|
4393
|
+
resuming: false,
|
|
4394
|
+
draft: null,
|
|
4395
|
+
draftRevision: 0,
|
|
4396
|
+
draftLoading: false,
|
|
4397
|
+
draftSaving: false,
|
|
4398
|
+
draftConflict: null,
|
|
4399
|
+
policy: effectivePolicy() ?? null,
|
|
4400
|
+
setModel: (model) => updateChoice({ model }),
|
|
4401
|
+
setReasoningEffort: (reasoningEffort) => updateChoice({ reasoningEffort }),
|
|
4402
|
+
setLatencyMode: (latencyMode) => updateChoice({ latencyMode }),
|
|
4403
|
+
draftPersistence: "disabled",
|
|
4404
|
+
applyDraft: () => {
|
|
4405
|
+
},
|
|
4406
|
+
reloadDraft: NOOP_ASYNC,
|
|
4407
|
+
resolveDraftConflict: NOOP_ASYNC,
|
|
4408
|
+
restoredResources: [],
|
|
4409
|
+
removeRestoredResource: () => {
|
|
4410
|
+
},
|
|
4411
|
+
error: null,
|
|
4412
|
+
clearError: () => {
|
|
4413
|
+
if (ownsScope()) setError(null);
|
|
4414
|
+
}
|
|
4415
|
+
};
|
|
4416
|
+
return {
|
|
4417
|
+
...scope,
|
|
4418
|
+
config,
|
|
4419
|
+
files,
|
|
4420
|
+
uploadsEnabled,
|
|
4421
|
+
available,
|
|
4422
|
+
showModelPicker,
|
|
4423
|
+
catalog,
|
|
4424
|
+
composer,
|
|
4425
|
+
realtimeVoiceEnabled: config.realtimeVoice && (options.realtimeVoice ?? config.realtimeVoiceOffered),
|
|
4426
|
+
error,
|
|
4427
|
+
finishingUploads: completion !== null,
|
|
4428
|
+
pending: pending ? { idempotencyKey: pending.key, realtimeModel: pending.realtimeModel } : null,
|
|
4429
|
+
retry: () => pendingRef.current ? deliver(pendingRef.current) : Promise.resolve(false),
|
|
4430
|
+
startRealtime
|
|
4431
|
+
};
|
|
4432
|
+
}
|
|
4433
|
+
|
|
4434
|
+
export {
|
|
4435
|
+
isTitleEvent,
|
|
4436
|
+
useSession,
|
|
4437
|
+
invokeOlderHistoryLoaderWithReceiptCapture,
|
|
4438
|
+
createOlderHistoryLoadReceipt,
|
|
4439
|
+
useTurnlessOutputGap,
|
|
4440
|
+
SESSION_EVENT_BROWSER_MAX_BYTES,
|
|
4441
|
+
SESSION_EVENT_BROWSER_MAX_COUNT,
|
|
4442
|
+
SESSION_EVENT_BROWSER_PENDING_MAX_BYTES,
|
|
4443
|
+
SESSION_EVENT_BROWSER_PENDING_MAX_COUNT,
|
|
4444
|
+
useSessionEvents,
|
|
4445
|
+
boundBrowserSessionEventWindow,
|
|
4446
|
+
isTurnQueueEvent,
|
|
4447
|
+
useTurnQueue,
|
|
4448
|
+
isGoalEvent,
|
|
4449
|
+
useGoal,
|
|
4450
|
+
useSessionControl,
|
|
4451
|
+
useAvailableModels,
|
|
4452
|
+
useWorkspaceModelCatalog,
|
|
4453
|
+
isHumanInputEvent,
|
|
4454
|
+
useHumanInputRequests,
|
|
4455
|
+
conversationTimeline,
|
|
4456
|
+
useSessionStartupTimeline,
|
|
4457
|
+
useSessionConversation,
|
|
4458
|
+
useSessionReadAcknowledgement,
|
|
4459
|
+
useNewConversation
|
|
4460
|
+
};
|
|
4461
|
+
//# sourceMappingURL=chunk-DF5EAHDN.js.map
|