@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
@@ -11,11 +11,13 @@ vi.mock('@memberjunction/ai-engine-base', () => ({
11
11
  // --- Mock the client-direct service so CloseSession's run finalization is observable (no DB),
12
12
  // plus the channel-plugin host so the start/close lifecycle notifications are observable. ---
13
13
  const finalizeCoAgentRunMock = vi.fn(async () => undefined);
14
+ const keepCoAgentRunAliveMock = vi.fn();
14
15
  const hostSessionStartedMock = vi.fn(async () => undefined);
15
16
  const hostSessionClosedMock = vi.fn(async () => undefined);
16
17
  vi.mock('@memberjunction/ai-agents', () => ({
17
18
  RealtimeClientSessionService: class {
18
19
  FinalizeCoAgentRun = finalizeCoAgentRunMock;
20
+ KeepCoAgentRunAlive = keepCoAgentRunAliveMock;
19
21
  },
20
22
  RealtimeChannelServerHost: {
21
23
  get Instance() {
@@ -27,15 +29,18 @@ vi.mock('@memberjunction/ai-agents', () => ({
27
29
  },
28
30
  }));
29
31
 
30
- // --- Mock RunView.FromMetadataProvider so channel sweeps are controllable, while leaving the
31
- // rest of @memberjunction/core intact. ---
32
+ // --- Mock RunView.FromMetadataProvider so channel sweeps and run-id lookups are controllable,
33
+ // while leaving the rest of @memberjunction/core intact. RunViews runs each view through runViewMock. ---
32
34
  const runViewMock = vi.fn();
33
35
  vi.mock('@memberjunction/core', async (importOriginal) => {
34
36
  const actual = await importOriginal<typeof import('@memberjunction/core')>();
35
37
  return {
36
38
  ...actual,
37
39
  RunView: {
38
- FromMetadataProvider: () => ({ RunView: runViewMock }),
40
+ FromMetadataProvider: () => ({
41
+ RunView: runViewMock,
42
+ RunViews: (paramsList: unknown[]) => Promise.all(paramsList.map((params) => runViewMock(params))),
43
+ }),
39
44
  },
40
45
  };
41
46
  });
@@ -100,6 +105,27 @@ function makeSessionEntity(overrides: Partial<FakeEntity> = {}): FakeEntity {
100
105
  };
101
106
  }
102
107
 
108
+ /** Co-agent observability ids as a voice session's Config holds them. */
109
+ const CO_RUN_ID = 'a5a5a5a5-0000-4000-8000-0000000000c0';
110
+ const PROMPT_RUN_ID = 'a5a5a5a5-0000-4000-8000-0000000000b0';
111
+ const RUN_STEP_ID = 'a5a5a5a5-0000-4000-8000-0000000000a0';
112
+
113
+ /**
114
+ * Makes the given runs records of the session for the run-id lookups: `runIDs` carry the session's
115
+ * id, and `promptRunID` (when given) is the TargetLogID of the co-agent run's Prompt step.
116
+ */
117
+ function mockSessionRunRecords(runIDs: string[], promptRunID?: string): void {
118
+ runViewMock.mockImplementation(async (params: { EntityName?: string }) => {
119
+ if (params.EntityName === 'MJ: AI Agent Runs') {
120
+ return { Success: true, Results: runIDs.map((ID) => ({ ID })) };
121
+ }
122
+ if (params.EntityName === 'MJ: AI Agent Run Steps') {
123
+ return { Success: true, Results: promptRunID ? [{ ID: RUN_STEP_ID, StepType: 'Prompt', TargetLogID: promptRunID }] : [] };
124
+ }
125
+ return { Success: true, Results: [] };
126
+ });
127
+ }
128
+
103
129
  beforeEach(() => {
104
130
  hasPermissionMock.mockReset();
105
131
  finalizeCoAgentRunMock.mockClear();
@@ -228,8 +254,9 @@ describe('SessionManager.CloseSession', () => {
228
254
  const session = makeSessionEntity({
229
255
  ID: 'session-voice',
230
256
  Status: 'Active',
231
- Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: 'co-run-5', promptRunID: 'prompt-run-5' }),
257
+ Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: CO_RUN_ID, promptRunID: PROMPT_RUN_ID }),
232
258
  });
259
+ mockSessionRunRecords([CO_RUN_ID], PROMPT_RUN_ID);
233
260
  const { provider } = makeProvider(() => session);
234
261
  const mgr = new SessionManager();
235
262
  const user = makeUser();
@@ -238,7 +265,7 @@ describe('SessionManager.CloseSession', () => {
238
265
 
239
266
  expect(ok).toBe(true);
240
267
  expect(finalizeCoAgentRunMock).toHaveBeenCalledTimes(1);
241
- expect(finalizeCoAgentRunMock).toHaveBeenCalledWith('co-run-5', 'prompt-run-5', user, provider, true, null);
268
+ expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(CO_RUN_ID, PROMPT_RUN_ID, user, provider, true, null);
242
269
  });
243
270
 
244
271
  it('threads the co-agent run-step id into finalize when the session config carries it', async () => {
@@ -247,11 +274,12 @@ describe('SessionManager.CloseSession', () => {
247
274
  Status: 'Active',
248
275
  Config_: JSON.stringify({
249
276
  targetAgentID: 't1',
250
- coAgentRunID: 'co-run-6',
251
- promptRunID: 'prompt-run-6',
252
- coAgentRunStepID: 'run-step-6',
277
+ coAgentRunID: CO_RUN_ID,
278
+ promptRunID: PROMPT_RUN_ID,
279
+ coAgentRunStepID: RUN_STEP_ID,
253
280
  }),
254
281
  });
282
+ mockSessionRunRecords([CO_RUN_ID], PROMPT_RUN_ID);
255
283
  const { provider } = makeProvider(() => session);
256
284
  const mgr = new SessionManager();
257
285
  const user = makeUser();
@@ -259,7 +287,7 @@ describe('SessionManager.CloseSession', () => {
259
287
  const ok = await mgr.CloseSession('session-voice-step', user, provider);
260
288
 
261
289
  expect(ok).toBe(true);
262
- expect(finalizeCoAgentRunMock).toHaveBeenCalledWith('co-run-6', 'prompt-run-6', user, provider, true, 'run-step-6');
290
+ expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(CO_RUN_ID, PROMPT_RUN_ID, user, provider, true, RUN_STEP_ID);
263
291
  });
264
292
 
265
293
  it('finalizes as the SYSTEM user when a scoped anonymous owner closes their session (issue #3371)', async () => {
@@ -274,8 +302,9 @@ describe('SessionManager.CloseSession', () => {
274
302
  const session = makeSessionEntity({
275
303
  ID: 'session-anon',
276
304
  Status: 'Active',
277
- Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: 'co-run-9', promptRunID: 'prompt-run-9' }),
305
+ Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: CO_RUN_ID, promptRunID: PROMPT_RUN_ID }),
278
306
  });
307
+ mockSessionRunRecords([CO_RUN_ID], PROMPT_RUN_ID);
279
308
  const { provider } = makeProvider(() => session);
280
309
  const mgr = new SessionManager();
281
310
 
@@ -284,7 +313,7 @@ describe('SessionManager.CloseSession', () => {
284
313
  expect(ok).toBe(true);
285
314
  // The session-close writes themselves stay on the caller — only finalize elevates.
286
315
  expect(session.Status).toBe('Closed');
287
- expect(finalizeCoAgentRunMock).toHaveBeenCalledWith('co-run-9', 'prompt-run-9', systemUser, provider, true, null);
316
+ expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(CO_RUN_ID, PROMPT_RUN_ID, systemUser, provider, true, null);
288
317
  });
289
318
 
290
319
  it('FAILS CLOSED — finalizes as the anonymous caller when no system user is available', async () => {
@@ -297,14 +326,15 @@ describe('SessionManager.CloseSession', () => {
297
326
  const session = makeSessionEntity({
298
327
  ID: 'session-anon-2',
299
328
  Status: 'Active',
300
- Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: 'co-run-10', promptRunID: null }),
329
+ Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: CO_RUN_ID, promptRunID: null }),
301
330
  });
331
+ mockSessionRunRecords([CO_RUN_ID]);
302
332
  const { provider } = makeProvider(() => session);
303
333
  const mgr = new SessionManager();
304
334
 
305
335
  await mgr.CloseSession('session-anon-2', anonUser, provider);
306
336
 
307
- expect(finalizeCoAgentRunMock).toHaveBeenCalledWith('co-run-10', null, anonUser, provider, true, null);
337
+ expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(CO_RUN_ID, null, anonUser, provider, true, null);
308
338
  });
309
339
 
310
340
  it('does not finalize when the session config has no run ids (target only)', async () => {
@@ -334,8 +364,9 @@ describe('SessionManager.CloseSession', () => {
334
364
  const session = makeSessionEntity({
335
365
  ID: 'session-injected',
336
366
  Status: 'Active',
337
- Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: 'co-run-injected', promptRunID: 'prompt-run-injected' }),
367
+ Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: CO_RUN_ID, promptRunID: PROMPT_RUN_ID }),
338
368
  });
369
+ mockSessionRunRecords([CO_RUN_ID], PROMPT_RUN_ID);
339
370
  const { provider } = makeProvider(() => session);
340
371
  const mgr = new SessionManager(injectedService);
341
372
  const user = makeUser();
@@ -345,7 +376,7 @@ describe('SessionManager.CloseSession', () => {
345
376
  expect(ok).toBe(true);
346
377
  // The finalize call landed on the INJECTED instance, never the module-default one.
347
378
  expect(injectedFinalizeMock).toHaveBeenCalledTimes(1);
348
- expect(injectedFinalizeMock).toHaveBeenCalledWith('co-run-injected', 'prompt-run-injected', user, provider, true, null);
379
+ expect(injectedFinalizeMock).toHaveBeenCalledWith(CO_RUN_ID, PROMPT_RUN_ID, user, provider, true, null);
349
380
  expect(finalizeCoAgentRunMock).not.toHaveBeenCalled();
350
381
  });
351
382
 
@@ -353,8 +384,9 @@ describe('SessionManager.CloseSession', () => {
353
384
  const session = makeSessionEntity({
354
385
  ID: 'session-default-svc',
355
386
  Status: 'Active',
356
- Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: 'co-run-default', promptRunID: 'prompt-run-default' }),
387
+ Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: CO_RUN_ID, promptRunID: PROMPT_RUN_ID }),
357
388
  });
389
+ mockSessionRunRecords([CO_RUN_ID], PROMPT_RUN_ID);
358
390
  const { provider } = makeProvider(() => session);
359
391
  const mgr = new SessionManager(); // no injection — e.g. SessionJanitor, telephony services
360
392
  const user = makeUser();
@@ -362,7 +394,7 @@ describe('SessionManager.CloseSession', () => {
362
394
  const ok = await mgr.CloseSession('session-default-svc', user, provider);
363
395
 
364
396
  expect(ok).toBe(true);
365
- expect(finalizeCoAgentRunMock).toHaveBeenCalledWith('co-run-default', 'prompt-run-default', user, provider, true, null);
397
+ expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(CO_RUN_ID, PROMPT_RUN_ID, user, provider, true, null);
366
398
  });
367
399
 
368
400
  it('stamps CloseReason = Explicit by default (untouched call sites get the right reason)', async () => {
@@ -425,6 +457,79 @@ describe('SessionManager.CloseSession', () => {
425
457
  });
426
458
  });
427
459
 
460
+ describe('SessionManager.CloseSessionForEndedBridge (a bridged agent left its room)', () => {
461
+ it.each([
462
+ ['Explicit', 'Explicit'],
463
+ ['HostEnded', 'Explicit'],
464
+ ['Error', 'Error'],
465
+ ['Janitor', 'Janitor'],
466
+ ['Shutdown', 'Shutdown'],
467
+ ] as const)('closes the session when its bridge ended with %s, stamping %s', async (bridgeReason, sessionReason) => {
468
+ const session = makeSessionEntity({ ID: 'session-bridged', Status: 'Active' });
469
+ const { provider } = makeProvider(() => session);
470
+ const mgr = new SessionManager();
471
+
472
+ const ok = await mgr.CloseSessionForEndedBridge('session-bridged', bridgeReason, makeUser(), provider);
473
+
474
+ expect(ok).toBe(true);
475
+ expect(session.Status).toBe('Closed');
476
+ expect(session.ClosedAt).toBeInstanceOf(Date);
477
+ expect(session.CloseReason).toBe(sessionReason);
478
+ // The same close the janitor runs: the channel plugins hear about it too.
479
+ expect(hostSessionClosedMock).toHaveBeenCalledWith('session-bridged', sessionReason);
480
+ });
481
+
482
+ it('keeps the reason of a session something else closed first (the janitor, the user)', async () => {
483
+ const session = makeSessionEntity({ ID: 'session-bridged', Status: 'Closed', CloseReason: 'Janitor' });
484
+ const { provider } = makeProvider(() => session);
485
+ const mgr = new SessionManager();
486
+
487
+ const ok = await mgr.CloseSessionForEndedBridge('session-bridged', 'Explicit', makeUser(), provider);
488
+
489
+ expect(ok).toBe(true);
490
+ expect(session.CloseReason).toBe('Janitor');
491
+ expect(session.Save).not.toHaveBeenCalled();
492
+ });
493
+
494
+ it('never throws: a close that fails is reported as not closed, leaving the session to the janitor', async () => {
495
+ const provider = {
496
+ GetEntityObject: vi.fn(async () => {
497
+ throw new Error('database unreachable');
498
+ }),
499
+ } as unknown as IMetadataProvider;
500
+ const mgr = new SessionManager();
501
+
502
+ await expect(mgr.CloseSessionForEndedBridge('session-bridged', 'Explicit', makeUser(), provider)).resolves.toBe(false);
503
+ });
504
+ });
505
+
506
+ describe('SessionManager.CloseSessionForFailedStart (an agent that never joined its room)', () => {
507
+ it('closes the session as Error, through the close the janitor runs', async () => {
508
+ const session = makeSessionEntity({ ID: 'session-unstarted', Status: 'Active' });
509
+ const { provider } = makeProvider(() => session);
510
+ const mgr = new SessionManager();
511
+
512
+ const ok = await mgr.CloseSessionForFailedStart('session-unstarted', makeUser(), provider);
513
+
514
+ expect(ok).toBe(true);
515
+ expect(session.Status).toBe('Closed');
516
+ expect(session.ClosedAt).toBeInstanceOf(Date);
517
+ expect(session.CloseReason).toBe('Error');
518
+ expect(hostSessionClosedMock).toHaveBeenCalledWith('session-unstarted', 'Error');
519
+ });
520
+
521
+ it('never throws: a close that fails is reported as not closed, leaving the session to the janitor', async () => {
522
+ const provider = {
523
+ GetEntityObject: vi.fn(async () => {
524
+ throw new Error('database unreachable');
525
+ }),
526
+ } as unknown as IMetadataProvider;
527
+ const mgr = new SessionManager();
528
+
529
+ await expect(mgr.CloseSessionForFailedStart('session-unstarted', makeUser(), provider)).resolves.toBe(false);
530
+ });
531
+ });
532
+
428
533
  describe('SessionManager.Heartbeat', () => {
429
534
  it('coalesces writes within the min interval (one write, not two)', async () => {
430
535
  const session = makeSessionEntity({ ID: 'session-1', Status: 'Active' });
@@ -461,6 +566,36 @@ describe('SessionManager.Heartbeat', () => {
461
566
  });
462
567
  });
463
568
 
569
+ describe('SessionManager.Heartbeat — the session\'s co-agent run stays alive', () => {
570
+ beforeEach(() => {
571
+ keepCoAgentRunAliveMock.mockClear();
572
+ });
573
+
574
+ it('keeps the co-agent run alive for the run watchdog on each persisted heartbeat', async () => {
575
+ const session = makeSessionEntity({ ID: 'session-1', Status: 'Active', Config_: JSON.stringify({ coAgentRunID: CO_RUN_ID, promptRunID: PROMPT_RUN_ID }) });
576
+ mockSessionRunRecords([CO_RUN_ID], PROMPT_RUN_ID);
577
+ const { provider } = makeProvider(() => session);
578
+ const mgr = new SessionManager();
579
+
580
+ await mgr.Heartbeat('session-1', makeUser(), provider);
581
+ await mgr.Heartbeat('session-1', makeUser(), provider); // coalesced: no write, no re-registration
582
+
583
+ expect(keepCoAgentRunAliveMock).toHaveBeenCalledOnce();
584
+ expect(keepCoAgentRunAliveMock.mock.calls[0][0]).toBe(CO_RUN_ID);
585
+ expect(keepCoAgentRunAliveMock.mock.calls[0][1]).toBe(provider);
586
+ });
587
+
588
+ it('does nothing for a closed session, and passes no run for a session without one', async () => {
589
+ const closed = makeSessionEntity({ ID: 'session-closed', Status: 'Closed', Config_: JSON.stringify({ coAgentRunID: 'co-run-1' }) });
590
+ await new SessionManager().Heartbeat('session-closed', makeUser(), makeProvider(() => closed).provider);
591
+ expect(keepCoAgentRunAliveMock).not.toHaveBeenCalled();
592
+
593
+ const workspace = makeSessionEntity({ ID: 'session-2', Status: 'Active', Config_: null });
594
+ await new SessionManager().Heartbeat('session-2', makeUser(), makeProvider(() => workspace).provider);
595
+ expect(keepCoAgentRunAliveMock.mock.calls[0][0]).toBeUndefined();
596
+ });
597
+ });
598
+
464
599
  describe('SessionManager.Heartbeat — bounded heartbeatLastWrite (Round 11 memory-leak fix)', () => {
465
600
  // Regression coverage: heartbeatLastWrite used to be a plain, unbounded `Map`. SessionManager
466
601
  // is constructed fresh by every resolver that needs one (AgentSessionResolver,
@@ -626,3 +761,64 @@ describe('SessionManager — server-side channel plugin lifecycle notifications'
626
761
  expect(session.Status).toBe('Closed');
627
762
  });
628
763
  });
764
+
765
+ // A session's Config is writable by its owner (generic CRUD on MJ: AI Agent Sessions), so the run
766
+ // ids in it are acted on only when they are UUIDs of records that belong to the session.
767
+ describe('SessionManager — run ids in the owner-writable session Config', () => {
768
+ const SESSION_ID = 'c3c3c3c3-1000-4000-8000-000000000001';
769
+ const FOREIGN_RUN_ID = 'c3c3c3c3-1000-4000-8000-0000000000f1';
770
+ const FOREIGN_PROMPT_RUN_ID = 'c3c3c3c3-1000-4000-8000-0000000000f2';
771
+ const FOREIGN_STEP_ID = 'c3c3c3c3-1000-4000-8000-0000000000f3';
772
+ /** Harmless text that is not a UUID and would change the meaning of a quoted filter. */
773
+ const NOT_A_UUID = "x' OR '1'='1";
774
+
775
+ function issuedFilters(): string[] {
776
+ return runViewMock.mock.calls.map(([params]) => (params as { ExtraFilter?: string }).ExtraFilter ?? '');
777
+ }
778
+
779
+ beforeEach(() => {
780
+ keepCoAgentRunAliveMock.mockClear();
781
+ });
782
+
783
+ it('CloseSession does not finalize run ids that are not records of the session', async () => {
784
+ const session = makeSessionEntity({
785
+ ID: SESSION_ID,
786
+ Status: 'Active',
787
+ Config_: JSON.stringify({
788
+ targetAgentID: 't1',
789
+ coAgentRunID: FOREIGN_RUN_ID,
790
+ promptRunID: FOREIGN_PROMPT_RUN_ID,
791
+ coAgentRunStepID: FOREIGN_STEP_ID,
792
+ }),
793
+ });
794
+ const { provider } = makeProvider(() => session);
795
+
796
+ await new SessionManager().CloseSession(SESSION_ID, makeUser(), provider);
797
+
798
+ expect(finalizeCoAgentRunMock).not.toHaveBeenCalled();
799
+ });
800
+
801
+ it('CloseSession does not finalize run ids that are not UUIDs, and never puts them in a filter', async () => {
802
+ const session = makeSessionEntity({
803
+ ID: SESSION_ID,
804
+ Status: 'Active',
805
+ Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: NOT_A_UUID, promptRunID: NOT_A_UUID }),
806
+ });
807
+ const { provider } = makeProvider(() => session);
808
+
809
+ await new SessionManager().CloseSession(SESSION_ID, makeUser(), provider);
810
+
811
+ expect(finalizeCoAgentRunMock).not.toHaveBeenCalled();
812
+ expect(issuedFilters().filter((f) => f.includes(NOT_A_UUID))).toEqual([]);
813
+ });
814
+
815
+ it('Heartbeat does not keep alive a run that is not a record of the session', async () => {
816
+ const session = makeSessionEntity({ ID: SESSION_ID, Status: 'Active', Config_: JSON.stringify({ coAgentRunID: FOREIGN_RUN_ID }) });
817
+ const { provider } = makeProvider(() => session);
818
+
819
+ await new SessionManager().Heartbeat(SESSION_ID, makeUser(), provider);
820
+
821
+ expect(keepCoAgentRunAliveMock).toHaveBeenCalledOnce();
822
+ expect(keepCoAgentRunAliveMock.mock.calls[0][0]).toBeUndefined();
823
+ });
824
+ });
@@ -0,0 +1,251 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import type { IMetadataProvider, RunViewParams, UserInfo } from '@memberjunction/core';
3
+ import { UserCache } from '@memberjunction/generic-database-provider';
4
+ import { SessionRunIDVerifier } from '../agentSessions/SessionRunIDVerifier.js';
5
+
6
+ const SESSION_ID = 'd3d3d3d3-0000-4000-8000-000000000001';
7
+ const CO_RUN_ID = 'd3d3d3d3-0000-4000-8000-000000000002';
8
+ const PROMPT_RUN_ID = 'd3d3d3d3-0000-4000-8000-000000000003';
9
+ const STEP_ID = 'd3d3d3d3-0000-4000-8000-000000000004';
10
+ const PAUSED_RUN_ID = 'd3d3d3d3-0000-4000-8000-000000000005';
11
+ const USER = { ID: 'd3d3d3d3-0000-4000-8000-0000000000aa' } as unknown as UserInfo;
12
+
13
+ /** Rows each lookup returns, keyed by entity name. */
14
+ type Rows = Record<string, Array<Record<string, unknown>>>;
15
+
16
+ function makeProvider(rows: Rows, success = true): { provider: IMetadataProvider; runViews: ReturnType<typeof vi.fn> } {
17
+ const runViews = vi.fn(async (paramsList: RunViewParams[]) =>
18
+ paramsList.map((params) => ({
19
+ Success: success,
20
+ Results: success ? rows[params.EntityName ?? ''] ?? [] : [],
21
+ ErrorMessage: success ? '' : 'lookup failed',
22
+ })),
23
+ );
24
+ return { provider: { RunViews: runViews } as unknown as IMetadataProvider, runViews };
25
+ }
26
+
27
+ function issuedViews(runViews: ReturnType<typeof vi.fn>): RunViewParams[] {
28
+ return runViews.mock.calls.flatMap(([paramsList]) => paramsList as RunViewParams[]);
29
+ }
30
+
31
+ beforeEach(() => {
32
+ vi.spyOn(console, 'error').mockImplementation(() => undefined);
33
+ });
34
+
35
+ afterEach(() => {
36
+ UserCache.Instance.SetUsers([]);
37
+ });
38
+
39
+ describe('SessionRunIDVerifier.Verify', () => {
40
+ it('keeps nothing, and runs no lookup, when no claim is a UUID', async () => {
41
+ const { provider, runViews } = makeProvider({});
42
+
43
+ const result = await new SessionRunIDVerifier().Verify(
44
+ SESSION_ID,
45
+ { CoAgentRunID: "x' OR '1'='1", PromptRunID: 'prompt-run-1', PendingFeedbackRunID: '' },
46
+ USER,
47
+ provider,
48
+ );
49
+
50
+ expect(result).toEqual({});
51
+ expect(runViews).not.toHaveBeenCalled();
52
+ });
53
+
54
+ it('looks up the claimed runs within the session and keeps only the ones it finds', async () => {
55
+ const { provider, runViews } = makeProvider({ 'MJ: AI Agent Runs': [{ ID: CO_RUN_ID }] });
56
+
57
+ const result = await new SessionRunIDVerifier().Verify(
58
+ SESSION_ID,
59
+ { CoAgentRunID: CO_RUN_ID, PendingFeedbackRunID: PAUSED_RUN_ID },
60
+ USER,
61
+ provider,
62
+ );
63
+
64
+ expect(result.CoAgentRunID).toBe(CO_RUN_ID);
65
+ expect(result.PendingFeedbackRunID).toBeUndefined();
66
+ const [runsView] = issuedViews(runViews);
67
+ expect(runsView.EntityName).toBe('MJ: AI Agent Runs');
68
+ expect(runsView.ExtraFilter).toBe(`AgentSessionID='${SESSION_ID}' AND ID IN ('${CO_RUN_ID}', '${PAUSED_RUN_ID}')`);
69
+ // No system user is cached here, so the lookup runs as the acting user.
70
+ expect(runViews.mock.calls[0][1]).toBe(USER);
71
+ });
72
+
73
+ it('runs the lookup as the system user when one is available', async () => {
74
+ // Row-level security can hide a session's own runs from its owner: a web-widget guest's
75
+ // delegated runs carry no ConversationID, so the guest's run filter does not match them.
76
+ const systemUser = { ID: UserCache.Instance.SYSTEM_USER_ID, Email: 'system@example.com' } as unknown as UserInfo;
77
+ UserCache.Instance.SetUsers([systemUser]);
78
+ const { provider, runViews } = makeProvider({ 'MJ: AI Agent Runs': [{ ID: PAUSED_RUN_ID }] });
79
+
80
+ const result = await new SessionRunIDVerifier().Verify(SESSION_ID, { PendingFeedbackRunID: PAUSED_RUN_ID }, USER, provider);
81
+
82
+ expect(result.PendingFeedbackRunID).toBe(PAUSED_RUN_ID);
83
+ expect(runViews.mock.calls[0][1]).toBe(systemUser);
84
+ });
85
+
86
+ it('keeps the prompt run and step that belong to the session\'s co-agent run', async () => {
87
+ const { provider, runViews } = makeProvider({
88
+ 'MJ: AI Agent Runs': [{ ID: CO_RUN_ID }],
89
+ 'MJ: AI Agent Run Steps': [{ ID: STEP_ID, StepType: 'Prompt', TargetLogID: PROMPT_RUN_ID }],
90
+ });
91
+
92
+ const result = await new SessionRunIDVerifier().Verify(
93
+ SESSION_ID,
94
+ { CoAgentRunID: CO_RUN_ID, PromptRunID: PROMPT_RUN_ID, CoAgentRunStepID: STEP_ID },
95
+ USER,
96
+ provider,
97
+ );
98
+
99
+ expect(result).toEqual({ CoAgentRunID: CO_RUN_ID, PromptRunID: PROMPT_RUN_ID, CoAgentRunStepID: STEP_ID, PendingFeedbackRunID: undefined });
100
+ const stepsView = issuedViews(runViews).find((view) => view.EntityName === 'MJ: AI Agent Run Steps');
101
+ expect(stepsView?.ExtraFilter).toBe(`AgentRunID='${CO_RUN_ID}' AND (ID='${STEP_ID}' OR TargetLogID='${PROMPT_RUN_ID}')`);
102
+ });
103
+
104
+ it('drops a prompt run that is the target of a step other than a Prompt step', async () => {
105
+ const { provider } = makeProvider({
106
+ 'MJ: AI Agent Runs': [{ ID: CO_RUN_ID }],
107
+ 'MJ: AI Agent Run Steps': [{ ID: STEP_ID, StepType: 'Tool', TargetLogID: PROMPT_RUN_ID }],
108
+ });
109
+
110
+ const result = await new SessionRunIDVerifier().Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID, PromptRunID: PROMPT_RUN_ID }, USER, provider);
111
+
112
+ expect(result.PromptRunID).toBeUndefined();
113
+ });
114
+
115
+ it('drops the prompt run and step when the co-agent run is not the session\'s', async () => {
116
+ const { provider } = makeProvider({
117
+ 'MJ: AI Agent Runs': [],
118
+ 'MJ: AI Agent Run Steps': [{ ID: STEP_ID, StepType: 'Prompt', TargetLogID: PROMPT_RUN_ID }],
119
+ });
120
+
121
+ const result = await new SessionRunIDVerifier().Verify(
122
+ SESSION_ID,
123
+ { CoAgentRunID: CO_RUN_ID, PromptRunID: PROMPT_RUN_ID, CoAgentRunStepID: STEP_ID },
124
+ USER,
125
+ provider,
126
+ );
127
+
128
+ expect(result).toEqual({ CoAgentRunID: undefined, PromptRunID: undefined, CoAgentRunStepID: undefined, PendingFeedbackRunID: undefined });
129
+ });
130
+
131
+ it('matches ids case-insensitively', async () => {
132
+ const { provider } = makeProvider({ 'MJ: AI Agent Runs': [{ ID: PAUSED_RUN_ID.toUpperCase() }] });
133
+
134
+ const result = await new SessionRunIDVerifier().Verify(SESSION_ID, { PendingFeedbackRunID: PAUSED_RUN_ID }, USER, provider);
135
+
136
+ expect(result.PendingFeedbackRunID).toBe(PAUSED_RUN_ID);
137
+ });
138
+
139
+ it('looks up the same claims again on every call', async () => {
140
+ const { provider, runViews } = makeProvider({ 'MJ: AI Agent Runs': [{ ID: CO_RUN_ID }] });
141
+ const verifier = new SessionRunIDVerifier();
142
+
143
+ await verifier.Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, USER, provider);
144
+ const second = await verifier.Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, USER, provider);
145
+
146
+ expect(second.CoAgentRunID).toBe(CO_RUN_ID);
147
+ expect(runViews).toHaveBeenCalledTimes(2);
148
+ });
149
+
150
+ it('reads the runs and steps from the database, not from a cached view', async () => {
151
+ const { provider, runViews } = makeProvider({ 'MJ: AI Agent Runs': [{ ID: CO_RUN_ID }] });
152
+
153
+ await new SessionRunIDVerifier().Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID, PromptRunID: PROMPT_RUN_ID }, USER, provider);
154
+
155
+ const views = issuedViews(runViews);
156
+ expect(views.map((view) => view.EntityName)).toEqual(['MJ: AI Agent Runs', 'MJ: AI Agent Run Steps']);
157
+ expect(views.every((view) => view.BypassCache === true)).toBe(true);
158
+ });
159
+
160
+ it('keeps nothing when a lookup fails, and looks up again next time', async () => {
161
+ const { provider, runViews } = makeProvider({}, false);
162
+ const verifier = new SessionRunIDVerifier();
163
+
164
+ const first = await verifier.Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, USER, provider);
165
+ await verifier.Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, USER, provider);
166
+
167
+ expect(first).toEqual({});
168
+ expect(runViews).toHaveBeenCalledTimes(2);
169
+ });
170
+
171
+ it('keeps nothing, without throwing, when the provider throws', async () => {
172
+ const provider = { RunViews: vi.fn(async () => { throw new Error('connection lost'); }) } as unknown as IMetadataProvider;
173
+
174
+ await expect(new SessionRunIDVerifier().Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, USER, provider)).resolves.toEqual({});
175
+ });
176
+ });
177
+
178
+ /**
179
+ * A run's `AgentSessionID` and a step's `AgentRunID` and `TargetLogID` can change after a lookup (the
180
+ * generated update mutations accept them), so each call must decide from what the rows say now.
181
+ */
182
+ describe('SessionRunIDVerifier.Verify — relationships that change between calls', () => {
183
+ const CLAIMS = { CoAgentRunID: CO_RUN_ID, PromptRunID: PROMPT_RUN_ID, CoAgentRunStepID: STEP_ID, PendingFeedbackRunID: PAUSED_RUN_ID };
184
+
185
+ /** Both runs are records of the session, and the step is the co-agent run's Prompt step for the prompt run. */
186
+ function linkedRows(): Rows {
187
+ return {
188
+ 'MJ: AI Agent Runs': [{ ID: CO_RUN_ID }, { ID: PAUSED_RUN_ID }],
189
+ 'MJ: AI Agent Run Steps': [{ ID: STEP_ID, StepType: 'Prompt', TargetLogID: PROMPT_RUN_ID }],
190
+ };
191
+ }
192
+
193
+ it('drops runs that stopped being records of the session since the last call', async () => {
194
+ const rows = linkedRows();
195
+ const { provider } = makeProvider(rows);
196
+ const verifier = new SessionRunIDVerifier();
197
+ const first = await verifier.Verify(SESSION_ID, CLAIMS, USER, provider);
198
+
199
+ // The runs were moved to another session, unlinked or deleted.
200
+ rows['MJ: AI Agent Runs'] = [];
201
+ const second = await verifier.Verify(SESSION_ID, CLAIMS, USER, provider);
202
+
203
+ expect(first).toEqual(CLAIMS);
204
+ expect(second).toEqual({ CoAgentRunID: undefined, PromptRunID: undefined, CoAgentRunStepID: undefined, PendingFeedbackRunID: undefined });
205
+ });
206
+
207
+ it('drops the prompt run and step once the step is no longer linked to the co-agent run', async () => {
208
+ const rows = linkedRows();
209
+ const { provider } = makeProvider(rows);
210
+ const verifier = new SessionRunIDVerifier();
211
+ const first = await verifier.Verify(SESSION_ID, CLAIMS, USER, provider);
212
+
213
+ rows['MJ: AI Agent Run Steps'] = [];
214
+ const second = await verifier.Verify(SESSION_ID, CLAIMS, USER, provider);
215
+
216
+ expect(first).toEqual(CLAIMS);
217
+ expect(second).toEqual({ ...CLAIMS, PromptRunID: undefined, CoAgentRunStepID: undefined });
218
+ });
219
+
220
+ it('still keeps a relationship that has not changed on the next call', async () => {
221
+ const { provider } = makeProvider(linkedRows());
222
+ const verifier = new SessionRunIDVerifier();
223
+
224
+ await verifier.Verify(SESSION_ID, CLAIMS, USER, provider);
225
+ const second = await verifier.Verify(SESSION_ID, CLAIMS, USER, provider);
226
+
227
+ expect(second).toEqual(CLAIMS);
228
+ });
229
+ });
230
+
231
+ describe('SessionRunIDVerifier.Verify — lookups as the acting user', () => {
232
+ it('does not give a user the result of a lookup made for another user', async () => {
233
+ // No system user is cached, so each lookup runs as the acting user, and only USER can see the run.
234
+ const otherUser = { ID: 'd3d3d3d3-0000-4000-8000-0000000000bb' } as unknown as UserInfo;
235
+ const runViews = vi.fn(async (paramsList: RunViewParams[], user: UserInfo) =>
236
+ paramsList.map((params) => {
237
+ const visible = user === USER && params.EntityName === 'MJ: AI Agent Runs';
238
+ return { Success: true, Results: visible ? [{ ID: CO_RUN_ID }] : [], ErrorMessage: '' };
239
+ }),
240
+ );
241
+ const provider = { RunViews: runViews } as unknown as IMetadataProvider;
242
+ const verifier = new SessionRunIDVerifier();
243
+
244
+ const forUser = await verifier.Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, USER, provider);
245
+ const forOtherUser = await verifier.Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, otherUser, provider);
246
+
247
+ expect(forUser.CoAgentRunID).toBe(CO_RUN_ID);
248
+ expect(forOtherUser.CoAgentRunID).toBeUndefined();
249
+ expect(runViews.mock.calls[1]?.[1]).toBe(otherUser);
250
+ });
251
+ });