@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
@@ -0,0 +1,172 @@
1
+ import {
2
+ IMetadataProvider,
3
+ LogError,
4
+ RunView,
5
+ UserInfo,
6
+ } from '@memberjunction/core';
7
+ import { EscapeSQLString, UUIDsEqual } from '@memberjunction/global';
8
+ import type {
9
+ MJMeetingEntity,
10
+ MJMeetingParticipantEntity,
11
+ MJPhoneNumberEntity,
12
+ } from '@memberjunction/core-entities';
13
+ import {
14
+ MeetingType,
15
+ MeetingParticipantType,
16
+ DialInPhoneNumberType,
17
+ } from './meetingTypes.js';
18
+ import {
19
+ LoadParticipants,
20
+ MapMeetingToType,
21
+ } from './meetingHelpers.js';
22
+
23
+ export async function ExecuteMyMeetings(
24
+ provider: IMetadataProvider,
25
+ user: UserInfo,
26
+ status?: string,
27
+ ): Promise<MeetingType[]> {
28
+ try {
29
+ const rv = provider ? RunView.FromMetadataProvider(provider) : new RunView();
30
+ const escapedUserID = EscapeSQLString(user.ID);
31
+
32
+ // 1. Find participant rows for this user
33
+ const participantRows = await rv.RunView<MJMeetingParticipantEntity>(
34
+ {
35
+ EntityName: 'MJ: Meeting Participants',
36
+ ExtraFilter: `UserID = '${escapedUserID}'`,
37
+ ResultType: 'entity_object',
38
+ },
39
+ user,
40
+ );
41
+
42
+ const participantMeetingIDs = new Set<string>();
43
+ if (participantRows.Success && participantRows.Results) {
44
+ for (const p of participantRows.Results) {
45
+ if (p.MeetingID) {
46
+ participantMeetingIDs.add(p.MeetingID);
47
+ }
48
+ }
49
+ }
50
+
51
+ // 2. Query meetings where user is host OR in participantMeetingIDs
52
+ let filter = `HostUserID = '${escapedUserID}'`;
53
+ if (participantMeetingIDs.size > 0) {
54
+ const idList = Array.from(participantMeetingIDs)
55
+ .map((id) => `'${EscapeSQLString(id)}'`)
56
+ .join(', ');
57
+ filter = `(HostUserID = '${escapedUserID}' OR ID IN (${idList}))`;
58
+ }
59
+
60
+ if (status && status.trim()) {
61
+ const escapedStatus = EscapeSQLString(status.trim());
62
+ filter = `${filter} AND Status = '${escapedStatus}'`;
63
+ }
64
+
65
+ const meetingsResult = await rv.RunView<MJMeetingEntity>(
66
+ {
67
+ EntityName: 'MJ: Meetings',
68
+ ExtraFilter: filter,
69
+ OrderBy: 'ScheduledStartAt DESC, __mj_CreatedAt DESC',
70
+ ResultType: 'entity_object',
71
+ },
72
+ user,
73
+ );
74
+
75
+ if (!meetingsResult.Success || !meetingsResult.Results) {
76
+ return [];
77
+ }
78
+
79
+ const output: MeetingType[] = [];
80
+ for (const m of meetingsResult.Results) {
81
+ const participants = await LoadParticipants(m.ID, user, provider);
82
+ output.push(MapMeetingToType(m, participants));
83
+ }
84
+ return output;
85
+ } catch (error) {
86
+ LogError(`[MeetingResolver] MyMeetings failed: ${error instanceof Error ? error.message : String(error)}`);
87
+ return [];
88
+ }
89
+ }
90
+
91
+ export async function ExecuteMeeting(
92
+ provider: IMetadataProvider,
93
+ user: UserInfo,
94
+ id: string,
95
+ ): Promise<MeetingType | null> {
96
+ try {
97
+ const rv = provider ? RunView.FromMetadataProvider(provider) : new RunView();
98
+ const escapedID = EscapeSQLString(id.trim());
99
+
100
+ const result = await rv.RunView<MJMeetingEntity>(
101
+ {
102
+ EntityName: 'MJ: Meetings',
103
+ ExtraFilter: `ID = '${escapedID}'`,
104
+ ResultType: 'entity_object',
105
+ },
106
+ user,
107
+ );
108
+
109
+ if (!result.Success || !result.Results || result.Results.length === 0) {
110
+ return null;
111
+ }
112
+
113
+ const meeting = result.Results[0];
114
+ const participants = await LoadParticipants(meeting.ID, user, provider);
115
+
116
+ // Verify authorization: caller must be host or participant
117
+ const isHost = UUIDsEqual(meeting.HostUserID, user.ID);
118
+ const isParticipant = participants.some((p) => p.UserID && UUIDsEqual(p.UserID, user.ID));
119
+ if (!isHost && !isParticipant) {
120
+ return null;
121
+ }
122
+
123
+ return MapMeetingToType(meeting, participants);
124
+ } catch (error) {
125
+ LogError(`[MeetingResolver] Meeting query failed: ${error instanceof Error ? error.message : String(error)}`);
126
+ return null;
127
+ }
128
+ }
129
+
130
+ export async function ExecuteMeetingParticipants(
131
+ provider: IMetadataProvider,
132
+ user: UserInfo,
133
+ meetingID: string,
134
+ ): Promise<MeetingParticipantType[]> {
135
+ try {
136
+ return await LoadParticipants(meetingID.trim(), user, provider);
137
+ } catch (error) {
138
+ LogError(`[MeetingResolver] MeetingParticipants failed: ${error instanceof Error ? error.message : String(error)}`);
139
+ return [];
140
+ }
141
+ }
142
+
143
+ export async function ExecuteAvailableDialInPhoneNumbers(
144
+ provider: IMetadataProvider,
145
+ user: UserInfo,
146
+ ): Promise<DialInPhoneNumberType[]> {
147
+ try {
148
+ const rv = provider ? RunView.FromMetadataProvider(provider) : new RunView();
149
+
150
+ const result = await rv.RunView<MJPhoneNumberEntity>(
151
+ {
152
+ EntityName: 'MJ: Phone Numbers',
153
+ ExtraFilter: "Status = 'Active'",
154
+ ResultType: 'entity_object',
155
+ },
156
+ user,
157
+ );
158
+
159
+ if (!result.Success || !result.Results) {
160
+ return [];
161
+ }
162
+
163
+ return result.Results.map((p) => ({
164
+ ID: p.ID,
165
+ Number: p.Number,
166
+ Label: p.Label ?? undefined,
167
+ }));
168
+ } catch (error) {
169
+ LogError(`[MeetingResolver] AvailableDialInPhoneNumbers failed: ${error instanceof Error ? error.message : String(error)}`);
170
+ return [];
171
+ }
172
+ }
@@ -0,0 +1,238 @@
1
+ import { ObjectType, InputType, Field } from 'type-graphql';
2
+
3
+ @ObjectType()
4
+ export class MeetingParticipantType {
5
+ @Field(() => String)
6
+ ID: string;
7
+
8
+ @Field(() => String)
9
+ MeetingID: string;
10
+
11
+ @Field(() => String, { nullable: true })
12
+ UserID?: string;
13
+
14
+ @Field(() => String, { nullable: true })
15
+ UserName?: string;
16
+
17
+ @Field(() => String, { nullable: true })
18
+ UserEmail?: string;
19
+
20
+ @Field(() => String, { nullable: true })
21
+ AgentID?: string;
22
+
23
+ @Field(() => String, { nullable: true })
24
+ AgentName?: string;
25
+
26
+ @Field(() => String, { nullable: true })
27
+ ExternalName?: string;
28
+
29
+ @Field(() => String, { nullable: true })
30
+ ExternalEmail?: string;
31
+
32
+ @Field(() => String, { nullable: true })
33
+ ExternalPhone?: string;
34
+
35
+ @Field(() => String)
36
+ Role: string;
37
+
38
+ @Field(() => String)
39
+ InviteStatus: string;
40
+
41
+ @Field(() => Date, { nullable: true })
42
+ JoinedAt?: Date;
43
+
44
+ @Field(() => Date, { nullable: true })
45
+ LeftAt?: Date;
46
+ }
47
+
48
+ @ObjectType()
49
+ export class MeetingType {
50
+ @Field(() => String)
51
+ ID: string;
52
+
53
+ @Field(() => String)
54
+ Title: string;
55
+
56
+ @Field(() => String, { nullable: true })
57
+ Description?: string;
58
+
59
+ @Field(() => String)
60
+ HostUserID: string;
61
+
62
+ @Field(() => String, { nullable: true })
63
+ HostUserName?: string;
64
+
65
+ @Field(() => String)
66
+ RoomName: string;
67
+
68
+ @Field(() => String)
69
+ Status: string;
70
+
71
+ @Field(() => Date, { nullable: true })
72
+ ScheduledStartAt?: Date;
73
+
74
+ @Field(() => Date, { nullable: true })
75
+ ScheduledEndAt?: Date;
76
+
77
+ @Field(() => Date, { nullable: true })
78
+ StartedAt?: Date;
79
+
80
+ @Field(() => Date, { nullable: true })
81
+ EndedAt?: Date;
82
+
83
+ @Field(() => Boolean)
84
+ AllowPhoneDialIn: boolean;
85
+
86
+ @Field(() => String, { nullable: true })
87
+ DialInPhoneNumberID?: string;
88
+
89
+ @Field(() => String, { nullable: true })
90
+ DialInPhoneNumber?: string;
91
+
92
+ @Field(() => String, { nullable: true })
93
+ DialInCode?: string;
94
+
95
+ @Field(() => String)
96
+ RecordingPolicy: string;
97
+
98
+ @Field(() => String, { nullable: true })
99
+ ConversationID?: string;
100
+
101
+ @Field(() => [MeetingParticipantType], { nullable: true })
102
+ Participants?: MeetingParticipantType[];
103
+
104
+ @Field(() => Date)
105
+ CreatedAt: Date;
106
+
107
+ @Field(() => Date)
108
+ UpdatedAt: Date;
109
+ }
110
+
111
+ @ObjectType()
112
+ export class MeetingResult {
113
+ @Field(() => Boolean)
114
+ Success: boolean;
115
+
116
+ @Field(() => String, { nullable: true })
117
+ ErrorMessage?: string;
118
+
119
+ @Field(() => MeetingType, { nullable: true })
120
+ Meeting?: MeetingType;
121
+ }
122
+
123
+ @ObjectType()
124
+ export class StartMeetingResult {
125
+ @Field(() => Boolean)
126
+ Success: boolean;
127
+
128
+ @Field(() => String, { nullable: true })
129
+ ErrorMessage?: string;
130
+
131
+ @Field(() => MeetingType, { nullable: true })
132
+ Meeting?: MeetingType;
133
+
134
+ @Field(() => String, { nullable: true })
135
+ RoomName?: string;
136
+
137
+ @Field(() => String, { nullable: true })
138
+ ClientToken?: string;
139
+ }
140
+
141
+ @ObjectType()
142
+ export class DialInPhoneNumberType {
143
+ @Field(() => String)
144
+ ID: string;
145
+
146
+ @Field(() => String)
147
+ Number: string;
148
+
149
+ @Field(() => String, { nullable: true })
150
+ Label?: string;
151
+ }
152
+
153
+ @InputType()
154
+ export class MeetingParticipantInput {
155
+ @Field(() => String, { nullable: true })
156
+ UserID?: string;
157
+
158
+ @Field(() => String, { nullable: true })
159
+ AgentID?: string;
160
+
161
+ @Field(() => String, { nullable: true })
162
+ ExternalName?: string;
163
+
164
+ @Field(() => String, { nullable: true })
165
+ ExternalEmail?: string;
166
+
167
+ @Field(() => String, { nullable: true })
168
+ ExternalPhone?: string;
169
+
170
+ @Field(() => String, { nullable: true })
171
+ Role?: string;
172
+ }
173
+
174
+ @InputType()
175
+ export class CreateMeetingInput {
176
+ @Field(() => String)
177
+ Title: string;
178
+
179
+ @Field(() => String, { nullable: true })
180
+ Description?: string;
181
+
182
+ @Field(() => Date, { nullable: true })
183
+ ScheduledStartAt?: Date;
184
+
185
+ @Field(() => Date, { nullable: true })
186
+ ScheduledEndAt?: Date;
187
+
188
+ @Field(() => Boolean, { nullable: true })
189
+ AllowPhoneDialIn?: boolean;
190
+
191
+ @Field(() => String, { nullable: true })
192
+ DialInPhoneNumberID?: string;
193
+
194
+ @Field(() => String, { nullable: true })
195
+ RecordingPolicy?: string;
196
+
197
+ @Field(() => [MeetingParticipantInput], { nullable: true })
198
+ Participants?: MeetingParticipantInput[];
199
+ }
200
+
201
+ @InputType()
202
+ export class UpdateMeetingInput {
203
+ @Field(() => String)
204
+ MeetingID: string;
205
+
206
+ @Field(() => String, { nullable: true })
207
+ Title?: string;
208
+
209
+ @Field(() => String, { nullable: true })
210
+ Description?: string;
211
+
212
+ @Field(() => Date, { nullable: true })
213
+ ScheduledStartAt?: Date;
214
+
215
+ @Field(() => Date, { nullable: true })
216
+ ScheduledEndAt?: Date;
217
+
218
+ @Field(() => Boolean, { nullable: true })
219
+ AllowPhoneDialIn?: boolean;
220
+
221
+ @Field(() => String, { nullable: true })
222
+ DialInPhoneNumberID?: string;
223
+
224
+ @Field(() => String, { nullable: true })
225
+ RecordingPolicy?: string;
226
+
227
+ @Field(() => [MeetingParticipantInput], { nullable: true })
228
+ Participants?: MeetingParticipantInput[];
229
+ }
230
+
231
+ @InputType()
232
+ export class RSVPMeetingInput {
233
+ @Field(() => String)
234
+ MeetingID: string;
235
+
236
+ @Field(() => String)
237
+ InviteStatus: string;
238
+ }
@@ -206,17 +206,20 @@ export class ViewOperationsHandler {
206
206
  params.Fields = (params.Fields as string).split(',');
207
207
  }
208
208
 
209
- // Sanitize numeric values
209
+ // Sanitize numeric values: base-10, integral, and clamped to a sane range so a
210
+ // negative/garbage value can never reach the SQL layer.
210
211
  if (params.MaxRows !== undefined) {
211
- params.MaxRows = typeof params.MaxRows === 'string'
212
- ? parseInt(params.MaxRows as string)
212
+ const parsed = typeof params.MaxRows === 'string'
213
+ ? parseInt(params.MaxRows as string, 10)
213
214
  : params.MaxRows;
215
+ params.MaxRows = Number.isFinite(parsed) ? Math.max(0, Math.floor(parsed as number)) : undefined;
214
216
  }
215
217
 
216
218
  if (params.StartRow !== undefined) {
217
- params.StartRow = typeof params.StartRow === 'string'
218
- ? parseInt(params.StartRow as string)
219
+ const parsed = typeof params.StartRow === 'string'
220
+ ? parseInt(params.StartRow as string, 10)
219
221
  : params.StartRow;
222
+ params.StartRow = Number.isFinite(parsed) ? Math.max(0, Math.floor(parsed as number)) : undefined;
220
223
  }
221
224
 
222
225
  // Default ResultType if not provided
@@ -35,9 +35,19 @@ export class TaskGraphActionRunner implements TaskActionRunner {
35
35
  Filters: [],
36
36
  });
37
37
 
38
+ const output = this.buildOutput(result.Params);
39
+ const unstorable = this.findUnstorableOutput(output);
40
+ if (unstorable) {
41
+ const message =
42
+ `Action "${action.Name}" returned output param "${unstorable.Name}", which cannot be stored ` +
43
+ `as the step's result: ${unstorable.Reason}. Output params of a durable step must be plain data.`;
44
+ LogError(`[TaskGraphActionRunner] Task ${params.TaskID}: ${message}`);
45
+ return { Success: false, ErrorMessage: message, ActionLogID: result.LogEntry?.ID };
46
+ }
47
+
38
48
  return {
39
49
  Success: result.Success,
40
- Output: this.buildOutput(result.Params),
50
+ Output: output,
41
51
  ErrorMessage: result.Success ? undefined : result.Message,
42
52
  // The engine already wrote the log; this is the only place its id is in hand. Without
43
53
  // carrying it out, a workflow's action step has no path back to its own execution
@@ -51,6 +61,24 @@ export class TaskGraphActionRunner implements TaskActionRunner {
51
61
  }
52
62
  }
53
63
 
64
+ /**
65
+ * The first output param that cannot be written to the Task row as JSON, or null.
66
+ *
67
+ * Checked here because this is the last place that knows the action's and the param's NAMES: the
68
+ * dispatcher serializes the whole Output later and, for a live object (an entity, an observable),
69
+ * fails with a bare "Converting circular structure to JSON" that names neither.
70
+ */
71
+ private findUnstorableOutput(output: Record<string, unknown>): { Name: string; Reason: string } | null {
72
+ for (const [Name, value] of Object.entries(output)) {
73
+ try {
74
+ JSON.stringify(value);
75
+ } catch (e) {
76
+ return { Name, Reason: e instanceof Error ? e.message.split('\n')[0] : String(e) };
77
+ }
78
+ }
79
+ return null;
80
+ }
81
+
54
82
  /**
55
83
  * Projects the action's parameters onto the flat result a workflow step is expected to produce.
56
84
  *
@@ -46,7 +46,8 @@ export class TaskGraphContinuationDeliverer implements TaskContinuationDeliverer
46
46
  ) {}
47
47
 
48
48
  /**
49
- * Posts the roll-up as an AI-role message in the graph's conversation.
49
+ * Posts the roll-up as an AI-role message in the graph's conversation, on the fork of the
50
+ * message that started the graph (Main when that message is in Main).
50
51
  *
51
52
  * Never throws. The dispatcher calls this inside the compare-and-swap that marks a completion
52
53
  * delivered; an error escaping would either abort that guard or leave the graph looking
@@ -62,8 +63,8 @@ export class TaskGraphContinuationDeliverer implements TaskContinuationDeliverer
62
63
  }
63
64
 
64
65
  const provider = await this.providerFactory.CreateProvider();
65
- const conversationID = await this.resolveConversationID(params.ConversationDetailID, provider);
66
- if (!conversationID) {
66
+ const origin = await this.resolveOrigin(params.ConversationDetailID, provider);
67
+ if (!origin) {
67
68
  LogError(
68
69
  `[TaskGraphContinuationDeliverer] Conversation detail ${params.ConversationDetailID} could not be loaded — ` +
69
70
  `"${params.WorkflowName}" has nowhere to post its outcome.`
@@ -73,7 +74,8 @@ export class TaskGraphContinuationDeliverer implements TaskContinuationDeliverer
73
74
 
74
75
  const detail = await provider.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details', this.contextUser);
75
76
  detail.NewRecord();
76
- detail.ConversationID = conversationID;
77
+ detail.ConversationID = origin.ConversationID;
78
+ detail.BranchID = origin.BranchID;
77
79
  detail.Role = 'AI';
78
80
  detail.Status = 'Complete';
79
81
  detail.HiddenToUser = false;
@@ -125,11 +127,15 @@ export class TaskGraphContinuationDeliverer implements TaskContinuationDeliverer
125
127
  return;
126
128
  }
127
129
 
130
+ // Run on the fork of the message that started the graph, as PostMessage posts into it:
131
+ // RunAgent derives the conversation from the detail, but not the fork.
132
+ const origin = params.ConversationDetailID ? await this.resolveOrigin(params.ConversationDetailID, provider) : null;
128
133
  await new AgentRunner().RunAgent({
129
134
  agent,
130
135
  conversationMessages: [{ role: ChatMessageRole.user, content: this.renderMessage(params) }],
131
136
  contextUser: this.contextUser,
132
137
  conversationDetailId: params.ConversationDetailID ?? undefined,
138
+ ConversationBranchID: origin?.BranchID ?? undefined,
133
139
  // The load-bearing value: without it the next graph this run submits restarts the
134
140
  // chain at zero and the cap never fires.
135
141
  continuationDepth: params.ReinvokeDepth + 1,
@@ -149,10 +155,10 @@ export class TaskGraphContinuationDeliverer implements TaskContinuationDeliverer
149
155
  }
150
156
  }
151
157
 
152
- /** The conversation a detail belongs to. */
153
- private async resolveConversationID(conversationDetailID: string, provider: IMetadataProvider): Promise<string | null> {
158
+ /** The conversation and fork (null is Main) of the detail that started the graph; null when it cannot be loaded. */
159
+ private async resolveOrigin(conversationDetailID: string, provider: IMetadataProvider): Promise<{ ConversationID: string; BranchID: string | null } | null> {
154
160
  const detail = await provider.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details', this.contextUser);
155
- return (await detail.Load(conversationDetailID)) ? detail.ConversationID : null;
161
+ return (await detail.Load(conversationDetailID)) ? { ConversationID: detail.ConversationID, BranchID: detail.BranchID ?? null } : null;
156
162
  }
157
163
 
158
164
  /**
@@ -0,0 +1,129 @@
1
+ /**
2
+ * @fileoverview Starts the durable work-queue host inside MJServer when `workQueue.enabled` is set.
3
+ * @module MJServer/services
4
+ */
5
+ import { randomBytes } from 'node:crypto';
6
+ import { hostname } from 'node:os';
7
+ import sql from 'mssql';
8
+ import { LogError, LogStatus } from '@memberjunction/core';
9
+ import type { DatabaseProviderBase, IMetadataProvider, UserInfo } from '@memberjunction/core';
10
+ import { UserCache } from '@memberjunction/generic-database-provider';
11
+ import { MJWorkLogger, WorkQueueEngine, WorkQueueHost } from '@memberjunction/work-queue-engine';
12
+ import type { WorkQueueHostConfig, WorkQueueProviderSource } from '@memberjunction/work-queue-engine';
13
+ import { TaskGraphProviderFactory } from './TaskGraphProviderFactory.js';
14
+ import type { WorkQueueConfig } from './workQueueConfig.js';
15
+
16
+ export const WORK_QUEUE_DISABLE_ENV = 'MJ_DISABLE_WORK_QUEUE_HOST';
17
+
18
+ /** Matches the width of WorkQueueDelivery.LeaseOwner. */
19
+ const MAX_INSTANCE_ID_LENGTH = 200;
20
+
21
+ type EnvironmentVariables = Record<string, string | undefined>;
22
+
23
+ export interface StartableWorkQueueHost {
24
+ Start(): Promise<void>;
25
+ }
26
+
27
+ /** Seams for tests; production uses DEFAULT_WORK_QUEUE_HOST_START_DEPENDENCIES. */
28
+ export interface WorkQueueHostStartDependencies {
29
+ Env: EnvironmentVariables;
30
+ FindUserByEmail(email: string): UserInfo | undefined;
31
+ ConfigureEngine(user: UserInfo, provider: DatabaseProviderBase): Promise<WorkQueueEngine>;
32
+ CreateHost(config: WorkQueueHostConfig, engine: WorkQueueEngine, user: UserInfo, provider: DatabaseProviderBase, providerSource: WorkQueueProviderSource): StartableWorkQueueHost;
33
+ }
34
+
35
+ export const DEFAULT_WORK_QUEUE_HOST_START_DEPENDENCIES: WorkQueueHostStartDependencies = {
36
+ Env: process.env,
37
+ FindUserByEmail: email => UserCache.Users.find(user => user.Email?.trim().toLowerCase() === email.trim().toLowerCase()),
38
+ ConfigureEngine: async (user, provider) => {
39
+ await WorkQueueEngine.Instance.Config(false, user, provider);
40
+ await ReportTopologyIssues(WorkQueueEngine.Instance);
41
+ return WorkQueueEngine.Instance;
42
+ },
43
+ CreateHost: (config, engine, user, provider, providerSource) =>
44
+ new WorkQueueHost(config, engine, user, provider, new MJWorkLogger(), { ProviderSource: providerSource }),
45
+ };
46
+
47
+ /**
48
+ * Topology rows, capability gating, driver bindings and database prerequisites (03 §11) — including SQL Server's
49
+ * READ_COMMITTED_SNAPSHOT requirement (03 §7). Problems are logged, never thrown: the planner still gates each
50
+ * subscription, and an operator can read the same list from WorkQueue.ValidateBindings.
51
+ */
52
+ async function ReportTopologyIssues(engine: WorkQueueEngine): Promise<void> {
53
+ try {
54
+ for (const issue of await engine.ValidateTopology()) {
55
+ const line = `[WorkQueue] ${issue.Severity}: ${issue.Subject} — ${issue.Message}`;
56
+ if (issue.Severity === 'Error') {
57
+ LogError(line);
58
+ } else {
59
+ LogStatus(line);
60
+ }
61
+ }
62
+ } catch (error) {
63
+ LogError(`[WorkQueue] Topology validation failed: ${error instanceof Error ? error.message : String(error)}`);
64
+ }
65
+ }
66
+
67
+ export function IsWorkQueueHostDisabledByEnv(env: EnvironmentVariables): boolean {
68
+ return env[WORK_QUEUE_DISABLE_ENV] === '1';
69
+ }
70
+
71
+ /**
72
+ * Host and pid for humans reading logs; the random suffix for uniqueness — containers routinely share pid 1 and
73
+ * HOSTNAME is not always exported. A restart is a new instance, so a dead process's leases simply expire.
74
+ */
75
+ export function CreateWorkQueueInstanceID(env: EnvironmentVariables = process.env): string {
76
+ const host = env.HOSTNAME ?? hostname();
77
+ return `${host}-${process.pid}-${randomBytes(4).toString('hex')}`.slice(0, MAX_INSTANCE_ID_LENGTH);
78
+ }
79
+
80
+ export function BuildWorkQueueHostConfig(config: WorkQueueConfig, instanceID: string): WorkQueueHostConfig {
81
+ return {
82
+ InstanceID: instanceID,
83
+ Subscriptions: config.subscriptions.map(entry => ({ Name: entry.name, Concurrency: entry.concurrency })),
84
+ IdlePollMinMs: config.idlePollMinMs,
85
+ IdlePollMaxMs: config.idlePollMaxMs,
86
+ ShutdownDrainMs: config.shutdownDrainMs,
87
+ SweeperIntervalMs: config.sweeperEnabled ? config.sweeperIntervalMs : 0,
88
+ ReconcileIntervalMs: config.reconcileIntervalMs,
89
+ };
90
+ }
91
+
92
+ /** A fresh SQL Server provider per delivery over the shared pool; the server provider otherwise. */
93
+ export class MJServerWorkQueueProviderSource implements WorkQueueProviderSource {
94
+ private readonly factory: TaskGraphProviderFactory | null;
95
+
96
+ constructor(pool: sql.ConnectionPool | null, private readonly fallback: IMetadataProvider) {
97
+ this.factory = pool ? new TaskGraphProviderFactory(pool) : null;
98
+ }
99
+
100
+ public CreateProvider(): Promise<IMetadataProvider> {
101
+ return this.factory ? this.factory.CreateProvider() : Promise.resolve(this.fallback);
102
+ }
103
+ }
104
+
105
+ /** Returns the started host, or null when this instance runs no subscriptions. */
106
+ export async function StartWorkQueueHost(
107
+ config: WorkQueueConfig,
108
+ provider: DatabaseProviderBase,
109
+ providerSource: WorkQueueProviderSource,
110
+ dependencies: WorkQueueHostStartDependencies = DEFAULT_WORK_QUEUE_HOST_START_DEPENDENCIES,
111
+ ): Promise<StartableWorkQueueHost | null> {
112
+ if (!config.enabled) {
113
+ return null;
114
+ }
115
+ if (IsWorkQueueHostDisabledByEnv(dependencies.Env)) {
116
+ LogStatus(`[WorkQueue] Host disabled by ${WORK_QUEUE_DISABLE_ENV}=1 — this process publishes but runs no subscriptions.`);
117
+ return null;
118
+ }
119
+ const user = dependencies.FindUserByEmail(config.systemUserEmail);
120
+ if (!user) {
121
+ throw new Error(`[WorkQueue] System user not found with email: ${config.systemUserEmail} — set workQueue.systemUserEmail in mj.config.cjs`);
122
+ }
123
+ const engine = await dependencies.ConfigureEngine(user, provider);
124
+ const hostConfig = BuildWorkQueueHostConfig(config, CreateWorkQueueInstanceID(dependencies.Env));
125
+ const host = dependencies.CreateHost(hostConfig, engine, user, provider, providerSource);
126
+ await host.Start();
127
+ LogStatus(`🔄 Work Queue host ${hostConfig.InstanceID}: ${hostConfig.Subscriptions.map(s => `${s.Name}×${s.Concurrency}`).join(', ')}`);
128
+ return host;
129
+ }