@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
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import dotenv from 'dotenv';
2
2
  dotenv.config({ quiet: true });
3
3
  import { expressMiddleware } from '@as-integrations/express5';
4
4
  import { mergeSchemas } from '@graphql-tools/schema';
5
- import { Metadata, SetProvider, StartupManager as StartupManagerImport, BaseEntity, RunView, DatabaseProviderBase, ResolveStartupMode } from '@memberjunction/core';
5
+ import { Metadata, SetProvider, StartupManager as StartupManagerImport, BaseEntity, RunView, DatabaseProviderBase, ProviderBase, ResolveStartupMode } from '@memberjunction/core';
6
6
  import { UserCache, resolveDbPlatformFromEnv } from '@memberjunction/generic-database-provider';
7
7
  import { MJGlobal, MJEventType, ShutdownRegistry } from '@memberjunction/global';
8
8
  import { setupSQLServerClient, SQLServerDataProvider, SQLServerProviderConfigData } from '@memberjunction/sqlserver-dataprovider';
@@ -17,9 +17,10 @@ import { default as fg } from 'fast-glob';
17
17
  import { useServer } from 'graphql-ws/lib/use/ws';
18
18
  import { createServer } from 'node:http';
19
19
  import { fileURLToPath } from 'node:url';
20
- import { readFileSync, writeFileSync } from 'node:fs';
20
+ import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
21
+ import { tmpdir } from 'node:os';
21
22
  import { Session as InspectorSession } from 'node:inspector';
22
- import { sep } from 'node:path';
23
+ import { join, sep } from 'node:path';
23
24
  import 'reflect-metadata';
24
25
  import { ReplaySubject } from 'rxjs';
25
26
  import { buildSchemaSync, getMetadataStorage, GraphQLTimestamp } from 'type-graphql';
@@ -29,8 +30,10 @@ import { WebSocketServer } from 'ws';
29
30
  import { RealtimeProxyServer } from './realtimeProxy/RealtimeProxyServer.js';
30
31
  import buildApolloServer from './apolloServer/index.js';
31
32
  import { configInfo, configFilePath, dbDatabase, dbHost, dbPort, dbUsername, graphqlPort, graphqlRootPath, mj_core_schema, websiteRunFromPackage } from './config.js';
33
+ import { TranslateBracketsToPG } from './postgresqlCompat.js';
34
+ import { BuildPostgreSQLConnectionConfig, DescribeReadOnlyLoginOverreach, PostgreSQLReadOnlyPool, ResolvePostgreSQLEndpoint, ResolvePostgreSQLReadOnlyCredentials, ToPGPoolConfig } from './postgresqlPoolSettings.js';
32
35
  import { default as jwt } from 'jsonwebtoken';
33
- import { contextFunction, CreateUnifiedAuthMiddleware, getUserPayload } from './context.js';
36
+ import { contextFunction, CreateReadOnlyProvider, CreateUnifiedAuthMiddleware, getUserPayload } from './context.js';
34
37
  import { requireSystemUserDirective, publicDirective } from './directives/index.js';
35
38
  import { variablesLoggingMiddleware } from './logging/variablesLoggingMiddleware.js';
36
39
  import { auditResolversForUndecoratedArgs } from './logging/bootAudit.js';
@@ -45,6 +48,7 @@ import { createRealtimeSdpBrokerRouter } from './rest/RealtimeSdpBrokerHandler.j
45
48
  import { REALTIME_SDP_EXCHANGE_PATH } from '@memberjunction/ai';
46
49
  import { createMagicLinkHandler, createMagicLinkJwksRouter, registerMagicLinkAuthProvider, MAGIC_LINK_MOUNT_PATH } from './auth/magicLink/index.js';
47
50
  import { createWidgetHandler, WIDGET_MOUNT_PATH } from './realtimeWidget/index.js';
51
+ import { CreateRealtimeVerifyRouter, REALTIME_VERIFY_MOUNT_PATH, RealtimeSessionEventService, RealtimeSessionVerificationService, ParseReplicatedRealtimeSessionEvent, } from './realtimeSessions/index.js';
48
52
  import { resolve } from 'node:path';
49
53
  import { DataSourceInfo, raiseEvent } from './types.js';
50
54
  import { ScheduledJobsService } from './services/ScheduledJobsService.js';
@@ -53,16 +57,20 @@ import { LocalCacheManager, TelemetryManager, LogStatus, LogError, SetVerboseLog
53
57
  import { validateAuthProvidersRegistered } from './auth/index.js';
54
58
  import { createAuthProviderCatalogRouter, AUTH_CATALOG_MOUNT_PATH } from './auth/AuthProviderCatalogRouter.js';
55
59
  import { GetAPIKeyEngine } from '@memberjunction/api-keys';
56
- import { RedisLocalStorageProvider } from '@memberjunction/redis-provider';
60
+ import { ActionEngineServer } from '@memberjunction/actions';
61
+ import { CreateAdhocSQLAuthorizer } from './resolvers/AdhocQueryResolver.js';
62
+ import { CacheManagerConfigFromSettings, CreateSharedCacheFromEnvironment, StartEngineSweeper, StartMetadataSweep, StartUserCacheChecks, WarmupLeaseMsFromSettings, WirePushStatusFanOut, WireSharedCacheEvents } from './sharedCache.js';
57
63
  import { PubSubManager } from './generic/PubSubManager.js';
58
64
  import { ReconcileOrphanedConversationDetails } from './generic/OrphanedConversationDetailReconciler.js';
59
- import { PUSH_STATUS_UPDATES_TOPIC, SetPushStatusPublishHook, ParseReplicatedStatusUpdate, } from './generic/PushStatusResolver.js';
65
+ import { HANDOFF_OFFER_FANOUT_CHANNEL, HANDOFF_OFFER_TOPIC, SetHandoffOfferPublishHook, ParseReplicatedHandoffOfferUpdate, } from './resolvers/HumanHandoffResolver.js';
66
+ import { RoomHandoffEngine } from '@memberjunction/livekit-room-server';
60
67
  import { IntegrationProgressEmitter } from '@memberjunction/integration-progress-artifacts';
61
68
  import { PublishIntegrationProgress } from './resolvers/IntegrationProgressResolver.js';
62
69
  import { RegisterRSUProgressBridge } from './integration/RSUProgressBridge.js';
63
70
  import { ClientToolRequestManager, AgentRunWatchdog } from '@memberjunction/ai-agents';
64
- import { SessionJanitor } from './agentSessions/index.js';
71
+ import { BindBridgeEngineHostInstance, SessionJanitor, SetHostInstancePort, StartBridgeOrphanReconciliation } from './agentSessions/index.js';
65
72
  import { StartTaskGraphDispatcher } from './services/StartTaskGraphDispatcher.js';
73
+ import { MJServerWorkQueueProviderSource, StartWorkQueueHost } from './services/WorkQueueHostService.js';
66
74
  import { GetAttachmentService } from '@memberjunction/aiengine';
67
75
  import { MJStorageBlobStore } from './services/MJStorageBlobStore.js';
68
76
  import { CACHE_INVALIDATION_TOPIC, MayBroadcastRecordData, ConfigureRecordDataBroadcast } from './generic/CacheInvalidationResolver.js';
@@ -100,6 +108,7 @@ export * from 'type-graphql';
100
108
  // later importers, which makes schema build fail on ViewResult.RowCount.
101
109
  export { Int, Float, ID } from 'type-graphql';
102
110
  export { NewUserBase } from './auth/newUsers.js';
111
+ export { CreateIsolatedProvider } from './isolatedProvider.js';
103
112
  export { configInfo, DEFAULT_SERVER_CONFIG } from './config.js';
104
113
  export { ServerExtensionLoader, BaseServerExtension, DefaultServerExtensionServiceRegistry } from '@memberjunction/server-extensions-core';
105
114
  export * from './directives/index.js';
@@ -146,6 +155,8 @@ export * from './resolvers/GenerateSeedTaxonomyResolver.js';
146
155
  export * from './resolvers/PipelineProgressResolver.js';
147
156
  export * from './resolvers/IntegrationProgressResolver.js';
148
157
  export * from './resolvers/IdentityClaimRedemptionResolver.js';
158
+ export * from './resolvers/UserAvatarResolver.js';
159
+ export * from './resolvers/avatarInputValidation.js';
149
160
  export * from './resolvers/ClientToolRequestResolver.js';
150
161
  export * from './resolvers/AutotagPipelineResolver.js';
151
162
  export * from './resolvers/TagGovernanceResolver.js';
@@ -201,6 +212,10 @@ export * from './resolvers/CurrentUserContextResolver.js';
201
212
  export * from './resolvers/RSUResolver.js';
202
213
  export * from './resolvers/AgentSessionResolver.js';
203
214
  export * from './resolvers/RealtimeClientSessionResolver.js';
215
+ export * from './resolvers/RealtimeSessionEventsResolver.js';
216
+ export * from './resolvers/RealtimeSessionVerificationResolver.js';
217
+ export * from './realtimeSessions/index.js';
218
+ export * from './resolvers/MeetingResolver.js';
204
219
  export * from './resolvers/RemoteBrowserActionResolver.js';
205
220
  export * from './agentSessions/index.js';
206
221
  export { GetReadOnlyDataSource, GetReadWriteDataSource, GetReadWriteProvider, GetReadOnlyProvider } from './util.js';
@@ -234,60 +249,87 @@ function resolveServerVersion() {
234
249
  const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
235
250
  return pkg.version;
236
251
  }
237
- catch {
252
+ catch (err) {
253
+ LogError('Failed to resolve server version from package.json', undefined, err);
238
254
  return undefined;
239
255
  }
240
256
  }
241
257
  /** How often to re-check for conversation details left behind by finished runs. */
242
258
  const ORPHAN_DETAIL_SWEEP_INTERVAL_MS = 5 * 60_000;
243
- /** Redis channel carrying replicated push-status updates between server instances. */
244
- const PUSH_STATUS_FANOUT_CHANNEL = 'push-status-updates';
245
259
  /**
246
- * Replicate push-status updates across server instances over Redis (MJ #4222).
247
- *
248
- * Outbound: every locally-published update is forwarded on a shared channel. Inbound: a message
249
- * from another instance is republished onto THIS instance's local topic, where the normal
250
- * subscription filter decides who receives it — so the identity gate (`ownerUserId` vs. the
251
- * connection's authenticated user) still applies to a replicated message exactly as it does to a
252
- * local one. The replica has no say in who sees what.
260
+ * Replicate handoff-offer updates across server instances over Redis.
253
261
  *
254
- * Republishing goes straight to `PubSubManager`, never back through `publishStatusUpdate`, so an
255
- * inbound message cannot be re-broadcast and loop. `SourceServerId` guards the remaining case: a
256
- * publisher also receives its own message from Redis.
262
+ * Outbound: every locally-published offer change is forwarded on a shared Redis channel.
263
+ * Inbound: a message from another instance is republished onto THIS instance's local GraphQL
264
+ * topic (where the subscription filter scopes to the target user) and delivered to RoomHandoffEngine
265
+ * so the call-hosting instance can transition its local flow (e.g. wait for the user to join the room).
257
266
  */
258
- async function wirePushStatusFanOut(redisProvider, startupLog) {
267
+ async function wireHandoffOfferFanOut(redisProvider, startupLog) {
259
268
  try {
260
- await redisProvider.SubscribeToChannel(PUSH_STATUS_FANOUT_CHANNEL, (raw) => {
269
+ await redisProvider.SubscribeToChannel(HANDOFF_OFFER_FANOUT_CHANNEL, (raw) => {
261
270
  try {
262
- const payload = ParseReplicatedStatusUpdate(raw, MJGlobal.Instance.ProcessUUID);
271
+ const payload = ParseReplicatedHandoffOfferUpdate(raw, MJGlobal.Instance.ProcessUUID);
263
272
  if (!payload) {
264
273
  return;
265
274
  }
266
- // Rebuilt as a plain record: the topic's publish signature takes an index-signature type,
267
- // and listing the fields keeps the wire shape explicit at the one place it crosses hosts.
268
- PubSubManager.Instance.Publish(PUSH_STATUS_UPDATES_TOPIC, {
269
- sessionId: payload.sessionId,
270
- ownerUserId: payload.ownerUserId,
271
- message: payload.message,
272
- SourceServerId: payload.SourceServerId,
275
+ PubSubManager.Instance.Publish(HANDOFF_OFFER_TOPIC, {
276
+ UserID: payload.UserID,
277
+ Kind: payload.Kind,
278
+ Offer: payload.Offer,
273
279
  });
280
+ RoomHandoffEngine.Instance.OnRemoteOfferChange(payload);
274
281
  }
275
- catch {
282
+ catch (err) {
276
283
  // A malformed message on a shared channel must not take down the subscriber.
284
+ LogError('Error processing handoff-offer fan-out message', undefined, err);
277
285
  }
278
286
  });
279
- SetPushStatusPublishHook((payload) => {
280
- redisProvider.PublishMessage(PUSH_STATUS_FANOUT_CHANNEL, JSON.stringify(payload));
287
+ SetHandoffOfferPublishHook((payload) => {
288
+ redisProvider.PublishMessage(HANDOFF_OFFER_FANOUT_CHANNEL, JSON.stringify(payload));
281
289
  });
282
- // Printed unconditionally, not verbose-gated. "Is fan-out actually on?" is the first question
283
- // anyone debugging a hung conversation behind a load balancer asks, and a silent default left
284
- // no way to answer it.
285
- console.log('[MJAPI] Push-status updates: cross-instance fan-out enabled via Redis');
290
+ console.log('[MJAPI] Handoff-offer updates: cross-instance fan-out enabled via Redis');
291
+ startupLog.LogIf('verbose', 'Handoff-offer updates: cross-instance fan-out enabled via Redis');
286
292
  }
287
293
  catch (err) {
288
- // Single-instance delivery still works, and the durable tail query covers the rest. Degraded,
289
- // not broken — so this must not stop the server from starting.
290
- console.warn(`Push-status fan-out unavailable: ${err.message}`);
294
+ console.warn(`Handoff-offer fan-out unavailable: ${err instanceof Error ? err.message : String(err)}`);
295
+ }
296
+ }
297
+ /** Redis channel carrying replicated realtime session events between server instances. */
298
+ const REALTIME_SESSION_EVENTS_FANOUT_CHANNEL = 'realtime-session-events';
299
+ /**
300
+ * Replicate realtime session events across server instances over Redis.
301
+ *
302
+ * Behind a load balancer the browser's WebSocket (the `RealtimeSessionEvents` subscription) lives on one
303
+ * replica while the request that raises the event — typically the public verify-link redemption — can be
304
+ * handled by another. The session-events topic is an in-process PubSub, so without this an
305
+ * `identity.verified` published on replica B never reaches the subscriber on replica A.
306
+ *
307
+ * Inbound messages are republished onto THIS instance's local topic only (never back through the
308
+ * replication hook, so there is no loop) and are still judged by the subscription filter against the
309
+ * connection's identity — a replica has no say in who sees what. The durable
310
+ * `RealtimeSessionVerificationStatus` query remains the backstop if Redis is down.
311
+ */
312
+ async function wireRealtimeSessionEventFanOut(redisProvider) {
313
+ try {
314
+ await redisProvider.SubscribeToChannel(REALTIME_SESSION_EVENTS_FANOUT_CHANNEL, (raw) => {
315
+ try {
316
+ const payload = ParseReplicatedRealtimeSessionEvent(raw, MJGlobal.Instance.ProcessUUID);
317
+ if (payload) {
318
+ RealtimeSessionEventService.Instance.PublishReplicated(payload);
319
+ }
320
+ }
321
+ catch (err) {
322
+ LogError(`Realtime session event fan-out: dropping a malformed replicated event: ${err.message}`);
323
+ }
324
+ });
325
+ RealtimeSessionEventService.Instance.SetReplicationHook((payload) => {
326
+ redisProvider.PublishMessage(REALTIME_SESSION_EVENTS_FANOUT_CHANNEL, JSON.stringify(payload));
327
+ });
328
+ console.log('[MJAPI] Realtime session events: cross-instance fan-out enabled via Redis');
329
+ }
330
+ catch (err) {
331
+ // Single-instance delivery still works and the status query covers the rest: degraded, not broken.
332
+ console.warn(`Realtime session event fan-out unavailable: ${err.message}`);
291
333
  }
292
334
  }
293
335
  // Bind MJStorage as the conversation-attachment blob store. The attachment service itself no longer
@@ -302,6 +344,12 @@ async function wirePushStatusFanOut(redisProvider, startupLog) {
302
344
  GetAttachmentService().BlobStore = new MJStorageBlobStore();
303
345
  export const Serve = async (resolverPaths, app = CreateApp(), options) => {
304
346
  const t0 = performance.now();
347
+ // Name this MJAPI instance by its port before anything stamps a session row with its identity, so the janitor's
348
+ // startup recovery closes only the sessions an earlier boot of this instance left, never those of another MJAPI on
349
+ // the same host (#5309).
350
+ SetHostInstancePort(graphqlPort);
351
+ // The realtime bridge engine stamps its bridge rows with the same identity and reconciles orphans by it (#5310).
352
+ BindBridgeEngineHostInstance();
305
353
  // Level-gated startup logger. Resolves verbosity from telemetry.level (single
306
354
  // operator knob). At `standard` (default), per-phase timings are collapsed into
307
355
  // the one-line summary; at `verbose`+ each phase prints inline as before.
@@ -325,33 +373,62 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
325
373
  const setupComplete$ = new ReplaySubject(1);
326
374
  const dbType = GetDbType();
327
375
  const dataSources = [];
376
+ // Shared cache first: the database provider loads metadata and startup engines through
377
+ // it, so the first server to start fills it and later servers read it instead of each querying
378
+ // the database and broadcasting what they loaded. Subscribed before engines load so they hear
379
+ // other servers while they start.
380
+ const sharedCache = CreateSharedCacheFromEnvironment(configInfo.cacheSettings);
381
+ if (sharedCache) {
382
+ await WireSharedCacheEvents(sharedCache, () => (Metadata.Provider instanceof ProviderBase ? Metadata.Provider : undefined)); // global-provider-ok: bootstrap
383
+ // The cross-instance fan-outs ride this connection. Push-status moved into sharedCache.ts
384
+ // with the rest of the Redis wiring; the handoff and realtime-session ones stay here deliberately —
385
+ // they reach into RoomHandoffEngine and RealtimeSessionEventService, and the cache module should not
386
+ // acquire a dependency on either.
387
+ await WirePushStatusFanOut(sharedCache);
388
+ await wireHandoffOfferFanOut(sharedCache, startupLog);
389
+ await wireRealtimeSessionEventFanOut(sharedCache);
390
+ startupLog.LogIf('verbose', `Redis cache provider connected: ${process.env.REDIS_URL}`);
391
+ }
392
+ const cacheManagerConfig = CacheManagerConfigFromSettings(configInfo.cacheSettings);
393
+ const warmupLeaseMs = WarmupLeaseMsFromSettings(configInfo.cacheSettings);
328
394
  if (dbType === 'postgresql') {
329
395
  // ─── PostgreSQL Path ───────────────────────────────────────────
330
396
  startupLog.BeginPhase('Connecting to database');
331
397
  startupLog.LogIf('verbose', 'Database type: PostgreSQL');
332
398
  const pg = await import('pg');
333
- const { PostgreSQLDataProvider, PostgreSQLProviderConfigData } = await import('@memberjunction/postgresql-dataprovider');
334
- const pgHost = process.env.PG_HOST || process.env.DB_HOST || 'localhost';
335
- const pgPort = parseInt(process.env.PG_PORT || process.env.DB_PORT || '5432', 10);
336
- const pgUser = process.env.PG_USERNAME || process.env.DB_USERNAME || 'postgres';
337
- const pgPass = process.env.PG_PASSWORD || process.env.DB_PASSWORD || '';
338
- const pgDatabase = process.env.PG_DATABASE || process.env.DB_DATABASE || '';
339
- const pgPool = new pg.default.Pool({
340
- host: pgHost,
341
- port: pgPort,
342
- user: pgUser,
343
- password: pgPass,
344
- database: pgDatabase,
345
- max: configInfo.databaseSettings.connectionPool?.max ?? 50,
346
- min: configInfo.databaseSettings.connectionPool?.min ?? 5,
347
- idleTimeoutMillis: configInfo.databaseSettings.connectionPool?.idleTimeoutMillis ?? 30000,
348
- connectionTimeoutMillis: configInfo.databaseSettings.connectionPool?.acquireTimeoutMillis ?? 30000,
349
- });
399
+ const { PostgreSQLDataProvider, PostgreSQLProviderConfigData, MJPostgresTypes } = await import('@memberjunction/postgresql-dataprovider');
400
+ const pgEndpoint = ResolvePostgreSQLEndpoint();
401
+ const { Host: pgHost, Port: pgPort, User: pgUser, Database: pgDatabase } = pgEndpoint;
402
+ // Every API pool carries the statement and idle-in-transaction timeouts from connection #1.
403
+ const pgConnectionConfig = BuildPostgreSQLConnectionConfig(pgEndpoint, configInfo.databaseSettings, 'api');
404
+ const pgPool = new pg.default.Pool(ToPGPoolConfig(pgConnectionConfig));
350
405
  // Verify connection
351
406
  const testClient = await pgPool.connect();
352
407
  await testClient.query('SELECT 1');
353
408
  testClient.release();
354
409
  startupLog.LogIf('verbose', `PostgreSQL pool connected to ${pgHost}:${pgPort}/${pgDatabase}`);
410
+ // A read-only pool opened with the read-only login, as SQL Server has. Read-only per-request
411
+ // providers (TestQuerySQL and other caller-supplied SQL) share it instead of the primary pool.
412
+ const pgReadOnlyCredentials = ResolvePostgreSQLReadOnlyCredentials(configInfo);
413
+ if (pgReadOnlyCredentials) {
414
+ // A pool a provider runs on must carry the provider's type parsers, so BIGINT and NUMERIC
415
+ // come back as numbers here as they do on the provider's own pool.
416
+ const readOnlyPgPool = new pg.default.Pool({
417
+ ...ToPGPoolConfig(BuildPostgreSQLConnectionConfig({ ...pgEndpoint, ...pgReadOnlyCredentials }, configInfo.databaseSettings, 'read-only')),
418
+ types: MJPostgresTypes,
419
+ });
420
+ const readOnlyTestClient = await readOnlyPgPool.connect();
421
+ try {
422
+ for (const warning of await DescribeReadOnlyLoginOverreach(readOnlyTestClient, mj_core_schema)) {
423
+ LogStatus(`WARNING: ${warning}`);
424
+ }
425
+ }
426
+ finally {
427
+ readOnlyTestClient.release();
428
+ }
429
+ PostgreSQLReadOnlyPool.Instance.Pool = readOnlyPgPool;
430
+ startupLog.LogIf('verbose', 'Read-only PostgreSQL pool has been initialized.');
431
+ }
355
432
  // Create a DataSourceInfo with a MSSQL-compatible wrapper around pg.Pool
356
433
  // This allows existing code (types, util, context) to work without changes
357
434
  const mssqlCompatPool = createMSSQLCompatPool(pgPool);
@@ -364,16 +441,8 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
364
441
  userName: pgUser,
365
442
  }));
366
443
  // Set up the PostgreSQL provider
367
- const pgConnectionConfig = {
368
- Host: pgHost,
369
- Port: pgPort,
370
- Database: pgDatabase,
371
- User: pgUser,
372
- Password: pgPass,
373
- MaxConnections: configInfo.databaseSettings.connectionPool?.max ?? 50,
374
- MinConnections: configInfo.databaseSettings.connectionPool?.min ?? 5,
375
- };
376
444
  const pgConfigData = new PostgreSQLProviderConfigData(pgConnectionConfig, mj_core_schema, cacheRefreshInterval / 1000);
445
+ pgConfigData.LocalStorageProvider = sharedCache ?? undefined;
377
446
  const provider = new PostgreSQLDataProvider();
378
447
  await provider.Config(pgConfigData);
379
448
  SetProvider(provider);
@@ -383,7 +452,7 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
383
452
  const sysUser = UserCache.Instance.GetSystemUser();
384
453
  const backupSysUser = UserCache.Instance.Users.find(u => u.IsActive && u.Type === 'Owner');
385
454
  const pgStartupMode = ResolveStartupMode({ configValue: configInfo.startup?.mode, defaultMode: 'full' });
386
- await StartupManagerImport.Instance.Startup(false, sysUser || backupSysUser, provider, { mode: pgStartupMode.mode });
455
+ await StartupManagerImport.Instance.Startup(false, sysUser || backupSysUser, provider, { mode: pgStartupMode.mode, cacheManagerConfig, warmupLeaseMs });
387
456
  // Both provider sources have now had their turn — config/env at module load, metadata via
388
457
  // AuthProviderEngine's startup hook — so "no providers at all" is finally a meaningful check.
389
458
  validateAuthProvidersRegistered();
@@ -396,7 +465,7 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
396
465
  if (poolAny._pgPool) {
397
466
  const thePgPool = poolAny._pgPool;
398
467
  // Translate SQL Server bracket syntax to PostgreSQL double-quote syntax
399
- const pgQuery = translateBracketsToPG(query);
468
+ const pgQuery = TranslateBracketsToPG(query);
400
469
  const result = await thePgPool.query(pgQuery);
401
470
  return result.rows;
402
471
  }
@@ -414,19 +483,15 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
414
483
  const pgCodegenPass = process.env.CODEGEN_DB_PASSWORD;
415
484
  if (pgCodegenUser && pgCodegenPass) {
416
485
  try {
417
- const codegenPgPool = new pg.default.Pool({
418
- host: pgHost,
419
- port: pgPort,
420
- user: pgCodegenUser,
421
- password: pgCodegenPass,
422
- database: pgDatabase,
423
- max: 10,
424
- });
486
+ // CodeGen and DDL work runs long statements across every entity, so this pool gets the
487
+ // long CodeGen timeout rather than the API one, as the SQL Server CodeGen pool does.
488
+ const codegenPgConnectionConfig = BuildPostgreSQLConnectionConfig({ ...pgEndpoint, User: pgCodegenUser, Password: pgCodegenPass }, configInfo.databaseSettings, 'codegen');
489
+ const codegenPgPool = new pg.default.Pool(ToPGPoolConfig(codegenPgConnectionConfig));
425
490
  const codegenTestClient = await codegenPgPool.connect();
426
491
  await codegenTestClient.query('SELECT 1');
427
492
  codegenTestClient.release();
428
493
  const { RuntimeSchemaManager } = await import('@memberjunction/schema-engine');
429
- const codegenPgConfigData = new PostgreSQLProviderConfigData({ Host: pgHost, Port: pgPort, Database: pgDatabase, User: pgCodegenUser, Password: pgCodegenPass }, mj_core_schema, cacheRefreshInterval / 1000);
494
+ const codegenPgConfigData = new PostgreSQLProviderConfigData(codegenPgConnectionConfig, mj_core_schema, cacheRefreshInterval / 1000);
430
495
  const codegenPgProvider = new PostgreSQLDataProvider();
431
496
  await codegenPgProvider.Config(codegenPgConfigData); // separate pool (per-instance manager); does NOT touch the global API provider
432
497
  RuntimeSchemaManager.Instance.SetDDLProvider(codegenPgProvider);
@@ -517,10 +582,11 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
517
582
  }
518
583
  // cacheRefreshInterval is configured in ms; checkRefreshIntervalSeconds declares seconds — see providerConfigUnits.ts
519
584
  const config = new SQLServerProviderConfigData(pool, mj_core_schema, MetadataCacheRefreshIntervalSeconds(cacheRefreshInterval));
585
+ config.LocalStorageProvider = sharedCache ?? undefined;
520
586
  // MJAPI is a long-running server, so entry-point default is 'full' engine pre-warm;
521
587
  // MJ_STARTUP_MODE / mj.config.cjs startup.mode can override per the shared precedence chain
522
588
  const startupMode = ResolveStartupMode({ configValue: configInfo.startup?.mode, defaultMode: 'full' });
523
- await setupSQLServerClient(config, { mode: startupMode.mode });
589
+ await setupSQLServerClient(config, { mode: startupMode.mode, cacheManagerConfig, warmupLeaseMs });
524
590
  // See the note on the PostgreSQL path above: this is the first point at which both the
525
591
  // config/env providers and the metadata catalog have been registered.
526
592
  validateAuthProvidersRegistered();
@@ -633,76 +699,28 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
633
699
  tm.SetEnabled(false);
634
700
  startupLog.LogIf('verbose', 'Server telemetry disabled');
635
701
  }
636
- // Optionally inject Redis as the shared storage provider for cross-server cache invalidation
637
- if (process.env.REDIS_URL) {
638
- const redisProvider = new RedisLocalStorageProvider({
639
- url: process.env.REDIS_URL,
640
- keyPrefix: process.env.REDIS_KEY_PREFIX || 'mj',
641
- enablePubSub: true,
642
- enableLogging: configInfo.cacheSettings?.verboseLogging ?? false,
643
- });
644
- Metadata.Provider.SetLocalStorageProvider(redisProvider); // global-provider-ok: bootstrap (Redis cache wiring)
645
- await redisProvider.StartListening();
646
- // Connect Redis pub/sub events to LocalCacheManager callback dispatch
647
- // so cross-server cache invalidation messages are routed to registered callbacks
648
- redisProvider.OnCacheChanged((event) => {
649
- const sourceShort = event.SourceServerId ? event.SourceServerId.substring(0, 8) : 'unknown';
650
- console.log(`[MJAPI] Redis pub/sub → DispatchCacheChange: ${event.Action} for "${event.CacheKey}" from server ${sourceShort}`);
651
- LocalCacheManager.Instance.DispatchCacheChange(event);
652
- // Also broadcast to connected browser clients via GraphQL subscription
653
- // Extract entity name from the cache key (format: EntityName|Filter|OrderBy|...)
654
- const entityName = event.CacheKey ? event.CacheKey.split('|')[0] : '';
655
- if (entityName) {
656
- PubSubManager.Instance.Publish(CACHE_INVALIDATION_TOPIC, {
657
- entityName,
658
- primaryKeyValues: null, // entity-level invalidation
659
- action: event.Action || 'save',
660
- sourceServerId: event.SourceServerId || 'unknown',
661
- timestamp: new Date(),
662
- });
663
- }
664
- });
665
- // Fan push-status updates across server instances (MJ #4222).
666
- //
667
- // Behind a load balancer the browser's WebSocket lives on one replica while the mutation that
668
- // drives the agent can be handled by another. The push topic is an in-process PubSub, so a
669
- // completion published on replica B never reaches a subscriber on replica A — the browser waits
670
- // forever for an event that was delivered to nobody. Replicating progress and completion over
671
- // Redis closes that, and the durable tail query remains the backstop if Redis is down.
672
- await wirePushStatusFanOut(redisProvider, startupLog);
673
- startupLog.LogIf('verbose', `Redis cache provider connected: ${process.env.REDIS_URL}`);
674
- }
675
- // If Redis is available, swap LocalCacheManager's storage provider to Redis.
676
- // LocalCacheManager may have already been initialized (with in-memory provider)
677
- // during engine loading. SetStorageProvider migrates cached data to Redis.
678
- if (process.env.REDIS_URL) {
679
- await LocalCacheManager.Instance.SetStorageProvider(Metadata.Provider.LocalStorageProvider); // global-provider-ok: bootstrap
680
- startupLog.LogIf('verbose', 'LocalCacheManager: storage provider swapped to Redis');
681
- }
682
- // Ensure LocalCacheManager is initialized (no-op if already done during engine loading)
683
- if (!LocalCacheManager.Instance.IsInitialized) {
684
- // Build cache config from mj.config.cjs cacheSettings
685
- const cs = configInfo.cacheSettings;
686
- const cacheConfig = {
687
- maxSizeBytes: (cs.maxMemoryMB ?? 150) * 1024 * 1024,
688
- maxPercentOfCachePerEntity: cs.maxPercentOfCachePerEntity ?? 50,
689
- defaultTTLMs: (cs.defaultTTLSeconds ?? 0) * 1000,
690
- evictionSweepIntervalMs: (cs.evictionSweepIntervalSeconds ?? 300) * 1000,
691
- verboseLogging: cs.verboseLogging ?? false,
692
- };
693
- await LocalCacheManager.Instance.Initialize(Metadata.Provider.LocalStorageProvider, cacheConfig); // global-provider-ok: bootstrap
694
- if (startupLog.IsAtLeast('verbose')) {
695
- // eslint-disable-next-line no-console
696
- console.log('LocalCacheManager initialized with cache config:', JSON.stringify({
697
- maxMemoryMB: cs.maxMemoryMB ?? 150,
698
- maxPercentOfCachePerEntity: cs.maxPercentOfCachePerEntity ?? 50,
699
- evictionSweepIntervalSeconds: cs.evictionSweepIntervalSeconds ?? 300,
700
- }));
701
- }
702
+ // The database provider already initialized LocalCacheManager from inside its own Config(), with
703
+ // no settings — this call is what makes `cacheSettings` take effect (Initialize merges a config
704
+ // handed to it later). Unconditional on purpose: the guard that used to sit here
705
+ // skipped exactly the case that needed it.
706
+ await LocalCacheManager.Instance.Initialize(Metadata.Provider.LocalStorageProvider, cacheManagerConfig); // global-provider-ok: bootstrap
707
+ if (sharedCache) {
708
+ await LocalCacheManager.Instance.SetStorageProvider(sharedCache);
702
709
  }
710
+ const engineSweepMs = StartEngineSweeper(configInfo.cacheSettings);
711
+ startupLog.LogIf('verbose', engineSweepMs > 0 ? `Engine/database sweep every ${engineSweepMs / 1000}s` : 'Engine/database sweep disabled');
712
+ const userCacheCheckMs = StartUserCacheChecks(configInfo.cacheSettings);
713
+ startupLog.LogIf('verbose', userCacheCheckMs > 0 ? `User cache staleness check every ${userCacheCheckMs / 1000}s` : 'User cache staleness check disabled');
714
+ // Costs nothing unless a metadata entity declares TrustServerCacheCompletely = false.
715
+ const metadataSweepMs = StartMetadataSweep(configInfo.cacheSettings, () => (Metadata.Provider instanceof ProviderBase ? Metadata.Provider : undefined)); // global-provider-ok: bootstrap (this process's one provider)
716
+ startupLog.LogIf('verbose', metadataSweepMs > 0 ? `Metadata/database sweep every ${metadataSweepMs / 1000}s (only for entities declaring drift)` : 'Metadata/database sweep disabled');
703
717
  // Initialize APIKeyEngine singleton — reads apiKeyGeneration from mj.config.cjs automatically
704
718
  // This must happen before any request handler calls GetAPIKeyEngine()
705
719
  GetAPIKeyEngine();
720
+ // Actions that run caller-supplied SQL (Run Ad-hoc Query) run only on the read-only login, with
721
+ // ExecuteAdhocQuery's table check and timeout limit.
722
+ ActionEngineServer.Instance.SetReadOnlyProviderFactory(() => CreateReadOnlyProvider(dataSources));
723
+ ActionEngineServer.Instance.SetAdhocSQLAuthorizer(CreateAdhocSQLAuthorizer(configInfo.databaseSettings.requestTimeout));
706
724
  setupComplete$.next(true);
707
725
  raiseEvent('setupComplete', dataSources, null, this);
708
726
  /******TEST HARNESS FOR CHANGE DETECTION */
@@ -912,7 +930,7 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
912
930
  const buildMs = performance.now() - tBuild;
913
931
  if (cpuProf && profSession) {
914
932
  const profile = await new Promise((res, rej) => profSession.post('Profiler.stop', (e, r) => (e ? rej(e) : res(r.profile))));
915
- const outPath = `/tmp/mjapi_schema_build_${process.pid}.cpuprofile`;
933
+ const outPath = join(mkdtempSync(join(tmpdir(), 'mjapi-schema-')), `mjapi_schema_build_${process.pid}.cpuprofile`);
916
934
  writeFileSync(outPath, JSON.stringify(profile));
917
935
  profSession.disconnect();
918
936
  // eslint-disable-next-line no-console
@@ -1159,6 +1177,18 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
1159
1177
  }
1160
1178
  startupLog.LogIf('verbose', `[Widget] Public routes registered at ${WIDGET_MOUNT_PATH}/session and ${WIDGET_MOUNT_PATH}/session/refresh`);
1161
1179
  }
1180
+ // ─── Realtime identity verification: PUBLIC verify-link routes (before auth mw) ───
1181
+ // The person opening the emailed link holds no MJ token — the single-use token is the capability.
1182
+ // GET only renders a confirm button; the POST redeems (mail scanners GET every link). Mounted only
1183
+ // when verification is enabled AND able to send mail AND able to protect its codes (see
1184
+ // realtime.identityVerification config: communicationProvider + hmacSecret).
1185
+ if (configInfo.realtime?.identityVerification?.enabled && !RealtimeSessionVerificationService.Instance.IsEnabled) {
1186
+ console.warn('[RealtimeVerify] realtime.identityVerification.enabled is true but verification is unavailable: it needs a communicationProvider and an hmacSecret of at least 16 characters.');
1187
+ }
1188
+ if (RealtimeSessionVerificationService.Instance.IsEnabled) {
1189
+ app.use(REALTIME_VERIFY_MOUNT_PATH, cors(), CreateRealtimeVerifyRouter());
1190
+ startupLog.LogIf('verbose', `[RealtimeVerify] Public routes registered at ${REALTIME_VERIFY_MOUNT_PATH}/:token (GET) and ${REALTIME_VERIFY_MOUNT_PATH} (POST)`);
1191
+ }
1162
1192
  // ─── Server extensions loader & shared service registry ───────────────────
1163
1193
  const extensionLoader = new ServerExtensionLoader();
1164
1194
  _currentExtensionLoader = extensionLoader;
@@ -1166,13 +1196,22 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
1166
1196
  // Backwards-compatibility shim: synthesize ServerExtensionConfig entries from legacy configInfo.telephony
1167
1197
  const telephonyExtensionConfigs = [];
1168
1198
  if (configInfo.telephony?.enabled) {
1199
+ // Settings every carrier shares (inbound run-as user, call cap, outbound policy). Each carrier's own block is
1200
+ // spread AFTER them, so a carrier can override one explicitly.
1201
+ const sharedTelephonySettings = {
1202
+ inboundRunAsUserEmail: configInfo.telephony.inboundRunAsUserEmail,
1203
+ maxCallSeconds: configInfo.telephony.maxCallSeconds,
1204
+ maxConcurrentCalls: configInfo.telephony.maxConcurrentCalls,
1205
+ transferTargets: configInfo.telephony.transferTargets,
1206
+ outbound: configInfo.telephony.outbound,
1207
+ };
1169
1208
  if (configInfo.telephony.twilio) {
1170
1209
  telephonyExtensionConfigs.push({
1171
1210
  Enabled: true,
1172
1211
  DriverClass: 'TwilioTelephonyExtension',
1173
1212
  RootPath: '/telephony/twilio',
1174
1213
  Phase: 'pre-auth',
1175
- Settings: configInfo.telephony.twilio,
1214
+ Settings: { ...sharedTelephonySettings, ...configInfo.telephony.twilio },
1176
1215
  });
1177
1216
  }
1178
1217
  if (configInfo.telephony.vonage) {
@@ -1181,7 +1220,7 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
1181
1220
  DriverClass: 'VonageTelephonyExtension',
1182
1221
  RootPath: '/telephony/vonage',
1183
1222
  Phase: 'pre-auth',
1184
- Settings: configInfo.telephony.vonage,
1223
+ Settings: { ...sharedTelephonySettings, ...configInfo.telephony.vonage },
1185
1224
  });
1186
1225
  }
1187
1226
  if (configInfo.telephony.ringcentral) {
@@ -1190,7 +1229,16 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
1190
1229
  DriverClass: 'RingCentralTelephonyExtension',
1191
1230
  RootPath: '/telephony/ringcentral',
1192
1231
  Phase: 'pre-auth',
1193
- Settings: configInfo.telephony.ringcentral,
1232
+ Settings: { ...sharedTelephonySettings, ...configInfo.telephony.ringcentral },
1233
+ });
1234
+ }
1235
+ if (configInfo.telephony.livekitSip) {
1236
+ telephonyExtensionConfigs.push({
1237
+ Enabled: true,
1238
+ DriverClass: 'LiveKitSipExtension',
1239
+ RootPath: '/telephony/livekit-sip',
1240
+ Phase: 'pre-auth',
1241
+ Settings: { ...sharedTelephonySettings, ...configInfo.telephony.livekitSip },
1194
1242
  });
1195
1243
  }
1196
1244
  if (configInfo.telephony.teams?.enabled) {
@@ -1442,12 +1490,16 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
1442
1490
  Shutdown: () => { clearInterval(orphanDetailTimer); },
1443
1491
  });
1444
1492
  }
1445
- // Launch the AI Agent Session janitor: run own-host orphan recovery once at boot, then keep a
1446
- // periodic staleness sweep running. Self-registers with ShutdownRegistry, so its timer is cleared
1447
- // by the gracefulShutdown drain below (no explicit Stop() wiring needed here).
1493
+ // Launch the AI Agent Session janitor: run own-host orphan recovery once at boot, then a periodic tick
1494
+ // that closes stale and over-deadline sessions and rebuilds lost realtime recordings from their
1495
+ // crash-recovery shards (storage reads and writes). Self-registers with ShutdownRegistry, so its timer
1496
+ // is cleared by the gracefulShutdown drain below (no explicit Stop() wiring needed here).
1448
1497
  if (resumeUser && Metadata.Provider instanceof DatabaseProviderBase) { // global-provider-ok: server startup recovery — boot-time session janitor uses the server's own provider
1449
1498
  SessionJanitor.Instance.Start(Metadata.Provider, resumeUser) // global-provider-ok: server-owned background reconciler runs under the server's provider + system user
1450
1499
  .catch(err => console.warn(`[SessionJanitor] Startup failed: ${err}`));
1500
+ // The bridge engine's orphan reconcile likewise starts only now that this process holds its port, so it can't
1501
+ // take the live bridges of another MJAPI still serving that port for its own orphans (#5310).
1502
+ StartBridgeOrphanReconciliation(Metadata.Provider, resumeUser); // global-provider-ok: server-owned background reconciler runs under the server's provider + system user
1451
1503
  }
1452
1504
  // Launch the durable task-graph dispatcher: claim Pending tasks, execute them with a fresh
1453
1505
  // provider each, and reconcile claims orphaned by a crash. Without this nothing ever picks up a
@@ -1480,6 +1532,17 @@ export const Serve = async (resolverPaths, app = CreateApp(), options) => {
1480
1532
  StartTaskGraphDispatcher(taskGraphPool, resumeUser)
1481
1533
  .catch(err => console.warn(`[TaskGraphDispatcher] Startup failed: ${err}`));
1482
1534
  }
1535
+ // Start the durable work-queue host where enabled. It plans which subscriptions this instance runs,
1536
+ // re-plans on a timer, and self-registers with ShutdownRegistry, so gracefulShutdown's awaited
1537
+ // ShutdownAll() drains it (up to 2 × shutdownDrainMs) before the HTTP server closes.
1538
+ // Not awaited: a slow engine load must not delay readiness, and a failure never stops the API.
1539
+ const workQueueProvider = Metadata.Provider; // global-provider-ok: server startup — the work-queue host runs on the server's own provider
1540
+ if (configInfo.workQueue?.enabled && workQueueProvider instanceof DatabaseProviderBase) {
1541
+ const workQueuePool = dataSources[0]?.dataSource;
1542
+ const providerSource = new MJServerWorkQueueProviderSource(workQueuePool instanceof sql.ConnectionPool ? workQueuePool : null, workQueueProvider);
1543
+ StartWorkQueueHost(configInfo.workQueue, workQueueProvider, providerSource)
1544
+ .catch(error => console.error('❌ Failed to start the work queue host:', error));
1545
+ }
1483
1546
  // Set up graceful shutdown handlers
1484
1547
  const gracefulShutdown = async (signal) => {
1485
1548
  console.log(`\n${signal} received, shutting down gracefully...`);
@@ -1923,12 +1986,4 @@ function createMSSQLCompatPool(pgPool) {
1923
1986
  };
1924
1987
  return wrapper;
1925
1988
  }
1926
- /**
1927
- * Translates SQL Server bracket-quoted identifiers to PostgreSQL double-quoted identifiers.
1928
- * Converts [schema].[table] to "schema"."table" and handles common T-SQL patterns.
1929
- */
1930
- function translateBracketsToPG(sql) {
1931
- // Replace [identifier] with "identifier"
1932
- return sql.replace(/\[([^\]]+)\]/g, '"$1"');
1933
- }
1934
1989
  //# sourceMappingURL=index.js.map