@memberjunction/server 6.2.0-edge.3 → 6.2.0-edge.4
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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/BridgeHostInstance.d.ts +25 -0
- package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -0
- package/dist/agentSessions/BridgeHostInstance.js +30 -0
- package/dist/agentSessions/BridgeHostInstance.js.map +1 -0
- package/dist/agentSessions/HostInstance.d.ts +75 -11
- package/dist/agentSessions/HostInstance.d.ts.map +1 -1
- package/dist/agentSessions/HostInstance.js +99 -28
- package/dist/agentSessions/HostInstance.js.map +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.js +7 -3
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +173 -25
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +452 -54
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +43 -2
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +99 -8
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/agentSessions/SessionRunIDVerifier.d.ts +63 -0
- package/dist/agentSessions/SessionRunIDVerifier.d.ts.map +1 -0
- package/dist/agentSessions/SessionRunIDVerifier.js +129 -0
- package/dist/agentSessions/SessionRunIDVerifier.js.map +1 -0
- package/dist/agentSessions/index.d.ts +2 -0
- package/dist/agentSessions/index.d.ts.map +1 -1
- package/dist/agentSessions/index.js +10 -3
- package/dist/agentSessions/index.js.map +1 -1
- package/dist/auth/actionAuthorization.d.ts +40 -0
- package/dist/auth/actionAuthorization.d.ts.map +1 -0
- package/dist/auth/actionAuthorization.js +94 -0
- package/dist/auth/actionAuthorization.js.map +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +3 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +6 -3
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/newUsers.d.ts +10 -0
- package/dist/auth/newUsers.d.ts.map +1 -1
- package/dist/auth/newUsers.js +80 -11
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/config.d.ts +2378 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +123 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +8 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +14 -11
- package/dist/context.js.map +1 -1
- package/dist/generated/graphql-schemas/__mj.d.ts +135 -0
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +706 -0
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +3 -3
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +20 -4
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +39 -10
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +4 -0
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +120 -131
- package/dist/index.js.map +1 -1
- package/dist/isolatedProvider.d.ts +20 -0
- package/dist/isolatedProvider.d.ts.map +1 -0
- package/dist/isolatedProvider.js +22 -0
- package/dist/isolatedProvider.js.map +1 -0
- package/dist/realtimeProxy/RealtimeProxyServer.d.ts +44 -4
- package/dist/realtimeProxy/RealtimeProxyServer.d.ts.map +1 -1
- package/dist/realtimeProxy/RealtimeProxyServer.js +121 -16
- package/dist/realtimeProxy/RealtimeProxyServer.js.map +1 -1
- package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts +171 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts.map +1 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.js +532 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.js.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.d.ts +148 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.js +191 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.js.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts +66 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js +138 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts +38 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.js +98 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts +61 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js +149 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts +57 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts.map +1 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.js +69 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.js.map +1 -0
- package/dist/realtimeSessions/index.d.ts +17 -0
- package/dist/realtimeSessions/index.d.ts.map +1 -0
- package/dist/realtimeSessions/index.js +17 -0
- package/dist/realtimeSessions/index.js.map +1 -0
- package/dist/realtimeSessions/sessionAccess.d.ts +86 -0
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -0
- package/dist/realtimeSessions/sessionAccess.js +90 -0
- package/dist/realtimeSessions/sessionAccess.js.map +1 -0
- package/dist/realtimeSessions/sessionConfigStamps.d.ts +70 -0
- package/dist/realtimeSessions/sessionConfigStamps.d.ts.map +1 -0
- package/dist/realtimeSessions/sessionConfigStamps.js +93 -0
- package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -0
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts +65 -0
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -0
- package/dist/realtimeSessions/subscriptionAuthorization.js +71 -0
- package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -0
- package/dist/realtimeSessions/verificationCore.d.ts +243 -0
- package/dist/realtimeSessions/verificationCore.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationCore.js +356 -0
- package/dist/realtimeSessions/verificationCore.js.map +1 -0
- package/dist/realtimeSessions/verificationPages.d.ts +66 -0
- package/dist/realtimeSessions/verificationPages.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationPages.js +110 -0
- package/dist/realtimeSessions/verificationPages.js.map +1 -0
- package/dist/realtimeSessions/verificationPolicy.d.ts +147 -0
- package/dist/realtimeSessions/verificationPolicy.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationPolicy.js +249 -0
- package/dist/realtimeSessions/verificationPolicy.js.map +1 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts +263 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationWorkflow.js +435 -0
- package/dist/realtimeSessions/verificationWorkflow.js.map +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +20 -2
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +31 -2
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts +31 -5
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +75 -14
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts +14 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +21 -5
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/CacheStatsResolver.d.ts +27 -0
- package/dist/resolvers/CacheStatsResolver.d.ts.map +1 -1
- package/dist/resolvers/CacheStatsResolver.js +113 -2
- package/dist/resolvers/CacheStatsResolver.js.map +1 -1
- package/dist/resolvers/ClientToolRequestResolver.d.ts +4 -2
- package/dist/resolvers/ClientToolRequestResolver.d.ts.map +1 -1
- package/dist/resolvers/ClientToolRequestResolver.js +28 -10
- package/dist/resolvers/ClientToolRequestResolver.js.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts +12 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +83 -2
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -1
- package/dist/resolvers/HumanHandoffResolver.js +7 -1
- package/dist/resolvers/HumanHandoffResolver.js.map +1 -1
- package/dist/resolvers/MCPResolver.d.ts.map +1 -1
- package/dist/resolvers/MCPResolver.js +9 -4
- package/dist/resolvers/MCPResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts +54 -2
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +155 -10
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +155 -25
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +394 -90
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RealtimeSessionEventsResolver.d.ts +61 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.js +130 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.js.map +1 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts +85 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.js +188 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.js.map +1 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts +2 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +5 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +10 -0
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +68 -11
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +12 -11
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncDataResolver.js +6 -2
- package/dist/resolvers/SyncDataResolver.js.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.d.ts +2 -0
- package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.js +23 -6
- package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
- package/dist/resolvers/UserAvatarResolver.d.ts +16 -0
- package/dist/resolvers/UserAvatarResolver.d.ts.map +1 -0
- package/dist/resolvers/UserAvatarResolver.js +124 -0
- package/dist/resolvers/UserAvatarResolver.js.map +1 -0
- package/dist/resolvers/UserResolver.d.ts.map +1 -1
- package/dist/resolvers/UserResolver.js +2 -17
- package/dist/resolvers/UserResolver.js.map +1 -1
- package/dist/resolvers/agentVisionConsentAudit.d.ts +37 -0
- package/dist/resolvers/agentVisionConsentAudit.d.ts.map +1 -0
- package/dist/resolvers/agentVisionConsentAudit.js +63 -0
- package/dist/resolvers/agentVisionConsentAudit.js.map +1 -0
- package/dist/resolvers/avatarInputValidation.d.ts +40 -0
- package/dist/resolvers/avatarInputValidation.d.ts.map +1 -0
- package/dist/resolvers/avatarInputValidation.js +97 -0
- package/dist/resolvers/avatarInputValidation.js.map +1 -0
- package/dist/resolvers/currentUserRoles.d.ts +29 -0
- package/dist/resolvers/currentUserRoles.d.ts.map +1 -1
- package/dist/resolvers/currentUserRoles.js +30 -0
- package/dist/resolvers/currentUserRoles.js.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.d.ts +6 -0
- package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.js +21 -0
- package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +4 -3
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +13 -7
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/sharedCache.d.ts +105 -0
- package/dist/sharedCache.d.ts.map +1 -0
- package/dist/sharedCache.js +276 -0
- package/dist/sharedCache.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +97 -95
- package/src/__tests__/ActionResolver.authorization.test.ts +353 -0
- package/src/__tests__/AdhocQueryResolver.sqlAuthorizer.test.ts +104 -0
- package/src/__tests__/BridgeHostInstance.test.ts +232 -0
- package/src/__tests__/ClientToolRequestResolver.test.ts +116 -0
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +285 -0
- package/src/__tests__/HostInstance.test.ts +84 -0
- package/src/__tests__/HumanHandoffResolver.test.ts +17 -0
- package/src/__tests__/MeetingResolver.test.ts +75 -1
- package/src/__tests__/RealtimeBridgeResolver.test.ts +415 -6
- package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +383 -0
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +682 -58
- package/src/__tests__/RealtimeProxyServer.test.ts +16 -1
- package/src/__tests__/RealtimeRelay.test.ts +876 -0
- package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +129 -0
- package/src/__tests__/ResolverBase.aggregateScreen.test.ts +174 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +6 -3
- package/src/__tests__/ReturningVisitorRecap.test.ts +115 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +169 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +4 -4
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +166 -0
- package/src/__tests__/RunTemplateResolver.test.ts +132 -0
- package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +63 -0
- package/src/__tests__/SessionJanitor.test.ts +845 -18
- package/src/__tests__/SessionManager.test.ts +213 -17
- package/src/__tests__/SessionRunIDVerifier.test.ts +251 -0
- package/src/__tests__/SyncRolesUsersResolver.userRoles.test.ts +97 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +50 -3
- package/src/__tests__/UserAvatarResolver.test.ts +255 -0
- package/src/__tests__/UserResolver.currentUser.test.ts +16 -1
- package/src/__tests__/actionAuthorization.test.ts +130 -0
- package/src/__tests__/agentVisionConsentAudit.test.ts +118 -0
- package/src/__tests__/avatarInputValidation.test.ts +160 -0
- package/src/__tests__/isolatedProviderTransactions.test.ts +169 -0
- package/src/__tests__/magicLinkService.consume.test.ts +6 -0
- package/src/__tests__/magicLinkService.provisionLog.test.ts +5 -0
- package/src/__tests__/newUsers.template.test.ts +151 -0
- package/src/__tests__/newUsers.test.ts +74 -5
- package/src/__tests__/orphanedConversationDetailReconciler.test.ts +17 -2
- package/src/__tests__/realtimeSessions/RealtimeSessionEventService.test.ts +201 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +215 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionVerificationResolver.test.ts +102 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionVerificationService.test.ts +349 -0
- package/src/__tests__/realtimeSessions/RealtimeVerifyRouter.test.ts +131 -0
- package/src/__tests__/realtimeSessions/SlidingWindowRateLimiter.test.ts +56 -0
- package/src/__tests__/realtimeSessions/helpers.ts +4 -0
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +129 -0
- package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +124 -0
- package/src/__tests__/realtimeSessions/verificationCore.test.ts +355 -0
- package/src/__tests__/realtimeSessions/verificationPages.test.ts +84 -0
- package/src/__tests__/realtimeSessions/verificationPolicy.test.ts +226 -0
- package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +633 -0
- package/src/__tests__/resolverBase.fls.test.ts +87 -4
- package/src/__tests__/serverExtensionReservedRoots.test.ts +2 -0
- package/src/__tests__/sharedCache.test.ts +310 -0
- package/src/__tests__/widgetGuestElevation.test.ts +44 -0
- package/src/agentSessions/BridgeHostInstance.ts +32 -0
- package/src/agentSessions/HostInstance.ts +124 -28
- package/src/agentSessions/ReturningVisitorRecap.ts +7 -3
- package/src/agentSessions/SessionJanitor.ts +543 -57
- package/src/agentSessions/SessionManager.ts +125 -25
- package/src/agentSessions/SessionRunIDVerifier.ts +171 -0
- package/src/agentSessions/index.ts +10 -3
- package/src/auth/actionAuthorization.ts +116 -0
- package/src/auth/index.ts +3 -8
- package/src/auth/magicLink/MagicLinkService.ts +6 -3
- package/src/auth/newUsers.ts +99 -11
- package/src/config.ts +125 -0
- package/src/context.ts +15 -10
- package/src/generated/graphql-schemas/__mj.ts +496 -1
- package/src/generic/OrphanedConversationDetailReconciler.ts +3 -3
- package/src/generic/ResolverBase.ts +39 -9
- package/src/generic/RunViewResolver.ts +4 -0
- package/src/index.ts +132 -143
- package/src/isolatedProvider.ts +22 -0
- package/src/realtimeProxy/RealtimeProxyServer.ts +148 -16
- package/src/realtimeProxy/RealtimeRelayTunnel.ts +645 -0
- package/src/realtimeSessions/README.md +179 -0
- package/src/realtimeSessions/RealtimeSessionEventService.ts +274 -0
- package/src/realtimeSessions/RealtimeSessionVerificationService.ts +167 -0
- package/src/realtimeSessions/RealtimeVerifyRouter.ts +108 -0
- package/src/realtimeSessions/SessionVerificationPortsBase.ts +189 -0
- package/src/realtimeSessions/SlidingWindowRateLimiter.ts +92 -0
- package/src/realtimeSessions/index.ts +16 -0
- package/src/realtimeSessions/sessionAccess.ts +141 -0
- package/src/realtimeSessions/sessionConfigStamps.ts +123 -0
- package/src/realtimeSessions/subscriptionAuthorization.ts +116 -0
- package/src/realtimeSessions/verificationCore.ts +502 -0
- package/src/realtimeSessions/verificationPages.ts +149 -0
- package/src/realtimeSessions/verificationPolicy.ts +321 -0
- package/src/realtimeSessions/verificationWorkflow.ts +702 -0
- package/src/realtimeWidget/widgetGuestElevation.ts +34 -2
- package/src/resolvers/ActionResolver.ts +84 -21
- package/src/resolvers/AdhocQueryResolver.ts +24 -6
- package/src/resolvers/CacheStatsResolver.ts +89 -2
- package/src/resolvers/ClientToolRequestResolver.ts +25 -7
- package/src/resolvers/FetchEntityVectorsResolver.ts +97 -2
- package/src/resolvers/HumanHandoffResolver.ts +8 -0
- package/src/resolvers/MCPResolver.ts +8 -5
- package/src/resolvers/RealtimeBridgeResolver.ts +163 -11
- package/src/resolvers/RealtimeClientSessionResolver.ts +501 -96
- package/src/resolvers/RealtimeSessionEventsResolver.ts +133 -0
- package/src/resolvers/RealtimeSessionVerificationResolver.ts +174 -0
- package/src/resolvers/RemoteBrowserActionResolver.ts +11 -1
- package/src/resolvers/RunAIAgentResolver.ts +78 -11
- package/src/resolvers/RunTemplateResolver.ts +13 -12
- package/src/resolvers/SyncDataResolver.ts +6 -2
- package/src/resolvers/SyncRolesUsersResolver.ts +24 -6
- package/src/resolvers/UserAvatarResolver.ts +111 -0
- package/src/resolvers/UserResolver.ts +2 -16
- package/src/resolvers/agentVisionConsentAudit.ts +82 -0
- package/src/resolvers/avatarInputValidation.ts +123 -0
- package/src/resolvers/currentUserRoles.ts +32 -1
- package/src/resolvers/meetings/meetingMutations.ts +21 -0
- package/src/services/TaskGraphContinuationDeliverer.ts +13 -7
- package/src/sharedCache.ts +302 -0
- package/src/types.ts +7 -0
|
@@ -11,11 +11,13 @@ vi.mock('@memberjunction/ai-engine-base', () => ({
|
|
|
11
11
|
// --- Mock the client-direct service so CloseSession's run finalization is observable (no DB),
|
|
12
12
|
// plus the channel-plugin host so the start/close lifecycle notifications are observable. ---
|
|
13
13
|
const finalizeCoAgentRunMock = vi.fn(async () => undefined);
|
|
14
|
+
const keepCoAgentRunAliveMock = vi.fn();
|
|
14
15
|
const hostSessionStartedMock = vi.fn(async () => undefined);
|
|
15
16
|
const hostSessionClosedMock = vi.fn(async () => undefined);
|
|
16
17
|
vi.mock('@memberjunction/ai-agents', () => ({
|
|
17
18
|
RealtimeClientSessionService: class {
|
|
18
19
|
FinalizeCoAgentRun = finalizeCoAgentRunMock;
|
|
20
|
+
KeepCoAgentRunAlive = keepCoAgentRunAliveMock;
|
|
19
21
|
},
|
|
20
22
|
RealtimeChannelServerHost: {
|
|
21
23
|
get Instance() {
|
|
@@ -27,15 +29,18 @@ vi.mock('@memberjunction/ai-agents', () => ({
|
|
|
27
29
|
},
|
|
28
30
|
}));
|
|
29
31
|
|
|
30
|
-
// --- Mock RunView.FromMetadataProvider so channel sweeps are controllable,
|
|
31
|
-
// rest of @memberjunction/core intact. ---
|
|
32
|
+
// --- Mock RunView.FromMetadataProvider so channel sweeps and run-id lookups are controllable,
|
|
33
|
+
// while leaving the rest of @memberjunction/core intact. RunViews runs each view through runViewMock. ---
|
|
32
34
|
const runViewMock = vi.fn();
|
|
33
35
|
vi.mock('@memberjunction/core', async (importOriginal) => {
|
|
34
36
|
const actual = await importOriginal<typeof import('@memberjunction/core')>();
|
|
35
37
|
return {
|
|
36
38
|
...actual,
|
|
37
39
|
RunView: {
|
|
38
|
-
FromMetadataProvider: () => ({
|
|
40
|
+
FromMetadataProvider: () => ({
|
|
41
|
+
RunView: runViewMock,
|
|
42
|
+
RunViews: (paramsList: unknown[]) => Promise.all(paramsList.map((params) => runViewMock(params))),
|
|
43
|
+
}),
|
|
39
44
|
},
|
|
40
45
|
};
|
|
41
46
|
});
|
|
@@ -100,6 +105,27 @@ function makeSessionEntity(overrides: Partial<FakeEntity> = {}): FakeEntity {
|
|
|
100
105
|
};
|
|
101
106
|
}
|
|
102
107
|
|
|
108
|
+
/** Co-agent observability ids as a voice session's Config holds them. */
|
|
109
|
+
const CO_RUN_ID = 'a5a5a5a5-0000-4000-8000-0000000000c0';
|
|
110
|
+
const PROMPT_RUN_ID = 'a5a5a5a5-0000-4000-8000-0000000000b0';
|
|
111
|
+
const RUN_STEP_ID = 'a5a5a5a5-0000-4000-8000-0000000000a0';
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Makes the given runs records of the session for the run-id lookups: `runIDs` carry the session's
|
|
115
|
+
* id, and `promptRunID` (when given) is the TargetLogID of the co-agent run's Prompt step.
|
|
116
|
+
*/
|
|
117
|
+
function mockSessionRunRecords(runIDs: string[], promptRunID?: string): void {
|
|
118
|
+
runViewMock.mockImplementation(async (params: { EntityName?: string }) => {
|
|
119
|
+
if (params.EntityName === 'MJ: AI Agent Runs') {
|
|
120
|
+
return { Success: true, Results: runIDs.map((ID) => ({ ID })) };
|
|
121
|
+
}
|
|
122
|
+
if (params.EntityName === 'MJ: AI Agent Run Steps') {
|
|
123
|
+
return { Success: true, Results: promptRunID ? [{ ID: RUN_STEP_ID, StepType: 'Prompt', TargetLogID: promptRunID }] : [] };
|
|
124
|
+
}
|
|
125
|
+
return { Success: true, Results: [] };
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
103
129
|
beforeEach(() => {
|
|
104
130
|
hasPermissionMock.mockReset();
|
|
105
131
|
finalizeCoAgentRunMock.mockClear();
|
|
@@ -228,8 +254,9 @@ describe('SessionManager.CloseSession', () => {
|
|
|
228
254
|
const session = makeSessionEntity({
|
|
229
255
|
ID: 'session-voice',
|
|
230
256
|
Status: 'Active',
|
|
231
|
-
Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID:
|
|
257
|
+
Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: CO_RUN_ID, promptRunID: PROMPT_RUN_ID }),
|
|
232
258
|
});
|
|
259
|
+
mockSessionRunRecords([CO_RUN_ID], PROMPT_RUN_ID);
|
|
233
260
|
const { provider } = makeProvider(() => session);
|
|
234
261
|
const mgr = new SessionManager();
|
|
235
262
|
const user = makeUser();
|
|
@@ -238,7 +265,7 @@ describe('SessionManager.CloseSession', () => {
|
|
|
238
265
|
|
|
239
266
|
expect(ok).toBe(true);
|
|
240
267
|
expect(finalizeCoAgentRunMock).toHaveBeenCalledTimes(1);
|
|
241
|
-
expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(
|
|
268
|
+
expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(CO_RUN_ID, PROMPT_RUN_ID, user, provider, true, null);
|
|
242
269
|
});
|
|
243
270
|
|
|
244
271
|
it('threads the co-agent run-step id into finalize when the session config carries it', async () => {
|
|
@@ -247,11 +274,12 @@ describe('SessionManager.CloseSession', () => {
|
|
|
247
274
|
Status: 'Active',
|
|
248
275
|
Config_: JSON.stringify({
|
|
249
276
|
targetAgentID: 't1',
|
|
250
|
-
coAgentRunID:
|
|
251
|
-
promptRunID:
|
|
252
|
-
coAgentRunStepID:
|
|
277
|
+
coAgentRunID: CO_RUN_ID,
|
|
278
|
+
promptRunID: PROMPT_RUN_ID,
|
|
279
|
+
coAgentRunStepID: RUN_STEP_ID,
|
|
253
280
|
}),
|
|
254
281
|
});
|
|
282
|
+
mockSessionRunRecords([CO_RUN_ID], PROMPT_RUN_ID);
|
|
255
283
|
const { provider } = makeProvider(() => session);
|
|
256
284
|
const mgr = new SessionManager();
|
|
257
285
|
const user = makeUser();
|
|
@@ -259,7 +287,7 @@ describe('SessionManager.CloseSession', () => {
|
|
|
259
287
|
const ok = await mgr.CloseSession('session-voice-step', user, provider);
|
|
260
288
|
|
|
261
289
|
expect(ok).toBe(true);
|
|
262
|
-
expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(
|
|
290
|
+
expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(CO_RUN_ID, PROMPT_RUN_ID, user, provider, true, RUN_STEP_ID);
|
|
263
291
|
});
|
|
264
292
|
|
|
265
293
|
it('finalizes as the SYSTEM user when a scoped anonymous owner closes their session (issue #3371)', async () => {
|
|
@@ -274,8 +302,9 @@ describe('SessionManager.CloseSession', () => {
|
|
|
274
302
|
const session = makeSessionEntity({
|
|
275
303
|
ID: 'session-anon',
|
|
276
304
|
Status: 'Active',
|
|
277
|
-
Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID:
|
|
305
|
+
Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: CO_RUN_ID, promptRunID: PROMPT_RUN_ID }),
|
|
278
306
|
});
|
|
307
|
+
mockSessionRunRecords([CO_RUN_ID], PROMPT_RUN_ID);
|
|
279
308
|
const { provider } = makeProvider(() => session);
|
|
280
309
|
const mgr = new SessionManager();
|
|
281
310
|
|
|
@@ -284,7 +313,7 @@ describe('SessionManager.CloseSession', () => {
|
|
|
284
313
|
expect(ok).toBe(true);
|
|
285
314
|
// The session-close writes themselves stay on the caller — only finalize elevates.
|
|
286
315
|
expect(session.Status).toBe('Closed');
|
|
287
|
-
expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(
|
|
316
|
+
expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(CO_RUN_ID, PROMPT_RUN_ID, systemUser, provider, true, null);
|
|
288
317
|
});
|
|
289
318
|
|
|
290
319
|
it('FAILS CLOSED — finalizes as the anonymous caller when no system user is available', async () => {
|
|
@@ -297,14 +326,15 @@ describe('SessionManager.CloseSession', () => {
|
|
|
297
326
|
const session = makeSessionEntity({
|
|
298
327
|
ID: 'session-anon-2',
|
|
299
328
|
Status: 'Active',
|
|
300
|
-
Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID:
|
|
329
|
+
Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: CO_RUN_ID, promptRunID: null }),
|
|
301
330
|
});
|
|
331
|
+
mockSessionRunRecords([CO_RUN_ID]);
|
|
302
332
|
const { provider } = makeProvider(() => session);
|
|
303
333
|
const mgr = new SessionManager();
|
|
304
334
|
|
|
305
335
|
await mgr.CloseSession('session-anon-2', anonUser, provider);
|
|
306
336
|
|
|
307
|
-
expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(
|
|
337
|
+
expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(CO_RUN_ID, null, anonUser, provider, true, null);
|
|
308
338
|
});
|
|
309
339
|
|
|
310
340
|
it('does not finalize when the session config has no run ids (target only)', async () => {
|
|
@@ -334,8 +364,9 @@ describe('SessionManager.CloseSession', () => {
|
|
|
334
364
|
const session = makeSessionEntity({
|
|
335
365
|
ID: 'session-injected',
|
|
336
366
|
Status: 'Active',
|
|
337
|
-
Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID:
|
|
367
|
+
Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: CO_RUN_ID, promptRunID: PROMPT_RUN_ID }),
|
|
338
368
|
});
|
|
369
|
+
mockSessionRunRecords([CO_RUN_ID], PROMPT_RUN_ID);
|
|
339
370
|
const { provider } = makeProvider(() => session);
|
|
340
371
|
const mgr = new SessionManager(injectedService);
|
|
341
372
|
const user = makeUser();
|
|
@@ -345,7 +376,7 @@ describe('SessionManager.CloseSession', () => {
|
|
|
345
376
|
expect(ok).toBe(true);
|
|
346
377
|
// The finalize call landed on the INJECTED instance, never the module-default one.
|
|
347
378
|
expect(injectedFinalizeMock).toHaveBeenCalledTimes(1);
|
|
348
|
-
expect(injectedFinalizeMock).toHaveBeenCalledWith(
|
|
379
|
+
expect(injectedFinalizeMock).toHaveBeenCalledWith(CO_RUN_ID, PROMPT_RUN_ID, user, provider, true, null);
|
|
349
380
|
expect(finalizeCoAgentRunMock).not.toHaveBeenCalled();
|
|
350
381
|
});
|
|
351
382
|
|
|
@@ -353,8 +384,9 @@ describe('SessionManager.CloseSession', () => {
|
|
|
353
384
|
const session = makeSessionEntity({
|
|
354
385
|
ID: 'session-default-svc',
|
|
355
386
|
Status: 'Active',
|
|
356
|
-
Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID:
|
|
387
|
+
Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: CO_RUN_ID, promptRunID: PROMPT_RUN_ID }),
|
|
357
388
|
});
|
|
389
|
+
mockSessionRunRecords([CO_RUN_ID], PROMPT_RUN_ID);
|
|
358
390
|
const { provider } = makeProvider(() => session);
|
|
359
391
|
const mgr = new SessionManager(); // no injection — e.g. SessionJanitor, telephony services
|
|
360
392
|
const user = makeUser();
|
|
@@ -362,7 +394,7 @@ describe('SessionManager.CloseSession', () => {
|
|
|
362
394
|
const ok = await mgr.CloseSession('session-default-svc', user, provider);
|
|
363
395
|
|
|
364
396
|
expect(ok).toBe(true);
|
|
365
|
-
expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(
|
|
397
|
+
expect(finalizeCoAgentRunMock).toHaveBeenCalledWith(CO_RUN_ID, PROMPT_RUN_ID, user, provider, true, null);
|
|
366
398
|
});
|
|
367
399
|
|
|
368
400
|
it('stamps CloseReason = Explicit by default (untouched call sites get the right reason)', async () => {
|
|
@@ -425,6 +457,79 @@ describe('SessionManager.CloseSession', () => {
|
|
|
425
457
|
});
|
|
426
458
|
});
|
|
427
459
|
|
|
460
|
+
describe('SessionManager.CloseSessionForEndedBridge (a bridged agent left its room)', () => {
|
|
461
|
+
it.each([
|
|
462
|
+
['Explicit', 'Explicit'],
|
|
463
|
+
['HostEnded', 'Explicit'],
|
|
464
|
+
['Error', 'Error'],
|
|
465
|
+
['Janitor', 'Janitor'],
|
|
466
|
+
['Shutdown', 'Shutdown'],
|
|
467
|
+
] as const)('closes the session when its bridge ended with %s, stamping %s', async (bridgeReason, sessionReason) => {
|
|
468
|
+
const session = makeSessionEntity({ ID: 'session-bridged', Status: 'Active' });
|
|
469
|
+
const { provider } = makeProvider(() => session);
|
|
470
|
+
const mgr = new SessionManager();
|
|
471
|
+
|
|
472
|
+
const ok = await mgr.CloseSessionForEndedBridge('session-bridged', bridgeReason, makeUser(), provider);
|
|
473
|
+
|
|
474
|
+
expect(ok).toBe(true);
|
|
475
|
+
expect(session.Status).toBe('Closed');
|
|
476
|
+
expect(session.ClosedAt).toBeInstanceOf(Date);
|
|
477
|
+
expect(session.CloseReason).toBe(sessionReason);
|
|
478
|
+
// The same close the janitor runs: the channel plugins hear about it too.
|
|
479
|
+
expect(hostSessionClosedMock).toHaveBeenCalledWith('session-bridged', sessionReason);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it('keeps the reason of a session something else closed first (the janitor, the user)', async () => {
|
|
483
|
+
const session = makeSessionEntity({ ID: 'session-bridged', Status: 'Closed', CloseReason: 'Janitor' });
|
|
484
|
+
const { provider } = makeProvider(() => session);
|
|
485
|
+
const mgr = new SessionManager();
|
|
486
|
+
|
|
487
|
+
const ok = await mgr.CloseSessionForEndedBridge('session-bridged', 'Explicit', makeUser(), provider);
|
|
488
|
+
|
|
489
|
+
expect(ok).toBe(true);
|
|
490
|
+
expect(session.CloseReason).toBe('Janitor');
|
|
491
|
+
expect(session.Save).not.toHaveBeenCalled();
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it('never throws: a close that fails is reported as not closed, leaving the session to the janitor', async () => {
|
|
495
|
+
const provider = {
|
|
496
|
+
GetEntityObject: vi.fn(async () => {
|
|
497
|
+
throw new Error('database unreachable');
|
|
498
|
+
}),
|
|
499
|
+
} as unknown as IMetadataProvider;
|
|
500
|
+
const mgr = new SessionManager();
|
|
501
|
+
|
|
502
|
+
await expect(mgr.CloseSessionForEndedBridge('session-bridged', 'Explicit', makeUser(), provider)).resolves.toBe(false);
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
describe('SessionManager.CloseSessionForFailedStart (an agent that never joined its room)', () => {
|
|
507
|
+
it('closes the session as Error, through the close the janitor runs', async () => {
|
|
508
|
+
const session = makeSessionEntity({ ID: 'session-unstarted', Status: 'Active' });
|
|
509
|
+
const { provider } = makeProvider(() => session);
|
|
510
|
+
const mgr = new SessionManager();
|
|
511
|
+
|
|
512
|
+
const ok = await mgr.CloseSessionForFailedStart('session-unstarted', makeUser(), provider);
|
|
513
|
+
|
|
514
|
+
expect(ok).toBe(true);
|
|
515
|
+
expect(session.Status).toBe('Closed');
|
|
516
|
+
expect(session.ClosedAt).toBeInstanceOf(Date);
|
|
517
|
+
expect(session.CloseReason).toBe('Error');
|
|
518
|
+
expect(hostSessionClosedMock).toHaveBeenCalledWith('session-unstarted', 'Error');
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
it('never throws: a close that fails is reported as not closed, leaving the session to the janitor', async () => {
|
|
522
|
+
const provider = {
|
|
523
|
+
GetEntityObject: vi.fn(async () => {
|
|
524
|
+
throw new Error('database unreachable');
|
|
525
|
+
}),
|
|
526
|
+
} as unknown as IMetadataProvider;
|
|
527
|
+
const mgr = new SessionManager();
|
|
528
|
+
|
|
529
|
+
await expect(mgr.CloseSessionForFailedStart('session-unstarted', makeUser(), provider)).resolves.toBe(false);
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
|
|
428
533
|
describe('SessionManager.Heartbeat', () => {
|
|
429
534
|
it('coalesces writes within the min interval (one write, not two)', async () => {
|
|
430
535
|
const session = makeSessionEntity({ ID: 'session-1', Status: 'Active' });
|
|
@@ -461,6 +566,36 @@ describe('SessionManager.Heartbeat', () => {
|
|
|
461
566
|
});
|
|
462
567
|
});
|
|
463
568
|
|
|
569
|
+
describe('SessionManager.Heartbeat — the session\'s co-agent run stays alive', () => {
|
|
570
|
+
beforeEach(() => {
|
|
571
|
+
keepCoAgentRunAliveMock.mockClear();
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it('keeps the co-agent run alive for the run watchdog on each persisted heartbeat', async () => {
|
|
575
|
+
const session = makeSessionEntity({ ID: 'session-1', Status: 'Active', Config_: JSON.stringify({ coAgentRunID: CO_RUN_ID, promptRunID: PROMPT_RUN_ID }) });
|
|
576
|
+
mockSessionRunRecords([CO_RUN_ID], PROMPT_RUN_ID);
|
|
577
|
+
const { provider } = makeProvider(() => session);
|
|
578
|
+
const mgr = new SessionManager();
|
|
579
|
+
|
|
580
|
+
await mgr.Heartbeat('session-1', makeUser(), provider);
|
|
581
|
+
await mgr.Heartbeat('session-1', makeUser(), provider); // coalesced: no write, no re-registration
|
|
582
|
+
|
|
583
|
+
expect(keepCoAgentRunAliveMock).toHaveBeenCalledOnce();
|
|
584
|
+
expect(keepCoAgentRunAliveMock.mock.calls[0][0]).toBe(CO_RUN_ID);
|
|
585
|
+
expect(keepCoAgentRunAliveMock.mock.calls[0][1]).toBe(provider);
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it('does nothing for a closed session, and passes no run for a session without one', async () => {
|
|
589
|
+
const closed = makeSessionEntity({ ID: 'session-closed', Status: 'Closed', Config_: JSON.stringify({ coAgentRunID: 'co-run-1' }) });
|
|
590
|
+
await new SessionManager().Heartbeat('session-closed', makeUser(), makeProvider(() => closed).provider);
|
|
591
|
+
expect(keepCoAgentRunAliveMock).not.toHaveBeenCalled();
|
|
592
|
+
|
|
593
|
+
const workspace = makeSessionEntity({ ID: 'session-2', Status: 'Active', Config_: null });
|
|
594
|
+
await new SessionManager().Heartbeat('session-2', makeUser(), makeProvider(() => workspace).provider);
|
|
595
|
+
expect(keepCoAgentRunAliveMock.mock.calls[0][0]).toBeUndefined();
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
|
|
464
599
|
describe('SessionManager.Heartbeat — bounded heartbeatLastWrite (Round 11 memory-leak fix)', () => {
|
|
465
600
|
// Regression coverage: heartbeatLastWrite used to be a plain, unbounded `Map`. SessionManager
|
|
466
601
|
// is constructed fresh by every resolver that needs one (AgentSessionResolver,
|
|
@@ -626,3 +761,64 @@ describe('SessionManager — server-side channel plugin lifecycle notifications'
|
|
|
626
761
|
expect(session.Status).toBe('Closed');
|
|
627
762
|
});
|
|
628
763
|
});
|
|
764
|
+
|
|
765
|
+
// A session's Config is writable by its owner (generic CRUD on MJ: AI Agent Sessions), so the run
|
|
766
|
+
// ids in it are acted on only when they are UUIDs of records that belong to the session.
|
|
767
|
+
describe('SessionManager — run ids in the owner-writable session Config', () => {
|
|
768
|
+
const SESSION_ID = 'c3c3c3c3-1000-4000-8000-000000000001';
|
|
769
|
+
const FOREIGN_RUN_ID = 'c3c3c3c3-1000-4000-8000-0000000000f1';
|
|
770
|
+
const FOREIGN_PROMPT_RUN_ID = 'c3c3c3c3-1000-4000-8000-0000000000f2';
|
|
771
|
+
const FOREIGN_STEP_ID = 'c3c3c3c3-1000-4000-8000-0000000000f3';
|
|
772
|
+
/** Harmless text that is not a UUID and would change the meaning of a quoted filter. */
|
|
773
|
+
const NOT_A_UUID = "x' OR '1'='1";
|
|
774
|
+
|
|
775
|
+
function issuedFilters(): string[] {
|
|
776
|
+
return runViewMock.mock.calls.map(([params]) => (params as { ExtraFilter?: string }).ExtraFilter ?? '');
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
beforeEach(() => {
|
|
780
|
+
keepCoAgentRunAliveMock.mockClear();
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
it('CloseSession does not finalize run ids that are not records of the session', async () => {
|
|
784
|
+
const session = makeSessionEntity({
|
|
785
|
+
ID: SESSION_ID,
|
|
786
|
+
Status: 'Active',
|
|
787
|
+
Config_: JSON.stringify({
|
|
788
|
+
targetAgentID: 't1',
|
|
789
|
+
coAgentRunID: FOREIGN_RUN_ID,
|
|
790
|
+
promptRunID: FOREIGN_PROMPT_RUN_ID,
|
|
791
|
+
coAgentRunStepID: FOREIGN_STEP_ID,
|
|
792
|
+
}),
|
|
793
|
+
});
|
|
794
|
+
const { provider } = makeProvider(() => session);
|
|
795
|
+
|
|
796
|
+
await new SessionManager().CloseSession(SESSION_ID, makeUser(), provider);
|
|
797
|
+
|
|
798
|
+
expect(finalizeCoAgentRunMock).not.toHaveBeenCalled();
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
it('CloseSession does not finalize run ids that are not UUIDs, and never puts them in a filter', async () => {
|
|
802
|
+
const session = makeSessionEntity({
|
|
803
|
+
ID: SESSION_ID,
|
|
804
|
+
Status: 'Active',
|
|
805
|
+
Config_: JSON.stringify({ targetAgentID: 't1', coAgentRunID: NOT_A_UUID, promptRunID: NOT_A_UUID }),
|
|
806
|
+
});
|
|
807
|
+
const { provider } = makeProvider(() => session);
|
|
808
|
+
|
|
809
|
+
await new SessionManager().CloseSession(SESSION_ID, makeUser(), provider);
|
|
810
|
+
|
|
811
|
+
expect(finalizeCoAgentRunMock).not.toHaveBeenCalled();
|
|
812
|
+
expect(issuedFilters().filter((f) => f.includes(NOT_A_UUID))).toEqual([]);
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
it('Heartbeat does not keep alive a run that is not a record of the session', async () => {
|
|
816
|
+
const session = makeSessionEntity({ ID: SESSION_ID, Status: 'Active', Config_: JSON.stringify({ coAgentRunID: FOREIGN_RUN_ID }) });
|
|
817
|
+
const { provider } = makeProvider(() => session);
|
|
818
|
+
|
|
819
|
+
await new SessionManager().Heartbeat(SESSION_ID, makeUser(), provider);
|
|
820
|
+
|
|
821
|
+
expect(keepCoAgentRunAliveMock).toHaveBeenCalledOnce();
|
|
822
|
+
expect(keepCoAgentRunAliveMock.mock.calls[0][0]).toBeUndefined();
|
|
823
|
+
});
|
|
824
|
+
});
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import type { IMetadataProvider, RunViewParams, UserInfo } from '@memberjunction/core';
|
|
3
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
4
|
+
import { SessionRunIDVerifier } from '../agentSessions/SessionRunIDVerifier.js';
|
|
5
|
+
|
|
6
|
+
const SESSION_ID = 'd3d3d3d3-0000-4000-8000-000000000001';
|
|
7
|
+
const CO_RUN_ID = 'd3d3d3d3-0000-4000-8000-000000000002';
|
|
8
|
+
const PROMPT_RUN_ID = 'd3d3d3d3-0000-4000-8000-000000000003';
|
|
9
|
+
const STEP_ID = 'd3d3d3d3-0000-4000-8000-000000000004';
|
|
10
|
+
const PAUSED_RUN_ID = 'd3d3d3d3-0000-4000-8000-000000000005';
|
|
11
|
+
const USER = { ID: 'd3d3d3d3-0000-4000-8000-0000000000aa' } as unknown as UserInfo;
|
|
12
|
+
|
|
13
|
+
/** Rows each lookup returns, keyed by entity name. */
|
|
14
|
+
type Rows = Record<string, Array<Record<string, unknown>>>;
|
|
15
|
+
|
|
16
|
+
function makeProvider(rows: Rows, success = true): { provider: IMetadataProvider; runViews: ReturnType<typeof vi.fn> } {
|
|
17
|
+
const runViews = vi.fn(async (paramsList: RunViewParams[]) =>
|
|
18
|
+
paramsList.map((params) => ({
|
|
19
|
+
Success: success,
|
|
20
|
+
Results: success ? rows[params.EntityName ?? ''] ?? [] : [],
|
|
21
|
+
ErrorMessage: success ? '' : 'lookup failed',
|
|
22
|
+
})),
|
|
23
|
+
);
|
|
24
|
+
return { provider: { RunViews: runViews } as unknown as IMetadataProvider, runViews };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function issuedViews(runViews: ReturnType<typeof vi.fn>): RunViewParams[] {
|
|
28
|
+
return runViews.mock.calls.flatMap(([paramsList]) => paramsList as RunViewParams[]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
UserCache.Instance.SetUsers([]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('SessionRunIDVerifier.Verify', () => {
|
|
40
|
+
it('keeps nothing, and runs no lookup, when no claim is a UUID', async () => {
|
|
41
|
+
const { provider, runViews } = makeProvider({});
|
|
42
|
+
|
|
43
|
+
const result = await new SessionRunIDVerifier().Verify(
|
|
44
|
+
SESSION_ID,
|
|
45
|
+
{ CoAgentRunID: "x' OR '1'='1", PromptRunID: 'prompt-run-1', PendingFeedbackRunID: '' },
|
|
46
|
+
USER,
|
|
47
|
+
provider,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(result).toEqual({});
|
|
51
|
+
expect(runViews).not.toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('looks up the claimed runs within the session and keeps only the ones it finds', async () => {
|
|
55
|
+
const { provider, runViews } = makeProvider({ 'MJ: AI Agent Runs': [{ ID: CO_RUN_ID }] });
|
|
56
|
+
|
|
57
|
+
const result = await new SessionRunIDVerifier().Verify(
|
|
58
|
+
SESSION_ID,
|
|
59
|
+
{ CoAgentRunID: CO_RUN_ID, PendingFeedbackRunID: PAUSED_RUN_ID },
|
|
60
|
+
USER,
|
|
61
|
+
provider,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
expect(result.CoAgentRunID).toBe(CO_RUN_ID);
|
|
65
|
+
expect(result.PendingFeedbackRunID).toBeUndefined();
|
|
66
|
+
const [runsView] = issuedViews(runViews);
|
|
67
|
+
expect(runsView.EntityName).toBe('MJ: AI Agent Runs');
|
|
68
|
+
expect(runsView.ExtraFilter).toBe(`AgentSessionID='${SESSION_ID}' AND ID IN ('${CO_RUN_ID}', '${PAUSED_RUN_ID}')`);
|
|
69
|
+
// No system user is cached here, so the lookup runs as the acting user.
|
|
70
|
+
expect(runViews.mock.calls[0][1]).toBe(USER);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('runs the lookup as the system user when one is available', async () => {
|
|
74
|
+
// Row-level security can hide a session's own runs from its owner: a web-widget guest's
|
|
75
|
+
// delegated runs carry no ConversationID, so the guest's run filter does not match them.
|
|
76
|
+
const systemUser = { ID: UserCache.Instance.SYSTEM_USER_ID, Email: 'system@example.com' } as unknown as UserInfo;
|
|
77
|
+
UserCache.Instance.SetUsers([systemUser]);
|
|
78
|
+
const { provider, runViews } = makeProvider({ 'MJ: AI Agent Runs': [{ ID: PAUSED_RUN_ID }] });
|
|
79
|
+
|
|
80
|
+
const result = await new SessionRunIDVerifier().Verify(SESSION_ID, { PendingFeedbackRunID: PAUSED_RUN_ID }, USER, provider);
|
|
81
|
+
|
|
82
|
+
expect(result.PendingFeedbackRunID).toBe(PAUSED_RUN_ID);
|
|
83
|
+
expect(runViews.mock.calls[0][1]).toBe(systemUser);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('keeps the prompt run and step that belong to the session\'s co-agent run', async () => {
|
|
87
|
+
const { provider, runViews } = makeProvider({
|
|
88
|
+
'MJ: AI Agent Runs': [{ ID: CO_RUN_ID }],
|
|
89
|
+
'MJ: AI Agent Run Steps': [{ ID: STEP_ID, StepType: 'Prompt', TargetLogID: PROMPT_RUN_ID }],
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const result = await new SessionRunIDVerifier().Verify(
|
|
93
|
+
SESSION_ID,
|
|
94
|
+
{ CoAgentRunID: CO_RUN_ID, PromptRunID: PROMPT_RUN_ID, CoAgentRunStepID: STEP_ID },
|
|
95
|
+
USER,
|
|
96
|
+
provider,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(result).toEqual({ CoAgentRunID: CO_RUN_ID, PromptRunID: PROMPT_RUN_ID, CoAgentRunStepID: STEP_ID, PendingFeedbackRunID: undefined });
|
|
100
|
+
const stepsView = issuedViews(runViews).find((view) => view.EntityName === 'MJ: AI Agent Run Steps');
|
|
101
|
+
expect(stepsView?.ExtraFilter).toBe(`AgentRunID='${CO_RUN_ID}' AND (ID='${STEP_ID}' OR TargetLogID='${PROMPT_RUN_ID}')`);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('drops a prompt run that is the target of a step other than a Prompt step', async () => {
|
|
105
|
+
const { provider } = makeProvider({
|
|
106
|
+
'MJ: AI Agent Runs': [{ ID: CO_RUN_ID }],
|
|
107
|
+
'MJ: AI Agent Run Steps': [{ ID: STEP_ID, StepType: 'Tool', TargetLogID: PROMPT_RUN_ID }],
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const result = await new SessionRunIDVerifier().Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID, PromptRunID: PROMPT_RUN_ID }, USER, provider);
|
|
111
|
+
|
|
112
|
+
expect(result.PromptRunID).toBeUndefined();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('drops the prompt run and step when the co-agent run is not the session\'s', async () => {
|
|
116
|
+
const { provider } = makeProvider({
|
|
117
|
+
'MJ: AI Agent Runs': [],
|
|
118
|
+
'MJ: AI Agent Run Steps': [{ ID: STEP_ID, StepType: 'Prompt', TargetLogID: PROMPT_RUN_ID }],
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const result = await new SessionRunIDVerifier().Verify(
|
|
122
|
+
SESSION_ID,
|
|
123
|
+
{ CoAgentRunID: CO_RUN_ID, PromptRunID: PROMPT_RUN_ID, CoAgentRunStepID: STEP_ID },
|
|
124
|
+
USER,
|
|
125
|
+
provider,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
expect(result).toEqual({ CoAgentRunID: undefined, PromptRunID: undefined, CoAgentRunStepID: undefined, PendingFeedbackRunID: undefined });
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('matches ids case-insensitively', async () => {
|
|
132
|
+
const { provider } = makeProvider({ 'MJ: AI Agent Runs': [{ ID: PAUSED_RUN_ID.toUpperCase() }] });
|
|
133
|
+
|
|
134
|
+
const result = await new SessionRunIDVerifier().Verify(SESSION_ID, { PendingFeedbackRunID: PAUSED_RUN_ID }, USER, provider);
|
|
135
|
+
|
|
136
|
+
expect(result.PendingFeedbackRunID).toBe(PAUSED_RUN_ID);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('looks up the same claims again on every call', async () => {
|
|
140
|
+
const { provider, runViews } = makeProvider({ 'MJ: AI Agent Runs': [{ ID: CO_RUN_ID }] });
|
|
141
|
+
const verifier = new SessionRunIDVerifier();
|
|
142
|
+
|
|
143
|
+
await verifier.Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, USER, provider);
|
|
144
|
+
const second = await verifier.Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, USER, provider);
|
|
145
|
+
|
|
146
|
+
expect(second.CoAgentRunID).toBe(CO_RUN_ID);
|
|
147
|
+
expect(runViews).toHaveBeenCalledTimes(2);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('reads the runs and steps from the database, not from a cached view', async () => {
|
|
151
|
+
const { provider, runViews } = makeProvider({ 'MJ: AI Agent Runs': [{ ID: CO_RUN_ID }] });
|
|
152
|
+
|
|
153
|
+
await new SessionRunIDVerifier().Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID, PromptRunID: PROMPT_RUN_ID }, USER, provider);
|
|
154
|
+
|
|
155
|
+
const views = issuedViews(runViews);
|
|
156
|
+
expect(views.map((view) => view.EntityName)).toEqual(['MJ: AI Agent Runs', 'MJ: AI Agent Run Steps']);
|
|
157
|
+
expect(views.every((view) => view.BypassCache === true)).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('keeps nothing when a lookup fails, and looks up again next time', async () => {
|
|
161
|
+
const { provider, runViews } = makeProvider({}, false);
|
|
162
|
+
const verifier = new SessionRunIDVerifier();
|
|
163
|
+
|
|
164
|
+
const first = await verifier.Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, USER, provider);
|
|
165
|
+
await verifier.Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, USER, provider);
|
|
166
|
+
|
|
167
|
+
expect(first).toEqual({});
|
|
168
|
+
expect(runViews).toHaveBeenCalledTimes(2);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('keeps nothing, without throwing, when the provider throws', async () => {
|
|
172
|
+
const provider = { RunViews: vi.fn(async () => { throw new Error('connection lost'); }) } as unknown as IMetadataProvider;
|
|
173
|
+
|
|
174
|
+
await expect(new SessionRunIDVerifier().Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, USER, provider)).resolves.toEqual({});
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* A run's `AgentSessionID` and a step's `AgentRunID` and `TargetLogID` can change after a lookup (the
|
|
180
|
+
* generated update mutations accept them), so each call must decide from what the rows say now.
|
|
181
|
+
*/
|
|
182
|
+
describe('SessionRunIDVerifier.Verify — relationships that change between calls', () => {
|
|
183
|
+
const CLAIMS = { CoAgentRunID: CO_RUN_ID, PromptRunID: PROMPT_RUN_ID, CoAgentRunStepID: STEP_ID, PendingFeedbackRunID: PAUSED_RUN_ID };
|
|
184
|
+
|
|
185
|
+
/** Both runs are records of the session, and the step is the co-agent run's Prompt step for the prompt run. */
|
|
186
|
+
function linkedRows(): Rows {
|
|
187
|
+
return {
|
|
188
|
+
'MJ: AI Agent Runs': [{ ID: CO_RUN_ID }, { ID: PAUSED_RUN_ID }],
|
|
189
|
+
'MJ: AI Agent Run Steps': [{ ID: STEP_ID, StepType: 'Prompt', TargetLogID: PROMPT_RUN_ID }],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
it('drops runs that stopped being records of the session since the last call', async () => {
|
|
194
|
+
const rows = linkedRows();
|
|
195
|
+
const { provider } = makeProvider(rows);
|
|
196
|
+
const verifier = new SessionRunIDVerifier();
|
|
197
|
+
const first = await verifier.Verify(SESSION_ID, CLAIMS, USER, provider);
|
|
198
|
+
|
|
199
|
+
// The runs were moved to another session, unlinked or deleted.
|
|
200
|
+
rows['MJ: AI Agent Runs'] = [];
|
|
201
|
+
const second = await verifier.Verify(SESSION_ID, CLAIMS, USER, provider);
|
|
202
|
+
|
|
203
|
+
expect(first).toEqual(CLAIMS);
|
|
204
|
+
expect(second).toEqual({ CoAgentRunID: undefined, PromptRunID: undefined, CoAgentRunStepID: undefined, PendingFeedbackRunID: undefined });
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('drops the prompt run and step once the step is no longer linked to the co-agent run', async () => {
|
|
208
|
+
const rows = linkedRows();
|
|
209
|
+
const { provider } = makeProvider(rows);
|
|
210
|
+
const verifier = new SessionRunIDVerifier();
|
|
211
|
+
const first = await verifier.Verify(SESSION_ID, CLAIMS, USER, provider);
|
|
212
|
+
|
|
213
|
+
rows['MJ: AI Agent Run Steps'] = [];
|
|
214
|
+
const second = await verifier.Verify(SESSION_ID, CLAIMS, USER, provider);
|
|
215
|
+
|
|
216
|
+
expect(first).toEqual(CLAIMS);
|
|
217
|
+
expect(second).toEqual({ ...CLAIMS, PromptRunID: undefined, CoAgentRunStepID: undefined });
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('still keeps a relationship that has not changed on the next call', async () => {
|
|
221
|
+
const { provider } = makeProvider(linkedRows());
|
|
222
|
+
const verifier = new SessionRunIDVerifier();
|
|
223
|
+
|
|
224
|
+
await verifier.Verify(SESSION_ID, CLAIMS, USER, provider);
|
|
225
|
+
const second = await verifier.Verify(SESSION_ID, CLAIMS, USER, provider);
|
|
226
|
+
|
|
227
|
+
expect(second).toEqual(CLAIMS);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe('SessionRunIDVerifier.Verify — lookups as the acting user', () => {
|
|
232
|
+
it('does not give a user the result of a lookup made for another user', async () => {
|
|
233
|
+
// No system user is cached, so each lookup runs as the acting user, and only USER can see the run.
|
|
234
|
+
const otherUser = { ID: 'd3d3d3d3-0000-4000-8000-0000000000bb' } as unknown as UserInfo;
|
|
235
|
+
const runViews = vi.fn(async (paramsList: RunViewParams[], user: UserInfo) =>
|
|
236
|
+
paramsList.map((params) => {
|
|
237
|
+
const visible = user === USER && params.EntityName === 'MJ: AI Agent Runs';
|
|
238
|
+
return { Success: true, Results: visible ? [{ ID: CO_RUN_ID }] : [], ErrorMessage: '' };
|
|
239
|
+
}),
|
|
240
|
+
);
|
|
241
|
+
const provider = { RunViews: runViews } as unknown as IMetadataProvider;
|
|
242
|
+
const verifier = new SessionRunIDVerifier();
|
|
243
|
+
|
|
244
|
+
const forUser = await verifier.Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, USER, provider);
|
|
245
|
+
const forOtherUser = await verifier.Verify(SESSION_ID, { CoAgentRunID: CO_RUN_ID }, otherUser, provider);
|
|
246
|
+
|
|
247
|
+
expect(forUser.CoAgentRunID).toBe(CO_RUN_ID);
|
|
248
|
+
expect(forOtherUser.CoAgentRunID).toBeUndefined();
|
|
249
|
+
expect(runViews.mock.calls[1]?.[1]).toBe(otherUser);
|
|
250
|
+
});
|
|
251
|
+
});
|