@memberjunction/server 6.2.0-edge.2 → 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 (435) hide show
  1. package/README.md +30 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/BridgeHostInstance.d.ts +25 -0
  4. package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -0
  5. package/dist/agentSessions/BridgeHostInstance.js +30 -0
  6. package/dist/agentSessions/BridgeHostInstance.js.map +1 -0
  7. package/dist/agentSessions/HostInstance.d.ts +75 -11
  8. package/dist/agentSessions/HostInstance.d.ts.map +1 -1
  9. package/dist/agentSessions/HostInstance.js +99 -28
  10. package/dist/agentSessions/HostInstance.js.map +1 -1
  11. package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
  12. package/dist/agentSessions/ReturningVisitorRecap.js +7 -3
  13. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  14. package/dist/agentSessions/SessionJanitor.d.ts +173 -25
  15. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  16. package/dist/agentSessions/SessionJanitor.js +452 -54
  17. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  18. package/dist/agentSessions/SessionManager.d.ts +43 -2
  19. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  20. package/dist/agentSessions/SessionManager.js +99 -8
  21. package/dist/agentSessions/SessionManager.js.map +1 -1
  22. package/dist/agentSessions/SessionRunIDVerifier.d.ts +63 -0
  23. package/dist/agentSessions/SessionRunIDVerifier.d.ts.map +1 -0
  24. package/dist/agentSessions/SessionRunIDVerifier.js +129 -0
  25. package/dist/agentSessions/SessionRunIDVerifier.js.map +1 -0
  26. package/dist/agentSessions/index.d.ts +2 -0
  27. package/dist/agentSessions/index.d.ts.map +1 -1
  28. package/dist/agentSessions/index.js +10 -3
  29. package/dist/agentSessions/index.js.map +1 -1
  30. package/dist/auth/actionAuthorization.d.ts +40 -0
  31. package/dist/auth/actionAuthorization.d.ts.map +1 -0
  32. package/dist/auth/actionAuthorization.js +94 -0
  33. package/dist/auth/actionAuthorization.js.map +1 -0
  34. package/dist/auth/index.d.ts.map +1 -1
  35. package/dist/auth/index.js +3 -10
  36. package/dist/auth/index.js.map +1 -1
  37. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  38. package/dist/auth/magicLink/MagicLinkService.js +6 -3
  39. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  40. package/dist/auth/newUsers.d.ts +10 -0
  41. package/dist/auth/newUsers.d.ts.map +1 -1
  42. package/dist/auth/newUsers.js +80 -11
  43. package/dist/auth/newUsers.js.map +1 -1
  44. package/dist/config.d.ts +4567 -71
  45. package/dist/config.d.ts.map +1 -1
  46. package/dist/config.js +246 -3
  47. package/dist/config.js.map +1 -1
  48. package/dist/context.d.ts +8 -0
  49. package/dist/context.d.ts.map +1 -1
  50. package/dist/context.js +25 -34
  51. package/dist/context.js.map +1 -1
  52. package/dist/generated/graphql-schemas/__mj.d.ts +1073 -10
  53. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  54. package/dist/generated/graphql-schemas/__mj.js +9188 -3649
  55. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  56. package/dist/generic/OrphanedConversationDetailReconciler.js +3 -3
  57. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  58. package/dist/generic/ResolverBase.d.ts +62 -6
  59. package/dist/generic/ResolverBase.d.ts.map +1 -1
  60. package/dist/generic/ResolverBase.js +179 -35
  61. package/dist/generic/ResolverBase.js.map +1 -1
  62. package/dist/generic/RunViewResolver.d.ts +4 -0
  63. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  64. package/dist/generic/RunViewResolver.js +37 -1
  65. package/dist/generic/RunViewResolver.js.map +1 -1
  66. package/dist/index.d.ts +7 -0
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +214 -159
  69. package/dist/index.js.map +1 -1
  70. package/dist/isolatedProvider.d.ts +20 -0
  71. package/dist/isolatedProvider.d.ts.map +1 -0
  72. package/dist/isolatedProvider.js +22 -0
  73. package/dist/isolatedProvider.js.map +1 -0
  74. package/dist/postgresqlCompat.d.ts +8 -0
  75. package/dist/postgresqlCompat.d.ts.map +1 -0
  76. package/dist/postgresqlCompat.js +19 -0
  77. package/dist/postgresqlCompat.js.map +1 -0
  78. package/dist/postgresqlPoolSettings.d.ts +108 -0
  79. package/dist/postgresqlPoolSettings.d.ts.map +1 -0
  80. package/dist/postgresqlPoolSettings.js +134 -0
  81. package/dist/postgresqlPoolSettings.js.map +1 -0
  82. package/dist/realtimeProxy/RealtimeProxyServer.d.ts +44 -4
  83. package/dist/realtimeProxy/RealtimeProxyServer.d.ts.map +1 -1
  84. package/dist/realtimeProxy/RealtimeProxyServer.js +121 -16
  85. package/dist/realtimeProxy/RealtimeProxyServer.js.map +1 -1
  86. package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts +171 -0
  87. package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts.map +1 -0
  88. package/dist/realtimeProxy/RealtimeRelayTunnel.js +532 -0
  89. package/dist/realtimeProxy/RealtimeRelayTunnel.js.map +1 -0
  90. package/dist/realtimeSessions/RealtimeSessionEventService.d.ts +148 -0
  91. package/dist/realtimeSessions/RealtimeSessionEventService.d.ts.map +1 -0
  92. package/dist/realtimeSessions/RealtimeSessionEventService.js +191 -0
  93. package/dist/realtimeSessions/RealtimeSessionEventService.js.map +1 -0
  94. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts +66 -0
  95. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -0
  96. package/dist/realtimeSessions/RealtimeSessionVerificationService.js +138 -0
  97. package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -0
  98. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts +38 -0
  99. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -0
  100. package/dist/realtimeSessions/RealtimeVerifyRouter.js +98 -0
  101. package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -0
  102. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts +61 -0
  103. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -0
  104. package/dist/realtimeSessions/SessionVerificationPortsBase.js +149 -0
  105. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -0
  106. package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts +57 -0
  107. package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts.map +1 -0
  108. package/dist/realtimeSessions/SlidingWindowRateLimiter.js +69 -0
  109. package/dist/realtimeSessions/SlidingWindowRateLimiter.js.map +1 -0
  110. package/dist/realtimeSessions/index.d.ts +17 -0
  111. package/dist/realtimeSessions/index.d.ts.map +1 -0
  112. package/dist/realtimeSessions/index.js +17 -0
  113. package/dist/realtimeSessions/index.js.map +1 -0
  114. package/dist/realtimeSessions/sessionAccess.d.ts +86 -0
  115. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -0
  116. package/dist/realtimeSessions/sessionAccess.js +90 -0
  117. package/dist/realtimeSessions/sessionAccess.js.map +1 -0
  118. package/dist/realtimeSessions/sessionConfigStamps.d.ts +70 -0
  119. package/dist/realtimeSessions/sessionConfigStamps.d.ts.map +1 -0
  120. package/dist/realtimeSessions/sessionConfigStamps.js +93 -0
  121. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -0
  122. package/dist/realtimeSessions/subscriptionAuthorization.d.ts +65 -0
  123. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -0
  124. package/dist/realtimeSessions/subscriptionAuthorization.js +71 -0
  125. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -0
  126. package/dist/realtimeSessions/verificationCore.d.ts +243 -0
  127. package/dist/realtimeSessions/verificationCore.d.ts.map +1 -0
  128. package/dist/realtimeSessions/verificationCore.js +356 -0
  129. package/dist/realtimeSessions/verificationCore.js.map +1 -0
  130. package/dist/realtimeSessions/verificationPages.d.ts +66 -0
  131. package/dist/realtimeSessions/verificationPages.d.ts.map +1 -0
  132. package/dist/realtimeSessions/verificationPages.js +110 -0
  133. package/dist/realtimeSessions/verificationPages.js.map +1 -0
  134. package/dist/realtimeSessions/verificationPolicy.d.ts +147 -0
  135. package/dist/realtimeSessions/verificationPolicy.d.ts.map +1 -0
  136. package/dist/realtimeSessions/verificationPolicy.js +249 -0
  137. package/dist/realtimeSessions/verificationPolicy.js.map +1 -0
  138. package/dist/realtimeSessions/verificationWorkflow.d.ts +263 -0
  139. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -0
  140. package/dist/realtimeSessions/verificationWorkflow.js +435 -0
  141. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -0
  142. package/dist/realtimeWidget/widgetGuestElevation.d.ts +20 -2
  143. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  144. package/dist/realtimeWidget/widgetGuestElevation.js +31 -2
  145. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  146. package/dist/resolvers/ActionResolver.d.ts +31 -5
  147. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  148. package/dist/resolvers/ActionResolver.js +75 -14
  149. package/dist/resolvers/ActionResolver.js.map +1 -1
  150. package/dist/resolvers/AdhocQueryResolver.d.ts +35 -17
  151. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  152. package/dist/resolvers/AdhocQueryResolver.js +92 -166
  153. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  154. package/dist/resolvers/CacheStatsResolver.d.ts +27 -0
  155. package/dist/resolvers/CacheStatsResolver.d.ts.map +1 -1
  156. package/dist/resolvers/CacheStatsResolver.js +113 -2
  157. package/dist/resolvers/CacheStatsResolver.js.map +1 -1
  158. package/dist/resolvers/ClientToolRequestResolver.d.ts +4 -2
  159. package/dist/resolvers/ClientToolRequestResolver.d.ts.map +1 -1
  160. package/dist/resolvers/ClientToolRequestResolver.js +28 -10
  161. package/dist/resolvers/ClientToolRequestResolver.js.map +1 -1
  162. package/dist/resolvers/EntityRecordNameResolver.d.ts +7 -23
  163. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  164. package/dist/resolvers/EntityRecordNameResolver.js +9 -47
  165. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  166. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +12 -1
  167. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  168. package/dist/resolvers/FetchEntityVectorsResolver.js +83 -2
  169. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  170. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  171. package/dist/resolvers/GetDataResolver.js +9 -0
  172. package/dist/resolvers/GetDataResolver.js.map +1 -1
  173. package/dist/resolvers/HumanHandoffResolver.d.ts +107 -0
  174. package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -0
  175. package/dist/resolvers/HumanHandoffResolver.js +330 -0
  176. package/dist/resolvers/HumanHandoffResolver.js.map +1 -0
  177. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  178. package/dist/resolvers/MCPResolver.js +9 -4
  179. package/dist/resolvers/MCPResolver.js.map +1 -1
  180. package/dist/resolvers/MeetingResolver.d.ts +69 -0
  181. package/dist/resolvers/MeetingResolver.d.ts.map +1 -0
  182. package/dist/resolvers/MeetingResolver.js +381 -0
  183. package/dist/resolvers/MeetingResolver.js.map +1 -0
  184. package/dist/resolvers/RealtimeBridgeResolver.d.ts +88 -3
  185. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  186. package/dist/resolvers/RealtimeBridgeResolver.js +352 -12
  187. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  188. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +156 -108
  189. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  190. package/dist/resolvers/RealtimeClientSessionResolver.js +395 -293
  191. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  192. package/dist/resolvers/RealtimeSessionEventsResolver.d.ts +61 -0
  193. package/dist/resolvers/RealtimeSessionEventsResolver.d.ts.map +1 -0
  194. package/dist/resolvers/RealtimeSessionEventsResolver.js +130 -0
  195. package/dist/resolvers/RealtimeSessionEventsResolver.js.map +1 -0
  196. package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts +85 -0
  197. package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts.map +1 -0
  198. package/dist/resolvers/RealtimeSessionVerificationResolver.js +188 -0
  199. package/dist/resolvers/RealtimeSessionVerificationResolver.js.map +1 -0
  200. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +2 -0
  201. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  202. package/dist/resolvers/RemoteBrowserActionResolver.js +5 -1
  203. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  204. package/dist/resolvers/RunAIAgentResolver.d.ts +10 -0
  205. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  206. package/dist/resolvers/RunAIAgentResolver.js +68 -11
  207. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  208. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  209. package/dist/resolvers/RunTemplateResolver.js +12 -11
  210. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  211. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  212. package/dist/resolvers/SyncDataResolver.js +6 -2
  213. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  214. package/dist/resolvers/SyncRolesUsersResolver.d.ts +2 -0
  215. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  216. package/dist/resolvers/SyncRolesUsersResolver.js +23 -6
  217. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  218. package/dist/resolvers/UserAvatarResolver.d.ts +16 -0
  219. package/dist/resolvers/UserAvatarResolver.d.ts.map +1 -0
  220. package/dist/resolvers/UserAvatarResolver.js +124 -0
  221. package/dist/resolvers/UserAvatarResolver.js.map +1 -0
  222. package/dist/resolvers/UserResolver.d.ts.map +1 -1
  223. package/dist/resolvers/UserResolver.js +2 -17
  224. package/dist/resolvers/UserResolver.js.map +1 -1
  225. package/dist/resolvers/adhocTimeout.d.ts +7 -0
  226. package/dist/resolvers/adhocTimeout.d.ts.map +1 -0
  227. package/dist/resolvers/adhocTimeout.js +12 -0
  228. package/dist/resolvers/adhocTimeout.js.map +1 -0
  229. package/dist/resolvers/agentVisionConsentAudit.d.ts +37 -0
  230. package/dist/resolvers/agentVisionConsentAudit.d.ts.map +1 -0
  231. package/dist/resolvers/agentVisionConsentAudit.js +63 -0
  232. package/dist/resolvers/agentVisionConsentAudit.js.map +1 -0
  233. package/dist/resolvers/avatarInputValidation.d.ts +40 -0
  234. package/dist/resolvers/avatarInputValidation.d.ts.map +1 -0
  235. package/dist/resolvers/avatarInputValidation.js +97 -0
  236. package/dist/resolvers/avatarInputValidation.js.map +1 -0
  237. package/dist/resolvers/currentUserRoles.d.ts +29 -0
  238. package/dist/resolvers/currentUserRoles.d.ts.map +1 -1
  239. package/dist/resolvers/currentUserRoles.js +30 -0
  240. package/dist/resolvers/currentUserRoles.js.map +1 -1
  241. package/dist/resolvers/meetings/index.d.ts +5 -0
  242. package/dist/resolvers/meetings/index.d.ts.map +1 -0
  243. package/dist/resolvers/meetings/index.js +5 -0
  244. package/dist/resolvers/meetings/index.js.map +1 -0
  245. package/dist/resolvers/meetings/meetingHelpers.d.ts +17 -0
  246. package/dist/resolvers/meetings/meetingHelpers.d.ts.map +1 -0
  247. package/dist/resolvers/meetings/meetingHelpers.js +212 -0
  248. package/dist/resolvers/meetings/meetingHelpers.js.map +1 -0
  249. package/dist/resolvers/meetings/meetingMutations.d.ts +18 -0
  250. package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -0
  251. package/dist/resolvers/meetings/meetingMutations.js +439 -0
  252. package/dist/resolvers/meetings/meetingMutations.js.map +1 -0
  253. package/dist/resolvers/meetings/meetingQueries.d.ts +7 -0
  254. package/dist/resolvers/meetings/meetingQueries.d.ts.map +1 -0
  255. package/dist/resolvers/meetings/meetingQueries.js +113 -0
  256. package/dist/resolvers/meetings/meetingQueries.js.map +1 -0
  257. package/dist/resolvers/meetings/meetingTypes.d.ts +89 -0
  258. package/dist/resolvers/meetings/meetingTypes.d.ts.map +1 -0
  259. package/dist/resolvers/meetings/meetingTypes.js +345 -0
  260. package/dist/resolvers/meetings/meetingTypes.js.map +1 -0
  261. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  262. package/dist/rest/ViewOperationsHandler.js +8 -5
  263. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  264. package/dist/services/TaskGraphActionRunner.d.ts +8 -0
  265. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  266. package/dist/services/TaskGraphActionRunner.js +27 -1
  267. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  268. package/dist/services/TaskGraphContinuationDeliverer.d.ts +4 -3
  269. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  270. package/dist/services/TaskGraphContinuationDeliverer.js +13 -7
  271. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  272. package/dist/services/WorkQueueHostService.d.ts +36 -0
  273. package/dist/services/WorkQueueHostService.d.ts.map +1 -0
  274. package/dist/services/WorkQueueHostService.js +97 -0
  275. package/dist/services/WorkQueueHostService.js.map +1 -0
  276. package/dist/services/workQueueConfig.d.ts +103 -0
  277. package/dist/services/workQueueConfig.d.ts.map +1 -0
  278. package/dist/services/workQueueConfig.js +48 -0
  279. package/dist/services/workQueueConfig.js.map +1 -0
  280. package/dist/sharedCache.d.ts +105 -0
  281. package/dist/sharedCache.d.ts.map +1 -0
  282. package/dist/sharedCache.js +276 -0
  283. package/dist/sharedCache.js.map +1 -0
  284. package/dist/types.d.ts +11 -0
  285. package/dist/types.d.ts.map +1 -1
  286. package/dist/types.js.map +1 -1
  287. package/package.json +97 -94
  288. package/src/__tests__/ActionResolver.authorization.test.ts +353 -0
  289. package/src/__tests__/AdhocQueryResolver.bugs.test.ts +14 -35
  290. package/src/__tests__/AdhocQueryResolver.pagination.test.ts +6 -2
  291. package/src/__tests__/AdhocQueryResolver.scopeGuard.test.ts +3 -3
  292. package/src/__tests__/AdhocQueryResolver.sqlAuthorizer.test.ts +104 -0
  293. package/src/__tests__/BridgeHostInstance.test.ts +232 -0
  294. package/src/__tests__/ClientToolRequestResolver.test.ts +116 -0
  295. package/src/__tests__/EntityRecordNameResolver.fieldSecurity.test.ts +27 -72
  296. package/src/__tests__/FetchEntityVectorsResolver.test.ts +285 -0
  297. package/src/__tests__/HostInstance.test.ts +84 -0
  298. package/src/__tests__/HumanHandoffResolver.test.ts +250 -0
  299. package/src/__tests__/MeetingResolver.test.ts +448 -0
  300. package/src/__tests__/RealtimeBridgeResolver.test.ts +651 -4
  301. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +383 -0
  302. package/src/__tests__/RealtimeClientSessionResolver.test.ts +682 -58
  303. package/src/__tests__/RealtimeProxyServer.test.ts +16 -1
  304. package/src/__tests__/RealtimeRelay.test.ts +876 -0
  305. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +129 -0
  306. package/src/__tests__/ResolverBase.adhocReadScope.test.ts +96 -0
  307. package/src/__tests__/ResolverBase.aggregateScreen.test.ts +174 -0
  308. package/src/__tests__/ResolverBase.clientOldValues.test.ts +6 -3
  309. package/src/__tests__/ResolverBase.filterEscaping.test.ts +21 -2
  310. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +291 -0
  311. package/src/__tests__/ReturningVisitorRecap.test.ts +115 -0
  312. package/src/__tests__/RunAIAgentResolver.branch.test.ts +169 -0
  313. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +4 -4
  314. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +166 -0
  315. package/src/__tests__/RunTemplateResolver.test.ts +132 -0
  316. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +63 -0
  317. package/src/__tests__/SessionJanitor.test.ts +845 -18
  318. package/src/__tests__/SessionManager.test.ts +213 -17
  319. package/src/__tests__/SessionRunIDVerifier.test.ts +251 -0
  320. package/src/__tests__/SyncRolesUsersResolver.userRoles.test.ts +97 -0
  321. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +41 -0
  322. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +50 -3
  323. package/src/__tests__/UserAvatarResolver.test.ts +255 -0
  324. package/src/__tests__/UserResolver.currentUser.test.ts +16 -1
  325. package/src/__tests__/WorkQueueHostService.test.ts +133 -0
  326. package/src/__tests__/actionAuthorization.test.ts +130 -0
  327. package/src/__tests__/adhocTimeoutClamp.test.ts +26 -0
  328. package/src/__tests__/agentVisionConsentAudit.test.ts +118 -0
  329. package/src/__tests__/avatarInputValidation.test.ts +160 -0
  330. package/src/__tests__/isolatedProviderTransactions.test.ts +169 -0
  331. package/src/__tests__/magicLinkService.consume.test.ts +6 -0
  332. package/src/__tests__/magicLinkService.provisionLog.test.ts +5 -0
  333. package/src/__tests__/newUsers.template.test.ts +151 -0
  334. package/src/__tests__/newUsers.test.ts +74 -5
  335. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +17 -2
  336. package/src/__tests__/postgresqlPoolSettings.test.ts +131 -0
  337. package/src/__tests__/realtimeSessions/RealtimeSessionEventService.test.ts +201 -0
  338. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +215 -0
  339. package/src/__tests__/realtimeSessions/RealtimeSessionVerificationResolver.test.ts +102 -0
  340. package/src/__tests__/realtimeSessions/RealtimeSessionVerificationService.test.ts +349 -0
  341. package/src/__tests__/realtimeSessions/RealtimeVerifyRouter.test.ts +131 -0
  342. package/src/__tests__/realtimeSessions/SlidingWindowRateLimiter.test.ts +56 -0
  343. package/src/__tests__/realtimeSessions/helpers.ts +4 -0
  344. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +129 -0
  345. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +124 -0
  346. package/src/__tests__/realtimeSessions/verificationCore.test.ts +355 -0
  347. package/src/__tests__/realtimeSessions/verificationPages.test.ts +84 -0
  348. package/src/__tests__/realtimeSessions/verificationPolicy.test.ts +226 -0
  349. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +633 -0
  350. package/src/__tests__/resolverBase.fls.test.ts +87 -4
  351. package/src/__tests__/serverExtensionReservedRoots.test.ts +2 -0
  352. package/src/__tests__/sharedCache.test.ts +310 -0
  353. package/src/__tests__/translateBracketsToPG.test.ts +20 -0
  354. package/src/__tests__/widgetGuestElevation.test.ts +44 -0
  355. package/src/agentSessions/BridgeHostInstance.ts +32 -0
  356. package/src/agentSessions/HostInstance.ts +124 -28
  357. package/src/agentSessions/ReturningVisitorRecap.ts +7 -3
  358. package/src/agentSessions/SessionJanitor.ts +543 -57
  359. package/src/agentSessions/SessionManager.ts +125 -25
  360. package/src/agentSessions/SessionRunIDVerifier.ts +171 -0
  361. package/src/agentSessions/index.ts +10 -3
  362. package/src/auth/actionAuthorization.ts +116 -0
  363. package/src/auth/index.ts +3 -8
  364. package/src/auth/magicLink/MagicLinkService.ts +6 -3
  365. package/src/auth/newUsers.ts +99 -11
  366. package/src/config.ts +255 -3
  367. package/src/context.ts +26 -34
  368. package/src/generated/graphql-schemas/__mj.ts +22701 -18838
  369. package/src/generic/OrphanedConversationDetailReconciler.ts +3 -3
  370. package/src/generic/ResolverBase.ts +210 -36
  371. package/src/generic/RunViewResolver.ts +37 -2
  372. package/src/index.ts +236 -170
  373. package/src/isolatedProvider.ts +22 -0
  374. package/src/postgresqlCompat.ts +21 -0
  375. package/src/postgresqlPoolSettings.ts +213 -0
  376. package/src/realtimeProxy/RealtimeProxyServer.ts +148 -16
  377. package/src/realtimeProxy/RealtimeRelayTunnel.ts +645 -0
  378. package/src/realtimeSessions/README.md +179 -0
  379. package/src/realtimeSessions/RealtimeSessionEventService.ts +274 -0
  380. package/src/realtimeSessions/RealtimeSessionVerificationService.ts +167 -0
  381. package/src/realtimeSessions/RealtimeVerifyRouter.ts +108 -0
  382. package/src/realtimeSessions/SessionVerificationPortsBase.ts +189 -0
  383. package/src/realtimeSessions/SlidingWindowRateLimiter.ts +92 -0
  384. package/src/realtimeSessions/index.ts +16 -0
  385. package/src/realtimeSessions/sessionAccess.ts +141 -0
  386. package/src/realtimeSessions/sessionConfigStamps.ts +123 -0
  387. package/src/realtimeSessions/subscriptionAuthorization.ts +116 -0
  388. package/src/realtimeSessions/verificationCore.ts +502 -0
  389. package/src/realtimeSessions/verificationPages.ts +149 -0
  390. package/src/realtimeSessions/verificationPolicy.ts +321 -0
  391. package/src/realtimeSessions/verificationWorkflow.ts +702 -0
  392. package/src/realtimeWidget/widgetGuestElevation.ts +34 -2
  393. package/src/resolvers/ActionResolver.ts +84 -21
  394. package/src/resolvers/AdhocQueryResolver.ts +105 -187
  395. package/src/resolvers/CacheStatsResolver.ts +89 -2
  396. package/src/resolvers/ClientToolRequestResolver.ts +25 -7
  397. package/src/resolvers/EntityRecordNameResolver.ts +9 -51
  398. package/src/resolvers/FetchEntityVectorsResolver.ts +97 -2
  399. package/src/resolvers/GetDataResolver.ts +10 -0
  400. package/src/resolvers/HumanHandoffResolver.ts +312 -0
  401. package/src/resolvers/MCPResolver.ts +8 -5
  402. package/src/resolvers/MeetingResolver.ts +438 -0
  403. package/src/resolvers/RealtimeBridgeResolver.ts +385 -19
  404. package/src/resolvers/RealtimeClientSessionResolver.ts +503 -344
  405. package/src/resolvers/RealtimeSessionEventsResolver.ts +133 -0
  406. package/src/resolvers/RealtimeSessionVerificationResolver.ts +174 -0
  407. package/src/resolvers/RemoteBrowserActionResolver.ts +11 -1
  408. package/src/resolvers/RunAIAgentResolver.ts +78 -11
  409. package/src/resolvers/RunTemplateResolver.ts +13 -12
  410. package/src/resolvers/SyncDataResolver.ts +6 -2
  411. package/src/resolvers/SyncRolesUsersResolver.ts +24 -6
  412. package/src/resolvers/UserAvatarResolver.ts +111 -0
  413. package/src/resolvers/UserResolver.ts +2 -16
  414. package/src/resolvers/adhocTimeout.ts +12 -0
  415. package/src/resolvers/agentVisionConsentAudit.ts +82 -0
  416. package/src/resolvers/avatarInputValidation.ts +123 -0
  417. package/src/resolvers/currentUserRoles.ts +32 -1
  418. package/src/resolvers/meetings/index.ts +4 -0
  419. package/src/resolvers/meetings/meetingHelpers.ts +307 -0
  420. package/src/resolvers/meetings/meetingMutations.ts +556 -0
  421. package/src/resolvers/meetings/meetingQueries.ts +172 -0
  422. package/src/resolvers/meetings/meetingTypes.ts +238 -0
  423. package/src/rest/ViewOperationsHandler.ts +8 -5
  424. package/src/services/TaskGraphActionRunner.ts +29 -1
  425. package/src/services/TaskGraphContinuationDeliverer.ts +13 -7
  426. package/src/services/WorkQueueHostService.ts +129 -0
  427. package/src/services/workQueueConfig.ts +52 -0
  428. package/src/sharedCache.ts +302 -0
  429. package/src/types.ts +12 -0
  430. package/dist/resolvers/adhoc-query-helpers.d.ts +0 -33
  431. package/dist/resolvers/adhoc-query-helpers.d.ts.map +0 -1
  432. package/dist/resolvers/adhoc-query-helpers.js +0 -50
  433. package/dist/resolvers/adhoc-query-helpers.js.map +0 -1
  434. package/src/__tests__/adhoc-query-helpers.test.ts +0 -50
  435. package/src/resolvers/adhoc-query-helpers.ts +0 -67
@@ -1,15 +1,30 @@
1
1
  import { Resolver, Mutation, Query, Arg, Ctx, ObjectType, InputType, Field } from 'type-graphql';
2
2
  import { randomUUID } from 'crypto';
3
- import { LogError, LogStatusEx, UserInfo, IMetadataProvider } from '@memberjunction/core';
4
- import { LiveKitTokenService, LiveKitAgentRoomCoordinator, LiveKitEgressService } from '@memberjunction/livekit-room-server';
3
+ import { LogError, LogStatusEx, UserInfo, IMetadataProvider, RunView } from '@memberjunction/core';
4
+ import { EscapeSQLString } from '@memberjunction/global';
5
+ import type { TurnAddressingMode } from '@memberjunction/ai-bridge-base';
6
+ import {
7
+ LiveKitTokenService,
8
+ LiveKitAgentRoomCoordinator,
9
+ LiveKitEgressService,
10
+ LiveKitParticipantService,
11
+ LiveKitUserIdentity,
12
+ RoomHandoffEngine,
13
+ type AgentRoomHostOptions,
14
+ type AgentRoomSession,
15
+ type ParticipantUpdateResult,
16
+ type StartAgentRoomSessionParams,
17
+ } from '@memberjunction/livekit-room-server';
5
18
  import { AppContext } from '../types.js';
6
19
  import { ResolverBase } from '../generic/ResolverBase.js';
7
20
  import { GetReadWriteProvider } from '../util.js';
8
- import { CreateBridgeRealtimeSession, FinalizeBridgeCoAgentRuns, GetRealtimeModelVoices, CreateBridgeRoomTranscriptSink, RealtimeTurnModeratorDecision } from '@memberjunction/ai-agents';
21
+ import { CreateBridgeRealtimeSession, FinalizeBridgeCoAgentRuns, GetRealtimeModelVoices, CreateBridgeRoomTranscriptSink, RealtimeTurnModeratorDecision, GetBridgeRealtimeRuntime } from '@memberjunction/ai-agents';
9
22
  import { AIBridgeEngine } from '@memberjunction/ai-bridge-server';
10
23
  import { SessionManager } from '../agentSessions/SessionManager.js';
11
24
  import { NotificationEngine } from '@memberjunction/notifications';
12
25
  import { RegisterMeetingRecordingFile, CorrelateRecordingStart } from './meetingRecordingRegistration.js';
26
+ import { WriteAgentVisionConsentAudit } from './agentVisionConsentAudit.js';
27
+ import { RoomAuthorizationService, type RoomAuthorizationResult } from '@memberjunction/livekit-room-server';
13
28
 
14
29
  /**
15
30
  * Binds the agent realtime-session factory onto the LiveKit room coordinator's model-session creation seam.
@@ -21,6 +36,37 @@ import { RegisterMeetingRecordingFile, CorrelateRecordingStart } from './meeting
21
36
  */
22
37
  LiveKitAgentRoomCoordinator.Instance.SetSessionFactory(CreateBridgeRealtimeSession);
23
38
 
39
+ /**
40
+ * Binds the turn-taking tool handler seam (same module-load rationale as the factory above). A full-duplex model
41
+ * in a shared room calls `i_am_addressed` / `yield_turn`; the bridge engine owns what those mean, and this installs
42
+ * the engine's handler as the model session runtime's local tool handler (`@memberjunction/ai-agents`).
43
+ */
44
+ LiveKitAgentRoomCoordinator.Instance.SetTurnToolBinder((session, handler) => {
45
+ const runtime = GetBridgeRealtimeRuntime(session);
46
+ if (!runtime) {
47
+ LogError('[RealtimeBridge] the model session has no bridge runtime; i_am_addressed / yield_turn will not execute for it.');
48
+ return;
49
+ }
50
+ runtime.SetLocalToolHandler(handler);
51
+ });
52
+
53
+ /**
54
+ * Binds whether an agent watches meetings (same module-load rationale as the factory above). The coordinator asks per
55
+ * model session; the answer is the agent's `realtime.video.watchMeetings` setting, which the session runtime read from
56
+ * its effective configuration (`@memberjunction/ai-agents`). The coordinator also requires a model that takes video.
57
+ */
58
+ LiveKitAgentRoomCoordinator.Instance.SetAgentVisionResolver((session) => GetBridgeRealtimeRuntime(session)?.WatchesMeetingVideo === true);
59
+
60
+ /**
61
+ * Binds how a failed agent start is recorded on the agent's co-agent run (same module-load rationale as the factory
62
+ * above). The coordinator closes the model session of a start that fails before the bridge engine takes it; that close
63
+ * alone finalizes the run as Completed, so the coordinator first has the session's runtime finalize it as Failed, with the
64
+ * start's error.
65
+ */
66
+ LiveKitAgentRoomCoordinator.Instance.SetFailedStartRecorder(async (session, error) => {
67
+ await GetBridgeRealtimeRuntime(session)?.Finalize(false, error);
68
+ });
69
+
24
70
  /**
25
71
  * Binds the co-agent run finalizer onto the bridge engine (same module-load rationale as the factory above).
26
72
  * Lets the engine finalize a session's dangling co-agent observability run when it reaps a bridge WITHOUT a
@@ -127,6 +173,35 @@ export class StartLiveKitAgentRoomSessionInput {
127
173
 
128
174
  @Field(() => String, { nullable: true })
129
175
  TurnMode?: string;
176
+
177
+ /**
178
+ * Start the agent able to bring someone into this room: a person (who is offered the conversation and may accept), a
179
+ * phone number, or another agent. Opt-in, and honoured only when this server has the handoff collaborators configured
180
+ * (the LiveKit SIP extension); otherwise the agent starts as it always did.
181
+ */
182
+ @Field(() => Boolean, { nullable: true })
183
+ EnableHandoff?: boolean;
184
+
185
+ /**
186
+ * How this agent decides it was addressed: `Auto` (default — the model's own judgement when it is full-duplex,
187
+ * name matching otherwise), `ModelSide`, or `Regex`.
188
+ */
189
+ @Field(() => String, { nullable: true })
190
+ TurnAddressing?: string;
191
+ }
192
+
193
+ /** The live turn-taking state of a room (floor holder, human speaking, hand-off, loop cap, recent events, seated agents). */
194
+ @ObjectType()
195
+ export class LiveKitRoomTurnStateResult {
196
+ @Field(() => Boolean)
197
+ Success: boolean;
198
+
199
+ @Field(() => String, { nullable: true })
200
+ ErrorMessage?: string;
201
+
202
+ /** JSON of the room's `RoomTurnSnapshot`; `null` when the room holds no agents. Parsed by the typed client. */
203
+ @Field(() => String, { nullable: true })
204
+ StateJSON?: string;
130
205
  }
131
206
 
132
207
  @ObjectType()
@@ -181,7 +256,30 @@ export class LiveKitRecordingResult {
181
256
  RecordingFileID?: string;
182
257
  }
183
258
 
184
- /** A selectable provider-native voice for the dev voice picker. */
259
+ /** A person's choice of whether agents may see their camera and shared screen in a room. */
260
+ @InputType()
261
+ export class SetLiveKitAgentVisionInput {
262
+ @Field(() => String)
263
+ RoomName: string;
264
+
265
+ /** `true` to let agents see, `false` to stop. */
266
+ @Field(() => Boolean)
267
+ Allow: boolean;
268
+ }
269
+
270
+ @ObjectType()
271
+ export class LiveKitAgentVisionResult {
272
+ @Field(() => Boolean)
273
+ Success: boolean;
274
+
275
+ @Field(() => String, { nullable: true })
276
+ ErrorMessage?: string;
277
+ }
278
+
279
+ /**
280
+ * A selectable provider-native voice for the dev voice picker: one per persona, or one per voice only the driver declares.
281
+ * Two personas can share an `ID` (a voice, and the same voice with a face); `PersonaID` tells them apart.
282
+ */
185
283
  @ObjectType()
186
284
  export class RealtimeVoiceOptionResult {
187
285
  @Field(() => String)
@@ -189,6 +287,22 @@ export class RealtimeVoiceOptionResult {
189
287
 
190
288
  @Field(() => String)
191
289
  Name: string;
290
+
291
+ /** The persona this voice belongs to (`MJ: AI Personas.ID`). Null for a voice only the driver declares. */
292
+ @Field(() => String, { nullable: true })
293
+ PersonaID?: string;
294
+
295
+ /**
296
+ * The avatar that comes with this voice: the `APIName` of its persona's preset Video binding on the model's vendor,
297
+ * which is what `realtime.video.avatarId` takes. Null for a voice without one, and for every voice only the driver
298
+ * declares.
299
+ */
300
+ @Field(() => String, { nullable: true })
301
+ AvatarID?: string;
302
+
303
+ /** The persona's preview image URL, or null when it has none. */
304
+ @Field(() => String, { nullable: true })
305
+ PreviewImageURL?: string;
192
306
  }
193
307
 
194
308
  /** An active Realtime model with the voices its driver supports — feeds the dev model/voice picker. */
@@ -231,6 +345,11 @@ export class RealtimeBridgeResolver extends ResolverBase {
231
345
  if (!user) {
232
346
  return failure('Unable to determine current user.');
233
347
  }
348
+ const provider = GetReadWriteProvider(context.providers) as IMetadataProvider;
349
+ const auth = await RoomAuthorizationService.Instance.AuthorizeRoomAccess(input.RoomName, user, provider);
350
+ if (!auth.Authorized) {
351
+ return failure(auth.Reason ?? 'Unauthorized room access.');
352
+ }
234
353
  const tokenService = new LiveKitTokenService();
235
354
  const minted = await tokenService.MintClientToken(input.RoomName, this.participantIdentity(user), input.DisplayName ?? user.Name ?? user.Email);
236
355
  return { Success: true, ...minted };
@@ -243,7 +362,9 @@ export class RealtimeBridgeResolver extends ResolverBase {
243
362
 
244
363
  /**
245
364
  * Starts (or reuses) an agent's presence in a LiveKit room and returns a client token so the calling
246
- * user can immediately join the same room.
365
+ * user can immediately join the same room. The token is minted before anything starts, so a start that can't give the
366
+ * user one fails with nothing started. When no `AgentSessionID` is passed, an agent session is created for the
367
+ * agent; if the agent then fails to start, that session is closed (`Error`) before the failure is returned.
247
368
  */
248
369
  @Mutation(() => LiveKitAgentRoomSessionResult)
249
370
  async StartLiveKitAgentRoomSession(
@@ -264,14 +385,44 @@ export class RealtimeBridgeResolver extends ResolverBase {
264
385
  if (!user) {
265
386
  return failure('Unable to determine current user.');
266
387
  }
267
- const provider = GetReadWriteProvider(context.providers) as unknown as IMetadataProvider;
388
+ const provider = GetReadWriteProvider(context.providers) as IMetadataProvider;
268
389
  const roomName = input.RoomName?.trim() || `mj-${randomUUID()}`;
269
390
 
391
+ if (input.RoomName?.trim()) {
392
+ const auth = await RoomAuthorizationService.Instance.AuthorizeRoomAccess(roomName, user, provider);
393
+ if (!auth.Authorized) {
394
+ return failure(auth.Reason ?? 'Unauthorized room access.', roomName);
395
+ }
396
+ }
397
+
398
+ // The user's token first: a start that can't let the user into the room fails before anything starts there. Minted
399
+ // after the agent joined, a failure left the agent in a room the user couldn't enter and gave the UI no bridge id to
400
+ // stop it by.
401
+ const clientToken = await new LiveKitTokenService().MintClientToken(roomName, this.participantIdentity(user), user.Name ?? user.Email);
402
+
403
+ // Opt-in: an agent that can bring a person, a number or another agent into this room (the same room, so a visitor
404
+ // is escalated without leaving it). Falls through to the standard start when the server cannot do handoffs.
405
+ const handoffSessionBridgeID = await this.startWithHandoff(input, roomName, user, provider);
406
+ if (handoffSessionBridgeID) {
407
+ return {
408
+ Success: true,
409
+ SessionBridgeID: handoffSessionBridgeID,
410
+ RoomName: roomName,
411
+ ServerUrl: clientToken.ServerUrl,
412
+ ClientToken: clientToken.Token,
413
+ Identity: clientToken.Identity,
414
+ };
415
+ }
416
+
270
417
  // Resolve the AIAgentSession the bridge will reference. The bridge row FK-references
271
418
  // AIAgentSession(ID), so we must use an EXISTING session — either one the caller supplied, or a
272
419
  // freshly-created one. Previously this minted a bare random UUID with no backing row, so the
273
420
  // bridge INSERT failed the FK_AIAgentSessionBridge_Session constraint.
274
421
  let agentSessionID = input.AgentSessionID?.trim();
422
+ // A session created here exists for this agent's time in the room alone, so it closes when the agent leaves, however
423
+ // it leaves, or at once if the agent fails to start (see startAgentInRoom). A session the caller supplied is the
424
+ // caller's to close.
425
+ let createdSessionID: string | undefined;
275
426
  if (!agentSessionID) {
276
427
  if (!input.AgentID?.trim()) {
277
428
  return failure('An AgentID is required to start an agent room session.', roomName);
@@ -282,9 +433,10 @@ export class RealtimeBridgeResolver extends ResolverBase {
282
433
  provider,
283
434
  );
284
435
  agentSessionID = createdSession.ID;
436
+ createdSessionID = createdSession.ID;
285
437
  }
286
438
 
287
- const session = await LiveKitAgentRoomCoordinator.Instance.StartAgentRoomSession({
439
+ const session = await this.startAgentInRoom(createdSessionID, user, provider, {
288
440
  AgentSessionID: agentSessionID,
289
441
  RoomName: roomName,
290
442
  AgentID: input.AgentID,
@@ -293,13 +445,11 @@ export class RealtimeBridgeResolver extends ResolverBase {
293
445
  RealtimeModelID: input.RealtimeModelID,
294
446
  RealtimeVoice: input.RealtimeVoice,
295
447
  TurnMode: this.normalizeTurnMode(input.TurnMode),
448
+ TurnAddressing: this.normalizeTurnAddressing(input.TurnAddressing),
296
449
  ContextUser: user,
297
450
  MetadataProvider: provider,
298
451
  });
299
452
 
300
- const tokenService = new LiveKitTokenService();
301
- const clientToken = await tokenService.MintClientToken(roomName, this.participantIdentity(user), user.Name ?? user.Email);
302
-
303
453
  return {
304
454
  Success: true,
305
455
  SessionBridgeID: session.SessionBridgeID,
@@ -319,6 +469,7 @@ export class RealtimeBridgeResolver extends ResolverBase {
319
469
  * Stops one agent's presence in a room (the bot leaves) — the remove half of in-room agent management.
320
470
  * Identified by the `SessionBridgeID` returned from {@link StartLiveKitAgentRoomSession}. Returns `true`
321
471
  * when the bridge was stopped. Best-effort: a missing/already-stopped bridge or any error resolves `false`.
472
+ * The agent session the start created for the agent is closed (`Explicit`) before this returns.
322
473
  *
323
474
  * @param sessionBridgeID The `MJ: AI Agent Session Bridges` row id of the agent to remove.
324
475
  */
@@ -332,7 +483,15 @@ export class RealtimeBridgeResolver extends ResolverBase {
332
483
  if (!user) {
333
484
  return false;
334
485
  }
335
- const provider = GetReadWriteProvider(context.providers) as unknown as IMetadataProvider;
486
+ const provider = GetReadWriteProvider(context.providers) as IMetadataProvider;
487
+ const roomName = LiveKitAgentRoomCoordinator.Instance.GetRoomForBridge(sessionBridgeID);
488
+ if (roomName) {
489
+ const auth = await RoomAuthorizationService.Instance.AuthorizeRoomAccess(roomName, user, provider);
490
+ if (!auth.Authorized) {
491
+ LogError(`StopLiveKitAgentRoomSession unauthorized: user ${user.ID} cannot access room ${roomName} (${auth.Reason})`);
492
+ return false;
493
+ }
494
+ }
336
495
  return await LiveKitAgentRoomCoordinator.Instance.StopAgentRoomSession(sessionBridgeID, 'Explicit', user, provider);
337
496
  } catch (error) {
338
497
  LogError(`StopLiveKitAgentRoomSession failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -345,6 +504,7 @@ export class RealtimeBridgeResolver extends ResolverBase {
345
504
  * coordinator's server-side roster). This is the "End meeting" half of the Zoom-style leave control —
346
505
  * usable by any participant, including one who only *joined* the room and never tracked the bridge ids.
347
506
  * Returns `true` when the teardown ran (even if the room held zero agents). Best-effort: any error → `false`.
507
+ * The agent sessions the starts created for the agents are closed (`Explicit`) as each agent leaves.
348
508
  *
349
509
  * @param roomName The LiveKit room to end.
350
510
  */
@@ -358,7 +518,12 @@ export class RealtimeBridgeResolver extends ResolverBase {
358
518
  if (!user) {
359
519
  return false;
360
520
  }
361
- const provider = GetReadWriteProvider(context.providers) as unknown as IMetadataProvider;
521
+ const provider = GetReadWriteProvider(context.providers) as IMetadataProvider;
522
+ const auth = await RoomAuthorizationService.Instance.AuthorizeRoomAccess(roomName, user, provider);
523
+ if (!auth.Authorized) {
524
+ LogError(`EndLiveKitRoom unauthorized: user ${user.ID} cannot access room ${roomName} (${auth.Reason})`);
525
+ return false;
526
+ }
362
527
  await LiveKitAgentRoomCoordinator.Instance.StopAllAgentsInRoom(roomName, 'Explicit', user, provider);
363
528
  return true;
364
529
  } catch (error) {
@@ -367,6 +532,37 @@ export class RealtimeBridgeResolver extends ResolverBase {
367
532
  }
368
533
  }
369
534
 
535
+ /**
536
+ * A room's live turn-taking state — who holds the floor, whether a person is speaking, any pending hand-off,
537
+ * the agent-to-agent loop-cap progress, the most recent floor/backchannel/yield events, and who is seated. The
538
+ * agent test bed polls it. Read-only. `StateJSON` is `null` when no agent is in the room.
539
+ *
540
+ * @param roomName The LiveKit room.
541
+ */
542
+ @Query(() => LiveKitRoomTurnStateResult)
543
+ async GetLiveKitRoomTurnState(
544
+ @Arg('roomName', () => String) roomName: string,
545
+ @Ctx() context: AppContext = {} as AppContext,
546
+ ): Promise<LiveKitRoomTurnStateResult> {
547
+ try {
548
+ const user = this.GetUserFromPayload(context.userPayload);
549
+ if (!user) {
550
+ return { Success: false, ErrorMessage: 'Unable to determine current user.' };
551
+ }
552
+ const provider = GetReadWriteProvider(context.providers) as IMetadataProvider;
553
+ const auth = await RoomAuthorizationService.Instance.AuthorizeRoomAccess(roomName, user, provider);
554
+ if (!auth.Authorized) {
555
+ return { Success: false, ErrorMessage: auth.Reason ?? 'Unauthorized room access.' };
556
+ }
557
+ const snapshot = LiveKitAgentRoomCoordinator.Instance.GetRoomTurnState(roomName);
558
+ return { Success: true, StateJSON: snapshot ? JSON.stringify(snapshot) : undefined };
559
+ } catch (error) {
560
+ const msg = error instanceof Error ? error.message : String(error);
561
+ LogError(`GetLiveKitRoomTurnState failed: ${msg}`);
562
+ return { Success: false, ErrorMessage: msg };
563
+ }
564
+ }
565
+
370
566
  /**
371
567
  * Lists active Realtime models with the voices each driver supports — the source for the dev model/voice
372
568
  * picker (gated client-side by the `Realtime: Advanced Session Controls` authorization). Read-only; returns
@@ -381,7 +577,7 @@ export class RealtimeBridgeResolver extends ResolverBase {
381
577
  if (!user) {
382
578
  return [];
383
579
  }
384
- const provider = GetReadWriteProvider(context.providers) as unknown as IMetadataProvider;
580
+ const provider = GetReadWriteProvider(context.providers) as IMetadataProvider;
385
581
  return await GetRealtimeModelVoices(user, provider);
386
582
  } catch (error) {
387
583
  LogError(`GetRealtimeModelVoices failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -411,7 +607,12 @@ export class RealtimeBridgeResolver extends ResolverBase {
411
607
  if (!user) {
412
608
  return false;
413
609
  }
414
- const provider = GetReadWriteProvider(context.providers) as unknown as IMetadataProvider;
610
+ const provider = GetReadWriteProvider(context.providers) as IMetadataProvider;
611
+ const auth = await RoomAuthorizationService.Instance.AuthorizeRoomAccess(roomName, user, provider);
612
+ if (!auth.Authorized) {
613
+ LogError(`InviteUsersToLiveKitRoom unauthorized: user ${user.ID} cannot access room ${roomName} (${auth.Reason})`);
614
+ return false;
615
+ }
415
616
  await NotificationEngine.Instance.Config(false, user, provider);
416
617
 
417
618
  const inviter = user.Name?.trim() || user.Email || 'Someone';
@@ -453,11 +654,15 @@ export class RealtimeBridgeResolver extends ResolverBase {
453
654
  if (!user) {
454
655
  return { Success: false, ErrorMessage: 'Unable to determine current user.', EgressID: '', Status: '' };
455
656
  }
657
+ const provider = GetReadWriteProvider(context.providers) as IMetadataProvider;
658
+ const auth = await RoomAuthorizationService.Instance.AuthorizeRoomAccess(input.RoomName, user, provider);
659
+ if (!auth.Authorized) {
660
+ return { Success: false, ErrorMessage: auth.Reason ?? 'Unauthorized room access.', EgressID: '', Status: '' };
661
+ }
456
662
  const info = await new LiveKitEgressService().StartRoomRecording({ RoomName: input.RoomName, Layout: input.Layout });
457
663
 
458
664
  // Best-effort: correlate the live recording with the room's Meeting-Room Conversation (if it exists
459
665
  // yet) by stamping its EgressID. Never fail the start on this — the stop-flow resolves/creates it.
460
- const provider = GetReadWriteProvider(context.providers) as unknown as IMetadataProvider;
461
666
  void CorrelateRecordingStart(input.RoomName, info.EgressID, user, provider);
462
667
 
463
668
  return { Success: true, EgressID: info.EgressID, Status: info.Status };
@@ -481,8 +686,43 @@ export class RealtimeBridgeResolver extends ResolverBase {
481
686
  if (!user) {
482
687
  return { Success: false, ErrorMessage: 'Unable to determine current user.', EgressID: egressID, Status: '' };
483
688
  }
484
- const info = await new LiveKitEgressService().StopRecording(egressID);
485
- const provider = GetReadWriteProvider(context.providers) as unknown as IMetadataProvider;
689
+ const provider = GetReadWriteProvider(context.providers) as IMetadataProvider;
690
+
691
+ // Map egress ID to room name: check LiveKit egress service first, fallback to Conversation if available.
692
+ const egressService = new LiveKitEgressService();
693
+ let roomName = await egressService.GetRoomForEgress(egressID);
694
+ if (!roomName && provider) {
695
+ try {
696
+ const rv = RunView.FromMetadataProvider(provider);
697
+ const convResult = await rv.RunView<{ ExternalID: string }>(
698
+ {
699
+ EntityName: 'MJ: Conversations',
700
+ ExtraFilter: `EgressID='${EscapeSQLString(egressID)}' AND (IsArchived IS NULL OR IsArchived=0)`,
701
+ Fields: ['ExternalID'],
702
+ OrderBy: '__mj_CreatedAt DESC',
703
+ MaxRows: 1,
704
+ ResultType: 'simple',
705
+ },
706
+ user,
707
+ );
708
+ if (convResult.Success && convResult.Results.length > 0 && convResult.Results[0].ExternalID) {
709
+ roomName = convResult.Results[0].ExternalID;
710
+ }
711
+ } catch (err) {
712
+ LogError(`StopLiveKitRecording: error looking up Conversation for egress ID '${egressID}'`, undefined, err);
713
+ }
714
+ }
715
+
716
+ if (roomName) {
717
+ const auth = await RoomAuthorizationService.Instance.AuthorizeRoomAccess(roomName, user, provider);
718
+ if (!auth.Authorized) {
719
+ return { Success: false, ErrorMessage: auth.Reason ?? 'Unauthorized room access.', EgressID: egressID, Status: '' };
720
+ }
721
+ } else {
722
+ return { Success: false, ErrorMessage: `Unable to determine room for egress ID '${egressID}'.`, EgressID: egressID, Status: '' };
723
+ }
724
+
725
+ const info = await egressService.StopRecording(egressID);
486
726
 
487
727
  // Register the completed egress MP4 as a Files row on the Meeting-Room Conversation. Best-effort:
488
728
  // any failure (e.g. storage provider not configured) leaves RecordingFileID unset but still
@@ -504,9 +744,135 @@ export class RealtimeBridgeResolver extends ResolverBase {
504
744
  }
505
745
  }
506
746
 
507
- /** Builds a stable, lowercased participant identity from the authenticated user. */
747
+ /**
748
+ * Records whether the current user lets agents see their camera and shared screen in a room. The participant is
749
+ * worked out from the authenticated user, never sent by the client, so a person can only answer for themselves. To
750
+ * allow, the room must be one they may enter ({@link RoomAuthorizationService}, as for every room mutation). A
751
+ * withdrawal skips that check: a cancelled meeting or a failed lookup must not keep an agent watching someone who is
752
+ * still in the room, and the identity is always the caller's own. LiveKit refuses either way when they are not in it. The choice is set as their `mj.agentCanSee` attribute through LiveKit's server SDK (a
753
+ * participant's token can't change its own attributes) and audited, applied or not.
754
+ */
755
+ @Mutation(() => LiveKitAgentVisionResult)
756
+ async SetLiveKitAgentVision(
757
+ @Arg('input', () => SetLiveKitAgentVisionInput) input: SetLiveKitAgentVisionInput,
758
+ @Ctx() context: AppContext = {} as AppContext,
759
+ ): Promise<LiveKitAgentVisionResult> {
760
+ try {
761
+ const user = this.GetUserFromPayload(context.userPayload);
762
+ if (!user) {
763
+ return { Success: false, ErrorMessage: 'Unable to determine current user.' };
764
+ }
765
+ const provider = GetReadWriteProvider(context.providers) as unknown as IMetadataProvider;
766
+ const auth: RoomAuthorizationResult = input.Allow
767
+ ? await RoomAuthorizationService.Instance.AuthorizeRoomAccess(input.RoomName, user, provider)
768
+ : { Authorized: true };
769
+ const result: ParticipantUpdateResult = auth.Authorized
770
+ ? await new LiveKitParticipantService().SetAgentVision(input.RoomName, this.participantIdentity(user), input.Allow)
771
+ : { Success: false, ErrorMessage: auth.Reason ?? 'Unauthorized room access.' };
772
+ await WriteAgentVisionConsentAudit(
773
+ { User: user, RoomName: input.RoomName, Allow: input.Allow, Applied: result.Success, ErrorMessage: result.ErrorMessage },
774
+ provider,
775
+ );
776
+ if (result.Success) {
777
+ return { Success: true };
778
+ }
779
+ return { Success: false, ErrorMessage: result.NotInRoom ? 'You are not in this room.' : result.ErrorMessage };
780
+ } catch (error) {
781
+ const msg = error instanceof Error ? error.message : String(error);
782
+ LogError(`SetLiveKitAgentVision failed: ${msg}`);
783
+ return { Success: false, ErrorMessage: msg };
784
+ }
785
+ }
786
+
787
+ /**
788
+ * Starts the agent through the handoff-capable room starter when the caller asked for it and the server can do it.
789
+ * Returns the bridge id, or `undefined` to mean "use the standard start" (not requested, no starter, no agent named).
790
+ */
791
+ private async startWithHandoff(
792
+ input: StartLiveKitAgentRoomSessionInput,
793
+ roomName: string,
794
+ user: UserInfo,
795
+ provider: IMetadataProvider,
796
+ ): Promise<string | undefined> {
797
+ if (!input.EnableHandoff) {
798
+ return undefined;
799
+ }
800
+ const starter = RoomHandoffEngine.Instance.Deps.AgentStarter;
801
+ const agentID = input.TargetAgentID?.trim() || input.AgentID?.trim();
802
+ if (!starter || !agentID) {
803
+ LogError('StartLiveKitAgentRoomSession: handoff was requested but this server has no handoff-capable agent starter; starting the agent without it.');
804
+ return undefined;
805
+ }
806
+ const started = await starter({
807
+ RoomName: roomName,
808
+ AgentID: agentID,
809
+ AgentName: input.AgentName?.trim() ?? '',
810
+ CallerLabel: user.Name?.trim() || user.Email || 'Web visitor',
811
+ ContextUser: user,
812
+ Provider: provider,
813
+ RealtimeModelID: input.RealtimeModelID,
814
+ RealtimeVoice: input.RealtimeVoice,
815
+ });
816
+ return started.SessionBridgeID;
817
+ }
818
+
819
+ /**
820
+ * Starts the agent in the room through the coordinator. An agent session created for this agent's time in the room
821
+ * (`createdSessionID`) is this start's to close: once the agent has joined, through the end-of-session hook from
822
+ * {@link closeSessionWhenAgentLeaves}; when the start fails, at once, as `Error`, through
823
+ * {@link SessionManager.CloseSessionForFailedStart}, before the error propagates, since the agent never joined and that
824
+ * hook never runs (#5308). By then the coordinator has closed any model session the start opened, its co-agent run
825
+ * finalized as failed. A session the caller supplied gets neither: it is the caller's.
826
+ */
827
+ private async startAgentInRoom(
828
+ createdSessionID: string | undefined,
829
+ user: UserInfo,
830
+ provider: IMetadataProvider,
831
+ params: Omit<StartAgentRoomSessionParams, 'Host'>,
832
+ ): Promise<AgentRoomSession> {
833
+ const coordinator = LiveKitAgentRoomCoordinator.Instance;
834
+ if (!createdSessionID) {
835
+ return coordinator.StartAgentRoomSession(params);
836
+ }
837
+ try {
838
+ return await coordinator.StartAgentRoomSession({ ...params, Host: this.closeSessionWhenAgentLeaves(createdSessionID, user, provider) });
839
+ } catch (error) {
840
+ await this.sessionManager.CloseSessionForFailedStart(createdSessionID, user, provider);
841
+ throw error;
842
+ }
843
+ }
844
+
845
+ /**
846
+ * What the coordinator is given for an agent session created for one agent's time in a room: its end-of-session hook
847
+ * closes the session once the agent's bridge has ended, whatever ended it (the agent stopped, the meeting ended, everyone
848
+ * left, the model session lost), through {@link SessionManager.CloseSessionForEndedBridge}. The close runs as the user
849
+ * who started the agent, whoever stops it: the session is theirs.
850
+ */
851
+ private closeSessionWhenAgentLeaves(agentSessionID: string, user: UserInfo, provider: IMetadataProvider): AgentRoomHostOptions {
852
+ return {
853
+ OnSessionEnded: async (reason) => {
854
+ await this.sessionManager.CloseSessionForEndedBridge(agentSessionID, reason, user, provider);
855
+ },
856
+ };
857
+ }
858
+
859
+ /** Builds a stable, lowercased participant identity from the authenticated user (shared with the handoff engine, which watches for it). */
508
860
  private participantIdentity(user: UserInfo): string {
509
- return `user-${user.ID}`.toLowerCase();
861
+ return LiveKitUserIdentity(user.ID);
862
+ }
863
+
864
+ /** Normalizes an addressing-mode string; unknown/absent leaves the coordinator's default (`Auto`). */
865
+ private normalizeTurnAddressing(mode?: string): TurnAddressingMode | undefined {
866
+ switch ((mode ?? '').trim().toLowerCase()) {
867
+ case 'auto':
868
+ return 'Auto';
869
+ case 'modelside':
870
+ return 'ModelSide';
871
+ case 'regex':
872
+ return 'Regex';
873
+ default:
874
+ return undefined;
875
+ }
510
876
  }
511
877
 
512
878
  /** Normalizes a turn-mode string to the bridge's accepted values. */