@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ChildSessionLink } from "./child-session-link";
|
|
1
2
|
/**
|
|
2
3
|
* SessionChrome — compact merged session signals above the composer.
|
|
3
4
|
*
|
|
@@ -28,15 +29,30 @@
|
|
|
28
29
|
* Inbox has no product dismiss API; pass `onDismissIncoming` when the host
|
|
29
30
|
* wants a visible action (dev harness may use a local dummy).
|
|
30
31
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
32
|
+
* Queue opens by default when chrome is idle and at least one authoritative
|
|
33
|
+
* prompt is queued. Closing the queue dismisses that session's offer until
|
|
34
|
+
* occupancy drains; a different session on the same chrome instance may
|
|
35
|
+
* offer again. An in-flight optimistic Send never opens the drawer —
|
|
36
|
+
* admission stays a paint-only receipt on the chip so layout does not jump
|
|
37
|
+
* under the pointer.
|
|
38
|
+
*
|
|
39
|
+
* Segment switches keep the panel shell mounted and crossfade content. The
|
|
40
|
+
* shell uses one CSS grid-track transition for deliberate open/close actions;
|
|
41
|
+
* live queue reconciliation never feeds measurements back into layout.
|
|
33
42
|
*/
|
|
34
|
-
import type {
|
|
43
|
+
import type {
|
|
44
|
+
SessionGoal,
|
|
45
|
+
SessionPendingInputPreview,
|
|
46
|
+
SessionStatus,
|
|
47
|
+
SessionTurn,
|
|
48
|
+
} from "@opengeni/sdk";
|
|
35
49
|
import {
|
|
50
|
+
ActivityIcon,
|
|
36
51
|
AudioLinesIcon,
|
|
37
52
|
ArrowDownIcon,
|
|
38
53
|
ArrowUpIcon,
|
|
39
54
|
BotIcon,
|
|
55
|
+
CheckIcon,
|
|
40
56
|
InboxIcon,
|
|
41
57
|
ListOrderedIcon,
|
|
42
58
|
Loader2Icon,
|
|
@@ -44,29 +60,33 @@ import {
|
|
|
44
60
|
PencilIcon,
|
|
45
61
|
PlayIcon,
|
|
46
62
|
Trash2Icon,
|
|
63
|
+
TerminalIcon,
|
|
47
64
|
TriangleAlertIcon,
|
|
48
65
|
XIcon,
|
|
49
|
-
|
|
66
|
+
CornerDownRightIcon,
|
|
67
|
+
TargetIcon,
|
|
50
68
|
} from "lucide-react";
|
|
51
69
|
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
|
|
52
|
-
import {
|
|
53
|
-
useEffect,
|
|
54
|
-
useId,
|
|
55
|
-
useLayoutEffect,
|
|
56
|
-
useMemo,
|
|
57
|
-
useRef,
|
|
58
|
-
useState,
|
|
59
|
-
type ReactNode,
|
|
60
|
-
} from "react";
|
|
70
|
+
import { useEffect, useId, useMemo, useRef, useState, type ReactNode } from "react";
|
|
61
71
|
|
|
62
|
-
import type { ComposerState } from "../hooks/use-composer";
|
|
72
|
+
import type { ComposerOptimisticMessage, ComposerState } from "../hooks/use-composer";
|
|
63
73
|
import type { UseGoalResult } from "../hooks/use-goal";
|
|
64
74
|
import type { UseTurnQueueResult } from "../hooks/use-turn-queue";
|
|
65
75
|
import { cn } from "../lib/cn";
|
|
76
|
+
import { useErrorMessage } from "../lib/error-message";
|
|
77
|
+
import { formatClockTime } from "../lib/format";
|
|
66
78
|
import { requestQueueDraftEdit } from "./queue-draft-policy";
|
|
79
|
+
import { QUEUE_ITEM_CONTENT_UNAVAILABLE, queueItemContent } from "./queue-item-content";
|
|
80
|
+
import { TimelineAnnotationsChip, type TimelineAnnotationLike } from "./timeline-annotations";
|
|
67
81
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip";
|
|
68
82
|
|
|
69
|
-
export type SessionChromeSignalId =
|
|
83
|
+
export type SessionChromeSignalId =
|
|
84
|
+
| "incoming"
|
|
85
|
+
| "steering"
|
|
86
|
+
| "queue"
|
|
87
|
+
| "goal"
|
|
88
|
+
| "agents"
|
|
89
|
+
| "commands";
|
|
70
90
|
|
|
71
91
|
export type SessionChromeSignalTone = "neutral" | "accent" | "waiting" | "running";
|
|
72
92
|
|
|
@@ -77,12 +97,24 @@ export type SessionChromeAgentsSignal = {
|
|
|
77
97
|
};
|
|
78
98
|
|
|
79
99
|
export type SessionChromeProps = {
|
|
100
|
+
/** Authoritative execution status; omitted by older embedding hosts. */
|
|
101
|
+
sessionStatus?: SessionStatus | undefined;
|
|
80
102
|
queue: UseTurnQueueResult;
|
|
103
|
+
/** Open and focus an existing queued prompt without changing its delivery. */
|
|
104
|
+
queueFocusTarget?: { turnId: string; requestId: number } | undefined;
|
|
81
105
|
/** Needed for queue edit → composer checkout. Omit with `readOnly`. */
|
|
82
106
|
composer?: ComposerState | undefined;
|
|
107
|
+
/** Focus the composer after a successful queue checkout has applied its draft.
|
|
108
|
+
* Connect to the composer controller's `focusInput`, or a custom input ref.
|
|
109
|
+
* Never called for failed checkout or while replacement awaits confirmation.
|
|
110
|
+
*/
|
|
111
|
+
onComposerFocus?: (() => void) | undefined;
|
|
83
112
|
goal?: UseGoalResult | null | undefined;
|
|
84
113
|
/** Expanded agents body (host supplies tree / list). */
|
|
85
114
|
agentsPanel?: ReactNode;
|
|
115
|
+
/** Active commands only. The host mounts the body only when opened. */
|
|
116
|
+
commandsPanel?: ReactNode;
|
|
117
|
+
commandsCount?: number | undefined;
|
|
86
118
|
/** Chip summary; when `count > 0` the agents segment appears. */
|
|
87
119
|
agentsSignal?: SessionChromeAgentsSignal | undefined;
|
|
88
120
|
/**
|
|
@@ -90,6 +122,8 @@ export type SessionChromeProps = {
|
|
|
90
122
|
* (and the gallery) may still pass a handler so the action is visible.
|
|
91
123
|
*/
|
|
92
124
|
onDismissIncoming?: ((inputId: string) => void) | undefined;
|
|
125
|
+
/** Open a typed child update's source through the host's normal authorized route. */
|
|
126
|
+
onOpenSession?: ((sessionId: string) => void) | undefined;
|
|
93
127
|
readOnly?: boolean | undefined;
|
|
94
128
|
className?: string | undefined;
|
|
95
129
|
/** Controlled active segment; omit for uncontrolled. */
|
|
@@ -100,7 +134,9 @@ export type SessionChromeProps = {
|
|
|
100
134
|
|
|
101
135
|
type GoalPillState =
|
|
102
136
|
| "pursuing"
|
|
137
|
+
| "waiting"
|
|
103
138
|
| "scheduled"
|
|
139
|
+
| "session_failed"
|
|
104
140
|
| "blocked"
|
|
105
141
|
| "held"
|
|
106
142
|
| "paused"
|
|
@@ -114,14 +150,90 @@ type QueuedTurnPresentation = {
|
|
|
114
150
|
|
|
115
151
|
const GOAL_LABEL: Record<GoalPillState, string> = {
|
|
116
152
|
pursuing: "Pursuing",
|
|
117
|
-
|
|
153
|
+
waiting: "Waiting",
|
|
154
|
+
scheduled: "Waiting",
|
|
118
155
|
blocked: "Blocked",
|
|
156
|
+
session_failed: "Blocked by session failure",
|
|
119
157
|
held: "Held",
|
|
120
158
|
paused: "Paused",
|
|
121
159
|
invariant_broken: "Needs attention",
|
|
122
160
|
completed: "Done",
|
|
123
161
|
};
|
|
124
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Short pill suffix per `pausedReason`. `max_auto_continuations` is pacing
|
|
165
|
+
* (new input resumes it); `limits` is budget/admission; `user_pause`/`api` is
|
|
166
|
+
* the human's own override; `agent` is the model declaring it is blocked on a
|
|
167
|
+
* human decision. Unknown or legacy reasons keep the bare "Paused".
|
|
168
|
+
*/
|
|
169
|
+
const GOAL_PAUSED_REASON_SUFFIX: Record<string, string> = {
|
|
170
|
+
max_auto_continuations: "cap",
|
|
171
|
+
limits: "budget",
|
|
172
|
+
user_pause: "manually",
|
|
173
|
+
api: "manually",
|
|
174
|
+
agent: "agent",
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const GOAL_PAUSED_REASON_EXPLANATION: Record<string, string> = {
|
|
178
|
+
max_auto_continuations:
|
|
179
|
+
"Paused at the automatic continuation cap. New input (a child result, an agent message, or your prompt) resumes it; you can also resume it here.",
|
|
180
|
+
limits: "Paused because budget or usage limits block another run. Resume once limits allow.",
|
|
181
|
+
user_pause:
|
|
182
|
+
"Paused manually by a person or an API call. Resume to let the goal continue on its own.",
|
|
183
|
+
api: "Paused manually by a person or an API call. Resume to let the goal continue on its own.",
|
|
184
|
+
agent: "Paused by the agent: it is waiting on a human decision before continuing.",
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
type GoalPillRecord = Pick<SessionGoal, "status" | "pausedReason"> & {
|
|
188
|
+
continuation?: SessionGoal["continuation"] | null | undefined;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/** Pill label, with the pause reason spelled out: "Paused · cap" / "Paused · manually". */
|
|
192
|
+
export function sessionChromeGoalPillLabel(
|
|
193
|
+
state: GoalPillState,
|
|
194
|
+
record: GoalPillRecord | null | undefined,
|
|
195
|
+
): string {
|
|
196
|
+
if (state !== "paused") return GOAL_LABEL[state];
|
|
197
|
+
const suffix = record?.pausedReason ? GOAL_PAUSED_REASON_SUFFIX[record.pausedReason] : undefined;
|
|
198
|
+
return suffix ? `Paused · ${suffix}` : "Paused";
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* One human sentence explaining WHY the goal is not pursuing right now: the
|
|
203
|
+
* pause reason, the agent's own `wait_for_input` hold (reason + deadline), or the
|
|
204
|
+
* next idle-backoff check time. Null when the state needs no explanation.
|
|
205
|
+
*/
|
|
206
|
+
export function sessionChromeGoalPillExplanation(
|
|
207
|
+
state: GoalPillState,
|
|
208
|
+
record: GoalPillRecord | null | undefined,
|
|
209
|
+
): string | null {
|
|
210
|
+
const continuation = record?.continuation ?? null;
|
|
211
|
+
if (state === "session_failed") {
|
|
212
|
+
return "Resolve the session failure, then use Continue or send a message to continue this active goal.";
|
|
213
|
+
}
|
|
214
|
+
if (state === "paused") {
|
|
215
|
+
return record?.pausedReason
|
|
216
|
+
? (GOAL_PAUSED_REASON_EXPLANATION[record.pausedReason] ?? null)
|
|
217
|
+
: null;
|
|
218
|
+
}
|
|
219
|
+
if (state === "scheduled") {
|
|
220
|
+
return continuation?.nextAttemptAt
|
|
221
|
+
? `Continues at ${formatClockTime(continuation.nextAttemptAt)}.`
|
|
222
|
+
: "Waiting to continue automatically.";
|
|
223
|
+
}
|
|
224
|
+
if (state === "waiting" && continuation?.reason === "system_work_pending") {
|
|
225
|
+
return "Waiting for other session work to finish before the goal continues automatically.";
|
|
226
|
+
}
|
|
227
|
+
if (state === "held" && continuation?.reason === "held_for_input") {
|
|
228
|
+
const reason = continuation.holdReason?.trim();
|
|
229
|
+
const until = continuation.nextAttemptAt
|
|
230
|
+
? ` until ${formatClockTime(continuation.nextAttemptAt)}`
|
|
231
|
+
: "";
|
|
232
|
+
return `Waiting for input${reason ? `: ${reason}` : ""}${until}. Relevant session input—including a child result, background-command result, agent message, schedule, or your prompt—wakes it sooner.`;
|
|
233
|
+
}
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
|
|
125
237
|
function queuedTurnPresentation(turn: SessionTurn): QueuedTurnPresentation {
|
|
126
238
|
const realtimeDelegation = objectValue(turn.metadata.realtimeDelegation);
|
|
127
239
|
const inputTranscript = realtimeDelegation?.inputTranscript;
|
|
@@ -138,6 +250,82 @@ function isSteeringTurn(turn: SessionTurn): boolean {
|
|
|
138
250
|
return turn.metadata.delivery === "steer";
|
|
139
251
|
}
|
|
140
252
|
|
|
253
|
+
function isAuthoritativeQueuedTurn(
|
|
254
|
+
turn: SessionTurn,
|
|
255
|
+
mutationFor: UseTurnQueueResult["mutationFor"],
|
|
256
|
+
): boolean {
|
|
257
|
+
return !isSteeringTurn(turn) && mutationFor(turn.id) !== "steer";
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function countAuthoritativeQueuedTurns(
|
|
261
|
+
turns: readonly SessionTurn[],
|
|
262
|
+
mutationFor: UseTurnQueueResult["mutationFor"],
|
|
263
|
+
): number {
|
|
264
|
+
return turns.filter((turn) => isAuthoritativeQueuedTurn(turn, mutationFor)).length;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function isOptimisticQueuedMessage(
|
|
268
|
+
message: ComposerOptimisticMessage,
|
|
269
|
+
queuedTurnIds: ReadonlySet<string>,
|
|
270
|
+
snapshot: UseTurnQueueResult["snapshot"],
|
|
271
|
+
): boolean {
|
|
272
|
+
return (
|
|
273
|
+
message.delivery === "send" &&
|
|
274
|
+
message.destination === "queue" &&
|
|
275
|
+
(!message.turnId || !queuedTurnIds.has(message.turnId)) &&
|
|
276
|
+
!(
|
|
277
|
+
message.turnId &&
|
|
278
|
+
message.appliedQueueVersion !== null &&
|
|
279
|
+
message.appliedQueueVersion !== undefined &&
|
|
280
|
+
snapshot &&
|
|
281
|
+
snapshot.version >= message.appliedQueueVersion
|
|
282
|
+
)
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function countOptimisticQueuedMessages(
|
|
287
|
+
messages: readonly ComposerOptimisticMessage[] | undefined,
|
|
288
|
+
queuedTurnIds: ReadonlySet<string>,
|
|
289
|
+
snapshot: UseTurnQueueResult["snapshot"],
|
|
290
|
+
): number {
|
|
291
|
+
return (messages ?? []).filter((message) =>
|
|
292
|
+
isOptimisticQueuedMessage(message, queuedTurnIds, snapshot),
|
|
293
|
+
).length;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* First uncontrolled segment. An existing authoritative queue opens itself
|
|
298
|
+
* when the host did not pick another default; optimistic-only occupancy
|
|
299
|
+
* stays collapsed so a live Send does not shove a drawer under the pointer.
|
|
300
|
+
*/
|
|
301
|
+
export function sessionChromeInitialActive(input: {
|
|
302
|
+
defaultActive: SessionChromeSignalId | null;
|
|
303
|
+
authoritativeQueuedCount: number;
|
|
304
|
+
}): SessionChromeSignalId | null {
|
|
305
|
+
if (input.defaultActive != null) return input.defaultActive;
|
|
306
|
+
return input.authoritativeQueuedCount >= 1 ? "queue" : null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Whether idle chrome should offer the queue panel. A dismissed session
|
|
311
|
+
* stays collapsed until occupancy drains. Controlled hosts own this.
|
|
312
|
+
*/
|
|
313
|
+
export function sessionChromeShouldOfferQueue(input: {
|
|
314
|
+
controlled: boolean;
|
|
315
|
+
active: SessionChromeSignalId | null;
|
|
316
|
+
activityOpen: boolean;
|
|
317
|
+
authoritativeQueuedCount: number;
|
|
318
|
+
suppressed: boolean;
|
|
319
|
+
}): boolean {
|
|
320
|
+
return (
|
|
321
|
+
!input.controlled &&
|
|
322
|
+
input.active === null &&
|
|
323
|
+
!input.activityOpen &&
|
|
324
|
+
input.authoritativeQueuedCount >= 1 &&
|
|
325
|
+
!input.suppressed
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
141
329
|
function objectValue(value: unknown): Record<string, unknown> | null {
|
|
142
330
|
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
143
331
|
? (value as Record<string, unknown>)
|
|
@@ -148,20 +336,34 @@ function objectValue(value: unknown): Record<string, unknown> | null {
|
|
|
148
336
|
export function sessionChromeGoalPillState(
|
|
149
337
|
goalStatus: "active" | "paused" | "completed",
|
|
150
338
|
continuation: SessionGoal["continuation"] | null | undefined,
|
|
339
|
+
sessionStatus?: SessionStatus,
|
|
151
340
|
): GoalPillState {
|
|
152
341
|
if (goalStatus === "completed") return "completed";
|
|
153
342
|
if (goalStatus === "paused") return "paused";
|
|
343
|
+
if (sessionStatus === "failed") return "session_failed";
|
|
154
344
|
if (!continuation) return "invariant_broken";
|
|
155
345
|
if (continuation.state === "running") {
|
|
156
346
|
return continuation.reason === "goal_turn_running"
|
|
157
347
|
? "pursuing"
|
|
158
348
|
: continuation.reason === "human_turn_running"
|
|
159
|
-
? "
|
|
349
|
+
? "waiting"
|
|
160
350
|
: "invariant_broken";
|
|
161
351
|
}
|
|
352
|
+
// `backoff_pending` (idle pacing between consecutive no-input continuations,
|
|
353
|
+
// next evaluation at `nextAttemptAt`) is an ordinary scheduled state.
|
|
162
354
|
if (continuation.state === "scheduled") return "scheduled";
|
|
163
355
|
if (continuation.state === "blocked") {
|
|
164
|
-
|
|
356
|
+
if (
|
|
357
|
+
continuation.reason === "human_turn_running" ||
|
|
358
|
+
continuation.reason === "system_work_pending"
|
|
359
|
+
) {
|
|
360
|
+
return "waiting";
|
|
361
|
+
}
|
|
362
|
+
// `held_for_input` is the agent's own wait_for_input hold (waiting for child
|
|
363
|
+
// results / external input until a deadline); it shares the Held pill.
|
|
364
|
+
return continuation.reason === "workstream_paused" || continuation.reason === "held_for_input"
|
|
365
|
+
? "held"
|
|
366
|
+
: "blocked";
|
|
165
367
|
}
|
|
166
368
|
return "invariant_broken";
|
|
167
369
|
}
|
|
@@ -199,6 +401,16 @@ function pendingKindLabel(kind: SessionPendingInputPreview["kind"]): string {
|
|
|
199
401
|
switch (kind) {
|
|
200
402
|
case "child_terminal_result":
|
|
201
403
|
return "Child result";
|
|
404
|
+
case "child_requires_action":
|
|
405
|
+
return "Child needs input";
|
|
406
|
+
case "child_requires_action_resolved":
|
|
407
|
+
return "Child unblocked";
|
|
408
|
+
case "child_paused":
|
|
409
|
+
return "Child paused";
|
|
410
|
+
case "child_waiting_capacity":
|
|
411
|
+
return "Child waiting";
|
|
412
|
+
case "child_progress":
|
|
413
|
+
return "Child progress";
|
|
202
414
|
case "agent_steer_instruction":
|
|
203
415
|
return "Steer";
|
|
204
416
|
case "scheduled_occurrence":
|
|
@@ -207,6 +419,12 @@ function pendingKindLabel(kind: SessionPendingInputPreview["kind"]): string {
|
|
|
207
419
|
return "Goal wake";
|
|
208
420
|
case "agent_message":
|
|
209
421
|
return "Update";
|
|
422
|
+
case "background_command_result":
|
|
423
|
+
return "Command result";
|
|
424
|
+
case "session_wait_timeout":
|
|
425
|
+
return "Wait ended";
|
|
426
|
+
case "media_generation_result":
|
|
427
|
+
return "Video result";
|
|
210
428
|
default:
|
|
211
429
|
return "Incoming";
|
|
212
430
|
}
|
|
@@ -226,52 +444,47 @@ function toneClass(tone: SessionChromeSignalTone, selected: boolean): string {
|
|
|
226
444
|
}
|
|
227
445
|
|
|
228
446
|
export function SessionChrome({
|
|
447
|
+
sessionStatus,
|
|
229
448
|
queue,
|
|
449
|
+
queueFocusTarget,
|
|
230
450
|
composer,
|
|
451
|
+
onComposerFocus,
|
|
231
452
|
goal,
|
|
232
453
|
agentsPanel,
|
|
454
|
+
commandsPanel,
|
|
455
|
+
commandsCount = 0,
|
|
233
456
|
agentsSignal,
|
|
234
457
|
onDismissIncoming,
|
|
458
|
+
onOpenSession,
|
|
235
459
|
readOnly = false,
|
|
236
460
|
className,
|
|
237
461
|
active: activeControlled,
|
|
238
462
|
defaultActive = null,
|
|
239
463
|
onActiveChange,
|
|
240
464
|
}: SessionChromeProps) {
|
|
465
|
+
const formatError = useErrorMessage();
|
|
466
|
+
const [activityRequested, setActivityOpen] = useState(
|
|
467
|
+
Boolean(defaultActive && ["incoming", "agents", "commands"].includes(defaultActive)),
|
|
468
|
+
);
|
|
241
469
|
const reactId = useId();
|
|
242
470
|
const panelId = `og-session-chrome-panel-${reactId}`;
|
|
243
471
|
const reduceMotion = useReducedMotion();
|
|
244
472
|
const record = goal?.goal ?? null;
|
|
245
473
|
const incoming = queue.pendingInputs;
|
|
246
474
|
const turns = queue.queue;
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
turnId: pendingQueueSteer.id,
|
|
261
|
-
}
|
|
262
|
-
: durableQueueSteer
|
|
263
|
-
? {
|
|
264
|
-
phase: "accepted" as const,
|
|
265
|
-
text: queuedTurnPresentation(durableQueueSteer).text,
|
|
266
|
-
turnId: durableQueueSteer.id,
|
|
267
|
-
}
|
|
268
|
-
: composerSteering;
|
|
269
|
-
const currentTurnId = currentSteering?.turnId ?? null;
|
|
270
|
-
return {
|
|
271
|
-
steering: currentSteering,
|
|
272
|
-
queuedTurns: currentTurnId ? turns.filter((turn) => turn.id !== currentTurnId) : turns,
|
|
273
|
-
};
|
|
274
|
-
}, [composerSteering, queue.pendingByTurn, turns]);
|
|
475
|
+
const queueMutationFor = queue.mutationFor;
|
|
476
|
+
const queuedTurns = useMemo(
|
|
477
|
+
() => turns.filter((turn) => isAuthoritativeQueuedTurn(turn, queueMutationFor)),
|
|
478
|
+
[queueMutationFor, turns],
|
|
479
|
+
);
|
|
480
|
+
const queuedTurnIds = useMemo(() => new Set(queuedTurns.map((turn) => turn.id)), [queuedTurns]);
|
|
481
|
+
const optimisticQueued = useMemo(
|
|
482
|
+
() =>
|
|
483
|
+
(composer?.optimisticMessages ?? []).filter((message) =>
|
|
484
|
+
isOptimisticQueuedMessage(message, queuedTurnIds, queue.snapshot),
|
|
485
|
+
),
|
|
486
|
+
[composer?.optimisticMessages, queue.snapshot, queuedTurnIds],
|
|
487
|
+
);
|
|
275
488
|
const stoppingKind =
|
|
276
489
|
composer?.stoppingAttempt ??
|
|
277
490
|
(queue.stoppingPreviousAttempt
|
|
@@ -282,22 +495,91 @@ export function SessionChrome({
|
|
|
282
495
|
const stopping = stoppingKind !== null;
|
|
283
496
|
const canMutateQueue = !readOnly && composer !== undefined;
|
|
284
497
|
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
record.continuation
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
498
|
+
const elapsed = useLiveElapsed(record?.createdAt, Boolean(record));
|
|
499
|
+
const goalState = record
|
|
500
|
+
? sessionChromeGoalPillState(record.status, record.continuation, sessionStatus)
|
|
501
|
+
: null;
|
|
502
|
+
|
|
503
|
+
const initialAuthoritativeQueuedCount = countAuthoritativeQueuedTurns(
|
|
504
|
+
queue.queue,
|
|
505
|
+
queue.mutationFor,
|
|
506
|
+
);
|
|
507
|
+
const [activeUncontrolled, setActiveUncontrolled] = useState<SessionChromeSignalId | null>(() =>
|
|
508
|
+
sessionChromeInitialActive({
|
|
509
|
+
defaultActive,
|
|
510
|
+
authoritativeQueuedCount: initialAuthoritativeQueuedCount,
|
|
511
|
+
}),
|
|
293
512
|
);
|
|
294
|
-
const
|
|
513
|
+
const active = activeControlled !== undefined ? activeControlled : activeUncontrolled;
|
|
514
|
+
const rootRef = useRef<HTMLDivElement>(null);
|
|
515
|
+
const openedQueueTargetRef = useRef<string | null>(null);
|
|
516
|
+
const focusedQueueTargetRef = useRef<string | null>(null);
|
|
517
|
+
const queueTargetKey = queueFocusTarget
|
|
518
|
+
? `${queueFocusTarget.turnId}:${queueFocusTarget.requestId}`
|
|
519
|
+
: null;
|
|
520
|
+
useEffect(() => {
|
|
521
|
+
if (!queueTargetKey || openedQueueTargetRef.current === queueTargetKey) return;
|
|
522
|
+
openedQueueTargetRef.current = queueTargetKey;
|
|
523
|
+
if (activeControlled === undefined) setActiveUncontrolled("queue");
|
|
524
|
+
onActiveChange?.("queue");
|
|
525
|
+
}, [queueTargetKey, activeControlled, onActiveChange]);
|
|
526
|
+
useEffect(() => {
|
|
527
|
+
if (!queueFocusTarget || active !== "queue" || focusedQueueTargetRef.current === queueTargetKey)
|
|
528
|
+
return;
|
|
529
|
+
const root = rootRef.current;
|
|
530
|
+
if (!root) return;
|
|
531
|
+
const focus = () => {
|
|
532
|
+
const row = Array.from(root.querySelectorAll<HTMLElement>("[data-queue-turn-id]")).find(
|
|
533
|
+
(element) => element.dataset.queueTurnId === queueFocusTarget.turnId,
|
|
534
|
+
);
|
|
535
|
+
if (!row) return false;
|
|
536
|
+
focusedQueueTargetRef.current = queueTargetKey;
|
|
537
|
+
row.focus({ preventScroll: true });
|
|
538
|
+
row.scrollIntoView?.({ block: "nearest" });
|
|
539
|
+
return true;
|
|
540
|
+
};
|
|
541
|
+
if (focus()) return;
|
|
542
|
+
const observer = new MutationObserver(() => {
|
|
543
|
+
if (focus()) observer.disconnect();
|
|
544
|
+
});
|
|
545
|
+
observer.observe(root, { childList: true, subtree: true });
|
|
546
|
+
return () => observer.disconnect();
|
|
547
|
+
}, [queueFocusTarget, queueTargetKey, active, queuedTurns]);
|
|
548
|
+
const activityOpen =
|
|
549
|
+
activityRequested || Boolean(active && ["incoming", "agents", "commands"].includes(active));
|
|
550
|
+
const activityVisibleRef = useRef(activityOpen);
|
|
551
|
+
activityVisibleRef.current = activityOpen;
|
|
552
|
+
// Two independent suppressions, both occupancy-scoped:
|
|
553
|
+
// - admission: a live Send while idle must not open a drawer under the pointer
|
|
554
|
+
// - dismiss: closing the queue remembers that session until occupancy drains
|
|
555
|
+
const queueOfferAdmissionSuppressedRef = useRef(
|
|
556
|
+
defaultActive == null &&
|
|
557
|
+
initialAuthoritativeQueuedCount === 0 &&
|
|
558
|
+
countOptimisticQueuedMessages(composer?.optimisticMessages, queuedTurnIds, queue.snapshot) >
|
|
559
|
+
0,
|
|
560
|
+
);
|
|
561
|
+
const queueOfferDismissedSessionIdsRef = useRef<Set<string>>(new Set());
|
|
562
|
+
const occupancySessionId = queuedTurns[0]?.sessionId ?? null;
|
|
563
|
+
const setActive = (next: SessionChromeSignalId | null) => {
|
|
564
|
+
if (active === "queue" && next === null) {
|
|
565
|
+
if (occupancySessionId) {
|
|
566
|
+
queueOfferDismissedSessionIdsRef.current.add(occupancySessionId);
|
|
567
|
+
} else {
|
|
568
|
+
queueOfferAdmissionSuppressedRef.current = true;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (activeControlled === undefined) setActiveUncontrolled(next);
|
|
572
|
+
onActiveChange?.(next);
|
|
573
|
+
};
|
|
295
574
|
|
|
575
|
+
const hasCommandsPanel = commandsPanel != null;
|
|
296
576
|
const signals = useMemo(() => {
|
|
297
577
|
const rows: Array<{
|
|
298
578
|
id: SessionChromeSignalId;
|
|
299
579
|
label: string;
|
|
300
580
|
detail?: string | undefined;
|
|
581
|
+
/** Hover explanation (why paused / held / when the next check is). */
|
|
582
|
+
title?: string | undefined;
|
|
301
583
|
tone: SessionChromeSignalTone;
|
|
302
584
|
icon: ReactNode;
|
|
303
585
|
}> = [];
|
|
@@ -315,44 +597,43 @@ export function SessionChrome({
|
|
|
315
597
|
icon: <InboxIcon className="size-3" />,
|
|
316
598
|
});
|
|
317
599
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
label: stopping
|
|
322
|
-
? stoppingKind === "current"
|
|
323
|
-
? "Stopping current work…"
|
|
324
|
-
: "Stopping previous work…"
|
|
325
|
-
: "Changing direction…",
|
|
326
|
-
...(steering?.text ? { detail: steering.text } : {}),
|
|
327
|
-
tone: stopping ? "waiting" : "accent",
|
|
328
|
-
icon:
|
|
329
|
-
steering?.phase === "submitting" || stopping ? (
|
|
330
|
-
<Loader2Icon className="size-3 animate-og-spin" />
|
|
331
|
-
) : (
|
|
332
|
-
<ZapIcon className="size-3" />
|
|
333
|
-
),
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
if (queuedTurns.length > 0) {
|
|
600
|
+
const queuedCount = queuedTurns.length + optimisticQueued.length;
|
|
601
|
+
const queueProblem = queue.mutationError ?? queue.error;
|
|
602
|
+
if (queuedCount > 0 || queueProblem) {
|
|
337
603
|
const presentations = queuedTurns.map(queuedTurnPresentation);
|
|
338
604
|
const first = presentations[0];
|
|
339
|
-
const allVoiceRequests =
|
|
605
|
+
const allVoiceRequests =
|
|
606
|
+
optimisticQueued.length === 0 &&
|
|
607
|
+
presentations.length > 0 &&
|
|
608
|
+
presentations.every(({ kind }) => kind === "realtime_voice");
|
|
340
609
|
const onlyVoiceHandoff =
|
|
341
|
-
|
|
610
|
+
optimisticQueued.length === 0 &&
|
|
611
|
+
presentations.length === 1 &&
|
|
612
|
+
first?.kind === "realtime_voice_handoff";
|
|
342
613
|
const voiceOnly = allVoiceRequests || onlyVoiceHandoff;
|
|
343
|
-
const detail =
|
|
614
|
+
const detail = queueProblem
|
|
615
|
+
? queue.mutationError
|
|
616
|
+
? "Action not confirmed"
|
|
617
|
+
: "Queue unavailable"
|
|
618
|
+
: (first?.text ?? optimisticQueued[0]?.text);
|
|
344
619
|
rows.push({
|
|
345
620
|
id: "queue",
|
|
346
|
-
label:
|
|
347
|
-
?
|
|
348
|
-
?
|
|
349
|
-
:
|
|
350
|
-
:
|
|
351
|
-
?
|
|
352
|
-
|
|
621
|
+
label: queueProblem
|
|
622
|
+
? queuedCount > 0
|
|
623
|
+
? `${queuedCount} queued · needs attention`
|
|
624
|
+
: "Queue needs attention"
|
|
625
|
+
: allVoiceRequests
|
|
626
|
+
? queuedCount === 1
|
|
627
|
+
? "Voice request queued"
|
|
628
|
+
: `${queuedCount} voice requests queued`
|
|
629
|
+
: onlyVoiceHandoff
|
|
630
|
+
? "Voice handoff queued"
|
|
631
|
+
: `${queuedCount} queued prompt${queuedCount === 1 ? "" : "s"}`,
|
|
353
632
|
...(detail ? { detail } : {}),
|
|
354
|
-
tone: "neutral",
|
|
355
|
-
icon:
|
|
633
|
+
tone: queueProblem ? "waiting" : "neutral",
|
|
634
|
+
icon: queueProblem ? (
|
|
635
|
+
<TriangleAlertIcon className="size-3" />
|
|
636
|
+
) : voiceOnly ? (
|
|
356
637
|
<AudioLinesIcon className="size-3" />
|
|
357
638
|
) : (
|
|
358
639
|
<ListOrderedIcon className="size-3" />
|
|
@@ -360,24 +641,24 @@ export function SessionChrome({
|
|
|
360
641
|
});
|
|
361
642
|
}
|
|
362
643
|
if (record && goalState) {
|
|
363
|
-
const waiting =
|
|
644
|
+
const waiting =
|
|
645
|
+
goalState === "waiting" ||
|
|
646
|
+
goalState === "blocked" ||
|
|
647
|
+
goalState === "session_failed" ||
|
|
648
|
+
goalState === "held" ||
|
|
649
|
+
goalState === "paused";
|
|
650
|
+
const explanation = sessionChromeGoalPillExplanation(goalState, record);
|
|
364
651
|
rows.push({
|
|
365
652
|
id: "goal",
|
|
366
|
-
label:
|
|
653
|
+
label: sessionChromeGoalPillLabel(goalState, record),
|
|
367
654
|
detail: elapsed ? `${elapsed} · ${record.text}` : record.text,
|
|
655
|
+
...(explanation ? { title: explanation } : {}),
|
|
368
656
|
tone: waiting
|
|
369
657
|
? "waiting"
|
|
370
658
|
: goalState === "pursuing" || goalState === "scheduled"
|
|
371
659
|
? "accent"
|
|
372
660
|
: "neutral",
|
|
373
|
-
icon:
|
|
374
|
-
goalState === "blocked" || goalState === "invariant_broken" ? (
|
|
375
|
-
<TriangleAlertIcon className="size-3" />
|
|
376
|
-
) : goalState === "paused" || goalState === "held" ? (
|
|
377
|
-
<PauseIcon className="size-3" />
|
|
378
|
-
) : (
|
|
379
|
-
<ZapIcon className="size-3" />
|
|
380
|
-
),
|
|
661
|
+
icon: <TargetIcon className="size-3" />,
|
|
381
662
|
});
|
|
382
663
|
}
|
|
383
664
|
if (agentsSignal && agentsSignal.count > 0) {
|
|
@@ -390,36 +671,95 @@ export function SessionChrome({
|
|
|
390
671
|
icon: <BotIcon className="size-3" />,
|
|
391
672
|
});
|
|
392
673
|
}
|
|
674
|
+
if (commandsCount > 0 || (active === "commands" && hasCommandsPanel)) {
|
|
675
|
+
rows.push({
|
|
676
|
+
id: "commands",
|
|
677
|
+
label:
|
|
678
|
+
commandsCount > 0
|
|
679
|
+
? `${commandsCount} command${commandsCount === 1 ? "" : "s"}`
|
|
680
|
+
: "Commands",
|
|
681
|
+
tone: commandsCount > 0 ? "running" : "neutral",
|
|
682
|
+
icon: <TerminalIcon className="size-3" />,
|
|
683
|
+
});
|
|
684
|
+
}
|
|
393
685
|
return rows;
|
|
394
686
|
}, [
|
|
687
|
+
commandsCount,
|
|
688
|
+
hasCommandsPanel,
|
|
689
|
+
active,
|
|
395
690
|
agentsSignal,
|
|
396
691
|
elapsed,
|
|
397
692
|
goalState,
|
|
398
693
|
incoming,
|
|
694
|
+
optimisticQueued,
|
|
695
|
+
queue.error,
|
|
696
|
+
queue.mutationError,
|
|
399
697
|
queuedTurns,
|
|
400
698
|
record,
|
|
401
|
-
steering,
|
|
402
|
-
stopping,
|
|
403
|
-
stoppingKind,
|
|
404
699
|
]);
|
|
405
700
|
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
);
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
701
|
+
const optimisticQueueKeys = optimisticQueued.map((message) => message.clientEventId).join(",");
|
|
702
|
+
const previousOptimisticQueueKeys = useRef(optimisticQueueKeys);
|
|
703
|
+
const [queueArrivalNonce, setQueueArrivalNonce] = useState(0);
|
|
704
|
+
useEffect(() => {
|
|
705
|
+
const previous = new Set(previousOptimisticQueueKeys.current.split(",").filter(Boolean));
|
|
706
|
+
const arrived = optimisticQueued.some((message) => !previous.has(message.clientEventId));
|
|
707
|
+
previousOptimisticQueueKeys.current = optimisticQueueKeys;
|
|
708
|
+
if (!arrived) return;
|
|
709
|
+
// Queue admission must not move the conversation or open a drawer beneath
|
|
710
|
+
// the pointer. A stable, paint-only receipt on the queue chip communicates
|
|
711
|
+
// destination without participating in layout.
|
|
712
|
+
setQueueArrivalNonce((current) => current + 1);
|
|
713
|
+
if (activeControlled === undefined && active === null) {
|
|
714
|
+
queueOfferAdmissionSuppressedRef.current = true;
|
|
715
|
+
}
|
|
716
|
+
}, [active, activeControlled, optimisticQueueKeys, optimisticQueued]);
|
|
717
|
+
const queueOccupied = queuedTurns.length > 0 || optimisticQueued.length > 0;
|
|
718
|
+
useEffect(() => {
|
|
719
|
+
if (queueOccupied) return;
|
|
720
|
+
// A later wave may offer again. A prior session's dismiss must not stick
|
|
721
|
+
// after this instance has actually gone empty.
|
|
722
|
+
queueOfferAdmissionSuppressedRef.current = false;
|
|
723
|
+
queueOfferDismissedSessionIdsRef.current.clear();
|
|
724
|
+
}, [queueOccupied]);
|
|
725
|
+
const queueOfferSuppressed =
|
|
726
|
+
queueOfferAdmissionSuppressedRef.current ||
|
|
727
|
+
(occupancySessionId != null &&
|
|
728
|
+
queueOfferDismissedSessionIdsRef.current.has(occupancySessionId));
|
|
729
|
+
useEffect(() => {
|
|
730
|
+
if (
|
|
731
|
+
!sessionChromeShouldOfferQueue({
|
|
732
|
+
controlled: activeControlled !== undefined,
|
|
733
|
+
active,
|
|
734
|
+
activityOpen,
|
|
735
|
+
authoritativeQueuedCount: queuedTurns.length,
|
|
736
|
+
suppressed: queueOfferSuppressed,
|
|
737
|
+
})
|
|
738
|
+
) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
if (activeControlled === undefined) setActiveUncontrolled("queue");
|
|
742
|
+
onActiveChange?.("queue");
|
|
743
|
+
}, [
|
|
744
|
+
active,
|
|
745
|
+
activeControlled,
|
|
746
|
+
activityOpen,
|
|
747
|
+
occupancySessionId,
|
|
748
|
+
onActiveChange,
|
|
749
|
+
queueOfferSuppressed,
|
|
750
|
+
queuedTurns.length,
|
|
751
|
+
]);
|
|
414
752
|
const [replaceDraftFor, setReplaceDraftFor] = useState<string | null>(null);
|
|
415
753
|
|
|
416
754
|
const chipRefs = useRef<Partial<Record<SessionChromeSignalId, HTMLButtonElement | null>>>({});
|
|
417
755
|
const railRef = useRef<HTMLDivElement | null>(null);
|
|
418
|
-
const panelBodyRef = useRef<HTMLDivElement | null>(null);
|
|
419
756
|
const [pill, setPill] = useState({ left: 0, top: 0, width: 0, height: 0, opacity: 0 });
|
|
420
|
-
const [panelHeight, setPanelHeight] = useState(0);
|
|
421
757
|
|
|
422
758
|
const signalIds = signals.map((signal) => signal.id).join(",");
|
|
759
|
+
useEffect(() => {
|
|
760
|
+
if (!signals.some((signal) => ["incoming", "agents", "commands"].includes(signal.id)))
|
|
761
|
+
setActivityOpen(false);
|
|
762
|
+
}, [signals]);
|
|
423
763
|
useEffect(() => {
|
|
424
764
|
if (active && !signalIds.split(",").includes(active)) {
|
|
425
765
|
if (activeControlled === undefined) setActiveUncontrolled(null);
|
|
@@ -442,7 +782,10 @@ export function SessionChrome({
|
|
|
442
782
|
return;
|
|
443
783
|
}
|
|
444
784
|
const chip = chipRefs.current[active];
|
|
445
|
-
if (!chip)
|
|
785
|
+
if (!chip) {
|
|
786
|
+
setPill((prev) => ({ ...prev, opacity: 0 }));
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
446
789
|
// Measure against the chip's own box so a wrapped multi-row rail never
|
|
447
790
|
// stretches the highlight into a tall stripe across every signal.
|
|
448
791
|
const railBox = rail.getBoundingClientRect();
|
|
@@ -471,28 +814,7 @@ export function SessionChrome({
|
|
|
471
814
|
|
|
472
815
|
const open = active !== null;
|
|
473
816
|
|
|
474
|
-
|
|
475
|
-
if (!open) {
|
|
476
|
-
setPanelHeight(0);
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
|
-
const node = panelBodyRef.current;
|
|
480
|
-
if (!node) return;
|
|
481
|
-
setPanelHeight(node.offsetHeight);
|
|
482
|
-
}, [open, active, incoming, queuedTurns, record, goalState, agentsPanel, agentsSignal, steering]);
|
|
483
|
-
|
|
484
|
-
useEffect(() => {
|
|
485
|
-
if (!open) return;
|
|
486
|
-
const node = panelBodyRef.current;
|
|
487
|
-
if (!node || typeof ResizeObserver === "undefined") return;
|
|
488
|
-
const observer = new ResizeObserver(() => {
|
|
489
|
-
setPanelHeight(node.offsetHeight);
|
|
490
|
-
});
|
|
491
|
-
observer.observe(node);
|
|
492
|
-
return () => observer.disconnect();
|
|
493
|
-
}, [open, active]);
|
|
494
|
-
|
|
495
|
-
if (signals.length === 0) return null;
|
|
817
|
+
if (signals.length === 0 && !stopping) return null;
|
|
496
818
|
|
|
497
819
|
const shellDuration = reduceMotion ? 0 : 0.22;
|
|
498
820
|
const crossfadeDuration = reduceMotion ? 0 : 0.18;
|
|
@@ -500,16 +822,22 @@ export function SessionChrome({
|
|
|
500
822
|
|
|
501
823
|
const panelBody =
|
|
502
824
|
active === "incoming" ? (
|
|
503
|
-
<IncomingPanel
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
stoppingKind={stoppingKind}
|
|
508
|
-
text={steering?.text}
|
|
825
|
+
<IncomingPanel
|
|
826
|
+
inputs={incoming}
|
|
827
|
+
onDismiss={onDismissIncoming}
|
|
828
|
+
onOpenSession={onOpenSession}
|
|
509
829
|
/>
|
|
510
830
|
) : active === "queue" ? (
|
|
511
831
|
<QueuePanel
|
|
512
832
|
turns={queuedTurns}
|
|
833
|
+
optimistic={optimisticQueued}
|
|
834
|
+
loadError={queue.error}
|
|
835
|
+
mutationError={queue.mutationError}
|
|
836
|
+
onRefresh={queue.refresh}
|
|
837
|
+
onClearMutationError={queue.clearMutationError}
|
|
838
|
+
onRetryOptimistic={composer?.retryOptimisticMessage}
|
|
839
|
+
onRestoreOptimistic={composer?.restoreOptimisticMessage}
|
|
840
|
+
onRemoveOptimistic={composer?.removeOptimisticMessage}
|
|
513
841
|
readOnly={!canMutateQueue}
|
|
514
842
|
mutationFor={queue.mutationFor}
|
|
515
843
|
replaceDraftFor={replaceDraftFor}
|
|
@@ -519,10 +847,16 @@ export function SessionChrome({
|
|
|
519
847
|
? () => {
|
|
520
848
|
const turnId = replaceDraftFor;
|
|
521
849
|
setReplaceDraftFor(null);
|
|
522
|
-
void
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
850
|
+
void (async () => {
|
|
851
|
+
const checkedOut = await queue.editTurn(turnId, {
|
|
852
|
+
expectedDraftRevision: composer.draftRevision,
|
|
853
|
+
replaceDraft: true,
|
|
854
|
+
});
|
|
855
|
+
if (checkedOut) {
|
|
856
|
+
composer.applyDraft(checkedOut);
|
|
857
|
+
onComposerFocus?.();
|
|
858
|
+
}
|
|
859
|
+
})();
|
|
526
860
|
}
|
|
527
861
|
: undefined
|
|
528
862
|
}
|
|
@@ -533,10 +867,16 @@ export function SessionChrome({
|
|
|
533
867
|
composer,
|
|
534
868
|
() => setReplaceDraftFor(turn.id),
|
|
535
869
|
() => {
|
|
536
|
-
void
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
870
|
+
void (async () => {
|
|
871
|
+
const checkedOut = await queue.editTurn(turn.id, {
|
|
872
|
+
expectedDraftRevision: composer.draftRevision,
|
|
873
|
+
replaceDraft: false,
|
|
874
|
+
});
|
|
875
|
+
if (checkedOut) {
|
|
876
|
+
composer.applyDraft(checkedOut);
|
|
877
|
+
onComposerFocus?.();
|
|
878
|
+
}
|
|
879
|
+
})();
|
|
540
880
|
},
|
|
541
881
|
);
|
|
542
882
|
}
|
|
@@ -566,6 +906,8 @@ export function SessionChrome({
|
|
|
566
906
|
/>
|
|
567
907
|
) : active === "goal" && record && goalState && goal ? (
|
|
568
908
|
<GoalPanel goal={goal} state={goalState} elapsed={elapsed} readOnly={readOnly} />
|
|
909
|
+
) : active === "commands" ? (
|
|
910
|
+
<div data-og-session-chrome-panel="commands">{commandsPanel}</div>
|
|
569
911
|
) : active === "agents" ? (
|
|
570
912
|
<div data-og-session-chrome-panel="agents">
|
|
571
913
|
{agentsPanel ?? <p className="text-og-xs text-og-fg-muted">No agent details.</p>}
|
|
@@ -577,6 +919,7 @@ export function SessionChrome({
|
|
|
577
919
|
<div
|
|
578
920
|
className={cn("og-session-chrome og-root w-full", className)}
|
|
579
921
|
data-testid="session-chrome"
|
|
922
|
+
ref={rootRef}
|
|
580
923
|
data-og-session-chrome=""
|
|
581
924
|
data-og-session-chrome-open={open ? "true" : "false"}
|
|
582
925
|
>
|
|
@@ -587,15 +930,9 @@ export function SessionChrome({
|
|
|
587
930
|
)}
|
|
588
931
|
style={{
|
|
589
932
|
borderRadius: "var(--_og-session-chrome-radius)",
|
|
590
|
-
background:
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
borderColor: open
|
|
594
|
-
? "var(--_og-session-chrome-border-open)"
|
|
595
|
-
: "var(--_og-session-chrome-border)",
|
|
596
|
-
boxShadow: open
|
|
597
|
-
? "var(--og-session-chrome-shadow-open)"
|
|
598
|
-
: "var(--og-session-chrome-shadow)",
|
|
933
|
+
background: "transparent",
|
|
934
|
+
borderColor: "transparent",
|
|
935
|
+
boxShadow: "none",
|
|
599
936
|
transitionDuration: "var(--og-session-chrome-duration)",
|
|
600
937
|
transitionTimingFunction: "var(--_og-session-chrome-ease)",
|
|
601
938
|
}}
|
|
@@ -611,9 +948,9 @@ export function SessionChrome({
|
|
|
611
948
|
>
|
|
612
949
|
<div
|
|
613
950
|
ref={railRef}
|
|
614
|
-
className="relative flex flex-
|
|
951
|
+
className="relative flex flex-nowrap items-center"
|
|
615
952
|
style={{
|
|
616
|
-
gap: "
|
|
953
|
+
gap: "10px",
|
|
617
954
|
}}
|
|
618
955
|
>
|
|
619
956
|
<motion.div
|
|
@@ -633,108 +970,307 @@ export function SessionChrome({
|
|
|
633
970
|
}}
|
|
634
971
|
transition={{ duration: shellDuration, ease }}
|
|
635
972
|
/>
|
|
636
|
-
{signals
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
973
|
+
{signals
|
|
974
|
+
.filter((signal) => !["incoming", "agents", "commands"].includes(signal.id))
|
|
975
|
+
.map((signal) => {
|
|
976
|
+
const selected = active === signal.id;
|
|
977
|
+
return (
|
|
978
|
+
<div
|
|
979
|
+
key={signal.id}
|
|
980
|
+
role="group"
|
|
981
|
+
aria-label={
|
|
982
|
+
signal.id === "queue"
|
|
983
|
+
? "Queued messages and actions"
|
|
984
|
+
: signal.id === "goal"
|
|
985
|
+
? "Goal and actions"
|
|
986
|
+
: signal.label
|
|
987
|
+
}
|
|
988
|
+
className={cn(
|
|
989
|
+
"group/signal relative z-[1] inline-flex min-w-0 max-w-full items-center rounded-og-md",
|
|
990
|
+
"bg-og-surface-2/60 px-0.5",
|
|
991
|
+
selected && "bg-og-surface-3",
|
|
992
|
+
)}
|
|
993
|
+
>
|
|
994
|
+
<button
|
|
995
|
+
type="button"
|
|
996
|
+
ref={(node) => {
|
|
997
|
+
chipRefs.current[signal.id] = node;
|
|
998
|
+
}}
|
|
999
|
+
aria-expanded={selected}
|
|
1000
|
+
aria-controls={panelId}
|
|
1001
|
+
aria-label={
|
|
1002
|
+
signal.id === "goal"
|
|
1003
|
+
? `Goal · ${goalState === "pursuing" ? "Running" : signal.label}`
|
|
1004
|
+
: selected
|
|
1005
|
+
? `Close ${signal.label}`
|
|
1006
|
+
: undefined
|
|
1007
|
+
}
|
|
1008
|
+
data-testid={`session-chrome-${signal.id}`}
|
|
1009
|
+
data-og-session-chrome-signal={signal.id}
|
|
1010
|
+
title={signal.title ?? signal.label}
|
|
1011
|
+
onClick={() => {
|
|
1012
|
+
setActivityOpen(false);
|
|
1013
|
+
setActive(selected ? null : signal.id);
|
|
1014
|
+
}}
|
|
1015
|
+
className={cn(
|
|
1016
|
+
"group relative z-[1] inline-flex min-w-0 min-h-[var(--og-session-chrome-chip-min-height)] max-w-full items-center gap-1 rounded-og-md py-1 text-left text-og-xs outline-hidden",
|
|
1017
|
+
// Coarse pointers keep a 44px target (session-pins acceptance).
|
|
1018
|
+
"pointer-coarse:min-h-11",
|
|
1019
|
+
"transition-colors duration-150 motion-reduce:transition-none",
|
|
1020
|
+
"hover:text-og-fg focus-visible:bg-og-surface-3/50",
|
|
1021
|
+
selected ? "text-og-fg" : "text-og-fg-muted",
|
|
1022
|
+
)}
|
|
1023
|
+
style={{
|
|
1024
|
+
paddingInline: "var(--og-session-chrome-chip-pad-x)",
|
|
1025
|
+
}}
|
|
1026
|
+
>
|
|
1027
|
+
{signal.id === "queue" && queueArrivalNonce > 0 && !reduceMotion ? (
|
|
1028
|
+
<motion.span
|
|
1029
|
+
key={queueArrivalNonce}
|
|
1030
|
+
aria-hidden="true"
|
|
1031
|
+
data-testid="session-chrome-queue-arrival"
|
|
1032
|
+
className="pointer-events-none absolute inset-0 rounded-og-md bg-og-accent-soft"
|
|
1033
|
+
initial={{ opacity: 0 }}
|
|
1034
|
+
animate={{ opacity: [0, 0.38, 0] }}
|
|
1035
|
+
transition={{ duration: 0.72, times: [0, 0.18, 1], ease }}
|
|
1036
|
+
/>
|
|
1037
|
+
) : null}
|
|
1038
|
+
<span className={cn("shrink-0", toneClass(signal.tone, selected))}>
|
|
1039
|
+
{signal.icon}
|
|
671
1040
|
</span>
|
|
672
|
-
<span className="
|
|
673
|
-
{signal.
|
|
1041
|
+
<span className={cn("font-medium text-og-fg", "min-w-0 truncate")}>
|
|
1042
|
+
{signal.id === "goal" ? "Goal · " : null}
|
|
1043
|
+
{signal.id === "goal"
|
|
1044
|
+
? goalState === "pursuing"
|
|
1045
|
+
? "Running"
|
|
1046
|
+
: signal.label
|
|
1047
|
+
: signal.id === "queue"
|
|
1048
|
+
? queue.error || queue.mutationError
|
|
1049
|
+
? signal.label
|
|
1050
|
+
: `${queuedTurns.length + optimisticQueued.length} queued`
|
|
1051
|
+
: signal.label}
|
|
674
1052
|
</span>
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
1053
|
+
</button>
|
|
1054
|
+
{signal.id === "goal" && goal && record && !readOnly ? (
|
|
1055
|
+
<div className="flex shrink-0 items-center pr-1 pl-0.5">
|
|
1056
|
+
{record.status === "active" || record.status === "paused" ? (
|
|
1057
|
+
<IconAction
|
|
1058
|
+
label={record.status === "paused" ? "Resume goal" : "Pause goal"}
|
|
1059
|
+
tip={record.status === "paused" ? "Resume goal" : "Pause goal"}
|
|
1060
|
+
disabled={goal.updating}
|
|
1061
|
+
onClick={() =>
|
|
1062
|
+
void (record.status === "paused"
|
|
1063
|
+
? goal.resume()
|
|
1064
|
+
: goal.pause("Paused from session chrome"))
|
|
1065
|
+
}
|
|
1066
|
+
>
|
|
1067
|
+
{goal.updating ? (
|
|
1068
|
+
<Loader2Icon className="size-3 animate-og-spin" />
|
|
1069
|
+
) : record.status === "paused" ? (
|
|
1070
|
+
<PlayIcon className="size-3" />
|
|
1071
|
+
) : (
|
|
1072
|
+
<PauseIcon className="size-3" />
|
|
1073
|
+
)}
|
|
1074
|
+
</IconAction>
|
|
1075
|
+
) : null}
|
|
1076
|
+
<IconAction
|
|
1077
|
+
label="Clear goal"
|
|
1078
|
+
tip="Clear goal"
|
|
1079
|
+
danger
|
|
1080
|
+
disabled={goal.updating}
|
|
1081
|
+
onClick={() => void goal.deleteGoal()}
|
|
1082
|
+
>
|
|
1083
|
+
<Trash2Icon className="size-3" />
|
|
1084
|
+
</IconAction>
|
|
1085
|
+
</div>
|
|
1086
|
+
) : signal.id === "queue" && canMutateQueue && queuedTurns[0] ? (
|
|
1087
|
+
<div className="flex shrink-0 items-center pr-1 pl-0.5">
|
|
1088
|
+
<IconAction
|
|
1089
|
+
label="Steer first queued message"
|
|
1090
|
+
analyticsAction="steer"
|
|
1091
|
+
text="Steer"
|
|
1092
|
+
tip={QUEUE_STEER_TIP}
|
|
1093
|
+
disabled={
|
|
1094
|
+
queue.mutating || Boolean(queue.mutationFor(queuedTurns[0].id))
|
|
1095
|
+
}
|
|
1096
|
+
onClick={() => void queue.steerTurn(queuedTurns[0]!.id)}
|
|
1097
|
+
>
|
|
1098
|
+
<CornerDownRightIcon className="size-3" />
|
|
1099
|
+
</IconAction>
|
|
1100
|
+
</div>
|
|
1101
|
+
) : null}
|
|
1102
|
+
</div>
|
|
1103
|
+
);
|
|
1104
|
+
})}
|
|
1105
|
+
{signals.some((signal) => ["incoming", "agents", "commands"].includes(signal.id)) ? (
|
|
1106
|
+
<button
|
|
1107
|
+
type="button"
|
|
1108
|
+
aria-label="Session activity"
|
|
1109
|
+
aria-describedby={commandsCount > 0 ? `${panelId}-commands-count` : undefined}
|
|
1110
|
+
title={activityOpen ? "Close activity" : "Inbox, agents and commands"}
|
|
1111
|
+
aria-controls={panelId}
|
|
1112
|
+
aria-expanded={activityOpen}
|
|
1113
|
+
onClick={() => {
|
|
1114
|
+
setActivityOpen(!activityOpen);
|
|
1115
|
+
setActive(
|
|
1116
|
+
activityOpen
|
|
1117
|
+
? null
|
|
1118
|
+
: commandsCount > 0 && hasCommandsPanel
|
|
1119
|
+
? "commands"
|
|
1120
|
+
: (signals.find((signal) =>
|
|
1121
|
+
["incoming", "agents", "commands"].includes(signal.id),
|
|
1122
|
+
)?.id ?? null),
|
|
1123
|
+
);
|
|
1124
|
+
}}
|
|
1125
|
+
className={cn(
|
|
1126
|
+
"ml-auto inline-flex min-h-8 shrink-0 items-center justify-center gap-1.5 rounded-og-md px-2 text-og-xs transition-colors outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent/40 pointer-coarse:min-h-11 pointer-coarse:min-w-11",
|
|
1127
|
+
activityOpen
|
|
1128
|
+
? "bg-og-surface-3 text-og-fg"
|
|
1129
|
+
: "text-og-fg-muted hover:bg-og-surface-2 hover:text-og-fg",
|
|
1130
|
+
)}
|
|
1131
|
+
>
|
|
1132
|
+
<ActivityIcon className="size-3.5" />
|
|
1133
|
+
<span className="sr-only">Activity</span>
|
|
1134
|
+
{commandsCount > 0 ? (
|
|
1135
|
+
<>
|
|
678
1136
|
<span
|
|
679
|
-
|
|
680
|
-
className="
|
|
681
|
-
|
|
682
|
-
>
|
|
683
|
-
|
|
1137
|
+
aria-hidden="true"
|
|
1138
|
+
className="size-1.5 shrink-0 animate-og-pulse rounded-full bg-og-fg-muted motion-reduce:animate-none"
|
|
1139
|
+
/>
|
|
1140
|
+
<span id={`${panelId}-commands-count`}>
|
|
1141
|
+
{commandsCount} command{commandsCount === 1 ? "" : "s"}
|
|
684
1142
|
</span>
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
1143
|
+
</>
|
|
1144
|
+
) : null}
|
|
1145
|
+
{incoming.length > 0 ? (
|
|
1146
|
+
<span className="size-1.5 rounded-full bg-og-accent" />
|
|
1147
|
+
) : null}
|
|
1148
|
+
</button>
|
|
1149
|
+
) : null}
|
|
1150
|
+
{stopping ? (
|
|
1151
|
+
<span
|
|
1152
|
+
role="status"
|
|
1153
|
+
aria-live="polite"
|
|
1154
|
+
className="inline-flex min-h-[var(--og-session-chrome-chip-min-height)] items-center gap-1.5 px-1.5 text-og-xs text-og-fg-muted"
|
|
1155
|
+
data-testid="session-chrome-stopping"
|
|
1156
|
+
>
|
|
1157
|
+
<Loader2Icon
|
|
1158
|
+
aria-hidden="true"
|
|
1159
|
+
className="size-3 animate-og-spin motion-reduce:animate-none"
|
|
1160
|
+
/>
|
|
1161
|
+
{stoppingKind === "current" ? "Current work stopping" : "Previous work stopping"}
|
|
1162
|
+
</span>
|
|
1163
|
+
) : null}
|
|
689
1164
|
</div>
|
|
690
1165
|
</div>
|
|
691
1166
|
|
|
692
|
-
<
|
|
1167
|
+
<AnimatePresence initial={false}>
|
|
1168
|
+
{activityOpen ? (
|
|
1169
|
+
<motion.div
|
|
1170
|
+
key="activity-tabs"
|
|
1171
|
+
initial={{ height: 0, opacity: 0 }}
|
|
1172
|
+
animate={{ height: "auto", opacity: 1 }}
|
|
1173
|
+
exit={{ height: 0, opacity: 0 }}
|
|
1174
|
+
transition={{ duration: shellDuration, ease }}
|
|
1175
|
+
style={{ overflow: "hidden" }}
|
|
1176
|
+
>
|
|
1177
|
+
<div className="mt-1 flex items-center gap-1 rounded-t-og-lg bg-og-surface-2/50 px-2 py-1">
|
|
1178
|
+
{signals
|
|
1179
|
+
.filter((signal) => ["incoming", "agents", "commands"].includes(signal.id))
|
|
1180
|
+
.map((signal) => (
|
|
1181
|
+
<button
|
|
1182
|
+
key={signal.id}
|
|
1183
|
+
type="button"
|
|
1184
|
+
aria-expanded={active === signal.id}
|
|
1185
|
+
onClick={() => {
|
|
1186
|
+
if (activityVisibleRef.current) setActive(signal.id);
|
|
1187
|
+
}}
|
|
1188
|
+
className={cn(
|
|
1189
|
+
"inline-flex min-h-9 items-center gap-1.5 rounded-og-md px-2 text-og-xs pointer-coarse:min-h-11",
|
|
1190
|
+
active === signal.id
|
|
1191
|
+
? "bg-og-surface-3 text-og-fg"
|
|
1192
|
+
: "text-og-fg-muted hover:text-og-fg",
|
|
1193
|
+
)}
|
|
1194
|
+
>
|
|
1195
|
+
{signal.icon}
|
|
1196
|
+
{signal.id === "incoming" ? `${incoming.length} incoming` : signal.label}
|
|
1197
|
+
</button>
|
|
1198
|
+
))}
|
|
1199
|
+
</div>
|
|
1200
|
+
</motion.div>
|
|
1201
|
+
) : null}
|
|
1202
|
+
</AnimatePresence>
|
|
1203
|
+
{goal?.mutationError ? (
|
|
1204
|
+
<p role="alert" className="px-3 pb-2 text-og-xs text-og-danger">
|
|
1205
|
+
Goal action not confirmed. {formatError(goal.mutationError)}
|
|
1206
|
+
</p>
|
|
1207
|
+
) : null}
|
|
1208
|
+
<div
|
|
693
1209
|
id={panelId}
|
|
694
1210
|
data-og-session-chrome-panel-shell=""
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
1211
|
+
aria-hidden={!open}
|
|
1212
|
+
inert={!open ? true : undefined}
|
|
1213
|
+
className="grid overflow-hidden motion-reduce:transition-none"
|
|
1214
|
+
style={{
|
|
1215
|
+
gridTemplateRows: open ? "1fr" : "0fr",
|
|
698
1216
|
opacity: open ? 1 : 0,
|
|
1217
|
+
pointerEvents: open ? "auto" : "none",
|
|
1218
|
+
transitionProperty: "grid-template-rows, opacity",
|
|
1219
|
+
transitionDuration: "var(--og-session-chrome-duration)",
|
|
1220
|
+
transitionTimingFunction: "var(--_og-session-chrome-ease)",
|
|
699
1221
|
}}
|
|
700
|
-
transition={{ duration: shellDuration, ease }}
|
|
701
|
-
className="overflow-hidden"
|
|
702
1222
|
>
|
|
703
|
-
<div
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
1223
|
+
<div className="min-h-0 overflow-hidden">
|
|
1224
|
+
<div
|
|
1225
|
+
className={cn(
|
|
1226
|
+
"relative overflow-y-auto overscroll-contain",
|
|
1227
|
+
activityOpen
|
|
1228
|
+
? "rounded-b-og-lg bg-og-surface-2/50"
|
|
1229
|
+
: "mt-1 rounded-og-lg bg-og-surface-2/50",
|
|
1230
|
+
)}
|
|
1231
|
+
style={{
|
|
1232
|
+
maxHeight: "var(--og-session-chrome-panel-max-height)",
|
|
1233
|
+
paddingInline: "var(--og-session-chrome-panel-pad-x)",
|
|
1234
|
+
paddingBlock: "var(--og-session-chrome-panel-pad-y)",
|
|
1235
|
+
}}
|
|
1236
|
+
>
|
|
1237
|
+
<AnimatePresence initial={false}>
|
|
1238
|
+
{active && active !== "commands" && panelBody ? (
|
|
1239
|
+
<motion.div
|
|
1240
|
+
key={active}
|
|
1241
|
+
data-og-session-chrome-panel-frame={active}
|
|
1242
|
+
initial={reduceMotion ? false : { opacity: 0 }}
|
|
1243
|
+
animate={{ opacity: 1, position: "relative" }}
|
|
1244
|
+
exit={
|
|
1245
|
+
reduceMotion
|
|
1246
|
+
? { opacity: 1, position: "relative" }
|
|
1247
|
+
: {
|
|
1248
|
+
opacity: 0,
|
|
1249
|
+
position: "absolute",
|
|
1250
|
+
top: 0,
|
|
1251
|
+
left: 0,
|
|
1252
|
+
right: 0,
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
transition={{ duration: crossfadeDuration, ease }}
|
|
1256
|
+
>
|
|
1257
|
+
{panelBody}
|
|
1258
|
+
</motion.div>
|
|
1259
|
+
) : null}
|
|
1260
|
+
</AnimatePresence>
|
|
1261
|
+
{active === "commands" ? (
|
|
714
1262
|
<motion.div
|
|
715
|
-
key=
|
|
716
|
-
data-og-session-chrome-panel-frame={active}
|
|
1263
|
+
key="commands"
|
|
717
1264
|
initial={reduceMotion ? false : { opacity: 0 }}
|
|
718
|
-
animate={{ opacity: 1
|
|
719
|
-
exit={
|
|
720
|
-
reduceMotion
|
|
721
|
-
? { opacity: 1, position: "relative" }
|
|
722
|
-
: {
|
|
723
|
-
opacity: 0,
|
|
724
|
-
position: "absolute",
|
|
725
|
-
top: 0,
|
|
726
|
-
left: 0,
|
|
727
|
-
right: 0,
|
|
728
|
-
}
|
|
729
|
-
}
|
|
1265
|
+
animate={{ opacity: 1 }}
|
|
730
1266
|
transition={{ duration: crossfadeDuration, ease }}
|
|
731
1267
|
>
|
|
732
1268
|
{panelBody}
|
|
733
1269
|
</motion.div>
|
|
734
1270
|
) : null}
|
|
735
|
-
</
|
|
1271
|
+
</div>
|
|
736
1272
|
</div>
|
|
737
|
-
</
|
|
1273
|
+
</div>
|
|
738
1274
|
</div>
|
|
739
1275
|
</div>
|
|
740
1276
|
</TooltipProvider>
|
|
@@ -744,93 +1280,72 @@ export function SessionChrome({
|
|
|
744
1280
|
function IncomingPanel({
|
|
745
1281
|
inputs,
|
|
746
1282
|
onDismiss,
|
|
1283
|
+
onOpenSession,
|
|
747
1284
|
}: {
|
|
748
1285
|
inputs: SessionPendingInputPreview[];
|
|
749
1286
|
onDismiss?: ((inputId: string) => void) | undefined;
|
|
1287
|
+
onOpenSession?: ((sessionId: string) => void) | undefined;
|
|
750
1288
|
}) {
|
|
751
1289
|
return (
|
|
752
|
-
<
|
|
753
|
-
className="
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
className={cn(
|
|
764
|
-
"mt-px shrink-0 rounded px-1 py-px text-[10px] font-medium leading-4",
|
|
765
|
-
input.classification === "action_required" || input.classification === "failure"
|
|
766
|
-
? "bg-og-status-waiting/12 text-og-status-waiting"
|
|
767
|
-
: "bg-og-surface-3/80 text-og-fg-muted",
|
|
768
|
-
)}
|
|
1290
|
+
<div>
|
|
1291
|
+
<p className="mb-2 text-og-xs text-og-fg-subtle">Waiting to be included in an agent turn.</p>
|
|
1292
|
+
<ul
|
|
1293
|
+
className="flex flex-col gap-0.5"
|
|
1294
|
+
aria-label="Incoming updates"
|
|
1295
|
+
data-og-session-chrome-panel="incoming"
|
|
1296
|
+
>
|
|
1297
|
+
{inputs.map((input) => (
|
|
1298
|
+
<li
|
|
1299
|
+
key={input.id}
|
|
1300
|
+
className="group flex items-start gap-1.5 rounded-og-sm px-1.5 py-1 transition-colors hover:bg-[var(--_og-session-chrome-row-hover)]"
|
|
769
1301
|
>
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
1302
|
+
<span
|
|
1303
|
+
className={cn(
|
|
1304
|
+
"mt-px shrink-0 rounded px-1 py-px text-[10px] font-medium leading-4",
|
|
1305
|
+
input.classification === "action_required" || input.classification === "failure"
|
|
1306
|
+
? "bg-og-status-waiting/12 text-og-status-waiting"
|
|
1307
|
+
: "bg-og-surface-3/80 text-og-fg-muted",
|
|
1308
|
+
)}
|
|
1309
|
+
>
|
|
1310
|
+
{pendingKindLabel(input.kind)}
|
|
1311
|
+
</span>
|
|
1312
|
+
<div className="min-w-0 flex-1">
|
|
1313
|
+
<p className="break-words text-og-xs leading-4 text-og-fg">{input.summary}</p>
|
|
1314
|
+
<ChildSessionLink
|
|
1315
|
+
kind={input.kind}
|
|
1316
|
+
sourceId={input.sourceId}
|
|
1317
|
+
onOpenSession={onOpenSession}
|
|
1318
|
+
/>
|
|
783
1319
|
</div>
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
}) {
|
|
800
|
-
return (
|
|
801
|
-
<div
|
|
802
|
-
className="flex items-start gap-2 rounded-og-sm px-1.5 py-1"
|
|
803
|
-
role="status"
|
|
804
|
-
aria-live="polite"
|
|
805
|
-
data-og-session-chrome-panel="steering"
|
|
806
|
-
>
|
|
807
|
-
<span className="mt-0.5 inline-flex size-5 shrink-0 items-center justify-center rounded-full bg-og-accent-soft text-og-accent">
|
|
808
|
-
{phase === "submitting" || phase === "stopping" ? (
|
|
809
|
-
<Loader2Icon className="size-3 animate-og-spin" aria-hidden="true" />
|
|
810
|
-
) : (
|
|
811
|
-
<ZapIcon className="size-3" aria-hidden="true" />
|
|
812
|
-
)}
|
|
813
|
-
</span>
|
|
814
|
-
<div className="min-w-0">
|
|
815
|
-
{text ? (
|
|
816
|
-
<p className="truncate text-og-xs font-medium leading-4 text-og-fg">{text}</p>
|
|
817
|
-
) : null}
|
|
818
|
-
<p className={cn(text && "mt-0.5", "text-[10px] leading-4 text-og-fg-muted")}>
|
|
819
|
-
{phase === "submitting"
|
|
820
|
-
? "Sending your latest direction…"
|
|
821
|
-
: phase === "stopping"
|
|
822
|
-
? stoppingKind === "previous"
|
|
823
|
-
? "Direction saved. Waiting for the previous command to stop safely."
|
|
824
|
-
: "Waiting for the current command to stop safely."
|
|
825
|
-
: "Direction accepted. The agent will continue from it."}
|
|
826
|
-
</p>
|
|
827
|
-
</div>
|
|
1320
|
+
{onDismiss ? (
|
|
1321
|
+
<div className="flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100 max-sm:opacity-100">
|
|
1322
|
+
<IconAction
|
|
1323
|
+
label={`Dismiss incoming ${pendingKindLabel(input.kind)}`}
|
|
1324
|
+
tip="Dismiss"
|
|
1325
|
+
onClick={() => onDismiss(input.id)}
|
|
1326
|
+
danger
|
|
1327
|
+
>
|
|
1328
|
+
<Trash2Icon className="size-3" />
|
|
1329
|
+
</IconAction>
|
|
1330
|
+
</div>
|
|
1331
|
+
) : null}
|
|
1332
|
+
</li>
|
|
1333
|
+
))}
|
|
1334
|
+
</ul>
|
|
828
1335
|
</div>
|
|
829
1336
|
);
|
|
830
1337
|
}
|
|
831
1338
|
|
|
832
1339
|
function QueuePanel({
|
|
833
1340
|
turns,
|
|
1341
|
+
optimistic,
|
|
1342
|
+
loadError,
|
|
1343
|
+
mutationError,
|
|
1344
|
+
onRefresh,
|
|
1345
|
+
onClearMutationError,
|
|
1346
|
+
onRetryOptimistic,
|
|
1347
|
+
onRestoreOptimistic,
|
|
1348
|
+
onRemoveOptimistic,
|
|
834
1349
|
readOnly,
|
|
835
1350
|
mutationFor,
|
|
836
1351
|
replaceDraftFor,
|
|
@@ -842,6 +1357,14 @@ function QueuePanel({
|
|
|
842
1357
|
onMove,
|
|
843
1358
|
}: {
|
|
844
1359
|
turns: SessionTurn[];
|
|
1360
|
+
optimistic: ComposerOptimisticMessage[];
|
|
1361
|
+
loadError: Error | null;
|
|
1362
|
+
mutationError: Error | null;
|
|
1363
|
+
onRefresh: () => Promise<void>;
|
|
1364
|
+
onClearMutationError: () => void;
|
|
1365
|
+
onRetryOptimistic?: ((clientEventId: string) => void) | undefined;
|
|
1366
|
+
onRestoreOptimistic?: ((clientEventId: string) => void) | undefined;
|
|
1367
|
+
onRemoveOptimistic?: ((clientEventId: string) => void) | undefined;
|
|
845
1368
|
readOnly: boolean;
|
|
846
1369
|
mutationFor: UseTurnQueueResult["mutationFor"];
|
|
847
1370
|
replaceDraftFor?: string | null | undefined;
|
|
@@ -852,6 +1375,28 @@ function QueuePanel({
|
|
|
852
1375
|
onRemove?: ((turnId: string) => void) | undefined;
|
|
853
1376
|
onMove?: ((turnId: string, beforeTurnId: string | null) => void) | undefined;
|
|
854
1377
|
}) {
|
|
1378
|
+
const reduceMotion = useReducedMotion();
|
|
1379
|
+
const turnIdsKey = turns.map((turn) => turn.id).join(",");
|
|
1380
|
+
const [interactiveTurnIds, setInteractiveTurnIds] = useState<ReadonlySet<string>>(
|
|
1381
|
+
() => new Set(),
|
|
1382
|
+
);
|
|
1383
|
+
useEffect(() => {
|
|
1384
|
+
const visibleTurnIds = new Set(turnIdsKey ? turnIdsKey.split(",") : []);
|
|
1385
|
+
setInteractiveTurnIds((current) => {
|
|
1386
|
+
const retained = new Set([...current].filter((turnId) => visibleTurnIds.has(turnId)));
|
|
1387
|
+
return retained.size === current.size ? current : retained;
|
|
1388
|
+
});
|
|
1389
|
+
if (visibleTurnIds.size === 0) return;
|
|
1390
|
+
// An optimistic queue row and its authoritative replacement have different
|
|
1391
|
+
// React identities. Do not expose an actionable control during that short
|
|
1392
|
+
// handoff: a pointer can otherwise press the outgoing DOM node and release
|
|
1393
|
+
// over its replacement, producing a completed-looking click with no event.
|
|
1394
|
+
const timer = setTimeout(
|
|
1395
|
+
() => setInteractiveTurnIds((current) => new Set([...current, ...visibleTurnIds])),
|
|
1396
|
+
reduceMotion ? 0 : 240,
|
|
1397
|
+
);
|
|
1398
|
+
return () => clearTimeout(timer);
|
|
1399
|
+
}, [reduceMotion, turnIdsKey]);
|
|
855
1400
|
return (
|
|
856
1401
|
<ol
|
|
857
1402
|
className="flex flex-col gap-0.5"
|
|
@@ -862,6 +1407,7 @@ function QueuePanel({
|
|
|
862
1407
|
const presentation = queuedTurnPresentation(turn);
|
|
863
1408
|
const voice = presentation.kind !== "prompt";
|
|
864
1409
|
const pending = mutationFor(turn.id);
|
|
1410
|
+
const settling = !interactiveTurnIds.has(turn.id);
|
|
865
1411
|
const beforeUp = index > 0 ? (turns[index - 1]?.id ?? null) : null;
|
|
866
1412
|
const beforeDown = index < turns.length - 1 ? (turns[index + 2]?.id ?? null) : null;
|
|
867
1413
|
const showActions = !readOnly && (onEdit || onSteer || onRemove || onMove);
|
|
@@ -870,6 +1416,7 @@ function QueuePanel({
|
|
|
870
1416
|
<li
|
|
871
1417
|
key={turn.id}
|
|
872
1418
|
data-queue-turn-id={turn.id}
|
|
1419
|
+
tabIndex={-1}
|
|
873
1420
|
className="group flex flex-col gap-1 rounded-og-sm px-1.5 py-1 transition-colors hover:bg-[var(--_og-session-chrome-row-hover)]"
|
|
874
1421
|
>
|
|
875
1422
|
<div className="flex items-start gap-1.5">
|
|
@@ -883,9 +1430,10 @@ function QueuePanel({
|
|
|
883
1430
|
{index + 1}
|
|
884
1431
|
</span>
|
|
885
1432
|
)}
|
|
886
|
-
<
|
|
887
|
-
{presentation.text}
|
|
888
|
-
|
|
1433
|
+
<CompactQueueItemContent
|
|
1434
|
+
text={presentation.text}
|
|
1435
|
+
annotations={turn.annotations ?? []}
|
|
1436
|
+
/>
|
|
889
1437
|
{showActions ? (
|
|
890
1438
|
<div className="flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100 max-sm:opacity-100">
|
|
891
1439
|
{onMove && turns.length > 1 ? (
|
|
@@ -893,7 +1441,7 @@ function QueuePanel({
|
|
|
893
1441
|
<IconAction
|
|
894
1442
|
label={`Move queued prompt ${index + 1} up`}
|
|
895
1443
|
tip="Move up"
|
|
896
|
-
disabled={pending !== null || index === 0}
|
|
1444
|
+
disabled={settling || pending !== null || index === 0}
|
|
897
1445
|
onClick={() => onMove(turn.id, beforeUp)}
|
|
898
1446
|
>
|
|
899
1447
|
<ArrowUpIcon className="size-3" />
|
|
@@ -901,7 +1449,7 @@ function QueuePanel({
|
|
|
901
1449
|
<IconAction
|
|
902
1450
|
label={`Move queued prompt ${index + 1} down`}
|
|
903
1451
|
tip="Move down"
|
|
904
|
-
disabled={pending !== null || index >= turns.length - 1}
|
|
1452
|
+
disabled={settling || pending !== null || index >= turns.length - 1}
|
|
905
1453
|
onClick={() => onMove(turn.id, beforeDown)}
|
|
906
1454
|
>
|
|
907
1455
|
<ArrowDownIcon className="size-3" />
|
|
@@ -911,14 +1459,16 @@ function QueuePanel({
|
|
|
911
1459
|
{onSteer ? (
|
|
912
1460
|
<IconAction
|
|
913
1461
|
label={`Steer queued prompt ${index + 1}`}
|
|
1462
|
+
analyticsAction="steer"
|
|
1463
|
+
text="Steer"
|
|
914
1464
|
tip={QUEUE_STEER_TIP}
|
|
915
|
-
disabled={pending !== null}
|
|
1465
|
+
disabled={settling || pending !== null}
|
|
916
1466
|
onClick={() => onSteer(turn.id)}
|
|
917
1467
|
>
|
|
918
1468
|
{pending === "steer" ? (
|
|
919
1469
|
<Loader2Icon className="size-3 animate-og-spin" />
|
|
920
1470
|
) : (
|
|
921
|
-
<
|
|
1471
|
+
<CornerDownRightIcon className="size-3" />
|
|
922
1472
|
)}
|
|
923
1473
|
</IconAction>
|
|
924
1474
|
) : null}
|
|
@@ -926,7 +1476,7 @@ function QueuePanel({
|
|
|
926
1476
|
<IconAction
|
|
927
1477
|
label={`Edit queued prompt ${index + 1}`}
|
|
928
1478
|
tip={QUEUE_EDIT_TIP}
|
|
929
|
-
disabled={pending !== null}
|
|
1479
|
+
disabled={settling || pending !== null}
|
|
930
1480
|
onClick={() => onEdit(turn)}
|
|
931
1481
|
>
|
|
932
1482
|
{pending === "edit" ? (
|
|
@@ -940,7 +1490,7 @@ function QueuePanel({
|
|
|
940
1490
|
<IconAction
|
|
941
1491
|
label={`Remove queued prompt ${index + 1}`}
|
|
942
1492
|
tip={QUEUE_DELETE_TIP}
|
|
943
|
-
disabled={pending !== null}
|
|
1493
|
+
disabled={settling || pending !== null}
|
|
944
1494
|
onClick={() => onRemove(turn.id)}
|
|
945
1495
|
danger
|
|
946
1496
|
>
|
|
@@ -971,7 +1521,7 @@ function QueuePanel({
|
|
|
971
1521
|
</button>
|
|
972
1522
|
<button
|
|
973
1523
|
type="button"
|
|
974
|
-
className="rounded-og-sm bg-og-
|
|
1524
|
+
className="rounded-og-sm border border-og-primary-border bg-og-primary text-og-primary-fg px-2 py-1 font-medium hover:bg-og-primary-hover focus-visible:ring-2 focus-visible:ring-og-accent/40"
|
|
975
1525
|
onClick={onConfirmReplace}
|
|
976
1526
|
>
|
|
977
1527
|
Replace and edit
|
|
@@ -982,10 +1532,140 @@ function QueuePanel({
|
|
|
982
1532
|
</li>
|
|
983
1533
|
);
|
|
984
1534
|
})}
|
|
1535
|
+
<AnimatePresence initial={false}>
|
|
1536
|
+
{optimistic.map((message, index) => (
|
|
1537
|
+
<motion.li
|
|
1538
|
+
key={message.clientEventId}
|
|
1539
|
+
aria-live="polite"
|
|
1540
|
+
data-optimistic-queue-message={message.clientEventId}
|
|
1541
|
+
initial={reduceMotion ? false : { opacity: 0 }}
|
|
1542
|
+
animate={{ opacity: 1 }}
|
|
1543
|
+
transition={{ duration: reduceMotion ? 0 : 0.12, ease: [0.22, 1, 0.36, 1] }}
|
|
1544
|
+
className={cn(
|
|
1545
|
+
"flex min-h-6 items-center gap-1.5 rounded-og-sm px-1.5 py-1",
|
|
1546
|
+
message.state === "failed" ? "bg-og-danger/8 text-og-danger" : "bg-og-accent-soft/45",
|
|
1547
|
+
)}
|
|
1548
|
+
>
|
|
1549
|
+
<span className="shrink-0 font-og-mono text-[10px] leading-4 text-og-fg-subtle">
|
|
1550
|
+
{turns.length + index + 1}
|
|
1551
|
+
</span>
|
|
1552
|
+
<div className="min-w-0 flex-1">
|
|
1553
|
+
<CompactQueueItemContent text={message.text} annotations={message.annotations} />
|
|
1554
|
+
{message.state === "failed" && message.error ? (
|
|
1555
|
+
<p className="break-words text-og-xs">{message.error}</p>
|
|
1556
|
+
) : null}
|
|
1557
|
+
</div>
|
|
1558
|
+
<span className="sr-only">
|
|
1559
|
+
{message.state === "failed"
|
|
1560
|
+
? message.outcomeUnknown
|
|
1561
|
+
? "Not confirmed"
|
|
1562
|
+
: "Message not sent"
|
|
1563
|
+
: message.state === "sending"
|
|
1564
|
+
? "Placing in queue"
|
|
1565
|
+
: "Queued"}
|
|
1566
|
+
</span>
|
|
1567
|
+
{message.state === "failed" ? (
|
|
1568
|
+
<div className="flex shrink-0 items-center gap-1 text-[10px]">
|
|
1569
|
+
{message.retryable !== false && onRetryOptimistic ? (
|
|
1570
|
+
<button
|
|
1571
|
+
type="button"
|
|
1572
|
+
className="rounded-og-sm px-1.5 py-1 font-medium hover:bg-og-surface-2 pointer-coarse:min-h-11 pointer-coarse:min-w-11"
|
|
1573
|
+
onClick={() => onRetryOptimistic(message.clientEventId)}
|
|
1574
|
+
>
|
|
1575
|
+
Retry
|
|
1576
|
+
</button>
|
|
1577
|
+
) : null}
|
|
1578
|
+
{message.retryable === false && onRestoreOptimistic ? (
|
|
1579
|
+
<button
|
|
1580
|
+
type="button"
|
|
1581
|
+
className="rounded-og-sm px-1.5 py-1 font-medium hover:bg-og-surface-2 pointer-coarse:min-h-11 pointer-coarse:min-w-11"
|
|
1582
|
+
onClick={() => onRestoreOptimistic(message.clientEventId)}
|
|
1583
|
+
>
|
|
1584
|
+
Edit message
|
|
1585
|
+
</button>
|
|
1586
|
+
) : null}
|
|
1587
|
+
{onRemoveOptimistic ? (
|
|
1588
|
+
<button
|
|
1589
|
+
type="button"
|
|
1590
|
+
aria-label="Dismiss unconfirmed queued prompt"
|
|
1591
|
+
className="rounded-og-sm p-1 text-og-fg-muted hover:bg-og-surface-2 hover:text-og-fg pointer-coarse:min-h-11 pointer-coarse:min-w-11"
|
|
1592
|
+
onClick={() => onRemoveOptimistic(message.clientEventId)}
|
|
1593
|
+
>
|
|
1594
|
+
<XIcon className="size-3" />
|
|
1595
|
+
</button>
|
|
1596
|
+
) : null}
|
|
1597
|
+
</div>
|
|
1598
|
+
) : message.state === "sending" ? (
|
|
1599
|
+
<Loader2Icon
|
|
1600
|
+
aria-hidden="true"
|
|
1601
|
+
className="size-3 shrink-0 animate-og-spin text-og-accent motion-reduce:animate-none"
|
|
1602
|
+
/>
|
|
1603
|
+
) : (
|
|
1604
|
+
<CheckIcon aria-hidden="true" className="size-3 shrink-0 text-og-accent" />
|
|
1605
|
+
)}
|
|
1606
|
+
</motion.li>
|
|
1607
|
+
))}
|
|
1608
|
+
</AnimatePresence>
|
|
1609
|
+
{loadError ? (
|
|
1610
|
+
<li
|
|
1611
|
+
role="alert"
|
|
1612
|
+
className="flex items-center gap-1.5 rounded-og-sm bg-og-danger/8 px-1.5 py-1 text-og-xs text-og-danger"
|
|
1613
|
+
>
|
|
1614
|
+
<TriangleAlertIcon className="size-3 shrink-0" />
|
|
1615
|
+
<span className="min-w-0 flex-1">Queue unavailable.</span>
|
|
1616
|
+
<button
|
|
1617
|
+
type="button"
|
|
1618
|
+
className="rounded-og-sm px-1.5 py-1 font-medium hover:bg-og-surface-2 pointer-coarse:min-h-11 pointer-coarse:min-w-11"
|
|
1619
|
+
onClick={() => void onRefresh()}
|
|
1620
|
+
>
|
|
1621
|
+
Retry
|
|
1622
|
+
</button>
|
|
1623
|
+
</li>
|
|
1624
|
+
) : null}
|
|
1625
|
+
{mutationError ? (
|
|
1626
|
+
<li
|
|
1627
|
+
role="alert"
|
|
1628
|
+
className="flex items-center gap-1.5 rounded-og-sm bg-og-danger/8 px-1.5 py-1 text-og-xs text-og-danger"
|
|
1629
|
+
>
|
|
1630
|
+
<TriangleAlertIcon className="size-3 shrink-0" />
|
|
1631
|
+
<span className="min-w-0 flex-1">Not confirmed. Check the queue before retrying.</span>
|
|
1632
|
+
<button
|
|
1633
|
+
type="button"
|
|
1634
|
+
className="rounded-og-sm px-1.5 py-1 font-medium hover:bg-og-surface-2 pointer-coarse:min-h-11 pointer-coarse:min-w-11"
|
|
1635
|
+
onClick={onClearMutationError}
|
|
1636
|
+
>
|
|
1637
|
+
Dismiss
|
|
1638
|
+
</button>
|
|
1639
|
+
</li>
|
|
1640
|
+
) : null}
|
|
985
1641
|
</ol>
|
|
986
1642
|
);
|
|
987
1643
|
}
|
|
988
1644
|
|
|
1645
|
+
function CompactQueueItemContent({
|
|
1646
|
+
text,
|
|
1647
|
+
annotations,
|
|
1648
|
+
}: {
|
|
1649
|
+
text: string;
|
|
1650
|
+
annotations: readonly TimelineAnnotationLike[];
|
|
1651
|
+
}) {
|
|
1652
|
+
const content = queueItemContent(text, annotations.length);
|
|
1653
|
+
return (
|
|
1654
|
+
<div className="flex min-w-0 flex-1 items-center gap-1.5">
|
|
1655
|
+
{content === "text" ? (
|
|
1656
|
+
<p className="min-w-0 flex-1 truncate text-og-xs leading-4 text-og-fg">{text}</p>
|
|
1657
|
+
) : content === "unavailable" ? (
|
|
1658
|
+
<p className="min-w-0 flex-1 truncate text-og-xs leading-4 text-og-fg-muted italic">
|
|
1659
|
+
{QUEUE_ITEM_CONTENT_UNAVAILABLE}
|
|
1660
|
+
</p>
|
|
1661
|
+
) : null}
|
|
1662
|
+
{annotations.length > 0 ? (
|
|
1663
|
+
<TimelineAnnotationsChip annotations={annotations} className="shrink-0" compact />
|
|
1664
|
+
) : null}
|
|
1665
|
+
</div>
|
|
1666
|
+
);
|
|
1667
|
+
}
|
|
1668
|
+
|
|
989
1669
|
function GoalPanel({
|
|
990
1670
|
goal,
|
|
991
1671
|
state,
|
|
@@ -997,17 +1677,19 @@ function GoalPanel({
|
|
|
997
1677
|
elapsed: string | null;
|
|
998
1678
|
readOnly: boolean;
|
|
999
1679
|
}) {
|
|
1680
|
+
const formatError = useErrorMessage();
|
|
1000
1681
|
const record = goal.goal;
|
|
1001
1682
|
if (!record) return null;
|
|
1002
1683
|
const canToggle = !readOnly && (record.status === "active" || record.status === "paused");
|
|
1684
|
+
const explanation = sessionChromeGoalPillExplanation(state, record);
|
|
1003
1685
|
|
|
1004
1686
|
return (
|
|
1005
1687
|
<div className="space-y-1.5" data-og-session-chrome-panel="goal">
|
|
1006
1688
|
<div className="flex flex-wrap items-center gap-1.5 text-[10px] font-medium uppercase tracking-wider text-og-fg-subtle">
|
|
1007
|
-
<span>{
|
|
1689
|
+
<span>{sessionChromeGoalPillLabel(state, record)}</span>
|
|
1008
1690
|
{elapsed ? (
|
|
1009
1691
|
<span className="tabular-nums normal-case tracking-normal text-og-fg-muted">
|
|
1010
|
-
· {elapsed}
|
|
1692
|
+
· Created {elapsed} ago
|
|
1011
1693
|
</span>
|
|
1012
1694
|
) : null}
|
|
1013
1695
|
<span className="normal-case tracking-normal text-og-fg-muted">· v{record.version}</span>
|
|
@@ -1018,15 +1700,23 @@ function GoalPanel({
|
|
|
1018
1700
|
<span className="font-medium text-og-fg">Done when</span> {record.successCriteria}
|
|
1019
1701
|
</p>
|
|
1020
1702
|
) : null}
|
|
1703
|
+
{explanation ? (
|
|
1704
|
+
<p
|
|
1705
|
+
data-og-session-chrome-goal-explanation
|
|
1706
|
+
className="text-og-xs leading-4 text-og-fg-muted"
|
|
1707
|
+
>
|
|
1708
|
+
{explanation}
|
|
1709
|
+
</p>
|
|
1710
|
+
) : null}
|
|
1021
1711
|
{record.continuation?.lastError ? (
|
|
1022
1712
|
<p className="rounded-og-sm bg-og-status-waiting/10 px-1.5 py-1 text-og-xs leading-4 text-og-status-waiting">
|
|
1023
|
-
{record.continuation.lastError}
|
|
1713
|
+
{formatError(record.continuation.lastError)}
|
|
1024
1714
|
</p>
|
|
1025
1715
|
) : null}
|
|
1026
1716
|
<div className="flex flex-wrap items-center justify-between gap-1.5 pt-0.5">
|
|
1027
1717
|
<div className="flex flex-wrap gap-1 text-[10px] text-og-fg-muted">
|
|
1028
1718
|
<span className="rounded bg-og-surface-3/70 px-1 py-px">
|
|
1029
|
-
{record.autoContinuations}
|
|
1719
|
+
{record.autoContinuations} consecutive unattended continues
|
|
1030
1720
|
</span>
|
|
1031
1721
|
<span className="rounded bg-og-surface-3/70 px-1 py-px">
|
|
1032
1722
|
{record.noProgressStreak} stalled
|
|
@@ -1081,7 +1771,9 @@ const QUEUE_EDIT_TIP = "Edit in composer";
|
|
|
1081
1771
|
const QUEUE_DELETE_TIP = "Delete this queued prompt";
|
|
1082
1772
|
|
|
1083
1773
|
function IconAction({
|
|
1774
|
+
text,
|
|
1084
1775
|
label,
|
|
1776
|
+
analyticsAction,
|
|
1085
1777
|
tip,
|
|
1086
1778
|
onClick,
|
|
1087
1779
|
disabled,
|
|
@@ -1089,6 +1781,9 @@ function IconAction({
|
|
|
1089
1781
|
children,
|
|
1090
1782
|
}: {
|
|
1091
1783
|
label: string;
|
|
1784
|
+
/** Stable, content-free control label a host's product analytics may read. */
|
|
1785
|
+
analyticsAction?: "steer";
|
|
1786
|
+
text?: string;
|
|
1092
1787
|
tip: ReactNode;
|
|
1093
1788
|
onClick: () => void;
|
|
1094
1789
|
disabled?: boolean;
|
|
@@ -1101,16 +1796,20 @@ function IconAction({
|
|
|
1101
1796
|
<button
|
|
1102
1797
|
type="button"
|
|
1103
1798
|
aria-label={label}
|
|
1799
|
+
data-analytics-action={analyticsAction}
|
|
1104
1800
|
disabled={disabled}
|
|
1105
1801
|
onClick={onClick}
|
|
1106
1802
|
className={cn(
|
|
1107
|
-
"inline-flex size-6 items-center justify-center rounded-og-sm
|
|
1803
|
+
"inline-flex size-6 items-center justify-center rounded-og-sm outline-hidden transition-colors",
|
|
1108
1804
|
"hover:bg-og-surface-2 hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent/40",
|
|
1109
1805
|
"disabled:pointer-events-none disabled:opacity-40 pointer-coarse:size-9",
|
|
1806
|
+
text ? "text-og-fg" : "text-og-fg-subtle",
|
|
1807
|
+
text && "w-auto gap-1 px-1.5 text-og-xs pointer-coarse:w-auto",
|
|
1110
1808
|
danger && "hover:text-og-danger",
|
|
1111
1809
|
)}
|
|
1112
1810
|
>
|
|
1113
1811
|
{children}
|
|
1812
|
+
{text}
|
|
1114
1813
|
</button>
|
|
1115
1814
|
</TooltipTrigger>
|
|
1116
1815
|
<TooltipContent side="top">{tip}</TooltipContent>
|