@memberjunction/ng-conversations 5.40.1 → 5.41.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/README.md +57 -0
- package/dist/__tests__/channel-optional-surface.test.d.ts +2 -0
- package/dist/__tests__/channel-optional-surface.test.d.ts.map +1 -0
- package/dist/__tests__/channel-optional-surface.test.js +53 -0
- package/dist/__tests__/channel-optional-surface.test.js.map +1 -0
- package/dist/__tests__/chat-events.test.d.ts +14 -0
- package/dist/__tests__/chat-events.test.d.ts.map +1 -0
- package/dist/__tests__/chat-events.test.js +109 -0
- package/dist/__tests__/chat-events.test.js.map +1 -0
- package/dist/__tests__/conversation-naming.test.d.ts +2 -0
- package/dist/__tests__/conversation-naming.test.d.ts.map +1 -0
- package/dist/__tests__/conversation-naming.test.js +110 -0
- package/dist/__tests__/conversation-naming.test.js.map +1 -0
- package/dist/__tests__/delegation-result-parser.test.d.ts +2 -0
- package/dist/__tests__/delegation-result-parser.test.d.ts.map +1 -0
- package/dist/__tests__/delegation-result-parser.test.js +107 -0
- package/dist/__tests__/delegation-result-parser.test.js.map +1 -0
- package/dist/__tests__/event-wiring.test.d.ts +15 -0
- package/dist/__tests__/event-wiring.test.d.ts.map +1 -0
- package/dist/__tests__/event-wiring.test.js +100 -0
- package/dist/__tests__/event-wiring.test.js.map +1 -0
- package/dist/__tests__/narration-template.test.d.ts +2 -0
- package/dist/__tests__/narration-template.test.d.ts.map +1 -0
- package/dist/__tests__/narration-template.test.js +76 -0
- package/dist/__tests__/narration-template.test.js.map +1 -0
- package/dist/__tests__/realtime-agent-picker-models.test.d.ts +2 -0
- package/dist/__tests__/realtime-agent-picker-models.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-agent-picker-models.test.js +49 -0
- package/dist/__tests__/realtime-agent-picker-models.test.js.map +1 -0
- package/dist/__tests__/realtime-audio-visuals.test.d.ts +2 -0
- package/dist/__tests__/realtime-audio-visuals.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-audio-visuals.test.js +123 -0
- package/dist/__tests__/realtime-audio-visuals.test.js.map +1 -0
- package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts +2 -0
- package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-delegation-card-cancel.test.js +48 -0
- package/dist/__tests__/realtime-delegation-card-cancel.test.js.map +1 -0
- package/dist/__tests__/realtime-disclosure.test.d.ts +2 -0
- package/dist/__tests__/realtime-disclosure.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-disclosure.test.js +164 -0
- package/dist/__tests__/realtime-disclosure.test.js.map +1 -0
- package/dist/__tests__/realtime-pairing.test.d.ts +2 -0
- package/dist/__tests__/realtime-pairing.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-pairing.test.js +207 -0
- package/dist/__tests__/realtime-pairing.test.js.map +1 -0
- package/dist/__tests__/realtime-review-lifecycle.test.d.ts +2 -0
- package/dist/__tests__/realtime-review-lifecycle.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-review-lifecycle.test.js +154 -0
- package/dist/__tests__/realtime-review-lifecycle.test.js.map +1 -0
- package/dist/__tests__/realtime-session-cancel-usage.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-cancel-usage.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-cancel-usage.test.js +230 -0
- package/dist/__tests__/realtime-session-cancel-usage.test.js.map +1 -0
- package/dist/__tests__/realtime-session-channels.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-channels.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-channels.test.js +252 -0
- package/dist/__tests__/realtime-session-channels.test.js.map +1 -0
- package/dist/__tests__/realtime-session-client-tools.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-client-tools.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-client-tools.test.js +103 -0
- package/dist/__tests__/realtime-session-client-tools.test.js.map +1 -0
- package/dist/__tests__/realtime-session-minimized.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-minimized.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-minimized.test.js +32 -0
- package/dist/__tests__/realtime-session-minimized.test.js.map +1 -0
- package/dist/__tests__/realtime-session-mint.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-mint.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-mint.test.js +69 -0
- package/dist/__tests__/realtime-session-mint.test.js.map +1 -0
- package/dist/__tests__/realtime-session-policy.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-policy.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-policy.test.js +303 -0
- package/dist/__tests__/realtime-session-policy.test.js.map +1 -0
- package/dist/__tests__/realtime-session-review.service.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-review.service.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-review.service.test.js +743 -0
- package/dist/__tests__/realtime-session-review.service.test.js.map +1 -0
- package/dist/__tests__/realtime-session-state.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-state.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-state.test.js +83 -0
- package/dist/__tests__/realtime-session-state.test.js.map +1 -0
- package/dist/__tests__/realtime-session-timeline-card.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-timeline-card.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-timeline-card.test.js +106 -0
- package/dist/__tests__/realtime-session-timeline-card.test.js.map +1 -0
- package/dist/__tests__/realtime-session-timeline.test.d.ts +2 -0
- package/dist/__tests__/realtime-session-timeline.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-session-timeline.test.js +142 -0
- package/dist/__tests__/realtime-session-timeline.test.js.map +1 -0
- package/dist/__tests__/realtime-sessions-adapter.test.d.ts +19 -0
- package/dist/__tests__/realtime-sessions-adapter.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-sessions-adapter.test.js +188 -0
- package/dist/__tests__/realtime-sessions-adapter.test.js.map +1 -0
- package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts +2 -0
- package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-surface-panel-prefs.test.js +100 -0
- package/dist/__tests__/realtime-surface-panel-prefs.test.js.map +1 -0
- package/dist/__tests__/realtime-surface-tabs-model.test.d.ts +2 -0
- package/dist/__tests__/realtime-surface-tabs-model.test.d.ts.map +1 -0
- package/dist/__tests__/realtime-surface-tabs-model.test.js +193 -0
- package/dist/__tests__/realtime-surface-tabs-model.test.js.map +1 -0
- package/dist/__tests__/remote-browser-audio-player.test.d.ts +2 -0
- package/dist/__tests__/remote-browser-audio-player.test.d.ts.map +1 -0
- package/dist/__tests__/remote-browser-audio-player.test.js +137 -0
- package/dist/__tests__/remote-browser-audio-player.test.js.map +1 -0
- package/dist/__tests__/remote-browser-channel.test.d.ts +2 -0
- package/dist/__tests__/remote-browser-channel.test.d.ts.map +1 -0
- package/dist/__tests__/remote-browser-channel.test.js +423 -0
- package/dist/__tests__/remote-browser-channel.test.js.map +1 -0
- package/dist/__tests__/slot-defaults.test.d.ts +24 -0
- package/dist/__tests__/slot-defaults.test.d.ts.map +1 -0
- package/dist/__tests__/slot-defaults.test.js +63 -0
- package/dist/__tests__/slot-defaults.test.js.map +1 -0
- package/dist/__tests__/user-authorization.test.d.ts +2 -0
- package/dist/__tests__/user-authorization.test.d.ts.map +1 -0
- package/dist/__tests__/user-authorization.test.js +97 -0
- package/dist/__tests__/user-authorization.test.js.map +1 -0
- package/dist/__tests__/voice-session-narration.test.d.ts +2 -0
- package/dist/__tests__/voice-session-narration.test.d.ts.map +1 -0
- package/dist/__tests__/voice-session-narration.test.js +609 -0
- package/dist/__tests__/voice-session-narration.test.js.map +1 -0
- package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts +2 -0
- package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts.map +1 -0
- package/dist/__tests__/whiteboard-artifact-viewer.test.js +101 -0
- package/dist/__tests__/whiteboard-artifact-viewer.test.js.map +1 -0
- package/dist/__tests__/whiteboard-channel.test.d.ts +2 -0
- package/dist/__tests__/whiteboard-channel.test.d.ts.map +1 -0
- package/dist/__tests__/whiteboard-channel.test.js +260 -0
- package/dist/__tests__/whiteboard-channel.test.js.map +1 -0
- package/dist/__tests__/whiteboard-restore-state.test.d.ts +2 -0
- package/dist/__tests__/whiteboard-restore-state.test.d.ts.map +1 -0
- package/dist/__tests__/whiteboard-restore-state.test.js +108 -0
- package/dist/__tests__/whiteboard-restore-state.test.js.map +1 -0
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +205 -3
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +911 -342
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/mention/mention-dropdown.component.js +35 -17
- package/dist/lib/components/mention/mention-dropdown.component.js.map +1 -1
- package/dist/lib/components/mention/mention-editor.component.d.ts +4 -0
- package/dist/lib/components/mention/mention-editor.component.d.ts.map +1 -1
- package/dist/lib/components/mention/mention-editor.component.js +43 -19
- package/dist/lib/components/mention/mention-editor.component.js.map +1 -1
- package/dist/lib/components/message/message-input-box.component.d.ts +17 -1
- package/dist/lib/components/message/message-input-box.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input-box.component.js +73 -15
- package/dist/lib/components/message/message-input-box.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +142 -6
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +328 -82
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +28 -3
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +180 -108
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +81 -2
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +252 -87
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts +282 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts.map +1 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js +158 -0
- package/dist/lib/components/realtime/channels/base-realtime-channel-client.js.map +1 -0
- package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts +25 -0
- package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.js +140 -0
- package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.js.map +1 -0
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts +35 -0
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js +58 -0
- package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts +63 -0
- package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-activity-rail.component.js +260 -0
- package/dist/lib/components/realtime/realtime-activity-rail.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +117 -0
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-agent-banner.component.js +504 -0
- package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts +168 -0
- package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-agent-picker.component.js +556 -0
- package/dist/lib/components/realtime/realtime-agent-picker.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-audio-visuals.d.ts +97 -0
- package/dist/lib/components/realtime/realtime-audio-visuals.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-audio-visuals.js +139 -0
- package/dist/lib/components/realtime/realtime-audio-visuals.js.map +1 -0
- package/dist/lib/components/realtime/realtime-channel-strip.component.d.ts +29 -0
- package/dist/lib/components/realtime/realtime-channel-strip.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-channel-strip.component.js +69 -0
- package/dist/lib/components/realtime/realtime-channel-strip.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-composer.component.d.ts +65 -0
- package/dist/lib/components/realtime/realtime-composer.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-composer.component.js +256 -0
- package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts +71 -0
- package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-delegation-card.component.js +324 -0
- package/dist/lib/components/realtime/realtime-delegation-card.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-disclosure.d.ts +135 -0
- package/dist/lib/components/realtime/realtime-disclosure.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-disclosure.js +188 -0
- package/dist/lib/components/realtime/realtime-disclosure.js.map +1 -0
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +491 -0
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-session-overlay.component.js +1274 -0
- package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-session-state.d.ts +191 -0
- package/dist/lib/components/realtime/realtime-session-state.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-session-state.js +244 -0
- package/dist/lib/components/realtime/realtime-session-state.js.map +1 -0
- package/dist/lib/components/realtime/realtime-session-thread.component.d.ts +56 -0
- package/dist/lib/components/realtime/realtime-session-thread.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-session-thread.component.js +246 -0
- package/dist/lib/components/realtime/realtime-session-thread.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts +51 -0
- package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-session-timeline-card.component.js +193 -0
- package/dist/lib/components/realtime/realtime-session-timeline-card.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-panel-prefs.d.ts +77 -0
- package/dist/lib/components/realtime/realtime-surface-panel-prefs.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-panel-prefs.js +114 -0
- package/dist/lib/components/realtime/realtime-surface-panel-prefs.js.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts +173 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js +496 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.component.js.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts +181 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js +223 -0
- package/dist/lib/components/realtime/realtime-surface-tabs.model.js.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.d.ts +163 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.d.ts.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.js +309 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.js.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts +168 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js +524 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.d.ts +346 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.js +851 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.js.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts +86 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts.map +1 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js +210 -0
- package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js.map +1 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.d.ts +48 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.d.ts.map +1 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.js +180 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.js.map +1 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts +119 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts.map +1 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js +274 -0
- package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js.map +1 -0
- package/dist/lib/components/slots/mj-chat-agent-presence-default.component.d.ts +11 -0
- package/dist/lib/components/slots/mj-chat-agent-presence-default.component.d.ts.map +1 -0
- package/dist/lib/components/slots/mj-chat-agent-presence-default.component.js +98 -0
- package/dist/lib/components/slots/mj-chat-agent-presence-default.component.js.map +1 -0
- package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.d.ts +9 -0
- package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.d.ts.map +1 -0
- package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.js +35 -0
- package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.js.map +1 -0
- package/dist/lib/components/slots/mj-chat-empty-state-default.component.d.ts +28 -0
- package/dist/lib/components/slots/mj-chat-empty-state-default.component.d.ts.map +1 -0
- package/dist/lib/components/slots/mj-chat-empty-state-default.component.js +104 -0
- package/dist/lib/components/slots/mj-chat-empty-state-default.component.js.map +1 -0
- package/dist/lib/components/slots/mj-chat-header-default.component.d.ts +11 -0
- package/dist/lib/components/slots/mj-chat-header-default.component.d.ts.map +1 -0
- package/dist/lib/components/slots/mj-chat-header-default.component.js +103 -0
- package/dist/lib/components/slots/mj-chat-header-default.component.js.map +1 -0
- package/dist/lib/components/slots/mj-chat-message-bubble-default.component.d.ts +15 -0
- package/dist/lib/components/slots/mj-chat-message-bubble-default.component.d.ts.map +1 -0
- package/dist/lib/components/slots/mj-chat-message-bubble-default.component.js +73 -0
- package/dist/lib/components/slots/mj-chat-message-bubble-default.component.js.map +1 -0
- package/dist/lib/components/slots/mj-chat-message-extra-default.component.d.ts +9 -0
- package/dist/lib/components/slots/mj-chat-message-extra-default.component.d.ts.map +1 -0
- package/dist/lib/components/slots/mj-chat-message-extra-default.component.js +34 -0
- package/dist/lib/components/slots/mj-chat-message-extra-default.component.js.map +1 -0
- package/dist/lib/components/slots/slot-interfaces.d.ts +95 -0
- package/dist/lib/components/slots/slot-interfaces.d.ts.map +1 -0
- package/dist/lib/components/slots/slot-interfaces.js +18 -0
- package/dist/lib/components/slots/slot-interfaces.js.map +1 -0
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts +11 -0
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +28 -4
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/conversations.module.d.ts +12 -1
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js +93 -5
- package/dist/lib/conversations.module.js.map +1 -1
- package/dist/lib/directives/chat-slot.directive.d.ts +44 -0
- package/dist/lib/directives/chat-slot.directive.d.ts.map +1 -0
- package/dist/lib/directives/chat-slot.directive.js +54 -0
- package/dist/lib/directives/chat-slot.directive.js.map +1 -0
- package/dist/lib/events/chat-events.d.ts +137 -0
- package/dist/lib/events/chat-events.d.ts.map +1 -0
- package/dist/lib/events/chat-events.js +189 -0
- package/dist/lib/events/chat-events.js.map +1 -0
- package/dist/lib/models/conversation-state.model.d.ts +2 -1
- 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/artifact-state.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-state.service.js +23 -6
- package/dist/lib/services/artifact-state.service.js.map +1 -1
- package/dist/lib/services/conversation-agent.service.d.ts +60 -74
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +100 -313
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/conversation-bridge.service.d.ts +11 -70
- package/dist/lib/services/conversation-bridge.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-bridge.service.js +51 -85
- package/dist/lib/services/conversation-bridge.service.js.map +1 -1
- package/dist/lib/services/conversation-naming.d.ts +63 -0
- package/dist/lib/services/conversation-naming.d.ts.map +1 -0
- package/dist/lib/services/conversation-naming.js +58 -0
- package/dist/lib/services/conversation-naming.js.map +1 -0
- package/dist/lib/services/conversation-streaming.service.d.ts +24 -154
- package/dist/lib/services/conversation-streaming.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-streaming.service.js +39 -361
- package/dist/lib/services/conversation-streaming.service.js.map +1 -1
- package/dist/lib/services/conversations-runtime-bootstrap.service.d.ts +10 -0
- package/dist/lib/services/conversations-runtime-bootstrap.service.d.ts.map +1 -0
- package/dist/lib/services/conversations-runtime-bootstrap.service.js +104 -0
- package/dist/lib/services/conversations-runtime-bootstrap.service.js.map +1 -0
- package/dist/lib/services/delegation-result-parser.d.ts +45 -0
- package/dist/lib/services/delegation-result-parser.d.ts.map +1 -0
- package/dist/lib/services/delegation-result-parser.js +48 -0
- package/dist/lib/services/delegation-result-parser.js.map +1 -0
- package/dist/lib/services/mention-autocomplete.service.d.ts +19 -4
- package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.js +65 -4
- package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
- package/dist/lib/services/mention-parser.service.d.ts +8 -53
- package/dist/lib/services/mention-parser.service.d.ts.map +1 -1
- package/dist/lib/services/mention-parser.service.js +32 -243
- package/dist/lib/services/mention-parser.service.js.map +1 -1
- package/dist/lib/services/narration-template.d.ts +42 -0
- package/dist/lib/services/narration-template.d.ts.map +1 -0
- package/dist/lib/services/narration-template.js +73 -0
- package/dist/lib/services/narration-template.js.map +1 -0
- package/dist/lib/services/realtime-pairing.d.ts +120 -0
- package/dist/lib/services/realtime-pairing.d.ts.map +1 -0
- package/dist/lib/services/realtime-pairing.js +150 -0
- package/dist/lib/services/realtime-pairing.js.map +1 -0
- package/dist/lib/services/realtime-session-review.service.d.ts +233 -0
- package/dist/lib/services/realtime-session-review.service.d.ts.map +1 -0
- package/dist/lib/services/realtime-session-review.service.js +417 -0
- package/dist/lib/services/realtime-session-review.service.js.map +1 -0
- package/dist/lib/services/realtime-session.service.d.ts +739 -0
- package/dist/lib/services/realtime-session.service.d.ts.map +1 -0
- package/dist/lib/services/realtime-session.service.js +1647 -0
- package/dist/lib/services/realtime-session.service.js.map +1 -0
- package/dist/lib/services/realtime-sessions-adapter.d.ts +54 -0
- package/dist/lib/services/realtime-sessions-adapter.d.ts.map +1 -0
- package/dist/lib/services/realtime-sessions-adapter.js +154 -0
- package/dist/lib/services/realtime-sessions-adapter.js.map +1 -0
- package/dist/lib/services/user-authorization.d.ts +67 -0
- package/dist/lib/services/user-authorization.d.ts.map +1 -0
- package/dist/lib/services/user-authorization.js +66 -0
- package/dist/lib/services/user-authorization.js.map +1 -0
- package/dist/lib/utils/realtime-session-timeline.d.ts +84 -0
- package/dist/lib/utils/realtime-session-timeline.d.ts.map +1 -0
- package/dist/lib/utils/realtime-session-timeline.js +94 -0
- package/dist/lib/utils/realtime-session-timeline.js.map +1 -0
- package/dist/public-api.d.ts +41 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +50 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +27 -24
- package/dist/__tests__/conversation-bridge.service.test.d.ts +0 -2
- package/dist/__tests__/conversation-bridge.service.test.d.ts.map +0 -1
- package/dist/__tests__/conversation-bridge.service.test.js +0 -98
- package/dist/__tests__/conversation-bridge.service.test.js.map +0 -1
- package/dist/__tests__/mention-parser.test.d.ts +0 -2
- package/dist/__tests__/mention-parser.test.d.ts.map +0 -1
- package/dist/__tests__/mention-parser.test.js +0 -154
- package/dist/__tests__/mention-parser.test.js.map +0 -1
|
@@ -0,0 +1,15 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-wiring.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/event-wiring.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,100 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"narration-template.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/narration-template.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,76 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-agent-picker-models.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/realtime-agent-picker-models.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-audio-visuals.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/realtime-audio-visuals.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,123 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-disclosure.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/realtime-disclosure.test.ts"],"names":[],"mappings":""}
|