@memberjunction/ng-conversations 5.43.0 → 5.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/components/active-tasks/active-tasks-panel.component.d.ts +0 -1
- 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 +56 -52
- package/dist/lib/components/active-tasks/active-tasks-panel.component.js.map +1 -1
- package/dist/lib/components/artifact/artifact-share-modal.component.d.ts +3 -1
- package/dist/lib/components/artifact/artifact-share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/artifact/artifact-share-modal.component.js +52 -52
- package/dist/lib/components/artifact/artifact-share-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-form-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-form-modal.component.js +10 -14
- package/dist/lib/components/collection/collection-form-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-share-modal.component.d.ts +3 -1
- package/dist/lib/components/collection/collection-share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-share-modal.component.js +56 -56
- package/dist/lib/components/collection/collection-share-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-tree.component.d.ts +3 -1
- package/dist/lib/components/collection/collection-tree.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-tree.component.js +10 -7
- package/dist/lib/components/collection/collection-tree.component.js.map +1 -1
- package/dist/lib/components/collection/collection-view.component.d.ts +3 -1
- package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-view.component.js +29 -41
- package/dist/lib/components/collection/collection-view.component.js.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.d.ts +4 -0
- package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.js +134 -166
- package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
- package/dist/lib/components/composer/ai-composer.component.d.ts +103 -0
- package/dist/lib/components/composer/ai-composer.component.d.ts.map +1 -0
- package/dist/lib/components/composer/ai-composer.component.js +263 -0
- package/dist/lib/components/composer/ai-composer.component.js.map +1 -0
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +76 -12
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +522 -158
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.d.ts +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.js.map +1 -1
- package/dist/lib/components/conversation/pinned-messages-panel.component.js +6 -9
- package/dist/lib/components/conversation/pinned-messages-panel.component.js.map +1 -1
- package/dist/lib/components/library/library-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/library/library-full-view.component.js +20 -24
- package/dist/lib/components/library/library-full-view.component.js.map +1 -1
- package/dist/lib/components/members/members-modal.component.d.ts.map +1 -1
- package/dist/lib/components/members/members-modal.component.js +10 -11
- package/dist/lib/components/members/members-modal.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +87 -23
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +245 -158
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +11 -3
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +73 -21
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.js +6 -9
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.d.ts +4 -1
- package/dist/lib/components/overlay/chat-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.js +9 -4
- package/dist/lib/components/overlay/chat-overlay.component.js.map +1 -1
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts +45 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts.map +1 -1
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js +10 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js.map +1 -1
- package/dist/lib/components/realtime/channels/client-context-channel.d.ts +46 -0
- package/dist/lib/components/realtime/channels/client-context-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/channels/client-context-channel.js +174 -0
- package/dist/lib/components/realtime/channels/client-context-channel.js.map +1 -0
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.d.ts +123 -0
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.js +397 -0
- package/dist/lib/components/realtime/evidence-playback/realtime-evidence-playback.component.js.map +1 -0
- package/dist/lib/components/realtime/media/media-channel-state.d.ts +103 -0
- package/dist/lib/components/realtime/media/media-channel-state.d.ts.map +1 -0
- package/dist/lib/components/realtime/media/media-channel-state.js +143 -0
- package/dist/lib/components/realtime/media/media-channel-state.js.map +1 -0
- package/dist/lib/components/realtime/media/media-channel-tools.d.ts +24 -0
- package/dist/lib/components/realtime/media/media-channel-tools.d.ts.map +1 -0
- package/dist/lib/components/realtime/media/media-channel-tools.js +107 -0
- package/dist/lib/components/realtime/media/media-channel-tools.js.map +1 -0
- package/dist/lib/components/realtime/media/media-channel.d.ts +89 -0
- package/dist/lib/components/realtime/media/media-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/media/media-channel.js +253 -0
- package/dist/lib/components/realtime/media/media-channel.js.map +1 -0
- package/dist/lib/components/realtime/media/realtime-media-surface.component.d.ts +141 -0
- package/dist/lib/components/realtime/media/realtime-media-surface.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/media/realtime-media-surface.component.js +574 -0
- package/dist/lib/components/realtime/media/realtime-media-surface.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts +59 -14
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-activity-rail.component.js +277 -74
- package/dist/lib/components/realtime/realtime-activity-rail.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +28 -27
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.js +144 -143
- package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts +12 -0
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-picker.component.js +105 -43
- package/dist/lib/components/realtime/realtime-agent-picker.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-audio-visuals.d.ts +2 -2
- package/dist/lib/components/realtime/realtime-audio-visuals.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-audio-visuals.js.map +1 -1
- package/dist/lib/components/realtime/realtime-composer.component.d.ts +41 -7
- package/dist/lib/components/realtime/realtime-composer.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-composer.component.js +216 -102
- package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts +0 -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 +67 -71
- package/dist/lib/components/realtime/realtime-delegation-card.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +364 -44
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.js +839 -153
- package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-state.d.ts +5 -5
- package/dist/lib/components/realtime/realtime-session-state.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-state.js.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tab-style.d.ts +70 -0
- package/dist/lib/components/realtime/realtime-surface-tab-style.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tab-style.js +115 -0
- package/dist/lib/components/realtime/realtime-surface-tab-style.js.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts +55 -57
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js +165 -143
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts +80 -71
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js +83 -113
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js.map +1 -1
- package/dist/lib/components/realtime/realtime-ui-config.d.ts +270 -0
- package/dist/lib/components/realtime/realtime-ui-config.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-ui-config.js +212 -0
- package/dist/lib/components/realtime/realtime-ui-config.js.map +1 -0
- package/dist/lib/components/search/search-panel.component.d.ts +2 -0
- package/dist/lib/components/search/search-panel.component.d.ts.map +1 -1
- package/dist/lib/components/search/search-panel.component.js +92 -101
- package/dist/lib/components/search/search-panel.component.js.map +1 -1
- package/dist/lib/components/share/share-modal.component.d.ts.map +1 -1
- package/dist/lib/components/share/share-modal.component.js +10 -11
- package/dist/lib/components/share/share-modal.component.js.map +1 -1
- package/dist/lib/components/shared/user-picker.component.d.ts.map +1 -1
- package/dist/lib/components/shared/user-picker.component.js +12 -15
- package/dist/lib/components/shared/user-picker.component.js.map +1 -1
- package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts +11 -3
- package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts.map +1 -1
- package/dist/lib/components/sidebar/conversation-sidebar.component.js +34 -10
- package/dist/lib/components/sidebar/conversation-sidebar.component.js.map +1 -1
- package/dist/lib/components/sidebar/routines-section.component.d.ts +73 -0
- package/dist/lib/components/sidebar/routines-section.component.d.ts.map +1 -0
- package/dist/lib/components/sidebar/routines-section.component.js +211 -0
- package/dist/lib/components/sidebar/routines-section.component.js.map +1 -0
- package/dist/lib/components/tasks/tasks-dropdown.component.d.ts +12 -0
- package/dist/lib/components/tasks/tasks-dropdown.component.d.ts.map +1 -1
- package/dist/lib/components/tasks/tasks-dropdown.component.js +37 -1
- package/dist/lib/components/tasks/tasks-dropdown.component.js.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts +16 -5
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +51 -31
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/composer-plugins/agent-mention.provider.d.ts +16 -0
- package/dist/lib/composer-plugins/agent-mention.provider.d.ts.map +1 -0
- package/dist/lib/composer-plugins/agent-mention.provider.js +28 -0
- package/dist/lib/composer-plugins/agent-mention.provider.js.map +1 -0
- package/dist/lib/composer-plugins/base-conversation-mention.provider.d.ts +20 -0
- package/dist/lib/composer-plugins/base-conversation-mention.provider.d.ts.map +1 -0
- package/dist/lib/composer-plugins/base-conversation-mention.provider.js +32 -0
- package/dist/lib/composer-plugins/base-conversation-mention.provider.js.map +1 -0
- package/dist/lib/composer-plugins/load-composer-plugins.d.ts +9 -0
- package/dist/lib/composer-plugins/load-composer-plugins.d.ts.map +1 -0
- package/dist/lib/composer-plugins/load-composer-plugins.js +17 -0
- package/dist/lib/composer-plugins/load-composer-plugins.js.map +1 -0
- package/dist/lib/composer-plugins/record-mention.provider.d.ts +14 -0
- package/dist/lib/composer-plugins/record-mention.provider.d.ts.map +1 -0
- package/dist/lib/composer-plugins/record-mention.provider.js +26 -0
- package/dist/lib/composer-plugins/record-mention.provider.js.map +1 -0
- package/dist/lib/composer-plugins/skill-command.provider.d.ts +15 -0
- package/dist/lib/composer-plugins/skill-command.provider.d.ts.map +1 -0
- package/dist/lib/composer-plugins/skill-command.provider.js +27 -0
- package/dist/lib/composer-plugins/skill-command.provider.js.map +1 -0
- package/dist/lib/conversations.module.d.ts +68 -64
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js +69 -18
- package/dist/lib/conversations.module.js.map +1 -1
- package/dist/lib/models/conversation-state.model.d.ts +6 -3
- 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/services/conversation-agent.service.d.ts +9 -2
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +26 -2
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/conversation-attachment.service.d.ts +1 -1
- package/dist/lib/services/conversation-attachment.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-attachment.service.js.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.d.ts +42 -19
- package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.js +113 -18
- package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
- package/dist/lib/services/realtime-audio-recorder.d.ts +143 -0
- package/dist/lib/services/realtime-audio-recorder.d.ts.map +1 -0
- package/dist/lib/services/realtime-audio-recorder.js +379 -0
- package/dist/lib/services/realtime-audio-recorder.js.map +1 -0
- package/dist/lib/services/realtime-pcm-wav.d.ts +75 -0
- package/dist/lib/services/realtime-pcm-wav.d.ts.map +1 -0
- package/dist/lib/services/realtime-pcm-wav.js +205 -0
- package/dist/lib/services/realtime-pcm-wav.js.map +1 -0
- package/dist/lib/services/realtime-session-review.service.d.ts +28 -1
- package/dist/lib/services/realtime-session-review.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session-review.service.js +12 -3
- package/dist/lib/services/realtime-session-review.service.js.map +1 -1
- package/dist/lib/services/realtime-session.service.d.ts +210 -20
- package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session.service.js +452 -21
- package/dist/lib/services/realtime-session.service.js.map +1 -1
- package/dist/lib/services/realtime-sessions-adapter.js +1 -1
- package/dist/lib/services/realtime-sessions-adapter.js.map +1 -1
- package/dist/lib/services/ui-command-handler.service.d.ts +7 -2
- package/dist/lib/services/ui-command-handler.service.d.ts.map +1 -1
- package/dist/lib/services/ui-command-handler.service.js +6 -2
- package/dist/lib/services/ui-command-handler.service.js.map +1 -1
- package/dist/lib/utils/plan-mode-preference.d.ts +62 -0
- package/dist/lib/utils/plan-mode-preference.d.ts.map +1 -0
- package/dist/lib/utils/plan-mode-preference.js +130 -0
- package/dist/lib/utils/plan-mode-preference.js.map +1 -0
- package/dist/public-api.d.ts +29 -3
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +46 -3
- package/dist/public-api.js.map +1 -1
- package/package.json +30 -26
- package/dist/__tests__/attachment-badge.test.d.ts +0 -2
- package/dist/__tests__/attachment-badge.test.d.ts.map +0 -1
- package/dist/__tests__/attachment-badge.test.js +0 -66
- package/dist/__tests__/attachment-badge.test.js.map +0 -1
- package/dist/__tests__/channel-optional-surface.test.d.ts +0 -2
- package/dist/__tests__/channel-optional-surface.test.d.ts.map +0 -1
- package/dist/__tests__/channel-optional-surface.test.js +0 -53
- package/dist/__tests__/channel-optional-surface.test.js.map +0 -1
- package/dist/__tests__/chat-events.test.d.ts +0 -14
- package/dist/__tests__/chat-events.test.d.ts.map +0 -1
- package/dist/__tests__/chat-events.test.js +0 -109
- package/dist/__tests__/chat-events.test.js.map +0 -1
- package/dist/__tests__/collections-full-view.test.d.ts +0 -2
- package/dist/__tests__/collections-full-view.test.d.ts.map +0 -1
- package/dist/__tests__/collections-full-view.test.js +0 -225
- package/dist/__tests__/collections-full-view.test.js.map +0 -1
- package/dist/__tests__/conversation-cache.test.d.ts +0 -2
- package/dist/__tests__/conversation-cache.test.d.ts.map +0 -1
- package/dist/__tests__/conversation-cache.test.js +0 -233
- package/dist/__tests__/conversation-cache.test.js.map +0 -1
- package/dist/__tests__/conversation-naming.test.d.ts +0 -2
- package/dist/__tests__/conversation-naming.test.d.ts.map +0 -1
- package/dist/__tests__/conversation-naming.test.js +0 -110
- package/dist/__tests__/conversation-naming.test.js.map +0 -1
- package/dist/__tests__/delegation-result-parser.test.d.ts +0 -2
- package/dist/__tests__/delegation-result-parser.test.d.ts.map +0 -1
- package/dist/__tests__/delegation-result-parser.test.js +0 -107
- package/dist/__tests__/delegation-result-parser.test.js.map +0 -1
- package/dist/__tests__/distinct-artifacts.test.d.ts +0 -2
- package/dist/__tests__/distinct-artifacts.test.d.ts.map +0 -1
- package/dist/__tests__/distinct-artifacts.test.js +0 -56
- package/dist/__tests__/distinct-artifacts.test.js.map +0 -1
- package/dist/__tests__/event-wiring.test.d.ts +0 -15
- package/dist/__tests__/event-wiring.test.d.ts.map +0 -1
- package/dist/__tests__/event-wiring.test.js +0 -100
- package/dist/__tests__/event-wiring.test.js.map +0 -1
- package/dist/__tests__/index.test.d.ts +0 -2
- package/dist/__tests__/index.test.d.ts.map +0 -1
- package/dist/__tests__/index.test.js +0 -7
- package/dist/__tests__/index.test.js.map +0 -1
- package/dist/__tests__/narration-template.test.d.ts +0 -2
- package/dist/__tests__/narration-template.test.d.ts.map +0 -1
- package/dist/__tests__/narration-template.test.js +0 -76
- package/dist/__tests__/narration-template.test.js.map +0 -1
- package/dist/__tests__/notification-model.test.d.ts +0 -2
- package/dist/__tests__/notification-model.test.d.ts.map +0 -1
- package/dist/__tests__/notification-model.test.js +0 -68
- package/dist/__tests__/notification-model.test.js.map +0 -1
- package/dist/__tests__/realtime-agent-picker-models.test.d.ts +0 -2
- package/dist/__tests__/realtime-agent-picker-models.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-agent-picker-models.test.js +0 -49
- package/dist/__tests__/realtime-agent-picker-models.test.js.map +0 -1
- package/dist/__tests__/realtime-audio-visuals.test.d.ts +0 -2
- package/dist/__tests__/realtime-audio-visuals.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-audio-visuals.test.js +0 -123
- package/dist/__tests__/realtime-audio-visuals.test.js.map +0 -1
- package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts +0 -2
- package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-delegation-card-cancel.test.js +0 -48
- package/dist/__tests__/realtime-delegation-card-cancel.test.js.map +0 -1
- package/dist/__tests__/realtime-disclosure.test.d.ts +0 -2
- package/dist/__tests__/realtime-disclosure.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-disclosure.test.js +0 -164
- package/dist/__tests__/realtime-disclosure.test.js.map +0 -1
- package/dist/__tests__/realtime-pairing.test.d.ts +0 -2
- package/dist/__tests__/realtime-pairing.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-pairing.test.js +0 -207
- package/dist/__tests__/realtime-pairing.test.js.map +0 -1
- package/dist/__tests__/realtime-review-lifecycle.test.d.ts +0 -2
- package/dist/__tests__/realtime-review-lifecycle.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-review-lifecycle.test.js +0 -154
- package/dist/__tests__/realtime-review-lifecycle.test.js.map +0 -1
- package/dist/__tests__/realtime-session-cancel-usage.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-cancel-usage.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-cancel-usage.test.js +0 -230
- package/dist/__tests__/realtime-session-cancel-usage.test.js.map +0 -1
- package/dist/__tests__/realtime-session-channels.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-channels.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-channels.test.js +0 -252
- package/dist/__tests__/realtime-session-channels.test.js.map +0 -1
- package/dist/__tests__/realtime-session-client-tools.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-client-tools.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-client-tools.test.js +0 -103
- package/dist/__tests__/realtime-session-client-tools.test.js.map +0 -1
- package/dist/__tests__/realtime-session-minimized.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-minimized.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-minimized.test.js +0 -32
- package/dist/__tests__/realtime-session-minimized.test.js.map +0 -1
- package/dist/__tests__/realtime-session-mint.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-mint.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-mint.test.js +0 -69
- package/dist/__tests__/realtime-session-mint.test.js.map +0 -1
- package/dist/__tests__/realtime-session-policy.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-policy.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-policy.test.js +0 -303
- package/dist/__tests__/realtime-session-policy.test.js.map +0 -1
- package/dist/__tests__/realtime-session-review.service.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-review.service.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-review.service.test.js +0 -743
- package/dist/__tests__/realtime-session-review.service.test.js.map +0 -1
- package/dist/__tests__/realtime-session-state.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-state.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-state.test.js +0 -83
- package/dist/__tests__/realtime-session-state.test.js.map +0 -1
- package/dist/__tests__/realtime-session-timeline-card.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-timeline-card.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-timeline-card.test.js +0 -106
- package/dist/__tests__/realtime-session-timeline-card.test.js.map +0 -1
- package/dist/__tests__/realtime-session-timeline.test.d.ts +0 -2
- package/dist/__tests__/realtime-session-timeline.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-session-timeline.test.js +0 -142
- package/dist/__tests__/realtime-session-timeline.test.js.map +0 -1
- package/dist/__tests__/realtime-sessions-adapter.test.d.ts +0 -19
- package/dist/__tests__/realtime-sessions-adapter.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-sessions-adapter.test.js +0 -188
- package/dist/__tests__/realtime-sessions-adapter.test.js.map +0 -1
- package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts +0 -2
- package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-surface-panel-prefs.test.js +0 -100
- package/dist/__tests__/realtime-surface-panel-prefs.test.js.map +0 -1
- package/dist/__tests__/realtime-surface-tabs-model.test.d.ts +0 -2
- package/dist/__tests__/realtime-surface-tabs-model.test.d.ts.map +0 -1
- package/dist/__tests__/realtime-surface-tabs-model.test.js +0 -193
- package/dist/__tests__/realtime-surface-tabs-model.test.js.map +0 -1
- package/dist/__tests__/remote-browser-audio-player.test.d.ts +0 -2
- package/dist/__tests__/remote-browser-audio-player.test.d.ts.map +0 -1
- package/dist/__tests__/remote-browser-audio-player.test.js +0 -137
- package/dist/__tests__/remote-browser-audio-player.test.js.map +0 -1
- package/dist/__tests__/remote-browser-channel.test.d.ts +0 -2
- package/dist/__tests__/remote-browser-channel.test.d.ts.map +0 -1
- package/dist/__tests__/remote-browser-channel.test.js +0 -585
- package/dist/__tests__/remote-browser-channel.test.js.map +0 -1
- package/dist/__tests__/search-service.test.d.ts +0 -2
- package/dist/__tests__/search-service.test.d.ts.map +0 -1
- package/dist/__tests__/search-service.test.js +0 -114
- package/dist/__tests__/search-service.test.js.map +0 -1
- package/dist/__tests__/slot-defaults.test.d.ts +0 -24
- package/dist/__tests__/slot-defaults.test.d.ts.map +0 -1
- package/dist/__tests__/slot-defaults.test.js +0 -63
- package/dist/__tests__/slot-defaults.test.js.map +0 -1
- package/dist/__tests__/toast-service.test.d.ts +0 -2
- package/dist/__tests__/toast-service.test.d.ts.map +0 -1
- package/dist/__tests__/toast-service.test.js +0 -143
- package/dist/__tests__/toast-service.test.js.map +0 -1
- package/dist/__tests__/user-authorization.test.d.ts +0 -2
- package/dist/__tests__/user-authorization.test.d.ts.map +0 -1
- package/dist/__tests__/user-authorization.test.js +0 -97
- package/dist/__tests__/user-authorization.test.js.map +0 -1
- package/dist/__tests__/voice-session-narration.test.d.ts +0 -2
- package/dist/__tests__/voice-session-narration.test.d.ts.map +0 -1
- package/dist/__tests__/voice-session-narration.test.js +0 -609
- package/dist/__tests__/voice-session-narration.test.js.map +0 -1
- package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts +0 -2
- package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts.map +0 -1
- package/dist/__tests__/whiteboard-artifact-viewer.test.js +0 -101
- package/dist/__tests__/whiteboard-artifact-viewer.test.js.map +0 -1
- package/dist/__tests__/whiteboard-channel.test.d.ts +0 -2
- package/dist/__tests__/whiteboard-channel.test.d.ts.map +0 -1
- package/dist/__tests__/whiteboard-channel.test.js +0 -260
- package/dist/__tests__/whiteboard-channel.test.js.map +0 -1
- package/dist/__tests__/whiteboard-restore-state.test.d.ts +0 -2
- package/dist/__tests__/whiteboard-restore-state.test.d.ts.map +0 -1
- package/dist/__tests__/whiteboard-restore-state.test.js +0 -108
- package/dist/__tests__/whiteboard-restore-state.test.js.map +0 -1
- package/dist/lib/components/mention/mention-dropdown.component.d.ts +0 -53
- package/dist/lib/components/mention/mention-dropdown.component.d.ts.map +0 -1
- package/dist/lib/components/mention/mention-dropdown.component.js +0 -276
- package/dist/lib/components/mention/mention-dropdown.component.js.map +0 -1
- package/dist/lib/components/mention/mention-editor.component.d.ts +0 -256
- package/dist/lib/components/mention/mention-editor.component.d.ts.map +0 -1
- package/dist/lib/components/mention/mention-editor.component.js +0 -1233
- package/dist/lib/components/mention/mention-editor.component.js.map +0 -1
- package/dist/lib/components/message/message-input-box.component.d.ts +0 -137
- package/dist/lib/components/message/message-input-box.component.d.ts.map +0 -1
- package/dist/lib/components/message/message-input-box.component.js +0 -321
- package/dist/lib/components/message/message-input-box.component.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"narration-template.test.js","sourceRoot":"","sources":["../../src/__tests__/narration-template.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unit tests for the pure narration-template helpers used by RealtimeSessionService — the DB-driven\n * `{{ progressMessage }}` / `{{ priorNarrations }}` / `{{ updateNumber }}` substitution plus the\n * built-in fallback for deployments that haven't synced the\n * `Realtime Co-Agent - Progress Narration` prompt.\n */\nimport { describe, it, expect } from 'vitest';\nimport {\n BuildNarrationInstructions,\n DefaultNarrationInstructions\n} from '../lib/services/narration-template';\n\ndescribe('DefaultNarrationInstructions', () => {\n it('embeds the progress digest and keeps the first-person + variation rules', () => {\n const out = DefaultNarrationInstructions('Analyzing the request');\n\n expect(out).toContain('Analyzing the request');\n expect(out).toContain('FIRST PERSON');\n expect(out).toContain('VARY the');\n expect(out).toContain('must not be the subject');\n expect(out).toContain('Never repeat');\n });\n\n it('numbers the update and reports no prior narrations on the first update', () => {\n const out = DefaultNarrationInstructions('Fetching', { UpdateNumber: 1 });\n\n expect(out).toContain('update #1');\n expect(out).toContain('first spoken update');\n });\n\n it('chains prior spoken narrations so the model can build on them', () => {\n const out = DefaultNarrationInstructions('Fetching', {\n UpdateNumber: 3,\n PriorNarrations: [\"I'm pulling that up now\", 'Got the first batch']\n });\n\n expect(out).toContain('update #3');\n expect(out).toContain(`- \"I'm pulling that up now\"`);\n expect(out).toContain('- \"Got the first batch\"');\n });\n});\n\ndescribe('BuildNarrationInstructions', () => {\n it('substitutes {{ progressMessage }} (spaced token) everywhere it appears', () => {\n const template = 'Progress: \"{{ progressMessage }}\". Repeat: {{ progressMessage }}.';\n const out = BuildNarrationInstructions(template, 'Fetching records');\n\n expect(out).toBe('Progress: \"Fetching records\". Repeat: Fetching records.');\n });\n\n it('tolerates the no-spaces {{progressMessage}} variant', () => {\n const out = BuildNarrationInstructions('Now: {{progressMessage}}!', 'Running the query');\n\n expect(out).toBe('Now: Running the query!');\n });\n\n it('substitutes a mix of both token variants', () => {\n const out = BuildNarrationInstructions('A {{ progressMessage }} B {{progressMessage}}', 'x');\n\n expect(out).toBe('A x B x');\n });\n\n it('substitutes {{ updateNumber }} and {{ priorNarrations }}', () => {\n const template = 'Update #{{ updateNumber }}. Said before:\\n{{ priorNarrations }}';\n const out = BuildNarrationInstructions(template, 'x', {\n UpdateNumber: 2,\n PriorNarrations: ['First thing I said']\n });\n\n expect(out).toBe('Update #2. Said before:\\n- \"First thing I said\"');\n });\n\n it('renders a friendly \"nothing yet\" for {{ priorNarrations }} on the first update', () => {\n const out = BuildNarrationInstructions('Prior: {{ priorNarrations }}', 'x');\n\n expect(out).toContain('first spoken update');\n });\n\n it('defaults {{ updateNumber }} to 1 when no options are given', () => {\n const out = BuildNarrationInstructions('N={{updateNumber}}', 'x');\n\n expect(out).toBe('N=1');\n });\n\n it('falls back to the built-in wording when the template is null', () => {\n const out = BuildNarrationInstructions(null, 'Analyzing the request', { UpdateNumber: 2 });\n\n expect(out).toBe(DefaultNarrationInstructions('Analyzing the request', { UpdateNumber: 2 }));\n });\n\n it('falls back to the built-in wording when the template is blank', () => {\n const out = BuildNarrationInstructions(' ', 'Analyzing the request');\n\n expect(out).toBe(DefaultNarrationInstructions('Analyzing the request'));\n });\n\n it('returns a template without tokens unchanged (no accidental injection)', () => {\n const out = BuildNarrationInstructions('Say something brief.', 'ignored');\n\n expect(out).toBe('Say something brief.');\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"notification-model.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/notification-model.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { DEFAULT_NOTIFICATION_PREFERENCES } from '../lib/models/notification.model';
|
|
3
|
-
describe('DEFAULT_NOTIFICATION_PREFERENCES', () => {
|
|
4
|
-
it('should have sound disabled by default', () => {
|
|
5
|
-
expect(DEFAULT_NOTIFICATION_PREFERENCES.enableSound).toBe(false);
|
|
6
|
-
});
|
|
7
|
-
it('should have desktop notifications disabled by default', () => {
|
|
8
|
-
expect(DEFAULT_NOTIFICATION_PREFERENCES.enableDesktopNotifications).toBe(false);
|
|
9
|
-
});
|
|
10
|
-
it('should have in-app notifications enabled by default', () => {
|
|
11
|
-
expect(DEFAULT_NOTIFICATION_PREFERENCES.enableInAppNotifications).toBe(true);
|
|
12
|
-
});
|
|
13
|
-
it('should have badges enabled by default', () => {
|
|
14
|
-
expect(DEFAULT_NOTIFICATION_PREFERENCES.showBadges).toBe(true);
|
|
15
|
-
});
|
|
16
|
-
it('should have empty muted conversations list', () => {
|
|
17
|
-
expect(DEFAULT_NOTIFICATION_PREFERENCES.mutedConversations).toEqual([]);
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
describe('Notification types', () => {
|
|
21
|
-
it('should support all notification types', () => {
|
|
22
|
-
const types = ['message', 'artifact', 'agent_process', 'task'];
|
|
23
|
-
expect(types).toHaveLength(4);
|
|
24
|
-
});
|
|
25
|
-
it('should support all priority levels', () => {
|
|
26
|
-
const priorities = ['low', 'normal', 'high', 'urgent'];
|
|
27
|
-
expect(priorities).toHaveLength(4);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
describe('ConversationNotification shape', () => {
|
|
31
|
-
it('should be constructable with required fields', () => {
|
|
32
|
-
const notification = {
|
|
33
|
-
conversationId: 'conv-1',
|
|
34
|
-
unreadMessageCount: 5,
|
|
35
|
-
lastReadMessageTimestamp: null,
|
|
36
|
-
lastMessageTimestamp: new Date(),
|
|
37
|
-
hasNewArtifacts: true,
|
|
38
|
-
hasActiveAgentProcesses: false,
|
|
39
|
-
newArtifactCount: 2,
|
|
40
|
-
activeAgentProcessCount: 0,
|
|
41
|
-
lastNotificationTimestamp: new Date(),
|
|
42
|
-
highestPriority: 'high'
|
|
43
|
-
};
|
|
44
|
-
expect(notification.conversationId).toBe('conv-1');
|
|
45
|
-
expect(notification.unreadMessageCount).toBe(5);
|
|
46
|
-
expect(notification.hasNewArtifacts).toBe(true);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
describe('BadgeConfig shape', () => {
|
|
50
|
-
it('should represent hidden badge', () => {
|
|
51
|
-
const badge = { show: false };
|
|
52
|
-
expect(badge.show).toBe(false);
|
|
53
|
-
expect(badge.count).toBeUndefined();
|
|
54
|
-
});
|
|
55
|
-
it('should represent visible badge with count', () => {
|
|
56
|
-
const badge = {
|
|
57
|
-
show: true,
|
|
58
|
-
count: 3,
|
|
59
|
-
type: 'count',
|
|
60
|
-
priority: 'normal',
|
|
61
|
-
animate: false
|
|
62
|
-
};
|
|
63
|
-
expect(badge.show).toBe(true);
|
|
64
|
-
expect(badge.count).toBe(3);
|
|
65
|
-
expect(badge.type).toBe('count');
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
//# sourceMappingURL=notification-model.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"notification-model.test.js","sourceRoot":"","sources":["../../src/__tests__/notification-model.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect } from 'vitest';\nimport {\n DEFAULT_NOTIFICATION_PREFERENCES\n} from '../lib/models/notification.model';\nimport type {\n NotificationType,\n NotificationPriority,\n ConversationNotification,\n BadgeConfig,\n NotificationPreferences\n} from '../lib/models/notification.model';\n\ndescribe('DEFAULT_NOTIFICATION_PREFERENCES', () => {\n it('should have sound disabled by default', () => {\n expect(DEFAULT_NOTIFICATION_PREFERENCES.enableSound).toBe(false);\n });\n\n it('should have desktop notifications disabled by default', () => {\n expect(DEFAULT_NOTIFICATION_PREFERENCES.enableDesktopNotifications).toBe(false);\n });\n\n it('should have in-app notifications enabled by default', () => {\n expect(DEFAULT_NOTIFICATION_PREFERENCES.enableInAppNotifications).toBe(true);\n });\n\n it('should have badges enabled by default', () => {\n expect(DEFAULT_NOTIFICATION_PREFERENCES.showBadges).toBe(true);\n });\n\n it('should have empty muted conversations list', () => {\n expect(DEFAULT_NOTIFICATION_PREFERENCES.mutedConversations).toEqual([]);\n });\n});\n\ndescribe('Notification types', () => {\n it('should support all notification types', () => {\n const types: NotificationType[] = ['message', 'artifact', 'agent_process', 'task'];\n expect(types).toHaveLength(4);\n });\n\n it('should support all priority levels', () => {\n const priorities: NotificationPriority[] = ['low', 'normal', 'high', 'urgent'];\n expect(priorities).toHaveLength(4);\n });\n});\n\ndescribe('ConversationNotification shape', () => {\n it('should be constructable with required fields', () => {\n const notification: ConversationNotification = {\n conversationId: 'conv-1',\n unreadMessageCount: 5,\n lastReadMessageTimestamp: null,\n lastMessageTimestamp: new Date(),\n hasNewArtifacts: true,\n hasActiveAgentProcesses: false,\n newArtifactCount: 2,\n activeAgentProcessCount: 0,\n lastNotificationTimestamp: new Date(),\n highestPriority: 'high'\n };\n expect(notification.conversationId).toBe('conv-1');\n expect(notification.unreadMessageCount).toBe(5);\n expect(notification.hasNewArtifacts).toBe(true);\n });\n});\n\ndescribe('BadgeConfig shape', () => {\n it('should represent hidden badge', () => {\n const badge: BadgeConfig = { show: false };\n expect(badge.show).toBe(false);\n expect(badge.count).toBeUndefined();\n });\n\n it('should represent visible badge with count', () => {\n const badge: BadgeConfig = {\n show: true,\n count: 3,\n type: 'count',\n priority: 'normal',\n animate: false\n };\n expect(badge.show).toBe(true);\n expect(badge.count).toBe(3);\n expect(badge.type).toBe('count');\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-agent-picker-models.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/realtime-agent-picker-models.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { BuildRealtimeModelOptions, } from '../lib/services/realtime-pairing';
|
|
3
|
-
/**
|
|
4
|
-
* The "Voice model" option builder the picker applies to {@link AIEngineBase}'s cached
|
|
5
|
-
* `Models` — the pure replacement for the previous narrow RunView
|
|
6
|
-
* (`IsActive = 1 AND AIModelType = 'Realtime'`, `OrderBy: 'Name'`). Filter parity with the
|
|
7
|
-
* SQL it replaced is the contract under test: active-only, Realtime-type-only
|
|
8
|
-
* (trim + case-insensitive, matching SQL collation), Name-sorted, ID+Name projection.
|
|
9
|
-
*/
|
|
10
|
-
function model(overrides) {
|
|
11
|
-
return {
|
|
12
|
-
AIModelType: 'Realtime',
|
|
13
|
-
IsActive: true,
|
|
14
|
-
...overrides,
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
describe('BuildRealtimeModelOptions', () => {
|
|
18
|
-
it('keeps only ACTIVE models of type Realtime, projected to {ID, Name}', () => {
|
|
19
|
-
const options = BuildRealtimeModelOptions([
|
|
20
|
-
model({ ID: 'm1', Name: 'Realtime One' }),
|
|
21
|
-
model({ ID: 'm2', Name: 'Inactive Realtime', IsActive: false }),
|
|
22
|
-
model({ ID: 'm3', Name: 'Some LLM', AIModelType: 'LLM' }),
|
|
23
|
-
model({ ID: 'm4', Name: 'Untyped', AIModelType: null }),
|
|
24
|
-
]);
|
|
25
|
-
expect(options).toEqual([{ ID: 'm1', Name: 'Realtime One' }]);
|
|
26
|
-
});
|
|
27
|
-
it('matches the Realtime type trim + case-insensitively (SQL-collation parity)', () => {
|
|
28
|
-
const options = BuildRealtimeModelOptions([
|
|
29
|
-
model({ ID: 'm1', Name: 'A', AIModelType: ' realtime ' }),
|
|
30
|
-
model({ ID: 'm2', Name: 'B', AIModelType: 'REALTIME' }),
|
|
31
|
-
]);
|
|
32
|
-
expect(options.map(o => o.ID)).toEqual(['m1', 'm2']);
|
|
33
|
-
});
|
|
34
|
-
it('sorts by Name ascending (parity with the previous OrderBy)', () => {
|
|
35
|
-
const options = BuildRealtimeModelOptions([
|
|
36
|
-
model({ ID: 'm2', Name: 'Zeta Voice' }),
|
|
37
|
-
model({ ID: 'm1', Name: 'Alpha Voice' }),
|
|
38
|
-
model({ ID: 'm3', Name: 'Mid Voice' }),
|
|
39
|
-
]);
|
|
40
|
-
expect(options.map(o => o.Name)).toEqual(['Alpha Voice', 'Mid Voice', 'Zeta Voice']);
|
|
41
|
-
});
|
|
42
|
-
it('returns an empty list for an empty cache and never mutates the input', () => {
|
|
43
|
-
expect(BuildRealtimeModelOptions([])).toEqual([]);
|
|
44
|
-
const input = [model({ ID: 'm2', Name: 'B' }), model({ ID: 'm1', Name: 'A' })];
|
|
45
|
-
BuildRealtimeModelOptions(input);
|
|
46
|
-
expect(input.map(m => m.ID)).toEqual(['m2', 'm1']); // input order untouched
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
//# sourceMappingURL=realtime-agent-picker-models.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-agent-picker-models.test.js","sourceRoot":"","sources":["../../src/__tests__/realtime-agent-picker-models.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect } from 'vitest';\nimport {\n BuildRealtimeModelOptions,\n VoiceModelCandidate,\n} from '../lib/services/realtime-pairing';\n\n/**\n * The \"Voice model\" option builder the picker applies to {@link AIEngineBase}'s cached\n * `Models` — the pure replacement for the previous narrow RunView\n * (`IsActive = 1 AND AIModelType = 'Realtime'`, `OrderBy: 'Name'`). Filter parity with the\n * SQL it replaced is the contract under test: active-only, Realtime-type-only\n * (trim + case-insensitive, matching SQL collation), Name-sorted, ID+Name projection.\n */\n\nfunction model(overrides: Partial<VoiceModelCandidate> & { ID: string; Name: string }): VoiceModelCandidate {\n return {\n AIModelType: 'Realtime',\n IsActive: true,\n ...overrides,\n };\n}\n\ndescribe('BuildRealtimeModelOptions', () => {\n it('keeps only ACTIVE models of type Realtime, projected to {ID, Name}', () => {\n const options = BuildRealtimeModelOptions([\n model({ ID: 'm1', Name: 'Realtime One' }),\n model({ ID: 'm2', Name: 'Inactive Realtime', IsActive: false }),\n model({ ID: 'm3', Name: 'Some LLM', AIModelType: 'LLM' }),\n model({ ID: 'm4', Name: 'Untyped', AIModelType: null }),\n ]);\n expect(options).toEqual([{ ID: 'm1', Name: 'Realtime One' }]);\n });\n\n it('matches the Realtime type trim + case-insensitively (SQL-collation parity)', () => {\n const options = BuildRealtimeModelOptions([\n model({ ID: 'm1', Name: 'A', AIModelType: ' realtime ' }),\n model({ ID: 'm2', Name: 'B', AIModelType: 'REALTIME' }),\n ]);\n expect(options.map(o => o.ID)).toEqual(['m1', 'm2']);\n });\n\n it('sorts by Name ascending (parity with the previous OrderBy)', () => {\n const options = BuildRealtimeModelOptions([\n model({ ID: 'm2', Name: 'Zeta Voice' }),\n model({ ID: 'm1', Name: 'Alpha Voice' }),\n model({ ID: 'm3', Name: 'Mid Voice' }),\n ]);\n expect(options.map(o => o.Name)).toEqual(['Alpha Voice', 'Mid Voice', 'Zeta Voice']);\n });\n\n it('returns an empty list for an empty cache and never mutates the input', () => {\n expect(BuildRealtimeModelOptions([])).toEqual([]);\n const input = [model({ ID: 'm2', Name: 'B' }), model({ ID: 'm1', Name: 'A' })];\n BuildRealtimeModelOptions(input);\n expect(input.map(m => m.ID)).toEqual(['m2', 'm1']); // input order untouched\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-audio-visuals.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/realtime-audio-visuals.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for the audio-reactive visual smoothing — the framework-free envelope +
|
|
3
|
-
* direction-hysteresis math between raw RealtimeAudioActivity samples and the CSS
|
|
4
|
-
* variables that drive the hero orb / EQ.
|
|
5
|
-
*/
|
|
6
|
-
import { describe, it, expect } from 'vitest';
|
|
7
|
-
import { GateLevel, RealtimeAudioVisualSmoother, SmoothLevel, AUDIO_INPUT_NOISE_GATE, AUDIO_OUTPUT_NOISE_GATE, AUDIO_DIRECTION_HOLD_MS, AUDIO_PRESENCE_FLOOR, AUDIO_VISUAL_BIN_COUNT } from '../lib/components/realtime/realtime-audio-visuals';
|
|
8
|
-
function activity(partial) {
|
|
9
|
-
return { InputLevel: null, OutputLevel: null, InputBins: null, OutputBins: null, ...partial };
|
|
10
|
-
}
|
|
11
|
-
describe('SmoothLevel (attack/decay envelope)', () => {
|
|
12
|
-
it('attacks fast and decays slow (speaker-cone feel, never a strobe)', () => {
|
|
13
|
-
const up = SmoothLevel(0, 1);
|
|
14
|
-
const down = SmoothLevel(1, 0);
|
|
15
|
-
expect(up).toBeGreaterThan(0.4); // fast attack
|
|
16
|
-
expect(down).toBeGreaterThan(0.8); // slow decay — still ringing down
|
|
17
|
-
});
|
|
18
|
-
it('clamps into 0..1 and converges to the target', () => {
|
|
19
|
-
let v = 0;
|
|
20
|
-
for (let i = 0; i < 60; i++) {
|
|
21
|
-
v = SmoothLevel(v, 0.7);
|
|
22
|
-
}
|
|
23
|
-
expect(v).toBeCloseTo(0.7, 1);
|
|
24
|
-
expect(SmoothLevel(0.5, 5)).toBeLessThanOrEqual(1);
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
describe('RealtimeAudioVisualSmoother', () => {
|
|
28
|
-
it('null in → null out (un-metered drivers keep turn-state visuals)', () => {
|
|
29
|
-
expect(new RealtimeAudioVisualSmoother().Next(null, 0)).toBeNull();
|
|
30
|
-
});
|
|
31
|
-
it('produces smoothed levels and a fixed-length bin array', () => {
|
|
32
|
-
const s = new RealtimeAudioVisualSmoother();
|
|
33
|
-
const frame = s.Next(activity({ OutputLevel: 0.8, OutputBins: new Array(9).fill(0.6) }), 0);
|
|
34
|
-
expect(frame).not.toBeNull();
|
|
35
|
-
expect(frame.OutputLevel).toBeGreaterThan(0);
|
|
36
|
-
expect(frame.OutputLevel).toBeLessThan(0.8); // smoothed, not raw
|
|
37
|
-
expect(frame.Bins).toHaveLength(AUDIO_VISUAL_BIN_COUNT);
|
|
38
|
-
});
|
|
39
|
-
it('adopts the louder direction from silence immediately', () => {
|
|
40
|
-
const s = new RealtimeAudioVisualSmoother();
|
|
41
|
-
const frame = s.Next(activity({ OutputLevel: 0.7, InputLevel: 0.1 }), 0);
|
|
42
|
-
expect(frame.Direction).toBe('agent');
|
|
43
|
-
});
|
|
44
|
-
it('holds the direction across syllable gaps (no flicker)', () => {
|
|
45
|
-
const s = new RealtimeAudioVisualSmoother();
|
|
46
|
-
s.Next(activity({ OutputLevel: 0.7 }), 0);
|
|
47
|
-
// Silence shorter than the hold window — direction must persist.
|
|
48
|
-
let frame = s.Next(activity({ OutputLevel: 0 }), AUDIO_DIRECTION_HOLD_MS / 2);
|
|
49
|
-
expect(frame.Direction).toBe('agent');
|
|
50
|
-
// Keep feeding silence until the smoothed level decays under the floor AND the hold expires.
|
|
51
|
-
for (let t = 1; t <= 40; t++) {
|
|
52
|
-
frame = s.Next(activity({ OutputLevel: 0 }), AUDIO_DIRECTION_HOLD_MS / 2 + t * 100);
|
|
53
|
-
}
|
|
54
|
-
expect(frame.Direction).toBe('none');
|
|
55
|
-
});
|
|
56
|
-
it('the other side steals the direction only by clearly dominating', () => {
|
|
57
|
-
const s = new RealtimeAudioVisualSmoother();
|
|
58
|
-
for (let i = 0; i < 10; i++) {
|
|
59
|
-
s.Next(activity({ OutputLevel: 0.6, InputLevel: 0 }), i);
|
|
60
|
-
}
|
|
61
|
-
// Slightly-louder mic does NOT steal…
|
|
62
|
-
let frame = s.Next(activity({ OutputLevel: 0.5, InputLevel: 0.5 }), 100);
|
|
63
|
-
expect(frame.Direction).toBe('agent');
|
|
64
|
-
// …a clearly dominant mic does.
|
|
65
|
-
for (let i = 0; i < 10; i++) {
|
|
66
|
-
frame = s.Next(activity({ OutputLevel: 0.05, InputLevel: 0.9 }), 200 + i);
|
|
67
|
-
}
|
|
68
|
-
expect(frame.Direction).toBe('user');
|
|
69
|
-
});
|
|
70
|
-
it('bins follow the louder direction and decay when no spectrum is available', () => {
|
|
71
|
-
const s = new RealtimeAudioVisualSmoother();
|
|
72
|
-
let frame = s.Next(activity({
|
|
73
|
-
OutputLevel: 0.8, InputLevel: 0.1,
|
|
74
|
-
OutputBins: new Array(9).fill(1), InputBins: new Array(9).fill(0.1)
|
|
75
|
-
}), 0);
|
|
76
|
-
expect(frame.Bins[0]).toBeGreaterThan(0.3); // tracking output's loud spectrum
|
|
77
|
-
const peak = frame.Bins[0];
|
|
78
|
-
frame = s.Next(activity({ OutputLevel: 0.8 }), 16); // bins gone → decay
|
|
79
|
-
expect(frame.Bins[0]).toBeLessThan(peak);
|
|
80
|
-
});
|
|
81
|
-
it('Reset returns to silence + no direction', () => {
|
|
82
|
-
const s = new RealtimeAudioVisualSmoother();
|
|
83
|
-
s.Next(activity({ OutputLevel: 0.9 }), 0);
|
|
84
|
-
s.Reset();
|
|
85
|
-
const frame = s.Next(activity({ OutputLevel: AUDIO_PRESENCE_FLOOR / 10 }), 1);
|
|
86
|
-
expect(frame.Direction).toBe('none');
|
|
87
|
-
expect(frame.OutputLevel).toBeLessThan(0.1);
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
describe('noise gating (idle mic must not animate the visuals)', () => {
|
|
91
|
-
it('GateLevel: at/below the gate is TRUE silence; above it rescales soft-knee to 0..1', () => {
|
|
92
|
-
expect(GateLevel(0, AUDIO_INPUT_NOISE_GATE)).toBe(0);
|
|
93
|
-
expect(GateLevel(AUDIO_INPUT_NOISE_GATE, AUDIO_INPUT_NOISE_GATE)).toBe(0);
|
|
94
|
-
expect(GateLevel(NaN, AUDIO_INPUT_NOISE_GATE)).toBe(0);
|
|
95
|
-
expect(GateLevel(1, AUDIO_INPUT_NOISE_GATE)).toBe(1);
|
|
96
|
-
const mid = GateLevel(0.5, AUDIO_INPUT_NOISE_GATE);
|
|
97
|
-
expect(mid).toBeGreaterThan(0.4);
|
|
98
|
-
expect(mid).toBeLessThan(0.5);
|
|
99
|
-
});
|
|
100
|
-
it('idle mic noise (below the input gate) renders as silence: levels decay, bins decay, direction releases', () => {
|
|
101
|
-
const s = new RealtimeAudioVisualSmoother();
|
|
102
|
-
let frame = s.Next(activity({ InputLevel: 0.6, InputBins: new Array(9).fill(0.8) }), 0);
|
|
103
|
-
expect(frame.Direction).toBe('user');
|
|
104
|
-
// Mic falls back to its idle room-noise floor (non-zero, but under the 0.045 gate) —
|
|
105
|
-
// the analyser keeps reporting it forever; the visuals must come to rest anyway.
|
|
106
|
-
for (let t = 1; t <= 60; t++) {
|
|
107
|
-
frame = s.Next(activity({ InputLevel: 0.03, InputBins: new Array(9).fill(0.3) }), t * 100);
|
|
108
|
-
}
|
|
109
|
-
expect(frame.InputLevel).toBeLessThan(0.01);
|
|
110
|
-
expect(Math.max(...frame.Bins)).toBeLessThan(0.01);
|
|
111
|
-
expect(frame.Direction).toBe('none');
|
|
112
|
-
});
|
|
113
|
-
it('the mic gate sits ABOVE the output gate (playback silence is true zero; mic silence is not)', () => {
|
|
114
|
-
expect(AUDIO_INPUT_NOISE_GATE).toBeGreaterThan(AUDIO_OUTPUT_NOISE_GATE);
|
|
115
|
-
});
|
|
116
|
-
it('real speech still passes the gate untouched in feel (fast attack from rest)', () => {
|
|
117
|
-
const s = new RealtimeAudioVisualSmoother();
|
|
118
|
-
const frame = s.Next(activity({ InputLevel: 0.5, InputBins: new Array(9).fill(0.6) }), 0);
|
|
119
|
-
expect(frame.InputLevel).toBeGreaterThan(0.15);
|
|
120
|
-
expect(frame.Direction).toBe('user');
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
//# sourceMappingURL=realtime-audio-visuals.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-audio-visuals.test.js","sourceRoot":"","sources":["../../src/__tests__/realtime-audio-visuals.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unit tests for the audio-reactive visual smoothing — the framework-free envelope +\n * direction-hysteresis math between raw RealtimeAudioActivity samples and the CSS\n * variables that drive the hero orb / EQ.\n */\nimport { describe, it, expect } from 'vitest';\nimport { RealtimeAudioActivity } from '@memberjunction/ai-realtime-client';\nimport {\n GateLevel,\n RealtimeAudioVisualSmoother,\n SmoothLevel,\n AUDIO_INPUT_NOISE_GATE,\n AUDIO_OUTPUT_NOISE_GATE,\n AUDIO_DIRECTION_HOLD_MS,\n AUDIO_PRESENCE_FLOOR,\n AUDIO_VISUAL_BIN_COUNT\n} from '../lib/components/realtime/realtime-audio-visuals';\n\nfunction activity(partial: Partial<RealtimeAudioActivity>): RealtimeAudioActivity {\n return { InputLevel: null, OutputLevel: null, InputBins: null, OutputBins: null, ...partial };\n}\n\ndescribe('SmoothLevel (attack/decay envelope)', () => {\n it('attacks fast and decays slow (speaker-cone feel, never a strobe)', () => {\n const up = SmoothLevel(0, 1);\n const down = SmoothLevel(1, 0);\n expect(up).toBeGreaterThan(0.4); // fast attack\n expect(down).toBeGreaterThan(0.8); // slow decay — still ringing down\n });\n\n it('clamps into 0..1 and converges to the target', () => {\n let v = 0;\n for (let i = 0; i < 60; i++) {\n v = SmoothLevel(v, 0.7);\n }\n expect(v).toBeCloseTo(0.7, 1);\n expect(SmoothLevel(0.5, 5)).toBeLessThanOrEqual(1);\n });\n});\n\ndescribe('RealtimeAudioVisualSmoother', () => {\n it('null in → null out (un-metered drivers keep turn-state visuals)', () => {\n expect(new RealtimeAudioVisualSmoother().Next(null, 0)).toBeNull();\n });\n\n it('produces smoothed levels and a fixed-length bin array', () => {\n const s = new RealtimeAudioVisualSmoother();\n const frame = s.Next(activity({ OutputLevel: 0.8, OutputBins: new Array(9).fill(0.6) }), 0);\n expect(frame).not.toBeNull();\n expect(frame!.OutputLevel).toBeGreaterThan(0);\n expect(frame!.OutputLevel).toBeLessThan(0.8); // smoothed, not raw\n expect(frame!.Bins).toHaveLength(AUDIO_VISUAL_BIN_COUNT);\n });\n\n it('adopts the louder direction from silence immediately', () => {\n const s = new RealtimeAudioVisualSmoother();\n const frame = s.Next(activity({ OutputLevel: 0.7, InputLevel: 0.1 }), 0);\n expect(frame!.Direction).toBe('agent');\n });\n\n it('holds the direction across syllable gaps (no flicker)', () => {\n const s = new RealtimeAudioVisualSmoother();\n s.Next(activity({ OutputLevel: 0.7 }), 0);\n // Silence shorter than the hold window — direction must persist.\n let frame = s.Next(activity({ OutputLevel: 0 }), AUDIO_DIRECTION_HOLD_MS / 2);\n expect(frame!.Direction).toBe('agent');\n // Keep feeding silence until the smoothed level decays under the floor AND the hold expires.\n for (let t = 1; t <= 40; t++) {\n frame = s.Next(activity({ OutputLevel: 0 }), AUDIO_DIRECTION_HOLD_MS / 2 + t * 100);\n }\n expect(frame!.Direction).toBe('none');\n });\n\n it('the other side steals the direction only by clearly dominating', () => {\n const s = new RealtimeAudioVisualSmoother();\n for (let i = 0; i < 10; i++) {\n s.Next(activity({ OutputLevel: 0.6, InputLevel: 0 }), i);\n }\n // Slightly-louder mic does NOT steal…\n let frame = s.Next(activity({ OutputLevel: 0.5, InputLevel: 0.5 }), 100);\n expect(frame!.Direction).toBe('agent');\n // …a clearly dominant mic does.\n for (let i = 0; i < 10; i++) {\n frame = s.Next(activity({ OutputLevel: 0.05, InputLevel: 0.9 }), 200 + i);\n }\n expect(frame!.Direction).toBe('user');\n });\n\n it('bins follow the louder direction and decay when no spectrum is available', () => {\n const s = new RealtimeAudioVisualSmoother();\n let frame = s.Next(activity({\n OutputLevel: 0.8, InputLevel: 0.1,\n OutputBins: new Array(9).fill(1), InputBins: new Array(9).fill(0.1)\n }), 0);\n expect(frame!.Bins[0]).toBeGreaterThan(0.3); // tracking output's loud spectrum\n const peak = frame!.Bins[0];\n frame = s.Next(activity({ OutputLevel: 0.8 }), 16); // bins gone → decay\n expect(frame!.Bins[0]).toBeLessThan(peak);\n });\n\n it('Reset returns to silence + no direction', () => {\n const s = new RealtimeAudioVisualSmoother();\n s.Next(activity({ OutputLevel: 0.9 }), 0);\n s.Reset();\n const frame = s.Next(activity({ OutputLevel: AUDIO_PRESENCE_FLOOR / 10 }), 1);\n expect(frame!.Direction).toBe('none');\n expect(frame!.OutputLevel).toBeLessThan(0.1);\n });\n});\n\ndescribe('noise gating (idle mic must not animate the visuals)', () => {\n it('GateLevel: at/below the gate is TRUE silence; above it rescales soft-knee to 0..1', () => {\n expect(GateLevel(0, AUDIO_INPUT_NOISE_GATE)).toBe(0);\n expect(GateLevel(AUDIO_INPUT_NOISE_GATE, AUDIO_INPUT_NOISE_GATE)).toBe(0);\n expect(GateLevel(NaN, AUDIO_INPUT_NOISE_GATE)).toBe(0);\n expect(GateLevel(1, AUDIO_INPUT_NOISE_GATE)).toBe(1);\n const mid = GateLevel(0.5, AUDIO_INPUT_NOISE_GATE);\n expect(mid).toBeGreaterThan(0.4);\n expect(mid).toBeLessThan(0.5);\n });\n\n it('idle mic noise (below the input gate) renders as silence: levels decay, bins decay, direction releases', () => {\n const s = new RealtimeAudioVisualSmoother();\n let frame = s.Next(activity({ InputLevel: 0.6, InputBins: new Array(9).fill(0.8) }), 0)!;\n expect(frame.Direction).toBe('user');\n\n // Mic falls back to its idle room-noise floor (non-zero, but under the 0.045 gate) —\n // the analyser keeps reporting it forever; the visuals must come to rest anyway.\n for (let t = 1; t <= 60; t++) {\n frame = s.Next(activity({ InputLevel: 0.03, InputBins: new Array(9).fill(0.3) }), t * 100)!;\n }\n expect(frame.InputLevel).toBeLessThan(0.01);\n expect(Math.max(...frame.Bins)).toBeLessThan(0.01);\n expect(frame.Direction).toBe('none');\n });\n\n it('the mic gate sits ABOVE the output gate (playback silence is true zero; mic silence is not)', () => {\n expect(AUDIO_INPUT_NOISE_GATE).toBeGreaterThan(AUDIO_OUTPUT_NOISE_GATE);\n });\n\n it('real speech still passes the gate untouched in feel (fast attack from rest)', () => {\n const s = new RealtimeAudioVisualSmoother();\n const frame = s.Next(activity({ InputLevel: 0.5, InputBins: new Array(9).fill(0.6) }), 0)!;\n expect(frame.InputLevel).toBeGreaterThan(0.15);\n expect(frame.Direction).toBe('user');\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-delegation-card-cancel.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/realtime-delegation-card-cancel.test.ts"],"names":[],"mappings":"AAEA,OAAO,mBAAmB,CAAC"}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
// Angular components in this package are partial-compiled — load the JIT compiler first
|
|
2
|
-
// (same convention as the whiteboard component suites in this node test environment).
|
|
3
|
-
import '@angular/compiler';
|
|
4
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
5
|
-
import { RealtimeDelegationCardComponent } from '../lib/components/realtime/realtime-delegation-card.component';
|
|
6
|
-
/**
|
|
7
|
-
* The WORKING card's ✕ cancel affordance — the first hop of the explicit-cancel event
|
|
8
|
-
* chain (card ✕ → thread re-emit → overlay → `RealtimeSessionService.CancelDelegation`).
|
|
9
|
-
* Class-level tests (no TestBed): the card's emission contract is what the chain depends
|
|
10
|
-
* on — the thread/overlay hops are template re-emits ((CancelRequested)="….emit($event)").
|
|
11
|
-
*/
|
|
12
|
-
function workingCard(overrides = {}) {
|
|
13
|
-
return {
|
|
14
|
-
CallID: 'call-1',
|
|
15
|
-
AgentName: 'Sage',
|
|
16
|
-
LatestMessage: 'Looking things up',
|
|
17
|
-
LatestStep: 'prompt_execution',
|
|
18
|
-
Done: false,
|
|
19
|
-
Success: false,
|
|
20
|
-
StartedAt: Date.now(),
|
|
21
|
-
...overrides,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
/** A minimal MouseEvent stand-in whose stopPropagation is observable. */
|
|
25
|
-
function fakeClick() {
|
|
26
|
-
return { stopPropagation: vi.fn() };
|
|
27
|
-
}
|
|
28
|
-
describe('RealtimeDelegationCardComponent — ✕ cancel affordance (explicit user intent)', () => {
|
|
29
|
-
it('emits CancelRequested with the call id for a WORKING card and stops propagation', () => {
|
|
30
|
-
const component = new RealtimeDelegationCardComponent();
|
|
31
|
-
component.Card = workingCard({ CallID: 'call-9' });
|
|
32
|
-
const emitted = [];
|
|
33
|
-
component.CancelRequested.subscribe((id) => emitted.push(id));
|
|
34
|
-
const event = fakeClick();
|
|
35
|
-
component.CancelWork(event);
|
|
36
|
-
expect(emitted).toEqual(['call-9']);
|
|
37
|
-
expect(event.stopPropagation).toHaveBeenCalled();
|
|
38
|
-
});
|
|
39
|
-
it('does NOT emit for a DONE card (the affordance only exists while work runs)', () => {
|
|
40
|
-
const component = new RealtimeDelegationCardComponent();
|
|
41
|
-
component.Card = workingCard({ Done: true, Success: true, Result: 'all set' });
|
|
42
|
-
const emitted = [];
|
|
43
|
-
component.CancelRequested.subscribe((id) => emitted.push(id));
|
|
44
|
-
component.CancelWork(fakeClick());
|
|
45
|
-
expect(emitted).toEqual([]);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
//# sourceMappingURL=realtime-delegation-card-cancel.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-delegation-card-cancel.test.js","sourceRoot":"","sources":["../../src/__tests__/realtime-delegation-card-cancel.test.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,sFAAsF;AACtF,OAAO,mBAAmB,CAAC;AAC3B,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,+BAA+B,EAAE,MAAM,+DAA+D,CAAC;AAGhH;;;;;GAKG;AAEH,SAAS,WAAW,CAAC,YAA+C,EAAE;IACpE,OAAO;QACL,MAAM,EAAE,QAAQ;QAChB,SAAS,EAAE,MAAM;QACjB,aAAa,EAAE,mBAAmB;QAClC,UAAU,EAAE,kBAAkB;QAC9B,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,KAAK;QACd,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,yEAAyE;AACzE,SAAS,SAAS;IAChB,OAAO,EAAE,eAAe,EAAE,EAAE,CAAC,EAAE,EAAE,EAA2E,CAAC;AAC/G,CAAC;AAED,QAAQ,CAAC,8EAA8E,EAAE,GAAG,EAAE;IAC5F,EAAE,CAAC,iFAAiF,EAAE,GAAG,EAAE;QACzF,MAAM,SAAS,GAAG,IAAI,+BAA+B,EAAE,CAAC;QACxD,SAAS,CAAC,IAAI,GAAG,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QACnD,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,SAAS,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAEtE,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;QAC1B,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAE5B,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,GAAG,EAAE;QACpF,MAAM,SAAS,GAAG,IAAI,+BAA+B,EAAE,CAAC;QACxD,SAAS,CAAC,IAAI,GAAG,WAAW,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QAC/E,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,SAAS,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAEtE,SAAS,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC;QAElC,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["// Angular components in this package are partial-compiled — load the JIT compiler first\n// (same convention as the whiteboard component suites in this node test environment).\nimport '@angular/compiler';\nimport { describe, it, expect, vi } from 'vitest';\nimport { RealtimeDelegationCardComponent } from '../lib/components/realtime/realtime-delegation-card.component';\nimport { RealtimeDelegationCardVM } from '../lib/components/realtime/realtime-session-state';\n\n/**\n * The WORKING card's ✕ cancel affordance — the first hop of the explicit-cancel event\n * chain (card ✕ → thread re-emit → overlay → `RealtimeSessionService.CancelDelegation`).\n * Class-level tests (no TestBed): the card's emission contract is what the chain depends\n * on — the thread/overlay hops are template re-emits ((CancelRequested)=\"….emit($event)\").\n */\n\nfunction workingCard(overrides: Partial<RealtimeDelegationCardVM> = {}): RealtimeDelegationCardVM {\n return {\n CallID: 'call-1',\n AgentName: 'Sage',\n LatestMessage: 'Looking things up',\n LatestStep: 'prompt_execution',\n Done: false,\n Success: false,\n StartedAt: Date.now(),\n ...overrides,\n };\n}\n\n/** A minimal MouseEvent stand-in whose stopPropagation is observable. */\nfunction fakeClick(): MouseEvent & { stopPropagation: ReturnType<typeof vi.fn> } {\n return { stopPropagation: vi.fn() } as unknown as MouseEvent & { stopPropagation: ReturnType<typeof vi.fn> };\n}\n\ndescribe('RealtimeDelegationCardComponent — ✕ cancel affordance (explicit user intent)', () => {\n it('emits CancelRequested with the call id for a WORKING card and stops propagation', () => {\n const component = new RealtimeDelegationCardComponent();\n component.Card = workingCard({ CallID: 'call-9' });\n const emitted: string[] = [];\n component.CancelRequested.subscribe((id: string) => emitted.push(id));\n\n const event = fakeClick();\n component.CancelWork(event);\n\n expect(emitted).toEqual(['call-9']);\n expect(event.stopPropagation).toHaveBeenCalled();\n });\n\n it('does NOT emit for a DONE card (the affordance only exists while work runs)', () => {\n const component = new RealtimeDelegationCardComponent();\n component.Card = workingCard({ Done: true, Success: true, Result: 'all set' });\n const emitted: string[] = [];\n component.CancelRequested.subscribe((id: string) => emitted.push(id));\n\n component.CancelWork(fakeClick());\n\n expect(emitted).toEqual([]);\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-disclosure.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/realtime-disclosure.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for the realtime overlay's PROGRESSIVE DISCLOSURE engine — the
|
|
3
|
-
* framework-free levels/milestones model behind the pure-audio-first call UX
|
|
4
|
-
* (persisted per-user via UserInfoEngine under `mj.realtimeVoice.uxMilestones.v1`).
|
|
5
|
-
*/
|
|
6
|
-
import { describe, it, expect } from 'vitest';
|
|
7
|
-
import { ClampUxLevel, DefaultUxMilestones, EffectiveBaseLevel, ParseUxMilestones, RatchetedMilestones, RealtimeDisclosureModel, SerializeUxMilestones, REALTIME_UX_MAX_LEVEL, REALTIME_UX_PREF_KEY, REALTIME_UX_PRO_CALLS } from '../lib/components/realtime/realtime-disclosure';
|
|
8
|
-
describe('pref key', () => {
|
|
9
|
-
it('uses the versioned mj.* naming convention', () => {
|
|
10
|
-
expect(REALTIME_UX_PREF_KEY).toBe('mj.realtimeVoice.uxMilestones.v1');
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
describe('ClampUxLevel', () => {
|
|
14
|
-
it('passes valid levels through and clamps the band edges', () => {
|
|
15
|
-
expect(ClampUxLevel(0)).toBe(0);
|
|
16
|
-
expect(ClampUxLevel(3)).toBe(3);
|
|
17
|
-
expect(ClampUxLevel(-2)).toBe(0);
|
|
18
|
-
expect(ClampUxLevel(9)).toBe(REALTIME_UX_MAX_LEVEL);
|
|
19
|
-
});
|
|
20
|
-
it('rounds fractional and zeroes non-finite levels', () => {
|
|
21
|
-
expect(ClampUxLevel(2.6)).toBe(3);
|
|
22
|
-
expect(ClampUxLevel(NaN)).toBe(0);
|
|
23
|
-
expect(ClampUxLevel(Infinity)).toBe(0);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
describe('ParseUxMilestones / SerializeUxMilestones', () => {
|
|
27
|
-
it('round-trips milestones', () => {
|
|
28
|
-
const m = { Level: 3, Calls: 7, Density: 'pro' };
|
|
29
|
-
expect(ParseUxMilestones(SerializeUxMilestones(m))).toEqual(m);
|
|
30
|
-
});
|
|
31
|
-
it.each([
|
|
32
|
-
['missing', undefined],
|
|
33
|
-
['null', null],
|
|
34
|
-
['blank', ''],
|
|
35
|
-
['malformed JSON', '{level:'],
|
|
36
|
-
['non-object', '"3"'],
|
|
37
|
-
['JSON null', 'null']
|
|
38
|
-
])('falls back to defaults (never throws) for %s', (_label, raw) => {
|
|
39
|
-
expect(ParseUxMilestones(raw)).toEqual(DefaultUxMilestones());
|
|
40
|
-
});
|
|
41
|
-
it('repairs field-wise: bad level/calls/density fall back individually', () => {
|
|
42
|
-
expect(ParseUxMilestones('{"level":"high","calls":2,"density":"standard"}'))
|
|
43
|
-
.toEqual({ Level: 0, Calls: 2, Density: 'standard' });
|
|
44
|
-
expect(ParseUxMilestones('{"level":3,"calls":-4,"density":"ultra"}'))
|
|
45
|
-
.toEqual({ Level: 3, Calls: 0, Density: 'auto' });
|
|
46
|
-
expect(ParseUxMilestones('{"level":99,"calls":1.7,"density":"simple"}'))
|
|
47
|
-
.toEqual({ Level: REALTIME_UX_MAX_LEVEL, Calls: 2, Density: 'simple' });
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
describe('EffectiveBaseLevel', () => {
|
|
51
|
-
it('day one: a brand-new user starts at PURE AUDIO (level 0)', () => {
|
|
52
|
-
expect(EffectiveBaseLevel(DefaultUxMilestones())).toBe(0);
|
|
53
|
-
});
|
|
54
|
-
it('auto density follows the earned ratchet', () => {
|
|
55
|
-
expect(EffectiveBaseLevel({ Level: 2, Calls: 1, Density: 'auto' })).toBe(2);
|
|
56
|
-
});
|
|
57
|
-
it(`auto density promotes to the full console after ${REALTIME_UX_PRO_CALLS} completed calls`, () => {
|
|
58
|
-
expect(EffectiveBaseLevel({ Level: 1, Calls: REALTIME_UX_PRO_CALLS, Density: 'auto' })).toBe(REALTIME_UX_MAX_LEVEL);
|
|
59
|
-
expect(EffectiveBaseLevel({ Level: 1, Calls: REALTIME_UX_PRO_CALLS - 1, Density: 'auto' })).toBe(1);
|
|
60
|
-
});
|
|
61
|
-
it('manual density overrides win outright (the escape hatch)', () => {
|
|
62
|
-
const earned = { Level: 4, Calls: 10, Density: 'simple' };
|
|
63
|
-
expect(EffectiveBaseLevel(earned)).toBe(0);
|
|
64
|
-
expect(EffectiveBaseLevel({ ...earned, Density: 'standard' })).toBe(2);
|
|
65
|
-
expect(EffectiveBaseLevel({ Level: 0, Calls: 0, Density: 'pro' })).toBe(REALTIME_UX_MAX_LEVEL);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
describe('RatchetedMilestones (call end)', () => {
|
|
69
|
-
it('increments calls and ratchets the level to the session peak', () => {
|
|
70
|
-
expect(RatchetedMilestones({ Level: 1, Calls: 1, Density: 'auto' }, 3))
|
|
71
|
-
.toEqual({ Level: 3, Calls: 2, Density: 'auto' });
|
|
72
|
-
});
|
|
73
|
-
it('a completed first call always earns at least level 1 (text next time)', () => {
|
|
74
|
-
expect(RatchetedMilestones(DefaultUxMilestones(), 0))
|
|
75
|
-
.toEqual({ Level: 1, Calls: 1, Density: 'auto' });
|
|
76
|
-
});
|
|
77
|
-
it('NEVER lowers the earned level (ratchet)', () => {
|
|
78
|
-
expect(RatchetedMilestones({ Level: 4, Calls: 5, Density: 'auto' }, 0).Level).toBe(4);
|
|
79
|
-
});
|
|
80
|
-
it('preserves the density override', () => {
|
|
81
|
-
expect(RatchetedMilestones({ Level: 0, Calls: 0, Density: 'simple' }, 2).Density).toBe('simple');
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
describe('RealtimeDisclosureModel', () => {
|
|
85
|
-
it('Load resets the session level to the effective base', () => {
|
|
86
|
-
const m = new RealtimeDisclosureModel();
|
|
87
|
-
m.Load(SerializeUxMilestones({ Level: 2, Calls: 1, Density: 'auto' }));
|
|
88
|
-
expect(m.SessionLevel).toBe(2);
|
|
89
|
-
expect(m.ShowThread).toBe(true);
|
|
90
|
-
expect(m.ShowComposer).toBe(true);
|
|
91
|
-
expect(m.ShowPanel).toBe(true);
|
|
92
|
-
expect(m.PowerLevel).toBe(false);
|
|
93
|
-
});
|
|
94
|
-
it('day one exposes NOTHING beyond pure audio', () => {
|
|
95
|
-
const m = new RealtimeDisclosureModel();
|
|
96
|
-
m.Load(null);
|
|
97
|
-
expect(m.SessionLevel).toBe(0);
|
|
98
|
-
expect(m.ShowThread).toBe(false);
|
|
99
|
-
expect(m.ShowComposer).toBe(false);
|
|
100
|
-
expect(m.ShowPanel).toBe(false);
|
|
101
|
-
expect(m.ShowGear).toBe(false);
|
|
102
|
-
});
|
|
103
|
-
it('Raise only raises — and reports whether anything changed', () => {
|
|
104
|
-
const m = new RealtimeDisclosureModel();
|
|
105
|
-
m.Load(null);
|
|
106
|
-
expect(m.Raise('engaged')).toBe(true);
|
|
107
|
-
expect(m.SessionLevel).toBe(2);
|
|
108
|
-
expect(m.Raise('text')).toBe(false); // lower event after a higher one: no-op
|
|
109
|
-
expect(m.SessionLevel).toBe(2);
|
|
110
|
-
expect(m.Raise('channel')).toBe(true);
|
|
111
|
-
expect(m.SessionLevel).toBe(3);
|
|
112
|
-
});
|
|
113
|
-
it('emits Changed$ on raises that change the level (and not on no-ops)', () => {
|
|
114
|
-
const m = new RealtimeDisclosureModel();
|
|
115
|
-
m.Load(null);
|
|
116
|
-
let emissions = 0;
|
|
117
|
-
m.Changed$.subscribe(() => emissions++);
|
|
118
|
-
m.Raise('text');
|
|
119
|
-
m.Raise('text');
|
|
120
|
-
expect(emissions).toBe(1);
|
|
121
|
-
});
|
|
122
|
-
it('BeginSession resets the volatile level back to the base', () => {
|
|
123
|
-
const m = new RealtimeDisclosureModel();
|
|
124
|
-
m.Load(null);
|
|
125
|
-
m.Raise('channel');
|
|
126
|
-
m.BeginSession();
|
|
127
|
-
expect(m.SessionLevel).toBe(0);
|
|
128
|
-
});
|
|
129
|
-
it('ReturnToPureAudio steps the volatile level back to 0 WITHOUT touching the ratchet', () => {
|
|
130
|
-
const m = new RealtimeDisclosureModel();
|
|
131
|
-
m.Load(SerializeUxMilestones({ Level: 2, Calls: 1, Density: 'auto' }));
|
|
132
|
-
m.Raise('power');
|
|
133
|
-
let emissions = 0;
|
|
134
|
-
m.Changed$.subscribe(() => emissions++);
|
|
135
|
-
m.ReturnToPureAudio();
|
|
136
|
-
expect(m.SessionLevel).toBe(0);
|
|
137
|
-
expect(m.ShowThread).toBe(false);
|
|
138
|
-
expect(m.Milestones.Level).toBe(2); // the cross-session ratchet is untouched
|
|
139
|
-
m.ReturnToPureAudio(); // already there — no extra emission
|
|
140
|
-
expect(emissions).toBe(1);
|
|
141
|
-
// the user can step back up afterward — the door swings both ways
|
|
142
|
-
expect(m.Raise('text')).toBe(true);
|
|
143
|
-
expect(m.SessionLevel).toBe(1);
|
|
144
|
-
});
|
|
145
|
-
it('SetDensity applies immediately — simple retracts even a mid-call console', () => {
|
|
146
|
-
const m = new RealtimeDisclosureModel();
|
|
147
|
-
m.Load(SerializeUxMilestones({ Level: 4, Calls: 9, Density: 'auto' }));
|
|
148
|
-
expect(m.SessionLevel).toBe(4);
|
|
149
|
-
m.SetDensity('simple');
|
|
150
|
-
expect(m.SessionLevel).toBe(0);
|
|
151
|
-
expect(m.Milestones.Density).toBe('simple');
|
|
152
|
-
m.SetDensity('pro');
|
|
153
|
-
expect(m.SessionLevel).toBe(REALTIME_UX_MAX_LEVEL);
|
|
154
|
-
});
|
|
155
|
-
it('RatchetOnSessionEnd replaces milestones and returns the persistable payload', () => {
|
|
156
|
-
const m = new RealtimeDisclosureModel();
|
|
157
|
-
m.Load(null);
|
|
158
|
-
m.Raise('channel');
|
|
159
|
-
const serialized = m.RatchetOnSessionEnd();
|
|
160
|
-
expect(m.Milestones).toEqual({ Level: 3, Calls: 1, Density: 'auto' });
|
|
161
|
-
expect(ParseUxMilestones(serialized)).toEqual(m.Milestones);
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
//# sourceMappingURL=realtime-disclosure.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-disclosure.test.js","sourceRoot":"","sources":["../../src/__tests__/realtime-disclosure.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unit tests for the realtime overlay's PROGRESSIVE DISCLOSURE engine — the\n * framework-free levels/milestones model behind the pure-audio-first call UX\n * (persisted per-user via UserInfoEngine under `mj.realtimeVoice.uxMilestones.v1`).\n */\nimport { describe, it, expect } from 'vitest';\nimport {\n ClampUxLevel,\n DefaultUxMilestones,\n EffectiveBaseLevel,\n ParseUxMilestones,\n RatchetedMilestones,\n RealtimeDisclosureModel,\n SerializeUxMilestones,\n REALTIME_UX_MAX_LEVEL,\n REALTIME_UX_PREF_KEY,\n REALTIME_UX_PRO_CALLS\n} from '../lib/components/realtime/realtime-disclosure';\n\ndescribe('pref key', () => {\n it('uses the versioned mj.* naming convention', () => {\n expect(REALTIME_UX_PREF_KEY).toBe('mj.realtimeVoice.uxMilestones.v1');\n });\n});\n\ndescribe('ClampUxLevel', () => {\n it('passes valid levels through and clamps the band edges', () => {\n expect(ClampUxLevel(0)).toBe(0);\n expect(ClampUxLevel(3)).toBe(3);\n expect(ClampUxLevel(-2)).toBe(0);\n expect(ClampUxLevel(9)).toBe(REALTIME_UX_MAX_LEVEL);\n });\n\n it('rounds fractional and zeroes non-finite levels', () => {\n expect(ClampUxLevel(2.6)).toBe(3);\n expect(ClampUxLevel(NaN)).toBe(0);\n expect(ClampUxLevel(Infinity)).toBe(0);\n });\n});\n\ndescribe('ParseUxMilestones / SerializeUxMilestones', () => {\n it('round-trips milestones', () => {\n const m = { Level: 3, Calls: 7, Density: 'pro' as const };\n expect(ParseUxMilestones(SerializeUxMilestones(m))).toEqual(m);\n });\n\n it.each([\n ['missing', undefined],\n ['null', null],\n ['blank', ''],\n ['malformed JSON', '{level:'],\n ['non-object', '\"3\"'],\n ['JSON null', 'null']\n ])('falls back to defaults (never throws) for %s', (_label, raw) => {\n expect(ParseUxMilestones(raw as string | null | undefined)).toEqual(DefaultUxMilestones());\n });\n\n it('repairs field-wise: bad level/calls/density fall back individually', () => {\n expect(ParseUxMilestones('{\"level\":\"high\",\"calls\":2,\"density\":\"standard\"}'))\n .toEqual({ Level: 0, Calls: 2, Density: 'standard' });\n expect(ParseUxMilestones('{\"level\":3,\"calls\":-4,\"density\":\"ultra\"}'))\n .toEqual({ Level: 3, Calls: 0, Density: 'auto' });\n expect(ParseUxMilestones('{\"level\":99,\"calls\":1.7,\"density\":\"simple\"}'))\n .toEqual({ Level: REALTIME_UX_MAX_LEVEL, Calls: 2, Density: 'simple' });\n });\n});\n\ndescribe('EffectiveBaseLevel', () => {\n it('day one: a brand-new user starts at PURE AUDIO (level 0)', () => {\n expect(EffectiveBaseLevel(DefaultUxMilestones())).toBe(0);\n });\n\n it('auto density follows the earned ratchet', () => {\n expect(EffectiveBaseLevel({ Level: 2, Calls: 1, Density: 'auto' })).toBe(2);\n });\n\n it(`auto density promotes to the full console after ${REALTIME_UX_PRO_CALLS} completed calls`, () => {\n expect(EffectiveBaseLevel({ Level: 1, Calls: REALTIME_UX_PRO_CALLS, Density: 'auto' })).toBe(REALTIME_UX_MAX_LEVEL);\n expect(EffectiveBaseLevel({ Level: 1, Calls: REALTIME_UX_PRO_CALLS - 1, Density: 'auto' })).toBe(1);\n });\n\n it('manual density overrides win outright (the escape hatch)', () => {\n const earned = { Level: 4, Calls: 10, Density: 'simple' as const };\n expect(EffectiveBaseLevel(earned)).toBe(0);\n expect(EffectiveBaseLevel({ ...earned, Density: 'standard' })).toBe(2);\n expect(EffectiveBaseLevel({ Level: 0, Calls: 0, Density: 'pro' })).toBe(REALTIME_UX_MAX_LEVEL);\n });\n});\n\ndescribe('RatchetedMilestones (call end)', () => {\n it('increments calls and ratchets the level to the session peak', () => {\n expect(RatchetedMilestones({ Level: 1, Calls: 1, Density: 'auto' }, 3))\n .toEqual({ Level: 3, Calls: 2, Density: 'auto' });\n });\n\n it('a completed first call always earns at least level 1 (text next time)', () => {\n expect(RatchetedMilestones(DefaultUxMilestones(), 0))\n .toEqual({ Level: 1, Calls: 1, Density: 'auto' });\n });\n\n it('NEVER lowers the earned level (ratchet)', () => {\n expect(RatchetedMilestones({ Level: 4, Calls: 5, Density: 'auto' }, 0).Level).toBe(4);\n });\n\n it('preserves the density override', () => {\n expect(RatchetedMilestones({ Level: 0, Calls: 0, Density: 'simple' }, 2).Density).toBe('simple');\n });\n});\n\ndescribe('RealtimeDisclosureModel', () => {\n it('Load resets the session level to the effective base', () => {\n const m = new RealtimeDisclosureModel();\n m.Load(SerializeUxMilestones({ Level: 2, Calls: 1, Density: 'auto' }));\n expect(m.SessionLevel).toBe(2);\n expect(m.ShowThread).toBe(true);\n expect(m.ShowComposer).toBe(true);\n expect(m.ShowPanel).toBe(true);\n expect(m.PowerLevel).toBe(false);\n });\n\n it('day one exposes NOTHING beyond pure audio', () => {\n const m = new RealtimeDisclosureModel();\n m.Load(null);\n expect(m.SessionLevel).toBe(0);\n expect(m.ShowThread).toBe(false);\n expect(m.ShowComposer).toBe(false);\n expect(m.ShowPanel).toBe(false);\n expect(m.ShowGear).toBe(false);\n });\n\n it('Raise only raises — and reports whether anything changed', () => {\n const m = new RealtimeDisclosureModel();\n m.Load(null);\n expect(m.Raise('engaged')).toBe(true);\n expect(m.SessionLevel).toBe(2);\n expect(m.Raise('text')).toBe(false); // lower event after a higher one: no-op\n expect(m.SessionLevel).toBe(2);\n expect(m.Raise('channel')).toBe(true);\n expect(m.SessionLevel).toBe(3);\n });\n\n it('emits Changed$ on raises that change the level (and not on no-ops)', () => {\n const m = new RealtimeDisclosureModel();\n m.Load(null);\n let emissions = 0;\n m.Changed$.subscribe(() => emissions++);\n m.Raise('text');\n m.Raise('text');\n expect(emissions).toBe(1);\n });\n\n it('BeginSession resets the volatile level back to the base', () => {\n const m = new RealtimeDisclosureModel();\n m.Load(null);\n m.Raise('channel');\n m.BeginSession();\n expect(m.SessionLevel).toBe(0);\n });\n\n it('ReturnToPureAudio steps the volatile level back to 0 WITHOUT touching the ratchet', () => {\n const m = new RealtimeDisclosureModel();\n m.Load(SerializeUxMilestones({ Level: 2, Calls: 1, Density: 'auto' }));\n m.Raise('power');\n let emissions = 0;\n m.Changed$.subscribe(() => emissions++);\n\n m.ReturnToPureAudio();\n expect(m.SessionLevel).toBe(0);\n expect(m.ShowThread).toBe(false);\n expect(m.Milestones.Level).toBe(2); // the cross-session ratchet is untouched\n m.ReturnToPureAudio(); // already there — no extra emission\n expect(emissions).toBe(1);\n\n // the user can step back up afterward — the door swings both ways\n expect(m.Raise('text')).toBe(true);\n expect(m.SessionLevel).toBe(1);\n });\n\n it('SetDensity applies immediately — simple retracts even a mid-call console', () => {\n const m = new RealtimeDisclosureModel();\n m.Load(SerializeUxMilestones({ Level: 4, Calls: 9, Density: 'auto' }));\n expect(m.SessionLevel).toBe(4);\n m.SetDensity('simple');\n expect(m.SessionLevel).toBe(0);\n expect(m.Milestones.Density).toBe('simple');\n m.SetDensity('pro');\n expect(m.SessionLevel).toBe(REALTIME_UX_MAX_LEVEL);\n });\n\n it('RatchetOnSessionEnd replaces milestones and returns the persistable payload', () => {\n const m = new RealtimeDisclosureModel();\n m.Load(null);\n m.Raise('channel');\n const serialized = m.RatchetOnSessionEnd();\n expect(m.Milestones).toEqual({ Level: 3, Calls: 1, Density: 'auto' });\n expect(ParseUxMilestones(serialized)).toEqual(m.Milestones);\n });\n});\n"]}
|