@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
@@ -49,11 +49,17 @@ const startSessionMock = vi.fn<[], Promise<FakeSession>>(async () => deadSession
49
49
  // documented outcome for anything that is not a dead handle, so it is the default: the pre-existing
50
50
  // error-reporting contract must survive untouched for every ordinary failure.
51
51
  const recoverDeadSessionMock = vi.fn<[], Promise<FakeSession | null>>(async () => null);
52
+ const endSessionMock = vi.fn(async (_agentSessionID: string) => true);
53
+ const restrictMock = vi.fn(async (_agentSessionID: string, _allowedHosts: string[]) => undefined);
52
54
  vi.mock('@memberjunction/remote-browser-server', () => ({
55
+ NormalizeInstanceKey: (key?: string) => key?.trim() || null,
53
56
  RemoteBrowserEngine: {
54
57
  Instance: {
55
58
  StartSessionForAgentSession: (...args: unknown[]) => startSessionMock(...(args as [])),
56
59
  RecoverDeadAgentSession: (...args: unknown[]) => recoverDeadSessionMock(...(args as [])),
60
+ EndSessionForAgentSession: (agentSessionID: string) => endSessionMock(agentSessionID),
61
+ RestrictPrivateNetworkForAgentSession: (agentSessionID: string, allowedHosts: string[]) => restrictMock(agentSessionID, allowedHosts),
62
+ GetSessionForAgentSession: () => undefined,
57
63
  },
58
64
  },
59
65
  }));
@@ -71,7 +77,12 @@ vi.mock('@memberjunction/ai-core-plus', async (importOriginal) => ({
71
77
  AIPromptParams: class {},
72
78
  }));
73
79
 
80
+ // The resolver reads its conversation-browser settings from the server config; off, as by default.
81
+ vi.mock('../config.js', () => ({ configInfo: { realtime: { conversationBrowser: { enabled: false } } } }));
82
+
74
83
  import { RemoteBrowserActionResolver } from '../resolvers/RemoteBrowserActionResolver.js';
84
+ import { ConversationBrowserLedger } from '../agentSessions/conversationBrowsers.js';
85
+ import type { ConversationBrowserConfig } from '../config.js';
75
86
 
76
87
  /** Bypasses the ownership/provider plumbing (covered elsewhere) so the recovery branch stands alone. */
77
88
  class TestableResolver extends RemoteBrowserActionResolver {
@@ -171,3 +182,279 @@ describe('RemoteBrowserActionResolver — dead-handle recovery on the action pat
171
182
  expect(recoverDeadSessionMock).toHaveBeenCalledTimes(1);
172
183
  });
173
184
  });
185
+
186
+ describe('RemoteBrowserActionResolver — conversation-scoped ownership & structured text actions', () => {
187
+ const userID = 'a1a1a1a1-0000-4000-8000-000000000001';
188
+ const otherUserID = 'c3c3c3c3-0000-4000-8000-000000000003';
189
+ const convID = 'b2b2b2b2-0000-4000-8000-000000000002';
190
+
191
+ const ENABLED: ConversationBrowserConfig = { enabled: true, allowAnonymous: false, maxPerUser: 2, maxAnonymousTotal: 4, idleMinutes: 15, allowedPrivateHosts: ['localhost:4200'] };
192
+
193
+ /**
194
+ * A resolver whose provider has no agent session by that ID, only conversations owned by `ownerID`
195
+ * (stamped with `externalID`, an anonymous guest's scope id), called by `caller`, under `settings`.
196
+ */
197
+ function conversationResolver(
198
+ ownerID: string,
199
+ caller: Record<string, unknown> = { ID: userID },
200
+ externalID: string | null = null,
201
+ settings: ConversationBrowserConfig = ENABLED,
202
+ ): RemoteBrowserActionResolver {
203
+ const getEntityObject = vi.fn(async (entityName: string) => {
204
+ if (entityName !== 'MJ: Conversations') {
205
+ return { Load: vi.fn(async () => false) };
206
+ }
207
+ const conversation = { ID: '', UserID: ownerID, ExternalID: externalID, Load: vi.fn(async (id: string) => {
208
+ conversation.ID = id;
209
+ return id.startsWith('b2b2b2b2');
210
+ }) };
211
+ return conversation;
212
+ });
213
+ class OwnershipResolver extends RemoteBrowserActionResolver {
214
+ protected override requireUserAndProvider() {
215
+ return { contextUser: caller, provider: { GetEntityObject: getEntityObject } } as never;
216
+ }
217
+ protected override ConversationBrowserSettings(): ConversationBrowserConfig {
218
+ return settings;
219
+ }
220
+ }
221
+ return new OwnershipResolver();
222
+ }
223
+
224
+ beforeEach(() => {
225
+ recoverDeadSessionMock.mockReset().mockResolvedValue(null);
226
+ for (const id of [convID, convID2, convID3]) {
227
+ ConversationBrowserLedger.Instance.Release(id);
228
+ }
229
+ });
230
+ const convID2 = 'b2b2b2b2-0000-4000-8000-000000000012';
231
+ const convID3 = 'b2b2b2b2-0000-4000-8000-000000000013';
232
+
233
+ it('refuses an instance key for a conversation, which would key a second, uncounted browser', async () => {
234
+ startSessionMock.mockReset();
235
+ const resolver = conversationResolver(userID);
236
+ await expect(
237
+ resolver.ExecuteRemoteBrowserAction(convID, 'inspect', ctx, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 'x'),
238
+ ).rejects.toThrow('instanceKey is not accepted');
239
+ await expect(resolver.EndRemoteBrowserSession(convID, ctx, 'x')).rejects.toThrow('instanceKey is not accepted');
240
+ expect(startSessionMock).not.toHaveBeenCalled();
241
+ });
242
+
243
+ it('applies the network policy when the browser starts, not on every poll', async () => {
244
+ deadSession = makeSession();
245
+ startSessionMock.mockReset().mockResolvedValue(deadSession);
246
+ restrictMock.mockClear();
247
+ const resolver = conversationResolver(userID);
248
+ await resolver.RemoteBrowserSnapshot(convID, ctx);
249
+ expect(restrictMock).not.toHaveBeenCalled();
250
+ await resolver.ExecuteRemoteBrowserAction(convID, 'inspect', ctx);
251
+ expect(restrictMock).toHaveBeenCalledWith(convID, ['localhost:4200']);
252
+ });
253
+
254
+ it('after the idle reaper ends a browser, the next open streams the new one instead of answering "already streaming"', async () => {
255
+ const streamingSession = { ...makeSession(), StartScreencast: vi.fn(async () => undefined) };
256
+ startSessionMock.mockReset().mockResolvedValue(streamingSession as never);
257
+ endSessionMock.mockClear();
258
+ const resolver = conversationResolver(userID, { ID: userID }, null, { ...ENABLED, idleMinutes: -1 });
259
+ const pubSub = { publish: vi.fn() } as never;
260
+
261
+ expect((await resolver.StartRemoteBrowserScreencast(convID, ctx, pubSub)).Streaming).toBe(true);
262
+ expect(await ConversationBrowserLedger.Instance.ReapIdle()).toBe(1);
263
+ expect(endSessionMock).toHaveBeenCalledWith(convID);
264
+
265
+ expect((await resolver.StartRemoteBrowserScreencast(convID, ctx, pubSub)).Streaming).toBe(true);
266
+ expect(streamingSession.StartScreencast).toHaveBeenCalledTimes(2);
267
+ });
268
+
269
+ it('caps each anonymous guest on its own, not every guest as one shared Anonymous user', async () => {
270
+ deadSession = makeSession();
271
+ startSessionMock.mockReset().mockResolvedValue(deadSession);
272
+ const settings = { ...ENABLED, allowAnonymous: true, maxPerUser: 1 };
273
+ const guestA = { ID: userID, IsMagicLinkAnonymous: true, AnonymousVisitorID: 'visitor-a' };
274
+ const guestB = { ID: userID, IsMagicLinkAnonymous: true, AnonymousVisitorID: 'visitor-b' };
275
+ expect((await conversationResolver(userID, guestA, 'visitor-a', settings).ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).Success).toBe(true);
276
+ expect((await conversationResolver(userID, guestB, 'visitor-b', settings).ExecuteRemoteBrowserAction(convID2, 'inspect', ctx)).Success).toBe(true);
277
+ });
278
+
279
+ it('caps all anonymous guests together, since a guest can start a new scope cheaply; signed-in users are not counted', async () => {
280
+ deadSession = makeSession();
281
+ startSessionMock.mockReset().mockResolvedValue(deadSession);
282
+ const settings = { ...ENABLED, allowAnonymous: true, maxPerUser: 1, maxAnonymousTotal: 2 };
283
+ const guest = (visitor: string) => ({ ID: userID, IsMagicLinkAnonymous: true, AnonymousVisitorID: visitor });
284
+ const open = (visitor: string, conversationID: string) =>
285
+ conversationResolver(userID, guest(visitor), visitor, settings).ExecuteRemoteBrowserAction(conversationID, 'inspect', ctx);
286
+ expect((await open('visitor-a', convID)).Success).toBe(true);
287
+ expect((await open('visitor-b', convID2)).Success).toBe(true);
288
+ const third = await open('visitor-c', convID3);
289
+ expect(third.Success).toBe(false);
290
+ expect(third.Detail).toContain('allows for guests is in use');
291
+ expect((await conversationResolver(userID, { ID: userID }, null, settings).ExecuteRemoteBrowserAction(convID3, 'inspect', ctx)).Success).toBe(true);
292
+ });
293
+
294
+ it('every refusal carries the REMOTE_BROWSER_REFUSED code, so a client stops asking', async () => {
295
+ deadSession = makeSession();
296
+ startSessionMock.mockReset().mockResolvedValue({ ...deadSession, StartScreencast: vi.fn(async () => undefined) } as never);
297
+ const refused = { extensions: { code: 'REMOTE_BROWSER_REFUSED' } };
298
+ const guest = { ID: userID, IsMagicLinkAnonymous: true, AnonymousVisitorID: 'visitor-a' };
299
+ const pubSub = { publish: vi.fn() } as never;
300
+ await expect(conversationResolver(userID, { ID: userID }, null, { ...ENABLED, enabled: false }).RemoteBrowserSnapshot(convID, ctx)).rejects.toMatchObject(refused);
301
+ await expect(conversationResolver(userID, guest, 'visitor-a').RemoteBrowserSnapshot(convID, ctx)).rejects.toMatchObject(refused);
302
+ await expect(conversationResolver(userID).EndRemoteBrowserSession(convID, ctx, 'x')).rejects.toMatchObject(refused);
303
+ await expect(conversationResolver(otherUserID).RemoteBrowserSnapshot(convID, ctx)).rejects.toMatchObject(refused);
304
+ await expect(conversationResolver(userID).RemoteBrowserSnapshot('d4d4d4d4-0000-4000-8000-000000000004', ctx)).rejects.toMatchObject(refused);
305
+ const capped = conversationResolver(userID, { ID: userID }, null, { ...ENABLED, maxPerUser: 1 });
306
+ await capped.StartRemoteBrowserScreencast(convID, ctx, pubSub);
307
+ await expect(capped.StartRemoteBrowserScreencast(convID2, ctx, pubSub)).rejects.toMatchObject(refused);
308
+ });
309
+
310
+ it('forgets where a stopped stream went, so a browser that never ends cannot leak its targets', async () => {
311
+ const streamingSession = { ...makeSession(), StartScreencast: vi.fn(async () => undefined) };
312
+ startSessionMock.mockReset().mockResolvedValue(streamingSession as never);
313
+ const resolver = conversationResolver(userID);
314
+ const pubSub = { publish: vi.fn() } as never;
315
+ const targets = (resolver as unknown as { streamTargets: Map<string, unknown> }).streamTargets;
316
+
317
+ await resolver.StartRemoteBrowserScreencast(convID, ctx, pubSub);
318
+ await resolver.StopRemoteBrowserScreencast(convID, ctx);
319
+ await resolver.StartRemoteBrowserScreencast(convID2, ctx, pubSub);
320
+ expect(targets.size).toBe(1);
321
+ });
322
+
323
+ it('a reloaded tab (a new push session) takes the frames over instead of hearing "already streaming"', async () => {
324
+ let onFrame: ((frame: { DataBase64: string; Width: number; Height: number; SequenceNumber: number }) => void) | null = null;
325
+ const streamingSession = { ...makeSession(), StartScreencast: vi.fn(async (cb: typeof onFrame) => { onFrame = cb; }) };
326
+ startSessionMock.mockReset().mockResolvedValue(streamingSession as never);
327
+ const resolver = conversationResolver(userID);
328
+ const publish = vi.spyOn(resolver as unknown as { PublishStatusUpdate: (...args: unknown[]) => void }, 'PublishStatusUpdate').mockImplementation(() => undefined);
329
+ const pubSub = { publish: vi.fn() } as never;
330
+ const tab = (sessionId: string) => ({ userPayload: { sessionId }, providers: {} }) as never;
331
+
332
+ await resolver.StartRemoteBrowserScreencast(convID, tab('push-A'), pubSub);
333
+ await resolver.StartRemoteBrowserScreencast(convID, tab('push-B'), pubSub);
334
+ expect(streamingSession.StartScreencast).toHaveBeenCalledTimes(1);
335
+ onFrame!({ DataBase64: 'f', Width: 1, Height: 1, SequenceNumber: 1 });
336
+ expect(publish.mock.calls[0][1]).toBe('push-B');
337
+ });
338
+
339
+ it('a cap refusal on opening the live view reaches the client as an error, not a silent "not streaming"', async () => {
340
+ deadSession = makeSession();
341
+ startSessionMock.mockReset().mockResolvedValue({ ...deadSession, StartScreencast: vi.fn(async () => undefined) } as never);
342
+ const resolver = conversationResolver(userID, { ID: userID }, null, { ...ENABLED, maxPerUser: 1 });
343
+ const pubSub = { publish: vi.fn() } as never;
344
+ expect((await resolver.StartRemoteBrowserScreencast(convID, ctx, pubSub)).Streaming).toBe(true);
345
+ await expect(resolver.StartRemoteBrowserScreencast(convID2, ctx, pubSub)).rejects.toThrow('the most this server allows');
346
+ });
347
+
348
+ it('refuses every conversation operation while the server has not enabled conversation browsers', async () => {
349
+ startSessionMock.mockReset();
350
+ const off = conversationResolver(userID, { ID: userID }, null, { ...ENABLED, enabled: false });
351
+ await expect(off.ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).rejects.toThrow('not enabled');
352
+ expect(startSessionMock).not.toHaveBeenCalled();
353
+ });
354
+
355
+ it('refuses an anonymous guest its own conversation browser unless anonymous use is enabled', async () => {
356
+ deadSession = makeSession();
357
+ startSessionMock.mockReset().mockResolvedValue(deadSession);
358
+ const guest = { ID: userID, IsMagicLinkAnonymous: true, AnonymousVisitorID: 'visitor-a' };
359
+ await expect(conversationResolver(userID, guest, 'visitor-a').ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).rejects.toThrow('anonymous');
360
+ expect(startSessionMock).not.toHaveBeenCalled();
361
+ const allowed = conversationResolver(userID, guest, 'visitor-a', { ...ENABLED, allowAnonymous: true });
362
+ expect((await allowed.ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).Success).toBe(true);
363
+ });
364
+
365
+ it('caps the conversation browsers one user holds, keeps it off the private network, and refuses goal runs', async () => {
366
+ deadSession = makeSession();
367
+ startSessionMock.mockReset().mockResolvedValue(deadSession);
368
+ restrictMock.mockClear();
369
+ const resolver = conversationResolver(userID);
370
+ expect((await resolver.ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).Success).toBe(true);
371
+ expect((await resolver.ExecuteRemoteBrowserAction(convID2, 'inspect', ctx)).Success).toBe(true);
372
+ expect((await resolver.ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).Success).toBe(true); // already held
373
+ const third = await resolver.ExecuteRemoteBrowserAction(convID3, 'inspect', ctx);
374
+ expect(third.Success).toBe(false);
375
+ expect(third.Detail).toContain('the most this server allows');
376
+ expect(startSessionMock).toHaveBeenCalledTimes(3);
377
+ expect(restrictMock).toHaveBeenCalledWith(convID, ['localhost:4200']);
378
+
379
+ const goal = await resolver.ExecuteRemoteBrowserGoal(convID, 'buy the thing', ctx);
380
+ expect(goal.Success).toBe(false);
381
+ expect(goal.Detail).toContain('not available in a text conversation');
382
+ });
383
+
384
+ it('acts on a conversation the caller owns', async () => {
385
+ deadSession = makeSession();
386
+ deadSession.ExecuteAction.mockResolvedValue({
387
+ Success: true,
388
+ CurrentUrl: 'https://httpbin.org/forms/post',
389
+ Detail: 'Inspected',
390
+ Observation: { ObservationID: 'obs-1', Url: 'https://httpbin.org/forms/post', Title: 'Order Form', Text: '', Fields: [], Actions: [], Links: [] },
391
+ });
392
+ startSessionMock.mockReset().mockResolvedValue(deadSession);
393
+
394
+ const res = await conversationResolver(userID).ExecuteRemoteBrowserAction(convID, 'inspect', ctx);
395
+ expect(res.Success).toBe(true);
396
+ expect(JSON.parse(res.ObservationJson!).ObservationID).toBe('obs-1');
397
+ });
398
+
399
+ it("refuses another user's conversation, for actions and for ending its browser", async () => {
400
+ startSessionMock.mockReset();
401
+ endSessionMock.mockClear();
402
+ const resolver = conversationResolver(otherUserID);
403
+ await expect(resolver.ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).rejects.toThrow('Not authorized');
404
+ await expect(resolver.EndRemoteBrowserSession(convID, ctx)).rejects.toThrow('Not authorized');
405
+ expect(startSessionMock).not.toHaveBeenCalled();
406
+ expect(endSessionMock).not.toHaveBeenCalled();
407
+ });
408
+
409
+ it("refuses an anonymous guest another guest's conversation, though both share the Anonymous user", async () => {
410
+ startSessionMock.mockReset();
411
+ const guest = { ID: userID, IsMagicLinkAnonymous: true, AnonymousVisitorID: 'visitor-b' };
412
+ await expect(conversationResolver(userID, guest, 'visitor-a').ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).rejects.toThrow('Not authorized');
413
+ expect(startSessionMock).not.toHaveBeenCalled();
414
+ });
415
+
416
+ it("ends the caller's own conversation browser", async () => {
417
+ endSessionMock.mockClear();
418
+ await expect(conversationResolver(userID).EndRemoteBrowserSession(convID, ctx)).resolves.toBe(true);
419
+ expect(endSessionMock).toHaveBeenCalledWith(convID);
420
+ });
421
+
422
+ it('maps a fill values object to field inputs and returns FilledJson + ObservationJson; any other shape is refused', async () => {
423
+ const resolver = new TestableResolver();
424
+ deadSession = makeSession();
425
+ deadSession.ExecuteAction.mockResolvedValue({
426
+ Success: true,
427
+ CurrentUrl: 'https://httpbin.org/forms/post',
428
+ Detail: 'Filled 1 field(s).',
429
+ Filled: [{ Ref: 'f1', Label: 'Customer name', Value: 'Colin' }],
430
+ Observation: { ObservationID: 'obs-2', Url: 'https://httpbin.org/forms/post', Title: 'Order Form', Text: '', Fields: [], Actions: [], Links: [] },
431
+ });
432
+ startSessionMock.mockReset().mockResolvedValue(deadSession);
433
+ const fill = (valuesJson: string) =>
434
+ resolver.ExecuteRemoteBrowserAction('sess-1', 'fill', ctx, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, valuesJson);
435
+
436
+ const res = await fill(JSON.stringify({ f1: 'Colin', f9: true }));
437
+ expect(res.Success).toBe(true);
438
+ expect(deadSession.ExecuteAction).toHaveBeenCalledWith({ Kind: 'fill', Values: [{ Target: 'f1', Value: 'Colin' }, { Target: 'f9', Value: true }] });
439
+ expect(JSON.parse(res.FilledJson!)).toEqual([{ Ref: 'f1', Label: 'Customer name', Value: 'Colin' }]);
440
+
441
+ expect((await fill(JSON.stringify([{ ref: 'f1', value: 'Colin' }]))).Success).toBe(false);
442
+ expect((await fill('not json')).Success).toBe(false);
443
+ });
444
+
445
+ it('maps getPageText and a ref/text click', async () => {
446
+ const resolver = new TestableResolver();
447
+ deadSession = makeSession();
448
+ deadSession.ExecuteAction.mockResolvedValue({ Success: true, CurrentUrl: 'https://example.com/', Detail: 'Example Domain' });
449
+ startSessionMock.mockReset().mockResolvedValue(deadSession);
450
+
451
+ expect((await resolver.ExecuteRemoteBrowserAction('sess-1', 'getPageText', ctx)).Success).toBe(true);
452
+ expect(deadSession.ExecuteAction).toHaveBeenCalledWith({ Kind: 'getPageText' });
453
+
454
+ const clickRes = await resolver.ExecuteRemoteBrowserAction(
455
+ 'sess-1', 'click', ctx, undefined, undefined, undefined, undefined, 'Learn more', undefined, undefined, undefined, undefined, undefined, 'a1',
456
+ );
457
+ expect(clickRes.Success).toBe(true);
458
+ expect(deadSession.ExecuteAction).toHaveBeenLastCalledWith({ Kind: 'click', Selector: undefined, Ref: 'a1', Text: 'Learn more', X: undefined, Y: undefined });
459
+ });
460
+ });
@@ -58,6 +58,9 @@ vi.mock('@memberjunction/ai-core-plus', async (importOriginal) => ({
58
58
  AIPromptParams: class {},
59
59
  }));
60
60
 
61
+ // The resolver reads its conversation-browser settings from the server config; off, as by default.
62
+ vi.mock('../config.js', () => ({ configInfo: { realtime: { conversationBrowser: { enabled: false } } } }));
63
+
61
64
  import { RemoteBrowserActionResolver } from '../resolvers/RemoteBrowserActionResolver.js';
62
65
  import type { AppContext } from '../types.js';
63
66
  import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
@@ -1,7 +1,6 @@
1
1
  import 'reflect-metadata';
2
2
  import { describe, it, expect, vi } from 'vitest';
3
3
  import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
4
- import type { MJAIAgentSessionEntity } from '@memberjunction/core-entities';
5
4
 
6
5
  vi.mock('type-graphql', () => {
7
6
  const noopDecorator = () => () => undefined;
@@ -38,7 +37,10 @@ vi.mock('@memberjunction/ai-core-plus', async (importOriginal) => ({
38
37
  AIPromptParams: class {},
39
38
  }));
40
39
 
41
- import { RemoteBrowserActionResolver } from '../resolvers/RemoteBrowserActionResolver.js';
40
+ // The resolver reads its conversation-browser settings from the server config; off, as by default.
41
+ vi.mock('../config.js', () => ({ configInfo: { realtime: { conversationBrowser: { enabled: false } } } }));
42
+
43
+ import { RemoteBrowserActionResolver, type OwnedBrowserScope } from '../resolvers/RemoteBrowserActionResolver.js';
42
44
 
43
45
  const uuid = (a: string, b: string) =>
44
46
  [a.repeat(8), b.repeat(4), '4' + b.repeat(3), '8' + b.repeat(3), b.repeat(12)].join('-');
@@ -59,7 +61,7 @@ function makeProvider(sessionFound = true, externalId = 'visitor-1'): IMetadataP
59
61
  GetEntityObject: vi.fn(async (entityName: string) =>
60
62
  entityName === 'MJ: AI Agent Sessions'
61
63
  ? { ID: SESSION, UserID: ANON, ConversationID: CONVERSATION, Load: vi.fn(async () => sessionFound) }
62
- : { ExternalID: externalId, Load: vi.fn(async () => true) },
64
+ : { ExternalID: externalId, Load: vi.fn(async (id: string) => id === CONVERSATION) },
63
65
  ),
64
66
  } as unknown as IMetadataProvider;
65
67
  }
@@ -69,7 +71,7 @@ class TestableRemoteBrowserResolver extends RemoteBrowserActionResolver {
69
71
  agentSessionID: string,
70
72
  contextUser: UserInfo,
71
73
  provider: IMetadataProvider,
72
- ): Promise<MJAIAgentSessionEntity> {
74
+ ): Promise<OwnedBrowserScope> {
73
75
  return this.loadOwnedSession(agentSessionID, contextUser, provider);
74
76
  }
75
77
  }
@@ -88,6 +88,9 @@ vi.mock('@memberjunction/ai-core-plus', async (importOriginal) => ({
88
88
  AIPromptParams: class {},
89
89
  }));
90
90
 
91
+ // The resolver reads its conversation-browser settings from the server config; off, as by default.
92
+ vi.mock('../config.js', () => ({ configInfo: { realtime: { conversationBrowser: { enabled: false } } } }));
93
+
91
94
  import { RemoteBrowserActionResolver } from '../resolvers/RemoteBrowserActionResolver.js';
92
95
 
93
96
  /**
@@ -76,6 +76,9 @@ vi.mock('@memberjunction/ai-core-plus', async (importOriginal) => ({
76
76
  AIPromptParams: class {},
77
77
  }));
78
78
 
79
+ // The resolver reads its conversation-browser settings from the server config; off, as by default.
80
+ vi.mock('../config.js', () => ({ configInfo: { realtime: { conversationBrowser: { enabled: false } } } }));
81
+
79
82
  import { RemoteBrowserActionResolver } from '../resolvers/RemoteBrowserActionResolver.js';
80
83
 
81
84
  /**