@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
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { selectFieldsToMap } from '../integration/EntityMapLifecycle.js';
2
+ import { SelectFieldsToMap } from '../integration/EntityMapLifecycle.js';
3
3
 
4
4
  /**
5
5
  * Deselecting the primary key must not cost the object its identity.
@@ -18,41 +18,41 @@ const names = (out: Array<{ Name: string }>) => out.map(x => x.Name);
18
18
 
19
19
  describe('selectFieldsToMap — the primary key is never optional', () => {
20
20
  it('includes the key even when the user did not select it', () => {
21
- expect(names(selectFieldsToMap(ALL, ['name', 'email']))).toEqual(['id', 'name', 'email']);
21
+ expect(names(SelectFieldsToMap(ALL, ['name', 'email']))).toEqual(['id', 'name', 'email']);
22
22
  });
23
23
 
24
24
  it('includes the key when the selection is EMPTY — an empty choice is still a choice', () => {
25
25
  // Distinct from "no selection". An empty array is unusual but real, and it still cannot
26
26
  // produce a keyless map.
27
- expect(names(selectFieldsToMap(ALL, []))).toEqual(['id']);
27
+ expect(names(SelectFieldsToMap(ALL, []))).toEqual(['id']);
28
28
  });
29
29
 
30
30
  it('does not duplicate the key when it IS selected', () => {
31
- expect(names(selectFieldsToMap(ALL, ['id', 'name']))).toEqual(['id', 'name']);
31
+ expect(names(SelectFieldsToMap(ALL, ['id', 'name']))).toEqual(['id', 'name']);
32
32
  });
33
33
 
34
34
  it('null or undefined selection means every field', () => {
35
- expect(names(selectFieldsToMap(ALL, null))).toEqual(['id', 'name', 'email', 'notes']);
36
- expect(names(selectFieldsToMap(ALL, undefined))).toEqual(['id', 'name', 'email', 'notes']);
35
+ expect(names(SelectFieldsToMap(ALL, null))).toEqual(['id', 'name', 'email', 'notes']);
36
+ expect(names(SelectFieldsToMap(ALL, undefined))).toEqual(['id', 'name', 'email', 'notes']);
37
37
  });
38
38
 
39
39
  it('keeps every part of a COMPOSITE key, selected or not', () => {
40
40
  const composite = [f('tenant_id', true), f('row_id', true), f('label')];
41
- expect(names(selectFieldsToMap(composite, ['label']))).toEqual(['tenant_id', 'row_id', 'label']);
41
+ expect(names(SelectFieldsToMap(composite, ['label']))).toEqual(['tenant_id', 'row_id', 'label']);
42
42
  });
43
43
 
44
44
  it('matches selection names case-insensitively', () => {
45
- expect(names(selectFieldsToMap(ALL, ['NAME', 'Email']))).toEqual(['id', 'name', 'email']);
45
+ expect(names(SelectFieldsToMap(ALL, ['NAME', 'Email']))).toEqual(['id', 'name', 'email']);
46
46
  });
47
47
 
48
48
  it('preserves the discovered field order', () => {
49
49
  // Field-map creation walks this list; reordering would reshuffle Priority-adjacent behaviour
50
50
  // for no reason.
51
- expect(names(selectFieldsToMap(ALL, ['notes', 'name']))).toEqual(['id', 'name', 'notes']);
51
+ expect(names(SelectFieldsToMap(ALL, ['notes', 'name']))).toEqual(['id', 'name', 'notes']);
52
52
  });
53
53
 
54
54
  it('an object with no key at all is unaffected', () => {
55
55
  const keyless = [f('a'), f('b')];
56
- expect(names(selectFieldsToMap(keyless, ['a']))).toEqual(['a']);
56
+ expect(names(SelectFieldsToMap(keyless, ['a']))).toEqual(['a']);
57
57
  });
58
58
  });
@@ -188,26 +188,26 @@ describe('RESTEndpointHandler.isEntityAllowed', () => {
188
188
  });
189
189
 
190
190
  it('blocks entities in excluded schemas (case-insensitive), with schema exclusion taking top precedence', () => {
191
- const schemaOnly = makeHandler({ excludeSchemas: ['VAULT'] });
191
+ const schemaOnly = makeHandler({ ExcludeSchemas: ['VAULT'] });
192
192
  expect(schemaOnly.isEntityAllowed('Secrets')).toBe(false);
193
193
  expect(schemaOnly.isEntityAllowed('Users')).toBe(true); // other schemas unaffected
194
194
 
195
195
  // Even an explicit entity include cannot override a schema exclusion
196
- const withInclude = makeHandler({ excludeSchemas: ['vault'], includeEntities: ['secrets'] });
196
+ const withInclude = makeHandler({ ExcludeSchemas: ['vault'], IncludeEntities: ['secrets'] });
197
197
  expect(withInclude.isEntityAllowed('Secrets')).toBe(false);
198
198
  });
199
199
 
200
200
  it('restricts to included schemas when includeSchemas is set', () => {
201
- const handler = makeHandler({ includeSchemas: ['crm'] });
201
+ const handler = makeHandler({ IncludeSchemas: ['crm'] });
202
202
  expect(handler.isEntityAllowed('Users')).toBe(true);
203
203
  expect(handler.isEntityAllowed('Secrets')).toBe(false);
204
204
  });
205
205
 
206
206
  it('blocks lowercase exact-name exclusions and wildcard exclusions of any case', () => {
207
- const lowercase = makeHandler({ excludeEntities: ['secrets'] });
207
+ const lowercase = makeHandler({ ExcludeEntities: ['secrets'] });
208
208
  expect(lowercase.isEntityAllowed('Secrets')).toBe(false);
209
209
 
210
- const wildcard = makeHandler({ excludeEntities: ['Secret*'] });
210
+ const wildcard = makeHandler({ ExcludeEntities: ['Secret*'] });
211
211
  expect(wildcard.isEntityAllowed('Secrets')).toBe(false);
212
212
  expect(wildcard.isEntityAllowed('Users')).toBe(true);
213
213
  });
@@ -217,25 +217,25 @@ describe('RESTEndpointHandler.isEntityAllowed', () => {
217
217
  // the RAW configured pattern (only the wildcard branch lowercases patterns).
218
218
  // An operator writing excludeEntities: ['Secrets'] — the natural casing —
219
219
  // gets NO exclusion. Pinned so a normalization fix flips this test.
220
- const handler = makeHandler({ excludeEntities: ['Secrets'] });
220
+ const handler = makeHandler({ ExcludeEntities: ['Secrets'] });
221
221
  expect(handler.isEntityAllowed('Secrets')).toBe(true); // exclusion ineffective!
222
222
  });
223
223
 
224
224
  it('exclusions override inclusions for the same entity', () => {
225
- const handler = makeHandler({ includeEntities: ['user*'], excludeEntities: ['userroles'] });
225
+ const handler = makeHandler({ IncludeEntities: ['user*'], ExcludeEntities: ['userroles'] });
226
226
  expect(handler.isEntityAllowed('Users')).toBe(true);
227
227
  expect(handler.isEntityAllowed('UserRoles')).toBe(false);
228
228
  });
229
229
 
230
230
  it('an include list denies everything not on it, with wildcard support', () => {
231
- const handler = makeHandler({ includeEntities: ['user*'] });
231
+ const handler = makeHandler({ IncludeEntities: ['user*'] });
232
232
  expect(handler.isEntityAllowed('Users')).toBe(true);
233
233
  expect(handler.isEntityAllowed('UserRoles')).toBe(true);
234
234
  expect(handler.isEntityAllowed('Secrets')).toBe(false);
235
235
  });
236
236
 
237
237
  it('SECURITY GAP: a mixed-case exact inclusion fails to include (same raw-pattern comparison)', () => {
238
- const handler = makeHandler({ includeEntities: ['Users'] });
238
+ const handler = makeHandler({ IncludeEntities: ['Users'] });
239
239
  expect(handler.isEntityAllowed('Users')).toBe(false); // include list active, but never matches
240
240
  });
241
241
  });
@@ -271,7 +271,7 @@ describe('RESTEndpointHandler middleware', () => {
271
271
 
272
272
  describe('checkEntityAccess', () => {
273
273
  it('rejects blocked entities with 403 before any handler runs', () => {
274
- const handler = makeHandler({ excludeEntities: ['secrets'] });
274
+ const handler = makeHandler({ ExcludeEntities: ['secrets'] });
275
275
  const req = makeReq({ params: { entityName: 'Secrets' } });
276
276
  const { res, statusCode, jsonBody } = makeRes();
277
277
  const next = vi.fn();
@@ -286,7 +286,7 @@ describe('RESTEndpointHandler middleware', () => {
286
286
  });
287
287
 
288
288
  it('passes allowed entities through', () => {
289
- const handler = makeHandler({ excludeEntities: ['secrets'] });
289
+ const handler = makeHandler({ ExcludeEntities: ['secrets'] });
290
290
  const next = vi.fn();
291
291
 
292
292
  handler.checkEntityAccess(makeReq({ params: { entityName: 'Users' } }), makeRes().res, next);
@@ -295,7 +295,7 @@ describe('RESTEndpointHandler middleware', () => {
295
295
  });
296
296
 
297
297
  it('passes through when no entityName param is present', () => {
298
- const handler = makeHandler({ includeEntities: ['nothing'] });
298
+ const handler = makeHandler({ IncludeEntities: ['nothing'] });
299
299
  const next = vi.fn();
300
300
 
301
301
  handler.checkEntityAccess(makeReq({ params: {} }), makeRes().res, next);
@@ -565,7 +565,7 @@ describe('RESTEndpointHandler view routes', () => {
565
565
  });
566
566
 
567
567
  it('returns 403 WITHOUT invoking the view layer when every requested entity is blocked', async () => {
568
- const handler = makeHandler({ includeEntities: ['users'] });
568
+ const handler = makeHandler({ IncludeEntities: ['users'] });
569
569
  const { res, statusCode } = makeRes();
570
570
 
571
571
  await handler.runViews(
@@ -579,7 +579,7 @@ describe('RESTEndpointHandler view routes', () => {
579
579
 
580
580
  it('silently drops blocked entities and executes only the allowed remainder', async () => {
581
581
  mockOpsRunViews.mockResolvedValue({ success: true, results: [{ Success: true, Results: [] }] });
582
- const handler = makeHandler({ excludeEntities: ['secrets'] });
582
+ const handler = makeHandler({ ExcludeEntities: ['secrets'] });
583
583
  const req = makeReq({ body: { params: [{ EntityName: 'Users' }, { EntityName: 'Secrets' }] } });
584
584
  const { res, jsonBody } = makeRes();
585
585
 
@@ -50,8 +50,10 @@ vi.mock('@memberjunction/ai-agents', () => ({
50
50
  // Mock the meeting-recording registration so the thin resolver is tested in isolation (no MJStorage /
51
51
  // core-entities graph). Its own behavior is covered by meetingRecordingRegistration.test.ts.
52
52
  vi.mock('../resolvers/meetingRecordingRegistration', () => ({
53
- registerMeetingRecordingFile: vi.fn(async () => ({ Success: true, RecordingFileID: 'file-1', ConversationID: 'conv-1' })),
54
- correlateRecordingStart: vi.fn(async () => true),
53
+ RegisterMeetingRecordingFile: vi.fn(async () => ({ Success: true, RecordingFileID: 'file-1', ConversationID: 'conv-1' })),
54
+ get registerMeetingRecordingFile() { return this.RegisterMeetingRecordingFile; },
55
+ CorrelateRecordingStart: vi.fn(async () => true),
56
+ get correlateRecordingStart() { return this.CorrelateRecordingStart; },
55
57
  }));
56
58
 
57
59
  import { RealtimeBridgeResolver, MintLiveKitClientTokenInput, LiveKitRecordingInput } from '../resolvers/RealtimeBridgeResolver';
@@ -3,7 +3,7 @@
3
3
  // bring it in automatically — this line MUST precede any import that pulls in the resolver file.
4
4
  import 'reflect-metadata';
5
5
 
6
- import { describe, it, expect, vi, beforeEach } from 'vitest';
6
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
7
7
 
8
8
  // --- Mock CanRun authorization (AIAgentPermissionHelper.HasPermission) plus the
9
9
  // provider-scoped AIEngineBase whose cached metadata serves the resolver's pairing-row
@@ -210,9 +210,23 @@ interface FakeSession {
210
210
  NewRecord: () => void;
211
211
  Save: () => Promise<boolean>;
212
212
  Load: (id: string) => Promise<boolean>;
213
- LatestResult?: { CompleteMessage?: string };
213
+ LatestResult?: { CompleteMessage?: string; Success?: boolean };
214
+ /** Only set on the fakes exercising `describeSaveFailure`'s "no failure detail recorded" branches. */
215
+ ResultHistory?: unknown[];
214
216
  }
215
217
 
218
+ /**
219
+ * A `console.error` spy shared by the save-failure-log tests (#4791) below. Assigned with
220
+ * `vi.spyOn(console, 'error').mockImplementation(() => undefined)` inside each test that needs it
221
+ * — restored here so no test leaks a mocked `console.error` into the ones after it.
222
+ */
223
+ let errSpy: ReturnType<typeof vi.spyOn> | undefined;
224
+
225
+ afterEach(() => {
226
+ errSpy?.mockRestore();
227
+ errSpy = undefined;
228
+ });
229
+
216
230
  function makeSessionEntity(overrides: Partial<FakeSession> = {}): FakeSession {
217
231
  return {
218
232
  ID: 'session-1',
@@ -848,6 +862,28 @@ describe('RealtimeClientSessionResolver.ExecuteRealtimeSessionTool', () => {
848
862
  resolver.ExecuteRealtimeSessionTool('session-1', 'call-1', 'x', '{}', makeCtx(), makePubSub()),
849
863
  ).rejects.toThrow(/no target agent configured/i);
850
864
  });
865
+
866
+ it('logs the session id and "no failure detail recorded" when updatePendingFeedbackRunID\'s save is refused silently (#4791)', async () => {
867
+ errSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
868
+ const session = makeSessionEntity({
869
+ Config_: JSON.stringify({ targetAgentID: 'target-1' }),
870
+ Save: vi.fn(async () => false),
871
+ LatestResult: undefined,
872
+ ResultHistory: [],
873
+ });
874
+ currentProvider = makeProvider(() => session);
875
+ executeRelayedToolMock.mockResolvedValue({ ResultJson: '{"q":1}', Success: true, PausedRunID: 'paused-1' });
876
+ const resolver = makeResolver();
877
+
878
+ await resolver.ExecuteRealtimeSessionTool('session-1', 'call-1', 'invoke-target-agent', '{}', makeCtx(), makePubSub());
879
+
880
+ const loggedLines = errSpy.mock.calls.map((call) => String(call[0]));
881
+ const failureLine = loggedLines.find((line) => line.includes('updatePendingFeedbackRunID'));
882
+ expect(failureLine).toBeDefined();
883
+ expect(failureLine).toContain('session-1');
884
+ expect(failureLine).toContain('no failure detail recorded');
885
+ expect(failureLine).not.toContain('unknown error');
886
+ });
851
887
  });
852
888
 
853
889
  describe('RealtimeClientSessionResolver.RelayRealtimeTranscript', () => {
@@ -1690,6 +1726,7 @@ describe('RealtimeClientSessionResolver.SaveSessionChannelArtifact', () => {
1690
1726
  });
1691
1727
 
1692
1728
  it('returns a structured failure when the artifact header save fails (no version attempted)', async () => {
1729
+ errSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
1693
1730
  const failingArtifact = makeSessionEntity({
1694
1731
  ID: 'artifact-fail',
1695
1732
  Save: vi.fn(async () => false),
@@ -1706,10 +1743,14 @@ describe('RealtimeClientSessionResolver.SaveSessionChannelArtifact', () => {
1706
1743
 
1707
1744
  expect(result.Success).toBe(false);
1708
1745
  expect(result.ErrorMessage).toMatch(/artifact save failed/i);
1746
+ expect(result.ErrorMessage).toContain('session session-1');
1747
+ expect(result.ErrorMessage).toContain('db down');
1748
+ expect(errSpy.mock.calls.map((call) => String(call[0]))).toContain(result.ErrorMessage);
1709
1749
  expect(version.Save).not.toHaveBeenCalled();
1710
1750
  });
1711
1751
 
1712
1752
  it('returns a structured failure (carrying the orphaned ArtifactID) when the version save fails', async () => {
1753
+ errSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
1713
1754
  const failingVersion = makeSessionEntity({
1714
1755
  ID: 'version-fail',
1715
1756
  Save: vi.fn(async () => false),
@@ -1728,6 +1769,10 @@ describe('RealtimeClientSessionResolver.SaveSessionChannelArtifact', () => {
1728
1769
  expect(result.Success).toBe(false);
1729
1770
  expect(result.ArtifactID).toBe('artifact-1');
1730
1771
  expect(result.ArtifactVersionID).toBeUndefined();
1772
+ expect(result.ErrorMessage).toMatch(/artifact version save failed/i);
1773
+ expect(result.ErrorMessage).toContain('session session-1');
1774
+ expect(result.ErrorMessage).toContain('too big');
1775
+ expect(errSpy.mock.calls.map((call) => String(call[0]))).toContain(result.ErrorMessage);
1731
1776
  expect(junction.Save).not.toHaveBeenCalled();
1732
1777
  });
1733
1778
 
@@ -2880,6 +2925,44 @@ describe('RealtimeClientSessionResolver — scoped-anonymous elevation (issue #3
2880
2925
  expect(executeRelayedToolMock.mock.calls[0][1]).toBe(ANON_USER);
2881
2926
  });
2882
2927
 
2928
+ it('writes the hidden direct-action tool turn (ExecuteRealtimeSessionTool) as the CALLER, not the system user (#4791)', async () => {
2929
+ const detail = makeSessionEntity({ ID: 'detail-1' });
2930
+ currentProvider = {
2931
+ GetEntityObject: vi.fn(async (name: string) =>
2932
+ name === 'MJ: Conversation Details' ? detail : makeSessionEntity({ UserID: 'anon-1' }),
2933
+ ),
2934
+ };
2935
+ executeRelayedToolMock.mockResolvedValue({ ResultJson: '{"success":true}', Success: true });
2936
+ const resolver = makeAnonResolver();
2937
+
2938
+ await resolver.ExecuteRealtimeSessionTool(
2939
+ 'session-1', 'call-1', 'Some Direct Action', '{}', makeCtx(), makePubSub(),
2940
+ );
2941
+
2942
+ // The dispatch itself still runs elevated (SYSTEM_USER)…
2943
+ expect(executeRelayedToolMock.mock.calls[0][1]).toBe(SYSTEM_USER);
2944
+ // …but the hidden Conversation Detail turn is written as the CALLER, never the system user.
2945
+ expect(getEntityObjectSpy()).toHaveBeenCalledWith('MJ: Conversation Details', ANON_USER);
2946
+ expect(getEntityObjectSpy()).not.toHaveBeenCalledWith('MJ: Conversation Details', SYSTEM_USER);
2947
+ expect(detail.Save).toHaveBeenCalled();
2948
+ expect(detail.UserID).toBe('anon-1');
2949
+ });
2950
+
2951
+ it('writes the direct-action tool turn as the caller unchanged for a normal authenticated user', async () => {
2952
+ const detail = makeSessionEntity({ ID: 'detail-2' });
2953
+ currentProvider = {
2954
+ GetEntityObject: vi.fn(async (name: string) => (name === 'MJ: Conversation Details' ? detail : makeSessionEntity())),
2955
+ };
2956
+ executeRelayedToolMock.mockResolvedValue({ ResultJson: '{"success":true}', Success: true });
2957
+ const resolver = makeResolver();
2958
+
2959
+ await resolver.ExecuteRealtimeSessionTool(
2960
+ 'session-1', 'call-1', 'Some Direct Action', '{}', makeCtx(), makePubSub(),
2961
+ );
2962
+
2963
+ expect(getEntityObjectSpy()).toHaveBeenCalledWith('MJ: Conversation Details', USER);
2964
+ });
2965
+
2883
2966
  it('accumulates relayed usage onto the prompt run under the SYSTEM user', async () => {
2884
2967
  const session = makeSessionEntity({
2885
2968
  UserID: 'anon-1',
@@ -2942,6 +3025,32 @@ describe('RealtimeClientSessionResolver — scoped-anonymous elevation (issue #3
2942
3025
  expect(getEntityObjectSpy()).toHaveBeenCalledWith('MJ: AI Prompt Runs', SYSTEM_USER);
2943
3026
  });
2944
3027
 
3028
+ it('writes the hidden co-agent tool turn (RelayRealtimeToolTurn) as the CALLER (#4791)', async () => {
3029
+ const session = makeSessionEntity({
3030
+ UserID: 'anon-1',
3031
+ Config_: JSON.stringify({ targetAgentID: 'target-1', promptRunID: 'prompt-run-1' }),
3032
+ });
3033
+ const promptRun = makeSessionEntity({ ID: 'prompt-run-1', Messages: null });
3034
+ const detail = makeSessionEntity({ ID: 'detail-3' });
3035
+ currentProvider = {
3036
+ GetEntityObject: vi.fn(async (name: string) => {
3037
+ if (name === 'MJ: AI Prompt Runs') return promptRun;
3038
+ if (name === 'MJ: Conversation Details') return detail;
3039
+ return session;
3040
+ }),
3041
+ };
3042
+ const resolver = makeAnonResolver();
3043
+
3044
+ const ok = await resolver.RelayRealtimeToolTurn('session-1', 'browser_navigate', makeCtx(), '{"url":"x"}');
3045
+
3046
+ expect(ok).toBe(true);
3047
+ // The co-agent run mirror stays SYSTEM…
3048
+ expect(getEntityObjectSpy()).toHaveBeenCalledWith('MJ: AI Prompt Runs', SYSTEM_USER);
3049
+ // …but the hidden Conversation Detail tool-turn is written as the CALLER, never SYSTEM.
3050
+ expect(getEntityObjectSpy()).toHaveBeenCalledWith('MJ: Conversation Details', ANON_USER);
3051
+ expect(getEntityObjectSpy()).not.toHaveBeenCalledWith('MJ: Conversation Details', SYSTEM_USER);
3052
+ });
3053
+
2945
3054
  it('prepares the session with observability under the SYSTEM user while UserID stays the visitor', async () => {
2946
3055
  hasPermissionMock.mockResolvedValue(true);
2947
3056
  currentProvider = makeProvider(() => makeSessionEntity({ UserID: 'anon-1' }));
@@ -3003,6 +3112,10 @@ describe('RealtimeClientSessionResolver — scoped-anonymous elevation (issue #3
3003
3112
  // The junction write (an entity the anon role does NOT hold) runs as the SYSTEM user.
3004
3113
  expect(getEntityObjectSpy()).toHaveBeenCalledWith('MJ: Conversation Detail Artifacts', SYSTEM_USER);
3005
3114
  expect(junctions).toHaveLength(1);
3115
+ // The hidden anchor `Conversation Detail` is written as the CALLER (#4791) — like every
3116
+ // other `MJ: Conversation Details` write, the System user is refused as not the owner.
3117
+ expect(getEntityObjectSpy()).toHaveBeenCalledWith('MJ: Conversation Details', ANON_USER);
3118
+ expect(getEntityObjectSpy()).not.toHaveBeenCalledWith('MJ: Conversation Details', SYSTEM_USER);
3006
3119
  // The hidden anchor is attributed to the SESSION owner, not the elevated principal.
3007
3120
  expect(anchors).toHaveLength(1);
3008
3121
  expect(anchors[0].UserID).toBe('anon-1');
@@ -3102,6 +3215,107 @@ describe('RealtimeClientSessionResolver — scoped-anonymous elevation (issue #3
3102
3215
  expect((storeRecordingMock.mock.calls[0][0] as { ContextUser: unknown }).ContextUser).toBe(USER);
3103
3216
  expect(getSystemUserMock).not.toHaveBeenCalled();
3104
3217
  });
3218
+
3219
+ /** Provider routing a hidden-tool-turn RelayRealtimeToolTurn call: session, prompt run, detail. */
3220
+ function makeToolTurnProvider(detail: FakeSession): unknown {
3221
+ const session = makeSessionEntity({
3222
+ UserID: 'anon-1',
3223
+ Config_: JSON.stringify({ targetAgentID: 'target-1', promptRunID: 'prompt-run-1' }),
3224
+ });
3225
+ const promptRun = makeSessionEntity({ ID: 'prompt-run-1', Messages: null });
3226
+ return {
3227
+ GetEntityObject: vi.fn(async (name: string) => {
3228
+ if (name === 'MJ: AI Prompt Runs') return promptRun;
3229
+ if (name === 'MJ: Conversation Details') return detail;
3230
+ return session;
3231
+ }),
3232
+ };
3233
+ }
3234
+
3235
+ it('logs the session id, write user id and "no failure detail recorded" when a hidden tool turn save is refused without a LatestResult (#4791)', async () => {
3236
+ errSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
3237
+ const detail = makeSessionEntity({
3238
+ ID: 'detail-1',
3239
+ Save: vi.fn(async () => false),
3240
+ LatestResult: undefined,
3241
+ ResultHistory: [],
3242
+ });
3243
+ currentProvider = makeToolTurnProvider(detail);
3244
+ const resolver = makeAnonResolver();
3245
+
3246
+ await resolver.RelayRealtimeToolTurn('session-1', 'browser_navigate', makeCtx(), '{}');
3247
+
3248
+ const loggedLines = errSpy.mock.calls.map((call) => String(call[0]));
3249
+ const failureLine = loggedLines.find((line) => line.includes('persistDirectActionTurn'));
3250
+ expect(failureLine).toBeDefined();
3251
+ // Exact substrings: 'anon-1' alone is the caller, session.UserID AND detail.UserID at once,
3252
+ // so only the labelled form proves the WRITE user is what got logged.
3253
+ expect(failureLine).toContain('session session-1');
3254
+ expect(failureLine).toContain('write user anon-1');
3255
+ expect(failureLine).toContain('no failure detail recorded (LatestResult null; ResultHistory length 0)');
3256
+ expect(failureLine).toContain('the write was refused before reaching the provider');
3257
+ expect(failureLine).not.toContain('unknown error');
3258
+ });
3259
+
3260
+ it('does not claim WHERE the write was refused when a result was registered with an empty message (#4791)', async () => {
3261
+ errSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
3262
+ const detail = makeSessionEntity({
3263
+ ID: 'detail-1',
3264
+ Save: vi.fn(async () => false),
3265
+ LatestResult: { Success: false, CompleteMessage: ' ' },
3266
+ ResultHistory: [{}],
3267
+ });
3268
+ currentProvider = makeToolTurnProvider(detail);
3269
+ const resolver = makeAnonResolver();
3270
+
3271
+ await resolver.RelayRealtimeToolTurn('session-1', 'browser_navigate', makeCtx(), '{}');
3272
+
3273
+ const loggedLines = errSpy.mock.calls.map((call) => String(call[0]));
3274
+ const failureLine = loggedLines.find((line) => line.includes('persistDirectActionTurn'));
3275
+ expect(failureLine).toBeDefined();
3276
+ expect(failureLine).toContain('no failure detail recorded (LatestResult empty; ResultHistory length 1)');
3277
+ expect(failureLine).not.toContain('refused before reaching the provider');
3278
+ });
3279
+
3280
+ it('never logs a PRIOR SUCCESS entry\'s text as the failure reason when the refusal registered nothing (#4791)', async () => {
3281
+ errSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
3282
+ const detail = makeSessionEntity({
3283
+ ID: 'detail-1',
3284
+ Save: vi.fn(async () => false),
3285
+ LatestResult: { Success: true, CompleteMessage: 'previous save ok' },
3286
+ ResultHistory: [{}, {}],
3287
+ });
3288
+ currentProvider = makeToolTurnProvider(detail);
3289
+ const resolver = makeAnonResolver();
3290
+
3291
+ await resolver.RelayRealtimeToolTurn('session-1', 'browser_navigate', makeCtx(), '{}');
3292
+
3293
+ const loggedLines = errSpy.mock.calls.map((call) => String(call[0]));
3294
+ const failureLine = loggedLines.find((line) => line.includes('persistDirectActionTurn'));
3295
+ expect(failureLine).toBeDefined();
3296
+ expect(failureLine).toContain(
3297
+ 'no failure detail recorded (latest result entry is a prior success; ResultHistory length 2)',
3298
+ );
3299
+ expect(failureLine).not.toContain('previous save ok');
3300
+ });
3301
+
3302
+ it('logs the real CompleteMessage when present (#4791)', async () => {
3303
+ errSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
3304
+ const detail = makeSessionEntity({
3305
+ ID: 'detail-1',
3306
+ Save: vi.fn(async () => false),
3307
+ LatestResult: { CompleteMessage: 'You do not have access to this conversation.' },
3308
+ });
3309
+ currentProvider = makeToolTurnProvider(detail);
3310
+ const resolver = makeAnonResolver();
3311
+
3312
+ await resolver.RelayRealtimeToolTurn('session-1', 'browser_navigate', makeCtx(), '{}');
3313
+
3314
+ const loggedLines = errSpy.mock.calls.map((call) => String(call[0]));
3315
+ const failureLine = loggedLines.find((line) => line.includes('persistDirectActionTurn'));
3316
+ expect(failureLine).toBeDefined();
3317
+ expect(failureLine).toContain('You do not have access to this conversation.');
3318
+ });
3105
3319
  });
3106
3320
 
3107
3321
  describe('RealtimeClientSessionResolver — colleague authorization (allowedAgents)', () => {
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
2
  import type { Request, Response, NextFunction } from 'express';
3
3
  import { MJGlobal, RegisterClass } from '@memberjunction/global';
4
4
  import { BaseRealtimeModel, RealtimeProxyRegistry, IRealtimeSession, RealtimeSessionParams } from '@memberjunction/ai';
5
- import { createRealtimeSdpBrokerRouter, createOpenAILiveBrokerRouter, requireTicket } from '../rest/RealtimeSdpBrokerHandler.js';
5
+ import { CreateRealtimeSdpBrokerRouter, createOpenAILiveBrokerRouter, RequireTicket } from '../rest/RealtimeSdpBrokerHandler.js';
6
6
 
7
7
  @RegisterClass(BaseRealtimeModel, 'MockOpenAILiveForBrokerTest', 10)
8
8
  class MockOpenAILiveForBrokerTest extends BaseRealtimeModel {
@@ -67,20 +67,20 @@ function makeMockReqRes(query: Record<string, string>, body: unknown): {
67
67
  }
68
68
 
69
69
  describe('RealtimeSdpBrokerHandler', () => {
70
- let router: ReturnType<typeof createRealtimeSdpBrokerRouter>;
70
+ let router: ReturnType<typeof CreateRealtimeSdpBrokerRouter>;
71
71
 
72
72
  beforeEach(() => {
73
- router = createRealtimeSdpBrokerRouter();
73
+ router = CreateRealtimeSdpBrokerRouter();
74
74
  });
75
75
 
76
76
  it('createOpenAILiveBrokerRouter is backwards-compatible alias', () => {
77
- expect(createOpenAILiveBrokerRouter).toBe(createRealtimeSdpBrokerRouter);
77
+ expect(createOpenAILiveBrokerRouter).toBe(CreateRealtimeSdpBrokerRouter);
78
78
  });
79
79
 
80
80
  it('requireTicket middleware rejects missing ticket without calling next()', () => {
81
81
  const { req, res, getStatus, getJson } = makeMockReqRes({}, {});
82
82
  const next = vi.fn();
83
- requireTicket(req, res, next as NextFunction);
83
+ RequireTicket(req, res, next as NextFunction);
84
84
  expect(getStatus()).toBe(401);
85
85
  expect(getJson().error).toContain('missing ticket');
86
86
  expect(next).not.toHaveBeenCalled();
@@ -89,7 +89,7 @@ describe('RealtimeSdpBrokerHandler', () => {
89
89
  it('requireTicket middleware rejects invalid ticket without calling next()', () => {
90
90
  const { req, res, getStatus, getJson } = makeMockReqRes({ ticket: 'non-existent-ticket' }, {});
91
91
  const next = vi.fn();
92
- requireTicket(req, res, next as NextFunction);
92
+ RequireTicket(req, res, next as NextFunction);
93
93
  expect(getStatus()).toBe(401);
94
94
  expect(getJson().error).toContain('invalid or expired ticket');
95
95
  expect(next).not.toHaveBeenCalled();
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { readFileSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
- import { decideFieldMapReconcile } from '../integration/EntityMapLifecycle.js';
4
+ import { DecideFieldMapReconcile } from '../integration/EntityMapLifecycle.js';
5
5
 
6
6
  /**
7
7
  * A REFRESH adopts the source's current shape: new objects and new columns both arrive ENABLED.
@@ -17,20 +17,20 @@ const fm = (SourceFieldName: string, Status: string) => ({ SourceFieldName, Stat
17
17
 
18
18
  describe('decideFieldMapReconcile — a refresh adopts new columns', () => {
19
19
  it('creates a new column ENABLED on an enabled map, by default', () => {
20
- const plan = decideFieldMapReconcile(['id', 'brand_new_col'], [fm('id', 'Active')], true);
20
+ const plan = DecideFieldMapReconcile(['id', 'brand_new_col'], [fm('id', 'Active')], true);
21
21
  expect(plan.Create).toEqual([{ SourceFieldName: 'brand_new_col', Status: 'Active' }]);
22
22
  expect(plan.Enable).toEqual([]);
23
23
  expect(plan.Disable).toEqual([]);
24
24
  });
25
25
 
26
26
  it('autoEnableNewColumns:false gates it for a connection that wants to review first', () => {
27
- const plan = decideFieldMapReconcile(['id', 'brand_new_col'], [fm('id', 'Active')], true, false);
27
+ const plan = DecideFieldMapReconcile(['id', 'brand_new_col'], [fm('id', 'Active')], true, false);
28
28
  expect(plan.Create).toEqual([{ SourceFieldName: 'brand_new_col', Status: 'Inactive' }]);
29
29
  });
30
30
 
31
31
  it('a DISABLED map never gets an Active column — the map always bounds the column', () => {
32
32
  // Adopting new columns must not resurrect a map the user switched off.
33
- const plan = decideFieldMapReconcile(['brand_new_col'], [], false, true);
33
+ const plan = DecideFieldMapReconcile(['brand_new_col'], [], false, true);
34
34
  expect(plan.Create).toEqual([{ SourceFieldName: 'brand_new_col', Status: 'Inactive' }]);
35
35
  });
36
36
  });
@@ -40,24 +40,24 @@ describe('decideFieldMapReconcile — retiring and restoring are both non-destru
40
40
  // That row is not new — it was disabled because the source stopped reporting the column, so
41
41
  // it returns to the state it had. Gating it would silently demote a column the user chose to
42
42
  // sync whenever the source flickered.
43
- const plan = decideFieldMapReconcile(['came_back'], [fm('came_back', 'Inactive')], true, false);
43
+ const plan = DecideFieldMapReconcile(['came_back'], [fm('came_back', 'Inactive')], true, false);
44
44
  expect(plan.Enable).toEqual(['came_back']);
45
45
  expect(plan.Create).toEqual([]); // reuses the row, never mints a second one
46
46
  });
47
47
 
48
48
  it('a column absent from the resolution is DISABLED, never deleted', () => {
49
- const plan = decideFieldMapReconcile(['kept'], [fm('kept', 'Active'), fm('vanished', 'Active')], true);
49
+ const plan = DecideFieldMapReconcile(['kept'], [fm('kept', 'Active'), fm('vanished', 'Active')], true);
50
50
  expect(plan.Disable).toEqual(['vanished']);
51
51
  expect(plan.Enable).toEqual([]); // survivor untouched
52
52
  });
53
53
 
54
54
  it('does not re-enable anything while the map itself is disabled', () => {
55
- const plan = decideFieldMapReconcile(['came_back'], [fm('came_back', 'Inactive')], false);
55
+ const plan = DecideFieldMapReconcile(['came_back'], [fm('came_back', 'Inactive')], false);
56
56
  expect(plan.Enable).toEqual([]);
57
57
  });
58
58
 
59
59
  it('matches source field names case-insensitively on both sides', () => {
60
- const plan = decideFieldMapReconcile(['ID', 'Name'], [fm('id', 'Active'), fm('name', 'Active')], true);
60
+ const plan = DecideFieldMapReconcile(['ID', 'Name'], [fm('id', 'Active'), fm('name', 'Active')], true);
61
61
  expect(plan.Create).toEqual([]);
62
62
  expect(plan.Disable).toEqual([]);
63
63
  });
@@ -59,7 +59,13 @@ vi.mock('@memberjunction/remote-browser-server', () => ({
59
59
  }));
60
60
 
61
61
  vi.mock('@memberjunction/aiengine', () => ({ AIEngine: { Instance: { Config: vi.fn(), Prompts: [] } } }));
62
- vi.mock('@memberjunction/ai-prompts', () => ({ AIPromptRunner: class {} }));
62
+ // Spread the real module, as for ai-core-plus below, so transitively-loaded modules resolve: runners such as
63
+ // AIRerankerRunner (which extends BaseModelRunner), and RunDecisionResolver's AIDecisionRunner. Only
64
+ // AIPromptRunner is overridden for the test.
65
+ vi.mock('@memberjunction/ai-prompts', async (importOriginal) => ({
66
+ ...(await importOriginal<typeof import('@memberjunction/ai-prompts')>()),
67
+ AIPromptRunner: class {},
68
+ }));
63
69
  vi.mock('@memberjunction/ai-core-plus', async (importOriginal) => ({
64
70
  ...(await importOriginal<typeof import('@memberjunction/ai-core-plus')>()),
65
71
  AIPromptParams: class {},
@@ -72,7 +72,13 @@ vi.mock('@memberjunction/remote-browser-server', () => ({
72
72
 
73
73
  // Keep the AI imports (visual interpreter) inert — they aren't exercised by the audio path.
74
74
  vi.mock('@memberjunction/aiengine', () => ({ AIEngine: { Instance: { Config: vi.fn(), Prompts: [] } } }));
75
- vi.mock('@memberjunction/ai-prompts', () => ({ AIPromptRunner: class {} }));
75
+ // Spread the real module, as for ai-core-plus below, so transitively-loaded modules resolve: runners such as
76
+ // AIRerankerRunner (which extends BaseModelRunner), and RunDecisionResolver's AIDecisionRunner. Only
77
+ // AIPromptRunner is overridden for the test.
78
+ vi.mock('@memberjunction/ai-prompts', async (importOriginal) => ({
79
+ ...(await importOriginal<typeof import('@memberjunction/ai-prompts')>()),
80
+ AIPromptRunner: class {},
81
+ }));
76
82
  // Spread the real module so transitively-loaded code (e.g. ArtifactToolManager → DataSnapshotToolLibrary,
77
83
  // which extends BaseArtifactToolLibrary) still resolves with its REAL base classes; only AIPromptParams is
78
84
  // overridden for the test. (Subsumes the narrower stub-two-exports approach — any other transitive export