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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. package/README.md +69 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/BridgeHostInstance.d.ts +8 -3
  4. package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -1
  5. package/dist/agentSessions/BridgeHostInstance.js +14 -4
  6. package/dist/agentSessions/BridgeHostInstance.js.map +1 -1
  7. package/dist/agentSessions/HandoffOfferExpiry.d.ts +10 -0
  8. package/dist/agentSessions/HandoffOfferExpiry.d.ts.map +1 -0
  9. package/dist/agentSessions/HandoffOfferExpiry.js +18 -0
  10. package/dist/agentSessions/HandoffOfferExpiry.js.map +1 -0
  11. package/dist/agentSessions/ReturningVisitorRecap.js +1 -0
  12. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  13. package/dist/agentSessions/SessionJanitor.d.ts +31 -8
  14. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  15. package/dist/agentSessions/SessionJanitor.js +85 -41
  16. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  17. package/dist/agentSessions/SessionManager.d.ts +14 -4
  18. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  19. package/dist/agentSessions/SessionManager.js +28 -4
  20. package/dist/agentSessions/SessionManager.js.map +1 -1
  21. package/dist/agentSessions/conversationBrowsers.d.ts +61 -0
  22. package/dist/agentSessions/conversationBrowsers.d.ts.map +1 -0
  23. package/dist/agentSessions/conversationBrowsers.js +118 -0
  24. package/dist/agentSessions/conversationBrowsers.js.map +1 -0
  25. package/dist/agentSessions/sessionDeadline.d.ts +43 -0
  26. package/dist/agentSessions/sessionDeadline.d.ts.map +1 -0
  27. package/dist/agentSessions/sessionDeadline.js +68 -0
  28. package/dist/agentSessions/sessionDeadline.js.map +1 -0
  29. package/dist/auth/APIKeyScopeAuth.d.ts.map +1 -1
  30. package/dist/auth/APIKeyScopeAuth.js +5 -4
  31. package/dist/auth/APIKeyScopeAuth.js.map +1 -1
  32. package/dist/auth/loginEmailResolution.d.ts +41 -0
  33. package/dist/auth/loginEmailResolution.d.ts.map +1 -0
  34. package/dist/auth/loginEmailResolution.js +47 -0
  35. package/dist/auth/loginEmailResolution.js.map +1 -0
  36. package/dist/auth/magicLink/MagicLinkKeys.d.ts +50 -2
  37. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  38. package/dist/auth/magicLink/MagicLinkKeys.js +129 -12
  39. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  40. package/dist/auth/magicLink/MagicLinkRouter.d.ts +3 -0
  41. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  42. package/dist/auth/magicLink/MagicLinkRouter.js +23 -5
  43. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  44. package/dist/auth/magicLink/MagicLinkService.d.ts +39 -5
  45. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  46. package/dist/auth/magicLink/MagicLinkService.js +221 -9
  47. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  48. package/dist/auth/magicLink/index.d.ts +1 -1
  49. package/dist/auth/magicLink/index.d.ts.map +1 -1
  50. package/dist/auth/magicLink/index.js +1 -1
  51. package/dist/auth/magicLink/index.js.map +1 -1
  52. package/dist/auth/magicLink/redeemLanding.d.ts +2 -2
  53. package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
  54. package/dist/auth/magicLink/redeemLanding.js +7 -4
  55. package/dist/auth/magicLink/redeemLanding.js.map +1 -1
  56. package/dist/auth/magicLink/types.d.ts +27 -3
  57. package/dist/auth/magicLink/types.d.ts.map +1 -1
  58. package/dist/auth/magicLink/types.js +0 -4
  59. package/dist/auth/magicLink/types.js.map +1 -1
  60. package/dist/auth/newUsers.d.ts.map +1 -1
  61. package/dist/auth/newUsers.js +1 -0
  62. package/dist/auth/newUsers.js.map +1 -1
  63. package/dist/backgroundSweepConfigUnits.d.ts +231 -0
  64. package/dist/backgroundSweepConfigUnits.d.ts.map +1 -0
  65. package/dist/backgroundSweepConfigUnits.js +180 -0
  66. package/dist/backgroundSweepConfigUnits.js.map +1 -0
  67. package/dist/config.d.ts +906 -8
  68. package/dist/config.d.ts.map +1 -1
  69. package/dist/config.js +59 -0
  70. package/dist/config.js.map +1 -1
  71. package/dist/context.d.ts +2 -0
  72. package/dist/context.d.ts.map +1 -1
  73. package/dist/context.js +35 -4
  74. package/dist/context.js.map +1 -1
  75. package/dist/coreRoutes.d.ts +3 -1
  76. package/dist/coreRoutes.d.ts.map +1 -1
  77. package/dist/coreRoutes.js +6 -4
  78. package/dist/coreRoutes.js.map +1 -1
  79. package/dist/generated/graphql-schemas/__mj.d.ts +228 -0
  80. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  81. package/dist/generated/graphql-schemas/__mj.js +1163 -6
  82. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  83. package/dist/generic/OrphanedConversationDetailReconciler.d.ts +11 -0
  84. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  85. package/dist/generic/OrphanedConversationDetailReconciler.js +27 -1
  86. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  87. package/dist/generic/ResolverBase.d.ts +50 -5
  88. package/dist/generic/ResolverBase.d.ts.map +1 -1
  89. package/dist/generic/ResolverBase.js +321 -132
  90. package/dist/generic/ResolverBase.js.map +1 -1
  91. package/dist/generic/RunViewResolver.d.ts +4 -0
  92. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  93. package/dist/generic/RunViewResolver.js +47 -7
  94. package/dist/generic/RunViewResolver.js.map +1 -1
  95. package/dist/index.d.ts +1 -0
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +157 -35
  98. package/dist/index.js.map +1 -1
  99. package/dist/integration/ConnectionPause.d.ts +121 -0
  100. package/dist/integration/ConnectionPause.d.ts.map +1 -0
  101. package/dist/integration/ConnectionPause.js +183 -0
  102. package/dist/integration/ConnectionPause.js.map +1 -0
  103. package/dist/integration/CustomColumnPromoter.d.ts +72 -6
  104. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  105. package/dist/integration/CustomColumnPromoter.js +87 -46
  106. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  107. package/dist/integration/EntityMapLifecycle.d.ts +78 -4
  108. package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
  109. package/dist/integration/EntityMapLifecycle.js +199 -5
  110. package/dist/integration/EntityMapLifecycle.js.map +1 -1
  111. package/dist/integration/InactiveRowWarnings.d.ts +31 -0
  112. package/dist/integration/InactiveRowWarnings.d.ts.map +1 -1
  113. package/dist/integration/InactiveRowWarnings.js +29 -0
  114. package/dist/integration/InactiveRowWarnings.js.map +1 -1
  115. package/dist/integration/ObjectNamesByID.d.ts +28 -0
  116. package/dist/integration/ObjectNamesByID.d.ts.map +1 -0
  117. package/dist/integration/ObjectNamesByID.js +87 -0
  118. package/dist/integration/ObjectNamesByID.js.map +1 -0
  119. package/dist/integration/PostRestartIntrospect.d.ts +26 -0
  120. package/dist/integration/PostRestartIntrospect.d.ts.map +1 -0
  121. package/dist/integration/PostRestartIntrospect.js +27 -0
  122. package/dist/integration/PostRestartIntrospect.js.map +1 -0
  123. package/dist/integration/RSUPostRestartProgress.d.ts +176 -0
  124. package/dist/integration/RSUPostRestartProgress.d.ts.map +1 -0
  125. package/dist/integration/RSUPostRestartProgress.js +323 -0
  126. package/dist/integration/RSUPostRestartProgress.js.map +1 -0
  127. package/dist/integration/RSUProgressBridge.d.ts +41 -2
  128. package/dist/integration/RSUProgressBridge.d.ts.map +1 -1
  129. package/dist/integration/RSUProgressBridge.js +84 -1
  130. package/dist/integration/RSUProgressBridge.js.map +1 -1
  131. package/dist/integration/RunArtifactAuthorization.d.ts +39 -0
  132. package/dist/integration/RunArtifactAuthorization.d.ts.map +1 -0
  133. package/dist/integration/RunArtifactAuthorization.js +45 -0
  134. package/dist/integration/RunArtifactAuthorization.js.map +1 -0
  135. package/dist/integration/SchemaRefreshLaunch.d.ts +9 -0
  136. package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -1
  137. package/dist/integration/SchemaRefreshLaunch.js +3 -2
  138. package/dist/integration/SchemaRefreshLaunch.js.map +1 -1
  139. package/dist/integration/ScopedFieldsForBuild.d.ts +46 -0
  140. package/dist/integration/ScopedFieldsForBuild.d.ts.map +1 -0
  141. package/dist/integration/ScopedFieldsForBuild.js +91 -0
  142. package/dist/integration/ScopedFieldsForBuild.js.map +1 -0
  143. package/dist/integration/TargetConfigGuard.d.ts +40 -0
  144. package/dist/integration/TargetConfigGuard.d.ts.map +1 -0
  145. package/dist/integration/TargetConfigGuard.js +40 -0
  146. package/dist/integration/TargetConfigGuard.js.map +1 -0
  147. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
  148. package/dist/realtimeSessions/ownedRealtimeSession.js +2 -3
  149. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
  150. package/dist/realtimeSessions/sessionAccess.d.ts +15 -0
  151. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  152. package/dist/realtimeSessions/sessionAccess.js +22 -0
  153. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  154. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  155. package/dist/realtimeWidget/WidgetSessionService.js +2 -0
  156. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  157. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
  158. package/dist/realtimeWidget/visitorIdentity.js +1 -0
  159. package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
  160. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  161. package/dist/realtimeWidget/widgetCore.js +4 -7
  162. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  163. package/dist/realtimeWidget/widgetGuestElevation.js +1 -0
  164. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  165. package/dist/requestOrigin.d.ts +22 -0
  166. package/dist/requestOrigin.d.ts.map +1 -0
  167. package/dist/requestOrigin.js +60 -0
  168. package/dist/requestOrigin.js.map +1 -0
  169. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  170. package/dist/resolvers/AgentSessionResolver.js +6 -2
  171. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  172. package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
  173. package/dist/resolvers/AutotagPipelineResolver.js +2 -1
  174. package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
  175. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  176. package/dist/resolvers/ConversationRunEventsResolver.js +2 -0
  177. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  178. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +6 -0
  179. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -1
  180. package/dist/resolvers/ExecuteRemoteOperationResolver.js +30 -7
  181. package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -1
  182. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  183. package/dist/resolvers/GetDataResolver.js +34 -13
  184. package/dist/resolvers/GetDataResolver.js.map +1 -1
  185. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +401 -11
  186. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  187. package/dist/resolvers/IntegrationDiscoveryResolver.js +2690 -1305
  188. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  189. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  190. package/dist/resolvers/MCPResolver.js +13 -2
  191. package/dist/resolvers/MCPResolver.js.map +1 -1
  192. package/dist/resolvers/QuerySystemUserResolver.d.ts +36 -0
  193. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  194. package/dist/resolvers/QuerySystemUserResolver.js +174 -87
  195. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  196. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  197. package/dist/resolvers/RealtimeBridgeResolver.js +1 -0
  198. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  199. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +6 -0
  200. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  201. package/dist/resolvers/RealtimeClientSessionResolver.js +36 -5
  202. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  203. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +62 -10
  204. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  205. package/dist/resolvers/RemoteBrowserActionResolver.js +217 -34
  206. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  207. package/dist/resolvers/RunAIAgentResolver.d.ts +24 -8
  208. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  209. package/dist/resolvers/RunAIAgentResolver.js +185 -67
  210. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  211. package/dist/resolvers/RunAIPromptResolver.d.ts +5 -3
  212. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  213. package/dist/resolvers/RunAIPromptResolver.js +19 -18
  214. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  215. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  216. package/dist/resolvers/RunTemplateResolver.js +3 -1
  217. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  218. package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
  219. package/dist/resolvers/meetingRecordingRegistration.js +2 -0
  220. package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
  221. package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
  222. package/dist/rest/MediaAccessKeys.js +6 -2
  223. package/dist/rest/MediaAccessKeys.js.map +1 -1
  224. package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
  225. package/dist/rest/OAuthCallbackHandler.js +11 -5
  226. package/dist/rest/OAuthCallbackHandler.js.map +1 -1
  227. package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
  228. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
  229. package/dist/rest/RealtimeRecordingHandler.js +2 -4
  230. package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
  231. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  232. package/dist/rest/ViewOperationsHandler.js +2 -1
  233. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  234. package/dist/rest/setupRESTEndpoints.d.ts +3 -1
  235. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  236. package/dist/rest/setupRESTEndpoints.js +11 -2
  237. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  238. package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -1
  239. package/dist/services/DurableEntityActionTaskSubmitter.js +6 -1
  240. package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -1
  241. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  242. package/dist/services/TaskGraphActionRunner.js +2 -26
  243. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  244. package/dist/services/TaskGraphAgentRunner.d.ts +6 -0
  245. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  246. package/dist/services/TaskGraphAgentRunner.js +38 -0
  247. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  248. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  249. package/dist/services/TaskGraphContinuationDeliverer.js +1 -0
  250. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  251. package/dist/services/WorkQueueHostService.d.ts +2 -1
  252. package/dist/services/WorkQueueHostService.d.ts.map +1 -1
  253. package/dist/services/WorkQueueHostService.js +19 -4
  254. package/dist/services/WorkQueueHostService.js.map +1 -1
  255. package/dist/services/workQueueConfig.d.ts +2 -2
  256. package/dist/services/workQueueConfig.js +5 -5
  257. package/dist/services/workQueueConfig.js.map +1 -1
  258. package/dist/sharedCache.js +1 -1
  259. package/dist/sharedCache.js.map +1 -1
  260. package/dist/types.d.ts +17 -0
  261. package/dist/types.d.ts.map +1 -1
  262. package/dist/types.js.map +1 -1
  263. package/package.json +98 -98
  264. package/src/__tests__/APIKeyScopeAuth.test.ts +4 -8
  265. package/src/__tests__/ActiveOperations.test.ts +68 -0
  266. package/src/__tests__/AgentSessionResolver.ownership.test.ts +13 -0
  267. package/src/__tests__/AssertTargetConfigs.test.ts +75 -0
  268. package/src/__tests__/BatchApplyParity.test.ts +109 -0
  269. package/src/__tests__/BridgeHostInstance.test.ts +42 -0
  270. package/src/__tests__/CollectInactiveRowWarnings.test.ts +115 -0
  271. package/src/__tests__/ConnectionPause.test.ts +192 -0
  272. package/src/__tests__/ConnectionPauseResolver.test.ts +166 -0
  273. package/src/__tests__/ConnectionPauseSave.test.ts +143 -0
  274. package/src/__tests__/CustomColumnPromoterCompletion.test.ts +142 -0
  275. package/src/__tests__/DeleteConnectionRemovesPerConnectionCatalog.test.ts +51 -0
  276. package/src/__tests__/DetachedDiscoveryStart.test.ts +65 -0
  277. package/src/__tests__/DurableEntityActionTaskSubmitter.test.ts +73 -0
  278. package/src/__tests__/EntityMapLifecycle.rekey.test.ts +252 -0
  279. package/src/__tests__/ExecuteRemoteOperationResolver.test.ts +213 -0
  280. package/src/__tests__/HandoffOfferExpiry.test.ts +40 -0
  281. package/src/__tests__/IntegrationRunArtifactAuthorization.test.ts +126 -0
  282. package/src/__tests__/MagicLinkKeys.test.ts +102 -0
  283. package/src/__tests__/MediaAccessKeys.test.ts +57 -10
  284. package/src/__tests__/NewArrivalsDefaultDisabled.test.ts +50 -0
  285. package/src/__tests__/ObjectNamesByID.test.ts +176 -0
  286. package/src/__tests__/PostRestartMapsInCatalogScope.test.ts +146 -0
  287. package/src/__tests__/QuerySystemUserResolver.childRows.test.ts +80 -0
  288. package/src/__tests__/QuerySystemUserResolver.collision.test.ts +200 -0
  289. package/src/__tests__/QuerySystemUserResolver.permissions.test.ts +132 -0
  290. package/src/__tests__/RSUPostRestartProgress.test.ts +343 -0
  291. package/src/__tests__/RSUProgressBridge.test.ts +188 -5
  292. package/src/__tests__/RSUStartupSweepWiring.test.ts +30 -0
  293. package/src/__tests__/ReactivateRefreshMessage.test.ts +10 -3
  294. package/src/__tests__/RealtimeClientSessionResolver.test.ts +22 -0
  295. package/src/__tests__/{RefreshAdoptsNewColumns.test.ts → RefreshReportsNewColumns.test.ts} +25 -16
  296. package/src/__tests__/RemoteBrowserActionRecovery.test.ts +287 -0
  297. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +3 -0
  298. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +6 -4
  299. package/src/__tests__/RemoteBrowserAudioStream.test.ts +3 -0
  300. package/src/__tests__/RemoteBrowserSnapshot.test.ts +3 -0
  301. package/src/__tests__/ResolverBase.clientOldValues.test.ts +323 -26
  302. package/src/__tests__/ResolverBase.encryptedApiZeroReencrypt.test.ts +307 -0
  303. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +53 -2
  304. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +60 -0
  305. package/src/__tests__/RunAIAgentResolver.branch.test.ts +4 -0
  306. package/src/__tests__/RunAIAgentResolver.consultPredecessor.test.ts +289 -0
  307. package/src/__tests__/RunAIAgentResolver.handoff.test.ts +649 -0
  308. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +2 -0
  309. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +2 -0
  310. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +87 -12
  311. package/src/__tests__/SchemaEvolutionCustomColumns.test.ts +119 -0
  312. package/src/__tests__/SchemaRefreshLaunch.test.ts +24 -2
  313. package/src/__tests__/ScopedFieldsForBuild.test.ts +192 -0
  314. package/src/__tests__/SessionJanitor.test.ts +195 -4
  315. package/src/__tests__/SessionManager.test.ts +69 -0
  316. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +2 -2
  317. package/src/__tests__/TaskGraphAgentRunner.test.ts +272 -16
  318. package/src/__tests__/ViewOperationsHandler.test.ts +2 -5
  319. package/src/__tests__/WorkQueueHostService.test.ts +25 -7
  320. package/src/__tests__/backgroundSweepConfigUnits.test.ts +153 -0
  321. package/src/__tests__/contextRequestOrigin.test.ts +105 -0
  322. package/src/__tests__/conversationBrowsers.test.ts +62 -0
  323. package/src/__tests__/magicLinkExtensionCapabilities.test.ts +413 -0
  324. package/src/__tests__/orphanedConversationDetailReconciler.start.test.ts +82 -0
  325. package/src/__tests__/ownedRealtimeSession.test.ts +14 -0
  326. package/src/__tests__/realtimeRecordingHandler.test.ts +7 -2
  327. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +79 -1
  328. package/src/__tests__/requestOrigin.test.ts +104 -0
  329. package/src/__tests__/securityHardeningPr6034.test.ts +120 -0
  330. package/src/__tests__/sessionDeadline.test.ts +51 -0
  331. package/src/__tests__/sharedCache.test.ts +11 -0
  332. package/src/__tests__/unifiedAuth.test.ts +73 -1
  333. package/src/agentSessions/BridgeHostInstance.ts +20 -5
  334. package/src/agentSessions/HandoffOfferExpiry.ts +18 -0
  335. package/src/agentSessions/ReturningVisitorRecap.ts +1 -0
  336. package/src/agentSessions/SessionJanitor.ts +92 -42
  337. package/src/agentSessions/SessionManager.ts +38 -8
  338. package/src/agentSessions/conversationBrowsers.ts +143 -0
  339. package/src/agentSessions/sessionDeadline.ts +81 -0
  340. package/src/auth/APIKeyScopeAuth.ts +5 -4
  341. package/src/auth/__tests__/loginEmailResolution.test.ts +67 -0
  342. package/src/auth/loginEmailResolution.ts +67 -0
  343. package/src/auth/magicLink/MagicLinkKeys.ts +146 -14
  344. package/src/auth/magicLink/MagicLinkRouter.ts +33 -6
  345. package/src/auth/magicLink/MagicLinkService.ts +250 -10
  346. package/src/auth/magicLink/index.ts +1 -1
  347. package/src/auth/magicLink/redeemLanding.ts +7 -4
  348. package/src/auth/magicLink/types.ts +28 -3
  349. package/src/auth/newUsers.ts +1 -0
  350. package/src/backgroundSweepConfigUnits.ts +213 -0
  351. package/src/config.ts +69 -0
  352. package/src/context.ts +39 -6
  353. package/src/coreRoutes.ts +14 -7
  354. package/src/generated/graphql-schemas/__mj.ts +822 -7
  355. package/src/generic/OrphanedConversationDetailReconciler.ts +32 -1
  356. package/src/generic/ResolverBase.ts +414 -143
  357. package/src/generic/RunViewResolver.ts +96 -49
  358. package/src/index.ts +201 -35
  359. package/src/integration/ConnectionPause.ts +222 -0
  360. package/src/integration/CustomColumnPromoter.ts +123 -53
  361. package/src/integration/EntityMapLifecycle.ts +229 -6
  362. package/src/integration/InactiveRowWarnings.ts +50 -0
  363. package/src/integration/ObjectNamesByID.ts +97 -0
  364. package/src/integration/PostRestartIntrospect.ts +49 -0
  365. package/src/integration/RSUPostRestartProgress.ts +359 -0
  366. package/src/integration/RSUProgressBridge.ts +85 -2
  367. package/src/integration/RunArtifactAuthorization.ts +65 -0
  368. package/src/integration/SchemaRefreshLaunch.ts +12 -2
  369. package/src/integration/ScopedFieldsForBuild.ts +113 -0
  370. package/src/integration/TargetConfigGuard.ts +71 -0
  371. package/src/realtimeSessions/ownedRealtimeSession.ts +2 -6
  372. package/src/realtimeSessions/sessionAccess.ts +30 -0
  373. package/src/realtimeWidget/WidgetSessionService.ts +2 -0
  374. package/src/realtimeWidget/visitorIdentity.ts +1 -0
  375. package/src/realtimeWidget/widgetCore.ts +4 -8
  376. package/src/realtimeWidget/widgetGuestElevation.ts +1 -0
  377. package/src/requestOrigin.ts +64 -0
  378. package/src/resolvers/AgentSessionResolver.ts +6 -2
  379. package/src/resolvers/AutotagPipelineResolver.ts +2 -1
  380. package/src/resolvers/ConversationRunEventsResolver.ts +2 -0
  381. package/src/resolvers/ExecuteRemoteOperationResolver.ts +41 -9
  382. package/src/resolvers/GetDataResolver.ts +35 -13
  383. package/src/resolvers/IntegrationDiscoveryResolver.ts +1522 -203
  384. package/src/resolvers/MCPResolver.ts +15 -2
  385. package/src/resolvers/QuerySystemUserResolver.ts +197 -98
  386. package/src/resolvers/RealtimeBridgeResolver.ts +1 -0
  387. package/src/resolvers/RealtimeClientSessionResolver.ts +40 -7
  388. package/src/resolvers/RemoteBrowserActionResolver.ts +242 -32
  389. package/src/resolvers/RunAIAgentResolver.ts +265 -86
  390. package/src/resolvers/RunAIPromptResolver.ts +18 -18
  391. package/src/resolvers/RunTemplateResolver.ts +3 -1
  392. package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +52 -0
  393. package/src/resolvers/meetingRecordingRegistration.ts +2 -0
  394. package/src/rest/MediaAccessKeys.ts +6 -2
  395. package/src/rest/OAuthCallbackHandler.ts +13 -5
  396. package/src/rest/RealtimeRecordingHandler.ts +5 -9
  397. package/src/rest/ViewOperationsHandler.ts +2 -1
  398. package/src/rest/setupRESTEndpoints.ts +11 -2
  399. package/src/services/DurableEntityActionTaskSubmitter.ts +10 -2
  400. package/src/services/TaskGraphActionRunner.ts +2 -23
  401. package/src/services/TaskGraphAgentRunner.ts +43 -0
  402. package/src/services/TaskGraphContinuationDeliverer.ts +1 -0
  403. package/src/services/WorkQueueHostService.ts +26 -4
  404. package/src/services/workQueueConfig.ts +5 -5
  405. package/src/sharedCache.ts +1 -1
  406. package/src/types.ts +18 -0
  407. package/src/services/TaskOrchestration-Integration.md +0 -188
@@ -3,7 +3,7 @@ import { AppContext, UserPayload } from '../types.js';
3
3
  import { DatabaseProviderBase, LogError, LogStatus, Metadata, RunView, UserInfo, IMetadataProvider } from '@memberjunction/core';
4
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';
6
+ import { AgentRunner, ArtifactToolManager, CanAgentOwnSubAgentConsults, LoadOpenSubAgentConsults, HostAllowedAgentIDs } from '@memberjunction/ai-agents';
7
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';
@@ -17,7 +17,7 @@ import { GetReadWriteProvider } from '../util.js';
17
17
  import { ResolveWidgetGuestRunContext, ElevateUserPayload } from '../realtimeWidget/widgetGuestElevation.js';
18
18
  import { AuthorizeConversationTurn } from '../realtimeSessions/sessionAccess.js';
19
19
  import { IsTrustedAgentRunCaller } from './agent-run-data-guard.js';
20
- import { SafeJSONParse, UUIDsEqual } from '@memberjunction/global';
20
+ import { EscapeSQLString, IsValidUUID, SafeJSONParse, UUIDsEqual } from '@memberjunction/global';
21
21
  import { GetAttachmentService } from '@memberjunction/aiengine';
22
22
  import { NotificationEngine } from '@memberjunction/notifications';
23
23
 
@@ -183,6 +183,13 @@ export class AgentExecutionStreamMessage {
183
183
 
184
184
  @Resolver()
185
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
+
186
193
  /**
187
194
  * Sanitize ExecuteAgentResult for JSON serialization
188
195
  * Removes circular references and non-serializable objects
@@ -198,7 +205,8 @@ export class RunAIAgentResolver extends ResolverBase {
198
205
  finalStep: result.agentRun?.FinalStep,
199
206
  cancelled: result.agentRun?.Status === 'Cancelled',
200
207
  cancellationReason: result.agentRun?.CancellationReason,
201
- feedbackRequestId: result.feedbackRequestId
208
+ feedbackRequestId: result.feedbackRequestId,
209
+ handoff: result.handoff
202
210
  };
203
211
 
204
212
  // Safely extract agent run data using GetAll() for proper serialization
@@ -324,7 +332,9 @@ export class RunAIAgentResolver extends ResolverBase {
324
332
  conversationBranchId?: string | null,
325
333
  /** The reply row this request created on the server for the run (`conversationDetailId` is its ID). The run
326
334
  * writes into this instance, so its writes carry the server-authored marker and the user it was made as. */
327
- replyDetail?: MJConversationDetailEntityExtended
335
+ replyDetail?: MJConversationDetailEntityExtended,
336
+ /** Optional explicit handoff override; defaults to whether the caller registered `userPayload` as interactive. */
337
+ followHandoffs?: boolean
328
338
  ): Promise<AIAgentRunResult> {
329
339
  const startTime = Date.now();
330
340
  // Best-effort handle for persistInFlightAgentFailure. Populated from a
@@ -368,25 +378,36 @@ export class RunAIAgentResolver extends ResolverBase {
368
378
 
369
379
  console.log(`🚀 Starting agent execution with sessionId: ${sessionId}`);
370
380
 
371
- // The resolver still copies the run into agentRunRef, including noise steps, for the
372
- // fire-and-forget liveness pulse.
373
- const publishProgress = statusPublisher.OnProgress;
381
+ // The publisher owns every status message. The resolver still copies the run into
382
+ // agentRunRef, including noise steps, for the fire-and-forget liveness pulse.
383
+ const sourcePublisher = statusPublisher;
384
+ let currentPublisher = sourcePublisher;
374
385
 
375
- // Execute the agent in conversation context - handles conversation, artifacts, etc.
376
- const conversationResult = await agentRunner.RunAgentInConversation({
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
+ );
396
+
397
+ const sourceParams = {
377
398
  agent: agentEntity,
378
- conversationMessages: parsedMessages,
399
+ conversationMessages: [...parsedMessages],
379
400
  payload: payload ? SafeJSONParse(payload) : undefined,
380
401
  contextUser: currentUser,
381
402
  sessionID: sessionId,
382
- onProgress: (progress) => {
403
+ onProgress: (progress: Parameters<AgentRunStatusPublisher['OnProgress']>[0]) => {
383
404
  const fromEvent = progress.metadata?.agentRun;
384
405
  if (isAgentRunEntity(fromEvent)) {
385
406
  agentRunRef.current = fromEvent;
386
407
  }
387
- publishProgress(progress);
408
+ sourcePublisher.OnProgress(progress);
388
409
  },
389
- onStreaming: statusPublisher.OnStreaming,
410
+ onStreaming: sourcePublisher.OnStreaming,
390
411
  lastRunId: lastRunId,
391
412
  autoPopulateLastRunPayload: autoPopulateLastRunPayload,
392
413
  configurationId: configurationId,
@@ -395,13 +416,17 @@ export class RunAIAgentResolver extends ResolverBase {
395
416
  taskGraphDebug: parseTaskGraphDebug(taskGraphDebug),
396
417
  ConversationHistoryFrom: conversationHistoryFrom,
397
418
  ...(conversationBranchId ? { ConversationBranchID: conversationBranchId } : {}),
419
+ ...(allowHandoff ? { handoffChain: [] as string[] } : {}),
398
420
  data: parsedData,
399
421
  // Whether the run may read the reserved data keys (scope, agent-type params); BaseAgent drops them otherwise.
400
422
  TrustReservedRunData: trustReservedRunData,
401
423
  context: {
402
424
  dataSource: dataSource
403
425
  }
404
- }, {
426
+ };
427
+
428
+ // Execute the agent in conversation context - handles conversation, artifacts, etc.
429
+ const conversationResult = await agentRunner.RunAgentInConversation(sourceParams, {
405
430
  conversationDetailId: conversationDetailId, // Use existing if provided
406
431
  agentResponseDetail: replyDetail, // the reply row this request created, when it did
407
432
  conversationId: conversationId, // LATENCY OPT #2: pre-resolved to skip redundant load in AgentRunner
@@ -412,7 +437,7 @@ export class RunAIAgentResolver extends ResolverBase {
412
437
  ConversationWriter: userPayload.ElevatedFrom,
413
438
  });
414
439
 
415
- const result = conversationResult.agentResult;
440
+ let result = conversationResult.agentResult;
416
441
  // Use agent response detail ID if available, otherwise fall back to user message detail ID
417
442
  const finalConversationDetailId = conversationResult.agentResponseDetailId || conversationResult.userMessageDetailId;
418
443
  const artifactInfo = conversationResult.artifactInfo;
@@ -422,61 +447,111 @@ export class RunAIAgentResolver extends ResolverBase {
422
447
  agentRunRef.current = result.agentRun;
423
448
  }
424
449
 
425
- const executionTime = Date.now() - startTime;
426
-
427
- // LATENCY OPTIMIZATION (Opt #6): These three post-execution operations are independent
428
- // of each other — none reads the output of another. Previously they ran sequentially,
429
- // adding their latencies together (~50ms total). Now they run in parallel via Promise.all,
430
- // so we only pay the cost of the slowest one.
431
- //
432
- // 1. syncFeedbackRequestFromConversation — links a prior Chat-step feedback request to
433
- // the new agent run so the conversation thread stays coherent.
434
- // 2. sendFeedbackRequestNotification — sends an in-app/email/SMS notification when the
435
- // agent paused for human input (Chat step).
436
- // 3. createCompletionNotification — sends an in-app/email/SMS notification that the
437
- // agent finished and created an artifact.
438
- const postExecutionOps: Promise<void>[] = [];
439
-
440
- if (lastRunId && result.agentRun?.ID) {
441
- postExecutionOps.push(
442
- this.syncFeedbackRequestFromConversation(lastRunId, result.agentRun.ID, userMessage, currentUser, p)
443
- );
444
- }
445
-
446
- if (result.feedbackRequestId) {
447
- postExecutionOps.push(
448
- this.sendFeedbackRequestNotification(result, currentUser, pubSub, userPayload)
449
- );
450
- }
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
+ );
451
463
 
452
- if (createNotification && result.success && artifactInfo && artifactInfo.artifactId && artifactInfo.versionId && artifactInfo.versionNumber) {
453
- postExecutionOps.push(
454
- this.createCompletionNotification(
455
- result.agentRun,
456
- {
457
- artifactId: artifactInfo.artifactId,
458
- versionId: artifactInfo.versionId,
459
- 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
+ };
460
498
  },
461
- conversationResult.conversationId,
462
- finalConversationDetailId,
463
- currentUser,
464
- pubSub,
465
- userPayload
466
- )
467
- );
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
+ }
468
543
  }
469
544
 
470
- if (postExecutionOps.length > 0) {
471
- await Promise.all(postExecutionOps);
472
- }
545
+ const executionTime = Date.now() - startTime;
473
546
 
474
- // Create sanitized payload for JSON serialization
547
+ // Create sanitized payload for JSON serialization (always tied to the source detail)
475
548
  const sanitizedResult = this.sanitizeAgentResult(result, currentUser);
476
549
  const returnResult = JSON.stringify(sanitizedResult);
477
550
 
478
- // Publish final events with enriched result data for fire-and-forget clients
479
- 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
+ }
480
555
 
481
556
  // Log completion
482
557
  if (result.success) {
@@ -523,6 +598,71 @@ export class RunAIAgentResolver extends ResolverBase {
523
598
  }
524
599
  }
525
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
+
526
666
  /**
527
667
  * When executeAIAgent throws after the run/detail exist, close them so Explorer
528
668
  * does not leave a red-pill timer on Status=Running / ConversationDetail In-Progress.
@@ -612,6 +752,9 @@ export class RunAIAgentResolver extends ResolverBase {
612
752
  // Only the system user or an API key holding the agent:trusted scope may set a run's scope and agent-type
613
753
  // params through data; for anyone else BaseAgent drops those keys (ExecuteAgentParams.TrustReservedRunData).
614
754
  const trustRunData = await IsTrustedAgentRunCaller({ UserPayload: userPayload, IsWidgetGuestRun: false, AgentID: agentId });
755
+ if (!userPayload.apiKeyHash) {
756
+ this.handoffEligiblePayloads.add(userPayload);
757
+ }
615
758
 
616
759
  if (fireAndForget) {
617
760
  // Fire-and-forget mode: start execution in background, return immediately.
@@ -813,40 +956,60 @@ export class RunAIAgentResolver extends ResolverBase {
813
956
  }
814
957
 
815
958
  /**
816
- * The ID of this agent's newest root run in the conversation IF the user stopped it, else
817
- * null. The query spans the runs that could be the predecessor (settled, or stopped by the
818
- * user) and the newest decides, so a stopped run that a later settled run already followed
819
- * is not chained to again. Uses the same filter and recognizer as `BaseAgent.findUserStoppedPredecessorRunId`
820
- * (`MJAIAgentRunEntityExtended.BuildStoppedPredecessorFilter` / `IsUserStopped`), which carries the stopped run's
821
- * completed results into the new run's context, so the two cannot pick different runs.
822
- * 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.
823
968
  */
824
- private async findUserStoppedPredecessorRunId(
969
+ private async findChainablePredecessor(
825
970
  conversationId: string,
826
971
  agentId: string,
827
972
  branchId: string | null,
828
973
  contextUser: UserInfo,
829
974
  provider: IMetadataProvider
830
- ): Promise<string | null> {
975
+ ): Promise<{ runId: string; reason: 'stopped' | 'open-consult' } | null> {
831
976
  try {
832
977
  // The run must have replied on this turn's branch path: a run stopped on a sibling branch is not
833
978
  // this path's history. The trunk needs no branch rows; a branch needs them to walk its ancestry.
834
979
  const branches = branchId ? await ConversationEngine.LoadBranchesFresh(conversationId, contextUser, provider) : [];
835
980
  const rv = RunView.FromMetadataProvider(provider);
836
- 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
+ }>({
837
987
  EntityName: 'MJ: AI Agent Runs',
838
988
  ExtraFilter: MJAIAgentRunEntityExtended.BuildStoppedPredecessorFilter(conversationId, agentId, branchId, branches),
839
989
  OrderBy: '__mj_CreatedAt DESC',
840
990
  MaxRows: 1,
841
- Fields: ['ID', 'Status', 'CancellationReason'],
991
+ SkipTotalRowCount: true,
992
+ Fields: ['ID', 'Status', 'CancellationReason', 'FinalStep'],
842
993
  ResultType: 'simple',
843
- // 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.
844
995
  BypassCache: true,
845
996
  }, contextUser);
846
997
  const run = newest.Success ? newest.Results?.[0] : undefined;
847
- 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;
848
1011
  } catch (error) {
849
- 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)}`);
850
1013
  return null;
851
1014
  }
852
1015
  }
@@ -866,11 +1029,16 @@ export class RunAIAgentResolver extends ResolverBase {
866
1029
  provider: IMetadataProvider
867
1030
  ): Promise<void> {
868
1031
  try {
1032
+ const trimmedLastRunId = typeof lastRunId === 'string' ? lastRunId.trim() : '';
1033
+ if (!IsValidUUID(trimmedLastRunId)) {
1034
+ return;
1035
+ }
869
1036
  const rv = RunView.FromMetadataProvider(provider);
870
1037
  const result = await rv.RunView<MJAIAgentRequestEntity>({
871
1038
  EntityName: 'MJ: AI Agent Requests',
872
- ExtraFilter: `OriginatingAgentRunID='${lastRunId}' AND Status='Requested'`,
1039
+ ExtraFilter: `OriginatingAgentRunID='${EscapeSQLString(trimmedLastRunId)}' AND Status='Requested'`,
873
1040
  MaxRows: 1,
1041
+ SkipTotalRowCount: true,
874
1042
  ResultType: 'entity_object'
875
1043
  }, contextUser);
876
1044
 
@@ -1048,6 +1216,9 @@ export class RunAIAgentResolver extends ResolverBase {
1048
1216
  const trustRunData = await IsTrustedAgentRunCaller({
1049
1217
  UserPayload: userPayload, IsWidgetGuestRun: widgetElevation !== null, AgentID: agentId,
1050
1218
  });
1219
+ if (!userPayload.apiKeyHash && !widgetElevation) {
1220
+ this.handoffEligiblePayloads.add(effectiveUserPayload);
1221
+ }
1051
1222
 
1052
1223
  try {
1053
1224
  // Parsed before anything loads: a floor the caller asked for must never be dropped.
@@ -1066,19 +1237,24 @@ export class RunAIAgentResolver extends ResolverBase {
1066
1237
  // decides which path the run reads.
1067
1238
  const branchId = turn.BranchId;
1068
1239
 
1069
- // A turn that follows a run the user STOPPED continues that run rather than starting
1070
- // over: chain it by LastRunID (the chat client never sends one) and, when the caller
1071
- // supplied no payload, roll the stopped run's FinalPayload into this run's start.
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.
1072
1244
  let effectiveLastRunId = lastRunId;
1073
1245
  let effectiveAutoPopulate = autoPopulateLastRunPayload;
1074
1246
  if (!effectiveLastRunId) {
1075
- const stoppedRunId = await this.findUserStoppedPredecessorRunId(turn.ConversationId, effectiveAgentId, branchId, currentUser, p);
1076
- if (stoppedRunId) {
1077
- effectiveLastRunId = stoppedRunId;
1247
+ const chainable = await this.findChainablePredecessor(turn.ConversationId, effectiveAgentId, branchId, currentUser, p);
1248
+ if (chainable) {
1249
+ effectiveLastRunId = chainable.runId;
1078
1250
  if (effectiveAutoPopulate === undefined && payload == null) {
1079
1251
  effectiveAutoPopulate = true;
1080
1252
  }
1081
- 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
+ }
1082
1258
  }
1083
1259
  }
1084
1260
 
@@ -1555,7 +1731,9 @@ export class RunAIAgentResolver extends ResolverBase {
1555
1731
  /** History floor: only rows written at or after it are loaded, and no summary is used. */
1556
1732
  historyFrom?: Date,
1557
1733
  /** The branch whose path to load; null reads the trunk. */
1558
- branchId?: string | null
1734
+ branchId?: string | null,
1735
+ /** Speaker attribution mode forwarded to `ConversationEngine.AssembleContextWindow`. */
1736
+ speakerAttribution?: 'off' | 'auto' | 'always'
1559
1737
  ): Promise<ChatMessage[]> {
1560
1738
  // Context windowing (summary boundary + raw tail, or legacy last-N when no
1561
1739
  // summary exists) shares ONE fold implementation with all other callers —
@@ -1576,7 +1754,8 @@ export class RunAIAgentResolver extends ResolverBase {
1576
1754
  const window = ConversationEngine.AssembleContextWindow(rows, {
1577
1755
  maxTailMessages: maxMessages,
1578
1756
  excludeDetailIds,
1579
- historyFrom
1757
+ historyFrom,
1758
+ speakerAttribution
1580
1759
  });
1581
1760
 
1582
1761
  // Batch load input artifacts for the windowed messages. Since the backfill migration
@@ -571,25 +571,18 @@ export class RunAIPromptResolver extends ResolverBase {
571
571
  }
572
572
 
573
573
  /**
574
- * Helper method to select an embedding model by size
574
+ * Selects a local embedding model by size: `'small'` is the lowest-PowerRank active local model;
575
+ * `'medium'` and anything else is the platform default (`AIEngine.DefaultLocalEmbeddingModel`).
576
+ * Picking by position in the list would shift whenever a model is added.
575
577
  * @private
576
578
  */
577
579
  private selectEmbeddingModelBySize(modelSize: string): MJAIModelEntityExtended {
578
- const localModels = AIEngine.Instance.LocalEmbeddingModels;
579
-
580
- if (!localModels || localModels.length === 0) {
580
+ const engine = AIEngine.Instance;
581
+ const model = modelSize === 'small' ? engine.LowestPowerLocalEmbeddingModel : engine.DefaultLocalEmbeddingModel;
582
+ if (!model) {
581
583
  throw new Error('No local embedding models available');
582
584
  }
583
-
584
- // Models are already sorted by PowerRank (highest first)
585
- switch (modelSize) {
586
- case 'small':
587
- return localModels[localModels.length - 1]; // Lowest power
588
- case 'medium':
589
- default:
590
- const midIndex = Math.floor(localModels.length / 2);
591
- return localModels[midIndex] || localModels[0];
592
- }
585
+ return model;
593
586
  }
594
587
 
595
588
  /**
@@ -825,7 +818,8 @@ export class RunAIPromptResolver extends ResolverBase {
825
818
  async EmbedText(
826
819
  @Arg('textToEmbed', () => [String]) textToEmbed: string[],
827
820
  @Arg('modelSize') modelSize: string,
828
- @Ctx() { userPayload }: { userPayload: UserPayload }
821
+ @Ctx() { userPayload }: { userPayload: UserPayload },
822
+ @Arg('inputType', { nullable: true, description: "'query' for search text, 'document' (default) for text that is stored and compared" }) inputType?: string
829
823
  ): Promise<EmbedTextResult> {
830
824
  // Check API key scope authorization for embedding generation
831
825
  await this.CheckAPIKeyScopeAuthorization('embedding:generate', '*', userPayload);
@@ -847,6 +841,11 @@ export class RunAIPromptResolver extends ResolverBase {
847
841
  // Ensure AI Engine is configured
848
842
  await AIEngine.Instance.Config(false, currentUser);
849
843
 
844
+ const role = inputType === 'query' || inputType === 'document' ? inputType : undefined;
845
+ if (inputType != null && !role) {
846
+ return { embeddings: [], modelName: 'Unknown', vectorDimensions: 0, error: `inputType must be 'query' or 'document'` };
847
+ }
848
+
850
849
  // Select embedding model by size
851
850
  const model = this.selectEmbeddingModelBySize(modelSize);
852
851
 
@@ -863,7 +862,7 @@ export class RunAIPromptResolver extends ResolverBase {
863
862
  }
864
863
 
865
864
  // Use AIEngine's EmbedText method
866
- const result = await AIEngine.Instance.EmbedText(model, text);
865
+ const result = await AIEngine.Instance.EmbedText(model, text, undefined, { inputType: role });
867
866
 
868
867
  if (result && result.vector && result.vector.length > 0) {
869
868
  embeddings.push(result.vector);
@@ -904,9 +903,10 @@ export class RunAIPromptResolver extends ResolverBase {
904
903
  async EmbedTextSystemUser(
905
904
  @Arg('textToEmbed', () => [String]) textToEmbed: string[],
906
905
  @Arg('modelSize') modelSize: string,
907
- @Ctx() { userPayload }: { userPayload: UserPayload }
906
+ @Ctx() { userPayload }: { userPayload: UserPayload },
907
+ @Arg('inputType', { nullable: true, description: "'query' for search text, 'document' (default) for text that is stored and compared" }) inputType?: string
908
908
  ): Promise<EmbedTextResult> {
909
909
  // Reuse the same logic as the regular mutation
910
- return this.EmbedText(textToEmbed, modelSize, { userPayload });
910
+ return this.EmbedText(textToEmbed, modelSize, { userPayload }, inputType);
911
911
  }
912
912
  }
@@ -1,6 +1,7 @@
1
1
  import { Resolver, Mutation, Arg, Ctx, ObjectType, Field } from 'type-graphql';
2
2
  import { AppContext, UserPayload } from '../types.js';
3
3
  import { LogError, LogStatus, Metadata, RunView } from '@memberjunction/core';
4
+ import { EscapeSQLString } from '@memberjunction/global';
4
5
  import { MJTemplateContentEntity, MJTemplateEntityExtended } from '@memberjunction/core-entities';
5
6
  import { GetTemplateRunRefusal, TemplateEngineServer } from '@memberjunction/templates';
6
7
  import { ResolverBase } from '../generic/ResolverBase.js';
@@ -87,9 +88,10 @@ export class RunTemplateResolver extends ResolverBase {
87
88
  const rv = RunView.FromMetadataProvider(p);
88
89
  const templateContentResult = await rv.RunView<MJTemplateContentEntity>({
89
90
  EntityName: 'MJ: Template Contents',
90
- ExtraFilter: `TemplateID = '${templateId}'`,
91
+ ExtraFilter: `TemplateID = '${EscapeSQLString(templateEntity.ID)}'`,
91
92
  OrderBy: 'Priority ASC',
92
93
  MaxRows: 1,
94
+ SkipTotalRowCount: true,
93
95
  ResultType: 'entity_object'
94
96
  }, currentUser);
95
97