@opengeni/react 3.1.0 → 3.1.3-canary.38890816741001
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/README.md +769 -22
- package/dist/accounts.js +89 -11
- package/dist/accounts.js.map +1 -1
- package/dist/approvals.d.ts +2 -1
- package/dist/artifacts-document.js +5 -3
- package/dist/artifacts-presentation.js +5 -3
- package/dist/artifacts-spreadsheet.d.ts +1 -1
- package/dist/artifacts-spreadsheet.js +5 -3
- package/dist/artifacts.d.ts +5 -1
- package/dist/artifacts.js +536 -42
- package/dist/artifacts.js.map +1 -1
- package/dist/browser-viewer-ZWR6MWJ7.js +19 -0
- package/dist/capability-catalog-row.d.ts +20 -0
- package/dist/capability-logo-fallback.d.ts +3 -0
- package/dist/chat-interactive-block-BO2IWXVK.js +14 -0
- package/dist/chunk-2DDJ6YH6.js +41 -0
- package/dist/chunk-2DDJ6YH6.js.map +1 -0
- package/dist/chunk-2HVLQ26H.js +183 -0
- package/dist/chunk-2HVLQ26H.js.map +1 -0
- package/dist/{chunk-RGALCTWO.js → chunk-2I6R3GCZ.js} +4285 -3980
- package/dist/chunk-2I6R3GCZ.js.map +1 -0
- package/dist/{chunk-D3UIAJKY.js → chunk-2SFFYIH3.js} +1179 -166
- package/dist/chunk-2SFFYIH3.js.map +1 -0
- package/dist/chunk-3PVDYTTB.js +24 -0
- package/dist/chunk-3PVDYTTB.js.map +1 -0
- package/dist/chunk-3UZCWK46.js +298 -0
- package/dist/chunk-3UZCWK46.js.map +1 -0
- package/dist/chunk-47CKUXTW.js +293 -0
- package/dist/chunk-47CKUXTW.js.map +1 -0
- package/dist/chunk-4QYP6QUI.js +109 -0
- package/dist/chunk-4QYP6QUI.js.map +1 -0
- package/dist/chunk-5EY26VZ7.js +1053 -0
- package/dist/chunk-5EY26VZ7.js.map +1 -0
- package/dist/chunk-5I4VRWBB.js +56 -0
- package/dist/chunk-5I4VRWBB.js.map +1 -0
- package/dist/chunk-5WOR7UCL.js +191 -0
- package/dist/chunk-5WOR7UCL.js.map +1 -0
- package/dist/chunk-5YJLGOM5.js +51 -0
- package/dist/chunk-5YJLGOM5.js.map +1 -0
- package/dist/chunk-6JFN3OWG.js +386 -0
- package/dist/chunk-6JFN3OWG.js.map +1 -0
- package/dist/chunk-7QJT5O4S.js +35 -0
- package/dist/chunk-7QJT5O4S.js.map +1 -0
- package/dist/chunk-A55KV72M.js +402 -0
- package/dist/chunk-A55KV72M.js.map +1 -0
- package/dist/chunk-ADWCLI4G.js +2028 -0
- package/dist/chunk-ADWCLI4G.js.map +1 -0
- package/dist/chunk-B27E3V37.js +29 -0
- package/dist/chunk-B27E3V37.js.map +1 -0
- package/dist/chunk-B7TC2A33.js +83 -0
- package/dist/chunk-B7TC2A33.js.map +1 -0
- package/dist/chunk-BLL56F7R.js +385 -0
- package/dist/chunk-BLL56F7R.js.map +1 -0
- package/dist/chunk-BOFEQC6K.js +162 -0
- package/dist/chunk-BOFEQC6K.js.map +1 -0
- package/dist/chunk-CM7LVUKC.js +35 -0
- package/dist/chunk-CM7LVUKC.js.map +1 -0
- package/dist/chunk-CORUP6JB.js +122 -0
- package/dist/chunk-CORUP6JB.js.map +1 -0
- package/dist/chunk-CSX5FFKZ.js +35 -0
- package/dist/chunk-CSX5FFKZ.js.map +1 -0
- package/dist/chunk-EGVHJKGJ.js +3125 -0
- package/dist/chunk-EGVHJKGJ.js.map +1 -0
- package/dist/{chunk-NEXLLTP4.js → chunk-G76LSA76.js} +6 -5
- package/dist/chunk-G76LSA76.js.map +1 -0
- package/dist/{chunk-PEI6RANI.js → chunk-GQMRL52F.js} +311 -75
- package/dist/chunk-GQMRL52F.js.map +1 -0
- package/dist/{chunk-ELIYWBZR.js → chunk-GUUXOISA.js} +1113 -457
- package/dist/chunk-GUUXOISA.js.map +1 -0
- package/dist/{chunk-HUIKIDM5.js → chunk-I7X2P4FU.js} +225 -116
- package/dist/chunk-I7X2P4FU.js.map +1 -0
- package/dist/chunk-IGFZFLI2.js +1390 -0
- package/dist/chunk-IGFZFLI2.js.map +1 -0
- package/dist/{chunk-4MDLEHXL.js → chunk-IMSUVYQB.js} +26 -13
- package/dist/chunk-IMSUVYQB.js.map +1 -0
- package/dist/chunk-IOYHDEP3.js +38 -0
- package/dist/chunk-IOYHDEP3.js.map +1 -0
- package/dist/chunk-IZV5FTJC.js +55 -0
- package/dist/chunk-IZV5FTJC.js.map +1 -0
- package/dist/chunk-KF33GTWN.js +343 -0
- package/dist/chunk-KF33GTWN.js.map +1 -0
- package/dist/chunk-KSGQ5ODA.js +457 -0
- package/dist/chunk-KSGQ5ODA.js.map +1 -0
- package/dist/{chunk-OAM4WAAM.js → chunk-KZC6QRNR.js} +1513 -697
- package/dist/chunk-KZC6QRNR.js.map +1 -0
- package/dist/chunk-LJUMYUBO.js +218 -0
- package/dist/chunk-LJUMYUBO.js.map +1 -0
- package/dist/chunk-LSW2OJVV.js +380 -0
- package/dist/chunk-LSW2OJVV.js.map +1 -0
- package/dist/chunk-M357MHH3.js +2 -0
- package/dist/chunk-M357MHH3.js.map +1 -0
- package/dist/chunk-MK2TQO4Y.js +24 -0
- package/dist/chunk-MK2TQO4Y.js.map +1 -0
- package/dist/chunk-N7S72EXK.js +25 -0
- package/dist/chunk-N7S72EXK.js.map +1 -0
- package/dist/{chunk-XXWH2JLC.js → chunk-NKMYZFHA.js} +616 -621
- package/dist/chunk-NKMYZFHA.js.map +1 -0
- package/dist/chunk-NX4ADH73.js +14 -0
- package/dist/chunk-NX4ADH73.js.map +1 -0
- package/dist/chunk-O7F6UJO4.js +71 -0
- package/dist/chunk-O7F6UJO4.js.map +1 -0
- package/dist/chunk-OVB5WIG5.js +14064 -0
- package/dist/chunk-OVB5WIG5.js.map +1 -0
- package/dist/chunk-OYQQJL3J.js +16 -0
- package/dist/chunk-OYQQJL3J.js.map +1 -0
- package/dist/{chunk-H5YGYNXS.js → chunk-P4BNUO57.js} +60 -17
- package/dist/chunk-P4BNUO57.js.map +1 -0
- package/dist/{chunk-JVYQRRC4.js → chunk-PEHTX3V7.js} +21 -1
- package/dist/chunk-PEHTX3V7.js.map +1 -0
- package/dist/chunk-PMV53IMM.js +26 -0
- package/dist/chunk-PMV53IMM.js.map +1 -0
- package/dist/{chunk-KCGXPP5V.js → chunk-PYEOPA4M.js} +13 -7
- package/dist/chunk-PYEOPA4M.js.map +1 -0
- package/dist/{chunk-KZ73RL76.js → chunk-QHUE7E4N.js} +8 -3
- package/dist/chunk-QHUE7E4N.js.map +1 -0
- package/dist/chunk-QV3DJVRZ.js +11 -0
- package/dist/chunk-QV3DJVRZ.js.map +1 -0
- package/dist/chunk-R5SBNG7S.js +810 -0
- package/dist/chunk-R5SBNG7S.js.map +1 -0
- package/dist/chunk-RI42H5IU.js +326 -0
- package/dist/chunk-RI42H5IU.js.map +1 -0
- package/dist/chunk-S2QH6EZ5.js +211 -0
- package/dist/chunk-S2QH6EZ5.js.map +1 -0
- package/dist/chunk-SHLZMAJN.js +304 -0
- package/dist/chunk-SHLZMAJN.js.map +1 -0
- package/dist/chunk-TGXK4BAZ.js +293 -0
- package/dist/chunk-TGXK4BAZ.js.map +1 -0
- package/dist/{chunk-5YK2ZDIG.js → chunk-TNEZNMSN.js} +4 -10
- package/dist/chunk-TNEZNMSN.js.map +1 -0
- package/dist/{chunk-22KP6LR5.js → chunk-TXS65BB2.js} +3 -1
- package/dist/{chunk-22KP6LR5.js.map → chunk-TXS65BB2.js.map} +1 -1
- package/dist/chunk-UNJPZGNK.js +63 -0
- package/dist/chunk-UNJPZGNK.js.map +1 -0
- package/dist/{chunk-MZ2G2XBA.js → chunk-V5ZUJT2T.js} +205 -45
- package/dist/chunk-V5ZUJT2T.js.map +1 -0
- package/dist/chunk-VIUEOHXY.js +394 -0
- package/dist/chunk-VIUEOHXY.js.map +1 -0
- package/dist/chunk-WIUKB3LD.js +1911 -0
- package/dist/chunk-WIUKB3LD.js.map +1 -0
- package/dist/{chunk-ICA6R6TW.js → chunk-WNN7NO2N.js} +1451 -355
- package/dist/chunk-WNN7NO2N.js.map +1 -0
- package/dist/{chunk-YJFGAFBD.js → chunk-X5UVUIMF.js} +1892 -1223
- package/dist/chunk-X5UVUIMF.js.map +1 -0
- package/dist/chunk-XDA5QJE7.js +402 -0
- package/dist/chunk-XDA5QJE7.js.map +1 -0
- package/dist/{chunk-VZTQ73XT.js → chunk-XEA5BCMA.js} +3 -1
- package/dist/{chunk-VZTQ73XT.js.map → chunk-XEA5BCMA.js.map} +1 -1
- package/dist/chunk-XHF7OYAN.js +1150 -0
- package/dist/chunk-XHF7OYAN.js.map +1 -0
- package/dist/chunk-YSRBFJMQ.js +43 -0
- package/dist/chunk-YSRBFJMQ.js.map +1 -0
- package/dist/chunk-ZOZAPAUO.js +174 -0
- package/dist/chunk-ZOZAPAUO.js.map +1 -0
- package/dist/chunk-ZQV5W7YC.js +809 -0
- package/dist/chunk-ZQV5W7YC.js.map +1 -0
- package/dist/chunk-ZUWF4NF7.js +13 -0
- package/dist/chunk-ZUWF4NF7.js.map +1 -0
- package/dist/client.d.ts +16 -7
- package/dist/clipboard.js +3 -1
- package/dist/commands/types.d.ts +2 -0
- package/dist/components/anthropic-mark.d.ts +3 -0
- package/dist/components/approval-surface.d.ts +19 -6
- package/dist/components/artifacts/artifact-chrome.d.ts +120 -0
- package/dist/components/artifacts/artifact-sandbox.d.ts +30 -0
- package/dist/components/artifacts/artifact-surface.d.ts +4 -2
- package/dist/components/artifacts/chat-interactive-block.d.ts +29 -0
- package/dist/components/artifacts/deferred-chat-media.d.ts +15 -0
- package/dist/components/artifacts/document-editor.d.ts +4 -2
- package/dist/components/artifacts/editable-artifact-ui.d.ts +2 -1
- package/dist/components/artifacts/editable-artifact-view.d.ts +48 -0
- package/dist/components/artifacts/editable-document.d.ts +1 -1
- package/dist/components/artifacts/editable-presentation.d.ts +2 -2
- package/dist/components/artifacts/editable-spreadsheet.d.ts +8 -3
- package/dist/components/artifacts/index.d.ts +9 -2
- package/dist/components/artifacts/inline-html-document.d.ts +2 -0
- package/dist/components/artifacts/latest-task-runner.d.ts +15 -0
- package/dist/components/artifacts/presentation-editor.d.ts +6 -2
- package/dist/components/artifacts/published-html-artifact-frame.d.ts +23 -3
- package/dist/components/artifacts/session-artifact-viewer.d.ts +39 -0
- package/dist/components/artifacts/site-view.d.ts +26 -0
- package/dist/components/artifacts/spreadsheet-command-queue.d.ts +20 -0
- package/dist/components/artifacts/spreadsheet-download.d.ts +7 -0
- package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
- package/dist/components/artifacts/spreadsheet-grid.d.ts +10 -3
- package/dist/components/artifacts/spreadsheet-resize.d.ts +50 -0
- package/dist/components/artifacts/spreadsheet-submissions.d.ts +9 -0
- package/dist/components/artifacts/visualization-assets.gen.d.ts +2 -0
- package/dist/components/browser-select-control.d.ts +16 -0
- package/dist/components/browser-viewer.d.ts +11 -2
- package/dist/components/chat-composer.d.ts +21 -3
- package/dist/components/child-session-link.d.ts +9 -0
- package/dist/components/claude-mark.d.ts +3 -0
- package/dist/components/code-editor.d.ts +1 -4
- package/dist/components/composer-transcription-control.d.ts +7 -0
- package/dist/components/composer.d.ts +34 -67
- package/dist/components/computer-viewer.d.ts +1 -1
- package/dist/components/copy-button.d.ts +3 -1
- package/dist/components/desktop-viewer.d.ts +1 -1
- package/dist/components/fleet-tile.d.ts +2 -2
- package/dist/components/grok-mark.d.ts +26 -0
- package/dist/components/human-input-form.d.ts +28 -32
- package/dist/components/human-input-surface.d.ts +7 -2
- package/dist/components/linkified-text.d.ts +18 -0
- package/dist/components/machine-input-display.d.ts +22 -1
- package/dist/components/machines/agent-status-icon.d.ts +6 -0
- package/dist/components/machines/machine-detail.d.ts +7 -1
- package/dist/components/machines/machine-status.d.ts +48 -0
- package/dist/components/markdown-table-layout.d.ts +18 -0
- package/dist/components/markdown.d.ts +37 -4
- package/dist/components/message-timeline.d.ts +86 -7
- package/dist/components/model-mark.d.ts +29 -0
- package/dist/components/model-policy-picker-menu.d.ts +9 -0
- package/dist/components/model-policy-picker.d.ts +41 -25
- package/dist/components/new-conversation.d.ts +32 -0
- package/dist/components/open-geni-chat.d.ts +73 -0
- package/dist/components/open-geni-links.d.ts +68 -0
- package/dist/components/openrouter-mark.d.ts +6 -0
- package/dist/components/opper-mark.d.ts +7 -0
- package/dist/components/preview-loading.d.ts +2 -0
- package/dist/components/provider-recovery-notice.d.ts +16 -0
- package/dist/components/queue-item-content.d.ts +9 -0
- package/dist/components/rehype-secret-refs.d.ts +23 -0
- package/dist/components/remark-soft-line-breaks.d.ts +38 -0
- package/dist/components/sandbox-files.d.ts +3 -1
- package/dist/components/sandbox-workspace.d.ts +18 -0
- package/dist/components/secret-question-field.d.ts +31 -0
- package/dist/components/secret-ref-chip.d.ts +35 -0
- package/dist/components/session-capability-frame.d.ts +37 -0
- package/dist/components/session-chrome.d.ts +57 -8
- package/dist/components/session-commands-panel.d.ts +6 -0
- package/dist/components/session-commands.d.ts +6 -0
- package/dist/components/session-connection-request.d.ts +8 -0
- package/dist/components/session-conversation.d.ts +157 -0
- package/dist/components/session-list.d.ts +39 -0
- package/dist/components/session-mcp-capability-card.d.ts +41 -0
- package/dist/components/session-proxy-scope.d.ts +35 -0
- package/dist/components/session-status.d.ts +4 -1
- package/dist/components/session-usage-limit-notice.d.ts +39 -0
- package/dist/components/timeline-anchor.d.ts +24 -0
- package/dist/components/timeline-annotation-chrome.d.ts +25 -0
- package/dist/components/timeline-annotation-layout.d.ts +42 -0
- package/dist/components/timeline-annotation-markers.d.ts +8 -0
- package/dist/components/timeline-annotation-reveal-context.d.ts +4 -0
- package/dist/components/timeline-annotation-shared.d.ts +31 -0
- package/dist/components/timeline-annotations-dialog.d.ts +3 -2
- package/dist/components/timeline-annotations.d.ts +13 -3
- package/dist/components/timeline-reading-progress.d.ts +11 -0
- package/dist/components/timeline-search.d.ts +34 -0
- package/dist/components/timeline-settlement.d.ts +16 -0
- package/dist/components/tip-follow.d.ts +16 -0
- package/dist/components/tool-action-review.d.ts +47 -0
- package/dist/components/tool-review-history.d.ts +25 -0
- package/dist/components/usage-limit-notice.d.ts +49 -0
- package/dist/components/usage-member-list.d.ts +91 -0
- package/dist/components/usage-meter.d.ts +73 -0
- package/dist/components/use-viewer-menu-dismiss.d.ts +2 -0
- package/dist/components/user-message-body.d.ts +10 -1
- package/dist/composer-messages.d.ts +74 -0
- package/dist/composer-messages.js +9 -0
- package/dist/composer-messages.js.map +1 -0
- package/dist/composer-voice-events.d.ts +8 -0
- package/dist/composer.d.ts +5 -4
- package/dist/composer.js +26 -13
- package/dist/computer-viewer-GMLDXPON.js +20 -0
- package/dist/computer-viewer-GMLDXPON.js.map +1 -0
- package/dist/connect-account-identity.d.ts +8 -0
- package/dist/connect-accounts.d.ts +15 -0
- package/dist/connect-chooser.d.ts +18 -0
- package/dist/connect-panel.d.ts +18 -0
- package/dist/connect-setup.d.ts +18 -0
- package/dist/connect.d.ts +21 -0
- package/dist/connect.js +2149 -0
- package/dist/connect.js.map +1 -0
- package/dist/connection-catalog.d.ts +38 -0
- package/dist/connection-discovery.d.ts +12 -0
- package/dist/connection-installed.d.ts +17 -0
- package/dist/connection-logo.d.ts +9 -0
- package/dist/connection-service-logo.d.ts +8 -0
- package/dist/connection-service-presentation.d.ts +13 -0
- package/dist/connection-skeleton.d.ts +5 -0
- package/dist/connection-type-picker.d.ts +6 -0
- package/dist/conversation-timeline.d.ts +5 -0
- package/dist/desktop.d.ts +7 -0
- package/dist/desktop.js +27 -0
- package/dist/desktop.js.map +1 -0
- package/dist/device-authorization.d.ts +23 -0
- package/dist/diffs.d.ts +8 -0
- package/dist/diffs.js +15 -0
- package/dist/diffs.js.map +1 -0
- package/dist/discovery-cache.d.ts +5 -0
- package/dist/editor.d.ts +7 -0
- package/dist/editor.js +22 -0
- package/dist/editor.js.map +1 -0
- package/dist/embedded-new-voice-OCRNT6U5.js +18 -0
- package/dist/embedded-new-voice-OCRNT6U5.js.map +1 -0
- package/dist/embedded-voice-RPHPYVBT.js +18 -0
- package/dist/embedded-voice-RPHPYVBT.js.map +1 -0
- package/dist/{fleet-decision-row-GGCAT4E4.js → fleet-decision-row-SI63O5V4.js} +4 -1
- package/dist/fleet-decision-row-SI63O5V4.js.map +1 -0
- package/dist/hooks/latest-question.d.ts +25 -0
- package/dist/hooks/shared-machines-list.d.ts +4 -0
- package/dist/hooks/shared-query.d.ts +113 -0
- package/dist/hooks/use-attached-browsers.d.ts +1 -1
- package/dist/hooks/use-auth-runs.d.ts +1 -4
- package/dist/hooks/use-available-models.d.ts +1 -1
- package/dist/hooks/use-browser-frame-stream.d.ts +1 -1
- package/dist/hooks/use-browser-session.d.ts +12 -1
- package/dist/hooks/use-client-config-flags.d.ts +49 -0
- package/dist/hooks/use-codex-accounts.d.ts +59 -10
- package/dist/hooks/use-composer.d.ts +24 -2
- package/dist/hooks/use-computer-frame-stream.d.ts +1 -1
- package/dist/hooks/use-computer-input-posture.d.ts +13 -0
- package/dist/hooks/use-computer-session.d.ts +4 -0
- package/dist/hooks/use-connect.d.ts +23 -0
- package/dist/hooks/use-desktop-stream.d.ts +1 -1
- package/dist/hooks/use-file-attachments.d.ts +12 -0
- package/dist/hooks/use-goal.d.ts +2 -1
- package/dist/hooks/use-last-started-turn-policy.d.ts +5 -1
- package/dist/hooks/use-new-conversation.d.ts +56 -0
- package/dist/hooks/use-new-session-draft.d.ts +74 -0
- package/dist/hooks/use-realtime-voice-models.d.ts +7 -0
- package/dist/hooks/use-sandbox-files.d.ts +4 -1
- package/dist/hooks/use-session-conversation.d.ts +49 -0
- package/dist/hooks/use-session-events.d.ts +28 -7
- package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
- package/dist/hooks/use-session-model-account.d.ts +48 -0
- package/dist/hooks/use-supergrok-accounts.d.ts +7 -1
- package/dist/hooks/use-terminal-stream.d.ts +11 -5
- package/dist/hooks/use-usage.d.ts +40 -0
- package/dist/hooks/use-vaults.d.ts +53 -0
- package/dist/hooks/use-voice-input.d.ts +8 -1
- package/dist/hooks/use-workspace-edit.d.ts +4 -0
- package/dist/hooks/use-workspace-sessions.d.ts +22 -5
- package/dist/human-input-model.d.ts +122 -0
- package/dist/identity-link-accounts.d.ts +11 -0
- package/dist/identity-link-consent.d.ts +18 -0
- package/dist/index.d.ts +82 -20
- package/dist/index.js +2350 -6605
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +1 -1
- package/dist/interaction.js +14 -24
- package/dist/interaction.js.map +1 -1
- package/dist/latest-question-R77KWSUP.js +118 -0
- package/dist/latest-question-R77KWSUP.js.map +1 -0
- package/dist/lib/clipboard.d.ts +7 -0
- package/dist/lib/error-message.d.ts +7 -0
- package/dist/lib/format.d.ts +58 -4
- package/dist/lib/host-theme.d.ts +77 -0
- package/dist/lib/interaction-errors.d.ts +3 -0
- package/dist/lib/menu-styles.d.ts +22 -0
- package/dist/lib/notify-observer.d.ts +2 -0
- package/dist/lib/pierre-diffs-loader.d.ts +20 -0
- package/dist/lib/provider-recovery.d.ts +55 -0
- package/dist/lib/scrollbars.d.ts +11 -0
- package/dist/lib/terminal-input-readiness.d.ts +11 -0
- package/dist/lib/transient-retry.d.ts +25 -0
- package/dist/lib/workbench-peers.d.ts +36 -0
- package/dist/lib/xterm-theme.d.ts +7 -5
- package/dist/machines.js +9 -5
- package/dist/markdown-table-layout-3PV2UAAH.js +113 -0
- package/dist/markdown-table-layout-3PV2UAAH.js.map +1 -0
- package/dist/mcp-connection-status.d.ts +14 -0
- package/dist/model-mark-paths.d.ts +34 -0
- package/dist/model-mark-paths.js +16 -0
- package/dist/model-mark-paths.js.map +1 -0
- package/dist/model-picker-model.d.ts +111 -0
- package/dist/model-picker-model.js +25 -0
- package/dist/model-picker-model.js.map +1 -0
- package/dist/model-policy-picker-menu-7GJ5URPR.js +17 -0
- package/dist/model-policy-picker-menu-7GJ5URPR.js.map +1 -0
- package/dist/model-policy.d.ts +30 -3
- package/dist/model-policy.js +14 -1
- package/dist/native-previews.d.ts +28 -0
- package/dist/native-previews.js +82 -0
- package/dist/native-previews.js.map +1 -0
- package/dist/new-session-draft.d.ts +7 -0
- package/dist/new-session-draft.js +639 -0
- package/dist/new-session-draft.js.map +1 -0
- package/dist/notification-text.d.ts +27 -0
- package/dist/{noto-sans-arabic-loader-IA2T4X2A.js → noto-sans-arabic-loader-XSH2KYWJ.js} +3 -1
- package/dist/{noto-sans-arabic-loader-IA2T4X2A.js.map → noto-sans-arabic-loader-XSH2KYWJ.js.map} +1 -1
- package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js → noto-sans-jp-loader.generated-MR5L6V7K.js} +3 -1
- package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js.map → noto-sans-jp-loader.generated-MR5L6V7K.js.map} +1 -1
- package/dist/older-history.d.ts +3 -2
- package/dist/organization-model.d.ts +32 -0
- package/dist/organization-model.js +65 -0
- package/dist/organization-model.js.map +1 -0
- package/dist/{platform-activity-row-BJHUHXAT.js → platform-activity-row-5OCKOQWZ.js} +16 -78
- package/dist/platform-activity-row-5OCKOQWZ.js.map +1 -0
- package/dist/plugin-details.d.ts +14 -0
- package/dist/plugin-discovery.d.ts +25 -0
- package/dist/preview-loading-paint.d.ts +20 -0
- package/dist/provider.d.ts +25 -2
- package/dist/queue-presentation-model.d.ts +72 -0
- package/dist/queue-presentation-model.js +38 -0
- package/dist/queue-presentation-model.js.map +1 -0
- package/dist/{queue-surface-implementation-BSKUWHRI.js → queue-surface-implementation-GJBH7NKO.js} +65 -265
- package/dist/queue-surface-implementation-GJBH7NKO.js.map +1 -0
- package/dist/realtime/embedded-new-voice.d.ts +1 -0
- package/dist/realtime/embedded-voice.d.ts +1 -0
- package/dist/realtime/realtime-control.d.ts +4 -65
- package/dist/realtime/session-realtime.d.ts +78 -0
- package/dist/realtime.js +25 -1294
- package/dist/realtime.js.map +1 -1
- package/dist/sandbox-files-O27IPOZW.js +21 -0
- package/dist/sandbox-files-O27IPOZW.js.map +1 -0
- package/dist/sandbox-label-model.d.ts +30 -0
- package/dist/sandbox-label-model.js +20 -0
- package/dist/sandbox-label-model.js.map +1 -0
- package/dist/scrollbars.d.ts +1 -0
- package/dist/scrollbars.js +10 -0
- package/dist/scrollbars.js.map +1 -0
- package/dist/secret-reference-label.d.ts +14 -0
- package/dist/secrets-ui.d.ts +57 -0
- package/dist/session-agents-model.d.ts +10 -0
- package/dist/session-agents-model.js +25 -0
- package/dist/session-agents-model.js.map +1 -0
- package/dist/session-attention-model.d.ts +8 -0
- package/dist/session-attention-model.js +47 -0
- package/dist/session-attention-model.js.map +1 -0
- package/dist/session-auth-recommendation.d.ts +5 -0
- package/dist/session-capability-policy.d.ts +31 -0
- package/dist/session-feedback-model.d.ts +20 -0
- package/dist/session-feedback-model.js +56 -0
- package/dist/session-feedback-model.js.map +1 -0
- package/dist/session-list-model.d.ts +138 -0
- package/dist/session-list-model.js +274 -0
- package/dist/session-list-model.js.map +1 -0
- package/dist/session-realtime-7DJOIHQW.js +21 -0
- package/dist/session-realtime-7DJOIHQW.js.map +1 -0
- package/dist/session-realtime.d.ts +7 -0
- package/dist/session-realtime.js +19 -0
- package/dist/session-realtime.js.map +1 -0
- package/dist/session-status-model.d.ts +36 -0
- package/dist/session-ui.d.ts +39 -1
- package/dist/session-ui.js +126 -12
- package/dist/session-ui.js.map +1 -1
- package/dist/session.d.ts +8 -2
- package/dist/session.js +35 -20
- package/dist/site-snapshot.d.ts +14 -0
- package/dist/sites-ui.d.ts +24 -0
- package/dist/sites.d.ts +3 -0
- package/dist/sites.js +14 -0
- package/dist/sites.js.map +1 -0
- package/dist/skill-discovery.d.ts +47 -0
- package/dist/terminal.d.ts +8 -0
- package/dist/terminal.js +32 -0
- package/dist/terminal.js.map +1 -0
- package/dist/testing/session-scenarios.d.ts +10 -0
- package/dist/testing/session-scenarios.js +367 -0
- package/dist/testing/session-scenarios.js.map +1 -0
- package/dist/timeline/activity-rail.d.ts +11 -2
- package/dist/timeline/agent-identity.d.ts +30 -0
- package/dist/timeline/agent-row.d.ts +31 -0
- package/dist/timeline/allowance-exhausted-row.d.ts +24 -0
- package/dist/timeline/compaction-copy.d.ts +46 -0
- package/dist/timeline/entrance.d.ts +13 -1
- package/dist/timeline/focus-sequence.d.ts +9 -0
- package/dist/timeline/genie-copy.d.ts +3 -0
- package/dist/timeline/genie-loading.d.ts +47 -0
- package/dist/timeline/index.d.ts +10 -5
- package/dist/timeline/knowledge-receipt.d.ts +17 -0
- package/dist/timeline/message-sender.d.ts +19 -0
- package/dist/timeline/notice-presentation.d.ts +13 -0
- package/dist/timeline/parsers.d.ts +11 -0
- package/dist/timeline/patch-apply-command.d.ts +18 -0
- package/dist/timeline/platform-activity-presentation.d.ts +21 -0
- package/dist/timeline/platform-activity-row.d.ts +1 -2
- package/dist/timeline/presented-image.d.ts +11 -0
- package/dist/timeline/projection.d.ts +31 -2
- package/dist/timeline/provider-logo.d.ts +15 -0
- package/dist/timeline/question-nav-model.d.ts +23 -0
- package/dist/timeline/registry.d.ts +12 -1
- package/dist/timeline/retained-image.d.ts +18 -0
- package/dist/timeline/retained-loaders.d.ts +27 -0
- package/dist/timeline/rolling-activity.d.ts +11 -0
- package/dist/timeline/screenshot-lightbox.d.ts +23 -1
- package/dist/timeline/session-usage-limit-row.d.ts +11 -0
- package/dist/timeline/shared.d.ts +4 -1
- package/dist/timeline/startup-dispatch-details.d.ts +5 -0
- package/dist/timeline/startup-preference.d.ts +3 -0
- package/dist/timeline/startup-timings.d.ts +4 -0
- package/dist/timeline/tool-display-name.d.ts +5 -2
- package/dist/timeline/tool-presentation.d.ts +172 -0
- package/dist/timeline/turn-summary-model.d.ts +95 -0
- package/dist/timeline/turn-summary.d.ts +20 -54
- package/dist/timeline/types.d.ts +179 -7
- package/dist/timeline/work-presentation.d.ts +60 -0
- package/dist/timeline-annotation-markers-U3G66KUD.js +166 -0
- package/dist/timeline-annotation-markers-U3G66KUD.js.map +1 -0
- package/dist/timeline-annotation-selection-BI7QTPTZ.js +242 -0
- package/dist/timeline-annotation-selection-BI7QTPTZ.js.map +1 -0
- package/dist/timeline-annotations-dialog-FOMHSDTE.js +274 -0
- package/dist/timeline-annotations-dialog-FOMHSDTE.js.map +1 -0
- package/dist/timeline-model.d.ts +29 -0
- package/dist/timeline-model.js +254 -0
- package/dist/timeline-model.js.map +1 -0
- package/dist/usage/allowance-copy.d.ts +25 -0
- package/dist/usage/session-usage-limit.d.ts +88 -0
- package/dist/usage/summary.d.ts +35 -0
- package/dist/usage.d.ts +17 -0
- package/dist/usage.js +1187 -0
- package/dist/usage.js.map +1 -0
- package/dist/voice-recording-owner.d.ts +7 -0
- package/package.json +107 -7
- package/src/accounts.tsx +116 -14
- package/src/approvals.ts +8 -1
- package/src/artifacts-spreadsheet.ts +1 -0
- package/src/artifacts.ts +44 -0
- package/src/assets/README.md +8 -0
- package/src/assets/gmail.ico +0 -0
- package/src/assets/outlook.ico +0 -0
- package/src/capability-catalog-row.tsx +94 -0
- package/src/capability-logo-fallback.ts +22 -0
- package/src/client.ts +20 -31
- package/src/commands/registry.ts +18 -15
- package/src/commands/types.ts +2 -0
- package/src/components/anthropic-mark.tsx +10 -0
- package/src/components/approval-surface.tsx +401 -138
- package/src/components/artifacts/artifact-chrome.tsx +413 -0
- package/src/components/artifacts/artifact-sandbox.tsx +189 -0
- package/src/components/artifacts/artifact-surface.tsx +40 -20
- package/src/components/artifacts/chat-interactive-block.tsx +260 -0
- package/src/components/artifacts/deferred-chat-media.tsx +67 -0
- package/src/components/artifacts/document-editor.tsx +141 -16
- package/src/components/artifacts/editable-artifact-ui.tsx +4 -2
- package/src/components/artifacts/editable-artifact-view.tsx +197 -0
- package/src/components/artifacts/editable-artifact-workbench.tsx +5 -2
- package/src/components/artifacts/editable-document.tsx +37 -36
- package/src/components/artifacts/editable-presentation.tsx +36 -18
- package/src/components/artifacts/editable-spreadsheet.tsx +543 -103
- package/src/components/artifacts/index.ts +45 -0
- package/src/components/artifacts/inline-html-document.ts +24 -0
- package/src/components/artifacts/latest-task-runner.ts +49 -0
- package/src/components/artifacts/presentation-editor.tsx +323 -67
- package/src/components/artifacts/published-html-artifact-frame.tsx +122 -5
- package/src/components/artifacts/session-artifact-viewer.tsx +378 -0
- package/src/components/artifacts/site-view.tsx +121 -0
- package/src/components/artifacts/spreadsheet-canvas.ts +5 -10
- package/src/components/artifacts/spreadsheet-command-queue.ts +184 -0
- package/src/components/artifacts/spreadsheet-download.tsx +109 -0
- package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
- package/src/components/artifacts/spreadsheet-grid.tsx +344 -144
- package/src/components/artifacts/spreadsheet-resize.tsx +421 -0
- package/src/components/artifacts/spreadsheet-submissions.tsx +46 -0
- package/src/components/artifacts/visualization-assets/base.css +1015 -0
- package/src/components/artifacts/visualization-assets/helpers.html +349 -0
- package/src/components/artifacts/visualization-assets.gen.ts +3 -0
- package/src/components/browser-select-control.tsx +312 -0
- package/src/components/browser-viewer.tsx +778 -318
- package/src/components/chat-composer.tsx +47 -5
- package/src/components/child-session-link.tsx +36 -0
- package/src/components/claude-mark.tsx +11 -0
- package/src/components/code-editor.tsx +13 -16
- package/src/components/composer-transcription-control.tsx +128 -30
- package/src/components/composer.tsx +404 -195
- package/src/components/computer-viewer.tsx +880 -253
- package/src/components/copy-button.tsx +13 -5
- package/src/components/desktop-viewer.tsx +1 -1
- package/src/components/enrollment-consent.tsx +1 -1
- package/src/components/enrollment-device-flow.tsx +1 -1
- package/src/components/file-browser.tsx +10 -14
- package/src/components/fleet-tile.tsx +9 -18
- package/src/components/generated-video-player.tsx +4 -2
- package/src/components/grok-mark.tsx +34 -0
- package/src/components/human-input-form.tsx +590 -283
- package/src/components/human-input-surface.tsx +11 -0
- package/src/components/interaction-intervention-banner.tsx +2 -2
- package/src/components/linkified-text.tsx +79 -0
- package/src/components/machine-card.tsx +35 -78
- package/src/components/machine-dock-bar.tsx +1 -1
- package/src/components/machine-input-display.ts +197 -3
- package/src/components/machines/agent-status-icon.tsx +31 -0
- package/src/components/machines/machine-detail.tsx +200 -31
- package/src/components/machines/machine-operation-policy.tsx +1 -1
- package/src/components/machines/machine-status.ts +241 -0
- package/src/components/machines-dashboard.tsx +2 -17
- package/src/components/markdown-table-layout.ts +143 -0
- package/src/components/markdown.tsx +466 -129
- package/src/components/message-timeline.tsx +2567 -728
- package/src/components/model-mark.tsx +102 -0
- package/src/components/model-picker.tsx +15 -4
- package/src/components/model-policy-picker-menu.tsx +371 -0
- package/src/components/model-policy-picker.tsx +256 -412
- package/src/components/new-conversation.tsx +221 -0
- package/src/components/open-geni-chat.tsx +309 -0
- package/src/components/open-geni-links.tsx +179 -0
- package/src/components/openrouter-mark.tsx +13 -0
- package/src/components/opper-mark.tsx +18 -0
- package/src/components/pierre-diff.tsx +41 -16
- package/src/components/pierre-file.tsx +35 -10
- package/src/components/preview-loading.tsx +109 -0
- package/src/components/provider-recovery-notice.tsx +49 -0
- package/src/components/queue-item-content.ts +14 -0
- package/src/components/queue-surface-implementation.tsx +51 -356
- package/src/components/queue-surface-state.tsx +3 -1
- package/src/components/rehype-secret-refs.ts +98 -0
- package/src/components/remark-soft-line-breaks.ts +166 -0
- package/src/components/sandbox-files.tsx +91 -15
- package/src/components/sandbox-terminal.tsx +84 -13
- package/src/components/sandbox-workspace.tsx +147 -51
- package/src/components/secret-question-field.tsx +324 -0
- package/src/components/secret-ref-chip.tsx +185 -0
- package/src/components/session-capability-frame.tsx +197 -0
- package/src/components/session-chrome.tsx +842 -251
- package/src/components/session-commands-panel.tsx +126 -0
- package/src/components/session-commands.tsx +18 -0
- package/src/components/session-connection-request.tsx +97 -0
- package/src/components/session-conversation.tsx +876 -0
- package/src/components/session-list.tsx +257 -0
- package/src/components/session-mcp-capability-card.tsx +680 -0
- package/src/components/session-proxy-scope.tsx +153 -0
- package/src/components/session-status.tsx +26 -17
- package/src/components/session-usage-limit-notice.tsx +174 -0
- package/src/components/timeline-anchor.tsx +119 -0
- package/src/components/timeline-annotation-chrome.tsx +196 -0
- package/src/components/timeline-annotation-layout.ts +268 -0
- package/src/components/timeline-annotation-markers.tsx +185 -0
- package/src/components/timeline-annotation-reveal-context.ts +9 -0
- package/src/components/timeline-annotation-selection.tsx +106 -54
- package/src/components/timeline-annotation-shared.ts +245 -0
- package/src/components/timeline-annotations-dialog.tsx +185 -128
- package/src/components/timeline-annotations.tsx +189 -45
- package/src/components/timeline-reading-progress.ts +80 -0
- package/src/components/timeline-search.tsx +168 -0
- package/src/components/timeline-settlement.ts +60 -0
- package/src/components/tip-follow.ts +52 -0
- package/src/components/tool-action-review.tsx +650 -0
- package/src/components/tool-review-history.tsx +88 -0
- package/src/components/tooltip.tsx +38 -5
- package/src/components/usage-limit-notice.tsx +179 -0
- package/src/components/usage-member-list.tsx +792 -0
- package/src/components/usage-meter.tsx +382 -0
- package/src/components/use-viewer-menu-dismiss.ts +33 -0
- package/src/components/user-message-body.tsx +27 -2
- package/src/components/workspace-dock.tsx +18 -1
- package/src/composer-messages.ts +150 -0
- package/src/composer-voice-events.ts +12 -0
- package/src/composer.ts +6 -1
- package/src/connect-account-identity.tsx +34 -0
- package/src/connect-accounts.tsx +233 -0
- package/src/connect-chooser.tsx +336 -0
- package/src/connect-panel.tsx +99 -0
- package/src/connect-setup.tsx +355 -0
- package/src/connect.ts +60 -0
- package/src/connection-assets.d.ts +4 -0
- package/src/connection-catalog.tsx +162 -0
- package/src/connection-discovery.tsx +238 -0
- package/src/connection-installed.tsx +51 -0
- package/src/connection-logo.tsx +37 -0
- package/src/connection-service-logo.tsx +55 -0
- package/src/connection-service-presentation.ts +46 -0
- package/src/connection-skeleton.tsx +16 -0
- package/src/connection-type-picker.tsx +36 -0
- package/src/conversation-timeline.ts +92 -0
- package/src/desktop.ts +24 -0
- package/src/device-authorization.tsx +98 -0
- package/src/diffs.ts +16 -0
- package/src/discovery-cache.ts +42 -0
- package/src/editor.ts +13 -0
- package/src/hooks/internal.ts +3 -2
- package/src/hooks/latest-question.ts +164 -0
- package/src/hooks/shared-machines-list.ts +34 -1
- package/src/hooks/shared-query.ts +721 -0
- package/src/hooks/use-attached-browsers.ts +1 -1
- package/src/hooks/use-auth-runs.ts +0 -27
- package/src/hooks/use-available-models.ts +21 -15
- package/src/hooks/use-browser-frame-stream.ts +19 -2
- package/src/hooks/use-browser-session.ts +680 -205
- package/src/hooks/use-browser-sessions.ts +32 -1
- package/src/hooks/use-client-config-flags.ts +118 -0
- package/src/hooks/use-codex-accounts.ts +247 -47
- package/src/hooks/use-composer.ts +276 -47
- package/src/hooks/use-computer-frame-stream.ts +25 -7
- package/src/hooks/use-computer-input-posture.ts +94 -0
- package/src/hooks/use-computer-session.ts +409 -95
- package/src/hooks/use-connect.ts +23 -0
- package/src/hooks/use-desktop-stream.ts +19 -28
- package/src/hooks/use-file-attachments.ts +80 -4
- package/src/hooks/use-goal.ts +107 -13
- package/src/hooks/use-last-started-turn-policy.ts +46 -14
- package/src/hooks/use-machines.ts +4 -0
- package/src/hooks/use-new-conversation.ts +374 -0
- package/src/hooks/use-new-session-draft.ts +813 -0
- package/src/hooks/use-realtime-voice-models.ts +46 -0
- package/src/hooks/use-sandbox-files.ts +68 -20
- package/src/hooks/use-session-background-commands.ts +22 -11
- package/src/hooks/use-session-capabilities.ts +3 -2
- package/src/hooks/use-session-conversation.ts +150 -0
- package/src/hooks/use-session-events.ts +703 -382
- package/src/hooks/use-session-lineage.ts +20 -16
- package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
- package/src/hooks/use-session-model-account.ts +174 -0
- package/src/hooks/use-session.ts +53 -21
- package/src/hooks/use-slash-commands.ts +6 -11
- package/src/hooks/use-supergrok-accounts.ts +22 -8
- package/src/hooks/use-terminal-stream.ts +153 -27
- package/src/hooks/use-transcription.ts +6 -0
- package/src/hooks/use-turn-queue.ts +2 -1
- package/src/hooks/use-usage.ts +100 -0
- package/src/hooks/use-vaults.ts +153 -0
- package/src/hooks/use-video-artifact-playback.ts +1 -1
- package/src/hooks/use-voice-input.ts +127 -19
- package/src/hooks/use-workspace-capture.ts +3 -2
- package/src/hooks/use-workspace-edit.ts +23 -5
- package/src/hooks/use-workspace-sessions.ts +97 -32
- package/src/human-input-model.ts +359 -0
- package/src/identity-link-accounts.tsx +148 -0
- package/src/identity-link-consent.tsx +192 -0
- package/src/index.ts +262 -16
- package/src/interaction.ts +1 -0
- package/src/lib/clipboard.ts +29 -0
- package/src/lib/error-message.ts +37 -0
- package/src/lib/format.ts +213 -16
- package/src/lib/host-theme.ts +529 -0
- package/src/lib/interaction-errors.ts +15 -0
- package/src/lib/menu-styles.ts +44 -0
- package/src/lib/notify-observer.ts +15 -0
- package/src/lib/pierre-diffs-loader.ts +53 -0
- package/src/lib/provider-recovery.ts +243 -0
- package/src/lib/scrollbars.ts +30 -0
- package/src/lib/terminal-input-readiness.ts +51 -0
- package/src/lib/transient-retry.ts +99 -0
- package/src/lib/workbench-peers.ts +62 -0
- package/src/lib/xterm-theme.ts +23 -21
- package/src/mcp-connection-status.ts +35 -0
- package/src/model-mark-paths.ts +53 -0
- package/src/model-picker-model.ts +330 -0
- package/src/model-policy.ts +299 -74
- package/src/native-previews.ts +100 -0
- package/src/new-session-draft.ts +22 -0
- package/src/notification-text.ts +81 -0
- package/src/older-history.ts +22 -4
- package/src/organization-model.ts +118 -0
- package/src/plugin-details.tsx +185 -0
- package/src/plugin-discovery.tsx +196 -0
- package/src/preview-loading-paint.ts +68 -0
- package/src/provider.tsx +74 -15
- package/src/queue-presentation-model.ts +464 -0
- package/src/realtime/dropdown-menu.tsx +19 -12
- package/src/realtime/embedded-new-voice.tsx +1 -0
- package/src/realtime/embedded-voice.tsx +3 -0
- package/src/realtime/realtime-control.tsx +138 -501
- package/src/realtime/session-realtime.ts +566 -0
- package/src/sandbox-label-model.ts +53 -0
- package/src/scrollbars.ts +3 -0
- package/src/secret-reference-label.ts +43 -0
- package/src/secrets-ui.tsx +59 -0
- package/src/session-agents-model.ts +33 -0
- package/src/session-attention-model.ts +62 -0
- package/src/session-auth-recommendation.ts +48 -0
- package/src/session-capability-policy.ts +196 -0
- package/src/session-context.ts +3 -4
- package/src/session-feedback-model.ts +95 -0
- package/src/session-list-model.ts +434 -0
- package/src/session-realtime.ts +18 -0
- package/src/session-status-model.ts +99 -0
- package/src/session-ui.ts +98 -1
- package/src/session.ts +17 -1
- package/src/site-snapshot.ts +74 -0
- package/src/sites-ui.tsx +348 -0
- package/src/sites.ts +3 -0
- package/src/skill-discovery.tsx +208 -0
- package/src/terminal.ts +26 -0
- package/src/testing/session-scenarios.ts +404 -0
- package/src/timeline/activity-rail.tsx +242 -101
- package/src/timeline/agent-identity.tsx +84 -0
- package/src/timeline/agent-row.tsx +231 -0
- package/src/timeline/allowance-exhausted-row.tsx +86 -0
- package/src/timeline/compaction-copy.ts +115 -0
- package/src/timeline/entrance.tsx +28 -3
- package/src/timeline/fleet-decision-projection.ts +2 -1
- package/src/timeline/fleet-decision-row.tsx +1 -0
- package/src/timeline/focus-sequence.ts +44 -0
- package/src/timeline/genie-copy.ts +19 -0
- package/src/timeline/genie-loading.tsx +143 -0
- package/src/timeline/index.ts +30 -2
- package/src/timeline/knowledge-receipt.tsx +100 -0
- package/src/timeline/message-sender.tsx +71 -0
- package/src/timeline/notice-presentation.ts +48 -0
- package/src/timeline/parsers.ts +42 -2
- package/src/timeline/patch-apply-command.tsx +135 -0
- package/src/timeline/platform-activity-presentation.ts +166 -0
- package/src/timeline/platform-activity-row.tsx +15 -90
- package/src/timeline/presented-image.ts +63 -0
- package/src/timeline/projection.ts +1746 -76
- package/src/timeline/provider-logo.tsx +53 -0
- package/src/timeline/question-nav-model.ts +28 -0
- package/src/timeline/registry.ts +26 -4
- package/src/timeline/retained-image.ts +97 -0
- package/src/timeline/retained-loaders.ts +91 -0
- package/src/timeline/rolling-activity.tsx +80 -0
- package/src/timeline/screenshot-lightbox.tsx +228 -5
- package/src/timeline/session-usage-limit-row.tsx +42 -0
- package/src/timeline/shared.tsx +42 -15
- package/src/timeline/startup-dispatch-details.tsx +36 -0
- package/src/timeline/startup-preference.ts +33 -0
- package/src/timeline/startup-timings.tsx +145 -0
- package/src/timeline/tool-display-name.ts +19 -7
- package/src/timeline/tool-presentation.ts +1316 -0
- package/src/timeline/tool-renderers.tsx +835 -1138
- package/src/timeline/turn-summary-model.ts +343 -0
- package/src/timeline/turn-summary.tsx +272 -365
- package/src/timeline/types.ts +189 -6
- package/src/timeline/work-presentation.ts +189 -0
- package/src/timeline-model.ts +143 -0
- package/src/usage/allowance-copy.ts +64 -0
- package/src/usage/session-usage-limit.ts +266 -0
- package/src/usage/summary.ts +93 -0
- package/src/usage.ts +70 -0
- package/src/voice-recording-owner.ts +22 -0
- package/styles/compiled.css +2319 -748
- package/styles/connect.css +290 -0
- package/styles/connect.d.ts +1 -0
- package/styles/effective-tokens.css +1 -0
- package/styles/index.css +309 -9
- package/styles/responsive.css +99 -7
- package/styles/session-capability.css +44 -0
- package/styles/tokens.css +134 -66
- package/dist/browser-viewer-AEFHUYZK.js +0 -14
- package/dist/chunk-4MDLEHXL.js.map +0 -1
- package/dist/chunk-5YK2ZDIG.js.map +0 -1
- package/dist/chunk-D3UIAJKY.js.map +0 -1
- package/dist/chunk-ELIYWBZR.js.map +0 -1
- package/dist/chunk-H5YGYNXS.js.map +0 -1
- package/dist/chunk-HQVCKCLG.js +0 -9458
- package/dist/chunk-HQVCKCLG.js.map +0 -1
- package/dist/chunk-HUIKIDM5.js.map +0 -1
- package/dist/chunk-ICA6R6TW.js.map +0 -1
- package/dist/chunk-JVYQRRC4.js.map +0 -1
- package/dist/chunk-KCGXPP5V.js.map +0 -1
- package/dist/chunk-KZ73RL76.js.map +0 -1
- package/dist/chunk-LAKLF4PA.js +0 -141
- package/dist/chunk-LAKLF4PA.js.map +0 -1
- package/dist/chunk-MZ2G2XBA.js.map +0 -1
- package/dist/chunk-NEXLLTP4.js.map +0 -1
- package/dist/chunk-OAM4WAAM.js.map +0 -1
- package/dist/chunk-PEI6RANI.js.map +0 -1
- package/dist/chunk-RGALCTWO.js.map +0 -1
- package/dist/chunk-W6NFJXSA.js +0 -454
- package/dist/chunk-W6NFJXSA.js.map +0 -1
- package/dist/chunk-WSK7G5LE.js +0 -170
- package/dist/chunk-WSK7G5LE.js.map +0 -1
- package/dist/chunk-X4A66EWZ.js +0 -223
- package/dist/chunk-X4A66EWZ.js.map +0 -1
- package/dist/chunk-XXWH2JLC.js.map +0 -1
- package/dist/chunk-YFKJ7VZH.js +0 -268
- package/dist/chunk-YFKJ7VZH.js.map +0 -1
- package/dist/chunk-YJFGAFBD.js.map +0 -1
- package/dist/computer-viewer-FWEZISW4.js +0 -16
- package/dist/fleet-decision-row-GGCAT4E4.js.map +0 -1
- package/dist/hooks/use-environments.d.ts +0 -41
- package/dist/hooks/use-packs.d.ts +0 -32
- package/dist/platform-activity-row-BJHUHXAT.js.map +0 -1
- package/dist/queue-surface-implementation-BSKUWHRI.js.map +0 -1
- package/dist/timeline-annotation-selection-3L7LHGG2.js +0 -187
- package/dist/timeline-annotation-selection-3L7LHGG2.js.map +0 -1
- package/dist/timeline-annotations-dialog-U7EVLR75.js +0 -196
- package/dist/timeline-annotations-dialog-U7EVLR75.js.map +0 -1
- package/src/hooks/use-environments.ts +0 -182
- package/src/hooks/use-packs.ts +0 -157
- /package/dist/{browser-viewer-AEFHUYZK.js.map → browser-viewer-ZWR6MWJ7.js.map} +0 -0
- /package/dist/{computer-viewer-FWEZISW4.js.map → chat-interactive-block-BO2IWXVK.js.map} +0 -0
|
@@ -1,133 +1,671 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import {
|
|
2
3
|
buildTimeline,
|
|
3
|
-
createOlderHistoryLoadReceipt,
|
|
4
4
|
groupTimeline,
|
|
5
|
-
isActionableHumanInputRequest,
|
|
6
5
|
sessionStatusFromEvents
|
|
7
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-2SFFYIH3.js";
|
|
7
|
+
import {
|
|
8
|
+
isActionableHumanInputRequest
|
|
9
|
+
} from "./chunk-IZV5FTJC.js";
|
|
10
|
+
import {
|
|
11
|
+
FILE_ONLY_MESSAGE_TEXT,
|
|
12
|
+
notifyObserver,
|
|
13
|
+
useComposer
|
|
14
|
+
} from "./chunk-V5ZUJT2T.js";
|
|
15
|
+
import {
|
|
16
|
+
coerceReasoningEffortForModel,
|
|
17
|
+
defaultEffortForModel,
|
|
18
|
+
projectPickerRows,
|
|
19
|
+
runnableLatencyModesForModel,
|
|
20
|
+
sortPickerRows
|
|
21
|
+
} from "./chunk-KF33GTWN.js";
|
|
8
22
|
import {
|
|
9
23
|
useDebouncedCallback,
|
|
10
24
|
useMutationRunner,
|
|
11
25
|
usePageLiveActivity,
|
|
12
26
|
usePolledValue,
|
|
13
27
|
useSessionEventTrigger
|
|
14
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-QHUE7E4N.js";
|
|
29
|
+
import {
|
|
30
|
+
normalizeError,
|
|
31
|
+
useErrorMessage
|
|
32
|
+
} from "./chunk-CM7LVUKC.js";
|
|
15
33
|
import {
|
|
16
34
|
useEmbeddedFileAttachments,
|
|
17
35
|
useEmbeddedGoal,
|
|
18
36
|
useEmbeddedHumanInputSession,
|
|
19
37
|
useEmbeddedSession,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
} from "./chunk-
|
|
38
|
+
useEmbeddedSessionRead,
|
|
39
|
+
useOpenGeni,
|
|
40
|
+
useOpenGeniClient
|
|
41
|
+
} from "./chunk-G76LSA76.js";
|
|
24
42
|
|
|
25
|
-
// src/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
// src/approvals.ts
|
|
44
|
+
import {
|
|
45
|
+
parseToolDisplayMetadata
|
|
46
|
+
} from "@opengeni/sdk";
|
|
47
|
+
function approvalsFromRequiresAction(payload) {
|
|
48
|
+
const approvals = payload && typeof payload === "object" ? payload.approvals : void 0;
|
|
49
|
+
if (!Array.isArray(approvals)) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
return approvals.map((approval, index) => {
|
|
53
|
+
const raw = approval && typeof approval === "object" ? approval : {};
|
|
54
|
+
const rawItem = raw.rawItem && typeof raw.rawItem === "object" ? raw.rawItem : {};
|
|
55
|
+
const display = parseToolDisplayMetadata(raw.display);
|
|
56
|
+
return {
|
|
57
|
+
// Mirror the runtime's canonical interruption identity before retaining
|
|
58
|
+
// the legacy top-level callId fallback for already-normalized payloads.
|
|
59
|
+
id: String(rawItem.callId ?? rawItem.id ?? raw.id ?? raw.callId ?? raw.name ?? index),
|
|
60
|
+
name: String(raw.name ?? raw.toolName ?? rawItem.name ?? "approval"),
|
|
61
|
+
...display ? { display } : {},
|
|
62
|
+
arguments: raw.arguments ?? rawItem.arguments,
|
|
63
|
+
raw: approval
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function projectPendingApprovals(events) {
|
|
68
|
+
let pending = [];
|
|
69
|
+
let owningTurnId = null;
|
|
70
|
+
for (const event of events) {
|
|
71
|
+
switch (event.type) {
|
|
72
|
+
case "session.requiresAction": {
|
|
73
|
+
pending = approvalsFromRequiresAction(event.payload);
|
|
74
|
+
owningTurnId = event.turnId ?? null;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case "user.approvalDecision": {
|
|
78
|
+
const payload = event.payload && typeof event.payload === "object" ? event.payload : {};
|
|
79
|
+
if (typeof payload.approvalId === "string") {
|
|
80
|
+
pending = pending.filter((approval) => approval.id !== payload.approvalId);
|
|
81
|
+
}
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
case "turn.completed":
|
|
85
|
+
case "turn.failed":
|
|
86
|
+
case "turn.cancelled": {
|
|
87
|
+
if (owningTurnId === null || event.turnId == null || event.turnId === owningTurnId) {
|
|
88
|
+
pending = [];
|
|
89
|
+
owningTurnId = null;
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
default:
|
|
94
|
+
break;
|
|
52
95
|
}
|
|
53
|
-
client[key] = method.bind(override === void 0 ? base : overrides);
|
|
54
96
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
97
|
+
return pending;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/older-history.ts
|
|
101
|
+
var activeReceiptCapture;
|
|
102
|
+
var preserveTailForAutomaticLoad = false;
|
|
103
|
+
function invokeOlderHistoryLoaderWithReceiptCapture(load, capture, preserveTail = false) {
|
|
104
|
+
const previousCapture = activeReceiptCapture;
|
|
105
|
+
const previousPreserveTail = preserveTailForAutomaticLoad;
|
|
106
|
+
activeReceiptCapture = capture;
|
|
107
|
+
preserveTailForAutomaticLoad = preserveTail;
|
|
108
|
+
try {
|
|
109
|
+
return load();
|
|
110
|
+
} finally {
|
|
111
|
+
activeReceiptCapture = previousCapture;
|
|
112
|
+
preserveTailForAutomaticLoad = previousPreserveTail;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function createOlderHistoryLoadReceipt(load) {
|
|
116
|
+
let resolveResult;
|
|
117
|
+
let rejectResult;
|
|
118
|
+
const result = new Promise((resolve, reject) => {
|
|
119
|
+
resolveResult = resolve;
|
|
120
|
+
rejectResult = reject;
|
|
121
|
+
});
|
|
122
|
+
result.committed = false;
|
|
123
|
+
result.tailPreserved = false;
|
|
124
|
+
activeReceiptCapture?.(result);
|
|
125
|
+
try {
|
|
126
|
+
const loaded = load(
|
|
127
|
+
() => {
|
|
128
|
+
result.committed = true;
|
|
129
|
+
},
|
|
130
|
+
preserveTailForAutomaticLoad,
|
|
131
|
+
() => {
|
|
132
|
+
result.tailPreserved = true;
|
|
133
|
+
}
|
|
61
134
|
);
|
|
135
|
+
Promise.resolve(loaded).then(resolveResult, rejectResult);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
rejectResult(error);
|
|
62
138
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/conversation-timeline.ts
|
|
143
|
+
function conversationTimeline(items, queue, composer) {
|
|
144
|
+
const queued = new Set(
|
|
145
|
+
queue.queue.filter((turn) => turn.metadata.delivery !== "steer").map((turn) => turn.triggerEventId)
|
|
146
|
+
);
|
|
147
|
+
const pending = composer.optimisticMessages ?? [];
|
|
148
|
+
const pendingQueue = new Set(
|
|
149
|
+
pending.filter(
|
|
150
|
+
(message) => message.destination === "queue" && !(message.turnId && message.appliedQueueVersion != null && queue.snapshot && queue.snapshot.version >= message.appliedQueueVersion && !queue.queue.some((turn) => turn.id === message.turnId))
|
|
151
|
+
).map((message) => `user-message:${message.clientEventId}`)
|
|
152
|
+
);
|
|
153
|
+
const visible = items.filter(
|
|
154
|
+
(item) => item.kind !== "user-message" || !queued.has(item.id) && !pendingQueue.has(item.reconciliationKey ?? "")
|
|
155
|
+
);
|
|
156
|
+
const keys = new Set(
|
|
157
|
+
visible.flatMap((item) => item.kind === "user-message" ? [item.reconciliationKey] : [])
|
|
158
|
+
);
|
|
159
|
+
const optimistic = pending.filter(
|
|
160
|
+
(message) => (message.destination === "chat" || message.state === "failed") && !keys.has(`user-message:${message.clientEventId}`) && !queue.queue.some((turn) => turn.id === message.turnId)
|
|
161
|
+
).map((message) => ({
|
|
162
|
+
kind: "user-message",
|
|
163
|
+
id: `optimistic:${message.clientEventId}`,
|
|
164
|
+
reconciliationKey: `user-message:${message.clientEventId}`,
|
|
165
|
+
text: message.text,
|
|
166
|
+
annotations: message.annotations.map((annotation, ordinal) => ({ ...annotation, ordinal })),
|
|
167
|
+
resources: message.resources,
|
|
168
|
+
tools: [],
|
|
169
|
+
occurredAt: message.occurredAt,
|
|
170
|
+
delivery: {
|
|
171
|
+
state: message.state,
|
|
172
|
+
...message.error ? { error: message.error } : {},
|
|
173
|
+
...message.state === "failed" ? {
|
|
174
|
+
...message.retryable !== false && composer.retryOptimisticMessage ? { onRetry: () => composer.retryOptimisticMessage?.(message.clientEventId) } : {},
|
|
175
|
+
...message.retryable === false && composer.restoreOptimisticMessage ? { onEdit: () => composer.restoreOptimisticMessage?.(message.clientEventId) } : {},
|
|
176
|
+
onRemove: () => composer.removeOptimisticMessage?.(message.clientEventId)
|
|
177
|
+
} : {}
|
|
178
|
+
}
|
|
179
|
+
}));
|
|
180
|
+
const ids = new Set(visible.map((item) => item.id));
|
|
181
|
+
const steers = (queue.acceptedSteers ?? []).filter((steer) => !ids.has(steer.triggerEventId)).map((steer) => ({
|
|
182
|
+
kind: "user-message",
|
|
183
|
+
id: steer.triggerEventId,
|
|
184
|
+
text: steer.text,
|
|
185
|
+
annotations: steer.annotations,
|
|
186
|
+
resources: steer.resources,
|
|
187
|
+
tools: steer.tools,
|
|
188
|
+
occurredAt: steer.occurredAt,
|
|
189
|
+
delivery: { state: steer.state }
|
|
190
|
+
}));
|
|
191
|
+
return [...visible, ...optimistic, ...steers];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/hooks/use-available-models.ts
|
|
195
|
+
import { useCallback } from "react";
|
|
196
|
+
function useAvailableModels(options = {}) {
|
|
197
|
+
const client = useOpenGeniClient(options);
|
|
198
|
+
const load = useCallback(
|
|
199
|
+
async (signal) => await client.getClientConfig({ signal }),
|
|
200
|
+
[client]
|
|
201
|
+
);
|
|
202
|
+
const state = usePolledValue(load, {
|
|
203
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
204
|
+
enabled: options.enabled
|
|
70
205
|
});
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
206
|
+
return {
|
|
207
|
+
models: state.data?.models ?? [],
|
|
208
|
+
defaultModel: state.data?.defaultModel ?? null,
|
|
209
|
+
loading: state.loading,
|
|
210
|
+
error: state.error,
|
|
211
|
+
refresh: state.refresh
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function useWorkspaceModelCatalog(options) {
|
|
215
|
+
const client = useOpenGeniClient(options);
|
|
216
|
+
const load = useCallback(
|
|
217
|
+
async (signal) => {
|
|
218
|
+
if (!options.workspaceId) {
|
|
219
|
+
return { models: [], defaultModel: null };
|
|
220
|
+
}
|
|
221
|
+
const [catalog, config] = await Promise.all([
|
|
222
|
+
client.getWorkspaceModelCatalog(options.workspaceId, { signal }),
|
|
223
|
+
client.getClientConfig({ signal, workspaceId: options.workspaceId })
|
|
224
|
+
]);
|
|
225
|
+
return {
|
|
226
|
+
models: catalog.models,
|
|
227
|
+
defaultModel: config.defaultModel
|
|
228
|
+
};
|
|
229
|
+
},
|
|
230
|
+
[client, options.workspaceId]
|
|
231
|
+
);
|
|
232
|
+
const state = usePolledValue(load, {
|
|
233
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
234
|
+
enabled: options.enabled !== false && Boolean(options.workspaceId)
|
|
76
235
|
});
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
236
|
+
const rows = sortPickerRows(projectPickerRows(state.data?.models ?? []));
|
|
237
|
+
return {
|
|
238
|
+
models: state.data?.models ?? [],
|
|
239
|
+
rows,
|
|
240
|
+
defaultModel: state.data?.defaultModel ?? null,
|
|
241
|
+
loading: state.loading,
|
|
242
|
+
error: state.error,
|
|
243
|
+
refresh: state.refresh
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// src/hooks/use-file-attachments.ts
|
|
248
|
+
import { useCallback as useCallback2, useEffect, useRef, useState } from "react";
|
|
249
|
+
var isImage = (file) => file.type.startsWith("image/");
|
|
250
|
+
var fallbackAttachmentId = 0;
|
|
251
|
+
function createAttachmentId() {
|
|
252
|
+
const cryptoSource = globalThis.crypto;
|
|
253
|
+
if (typeof cryptoSource?.randomUUID === "function") return cryptoSource.randomUUID();
|
|
254
|
+
fallbackAttachmentId += 1;
|
|
255
|
+
return `attachment:${Date.now().toString(36)}:${fallbackAttachmentId.toString(36)}`;
|
|
256
|
+
}
|
|
257
|
+
function secureContextRequiredErrorCode(error) {
|
|
258
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "secure_context_required" ? error.code : void 0;
|
|
259
|
+
}
|
|
260
|
+
function useFileAttachments(options = {}) {
|
|
261
|
+
const { client, workspaceId } = useEmbeddedFileAttachments(options);
|
|
262
|
+
const formatError = useErrorMessage();
|
|
263
|
+
const pasteFilter = options.pasteFilter ?? isImage;
|
|
264
|
+
const [attachments, setAttachments] = useState([]);
|
|
265
|
+
const attachmentsRef = useRef(attachments);
|
|
266
|
+
attachmentsRef.current = attachments;
|
|
267
|
+
const sources = useRef(/* @__PURE__ */ new Map());
|
|
268
|
+
const previewUrls = useRef(/* @__PURE__ */ new Map());
|
|
269
|
+
const previewRetainers = useRef(/* @__PURE__ */ new Map());
|
|
270
|
+
const pendingPreviewRevocations = useRef(/* @__PURE__ */ new Set());
|
|
271
|
+
const revokePreview = useCallback2((id) => {
|
|
272
|
+
const previewUrl = previewUrls.current.get(id);
|
|
273
|
+
if (!previewUrl) return;
|
|
274
|
+
if ((previewRetainers.current.get(id) ?? 0) > 0) {
|
|
275
|
+
pendingPreviewRevocations.current.add(id);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
previewUrls.current.delete(id);
|
|
279
|
+
pendingPreviewRevocations.current.delete(id);
|
|
280
|
+
URL.revokeObjectURL(previewUrl);
|
|
281
|
+
}, []);
|
|
282
|
+
const revokeAllPreviews = useCallback2(() => {
|
|
283
|
+
for (const id of previewUrls.current.keys()) revokePreview(id);
|
|
284
|
+
}, [revokePreview]);
|
|
285
|
+
const retainPreview = useCallback2(
|
|
286
|
+
(id) => {
|
|
287
|
+
if (!previewUrls.current.has(id)) return void 0;
|
|
288
|
+
previewRetainers.current.set(id, (previewRetainers.current.get(id) ?? 0) + 1);
|
|
289
|
+
let released = false;
|
|
290
|
+
return () => {
|
|
291
|
+
if (released) return;
|
|
292
|
+
released = true;
|
|
293
|
+
const remaining = (previewRetainers.current.get(id) ?? 1) - 1;
|
|
294
|
+
if (remaining > 0) {
|
|
295
|
+
previewRetainers.current.set(id, remaining);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
previewRetainers.current.delete(id);
|
|
299
|
+
if (pendingPreviewRevocations.current.has(id)) revokePreview(id);
|
|
300
|
+
};
|
|
301
|
+
},
|
|
302
|
+
[revokePreview]
|
|
303
|
+
);
|
|
304
|
+
const committedAttachmentIds = useRef(/* @__PURE__ */ new Set());
|
|
305
|
+
useEffect(() => {
|
|
306
|
+
const currentIds = new Set(attachments.map((attachment) => attachment.id));
|
|
307
|
+
for (const id of committedAttachmentIds.current) {
|
|
308
|
+
if (!currentIds.has(id)) revokePreview(id);
|
|
309
|
+
}
|
|
310
|
+
committedAttachmentIds.current = currentIds;
|
|
311
|
+
}, [attachments, revokePreview]);
|
|
312
|
+
const scopeGeneration = useRef(0);
|
|
313
|
+
const previousScope = useRef({ client, workspaceId });
|
|
314
|
+
useEffect(() => {
|
|
315
|
+
const previous = previousScope.current;
|
|
316
|
+
if (previous.client === client && previous.workspaceId === workspaceId) return;
|
|
317
|
+
previousScope.current = { client, workspaceId };
|
|
318
|
+
scopeGeneration.current += 1;
|
|
319
|
+
sources.current.clear();
|
|
320
|
+
revokeAllPreviews();
|
|
321
|
+
setAttachments([]);
|
|
322
|
+
}, [client, revokeAllPreviews, workspaceId]);
|
|
323
|
+
useEffect(
|
|
324
|
+
() => () => {
|
|
325
|
+
scopeGeneration.current += 1;
|
|
326
|
+
sources.current.clear();
|
|
327
|
+
revokeAllPreviews();
|
|
328
|
+
},
|
|
329
|
+
[revokeAllPreviews]
|
|
330
|
+
);
|
|
331
|
+
const startUpload = useCallback2(
|
|
332
|
+
(id, file) => {
|
|
333
|
+
const generation = scopeGeneration.current;
|
|
334
|
+
void client.uploadFile(workspaceId, {
|
|
335
|
+
filename: file.name || "file",
|
|
336
|
+
contentType: file.type || "application/octet-stream",
|
|
337
|
+
data: file,
|
|
338
|
+
...options.scope ? { scope: options.scope } : {}
|
|
339
|
+
}).then((asset) => {
|
|
340
|
+
if (scopeGeneration.current !== generation) return;
|
|
341
|
+
sources.current.delete(id);
|
|
342
|
+
setAttachments(
|
|
343
|
+
(current) => current.map(
|
|
344
|
+
(attachment) => attachment.id === id ? {
|
|
345
|
+
...attachment,
|
|
346
|
+
status: "ready",
|
|
347
|
+
file: asset,
|
|
348
|
+
name: asset.filename,
|
|
349
|
+
contentType: asset.contentType,
|
|
350
|
+
sizeBytes: asset.sizeBytes,
|
|
351
|
+
errorCode: void 0,
|
|
352
|
+
error: void 0,
|
|
353
|
+
errorCause: void 0
|
|
354
|
+
} : attachment
|
|
355
|
+
)
|
|
356
|
+
);
|
|
357
|
+
}).catch((error) => {
|
|
358
|
+
if (scopeGeneration.current !== generation) return;
|
|
359
|
+
setAttachments(
|
|
360
|
+
(current) => current.map(
|
|
361
|
+
(attachment) => attachment.id === id ? {
|
|
362
|
+
...attachment,
|
|
363
|
+
status: "failed",
|
|
364
|
+
errorCode: secureContextRequiredErrorCode(error),
|
|
365
|
+
error: formatError(error),
|
|
366
|
+
errorCause: error
|
|
367
|
+
} : attachment
|
|
368
|
+
)
|
|
369
|
+
);
|
|
370
|
+
});
|
|
371
|
+
},
|
|
372
|
+
[client, workspaceId, options.scope, formatError]
|
|
373
|
+
);
|
|
374
|
+
const addFiles = useCallback2(
|
|
375
|
+
(files) => {
|
|
376
|
+
for (const file of files) {
|
|
377
|
+
const id = createAttachmentId();
|
|
378
|
+
sources.current.set(id, file);
|
|
379
|
+
const previewUrl = isImage(file) ? URL.createObjectURL(file) : void 0;
|
|
380
|
+
if (previewUrl) previewUrls.current.set(id, previewUrl);
|
|
381
|
+
setAttachments((current) => [
|
|
382
|
+
...current,
|
|
383
|
+
{
|
|
384
|
+
id,
|
|
385
|
+
name: file.name || "image",
|
|
386
|
+
contentType: file.type || "application/octet-stream",
|
|
387
|
+
sizeBytes: file.size,
|
|
388
|
+
status: "uploading",
|
|
389
|
+
...previewUrl ? { previewUrl } : {}
|
|
390
|
+
}
|
|
391
|
+
]);
|
|
392
|
+
startUpload(id, file);
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
[startUpload]
|
|
396
|
+
);
|
|
397
|
+
const retry = useCallback2(
|
|
398
|
+
(id) => {
|
|
399
|
+
const file = sources.current.get(id);
|
|
400
|
+
if (!file) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
setAttachments(
|
|
404
|
+
(current) => current.map(
|
|
405
|
+
(attachment) => attachment.id === id ? {
|
|
406
|
+
...attachment,
|
|
407
|
+
status: "uploading",
|
|
408
|
+
errorCode: void 0,
|
|
409
|
+
error: void 0,
|
|
410
|
+
errorCause: void 0
|
|
411
|
+
} : attachment
|
|
412
|
+
)
|
|
413
|
+
);
|
|
414
|
+
startUpload(id, file);
|
|
415
|
+
},
|
|
416
|
+
[startUpload]
|
|
417
|
+
);
|
|
418
|
+
const addFromPaste = useCallback2(
|
|
419
|
+
(event) => {
|
|
420
|
+
const clipboardFiles = event.clipboardData?.files;
|
|
421
|
+
if (!clipboardFiles) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const files = [...clipboardFiles].filter(pasteFilter);
|
|
425
|
+
if (files.length > 0) {
|
|
426
|
+
addFiles(files);
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
[addFiles, pasteFilter]
|
|
430
|
+
);
|
|
431
|
+
const restoreReadyFiles = useCallback2(
|
|
432
|
+
(files) => {
|
|
433
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
434
|
+
for (const file of files) {
|
|
435
|
+
if (file.status === "ready" && file.workspaceId === workspaceId) {
|
|
436
|
+
incoming.set(file.id, file);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
setAttachments((current) => {
|
|
440
|
+
const unresolved = current.filter((attachment) => attachment.status !== "ready");
|
|
441
|
+
const existingReady = new Map(
|
|
442
|
+
current.flatMap(
|
|
443
|
+
(attachment) => attachment.status === "ready" && attachment.file ? [[attachment.file.id, attachment]] : []
|
|
444
|
+
)
|
|
445
|
+
);
|
|
446
|
+
const restored = [...incoming.values()].map((file) => {
|
|
447
|
+
const existing = existingReady.get(file.id);
|
|
448
|
+
return existing ? {
|
|
449
|
+
...existing,
|
|
450
|
+
name: file.filename,
|
|
451
|
+
contentType: file.contentType,
|
|
452
|
+
sizeBytes: file.sizeBytes,
|
|
453
|
+
status: "ready",
|
|
454
|
+
file,
|
|
455
|
+
errorCode: void 0,
|
|
456
|
+
error: void 0,
|
|
457
|
+
errorCause: void 0
|
|
458
|
+
} : {
|
|
459
|
+
id: `restored:${file.id}`,
|
|
460
|
+
name: file.filename,
|
|
461
|
+
contentType: file.contentType,
|
|
462
|
+
sizeBytes: file.sizeBytes,
|
|
463
|
+
status: "ready",
|
|
464
|
+
file
|
|
465
|
+
// No source File and no object URL: server metadata is the
|
|
466
|
+
// only authority restored across page/device boundaries.
|
|
467
|
+
};
|
|
468
|
+
});
|
|
469
|
+
return [...unresolved, ...restored];
|
|
470
|
+
});
|
|
471
|
+
},
|
|
472
|
+
[workspaceId]
|
|
473
|
+
);
|
|
474
|
+
const loadPreview = useCallback2(
|
|
475
|
+
async (id, signal) => {
|
|
476
|
+
const generation = scopeGeneration.current;
|
|
477
|
+
const attachment = attachmentsRef.current.find((candidate) => candidate.id === id);
|
|
478
|
+
const createDownloadUrl = client.createFileDownloadUrl;
|
|
479
|
+
if (!attachment || attachment.status !== "ready" || !attachment.file || !attachment.contentType.startsWith("image/") || typeof createDownloadUrl !== "function" || signal?.aborted) {
|
|
480
|
+
return void 0;
|
|
481
|
+
}
|
|
482
|
+
const fileId = attachment.file.id;
|
|
483
|
+
const signed = await createDownloadUrl.call(client, workspaceId, fileId, { signal });
|
|
484
|
+
if (scopeGeneration.current !== generation || signal?.aborted) return void 0;
|
|
485
|
+
const current = attachmentsRef.current.find((candidate) => candidate.id === id);
|
|
486
|
+
if (current?.status !== "ready" || current.file?.id !== fileId) return void 0;
|
|
487
|
+
return signed.url;
|
|
488
|
+
},
|
|
489
|
+
[client, workspaceId]
|
|
490
|
+
);
|
|
491
|
+
const remove = useCallback2(
|
|
492
|
+
(id) => {
|
|
493
|
+
sources.current.delete(id);
|
|
494
|
+
revokePreview(id);
|
|
495
|
+
setAttachments((current) => current.filter((attachment) => attachment.id !== id));
|
|
496
|
+
},
|
|
497
|
+
[revokePreview]
|
|
498
|
+
);
|
|
499
|
+
const removeReadyFiles = useCallback2((fileIds) => {
|
|
500
|
+
const accepted = new Set(fileIds);
|
|
501
|
+
if (accepted.size === 0) return;
|
|
502
|
+
setAttachments(
|
|
503
|
+
(current) => current.filter((attachment) => {
|
|
504
|
+
return !(attachment.status === "ready" && attachment.file !== void 0 && accepted.has(attachment.file.id));
|
|
86
505
|
})
|
|
87
|
-
|
|
506
|
+
);
|
|
507
|
+
}, []);
|
|
508
|
+
const clear = useCallback2(() => {
|
|
509
|
+
sources.current.clear();
|
|
510
|
+
revokeAllPreviews();
|
|
511
|
+
setAttachments([]);
|
|
512
|
+
}, [revokeAllPreviews]);
|
|
513
|
+
return {
|
|
514
|
+
attachments,
|
|
515
|
+
readyResources: attachments.flatMap(
|
|
516
|
+
(attachment) => attachment.status === "ready" && attachment.file ? [{ kind: "file", fileId: attachment.file.id }] : []
|
|
517
|
+
),
|
|
518
|
+
uploading: attachments.some((attachment) => attachment.status === "uploading"),
|
|
519
|
+
hasUnresolved: attachments.some((attachment) => attachment.status !== "ready"),
|
|
520
|
+
addFiles,
|
|
521
|
+
addFromPaste,
|
|
522
|
+
restoreReadyFiles,
|
|
523
|
+
...typeof client.createFileDownloadUrl === "function" ? { loadPreview } : {},
|
|
524
|
+
retry,
|
|
525
|
+
retainPreview,
|
|
526
|
+
remove,
|
|
527
|
+
removeReadyFiles,
|
|
528
|
+
clear
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// src/hooks/use-human-input.ts
|
|
533
|
+
import { useCallback as useCallback3, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
534
|
+
function isHumanInputEvent(event) {
|
|
535
|
+
return event.type === "session.humanInput.requested" || event.type === "user.humanInputResponse" || event.type === "turn.completed" || event.type === "turn.failed" || event.type === "turn.cancelled";
|
|
536
|
+
}
|
|
537
|
+
function useHumanInputRequests(sessionId, options = {}) {
|
|
538
|
+
const { client, workspaceId, registerSessionReconciler } = useEmbeddedHumanInputSession(options);
|
|
539
|
+
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
540
|
+
const load = useCallback3(
|
|
541
|
+
async () => sessionId ? await client.listHumanInputRequests(workspaceId, sessionId, { status: "pending" }) : [],
|
|
542
|
+
[client, sessionId, workspaceId]
|
|
543
|
+
);
|
|
544
|
+
const state = usePolledValue(load, {
|
|
545
|
+
enabled,
|
|
546
|
+
pollIntervalMs: options.pollIntervalMs
|
|
547
|
+
});
|
|
548
|
+
const loadedRequests = state.data;
|
|
549
|
+
const refreshRequests = state.refresh;
|
|
550
|
+
const mutation = useMutationRunner(`${workspaceId}\0${sessionId ?? ""}`);
|
|
551
|
+
const [respondingRequestId, setRespondingRequestId] = useState2(null);
|
|
552
|
+
const [deadlineVersion, setDeadlineVersion] = useState2(0);
|
|
553
|
+
const respondingRequestRef = useRef2(null);
|
|
554
|
+
useEffect2(() => {
|
|
555
|
+
respondingRequestRef.current = null;
|
|
556
|
+
setRespondingRequestId(null);
|
|
557
|
+
}, [sessionId, workspaceId]);
|
|
558
|
+
useEffect2(() => {
|
|
559
|
+
if (!sessionId || !enabled) return;
|
|
560
|
+
return registerSessionReconciler(sessionId, "human-input", state.refresh);
|
|
561
|
+
}, [enabled, registerSessionReconciler, sessionId, state.refresh]);
|
|
562
|
+
const scheduleRefresh = useDebouncedCallback(() => void state.refresh());
|
|
563
|
+
useSessionEventTrigger(client, workspaceId, sessionId, isHumanInputEvent, scheduleRefresh, {
|
|
564
|
+
enabled,
|
|
565
|
+
...options.events !== void 0 ? { events: options.events } : {}
|
|
566
|
+
});
|
|
567
|
+
useEffect2(() => {
|
|
568
|
+
const now2 = Date.now();
|
|
569
|
+
const nextDeadline = (loadedRequests ?? []).reduce((next, request) => {
|
|
570
|
+
if (request.status !== "pending" || !request.expiresAt) return next;
|
|
571
|
+
const deadline = Date.parse(request.expiresAt);
|
|
572
|
+
if (!Number.isFinite(deadline) || deadline <= now2) return next;
|
|
573
|
+
return next === null ? deadline : Math.min(next, deadline);
|
|
574
|
+
}, null);
|
|
575
|
+
if (nextDeadline === null) return;
|
|
576
|
+
const timer = globalThis.setTimeout(
|
|
577
|
+
() => {
|
|
578
|
+
setDeadlineVersion((current) => current + 1);
|
|
579
|
+
void refreshRequests();
|
|
580
|
+
},
|
|
581
|
+
Math.min(2147483647, Math.max(0, nextDeadline - now2 + 1))
|
|
582
|
+
);
|
|
583
|
+
return () => globalThis.clearTimeout(timer);
|
|
584
|
+
}, [loadedRequests, refreshRequests]);
|
|
585
|
+
const respond = useCallback3(
|
|
586
|
+
async (requestId, response) => {
|
|
587
|
+
if (!sessionId || respondingRequestRef.current !== null) return null;
|
|
588
|
+
respondingRequestRef.current = requestId;
|
|
589
|
+
setRespondingRequestId(requestId);
|
|
590
|
+
try {
|
|
591
|
+
const event = await mutation.run(
|
|
592
|
+
() => client.submitHumanInputResponse(workspaceId, sessionId, requestId, response, {
|
|
593
|
+
clientEventId: globalThis.crypto.randomUUID()
|
|
594
|
+
})
|
|
595
|
+
);
|
|
596
|
+
if (event) await state.refresh();
|
|
597
|
+
return event;
|
|
598
|
+
} finally {
|
|
599
|
+
if (respondingRequestRef.current === requestId) {
|
|
600
|
+
respondingRequestRef.current = null;
|
|
601
|
+
setRespondingRequestId(null);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
[client, mutation, sessionId, state, workspaceId]
|
|
606
|
+
);
|
|
607
|
+
const now = Date.now();
|
|
608
|
+
void deadlineVersion;
|
|
609
|
+
return {
|
|
610
|
+
requests: [...state.data ?? []].filter((request) => isActionableHumanInputRequest(request, now)).sort(
|
|
611
|
+
(left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id)
|
|
612
|
+
),
|
|
613
|
+
loading: state.loading,
|
|
614
|
+
error: state.error,
|
|
615
|
+
refresh: state.refresh,
|
|
616
|
+
respond,
|
|
617
|
+
respondingRequestId,
|
|
618
|
+
mutationError: mutation.mutationError,
|
|
619
|
+
clearMutationError: mutation.clearMutationError
|
|
88
620
|
};
|
|
89
|
-
return client;
|
|
90
621
|
}
|
|
91
622
|
|
|
92
623
|
// src/hooks/use-session.ts
|
|
93
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
624
|
+
import { useCallback as useCallback4, useEffect as useEffect3, useMemo, useRef as useRef3, useState as useState3 } from "react";
|
|
94
625
|
function isTitleEvent(event) {
|
|
95
626
|
return event.type === "session.title_set";
|
|
96
627
|
}
|
|
628
|
+
function isSessionDetailEvent(event) {
|
|
629
|
+
return isTitleEvent(event) || event.type.startsWith("session.command.") || event.type.startsWith("session.wait.") || event.type === "session.status.changed" || event.type === "system.update.pending";
|
|
630
|
+
}
|
|
97
631
|
function useSession(sessionId, options = {}) {
|
|
98
632
|
const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } = useEmbeddedSessionRead(options);
|
|
99
633
|
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
100
|
-
const [override, setOverride] =
|
|
101
|
-
const nextReadRevision =
|
|
102
|
-
const nextReadGeneration =
|
|
634
|
+
const [override, setOverride] = useState3(null);
|
|
635
|
+
const nextReadRevision = useRef3(0);
|
|
636
|
+
const nextReadGeneration = useRef3(0);
|
|
103
637
|
const beginRead = options.beginRead;
|
|
104
638
|
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
105
|
-
const load =
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
let readGeneration2 = 0;
|
|
110
|
-
const fetched = await client.getSession(workspaceId, sessionId, {
|
|
111
|
-
fresh: true,
|
|
112
|
-
onRequestStart: () => {
|
|
113
|
-
readGeneration2 = beginRead?.() ?? ++nextReadGeneration.current;
|
|
639
|
+
const load = useCallback4(
|
|
640
|
+
async (signal) => {
|
|
641
|
+
if (!sessionId) {
|
|
642
|
+
return null;
|
|
114
643
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
644
|
+
let readGeneration2 = 0;
|
|
645
|
+
const fetched = await client.getSession(workspaceId, sessionId, {
|
|
646
|
+
fresh: true,
|
|
647
|
+
signal,
|
|
648
|
+
onRequestStart: () => {
|
|
649
|
+
readGeneration2 = beginRead?.() ?? ++nextReadGeneration.current;
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
setOverride(null);
|
|
653
|
+
return {
|
|
654
|
+
session: fetched,
|
|
655
|
+
revision: ++nextReadRevision.current,
|
|
656
|
+
readGeneration: readGeneration2
|
|
657
|
+
};
|
|
658
|
+
},
|
|
659
|
+
[beginRead, client, workspaceId, sessionId]
|
|
660
|
+
);
|
|
123
661
|
const { data, loading, error, refresh } = usePolledValue(load, {
|
|
124
662
|
pollIntervalMs: options.pollIntervalMs,
|
|
125
663
|
enabled
|
|
126
664
|
});
|
|
127
|
-
|
|
665
|
+
useEffect3(() => {
|
|
128
666
|
if (enabled && workspaceControlEvent) void refresh();
|
|
129
667
|
}, [enabled, refresh, workspaceControlEvent]);
|
|
130
|
-
|
|
668
|
+
useEffect3(() => {
|
|
131
669
|
if (!sessionId || !enabled) return;
|
|
132
670
|
return registerSessionReconciler(sessionId, "session", refresh);
|
|
133
671
|
}, [enabled, refresh, registerSessionReconciler, sessionId]);
|
|
@@ -138,8 +676,17 @@ function useSession(sessionId, options = {}) {
|
|
|
138
676
|
() => base && override && override.id === base.id ? { ...base, title: override.title, titleSource: override.titleSource } : base,
|
|
139
677
|
[base, override]
|
|
140
678
|
);
|
|
141
|
-
const
|
|
679
|
+
const sharedEvents = options.events;
|
|
680
|
+
const onTitleEvent = useCallback4(
|
|
142
681
|
(event) => {
|
|
682
|
+
if (sharedEvents?.some(
|
|
683
|
+
(item) => item.sessionId === sessionId && !isTitleEvent(item) && isSessionDetailEvent(item) && item.sequence > (base?.lastSequence ?? -1)
|
|
684
|
+
)) {
|
|
685
|
+
void refresh();
|
|
686
|
+
} else if (!isTitleEvent(event)) {
|
|
687
|
+
if (!base || event.sequence > base.lastSequence) void refresh();
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
143
690
|
if (!base || event.sequence <= base.lastSequence) {
|
|
144
691
|
return;
|
|
145
692
|
}
|
|
@@ -154,13 +701,13 @@ function useSession(sessionId, options = {}) {
|
|
|
154
701
|
return { ...next, title, titleSource: source };
|
|
155
702
|
});
|
|
156
703
|
},
|
|
157
|
-
[base]
|
|
704
|
+
[base, refresh, sharedEvents, sessionId]
|
|
158
705
|
);
|
|
159
|
-
useSessionEventTrigger(client, workspaceId, sessionId,
|
|
706
|
+
useSessionEventTrigger(client, workspaceId, sessionId, isSessionDetailEvent, onTitleEvent, {
|
|
160
707
|
enabled,
|
|
161
708
|
...options.events !== void 0 ? { events: options.events } : {}
|
|
162
709
|
});
|
|
163
|
-
const updateTitle =
|
|
710
|
+
const updateTitle = useCallback4(
|
|
164
711
|
async (title) => {
|
|
165
712
|
if (!sessionId) {
|
|
166
713
|
return null;
|
|
@@ -187,12 +734,109 @@ function useSession(sessionId, options = {}) {
|
|
|
187
734
|
};
|
|
188
735
|
}
|
|
189
736
|
|
|
737
|
+
// src/hooks/use-session-control.ts
|
|
738
|
+
import { useCallback as useCallback5, useLayoutEffect, useRef as useRef4 } from "react";
|
|
739
|
+
function useSessionControl(sessionId, options = {}) {
|
|
740
|
+
const { client, workspaceId } = useEmbeddedSession(options);
|
|
741
|
+
const approvalTargetKey = `${workspaceId}\0${sessionId ?? ""}`;
|
|
742
|
+
const pendingApproval = useRef4(null);
|
|
743
|
+
useLayoutEffect(() => {
|
|
744
|
+
if (pendingApproval.current?.targetKey !== approvalTargetKey) {
|
|
745
|
+
pendingApproval.current = null;
|
|
746
|
+
}
|
|
747
|
+
}, [approvalTargetKey]);
|
|
748
|
+
const {
|
|
749
|
+
run: runControl,
|
|
750
|
+
mutating: controlling,
|
|
751
|
+
mutationError: controlError,
|
|
752
|
+
clearMutationError: clearControlError
|
|
753
|
+
} = useMutationRunner(approvalTargetKey);
|
|
754
|
+
const {
|
|
755
|
+
run: runApproval,
|
|
756
|
+
mutating: responding,
|
|
757
|
+
mutationError: approvalError,
|
|
758
|
+
clearMutationError: clearApprovalError
|
|
759
|
+
} = useMutationRunner(approvalTargetKey);
|
|
760
|
+
const pause = useCallback5(
|
|
761
|
+
async (reason) => {
|
|
762
|
+
if (!sessionId) {
|
|
763
|
+
return null;
|
|
764
|
+
}
|
|
765
|
+
return await runControl(
|
|
766
|
+
() => client.pauseSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
|
|
767
|
+
);
|
|
768
|
+
},
|
|
769
|
+
[client, workspaceId, sessionId, runControl]
|
|
770
|
+
);
|
|
771
|
+
const resume = useCallback5(
|
|
772
|
+
async (reason) => {
|
|
773
|
+
if (!sessionId) return null;
|
|
774
|
+
return await runControl(
|
|
775
|
+
() => client.resumeSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
|
|
776
|
+
);
|
|
777
|
+
},
|
|
778
|
+
[client, workspaceId, sessionId, runControl]
|
|
779
|
+
);
|
|
780
|
+
const decide = useCallback5(
|
|
781
|
+
async (approvalId, decision, message) => {
|
|
782
|
+
if (!sessionId) {
|
|
783
|
+
return null;
|
|
784
|
+
}
|
|
785
|
+
const decisionKey = JSON.stringify([approvalId, decision, message ?? null]);
|
|
786
|
+
if (pendingApproval.current?.decisionKey !== decisionKey) {
|
|
787
|
+
pendingApproval.current = {
|
|
788
|
+
targetKey: approvalTargetKey,
|
|
789
|
+
decisionKey,
|
|
790
|
+
clientEventId: crypto.randomUUID()
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
const clientEventId = pendingApproval.current.clientEventId;
|
|
794
|
+
const accepted = await runApproval(
|
|
795
|
+
() => client.sendApprovalDecision(workspaceId, sessionId, {
|
|
796
|
+
approvalId,
|
|
797
|
+
decision,
|
|
798
|
+
...message !== void 0 ? { message } : {},
|
|
799
|
+
clientEventId
|
|
800
|
+
})
|
|
801
|
+
);
|
|
802
|
+
if (accepted !== null && pendingApproval.current?.targetKey === approvalTargetKey && pendingApproval.current.clientEventId === clientEventId) {
|
|
803
|
+
pendingApproval.current = null;
|
|
804
|
+
}
|
|
805
|
+
return accepted;
|
|
806
|
+
},
|
|
807
|
+
[approvalTargetKey, client, workspaceId, sessionId, runApproval]
|
|
808
|
+
);
|
|
809
|
+
const approve = useCallback5(
|
|
810
|
+
async (approvalId, message) => await decide(approvalId, "approve", message),
|
|
811
|
+
[decide]
|
|
812
|
+
);
|
|
813
|
+
const reject = useCallback5(
|
|
814
|
+
async (approvalId, message) => await decide(approvalId, "reject", message),
|
|
815
|
+
[decide]
|
|
816
|
+
);
|
|
817
|
+
const error = approvalError ?? controlError;
|
|
818
|
+
const clearError = useCallback5(() => {
|
|
819
|
+
clearControlError();
|
|
820
|
+
clearApprovalError();
|
|
821
|
+
}, [clearControlError, clearApprovalError]);
|
|
822
|
+
return {
|
|
823
|
+
pause,
|
|
824
|
+
resume,
|
|
825
|
+
controlling,
|
|
826
|
+
approve,
|
|
827
|
+
reject,
|
|
828
|
+
responding,
|
|
829
|
+
error,
|
|
830
|
+
clearError
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
|
|
190
834
|
// src/hooks/use-session-events.ts
|
|
191
|
-
import {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
var
|
|
835
|
+
import {
|
|
836
|
+
sessionEventStreamCoveredThrough
|
|
837
|
+
} from "@opengeni/sdk";
|
|
838
|
+
import { startTransition, useCallback as useCallback6, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef5, useState as useState4 } from "react";
|
|
839
|
+
var SESSION_HISTORY_PAGE_SIZE = 1e3;
|
|
196
840
|
var INITIAL_FETCH_CAP = 1;
|
|
197
841
|
var OLDER_GROUP_TARGET = 32;
|
|
198
842
|
var OLDER_FETCH_CAP = 2;
|
|
@@ -200,35 +844,16 @@ var NEWER_GROUP_TARGET = 32;
|
|
|
200
844
|
var NEWER_FETCH_CAP = 2;
|
|
201
845
|
var OLDEST_GROUP_TARGET = 32;
|
|
202
846
|
var OLDEST_FETCH_CAP = 2;
|
|
203
|
-
var BOUNDARY_PAGE_CAP =
|
|
847
|
+
var BOUNDARY_PAGE_CAP = 1;
|
|
848
|
+
var FOREGROUND_DIRECT_REPLAY_MAX_SEQUENCES = 16;
|
|
849
|
+
var FOREGROUND_COMPACT_PROBE_MAX_SEQUENCES = SESSION_HISTORY_PAGE_SIZE;
|
|
850
|
+
var FOREGROUND_COMPACT_CATCHUP_MAX_EVENTS = 128;
|
|
851
|
+
var FOREGROUND_COMPACT_CATCHUP_MAX_GROUPS = 16;
|
|
852
|
+
var FOREGROUND_COMPACT_CATCHUP_MAX_BYTES = 512 * 1024;
|
|
204
853
|
var EMPTY_EVENTS = [];
|
|
205
854
|
var encoder = new TextEncoder();
|
|
206
|
-
var
|
|
207
|
-
var
|
|
208
|
-
var BROWSER_EVENT_ID_MAX_BYTES = 256;
|
|
209
|
-
var BROWSER_EVENT_CLIENT_ID_MAX_BYTES = 4 * 1024;
|
|
210
|
-
var BROWSER_EVENT_DUPLICATE_REASON_MAX_BYTES = 4 * 1024;
|
|
211
|
-
var BROWSER_EVENT_PAYLOAD_PREVIEW_MAX_BYTES = 48 * 1024;
|
|
212
|
-
var BROWSER_EVENT_PAYLOAD_IDENTITY_FIELDS = [
|
|
213
|
-
"id",
|
|
214
|
-
"callId",
|
|
215
|
-
"call_id",
|
|
216
|
-
"name",
|
|
217
|
-
"toolName",
|
|
218
|
-
"status",
|
|
219
|
-
"code",
|
|
220
|
-
"isError",
|
|
221
|
-
"stream",
|
|
222
|
-
"commandId",
|
|
223
|
-
"sequence",
|
|
224
|
-
"coalescedUntil",
|
|
225
|
-
"coalescedCount",
|
|
226
|
-
"firstSequence",
|
|
227
|
-
"lastSequence"
|
|
228
|
-
];
|
|
229
|
-
var SESSION_EVENT_BROWSER_MAX_BYTES = 8 * 1024 * 1024;
|
|
230
|
-
var SESSION_EVENT_BROWSER_MAX_COUNT = 1e4;
|
|
231
|
-
var SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES = 96 * 1024;
|
|
855
|
+
var SESSION_EVENT_BROWSER_MAX_BYTES = 16 * 1024 * 1024;
|
|
856
|
+
var SESSION_EVENT_BROWSER_MAX_COUNT = 2e4;
|
|
232
857
|
var SESSION_EVENT_BROWSER_PENDING_MAX_BYTES = 1024 * 1024;
|
|
233
858
|
var SESSION_EVENT_BROWSER_PENDING_MAX_COUNT = 256;
|
|
234
859
|
var EMPTY_EVENT_WINDOW = {
|
|
@@ -245,43 +870,75 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
245
870
|
const replay = options.replay ?? "windowed";
|
|
246
871
|
const fullReplay = replay === "full" || after !== 0;
|
|
247
872
|
const streamKey = `${workspaceId}\0${sessionId ?? ""}\0${after}\0${fullReplay ? "full" : "windowed"}`;
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
const
|
|
873
|
+
const navigationIdentityRef = useRef5({ client, streamKey, enabled });
|
|
874
|
+
if (navigationIdentityRef.current.client !== client || navigationIdentityRef.current.streamKey !== streamKey || navigationIdentityRef.current.enabled !== enabled) {
|
|
875
|
+
navigationIdentityRef.current = { client, streamKey, enabled };
|
|
876
|
+
}
|
|
877
|
+
const navigationIdentity = navigationIdentityRef.current;
|
|
878
|
+
const ownsNavigation = useCallback6(
|
|
879
|
+
() => navigationIdentityRef.current === navigationIdentity,
|
|
880
|
+
[navigationIdentity]
|
|
881
|
+
);
|
|
882
|
+
const [eventWindow, setEventWindow] = useState4(EMPTY_EVENT_WINDOW);
|
|
883
|
+
const [questionEvidence, setQuestionEvidence] = useState4(null);
|
|
884
|
+
const [connectionState, setConnectionState] = useState4("idle");
|
|
885
|
+
const [error, setError] = useState4(null);
|
|
886
|
+
const [newerError, setNewerError] = useState4(null);
|
|
887
|
+
const [hasOlder, setHasOlder] = useState4(false);
|
|
888
|
+
const [hasNewer, setHasNewer] = useState4(false);
|
|
889
|
+
const [sessionStatusProjection, setSessionStatusProjection] = useState4(
|
|
254
890
|
null
|
|
255
891
|
);
|
|
256
|
-
const [initialLoading, setInitialLoading] =
|
|
257
|
-
const [
|
|
258
|
-
const [
|
|
259
|
-
const [
|
|
260
|
-
const [
|
|
261
|
-
const [
|
|
262
|
-
const [
|
|
263
|
-
const
|
|
264
|
-
const
|
|
265
|
-
const
|
|
266
|
-
const
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
const
|
|
270
|
-
const
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
const
|
|
274
|
-
const
|
|
275
|
-
const
|
|
276
|
-
const
|
|
277
|
-
const
|
|
278
|
-
const
|
|
279
|
-
const
|
|
892
|
+
const [initialLoading, setInitialLoading] = useState4(true);
|
|
893
|
+
const [initialHistoryReady, setInitialHistoryReady] = useState4(false);
|
|
894
|
+
const [loadingOlder, setLoadingOlder] = useState4(false);
|
|
895
|
+
const [loadingNewer, setLoadingNewer] = useState4(false);
|
|
896
|
+
const [loadingOldest, setLoadingOldest] = useState4(false);
|
|
897
|
+
const [loadingLatest, setLoadingLatest] = useState4(false);
|
|
898
|
+
const [loadingTarget, setLoadingTarget] = useState4(false);
|
|
899
|
+
const loadingTargetRef = useRef5(false);
|
|
900
|
+
const [streamEpoch, setStreamEpoch] = useState4(0);
|
|
901
|
+
const [viewMode, setViewMode] = useState4("live");
|
|
902
|
+
const lastSequenceRef = useRef5(after);
|
|
903
|
+
const streamResumeSequenceRef = useRef5(after);
|
|
904
|
+
const oldestSequenceRef = useRef5(null);
|
|
905
|
+
const newestSequenceRef = useRef5(null);
|
|
906
|
+
const hasOlderRef = useRef5(false);
|
|
907
|
+
const hasNewerRef = useRef5(false);
|
|
908
|
+
const loadingOlderRef = useRef5(false);
|
|
909
|
+
const loadingNewerRef = useRef5(false);
|
|
910
|
+
const loadingOldestRef = useRef5(false);
|
|
911
|
+
const loadingLatestRef = useRef5(false);
|
|
912
|
+
const viewModeRef = useRef5("live");
|
|
913
|
+
const initialWindowLoadedRef = useRef5(false);
|
|
914
|
+
const reconcileAfterPageResumeRef = useRef5(false);
|
|
915
|
+
const streamAbortRef = useRef5(null);
|
|
916
|
+
const streamKeyRef = useRef5(null);
|
|
917
|
+
const generationRef = useRef5(0);
|
|
918
|
+
const navigationGenerationRef = useRef5(0);
|
|
919
|
+
const eventWindowRef = useRef5(EMPTY_EVENT_WINDOW);
|
|
920
|
+
const sessionStatusRef = useRef5({
|
|
280
921
|
sequence: after,
|
|
281
922
|
status: null
|
|
282
923
|
});
|
|
283
|
-
const [stateStreamKey, setStateStreamKey] =
|
|
284
|
-
|
|
924
|
+
const [stateStreamKey, setStateStreamKey] = useState4(streamKey);
|
|
925
|
+
useEffect4(() => {
|
|
926
|
+
navigationGenerationRef.current += 1;
|
|
927
|
+
setNewerError(null);
|
|
928
|
+
loadingOlderRef.current = false;
|
|
929
|
+
loadingNewerRef.current = false;
|
|
930
|
+
loadingOldestRef.current = false;
|
|
931
|
+
loadingTargetRef.current = false;
|
|
932
|
+
setQuestionEvidence(null);
|
|
933
|
+
setLoadingTarget(false);
|
|
934
|
+
setLoadingOlder(false);
|
|
935
|
+
setLoadingNewer(false);
|
|
936
|
+
setLoadingOldest(false);
|
|
937
|
+
return () => {
|
|
938
|
+
navigationGenerationRef.current += 1;
|
|
939
|
+
};
|
|
940
|
+
}, [client, workspaceId, sessionId, after, enabled, fullReplay]);
|
|
941
|
+
useEffect4(() => {
|
|
285
942
|
if (streamKeyRef.current !== streamKey) {
|
|
286
943
|
streamKeyRef.current = streamKey;
|
|
287
944
|
setStateStreamKey(streamKey);
|
|
@@ -297,6 +954,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
297
954
|
setLoadingOldest(false);
|
|
298
955
|
setLoadingLatest(false);
|
|
299
956
|
setInitialLoading(true);
|
|
957
|
+
setInitialHistoryReady(false);
|
|
300
958
|
lastSequenceRef.current = after;
|
|
301
959
|
streamResumeSequenceRef.current = after;
|
|
302
960
|
oldestSequenceRef.current = null;
|
|
@@ -310,24 +968,27 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
310
968
|
viewModeRef.current = "live";
|
|
311
969
|
setViewMode("live");
|
|
312
970
|
initialWindowLoadedRef.current = false;
|
|
971
|
+
reconcileAfterPageResumeRef.current = false;
|
|
313
972
|
}
|
|
314
973
|
const generation = generationRef.current + 1;
|
|
315
974
|
generationRef.current = generation;
|
|
316
|
-
if (loadingOlderRef.current) {
|
|
317
|
-
loadingOlderRef.current = false;
|
|
318
|
-
setLoadingOlder(false);
|
|
319
|
-
}
|
|
320
975
|
if (!sessionId || !streamEnabled) {
|
|
976
|
+
if (sessionId && enabled && !pageLive && !fullReplay && viewModeRef.current === "live" && initialWindowLoadedRef.current) {
|
|
977
|
+
reconcileAfterPageResumeRef.current = true;
|
|
978
|
+
}
|
|
321
979
|
setConnectionState("idle");
|
|
322
980
|
return;
|
|
323
981
|
}
|
|
324
982
|
if (viewMode === "history") {
|
|
983
|
+
reconcileAfterPageResumeRef.current = false;
|
|
325
984
|
setConnectionState("idle");
|
|
326
985
|
setInitialLoading(false);
|
|
327
986
|
return;
|
|
328
987
|
}
|
|
988
|
+
const reconcileForegroundResume = reconcileAfterPageResumeRef.current && !fullReplay;
|
|
989
|
+
reconcileAfterPageResumeRef.current = false;
|
|
329
990
|
const controller = new AbortController();
|
|
330
|
-
const isCurrent = () => generationRef.current === generation && !controller.signal.aborted;
|
|
991
|
+
const isCurrent = () => generationRef.current === generation && !controller.signal.aborted && navigationIdentityRef.current.client === client && navigationIdentityRef.current.streamKey === streamKey && navigationIdentityRef.current.enabled === enabled;
|
|
331
992
|
streamAbortRef.current = controller;
|
|
332
993
|
let pending = [];
|
|
333
994
|
let pendingBytes = 2;
|
|
@@ -357,9 +1018,12 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
357
1018
|
batchResumeSequence
|
|
358
1019
|
);
|
|
359
1020
|
}
|
|
360
|
-
observeSessionStatus(batch, sessionStatusRef
|
|
1021
|
+
const status = observeSessionStatus(batch, sessionStatusRef);
|
|
1022
|
+
if (status !== void 0) {
|
|
1023
|
+
setSessionStatusProjection(status);
|
|
1024
|
+
}
|
|
361
1025
|
const current = eventWindowRef.current;
|
|
362
|
-
const next =
|
|
1026
|
+
const next = appendBrowserSessionEventWindow(current, batch);
|
|
363
1027
|
const retained = {
|
|
364
1028
|
...next,
|
|
365
1029
|
truncated: current.truncated || next.truncated
|
|
@@ -367,7 +1031,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
367
1031
|
eventWindowRef.current = retained;
|
|
368
1032
|
setEventWindow(retained);
|
|
369
1033
|
oldestSequenceRef.current = retained.events[0]?.sequence ?? null;
|
|
370
|
-
newestSequenceRef.current = retained.events
|
|
1034
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
|
|
371
1035
|
if (retained.truncated) {
|
|
372
1036
|
hasOlderRef.current = true;
|
|
373
1037
|
setHasOlder(true);
|
|
@@ -379,29 +1043,85 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
379
1043
|
};
|
|
380
1044
|
void (async () => {
|
|
381
1045
|
try {
|
|
1046
|
+
if (reconcileForegroundResume) {
|
|
1047
|
+
setConnectionState("connecting");
|
|
1048
|
+
const cursor = streamResumeSequenceRef.current;
|
|
1049
|
+
let plan = { kind: "resume" };
|
|
1050
|
+
try {
|
|
1051
|
+
plan = await planForegroundCatchup(client, workspaceId, sessionId, cursor, {
|
|
1052
|
+
signal: controller.signal
|
|
1053
|
+
});
|
|
1054
|
+
} catch {
|
|
1055
|
+
if (controller.signal.aborted) return;
|
|
1056
|
+
plan = { kind: "resume" };
|
|
1057
|
+
}
|
|
1058
|
+
if (!isCurrent()) return;
|
|
1059
|
+
if (plan.kind === "append") {
|
|
1060
|
+
const current = eventWindowRef.current;
|
|
1061
|
+
assertAppendOrder(current.events, plan.events);
|
|
1062
|
+
const status = observeSessionStatus(plan.events, sessionStatusRef);
|
|
1063
|
+
const next = appendBrowserSessionEventWindow(current, plan.events);
|
|
1064
|
+
const retained = {
|
|
1065
|
+
...next,
|
|
1066
|
+
truncated: current.truncated || next.truncated
|
|
1067
|
+
};
|
|
1068
|
+
eventWindowRef.current = retained;
|
|
1069
|
+
oldestSequenceRef.current = retained.events[0]?.sequence ?? null;
|
|
1070
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
|
|
1071
|
+
lastSequenceRef.current = Math.max(lastSequenceRef.current, plan.resumeSequence);
|
|
1072
|
+
streamResumeSequenceRef.current = Math.max(
|
|
1073
|
+
streamResumeSequenceRef.current,
|
|
1074
|
+
plan.resumeSequence
|
|
1075
|
+
);
|
|
1076
|
+
if (status !== void 0) {
|
|
1077
|
+
setSessionStatusProjection(status);
|
|
1078
|
+
}
|
|
1079
|
+
setEventWindow(retained);
|
|
1080
|
+
if (retained.truncated) {
|
|
1081
|
+
hasOlderRef.current = true;
|
|
1082
|
+
setHasOlder(true);
|
|
1083
|
+
}
|
|
1084
|
+
} else if (plan.kind === "reload") {
|
|
1085
|
+
initialWindowLoadedRef.current = false;
|
|
1086
|
+
setError(null);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
382
1089
|
if (!fullReplay && !initialWindowLoadedRef.current) {
|
|
383
1090
|
setConnectionState("connecting");
|
|
384
1091
|
const window = await loadEventWindow(client, workspaceId, sessionId, {
|
|
385
1092
|
before: Number.MAX_SAFE_INTEGER,
|
|
386
|
-
pageSize:
|
|
1093
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
387
1094
|
targetGroups: Number.POSITIVE_INFINITY,
|
|
388
1095
|
maxFetches: INITIAL_FETCH_CAP,
|
|
1096
|
+
boundaryPageCap: BOUNDARY_PAGE_CAP,
|
|
389
1097
|
signal: controller.signal
|
|
390
1098
|
});
|
|
391
1099
|
if (!isCurrent()) {
|
|
392
1100
|
return;
|
|
393
1101
|
}
|
|
394
|
-
observeSessionStatus(window.events, sessionStatusRef
|
|
1102
|
+
const status = observeSessionStatus(window.events, sessionStatusRef);
|
|
395
1103
|
const retained = boundBrowserSessionEventWindow(window.events);
|
|
1104
|
+
navigationGenerationRef.current += 1;
|
|
1105
|
+
loadingOlderRef.current = false;
|
|
1106
|
+
loadingNewerRef.current = false;
|
|
1107
|
+
loadingOldestRef.current = false;
|
|
1108
|
+
setLoadingOlder(false);
|
|
1109
|
+
setLoadingNewer(false);
|
|
1110
|
+
setLoadingOldest(false);
|
|
396
1111
|
eventWindowRef.current = retained;
|
|
397
|
-
setEventWindow(retained);
|
|
398
1112
|
oldestSequenceRef.current = retained.events[0]?.sequence ?? window.oldestSequence;
|
|
399
|
-
newestSequenceRef.current = retained.events
|
|
1113
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events) ?? window.newestSequence;
|
|
400
1114
|
hasOlderRef.current = window.hasOlder || retained.truncated;
|
|
401
1115
|
hasNewerRef.current = false;
|
|
402
1116
|
lastSequenceRef.current = window.newestSequence;
|
|
403
1117
|
streamResumeSequenceRef.current = window.newestSequence;
|
|
404
1118
|
initialWindowLoadedRef.current = true;
|
|
1119
|
+
setInitialHistoryReady(true);
|
|
1120
|
+
setError(null);
|
|
1121
|
+
if (status !== void 0) {
|
|
1122
|
+
setSessionStatusProjection(status);
|
|
1123
|
+
}
|
|
1124
|
+
setEventWindow(retained);
|
|
405
1125
|
setHasOlder(window.hasOlder || retained.truncated);
|
|
406
1126
|
setHasNewer(false);
|
|
407
1127
|
setInitialLoading(false);
|
|
@@ -415,7 +1135,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
415
1135
|
onOpen: () => {
|
|
416
1136
|
void Promise.resolve().then(() => reconcileSession(sessionId)).catch((cause) => {
|
|
417
1137
|
if (isCurrent()) {
|
|
418
|
-
setError(
|
|
1138
|
+
setError(normalizeError(cause));
|
|
419
1139
|
}
|
|
420
1140
|
});
|
|
421
1141
|
},
|
|
@@ -427,7 +1147,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
427
1147
|
});
|
|
428
1148
|
for await (const event of stream) {
|
|
429
1149
|
if (!isCurrent()) break;
|
|
430
|
-
const boundedEvent =
|
|
1150
|
+
const boundedEvent = event;
|
|
431
1151
|
const boundedEventBytes = browserJsonBytes(boundedEvent);
|
|
432
1152
|
const separatorBytes = pending.length === 0 ? 0 : 1;
|
|
433
1153
|
if (pending.length > 0 && (pending.length >= SESSION_EVENT_BROWSER_PENDING_MAX_COUNT || pendingBytes + separatorBytes + boundedEventBytes > SESSION_EVENT_BROWSER_PENDING_MAX_BYTES)) {
|
|
@@ -448,8 +1168,9 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
448
1168
|
} catch (cause) {
|
|
449
1169
|
if (isCurrent()) {
|
|
450
1170
|
flush();
|
|
451
|
-
setError(
|
|
1171
|
+
setError(normalizeError(cause));
|
|
452
1172
|
setConnectionState("error");
|
|
1173
|
+
setInitialLoading(false);
|
|
453
1174
|
}
|
|
454
1175
|
}
|
|
455
1176
|
})();
|
|
@@ -470,6 +1191,8 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
470
1191
|
workspaceId,
|
|
471
1192
|
sessionId,
|
|
472
1193
|
after,
|
|
1194
|
+
enabled,
|
|
1195
|
+
pageLive,
|
|
473
1196
|
streamEnabled,
|
|
474
1197
|
fullReplay,
|
|
475
1198
|
streamKey,
|
|
@@ -477,10 +1200,10 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
477
1200
|
viewMode,
|
|
478
1201
|
reconcileSession
|
|
479
1202
|
]);
|
|
480
|
-
const navigationBusy = () => loadingOlderRef.current || loadingNewerRef.current || loadingOldestRef.current || loadingLatestRef.current;
|
|
481
|
-
const loadOlder =
|
|
482
|
-
() => createOlderHistoryLoadReceipt(async (markCommitted) => {
|
|
483
|
-
if (!sessionId || navigationBusy() || !hasOlderRef.current) {
|
|
1203
|
+
const navigationBusy = () => loadingTargetRef.current || loadingOlderRef.current || loadingNewerRef.current || loadingOldestRef.current || loadingLatestRef.current;
|
|
1204
|
+
const loadOlder = useCallback6(
|
|
1205
|
+
() => createOlderHistoryLoadReceipt(async (markCommitted, preserveTail, markTailPreserved) => {
|
|
1206
|
+
if (!ownsNavigation() || !sessionId || navigationBusy() || !hasOlderRef.current) {
|
|
484
1207
|
return false;
|
|
485
1208
|
}
|
|
486
1209
|
const before = oldestSequenceRef.current;
|
|
@@ -490,17 +1213,20 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
490
1213
|
setHasOlder(false);
|
|
491
1214
|
return false;
|
|
492
1215
|
}
|
|
493
|
-
const generation =
|
|
1216
|
+
const generation = navigationGenerationRef.current;
|
|
1217
|
+
const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
|
|
494
1218
|
loadingOlderRef.current = true;
|
|
495
1219
|
setLoadingOlder(true);
|
|
1220
|
+
let published = false;
|
|
496
1221
|
try {
|
|
497
1222
|
const window = await loadEventWindow(client, workspaceId, sessionId, {
|
|
498
1223
|
before,
|
|
499
|
-
pageSize:
|
|
1224
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
500
1225
|
targetGroups: OLDER_GROUP_TARGET,
|
|
501
|
-
maxFetches: OLDER_FETCH_CAP
|
|
1226
|
+
maxFetches: OLDER_FETCH_CAP,
|
|
1227
|
+
isCurrent
|
|
502
1228
|
});
|
|
503
|
-
if (
|
|
1229
|
+
if (!isCurrent()) {
|
|
504
1230
|
return false;
|
|
505
1231
|
}
|
|
506
1232
|
if (window.events.length === 0) {
|
|
@@ -511,11 +1237,14 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
511
1237
|
}
|
|
512
1238
|
const current = eventWindowRef.current;
|
|
513
1239
|
assertPrependOrder(current.events, window.events);
|
|
514
|
-
streamAbortRef.current?.abort();
|
|
515
|
-
observeSessionStatus(window.events, sessionStatusRef, setSessionStatusProjection);
|
|
516
1240
|
const next = boundBrowserSessionEventWindow([...window.events, ...current.events], {
|
|
517
1241
|
direction: "oldest"
|
|
518
1242
|
});
|
|
1243
|
+
if (preserveTail && maxResumeSequence(next.events) < maxResumeSequence(current.events)) {
|
|
1244
|
+
markTailPreserved();
|
|
1245
|
+
return false;
|
|
1246
|
+
}
|
|
1247
|
+
const status = observeSessionStatus(window.events, sessionStatusRef);
|
|
519
1248
|
const retained = {
|
|
520
1249
|
...next,
|
|
521
1250
|
truncated: current.truncated || next.truncated
|
|
@@ -524,47 +1253,80 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
524
1253
|
if (retainedOldest === null || retainedOldest >= before) {
|
|
525
1254
|
throw new Error("@opengeni/react: loadOlder made no durable sequence progress");
|
|
526
1255
|
}
|
|
527
|
-
const retainedNewest = retained.events
|
|
1256
|
+
const retainedNewest = maxResumeSequenceOrNull(retained.events);
|
|
1257
|
+
const previousNewest = maxResumeSequenceOrNull(current.events);
|
|
1258
|
+
const evictedLiveTail = previousNewest !== null && retainedNewest !== null && retainedNewest < previousNewest;
|
|
1259
|
+
if (evictedLiveTail && viewModeRef.current === "live") {
|
|
1260
|
+
streamAbortRef.current?.abort();
|
|
1261
|
+
}
|
|
528
1262
|
eventWindowRef.current = retained;
|
|
529
1263
|
markCommitted();
|
|
530
|
-
setEventWindow(retained);
|
|
531
1264
|
oldestSequenceRef.current = retainedOldest;
|
|
532
1265
|
newestSequenceRef.current = retainedNewest;
|
|
533
|
-
|
|
1266
|
+
if (evictedLiveTail) {
|
|
1267
|
+
streamResumeSequenceRef.current = maxResumeSequence(retained.events);
|
|
1268
|
+
}
|
|
534
1269
|
const olderStillAvailable = window.hasOlder;
|
|
535
1270
|
hasOlderRef.current = olderStillAvailable;
|
|
536
|
-
|
|
537
|
-
|
|
1271
|
+
let newer = null;
|
|
1272
|
+
let enterHistory = false;
|
|
1273
|
+
if (viewModeRef.current === "history" || evictedLiveTail) {
|
|
538
1274
|
const highWater = lastSequenceRef.current;
|
|
539
|
-
|
|
1275
|
+
newer = retainedNewest !== null && (retainedNewest < highWater || retained.truncated);
|
|
540
1276
|
hasNewerRef.current = newer;
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
1277
|
+
if (evictedLiveTail) {
|
|
1278
|
+
viewModeRef.current = "history";
|
|
1279
|
+
enterHistory = true;
|
|
1280
|
+
}
|
|
544
1281
|
}
|
|
1282
|
+
loadingOlderRef.current = false;
|
|
1283
|
+
startTransition(() => {
|
|
1284
|
+
if (status !== void 0) {
|
|
1285
|
+
setSessionStatusProjection(status);
|
|
1286
|
+
}
|
|
1287
|
+
setEventWindow(retained);
|
|
1288
|
+
setHasOlder(olderStillAvailable);
|
|
1289
|
+
if (newer !== null) {
|
|
1290
|
+
setHasNewer(newer);
|
|
1291
|
+
}
|
|
1292
|
+
if (enterHistory) {
|
|
1293
|
+
setViewMode("history");
|
|
1294
|
+
}
|
|
1295
|
+
setLoadingOlder(false);
|
|
1296
|
+
});
|
|
1297
|
+
published = true;
|
|
545
1298
|
return olderStillAvailable;
|
|
1299
|
+
} catch (reason) {
|
|
1300
|
+
if (!isCurrent()) return false;
|
|
1301
|
+
throw reason;
|
|
546
1302
|
} finally {
|
|
547
|
-
|
|
548
|
-
|
|
1303
|
+
if (!published && isCurrent()) {
|
|
1304
|
+
loadingOlderRef.current = false;
|
|
1305
|
+
setLoadingOlder(false);
|
|
1306
|
+
}
|
|
549
1307
|
}
|
|
550
1308
|
}),
|
|
551
|
-
[client, workspaceId, sessionId]
|
|
1309
|
+
[client, workspaceId, sessionId, ownsNavigation]
|
|
552
1310
|
);
|
|
553
|
-
const loadOldest =
|
|
554
|
-
if (!sessionId || navigationBusy() || !hasOlderRef.current) {
|
|
1311
|
+
const loadOldest = useCallback6(async () => {
|
|
1312
|
+
if (!ownsNavigation() || !sessionId || navigationBusy() || !hasOlderRef.current) {
|
|
555
1313
|
return false;
|
|
556
1314
|
}
|
|
557
|
-
const generation =
|
|
1315
|
+
const generation = ++navigationGenerationRef.current;
|
|
1316
|
+
const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
|
|
1317
|
+
setQuestionEvidence(null);
|
|
558
1318
|
loadingOldestRef.current = true;
|
|
559
1319
|
setLoadingOldest(true);
|
|
1320
|
+
let published = false;
|
|
560
1321
|
try {
|
|
561
1322
|
const window = await loadForwardEventWindow(client, workspaceId, sessionId, {
|
|
562
1323
|
after: 0,
|
|
563
|
-
pageSize:
|
|
1324
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
564
1325
|
targetGroups: OLDEST_GROUP_TARGET,
|
|
565
|
-
maxFetches: OLDEST_FETCH_CAP
|
|
1326
|
+
maxFetches: OLDEST_FETCH_CAP,
|
|
1327
|
+
isCurrent
|
|
566
1328
|
});
|
|
567
|
-
if (
|
|
1329
|
+
if (!isCurrent()) {
|
|
568
1330
|
return false;
|
|
569
1331
|
}
|
|
570
1332
|
if (window.events.length === 0) {
|
|
@@ -573,32 +1335,48 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
573
1335
|
return false;
|
|
574
1336
|
}
|
|
575
1337
|
streamAbortRef.current?.abort();
|
|
576
|
-
observeSessionStatus(window.events, sessionStatusRef
|
|
1338
|
+
const status = observeSessionStatus(window.events, sessionStatusRef);
|
|
577
1339
|
const retained = boundBrowserSessionEventWindow(window.events, { direction: "oldest" });
|
|
578
1340
|
const retainedOldest = retained.events[0]?.sequence ?? null;
|
|
579
|
-
const retainedNewest = retained.events
|
|
1341
|
+
const retainedNewest = maxResumeSequenceOrNull(retained.events);
|
|
580
1342
|
eventWindowRef.current = retained;
|
|
581
|
-
setEventWindow(retained);
|
|
582
1343
|
oldestSequenceRef.current = retainedOldest;
|
|
583
1344
|
newestSequenceRef.current = retainedNewest;
|
|
584
1345
|
hasOlderRef.current = false;
|
|
585
|
-
setHasOlder(false);
|
|
586
1346
|
const highWater = Math.max(lastSequenceRef.current, retainedNewest ?? 0);
|
|
587
1347
|
lastSequenceRef.current = highWater;
|
|
588
1348
|
const newer = window.hasNewer || retained.truncated || retainedNewest !== null && retainedNewest < highWater;
|
|
589
1349
|
hasNewerRef.current = newer;
|
|
590
|
-
setHasNewer(newer);
|
|
591
1350
|
initialWindowLoadedRef.current = true;
|
|
1351
|
+
setInitialHistoryReady(true);
|
|
1352
|
+
setError(null);
|
|
592
1353
|
viewModeRef.current = "history";
|
|
593
|
-
|
|
1354
|
+
loadingOldestRef.current = false;
|
|
1355
|
+
setNewerError(null);
|
|
1356
|
+
startTransition(() => {
|
|
1357
|
+
if (status !== void 0) {
|
|
1358
|
+
setSessionStatusProjection(status);
|
|
1359
|
+
}
|
|
1360
|
+
setEventWindow(retained);
|
|
1361
|
+
setHasOlder(false);
|
|
1362
|
+
setHasNewer(newer);
|
|
1363
|
+
setViewMode("history");
|
|
1364
|
+
setLoadingOldest(false);
|
|
1365
|
+
});
|
|
1366
|
+
published = true;
|
|
594
1367
|
return newer;
|
|
1368
|
+
} catch (reason) {
|
|
1369
|
+
if (!isCurrent()) return false;
|
|
1370
|
+
throw reason;
|
|
595
1371
|
} finally {
|
|
596
|
-
|
|
597
|
-
|
|
1372
|
+
if (!published && isCurrent()) {
|
|
1373
|
+
loadingOldestRef.current = false;
|
|
1374
|
+
setLoadingOldest(false);
|
|
1375
|
+
}
|
|
598
1376
|
}
|
|
599
|
-
}, [client, workspaceId, sessionId]);
|
|
600
|
-
const loadNewer =
|
|
601
|
-
if (!sessionId || navigationBusy() || !hasNewerRef.current) {
|
|
1377
|
+
}, [client, workspaceId, sessionId, ownsNavigation]);
|
|
1378
|
+
const loadNewer = useCallback6(async () => {
|
|
1379
|
+
if (!ownsNavigation() || !sessionId || navigationBusy() || !hasNewerRef.current) {
|
|
602
1380
|
return false;
|
|
603
1381
|
}
|
|
604
1382
|
const afterSequence = newestSequenceRef.current;
|
|
@@ -607,20 +1385,24 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
607
1385
|
setHasNewer(false);
|
|
608
1386
|
return false;
|
|
609
1387
|
}
|
|
610
|
-
const generation =
|
|
1388
|
+
const generation = navigationGenerationRef.current;
|
|
1389
|
+
const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
|
|
611
1390
|
loadingNewerRef.current = true;
|
|
612
1391
|
setLoadingNewer(true);
|
|
1392
|
+
let published = false;
|
|
613
1393
|
try {
|
|
614
1394
|
const window = await loadForwardEventWindow(client, workspaceId, sessionId, {
|
|
615
1395
|
after: afterSequence,
|
|
616
|
-
pageSize:
|
|
1396
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
617
1397
|
targetGroups: NEWER_GROUP_TARGET,
|
|
618
|
-
maxFetches: NEWER_FETCH_CAP
|
|
1398
|
+
maxFetches: NEWER_FETCH_CAP,
|
|
1399
|
+
isCurrent
|
|
619
1400
|
});
|
|
620
|
-
if (
|
|
1401
|
+
if (!isCurrent()) {
|
|
621
1402
|
return false;
|
|
622
1403
|
}
|
|
623
1404
|
if (window.events.length === 0) {
|
|
1405
|
+
setNewerError(null);
|
|
624
1406
|
hasNewerRef.current = false;
|
|
625
1407
|
setHasNewer(false);
|
|
626
1408
|
streamResumeSequenceRef.current = afterSequence;
|
|
@@ -631,85 +1413,246 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
631
1413
|
}
|
|
632
1414
|
const current = eventWindowRef.current;
|
|
633
1415
|
assertAppendOrder(current.events, window.events);
|
|
634
|
-
observeSessionStatus(window.events, sessionStatusRef
|
|
1416
|
+
const status = observeSessionStatus(window.events, sessionStatusRef);
|
|
635
1417
|
const previousOldest = current.events[0]?.sequence ?? null;
|
|
636
|
-
const next =
|
|
637
|
-
direction: "newest"
|
|
638
|
-
});
|
|
1418
|
+
const next = appendBrowserSessionEventWindow(current, window.events);
|
|
639
1419
|
const retained = {
|
|
640
1420
|
...next,
|
|
641
1421
|
truncated: current.truncated || next.truncated || window.hasNewer
|
|
642
1422
|
};
|
|
643
1423
|
const retainedOldest = retained.events[0]?.sequence ?? null;
|
|
644
|
-
const retainedNewest = retained.events
|
|
1424
|
+
const retainedNewest = maxResumeSequenceOrNull(retained.events);
|
|
645
1425
|
if (retainedNewest === null || retainedNewest <= afterSequence) {
|
|
646
1426
|
throw new Error("@opengeni/react: loadNewer made no durable sequence progress");
|
|
647
1427
|
}
|
|
648
1428
|
eventWindowRef.current = retained;
|
|
649
|
-
setEventWindow(retained);
|
|
650
1429
|
oldestSequenceRef.current = retainedOldest;
|
|
651
1430
|
newestSequenceRef.current = retainedNewest;
|
|
652
1431
|
if (retainedOldest !== null && (previousOldest === null || retainedOldest > previousOldest || retained.truncated && retained.events[0]?.type !== "session.created" && retainedOldest > 1)) {
|
|
653
1432
|
hasOlderRef.current = true;
|
|
654
|
-
setHasOlder(true);
|
|
655
1433
|
}
|
|
1434
|
+
const rearmedOlder = hasOlderRef.current;
|
|
656
1435
|
const highWater = Math.max(lastSequenceRef.current, retainedNewest);
|
|
657
1436
|
lastSequenceRef.current = highWater;
|
|
658
1437
|
const newer = window.hasNewer || retainedNewest < highWater;
|
|
659
1438
|
hasNewerRef.current = newer;
|
|
660
|
-
|
|
1439
|
+
let resumeLive = false;
|
|
661
1440
|
if (!newer) {
|
|
662
1441
|
streamResumeSequenceRef.current = retainedNewest;
|
|
663
1442
|
viewModeRef.current = "live";
|
|
664
|
-
|
|
665
|
-
setStreamEpoch((epoch) => epoch + 1);
|
|
1443
|
+
resumeLive = true;
|
|
666
1444
|
}
|
|
1445
|
+
loadingNewerRef.current = false;
|
|
1446
|
+
setNewerError(null);
|
|
1447
|
+
startTransition(() => {
|
|
1448
|
+
if (status !== void 0) {
|
|
1449
|
+
setSessionStatusProjection(status);
|
|
1450
|
+
}
|
|
1451
|
+
setEventWindow(retained);
|
|
1452
|
+
setHasOlder(rearmedOlder);
|
|
1453
|
+
setHasNewer(newer);
|
|
1454
|
+
if (resumeLive) {
|
|
1455
|
+
setViewMode("live");
|
|
1456
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
1457
|
+
}
|
|
1458
|
+
setLoadingNewer(false);
|
|
1459
|
+
});
|
|
1460
|
+
published = true;
|
|
667
1461
|
return newer;
|
|
1462
|
+
} catch (reason) {
|
|
1463
|
+
if (!isCurrent()) {
|
|
1464
|
+
return false;
|
|
1465
|
+
}
|
|
1466
|
+
setNewerError(normalizeError(reason));
|
|
1467
|
+
throw reason;
|
|
668
1468
|
} finally {
|
|
669
|
-
|
|
1469
|
+
if (!published && isCurrent()) {
|
|
1470
|
+
loadingNewerRef.current = false;
|
|
1471
|
+
setLoadingNewer(false);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}, [client, workspaceId, sessionId, ownsNavigation]);
|
|
1475
|
+
const jumpToSequence = useCallback6(
|
|
1476
|
+
async (sequence, navigationOptions) => {
|
|
1477
|
+
const signal = navigationOptions?.signal;
|
|
1478
|
+
if (!ownsNavigation() || !sessionId || !Number.isSafeInteger(sequence) || sequence < 1)
|
|
1479
|
+
return false;
|
|
1480
|
+
if (signal?.aborted) return false;
|
|
1481
|
+
const generation = ++navigationGenerationRef.current;
|
|
1482
|
+
setQuestionEvidence(null);
|
|
1483
|
+
const current = () => generation === navigationGenerationRef.current && ownsNavigation();
|
|
1484
|
+
const previousMode = viewModeRef.current;
|
|
1485
|
+
streamAbortRef.current?.abort();
|
|
1486
|
+
generationRef.current += 1;
|
|
1487
|
+
viewModeRef.current = "history";
|
|
1488
|
+
setViewMode("history");
|
|
1489
|
+
loadingOlderRef.current = loadingNewerRef.current = loadingOldestRef.current = false;
|
|
1490
|
+
setLoadingOlder(false);
|
|
670
1491
|
setLoadingNewer(false);
|
|
1492
|
+
setLoadingOldest(false);
|
|
1493
|
+
loadingTargetRef.current = true;
|
|
1494
|
+
setLoadingTarget(true);
|
|
1495
|
+
let published = false;
|
|
1496
|
+
try {
|
|
1497
|
+
const [before, following] = await Promise.all([
|
|
1498
|
+
loadPreviousPage(client, workspaceId, sessionId, sequence + 1, { pageSize: 128 }),
|
|
1499
|
+
loadNextPage(client, workspaceId, sessionId, sequence, { pageSize: 128 })
|
|
1500
|
+
]);
|
|
1501
|
+
if (signal?.aborted) return false;
|
|
1502
|
+
if (!current()) return false;
|
|
1503
|
+
if (!before.some((event) => event.sequence === sequence)) return false;
|
|
1504
|
+
const prefix = boundBrowserSessionEventWindow(before, {
|
|
1505
|
+
direction: "newest",
|
|
1506
|
+
maxBytes: SESSION_EVENT_BROWSER_MAX_BYTES / 2
|
|
1507
|
+
});
|
|
1508
|
+
const retained = boundBrowserSessionEventWindow([...prefix.events, ...following], {
|
|
1509
|
+
direction: "oldest"
|
|
1510
|
+
});
|
|
1511
|
+
if (!retained.events.some((event) => event.sequence === sequence)) return false;
|
|
1512
|
+
streamAbortRef.current?.abort();
|
|
1513
|
+
generationRef.current += 1;
|
|
1514
|
+
const status = observeSessionStatus(retained.events, sessionStatusRef);
|
|
1515
|
+
eventWindowRef.current = retained;
|
|
1516
|
+
oldestSequenceRef.current = retained.events[0].sequence;
|
|
1517
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
|
|
1518
|
+
lastSequenceRef.current = Math.max(lastSequenceRef.current, newestSequenceRef.current ?? 0);
|
|
1519
|
+
hasOlderRef.current = !isLogStart(retained.events[0]);
|
|
1520
|
+
hasNewerRef.current = following.length > 0 || retained.truncated || (newestSequenceRef.current ?? 0) < lastSequenceRef.current;
|
|
1521
|
+
initialWindowLoadedRef.current = true;
|
|
1522
|
+
setInitialHistoryReady(true);
|
|
1523
|
+
setError(null);
|
|
1524
|
+
viewModeRef.current = "history";
|
|
1525
|
+
setEventWindow(retained);
|
|
1526
|
+
setHasOlder(hasOlderRef.current);
|
|
1527
|
+
setHasNewer(hasNewerRef.current);
|
|
1528
|
+
setInitialLoading(false);
|
|
1529
|
+
setViewMode("history");
|
|
1530
|
+
setNewerError(null);
|
|
1531
|
+
if (status !== void 0) setSessionStatusProjection(status);
|
|
1532
|
+
published = true;
|
|
1533
|
+
return true;
|
|
1534
|
+
} catch (reason) {
|
|
1535
|
+
if (!current() || signal?.aborted) return false;
|
|
1536
|
+
throw reason;
|
|
1537
|
+
} finally {
|
|
1538
|
+
if (current()) {
|
|
1539
|
+
loadingTargetRef.current = false;
|
|
1540
|
+
setLoadingTarget(false);
|
|
1541
|
+
if (!published && previousMode === "live") {
|
|
1542
|
+
viewModeRef.current = "live";
|
|
1543
|
+
setViewMode("live");
|
|
1544
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
},
|
|
1549
|
+
[client, workspaceId, sessionId, ownsNavigation]
|
|
1550
|
+
);
|
|
1551
|
+
const jumpToLatestQuestion = useCallback6(
|
|
1552
|
+
async (questionOptions) => {
|
|
1553
|
+
if (!sessionId || !ownsNavigation()) return null;
|
|
1554
|
+
let generation = navigationGenerationRef.current;
|
|
1555
|
+
const current = () => ownsNavigation() && generation === navigationGenerationRef.current;
|
|
1556
|
+
try {
|
|
1557
|
+
const { resolveLatestQuestion } = await import("./latest-question-R77KWSUP.js");
|
|
1558
|
+
if (!current()) return null;
|
|
1559
|
+
const destination = await resolveLatestQuestion({
|
|
1560
|
+
client,
|
|
1561
|
+
workspaceId,
|
|
1562
|
+
sessionId,
|
|
1563
|
+
isCurrent: current,
|
|
1564
|
+
resumeSequence: maxResumeSequence,
|
|
1565
|
+
options: questionOptions
|
|
1566
|
+
});
|
|
1567
|
+
if (!destination || !current()) return null;
|
|
1568
|
+
if (!eventWindowRef.current.events.some((event) => event.sequence === destination.anchor)) {
|
|
1569
|
+
const navigation = jumpToSequence(destination.anchor);
|
|
1570
|
+
generation = navigationGenerationRef.current;
|
|
1571
|
+
if (!await navigation || !current()) return null;
|
|
1572
|
+
}
|
|
1573
|
+
setQuestionEvidence({
|
|
1574
|
+
client,
|
|
1575
|
+
streamKey,
|
|
1576
|
+
anchor: destination.anchor,
|
|
1577
|
+
events: destination.events
|
|
1578
|
+
});
|
|
1579
|
+
return destination.questionSequence;
|
|
1580
|
+
} catch (reason) {
|
|
1581
|
+
if (!current()) return null;
|
|
1582
|
+
throw reason;
|
|
1583
|
+
}
|
|
1584
|
+
},
|
|
1585
|
+
[client, workspaceId, sessionId, streamKey, ownsNavigation, jumpToSequence]
|
|
1586
|
+
);
|
|
1587
|
+
const jumpToLatest = useCallback6(async () => {
|
|
1588
|
+
if (!ownsNavigation()) return;
|
|
1589
|
+
if (loadingTargetRef.current) {
|
|
1590
|
+
navigationGenerationRef.current += 1;
|
|
1591
|
+
loadingTargetRef.current = false;
|
|
1592
|
+
setLoadingTarget(false);
|
|
671
1593
|
}
|
|
672
|
-
}, [client, workspaceId, sessionId]);
|
|
673
|
-
const jumpToLatest = useCallback2(async () => {
|
|
674
1594
|
if (!sessionId || navigationBusy()) {
|
|
675
1595
|
return;
|
|
676
1596
|
}
|
|
1597
|
+
navigationGenerationRef.current += 1;
|
|
1598
|
+
setQuestionEvidence(null);
|
|
677
1599
|
loadingLatestRef.current = true;
|
|
678
1600
|
setLoadingLatest(true);
|
|
1601
|
+
setNewerError(null);
|
|
1602
|
+
setError(null);
|
|
1603
|
+
let published = false;
|
|
679
1604
|
try {
|
|
680
1605
|
streamAbortRef.current?.abort();
|
|
681
1606
|
hasNewerRef.current = false;
|
|
682
|
-
setHasNewer(false);
|
|
683
1607
|
hasOlderRef.current = false;
|
|
684
|
-
setHasOlder(false);
|
|
685
1608
|
newestSequenceRef.current = null;
|
|
686
1609
|
oldestSequenceRef.current = null;
|
|
687
1610
|
eventWindowRef.current = EMPTY_EVENT_WINDOW;
|
|
688
|
-
setEventWindow(EMPTY_EVENT_WINDOW);
|
|
689
1611
|
initialWindowLoadedRef.current = false;
|
|
690
1612
|
streamResumeSequenceRef.current = after;
|
|
691
|
-
setInitialLoading(true);
|
|
692
1613
|
viewModeRef.current = "live";
|
|
693
|
-
|
|
694
|
-
|
|
1614
|
+
loadingLatestRef.current = false;
|
|
1615
|
+
startTransition(() => {
|
|
1616
|
+
setHasNewer(false);
|
|
1617
|
+
setHasOlder(false);
|
|
1618
|
+
setEventWindow(EMPTY_EVENT_WINDOW);
|
|
1619
|
+
setInitialLoading(true);
|
|
1620
|
+
setViewMode("live");
|
|
1621
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
1622
|
+
setLoadingLatest(false);
|
|
1623
|
+
});
|
|
1624
|
+
published = true;
|
|
695
1625
|
await Promise.resolve();
|
|
696
1626
|
} finally {
|
|
697
|
-
|
|
698
|
-
|
|
1627
|
+
if (!published) {
|
|
1628
|
+
loadingLatestRef.current = false;
|
|
1629
|
+
setLoadingLatest(false);
|
|
1630
|
+
}
|
|
699
1631
|
}
|
|
700
|
-
}, [after, sessionId]);
|
|
1632
|
+
}, [after, sessionId, ownsNavigation]);
|
|
701
1633
|
const identityMatches = stateStreamKey === streamKey;
|
|
702
1634
|
const visibleEvents = identityMatches ? eventWindow.events : EMPTY_EVENTS;
|
|
703
|
-
const timeline = useMemo2(() =>
|
|
1635
|
+
const timeline = useMemo2(() => {
|
|
1636
|
+
const witness = questionEvidence?.client === client && questionEvidence.streamKey === streamKey && visibleEvents.some((event) => event.sequence === questionEvidence.anchor) ? questionEvidence.events : EMPTY_EVENTS;
|
|
1637
|
+
const timelineOptions = { partialStart: hasOlder || eventWindow.truncated || after > 0 };
|
|
1638
|
+
if (witness.length === 0) return buildTimeline(visibleEvents, timelineOptions);
|
|
1639
|
+
const ids = new Set(visibleEvents.map((event) => event.id));
|
|
1640
|
+
return buildTimeline(
|
|
1641
|
+
[...visibleEvents, ...witness.filter((event) => !ids.has(event.id))],
|
|
1642
|
+
timelineOptions
|
|
1643
|
+
);
|
|
1644
|
+
}, [visibleEvents, hasOlder, eventWindow.truncated, after, questionEvidence, client, streamKey]);
|
|
704
1645
|
return {
|
|
705
1646
|
events: visibleEvents,
|
|
706
1647
|
timeline,
|
|
707
1648
|
sessionStatus: identityMatches ? sessionStatusProjection : null,
|
|
1649
|
+
sessionStatusSequence: identityMatches ? sessionStatusRef.current.sequence : 0,
|
|
708
1650
|
connectionState: identityMatches ? connectionState : "idle",
|
|
709
1651
|
lastSequence: identityMatches ? lastSequenceRef.current : after,
|
|
710
1652
|
windowBytes: identityMatches ? eventWindow.bytes : 2,
|
|
711
1653
|
windowTruncated: identityMatches ? eventWindow.truncated : false,
|
|
712
1654
|
initialLoading: fullReplay ? false : identityMatches ? initialLoading : true,
|
|
1655
|
+
initialHistoryReady: identityMatches && initialHistoryReady,
|
|
713
1656
|
hasOlder: !identityMatches ? false : hasOlder,
|
|
714
1657
|
loadingOlder: !identityMatches ? false : loadingOlder,
|
|
715
1658
|
loadOlder,
|
|
@@ -720,13 +1663,16 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
720
1663
|
loadOldest,
|
|
721
1664
|
loadingLatest: !identityMatches ? false : loadingLatest,
|
|
722
1665
|
jumpToLatest,
|
|
723
|
-
|
|
1666
|
+
jumpToLatestQuestion,
|
|
1667
|
+
jumpToSequence,
|
|
1668
|
+
loadingTarget: identityMatches && loadingTarget,
|
|
1669
|
+
error: identityMatches ? newerError ?? error : null
|
|
724
1670
|
};
|
|
725
1671
|
}
|
|
726
1672
|
function boundBrowserSessionEventWindow(events, options = {}) {
|
|
727
1673
|
const maxBytes = Math.max(1024, options.maxBytes ?? SESSION_EVENT_BROWSER_MAX_BYTES);
|
|
728
1674
|
const maxCount = Math.max(1, Math.floor(options.maxCount ?? SESSION_EVENT_BROWSER_MAX_COUNT));
|
|
729
|
-
const safe = events
|
|
1675
|
+
const safe = events;
|
|
730
1676
|
const selected = [];
|
|
731
1677
|
let bytes = 2;
|
|
732
1678
|
const direction = options.direction ?? "newest";
|
|
@@ -737,7 +1683,7 @@ function boundBrowserSessionEventWindow(events, options = {}) {
|
|
|
737
1683
|
const event = safe[index];
|
|
738
1684
|
const eventBytes = browserJsonBytes(event);
|
|
739
1685
|
const separator = selected.length === 0 ? 0 : 1;
|
|
740
|
-
if (bytes + separator + eventBytes > maxBytes) break;
|
|
1686
|
+
if (selected.length > 0 && bytes + separator + eventBytes > maxBytes) break;
|
|
741
1687
|
selected.push(event);
|
|
742
1688
|
bytes += separator + eventBytes;
|
|
743
1689
|
}
|
|
@@ -748,184 +1694,29 @@ function boundBrowserSessionEventWindow(events, options = {}) {
|
|
|
748
1694
|
truncated: selected.length < safe.length
|
|
749
1695
|
};
|
|
750
1696
|
}
|
|
751
|
-
function
|
|
752
|
-
const
|
|
753
|
-
const
|
|
754
|
-
const
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
);
|
|
759
|
-
const duplicateReason = boundBrowserOptionalText(
|
|
760
|
-
event.duplicateReason,
|
|
761
|
-
BROWSER_EVENT_DUPLICATE_REASON_MAX_BYTES
|
|
762
|
-
);
|
|
763
|
-
if (serialized.serializable && originalBytes !== null && originalBytes <= SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES && typeIsSafe && clientEventId === event.clientEventId && duplicateReason === event.duplicateReason) {
|
|
764
|
-
return event;
|
|
765
|
-
}
|
|
766
|
-
const envelopeProjection = [
|
|
767
|
-
!typeIsSafe ? browserEnvelopeFieldProjection("type", event.type, "session.event.envelope_omitted") : null,
|
|
768
|
-
clientEventId !== event.clientEventId ? browserEnvelopeFieldProjection("clientEventId", event.clientEventId, clientEventId) : null,
|
|
769
|
-
duplicateReason !== event.duplicateReason ? browserEnvelopeFieldProjection("duplicateReason", event.duplicateReason, duplicateReason) : null
|
|
770
|
-
].filter((field) => field !== null);
|
|
771
|
-
const payloadSerialization = browserSerialize(event.payload);
|
|
772
|
-
const payloadBytes = payloadSerialization.serializable ? encoder.encode(payloadSerialization.value).byteLength : null;
|
|
773
|
-
const preview = truncateBrowserUtf8Middle(
|
|
774
|
-
payloadSerialization.value,
|
|
775
|
-
BROWSER_EVENT_PAYLOAD_PREVIEW_MAX_BYTES
|
|
776
|
-
);
|
|
777
|
-
const truncation = {
|
|
778
|
-
truncated: true,
|
|
779
|
-
surface: "browser_legacy_guard",
|
|
780
|
-
reason: serialized.serializable ? "event_envelope_bytes_exceeded" : "event_not_serializable",
|
|
781
|
-
originalBytes,
|
|
782
|
-
deliveredBytes: 0,
|
|
783
|
-
omittedBytes: originalBytes,
|
|
784
|
-
estimatedOriginalTokens: originalBytes === null ? null : Math.ceil(originalBytes / 4),
|
|
785
|
-
estimatedDeliveredTokens: 0,
|
|
786
|
-
fullEvidence: { available: false, reason: "not_retained" },
|
|
787
|
-
details: [
|
|
788
|
-
{
|
|
789
|
-
path: "$.payload",
|
|
790
|
-
kind: "payload_preview",
|
|
791
|
-
originalBytes: payloadBytes,
|
|
792
|
-
deliveredBytes: browserUtf8Bytes(preview)
|
|
793
|
-
}
|
|
794
|
-
]
|
|
795
|
-
};
|
|
796
|
-
const payload = {
|
|
797
|
-
...browserPayloadIdentity(event.payload),
|
|
798
|
-
preview,
|
|
799
|
-
...envelopeProjection.length > 0 ? {
|
|
800
|
-
originalType: boundBrowserText(event.type, BROWSER_EVENT_TYPE_MAX_BYTES),
|
|
801
|
-
envelopeProjection: {
|
|
802
|
-
truncated: true,
|
|
803
|
-
surface: "browser_legacy_guard",
|
|
804
|
-
fields: envelopeProjection
|
|
805
|
-
}
|
|
806
|
-
} : {},
|
|
807
|
-
truncation
|
|
808
|
-
};
|
|
809
|
-
const bounded = {
|
|
810
|
-
id: boundBrowserText(event.id, BROWSER_EVENT_ID_MAX_BYTES),
|
|
811
|
-
workspaceId: boundBrowserText(event.workspaceId, BROWSER_EVENT_ID_MAX_BYTES),
|
|
812
|
-
sessionId: boundBrowserText(event.sessionId, BROWSER_EVENT_ID_MAX_BYTES),
|
|
813
|
-
sequence: event.sequence,
|
|
814
|
-
type: typeIsSafe ? event.type : "session.event.envelope_omitted",
|
|
815
|
-
payload,
|
|
816
|
-
occurredAt: boundBrowserText(event.occurredAt, BROWSER_EVENT_ID_MAX_BYTES),
|
|
817
|
-
clientEventId,
|
|
818
|
-
turnId: boundBrowserOptionalText(event.turnId, BROWSER_EVENT_ID_MAX_BYTES),
|
|
819
|
-
turnGeneration: event.turnGeneration,
|
|
820
|
-
turnAttemptId: boundBrowserOptionalText(event.turnAttemptId, BROWSER_EVENT_ID_MAX_BYTES),
|
|
821
|
-
turnAssociation: event.turnAssociation,
|
|
822
|
-
duplicateOfEventId: boundBrowserOptionalText(
|
|
823
|
-
event.duplicateOfEventId,
|
|
824
|
-
BROWSER_EVENT_ID_MAX_BYTES
|
|
825
|
-
),
|
|
826
|
-
duplicateReason
|
|
827
|
-
};
|
|
828
|
-
settleBrowserEventTruncation(bounded, truncation);
|
|
829
|
-
if (browserJsonBytes(bounded) > SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES) {
|
|
830
|
-
payload.preview = truncateBrowserUtf8Middle(String(payload.preview), 4 * 1024);
|
|
831
|
-
truncation.details = truncation.details.slice(0, 1);
|
|
832
|
-
settleBrowserEventTruncation(bounded, truncation);
|
|
833
|
-
}
|
|
834
|
-
if (browserJsonBytes(bounded) > SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES) {
|
|
835
|
-
bounded.clientEventId = null;
|
|
836
|
-
bounded.duplicateReason = null;
|
|
837
|
-
payload.preview = "[legacy event omitted at the browser byte boundary]";
|
|
838
|
-
settleBrowserEventTruncation(bounded, truncation);
|
|
839
|
-
}
|
|
840
|
-
return bounded;
|
|
841
|
-
}
|
|
842
|
-
function browserJsonBytes(value) {
|
|
843
|
-
return encoder.encode(browserSerialize(value).value).byteLength;
|
|
844
|
-
}
|
|
845
|
-
function browserSerialize(value) {
|
|
846
|
-
try {
|
|
847
|
-
const serialized = JSON.stringify(value);
|
|
848
|
-
return { value: serialized === void 0 ? "null" : serialized, serializable: true };
|
|
849
|
-
} catch {
|
|
850
|
-
return {
|
|
851
|
-
value: '"[unserializable event payload omitted at browser boundary]"',
|
|
852
|
-
serializable: false
|
|
853
|
-
};
|
|
1697
|
+
function appendBrowserSessionEventWindow(current, batch, options = {}) {
|
|
1698
|
+
const maxBytes = Math.max(1024, options.maxBytes ?? SESSION_EVENT_BROWSER_MAX_BYTES);
|
|
1699
|
+
const maxCount = Math.max(1, Math.floor(options.maxCount ?? SESSION_EVENT_BROWSER_MAX_COUNT));
|
|
1700
|
+
const events = [...current.events, ...batch];
|
|
1701
|
+
let bytes = current.bytes;
|
|
1702
|
+
for (let index = 0; index < batch.length; index += 1) {
|
|
1703
|
+
bytes += browserJsonBytes(batch[index]) + (current.events.length + index > 0 ? 1 : 0);
|
|
854
1704
|
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
const identity = {};
|
|
860
|
-
for (const field of BROWSER_EVENT_PAYLOAD_IDENTITY_FIELDS) {
|
|
861
|
-
const value = record[field];
|
|
862
|
-
if (typeof value === "string") {
|
|
863
|
-
identity[field] = boundBrowserText(value, BROWSER_EVENT_ID_MAX_BYTES);
|
|
864
|
-
} else if (typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
865
|
-
identity[field] = value;
|
|
866
|
-
}
|
|
1705
|
+
let start = 0;
|
|
1706
|
+
while (events.length - start > 1 && (bytes > maxBytes || events.length - start > maxCount)) {
|
|
1707
|
+
bytes -= browserJsonBytes(events[start]) + 1;
|
|
1708
|
+
start += 1;
|
|
867
1709
|
}
|
|
868
|
-
return identity;
|
|
869
|
-
}
|
|
870
|
-
function browserEnvelopeFieldProjection(field, original, delivered) {
|
|
871
1710
|
return {
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1711
|
+
events: start === 0 ? events : events.slice(start),
|
|
1712
|
+
bytes,
|
|
1713
|
+
truncated: current.truncated || start > 0
|
|
875
1714
|
};
|
|
876
1715
|
}
|
|
877
|
-
function
|
|
878
|
-
return
|
|
879
|
-
}
|
|
880
|
-
function boundBrowserText(value, maxBytes) {
|
|
881
|
-
const bytes = encoder.encode(value);
|
|
882
|
-
if (bytes.byteLength <= maxBytes) return value;
|
|
883
|
-
const marker = "\u2026[truncated]";
|
|
884
|
-
const prefixBudget = Math.max(0, maxBytes - browserUtf8Bytes(marker));
|
|
885
|
-
let prefixEnd = Math.min(prefixBudget, bytes.byteLength);
|
|
886
|
-
while (prefixEnd > 0 && prefixEnd < bytes.byteLength && isBrowserUtf8Continuation(bytes[prefixEnd])) {
|
|
887
|
-
prefixEnd -= 1;
|
|
888
|
-
}
|
|
889
|
-
return `${decoder.decode(bytes.subarray(0, prefixEnd))}${marker}`;
|
|
890
|
-
}
|
|
891
|
-
function truncateBrowserUtf8Middle(value, maxBytes) {
|
|
892
|
-
const bytes = encoder.encode(value);
|
|
893
|
-
if (bytes.byteLength <= maxBytes) return value;
|
|
894
|
-
const marker = `\u2026[${bytes.byteLength - maxBytes} bytes omitted]\u2026`;
|
|
895
|
-
const contentBudget = Math.max(0, maxBytes - browserUtf8Bytes(marker));
|
|
896
|
-
const leftBudget = Math.floor(contentBudget / 2);
|
|
897
|
-
const rightBudget = contentBudget - leftBudget;
|
|
898
|
-
let leftEnd = Math.min(leftBudget, bytes.byteLength);
|
|
899
|
-
while (leftEnd > 0 && leftEnd < bytes.byteLength && isBrowserUtf8Continuation(bytes[leftEnd])) {
|
|
900
|
-
leftEnd -= 1;
|
|
901
|
-
}
|
|
902
|
-
let rightStart = Math.max(0, bytes.byteLength - rightBudget);
|
|
903
|
-
while (rightStart < bytes.byteLength && isBrowserUtf8Continuation(bytes[rightStart])) {
|
|
904
|
-
rightStart += 1;
|
|
905
|
-
}
|
|
906
|
-
return `${decoder.decode(bytes.subarray(0, leftEnd))}${marker}${decoder.decode(bytes.subarray(rightStart))}`;
|
|
907
|
-
}
|
|
908
|
-
function settleBrowserEventTruncation(event, truncation) {
|
|
909
|
-
for (let attempt = 0; attempt < 16; attempt += 1) {
|
|
910
|
-
const deliveredBytes = browserJsonBytes(event);
|
|
911
|
-
const omittedBytes = truncation.originalBytes === null ? null : Math.max(0, truncation.originalBytes - deliveredBytes);
|
|
912
|
-
const estimatedDeliveredTokens = Math.ceil(deliveredBytes / 4);
|
|
913
|
-
if (truncation.deliveredBytes === deliveredBytes && truncation.omittedBytes === omittedBytes && truncation.estimatedDeliveredTokens === estimatedDeliveredTokens) {
|
|
914
|
-
return;
|
|
915
|
-
}
|
|
916
|
-
truncation.deliveredBytes = deliveredBytes;
|
|
917
|
-
truncation.omittedBytes = omittedBytes;
|
|
918
|
-
truncation.estimatedDeliveredTokens = estimatedDeliveredTokens;
|
|
919
|
-
}
|
|
920
|
-
throw new RangeError("Browser event byte accounting did not converge");
|
|
921
|
-
}
|
|
922
|
-
function browserUtf8Bytes(value) {
|
|
923
|
-
return encoder.encode(value).byteLength;
|
|
924
|
-
}
|
|
925
|
-
function isBrowserUtf8Continuation(value) {
|
|
926
|
-
return (value & 192) === 128;
|
|
1716
|
+
function browserJsonBytes(value) {
|
|
1717
|
+
return encoder.encode(JSON.stringify(value)).byteLength;
|
|
927
1718
|
}
|
|
928
|
-
function observeSessionStatus(events, ref
|
|
1719
|
+
function observeSessionStatus(events, ref) {
|
|
929
1720
|
let latest = null;
|
|
930
1721
|
for (const event of events) {
|
|
931
1722
|
const status = sessionStatusFromEvents([event]);
|
|
@@ -933,9 +1724,42 @@ function observeSessionStatus(events, ref, setStatus) {
|
|
|
933
1724
|
latest = { sequence: event.sequence, status };
|
|
934
1725
|
}
|
|
935
1726
|
}
|
|
936
|
-
if (!latest || latest.sequence < ref.current.sequence) return;
|
|
937
|
-
ref.current = latest;
|
|
938
|
-
|
|
1727
|
+
if (!latest || latest.sequence < ref.current.sequence) return void 0;
|
|
1728
|
+
ref.current = latest;
|
|
1729
|
+
return latest.status;
|
|
1730
|
+
}
|
|
1731
|
+
async function planForegroundCatchup(client, workspaceId, sessionId, cursor, options = {}) {
|
|
1732
|
+
if (options.signal?.aborted) throw abortError();
|
|
1733
|
+
const session = await client.getSession(workspaceId, sessionId, {
|
|
1734
|
+
fresh: true,
|
|
1735
|
+
...options.signal ? { signal: options.signal } : {}
|
|
1736
|
+
});
|
|
1737
|
+
if (options.signal?.aborted) throw abortError();
|
|
1738
|
+
const durableHead = Math.max(cursor, session.lastSequence);
|
|
1739
|
+
const rawGap = durableHead - cursor;
|
|
1740
|
+
if (rawGap <= FOREGROUND_DIRECT_REPLAY_MAX_SEQUENCES) {
|
|
1741
|
+
return { kind: "resume" };
|
|
1742
|
+
}
|
|
1743
|
+
if (rawGap > FOREGROUND_COMPACT_PROBE_MAX_SEQUENCES) {
|
|
1744
|
+
return { kind: "reload" };
|
|
1745
|
+
}
|
|
1746
|
+
const page = await loadNextPage(client, workspaceId, sessionId, cursor, {
|
|
1747
|
+
pageSize: rawGap,
|
|
1748
|
+
...options.signal ? { signal: options.signal } : {}
|
|
1749
|
+
});
|
|
1750
|
+
const events = page.filter((event) => eventResumeSequence(event) > cursor);
|
|
1751
|
+
if (events.length === 0) {
|
|
1752
|
+
return { kind: "reload" };
|
|
1753
|
+
}
|
|
1754
|
+
assertAscending(events);
|
|
1755
|
+
const resumeSequence = maxResumeSequence(events);
|
|
1756
|
+
if (resumeSequence < durableHead) {
|
|
1757
|
+
return { kind: "reload" };
|
|
1758
|
+
}
|
|
1759
|
+
if (events.length > FOREGROUND_COMPACT_CATCHUP_MAX_EVENTS || groupCount(events) > FOREGROUND_COMPACT_CATCHUP_MAX_GROUPS || browserJsonBytes(events) > FOREGROUND_COMPACT_CATCHUP_MAX_BYTES) {
|
|
1760
|
+
return { kind: "reload" };
|
|
1761
|
+
}
|
|
1762
|
+
return { kind: "append", events, resumeSequence };
|
|
939
1763
|
}
|
|
940
1764
|
async function loadEventWindow(client, workspaceId, sessionId, options) {
|
|
941
1765
|
let cursor = options.before;
|
|
@@ -950,6 +1774,7 @@ async function loadEventWindow(client, workspaceId, sessionId, options) {
|
|
|
950
1774
|
pageSize: options.pageSize,
|
|
951
1775
|
...options.signal ? { signal: options.signal } : {}
|
|
952
1776
|
});
|
|
1777
|
+
if (options.isCurrent?.() === false) throw abortError();
|
|
953
1778
|
fetches += 1;
|
|
954
1779
|
if (page.length === 0) {
|
|
955
1780
|
reachedStart = true;
|
|
@@ -964,11 +1789,12 @@ async function loadEventWindow(client, workspaceId, sessionId, options) {
|
|
|
964
1789
|
}
|
|
965
1790
|
}
|
|
966
1791
|
let snapPages = 0;
|
|
967
|
-
while (!reachedStart && findBoundaryIndex(buffer) === -1 && snapPages <
|
|
1792
|
+
while (!reachedStart && findBoundaryIndex(buffer) === -1 && snapPages < (options.boundaryPageCap ?? 0)) {
|
|
968
1793
|
const page = await loadPreviousPage(client, workspaceId, sessionId, cursor, {
|
|
969
1794
|
pageSize: options.pageSize,
|
|
970
1795
|
...options.signal ? { signal: options.signal } : {}
|
|
971
1796
|
});
|
|
1797
|
+
if (options.isCurrent?.() === false) throw abortError();
|
|
972
1798
|
fetches += 1;
|
|
973
1799
|
snapPages += 1;
|
|
974
1800
|
if (page.length === 0) {
|
|
@@ -989,24 +1815,28 @@ async function loadEventWindow(client, workspaceId, sessionId, options) {
|
|
|
989
1815
|
buffer = buffer.slice(boundary);
|
|
990
1816
|
}
|
|
991
1817
|
}
|
|
1818
|
+
const densityWindow = trimBackwardToGroupTarget(buffer, options.targetGroups);
|
|
1819
|
+
buffer = densityWindow.events;
|
|
992
1820
|
const oldest = buffer[0] ?? null;
|
|
993
1821
|
const newest = buffer[buffer.length - 1] ?? null;
|
|
994
1822
|
return {
|
|
995
1823
|
events: buffer,
|
|
996
1824
|
oldestSequence: oldest?.sequence ?? null,
|
|
997
1825
|
newestSequence: newest ? maxResumeSequence(buffer) : 0,
|
|
998
|
-
hasOlder: buffer.length > 0 && !reachedStart && oldest?.type !== "session.created"
|
|
1826
|
+
hasOlder: buffer.length > 0 && (densityWindow.trimmed || !reachedStart && oldest?.type !== "session.created")
|
|
999
1827
|
};
|
|
1000
1828
|
}
|
|
1001
1829
|
function findBoundaryIndex(events) {
|
|
1002
1830
|
for (let index = 0; index < events.length; index += 1) {
|
|
1003
|
-
|
|
1004
|
-
if (type === "session.created" || type === "user.message") {
|
|
1831
|
+
if (isTurnBoundary(events[index])) {
|
|
1005
1832
|
return index;
|
|
1006
1833
|
}
|
|
1007
1834
|
}
|
|
1008
1835
|
return -1;
|
|
1009
1836
|
}
|
|
1837
|
+
function isTurnBoundary(event) {
|
|
1838
|
+
return event.type === "session.created" || event.type === "user.message";
|
|
1839
|
+
}
|
|
1010
1840
|
async function loadForwardEventWindow(client, workspaceId, sessionId, options) {
|
|
1011
1841
|
let cursor = options.after;
|
|
1012
1842
|
let buffer = [];
|
|
@@ -1020,6 +1850,7 @@ async function loadForwardEventWindow(client, workspaceId, sessionId, options) {
|
|
|
1020
1850
|
pageSize: options.pageSize,
|
|
1021
1851
|
...options.signal ? { signal: options.signal } : {}
|
|
1022
1852
|
});
|
|
1853
|
+
if (options.isCurrent?.() === false) throw abortError();
|
|
1023
1854
|
fetches += 1;
|
|
1024
1855
|
if (page.length === 0) {
|
|
1025
1856
|
reachedEnd = true;
|
|
@@ -1027,21 +1858,45 @@ async function loadForwardEventWindow(client, workspaceId, sessionId, options) {
|
|
|
1027
1858
|
}
|
|
1028
1859
|
assertAscending(page);
|
|
1029
1860
|
buffer = [...buffer, ...page];
|
|
1030
|
-
cursor = page
|
|
1031
|
-
if (page.length < options.pageSize) {
|
|
1032
|
-
reachedEnd = true;
|
|
1033
|
-
break;
|
|
1034
|
-
}
|
|
1861
|
+
cursor = maxResumeSequence(page);
|
|
1035
1862
|
}
|
|
1863
|
+
const densityWindow = trimForwardToGroupTarget(buffer, options.targetGroups);
|
|
1864
|
+
buffer = densityWindow.events;
|
|
1036
1865
|
const oldest = buffer[0] ?? null;
|
|
1037
1866
|
const newest = buffer[buffer.length - 1] ?? null;
|
|
1038
1867
|
return {
|
|
1039
1868
|
events: buffer,
|
|
1040
1869
|
oldestSequence: oldest?.sequence ?? null,
|
|
1041
1870
|
newestSequence: newest ? maxResumeSequence(buffer) : 0,
|
|
1042
|
-
hasNewer: buffer.length > 0 && !reachedEnd
|
|
1871
|
+
hasNewer: buffer.length > 0 && (densityWindow.trimmed || !reachedEnd)
|
|
1043
1872
|
};
|
|
1044
1873
|
}
|
|
1874
|
+
function trimBackwardToGroupTarget(events, targetGroups) {
|
|
1875
|
+
if (!Number.isFinite(targetGroups) || targetGroups <= 0 || events.length === 0) {
|
|
1876
|
+
return { events, trimmed: false };
|
|
1877
|
+
}
|
|
1878
|
+
for (let index = events.length - 1; index > 0; index -= 1) {
|
|
1879
|
+
if (!isTurnBoundary(events[index])) continue;
|
|
1880
|
+
const candidate = events.slice(index);
|
|
1881
|
+
if (groupCount(candidate) >= targetGroups) {
|
|
1882
|
+
return { events: candidate, trimmed: true };
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
return { events, trimmed: false };
|
|
1886
|
+
}
|
|
1887
|
+
function trimForwardToGroupTarget(events, targetGroups) {
|
|
1888
|
+
if (!Number.isFinite(targetGroups) || targetGroups <= 0 || events.length === 0) {
|
|
1889
|
+
return { events, trimmed: false };
|
|
1890
|
+
}
|
|
1891
|
+
for (let index = 1; index < events.length; index += 1) {
|
|
1892
|
+
if (!isTurnBoundary(events[index])) continue;
|
|
1893
|
+
const candidate = events.slice(0, index);
|
|
1894
|
+
if (groupCount(candidate) >= targetGroups) {
|
|
1895
|
+
return { events: candidate, trimmed: true };
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
return { events, trimmed: false };
|
|
1899
|
+
}
|
|
1045
1900
|
async function loadPreviousPage(client, workspaceId, sessionId, before, options) {
|
|
1046
1901
|
if (options.signal?.aborted) {
|
|
1047
1902
|
throw abortError();
|
|
@@ -1097,13 +1952,13 @@ function isLogStart(event) {
|
|
|
1097
1952
|
function maxResumeSequence(events) {
|
|
1098
1953
|
return events.reduce((max, event) => Math.max(max, eventResumeSequence(event)), 0);
|
|
1099
1954
|
}
|
|
1100
|
-
function
|
|
1101
|
-
|
|
1102
|
-
const coalescedUntil = Number(payload.coalescedUntil);
|
|
1103
|
-
return Math.max(event.sequence, Number.isFinite(coalescedUntil) ? Math.floor(coalescedUntil) : 0);
|
|
1955
|
+
function maxResumeSequenceOrNull(events) {
|
|
1956
|
+
return events.length > 0 ? maxResumeSequence(events) : null;
|
|
1104
1957
|
}
|
|
1105
|
-
function
|
|
1106
|
-
|
|
1958
|
+
function eventResumeSequence(event) {
|
|
1959
|
+
const streamedCoverage = sessionEventStreamCoveredThrough(event);
|
|
1960
|
+
if (streamedCoverage !== null) return streamedCoverage;
|
|
1961
|
+
return typeof event.coveredThrough === "number" && Number.isSafeInteger(event.coveredThrough) && event.coveredThrough >= event.sequence ? event.coveredThrough : event.sequence;
|
|
1107
1962
|
}
|
|
1108
1963
|
function assertAscending(events) {
|
|
1109
1964
|
for (let index = 1; index < events.length; index += 1) {
|
|
@@ -1115,272 +1970,31 @@ function assertAscending(events) {
|
|
|
1115
1970
|
function assertPrependOrder(existing, older) {
|
|
1116
1971
|
if (!shouldAssertDevelopment() || existing.length === 0 || older.length === 0) {
|
|
1117
1972
|
return;
|
|
1118
|
-
}
|
|
1119
|
-
if (older[older.length - 1].sequence >= existing[0].sequence) {
|
|
1120
|
-
throw new Error("@opengeni/react: loadOlder returned overlapping session events");
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
function assertAppendOrder(existing, newer) {
|
|
1124
|
-
if (!shouldAssertDevelopment() || existing.length === 0 || newer.length === 0) {
|
|
1125
|
-
return;
|
|
1126
|
-
}
|
|
1127
|
-
if (newer[0].sequence <= existing[existing.length - 1].sequence) {
|
|
1128
|
-
throw new Error("@opengeni/react: loadNewer returned overlapping session events");
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
function shouldAssertDevelopment() {
|
|
1132
|
-
const processEnv = globalThis.process?.env;
|
|
1133
|
-
return processEnv !== void 0 && processEnv.NODE_ENV !== "production";
|
|
1134
|
-
}
|
|
1135
|
-
function abortError() {
|
|
1136
|
-
const error = new Error("The operation was aborted.");
|
|
1137
|
-
error.name = "AbortError";
|
|
1138
|
-
return error;
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
// src/hooks/use-file-attachments.ts
|
|
1142
|
-
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
1143
|
-
var isImage = (file) => file.type.startsWith("image/");
|
|
1144
|
-
function useFileAttachments(options = {}) {
|
|
1145
|
-
const { client, workspaceId } = useEmbeddedFileAttachments(options);
|
|
1146
|
-
const pasteFilter = options.pasteFilter ?? isImage;
|
|
1147
|
-
const [attachments, setAttachments] = useState3([]);
|
|
1148
|
-
const sources = useRef3(/* @__PURE__ */ new Map());
|
|
1149
|
-
const previewUrls = useRef3(/* @__PURE__ */ new Map());
|
|
1150
|
-
const previewRetainers = useRef3(/* @__PURE__ */ new Map());
|
|
1151
|
-
const pendingPreviewRevocations = useRef3(/* @__PURE__ */ new Set());
|
|
1152
|
-
const revokePreview = useCallback3((id) => {
|
|
1153
|
-
const previewUrl = previewUrls.current.get(id);
|
|
1154
|
-
if (!previewUrl) return;
|
|
1155
|
-
if ((previewRetainers.current.get(id) ?? 0) > 0) {
|
|
1156
|
-
pendingPreviewRevocations.current.add(id);
|
|
1157
|
-
return;
|
|
1158
|
-
}
|
|
1159
|
-
previewUrls.current.delete(id);
|
|
1160
|
-
pendingPreviewRevocations.current.delete(id);
|
|
1161
|
-
URL.revokeObjectURL(previewUrl);
|
|
1162
|
-
}, []);
|
|
1163
|
-
const revokeAllPreviews = useCallback3(() => {
|
|
1164
|
-
for (const id of previewUrls.current.keys()) revokePreview(id);
|
|
1165
|
-
}, [revokePreview]);
|
|
1166
|
-
const retainPreview = useCallback3(
|
|
1167
|
-
(id) => {
|
|
1168
|
-
if (!previewUrls.current.has(id)) return void 0;
|
|
1169
|
-
previewRetainers.current.set(id, (previewRetainers.current.get(id) ?? 0) + 1);
|
|
1170
|
-
let released = false;
|
|
1171
|
-
return () => {
|
|
1172
|
-
if (released) return;
|
|
1173
|
-
released = true;
|
|
1174
|
-
const remaining = (previewRetainers.current.get(id) ?? 1) - 1;
|
|
1175
|
-
if (remaining > 0) {
|
|
1176
|
-
previewRetainers.current.set(id, remaining);
|
|
1177
|
-
return;
|
|
1178
|
-
}
|
|
1179
|
-
previewRetainers.current.delete(id);
|
|
1180
|
-
if (pendingPreviewRevocations.current.has(id)) revokePreview(id);
|
|
1181
|
-
};
|
|
1182
|
-
},
|
|
1183
|
-
[revokePreview]
|
|
1184
|
-
);
|
|
1185
|
-
const committedAttachmentIds = useRef3(/* @__PURE__ */ new Set());
|
|
1186
|
-
useEffect3(() => {
|
|
1187
|
-
const currentIds = new Set(attachments.map((attachment) => attachment.id));
|
|
1188
|
-
for (const id of committedAttachmentIds.current) {
|
|
1189
|
-
if (!currentIds.has(id)) revokePreview(id);
|
|
1190
|
-
}
|
|
1191
|
-
committedAttachmentIds.current = currentIds;
|
|
1192
|
-
}, [attachments, revokePreview]);
|
|
1193
|
-
const scopeGeneration = useRef3(0);
|
|
1194
|
-
const previousScope = useRef3({ client, workspaceId });
|
|
1195
|
-
useEffect3(() => {
|
|
1196
|
-
const previous = previousScope.current;
|
|
1197
|
-
if (previous.client === client && previous.workspaceId === workspaceId) return;
|
|
1198
|
-
previousScope.current = { client, workspaceId };
|
|
1199
|
-
scopeGeneration.current += 1;
|
|
1200
|
-
sources.current.clear();
|
|
1201
|
-
revokeAllPreviews();
|
|
1202
|
-
setAttachments([]);
|
|
1203
|
-
}, [client, revokeAllPreviews, workspaceId]);
|
|
1204
|
-
useEffect3(
|
|
1205
|
-
() => () => {
|
|
1206
|
-
scopeGeneration.current += 1;
|
|
1207
|
-
sources.current.clear();
|
|
1208
|
-
revokeAllPreviews();
|
|
1209
|
-
},
|
|
1210
|
-
[revokeAllPreviews]
|
|
1211
|
-
);
|
|
1212
|
-
const startUpload = useCallback3(
|
|
1213
|
-
(id, file) => {
|
|
1214
|
-
const generation = scopeGeneration.current;
|
|
1215
|
-
void client.uploadFile(workspaceId, {
|
|
1216
|
-
filename: file.name || "file",
|
|
1217
|
-
contentType: file.type || "application/octet-stream",
|
|
1218
|
-
data: file
|
|
1219
|
-
}).then((asset) => {
|
|
1220
|
-
if (scopeGeneration.current !== generation) return;
|
|
1221
|
-
sources.current.delete(id);
|
|
1222
|
-
setAttachments(
|
|
1223
|
-
(current) => current.map(
|
|
1224
|
-
(attachment) => attachment.id === id ? {
|
|
1225
|
-
...attachment,
|
|
1226
|
-
status: "ready",
|
|
1227
|
-
file: asset,
|
|
1228
|
-
name: asset.filename,
|
|
1229
|
-
contentType: asset.contentType,
|
|
1230
|
-
sizeBytes: asset.sizeBytes,
|
|
1231
|
-
error: void 0
|
|
1232
|
-
} : attachment
|
|
1233
|
-
)
|
|
1234
|
-
);
|
|
1235
|
-
}).catch((error) => {
|
|
1236
|
-
if (scopeGeneration.current !== generation) return;
|
|
1237
|
-
setAttachments(
|
|
1238
|
-
(current) => current.map(
|
|
1239
|
-
(attachment) => attachment.id === id ? {
|
|
1240
|
-
...attachment,
|
|
1241
|
-
status: "failed",
|
|
1242
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1243
|
-
} : attachment
|
|
1244
|
-
)
|
|
1245
|
-
);
|
|
1246
|
-
});
|
|
1247
|
-
},
|
|
1248
|
-
[client, workspaceId]
|
|
1249
|
-
);
|
|
1250
|
-
const addFiles = useCallback3(
|
|
1251
|
-
(files) => {
|
|
1252
|
-
for (const file of files) {
|
|
1253
|
-
const id = crypto.randomUUID();
|
|
1254
|
-
sources.current.set(id, file);
|
|
1255
|
-
const previewUrl = isImage(file) ? URL.createObjectURL(file) : void 0;
|
|
1256
|
-
if (previewUrl) previewUrls.current.set(id, previewUrl);
|
|
1257
|
-
setAttachments((current) => [
|
|
1258
|
-
...current,
|
|
1259
|
-
{
|
|
1260
|
-
id,
|
|
1261
|
-
name: file.name || "image",
|
|
1262
|
-
contentType: file.type || "application/octet-stream",
|
|
1263
|
-
sizeBytes: file.size,
|
|
1264
|
-
status: "uploading",
|
|
1265
|
-
...previewUrl ? { previewUrl } : {}
|
|
1266
|
-
}
|
|
1267
|
-
]);
|
|
1268
|
-
startUpload(id, file);
|
|
1269
|
-
}
|
|
1270
|
-
},
|
|
1271
|
-
[startUpload]
|
|
1272
|
-
);
|
|
1273
|
-
const retry = useCallback3(
|
|
1274
|
-
(id) => {
|
|
1275
|
-
const file = sources.current.get(id);
|
|
1276
|
-
if (!file) {
|
|
1277
|
-
return;
|
|
1278
|
-
}
|
|
1279
|
-
setAttachments(
|
|
1280
|
-
(current) => current.map(
|
|
1281
|
-
(attachment) => attachment.id === id ? { ...attachment, status: "uploading", error: void 0 } : attachment
|
|
1282
|
-
)
|
|
1283
|
-
);
|
|
1284
|
-
startUpload(id, file);
|
|
1285
|
-
},
|
|
1286
|
-
[startUpload]
|
|
1287
|
-
);
|
|
1288
|
-
const addFromPaste = useCallback3(
|
|
1289
|
-
(event) => {
|
|
1290
|
-
const clipboardFiles = event.clipboardData?.files;
|
|
1291
|
-
if (!clipboardFiles) {
|
|
1292
|
-
return;
|
|
1293
|
-
}
|
|
1294
|
-
const files = [...clipboardFiles].filter(pasteFilter);
|
|
1295
|
-
if (files.length > 0) {
|
|
1296
|
-
addFiles(files);
|
|
1297
|
-
}
|
|
1298
|
-
},
|
|
1299
|
-
[addFiles, pasteFilter]
|
|
1300
|
-
);
|
|
1301
|
-
const restoreReadyFiles = useCallback3(
|
|
1302
|
-
(files) => {
|
|
1303
|
-
const incoming = /* @__PURE__ */ new Map();
|
|
1304
|
-
for (const file of files) {
|
|
1305
|
-
if (file.status === "ready" && file.workspaceId === workspaceId) {
|
|
1306
|
-
incoming.set(file.id, file);
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
setAttachments((current) => {
|
|
1310
|
-
const unresolved = current.filter((attachment) => attachment.status !== "ready");
|
|
1311
|
-
const existingReady = new Map(
|
|
1312
|
-
current.flatMap(
|
|
1313
|
-
(attachment) => attachment.status === "ready" && attachment.file ? [[attachment.file.id, attachment]] : []
|
|
1314
|
-
)
|
|
1315
|
-
);
|
|
1316
|
-
const restored = [...incoming.values()].map((file) => {
|
|
1317
|
-
const existing = existingReady.get(file.id);
|
|
1318
|
-
return existing ? {
|
|
1319
|
-
...existing,
|
|
1320
|
-
name: file.filename,
|
|
1321
|
-
contentType: file.contentType,
|
|
1322
|
-
sizeBytes: file.sizeBytes,
|
|
1323
|
-
status: "ready",
|
|
1324
|
-
file,
|
|
1325
|
-
error: void 0
|
|
1326
|
-
} : {
|
|
1327
|
-
id: `restored:${file.id}`,
|
|
1328
|
-
name: file.filename,
|
|
1329
|
-
contentType: file.contentType,
|
|
1330
|
-
sizeBytes: file.sizeBytes,
|
|
1331
|
-
status: "ready",
|
|
1332
|
-
file
|
|
1333
|
-
// No source File and no object URL: server metadata is the
|
|
1334
|
-
// only authority restored across page/device boundaries.
|
|
1335
|
-
};
|
|
1336
|
-
});
|
|
1337
|
-
return [...unresolved, ...restored];
|
|
1338
|
-
});
|
|
1339
|
-
},
|
|
1340
|
-
[workspaceId]
|
|
1341
|
-
);
|
|
1342
|
-
const remove = useCallback3(
|
|
1343
|
-
(id) => {
|
|
1344
|
-
sources.current.delete(id);
|
|
1345
|
-
revokePreview(id);
|
|
1346
|
-
setAttachments((current) => current.filter((attachment) => attachment.id !== id));
|
|
1347
|
-
},
|
|
1348
|
-
[revokePreview]
|
|
1349
|
-
);
|
|
1350
|
-
const removeReadyFiles = useCallback3((fileIds) => {
|
|
1351
|
-
const accepted = new Set(fileIds);
|
|
1352
|
-
if (accepted.size === 0) return;
|
|
1353
|
-
setAttachments(
|
|
1354
|
-
(current) => current.filter((attachment) => {
|
|
1355
|
-
return !(attachment.status === "ready" && attachment.file !== void 0 && accepted.has(attachment.file.id));
|
|
1356
|
-
})
|
|
1357
|
-
);
|
|
1358
|
-
}, []);
|
|
1359
|
-
const clear = useCallback3(() => {
|
|
1360
|
-
sources.current.clear();
|
|
1361
|
-
revokeAllPreviews();
|
|
1362
|
-
setAttachments([]);
|
|
1363
|
-
}, [revokeAllPreviews]);
|
|
1364
|
-
return {
|
|
1365
|
-
attachments,
|
|
1366
|
-
readyResources: attachments.flatMap(
|
|
1367
|
-
(attachment) => attachment.status === "ready" && attachment.file ? [{ kind: "file", fileId: attachment.file.id }] : []
|
|
1368
|
-
),
|
|
1369
|
-
uploading: attachments.some((attachment) => attachment.status === "uploading"),
|
|
1370
|
-
hasUnresolved: attachments.some((attachment) => attachment.status !== "ready"),
|
|
1371
|
-
addFiles,
|
|
1372
|
-
addFromPaste,
|
|
1373
|
-
restoreReadyFiles,
|
|
1374
|
-
retry,
|
|
1375
|
-
retainPreview,
|
|
1376
|
-
remove,
|
|
1377
|
-
removeReadyFiles,
|
|
1378
|
-
clear
|
|
1379
|
-
};
|
|
1973
|
+
}
|
|
1974
|
+
if (older[older.length - 1].sequence >= existing[0].sequence) {
|
|
1975
|
+
throw new Error("@opengeni/react: loadOlder returned overlapping session events");
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
function assertAppendOrder(existing, newer) {
|
|
1979
|
+
if (!shouldAssertDevelopment() || existing.length === 0 || newer.length === 0) {
|
|
1980
|
+
return;
|
|
1981
|
+
}
|
|
1982
|
+
if (newer[0].sequence <= existing[existing.length - 1].sequence) {
|
|
1983
|
+
throw new Error("@opengeni/react: loadNewer returned overlapping session events");
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
function shouldAssertDevelopment() {
|
|
1987
|
+
const processEnv = globalThis.process?.env;
|
|
1988
|
+
return processEnv !== void 0 && processEnv.NODE_ENV !== "production";
|
|
1989
|
+
}
|
|
1990
|
+
function abortError() {
|
|
1991
|
+
const error = new Error("The operation was aborted.");
|
|
1992
|
+
error.name = "AbortError";
|
|
1993
|
+
return error;
|
|
1380
1994
|
}
|
|
1381
1995
|
|
|
1382
1996
|
// src/hooks/use-turn-queue.ts
|
|
1383
|
-
import { useCallback as
|
|
1997
|
+
import { useCallback as useCallback7, useEffect as useEffect5, useLayoutEffect as useLayoutEffect2, useMemo as useMemo3, useRef as useRef6, useState as useState5 } from "react";
|
|
1384
1998
|
function isTurnQueueEvent(event) {
|
|
1385
1999
|
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.");
|
|
1386
2000
|
}
|
|
@@ -1395,18 +2009,18 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1395
2009
|
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
1396
2010
|
const targetKey = `${workspaceId}\0${sessionId ?? ""}`;
|
|
1397
2011
|
const pageLive = usePageLiveActivity();
|
|
1398
|
-
const [snapshot, setSnapshot] =
|
|
1399
|
-
const [stateTargetKey, setStateTargetKey] =
|
|
1400
|
-
const [loading, setLoading] =
|
|
1401
|
-
const [error, setError] =
|
|
1402
|
-
const [mutationError, setMutationError] =
|
|
1403
|
-
const [pendingByTurn, setPendingByTurn] =
|
|
1404
|
-
const [acceptedSteers, setAcceptedSteers] =
|
|
1405
|
-
const pendingRef =
|
|
1406
|
-
const readGeneration =
|
|
1407
|
-
const targetKeyRef =
|
|
1408
|
-
const snapshotRef =
|
|
1409
|
-
|
|
2012
|
+
const [snapshot, setSnapshot] = useState5(null);
|
|
2013
|
+
const [stateTargetKey, setStateTargetKey] = useState5(targetKey);
|
|
2014
|
+
const [loading, setLoading] = useState5(enabled);
|
|
2015
|
+
const [error, setError] = useState5(null);
|
|
2016
|
+
const [mutationError, setMutationError] = useState5(null);
|
|
2017
|
+
const [pendingByTurn, setPendingByTurn] = useState5({});
|
|
2018
|
+
const [acceptedSteers, setAcceptedSteers] = useState5([]);
|
|
2019
|
+
const pendingRef = useRef6({});
|
|
2020
|
+
const readGeneration = useRef6(0);
|
|
2021
|
+
const targetKeyRef = useRef6(targetKey);
|
|
2022
|
+
const snapshotRef = useRef6(null);
|
|
2023
|
+
useLayoutEffect2(() => {
|
|
1410
2024
|
if (targetKeyRef.current === targetKey) return;
|
|
1411
2025
|
targetKeyRef.current = targetKey;
|
|
1412
2026
|
readGeneration.current += 1;
|
|
@@ -1420,7 +2034,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1420
2034
|
setPendingByTurn({});
|
|
1421
2035
|
setAcceptedSteers([]);
|
|
1422
2036
|
}, [enabled, targetKey]);
|
|
1423
|
-
|
|
2037
|
+
useEffect5(() => {
|
|
1424
2038
|
if (acceptedSteers.length === 0 || options.events === void 0) return;
|
|
1425
2039
|
const durablyStartedTurnIds = new Set(
|
|
1426
2040
|
options.events.flatMap(
|
|
@@ -1432,7 +2046,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1432
2046
|
(current) => current.filter((steer) => !durablyStartedTurnIds.has(steer.turnId))
|
|
1433
2047
|
);
|
|
1434
2048
|
}, [acceptedSteers.length, options.events]);
|
|
1435
|
-
const acceptSnapshot =
|
|
2049
|
+
const acceptSnapshot = useCallback7(
|
|
1436
2050
|
(ownedTargetKey, next) => {
|
|
1437
2051
|
if (targetKeyRef.current !== ownedTargetKey) return false;
|
|
1438
2052
|
const current = snapshotRef.current;
|
|
@@ -1446,7 +2060,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1446
2060
|
},
|
|
1447
2061
|
[]
|
|
1448
2062
|
);
|
|
1449
|
-
const load =
|
|
2063
|
+
const load = useCallback7(
|
|
1450
2064
|
async (rejectOnFailure = false) => {
|
|
1451
2065
|
if (!sessionId) return;
|
|
1452
2066
|
const ownedTargetKey = `${workspaceId}\0${sessionId}`;
|
|
@@ -1477,7 +2091,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1477
2091
|
},
|
|
1478
2092
|
[client, workspaceId, sessionId, acceptSnapshot]
|
|
1479
2093
|
);
|
|
1480
|
-
|
|
2094
|
+
useEffect5(() => {
|
|
1481
2095
|
if (!enabled || !pageLive) {
|
|
1482
2096
|
setLoading(false);
|
|
1483
2097
|
return;
|
|
@@ -1506,10 +2120,10 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1506
2120
|
readGeneration.current += 1;
|
|
1507
2121
|
};
|
|
1508
2122
|
}, [load, enabled, pageLive, options.pollIntervalMs]);
|
|
1509
|
-
|
|
2123
|
+
useEffect5(() => {
|
|
1510
2124
|
if (enabled && workspaceControlEvent) void load();
|
|
1511
2125
|
}, [enabled, load, workspaceControlEvent]);
|
|
1512
|
-
|
|
2126
|
+
useEffect5(() => {
|
|
1513
2127
|
if (!sessionId || !enabled) return;
|
|
1514
2128
|
return registerSessionReconciler(sessionId, "queue", load);
|
|
1515
2129
|
}, [enabled, load, registerSessionReconciler, sessionId]);
|
|
@@ -1526,7 +2140,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1526
2140
|
},
|
|
1527
2141
|
async () => await load(true)
|
|
1528
2142
|
);
|
|
1529
|
-
const mutate =
|
|
2143
|
+
const mutate = useCallback7(
|
|
1530
2144
|
async (turnId, kind, command) => {
|
|
1531
2145
|
const ownedTargetKey = targetKey;
|
|
1532
2146
|
if (!sessionId || targetKeyRef.current !== ownedTargetKey || pendingRef.current[turnId]) {
|
|
@@ -1568,7 +2182,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1568
2182
|
},
|
|
1569
2183
|
[acceptSnapshot, load, sessionId, targetKey]
|
|
1570
2184
|
);
|
|
1571
|
-
const moveTurn =
|
|
2185
|
+
const moveTurn = useCallback7(
|
|
1572
2186
|
async (turnId, beforeTurnId) => {
|
|
1573
2187
|
const result = await mutate(
|
|
1574
2188
|
turnId,
|
|
@@ -1583,7 +2197,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1583
2197
|
},
|
|
1584
2198
|
[client, mutate, sessionId, workspaceId]
|
|
1585
2199
|
);
|
|
1586
|
-
const editTurn =
|
|
2200
|
+
const editTurn = useCallback7(
|
|
1587
2201
|
async (turnId, edit) => {
|
|
1588
2202
|
const result = await mutate(
|
|
1589
2203
|
turnId,
|
|
@@ -1599,7 +2213,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1599
2213
|
},
|
|
1600
2214
|
[client, mutate, sessionId, workspaceId]
|
|
1601
2215
|
);
|
|
1602
|
-
const steerTurn =
|
|
2216
|
+
const steerTurn = useCallback7(
|
|
1603
2217
|
async (turnId) => {
|
|
1604
2218
|
const ownedTargetKey = targetKey;
|
|
1605
2219
|
const turn = snapshotRef.current?.items.find((candidate) => candidate.id === turnId);
|
|
@@ -1651,7 +2265,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1651
2265
|
},
|
|
1652
2266
|
[client, load, mutate, sessionId, targetKey, workspaceId]
|
|
1653
2267
|
);
|
|
1654
|
-
const removeTurn =
|
|
2268
|
+
const removeTurn = useCallback7(
|
|
1655
2269
|
async (turnId) => {
|
|
1656
2270
|
const result = await mutate(
|
|
1657
2271
|
turnId,
|
|
@@ -1673,7 +2287,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1673
2287
|
() => Object.keys(visiblePendingByTurn).length > 0,
|
|
1674
2288
|
[visiblePendingByTurn]
|
|
1675
2289
|
);
|
|
1676
|
-
const mutationFor =
|
|
2290
|
+
const mutationFor = useCallback7(
|
|
1677
2291
|
(turnId) => visiblePendingByTurn[turnId] ?? null,
|
|
1678
2292
|
[visiblePendingByTurn]
|
|
1679
2293
|
);
|
|
@@ -1696,7 +2310,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1696
2310
|
mutationFor,
|
|
1697
2311
|
mutating,
|
|
1698
2312
|
mutationError: identityMatches ? mutationError : null,
|
|
1699
|
-
clearMutationError:
|
|
2313
|
+
clearMutationError: useCallback7(() => {
|
|
1700
2314
|
if (targetKeyRef.current === targetKey) setMutationError(null);
|
|
1701
2315
|
}, [targetKey]),
|
|
1702
2316
|
acceptedSteers: identityMatches ? acceptedSteers : []
|
|
@@ -1706,15 +2320,175 @@ function operationKey() {
|
|
|
1706
2320
|
return globalThis.crypto.randomUUID();
|
|
1707
2321
|
}
|
|
1708
2322
|
function asError(cause) {
|
|
1709
|
-
return
|
|
2323
|
+
return normalizeError(cause);
|
|
1710
2324
|
}
|
|
1711
2325
|
function isOutcomeUnknownError(cause) {
|
|
1712
2326
|
return typeof cause === "object" && cause !== null && cause.outcomeUnknown === true;
|
|
1713
2327
|
}
|
|
1714
2328
|
|
|
2329
|
+
// src/hooks/use-session-conversation.ts
|
|
2330
|
+
import { useLayoutEffect as useLayoutEffect3, useMemo as useMemo4, useRef as useRef7 } from "react";
|
|
2331
|
+
|
|
2332
|
+
// src/hooks/use-client-config-flags.ts
|
|
2333
|
+
import { useEffect as useEffect6, useState as useState6 } from "react";
|
|
2334
|
+
var INITIAL_FLAGS = {
|
|
2335
|
+
loaded: false,
|
|
2336
|
+
uploads: false,
|
|
2337
|
+
modelSelection: false,
|
|
2338
|
+
sandboxFiles: false,
|
|
2339
|
+
defaultReasoningEffort: null,
|
|
2340
|
+
artifacts: true,
|
|
2341
|
+
sessionCreation: true,
|
|
2342
|
+
archive: true,
|
|
2343
|
+
voiceInput: null,
|
|
2344
|
+
realtimeVoice: false,
|
|
2345
|
+
realtimeVoiceOffered: false
|
|
2346
|
+
};
|
|
2347
|
+
var configReads = /* @__PURE__ */ new WeakMap();
|
|
2348
|
+
function readClientConfig(client) {
|
|
2349
|
+
let read = configReads.get(client);
|
|
2350
|
+
if (!read) {
|
|
2351
|
+
read = client.getClientConfig();
|
|
2352
|
+
configReads.set(client, read);
|
|
2353
|
+
read.catch(() => {
|
|
2354
|
+
if (configReads.get(client) === read) configReads.delete(client);
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2357
|
+
return read;
|
|
2358
|
+
}
|
|
2359
|
+
function useClientConfigFlags(client) {
|
|
2360
|
+
const [flags, setFlags] = useState6(INITIAL_FLAGS);
|
|
2361
|
+
useEffect6(() => {
|
|
2362
|
+
if (typeof client.getClientConfig !== "function") return;
|
|
2363
|
+
let live = true;
|
|
2364
|
+
readClientConfig(client).then(
|
|
2365
|
+
(config) => {
|
|
2366
|
+
if (live) {
|
|
2367
|
+
setFlags({
|
|
2368
|
+
loaded: true,
|
|
2369
|
+
uploads: config.fileUploads?.enabled === true,
|
|
2370
|
+
// Only an explicit offer shows end users the picker.
|
|
2371
|
+
modelSelection: config.modelSelection === true,
|
|
2372
|
+
sandboxFiles: config.sandboxFiles !== false,
|
|
2373
|
+
defaultReasoningEffort: config.defaultReasoningEffort ?? null,
|
|
2374
|
+
artifacts: config.artifacts !== false,
|
|
2375
|
+
sessionCreation: config.sessionCreation !== false,
|
|
2376
|
+
archive: config.archive !== false,
|
|
2377
|
+
voiceInput: config.voiceInput?.available === true ? config.voiceInput : null,
|
|
2378
|
+
realtimeVoice: config.realtimeVoice !== false,
|
|
2379
|
+
realtimeVoiceOffered: config.realtimeVoice === true
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
},
|
|
2383
|
+
() => void 0
|
|
2384
|
+
);
|
|
2385
|
+
return () => {
|
|
2386
|
+
live = false;
|
|
2387
|
+
};
|
|
2388
|
+
}, [client]);
|
|
2389
|
+
return flags;
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
// src/hooks/use-session-conversation.ts
|
|
2393
|
+
function useSessionConversation(sessionId, options = {}) {
|
|
2394
|
+
const context = useOpenGeni(options);
|
|
2395
|
+
const scope = { client: context.client, workspaceId: context.workspaceId };
|
|
2396
|
+
const config = useClientConfigFlags(context.client);
|
|
2397
|
+
const showModelPicker = options.modelPicker ?? config.modelSelection;
|
|
2398
|
+
const catalog = useWorkspaceModelCatalog({ ...scope, enabled: showModelPicker });
|
|
2399
|
+
const feed = useSessionEvents(sessionId, scope);
|
|
2400
|
+
const eventOptions = { ...scope, events: feed.events };
|
|
2401
|
+
const detail = useSession(sessionId, eventOptions);
|
|
2402
|
+
const queue = useTurnQueue(sessionId, eventOptions);
|
|
2403
|
+
const human = useHumanInputRequests(sessionId, eventOptions);
|
|
2404
|
+
const control = useSessionControl(sessionId, scope);
|
|
2405
|
+
const approvals = useMemo4(() => projectPendingApprovals(feed.events), [feed.events]);
|
|
2406
|
+
const files = useFileAttachments(scope);
|
|
2407
|
+
const clearFiles = files.clear;
|
|
2408
|
+
const attachmentSession = useRef7(sessionId);
|
|
2409
|
+
useLayoutEffect3(() => {
|
|
2410
|
+
if (attachmentSession.current === sessionId) return;
|
|
2411
|
+
attachmentSession.current = sessionId;
|
|
2412
|
+
clearFiles();
|
|
2413
|
+
}, [clearFiles, sessionId]);
|
|
2414
|
+
const uploadsEnabled = (options.attachments ?? true) && config.uploads;
|
|
2415
|
+
const status = feed.sessionStatus ?? detail.session?.status;
|
|
2416
|
+
const terminal = status === "cancelled";
|
|
2417
|
+
const importedArchive = detail.session?.importedArchive?.readOnly === true;
|
|
2418
|
+
const host = options.composerOptions;
|
|
2419
|
+
const resolveExtras = () => typeof host?.sendExtras === "function" ? host.sendExtras() : host?.sendExtras;
|
|
2420
|
+
const releaseSentFiles = (input) => files.removeReadyFiles(
|
|
2421
|
+
(input.resources ?? []).flatMap(
|
|
2422
|
+
(resource) => resource.kind === "file" ? [resource.fileId] : []
|
|
2423
|
+
)
|
|
2424
|
+
);
|
|
2425
|
+
const composer = useComposer(sessionId, {
|
|
2426
|
+
...eventOptions,
|
|
2427
|
+
initialDraft: host?.initialDraft,
|
|
2428
|
+
effectiveControl: queue.effectiveControl ?? detail.session?.effectiveControl,
|
|
2429
|
+
sendDestination: () => queue.queue.length > 0 || status === "running" ? "queue" : "chat",
|
|
2430
|
+
sendExtras: () => {
|
|
2431
|
+
const extras = resolveExtras();
|
|
2432
|
+
return {
|
|
2433
|
+
...extras,
|
|
2434
|
+
resources: [...extras?.resources ?? [], ...uploadsEnabled ? files.readyResources : []]
|
|
2435
|
+
};
|
|
2436
|
+
},
|
|
2437
|
+
sendBlocked: () => terminal || importedArchive || uploadsEnabled && files.hasUnresolved || host?.sendBlocked?.() === true,
|
|
2438
|
+
onSubmitted: (text, input) => {
|
|
2439
|
+
releaseSentFiles(input);
|
|
2440
|
+
return host?.onSubmitted?.(text, input);
|
|
2441
|
+
},
|
|
2442
|
+
onSent: (text, input) => {
|
|
2443
|
+
releaseSentFiles(input);
|
|
2444
|
+
return host?.onSent?.(text, input);
|
|
2445
|
+
},
|
|
2446
|
+
onDeliveryError: host?.onDeliveryError
|
|
2447
|
+
});
|
|
2448
|
+
const timeline = useMemo4(
|
|
2449
|
+
() => conversationTimeline(feed.timeline, queue, composer),
|
|
2450
|
+
[feed.timeline, queue, composer]
|
|
2451
|
+
);
|
|
2452
|
+
const error = detail.error ?? feed.error ?? human.error;
|
|
2453
|
+
const loadFailed = Boolean(feed.error) && !feed.initialHistoryReady;
|
|
2454
|
+
const retry = async () => {
|
|
2455
|
+
await Promise.all([
|
|
2456
|
+
...detail.error ? [detail.refresh()] : [],
|
|
2457
|
+
...human.error ? [human.refresh()] : [],
|
|
2458
|
+
...feed.error ? [feed.jumpToLatest()] : []
|
|
2459
|
+
]);
|
|
2460
|
+
};
|
|
2461
|
+
return {
|
|
2462
|
+
...scope,
|
|
2463
|
+
sessionId,
|
|
2464
|
+
config,
|
|
2465
|
+
catalog,
|
|
2466
|
+
showModelPicker,
|
|
2467
|
+
feed,
|
|
2468
|
+
detail,
|
|
2469
|
+
queue,
|
|
2470
|
+
human,
|
|
2471
|
+
control,
|
|
2472
|
+
approvals,
|
|
2473
|
+
files,
|
|
2474
|
+
uploadsEnabled,
|
|
2475
|
+
composer,
|
|
2476
|
+
/** The same live application context for text and durable voice messages. */
|
|
2477
|
+
getModelContext: () => resolveExtras()?.modelContext,
|
|
2478
|
+
timeline,
|
|
2479
|
+
status,
|
|
2480
|
+
terminal,
|
|
2481
|
+
importedArchive,
|
|
2482
|
+
running: status === "running" || status === "recovering" || status === "waiting_capacity",
|
|
2483
|
+
error,
|
|
2484
|
+
loadFailed,
|
|
2485
|
+
retry
|
|
2486
|
+
};
|
|
2487
|
+
}
|
|
2488
|
+
|
|
1715
2489
|
// src/hooks/use-goal.ts
|
|
1716
2490
|
import { OpenGeniApiError } from "@opengeni/sdk";
|
|
1717
|
-
import { useCallback as
|
|
2491
|
+
import { useCallback as useCallback8, useEffect as useEffect7, useLayoutEffect as useLayoutEffect4, useRef as useRef8, useState as useState7 } from "react";
|
|
1718
2492
|
function isGoalEvent(event) {
|
|
1719
2493
|
return event.type.startsWith("goal.");
|
|
1720
2494
|
}
|
|
@@ -1728,20 +2502,38 @@ function useGoal(sessionId, options = {}) {
|
|
|
1728
2502
|
const sharedEvents = options.events;
|
|
1729
2503
|
const sharedFeed = sharedEvents !== void 0;
|
|
1730
2504
|
const pageLive = usePageLiveActivity();
|
|
1731
|
-
const [goal, setGoal] =
|
|
1732
|
-
const [loading, setLoading] =
|
|
1733
|
-
const [error, setError] =
|
|
2505
|
+
const [goal, setGoal] = useState7(null);
|
|
2506
|
+
const [loading, setLoading] = useState7(enabled);
|
|
2507
|
+
const [error, setError] = useState7(null);
|
|
1734
2508
|
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
1735
|
-
const generation =
|
|
1736
|
-
const targetKeyRef =
|
|
1737
|
-
const
|
|
2509
|
+
const generation = useRef8(0);
|
|
2510
|
+
const targetKeyRef = useRef8(null);
|
|
2511
|
+
const goalAbsentRef = useRef8(false);
|
|
2512
|
+
const pendingRefreshRef = useRef8("none");
|
|
2513
|
+
const sharedEventsRef = useRef8(sharedEvents);
|
|
2514
|
+
useLayoutEffect4(() => {
|
|
2515
|
+
sharedEventsRef.current = sharedEvents;
|
|
2516
|
+
}, [sharedEvents]);
|
|
2517
|
+
const loadAbort = useRef8(null);
|
|
2518
|
+
const scheduleRefreshRef = useRef8(() => void 0);
|
|
2519
|
+
const retireLoads = useCallback8(() => {
|
|
2520
|
+
generation.current += 1;
|
|
2521
|
+
loadAbort.current?.abort();
|
|
2522
|
+
loadAbort.current = null;
|
|
2523
|
+
}, []);
|
|
2524
|
+
const load = useCallback8(async () => {
|
|
1738
2525
|
if (!sessionId) {
|
|
1739
2526
|
return;
|
|
1740
2527
|
}
|
|
1741
2528
|
const ticket = ++generation.current;
|
|
2529
|
+
pendingRefreshRef.current = "none";
|
|
2530
|
+
loadAbort.current?.abort();
|
|
2531
|
+
const controller = new AbortController();
|
|
2532
|
+
loadAbort.current = controller;
|
|
1742
2533
|
try {
|
|
1743
|
-
const fetched = await client.getGoal(workspaceId, sessionId);
|
|
2534
|
+
const fetched = typeof client.findGoal === "function" ? await client.findGoal(workspaceId, sessionId, { signal: controller.signal }) : await client.getGoal(workspaceId, sessionId, { signal: controller.signal });
|
|
1744
2535
|
if (ticket === generation.current) {
|
|
2536
|
+
goalAbsentRef.current = fetched === null;
|
|
1745
2537
|
setGoal(fetched);
|
|
1746
2538
|
setError(null);
|
|
1747
2539
|
setLoading(false);
|
|
@@ -1751,29 +2543,53 @@ function useGoal(sessionId, options = {}) {
|
|
|
1751
2543
|
return;
|
|
1752
2544
|
}
|
|
1753
2545
|
if (cause instanceof OpenGeniApiError && cause.status === 404) {
|
|
2546
|
+
goalAbsentRef.current = true;
|
|
1754
2547
|
setGoal(null);
|
|
1755
2548
|
setError(null);
|
|
1756
2549
|
} else {
|
|
1757
|
-
setError(
|
|
2550
|
+
setError(normalizeError(cause));
|
|
1758
2551
|
}
|
|
1759
2552
|
setLoading(false);
|
|
2553
|
+
} finally {
|
|
2554
|
+
if (loadAbort.current === controller) loadAbort.current = null;
|
|
1760
2555
|
}
|
|
2556
|
+
if (ticket !== generation.current || pendingRefreshRef.current === "none") {
|
|
2557
|
+
return;
|
|
2558
|
+
}
|
|
2559
|
+
if (goalAbsentRef.current && pendingRefreshRef.current === "refresh") {
|
|
2560
|
+
const events = sharedEventsRef.current ?? [];
|
|
2561
|
+
let latestGoalEvent;
|
|
2562
|
+
for (let index = events.length - 1; index >= 0 && !latestGoalEvent; index -= 1) {
|
|
2563
|
+
const event = events[index];
|
|
2564
|
+
if (event && isGoalEvent(event)) latestGoalEvent = event;
|
|
2565
|
+
}
|
|
2566
|
+
if (!latestGoalEvent || latestGoalEvent.type === "goal.cleared") {
|
|
2567
|
+
pendingRefreshRef.current = "none";
|
|
2568
|
+
return;
|
|
2569
|
+
}
|
|
2570
|
+
pendingRefreshRef.current = "goal";
|
|
2571
|
+
}
|
|
2572
|
+
scheduleRefreshRef.current();
|
|
1761
2573
|
}, [client, workspaceId, sessionId]);
|
|
1762
|
-
|
|
2574
|
+
useEffect7(() => {
|
|
1763
2575
|
const targetKey = `${workspaceId} ${sessionId ?? ""}`;
|
|
1764
2576
|
if (targetKeyRef.current !== targetKey) {
|
|
1765
2577
|
targetKeyRef.current = targetKey;
|
|
2578
|
+
goalAbsentRef.current = false;
|
|
2579
|
+
pendingRefreshRef.current = "none";
|
|
1766
2580
|
setGoal(null);
|
|
1767
2581
|
setError(null);
|
|
1768
2582
|
}
|
|
1769
2583
|
if (!enabled || !pageLive) {
|
|
2584
|
+
retireLoads();
|
|
1770
2585
|
setLoading(false);
|
|
1771
2586
|
return;
|
|
1772
2587
|
}
|
|
1773
2588
|
if (sharedFeed) {
|
|
1774
|
-
setLoading(
|
|
2589
|
+
setLoading(true);
|
|
2590
|
+
void load();
|
|
1775
2591
|
return () => {
|
|
1776
|
-
|
|
2592
|
+
retireLoads();
|
|
1777
2593
|
};
|
|
1778
2594
|
}
|
|
1779
2595
|
setLoading(true);
|
|
@@ -1781,7 +2597,7 @@ function useGoal(sessionId, options = {}) {
|
|
|
1781
2597
|
if (pollIntervalMs === void 0 || pollIntervalMs <= 0) {
|
|
1782
2598
|
void load();
|
|
1783
2599
|
return () => {
|
|
1784
|
-
|
|
2600
|
+
retireLoads();
|
|
1785
2601
|
};
|
|
1786
2602
|
}
|
|
1787
2603
|
let cancelled = false;
|
|
@@ -1797,541 +2613,394 @@ function useGoal(sessionId, options = {}) {
|
|
|
1797
2613
|
return () => {
|
|
1798
2614
|
cancelled = true;
|
|
1799
2615
|
if (timer !== null) clearTimeout(timer);
|
|
1800
|
-
|
|
2616
|
+
retireLoads();
|
|
1801
2617
|
};
|
|
1802
|
-
}, [
|
|
1803
|
-
|
|
1804
|
-
useSessionEventTrigger(client, workspaceId, sessionId, isGoalRefreshEvent, scheduleRefresh, {
|
|
2618
|
+
}, [
|
|
2619
|
+
load,
|
|
1805
2620
|
enabled,
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
);
|
|
1819
|
-
if (result) {
|
|
1820
|
-
setGoal(result);
|
|
1821
|
-
}
|
|
1822
|
-
return result;
|
|
1823
|
-
},
|
|
1824
|
-
[client, workspaceId, sessionId, run]
|
|
1825
|
-
);
|
|
1826
|
-
const resume = useCallback5(async () => {
|
|
1827
|
-
if (!sessionId) {
|
|
1828
|
-
return null;
|
|
1829
|
-
}
|
|
1830
|
-
const result = await run(() => client.updateGoal(workspaceId, sessionId, { status: "active" }));
|
|
1831
|
-
if (result) {
|
|
1832
|
-
setGoal(result);
|
|
1833
|
-
}
|
|
1834
|
-
return result;
|
|
1835
|
-
}, [client, workspaceId, sessionId, run]);
|
|
1836
|
-
const clearGoal = useCallback5(async () => {
|
|
1837
|
-
if (!sessionId) {
|
|
2621
|
+
pageLive,
|
|
2622
|
+
workspaceId,
|
|
2623
|
+
sessionId,
|
|
2624
|
+
options.pollIntervalMs,
|
|
2625
|
+
retireLoads,
|
|
2626
|
+
sharedFeed
|
|
2627
|
+
]);
|
|
2628
|
+
const scheduleRefresh = useDebouncedCallback(() => {
|
|
2629
|
+
const pending = pendingRefreshRef.current;
|
|
2630
|
+
if (pending === "none" || loadAbort.current !== null) return;
|
|
2631
|
+
if (pending === "refresh" && goalAbsentRef.current) {
|
|
2632
|
+
pendingRefreshRef.current = "none";
|
|
1838
2633
|
return;
|
|
1839
2634
|
}
|
|
1840
|
-
|
|
1841
|
-
await client.deleteGoal(workspaceId, sessionId);
|
|
1842
|
-
return true;
|
|
1843
|
-
});
|
|
1844
|
-
if (ok) {
|
|
1845
|
-
generation.current += 1;
|
|
1846
|
-
setGoal(null);
|
|
1847
|
-
setError(null);
|
|
1848
|
-
}
|
|
1849
|
-
}, [client, workspaceId, sessionId, run]);
|
|
1850
|
-
return {
|
|
1851
|
-
goal,
|
|
1852
|
-
isActive: goal?.status === "active",
|
|
1853
|
-
isPaused: goal?.status === "paused",
|
|
1854
|
-
isCompleted: goal?.status === "completed",
|
|
1855
|
-
loading,
|
|
1856
|
-
error,
|
|
1857
|
-
refresh: load,
|
|
1858
|
-
pause,
|
|
1859
|
-
resume,
|
|
1860
|
-
clearGoal,
|
|
1861
|
-
deleteGoal: clearGoal,
|
|
1862
|
-
updating: mutating,
|
|
1863
|
-
mutationError,
|
|
1864
|
-
clearMutationError
|
|
1865
|
-
};
|
|
1866
|
-
}
|
|
1867
|
-
|
|
1868
|
-
// src/hooks/use-session-control.ts
|
|
1869
|
-
import { useCallback as useCallback6, useLayoutEffect as useLayoutEffect2, useRef as useRef6 } from "react";
|
|
1870
|
-
function useSessionControl(sessionId, options = {}) {
|
|
1871
|
-
const { client, workspaceId } = useEmbeddedSession(options);
|
|
1872
|
-
const approvalTargetKey = `${workspaceId}\0${sessionId ?? ""}`;
|
|
1873
|
-
const pendingApproval = useRef6(null);
|
|
1874
|
-
useLayoutEffect2(() => {
|
|
1875
|
-
if (pendingApproval.current?.targetKey !== approvalTargetKey) {
|
|
1876
|
-
pendingApproval.current = null;
|
|
1877
|
-
}
|
|
1878
|
-
}, [approvalTargetKey]);
|
|
1879
|
-
const {
|
|
1880
|
-
run: runControl,
|
|
1881
|
-
mutating: controlling,
|
|
1882
|
-
mutationError: controlError,
|
|
1883
|
-
clearMutationError: clearControlError
|
|
1884
|
-
} = useMutationRunner(approvalTargetKey);
|
|
1885
|
-
const {
|
|
1886
|
-
run: runApproval,
|
|
1887
|
-
mutating: responding,
|
|
1888
|
-
mutationError: approvalError,
|
|
1889
|
-
clearMutationError: clearApprovalError
|
|
1890
|
-
} = useMutationRunner(approvalTargetKey);
|
|
1891
|
-
const pause = useCallback6(
|
|
1892
|
-
async (reason) => {
|
|
1893
|
-
if (!sessionId) {
|
|
1894
|
-
return null;
|
|
1895
|
-
}
|
|
1896
|
-
return await runControl(
|
|
1897
|
-
() => client.pauseSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
|
|
1898
|
-
);
|
|
1899
|
-
},
|
|
1900
|
-
[client, workspaceId, sessionId, runControl]
|
|
1901
|
-
);
|
|
1902
|
-
const resume = useCallback6(
|
|
1903
|
-
async (reason) => {
|
|
1904
|
-
if (!sessionId) return null;
|
|
1905
|
-
return await runControl(
|
|
1906
|
-
() => client.resumeSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
|
|
1907
|
-
);
|
|
1908
|
-
},
|
|
1909
|
-
[client, workspaceId, sessionId, runControl]
|
|
1910
|
-
);
|
|
1911
|
-
const decide = useCallback6(
|
|
1912
|
-
async (approvalId, decision, message) => {
|
|
1913
|
-
if (!sessionId) {
|
|
1914
|
-
return null;
|
|
1915
|
-
}
|
|
1916
|
-
const decisionKey = JSON.stringify([approvalId, decision, message ?? null]);
|
|
1917
|
-
if (pendingApproval.current?.decisionKey !== decisionKey) {
|
|
1918
|
-
pendingApproval.current = {
|
|
1919
|
-
targetKey: approvalTargetKey,
|
|
1920
|
-
decisionKey,
|
|
1921
|
-
clientEventId: crypto.randomUUID()
|
|
1922
|
-
};
|
|
1923
|
-
}
|
|
1924
|
-
const clientEventId = pendingApproval.current.clientEventId;
|
|
1925
|
-
const accepted = await runApproval(
|
|
1926
|
-
() => client.sendApprovalDecision(workspaceId, sessionId, {
|
|
1927
|
-
approvalId,
|
|
1928
|
-
decision,
|
|
1929
|
-
...message !== void 0 ? { message } : {},
|
|
1930
|
-
clientEventId
|
|
1931
|
-
})
|
|
1932
|
-
);
|
|
1933
|
-
if (accepted !== null && pendingApproval.current?.targetKey === approvalTargetKey && pendingApproval.current.clientEventId === clientEventId) {
|
|
1934
|
-
pendingApproval.current = null;
|
|
1935
|
-
}
|
|
1936
|
-
return accepted;
|
|
1937
|
-
},
|
|
1938
|
-
[approvalTargetKey, client, workspaceId, sessionId, runApproval]
|
|
1939
|
-
);
|
|
1940
|
-
const approve = useCallback6(
|
|
1941
|
-
async (approvalId, message) => await decide(approvalId, "approve", message),
|
|
1942
|
-
[decide]
|
|
1943
|
-
);
|
|
1944
|
-
const reject = useCallback6(
|
|
1945
|
-
async (approvalId, message) => await decide(approvalId, "reject", message),
|
|
1946
|
-
[decide]
|
|
1947
|
-
);
|
|
1948
|
-
const error = approvalError ?? controlError;
|
|
1949
|
-
const clearError = useCallback6(() => {
|
|
1950
|
-
clearControlError();
|
|
1951
|
-
clearApprovalError();
|
|
1952
|
-
}, [clearControlError, clearApprovalError]);
|
|
1953
|
-
return {
|
|
1954
|
-
pause,
|
|
1955
|
-
resume,
|
|
1956
|
-
controlling,
|
|
1957
|
-
approve,
|
|
1958
|
-
reject,
|
|
1959
|
-
responding,
|
|
1960
|
-
error,
|
|
1961
|
-
clearError
|
|
1962
|
-
};
|
|
1963
|
-
}
|
|
1964
|
-
|
|
1965
|
-
// src/hooks/use-session-mcp-approval-policy.ts
|
|
1966
|
-
import { useCallback as useCallback7, useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useMemo as useMemo4, useRef as useRef7, useState as useState6 } from "react";
|
|
1967
|
-
function isSessionMcpApprovalPolicyEvent(event, serverId) {
|
|
1968
|
-
if (event.type !== "session.mcp.approval_policy.updated") {
|
|
1969
|
-
return false;
|
|
1970
|
-
}
|
|
1971
|
-
if (serverId === void 0) {
|
|
1972
|
-
return true;
|
|
1973
|
-
}
|
|
1974
|
-
const payload = event.payload;
|
|
1975
|
-
return payload?.serverId === serverId;
|
|
1976
|
-
}
|
|
1977
|
-
function useSessionMcpApprovalPolicy(sessionId, serverId, options = {}) {
|
|
1978
|
-
const { client, workspaceId } = useEmbeddedSessionMcpApprovalPolicy(options);
|
|
1979
|
-
const enabled = (options.enabled ?? true) && Boolean(sessionId && serverId);
|
|
1980
|
-
const [override, setOverride] = useState6(null);
|
|
1981
|
-
const targetKey = `${workspaceId}\0${sessionId ?? ""}\0${serverId ?? ""}`;
|
|
1982
|
-
const authoritativeGeneration = useRef7(0);
|
|
1983
|
-
const mutationIdentity = useMemo4(() => ({ client, targetKey }), [client, targetKey]);
|
|
1984
|
-
const { run, mutating, mutationError, clearMutationError } = useMutationRunner(mutationIdentity);
|
|
1985
|
-
useLayoutEffect3(() => {
|
|
1986
|
-
authoritativeGeneration.current += 1;
|
|
1987
|
-
setOverride(null);
|
|
1988
|
-
}, [client, targetKey]);
|
|
1989
|
-
const load = useCallback7(async () => {
|
|
1990
|
-
const generation = authoritativeGeneration.current;
|
|
1991
|
-
if (!sessionId || !serverId) {
|
|
1992
|
-
return { targetKey, generation, server: null };
|
|
1993
|
-
}
|
|
1994
|
-
const session = await client.getSession(workspaceId, sessionId);
|
|
1995
|
-
return {
|
|
1996
|
-
targetKey,
|
|
1997
|
-
generation,
|
|
1998
|
-
server: session.mcpServers.find((server2) => server2.id === serverId) ?? null
|
|
1999
|
-
};
|
|
2000
|
-
}, [client, serverId, sessionId, targetKey, workspaceId]);
|
|
2001
|
-
const {
|
|
2002
|
-
data,
|
|
2003
|
-
loading,
|
|
2004
|
-
error: loadError,
|
|
2005
|
-
refresh
|
|
2006
|
-
} = usePolledValue(load, {
|
|
2007
|
-
enabled,
|
|
2008
|
-
pollIntervalMs: options.pollIntervalMs
|
|
2635
|
+
void load();
|
|
2009
2636
|
});
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
workspaceId,
|
|
2023
|
-
sessionId,
|
|
2024
|
-
matchesPolicyEvent,
|
|
2025
|
-
scheduleRefresh,
|
|
2026
|
-
{
|
|
2027
|
-
enabled,
|
|
2028
|
-
...options.events !== void 0 ? { events: options.events } : {}
|
|
2637
|
+
useLayoutEffect4(() => {
|
|
2638
|
+
scheduleRefreshRef.current = scheduleRefresh;
|
|
2639
|
+
}, [scheduleRefresh]);
|
|
2640
|
+
const isRefreshEvent = useCallback8(
|
|
2641
|
+
(event) => goalAbsentRef.current ? isGoalEvent(event) : isGoalRefreshEvent(event),
|
|
2642
|
+
[]
|
|
2643
|
+
);
|
|
2644
|
+
const onRefreshEvent = useCallback8(
|
|
2645
|
+
(event) => {
|
|
2646
|
+
if (isGoalEvent(event)) pendingRefreshRef.current = "goal";
|
|
2647
|
+
else if (pendingRefreshRef.current === "none") pendingRefreshRef.current = "refresh";
|
|
2648
|
+
scheduleRefresh();
|
|
2029
2649
|
},
|
|
2030
|
-
|
|
2650
|
+
[scheduleRefresh]
|
|
2031
2651
|
);
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2652
|
+
useSessionEventTrigger(client, workspaceId, sessionId, isRefreshEvent, onRefreshEvent, {
|
|
2653
|
+
enabled,
|
|
2654
|
+
...sharedEvents !== void 0 ? { events: sharedEvents } : {}
|
|
2655
|
+
});
|
|
2656
|
+
const pause = useCallback8(
|
|
2657
|
+
async (rationale) => {
|
|
2658
|
+
if (!sessionId) {
|
|
2035
2659
|
return null;
|
|
2036
2660
|
}
|
|
2037
|
-
const
|
|
2038
|
-
() => client.
|
|
2039
|
-
|
|
2661
|
+
const result = await run(
|
|
2662
|
+
() => client.updateGoal(workspaceId, sessionId, {
|
|
2663
|
+
status: "paused",
|
|
2664
|
+
...rationale !== void 0 ? { rationale } : {}
|
|
2040
2665
|
})
|
|
2041
2666
|
);
|
|
2042
|
-
if (
|
|
2043
|
-
|
|
2044
|
-
setOverride(response.server);
|
|
2045
|
-
void refresh();
|
|
2667
|
+
if (result) {
|
|
2668
|
+
setGoal(result);
|
|
2046
2669
|
}
|
|
2047
|
-
return
|
|
2670
|
+
return result;
|
|
2048
2671
|
},
|
|
2049
|
-
[client,
|
|
2672
|
+
[client, workspaceId, sessionId, run]
|
|
2050
2673
|
);
|
|
2051
|
-
const
|
|
2674
|
+
const resume = useCallback8(async () => {
|
|
2675
|
+
if (!sessionId) {
|
|
2676
|
+
return null;
|
|
2677
|
+
}
|
|
2678
|
+
const result = await run(() => client.updateGoal(workspaceId, sessionId, { status: "active" }));
|
|
2679
|
+
if (result) {
|
|
2680
|
+
setGoal(result);
|
|
2681
|
+
}
|
|
2682
|
+
return result;
|
|
2683
|
+
}, [client, workspaceId, sessionId, run]);
|
|
2684
|
+
const clearGoal = useCallback8(async () => {
|
|
2685
|
+
if (!sessionId) {
|
|
2686
|
+
return;
|
|
2687
|
+
}
|
|
2688
|
+
const ok = await run(async () => {
|
|
2689
|
+
await client.deleteGoal(workspaceId, sessionId);
|
|
2690
|
+
return true;
|
|
2691
|
+
});
|
|
2692
|
+
if (ok) {
|
|
2693
|
+
retireLoads();
|
|
2694
|
+
setGoal(null);
|
|
2695
|
+
setError(null);
|
|
2696
|
+
}
|
|
2697
|
+
}, [client, workspaceId, sessionId, run, retireLoads]);
|
|
2052
2698
|
return {
|
|
2053
|
-
|
|
2054
|
-
|
|
2699
|
+
goal,
|
|
2700
|
+
isActive: goal?.status === "active",
|
|
2701
|
+
isPaused: goal?.status === "paused",
|
|
2702
|
+
isCompleted: goal?.status === "completed",
|
|
2055
2703
|
loading,
|
|
2056
|
-
error
|
|
2057
|
-
refresh:
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2704
|
+
error,
|
|
2705
|
+
refresh: load,
|
|
2706
|
+
pause,
|
|
2707
|
+
resume,
|
|
2708
|
+
clearGoal,
|
|
2709
|
+
deleteGoal: clearGoal,
|
|
2061
2710
|
updating: mutating,
|
|
2062
|
-
|
|
2711
|
+
mutationError,
|
|
2712
|
+
clearMutationError
|
|
2063
2713
|
};
|
|
2064
2714
|
}
|
|
2065
2715
|
|
|
2066
|
-
// src/hooks/use-
|
|
2067
|
-
import {
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
}
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
const
|
|
2084
|
-
const
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
const
|
|
2089
|
-
const
|
|
2090
|
-
const
|
|
2091
|
-
const
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
};
|
|
2109
|
-
}, [beginRead, client, workspaceId, sessionId]);
|
|
2110
|
-
const state = usePolledValue(load, { pollIntervalMs: options.pollIntervalMs, enabled });
|
|
2111
|
-
const refresh = state.refresh;
|
|
2112
|
-
useEffect7(() => {
|
|
2113
|
-
if (enabled && workspaceControlEvent) void refresh();
|
|
2114
|
-
}, [enabled, refresh, workspaceControlEvent]);
|
|
2115
|
-
useEffect7(() => {
|
|
2116
|
-
if (!sessionId || !enabled) return;
|
|
2117
|
-
return registerSessionReconciler(sessionId, "lineage", refresh);
|
|
2118
|
-
}, [enabled, refresh, registerSessionReconciler, sessionId]);
|
|
2119
|
-
const refreshSoon = useDebouncedCallback(() => void refresh(), 150);
|
|
2120
|
-
const delayedChildRefreshRef = useRef8(null);
|
|
2121
|
-
useEffect7(() => {
|
|
2716
|
+
// src/hooks/use-new-conversation.ts
|
|
2717
|
+
import {
|
|
2718
|
+
OpenGeniApiError as OpenGeniApiError2
|
|
2719
|
+
} from "@opengeni/sdk";
|
|
2720
|
+
import { useLayoutEffect as useLayoutEffect5, useMemo as useMemo5, useRef as useRef9, useState as useState8 } from "react";
|
|
2721
|
+
var NOOP_ASYNC = async () => {
|
|
2722
|
+
};
|
|
2723
|
+
function useNewConversation(options = {}) {
|
|
2724
|
+
const context = useOpenGeni(options);
|
|
2725
|
+
const scope = { client: context.client, workspaceId: context.workspaceId };
|
|
2726
|
+
const config = useClientConfigFlags(context.client);
|
|
2727
|
+
const showModelPicker = options.modelPicker ?? config.modelSelection;
|
|
2728
|
+
const catalog = useWorkspaceModelCatalog({ ...scope, enabled: showModelPicker });
|
|
2729
|
+
const files = useFileAttachments(scope);
|
|
2730
|
+
const uploadsEnabled = (options.attachments ?? true) && config.uploads;
|
|
2731
|
+
const available = options.enabled !== false && (options.createSession !== void 0 || config.sessionCreation);
|
|
2732
|
+
const [text, setText] = useState8("");
|
|
2733
|
+
const [choice, setChoice] = useState8({});
|
|
2734
|
+
const [sending, setSending] = useState8(false);
|
|
2735
|
+
const [error, setError] = useState8(null);
|
|
2736
|
+
const [pending, setPending] = useState8(null);
|
|
2737
|
+
const [completion, setCompletion] = useState8(null);
|
|
2738
|
+
const textRef = useRef9(text);
|
|
2739
|
+
const textRevision = useRef9(0);
|
|
2740
|
+
const choiceRef = useRef9(choice);
|
|
2741
|
+
const filesRef = useRef9(files);
|
|
2742
|
+
filesRef.current = files;
|
|
2743
|
+
const pendingRef = useRef9(null);
|
|
2744
|
+
const completionRef = useRef9(null);
|
|
2745
|
+
const inFlight = useRef9(false);
|
|
2746
|
+
const lifetime = useRef9({ mounted: true, generation: 0 }).current;
|
|
2747
|
+
const identity = useMemo5(
|
|
2748
|
+
() => ({
|
|
2749
|
+
client: context.client,
|
|
2750
|
+
workspaceId: context.workspaceId,
|
|
2751
|
+
scopeKey: options.scopeKey
|
|
2752
|
+
}),
|
|
2753
|
+
[context.client, context.workspaceId, options.scopeKey]
|
|
2754
|
+
);
|
|
2755
|
+
const committedIdentity = useRef9(identity);
|
|
2756
|
+
useLayoutEffect5(() => {
|
|
2757
|
+
lifetime.mounted = true;
|
|
2122
2758
|
return () => {
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
delayedChildRefreshRef.current = null;
|
|
2126
|
-
}
|
|
2759
|
+
lifetime.mounted = false;
|
|
2760
|
+
lifetime.generation++;
|
|
2127
2761
|
};
|
|
2128
|
-
}, [
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2762
|
+
}, [lifetime]);
|
|
2763
|
+
useLayoutEffect5(() => {
|
|
2764
|
+
if (committedIdentity.current === identity) return;
|
|
2765
|
+
committedIdentity.current = identity;
|
|
2766
|
+
lifetime.generation++;
|
|
2767
|
+
inFlight.current = false;
|
|
2768
|
+
pendingRef.current = null;
|
|
2769
|
+
completionRef.current = null;
|
|
2770
|
+
textRef.current = "";
|
|
2771
|
+
textRevision.current = 0;
|
|
2772
|
+
choiceRef.current = {};
|
|
2773
|
+
setText("");
|
|
2774
|
+
setChoice({});
|
|
2775
|
+
setSending(false);
|
|
2776
|
+
setError(null);
|
|
2777
|
+
setPending(null);
|
|
2778
|
+
setCompletion(null);
|
|
2779
|
+
filesRef.current.clear();
|
|
2780
|
+
}, [identity, lifetime]);
|
|
2781
|
+
const ownsScope = () => lifetime.mounted && committedIdentity.current === identity;
|
|
2782
|
+
const updateText = (value) => {
|
|
2783
|
+
if (!ownsScope()) return;
|
|
2784
|
+
if (value !== textRef.current) textRevision.current++;
|
|
2785
|
+
textRef.current = value;
|
|
2786
|
+
setText(value);
|
|
2787
|
+
};
|
|
2788
|
+
const updateChoice = (patch) => {
|
|
2789
|
+
if (!ownsScope()) return;
|
|
2790
|
+
const next = { ...choiceRef.current, ...patch };
|
|
2791
|
+
const model = catalog.models.find((row) => row.id === (next.model ?? catalog.defaultModel));
|
|
2792
|
+
if (model) {
|
|
2793
|
+
if (next.reasoningEffort)
|
|
2794
|
+
next.reasoningEffort = coerceReasoningEffortForModel(model, next.reasoningEffort);
|
|
2795
|
+
if (next.latencyMode && !runnableLatencyModesForModel(model).includes(next.latencyMode))
|
|
2796
|
+
next.latencyMode = "standard";
|
|
2133
2797
|
}
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
void refresh();
|
|
2137
|
-
}, 2500);
|
|
2138
|
-
}, [refresh]);
|
|
2139
|
-
useSessionEventTrigger(
|
|
2140
|
-
client,
|
|
2141
|
-
workspaceId,
|
|
2142
|
-
sessionId,
|
|
2143
|
-
isLineageRefreshEvent,
|
|
2144
|
-
refreshSoon,
|
|
2145
|
-
// SHARED-FEED ONLY: without a caller-provided events log the trigger would
|
|
2146
|
-
// open its OWN streamEvents tail — a second live SSE connection next to the
|
|
2147
|
-
// session route's useSessionEvents. A caller with no feed opts into polling
|
|
2148
|
-
// (pollIntervalMs), never a duplicate stream.
|
|
2149
|
-
{ events: options.events, enabled: enabled && options.events !== void 0 }
|
|
2150
|
-
);
|
|
2151
|
-
useSessionEventTrigger(
|
|
2152
|
-
client,
|
|
2153
|
-
workspaceId,
|
|
2154
|
-
sessionId,
|
|
2155
|
-
isSessionCreateToolCallCreated,
|
|
2156
|
-
refreshAfterChildCreate,
|
|
2157
|
-
{ events: options.events, enabled: enabled && options.events !== void 0 }
|
|
2158
|
-
);
|
|
2159
|
-
return {
|
|
2160
|
-
lineage: state.data?.lineage ?? null,
|
|
2161
|
-
loading: state.loading,
|
|
2162
|
-
error: state.error,
|
|
2163
|
-
readGeneration: state.data?.readGeneration ?? 0,
|
|
2164
|
-
refresh
|
|
2798
|
+
choiceRef.current = next;
|
|
2799
|
+
setChoice(choiceRef.current);
|
|
2165
2800
|
};
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
};
|
|
2801
|
+
const effectivePolicy = () => {
|
|
2802
|
+
const model = choiceRef.current.model ?? catalog.defaultModel;
|
|
2803
|
+
const definition = catalog.models.find((row) => row.id === model);
|
|
2804
|
+
const effort = choiceRef.current.reasoningEffort ?? (definition ? defaultEffortForModel(definition) : config.defaultReasoningEffort ?? "medium");
|
|
2805
|
+
return showModelPicker && model ? {
|
|
2806
|
+
model,
|
|
2807
|
+
reasoningEffort: definition ? coerceReasoningEffortForModel(definition, effort) : effort,
|
|
2808
|
+
latencyMode: choiceRef.current.latencyMode ?? "standard"
|
|
2809
|
+
} : void 0;
|
|
2810
|
+
};
|
|
2811
|
+
const create = options.createSession ?? (async (initialMessage, idempotencyKey, input) => {
|
|
2812
|
+
const creator = context.client.createSession;
|
|
2813
|
+
if (typeof creator !== "function")
|
|
2814
|
+
throw new Error("New chats are not enabled for this product.");
|
|
2815
|
+
const created = await creator.call(context.client, context.workspaceId, {
|
|
2816
|
+
...input.startMode === "realtime" ? {} : { initialMessage },
|
|
2817
|
+
idempotencyKey,
|
|
2818
|
+
...input
|
|
2819
|
+
});
|
|
2820
|
+
return created.id;
|
|
2185
2821
|
});
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2822
|
+
const finishCreation = (accepted) => {
|
|
2823
|
+
if (!lifetime.mounted || lifetime.generation !== accepted.generation) return;
|
|
2824
|
+
const { attempt, sessionId } = accepted;
|
|
2825
|
+
const sentFiles = new Set(attempt.options.resources?.map((resource) => resource.fileId) ?? []);
|
|
2826
|
+
const policyChanged = ["model", "reasoningEffort", "latencyMode"].some(
|
|
2827
|
+
(field) => choiceRef.current[field] !== attempt.options[field]
|
|
2828
|
+
);
|
|
2829
|
+
const draft = {
|
|
2830
|
+
text: attempt.clearDraft && textRevision.current === attempt.textRevision ? "" : textRef.current,
|
|
2831
|
+
resources: filesRef.current.readyResources.filter(
|
|
2832
|
+
(resource) => !sentFiles.has(resource.fileId)
|
|
2833
|
+
),
|
|
2834
|
+
// Unchanged choices are already part of the created session. Do not
|
|
2835
|
+
// replace server-owned defaults with a browser's catalog estimate.
|
|
2836
|
+
...policyChanged ? { policy: effectivePolicy() } : {}
|
|
2837
|
+
};
|
|
2838
|
+
pendingRef.current = null;
|
|
2839
|
+
completionRef.current = null;
|
|
2840
|
+
setPending(null);
|
|
2841
|
+
setCompletion(null);
|
|
2842
|
+
updateText(draft.text);
|
|
2843
|
+
filesRef.current.removeReadyFiles(sentFiles);
|
|
2844
|
+
notifyObserver(options.onCreated, {
|
|
2845
|
+
sessionId,
|
|
2846
|
+
initialMessage: attempt.initialMessage,
|
|
2847
|
+
draft,
|
|
2848
|
+
...attempt.realtimeModel ? { realtimeModel: attempt.realtimeModel } : {}
|
|
2849
|
+
});
|
|
2850
|
+
};
|
|
2851
|
+
const finishRef = useRef9(finishCreation);
|
|
2852
|
+
finishRef.current = finishCreation;
|
|
2853
|
+
useLayoutEffect5(() => {
|
|
2854
|
+
if (completion && !files.hasUnresolved) finishRef.current(completion);
|
|
2855
|
+
}, [completion, files.hasUnresolved]);
|
|
2856
|
+
const deliver = async (attempt) => {
|
|
2857
|
+
if (!ownsScope() || inFlight.current || completionRef.current || !available) return false;
|
|
2858
|
+
const ownedGeneration = lifetime.generation;
|
|
2859
|
+
inFlight.current = true;
|
|
2860
|
+
pendingRef.current = attempt;
|
|
2861
|
+
setSending(true);
|
|
2862
|
+
setError(null);
|
|
2863
|
+
setPending(null);
|
|
2864
|
+
try {
|
|
2865
|
+
const sessionId = await attempt.create(
|
|
2866
|
+
attempt.initialMessage,
|
|
2867
|
+
attempt.key,
|
|
2868
|
+
structuredClone(attempt.options)
|
|
2869
|
+
);
|
|
2870
|
+
if (!lifetime.mounted || lifetime.generation !== ownedGeneration) return true;
|
|
2871
|
+
const accepted = { sessionId, attempt, generation: ownedGeneration };
|
|
2872
|
+
completionRef.current = accepted;
|
|
2873
|
+
if (filesRef.current.hasUnresolved) setCompletion(accepted);
|
|
2874
|
+
else finishCreation(accepted);
|
|
2875
|
+
return true;
|
|
2876
|
+
} catch (cause) {
|
|
2877
|
+
if (!lifetime.mounted || lifetime.generation !== ownedGeneration) return false;
|
|
2878
|
+
setError({ cause });
|
|
2879
|
+
if (cause instanceof OpenGeniApiError2 && cause.outcomeUnknown === false) {
|
|
2880
|
+
pendingRef.current = null;
|
|
2881
|
+
} else {
|
|
2882
|
+
setPending(attempt);
|
|
2203
2883
|
}
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
owningTurnId = null;
|
|
2210
|
-
}
|
|
2211
|
-
break;
|
|
2884
|
+
return false;
|
|
2885
|
+
} finally {
|
|
2886
|
+
if (lifetime.mounted && lifetime.generation === ownedGeneration) {
|
|
2887
|
+
inFlight.current = false;
|
|
2888
|
+
setSending(false);
|
|
2212
2889
|
}
|
|
2213
|
-
default:
|
|
2214
|
-
break;
|
|
2215
2890
|
}
|
|
2216
|
-
}
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
enabled,
|
|
2234
|
-
pollIntervalMs: options.pollIntervalMs
|
|
2235
|
-
});
|
|
2236
|
-
const loadedRequests = state.data;
|
|
2237
|
-
const refreshRequests = state.refresh;
|
|
2238
|
-
const mutation = useMutationRunner(`${workspaceId}\0${sessionId ?? ""}`);
|
|
2239
|
-
const [respondingRequestId, setRespondingRequestId] = useState7(null);
|
|
2240
|
-
const [deadlineVersion, setDeadlineVersion] = useState7(0);
|
|
2241
|
-
const respondingRequestRef = useRef9(null);
|
|
2242
|
-
useEffect8(() => {
|
|
2243
|
-
respondingRequestRef.current = null;
|
|
2244
|
-
setRespondingRequestId(null);
|
|
2245
|
-
}, [sessionId, workspaceId]);
|
|
2246
|
-
useEffect8(() => {
|
|
2247
|
-
if (!sessionId || !enabled) return;
|
|
2248
|
-
return registerSessionReconciler(sessionId, "human-input", state.refresh);
|
|
2249
|
-
}, [enabled, registerSessionReconciler, sessionId, state.refresh]);
|
|
2250
|
-
const scheduleRefresh = useDebouncedCallback(() => void state.refresh());
|
|
2251
|
-
useSessionEventTrigger(client, workspaceId, sessionId, isHumanInputEvent, scheduleRefresh, {
|
|
2252
|
-
enabled,
|
|
2253
|
-
...options.events !== void 0 ? { events: options.events } : {}
|
|
2254
|
-
});
|
|
2255
|
-
useEffect8(() => {
|
|
2256
|
-
const now2 = Date.now();
|
|
2257
|
-
const nextDeadline = (loadedRequests ?? []).reduce((next, request) => {
|
|
2258
|
-
if (request.status !== "pending" || !request.expiresAt) return next;
|
|
2259
|
-
const deadline = Date.parse(request.expiresAt);
|
|
2260
|
-
if (!Number.isFinite(deadline) || deadline <= now2) return next;
|
|
2261
|
-
return next === null ? deadline : Math.min(next, deadline);
|
|
2262
|
-
}, null);
|
|
2263
|
-
if (nextDeadline === null) return;
|
|
2264
|
-
const timer = globalThis.setTimeout(
|
|
2265
|
-
() => {
|
|
2266
|
-
setDeadlineVersion((current) => current + 1);
|
|
2267
|
-
void refreshRequests();
|
|
2268
|
-
},
|
|
2269
|
-
Math.min(2147483647, Math.max(0, nextDeadline - now2 + 1))
|
|
2270
|
-
);
|
|
2271
|
-
return () => globalThis.clearTimeout(timer);
|
|
2272
|
-
}, [loadedRequests, refreshRequests]);
|
|
2273
|
-
const respond = useCallback9(
|
|
2274
|
-
async (requestId, response) => {
|
|
2275
|
-
if (!sessionId || respondingRequestRef.current !== null) return null;
|
|
2276
|
-
respondingRequestRef.current = requestId;
|
|
2277
|
-
setRespondingRequestId(requestId);
|
|
2278
|
-
try {
|
|
2279
|
-
const event = await mutation.run(
|
|
2280
|
-
() => client.submitHumanInputResponse(workspaceId, sessionId, requestId, response, {
|
|
2281
|
-
clientEventId: globalThis.crypto.randomUUID()
|
|
2282
|
-
})
|
|
2283
|
-
);
|
|
2284
|
-
if (event) await state.refresh();
|
|
2285
|
-
return event;
|
|
2286
|
-
} finally {
|
|
2287
|
-
if (respondingRequestRef.current === requestId) {
|
|
2288
|
-
respondingRequestRef.current = null;
|
|
2289
|
-
setRespondingRequestId(null);
|
|
2290
|
-
}
|
|
2891
|
+
};
|
|
2892
|
+
const submit = async (explicit) => {
|
|
2893
|
+
if (!ownsScope() || !available || inFlight.current || pendingRef.current || uploadsEnabled && filesRef.current.hasUnresolved)
|
|
2894
|
+
return false;
|
|
2895
|
+
const resources = uploadsEnabled ? filesRef.current.readyResources : [];
|
|
2896
|
+
const draftText = explicit ?? textRef.current;
|
|
2897
|
+
const initialMessage = draftText.trim() || (resources.length > 0 ? FILE_ONLY_MESSAGE_TEXT : "");
|
|
2898
|
+
if (!initialMessage) return false;
|
|
2899
|
+
return deliver({
|
|
2900
|
+
key: crypto.randomUUID(),
|
|
2901
|
+
initialMessage,
|
|
2902
|
+
textRevision: textRevision.current,
|
|
2903
|
+
clearDraft: explicit === void 0,
|
|
2904
|
+
create,
|
|
2905
|
+
options: {
|
|
2906
|
+
...resources.length ? { resources: structuredClone(resources) } : {},
|
|
2907
|
+
...showModelPicker ? { ...choiceRef.current } : {}
|
|
2291
2908
|
}
|
|
2909
|
+
});
|
|
2910
|
+
};
|
|
2911
|
+
const startRealtime = async (model) => {
|
|
2912
|
+
if (!ownsScope() || !available || inFlight.current || pendingRef.current || !config.realtimeVoice || !(options.realtimeVoice ?? config.realtimeVoiceOffered))
|
|
2913
|
+
return false;
|
|
2914
|
+
if (filesRef.current.hasUnresolved) return false;
|
|
2915
|
+
return deliver({
|
|
2916
|
+
key: crypto.randomUUID(),
|
|
2917
|
+
initialMessage: "",
|
|
2918
|
+
textRevision: textRevision.current,
|
|
2919
|
+
clearDraft: false,
|
|
2920
|
+
create,
|
|
2921
|
+
options: { ...showModelPicker ? { ...choiceRef.current } : {}, startMode: "realtime" },
|
|
2922
|
+
realtimeModel: model
|
|
2923
|
+
});
|
|
2924
|
+
};
|
|
2925
|
+
const composer = {
|
|
2926
|
+
value: text,
|
|
2927
|
+
setValue: updateText,
|
|
2928
|
+
hasDraftContent: () => textRef.current.length > 0 || filesRef.current.attachments.length > 0,
|
|
2929
|
+
send: submit,
|
|
2930
|
+
steer: submit,
|
|
2931
|
+
sending,
|
|
2932
|
+
canSend: available && !sending && !pending && !completion && (text.trim().length > 0 || uploadsEnabled && files.readyResources.length > 0) && !(uploadsEnabled && files.hasUnresolved),
|
|
2933
|
+
pause: NOOP_ASYNC,
|
|
2934
|
+
pausing: false,
|
|
2935
|
+
resume: NOOP_ASYNC,
|
|
2936
|
+
resumeScope: NOOP_ASYNC,
|
|
2937
|
+
resuming: false,
|
|
2938
|
+
draft: null,
|
|
2939
|
+
draftRevision: 0,
|
|
2940
|
+
draftLoading: false,
|
|
2941
|
+
draftSaving: false,
|
|
2942
|
+
draftConflict: null,
|
|
2943
|
+
policy: effectivePolicy() ?? null,
|
|
2944
|
+
setModel: (model) => updateChoice({ model }),
|
|
2945
|
+
setReasoningEffort: (reasoningEffort) => updateChoice({ reasoningEffort }),
|
|
2946
|
+
setLatencyMode: (latencyMode) => updateChoice({ latencyMode }),
|
|
2947
|
+
draftPersistence: "disabled",
|
|
2948
|
+
applyDraft: () => {
|
|
2292
2949
|
},
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2950
|
+
reloadDraft: NOOP_ASYNC,
|
|
2951
|
+
resolveDraftConflict: NOOP_ASYNC,
|
|
2952
|
+
restoredResources: [],
|
|
2953
|
+
removeRestoredResource: () => {
|
|
2954
|
+
},
|
|
2955
|
+
error: null,
|
|
2956
|
+
clearError: () => {
|
|
2957
|
+
if (ownsScope()) setError(null);
|
|
2958
|
+
}
|
|
2959
|
+
};
|
|
2297
2960
|
return {
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2961
|
+
...scope,
|
|
2962
|
+
config,
|
|
2963
|
+
files,
|
|
2964
|
+
uploadsEnabled,
|
|
2965
|
+
available,
|
|
2966
|
+
showModelPicker,
|
|
2967
|
+
catalog,
|
|
2968
|
+
composer,
|
|
2969
|
+
realtimeVoiceEnabled: config.realtimeVoice && (options.realtimeVoice ?? config.realtimeVoiceOffered),
|
|
2970
|
+
error,
|
|
2971
|
+
finishingUploads: completion !== null,
|
|
2972
|
+
pending: pending ? { idempotencyKey: pending.key, realtimeModel: pending.realtimeModel } : null,
|
|
2973
|
+
retry: () => pendingRef.current ? deliver(pendingRef.current) : Promise.resolve(false),
|
|
2974
|
+
startRealtime
|
|
2308
2975
|
};
|
|
2309
2976
|
}
|
|
2310
2977
|
|
|
2311
2978
|
export {
|
|
2312
|
-
|
|
2979
|
+
approvalsFromRequiresAction,
|
|
2980
|
+
projectPendingApprovals,
|
|
2981
|
+
invokeOlderHistoryLoaderWithReceiptCapture,
|
|
2982
|
+
createOlderHistoryLoadReceipt,
|
|
2983
|
+
conversationTimeline,
|
|
2984
|
+
useAvailableModels,
|
|
2985
|
+
useWorkspaceModelCatalog,
|
|
2986
|
+
useClientConfigFlags,
|
|
2987
|
+
useFileAttachments,
|
|
2988
|
+
isHumanInputEvent,
|
|
2989
|
+
useHumanInputRequests,
|
|
2313
2990
|
isTitleEvent,
|
|
2314
2991
|
useSession,
|
|
2992
|
+
useSessionControl,
|
|
2315
2993
|
SESSION_EVENT_BROWSER_MAX_BYTES,
|
|
2316
2994
|
SESSION_EVENT_BROWSER_MAX_COUNT,
|
|
2317
|
-
SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES,
|
|
2318
2995
|
SESSION_EVENT_BROWSER_PENDING_MAX_BYTES,
|
|
2319
2996
|
SESSION_EVENT_BROWSER_PENDING_MAX_COUNT,
|
|
2320
2997
|
useSessionEvents,
|
|
2321
2998
|
boundBrowserSessionEventWindow,
|
|
2322
|
-
useFileAttachments,
|
|
2323
2999
|
isTurnQueueEvent,
|
|
2324
3000
|
useTurnQueue,
|
|
3001
|
+
useSessionConversation,
|
|
2325
3002
|
isGoalEvent,
|
|
2326
3003
|
useGoal,
|
|
2327
|
-
|
|
2328
|
-
isSessionMcpApprovalPolicyEvent,
|
|
2329
|
-
useSessionMcpApprovalPolicy,
|
|
2330
|
-
isLineageRefreshEvent,
|
|
2331
|
-
useSessionLineage,
|
|
2332
|
-
approvalsFromRequiresAction,
|
|
2333
|
-
projectPendingApprovals,
|
|
2334
|
-
isHumanInputEvent,
|
|
2335
|
-
useHumanInputRequests
|
|
3004
|
+
useNewConversation
|
|
2336
3005
|
};
|
|
2337
|
-
//# sourceMappingURL=chunk-
|
|
3006
|
+
//# sourceMappingURL=chunk-X5UVUIMF.js.map
|