@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
@@ -7,8 +7,10 @@ import {
7
7
  } from '@memberjunction/core-entities';
8
8
  import { AIAgentPermissionHelper } from '@memberjunction/ai-engine-base';
9
9
  import { RealtimeClientSessionService, RealtimeChannelServerHost } from '@memberjunction/ai-agents';
10
+ import type { BridgeDisconnectReason } from '@memberjunction/ai-bridge-base';
10
11
  import { GetHostInstanceID } from './HostInstance.js';
11
12
  import { WriteReturningVisitorRecap } from './ReturningVisitorRecap.js';
13
+ import { SessionRunIDVerifier, SessionRunIDs } from './SessionRunIDVerifier.js';
12
14
  import { ResolveScopedAnonymousRunUser } from '../realtimeWidget/widgetGuestElevation.js';
13
15
 
14
16
  /** Entity names — centralised so the `MJ:`-prefix convention is applied in exactly one place. */
@@ -16,18 +18,6 @@ const SESSION_ENTITY = 'MJ: AI Agent Sessions';
16
18
  const SESSION_CHANNEL_ENTITY = 'MJ: AI Agent Session Channels';
17
19
  const CONVERSATION_ENTITY = 'MJ: Conversations';
18
20
 
19
- /**
20
- * Shape of the observability run ids a realtime voice session persists in `AIAgentSession.Config_`.
21
- * Read on {@link SessionManager.CloseSession} to finalize the co-agent runs. All fields optional —
22
- * a non-voice session (or one whose run creation was skipped) carries none of them.
23
- */
24
- interface SessionRunConfig {
25
- coAgentRunID?: string;
26
- promptRunID?: string;
27
- /** The co-agent run's single system-prompt `MJ: AI Agent Run Steps` row id (its Timeline entry). */
28
- coAgentRunStepID?: string;
29
- }
30
-
31
21
  /**
32
22
  * Inputs for {@link SessionManager.CreateSession}. `conversationID`/`lastSessionID`/`config`
33
23
  * are optional — when no `conversationID` is supplied a fresh Conversation is created and linked.
@@ -54,6 +44,24 @@ export interface CreateSessionInput {
54
44
  */
55
45
  export type SessionCloseReason = 'Explicit' | 'Janitor' | 'Shutdown' | 'Error';
56
46
 
47
+ /**
48
+ * The {@link SessionCloseReason} for a session whose bridged agent has left (see
49
+ * {@link SessionManager.CloseSessionForEndedBridge}). The bridge's reason carries over where the session has the same one;
50
+ * `HostEnded` (everyone left the room, the call hung up) closes it as `Explicit`, people ending it, as phone calls close
51
+ * theirs.
52
+ */
53
+ function sessionCloseReasonForBridgeEnd(reason: BridgeDisconnectReason): SessionCloseReason {
54
+ switch (reason) {
55
+ case 'Error':
56
+ case 'Janitor':
57
+ case 'Shutdown':
58
+ return reason;
59
+ case 'Explicit':
60
+ case 'HostEnded':
61
+ return 'Explicit';
62
+ }
63
+ }
64
+
57
65
  /**
58
66
  * Thrown by {@link SessionManager.CreateSession} when the caller lacks `CanRun` on the target
59
67
  * agent. No session row is created — denial happens before any write. Distinct error type so the
@@ -126,6 +134,9 @@ export class SessionManager {
126
134
  */
127
135
  private readonly realtimeClientSessionService: RealtimeClientSessionService;
128
136
 
137
+ /** Checks the run ids read from a session's owner-editable `Config_` before they are used. */
138
+ private readonly runIDVerifier = new SessionRunIDVerifier();
139
+
129
140
  /**
130
141
  * @param realtimeClientSessionService The shared {@link RealtimeClientSessionService} instance to
131
142
  * finalize observability runs through. Pass the SAME instance your resolver/service used to create
@@ -211,6 +222,68 @@ export class SessionManager {
211
222
  return true;
212
223
  }
213
224
 
225
+ /**
226
+ * Closes a session that was created for one bridged agent (a bot in a meeting room) once that agent's bridge has
227
+ * ended, whatever ended it: the agent was stopped, the meeting was ended, everyone left, the model session was lost.
228
+ * Without this the session stays `Active` until the janitor's staleness sweep closes it, 15 minutes or more later.
229
+ *
230
+ * The close is {@link CloseSession}, the one the janitor runs, with the bridge's reason: `Explicit` for a stop, an
231
+ * ended meeting or an emptied room (`HostEnded`), otherwise the bridge's own (`Error`, `Janitor`, `Shutdown`). A
232
+ * session already closed (by the janitor, or by its user) keeps its reason.
233
+ *
234
+ * Never throws: it runs as the bridge's end-of-session bookkeeping. A failure is logged and leaves the session to the
235
+ * janitor, as before.
236
+ *
237
+ * @param agentSessionID The session created for the bridged agent.
238
+ * @param reason Why the bridge ended.
239
+ * @param contextUser The user the session was created for.
240
+ * @param provider The metadata provider the session was created through.
241
+ * @returns `true` when the session is (or already was) closed.
242
+ */
243
+ public async CloseSessionForEndedBridge(
244
+ agentSessionID: string,
245
+ reason: BridgeDisconnectReason,
246
+ contextUser: UserInfo,
247
+ provider: IMetadataProvider,
248
+ ): Promise<boolean> {
249
+ try {
250
+ return await this.CloseSession(agentSessionID, contextUser, provider, sessionCloseReasonForBridgeEnd(reason));
251
+ } catch (e) {
252
+ LogError(
253
+ `SessionManager.CloseSessionForEndedBridge could not close agent session ${agentSessionID} after its bridge ended ` +
254
+ `(${reason}); the janitor will: ${e instanceof Error ? e.message : String(e)}`,
255
+ );
256
+ return false;
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Closes a session that was created for one agent's time in a room when that agent failed to start: its model session
262
+ * could not open, the bot's token could not be minted, the bridge did not start. The agent never joined, so no
263
+ * end-of-session hook will close the session; without this it stays `Active` until the janitor's staleness sweep.
264
+ *
265
+ * The close is {@link CloseSession} with reason `Error`. A session already closed keeps its reason.
266
+ *
267
+ * Never throws: it runs while the caller handles the failed start, whose error is the one the caller reports. A failure
268
+ * is logged and leaves the session to the janitor.
269
+ *
270
+ * @param agentSessionID The session created for the agent.
271
+ * @param contextUser The user the session was created for.
272
+ * @param provider The metadata provider the session was created through.
273
+ * @returns `true` when the session is (or already was) closed.
274
+ */
275
+ public async CloseSessionForFailedStart(agentSessionID: string, contextUser: UserInfo, provider: IMetadataProvider): Promise<boolean> {
276
+ try {
277
+ return await this.CloseSession(agentSessionID, contextUser, provider, 'Error');
278
+ } catch (e) {
279
+ LogError(
280
+ `SessionManager.CloseSessionForFailedStart could not close agent session ${agentSessionID} after its agent failed ` +
281
+ `to start; the janitor will: ${e instanceof Error ? e.message : String(e)}`,
282
+ );
283
+ return false;
284
+ }
285
+ }
286
+
214
287
  /**
215
288
  * Notifies the server-side channel-plugin host that a session started, so it can resolve the
216
289
  * ACTIVE `MJ: AI Agent Channels` rows' `ServerPluginClass` plugins (one fresh instance per
@@ -260,7 +333,8 @@ export class SessionManager {
260
333
 
261
334
  /**
262
335
  * Finalizes the co-agent observability runs a realtime voice session stored in its `Config_`
263
- * (see `RealtimeClientSessionResolver`). No-op when the config carries no run ids. Tolerant: a
336
+ * (see `RealtimeClientSessionResolver`). Only run ids that are records of the session are
337
+ * finalized, because the owner can edit `Config_`. No-op when none remain. Tolerant: a
264
338
  * malformed config or a finalize failure is swallowed so it can never break session close.
265
339
  */
266
340
  private async finalizeObservabilityRuns(
@@ -268,8 +342,8 @@ export class SessionManager {
268
342
  contextUser: UserInfo,
269
343
  provider: IMetadataProvider,
270
344
  ): Promise<void> {
271
- const config = this.parseSessionRunConfig(session.Config_);
272
- if (!config.coAgentRunID && !config.promptRunID && !config.coAgentRunStepID) {
345
+ const claimed = this.parseSessionRunConfig(session.Config_);
346
+ if (!claimed.CoAgentRunID && !claimed.PromptRunID && !claimed.CoAgentRunStepID) {
273
347
  return;
274
348
  }
275
349
  try {
@@ -277,36 +351,54 @@ export class SessionManager {
277
351
  // for a scoped anonymous session, so an owner-initiated (or error) close must finalize
278
352
  // under it too — the caller's role holds no grants on the AI run entities. Janitor and
279
353
  // shutdown sweeps already close as the system user and pass through unchanged.
354
+ const runUser = ResolveScopedAnonymousRunUser(contextUser);
355
+ const runIDs = await this.runIDVerifier.Verify(session.ID, claimed, runUser, provider);
356
+ if (!runIDs.CoAgentRunID && !runIDs.PromptRunID && !runIDs.CoAgentRunStepID) {
357
+ return;
358
+ }
280
359
  await this.realtimeClientSessionService.FinalizeCoAgentRun(
281
- config.coAgentRunID ?? null,
282
- config.promptRunID ?? null,
283
- ResolveScopedAnonymousRunUser(contextUser),
360
+ runIDs.CoAgentRunID ?? null,
361
+ runIDs.PromptRunID ?? null,
362
+ runUser,
284
363
  provider,
285
364
  true,
286
- config.coAgentRunStepID ?? null,
365
+ runIDs.CoAgentRunStepID ?? null,
287
366
  );
288
367
  } catch (e) {
289
368
  LogError(`SessionManager.finalizeObservabilityRuns failed: ${e instanceof Error ? e.message : String(e)}`);
290
369
  }
291
370
  }
292
371
 
293
- /** Parses the session's `Config_` for observability run ids; returns an empty config on any error. */
294
- private parseSessionRunConfig(raw: string | null): SessionRunConfig {
372
+ /** Parses the session's `Config_` for observability run ids; returns none on any error. */
373
+ private parseSessionRunConfig(raw: string | null): SessionRunIDs {
295
374
  if (!raw) {
296
375
  return {};
297
376
  }
298
377
  try {
299
- const parsed = JSON.parse(raw) as Partial<SessionRunConfig>;
378
+ const parsed = JSON.parse(raw) as { coAgentRunID?: unknown; promptRunID?: unknown; coAgentRunStepID?: unknown };
300
379
  return {
301
- coAgentRunID: typeof parsed.coAgentRunID === 'string' ? parsed.coAgentRunID : undefined,
302
- promptRunID: typeof parsed.promptRunID === 'string' ? parsed.promptRunID : undefined,
303
- coAgentRunStepID: typeof parsed.coAgentRunStepID === 'string' ? parsed.coAgentRunStepID : undefined,
380
+ CoAgentRunID: typeof parsed.coAgentRunID === 'string' ? parsed.coAgentRunID : undefined,
381
+ PromptRunID: typeof parsed.promptRunID === 'string' ? parsed.promptRunID : undefined,
382
+ CoAgentRunStepID: typeof parsed.coAgentRunStepID === 'string' ? parsed.coAgentRunStepID : undefined,
304
383
  };
305
384
  } catch {
306
385
  return {};
307
386
  }
308
387
  }
309
388
 
389
+ /** The session's co-agent run id from its `Config_`, when it is a run of the session. */
390
+ private async verifiedCoAgentRunID(
391
+ session: MJAIAgentSessionEntity,
392
+ runUser: UserInfo,
393
+ provider: IMetadataProvider,
394
+ ): Promise<string | undefined> {
395
+ const { CoAgentRunID } = this.parseSessionRunConfig(session.Config_);
396
+ if (!CoAgentRunID) {
397
+ return undefined;
398
+ }
399
+ return (await this.runIDVerifier.Verify(session.ID, { CoAgentRunID }, runUser, provider)).CoAgentRunID;
400
+ }
401
+
310
402
  /**
311
403
  * Record activity on a session. Coalesces persisted writes so at most one `LastActiveAt` update
312
404
  * lands per session per {@link HEARTBEAT_MIN_WRITE_INTERVAL_MS}; intervening calls return `true`
@@ -565,6 +657,14 @@ export class SessionManager {
565
657
  const saved = await this.saveOrLog(session, 'Heartbeat');
566
658
  if (saved) {
567
659
  this.heartbeatLastWrite.Set(agentSessionID.toLowerCase(), Date.now());
660
+ // A live session keeps its co-agent run live too — on whichever instance gets its heartbeats —
661
+ // or the agent-run watchdog force-fails the run mid-call. Same elevation as the finalize path.
662
+ const runUser = ResolveScopedAnonymousRunUser(contextUser);
663
+ this.realtimeClientSessionService.KeepCoAgentRunAlive(
664
+ await this.verifiedCoAgentRunID(session, runUser, provider),
665
+ provider,
666
+ runUser,
667
+ );
568
668
  }
569
669
  return saved;
570
670
  }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * @fileoverview Checks the run ids a realtime session keeps in its `Config` before server code
3
+ * acts on them.
4
+ *
5
+ * @module @memberjunction/server
6
+ */
7
+ import { IMetadataProvider, LogError, RunView, RunViewParams, UserInfo } from '@memberjunction/core';
8
+ import { EscapeSQLString, IsValidUUID, UUIDsEqual } from '@memberjunction/global';
9
+ import { UserCache } from '@memberjunction/generic-database-provider';
10
+
11
+ const AGENT_RUN_ENTITY = 'MJ: AI Agent Runs';
12
+ const AGENT_RUN_STEP_ENTITY = 'MJ: AI Agent Run Steps';
13
+
14
+ /**
15
+ * The run ids a realtime voice session keeps in its `Config` JSON. Server code reads and writes the
16
+ * records they point at, often as an elevated user.
17
+ */
18
+ export interface SessionRunIDs {
19
+ /** The co-agent observability run (`MJ: AI Agent Runs`). */
20
+ CoAgentRunID?: string;
21
+ /** The co-agent prompt run (`MJ: AI Prompt Runs`), linked through the co-agent run's prompt step. */
22
+ PromptRunID?: string;
23
+ /** The co-agent run's system-prompt step (`MJ: AI Agent Run Steps`). */
24
+ CoAgentRunStepID?: string;
25
+ /** A delegated run (`MJ: AI Agent Runs`) that paused for the user's answer. */
26
+ PendingFeedbackRunID?: string;
27
+ }
28
+
29
+ /** A row of the run-step lookup. */
30
+ interface RunStepRow {
31
+ ID: string;
32
+ StepType: string;
33
+ TargetLogID: string | null;
34
+ }
35
+
36
+ /** A row of either lookup; the run lookup returns only `ID`. */
37
+ type LookupRow = Partial<RunStepRow> & { ID: string };
38
+
39
+ /**
40
+ * Keeps the run ids from a session's `Config` that are records of that session.
41
+ *
42
+ * The session owner can edit `Config`, so its run ids are claims. A run id is kept when it is the
43
+ * UUID of an `MJ: AI Agent Runs` row whose `AgentSessionID` is the session. The step id and the
44
+ * prompt run id are kept when they belong to that co-agent run: the step's `AgentRunID` is the
45
+ * co-agent run, and the prompt run is the `TargetLogID` of one of its `Prompt` steps.
46
+ *
47
+ * The lookups run as the system user when one is cached, else as the acting user. They are pinned
48
+ * to the session's id and return nothing to the caller, and row-level security can hide a session's
49
+ * own runs from its owner (a web-widget guest's delegated runs carry no `ConversationID`, which the
50
+ * guest's run filter keys on).
51
+ *
52
+ * Each call reads the database again (`BypassCache`) and keeps no result, because the links can
53
+ * change after a lookup: a run's `AgentSessionID` and a step's `AgentRunID` and `TargetLogID` are
54
+ * updatable.
55
+ */
56
+ export class SessionRunIDVerifier {
57
+ /**
58
+ * Returns the subset of `claimed` that belongs to the session now. Never throws: when a lookup
59
+ * fails, nothing is kept.
60
+ *
61
+ * @param sessionID The `MJ: AI Agent Sessions` id the claims were read from. The caller must have
62
+ * checked that the session is the caller's own.
63
+ * @param claimed The run ids read from the session's `Config`.
64
+ * @param contextUser The acting user; the lookups run as it only when no system user is cached.
65
+ * @param provider The request-scoped metadata provider.
66
+ * @returns The claims that are records of the session.
67
+ */
68
+ public async Verify(
69
+ sessionID: string,
70
+ claimed: SessionRunIDs,
71
+ contextUser: UserInfo,
72
+ provider: IMetadataProvider,
73
+ ): Promise<SessionRunIDs> {
74
+ const candidates = this.keepUUIDs(claimed);
75
+ if (!candidates.CoAgentRunID && !candidates.PendingFeedbackRunID) {
76
+ return {};
77
+ }
78
+ return (await this.lookUpOwned(sessionID.trim(), candidates, contextUser, provider)) ?? {};
79
+ }
80
+
81
+ /** Drops every claim that is not a UUID. The prompt run and step need a co-agent run to hang off. */
82
+ private keepUUIDs(claimed: SessionRunIDs): SessionRunIDs {
83
+ const coAgentRunID = this.uuidOrUndefined(claimed.CoAgentRunID);
84
+ return {
85
+ CoAgentRunID: coAgentRunID,
86
+ PromptRunID: coAgentRunID ? this.uuidOrUndefined(claimed.PromptRunID) : undefined,
87
+ CoAgentRunStepID: coAgentRunID ? this.uuidOrUndefined(claimed.CoAgentRunStepID) : undefined,
88
+ PendingFeedbackRunID: this.uuidOrUndefined(claimed.PendingFeedbackRunID),
89
+ };
90
+ }
91
+
92
+ private uuidOrUndefined(value: string | undefined): string | undefined {
93
+ return value && IsValidUUID(value) ? value.trim() : undefined;
94
+ }
95
+
96
+ /** Runs the lookups; `null` when one of them fails. */
97
+ private async lookUpOwned(
98
+ sessionID: string,
99
+ candidates: SessionRunIDs,
100
+ contextUser: UserInfo,
101
+ provider: IMetadataProvider,
102
+ ): Promise<SessionRunIDs | null> {
103
+ const stepsQuery = this.stepsQuery(candidates);
104
+ const queries = stepsQuery ? [this.runsQuery(sessionID, candidates), stepsQuery] : [this.runsQuery(sessionID, candidates)];
105
+ try {
106
+ const lookupUser = UserCache.Instance.GetSystemUser() ?? contextUser;
107
+ const [runs, steps] = await RunView.FromMetadataProvider(provider).RunViews<LookupRow>(queries, lookupUser);
108
+ const failed = [runs, steps].find((result) => result && !result.Success);
109
+ if (!runs || failed) {
110
+ LogError(`SessionRunIDVerifier: run lookup failed for session ${sessionID}: ${failed?.ErrorMessage ?? 'no result'}`);
111
+ return null;
112
+ }
113
+ return this.pickOwned(candidates, runs.Results, steps?.Results ?? []);
114
+ } catch (error) {
115
+ LogError(`SessionRunIDVerifier: run lookup failed for session ${sessionID}: ${error instanceof Error ? error.message : String(error)}`);
116
+ return null;
117
+ }
118
+ }
119
+
120
+ /** The claimed runs that carry this session's id. */
121
+ private runsQuery(sessionID: string, candidates: SessionRunIDs): RunViewParams {
122
+ const ids = [candidates.CoAgentRunID, candidates.PendingFeedbackRunID]
123
+ .filter((id): id is string => !!id)
124
+ .map((id) => `'${EscapeSQLString(id)}'`);
125
+ return {
126
+ EntityName: AGENT_RUN_ENTITY,
127
+ ExtraFilter: `AgentSessionID='${EscapeSQLString(sessionID)}' AND ID IN (${ids.join(', ')})`,
128
+ Fields: ['ID'],
129
+ ResultType: 'simple',
130
+ BypassCache: true,
131
+ };
132
+ }
133
+
134
+ /** The claimed co-agent run's steps that match the claimed step or prompt run; `null` when neither is claimed. */
135
+ private stepsQuery(candidates: SessionRunIDs): RunViewParams | null {
136
+ const matches: string[] = [];
137
+ if (candidates.CoAgentRunStepID) {
138
+ matches.push(`ID='${EscapeSQLString(candidates.CoAgentRunStepID)}'`);
139
+ }
140
+ if (candidates.PromptRunID) {
141
+ matches.push(`TargetLogID='${EscapeSQLString(candidates.PromptRunID)}'`);
142
+ }
143
+ if (!candidates.CoAgentRunID || matches.length === 0) {
144
+ return null;
145
+ }
146
+ return {
147
+ EntityName: AGENT_RUN_STEP_ENTITY,
148
+ ExtraFilter: `AgentRunID='${EscapeSQLString(candidates.CoAgentRunID)}' AND (${matches.join(' OR ')})`,
149
+ Fields: ['ID', 'StepType', 'TargetLogID'],
150
+ ResultType: 'simple',
151
+ BypassCache: true,
152
+ };
153
+ }
154
+
155
+ private pickOwned(candidates: SessionRunIDs, runs: LookupRow[], steps: LookupRow[]): SessionRunIDs {
156
+ const isOwnedRun = (id: string | undefined): boolean => !!id && runs.some((row) => UUIDsEqual(row.ID, id));
157
+ const coAgentRunID = isOwnedRun(candidates.CoAgentRunID) ? candidates.CoAgentRunID : undefined;
158
+ const ownedSteps = coAgentRunID ? steps : [];
159
+ const stepID = candidates.CoAgentRunStepID;
160
+ const promptRunID = candidates.PromptRunID;
161
+ return {
162
+ CoAgentRunID: coAgentRunID,
163
+ CoAgentRunStepID: stepID && ownedSteps.some((row) => UUIDsEqual(row.ID, stepID)) ? stepID : undefined,
164
+ PromptRunID:
165
+ promptRunID && ownedSteps.some((row) => row.StepType === 'Prompt' && UUIDsEqual(row.TargetLogID, promptRunID))
166
+ ? promptRunID
167
+ : undefined,
168
+ PendingFeedbackRunID: isOwnedRun(candidates.PendingFeedbackRunID) ? candidates.PendingFeedbackRunID : undefined,
169
+ };
170
+ }
171
+ }
@@ -2,12 +2,14 @@
2
2
  * @fileoverview AI Agent Session lifecycle substrate (server-side, record + janitor layer).
3
3
  *
4
4
  * Exposes the session-record lifecycle manager, the orphan-reconciliation janitor, and the
5
- * per-process host identity helpers. The audio/media (WebRTC) transport and the long-lived
6
- * realtime agent run are deliberately **not** part of this layer — they arrive in P5.
5
+ * host-instance identity (`hostname:port:pid:bootId`) the janitor matches sessions by, which the
6
+ * realtime bridge engine is given for its bridge rows too (`BindBridgeEngineHostInstance`). The
7
+ * audio/media (WebRTC) transport and the long-lived realtime agent run are deliberately **not**
8
+ * part of this layer — they arrive in P5.
7
9
  *
8
10
  * @module @memberjunction/server
9
11
  */
10
- import { LoadWhiteboardChannelServer, LoadMeetingControlsChannelServer, LoadMediaChannelServer, LoadClientContextChannelServer } from '@memberjunction/ai-agents';
12
+ import { LoadWhiteboardChannelServer, LoadMeetingControlsChannelServer, LoadMediaChannelServer, LoadClientContextChannelServer, LoadClientOnlyChannelServer, LoadIdentityVerificationChannelServer, LoadInteractiveComponentChannelServer } from '@memberjunction/ai-agents';
11
13
  import { LoadRemoteBrowserChannel } from '@memberjunction/remote-browser-server';
12
14
  import { LoadSelfHostRemoteBrowser } from '@memberjunction/remote-browser-selfhost';
13
15
  import { BindRemoteBrowserGoalEngine } from './remoteBrowserGoalEngine.js';
@@ -19,6 +21,9 @@ LoadWhiteboardChannelServer();
19
21
  LoadMeetingControlsChannelServer();
20
22
  LoadMediaChannelServer();
21
23
  LoadClientContextChannelServer();
24
+ LoadClientOnlyChannelServer();
25
+ LoadIdentityVerificationChannelServer();
26
+ LoadInteractiveComponentChannelServer();
22
27
  // Remote Browser native channel (client-direct): the lifecycle-only server channel plugin + the
23
28
  // Self-Hosted Chrome backend driver (whose default runner launches a local headless Chromium via
24
29
  // Playwright — pulled in transitively through the SelfHost package, documented and acceptable).
@@ -30,5 +35,7 @@ BindRemoteBrowserGoalEngine();
30
35
 
31
36
  export * from './remoteBrowserGoalEngine.js';
32
37
  export * from './HostInstance.js';
38
+ export * from './BridgeHostInstance.js';
33
39
  export * from './SessionManager.js';
34
40
  export * from './SessionJanitor.js';
41
+ export * from './SessionRunIDVerifier.js';
@@ -0,0 +1,116 @@
1
+ import { AuthorizationInfo, AuthorizationRoleInfo, AuthorizationRoleType, IMetadataProvider, UserInfo } from '@memberjunction/core';
2
+ import { FilterByUUIDs, IndexByUUID, NormalizeUUID, UUIDMap, UUIDsEqual } from '@memberjunction/global';
3
+
4
+ /** The outcome of {@link EvaluateActionRunAuthorization}. */
5
+ export interface ActionRunAuthorization {
6
+ /** True when the user may run the action. */
7
+ Allowed: boolean;
8
+ /** Why the run is allowed or refused. */
9
+ Reason: string;
10
+ }
11
+
12
+ /** One provider's authorization tree and the role grants on it. */
13
+ export type AuthorizationMetadata = Pick<IMetadataProvider, 'Authorizations' | 'AuthorizationRoles'>;
14
+
15
+ /**
16
+ * True when the user is an Owner, MJ's superuser type. The column is nchar, so the stored value is
17
+ * padded.
18
+ */
19
+ export function IsOwner(user: UserInfo): boolean {
20
+ return user.Type?.trim().toLowerCase() === 'owner';
21
+ }
22
+
23
+ /**
24
+ * Decides whether a user may run an action through a public entry point such as the `RunAction`
25
+ * mutation. It fails closed:
26
+ *
27
+ * - An Owner may run any action.
28
+ * - An action with `MJ: Action Authorizations` rows runs for a user who holds at least one of the
29
+ * linked authorizations. A linked authorization counts only when it is active and none of the
30
+ * user's roles is denied it; the user may then hold it directly or through an ancestor.
31
+ * - An action with no such rows runs for any user when its class authorizes its caller itself
32
+ * (`BaseAction.AuthorizesCaller`), so the action's own check decides. Otherwise it runs for
33
+ * Owners only.
34
+ *
35
+ * The authorizations, their parents and their role grants are read only from `metadata`, never
36
+ * from the global provider. Pass the provider the `MJ: Action Authorizations` rows were read from.
37
+ *
38
+ * Refuse scope-limited sessions before calling this; see `IsScopeLimitedPrincipal`.
39
+ *
40
+ * @param user the caller
41
+ * @param actionAuthorizationIDs the AuthorizationID of each `MJ: Action Authorizations` row for the action
42
+ * @param metadata the provider whose authorizations and role grants decide
43
+ * @param actionAuthorizesCaller true when the action's class authorizes its caller itself
44
+ * @returns whether the run is allowed, and why
45
+ */
46
+ export function EvaluateActionRunAuthorization(
47
+ user: UserInfo,
48
+ actionAuthorizationIDs: readonly string[],
49
+ metadata: AuthorizationMetadata,
50
+ actionAuthorizesCaller: boolean
51
+ ): ActionRunAuthorization {
52
+ if (IsOwner(user)) {
53
+ return { Allowed: true, Reason: 'The user is an Owner.' };
54
+ }
55
+ if (actionAuthorizationIDs.length === 0) {
56
+ return actionAuthorizesCaller
57
+ ? { Allowed: true, Reason: 'The action authorizes its caller itself.' }
58
+ : { Allowed: false, Reason: 'No Action Authorization grants this action to any role, and the action does not authorize its caller itself; only an Owner may run it.' };
59
+ }
60
+ const authorizationsByID = IndexByUUID(metadata.Authorizations);
61
+ const userGrants = grantsToUserRoles(user, metadata.AuthorizationRoles);
62
+ const granted = actionAuthorizationIDs
63
+ .map((id) => authorizationsByID.Get(id))
64
+ .filter((auth): auth is AuthorizationInfo => auth !== undefined)
65
+ .find((auth) => holdsLinkedAuthorization(auth, authorizationsByID, userGrants));
66
+ return granted
67
+ ? { Allowed: true, Reason: `The user holds the "${granted.Name}" authorization.` }
68
+ : { Allowed: false, Reason: 'None of your roles holds an authorization this action requires.' };
69
+ }
70
+
71
+ /** The role grants that name one of the user's roles. */
72
+ function grantsToUserRoles(user: UserInfo, grants: readonly AuthorizationRoleInfo[]): AuthorizationRoleInfo[] {
73
+ const userRoleIDs = (user.UserRoles ?? []).map((userRole) => userRole.RoleID);
74
+ return FilterByUUIDs(grants, userRoleIDs, (grant) => grant.RoleID);
75
+ }
76
+
77
+ /**
78
+ * True when the user holds a linked authorization: it is active, none of the user's roles is denied
79
+ * it, and the user holds it directly or through an ancestor. The first two are checked on the linked
80
+ * authorization itself, because the ancestor walk would otherwise grant it through the parent.
81
+ */
82
+ function holdsLinkedAuthorization(auth: AuthorizationInfo, authorizationsByID: UUIDMap<AuthorizationInfo>, userGrants: readonly AuthorizationRoleInfo[]): boolean {
83
+ if (!auth.IsActive || grantTypes(auth, userGrants).includes(AuthorizationRoleType.Deny)) {
84
+ return false;
85
+ }
86
+ return holdsDirectlyOrThroughAncestor(auth, authorizationsByID, userGrants);
87
+ }
88
+
89
+ /**
90
+ * True when the user holds the authorization or one of its ancestors, following `ParentID` within
91
+ * `authorizationsByID`. This is the walk `AuthorizationEvaluator.UserCanExecuteWithAncestors` does,
92
+ * with the role grants taken from the same provider as the authorizations.
93
+ */
94
+ function holdsDirectlyOrThroughAncestor(auth: AuthorizationInfo, authorizationsByID: UUIDMap<AuthorizationInfo>, userGrants: readonly AuthorizationRoleInfo[]): boolean {
95
+ const visited = new Set<string>();
96
+ let node: AuthorizationInfo | undefined = auth;
97
+ while (node && !visited.has(NormalizeUUID(node.ID))) {
98
+ if (holdsDirectly(node, userGrants)) {
99
+ return true;
100
+ }
101
+ visited.add(NormalizeUUID(node.ID));
102
+ node = node.ParentID ? authorizationsByID.Get(node.ParentID) : undefined;
103
+ }
104
+ return false;
105
+ }
106
+
107
+ /** True when the authorization is active and the user's roles have an Allow and no Deny on it. */
108
+ function holdsDirectly(auth: AuthorizationInfo, userGrants: readonly AuthorizationRoleInfo[]): boolean {
109
+ const types = grantTypes(auth, userGrants);
110
+ return auth.IsActive && types.includes(AuthorizationRoleType.Allow) && !types.includes(AuthorizationRoleType.Deny);
111
+ }
112
+
113
+ /** Allow or Deny, for each of the user's role grants on the authorization. */
114
+ function grantTypes(auth: AuthorizationInfo, userGrants: readonly AuthorizationRoleInfo[]): AuthorizationRoleType[] {
115
+ return userGrants.filter((grant) => UUIDsEqual(grant.AuthorizationID, auth.ID)).map((grant) => grant.AuthorizationType());
116
+ }
package/src/auth/index.ts CHANGED
@@ -253,14 +253,9 @@ export const VerifyUserRecord = async (
253
253
  ): Promise<UserInfo | undefined> => {
254
254
  if (!email) return undefined;
255
255
 
256
- let user = UserCache.Instance.Users.find((u) => {
257
- if (!u.Email || u.Email.trim() === '') {
258
- // this condition should never occur. If it doesn throw a console error including the user id
259
- // DB requires non-null but this is just an extra check and we could in theory have a blank string in the DB
260
- console.error(`SYSTEM METADATA ISSUE: User ${u.ID} has no email address`);
261
- return false;
262
- } else return u.Email.toLowerCase().trim() === email.toLowerCase().trim();
263
- });
256
+ // FindUser, not Users.find: a user this process has not loaded yet (created seconds ago on
257
+ // another server) is looked up in the database instead of being treated as nonexistent.
258
+ let user = await UserCache.Instance.FindUser({ Email: email });
264
259
 
265
260
  if (!user) {
266
261
  // NOTE: `requestDomain` (parsed from the spoofable `Origin` header) is deliberately NOT part of
@@ -34,6 +34,7 @@ import { ResolveConfiguredPrincipal } from '../principals.js';
34
34
  import { CommunicationEngine } from '@memberjunction/communication-engine';
35
35
  import { Message } from '@memberjunction/communication-types';
36
36
  import { configInfo, type MagicLinkConfig } from '../../config.js';
37
+ import { CreateIsolatedProvider } from '../../isolatedProvider.js';
37
38
  import { MagicLinkKeyManager } from './MagicLinkKeys.js';
38
39
  import { GenerateRawToken, GenerateSessionId, HashToken, EvaluateInvite, BuildSessionClaims, BuildConsumeInvitePostgresSQL, CanIssueInvites, IsRoleGrantable, MAGIC_LINK_TOKEN_PREFIX } from './magicLinkCore.js';
39
40
  import type {
@@ -521,9 +522,11 @@ export class MagicLinkService {
521
522
  /** Creates a new user with exactly the restricted role + single app, transactionally. */
522
523
  private async createScopedUser(invite: MJMagicLinkInviteEntity, role: RoleInfo, contextUser: UserInfo): Promise<ProvisionResult> {
523
524
  // Single provider for BOTH the transaction and the entity writes, so the
524
- // BeginTransaction/Save/Commit are provably on the same connection.
525
- // global-provider-ok: redemption runs in the pre-auth flow, no per-request provider yet.
526
- const provider = Metadata.Provider as DatabaseProviderBase; // global-provider-ok: server-side magic-link service; runs under the server's single default provider
525
+ // BeginTransaction/Save/Commit are provably on the same connection. Redemption runs
526
+ // pre-auth, with no per-request provider yet, and the global Metadata.Provider is shared
527
+ // by every concurrent request: a transaction opened there nests into anyone else's and
528
+ // swallows their writes. So it gets its own (see CreateIsolatedProvider).
529
+ const provider = await CreateIsolatedProvider();
527
530
  const email = invite.Email;
528
531
  // The invite does not persist a name; provision a sensible default. (The
529
532
  // create-invite params accept first/last name but there is no column to