@memberjunction/ng-conversations 5.42.0 → 5.44.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.js +37 -41
- 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.js +41 -45
- package/dist/lib/components/collection/collection-share-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-view.component.js +22 -36
- 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/conversation/conversation-chat-area.component.d.ts +73 -8
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +487 -123
- package/dist/lib/components/conversation/conversation-chat-area.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/mention/mention-dropdown.component.js +65 -26
- package/dist/lib/components/mention/mention-dropdown.component.js.map +1 -1
- package/dist/lib/components/mention/mention-editor.component.d.ts.map +1 -1
- package/dist/lib/components/mention/mention-editor.component.js +31 -10
- package/dist/lib/components/mention/mention-editor.component.js.map +1 -1
- package/dist/lib/components/message/message-input-box.component.d.ts +11 -16
- package/dist/lib/components/message/message-input-box.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input-box.component.js +52 -46
- package/dist/lib/components/message/message-input-box.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +83 -18
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +238 -151
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +15 -1
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +90 -15
- 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 +3 -0
- 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/workspace/conversation-workspace.component.d.ts +5 -0
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +18 -7
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/conversations.module.d.ts +29 -26
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js +37 -6
- 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 +2 -2
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +6 -2
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.d.ts +41 -5
- package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.js +143 -19
- 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 +6 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +8 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +28 -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
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"distinct-artifacts.test.js","sourceRoot":"","sources":["../../src/__tests__/distinct-artifacts.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect } from 'vitest';\nimport { selectDistinctLatestArtifacts } from '../lib/utils/distinct-artifacts';\n\n/**\n * Regression guard for the bug where a message carrying TWO distinct artifacts\n * (e.g. a research report + a standalone generated infographic) only ever showed\n * ONE of them — the message list previously displayed `artifactList[length - 1]`,\n * silently hiding the report behind the image. The fix surfaces every distinct\n * artifact (latest version each); these tests lock that behavior in.\n */\ndescribe('selectDistinctLatestArtifacts', () => {\n const make = (artifactId: string, versionNumber: number, tag = '') =>\n ({ artifactId, versionNumber, tag });\n\n it('returns an empty array for empty input', () => {\n expect(selectDistinctLatestArtifacts([])).toEqual([]);\n });\n\n it('keeps BOTH distinct artifacts (the report-hidden-behind-image bug)', () => {\n const report = make('report-id', 1, 'report');\n const image = make('image-id', 1, 'image');\n const result = selectDistinctLatestArtifacts([report, image]);\n\n expect(result).toHaveLength(2);\n expect(result.map(r => r.artifactId)).toEqual(['report-id', 'image-id']);\n });\n\n it('collapses multiple versions of the SAME artifact to the latest', () => {\n const v1 = make('a', 1, 'v1');\n const v2 = make('a', 2, 'v2');\n const v3 = make('a', 3, 'v3');\n const result = selectDistinctLatestArtifacts([v1, v3, v2]);\n\n expect(result).toHaveLength(1);\n expect(result[0].versionNumber).toBe(3);\n expect(result[0].tag).toBe('v3');\n });\n\n it('handles a mix: distinct artifacts each collapsed to their latest version', () => {\n const reportV1 = make('report', 1, 'r1');\n const reportV2 = make('report', 2, 'r2');\n const image = make('image', 1, 'img');\n const result = selectDistinctLatestArtifacts([reportV1, image, reportV2]);\n\n expect(result).toHaveLength(2);\n const byId = Object.fromEntries(result.map(r => [r.artifactId, r]));\n expect(byId['report'].versionNumber).toBe(2);\n expect(byId['report'].tag).toBe('r2');\n expect(byId['image'].versionNumber).toBe(1);\n });\n\n it('preserves first-seen ordering of distinct artifacts', () => {\n const image = make('image', 1);\n const report = make('report', 1);\n // image seen first → image stays first even though report has a later update\n const result = selectDistinctLatestArtifacts([image, report, make('report', 2)]);\n expect(result.map(r => r.artifactId)).toEqual(['image', 'report']);\n });\n\n it('does not mutate the input array', () => {\n const input = [make('a', 1), make('a', 2), make('b', 1)];\n const copy = [...input];\n selectDistinctLatestArtifacts(input);\n expect(input).toEqual(copy);\n });\n});\n"]}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Tests for the Before/After cancelable event wiring.
|
|
3
|
-
*
|
|
4
|
-
* Covers the cancel semantics of `BeforeAgentTurnEventArgs` and
|
|
5
|
-
* `BeforeResponseFormSubmittedEventArgs` — the contracts the wiring relies on.
|
|
6
|
-
* The full Angular emit-then-react flow (subscribing on the EventEmitter and
|
|
7
|
-
* having the synchronous cancel propagate through component event re-emit
|
|
8
|
-
* bindings) is integration territory that needs TestBed + DOM, but the
|
|
9
|
-
* per-event contract — "Cancel = false by default, flip to true halts the
|
|
10
|
-
* default behavior, payload fields round-trip" — is what the firing code in
|
|
11
|
-
* message-input + message-item actually depends on. Tested without Angular
|
|
12
|
-
* instantiation here so it runs fast and stays decoupled from the framework.
|
|
13
|
-
*/
|
|
14
|
-
export {};
|
|
15
|
-
//# sourceMappingURL=event-wiring.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"event-wiring.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/event-wiring.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Tests for the Before/After cancelable event wiring.
|
|
3
|
-
*
|
|
4
|
-
* Covers the cancel semantics of `BeforeAgentTurnEventArgs` and
|
|
5
|
-
* `BeforeResponseFormSubmittedEventArgs` — the contracts the wiring relies on.
|
|
6
|
-
* The full Angular emit-then-react flow (subscribing on the EventEmitter and
|
|
7
|
-
* having the synchronous cancel propagate through component event re-emit
|
|
8
|
-
* bindings) is integration territory that needs TestBed + DOM, but the
|
|
9
|
-
* per-event contract — "Cancel = false by default, flip to true halts the
|
|
10
|
-
* default behavior, payload fields round-trip" — is what the firing code in
|
|
11
|
-
* message-input + message-item actually depends on. Tested without Angular
|
|
12
|
-
* instantiation here so it runs fast and stays decoupled from the framework.
|
|
13
|
-
*/
|
|
14
|
-
import { describe, it, expect } from 'vitest';
|
|
15
|
-
import { BeforeAgentTurnEventArgs, AfterAgentTurnEventArgs, BeforeResponseFormSubmittedEventArgs, AfterResponseFormSubmittedEventArgs, } from '../lib/events/chat-events';
|
|
16
|
-
describe('Before/After event wiring contracts', () => {
|
|
17
|
-
describe('BeforeAgentTurnEventArgs (wired in message-input.component)', () => {
|
|
18
|
-
it('defaults Cancel to false — agent turn proceeds when no listener intervenes', () => {
|
|
19
|
-
const e = new BeforeAgentTurnEventArgs('conv-1', 'hello', 'app-1');
|
|
20
|
-
expect(e.Cancel).toBe(false);
|
|
21
|
-
});
|
|
22
|
-
it('lets a listener flip Cancel to true — wiring should bail before processMessage', () => {
|
|
23
|
-
const e = new BeforeAgentTurnEventArgs('conv-1', 'hello', null);
|
|
24
|
-
// Listener simulation
|
|
25
|
-
e.Cancel = true;
|
|
26
|
-
e.CancelReason = 'rate-limited';
|
|
27
|
-
expect(e.Cancel).toBe(true);
|
|
28
|
-
expect(e.CancelReason).toBe('rate-limited');
|
|
29
|
-
});
|
|
30
|
-
it('payload fields round-trip — listeners can read what was emitted', () => {
|
|
31
|
-
const e = new BeforeAgentTurnEventArgs('conv-xyz', 'send me data', 'app-abc');
|
|
32
|
-
expect(e.ConversationId).toBe('conv-xyz');
|
|
33
|
-
expect(e.MessageText).toBe('send me data');
|
|
34
|
-
expect(e.ApplicationId).toBe('app-abc');
|
|
35
|
-
});
|
|
36
|
-
it('null applicationId is preserved (surfaces with no app context)', () => {
|
|
37
|
-
const e = new BeforeAgentTurnEventArgs('conv-1', 'hi', null);
|
|
38
|
-
expect(e.ApplicationId).toBeNull();
|
|
39
|
-
});
|
|
40
|
-
it('default applicationId is null when omitted (constructor default)', () => {
|
|
41
|
-
const e = new BeforeAgentTurnEventArgs('conv-1', 'hi');
|
|
42
|
-
expect(e.ApplicationId).toBeNull();
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
describe('AfterAgentTurnEventArgs (wired in message-input.component)', () => {
|
|
46
|
-
it('carries the agent run id and result payload as readonly fields', () => {
|
|
47
|
-
const result = { success: true, executionTimeMs: 123 };
|
|
48
|
-
const e = new AfterAgentTurnEventArgs('conv-1', 'run-abc', result);
|
|
49
|
-
expect(e.ConversationId).toBe('conv-1');
|
|
50
|
-
expect(e.AgentRunId).toBe('run-abc');
|
|
51
|
-
expect(e.Result).toBe(result);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
describe('BeforeResponseFormSubmittedEventArgs (wired in message-item.component)', () => {
|
|
55
|
-
it('defaults Cancel to false — form submission proceeds when no listener intervenes', () => {
|
|
56
|
-
const e = new BeforeResponseFormSubmittedEventArgs('msg-1', { q1: 'yes' });
|
|
57
|
-
expect(e.Cancel).toBe(false);
|
|
58
|
-
});
|
|
59
|
-
it('lets a listener flip Cancel to true — wiring should skip suggestedResponseSelected emit', () => {
|
|
60
|
-
const e = new BeforeResponseFormSubmittedEventArgs('msg-1', { q1: 'yes' });
|
|
61
|
-
e.Cancel = true;
|
|
62
|
-
e.CancelReason = 'validation failed';
|
|
63
|
-
expect(e.Cancel).toBe(true);
|
|
64
|
-
expect(e.CancelReason).toBe('validation failed');
|
|
65
|
-
});
|
|
66
|
-
it('uses the message ID as the form id (stable per-message identifier)', () => {
|
|
67
|
-
const e = new BeforeResponseFormSubmittedEventArgs('msg-xyz', {});
|
|
68
|
-
expect(e.FormId).toBe('msg-xyz');
|
|
69
|
-
});
|
|
70
|
-
it('carries the submitted values map as a readonly field', () => {
|
|
71
|
-
const vals = { feedback: 'great', rating: 5, agree: true };
|
|
72
|
-
const e = new BeforeResponseFormSubmittedEventArgs('msg-1', vals);
|
|
73
|
-
expect(e.Values).toEqual(vals);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
describe('AfterResponseFormSubmittedEventArgs (wired in message-item.component)', () => {
|
|
77
|
-
it('mirrors the Before event payload (form id + values)', () => {
|
|
78
|
-
const vals = { feedback: 'great', rating: 5 };
|
|
79
|
-
const before = new BeforeResponseFormSubmittedEventArgs('msg-1', vals);
|
|
80
|
-
const after = new AfterResponseFormSubmittedEventArgs(before.FormId, before.Values);
|
|
81
|
-
expect(after.FormId).toBe('msg-1');
|
|
82
|
-
expect(after.Values).toEqual(vals);
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
describe('wiring assumptions (documenting the firing-code contract)', () => {
|
|
86
|
-
it('cancel semantics are synchronous — flipping Cancel inside the same call is sufficient', () => {
|
|
87
|
-
// The message-input + message-item firing code calls .emit(event), then checks
|
|
88
|
-
// event.Cancel BEFORE proceeding. This is correct because Angular's EventEmitter
|
|
89
|
-
// is synchronous by default (extends Subject), so all subscribers run before
|
|
90
|
-
// .emit() returns. Verify the contract: mutations to the event during a
|
|
91
|
-
// synchronous "listener" are visible after the synchronous call chain returns.
|
|
92
|
-
const e = new BeforeAgentTurnEventArgs('conv-1', 'hi');
|
|
93
|
-
(function listener(eventArgs) {
|
|
94
|
-
eventArgs.Cancel = true;
|
|
95
|
-
})(e);
|
|
96
|
-
expect(e.Cancel).toBe(true);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
//# sourceMappingURL=event-wiring.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"event-wiring.test.js","sourceRoot":"","sources":["../../src/__tests__/event-wiring.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Tests for the Before/After cancelable event wiring.\n *\n * Covers the cancel semantics of `BeforeAgentTurnEventArgs` and\n * `BeforeResponseFormSubmittedEventArgs` — the contracts the wiring relies on.\n * The full Angular emit-then-react flow (subscribing on the EventEmitter and\n * having the synchronous cancel propagate through component event re-emit\n * bindings) is integration territory that needs TestBed + DOM, but the\n * per-event contract — \"Cancel = false by default, flip to true halts the\n * default behavior, payload fields round-trip\" — is what the firing code in\n * message-input + message-item actually depends on. Tested without Angular\n * instantiation here so it runs fast and stays decoupled from the framework.\n */\n\nimport { describe, it, expect } from 'vitest';\n\nimport {\n BeforeAgentTurnEventArgs,\n AfterAgentTurnEventArgs,\n BeforeResponseFormSubmittedEventArgs,\n AfterResponseFormSubmittedEventArgs,\n} from '../lib/events/chat-events';\n\ndescribe('Before/After event wiring contracts', () => {\n describe('BeforeAgentTurnEventArgs (wired in message-input.component)', () => {\n it('defaults Cancel to false — agent turn proceeds when no listener intervenes', () => {\n const e = new BeforeAgentTurnEventArgs('conv-1', 'hello', 'app-1');\n expect(e.Cancel).toBe(false);\n });\n\n it('lets a listener flip Cancel to true — wiring should bail before processMessage', () => {\n const e = new BeforeAgentTurnEventArgs('conv-1', 'hello', null);\n // Listener simulation\n e.Cancel = true;\n e.CancelReason = 'rate-limited';\n expect(e.Cancel).toBe(true);\n expect(e.CancelReason).toBe('rate-limited');\n });\n\n it('payload fields round-trip — listeners can read what was emitted', () => {\n const e = new BeforeAgentTurnEventArgs('conv-xyz', 'send me data', 'app-abc');\n expect(e.ConversationId).toBe('conv-xyz');\n expect(e.MessageText).toBe('send me data');\n expect(e.ApplicationId).toBe('app-abc');\n });\n\n it('null applicationId is preserved (surfaces with no app context)', () => {\n const e = new BeforeAgentTurnEventArgs('conv-1', 'hi', null);\n expect(e.ApplicationId).toBeNull();\n });\n\n it('default applicationId is null when omitted (constructor default)', () => {\n const e = new BeforeAgentTurnEventArgs('conv-1', 'hi');\n expect(e.ApplicationId).toBeNull();\n });\n });\n\n describe('AfterAgentTurnEventArgs (wired in message-input.component)', () => {\n it('carries the agent run id and result payload as readonly fields', () => {\n const result = { success: true, executionTimeMs: 123 } as never;\n const e = new AfterAgentTurnEventArgs('conv-1', 'run-abc', result);\n expect(e.ConversationId).toBe('conv-1');\n expect(e.AgentRunId).toBe('run-abc');\n expect(e.Result).toBe(result);\n });\n });\n\n describe('BeforeResponseFormSubmittedEventArgs (wired in message-item.component)', () => {\n it('defaults Cancel to false — form submission proceeds when no listener intervenes', () => {\n const e = new BeforeResponseFormSubmittedEventArgs('msg-1', { q1: 'yes' });\n expect(e.Cancel).toBe(false);\n });\n\n it('lets a listener flip Cancel to true — wiring should skip suggestedResponseSelected emit', () => {\n const e = new BeforeResponseFormSubmittedEventArgs('msg-1', { q1: 'yes' });\n e.Cancel = true;\n e.CancelReason = 'validation failed';\n expect(e.Cancel).toBe(true);\n expect(e.CancelReason).toBe('validation failed');\n });\n\n it('uses the message ID as the form id (stable per-message identifier)', () => {\n const e = new BeforeResponseFormSubmittedEventArgs('msg-xyz', {});\n expect(e.FormId).toBe('msg-xyz');\n });\n\n it('carries the submitted values map as a readonly field', () => {\n const vals = { feedback: 'great', rating: 5, agree: true };\n const e = new BeforeResponseFormSubmittedEventArgs('msg-1', vals);\n expect(e.Values).toEqual(vals);\n });\n });\n\n describe('AfterResponseFormSubmittedEventArgs (wired in message-item.component)', () => {\n it('mirrors the Before event payload (form id + values)', () => {\n const vals = { feedback: 'great', rating: 5 };\n const before = new BeforeResponseFormSubmittedEventArgs('msg-1', vals);\n const after = new AfterResponseFormSubmittedEventArgs(before.FormId, before.Values);\n expect(after.FormId).toBe('msg-1');\n expect(after.Values).toEqual(vals);\n });\n });\n\n describe('wiring assumptions (documenting the firing-code contract)', () => {\n it('cancel semantics are synchronous — flipping Cancel inside the same call is sufficient', () => {\n // The message-input + message-item firing code calls .emit(event), then checks\n // event.Cancel BEFORE proceeding. This is correct because Angular's EventEmitter\n // is synchronous by default (extends Subject), so all subscribers run before\n // .emit() returns. Verify the contract: mutations to the event during a\n // synchronous \"listener\" are visible after the synchronous call chain returns.\n const e = new BeforeAgentTurnEventArgs('conv-1', 'hi');\n (function listener(eventArgs: BeforeAgentTurnEventArgs) {\n eventArgs.Cancel = true;\n })(e);\n expect(e.Cancel).toBe(true);\n });\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE9C,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it, expect } from 'vitest';\n\ndescribe('@memberjunction/ng-conversations', () => {\n it('should have a passing test', () => {\n expect(true).toBe(true);\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"narration-template.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/narration-template.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for the pure narration-template helpers used by RealtimeSessionService — the DB-driven
|
|
3
|
-
* `{{ progressMessage }}` / `{{ priorNarrations }}` / `{{ updateNumber }}` substitution plus the
|
|
4
|
-
* built-in fallback for deployments that haven't synced the
|
|
5
|
-
* `Realtime Co-Agent - Progress Narration` prompt.
|
|
6
|
-
*/
|
|
7
|
-
import { describe, it, expect } from 'vitest';
|
|
8
|
-
import { BuildNarrationInstructions, DefaultNarrationInstructions } from '../lib/services/narration-template';
|
|
9
|
-
describe('DefaultNarrationInstructions', () => {
|
|
10
|
-
it('embeds the progress digest and keeps the first-person + variation rules', () => {
|
|
11
|
-
const out = DefaultNarrationInstructions('Analyzing the request');
|
|
12
|
-
expect(out).toContain('Analyzing the request');
|
|
13
|
-
expect(out).toContain('FIRST PERSON');
|
|
14
|
-
expect(out).toContain('VARY the');
|
|
15
|
-
expect(out).toContain('must not be the subject');
|
|
16
|
-
expect(out).toContain('Never repeat');
|
|
17
|
-
});
|
|
18
|
-
it('numbers the update and reports no prior narrations on the first update', () => {
|
|
19
|
-
const out = DefaultNarrationInstructions('Fetching', { UpdateNumber: 1 });
|
|
20
|
-
expect(out).toContain('update #1');
|
|
21
|
-
expect(out).toContain('first spoken update');
|
|
22
|
-
});
|
|
23
|
-
it('chains prior spoken narrations so the model can build on them', () => {
|
|
24
|
-
const out = DefaultNarrationInstructions('Fetching', {
|
|
25
|
-
UpdateNumber: 3,
|
|
26
|
-
PriorNarrations: ["I'm pulling that up now", 'Got the first batch']
|
|
27
|
-
});
|
|
28
|
-
expect(out).toContain('update #3');
|
|
29
|
-
expect(out).toContain(`- "I'm pulling that up now"`);
|
|
30
|
-
expect(out).toContain('- "Got the first batch"');
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
describe('BuildNarrationInstructions', () => {
|
|
34
|
-
it('substitutes {{ progressMessage }} (spaced token) everywhere it appears', () => {
|
|
35
|
-
const template = 'Progress: "{{ progressMessage }}". Repeat: {{ progressMessage }}.';
|
|
36
|
-
const out = BuildNarrationInstructions(template, 'Fetching records');
|
|
37
|
-
expect(out).toBe('Progress: "Fetching records". Repeat: Fetching records.');
|
|
38
|
-
});
|
|
39
|
-
it('tolerates the no-spaces {{progressMessage}} variant', () => {
|
|
40
|
-
const out = BuildNarrationInstructions('Now: {{progressMessage}}!', 'Running the query');
|
|
41
|
-
expect(out).toBe('Now: Running the query!');
|
|
42
|
-
});
|
|
43
|
-
it('substitutes a mix of both token variants', () => {
|
|
44
|
-
const out = BuildNarrationInstructions('A {{ progressMessage }} B {{progressMessage}}', 'x');
|
|
45
|
-
expect(out).toBe('A x B x');
|
|
46
|
-
});
|
|
47
|
-
it('substitutes {{ updateNumber }} and {{ priorNarrations }}', () => {
|
|
48
|
-
const template = 'Update #{{ updateNumber }}. Said before:\n{{ priorNarrations }}';
|
|
49
|
-
const out = BuildNarrationInstructions(template, 'x', {
|
|
50
|
-
UpdateNumber: 2,
|
|
51
|
-
PriorNarrations: ['First thing I said']
|
|
52
|
-
});
|
|
53
|
-
expect(out).toBe('Update #2. Said before:\n- "First thing I said"');
|
|
54
|
-
});
|
|
55
|
-
it('renders a friendly "nothing yet" for {{ priorNarrations }} on the first update', () => {
|
|
56
|
-
const out = BuildNarrationInstructions('Prior: {{ priorNarrations }}', 'x');
|
|
57
|
-
expect(out).toContain('first spoken update');
|
|
58
|
-
});
|
|
59
|
-
it('defaults {{ updateNumber }} to 1 when no options are given', () => {
|
|
60
|
-
const out = BuildNarrationInstructions('N={{updateNumber}}', 'x');
|
|
61
|
-
expect(out).toBe('N=1');
|
|
62
|
-
});
|
|
63
|
-
it('falls back to the built-in wording when the template is null', () => {
|
|
64
|
-
const out = BuildNarrationInstructions(null, 'Analyzing the request', { UpdateNumber: 2 });
|
|
65
|
-
expect(out).toBe(DefaultNarrationInstructions('Analyzing the request', { UpdateNumber: 2 }));
|
|
66
|
-
});
|
|
67
|
-
it('falls back to the built-in wording when the template is blank', () => {
|
|
68
|
-
const out = BuildNarrationInstructions(' ', 'Analyzing the request');
|
|
69
|
-
expect(out).toBe(DefaultNarrationInstructions('Analyzing the request'));
|
|
70
|
-
});
|
|
71
|
-
it('returns a template without tokens unchanged (no accidental injection)', () => {
|
|
72
|
-
const out = BuildNarrationInstructions('Say something brief.', 'ignored');
|
|
73
|
-
expect(out).toBe('Say something brief.');
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
//# sourceMappingURL=narration-template.test.js.map
|
|
@@ -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"]}
|