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

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 (407) hide show
  1. package/README.md +69 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/BridgeHostInstance.d.ts +8 -3
  4. package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -1
  5. package/dist/agentSessions/BridgeHostInstance.js +14 -4
  6. package/dist/agentSessions/BridgeHostInstance.js.map +1 -1
  7. package/dist/agentSessions/HandoffOfferExpiry.d.ts +10 -0
  8. package/dist/agentSessions/HandoffOfferExpiry.d.ts.map +1 -0
  9. package/dist/agentSessions/HandoffOfferExpiry.js +18 -0
  10. package/dist/agentSessions/HandoffOfferExpiry.js.map +1 -0
  11. package/dist/agentSessions/ReturningVisitorRecap.js +1 -0
  12. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  13. package/dist/agentSessions/SessionJanitor.d.ts +31 -8
  14. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  15. package/dist/agentSessions/SessionJanitor.js +85 -41
  16. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  17. package/dist/agentSessions/SessionManager.d.ts +14 -4
  18. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  19. package/dist/agentSessions/SessionManager.js +28 -4
  20. package/dist/agentSessions/SessionManager.js.map +1 -1
  21. package/dist/agentSessions/conversationBrowsers.d.ts +61 -0
  22. package/dist/agentSessions/conversationBrowsers.d.ts.map +1 -0
  23. package/dist/agentSessions/conversationBrowsers.js +118 -0
  24. package/dist/agentSessions/conversationBrowsers.js.map +1 -0
  25. package/dist/agentSessions/sessionDeadline.d.ts +43 -0
  26. package/dist/agentSessions/sessionDeadline.d.ts.map +1 -0
  27. package/dist/agentSessions/sessionDeadline.js +68 -0
  28. package/dist/agentSessions/sessionDeadline.js.map +1 -0
  29. package/dist/auth/APIKeyScopeAuth.d.ts.map +1 -1
  30. package/dist/auth/APIKeyScopeAuth.js +5 -4
  31. package/dist/auth/APIKeyScopeAuth.js.map +1 -1
  32. package/dist/auth/loginEmailResolution.d.ts +41 -0
  33. package/dist/auth/loginEmailResolution.d.ts.map +1 -0
  34. package/dist/auth/loginEmailResolution.js +47 -0
  35. package/dist/auth/loginEmailResolution.js.map +1 -0
  36. package/dist/auth/magicLink/MagicLinkKeys.d.ts +50 -2
  37. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  38. package/dist/auth/magicLink/MagicLinkKeys.js +129 -12
  39. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  40. package/dist/auth/magicLink/MagicLinkRouter.d.ts +3 -0
  41. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  42. package/dist/auth/magicLink/MagicLinkRouter.js +23 -5
  43. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  44. package/dist/auth/magicLink/MagicLinkService.d.ts +39 -5
  45. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  46. package/dist/auth/magicLink/MagicLinkService.js +221 -9
  47. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  48. package/dist/auth/magicLink/index.d.ts +1 -1
  49. package/dist/auth/magicLink/index.d.ts.map +1 -1
  50. package/dist/auth/magicLink/index.js +1 -1
  51. package/dist/auth/magicLink/index.js.map +1 -1
  52. package/dist/auth/magicLink/redeemLanding.d.ts +2 -2
  53. package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
  54. package/dist/auth/magicLink/redeemLanding.js +7 -4
  55. package/dist/auth/magicLink/redeemLanding.js.map +1 -1
  56. package/dist/auth/magicLink/types.d.ts +27 -3
  57. package/dist/auth/magicLink/types.d.ts.map +1 -1
  58. package/dist/auth/magicLink/types.js +0 -4
  59. package/dist/auth/magicLink/types.js.map +1 -1
  60. package/dist/auth/newUsers.d.ts.map +1 -1
  61. package/dist/auth/newUsers.js +1 -0
  62. package/dist/auth/newUsers.js.map +1 -1
  63. package/dist/backgroundSweepConfigUnits.d.ts +231 -0
  64. package/dist/backgroundSweepConfigUnits.d.ts.map +1 -0
  65. package/dist/backgroundSweepConfigUnits.js +180 -0
  66. package/dist/backgroundSweepConfigUnits.js.map +1 -0
  67. package/dist/config.d.ts +906 -8
  68. package/dist/config.d.ts.map +1 -1
  69. package/dist/config.js +59 -0
  70. package/dist/config.js.map +1 -1
  71. package/dist/context.d.ts +2 -0
  72. package/dist/context.d.ts.map +1 -1
  73. package/dist/context.js +35 -4
  74. package/dist/context.js.map +1 -1
  75. package/dist/coreRoutes.d.ts +3 -1
  76. package/dist/coreRoutes.d.ts.map +1 -1
  77. package/dist/coreRoutes.js +6 -4
  78. package/dist/coreRoutes.js.map +1 -1
  79. package/dist/generated/graphql-schemas/__mj.d.ts +228 -0
  80. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  81. package/dist/generated/graphql-schemas/__mj.js +1163 -6
  82. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  83. package/dist/generic/OrphanedConversationDetailReconciler.d.ts +11 -0
  84. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  85. package/dist/generic/OrphanedConversationDetailReconciler.js +27 -1
  86. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  87. package/dist/generic/ResolverBase.d.ts +50 -5
  88. package/dist/generic/ResolverBase.d.ts.map +1 -1
  89. package/dist/generic/ResolverBase.js +321 -132
  90. package/dist/generic/ResolverBase.js.map +1 -1
  91. package/dist/generic/RunViewResolver.d.ts +4 -0
  92. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  93. package/dist/generic/RunViewResolver.js +47 -7
  94. package/dist/generic/RunViewResolver.js.map +1 -1
  95. package/dist/index.d.ts +1 -0
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +157 -35
  98. package/dist/index.js.map +1 -1
  99. package/dist/integration/ConnectionPause.d.ts +121 -0
  100. package/dist/integration/ConnectionPause.d.ts.map +1 -0
  101. package/dist/integration/ConnectionPause.js +183 -0
  102. package/dist/integration/ConnectionPause.js.map +1 -0
  103. package/dist/integration/CustomColumnPromoter.d.ts +72 -6
  104. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  105. package/dist/integration/CustomColumnPromoter.js +87 -46
  106. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  107. package/dist/integration/EntityMapLifecycle.d.ts +78 -4
  108. package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
  109. package/dist/integration/EntityMapLifecycle.js +199 -5
  110. package/dist/integration/EntityMapLifecycle.js.map +1 -1
  111. package/dist/integration/InactiveRowWarnings.d.ts +31 -0
  112. package/dist/integration/InactiveRowWarnings.d.ts.map +1 -1
  113. package/dist/integration/InactiveRowWarnings.js +29 -0
  114. package/dist/integration/InactiveRowWarnings.js.map +1 -1
  115. package/dist/integration/ObjectNamesByID.d.ts +28 -0
  116. package/dist/integration/ObjectNamesByID.d.ts.map +1 -0
  117. package/dist/integration/ObjectNamesByID.js +87 -0
  118. package/dist/integration/ObjectNamesByID.js.map +1 -0
  119. package/dist/integration/PostRestartIntrospect.d.ts +26 -0
  120. package/dist/integration/PostRestartIntrospect.d.ts.map +1 -0
  121. package/dist/integration/PostRestartIntrospect.js +27 -0
  122. package/dist/integration/PostRestartIntrospect.js.map +1 -0
  123. package/dist/integration/RSUPostRestartProgress.d.ts +176 -0
  124. package/dist/integration/RSUPostRestartProgress.d.ts.map +1 -0
  125. package/dist/integration/RSUPostRestartProgress.js +323 -0
  126. package/dist/integration/RSUPostRestartProgress.js.map +1 -0
  127. package/dist/integration/RSUProgressBridge.d.ts +41 -2
  128. package/dist/integration/RSUProgressBridge.d.ts.map +1 -1
  129. package/dist/integration/RSUProgressBridge.js +84 -1
  130. package/dist/integration/RSUProgressBridge.js.map +1 -1
  131. package/dist/integration/RunArtifactAuthorization.d.ts +39 -0
  132. package/dist/integration/RunArtifactAuthorization.d.ts.map +1 -0
  133. package/dist/integration/RunArtifactAuthorization.js +45 -0
  134. package/dist/integration/RunArtifactAuthorization.js.map +1 -0
  135. package/dist/integration/SchemaRefreshLaunch.d.ts +9 -0
  136. package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -1
  137. package/dist/integration/SchemaRefreshLaunch.js +3 -2
  138. package/dist/integration/SchemaRefreshLaunch.js.map +1 -1
  139. package/dist/integration/ScopedFieldsForBuild.d.ts +46 -0
  140. package/dist/integration/ScopedFieldsForBuild.d.ts.map +1 -0
  141. package/dist/integration/ScopedFieldsForBuild.js +91 -0
  142. package/dist/integration/ScopedFieldsForBuild.js.map +1 -0
  143. package/dist/integration/TargetConfigGuard.d.ts +40 -0
  144. package/dist/integration/TargetConfigGuard.d.ts.map +1 -0
  145. package/dist/integration/TargetConfigGuard.js +40 -0
  146. package/dist/integration/TargetConfigGuard.js.map +1 -0
  147. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
  148. package/dist/realtimeSessions/ownedRealtimeSession.js +2 -3
  149. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
  150. package/dist/realtimeSessions/sessionAccess.d.ts +15 -0
  151. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  152. package/dist/realtimeSessions/sessionAccess.js +22 -0
  153. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  154. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  155. package/dist/realtimeWidget/WidgetSessionService.js +2 -0
  156. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  157. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
  158. package/dist/realtimeWidget/visitorIdentity.js +1 -0
  159. package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
  160. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  161. package/dist/realtimeWidget/widgetCore.js +4 -7
  162. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  163. package/dist/realtimeWidget/widgetGuestElevation.js +1 -0
  164. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  165. package/dist/requestOrigin.d.ts +22 -0
  166. package/dist/requestOrigin.d.ts.map +1 -0
  167. package/dist/requestOrigin.js +60 -0
  168. package/dist/requestOrigin.js.map +1 -0
  169. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  170. package/dist/resolvers/AgentSessionResolver.js +6 -2
  171. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  172. package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
  173. package/dist/resolvers/AutotagPipelineResolver.js +2 -1
  174. package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
  175. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  176. package/dist/resolvers/ConversationRunEventsResolver.js +2 -0
  177. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  178. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +6 -0
  179. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -1
  180. package/dist/resolvers/ExecuteRemoteOperationResolver.js +30 -7
  181. package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -1
  182. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  183. package/dist/resolvers/GetDataResolver.js +34 -13
  184. package/dist/resolvers/GetDataResolver.js.map +1 -1
  185. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +401 -11
  186. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  187. package/dist/resolvers/IntegrationDiscoveryResolver.js +2690 -1305
  188. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  189. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  190. package/dist/resolvers/MCPResolver.js +13 -2
  191. package/dist/resolvers/MCPResolver.js.map +1 -1
  192. package/dist/resolvers/QuerySystemUserResolver.d.ts +36 -0
  193. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  194. package/dist/resolvers/QuerySystemUserResolver.js +174 -87
  195. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  196. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  197. package/dist/resolvers/RealtimeBridgeResolver.js +1 -0
  198. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  199. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +6 -0
  200. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  201. package/dist/resolvers/RealtimeClientSessionResolver.js +36 -5
  202. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  203. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +62 -10
  204. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  205. package/dist/resolvers/RemoteBrowserActionResolver.js +217 -34
  206. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  207. package/dist/resolvers/RunAIAgentResolver.d.ts +24 -8
  208. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  209. package/dist/resolvers/RunAIAgentResolver.js +185 -67
  210. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  211. package/dist/resolvers/RunAIPromptResolver.d.ts +5 -3
  212. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  213. package/dist/resolvers/RunAIPromptResolver.js +19 -18
  214. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  215. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  216. package/dist/resolvers/RunTemplateResolver.js +3 -1
  217. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  218. package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
  219. package/dist/resolvers/meetingRecordingRegistration.js +2 -0
  220. package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
  221. package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
  222. package/dist/rest/MediaAccessKeys.js +6 -2
  223. package/dist/rest/MediaAccessKeys.js.map +1 -1
  224. package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
  225. package/dist/rest/OAuthCallbackHandler.js +11 -5
  226. package/dist/rest/OAuthCallbackHandler.js.map +1 -1
  227. package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
  228. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
  229. package/dist/rest/RealtimeRecordingHandler.js +2 -4
  230. package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
  231. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  232. package/dist/rest/ViewOperationsHandler.js +2 -1
  233. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  234. package/dist/rest/setupRESTEndpoints.d.ts +3 -1
  235. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  236. package/dist/rest/setupRESTEndpoints.js +11 -2
  237. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  238. package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -1
  239. package/dist/services/DurableEntityActionTaskSubmitter.js +6 -1
  240. package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -1
  241. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  242. package/dist/services/TaskGraphActionRunner.js +2 -26
  243. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  244. package/dist/services/TaskGraphAgentRunner.d.ts +6 -0
  245. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  246. package/dist/services/TaskGraphAgentRunner.js +38 -0
  247. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  248. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  249. package/dist/services/TaskGraphContinuationDeliverer.js +1 -0
  250. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  251. package/dist/services/WorkQueueHostService.d.ts +2 -1
  252. package/dist/services/WorkQueueHostService.d.ts.map +1 -1
  253. package/dist/services/WorkQueueHostService.js +19 -4
  254. package/dist/services/WorkQueueHostService.js.map +1 -1
  255. package/dist/services/workQueueConfig.d.ts +2 -2
  256. package/dist/services/workQueueConfig.js +5 -5
  257. package/dist/services/workQueueConfig.js.map +1 -1
  258. package/dist/sharedCache.js +1 -1
  259. package/dist/sharedCache.js.map +1 -1
  260. package/dist/types.d.ts +17 -0
  261. package/dist/types.d.ts.map +1 -1
  262. package/dist/types.js.map +1 -1
  263. package/package.json +98 -98
  264. package/src/__tests__/APIKeyScopeAuth.test.ts +4 -8
  265. package/src/__tests__/ActiveOperations.test.ts +68 -0
  266. package/src/__tests__/AgentSessionResolver.ownership.test.ts +13 -0
  267. package/src/__tests__/AssertTargetConfigs.test.ts +75 -0
  268. package/src/__tests__/BatchApplyParity.test.ts +109 -0
  269. package/src/__tests__/BridgeHostInstance.test.ts +42 -0
  270. package/src/__tests__/CollectInactiveRowWarnings.test.ts +115 -0
  271. package/src/__tests__/ConnectionPause.test.ts +192 -0
  272. package/src/__tests__/ConnectionPauseResolver.test.ts +166 -0
  273. package/src/__tests__/ConnectionPauseSave.test.ts +143 -0
  274. package/src/__tests__/CustomColumnPromoterCompletion.test.ts +142 -0
  275. package/src/__tests__/DeleteConnectionRemovesPerConnectionCatalog.test.ts +51 -0
  276. package/src/__tests__/DetachedDiscoveryStart.test.ts +65 -0
  277. package/src/__tests__/DurableEntityActionTaskSubmitter.test.ts +73 -0
  278. package/src/__tests__/EntityMapLifecycle.rekey.test.ts +252 -0
  279. package/src/__tests__/ExecuteRemoteOperationResolver.test.ts +213 -0
  280. package/src/__tests__/HandoffOfferExpiry.test.ts +40 -0
  281. package/src/__tests__/IntegrationRunArtifactAuthorization.test.ts +126 -0
  282. package/src/__tests__/MagicLinkKeys.test.ts +102 -0
  283. package/src/__tests__/MediaAccessKeys.test.ts +57 -10
  284. package/src/__tests__/NewArrivalsDefaultDisabled.test.ts +50 -0
  285. package/src/__tests__/ObjectNamesByID.test.ts +176 -0
  286. package/src/__tests__/PostRestartMapsInCatalogScope.test.ts +146 -0
  287. package/src/__tests__/QuerySystemUserResolver.childRows.test.ts +80 -0
  288. package/src/__tests__/QuerySystemUserResolver.collision.test.ts +200 -0
  289. package/src/__tests__/QuerySystemUserResolver.permissions.test.ts +132 -0
  290. package/src/__tests__/RSUPostRestartProgress.test.ts +343 -0
  291. package/src/__tests__/RSUProgressBridge.test.ts +188 -5
  292. package/src/__tests__/RSUStartupSweepWiring.test.ts +30 -0
  293. package/src/__tests__/ReactivateRefreshMessage.test.ts +10 -3
  294. package/src/__tests__/RealtimeClientSessionResolver.test.ts +22 -0
  295. package/src/__tests__/{RefreshAdoptsNewColumns.test.ts → RefreshReportsNewColumns.test.ts} +25 -16
  296. package/src/__tests__/RemoteBrowserActionRecovery.test.ts +287 -0
  297. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +3 -0
  298. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +6 -4
  299. package/src/__tests__/RemoteBrowserAudioStream.test.ts +3 -0
  300. package/src/__tests__/RemoteBrowserSnapshot.test.ts +3 -0
  301. package/src/__tests__/ResolverBase.clientOldValues.test.ts +323 -26
  302. package/src/__tests__/ResolverBase.encryptedApiZeroReencrypt.test.ts +307 -0
  303. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +53 -2
  304. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +60 -0
  305. package/src/__tests__/RunAIAgentResolver.branch.test.ts +4 -0
  306. package/src/__tests__/RunAIAgentResolver.consultPredecessor.test.ts +289 -0
  307. package/src/__tests__/RunAIAgentResolver.handoff.test.ts +649 -0
  308. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +2 -0
  309. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +2 -0
  310. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +87 -12
  311. package/src/__tests__/SchemaEvolutionCustomColumns.test.ts +119 -0
  312. package/src/__tests__/SchemaRefreshLaunch.test.ts +24 -2
  313. package/src/__tests__/ScopedFieldsForBuild.test.ts +192 -0
  314. package/src/__tests__/SessionJanitor.test.ts +195 -4
  315. package/src/__tests__/SessionManager.test.ts +69 -0
  316. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +2 -2
  317. package/src/__tests__/TaskGraphAgentRunner.test.ts +272 -16
  318. package/src/__tests__/ViewOperationsHandler.test.ts +2 -5
  319. package/src/__tests__/WorkQueueHostService.test.ts +25 -7
  320. package/src/__tests__/backgroundSweepConfigUnits.test.ts +153 -0
  321. package/src/__tests__/contextRequestOrigin.test.ts +105 -0
  322. package/src/__tests__/conversationBrowsers.test.ts +62 -0
  323. package/src/__tests__/magicLinkExtensionCapabilities.test.ts +413 -0
  324. package/src/__tests__/orphanedConversationDetailReconciler.start.test.ts +82 -0
  325. package/src/__tests__/ownedRealtimeSession.test.ts +14 -0
  326. package/src/__tests__/realtimeRecordingHandler.test.ts +7 -2
  327. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +79 -1
  328. package/src/__tests__/requestOrigin.test.ts +104 -0
  329. package/src/__tests__/securityHardeningPr6034.test.ts +120 -0
  330. package/src/__tests__/sessionDeadline.test.ts +51 -0
  331. package/src/__tests__/sharedCache.test.ts +11 -0
  332. package/src/__tests__/unifiedAuth.test.ts +73 -1
  333. package/src/agentSessions/BridgeHostInstance.ts +20 -5
  334. package/src/agentSessions/HandoffOfferExpiry.ts +18 -0
  335. package/src/agentSessions/ReturningVisitorRecap.ts +1 -0
  336. package/src/agentSessions/SessionJanitor.ts +92 -42
  337. package/src/agentSessions/SessionManager.ts +38 -8
  338. package/src/agentSessions/conversationBrowsers.ts +143 -0
  339. package/src/agentSessions/sessionDeadline.ts +81 -0
  340. package/src/auth/APIKeyScopeAuth.ts +5 -4
  341. package/src/auth/__tests__/loginEmailResolution.test.ts +67 -0
  342. package/src/auth/loginEmailResolution.ts +67 -0
  343. package/src/auth/magicLink/MagicLinkKeys.ts +146 -14
  344. package/src/auth/magicLink/MagicLinkRouter.ts +33 -6
  345. package/src/auth/magicLink/MagicLinkService.ts +250 -10
  346. package/src/auth/magicLink/index.ts +1 -1
  347. package/src/auth/magicLink/redeemLanding.ts +7 -4
  348. package/src/auth/magicLink/types.ts +28 -3
  349. package/src/auth/newUsers.ts +1 -0
  350. package/src/backgroundSweepConfigUnits.ts +213 -0
  351. package/src/config.ts +69 -0
  352. package/src/context.ts +39 -6
  353. package/src/coreRoutes.ts +14 -7
  354. package/src/generated/graphql-schemas/__mj.ts +822 -7
  355. package/src/generic/OrphanedConversationDetailReconciler.ts +32 -1
  356. package/src/generic/ResolverBase.ts +414 -143
  357. package/src/generic/RunViewResolver.ts +96 -49
  358. package/src/index.ts +201 -35
  359. package/src/integration/ConnectionPause.ts +222 -0
  360. package/src/integration/CustomColumnPromoter.ts +123 -53
  361. package/src/integration/EntityMapLifecycle.ts +229 -6
  362. package/src/integration/InactiveRowWarnings.ts +50 -0
  363. package/src/integration/ObjectNamesByID.ts +97 -0
  364. package/src/integration/PostRestartIntrospect.ts +49 -0
  365. package/src/integration/RSUPostRestartProgress.ts +359 -0
  366. package/src/integration/RSUProgressBridge.ts +85 -2
  367. package/src/integration/RunArtifactAuthorization.ts +65 -0
  368. package/src/integration/SchemaRefreshLaunch.ts +12 -2
  369. package/src/integration/ScopedFieldsForBuild.ts +113 -0
  370. package/src/integration/TargetConfigGuard.ts +71 -0
  371. package/src/realtimeSessions/ownedRealtimeSession.ts +2 -6
  372. package/src/realtimeSessions/sessionAccess.ts +30 -0
  373. package/src/realtimeWidget/WidgetSessionService.ts +2 -0
  374. package/src/realtimeWidget/visitorIdentity.ts +1 -0
  375. package/src/realtimeWidget/widgetCore.ts +4 -8
  376. package/src/realtimeWidget/widgetGuestElevation.ts +1 -0
  377. package/src/requestOrigin.ts +64 -0
  378. package/src/resolvers/AgentSessionResolver.ts +6 -2
  379. package/src/resolvers/AutotagPipelineResolver.ts +2 -1
  380. package/src/resolvers/ConversationRunEventsResolver.ts +2 -0
  381. package/src/resolvers/ExecuteRemoteOperationResolver.ts +41 -9
  382. package/src/resolvers/GetDataResolver.ts +35 -13
  383. package/src/resolvers/IntegrationDiscoveryResolver.ts +1522 -203
  384. package/src/resolvers/MCPResolver.ts +15 -2
  385. package/src/resolvers/QuerySystemUserResolver.ts +197 -98
  386. package/src/resolvers/RealtimeBridgeResolver.ts +1 -0
  387. package/src/resolvers/RealtimeClientSessionResolver.ts +40 -7
  388. package/src/resolvers/RemoteBrowserActionResolver.ts +242 -32
  389. package/src/resolvers/RunAIAgentResolver.ts +265 -86
  390. package/src/resolvers/RunAIPromptResolver.ts +18 -18
  391. package/src/resolvers/RunTemplateResolver.ts +3 -1
  392. package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +52 -0
  393. package/src/resolvers/meetingRecordingRegistration.ts +2 -0
  394. package/src/rest/MediaAccessKeys.ts +6 -2
  395. package/src/rest/OAuthCallbackHandler.ts +13 -5
  396. package/src/rest/RealtimeRecordingHandler.ts +5 -9
  397. package/src/rest/ViewOperationsHandler.ts +2 -1
  398. package/src/rest/setupRESTEndpoints.ts +11 -2
  399. package/src/services/DurableEntityActionTaskSubmitter.ts +10 -2
  400. package/src/services/TaskGraphActionRunner.ts +2 -23
  401. package/src/services/TaskGraphAgentRunner.ts +43 -0
  402. package/src/services/TaskGraphContinuationDeliverer.ts +1 -0
  403. package/src/services/WorkQueueHostService.ts +26 -4
  404. package/src/services/workQueueConfig.ts +5 -5
  405. package/src/sharedCache.ts +1 -1
  406. package/src/types.ts +18 -0
  407. package/src/services/TaskOrchestration-Integration.md +0 -188
@@ -1,19 +1,54 @@
1
1
  /**
2
- * Tests for `TaskGraphAgentRunner`.
3
- *
4
- * Verifies that when a task node in a durable task graph executes a specialist agent
5
- * (such as Query Builder or Research Report Writer) and finishes with a payload,
6
- * the runner resolves the originating `ConversationDetailID` and persists artifacts
7
- * via `AgentRunner.ProcessAgentArtifacts`.
2
+ * Tests for `TaskGraphAgentRunner`:
3
+ * - VS-CON-7 preset resolution (`ConfigurationPresetName`)
4
+ * - VS-INV-2b / VS-QA-3 #9 `parentAgentHierarchy` from submitted task
5
+ * - Specialist agent artifact persistence (`ProcessAgentArtifacts`), `CanUserRunAgent` permission check,
6
+ * task Name/Description prompt formatting, and output normalization.
8
7
  */
9
8
  import 'reflect-metadata';
10
9
  import { describe, it, expect, vi, beforeEach } from 'vitest';
10
+ import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
11
+ import type { TaskAgentRunParams } from '@memberjunction/task-graph';
11
12
 
12
- vi.mock('@memberjunction/core', () => ({
13
- LogError: vi.fn(),
14
- LogStatus: vi.fn(),
13
+ const {
14
+ runAgent,
15
+ processAgentArtifacts,
16
+ canUserRunAgent,
17
+ mockEngineAgents,
18
+ mockGetAgentConfigurationPresets,
19
+ mockEngineConfig,
20
+ mockRunView,
21
+ mockLogError,
22
+ } = vi.hoisted(() => ({
23
+ runAgent: vi.fn(),
24
+ processAgentArtifacts: vi.fn(),
25
+ canUserRunAgent: vi.fn().mockResolvedValue(true),
26
+ mockEngineAgents: [
27
+ { ID: 'agent-1', Name: 'Storyboard Artist' },
28
+ { ID: 'qb-agent' },
29
+ { ID: 'writer-agent' },
30
+ ] as Array<{ ID: string; Name?: string }>,
31
+ mockGetAgentConfigurationPresets: vi.fn(),
32
+ mockEngineConfig: vi.fn(),
33
+ mockRunView: vi.fn(),
34
+ mockLogError: vi.fn(),
15
35
  }));
16
36
 
37
+ const mockRunAgent = runAgent;
38
+
39
+ vi.mock('@memberjunction/core', async (importOriginal) => {
40
+ const actual = await importOriginal<typeof import('@memberjunction/core')>();
41
+ return {
42
+ ...actual,
43
+ LogError: (...args: unknown[]) => mockLogError(...args),
44
+ LogStatus: vi.fn(),
45
+ RunView: class {
46
+ static FromMetadataProvider = () => ({ RunView: mockRunView });
47
+ RunView = mockRunView;
48
+ },
49
+ };
50
+ });
51
+
17
52
  vi.mock('@memberjunction/generic-database-provider', () => ({
18
53
  UserCache: {
19
54
  Instance: { Users: [] },
@@ -31,31 +66,251 @@ vi.mock('@memberjunction/ai-core-plus', () => ({
31
66
  }));
32
67
  vi.mock('@memberjunction/ai', () => ({ ChatMessageRole: { user: 'user' } }));
33
68
 
34
- const { runAgent, processAgentArtifacts, canUserRunAgent, mockEngineAgents } = vi.hoisted(() => ({
35
- runAgent: vi.fn(),
36
- processAgentArtifacts: vi.fn(),
37
- canUserRunAgent: vi.fn().mockResolvedValue(true),
38
- mockEngineAgents: [{ ID: 'qb-agent' }, { ID: 'writer-agent' }] as Array<{ ID: string }>,
39
- }));
40
69
  vi.mock('@memberjunction/ai-agents', () => ({
41
70
  AgentRunner: class {
42
71
  RunAgent = runAgent;
43
72
  ProcessAgentArtifacts = processAgentArtifacts;
44
73
  },
45
74
  }));
75
+
46
76
  vi.mock('@memberjunction/aiengine', () => ({
47
77
  AIEngine: {
48
78
  get Instance() {
49
79
  return {
80
+ Config: mockEngineConfig,
50
81
  Agents: mockEngineAgents,
51
82
  CanUserRunAgent: canUserRunAgent,
83
+ GetAgentConfigurationPresets: mockGetAgentConfigurationPresets,
84
+ FindActiveAgentConfigurationPreset: (agentId: string, presetName: string) => {
85
+ const target = presetName.trim().toLowerCase();
86
+ if (!target) return undefined;
87
+ const presets = (mockGetAgentConfigurationPresets(agentId, true) ?? []) as Array<{
88
+ Name?: string;
89
+ DisplayName?: string;
90
+ }>;
91
+ return presets.find((p) => p.Name?.trim().toLowerCase() === target)
92
+ ?? presets.find((p) => p.DisplayName?.trim().toLowerCase() === target);
93
+ },
52
94
  };
53
95
  },
54
96
  },
55
97
  }));
56
98
 
57
99
  import { TaskGraphAgentRunner } from '../services/TaskGraphAgentRunner';
58
- import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
100
+
101
+ interface SubmittingRunOverrides {
102
+ ID?: string;
103
+ ConfigurationID?: string | null;
104
+ Agent?: string | null;
105
+ AgentID?: string | null;
106
+ ParentRunID?: string | null;
107
+ LoadSuccess?: boolean;
108
+ }
109
+
110
+ function makeParams(
111
+ overrides: Partial<TaskAgentRunParams> = {},
112
+ parentConfigurationID: string | null = 'parent-cfg-1',
113
+ submittingRunOverrides: SubmittingRunOverrides = {},
114
+ ): TaskAgentRunParams {
115
+ const loadSuccess = submittingRunOverrides.LoadSuccess ?? true;
116
+ const provider = {
117
+ GetEntityObject: vi.fn().mockImplementation(async (entityName: string) => {
118
+ if (entityName === 'MJ: AI Agents') {
119
+ return {
120
+ Load: vi.fn().mockResolvedValue(true),
121
+ ID: overrides.AgentID ?? 'agent-1',
122
+ Name: 'Storyboard Artist',
123
+ };
124
+ }
125
+ if (entityName === 'MJ: Tasks') {
126
+ return {
127
+ Load: vi.fn().mockResolvedValue(false),
128
+ };
129
+ }
130
+ return {
131
+ Load: vi.fn().mockResolvedValue(loadSuccess),
132
+ ID: submittingRunOverrides.ID ?? 'submitting-run-1',
133
+ ConfigurationID:
134
+ 'ConfigurationID' in submittingRunOverrides
135
+ ? submittingRunOverrides.ConfigurationID
136
+ : parentConfigurationID,
137
+ Agent: submittingRunOverrides.Agent ?? null,
138
+ AgentID: submittingRunOverrides.AgentID ?? null,
139
+ ParentRunID: submittingRunOverrides.ParentRunID ?? null,
140
+ };
141
+ }),
142
+ } as unknown as IMetadataProvider;
143
+
144
+ return {
145
+ TaskID: 'task-1',
146
+ AgentID: 'agent-1',
147
+ InputPayload: 'Draw the opening shot',
148
+ DependencyOutputs: new Map(),
149
+ SubmittingAgentRunID: 'submitting-run-1',
150
+ Provider: provider,
151
+ ContextUser: { ID: 'user-1' } as unknown as UserInfo,
152
+ ...overrides,
153
+ };
154
+ }
155
+
156
+ describe('VS-CON-7: TaskGraphAgentRunner preset resolution', () => {
157
+ beforeEach(() => {
158
+ vi.clearAllMocks();
159
+ canUserRunAgent.mockResolvedValue(true);
160
+ processAgentArtifacts.mockResolvedValue(undefined);
161
+ mockEngineConfig.mockResolvedValue(undefined);
162
+ mockRunAgent.mockResolvedValue({
163
+ success: true,
164
+ agentRun: { ID: 'child-run-1', Message: 'Done' },
165
+ payload: { result: 'ok' },
166
+ });
167
+ });
168
+
169
+ it('resolves ConfigurationPresetName to the matching preset AIConfigurationID', async () => {
170
+ mockGetAgentConfigurationPresets.mockReturnValue([
171
+ {
172
+ ID: 'preset-draft',
173
+ Name: 'Draft',
174
+ DisplayName: 'Draft Mode',
175
+ AIConfigurationID: 'cfg-draft-1',
176
+ Status: 'Active',
177
+ },
178
+ {
179
+ ID: 'preset-high',
180
+ Name: 'High',
181
+ DisplayName: 'High Fidelity',
182
+ AIConfigurationID: 'cfg-high-1',
183
+ Status: 'Active',
184
+ },
185
+ ]);
186
+
187
+ const runner = new TaskGraphAgentRunner();
188
+ const result = await runner.RunAgentForTask(
189
+ makeParams({ ConfigurationPresetName: 'high fidelity' })
190
+ );
191
+
192
+ expect(result.Success).toBe(true);
193
+ expect(mockRunAgent).toHaveBeenCalledTimes(1);
194
+ expect(mockRunAgent.mock.calls[0][0].configurationId).toBe('cfg-high-1');
195
+ });
196
+
197
+ it('passes configurationId: undefined when the matched preset has NULL AIConfigurationID, even if the submitting run has a ConfigurationID', async () => {
198
+ mockGetAgentConfigurationPresets.mockReturnValue([
199
+ {
200
+ ID: 'preset-default',
201
+ Name: 'SystemDefault',
202
+ DisplayName: 'System Default',
203
+ AIConfigurationID: null,
204
+ Status: 'Active',
205
+ },
206
+ ]);
207
+
208
+ const runner = new TaskGraphAgentRunner();
209
+ const result = await runner.RunAgentForTask(
210
+ makeParams({ ConfigurationPresetName: 'SystemDefault' }, 'parent-cfg-1')
211
+ );
212
+
213
+ expect(result.Success).toBe(true);
214
+ expect(mockRunAgent).toHaveBeenCalledTimes(1);
215
+ expect(mockRunAgent.mock.calls[0][0].configurationId).toBeUndefined();
216
+ });
217
+
218
+ it('falls back to the submitting run ConfigurationID when ConfigurationPresetName is not specified', async () => {
219
+ const runner = new TaskGraphAgentRunner();
220
+ const result = await runner.RunAgentForTask(makeParams({}, 'parent-cfg-99'));
221
+
222
+ expect(result.Success).toBe(true);
223
+ expect(mockRunAgent).toHaveBeenCalledTimes(1);
224
+ expect(mockRunAgent.mock.calls[0][0].configurationId).toBe('parent-cfg-99');
225
+ });
226
+
227
+ it('fails the task with a clear message when ConfigurationPresetName does not match any active preset at run time', async () => {
228
+ mockGetAgentConfigurationPresets.mockReturnValue([
229
+ {
230
+ ID: 'preset-draft',
231
+ Name: 'Draft',
232
+ DisplayName: 'Draft Mode',
233
+ AIConfigurationID: 'cfg-draft-1',
234
+ Status: 'Active',
235
+ },
236
+ ]);
237
+
238
+ const runner = new TaskGraphAgentRunner();
239
+ const result = await runner.RunAgentForTask(
240
+ makeParams({ ConfigurationPresetName: 'DeletedPreset' })
241
+ );
242
+
243
+ expect(result.Success).toBe(false);
244
+ expect(result.ErrorMessage).toBe(
245
+ "Agent 'Storyboard Artist' has no active preset 'DeletedPreset'. Available: Draft."
246
+ );
247
+ expect(mockRunAgent).not.toHaveBeenCalled();
248
+ });
249
+ });
250
+
251
+ describe('VS-INV-2b / VS-QA-3 #9: TaskGraphAgentRunner parentAgentHierarchy from submitted task', () => {
252
+ const RUN_B_ID = '22222222-2222-4222-8222-222222222222';
253
+ const RUN_C_ID = '33333333-3333-4333-8333-333333333333';
254
+
255
+ beforeEach(() => {
256
+ vi.clearAllMocks();
257
+ canUserRunAgent.mockResolvedValue(true);
258
+ processAgentArtifacts.mockResolvedValue(undefined);
259
+ mockEngineConfig.mockResolvedValue(undefined);
260
+ mockRunAgent.mockResolvedValue({
261
+ success: true,
262
+ agentRun: { ID: 'child-run-1', Message: 'Done' },
263
+ payload: { result: 'ok' },
264
+ });
265
+ });
266
+
267
+ it('reads root-first ParentAgentHierarchy carried from the submitted task without running RunView in a loop (VS-QA-3 #9)', async () => {
268
+ const runner = new TaskGraphAgentRunner();
269
+ const params = makeParams(
270
+ {
271
+ SubmittingAgentRunID: RUN_C_ID,
272
+ ParentAgentHierarchy: ['A', 'B', 'C'],
273
+ },
274
+ 'parent-cfg-1',
275
+ { ID: RUN_C_ID, Agent: 'C', ParentRunID: RUN_B_ID },
276
+ );
277
+
278
+ const result = await runner.RunAgentForTask(params);
279
+
280
+ expect(result.Success).toBe(true);
281
+ expect(mockRunView).not.toHaveBeenCalled();
282
+ expect(mockRunAgent).toHaveBeenCalledTimes(1);
283
+ expect(mockRunAgent.mock.calls[0][0].parentAgentHierarchy).toEqual(['A', 'B', 'C']);
284
+ });
285
+
286
+ it('falls back to the loaded parentRun agent name when ParentAgentHierarchy is omitted, with zero RunView calls', async () => {
287
+ const runner = new TaskGraphAgentRunner();
288
+ const params = makeParams(
289
+ { SubmittingAgentRunID: RUN_C_ID },
290
+ 'parent-cfg-1',
291
+ { ID: RUN_C_ID, Agent: 'C', ParentRunID: RUN_B_ID },
292
+ );
293
+
294
+ const result = await runner.RunAgentForTask(params);
295
+
296
+ expect(result.Success).toBe(true);
297
+ expect(mockRunView).not.toHaveBeenCalled();
298
+ expect(mockRunAgent).toHaveBeenCalledTimes(1);
299
+ expect(mockRunAgent.mock.calls[0][0].parentAgentHierarchy).toEqual(['C']);
300
+ });
301
+
302
+ it('passes undefined parentAgentHierarchy and performs no RunView reads when there is no parentRun and no ParentAgentHierarchy', async () => {
303
+ const runner = new TaskGraphAgentRunner();
304
+ const result = await runner.RunAgentForTask(
305
+ makeParams({ SubmittingAgentRunID: undefined }),
306
+ );
307
+
308
+ expect(result.Success).toBe(true);
309
+ expect(mockRunView).not.toHaveBeenCalled();
310
+ expect(mockRunAgent).toHaveBeenCalledTimes(1);
311
+ expect(mockRunAgent.mock.calls[0][0].parentAgentHierarchy).toBeUndefined();
312
+ });
313
+ });
59
314
 
60
315
  describe('TaskGraphAgentRunner', () => {
61
316
  beforeEach(() => {
@@ -264,3 +519,4 @@ describe('TaskGraphAgentRunner', () => {
264
519
  });
265
520
  });
266
521
 
522
+
@@ -267,16 +267,13 @@ describe('ViewOperationsHandler.getEntityViews', () => {
267
267
  expect(outcome.error).toBe('timeout talking to DB');
268
268
  });
269
269
 
270
- it('KNOWN GAP: the entity name is interpolated into ExtraFilter without escaping', async () => {
271
- // Reachable only for names registered in metadata, so a hostile value requires a
272
- // hostile entity name — but an entity legitimately named with an apostrophe
273
- // (e.g. "O'Brien Records") already produces a broken/injectable filter today.
270
+ it('escapes single quotes in the entity name when building ExtraFilter', async () => {
274
271
  registerEntity("O'Brien Records", true);
275
272
  mockRunViewFn.mockResolvedValue({ Success: true, Results: [] });
276
273
 
277
274
  await ViewOperationsHandler.getEntityViews("O'Brien Records", makeUser());
278
275
 
279
276
  const executed = mockRunViewFn.mock.calls[0][0] as RunViewParams;
280
- expect(executed.ExtraFilter).toBe(`Entity = 'O'Brien Records'`); // quote NOT doubled
277
+ expect(executed.ExtraFilter).toBe(`Entity = 'O''Brien Records'`);
281
278
  });
282
279
  });
@@ -56,9 +56,9 @@ function enabled(overrides: Partial<WorkQueueConfig> = {}): WorkQueueConfig {
56
56
  }
57
57
 
58
58
  describe('workQueue configuration', () => {
59
- it('defaults to disabled, running every MJWorker subscription once enabled', () => {
59
+ it('defaults to enabled, running every MJWorker subscription out of the box', () => {
60
60
  expect(WorkQueueSchema.parse({})).toEqual({
61
- enabled: false, systemUserEmail: 'system@memberjunction.org', subscriptions: [{ name: '*', concurrency: 4 }],
61
+ enabled: true, systemUserEmail: 'system@memberjunction.org', subscriptions: [{ name: '*', concurrency: 4 }],
62
62
  idlePollMinMs: 250, idlePollMaxMs: 5000, shutdownDrainMs: 8000, sweeperEnabled: true, sweeperIntervalMs: 60000,
63
63
  reconcileIntervalMs: 30000,
64
64
  });
@@ -94,7 +94,7 @@ describe('host configuration helpers', () => {
94
94
  describe('StartWorkQueueHost', () => {
95
95
  it('starts nothing when the section is disabled', async () => {
96
96
  const rec = recorder();
97
- expect(await StartWorkQueueHost(WorkQueueSchema.parse({}), PROVIDER, PROVIDER_SOURCE, rec.Dependencies)).toBeNull();
97
+ expect(await StartWorkQueueHost(WorkQueueSchema.parse({ enabled: false }), PROVIDER, PROVIDER_SOURCE, rec.Dependencies)).toBeNull();
98
98
  expect(rec.EngineCalls).toBe(0);
99
99
  });
100
100
 
@@ -109,9 +109,18 @@ describe('StartWorkQueueHost', () => {
109
109
  await expect(StartWorkQueueHost(enabled(), PROVIDER, PROVIDER_SOURCE, rec.Dependencies)).rejects.toThrow('System user not found with email: system@memberjunction.org');
110
110
  });
111
111
 
112
- it('names the setting to fix when the placeholder email from DEFAULT_SERVER_CONFIG is still in effect', async () => {
113
- const rec = recorder();
114
- await expect(StartWorkQueueHost(enabled({ systemUserEmail: 'not.set@nowhere.com' }), PROVIDER, PROVIDER_SOURCE, rec.Dependencies))
112
+ it('falls back to FindSystemUser when default systemUserEmail is not literal in UserCache', async () => {
113
+ const rec = recorder([]);
114
+ rec.Dependencies.FindSystemUser = () => SYSTEM_USER;
115
+ const host = await StartWorkQueueHost(WorkQueueSchema.parse({}), PROVIDER, PROVIDER_SOURCE, rec.Dependencies);
116
+ expect(host).not.toBeNull();
117
+ expect(rec.EngineCalls).toBe(1);
118
+ });
119
+
120
+ it('names the setting to fix when an explicit custom email is not found even if FindSystemUser exists', async () => {
121
+ const rec = recorder([]);
122
+ rec.Dependencies.FindSystemUser = () => SYSTEM_USER;
123
+ await expect(StartWorkQueueHost(enabled({ systemUserEmail: 'missing-worker@example.com' }), PROVIDER, PROVIDER_SOURCE, rec.Dependencies))
115
124
  .rejects.toThrow('set workQueue.systemUserEmail');
116
125
  });
117
126
 
@@ -126,7 +135,16 @@ describe('StartWorkQueueHost', () => {
126
135
  });
127
136
 
128
137
  describe('MJServerWorkQueueProviderSource', () => {
129
- it('falls back to the shared server provider when there is no SQL Server pool', async () => {
138
+ it('mints an independent provider instance when fallback implements CreateIndependentInstance (e.g. PostgreSQL)', async () => {
139
+ const independent = { Name: 'independent-pg-provider' } as IMetadataProvider;
140
+ const createIndependent = vi.fn().mockResolvedValue(independent);
141
+ const pgFallback = Object.assign({}, PROVIDER, { CreateIndependentInstance: createIndependent });
142
+ const source = new MJServerWorkQueueProviderSource(null, pgFallback);
143
+ expect(await source.CreateProvider()).toBe(independent);
144
+ expect(createIndependent).toHaveBeenCalledTimes(1);
145
+ });
146
+
147
+ it('falls back to the shared server provider when there is no SQL Server pool and no CreateIndependentInstance', async () => {
130
148
  const source = new MJServerWorkQueueProviderSource(null, PROVIDER);
131
149
  expect(await source.CreateProvider()).toBe(PROVIDER);
132
150
  });
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Config contract for the two background sweeps an idle MJAPI runs against its database: the AI
3
+ * Agent Session janitor (`agentSessions.janitor`) and the agent-run watchdog (`agentRunWatchdog`).
4
+ *
5
+ * Pins three things: the shipped defaults are exactly the values MJAPI always ran with (so existing
6
+ * deployments see no change), each sweep can be turned off the documented way, and the env-var
7
+ * readers that feed `DEFAULT_SERVER_CONFIG` reject typos instead of silently running a default or
8
+ * a zero-delay timer.
9
+ */
10
+ import { describe, it, expect } from 'vitest';
11
+ import {
12
+ AgentRunWatchdogDefaultsFromEnv,
13
+ AgentRunWatchdogSchema,
14
+ AgentSessionsDefaultsFromEnv,
15
+ AgentSessionsSchema,
16
+ ConversationDetailReconcilerDefaultsFromEnv,
17
+ ConversationDetailReconcilerSchema,
18
+ EnabledFromEnv,
19
+ IntervalMsFromEnv,
20
+ } from '../backgroundSweepConfigUnits.js';
21
+
22
+ describe('agentSessions.janitor schema', () => {
23
+ it('defaults to what MJAPI has always run: janitor on every 60s (15-minute idle close), bridge reconcile on every 10 min', () => {
24
+ expect(AgentSessionsSchema.parse({})).toEqual({
25
+ janitor: { enabled: true, sweepIntervalMs: 60_000, closeThresholdMinutes: 15 },
26
+ bridgeReconciler: { enabled: true, sweepIntervalMs: 600_000 },
27
+ handoffOfferExpiry: { enabled: true, sweepIntervalMs: 15_000 },
28
+ });
29
+ });
30
+
31
+ it('accepts enabled: false and a longer interval', () => {
32
+ const parsed = AgentSessionsSchema.parse({ janitor: { enabled: false, sweepIntervalMs: 3_600_000 }, bridgeReconciler: { enabled: false } });
33
+ expect(parsed.janitor).toEqual({ enabled: false, sweepIntervalMs: 3_600_000, closeThresholdMinutes: 15 });
34
+ expect(parsed.bridgeReconciler).toEqual({ enabled: false, sweepIntervalMs: 600_000 });
35
+ });
36
+
37
+ it('rejects a zero or negative interval (turn the tick off with enabled: false instead)', () => {
38
+ expect(AgentSessionsSchema.safeParse({ janitor: { sweepIntervalMs: 0 } }).success).toBe(false);
39
+ expect(AgentSessionsSchema.safeParse({ janitor: { closeThresholdMinutes: -1 } }).success).toBe(false);
40
+ expect(AgentSessionsSchema.safeParse({ bridgeReconciler: { sweepIntervalMs: 0 } }).success).toBe(false);
41
+ });
42
+ });
43
+
44
+ describe('conversationDetailReconciler schema', () => {
45
+ it('defaults to what MJAPI has always run: on, every 5 minutes', () => {
46
+ expect(ConversationDetailReconcilerSchema.parse({})).toEqual({ enabled: true, sweepIntervalMs: 300_000 });
47
+ });
48
+
49
+ it('accepts enabled: false and rejects a zero interval', () => {
50
+ expect(ConversationDetailReconcilerSchema.parse({ enabled: false }).enabled).toBe(false);
51
+ expect(ConversationDetailReconcilerSchema.safeParse({ sweepIntervalMs: 0 }).success).toBe(false);
52
+ });
53
+ });
54
+
55
+ describe('DEFAULT_SERVER_CONFIG blocks from the environment', () => {
56
+ it('an empty environment gives exactly the shipped defaults', () => {
57
+ expect(AgentSessionsDefaultsFromEnv({})).toEqual(AgentSessionsSchema.parse({}));
58
+ expect(ConversationDetailReconcilerDefaultsFromEnv({})).toEqual(ConversationDetailReconcilerSchema.parse({}));
59
+ expect(AgentRunWatchdogDefaultsFromEnv({})).toEqual(AgentRunWatchdogSchema.parse({}));
60
+ });
61
+
62
+ it('reads every documented variable', () => {
63
+ const env = {
64
+ MJ_SESSION_JANITOR_ENABLED: 'false',
65
+ MJ_SESSION_JANITOR_SWEEP_INTERVAL_MS: '120000',
66
+ MJ_BRIDGE_RECONCILER_ENABLED: 'off',
67
+ MJ_BRIDGE_RECONCILER_SWEEP_INTERVAL_MS: '1200000',
68
+ MJ_CONVERSATION_DETAIL_RECONCILER_ENABLED: '0',
69
+ MJ_CONVERSATION_DETAIL_RECONCILER_SWEEP_INTERVAL_MS: '900000',
70
+ MJ_HANDOFF_OFFER_EXPIRY_ENABLED: 'no',
71
+ MJ_HANDOFF_OFFER_EXPIRY_SWEEP_INTERVAL_MS: '60000',
72
+ MJ_AGENT_RUN_WATCHDOG_SWEEP_INTERVAL_MS: '0',
73
+ };
74
+ expect(AgentSessionsDefaultsFromEnv(env)).toEqual({
75
+ janitor: { enabled: false, sweepIntervalMs: 120_000, closeThresholdMinutes: 15 },
76
+ bridgeReconciler: { enabled: false, sweepIntervalMs: 1_200_000 },
77
+ handoffOfferExpiry: { enabled: false, sweepIntervalMs: 60_000 },
78
+ });
79
+ expect(ConversationDetailReconcilerDefaultsFromEnv(env)).toEqual({ enabled: false, sweepIntervalMs: 900_000 });
80
+ expect(AgentRunWatchdogDefaultsFromEnv(env).sweepIntervalMs).toBe(0);
81
+ });
82
+
83
+ it('a bad value fails, naming the variable', () => {
84
+ expect(() => AgentSessionsDefaultsFromEnv({ MJ_BRIDGE_RECONCILER_ENABLED: 'nope' }))
85
+ .toThrow('Invalid MJ_BRIDGE_RECONCILER_ENABLED "nope". Expected true or false.');
86
+ expect(() => ConversationDetailReconcilerDefaultsFromEnv({ MJ_CONVERSATION_DETAIL_RECONCILER_SWEEP_INTERVAL_MS: '0' }))
87
+ .toThrow(/Invalid MJ_CONVERSATION_DETAIL_RECONCILER_SWEEP_INTERVAL_MS "0"/);
88
+ });
89
+ });
90
+
91
+ describe('agentRunWatchdog schema', () => {
92
+ it('defaults to the watchdog MJAPI has always run: 5-min sweep, 30s heartbeat, 5-min stale threshold', () => {
93
+ expect(AgentRunWatchdogSchema.parse({})).toEqual({
94
+ sweepIntervalMs: 300_000,
95
+ heartbeatIntervalMs: 30_000,
96
+ staleThresholdMinutes: 5,
97
+ });
98
+ });
99
+
100
+ it('accepts sweepIntervalMs: 0 (startup sweep only)', () => {
101
+ expect(AgentRunWatchdogSchema.parse({ sweepIntervalMs: 0 }).sweepIntervalMs).toBe(0);
102
+ });
103
+
104
+ it('rejects a zero heartbeat (other instances would force-fail this one\'s runs) and negative values', () => {
105
+ expect(AgentRunWatchdogSchema.safeParse({ heartbeatIntervalMs: 0 }).success).toBe(false);
106
+ expect(AgentRunWatchdogSchema.safeParse({ staleThresholdMinutes: 0 }).success).toBe(false);
107
+ expect(AgentRunWatchdogSchema.safeParse({ sweepIntervalMs: -1 }).success).toBe(false);
108
+ });
109
+ });
110
+
111
+ describe('EnabledFromEnv', () => {
112
+ it('returns the fallback when the variable is unset or blank', () => {
113
+ expect(EnabledFromEnv('X', undefined, true)).toBe(true);
114
+ expect(EnabledFromEnv('X', null, false)).toBe(false);
115
+ expect(EnabledFromEnv('X', ' ', true)).toBe(true);
116
+ });
117
+
118
+ it.each(['false', 'FALSE', '0', 'no', 'off', 'n', 'f'])('%j turns the switch off', value => {
119
+ expect(EnabledFromEnv('X', value, true)).toBe(false);
120
+ });
121
+
122
+ it.each(['true', '1', 'yes', 'on', 'Y', 't'])('%j turns it on', value => {
123
+ expect(EnabledFromEnv('X', value, false)).toBe(true);
124
+ });
125
+
126
+ it('fails fast on anything else, naming the variable (a typo must not silently turn a sweep off)', () => {
127
+ expect(() => EnabledFromEnv('MJ_SESSION_JANITOR_ENABLED', 'flase', true))
128
+ .toThrow('Invalid MJ_SESSION_JANITOR_ENABLED "flase". Expected true or false.');
129
+ });
130
+ });
131
+
132
+ describe('IntervalMsFromEnv', () => {
133
+ it('returns the fallback when the variable is unset or blank', () => {
134
+ expect(IntervalMsFromEnv('X', undefined, 60_000, false)).toBe(60_000);
135
+ expect(IntervalMsFromEnv('X', '', 60_000, false)).toBe(60_000);
136
+ });
137
+
138
+ it('reads a number of milliseconds', () => {
139
+ expect(IntervalMsFromEnv('X', ' 3600000 ', 60_000, false)).toBe(3_600_000);
140
+ });
141
+
142
+ it('accepts 0 only where 0 means off', () => {
143
+ expect(IntervalMsFromEnv('MJ_AGENT_RUN_WATCHDOG_SWEEP_INTERVAL_MS', '0', 300_000, true)).toBe(0);
144
+ expect(() => IntervalMsFromEnv('MJ_SESSION_JANITOR_SWEEP_INTERVAL_MS', '0', 60_000, false))
145
+ .toThrow('Invalid MJ_SESSION_JANITOR_SWEEP_INTERVAL_MS "0". Expected a number of milliseconds greater than 0.');
146
+ });
147
+
148
+ it('fails fast on a typo, naming the variable', () => {
149
+ expect(() => IntervalMsFromEnv('MJ_AGENT_RUN_WATCHDOG_SWEEP_INTERVAL_MS', '5m', 300_000, true))
150
+ .toThrow('Invalid MJ_AGENT_RUN_WATCHDOG_SWEEP_INTERVAL_MS "5m". Expected a number of milliseconds, 0 or greater.');
151
+ expect(() => IntervalMsFromEnv('X', '-5', 60_000, true)).toThrow(/Invalid X/);
152
+ });
153
+ });
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Tests that ContextFunction surfaces the request's normalized Origin header on AppContext as
3
+ * `RequestOrigin`. Database-facing dependencies are mocked; no connection is opened.
4
+ */
5
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
6
+ import { BehaviorSubject } from 'rxjs';
7
+
8
+ const { mockProviderConfig } = vi.hoisted(() => ({
9
+ mockProviderConfig: vi.fn(),
10
+ }));
11
+
12
+ vi.mock('../util.js', () => ({
13
+ GetReadOnlyDataSource: vi.fn(() => undefined),
14
+ GetReadWriteDataSource: vi.fn(() => undefined),
15
+ }));
16
+
17
+ vi.mock('../config.js', () => ({
18
+ configInfo: { databaseSettings: { metadataCacheRefreshInterval: 0 } },
19
+ userEmailMap: {},
20
+ apiKey: 'test-api-key',
21
+ mj_core_schema: '__mj',
22
+ }));
23
+
24
+ vi.mock('@memberjunction/sqlserver-dataprovider', () => ({
25
+ SQLServerDataProvider: class {
26
+ Config = mockProviderConfig;
27
+ },
28
+ SQLServerProviderConfigData: class {},
29
+ }));
30
+
31
+ vi.mock('@memberjunction/postgresql-dataprovider', () => ({
32
+ PostgreSQLDataProvider: class {
33
+ Config = mockProviderConfig;
34
+ },
35
+ PostgreSQLProviderConfigData: class {},
36
+ }));
37
+
38
+ vi.mock('@memberjunction/generic-database-provider', async (importOriginal) => {
39
+ const actual = await importOriginal<typeof import('@memberjunction/generic-database-provider')>();
40
+ return {
41
+ ...actual,
42
+ resolveDbPlatformFromEnv: () => 'sqlserver' as const,
43
+ UserCache: { Instance: {} } as typeof actual.UserCache,
44
+ };
45
+ });
46
+
47
+ vi.mock('@memberjunction/api-keys', () => ({
48
+ GetAPIKeyEngine: vi.fn(),
49
+ }));
50
+
51
+ vi.mock('@memberjunction/core', async (importOriginal) => {
52
+ const actual = await importOriginal<typeof import('@memberjunction/core')>();
53
+ return { ...actual, Metadata: { Provider: { Entities: [{}] } } };
54
+ });
55
+
56
+ import { ContextFunction } from '../context.js';
57
+ import type { IncomingMessage } from 'http';
58
+ import type { ConnectionPool } from 'mssql';
59
+ import type { UserPayload } from '../types.js';
60
+
61
+ const MOCK_POOL = {} as ConnectionPool;
62
+ const MOCK_PAYLOAD = { email: 'user@example.com' } as UserPayload;
63
+
64
+ function makeReq(headers: IncomingMessage['headers']): IncomingMessage {
65
+ return { headers, body: {}, ip: '127.0.0.1', userPayload: MOCK_PAYLOAD } as unknown as IncomingMessage;
66
+ }
67
+
68
+ async function contextFor(headers: IncomingMessage['headers']) {
69
+ const build = ContextFunction({ setupComplete$: new BehaviorSubject<unknown>(true), dataSource: MOCK_POOL, dataSources: [] });
70
+ return build({ req: makeReq(headers) });
71
+ }
72
+
73
+ describe('ContextFunction RequestOrigin', () => {
74
+ beforeEach(() => {
75
+ vi.clearAllMocks();
76
+ mockProviderConfig.mockResolvedValue(true);
77
+ });
78
+
79
+ it('carries the normalized Origin header', async () => {
80
+ const ctx = await contextFor({ origin: 'https://Embed.Example.com/' });
81
+ expect(ctx.RequestOrigin).toBe('https://embed.example.com');
82
+ });
83
+
84
+ it('keeps an explicit port', async () => {
85
+ const ctx = await contextFor({ origin: 'http://localhost:4200' });
86
+ expect(ctx.RequestOrigin).toBe('http://localhost:4200');
87
+ });
88
+
89
+ it('is undefined when the request sent no Origin header', async () => {
90
+ const ctx = await contextFor({});
91
+ expect(ctx.RequestOrigin).toBeUndefined();
92
+ });
93
+
94
+ it('is undefined for a malformed or opaque Origin header', async () => {
95
+ expect((await contextFor({ origin: 'null' })).RequestOrigin).toBeUndefined();
96
+ expect((await contextFor({ origin: 'https://a.com/path' })).RequestOrigin).toBeUndefined();
97
+ });
98
+
99
+ it('leaves the existing context fields in place', async () => {
100
+ const ctx = await contextFor({ origin: 'https://a.com' });
101
+ expect(ctx.userPayload).toBe(MOCK_PAYLOAD);
102
+ expect(ctx.ClientIp).toBe('127.0.0.1');
103
+ expect(ctx.providers).toHaveLength(1);
104
+ });
105
+ });