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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (401) hide show
  1. package/README.md +27 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/SessionJanitor.d.ts +3 -3
  4. package/dist/agentSessions/SessionJanitor.js +3 -3
  5. package/dist/agentSessions/SessionManager.d.ts +15 -5
  6. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  7. package/dist/agentSessions/SessionManager.js +34 -12
  8. package/dist/agentSessions/SessionManager.js.map +1 -1
  9. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
  10. package/dist/auth/AuthProviderCatalogRouter.js +2 -0
  11. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
  12. package/dist/auth/index.d.ts.map +1 -1
  13. package/dist/auth/index.js +51 -10
  14. package/dist/auth/index.js.map +1 -1
  15. package/dist/auth/initializeProviders.d.ts +10 -0
  16. package/dist/auth/initializeProviders.d.ts.map +1 -1
  17. package/dist/auth/initializeProviders.js +37 -1
  18. package/dist/auth/initializeProviders.js.map +1 -1
  19. package/dist/auth/localDevAuth.d.ts +79 -0
  20. package/dist/auth/localDevAuth.d.ts.map +1 -0
  21. package/dist/auth/localDevAuth.js +439 -0
  22. package/dist/auth/localDevAuth.js.map +1 -0
  23. package/dist/auth/magicLink/MagicLinkKeys.d.ts +2 -2
  24. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  25. package/dist/auth/magicLink/MagicLinkKeys.js +1 -1
  26. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  27. package/dist/auth/magicLink/types.d.ts +12 -0
  28. package/dist/auth/magicLink/types.d.ts.map +1 -1
  29. package/dist/auth/sessionUserClone.d.ts +1 -1
  30. package/dist/auth/sessionUserClone.d.ts.map +1 -1
  31. package/dist/auth/sessionUserClone.js +3 -2
  32. package/dist/auth/sessionUserClone.js.map +1 -1
  33. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts +292 -0
  34. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -0
  35. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +442 -0
  36. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -0
  37. package/dist/broadcast/index.d.ts +6 -0
  38. package/dist/broadcast/index.d.ts.map +1 -0
  39. package/dist/broadcast/index.js +6 -0
  40. package/dist/broadcast/index.js.map +1 -0
  41. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts +43 -0
  42. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts.map +1 -0
  43. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js +144 -0
  44. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js.map +1 -0
  45. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts +46 -0
  46. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts.map +1 -0
  47. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js +149 -0
  48. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js.map +1 -0
  49. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts +49 -0
  50. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts.map +1 -0
  51. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js +131 -0
  52. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js.map +1 -0
  53. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts +24 -0
  54. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts.map +1 -0
  55. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js +51 -0
  56. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js.map +1 -0
  57. package/dist/cache.d.ts +3 -2
  58. package/dist/cache.d.ts.map +1 -1
  59. package/dist/cache.js.map +1 -1
  60. package/dist/config.d.ts +496 -0
  61. package/dist/config.d.ts.map +1 -1
  62. package/dist/config.js +61 -0
  63. package/dist/config.js.map +1 -1
  64. package/dist/context.d.ts +3 -1
  65. package/dist/context.d.ts.map +1 -1
  66. package/dist/context.js +29 -8
  67. package/dist/context.js.map +1 -1
  68. package/dist/coreRoutes.d.ts.map +1 -1
  69. package/dist/coreRoutes.js +14 -0
  70. package/dist/coreRoutes.js.map +1 -1
  71. package/dist/generated/graphql-schemas/__mj.d.ts +635 -14
  72. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  73. package/dist/generated/graphql-schemas/__mj.js +3366 -418
  74. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  75. package/dist/generic/CacheInvalidationResolver.d.ts +50 -30
  76. package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
  77. package/dist/generic/CacheInvalidationResolver.js +154 -79
  78. package/dist/generic/CacheInvalidationResolver.js.map +1 -1
  79. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  80. package/dist/generic/OrphanedConversationDetailReconciler.js +2 -0
  81. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  82. package/dist/generic/PubSubManager.d.ts +10 -0
  83. package/dist/generic/PubSubManager.d.ts.map +1 -1
  84. package/dist/generic/PubSubManager.js +44 -1
  85. package/dist/generic/PubSubManager.js.map +1 -1
  86. package/dist/generic/PushStatusResolver.d.ts +39 -6
  87. package/dist/generic/PushStatusResolver.d.ts.map +1 -1
  88. package/dist/generic/PushStatusResolver.js +93 -11
  89. package/dist/generic/PushStatusResolver.js.map +1 -1
  90. package/dist/generic/ResolverBase.d.ts +65 -25
  91. package/dist/generic/ResolverBase.d.ts.map +1 -1
  92. package/dist/generic/ResolverBase.js +201 -101
  93. package/dist/generic/ResolverBase.js.map +1 -1
  94. package/dist/generic/RunViewResolver.d.ts +11 -3
  95. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  96. package/dist/generic/RunViewResolver.js +79 -13
  97. package/dist/generic/RunViewResolver.js.map +1 -1
  98. package/dist/generic/actionOutputFieldSecurity.d.ts +26 -0
  99. package/dist/generic/actionOutputFieldSecurity.d.ts.map +1 -0
  100. package/dist/generic/actionOutputFieldSecurity.js +47 -0
  101. package/dist/generic/actionOutputFieldSecurity.js.map +1 -0
  102. package/dist/generic/fieldSecurityTransport.d.ts +30 -0
  103. package/dist/generic/fieldSecurityTransport.d.ts.map +1 -0
  104. package/dist/generic/fieldSecurityTransport.js +30 -0
  105. package/dist/generic/fieldSecurityTransport.js.map +1 -0
  106. package/dist/index.d.ts +3 -0
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +122 -66
  109. package/dist/index.js.map +1 -1
  110. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  111. package/dist/integration/CustomColumnPromoter.js +6 -1
  112. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  113. package/dist/logging/variablesLoggingMiddleware.d.ts +12 -0
  114. package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
  115. package/dist/logging/variablesLoggingMiddleware.js +50 -1
  116. package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
  117. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -1
  118. package/dist/realtimeSessions/SessionVerificationPortsBase.js +1 -0
  119. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -1
  120. package/dist/realtimeSessions/ownedRealtimeSession.d.ts +8 -6
  121. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
  122. package/dist/realtimeSessions/ownedRealtimeSession.js +15 -6
  123. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
  124. package/dist/realtimeSessions/sessionAccess.d.ts +88 -11
  125. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  126. package/dist/realtimeSessions/sessionAccess.js +125 -22
  127. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  128. package/dist/realtimeSessions/sessionConfigStamps.js +1 -1
  129. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -1
  130. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -1
  131. package/dist/realtimeSessions/subscriptionAuthorization.js +5 -2
  132. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -1
  133. package/dist/realtimeSessions/verificationWorkflow.d.ts +2 -0
  134. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -1
  135. package/dist/realtimeSessions/verificationWorkflow.js +1 -1
  136. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -1
  137. package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
  138. package/dist/realtimeWidget/WidgetRouter.js +19 -3
  139. package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
  140. package/dist/realtimeWidget/WidgetSessionService.d.ts +67 -7
  141. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  142. package/dist/realtimeWidget/WidgetSessionService.js +201 -19
  143. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  144. package/dist/realtimeWidget/host-identity.d.ts +1 -1
  145. package/dist/realtimeWidget/host-identity.js +1 -1
  146. package/dist/realtimeWidget/widgetCore.d.ts +20 -1
  147. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  148. package/dist/realtimeWidget/widgetCore.js +17 -1
  149. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  150. package/dist/realtimeWidget/widgetGuestElevation.d.ts +1 -1
  151. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  152. package/dist/realtimeWidget/widgetGuestElevation.js +2 -1
  153. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  154. package/dist/resolvers/ActionResolver.d.ts +19 -1
  155. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  156. package/dist/resolvers/ActionResolver.js +63 -12
  157. package/dist/resolvers/ActionResolver.js.map +1 -1
  158. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  159. package/dist/resolvers/AdhocQueryResolver.js +1 -0
  160. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  161. package/dist/resolvers/AgentRunStatusPublisher.d.ts +18 -4
  162. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
  163. package/dist/resolvers/AgentRunStatusPublisher.js +32 -8
  164. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
  165. package/dist/resolvers/AgentSessionResolver.d.ts +10 -4
  166. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  167. package/dist/resolvers/AgentSessionResolver.js +40 -17
  168. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  169. package/dist/resolvers/EntityCommunicationsResolver.d.ts +13 -1
  170. package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
  171. package/dist/resolvers/EntityCommunicationsResolver.js +50 -14
  172. package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
  173. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +28 -0
  174. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  175. package/dist/resolvers/FetchEntityVectorsResolver.js +117 -21
  176. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  177. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  178. package/dist/resolvers/FileCategoryResolver.js +1 -0
  179. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  180. package/dist/resolvers/FileResolver.d.ts +5 -0
  181. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  182. package/dist/resolvers/FileResolver.js +13 -4
  183. package/dist/resolvers/FileResolver.js.map +1 -1
  184. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  185. package/dist/resolvers/IntegrationDiscoveryResolver.js +45 -7
  186. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  187. package/dist/resolvers/MergeRecordsResolver.d.ts +8 -1
  188. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  189. package/dist/resolvers/MergeRecordsResolver.js +31 -3
  190. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  191. package/dist/resolvers/PipelineProgressResolver.d.ts +6 -0
  192. package/dist/resolvers/PipelineProgressResolver.d.ts.map +1 -1
  193. package/dist/resolvers/PipelineProgressResolver.js +5 -3
  194. package/dist/resolvers/PipelineProgressResolver.js.map +1 -1
  195. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  196. package/dist/resolvers/QueryResolver.js +20 -6
  197. package/dist/resolvers/QueryResolver.js.map +1 -1
  198. package/dist/resolvers/QuerySystemUserResolver.d.ts +2 -0
  199. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  200. package/dist/resolvers/QuerySystemUserResolver.js +15 -6
  201. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  202. package/dist/resolvers/RSUResolver.d.ts.map +1 -1
  203. package/dist/resolvers/RSUResolver.js +12 -0
  204. package/dist/resolvers/RSUResolver.js.map +1 -1
  205. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +16 -2
  206. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  207. package/dist/resolvers/RealtimeClientSessionResolver.js +82 -28
  208. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  209. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  210. package/dist/resolvers/RemoteBrowserActionResolver.js +11 -9
  211. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  212. package/dist/resolvers/RunAIAgentResolver.d.ts +56 -8
  213. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  214. package/dist/resolvers/RunAIAgentResolver.js +209 -64
  215. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  216. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  217. package/dist/resolvers/RunTemplateResolver.js +4 -4
  218. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  219. package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
  220. package/dist/resolvers/RunTestResolver.js +3 -1
  221. package/dist/resolvers/RunTestResolver.js.map +1 -1
  222. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  223. package/dist/resolvers/SyncDataResolver.js +4 -0
  224. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  225. package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
  226. package/dist/resolvers/TestQuerySQLResolver.js +1 -0
  227. package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
  228. package/dist/resolvers/TransactionGroupResolver.d.ts +17 -2
  229. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  230. package/dist/resolvers/TransactionGroupResolver.js +60 -15
  231. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  232. package/dist/resolvers/UserViewResolver.d.ts +1 -1
  233. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  234. package/dist/resolvers/UserViewResolver.js +2 -1
  235. package/dist/resolvers/UserViewResolver.js.map +1 -1
  236. package/dist/rest/EntityCRUDHandler.d.ts +6 -0
  237. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  238. package/dist/rest/EntityCRUDHandler.js +19 -5
  239. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  240. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  241. package/dist/rest/RESTEndpointHandler.js +8 -5
  242. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  243. package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
  244. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
  245. package/dist/rest/RealtimeRecordingHandler.js +7 -7
  246. package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
  247. package/dist/rest/ViewOperationsHandler.d.ts +6 -0
  248. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  249. package/dist/rest/ViewOperationsHandler.js +22 -5
  250. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  251. package/dist/serverRoleConfigUnits.d.ts +29 -0
  252. package/dist/serverRoleConfigUnits.d.ts.map +1 -0
  253. package/dist/serverRoleConfigUnits.js +33 -0
  254. package/dist/serverRoleConfigUnits.js.map +1 -0
  255. package/dist/services/TaskGraphContinuationDeliverer.d.ts +5 -0
  256. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  257. package/dist/services/TaskGraphContinuationDeliverer.js +42 -15
  258. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  259. package/dist/types.d.ts +23 -1
  260. package/dist/types.d.ts.map +1 -1
  261. package/dist/types.js.map +1 -1
  262. package/package.json +98 -98
  263. package/src/__tests__/ActionResolver.fieldSecurity.test.ts +237 -0
  264. package/src/__tests__/AdhocQueryResolver.dialect.test.ts +137 -0
  265. package/src/__tests__/AgentRunStatusPublisher.test.ts +49 -0
  266. package/src/__tests__/AgentSessionResolver.ownership.test.ts +137 -0
  267. package/src/__tests__/CustomColumnPromoterOnePass.test.ts +15 -1
  268. package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
  269. package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
  270. package/src/__tests__/FetchEntityVectorsResolver.test.ts +206 -3
  271. package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
  272. package/src/__tests__/PipelineProgressResolver.filter.test.ts +58 -0
  273. package/src/__tests__/QuerySystemUserResolver.fieldSecurity.test.ts +49 -0
  274. package/src/__tests__/RESTEndpointHandler.test.ts +25 -0
  275. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +23 -3
  276. package/src/__tests__/RealtimeClientSessionResolver.test.ts +324 -46
  277. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +98 -0
  278. package/src/__tests__/ResolverBase.clientOldValues.test.ts +37 -4
  279. package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
  280. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +176 -0
  281. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +23 -1
  282. package/src/__tests__/ResolverBase.publishStatusUpdate.test.ts +68 -0
  283. package/src/__tests__/ResolverBase.userViewCacheAndDedup.test.ts +130 -0
  284. package/src/__tests__/RunAIAgentResolver.branch.test.ts +68 -13
  285. package/src/__tests__/RunAIAgentResolver.conversationWriter.test.ts +210 -0
  286. package/src/__tests__/RunAIAgentResolver.fireAndForgetCompletion.test.ts +209 -0
  287. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +17 -27
  288. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +8 -2
  289. package/src/__tests__/RunAIAgentResolver.serverReply.test.ts +358 -0
  290. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +4 -1
  291. package/src/__tests__/RunTemplateResolver.test.ts +35 -2
  292. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +11 -0
  293. package/src/__tests__/SessionManager.test.ts +95 -0
  294. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +114 -3
  295. package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
  296. package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
  297. package/src/__tests__/ViewOperationsHandler.test.ts +7 -2
  298. package/src/__tests__/WidgetSessionService.test.ts +583 -4
  299. package/src/__tests__/apiRootBoot.test.ts +1 -1
  300. package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
  301. package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
  302. package/src/__tests__/cacheInvalidationRecordData.test.ts +45 -0
  303. package/src/__tests__/fieldSecurityFixtures.ts +91 -0
  304. package/src/__tests__/globalCors.test.ts +3 -3
  305. package/src/__tests__/localDevAuth.test.ts +196 -0
  306. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +10 -0
  307. package/src/__tests__/pushStatusResolver.filter.test.ts +57 -1
  308. package/src/__tests__/realtimeRecordingHandler.test.ts +19 -2
  309. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +29 -1
  310. package/src/__tests__/realtimeSessions/ownedRealtimeSession.anonymous.test.ts +31 -0
  311. package/src/__tests__/realtimeSessions/ownedRealtimeSession.namedScoped.test.ts +37 -0
  312. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +146 -7
  313. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +1 -1
  314. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +25 -4
  315. package/src/__tests__/resolverBase.cloudEventChangedFields.test.ts +157 -0
  316. package/src/__tests__/resolverBase.fieldSecurityTransport.test.ts +222 -0
  317. package/src/__tests__/resolverBase.fls.test.ts +146 -97
  318. package/src/__tests__/restTransport.fieldSecurity.test.ts +148 -0
  319. package/src/__tests__/runParamsTimeZone.test.ts +179 -0
  320. package/src/__tests__/runStreams.fieldSecurity.test.ts +100 -0
  321. package/src/__tests__/runViewProperties.test.ts +114 -0
  322. package/src/__tests__/scalabilityBenchmarks.test.ts +765 -0
  323. package/src/__tests__/scalabilityDatabaseIntegration.test.ts +207 -0
  324. package/src/__tests__/sessionUserClone.test.ts +5 -1
  325. package/src/__tests__/startupSweepOrder.test.ts +40 -0
  326. package/src/__tests__/unifiedAuth.test.ts +28 -0
  327. package/src/__tests__/widgetGuestElevation.test.ts +2 -0
  328. package/src/agentSessions/SessionJanitor.ts +3 -3
  329. package/src/agentSessions/SessionManager.ts +35 -12
  330. package/src/auth/AuthProviderCatalogRouter.ts +3 -0
  331. package/src/auth/index.ts +56 -12
  332. package/src/auth/initializeProviders.ts +43 -1
  333. package/src/auth/localDevAuth.ts +528 -0
  334. package/src/auth/magicLink/MagicLinkKeys.ts +2 -2
  335. package/src/auth/magicLink/types.ts +10 -0
  336. package/src/auth/sessionUserClone.ts +3 -2
  337. package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +674 -0
  338. package/src/broadcast/index.ts +5 -0
  339. package/src/broadcast/providers/AWSIoTCoreBroadcastProvider.ts +180 -0
  340. package/src/broadcast/providers/AzureWebPubSubBroadcastProvider.ts +182 -0
  341. package/src/broadcast/providers/GCPPubSubBroadcastProvider.ts +169 -0
  342. package/src/broadcast/providers/SequencedRoleBucketBroadcastProvider.ts +57 -0
  343. package/src/cache.ts +2 -1
  344. package/src/config.ts +69 -0
  345. package/src/context.ts +32 -8
  346. package/src/coreRoutes.ts +15 -0
  347. package/src/generated/graphql-schemas/__mj.ts +2239 -102
  348. package/src/generic/CacheInvalidationResolver.ts +135 -78
  349. package/src/generic/OrphanedConversationDetailReconciler.ts +4 -2
  350. package/src/generic/PubSubManager.ts +54 -1
  351. package/src/generic/PushStatusResolver.ts +108 -11
  352. package/src/generic/ResolverBase.ts +237 -107
  353. package/src/generic/RunViewResolver.ts +80 -15
  354. package/src/generic/actionOutputFieldSecurity.ts +75 -0
  355. package/src/generic/fieldSecurityTransport.ts +62 -0
  356. package/src/index.ts +127 -68
  357. package/src/integration/CustomColumnPromoter.ts +6 -1
  358. package/src/logging/variablesLoggingMiddleware.ts +59 -3
  359. package/src/realtimeSessions/README.md +7 -3
  360. package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
  361. package/src/realtimeSessions/ownedRealtimeSession.ts +20 -6
  362. package/src/realtimeSessions/sessionAccess.ts +181 -25
  363. package/src/realtimeSessions/sessionConfigStamps.ts +1 -1
  364. package/src/realtimeSessions/subscriptionAuthorization.ts +4 -2
  365. package/src/realtimeSessions/verificationWorkflow.ts +3 -1
  366. package/src/realtimeWidget/WidgetRouter.ts +22 -4
  367. package/src/realtimeWidget/WidgetSessionService.ts +252 -17
  368. package/src/realtimeWidget/host-identity.ts +1 -1
  369. package/src/realtimeWidget/widgetCore.ts +30 -1
  370. package/src/realtimeWidget/widgetGuestElevation.ts +2 -1
  371. package/src/resolvers/ActionResolver.ts +78 -15
  372. package/src/resolvers/AdhocQueryResolver.ts +1 -0
  373. package/src/resolvers/AgentRunStatusPublisher.ts +36 -8
  374. package/src/resolvers/AgentSessionResolver.ts +46 -18
  375. package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
  376. package/src/resolvers/FetchEntityVectorsResolver.ts +132 -21
  377. package/src/resolvers/FileCategoryResolver.ts +1 -0
  378. package/src/resolvers/FileResolver.ts +14 -4
  379. package/src/resolvers/IntegrationDiscoveryResolver.ts +49 -7
  380. package/src/resolvers/MergeRecordsResolver.ts +23 -2
  381. package/src/resolvers/PipelineProgressResolver.ts +12 -3
  382. package/src/resolvers/QueryResolver.ts +20 -6
  383. package/src/resolvers/QuerySystemUserResolver.ts +15 -6
  384. package/src/resolvers/RSUResolver.ts +12 -0
  385. package/src/resolvers/RealtimeClientSessionResolver.ts +104 -33
  386. package/src/resolvers/RemoteBrowserActionResolver.ts +15 -10
  387. package/src/resolvers/RunAIAgentResolver.ts +258 -82
  388. package/src/resolvers/RunTemplateResolver.ts +6 -4
  389. package/src/resolvers/RunTestResolver.ts +3 -1
  390. package/src/resolvers/SyncDataResolver.ts +4 -0
  391. package/src/resolvers/TestQuerySQLResolver.ts +1 -0
  392. package/src/resolvers/TransactionGroupResolver.ts +66 -15
  393. package/src/resolvers/UserViewResolver.ts +2 -1
  394. package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
  395. package/src/rest/EntityCRUDHandler.ts +22 -5
  396. package/src/rest/RESTEndpointHandler.ts +8 -5
  397. package/src/rest/RealtimeRecordingHandler.ts +13 -10
  398. package/src/rest/ViewOperationsHandler.ts +24 -6
  399. package/src/serverRoleConfigUnits.ts +48 -0
  400. package/src/services/TaskGraphContinuationDeliverer.ts +52 -20
  401. package/src/types.ts +23 -1
@@ -143,20 +143,69 @@ export declare class RunAIAgentResolver extends ResolverBase {
143
143
  */
144
144
  private sendFeedbackRequestNotification;
145
145
  /**
146
- * Optimized mutation that loads conversation history server-side.
147
- * This avoids sending large attachment data from client to server.
146
+ * Runs an agent's turn in a conversation: the server loads the history (attachments included, so the client never
147
+ * sends them), creates the agent's reply row, and runs the agent into it.
148
148
  *
149
- * @param conversationDetailId - The conversation detail ID (user's message already saved)
149
+ * Only the server writes agent replies (A19). The reply row (`Role` `AI`, In-Progress, its `ParentID` the user's
150
+ * message) is created here, as the person asking, before the run starts. In fire-and-forget mode the
151
+ * acknowledgement names it, `{ accepted, fireAndForget, conversationDetailId, createdAt }`, and is returned before
152
+ * the run publishes anything; every progress, streaming and completion message carries the same
153
+ * `conversationDetailId`. In synchronous mode the result's `agentRun.ConversationDetailID` names it.
154
+ *
155
+ * Threading, both optional: `replyParentId` puts the reply under another message of the same conversation the
156
+ * caller can read (MJ's chat threads a delegate's reply under the manager's); `announcedByAgentId` has the server
157
+ * first post that agent's fixed line "Continuing with **{agent}** for refinement..." and thread the reply under
158
+ * it, and the acknowledgement then also carries `announcementDetailId`.
159
+ *
160
+ * @param userMessageId - The person's message this turn answers, already saved.
150
161
  * @param agentId - The agent to execute
151
162
  * @param maxHistoryMessages - Maximum number of history messages to include (default: 20)
152
163
  */
153
- RunAIAgentFromConversationDetail(conversationDetailId: string, agentId: string, { userPayload, providers, dataSource }: AppContext, sessionId: string, pubSub: PubSubEngine, maxHistoryMessages?: number, data?: string, payload?: string, lastRunId?: string, autoPopulateLastRunPayload?: boolean, configurationId?: string, createArtifacts?: boolean, createNotification?: boolean, sourceArtifactId?: string, sourceArtifactVersionId?: string, fireAndForget?: boolean, planMode?: boolean, requestedSkillIDs?: string[],
164
+ RunAIAgentFromConversationDetail(userMessageId: string, agentId: string, { userPayload, providers, dataSource }: AppContext, sessionId: string, pubSub: PubSubEngine, maxHistoryMessages?: number, data?: string, payload?: string, lastRunId?: string, autoPopulateLastRunPayload?: boolean, configurationId?: string, createArtifacts?: boolean, createNotification?: boolean, sourceArtifactId?: string, sourceArtifactVersionId?: string, fireAndForget?: boolean, planMode?: boolean, requestedSkillIDs?: string[],
154
165
  /** History floor: an ISO-8601 timestamp, the first moment of the conversation this run may
155
166
  * read. The history is loaded from there and no summary of earlier messages is used, and the
156
167
  * run's own conversation reads honour it too. Omitted (clients send it only when set, so an
157
168
  * older client never does), the run reads the whole conversation as before. An unreadable
158
169
  * value fails the request rather than being dropped. */
159
- agentHistoryFrom?: string): Promise<AIAgentRunResult>;
170
+ agentHistoryFrom?: string,
171
+ /** The message the reply threads under, when not the user's message: one in the same conversation the
172
+ * caller can read. */
173
+ replyParentId?: string,
174
+ /** An active agent (MJ's chat passes its conversation manager) whose fixed line "Continuing with
175
+ * **{agent}** for refinement..." the server posts before the reply, which then threads under it. */
176
+ announcedByAgentId?: string,
177
+ /** The fork the reply and history run on, when the turn forks from `userMessageId` (such as Regenerate).
178
+ * Checked to be a branch of the conversation whose path holds `userMessageId`. */
179
+ replyBranchId?: string): Promise<AIAgentRunResult>;
180
+ /**
181
+ * Everything a conversation turn needs before its agent runs: the user's message (read as the caller, so a
182
+ * message they can't see is "not found", and for an anonymous visitor also checked against the conversation's
183
+ * scope stamp), the conversation history, the optional announcement line, and the reply
184
+ * row, created last so the history never holds the empty reply. Every row is written as `user`, the person asking,
185
+ * and marked as server-authored by {@link AgentRunner.CreateAgentReplyDetail}.
186
+ */
187
+ private startConversationTurn;
188
+ /** The conversation, branch (null is Main) and sequence of a message, read as `user`; throws when they can't read it. */
189
+ private loadPlacementOf;
190
+ /**
191
+ * When a turn forks from an earlier message (such as Regenerate), the reply and history run on the fork while the
192
+ * user's message stays on the branch it was written on. Checks that `replyBranchId` is a branch of the turn's
193
+ * conversation whose path holds the user's message (`IsRowOnBranchPath` rather than exact `ForkFromSequence`
194
+ * equality is deliberate: `ConversationEngine.ForkPointFrom` sets `ForkFromSequence = message.Sequence` and the
195
+ * fork belongs to this conversation); returns `replyBranchId`, or throws before any row is created.
196
+ */
197
+ private validateReplyBranch;
198
+ /**
199
+ * A reply's parent is part of what the reply says, so the server sets it, from a checked input: a message the
200
+ * caller can read, in the turn's own conversation. Returns its ID; throws otherwise.
201
+ */
202
+ private validateReplyParent;
203
+ /**
204
+ * Posts the announcing agent's line, "Continuing with **{agent}** for refinement...", as a completed reply of the
205
+ * announcing agent. Its text is fixed and names the agent from metadata, so a caller chooses only whether it is
206
+ * posted and by which active agent, never what it says.
207
+ */
208
+ private postContinuationAnnouncement;
160
209
  /**
161
210
  * Respond to a pending AIAgentRequest from the dashboard or API.
162
211
  * Updates the request record with the response and optionally spawns a
@@ -170,9 +219,8 @@ export declare class RunAIAgentResolver extends ResolverBase {
170
219
  ReassignAgentRequest(requestId: string, newUserID: string, { userPayload, providers }: AppContext, note?: string): Promise<AIAgentRunResult>;
171
220
  /**
172
221
  * Execute agent in background (fire-and-forget).
173
- * Handles errors by publishing error completion events via PubSub,
174
- * so the client receives them via WebSocket even though the HTTP response
175
- * has already been sent.
222
+ * The client learns the outcome only from the run's completion status message (executeAIAgent publishes it,
223
+ * failure included), since the HTTP response has already been sent. A liveness pulse runs until the run ends.
176
224
  */
177
225
  private executeAgentInBackground;
178
226
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"RunAIAgentResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/RunAIAgentResolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RunAIAgentResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/RunAIAgentResolver.ts"],"names":[],"mappings":"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"}
@@ -15,14 +15,17 @@ import { LogError, LogStatus, RunView } from '@memberjunction/core';
15
15
  import { ArtifactMetadataEngine, ConversationEngine } from '@memberjunction/core-entities';
16
16
  import { RouteArtifact } from './artifact-routing.js';
17
17
  import { AgentRunner, ArtifactToolManager } from '@memberjunction/ai-agents';
18
- import { MJAIAgentRunEntityExtended, ConversationUtility } from '@memberjunction/ai-core-plus';
18
+ import { MJAIAgentRunEntityExtended, ConversationUtility, ContinuationAnnouncementText } from '@memberjunction/ai-core-plus';
19
19
  import { AIEngine } from '@memberjunction/aiengine';
20
20
  import { ResolverBase } from '../generic/ResolverBase.js';
21
21
  import { AgentRunStatusPublisher } from './AgentRunStatusPublisher.js';
22
+ import { ProjectEntityObjectForCaller } from '../generic/fieldSecurityTransport.js';
22
23
  import { StartLivenessPulse } from '../generic/FireAndForgetHeartbeat.js';
24
+ import { StatusUpdateOwnerID } from '../generic/PushStatusResolver.js';
23
25
  import { RequireSystemUser } from '../directives/RequireSystemUser.js';
24
26
  import { GetReadWriteProvider } from '../util.js';
25
27
  import { ResolveWidgetGuestRunContext, ElevateUserPayload } from '../realtimeWidget/widgetGuestElevation.js';
28
+ import { AuthorizeConversationTurn } from '../realtimeSessions/sessionAccess.js';
26
29
  import { IsTrustedAgentRunCaller } from './agent-run-data-guard.js';
27
30
  import { SafeJSONParse, UUIDsEqual } from '@memberjunction/global';
28
31
  import { GetAttachmentService } from '@memberjunction/aiengine';
@@ -212,7 +215,7 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
212
215
  * Sanitize ExecuteAgentResult for JSON serialization
213
216
  * Removes circular references and non-serializable objects
214
217
  */
215
- sanitizeAgentResult(result) {
218
+ sanitizeAgentResult(result, recipient) {
216
219
  const sanitized = {
217
220
  success: result.success,
218
221
  payload: result.payload,
@@ -227,8 +230,9 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
227
230
  };
228
231
  // Safely extract agent run data using GetAll() for proper serialization
229
232
  if (result.agentRun && typeof result.agentRun.GetAll === 'function') {
230
- // Use GetAll() to get the full serialized object including extended properties
231
- sanitized.agentRun = result.agentRun.GetAll();
233
+ // GetAll() for the full serialized object including extended properties, projected for
234
+ // the recipient: the run comes from the agent engine, not from a load bound to them
235
+ sanitized.agentRun = ProjectEntityObjectForCaller(result.agentRun, recipient);
232
236
  }
233
237
  else {
234
238
  // shouldn't ever get here
@@ -314,7 +318,10 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
314
318
  /** ExecuteAgentParams.TrustReservedRunData: true only for a caller IsTrustedAgentRunCaller accepts. */
315
319
  trustReservedRunData = false,
316
320
  /** The conversation branch of the run — threaded into ExecuteAgentParams.ConversationBranchID. */
317
- conversationBranchId) {
321
+ conversationBranchId,
322
+ /** The reply row this request created on the server for the run (`conversationDetailId` is its ID). The run
323
+ * writes into this instance, so its writes carry the server-authored marker and the user it was made as. */
324
+ replyDetail) {
318
325
  const startTime = Date.now();
319
326
  // Best-effort handle for persistInFlightAgentFailure. Populated from a
320
327
  // progress event carrying metadata.agentRun, or from the successful
@@ -322,6 +329,8 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
322
329
  // run is not marked Failed — the early-failure case we most want to
323
330
  // persist, but we have no run object yet.
324
331
  const agentRunRef = runRef ?? { current: null };
332
+ // The publisher owns every status message, including the failure completion the catch below sends.
333
+ const statusPublisher = new AgentRunStatusPublisher(pubSub, userPayload, sessionId, conversationDetailId);
325
334
  try {
326
335
  LogStatus(`=== RUNNING AI AGENT FOR ID: ${agentId} ===`);
327
336
  // Parse and validate messages
@@ -346,9 +355,8 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
346
355
  // singleton's transaction state with concurrent requests (e.g. conversation deletes).
347
356
  const agentRunner = new AgentRunner(p);
348
357
  console.log(`🚀 Starting agent execution with sessionId: ${sessionId}`);
349
- // The publisher owns every status message. The resolver still copies the run into
350
- // agentRunRef, including noise steps, for the fire-and-forget liveness pulse.
351
- const statusPublisher = new AgentRunStatusPublisher(pubSub, userPayload, sessionId);
358
+ // The resolver still copies the run into agentRunRef, including noise steps, for the
359
+ // fire-and-forget liveness pulse.
352
360
  const publishProgress = statusPublisher.OnProgress;
353
361
  // Execute the agent in conversation context - handles conversation, artifacts, etc.
354
362
  const conversationResult = await agentRunner.RunAgentInConversation({
@@ -381,10 +389,13 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
381
389
  }
382
390
  }, {
383
391
  conversationDetailId: conversationDetailId, // Use existing if provided
392
+ agentResponseDetail: replyDetail, // the reply row this request created, when it did
384
393
  conversationId: conversationId, // LATENCY OPT #2: pre-resolved to skip redundant load in AgentRunner
385
394
  userMessage: userMessage, // Provide user message when conversationDetailId not provided
386
395
  createArtifacts: createArtifacts || false,
387
- sourceArtifactId: sourceArtifactId
396
+ sourceArtifactId: sourceArtifactId,
397
+ // A widget guest runs as the system user, but its conversation stays the guest's to write (MJ#4791).
398
+ ConversationWriter: userPayload.ElevatedFrom,
388
399
  });
389
400
  const result = conversationResult.agentResult;
390
401
  // Use agent response detail ID if available, otherwise fall back to user message detail ID
@@ -424,7 +435,7 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
424
435
  await Promise.all(postExecutionOps);
425
436
  }
426
437
  // Create sanitized payload for JSON serialization
427
- const sanitizedResult = this.sanitizeAgentResult(result);
438
+ const sanitizedResult = this.sanitizeAgentResult(result, currentUser);
428
439
  const returnResult = JSON.stringify(sanitizedResult);
429
440
  // Publish final events with enriched result data for fire-and-forget clients
430
441
  statusPublisher.PublishFinal(result, result.agentRun?.ConversationDetailID, returnResult);
@@ -445,10 +456,17 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
445
456
  catch (error) {
446
457
  const executionTime = Date.now() - startTime;
447
458
  LogError(`AI Agent run failed:`, undefined, error);
448
- const errorMessage = error.message || 'Unknown error occurred';
459
+ // Never dereference a non-Error throw: this catch must not itself throw, since executeAIAgent resolves on
460
+ // every failure (executeAgentInBackground relies on it).
461
+ const errorMessage = (error instanceof Error && error.message) || 'Unknown error occurred';
449
462
  // Fire-and-forget clients otherwise leave the run Running and the
450
463
  // conversation detail In-Progress (Explorer red-pill timer).
451
- await this.persistInFlightAgentFailure(p, userPayload, agentRunRef.current, conversationDetailId, errorMessage);
464
+ await this.persistInFlightAgentFailure(p, userPayload, agentRunRef.current, conversationDetailId, errorMessage, replyDetail);
465
+ // A fire-and-forget client has only this message to learn the run ended: without it, it waits out its
466
+ // idle timeout and then reconciles the run as a success. Published after the rows are closed, so a
467
+ // client that reloads the reply on this message reads the error. PublishFinal is the try's last step
468
+ // that can throw, so a run gets one completion or the other, never both.
469
+ statusPublisher.PublishFailure(conversationDetailId, errorMessage);
452
470
  const errorResult = {
453
471
  success: false,
454
472
  errorMessage,
@@ -466,7 +484,9 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
466
484
  * When executeAIAgent throws after the run/detail exist, close them so Explorer
467
485
  * does not leave a red-pill timer on Status=Running / ConversationDetail In-Progress.
468
486
  */
469
- async persistInFlightAgentFailure(provider, userPayload, run, conversationDetailId, errorMessage) {
487
+ async persistInFlightAgentFailure(provider, userPayload, run, conversationDetailId, errorMessage,
488
+ /** The run's reply row, when this request created it: written in place, as the server. */
489
+ replyDetail) {
470
490
  try {
471
491
  if (run && run.Status === 'Running') {
472
492
  await run.EnsureSaveComplete();
@@ -482,7 +502,8 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
482
502
  return;
483
503
  }
484
504
  if (conversationDetailId) {
485
- const detail = await provider.GetEntityObject('MJ: Conversation Details', user);
505
+ const detail = replyDetail ?? await provider.GetEntityObject('MJ: Conversation Details', user);
506
+ await detail.EnsureSaveComplete(); // the run may still be saving into the same reply instance
486
507
  if (await detail.Load(conversationDetailId) && detail.Status === 'In-Progress') {
487
508
  await detail.EnsureSaveComplete();
488
509
  detail.Status = 'Error';
@@ -741,16 +762,25 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
741
762
  }
742
763
  }
743
764
  /**
744
- * Optimized mutation that loads conversation history server-side.
745
- * This avoids sending large attachment data from client to server.
765
+ * Runs an agent's turn in a conversation: the server loads the history (attachments included, so the client never
766
+ * sends them), creates the agent's reply row, and runs the agent into it.
746
767
  *
747
- * @param conversationDetailId - The conversation detail ID (user's message already saved)
768
+ * Only the server writes agent replies (A19). The reply row (`Role` `AI`, In-Progress, its `ParentID` the user's
769
+ * message) is created here, as the person asking, before the run starts. In fire-and-forget mode the
770
+ * acknowledgement names it, `{ accepted, fireAndForget, conversationDetailId, createdAt }`, and is returned before
771
+ * the run publishes anything; every progress, streaming and completion message carries the same
772
+ * `conversationDetailId`. In synchronous mode the result's `agentRun.ConversationDetailID` names it.
773
+ *
774
+ * Threading, both optional: `replyParentId` puts the reply under another message of the same conversation the
775
+ * caller can read (MJ's chat threads a delegate's reply under the manager's); `announcedByAgentId` has the server
776
+ * first post that agent's fixed line "Continuing with **{agent}** for refinement..." and thread the reply under
777
+ * it, and the acknowledgement then also carries `announcementDetailId`.
778
+ *
779
+ * @param userMessageId - The person's message this turn answers, already saved.
748
780
  * @param agentId - The agent to execute
749
781
  * @param maxHistoryMessages - Maximum number of history messages to include (default: 20)
750
782
  */
751
- async RunAIAgentFromConversationDetail(conversationDetailId, agentId, { userPayload, providers, dataSource }, sessionId, pubSub, maxHistoryMessages, data, payload, lastRunId, autoPopulateLastRunPayload, configurationId, createArtifacts, createNotification, sourceArtifactId, sourceArtifactVersionId, fireAndForget, planMode, requestedSkillIDs, agentHistoryFrom) {
752
- // Check API key scope authorization for agent execution
753
- await this.CheckAPIKeyScopeAuthorization('agent:execute', agentId, userPayload);
783
+ async RunAIAgentFromConversationDetail(userMessageId, agentId, { userPayload, providers, dataSource }, sessionId, pubSub, maxHistoryMessages, data, payload, lastRunId, autoPopulateLastRunPayload, configurationId, createArtifacts, createNotification, sourceArtifactId, sourceArtifactVersionId, fireAndForget, planMode, requestedSkillIDs, agentHistoryFrom, replyParentId, announcedByAgentId, replyBranchId) {
754
784
  const p = GetReadWriteProvider(providers);
755
785
  const currentUser = this.GetUserFromPayload(userPayload);
756
786
  if (!currentUser) {
@@ -765,10 +795,20 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
765
795
  // AUTHORITATIVELY from the widget instance (never the client-supplied arg) — so a guest needs
766
796
  // no grants to WRITE the AI run entities and cannot run an arbitrary agent under elevation.
767
797
  // Conversation OWNERSHIP is still enforced under the guest principal below: the guest loads its
768
- // own ConversationDetail through the Widget Guest RLS filters, so a detail id from another
769
- // session resolves to "not found" before any elevated work happens.
798
+ // own ConversationDetail through its role's RLS filters, and startConversationTurn then checks
799
+ // the conversation's scope stamp against the visitor (MJ#6032), so a detail id from another
800
+ // visitor's session is refused before any elevated work happens. The reply row is written
801
+ // as the guest too, never as the elevated principal, which owns no guest's conversation.
770
802
  const widgetElevation = await ResolveWidgetGuestRunContext(userPayload, p);
771
803
  const effectiveAgentId = widgetElevation ? widgetElevation.PinnedAgentId : agentId;
804
+ // Check API key scope authorization for agent execution against the resolved agent id.
805
+ await this.CheckAPIKeyScopeAuthorization('agent:execute', effectiveAgentId, userPayload);
806
+ // A widget guest has one pinned agent and never posts a continuation announcement as another agent;
807
+ // an API-key caller must hold agent:execute for the announcing agent as well as the running agent.
808
+ const effectiveAnnouncedByAgentId = widgetElevation ? undefined : announcedByAgentId;
809
+ if (effectiveAnnouncedByAgentId) {
810
+ await this.CheckAPIKeyScopeAuthorization('agent:execute', effectiveAnnouncedByAgentId, userPayload);
811
+ }
772
812
  const effectiveUserPayload = widgetElevation ? ElevateUserPayload(userPayload, widgetElevation.ElevatedUser) : userPayload;
773
813
  // Only the system user or an API key holding the agent:trusted scope may set a run's scope and agent-type
774
814
  // params through data (ExecuteAgentParams.TrustReservedRunData). Judged on the caller's own payload: a widget
@@ -779,25 +819,26 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
779
819
  try {
780
820
  // Parsed before anything loads: a floor the caller asked for must never be dropped.
781
821
  const historyFrom = parseAgentHistoryFrom(agentHistoryFrom);
782
- // LATENCY OPTIMIZATION (Opt #2 + #3): Load ConversationDetail once here to extract
783
- // conversationId, then pass it downstream. Previously this record was loaded multiple
784
- // times: once in loadConversationHistoryWithAttachments (just to get conversationId),
785
- // once in AgentRunner (same reason), and once in createCompletionNotification. Now we
786
- // load it a single time and thread conversationId through the call chain.
787
- const currentDetail = await p.GetEntityObject('MJ: Conversation Details', currentUser);
788
- if (!await currentDetail.Load(conversationDetailId)) {
789
- throw new Error(`Conversation detail ${conversationDetailId} not found`);
790
- }
791
- const conversationId = currentDetail.ConversationID;
792
- // The branch the placeholder row was created on decides which path the run reads.
793
- const branchId = currentDetail.BranchID ?? null;
822
+ const turn = await this.startConversationTurn(p, currentUser, {
823
+ UserMessageId: userMessageId,
824
+ AgentId: effectiveAgentId,
825
+ MaxHistoryMessages: maxHistoryMessages || 20,
826
+ HistoryFrom: historyFrom,
827
+ ReplyParentId: replyParentId,
828
+ AnnouncedByAgentId: effectiveAnnouncedByAgentId,
829
+ ReplyBranchId: replyBranchId
830
+ });
831
+ const reply = turn.Reply;
832
+ // The turn's branch (the message's own branch, or a validated fork whose path holds the message)
833
+ // decides which path the run reads.
834
+ const branchId = turn.BranchId;
794
835
  // A turn that follows a run the user STOPPED continues that run rather than starting
795
836
  // over: chain it by LastRunID (the chat client never sends one) and, when the caller
796
837
  // supplied no payload, roll the stopped run's FinalPayload into this run's start.
797
838
  let effectiveLastRunId = lastRunId;
798
839
  let effectiveAutoPopulate = autoPopulateLastRunPayload;
799
840
  if (!effectiveLastRunId) {
800
- const stoppedRunId = await this.findUserStoppedPredecessorRunId(conversationId, effectiveAgentId, branchId, currentUser, p);
841
+ const stoppedRunId = await this.findUserStoppedPredecessorRunId(turn.ConversationId, effectiveAgentId, branchId, currentUser, p);
801
842
  if (stoppedRunId) {
802
843
  effectiveLastRunId = stoppedRunId;
803
844
  if (effectiveAutoPopulate === undefined && payload == null) {
@@ -806,30 +847,29 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
806
847
  LogStatus(`↩️ Continuing from user-stopped run ${stoppedRunId} (chained by LastRunID)`);
807
848
  }
808
849
  }
809
- // Load conversation history with attachments from DB
810
- const messages = await this.loadConversationHistoryWithAttachments(conversationId, currentUser, maxHistoryMessages || 20, p,
811
- // The UI creates the agent-response placeholder row ('⏳ ...') before invoking
812
- // this mutation — exclude it so the model never sees an empty assistant turn.
813
- [conversationDetailId], historyFrom, branchId);
814
- // Convert to JSON string for the existing executeAIAgent method
815
- const messagesJson = JSON.stringify(messages);
816
850
  if (fireAndForget) {
817
851
  // Fire-and-forget mode: start execution in background, return immediately.
818
852
  // The client will receive the result via WebSocket PubSub completion event.
819
- this.executeAgentInBackground(p, dataSource, effectiveAgentId, effectiveUserPayload, messagesJson, sessionId, pubSub, data, payload, effectiveLastRunId, effectiveAutoPopulate, configurationId, conversationDetailId, createArtifacts || false, createNotification || false, sourceArtifactId, sourceArtifactVersionId, conversationId, planMode, requestedSkillIDs, undefined, // taskGraphDebug
820
- historyFrom, trustRunData, branchId);
853
+ this.executeAgentInBackground(p, dataSource, effectiveAgentId, effectiveUserPayload, turn.MessagesJson, sessionId, pubSub, data, payload, effectiveLastRunId, effectiveAutoPopulate, configurationId, reply.ID, createArtifacts || false, createNotification || false, sourceArtifactId, sourceArtifactVersionId, turn.ConversationId, planMode, requestedSkillIDs, undefined, // taskGraphDebug
854
+ historyFrom, trustRunData, branchId, reply);
821
855
  LogStatus(`🔥 Fire-and-forget: Agent ${effectiveAgentId} execution started in background for session ${sessionId}`);
822
856
  return {
823
857
  success: true,
824
- result: JSON.stringify({ accepted: true, fireAndForget: true })
858
+ result: JSON.stringify({
859
+ accepted: true,
860
+ fireAndForget: true,
861
+ conversationDetailId: reply.ID,
862
+ createdAt: reply.__mj_CreatedAt,
863
+ ...(turn.Announcement ? { announcementDetailId: turn.Announcement.ID } : {})
864
+ })
825
865
  };
826
866
  }
827
867
  // Synchronous mode (default): wait for execution to complete
828
- return this.executeAIAgent(p, dataSource, effectiveAgentId, effectiveUserPayload, messagesJson, sessionId, pubSub, data, payload, undefined, // templateData
829
- effectiveLastRunId, effectiveAutoPopulate, configurationId, conversationDetailId, createArtifacts || false, createNotification || false, sourceArtifactId, sourceArtifactVersionId, conversationId, // LATENCY OPT #2: pass pre-resolved conversationId
868
+ return this.executeAIAgent(p, dataSource, effectiveAgentId, effectiveUserPayload, turn.MessagesJson, sessionId, pubSub, data, payload, undefined, // templateData
869
+ effectiveLastRunId, effectiveAutoPopulate, configurationId, reply.ID, createArtifacts || false, createNotification || false, sourceArtifactId, sourceArtifactVersionId, turn.ConversationId, // LATENCY OPT #2: pass pre-resolved conversationId
830
870
  undefined, // runRef
831
871
  planMode, requestedSkillIDs, undefined, // taskGraphDebug
832
- historyFrom, trustRunData, branchId);
872
+ historyFrom, trustRunData, branchId, reply);
833
873
  }
834
874
  catch (error) {
835
875
  const errorMessage = error.message || 'Unknown error loading conversation history';
@@ -841,6 +881,110 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
841
881
  };
842
882
  }
843
883
  }
884
+ /**
885
+ * Everything a conversation turn needs before its agent runs: the user's message (read as the caller, so a
886
+ * message they can't see is "not found", and for an anonymous visitor also checked against the conversation's
887
+ * scope stamp), the conversation history, the optional announcement line, and the reply
888
+ * row, created last so the history never holds the empty reply. Every row is written as `user`, the person asking,
889
+ * and marked as server-authored by {@link AgentRunner.CreateAgentReplyDetail}.
890
+ */
891
+ async startConversationTurn(p, user, request) {
892
+ // LATENCY OPTIMIZATION (Opt #2 + #3): the user's message is loaded once here for its conversation and
893
+ // branch, which are threaded through the call chain rather than reloaded downstream.
894
+ const placement = await this.loadPlacementOf(p, user, request.UserMessageId);
895
+ const conversationId = placement.ConversationId;
896
+ // Loading the message as the caller is the whole ownership check for a named user. It is not for an
897
+ // anonymous visitor: every visitor is the one shared Anonymous user, so under a guest role whose row
898
+ // filters key on UserID another visitor's message loads fine (MJ#6032). The conversation's scope stamp
899
+ // decides instead, exactly as it does for the realtime gates — and before any row is written.
900
+ const access = await AuthorizeConversationTurn(conversationId, user, p);
901
+ if (!access.Allowed) {
902
+ LogError(`RunAIAgentFromConversationDetail: anonymous visitor ${user.AnonymousVisitorID ?? '(no visitor id)'} refused a turn in conversation ${conversationId} (${access.Reason})`);
903
+ throw new Error('Not authorized: you do not own this conversation');
904
+ }
905
+ const branchId = request.ReplyBranchId
906
+ ? await this.validateReplyBranch(p, user, conversationId, placement, request.ReplyBranchId)
907
+ : placement.BranchId;
908
+ const parentId = request.ReplyParentId
909
+ ? await this.validateReplyParent(p, user, request.ReplyParentId, conversationId)
910
+ : request.UserMessageId;
911
+ const messages = await this.loadConversationHistoryWithAttachments(conversationId, user, request.MaxHistoryMessages, p, undefined, request.HistoryFrom, branchId);
912
+ const runner = new AgentRunner(p);
913
+ const announcement = request.AnnouncedByAgentId
914
+ ? await this.postContinuationAnnouncement(runner, user, conversationId, branchId, parentId, request.AnnouncedByAgentId, request.AgentId)
915
+ : undefined;
916
+ const reply = await runner.CreateAgentReplyDetail({
917
+ conversationId,
918
+ branchId,
919
+ agentId: request.AgentId,
920
+ contextUser: user,
921
+ parentId: announcement?.ID ?? parentId,
922
+ provider: p
923
+ });
924
+ return { ConversationId: conversationId, BranchId: branchId, MessagesJson: JSON.stringify(messages), Reply: reply, Announcement: announcement };
925
+ }
926
+ /** The conversation, branch (null is Main) and sequence of a message, read as `user`; throws when they can't read it. */
927
+ async loadPlacementOf(p, user, messageId) {
928
+ const message = await p.GetEntityObject('MJ: Conversation Details', user);
929
+ if (!await message.Load(messageId)) {
930
+ throw new Error(`Conversation detail ${messageId} not found`);
931
+ }
932
+ return { ConversationId: message.ConversationID, BranchId: message.BranchID ?? null, Sequence: message.Sequence ?? -1 };
933
+ }
934
+ /**
935
+ * When a turn forks from an earlier message (such as Regenerate), the reply and history run on the fork while the
936
+ * user's message stays on the branch it was written on. Checks that `replyBranchId` is a branch of the turn's
937
+ * conversation whose path holds the user's message (`IsRowOnBranchPath` rather than exact `ForkFromSequence`
938
+ * equality is deliberate: `ConversationEngine.ForkPointFrom` sets `ForkFromSequence = message.Sequence` and the
939
+ * fork belongs to this conversation); returns `replyBranchId`, or throws before any row is created.
940
+ */
941
+ async validateReplyBranch(p, user, conversationId, userMessage, replyBranchId) {
942
+ if (UUIDsEqual(replyBranchId, userMessage.BranchId)) {
943
+ return replyBranchId;
944
+ }
945
+ if (userMessage.Sequence < 0) {
946
+ throw new Error(`Conversation detail is not on branch ${replyBranchId}'s path, so the reply cannot be written on that branch`);
947
+ }
948
+ const branches = await ConversationEngine.LoadBranchesFresh(conversationId, user, p);
949
+ const chain = ConversationEngine.BuildBranchChain(branches, replyBranchId);
950
+ if (!ConversationEngine.IsRowOnBranchPath({ BranchID: userMessage.BranchId, Sequence: userMessage.Sequence }, chain)) {
951
+ throw new Error(`Conversation detail is not on branch ${replyBranchId}'s path, so the reply cannot be written on that branch`);
952
+ }
953
+ return replyBranchId;
954
+ }
955
+ /**
956
+ * A reply's parent is part of what the reply says, so the server sets it, from a checked input: a message the
957
+ * caller can read, in the turn's own conversation. Returns its ID; throws otherwise.
958
+ */
959
+ async validateReplyParent(p, user, parentId, conversationId) {
960
+ const { ConversationId: parentConversationId } = await this.loadPlacementOf(p, user, parentId);
961
+ if (!UUIDsEqual(parentConversationId, conversationId)) {
962
+ throw new Error(`Conversation detail ${parentId} is not in this turn's conversation, so the reply cannot thread under it`);
963
+ }
964
+ return parentId;
965
+ }
966
+ /**
967
+ * Posts the announcing agent's line, "Continuing with **{agent}** for refinement...", as a completed reply of the
968
+ * announcing agent. Its text is fixed and names the agent from metadata, so a caller chooses only whether it is
969
+ * posted and by which active agent, never what it says.
970
+ */
971
+ async postContinuationAnnouncement(runner, user, conversationId, branchId, parentId, announcerId, agentId) {
972
+ await AIEngine.Instance.Config(false, user);
973
+ const announcer = AIEngine.Instance.GetAgentByID(announcerId);
974
+ if (!announcer || announcer.Status !== 'Active') {
975
+ throw new Error(`Agent ${announcerId} cannot announce the turn: it is not an active agent`);
976
+ }
977
+ const agentName = AIEngine.Instance.GetAgentByID(agentId)?.Name ?? 'the agent';
978
+ return runner.CreateAgentReplyDetail({
979
+ conversationId,
980
+ branchId,
981
+ agentId: announcer.ID,
982
+ contextUser: user,
983
+ parentId,
984
+ message: ContinuationAnnouncementText(agentName),
985
+ status: 'Complete'
986
+ });
987
+ }
844
988
  /**
845
989
  * Respond to a pending AIAgentRequest from the dashboard or API.
846
990
  * Updates the request record with the response and optionally spawns a
@@ -985,9 +1129,8 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
985
1129
  }
986
1130
  /**
987
1131
  * Execute agent in background (fire-and-forget).
988
- * Handles errors by publishing error completion events via PubSub,
989
- * so the client receives them via WebSocket even though the HTTP response
990
- * has already been sent.
1132
+ * The client learns the outcome only from the run's completion status message (executeAIAgent publishes it,
1133
+ * failure included), since the HTTP response has already been sent. A liveness pulse runs until the run ends.
991
1134
  */
992
1135
  executeAgentInBackground(p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub, data, payload, lastRunId, autoPopulateLastRunPayload, configurationId, conversationDetailId, createArtifacts = false, createNotification = false, sourceArtifactId, sourceArtifactVersionId,
993
1136
  /** LATENCY OPT #2: Pre-resolved conversationId avoids redundant DB load in AgentRunner */
@@ -1001,27 +1144,26 @@ let RunAIAgentResolver = class RunAIAgentResolver extends ResolverBase {
1001
1144
  /** ExecuteAgentParams.TrustReservedRunData — see executeAIAgent. */
1002
1145
  trustReservedRunData = false,
1003
1146
  /** The conversation branch of the run — threaded through to ExecuteAgentParams.ConversationBranchID. */
1004
- conversationBranchId) {
1147
+ conversationBranchId,
1148
+ /** The reply row this request created for the run — see executeAIAgent. */
1149
+ replyDetail) {
1005
1150
  // Ref the liveness pulse reads to enrich heartbeats once the run is created.
1006
1151
  const runRef = { current: null };
1007
1152
  const pulse = StartLivenessPulse({
1008
1153
  pubSub,
1009
1154
  sessionId,
1010
- ownerUserId: userPayload.userRecord.ID,
1155
+ ownerUserId: StatusUpdateOwnerID(userPayload),
1011
1156
  resolver: 'RunAIAgentResolver',
1012
1157
  readStatus: () => runRef.current
1013
1158
  ? { runId: runRef.current.ID, status: runRef.current.Status }
1014
1159
  : undefined,
1015
1160
  });
1016
- // Execute in background - errors are handled within, not propagated
1017
- this.executeAIAgent(p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub, data, payload, undefined, lastRunId, autoPopulateLastRunPayload, configurationId, conversationDetailId, createArtifacts, createNotification, sourceArtifactId, sourceArtifactVersionId, conversationId, runRef, planMode, requestedSkillIDs, taskGraphDebug, conversationHistoryFrom, trustReservedRunData, conversationBranchId).catch((error) => {
1018
- // Background execution failed unexpectedly (executeAIAgent has its own try-catch,
1019
- // so this would only fire for truly unexpected errors).
1020
- const errorMessage = (error instanceof Error) ? error.message : 'Unknown background execution error';
1021
- LogError(`🔥 Fire-and-forget background execution failed: ${errorMessage}`, undefined, error);
1022
- // Publish error completion event so the client knows the agent failed.
1023
- // The publisher is created inside executeAIAgent, which rejected before returning one.
1024
- new AgentRunStatusPublisher(pubSub, userPayload, sessionId).PublishFailure(conversationDetailId, errorMessage);
1161
+ // executeAIAgent resolves on every failure and publishes the run's completion itself (PublishFinal, or
1162
+ // PublishFailure from its catch), so nothing here publishes one.
1163
+ this.executeAIAgent(p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub, data, payload, undefined, lastRunId, autoPopulateLastRunPayload, configurationId, conversationDetailId, createArtifacts, createNotification, sourceArtifactId, sourceArtifactVersionId, conversationId, runRef, planMode, requestedSkillIDs, taskGraphDebug, conversationHistoryFrom, trustReservedRunData, conversationBranchId, replyDetail).catch((error) => {
1164
+ // Only a broken executeAIAgent contract lands here. Logged rather than left as an unhandled rejection;
1165
+ // no completion is published, because executeAIAgent may already have sent one.
1166
+ LogError(`🔥 Fire-and-forget run of agent ${agentId} for session ${sessionId} rejected, which executeAIAgent never should`, undefined, error);
1025
1167
  }).finally(() => pulse.Stop());
1026
1168
  }
1027
1169
  /**
@@ -1318,7 +1460,7 @@ __decorate([
1318
1460
  ], RunAIAgentResolver.prototype, "RunAIAgentSystemUser", null);
1319
1461
  __decorate([
1320
1462
  Mutation(() => AIAgentRunResult),
1321
- __param(0, Arg('conversationDetailId')),
1463
+ __param(0, Arg('userMessageId')),
1322
1464
  __param(1, Arg('agentId')),
1323
1465
  __param(2, Ctx()),
1324
1466
  __param(3, Arg('sessionId')),
@@ -1337,8 +1479,11 @@ __decorate([
1337
1479
  __param(16, Arg('planMode', { nullable: true })),
1338
1480
  __param(17, Arg('requestedSkillIDs', () => [String], { nullable: true })),
1339
1481
  __param(18, Arg('agentHistoryFrom', { nullable: true })),
1482
+ __param(19, Arg('replyParentId', { nullable: true })),
1483
+ __param(20, Arg('announcedByAgentId', { nullable: true })),
1484
+ __param(21, Arg('replyBranchId', { nullable: true })),
1340
1485
  __metadata("design:type", Function),
1341
- __metadata("design:paramtypes", [String, String, Object, String, PubSubEngine, Number, String, String, String, Boolean, String, Boolean, Boolean, String, String, Boolean, Boolean, Array, String]),
1486
+ __metadata("design:paramtypes", [String, String, Object, String, PubSubEngine, Number, String, String, String, Boolean, String, Boolean, Boolean, String, String, Boolean, Boolean, Array, String, String, String, String]),
1342
1487
  __metadata("design:returntype", Promise)
1343
1488
  ], RunAIAgentResolver.prototype, "RunAIAgentFromConversationDetail", null);
1344
1489
  __decorate([