@memberjunction/ng-conversations 6.1.4 → 6.2.0-edge.1
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 +36 -3
- package/dist/lib/components/active-tasks/active-tasks-panel.component.d.ts +19 -3
- package/dist/lib/components/active-tasks/active-tasks-panel.component.d.ts.map +1 -1
- package/dist/lib/components/active-tasks/active-tasks-panel.component.js +39 -7
- package/dist/lib/components/active-tasks/active-tasks-panel.component.js.map +1 -1
- package/dist/lib/components/agent/active-agent-indicator.component.d.ts +74 -6
- package/dist/lib/components/agent/active-agent-indicator.component.d.ts.map +1 -1
- package/dist/lib/components/agent/active-agent-indicator.component.js +111 -21
- package/dist/lib/components/agent/active-agent-indicator.component.js.map +1 -1
- package/dist/lib/components/agent/agent-process-panel.component.d.ts +31 -5
- package/dist/lib/components/agent/agent-process-panel.component.d.ts.map +1 -1
- package/dist/lib/components/agent/agent-process-panel.component.js +71 -15
- package/dist/lib/components/agent/agent-process-panel.component.js.map +1 -1
- package/dist/lib/components/artifact/artifact-share-modal.component.d.ts +65 -9
- package/dist/lib/components/artifact/artifact-share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/artifact/artifact-share-modal.component.js +169 -57
- package/dist/lib/components/artifact/artifact-share-modal.component.js.map +1 -1
- package/dist/lib/components/attachment/image-viewer.component.d.ts +77 -11
- package/dist/lib/components/attachment/image-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/attachment/image-viewer.component.js +181 -47
- package/dist/lib/components/attachment/image-viewer.component.js.map +1 -1
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.d.ts +129 -19
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.js +350 -116
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.js.map +1 -1
- package/dist/lib/components/collection/artifact-create-modal.component.d.ts +60 -8
- package/dist/lib/components/collection/artifact-create-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/artifact-create-modal.component.js +130 -46
- package/dist/lib/components/collection/artifact-create-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-artifact-card.component.d.ts +63 -3
- package/dist/lib/components/collection/collection-artifact-card.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-artifact-card.component.js +113 -19
- package/dist/lib/components/collection/collection-artifact-card.component.js.map +1 -1
- package/dist/lib/components/collection/collection-form-modal.component.d.ts +54 -8
- package/dist/lib/components/collection/collection-form-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-form-modal.component.js +130 -46
- package/dist/lib/components/collection/collection-form-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-share-modal.component.d.ts +70 -10
- package/dist/lib/components/collection/collection-share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-share-modal.component.js +175 -53
- package/dist/lib/components/collection/collection-share-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-tree.component.d.ts +89 -9
- package/dist/lib/components/collection/collection-tree.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-tree.component.js +220 -70
- package/dist/lib/components/collection/collection-tree.component.js.map +1 -1
- package/dist/lib/components/collection/collection-view.component.d.ts +89 -11
- package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-view.component.js +154 -40
- package/dist/lib/components/collection/collection-view.component.js.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.d.ts +390 -38
- package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.js +951 -367
- package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
- package/dist/lib/components/composer/ai-composer.component.d.ts +213 -18
- package/dist/lib/components/composer/ai-composer.component.d.ts.map +1 -1
- package/dist/lib/components/composer/ai-composer.component.js +388 -44
- package/dist/lib/components/composer/ai-composer.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-agent-picker.component.d.ts +12 -1
- package/dist/lib/components/conversation/conversation-agent-picker.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-agent-picker.component.js +26 -4
- package/dist/lib/components/conversation/conversation-agent-picker.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +1090 -123
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +2407 -731
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.d.ts +139 -19
- package/dist/lib/components/conversation/conversation-empty-state.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.js +296 -59
- package/dist/lib/components/conversation/conversation-empty-state.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-list.component.d.ts +579 -78
- package/dist/lib/components/conversation/conversation-list.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-list.component.js +2322 -823
- package/dist/lib/components/conversation/conversation-list.component.js.map +1 -1
- package/dist/lib/components/conversation/pinned-messages-panel.component.d.ts +40 -4
- package/dist/lib/components/conversation/pinned-messages-panel.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/pinned-messages-panel.component.js +72 -14
- package/dist/lib/components/conversation/pinned-messages-panel.component.js.map +1 -1
- package/dist/lib/components/dialogs/input-dialog.component.d.ts +57 -11
- package/dist/lib/components/dialogs/input-dialog.component.d.ts.map +1 -1
- package/dist/lib/components/dialogs/input-dialog.component.js +128 -16
- package/dist/lib/components/dialogs/input-dialog.component.js.map +1 -1
- package/dist/lib/components/dialogs/rating-dialog.component.d.ts +68 -10
- package/dist/lib/components/dialogs/rating-dialog.component.d.ts.map +1 -1
- package/dist/lib/components/dialogs/rating-dialog.component.js +151 -27
- package/dist/lib/components/dialogs/rating-dialog.component.js.map +1 -1
- package/dist/lib/components/export/export-modal.component.d.ts +84 -10
- package/dist/lib/components/export/export-modal.component.d.ts.map +1 -1
- package/dist/lib/components/export/export-modal.component.js +173 -49
- package/dist/lib/components/export/export-modal.component.js.map +1 -1
- package/dist/lib/components/global-tasks/global-tasks-panel.component.d.ts +29 -3
- package/dist/lib/components/global-tasks/global-tasks-panel.component.d.ts.map +1 -1
- package/dist/lib/components/global-tasks/global-tasks-panel.component.js +58 -12
- package/dist/lib/components/global-tasks/global-tasks-panel.component.js.map +1 -1
- package/dist/lib/components/library/library-full-view.component.d.ts +57 -8
- package/dist/lib/components/library/library-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/library/library-full-view.component.js +119 -35
- package/dist/lib/components/library/library-full-view.component.js.map +1 -1
- package/dist/lib/components/members/members-modal.component.d.ts +76 -10
- package/dist/lib/components/members/members-modal.component.d.ts.map +1 -1
- package/dist/lib/components/members/members-modal.component.js +164 -50
- package/dist/lib/components/members/members-modal.component.js.map +1 -1
- package/dist/lib/components/message/actionable-commands.component.d.ts +37 -5
- package/dist/lib/components/message/actionable-commands.component.d.ts.map +1 -1
- package/dist/lib/components/message/actionable-commands.component.js +82 -16
- package/dist/lib/components/message/actionable-commands.component.js.map +1 -1
- package/dist/lib/components/message/conversation-message-rating.component.d.ts +46 -10
- package/dist/lib/components/message/conversation-message-rating.component.d.ts.map +1 -1
- package/dist/lib/components/message/conversation-message-rating.component.js +120 -36
- package/dist/lib/components/message/conversation-message-rating.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +549 -74
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +1250 -421
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +411 -29
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +896 -222
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +288 -29
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +561 -123
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/navigation/conversation-navigation.component.d.ts +64 -5
- package/dist/lib/components/navigation/conversation-navigation.component.d.ts.map +1 -1
- package/dist/lib/components/navigation/conversation-navigation.component.js +101 -11
- package/dist/lib/components/navigation/conversation-navigation.component.js.map +1 -1
- package/dist/lib/components/notification/activity-indicator.component.d.ts +6 -2
- package/dist/lib/components/notification/activity-indicator.component.d.ts.map +1 -1
- package/dist/lib/components/notification/activity-indicator.component.js +13 -3
- package/dist/lib/components/notification/activity-indicator.component.js.map +1 -1
- package/dist/lib/components/notification/notification-badge.component.d.ts +15 -3
- package/dist/lib/components/notification/notification-badge.component.d.ts.map +1 -1
- package/dist/lib/components/notification/notification-badge.component.js +36 -8
- package/dist/lib/components/notification/notification-badge.component.js.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.d.ts +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.js +4 -4
- package/dist/lib/components/overlay/chat-overlay.component.js.map +1 -1
- package/dist/lib/components/project/project-form-modal.component.d.ts +93 -11
- package/dist/lib/components/project/project-form-modal.component.d.ts.map +1 -1
- package/dist/lib/components/project/project-form-modal.component.js +348 -87
- package/dist/lib/components/project/project-form-modal.component.js.map +1 -1
- package/dist/lib/components/project/project-selector.component.d.ts +78 -8
- package/dist/lib/components/project/project-selector.component.d.ts.map +1 -1
- package/dist/lib/components/project/project-selector.component.js +177 -46
- package/dist/lib/components/project/project-selector.component.js.map +1 -1
- package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts +1 -1
- package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.js.map +1 -1
- package/dist/lib/components/realtime/channels/client-context-channel.d.ts +1 -1
- package/dist/lib/components/realtime/channels/client-context-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/channels/client-context-channel.js +1 -1
- package/dist/lib/components/realtime/channels/client-context-channel.js.map +1 -1
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts +1 -1
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js +3 -0
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js.map +1 -1
- package/dist/lib/components/realtime/media/media-channel.d.ts +2 -1
- package/dist/lib/components/realtime/media/media-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/media/media-channel.js +1 -1
- package/dist/lib/components/realtime/media/media-channel.js.map +1 -1
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts +2 -2
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-activity-rail.component.js +2 -2
- package/dist/lib/components/realtime/realtime-activity-rail.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-composer.component.d.ts +3 -1
- package/dist/lib/components/realtime/realtime-composer.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-composer.component.js +26 -8
- package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts +2 -2
- package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-delegation-card.component.js +2 -2
- package/dist/lib/components/realtime/realtime-delegation-card.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +4 -4
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.js +19 -19
- package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-state.d.ts +2 -3
- package/dist/lib/components/realtime/realtime-session-state.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-state.js +3 -2
- package/dist/lib/components/realtime/realtime-session-state.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-thread.component.d.ts +17 -1
- package/dist/lib/components/realtime/realtime-session-thread.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-thread.component.js +122 -45
- package/dist/lib/components/realtime/realtime-session-thread.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts +47 -31
- package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-timeline-card.component.js +138 -124
- package/dist/lib/components/realtime/realtime-session-timeline-card.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts +4 -4
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js +4 -4
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts +2 -2
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js +3 -3
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js.map +1 -1
- package/dist/lib/components/realtime/realtime-ui-config.d.ts +13 -11
- package/dist/lib/components/realtime/realtime-ui-config.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-ui-config.js +29 -25
- package/dist/lib/components/realtime/realtime-ui-config.js.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js.map +1 -1
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts +4 -2
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js +7 -3
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js.map +1 -1
- package/dist/lib/components/search/search-panel.component.d.ts +133 -22
- package/dist/lib/components/search/search-panel.component.d.ts.map +1 -1
- package/dist/lib/components/search/search-panel.component.js +312 -83
- package/dist/lib/components/search/search-panel.component.js.map +1 -1
- package/dist/lib/components/share/share-modal.component.d.ts +69 -9
- package/dist/lib/components/share/share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/share/share-modal.component.js +139 -31
- package/dist/lib/components/share/share-modal.component.js.map +1 -1
- package/dist/lib/components/shared/user-picker.component.d.ts +48 -8
- package/dist/lib/components/shared/user-picker.component.d.ts.map +1 -1
- package/dist/lib/components/shared/user-picker.component.js +110 -30
- package/dist/lib/components/shared/user-picker.component.js.map +1 -1
- package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts +114 -11
- package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts.map +1 -1
- package/dist/lib/components/sidebar/conversation-sidebar.component.js +199 -21
- package/dist/lib/components/sidebar/conversation-sidebar.component.js.map +1 -1
- package/dist/lib/components/sidebar/routines-section.component.d.ts +20 -1
- package/dist/lib/components/sidebar/routines-section.component.d.ts.map +1 -1
- package/dist/lib/components/sidebar/routines-section.component.js +25 -5
- package/dist/lib/components/sidebar/routines-section.component.js.map +1 -1
- package/dist/lib/components/slots/slot-interfaces.d.ts +19 -0
- package/dist/lib/components/slots/slot-interfaces.d.ts.map +1 -1
- package/dist/lib/components/slots/slot-interfaces.js.map +1 -1
- package/dist/lib/components/task/tasks-full-view.component.d.ts +95 -13
- package/dist/lib/components/task/tasks-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/task/tasks-full-view.component.js +223 -75
- package/dist/lib/components/task/tasks-full-view.component.js.map +1 -1
- package/dist/lib/components/tasks/task-widget.component.d.ts +46 -6
- package/dist/lib/components/tasks/task-widget.component.d.ts.map +1 -1
- package/dist/lib/components/tasks/task-widget.component.js +100 -16
- package/dist/lib/components/tasks/task-widget.component.js.map +1 -1
- package/dist/lib/components/tasks/tasks-dropdown.component.d.ts +64 -7
- package/dist/lib/components/tasks/tasks-dropdown.component.d.ts.map +1 -1
- package/dist/lib/components/tasks/tasks-dropdown.component.js +127 -27
- package/dist/lib/components/tasks/tasks-dropdown.component.js.map +1 -1
- package/dist/lib/components/thread/thread-panel.component.d.ts +66 -8
- package/dist/lib/components/thread/thread-panel.component.d.ts.map +1 -1
- package/dist/lib/components/thread/thread-panel.component.js +146 -36
- package/dist/lib/components/thread/thread-panel.component.js.map +1 -1
- package/dist/lib/components/toast/toast.component.d.ts +11 -1
- package/dist/lib/components/toast/toast.component.d.ts.map +1 -1
- package/dist/lib/components/toast/toast.component.js +25 -5
- package/dist/lib/components/toast/toast.component.js.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts +385 -42
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +887 -291
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/composer-plugins/base-conversation-mention.provider.d.ts +8 -0
- package/dist/lib/composer-plugins/base-conversation-mention.provider.d.ts.map +1 -1
- package/dist/lib/composer-plugins/base-conversation-mention.provider.js +8 -1
- package/dist/lib/composer-plugins/base-conversation-mention.provider.js.map +1 -1
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js.map +1 -1
- package/dist/lib/directives/chat-slot.directive.d.ts +5 -1
- package/dist/lib/directives/chat-slot.directive.d.ts.map +1 -1
- package/dist/lib/directives/chat-slot.directive.js.map +1 -1
- package/dist/lib/directives/search-shortcut.directive.d.ts +9 -1
- package/dist/lib/directives/search-shortcut.directive.d.ts.map +1 -1
- package/dist/lib/directives/search-shortcut.directive.js +14 -4
- package/dist/lib/directives/search-shortcut.directive.js.map +1 -1
- package/dist/lib/events/chat-events.d.ts +34 -7
- package/dist/lib/events/chat-events.d.ts.map +1 -1
- package/dist/lib/events/chat-events.js +37 -7
- package/dist/lib/events/chat-events.js.map +1 -1
- package/dist/lib/models/agent-turn.model.d.ts +103 -0
- package/dist/lib/models/agent-turn.model.d.ts.map +1 -0
- package/dist/lib/models/agent-turn.model.js +12 -0
- package/dist/lib/models/agent-turn.model.js.map +1 -0
- package/dist/lib/models/collection-view.model.js.map +1 -1
- package/dist/lib/models/conversation-state.model.d.ts +8 -0
- package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
- package/dist/lib/models/conversation-state.model.js.map +1 -1
- package/dist/lib/models/lazy-artifact-info.d.ts +44 -12
- package/dist/lib/models/lazy-artifact-info.d.ts.map +1 -1
- package/dist/lib/models/lazy-artifact-info.js +98 -34
- package/dist/lib/models/lazy-artifact-info.js.map +1 -1
- package/dist/lib/services/active-tasks.service.d.ts +30 -4
- package/dist/lib/services/active-tasks.service.d.ts.map +1 -1
- package/dist/lib/services/active-tasks.service.js +71 -19
- package/dist/lib/services/active-tasks.service.js.map +1 -1
- package/dist/lib/services/agent-state.service.d.ts +17 -1
- package/dist/lib/services/agent-state.service.d.ts.map +1 -1
- package/dist/lib/services/agent-state.service.js +48 -16
- package/dist/lib/services/agent-state.service.js.map +1 -1
- package/dist/lib/services/artifact-permission.service.d.ts +39 -0
- package/dist/lib/services/artifact-permission.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-permission.service.js +152 -17
- package/dist/lib/services/artifact-permission.service.js.map +1 -1
- package/dist/lib/services/artifact-state.service.d.ts +58 -5
- package/dist/lib/services/artifact-state.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-state.service.js +135 -35
- package/dist/lib/services/artifact-state.service.js.map +1 -1
- package/dist/lib/services/browser-realtime-media-host.d.ts +24 -0
- package/dist/lib/services/browser-realtime-media-host.d.ts.map +1 -0
- package/dist/lib/services/browser-realtime-media-host.js +95 -0
- package/dist/lib/services/browser-realtime-media-host.js.map +1 -0
- package/dist/lib/services/collection-permission.service.d.ts +28 -0
- package/dist/lib/services/collection-permission.service.d.ts.map +1 -1
- package/dist/lib/services/collection-permission.service.js +88 -32
- package/dist/lib/services/collection-permission.service.js.map +1 -1
- package/dist/lib/services/collection-state.service.d.ts +11 -1
- package/dist/lib/services/collection-state.service.d.ts.map +1 -1
- package/dist/lib/services/collection-state.service.js +25 -5
- package/dist/lib/services/collection-state.service.js.map +1 -1
- package/dist/lib/services/conversation-agent.service.d.ts +26 -4
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +57 -18
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/conversation-attachment.service.d.ts +41 -2
- package/dist/lib/services/conversation-attachment.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-attachment.service.js +80 -13
- package/dist/lib/services/conversation-attachment.service.js.map +1 -1
- package/dist/lib/services/conversation-streaming.service.d.ts +22 -0
- package/dist/lib/services/conversation-streaming.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-streaming.service.js +50 -10
- package/dist/lib/services/conversation-streaming.service.js.map +1 -1
- package/dist/lib/services/data-cache.service.d.ts +31 -0
- package/dist/lib/services/data-cache.service.d.ts.map +1 -1
- package/dist/lib/services/data-cache.service.js +71 -15
- package/dist/lib/services/data-cache.service.js.map +1 -1
- package/dist/lib/services/dialog.service.d.ts +13 -0
- package/dist/lib/services/dialog.service.d.ts.map +1 -1
- package/dist/lib/services/dialog.service.js +25 -5
- package/dist/lib/services/dialog.service.js.map +1 -1
- package/dist/lib/services/export.service.d.ts +2 -0
- package/dist/lib/services/export.service.d.ts.map +1 -1
- package/dist/lib/services/export.service.js +5 -1
- package/dist/lib/services/export.service.js.map +1 -1
- package/dist/lib/services/graphql-attachment-blob-store.d.ts +44 -0
- package/dist/lib/services/graphql-attachment-blob-store.d.ts.map +1 -0
- package/dist/lib/services/graphql-attachment-blob-store.js +113 -0
- package/dist/lib/services/graphql-attachment-blob-store.js.map +1 -0
- package/dist/lib/services/mention-autocomplete.service.d.ts +17 -141
- package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.js +15 -460
- package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
- package/dist/lib/services/mention-parser.service.d.ts +10 -0
- package/dist/lib/services/mention-parser.service.d.ts.map +1 -1
- package/dist/lib/services/mention-parser.service.js +25 -5
- package/dist/lib/services/mention-parser.service.js.map +1 -1
- package/dist/lib/services/notification.service.d.ts +54 -6
- package/dist/lib/services/notification.service.d.ts.map +1 -1
- package/dist/lib/services/notification.service.js +123 -27
- package/dist/lib/services/notification.service.js.map +1 -1
- package/dist/lib/services/realtime-audio-recorder.d.ts +4 -4
- package/dist/lib/services/realtime-audio-recorder.js +12 -12
- package/dist/lib/services/realtime-audio-recorder.js.map +1 -1
- package/dist/lib/services/realtime-pcm-wav.d.ts +4 -0
- package/dist/lib/services/realtime-pcm-wav.d.ts.map +1 -1
- package/dist/lib/services/realtime-pcm-wav.js +10 -2
- package/dist/lib/services/realtime-pcm-wav.js.map +1 -1
- package/dist/lib/services/realtime-session-review.service.d.ts +1 -1
- package/dist/lib/services/realtime-session-review.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session-review.service.js +1 -1
- package/dist/lib/services/realtime-session-review.service.js.map +1 -1
- package/dist/lib/services/realtime-session.service.d.ts +16 -1150
- package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session.service.js +28 -2430
- package/dist/lib/services/realtime-session.service.js.map +1 -1
- package/dist/lib/services/search.service.d.ts +42 -9
- package/dist/lib/services/search.service.d.ts.map +1 -1
- package/dist/lib/services/search.service.js +103 -23
- package/dist/lib/services/search.service.js.map +1 -1
- package/dist/lib/services/toast.service.d.ts +19 -1
- package/dist/lib/services/toast.service.d.ts.map +1 -1
- package/dist/lib/services/toast.service.js +45 -9
- package/dist/lib/services/toast.service.js.map +1 -1
- package/dist/lib/services/ui-command-handler.service.d.ts +16 -2
- package/dist/lib/services/ui-command-handler.service.d.ts.map +1 -1
- package/dist/lib/services/ui-command-handler.service.js +36 -8
- package/dist/lib/services/ui-command-handler.service.js.map +1 -1
- package/dist/lib/utils/agent-artifact-summary.d.ts +2 -2
- package/dist/lib/utils/agent-artifact-summary.js +3 -3
- package/dist/lib/utils/agent-artifact-summary.js.map +1 -1
- package/dist/lib/utils/agent-turn-routing.d.ts +69 -0
- package/dist/lib/utils/agent-turn-routing.d.ts.map +1 -0
- package/dist/lib/utils/agent-turn-routing.js +96 -0
- package/dist/lib/utils/agent-turn-routing.js.map +1 -0
- package/dist/lib/utils/artifact-panel-action.d.ts +15 -4
- package/dist/lib/utils/artifact-panel-action.d.ts.map +1 -1
- package/dist/lib/utils/artifact-panel-action.js +10 -2
- package/dist/lib/utils/artifact-panel-action.js.map +1 -1
- package/dist/lib/utils/distinct-artifacts.d.ts +2 -0
- package/dist/lib/utils/distinct-artifacts.d.ts.map +1 -1
- package/dist/lib/utils/distinct-artifacts.js +5 -1
- package/dist/lib/utils/distinct-artifacts.js.map +1 -1
- package/dist/lib/utils/plan-mode-preference.d.ts +2 -2
- package/dist/lib/utils/plan-mode-preference.js +6 -6
- package/dist/lib/utils/plan-mode-preference.js.map +1 -1
- package/dist/lib/utils/realtime-session-timeline.d.ts +8 -79
- package/dist/lib/utils/realtime-session-timeline.d.ts.map +1 -1
- package/dist/lib/utils/realtime-session-timeline.js +8 -89
- package/dist/lib/utils/realtime-session-timeline.js.map +1 -1
- package/dist/public-api.d.ts +5 -2
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +9 -2
- package/dist/public-api.js.map +1 -1
- package/package.json +33 -32
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts +0 -384
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts.map +0 -1
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js +0 -231
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js.map +0 -1
- package/dist/lib/services/delegation-result-parser.d.ts +0 -50
- package/dist/lib/services/delegation-result-parser.d.ts.map +0 -1
- package/dist/lib/services/delegation-result-parser.js +0 -60
- package/dist/lib/services/delegation-result-parser.js.map +0 -1
- package/dist/lib/services/narration-template.d.ts +0 -42
- package/dist/lib/services/narration-template.d.ts.map +0 -1
- package/dist/lib/services/narration-template.js +0 -73
- package/dist/lib/services/narration-template.js.map +0 -1
- package/dist/lib/services/skill-picker-narrowing.d.ts +0 -13
- package/dist/lib/services/skill-picker-narrowing.d.ts.map +0 -1
- package/dist/lib/services/skill-picker-narrowing.js +0 -13
- package/dist/lib/services/skill-picker-narrowing.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-ui-config.js","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-ui-config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Declarative UI configuration for the realtime voice widget.\n *\n * The realtime widget historically decided *what to show* from an internal\n * \"disclosure ratchet\" ({@link import('./realtime-disclosure').RealtimeDisclosureModel})\n * plus a handful of scattered getters. That made it impossible for a host\n * (the chat overlay, a full-screen route, an embedded demo surface, a kiosk,\n * a mobile sheet…) to tailor the surface to its context.\n *\n * This module introduces a single, pure, side-effect-free resolution step that\n * sits between three concerns:\n *\n * 1. **{@link RealtimeUiInputs}** — the *consumer's intent*. A flat bag of\n * `@Input()`-friendly feature switches (\"show the surface panel?\",\n * \"allow text?\", \"which chrome?\") with documented defaults. A host sets\n * only what it cares about; everything else stays on its sensible default,\n * so the widget behaves exactly as before when no inputs are provided.\n *\n * 2. **{@link RealtimeUiSignals}** — the *live runtime state*. Read-only facts\n * the widget already knows: how wide its container is, whether the user has\n * revealed text, what the disclosure model currently permits, whether a\n * surface/channel/activity exists, the connection state, review/focus modes.\n *\n * 3. **{@link ResolvedRealtimeUi}** — the *answer*. A fully-resolved, boolean\n * view-model the template binds to directly. No `&&` chains in the HTML; no\n * business logic in getters. One source of truth, trivially unit-testable.\n *\n * ## The headline behaviour: auto orb ↔ console\n *\n * The default {@link RealtimeChromeMode} is `'auto'`. In auto mode the widget is\n * a calm **orb** until *two* things are true at once:\n *\n * - the container is at least {@link RealtimeUiInputs.consoleBreakpointPx}\n * wide (there's room for a structured console), **and**\n * - the user has actually asked to see text (`textRevealed`), or the widget\n * is reviewing a past session (which always shows the transcript).\n *\n * In other words: **getting bigger is not enough — if the user hasn't asked for\n * text, we keep the orb.** This preserves the loved \"ambient by default\"\n * feeling while letting the experience graduate to a full command console the\n * moment it earns the space *and* the intent. Hosts that want to force one\n * chrome forever can pass `chrome: 'orb'` or `chrome: 'console'`.\n *\n * The resolver is intentionally framework-free (no Angular import) so it can be\n * unit-tested in isolation and reused by any host.\n *\n * @module realtime-ui-config\n */\n\nimport { RealtimeUxDensity } from './realtime-disclosure';\n\n/**\n * Which visual chrome the widget renders.\n *\n * - `'orb'` — the ambient hero orb. Minimal controls; transcript/console hidden\n * unless the user reveals them. Best for narrow/overlay/mobile contexts.\n * - `'console'` — the structured command console: header, transcript, composer\n * dock, and (when room allows) the surface panel. Best for full-screen.\n * - `'auto'` — *(default)* start as an orb; graduate to a console when the\n * container is wide enough **and** the user has revealed text. See the module\n * docs for the exact rule.\n */\nexport type RealtimeChromeMode = 'orb' | 'console' | 'auto';\n\n/**\n * Identifies a control the user invoked. Emitted by the widget's `ControlInvoked`\n * output so a host can observe (or react to) any control — even one it has hidden\n * via inputs and re-rendered itself. Extend the union as new controls are added.\n */\nexport type RealtimeControlId =\n | 'mute'\n | 'captions'\n | 'type'\n | 'end'\n | 'minimize'\n | 'surface'\n | 'gear'\n | 'reveal-text'\n | 'pure-audio'\n | 'density';\n\n/**\n * The connection lifecycle states the widget cares about for layout decisions.\n * Mirrors the widget's runtime `RealtimeConnectionState`; duplicated as a string\n * union here so this module stays import-free of the Angular component.\n */\nexport type RealtimeUiConnectionState =\n | 'connecting'\n | 'listening'\n | 'speaking'\n | 'thinking'\n | 'idle'\n | 'error';\n\n/**\n * The default container width (px) at or above which `'auto'` chrome is *allowed*\n * to graduate from orb to console — provided the user has also revealed text.\n * Chosen so a typical 390px chat overlay stays an orb, while a half-screen or\n * full-screen surface can become a console. Override per-host via\n * {@link RealtimeUiInputs.consoleBreakpointPx}.\n */\nexport const REALTIME_CONSOLE_BREAKPOINT_DEFAULT = 560;\n\n/**\n * Consumer-facing configuration for the realtime widget — every field is a\n * declarative switch a host can set via an `@Input()`. **All fields are\n * optional**; omitted fields fall back to {@link DEFAULT_REALTIME_UI_INPUTS},\n * which reproduces the widget's historical behaviour. This is the surface that\n * lets the chat overlay slim the widget down, a full-screen route open it up,\n * and unexpected hosts (kiosks, demos, embeds) compose their own variant.\n *\n * A `false` here is a **hard disable** — it removes the affordance regardless of\n * what the disclosure model or runtime state would otherwise permit. A `true`\n * means \"allow it\"; the affordance still only appears when it's also *earned* by\n * the runtime (e.g. a surface panel only renders once a channel actually exists).\n */\nexport interface RealtimeUiInputs {\n /** Which chrome to render. Default `'auto'`. */\n chrome?: RealtimeChromeMode;\n /**\n * In `'auto'` chrome, the container width (px) at/above which the widget may\n * graduate to a console (still gated on text being revealed). Default\n * {@link REALTIME_CONSOLE_BREAKPOINT_DEFAULT}.\n */\n consoleBreakpointPx?: number;\n /**\n * Force compact spacing/typography regardless of size — useful for dense\n * mobile sheets. Default `false` (the widget infers compactness from width).\n */\n compact?: boolean;\n /**\n * Fade non-essential controls when the session is idle (orb chrome only), for\n * a cinematic feel. Default `true`.\n */\n autoHideControls?: boolean;\n /**\n * May the user reveal the transcript at all? When `false`, the widget is a\n * pure voice orb with no path to text (and therefore never graduates to a\n * console in `'auto'` mode). Default `true`.\n */\n allowTextReveal?: boolean;\n\n /** Show the captions toggle. Default `true`. */\n showCaptionsControl?: boolean;\n /** Show the density picker inside the gear menu. Default `true`. */\n showDensityPicker?: boolean;\n /** Show the minimize control (collapse the call without ending it). Default `true`. */\n showMinimize?: boolean;\n /** Show the end-call control. Default `true`. (Hosts that own their own end button may hide it.) */\n showEnd?: boolean;\n /**\n * Show the right-hand surface panel (whiteboard / browser / interactive\n * channels). Default `true`. Only renders in console chrome and once a panel\n * is actually earned.\n */\n showSurfacePanel?: boolean;\n /** Show the channel strip (active interactive channels). Default `true`. */\n showChannels?: boolean;\n /** Show the Activity rail/tab (delegations, artifacts timeline). Default `true`. */\n showActivityRail?: boolean;\n /**\n * Show developer affordances (\"open session / open run\" links, dev-only\n * panels). Default `true`, but still additionally gated by the per-session\n * dev-mode toggle, so this is a hard *ceiling*, not a force-on.\n */\n showDevLinks?: boolean;\n /** Allow drag-to-resize of the surface panel. Default `true`. */\n allowResize?: boolean;\n}\n\n/**\n * The complete, defaulted input set. Spreading this over a partial\n * {@link RealtimeUiInputs} yields a fully-populated config; the resolver does\n * exactly that, so callers never deal with `undefined`.\n */\nexport const DEFAULT_REALTIME_UI_INPUTS: Required<RealtimeUiInputs> = {\n chrome: 'auto',\n consoleBreakpointPx: REALTIME_CONSOLE_BREAKPOINT_DEFAULT,\n compact: false,\n autoHideControls: true,\n allowTextReveal: true,\n showCaptionsControl: true,\n showDensityPicker: true,\n showMinimize: true,\n showEnd: true,\n showSurfacePanel: true,\n showChannels: true,\n showActivityRail: true,\n showDevLinks: true,\n allowResize: true,\n};\n\n/**\n * Live runtime facts the widget feeds into the resolver. These are *observed*,\n * not configured — they come from the disclosure model, a `ResizeObserver`, and\n * the session/channel state. Pure data so the resolver stays testable.\n */\nexport interface RealtimeUiSignals {\n /** Current width of the widget's container, in px (from a `ResizeObserver`). */\n containerWidthPx: number;\n /**\n * Has the user revealed text this session? (i.e. tapped \"show the\n * conversation\" / engaged the composer — disclosure level ≥ 1.) This is the\n * intent half of the auto orb↔console rule.\n */\n textRevealed: boolean;\n /** Disclosure model says a transcript may be shown. */\n disclosureShowThread: boolean;\n /** Disclosure model says the composer/text dock may be shown. */\n disclosureShowComposer: boolean;\n /** Disclosure model says a surface panel may be shown. */\n disclosureShowPanel: boolean;\n /** Disclosure model says the gear/settings menu may be shown. */\n disclosureShowGear: boolean;\n /** A surface/channel actually exists to populate the panel. */\n surfacePanelEarned: boolean;\n /** ≥ 1 interactive channel is active. */\n hasChannels: boolean;\n /** ≥ 1 delegation/activity has occurred (Activity rail has content). */\n hasActivity: boolean;\n /** Per-session developer mode is on. */\n devMode: boolean;\n /** The widget is reviewing a past (recorded) session, not a live call. */\n isReviewing: boolean;\n /** A single channel surface is maximized (focus mode); the main column is hidden. */\n channelFocus: boolean;\n /** Current connection lifecycle state. */\n connectionState: RealtimeUiConnectionState;\n}\n\n/**\n * The resolved, render-ready view-model. Every field is a final boolean (or the\n * effective chrome) — the template binds straight to these with no further\n * logic. This is the single source of truth for \"what is on screen right now\".\n */\nexport interface ResolvedRealtimeUi {\n /** The effective chrome after resolving `'auto'`. */\n chrome: 'orb' | 'console';\n /** Whether compact spacing/typography applies. */\n compact: boolean;\n /** True while connecting — hosts can show a single, size-independent loader. */\n connecting: boolean;\n\n /** The big ambient hero orb is the primary surface. */\n showHero: boolean;\n /** The transcript/thread is visible. */\n showThread: boolean;\n /** The text composer dock is visible. */\n showComposer: boolean;\n /** The right-hand surface panel is visible. */\n showSurfacePanel: boolean;\n /** The Activity tab/rail is available. */\n showActivityTab: boolean;\n /** The channel strip is visible. */\n showChannelStrip: boolean;\n\n /** The captions toggle is available. */\n showCaptionsControl: boolean;\n /** The density picker is available (inside the gear). */\n showDensityPicker: boolean;\n /** The gear/settings affordance is available. */\n showGear: boolean;\n /** Developer links/panels are available. */\n showDevLinks: boolean;\n /** The minimize control is available. */\n showMinimize: boolean;\n /** The end-call control is available. */\n showEnd: boolean;\n\n /** The surface panel may be drag-resized. */\n allowResize: boolean;\n /** The user has a path to reveal text. */\n allowTextReveal: boolean;\n /** Non-essential controls should auto-fade when idle. */\n autoHideControls: boolean;\n}\n\n/**\n * A safe, dependency-free baseline {@link RealtimeUiSignals}. Use this to seed a\n * resolved view-model **before** the component's runtime dependencies (the\n * disclosure model, the session state, the `ResizeObserver`) exist — e.g. in a\n * field initializer. It describes a freshly-connecting widget at zero width with\n * nothing earned yet, so the first paint is a calm orb until `recomputeUi()`\n * runs with real signals. Never read `this` to build the first value.\n */\nexport const DEFAULT_REALTIME_UI_SIGNALS: RealtimeUiSignals = {\n containerWidthPx: 0,\n textRevealed: false,\n disclosureShowThread: false,\n disclosureShowComposer: false,\n disclosureShowPanel: false,\n disclosureShowGear: false,\n surfacePanelEarned: false,\n hasChannels: false,\n hasActivity: false,\n devMode: false,\n isReviewing: false,\n channelFocus: false,\n connectionState: 'connecting',\n};\n\n/**\n * Resolve the consumer config + live signals into a render-ready view-model.\n *\n * Pure and deterministic: same inputs ⇒ same output, no side effects, no clock,\n * no DOM. Safe to call on every change-detection pass and trivial to unit test.\n *\n * The resolution order is:\n * 1. fill defaults,\n * 2. decide the effective {@link RealtimeChromeMode} (the orb↔console rule),\n * 3. for each affordance, AND the consumer's switch with the runtime gate.\n *\n * @param inputs consumer configuration (partial; defaults fill the rest)\n * @param signals live runtime facts\n * @returns the fully-resolved {@link ResolvedRealtimeUi}\n *\n * @example Chat overlay (lean, ambient):\n * ```ts\n * resolveRealtimeUi(\n * { chrome: 'auto', showSurfacePanel: false, showActivityRail: false, showDevLinks: false },\n * signals\n * );\n * ```\n *\n * @example Full-screen route (rich console):\n * ```ts\n * resolveRealtimeUi({ chrome: 'auto', consoleBreakpointPx: 480 }, wideSignals);\n * ```\n */\nexport function resolveRealtimeUi(\n inputs: RealtimeUiInputs | null | undefined,\n signals: RealtimeUiSignals,\n): ResolvedRealtimeUi {\n const cfg: Required<RealtimeUiInputs> = { ...DEFAULT_REALTIME_UI_INPUTS, ...(inputs ?? {}) };\n\n const connecting = signals.connectionState === 'connecting';\n const allowTextReveal = cfg.allowTextReveal;\n const chrome = resolveChrome(cfg, signals, allowTextReveal);\n const isConsole = chrome === 'console';\n const compact = cfg.compact || signals.containerWidthPx < cfg.consoleBreakpointPx;\n\n // The transcript shows when the user has EXPLICITLY revealed text this session\n // (`textRevealed`), OR we're in a console (consoles always carry the thread), OR we're\n // reviewing a recording. We deliberately do NOT consult the disclosure ratchet here: a\n // power user with a high base level still opens to the calm orb, exactly like the historical\n // `ShowHero = !ShowCaptions` behaviour — \"keep the orb until the user asks for text\".\n const showThread = allowTextReveal && (signals.textRevealed || isConsole || signals.isReviewing);\n\n // The hero orb owns the surface in orb chrome (and there's nothing else to show).\n const showHero = chrome === 'orb' && !showThread;\n\n const showComposer = isConsole && allowTextReveal && signals.disclosureShowComposer && !signals.isReviewing;\n\n // The surface/Details panel opens ON DEMAND when earned (the Details peek, the agent's\n // OpenSurfacePanel, content auto-reveal — all set surfacePanelEarned). It is an INDEPENDENT\n // right-hand peek that rides ALONGSIDE whatever the main column shows — it must NOT require the\n // cross-session disclosure ratchet (disclosureShowPanel = SessionLevel >= 2), and it must NOT\n // require the user to have revealed text.\n //\n // It was previously gated on `isConsole`, which bundled TWO things: \"there's room\" (legitimate —\n // don't cram a side panel into a narrow overlay) AND \"the user revealed text\" (wrong for a peek).\n // The text-reveal coupling meant opening Details with captions OFF flipped the whole left column\n // from the hero orb to an empty transcript (the orb vanished), and toggling captions back off\n // yanked the panel out from under an open peek. The design intent (see the auto-reveal comment in\n // the overlay component: \"the left column stays exactly as it was, pure audio included\") is a peek\n // beside the orb. So we keep only the ROOM half: the panel shows when earned and either the chrome\n // is already a console (incl. review / forced-console at any width) OR the container is at least as\n // wide as the console breakpoint — never in a cramped narrow overlay, and never dependent on text.\n const roomForSurfacePanel = isConsole || signals.containerWidthPx >= cfg.consoleBreakpointPx;\n const showSurfacePanel =\n cfg.showSurfacePanel && signals.surfacePanelEarned && !signals.channelFocus && roomForSurfacePanel;\n\n const showActivityTab = cfg.showActivityRail && (signals.hasActivity || signals.isReviewing) && isConsole;\n\n const showChannelStrip = cfg.showChannels && signals.hasChannels && isConsole && !signals.channelFocus;\n\n const showGear = signals.disclosureShowGear || isConsole;\n\n return {\n chrome,\n compact,\n connecting,\n showHero,\n showThread,\n showComposer,\n showSurfacePanel,\n showActivityTab,\n showChannelStrip,\n showCaptionsControl: cfg.showCaptionsControl,\n showDensityPicker: cfg.showDensityPicker && showGear,\n showGear,\n showDevLinks: cfg.showDevLinks && signals.devMode,\n showMinimize: cfg.showMinimize && !signals.isReviewing,\n showEnd: cfg.showEnd,\n allowResize: cfg.allowResize && showSurfacePanel,\n allowTextReveal,\n autoHideControls: cfg.autoHideControls && chrome === 'orb',\n };\n}\n\n/**\n * Decide the effective chrome. Forced modes win outright; `'auto'` graduates to a\n * console only when there's both **room** (width ≥ breakpoint) and **intent**\n * (text revealed) — or when reviewing, which always wants the transcript.\n *\n * @internal\n */\nfunction resolveChrome(\n cfg: Required<RealtimeUiInputs>,\n signals: RealtimeUiSignals,\n allowTextReveal: boolean,\n): 'orb' | 'console' {\n if (cfg.chrome === 'orb') {\n return 'orb';\n }\n if (cfg.chrome === 'console') {\n return 'console';\n }\n // auto\n if (signals.isReviewing) {\n return 'console';\n }\n if (!allowTextReveal) {\n return 'orb';\n }\n // Intent is the USER explicitly revealing text this session — NOT the disclosure ratchet.\n // Wider-alone keeps the orb; a power user who hasn't asked for text keeps the orb too.\n const hasRoom = signals.containerWidthPx >= cfg.consoleBreakpointPx;\n return hasRoom && signals.textRevealed ? 'console' : 'orb';\n}\n"]}
|
|
1
|
+
{"version":3,"file":"realtime-ui-config.js","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-ui-config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Declarative UI configuration for the realtime voice widget.\n *\n * The realtime widget historically decided *what to show* from an internal\n * \"disclosure ratchet\" ({@link import('./realtime-disclosure').RealtimeDisclosureModel})\n * plus a handful of scattered getters. That made it impossible for a host\n * (the chat overlay, a full-screen route, an embedded demo surface, a kiosk,\n * a mobile sheet…) to tailor the surface to its context.\n *\n * This module introduces a single, pure, side-effect-free resolution step that\n * sits between three concerns:\n *\n * 1. **{@link RealtimeUiInputs}** — the *consumer's intent*. A flat bag of\n * `@Input()`-friendly feature switches (\"show the surface panel?\",\n * \"allow text?\", \"which chrome?\") with documented defaults. A host sets\n * only what it cares about; everything else stays on its sensible default,\n * so the widget behaves exactly as before when no inputs are provided.\n *\n * 2. **{@link RealtimeUiSignals}** — the *live runtime state*. Read-only facts\n * the widget already knows: how wide its container is, whether the user has\n * revealed text, what the disclosure model currently permits, whether a\n * surface/channel/activity exists, the connection state, review/focus modes.\n *\n * 3. **{@link ResolvedRealtimeUi}** — the *answer*. A fully-resolved, boolean\n * view-model the template binds to directly. No `&&` chains in the HTML; no\n * business logic in getters. One source of truth, trivially unit-testable.\n *\n * ## The headline behaviour: auto orb ↔ console\n *\n * The default {@link RealtimeChromeMode} is `'auto'`. In auto mode the widget is\n * a calm **orb** until *two* things are true at once:\n *\n * - the container is at least {@link RealtimeUiInputs.consoleBreakpointPx}\n * wide (there's room for a structured console), **and**\n * - the user has actually asked to see text (`textRevealed`), or the widget\n * is reviewing a past session (which always shows the transcript).\n *\n * In other words: **getting bigger is not enough — if the user hasn't asked for\n * text, we keep the orb.** This preserves the loved \"ambient by default\"\n * feeling while letting the experience graduate to a full command console the\n * moment it earns the space *and* the intent. Hosts that want to force one\n * chrome forever can pass `chrome: 'orb'` or `chrome: 'console'`.\n *\n * The resolver is intentionally framework-free (no Angular import) so it can be\n * unit-tested in isolation and reused by any host.\n *\n * @module realtime-ui-config\n */\n\nimport { RealtimeUxDensity } from './realtime-disclosure';\n\n/**\n * Which visual chrome the widget renders.\n *\n * - `'orb'` — the ambient hero orb. Minimal controls; transcript/console hidden\n * unless the user reveals them. Best for narrow/overlay/mobile contexts.\n * - `'console'` — the structured command console: header, transcript, composer\n * dock, and (when room allows) the surface panel. Best for full-screen.\n * - `'auto'` — *(default)* start as an orb; graduate to a console when the\n * container is wide enough **and** the user has revealed text. See the module\n * docs for the exact rule.\n */\nexport type RealtimeChromeMode = 'orb' | 'console' | 'auto';\n\n/**\n * Identifies a control the user invoked. Emitted by the widget's `ControlInvoked`\n * output so a host can observe (or react to) any control — even one it has hidden\n * via inputs and re-rendered itself. Extend the union as new controls are added.\n */\nexport type RealtimeControlId =\n | 'mute'\n | 'captions'\n | 'type'\n | 'end'\n | 'minimize'\n | 'surface'\n | 'gear'\n | 'reveal-text'\n | 'pure-audio'\n | 'density';\n\n/**\n * The connection lifecycle states the widget cares about for layout decisions.\n * Mirrors the widget's runtime `RealtimeConnectionState`; duplicated as a string\n * union here so this module stays import-free of the Angular component.\n */\nexport type RealtimeUiConnectionState =\n | 'connecting'\n | 'listening'\n | 'speaking'\n | 'thinking'\n | 'idle'\n | 'error';\n\n/**\n * The default container width (px) at or above which `'auto'` chrome is *allowed*\n * to graduate from orb to console — provided the user has also revealed text.\n * Chosen so a typical 390px chat overlay stays an orb, while a half-screen or\n * full-screen surface can become a console. Override per-host via\n * {@link RealtimeUiInputs.consoleBreakpointPx}.\n */\nexport const REALTIME_CONSOLE_BREAKPOINT_DEFAULT = 560;\n\n/**\n * Consumer-facing configuration for the realtime widget — every field is a\n * declarative switch a host can set via an `@Input()`. **All fields are\n * optional**; omitted fields fall back to {@link DEFAULT_REALTIME_UI_INPUTS},\n * which reproduces the widget's historical behaviour. This is the surface that\n * lets the chat overlay slim the widget down, a full-screen route open it up,\n * and unexpected hosts (kiosks, demos, embeds) compose their own variant.\n *\n * A `false` here is a **hard disable** — it removes the affordance regardless of\n * what the disclosure model or runtime state would otherwise permit. A `true`\n * means \"allow it\"; the affordance still only appears when it's also *earned* by\n * the runtime (e.g. a surface panel only renders once a channel actually exists).\n */\nexport interface RealtimeUiInputs {\n /** Which chrome to render. Default `'auto'`. */\n chrome?: RealtimeChromeMode; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /**\n * In `'auto'` chrome, the container width (px) at/above which the widget may\n * graduate to a console (still gated on text being revealed). Default\n * {@link REALTIME_CONSOLE_BREAKPOINT_DEFAULT}.\n */\n consoleBreakpointPx?: number; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /**\n * Force compact spacing/typography regardless of size — useful for dense\n * mobile sheets. Default `false` (the widget infers compactness from width).\n */\n compact?: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /**\n * Fade non-essential controls when the session is idle (orb chrome only), for\n * a cinematic feel. Default `true`.\n */\n autoHideControls?: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /**\n * May the user reveal the transcript at all? When `false`, the widget is a\n * pure voice orb with no path to text (and therefore never graduates to a\n * console in `'auto'` mode). Default `true`.\n */\n allowTextReveal?: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n /** Show the captions toggle. Default `true`. */\n showCaptionsControl?: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** Show the density picker inside the gear menu. Default `true`. */\n showDensityPicker?: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** Show the minimize control (collapse the call without ending it). Default `true`. */\n showMinimize?: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** Show the end-call control. Default `true`. (Hosts that own their own end button may hide it.) */\n showEnd?: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /**\n * Show the right-hand surface panel (whiteboard / browser / interactive\n * channels). Default `true`. Only renders in console chrome and once a panel\n * is actually earned.\n */\n showSurfacePanel?: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** Show the channel strip (active interactive channels). Default `true`. */\n showChannels?: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** Show the Activity rail/tab (delegations, artifacts timeline). Default `true`. */\n showActivityRail?: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /**\n * Show developer affordances (\"open session / open run\" links, dev-only\n * panels). Default `true`, but still additionally gated by the per-session\n * dev-mode toggle, so this is a hard *ceiling*, not a force-on.\n */\n showDevLinks?: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** Allow drag-to-resize of the surface panel. Default `true`. */\n allowResize?: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n}\n\n/**\n * The complete, defaulted input set. Spreading this over a partial\n * {@link RealtimeUiInputs} yields a fully-populated config; the resolver does\n * exactly that, so callers never deal with `undefined`.\n */\nexport const DEFAULT_REALTIME_UI_INPUTS: Required<RealtimeUiInputs> = {\n chrome: 'auto',\n consoleBreakpointPx: REALTIME_CONSOLE_BREAKPOINT_DEFAULT,\n compact: false,\n autoHideControls: true,\n allowTextReveal: true,\n showCaptionsControl: true,\n showDensityPicker: true,\n showMinimize: true,\n showEnd: true,\n showSurfacePanel: true,\n showChannels: true,\n showActivityRail: true,\n showDevLinks: true,\n allowResize: true,\n};\n\n/**\n * Live runtime facts the widget feeds into the resolver. These are *observed*,\n * not configured — they come from the disclosure model, a `ResizeObserver`, and\n * the session/channel state. Pure data so the resolver stays testable.\n */\nexport interface RealtimeUiSignals {\n /** Current width of the widget's container, in px (from a `ResizeObserver`). */\n ContainerWidthPx: number;\n /**\n * Has the user revealed text this session? (i.e. tapped \"show the\n * conversation\" / engaged the composer — disclosure level ≥ 1.) This is the\n * intent half of the auto orb↔console rule.\n */\n TextRevealed: boolean;\n /** Disclosure model says a transcript may be shown. */\n DisclosureShowThread: boolean;\n /** Disclosure model says the composer/text dock may be shown. */\n DisclosureShowComposer: boolean;\n /** Disclosure model says a surface panel may be shown. */\n DisclosureShowPanel: boolean;\n /** Disclosure model says the gear/settings menu may be shown. */\n DisclosureShowGear: boolean;\n /** A surface/channel actually exists to populate the panel. */\n SurfacePanelEarned: boolean;\n /** ≥ 1 interactive channel is active. */\n hasChannels: boolean; // case-violation-ok-legacy-back-compat: the old name is also read off a value typed `any`, where a rename would compile and silently return undefined\n /** ≥ 1 delegation/activity has occurred (Activity rail has content). */\n HasActivity: boolean;\n /** Per-session developer mode is on. */\n DevMode: boolean;\n /** The widget is reviewing a past (recorded) session, not a live call. */\n IsReviewing: boolean;\n /** A single channel surface is maximized (focus mode); the main column is hidden. */\n channelFocus: boolean; // case-violation-ok-legacy-back-compat: the old name is also read off a value typed `any`, where a rename would compile and silently return undefined\n /** Current connection lifecycle state. */\n ConnectionState: RealtimeUiConnectionState;\n}\n\n/**\n * The resolved, render-ready view-model. Every field is a final boolean (or the\n * effective chrome) — the template binds straight to these with no further\n * logic. This is the single source of truth for \"what is on screen right now\".\n */\nexport interface ResolvedRealtimeUi {\n /** The effective chrome after resolving `'auto'`. */\n chrome: 'orb' | 'console'; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** Whether compact spacing/typography applies. */\n compact: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** True while connecting — hosts can show a single, size-independent loader. */\n connecting: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n /** The big ambient hero orb is the primary surface. */\n showHero: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** The transcript/thread is visible. */\n showThread: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** The text composer dock is visible. */\n showComposer: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** The right-hand surface panel is visible. */\n showSurfacePanel: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** The Activity tab/rail is available. */\n showActivityTab: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** The channel strip is visible. */\n showChannelStrip: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n /** The captions toggle is available. */\n showCaptionsControl: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** The density picker is available (inside the gear). */\n showDensityPicker: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** The gear/settings affordance is available. */\n showGear: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** Developer links/panels are available. */\n showDevLinks: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** The minimize control is available. */\n showMinimize: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** The end-call control is available. */\n showEnd: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n\n /** The surface panel may be drag-resized. */\n allowResize: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** The user has a path to reveal text. */\n allowTextReveal: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n /** Non-essential controls should auto-fade when idle. */\n autoHideControls: boolean; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n}\n\n/**\n * A safe, dependency-free baseline {@link RealtimeUiSignals}. Use this to seed a\n * resolved view-model **before** the component's runtime dependencies (the\n * disclosure model, the session state, the `ResizeObserver`) exist — e.g. in a\n * field initializer. It describes a freshly-connecting widget at zero width with\n * nothing earned yet, so the first paint is a calm orb until `recomputeUi()`\n * runs with real signals. Never read `this` to build the first value.\n */\nexport const DEFAULT_REALTIME_UI_SIGNALS: RealtimeUiSignals = {\n ContainerWidthPx: 0,\n TextRevealed: false,\n DisclosureShowThread: false,\n DisclosureShowComposer: false,\n DisclosureShowPanel: false,\n DisclosureShowGear: false,\n SurfacePanelEarned: false,\n hasChannels: false,\n HasActivity: false,\n DevMode: false,\n IsReviewing: false,\n channelFocus: false,\n ConnectionState: 'connecting',\n};\n\n/**\n * Resolve the consumer config + live signals into a render-ready view-model.\n *\n * Pure and deterministic: same inputs ⇒ same output, no side effects, no clock,\n * no DOM. Safe to call on every change-detection pass and trivial to unit test.\n *\n * The resolution order is:\n * 1. fill defaults,\n * 2. decide the effective {@link RealtimeChromeMode} (the orb↔console rule),\n * 3. for each affordance, AND the consumer's switch with the runtime gate.\n *\n * @param inputs consumer configuration (partial; defaults fill the rest)\n * @param signals live runtime facts\n * @returns the fully-resolved {@link ResolvedRealtimeUi}\n *\n * @example Chat overlay (lean, ambient):\n * ```ts\n * resolveRealtimeUi(\n * { chrome: 'auto', showSurfacePanel: false, showActivityRail: false, showDevLinks: false },\n * signals\n * );\n * ```\n *\n * @example Full-screen route (rich console):\n * ```ts\n * resolveRealtimeUi({ chrome: 'auto', consoleBreakpointPx: 480 }, wideSignals);\n * ```\n */\nexport function ResolveRealtimeUi(\n inputs: RealtimeUiInputs | null | undefined,\n signals: RealtimeUiSignals,\n): ResolvedRealtimeUi {\n const cfg: Required<RealtimeUiInputs> = { ...DEFAULT_REALTIME_UI_INPUTS, ...(inputs ?? {}) };\n\n const connecting = signals.ConnectionState === 'connecting';\n const allowTextReveal = cfg.allowTextReveal;\n const chrome = resolveChrome(cfg, signals, allowTextReveal);\n const isConsole = chrome === 'console';\n const compact = cfg.compact || signals.ContainerWidthPx < cfg.consoleBreakpointPx;\n\n // The transcript shows when the user has EXPLICITLY revealed text this session\n // (`textRevealed`), OR we're in a console (consoles always carry the thread), OR we're\n // reviewing a recording. We deliberately do NOT consult the disclosure ratchet here: a\n // power user with a high base level still opens to the calm orb, exactly like the historical\n // `ShowHero = !ShowCaptions` behaviour — \"keep the orb until the user asks for text\".\n const showThread = allowTextReveal && (signals.TextRevealed || isConsole || signals.IsReviewing);\n\n // The hero orb owns the surface in orb chrome (and there's nothing else to show).\n const showHero = chrome === 'orb' && !showThread;\n\n const showComposer = isConsole && allowTextReveal && signals.DisclosureShowComposer && !signals.IsReviewing;\n\n // The surface/Details panel opens ON DEMAND when earned (the Details peek, the agent's\n // OpenSurfacePanel, content auto-reveal — all set surfacePanelEarned). It is an INDEPENDENT\n // right-hand peek that rides ALONGSIDE whatever the main column shows — it must NOT require the\n // cross-session disclosure ratchet (disclosureShowPanel = SessionLevel >= 2), and it must NOT\n // require the user to have revealed text.\n //\n // It was previously gated on `isConsole`, which bundled TWO things: \"there's room\" (legitimate —\n // don't cram a side panel into a narrow overlay) AND \"the user revealed text\" (wrong for a peek).\n // The text-reveal coupling meant opening Details with captions OFF flipped the whole left column\n // from the hero orb to an empty transcript (the orb vanished), and toggling captions back off\n // yanked the panel out from under an open peek. The design intent (see the auto-reveal comment in\n // the overlay component: \"the left column stays exactly as it was, pure audio included\") is a peek\n // beside the orb. So we keep only the ROOM half: the panel shows when earned and either the chrome\n // is already a console (incl. review / forced-console at any width) OR the container is at least as\n // wide as the console breakpoint — never in a cramped narrow overlay, and never dependent on text.\n const roomForSurfacePanel = isConsole || signals.ContainerWidthPx >= cfg.consoleBreakpointPx;\n const showSurfacePanel =\n cfg.showSurfacePanel && signals.SurfacePanelEarned && !signals.channelFocus && roomForSurfacePanel;\n\n const showActivityTab = cfg.showActivityRail && (signals.HasActivity || signals.IsReviewing) && isConsole;\n\n const showChannelStrip = cfg.showChannels && signals.hasChannels && isConsole && !signals.channelFocus;\n\n const showGear = signals.DisclosureShowGear || isConsole;\n\n return {\n chrome,\n compact,\n connecting,\n showHero,\n showThread,\n showComposer,\n showSurfacePanel,\n showActivityTab,\n showChannelStrip,\n showCaptionsControl: cfg.showCaptionsControl,\n showDensityPicker: cfg.showDensityPicker && showGear,\n showGear,\n showDevLinks: cfg.showDevLinks && signals.DevMode,\n showMinimize: cfg.showMinimize && !signals.IsReviewing,\n showEnd: cfg.showEnd,\n allowResize: cfg.allowResize && showSurfacePanel,\n allowTextReveal,\n autoHideControls: cfg.autoHideControls && chrome === 'orb',\n };\n}\n\n/** @deprecated Use {@link ResolveRealtimeUi}. */\nexport function resolveRealtimeUi(\n inputs: RealtimeUiInputs | null | undefined,\n signals: RealtimeUiSignals,\n): ResolvedRealtimeUi {\n return ResolveRealtimeUi(inputs, signals);\n}\n\n/**\n * Decide the effective chrome. Forced modes win outright; `'auto'` graduates to a\n * console only when there's both **room** (width ≥ breakpoint) and **intent**\n * (text revealed) — or when reviewing, which always wants the transcript.\n *\n * @internal\n */\nfunction resolveChrome(\n cfg: Required<RealtimeUiInputs>,\n signals: RealtimeUiSignals,\n allowTextReveal: boolean,\n): 'orb' | 'console' {\n if (cfg.chrome === 'orb') {\n return 'orb';\n }\n if (cfg.chrome === 'console') {\n return 'console';\n }\n // auto\n if (signals.IsReviewing) {\n return 'console';\n }\n if (!allowTextReveal) {\n return 'orb';\n }\n // Intent is the USER explicitly revealing text this session — NOT the disclosure ratchet.\n // Wider-alone keeps the orb; a power user who hasn't asked for text keeps the orb too.\n const hasRoom = signals.ContainerWidthPx >= cfg.consoleBreakpointPx;\n return hasRoom && signals.TextRevealed ? 'console' : 'orb';\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Type } from '@angular/core';
|
|
2
2
|
import { RealtimeToolDefinition, RealtimeTrackDescriptor } from '@memberjunction/ai';
|
|
3
3
|
import { IChannelFrameProvider } from '@memberjunction/ai-realtime-client';
|
|
4
|
-
import { BaseRealtimeChannelClient, ChannelOnboardingDetails } from '
|
|
4
|
+
import { BaseRealtimeChannelClient, ChannelOnboardingDetails } from '@memberjunction/realtime-runtime';
|
|
5
5
|
import { RemoteBrowserSurfaceComponent } from './remote-browser-surface.component';
|
|
6
6
|
import { RemoteBrowserAudioChunkInput } from './remote-browser-audio-player';
|
|
7
7
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-browser-channel.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/remote-browser/remote-browser-channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C,OAAO,EAA0C,sBAAsB,EAAiB,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC5I,OAAO,EAA6B,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AACtG,OAAO,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"remote-browser-channel.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/remote-browser/remote-browser-channel.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ var RemoteBrowserChannel_1;
|
|
|
8
8
|
import { RegisterClass } from '@memberjunction/global';
|
|
9
9
|
import { CHANNEL_INBOUND_VIDEO_TRACK } from '@memberjunction/ai';
|
|
10
10
|
import { ChannelInboundVideoBridge } from '@memberjunction/ai-realtime-client';
|
|
11
|
-
import { BaseRealtimeChannelClient } from '
|
|
11
|
+
import { BaseRealtimeChannelClient } from '@memberjunction/realtime-runtime';
|
|
12
12
|
import { RemoteBrowserSurfaceComponent } from './remote-browser-surface.component';
|
|
13
13
|
import { MapToolToAction, REMOTE_BROWSER_TOOL_DEFINITIONS, REMOTE_BROWSER_TOOL_NAMES, REMOTE_BROWSER_TOOL_PREFIX, RemoteBrowserToolArgError, } from './remote-browser-tools';
|
|
14
14
|
import { MediaSourceAudioSink, RemoteBrowserAudioPlayer } from './remote-browser-audio-player';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-browser-channel.js","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/remote-browser/remote-browser-channel.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Type } from '@angular/core';\nimport type { Subscription } from 'rxjs';\nimport { RegisterClass } from '@memberjunction/global';\nimport { CHANNEL_INBOUND_VIDEO_TRACK, JSONValue, RealtimeToolDefinition, RealtimeTrack, RealtimeTrackDescriptor } from '@memberjunction/ai';\nimport { ChannelInboundVideoBridge, IChannelFrameProvider } from '@memberjunction/ai-realtime-client';\nimport { BaseRealtimeChannelClient, ChannelOnboardingDetails } from '../channels/base-realtime-channel-client';\nimport { RemoteBrowserHumanInputEvent, RemoteBrowserSnapshotView, RemoteBrowserSurfaceComponent } from './remote-browser-surface.component';\nimport {\n MapToolToAction,\n REMOTE_BROWSER_TOOL_DEFINITIONS,\n REMOTE_BROWSER_TOOL_NAMES,\n REMOTE_BROWSER_TOOL_PREFIX,\n RemoteBrowserAction,\n RemoteBrowserToolArgError,\n} from './remote-browser-tools';\nimport { MediaSourceAudioSink, RemoteBrowserAudioChunkInput, RemoteBrowserAudioPlayer } from './remote-browser-audio-player';\n\n/**\n * GraphQL mutation that drives the SERVER-hosted browser. The channel awaits it for every\n * `browser_*` tool call and feeds the result back to the model.\n */\nconst EXECUTE_REMOTE_BROWSER_ACTION_MUTATION = `\n mutation ExecuteRemoteBrowserAction(\n $agentSessionID: String!, $kind: String!, $url: String, $selector: String,\n $x: Float, $y: Float, $text: String, $key: String,\n $deltaX: Float, $deltaY: Float, $ms: Float\n ) {\n ExecuteRemoteBrowserAction(\n agentSessionID: $agentSessionID, kind: $kind, url: $url, selector: $selector,\n x: $x, y: $y, text: $text, key: $key,\n deltaX: $deltaX, deltaY: $deltaY, ms: $ms\n ) {\n Success\n CurrentUrl\n Detail\n }\n }\n`;\n\n/** The narrow projection of the `ExecuteRemoteBrowserAction` mutation payload the channel reads. */\ninterface ExecuteRemoteBrowserActionResult {\n ExecuteRemoteBrowserAction: {\n Success: boolean;\n CurrentUrl: string | null;\n Detail: string | null;\n } | null;\n}\n\n/**\n * GOAL mutation — hands a high-level goal to the server's autonomous browser agent (computer-use loop or\n * backend native AI) instead of relaying a granular action. The server plans + executes; we await the\n * terminal outcome. (Live per-step narration is a server-bridged concern; over this client-direct mutation\n * we get the final result.)\n */\nconst EXECUTE_REMOTE_BROWSER_GOAL_MUTATION = `\n mutation ExecuteRemoteBrowserGoal($agentSessionID: String!, $goal: String!, $startUrl: String) {\n ExecuteRemoteBrowserGoal(agentSessionID: $agentSessionID, goal: $goal, startUrl: $startUrl) {\n Success\n GoalRunID\n Strategy\n CurrentUrl\n Status\n StepCount\n Detail\n }\n }\n`;\n\n/**\n * POLL query for the async goal run. `ExecuteRemoteBrowserGoal` STARTS the (potentially multi-minute)\n * goal loop and returns a `GoalRunID` with `Status: 'Running'`; the channel polls this until the run\n * reports a terminal status — so no single request is held open for the whole loop (which would die at\n * a browser/proxy/janitor transport timeout and lose the result).\n */\nconst GET_REMOTE_BROWSER_GOAL_RESULT_QUERY = `\n query GetRemoteBrowserGoalResult($agentSessionID: String!, $goalRunID: String!) {\n GetRemoteBrowserGoalResult(agentSessionID: $agentSessionID, goalRunID: $goalRunID) {\n Success\n GoalRunID\n Strategy\n CurrentUrl\n Status\n StepCount\n Detail\n }\n }\n`;\n\n/** The narrow projection of a goal-result payload (shared by the start mutation + the poll query). */\ninterface RemoteBrowserGoalPayload {\n Success: boolean;\n GoalRunID: string | null;\n Strategy: string | null;\n CurrentUrl: string | null;\n Status: string | null;\n StepCount: number | null;\n Detail: string | null;\n}\n\n/** The narrow projection of the `ExecuteRemoteBrowserGoal` (start) mutation payload the channel reads. */\ninterface ExecuteRemoteBrowserGoalResult {\n ExecuteRemoteBrowserGoal: RemoteBrowserGoalPayload | null;\n}\n\n/** The narrow projection of the `GetRemoteBrowserGoalResult` (poll) query payload the channel reads. */\ninterface GetRemoteBrowserGoalResultPayload {\n GetRemoteBrowserGoalResult: RemoteBrowserGoalPayload | null;\n}\n\n/**\n * GraphQL mutation that asks the server to start PUSHING live CDP screencast frames for the session.\n * Returns `Streaming: true` when the backend supports `ScreenStreaming` and the stream started; the\n * channel then drives the surface's canvas. `Streaming: false` (capability absent / start failed) leaves\n * the surface on its 700ms snapshot poll fallback.\n */\nconst START_SCREENCAST_MUTATION = `\n mutation StartRemoteBrowserScreencast($agentSessionID: String!) {\n StartRemoteBrowserScreencast(agentSessionID: $agentSessionID) {\n Streaming\n }\n }\n`;\n\n/** GraphQL mutation that stops the server-pushed screencast (best-effort teardown). */\nconst STOP_SCREENCAST_MUTATION = `\n mutation StopRemoteBrowserScreencast($agentSessionID: String!) {\n StopRemoteBrowserScreencast(agentSessionID: $agentSessionID)\n }\n`;\n\n/**\n * GraphQL mutation that asks the server to start PUSHING live tab-audio chunks for the session — so a\n * co-agent demoing a video/audio site is HEARD. Returns `Streaming: true` when the backend can capture\n * audio (v1 gates by backend implementation) and the stream started; `Streaming: false` means no audio\n * (capability absent / start failed) and the client simply plays nothing.\n */\nconst START_AUDIO_STREAM_MUTATION = `\n mutation StartRemoteBrowserAudioStream($agentSessionID: String!) {\n StartRemoteBrowserAudioStream(agentSessionID: $agentSessionID) {\n Streaming\n }\n }\n`;\n\n/** GraphQL mutation that stops the server-pushed tab-audio stream (best-effort teardown). */\nconst STOP_AUDIO_STREAM_MUTATION = `\n mutation StopRemoteBrowserAudioStream($agentSessionID: String!) {\n StopRemoteBrowserAudioStream(agentSessionID: $agentSessionID)\n }\n`;\n\n/** The narrow projection of the `StartRemoteBrowserAudioStream` mutation payload the channel reads. */\ninterface StartRemoteBrowserAudioStreamResult {\n StartRemoteBrowserAudioStream: {\n Streaming: boolean;\n } | null;\n}\n\n/**\n * HUMAN-TAKEOVER mutation — relays ONE pointer/keyboard input the user performed on the live canvas into the\n * server-hosted browser (Collaborative control). Best-effort: the server returns `false` when the backend\n * lacks `HumanTakeover` or no live browser exists; the channel doesn't act on the boolean.\n */\nconst RELAY_HUMAN_INPUT_MUTATION = `\n mutation RelayRemoteBrowserHumanInput(\n $agentSessionID: String!, $kind: String!, $x: Float, $y: Float, $button: String, $key: String,\n $text: String, $deltaX: Float, $deltaY: Float, $modifiers: String\n ) {\n RelayRemoteBrowserHumanInput(\n agentSessionID: $agentSessionID, kind: $kind, x: $x, y: $y, button: $button, key: $key,\n text: $text, deltaX: $deltaX, deltaY: $deltaY, modifiers: $modifiers\n )\n }\n`;\n\n/**\n * COPY-OUT query — reads the remote page's current text selection so the surface can write it to the LOCAL\n * clipboard on a `copy` / Cmd+C (the mirror of the `'text'` paste-in path). Returns `''` when nothing is\n * selected, no live browser exists, or the backend lacks `HumanTakeover`.\n */\nconst GET_SELECTION_QUERY = `\n query GetRemoteBrowserSelection($agentSessionID: String!) {\n GetRemoteBrowserSelection(agentSessionID: $agentSessionID) {\n Text\n }\n }\n`;\n\n/** The narrow projection of the `GetRemoteBrowserSelection` query payload the channel reads. */\ninterface GetRemoteBrowserSelectionResult {\n GetRemoteBrowserSelection: {\n Text: string;\n } | null;\n}\n\n/** The narrow projection of the `StartRemoteBrowserScreencast` mutation payload the channel reads. */\ninterface StartRemoteBrowserScreencastResult {\n StartRemoteBrowserScreencast: {\n Streaming: boolean;\n } | null;\n}\n\n/** GraphQL query the surface fetcher runs — the live screenshot + URL of the server browser. */\nconst REMOTE_BROWSER_SNAPSHOT_QUERY = `\n query RemoteBrowserSnapshot($agentSessionID: String!) {\n RemoteBrowserSnapshot(agentSessionID: $agentSessionID) {\n ScreenshotBase64\n CurrentUrl\n }\n }\n`;\n\n/** The narrow projection of the `RemoteBrowserSnapshot` query payload the channel reads. */\ninterface RemoteBrowserSnapshotResult {\n RemoteBrowserSnapshot: RemoteBrowserSnapshotView | null;\n}\n\n/**\n * VISION-QUERY mutation (separate from {@link EXECUTE_REMOTE_BROWSER_ACTION_MUTATION} — this OBSERVES the page,\n * it does not drive it). The server captures the live screenshot and runs a fast vision model over it, so the\n * non-omnimodal voice agent can \"see\" the page: a text description and/or localized elements with pixel\n * centroids. Backs both `browser_DescribePage` (no target) and `browser_LocateElement` (a target description).\n */\nconst INTERPRET_PAGE_MUTATION = `\n mutation InterpretRemoteBrowserPage($agentSessionID: String!, $query: String) {\n InterpretRemoteBrowserPage(agentSessionID: $agentSessionID, query: $query) {\n Description\n Elements {\n Label\n X\n Y\n Confidence\n }\n Detail\n }\n }\n`;\n\n/** One element localized by the visual interpreter — label + pixel centroid + confidence. */\ninterface RemoteBrowserInterpretedElement {\n Label: string;\n X: number;\n Y: number;\n Confidence: number;\n}\n\n/** The narrow projection of the `InterpretRemoteBrowserPage` mutation payload the channel reads. */\ninterface InterpretRemoteBrowserPageResult {\n InterpretRemoteBrowserPage: {\n Description: string | null;\n Elements: RemoteBrowserInterpretedElement[];\n Detail: string | null;\n } | null;\n}\n\n/** The result payload (serialized to JSON) the channel returns to the model per tool call. */\ninterface RemoteBrowserToolResult {\n success: boolean;\n /** The page URL after the action, when the server reported one. */\n currentUrl?: string;\n /** Human-readable detail (e.g. extracted page text for `browser_GetPageText`, or an error). */\n detail?: string;\n /** Error description when `success` is false. */\n error?: string;\n}\n\n/**\n * The REMOTE BROWSER as a pluggable interactive channel — a {@link BaseRealtimeChannelClient}\n * resolved from the `MJ: AI Agent Channels` registry row whose `ClientPluginClass` is\n * `'RealtimeRemoteBrowserChannel'`. One instance per session (ClassFactory-created at session\n * start).\n *\n * Topology (CLIENT-DIRECT, like the whiteboard — the browser just happens to live on the\n * server):\n * - **Action**: the `browser_*` client-executed tool set ({@link REMOTE_BROWSER_TOOL_DEFINITIONS}).\n * {@link ApplyAgentTool} maps a tool call → a normalized {@link RemoteBrowserAction}\n * ({@link MapToolToAction}) and awaits the `ExecuteRemoteBrowserAction` GraphQL mutation\n * (via {@link RealtimeChannelContext.ExecuteServerAction}) to drive the server browser,\n * returning a concise JSON result for the model. Never throws — argument and transport\n * failures map to a `{ success: false, error }` payload.\n * - **Perception**: after a successful action, a `[browser]` context note carries the new\n * page URL so the agent perceives where the page went; the surface independently polls a\n * live screenshot.\n * - **Surface**: {@link RemoteBrowserSurfaceComponent}, created dynamically in the channel\n * tab; the plugin hands it the session's provider + id in {@link BindSurface} so it can\n * poll `RemoteBrowserSnapshot`. HUMAN TAKEOVER is enabled by default (Collaborative): the\n * user can click/type into the live canvas and those events relay to the server browser via\n * {@link RELAY_HUMAN_INPUT_MUTATION} (server-gated on the backend's `HumanTakeover` capability).\n *\n * The channel keeps NO client-side state of record — the browser's state lives entirely on\n * the server — so {@link SerializeState} / {@link RestoreState} use the base no-op behavior.\n */\n@RegisterClass(BaseRealtimeChannelClient, 'RealtimeRemoteBrowserChannel')\nexport class RemoteBrowserChannel extends BaseRealtimeChannelClient<RemoteBrowserSurfaceComponent> implements IChannelFrameProvider {\n /** The live bound surface, when the channel tab's pane is instantiated. */\n private surface: RemoteBrowserSurfaceComponent | null = null;\n\n /** Shared video bridge streaming browser frames to the model when the model supports inbound video. */\n private videoBridge: ChannelInboundVideoBridge | null = null;\n\n /**\n * Sourced tracks: Remote Browser can source inbound video to the model when the model supports it.\n */\n public override GetSourcedTracks(): readonly RealtimeTrackDescriptor[] {\n return [CHANNEL_INBOUND_VIDEO_TRACK];\n }\n\n /**\n * Produces the latest browser screenshot as a base64-encoded frame for the video bridge.\n * Under active screencast streaming, returns null to avoid redundant server snapshot round-trips\n * and duplicate frame sourcing into the video bridge.\n */\n public async GetLatestFrame(): Promise<string | null> {\n this.ensureVideoBridge();\n if (this.streaming) {\n return null;\n }\n const snapshot = await this.fetchSnapshot();\n return snapshot?.ScreenshotBase64 ?? null;\n }\n\n /** Subscription to the bound surface's `HumanInput` output, torn down on unbind/dispose. */\n private humanInputSub: Subscription | null = null;\n\n /**\n * Whether the server confirmed it is PUSHING screencast frames for this session (`ScreenStreaming`\n * supported). When `true` the surface renders pushed frames on its canvas and skips its poll, and\n * {@link OnScreencastFrame} forwards each pushed frame to it. When `false` the surface keeps polling.\n */\n private streaming = false;\n\n /**\n * Whether the server confirmed it is PUSHING tab-audio chunks for this session (the backend can capture\n * audio). When `true` {@link OnAudioChunk} feeds each pushed chunk to {@link audioPlayer}; when `false`\n * no audio plays.\n */\n private audioStreaming = false;\n\n /**\n * The last URL the agent was TOLD about, so an unchanged page is not re-announced (#3496). `null`\n * until the first sighting, which is deliberately announced as a plain \"current page\" rather than\n * as somebody else navigating — the first page of a session was nobody's takeover.\n */\n private lastNotedUrl: string | null = null;\n\n /**\n * How many browser operations the AGENT itself has in flight right now (#3496).\n *\n * Attribution cannot be read off the feed a change arrives on. A pushed frame or a perception poll\n * only says the page moved; it is `> 0` here that says the agent is the one moving it. Two cases\n * make that necessary rather than tidy:\n *\n * - **an action's frames outrun its reply.** Under streaming, frames of the new page are pushed\n * while `ExecuteRemoteBrowserAction` is still in flight, so the observation reliably lands BEFORE\n * the mutation returns the URL — the agent's own navigation announced back to it as a takeover.\n * - **a goal drives for minutes.** `browser_AchieveGoal` runs an autonomous loop server-side and\n * every page it opens is observed with nothing yet returned, so the whole run would narrate\n * itself as somebody else.\n *\n * A counter, not a flag: goals and actions overlap, and the last one to finish must not clear a\n * window another still owns.\n */\n private agentDrivingDepth = 0;\n\n /** The client-side audio player fed pushed chunks while {@link audioStreaming}; `null` until started. */\n private audioPlayer: RemoteBrowserAudioPlayer | null = null;\n\n /** Subscription to the bound surface's `AudioMutedChange` output (the speaker toggle), torn down on unbind. */\n private audioMutedSub: Subscription | null = null;\n\n /**\n * Poll cadence + deadline for {@link pollGoalResult} (the async `browser_AchieveGoal` result poll).\n * Protected so tests can shrink them; production polls every 2.5s for up to 5 minutes (a goal loop\n * past 5 min keeps running server-side, the model just gets a \"still running\" note).\n */\n protected GoalPollIntervalMs = 2500;\n protected GoalPollTimeoutMs = 5 * 60 * 1000;\n\n public get ChannelName(): string {\n return 'Remote Browser';\n }\n\n public get ToolNamePrefix(): string {\n return REMOTE_BROWSER_TOOL_PREFIX;\n }\n\n public get TabTitle(): string {\n return 'Browser';\n }\n\n public get TabIcon(): string {\n return 'fa-solid fa-globe';\n }\n\n public GetToolDefinitions(): RealtimeToolDefinition[] {\n return REMOTE_BROWSER_TOOL_DEFINITIONS;\n }\n\n public override GetSurfaceComponent(): Type<RemoteBrowserSurfaceComponent> {\n return RemoteBrowserSurfaceComponent;\n }\n\n /** First-run intro shown the first time the user opens the Browser tab (once per user). */\n public override GetOnboardingDetails(): ChannelOnboardingDetails {\n return {\n Heading: 'Remote Browser',\n Description:\n 'The agent drives a live web browser on the server and you can watch it work right here. ' +\n 'You can also grab the wheel: click and type directly on the live page and the agent picks ' +\n 'up right where you left off.',\n Tips: [\n 'Ask the agent to look something up, open a site or fill in a form — it controls the page.',\n 'Click on the live view to take over, then click and type to drive it yourself — the \"You\\'re driving\" badge shows when takeover is active.',\n 'The current page URL is shown so you always know where the agent has navigated.',\n ],\n IconClass: 'fa-solid fa-globe',\n };\n }\n\n /** Starts the video bridge streaming browser frames to the model when inbound video is supported. */\n protected override OnInitialize(): void {\n this.ensureVideoBridge();\n }\n\n public override OnSessionStarted(): void {\n this.ensureVideoBridge();\n }\n\n private ensureVideoBridge(): ChannelInboundVideoBridge | null {\n if (!this.videoBridge && this.Context) {\n this.videoBridge = new ChannelInboundVideoBridge(() => this.Context?.Client, this);\n }\n if (this.videoBridge && !this.videoBridge.IsActive && this.Context?.Client?.IsTrackEstablished('video', 'inbound')) {\n this.videoBridge.Start?.();\n }\n return this.videoBridge;\n }\n\n /**\n * Wires the dynamically-created surface: hands it a snapshot fetcher closing over the\n * channel context (session id + provider live there), so the surface stays transport-\n * agnostic. Set BEFORE the surface's first change detection (the pane binds synchronously),\n * so its `ngOnInit` poll has the fetcher.\n *\n * Also asks the server to start a live screencast. When the backend supports `ScreenStreaming` the\n * server starts PUSHING frames and reports `Streaming: true`; we flip the surface to canvas mode (its\n * poll is then skipped) and {@link OnScreencastFrame} paints each pushed frame. When the backend lacks\n * the capability (`Streaming: false`) the surface keeps the snapshot poll already running — graceful\n * fallback, no further action.\n *\n * HUMAN TAKEOVER is enabled BY DEFAULT (Collaborative): the surface is flipped `Interactive = true` and\n * its `HumanInput` output subscribed, forwarding each pointer/keyboard event to the server via\n * {@link RELAY_HUMAN_INPUT_MUTATION}. Takeover only takes effect on the canvas (screencast) path; the\n * `<img>` poll fallback stays view-only. The server gates the actual routing on the backend's\n * `HumanTakeover` capability, so this is safe to enable unconditionally.\n */\n public BindSurface(instance: RemoteBrowserSurfaceComponent): void {\n this.surface = instance;\n instance.Fetch = () => this.fetchSnapshot();\n // Copy-out: the surface reads the remote selection through this on a local `copy` and writes it locally.\n instance.FetchSelection = () => this.fetchSelection();\n instance.Interactive = true;\n this.ensureVideoBridge();\n this.humanInputSub = instance.HumanInput.subscribe((input) => this.relayHumanInput(input));\n this.audioMutedSub = instance.AudioMutedChange.subscribe((muted) => this.audioPlayer?.SetMuted(muted));\n void this.startScreencast(instance);\n void this.startAudioStream(instance);\n }\n\n public override UnbindSurface(): void {\n this.humanInputSub?.unsubscribe();\n this.humanInputSub = null;\n this.audioMutedSub?.unsubscribe();\n this.audioMutedSub = null;\n void this.stopScreencast();\n void this.stopAudioStream();\n this.surface = null;\n }\n\n public override Dispose(): void {\n this.clearScreencastTrailingTimer();\n this.pendingScreencastFrame = null;\n this.videoBridge?.Stop();\n this.videoBridge = null;\n this.humanInputSub?.unsubscribe();\n this.humanInputSub = null;\n this.audioMutedSub?.unsubscribe();\n this.audioMutedSub = null;\n void this.stopScreencast();\n void this.stopAudioStream();\n super.Dispose();\n }\n\n /** Timestamp of the last screencast frame pushed to the video bridge (paces pushes to negotiated cadence). */\n private lastScreencastPushTime = 0;\n\n /** Base64 content of the last frame pushed to the video bridge (for change-driven deduplication). */\n private lastPushedScreencastFrame: string | null = null;\n\n /** URL associated with the last frame pushed to the video bridge. */\n private lastPushedUrl: string | null = null;\n\n /** Minimum allowable screencast push cadence (250ms = 4 fps debounce limit during active bursts). */\n private static readonly SCREENCAST_MIN_CADENCE_MS = 250;\n\n /** Default screencast push cadence when not otherwise negotiated (1000ms = 1 fps ceiling for Gemini Live). */\n private static readonly SCREENCAST_DEFAULT_CADENCE_MS = 1000;\n\n /** Maximum interval (ms) between visual frame pushes on a static page (heartbeat to maintain track liveness). */\n private static readonly SCREENCAST_HEARTBEAT_MS = 15_000;\n\n /** Trailing timer to deliver the settled resting frame after a burst of rapid user activity. */\n private screencastTrailingTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** Pending frame buffered during an active cooldown interval, waiting to settle. */\n private pendingScreencastFrame: { dataBase64: string; currentUrl?: string | null } | null = null;\n\n /** Cancels any active trailing-edge settle timer and clears the handle. */\n private clearScreencastTrailingTimer(): void {\n if (this.screencastTrailingTimer != null) {\n clearTimeout(this.screencastTrailingTimer);\n this.screencastTrailingTimer = null;\n }\n }\n\n /**\n * Pushes a frame to the video bridge, recording timestamp, frame content, and URL.\n */\n private pushScreencastFrame(dataBase64: string, currentUrl?: string | null): void {\n this.lastScreencastPushTime = Date.now();\n this.lastPushedScreencastFrame = dataBase64;\n this.lastPushedUrl = currentUrl ?? null;\n this.ensureVideoBridge()?.PushFrame(dataBase64);\n }\n\n /**\n * Resolves the effective push cadence in milliseconds based on the negotiated inbound video track.\n * Defaults to 1000ms (1 fps ceiling for Gemini Live), but clamps down to a minimum of 250ms (4 fps)\n * if the negotiated track specifies a higher `Rate`.\n */\n private getNegotiatedVideoCadenceMs(): number {\n const client = this.Context?.Client;\n if (!client) {\n return RemoteBrowserChannel.SCREENCAST_DEFAULT_CADENCE_MS;\n }\n const tracks: readonly RealtimeTrack[] = client.EstablishedTracks;\n const videoTrack = tracks.find(\n (t: RealtimeTrack) =>\n t.Descriptor.Modality === 'video' &&\n t.Descriptor.Direction === 'inbound'\n );\n const rate = videoTrack?.Descriptor.Rate;\n if (typeof rate === 'number' && rate > 0) {\n return Math.max(\n RemoteBrowserChannel.SCREENCAST_MIN_CADENCE_MS,\n Math.floor(1000 / rate)\n );\n }\n return RemoteBrowserChannel.SCREENCAST_DEFAULT_CADENCE_MS;\n }\n\n /**\n * Forwards one PUSHED screencast frame to the bound surface's canvas. Called by the session service\n * when a `RemoteBrowserScreencastFrame` arrives on the push-status stream for THIS session. No-op when\n * the channel isn't streaming or has no bound surface (e.g. the tab pane is collapsed).\n *\n * Under streaming the surface's snapshot poll is stopped, so this is the ONLY thing that sees the\n * page while frames are being pushed — which is why the frame now carries the URL (#3496). Without\n * it, a user navigating during a screencast changed the picture and nothing else: the agent kept\n * describing the page it last opened, and the pixels proving otherwise were right there on screen.\n *\n * Pushes to the video bridge are CHANGE-DRIVEN with a TRAILING-EDGE SETTLE DEBOUNCE:\n * 1. The human user surface renders immediately at full 60fps responsiveness.\n * 2. When the page is static (same image and URL), pushes are deduplicated to avoid burning\n * ~15k tokens/min of identical visual context.\n * 3. A frame is pushed immediately on the leading edge when visual content or URL changes after\n * the negotiated cadence (default 1000ms, clamped to >= 250ms).\n * 4. During rapid bursts (scrolling, typing, clicking) within the cooldown window, subsequent frames\n * are buffered; when the cooldown expires, a trailing timer delivers the settled resting frame\n * so the model never misses the final user state.\n * 5. A 15-second heartbeat ensures visual track liveness even during extended idle periods.\n *\n * @param dataBase64 The frame image as raw base64 JPEG (no `data:` prefix).\n * @param currentUrl The browser's URL when the frame was captured; absent from older servers.\n */\n public OnScreencastFrame(dataBase64: string, currentUrl?: string | null): void {\n if (!this.streaming) {\n return;\n }\n\n this.surface?.RenderFrame(dataBase64);\n\n const now = Date.now();\n const cadenceMs = this.getNegotiatedVideoCadenceMs();\n const elapsed = now - this.lastScreencastPushTime;\n\n if (elapsed >= cadenceMs) {\n // Cooldown has expired: leading edge push.\n this.clearScreencastTrailingTimer();\n this.pendingScreencastFrame = null;\n\n const frameChanged = dataBase64 !== this.lastPushedScreencastFrame;\n const urlChanged = currentUrl != null && currentUrl !== this.lastPushedUrl;\n const heartbeatElapsed = elapsed >= RemoteBrowserChannel.SCREENCAST_HEARTBEAT_MS;\n\n if (frameChanged || urlChanged || heartbeatElapsed) {\n this.pushScreencastFrame(dataBase64, currentUrl);\n }\n } else {\n // Within cooldown window: buffer latest frame and schedule trailing settle timer.\n this.pendingScreencastFrame = { dataBase64, currentUrl };\n if (!this.screencastTrailingTimer) {\n const delay = Math.max(0, cadenceMs - elapsed);\n this.screencastTrailingTimer = setTimeout(() => {\n this.screencastTrailingTimer = null;\n if (!this.streaming || !this.pendingScreencastFrame) {\n return;\n }\n const { dataBase64: pendingData, currentUrl: pendingUrl } = this.pendingScreencastFrame;\n this.pendingScreencastFrame = null;\n\n const frameChanged = pendingData !== this.lastPushedScreencastFrame;\n const urlChanged = pendingUrl != null && pendingUrl !== this.lastPushedUrl;\n const heartbeat = (Date.now() - this.lastScreencastPushTime) >= RemoteBrowserChannel.SCREENCAST_HEARTBEAT_MS;\n\n if (frameChanged || urlChanged || heartbeat) {\n this.pushScreencastFrame(pendingData, pendingUrl);\n }\n }, delay);\n }\n }\n\n // #3496: URL perception note runs AFTER the push so the visual image lands before/with the note.\n this.notePageChange(currentUrl, 'observed');\n }\n\n /**\n * Feeds one PUSHED tab-audio chunk to the client-side audio player. Called by the session service when a\n * `RemoteBrowserAudioChunk` arrives on the push-status stream for THIS session. No-op when the channel\n * isn't audio-streaming or the player has been torn down.\n *\n * @param chunk The pushed audio chunk (base64 webm-opus + codec/seq metadata).\n */\n public OnAudioChunk(chunk: RemoteBrowserAudioChunkInput): void {\n if (this.audioStreaming) {\n this.audioPlayer?.Enqueue(chunk);\n }\n }\n\n /**\n * Asks the server to start the live screencast and, on success, flips the surface to canvas mode so it\n * stops polling and starts painting pushed frames. Best-effort: a `null`/`Streaming: false` result\n * (capability absent or transport failure) leaves the surface on its poll fallback.\n */\n private async startScreencast(instance: RemoteBrowserSurfaceComponent): Promise<void> {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return;\n }\n const data = await this.Context?.ExecuteServerAction<StartRemoteBrowserScreencastResult>(START_SCREENCAST_MUTATION, { agentSessionID: sessionId });\n if (data?.StartRemoteBrowserScreencast?.Streaming === true) {\n this.streaming = true;\n instance.Streaming = true;\n }\n }\n\n /**\n * Asks the server to start the live tab-audio stream and, on success, spins up the client-side\n * {@link RemoteBrowserAudioPlayer} (speaker ON by default — the call is the activating user gesture) so\n * pushed chunks play. Best-effort: a `null`/`Streaming: false` result (no audio capability or transport\n * failure) leaves the channel silent with no player.\n *\n * @param instance The bound surface, flipped to show the speaker toggle when audio starts.\n */\n private async startAudioStream(instance: RemoteBrowserSurfaceComponent): Promise<void> {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return;\n }\n const data = await this.Context?.ExecuteServerAction<StartRemoteBrowserAudioStreamResult>(START_AUDIO_STREAM_MUTATION, { agentSessionID: sessionId });\n if (data?.StartRemoteBrowserAudioStream?.Streaming === true) {\n this.audioStreaming = true;\n this.audioPlayer = new RemoteBrowserAudioPlayer(new MediaSourceAudioSink());\n // Speaker defaults ON; reflect that on the surface so the toggle renders un-muted.\n instance.AudioAvailable = true;\n instance.AudioMuted = false;\n }\n }\n\n /** Asks the server to stop the tab-audio stream (best-effort), disposes the player, and clears state. */\n private async stopAudioStream(): Promise<void> {\n const wasStreaming = this.audioStreaming;\n this.audioStreaming = false;\n this.audioPlayer?.Dispose();\n this.audioPlayer = null;\n const sessionId = this.Context?.AgentSessionID;\n if (!wasStreaming || !sessionId) {\n return;\n }\n await this.Context?.ExecuteServerAction(STOP_AUDIO_STREAM_MUTATION, { agentSessionID: sessionId });\n }\n\n /**\n * Relays ONE human-takeover input from the surface to the server's {@link RELAY_HUMAN_INPUT_MUTATION}.\n * Best-effort and fire-and-forget: never throws, and the boolean result is ignored (the server already\n * gates routing on the backend's `HumanTakeover` capability). No-op when no live session id exists.\n *\n * @param input The flattened pointer/keyboard input the user performed on the live canvas.\n */\n private relayHumanInput(input: RemoteBrowserHumanInputEvent): void {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return;\n }\n void this.Context?.ExecuteServerAction(RELAY_HUMAN_INPUT_MUTATION, {\n agentSessionID: sessionId,\n kind: input.kind,\n x: input.x ?? null,\n y: input.y ?? null,\n button: input.button ?? null,\n key: input.key ?? null,\n // Paste-in: the `'text'` kind carries the local clipboard text the server inserts into the page.\n text: input.text ?? null,\n deltaX: input.deltaX ?? null,\n deltaY: input.deltaY ?? null,\n // Server expects a comma-separated modifier list (e.g. \"Shift,Control\"); null when none held.\n modifiers: input.modifiers && input.modifiers.length > 0 ? input.modifiers.join(',') : null,\n });\n }\n\n /**\n * Reads the remote page's current selection for COPY-OUT — backs the surface's `FetchSelection`. Awaits\n * the `GetRemoteBrowserSelection` query and returns its text. Best-effort by contract\n * ({@link RemoteBrowserSelectionFetcher}): returns `''` when no session is live or the query fails, so a\n * best-effort copy never throws.\n */\n private async fetchSelection(): Promise<string> {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return '';\n }\n const data = await this.Context?.ExecuteServerAction<GetRemoteBrowserSelectionResult>(GET_SELECTION_QUERY, { agentSessionID: sessionId });\n return data?.GetRemoteBrowserSelection?.Text ?? '';\n }\n\n /** Asks the server to stop the screencast (best-effort) and clears the streaming flag. */\n private async stopScreencast(): Promise<void> {\n const wasStreaming = this.streaming;\n this.streaming = false;\n this.clearScreencastTrailingTimer();\n this.pendingScreencastFrame = null;\n this.lastPushedScreencastFrame = null;\n this.lastPushedUrl = null;\n this.lastScreencastPushTime = 0;\n const sessionId = this.Context?.AgentSessionID;\n if (!wasStreaming || !sessionId) {\n return;\n }\n await this.Context?.ExecuteServerAction(STOP_SCREENCAST_MUTATION, { agentSessionID: sessionId });\n }\n\n /**\n * Fetches one live snapshot of the server browser for the surface — the PERCEPTION poll.\n * Best-effort by contract ({@link RemoteBrowserSnapshotFetcher}): returns `null` when no\n * session is live or the query fails (the surface keeps its last good frame).\n *\n * Also the channel's chance to NOTICE a page change it did not cause (#3496): the poll already\n * carries the URL, and until now only the surface read it.\n */\n private async fetchSnapshot(): Promise<RemoteBrowserSnapshotView | null> {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return null;\n }\n const data = await this.Context?.ExecuteServerAction<RemoteBrowserSnapshotResult>(REMOTE_BROWSER_SNAPSHOT_QUERY, { agentSessionID: sessionId });\n const snapshot = data?.RemoteBrowserSnapshot ?? null;\n this.notePageChange(snapshot?.CurrentUrl ?? null, 'observed');\n return snapshot;\n }\n\n /**\n * The ONE place the agent is told where the page is (#3496).\n *\n * Before this, the `[browser] current page:` note was pushed from exactly two call sites, both\n * immediately after a server action the MODEL itself initiated. That placement — not any caching —\n * was the bug: **a surface change the agent did not cause was invisible to it.** A user taking over\n * and navigating produced no note, so the agent went on confidently describing the previous page,\n * and only corrected when told to look again. Human takeover is on by default for `Collaborative`\n * providers, so the default configuration was the broken one.\n *\n * Routing every observation through here makes the rule \"the agent hears about the page whenever it\n * MOVES, whoever moved it\" — a property of this method rather than of where callers happen to be.\n *\n * `cause` is what the agent could never work out for itself. An `'agent'` change is one this\n * channel just performed; anything spotted by the perception poll or a pushed frame is\n * `'observed'` — someone else is driving, and saying so is the difference between the agent\n * knowing the page moved and knowing it is no longer the one moving it.\n *\n * Silent when the URL has not changed, which is not an optimisation but a requirement: the\n * perception poll runs every ~700ms and an unconditional note would bury the conversation in\n * hundreds of identical lines.\n */\n private notePageChange(url: string | null | undefined, cause: 'agent' | 'observed'): void {\n const next = (url ?? '').trim();\n if (next.length === 0) {\n return;\n }\n // The live-view bar is refreshed even when the URL is unchanged: it is idempotent, and in\n // streaming mode it is the only thing keeping the bar off \"No page loaded yet\".\n this.surface?.SetCurrentUrl(next);\n if (next === this.lastNotedUrl) {\n return;\n }\n const firstSighting = this.lastNotedUrl === null;\n this.lastNotedUrl = next;\n // A change seen WHILE the agent is driving is the agent's own, whichever feed spotted it first.\n const byAgent = cause === 'agent' || this.agentDrivingDepth > 0;\n this.Context?.SendContextNote(\n byAgent || firstSighting\n ? `[browser] current page: ${next}`\n : `[browser] the page changed to: ${next} — you did not navigate here, so someone else is driving. `\n + `Describe THIS page, not the one you last opened.`,\n );\n }\n\n /**\n * Runs one agent-initiated browser operation with {@link agentDrivingDepth} raised, so any page\n * change observed while it runs is attributed to the agent rather than to a phantom third party.\n *\n * `finally` and not a trailing decrement: the window must close on a thrown transport error too,\n * or one failed tool call would leave the channel permanently unable to report a real takeover.\n */\n private async whileAgentDrives<T>(work: () => Promise<T>): Promise<T> {\n this.agentDrivingDepth++;\n try {\n return await work();\n } finally {\n this.agentDrivingDepth--;\n }\n }\n\n /**\n * Executes ONE `browser_*` tool call: maps it to a server action and awaits the\n * `ExecuteRemoteBrowserAction` mutation, then returns a concise JSON result the model reads.\n * On success it also feeds the new page URL to the agent as a `[browser]` context note. Never\n * throws — bad arguments, no live session, and server failures all become a failed-result\n * payload so the model can narrate the problem.\n */\n public async ApplyAgentTool(toolName: string, argsJson: string): Promise<string> {\n // VISION-QUERY branch: the two interpreter tools OBSERVE the page (run a vision model over a screenshot)\n // rather than drive it, so they route to the separate interpret mutation, not MapToolToAction.\n if (toolName === REMOTE_BROWSER_TOOL_NAMES.DescribePage) {\n return this.interpretPage(undefined);\n }\n if (toolName === REMOTE_BROWSER_TOOL_NAMES.AchieveGoal) {\n return this.achieveGoal(argsJson);\n }\n if (toolName === REMOTE_BROWSER_TOOL_NAMES.LocateElement) {\n const description = this.asArgString(this.parseArgs(argsJson)['description']);\n if (!description) {\n return this.fail('browser_LocateElement requires a \"description\" of the element to find.');\n }\n return this.interpretPage(description);\n }\n\n let action: RemoteBrowserAction;\n try {\n action = MapToolToAction(toolName, this.parseArgs(argsJson));\n } catch (error) {\n const message = error instanceof RemoteBrowserToolArgError ? error.message : 'Invalid Remote Browser tool arguments.';\n return this.fail(message);\n }\n\n // Briefly WAIT for the session id rather than failing instantly: the model can fire a tool the\n // first beat it connects, before mintSession binds the id (the \"session id missing\" race).\n const sessionId = await this.ResolveAgentSessionId();\n if (!sessionId) {\n // Diagnostic: distinguishes \"channel never Initialized (no Context)\" from \"session id never\n // resolved within the wait window\" — the two distinct causes of a null id at tool-execution time.\n console.warn('[RemoteBrowser] browser tool invoked but AgentSessionID is null', {\n tool: toolName,\n hasContext: !!this.Context,\n agentSessionID: this.Context?.AgentSessionID ?? null,\n });\n return this.fail('No live browser session is available yet — the realtime session may still be connecting; try again in a moment.');\n }\n\n return this.whileAgentDrives(async () => {\n const data = await this.Context?.ExecuteServerAction<ExecuteRemoteBrowserActionResult>(\n EXECUTE_REMOTE_BROWSER_ACTION_MUTATION,\n this.buildVariables(sessionId, action),\n );\n const result = data?.ExecuteRemoteBrowserAction ?? null;\n if (!result) {\n return this.fail('The browser action could not be executed (no response from the server).');\n }\n if (!result.Success) {\n return this.fail(result.Detail ?? 'The browser action failed.');\n }\n\n // PERCEPTION: tell the agent where the page is now (background — no spoken reply), and refresh\n // the live-view bar, which in streaming mode has no poll feeding it.\n this.notePageChange(result.CurrentUrl, 'agent');\n return this.ok(result.CurrentUrl, result.Detail);\n });\n }\n\n /** Builds the flat mutation variables from a normalized action (omitted fields ride as null). */\n private buildVariables(sessionId: string, action: RemoteBrowserAction): Record<string, JSONValue> {\n return {\n agentSessionID: sessionId,\n kind: action.Kind,\n url: action.Url ?? null,\n selector: action.Selector ?? null,\n x: action.X ?? null,\n y: action.Y ?? null,\n text: action.Text ?? null,\n key: action.Key ?? null,\n deltaX: action.DeltaX ?? null,\n deltaY: action.DeltaY ?? null,\n ms: action.Ms ?? null,\n };\n }\n\n /**\n * GOAL path for the `browser_AchieveGoal` tool: hands a high-level goal to the server's autonomous\n * browser agent (computer-use loop / native AI) via {@link EXECUTE_REMOTE_BROWSER_GOAL_MUTATION} and\n * returns a concise result for the model. Never throws — failures map to a `{ success: false }` string.\n *\n * @param argsJson The raw tool-call arguments (`{ goal, startUrl? }`).\n * @returns The model-readable result string.\n */\n private async achieveGoal(argsJson: string): Promise<string> {\n // Wait briefly for the session id (mint/reconnect race) before giving up — see ResolveAgentSessionId.\n const sessionId = await this.ResolveAgentSessionId();\n if (!sessionId) {\n return this.fail('No live browser session is available yet — the realtime session may still be connecting; try again in a moment.');\n }\n const args = this.parseArgs(argsJson);\n const goal = this.asArgString(args['goal']);\n if (!goal) {\n return this.fail('browser_AchieveGoal requires a \"goal\" describing what to accomplish.');\n }\n const startUrl = this.asArgString(args['startUrl']) ?? null;\n\n // START the goal (returns immediately with a GoalRunID) then POLL — a goal loop can run for\n // minutes, far longer than any single request survives, so we never hold one open for the loop.\n // The whole span is agent-driven: every page the loop opens is observed with nothing returned\n // yet, so without the window the agent would narrate its own goal as somebody else's takeover.\n return this.whileAgentDrives(async () => {\n const startData = await this.Context?.ExecuteServerAction<ExecuteRemoteBrowserGoalResult>(EXECUTE_REMOTE_BROWSER_GOAL_MUTATION, {\n agentSessionID: sessionId,\n goal,\n startUrl,\n });\n const started = startData?.ExecuteRemoteBrowserGoal ?? null;\n if (!started) {\n return this.fail('The browser goal could not be started (no response from the server).');\n }\n // Defensive: if the server ever returns a terminal status synchronously, use it directly.\n const result = started.Status === 'Running' && started.GoalRunID\n ? await this.pollGoalResult(sessionId, started.GoalRunID)\n : started;\n if (!result) {\n return this.fail('The browser goal is taking longer than expected and could not be confirmed. It may still be running.');\n }\n this.notePageChange(result.CurrentUrl, 'agent');\n if (!result.Success) {\n return this.fail(result.Detail ?? `The goal could not be completed (${result.Status ?? 'unknown'}).`);\n }\n return this.ok(result.CurrentUrl, result.Detail ?? `Goal completed (${result.Status ?? 'Completed'}, ${result.StepCount ?? 0} steps).`);\n });\n }\n\n /**\n * Polls {@link GET_REMOTE_BROWSER_GOAL_RESULT_QUERY} for a started goal run until it reports a\n * terminal status (anything other than `Running`), then returns that terminal payload. Each poll is\n * a short request, so no transport boundary times out while a long goal loop runs server-side.\n *\n * Returns `null` if the goal is still `Running` after {@link GoalPollTimeoutMs} (the loop keeps\n * running server-side; the caller maps this to a \"still running\" message), the session went away, or\n * the run id is no longer known (`Unknown` — expired/superseded).\n */\n private async pollGoalResult(sessionId: string, goalRunID: string): Promise<RemoteBrowserGoalPayload | null> {\n const interval = Math.max(1, this.GoalPollIntervalMs);\n for (let waited = 0; waited < this.GoalPollTimeoutMs; waited += interval) {\n await new Promise<void>((resolve) => setTimeout(resolve, interval));\n // The session ended mid-goal — stop polling (the live browser is gone).\n if (!this.Context?.AgentSessionID) {\n return null;\n }\n const data = await this.Context.ExecuteServerAction<GetRemoteBrowserGoalResultPayload>(GET_REMOTE_BROWSER_GOAL_RESULT_QUERY, {\n agentSessionID: sessionId,\n goalRunID,\n });\n const payload = data?.GetRemoteBrowserGoalResult ?? null;\n // A transient null (one failed poll) shouldn't abort — keep polling until the deadline.\n if (payload && payload.Status !== 'Running') {\n return payload;\n }\n }\n return null;\n }\n\n /**\n * Runs the VISION-QUERY path for the `browser_DescribePage` / `browser_LocateElement` tools: awaits the\n * `InterpretRemoteBrowserPage` mutation (server captures a screenshot + runs a fast vision model) and returns\n * a concise JSON string the model reads. For a describe (no `query`) it returns `{ description }`; for a locate\n * it returns `{ found, element, all }` so the agent can `browser_Click` the centroid. Never throws — a null\n * session, no response, or a server detail-only result all map to a result string the model can narrate.\n *\n * @param query The visual target to locate, or `undefined` for a plain page description.\n * @returns A JSON string describing the interpretation for the model.\n */\n private async interpretPage(query: string | undefined): Promise<string> {\n // Wait briefly for the session id (mint/reconnect race) before giving up — see ResolveAgentSessionId.\n const sessionId = await this.ResolveAgentSessionId();\n if (!sessionId) {\n console.warn('[RemoteBrowser] interpret tool invoked but AgentSessionID is null', {\n hasContext: !!this.Context,\n agentSessionID: this.Context?.AgentSessionID ?? null,\n });\n return this.fail('No live browser session is available yet — the realtime session may still be connecting; try again in a moment.');\n }\n\n const data = await this.Context?.ExecuteServerAction<InterpretRemoteBrowserPageResult>(INTERPRET_PAGE_MUTATION, {\n agentSessionID: sessionId,\n query: query ?? null,\n });\n const result = data?.InterpretRemoteBrowserPage ?? null;\n if (!result) {\n return this.fail('The page could not be interpreted (no response from the server).');\n }\n\n if (query === undefined) {\n // DescribePage — hand back the description (or the server's detail note when nothing was interpreted).\n return JSON.stringify({ description: result.Description ?? result.Detail ?? null });\n }\n\n // LocateElement — surface whether anything matched, the best match, and all candidates.\n const elements = result.Elements ?? [];\n const best = elements[0];\n return JSON.stringify({\n found: elements.length > 0,\n element: best ? { label: best.Label, x: best.X, y: best.Y } : null,\n all: elements,\n ...(result.Detail ? { detail: result.Detail } : {}),\n });\n }\n\n /** Coerces a parsed tool-arg to a non-empty string, or `undefined` when absent / wrong-typed. */\n private asArgString(value: JSONValue | undefined): string | undefined {\n return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;\n }\n\n /** Parses the tool-args JSON into a plain object; returns `{}` for empty/malformed input. */\n private parseArgs(argsJson: string): Record<string, JSONValue> {\n const trimmed = argsJson?.trim();\n if (!trimmed) {\n return {};\n }\n try {\n const parsed: unknown = JSON.parse(trimmed);\n return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Record<string, JSONValue>) : {};\n } catch {\n return {};\n }\n }\n\n /** Builds a successful tool-result JSON string for the model. */\n private ok(currentUrl: string | null, detail: string | null): string {\n const result: RemoteBrowserToolResult = { success: true };\n if (currentUrl) {\n result.currentUrl = currentUrl;\n }\n if (detail) {\n result.detail = detail;\n }\n return JSON.stringify(result);\n }\n\n /** Builds a failed tool-result JSON string for the model. */\n private fail(error: string): string {\n const result: RemoteBrowserToolResult = { success: false, error };\n return JSON.stringify(result);\n }\n}\n\n/**\n * Tree-shaking prevention: the Remote Browser channel is resolved dynamically through the\n * ClassFactory (by the registry row's `ClientPluginClass` key), so this static call is what\n * keeps its `@RegisterClass` side effect from being eliminated by the bundler. Called from\n * `conversations.module.ts` alongside {@link LoadRealtimeWhiteboardChannel}.\n */\nexport function LoadRealtimeRemoteBrowserChannel(): void {\n // intentional no-op — the import side effect performs the registration\n}\n"]}
|
|
1
|
+
{"version":3,"file":"remote-browser-channel.js","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/remote-browser/remote-browser-channel.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Type } from '@angular/core';\nimport type { Subscription } from 'rxjs';\nimport { RegisterClass } from '@memberjunction/global';\nimport { CHANNEL_INBOUND_VIDEO_TRACK, JSONValue, RealtimeToolDefinition, RealtimeTrack, RealtimeTrackDescriptor } from '@memberjunction/ai';\nimport { ChannelInboundVideoBridge, IChannelFrameProvider } from '@memberjunction/ai-realtime-client';\nimport { BaseRealtimeChannelClient, ChannelOnboardingDetails } from '@memberjunction/realtime-runtime';\nimport { RemoteBrowserHumanInputEvent, RemoteBrowserSnapshotView, RemoteBrowserSurfaceComponent } from './remote-browser-surface.component';\nimport {\n MapToolToAction,\n REMOTE_BROWSER_TOOL_DEFINITIONS,\n REMOTE_BROWSER_TOOL_NAMES,\n REMOTE_BROWSER_TOOL_PREFIX,\n RemoteBrowserAction,\n RemoteBrowserToolArgError,\n} from './remote-browser-tools';\nimport { MediaSourceAudioSink, RemoteBrowserAudioChunkInput, RemoteBrowserAudioPlayer } from './remote-browser-audio-player';\n\n/**\n * GraphQL mutation that drives the SERVER-hosted browser. The channel awaits it for every\n * `browser_*` tool call and feeds the result back to the model.\n */\nconst EXECUTE_REMOTE_BROWSER_ACTION_MUTATION = `\n mutation ExecuteRemoteBrowserAction(\n $agentSessionID: String!, $kind: String!, $url: String, $selector: String,\n $x: Float, $y: Float, $text: String, $key: String,\n $deltaX: Float, $deltaY: Float, $ms: Float\n ) {\n ExecuteRemoteBrowserAction(\n agentSessionID: $agentSessionID, kind: $kind, url: $url, selector: $selector,\n x: $x, y: $y, text: $text, key: $key,\n deltaX: $deltaX, deltaY: $deltaY, ms: $ms\n ) {\n Success\n CurrentUrl\n Detail\n }\n }\n`;\n\n/** The narrow projection of the `ExecuteRemoteBrowserAction` mutation payload the channel reads. */\ninterface ExecuteRemoteBrowserActionResult {\n ExecuteRemoteBrowserAction: {\n Success: boolean;\n CurrentUrl: string | null;\n Detail: string | null;\n } | null;\n}\n\n/**\n * GOAL mutation — hands a high-level goal to the server's autonomous browser agent (computer-use loop or\n * backend native AI) instead of relaying a granular action. The server plans + executes; we await the\n * terminal outcome. (Live per-step narration is a server-bridged concern; over this client-direct mutation\n * we get the final result.)\n */\nconst EXECUTE_REMOTE_BROWSER_GOAL_MUTATION = `\n mutation ExecuteRemoteBrowserGoal($agentSessionID: String!, $goal: String!, $startUrl: String) {\n ExecuteRemoteBrowserGoal(agentSessionID: $agentSessionID, goal: $goal, startUrl: $startUrl) {\n Success\n GoalRunID\n Strategy\n CurrentUrl\n Status\n StepCount\n Detail\n }\n }\n`;\n\n/**\n * POLL query for the async goal run. `ExecuteRemoteBrowserGoal` STARTS the (potentially multi-minute)\n * goal loop and returns a `GoalRunID` with `Status: 'Running'`; the channel polls this until the run\n * reports a terminal status — so no single request is held open for the whole loop (which would die at\n * a browser/proxy/janitor transport timeout and lose the result).\n */\nconst GET_REMOTE_BROWSER_GOAL_RESULT_QUERY = `\n query GetRemoteBrowserGoalResult($agentSessionID: String!, $goalRunID: String!) {\n GetRemoteBrowserGoalResult(agentSessionID: $agentSessionID, goalRunID: $goalRunID) {\n Success\n GoalRunID\n Strategy\n CurrentUrl\n Status\n StepCount\n Detail\n }\n }\n`;\n\n/** The narrow projection of a goal-result payload (shared by the start mutation + the poll query). */\ninterface RemoteBrowserGoalPayload {\n Success: boolean;\n GoalRunID: string | null;\n Strategy: string | null;\n CurrentUrl: string | null;\n Status: string | null;\n StepCount: number | null;\n Detail: string | null;\n}\n\n/** The narrow projection of the `ExecuteRemoteBrowserGoal` (start) mutation payload the channel reads. */\ninterface ExecuteRemoteBrowserGoalResult {\n ExecuteRemoteBrowserGoal: RemoteBrowserGoalPayload | null;\n}\n\n/** The narrow projection of the `GetRemoteBrowserGoalResult` (poll) query payload the channel reads. */\ninterface GetRemoteBrowserGoalResultPayload {\n GetRemoteBrowserGoalResult: RemoteBrowserGoalPayload | null;\n}\n\n/**\n * GraphQL mutation that asks the server to start PUSHING live CDP screencast frames for the session.\n * Returns `Streaming: true` when the backend supports `ScreenStreaming` and the stream started; the\n * channel then drives the surface's canvas. `Streaming: false` (capability absent / start failed) leaves\n * the surface on its 700ms snapshot poll fallback.\n */\nconst START_SCREENCAST_MUTATION = `\n mutation StartRemoteBrowserScreencast($agentSessionID: String!) {\n StartRemoteBrowserScreencast(agentSessionID: $agentSessionID) {\n Streaming\n }\n }\n`;\n\n/** GraphQL mutation that stops the server-pushed screencast (best-effort teardown). */\nconst STOP_SCREENCAST_MUTATION = `\n mutation StopRemoteBrowserScreencast($agentSessionID: String!) {\n StopRemoteBrowserScreencast(agentSessionID: $agentSessionID)\n }\n`;\n\n/**\n * GraphQL mutation that asks the server to start PUSHING live tab-audio chunks for the session — so a\n * co-agent demoing a video/audio site is HEARD. Returns `Streaming: true` when the backend can capture\n * audio (v1 gates by backend implementation) and the stream started; `Streaming: false` means no audio\n * (capability absent / start failed) and the client simply plays nothing.\n */\nconst START_AUDIO_STREAM_MUTATION = `\n mutation StartRemoteBrowserAudioStream($agentSessionID: String!) {\n StartRemoteBrowserAudioStream(agentSessionID: $agentSessionID) {\n Streaming\n }\n }\n`;\n\n/** GraphQL mutation that stops the server-pushed tab-audio stream (best-effort teardown). */\nconst STOP_AUDIO_STREAM_MUTATION = `\n mutation StopRemoteBrowserAudioStream($agentSessionID: String!) {\n StopRemoteBrowserAudioStream(agentSessionID: $agentSessionID)\n }\n`;\n\n/** The narrow projection of the `StartRemoteBrowserAudioStream` mutation payload the channel reads. */\ninterface StartRemoteBrowserAudioStreamResult {\n StartRemoteBrowserAudioStream: {\n Streaming: boolean;\n } | null;\n}\n\n/**\n * HUMAN-TAKEOVER mutation — relays ONE pointer/keyboard input the user performed on the live canvas into the\n * server-hosted browser (Collaborative control). Best-effort: the server returns `false` when the backend\n * lacks `HumanTakeover` or no live browser exists; the channel doesn't act on the boolean.\n */\nconst RELAY_HUMAN_INPUT_MUTATION = `\n mutation RelayRemoteBrowserHumanInput(\n $agentSessionID: String!, $kind: String!, $x: Float, $y: Float, $button: String, $key: String,\n $text: String, $deltaX: Float, $deltaY: Float, $modifiers: String\n ) {\n RelayRemoteBrowserHumanInput(\n agentSessionID: $agentSessionID, kind: $kind, x: $x, y: $y, button: $button, key: $key,\n text: $text, deltaX: $deltaX, deltaY: $deltaY, modifiers: $modifiers\n )\n }\n`;\n\n/**\n * COPY-OUT query — reads the remote page's current text selection so the surface can write it to the LOCAL\n * clipboard on a `copy` / Cmd+C (the mirror of the `'text'` paste-in path). Returns `''` when nothing is\n * selected, no live browser exists, or the backend lacks `HumanTakeover`.\n */\nconst GET_SELECTION_QUERY = `\n query GetRemoteBrowserSelection($agentSessionID: String!) {\n GetRemoteBrowserSelection(agentSessionID: $agentSessionID) {\n Text\n }\n }\n`;\n\n/** The narrow projection of the `GetRemoteBrowserSelection` query payload the channel reads. */\ninterface GetRemoteBrowserSelectionResult {\n GetRemoteBrowserSelection: {\n Text: string;\n } | null;\n}\n\n/** The narrow projection of the `StartRemoteBrowserScreencast` mutation payload the channel reads. */\ninterface StartRemoteBrowserScreencastResult {\n StartRemoteBrowserScreencast: {\n Streaming: boolean;\n } | null;\n}\n\n/** GraphQL query the surface fetcher runs — the live screenshot + URL of the server browser. */\nconst REMOTE_BROWSER_SNAPSHOT_QUERY = `\n query RemoteBrowserSnapshot($agentSessionID: String!) {\n RemoteBrowserSnapshot(agentSessionID: $agentSessionID) {\n ScreenshotBase64\n CurrentUrl\n }\n }\n`;\n\n/** The narrow projection of the `RemoteBrowserSnapshot` query payload the channel reads. */\ninterface RemoteBrowserSnapshotResult {\n RemoteBrowserSnapshot: RemoteBrowserSnapshotView | null;\n}\n\n/**\n * VISION-QUERY mutation (separate from {@link EXECUTE_REMOTE_BROWSER_ACTION_MUTATION} — this OBSERVES the page,\n * it does not drive it). The server captures the live screenshot and runs a fast vision model over it, so the\n * non-omnimodal voice agent can \"see\" the page: a text description and/or localized elements with pixel\n * centroids. Backs both `browser_DescribePage` (no target) and `browser_LocateElement` (a target description).\n */\nconst INTERPRET_PAGE_MUTATION = `\n mutation InterpretRemoteBrowserPage($agentSessionID: String!, $query: String) {\n InterpretRemoteBrowserPage(agentSessionID: $agentSessionID, query: $query) {\n Description\n Elements {\n Label\n X\n Y\n Confidence\n }\n Detail\n }\n }\n`;\n\n/** One element localized by the visual interpreter — label + pixel centroid + confidence. */\ninterface RemoteBrowserInterpretedElement {\n Label: string;\n X: number;\n Y: number;\n Confidence: number;\n}\n\n/** The narrow projection of the `InterpretRemoteBrowserPage` mutation payload the channel reads. */\ninterface InterpretRemoteBrowserPageResult {\n InterpretRemoteBrowserPage: {\n Description: string | null;\n Elements: RemoteBrowserInterpretedElement[];\n Detail: string | null;\n } | null;\n}\n\n/** The result payload (serialized to JSON) the channel returns to the model per tool call. */\ninterface RemoteBrowserToolResult {\n success: boolean;\n /** The page URL after the action, when the server reported one. */\n currentUrl?: string;\n /** Human-readable detail (e.g. extracted page text for `browser_GetPageText`, or an error). */\n detail?: string;\n /** Error description when `success` is false. */\n error?: string;\n}\n\n/**\n * The REMOTE BROWSER as a pluggable interactive channel — a {@link BaseRealtimeChannelClient}\n * resolved from the `MJ: AI Agent Channels` registry row whose `ClientPluginClass` is\n * `'RealtimeRemoteBrowserChannel'`. One instance per session (ClassFactory-created at session\n * start).\n *\n * Topology (CLIENT-DIRECT, like the whiteboard — the browser just happens to live on the\n * server):\n * - **Action**: the `browser_*` client-executed tool set ({@link REMOTE_BROWSER_TOOL_DEFINITIONS}).\n * {@link ApplyAgentTool} maps a tool call → a normalized {@link RemoteBrowserAction}\n * ({@link MapToolToAction}) and awaits the `ExecuteRemoteBrowserAction` GraphQL mutation\n * (via {@link RealtimeChannelContext.ExecuteServerAction}) to drive the server browser,\n * returning a concise JSON result for the model. Never throws — argument and transport\n * failures map to a `{ success: false, error }` payload.\n * - **Perception**: after a successful action, a `[browser]` context note carries the new\n * page URL so the agent perceives where the page went; the surface independently polls a\n * live screenshot.\n * - **Surface**: {@link RemoteBrowserSurfaceComponent}, created dynamically in the channel\n * tab; the plugin hands it the session's provider + id in {@link BindSurface} so it can\n * poll `RemoteBrowserSnapshot`. HUMAN TAKEOVER is enabled by default (Collaborative): the\n * user can click/type into the live canvas and those events relay to the server browser via\n * {@link RELAY_HUMAN_INPUT_MUTATION} (server-gated on the backend's `HumanTakeover` capability).\n *\n * The channel keeps NO client-side state of record — the browser's state lives entirely on\n * the server — so {@link SerializeState} / {@link RestoreState} use the base no-op behavior.\n */\n@RegisterClass(BaseRealtimeChannelClient, 'RealtimeRemoteBrowserChannel')\nexport class RemoteBrowserChannel extends BaseRealtimeChannelClient<RemoteBrowserSurfaceComponent> implements IChannelFrameProvider {\n /** The live bound surface, when the channel tab's pane is instantiated. */\n private surface: RemoteBrowserSurfaceComponent | null = null;\n\n /** Shared video bridge streaming browser frames to the model when the model supports inbound video. */\n private videoBridge: ChannelInboundVideoBridge | null = null;\n\n /**\n * Sourced tracks: Remote Browser can source inbound video to the model when the model supports it.\n */\n public override GetSourcedTracks(): readonly RealtimeTrackDescriptor[] {\n return [CHANNEL_INBOUND_VIDEO_TRACK];\n }\n\n /**\n * Produces the latest browser screenshot as a base64-encoded frame for the video bridge.\n * Under active screencast streaming, returns null to avoid redundant server snapshot round-trips\n * and duplicate frame sourcing into the video bridge.\n */\n public async GetLatestFrame(): Promise<string | null> {\n this.ensureVideoBridge();\n if (this.streaming) {\n return null;\n }\n const snapshot = await this.fetchSnapshot();\n return snapshot?.ScreenshotBase64 ?? null;\n }\n\n /** Subscription to the bound surface's `HumanInput` output, torn down on unbind/dispose. */\n private humanInputSub: Subscription | null = null;\n\n /**\n * Whether the server confirmed it is PUSHING screencast frames for this session (`ScreenStreaming`\n * supported). When `true` the surface renders pushed frames on its canvas and skips its poll, and\n * {@link OnScreencastFrame} forwards each pushed frame to it. When `false` the surface keeps polling.\n */\n private streaming = false;\n\n /**\n * Whether the server confirmed it is PUSHING tab-audio chunks for this session (the backend can capture\n * audio). When `true` {@link OnAudioChunk} feeds each pushed chunk to {@link audioPlayer}; when `false`\n * no audio plays.\n */\n private audioStreaming = false;\n\n /**\n * The last URL the agent was TOLD about, so an unchanged page is not re-announced (#3496). `null`\n * until the first sighting, which is deliberately announced as a plain \"current page\" rather than\n * as somebody else navigating — the first page of a session was nobody's takeover.\n */\n private lastNotedUrl: string | null = null;\n\n /**\n * How many browser operations the AGENT itself has in flight right now (#3496).\n *\n * Attribution cannot be read off the feed a change arrives on. A pushed frame or a perception poll\n * only says the page moved; it is `> 0` here that says the agent is the one moving it. Two cases\n * make that necessary rather than tidy:\n *\n * - **an action's frames outrun its reply.** Under streaming, frames of the new page are pushed\n * while `ExecuteRemoteBrowserAction` is still in flight, so the observation reliably lands BEFORE\n * the mutation returns the URL — the agent's own navigation announced back to it as a takeover.\n * - **a goal drives for minutes.** `browser_AchieveGoal` runs an autonomous loop server-side and\n * every page it opens is observed with nothing yet returned, so the whole run would narrate\n * itself as somebody else.\n *\n * A counter, not a flag: goals and actions overlap, and the last one to finish must not clear a\n * window another still owns.\n */\n private agentDrivingDepth = 0;\n\n /** The client-side audio player fed pushed chunks while {@link audioStreaming}; `null` until started. */\n private audioPlayer: RemoteBrowserAudioPlayer | null = null;\n\n /** Subscription to the bound surface's `AudioMutedChange` output (the speaker toggle), torn down on unbind. */\n private audioMutedSub: Subscription | null = null;\n\n /**\n * Poll cadence + deadline for {@link pollGoalResult} (the async `browser_AchieveGoal` result poll).\n * Protected so tests can shrink them; production polls every 2.5s for up to 5 minutes (a goal loop\n * past 5 min keeps running server-side, the model just gets a \"still running\" note).\n */\n protected GoalPollIntervalMs = 2500;\n protected GoalPollTimeoutMs = 5 * 60 * 1000;\n\n public get ChannelName(): string {\n return 'Remote Browser';\n }\n\n public get ToolNamePrefix(): string {\n return REMOTE_BROWSER_TOOL_PREFIX;\n }\n\n public get TabTitle(): string {\n return 'Browser';\n }\n\n public get TabIcon(): string {\n return 'fa-solid fa-globe';\n }\n\n public GetToolDefinitions(): RealtimeToolDefinition[] {\n return REMOTE_BROWSER_TOOL_DEFINITIONS;\n }\n\n public override GetSurfaceComponent(): Type<RemoteBrowserSurfaceComponent> {\n return RemoteBrowserSurfaceComponent;\n }\n\n /** First-run intro shown the first time the user opens the Browser tab (once per user). */\n public override GetOnboardingDetails(): ChannelOnboardingDetails {\n return {\n Heading: 'Remote Browser',\n Description:\n 'The agent drives a live web browser on the server and you can watch it work right here. ' +\n 'You can also grab the wheel: click and type directly on the live page and the agent picks ' +\n 'up right where you left off.',\n Tips: [\n 'Ask the agent to look something up, open a site or fill in a form — it controls the page.',\n 'Click on the live view to take over, then click and type to drive it yourself — the \"You\\'re driving\" badge shows when takeover is active.',\n 'The current page URL is shown so you always know where the agent has navigated.',\n ],\n IconClass: 'fa-solid fa-globe',\n };\n }\n\n /** Starts the video bridge streaming browser frames to the model when inbound video is supported. */\n protected override OnInitialize(): void {\n this.ensureVideoBridge();\n }\n\n public override OnSessionStarted(): void {\n this.ensureVideoBridge();\n }\n\n private ensureVideoBridge(): ChannelInboundVideoBridge | null {\n if (!this.videoBridge && this.Context) {\n this.videoBridge = new ChannelInboundVideoBridge(() => this.Context?.Client, this);\n }\n if (this.videoBridge && !this.videoBridge.IsActive && this.Context?.Client?.IsTrackEstablished('video', 'inbound')) {\n this.videoBridge.Start?.();\n }\n return this.videoBridge;\n }\n\n /**\n * Wires the dynamically-created surface: hands it a snapshot fetcher closing over the\n * channel context (session id + provider live there), so the surface stays transport-\n * agnostic. Set BEFORE the surface's first change detection (the pane binds synchronously),\n * so its `ngOnInit` poll has the fetcher.\n *\n * Also asks the server to start a live screencast. When the backend supports `ScreenStreaming` the\n * server starts PUSHING frames and reports `Streaming: true`; we flip the surface to canvas mode (its\n * poll is then skipped) and {@link OnScreencastFrame} paints each pushed frame. When the backend lacks\n * the capability (`Streaming: false`) the surface keeps the snapshot poll already running — graceful\n * fallback, no further action.\n *\n * HUMAN TAKEOVER is enabled BY DEFAULT (Collaborative): the surface is flipped `Interactive = true` and\n * its `HumanInput` output subscribed, forwarding each pointer/keyboard event to the server via\n * {@link RELAY_HUMAN_INPUT_MUTATION}. Takeover only takes effect on the canvas (screencast) path; the\n * `<img>` poll fallback stays view-only. The server gates the actual routing on the backend's\n * `HumanTakeover` capability, so this is safe to enable unconditionally.\n */\n public BindSurface(instance: RemoteBrowserSurfaceComponent): void {\n this.surface = instance;\n instance.Fetch = () => this.fetchSnapshot();\n // Copy-out: the surface reads the remote selection through this on a local `copy` and writes it locally.\n instance.FetchSelection = () => this.fetchSelection();\n instance.Interactive = true;\n this.ensureVideoBridge();\n this.humanInputSub = instance.HumanInput.subscribe((input) => this.relayHumanInput(input));\n this.audioMutedSub = instance.AudioMutedChange.subscribe((muted) => this.audioPlayer?.SetMuted(muted));\n void this.startScreencast(instance);\n void this.startAudioStream(instance);\n }\n\n public override UnbindSurface(): void {\n this.humanInputSub?.unsubscribe();\n this.humanInputSub = null;\n this.audioMutedSub?.unsubscribe();\n this.audioMutedSub = null;\n void this.stopScreencast();\n void this.stopAudioStream();\n this.surface = null;\n }\n\n public override Dispose(): void {\n this.clearScreencastTrailingTimer();\n this.pendingScreencastFrame = null;\n this.videoBridge?.Stop();\n this.videoBridge = null;\n this.humanInputSub?.unsubscribe();\n this.humanInputSub = null;\n this.audioMutedSub?.unsubscribe();\n this.audioMutedSub = null;\n void this.stopScreencast();\n void this.stopAudioStream();\n super.Dispose();\n }\n\n /** Timestamp of the last screencast frame pushed to the video bridge (paces pushes to negotiated cadence). */\n private lastScreencastPushTime = 0;\n\n /** Base64 content of the last frame pushed to the video bridge (for change-driven deduplication). */\n private lastPushedScreencastFrame: string | null = null;\n\n /** URL associated with the last frame pushed to the video bridge. */\n private lastPushedUrl: string | null = null;\n\n /** Minimum allowable screencast push cadence (250ms = 4 fps debounce limit during active bursts). */\n private static readonly SCREENCAST_MIN_CADENCE_MS = 250;\n\n /** Default screencast push cadence when not otherwise negotiated (1000ms = 1 fps ceiling for Gemini Live). */\n private static readonly SCREENCAST_DEFAULT_CADENCE_MS = 1000;\n\n /** Maximum interval (ms) between visual frame pushes on a static page (heartbeat to maintain track liveness). */\n private static readonly SCREENCAST_HEARTBEAT_MS = 15_000;\n\n /** Trailing timer to deliver the settled resting frame after a burst of rapid user activity. */\n private screencastTrailingTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** Pending frame buffered during an active cooldown interval, waiting to settle. */\n private pendingScreencastFrame: { dataBase64: string; currentUrl?: string | null } | null = null;\n\n /** Cancels any active trailing-edge settle timer and clears the handle. */\n private clearScreencastTrailingTimer(): void {\n if (this.screencastTrailingTimer != null) {\n clearTimeout(this.screencastTrailingTimer);\n this.screencastTrailingTimer = null;\n }\n }\n\n /**\n * Pushes a frame to the video bridge, recording timestamp, frame content, and URL.\n */\n private pushScreencastFrame(dataBase64: string, currentUrl?: string | null): void {\n this.lastScreencastPushTime = Date.now();\n this.lastPushedScreencastFrame = dataBase64;\n this.lastPushedUrl = currentUrl ?? null;\n this.ensureVideoBridge()?.PushFrame(dataBase64);\n }\n\n /**\n * Resolves the effective push cadence in milliseconds based on the negotiated inbound video track.\n * Defaults to 1000ms (1 fps ceiling for Gemini Live), but clamps down to a minimum of 250ms (4 fps)\n * if the negotiated track specifies a higher `Rate`.\n */\n private getNegotiatedVideoCadenceMs(): number {\n const client = this.Context?.Client;\n if (!client) {\n return RemoteBrowserChannel.SCREENCAST_DEFAULT_CADENCE_MS;\n }\n const tracks: readonly RealtimeTrack[] = client.EstablishedTracks;\n const videoTrack = tracks.find(\n (t: RealtimeTrack) =>\n t.Descriptor.Modality === 'video' &&\n t.Descriptor.Direction === 'inbound'\n );\n const rate = videoTrack?.Descriptor.Rate;\n if (typeof rate === 'number' && rate > 0) {\n return Math.max(\n RemoteBrowserChannel.SCREENCAST_MIN_CADENCE_MS,\n Math.floor(1000 / rate)\n );\n }\n return RemoteBrowserChannel.SCREENCAST_DEFAULT_CADENCE_MS;\n }\n\n /**\n * Forwards one PUSHED screencast frame to the bound surface's canvas. Called by the session service\n * when a `RemoteBrowserScreencastFrame` arrives on the push-status stream for THIS session. No-op when\n * the channel isn't streaming or has no bound surface (e.g. the tab pane is collapsed).\n *\n * Under streaming the surface's snapshot poll is stopped, so this is the ONLY thing that sees the\n * page while frames are being pushed — which is why the frame now carries the URL (#3496). Without\n * it, a user navigating during a screencast changed the picture and nothing else: the agent kept\n * describing the page it last opened, and the pixels proving otherwise were right there on screen.\n *\n * Pushes to the video bridge are CHANGE-DRIVEN with a TRAILING-EDGE SETTLE DEBOUNCE:\n * 1. The human user surface renders immediately at full 60fps responsiveness.\n * 2. When the page is static (same image and URL), pushes are deduplicated to avoid burning\n * ~15k tokens/min of identical visual context.\n * 3. A frame is pushed immediately on the leading edge when visual content or URL changes after\n * the negotiated cadence (default 1000ms, clamped to >= 250ms).\n * 4. During rapid bursts (scrolling, typing, clicking) within the cooldown window, subsequent frames\n * are buffered; when the cooldown expires, a trailing timer delivers the settled resting frame\n * so the model never misses the final user state.\n * 5. A 15-second heartbeat ensures visual track liveness even during extended idle periods.\n *\n * @param dataBase64 The frame image as raw base64 JPEG (no `data:` prefix).\n * @param currentUrl The browser's URL when the frame was captured; absent from older servers.\n */\n public OnScreencastFrame(dataBase64: string, currentUrl?: string | null): void {\n if (!this.streaming) {\n return;\n }\n\n this.surface?.RenderFrame(dataBase64);\n\n const now = Date.now();\n const cadenceMs = this.getNegotiatedVideoCadenceMs();\n const elapsed = now - this.lastScreencastPushTime;\n\n if (elapsed >= cadenceMs) {\n // Cooldown has expired: leading edge push.\n this.clearScreencastTrailingTimer();\n this.pendingScreencastFrame = null;\n\n const frameChanged = dataBase64 !== this.lastPushedScreencastFrame;\n const urlChanged = currentUrl != null && currentUrl !== this.lastPushedUrl;\n const heartbeatElapsed = elapsed >= RemoteBrowserChannel.SCREENCAST_HEARTBEAT_MS;\n\n if (frameChanged || urlChanged || heartbeatElapsed) {\n this.pushScreencastFrame(dataBase64, currentUrl);\n }\n } else {\n // Within cooldown window: buffer latest frame and schedule trailing settle timer.\n this.pendingScreencastFrame = { dataBase64, currentUrl };\n if (!this.screencastTrailingTimer) {\n const delay = Math.max(0, cadenceMs - elapsed);\n this.screencastTrailingTimer = setTimeout(() => {\n this.screencastTrailingTimer = null;\n if (!this.streaming || !this.pendingScreencastFrame) {\n return;\n }\n const { dataBase64: pendingData, currentUrl: pendingUrl } = this.pendingScreencastFrame;\n this.pendingScreencastFrame = null;\n\n const frameChanged = pendingData !== this.lastPushedScreencastFrame;\n const urlChanged = pendingUrl != null && pendingUrl !== this.lastPushedUrl;\n const heartbeat = (Date.now() - this.lastScreencastPushTime) >= RemoteBrowserChannel.SCREENCAST_HEARTBEAT_MS;\n\n if (frameChanged || urlChanged || heartbeat) {\n this.pushScreencastFrame(pendingData, pendingUrl);\n }\n }, delay);\n }\n }\n\n // #3496: URL perception note runs AFTER the push so the visual image lands before/with the note.\n this.notePageChange(currentUrl, 'observed');\n }\n\n /**\n * Feeds one PUSHED tab-audio chunk to the client-side audio player. Called by the session service when a\n * `RemoteBrowserAudioChunk` arrives on the push-status stream for THIS session. No-op when the channel\n * isn't audio-streaming or the player has been torn down.\n *\n * @param chunk The pushed audio chunk (base64 webm-opus + codec/seq metadata).\n */\n public OnAudioChunk(chunk: RemoteBrowserAudioChunkInput): void {\n if (this.audioStreaming) {\n this.audioPlayer?.Enqueue(chunk);\n }\n }\n\n /**\n * Asks the server to start the live screencast and, on success, flips the surface to canvas mode so it\n * stops polling and starts painting pushed frames. Best-effort: a `null`/`Streaming: false` result\n * (capability absent or transport failure) leaves the surface on its poll fallback.\n */\n private async startScreencast(instance: RemoteBrowserSurfaceComponent): Promise<void> {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return;\n }\n const data = await this.Context?.ExecuteServerAction<StartRemoteBrowserScreencastResult>(START_SCREENCAST_MUTATION, { agentSessionID: sessionId });\n if (data?.StartRemoteBrowserScreencast?.Streaming === true) {\n this.streaming = true;\n instance.Streaming = true;\n }\n }\n\n /**\n * Asks the server to start the live tab-audio stream and, on success, spins up the client-side\n * {@link RemoteBrowserAudioPlayer} (speaker ON by default — the call is the activating user gesture) so\n * pushed chunks play. Best-effort: a `null`/`Streaming: false` result (no audio capability or transport\n * failure) leaves the channel silent with no player.\n *\n * @param instance The bound surface, flipped to show the speaker toggle when audio starts.\n */\n private async startAudioStream(instance: RemoteBrowserSurfaceComponent): Promise<void> {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return;\n }\n const data = await this.Context?.ExecuteServerAction<StartRemoteBrowserAudioStreamResult>(START_AUDIO_STREAM_MUTATION, { agentSessionID: sessionId });\n if (data?.StartRemoteBrowserAudioStream?.Streaming === true) {\n this.audioStreaming = true;\n this.audioPlayer = new RemoteBrowserAudioPlayer(new MediaSourceAudioSink());\n // Speaker defaults ON; reflect that on the surface so the toggle renders un-muted.\n instance.AudioAvailable = true;\n instance.AudioMuted = false;\n }\n }\n\n /** Asks the server to stop the tab-audio stream (best-effort), disposes the player, and clears state. */\n private async stopAudioStream(): Promise<void> {\n const wasStreaming = this.audioStreaming;\n this.audioStreaming = false;\n this.audioPlayer?.Dispose();\n this.audioPlayer = null;\n const sessionId = this.Context?.AgentSessionID;\n if (!wasStreaming || !sessionId) {\n return;\n }\n await this.Context?.ExecuteServerAction(STOP_AUDIO_STREAM_MUTATION, { agentSessionID: sessionId });\n }\n\n /**\n * Relays ONE human-takeover input from the surface to the server's {@link RELAY_HUMAN_INPUT_MUTATION}.\n * Best-effort and fire-and-forget: never throws, and the boolean result is ignored (the server already\n * gates routing on the backend's `HumanTakeover` capability). No-op when no live session id exists.\n *\n * @param input The flattened pointer/keyboard input the user performed on the live canvas.\n */\n private relayHumanInput(input: RemoteBrowserHumanInputEvent): void {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return;\n }\n void this.Context?.ExecuteServerAction(RELAY_HUMAN_INPUT_MUTATION, {\n agentSessionID: sessionId,\n kind: input.kind,\n x: input.x ?? null,\n y: input.y ?? null,\n button: input.button ?? null,\n key: input.key ?? null,\n // Paste-in: the `'text'` kind carries the local clipboard text the server inserts into the page.\n text: input.text ?? null,\n deltaX: input.deltaX ?? null,\n deltaY: input.deltaY ?? null,\n // Server expects a comma-separated modifier list (e.g. \"Shift,Control\"); null when none held.\n modifiers: input.modifiers && input.modifiers.length > 0 ? input.modifiers.join(',') : null,\n });\n }\n\n /**\n * Reads the remote page's current selection for COPY-OUT — backs the surface's `FetchSelection`. Awaits\n * the `GetRemoteBrowserSelection` query and returns its text. Best-effort by contract\n * ({@link RemoteBrowserSelectionFetcher}): returns `''` when no session is live or the query fails, so a\n * best-effort copy never throws.\n */\n private async fetchSelection(): Promise<string> {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return '';\n }\n const data = await this.Context?.ExecuteServerAction<GetRemoteBrowserSelectionResult>(GET_SELECTION_QUERY, { agentSessionID: sessionId });\n return data?.GetRemoteBrowserSelection?.Text ?? '';\n }\n\n /** Asks the server to stop the screencast (best-effort) and clears the streaming flag. */\n private async stopScreencast(): Promise<void> {\n const wasStreaming = this.streaming;\n this.streaming = false;\n this.clearScreencastTrailingTimer();\n this.pendingScreencastFrame = null;\n this.lastPushedScreencastFrame = null;\n this.lastPushedUrl = null;\n this.lastScreencastPushTime = 0;\n const sessionId = this.Context?.AgentSessionID;\n if (!wasStreaming || !sessionId) {\n return;\n }\n await this.Context?.ExecuteServerAction(STOP_SCREENCAST_MUTATION, { agentSessionID: sessionId });\n }\n\n /**\n * Fetches one live snapshot of the server browser for the surface — the PERCEPTION poll.\n * Best-effort by contract ({@link RemoteBrowserSnapshotFetcher}): returns `null` when no\n * session is live or the query fails (the surface keeps its last good frame).\n *\n * Also the channel's chance to NOTICE a page change it did not cause (#3496): the poll already\n * carries the URL, and until now only the surface read it.\n */\n private async fetchSnapshot(): Promise<RemoteBrowserSnapshotView | null> {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return null;\n }\n const data = await this.Context?.ExecuteServerAction<RemoteBrowserSnapshotResult>(REMOTE_BROWSER_SNAPSHOT_QUERY, { agentSessionID: sessionId });\n const snapshot = data?.RemoteBrowserSnapshot ?? null;\n this.notePageChange(snapshot?.CurrentUrl ?? null, 'observed');\n return snapshot;\n }\n\n /**\n * The ONE place the agent is told where the page is (#3496).\n *\n * Before this, the `[browser] current page:` note was pushed from exactly two call sites, both\n * immediately after a server action the MODEL itself initiated. That placement — not any caching —\n * was the bug: **a surface change the agent did not cause was invisible to it.** A user taking over\n * and navigating produced no note, so the agent went on confidently describing the previous page,\n * and only corrected when told to look again. Human takeover is on by default for `Collaborative`\n * providers, so the default configuration was the broken one.\n *\n * Routing every observation through here makes the rule \"the agent hears about the page whenever it\n * MOVES, whoever moved it\" — a property of this method rather than of where callers happen to be.\n *\n * `cause` is what the agent could never work out for itself. An `'agent'` change is one this\n * channel just performed; anything spotted by the perception poll or a pushed frame is\n * `'observed'` — someone else is driving, and saying so is the difference between the agent\n * knowing the page moved and knowing it is no longer the one moving it.\n *\n * Silent when the URL has not changed, which is not an optimisation but a requirement: the\n * perception poll runs every ~700ms and an unconditional note would bury the conversation in\n * hundreds of identical lines.\n */\n private notePageChange(url: string | null | undefined, cause: 'agent' | 'observed'): void {\n const next = (url ?? '').trim();\n if (next.length === 0) {\n return;\n }\n // The live-view bar is refreshed even when the URL is unchanged: it is idempotent, and in\n // streaming mode it is the only thing keeping the bar off \"No page loaded yet\".\n this.surface?.SetCurrentUrl(next);\n if (next === this.lastNotedUrl) {\n return;\n }\n const firstSighting = this.lastNotedUrl === null;\n this.lastNotedUrl = next;\n // A change seen WHILE the agent is driving is the agent's own, whichever feed spotted it first.\n const byAgent = cause === 'agent' || this.agentDrivingDepth > 0;\n this.Context?.SendContextNote(\n byAgent || firstSighting\n ? `[browser] current page: ${next}`\n : `[browser] the page changed to: ${next} — you did not navigate here, so someone else is driving. `\n + `Describe THIS page, not the one you last opened.`,\n );\n }\n\n /**\n * Runs one agent-initiated browser operation with {@link agentDrivingDepth} raised, so any page\n * change observed while it runs is attributed to the agent rather than to a phantom third party.\n *\n * `finally` and not a trailing decrement: the window must close on a thrown transport error too,\n * or one failed tool call would leave the channel permanently unable to report a real takeover.\n */\n private async whileAgentDrives<T>(work: () => Promise<T>): Promise<T> {\n this.agentDrivingDepth++;\n try {\n return await work();\n } finally {\n this.agentDrivingDepth--;\n }\n }\n\n /**\n * Executes ONE `browser_*` tool call: maps it to a server action and awaits the\n * `ExecuteRemoteBrowserAction` mutation, then returns a concise JSON result the model reads.\n * On success it also feeds the new page URL to the agent as a `[browser]` context note. Never\n * throws — bad arguments, no live session, and server failures all become a failed-result\n * payload so the model can narrate the problem.\n */\n public async ApplyAgentTool(toolName: string, argsJson: string): Promise<string> {\n // VISION-QUERY branch: the two interpreter tools OBSERVE the page (run a vision model over a screenshot)\n // rather than drive it, so they route to the separate interpret mutation, not MapToolToAction.\n if (toolName === REMOTE_BROWSER_TOOL_NAMES.DescribePage) {\n return this.interpretPage(undefined);\n }\n if (toolName === REMOTE_BROWSER_TOOL_NAMES.AchieveGoal) {\n return this.achieveGoal(argsJson);\n }\n if (toolName === REMOTE_BROWSER_TOOL_NAMES.LocateElement) {\n const description = this.asArgString(this.parseArgs(argsJson)['description']);\n if (!description) {\n return this.fail('browser_LocateElement requires a \"description\" of the element to find.');\n }\n return this.interpretPage(description);\n }\n\n let action: RemoteBrowserAction;\n try {\n action = MapToolToAction(toolName, this.parseArgs(argsJson));\n } catch (error) {\n const message = error instanceof RemoteBrowserToolArgError ? error.message : 'Invalid Remote Browser tool arguments.';\n return this.fail(message);\n }\n\n // Briefly WAIT for the session id rather than failing instantly: the model can fire a tool the\n // first beat it connects, before mintSession binds the id (the \"session id missing\" race).\n const sessionId = await this.ResolveAgentSessionId();\n if (!sessionId) {\n // Diagnostic: distinguishes \"channel never Initialized (no Context)\" from \"session id never\n // resolved within the wait window\" — the two distinct causes of a null id at tool-execution time.\n console.warn('[RemoteBrowser] browser tool invoked but AgentSessionID is null', {\n tool: toolName,\n hasContext: !!this.Context,\n agentSessionID: this.Context?.AgentSessionID ?? null,\n });\n return this.fail('No live browser session is available yet — the realtime session may still be connecting; try again in a moment.');\n }\n\n return this.whileAgentDrives(async () => {\n const data = await this.Context?.ExecuteServerAction<ExecuteRemoteBrowserActionResult>(\n EXECUTE_REMOTE_BROWSER_ACTION_MUTATION,\n this.buildVariables(sessionId, action),\n );\n const result = data?.ExecuteRemoteBrowserAction ?? null;\n if (!result) {\n return this.fail('The browser action could not be executed (no response from the server).');\n }\n if (!result.Success) {\n return this.fail(result.Detail ?? 'The browser action failed.');\n }\n\n // PERCEPTION: tell the agent where the page is now (background — no spoken reply), and refresh\n // the live-view bar, which in streaming mode has no poll feeding it.\n this.notePageChange(result.CurrentUrl, 'agent');\n return this.ok(result.CurrentUrl, result.Detail);\n });\n }\n\n /** Builds the flat mutation variables from a normalized action (omitted fields ride as null). */\n private buildVariables(sessionId: string, action: RemoteBrowserAction): Record<string, JSONValue> {\n return {\n agentSessionID: sessionId,\n kind: action.Kind,\n url: action.Url ?? null,\n selector: action.Selector ?? null,\n x: action.X ?? null,\n y: action.Y ?? null,\n text: action.Text ?? null,\n key: action.Key ?? null,\n deltaX: action.DeltaX ?? null,\n deltaY: action.DeltaY ?? null,\n ms: action.Ms ?? null,\n };\n }\n\n /**\n * GOAL path for the `browser_AchieveGoal` tool: hands a high-level goal to the server's autonomous\n * browser agent (computer-use loop / native AI) via {@link EXECUTE_REMOTE_BROWSER_GOAL_MUTATION} and\n * returns a concise result for the model. Never throws — failures map to a `{ success: false }` string.\n *\n * @param argsJson The raw tool-call arguments (`{ goal, startUrl? }`).\n * @returns The model-readable result string.\n */\n private async achieveGoal(argsJson: string): Promise<string> {\n // Wait briefly for the session id (mint/reconnect race) before giving up — see ResolveAgentSessionId.\n const sessionId = await this.ResolveAgentSessionId();\n if (!sessionId) {\n return this.fail('No live browser session is available yet — the realtime session may still be connecting; try again in a moment.');\n }\n const args = this.parseArgs(argsJson);\n const goal = this.asArgString(args['goal']);\n if (!goal) {\n return this.fail('browser_AchieveGoal requires a \"goal\" describing what to accomplish.');\n }\n const startUrl = this.asArgString(args['startUrl']) ?? null;\n\n // START the goal (returns immediately with a GoalRunID) then POLL — a goal loop can run for\n // minutes, far longer than any single request survives, so we never hold one open for the loop.\n // The whole span is agent-driven: every page the loop opens is observed with nothing returned\n // yet, so without the window the agent would narrate its own goal as somebody else's takeover.\n return this.whileAgentDrives(async () => {\n const startData = await this.Context?.ExecuteServerAction<ExecuteRemoteBrowserGoalResult>(EXECUTE_REMOTE_BROWSER_GOAL_MUTATION, {\n agentSessionID: sessionId,\n goal,\n startUrl,\n });\n const started = startData?.ExecuteRemoteBrowserGoal ?? null;\n if (!started) {\n return this.fail('The browser goal could not be started (no response from the server).');\n }\n // Defensive: if the server ever returns a terminal status synchronously, use it directly.\n const result = started.Status === 'Running' && started.GoalRunID\n ? await this.pollGoalResult(sessionId, started.GoalRunID)\n : started;\n if (!result) {\n return this.fail('The browser goal is taking longer than expected and could not be confirmed. It may still be running.');\n }\n this.notePageChange(result.CurrentUrl, 'agent');\n if (!result.Success) {\n return this.fail(result.Detail ?? `The goal could not be completed (${result.Status ?? 'unknown'}).`);\n }\n return this.ok(result.CurrentUrl, result.Detail ?? `Goal completed (${result.Status ?? 'Completed'}, ${result.StepCount ?? 0} steps).`);\n });\n }\n\n /**\n * Polls {@link GET_REMOTE_BROWSER_GOAL_RESULT_QUERY} for a started goal run until it reports a\n * terminal status (anything other than `Running`), then returns that terminal payload. Each poll is\n * a short request, so no transport boundary times out while a long goal loop runs server-side.\n *\n * Returns `null` if the goal is still `Running` after {@link GoalPollTimeoutMs} (the loop keeps\n * running server-side; the caller maps this to a \"still running\" message), the session went away, or\n * the run id is no longer known (`Unknown` — expired/superseded).\n */\n private async pollGoalResult(sessionId: string, goalRunID: string): Promise<RemoteBrowserGoalPayload | null> {\n const interval = Math.max(1, this.GoalPollIntervalMs);\n for (let waited = 0; waited < this.GoalPollTimeoutMs; waited += interval) {\n await new Promise<void>((resolve) => setTimeout(resolve, interval));\n // The session ended mid-goal — stop polling (the live browser is gone).\n if (!this.Context?.AgentSessionID) {\n return null;\n }\n const data = await this.Context.ExecuteServerAction<GetRemoteBrowserGoalResultPayload>(GET_REMOTE_BROWSER_GOAL_RESULT_QUERY, {\n agentSessionID: sessionId,\n goalRunID,\n });\n const payload = data?.GetRemoteBrowserGoalResult ?? null;\n // A transient null (one failed poll) shouldn't abort — keep polling until the deadline.\n if (payload && payload.Status !== 'Running') {\n return payload;\n }\n }\n return null;\n }\n\n /**\n * Runs the VISION-QUERY path for the `browser_DescribePage` / `browser_LocateElement` tools: awaits the\n * `InterpretRemoteBrowserPage` mutation (server captures a screenshot + runs a fast vision model) and returns\n * a concise JSON string the model reads. For a describe (no `query`) it returns `{ description }`; for a locate\n * it returns `{ found, element, all }` so the agent can `browser_Click` the centroid. Never throws — a null\n * session, no response, or a server detail-only result all map to a result string the model can narrate.\n *\n * @param query The visual target to locate, or `undefined` for a plain page description.\n * @returns A JSON string describing the interpretation for the model.\n */\n private async interpretPage(query: string | undefined): Promise<string> {\n // Wait briefly for the session id (mint/reconnect race) before giving up — see ResolveAgentSessionId.\n const sessionId = await this.ResolveAgentSessionId();\n if (!sessionId) {\n console.warn('[RemoteBrowser] interpret tool invoked but AgentSessionID is null', {\n hasContext: !!this.Context,\n agentSessionID: this.Context?.AgentSessionID ?? null,\n });\n return this.fail('No live browser session is available yet — the realtime session may still be connecting; try again in a moment.');\n }\n\n const data = await this.Context?.ExecuteServerAction<InterpretRemoteBrowserPageResult>(INTERPRET_PAGE_MUTATION, {\n agentSessionID: sessionId,\n query: query ?? null,\n });\n const result = data?.InterpretRemoteBrowserPage ?? null;\n if (!result) {\n return this.fail('The page could not be interpreted (no response from the server).');\n }\n\n if (query === undefined) {\n // DescribePage — hand back the description (or the server's detail note when nothing was interpreted).\n return JSON.stringify({ description: result.Description ?? result.Detail ?? null });\n }\n\n // LocateElement — surface whether anything matched, the best match, and all candidates.\n const elements = result.Elements ?? [];\n const best = elements[0];\n return JSON.stringify({\n found: elements.length > 0,\n element: best ? { label: best.Label, x: best.X, y: best.Y } : null,\n all: elements,\n ...(result.Detail ? { detail: result.Detail } : {}),\n });\n }\n\n /** Coerces a parsed tool-arg to a non-empty string, or `undefined` when absent / wrong-typed. */\n private asArgString(value: JSONValue | undefined): string | undefined {\n return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;\n }\n\n /** Parses the tool-args JSON into a plain object; returns `{}` for empty/malformed input. */\n private parseArgs(argsJson: string): Record<string, JSONValue> {\n const trimmed = argsJson?.trim();\n if (!trimmed) {\n return {};\n }\n try {\n const parsed: unknown = JSON.parse(trimmed);\n return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Record<string, JSONValue>) : {};\n } catch {\n return {};\n }\n }\n\n /** Builds a successful tool-result JSON string for the model. */\n private ok(currentUrl: string | null, detail: string | null): string {\n const result: RemoteBrowserToolResult = { success: true };\n if (currentUrl) {\n result.currentUrl = currentUrl;\n }\n if (detail) {\n result.detail = detail;\n }\n return JSON.stringify(result);\n }\n\n /** Builds a failed tool-result JSON string for the model. */\n private fail(error: string): string {\n const result: RemoteBrowserToolResult = { success: false, error };\n return JSON.stringify(result);\n }\n}\n\n/**\n * Tree-shaking prevention: the Remote Browser channel is resolved dynamically through the\n * ClassFactory (by the registry row's `ClientPluginClass` key), so this static call is what\n * keeps its `@RegisterClass` side effect from being eliminated by the bundler. Called from\n * `conversations.module.ts` alongside {@link LoadRealtimeWhiteboardChannel}.\n */\nexport function LoadRealtimeRemoteBrowserChannel(): void {\n // intentional no-op — the import side effect performs the registration\n}\n"]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { Type } from '@angular/core';
|
|
2
2
|
import { RealtimeToolDefinition, RealtimeTrackDescriptor } from '@memberjunction/ai';
|
|
3
3
|
import { IChannelFrameProvider } from '@memberjunction/ai-realtime-client';
|
|
4
|
-
import { BaseRealtimeChannelClient, ChannelOnboardingDetails } from '
|
|
4
|
+
import { BaseRealtimeChannelClient, ChannelOnboardingDetails } from '@memberjunction/realtime-runtime';
|
|
5
5
|
import { RealtimeWhiteboardHostComponent, WhiteboardState } from '@memberjunction/ng-whiteboard';
|
|
6
6
|
/**
|
|
7
7
|
* Asynchronously rasterizes an SVG string to a JPEG base64 string (without the `data:image/jpeg;base64,` prefix)
|
|
8
8
|
* using an offscreen canvas. Returns null in non-DOM environments or when rendering fails.
|
|
9
9
|
*/
|
|
10
|
-
export declare function
|
|
10
|
+
export declare function RasterizeSvgToJpegBase64(svg: string, width?: number, height?: number): Promise<string | null>;
|
|
11
11
|
/**
|
|
12
12
|
* Per-widget throttle window for AMBIENT interaction context notes: at most one note per
|
|
13
13
|
* widget per this many ms — within the window the LATEST summary wins (chatty widgets
|
|
@@ -165,4 +165,6 @@ export declare class RealtimeWhiteboardChannel extends BaseRealtimeChannelClient
|
|
|
165
165
|
* Called by `RealtimeSessionService` alongside the realtime-client driver Load calls.
|
|
166
166
|
*/
|
|
167
167
|
export declare function LoadRealtimeWhiteboardChannel(): void;
|
|
168
|
+
/** @deprecated Use {@link RasterizeSvgToJpegBase64}. */
|
|
169
|
+
export declare function rasterizeSvgToJpegBase64(svg: string, width?: number, height?: number): Promise<string | null>;
|
|
168
170
|
//# sourceMappingURL=whiteboard-channel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whiteboard-channel.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/whiteboard/whiteboard-channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C,OAAO,EAA+B,sBAAsB,EAAiB,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AACjI,OAAO,EAA6B,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AACtG,OAAO,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"whiteboard-channel.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/whiteboard/whiteboard-channel.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ var RealtimeWhiteboardChannel_1;
|
|
|
8
8
|
import { RegisterClass } from '@memberjunction/global';
|
|
9
9
|
import { CHANNEL_INBOUND_VIDEO_TRACK } from '@memberjunction/ai';
|
|
10
10
|
import { ChannelInboundVideoBridge } from '@memberjunction/ai-realtime-client';
|
|
11
|
-
import { BaseRealtimeChannelClient } from '
|
|
11
|
+
import { BaseRealtimeChannelClient } from '@memberjunction/realtime-runtime';
|
|
12
12
|
import { ApplyWhiteboardAgentTool, BuildWhiteboardExportSvg, RealtimeWhiteboardHostComponent, WHITEBOARD_TOOL_DEFINITIONS, WHITEBOARD_TOOL_PREFIX, WhiteboardState } from '@memberjunction/ng-whiteboard';
|
|
13
13
|
/**
|
|
14
14
|
* Whether a whiteboard tool result reports success.
|
|
@@ -34,7 +34,7 @@ function toolSucceeded(result) {
|
|
|
34
34
|
* Asynchronously rasterizes an SVG string to a JPEG base64 string (without the `data:image/jpeg;base64,` prefix)
|
|
35
35
|
* using an offscreen canvas. Returns null in non-DOM environments or when rendering fails.
|
|
36
36
|
*/
|
|
37
|
-
export async function
|
|
37
|
+
export async function RasterizeSvgToJpegBase64(svg, width = 1280, height = 720) {
|
|
38
38
|
if (typeof document === 'undefined' || typeof Image === 'undefined') {
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
@@ -171,7 +171,7 @@ let RealtimeWhiteboardChannel = class RealtimeWhiteboardChannel extends BaseReal
|
|
|
171
171
|
}
|
|
172
172
|
try {
|
|
173
173
|
const svg = BuildWhiteboardExportSvg(this.State);
|
|
174
|
-
return await
|
|
174
|
+
return await RasterizeSvgToJpegBase64(svg);
|
|
175
175
|
}
|
|
176
176
|
catch (err) {
|
|
177
177
|
console.error('[RealtimeWhiteboardChannel] Failed to export whiteboard frame:', err);
|
|
@@ -551,4 +551,8 @@ export { RealtimeWhiteboardChannel };
|
|
|
551
551
|
export function LoadRealtimeWhiteboardChannel() {
|
|
552
552
|
// intentional no-op — the import side effect performs the registration
|
|
553
553
|
}
|
|
554
|
+
/** @deprecated Use {@link RasterizeSvgToJpegBase64}. */
|
|
555
|
+
export async function rasterizeSvgToJpegBase64(svg, width = 1280, height = 720) {
|
|
556
|
+
return RasterizeSvgToJpegBase64(svg, width, height);
|
|
557
|
+
}
|
|
554
558
|
//# sourceMappingURL=whiteboard-channel.js.map
|