@memberjunction/server 6.2.0-edge.5 → 6.2.0-edge.6

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 (401) hide show
  1. package/README.md +27 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/SessionJanitor.d.ts +3 -3
  4. package/dist/agentSessions/SessionJanitor.js +3 -3
  5. package/dist/agentSessions/SessionManager.d.ts +15 -5
  6. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  7. package/dist/agentSessions/SessionManager.js +34 -12
  8. package/dist/agentSessions/SessionManager.js.map +1 -1
  9. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
  10. package/dist/auth/AuthProviderCatalogRouter.js +2 -0
  11. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
  12. package/dist/auth/index.d.ts.map +1 -1
  13. package/dist/auth/index.js +51 -10
  14. package/dist/auth/index.js.map +1 -1
  15. package/dist/auth/initializeProviders.d.ts +10 -0
  16. package/dist/auth/initializeProviders.d.ts.map +1 -1
  17. package/dist/auth/initializeProviders.js +37 -1
  18. package/dist/auth/initializeProviders.js.map +1 -1
  19. package/dist/auth/localDevAuth.d.ts +79 -0
  20. package/dist/auth/localDevAuth.d.ts.map +1 -0
  21. package/dist/auth/localDevAuth.js +439 -0
  22. package/dist/auth/localDevAuth.js.map +1 -0
  23. package/dist/auth/magicLink/MagicLinkKeys.d.ts +2 -2
  24. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  25. package/dist/auth/magicLink/MagicLinkKeys.js +1 -1
  26. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  27. package/dist/auth/magicLink/types.d.ts +12 -0
  28. package/dist/auth/magicLink/types.d.ts.map +1 -1
  29. package/dist/auth/sessionUserClone.d.ts +1 -1
  30. package/dist/auth/sessionUserClone.d.ts.map +1 -1
  31. package/dist/auth/sessionUserClone.js +3 -2
  32. package/dist/auth/sessionUserClone.js.map +1 -1
  33. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts +292 -0
  34. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -0
  35. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +442 -0
  36. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -0
  37. package/dist/broadcast/index.d.ts +6 -0
  38. package/dist/broadcast/index.d.ts.map +1 -0
  39. package/dist/broadcast/index.js +6 -0
  40. package/dist/broadcast/index.js.map +1 -0
  41. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts +43 -0
  42. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts.map +1 -0
  43. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js +144 -0
  44. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js.map +1 -0
  45. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts +46 -0
  46. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts.map +1 -0
  47. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js +149 -0
  48. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js.map +1 -0
  49. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts +49 -0
  50. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts.map +1 -0
  51. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js +131 -0
  52. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js.map +1 -0
  53. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts +24 -0
  54. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts.map +1 -0
  55. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js +51 -0
  56. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js.map +1 -0
  57. package/dist/cache.d.ts +3 -2
  58. package/dist/cache.d.ts.map +1 -1
  59. package/dist/cache.js.map +1 -1
  60. package/dist/config.d.ts +496 -0
  61. package/dist/config.d.ts.map +1 -1
  62. package/dist/config.js +61 -0
  63. package/dist/config.js.map +1 -1
  64. package/dist/context.d.ts +3 -1
  65. package/dist/context.d.ts.map +1 -1
  66. package/dist/context.js +29 -8
  67. package/dist/context.js.map +1 -1
  68. package/dist/coreRoutes.d.ts.map +1 -1
  69. package/dist/coreRoutes.js +14 -0
  70. package/dist/coreRoutes.js.map +1 -1
  71. package/dist/generated/graphql-schemas/__mj.d.ts +635 -14
  72. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  73. package/dist/generated/graphql-schemas/__mj.js +3366 -418
  74. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  75. package/dist/generic/CacheInvalidationResolver.d.ts +50 -30
  76. package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
  77. package/dist/generic/CacheInvalidationResolver.js +154 -79
  78. package/dist/generic/CacheInvalidationResolver.js.map +1 -1
  79. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  80. package/dist/generic/OrphanedConversationDetailReconciler.js +2 -0
  81. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  82. package/dist/generic/PubSubManager.d.ts +10 -0
  83. package/dist/generic/PubSubManager.d.ts.map +1 -1
  84. package/dist/generic/PubSubManager.js +44 -1
  85. package/dist/generic/PubSubManager.js.map +1 -1
  86. package/dist/generic/PushStatusResolver.d.ts +39 -6
  87. package/dist/generic/PushStatusResolver.d.ts.map +1 -1
  88. package/dist/generic/PushStatusResolver.js +93 -11
  89. package/dist/generic/PushStatusResolver.js.map +1 -1
  90. package/dist/generic/ResolverBase.d.ts +65 -25
  91. package/dist/generic/ResolverBase.d.ts.map +1 -1
  92. package/dist/generic/ResolverBase.js +201 -101
  93. package/dist/generic/ResolverBase.js.map +1 -1
  94. package/dist/generic/RunViewResolver.d.ts +11 -3
  95. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  96. package/dist/generic/RunViewResolver.js +79 -13
  97. package/dist/generic/RunViewResolver.js.map +1 -1
  98. package/dist/generic/actionOutputFieldSecurity.d.ts +26 -0
  99. package/dist/generic/actionOutputFieldSecurity.d.ts.map +1 -0
  100. package/dist/generic/actionOutputFieldSecurity.js +47 -0
  101. package/dist/generic/actionOutputFieldSecurity.js.map +1 -0
  102. package/dist/generic/fieldSecurityTransport.d.ts +30 -0
  103. package/dist/generic/fieldSecurityTransport.d.ts.map +1 -0
  104. package/dist/generic/fieldSecurityTransport.js +30 -0
  105. package/dist/generic/fieldSecurityTransport.js.map +1 -0
  106. package/dist/index.d.ts +3 -0
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +122 -66
  109. package/dist/index.js.map +1 -1
  110. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  111. package/dist/integration/CustomColumnPromoter.js +6 -1
  112. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  113. package/dist/logging/variablesLoggingMiddleware.d.ts +12 -0
  114. package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
  115. package/dist/logging/variablesLoggingMiddleware.js +50 -1
  116. package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
  117. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -1
  118. package/dist/realtimeSessions/SessionVerificationPortsBase.js +1 -0
  119. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -1
  120. package/dist/realtimeSessions/ownedRealtimeSession.d.ts +8 -6
  121. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
  122. package/dist/realtimeSessions/ownedRealtimeSession.js +15 -6
  123. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
  124. package/dist/realtimeSessions/sessionAccess.d.ts +88 -11
  125. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  126. package/dist/realtimeSessions/sessionAccess.js +125 -22
  127. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  128. package/dist/realtimeSessions/sessionConfigStamps.js +1 -1
  129. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -1
  130. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -1
  131. package/dist/realtimeSessions/subscriptionAuthorization.js +5 -2
  132. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -1
  133. package/dist/realtimeSessions/verificationWorkflow.d.ts +2 -0
  134. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -1
  135. package/dist/realtimeSessions/verificationWorkflow.js +1 -1
  136. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -1
  137. package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
  138. package/dist/realtimeWidget/WidgetRouter.js +19 -3
  139. package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
  140. package/dist/realtimeWidget/WidgetSessionService.d.ts +67 -7
  141. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  142. package/dist/realtimeWidget/WidgetSessionService.js +201 -19
  143. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  144. package/dist/realtimeWidget/host-identity.d.ts +1 -1
  145. package/dist/realtimeWidget/host-identity.js +1 -1
  146. package/dist/realtimeWidget/widgetCore.d.ts +20 -1
  147. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  148. package/dist/realtimeWidget/widgetCore.js +17 -1
  149. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  150. package/dist/realtimeWidget/widgetGuestElevation.d.ts +1 -1
  151. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  152. package/dist/realtimeWidget/widgetGuestElevation.js +2 -1
  153. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  154. package/dist/resolvers/ActionResolver.d.ts +19 -1
  155. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  156. package/dist/resolvers/ActionResolver.js +63 -12
  157. package/dist/resolvers/ActionResolver.js.map +1 -1
  158. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  159. package/dist/resolvers/AdhocQueryResolver.js +1 -0
  160. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  161. package/dist/resolvers/AgentRunStatusPublisher.d.ts +18 -4
  162. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
  163. package/dist/resolvers/AgentRunStatusPublisher.js +32 -8
  164. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
  165. package/dist/resolvers/AgentSessionResolver.d.ts +10 -4
  166. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  167. package/dist/resolvers/AgentSessionResolver.js +40 -17
  168. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  169. package/dist/resolvers/EntityCommunicationsResolver.d.ts +13 -1
  170. package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
  171. package/dist/resolvers/EntityCommunicationsResolver.js +50 -14
  172. package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
  173. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +28 -0
  174. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  175. package/dist/resolvers/FetchEntityVectorsResolver.js +117 -21
  176. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  177. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  178. package/dist/resolvers/FileCategoryResolver.js +1 -0
  179. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  180. package/dist/resolvers/FileResolver.d.ts +5 -0
  181. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  182. package/dist/resolvers/FileResolver.js +13 -4
  183. package/dist/resolvers/FileResolver.js.map +1 -1
  184. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  185. package/dist/resolvers/IntegrationDiscoveryResolver.js +45 -7
  186. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  187. package/dist/resolvers/MergeRecordsResolver.d.ts +8 -1
  188. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  189. package/dist/resolvers/MergeRecordsResolver.js +31 -3
  190. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  191. package/dist/resolvers/PipelineProgressResolver.d.ts +6 -0
  192. package/dist/resolvers/PipelineProgressResolver.d.ts.map +1 -1
  193. package/dist/resolvers/PipelineProgressResolver.js +5 -3
  194. package/dist/resolvers/PipelineProgressResolver.js.map +1 -1
  195. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  196. package/dist/resolvers/QueryResolver.js +20 -6
  197. package/dist/resolvers/QueryResolver.js.map +1 -1
  198. package/dist/resolvers/QuerySystemUserResolver.d.ts +2 -0
  199. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  200. package/dist/resolvers/QuerySystemUserResolver.js +15 -6
  201. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  202. package/dist/resolvers/RSUResolver.d.ts.map +1 -1
  203. package/dist/resolvers/RSUResolver.js +12 -0
  204. package/dist/resolvers/RSUResolver.js.map +1 -1
  205. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +16 -2
  206. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  207. package/dist/resolvers/RealtimeClientSessionResolver.js +82 -28
  208. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  209. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  210. package/dist/resolvers/RemoteBrowserActionResolver.js +11 -9
  211. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  212. package/dist/resolvers/RunAIAgentResolver.d.ts +56 -8
  213. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  214. package/dist/resolvers/RunAIAgentResolver.js +209 -64
  215. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  216. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  217. package/dist/resolvers/RunTemplateResolver.js +4 -4
  218. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  219. package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
  220. package/dist/resolvers/RunTestResolver.js +3 -1
  221. package/dist/resolvers/RunTestResolver.js.map +1 -1
  222. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  223. package/dist/resolvers/SyncDataResolver.js +4 -0
  224. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  225. package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
  226. package/dist/resolvers/TestQuerySQLResolver.js +1 -0
  227. package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
  228. package/dist/resolvers/TransactionGroupResolver.d.ts +17 -2
  229. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  230. package/dist/resolvers/TransactionGroupResolver.js +60 -15
  231. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  232. package/dist/resolvers/UserViewResolver.d.ts +1 -1
  233. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  234. package/dist/resolvers/UserViewResolver.js +2 -1
  235. package/dist/resolvers/UserViewResolver.js.map +1 -1
  236. package/dist/rest/EntityCRUDHandler.d.ts +6 -0
  237. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  238. package/dist/rest/EntityCRUDHandler.js +19 -5
  239. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  240. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  241. package/dist/rest/RESTEndpointHandler.js +8 -5
  242. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  243. package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
  244. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
  245. package/dist/rest/RealtimeRecordingHandler.js +7 -7
  246. package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
  247. package/dist/rest/ViewOperationsHandler.d.ts +6 -0
  248. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  249. package/dist/rest/ViewOperationsHandler.js +22 -5
  250. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  251. package/dist/serverRoleConfigUnits.d.ts +29 -0
  252. package/dist/serverRoleConfigUnits.d.ts.map +1 -0
  253. package/dist/serverRoleConfigUnits.js +33 -0
  254. package/dist/serverRoleConfigUnits.js.map +1 -0
  255. package/dist/services/TaskGraphContinuationDeliverer.d.ts +5 -0
  256. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  257. package/dist/services/TaskGraphContinuationDeliverer.js +42 -15
  258. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  259. package/dist/types.d.ts +23 -1
  260. package/dist/types.d.ts.map +1 -1
  261. package/dist/types.js.map +1 -1
  262. package/package.json +98 -98
  263. package/src/__tests__/ActionResolver.fieldSecurity.test.ts +237 -0
  264. package/src/__tests__/AdhocQueryResolver.dialect.test.ts +137 -0
  265. package/src/__tests__/AgentRunStatusPublisher.test.ts +49 -0
  266. package/src/__tests__/AgentSessionResolver.ownership.test.ts +137 -0
  267. package/src/__tests__/CustomColumnPromoterOnePass.test.ts +15 -1
  268. package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
  269. package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
  270. package/src/__tests__/FetchEntityVectorsResolver.test.ts +206 -3
  271. package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
  272. package/src/__tests__/PipelineProgressResolver.filter.test.ts +58 -0
  273. package/src/__tests__/QuerySystemUserResolver.fieldSecurity.test.ts +49 -0
  274. package/src/__tests__/RESTEndpointHandler.test.ts +25 -0
  275. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +23 -3
  276. package/src/__tests__/RealtimeClientSessionResolver.test.ts +324 -46
  277. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +98 -0
  278. package/src/__tests__/ResolverBase.clientOldValues.test.ts +37 -4
  279. package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
  280. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +176 -0
  281. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +23 -1
  282. package/src/__tests__/ResolverBase.publishStatusUpdate.test.ts +68 -0
  283. package/src/__tests__/ResolverBase.userViewCacheAndDedup.test.ts +130 -0
  284. package/src/__tests__/RunAIAgentResolver.branch.test.ts +68 -13
  285. package/src/__tests__/RunAIAgentResolver.conversationWriter.test.ts +210 -0
  286. package/src/__tests__/RunAIAgentResolver.fireAndForgetCompletion.test.ts +209 -0
  287. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +17 -27
  288. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +8 -2
  289. package/src/__tests__/RunAIAgentResolver.serverReply.test.ts +358 -0
  290. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +4 -1
  291. package/src/__tests__/RunTemplateResolver.test.ts +35 -2
  292. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +11 -0
  293. package/src/__tests__/SessionManager.test.ts +95 -0
  294. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +114 -3
  295. package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
  296. package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
  297. package/src/__tests__/ViewOperationsHandler.test.ts +7 -2
  298. package/src/__tests__/WidgetSessionService.test.ts +583 -4
  299. package/src/__tests__/apiRootBoot.test.ts +1 -1
  300. package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
  301. package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
  302. package/src/__tests__/cacheInvalidationRecordData.test.ts +45 -0
  303. package/src/__tests__/fieldSecurityFixtures.ts +91 -0
  304. package/src/__tests__/globalCors.test.ts +3 -3
  305. package/src/__tests__/localDevAuth.test.ts +196 -0
  306. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +10 -0
  307. package/src/__tests__/pushStatusResolver.filter.test.ts +57 -1
  308. package/src/__tests__/realtimeRecordingHandler.test.ts +19 -2
  309. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +29 -1
  310. package/src/__tests__/realtimeSessions/ownedRealtimeSession.anonymous.test.ts +31 -0
  311. package/src/__tests__/realtimeSessions/ownedRealtimeSession.namedScoped.test.ts +37 -0
  312. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +146 -7
  313. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +1 -1
  314. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +25 -4
  315. package/src/__tests__/resolverBase.cloudEventChangedFields.test.ts +157 -0
  316. package/src/__tests__/resolverBase.fieldSecurityTransport.test.ts +222 -0
  317. package/src/__tests__/resolverBase.fls.test.ts +146 -97
  318. package/src/__tests__/restTransport.fieldSecurity.test.ts +148 -0
  319. package/src/__tests__/runParamsTimeZone.test.ts +179 -0
  320. package/src/__tests__/runStreams.fieldSecurity.test.ts +100 -0
  321. package/src/__tests__/runViewProperties.test.ts +114 -0
  322. package/src/__tests__/scalabilityBenchmarks.test.ts +765 -0
  323. package/src/__tests__/scalabilityDatabaseIntegration.test.ts +207 -0
  324. package/src/__tests__/sessionUserClone.test.ts +5 -1
  325. package/src/__tests__/startupSweepOrder.test.ts +40 -0
  326. package/src/__tests__/unifiedAuth.test.ts +28 -0
  327. package/src/__tests__/widgetGuestElevation.test.ts +2 -0
  328. package/src/agentSessions/SessionJanitor.ts +3 -3
  329. package/src/agentSessions/SessionManager.ts +35 -12
  330. package/src/auth/AuthProviderCatalogRouter.ts +3 -0
  331. package/src/auth/index.ts +56 -12
  332. package/src/auth/initializeProviders.ts +43 -1
  333. package/src/auth/localDevAuth.ts +528 -0
  334. package/src/auth/magicLink/MagicLinkKeys.ts +2 -2
  335. package/src/auth/magicLink/types.ts +10 -0
  336. package/src/auth/sessionUserClone.ts +3 -2
  337. package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +674 -0
  338. package/src/broadcast/index.ts +5 -0
  339. package/src/broadcast/providers/AWSIoTCoreBroadcastProvider.ts +180 -0
  340. package/src/broadcast/providers/AzureWebPubSubBroadcastProvider.ts +182 -0
  341. package/src/broadcast/providers/GCPPubSubBroadcastProvider.ts +169 -0
  342. package/src/broadcast/providers/SequencedRoleBucketBroadcastProvider.ts +57 -0
  343. package/src/cache.ts +2 -1
  344. package/src/config.ts +69 -0
  345. package/src/context.ts +32 -8
  346. package/src/coreRoutes.ts +15 -0
  347. package/src/generated/graphql-schemas/__mj.ts +2239 -102
  348. package/src/generic/CacheInvalidationResolver.ts +135 -78
  349. package/src/generic/OrphanedConversationDetailReconciler.ts +4 -2
  350. package/src/generic/PubSubManager.ts +54 -1
  351. package/src/generic/PushStatusResolver.ts +108 -11
  352. package/src/generic/ResolverBase.ts +237 -107
  353. package/src/generic/RunViewResolver.ts +80 -15
  354. package/src/generic/actionOutputFieldSecurity.ts +75 -0
  355. package/src/generic/fieldSecurityTransport.ts +62 -0
  356. package/src/index.ts +127 -68
  357. package/src/integration/CustomColumnPromoter.ts +6 -1
  358. package/src/logging/variablesLoggingMiddleware.ts +59 -3
  359. package/src/realtimeSessions/README.md +7 -3
  360. package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
  361. package/src/realtimeSessions/ownedRealtimeSession.ts +20 -6
  362. package/src/realtimeSessions/sessionAccess.ts +181 -25
  363. package/src/realtimeSessions/sessionConfigStamps.ts +1 -1
  364. package/src/realtimeSessions/subscriptionAuthorization.ts +4 -2
  365. package/src/realtimeSessions/verificationWorkflow.ts +3 -1
  366. package/src/realtimeWidget/WidgetRouter.ts +22 -4
  367. package/src/realtimeWidget/WidgetSessionService.ts +252 -17
  368. package/src/realtimeWidget/host-identity.ts +1 -1
  369. package/src/realtimeWidget/widgetCore.ts +30 -1
  370. package/src/realtimeWidget/widgetGuestElevation.ts +2 -1
  371. package/src/resolvers/ActionResolver.ts +78 -15
  372. package/src/resolvers/AdhocQueryResolver.ts +1 -0
  373. package/src/resolvers/AgentRunStatusPublisher.ts +36 -8
  374. package/src/resolvers/AgentSessionResolver.ts +46 -18
  375. package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
  376. package/src/resolvers/FetchEntityVectorsResolver.ts +132 -21
  377. package/src/resolvers/FileCategoryResolver.ts +1 -0
  378. package/src/resolvers/FileResolver.ts +14 -4
  379. package/src/resolvers/IntegrationDiscoveryResolver.ts +49 -7
  380. package/src/resolvers/MergeRecordsResolver.ts +23 -2
  381. package/src/resolvers/PipelineProgressResolver.ts +12 -3
  382. package/src/resolvers/QueryResolver.ts +20 -6
  383. package/src/resolvers/QuerySystemUserResolver.ts +15 -6
  384. package/src/resolvers/RSUResolver.ts +12 -0
  385. package/src/resolvers/RealtimeClientSessionResolver.ts +104 -33
  386. package/src/resolvers/RemoteBrowserActionResolver.ts +15 -10
  387. package/src/resolvers/RunAIAgentResolver.ts +258 -82
  388. package/src/resolvers/RunTemplateResolver.ts +6 -4
  389. package/src/resolvers/RunTestResolver.ts +3 -1
  390. package/src/resolvers/SyncDataResolver.ts +4 -0
  391. package/src/resolvers/TestQuerySQLResolver.ts +1 -0
  392. package/src/resolvers/TransactionGroupResolver.ts +66 -15
  393. package/src/resolvers/UserViewResolver.ts +2 -1
  394. package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
  395. package/src/rest/EntityCRUDHandler.ts +22 -5
  396. package/src/rest/RESTEndpointHandler.ts +8 -5
  397. package/src/rest/RealtimeRecordingHandler.ts +13 -10
  398. package/src/rest/ViewOperationsHandler.ts +24 -6
  399. package/src/serverRoleConfigUnits.ts +48 -0
  400. package/src/services/TaskGraphContinuationDeliverer.ts +52 -20
  401. package/src/types.ts +23 -1
package/src/index.ts CHANGED
@@ -38,7 +38,7 @@ import { default as jwt } from 'jsonwebtoken';
38
38
  import { contextFunction, CreateReadOnlyProvider, CreateUnifiedAuthMiddleware, getUserPayload } from './context.js';
39
39
  import { UserPayload } from './types.js';
40
40
  import { requireSystemUserDirective, publicDirective } from './directives/index.js';
41
- import { variablesLoggingMiddleware } from './logging/variablesLoggingMiddleware.js';
41
+ import { ApplyRootVariablesLoggingMiddleware, variablesLoggingMiddleware } from './logging/variablesLoggingMiddleware.js';
42
42
  import { auditResolversForUndecoratedArgs } from './logging/bootAudit.js';
43
43
  import { StartupLogger } from './logging/StartupLogger.js';
44
44
  import { AuthProviderFactory } from '@memberjunction/auth-providers';
@@ -46,7 +46,7 @@ import createMSSQLConfig from './orm.js';
46
46
  import { setupRESTEndpoints } from './rest/setupRESTEndpoints.js';
47
47
  import { CreateRealtimeRecordingRouter, REALTIME_RECORDING_MOUNT_PATH } from './rest/RealtimeRecordingHandler.js';
48
48
  import { LoadOwnedRealtimeSession } from './realtimeSessions/ownedRealtimeSession.js';
49
- import { LoadSessionConversationExternalID } from './realtimeSessions/sessionAccess.js';
49
+ import { AuthorizeAnonymousRoomSessions, LoadSessionConversationScope } from './realtimeSessions/sessionAccess.js';
50
50
  import { RecordingAssemblyScheduler } from './realtimeSessions/RecordingAssemblyScheduler.js';
51
51
  import {
52
52
  RealtimeSessionEventService,
@@ -73,11 +73,11 @@ import {
73
73
  SetHandoffOfferPublishHook,
74
74
  ParseReplicatedHandoffOfferUpdate,
75
75
  } from './resolvers/HumanHandoffResolver.js';
76
- import { RoomHandoffEngine } from '@memberjunction/livekit-room-server';
76
+ import { RoomAuthorizationService, RoomHandoffEngine } from '@memberjunction/livekit-room-server';
77
77
  // Type-only: the provider is built in ./sharedCache.ts. Still needed here because the handoff-offer
78
78
  // fan-out below stays in this file — see the note at its call site.
79
79
  import type { RedisLocalStorageProvider } from '@memberjunction/redis-provider';
80
- import { IntegrationProgressEmitter } from '@memberjunction/integration-progress-artifacts';
80
+ import { IntegrationProgressEmitter, SweepInterruptedRuns } from '@memberjunction/integration-progress-artifacts';
81
81
  import { PublishIntegrationProgress } from './resolvers/IntegrationProgressResolver.js';
82
82
  import { RegisterRSUProgressBridge } from './integration/RSUProgressBridge.js';
83
83
  import { ClientToolRequestManager, AgentRunWatchdog, SqlRealtimeRecordingLedger } from '@memberjunction/ai-agents';
@@ -103,10 +103,15 @@ import { ApiRoute, ApiRootPath, ConfiguredPublicUrl } from './apiRoot.js';
103
103
  import { MountHealthcheckRoute, MountPublicCoreRoutes, MountExtensionHealthRoute, MountAuthCatalogRoute, MountAuthenticatedCoreRoutes } from './coreRoutes.js';
104
104
  import { MetadataCacheRefreshIntervalSeconds } from './providerConfigUnits.js';
105
105
  import { CreateMetadataRefreshSignalHandler, METADATA_REFRESH_SIGNAL } from './metadataRefreshSignal.js';
106
+ import { CacheInvalidationBroadcastManager } from './broadcast/index.js';
107
+ import { ResolveServerRole, ShouldRunBackgroundWorkers } from './serverRoleConfigUnits.js';
106
108
 
107
109
  const cacheRefreshInterval = configInfo.databaseSettings.metadataCacheRefreshInterval;
108
110
 
109
111
  export { MetadataCacheRefreshIntervalSeconds } from './providerConfigUnits.js';
112
+ export * from './serverRoleConfigUnits.js';
113
+ export * from './broadcast/index.js';
114
+ export { ApplyRootVariablesLoggingMiddleware } from './logging/variablesLoggingMiddleware.js';
110
115
 
111
116
  /**
112
117
  * Returns the configured database platform from the `DB_PLATFORM` environment
@@ -432,6 +437,9 @@ export const Serve = async (resolverPaths: Array<string>, app: Application = Cre
432
437
  SetHostInstancePort(graphqlPort);
433
438
  // The realtime bridge engine stamps its bridge rows with the same identity and reconciles orphans by it (#5310).
434
439
  BindBridgeEngineHostInstance();
440
+ // An anonymous visitor may join only the LiveKit agent-session room it owns by scope; the room gate cannot import
441
+ // this package's session-access decision, so it is handed in here, before any resolver runs (MJ#6044).
442
+ RoomAuthorizationService.Instance.SetAnonymousSessionOwnerCheck(AuthorizeAnonymousRoomSessions);
435
443
  // Level-gated startup logger. Resolves verbosity from telemetry.level (single
436
444
  // operator knob). At `standard` (default), per-phase timings are collapsed into
437
445
  // the one-line summary; at `verbose`+ each phase prints inline as before.
@@ -491,6 +499,8 @@ const setupComplete$ = new ReplaySubject(1);
491
499
  // Every API pool carries the statement and idle-in-transaction timeouts from connection #1.
492
500
  const pgConnectionConfig = BuildPostgreSQLConnectionConfig(pgEndpoint, configInfo.databaseSettings, 'api');
493
501
  const pgPool = new pg.default.Pool(ToPGPoolConfig(pgConnectionConfig));
502
+ // An idle connection the server drops is emitted on the pool; without a listener Node exits.
503
+ pgPool.on('error', (err) => console.error('[PostgreSQL pool] Idle connection error (connection evicted):', err.message));
494
504
 
495
505
  // Verify connection
496
506
  const testClient = await pgPool.connect();
@@ -508,6 +518,7 @@ const setupComplete$ = new ReplaySubject(1);
508
518
  ...ToPGPoolConfig(BuildPostgreSQLConnectionConfig({ ...pgEndpoint, ...pgReadOnlyCredentials }, configInfo.databaseSettings, 'read-only')),
509
519
  types: MJPostgresTypes,
510
520
  });
521
+ readOnlyPgPool.on('error', (err) => console.error('[PostgreSQL read-only pool] Idle connection error (connection evicted):', err.message));
511
522
  const readOnlyTestClient = await readOnlyPgPool.connect();
512
523
  try {
513
524
  for (const warning of await DescribeReadOnlyLoginOverreach(readOnlyTestClient, mj_core_schema)) {
@@ -594,6 +605,7 @@ const setupComplete$ = new ReplaySubject(1);
594
605
  'codegen',
595
606
  );
596
607
  const codegenPgPool = new pg.default.Pool(ToPGPoolConfig(codegenPgConnectionConfig));
608
+ codegenPgPool.on('error', (err) => console.error('[PostgreSQL CodeGen pool] Idle connection error (connection evicted):', err.message));
597
609
  const codegenTestClient = await codegenPgPool.connect();
598
610
  await codegenTestClient.query('SELECT 1');
599
611
  codegenTestClient.release();
@@ -632,7 +644,7 @@ const setupComplete$ = new ReplaySubject(1);
632
644
  startupLog.LogIf('verbose', 'RSU in-process CodeGen runner initialized (PostgreSQL).');
633
645
 
634
646
  // Inject CodeGen output paths for targeted git staging
635
- const { initializeConfig } = await import('@memberjunction/codegen-lib');
647
+ const { initializeConfig, ResolveAdditionalSchemaInfoPath } = await import('@memberjunction/codegen-lib');
636
648
  const cgConfig = initializeConfig(rsuWorkDir);
637
649
  const outputPaths = (cgConfig.output ?? []).map((o: { directory: string }) => o.directory);
638
650
  RuntimeSchemaManager.Instance.SetCodeGenOutputPaths(outputPaths);
@@ -641,9 +653,12 @@ const setupComplete$ = new ReplaySubject(1);
641
653
  // Point RSU's soft PK/FK writer at the SAME file CodeGen reads (mj.config.cjs
642
654
  // `additionalSchemaInfo`), or RSU writes soft PKs to its own default path while
643
655
  // CodeGen reads a different one and skips integration tables with "No primary key found".
644
- if (cgConfig.additionalSchemaInfo) {
645
- RuntimeSchemaManager.Instance.SetAdditionalSchemaInfoPath(cgConfig.additionalSchemaInfo);
646
- startupLog.LogIf('verbose', `RSU additionalSchemaInfo path: ${cgConfig.additionalSchemaInfo}`);
656
+ // Inject the ABSOLUTE path CodeGen resolves (env override, else relative to the mj.config
657
+ // file's directory) so RSU, the resolvers and CodeGen agree whatever their cwd (#5858).
658
+ const resolvedSchemaInfoPath = ResolveAdditionalSchemaInfoPath();
659
+ if (resolvedSchemaInfoPath) {
660
+ RuntimeSchemaManager.Instance.SetAdditionalSchemaInfoPath(resolvedSchemaInfoPath);
661
+ startupLog.LogIf('verbose', `RSU additionalSchemaInfo path: ${resolvedSchemaInfoPath}`);
647
662
  } else if (RuntimeSchemaManager.Instance.IsEnabled) {
648
663
  // U5 — boot-time assert: RSU is enabled but CodeGen has NO additionalSchemaInfo path
649
664
  // configured, so RSU's soft-PK/FK writes land in a file CodeGen never reads. Every
@@ -651,7 +666,7 @@ const setupComplete$ = new ReplaySubject(1);
651
666
  // named, and actionable — grep for RSU_ADDITIONAL_SCHEMA_INFO_DIVERGENCE.
652
667
  console.error(
653
668
  `[RSU_ADDITIONAL_SCHEMA_INFO_DIVERGENCE] Runtime Schema Update is ENABLED but mj.config.cjs has no ` +
654
- `'additionalSchemaInfo' setting — RSU writes soft PK/FK definitions to '${process.env.RSU_ADDITIONAL_SCHEMA_INFO_PATH ?? 'additionalSchemaInfo.json'}' ` +
669
+ `'additionalSchemaInfo' setting (and RSU_ADDITIONAL_SCHEMA_INFO_PATH is unset) — RSU writes soft PK/FK definitions to '${RuntimeSchemaManager.Instance.AdditionalSchemaInfoPath}' ` +
655
670
  `while CodeGen reads none, so resolved soft primary keys will be silently LOST at codegen time. ` +
656
671
  `Set 'additionalSchemaInfo' in mj.config.cjs to the same file so the write path and read path agree.`
657
672
  );
@@ -777,7 +792,7 @@ const setupComplete$ = new ReplaySubject(1);
777
792
  startupLog.LogIf('verbose', 'RSU in-process CodeGen runner initialized.');
778
793
 
779
794
  // Inject CodeGen output paths for targeted git staging
780
- const { initializeConfig } = await import('@memberjunction/codegen-lib');
795
+ const { initializeConfig, ResolveAdditionalSchemaInfoPath } = await import('@memberjunction/codegen-lib');
781
796
  const codegenConfig = initializeConfig(rsuWorkDir);
782
797
  const outputPaths = (codegenConfig.output ?? []).map((o: { directory: string }) => o.directory);
783
798
  RuntimeSchemaManager.Instance.SetCodeGenOutputPaths(outputPaths);
@@ -787,14 +802,17 @@ const setupComplete$ = new ReplaySubject(1);
787
802
  // `additionalSchemaInfo`). Without this, RSU writes soft PKs to its own default
788
803
  // path while CodeGen reads a different one and skips every integration table
789
804
  // with "No primary key found".
790
- if (codegenConfig.additionalSchemaInfo) {
791
- RuntimeSchemaManager.Instance.SetAdditionalSchemaInfoPath(codegenConfig.additionalSchemaInfo);
792
- startupLog.LogIf('verbose', `RSU additionalSchemaInfo path: ${codegenConfig.additionalSchemaInfo}`);
805
+ // Inject the ABSOLUTE path CodeGen resolves (env override, else relative to the mj.config
806
+ // file's directory) so RSU, the resolvers and CodeGen agree whatever their cwd (#5858).
807
+ const resolvedSchemaInfoPath = ResolveAdditionalSchemaInfoPath();
808
+ if (resolvedSchemaInfoPath) {
809
+ RuntimeSchemaManager.Instance.SetAdditionalSchemaInfoPath(resolvedSchemaInfoPath);
810
+ startupLog.LogIf('verbose', `RSU additionalSchemaInfo path: ${resolvedSchemaInfoPath}`);
793
811
  } else if (RuntimeSchemaManager.Instance.IsEnabled) {
794
812
  // U5 — boot-time assert: write path ≠ read path (see the PostgreSQL branch for detail).
795
813
  console.error(
796
814
  `[RSU_ADDITIONAL_SCHEMA_INFO_DIVERGENCE] Runtime Schema Update is ENABLED but mj.config.cjs has no ` +
797
- `'additionalSchemaInfo' setting — RSU writes soft PK/FK definitions to '${process.env.RSU_ADDITIONAL_SCHEMA_INFO_PATH ?? 'additionalSchemaInfo.json'}' ` +
815
+ `'additionalSchemaInfo' setting (and RSU_ADDITIONAL_SCHEMA_INFO_PATH is unset) — RSU writes soft PK/FK definitions to '${RuntimeSchemaManager.Instance.AdditionalSchemaInfoPath}' ` +
798
816
  `while CodeGen reads none, so resolved soft primary keys will be silently LOST at codegen time. ` +
799
817
  `Set 'additionalSchemaInfo' in mj.config.cjs to the same file so the write path and read path agree.`
800
818
  );
@@ -1030,6 +1048,7 @@ const setupComplete$ = new ReplaySubject(1);
1030
1048
  // config.ts loads and validates at module scope, so importing it there would pull full config
1031
1049
  // validation into every import chain that touches the resolver, unit tests included.
1032
1050
  ConfigureRecordDataBroadcast(configInfo.cacheSettings?.recordDataBroadcastEntities);
1051
+ CacheInvalidationBroadcastManager.Instance.Configure(configInfo.cacheSettings?.broadcast);
1033
1052
 
1034
1053
  // Global listener: broadcast CACHE_INVALIDATION to all browser clients whenever
1035
1054
  // ANY BaseEntity save/delete occurs on this server — regardless of whether it
@@ -1052,7 +1071,8 @@ const setupComplete$ = new ReplaySubject(1);
1052
1071
  // fires for server-internal saves too (agents, actions, orchestrator), which are exactly
1053
1072
  // the ones no browser session asked for.
1054
1073
  recordData:
1055
- beEvent.type === 'save' && MayBroadcastRecordData(entityName)
1074
+ beEvent.type === 'save' && MayBroadcastRecordData(entityName, beEvent.baseEntity.EntityInfo)
1075
+ // fls-guard: MayBroadcastRecordData refuses FLS entities and Record Changes
1056
1076
  ? JSON.stringify(beEvent.baseEntity.GetAll())
1057
1077
  : undefined,
1058
1078
  });
@@ -1103,7 +1123,6 @@ const setupComplete$ = new ReplaySubject(1);
1103
1123
  scalarsMap: [{ type: Date, scalar: GraphQLTimestamp }],
1104
1124
  emitSchemaFile: websiteRunFromPackage !== 1,
1105
1125
  pubSub,
1106
- globalMiddlewares: [variablesLoggingMiddleware],
1107
1126
  });
1108
1127
  const buildMs = performance.now() - tBuild;
1109
1128
 
@@ -1130,6 +1149,7 @@ const setupComplete$ = new ReplaySubject(1);
1130
1149
  auditResolversForUndecoratedArgs();
1131
1150
 
1132
1151
  const tTransform = performance.now();
1152
+ schema = ApplyRootVariablesLoggingMiddleware(schema);
1133
1153
  schema = requireSystemUserDirective.transformer(schema);
1134
1154
  schema = publicDirective.transformer(schema);
1135
1155
 
@@ -1530,11 +1550,29 @@ const setupComplete$ = new ReplaySubject(1);
1530
1550
  // ─── GraphQL middleware (contextFunction reads req.userPayload, no re-auth) ─────
1531
1551
  app.use(
1532
1552
  graphqlRootPath,
1533
- cors<cors.CorsRequest>(),
1553
+ cors<cors.CorsRequest>({
1554
+ exposedHeaders: ['X-MJ-Cache-Seq', 'X-MJ-Server-ID'],
1555
+ }),
1534
1556
  BodyParser.json({ limit: '50mb' }),
1535
1557
  // Express 5 leaves req.body as undefined for non-JSON or empty bodies;
1536
1558
  // Apollo Server's expressMiddleware requires req.body to be defined.
1537
- (req, _res, next) => { if (req.body === undefined) req.body = {}; next(); },
1559
+ // Also piggyback the latest monotonic cache invalidation sequence and server process ID
1560
+ // onto every GraphQL HTTP response so clients can detect and recover missed invalidations.
1561
+ (req, res, next) => {
1562
+ if (req.body === undefined) req.body = {};
1563
+ const broadcastProvider = CacheInvalidationBroadcastManager.Instance.Provider;
1564
+ res.setHeader('X-MJ-Cache-Seq', String(broadcastProvider.CurrentSequence));
1565
+ res.setHeader('X-MJ-Server-ID', broadcastProvider.ServerProcessID);
1566
+ const originalWriteHead = res.writeHead.bind(res);
1567
+ res.writeHead = ((...args: Parameters<typeof res.writeHead>) => {
1568
+ if (!res.headersSent) {
1569
+ res.setHeader('X-MJ-Cache-Seq', String(broadcastProvider.CurrentSequence));
1570
+ res.setHeader('X-MJ-Server-ID', broadcastProvider.ServerProcessID);
1571
+ }
1572
+ return originalWriteHead(...args);
1573
+ }) as typeof res.writeHead;
1574
+ next();
1575
+ },
1538
1576
  expressMiddleware(apolloServer, {
1539
1577
  context: contextFunction({
1540
1578
  setupComplete$,
@@ -1549,9 +1587,26 @@ const setupComplete$ = new ReplaySubject(1);
1549
1587
  app.use(mw);
1550
1588
  }
1551
1589
 
1590
+ const serverRole = ResolveServerRole(configInfo.serverRole ?? process.env.MJ_SERVER_ROLE);
1591
+ const runBackgroundWorkers = ShouldRunBackgroundWorkers(serverRole);
1592
+ if (!runBackgroundWorkers) {
1593
+ LogStatus(`[ServerRole] Running in '${serverRole}' mode — background worker services are disabled on this replica.`);
1594
+ }
1595
+
1596
+ // Close out run artifacts a dead process left open, BEFORE anything below can start a new run.
1597
+ //
1598
+ // A killed process (OOM killer, SIGKILL, host reboot) never writes its run's terminal result, so
1599
+ // the run reads as in-flight forever: the wizard spins, the active-operations panel shows a step
1600
+ // that will never advance, and no failure is reported anywhere. Sound only before the first new
1601
+ // run: artifacts are process-local, so anything still in flight as THIS process starts belongs
1602
+ // to a process that no longer exists. That is why it runs here, ahead of the scheduler, the
1603
+ // integration sync worker (its Start() polls immediately), the HTTP listener (a request can
1604
+ // start a discovery) and pending RSU work, and why it is awaited. It needs none of them.
1605
+ await SweepInterruptedRuns(undefined, msg => console.warn(msg));
1606
+
1552
1607
  // Initialize and start scheduled jobs service if enabled
1553
1608
  let scheduledJobsService: ScheduledJobsService | null = null;
1554
- if (configInfo.scheduledJobs?.enabled) {
1609
+ if (runBackgroundWorkers && configInfo.scheduledJobs?.enabled) {
1555
1610
  try {
1556
1611
  scheduledJobsService = new ScheduledJobsService(configInfo.scheduledJobs);
1557
1612
  await scheduledJobsService.Initialize();
@@ -1565,7 +1620,7 @@ const setupComplete$ = new ReplaySubject(1);
1565
1620
  // Initialize and start the integration sync worker if enabled (PR 1 item 8).
1566
1621
  // Off by default — deployments that don't opt in keep running syncs inline.
1567
1622
  let integrationSyncWorker: IntegrationSyncWorkerService | null = null;
1568
- if (configInfo.integrationSyncWorker?.enabled) {
1623
+ if (runBackgroundWorkers && configInfo.integrationSyncWorker?.enabled) {
1569
1624
  try {
1570
1625
  integrationSyncWorker = new IntegrationSyncWorkerService(configInfo.integrationSyncWorker);
1571
1626
  await integrationSyncWorker.Initialize();
@@ -1603,52 +1658,55 @@ const setupComplete$ = new ReplaySubject(1);
1603
1658
  }
1604
1659
  startupLog.PrintSummary();
1605
1660
 
1606
- // Process pending RSU work from pre-restart (entity maps, field maps, sync)
1607
- processRSUPendingWork().catch(err => console.warn(`RSU pending work processing failed: ${err}`));
1608
-
1609
- // Resume any integration syncs that were orphaned by the previous process restart
1610
1661
  const resumeUser = UserCache.Instance.GetSystemUser();
1611
- if (resumeUser) {
1612
- IntegrationEngine.Instance.ResumeOrphanedSyncs(resumeUser)
1613
- .catch(err => console.warn(`[IntegrationEngine] Orphaned sync resume failed: ${err}`));
1614
- }
1615
1662
 
1616
- // Force-fail any agent runs left 'Running' by a process that died (restart/crash/OOM) or whose
1617
- // terminal-state write never landed. Staleness-based, so it never touches runs another healthy
1618
- // instance is still heart-beating. The watchdog also self-registers for graceful-shutdown
1619
- // cancellation (via ShutdownRegistry) once it begins tracking this process's first live run.
1620
- if (resumeUser && Metadata.Provider instanceof DatabaseProviderBase) { // global-provider-ok: server startup recovery — one-shot orphaned-run sweep at boot
1621
- const sweepUser = resumeUser;
1622
- const sweepProvider = Metadata.Provider; // global-provider-ok: server startup recovery — one-shot orphaned-run sweep at boot
1623
- AgentRunWatchdog.SweepOrphanedRuns(sweepProvider, sweepUser)
1624
- .catch(err => console.warn(`[AgentRunWatchdog] Startup sweep failed: ${err}`));
1625
-
1626
- // The watchdog repairs the RUN; nothing repaired the conversation detail, which is the row the
1627
- // chat actually renders from (MJ #4222). A process that dies mid-run leaves a terminal run
1628
- // beside a message that still claims to be generating, and it spins forever for anyone who
1629
- // opens it. Runs at boot (closes restart orphans) and on a timer (closes mid-life orphans),
1630
- // mirroring the watchdog's own two-phase shape.
1631
- const reconcileOrphans = () =>
1632
- ReconcileOrphanedConversationDetails(sweepProvider, sweepUser)
1633
- .catch(err => console.warn(`[OrphanDetailReconciler] Pass failed: ${err}`));
1634
- void reconcileOrphans();
1635
- const orphanDetailTimer = setInterval(() => void reconcileOrphans(), ORPHAN_DETAIL_SWEEP_INTERVAL_MS);
1636
- ShutdownRegistry.Instance.Register({
1637
- ShutdownName: 'OrphanDetailReconciler',
1638
- Shutdown: () => { clearInterval(orphanDetailTimer); },
1639
- });
1640
- }
1663
+ if (runBackgroundWorkers) {
1664
+ // Process pending RSU work from pre-restart (entity maps, field maps, sync)
1665
+ processRSUPendingWork().catch(err => console.warn(`RSU pending work processing failed: ${err}`));
1641
1666
 
1642
- // Launch the AI Agent Session janitor: run own-host orphan recovery once at boot, then a periodic tick
1643
- // that closes stale and over-deadline sessions and rebuilds lost realtime recordings from their
1644
- // crash-recovery shards (storage reads and writes). Self-registers with ShutdownRegistry, so its timer
1645
- // is cleared by the gracefulShutdown drain below (no explicit Stop() wiring needed here).
1646
- if (resumeUser && Metadata.Provider instanceof DatabaseProviderBase) { // global-provider-ok: server startup recovery — boot-time session janitor uses the server's own provider
1647
- SessionJanitor.Instance.Start(Metadata.Provider, resumeUser) // global-provider-ok: server-owned background reconciler runs under the server's provider + system user
1648
- .catch(err => console.warn(`[SessionJanitor] Startup failed: ${err}`));
1649
- // The bridge engine's orphan reconcile likewise starts only now that this process holds its port, so it can't
1650
- // take the live bridges of another MJAPI still serving that port for its own orphans (#5310).
1651
- StartBridgeOrphanReconciliation(Metadata.Provider, resumeUser); // global-provider-ok: server-owned background reconciler runs under the server's provider + system user
1667
+ // Resume any integration syncs that were orphaned by the previous process restart
1668
+ if (resumeUser) {
1669
+ IntegrationEngine.Instance.ResumeOrphanedSyncs(resumeUser)
1670
+ .catch(err => console.warn(`[IntegrationEngine] Orphaned sync resume failed: ${err}`));
1671
+ }
1672
+
1673
+ // Force-fail any agent runs left 'Running' by a process that died (restart/crash/OOM) or whose
1674
+ // terminal-state write never landed. Staleness-based, so it never touches runs another healthy
1675
+ // instance is still heart-beating. The watchdog also self-registers for graceful-shutdown
1676
+ // cancellation (via ShutdownRegistry) once it begins tracking this process's first live run.
1677
+ if (resumeUser && Metadata.Provider instanceof DatabaseProviderBase) { // global-provider-ok: server startup recovery — one-shot orphaned-run sweep at boot
1678
+ const sweepUser = resumeUser;
1679
+ const sweepProvider = Metadata.Provider; // global-provider-ok: server startup recovery — one-shot orphaned-run sweep at boot
1680
+ AgentRunWatchdog.SweepOrphanedRuns(sweepProvider, sweepUser)
1681
+ .catch(err => console.warn(`[AgentRunWatchdog] Startup sweep failed: ${err}`));
1682
+
1683
+ // The watchdog repairs the RUN; nothing repaired the conversation detail, which is the row the
1684
+ // chat actually renders from (MJ #4222). A process that dies mid-run leaves a terminal run
1685
+ // beside a message that still claims to be generating, and it spins forever for anyone who
1686
+ // opens it. Runs at boot (closes restart orphans) and on a timer (closes mid-life orphans),
1687
+ // mirroring the watchdog's own two-phase shape.
1688
+ const reconcileOrphans = () =>
1689
+ ReconcileOrphanedConversationDetails(sweepProvider, sweepUser)
1690
+ .catch(err => console.warn(`[OrphanDetailReconciler] Pass failed: ${err}`));
1691
+ void reconcileOrphans();
1692
+ const orphanDetailTimer = setInterval(() => void reconcileOrphans(), ORPHAN_DETAIL_SWEEP_INTERVAL_MS);
1693
+ ShutdownRegistry.Instance.Register({
1694
+ ShutdownName: 'OrphanDetailReconciler',
1695
+ Shutdown: () => { clearInterval(orphanDetailTimer); },
1696
+ });
1697
+ }
1698
+
1699
+ // Launch the AI Agent Session janitor: run own-host orphan recovery once at boot, then a periodic tick
1700
+ // that closes stale and over-deadline sessions and rebuilds lost realtime recordings from their
1701
+ // crash-recovery shards (storage reads and writes). Self-registers with ShutdownRegistry, so its timer
1702
+ // is cleared by the gracefulShutdown drain below (no explicit Stop() wiring needed here).
1703
+ if (resumeUser && Metadata.Provider instanceof DatabaseProviderBase) { // global-provider-ok: server startup recovery — boot-time session janitor uses the server's own provider
1704
+ SessionJanitor.Instance.Start(Metadata.Provider, resumeUser) // global-provider-ok: server-owned background reconciler runs under the server's provider + system user
1705
+ .catch(err => console.warn(`[SessionJanitor] Startup failed: ${err}`));
1706
+ // The bridge engine's orphan reconcile likewise starts only now that this process holds its port, so it can't
1707
+ // take the live bridges of another MJAPI still serving that port for its own orphans (#5310).
1708
+ StartBridgeOrphanReconciliation(Metadata.Provider, resumeUser); // global-provider-ok: server-owned background reconciler runs under the server's provider + system user
1709
+ }
1652
1710
  }
1653
1711
 
1654
1712
  // Launch the durable task-graph dispatcher: claim Pending tasks, execute them with a fresh
@@ -1677,7 +1735,7 @@ const setupComplete$ = new ReplaySubject(1);
1677
1735
  // StartTaskGraphDispatcher); this gives the agent seam the same.
1678
1736
  SuppressTaskGraphSubmission('MJ_DISABLE_TASK_GRAPH_DISPATCHER=1 is set on this host');
1679
1737
  LogStatus('[TaskGraphDispatcher] Disabled by MJ_DISABLE_TASK_GRAPH_DISPATCHER=1 — this process will neither accept nor execute task graphs.');
1680
- } else if (resumeUser && taskGraphPool instanceof sql.ConnectionPool) {
1738
+ } else if (runBackgroundWorkers && resumeUser && taskGraphPool instanceof sql.ConnectionPool) {
1681
1739
  StartTaskGraphDispatcher(taskGraphPool, resumeUser)
1682
1740
  .catch(err => console.warn(`[TaskGraphDispatcher] Startup failed: ${err}`));
1683
1741
  }
@@ -1687,7 +1745,7 @@ const setupComplete$ = new ReplaySubject(1);
1687
1745
  // ShutdownAll() drains it (up to 2 × shutdownDrainMs) before the HTTP server closes.
1688
1746
  // Not awaited: a slow engine load must not delay readiness, and a failure never stops the API.
1689
1747
  const workQueueProvider = Metadata.Provider; // global-provider-ok: server startup — the work-queue host runs on the server's own provider
1690
- if (configInfo.workQueue?.enabled && workQueueProvider instanceof DatabaseProviderBase) {
1748
+ if (runBackgroundWorkers && configInfo.workQueue?.enabled && workQueueProvider instanceof DatabaseProviderBase) {
1691
1749
  const workQueuePool = dataSources[0]?.dataSource;
1692
1750
  const providerSource = new MJServerWorkQueueProviderSource(workQueuePool instanceof sql.ConnectionPool ? workQueuePool : null, workQueueProvider);
1693
1751
  StartWorkQueueHost(configInfo.workQueue, workQueueProvider, providerSource)
@@ -2193,9 +2251,10 @@ function mountRealtimeRecordingRoutes(app: Application): boolean {
2193
2251
  }
2194
2252
  const graceMs = (): number => configInfo.realtime.recordingAssemblyGraceSeconds * 1000;
2195
2253
  const systemUser = UserCache.Instance.GetSystemUser();
2196
- if (systemUser) {
2254
+ const runWorkers = ShouldRunBackgroundWorkers(ResolveServerRole(configInfo.serverRole ?? process.env.MJ_SERVER_ROLE));
2255
+ if (systemUser && runWorkers) {
2197
2256
  RecordingAssemblyScheduler.Instance.Start({ Provider: provider, SystemUser: systemUser, GraceMs: graceMs });
2198
- } else {
2257
+ } else if (!systemUser) {
2199
2258
  LogError('[Realtime] Recording assembly scheduler not started: no system user; the session janitor assembles recordings instead');
2200
2259
  }
2201
2260
  app.use(ApiRoute(REALTIME_RECORDING_MOUNT_PATH), cors<cors.CorsRequest>(), CreateRealtimeRecordingRouter({
@@ -2204,7 +2263,7 @@ function mountRealtimeRecordingRoutes(app: Application): boolean {
2204
2263
  return user ? { ContextUser: user, Provider: provider } : null;
2205
2264
  },
2206
2265
  LoadOwnedSession: LoadOwnedRealtimeSession,
2207
- LoadConversationExternalID: LoadSessionConversationExternalID,
2266
+ LoadConversationScope: LoadSessionConversationScope,
2208
2267
  CreateLedger: (ledgerProvider, contextUser) => new SqlRealtimeRecordingLedger(ledgerProvider, contextUser),
2209
2268
  Scheduler: RecordingAssemblyScheduler.Instance,
2210
2269
  GraceMs: graceMs,
@@ -300,7 +300,12 @@ export class IntegrationCustomColumnPromoter {
300
300
  // step with the database.
301
301
  let batchResults: RSUPipelineResult[] = [];
302
302
  if (batchInputs.length > 0) {
303
- const batch = await RuntimeSchemaManager.Instance.RunPipelineBatch(batchInputs);
303
+ // Retrying variant. This runs UNATTENDED, mid-sync, with no operator watching: a
304
+ // transient ExecuteMigration / RunCodeGen / CompileTypeScript / RestartMJAPI failure
305
+ // drops the whole promotion, and the columns simply stay in overflow until something
306
+ // triggers a promotion again. The wrapper replays only those four steps and only while
307
+ // nothing in the batch was applied, so a partially-applied batch is still returned as-is.
308
+ const batch = await RuntimeSchemaManager.Instance.RunPipelineBatchWithRetry(batchInputs);
304
309
  batchResults = batch.Results ?? [];
305
310
  // M3: make the freshly-created EntityFields + regenerated sprocs visible in-process
306
311
  // ONCE for the whole batch. CRITICAL: the spread below builds sproc calls from this
@@ -1,7 +1,7 @@
1
- import type { MiddlewareFn } from 'type-graphql';
1
+ import type { MiddlewareFn, ResolverData } from 'type-graphql';
2
2
  import { getMetadataStorage } from 'type-graphql';
3
- import { getNamedType } from 'graphql';
4
- import type { GraphQLArgument } from 'graphql';
3
+ import { defaultFieldResolver, getNamedType } from 'graphql';
4
+ import type { GraphQLArgument, GraphQLField, GraphQLObjectType, GraphQLSchema } from 'graphql';
5
5
  import { configInfo } from '../config.js';
6
6
  import type { AppContext } from '../types.js';
7
7
  import { RedactArg } from './secretRedactor.js';
@@ -191,6 +191,62 @@ export const VariablesLoggingMiddleware: MiddlewareFn<AppContext> = async (actio
191
191
  /** @deprecated Use {@link VariablesLoggingMiddleware}. */
192
192
  export const variablesLoggingMiddleware: MiddlewareFn<AppContext> = VariablesLoggingMiddleware;
193
193
 
194
+ /**
195
+ * Attaches {@link VariablesLoggingMiddleware} ONLY to root `Query`, `Mutation`, and `Subscription`
196
+ * fields on the compiled GraphQLSchema instead of registering it via TypeGraphQL `globalMiddlewares`.
197
+ *
198
+ * Registering a global middleware in TypeGraphQL wraps every scalar and child field resolver across
199
+ * all 300+ `@ObjectType` entities in an async middleware chain, allocating $O(\text{Rows} \times \text{Fields})$
200
+ * Promises per query even when `logVariables` is false or `info.path.prev !== undefined`.
201
+ * Wrapping only the root operation fields reduces middleware invocations from ~15,000 per 500-row
202
+ * `RunView` response down to 1.
203
+ */
204
+ export function ApplyRootVariablesLoggingMiddleware(
205
+ schema: GraphQLSchema,
206
+ middleware: MiddlewareFn<AppContext> = VariablesLoggingMiddleware
207
+ ): GraphQLSchema {
208
+ const rootTypes: Array<GraphQLObjectType | null | undefined> = [
209
+ schema.getQueryType(),
210
+ schema.getMutationType(),
211
+ schema.getSubscriptionType(),
212
+ ];
213
+
214
+ for (const rootType of rootTypes) {
215
+ if (!rootType) continue;
216
+ const fields = rootType.getFields();
217
+ for (const field of Object.values(fields) as Array<GraphQLField<unknown, AppContext>>) {
218
+ if (field.resolve) {
219
+ const originalResolve = field.resolve;
220
+ field.resolve = (root, args, context, info) => {
221
+ const action: ResolverData<AppContext> = {
222
+ root,
223
+ args: args as Record<string, unknown>,
224
+ context,
225
+ info,
226
+ };
227
+ return middleware(action, async () => originalResolve(root, args, context, info));
228
+ };
229
+ }
230
+ if (field.subscribe) {
231
+ const originalSubscribe = field.subscribe;
232
+ field.subscribe = (root, args, context, info) => {
233
+ const action: ResolverData<AppContext> = {
234
+ root,
235
+ args: args as Record<string, unknown>,
236
+ context,
237
+ info,
238
+ };
239
+ return middleware(action, async () =>
240
+ originalSubscribe ? originalSubscribe(root, args, context, info) : defaultFieldResolver(root, args, context, info)
241
+ );
242
+ };
243
+ }
244
+ }
245
+ }
246
+
247
+ return schema;
248
+ }
249
+
194
250
  /**
195
251
  * Heuristic: an arg is "custom" (i.e. needs `@NoLog` discipline because metadata can't redact it)
196
252
  * when its GraphQL input type is not a known `Create<X>Input` / `Update<X>Input` for an entity
@@ -64,9 +64,13 @@ The principal comes from the **server-authenticated** context (`userPayload.user
64
64
  argument. An id argument only selects *which of the caller's own sessions*.
65
65
 
66
66
  A caller may use a session when `AIAgentSession.UserID` equals their user (`UUIDsEqual`). Widget guests all
67
- share one Anonymous user, so for an anonymous/magic-link principal that is **not enough**: the guest's
68
- signed per-session scope (`MagicLinkScope.ResourceID`) must equal the session's conversation `ExternalID`.
69
- An anonymous principal with no scope is refused outright.
67
+ share one Anonymous user, so for an anonymous/magic-link principal that is **not enough**: the session's
68
+ conversation `ExternalID` must equal the principal's scope identity (`ResolveSessionScopeIdentity`): the
69
+ signed `MagicLinkScope.ResourceID` when the token carries one, else the anonymous visitor's `mj_sid`.
70
+ `SessionManager` stamps that same value when it creates the conversation. An anonymous principal with
71
+ neither is refused outright. A *named* user with a resource scope must match too, which keeps one person's
72
+ different links apart; but it may also use its own session on a conversation that carries no stamp (one
73
+ started from the Conversations app) when its own row-level security let it read that conversation.
70
74
 
71
75
  - Refusals are uniform ("session not found") whether the session is missing or simply not yours, so ids
72
76
  cannot be probed.
@@ -54,6 +54,7 @@ function toSnapshot(loaded: LoadedSessionForAccess): SessionSnapshot {
54
54
  AgentSessionID: Session.ID,
55
55
  OwnerUserID: Session.UserID,
56
56
  ConversationExternalID: loaded.ConversationExternalID,
57
+ ConversationReadable: loaded.ConversationReadable,
57
58
  Status: Session.Status,
58
59
  ConfigRaw: Session.Config_,
59
60
  // `__mj_CreatedAt` is the row's creation instant — i.e. the session start. It is read-only and
@@ -8,6 +8,7 @@
8
8
  import { IMetadataProvider, LogError, UserInfo } from '@memberjunction/core';
9
9
  import { UUIDsEqual } from '@memberjunction/global';
10
10
  import type { MJAIAgentSessionEntity } from '@memberjunction/core-entities';
11
+ import { AuthorizeSessionAccess, LoadSessionConversationScope } from './sessionAccess.js';
11
12
 
12
13
  const SESSION_ENTITY = 'MJ: AI Agent Sessions';
13
14
 
@@ -19,7 +20,7 @@ export class RealtimeSessionNotFoundError extends Error {
19
20
  }
20
21
  }
21
22
 
22
- /** The session exists but belongs to another user. */
23
+ /** The session exists but the caller may not use it: another user owns it, or (for a scoped or anonymous caller) its conversation carries another scope identity. */
23
24
  export class RealtimeSessionOwnershipError extends Error {
24
25
  constructor(public readonly AgentSessionID: string) {
25
26
  super('Not authorized: you do not own this realtime session');
@@ -28,13 +29,15 @@ export class RealtimeSessionOwnershipError extends Error {
28
29
  }
29
30
 
30
31
  /**
31
- * Loads a session under the caller's identity and enforces ownership (`UserID === contextUser.ID`). It
32
- * deliberately does NOT reject `Closed` sessions: the final channel-state flush and the recording terminal
33
- * signal legitimately arrive after the session closed. A scoped-anonymous widget guest owns the session it
34
- * minted, so the same check covers it; loading as the caller also applies the entity's row-level security.
32
+ * Loads a session under the caller's identity and enforces ownership: `UserID === contextUser.ID`, then the
33
+ * scope rule of {@link AuthorizeSessionAccess} (every anonymous visitor shares one Anonymous user, so the
34
+ * session's conversation `ExternalID` must equal the caller's scope identity). It deliberately does NOT
35
+ * reject `Closed` sessions: the final channel-state flush and the recording terminal signal legitimately
36
+ * arrive after the session closed. Loading as the caller also applies the entity's row-level security.
35
37
  *
36
38
  * @throws RealtimeSessionNotFoundError when the row does not load
37
- * @throws RealtimeSessionOwnershipError when the caller is not the owner (logged with both user ids)
39
+ * @throws RealtimeSessionOwnershipError when another user owns the session (logged with both user ids) or
40
+ * the scope rule refuses the caller (logged with the refusal reason)
38
41
  */
39
42
  export async function LoadOwnedRealtimeSession(
40
43
  agentSessionId: string,
@@ -51,5 +54,16 @@ export async function LoadOwnedRealtimeSession(
51
54
  );
52
55
  throw new RealtimeSessionOwnershipError(agentSessionId);
53
56
  }
57
+ const conversation = await LoadSessionConversationScope(session, contextUser, provider);
58
+ const access = AuthorizeSessionAccess(
59
+ { SessionUserID: session.UserID, ConversationExternalID: conversation.ExternalID, ConversationReadable: conversation.Readable },
60
+ contextUser,
61
+ );
62
+ if (!access.Allowed) {
63
+ LogError(
64
+ `LoadOwnedRealtimeSession: session access refused (${access.Reason}) — user ${contextUser.ID} attempted to operate on session ${agentSessionId}`,
65
+ );
66
+ throw new RealtimeSessionOwnershipError(agentSessionId);
67
+ }
54
68
  return session;
55
69
  }