@opengeni/react 0.57.0 → 1.0.2-canary.37191796234001
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 +654 -22
- package/dist/accounts.d.ts +64 -0
- package/dist/accounts.js +822 -0
- package/dist/accounts.js.map +1 -0
- package/dist/approvals.d.ts +2 -1
- package/dist/artifacts-document.js +3 -2
- package/dist/artifacts-presentation.js +3 -2
- package/dist/artifacts-spreadsheet.js +2 -2
- package/dist/artifacts.d.ts +4 -1
- package/dist/artifacts.js +518 -42
- package/dist/artifacts.js.map +1 -1
- package/dist/browser-viewer-GTR5XQ7X.js +16 -0
- package/dist/capability-catalog-row.d.ts +20 -0
- package/dist/capability-logo-fallback.d.ts +3 -0
- package/dist/chat-interactive-block-UGBTNB3L.js +11 -0
- package/dist/chat-interactive-block-UGBTNB3L.js.map +1 -0
- package/dist/{chunk-5UQPNLIO.js → chunk-23J2ZNXT.js} +4117 -3896
- package/dist/chunk-23J2ZNXT.js.map +1 -0
- package/dist/chunk-447JLCBU.js +209 -0
- package/dist/chunk-447JLCBU.js.map +1 -0
- package/dist/chunk-4RVYOY4O.js +379 -0
- package/dist/chunk-4RVYOY4O.js.map +1 -0
- package/dist/chunk-5CVXZA3Y.js +292 -0
- package/dist/chunk-5CVXZA3Y.js.map +1 -0
- package/dist/chunk-5J4FOW6Q.js +108 -0
- package/dist/chunk-5J4FOW6Q.js.map +1 -0
- package/dist/chunk-A2PI6UMY.js +41 -0
- package/dist/chunk-A2PI6UMY.js.map +1 -0
- package/dist/{chunk-7WCLAEFV.js → chunk-A4UJB2MM.js} +37 -27
- package/dist/chunk-A4UJB2MM.js.map +1 -0
- package/dist/chunk-B3V6TG77.js +41 -0
- package/dist/chunk-B3V6TG77.js.map +1 -0
- package/dist/chunk-BRM6FVZA.js +302 -0
- package/dist/chunk-BRM6FVZA.js.map +1 -0
- package/dist/chunk-C5X5AJXK.js +182 -0
- package/dist/chunk-C5X5AJXK.js.map +1 -0
- package/dist/chunk-CR7U7HAR.js +453 -0
- package/dist/chunk-CR7U7HAR.js.map +1 -0
- package/dist/{chunk-R3AZOAB5.js → chunk-DYWBUX5Q.js} +29 -123
- package/dist/chunk-DYWBUX5Q.js.map +1 -0
- package/dist/chunk-E3G7SAYZ.js +3124 -0
- package/dist/chunk-E3G7SAYZ.js.map +1 -0
- package/dist/chunk-ELCE5SXV.js +359 -0
- package/dist/chunk-ELCE5SXV.js.map +1 -0
- package/dist/chunk-ERGYJIBB.js +21 -0
- package/dist/chunk-ERGYJIBB.js.map +1 -0
- package/dist/chunk-FFA2OVOV.js +319 -0
- package/dist/chunk-FFA2OVOV.js.map +1 -0
- package/dist/chunk-FZCP527Y.js +116 -0
- package/dist/chunk-FZCP527Y.js.map +1 -0
- package/dist/chunk-GIMRELBW.js +14 -0
- package/dist/chunk-GIMRELBW.js.map +1 -0
- package/dist/{chunk-6KLX2RH5.js → chunk-H2NJQORO.js} +1069 -79
- package/dist/chunk-H2NJQORO.js.map +1 -0
- package/dist/chunk-I7IWRQLN.js +1608 -0
- package/dist/chunk-I7IWRQLN.js.map +1 -0
- package/dist/{chunk-HA46CYQK.js → chunk-IQUCRAT7.js} +792 -537
- package/dist/chunk-IQUCRAT7.js.map +1 -0
- package/dist/chunk-IWIDV7SB.js +1 -0
- package/dist/chunk-IWIDV7SB.js.map +1 -0
- package/dist/chunk-J2QWVSQY.js +312 -0
- package/dist/chunk-J2QWVSQY.js.map +1 -0
- package/dist/chunk-JFNEV4LU.js +2733 -0
- package/dist/chunk-JFNEV4LU.js.map +1 -0
- package/dist/chunk-JT4JW7BC.js +54 -0
- package/dist/chunk-JT4JW7BC.js.map +1 -0
- package/dist/chunk-K3HQAKQX.js +24 -0
- package/dist/chunk-K3HQAKQX.js.map +1 -0
- package/dist/{chunk-C5ZPSSS5.js → chunk-NEXLLTP4.js} +2 -1
- package/dist/{chunk-C5ZPSSS5.js.map → chunk-NEXLLTP4.js.map} +1 -1
- package/dist/chunk-NZUI76EM.js +371 -0
- package/dist/chunk-NZUI76EM.js.map +1 -0
- package/dist/{chunk-PEI6RANI.js → chunk-PNY3JKBJ.js} +309 -74
- package/dist/chunk-PNY3JKBJ.js.map +1 -0
- package/dist/{chunk-IGC6A2ZI.js → chunk-Q5QBEZIQ.js} +777 -246
- package/dist/chunk-Q5QBEZIQ.js.map +1 -0
- package/dist/chunk-RHREZ7BO.js +11 -0
- package/dist/chunk-RHREZ7BO.js.map +1 -0
- package/dist/chunk-SCOTPE2I.js +589 -0
- package/dist/chunk-SCOTPE2I.js.map +1 -0
- package/dist/{chunk-B6WPZSTT.js → chunk-SJZDKVW2.js} +1331 -1163
- package/dist/chunk-SJZDKVW2.js.map +1 -0
- package/dist/chunk-TMJRG4YC.js +12803 -0
- package/dist/chunk-TMJRG4YC.js.map +1 -0
- package/dist/chunk-TMYGL6X5.js +742 -0
- package/dist/chunk-TMYGL6X5.js.map +1 -0
- package/dist/chunk-UGLEYEMP.js +33 -0
- package/dist/chunk-UGLEYEMP.js.map +1 -0
- package/dist/chunk-UK4TINKT.js +27 -0
- package/dist/chunk-UK4TINKT.js.map +1 -0
- package/dist/chunk-UTRTG6LH.js +220 -0
- package/dist/chunk-UTRTG6LH.js.map +1 -0
- package/dist/chunk-WEHMPOXK.js +39 -0
- package/dist/chunk-WEHMPOXK.js.map +1 -0
- package/dist/chunk-WRHIZYZF.js +988 -0
- package/dist/chunk-WRHIZYZF.js.map +1 -0
- package/dist/{chunk-HUIKIDM5.js → chunk-WUISZ3WU.js} +223 -115
- package/dist/chunk-WUISZ3WU.js.map +1 -0
- package/dist/chunk-XCHWDJ22.js +107 -0
- package/dist/chunk-XCHWDJ22.js.map +1 -0
- package/dist/chunk-XOAMRDDK.js +481 -0
- package/dist/chunk-XOAMRDDK.js.map +1 -0
- package/dist/chunk-XUW2UWQM.js +299 -0
- package/dist/chunk-XUW2UWQM.js.map +1 -0
- package/dist/{chunk-KCGXPP5V.js → chunk-Y2PRMYLC.js} +11 -6
- package/dist/chunk-Y2PRMYLC.js.map +1 -0
- package/dist/chunk-YBXQDQFX.js +1772 -0
- package/dist/chunk-YBXQDQFX.js.map +1 -0
- package/dist/{chunk-KZ73RL76.js → chunk-YQEAA6LT.js} +7 -3
- package/dist/chunk-YQEAA6LT.js.map +1 -0
- package/dist/chunk-YV4L3ILF.js +9 -0
- package/dist/chunk-YV4L3ILF.js.map +1 -0
- package/dist/{chunk-LLOLUHVG.js → chunk-YWFG7ZX7.js} +1358 -2746
- package/dist/chunk-YWFG7ZX7.js.map +1 -0
- package/dist/chunk-ZYRGEKWS.js +216 -0
- package/dist/chunk-ZYRGEKWS.js.map +1 -0
- package/dist/client.d.ts +7 -4
- package/dist/clipboard.js +1 -0
- package/dist/commands/types.d.ts +2 -0
- package/dist/components/anthropic-mark.d.ts +3 -0
- package/dist/components/approval-surface.d.ts +15 -0
- package/dist/components/artifacts/artifact-chrome.d.ts +120 -0
- package/dist/components/artifacts/artifact-sandbox.d.ts +27 -0
- package/dist/components/artifacts/artifact-surface.d.ts +3 -1
- 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 +46 -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 +2 -1
- package/dist/components/artifacts/index.d.ts +8 -1
- 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 +48 -3
- package/dist/components/artifacts/session-artifact-viewer.d.ts +39 -0
- package/dist/components/artifacts/site-view.d.ts +23 -0
- package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -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 +10 -1
- package/dist/components/chat-composer.d.ts +21 -3
- package/dist/components/child-session-link.d.ts +5 -0
- package/dist/components/claude-mark.d.ts +3 -0
- package/dist/components/code-editor.d.ts +1 -4
- package/dist/components/composer.d.ts +45 -4
- package/dist/components/computer-viewer.d.ts +4 -1
- package/dist/components/copy-button.d.ts +3 -1
- package/dist/components/desktop-viewer.d.ts +1 -1
- package/dist/components/enrollment-consent.d.ts +6 -3
- package/dist/components/file-browser.d.ts +10 -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 +21 -2
- package/dist/components/human-input-surface.d.ts +6 -3
- package/dist/components/machine-card.d.ts +4 -1
- package/dist/components/machines/machine-detail.d.ts +4 -1
- package/dist/components/machines/machine-operation-policy.d.ts +11 -0
- package/dist/components/machines-dashboard.d.ts +3 -1
- package/dist/components/markdown-table-layout.d.ts +12 -0
- package/dist/components/markdown.d.ts +47 -3
- package/dist/components/message-timeline.d.ts +72 -9
- 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 +62 -13
- package/dist/components/open-geni-chat.d.ts +55 -0
- package/dist/components/open-geni-links.d.ts +68 -0
- package/dist/components/openrouter-mark.d.ts +6 -0
- package/dist/components/pierre-file.d.ts +1 -1
- package/dist/components/preview-loading.d.ts +2 -0
- package/dist/components/queue-item-content.d.ts +9 -0
- package/dist/components/sandbox-files.d.ts +13 -2
- package/dist/components/sandbox-workspace.d.ts +43 -3
- package/dist/components/session-capability-frame.d.ts +35 -0
- package/dist/components/session-chrome.d.ts +66 -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 +86 -0
- package/dist/components/session-list.d.ts +36 -0
- package/dist/components/session-mcp-capability-card.d.ts +41 -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/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/components/workspace-dock.d.ts +6 -2
- package/dist/composer.d.ts +5 -4
- package/dist/composer.js +15 -9
- package/dist/computer-viewer-ENHVAZ2Q.js +19 -0
- package/dist/computer-viewer-ENHVAZ2Q.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 +16 -0
- package/dist/connect.d.ts +21 -0
- package/dist/connect.js +2129 -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 +26 -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 +14 -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 +21 -0
- package/dist/editor.js.map +1 -0
- package/dist/embedded-session-client.d.ts +23 -0
- package/dist/file-node-visibility.d.ts +15 -0
- package/dist/{fleet-decision-row-GGCAT4E4.js → fleet-decision-row-YZ3ZKWMM.js} +2 -1
- package/dist/fleet-decision-row-YZ3ZKWMM.js.map +1 -0
- package/dist/hooks/channel-cache.d.ts +23 -0
- package/dist/hooks/latest-question.d.ts +25 -0
- package/dist/hooks/shared-machines-list.d.ts +14 -0
- package/dist/hooks/use-attached-browsers.d.ts +2 -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-channels.d.ts +1 -0
- package/dist/hooks/use-codex-accounts.d.ts +25 -7
- package/dist/hooks/use-composer.d.ts +45 -9
- 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 +2 -0
- package/dist/hooks/use-connect.d.ts +22 -0
- package/dist/hooks/use-desktop-stream.d.ts +1 -1
- package/dist/hooks/use-file-attachments.d.ts +21 -2
- package/dist/hooks/use-machine-chip.d.ts +3 -3
- package/dist/hooks/use-machines.d.ts +19 -1
- package/dist/hooks/use-sandbox-files.d.ts +5 -1
- package/dist/hooks/use-session-background-commands.d.ts +14 -0
- package/dist/hooks/use-session-events.d.ts +30 -8
- package/dist/hooks/use-session-lineage.d.ts +4 -0
- package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
- package/dist/hooks/use-session.d.ts +6 -0
- package/dist/hooks/use-turn-queue.d.ts +12 -0
- package/dist/hooks/use-usage.d.ts +40 -0
- package/dist/hooks/use-workspace-edit.d.ts +4 -0
- package/dist/hooks/use-workspace-sessions.d.ts +27 -5
- package/dist/human-input.d.ts +3 -1
- package/dist/identity-link-accounts.d.ts +11 -0
- package/dist/identity-link-consent.d.ts +18 -0
- package/dist/index.d.ts +60 -17
- package/dist/index.js +2237 -6200
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +1 -1
- package/dist/interaction.js +35 -184
- package/dist/interaction.js.map +1 -1
- package/dist/latest-question-755OW5DS.js +117 -0
- package/dist/latest-question-755OW5DS.js.map +1 -0
- package/dist/lib/error-message.d.ts +7 -0
- package/dist/lib/format.d.ts +36 -2
- package/dist/lib/host-theme.d.ts +70 -0
- package/dist/lib/interaction-errors.d.ts +6 -0
- package/dist/lib/menu-styles.d.ts +22 -0
- package/dist/lib/pierre-diffs-loader.d.ts +20 -0
- package/dist/lib/workbench-peers.d.ts +36 -0
- package/dist/lib/xterm-theme.d.ts +7 -5
- package/dist/machines.d.ts +4 -2
- package/dist/machines.js +14 -4
- package/dist/markdown-table-layout-QXSLK4BY.js +106 -0
- package/dist/markdown-table-layout-QXSLK4BY.js.map +1 -0
- package/dist/mcp-connection-status.d.ts +14 -0
- package/dist/model-policy-picker-menu-PWRI7R2X.js +14 -0
- package/dist/model-policy-picker-menu-PWRI7R2X.js.map +1 -0
- package/dist/model-policy.d.ts +19 -3
- package/dist/model-policy.js +9 -1
- package/dist/older-history.d.ts +30 -0
- package/dist/platform-activity-row-627BFZ46.js +182 -0
- package/dist/platform-activity-row-627BFZ46.js.map +1 -0
- package/dist/plugin-details.d.ts +14 -0
- package/dist/plugin-discovery.d.ts +25 -0
- package/dist/provider.d.ts +13 -1
- package/dist/{queue-surface-implementation-2KVJTYXH.js → queue-surface-implementation-ZHFI5JBP.js} +131 -44
- package/dist/queue-surface-implementation-ZHFI5JBP.js.map +1 -0
- package/dist/realtime/realtime-control.d.ts +10 -3
- package/dist/realtime.js +229 -83
- package/dist/realtime.js.map +1 -1
- package/dist/sandbox-files-BBC47SLD.js +20 -0
- package/dist/sandbox-files-BBC47SLD.js.map +1 -0
- package/dist/session-auth-recommendation.d.ts +5 -0
- package/dist/session-capability-policy.d.ts +12 -0
- package/dist/session-ui.d.ts +30 -2
- package/dist/session-ui.js +85 -7
- package/dist/session-ui.js.map +1 -1
- package/dist/session.d.ts +8 -2
- package/dist/session.js +30 -19
- package/dist/sites-ui.d.ts +34 -0
- package/dist/sites.d.ts +3 -0
- package/dist/sites.js +11 -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 +31 -0
- package/dist/terminal.js.map +1 -0
- package/dist/timeline/activity-rail.d.ts +11 -2
- package/dist/timeline/allowance-exhausted-row.d.ts +24 -0
- package/dist/timeline/compaction-copy.d.ts +10 -0
- package/dist/timeline/entrance.d.ts +13 -1
- package/dist/timeline/genie-loading.d.ts +34 -0
- package/dist/timeline/index.d.ts +6 -2
- package/dist/timeline/knowledge-receipt.d.ts +17 -0
- package/dist/timeline/platform-activity-row.d.ts +14 -0
- package/dist/timeline/presented-image.d.ts +6 -0
- package/dist/timeline/projection.d.ts +31 -2
- package/dist/timeline/registry.d.ts +2 -0
- package/dist/timeline/retained-image.d.ts +18 -0
- package/dist/timeline/rolling-activity.d.ts +11 -0
- package/dist/timeline/screenshot-lightbox.d.ts +16 -1
- package/dist/timeline/shared.d.ts +8 -3
- 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 +2 -1
- package/dist/timeline/turn-summary.d.ts +32 -3
- package/dist/timeline/types.d.ts +141 -7
- package/dist/timeline-annotation-markers-GNDHTGBD.js +165 -0
- package/dist/timeline-annotation-markers-GNDHTGBD.js.map +1 -0
- package/dist/timeline-annotation-selection-DMLMM2TX.js +241 -0
- package/dist/timeline-annotation-selection-DMLMM2TX.js.map +1 -0
- package/dist/timeline-annotations-dialog-ELRDBHTS.js +273 -0
- package/dist/timeline-annotations-dialog-ELRDBHTS.js.map +1 -0
- package/dist/types/machines.d.ts +2 -2
- package/dist/usage/allowance-copy.d.ts +25 -0
- package/dist/usage/summary.d.ts +35 -0
- package/dist/usage.d.ts +13 -0
- package/dist/usage.js +1074 -0
- package/dist/usage.js.map +1 -0
- package/package.json +44 -4
- package/src/accounts.tsx +1072 -0
- package/src/approvals.ts +8 -1
- package/src/artifacts.ts +42 -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 +22 -13
- 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 +171 -37
- package/src/components/artifacts/artifact-chrome.tsx +413 -0
- package/src/components/artifacts/artifact-sandbox.tsx +185 -0
- package/src/components/artifacts/artifact-surface.tsx +39 -19
- 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 +187 -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 +5 -2
- package/src/components/artifacts/index.ts +44 -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 +338 -5
- package/src/components/artifacts/session-artifact-viewer.tsx +376 -0
- package/src/components/artifacts/site-view.tsx +116 -0
- package/src/components/artifacts/spreadsheet-canvas.ts +5 -10
- package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
- package/src/components/artifacts/spreadsheet-grid.tsx +12 -13
- 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 +1054 -314
- package/src/components/chat-composer.tsx +131 -85
- package/src/components/child-session-link.tsx +27 -0
- package/src/components/claude-mark.tsx +10 -0
- package/src/components/code-editor.tsx +13 -16
- package/src/components/composer-transcription-control.tsx +7 -5
- package/src/components/composer.tsx +376 -49
- package/src/components/computer-viewer.tsx +1041 -329
- package/src/components/copy-button.tsx +13 -5
- package/src/components/desktop-viewer.tsx +1 -1
- package/src/components/enrollment-consent.tsx +65 -7
- package/src/components/enrollment-device-flow.tsx +1 -1
- package/src/components/file-browser.tsx +158 -34
- package/src/components/fleet-tile.tsx +8 -16
- package/src/components/generated-video-player.tsx +4 -2
- package/src/components/grok-mark.tsx +33 -0
- package/src/components/human-input-form.tsx +455 -114
- package/src/components/human-input-surface.tsx +18 -7
- package/src/components/interaction-intervention-banner.tsx +2 -2
- package/src/components/machine-card.tsx +119 -4
- package/src/components/machine-dock-bar.tsx +1 -1
- package/src/components/machine-input-display.ts +23 -2
- package/src/components/machines/machine-detail.tsx +52 -2
- package/src/components/machines/machine-operation-policy.tsx +179 -0
- package/src/components/machines-dashboard.tsx +8 -17
- package/src/components/markdown-table-layout.ts +132 -0
- package/src/components/markdown.tsx +473 -44
- package/src/components/message-timeline.tsx +2498 -663
- package/src/components/model-mark.tsx +90 -0
- package/src/components/model-picker.tsx +15 -4
- package/src/components/model-policy-picker-menu.tsx +424 -0
- package/src/components/model-policy-picker.tsx +337 -381
- package/src/components/open-geni-chat.tsx +319 -0
- package/src/components/open-geni-links.tsx +179 -0
- package/src/components/openrouter-mark.tsx +13 -0
- package/src/components/pierre-diff.tsx +41 -16
- package/src/components/pierre-file.tsx +36 -11
- package/src/components/preview-loading.tsx +154 -0
- package/src/components/queue-item-content.ts +14 -0
- package/src/components/queue-surface-implementation.tsx +132 -42
- package/src/components/queue-surface-state.tsx +3 -1
- package/src/components/sandbox-files.tsx +267 -35
- package/src/components/sandbox-terminal.tsx +48 -11
- package/src/components/sandbox-workspace.tsx +337 -135
- package/src/components/session-capability-frame.tsx +192 -0
- package/src/components/session-chrome.tsx +1027 -328
- 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 +512 -0
- package/src/components/session-list.tsx +250 -0
- package/src/components/session-mcp-capability-card.tsx +680 -0
- package/src/components/session-status.tsx +1 -1
- 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 +70 -7
- 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 +194 -49
- package/src/components/workspace-dock.tsx +141 -175
- 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 +307 -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 +235 -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/embedded-session-client.ts +104 -0
- package/src/file-node-visibility.ts +66 -0
- package/src/hooks/channel-cache.ts +104 -0
- package/src/hooks/internal.ts +3 -2
- package/src/hooks/latest-question.ts +164 -0
- package/src/hooks/shared-machines-list.ts +239 -0
- package/src/hooks/use-attached-browsers.ts +5 -1
- package/src/hooks/use-available-models.ts +20 -14
- package/src/hooks/use-browser-frame-stream.ts +25 -3
- package/src/hooks/use-browser-session.ts +271 -71
- package/src/hooks/use-browser-sessions.ts +7 -1
- package/src/hooks/use-channels.ts +32 -10
- package/src/hooks/use-codex-accounts.ts +78 -21
- package/src/hooks/use-composer.ts +1034 -320
- package/src/hooks/use-computer-frame-stream.ts +39 -16
- package/src/hooks/use-computer-input-posture.ts +94 -0
- package/src/hooks/use-computer-session.ts +110 -32
- package/src/hooks/use-computer-sessions.ts +8 -1
- package/src/hooks/use-connect.ts +23 -0
- package/src/hooks/use-desktop-stream.ts +19 -28
- package/src/hooks/use-file-attachments.ts +118 -10
- package/src/hooks/use-goal.ts +36 -9
- package/src/hooks/use-human-input.ts +31 -4
- package/src/hooks/use-last-started-turn-policy.ts +15 -13
- package/src/hooks/use-machine-chip.ts +23 -6
- package/src/hooks/use-machines.ts +104 -6
- package/src/hooks/use-sandbox-files.ts +253 -150
- package/src/hooks/use-sandbox-git.ts +58 -29
- package/src/hooks/use-session-background-commands.ts +67 -0
- package/src/hooks/use-session-capabilities.ts +21 -2
- package/src/hooks/use-session-events.ts +759 -433
- package/src/hooks/use-session-lineage.ts +28 -6
- package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
- package/src/hooks/use-session.ts +75 -16
- package/src/hooks/use-slash-commands.ts +6 -11
- package/src/hooks/use-turn-queue.ts +113 -13
- package/src/hooks/use-usage.ts +100 -0
- package/src/hooks/use-voice-input.ts +12 -1
- 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 +99 -11
- package/src/human-input.ts +12 -1
- package/src/identity-link-accounts.tsx +148 -0
- package/src/identity-link-consent.tsx +192 -0
- package/src/index.ts +157 -12
- package/src/interaction.ts +1 -0
- package/src/lib/error-message.ts +37 -0
- package/src/lib/format.ts +150 -14
- package/src/lib/host-theme.ts +511 -0
- package/src/lib/interaction-errors.ts +33 -0
- package/src/lib/menu-styles.ts +44 -0
- package/src/lib/pierre-diffs-loader.ts +53 -0
- package/src/lib/workbench-peers.ts +62 -0
- package/src/lib/xterm-theme.ts +23 -21
- package/src/machines.ts +9 -1
- package/src/mcp-connection-status.ts +35 -0
- package/src/model-policy.ts +223 -69
- package/src/older-history.ts +87 -0
- package/src/plugin-details.tsx +185 -0
- package/src/plugin-discovery.tsx +196 -0
- package/src/provider.tsx +55 -14
- package/src/realtime/dropdown-menu.tsx +19 -12
- package/src/realtime/realtime-control.tsx +274 -60
- package/src/session-auth-recommendation.ts +48 -0
- package/src/session-capability-policy.ts +83 -0
- package/src/session-context.ts +1 -0
- package/src/session-ui.ts +66 -2
- package/src/session.ts +18 -1
- package/src/sites-ui.tsx +402 -0
- package/src/sites.ts +3 -0
- package/src/skill-discovery.tsx +208 -0
- package/src/terminal.ts +26 -0
- package/src/timeline/activity-rail.tsx +171 -80
- package/src/timeline/allowance-exhausted-row.tsx +86 -0
- package/src/timeline/compaction-copy.ts +42 -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/genie-loading.tsx +136 -0
- package/src/timeline/index.ts +11 -0
- package/src/timeline/knowledge-receipt.tsx +100 -0
- package/src/timeline/parsers.ts +2 -2
- package/src/timeline/platform-activity-row.tsx +216 -0
- package/src/timeline/presented-image.ts +49 -0
- package/src/timeline/projection.ts +1600 -84
- package/src/timeline/registry.ts +15 -3
- package/src/timeline/retained-image.ts +97 -0
- package/src/timeline/rolling-activity.tsx +92 -0
- package/src/timeline/screenshot-lightbox.tsx +115 -17
- package/src/timeline/shared.tsx +47 -15
- package/src/timeline/startup-preference.ts +33 -0
- package/src/timeline/startup-timings.tsx +145 -0
- package/src/timeline/tool-display-name.ts +8 -6
- package/src/timeline/tool-renderers.tsx +497 -128
- package/src/timeline/turn-summary.tsx +208 -44
- package/src/timeline/types.ts +186 -7
- package/src/types/machines.ts +10 -0
- package/src/usage/allowance-copy.ts +64 -0
- package/src/usage/summary.ts +93 -0
- package/src/usage.ts +50 -0
- package/styles/compiled.css +2098 -757
- package/styles/connect.css +284 -0
- package/styles/connect.d.ts +1 -0
- package/styles/effective-tokens.css +1 -0
- package/styles/index.css +201 -9
- package/styles/responsive.css +27 -1
- package/styles/session-capability.css +44 -0
- package/styles/tokens.css +135 -65
- package/dist/chunk-5UQPNLIO.js.map +0 -1
- package/dist/chunk-6KLX2RH5.js.map +0 -1
- package/dist/chunk-7WCLAEFV.js.map +0 -1
- package/dist/chunk-B6WPZSTT.js.map +0 -1
- package/dist/chunk-FK6VG4LL.js +0 -90
- package/dist/chunk-FK6VG4LL.js.map +0 -1
- package/dist/chunk-HA46CYQK.js.map +0 -1
- package/dist/chunk-HUIKIDM5.js.map +0 -1
- package/dist/chunk-IGC6A2ZI.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-LLOLUHVG.js.map +0 -1
- package/dist/chunk-MKPIAGVL.js +0 -465
- package/dist/chunk-MKPIAGVL.js.map +0 -1
- package/dist/chunk-PEI6RANI.js.map +0 -1
- package/dist/chunk-R3AZOAB5.js.map +0 -1
- package/dist/chunk-WSK7G5LE.js +0 -170
- package/dist/chunk-WSK7G5LE.js.map +0 -1
- package/dist/chunk-XBZWOM2K.js +0 -218
- package/dist/chunk-XBZWOM2K.js.map +0 -1
- package/dist/chunk-Y7OEDUJ5.js +0 -8810
- package/dist/chunk-Y7OEDUJ5.js.map +0 -1
- package/dist/fleet-decision-row-GGCAT4E4.js.map +0 -1
- package/dist/hooks/use-packs.d.ts +0 -32
- package/dist/interactive-workbench-viewers-TOHBXJEE.js +0 -15
- package/dist/queue-surface-implementation-2KVJTYXH.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-packs.ts +0 -157
- /package/dist/{interactive-workbench-viewers-TOHBXJEE.js.map → browser-viewer-GTR5XQ7X.js.map} +0 -0
|
@@ -2,111 +2,120 @@ import {
|
|
|
2
2
|
buildTimeline,
|
|
3
3
|
groupTimeline,
|
|
4
4
|
sessionStatusFromEvents
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-H2NJQORO.js";
|
|
6
6
|
import {
|
|
7
7
|
useDebouncedCallback,
|
|
8
8
|
useMutationRunner,
|
|
9
9
|
usePageLiveActivity,
|
|
10
10
|
usePolledValue,
|
|
11
11
|
useSessionEventTrigger
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-YQEAA6LT.js";
|
|
13
|
+
import {
|
|
14
|
+
normalizeError,
|
|
15
|
+
useErrorMessage
|
|
16
|
+
} from "./chunk-UGLEYEMP.js";
|
|
13
17
|
import {
|
|
14
18
|
useEmbeddedFileAttachments,
|
|
15
|
-
useEmbeddedGoal,
|
|
16
19
|
useEmbeddedHumanInputSession,
|
|
17
20
|
useEmbeddedSession,
|
|
18
|
-
useEmbeddedSessionLineage,
|
|
19
|
-
useEmbeddedSessionMcpApprovalPolicy,
|
|
20
21
|
useEmbeddedSessionRead
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-NEXLLTP4.js";
|
|
22
23
|
|
|
23
|
-
// src/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
// src/human-input.ts
|
|
25
|
+
function isActionableHumanInputRequest(request, nowMs = Date.now()) {
|
|
26
|
+
if (request.status !== "pending") return false;
|
|
27
|
+
if (!request.expiresAt) return true;
|
|
28
|
+
const deadline = Date.parse(request.expiresAt);
|
|
29
|
+
return Number.isFinite(deadline) && deadline > nowMs;
|
|
27
30
|
}
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
function humanInputRequestFromEvent(event) {
|
|
32
|
+
if (event.type !== "session.humanInput.requested" || !isRecord(event.payload)) return null;
|
|
33
|
+
const request = event.payload.request;
|
|
34
|
+
if (!isRecord(request) || typeof request.id !== "string" || !Array.isArray(request.questions)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
id: request.id,
|
|
39
|
+
turnId: event.turnId ?? null,
|
|
40
|
+
questions: request.questions,
|
|
41
|
+
allowSkip: request.allowSkip === true,
|
|
42
|
+
expiresAt: typeof request.expiresAt === "string" ? request.expiresAt : null
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function projectPendingHumanInputRequests(events) {
|
|
46
|
+
const pending = /* @__PURE__ */ new Map();
|
|
47
|
+
for (const event of events) {
|
|
48
|
+
const requested = humanInputRequestFromEvent(event);
|
|
49
|
+
if (requested) {
|
|
50
|
+
pending.set(requested.id, requested);
|
|
51
|
+
continue;
|
|
36
52
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
if (enabled && workspaceControlEvent) void refresh();
|
|
47
|
-
}, [enabled, refresh, workspaceControlEvent]);
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
if (!sessionId || !enabled) return;
|
|
50
|
-
return registerSessionReconciler(sessionId, "session", refresh);
|
|
51
|
-
}, [enabled, refresh, registerSessionReconciler, sessionId]);
|
|
52
|
-
const base = data ?? null;
|
|
53
|
-
const session = useMemo(
|
|
54
|
-
() => base && override && override.id === base.id ? { ...base, title: override.title, titleSource: override.titleSource } : base,
|
|
55
|
-
[base, override]
|
|
56
|
-
);
|
|
57
|
-
const onTitleEvent = useCallback(
|
|
58
|
-
(event) => {
|
|
59
|
-
const payload = event.payload ?? {};
|
|
60
|
-
const title = payload.title;
|
|
61
|
-
if (typeof title !== "string") {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
const source = payload.source === "user" || payload.source === "agent" ? payload.source : null;
|
|
65
|
-
setOverride((current) => {
|
|
66
|
-
const next = current ?? base;
|
|
67
|
-
if (!next) {
|
|
68
|
-
return current;
|
|
53
|
+
if (event.type === "user.humanInputResponse" && isRecord(event.payload)) {
|
|
54
|
+
if (typeof event.payload.requestId === "string") pending.delete(event.payload.requestId);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (event.type === "turn.completed" || event.type === "turn.failed" || event.type === "turn.cancelled") {
|
|
58
|
+
for (const [id, request] of pending) {
|
|
59
|
+
if (request.turnId === null || event.turnId === null || event.turnId === void 0 || request.turnId === event.turnId) {
|
|
60
|
+
pending.delete(id);
|
|
69
61
|
}
|
|
70
|
-
return { ...next, title, titleSource: source };
|
|
71
|
-
});
|
|
72
|
-
},
|
|
73
|
-
[base]
|
|
74
|
-
);
|
|
75
|
-
useSessionEventTrigger(client, workspaceId, sessionId, isTitleEvent, onTitleEvent, {
|
|
76
|
-
enabled,
|
|
77
|
-
...options.events !== void 0 ? { events: options.events } : {}
|
|
78
|
-
});
|
|
79
|
-
const updateTitle = useCallback(
|
|
80
|
-
async (title) => {
|
|
81
|
-
if (!sessionId) {
|
|
82
|
-
return null;
|
|
83
62
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return [...pending.values()];
|
|
66
|
+
}
|
|
67
|
+
function isRecord(value) {
|
|
68
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/older-history.ts
|
|
72
|
+
var activeReceiptCapture;
|
|
73
|
+
var preserveTailForAutomaticLoad = false;
|
|
74
|
+
function invokeOlderHistoryLoaderWithReceiptCapture(load, capture, preserveTail = false) {
|
|
75
|
+
const previousCapture = activeReceiptCapture;
|
|
76
|
+
const previousPreserveTail = preserveTailForAutomaticLoad;
|
|
77
|
+
activeReceiptCapture = capture;
|
|
78
|
+
preserveTailForAutomaticLoad = preserveTail;
|
|
79
|
+
try {
|
|
80
|
+
return load();
|
|
81
|
+
} finally {
|
|
82
|
+
activeReceiptCapture = previousCapture;
|
|
83
|
+
preserveTailForAutomaticLoad = previousPreserveTail;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function createOlderHistoryLoadReceipt(load) {
|
|
87
|
+
let resolveResult;
|
|
88
|
+
let rejectResult;
|
|
89
|
+
const result = new Promise((resolve, reject) => {
|
|
90
|
+
resolveResult = resolve;
|
|
91
|
+
rejectResult = reject;
|
|
92
|
+
});
|
|
93
|
+
result.committed = false;
|
|
94
|
+
result.tailPreserved = false;
|
|
95
|
+
activeReceiptCapture?.(result);
|
|
96
|
+
try {
|
|
97
|
+
const loaded = load(
|
|
98
|
+
() => {
|
|
99
|
+
result.committed = true;
|
|
100
|
+
},
|
|
101
|
+
preserveTailForAutomaticLoad,
|
|
102
|
+
() => {
|
|
103
|
+
result.tailPreserved = true;
|
|
87
104
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
loading,
|
|
95
|
-
error,
|
|
96
|
-
refresh,
|
|
97
|
-
updateTitle,
|
|
98
|
-
updating: mutating,
|
|
99
|
-
mutationError,
|
|
100
|
-
clearMutationError
|
|
101
|
-
};
|
|
105
|
+
);
|
|
106
|
+
Promise.resolve(loaded).then(resolveResult, rejectResult);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
rejectResult(error);
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
102
111
|
}
|
|
103
112
|
|
|
104
113
|
// src/hooks/use-session-events.ts
|
|
105
|
-
import {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
var
|
|
114
|
+
import {
|
|
115
|
+
sessionEventStreamCoveredThrough
|
|
116
|
+
} from "@opengeni/sdk";
|
|
117
|
+
import { startTransition, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
118
|
+
var SESSION_HISTORY_PAGE_SIZE = 1e3;
|
|
110
119
|
var INITIAL_FETCH_CAP = 1;
|
|
111
120
|
var OLDER_GROUP_TARGET = 32;
|
|
112
121
|
var OLDER_FETCH_CAP = 2;
|
|
@@ -114,35 +123,16 @@ var NEWER_GROUP_TARGET = 32;
|
|
|
114
123
|
var NEWER_FETCH_CAP = 2;
|
|
115
124
|
var OLDEST_GROUP_TARGET = 32;
|
|
116
125
|
var OLDEST_FETCH_CAP = 2;
|
|
117
|
-
var BOUNDARY_PAGE_CAP =
|
|
126
|
+
var BOUNDARY_PAGE_CAP = 1;
|
|
127
|
+
var FOREGROUND_DIRECT_REPLAY_MAX_SEQUENCES = 16;
|
|
128
|
+
var FOREGROUND_COMPACT_PROBE_MAX_SEQUENCES = SESSION_HISTORY_PAGE_SIZE;
|
|
129
|
+
var FOREGROUND_COMPACT_CATCHUP_MAX_EVENTS = 128;
|
|
130
|
+
var FOREGROUND_COMPACT_CATCHUP_MAX_GROUPS = 16;
|
|
131
|
+
var FOREGROUND_COMPACT_CATCHUP_MAX_BYTES = 512 * 1024;
|
|
118
132
|
var EMPTY_EVENTS = [];
|
|
119
133
|
var encoder = new TextEncoder();
|
|
120
|
-
var
|
|
121
|
-
var
|
|
122
|
-
var BROWSER_EVENT_ID_MAX_BYTES = 256;
|
|
123
|
-
var BROWSER_EVENT_CLIENT_ID_MAX_BYTES = 4 * 1024;
|
|
124
|
-
var BROWSER_EVENT_DUPLICATE_REASON_MAX_BYTES = 4 * 1024;
|
|
125
|
-
var BROWSER_EVENT_PAYLOAD_PREVIEW_MAX_BYTES = 48 * 1024;
|
|
126
|
-
var BROWSER_EVENT_PAYLOAD_IDENTITY_FIELDS = [
|
|
127
|
-
"id",
|
|
128
|
-
"callId",
|
|
129
|
-
"call_id",
|
|
130
|
-
"name",
|
|
131
|
-
"toolName",
|
|
132
|
-
"status",
|
|
133
|
-
"code",
|
|
134
|
-
"isError",
|
|
135
|
-
"stream",
|
|
136
|
-
"commandId",
|
|
137
|
-
"sequence",
|
|
138
|
-
"coalescedUntil",
|
|
139
|
-
"coalescedCount",
|
|
140
|
-
"firstSequence",
|
|
141
|
-
"lastSequence"
|
|
142
|
-
];
|
|
143
|
-
var SESSION_EVENT_BROWSER_MAX_BYTES = 8 * 1024 * 1024;
|
|
144
|
-
var SESSION_EVENT_BROWSER_MAX_COUNT = 1e4;
|
|
145
|
-
var SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES = 96 * 1024;
|
|
134
|
+
var SESSION_EVENT_BROWSER_MAX_BYTES = 16 * 1024 * 1024;
|
|
135
|
+
var SESSION_EVENT_BROWSER_MAX_COUNT = 2e4;
|
|
146
136
|
var SESSION_EVENT_BROWSER_PENDING_MAX_BYTES = 1024 * 1024;
|
|
147
137
|
var SESSION_EVENT_BROWSER_PENDING_MAX_COUNT = 256;
|
|
148
138
|
var EMPTY_EVENT_WINDOW = {
|
|
@@ -159,21 +149,35 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
159
149
|
const replay = options.replay ?? "windowed";
|
|
160
150
|
const fullReplay = replay === "full" || after !== 0;
|
|
161
151
|
const streamKey = `${workspaceId}\0${sessionId ?? ""}\0${after}\0${fullReplay ? "full" : "windowed"}`;
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
const
|
|
152
|
+
const navigationIdentityRef = useRef({ client, streamKey, enabled });
|
|
153
|
+
if (navigationIdentityRef.current.client !== client || navigationIdentityRef.current.streamKey !== streamKey || navigationIdentityRef.current.enabled !== enabled) {
|
|
154
|
+
navigationIdentityRef.current = { client, streamKey, enabled };
|
|
155
|
+
}
|
|
156
|
+
const navigationIdentity = navigationIdentityRef.current;
|
|
157
|
+
const ownsNavigation = useCallback(
|
|
158
|
+
() => navigationIdentityRef.current === navigationIdentity,
|
|
159
|
+
[navigationIdentity]
|
|
160
|
+
);
|
|
161
|
+
const [eventWindow, setEventWindow] = useState(EMPTY_EVENT_WINDOW);
|
|
162
|
+
const [questionEvidence, setQuestionEvidence] = useState(null);
|
|
163
|
+
const [connectionState, setConnectionState] = useState("idle");
|
|
164
|
+
const [error, setError] = useState(null);
|
|
165
|
+
const [newerError, setNewerError] = useState(null);
|
|
166
|
+
const [hasOlder, setHasOlder] = useState(false);
|
|
167
|
+
const [hasNewer, setHasNewer] = useState(false);
|
|
168
|
+
const [sessionStatusProjection, setSessionStatusProjection] = useState(
|
|
168
169
|
null
|
|
169
170
|
);
|
|
170
|
-
const [initialLoading, setInitialLoading] =
|
|
171
|
-
const [
|
|
172
|
-
const [
|
|
173
|
-
const [
|
|
174
|
-
const [
|
|
175
|
-
const [
|
|
176
|
-
const [
|
|
171
|
+
const [initialLoading, setInitialLoading] = useState(true);
|
|
172
|
+
const [initialHistoryReady, setInitialHistoryReady] = useState(false);
|
|
173
|
+
const [loadingOlder, setLoadingOlder] = useState(false);
|
|
174
|
+
const [loadingNewer, setLoadingNewer] = useState(false);
|
|
175
|
+
const [loadingOldest, setLoadingOldest] = useState(false);
|
|
176
|
+
const [loadingLatest, setLoadingLatest] = useState(false);
|
|
177
|
+
const [loadingTarget, setLoadingTarget] = useState(false);
|
|
178
|
+
const loadingTargetRef = useRef(false);
|
|
179
|
+
const [streamEpoch, setStreamEpoch] = useState(0);
|
|
180
|
+
const [viewMode, setViewMode] = useState("live");
|
|
177
181
|
const lastSequenceRef = useRef(after);
|
|
178
182
|
const streamResumeSequenceRef = useRef(after);
|
|
179
183
|
const oldestSequenceRef = useRef(null);
|
|
@@ -186,16 +190,34 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
186
190
|
const loadingLatestRef = useRef(false);
|
|
187
191
|
const viewModeRef = useRef("live");
|
|
188
192
|
const initialWindowLoadedRef = useRef(false);
|
|
193
|
+
const reconcileAfterPageResumeRef = useRef(false);
|
|
189
194
|
const streamAbortRef = useRef(null);
|
|
190
195
|
const streamKeyRef = useRef(null);
|
|
191
196
|
const generationRef = useRef(0);
|
|
197
|
+
const navigationGenerationRef = useRef(0);
|
|
192
198
|
const eventWindowRef = useRef(EMPTY_EVENT_WINDOW);
|
|
193
199
|
const sessionStatusRef = useRef({
|
|
194
200
|
sequence: after,
|
|
195
201
|
status: null
|
|
196
202
|
});
|
|
197
|
-
const [stateStreamKey, setStateStreamKey] =
|
|
198
|
-
|
|
203
|
+
const [stateStreamKey, setStateStreamKey] = useState(streamKey);
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
navigationGenerationRef.current += 1;
|
|
206
|
+
setNewerError(null);
|
|
207
|
+
loadingOlderRef.current = false;
|
|
208
|
+
loadingNewerRef.current = false;
|
|
209
|
+
loadingOldestRef.current = false;
|
|
210
|
+
loadingTargetRef.current = false;
|
|
211
|
+
setQuestionEvidence(null);
|
|
212
|
+
setLoadingTarget(false);
|
|
213
|
+
setLoadingOlder(false);
|
|
214
|
+
setLoadingNewer(false);
|
|
215
|
+
setLoadingOldest(false);
|
|
216
|
+
return () => {
|
|
217
|
+
navigationGenerationRef.current += 1;
|
|
218
|
+
};
|
|
219
|
+
}, [client, workspaceId, sessionId, after, enabled, fullReplay]);
|
|
220
|
+
useEffect(() => {
|
|
199
221
|
if (streamKeyRef.current !== streamKey) {
|
|
200
222
|
streamKeyRef.current = streamKey;
|
|
201
223
|
setStateStreamKey(streamKey);
|
|
@@ -211,6 +233,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
211
233
|
setLoadingOldest(false);
|
|
212
234
|
setLoadingLatest(false);
|
|
213
235
|
setInitialLoading(true);
|
|
236
|
+
setInitialHistoryReady(false);
|
|
214
237
|
lastSequenceRef.current = after;
|
|
215
238
|
streamResumeSequenceRef.current = after;
|
|
216
239
|
oldestSequenceRef.current = null;
|
|
@@ -224,24 +247,27 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
224
247
|
viewModeRef.current = "live";
|
|
225
248
|
setViewMode("live");
|
|
226
249
|
initialWindowLoadedRef.current = false;
|
|
250
|
+
reconcileAfterPageResumeRef.current = false;
|
|
227
251
|
}
|
|
228
252
|
const generation = generationRef.current + 1;
|
|
229
253
|
generationRef.current = generation;
|
|
230
|
-
if (loadingOlderRef.current) {
|
|
231
|
-
loadingOlderRef.current = false;
|
|
232
|
-
setLoadingOlder(false);
|
|
233
|
-
}
|
|
234
254
|
if (!sessionId || !streamEnabled) {
|
|
255
|
+
if (sessionId && enabled && !pageLive && !fullReplay && viewModeRef.current === "live" && initialWindowLoadedRef.current) {
|
|
256
|
+
reconcileAfterPageResumeRef.current = true;
|
|
257
|
+
}
|
|
235
258
|
setConnectionState("idle");
|
|
236
259
|
return;
|
|
237
260
|
}
|
|
238
261
|
if (viewMode === "history") {
|
|
262
|
+
reconcileAfterPageResumeRef.current = false;
|
|
239
263
|
setConnectionState("idle");
|
|
240
264
|
setInitialLoading(false);
|
|
241
265
|
return;
|
|
242
266
|
}
|
|
267
|
+
const reconcileForegroundResume = reconcileAfterPageResumeRef.current && !fullReplay;
|
|
268
|
+
reconcileAfterPageResumeRef.current = false;
|
|
243
269
|
const controller = new AbortController();
|
|
244
|
-
const isCurrent = () => generationRef.current === generation && !controller.signal.aborted;
|
|
270
|
+
const isCurrent = () => generationRef.current === generation && !controller.signal.aborted && navigationIdentityRef.current.client === client && navigationIdentityRef.current.streamKey === streamKey && navigationIdentityRef.current.enabled === enabled;
|
|
245
271
|
streamAbortRef.current = controller;
|
|
246
272
|
let pending = [];
|
|
247
273
|
let pendingBytes = 2;
|
|
@@ -271,9 +297,12 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
271
297
|
batchResumeSequence
|
|
272
298
|
);
|
|
273
299
|
}
|
|
274
|
-
observeSessionStatus(batch, sessionStatusRef
|
|
300
|
+
const status = observeSessionStatus(batch, sessionStatusRef);
|
|
301
|
+
if (status !== void 0) {
|
|
302
|
+
setSessionStatusProjection(status);
|
|
303
|
+
}
|
|
275
304
|
const current = eventWindowRef.current;
|
|
276
|
-
const next =
|
|
305
|
+
const next = appendBrowserSessionEventWindow(current, batch);
|
|
277
306
|
const retained = {
|
|
278
307
|
...next,
|
|
279
308
|
truncated: current.truncated || next.truncated
|
|
@@ -281,7 +310,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
281
310
|
eventWindowRef.current = retained;
|
|
282
311
|
setEventWindow(retained);
|
|
283
312
|
oldestSequenceRef.current = retained.events[0]?.sequence ?? null;
|
|
284
|
-
newestSequenceRef.current = retained.events
|
|
313
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
|
|
285
314
|
if (retained.truncated) {
|
|
286
315
|
hasOlderRef.current = true;
|
|
287
316
|
setHasOlder(true);
|
|
@@ -293,29 +322,85 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
293
322
|
};
|
|
294
323
|
void (async () => {
|
|
295
324
|
try {
|
|
325
|
+
if (reconcileForegroundResume) {
|
|
326
|
+
setConnectionState("connecting");
|
|
327
|
+
const cursor = streamResumeSequenceRef.current;
|
|
328
|
+
let plan = { kind: "resume" };
|
|
329
|
+
try {
|
|
330
|
+
plan = await planForegroundCatchup(client, workspaceId, sessionId, cursor, {
|
|
331
|
+
signal: controller.signal
|
|
332
|
+
});
|
|
333
|
+
} catch {
|
|
334
|
+
if (controller.signal.aborted) return;
|
|
335
|
+
plan = { kind: "resume" };
|
|
336
|
+
}
|
|
337
|
+
if (!isCurrent()) return;
|
|
338
|
+
if (plan.kind === "append") {
|
|
339
|
+
const current = eventWindowRef.current;
|
|
340
|
+
assertAppendOrder(current.events, plan.events);
|
|
341
|
+
const status = observeSessionStatus(plan.events, sessionStatusRef);
|
|
342
|
+
const next = appendBrowserSessionEventWindow(current, plan.events);
|
|
343
|
+
const retained = {
|
|
344
|
+
...next,
|
|
345
|
+
truncated: current.truncated || next.truncated
|
|
346
|
+
};
|
|
347
|
+
eventWindowRef.current = retained;
|
|
348
|
+
oldestSequenceRef.current = retained.events[0]?.sequence ?? null;
|
|
349
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
|
|
350
|
+
lastSequenceRef.current = Math.max(lastSequenceRef.current, plan.resumeSequence);
|
|
351
|
+
streamResumeSequenceRef.current = Math.max(
|
|
352
|
+
streamResumeSequenceRef.current,
|
|
353
|
+
plan.resumeSequence
|
|
354
|
+
);
|
|
355
|
+
if (status !== void 0) {
|
|
356
|
+
setSessionStatusProjection(status);
|
|
357
|
+
}
|
|
358
|
+
setEventWindow(retained);
|
|
359
|
+
if (retained.truncated) {
|
|
360
|
+
hasOlderRef.current = true;
|
|
361
|
+
setHasOlder(true);
|
|
362
|
+
}
|
|
363
|
+
} else if (plan.kind === "reload") {
|
|
364
|
+
initialWindowLoadedRef.current = false;
|
|
365
|
+
setError(null);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
296
368
|
if (!fullReplay && !initialWindowLoadedRef.current) {
|
|
297
369
|
setConnectionState("connecting");
|
|
298
370
|
const window = await loadEventWindow(client, workspaceId, sessionId, {
|
|
299
371
|
before: Number.MAX_SAFE_INTEGER,
|
|
300
|
-
pageSize:
|
|
372
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
301
373
|
targetGroups: Number.POSITIVE_INFINITY,
|
|
302
374
|
maxFetches: INITIAL_FETCH_CAP,
|
|
375
|
+
boundaryPageCap: BOUNDARY_PAGE_CAP,
|
|
303
376
|
signal: controller.signal
|
|
304
377
|
});
|
|
305
378
|
if (!isCurrent()) {
|
|
306
379
|
return;
|
|
307
380
|
}
|
|
308
|
-
observeSessionStatus(window.events, sessionStatusRef
|
|
381
|
+
const status = observeSessionStatus(window.events, sessionStatusRef);
|
|
309
382
|
const retained = boundBrowserSessionEventWindow(window.events);
|
|
383
|
+
navigationGenerationRef.current += 1;
|
|
384
|
+
loadingOlderRef.current = false;
|
|
385
|
+
loadingNewerRef.current = false;
|
|
386
|
+
loadingOldestRef.current = false;
|
|
387
|
+
setLoadingOlder(false);
|
|
388
|
+
setLoadingNewer(false);
|
|
389
|
+
setLoadingOldest(false);
|
|
310
390
|
eventWindowRef.current = retained;
|
|
311
|
-
setEventWindow(retained);
|
|
312
391
|
oldestSequenceRef.current = retained.events[0]?.sequence ?? window.oldestSequence;
|
|
313
|
-
newestSequenceRef.current = retained.events
|
|
392
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events) ?? window.newestSequence;
|
|
314
393
|
hasOlderRef.current = window.hasOlder || retained.truncated;
|
|
315
394
|
hasNewerRef.current = false;
|
|
316
395
|
lastSequenceRef.current = window.newestSequence;
|
|
317
396
|
streamResumeSequenceRef.current = window.newestSequence;
|
|
318
397
|
initialWindowLoadedRef.current = true;
|
|
398
|
+
setInitialHistoryReady(true);
|
|
399
|
+
setError(null);
|
|
400
|
+
if (status !== void 0) {
|
|
401
|
+
setSessionStatusProjection(status);
|
|
402
|
+
}
|
|
403
|
+
setEventWindow(retained);
|
|
319
404
|
setHasOlder(window.hasOlder || retained.truncated);
|
|
320
405
|
setHasNewer(false);
|
|
321
406
|
setInitialLoading(false);
|
|
@@ -329,7 +414,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
329
414
|
onOpen: () => {
|
|
330
415
|
void Promise.resolve().then(() => reconcileSession(sessionId)).catch((cause) => {
|
|
331
416
|
if (isCurrent()) {
|
|
332
|
-
setError(
|
|
417
|
+
setError(normalizeError(cause));
|
|
333
418
|
}
|
|
334
419
|
});
|
|
335
420
|
},
|
|
@@ -341,7 +426,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
341
426
|
});
|
|
342
427
|
for await (const event of stream) {
|
|
343
428
|
if (!isCurrent()) break;
|
|
344
|
-
const boundedEvent =
|
|
429
|
+
const boundedEvent = event;
|
|
345
430
|
const boundedEventBytes = browserJsonBytes(boundedEvent);
|
|
346
431
|
const separatorBytes = pending.length === 0 ? 0 : 1;
|
|
347
432
|
if (pending.length > 0 && (pending.length >= SESSION_EVENT_BROWSER_PENDING_MAX_COUNT || pendingBytes + separatorBytes + boundedEventBytes > SESSION_EVENT_BROWSER_PENDING_MAX_BYTES)) {
|
|
@@ -362,8 +447,9 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
362
447
|
} catch (cause) {
|
|
363
448
|
if (isCurrent()) {
|
|
364
449
|
flush();
|
|
365
|
-
setError(
|
|
450
|
+
setError(normalizeError(cause));
|
|
366
451
|
setConnectionState("error");
|
|
452
|
+
setInitialLoading(false);
|
|
367
453
|
}
|
|
368
454
|
}
|
|
369
455
|
})();
|
|
@@ -384,6 +470,8 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
384
470
|
workspaceId,
|
|
385
471
|
sessionId,
|
|
386
472
|
after,
|
|
473
|
+
enabled,
|
|
474
|
+
pageLive,
|
|
387
475
|
streamEnabled,
|
|
388
476
|
fullReplay,
|
|
389
477
|
streamKey,
|
|
@@ -391,89 +479,134 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
391
479
|
viewMode,
|
|
392
480
|
reconcileSession
|
|
393
481
|
]);
|
|
394
|
-
const navigationBusy = () => loadingOlderRef.current || loadingNewerRef.current || loadingOldestRef.current || loadingLatestRef.current;
|
|
395
|
-
const loadOlder =
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}
|
|
399
|
-
const before = oldestSequenceRef.current;
|
|
400
|
-
if (before === null) {
|
|
401
|
-
hasOlderRef.current = false;
|
|
402
|
-
setHasOlder(false);
|
|
403
|
-
return false;
|
|
404
|
-
}
|
|
405
|
-
const generation = generationRef.current;
|
|
406
|
-
loadingOlderRef.current = true;
|
|
407
|
-
setLoadingOlder(true);
|
|
408
|
-
try {
|
|
409
|
-
const window = await loadEventWindow(client, workspaceId, sessionId, {
|
|
410
|
-
before,
|
|
411
|
-
pageSize: OLDER_PAGE_SIZE,
|
|
412
|
-
targetGroups: OLDER_GROUP_TARGET,
|
|
413
|
-
maxFetches: OLDER_FETCH_CAP
|
|
414
|
-
});
|
|
415
|
-
if (generationRef.current !== generation) {
|
|
482
|
+
const navigationBusy = () => loadingTargetRef.current || loadingOlderRef.current || loadingNewerRef.current || loadingOldestRef.current || loadingLatestRef.current;
|
|
483
|
+
const loadOlder = useCallback(
|
|
484
|
+
() => createOlderHistoryLoadReceipt(async (markCommitted, preserveTail, markTailPreserved) => {
|
|
485
|
+
if (!ownsNavigation() || !sessionId || navigationBusy() || !hasOlderRef.current) {
|
|
416
486
|
return false;
|
|
417
487
|
}
|
|
418
|
-
|
|
488
|
+
const before = oldestSequenceRef.current;
|
|
489
|
+
if (before === null) {
|
|
419
490
|
oldestSequenceRef.current = null;
|
|
420
491
|
hasOlderRef.current = false;
|
|
421
492
|
setHasOlder(false);
|
|
422
493
|
return false;
|
|
423
494
|
}
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
495
|
+
const generation = navigationGenerationRef.current;
|
|
496
|
+
const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
|
|
497
|
+
loadingOlderRef.current = true;
|
|
498
|
+
setLoadingOlder(true);
|
|
499
|
+
let published = false;
|
|
500
|
+
try {
|
|
501
|
+
const window = await loadEventWindow(client, workspaceId, sessionId, {
|
|
502
|
+
before,
|
|
503
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
504
|
+
targetGroups: OLDER_GROUP_TARGET,
|
|
505
|
+
maxFetches: OLDER_FETCH_CAP,
|
|
506
|
+
isCurrent
|
|
507
|
+
});
|
|
508
|
+
if (!isCurrent()) {
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
if (window.events.length === 0) {
|
|
512
|
+
oldestSequenceRef.current = null;
|
|
513
|
+
hasOlderRef.current = false;
|
|
514
|
+
setHasOlder(false);
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
const current = eventWindowRef.current;
|
|
518
|
+
assertPrependOrder(current.events, window.events);
|
|
519
|
+
const next = boundBrowserSessionEventWindow([...window.events, ...current.events], {
|
|
520
|
+
direction: "oldest"
|
|
521
|
+
});
|
|
522
|
+
if (preserveTail && maxResumeSequence(next.events) < maxResumeSequence(current.events)) {
|
|
523
|
+
markTailPreserved();
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
streamAbortRef.current?.abort();
|
|
527
|
+
const status = observeSessionStatus(window.events, sessionStatusRef);
|
|
528
|
+
const retained = {
|
|
529
|
+
...next,
|
|
530
|
+
truncated: current.truncated || next.truncated
|
|
531
|
+
};
|
|
532
|
+
const retainedOldest = retained.events[0]?.sequence ?? null;
|
|
533
|
+
if (retainedOldest === null || retainedOldest >= before) {
|
|
534
|
+
throw new Error("@opengeni/react: loadOlder made no durable sequence progress");
|
|
535
|
+
}
|
|
536
|
+
const retainedNewest = maxResumeSequenceOrNull(retained.events);
|
|
537
|
+
const previousNewest = maxResumeSequenceOrNull(current.events);
|
|
538
|
+
eventWindowRef.current = retained;
|
|
539
|
+
markCommitted();
|
|
540
|
+
oldestSequenceRef.current = retainedOldest;
|
|
541
|
+
newestSequenceRef.current = retainedNewest;
|
|
542
|
+
streamResumeSequenceRef.current = maxResumeSequence(retained.events);
|
|
543
|
+
const olderStillAvailable = window.hasOlder;
|
|
544
|
+
hasOlderRef.current = olderStillAvailable;
|
|
545
|
+
const evictedLiveTail = previousNewest !== null && retainedNewest !== null && retainedNewest < previousNewest;
|
|
546
|
+
let newer = null;
|
|
547
|
+
let enterHistory = false;
|
|
548
|
+
let reconnectLive = false;
|
|
549
|
+
if (viewModeRef.current === "history" || evictedLiveTail) {
|
|
550
|
+
const highWater = lastSequenceRef.current;
|
|
551
|
+
newer = retainedNewest !== null && (retainedNewest < highWater || retained.truncated);
|
|
552
|
+
hasNewerRef.current = newer;
|
|
553
|
+
if (evictedLiveTail) {
|
|
554
|
+
viewModeRef.current = "history";
|
|
555
|
+
enterHistory = true;
|
|
556
|
+
}
|
|
557
|
+
} else {
|
|
558
|
+
reconnectLive = true;
|
|
559
|
+
}
|
|
560
|
+
loadingOlderRef.current = false;
|
|
561
|
+
startTransition(() => {
|
|
562
|
+
if (status !== void 0) {
|
|
563
|
+
setSessionStatusProjection(status);
|
|
564
|
+
}
|
|
565
|
+
setEventWindow(retained);
|
|
566
|
+
setHasOlder(olderStillAvailable);
|
|
567
|
+
if (newer !== null) {
|
|
568
|
+
setHasNewer(newer);
|
|
569
|
+
}
|
|
570
|
+
if (enterHistory) {
|
|
571
|
+
setViewMode("history");
|
|
572
|
+
} else if (reconnectLive) {
|
|
573
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
574
|
+
}
|
|
575
|
+
setLoadingOlder(false);
|
|
576
|
+
});
|
|
577
|
+
published = true;
|
|
578
|
+
return olderStillAvailable;
|
|
579
|
+
} catch (reason) {
|
|
580
|
+
if (!isCurrent()) return false;
|
|
581
|
+
throw reason;
|
|
582
|
+
} finally {
|
|
583
|
+
if (!published && isCurrent()) {
|
|
584
|
+
loadingOlderRef.current = false;
|
|
585
|
+
setLoadingOlder(false);
|
|
586
|
+
}
|
|
455
587
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}, [client, workspaceId, sessionId]);
|
|
462
|
-
const loadOldest = useCallback2(async () => {
|
|
463
|
-
if (!sessionId || navigationBusy() || !hasOlderRef.current) {
|
|
588
|
+
}),
|
|
589
|
+
[client, workspaceId, sessionId, ownsNavigation]
|
|
590
|
+
);
|
|
591
|
+
const loadOldest = useCallback(async () => {
|
|
592
|
+
if (!ownsNavigation() || !sessionId || navigationBusy() || !hasOlderRef.current) {
|
|
464
593
|
return false;
|
|
465
594
|
}
|
|
466
|
-
const generation =
|
|
595
|
+
const generation = ++navigationGenerationRef.current;
|
|
596
|
+
const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
|
|
597
|
+
setQuestionEvidence(null);
|
|
467
598
|
loadingOldestRef.current = true;
|
|
468
599
|
setLoadingOldest(true);
|
|
600
|
+
let published = false;
|
|
469
601
|
try {
|
|
470
602
|
const window = await loadForwardEventWindow(client, workspaceId, sessionId, {
|
|
471
603
|
after: 0,
|
|
472
|
-
pageSize:
|
|
604
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
473
605
|
targetGroups: OLDEST_GROUP_TARGET,
|
|
474
|
-
maxFetches: OLDEST_FETCH_CAP
|
|
606
|
+
maxFetches: OLDEST_FETCH_CAP,
|
|
607
|
+
isCurrent
|
|
475
608
|
});
|
|
476
|
-
if (
|
|
609
|
+
if (!isCurrent()) {
|
|
477
610
|
return false;
|
|
478
611
|
}
|
|
479
612
|
if (window.events.length === 0) {
|
|
@@ -482,32 +615,48 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
482
615
|
return false;
|
|
483
616
|
}
|
|
484
617
|
streamAbortRef.current?.abort();
|
|
485
|
-
observeSessionStatus(window.events, sessionStatusRef
|
|
618
|
+
const status = observeSessionStatus(window.events, sessionStatusRef);
|
|
486
619
|
const retained = boundBrowserSessionEventWindow(window.events, { direction: "oldest" });
|
|
487
620
|
const retainedOldest = retained.events[0]?.sequence ?? null;
|
|
488
|
-
const retainedNewest = retained.events
|
|
621
|
+
const retainedNewest = maxResumeSequenceOrNull(retained.events);
|
|
489
622
|
eventWindowRef.current = retained;
|
|
490
|
-
setEventWindow(retained);
|
|
491
623
|
oldestSequenceRef.current = retainedOldest;
|
|
492
624
|
newestSequenceRef.current = retainedNewest;
|
|
493
625
|
hasOlderRef.current = false;
|
|
494
|
-
setHasOlder(false);
|
|
495
626
|
const highWater = Math.max(lastSequenceRef.current, retainedNewest ?? 0);
|
|
496
627
|
lastSequenceRef.current = highWater;
|
|
497
628
|
const newer = window.hasNewer || retained.truncated || retainedNewest !== null && retainedNewest < highWater;
|
|
498
629
|
hasNewerRef.current = newer;
|
|
499
|
-
setHasNewer(newer);
|
|
500
630
|
initialWindowLoadedRef.current = true;
|
|
631
|
+
setInitialHistoryReady(true);
|
|
632
|
+
setError(null);
|
|
501
633
|
viewModeRef.current = "history";
|
|
502
|
-
|
|
634
|
+
loadingOldestRef.current = false;
|
|
635
|
+
setNewerError(null);
|
|
636
|
+
startTransition(() => {
|
|
637
|
+
if (status !== void 0) {
|
|
638
|
+
setSessionStatusProjection(status);
|
|
639
|
+
}
|
|
640
|
+
setEventWindow(retained);
|
|
641
|
+
setHasOlder(false);
|
|
642
|
+
setHasNewer(newer);
|
|
643
|
+
setViewMode("history");
|
|
644
|
+
setLoadingOldest(false);
|
|
645
|
+
});
|
|
646
|
+
published = true;
|
|
503
647
|
return newer;
|
|
648
|
+
} catch (reason) {
|
|
649
|
+
if (!isCurrent()) return false;
|
|
650
|
+
throw reason;
|
|
504
651
|
} finally {
|
|
505
|
-
|
|
506
|
-
|
|
652
|
+
if (!published && isCurrent()) {
|
|
653
|
+
loadingOldestRef.current = false;
|
|
654
|
+
setLoadingOldest(false);
|
|
655
|
+
}
|
|
507
656
|
}
|
|
508
|
-
}, [client, workspaceId, sessionId]);
|
|
509
|
-
const loadNewer =
|
|
510
|
-
if (!sessionId || navigationBusy() || !hasNewerRef.current) {
|
|
657
|
+
}, [client, workspaceId, sessionId, ownsNavigation]);
|
|
658
|
+
const loadNewer = useCallback(async () => {
|
|
659
|
+
if (!ownsNavigation() || !sessionId || navigationBusy() || !hasNewerRef.current) {
|
|
511
660
|
return false;
|
|
512
661
|
}
|
|
513
662
|
const afterSequence = newestSequenceRef.current;
|
|
@@ -516,20 +665,24 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
516
665
|
setHasNewer(false);
|
|
517
666
|
return false;
|
|
518
667
|
}
|
|
519
|
-
const generation =
|
|
668
|
+
const generation = navigationGenerationRef.current;
|
|
669
|
+
const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
|
|
520
670
|
loadingNewerRef.current = true;
|
|
521
671
|
setLoadingNewer(true);
|
|
672
|
+
let published = false;
|
|
522
673
|
try {
|
|
523
674
|
const window = await loadForwardEventWindow(client, workspaceId, sessionId, {
|
|
524
675
|
after: afterSequence,
|
|
525
|
-
pageSize:
|
|
676
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
526
677
|
targetGroups: NEWER_GROUP_TARGET,
|
|
527
|
-
maxFetches: NEWER_FETCH_CAP
|
|
678
|
+
maxFetches: NEWER_FETCH_CAP,
|
|
679
|
+
isCurrent
|
|
528
680
|
});
|
|
529
|
-
if (
|
|
681
|
+
if (!isCurrent()) {
|
|
530
682
|
return false;
|
|
531
683
|
}
|
|
532
684
|
if (window.events.length === 0) {
|
|
685
|
+
setNewerError(null);
|
|
533
686
|
hasNewerRef.current = false;
|
|
534
687
|
setHasNewer(false);
|
|
535
688
|
streamResumeSequenceRef.current = afterSequence;
|
|
@@ -540,85 +693,246 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
540
693
|
}
|
|
541
694
|
const current = eventWindowRef.current;
|
|
542
695
|
assertAppendOrder(current.events, window.events);
|
|
543
|
-
observeSessionStatus(window.events, sessionStatusRef
|
|
696
|
+
const status = observeSessionStatus(window.events, sessionStatusRef);
|
|
544
697
|
const previousOldest = current.events[0]?.sequence ?? null;
|
|
545
|
-
const next =
|
|
546
|
-
direction: "newest"
|
|
547
|
-
});
|
|
698
|
+
const next = appendBrowserSessionEventWindow(current, window.events);
|
|
548
699
|
const retained = {
|
|
549
700
|
...next,
|
|
550
701
|
truncated: current.truncated || next.truncated || window.hasNewer
|
|
551
702
|
};
|
|
552
703
|
const retainedOldest = retained.events[0]?.sequence ?? null;
|
|
553
|
-
const retainedNewest = retained.events
|
|
704
|
+
const retainedNewest = maxResumeSequenceOrNull(retained.events);
|
|
554
705
|
if (retainedNewest === null || retainedNewest <= afterSequence) {
|
|
555
706
|
throw new Error("@opengeni/react: loadNewer made no durable sequence progress");
|
|
556
707
|
}
|
|
557
708
|
eventWindowRef.current = retained;
|
|
558
|
-
setEventWindow(retained);
|
|
559
709
|
oldestSequenceRef.current = retainedOldest;
|
|
560
710
|
newestSequenceRef.current = retainedNewest;
|
|
561
711
|
if (retainedOldest !== null && (previousOldest === null || retainedOldest > previousOldest || retained.truncated && retained.events[0]?.type !== "session.created" && retainedOldest > 1)) {
|
|
562
712
|
hasOlderRef.current = true;
|
|
563
|
-
setHasOlder(true);
|
|
564
713
|
}
|
|
714
|
+
const rearmedOlder = hasOlderRef.current;
|
|
565
715
|
const highWater = Math.max(lastSequenceRef.current, retainedNewest);
|
|
566
716
|
lastSequenceRef.current = highWater;
|
|
567
717
|
const newer = window.hasNewer || retainedNewest < highWater;
|
|
568
718
|
hasNewerRef.current = newer;
|
|
569
|
-
|
|
719
|
+
let resumeLive = false;
|
|
570
720
|
if (!newer) {
|
|
571
721
|
streamResumeSequenceRef.current = retainedNewest;
|
|
572
722
|
viewModeRef.current = "live";
|
|
573
|
-
|
|
574
|
-
setStreamEpoch((epoch) => epoch + 1);
|
|
723
|
+
resumeLive = true;
|
|
575
724
|
}
|
|
725
|
+
loadingNewerRef.current = false;
|
|
726
|
+
setNewerError(null);
|
|
727
|
+
startTransition(() => {
|
|
728
|
+
if (status !== void 0) {
|
|
729
|
+
setSessionStatusProjection(status);
|
|
730
|
+
}
|
|
731
|
+
setEventWindow(retained);
|
|
732
|
+
setHasOlder(rearmedOlder);
|
|
733
|
+
setHasNewer(newer);
|
|
734
|
+
if (resumeLive) {
|
|
735
|
+
setViewMode("live");
|
|
736
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
737
|
+
}
|
|
738
|
+
setLoadingNewer(false);
|
|
739
|
+
});
|
|
740
|
+
published = true;
|
|
576
741
|
return newer;
|
|
742
|
+
} catch (reason) {
|
|
743
|
+
if (!isCurrent()) {
|
|
744
|
+
return false;
|
|
745
|
+
}
|
|
746
|
+
setNewerError(normalizeError(reason));
|
|
747
|
+
throw reason;
|
|
577
748
|
} finally {
|
|
578
|
-
|
|
749
|
+
if (!published && isCurrent()) {
|
|
750
|
+
loadingNewerRef.current = false;
|
|
751
|
+
setLoadingNewer(false);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}, [client, workspaceId, sessionId, ownsNavigation]);
|
|
755
|
+
const jumpToSequence = useCallback(
|
|
756
|
+
async (sequence, navigationOptions) => {
|
|
757
|
+
const signal = navigationOptions?.signal;
|
|
758
|
+
if (!ownsNavigation() || !sessionId || !Number.isSafeInteger(sequence) || sequence < 1)
|
|
759
|
+
return false;
|
|
760
|
+
if (signal?.aborted) return false;
|
|
761
|
+
const generation = ++navigationGenerationRef.current;
|
|
762
|
+
setQuestionEvidence(null);
|
|
763
|
+
const current = () => generation === navigationGenerationRef.current && ownsNavigation();
|
|
764
|
+
const previousMode = viewModeRef.current;
|
|
765
|
+
streamAbortRef.current?.abort();
|
|
766
|
+
generationRef.current += 1;
|
|
767
|
+
viewModeRef.current = "history";
|
|
768
|
+
setViewMode("history");
|
|
769
|
+
loadingOlderRef.current = loadingNewerRef.current = loadingOldestRef.current = false;
|
|
770
|
+
setLoadingOlder(false);
|
|
579
771
|
setLoadingNewer(false);
|
|
772
|
+
setLoadingOldest(false);
|
|
773
|
+
loadingTargetRef.current = true;
|
|
774
|
+
setLoadingTarget(true);
|
|
775
|
+
let published = false;
|
|
776
|
+
try {
|
|
777
|
+
const [before, following] = await Promise.all([
|
|
778
|
+
loadPreviousPage(client, workspaceId, sessionId, sequence + 1, { pageSize: 128 }),
|
|
779
|
+
loadNextPage(client, workspaceId, sessionId, sequence, { pageSize: 128 })
|
|
780
|
+
]);
|
|
781
|
+
if (signal?.aborted) return false;
|
|
782
|
+
if (!current()) return false;
|
|
783
|
+
if (!before.some((event) => event.sequence === sequence)) return false;
|
|
784
|
+
const prefix = boundBrowserSessionEventWindow(before, {
|
|
785
|
+
direction: "newest",
|
|
786
|
+
maxBytes: SESSION_EVENT_BROWSER_MAX_BYTES / 2
|
|
787
|
+
});
|
|
788
|
+
const retained = boundBrowserSessionEventWindow([...prefix.events, ...following], {
|
|
789
|
+
direction: "oldest"
|
|
790
|
+
});
|
|
791
|
+
if (!retained.events.some((event) => event.sequence === sequence)) return false;
|
|
792
|
+
streamAbortRef.current?.abort();
|
|
793
|
+
generationRef.current += 1;
|
|
794
|
+
const status = observeSessionStatus(retained.events, sessionStatusRef);
|
|
795
|
+
eventWindowRef.current = retained;
|
|
796
|
+
oldestSequenceRef.current = retained.events[0].sequence;
|
|
797
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
|
|
798
|
+
lastSequenceRef.current = Math.max(lastSequenceRef.current, newestSequenceRef.current ?? 0);
|
|
799
|
+
hasOlderRef.current = !isLogStart(retained.events[0]);
|
|
800
|
+
hasNewerRef.current = following.length > 0 || retained.truncated || (newestSequenceRef.current ?? 0) < lastSequenceRef.current;
|
|
801
|
+
initialWindowLoadedRef.current = true;
|
|
802
|
+
setInitialHistoryReady(true);
|
|
803
|
+
setError(null);
|
|
804
|
+
viewModeRef.current = "history";
|
|
805
|
+
setEventWindow(retained);
|
|
806
|
+
setHasOlder(hasOlderRef.current);
|
|
807
|
+
setHasNewer(hasNewerRef.current);
|
|
808
|
+
setInitialLoading(false);
|
|
809
|
+
setViewMode("history");
|
|
810
|
+
setNewerError(null);
|
|
811
|
+
if (status !== void 0) setSessionStatusProjection(status);
|
|
812
|
+
published = true;
|
|
813
|
+
return true;
|
|
814
|
+
} catch (reason) {
|
|
815
|
+
if (!current() || signal?.aborted) return false;
|
|
816
|
+
throw reason;
|
|
817
|
+
} finally {
|
|
818
|
+
if (current()) {
|
|
819
|
+
loadingTargetRef.current = false;
|
|
820
|
+
setLoadingTarget(false);
|
|
821
|
+
if (!published && previousMode === "live") {
|
|
822
|
+
viewModeRef.current = "live";
|
|
823
|
+
setViewMode("live");
|
|
824
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
},
|
|
829
|
+
[client, workspaceId, sessionId, ownsNavigation]
|
|
830
|
+
);
|
|
831
|
+
const jumpToLatestQuestion = useCallback(
|
|
832
|
+
async (questionOptions) => {
|
|
833
|
+
if (!sessionId || !ownsNavigation()) return null;
|
|
834
|
+
let generation = navigationGenerationRef.current;
|
|
835
|
+
const current = () => ownsNavigation() && generation === navigationGenerationRef.current;
|
|
836
|
+
try {
|
|
837
|
+
const { resolveLatestQuestion } = await import("./latest-question-755OW5DS.js");
|
|
838
|
+
if (!current()) return null;
|
|
839
|
+
const destination = await resolveLatestQuestion({
|
|
840
|
+
client,
|
|
841
|
+
workspaceId,
|
|
842
|
+
sessionId,
|
|
843
|
+
isCurrent: current,
|
|
844
|
+
resumeSequence: maxResumeSequence,
|
|
845
|
+
options: questionOptions
|
|
846
|
+
});
|
|
847
|
+
if (!destination || !current()) return null;
|
|
848
|
+
if (!eventWindowRef.current.events.some((event) => event.sequence === destination.anchor)) {
|
|
849
|
+
const navigation = jumpToSequence(destination.anchor);
|
|
850
|
+
generation = navigationGenerationRef.current;
|
|
851
|
+
if (!await navigation || !current()) return null;
|
|
852
|
+
}
|
|
853
|
+
setQuestionEvidence({
|
|
854
|
+
client,
|
|
855
|
+
streamKey,
|
|
856
|
+
anchor: destination.anchor,
|
|
857
|
+
events: destination.events
|
|
858
|
+
});
|
|
859
|
+
return destination.questionSequence;
|
|
860
|
+
} catch (reason) {
|
|
861
|
+
if (!current()) return null;
|
|
862
|
+
throw reason;
|
|
863
|
+
}
|
|
864
|
+
},
|
|
865
|
+
[client, workspaceId, sessionId, streamKey, ownsNavigation, jumpToSequence]
|
|
866
|
+
);
|
|
867
|
+
const jumpToLatest = useCallback(async () => {
|
|
868
|
+
if (!ownsNavigation()) return;
|
|
869
|
+
if (loadingTargetRef.current) {
|
|
870
|
+
navigationGenerationRef.current += 1;
|
|
871
|
+
loadingTargetRef.current = false;
|
|
872
|
+
setLoadingTarget(false);
|
|
580
873
|
}
|
|
581
|
-
}, [client, workspaceId, sessionId]);
|
|
582
|
-
const jumpToLatest = useCallback2(async () => {
|
|
583
874
|
if (!sessionId || navigationBusy()) {
|
|
584
875
|
return;
|
|
585
876
|
}
|
|
877
|
+
navigationGenerationRef.current += 1;
|
|
878
|
+
setQuestionEvidence(null);
|
|
586
879
|
loadingLatestRef.current = true;
|
|
587
880
|
setLoadingLatest(true);
|
|
881
|
+
setNewerError(null);
|
|
882
|
+
setError(null);
|
|
883
|
+
let published = false;
|
|
588
884
|
try {
|
|
589
885
|
streamAbortRef.current?.abort();
|
|
590
886
|
hasNewerRef.current = false;
|
|
591
|
-
setHasNewer(false);
|
|
592
887
|
hasOlderRef.current = false;
|
|
593
|
-
setHasOlder(false);
|
|
594
888
|
newestSequenceRef.current = null;
|
|
595
889
|
oldestSequenceRef.current = null;
|
|
596
890
|
eventWindowRef.current = EMPTY_EVENT_WINDOW;
|
|
597
|
-
setEventWindow(EMPTY_EVENT_WINDOW);
|
|
598
891
|
initialWindowLoadedRef.current = false;
|
|
599
892
|
streamResumeSequenceRef.current = after;
|
|
600
|
-
setInitialLoading(true);
|
|
601
893
|
viewModeRef.current = "live";
|
|
602
|
-
|
|
603
|
-
|
|
894
|
+
loadingLatestRef.current = false;
|
|
895
|
+
startTransition(() => {
|
|
896
|
+
setHasNewer(false);
|
|
897
|
+
setHasOlder(false);
|
|
898
|
+
setEventWindow(EMPTY_EVENT_WINDOW);
|
|
899
|
+
setInitialLoading(true);
|
|
900
|
+
setViewMode("live");
|
|
901
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
902
|
+
setLoadingLatest(false);
|
|
903
|
+
});
|
|
904
|
+
published = true;
|
|
604
905
|
await Promise.resolve();
|
|
605
906
|
} finally {
|
|
606
|
-
|
|
607
|
-
|
|
907
|
+
if (!published) {
|
|
908
|
+
loadingLatestRef.current = false;
|
|
909
|
+
setLoadingLatest(false);
|
|
910
|
+
}
|
|
608
911
|
}
|
|
609
|
-
}, [after, sessionId]);
|
|
912
|
+
}, [after, sessionId, ownsNavigation]);
|
|
610
913
|
const identityMatches = stateStreamKey === streamKey;
|
|
611
914
|
const visibleEvents = identityMatches ? eventWindow.events : EMPTY_EVENTS;
|
|
612
|
-
const timeline =
|
|
915
|
+
const timeline = useMemo(() => {
|
|
916
|
+
const witness = questionEvidence?.client === client && questionEvidence.streamKey === streamKey && visibleEvents.some((event) => event.sequence === questionEvidence.anchor) ? questionEvidence.events : EMPTY_EVENTS;
|
|
917
|
+
const timelineOptions = { partialStart: hasOlder || eventWindow.truncated || after > 0 };
|
|
918
|
+
if (witness.length === 0) return buildTimeline(visibleEvents, timelineOptions);
|
|
919
|
+
const ids = new Set(visibleEvents.map((event) => event.id));
|
|
920
|
+
return buildTimeline(
|
|
921
|
+
[...visibleEvents, ...witness.filter((event) => !ids.has(event.id))],
|
|
922
|
+
timelineOptions
|
|
923
|
+
);
|
|
924
|
+
}, [visibleEvents, hasOlder, eventWindow.truncated, after, questionEvidence, client, streamKey]);
|
|
613
925
|
return {
|
|
614
926
|
events: visibleEvents,
|
|
615
927
|
timeline,
|
|
616
928
|
sessionStatus: identityMatches ? sessionStatusProjection : null,
|
|
929
|
+
sessionStatusSequence: identityMatches ? sessionStatusRef.current.sequence : 0,
|
|
617
930
|
connectionState: identityMatches ? connectionState : "idle",
|
|
618
931
|
lastSequence: identityMatches ? lastSequenceRef.current : after,
|
|
619
932
|
windowBytes: identityMatches ? eventWindow.bytes : 2,
|
|
620
933
|
windowTruncated: identityMatches ? eventWindow.truncated : false,
|
|
621
934
|
initialLoading: fullReplay ? false : identityMatches ? initialLoading : true,
|
|
935
|
+
initialHistoryReady: identityMatches && initialHistoryReady,
|
|
622
936
|
hasOlder: !identityMatches ? false : hasOlder,
|
|
623
937
|
loadingOlder: !identityMatches ? false : loadingOlder,
|
|
624
938
|
loadOlder,
|
|
@@ -629,13 +943,16 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
629
943
|
loadOldest,
|
|
630
944
|
loadingLatest: !identityMatches ? false : loadingLatest,
|
|
631
945
|
jumpToLatest,
|
|
632
|
-
|
|
946
|
+
jumpToLatestQuestion,
|
|
947
|
+
jumpToSequence,
|
|
948
|
+
loadingTarget: identityMatches && loadingTarget,
|
|
949
|
+
error: identityMatches ? newerError ?? error : null
|
|
633
950
|
};
|
|
634
951
|
}
|
|
635
952
|
function boundBrowserSessionEventWindow(events, options = {}) {
|
|
636
953
|
const maxBytes = Math.max(1024, options.maxBytes ?? SESSION_EVENT_BROWSER_MAX_BYTES);
|
|
637
954
|
const maxCount = Math.max(1, Math.floor(options.maxCount ?? SESSION_EVENT_BROWSER_MAX_COUNT));
|
|
638
|
-
const safe = events
|
|
955
|
+
const safe = events;
|
|
639
956
|
const selected = [];
|
|
640
957
|
let bytes = 2;
|
|
641
958
|
const direction = options.direction ?? "newest";
|
|
@@ -646,7 +963,7 @@ function boundBrowserSessionEventWindow(events, options = {}) {
|
|
|
646
963
|
const event = safe[index];
|
|
647
964
|
const eventBytes = browserJsonBytes(event);
|
|
648
965
|
const separator = selected.length === 0 ? 0 : 1;
|
|
649
|
-
if (bytes + separator + eventBytes > maxBytes) break;
|
|
966
|
+
if (selected.length > 0 && bytes + separator + eventBytes > maxBytes) break;
|
|
650
967
|
selected.push(event);
|
|
651
968
|
bytes += separator + eventBytes;
|
|
652
969
|
}
|
|
@@ -657,184 +974,29 @@ function boundBrowserSessionEventWindow(events, options = {}) {
|
|
|
657
974
|
truncated: selected.length < safe.length
|
|
658
975
|
};
|
|
659
976
|
}
|
|
660
|
-
function
|
|
661
|
-
const
|
|
662
|
-
const
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
);
|
|
668
|
-
const duplicateReason = boundBrowserOptionalText(
|
|
669
|
-
event.duplicateReason,
|
|
670
|
-
BROWSER_EVENT_DUPLICATE_REASON_MAX_BYTES
|
|
671
|
-
);
|
|
672
|
-
if (serialized.serializable && originalBytes !== null && originalBytes <= SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES && typeIsSafe && clientEventId === event.clientEventId && duplicateReason === event.duplicateReason) {
|
|
673
|
-
return event;
|
|
977
|
+
function appendBrowserSessionEventWindow(current, batch, options = {}) {
|
|
978
|
+
const maxBytes = Math.max(1024, options.maxBytes ?? SESSION_EVENT_BROWSER_MAX_BYTES);
|
|
979
|
+
const maxCount = Math.max(1, Math.floor(options.maxCount ?? SESSION_EVENT_BROWSER_MAX_COUNT));
|
|
980
|
+
const events = [...current.events, ...batch];
|
|
981
|
+
let bytes = current.bytes;
|
|
982
|
+
for (let index = 0; index < batch.length; index += 1) {
|
|
983
|
+
bytes += browserJsonBytes(batch[index]) + (current.events.length + index > 0 ? 1 : 0);
|
|
674
984
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
].filter((field) => field !== null);
|
|
680
|
-
const payloadSerialization = browserSerialize(event.payload);
|
|
681
|
-
const payloadBytes = payloadSerialization.serializable ? encoder.encode(payloadSerialization.value).byteLength : null;
|
|
682
|
-
const preview = truncateBrowserUtf8Middle(
|
|
683
|
-
payloadSerialization.value,
|
|
684
|
-
BROWSER_EVENT_PAYLOAD_PREVIEW_MAX_BYTES
|
|
685
|
-
);
|
|
686
|
-
const truncation = {
|
|
687
|
-
truncated: true,
|
|
688
|
-
surface: "browser_legacy_guard",
|
|
689
|
-
reason: serialized.serializable ? "event_envelope_bytes_exceeded" : "event_not_serializable",
|
|
690
|
-
originalBytes,
|
|
691
|
-
deliveredBytes: 0,
|
|
692
|
-
omittedBytes: originalBytes,
|
|
693
|
-
estimatedOriginalTokens: originalBytes === null ? null : Math.ceil(originalBytes / 4),
|
|
694
|
-
estimatedDeliveredTokens: 0,
|
|
695
|
-
fullEvidence: { available: false, reason: "not_retained" },
|
|
696
|
-
details: [
|
|
697
|
-
{
|
|
698
|
-
path: "$.payload",
|
|
699
|
-
kind: "payload_preview",
|
|
700
|
-
originalBytes: payloadBytes,
|
|
701
|
-
deliveredBytes: browserUtf8Bytes(preview)
|
|
702
|
-
}
|
|
703
|
-
]
|
|
704
|
-
};
|
|
705
|
-
const payload = {
|
|
706
|
-
...browserPayloadIdentity(event.payload),
|
|
707
|
-
preview,
|
|
708
|
-
...envelopeProjection.length > 0 ? {
|
|
709
|
-
originalType: boundBrowserText(event.type, BROWSER_EVENT_TYPE_MAX_BYTES),
|
|
710
|
-
envelopeProjection: {
|
|
711
|
-
truncated: true,
|
|
712
|
-
surface: "browser_legacy_guard",
|
|
713
|
-
fields: envelopeProjection
|
|
714
|
-
}
|
|
715
|
-
} : {},
|
|
716
|
-
truncation
|
|
717
|
-
};
|
|
718
|
-
const bounded = {
|
|
719
|
-
id: boundBrowserText(event.id, BROWSER_EVENT_ID_MAX_BYTES),
|
|
720
|
-
workspaceId: boundBrowserText(event.workspaceId, BROWSER_EVENT_ID_MAX_BYTES),
|
|
721
|
-
sessionId: boundBrowserText(event.sessionId, BROWSER_EVENT_ID_MAX_BYTES),
|
|
722
|
-
sequence: event.sequence,
|
|
723
|
-
type: typeIsSafe ? event.type : "session.event.envelope_omitted",
|
|
724
|
-
payload,
|
|
725
|
-
occurredAt: boundBrowserText(event.occurredAt, BROWSER_EVENT_ID_MAX_BYTES),
|
|
726
|
-
clientEventId,
|
|
727
|
-
turnId: boundBrowserOptionalText(event.turnId, BROWSER_EVENT_ID_MAX_BYTES),
|
|
728
|
-
turnGeneration: event.turnGeneration,
|
|
729
|
-
turnAttemptId: boundBrowserOptionalText(event.turnAttemptId, BROWSER_EVENT_ID_MAX_BYTES),
|
|
730
|
-
turnAssociation: event.turnAssociation,
|
|
731
|
-
duplicateOfEventId: boundBrowserOptionalText(
|
|
732
|
-
event.duplicateOfEventId,
|
|
733
|
-
BROWSER_EVENT_ID_MAX_BYTES
|
|
734
|
-
),
|
|
735
|
-
duplicateReason
|
|
736
|
-
};
|
|
737
|
-
settleBrowserEventTruncation(bounded, truncation);
|
|
738
|
-
if (browserJsonBytes(bounded) > SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES) {
|
|
739
|
-
payload.preview = truncateBrowserUtf8Middle(String(payload.preview), 4 * 1024);
|
|
740
|
-
truncation.details = truncation.details.slice(0, 1);
|
|
741
|
-
settleBrowserEventTruncation(bounded, truncation);
|
|
985
|
+
let start = 0;
|
|
986
|
+
while (events.length - start > 1 && (bytes > maxBytes || events.length - start > maxCount)) {
|
|
987
|
+
bytes -= browserJsonBytes(events[start]) + 1;
|
|
988
|
+
start += 1;
|
|
742
989
|
}
|
|
743
|
-
if (browserJsonBytes(bounded) > SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES) {
|
|
744
|
-
bounded.clientEventId = null;
|
|
745
|
-
bounded.duplicateReason = null;
|
|
746
|
-
payload.preview = "[legacy event omitted at the browser byte boundary]";
|
|
747
|
-
settleBrowserEventTruncation(bounded, truncation);
|
|
748
|
-
}
|
|
749
|
-
return bounded;
|
|
750
|
-
}
|
|
751
|
-
function browserJsonBytes(value) {
|
|
752
|
-
return encoder.encode(browserSerialize(value).value).byteLength;
|
|
753
|
-
}
|
|
754
|
-
function browserSerialize(value) {
|
|
755
|
-
try {
|
|
756
|
-
const serialized = JSON.stringify(value);
|
|
757
|
-
return { value: serialized === void 0 ? "null" : serialized, serializable: true };
|
|
758
|
-
} catch {
|
|
759
|
-
return {
|
|
760
|
-
value: '"[unserializable event payload omitted at browser boundary]"',
|
|
761
|
-
serializable: false
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
function browserPayloadIdentity(payload) {
|
|
766
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return {};
|
|
767
|
-
const record = payload;
|
|
768
|
-
const identity = {};
|
|
769
|
-
for (const field of BROWSER_EVENT_PAYLOAD_IDENTITY_FIELDS) {
|
|
770
|
-
const value = record[field];
|
|
771
|
-
if (typeof value === "string") {
|
|
772
|
-
identity[field] = boundBrowserText(value, BROWSER_EVENT_ID_MAX_BYTES);
|
|
773
|
-
} else if (typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
774
|
-
identity[field] = value;
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
return identity;
|
|
778
|
-
}
|
|
779
|
-
function browserEnvelopeFieldProjection(field, original, delivered) {
|
|
780
990
|
return {
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
991
|
+
events: start === 0 ? events : events.slice(start),
|
|
992
|
+
bytes,
|
|
993
|
+
truncated: current.truncated || start > 0
|
|
784
994
|
};
|
|
785
995
|
}
|
|
786
|
-
function
|
|
787
|
-
return
|
|
788
|
-
}
|
|
789
|
-
function boundBrowserText(value, maxBytes) {
|
|
790
|
-
const bytes = encoder.encode(value);
|
|
791
|
-
if (bytes.byteLength <= maxBytes) return value;
|
|
792
|
-
const marker = "\u2026[truncated]";
|
|
793
|
-
const prefixBudget = Math.max(0, maxBytes - browserUtf8Bytes(marker));
|
|
794
|
-
let prefixEnd = Math.min(prefixBudget, bytes.byteLength);
|
|
795
|
-
while (prefixEnd > 0 && prefixEnd < bytes.byteLength && isBrowserUtf8Continuation(bytes[prefixEnd])) {
|
|
796
|
-
prefixEnd -= 1;
|
|
797
|
-
}
|
|
798
|
-
return `${decoder.decode(bytes.subarray(0, prefixEnd))}${marker}`;
|
|
799
|
-
}
|
|
800
|
-
function truncateBrowserUtf8Middle(value, maxBytes) {
|
|
801
|
-
const bytes = encoder.encode(value);
|
|
802
|
-
if (bytes.byteLength <= maxBytes) return value;
|
|
803
|
-
const marker = `\u2026[${bytes.byteLength - maxBytes} bytes omitted]\u2026`;
|
|
804
|
-
const contentBudget = Math.max(0, maxBytes - browserUtf8Bytes(marker));
|
|
805
|
-
const leftBudget = Math.floor(contentBudget / 2);
|
|
806
|
-
const rightBudget = contentBudget - leftBudget;
|
|
807
|
-
let leftEnd = Math.min(leftBudget, bytes.byteLength);
|
|
808
|
-
while (leftEnd > 0 && leftEnd < bytes.byteLength && isBrowserUtf8Continuation(bytes[leftEnd])) {
|
|
809
|
-
leftEnd -= 1;
|
|
810
|
-
}
|
|
811
|
-
let rightStart = Math.max(0, bytes.byteLength - rightBudget);
|
|
812
|
-
while (rightStart < bytes.byteLength && isBrowserUtf8Continuation(bytes[rightStart])) {
|
|
813
|
-
rightStart += 1;
|
|
814
|
-
}
|
|
815
|
-
return `${decoder.decode(bytes.subarray(0, leftEnd))}${marker}${decoder.decode(bytes.subarray(rightStart))}`;
|
|
816
|
-
}
|
|
817
|
-
function settleBrowserEventTruncation(event, truncation) {
|
|
818
|
-
for (let attempt = 0; attempt < 16; attempt += 1) {
|
|
819
|
-
const deliveredBytes = browserJsonBytes(event);
|
|
820
|
-
const omittedBytes = truncation.originalBytes === null ? null : Math.max(0, truncation.originalBytes - deliveredBytes);
|
|
821
|
-
const estimatedDeliveredTokens = Math.ceil(deliveredBytes / 4);
|
|
822
|
-
if (truncation.deliveredBytes === deliveredBytes && truncation.omittedBytes === omittedBytes && truncation.estimatedDeliveredTokens === estimatedDeliveredTokens) {
|
|
823
|
-
return;
|
|
824
|
-
}
|
|
825
|
-
truncation.deliveredBytes = deliveredBytes;
|
|
826
|
-
truncation.omittedBytes = omittedBytes;
|
|
827
|
-
truncation.estimatedDeliveredTokens = estimatedDeliveredTokens;
|
|
828
|
-
}
|
|
829
|
-
throw new RangeError("Browser event byte accounting did not converge");
|
|
830
|
-
}
|
|
831
|
-
function browserUtf8Bytes(value) {
|
|
832
|
-
return encoder.encode(value).byteLength;
|
|
833
|
-
}
|
|
834
|
-
function isBrowserUtf8Continuation(value) {
|
|
835
|
-
return (value & 192) === 128;
|
|
996
|
+
function browserJsonBytes(value) {
|
|
997
|
+
return encoder.encode(JSON.stringify(value)).byteLength;
|
|
836
998
|
}
|
|
837
|
-
function observeSessionStatus(events, ref
|
|
999
|
+
function observeSessionStatus(events, ref) {
|
|
838
1000
|
let latest = null;
|
|
839
1001
|
for (const event of events) {
|
|
840
1002
|
const status = sessionStatusFromEvents([event]);
|
|
@@ -842,9 +1004,42 @@ function observeSessionStatus(events, ref, setStatus) {
|
|
|
842
1004
|
latest = { sequence: event.sequence, status };
|
|
843
1005
|
}
|
|
844
1006
|
}
|
|
845
|
-
if (!latest || latest.sequence < ref.current.sequence) return;
|
|
1007
|
+
if (!latest || latest.sequence < ref.current.sequence) return void 0;
|
|
846
1008
|
ref.current = latest;
|
|
847
|
-
|
|
1009
|
+
return latest.status;
|
|
1010
|
+
}
|
|
1011
|
+
async function planForegroundCatchup(client, workspaceId, sessionId, cursor, options = {}) {
|
|
1012
|
+
if (options.signal?.aborted) throw abortError();
|
|
1013
|
+
const session = await client.getSession(workspaceId, sessionId, {
|
|
1014
|
+
fresh: true,
|
|
1015
|
+
...options.signal ? { signal: options.signal } : {}
|
|
1016
|
+
});
|
|
1017
|
+
if (options.signal?.aborted) throw abortError();
|
|
1018
|
+
const durableHead = Math.max(cursor, session.lastSequence);
|
|
1019
|
+
const rawGap = durableHead - cursor;
|
|
1020
|
+
if (rawGap <= FOREGROUND_DIRECT_REPLAY_MAX_SEQUENCES) {
|
|
1021
|
+
return { kind: "resume" };
|
|
1022
|
+
}
|
|
1023
|
+
if (rawGap > FOREGROUND_COMPACT_PROBE_MAX_SEQUENCES) {
|
|
1024
|
+
return { kind: "reload" };
|
|
1025
|
+
}
|
|
1026
|
+
const page = await loadNextPage(client, workspaceId, sessionId, cursor, {
|
|
1027
|
+
pageSize: rawGap,
|
|
1028
|
+
...options.signal ? { signal: options.signal } : {}
|
|
1029
|
+
});
|
|
1030
|
+
const events = page.filter((event) => eventResumeSequence(event) > cursor);
|
|
1031
|
+
if (events.length === 0) {
|
|
1032
|
+
return { kind: "reload" };
|
|
1033
|
+
}
|
|
1034
|
+
assertAscending(events);
|
|
1035
|
+
const resumeSequence = maxResumeSequence(events);
|
|
1036
|
+
if (resumeSequence < durableHead) {
|
|
1037
|
+
return { kind: "reload" };
|
|
1038
|
+
}
|
|
1039
|
+
if (events.length > FOREGROUND_COMPACT_CATCHUP_MAX_EVENTS || groupCount(events) > FOREGROUND_COMPACT_CATCHUP_MAX_GROUPS || browserJsonBytes(events) > FOREGROUND_COMPACT_CATCHUP_MAX_BYTES) {
|
|
1040
|
+
return { kind: "reload" };
|
|
1041
|
+
}
|
|
1042
|
+
return { kind: "append", events, resumeSequence };
|
|
848
1043
|
}
|
|
849
1044
|
async function loadEventWindow(client, workspaceId, sessionId, options) {
|
|
850
1045
|
let cursor = options.before;
|
|
@@ -859,6 +1054,7 @@ async function loadEventWindow(client, workspaceId, sessionId, options) {
|
|
|
859
1054
|
pageSize: options.pageSize,
|
|
860
1055
|
...options.signal ? { signal: options.signal } : {}
|
|
861
1056
|
});
|
|
1057
|
+
if (options.isCurrent?.() === false) throw abortError();
|
|
862
1058
|
fetches += 1;
|
|
863
1059
|
if (page.length === 0) {
|
|
864
1060
|
reachedStart = true;
|
|
@@ -873,11 +1069,12 @@ async function loadEventWindow(client, workspaceId, sessionId, options) {
|
|
|
873
1069
|
}
|
|
874
1070
|
}
|
|
875
1071
|
let snapPages = 0;
|
|
876
|
-
while (!reachedStart && findBoundaryIndex(buffer) === -1 && snapPages <
|
|
1072
|
+
while (!reachedStart && findBoundaryIndex(buffer) === -1 && snapPages < (options.boundaryPageCap ?? 0)) {
|
|
877
1073
|
const page = await loadPreviousPage(client, workspaceId, sessionId, cursor, {
|
|
878
1074
|
pageSize: options.pageSize,
|
|
879
1075
|
...options.signal ? { signal: options.signal } : {}
|
|
880
1076
|
});
|
|
1077
|
+
if (options.isCurrent?.() === false) throw abortError();
|
|
881
1078
|
fetches += 1;
|
|
882
1079
|
snapPages += 1;
|
|
883
1080
|
if (page.length === 0) {
|
|
@@ -898,24 +1095,28 @@ async function loadEventWindow(client, workspaceId, sessionId, options) {
|
|
|
898
1095
|
buffer = buffer.slice(boundary);
|
|
899
1096
|
}
|
|
900
1097
|
}
|
|
1098
|
+
const densityWindow = trimBackwardToGroupTarget(buffer, options.targetGroups);
|
|
1099
|
+
buffer = densityWindow.events;
|
|
901
1100
|
const oldest = buffer[0] ?? null;
|
|
902
1101
|
const newest = buffer[buffer.length - 1] ?? null;
|
|
903
1102
|
return {
|
|
904
1103
|
events: buffer,
|
|
905
1104
|
oldestSequence: oldest?.sequence ?? null,
|
|
906
1105
|
newestSequence: newest ? maxResumeSequence(buffer) : 0,
|
|
907
|
-
hasOlder: buffer.length > 0 && !reachedStart && oldest?.type !== "session.created"
|
|
1106
|
+
hasOlder: buffer.length > 0 && (densityWindow.trimmed || !reachedStart && oldest?.type !== "session.created")
|
|
908
1107
|
};
|
|
909
1108
|
}
|
|
910
1109
|
function findBoundaryIndex(events) {
|
|
911
1110
|
for (let index = 0; index < events.length; index += 1) {
|
|
912
|
-
|
|
913
|
-
if (type === "session.created" || type === "user.message") {
|
|
1111
|
+
if (isTurnBoundary(events[index])) {
|
|
914
1112
|
return index;
|
|
915
1113
|
}
|
|
916
1114
|
}
|
|
917
1115
|
return -1;
|
|
918
1116
|
}
|
|
1117
|
+
function isTurnBoundary(event) {
|
|
1118
|
+
return event.type === "session.created" || event.type === "user.message";
|
|
1119
|
+
}
|
|
919
1120
|
async function loadForwardEventWindow(client, workspaceId, sessionId, options) {
|
|
920
1121
|
let cursor = options.after;
|
|
921
1122
|
let buffer = [];
|
|
@@ -929,6 +1130,7 @@ async function loadForwardEventWindow(client, workspaceId, sessionId, options) {
|
|
|
929
1130
|
pageSize: options.pageSize,
|
|
930
1131
|
...options.signal ? { signal: options.signal } : {}
|
|
931
1132
|
});
|
|
1133
|
+
if (options.isCurrent?.() === false) throw abortError();
|
|
932
1134
|
fetches += 1;
|
|
933
1135
|
if (page.length === 0) {
|
|
934
1136
|
reachedEnd = true;
|
|
@@ -936,21 +1138,45 @@ async function loadForwardEventWindow(client, workspaceId, sessionId, options) {
|
|
|
936
1138
|
}
|
|
937
1139
|
assertAscending(page);
|
|
938
1140
|
buffer = [...buffer, ...page];
|
|
939
|
-
cursor = page
|
|
940
|
-
if (page.length < options.pageSize) {
|
|
941
|
-
reachedEnd = true;
|
|
942
|
-
break;
|
|
943
|
-
}
|
|
1141
|
+
cursor = maxResumeSequence(page);
|
|
944
1142
|
}
|
|
1143
|
+
const densityWindow = trimForwardToGroupTarget(buffer, options.targetGroups);
|
|
1144
|
+
buffer = densityWindow.events;
|
|
945
1145
|
const oldest = buffer[0] ?? null;
|
|
946
1146
|
const newest = buffer[buffer.length - 1] ?? null;
|
|
947
1147
|
return {
|
|
948
1148
|
events: buffer,
|
|
949
1149
|
oldestSequence: oldest?.sequence ?? null,
|
|
950
1150
|
newestSequence: newest ? maxResumeSequence(buffer) : 0,
|
|
951
|
-
hasNewer: buffer.length > 0 && !reachedEnd
|
|
1151
|
+
hasNewer: buffer.length > 0 && (densityWindow.trimmed || !reachedEnd)
|
|
952
1152
|
};
|
|
953
1153
|
}
|
|
1154
|
+
function trimBackwardToGroupTarget(events, targetGroups) {
|
|
1155
|
+
if (!Number.isFinite(targetGroups) || targetGroups <= 0 || events.length === 0) {
|
|
1156
|
+
return { events, trimmed: false };
|
|
1157
|
+
}
|
|
1158
|
+
for (let index = events.length - 1; index > 0; index -= 1) {
|
|
1159
|
+
if (!isTurnBoundary(events[index])) continue;
|
|
1160
|
+
const candidate = events.slice(index);
|
|
1161
|
+
if (groupCount(candidate) >= targetGroups) {
|
|
1162
|
+
return { events: candidate, trimmed: true };
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
return { events, trimmed: false };
|
|
1166
|
+
}
|
|
1167
|
+
function trimForwardToGroupTarget(events, targetGroups) {
|
|
1168
|
+
if (!Number.isFinite(targetGroups) || targetGroups <= 0 || events.length === 0) {
|
|
1169
|
+
return { events, trimmed: false };
|
|
1170
|
+
}
|
|
1171
|
+
for (let index = 1; index < events.length; index += 1) {
|
|
1172
|
+
if (!isTurnBoundary(events[index])) continue;
|
|
1173
|
+
const candidate = events.slice(0, index);
|
|
1174
|
+
if (groupCount(candidate) >= targetGroups) {
|
|
1175
|
+
return { events: candidate, trimmed: true };
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
return { events, trimmed: false };
|
|
1179
|
+
}
|
|
954
1180
|
async function loadPreviousPage(client, workspaceId, sessionId, before, options) {
|
|
955
1181
|
if (options.signal?.aborted) {
|
|
956
1182
|
throw abortError();
|
|
@@ -1006,13 +1232,13 @@ function isLogStart(event) {
|
|
|
1006
1232
|
function maxResumeSequence(events) {
|
|
1007
1233
|
return events.reduce((max, event) => Math.max(max, eventResumeSequence(event)), 0);
|
|
1008
1234
|
}
|
|
1009
|
-
function
|
|
1010
|
-
|
|
1011
|
-
const coalescedUntil = Number(payload.coalescedUntil);
|
|
1012
|
-
return Math.max(event.sequence, Number.isFinite(coalescedUntil) ? Math.floor(coalescedUntil) : 0);
|
|
1235
|
+
function maxResumeSequenceOrNull(events) {
|
|
1236
|
+
return events.length > 0 ? maxResumeSequence(events) : null;
|
|
1013
1237
|
}
|
|
1014
|
-
function
|
|
1015
|
-
|
|
1238
|
+
function eventResumeSequence(event) {
|
|
1239
|
+
const streamedCoverage = sessionEventStreamCoveredThrough(event);
|
|
1240
|
+
if (streamedCoverage !== null) return streamedCoverage;
|
|
1241
|
+
return typeof event.coveredThrough === "number" && Number.isSafeInteger(event.coveredThrough) && event.coveredThrough >= event.sequence ? event.coveredThrough : event.sequence;
|
|
1016
1242
|
}
|
|
1017
1243
|
function assertAscending(events) {
|
|
1018
1244
|
for (let index = 1; index < events.length; index += 1) {
|
|
@@ -1047,224 +1273,122 @@ function abortError() {
|
|
|
1047
1273
|
return error;
|
|
1048
1274
|
}
|
|
1049
1275
|
|
|
1050
|
-
// src/hooks/use-
|
|
1051
|
-
import { useCallback as
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
const
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
const committedAttachmentIds = useRef2(/* @__PURE__ */ new Set());
|
|
1071
|
-
useEffect3(() => {
|
|
1072
|
-
const currentIds = new Set(attachments.map((attachment) => attachment.id));
|
|
1073
|
-
for (const id of committedAttachmentIds.current) {
|
|
1074
|
-
if (!currentIds.has(id)) revokePreview(id);
|
|
1075
|
-
}
|
|
1076
|
-
committedAttachmentIds.current = currentIds;
|
|
1077
|
-
}, [attachments, revokePreview]);
|
|
1078
|
-
const scopeGeneration = useRef2(0);
|
|
1079
|
-
const previousScope = useRef2({ client, workspaceId });
|
|
1080
|
-
useEffect3(() => {
|
|
1081
|
-
const previous = previousScope.current;
|
|
1082
|
-
if (previous.client === client && previous.workspaceId === workspaceId) return;
|
|
1083
|
-
previousScope.current = { client, workspaceId };
|
|
1084
|
-
scopeGeneration.current += 1;
|
|
1085
|
-
sources.current.clear();
|
|
1086
|
-
revokeAllPreviews();
|
|
1087
|
-
setAttachments([]);
|
|
1088
|
-
}, [client, revokeAllPreviews, workspaceId]);
|
|
1089
|
-
useEffect3(
|
|
1090
|
-
() => () => {
|
|
1091
|
-
scopeGeneration.current += 1;
|
|
1092
|
-
sources.current.clear();
|
|
1093
|
-
revokeAllPreviews();
|
|
1094
|
-
},
|
|
1095
|
-
[revokeAllPreviews]
|
|
1096
|
-
);
|
|
1097
|
-
const startUpload = useCallback3(
|
|
1098
|
-
(id, file) => {
|
|
1099
|
-
const generation = scopeGeneration.current;
|
|
1100
|
-
void client.uploadFile(workspaceId, {
|
|
1101
|
-
filename: file.name || "file",
|
|
1102
|
-
contentType: file.type || "application/octet-stream",
|
|
1103
|
-
data: file
|
|
1104
|
-
}).then((asset) => {
|
|
1105
|
-
if (scopeGeneration.current !== generation) return;
|
|
1106
|
-
sources.current.delete(id);
|
|
1107
|
-
setAttachments(
|
|
1108
|
-
(current) => current.map(
|
|
1109
|
-
(attachment) => attachment.id === id ? {
|
|
1110
|
-
...attachment,
|
|
1111
|
-
status: "ready",
|
|
1112
|
-
file: asset,
|
|
1113
|
-
name: asset.filename,
|
|
1114
|
-
contentType: asset.contentType,
|
|
1115
|
-
sizeBytes: asset.sizeBytes,
|
|
1116
|
-
error: void 0
|
|
1117
|
-
} : attachment
|
|
1118
|
-
)
|
|
1119
|
-
);
|
|
1120
|
-
}).catch((error) => {
|
|
1121
|
-
if (scopeGeneration.current !== generation) return;
|
|
1122
|
-
setAttachments(
|
|
1123
|
-
(current) => current.map(
|
|
1124
|
-
(attachment) => attachment.id === id ? {
|
|
1125
|
-
...attachment,
|
|
1126
|
-
status: "failed",
|
|
1127
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1128
|
-
} : attachment
|
|
1129
|
-
)
|
|
1130
|
-
);
|
|
1131
|
-
});
|
|
1132
|
-
},
|
|
1133
|
-
[client, workspaceId]
|
|
1134
|
-
);
|
|
1135
|
-
const addFiles = useCallback3(
|
|
1136
|
-
(files) => {
|
|
1137
|
-
for (const file of files) {
|
|
1138
|
-
const id = crypto.randomUUID();
|
|
1139
|
-
sources.current.set(id, file);
|
|
1140
|
-
const previewUrl = isImage(file) ? URL.createObjectURL(file) : void 0;
|
|
1141
|
-
if (previewUrl) previewUrls.current.set(id, previewUrl);
|
|
1142
|
-
setAttachments((current) => [
|
|
1143
|
-
...current,
|
|
1144
|
-
{
|
|
1145
|
-
id,
|
|
1146
|
-
name: file.name || "image",
|
|
1147
|
-
contentType: file.type || "application/octet-stream",
|
|
1148
|
-
sizeBytes: file.size,
|
|
1149
|
-
status: "uploading",
|
|
1150
|
-
...previewUrl ? { previewUrl } : {}
|
|
1151
|
-
}
|
|
1152
|
-
]);
|
|
1153
|
-
startUpload(id, file);
|
|
1276
|
+
// src/hooks/use-session.ts
|
|
1277
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
1278
|
+
function isTitleEvent(event) {
|
|
1279
|
+
return event.type === "session.title_set";
|
|
1280
|
+
}
|
|
1281
|
+
function isSessionDetailEvent(event) {
|
|
1282
|
+
return isTitleEvent(event) || event.type.startsWith("session.command.") || event.type.startsWith("session.wait.") || event.type === "session.status.changed" || event.type === "system.update.pending";
|
|
1283
|
+
}
|
|
1284
|
+
function useSession(sessionId, options = {}) {
|
|
1285
|
+
const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } = useEmbeddedSessionRead(options);
|
|
1286
|
+
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
1287
|
+
const [override, setOverride] = useState2(null);
|
|
1288
|
+
const nextReadRevision = useRef2(0);
|
|
1289
|
+
const nextReadGeneration = useRef2(0);
|
|
1290
|
+
const beginRead = options.beginRead;
|
|
1291
|
+
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
1292
|
+
const load = useCallback2(
|
|
1293
|
+
async (signal) => {
|
|
1294
|
+
if (!sessionId) {
|
|
1295
|
+
return null;
|
|
1154
1296
|
}
|
|
1297
|
+
let readGeneration2 = 0;
|
|
1298
|
+
const fetched = await client.getSession(workspaceId, sessionId, {
|
|
1299
|
+
fresh: true,
|
|
1300
|
+
signal,
|
|
1301
|
+
onRequestStart: () => {
|
|
1302
|
+
readGeneration2 = beginRead?.() ?? ++nextReadGeneration.current;
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
setOverride(null);
|
|
1306
|
+
return {
|
|
1307
|
+
session: fetched,
|
|
1308
|
+
revision: ++nextReadRevision.current,
|
|
1309
|
+
readGeneration: readGeneration2
|
|
1310
|
+
};
|
|
1155
1311
|
},
|
|
1156
|
-
[
|
|
1312
|
+
[beginRead, client, workspaceId, sessionId]
|
|
1157
1313
|
);
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1314
|
+
const { data, loading, error, refresh } = usePolledValue(load, {
|
|
1315
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
1316
|
+
enabled
|
|
1317
|
+
});
|
|
1318
|
+
useEffect2(() => {
|
|
1319
|
+
if (enabled && workspaceControlEvent) void refresh();
|
|
1320
|
+
}, [enabled, refresh, workspaceControlEvent]);
|
|
1321
|
+
useEffect2(() => {
|
|
1322
|
+
if (!sessionId || !enabled) return;
|
|
1323
|
+
return registerSessionReconciler(sessionId, "session", refresh);
|
|
1324
|
+
}, [enabled, refresh, registerSessionReconciler, sessionId]);
|
|
1325
|
+
const base = data?.session ?? null;
|
|
1326
|
+
const readRevision = data?.revision ?? 0;
|
|
1327
|
+
const readGeneration = data?.readGeneration ?? 0;
|
|
1328
|
+
const session = useMemo2(
|
|
1329
|
+
() => base && override && override.id === base.id ? { ...base, title: override.title, titleSource: override.titleSource } : base,
|
|
1330
|
+
[base, override]
|
|
1172
1331
|
);
|
|
1173
|
-
const
|
|
1332
|
+
const sharedEvents = options.events;
|
|
1333
|
+
const onTitleEvent = useCallback2(
|
|
1174
1334
|
(event) => {
|
|
1175
|
-
|
|
1176
|
-
|
|
1335
|
+
if (sharedEvents?.some(
|
|
1336
|
+
(item) => item.sessionId === sessionId && !isTitleEvent(item) && isSessionDetailEvent(item) && item.sequence > (base?.lastSequence ?? -1)
|
|
1337
|
+
)) {
|
|
1338
|
+
void refresh();
|
|
1339
|
+
} else if (!isTitleEvent(event)) {
|
|
1340
|
+
if (!base || event.sequence > base.lastSequence) void refresh();
|
|
1177
1341
|
return;
|
|
1178
1342
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
addFiles(files);
|
|
1343
|
+
if (!base || event.sequence <= base.lastSequence) {
|
|
1344
|
+
return;
|
|
1182
1345
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
(files) => {
|
|
1188
|
-
const incoming = /* @__PURE__ */ new Map();
|
|
1189
|
-
for (const file of files) {
|
|
1190
|
-
if (file.status === "ready" && file.workspaceId === workspaceId) {
|
|
1191
|
-
incoming.set(file.id, file);
|
|
1192
|
-
}
|
|
1346
|
+
const payload = event.payload ?? {};
|
|
1347
|
+
const title = payload.title;
|
|
1348
|
+
if (typeof title !== "string") {
|
|
1349
|
+
return;
|
|
1193
1350
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1198
|
-
(attachment) => attachment.status === "ready" && attachment.file ? [[attachment.file.id, attachment]] : []
|
|
1199
|
-
)
|
|
1200
|
-
);
|
|
1201
|
-
const restored = [...incoming.values()].map((file) => {
|
|
1202
|
-
const existing = existingReady.get(file.id);
|
|
1203
|
-
return existing ? {
|
|
1204
|
-
...existing,
|
|
1205
|
-
name: file.filename,
|
|
1206
|
-
contentType: file.contentType,
|
|
1207
|
-
sizeBytes: file.sizeBytes,
|
|
1208
|
-
status: "ready",
|
|
1209
|
-
file,
|
|
1210
|
-
error: void 0
|
|
1211
|
-
} : {
|
|
1212
|
-
id: `restored:${file.id}`,
|
|
1213
|
-
name: file.filename,
|
|
1214
|
-
contentType: file.contentType,
|
|
1215
|
-
sizeBytes: file.sizeBytes,
|
|
1216
|
-
status: "ready",
|
|
1217
|
-
file
|
|
1218
|
-
// No source File and no object URL: server metadata is the
|
|
1219
|
-
// only authority restored across page/device boundaries.
|
|
1220
|
-
};
|
|
1221
|
-
});
|
|
1222
|
-
return [...unresolved, ...restored];
|
|
1351
|
+
const source = payload.source === "user" || payload.source === "agent" ? payload.source : null;
|
|
1352
|
+
setOverride((current) => {
|
|
1353
|
+
const next = current ?? base;
|
|
1354
|
+
return { ...next, title, titleSource: source };
|
|
1223
1355
|
});
|
|
1224
1356
|
},
|
|
1225
|
-
[
|
|
1357
|
+
[base, refresh, sharedEvents, sessionId]
|
|
1226
1358
|
);
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1359
|
+
useSessionEventTrigger(client, workspaceId, sessionId, isSessionDetailEvent, onTitleEvent, {
|
|
1360
|
+
enabled,
|
|
1361
|
+
...options.events !== void 0 ? { events: options.events } : {}
|
|
1362
|
+
});
|
|
1363
|
+
const updateTitle = useCallback2(
|
|
1364
|
+
async (title) => {
|
|
1365
|
+
if (!sessionId) {
|
|
1366
|
+
return null;
|
|
1367
|
+
}
|
|
1368
|
+
const result = await run(() => client.updateSession(workspaceId, sessionId, { title }));
|
|
1369
|
+
if (result) {
|
|
1370
|
+
setOverride(result);
|
|
1371
|
+
}
|
|
1372
|
+
return result;
|
|
1232
1373
|
},
|
|
1233
|
-
[
|
|
1374
|
+
[client, workspaceId, sessionId, run]
|
|
1234
1375
|
);
|
|
1235
|
-
const removeReadyFiles = useCallback3((fileIds) => {
|
|
1236
|
-
const accepted = new Set(fileIds);
|
|
1237
|
-
if (accepted.size === 0) return;
|
|
1238
|
-
setAttachments(
|
|
1239
|
-
(current) => current.filter((attachment) => {
|
|
1240
|
-
return !(attachment.status === "ready" && attachment.file !== void 0 && accepted.has(attachment.file.id));
|
|
1241
|
-
})
|
|
1242
|
-
);
|
|
1243
|
-
}, []);
|
|
1244
|
-
const clear = useCallback3(() => {
|
|
1245
|
-
sources.current.clear();
|
|
1246
|
-
revokeAllPreviews();
|
|
1247
|
-
setAttachments([]);
|
|
1248
|
-
}, [revokeAllPreviews]);
|
|
1249
1376
|
return {
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
remove,
|
|
1261
|
-
removeReadyFiles,
|
|
1262
|
-
clear
|
|
1377
|
+
session,
|
|
1378
|
+
loading,
|
|
1379
|
+
error,
|
|
1380
|
+
readRevision,
|
|
1381
|
+
readGeneration,
|
|
1382
|
+
refresh,
|
|
1383
|
+
updateTitle,
|
|
1384
|
+
updating: mutating,
|
|
1385
|
+
mutationError,
|
|
1386
|
+
clearMutationError
|
|
1263
1387
|
};
|
|
1264
1388
|
}
|
|
1265
1389
|
|
|
1266
1390
|
// src/hooks/use-turn-queue.ts
|
|
1267
|
-
import { useCallback as
|
|
1391
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useLayoutEffect, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
|
|
1268
1392
|
function isTurnQueueEvent(event) {
|
|
1269
1393
|
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.");
|
|
1270
1394
|
}
|
|
@@ -1279,12 +1403,13 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1279
1403
|
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
1280
1404
|
const targetKey = `${workspaceId}\0${sessionId ?? ""}`;
|
|
1281
1405
|
const pageLive = usePageLiveActivity();
|
|
1282
|
-
const [snapshot, setSnapshot] =
|
|
1283
|
-
const [stateTargetKey, setStateTargetKey] =
|
|
1284
|
-
const [loading, setLoading] =
|
|
1285
|
-
const [error, setError] =
|
|
1286
|
-
const [mutationError, setMutationError] =
|
|
1287
|
-
const [pendingByTurn, setPendingByTurn] =
|
|
1406
|
+
const [snapshot, setSnapshot] = useState3(null);
|
|
1407
|
+
const [stateTargetKey, setStateTargetKey] = useState3(targetKey);
|
|
1408
|
+
const [loading, setLoading] = useState3(enabled);
|
|
1409
|
+
const [error, setError] = useState3(null);
|
|
1410
|
+
const [mutationError, setMutationError] = useState3(null);
|
|
1411
|
+
const [pendingByTurn, setPendingByTurn] = useState3({});
|
|
1412
|
+
const [acceptedSteers, setAcceptedSteers] = useState3([]);
|
|
1288
1413
|
const pendingRef = useRef3({});
|
|
1289
1414
|
const readGeneration = useRef3(0);
|
|
1290
1415
|
const targetKeyRef = useRef3(targetKey);
|
|
@@ -1301,8 +1426,21 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1301
1426
|
setError(null);
|
|
1302
1427
|
setMutationError(null);
|
|
1303
1428
|
setPendingByTurn({});
|
|
1429
|
+
setAcceptedSteers([]);
|
|
1304
1430
|
}, [enabled, targetKey]);
|
|
1305
|
-
|
|
1431
|
+
useEffect3(() => {
|
|
1432
|
+
if (acceptedSteers.length === 0 || options.events === void 0) return;
|
|
1433
|
+
const durablyStartedTurnIds = new Set(
|
|
1434
|
+
options.events.flatMap(
|
|
1435
|
+
(event) => event.type === "turn.started" && event.turnId ? [event.turnId] : []
|
|
1436
|
+
)
|
|
1437
|
+
);
|
|
1438
|
+
if (durablyStartedTurnIds.size === 0) return;
|
|
1439
|
+
setAcceptedSteers(
|
|
1440
|
+
(current) => current.filter((steer) => !durablyStartedTurnIds.has(steer.turnId))
|
|
1441
|
+
);
|
|
1442
|
+
}, [acceptedSteers.length, options.events]);
|
|
1443
|
+
const acceptSnapshot = useCallback3(
|
|
1306
1444
|
(ownedTargetKey, next) => {
|
|
1307
1445
|
if (targetKeyRef.current !== ownedTargetKey) return false;
|
|
1308
1446
|
const current = snapshotRef.current;
|
|
@@ -1316,7 +1454,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1316
1454
|
},
|
|
1317
1455
|
[]
|
|
1318
1456
|
);
|
|
1319
|
-
const load =
|
|
1457
|
+
const load = useCallback3(
|
|
1320
1458
|
async (rejectOnFailure = false) => {
|
|
1321
1459
|
if (!sessionId) return;
|
|
1322
1460
|
const ownedTargetKey = `${workspaceId}\0${sessionId}`;
|
|
@@ -1347,7 +1485,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1347
1485
|
},
|
|
1348
1486
|
[client, workspaceId, sessionId, acceptSnapshot]
|
|
1349
1487
|
);
|
|
1350
|
-
|
|
1488
|
+
useEffect3(() => {
|
|
1351
1489
|
if (!enabled || !pageLive) {
|
|
1352
1490
|
setLoading(false);
|
|
1353
1491
|
return;
|
|
@@ -1376,10 +1514,10 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1376
1514
|
readGeneration.current += 1;
|
|
1377
1515
|
};
|
|
1378
1516
|
}, [load, enabled, pageLive, options.pollIntervalMs]);
|
|
1379
|
-
|
|
1517
|
+
useEffect3(() => {
|
|
1380
1518
|
if (enabled && workspaceControlEvent) void load();
|
|
1381
1519
|
}, [enabled, load, workspaceControlEvent]);
|
|
1382
|
-
|
|
1520
|
+
useEffect3(() => {
|
|
1383
1521
|
if (!sessionId || !enabled) return;
|
|
1384
1522
|
return registerSessionReconciler(sessionId, "queue", load);
|
|
1385
1523
|
}, [enabled, load, registerSessionReconciler, sessionId]);
|
|
@@ -1396,7 +1534,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1396
1534
|
},
|
|
1397
1535
|
async () => await load(true)
|
|
1398
1536
|
);
|
|
1399
|
-
const mutate =
|
|
1537
|
+
const mutate = useCallback3(
|
|
1400
1538
|
async (turnId, kind, command) => {
|
|
1401
1539
|
const ownedTargetKey = targetKey;
|
|
1402
1540
|
if (!sessionId || targetKeyRef.current !== ownedTargetKey || pendingRef.current[turnId]) {
|
|
@@ -1409,8 +1547,15 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1409
1547
|
setStateTargetKey(ownedTargetKey);
|
|
1410
1548
|
setPendingByTurn(pendingRef.current);
|
|
1411
1549
|
setMutationError(null);
|
|
1550
|
+
const clientEventId = operationKey();
|
|
1412
1551
|
try {
|
|
1413
|
-
|
|
1552
|
+
let result;
|
|
1553
|
+
try {
|
|
1554
|
+
result = await command(current, turn, clientEventId);
|
|
1555
|
+
} catch (cause) {
|
|
1556
|
+
if (!isOutcomeUnknownError(cause)) throw cause;
|
|
1557
|
+
result = await command(current, turn, clientEventId);
|
|
1558
|
+
}
|
|
1414
1559
|
if (targetKeyRef.current !== ownedTargetKey) return null;
|
|
1415
1560
|
acceptSnapshot(ownedTargetKey, result.snapshot);
|
|
1416
1561
|
return result;
|
|
@@ -1431,13 +1576,13 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1431
1576
|
},
|
|
1432
1577
|
[acceptSnapshot, load, sessionId, targetKey]
|
|
1433
1578
|
);
|
|
1434
|
-
const moveTurn =
|
|
1579
|
+
const moveTurn = useCallback3(
|
|
1435
1580
|
async (turnId, beforeTurnId) => {
|
|
1436
1581
|
const result = await mutate(
|
|
1437
1582
|
turnId,
|
|
1438
1583
|
"move",
|
|
1439
|
-
(current) => client.moveQueueItem(workspaceId, sessionId, turnId, {
|
|
1440
|
-
clientEventId
|
|
1584
|
+
(current, _turn, clientEventId) => client.moveQueueItem(workspaceId, sessionId, turnId, {
|
|
1585
|
+
clientEventId,
|
|
1441
1586
|
expectedQueueVersion: current.version,
|
|
1442
1587
|
beforeTurnId
|
|
1443
1588
|
})
|
|
@@ -1446,13 +1591,13 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1446
1591
|
},
|
|
1447
1592
|
[client, mutate, sessionId, workspaceId]
|
|
1448
1593
|
);
|
|
1449
|
-
const editTurn =
|
|
1594
|
+
const editTurn = useCallback3(
|
|
1450
1595
|
async (turnId, edit) => {
|
|
1451
1596
|
const result = await mutate(
|
|
1452
1597
|
turnId,
|
|
1453
1598
|
"edit",
|
|
1454
|
-
(_current, turn) => client.editQueueItem(workspaceId, sessionId, turnId, {
|
|
1455
|
-
clientEventId
|
|
1599
|
+
(_current, turn, clientEventId) => client.editQueueItem(workspaceId, sessionId, turnId, {
|
|
1600
|
+
clientEventId,
|
|
1456
1601
|
expectedTurnVersion: turn.version,
|
|
1457
1602
|
expectedDraftRevision: edit.expectedDraftRevision,
|
|
1458
1603
|
replaceDraft: edit.replaceDraft
|
|
@@ -1462,28 +1607,65 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1462
1607
|
},
|
|
1463
1608
|
[client, mutate, sessionId, workspaceId]
|
|
1464
1609
|
);
|
|
1465
|
-
const steerTurn =
|
|
1610
|
+
const steerTurn = useCallback3(
|
|
1466
1611
|
async (turnId) => {
|
|
1612
|
+
const ownedTargetKey = targetKey;
|
|
1613
|
+
const turn = snapshotRef.current?.items.find((candidate) => candidate.id === turnId);
|
|
1614
|
+
if (targetKeyRef.current !== ownedTargetKey) return false;
|
|
1615
|
+
if (!turn) {
|
|
1616
|
+
setMutationError(new Error("That queued prompt changed before Steer could be sent."));
|
|
1617
|
+
await load();
|
|
1618
|
+
return false;
|
|
1619
|
+
}
|
|
1620
|
+
const optimistic = {
|
|
1621
|
+
turnId: turn.id,
|
|
1622
|
+
triggerEventId: turn.triggerEventId,
|
|
1623
|
+
text: turn.prompt,
|
|
1624
|
+
annotations: turn.annotations ?? [],
|
|
1625
|
+
resources: turn.resources,
|
|
1626
|
+
tools: turn.tools,
|
|
1627
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1628
|
+
state: "sending"
|
|
1629
|
+
};
|
|
1630
|
+
setAcceptedSteers((current) => [
|
|
1631
|
+
...current.filter((steer) => steer.turnId !== turnId),
|
|
1632
|
+
optimistic
|
|
1633
|
+
]);
|
|
1467
1634
|
const result = await mutate(
|
|
1468
1635
|
turnId,
|
|
1469
1636
|
"steer",
|
|
1470
|
-
(current,
|
|
1471
|
-
clientEventId
|
|
1472
|
-
expectedTurnVersion:
|
|
1637
|
+
(current, queuedTurn, clientEventId) => client.steerQueueItem(workspaceId, sessionId, turnId, {
|
|
1638
|
+
clientEventId,
|
|
1639
|
+
expectedTurnVersion: queuedTurn.version,
|
|
1473
1640
|
controlEtag: current.effectiveControl.controlEtag
|
|
1474
1641
|
})
|
|
1475
1642
|
);
|
|
1476
|
-
|
|
1643
|
+
if (targetKeyRef.current !== ownedTargetKey) return false;
|
|
1644
|
+
const advancedWithoutSteer = result === null && snapshotRef.current !== null && !snapshotRef.current.items.some((candidate) => candidate.id === turnId);
|
|
1645
|
+
if (advancedWithoutSteer) {
|
|
1646
|
+
setMutationError(null);
|
|
1647
|
+
setAcceptedSteers((current) => current.filter((steer) => steer.turnId !== turnId));
|
|
1648
|
+
return true;
|
|
1649
|
+
}
|
|
1650
|
+
const accepted = result !== null || snapshotRef.current?.items.some(
|
|
1651
|
+
(candidate) => candidate.id === turnId && candidate.metadata.delivery === "steer"
|
|
1652
|
+
) === true;
|
|
1653
|
+
setAcceptedSteers(
|
|
1654
|
+
(current) => accepted ? current.map(
|
|
1655
|
+
(steer) => steer.turnId === turnId ? { ...steer, state: "queued" } : steer
|
|
1656
|
+
) : current.filter((steer) => steer.turnId !== turnId)
|
|
1657
|
+
);
|
|
1658
|
+
return accepted;
|
|
1477
1659
|
},
|
|
1478
|
-
[client, mutate, sessionId, workspaceId]
|
|
1660
|
+
[client, load, mutate, sessionId, targetKey, workspaceId]
|
|
1479
1661
|
);
|
|
1480
|
-
const removeTurn =
|
|
1662
|
+
const removeTurn = useCallback3(
|
|
1481
1663
|
async (turnId) => {
|
|
1482
1664
|
const result = await mutate(
|
|
1483
1665
|
turnId,
|
|
1484
1666
|
"delete",
|
|
1485
|
-
(_current, turn) => client.deleteQueueItem(workspaceId, sessionId, turnId, {
|
|
1486
|
-
clientEventId
|
|
1667
|
+
(_current, turn, clientEventId) => client.deleteQueueItem(workspaceId, sessionId, turnId, {
|
|
1668
|
+
clientEventId,
|
|
1487
1669
|
expectedTurnVersion: turn.version,
|
|
1488
1670
|
reason: "Deleted from the prompt queue"
|
|
1489
1671
|
})
|
|
@@ -1499,7 +1681,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1499
1681
|
() => Object.keys(visiblePendingByTurn).length > 0,
|
|
1500
1682
|
[visiblePendingByTurn]
|
|
1501
1683
|
);
|
|
1502
|
-
const mutationFor =
|
|
1684
|
+
const mutationFor = useCallback3(
|
|
1503
1685
|
(turnId) => visiblePendingByTurn[turnId] ?? null,
|
|
1504
1686
|
[visiblePendingByTurn]
|
|
1505
1687
|
);
|
|
@@ -1522,221 +1704,448 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1522
1704
|
mutationFor,
|
|
1523
1705
|
mutating,
|
|
1524
1706
|
mutationError: identityMatches ? mutationError : null,
|
|
1525
|
-
clearMutationError:
|
|
1707
|
+
clearMutationError: useCallback3(() => {
|
|
1526
1708
|
if (targetKeyRef.current === targetKey) setMutationError(null);
|
|
1527
|
-
}, [targetKey])
|
|
1709
|
+
}, [targetKey]),
|
|
1710
|
+
acceptedSteers: identityMatches ? acceptedSteers : []
|
|
1528
1711
|
};
|
|
1529
1712
|
}
|
|
1530
1713
|
function operationKey() {
|
|
1531
1714
|
return globalThis.crypto.randomUUID();
|
|
1532
1715
|
}
|
|
1533
1716
|
function asError(cause) {
|
|
1534
|
-
return
|
|
1717
|
+
return normalizeError(cause);
|
|
1535
1718
|
}
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
import { OpenGeniApiError } from "@opengeni/sdk";
|
|
1539
|
-
import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef4, useState as useState5 } from "react";
|
|
1540
|
-
function isGoalEvent(event) {
|
|
1541
|
-
return event.type.startsWith("goal.");
|
|
1719
|
+
function isOutcomeUnknownError(cause) {
|
|
1720
|
+
return typeof cause === "object" && cause !== null && cause.outcomeUnknown === true;
|
|
1542
1721
|
}
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1722
|
+
|
|
1723
|
+
// src/hooks/use-human-input.ts
|
|
1724
|
+
import { useCallback as useCallback4, useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
|
|
1725
|
+
function isHumanInputEvent(event) {
|
|
1726
|
+
return event.type === "session.humanInput.requested" || event.type === "user.humanInputResponse" || event.type === "turn.completed" || event.type === "turn.failed" || event.type === "turn.cancelled";
|
|
1546
1727
|
}
|
|
1547
|
-
function
|
|
1548
|
-
const { client, workspaceId } =
|
|
1728
|
+
function useHumanInputRequests(sessionId, options = {}) {
|
|
1729
|
+
const { client, workspaceId, registerSessionReconciler } = useEmbeddedHumanInputSession(options);
|
|
1549
1730
|
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
1550
|
-
const
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
const
|
|
1555
|
-
const [error, setError] = useState5(null);
|
|
1556
|
-
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
1557
|
-
const generation = useRef4(0);
|
|
1558
|
-
const targetKeyRef = useRef4(null);
|
|
1559
|
-
const load = useCallback5(async () => {
|
|
1560
|
-
if (!sessionId) {
|
|
1561
|
-
return;
|
|
1562
|
-
}
|
|
1563
|
-
const ticket = ++generation.current;
|
|
1564
|
-
try {
|
|
1565
|
-
const fetched = await client.getGoal(workspaceId, sessionId);
|
|
1566
|
-
if (ticket === generation.current) {
|
|
1567
|
-
setGoal(fetched);
|
|
1568
|
-
setError(null);
|
|
1569
|
-
setLoading(false);
|
|
1570
|
-
}
|
|
1571
|
-
} catch (cause) {
|
|
1572
|
-
if (ticket !== generation.current) {
|
|
1573
|
-
return;
|
|
1574
|
-
}
|
|
1575
|
-
if (cause instanceof OpenGeniApiError && cause.status === 404) {
|
|
1576
|
-
setGoal(null);
|
|
1577
|
-
setError(null);
|
|
1578
|
-
} else {
|
|
1579
|
-
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
1580
|
-
}
|
|
1581
|
-
setLoading(false);
|
|
1582
|
-
}
|
|
1583
|
-
}, [client, workspaceId, sessionId]);
|
|
1584
|
-
useEffect5(() => {
|
|
1585
|
-
const targetKey = `${workspaceId} ${sessionId ?? ""}`;
|
|
1586
|
-
if (targetKeyRef.current !== targetKey) {
|
|
1587
|
-
targetKeyRef.current = targetKey;
|
|
1588
|
-
setGoal(null);
|
|
1589
|
-
setError(null);
|
|
1590
|
-
}
|
|
1591
|
-
if (!enabled || !pageLive) {
|
|
1592
|
-
setLoading(false);
|
|
1593
|
-
return;
|
|
1594
|
-
}
|
|
1595
|
-
if (sharedFeed) {
|
|
1596
|
-
setLoading(false);
|
|
1597
|
-
return () => {
|
|
1598
|
-
generation.current += 1;
|
|
1599
|
-
};
|
|
1600
|
-
}
|
|
1601
|
-
setLoading(true);
|
|
1602
|
-
const pollIntervalMs = options.pollIntervalMs;
|
|
1603
|
-
if (pollIntervalMs === void 0 || pollIntervalMs <= 0) {
|
|
1604
|
-
void load();
|
|
1605
|
-
return () => {
|
|
1606
|
-
generation.current += 1;
|
|
1607
|
-
};
|
|
1608
|
-
}
|
|
1609
|
-
let cancelled = false;
|
|
1610
|
-
let timer = null;
|
|
1611
|
-
const schedule = () => {
|
|
1612
|
-
if (cancelled) return;
|
|
1613
|
-
timer = setTimeout(() => {
|
|
1614
|
-
timer = null;
|
|
1615
|
-
void load().finally(schedule);
|
|
1616
|
-
}, pollIntervalMs);
|
|
1617
|
-
};
|
|
1618
|
-
void load().finally(schedule);
|
|
1619
|
-
return () => {
|
|
1620
|
-
cancelled = true;
|
|
1621
|
-
if (timer !== null) clearTimeout(timer);
|
|
1622
|
-
generation.current += 1;
|
|
1623
|
-
};
|
|
1624
|
-
}, [load, enabled, pageLive, workspaceId, sessionId, options.pollIntervalMs, sharedFeed]);
|
|
1625
|
-
const scheduleRefresh = useDebouncedCallback(() => void load());
|
|
1626
|
-
useSessionEventTrigger(client, workspaceId, sessionId, isGoalRefreshEvent, scheduleRefresh, {
|
|
1731
|
+
const load = useCallback4(
|
|
1732
|
+
async () => sessionId ? await client.listHumanInputRequests(workspaceId, sessionId, { status: "pending" }) : [],
|
|
1733
|
+
[client, sessionId, workspaceId]
|
|
1734
|
+
);
|
|
1735
|
+
const state = usePolledValue(load, {
|
|
1627
1736
|
enabled,
|
|
1628
|
-
|
|
1737
|
+
pollIntervalMs: options.pollIntervalMs
|
|
1629
1738
|
});
|
|
1630
|
-
const
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1739
|
+
const loadedRequests = state.data;
|
|
1740
|
+
const refreshRequests = state.refresh;
|
|
1741
|
+
const mutation = useMutationRunner(`${workspaceId}\0${sessionId ?? ""}`);
|
|
1742
|
+
const [respondingRequestId, setRespondingRequestId] = useState4(null);
|
|
1743
|
+
const [deadlineVersion, setDeadlineVersion] = useState4(0);
|
|
1744
|
+
const respondingRequestRef = useRef4(null);
|
|
1745
|
+
useEffect4(() => {
|
|
1746
|
+
respondingRequestRef.current = null;
|
|
1747
|
+
setRespondingRequestId(null);
|
|
1748
|
+
}, [sessionId, workspaceId]);
|
|
1749
|
+
useEffect4(() => {
|
|
1750
|
+
if (!sessionId || !enabled) return;
|
|
1751
|
+
return registerSessionReconciler(sessionId, "human-input", state.refresh);
|
|
1752
|
+
}, [enabled, registerSessionReconciler, sessionId, state.refresh]);
|
|
1753
|
+
const scheduleRefresh = useDebouncedCallback(() => void state.refresh());
|
|
1754
|
+
useSessionEventTrigger(client, workspaceId, sessionId, isHumanInputEvent, scheduleRefresh, {
|
|
1755
|
+
enabled,
|
|
1756
|
+
...options.events !== void 0 ? { events: options.events } : {}
|
|
1757
|
+
});
|
|
1758
|
+
useEffect4(() => {
|
|
1759
|
+
const now2 = Date.now();
|
|
1760
|
+
const nextDeadline = (loadedRequests ?? []).reduce((next, request) => {
|
|
1761
|
+
if (request.status !== "pending" || !request.expiresAt) return next;
|
|
1762
|
+
const deadline = Date.parse(request.expiresAt);
|
|
1763
|
+
if (!Number.isFinite(deadline) || deadline <= now2) return next;
|
|
1764
|
+
return next === null ? deadline : Math.min(next, deadline);
|
|
1765
|
+
}, null);
|
|
1766
|
+
if (nextDeadline === null) return;
|
|
1767
|
+
const timer = globalThis.setTimeout(
|
|
1768
|
+
() => {
|
|
1769
|
+
setDeadlineVersion((current) => current + 1);
|
|
1770
|
+
void refreshRequests();
|
|
1771
|
+
},
|
|
1772
|
+
Math.min(2147483647, Math.max(0, nextDeadline - now2 + 1))
|
|
1773
|
+
);
|
|
1774
|
+
return () => globalThis.clearTimeout(timer);
|
|
1775
|
+
}, [loadedRequests, refreshRequests]);
|
|
1776
|
+
const respond = useCallback4(
|
|
1777
|
+
async (requestId, response) => {
|
|
1778
|
+
if (!sessionId || respondingRequestRef.current !== null) return null;
|
|
1779
|
+
respondingRequestRef.current = requestId;
|
|
1780
|
+
setRespondingRequestId(requestId);
|
|
1781
|
+
try {
|
|
1782
|
+
const event = await mutation.run(
|
|
1783
|
+
() => client.submitHumanInputResponse(workspaceId, sessionId, requestId, response, {
|
|
1784
|
+
clientEventId: globalThis.crypto.randomUUID()
|
|
1785
|
+
})
|
|
1786
|
+
);
|
|
1787
|
+
if (event) await state.refresh();
|
|
1788
|
+
return event;
|
|
1789
|
+
} finally {
|
|
1790
|
+
if (respondingRequestRef.current === requestId) {
|
|
1791
|
+
respondingRequestRef.current = null;
|
|
1792
|
+
setRespondingRequestId(null);
|
|
1793
|
+
}
|
|
1643
1794
|
}
|
|
1644
|
-
return result;
|
|
1645
1795
|
},
|
|
1646
|
-
[client,
|
|
1796
|
+
[client, mutation, sessionId, state, workspaceId]
|
|
1647
1797
|
);
|
|
1648
|
-
const
|
|
1649
|
-
|
|
1650
|
-
return null;
|
|
1651
|
-
}
|
|
1652
|
-
const result = await run(() => client.updateGoal(workspaceId, sessionId, { status: "active" }));
|
|
1653
|
-
if (result) {
|
|
1654
|
-
setGoal(result);
|
|
1655
|
-
}
|
|
1656
|
-
return result;
|
|
1657
|
-
}, [client, workspaceId, sessionId, run]);
|
|
1658
|
-
const clearGoal = useCallback5(async () => {
|
|
1659
|
-
if (!sessionId) {
|
|
1660
|
-
return;
|
|
1661
|
-
}
|
|
1662
|
-
const ok = await run(async () => {
|
|
1663
|
-
await client.deleteGoal(workspaceId, sessionId);
|
|
1664
|
-
return true;
|
|
1665
|
-
});
|
|
1666
|
-
if (ok) {
|
|
1667
|
-
generation.current += 1;
|
|
1668
|
-
setGoal(null);
|
|
1669
|
-
setError(null);
|
|
1670
|
-
}
|
|
1671
|
-
}, [client, workspaceId, sessionId, run]);
|
|
1798
|
+
const now = Date.now();
|
|
1799
|
+
void deadlineVersion;
|
|
1672
1800
|
return {
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
deleteGoal: clearGoal,
|
|
1684
|
-
updating: mutating,
|
|
1685
|
-
mutationError,
|
|
1686
|
-
clearMutationError
|
|
1801
|
+
requests: [...state.data ?? []].filter((request) => isActionableHumanInputRequest(request, now)).sort(
|
|
1802
|
+
(left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id)
|
|
1803
|
+
),
|
|
1804
|
+
loading: state.loading,
|
|
1805
|
+
error: state.error,
|
|
1806
|
+
refresh: state.refresh,
|
|
1807
|
+
respond,
|
|
1808
|
+
respondingRequestId,
|
|
1809
|
+
mutationError: mutation.mutationError,
|
|
1810
|
+
clearMutationError: mutation.clearMutationError
|
|
1687
1811
|
};
|
|
1688
1812
|
}
|
|
1689
1813
|
|
|
1690
|
-
// src/hooks/use-
|
|
1691
|
-
import { useCallback as
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
const
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1814
|
+
// src/hooks/use-file-attachments.ts
|
|
1815
|
+
import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef5, useState as useState5 } from "react";
|
|
1816
|
+
var isImage = (file) => file.type.startsWith("image/");
|
|
1817
|
+
var fallbackAttachmentId = 0;
|
|
1818
|
+
function createAttachmentId() {
|
|
1819
|
+
const cryptoSource = globalThis.crypto;
|
|
1820
|
+
if (typeof cryptoSource?.randomUUID === "function") return cryptoSource.randomUUID();
|
|
1821
|
+
fallbackAttachmentId += 1;
|
|
1822
|
+
return `attachment:${Date.now().toString(36)}:${fallbackAttachmentId.toString(36)}`;
|
|
1823
|
+
}
|
|
1824
|
+
function secureContextRequiredErrorCode(error) {
|
|
1825
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "secure_context_required" ? error.code : void 0;
|
|
1826
|
+
}
|
|
1827
|
+
function useFileAttachments(options = {}) {
|
|
1828
|
+
const { client, workspaceId } = useEmbeddedFileAttachments(options);
|
|
1829
|
+
const formatError = useErrorMessage();
|
|
1830
|
+
const pasteFilter = options.pasteFilter ?? isImage;
|
|
1831
|
+
const [attachments, setAttachments] = useState5([]);
|
|
1832
|
+
const attachmentsRef = useRef5(attachments);
|
|
1833
|
+
attachmentsRef.current = attachments;
|
|
1834
|
+
const sources = useRef5(/* @__PURE__ */ new Map());
|
|
1835
|
+
const previewUrls = useRef5(/* @__PURE__ */ new Map());
|
|
1836
|
+
const previewRetainers = useRef5(/* @__PURE__ */ new Map());
|
|
1837
|
+
const pendingPreviewRevocations = useRef5(/* @__PURE__ */ new Set());
|
|
1838
|
+
const revokePreview = useCallback5((id) => {
|
|
1839
|
+
const previewUrl = previewUrls.current.get(id);
|
|
1840
|
+
if (!previewUrl) return;
|
|
1841
|
+
if ((previewRetainers.current.get(id) ?? 0) > 0) {
|
|
1842
|
+
pendingPreviewRevocations.current.add(id);
|
|
1843
|
+
return;
|
|
1699
1844
|
}
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
}
|
|
1707
|
-
const
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1845
|
+
previewUrls.current.delete(id);
|
|
1846
|
+
pendingPreviewRevocations.current.delete(id);
|
|
1847
|
+
URL.revokeObjectURL(previewUrl);
|
|
1848
|
+
}, []);
|
|
1849
|
+
const revokeAllPreviews = useCallback5(() => {
|
|
1850
|
+
for (const id of previewUrls.current.keys()) revokePreview(id);
|
|
1851
|
+
}, [revokePreview]);
|
|
1852
|
+
const retainPreview = useCallback5(
|
|
1853
|
+
(id) => {
|
|
1854
|
+
if (!previewUrls.current.has(id)) return void 0;
|
|
1855
|
+
previewRetainers.current.set(id, (previewRetainers.current.get(id) ?? 0) + 1);
|
|
1856
|
+
let released = false;
|
|
1857
|
+
return () => {
|
|
1858
|
+
if (released) return;
|
|
1859
|
+
released = true;
|
|
1860
|
+
const remaining = (previewRetainers.current.get(id) ?? 1) - 1;
|
|
1861
|
+
if (remaining > 0) {
|
|
1862
|
+
previewRetainers.current.set(id, remaining);
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
previewRetainers.current.delete(id);
|
|
1866
|
+
if (pendingPreviewRevocations.current.has(id)) revokePreview(id);
|
|
1867
|
+
};
|
|
1721
1868
|
},
|
|
1722
|
-
[
|
|
1869
|
+
[revokePreview]
|
|
1723
1870
|
);
|
|
1724
|
-
const
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1871
|
+
const committedAttachmentIds = useRef5(/* @__PURE__ */ new Set());
|
|
1872
|
+
useEffect5(() => {
|
|
1873
|
+
const currentIds = new Set(attachments.map((attachment) => attachment.id));
|
|
1874
|
+
for (const id of committedAttachmentIds.current) {
|
|
1875
|
+
if (!currentIds.has(id)) revokePreview(id);
|
|
1876
|
+
}
|
|
1877
|
+
committedAttachmentIds.current = currentIds;
|
|
1878
|
+
}, [attachments, revokePreview]);
|
|
1879
|
+
const scopeGeneration = useRef5(0);
|
|
1880
|
+
const previousScope = useRef5({ client, workspaceId });
|
|
1881
|
+
useEffect5(() => {
|
|
1882
|
+
const previous = previousScope.current;
|
|
1883
|
+
if (previous.client === client && previous.workspaceId === workspaceId) return;
|
|
1884
|
+
previousScope.current = { client, workspaceId };
|
|
1885
|
+
scopeGeneration.current += 1;
|
|
1886
|
+
sources.current.clear();
|
|
1887
|
+
revokeAllPreviews();
|
|
1888
|
+
setAttachments([]);
|
|
1889
|
+
}, [client, revokeAllPreviews, workspaceId]);
|
|
1890
|
+
useEffect5(
|
|
1891
|
+
() => () => {
|
|
1892
|
+
scopeGeneration.current += 1;
|
|
1893
|
+
sources.current.clear();
|
|
1894
|
+
revokeAllPreviews();
|
|
1730
1895
|
},
|
|
1731
|
-
[
|
|
1896
|
+
[revokeAllPreviews]
|
|
1732
1897
|
);
|
|
1733
|
-
const
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1898
|
+
const startUpload = useCallback5(
|
|
1899
|
+
(id, file) => {
|
|
1900
|
+
const generation = scopeGeneration.current;
|
|
1901
|
+
void client.uploadFile(workspaceId, {
|
|
1902
|
+
filename: file.name || "file",
|
|
1903
|
+
contentType: file.type || "application/octet-stream",
|
|
1904
|
+
data: file,
|
|
1905
|
+
...options.scope ? { scope: options.scope } : {}
|
|
1906
|
+
}).then((asset) => {
|
|
1907
|
+
if (scopeGeneration.current !== generation) return;
|
|
1908
|
+
sources.current.delete(id);
|
|
1909
|
+
setAttachments(
|
|
1910
|
+
(current) => current.map(
|
|
1911
|
+
(attachment) => attachment.id === id ? {
|
|
1912
|
+
...attachment,
|
|
1913
|
+
status: "ready",
|
|
1914
|
+
file: asset,
|
|
1915
|
+
name: asset.filename,
|
|
1916
|
+
contentType: asset.contentType,
|
|
1917
|
+
sizeBytes: asset.sizeBytes,
|
|
1918
|
+
errorCode: void 0,
|
|
1919
|
+
error: void 0,
|
|
1920
|
+
errorCause: void 0
|
|
1921
|
+
} : attachment
|
|
1922
|
+
)
|
|
1923
|
+
);
|
|
1924
|
+
}).catch((error) => {
|
|
1925
|
+
if (scopeGeneration.current !== generation) return;
|
|
1926
|
+
setAttachments(
|
|
1927
|
+
(current) => current.map(
|
|
1928
|
+
(attachment) => attachment.id === id ? {
|
|
1929
|
+
...attachment,
|
|
1930
|
+
status: "failed",
|
|
1931
|
+
errorCode: secureContextRequiredErrorCode(error),
|
|
1932
|
+
error: formatError(error),
|
|
1933
|
+
errorCause: error
|
|
1934
|
+
} : attachment
|
|
1935
|
+
)
|
|
1936
|
+
);
|
|
1937
|
+
});
|
|
1938
|
+
},
|
|
1939
|
+
[client, workspaceId, options.scope, formatError]
|
|
1940
|
+
);
|
|
1941
|
+
const addFiles = useCallback5(
|
|
1942
|
+
(files) => {
|
|
1943
|
+
for (const file of files) {
|
|
1944
|
+
const id = createAttachmentId();
|
|
1945
|
+
sources.current.set(id, file);
|
|
1946
|
+
const previewUrl = isImage(file) ? URL.createObjectURL(file) : void 0;
|
|
1947
|
+
if (previewUrl) previewUrls.current.set(id, previewUrl);
|
|
1948
|
+
setAttachments((current) => [
|
|
1949
|
+
...current,
|
|
1950
|
+
{
|
|
1951
|
+
id,
|
|
1952
|
+
name: file.name || "image",
|
|
1953
|
+
contentType: file.type || "application/octet-stream",
|
|
1954
|
+
sizeBytes: file.size,
|
|
1955
|
+
status: "uploading",
|
|
1956
|
+
...previewUrl ? { previewUrl } : {}
|
|
1957
|
+
}
|
|
1958
|
+
]);
|
|
1959
|
+
startUpload(id, file);
|
|
1960
|
+
}
|
|
1961
|
+
},
|
|
1962
|
+
[startUpload]
|
|
1963
|
+
);
|
|
1964
|
+
const retry = useCallback5(
|
|
1965
|
+
(id) => {
|
|
1966
|
+
const file = sources.current.get(id);
|
|
1967
|
+
if (!file) {
|
|
1968
|
+
return;
|
|
1969
|
+
}
|
|
1970
|
+
setAttachments(
|
|
1971
|
+
(current) => current.map(
|
|
1972
|
+
(attachment) => attachment.id === id ? {
|
|
1973
|
+
...attachment,
|
|
1974
|
+
status: "uploading",
|
|
1975
|
+
errorCode: void 0,
|
|
1976
|
+
error: void 0,
|
|
1977
|
+
errorCause: void 0
|
|
1978
|
+
} : attachment
|
|
1979
|
+
)
|
|
1980
|
+
);
|
|
1981
|
+
startUpload(id, file);
|
|
1982
|
+
},
|
|
1983
|
+
[startUpload]
|
|
1984
|
+
);
|
|
1985
|
+
const addFromPaste = useCallback5(
|
|
1986
|
+
(event) => {
|
|
1987
|
+
const clipboardFiles = event.clipboardData?.files;
|
|
1988
|
+
if (!clipboardFiles) {
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1991
|
+
const files = [...clipboardFiles].filter(pasteFilter);
|
|
1992
|
+
if (files.length > 0) {
|
|
1993
|
+
addFiles(files);
|
|
1994
|
+
}
|
|
1995
|
+
},
|
|
1996
|
+
[addFiles, pasteFilter]
|
|
1997
|
+
);
|
|
1998
|
+
const restoreReadyFiles = useCallback5(
|
|
1999
|
+
(files) => {
|
|
2000
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
2001
|
+
for (const file of files) {
|
|
2002
|
+
if (file.status === "ready" && file.workspaceId === workspaceId) {
|
|
2003
|
+
incoming.set(file.id, file);
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
setAttachments((current) => {
|
|
2007
|
+
const unresolved = current.filter((attachment) => attachment.status !== "ready");
|
|
2008
|
+
const existingReady = new Map(
|
|
2009
|
+
current.flatMap(
|
|
2010
|
+
(attachment) => attachment.status === "ready" && attachment.file ? [[attachment.file.id, attachment]] : []
|
|
2011
|
+
)
|
|
2012
|
+
);
|
|
2013
|
+
const restored = [...incoming.values()].map((file) => {
|
|
2014
|
+
const existing = existingReady.get(file.id);
|
|
2015
|
+
return existing ? {
|
|
2016
|
+
...existing,
|
|
2017
|
+
name: file.filename,
|
|
2018
|
+
contentType: file.contentType,
|
|
2019
|
+
sizeBytes: file.sizeBytes,
|
|
2020
|
+
status: "ready",
|
|
2021
|
+
file,
|
|
2022
|
+
errorCode: void 0,
|
|
2023
|
+
error: void 0,
|
|
2024
|
+
errorCause: void 0
|
|
2025
|
+
} : {
|
|
2026
|
+
id: `restored:${file.id}`,
|
|
2027
|
+
name: file.filename,
|
|
2028
|
+
contentType: file.contentType,
|
|
2029
|
+
sizeBytes: file.sizeBytes,
|
|
2030
|
+
status: "ready",
|
|
2031
|
+
file
|
|
2032
|
+
// No source File and no object URL: server metadata is the
|
|
2033
|
+
// only authority restored across page/device boundaries.
|
|
2034
|
+
};
|
|
2035
|
+
});
|
|
2036
|
+
return [...unresolved, ...restored];
|
|
2037
|
+
});
|
|
2038
|
+
},
|
|
2039
|
+
[workspaceId]
|
|
2040
|
+
);
|
|
2041
|
+
const loadPreview = useCallback5(
|
|
2042
|
+
async (id, signal) => {
|
|
2043
|
+
const generation = scopeGeneration.current;
|
|
2044
|
+
const attachment = attachmentsRef.current.find((candidate) => candidate.id === id);
|
|
2045
|
+
const createDownloadUrl = client.createFileDownloadUrl;
|
|
2046
|
+
if (!attachment || attachment.status !== "ready" || !attachment.file || !attachment.contentType.startsWith("image/") || typeof createDownloadUrl !== "function" || signal?.aborted) {
|
|
2047
|
+
return void 0;
|
|
2048
|
+
}
|
|
2049
|
+
const fileId = attachment.file.id;
|
|
2050
|
+
const signed = await createDownloadUrl.call(client, workspaceId, fileId, { signal });
|
|
2051
|
+
if (scopeGeneration.current !== generation || signal?.aborted) return void 0;
|
|
2052
|
+
const current = attachmentsRef.current.find((candidate) => candidate.id === id);
|
|
2053
|
+
if (current?.status !== "ready" || current.file?.id !== fileId) return void 0;
|
|
2054
|
+
return signed.url;
|
|
2055
|
+
},
|
|
2056
|
+
[client, workspaceId]
|
|
2057
|
+
);
|
|
2058
|
+
const remove = useCallback5(
|
|
2059
|
+
(id) => {
|
|
2060
|
+
sources.current.delete(id);
|
|
2061
|
+
revokePreview(id);
|
|
2062
|
+
setAttachments((current) => current.filter((attachment) => attachment.id !== id));
|
|
2063
|
+
},
|
|
2064
|
+
[revokePreview]
|
|
2065
|
+
);
|
|
2066
|
+
const removeReadyFiles = useCallback5((fileIds) => {
|
|
2067
|
+
const accepted = new Set(fileIds);
|
|
2068
|
+
if (accepted.size === 0) return;
|
|
2069
|
+
setAttachments(
|
|
2070
|
+
(current) => current.filter((attachment) => {
|
|
2071
|
+
return !(attachment.status === "ready" && attachment.file !== void 0 && accepted.has(attachment.file.id));
|
|
2072
|
+
})
|
|
2073
|
+
);
|
|
2074
|
+
}, []);
|
|
2075
|
+
const clear = useCallback5(() => {
|
|
2076
|
+
sources.current.clear();
|
|
2077
|
+
revokeAllPreviews();
|
|
2078
|
+
setAttachments([]);
|
|
2079
|
+
}, [revokeAllPreviews]);
|
|
2080
|
+
return {
|
|
2081
|
+
attachments,
|
|
2082
|
+
readyResources: attachments.flatMap(
|
|
2083
|
+
(attachment) => attachment.status === "ready" && attachment.file ? [{ kind: "file", fileId: attachment.file.id }] : []
|
|
2084
|
+
),
|
|
2085
|
+
uploading: attachments.some((attachment) => attachment.status === "uploading"),
|
|
2086
|
+
hasUnresolved: attachments.some((attachment) => attachment.status !== "ready"),
|
|
2087
|
+
addFiles,
|
|
2088
|
+
addFromPaste,
|
|
2089
|
+
restoreReadyFiles,
|
|
2090
|
+
...typeof client.createFileDownloadUrl === "function" ? { loadPreview } : {},
|
|
2091
|
+
retry,
|
|
2092
|
+
retainPreview,
|
|
2093
|
+
remove,
|
|
2094
|
+
removeReadyFiles,
|
|
2095
|
+
clear
|
|
2096
|
+
};
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
// src/hooks/use-session-control.ts
|
|
2100
|
+
import { useCallback as useCallback6, useLayoutEffect as useLayoutEffect2, useRef as useRef6 } from "react";
|
|
2101
|
+
function useSessionControl(sessionId, options = {}) {
|
|
2102
|
+
const { client, workspaceId } = useEmbeddedSession(options);
|
|
2103
|
+
const approvalTargetKey = `${workspaceId}\0${sessionId ?? ""}`;
|
|
2104
|
+
const pendingApproval = useRef6(null);
|
|
2105
|
+
useLayoutEffect2(() => {
|
|
2106
|
+
if (pendingApproval.current?.targetKey !== approvalTargetKey) {
|
|
2107
|
+
pendingApproval.current = null;
|
|
2108
|
+
}
|
|
2109
|
+
}, [approvalTargetKey]);
|
|
2110
|
+
const {
|
|
2111
|
+
run: runControl,
|
|
2112
|
+
mutating: controlling,
|
|
2113
|
+
mutationError: controlError,
|
|
2114
|
+
clearMutationError: clearControlError
|
|
2115
|
+
} = useMutationRunner(approvalTargetKey);
|
|
2116
|
+
const {
|
|
2117
|
+
run: runApproval,
|
|
2118
|
+
mutating: responding,
|
|
2119
|
+
mutationError: approvalError,
|
|
2120
|
+
clearMutationError: clearApprovalError
|
|
2121
|
+
} = useMutationRunner(approvalTargetKey);
|
|
2122
|
+
const pause = useCallback6(
|
|
2123
|
+
async (reason) => {
|
|
2124
|
+
if (!sessionId) {
|
|
2125
|
+
return null;
|
|
2126
|
+
}
|
|
2127
|
+
return await runControl(
|
|
2128
|
+
() => client.pauseSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
|
|
2129
|
+
);
|
|
2130
|
+
},
|
|
2131
|
+
[client, workspaceId, sessionId, runControl]
|
|
2132
|
+
);
|
|
2133
|
+
const resume = useCallback6(
|
|
2134
|
+
async (reason) => {
|
|
2135
|
+
if (!sessionId) return null;
|
|
2136
|
+
return await runControl(
|
|
2137
|
+
() => client.resumeSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
|
|
2138
|
+
);
|
|
2139
|
+
},
|
|
2140
|
+
[client, workspaceId, sessionId, runControl]
|
|
2141
|
+
);
|
|
2142
|
+
const decide = useCallback6(
|
|
2143
|
+
async (approvalId, decision, message) => {
|
|
2144
|
+
if (!sessionId) {
|
|
2145
|
+
return null;
|
|
2146
|
+
}
|
|
2147
|
+
const decisionKey = JSON.stringify([approvalId, decision, message ?? null]);
|
|
2148
|
+
if (pendingApproval.current?.decisionKey !== decisionKey) {
|
|
1740
2149
|
pendingApproval.current = {
|
|
1741
2150
|
targetKey: approvalTargetKey,
|
|
1742
2151
|
decisionKey,
|
|
@@ -1784,193 +2193,10 @@ function useSessionControl(sessionId, options = {}) {
|
|
|
1784
2193
|
};
|
|
1785
2194
|
}
|
|
1786
2195
|
|
|
1787
|
-
// src/hooks/use-session-mcp-approval-policy.ts
|
|
1788
|
-
import { useCallback as useCallback7, useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useMemo as useMemo4, useRef as useRef6, useState as useState6 } from "react";
|
|
1789
|
-
function isSessionMcpApprovalPolicyEvent(event, serverId) {
|
|
1790
|
-
if (event.type !== "session.mcp.approval_policy.updated") {
|
|
1791
|
-
return false;
|
|
1792
|
-
}
|
|
1793
|
-
if (serverId === void 0) {
|
|
1794
|
-
return true;
|
|
1795
|
-
}
|
|
1796
|
-
const payload = event.payload;
|
|
1797
|
-
return payload?.serverId === serverId;
|
|
1798
|
-
}
|
|
1799
|
-
function useSessionMcpApprovalPolicy(sessionId, serverId, options = {}) {
|
|
1800
|
-
const { client, workspaceId } = useEmbeddedSessionMcpApprovalPolicy(options);
|
|
1801
|
-
const enabled = (options.enabled ?? true) && Boolean(sessionId && serverId);
|
|
1802
|
-
const [override, setOverride] = useState6(null);
|
|
1803
|
-
const targetKey = `${workspaceId}\0${sessionId ?? ""}\0${serverId ?? ""}`;
|
|
1804
|
-
const authoritativeGeneration = useRef6(0);
|
|
1805
|
-
const mutationIdentity = useMemo4(() => ({ client, targetKey }), [client, targetKey]);
|
|
1806
|
-
const { run, mutating, mutationError, clearMutationError } = useMutationRunner(mutationIdentity);
|
|
1807
|
-
useLayoutEffect3(() => {
|
|
1808
|
-
authoritativeGeneration.current += 1;
|
|
1809
|
-
setOverride(null);
|
|
1810
|
-
}, [client, targetKey]);
|
|
1811
|
-
const load = useCallback7(async () => {
|
|
1812
|
-
const generation = authoritativeGeneration.current;
|
|
1813
|
-
if (!sessionId || !serverId) {
|
|
1814
|
-
return { targetKey, generation, server: null };
|
|
1815
|
-
}
|
|
1816
|
-
const session = await client.getSession(workspaceId, sessionId);
|
|
1817
|
-
return {
|
|
1818
|
-
targetKey,
|
|
1819
|
-
generation,
|
|
1820
|
-
server: session.mcpServers.find((server2) => server2.id === serverId) ?? null
|
|
1821
|
-
};
|
|
1822
|
-
}, [client, serverId, sessionId, targetKey, workspaceId]);
|
|
1823
|
-
const {
|
|
1824
|
-
data,
|
|
1825
|
-
loading,
|
|
1826
|
-
error: loadError,
|
|
1827
|
-
refresh
|
|
1828
|
-
} = usePolledValue(load, {
|
|
1829
|
-
enabled,
|
|
1830
|
-
pollIntervalMs: options.pollIntervalMs
|
|
1831
|
-
});
|
|
1832
|
-
useEffect6(() => {
|
|
1833
|
-
if (data?.targetKey === targetKey && data.generation === authoritativeGeneration.current) {
|
|
1834
|
-
setOverride(null);
|
|
1835
|
-
}
|
|
1836
|
-
}, [data, targetKey]);
|
|
1837
|
-
const scheduleRefresh = useDebouncedCallback(() => void refresh());
|
|
1838
|
-
const matchesPolicyEvent = useCallback7(
|
|
1839
|
-
(event) => isSessionMcpApprovalPolicyEvent(event, serverId ?? void 0),
|
|
1840
|
-
[serverId]
|
|
1841
|
-
);
|
|
1842
|
-
useSessionEventTrigger(
|
|
1843
|
-
client,
|
|
1844
|
-
workspaceId,
|
|
1845
|
-
sessionId,
|
|
1846
|
-
matchesPolicyEvent,
|
|
1847
|
-
scheduleRefresh,
|
|
1848
|
-
{
|
|
1849
|
-
enabled,
|
|
1850
|
-
...options.events !== void 0 ? { events: options.events } : {}
|
|
1851
|
-
},
|
|
1852
|
-
async () => await refresh()
|
|
1853
|
-
);
|
|
1854
|
-
const update = useCallback7(
|
|
1855
|
-
async (policy) => {
|
|
1856
|
-
if (!sessionId || !serverId) {
|
|
1857
|
-
return null;
|
|
1858
|
-
}
|
|
1859
|
-
const response = await run(
|
|
1860
|
-
() => client.updateSessionMcpApprovalPolicy(workspaceId, sessionId, serverId, {
|
|
1861
|
-
requireApproval: policy
|
|
1862
|
-
})
|
|
1863
|
-
);
|
|
1864
|
-
if (response) {
|
|
1865
|
-
authoritativeGeneration.current += 1;
|
|
1866
|
-
setOverride(response.server);
|
|
1867
|
-
void refresh();
|
|
1868
|
-
}
|
|
1869
|
-
return response;
|
|
1870
|
-
},
|
|
1871
|
-
[client, refresh, run, serverId, sessionId, workspaceId]
|
|
1872
|
-
);
|
|
1873
|
-
const server = override ?? data?.server ?? null;
|
|
1874
|
-
return {
|
|
1875
|
-
server,
|
|
1876
|
-
policy: server?.requireApproval ?? null,
|
|
1877
|
-
loading,
|
|
1878
|
-
error: mutationError ?? loadError,
|
|
1879
|
-
refresh: async () => {
|
|
1880
|
-
await refresh();
|
|
1881
|
-
},
|
|
1882
|
-
update,
|
|
1883
|
-
updating: mutating,
|
|
1884
|
-
clearError: clearMutationError
|
|
1885
|
-
};
|
|
1886
|
-
}
|
|
1887
|
-
|
|
1888
|
-
// src/hooks/use-session-lineage.ts
|
|
1889
|
-
import { useCallback as useCallback8, useEffect as useEffect7, useRef as useRef7 } from "react";
|
|
1890
|
-
function isLineageRefreshEvent(event) {
|
|
1891
|
-
if (event.type === "session.status.changed" || event.type === "session.created") {
|
|
1892
|
-
return true;
|
|
1893
|
-
}
|
|
1894
|
-
if (event.type === "agent.toolCall.output") {
|
|
1895
|
-
const payload = event.payload;
|
|
1896
|
-
const name = typeof payload?.name === "string" ? payload.name : typeof payload?.toolName === "string" ? payload.toolName : "";
|
|
1897
|
-
return name === "session_create" || name.endsWith("__session_create");
|
|
1898
|
-
}
|
|
1899
|
-
return false;
|
|
1900
|
-
}
|
|
1901
|
-
function isSessionCreateToolCallCreated(event) {
|
|
1902
|
-
if (event.type !== "agent.toolCall.created") {
|
|
1903
|
-
return false;
|
|
1904
|
-
}
|
|
1905
|
-
const payload = event.payload;
|
|
1906
|
-
const name = typeof payload?.name === "string" ? payload.name : typeof payload?.toolName === "string" ? payload.toolName : "";
|
|
1907
|
-
return name === "session_create" || name.endsWith("__session_create");
|
|
1908
|
-
}
|
|
1909
|
-
function useSessionLineage(sessionId, options = {}) {
|
|
1910
|
-
const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } = useEmbeddedSessionLineage(options);
|
|
1911
|
-
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
1912
|
-
const load = useCallback8(
|
|
1913
|
-
async () => sessionId ? await client.getSessionLineage(workspaceId, sessionId) : { ancestors: [], children: [], truncated: false },
|
|
1914
|
-
[client, workspaceId, sessionId]
|
|
1915
|
-
);
|
|
1916
|
-
const state = usePolledValue(load, { pollIntervalMs: options.pollIntervalMs, enabled });
|
|
1917
|
-
const refresh = state.refresh;
|
|
1918
|
-
useEffect7(() => {
|
|
1919
|
-
if (enabled && workspaceControlEvent) void refresh();
|
|
1920
|
-
}, [enabled, refresh, workspaceControlEvent]);
|
|
1921
|
-
useEffect7(() => {
|
|
1922
|
-
if (!sessionId || !enabled) return;
|
|
1923
|
-
return registerSessionReconciler(sessionId, "lineage", refresh);
|
|
1924
|
-
}, [enabled, refresh, registerSessionReconciler, sessionId]);
|
|
1925
|
-
const refreshSoon = useDebouncedCallback(() => void refresh(), 150);
|
|
1926
|
-
const delayedChildRefreshRef = useRef7(null);
|
|
1927
|
-
useEffect7(() => {
|
|
1928
|
-
return () => {
|
|
1929
|
-
if (delayedChildRefreshRef.current !== null) {
|
|
1930
|
-
clearTimeout(delayedChildRefreshRef.current);
|
|
1931
|
-
delayedChildRefreshRef.current = null;
|
|
1932
|
-
}
|
|
1933
|
-
};
|
|
1934
|
-
}, [sessionId, workspaceId]);
|
|
1935
|
-
const refreshAfterChildCreate = useCallback8(() => {
|
|
1936
|
-
void refresh();
|
|
1937
|
-
if (delayedChildRefreshRef.current !== null) {
|
|
1938
|
-
clearTimeout(delayedChildRefreshRef.current);
|
|
1939
|
-
}
|
|
1940
|
-
delayedChildRefreshRef.current = setTimeout(() => {
|
|
1941
|
-
delayedChildRefreshRef.current = null;
|
|
1942
|
-
void refresh();
|
|
1943
|
-
}, 2500);
|
|
1944
|
-
}, [refresh]);
|
|
1945
|
-
useSessionEventTrigger(
|
|
1946
|
-
client,
|
|
1947
|
-
workspaceId,
|
|
1948
|
-
sessionId,
|
|
1949
|
-
isLineageRefreshEvent,
|
|
1950
|
-
refreshSoon,
|
|
1951
|
-
// SHARED-FEED ONLY: without a caller-provided events log the trigger would
|
|
1952
|
-
// open its OWN streamEvents tail — a second live SSE connection next to the
|
|
1953
|
-
// session route's useSessionEvents. A caller with no feed opts into polling
|
|
1954
|
-
// (pollIntervalMs), never a duplicate stream.
|
|
1955
|
-
{ events: options.events, enabled: enabled && options.events !== void 0 }
|
|
1956
|
-
);
|
|
1957
|
-
useSessionEventTrigger(
|
|
1958
|
-
client,
|
|
1959
|
-
workspaceId,
|
|
1960
|
-
sessionId,
|
|
1961
|
-
isSessionCreateToolCallCreated,
|
|
1962
|
-
refreshAfterChildCreate,
|
|
1963
|
-
{ events: options.events, enabled: enabled && options.events !== void 0 }
|
|
1964
|
-
);
|
|
1965
|
-
return {
|
|
1966
|
-
lineage: state.data,
|
|
1967
|
-
loading: state.loading,
|
|
1968
|
-
error: state.error,
|
|
1969
|
-
refresh
|
|
1970
|
-
};
|
|
1971
|
-
}
|
|
1972
|
-
|
|
1973
2196
|
// src/approvals.ts
|
|
2197
|
+
import {
|
|
2198
|
+
parseToolDisplayMetadata
|
|
2199
|
+
} from "@opengeni/sdk";
|
|
1974
2200
|
function approvalsFromRequiresAction(payload) {
|
|
1975
2201
|
const approvals = payload && typeof payload === "object" ? payload.approvals : void 0;
|
|
1976
2202
|
if (!Array.isArray(approvals)) {
|
|
@@ -1979,11 +2205,13 @@ function approvalsFromRequiresAction(payload) {
|
|
|
1979
2205
|
return approvals.map((approval, index) => {
|
|
1980
2206
|
const raw = approval && typeof approval === "object" ? approval : {};
|
|
1981
2207
|
const rawItem = raw.rawItem && typeof raw.rawItem === "object" ? raw.rawItem : {};
|
|
2208
|
+
const display = parseToolDisplayMetadata(raw.display);
|
|
1982
2209
|
return {
|
|
1983
2210
|
// Mirror the runtime's canonical interruption identity before retaining
|
|
1984
2211
|
// the legacy top-level callId fallback for already-normalized payloads.
|
|
1985
2212
|
id: String(rawItem.callId ?? rawItem.id ?? raw.id ?? raw.callId ?? raw.name ?? index),
|
|
1986
2213
|
name: String(raw.name ?? raw.toolName ?? rawItem.name ?? "approval"),
|
|
2214
|
+
...display ? { display } : {},
|
|
1987
2215
|
arguments: raw.arguments ?? rawItem.arguments,
|
|
1988
2216
|
raw: approval
|
|
1989
2217
|
};
|
|
@@ -2022,140 +2250,80 @@ function projectPendingApprovals(events) {
|
|
|
2022
2250
|
return pending;
|
|
2023
2251
|
}
|
|
2024
2252
|
|
|
2025
|
-
// src/
|
|
2026
|
-
function
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
if (!isRecord(request) || typeof request.id !== "string" || !Array.isArray(request.questions)) {
|
|
2030
|
-
return null;
|
|
2031
|
-
}
|
|
2032
|
-
return {
|
|
2033
|
-
id: request.id,
|
|
2034
|
-
turnId: event.turnId ?? null,
|
|
2035
|
-
questions: request.questions,
|
|
2036
|
-
allowSkip: request.allowSkip === true,
|
|
2037
|
-
expiresAt: typeof request.expiresAt === "string" ? request.expiresAt : null
|
|
2038
|
-
};
|
|
2039
|
-
}
|
|
2040
|
-
function projectPendingHumanInputRequests(events) {
|
|
2041
|
-
const pending = /* @__PURE__ */ new Map();
|
|
2042
|
-
for (const event of events) {
|
|
2043
|
-
const requested = humanInputRequestFromEvent(event);
|
|
2044
|
-
if (requested) {
|
|
2045
|
-
pending.set(requested.id, requested);
|
|
2046
|
-
continue;
|
|
2047
|
-
}
|
|
2048
|
-
if (event.type === "user.humanInputResponse" && isRecord(event.payload)) {
|
|
2049
|
-
if (typeof event.payload.requestId === "string") pending.delete(event.payload.requestId);
|
|
2050
|
-
continue;
|
|
2051
|
-
}
|
|
2052
|
-
if (event.type === "turn.completed" || event.type === "turn.failed" || event.type === "turn.cancelled") {
|
|
2053
|
-
for (const [id, request] of pending) {
|
|
2054
|
-
if (request.turnId === null || event.turnId === null || event.turnId === void 0 || request.turnId === event.turnId) {
|
|
2055
|
-
pending.delete(id);
|
|
2056
|
-
}
|
|
2057
|
-
}
|
|
2058
|
-
}
|
|
2059
|
-
}
|
|
2060
|
-
return [...pending.values()];
|
|
2061
|
-
}
|
|
2062
|
-
function isRecord(value) {
|
|
2063
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2064
|
-
}
|
|
2065
|
-
|
|
2066
|
-
// src/hooks/use-human-input.ts
|
|
2067
|
-
import { useCallback as useCallback9, useEffect as useEffect8, useRef as useRef8, useState as useState7 } from "react";
|
|
2068
|
-
function isHumanInputEvent(event) {
|
|
2069
|
-
return event.type === "session.humanInput.requested" || event.type === "user.humanInputResponse" || event.type === "turn.completed" || event.type === "turn.failed" || event.type === "turn.cancelled";
|
|
2070
|
-
}
|
|
2071
|
-
function useHumanInputRequests(sessionId, options = {}) {
|
|
2072
|
-
const { client, workspaceId, registerSessionReconciler } = useEmbeddedHumanInputSession(options);
|
|
2073
|
-
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
2074
|
-
const load = useCallback9(
|
|
2075
|
-
async () => sessionId ? await client.listHumanInputRequests(workspaceId, sessionId, { status: "pending" }) : [],
|
|
2076
|
-
[client, sessionId, workspaceId]
|
|
2253
|
+
// src/conversation-timeline.ts
|
|
2254
|
+
function conversationTimeline(items, queue, composer) {
|
|
2255
|
+
const queued = new Set(
|
|
2256
|
+
queue.queue.filter((turn) => turn.metadata.delivery !== "steer").map((turn) => turn.triggerEventId)
|
|
2077
2257
|
);
|
|
2078
|
-
const
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
const [respondingRequestId, setRespondingRequestId] = useState7(null);
|
|
2084
|
-
const respondingRequestRef = useRef8(null);
|
|
2085
|
-
useEffect8(() => {
|
|
2086
|
-
respondingRequestRef.current = null;
|
|
2087
|
-
setRespondingRequestId(null);
|
|
2088
|
-
}, [sessionId, workspaceId]);
|
|
2089
|
-
useEffect8(() => {
|
|
2090
|
-
if (!sessionId || !enabled) return;
|
|
2091
|
-
return registerSessionReconciler(sessionId, "human-input", state.refresh);
|
|
2092
|
-
}, [enabled, registerSessionReconciler, sessionId, state.refresh]);
|
|
2093
|
-
const scheduleRefresh = useDebouncedCallback(() => void state.refresh());
|
|
2094
|
-
useSessionEventTrigger(client, workspaceId, sessionId, isHumanInputEvent, scheduleRefresh, {
|
|
2095
|
-
enabled,
|
|
2096
|
-
...options.events !== void 0 ? { events: options.events } : {}
|
|
2097
|
-
});
|
|
2098
|
-
const respond = useCallback9(
|
|
2099
|
-
async (requestId, response) => {
|
|
2100
|
-
if (!sessionId || respondingRequestRef.current !== null) return null;
|
|
2101
|
-
respondingRequestRef.current = requestId;
|
|
2102
|
-
setRespondingRequestId(requestId);
|
|
2103
|
-
try {
|
|
2104
|
-
const event = await mutation.run(
|
|
2105
|
-
() => client.submitHumanInputResponse(workspaceId, sessionId, requestId, response, {
|
|
2106
|
-
clientEventId: globalThis.crypto.randomUUID()
|
|
2107
|
-
})
|
|
2108
|
-
);
|
|
2109
|
-
if (event) await state.refresh();
|
|
2110
|
-
return event;
|
|
2111
|
-
} finally {
|
|
2112
|
-
if (respondingRequestRef.current === requestId) {
|
|
2113
|
-
respondingRequestRef.current = null;
|
|
2114
|
-
setRespondingRequestId(null);
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2117
|
-
},
|
|
2118
|
-
[client, mutation, sessionId, state, workspaceId]
|
|
2258
|
+
const pending = composer.optimisticMessages ?? [];
|
|
2259
|
+
const pendingQueue = new Set(
|
|
2260
|
+
pending.filter(
|
|
2261
|
+
(message) => message.destination === "queue" && !(message.turnId && message.appliedQueueVersion != null && queue.snapshot && queue.snapshot.version >= message.appliedQueueVersion && !queue.queue.some((turn) => turn.id === message.turnId))
|
|
2262
|
+
).map((message) => `user-message:${message.clientEventId}`)
|
|
2119
2263
|
);
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2264
|
+
const visible = items.filter(
|
|
2265
|
+
(item) => item.kind !== "user-message" || !queued.has(item.id) && !pendingQueue.has(item.reconciliationKey ?? "")
|
|
2266
|
+
);
|
|
2267
|
+
const keys = new Set(
|
|
2268
|
+
visible.flatMap((item) => item.kind === "user-message" ? [item.reconciliationKey] : [])
|
|
2269
|
+
);
|
|
2270
|
+
const optimistic = pending.filter(
|
|
2271
|
+
(message) => (message.destination === "chat" || message.state === "failed") && !keys.has(`user-message:${message.clientEventId}`) && !queue.queue.some((turn) => turn.id === message.turnId)
|
|
2272
|
+
).map((message) => ({
|
|
2273
|
+
kind: "user-message",
|
|
2274
|
+
id: `optimistic:${message.clientEventId}`,
|
|
2275
|
+
reconciliationKey: `user-message:${message.clientEventId}`,
|
|
2276
|
+
text: message.text,
|
|
2277
|
+
annotations: message.annotations.map((annotation, ordinal) => ({ ...annotation, ordinal })),
|
|
2278
|
+
resources: message.resources,
|
|
2279
|
+
tools: [],
|
|
2280
|
+
occurredAt: message.occurredAt,
|
|
2281
|
+
delivery: {
|
|
2282
|
+
state: message.state,
|
|
2283
|
+
...message.error ? { error: message.error } : {},
|
|
2284
|
+
...message.state === "failed" ? {
|
|
2285
|
+
...message.retryable !== false && composer.retryOptimisticMessage ? { onRetry: () => composer.retryOptimisticMessage?.(message.clientEventId) } : {},
|
|
2286
|
+
...message.retryable === false && composer.restoreOptimisticMessage ? { onEdit: () => composer.restoreOptimisticMessage?.(message.clientEventId) } : {},
|
|
2287
|
+
onRemove: () => composer.removeOptimisticMessage?.(message.clientEventId)
|
|
2288
|
+
} : {}
|
|
2289
|
+
}
|
|
2290
|
+
}));
|
|
2291
|
+
const ids = new Set(visible.map((item) => item.id));
|
|
2292
|
+
const steers = (queue.acceptedSteers ?? []).filter((steer) => !ids.has(steer.triggerEventId)).map((steer) => ({
|
|
2293
|
+
kind: "user-message",
|
|
2294
|
+
id: steer.triggerEventId,
|
|
2295
|
+
text: steer.text,
|
|
2296
|
+
annotations: steer.annotations,
|
|
2297
|
+
resources: steer.resources,
|
|
2298
|
+
tools: steer.tools,
|
|
2299
|
+
occurredAt: steer.occurredAt,
|
|
2300
|
+
delivery: { state: steer.state }
|
|
2301
|
+
}));
|
|
2302
|
+
return [...visible, ...optimistic, ...steers];
|
|
2132
2303
|
}
|
|
2133
2304
|
|
|
2134
2305
|
export {
|
|
2135
|
-
|
|
2136
|
-
|
|
2306
|
+
isActionableHumanInputRequest,
|
|
2307
|
+
humanInputRequestFromEvent,
|
|
2308
|
+
projectPendingHumanInputRequests,
|
|
2309
|
+
invokeOlderHistoryLoaderWithReceiptCapture,
|
|
2310
|
+
createOlderHistoryLoadReceipt,
|
|
2137
2311
|
SESSION_EVENT_BROWSER_MAX_BYTES,
|
|
2138
2312
|
SESSION_EVENT_BROWSER_MAX_COUNT,
|
|
2139
|
-
SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES,
|
|
2140
2313
|
SESSION_EVENT_BROWSER_PENDING_MAX_BYTES,
|
|
2141
2314
|
SESSION_EVENT_BROWSER_PENDING_MAX_COUNT,
|
|
2142
2315
|
useSessionEvents,
|
|
2143
2316
|
boundBrowserSessionEventWindow,
|
|
2144
|
-
|
|
2317
|
+
isTitleEvent,
|
|
2318
|
+
useSession,
|
|
2145
2319
|
isTurnQueueEvent,
|
|
2146
2320
|
useTurnQueue,
|
|
2147
|
-
|
|
2148
|
-
|
|
2321
|
+
isHumanInputEvent,
|
|
2322
|
+
useHumanInputRequests,
|
|
2323
|
+
useFileAttachments,
|
|
2149
2324
|
useSessionControl,
|
|
2150
|
-
isSessionMcpApprovalPolicyEvent,
|
|
2151
|
-
useSessionMcpApprovalPolicy,
|
|
2152
|
-
isLineageRefreshEvent,
|
|
2153
|
-
useSessionLineage,
|
|
2154
2325
|
approvalsFromRequiresAction,
|
|
2155
2326
|
projectPendingApprovals,
|
|
2156
|
-
|
|
2157
|
-
projectPendingHumanInputRequests,
|
|
2158
|
-
isHumanInputEvent,
|
|
2159
|
-
useHumanInputRequests
|
|
2327
|
+
conversationTimeline
|
|
2160
2328
|
};
|
|
2161
|
-
//# sourceMappingURL=chunk-
|
|
2329
|
+
//# sourceMappingURL=chunk-SJZDKVW2.js.map
|