@memberjunction/ng-conversations 5.40.2 → 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.
Files changed (378) hide show
  1. package/README.md +57 -0
  2. package/dist/__tests__/channel-optional-surface.test.d.ts +2 -0
  3. package/dist/__tests__/channel-optional-surface.test.d.ts.map +1 -0
  4. package/dist/__tests__/channel-optional-surface.test.js +53 -0
  5. package/dist/__tests__/channel-optional-surface.test.js.map +1 -0
  6. package/dist/__tests__/chat-events.test.d.ts +14 -0
  7. package/dist/__tests__/chat-events.test.d.ts.map +1 -0
  8. package/dist/__tests__/chat-events.test.js +109 -0
  9. package/dist/__tests__/chat-events.test.js.map +1 -0
  10. package/dist/__tests__/conversation-naming.test.d.ts +2 -0
  11. package/dist/__tests__/conversation-naming.test.d.ts.map +1 -0
  12. package/dist/__tests__/conversation-naming.test.js +110 -0
  13. package/dist/__tests__/conversation-naming.test.js.map +1 -0
  14. package/dist/__tests__/delegation-result-parser.test.d.ts +2 -0
  15. package/dist/__tests__/delegation-result-parser.test.d.ts.map +1 -0
  16. package/dist/__tests__/delegation-result-parser.test.js +107 -0
  17. package/dist/__tests__/delegation-result-parser.test.js.map +1 -0
  18. package/dist/__tests__/event-wiring.test.d.ts +15 -0
  19. package/dist/__tests__/event-wiring.test.d.ts.map +1 -0
  20. package/dist/__tests__/event-wiring.test.js +100 -0
  21. package/dist/__tests__/event-wiring.test.js.map +1 -0
  22. package/dist/__tests__/narration-template.test.d.ts +2 -0
  23. package/dist/__tests__/narration-template.test.d.ts.map +1 -0
  24. package/dist/__tests__/narration-template.test.js +76 -0
  25. package/dist/__tests__/narration-template.test.js.map +1 -0
  26. package/dist/__tests__/realtime-agent-picker-models.test.d.ts +2 -0
  27. package/dist/__tests__/realtime-agent-picker-models.test.d.ts.map +1 -0
  28. package/dist/__tests__/realtime-agent-picker-models.test.js +49 -0
  29. package/dist/__tests__/realtime-agent-picker-models.test.js.map +1 -0
  30. package/dist/__tests__/realtime-audio-visuals.test.d.ts +2 -0
  31. package/dist/__tests__/realtime-audio-visuals.test.d.ts.map +1 -0
  32. package/dist/__tests__/realtime-audio-visuals.test.js +123 -0
  33. package/dist/__tests__/realtime-audio-visuals.test.js.map +1 -0
  34. package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts +2 -0
  35. package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts.map +1 -0
  36. package/dist/__tests__/realtime-delegation-card-cancel.test.js +48 -0
  37. package/dist/__tests__/realtime-delegation-card-cancel.test.js.map +1 -0
  38. package/dist/__tests__/realtime-disclosure.test.d.ts +2 -0
  39. package/dist/__tests__/realtime-disclosure.test.d.ts.map +1 -0
  40. package/dist/__tests__/realtime-disclosure.test.js +164 -0
  41. package/dist/__tests__/realtime-disclosure.test.js.map +1 -0
  42. package/dist/__tests__/realtime-pairing.test.d.ts +2 -0
  43. package/dist/__tests__/realtime-pairing.test.d.ts.map +1 -0
  44. package/dist/__tests__/realtime-pairing.test.js +207 -0
  45. package/dist/__tests__/realtime-pairing.test.js.map +1 -0
  46. package/dist/__tests__/realtime-review-lifecycle.test.d.ts +2 -0
  47. package/dist/__tests__/realtime-review-lifecycle.test.d.ts.map +1 -0
  48. package/dist/__tests__/realtime-review-lifecycle.test.js +154 -0
  49. package/dist/__tests__/realtime-review-lifecycle.test.js.map +1 -0
  50. package/dist/__tests__/realtime-session-cancel-usage.test.d.ts +2 -0
  51. package/dist/__tests__/realtime-session-cancel-usage.test.d.ts.map +1 -0
  52. package/dist/__tests__/realtime-session-cancel-usage.test.js +230 -0
  53. package/dist/__tests__/realtime-session-cancel-usage.test.js.map +1 -0
  54. package/dist/__tests__/realtime-session-channels.test.d.ts +2 -0
  55. package/dist/__tests__/realtime-session-channels.test.d.ts.map +1 -0
  56. package/dist/__tests__/realtime-session-channels.test.js +252 -0
  57. package/dist/__tests__/realtime-session-channels.test.js.map +1 -0
  58. package/dist/__tests__/realtime-session-client-tools.test.d.ts +2 -0
  59. package/dist/__tests__/realtime-session-client-tools.test.d.ts.map +1 -0
  60. package/dist/__tests__/realtime-session-client-tools.test.js +103 -0
  61. package/dist/__tests__/realtime-session-client-tools.test.js.map +1 -0
  62. package/dist/__tests__/realtime-session-minimized.test.d.ts +2 -0
  63. package/dist/__tests__/realtime-session-minimized.test.d.ts.map +1 -0
  64. package/dist/__tests__/realtime-session-minimized.test.js +32 -0
  65. package/dist/__tests__/realtime-session-minimized.test.js.map +1 -0
  66. package/dist/__tests__/realtime-session-mint.test.d.ts +2 -0
  67. package/dist/__tests__/realtime-session-mint.test.d.ts.map +1 -0
  68. package/dist/__tests__/realtime-session-mint.test.js +69 -0
  69. package/dist/__tests__/realtime-session-mint.test.js.map +1 -0
  70. package/dist/__tests__/realtime-session-policy.test.d.ts +2 -0
  71. package/dist/__tests__/realtime-session-policy.test.d.ts.map +1 -0
  72. package/dist/__tests__/realtime-session-policy.test.js +303 -0
  73. package/dist/__tests__/realtime-session-policy.test.js.map +1 -0
  74. package/dist/__tests__/realtime-session-review.service.test.d.ts +2 -0
  75. package/dist/__tests__/realtime-session-review.service.test.d.ts.map +1 -0
  76. package/dist/__tests__/realtime-session-review.service.test.js +743 -0
  77. package/dist/__tests__/realtime-session-review.service.test.js.map +1 -0
  78. package/dist/__tests__/realtime-session-state.test.d.ts +2 -0
  79. package/dist/__tests__/realtime-session-state.test.d.ts.map +1 -0
  80. package/dist/__tests__/realtime-session-state.test.js +83 -0
  81. package/dist/__tests__/realtime-session-state.test.js.map +1 -0
  82. package/dist/__tests__/realtime-session-timeline-card.test.d.ts +2 -0
  83. package/dist/__tests__/realtime-session-timeline-card.test.d.ts.map +1 -0
  84. package/dist/__tests__/realtime-session-timeline-card.test.js +106 -0
  85. package/dist/__tests__/realtime-session-timeline-card.test.js.map +1 -0
  86. package/dist/__tests__/realtime-session-timeline.test.d.ts +2 -0
  87. package/dist/__tests__/realtime-session-timeline.test.d.ts.map +1 -0
  88. package/dist/__tests__/realtime-session-timeline.test.js +142 -0
  89. package/dist/__tests__/realtime-session-timeline.test.js.map +1 -0
  90. package/dist/__tests__/realtime-sessions-adapter.test.d.ts +19 -0
  91. package/dist/__tests__/realtime-sessions-adapter.test.d.ts.map +1 -0
  92. package/dist/__tests__/realtime-sessions-adapter.test.js +188 -0
  93. package/dist/__tests__/realtime-sessions-adapter.test.js.map +1 -0
  94. package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts +2 -0
  95. package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts.map +1 -0
  96. package/dist/__tests__/realtime-surface-panel-prefs.test.js +100 -0
  97. package/dist/__tests__/realtime-surface-panel-prefs.test.js.map +1 -0
  98. package/dist/__tests__/realtime-surface-tabs-model.test.d.ts +2 -0
  99. package/dist/__tests__/realtime-surface-tabs-model.test.d.ts.map +1 -0
  100. package/dist/__tests__/realtime-surface-tabs-model.test.js +193 -0
  101. package/dist/__tests__/realtime-surface-tabs-model.test.js.map +1 -0
  102. package/dist/__tests__/remote-browser-audio-player.test.d.ts +2 -0
  103. package/dist/__tests__/remote-browser-audio-player.test.d.ts.map +1 -0
  104. package/dist/__tests__/remote-browser-audio-player.test.js +137 -0
  105. package/dist/__tests__/remote-browser-audio-player.test.js.map +1 -0
  106. package/dist/__tests__/remote-browser-channel.test.d.ts +2 -0
  107. package/dist/__tests__/remote-browser-channel.test.d.ts.map +1 -0
  108. package/dist/__tests__/remote-browser-channel.test.js +423 -0
  109. package/dist/__tests__/remote-browser-channel.test.js.map +1 -0
  110. package/dist/__tests__/slot-defaults.test.d.ts +24 -0
  111. package/dist/__tests__/slot-defaults.test.d.ts.map +1 -0
  112. package/dist/__tests__/slot-defaults.test.js +63 -0
  113. package/dist/__tests__/slot-defaults.test.js.map +1 -0
  114. package/dist/__tests__/user-authorization.test.d.ts +2 -0
  115. package/dist/__tests__/user-authorization.test.d.ts.map +1 -0
  116. package/dist/__tests__/user-authorization.test.js +97 -0
  117. package/dist/__tests__/user-authorization.test.js.map +1 -0
  118. package/dist/__tests__/voice-session-narration.test.d.ts +2 -0
  119. package/dist/__tests__/voice-session-narration.test.d.ts.map +1 -0
  120. package/dist/__tests__/voice-session-narration.test.js +609 -0
  121. package/dist/__tests__/voice-session-narration.test.js.map +1 -0
  122. package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts +2 -0
  123. package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts.map +1 -0
  124. package/dist/__tests__/whiteboard-artifact-viewer.test.js +101 -0
  125. package/dist/__tests__/whiteboard-artifact-viewer.test.js.map +1 -0
  126. package/dist/__tests__/whiteboard-channel.test.d.ts +2 -0
  127. package/dist/__tests__/whiteboard-channel.test.d.ts.map +1 -0
  128. package/dist/__tests__/whiteboard-channel.test.js +260 -0
  129. package/dist/__tests__/whiteboard-channel.test.js.map +1 -0
  130. package/dist/__tests__/whiteboard-restore-state.test.d.ts +2 -0
  131. package/dist/__tests__/whiteboard-restore-state.test.d.ts.map +1 -0
  132. package/dist/__tests__/whiteboard-restore-state.test.js +108 -0
  133. package/dist/__tests__/whiteboard-restore-state.test.js.map +1 -0
  134. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +205 -3
  135. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
  136. package/dist/lib/components/conversation/conversation-chat-area.component.js +911 -342
  137. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
  138. package/dist/lib/components/mention/mention-dropdown.component.js +35 -17
  139. package/dist/lib/components/mention/mention-dropdown.component.js.map +1 -1
  140. package/dist/lib/components/mention/mention-editor.component.d.ts +4 -0
  141. package/dist/lib/components/mention/mention-editor.component.d.ts.map +1 -1
  142. package/dist/lib/components/mention/mention-editor.component.js +43 -19
  143. package/dist/lib/components/mention/mention-editor.component.js.map +1 -1
  144. package/dist/lib/components/message/message-input-box.component.d.ts +17 -1
  145. package/dist/lib/components/message/message-input-box.component.d.ts.map +1 -1
  146. package/dist/lib/components/message/message-input-box.component.js +73 -15
  147. package/dist/lib/components/message/message-input-box.component.js.map +1 -1
  148. package/dist/lib/components/message/message-input.component.d.ts +142 -6
  149. package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
  150. package/dist/lib/components/message/message-input.component.js +328 -82
  151. package/dist/lib/components/message/message-input.component.js.map +1 -1
  152. package/dist/lib/components/message/message-item.component.d.ts +28 -3
  153. package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
  154. package/dist/lib/components/message/message-item.component.js +180 -108
  155. package/dist/lib/components/message/message-item.component.js.map +1 -1
  156. package/dist/lib/components/message/message-list.component.d.ts +81 -2
  157. package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
  158. package/dist/lib/components/message/message-list.component.js +252 -87
  159. package/dist/lib/components/message/message-list.component.js.map +1 -1
  160. package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts +282 -0
  161. package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts.map +1 -0
  162. package/dist/lib/components/realtime/channels/base-realtime-channel-client.js +158 -0
  163. package/dist/lib/components/realtime/channels/base-realtime-channel-client.js.map +1 -0
  164. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts +25 -0
  165. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts.map +1 -0
  166. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.js +140 -0
  167. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.js.map +1 -0
  168. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts +35 -0
  169. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts.map +1 -0
  170. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js +58 -0
  171. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js.map +1 -0
  172. package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts +63 -0
  173. package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts.map +1 -0
  174. package/dist/lib/components/realtime/realtime-activity-rail.component.js +260 -0
  175. package/dist/lib/components/realtime/realtime-activity-rail.component.js.map +1 -0
  176. package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +117 -0
  177. package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -0
  178. package/dist/lib/components/realtime/realtime-agent-banner.component.js +504 -0
  179. package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -0
  180. package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts +168 -0
  181. package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts.map +1 -0
  182. package/dist/lib/components/realtime/realtime-agent-picker.component.js +556 -0
  183. package/dist/lib/components/realtime/realtime-agent-picker.component.js.map +1 -0
  184. package/dist/lib/components/realtime/realtime-audio-visuals.d.ts +97 -0
  185. package/dist/lib/components/realtime/realtime-audio-visuals.d.ts.map +1 -0
  186. package/dist/lib/components/realtime/realtime-audio-visuals.js +139 -0
  187. package/dist/lib/components/realtime/realtime-audio-visuals.js.map +1 -0
  188. package/dist/lib/components/realtime/realtime-channel-strip.component.d.ts +29 -0
  189. package/dist/lib/components/realtime/realtime-channel-strip.component.d.ts.map +1 -0
  190. package/dist/lib/components/realtime/realtime-channel-strip.component.js +69 -0
  191. package/dist/lib/components/realtime/realtime-channel-strip.component.js.map +1 -0
  192. package/dist/lib/components/realtime/realtime-composer.component.d.ts +65 -0
  193. package/dist/lib/components/realtime/realtime-composer.component.d.ts.map +1 -0
  194. package/dist/lib/components/realtime/realtime-composer.component.js +256 -0
  195. package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -0
  196. package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts +71 -0
  197. package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts.map +1 -0
  198. package/dist/lib/components/realtime/realtime-delegation-card.component.js +324 -0
  199. package/dist/lib/components/realtime/realtime-delegation-card.component.js.map +1 -0
  200. package/dist/lib/components/realtime/realtime-disclosure.d.ts +135 -0
  201. package/dist/lib/components/realtime/realtime-disclosure.d.ts.map +1 -0
  202. package/dist/lib/components/realtime/realtime-disclosure.js +188 -0
  203. package/dist/lib/components/realtime/realtime-disclosure.js.map +1 -0
  204. package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +491 -0
  205. package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -0
  206. package/dist/lib/components/realtime/realtime-session-overlay.component.js +1274 -0
  207. package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -0
  208. package/dist/lib/components/realtime/realtime-session-state.d.ts +191 -0
  209. package/dist/lib/components/realtime/realtime-session-state.d.ts.map +1 -0
  210. package/dist/lib/components/realtime/realtime-session-state.js +244 -0
  211. package/dist/lib/components/realtime/realtime-session-state.js.map +1 -0
  212. package/dist/lib/components/realtime/realtime-session-thread.component.d.ts +56 -0
  213. package/dist/lib/components/realtime/realtime-session-thread.component.d.ts.map +1 -0
  214. package/dist/lib/components/realtime/realtime-session-thread.component.js +246 -0
  215. package/dist/lib/components/realtime/realtime-session-thread.component.js.map +1 -0
  216. package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts +51 -0
  217. package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts.map +1 -0
  218. package/dist/lib/components/realtime/realtime-session-timeline-card.component.js +193 -0
  219. package/dist/lib/components/realtime/realtime-session-timeline-card.component.js.map +1 -0
  220. package/dist/lib/components/realtime/realtime-surface-panel-prefs.d.ts +77 -0
  221. package/dist/lib/components/realtime/realtime-surface-panel-prefs.d.ts.map +1 -0
  222. package/dist/lib/components/realtime/realtime-surface-panel-prefs.js +114 -0
  223. package/dist/lib/components/realtime/realtime-surface-panel-prefs.js.map +1 -0
  224. package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts +173 -0
  225. package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts.map +1 -0
  226. package/dist/lib/components/realtime/realtime-surface-tabs.component.js +496 -0
  227. package/dist/lib/components/realtime/realtime-surface-tabs.component.js.map +1 -0
  228. package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts +181 -0
  229. package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts.map +1 -0
  230. package/dist/lib/components/realtime/realtime-surface-tabs.model.js +223 -0
  231. package/dist/lib/components/realtime/realtime-surface-tabs.model.js.map +1 -0
  232. package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.d.ts +163 -0
  233. package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.d.ts.map +1 -0
  234. package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.js +309 -0
  235. package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.js.map +1 -0
  236. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts +168 -0
  237. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts.map +1 -0
  238. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js +524 -0
  239. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js.map +1 -0
  240. package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.d.ts +346 -0
  241. package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.d.ts.map +1 -0
  242. package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.js +851 -0
  243. package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.js.map +1 -0
  244. package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts +86 -0
  245. package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts.map +1 -0
  246. package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js +210 -0
  247. package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js.map +1 -0
  248. package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.d.ts +48 -0
  249. package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.d.ts.map +1 -0
  250. package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.js +180 -0
  251. package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.js.map +1 -0
  252. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts +119 -0
  253. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts.map +1 -0
  254. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js +274 -0
  255. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js.map +1 -0
  256. package/dist/lib/components/slots/mj-chat-agent-presence-default.component.d.ts +11 -0
  257. package/dist/lib/components/slots/mj-chat-agent-presence-default.component.d.ts.map +1 -0
  258. package/dist/lib/components/slots/mj-chat-agent-presence-default.component.js +98 -0
  259. package/dist/lib/components/slots/mj-chat-agent-presence-default.component.js.map +1 -0
  260. package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.d.ts +9 -0
  261. package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.d.ts.map +1 -0
  262. package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.js +35 -0
  263. package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.js.map +1 -0
  264. package/dist/lib/components/slots/mj-chat-empty-state-default.component.d.ts +28 -0
  265. package/dist/lib/components/slots/mj-chat-empty-state-default.component.d.ts.map +1 -0
  266. package/dist/lib/components/slots/mj-chat-empty-state-default.component.js +104 -0
  267. package/dist/lib/components/slots/mj-chat-empty-state-default.component.js.map +1 -0
  268. package/dist/lib/components/slots/mj-chat-header-default.component.d.ts +11 -0
  269. package/dist/lib/components/slots/mj-chat-header-default.component.d.ts.map +1 -0
  270. package/dist/lib/components/slots/mj-chat-header-default.component.js +103 -0
  271. package/dist/lib/components/slots/mj-chat-header-default.component.js.map +1 -0
  272. package/dist/lib/components/slots/mj-chat-message-bubble-default.component.d.ts +15 -0
  273. package/dist/lib/components/slots/mj-chat-message-bubble-default.component.d.ts.map +1 -0
  274. package/dist/lib/components/slots/mj-chat-message-bubble-default.component.js +73 -0
  275. package/dist/lib/components/slots/mj-chat-message-bubble-default.component.js.map +1 -0
  276. package/dist/lib/components/slots/mj-chat-message-extra-default.component.d.ts +9 -0
  277. package/dist/lib/components/slots/mj-chat-message-extra-default.component.d.ts.map +1 -0
  278. package/dist/lib/components/slots/mj-chat-message-extra-default.component.js +34 -0
  279. package/dist/lib/components/slots/mj-chat-message-extra-default.component.js.map +1 -0
  280. package/dist/lib/components/slots/slot-interfaces.d.ts +95 -0
  281. package/dist/lib/components/slots/slot-interfaces.d.ts.map +1 -0
  282. package/dist/lib/components/slots/slot-interfaces.js +18 -0
  283. package/dist/lib/components/slots/slot-interfaces.js.map +1 -0
  284. package/dist/lib/components/workspace/conversation-workspace.component.d.ts +11 -0
  285. package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
  286. package/dist/lib/components/workspace/conversation-workspace.component.js +28 -4
  287. package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
  288. package/dist/lib/conversations.module.d.ts +12 -1
  289. package/dist/lib/conversations.module.d.ts.map +1 -1
  290. package/dist/lib/conversations.module.js +93 -5
  291. package/dist/lib/conversations.module.js.map +1 -1
  292. package/dist/lib/directives/chat-slot.directive.d.ts +44 -0
  293. package/dist/lib/directives/chat-slot.directive.d.ts.map +1 -0
  294. package/dist/lib/directives/chat-slot.directive.js +54 -0
  295. package/dist/lib/directives/chat-slot.directive.js.map +1 -0
  296. package/dist/lib/events/chat-events.d.ts +137 -0
  297. package/dist/lib/events/chat-events.d.ts.map +1 -0
  298. package/dist/lib/events/chat-events.js +189 -0
  299. package/dist/lib/events/chat-events.js.map +1 -0
  300. package/dist/lib/models/conversation-state.model.d.ts +2 -1
  301. package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
  302. package/dist/lib/models/conversation-state.model.js.map +1 -1
  303. package/dist/lib/services/artifact-state.service.d.ts.map +1 -1
  304. package/dist/lib/services/artifact-state.service.js +23 -6
  305. package/dist/lib/services/artifact-state.service.js.map +1 -1
  306. package/dist/lib/services/conversation-agent.service.d.ts +60 -74
  307. package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
  308. package/dist/lib/services/conversation-agent.service.js +100 -313
  309. package/dist/lib/services/conversation-agent.service.js.map +1 -1
  310. package/dist/lib/services/conversation-bridge.service.d.ts +11 -70
  311. package/dist/lib/services/conversation-bridge.service.d.ts.map +1 -1
  312. package/dist/lib/services/conversation-bridge.service.js +51 -85
  313. package/dist/lib/services/conversation-bridge.service.js.map +1 -1
  314. package/dist/lib/services/conversation-naming.d.ts +63 -0
  315. package/dist/lib/services/conversation-naming.d.ts.map +1 -0
  316. package/dist/lib/services/conversation-naming.js +58 -0
  317. package/dist/lib/services/conversation-naming.js.map +1 -0
  318. package/dist/lib/services/conversation-streaming.service.d.ts +24 -154
  319. package/dist/lib/services/conversation-streaming.service.d.ts.map +1 -1
  320. package/dist/lib/services/conversation-streaming.service.js +39 -361
  321. package/dist/lib/services/conversation-streaming.service.js.map +1 -1
  322. package/dist/lib/services/conversations-runtime-bootstrap.service.d.ts +10 -0
  323. package/dist/lib/services/conversations-runtime-bootstrap.service.d.ts.map +1 -0
  324. package/dist/lib/services/conversations-runtime-bootstrap.service.js +104 -0
  325. package/dist/lib/services/conversations-runtime-bootstrap.service.js.map +1 -0
  326. package/dist/lib/services/delegation-result-parser.d.ts +45 -0
  327. package/dist/lib/services/delegation-result-parser.d.ts.map +1 -0
  328. package/dist/lib/services/delegation-result-parser.js +48 -0
  329. package/dist/lib/services/delegation-result-parser.js.map +1 -0
  330. package/dist/lib/services/mention-autocomplete.service.d.ts +19 -4
  331. package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
  332. package/dist/lib/services/mention-autocomplete.service.js +65 -4
  333. package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
  334. package/dist/lib/services/mention-parser.service.d.ts +8 -53
  335. package/dist/lib/services/mention-parser.service.d.ts.map +1 -1
  336. package/dist/lib/services/mention-parser.service.js +32 -243
  337. package/dist/lib/services/mention-parser.service.js.map +1 -1
  338. package/dist/lib/services/narration-template.d.ts +42 -0
  339. package/dist/lib/services/narration-template.d.ts.map +1 -0
  340. package/dist/lib/services/narration-template.js +73 -0
  341. package/dist/lib/services/narration-template.js.map +1 -0
  342. package/dist/lib/services/realtime-pairing.d.ts +120 -0
  343. package/dist/lib/services/realtime-pairing.d.ts.map +1 -0
  344. package/dist/lib/services/realtime-pairing.js +150 -0
  345. package/dist/lib/services/realtime-pairing.js.map +1 -0
  346. package/dist/lib/services/realtime-session-review.service.d.ts +233 -0
  347. package/dist/lib/services/realtime-session-review.service.d.ts.map +1 -0
  348. package/dist/lib/services/realtime-session-review.service.js +417 -0
  349. package/dist/lib/services/realtime-session-review.service.js.map +1 -0
  350. package/dist/lib/services/realtime-session.service.d.ts +739 -0
  351. package/dist/lib/services/realtime-session.service.d.ts.map +1 -0
  352. package/dist/lib/services/realtime-session.service.js +1647 -0
  353. package/dist/lib/services/realtime-session.service.js.map +1 -0
  354. package/dist/lib/services/realtime-sessions-adapter.d.ts +54 -0
  355. package/dist/lib/services/realtime-sessions-adapter.d.ts.map +1 -0
  356. package/dist/lib/services/realtime-sessions-adapter.js +154 -0
  357. package/dist/lib/services/realtime-sessions-adapter.js.map +1 -0
  358. package/dist/lib/services/user-authorization.d.ts +67 -0
  359. package/dist/lib/services/user-authorization.d.ts.map +1 -0
  360. package/dist/lib/services/user-authorization.js +66 -0
  361. package/dist/lib/services/user-authorization.js.map +1 -0
  362. package/dist/lib/utils/realtime-session-timeline.d.ts +84 -0
  363. package/dist/lib/utils/realtime-session-timeline.d.ts.map +1 -0
  364. package/dist/lib/utils/realtime-session-timeline.js +94 -0
  365. package/dist/lib/utils/realtime-session-timeline.js.map +1 -0
  366. package/dist/public-api.d.ts +41 -0
  367. package/dist/public-api.d.ts.map +1 -1
  368. package/dist/public-api.js +50 -0
  369. package/dist/public-api.js.map +1 -1
  370. package/package.json +27 -24
  371. package/dist/__tests__/conversation-bridge.service.test.d.ts +0 -2
  372. package/dist/__tests__/conversation-bridge.service.test.d.ts.map +0 -1
  373. package/dist/__tests__/conversation-bridge.service.test.js +0 -98
  374. package/dist/__tests__/conversation-bridge.service.test.js.map +0 -1
  375. package/dist/__tests__/mention-parser.test.d.ts +0 -2
  376. package/dist/__tests__/mention-parser.test.d.ts.map +0 -1
  377. package/dist/__tests__/mention-parser.test.js +0 -154
  378. package/dist/__tests__/mention-parser.test.js.map +0 -1
@@ -0,0 +1,609 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { Subject } from 'rxjs';
3
+ import { RealtimeSessionService } from '../lib/services/realtime-session.service';
4
+ /**
5
+ * The NARRATION PACING / AGGREGATION state machine — the provider-agnostic policy that
6
+ * decides WHEN the realtime model is asked to speak an interim progress update while a
7
+ * delegated tool call runs, and WHAT digest it speaks:
8
+ *
9
+ * - first spoken update no earlier than ~5s into a delegation burst (FirstNarrationDelayMs),
10
+ * - ≥8s between spoken updates, SESSION-global (NarrationIntervalMs — sequential tool calls
11
+ * must never re-arm the faster first-update path),
12
+ * - floods of progress AGGREGATE into one ' → ' digest (max 4, oldest dropped),
13
+ * - busy / audio-playing fire moments RETRY at 1.5s with the buffer intact,
14
+ * - the buffer is DISCARDED when the result lands first (never narrate over the answer),
15
+ * - what the model actually SAID is chained into the next instructions (≤3, anti-repeat).
16
+ *
17
+ * The machine lives behind private members, so these tests use the same narrow typed-seam
18
+ * approach as the channels/client-tools suites: a fake realtime client capturing
19
+ * `SendContextNote` / `RequestSpokenUpdate`, direct invocation of the private handlers
20
+ * (`handleToolCall`, `dispatchProgress`, `onClientTranscript`), and a fake Provider whose
21
+ * `ExecuteGQL` returns controllable deferred promises so tool calls stay in-flight exactly
22
+ * as long as each test needs. `vi.useFakeTimers` (timers + Date) walks the 5s/8s/1.5s
23
+ * schedule deterministically.
24
+ */
25
+ // ── Test doubles ──────────────────────────────────────────────────────────────
26
+ class FakeRealtimeClient {
27
+ ContextNotes = [];
28
+ SpokenUpdates = [];
29
+ ToolResults = [];
30
+ IsBusy = false;
31
+ IsAudioPlaying = false;
32
+ SendContextNote(text) {
33
+ this.ContextNotes.push(text);
34
+ }
35
+ RequestSpokenUpdate(instructions) {
36
+ this.SpokenUpdates.push(instructions);
37
+ }
38
+ SendToolResult(callId, resultJson) {
39
+ this.ToolResults.push({ CallID: callId, ResultJson: resultJson });
40
+ }
41
+ async Disconnect() {
42
+ // no-op for teardown
43
+ }
44
+ }
45
+ function internals(service) {
46
+ return service;
47
+ }
48
+ function createDeferred() {
49
+ let resolve;
50
+ let reject;
51
+ const promise = new Promise((res, rej) => {
52
+ resolve = res;
53
+ reject = rej;
54
+ });
55
+ return { promise, resolve, reject };
56
+ }
57
+ function createHarness() {
58
+ const service = new RealtimeSessionService();
59
+ const client = new FakeRealtimeClient();
60
+ const i = internals(service);
61
+ const pendingTools = [];
62
+ const pushStatus$ = new Subject();
63
+ const executeGQL = vi.fn((mutation) => {
64
+ if (mutation.includes('ExecuteRealtimeSessionTool')) {
65
+ const deferred = createDeferred();
66
+ pendingTools.push(deferred);
67
+ return deferred.promise;
68
+ }
69
+ return Promise.resolve({});
70
+ });
71
+ service.Provider = {
72
+ ExecuteGQL: executeGQL,
73
+ sessionId: 'transport-1',
74
+ PushStatusUpdates: () => pushStatus$.asObservable()
75
+ };
76
+ i.client = client;
77
+ i.agentSessionId = 'sess-1';
78
+ return { service, client, i, pendingTools, executeGQL, pushStatus$ };
79
+ }
80
+ /**
81
+ * Starts a server-relayed delegation. The returned promise settles when the call's
82
+ * deferred is resolved/rejected; until then the call is IN-FLIGHT (the realistic state
83
+ * while progress streams in).
84
+ */
85
+ function beginDelegation(h, callId) {
86
+ return h.i.handleToolCall({ CallID: callId, ToolName: 'invoke-target-agent', ArgumentsJson: '{}' });
87
+ }
88
+ /** Resolves the OLDEST unresolved tool deferred with a delegation result and settles the call. */
89
+ async function completeDelegation(h, call, resultJson = '{"success":true,"output":"done"}') {
90
+ const deferred = h.pendingTools.shift();
91
+ if (!deferred) {
92
+ throw new Error('No pending tool execution to complete');
93
+ }
94
+ deferred.resolve({ ExecuteRealtimeSessionTool: resultJson });
95
+ await call;
96
+ }
97
+ function progress(h, callId, message) {
98
+ h.i.dispatchProgress({ CallID: callId, Step: 'subagent_execution', Message: message });
99
+ }
100
+ describe('RealtimeSessionService — narration timing (5s anchor + session-global 8s floor)', () => {
101
+ let h;
102
+ beforeEach(() => {
103
+ vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'Date'] });
104
+ h = createHarness();
105
+ });
106
+ afterEach(() => {
107
+ vi.useRealTimers();
108
+ });
109
+ it('the first spoken update fires no earlier than 5s after the burst started', () => {
110
+ void beginDelegation(h, 'c1');
111
+ progress(h, 'c1', 'warming up');
112
+ vi.advanceTimersByTime(4999);
113
+ expect(h.client.SpokenUpdates).toHaveLength(0);
114
+ vi.advanceTimersByTime(1);
115
+ expect(h.client.SpokenUpdates).toHaveLength(1);
116
+ expect(h.client.SpokenUpdates[0]).toContain('warming up');
117
+ });
118
+ it('anchors the 5s delay at burst START — progress arriving at 6s fires after the 250ms minimum', () => {
119
+ void beginDelegation(h, 'c1');
120
+ vi.advanceTimersByTime(6000); // burst is already 6s old when the first progress lands
121
+ progress(h, 'c1', 'late progress');
122
+ vi.advanceTimersByTime(249);
123
+ expect(h.client.SpokenUpdates).toHaveLength(0);
124
+ vi.advanceTimersByTime(1);
125
+ expect(h.client.SpokenUpdates).toHaveLength(1);
126
+ });
127
+ it('spaces the SECOND spoken update ≥8s after the first', () => {
128
+ void beginDelegation(h, 'c1');
129
+ progress(h, 'c1', 'first');
130
+ vi.advanceTimersByTime(5000);
131
+ expect(h.client.SpokenUpdates).toHaveLength(1);
132
+ progress(h, 'c1', 'second');
133
+ vi.advanceTimersByTime(7999);
134
+ expect(h.client.SpokenUpdates).toHaveLength(1);
135
+ vi.advanceTimersByTime(1);
136
+ expect(h.client.SpokenUpdates).toHaveLength(2);
137
+ expect(h.client.SpokenUpdates[1]).toContain('second');
138
+ });
139
+ it('REGRESSION: a sequential tool call seconds later does NOT re-arm the fast first-update path — the 8s floor is session-global', async () => {
140
+ // Burst 1: narrate once at T+5s, then the result lands.
141
+ const call1 = beginDelegation(h, 'c1');
142
+ progress(h, 'c1', 'burst one');
143
+ vi.advanceTimersByTime(5000); // narration #1 at T=5000
144
+ expect(h.client.SpokenUpdates).toHaveLength(1);
145
+ await completeDelegation(h, call1);
146
+ // Burst 2 starts only 2s later (T=7000). Its 5s anchor would allow T=12000,
147
+ // but the session-global floor (5000 + 8000 = 13000) must win.
148
+ vi.advanceTimersByTime(2000);
149
+ void beginDelegation(h, 'c2');
150
+ progress(h, 'c2', 'burst two');
151
+ vi.advanceTimersByTime(5999); // T=12999 — past the burst anchor, still under the floor
152
+ expect(h.client.SpokenUpdates).toHaveLength(1);
153
+ vi.advanceTimersByTime(1); // T=13000 — exactly 8s after the last narration
154
+ expect(h.client.SpokenUpdates).toHaveLength(2);
155
+ expect(h.client.SpokenUpdates[1]).toContain('burst two');
156
+ });
157
+ it('a new burst resets the per-burst state (count/buffer/tail) but PRESERVES the floor and spoken history', async () => {
158
+ const call1 = beginDelegation(h, 'c1');
159
+ progress(h, 'c1', 'burst one');
160
+ vi.advanceTimersByTime(5000);
161
+ await h.i.onClientTranscript({ Role: 'Assistant', Text: 'I said this already', IsFinal: true, Kind: 'narration' });
162
+ await completeDelegation(h, call1);
163
+ const floorBefore = h.i.lastDelegationNarrationAt;
164
+ expect(floorBefore).toBeGreaterThan(0);
165
+ void beginDelegation(h, 'c2');
166
+ expect(h.i.narrationCount).toBe(0);
167
+ expect(h.i.pendingNarrationMessages).toEqual([]);
168
+ expect(h.i.lastNarratedTail).toBe('');
169
+ expect(h.i.lastDelegationNarrationAt).toBe(floorBefore); // floor NOT reset
170
+ expect(h.i.spokenNarrations).toEqual(['I said this already']); // history NOT reset
171
+ });
172
+ it('the second burst restarts narration numbering at update #1 while chaining the prior utterance', async () => {
173
+ const call1 = beginDelegation(h, 'c1');
174
+ progress(h, 'c1', 'burst one');
175
+ vi.advanceTimersByTime(5000);
176
+ await h.i.onClientTranscript({ Role: 'Assistant', Text: 'Pulling that up now', IsFinal: true, Kind: 'narration' });
177
+ await completeDelegation(h, call1);
178
+ vi.advanceTimersByTime(2000);
179
+ void beginDelegation(h, 'c2');
180
+ progress(h, 'c2', 'burst two');
181
+ vi.advanceTimersByTime(6000); // the 8s session floor elapses
182
+ expect(h.client.SpokenUpdates).toHaveLength(2);
183
+ expect(h.client.SpokenUpdates[1]).toContain('spoken update #1'); // per-burst numbering reset
184
+ expect(h.client.SpokenUpdates[1]).toContain('- "Pulling that up now"'); // history chained across bursts
185
+ });
186
+ it('teardown → new session resets the 8s floor (the next first update honors only the 5s anchor)', async () => {
187
+ const call1 = beginDelegation(h, 'c1');
188
+ progress(h, 'c1', 'old session');
189
+ vi.advanceTimersByTime(5000);
190
+ expect(h.client.SpokenUpdates).toHaveLength(1);
191
+ await completeDelegation(h, call1);
192
+ await h.i.teardown(false);
193
+ // New session on the same service instance: fresh client + session id.
194
+ const freshClient = new FakeRealtimeClient();
195
+ h.i.client = freshClient;
196
+ h.i.agentSessionId = 'sess-2';
197
+ void beginDelegation(h, 'c9');
198
+ progress(h, 'c9', 'new session');
199
+ vi.advanceTimersByTime(4999);
200
+ expect(freshClient.SpokenUpdates).toHaveLength(0);
201
+ vi.advanceTimersByTime(1);
202
+ expect(freshClient.SpokenUpdates).toHaveLength(1); // 5s anchor only — floor was reset
203
+ });
204
+ it('teardownDelegationProgress resets ALL narration state', () => {
205
+ void beginDelegation(h, 'c1');
206
+ progress(h, 'c1', 'm1');
207
+ vi.advanceTimersByTime(5000);
208
+ expect(h.client.SpokenUpdates).toHaveLength(1);
209
+ h.i.spokenNarrations.push('something said');
210
+ progress(h, 'c1', 'm2'); // re-arms the timer + refills the buffer
211
+ h.i.teardownDelegationProgress();
212
+ expect(h.i.inFlightCallIds.size).toBe(0);
213
+ expect(h.i.narrationTimer).toBeNull();
214
+ expect(h.i.pendingNarrationMessages).toEqual([]);
215
+ expect(h.i.lastDelegationNarrationAt).toBe(0);
216
+ expect(h.i.delegationBurstStartedAt).toBe(0);
217
+ expect(h.i.narrationCount).toBe(0);
218
+ expect(h.i.spokenNarrations).toEqual([]);
219
+ expect(h.i.lastNarratedTail).toBe('');
220
+ });
221
+ });
222
+ describe('RealtimeSessionService — narration aggregation (digest buffer)', () => {
223
+ let h;
224
+ beforeEach(() => {
225
+ vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'Date'] });
226
+ h = createHarness();
227
+ });
228
+ afterEach(() => {
229
+ vi.useRealTimers();
230
+ });
231
+ it('a flood of 6 distinct messages becomes ONE spoken digest of the LAST 4, joined " → "', () => {
232
+ void beginDelegation(h, 'c1');
233
+ for (const m of ['m1', 'm2', 'm3', 'm4', 'm5', 'm6']) {
234
+ progress(h, 'c1', m);
235
+ }
236
+ vi.advanceTimersByTime(5000);
237
+ expect(h.client.SpokenUpdates).toHaveLength(1);
238
+ expect(h.client.SpokenUpdates[0]).toContain('m3 → m4 → m5 → m6');
239
+ expect(h.client.SpokenUpdates[0]).not.toContain('m1');
240
+ expect(h.client.SpokenUpdates[0]).not.toContain('m2');
241
+ });
242
+ it('every progress event STILL feeds a context note, even when the digest dedupes/caps', () => {
243
+ void beginDelegation(h, 'c1');
244
+ for (const m of ['m1', 'm1', 'm2', 'm3', 'm4', 'm5', 'm6']) {
245
+ progress(h, 'c1', m);
246
+ }
247
+ expect(h.client.ContextNotes).toHaveLength(7);
248
+ expect(h.client.ContextNotes[0]).toBe('[delegated-agent progress] m1');
249
+ });
250
+ it('an already-buffered duplicate message is not re-buffered', () => {
251
+ void beginDelegation(h, 'c1');
252
+ progress(h, 'c1', 'dup');
253
+ progress(h, 'c1', 'dup');
254
+ progress(h, 'c1', 'next');
255
+ vi.advanceTimersByTime(5000);
256
+ expect(h.client.SpokenUpdates).toHaveLength(1);
257
+ expect(h.client.SpokenUpdates[0]).toContain('dup → next');
258
+ expect(h.client.SpokenUpdates[0]).not.toContain('dup → dup');
259
+ });
260
+ it('a message equal to the LAST NARRATED TAIL is not re-buffered (no empty re-narration)', () => {
261
+ void beginDelegation(h, 'c1');
262
+ progress(h, 'c1', 'still working');
263
+ vi.advanceTimersByTime(5000);
264
+ expect(h.client.SpokenUpdates).toHaveLength(1);
265
+ // The same trailing message arrives again — context note yes, narration no.
266
+ progress(h, 'c1', 'still working');
267
+ expect(h.i.pendingNarrationMessages).toEqual([]);
268
+ expect(h.i.narrationTimer).toBeNull();
269
+ vi.advanceTimersByTime(30000);
270
+ expect(h.client.SpokenUpdates).toHaveLength(1);
271
+ expect(h.client.ContextNotes).toHaveLength(2);
272
+ });
273
+ it('a DISTINCT message after a tail-dedupe re-arms the machine normally', () => {
274
+ void beginDelegation(h, 'c1');
275
+ progress(h, 'c1', 'still working');
276
+ vi.advanceTimersByTime(5000);
277
+ progress(h, 'c1', 'still working'); // tail dedupe
278
+ vi.advanceTimersByTime(20000); // well past the 8s floor
279
+ progress(h, 'c1', 'finishing touches');
280
+ vi.advanceTimersByTime(250); // floor already satisfied → minimum delay
281
+ expect(h.client.SpokenUpdates).toHaveLength(2);
282
+ expect(h.client.SpokenUpdates[1]).toContain('finishing touches');
283
+ });
284
+ it('discards the buffer when the result lands before the fire moment — nothing is spoken', async () => {
285
+ const call = beginDelegation(h, 'c1');
286
+ progress(h, 'c1', 'never spoken');
287
+ vi.advanceTimersByTime(3000);
288
+ await completeDelegation(h, call);
289
+ expect(h.i.narrationTimer).toBeNull();
290
+ expect(h.i.pendingNarrationMessages).toEqual([]);
291
+ vi.advanceTimersByTime(60000);
292
+ expect(h.client.SpokenUpdates).toHaveLength(0);
293
+ });
294
+ it('a result for ONE of several in-flight calls still cancels the pending digest (result speaks next)', async () => {
295
+ const call1 = beginDelegation(h, 'c1');
296
+ void beginDelegation(h, 'c2');
297
+ progress(h, 'c1', 'from c1');
298
+ progress(h, 'c2', 'from c2');
299
+ await completeDelegation(h, call1);
300
+ expect(h.i.pendingNarrationMessages).toEqual([]);
301
+ expect(h.i.inFlightCallIds.has('c2')).toBe(true);
302
+ vi.advanceTimersByTime(60000);
303
+ expect(h.client.SpokenUpdates).toHaveLength(0); // buffer was discarded with the timer
304
+ });
305
+ });
306
+ describe('RealtimeSessionService — busy / audio-playing retry', () => {
307
+ let h;
308
+ beforeEach(() => {
309
+ vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'Date'] });
310
+ h = createHarness();
311
+ });
312
+ afterEach(() => {
313
+ vi.useRealTimers();
314
+ });
315
+ it('a busy fire moment retries 1.5s later with the SAME buffer', () => {
316
+ h.client.IsBusy = true;
317
+ void beginDelegation(h, 'c1');
318
+ progress(h, 'c1', 'kept safe');
319
+ vi.advanceTimersByTime(5000);
320
+ expect(h.client.SpokenUpdates).toHaveLength(0);
321
+ expect(h.i.pendingNarrationMessages).toEqual(['kept safe']); // buffer intact
322
+ h.client.IsBusy = false;
323
+ vi.advanceTimersByTime(1499);
324
+ expect(h.client.SpokenUpdates).toHaveLength(0);
325
+ vi.advanceTimersByTime(1);
326
+ expect(h.client.SpokenUpdates).toHaveLength(1);
327
+ expect(h.client.SpokenUpdates[0]).toContain('kept safe');
328
+ });
329
+ it('keeps retrying while the model stays busy, then fires once when it frees up', () => {
330
+ h.client.IsBusy = true;
331
+ void beginDelegation(h, 'c1');
332
+ progress(h, 'c1', 'patient update');
333
+ vi.advanceTimersByTime(5000); // initial fire → busy
334
+ vi.advanceTimersByTime(1500); // retry 1 → busy
335
+ vi.advanceTimersByTime(1500); // retry 2 → busy
336
+ expect(h.client.SpokenUpdates).toHaveLength(0);
337
+ h.client.IsBusy = false;
338
+ vi.advanceTimersByTime(1500); // retry 3 → fires
339
+ expect(h.client.SpokenUpdates).toHaveLength(1);
340
+ });
341
+ it('progress arriving during the retry window joins the digest', () => {
342
+ h.client.IsBusy = true;
343
+ void beginDelegation(h, 'c1');
344
+ progress(h, 'c1', 'part one');
345
+ vi.advanceTimersByTime(5000); // busy → retry armed
346
+ progress(h, 'c1', 'part two'); // buffered while waiting
347
+ h.client.IsBusy = false;
348
+ vi.advanceTimersByTime(1500);
349
+ expect(h.client.SpokenUpdates).toHaveLength(1);
350
+ expect(h.client.SpokenUpdates[0]).toContain('part one → part two');
351
+ });
352
+ it('IsAudioPlaying gates exactly like IsBusy', () => {
353
+ h.client.IsAudioPlaying = true;
354
+ void beginDelegation(h, 'c1');
355
+ progress(h, 'c1', 'audio gate');
356
+ vi.advanceTimersByTime(5000);
357
+ expect(h.client.SpokenUpdates).toHaveLength(0);
358
+ h.client.IsAudioPlaying = false;
359
+ vi.advanceTimersByTime(1500);
360
+ expect(h.client.SpokenUpdates).toHaveLength(1);
361
+ });
362
+ it('a result landing during the retry window cancels the retry (nothing spoken)', async () => {
363
+ h.client.IsBusy = true;
364
+ const call = beginDelegation(h, 'c1');
365
+ progress(h, 'c1', 'moot now');
366
+ vi.advanceTimersByTime(5000); // busy → retry armed
367
+ await completeDelegation(h, call);
368
+ h.client.IsBusy = false;
369
+ vi.advanceTimersByTime(60000);
370
+ expect(h.client.SpokenUpdates).toHaveLength(0);
371
+ });
372
+ it('defensive: a fire with the in-flight set EMPTY drops the buffer silently', () => {
373
+ h.i.pendingNarrationMessages.push('orphaned');
374
+ h.i.inFlightCallIds.clear();
375
+ h.i.fireDeferredNarration();
376
+ expect(h.client.SpokenUpdates).toHaveLength(0);
377
+ expect(h.i.pendingNarrationMessages).toEqual([]);
378
+ });
379
+ it('defensive: a fire with NO client drops the buffer without throwing', () => {
380
+ h.i.inFlightCallIds.add('c1');
381
+ h.i.pendingNarrationMessages.push('orphaned');
382
+ h.i.client = null;
383
+ expect(() => h.i.fireDeferredNarration()).not.toThrow();
384
+ expect(h.i.pendingNarrationMessages).toEqual([]);
385
+ });
386
+ it('defensive: a fire with an EMPTY buffer is a no-op (no spoken update, no retry)', () => {
387
+ h.i.inFlightCallIds.add('c1');
388
+ h.i.fireDeferredNarration();
389
+ expect(h.client.SpokenUpdates).toHaveLength(0);
390
+ expect(h.i.narrationTimer).toBeNull();
391
+ });
392
+ });
393
+ describe('RealtimeSessionService — narration instructions + spoken-history capture', () => {
394
+ let h;
395
+ beforeEach(() => {
396
+ vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'Date'] });
397
+ h = createHarness();
398
+ });
399
+ afterEach(() => {
400
+ vi.useRealTimers();
401
+ });
402
+ it('chains what the model ACTUALLY SAID into the next instructions, with per-burst numbering', async () => {
403
+ void beginDelegation(h, 'c1');
404
+ progress(h, 'c1', 'step one');
405
+ vi.advanceTimersByTime(5000);
406
+ expect(h.client.SpokenUpdates[0]).toContain('spoken update #1');
407
+ expect(h.client.SpokenUpdates[0]).toContain('first spoken update'); // "none yet" wording
408
+ await h.i.onClientTranscript({ Role: 'Assistant', Text: 'On it — digging in', IsFinal: true, Kind: 'narration' });
409
+ progress(h, 'c1', 'step two');
410
+ vi.advanceTimersByTime(8000);
411
+ expect(h.client.SpokenUpdates).toHaveLength(2);
412
+ expect(h.client.SpokenUpdates[1]).toContain('spoken update #2');
413
+ expect(h.client.SpokenUpdates[1]).toContain('- "On it — digging in"');
414
+ });
415
+ it('caps the chained prior narrations at the LAST 3', async () => {
416
+ void beginDelegation(h, 'c1');
417
+ for (const said of ['utterance 1', 'utterance 2', 'utterance 3', 'utterance 4']) {
418
+ await h.i.onClientTranscript({ Role: 'Assistant', Text: said, IsFinal: true, Kind: 'narration' });
419
+ }
420
+ expect(h.i.spokenNarrations).toEqual(['utterance 2', 'utterance 3', 'utterance 4']);
421
+ progress(h, 'c1', 'wrap up');
422
+ vi.advanceTimersByTime(5000);
423
+ const instructions = h.client.SpokenUpdates[0];
424
+ expect(instructions).toContain('- "utterance 2"');
425
+ expect(instructions).toContain('- "utterance 4"');
426
+ expect(instructions).not.toContain('utterance 1');
427
+ });
428
+ it('threads digest / update number / prior narrations through a DB-driven template', async () => {
429
+ h.i.narrationTemplate = 'T[{{ progressMessage }}][#{{ updateNumber }}][{{ priorNarrations }}]';
430
+ void beginDelegation(h, 'c1');
431
+ await h.i.onClientTranscript({ Role: 'Assistant', Text: 'prior words', IsFinal: true, Kind: 'narration' });
432
+ progress(h, 'c1', 'alpha');
433
+ progress(h, 'c1', 'beta');
434
+ vi.advanceTimersByTime(5000);
435
+ expect(h.client.SpokenUpdates).toEqual(['T[alpha → beta][#1][- "prior words"]']);
436
+ });
437
+ it('narration-kind final transcripts are EPHEMERAL: emitted on DelegationNarration$, never a caption, never relayed', async () => {
438
+ const narrations = [];
439
+ const captionCounts = [];
440
+ h.service.DelegationNarration$.subscribe(n => narrations.push(n));
441
+ h.service.Captions$.subscribe(c => captionCounts.push(c.length));
442
+ await h.i.onClientTranscript({ Role: 'Assistant', Text: 'ephemeral note', IsFinal: true, Kind: 'narration' });
443
+ expect(narrations).toEqual([{ Text: 'ephemeral note' }]);
444
+ expect(captionCounts).toEqual([0]); // only the BehaviorSubject replay — no caption emission
445
+ expect(h.executeGQL).not.toHaveBeenCalled(); // no RelayRealtimeTranscript
446
+ });
447
+ it('NORMAL assistant final transcripts become captions and are relayed', async () => {
448
+ await h.i.onClientTranscript({ Role: 'Assistant', Text: 'a real answer', IsFinal: true, Kind: 'normal' });
449
+ let captions = [];
450
+ h.service.Captions$.subscribe(c => (captions = c)).unsubscribe();
451
+ expect(captions).toEqual([{ Role: 'Assistant', Text: 'a real answer' }]);
452
+ expect(h.executeGQL).toHaveBeenCalledWith(expect.stringContaining('RelayRealtimeTranscript'), expect.objectContaining({ role: 'assistant', text: 'a real answer' }));
453
+ });
454
+ it('interim (non-final) transcripts are ignored entirely', async () => {
455
+ const narrations = [];
456
+ h.service.DelegationNarration$.subscribe(n => narrations.push(n));
457
+ await h.i.onClientTranscript({ Role: 'Assistant', Text: 'partial…', IsFinal: false, Kind: 'narration' });
458
+ await h.i.onClientTranscript({ Role: 'Assistant', Text: 'partial…', IsFinal: false, Kind: 'normal' });
459
+ expect(narrations).toEqual([]);
460
+ expect(h.i.spokenNarrations).toEqual([]);
461
+ expect(h.executeGQL).not.toHaveBeenCalled();
462
+ });
463
+ });
464
+ describe('RealtimeSessionService — progress dispatch + stale filtering + push-status parsing', () => {
465
+ let h;
466
+ beforeEach(() => {
467
+ vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'Date'] });
468
+ h = createHarness();
469
+ });
470
+ afterEach(() => {
471
+ vi.useRealTimers();
472
+ });
473
+ it('drops STALE progress for a CallID not in flight — no UI emission, no context note, no timer', () => {
474
+ const seen = [];
475
+ h.service.DelegationProgress$.subscribe(p => seen.push(p));
476
+ progress(h, 'never-started', 'stale message');
477
+ expect(seen).toEqual([]);
478
+ expect(h.client.ContextNotes).toEqual([]);
479
+ expect(h.i.narrationTimer).toBeNull();
480
+ });
481
+ it('drops progress that arrives AFTER the call completed (the PubSub-lag case)', async () => {
482
+ const call = beginDelegation(h, 'c1');
483
+ await completeDelegation(h, call);
484
+ const seen = [];
485
+ h.service.DelegationProgress$.subscribe(p => seen.push(p));
486
+ progress(h, 'c1', 'too late');
487
+ expect(seen).toEqual([]);
488
+ expect(h.client.ContextNotes).toEqual([]);
489
+ });
490
+ it('emits in-flight progress on DelegationProgress$ AND feeds the model a context note', () => {
491
+ void beginDelegation(h, 'c1');
492
+ const seen = [];
493
+ h.service.DelegationProgress$.subscribe(p => seen.push(p));
494
+ progress(h, 'c1', 'live update');
495
+ expect(seen).toEqual([{ CallID: 'c1', Step: 'subagent_execution', Message: 'live update' }]);
496
+ expect(h.client.ContextNotes).toEqual(['[delegated-agent progress] live update']);
497
+ });
498
+ it('parses a matching push-status frame end-to-end (raw JSON → DelegationProgress$)', () => {
499
+ h.i.subscribeDelegationProgress();
500
+ void beginDelegation(h, 'c1');
501
+ const seen = [];
502
+ h.service.DelegationProgress$.subscribe(p => seen.push(p));
503
+ h.pushStatus$.next(JSON.stringify({
504
+ resolver: 'RealtimeClientSessionResolver',
505
+ type: 'RealtimeDelegationProgress',
506
+ agentSessionID: 'sess-1',
507
+ callID: 'c1',
508
+ step: 'prompt_execution',
509
+ message: 'thinking hard',
510
+ percentage: 42
511
+ }));
512
+ expect(seen).toEqual([{ CallID: 'c1', Step: 'prompt_execution', Message: 'thinking hard', Percentage: 42 }]);
513
+ });
514
+ it('ignores non-JSON frames and frames with the wrong resolver / type / session', () => {
515
+ h.i.subscribeDelegationProgress();
516
+ void beginDelegation(h, 'c1');
517
+ const seen = [];
518
+ h.service.DelegationProgress$.subscribe(p => seen.push(p));
519
+ h.pushStatus$.next('not json at all');
520
+ h.pushStatus$.next(JSON.stringify({ resolver: 'SomeOtherResolver', type: 'RealtimeDelegationProgress', agentSessionID: 'sess-1', callID: 'c1', step: 's', message: 'x' }));
521
+ h.pushStatus$.next(JSON.stringify({ resolver: 'RealtimeClientSessionResolver', type: 'AgentRunProgress', agentSessionID: 'sess-1', callID: 'c1', step: 's', message: 'x' }));
522
+ h.pushStatus$.next(JSON.stringify({ resolver: 'RealtimeClientSessionResolver', type: 'RealtimeDelegationProgress', agentSessionID: 'OTHER-session', callID: 'c1', step: 's', message: 'x' }));
523
+ expect(seen).toEqual([]);
524
+ expect(h.client.ContextNotes).toEqual([]);
525
+ });
526
+ it('subscribeDelegationProgress is idempotent for a session (one PushStatusUpdates subscription)', () => {
527
+ const pushSpy = vi.spyOn(h.service.Provider, 'PushStatusUpdates');
528
+ h.i.subscribeDelegationProgress();
529
+ h.i.subscribeDelegationProgress();
530
+ expect(pushSpy).toHaveBeenCalledTimes(1);
531
+ });
532
+ });
533
+ describe('RealtimeSessionService — delegation lifecycle (thinking state, results, errors)', () => {
534
+ let h;
535
+ beforeEach(() => {
536
+ vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'Date'] });
537
+ h = createHarness();
538
+ });
539
+ afterEach(() => {
540
+ vi.useRealTimers();
541
+ });
542
+ it('a server-relayed tool call flips the connection state to thinking', () => {
543
+ const states = [];
544
+ h.service.ConnectionState$.subscribe(s => states.push(s));
545
+ void beginDelegation(h, 'c1');
546
+ expect(states[states.length - 1]).toBe('thinking');
547
+ });
548
+ it('emits the parsed delegation result (incl. RunID) and feeds the raw JSON back to the model', async () => {
549
+ const results = [];
550
+ h.service.DelegationResult$.subscribe(r => results.push(r));
551
+ const call = beginDelegation(h, 'c1');
552
+ await completeDelegation(h, call, '{"success":true,"output":"all done","runId":"run-9"}');
553
+ expect(results).toEqual([
554
+ expect.objectContaining({ CallID: 'c1', Success: true, Output: 'all done', RunID: 'run-9' })
555
+ ]);
556
+ expect(h.client.ToolResults).toEqual([
557
+ { CallID: 'c1', ResultJson: '{"success":true,"output":"all done","runId":"run-9"}' }
558
+ ]);
559
+ expect(h.i.inFlightCallIds.size).toBe(0);
560
+ });
561
+ it('a tool-execution failure feeds an error payload to the model and surfaces the error as the result output', async () => {
562
+ vi.spyOn(console, 'error').mockImplementation(() => undefined);
563
+ const results = [];
564
+ h.service.DelegationResult$.subscribe(r => results.push(r));
565
+ const call = beginDelegation(h, 'c1');
566
+ h.pendingTools.shift().reject(new Error('broker down'));
567
+ await call;
568
+ expect(results).toHaveLength(1);
569
+ expect(results[0]).toMatchObject({ CallID: 'c1', Output: 'broker down' });
570
+ // The catch path serializes success:false (matching the server broker's failure
571
+ // shape) so the overlay renders a FAILURE card — a coverage-exposed bug fix.
572
+ expect(results[0].Success).toBe(false);
573
+ expect(JSON.parse(h.client.ToolResults[0].ResultJson)).toEqual({ success: false, error: 'broker down' });
574
+ expect(h.i.inFlightCallIds.size).toBe(0);
575
+ });
576
+ it('a SECOND tool call while one is in flight joins the SAME burst (no anchor reset)', () => {
577
+ void beginDelegation(h, 'c1');
578
+ const burstStart = h.i.delegationBurstStartedAt;
579
+ vi.advanceTimersByTime(3000);
580
+ void beginDelegation(h, 'c2');
581
+ expect(h.i.delegationBurstStartedAt).toBe(burstStart); // inFlight was non-empty → same burst
582
+ expect(h.i.inFlightCallIds.size).toBe(2);
583
+ });
584
+ it('TRUE BARGE-IN cancels pending narration (user took the floor) but leaves delegations in flight', () => {
585
+ // capture the OnInterruption handler through the real wiring path
586
+ let interrupt = null;
587
+ const wiringClient = {
588
+ OnStateChange: () => undefined,
589
+ OnTranscript: () => undefined,
590
+ OnToolCall: () => undefined,
591
+ OnError: () => undefined,
592
+ OnUsage: () => undefined,
593
+ OnInterruption: (handler) => { interrupt = handler; }
594
+ };
595
+ h.i.wireClientHandlers(wiringClient);
596
+ expect(interrupt).not.toBeNull();
597
+ void beginDelegation(h, 'c1');
598
+ h.i.dispatchProgress({ CallID: 'c1', Step: 'action_execution', Message: 'fetching data' });
599
+ expect(h.i.pendingNarrationMessages).toEqual(['fetching data']);
600
+ expect(h.i.narrationTimer).not.toBeNull();
601
+ interrupt();
602
+ expect(h.i.pendingNarrationMessages).toEqual([]); // stale narration discarded
603
+ expect(h.i.narrationTimer).toBeNull();
604
+ expect(h.i.inFlightCallIds.has('c1')).toBe(true); // the delegated run keeps running
605
+ vi.advanceTimersByTime(20000);
606
+ expect(h.client.SpokenUpdates).toEqual([]); // nothing stale ever spoken
607
+ });
608
+ });
609
+ //# sourceMappingURL=voice-session-narration.test.js.map