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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (664) hide show
  1. package/README.md +96 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/BridgeHostInstance.d.ts +8 -3
  4. package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -1
  5. package/dist/agentSessions/BridgeHostInstance.js +14 -4
  6. package/dist/agentSessions/BridgeHostInstance.js.map +1 -1
  7. package/dist/agentSessions/HandoffOfferExpiry.d.ts +10 -0
  8. package/dist/agentSessions/HandoffOfferExpiry.d.ts.map +1 -0
  9. package/dist/agentSessions/HandoffOfferExpiry.js +18 -0
  10. package/dist/agentSessions/HandoffOfferExpiry.js.map +1 -0
  11. package/dist/agentSessions/ReturningVisitorRecap.js +1 -0
  12. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  13. package/dist/agentSessions/SessionJanitor.d.ts +34 -11
  14. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  15. package/dist/agentSessions/SessionJanitor.js +88 -44
  16. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  17. package/dist/agentSessions/SessionManager.d.ts +29 -9
  18. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  19. package/dist/agentSessions/SessionManager.js +62 -16
  20. package/dist/agentSessions/SessionManager.js.map +1 -1
  21. package/dist/agentSessions/conversationBrowsers.d.ts +61 -0
  22. package/dist/agentSessions/conversationBrowsers.d.ts.map +1 -0
  23. package/dist/agentSessions/conversationBrowsers.js +118 -0
  24. package/dist/agentSessions/conversationBrowsers.js.map +1 -0
  25. package/dist/agentSessions/sessionDeadline.d.ts +43 -0
  26. package/dist/agentSessions/sessionDeadline.d.ts.map +1 -0
  27. package/dist/agentSessions/sessionDeadline.js +68 -0
  28. package/dist/agentSessions/sessionDeadline.js.map +1 -0
  29. package/dist/auth/APIKeyScopeAuth.d.ts.map +1 -1
  30. package/dist/auth/APIKeyScopeAuth.js +5 -4
  31. package/dist/auth/APIKeyScopeAuth.js.map +1 -1
  32. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
  33. package/dist/auth/AuthProviderCatalogRouter.js +2 -0
  34. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
  35. package/dist/auth/index.d.ts.map +1 -1
  36. package/dist/auth/index.js +51 -10
  37. package/dist/auth/index.js.map +1 -1
  38. package/dist/auth/initializeProviders.d.ts +10 -0
  39. package/dist/auth/initializeProviders.d.ts.map +1 -1
  40. package/dist/auth/initializeProviders.js +37 -1
  41. package/dist/auth/initializeProviders.js.map +1 -1
  42. package/dist/auth/localDevAuth.d.ts +79 -0
  43. package/dist/auth/localDevAuth.d.ts.map +1 -0
  44. package/dist/auth/localDevAuth.js +439 -0
  45. package/dist/auth/localDevAuth.js.map +1 -0
  46. package/dist/auth/loginEmailResolution.d.ts +41 -0
  47. package/dist/auth/loginEmailResolution.d.ts.map +1 -0
  48. package/dist/auth/loginEmailResolution.js +47 -0
  49. package/dist/auth/loginEmailResolution.js.map +1 -0
  50. package/dist/auth/magicLink/MagicLinkKeys.d.ts +51 -3
  51. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  52. package/dist/auth/magicLink/MagicLinkKeys.js +130 -13
  53. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  54. package/dist/auth/magicLink/MagicLinkRouter.d.ts +3 -0
  55. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  56. package/dist/auth/magicLink/MagicLinkRouter.js +23 -5
  57. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  58. package/dist/auth/magicLink/MagicLinkService.d.ts +39 -5
  59. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  60. package/dist/auth/magicLink/MagicLinkService.js +221 -9
  61. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  62. package/dist/auth/magicLink/index.d.ts +1 -1
  63. package/dist/auth/magicLink/index.d.ts.map +1 -1
  64. package/dist/auth/magicLink/index.js +1 -1
  65. package/dist/auth/magicLink/index.js.map +1 -1
  66. package/dist/auth/magicLink/redeemLanding.d.ts +2 -2
  67. package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
  68. package/dist/auth/magicLink/redeemLanding.js +7 -4
  69. package/dist/auth/magicLink/redeemLanding.js.map +1 -1
  70. package/dist/auth/magicLink/types.d.ts +39 -3
  71. package/dist/auth/magicLink/types.d.ts.map +1 -1
  72. package/dist/auth/magicLink/types.js +0 -4
  73. package/dist/auth/magicLink/types.js.map +1 -1
  74. package/dist/auth/newUsers.d.ts.map +1 -1
  75. package/dist/auth/newUsers.js +1 -0
  76. package/dist/auth/newUsers.js.map +1 -1
  77. package/dist/auth/sessionUserClone.d.ts +1 -1
  78. package/dist/auth/sessionUserClone.d.ts.map +1 -1
  79. package/dist/auth/sessionUserClone.js +3 -2
  80. package/dist/auth/sessionUserClone.js.map +1 -1
  81. package/dist/backgroundSweepConfigUnits.d.ts +231 -0
  82. package/dist/backgroundSweepConfigUnits.d.ts.map +1 -0
  83. package/dist/backgroundSweepConfigUnits.js +180 -0
  84. package/dist/backgroundSweepConfigUnits.js.map +1 -0
  85. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts +292 -0
  86. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -0
  87. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +442 -0
  88. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -0
  89. package/dist/broadcast/index.d.ts +6 -0
  90. package/dist/broadcast/index.d.ts.map +1 -0
  91. package/dist/broadcast/index.js +6 -0
  92. package/dist/broadcast/index.js.map +1 -0
  93. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts +43 -0
  94. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts.map +1 -0
  95. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js +144 -0
  96. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js.map +1 -0
  97. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts +46 -0
  98. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts.map +1 -0
  99. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js +149 -0
  100. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js.map +1 -0
  101. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts +49 -0
  102. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts.map +1 -0
  103. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js +131 -0
  104. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js.map +1 -0
  105. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts +24 -0
  106. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts.map +1 -0
  107. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js +51 -0
  108. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js.map +1 -0
  109. package/dist/cache.d.ts +3 -2
  110. package/dist/cache.d.ts.map +1 -1
  111. package/dist/cache.js.map +1 -1
  112. package/dist/config.d.ts +1402 -8
  113. package/dist/config.d.ts.map +1 -1
  114. package/dist/config.js +120 -0
  115. package/dist/config.js.map +1 -1
  116. package/dist/context.d.ts +5 -1
  117. package/dist/context.d.ts.map +1 -1
  118. package/dist/context.js +64 -12
  119. package/dist/context.js.map +1 -1
  120. package/dist/coreRoutes.d.ts +3 -1
  121. package/dist/coreRoutes.d.ts.map +1 -1
  122. package/dist/coreRoutes.js +20 -4
  123. package/dist/coreRoutes.js.map +1 -1
  124. package/dist/generated/graphql-schemas/__mj.d.ts +863 -14
  125. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  126. package/dist/generated/graphql-schemas/__mj.js +4167 -62
  127. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  128. package/dist/generic/CacheInvalidationResolver.d.ts +50 -30
  129. package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
  130. package/dist/generic/CacheInvalidationResolver.js +154 -79
  131. package/dist/generic/CacheInvalidationResolver.js.map +1 -1
  132. package/dist/generic/OrphanedConversationDetailReconciler.d.ts +11 -0
  133. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  134. package/dist/generic/OrphanedConversationDetailReconciler.js +29 -1
  135. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  136. package/dist/generic/PubSubManager.d.ts +10 -0
  137. package/dist/generic/PubSubManager.d.ts.map +1 -1
  138. package/dist/generic/PubSubManager.js +44 -1
  139. package/dist/generic/PubSubManager.js.map +1 -1
  140. package/dist/generic/PushStatusResolver.d.ts +39 -6
  141. package/dist/generic/PushStatusResolver.d.ts.map +1 -1
  142. package/dist/generic/PushStatusResolver.js +93 -11
  143. package/dist/generic/PushStatusResolver.js.map +1 -1
  144. package/dist/generic/ResolverBase.d.ts +114 -29
  145. package/dist/generic/ResolverBase.d.ts.map +1 -1
  146. package/dist/generic/ResolverBase.js +509 -220
  147. package/dist/generic/ResolverBase.js.map +1 -1
  148. package/dist/generic/RunViewResolver.d.ts +15 -3
  149. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  150. package/dist/generic/RunViewResolver.js +126 -20
  151. package/dist/generic/RunViewResolver.js.map +1 -1
  152. package/dist/generic/actionOutputFieldSecurity.d.ts +26 -0
  153. package/dist/generic/actionOutputFieldSecurity.d.ts.map +1 -0
  154. package/dist/generic/actionOutputFieldSecurity.js +47 -0
  155. package/dist/generic/actionOutputFieldSecurity.js.map +1 -0
  156. package/dist/generic/fieldSecurityTransport.d.ts +30 -0
  157. package/dist/generic/fieldSecurityTransport.d.ts.map +1 -0
  158. package/dist/generic/fieldSecurityTransport.js +30 -0
  159. package/dist/generic/fieldSecurityTransport.js.map +1 -0
  160. package/dist/index.d.ts +4 -0
  161. package/dist/index.d.ts.map +1 -1
  162. package/dist/index.js +262 -84
  163. package/dist/index.js.map +1 -1
  164. package/dist/integration/ConnectionPause.d.ts +121 -0
  165. package/dist/integration/ConnectionPause.d.ts.map +1 -0
  166. package/dist/integration/ConnectionPause.js +183 -0
  167. package/dist/integration/ConnectionPause.js.map +1 -0
  168. package/dist/integration/CustomColumnPromoter.d.ts +72 -6
  169. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  170. package/dist/integration/CustomColumnPromoter.js +93 -47
  171. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  172. package/dist/integration/EntityMapLifecycle.d.ts +78 -4
  173. package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
  174. package/dist/integration/EntityMapLifecycle.js +199 -5
  175. package/dist/integration/EntityMapLifecycle.js.map +1 -1
  176. package/dist/integration/InactiveRowWarnings.d.ts +31 -0
  177. package/dist/integration/InactiveRowWarnings.d.ts.map +1 -1
  178. package/dist/integration/InactiveRowWarnings.js +29 -0
  179. package/dist/integration/InactiveRowWarnings.js.map +1 -1
  180. package/dist/integration/ObjectNamesByID.d.ts +28 -0
  181. package/dist/integration/ObjectNamesByID.d.ts.map +1 -0
  182. package/dist/integration/ObjectNamesByID.js +87 -0
  183. package/dist/integration/ObjectNamesByID.js.map +1 -0
  184. package/dist/integration/PostRestartIntrospect.d.ts +26 -0
  185. package/dist/integration/PostRestartIntrospect.d.ts.map +1 -0
  186. package/dist/integration/PostRestartIntrospect.js +27 -0
  187. package/dist/integration/PostRestartIntrospect.js.map +1 -0
  188. package/dist/integration/RSUPostRestartProgress.d.ts +176 -0
  189. package/dist/integration/RSUPostRestartProgress.d.ts.map +1 -0
  190. package/dist/integration/RSUPostRestartProgress.js +323 -0
  191. package/dist/integration/RSUPostRestartProgress.js.map +1 -0
  192. package/dist/integration/RSUProgressBridge.d.ts +41 -2
  193. package/dist/integration/RSUProgressBridge.d.ts.map +1 -1
  194. package/dist/integration/RSUProgressBridge.js +84 -1
  195. package/dist/integration/RSUProgressBridge.js.map +1 -1
  196. package/dist/integration/RunArtifactAuthorization.d.ts +39 -0
  197. package/dist/integration/RunArtifactAuthorization.d.ts.map +1 -0
  198. package/dist/integration/RunArtifactAuthorization.js +45 -0
  199. package/dist/integration/RunArtifactAuthorization.js.map +1 -0
  200. package/dist/integration/SchemaRefreshLaunch.d.ts +9 -0
  201. package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -1
  202. package/dist/integration/SchemaRefreshLaunch.js +3 -2
  203. package/dist/integration/SchemaRefreshLaunch.js.map +1 -1
  204. package/dist/integration/ScopedFieldsForBuild.d.ts +46 -0
  205. package/dist/integration/ScopedFieldsForBuild.d.ts.map +1 -0
  206. package/dist/integration/ScopedFieldsForBuild.js +91 -0
  207. package/dist/integration/ScopedFieldsForBuild.js.map +1 -0
  208. package/dist/integration/TargetConfigGuard.d.ts +40 -0
  209. package/dist/integration/TargetConfigGuard.d.ts.map +1 -0
  210. package/dist/integration/TargetConfigGuard.js +40 -0
  211. package/dist/integration/TargetConfigGuard.js.map +1 -0
  212. package/dist/logging/variablesLoggingMiddleware.d.ts +12 -0
  213. package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
  214. package/dist/logging/variablesLoggingMiddleware.js +50 -1
  215. package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
  216. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -1
  217. package/dist/realtimeSessions/SessionVerificationPortsBase.js +1 -0
  218. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -1
  219. package/dist/realtimeSessions/ownedRealtimeSession.d.ts +8 -6
  220. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
  221. package/dist/realtimeSessions/ownedRealtimeSession.js +14 -6
  222. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
  223. package/dist/realtimeSessions/sessionAccess.d.ts +103 -11
  224. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  225. package/dist/realtimeSessions/sessionAccess.js +147 -22
  226. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  227. package/dist/realtimeSessions/sessionConfigStamps.js +1 -1
  228. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -1
  229. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -1
  230. package/dist/realtimeSessions/subscriptionAuthorization.js +5 -2
  231. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -1
  232. package/dist/realtimeSessions/verificationWorkflow.d.ts +2 -0
  233. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -1
  234. package/dist/realtimeSessions/verificationWorkflow.js +1 -1
  235. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -1
  236. package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
  237. package/dist/realtimeWidget/WidgetRouter.js +19 -3
  238. package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
  239. package/dist/realtimeWidget/WidgetSessionService.d.ts +67 -7
  240. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  241. package/dist/realtimeWidget/WidgetSessionService.js +203 -19
  242. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  243. package/dist/realtimeWidget/host-identity.d.ts +1 -1
  244. package/dist/realtimeWidget/host-identity.js +1 -1
  245. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
  246. package/dist/realtimeWidget/visitorIdentity.js +1 -0
  247. package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
  248. package/dist/realtimeWidget/widgetCore.d.ts +20 -1
  249. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  250. package/dist/realtimeWidget/widgetCore.js +21 -8
  251. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  252. package/dist/realtimeWidget/widgetGuestElevation.d.ts +1 -1
  253. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  254. package/dist/realtimeWidget/widgetGuestElevation.js +3 -1
  255. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  256. package/dist/requestOrigin.d.ts +22 -0
  257. package/dist/requestOrigin.d.ts.map +1 -0
  258. package/dist/requestOrigin.js +60 -0
  259. package/dist/requestOrigin.js.map +1 -0
  260. package/dist/resolvers/ActionResolver.d.ts +19 -1
  261. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  262. package/dist/resolvers/ActionResolver.js +63 -12
  263. package/dist/resolvers/ActionResolver.js.map +1 -1
  264. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  265. package/dist/resolvers/AdhocQueryResolver.js +1 -0
  266. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  267. package/dist/resolvers/AgentRunStatusPublisher.d.ts +18 -4
  268. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
  269. package/dist/resolvers/AgentRunStatusPublisher.js +32 -8
  270. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
  271. package/dist/resolvers/AgentSessionResolver.d.ts +10 -4
  272. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  273. package/dist/resolvers/AgentSessionResolver.js +44 -17
  274. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  275. package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
  276. package/dist/resolvers/AutotagPipelineResolver.js +2 -1
  277. package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
  278. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  279. package/dist/resolvers/ConversationRunEventsResolver.js +2 -0
  280. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  281. package/dist/resolvers/EntityCommunicationsResolver.d.ts +13 -1
  282. package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
  283. package/dist/resolvers/EntityCommunicationsResolver.js +50 -14
  284. package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
  285. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +6 -0
  286. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -1
  287. package/dist/resolvers/ExecuteRemoteOperationResolver.js +30 -7
  288. package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -1
  289. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +28 -0
  290. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  291. package/dist/resolvers/FetchEntityVectorsResolver.js +117 -21
  292. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  293. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  294. package/dist/resolvers/FileCategoryResolver.js +1 -0
  295. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  296. package/dist/resolvers/FileResolver.d.ts +5 -0
  297. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  298. package/dist/resolvers/FileResolver.js +13 -4
  299. package/dist/resolvers/FileResolver.js.map +1 -1
  300. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  301. package/dist/resolvers/GetDataResolver.js +34 -13
  302. package/dist/resolvers/GetDataResolver.js.map +1 -1
  303. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +401 -11
  304. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  305. package/dist/resolvers/IntegrationDiscoveryResolver.js +2713 -1290
  306. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  307. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  308. package/dist/resolvers/MCPResolver.js +13 -2
  309. package/dist/resolvers/MCPResolver.js.map +1 -1
  310. package/dist/resolvers/MergeRecordsResolver.d.ts +8 -1
  311. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  312. package/dist/resolvers/MergeRecordsResolver.js +31 -3
  313. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  314. package/dist/resolvers/PipelineProgressResolver.d.ts +6 -0
  315. package/dist/resolvers/PipelineProgressResolver.d.ts.map +1 -1
  316. package/dist/resolvers/PipelineProgressResolver.js +5 -3
  317. package/dist/resolvers/PipelineProgressResolver.js.map +1 -1
  318. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  319. package/dist/resolvers/QueryResolver.js +20 -6
  320. package/dist/resolvers/QueryResolver.js.map +1 -1
  321. package/dist/resolvers/QuerySystemUserResolver.d.ts +38 -0
  322. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  323. package/dist/resolvers/QuerySystemUserResolver.js +184 -88
  324. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  325. package/dist/resolvers/RSUResolver.d.ts.map +1 -1
  326. package/dist/resolvers/RSUResolver.js +12 -0
  327. package/dist/resolvers/RSUResolver.js.map +1 -1
  328. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  329. package/dist/resolvers/RealtimeBridgeResolver.js +1 -0
  330. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  331. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +22 -2
  332. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  333. package/dist/resolvers/RealtimeClientSessionResolver.js +118 -33
  334. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  335. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +62 -10
  336. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  337. package/dist/resolvers/RemoteBrowserActionResolver.js +224 -39
  338. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  339. package/dist/resolvers/RunAIAgentResolver.d.ts +80 -16
  340. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  341. package/dist/resolvers/RunAIAgentResolver.js +388 -125
  342. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  343. package/dist/resolvers/RunAIPromptResolver.d.ts +5 -3
  344. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  345. package/dist/resolvers/RunAIPromptResolver.js +19 -18
  346. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  347. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  348. package/dist/resolvers/RunTemplateResolver.js +7 -5
  349. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  350. package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
  351. package/dist/resolvers/RunTestResolver.js +3 -1
  352. package/dist/resolvers/RunTestResolver.js.map +1 -1
  353. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  354. package/dist/resolvers/SyncDataResolver.js +4 -0
  355. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  356. package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
  357. package/dist/resolvers/TestQuerySQLResolver.js +1 -0
  358. package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
  359. package/dist/resolvers/TransactionGroupResolver.d.ts +17 -2
  360. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  361. package/dist/resolvers/TransactionGroupResolver.js +60 -15
  362. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  363. package/dist/resolvers/UserViewResolver.d.ts +1 -1
  364. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  365. package/dist/resolvers/UserViewResolver.js +2 -1
  366. package/dist/resolvers/UserViewResolver.js.map +1 -1
  367. package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
  368. package/dist/resolvers/meetingRecordingRegistration.js +2 -0
  369. package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
  370. package/dist/rest/EntityCRUDHandler.d.ts +6 -0
  371. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  372. package/dist/rest/EntityCRUDHandler.js +19 -5
  373. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  374. package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
  375. package/dist/rest/MediaAccessKeys.js +6 -2
  376. package/dist/rest/MediaAccessKeys.js.map +1 -1
  377. package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
  378. package/dist/rest/OAuthCallbackHandler.js +11 -5
  379. package/dist/rest/OAuthCallbackHandler.js.map +1 -1
  380. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  381. package/dist/rest/RESTEndpointHandler.js +8 -5
  382. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  383. package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
  384. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
  385. package/dist/rest/RealtimeRecordingHandler.js +6 -8
  386. package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
  387. package/dist/rest/ViewOperationsHandler.d.ts +6 -0
  388. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  389. package/dist/rest/ViewOperationsHandler.js +24 -6
  390. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  391. package/dist/rest/setupRESTEndpoints.d.ts +3 -1
  392. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  393. package/dist/rest/setupRESTEndpoints.js +11 -2
  394. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  395. package/dist/serverRoleConfigUnits.d.ts +29 -0
  396. package/dist/serverRoleConfigUnits.d.ts.map +1 -0
  397. package/dist/serverRoleConfigUnits.js +33 -0
  398. package/dist/serverRoleConfigUnits.js.map +1 -0
  399. package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -1
  400. package/dist/services/DurableEntityActionTaskSubmitter.js +6 -1
  401. package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -1
  402. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  403. package/dist/services/TaskGraphActionRunner.js +2 -26
  404. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  405. package/dist/services/TaskGraphAgentRunner.d.ts +6 -0
  406. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  407. package/dist/services/TaskGraphAgentRunner.js +38 -0
  408. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  409. package/dist/services/TaskGraphContinuationDeliverer.d.ts +5 -0
  410. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  411. package/dist/services/TaskGraphContinuationDeliverer.js +43 -15
  412. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  413. package/dist/services/WorkQueueHostService.d.ts +2 -1
  414. package/dist/services/WorkQueueHostService.d.ts.map +1 -1
  415. package/dist/services/WorkQueueHostService.js +19 -4
  416. package/dist/services/WorkQueueHostService.js.map +1 -1
  417. package/dist/services/workQueueConfig.d.ts +2 -2
  418. package/dist/services/workQueueConfig.js +5 -5
  419. package/dist/services/workQueueConfig.js.map +1 -1
  420. package/dist/sharedCache.js +1 -1
  421. package/dist/sharedCache.js.map +1 -1
  422. package/dist/types.d.ts +40 -1
  423. package/dist/types.d.ts.map +1 -1
  424. package/dist/types.js.map +1 -1
  425. package/package.json +98 -98
  426. package/src/__tests__/APIKeyScopeAuth.test.ts +4 -8
  427. package/src/__tests__/ActionResolver.fieldSecurity.test.ts +237 -0
  428. package/src/__tests__/ActiveOperations.test.ts +68 -0
  429. package/src/__tests__/AdhocQueryResolver.dialect.test.ts +137 -0
  430. package/src/__tests__/AgentRunStatusPublisher.test.ts +49 -0
  431. package/src/__tests__/AgentSessionResolver.ownership.test.ts +150 -0
  432. package/src/__tests__/AssertTargetConfigs.test.ts +75 -0
  433. package/src/__tests__/BatchApplyParity.test.ts +109 -0
  434. package/src/__tests__/BridgeHostInstance.test.ts +42 -0
  435. package/src/__tests__/CollectInactiveRowWarnings.test.ts +115 -0
  436. package/src/__tests__/ConnectionPause.test.ts +192 -0
  437. package/src/__tests__/ConnectionPauseResolver.test.ts +166 -0
  438. package/src/__tests__/ConnectionPauseSave.test.ts +143 -0
  439. package/src/__tests__/CustomColumnPromoterCompletion.test.ts +142 -0
  440. package/src/__tests__/CustomColumnPromoterOnePass.test.ts +15 -1
  441. package/src/__tests__/DeleteConnectionRemovesPerConnectionCatalog.test.ts +51 -0
  442. package/src/__tests__/DetachedDiscoveryStart.test.ts +65 -0
  443. package/src/__tests__/DurableEntityActionTaskSubmitter.test.ts +73 -0
  444. package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
  445. package/src/__tests__/EntityMapLifecycle.rekey.test.ts +252 -0
  446. package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
  447. package/src/__tests__/ExecuteRemoteOperationResolver.test.ts +213 -0
  448. package/src/__tests__/FetchEntityVectorsResolver.test.ts +206 -3
  449. package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
  450. package/src/__tests__/HandoffOfferExpiry.test.ts +40 -0
  451. package/src/__tests__/IntegrationRunArtifactAuthorization.test.ts +126 -0
  452. package/src/__tests__/MagicLinkKeys.test.ts +102 -0
  453. package/src/__tests__/MediaAccessKeys.test.ts +57 -10
  454. package/src/__tests__/NewArrivalsDefaultDisabled.test.ts +50 -0
  455. package/src/__tests__/ObjectNamesByID.test.ts +176 -0
  456. package/src/__tests__/PipelineProgressResolver.filter.test.ts +58 -0
  457. package/src/__tests__/PostRestartMapsInCatalogScope.test.ts +146 -0
  458. package/src/__tests__/QuerySystemUserResolver.childRows.test.ts +80 -0
  459. package/src/__tests__/QuerySystemUserResolver.collision.test.ts +200 -0
  460. package/src/__tests__/QuerySystemUserResolver.fieldSecurity.test.ts +49 -0
  461. package/src/__tests__/QuerySystemUserResolver.permissions.test.ts +132 -0
  462. package/src/__tests__/RESTEndpointHandler.test.ts +25 -0
  463. package/src/__tests__/RSUPostRestartProgress.test.ts +343 -0
  464. package/src/__tests__/RSUProgressBridge.test.ts +188 -5
  465. package/src/__tests__/RSUStartupSweepWiring.test.ts +30 -0
  466. package/src/__tests__/ReactivateRefreshMessage.test.ts +10 -3
  467. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +23 -3
  468. package/src/__tests__/RealtimeClientSessionResolver.test.ts +346 -46
  469. package/src/__tests__/{RefreshAdoptsNewColumns.test.ts → RefreshReportsNewColumns.test.ts} +25 -16
  470. package/src/__tests__/RemoteBrowserActionRecovery.test.ts +287 -0
  471. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +3 -0
  472. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +100 -0
  473. package/src/__tests__/RemoteBrowserAudioStream.test.ts +3 -0
  474. package/src/__tests__/RemoteBrowserSnapshot.test.ts +3 -0
  475. package/src/__tests__/ResolverBase.clientOldValues.test.ts +344 -14
  476. package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
  477. package/src/__tests__/ResolverBase.encryptedApiZeroReencrypt.test.ts +307 -0
  478. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +227 -0
  479. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +83 -1
  480. package/src/__tests__/ResolverBase.publishStatusUpdate.test.ts +68 -0
  481. package/src/__tests__/ResolverBase.userViewCacheAndDedup.test.ts +130 -0
  482. package/src/__tests__/RunAIAgentResolver.branch.test.ts +72 -13
  483. package/src/__tests__/RunAIAgentResolver.consultPredecessor.test.ts +289 -0
  484. package/src/__tests__/RunAIAgentResolver.conversationWriter.test.ts +210 -0
  485. package/src/__tests__/RunAIAgentResolver.fireAndForgetCompletion.test.ts +209 -0
  486. package/src/__tests__/RunAIAgentResolver.handoff.test.ts +649 -0
  487. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +19 -27
  488. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +10 -2
  489. package/src/__tests__/RunAIAgentResolver.serverReply.test.ts +358 -0
  490. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +91 -13
  491. package/src/__tests__/RunTemplateResolver.test.ts +35 -2
  492. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +11 -0
  493. package/src/__tests__/SchemaEvolutionCustomColumns.test.ts +119 -0
  494. package/src/__tests__/SchemaRefreshLaunch.test.ts +24 -2
  495. package/src/__tests__/ScopedFieldsForBuild.test.ts +192 -0
  496. package/src/__tests__/SessionJanitor.test.ts +195 -4
  497. package/src/__tests__/SessionManager.test.ts +164 -0
  498. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +2 -2
  499. package/src/__tests__/TaskGraphAgentRunner.test.ts +272 -16
  500. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +114 -3
  501. package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
  502. package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
  503. package/src/__tests__/ViewOperationsHandler.test.ts +9 -7
  504. package/src/__tests__/WidgetSessionService.test.ts +583 -4
  505. package/src/__tests__/WorkQueueHostService.test.ts +25 -7
  506. package/src/__tests__/apiRootBoot.test.ts +1 -1
  507. package/src/__tests__/backgroundSweepConfigUnits.test.ts +153 -0
  508. package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
  509. package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
  510. package/src/__tests__/cacheInvalidationRecordData.test.ts +45 -0
  511. package/src/__tests__/contextRequestOrigin.test.ts +105 -0
  512. package/src/__tests__/conversationBrowsers.test.ts +62 -0
  513. package/src/__tests__/fieldSecurityFixtures.ts +91 -0
  514. package/src/__tests__/globalCors.test.ts +3 -3
  515. package/src/__tests__/localDevAuth.test.ts +196 -0
  516. package/src/__tests__/magicLinkExtensionCapabilities.test.ts +413 -0
  517. package/src/__tests__/orphanedConversationDetailReconciler.start.test.ts +82 -0
  518. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +10 -0
  519. package/src/__tests__/ownedRealtimeSession.test.ts +14 -0
  520. package/src/__tests__/pushStatusResolver.filter.test.ts +57 -1
  521. package/src/__tests__/realtimeRecordingHandler.test.ts +24 -2
  522. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +29 -1
  523. package/src/__tests__/realtimeSessions/ownedRealtimeSession.anonymous.test.ts +31 -0
  524. package/src/__tests__/realtimeSessions/ownedRealtimeSession.namedScoped.test.ts +37 -0
  525. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +224 -7
  526. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +1 -1
  527. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +25 -4
  528. package/src/__tests__/requestOrigin.test.ts +104 -0
  529. package/src/__tests__/resolverBase.cloudEventChangedFields.test.ts +157 -0
  530. package/src/__tests__/resolverBase.fieldSecurityTransport.test.ts +222 -0
  531. package/src/__tests__/resolverBase.fls.test.ts +146 -97
  532. package/src/__tests__/restTransport.fieldSecurity.test.ts +148 -0
  533. package/src/__tests__/runParamsTimeZone.test.ts +179 -0
  534. package/src/__tests__/runStreams.fieldSecurity.test.ts +100 -0
  535. package/src/__tests__/runViewProperties.test.ts +114 -0
  536. package/src/__tests__/scalabilityBenchmarks.test.ts +765 -0
  537. package/src/__tests__/scalabilityDatabaseIntegration.test.ts +207 -0
  538. package/src/__tests__/securityHardeningPr6034.test.ts +120 -0
  539. package/src/__tests__/sessionDeadline.test.ts +51 -0
  540. package/src/__tests__/sessionUserClone.test.ts +5 -1
  541. package/src/__tests__/sharedCache.test.ts +11 -0
  542. package/src/__tests__/startupSweepOrder.test.ts +40 -0
  543. package/src/__tests__/unifiedAuth.test.ts +101 -1
  544. package/src/__tests__/widgetGuestElevation.test.ts +2 -0
  545. package/src/agentSessions/BridgeHostInstance.ts +20 -5
  546. package/src/agentSessions/HandoffOfferExpiry.ts +18 -0
  547. package/src/agentSessions/ReturningVisitorRecap.ts +1 -0
  548. package/src/agentSessions/SessionJanitor.ts +95 -45
  549. package/src/agentSessions/SessionManager.ts +73 -20
  550. package/src/agentSessions/conversationBrowsers.ts +143 -0
  551. package/src/agentSessions/sessionDeadline.ts +81 -0
  552. package/src/auth/APIKeyScopeAuth.ts +5 -4
  553. package/src/auth/AuthProviderCatalogRouter.ts +3 -0
  554. package/src/auth/__tests__/loginEmailResolution.test.ts +67 -0
  555. package/src/auth/index.ts +56 -12
  556. package/src/auth/initializeProviders.ts +43 -1
  557. package/src/auth/localDevAuth.ts +528 -0
  558. package/src/auth/loginEmailResolution.ts +67 -0
  559. package/src/auth/magicLink/MagicLinkKeys.ts +147 -15
  560. package/src/auth/magicLink/MagicLinkRouter.ts +33 -6
  561. package/src/auth/magicLink/MagicLinkService.ts +250 -10
  562. package/src/auth/magicLink/index.ts +1 -1
  563. package/src/auth/magicLink/redeemLanding.ts +7 -4
  564. package/src/auth/magicLink/types.ts +38 -3
  565. package/src/auth/newUsers.ts +1 -0
  566. package/src/auth/sessionUserClone.ts +3 -2
  567. package/src/backgroundSweepConfigUnits.ts +213 -0
  568. package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +674 -0
  569. package/src/broadcast/index.ts +5 -0
  570. package/src/broadcast/providers/AWSIoTCoreBroadcastProvider.ts +180 -0
  571. package/src/broadcast/providers/AzureWebPubSubBroadcastProvider.ts +182 -0
  572. package/src/broadcast/providers/GCPPubSubBroadcastProvider.ts +169 -0
  573. package/src/broadcast/providers/SequencedRoleBucketBroadcastProvider.ts +57 -0
  574. package/src/cache.ts +2 -1
  575. package/src/config.ts +138 -0
  576. package/src/context.ts +71 -14
  577. package/src/coreRoutes.ts +29 -7
  578. package/src/generated/graphql-schemas/__mj.ts +4145 -1193
  579. package/src/generic/CacheInvalidationResolver.ts +135 -78
  580. package/src/generic/OrphanedConversationDetailReconciler.ts +36 -3
  581. package/src/generic/PubSubManager.ts +54 -1
  582. package/src/generic/PushStatusResolver.ts +108 -11
  583. package/src/generic/ResolverBase.ts +635 -234
  584. package/src/generic/RunViewResolver.ts +176 -64
  585. package/src/generic/actionOutputFieldSecurity.ts +75 -0
  586. package/src/generic/fieldSecurityTransport.ts +62 -0
  587. package/src/index.ts +310 -85
  588. package/src/integration/ConnectionPause.ts +222 -0
  589. package/src/integration/CustomColumnPromoter.ts +129 -54
  590. package/src/integration/EntityMapLifecycle.ts +229 -6
  591. package/src/integration/InactiveRowWarnings.ts +50 -0
  592. package/src/integration/ObjectNamesByID.ts +97 -0
  593. package/src/integration/PostRestartIntrospect.ts +49 -0
  594. package/src/integration/RSUPostRestartProgress.ts +359 -0
  595. package/src/integration/RSUProgressBridge.ts +85 -2
  596. package/src/integration/RunArtifactAuthorization.ts +65 -0
  597. package/src/integration/SchemaRefreshLaunch.ts +12 -2
  598. package/src/integration/ScopedFieldsForBuild.ts +113 -0
  599. package/src/integration/TargetConfigGuard.ts +71 -0
  600. package/src/logging/variablesLoggingMiddleware.ts +59 -3
  601. package/src/realtimeSessions/README.md +7 -3
  602. package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
  603. package/src/realtimeSessions/ownedRealtimeSession.ts +16 -6
  604. package/src/realtimeSessions/sessionAccess.ts +211 -25
  605. package/src/realtimeSessions/sessionConfigStamps.ts +1 -1
  606. package/src/realtimeSessions/subscriptionAuthorization.ts +4 -2
  607. package/src/realtimeSessions/verificationWorkflow.ts +3 -1
  608. package/src/realtimeWidget/WidgetRouter.ts +22 -4
  609. package/src/realtimeWidget/WidgetSessionService.ts +254 -17
  610. package/src/realtimeWidget/host-identity.ts +1 -1
  611. package/src/realtimeWidget/visitorIdentity.ts +1 -0
  612. package/src/realtimeWidget/widgetCore.ts +34 -9
  613. package/src/realtimeWidget/widgetGuestElevation.ts +3 -1
  614. package/src/requestOrigin.ts +64 -0
  615. package/src/resolvers/ActionResolver.ts +78 -15
  616. package/src/resolvers/AdhocQueryResolver.ts +1 -0
  617. package/src/resolvers/AgentRunStatusPublisher.ts +36 -8
  618. package/src/resolvers/AgentSessionResolver.ts +50 -18
  619. package/src/resolvers/AutotagPipelineResolver.ts +2 -1
  620. package/src/resolvers/ConversationRunEventsResolver.ts +2 -0
  621. package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
  622. package/src/resolvers/ExecuteRemoteOperationResolver.ts +41 -9
  623. package/src/resolvers/FetchEntityVectorsResolver.ts +132 -21
  624. package/src/resolvers/FileCategoryResolver.ts +1 -0
  625. package/src/resolvers/FileResolver.ts +14 -4
  626. package/src/resolvers/GetDataResolver.ts +35 -13
  627. package/src/resolvers/IntegrationDiscoveryResolver.ts +1570 -209
  628. package/src/resolvers/MCPResolver.ts +15 -2
  629. package/src/resolvers/MergeRecordsResolver.ts +23 -2
  630. package/src/resolvers/PipelineProgressResolver.ts +12 -3
  631. package/src/resolvers/QueryResolver.ts +20 -6
  632. package/src/resolvers/QuerySystemUserResolver.ts +207 -99
  633. package/src/resolvers/RSUResolver.ts +12 -0
  634. package/src/resolvers/RealtimeBridgeResolver.ts +1 -0
  635. package/src/resolvers/RealtimeClientSessionResolver.ts +144 -40
  636. package/src/resolvers/RemoteBrowserActionResolver.ts +252 -37
  637. package/src/resolvers/RunAIAgentResolver.ts +518 -163
  638. package/src/resolvers/RunAIPromptResolver.ts +18 -18
  639. package/src/resolvers/RunTemplateResolver.ts +9 -5
  640. package/src/resolvers/RunTestResolver.ts +3 -1
  641. package/src/resolvers/SyncDataResolver.ts +4 -0
  642. package/src/resolvers/TestQuerySQLResolver.ts +1 -0
  643. package/src/resolvers/TransactionGroupResolver.ts +66 -15
  644. package/src/resolvers/UserViewResolver.ts +2 -1
  645. package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +52 -0
  646. package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
  647. package/src/resolvers/meetingRecordingRegistration.ts +2 -0
  648. package/src/rest/EntityCRUDHandler.ts +22 -5
  649. package/src/rest/MediaAccessKeys.ts +6 -2
  650. package/src/rest/OAuthCallbackHandler.ts +13 -5
  651. package/src/rest/RESTEndpointHandler.ts +8 -5
  652. package/src/rest/RealtimeRecordingHandler.ts +9 -10
  653. package/src/rest/ViewOperationsHandler.ts +26 -7
  654. package/src/rest/setupRESTEndpoints.ts +11 -2
  655. package/src/serverRoleConfigUnits.ts +48 -0
  656. package/src/services/DurableEntityActionTaskSubmitter.ts +10 -2
  657. package/src/services/TaskGraphActionRunner.ts +2 -23
  658. package/src/services/TaskGraphAgentRunner.ts +43 -0
  659. package/src/services/TaskGraphContinuationDeliverer.ts +53 -20
  660. package/src/services/WorkQueueHostService.ts +26 -4
  661. package/src/services/workQueueConfig.ts +5 -5
  662. package/src/sharedCache.ts +1 -1
  663. package/src/types.ts +41 -1
  664. package/src/services/TaskOrchestration-Integration.md +0 -188
@@ -1,20 +1,23 @@
1
1
  import { Resolver, Mutation, Query, Arg, Ctx, ObjectType, Field, PubSub, PubSubEngine, Subscription, Root, ResolverFilterData, ID, Int } from 'type-graphql';
2
2
  import { AppContext, UserPayload } from '../types.js';
3
3
  import { DatabaseProviderBase, LogError, LogStatus, Metadata, RunView, UserInfo, IMetadataProvider } from '@memberjunction/core';
4
- import { MJConversationDetailEntity, MJConversationDetailAttachmentEntity, MJConversationDetailArtifactEntity, MJArtifactVersionEntity, MJAIAgentRequestEntity, ArtifactMetadataEngine, ConversationEngine } from '@memberjunction/core-entities';
4
+ import { MJConversationDetailEntity, MJConversationDetailEntityExtended, MJConversationDetailAttachmentEntity, MJConversationDetailArtifactEntity, MJArtifactVersionEntity, MJAIAgentRequestEntity, ArtifactMetadataEngine, ConversationEngine } from '@memberjunction/core-entities';
5
5
  import { RouteArtifact } from './artifact-routing.js';
6
- import { AgentRunner, ArtifactToolManager } from '@memberjunction/ai-agents';
7
- import { MJAIAgentEntityExtended, MJAIAgentRunEntityExtended, ExecuteAgentResult, ConversationUtility, AttachmentData } from '@memberjunction/ai-core-plus';
6
+ import { AgentRunner, ArtifactToolManager, CanAgentOwnSubAgentConsults, LoadOpenSubAgentConsults, HostAllowedAgentIDs } from '@memberjunction/ai-agents';
7
+ import { MJAIAgentEntityExtended, MJAIAgentRunEntityExtended, ExecuteAgentResult, ConversationUtility, AttachmentData, ContinuationAnnouncementText } from '@memberjunction/ai-core-plus';
8
8
  import { AIEngine } from '@memberjunction/aiengine';
9
9
  import { ChatMessage, ChatMessageContent } from '@memberjunction/ai';
10
10
  import { ResolverBase } from '../generic/ResolverBase.js';
11
11
  import { AgentRunStatusPublisher } from './AgentRunStatusPublisher.js';
12
+ import { ProjectEntityObjectForCaller } from '../generic/fieldSecurityTransport.js';
12
13
  import { StartLivenessPulse } from '../generic/FireAndForgetHeartbeat.js';
14
+ import { StatusUpdateOwnerID } from '../generic/PushStatusResolver.js';
13
15
  import { RequireSystemUser } from '../directives/RequireSystemUser.js';
14
16
  import { GetReadWriteProvider } from '../util.js';
15
17
  import { ResolveWidgetGuestRunContext, ElevateUserPayload } from '../realtimeWidget/widgetGuestElevation.js';
18
+ import { AuthorizeConversationTurn } from '../realtimeSessions/sessionAccess.js';
16
19
  import { IsTrustedAgentRunCaller } from './agent-run-data-guard.js';
17
- import { SafeJSONParse, UUIDsEqual } from '@memberjunction/global';
20
+ import { EscapeSQLString, IsValidUUID, SafeJSONParse, UUIDsEqual } from '@memberjunction/global';
18
21
  import { GetAttachmentService } from '@memberjunction/aiengine';
19
22
  import { NotificationEngine } from '@memberjunction/notifications';
20
23
 
@@ -32,6 +35,17 @@ function isAgentRunEntity(value: unknown): value is MJAIAgentRunEntityExtended {
32
35
  return typeof value === 'object' && value !== null && 'ID' in value;
33
36
  }
34
37
 
38
+ /** What a conversation turn asks for, before its agent runs (see `RunAIAgentFromConversationDetail`). */
39
+ interface ConversationTurnRequest {
40
+ UserMessageId: string;
41
+ AgentId: string;
42
+ MaxHistoryMessages: number;
43
+ HistoryFrom?: Date;
44
+ ReplyParentId?: string;
45
+ AnnouncedByAgentId?: string;
46
+ ReplyBranchId?: string | null;
47
+ }
48
+
35
49
  @ObjectType()
36
50
  export class AIAgentRunResult {
37
51
  @Field()
@@ -169,11 +183,18 @@ export class AgentExecutionStreamMessage {
169
183
 
170
184
  @Resolver()
171
185
  export class RunAIAgentResolver extends ResolverBase {
186
+ /**
187
+ * Tracks `userPayload` instances originating from interactive user mutations (`RunAIAgent` and
188
+ * `RunAIAgentFromConversationDetail` without API key or widget guest elevation) that may expose
189
+ * `Handoff` and follow returned handoff decisions.
190
+ */
191
+ private readonly handoffEligiblePayloads = new WeakSet<object>();
192
+
172
193
  /**
173
194
  * Sanitize ExecuteAgentResult for JSON serialization
174
195
  * Removes circular references and non-serializable objects
175
196
  */
176
- private sanitizeAgentResult(result: ExecuteAgentResult): any {
197
+ private sanitizeAgentResult(result: ExecuteAgentResult, recipient?: UserInfo): any {
177
198
  const sanitized: any = {
178
199
  success: result.success,
179
200
  payload: result.payload,
@@ -184,13 +205,15 @@ export class RunAIAgentResolver extends ResolverBase {
184
205
  finalStep: result.agentRun?.FinalStep,
185
206
  cancelled: result.agentRun?.Status === 'Cancelled',
186
207
  cancellationReason: result.agentRun?.CancellationReason,
187
- feedbackRequestId: result.feedbackRequestId
208
+ feedbackRequestId: result.feedbackRequestId,
209
+ handoff: result.handoff
188
210
  };
189
211
 
190
212
  // Safely extract agent run data using GetAll() for proper serialization
191
213
  if (result.agentRun && typeof result.agentRun.GetAll === 'function') {
192
- // Use GetAll() to get the full serialized object including extended properties
193
- sanitized.agentRun = result.agentRun.GetAll();
214
+ // GetAll() for the full serialized object including extended properties, projected for
215
+ // the recipient: the run comes from the agent engine, not from a load bound to them
216
+ sanitized.agentRun = ProjectEntityObjectForCaller(result.agentRun, recipient);
194
217
  }
195
218
  else {
196
219
  // shouldn't ever get here
@@ -306,7 +329,12 @@ export class RunAIAgentResolver extends ResolverBase {
306
329
  /** ExecuteAgentParams.TrustReservedRunData: true only for a caller IsTrustedAgentRunCaller accepts. */
307
330
  trustReservedRunData: boolean = false,
308
331
  /** The conversation branch of the run — threaded into ExecuteAgentParams.ConversationBranchID. */
309
- conversationBranchId?: string | null
332
+ conversationBranchId?: string | null,
333
+ /** The reply row this request created on the server for the run (`conversationDetailId` is its ID). The run
334
+ * writes into this instance, so its writes carry the server-authored marker and the user it was made as. */
335
+ replyDetail?: MJConversationDetailEntityExtended,
336
+ /** Optional explicit handoff override; defaults to whether the caller registered `userPayload` as interactive. */
337
+ followHandoffs?: boolean
310
338
  ): Promise<AIAgentRunResult> {
311
339
  const startTime = Date.now();
312
340
  // Best-effort handle for persistInFlightAgentFailure. Populated from a
@@ -315,6 +343,8 @@ export class RunAIAgentResolver extends ResolverBase {
315
343
  // run is not marked Failed — the early-failure case we most want to
316
344
  // persist, but we have no run object yet.
317
345
  const agentRunRef = runRef ?? { current: null as MJAIAgentRunEntityExtended | null };
346
+ // The publisher owns every status message, including the failure completion the catch below sends.
347
+ const statusPublisher = new AgentRunStatusPublisher(pubSub, userPayload, sessionId, conversationDetailId);
318
348
 
319
349
  try {
320
350
  LogStatus(`=== RUNNING AI AGENT FOR ID: ${agentId} ===`);
@@ -350,24 +380,34 @@ export class RunAIAgentResolver extends ResolverBase {
350
380
 
351
381
  // The publisher owns every status message. The resolver still copies the run into
352
382
  // agentRunRef, including noise steps, for the fire-and-forget liveness pulse.
353
- const statusPublisher = new AgentRunStatusPublisher(pubSub, userPayload, sessionId);
354
- const publishProgress = statusPublisher.OnProgress;
383
+ const sourcePublisher = statusPublisher;
384
+ let currentPublisher = sourcePublisher;
385
+
386
+ // A widget- or guest-hosted run must not get a handoffChain unless the host also passes an
387
+ // allow-list under trusted run data.
388
+ const effectiveFollowHandoffs = followHandoffs ?? this.handoffEligiblePayloads.has(userPayload);
389
+ const isGuestOrWidgetUser = Boolean(
390
+ currentUser.IsMagicLinkAnonymous || currentUser.WidgetGuestContext
391
+ );
392
+ const allowHandoff = effectiveFollowHandoffs && (
393
+ !isGuestOrWidgetUser
394
+ || (trustReservedRunData && HostAllowedAgentIDs(parsedData) !== undefined)
395
+ );
355
396
 
356
- // Execute the agent in conversation context - handles conversation, artifacts, etc.
357
- const conversationResult = await agentRunner.RunAgentInConversation({
397
+ const sourceParams = {
358
398
  agent: agentEntity,
359
- conversationMessages: parsedMessages,
399
+ conversationMessages: [...parsedMessages],
360
400
  payload: payload ? SafeJSONParse(payload) : undefined,
361
401
  contextUser: currentUser,
362
402
  sessionID: sessionId,
363
- onProgress: (progress) => {
403
+ onProgress: (progress: Parameters<AgentRunStatusPublisher['OnProgress']>[0]) => {
364
404
  const fromEvent = progress.metadata?.agentRun;
365
405
  if (isAgentRunEntity(fromEvent)) {
366
406
  agentRunRef.current = fromEvent;
367
407
  }
368
- publishProgress(progress);
408
+ sourcePublisher.OnProgress(progress);
369
409
  },
370
- onStreaming: statusPublisher.OnStreaming,
410
+ onStreaming: sourcePublisher.OnStreaming,
371
411
  lastRunId: lastRunId,
372
412
  autoPopulateLastRunPayload: autoPopulateLastRunPayload,
373
413
  configurationId: configurationId,
@@ -376,21 +416,28 @@ export class RunAIAgentResolver extends ResolverBase {
376
416
  taskGraphDebug: parseTaskGraphDebug(taskGraphDebug),
377
417
  ConversationHistoryFrom: conversationHistoryFrom,
378
418
  ...(conversationBranchId ? { ConversationBranchID: conversationBranchId } : {}),
419
+ ...(allowHandoff ? { handoffChain: [] as string[] } : {}),
379
420
  data: parsedData,
380
421
  // Whether the run may read the reserved data keys (scope, agent-type params); BaseAgent drops them otherwise.
381
422
  TrustReservedRunData: trustReservedRunData,
382
423
  context: {
383
424
  dataSource: dataSource
384
425
  }
385
- }, {
426
+ };
427
+
428
+ // Execute the agent in conversation context - handles conversation, artifacts, etc.
429
+ const conversationResult = await agentRunner.RunAgentInConversation(sourceParams, {
386
430
  conversationDetailId: conversationDetailId, // Use existing if provided
431
+ agentResponseDetail: replyDetail, // the reply row this request created, when it did
387
432
  conversationId: conversationId, // LATENCY OPT #2: pre-resolved to skip redundant load in AgentRunner
388
433
  userMessage: userMessage, // Provide user message when conversationDetailId not provided
389
434
  createArtifacts: createArtifacts || false,
390
- sourceArtifactId: sourceArtifactId
435
+ sourceArtifactId: sourceArtifactId,
436
+ // A widget guest runs as the system user, but its conversation stays the guest's to write (MJ#4791).
437
+ ConversationWriter: userPayload.ElevatedFrom,
391
438
  });
392
439
 
393
- const result = conversationResult.agentResult;
440
+ let result = conversationResult.agentResult;
394
441
  // Use agent response detail ID if available, otherwise fall back to user message detail ID
395
442
  const finalConversationDetailId = conversationResult.agentResponseDetailId || conversationResult.userMessageDetailId;
396
443
  const artifactInfo = conversationResult.artifactInfo;
@@ -400,61 +447,111 @@ export class RunAIAgentResolver extends ResolverBase {
400
447
  agentRunRef.current = result.agentRun;
401
448
  }
402
449
 
403
- const executionTime = Date.now() - startTime;
404
-
405
- // LATENCY OPTIMIZATION (Opt #6): These three post-execution operations are independent
406
- // of each other — none reads the output of another. Previously they ran sequentially,
407
- // adding their latencies together (~50ms total). Now they run in parallel via Promise.all,
408
- // so we only pay the cost of the slowest one.
409
- //
410
- // 1. syncFeedbackRequestFromConversation — links a prior Chat-step feedback request to
411
- // the new agent run so the conversation thread stays coherent.
412
- // 2. sendFeedbackRequestNotification — sends an in-app/email/SMS notification when the
413
- // agent paused for human input (Chat step).
414
- // 3. createCompletionNotification — sends an in-app/email/SMS notification that the
415
- // agent finished and created an artifact.
416
- const postExecutionOps: Promise<void>[] = [];
417
-
418
- if (lastRunId && result.agentRun?.ID) {
419
- postExecutionOps.push(
420
- this.syncFeedbackRequestFromConversation(lastRunId, result.agentRun.ID, userMessage, currentUser, p)
421
- );
422
- }
423
-
424
- if (result.feedbackRequestId) {
425
- postExecutionOps.push(
426
- this.sendFeedbackRequestNotification(result, currentUser, pubSub, userPayload)
427
- );
428
- }
450
+ await this.runPostExecutionNotifications(
451
+ result,
452
+ artifactInfo,
453
+ conversationResult.conversationId,
454
+ finalConversationDetailId,
455
+ lastRunId,
456
+ userMessage,
457
+ createNotification,
458
+ currentUser,
459
+ pubSub,
460
+ userPayload,
461
+ p
462
+ );
429
463
 
430
- if (createNotification && result.success && artifactInfo && artifactInfo.artifactId && artifactInfo.versionId && artifactInfo.versionNumber) {
431
- postExecutionOps.push(
432
- this.createCompletionNotification(
433
- result.agentRun,
434
- {
435
- artifactId: artifactInfo.artifactId,
436
- versionId: artifactInfo.versionId,
437
- versionNumber: artifactInfo.versionNumber
464
+ let handoffPublishedFinal = false;
465
+ if (allowHandoff && result.handoff && conversationResult.conversationId && conversationResult.agentResponseDetailId) {
466
+ const outcomes = await agentRunner.FollowHandoffs({
467
+ params: { ...sourceParams, conversationMessages: parsedMessages },
468
+ sourceResult: result,
469
+ conversationId: conversationResult.conversationId,
470
+ sourceConversationDetailId: conversationResult.agentResponseDetailId,
471
+ createArtifacts: createArtifacts || false,
472
+ ConversationWriter: userPayload.ElevatedFrom,
473
+ sourceConversationDetail: replyDetail,
474
+ hooks: {
475
+ OnHopStart: (hop, previousRun) => {
476
+ const prevSanitized = JSON.stringify(this.sanitizeAgentResult(previousRun.agentResult, currentUser));
477
+ currentPublisher.PublishFinal(
478
+ previousRun.agentResult,
479
+ previousRun.agentResult.agentRun?.ConversationDetailID ?? previousRun.conversationDetailId,
480
+ prevSanitized
481
+ );
482
+ currentPublisher = new AgentRunStatusPublisher(
483
+ pubSub,
484
+ userPayload,
485
+ sessionId,
486
+ hop.targetConversationDetailId
487
+ );
488
+ return {
489
+ onProgress: (progress) => {
490
+ const fromEvent = progress.metadata?.agentRun;
491
+ if (isAgentRunEntity(fromEvent)) {
492
+ agentRunRef.current = fromEvent;
493
+ }
494
+ currentPublisher.OnProgress(progress);
495
+ },
496
+ onStreaming: currentPublisher.OnStreaming,
497
+ };
438
498
  },
439
- conversationResult.conversationId,
440
- finalConversationDetailId,
441
- currentUser,
442
- pubSub,
443
- userPayload
444
- )
445
- );
499
+ OnHopEnd: async (_hop, outcome) => {
500
+ if (!outcome.agentResult) {
501
+ return;
502
+ }
503
+ if (outcome.agentResult.agentRun) {
504
+ agentRunRef.current = outcome.agentResult.agentRun;
505
+ }
506
+ await this.runPostExecutionNotifications(
507
+ outcome.agentResult,
508
+ outcome.artifactInfo,
509
+ conversationResult.conversationId,
510
+ outcome.targetConversationDetailId,
511
+ undefined,
512
+ undefined,
513
+ createNotification,
514
+ currentUser,
515
+ pubSub,
516
+ userPayload,
517
+ p
518
+ );
519
+ },
520
+ },
521
+ });
522
+ const lastOutcome = outcomes[outcomes.length - 1];
523
+ if (lastOutcome?.error) {
524
+ const executionTime = Date.now() - startTime;
525
+ currentPublisher.PublishFailure(lastOutcome.targetConversationDetailId, lastOutcome.error);
526
+ LogError(`AI Agent handoff hop failed: ${lastOutcome.error}`);
527
+ return {
528
+ success: false,
529
+ errorMessage: lastOutcome.error,
530
+ executionTimeMs: executionTime,
531
+ result: JSON.stringify({ success: false, errorMessage: lastOutcome.error })
532
+ };
533
+ }
534
+ if (lastOutcome?.agentResult) {
535
+ const lastHopSanitized = JSON.stringify(this.sanitizeAgentResult(lastOutcome.agentResult, currentUser));
536
+ currentPublisher.PublishFinal(
537
+ lastOutcome.agentResult,
538
+ lastOutcome.agentResult.agentRun?.ConversationDetailID ?? lastOutcome.targetConversationDetailId,
539
+ lastHopSanitized
540
+ );
541
+ handoffPublishedFinal = true;
542
+ }
446
543
  }
447
544
 
448
- if (postExecutionOps.length > 0) {
449
- await Promise.all(postExecutionOps);
450
- }
545
+ const executionTime = Date.now() - startTime;
451
546
 
452
- // Create sanitized payload for JSON serialization
453
- const sanitizedResult = this.sanitizeAgentResult(result);
547
+ // Create sanitized payload for JSON serialization (always tied to the source detail)
548
+ const sanitizedResult = this.sanitizeAgentResult(result, currentUser);
454
549
  const returnResult = JSON.stringify(sanitizedResult);
455
550
 
456
- // Publish final events with enriched result data for fire-and-forget clients
457
- statusPublisher.PublishFinal(result, result.agentRun?.ConversationDetailID, returnResult);
551
+ // Publish final events with enriched result data when no handoff hop already finalized via OnHopStart
552
+ if (!handoffPublishedFinal) {
553
+ currentPublisher.PublishFinal(result, result.agentRun?.ConversationDetailID, returnResult);
554
+ }
458
555
 
459
556
  // Log completion
460
557
  if (result.success) {
@@ -473,11 +570,18 @@ export class RunAIAgentResolver extends ResolverBase {
473
570
  } catch (error) {
474
571
  const executionTime = Date.now() - startTime;
475
572
  LogError(`AI Agent run failed:`, undefined, error);
476
- const errorMessage = (error as Error).message || 'Unknown error occurred';
573
+ // Never dereference a non-Error throw: this catch must not itself throw, since executeAIAgent resolves on
574
+ // every failure (executeAgentInBackground relies on it).
575
+ const errorMessage = (error instanceof Error && error.message) || 'Unknown error occurred';
477
576
 
478
577
  // Fire-and-forget clients otherwise leave the run Running and the
479
578
  // conversation detail In-Progress (Explorer red-pill timer).
480
- await this.persistInFlightAgentFailure(p, userPayload, agentRunRef.current, conversationDetailId, errorMessage);
579
+ await this.persistInFlightAgentFailure(p, userPayload, agentRunRef.current, conversationDetailId, errorMessage, replyDetail);
580
+ // A fire-and-forget client has only this message to learn the run ended: without it, it waits out its
581
+ // idle timeout and then reconciles the run as a success. Published after the rows are closed, so a
582
+ // client that reloads the reply on this message reads the error. PublishFinal is the try's last step
583
+ // that can throw, so a run gets one completion or the other, never both.
584
+ statusPublisher.PublishFailure(conversationDetailId, errorMessage);
481
585
 
482
586
  const errorResult = {
483
587
  success: false,
@@ -494,6 +598,71 @@ export class RunAIAgentResolver extends ResolverBase {
494
598
  }
495
599
  }
496
600
 
601
+ /**
602
+ * LATENCY OPTIMIZATION (Opt #6): Runs the three independent post-execution notification/sync
603
+ * operations in parallel via `Promise.all` (shared by the initial run and each handoff hop):
604
+ * 1. `syncFeedbackRequestFromConversation` — links a prior Chat-step feedback request to the new run.
605
+ * 2. `sendFeedbackRequestNotification` — sends an in-app/email/SMS notification when paused for human input.
606
+ * 3. `createCompletionNotification` — sends an in-app/email/SMS notification when an artifact was created.
607
+ */
608
+ private async runPostExecutionNotifications(
609
+ result: ExecuteAgentResult,
610
+ artifactInfo: { artifactId: string; versionId: string; versionNumber: number } | undefined,
611
+ conversationId: string,
612
+ conversationDetailId: string | undefined,
613
+ lastRunId: string | undefined,
614
+ userMessage: string | undefined,
615
+ createNotification: boolean,
616
+ currentUser: UserInfo,
617
+ pubSub: PubSubEngine,
618
+ userPayload: UserPayload,
619
+ provider: IMetadataProvider
620
+ ): Promise<void> {
621
+ const postExecutionOps: Promise<void>[] = [];
622
+
623
+ if (lastRunId && result.agentRun?.ID) {
624
+ postExecutionOps.push(
625
+ this.syncFeedbackRequestFromConversation(lastRunId, result.agentRun.ID, userMessage, currentUser, provider)
626
+ );
627
+ }
628
+
629
+ if (result.feedbackRequestId) {
630
+ postExecutionOps.push(
631
+ this.sendFeedbackRequestNotification(result, currentUser, pubSub, userPayload)
632
+ );
633
+ }
634
+
635
+ if (
636
+ createNotification
637
+ && result.success
638
+ && artifactInfo
639
+ && artifactInfo.artifactId
640
+ && artifactInfo.versionId
641
+ && artifactInfo.versionNumber
642
+ && conversationDetailId
643
+ ) {
644
+ postExecutionOps.push(
645
+ this.createCompletionNotification(
646
+ result.agentRun,
647
+ {
648
+ artifactId: artifactInfo.artifactId,
649
+ versionId: artifactInfo.versionId,
650
+ versionNumber: artifactInfo.versionNumber
651
+ },
652
+ conversationId,
653
+ conversationDetailId,
654
+ currentUser,
655
+ pubSub,
656
+ userPayload
657
+ )
658
+ );
659
+ }
660
+
661
+ if (postExecutionOps.length > 0) {
662
+ await Promise.all(postExecutionOps);
663
+ }
664
+ }
665
+
497
666
  /**
498
667
  * When executeAIAgent throws after the run/detail exist, close them so Explorer
499
668
  * does not leave a red-pill timer on Status=Running / ConversationDetail In-Progress.
@@ -503,7 +672,9 @@ export class RunAIAgentResolver extends ResolverBase {
503
672
  userPayload: UserPayload,
504
673
  run: MJAIAgentRunEntityExtended | null | undefined,
505
674
  conversationDetailId: string | undefined,
506
- errorMessage: string
675
+ errorMessage: string,
676
+ /** The run's reply row, when this request created it: written in place, as the server. */
677
+ replyDetail?: MJConversationDetailEntityExtended
507
678
  ): Promise<void> {
508
679
  try {
509
680
  if (run && run.Status === 'Running') {
@@ -520,10 +691,11 @@ export class RunAIAgentResolver extends ResolverBase {
520
691
  return;
521
692
  }
522
693
  if (conversationDetailId) {
523
- const detail = await provider.GetEntityObject<MJConversationDetailEntity>(
694
+ const detail = replyDetail ?? await provider.GetEntityObject<MJConversationDetailEntity>(
524
695
  'MJ: Conversation Details',
525
696
  user
526
697
  );
698
+ await detail.EnsureSaveComplete(); // the run may still be saving into the same reply instance
527
699
  if (await detail.Load(conversationDetailId) && detail.Status === 'In-Progress') {
528
700
  await detail.EnsureSaveComplete();
529
701
  detail.Status = 'Error';
@@ -580,6 +752,9 @@ export class RunAIAgentResolver extends ResolverBase {
580
752
  // Only the system user or an API key holding the agent:trusted scope may set a run's scope and agent-type
581
753
  // params through data; for anyone else BaseAgent drops those keys (ExecuteAgentParams.TrustReservedRunData).
582
754
  const trustRunData = await IsTrustedAgentRunCaller({ UserPayload: userPayload, IsWidgetGuestRun: false, AgentID: agentId });
755
+ if (!userPayload.apiKeyHash) {
756
+ this.handoffEligiblePayloads.add(userPayload);
757
+ }
583
758
 
584
759
  if (fireAndForget) {
585
760
  // Fire-and-forget mode: start execution in background, return immediately.
@@ -781,40 +956,60 @@ export class RunAIAgentResolver extends ResolverBase {
781
956
  }
782
957
 
783
958
  /**
784
- * The ID of this agent's newest root run in the conversation IF the user stopped it, else
785
- * null. The query spans the runs that could be the predecessor (settled, or stopped by the
786
- * user) and the newest decides, so a stopped run that a later settled run already followed
787
- * is not chained to again. Uses the same filter and recognizer as `BaseAgent.findUserStoppedPredecessorRunId`
788
- * (`MJAIAgentRunEntityExtended.BuildStoppedPredecessorFilter` / `IsUserStopped`), which carries the stopped run's
789
- * completed results into the new run's context, so the two cannot pick different runs.
790
- * Fail-soft: a lookup failure means no chaining, never a failed turn.
959
+ * Discovers whether this agent's newest root run on this conversation/branch path should be
960
+ * chained by `LastRunID` on a chat-thread reply — either because the user stopped it
961
+ * (`reason: 'stopped'`) or because it paused on `FinalStep === 'Chat'` with one or more
962
+ * unanswered sub-agent consultations (`reason: 'open-consult'`).
963
+ *
964
+ * The query spans the runs that could be the predecessor (settled, or stopped by the user)
965
+ * and the newest decides, so a stopped or paused-consult run that a later settled run already
966
+ * followed is not chained to again. Fail-soft: a lookup failure means no chaining, never a
967
+ * failed turn.
791
968
  */
792
- private async findUserStoppedPredecessorRunId(
969
+ private async findChainablePredecessor(
793
970
  conversationId: string,
794
971
  agentId: string,
795
972
  branchId: string | null,
796
973
  contextUser: UserInfo,
797
974
  provider: IMetadataProvider
798
- ): Promise<string | null> {
975
+ ): Promise<{ runId: string; reason: 'stopped' | 'open-consult' } | null> {
799
976
  try {
800
977
  // The run must have replied on this turn's branch path: a run stopped on a sibling branch is not
801
978
  // this path's history. The trunk needs no branch rows; a branch needs them to walk its ancestry.
802
979
  const branches = branchId ? await ConversationEngine.LoadBranchesFresh(conversationId, contextUser, provider) : [];
803
980
  const rv = RunView.FromMetadataProvider(provider);
804
- const newest = await rv.RunView<{ ID: string; Status: string; CancellationReason: string | null }>({
981
+ const newest = await rv.RunView<{
982
+ ID: string;
983
+ Status: string;
984
+ CancellationReason: string | null;
985
+ FinalStep: string | null;
986
+ }>({
805
987
  EntityName: 'MJ: AI Agent Runs',
806
988
  ExtraFilter: MJAIAgentRunEntityExtended.BuildStoppedPredecessorFilter(conversationId, agentId, branchId, branches),
807
989
  OrderBy: '__mj_CreatedAt DESC',
808
990
  MaxRows: 1,
809
- Fields: ['ID', 'Status', 'CancellationReason'],
991
+ SkipTotalRowCount: true,
992
+ Fields: ['ID', 'Status', 'CancellationReason', 'FinalStep'],
810
993
  ResultType: 'simple',
811
- // The row this looks for was written by the Stop button moments ago; read the database, not a cache.
994
+ // The row this looks for was written by the Stop button or a Chat pause moments ago; read the database, not a cache.
812
995
  BypassCache: true,
813
996
  }, contextUser);
814
997
  const run = newest.Success ? newest.Results?.[0] : undefined;
815
- return run && MJAIAgentRunEntityExtended.IsUserStopped(run.Status, run.CancellationReason) ? run.ID : null;
998
+ if (!run) {
999
+ return null;
1000
+ }
1001
+ if (MJAIAgentRunEntityExtended.IsUserStopped(run.Status, run.CancellationReason)) {
1002
+ return { runId: run.ID, reason: 'stopped' };
1003
+ }
1004
+ if (run.FinalStep === 'Chat' && CanAgentOwnSubAgentConsults(agentId)) {
1005
+ const open = await LoadOpenSubAgentConsults(provider, run.ID, contextUser);
1006
+ if (open.length > 0) {
1007
+ return { runId: run.ID, reason: 'open-consult' };
1008
+ }
1009
+ }
1010
+ return null;
816
1011
  } catch (error) {
817
- LogError(`findUserStoppedPredecessorRunId failed; the turn runs unchained: ${error instanceof Error ? error.message : String(error)}`);
1012
+ LogError(`findChainablePredecessor failed; the turn runs unchained: ${error instanceof Error ? error.message : String(error)}`);
818
1013
  return null;
819
1014
  }
820
1015
  }
@@ -834,11 +1029,16 @@ export class RunAIAgentResolver extends ResolverBase {
834
1029
  provider: IMetadataProvider
835
1030
  ): Promise<void> {
836
1031
  try {
1032
+ const trimmedLastRunId = typeof lastRunId === 'string' ? lastRunId.trim() : '';
1033
+ if (!IsValidUUID(trimmedLastRunId)) {
1034
+ return;
1035
+ }
837
1036
  const rv = RunView.FromMetadataProvider(provider);
838
1037
  const result = await rv.RunView<MJAIAgentRequestEntity>({
839
1038
  EntityName: 'MJ: AI Agent Requests',
840
- ExtraFilter: `OriginatingAgentRunID='${lastRunId}' AND Status='Requested'`,
1039
+ ExtraFilter: `OriginatingAgentRunID='${EscapeSQLString(trimmedLastRunId)}' AND Status='Requested'`,
841
1040
  MaxRows: 1,
1041
+ SkipTotalRowCount: true,
842
1042
  ResultType: 'entity_object'
843
1043
  }, contextUser);
844
1044
 
@@ -925,16 +1125,27 @@ export class RunAIAgentResolver extends ResolverBase {
925
1125
  }
926
1126
 
927
1127
  /**
928
- * Optimized mutation that loads conversation history server-side.
929
- * This avoids sending large attachment data from client to server.
1128
+ * Runs an agent's turn in a conversation: the server loads the history (attachments included, so the client never
1129
+ * sends them), creates the agent's reply row, and runs the agent into it.
930
1130
  *
931
- * @param conversationDetailId - The conversation detail ID (user's message already saved)
1131
+ * Only the server writes agent replies (A19). The reply row (`Role` `AI`, In-Progress, its `ParentID` the user's
1132
+ * message) is created here, as the person asking, before the run starts. In fire-and-forget mode the
1133
+ * acknowledgement names it, `{ accepted, fireAndForget, conversationDetailId, createdAt }`, and is returned before
1134
+ * the run publishes anything; every progress, streaming and completion message carries the same
1135
+ * `conversationDetailId`. In synchronous mode the result's `agentRun.ConversationDetailID` names it.
1136
+ *
1137
+ * Threading, both optional: `replyParentId` puts the reply under another message of the same conversation the
1138
+ * caller can read (MJ's chat threads a delegate's reply under the manager's); `announcedByAgentId` has the server
1139
+ * first post that agent's fixed line "Continuing with **{agent}** for refinement..." and thread the reply under
1140
+ * it, and the acknowledgement then also carries `announcementDetailId`.
1141
+ *
1142
+ * @param userMessageId - The person's message this turn answers, already saved.
932
1143
  * @param agentId - The agent to execute
933
1144
  * @param maxHistoryMessages - Maximum number of history messages to include (default: 20)
934
1145
  */
935
1146
  @Mutation(() => AIAgentRunResult)
936
1147
  async RunAIAgentFromConversationDetail(
937
- @Arg('conversationDetailId') conversationDetailId: string,
1148
+ @Arg('userMessageId') userMessageId: string,
938
1149
  @Arg('agentId') agentId: string,
939
1150
  @Ctx() { userPayload, providers, dataSource }: AppContext,
940
1151
  @Arg('sessionId') sessionId: string,
@@ -957,11 +1168,17 @@ export class RunAIAgentResolver extends ResolverBase {
957
1168
  * run's own conversation reads honour it too. Omitted (clients send it only when set, so an
958
1169
  * older client never does), the run reads the whole conversation as before. An unreadable
959
1170
  * value fails the request rather than being dropped. */
960
- @Arg('agentHistoryFrom', { nullable: true }) agentHistoryFrom?: string
1171
+ @Arg('agentHistoryFrom', { nullable: true }) agentHistoryFrom?: string,
1172
+ /** The message the reply threads under, when not the user's message: one in the same conversation the
1173
+ * caller can read. */
1174
+ @Arg('replyParentId', { nullable: true }) replyParentId?: string,
1175
+ /** An active agent (MJ's chat passes its conversation manager) whose fixed line "Continuing with
1176
+ * **{agent}** for refinement..." the server posts before the reply, which then threads under it. */
1177
+ @Arg('announcedByAgentId', { nullable: true }) announcedByAgentId?: string,
1178
+ /** The fork the reply and history run on, when the turn forks from `userMessageId` (such as Regenerate).
1179
+ * Checked to be a branch of the conversation whose path holds `userMessageId`. */
1180
+ @Arg('replyBranchId', { nullable: true }) replyBranchId?: string
961
1181
  ): Promise<AIAgentRunResult> {
962
- // Check API key scope authorization for agent execution
963
- await this.CheckAPIKeyScopeAuthorization('agent:execute', agentId, userPayload);
964
-
965
1182
  const p = GetReadWriteProvider(providers);
966
1183
  const currentUser = this.GetUserFromPayload(userPayload);
967
1184
 
@@ -978,10 +1195,20 @@ export class RunAIAgentResolver extends ResolverBase {
978
1195
  // AUTHORITATIVELY from the widget instance (never the client-supplied arg) — so a guest needs
979
1196
  // no grants to WRITE the AI run entities and cannot run an arbitrary agent under elevation.
980
1197
  // Conversation OWNERSHIP is still enforced under the guest principal below: the guest loads its
981
- // own ConversationDetail through the Widget Guest RLS filters, so a detail id from another
982
- // session resolves to "not found" before any elevated work happens.
1198
+ // own ConversationDetail through its role's RLS filters, and startConversationTurn then checks
1199
+ // the conversation's scope stamp against the visitor (MJ#6032), so a detail id from another
1200
+ // visitor's session is refused before any elevated work happens. The reply row is written
1201
+ // as the guest too, never as the elevated principal, which owns no guest's conversation.
983
1202
  const widgetElevation = await ResolveWidgetGuestRunContext(userPayload, p);
984
1203
  const effectiveAgentId = widgetElevation ? widgetElevation.PinnedAgentId : agentId;
1204
+ // Check API key scope authorization for agent execution against the resolved agent id.
1205
+ await this.CheckAPIKeyScopeAuthorization('agent:execute', effectiveAgentId, userPayload);
1206
+ // A widget guest has one pinned agent and never posts a continuation announcement as another agent;
1207
+ // an API-key caller must hold agent:execute for the announcing agent as well as the running agent.
1208
+ const effectiveAnnouncedByAgentId = widgetElevation ? undefined : announcedByAgentId;
1209
+ if (effectiveAnnouncedByAgentId) {
1210
+ await this.CheckAPIKeyScopeAuthorization('agent:execute', effectiveAnnouncedByAgentId, userPayload);
1211
+ }
985
1212
  const effectiveUserPayload = widgetElevation ? ElevateUserPayload(userPayload, widgetElevation.ElevatedUser) : userPayload;
986
1213
  // Only the system user or an API key holding the agent:trusted scope may set a run's scope and agent-type
987
1214
  // params through data (ExecuteAgentParams.TrustReservedRunData). Judged on the caller's own payload: a widget
@@ -989,78 +1216,74 @@ export class RunAIAgentResolver extends ResolverBase {
989
1216
  const trustRunData = await IsTrustedAgentRunCaller({
990
1217
  UserPayload: userPayload, IsWidgetGuestRun: widgetElevation !== null, AgentID: agentId,
991
1218
  });
1219
+ if (!userPayload.apiKeyHash && !widgetElevation) {
1220
+ this.handoffEligiblePayloads.add(effectiveUserPayload);
1221
+ }
992
1222
 
993
1223
  try {
994
1224
  // Parsed before anything loads: a floor the caller asked for must never be dropped.
995
1225
  const historyFrom = parseAgentHistoryFrom(agentHistoryFrom);
996
-
997
- // LATENCY OPTIMIZATION (Opt #2 + #3): Load ConversationDetail once here to extract
998
- // conversationId, then pass it downstream. Previously this record was loaded multiple
999
- // times: once in loadConversationHistoryWithAttachments (just to get conversationId),
1000
- // once in AgentRunner (same reason), and once in createCompletionNotification. Now we
1001
- // load it a single time and thread conversationId through the call chain.
1002
- const currentDetail = await p.GetEntityObject<MJConversationDetailEntity>(
1003
- 'MJ: Conversation Details',
1004
- currentUser
1005
- );
1006
- if (!await currentDetail.Load(conversationDetailId)) {
1007
- throw new Error(`Conversation detail ${conversationDetailId} not found`);
1008
- }
1009
- const conversationId = currentDetail.ConversationID;
1010
- // The branch the placeholder row was created on decides which path the run reads.
1011
- const branchId: string | null = currentDetail.BranchID ?? null;
1012
-
1013
- // A turn that follows a run the user STOPPED continues that run rather than starting
1014
- // over: chain it by LastRunID (the chat client never sends one) and, when the caller
1015
- // supplied no payload, roll the stopped run's FinalPayload into this run's start.
1226
+ const turn = await this.startConversationTurn(p, currentUser, {
1227
+ UserMessageId: userMessageId,
1228
+ AgentId: effectiveAgentId,
1229
+ MaxHistoryMessages: maxHistoryMessages || 20,
1230
+ HistoryFrom: historyFrom,
1231
+ ReplyParentId: replyParentId,
1232
+ AnnouncedByAgentId: effectiveAnnouncedByAgentId,
1233
+ ReplyBranchId: replyBranchId
1234
+ });
1235
+ const reply = turn.Reply;
1236
+ // The turn's branch (the message's own branch, or a validated fork whose path holds the message)
1237
+ // decides which path the run reads.
1238
+ const branchId = turn.BranchId;
1239
+
1240
+ // A turn that follows a run the user STOPPED or a run paused on FinalStep === 'Chat'
1241
+ // with an open sub-agent consult continues that run rather than starting over: chain it
1242
+ // by LastRunID (the chat client never sends one) and, when the caller supplied no
1243
+ // payload, roll the prior run's FinalPayload into this run's start.
1016
1244
  let effectiveLastRunId = lastRunId;
1017
1245
  let effectiveAutoPopulate = autoPopulateLastRunPayload;
1018
1246
  if (!effectiveLastRunId) {
1019
- const stoppedRunId = await this.findUserStoppedPredecessorRunId(conversationId, effectiveAgentId, branchId, currentUser, p);
1020
- if (stoppedRunId) {
1021
- effectiveLastRunId = stoppedRunId;
1247
+ const chainable = await this.findChainablePredecessor(turn.ConversationId, effectiveAgentId, branchId, currentUser, p);
1248
+ if (chainable) {
1249
+ effectiveLastRunId = chainable.runId;
1022
1250
  if (effectiveAutoPopulate === undefined && payload == null) {
1023
1251
  effectiveAutoPopulate = true;
1024
1252
  }
1025
- LogStatus(`↩️ Continuing from user-stopped run ${stoppedRunId} (chained by LastRunID)`);
1253
+ if (chainable.reason === 'stopped') {
1254
+ LogStatus(`↩️ Continuing from user-stopped run ${chainable.runId} (chained by LastRunID)`);
1255
+ } else {
1256
+ LogStatus(`Resuming open sub-agent consult(s) from run ${chainable.runId} (chained by LastRunID)`);
1257
+ }
1026
1258
  }
1027
1259
  }
1028
1260
 
1029
- // Load conversation history with attachments from DB
1030
- const messages = await this.loadConversationHistoryWithAttachments(
1031
- conversationId,
1032
- currentUser,
1033
- maxHistoryMessages || 20,
1034
- p,
1035
- // The UI creates the agent-response placeholder row ('⏳ ...') before invoking
1036
- // this mutation — exclude it so the model never sees an empty assistant turn.
1037
- [conversationDetailId],
1038
- historyFrom,
1039
- branchId
1040
- );
1041
-
1042
- // Convert to JSON string for the existing executeAIAgent method
1043
- const messagesJson = JSON.stringify(messages);
1044
-
1045
1261
  if (fireAndForget) {
1046
1262
  // Fire-and-forget mode: start execution in background, return immediately.
1047
1263
  // The client will receive the result via WebSocket PubSub completion event.
1048
1264
  this.executeAgentInBackground(
1049
- p, dataSource, effectiveAgentId, effectiveUserPayload, messagesJson, sessionId, pubSub,
1265
+ p, dataSource, effectiveAgentId, effectiveUserPayload, turn.MessagesJson, sessionId, pubSub,
1050
1266
  data, payload, effectiveLastRunId, effectiveAutoPopulate, configurationId,
1051
- conversationDetailId, createArtifacts || false, createNotification || false,
1052
- sourceArtifactId, sourceArtifactVersionId, conversationId, planMode, requestedSkillIDs,
1267
+ reply.ID, createArtifacts || false, createNotification || false,
1268
+ sourceArtifactId, sourceArtifactVersionId, turn.ConversationId, planMode, requestedSkillIDs,
1053
1269
  undefined, // taskGraphDebug
1054
1270
  historyFrom,
1055
1271
  trustRunData,
1056
- branchId
1272
+ branchId,
1273
+ reply
1057
1274
  );
1058
1275
 
1059
1276
  LogStatus(`🔥 Fire-and-forget: Agent ${effectiveAgentId} execution started in background for session ${sessionId}`);
1060
1277
 
1061
1278
  return {
1062
1279
  success: true,
1063
- result: JSON.stringify({ accepted: true, fireAndForget: true })
1280
+ result: JSON.stringify({
1281
+ accepted: true,
1282
+ fireAndForget: true,
1283
+ conversationDetailId: reply.ID,
1284
+ createdAt: reply.__mj_CreatedAt,
1285
+ ...(turn.Announcement ? { announcementDetailId: turn.Announcement.ID } : {})
1286
+ })
1064
1287
  };
1065
1288
  }
1066
1289
 
@@ -1070,7 +1293,7 @@ export class RunAIAgentResolver extends ResolverBase {
1070
1293
  dataSource,
1071
1294
  effectiveAgentId,
1072
1295
  effectiveUserPayload,
1073
- messagesJson,
1296
+ turn.MessagesJson,
1074
1297
  sessionId,
1075
1298
  pubSub,
1076
1299
  data,
@@ -1079,19 +1302,20 @@ export class RunAIAgentResolver extends ResolverBase {
1079
1302
  effectiveLastRunId,
1080
1303
  effectiveAutoPopulate,
1081
1304
  configurationId,
1082
- conversationDetailId,
1305
+ reply.ID,
1083
1306
  createArtifacts || false,
1084
1307
  createNotification || false,
1085
1308
  sourceArtifactId,
1086
1309
  sourceArtifactVersionId,
1087
- conversationId, // LATENCY OPT #2: pass pre-resolved conversationId
1310
+ turn.ConversationId, // LATENCY OPT #2: pass pre-resolved conversationId
1088
1311
  undefined, // runRef
1089
1312
  planMode,
1090
1313
  requestedSkillIDs,
1091
1314
  undefined, // taskGraphDebug
1092
1315
  historyFrom,
1093
1316
  trustRunData,
1094
- branchId
1317
+ branchId,
1318
+ reply
1095
1319
  );
1096
1320
  } catch (error) {
1097
1321
  const errorMessage = (error as Error).message || 'Unknown error loading conversation history';
@@ -1104,6 +1328,137 @@ export class RunAIAgentResolver extends ResolverBase {
1104
1328
  }
1105
1329
  }
1106
1330
 
1331
+ /**
1332
+ * Everything a conversation turn needs before its agent runs: the user's message (read as the caller, so a
1333
+ * message they can't see is "not found", and for an anonymous visitor also checked against the conversation's
1334
+ * scope stamp), the conversation history, the optional announcement line, and the reply
1335
+ * row, created last so the history never holds the empty reply. Every row is written as `user`, the person asking,
1336
+ * and marked as server-authored by {@link AgentRunner.CreateAgentReplyDetail}.
1337
+ */
1338
+ private async startConversationTurn(
1339
+ p: DatabaseProviderBase,
1340
+ user: UserInfo,
1341
+ request: ConversationTurnRequest
1342
+ ): Promise<{ ConversationId: string; BranchId: string | null; MessagesJson: string; Reply: MJConversationDetailEntityExtended; Announcement?: MJConversationDetailEntityExtended }> {
1343
+ // LATENCY OPTIMIZATION (Opt #2 + #3): the user's message is loaded once here for its conversation and
1344
+ // branch, which are threaded through the call chain rather than reloaded downstream.
1345
+ const placement = await this.loadPlacementOf(p, user, request.UserMessageId);
1346
+ const conversationId = placement.ConversationId;
1347
+ // Loading the message as the caller is the whole ownership check for a named user. It is not for an
1348
+ // anonymous visitor: every visitor is the one shared Anonymous user, so under a guest role whose row
1349
+ // filters key on UserID another visitor's message loads fine (MJ#6032). The conversation's scope stamp
1350
+ // decides instead, exactly as it does for the realtime gates — and before any row is written.
1351
+ const access = await AuthorizeConversationTurn(conversationId, user, p);
1352
+ if (!access.Allowed) {
1353
+ LogError(`RunAIAgentFromConversationDetail: anonymous visitor ${user.AnonymousVisitorID ?? '(no visitor id)'} refused a turn in conversation ${conversationId} (${access.Reason})`);
1354
+ throw new Error('Not authorized: you do not own this conversation');
1355
+ }
1356
+ const branchId = request.ReplyBranchId
1357
+ ? await this.validateReplyBranch(p, user, conversationId, placement, request.ReplyBranchId)
1358
+ : placement.BranchId;
1359
+ const parentId = request.ReplyParentId
1360
+ ? await this.validateReplyParent(p, user, request.ReplyParentId, conversationId)
1361
+ : request.UserMessageId;
1362
+ const messages = await this.loadConversationHistoryWithAttachments(conversationId, user, request.MaxHistoryMessages, p, undefined, request.HistoryFrom, branchId);
1363
+ const runner = new AgentRunner(p);
1364
+ const announcement = request.AnnouncedByAgentId
1365
+ ? await this.postContinuationAnnouncement(runner, user, conversationId, branchId, parentId, request.AnnouncedByAgentId, request.AgentId)
1366
+ : undefined;
1367
+ const reply = await runner.CreateAgentReplyDetail({
1368
+ conversationId,
1369
+ branchId,
1370
+ agentId: request.AgentId,
1371
+ contextUser: user,
1372
+ parentId: announcement?.ID ?? parentId,
1373
+ provider: p
1374
+ });
1375
+ return { ConversationId: conversationId, BranchId: branchId, MessagesJson: JSON.stringify(messages), Reply: reply, Announcement: announcement };
1376
+ }
1377
+
1378
+ /** The conversation, branch (null is Main) and sequence of a message, read as `user`; throws when they can't read it. */
1379
+ private async loadPlacementOf(
1380
+ p: DatabaseProviderBase,
1381
+ user: UserInfo,
1382
+ messageId: string
1383
+ ): Promise<{ ConversationId: string; BranchId: string | null; Sequence: number }> {
1384
+ const message = await p.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details', user);
1385
+ if (!await message.Load(messageId)) {
1386
+ throw new Error(`Conversation detail ${messageId} not found`);
1387
+ }
1388
+ return { ConversationId: message.ConversationID, BranchId: message.BranchID ?? null, Sequence: message.Sequence ?? -1 };
1389
+ }
1390
+
1391
+ /**
1392
+ * When a turn forks from an earlier message (such as Regenerate), the reply and history run on the fork while the
1393
+ * user's message stays on the branch it was written on. Checks that `replyBranchId` is a branch of the turn's
1394
+ * conversation whose path holds the user's message (`IsRowOnBranchPath` rather than exact `ForkFromSequence`
1395
+ * equality is deliberate: `ConversationEngine.ForkPointFrom` sets `ForkFromSequence = message.Sequence` and the
1396
+ * fork belongs to this conversation); returns `replyBranchId`, or throws before any row is created.
1397
+ */
1398
+ private async validateReplyBranch(
1399
+ p: DatabaseProviderBase,
1400
+ user: UserInfo,
1401
+ conversationId: string,
1402
+ userMessage: { BranchId: string | null; Sequence: number },
1403
+ replyBranchId: string
1404
+ ): Promise<string> {
1405
+ if (UUIDsEqual(replyBranchId, userMessage.BranchId)) {
1406
+ return replyBranchId;
1407
+ }
1408
+ if (userMessage.Sequence < 0) {
1409
+ throw new Error(`Conversation detail is not on branch ${replyBranchId}'s path, so the reply cannot be written on that branch`);
1410
+ }
1411
+ const branches = await ConversationEngine.LoadBranchesFresh(conversationId, user, p);
1412
+ const chain = ConversationEngine.BuildBranchChain(branches, replyBranchId);
1413
+ if (!ConversationEngine.IsRowOnBranchPath({ BranchID: userMessage.BranchId, Sequence: userMessage.Sequence }, chain)) {
1414
+ throw new Error(`Conversation detail is not on branch ${replyBranchId}'s path, so the reply cannot be written on that branch`);
1415
+ }
1416
+ return replyBranchId;
1417
+ }
1418
+
1419
+ /**
1420
+ * A reply's parent is part of what the reply says, so the server sets it, from a checked input: a message the
1421
+ * caller can read, in the turn's own conversation. Returns its ID; throws otherwise.
1422
+ */
1423
+ private async validateReplyParent(p: DatabaseProviderBase, user: UserInfo, parentId: string, conversationId: string): Promise<string> {
1424
+ const { ConversationId: parentConversationId } = await this.loadPlacementOf(p, user, parentId);
1425
+ if (!UUIDsEqual(parentConversationId, conversationId)) {
1426
+ throw new Error(`Conversation detail ${parentId} is not in this turn's conversation, so the reply cannot thread under it`);
1427
+ }
1428
+ return parentId;
1429
+ }
1430
+
1431
+ /**
1432
+ * Posts the announcing agent's line, "Continuing with **{agent}** for refinement...", as a completed reply of the
1433
+ * announcing agent. Its text is fixed and names the agent from metadata, so a caller chooses only whether it is
1434
+ * posted and by which active agent, never what it says.
1435
+ */
1436
+ private async postContinuationAnnouncement(
1437
+ runner: AgentRunner,
1438
+ user: UserInfo,
1439
+ conversationId: string,
1440
+ branchId: string | null,
1441
+ parentId: string,
1442
+ announcerId: string,
1443
+ agentId: string
1444
+ ): Promise<MJConversationDetailEntityExtended> {
1445
+ await AIEngine.Instance.Config(false, user);
1446
+ const announcer = AIEngine.Instance.GetAgentByID(announcerId);
1447
+ if (!announcer || announcer.Status !== 'Active') {
1448
+ throw new Error(`Agent ${announcerId} cannot announce the turn: it is not an active agent`);
1449
+ }
1450
+ const agentName = AIEngine.Instance.GetAgentByID(agentId)?.Name ?? 'the agent';
1451
+ return runner.CreateAgentReplyDetail({
1452
+ conversationId,
1453
+ branchId,
1454
+ agentId: announcer.ID,
1455
+ contextUser: user,
1456
+ parentId,
1457
+ message: ContinuationAnnouncementText(agentName),
1458
+ status: 'Complete'
1459
+ });
1460
+ }
1461
+
1107
1462
  /**
1108
1463
  * Respond to a pending AIAgentRequest from the dashboard or API.
1109
1464
  * Updates the request record with the response and optionally spawns a
@@ -1283,9 +1638,8 @@ export class RunAIAgentResolver extends ResolverBase {
1283
1638
 
1284
1639
  /**
1285
1640
  * Execute agent in background (fire-and-forget).
1286
- * Handles errors by publishing error completion events via PubSub,
1287
- * so the client receives them via WebSocket even though the HTTP response
1288
- * has already been sent.
1641
+ * The client learns the outcome only from the run's completion status message (executeAIAgent publishes it,
1642
+ * failure included), since the HTTP response has already been sent. A liveness pulse runs until the run ends.
1289
1643
  */
1290
1644
  private executeAgentInBackground(
1291
1645
  p: DatabaseProviderBase,
@@ -1317,36 +1671,34 @@ export class RunAIAgentResolver extends ResolverBase {
1317
1671
  /** ExecuteAgentParams.TrustReservedRunData — see executeAIAgent. */
1318
1672
  trustReservedRunData: boolean = false,
1319
1673
  /** The conversation branch of the run — threaded through to ExecuteAgentParams.ConversationBranchID. */
1320
- conversationBranchId?: string | null
1674
+ conversationBranchId?: string | null,
1675
+ /** The reply row this request created for the run — see executeAIAgent. */
1676
+ replyDetail?: MJConversationDetailEntityExtended
1321
1677
  ): void {
1322
1678
  // Ref the liveness pulse reads to enrich heartbeats once the run is created.
1323
1679
  const runRef: { current: MJAIAgentRunEntityExtended | null } = { current: null };
1324
1680
  const pulse = StartLivenessPulse({
1325
1681
  pubSub,
1326
1682
  sessionId,
1327
- ownerUserId: userPayload.userRecord.ID,
1683
+ ownerUserId: StatusUpdateOwnerID(userPayload),
1328
1684
  resolver: 'RunAIAgentResolver',
1329
1685
  readStatus: () => runRef.current
1330
1686
  ? { runId: runRef.current.ID, status: runRef.current.Status }
1331
1687
  : undefined,
1332
1688
  });
1333
1689
 
1334
- // Execute in background - errors are handled within, not propagated
1690
+ // executeAIAgent resolves on every failure and publishes the run's completion itself (PublishFinal, or
1691
+ // PublishFailure from its catch), so nothing here publishes one.
1335
1692
  this.executeAIAgent(
1336
1693
  p, dataSource, agentId, userPayload, messagesJson, sessionId, pubSub,
1337
1694
  data, payload, undefined, lastRunId, autoPopulateLastRunPayload,
1338
1695
  configurationId, conversationDetailId, createArtifacts, createNotification,
1339
1696
  sourceArtifactId, sourceArtifactVersionId, conversationId, runRef, planMode, requestedSkillIDs, taskGraphDebug,
1340
- conversationHistoryFrom, trustReservedRunData, conversationBranchId
1697
+ conversationHistoryFrom, trustReservedRunData, conversationBranchId, replyDetail
1341
1698
  ).catch((error: unknown) => {
1342
- // Background execution failed unexpectedly (executeAIAgent has its own try-catch,
1343
- // so this would only fire for truly unexpected errors).
1344
- const errorMessage = (error instanceof Error) ? error.message : 'Unknown background execution error';
1345
- LogError(`🔥 Fire-and-forget background execution failed: ${errorMessage}`, undefined, error);
1346
-
1347
- // Publish error completion event so the client knows the agent failed.
1348
- // The publisher is created inside executeAIAgent, which rejected before returning one.
1349
- new AgentRunStatusPublisher(pubSub, userPayload, sessionId).PublishFailure(conversationDetailId, errorMessage);
1699
+ // Only a broken executeAIAgent contract lands here. Logged rather than left as an unhandled rejection;
1700
+ // no completion is published, because executeAIAgent may already have sent one.
1701
+ LogError(`🔥 Fire-and-forget run of agent ${agentId} for session ${sessionId} rejected, which executeAIAgent never should`, undefined, error);
1350
1702
  }).finally(() => pulse.Stop());
1351
1703
  }
1352
1704
 
@@ -1379,7 +1731,9 @@ export class RunAIAgentResolver extends ResolverBase {
1379
1731
  /** History floor: only rows written at or after it are loaded, and no summary is used. */
1380
1732
  historyFrom?: Date,
1381
1733
  /** The branch whose path to load; null reads the trunk. */
1382
- branchId?: string | null
1734
+ branchId?: string | null,
1735
+ /** Speaker attribution mode forwarded to `ConversationEngine.AssembleContextWindow`. */
1736
+ speakerAttribution?: 'off' | 'auto' | 'always'
1383
1737
  ): Promise<ChatMessage[]> {
1384
1738
  // Context windowing (summary boundary + raw tail, or legacy last-N when no
1385
1739
  // summary exists) shares ONE fold implementation with all other callers —
@@ -1400,7 +1754,8 @@ export class RunAIAgentResolver extends ResolverBase {
1400
1754
  const window = ConversationEngine.AssembleContextWindow(rows, {
1401
1755
  maxTailMessages: maxMessages,
1402
1756
  excludeDetailIds,
1403
- historyFrom
1757
+ historyFrom,
1758
+ speakerAttribution
1404
1759
  });
1405
1760
 
1406
1761
  // Batch load input artifacts for the windowed messages. Since the backfill migration