@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
@@ -19,15 +19,18 @@
19
19
  import { Resolver, Mutation, Query, Arg, Ctx, Float, Int, ObjectType, Field, PubSub, PubSubEngine } from 'type-graphql';
20
20
  import { AppContext, UserPayload } from '../types.js';
21
21
  import { UserInfo, IMetadataProvider, LogError } from '@memberjunction/core';
22
- import { MJAIAgentSessionEntity, MJAIAgentEntity } from '@memberjunction/core-entities';
22
+ import { NormalizeUUID } from '@memberjunction/global';
23
+ import { MJAIAgentEntity, MJConversationEntity } from '@memberjunction/core-entities';
23
24
  import { NormalizeInstanceKey, RemoteBrowserEngine } from '@memberjunction/remote-browser-server';
24
25
  import { BeginBrowserGoalStep, FinalizeBrowserGoalStep, ExtractCoAgentRunID } from '../agentSessions/remoteBrowserGoalEngine.js';
25
26
  import { RemoteBrowserGoalRegistry } from '../agentSessions/remoteBrowserGoalRegistry.js';
26
27
  import { SessionRunIDVerifier } from '../agentSessions/SessionRunIDVerifier.js';
27
28
  import { randomUUID } from 'node:crypto';
28
29
  import {
30
+ IRemoteBrowserSession,
29
31
  RemoteBrowserAction,
30
32
  RemoteBrowserAudioChunk,
33
+ RemoteBrowserFillFieldInput,
31
34
  RemoteBrowserHumanInput,
32
35
  RemoteBrowserModifierKey,
33
36
  RemoteBrowserCapabilityNotSupportedError,
@@ -39,11 +42,27 @@ import {
39
42
  RealtimeSessionNotFoundError,
40
43
  RealtimeSessionOwnershipError,
41
44
  } from '../realtimeSessions/ownedRealtimeSession.js';
45
+ import { AuthorizeSessionAccess, ResolveSessionScopeIdentity } from '../realtimeSessions/sessionAccess.js';
46
+ import { ConversationBrowserLedger, RemoteBrowserRefusedError } from '../agentSessions/conversationBrowsers.js';
47
+ import { configInfo, type ConversationBrowserConfig } from '../config.js';
42
48
  import { PUSH_STATUS_UPDATES_TOPIC } from '../generic/PushStatusResolver.js';
43
49
  import { AIEngine } from '@memberjunction/aiengine';
44
50
  import { AIPromptRunner } from '@memberjunction/ai-prompts';
45
51
  import { AIPromptParams } from '@memberjunction/ai-core-plus';
46
52
 
53
+ /** Entity name for turn-based text conversations that scope a live Remote Browser session. */
54
+ const CONVERSATION_ENTITY = 'MJ: Conversations';
55
+ /**
56
+ * What an owned browser scope supplies: an `MJ: AI Agent Sessions` row (a realtime call), or an owned
57
+ * `MJ: Conversations` row (text chat), which has no agent and no config.
58
+ */
59
+ export interface OwnedBrowserScope {
60
+ ID: string;
61
+ AgentID: string | null;
62
+ Config_: string | null;
63
+ /** `true` for a text conversation's browser (the opt-in, capped, private-network-restricted kind). */
64
+ IsConversation?: boolean;
65
+ }
47
66
  /** Entity name for the agent whose `TypeConfiguration` carries the remote-browser provider preference. */
48
67
  const AGENT_ENTITY = 'MJ: AI Agents';
49
68
  /**
@@ -67,7 +86,7 @@ interface RemoteBrowserTypeConfiguration {
67
86
 
68
87
  /**
69
88
  * Result of {@link RemoteBrowserActionResolver.ExecuteRemoteBrowserAction} — the outcome of one browser
70
- * action plus the resulting URL the client narrates / displays.
89
+ * action plus the resulting URL and optional structured DOM observation / filled-field list.
71
90
  */
72
91
  @ObjectType()
73
92
  export class RemoteBrowserActionResult {
@@ -82,6 +101,14 @@ export class RemoteBrowserActionResult {
82
101
  /** Human-readable detail — an error message on failure, a note on success. Null when none. */
83
102
  @Field(() => String, { nullable: true })
84
103
  Detail?: string;
104
+
105
+ /** Optional JSON-serialized {@link import('@memberjunction/remote-browser-base').RemoteBrowserPageObservation} returned by `inspect`, `fill`, or `click`. */
106
+ @Field(() => String, { nullable: true })
107
+ ObservationJson?: string;
108
+
109
+ /** Optional JSON-serialized `RemoteBrowserFilledFieldResult[]` returned by `fill`. */
110
+ @Field(() => String, { nullable: true })
111
+ FilledJson?: string;
85
112
  }
86
113
 
87
114
  /**
@@ -249,6 +276,12 @@ const MAX_STREAM_ENTRY_AGE_MS = 4 * 60 * 60 * 1000; // 4 hours — generous vs.
249
276
 
250
277
  @Resolver()
251
278
  export class RemoteBrowserActionResolver extends ResolverBase {
279
+ constructor() {
280
+ super();
281
+ // The idle reaper ends a conversation browser the way EndRemoteBrowserSession does.
282
+ ConversationBrowserLedger.Instance.EndBrowser = (conversationID) => this.endBrowser(conversationID);
283
+ }
284
+
252
285
  /**
253
286
  * Surfaces whose live CDP screencast this resolver has already started, keyed by
254
287
  * {@link RemoteBrowserActionResolver.streamKey} (agent session **plus** instance) so a re-issued
@@ -275,6 +308,14 @@ export class RemoteBrowserActionResolver extends ResolverBase {
275
308
  */
276
309
  private startedAudioStreams = new Map<string, number>();
277
310
 
311
+ /**
312
+ * The push session each surface's frames and audio go to, by stream key. A reloaded tab starts again
313
+ * from a new push session while the browser keeps streaming; it takes the frames over here instead of
314
+ * being told "already streaming" while they still go to the old session. An entry whose streams have
315
+ * stopped is dropped by {@link sweepStreamEntries}.
316
+ */
317
+ private streamTargets = new Map<string, UserPayload>();
318
+
278
319
  /**
279
320
  * Drops any {@link startedScreencasts} / {@link startedAudioStreams} entry older than
280
321
  * {@link MAX_STREAM_ENTRY_AGE_MS} — the reclaim path for sessions that never called
@@ -293,6 +334,12 @@ export class RemoteBrowserActionResolver extends ResolverBase {
293
334
  this.startedAudioStreams.delete(key);
294
335
  }
295
336
  }
337
+ // A target outlives its streams only until here (a stop, a failed start, a voice session's end).
338
+ for (const key of this.streamTargets.keys()) {
339
+ if (!this.startedScreencasts.has(key) && !this.startedAudioStreams.has(key)) {
340
+ this.streamTargets.delete(key);
341
+ }
342
+ }
296
343
  }
297
344
 
298
345
  /**
@@ -326,20 +373,28 @@ export class RemoteBrowserActionResolver extends ResolverBase {
326
373
  @Arg('deltaY', () => Float, { nullable: true }) deltaY?: number,
327
374
  @Arg('ms', () => Float, { nullable: true }) ms?: number,
328
375
  @Arg('instanceKey', () => String, { nullable: true }) instanceKey?: string,
376
+ @Arg('ref', () => String, { nullable: true }) ref?: string,
377
+ @Arg('valuesJson', () => String, { nullable: true }) valuesJson?: string,
329
378
  ): Promise<RemoteBrowserActionResult> {
330
379
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
331
- const session = await this.loadOwnedSession(agentSessionID, contextUser, provider);
380
+ const session = await this.loadOwnedSession(agentSessionID, contextUser, provider, instanceKey);
332
381
 
333
- const action = this.buildAction({ kind, url, selector, x, y, text, key, deltaX, deltaY, ms });
382
+ const action = this.buildAction({ kind, url, selector, ref, x, y, text, key, deltaX, deltaY, ms, valuesJson });
334
383
  if (!action) {
335
384
  return { Success: false, Detail: `Unknown or incomplete remote-browser action '${kind}'.` };
336
385
  }
337
386
 
338
387
  const providerName = await this.resolveProviderName(session, contextUser, provider);
339
388
  try {
340
- const liveSession = await RemoteBrowserEngine.Instance.StartSessionForAgentSession(agentSessionID, contextUser, providerName, instanceKey);
389
+ const liveSession = await this.startBrowser(agentSessionID, session, contextUser, providerName, instanceKey);
341
390
  const result = await liveSession.ExecuteAction(action);
342
- return { Success: result.Success, CurrentUrl: result.CurrentUrl, Detail: result.Detail };
391
+ return {
392
+ Success: result.Success,
393
+ CurrentUrl: result.CurrentUrl,
394
+ Detail: result.Detail,
395
+ ...(result.Observation ? { ObservationJson: JSON.stringify(result.Observation) } : {}),
396
+ ...(result.Filled ? { FilledJson: JSON.stringify(result.Filled) } : {}),
397
+ };
343
398
  } catch (err) {
344
399
  // A dead handle is not a failure to report, it is a mapping to delete (#3598) — and this is the
345
400
  // path where that fault is actually FELT: the poll below only freezes a pane, while here the
@@ -364,6 +419,8 @@ export class RemoteBrowserActionResolver extends ResolverBase {
364
419
  Detail: retried.Success
365
420
  ? retried.Detail
366
421
  : `The browser had closed and was replaced (it is now on a blank page). ${retried.Detail ?? ''}`.trim(),
422
+ ...(retried.Observation ? { ObservationJson: JSON.stringify(retried.Observation) } : {}),
423
+ ...(retried.Filled ? { FilledJson: JSON.stringify(retried.Filled) } : {}),
367
424
  };
368
425
  } catch (retryError) {
369
426
  const retryMessage = retryError instanceof Error ? retryError.message : String(retryError);
@@ -402,7 +459,11 @@ export class RemoteBrowserActionResolver extends ResolverBase {
402
459
  @Arg('instanceKey', () => String, { nullable: true }) instanceKey?: string,
403
460
  ): Promise<RemoteBrowserGoalResultType> {
404
461
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
405
- const session = await this.loadOwnedSession(agentSessionID, contextUser, provider);
462
+ const session = await this.loadOwnedSession(agentSessionID, contextUser, provider, instanceKey);
463
+ if (session.IsConversation) {
464
+ // A goal loop clicks and navigates on its own, past the click guard the browser_* tools go through.
465
+ return { Success: false, Status: 'Error', Detail: 'Goal runs are not available in a text conversation; use the browser_* tools.' };
466
+ }
406
467
  const providerName = await this.resolveProviderName(session, contextUser, provider);
407
468
  // Observability: nest this goal's many prompt runs under ONE "Browser goal" step on the realtime
408
469
  // co-agent run (when the session has one). Best-effort — a null step just means the goal runs unlinked.
@@ -489,7 +550,7 @@ export class RemoteBrowserActionResolver extends ResolverBase {
489
550
  @Arg('instanceKey', () => String, { nullable: true }) instanceKey?: string,
490
551
  ): Promise<RemoteBrowserSnapshot> {
491
552
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
492
- await this.loadOwnedSession(agentSessionID, contextUser, provider);
553
+ await this.loadOwnedSession(agentSessionID, contextUser, provider, instanceKey);
493
554
 
494
555
  const liveSession = RemoteBrowserEngine.Instance.GetSessionForAgentSession(agentSessionID, instanceKey);
495
556
  if (!liveSession) {
@@ -572,7 +633,7 @@ export class RemoteBrowserActionResolver extends ResolverBase {
572
633
  @Arg('instanceKey', () => String, { nullable: true }) instanceKey?: string,
573
634
  ): Promise<RemoteBrowserInterpretation> {
574
635
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
575
- await this.loadOwnedSession(agentSessionID, contextUser, provider);
636
+ await this.loadOwnedSession(agentSessionID, contextUser, provider, instanceKey);
576
637
 
577
638
  const liveSession = RemoteBrowserEngine.Instance.GetSessionForAgentSession(agentSessionID, instanceKey);
578
639
  if (!liveSession) {
@@ -614,21 +675,22 @@ export class RemoteBrowserActionResolver extends ResolverBase {
614
675
  @Arg('instanceKey', () => String, { nullable: true }) instanceKey?: string,
615
676
  ): Promise<RemoteBrowserScreencastResult> {
616
677
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
617
- const session = await this.loadOwnedSession(agentSessionID, contextUser, provider);
678
+ const session = await this.loadOwnedSession(agentSessionID, contextUser, provider, instanceKey);
618
679
 
619
680
  // Idempotent PER SURFACE: a re-bind must not stack a second screencast on the one live browser,
620
681
  // but a SECOND surface binding for the first time is not a re-bind (#3531).
621
682
  this.sweepStreamEntries();
622
683
  const streamKey = this.streamKey(agentSessionID, instanceKey);
684
+ this.streamTargets.set(streamKey, userPayload);
623
685
  if (this.startedScreencasts.has(streamKey)) {
624
686
  return { Streaming: true };
625
687
  }
626
688
 
627
689
  const providerName = await this.resolveProviderName(session, contextUser, provider);
628
690
  try {
629
- const liveSession = await RemoteBrowserEngine.Instance.StartSessionForAgentSession(agentSessionID, contextUser, providerName, instanceKey);
691
+ const liveSession = await this.startBrowser(agentSessionID, session, contextUser, providerName, instanceKey);
630
692
  await liveSession.StartScreencast((frame) =>
631
- this.publishFrame(pubSub, userPayload, agentSessionID, instanceKey, frame, liveSession.GetCurrentUrl()),
693
+ this.publishFrame(pubSub, this.streamTargets.get(streamKey) ?? userPayload, agentSessionID, instanceKey, frame, liveSession.GetCurrentUrl()),
632
694
  );
633
695
  this.startedScreencasts.set(streamKey, Date.now());
634
696
  return { Streaming: true };
@@ -637,6 +699,9 @@ export class RemoteBrowserActionResolver extends ResolverBase {
637
699
  // Backend can't stream — the client keeps polling. Not an error condition.
638
700
  return { Streaming: false };
639
701
  }
702
+ if (err instanceof RemoteBrowserRefusedError) {
703
+ throw err; // the client must stop and say so, not poll for a browser that will never start
704
+ }
640
705
  const message = err instanceof Error ? err.message : String(err);
641
706
  LogError(`StartRemoteBrowserScreencast failed (provider='${providerName}'): ${message}`);
642
707
  return { Streaming: false };
@@ -658,7 +723,7 @@ export class RemoteBrowserActionResolver extends ResolverBase {
658
723
  @Arg('instanceKey', () => String, { nullable: true }) instanceKey?: string,
659
724
  ): Promise<boolean> {
660
725
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
661
- await this.loadOwnedSession(agentSessionID, contextUser, provider);
726
+ await this.loadOwnedSession(agentSessionID, contextUser, provider, instanceKey);
662
727
 
663
728
  this.startedScreencasts.delete(this.streamKey(agentSessionID, instanceKey));
664
729
  const liveSession = RemoteBrowserEngine.Instance.GetSessionForAgentSession(agentSessionID, instanceKey);
@@ -674,6 +739,40 @@ export class RemoteBrowserActionResolver extends ResolverBase {
674
739
  return true;
675
740
  }
676
741
 
742
+ /**
743
+ * Ends the browser the caller's own session or conversation started, and forgets its stream
744
+ * bookkeeping. Ownership-gated. Idempotent: ending a scope that holds no browser resolves `false`.
745
+ * A text chat calls this when its browser drawer closes, the conversation changes, or the chat is
746
+ * destroyed, so a closed drawer never leaves a Chromium process (or a billed hosted browser) running.
747
+ *
748
+ * @param agentSessionID The `AIAgentSession` id or `Conversation` id.
749
+ * @returns `true` when a live browser was ended.
750
+ */
751
+ @Mutation(() => Boolean)
752
+ async EndRemoteBrowserSession(
753
+ @Arg('agentSessionID', () => String) agentSessionID: string,
754
+ @Ctx() { userPayload, providers }: AppContext,
755
+ @Arg('instanceKey', () => String, { nullable: true }) instanceKey?: string,
756
+ ): Promise<boolean> {
757
+ const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
758
+ await this.loadOwnedSession(agentSessionID, contextUser, provider, instanceKey);
759
+ return this.endBrowser(agentSessionID, instanceKey);
760
+ }
761
+
762
+ /**
763
+ * Ends a scope's browser and everything kept about it: the ledger slot (released first, so an action
764
+ * landing during teardown is counted again), the stream entries (so the next open streams the next
765
+ * browser) and the browser itself. The idle reaper ends conversation browsers through here too.
766
+ */
767
+ private async endBrowser(agentSessionID: string, instanceKey?: string): Promise<boolean> {
768
+ ConversationBrowserLedger.Instance.Release(agentSessionID);
769
+ const streamKey = this.streamKey(agentSessionID, instanceKey);
770
+ this.startedScreencasts.delete(streamKey);
771
+ this.startedAudioStreams.delete(streamKey);
772
+ this.streamTargets.delete(streamKey);
773
+ return RemoteBrowserEngine.Instance.EndSessionForAgentSession(agentSessionID, instanceKey);
774
+ }
775
+
677
776
  /**
678
777
  * Starts streaming the session browser's TAB AUDIO and PUSHES each encoded chunk to the calling user's
679
778
  * push-status topic — so a co-agent demoing a video/audio site is HEARD, not just seen. Ownership-gated.
@@ -695,19 +794,20 @@ export class RemoteBrowserActionResolver extends ResolverBase {
695
794
  @Arg('instanceKey', () => String, { nullable: true }) instanceKey?: string,
696
795
  ): Promise<RemoteBrowserAudioStreamResult> {
697
796
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
698
- const session = await this.loadOwnedSession(agentSessionID, contextUser, provider);
797
+ const session = await this.loadOwnedSession(agentSessionID, contextUser, provider, instanceKey);
699
798
 
700
799
  // Idempotent PER SURFACE — same reasoning as the screencast map (#3531).
701
800
  this.sweepStreamEntries();
702
801
  const streamKey = this.streamKey(agentSessionID, instanceKey);
802
+ this.streamTargets.set(streamKey, userPayload);
703
803
  if (this.startedAudioStreams.has(streamKey)) {
704
804
  return { Streaming: true };
705
805
  }
706
806
 
707
807
  const providerName = await this.resolveProviderName(session, contextUser, provider);
708
808
  try {
709
- const liveSession = await RemoteBrowserEngine.Instance.StartSessionForAgentSession(agentSessionID, contextUser, providerName, instanceKey);
710
- await liveSession.StartAudioStream((chunk) => this.publishAudioChunk(pubSub, userPayload, agentSessionID, instanceKey, chunk));
809
+ const liveSession = await this.startBrowser(agentSessionID, session, contextUser, providerName, instanceKey);
810
+ await liveSession.StartAudioStream((chunk) => this.publishAudioChunk(pubSub, this.streamTargets.get(streamKey) ?? userPayload, agentSessionID, instanceKey, chunk));
711
811
  this.startedAudioStreams.set(streamKey, Date.now());
712
812
  return { Streaming: true };
713
813
  } catch (err) {
@@ -715,6 +815,9 @@ export class RemoteBrowserActionResolver extends ResolverBase {
715
815
  // Backend can't capture audio — the client plays no audio. Not an error condition.
716
816
  return { Streaming: false };
717
817
  }
818
+ if (err instanceof RemoteBrowserRefusedError) {
819
+ throw err;
820
+ }
718
821
  const message = err instanceof Error ? err.message : String(err);
719
822
  LogError(`StartRemoteBrowserAudioStream failed (provider='${providerName}'): ${message}`);
720
823
  return { Streaming: false };
@@ -736,7 +839,7 @@ export class RemoteBrowserActionResolver extends ResolverBase {
736
839
  @Arg('instanceKey', () => String, { nullable: true }) instanceKey?: string,
737
840
  ): Promise<boolean> {
738
841
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
739
- await this.loadOwnedSession(agentSessionID, contextUser, provider);
842
+ await this.loadOwnedSession(agentSessionID, contextUser, provider, instanceKey);
740
843
 
741
844
  this.startedAudioStreams.delete(this.streamKey(agentSessionID, instanceKey));
742
845
  const liveSession = RemoteBrowserEngine.Instance.GetSessionForAgentSession(agentSessionID, instanceKey);
@@ -792,7 +895,7 @@ export class RemoteBrowserActionResolver extends ResolverBase {
792
895
  @Arg('instanceKey', () => String, { nullable: true }) instanceKey?: string,
793
896
  ): Promise<boolean> {
794
897
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
795
- await this.loadOwnedSession(agentSessionID, contextUser, provider);
898
+ await this.loadOwnedSession(agentSessionID, contextUser, provider, instanceKey);
796
899
 
797
900
  const liveSession = RemoteBrowserEngine.Instance.GetSessionForAgentSession(agentSessionID, instanceKey);
798
901
  if (!liveSession) {
@@ -839,7 +942,7 @@ export class RemoteBrowserActionResolver extends ResolverBase {
839
942
  @Arg('instanceKey', () => String, { nullable: true }) instanceKey?: string,
840
943
  ): Promise<RemoteBrowserSelection> {
841
944
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
842
- await this.loadOwnedSession(agentSessionID, contextUser, provider);
945
+ await this.loadOwnedSession(agentSessionID, contextUser, provider, instanceKey);
843
946
 
844
947
  const liveSession = RemoteBrowserEngine.Instance.GetSessionForAgentSession(agentSessionID, instanceKey);
845
948
  if (!liveSession) {
@@ -1079,21 +1182,89 @@ export class RemoteBrowserActionResolver extends ResolverBase {
1079
1182
  }
1080
1183
 
1081
1184
  /**
1082
- * Loads the agent session and enforces inbound ownership: the session's `UserID` must equal the
1083
- * caller's. Throws when the session is missing or owned by another user.
1185
+ * Loads the owning scope and enforces inbound ownership: an `MJ: AI Agent Sessions` row (a realtime
1186
+ * call, via `LoadOwnedRealtimeSession`) or, failing that, an `MJ: Conversations` row (a text chat's
1187
+ * browser), both under `AuthorizeSessionAccess`. Throws when neither exists or the caller may not use it.
1084
1188
  *
1085
- * @param agentSessionID The `AIAgentSession` id.
1086
- * @returns The loaded, owned session entity.
1189
+ * @param agentSessionID The `AIAgentSession` id or `Conversation` id.
1190
+ * @returns The owned scope.
1087
1191
  */
1088
- protected async loadOwnedSession(agentSessionID: string, contextUser: UserInfo, provider: IMetadataProvider): Promise<MJAIAgentSessionEntity> {
1192
+ protected async loadOwnedSession(agentSessionID: string, contextUser: UserInfo, provider: IMetadataProvider, instanceKey?: string): Promise<OwnedBrowserScope> {
1089
1193
  try {
1090
1194
  return await LoadOwnedRealtimeSession(agentSessionID, contextUser, provider);
1091
1195
  } catch (err) {
1092
- if (err instanceof RealtimeSessionNotFoundError) {
1093
- throw new Error(`Remote-browser session ${agentSessionID} not found.`);
1094
- }
1095
1196
  if (err instanceof RealtimeSessionOwnershipError) {
1096
- throw new Error(`Not authorized: remote-browser session ${agentSessionID} is not owned by you.`);
1197
+ throw new RemoteBrowserRefusedError(`Not authorized: remote-browser session ${agentSessionID} is not owned by you.`);
1198
+ }
1199
+ if (!(err instanceof RealtimeSessionNotFoundError)) {
1200
+ throw err;
1201
+ }
1202
+ }
1203
+ // Not a realtime session: a text chat scopes its browser to a conversation, which this server must opt
1204
+ // into (each browser is a server Chromium or a billed hosted session), under the same access rule
1205
+ // (owner, and for an anonymous widget guest its scope id), loaded as the caller so its RLS applies too.
1206
+ const settings = this.ConversationBrowserSettings();
1207
+ if (!settings.enabled) {
1208
+ throw new RemoteBrowserRefusedError(`Remote-browser session ${agentSessionID} not found. Conversation browsers are not enabled on this server (realtime.conversationBrowser.enabled).`);
1209
+ }
1210
+ if (contextUser.IsMagicLinkAnonymous && !settings.allowAnonymous) {
1211
+ throw new RemoteBrowserRefusedError(`Not authorized: anonymous users cannot open a conversation browser on this server.`);
1212
+ }
1213
+ // One browser per conversation: an instance key would key a second one past the cap, the network
1214
+ // policy and the reaper (the client never sends one).
1215
+ if (instanceKey?.trim()) {
1216
+ throw new RemoteBrowserRefusedError('A conversation browser has no instances; instanceKey is not accepted.');
1217
+ }
1218
+ const conversation = await provider.GetEntityObject<MJConversationEntity>(CONVERSATION_ENTITY, contextUser);
1219
+ if (!(await conversation.Load(agentSessionID))) {
1220
+ throw new RemoteBrowserRefusedError(`Remote-browser session ${agentSessionID} not found.`);
1221
+ }
1222
+ const access = AuthorizeSessionAccess(
1223
+ { SessionUserID: conversation.UserID, ConversationExternalID: conversation.ExternalID, ConversationReadable: true },
1224
+ contextUser,
1225
+ );
1226
+ if (!access.Allowed) {
1227
+ throw new RemoteBrowserRefusedError(`Not authorized: remote-browser session ${agentSessionID} is not owned by you.`);
1228
+ }
1229
+ // Each use puts off the browser's idle end.
1230
+ ConversationBrowserLedger.Instance.Touch(agentSessionID);
1231
+ return { ID: conversation.ID, AgentID: null, Config_: null, IsConversation: true };
1232
+ }
1233
+
1234
+ /** This server's conversation-browser settings (a seam for tests). */
1235
+ protected ConversationBrowserSettings(): ConversationBrowserConfig {
1236
+ return configInfo.realtime.conversationBrowser;
1237
+ }
1238
+
1239
+ /**
1240
+ * Starts (or reuses) the scope's browser. A conversation's browser first counts against its owner's
1241
+ * cap, and gives the slot back when it fails to start.
1242
+ */
1243
+ private async startBrowser(
1244
+ agentSessionID: string,
1245
+ scope: OwnedBrowserScope,
1246
+ contextUser: UserInfo,
1247
+ providerName: string | undefined,
1248
+ instanceKey?: string,
1249
+ ): Promise<IRemoteBrowserSession> {
1250
+ if (scope.IsConversation) {
1251
+ const settings = this.ConversationBrowserSettings();
1252
+ // ponytail: an action that joins a launch a concurrent End then tears down keeps this slot until the
1253
+ // reaper frees it (idleMinutes); release it on that teardown if users hit maxPerUser this way.
1254
+ // An anonymous guest's cap is its own: every guest is the one shared Anonymous user.
1255
+ const guestScope = contextUser.IsMagicLinkAnonymous ? ResolveSessionScopeIdentity(contextUser) : undefined;
1256
+ const owner = { Key: guestScope ? `scope:${guestScope}` : `user:${NormalizeUUID(contextUser.ID)}`, Anonymous: Boolean(contextUser.IsMagicLinkAnonymous) };
1257
+ ConversationBrowserLedger.Instance.Claim(agentSessionID, owner, settings);
1258
+ // Set once, before the browser launches; the engine re-applies it to any replacement.
1259
+ if (!RemoteBrowserEngine.Instance.GetSessionForAgentSession(agentSessionID, instanceKey)) {
1260
+ await RemoteBrowserEngine.Instance.RestrictPrivateNetworkForAgentSession(agentSessionID, settings.allowedPrivateHosts);
1261
+ }
1262
+ }
1263
+ try {
1264
+ return await RemoteBrowserEngine.Instance.StartSessionForAgentSession(agentSessionID, contextUser, providerName, instanceKey);
1265
+ } catch (err) {
1266
+ if (scope.IsConversation && !RemoteBrowserEngine.Instance.GetSessionForAgentSession(agentSessionID, instanceKey)) {
1267
+ ConversationBrowserLedger.Instance.Release(agentSessionID);
1097
1268
  }
1098
1269
  throw err;
1099
1270
  }
@@ -1105,10 +1276,13 @@ export class RemoteBrowserActionResolver extends ResolverBase {
1105
1276
  * config, or unparseable JSON all yield `undefined`, letting the engine fall back to the single Active
1106
1277
  * provider.
1107
1278
  *
1108
- * @param session The owned session entity (supplies the agent id).
1279
+ * @param session The owned scope (supplies the agent id; a conversation has none).
1109
1280
  * @returns The configured backend name, or `undefined` to let the engine auto-select.
1110
1281
  */
1111
- protected async resolveProviderName(session: MJAIAgentSessionEntity, contextUser: UserInfo, provider: IMetadataProvider): Promise<string | undefined> {
1282
+ protected async resolveProviderName(session: OwnedBrowserScope, contextUser: UserInfo, provider: IMetadataProvider): Promise<string | undefined> {
1283
+ if (!session.AgentID) {
1284
+ return undefined;
1285
+ }
1112
1286
  // The session's agent IS the co-agent (the realtime voice agent), and the interactive channels
1113
1287
  // (Remote Browser, Whiteboard) are the CO-AGENT's abilities — so the remoteBrowser backend config
1114
1288
  // lives on the co-agent's TypeConfiguration, not on the target agent it voices.
@@ -1159,6 +1333,7 @@ export class RemoteBrowserActionResolver extends ResolverBase {
1159
1333
  kind: string;
1160
1334
  url?: string;
1161
1335
  selector?: string;
1336
+ ref?: string;
1162
1337
  x?: number;
1163
1338
  y?: number;
1164
1339
  text?: string;
@@ -1166,13 +1341,21 @@ export class RemoteBrowserActionResolver extends ResolverBase {
1166
1341
  deltaX?: number;
1167
1342
  deltaY?: number;
1168
1343
  ms?: number;
1344
+ valuesJson?: string;
1169
1345
  }): RemoteBrowserAction | null {
1170
1346
  switch (input.kind) {
1171
1347
  case 'navigate':
1172
1348
  return input.url ? { Kind: 'navigate', Url: input.url } : null;
1173
1349
  case 'click':
1174
- if (input.selector || (typeof input.x === 'number' && typeof input.y === 'number')) {
1175
- return { Kind: 'click', Selector: input.selector, X: input.x, Y: input.y };
1350
+ if (input.selector || input.ref || input.text || (typeof input.x === 'number' && typeof input.y === 'number')) {
1351
+ return {
1352
+ Kind: 'click',
1353
+ Selector: input.selector,
1354
+ Ref: input.ref,
1355
+ Text: input.text,
1356
+ X: input.x,
1357
+ Y: input.y,
1358
+ };
1176
1359
  }
1177
1360
  return null;
1178
1361
  case 'type':
@@ -1193,11 +1376,38 @@ export class RemoteBrowserActionResolver extends ResolverBase {
1193
1376
  return { Kind: 'wait', Ms: input.ms, Selector: input.selector };
1194
1377
  }
1195
1378
  return null;
1379
+ case 'inspect':
1380
+ return { Kind: 'inspect' };
1381
+ case 'getPageText':
1382
+ return { Kind: 'getPageText' };
1383
+ case 'fill': {
1384
+ const values = this.parseFillValues(input.valuesJson);
1385
+ return values.length > 0 ? { Kind: 'fill', Values: values } : null;
1386
+ }
1196
1387
  default:
1197
1388
  return null;
1198
1389
  }
1199
1390
  }
1200
1391
 
1392
+ /**
1393
+ * Parses the `valuesJson` payload for a `fill` action: a JSON object mapping a field ref (`f1`) or
1394
+ * label to its value (`string`, or `boolean` for a checkbox). Anything else yields no values.
1395
+ */
1396
+ private parseFillValues(valuesJson?: string): RemoteBrowserFillFieldInput[] {
1397
+ let parsed: unknown;
1398
+ try {
1399
+ parsed = JSON.parse(valuesJson ?? '');
1400
+ } catch {
1401
+ return [];
1402
+ }
1403
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
1404
+ return [];
1405
+ }
1406
+ return Object.entries(parsed as Record<string, unknown>)
1407
+ .filter(([target, value]) => target.trim().length > 0 && value !== undefined && value !== null)
1408
+ .map(([target, value]) => ({ Target: target.trim(), Value: typeof value === 'boolean' ? value : String(value) }));
1409
+ }
1410
+
1201
1411
  /**
1202
1412
  * Builds a strongly-typed {@link RemoteBrowserHumanInput} from the relayed `kind` + fields, validating
1203
1413
  * each kind's required field(s). Returns `null` for an unknown kind or a kind missing its required