@memberjunction/server 6.2.0-edge.3 → 6.2.0-edge.4

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 (346) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/agentSessions/BridgeHostInstance.d.ts +25 -0
  3. package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -0
  4. package/dist/agentSessions/BridgeHostInstance.js +30 -0
  5. package/dist/agentSessions/BridgeHostInstance.js.map +1 -0
  6. package/dist/agentSessions/HostInstance.d.ts +75 -11
  7. package/dist/agentSessions/HostInstance.d.ts.map +1 -1
  8. package/dist/agentSessions/HostInstance.js +99 -28
  9. package/dist/agentSessions/HostInstance.js.map +1 -1
  10. package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
  11. package/dist/agentSessions/ReturningVisitorRecap.js +7 -3
  12. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  13. package/dist/agentSessions/SessionJanitor.d.ts +173 -25
  14. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  15. package/dist/agentSessions/SessionJanitor.js +452 -54
  16. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  17. package/dist/agentSessions/SessionManager.d.ts +43 -2
  18. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  19. package/dist/agentSessions/SessionManager.js +99 -8
  20. package/dist/agentSessions/SessionManager.js.map +1 -1
  21. package/dist/agentSessions/SessionRunIDVerifier.d.ts +63 -0
  22. package/dist/agentSessions/SessionRunIDVerifier.d.ts.map +1 -0
  23. package/dist/agentSessions/SessionRunIDVerifier.js +129 -0
  24. package/dist/agentSessions/SessionRunIDVerifier.js.map +1 -0
  25. package/dist/agentSessions/index.d.ts +2 -0
  26. package/dist/agentSessions/index.d.ts.map +1 -1
  27. package/dist/agentSessions/index.js +10 -3
  28. package/dist/agentSessions/index.js.map +1 -1
  29. package/dist/auth/actionAuthorization.d.ts +40 -0
  30. package/dist/auth/actionAuthorization.d.ts.map +1 -0
  31. package/dist/auth/actionAuthorization.js +94 -0
  32. package/dist/auth/actionAuthorization.js.map +1 -0
  33. package/dist/auth/index.d.ts.map +1 -1
  34. package/dist/auth/index.js +3 -10
  35. package/dist/auth/index.js.map +1 -1
  36. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  37. package/dist/auth/magicLink/MagicLinkService.js +6 -3
  38. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  39. package/dist/auth/newUsers.d.ts +10 -0
  40. package/dist/auth/newUsers.d.ts.map +1 -1
  41. package/dist/auth/newUsers.js +80 -11
  42. package/dist/auth/newUsers.js.map +1 -1
  43. package/dist/config.d.ts +2378 -0
  44. package/dist/config.d.ts.map +1 -1
  45. package/dist/config.js +123 -0
  46. package/dist/config.js.map +1 -1
  47. package/dist/context.d.ts +8 -0
  48. package/dist/context.d.ts.map +1 -1
  49. package/dist/context.js +14 -11
  50. package/dist/context.js.map +1 -1
  51. package/dist/generated/graphql-schemas/__mj.d.ts +135 -0
  52. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  53. package/dist/generated/graphql-schemas/__mj.js +706 -0
  54. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  55. package/dist/generic/OrphanedConversationDetailReconciler.js +3 -3
  56. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  57. package/dist/generic/ResolverBase.d.ts +20 -4
  58. package/dist/generic/ResolverBase.d.ts.map +1 -1
  59. package/dist/generic/ResolverBase.js +39 -10
  60. package/dist/generic/ResolverBase.js.map +1 -1
  61. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  62. package/dist/generic/RunViewResolver.js +4 -0
  63. package/dist/generic/RunViewResolver.js.map +1 -1
  64. package/dist/index.d.ts +6 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +120 -131
  67. package/dist/index.js.map +1 -1
  68. package/dist/isolatedProvider.d.ts +20 -0
  69. package/dist/isolatedProvider.d.ts.map +1 -0
  70. package/dist/isolatedProvider.js +22 -0
  71. package/dist/isolatedProvider.js.map +1 -0
  72. package/dist/realtimeProxy/RealtimeProxyServer.d.ts +44 -4
  73. package/dist/realtimeProxy/RealtimeProxyServer.d.ts.map +1 -1
  74. package/dist/realtimeProxy/RealtimeProxyServer.js +121 -16
  75. package/dist/realtimeProxy/RealtimeProxyServer.js.map +1 -1
  76. package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts +171 -0
  77. package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts.map +1 -0
  78. package/dist/realtimeProxy/RealtimeRelayTunnel.js +532 -0
  79. package/dist/realtimeProxy/RealtimeRelayTunnel.js.map +1 -0
  80. package/dist/realtimeSessions/RealtimeSessionEventService.d.ts +148 -0
  81. package/dist/realtimeSessions/RealtimeSessionEventService.d.ts.map +1 -0
  82. package/dist/realtimeSessions/RealtimeSessionEventService.js +191 -0
  83. package/dist/realtimeSessions/RealtimeSessionEventService.js.map +1 -0
  84. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts +66 -0
  85. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -0
  86. package/dist/realtimeSessions/RealtimeSessionVerificationService.js +138 -0
  87. package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -0
  88. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts +38 -0
  89. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -0
  90. package/dist/realtimeSessions/RealtimeVerifyRouter.js +98 -0
  91. package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -0
  92. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts +61 -0
  93. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -0
  94. package/dist/realtimeSessions/SessionVerificationPortsBase.js +149 -0
  95. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -0
  96. package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts +57 -0
  97. package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts.map +1 -0
  98. package/dist/realtimeSessions/SlidingWindowRateLimiter.js +69 -0
  99. package/dist/realtimeSessions/SlidingWindowRateLimiter.js.map +1 -0
  100. package/dist/realtimeSessions/index.d.ts +17 -0
  101. package/dist/realtimeSessions/index.d.ts.map +1 -0
  102. package/dist/realtimeSessions/index.js +17 -0
  103. package/dist/realtimeSessions/index.js.map +1 -0
  104. package/dist/realtimeSessions/sessionAccess.d.ts +86 -0
  105. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -0
  106. package/dist/realtimeSessions/sessionAccess.js +90 -0
  107. package/dist/realtimeSessions/sessionAccess.js.map +1 -0
  108. package/dist/realtimeSessions/sessionConfigStamps.d.ts +70 -0
  109. package/dist/realtimeSessions/sessionConfigStamps.d.ts.map +1 -0
  110. package/dist/realtimeSessions/sessionConfigStamps.js +93 -0
  111. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -0
  112. package/dist/realtimeSessions/subscriptionAuthorization.d.ts +65 -0
  113. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -0
  114. package/dist/realtimeSessions/subscriptionAuthorization.js +71 -0
  115. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -0
  116. package/dist/realtimeSessions/verificationCore.d.ts +243 -0
  117. package/dist/realtimeSessions/verificationCore.d.ts.map +1 -0
  118. package/dist/realtimeSessions/verificationCore.js +356 -0
  119. package/dist/realtimeSessions/verificationCore.js.map +1 -0
  120. package/dist/realtimeSessions/verificationPages.d.ts +66 -0
  121. package/dist/realtimeSessions/verificationPages.d.ts.map +1 -0
  122. package/dist/realtimeSessions/verificationPages.js +110 -0
  123. package/dist/realtimeSessions/verificationPages.js.map +1 -0
  124. package/dist/realtimeSessions/verificationPolicy.d.ts +147 -0
  125. package/dist/realtimeSessions/verificationPolicy.d.ts.map +1 -0
  126. package/dist/realtimeSessions/verificationPolicy.js +249 -0
  127. package/dist/realtimeSessions/verificationPolicy.js.map +1 -0
  128. package/dist/realtimeSessions/verificationWorkflow.d.ts +263 -0
  129. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -0
  130. package/dist/realtimeSessions/verificationWorkflow.js +435 -0
  131. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -0
  132. package/dist/realtimeWidget/widgetGuestElevation.d.ts +20 -2
  133. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  134. package/dist/realtimeWidget/widgetGuestElevation.js +31 -2
  135. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  136. package/dist/resolvers/ActionResolver.d.ts +31 -5
  137. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  138. package/dist/resolvers/ActionResolver.js +75 -14
  139. package/dist/resolvers/ActionResolver.js.map +1 -1
  140. package/dist/resolvers/AdhocQueryResolver.d.ts +14 -1
  141. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  142. package/dist/resolvers/AdhocQueryResolver.js +21 -5
  143. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  144. package/dist/resolvers/CacheStatsResolver.d.ts +27 -0
  145. package/dist/resolvers/CacheStatsResolver.d.ts.map +1 -1
  146. package/dist/resolvers/CacheStatsResolver.js +113 -2
  147. package/dist/resolvers/CacheStatsResolver.js.map +1 -1
  148. package/dist/resolvers/ClientToolRequestResolver.d.ts +4 -2
  149. package/dist/resolvers/ClientToolRequestResolver.d.ts.map +1 -1
  150. package/dist/resolvers/ClientToolRequestResolver.js +28 -10
  151. package/dist/resolvers/ClientToolRequestResolver.js.map +1 -1
  152. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +12 -1
  153. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  154. package/dist/resolvers/FetchEntityVectorsResolver.js +83 -2
  155. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  156. package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -1
  157. package/dist/resolvers/HumanHandoffResolver.js +7 -1
  158. package/dist/resolvers/HumanHandoffResolver.js.map +1 -1
  159. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  160. package/dist/resolvers/MCPResolver.js +9 -4
  161. package/dist/resolvers/MCPResolver.js.map +1 -1
  162. package/dist/resolvers/RealtimeBridgeResolver.d.ts +54 -2
  163. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  164. package/dist/resolvers/RealtimeBridgeResolver.js +155 -10
  165. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  166. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +155 -25
  167. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  168. package/dist/resolvers/RealtimeClientSessionResolver.js +394 -90
  169. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  170. package/dist/resolvers/RealtimeSessionEventsResolver.d.ts +61 -0
  171. package/dist/resolvers/RealtimeSessionEventsResolver.d.ts.map +1 -0
  172. package/dist/resolvers/RealtimeSessionEventsResolver.js +130 -0
  173. package/dist/resolvers/RealtimeSessionEventsResolver.js.map +1 -0
  174. package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts +85 -0
  175. package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts.map +1 -0
  176. package/dist/resolvers/RealtimeSessionVerificationResolver.js +188 -0
  177. package/dist/resolvers/RealtimeSessionVerificationResolver.js.map +1 -0
  178. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +2 -0
  179. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  180. package/dist/resolvers/RemoteBrowserActionResolver.js +5 -1
  181. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  182. package/dist/resolvers/RunAIAgentResolver.d.ts +10 -0
  183. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  184. package/dist/resolvers/RunAIAgentResolver.js +68 -11
  185. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  186. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  187. package/dist/resolvers/RunTemplateResolver.js +12 -11
  188. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  189. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  190. package/dist/resolvers/SyncDataResolver.js +6 -2
  191. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  192. package/dist/resolvers/SyncRolesUsersResolver.d.ts +2 -0
  193. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  194. package/dist/resolvers/SyncRolesUsersResolver.js +23 -6
  195. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  196. package/dist/resolvers/UserAvatarResolver.d.ts +16 -0
  197. package/dist/resolvers/UserAvatarResolver.d.ts.map +1 -0
  198. package/dist/resolvers/UserAvatarResolver.js +124 -0
  199. package/dist/resolvers/UserAvatarResolver.js.map +1 -0
  200. package/dist/resolvers/UserResolver.d.ts.map +1 -1
  201. package/dist/resolvers/UserResolver.js +2 -17
  202. package/dist/resolvers/UserResolver.js.map +1 -1
  203. package/dist/resolvers/agentVisionConsentAudit.d.ts +37 -0
  204. package/dist/resolvers/agentVisionConsentAudit.d.ts.map +1 -0
  205. package/dist/resolvers/agentVisionConsentAudit.js +63 -0
  206. package/dist/resolvers/agentVisionConsentAudit.js.map +1 -0
  207. package/dist/resolvers/avatarInputValidation.d.ts +40 -0
  208. package/dist/resolvers/avatarInputValidation.d.ts.map +1 -0
  209. package/dist/resolvers/avatarInputValidation.js +97 -0
  210. package/dist/resolvers/avatarInputValidation.js.map +1 -0
  211. package/dist/resolvers/currentUserRoles.d.ts +29 -0
  212. package/dist/resolvers/currentUserRoles.d.ts.map +1 -1
  213. package/dist/resolvers/currentUserRoles.js +30 -0
  214. package/dist/resolvers/currentUserRoles.js.map +1 -1
  215. package/dist/resolvers/meetings/meetingMutations.d.ts +6 -0
  216. package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
  217. package/dist/resolvers/meetings/meetingMutations.js +21 -0
  218. package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
  219. package/dist/services/TaskGraphContinuationDeliverer.d.ts +4 -3
  220. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  221. package/dist/services/TaskGraphContinuationDeliverer.js +13 -7
  222. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  223. package/dist/sharedCache.d.ts +105 -0
  224. package/dist/sharedCache.d.ts.map +1 -0
  225. package/dist/sharedCache.js +276 -0
  226. package/dist/sharedCache.js.map +1 -0
  227. package/dist/types.d.ts +6 -0
  228. package/dist/types.d.ts.map +1 -1
  229. package/dist/types.js.map +1 -1
  230. package/package.json +97 -95
  231. package/src/__tests__/ActionResolver.authorization.test.ts +353 -0
  232. package/src/__tests__/AdhocQueryResolver.sqlAuthorizer.test.ts +104 -0
  233. package/src/__tests__/BridgeHostInstance.test.ts +232 -0
  234. package/src/__tests__/ClientToolRequestResolver.test.ts +116 -0
  235. package/src/__tests__/FetchEntityVectorsResolver.test.ts +285 -0
  236. package/src/__tests__/HostInstance.test.ts +84 -0
  237. package/src/__tests__/HumanHandoffResolver.test.ts +17 -0
  238. package/src/__tests__/MeetingResolver.test.ts +75 -1
  239. package/src/__tests__/RealtimeBridgeResolver.test.ts +415 -6
  240. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +383 -0
  241. package/src/__tests__/RealtimeClientSessionResolver.test.ts +682 -58
  242. package/src/__tests__/RealtimeProxyServer.test.ts +16 -1
  243. package/src/__tests__/RealtimeRelay.test.ts +876 -0
  244. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +129 -0
  245. package/src/__tests__/ResolverBase.aggregateScreen.test.ts +174 -0
  246. package/src/__tests__/ResolverBase.clientOldValues.test.ts +6 -3
  247. package/src/__tests__/ReturningVisitorRecap.test.ts +115 -0
  248. package/src/__tests__/RunAIAgentResolver.branch.test.ts +169 -0
  249. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +4 -4
  250. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +166 -0
  251. package/src/__tests__/RunTemplateResolver.test.ts +132 -0
  252. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +63 -0
  253. package/src/__tests__/SessionJanitor.test.ts +845 -18
  254. package/src/__tests__/SessionManager.test.ts +213 -17
  255. package/src/__tests__/SessionRunIDVerifier.test.ts +251 -0
  256. package/src/__tests__/SyncRolesUsersResolver.userRoles.test.ts +97 -0
  257. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +50 -3
  258. package/src/__tests__/UserAvatarResolver.test.ts +255 -0
  259. package/src/__tests__/UserResolver.currentUser.test.ts +16 -1
  260. package/src/__tests__/actionAuthorization.test.ts +130 -0
  261. package/src/__tests__/agentVisionConsentAudit.test.ts +118 -0
  262. package/src/__tests__/avatarInputValidation.test.ts +160 -0
  263. package/src/__tests__/isolatedProviderTransactions.test.ts +169 -0
  264. package/src/__tests__/magicLinkService.consume.test.ts +6 -0
  265. package/src/__tests__/magicLinkService.provisionLog.test.ts +5 -0
  266. package/src/__tests__/newUsers.template.test.ts +151 -0
  267. package/src/__tests__/newUsers.test.ts +74 -5
  268. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +17 -2
  269. package/src/__tests__/realtimeSessions/RealtimeSessionEventService.test.ts +201 -0
  270. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +215 -0
  271. package/src/__tests__/realtimeSessions/RealtimeSessionVerificationResolver.test.ts +102 -0
  272. package/src/__tests__/realtimeSessions/RealtimeSessionVerificationService.test.ts +349 -0
  273. package/src/__tests__/realtimeSessions/RealtimeVerifyRouter.test.ts +131 -0
  274. package/src/__tests__/realtimeSessions/SlidingWindowRateLimiter.test.ts +56 -0
  275. package/src/__tests__/realtimeSessions/helpers.ts +4 -0
  276. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +129 -0
  277. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +124 -0
  278. package/src/__tests__/realtimeSessions/verificationCore.test.ts +355 -0
  279. package/src/__tests__/realtimeSessions/verificationPages.test.ts +84 -0
  280. package/src/__tests__/realtimeSessions/verificationPolicy.test.ts +226 -0
  281. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +633 -0
  282. package/src/__tests__/resolverBase.fls.test.ts +87 -4
  283. package/src/__tests__/serverExtensionReservedRoots.test.ts +2 -0
  284. package/src/__tests__/sharedCache.test.ts +310 -0
  285. package/src/__tests__/widgetGuestElevation.test.ts +44 -0
  286. package/src/agentSessions/BridgeHostInstance.ts +32 -0
  287. package/src/agentSessions/HostInstance.ts +124 -28
  288. package/src/agentSessions/ReturningVisitorRecap.ts +7 -3
  289. package/src/agentSessions/SessionJanitor.ts +543 -57
  290. package/src/agentSessions/SessionManager.ts +125 -25
  291. package/src/agentSessions/SessionRunIDVerifier.ts +171 -0
  292. package/src/agentSessions/index.ts +10 -3
  293. package/src/auth/actionAuthorization.ts +116 -0
  294. package/src/auth/index.ts +3 -8
  295. package/src/auth/magicLink/MagicLinkService.ts +6 -3
  296. package/src/auth/newUsers.ts +99 -11
  297. package/src/config.ts +125 -0
  298. package/src/context.ts +15 -10
  299. package/src/generated/graphql-schemas/__mj.ts +496 -1
  300. package/src/generic/OrphanedConversationDetailReconciler.ts +3 -3
  301. package/src/generic/ResolverBase.ts +39 -9
  302. package/src/generic/RunViewResolver.ts +4 -0
  303. package/src/index.ts +132 -143
  304. package/src/isolatedProvider.ts +22 -0
  305. package/src/realtimeProxy/RealtimeProxyServer.ts +148 -16
  306. package/src/realtimeProxy/RealtimeRelayTunnel.ts +645 -0
  307. package/src/realtimeSessions/README.md +179 -0
  308. package/src/realtimeSessions/RealtimeSessionEventService.ts +274 -0
  309. package/src/realtimeSessions/RealtimeSessionVerificationService.ts +167 -0
  310. package/src/realtimeSessions/RealtimeVerifyRouter.ts +108 -0
  311. package/src/realtimeSessions/SessionVerificationPortsBase.ts +189 -0
  312. package/src/realtimeSessions/SlidingWindowRateLimiter.ts +92 -0
  313. package/src/realtimeSessions/index.ts +16 -0
  314. package/src/realtimeSessions/sessionAccess.ts +141 -0
  315. package/src/realtimeSessions/sessionConfigStamps.ts +123 -0
  316. package/src/realtimeSessions/subscriptionAuthorization.ts +116 -0
  317. package/src/realtimeSessions/verificationCore.ts +502 -0
  318. package/src/realtimeSessions/verificationPages.ts +149 -0
  319. package/src/realtimeSessions/verificationPolicy.ts +321 -0
  320. package/src/realtimeSessions/verificationWorkflow.ts +702 -0
  321. package/src/realtimeWidget/widgetGuestElevation.ts +34 -2
  322. package/src/resolvers/ActionResolver.ts +84 -21
  323. package/src/resolvers/AdhocQueryResolver.ts +24 -6
  324. package/src/resolvers/CacheStatsResolver.ts +89 -2
  325. package/src/resolvers/ClientToolRequestResolver.ts +25 -7
  326. package/src/resolvers/FetchEntityVectorsResolver.ts +97 -2
  327. package/src/resolvers/HumanHandoffResolver.ts +8 -0
  328. package/src/resolvers/MCPResolver.ts +8 -5
  329. package/src/resolvers/RealtimeBridgeResolver.ts +163 -11
  330. package/src/resolvers/RealtimeClientSessionResolver.ts +501 -96
  331. package/src/resolvers/RealtimeSessionEventsResolver.ts +133 -0
  332. package/src/resolvers/RealtimeSessionVerificationResolver.ts +174 -0
  333. package/src/resolvers/RemoteBrowserActionResolver.ts +11 -1
  334. package/src/resolvers/RunAIAgentResolver.ts +78 -11
  335. package/src/resolvers/RunTemplateResolver.ts +13 -12
  336. package/src/resolvers/SyncDataResolver.ts +6 -2
  337. package/src/resolvers/SyncRolesUsersResolver.ts +24 -6
  338. package/src/resolvers/UserAvatarResolver.ts +111 -0
  339. package/src/resolvers/UserResolver.ts +2 -16
  340. package/src/resolvers/agentVisionConsentAudit.ts +82 -0
  341. package/src/resolvers/avatarInputValidation.ts +123 -0
  342. package/src/resolvers/currentUserRoles.ts +32 -1
  343. package/src/resolvers/meetings/meetingMutations.ts +21 -0
  344. package/src/services/TaskGraphContinuationDeliverer.ts +13 -7
  345. package/src/sharedCache.ts +302 -0
  346. package/src/types.ts +7 -0
@@ -0,0 +1,285 @@
1
+ import 'reflect-metadata';
2
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
3
+ import { UserInfo, type DatabaseProviderBase, type EntityInfo, type RunViewParams } from '@memberjunction/core';
4
+ import { MJGlobal } from '@memberjunction/global';
5
+ import type { VectorDBBase } from '@memberjunction/ai-vectordb';
6
+
7
+ type ViewResult = { Success: boolean; Results: unknown[]; ErrorMessage?: string };
8
+ type RunViewHandler = (params: RunViewParams, contextUser: UserInfo | undefined) => ViewResult;
9
+
10
+ // Vitest's transform emits no decorator metadata, so type-graphql can't infer field types —
11
+ // stub the decorators; these tests exercise the resolver method, not the schema.
12
+ vi.mock('type-graphql', () => {
13
+ const decoratorFactory = (..._args: unknown[]) => (..._decorated: unknown[]) => undefined;
14
+ const exportNames = [
15
+ 'Resolver', 'Query', 'Mutation', 'Subscription', 'Arg', 'Args', 'ArgsType', 'Ctx', 'Root',
16
+ 'Info', 'Field', 'FieldResolver', 'ObjectType', 'InputType', 'InterfaceType', 'Authorized',
17
+ 'UseMiddleware', 'Extensions', 'Directive', 'ID', 'Int', 'Float', 'GraphQLISODateTime',
18
+ 'GraphQLTimestamp', 'registerEnumType', 'createMethodDecorator', 'createParamDecorator',
19
+ 'buildSchema', 'buildSchemaSync', 'PubSub',
20
+ ];
21
+ return Object.fromEntries(exportNames.map((name) => [name, decoratorFactory]));
22
+ });
23
+
24
+ const runViewCalls: Array<{ params: RunViewParams; contextUser: UserInfo | undefined }> = [];
25
+ let runViewHandler: RunViewHandler = () => ({ Success: true, Results: [] });
26
+
27
+ vi.mock('@memberjunction/core', async (importOriginal) => {
28
+ const actual = await importOriginal<typeof import('@memberjunction/core')>();
29
+ class MockRunView {
30
+ async RunView(params: RunViewParams, contextUser?: UserInfo): Promise<ViewResult> {
31
+ runViewCalls.push({ params, contextUser });
32
+ return runViewHandler(params, contextUser);
33
+ }
34
+ }
35
+ return { ...actual, RunView: MockRunView, LogStatus: () => undefined, LogError: () => undefined };
36
+ });
37
+
38
+ // GetAIAPIKey resolves through the ClassFactory too; keep it off the CreateInstance spy below.
39
+ vi.mock('@memberjunction/ai', async (importOriginal) => {
40
+ const actual = await importOriginal<typeof import('@memberjunction/ai')>();
41
+ return { ...actual, GetAIAPIKey: () => 'test-key' };
42
+ });
43
+
44
+ vi.mock('@memberjunction/aiengine', async (importOriginal) => {
45
+ const actual = await importOriginal<typeof import('@memberjunction/aiengine')>();
46
+ return { ...actual, AIEngine: { Instance: { GetProviderIndexName: () => 'provider-index-name' } } };
47
+ });
48
+
49
+ import { FetchEntityVectorsResolver } from '../resolvers/FetchEntityVectorsResolver.js';
50
+ import type { AppContext } from '../types.js';
51
+
52
+ /* ───── fixtures ───── */
53
+
54
+ const DOC_ID = 'DOC-1';
55
+ const contextUser = new UserInfo();
56
+
57
+ const membersEntity = {
58
+ Name: 'Members',
59
+ PrimaryKeys: [{ Name: 'ID' }],
60
+ Fields: [
61
+ { Name: 'ID', IsNameField: false },
62
+ { Name: 'FirstName', IsNameField: true },
63
+ { Name: 'LastName', IsNameField: true },
64
+ { Name: 'Email', IsNameField: false },
65
+ ],
66
+ NameField: undefined,
67
+ } as unknown as EntityInfo;
68
+
69
+ const orderLinesEntity = {
70
+ Name: 'Order Lines',
71
+ PrimaryKeys: [{ Name: 'OrderID' }, { Name: 'LineNo' }],
72
+ Fields: [
73
+ { Name: 'OrderID', IsNameField: false },
74
+ { Name: 'LineNo', IsNameField: false },
75
+ { Name: 'Description', IsNameField: false },
76
+ ],
77
+ NameField: { Name: 'Description' },
78
+ } as unknown as EntityInfo;
79
+
80
+ function fakeVectorDB(queryKeyIsEntityDocumentID: boolean) {
81
+ const queryIndex = vi.fn().mockResolvedValue({
82
+ success: true,
83
+ data: { matches: [{ id: 'pc-1', values: [0.5, 0.5], metadata: { Entity: 'Members', RecordID: 'a' } }] },
84
+ });
85
+ const db = { QueryKeyIsEntityDocumentID: queryKeyIsEntityDocumentID, QueryIndex: queryIndex } as unknown as VectorDBBase;
86
+ return { db, queryIndex };
87
+ }
88
+
89
+ function appContext(entityInfo: EntityInfo): AppContext {
90
+ const provider = { EntityByName: (name: string) => (name === entityInfo.Name ? entityInfo : undefined) } as unknown as DatabaseProviderBase;
91
+ return {
92
+ userPayload: { email: 'test@example.com', sessionId: 's', userRecord: contextUser },
93
+ providers: [{ type: 'Read-Only', provider }],
94
+ } as unknown as AppContext;
95
+ }
96
+
97
+ /** Route RunView by entity: the doc → index → database lookups, then the case-specific views. */
98
+ function routeViews(entityName: string, views: Record<string, (params: RunViewParams) => ViewResult>): void {
99
+ runViewHandler = (params) => {
100
+ switch (params.EntityName) {
101
+ case 'MJ: Entity Documents':
102
+ return { Success: true, Results: [{ ID: DOC_ID, Name: 'Doc', Entity: entityName, VectorIndexID: 'VI-1' }] };
103
+ case 'MJ: Vector Indexes':
104
+ return { Success: true, Results: [{ ID: 'VI-1', Name: 'Index', VectorDatabaseID: 'VDB-1', Dimensions: 2 }] };
105
+ case 'MJ: Vector Databases':
106
+ return { Success: true, Results: [{ ID: 'VDB-1', ClassKey: 'TestVectorDB' }] };
107
+ default: {
108
+ const view = views[params.EntityName ?? ''];
109
+ return view ? view(params) : { Success: false, Results: [], ErrorMessage: `unexpected view ${params.EntityName}` };
110
+ }
111
+ }
112
+ };
113
+ }
114
+
115
+ const storedRows = [
116
+ { ID: 'ERD-1', RecordID: 'AAA', VectorJSON: '[0.1,0.2]' },
117
+ { ID: 'ERD-2', RecordID: 'bbb', VectorJSON: '[0.3,0.4]' },
118
+ { ID: 'ERD-3', RecordID: 'ccc', VectorJSON: '[0.5,0.6]' },
119
+ ];
120
+ const memberRows = [
121
+ { ID: 'aaa', FirstName: 'Ada', LastName: 'Lovelace' },
122
+ { ID: 'bbb', FirstName: 'Grace', LastName: 'Hopper' },
123
+ { ID: 'ccc', FirstName: 'Alan', LastName: null },
124
+ ];
125
+
126
+ function callsFor(entityName: string) {
127
+ return runViewCalls.filter((c) => c.params.EntityName === entityName);
128
+ }
129
+
130
+ /* ───── tests ───── */
131
+
132
+ describe('FetchEntityVectorsResolver', () => {
133
+ let createInstanceSpy: ReturnType<typeof vi.spyOn>;
134
+
135
+ beforeEach(() => {
136
+ runViewCalls.length = 0;
137
+ });
138
+
139
+ afterEach(() => {
140
+ createInstanceSpy?.mockRestore();
141
+ });
142
+
143
+ function useVectorDB(queryKeyIsEntityDocumentID: boolean) {
144
+ const fake = fakeVectorDB(queryKeyIsEntityDocumentID);
145
+ createInstanceSpy = vi.spyOn(MJGlobal.Instance.ClassFactory, 'CreateInstance').mockReturnValue(fake.db);
146
+ return fake;
147
+ }
148
+
149
+ describe('local vector store (QueryKeyIsEntityDocumentID)', () => {
150
+ it('reads VectorJSON for the document with the context user instead of calling QueryIndex', async () => {
151
+ const { queryIndex } = useVectorDB(true);
152
+ routeViews('Members', {
153
+ 'MJ: Entity Record Documents': () => ({ Success: true, Results: storedRows }),
154
+ Members: () => ({ Success: true, Results: memberRows }),
155
+ });
156
+
157
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
158
+
159
+ expect(result.ErrorMessage).toBeUndefined();
160
+ expect(result.Success).toBe(true);
161
+ expect(queryIndex).not.toHaveBeenCalled();
162
+ expect(result.TotalCount).toBe(3);
163
+ expect(result.Results.map((r) => r.ID)).toEqual(['ERD-1', 'ERD-2', 'ERD-3']);
164
+ expect(result.Results.map((r) => r.Values)).toEqual([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]);
165
+
166
+ const [erdCall] = callsFor('MJ: Entity Record Documents');
167
+ expect(erdCall.contextUser).toBe(contextUser);
168
+ expect(erdCall.params.ExtraFilter).toBe(`EntityDocumentID='${DOC_ID}' AND VectorJSON IS NOT NULL`);
169
+ expect(erdCall.params.Fields).toEqual(['ID', 'RecordID', 'VectorJSON']);
170
+ expect(erdCall.params.ResultType).toBe('simple');
171
+ expect(erdCall.params.IgnoreMaxRows).toBe(true);
172
+ });
173
+
174
+ it('labels each item with Entity, RecordID and the record name fields (RecordID matched case-insensitively)', async () => {
175
+ useVectorDB(true);
176
+ routeViews('Members', {
177
+ 'MJ: Entity Record Documents': () => ({ Success: true, Results: storedRows }),
178
+ Members: () => ({ Success: true, Results: memberRows }),
179
+ });
180
+
181
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
182
+
183
+ expect(result.Results.map((r) => JSON.parse(r.Metadata))).toEqual([
184
+ { Entity: 'Members', RecordID: 'AAA', FirstName: 'Ada', LastName: 'Lovelace' },
185
+ { Entity: 'Members', RecordID: 'bbb', FirstName: 'Grace', LastName: 'Hopper' },
186
+ { Entity: 'Members', RecordID: 'ccc', FirstName: 'Alan' },
187
+ ]);
188
+ const [membersCall] = callsFor('Members');
189
+ expect(membersCall.contextUser).toBe(contextUser);
190
+ expect(membersCall.params.Fields).toEqual(['ID', 'FirstName', 'LastName']);
191
+ expect(membersCall.params.ResultType).toBe('simple');
192
+ expect(membersCall.params.ExtraFilter).toBeUndefined();
193
+ });
194
+
195
+ it('honours the filter: passes it to the entity view and drops vectors whose record did not pass', async () => {
196
+ useVectorDB(true);
197
+ const filter = "LastName = 'Hopper'";
198
+ routeViews('Members', {
199
+ 'MJ: Entity Record Documents': () => ({ Success: true, Results: storedRows }),
200
+ Members: (params) => ({
201
+ Success: true,
202
+ Results: params.ExtraFilter === filter ? memberRows.filter((r) => r.LastName === 'Hopper') : memberRows,
203
+ }),
204
+ });
205
+
206
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, filter, appContext(membersEntity));
207
+
208
+ expect(result.Success).toBe(true);
209
+ expect(callsFor('Members')[0].params.ExtraFilter).toBe(filter);
210
+ expect(result.TotalCount).toBe(1);
211
+ expect(result.Results[0].ID).toBe('ERD-2');
212
+ expect(JSON.parse(result.Results[0].Metadata)).toEqual({ Entity: 'Members', RecordID: 'bbb', FirstName: 'Grace', LastName: 'Hopper' });
213
+ });
214
+
215
+ it('stops at maxRecords', async () => {
216
+ useVectorDB(true);
217
+ routeViews('Members', {
218
+ 'MJ: Entity Record Documents': () => ({ Success: true, Results: storedRows }),
219
+ Members: () => ({ Success: true, Results: memberRows }),
220
+ });
221
+
222
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 2, undefined, appContext(membersEntity));
223
+
224
+ expect(result.Results.map((r) => r.ID)).toEqual(['ERD-1', 'ERD-2']);
225
+ expect(result.TotalCount).toBe(2);
226
+ });
227
+
228
+ it('matches composite-key records by the compact RecordID segment and falls back to NameField for labels', async () => {
229
+ useVectorDB(true);
230
+ routeViews('Order Lines', {
231
+ 'MJ: Entity Record Documents': () => ({
232
+ Success: true,
233
+ Results: [{ ID: 'ERD-9', RecordID: 'OrderID|o-1||LineNo|2', VectorJSON: '[1,0]' }],
234
+ }),
235
+ 'Order Lines': () => ({ Success: true, Results: [{ OrderID: 'o-1', LineNo: 2, Description: 'Brie wheel' }] }),
236
+ });
237
+
238
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(orderLinesEntity));
239
+
240
+ expect(result.Success).toBe(true);
241
+ expect(result.Results).toHaveLength(1);
242
+ expect(JSON.parse(result.Results[0].Metadata)).toEqual({
243
+ Entity: 'Order Lines',
244
+ RecordID: 'OrderID|o-1||LineNo|2',
245
+ Description: 'Brie wheel',
246
+ });
247
+ expect(callsFor('Order Lines')[0].params.Fields).toEqual(['OrderID', 'LineNo', 'Description']);
248
+ });
249
+
250
+ it('returns an error result when the entity records cannot be loaded (e.g. an invalid filter)', async () => {
251
+ useVectorDB(true);
252
+ routeViews('Members', {
253
+ 'MJ: Entity Record Documents': () => ({ Success: true, Results: storedRows }),
254
+ Members: () => ({ Success: false, Results: [], ErrorMessage: 'bad filter' }),
255
+ });
256
+
257
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, 'nonsense', appContext(membersEntity));
258
+
259
+ expect(result.Success).toBe(false);
260
+ expect(result.Results).toEqual([]);
261
+ expect(result.ErrorMessage).toBe('Failed to load "Members" records: bad filter');
262
+ });
263
+ });
264
+
265
+ describe('index-keyed providers (Pinecone path, unchanged)', () => {
266
+ it('queries the provider index by name and never reads Entity Record Documents', async () => {
267
+ const { queryIndex } = useVectorDB(false);
268
+ routeViews('Members', {});
269
+
270
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
271
+
272
+ expect(result.Success).toBe(true);
273
+ expect(queryIndex).toHaveBeenCalledTimes(1);
274
+ const query = queryIndex.mock.calls[0][0];
275
+ expect(query.id).toBe('provider-index-name');
276
+ expect(query.topK).toBe(50);
277
+ expect(query.includeValues).toBe(true);
278
+ expect(query.filter).toEqual({ Entity: { $eq: 'Members' } });
279
+ expect(query.vector).toHaveLength(2);
280
+ expect(result.Results).toEqual([{ ID: 'pc-1', Values: [0.5, 0.5], Metadata: JSON.stringify({ Entity: 'Members', RecordID: 'a' }) }]);
281
+ expect(callsFor('MJ: Entity Record Documents')).toHaveLength(0);
282
+ expect(callsFor('Members')).toHaveLength(0);
283
+ });
284
+ });
285
+ });
@@ -0,0 +1,84 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { hostname } from 'os';
3
+
4
+ const { logErrorMock } = vi.hoisted(() => ({ logErrorMock: vi.fn() }));
5
+ vi.mock('@memberjunction/core', async (importOriginal) => {
6
+ const actual = await importOriginal<typeof import('@memberjunction/core')>();
7
+ return { ...actual, LogError: logErrorMock };
8
+ });
9
+
10
+ import { HostInstanceIdentity } from '../agentSessions/HostInstance.js';
11
+
12
+ /** A fresh copy of the module: no port set, no identity read yet. */
13
+ async function freshHostInstanceModule(): Promise<typeof import('../agentSessions/HostInstance.js')> {
14
+ vi.resetModules();
15
+ return import('../agentSessions/HostInstance.js');
16
+ }
17
+
18
+ beforeEach(() => {
19
+ logErrorMock.mockReset();
20
+ });
21
+
22
+ describe('HostInstanceIdentity', () => {
23
+ const ours = new HostInstanceIdentity('build-01', '4000', 101, 'boot-ours');
24
+
25
+ it('stamps hostname:port:pid:bootId, and every boot of the instance shares hostname:port:', () => {
26
+ expect(ours.GetHostInstanceID()).toBe('build-01:4000:101:boot-ours');
27
+ expect(ours.GetInstancePrefix()).toBe('build-01:4000:');
28
+ });
29
+
30
+ describe('IsPriorBoot', () => {
31
+ it('is true for an earlier boot of the same instance, including one whose pid the OS reused', () => {
32
+ expect(ours.IsPriorBoot('build-01:4000:99:boot-before')).toBe(true);
33
+ expect(ours.IsPriorBoot('build-01:4000:101:boot-before')).toBe(true);
34
+ });
35
+
36
+ it.each([
37
+ ['this boot', 'build-01:4000:101:boot-ours'],
38
+ ['another MJAPI on the same host (another port)', 'build-01:4100:202:boot-other'],
39
+ ['an MJAPI on another host', 'build-02:4000:101:boot-other'],
40
+ ['the format before the port was added, with a pid equal to the port', 'build-01:4000:boot-before'],
41
+ ['no id', null],
42
+ ['an empty id', ''],
43
+ ])('is false for %s', (_case, hostInstanceID) => {
44
+ expect(ours.IsPriorBoot(hostInstanceID)).toBe(false);
45
+ });
46
+
47
+ it("matches a host name exactly, though a SQL LIKE reads its '_' as any character", () => {
48
+ const underscored = new HostInstanceIdentity('web_1', '4000', 7, 'boot-ours');
49
+ expect(underscored.IsPriorBoot('web_1:4000:8:boot-before')).toBe(true);
50
+ expect(underscored.IsPriorBoot('web-1:4000:8:boot-before')).toBe(false);
51
+ });
52
+ });
53
+ });
54
+
55
+ describe('the current host instance', () => {
56
+ it('carries the port SetHostInstancePort set before it was first read', async () => {
57
+ const hostInstance = await freshHostInstanceModule();
58
+ hostInstance.SetHostInstancePort(4100);
59
+
60
+ const current = hostInstance.GetCurrentHostInstance();
61
+ expect(current.GetHostInstanceID()).toBe(`${hostname()}:4100:${process.pid}:${hostInstance.GetBootID()}`);
62
+ expect(current.GetInstancePrefix()).toBe(`${hostname()}:4100:`);
63
+ expect(hostInstance.GetHostInstanceID()).toBe(current.GetHostInstanceID());
64
+ });
65
+
66
+ it('is this process alone (pid-<pid>) when no port was set', async () => {
67
+ const hostInstance = await freshHostInstanceModule();
68
+
69
+ expect(hostInstance.GetCurrentHostInstance().InstanceKey).toBe(`pid-${process.pid}`);
70
+ expect(hostInstance.GetHostInstanceID()).toBe(`${hostname()}:pid-${process.pid}:${process.pid}:${hostInstance.GetBootID()}`);
71
+ });
72
+
73
+ it('keeps its identity, and logs, when another port is set after it was read', async () => {
74
+ const hostInstance = await freshHostInstanceModule();
75
+ hostInstance.SetHostInstancePort(4100);
76
+ const before = hostInstance.GetHostInstanceID();
77
+
78
+ hostInstance.SetHostInstancePort(4200);
79
+
80
+ expect(hostInstance.GetHostInstanceID()).toBe(before);
81
+ expect(logErrorMock).toHaveBeenCalledTimes(1);
82
+ expect(String(logErrorMock.mock.calls[0][0])).toContain('Port 4200 was set after');
83
+ });
84
+ });
@@ -7,6 +7,8 @@ import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
7
7
 
8
8
  const h = vi.hoisted(() => ({
9
9
  configure: vi.fn(),
10
+ registryConfigure: vi.fn(),
11
+ getSystemUser: vi.fn(),
10
12
  listOffersForUser: vi.fn(),
11
13
  acceptOffer: vi.fn(),
12
14
  declineOffer: vi.fn(),
@@ -23,8 +25,12 @@ vi.mock('@memberjunction/livekit-room-server', () => ({
23
25
  DeclineOffer: h.declineOffer,
24
26
  },
25
27
  },
28
+ HandoffOfferRegistry: { Instance: { Configure: h.registryConfigure } },
26
29
  LiveKitSipService: class {},
27
30
  }));
31
+ vi.mock('@memberjunction/generic-database-provider', () => ({
32
+ UserCache: { Instance: { GetSystemUser: h.getSystemUser } },
33
+ }));
28
34
  vi.mock('@memberjunction/notifications', () => ({
29
35
  NotificationEngine: { Instance: { Config: h.notificationConfig, SendNotification: h.sendNotification } },
30
36
  }));
@@ -44,6 +50,17 @@ import type { AppContext } from '../types.js';
44
50
  /** Captured at module load: the resolver binds the engine's server-only collaborators then. */
45
51
  const wiredAtLoad = h.configure.mock.calls[0]?.[0] as { Presence: unknown; Publisher: unknown; Notifier: unknown };
46
52
 
53
+ const registryWiredAtLoad = h.registryConfigure.mock.calls[0]?.[0] as { ResolveContextUser: () => UserInfo | undefined };
54
+
55
+ describe('offer registry server identity wiring', () => {
56
+ it('configures the registry with a lazy system-user resolver that reads the user cache on every call', () => {
57
+ const systemUser = { ID: 'sys' } as unknown as UserInfo;
58
+ h.getSystemUser.mockReturnValueOnce(undefined).mockReturnValueOnce(systemUser);
59
+ expect(registryWiredAtLoad.ResolveContextUser()).toBeUndefined();
60
+ expect(registryWiredAtLoad.ResolveContextUser()).toBe(systemUser);
61
+ });
62
+ });
63
+
47
64
  class TestableResolver extends HumanHandoffResolver {
48
65
  public user: UserInfo | undefined = { ID: 'U1', Name: 'Dana', Email: 'dana@x.com' } as unknown as UserInfo;
49
66
  protected override GetUserFromPayload(): UserInfo | undefined {
@@ -3,12 +3,14 @@ import 'reflect-metadata';
3
3
  import { describe, it, expect, vi, beforeEach } from 'vitest';
4
4
  import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
5
5
  import type {
6
+ MJAIAgentSessionEntity,
6
7
  MJMeetingEntity,
7
8
  MJMeetingParticipantEntity,
8
9
  MJPhoneNumberEntity,
9
10
  MJInteractionEntity,
10
11
  MJInteractionEventEntity,
11
12
  } from '@memberjunction/core-entities';
13
+ import type { StartAgentRoomSessionParams } from '@memberjunction/livekit-room-server';
12
14
 
13
15
  const mocks = vi.hoisted(() => ({
14
16
  mintClientToken: vi.fn(async (roomName: string, identity: string, name?: string) => ({
@@ -16,7 +18,7 @@ const mocks = vi.hoisted(() => ({
16
18
  Identity: identity,
17
19
  ServerUrl: 'wss://livekit.example.com',
18
20
  })),
19
- startAgentRoomSession: vi.fn(async () => ({
21
+ startAgentRoomSession: vi.fn(async (_params: StartAgentRoomSessionParams) => ({
20
22
  SessionBridgeID: 'bridge-123',
21
23
  RoomName: 'room-123',
22
24
  })),
@@ -69,6 +71,7 @@ import {
69
71
  type RSVPMeetingInput,
70
72
  } from '../resolvers/MeetingResolver.js';
71
73
  import type { AppContext } from '../types.js';
74
+ import type { SessionManager } from '../agentSessions/SessionManager.js';
72
75
 
73
76
  class MockEntityBase {
74
77
  public ID: string = `mock-${Date.now()}-${Math.random()}`;
@@ -362,6 +365,77 @@ describe('MeetingResolver', () => {
362
365
  expect(m.EndedAt).toBeDefined();
363
366
  expect(mocks.stopAllAgentsInRoom).toHaveBeenCalledWith('mj-mtg-standup-1', 'Explicit', resolver.user, mockProvider);
364
367
  });
368
+
369
+ it("closes an agent participant's session when its agent leaves the room (#5356)", async () => {
370
+ const m = new MockMeetingEntity();
371
+ m.ID = 'mtg-live-2';
372
+ m.HostUserID = 'user-host-1';
373
+ m.RoomName = 'mj-mtg-standup-2';
374
+ m.Status = 'Scheduled';
375
+ resolver.mockMeetings.push(m);
376
+
377
+ // The resolver's protected seams this test stands in for.
378
+ const seams = resolver as unknown as {
379
+ loadMeetingEntity: () => Promise<MockMeetingEntity>;
380
+ isUserHostOrCoHost: () => Promise<boolean>;
381
+ loadParticipants: () => Promise<object[]>;
382
+ sessionManager: SessionManager;
383
+ };
384
+ vi.spyOn(seams, 'loadMeetingEntity').mockResolvedValue(m);
385
+ vi.spyOn(seams, 'isUserHostOrCoHost').mockResolvedValue(true);
386
+ vi.spyOn(seams, 'loadParticipants').mockResolvedValue([
387
+ { ID: 'p-1', MeetingID: m.ID, Role: 'Agent', AgentID: 'agent-co-1', InviteStatus: 'Accepted' },
388
+ ]);
389
+ vi.spyOn(seams.sessionManager, 'CreateSession').mockResolvedValue({ ID: 'agent-session-789' } as unknown as MJAIAgentSessionEntity);
390
+ const closeSession = vi.spyOn(seams.sessionManager, 'CloseSession').mockResolvedValue(true);
391
+
392
+ await resolver.StartMeeting(m.ID, ctx);
393
+ const started = mocks.startAgentRoomSession.mock.calls[0][0];
394
+ expect(started.AgentSessionID).toBe('agent-session-789');
395
+ expect(closeSession).not.toHaveBeenCalled();
396
+
397
+ // The agent leaves (the meeting was ended, or everyone left): the coordinator runs the hook once its bridge has ended.
398
+ await started.Host?.OnSessionEnded?.('Explicit');
399
+
400
+ expect(closeSession).toHaveBeenCalledWith('agent-session-789', resolver.user, mockProvider, 'Explicit');
401
+ });
402
+
403
+ it("closes an agent participant's session at once, as Error, when its agent fails to start (#5308)", async () => {
404
+ const m = new MockMeetingEntity();
405
+ m.ID = 'mtg-live-3';
406
+ m.HostUserID = 'user-host-1';
407
+ m.RoomName = 'mj-mtg-standup-3';
408
+ m.Status = 'Scheduled';
409
+ resolver.mockMeetings.push(m);
410
+
411
+ // The resolver's protected seams this test stands in for.
412
+ const seams = resolver as unknown as {
413
+ loadMeetingEntity: () => Promise<MockMeetingEntity>;
414
+ isUserHostOrCoHost: () => Promise<boolean>;
415
+ loadParticipants: () => Promise<object[]>;
416
+ sessionManager: SessionManager;
417
+ };
418
+ vi.spyOn(seams, 'loadMeetingEntity').mockResolvedValue(m);
419
+ vi.spyOn(seams, 'isUserHostOrCoHost').mockResolvedValue(true);
420
+ vi.spyOn(seams, 'loadParticipants').mockResolvedValue([
421
+ { ID: 'p-1', MeetingID: m.ID, Role: 'Agent', AgentID: 'agent-fails', InviteStatus: 'Accepted' },
422
+ { ID: 'p-2', MeetingID: m.ID, Role: 'Agent', AgentID: 'agent-joins', InviteStatus: 'Accepted' },
423
+ ]);
424
+ vi.spyOn(seams.sessionManager, 'CreateSession')
425
+ .mockResolvedValueOnce({ ID: 'agent-session-fails' } as unknown as MJAIAgentSessionEntity)
426
+ .mockResolvedValueOnce({ ID: 'agent-session-joins' } as unknown as MJAIAgentSessionEntity);
427
+ const closeSession = vi.spyOn(seams.sessionManager, 'CloseSession').mockResolvedValue(true);
428
+ // The first agent's bridge can't start (the coordinator has closed its model session by the time this throws).
429
+ mocks.startAgentRoomSession.mockRejectedValueOnce(new Error('Failed to create AIAgentSessionBridge: permission denied'));
430
+
431
+ const result = await resolver.StartMeeting(m.ID, ctx);
432
+
433
+ // The meeting still starts, with the agent that joined; only the failed agent's session is closed.
434
+ expect(result.Success).toBe(true);
435
+ expect(mocks.startAgentRoomSession).toHaveBeenCalledTimes(2);
436
+ expect(closeSession).toHaveBeenCalledTimes(1);
437
+ expect(closeSession).toHaveBeenCalledWith('agent-session-fails', resolver.user, mockProvider, 'Error');
438
+ });
365
439
  });
366
440
 
367
441
  describe('RSVPMeeting', () => {