@memberjunction/server 6.2.0-edge.0 → 6.2.0-edge.2

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 (459) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/agentSessions/ReturningVisitorRecap.d.ts +2 -0
  3. package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
  4. package/dist/agentSessions/ReturningVisitorRecap.js +5 -1
  5. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  6. package/dist/agentSessions/SessionManager.js +2 -2
  7. package/dist/agentSessions/remoteBrowserGoalEngine.d.ts +8 -0
  8. package/dist/agentSessions/remoteBrowserGoalEngine.d.ts.map +1 -1
  9. package/dist/agentSessions/remoteBrowserGoalEngine.js +21 -5
  10. package/dist/agentSessions/remoteBrowserGoalEngine.js.map +1 -1
  11. package/dist/auth/AuthProviderCatalogRouter.d.ts +2 -0
  12. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
  13. package/dist/auth/AuthProviderCatalogRouter.js +5 -1
  14. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
  15. package/dist/auth/index.d.ts +20 -1
  16. package/dist/auth/index.d.ts.map +1 -1
  17. package/dist/auth/index.js +20 -10
  18. package/dist/auth/index.js.map +1 -1
  19. package/dist/auth/initializeProviders.d.ts +8 -0
  20. package/dist/auth/initializeProviders.d.ts.map +1 -1
  21. package/dist/auth/initializeProviders.js +21 -5
  22. package/dist/auth/initializeProviders.js.map +1 -1
  23. package/dist/auth/magicLink/MagicLinkRouter.d.ts +9 -0
  24. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  25. package/dist/auth/magicLink/MagicLinkRouter.js +22 -10
  26. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  27. package/dist/auth/magicLink/MagicLinkService.d.ts +17 -15
  28. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  29. package/dist/auth/magicLink/MagicLinkService.js +65 -51
  30. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  31. package/dist/auth/magicLink/index.d.ts +2 -2
  32. package/dist/auth/magicLink/index.d.ts.map +1 -1
  33. package/dist/auth/magicLink/index.js +2 -2
  34. package/dist/auth/magicLink/index.js.map +1 -1
  35. package/dist/auth/magicLink/magicLinkCore.d.ts +54 -33
  36. package/dist/auth/magicLink/magicLinkCore.d.ts.map +1 -1
  37. package/dist/auth/magicLink/magicLinkCore.js +61 -61
  38. package/dist/auth/magicLink/magicLinkCore.js.map +1 -1
  39. package/dist/auth/magicLink/redeemLanding.d.ts +4 -0
  40. package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
  41. package/dist/auth/magicLink/redeemLanding.js +12 -4
  42. package/dist/auth/magicLink/redeemLanding.js.map +1 -1
  43. package/dist/auth/magicLink/types.d.ts +4 -4
  44. package/dist/auth/principals.d.ts +2 -0
  45. package/dist/auth/principals.d.ts.map +1 -1
  46. package/dist/auth/principals.js +6 -2
  47. package/dist/auth/principals.js.map +1 -1
  48. package/dist/cache.d.ts +2 -0
  49. package/dist/cache.d.ts.map +1 -1
  50. package/dist/cache.js +3 -1
  51. package/dist/cache.js.map +1 -1
  52. package/dist/config.d.ts +346 -2
  53. package/dist/config.d.ts.map +1 -1
  54. package/dist/config.js +34 -4
  55. package/dist/config.js.map +1 -1
  56. package/dist/context.d.ts +20 -0
  57. package/dist/context.d.ts.map +1 -1
  58. package/dist/context.js +27 -19
  59. package/dist/context.js.map +1 -1
  60. package/dist/directives/Public.d.ts +2 -0
  61. package/dist/directives/Public.d.ts.map +1 -1
  62. package/dist/directives/Public.js +3 -1
  63. package/dist/directives/Public.js.map +1 -1
  64. package/dist/directives/RequireSystemUser.d.ts +2 -0
  65. package/dist/directives/RequireSystemUser.d.ts.map +1 -1
  66. package/dist/directives/RequireSystemUser.js +3 -1
  67. package/dist/directives/RequireSystemUser.js.map +1 -1
  68. package/dist/generated/graphql-schemas/__mj.d.ts +1021 -0
  69. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  70. package/dist/generated/graphql-schemas/__mj.js +7368 -2145
  71. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  72. package/dist/generic/CacheInvalidationResolver.d.ts +94 -4
  73. package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
  74. package/dist/generic/CacheInvalidationResolver.js +122 -3
  75. package/dist/generic/CacheInvalidationResolver.js.map +1 -1
  76. package/dist/generic/DeleteOptionsInput.d.ts +8 -1
  77. package/dist/generic/DeleteOptionsInput.d.ts.map +1 -1
  78. package/dist/generic/DeleteOptionsInput.js +1 -1
  79. package/dist/generic/DeleteOptionsInput.js.map +1 -1
  80. package/dist/generic/FireAndForgetHeartbeat.d.ts +18 -2
  81. package/dist/generic/FireAndForgetHeartbeat.d.ts.map +1 -1
  82. package/dist/generic/FireAndForgetHeartbeat.js +24 -6
  83. package/dist/generic/FireAndForgetHeartbeat.js.map +1 -1
  84. package/dist/generic/OrphanedConversationDetailReconciler.d.ts +31 -0
  85. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -0
  86. package/dist/generic/OrphanedConversationDetailReconciler.js +215 -0
  87. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -0
  88. package/dist/generic/PushStatusResolver.d.ts +48 -1
  89. package/dist/generic/PushStatusResolver.d.ts.map +1 -1
  90. package/dist/generic/PushStatusResolver.js +95 -4
  91. package/dist/generic/PushStatusResolver.js.map +1 -1
  92. package/dist/generic/ResolverBase.d.ts +18 -2
  93. package/dist/generic/ResolverBase.d.ts.map +1 -1
  94. package/dist/generic/ResolverBase.js +64 -55
  95. package/dist/generic/ResolverBase.js.map +1 -1
  96. package/dist/index.d.ts +9 -0
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +120 -11
  99. package/dist/index.js.map +1 -1
  100. package/dist/integration/CustomColumnPromoter.d.ts +2 -0
  101. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  102. package/dist/integration/CustomColumnPromoter.js +5 -1
  103. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  104. package/dist/integration/EntityMapLifecycle.d.ts +4 -0
  105. package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
  106. package/dist/integration/EntityMapLifecycle.js +10 -2
  107. package/dist/integration/EntityMapLifecycle.js.map +1 -1
  108. package/dist/logging/NoLog.d.ts +4 -0
  109. package/dist/logging/NoLog.d.ts.map +1 -1
  110. package/dist/logging/NoLog.js +10 -2
  111. package/dist/logging/NoLog.js.map +1 -1
  112. package/dist/logging/StartupLogger.d.ts +2 -0
  113. package/dist/logging/StartupLogger.d.ts.map +1 -1
  114. package/dist/logging/StartupLogger.js +5 -1
  115. package/dist/logging/StartupLogger.js.map +1 -1
  116. package/dist/logging/bootAudit.d.ts +11 -7
  117. package/dist/logging/bootAudit.d.ts.map +1 -1
  118. package/dist/logging/bootAudit.js +15 -7
  119. package/dist/logging/bootAudit.js.map +1 -1
  120. package/dist/logging/boundaryLogPayload.d.ts +4 -0
  121. package/dist/logging/boundaryLogPayload.d.ts.map +1 -1
  122. package/dist/logging/boundaryLogPayload.js +5 -1
  123. package/dist/logging/boundaryLogPayload.js.map +1 -1
  124. package/dist/logging/secretRedactor.d.ts +2 -0
  125. package/dist/logging/secretRedactor.d.ts.map +1 -1
  126. package/dist/logging/secretRedactor.js +8 -4
  127. package/dist/logging/secretRedactor.js.map +1 -1
  128. package/dist/logging/shortenForLog.d.ts +2 -0
  129. package/dist/logging/shortenForLog.d.ts.map +1 -1
  130. package/dist/logging/shortenForLog.js +7 -3
  131. package/dist/logging/shortenForLog.js.map +1 -1
  132. package/dist/logging/variablesLoggingMiddleware.d.ts +2 -0
  133. package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
  134. package/dist/logging/variablesLoggingMiddleware.js +8 -6
  135. package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
  136. package/dist/metadataRefreshSignal.d.ts +19 -0
  137. package/dist/metadataRefreshSignal.d.ts.map +1 -0
  138. package/dist/metadataRefreshSignal.js +45 -0
  139. package/dist/metadataRefreshSignal.js.map +1 -0
  140. package/dist/middleware/MJTenantFilterMiddleware.js +4 -4
  141. package/dist/multiTenancy/index.d.ts +8 -0
  142. package/dist/multiTenancy/index.d.ts.map +1 -1
  143. package/dist/multiTenancy/index.js +21 -5
  144. package/dist/multiTenancy/index.js.map +1 -1
  145. package/dist/realtimeWidget/WidgetRouter.d.ts +5 -0
  146. package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
  147. package/dist/realtimeWidget/WidgetRouter.js +14 -10
  148. package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
  149. package/dist/realtimeWidget/WidgetSessionService.d.ts +6 -6
  150. package/dist/realtimeWidget/WidgetSessionService.js +27 -27
  151. package/dist/realtimeWidget/host-identity.d.ts +5 -1
  152. package/dist/realtimeWidget/host-identity.d.ts.map +1 -1
  153. package/dist/realtimeWidget/host-identity.js +11 -3
  154. package/dist/realtimeWidget/host-identity.js.map +1 -1
  155. package/dist/realtimeWidget/index.d.ts +2 -2
  156. package/dist/realtimeWidget/index.d.ts.map +1 -1
  157. package/dist/realtimeWidget/index.js +2 -2
  158. package/dist/realtimeWidget/index.js.map +1 -1
  159. package/dist/realtimeWidget/visitorIdentity.d.ts +21 -1
  160. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
  161. package/dist/realtimeWidget/visitorIdentity.js +16 -4
  162. package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
  163. package/dist/realtimeWidget/widgetCore.d.ts +48 -0
  164. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  165. package/dist/realtimeWidget/widgetCore.js +38 -10
  166. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  167. package/dist/realtimeWidget/widgetGuestElevation.d.ts +10 -6
  168. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  169. package/dist/realtimeWidget/widgetGuestElevation.js +14 -6
  170. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  171. package/dist/resolvers/AdhocQueryResolver.js +3 -3
  172. package/dist/resolvers/AgentRunStatusPublisher.d.ts +55 -0
  173. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -0
  174. package/dist/resolvers/AgentRunStatusPublisher.js +141 -0
  175. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -0
  176. package/dist/resolvers/ConversationRunEventsResolver.d.ts +90 -0
  177. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -0
  178. package/dist/resolvers/ConversationRunEventsResolver.js +283 -0
  179. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -0
  180. package/dist/resolvers/DuplicateEntryCheckResolver.d.ts +98 -0
  181. package/dist/resolvers/DuplicateEntryCheckResolver.d.ts.map +1 -0
  182. package/dist/resolvers/DuplicateEntryCheckResolver.js +227 -0
  183. package/dist/resolvers/DuplicateEntryCheckResolver.js.map +1 -0
  184. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  185. package/dist/resolvers/FetchEntityVectorsResolver.js +2 -1
  186. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  187. package/dist/resolvers/FileResolver.js +3 -3
  188. package/dist/resolvers/GeoResolver.d.ts +2 -2
  189. package/dist/resolvers/GeoResolver.js +4 -4
  190. package/dist/resolvers/GetDataResolver.d.ts +14 -0
  191. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  192. package/dist/resolvers/GetDataResolver.js +43 -15
  193. package/dist/resolvers/GetDataResolver.js.map +1 -1
  194. package/dist/resolvers/IntegrationDiscoveryResolver.js +2 -2
  195. package/dist/resolvers/ListOperationsResolver.d.ts +20 -0
  196. package/dist/resolvers/ListOperationsResolver.d.ts.map +1 -1
  197. package/dist/resolvers/ListOperationsResolver.js +68 -28
  198. package/dist/resolvers/ListOperationsResolver.js.map +1 -1
  199. package/dist/resolvers/MCPResolver.d.ts +11 -1
  200. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  201. package/dist/resolvers/MCPResolver.js +7 -3
  202. package/dist/resolvers/MCPResolver.js.map +1 -1
  203. package/dist/resolvers/MergeRecordsResolver.d.ts +7 -0
  204. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  205. package/dist/resolvers/MergeRecordsResolver.js +16 -2
  206. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  207. package/dist/resolvers/RealtimeBridgeResolver.js +3 -3
  208. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +44 -6
  209. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  210. package/dist/resolvers/RealtimeClientSessionResolver.js +93 -37
  211. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  212. package/dist/resolvers/RemoteBrowserActionResolver.js +5 -5
  213. package/dist/resolvers/RunAIAgentResolver.d.ts +8 -18
  214. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  215. package/dist/resolvers/RunAIAgentResolver.js +85 -184
  216. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  217. package/dist/resolvers/RunAIPromptResolver.d.ts +1 -1
  218. package/dist/resolvers/RunAIPromptResolver.js +2 -2
  219. package/dist/resolvers/RunDecisionResolver.d.ts +149 -0
  220. package/dist/resolvers/RunDecisionResolver.d.ts.map +1 -0
  221. package/dist/resolvers/RunDecisionResolver.js +396 -0
  222. package/dist/resolvers/RunDecisionResolver.js.map +1 -0
  223. package/dist/resolvers/RunTestResolver.js +5 -5
  224. package/dist/resolvers/SqlLoggingConfigResolver.d.ts +8 -4
  225. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  226. package/dist/resolvers/SqlLoggingConfigResolver.js +5 -5
  227. package/dist/resolvers/TaskGraphFrameResolver.d.ts +8 -1
  228. package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
  229. package/dist/resolvers/TaskGraphFrameResolver.js +6 -2
  230. package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
  231. package/dist/resolvers/VectorizeEntityResolver.d.ts +1 -1
  232. package/dist/resolvers/VectorizeEntityResolver.d.ts.map +1 -1
  233. package/dist/resolvers/VectorizeEntityResolver.js +14 -10
  234. package/dist/resolvers/VectorizeEntityResolver.js.map +1 -1
  235. package/dist/resolvers/adhoc-query-helpers.d.ts +6 -0
  236. package/dist/resolvers/adhoc-query-helpers.d.ts.map +1 -1
  237. package/dist/resolvers/adhoc-query-helpers.js +10 -2
  238. package/dist/resolvers/adhoc-query-helpers.js.map +1 -1
  239. package/dist/resolvers/artifact-routing.d.ts +8 -8
  240. package/dist/resolvers/artifact-routing.js +2 -2
  241. package/dist/resolvers/artifact-routing.js.map +1 -1
  242. package/dist/resolvers/meetingRecordingRegistration.d.ts +10 -2
  243. package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
  244. package/dist/resolvers/meetingRecordingRegistration.js +24 -8
  245. package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
  246. package/dist/resolvers/peaksSidecar.d.ts +4 -0
  247. package/dist/resolvers/peaksSidecar.d.ts.map +1 -1
  248. package/dist/resolvers/peaksSidecar.js +10 -2
  249. package/dist/resolvers/peaksSidecar.js.map +1 -1
  250. package/dist/rest/EntityCRUDHandler.d.ts +28 -0
  251. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  252. package/dist/rest/EntityCRUDHandler.js +20 -4
  253. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  254. package/dist/rest/MediaStreamHandler.d.ts +2 -0
  255. package/dist/rest/MediaStreamHandler.d.ts.map +1 -1
  256. package/dist/rest/MediaStreamHandler.js +10 -6
  257. package/dist/rest/MediaStreamHandler.js.map +1 -1
  258. package/dist/rest/OAuthCallbackHandler.d.ts +9 -0
  259. package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
  260. package/dist/rest/OAuthCallbackHandler.js +15 -3
  261. package/dist/rest/OAuthCallbackHandler.js.map +1 -1
  262. package/dist/rest/RESTEndpointHandler.d.ts +6 -4
  263. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  264. package/dist/rest/RESTEndpointHandler.js +15 -11
  265. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  266. package/dist/rest/RealtimeSdpBrokerHandler.d.ts +5 -1
  267. package/dist/rest/RealtimeSdpBrokerHandler.d.ts.map +1 -1
  268. package/dist/rest/RealtimeSdpBrokerHandler.js +12 -4
  269. package/dist/rest/RealtimeSdpBrokerHandler.js.map +1 -1
  270. package/dist/rest/SignatureWebhookHandler.d.ts +2 -0
  271. package/dist/rest/SignatureWebhookHandler.d.ts.map +1 -1
  272. package/dist/rest/SignatureWebhookHandler.js +5 -1
  273. package/dist/rest/SignatureWebhookHandler.js.map +1 -1
  274. package/dist/rest/UploadTokenManager.d.ts +24 -8
  275. package/dist/rest/UploadTokenManager.d.ts.map +1 -1
  276. package/dist/rest/UploadTokenManager.js +53 -21
  277. package/dist/rest/UploadTokenManager.js.map +1 -1
  278. package/dist/rest/ViewOperationsHandler.d.ts +20 -0
  279. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  280. package/dist/rest/ViewOperationsHandler.js +20 -4
  281. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  282. package/dist/rest/mediaRange.d.ts +4 -0
  283. package/dist/rest/mediaRange.d.ts.map +1 -1
  284. package/dist/rest/mediaRange.js +10 -2
  285. package/dist/rest/mediaRange.js.map +1 -1
  286. package/dist/rest/setupRESTEndpoints.d.ts +4 -0
  287. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  288. package/dist/rest/setupRESTEndpoints.js +13 -7
  289. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  290. package/dist/serverExtensionReservedRoots.d.ts +2 -0
  291. package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
  292. package/dist/serverExtensionReservedRoots.js +5 -1
  293. package/dist/serverExtensionReservedRoots.js.map +1 -1
  294. package/dist/services/TaskGraphActionRunner.d.ts +13 -8
  295. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  296. package/dist/services/TaskGraphActionRunner.js +16 -15
  297. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  298. package/dist/types.d.ts +2 -0
  299. package/dist/types.d.ts.map +1 -1
  300. package/dist/types.js +7 -3
  301. package/dist/types.js.map +1 -1
  302. package/dist/util.d.ts +6 -0
  303. package/dist/util.d.ts.map +1 -1
  304. package/dist/util.js +13 -5
  305. package/dist/util.js.map +1 -1
  306. package/package.json +94 -92
  307. package/src/__tests__/AgentRunStatusPublisher.test.ts +175 -0
  308. package/src/__tests__/DeleteOptionsInput.wireShape.test.ts +62 -0
  309. package/src/__tests__/DuplicateEntryCheckResolver.test.ts +300 -0
  310. package/src/__tests__/FileResolverPeaks.test.ts +13 -13
  311. package/src/__tests__/ListOperationsResolver.test.ts +17 -17
  312. package/src/__tests__/MediaStreamHandler.test.ts +28 -28
  313. package/src/__tests__/NoLog.test.ts +11 -11
  314. package/src/__tests__/PrimaryKeyAlwaysMapped.test.ts +10 -10
  315. package/src/__tests__/RESTEndpointHandler.test.ts +14 -14
  316. package/src/__tests__/RealtimeBridgeResolver.test.ts +4 -2
  317. package/src/__tests__/RealtimeClientSessionResolver.test.ts +216 -2
  318. package/src/__tests__/RealtimeSdpBrokerHandler.test.ts +6 -6
  319. package/src/__tests__/RefreshAdoptsNewColumns.test.ts +8 -8
  320. package/src/__tests__/RemoteBrowserActionRecovery.test.ts +7 -1
  321. package/src/__tests__/RemoteBrowserAudioStream.test.ts +7 -1
  322. package/src/__tests__/RemoteBrowserGoalEngine.test.ts +18 -18
  323. package/src/__tests__/RemoteBrowserSnapshot.test.ts +7 -1
  324. package/src/__tests__/ResolverBase.clientOldValues.test.ts +52 -0
  325. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +398 -0
  326. package/src/__tests__/RunAIAgentResolver.streaming.test.ts +18 -27
  327. package/src/__tests__/RunDecisionResolver.test.ts +699 -0
  328. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +131 -0
  329. package/src/__tests__/TaskGraphFrameResolver.test.ts +2 -2
  330. package/src/__tests__/VectorizeEntityResolver.test.ts +106 -0
  331. package/src/__tests__/WidgetSessionService.test.ts +8 -4
  332. package/src/__tests__/adhoc-query-helpers.test.ts +21 -21
  333. package/src/__tests__/bootAudit.test.ts +11 -11
  334. package/src/__tests__/boundaryLogPayload.test.ts +6 -6
  335. package/src/__tests__/cacheInvalidationRecordData.test.ts +177 -0
  336. package/src/__tests__/conversationRunEvents.test.ts +399 -0
  337. package/src/__tests__/databaseAbstraction.test.ts +12 -6
  338. package/src/__tests__/getDataTokenRedaction.test.ts +7 -7
  339. package/src/__tests__/initializeProviders.refresh.test.ts +4 -4
  340. package/src/__tests__/magicLink.test.ts +73 -134
  341. package/src/__tests__/magicLinkService.consume.test.ts +224 -0
  342. package/src/__tests__/meetingRecordingRegistration.test.ts +12 -12
  343. package/src/__tests__/metadataRefreshSignal.test.ts +78 -0
  344. package/src/__tests__/multiTenancy.security.test.ts +40 -40
  345. package/src/__tests__/multiTenancy.test.ts +18 -18
  346. package/src/__tests__/newUsers.test.ts +24 -23
  347. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +283 -0
  348. package/src/__tests__/principals.test.ts +22 -22
  349. package/src/__tests__/pushStatusFanOut.test.ts +206 -0
  350. package/src/__tests__/pushStatusResolver.filter.test.ts +8 -8
  351. package/src/__tests__/secretRedactor.failOpen.test.ts +12 -12
  352. package/src/__tests__/secretRedactor.test.ts +12 -12
  353. package/src/__tests__/serverExtensionReservedRoots.test.ts +7 -7
  354. package/src/__tests__/subscriptionRedaction.test.ts +5 -5
  355. package/src/__tests__/unifiedAuth.test.ts +15 -9
  356. package/src/__tests__/visitorIdentity.resolve.test.ts +6 -6
  357. package/src/__tests__/widget.test.ts +45 -45
  358. package/src/__tests__/widgetGuestElevation.test.ts +7 -7
  359. package/src/__tests__/widgetHostIdentity.test.ts +14 -14
  360. package/src/agentSessions/ReturningVisitorRecap.ts +12 -1
  361. package/src/agentSessions/SessionManager.ts +2 -2
  362. package/src/agentSessions/remoteBrowserGoalEngine.ts +30 -5
  363. package/src/auth/AuthProviderCatalogRouter.ts +6 -1
  364. package/src/auth/__tests__/backward-compatibility.test.ts +4 -3
  365. package/src/auth/index.ts +43 -28
  366. package/src/auth/initializeProviders.ts +25 -5
  367. package/src/auth/magicLink/MagicLinkRouter.ts +28 -10
  368. package/src/auth/magicLink/MagicLinkService.ts +71 -52
  369. package/src/auth/magicLink/index.ts +7 -7
  370. package/src/auth/magicLink/magicLinkCore.ts +96 -64
  371. package/src/auth/magicLink/redeemLanding.ts +14 -4
  372. package/src/auth/magicLink/types.ts +54 -54
  373. package/src/auth/newUsers.ts +1 -1
  374. package/src/auth/principals.ts +11 -2
  375. package/src/cache.ts +4 -1
  376. package/src/config.ts +37 -4
  377. package/src/context.ts +37 -24
  378. package/src/directives/Public.ts +4 -1
  379. package/src/directives/RequireSystemUser.ts +4 -1
  380. package/src/generated/graphql-schemas/__mj.ts +3674 -4
  381. package/src/generic/CacheInvalidationResolver.ts +148 -4
  382. package/src/generic/DeleteOptionsInput.ts +9 -2
  383. package/src/generic/FireAndForgetHeartbeat.ts +35 -16
  384. package/src/generic/OrphanedConversationDetailReconciler.ts +267 -0
  385. package/src/generic/PushStatusResolver.ts +118 -8
  386. package/src/generic/ResolverBase.ts +65 -57
  387. package/src/generic/RunViewResolver.ts +22 -22
  388. package/src/generic/__tests__/FireAndForgetHeartbeat.test.ts +7 -7
  389. package/src/index.ts +137 -11
  390. package/src/integration/CustomColumnPromoter.ts +6 -1
  391. package/src/integration/EntityMapLifecycle.ts +20 -2
  392. package/src/logging/NoLog.ts +16 -2
  393. package/src/logging/StartupLogger.ts +6 -1
  394. package/src/logging/bootAudit.ts +26 -16
  395. package/src/logging/boundaryLogPayload.ts +8 -1
  396. package/src/logging/secretRedactor.ts +14 -9
  397. package/src/logging/shortenForLog.ts +8 -3
  398. package/src/logging/variablesLoggingMiddleware.ts +9 -6
  399. package/src/metadataRefreshSignal.ts +45 -0
  400. package/src/middleware/MJTenantFilterMiddleware.ts +4 -4
  401. package/src/multiTenancy/index.ts +29 -5
  402. package/src/realtimeWidget/WidgetRouter.ts +15 -10
  403. package/src/realtimeWidget/WidgetSessionService.ts +76 -76
  404. package/src/realtimeWidget/host-identity.ts +24 -10
  405. package/src/realtimeWidget/index.ts +3 -3
  406. package/src/realtimeWidget/visitorIdentity.ts +39 -8
  407. package/src/realtimeWidget/widgetCore.ts +75 -10
  408. package/src/realtimeWidget/widgetGuestElevation.ts +25 -12
  409. package/src/resolvers/AdhocQueryResolver.ts +3 -3
  410. package/src/resolvers/AgentRunStatusPublisher.ts +175 -0
  411. package/src/resolvers/AgentSessionResolver.ts +3 -3
  412. package/src/resolvers/ColorResolver.ts +6 -6
  413. package/src/resolvers/ComponentRegistryResolver.ts +36 -36
  414. package/src/resolvers/ConversationRunEventsResolver.ts +308 -0
  415. package/src/resolvers/DuplicateEntryCheckResolver.ts +232 -0
  416. package/src/resolvers/EntityCommunicationsResolver.ts +4 -4
  417. package/src/resolvers/ExecuteRemoteOperationResolver.ts +9 -9
  418. package/src/resolvers/FetchEntityVectorsResolver.ts +2 -1
  419. package/src/resolvers/FileResolver.ts +42 -42
  420. package/src/resolvers/GeoResolver.ts +4 -4
  421. package/src/resolvers/GetDataResolver.ts +50 -15
  422. package/src/resolvers/IntegrationDiscoveryResolver.ts +2 -2
  423. package/src/resolvers/ListOperationsResolver.ts +78 -28
  424. package/src/resolvers/MCPResolver.ts +30 -15
  425. package/src/resolvers/MergeRecordsResolver.ts +14 -2
  426. package/src/resolvers/QueryResolver.ts +13 -13
  427. package/src/resolvers/RealtimeBridgeResolver.ts +3 -3
  428. package/src/resolvers/RealtimeClientSessionResolver.ts +96 -38
  429. package/src/resolvers/RemoteBrowserActionResolver.ts +5 -5
  430. package/src/resolvers/RunAIAgentResolver.ts +134 -239
  431. package/src/resolvers/RunAIPromptResolver.ts +22 -22
  432. package/src/resolvers/RunDecisionResolver.ts +435 -0
  433. package/src/resolvers/RunTemplateResolver.ts +4 -4
  434. package/src/resolvers/RunTestResolver.ts +25 -25
  435. package/src/resolvers/SqlLoggingConfigResolver.ts +56 -56
  436. package/src/resolvers/TaskGraphFrameResolver.ts +37 -28
  437. package/src/resolvers/TelemetryResolver.ts +79 -79
  438. package/src/resolvers/VectorizeEntityResolver.ts +16 -11
  439. package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +40 -38
  440. package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +93 -0
  441. package/src/resolvers/__tests__/artifact-routing.test.ts +23 -23
  442. package/src/resolvers/adhoc-query-helpers.ts +19 -2
  443. package/src/resolvers/artifact-routing.ts +19 -19
  444. package/src/resolvers/meetingRecordingRegistration.ts +44 -10
  445. package/src/resolvers/peaksSidecar.ts +12 -2
  446. package/src/rest/EntityCRUDHandler.ts +24 -4
  447. package/src/rest/MediaStreamHandler.ts +11 -6
  448. package/src/rest/OAuthCallbackHandler.ts +25 -7
  449. package/src/rest/RESTEndpointHandler.ts +20 -15
  450. package/src/rest/RealtimeSdpBrokerHandler.ts +14 -4
  451. package/src/rest/SignatureWebhookHandler.ts +6 -1
  452. package/src/rest/UploadTokenManager.ts +66 -30
  453. package/src/rest/ViewOperationsHandler.ts +24 -4
  454. package/src/rest/mediaRange.ts +16 -6
  455. package/src/rest/setupRESTEndpoints.ts +24 -12
  456. package/src/serverExtensionReservedRoots.ts +6 -1
  457. package/src/services/TaskGraphActionRunner.ts +16 -15
  458. package/src/types.ts +21 -16
  459. package/src/util.ts +16 -6
@@ -0,0 +1,399 @@
1
+ /**
2
+ * Tests for TailConversationEvents — the durable, resumable read of an agent run's
3
+ * progress (MJ #4222).
4
+ *
5
+ * The live `statusUpdates` subscription has no replay, so anything published while a
6
+ * socket is half-open is lost and the client cannot tell "nothing happened" from "I
7
+ * missed everything". This query is the durable counterpart. The properties that make
8
+ * it safe to rely on are pinned here: a cursor that never rewinds, a terminal payload
9
+ * that is never handed over early, and an authorization answer that does not leak
10
+ * whether a record exists.
11
+ *
12
+ * Mocking is at the package boundary — `@memberjunction/core`'s RunView, which is the
13
+ * only I/O the resolver performs. The resolver itself runs unmodified.
14
+ */
15
+ import 'reflect-metadata';
16
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
17
+ import type { RunViewParams, UserInfo } from '@memberjunction/core';
18
+
19
+ const { mockRunView, providersUsed } = vi.hoisted(() => ({ mockRunView: vi.fn(), providersUsed: [] as unknown[] }));
20
+
21
+ // RunView is the resolver's only runtime dependency on core — every other import it
22
+ // makes is type-only and erased at compile time, so a narrow mock is sufficient and
23
+ // keeps the real package (which loads config at import) out of the test.
24
+ vi.mock('@memberjunction/core', () => ({
25
+ RunView: class MockRunView {
26
+ constructor(provider?: unknown) {
27
+ providersUsed.push(provider);
28
+ }
29
+ public static FromMetadataProvider(provider: unknown): MockRunView {
30
+ return new MockRunView(provider);
31
+ }
32
+ public async RunView(params: RunViewParams, contextUser?: UserInfo): Promise<unknown> {
33
+ return mockRunView(params, contextUser);
34
+ }
35
+ },
36
+ }));
37
+
38
+ import { ConversationRunEventsResolver } from '../resolvers/ConversationRunEventsResolver';
39
+
40
+ const DETAIL_ID = 'A1111111-1111-1111-1111-111111111111';
41
+ const RUN_ID = 'B2222222-2222-2222-2222-222222222222';
42
+ const USER = { ID: 'U0000000-0000-0000-0000-000000000000', Email: 'someone@example.com' } as unknown as UserInfo;
43
+
44
+ const READ_WRITE_PROVIDER = { name: 'read-write' };
45
+ const READ_ONLY_PROVIDER = { name: 'read-only' };
46
+ const ctxFor = (user?: UserInfo) =>
47
+ ({
48
+ userPayload: { userRecord: user },
49
+ providers: [
50
+ { type: 'Read-Write', provider: READ_WRITE_PROVIDER },
51
+ { type: 'Read-Only', provider: READ_ONLY_PROVIDER },
52
+ ],
53
+ }) as never;
54
+
55
+ /** Scripts RunView per entity so each test states only what it cares about. */
56
+ function scriptRunView(opts: {
57
+ detail?: Record<string, unknown> | null;
58
+ run?: Record<string, unknown> | null;
59
+ steps?: Record<string, unknown>[];
60
+ }) {
61
+ mockRunView.mockImplementation(async (params: RunViewParams) => {
62
+ if (params.EntityName === 'MJ: Conversation Details') {
63
+ return { Success: true, Results: opts.detail ? [opts.detail] : [] };
64
+ }
65
+ if (params.EntityName === 'MJ: AI Agent Runs') {
66
+ return { Success: true, Results: opts.run ? [opts.run] : [] };
67
+ }
68
+ if (params.EntityName === 'MJ: AI Agent Run Steps') {
69
+ return { Success: true, Results: opts.steps ?? [] };
70
+ }
71
+ throw new Error(`unexpected entity ${params.EntityName}`);
72
+ });
73
+ }
74
+
75
+ const step = (seq: number, over: Record<string, unknown> = {}) => ({
76
+ StepNumber: seq,
77
+ StepType: 'Actions',
78
+ StepName: `Step ${seq}`,
79
+ Status: 'Completed',
80
+ Success: true,
81
+ StartedAt: new Date('2026-09-16T04:55:12Z'),
82
+ CompletedAt: new Date('2026-09-16T04:55:21Z'),
83
+ ...over,
84
+ });
85
+
86
+ describe('TailConversationEvents', () => {
87
+ let resolver: ConversationRunEventsResolver;
88
+
89
+ beforeEach(() => {
90
+ vi.clearAllMocks();
91
+ providersUsed.length = 0;
92
+ resolver = new ConversationRunEventsResolver();
93
+ });
94
+
95
+ describe('provider', () => {
96
+ it("reads through the request's read-only provider, not the process-wide one", async () => {
97
+ // The global provider is shared by every request, including its transaction state.
98
+ scriptRunView({
99
+ detail: { ID: DETAIL_ID, Status: 'In-Progress' },
100
+ run: { ID: RUN_ID, Status: 'Running' },
101
+ steps: [],
102
+ });
103
+
104
+ await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
105
+
106
+ expect(providersUsed.length).toBe(3);
107
+ expect(providersUsed.every((p) => p === READ_ONLY_PROVIDER)).toBe(true);
108
+ });
109
+ });
110
+
111
+ describe('columns read', () => {
112
+ it('reads only the step columns an event carries', async () => {
113
+ // A step row holds InputData, OutputData and payload snapshots — the largest columns
114
+ // in the table. Loading 200 of them to return eight small fields wastes MJAPI memory.
115
+ scriptRunView({ detail: { ID: DETAIL_ID }, run: { ID: RUN_ID, Status: 'Running' }, steps: [] });
116
+ await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
117
+
118
+ const stepCall = mockRunView.mock.calls.find((c) => c[0].EntityName === 'MJ: AI Agent Run Steps');
119
+ expect(stepCall?.[0].ResultType).toBe('simple');
120
+ expect([...(stepCall?.[0].Fields ?? [])].sort()).toEqual(
121
+ ['CompletedAt', 'ErrorMessage', 'StartedAt', 'Status', 'StepName', 'StepNumber', 'StepType', 'Success']
122
+ );
123
+ });
124
+
125
+ it('reads only the run columns it uses', async () => {
126
+ scriptRunView({ detail: { ID: DETAIL_ID }, run: { ID: RUN_ID, Status: 'Running' }, steps: [] });
127
+ await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
128
+
129
+ const runCall = mockRunView.mock.calls.find((c) => c[0].EntityName === 'MJ: AI Agent Runs');
130
+ expect(runCall?.[0].ResultType).toBe('simple');
131
+ expect([...(runCall?.[0].Fields ?? [])].sort()).toEqual(['ID', 'Result', 'Status']);
132
+ });
133
+
134
+ it('returns real dates when a plain row carries them as strings', async () => {
135
+ scriptRunView({
136
+ detail: { ID: DETAIL_ID },
137
+ run: { ID: RUN_ID, Status: 'Running' },
138
+ steps: [step(1, { StartedAt: '2026-09-16T04:55:12.000Z', CompletedAt: '2026-09-16T04:55:21.000Z' })],
139
+ });
140
+ const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
141
+
142
+ expect(out.Events[0].StartedAt).toBeInstanceOf(Date);
143
+ expect(out.Events[0].StartedAt.toISOString()).toBe('2026-09-16T04:55:12.000Z');
144
+ expect(out.Events[0].CompletedAt).toBeInstanceOf(Date);
145
+ });
146
+ });
147
+
148
+ describe('authorization', () => {
149
+ it('refuses an unauthenticated caller', async () => {
150
+ const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(undefined), 0);
151
+ expect(out.Success).toBe(false);
152
+ expect(out.Message).toMatch(/not authenticated/i);
153
+ expect(mockRunView).not.toHaveBeenCalled();
154
+ });
155
+
156
+ it('reads the conversation detail AS THE CALLING USER, so row-level security applies', async () => {
157
+ scriptRunView({ detail: { ID: DETAIL_ID, Status: 'In-Progress' }, run: null });
158
+ await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
159
+
160
+ const [params, contextUser] = mockRunView.mock.calls[0];
161
+ expect(params.EntityName).toBe('MJ: Conversation Details');
162
+ // Passing the user is the entire authorization mechanism. A system user here
163
+ // would silently expose every conversation in the instance.
164
+ expect(contextUser).toBe(USER);
165
+ });
166
+
167
+ it('bypasses the server RunView cache on every read', async () => {
168
+ // This is the last-resort recovery path, and the rows it judges are written by
169
+ // things that never fire a cache invalidation: `spSweepStaleAIAgentRuns` force-fails
170
+ // a run with a direct set-based UPDATE. `TrustServerCacheCompletely` defaults on, so
171
+ // a cached read here would keep reporting the run as Running and the recovery would
172
+ // never fire — the exact failure this resolver exists to end.
173
+ scriptRunView({
174
+ detail: { ID: DETAIL_ID, Status: 'In-Progress' },
175
+ run: { ID: RUN_ID, Status: 'Completed' },
176
+ steps: [],
177
+ });
178
+
179
+ await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
180
+
181
+ expect(mockRunView.mock.calls.length).toBeGreaterThan(0);
182
+ for (const [params] of mockRunView.mock.calls) {
183
+ expect(params.BypassCache, `${params.EntityName} must bypass the cache`).toBe(true);
184
+ }
185
+ });
186
+
187
+ it('gives an unreadable detail the same answer as a missing one', async () => {
188
+ scriptRunView({ detail: null });
189
+ const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
190
+
191
+ // "Exists but denied" would confirm the id to someone probing for one.
192
+ expect(out.Success).toBe(false);
193
+ expect(out.Message).toMatch(/not found/i);
194
+ expect(out.Events).toEqual([]);
195
+ });
196
+ });
197
+
198
+ describe('cursor behavior', () => {
199
+ it('asks only for steps beyond the caller cursor', async () => {
200
+ scriptRunView({ detail: { ID: DETAIL_ID }, run: { ID: RUN_ID, Status: 'Running' }, steps: [] });
201
+ await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 7);
202
+
203
+ const stepCall = mockRunView.mock.calls.find((c) => c[0].EntityName === 'MJ: AI Agent Run Steps');
204
+ expect(stepCall?.[0].ExtraFilter).toContain('StepNumber > 7');
205
+ expect(stepCall?.[0].OrderBy).toBe('StepNumber ASC');
206
+ });
207
+
208
+ it('never rewinds the cursor when nothing new has landed', async () => {
209
+ scriptRunView({ detail: { ID: DETAIL_ID }, run: { ID: RUN_ID, Status: 'Running' }, steps: [] });
210
+ const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 12);
211
+
212
+ // Returning 0 here would make a polling client replay the whole run every tick.
213
+ expect(out.LatestSeq).toBe(12);
214
+ expect(out.Events).toEqual([]);
215
+ });
216
+
217
+ it('advances the cursor to the last event returned', async () => {
218
+ scriptRunView({
219
+ detail: { ID: DETAIL_ID },
220
+ run: { ID: RUN_ID, Status: 'Running' },
221
+ steps: [step(3), step(4), step(5)],
222
+ });
223
+ const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 2);
224
+
225
+ expect(out.Events.map((e) => e.Seq)).toEqual([3, 4, 5]);
226
+ expect(out.LatestSeq).toBe(5);
227
+ });
228
+
229
+ it('treats a negative cursor as the start of the run', async () => {
230
+ scriptRunView({ detail: { ID: DETAIL_ID }, run: { ID: RUN_ID, Status: 'Running' }, steps: [] });
231
+ const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), -5);
232
+
233
+ const stepCall = mockRunView.mock.calls.find((c) => c[0].EntityName === 'MJ: AI Agent Run Steps');
234
+ expect(stepCall?.[0].ExtraFilter).toContain('StepNumber > 0');
235
+ expect(out.LatestSeq).toBe(0);
236
+ });
237
+
238
+ it('bounds how much one call can return', async () => {
239
+ scriptRunView({ detail: { ID: DETAIL_ID }, run: { ID: RUN_ID, Status: 'Running' }, steps: [] });
240
+ await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
241
+
242
+ // A run can hold hundreds of steps. Unbounded, one tail could return tens of
243
+ // megabytes — the same failure class as #4539.
244
+ const stepCall = mockRunView.mock.calls.find((c) => c[0].EntityName === 'MJ: AI Agent Run Steps');
245
+ expect(stepCall?.[0].MaxRows).toBe(200);
246
+ });
247
+ });
248
+
249
+ describe('run lifecycle', () => {
250
+ it('reports an early poll as success, not failure, when the run row does not exist yet', async () => {
251
+ scriptRunView({ detail: { ID: DETAIL_ID, Status: 'In-Progress' }, run: null });
252
+ const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
253
+
254
+ // Fire-and-forget acks before the run row is written, so this is an ordinary
255
+ // race. Reporting failure would make a client abandon a run that is about to start.
256
+ expect(out.Success).toBe(true);
257
+ expect(out.IsInFlight).toBe(false);
258
+ expect(out.DetailStatus).toBe('In-Progress');
259
+ });
260
+
261
+ it('marks a Running run in flight and withholds the payload', async () => {
262
+ scriptRunView({
263
+ detail: { ID: DETAIL_ID, Status: 'In-Progress' },
264
+ run: { ID: RUN_ID, Status: 'Running', Result: '{"partial":true}' },
265
+ steps: [step(1)],
266
+ });
267
+ const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
268
+
269
+ expect(out.IsInFlight).toBe(true);
270
+ // A partial Result mid-run could be mistaken for the final answer.
271
+ expect(out.FinalPayload).toBeUndefined();
272
+ });
273
+
274
+ it('releases the payload once the run is terminal, so one call ends the wait', async () => {
275
+ scriptRunView({
276
+ detail: { ID: DETAIL_ID, Status: 'Complete' },
277
+ run: { ID: RUN_ID, Status: 'Completed', Result: '{"answer":"done"}' },
278
+ steps: [step(1)],
279
+ });
280
+ const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
281
+
282
+ expect(out.IsInFlight).toBe(false);
283
+ expect(out.FinalPayload).toBe('{"answer":"done"}');
284
+ expect(out.RunStatus).toBe('Completed');
285
+ expect(out.DetailStatus).toBe('Complete');
286
+ });
287
+
288
+ it('treats a failed run as terminal so a client stops tailing', async () => {
289
+ scriptRunView({
290
+ detail: { ID: DETAIL_ID, Status: 'Error' },
291
+ run: { ID: RUN_ID, Status: 'Failed', Result: null },
292
+ steps: [step(1, { Status: 'Failed', Success: false, ErrorMessage: 'boom' })],
293
+ });
294
+ const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
295
+
296
+ expect(out.IsInFlight).toBe(false);
297
+ expect(out.Events[0].ErrorMessage).toBe('boom');
298
+ });
299
+
300
+ it('treats a Paused run as in flight, because it is parked on a workflow that is still running', async () => {
301
+ // The orphan reconciler and the process panel both treat Paused as not finished.
302
+ // Reporting it as terminal would tell a client to complete a message mid-workflow.
303
+ scriptRunView({
304
+ detail: { ID: DETAIL_ID, Status: 'In-Progress' },
305
+ run: { ID: RUN_ID, Status: 'Paused', Result: '{"started":"workflow"}' },
306
+ steps: [step(1)],
307
+ });
308
+ const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
309
+
310
+ expect(out.IsInFlight).toBe(true);
311
+ expect(out.FinalPayload).toBeUndefined();
312
+ });
313
+
314
+ it('selects the NEWEST run, so a retried detail does not resolve from a dead run', async () => {
315
+ scriptRunView({ detail: { ID: DETAIL_ID }, run: { ID: RUN_ID, Status: 'Running' }, steps: [] });
316
+ await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
317
+
318
+ const runCall = mockRunView.mock.calls.find((c) => c[0].EntityName === 'MJ: AI Agent Runs');
319
+ expect(runCall?.[0].OrderBy).toMatch(/DESC/i);
320
+ expect(runCall?.[0].MaxRows).toBe(1);
321
+ });
322
+ });
323
+
324
+ describe('robustness', () => {
325
+ it('returns a failure result rather than throwing when a read blows up', async () => {
326
+ mockRunView.mockRejectedValue(new Error('db unavailable'));
327
+ const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 4);
328
+
329
+ // This is a recovery path. Throwing would break the very reconciliation it exists
330
+ // to serve, and would rewind the caller's cursor.
331
+ expect(out.Success).toBe(false);
332
+ expect(out.Message).toMatch(/db unavailable/);
333
+ expect(out.LatestSeq).toBe(4);
334
+ });
335
+
336
+ it('reports a failed read as still in flight, matching the client', async () => {
337
+ // A failure knows nothing about the run. "Not in flight" would claim it is over.
338
+ mockRunView.mockRejectedValue(new Error('db unavailable'));
339
+ const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 4);
340
+
341
+ expect(out.IsInFlight).toBe(true);
342
+ });
343
+
344
+ it('escapes quotes in the id instead of interpolating them into the filter', async () => {
345
+ scriptRunView({ detail: null });
346
+ await resolver.TailConversationEvents("x' OR '1'='1", ctxFor(USER), 0);
347
+
348
+ const params = mockRunView.mock.calls[0][0];
349
+ expect(params.ExtraFilter).toContain("''");
350
+ expect(params.ExtraFilter).not.toMatch(/ID='x' OR '1'='1'/);
351
+ });
352
+ });
353
+ });
354
+
355
+ /**
356
+ * REGRESSION, found in manual testing. `FinalPayload` read only `AIAgentRun.Result`, which is
357
+ * agent-dependent — null on 16 of 40 completed runs in a real database, including every run of the
358
+ * Loop-type agent used to reproduce #4222. A caller waiting on this field to decide a message was
359
+ * finished would wait forever on those agents.
360
+ */
361
+ describe('TailConversationEvents FinalPayload sourcing', () => {
362
+ it('falls back to the conversation detail message when the run has no Result', async () => {
363
+ scriptRunView({
364
+ detail: { ID: DETAIL_ID, Status: 'Complete', Message: 'the answer text' },
365
+ run: { ID: RUN_ID, Status: 'Completed', Result: null },
366
+ steps: [],
367
+ });
368
+
369
+ const result = await new ConversationRunEventsResolver().TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
370
+
371
+ expect(result.IsInFlight).toBe(false);
372
+ expect(result.FinalPayload).toBe('the answer text');
373
+ });
374
+
375
+ it('prefers the run Result when the agent does write one', async () => {
376
+ scriptRunView({
377
+ detail: { ID: DETAIL_ID, Status: 'Complete', Message: 'detail message' },
378
+ run: { ID: RUN_ID, Status: 'Completed', Result: 'run result' },
379
+ steps: [],
380
+ });
381
+
382
+ const result = await new ConversationRunEventsResolver().TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
383
+
384
+ expect(result.FinalPayload).toBe('run result');
385
+ });
386
+
387
+ it('still withholds the payload while the run is in flight', async () => {
388
+ scriptRunView({
389
+ detail: { ID: DETAIL_ID, Status: 'In-Progress', Message: 'partial text' },
390
+ run: { ID: RUN_ID, Status: 'Running', Result: null },
391
+ steps: [],
392
+ });
393
+
394
+ const result = await new ConversationRunEventsResolver().TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
395
+
396
+ expect(result.IsInFlight).toBe(true);
397
+ expect(result.FinalPayload).toBeUndefined();
398
+ });
399
+ });
@@ -66,11 +66,16 @@ vi.mock('mssql', () => {
66
66
  // We must also mock the auth module and its transitive deps so that importing
67
67
  // individual source files does not trigger type-graphql or reflect-metadata.
68
68
  vi.mock('../auth/index', () => ({
69
- getSigningKeys: vi.fn(),
70
- getSystemUser: vi.fn(),
71
- getValidationOptions: vi.fn(),
72
- verifyUserRecord: vi.fn(),
73
- extractUserInfoFromPayload: vi.fn(),
69
+ GetSigningKeys: vi.fn(),
70
+ get getSigningKeys() { return this.GetSigningKeys; },
71
+ GetSystemUser: vi.fn(),
72
+ get getSystemUser() { return this.GetSystemUser; },
73
+ GetValidationOptions: vi.fn(),
74
+ get getValidationOptions() { return this.GetValidationOptions; },
75
+ VerifyUserRecord: vi.fn(),
76
+ get verifyUserRecord() { return this.VerifyUserRecord; },
77
+ ExtractUserInfoFromPayload: vi.fn(),
78
+ get extractUserInfoFromPayload() { return this.ExtractUserInfoFromPayload; },
74
79
  TokenExpiredError: class TokenExpiredError extends Error {},
75
80
  AuthProviderFactory: { getInstance: vi.fn(() => ({ getByIssuer: vi.fn() })) },
76
81
  IAuthProvider: {},
@@ -82,7 +87,8 @@ vi.mock('../auth/AuthProviderFactory', () => ({
82
87
  }));
83
88
 
84
89
  vi.mock('../cache', () => ({
85
- authCache: new Map(),
90
+ AuthCache: new Map(),
91
+ get authCache() { return this.AuthCache; },
86
92
  }));
87
93
 
88
94
  vi.mock('@memberjunction/core', () => ({
@@ -1,8 +1,8 @@
1
1
  import 'reflect-metadata';
2
2
  import { describe, it, expect } from 'vitest';
3
3
  import type { IMetadataProvider } from '@memberjunction/core';
4
- import { redactArg } from '../logging/secretRedactor.js';
5
- import { NoLog, getNoLogFields } from '../logging/NoLog.js';
4
+ import { RedactArg } from '../logging/secretRedactor.js';
5
+ import { NoLog, GetNoLogFields } from '../logging/NoLog.js';
6
6
 
7
7
  /**
8
8
  * Test 4 (unit form): the `@NoLog`-decorated `Token` field on GetDataInputType is redacted
@@ -27,7 +27,7 @@ describe('Test 4: GetData Token redaction via field-level @NoLog', () => {
27
27
  const provider = { Entities: [] } as unknown as IMetadataProvider;
28
28
 
29
29
  it('getNoLogFields picks up the Token field mark', () => {
30
- const fields = getNoLogFields(GetDataInputType);
30
+ const fields = GetNoLogFields(GetDataInputType);
31
31
  expect(fields.has('Token')).toBe(true);
32
32
  expect(fields.has('Queries')).toBe(false);
33
33
  });
@@ -40,12 +40,12 @@ describe('Test 4: GetData Token redaction via field-level @NoLog', () => {
40
40
  Entities: [{ ClassName: 'Something', EncryptedFields: [] }],
41
41
  } as unknown as IMetadataProvider;
42
42
 
43
- const result = redactArg({
43
+ const result = RedactArg({
44
44
  inputTypeName: 'GetDataInput', // not entity-bound — regex won't match
45
45
  rawValue: { Token: 'FAKE_SYSTEM_TOKEN_DO_NOT_USE', Queries: ['SELECT 1', 'SELECT 2'] },
46
46
  provider: providerWithEntities,
47
47
  noLogParameter: false,
48
- noLogFields: getNoLogFields(GetDataInputType),
48
+ noLogFields: GetNoLogFields(GetDataInputType),
49
49
  });
50
50
 
51
51
  expect(result).toEqual({ Token: '<redacted>', Queries: ['SELECT 1', 'SELECT 2'] });
@@ -58,7 +58,7 @@ describe('Test 4: GetData Token redaction via field-level @NoLog', () => {
58
58
  Entities: [{ ClassName: 'GetDataThing', EncryptedFields: [] }],
59
59
  } as unknown as IMetadataProvider;
60
60
 
61
- const result = redactArg({
61
+ const result = RedactArg({
62
62
  inputTypeName: 'CreateGetDataThingInput',
63
63
  rawValue: { Token: 'FAKE_SYSTEM_TOKEN_DO_NOT_USE', Queries: ['SELECT 1'] },
64
64
  provider: providerWithEntity,
@@ -72,7 +72,7 @@ describe('Test 4: GetData Token redaction via field-level @NoLog', () => {
72
72
 
73
73
  it('parameter-level @NoLog masks the entire arg regardless of shape', () => {
74
74
  // If Token were a top-level @Arg with @NoLog (parameter form), noLogParameter=true masks it whole.
75
- const result = redactArg({
75
+ const result = RedactArg({
76
76
  inputTypeName: 'String',
77
77
  rawValue: 'FAKE_SYSTEM_TOKEN_DO_NOT_USE',
78
78
  provider,
@@ -78,7 +78,7 @@ vi.mock('@memberjunction/core', () => {
78
78
  });
79
79
 
80
80
  import { AuthProviderEngine } from '../auth/AuthProviderEngine.js';
81
- import { refreshAuthProviders } from '../auth/initializeProviders.js';
81
+ import { RefreshAuthProviders } from '../auth/initializeProviders.js';
82
82
 
83
83
  /** Minimal stand-in for a generated MJAuthenticationProviderEntity row. */
84
84
  function metadataRow(name: string, driverClass: string) {
@@ -106,7 +106,7 @@ beforeEach(() => {
106
106
 
107
107
  describe('refreshAuthProviders', () => {
108
108
  it('re-registers the config-declared providers after clearing, so a refresh cannot lock config-path users out', async () => {
109
- await refreshAuthProviders();
109
+ await RefreshAuthProviders();
110
110
 
111
111
  expect(clearMock).toHaveBeenCalledTimes(1);
112
112
  expect(createProviderMock).toHaveBeenCalledWith(expect.objectContaining({ name: 'config-okta' }));
@@ -124,7 +124,7 @@ describe('refreshAuthProviders', () => {
124
124
  });
125
125
 
126
126
  try {
127
- await expect(refreshAuthProviders()).rejects.toThrow('PermissionConstrainedError');
127
+ await expect(RefreshAuthProviders()).rejects.toThrow('PermissionConstrainedError');
128
128
  expect(clearMock).not.toHaveBeenCalled();
129
129
  expect(registerMock).not.toHaveBeenCalled();
130
130
  } finally {
@@ -135,7 +135,7 @@ describe('refreshAuthProviders', () => {
135
135
  it('layers the metadata rows on top of the config-declared providers, in that order', async () => {
136
136
  (AuthProviderEngine.Instance as unknown as { _providers: unknown[] })._providers = [metadataRow('Catalog WorkOS', 'workos')];
137
137
 
138
- const count = await refreshAuthProviders();
138
+ const count = await RefreshAuthProviders();
139
139
 
140
140
  expect(count).toBe(1);
141
141
  expect(registerMock).toHaveBeenCalledTimes(2);