@memberjunction/server 6.2.0-edge.5 → 6.2.0-edge.6
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/README.md +27 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +3 -3
- package/dist/agentSessions/SessionJanitor.js +3 -3
- package/dist/agentSessions/SessionManager.d.ts +15 -5
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +34 -12
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.js +2 -0
- package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +51 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/initializeProviders.d.ts +10 -0
- package/dist/auth/initializeProviders.d.ts.map +1 -1
- package/dist/auth/initializeProviders.js +37 -1
- package/dist/auth/initializeProviders.js.map +1 -1
- package/dist/auth/localDevAuth.d.ts +79 -0
- package/dist/auth/localDevAuth.d.ts.map +1 -0
- package/dist/auth/localDevAuth.js +439 -0
- package/dist/auth/localDevAuth.js.map +1 -0
- package/dist/auth/magicLink/MagicLinkKeys.d.ts +2 -2
- package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.js +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
- package/dist/auth/magicLink/types.d.ts +12 -0
- package/dist/auth/magicLink/types.d.ts.map +1 -1
- package/dist/auth/sessionUserClone.d.ts +1 -1
- package/dist/auth/sessionUserClone.d.ts.map +1 -1
- package/dist/auth/sessionUserClone.js +3 -2
- package/dist/auth/sessionUserClone.js.map +1 -1
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts +292 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +442 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -0
- package/dist/broadcast/index.d.ts +6 -0
- package/dist/broadcast/index.d.ts.map +1 -0
- package/dist/broadcast/index.js +6 -0
- package/dist/broadcast/index.js.map +1 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts +43 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js +144 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts +46 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js +149 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts +49 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js +131 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts +24 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js +51 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js.map +1 -0
- package/dist/cache.d.ts +3 -2
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js.map +1 -1
- package/dist/config.d.ts +496 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +61 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +3 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +29 -8
- package/dist/context.js.map +1 -1
- package/dist/coreRoutes.d.ts.map +1 -1
- package/dist/coreRoutes.js +14 -0
- package/dist/coreRoutes.js.map +1 -1
- package/dist/generated/graphql-schemas/__mj.d.ts +635 -14
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +3366 -418
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +50 -30
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +154 -79
- package/dist/generic/CacheInvalidationResolver.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +2 -0
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/PubSubManager.d.ts +10 -0
- package/dist/generic/PubSubManager.d.ts.map +1 -1
- package/dist/generic/PubSubManager.js +44 -1
- package/dist/generic/PubSubManager.js.map +1 -1
- package/dist/generic/PushStatusResolver.d.ts +39 -6
- package/dist/generic/PushStatusResolver.d.ts.map +1 -1
- package/dist/generic/PushStatusResolver.js +93 -11
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +65 -25
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +201 -101
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +11 -3
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +79 -13
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/generic/actionOutputFieldSecurity.d.ts +26 -0
- package/dist/generic/actionOutputFieldSecurity.d.ts.map +1 -0
- package/dist/generic/actionOutputFieldSecurity.js +47 -0
- package/dist/generic/actionOutputFieldSecurity.js.map +1 -0
- package/dist/generic/fieldSecurityTransport.d.ts +30 -0
- package/dist/generic/fieldSecurityTransport.d.ts.map +1 -0
- package/dist/generic/fieldSecurityTransport.js +30 -0
- package/dist/generic/fieldSecurityTransport.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +122 -66
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +6 -1
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.d.ts +12 -0
- package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.js +50 -1
- package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
- package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -1
- package/dist/realtimeSessions/SessionVerificationPortsBase.js +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts +8 -6
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.js +15 -6
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
- package/dist/realtimeSessions/sessionAccess.d.ts +88 -11
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
- package/dist/realtimeSessions/sessionAccess.js +125 -22
- package/dist/realtimeSessions/sessionAccess.js.map +1 -1
- package/dist/realtimeSessions/sessionConfigStamps.js +1 -1
- package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -1
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -1
- package/dist/realtimeSessions/subscriptionAuthorization.js +5 -2
- package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -1
- package/dist/realtimeSessions/verificationWorkflow.d.ts +2 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -1
- package/dist/realtimeSessions/verificationWorkflow.js +1 -1
- package/dist/realtimeSessions/verificationWorkflow.js.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.js +19 -3
- package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.d.ts +67 -7
- package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js +201 -19
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
- package/dist/realtimeWidget/host-identity.d.ts +1 -1
- package/dist/realtimeWidget/host-identity.js +1 -1
- package/dist/realtimeWidget/widgetCore.d.ts +20 -1
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetCore.js +17 -1
- package/dist/realtimeWidget/widgetCore.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +2 -1
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts +19 -1
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +63 -12
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +1 -0
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/AgentRunStatusPublisher.d.ts +18 -4
- package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
- package/dist/resolvers/AgentRunStatusPublisher.js +32 -8
- package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
- package/dist/resolvers/AgentSessionResolver.d.ts +10 -4
- package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/AgentSessionResolver.js +40 -17
- package/dist/resolvers/AgentSessionResolver.js.map +1 -1
- package/dist/resolvers/EntityCommunicationsResolver.d.ts +13 -1
- package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityCommunicationsResolver.js +50 -14
- package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts +28 -0
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +117 -21
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
- package/dist/resolvers/FileCategoryResolver.js +1 -0
- package/dist/resolvers/FileCategoryResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts +5 -0
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +13 -4
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +45 -7
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts +8 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.js +31 -3
- package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
- package/dist/resolvers/PipelineProgressResolver.d.ts +6 -0
- package/dist/resolvers/PipelineProgressResolver.d.ts.map +1 -1
- package/dist/resolvers/PipelineProgressResolver.js +5 -3
- package/dist/resolvers/PipelineProgressResolver.js.map +1 -1
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +20 -6
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +2 -0
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +15 -6
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RSUResolver.d.ts.map +1 -1
- package/dist/resolvers/RSUResolver.js +12 -0
- package/dist/resolvers/RSUResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +16 -2
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +82 -28
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +11 -9
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +56 -8
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +209 -64
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +4 -4
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTestResolver.js +3 -1
- package/dist/resolvers/RunTestResolver.js.map +1 -1
- package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncDataResolver.js +4 -0
- package/dist/resolvers/SyncDataResolver.js.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.js +1 -0
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts +17 -2
- package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.js +60 -15
- package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
- package/dist/resolvers/UserViewResolver.d.ts +1 -1
- package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
- package/dist/resolvers/UserViewResolver.js +2 -1
- package/dist/resolvers/UserViewResolver.js.map +1 -1
- package/dist/rest/EntityCRUDHandler.d.ts +6 -0
- package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
- package/dist/rest/EntityCRUDHandler.js +19 -5
- package/dist/rest/EntityCRUDHandler.js.map +1 -1
- package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
- package/dist/rest/RESTEndpointHandler.js +8 -5
- package/dist/rest/RESTEndpointHandler.js.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
- package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.js +7 -7
- package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
- package/dist/rest/ViewOperationsHandler.d.ts +6 -0
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +22 -5
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/serverRoleConfigUnits.d.ts +29 -0
- package/dist/serverRoleConfigUnits.d.ts.map +1 -0
- package/dist/serverRoleConfigUnits.js +33 -0
- package/dist/serverRoleConfigUnits.js.map +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +5 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +42 -15
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/types.d.ts +23 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +98 -98
- package/src/__tests__/ActionResolver.fieldSecurity.test.ts +237 -0
- package/src/__tests__/AdhocQueryResolver.dialect.test.ts +137 -0
- package/src/__tests__/AgentRunStatusPublisher.test.ts +49 -0
- package/src/__tests__/AgentSessionResolver.ownership.test.ts +137 -0
- package/src/__tests__/CustomColumnPromoterOnePass.test.ts +15 -1
- package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
- package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +206 -3
- package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
- package/src/__tests__/PipelineProgressResolver.filter.test.ts +58 -0
- package/src/__tests__/QuerySystemUserResolver.fieldSecurity.test.ts +49 -0
- package/src/__tests__/RESTEndpointHandler.test.ts +25 -0
- package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +23 -3
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +324 -46
- package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +98 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +37 -4
- package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
- package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +176 -0
- package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +23 -1
- package/src/__tests__/ResolverBase.publishStatusUpdate.test.ts +68 -0
- package/src/__tests__/ResolverBase.userViewCacheAndDedup.test.ts +130 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +68 -13
- package/src/__tests__/RunAIAgentResolver.conversationWriter.test.ts +210 -0
- package/src/__tests__/RunAIAgentResolver.fireAndForgetCompletion.test.ts +209 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +17 -27
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +8 -2
- package/src/__tests__/RunAIAgentResolver.serverReply.test.ts +358 -0
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +4 -1
- package/src/__tests__/RunTemplateResolver.test.ts +35 -2
- package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +11 -0
- package/src/__tests__/SessionManager.test.ts +95 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +114 -3
- package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
- package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
- package/src/__tests__/ViewOperationsHandler.test.ts +7 -2
- package/src/__tests__/WidgetSessionService.test.ts +583 -4
- package/src/__tests__/apiRootBoot.test.ts +1 -1
- package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
- package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
- package/src/__tests__/cacheInvalidationRecordData.test.ts +45 -0
- package/src/__tests__/fieldSecurityFixtures.ts +91 -0
- package/src/__tests__/globalCors.test.ts +3 -3
- package/src/__tests__/localDevAuth.test.ts +196 -0
- package/src/__tests__/orphanedConversationDetailReconciler.test.ts +10 -0
- package/src/__tests__/pushStatusResolver.filter.test.ts +57 -1
- package/src/__tests__/realtimeRecordingHandler.test.ts +19 -2
- package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +29 -1
- package/src/__tests__/realtimeSessions/ownedRealtimeSession.anonymous.test.ts +31 -0
- package/src/__tests__/realtimeSessions/ownedRealtimeSession.namedScoped.test.ts +37 -0
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +146 -7
- package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +1 -1
- package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +25 -4
- package/src/__tests__/resolverBase.cloudEventChangedFields.test.ts +157 -0
- package/src/__tests__/resolverBase.fieldSecurityTransport.test.ts +222 -0
- package/src/__tests__/resolverBase.fls.test.ts +146 -97
- package/src/__tests__/restTransport.fieldSecurity.test.ts +148 -0
- package/src/__tests__/runParamsTimeZone.test.ts +179 -0
- package/src/__tests__/runStreams.fieldSecurity.test.ts +100 -0
- package/src/__tests__/runViewProperties.test.ts +114 -0
- package/src/__tests__/scalabilityBenchmarks.test.ts +765 -0
- package/src/__tests__/scalabilityDatabaseIntegration.test.ts +207 -0
- package/src/__tests__/sessionUserClone.test.ts +5 -1
- package/src/__tests__/startupSweepOrder.test.ts +40 -0
- package/src/__tests__/unifiedAuth.test.ts +28 -0
- package/src/__tests__/widgetGuestElevation.test.ts +2 -0
- package/src/agentSessions/SessionJanitor.ts +3 -3
- package/src/agentSessions/SessionManager.ts +35 -12
- package/src/auth/AuthProviderCatalogRouter.ts +3 -0
- package/src/auth/index.ts +56 -12
- package/src/auth/initializeProviders.ts +43 -1
- package/src/auth/localDevAuth.ts +528 -0
- package/src/auth/magicLink/MagicLinkKeys.ts +2 -2
- package/src/auth/magicLink/types.ts +10 -0
- package/src/auth/sessionUserClone.ts +3 -2
- package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +674 -0
- package/src/broadcast/index.ts +5 -0
- package/src/broadcast/providers/AWSIoTCoreBroadcastProvider.ts +180 -0
- package/src/broadcast/providers/AzureWebPubSubBroadcastProvider.ts +182 -0
- package/src/broadcast/providers/GCPPubSubBroadcastProvider.ts +169 -0
- package/src/broadcast/providers/SequencedRoleBucketBroadcastProvider.ts +57 -0
- package/src/cache.ts +2 -1
- package/src/config.ts +69 -0
- package/src/context.ts +32 -8
- package/src/coreRoutes.ts +15 -0
- package/src/generated/graphql-schemas/__mj.ts +2239 -102
- package/src/generic/CacheInvalidationResolver.ts +135 -78
- package/src/generic/OrphanedConversationDetailReconciler.ts +4 -2
- package/src/generic/PubSubManager.ts +54 -1
- package/src/generic/PushStatusResolver.ts +108 -11
- package/src/generic/ResolverBase.ts +237 -107
- package/src/generic/RunViewResolver.ts +80 -15
- package/src/generic/actionOutputFieldSecurity.ts +75 -0
- package/src/generic/fieldSecurityTransport.ts +62 -0
- package/src/index.ts +127 -68
- package/src/integration/CustomColumnPromoter.ts +6 -1
- package/src/logging/variablesLoggingMiddleware.ts +59 -3
- package/src/realtimeSessions/README.md +7 -3
- package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
- package/src/realtimeSessions/ownedRealtimeSession.ts +20 -6
- package/src/realtimeSessions/sessionAccess.ts +181 -25
- package/src/realtimeSessions/sessionConfigStamps.ts +1 -1
- package/src/realtimeSessions/subscriptionAuthorization.ts +4 -2
- package/src/realtimeSessions/verificationWorkflow.ts +3 -1
- package/src/realtimeWidget/WidgetRouter.ts +22 -4
- package/src/realtimeWidget/WidgetSessionService.ts +252 -17
- package/src/realtimeWidget/host-identity.ts +1 -1
- package/src/realtimeWidget/widgetCore.ts +30 -1
- package/src/realtimeWidget/widgetGuestElevation.ts +2 -1
- package/src/resolvers/ActionResolver.ts +78 -15
- package/src/resolvers/AdhocQueryResolver.ts +1 -0
- package/src/resolvers/AgentRunStatusPublisher.ts +36 -8
- package/src/resolvers/AgentSessionResolver.ts +46 -18
- package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
- package/src/resolvers/FetchEntityVectorsResolver.ts +132 -21
- package/src/resolvers/FileCategoryResolver.ts +1 -0
- package/src/resolvers/FileResolver.ts +14 -4
- package/src/resolvers/IntegrationDiscoveryResolver.ts +49 -7
- package/src/resolvers/MergeRecordsResolver.ts +23 -2
- package/src/resolvers/PipelineProgressResolver.ts +12 -3
- package/src/resolvers/QueryResolver.ts +20 -6
- package/src/resolvers/QuerySystemUserResolver.ts +15 -6
- package/src/resolvers/RSUResolver.ts +12 -0
- package/src/resolvers/RealtimeClientSessionResolver.ts +104 -33
- package/src/resolvers/RemoteBrowserActionResolver.ts +15 -10
- package/src/resolvers/RunAIAgentResolver.ts +258 -82
- package/src/resolvers/RunTemplateResolver.ts +6 -4
- package/src/resolvers/RunTestResolver.ts +3 -1
- package/src/resolvers/SyncDataResolver.ts +4 -0
- package/src/resolvers/TestQuerySQLResolver.ts +1 -0
- package/src/resolvers/TransactionGroupResolver.ts +66 -15
- package/src/resolvers/UserViewResolver.ts +2 -1
- package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
- package/src/rest/EntityCRUDHandler.ts +22 -5
- package/src/rest/RESTEndpointHandler.ts +8 -5
- package/src/rest/RealtimeRecordingHandler.ts +13 -10
- package/src/rest/ViewOperationsHandler.ts +24 -6
- package/src/serverRoleConfigUnits.ts +48 -0
- package/src/services/TaskGraphContinuationDeliverer.ts +52 -20
- package/src/types.ts +23 -1
|
@@ -233,14 +233,24 @@ afterEach(() => {
|
|
|
233
233
|
errSpy = undefined;
|
|
234
234
|
});
|
|
235
235
|
|
|
236
|
+
const makeTestUuid = (a: string, b: string) =>
|
|
237
|
+
[a.repeat(8), b.repeat(4), '4' + b.repeat(3), '8' + b.repeat(3), b.repeat(12)].join('-');
|
|
238
|
+
const PRIOR_1 = makeTestUuid('1', '1');
|
|
239
|
+
const PRIOR_2 = makeTestUuid('2', '2');
|
|
240
|
+
const PRIOR_A = makeTestUuid('a', 'a');
|
|
241
|
+
const PRIOR_B = makeTestUuid('b', 'b');
|
|
242
|
+
const PRIOR_GONE = makeTestUuid('9', '9');
|
|
243
|
+
|
|
236
244
|
function makeSessionEntity(overrides: Partial<FakeSession> = {}): FakeSession {
|
|
237
245
|
return {
|
|
238
246
|
ID: 'session-1',
|
|
239
247
|
Status: 'Active',
|
|
240
248
|
ConversationID: 'conv-1',
|
|
249
|
+
ExternalID: 'scope-res-1',
|
|
241
250
|
UserID: 'user-1',
|
|
242
251
|
Config_: JSON.stringify({ targetAgentID: 'target-1' }),
|
|
243
252
|
NewRecord: vi.fn(),
|
|
253
|
+
MarkServerAuthoredReply: vi.fn(),
|
|
244
254
|
Save: vi.fn(async () => true),
|
|
245
255
|
Load: vi.fn(async () => true),
|
|
246
256
|
LatestResult: { CompleteMessage: '' },
|
|
@@ -374,6 +384,95 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession', () => {
|
|
|
374
384
|
expect(JSON.parse(result.SessionConfigJson)).toEqual({ instructions: 'hi' });
|
|
375
385
|
});
|
|
376
386
|
|
|
387
|
+
describe('recording consent at start (#6010)', () => {
|
|
388
|
+
const RECORDING_SESSION_ID = 'a1a1a1a1-0000-4000-8000-000000000001';
|
|
389
|
+
const STARTED_AT = '2026-10-01T00:00:00.000Z';
|
|
390
|
+
|
|
391
|
+
/** A provider that can run the ledger's proc, recording every SQL call; `failSql` makes it throw. */
|
|
392
|
+
function makeSqlProvider(failSql = false) {
|
|
393
|
+
const sqlCalls: { Sql: string; Params: unknown[] }[] = [];
|
|
394
|
+
const provider = {
|
|
395
|
+
...(makeProvider(() => makeSessionEntity()) as object),
|
|
396
|
+
MJCoreSchemaName: '__mj',
|
|
397
|
+
Dialect: {
|
|
398
|
+
ProcedureCallSyntax: (schema: string, name: string, params: string[]) => `EXEC [${schema}].[${name}] ${params.join(', ')}`,
|
|
399
|
+
ParameterPlaceholder: (index: number) => `@p${index}`,
|
|
400
|
+
},
|
|
401
|
+
ExecuteSQL: vi.fn(async (sql: string, params: unknown[]) => {
|
|
402
|
+
sqlCalls.push({ Sql: sql, Params: params });
|
|
403
|
+
if (failSql) throw new Error('proc exploded');
|
|
404
|
+
return [{ RecordingStatus: null }];
|
|
405
|
+
}),
|
|
406
|
+
};
|
|
407
|
+
return { provider, sqlCalls };
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async function startWithConsent(provider: unknown) {
|
|
411
|
+
currentProvider = provider;
|
|
412
|
+
const createdSession = makeSessionEntity({ ID: RECORDING_SESSION_ID });
|
|
413
|
+
createSessionMock.mockResolvedValue(createdSession);
|
|
414
|
+
prepareClientSessionMock.mockResolvedValue({
|
|
415
|
+
Success: true,
|
|
416
|
+
ClientConfig: { Provider: 'openai', Model: 'gpt-realtime', EphemeralToken: 'ek', ExpiresAt: '2026-01-01T00:00:00Z', SessionConfig: {} },
|
|
417
|
+
});
|
|
418
|
+
const result = await makeResolver().StartRealtimeClientSession(
|
|
419
|
+
'target-1', makeCtx(), undefined, undefined, undefined, undefined, undefined, undefined, STARTED_AT, true,
|
|
420
|
+
);
|
|
421
|
+
return { createdSession, result };
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** The one stamp-proc call the start made, with its positional params. */
|
|
425
|
+
function onlyStamp(sqlCalls: { Sql: string; Params: unknown[] }[]): unknown[] {
|
|
426
|
+
const stamps = sqlCalls.filter((c) => c.Sql.includes('spStampAIAgentSessionRecordingFile'));
|
|
427
|
+
expect(stamps).toHaveLength(1);
|
|
428
|
+
return stamps[0].Params;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
it('saves RecordingStartedAt through the entity and stamps RecordingMedia and t0 through the ledger proc', async () => {
|
|
432
|
+
const { provider, sqlCalls } = makeSqlProvider();
|
|
433
|
+
const { createdSession } = await startWithConsent(provider);
|
|
434
|
+
|
|
435
|
+
expect((createdSession.RecordingStartedAt as Date).toISOString()).toBe(STARTED_AT);
|
|
436
|
+
expect(createdSession.Save).toHaveBeenCalled();
|
|
437
|
+
const [sessionID, fileID, media, startedAt] = onlyStamp(sqlCalls);
|
|
438
|
+
expect([sessionID, fileID, media]).toEqual([RECORDING_SESSION_ID, null, 'Audio']);
|
|
439
|
+
expect((startedAt as Date).toISOString()).toBe(STARTED_AT);
|
|
440
|
+
expect(createdSession.RecordingMedia).toBeUndefined();
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('still stamps t0 through the ledger proc when the entity save fails (the janitor recovers only sessions with a t0)', async () => {
|
|
444
|
+
errSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
445
|
+
const { provider, sqlCalls } = makeSqlProvider();
|
|
446
|
+
createSessionMock.mockReset();
|
|
447
|
+
const failing = makeSessionEntity({
|
|
448
|
+
ID: RECORDING_SESSION_ID,
|
|
449
|
+
Save: vi.fn(async () => false),
|
|
450
|
+
LatestResult: { CompleteMessage: 'save refused' },
|
|
451
|
+
});
|
|
452
|
+
createSessionMock.mockResolvedValue(failing);
|
|
453
|
+
currentProvider = provider;
|
|
454
|
+
prepareClientSessionMock.mockResolvedValue({
|
|
455
|
+
Success: true,
|
|
456
|
+
ClientConfig: { Provider: 'openai', Model: 'gpt-realtime', EphemeralToken: 'ek', ExpiresAt: '2026-01-01T00:00:00Z', SessionConfig: {} },
|
|
457
|
+
});
|
|
458
|
+
await makeResolver().StartRealtimeClientSession(
|
|
459
|
+
'target-1', makeCtx(), undefined, undefined, undefined, undefined, undefined, undefined, STARTED_AT, true,
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
const [, , , startedAt] = onlyStamp(sqlCalls);
|
|
463
|
+
expect((startedAt as Date).toISOString()).toBe(STARTED_AT);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('does not fail the session start when the media stamp throws, and logs it with the session id', async () => {
|
|
467
|
+
errSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
468
|
+
const { provider } = makeSqlProvider(true);
|
|
469
|
+
const { result } = await startWithConsent(provider);
|
|
470
|
+
|
|
471
|
+
expect(result.AgentSessionId).toBe(RECORDING_SESSION_ID);
|
|
472
|
+
expect(errSpy?.mock.calls.some((c) => String(c[0]).includes(RECORDING_SESSION_ID) && String(c[0]).includes('proc exploded'))).toBe(true);
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
|
|
377
476
|
it('threads preferredModelId to the prepare service and surfaces ModelName + narration template', async () => {
|
|
378
477
|
hasPermissionMock.mockResolvedValue(true);
|
|
379
478
|
// Explicit model selection is gated by 'Realtime: Advanced Session Controls' — grant it.
|
|
@@ -1009,6 +1108,39 @@ describe('RealtimeClientSessionResolver.RelayRealtimeTranscript', () => {
|
|
|
1009
1108
|
|
|
1010
1109
|
await resolver.RelayRealtimeTranscript('session-1', 'assistant', 'response', makeCtx());
|
|
1011
1110
|
expect(detail.Role).toBe('AI');
|
|
1111
|
+
// The agent's side of the transcript is the server's write: unmarked, the reply gate refuses it.
|
|
1112
|
+
expect(detail.MarkServerAuthoredReply).toHaveBeenCalled();
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
/** A19: the agent's words come from the browser here, so they are stored only in the caller's own conversation. */
|
|
1116
|
+
it("does not store the agent's side in a conversation the caller doesn't own, while still mirroring to the prompt run, heartbeating, and storing their own side", async () => {
|
|
1117
|
+
const detail = makeSessionEntity({ ID: 'detail-shared' });
|
|
1118
|
+
const promptRun = makeSessionEntity({ ID: PROMPT_RUN_ID, Messages: null });
|
|
1119
|
+
const session = makeSessionEntity({
|
|
1120
|
+
ConversationID: 'conv-shared',
|
|
1121
|
+
Config_: JSON.stringify({ targetAgentID: 'target-1', coAgentRunID: CO_RUN_ID, promptRunID: PROMPT_RUN_ID }),
|
|
1122
|
+
});
|
|
1123
|
+
const conversation = makeSessionEntity({ ID: 'conv-shared', UserID: 'the-owner' });
|
|
1124
|
+
currentProvider = makeProvider(
|
|
1125
|
+
(name) => {
|
|
1126
|
+
if (name === 'MJ: Conversation Details') return detail;
|
|
1127
|
+
if (name === 'MJ: Conversations') return conversation;
|
|
1128
|
+
if (name === 'MJ: AI Prompt Runs') return promptRun;
|
|
1129
|
+
return session;
|
|
1130
|
+
},
|
|
1131
|
+
sessionRunLookups([CO_RUN_ID], PROMPT_RUN_ID),
|
|
1132
|
+
);
|
|
1133
|
+
const resolver = makeResolver();
|
|
1134
|
+
|
|
1135
|
+
expect(await resolver.RelayRealtimeTranscript('session-1', 'assistant', 'words the agent never said', makeCtx())).toBe(false);
|
|
1136
|
+
expect(await resolver.RelayRealtimeTranscript('session-1', 'assistant', 'a correction', makeCtx(), true)).toBe(false);
|
|
1137
|
+
expect(detail.Save).not.toHaveBeenCalled();
|
|
1138
|
+
expect(conversation.Load).toHaveBeenCalledWith('conv-shared');
|
|
1139
|
+
expect(promptRun.Save).toHaveBeenCalled();
|
|
1140
|
+
expect(heartbeatMock).toHaveBeenCalledTimes(2);
|
|
1141
|
+
|
|
1142
|
+
expect(await resolver.RelayRealtimeTranscript('session-1', 'user', 'my own words', makeCtx())).toBe(true);
|
|
1143
|
+
expect(detail.Role).toBe('User');
|
|
1012
1144
|
});
|
|
1013
1145
|
|
|
1014
1146
|
it("maps role case/whitespace-insensitively (' USER ' → 'User')", async () => {
|
|
@@ -1449,12 +1581,21 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior cha
|
|
|
1449
1581
|
*/
|
|
1450
1582
|
function makeRestoreProvider(opts: {
|
|
1451
1583
|
prior?: FakeSession;
|
|
1584
|
+
externalId?: string | null;
|
|
1452
1585
|
rows?: Array<{ Channel: string; Config: string | null }>;
|
|
1453
1586
|
runViewResult?: { Success: boolean; ErrorMessage?: string; Results?: unknown[] };
|
|
1454
1587
|
}): { provider: unknown; runView: ReturnType<typeof vi.fn>; prior: FakeSession } {
|
|
1455
|
-
const prior = opts.prior ?? makeSessionEntity({ ID:
|
|
1588
|
+
const prior = opts.prior ?? makeSessionEntity({ ID: PRIOR_1, UserID: 'user-1' });
|
|
1589
|
+
const conversation = makeSessionEntity({ ExternalID: opts.externalId ?? prior.ExternalID ?? 'scope-res-1' });
|
|
1456
1590
|
const runView = vi.fn(async () => opts.runViewResult ?? { Success: true, Results: opts.rows ?? [] });
|
|
1457
|
-
return {
|
|
1591
|
+
return {
|
|
1592
|
+
provider: {
|
|
1593
|
+
GetEntityObject: vi.fn(async (name?: string) => (name === 'MJ: Conversations' ? conversation : prior)),
|
|
1594
|
+
RunView: runView,
|
|
1595
|
+
},
|
|
1596
|
+
runView,
|
|
1597
|
+
prior,
|
|
1598
|
+
};
|
|
1458
1599
|
}
|
|
1459
1600
|
|
|
1460
1601
|
function setupHappyStart(): void {
|
|
@@ -1475,7 +1616,7 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior cha
|
|
|
1475
1616
|
currentProvider = provider;
|
|
1476
1617
|
const resolver = makeResolver();
|
|
1477
1618
|
|
|
1478
|
-
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
1619
|
+
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
1479
1620
|
|
|
1480
1621
|
expect(result.PriorChannelStatesJson).toBeDefined();
|
|
1481
1622
|
expect(JSON.parse(result.PriorChannelStatesJson as string)).toEqual({
|
|
@@ -1484,7 +1625,7 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior cha
|
|
|
1484
1625
|
});
|
|
1485
1626
|
// lastSessionId still flows into the new durable session record.
|
|
1486
1627
|
const createArg = createSessionMock.mock.calls[0][0] as { lastSessionID?: string };
|
|
1487
|
-
expect(createArg.lastSessionID).toBe(
|
|
1628
|
+
expect(createArg.lastSessionID).toBe(PRIOR_1);
|
|
1488
1629
|
});
|
|
1489
1630
|
|
|
1490
1631
|
/** RunView calls that hit the session-channel restore query (the only RunView in the start flow). */
|
|
@@ -1507,18 +1648,55 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior cha
|
|
|
1507
1648
|
it('omits the field (no state leak, no query) when the prior session belongs to ANOTHER user', async () => {
|
|
1508
1649
|
setupHappyStart();
|
|
1509
1650
|
const { provider, runView } = makeRestoreProvider({
|
|
1510
|
-
prior: makeSessionEntity({ ID:
|
|
1651
|
+
prior: makeSessionEntity({ ID: PRIOR_1, UserID: 'someone-else' }),
|
|
1511
1652
|
rows: [{ Channel: 'Whiteboard', Config: '{"secret":true}' }],
|
|
1512
1653
|
});
|
|
1513
1654
|
currentProvider = provider;
|
|
1514
1655
|
const resolver = makeResolver();
|
|
1515
1656
|
|
|
1516
|
-
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
1657
|
+
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
1517
1658
|
|
|
1518
1659
|
expect(result.PriorChannelStatesJson).toBeUndefined();
|
|
1519
1660
|
expect(restoreQueryCalls(runView)).toHaveLength(0);
|
|
1520
1661
|
});
|
|
1521
1662
|
|
|
1663
|
+
it('skips restore for a second anonymous visitor (same UserID, different AnonymousVisitorID) and allows the matching anonymous visitor', async () => {
|
|
1664
|
+
setupHappyStart();
|
|
1665
|
+
const anonGuest = (visitorId: string): UserInfo =>
|
|
1666
|
+
({
|
|
1667
|
+
ID: 'anon-1',
|
|
1668
|
+
Email: 'anonymous@magic-link.local',
|
|
1669
|
+
IsMagicLinkAnonymous: true,
|
|
1670
|
+
// An unscoped multi-use invite: redemptions are told apart by mj_sid (a resource scope would be shared).
|
|
1671
|
+
AnonymousVisitorID: visitorId,
|
|
1672
|
+
}) as unknown as UserInfo;
|
|
1673
|
+
const makeAnonResolver = (visitorId: string): RealtimeClientSessionResolver => {
|
|
1674
|
+
const r = new RealtimeClientSessionResolver();
|
|
1675
|
+
(r as unknown as { GetUserFromPayload: () => unknown }).GetUserFromPayload = () => anonGuest(visitorId);
|
|
1676
|
+
return r;
|
|
1677
|
+
};
|
|
1678
|
+
|
|
1679
|
+
const { provider: provider2, runView: runView2 } = makeRestoreProvider({
|
|
1680
|
+
prior: makeSessionEntity({ ID: PRIOR_1, UserID: 'anon-1', ExternalID: 'visitor-1' }),
|
|
1681
|
+
externalId: 'visitor-1',
|
|
1682
|
+
rows: [{ Channel: 'Whiteboard', Config: '{"secret":true}' }],
|
|
1683
|
+
});
|
|
1684
|
+
currentProvider = provider2;
|
|
1685
|
+
const refusedResult = await makeAnonResolver('visitor-2').StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
1686
|
+
expect(refusedResult.PriorChannelStatesJson).toBeUndefined();
|
|
1687
|
+
expect(restoreQueryCalls(runView2)).toHaveLength(0);
|
|
1688
|
+
|
|
1689
|
+
setupHappyStart();
|
|
1690
|
+
const { provider: provider1 } = makeRestoreProvider({
|
|
1691
|
+
prior: makeSessionEntity({ ID: PRIOR_1, UserID: 'anon-1', ExternalID: 'visitor-1' }),
|
|
1692
|
+
externalId: 'visitor-1',
|
|
1693
|
+
rows: [{ Channel: 'Whiteboard', Config: '{"items":[1]}' }],
|
|
1694
|
+
});
|
|
1695
|
+
currentProvider = provider1;
|
|
1696
|
+
const allowedResult = await makeAnonResolver('visitor-1').StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
1697
|
+
expect(allowedResult.PriorChannelStatesJson).toBe(JSON.stringify({ Whiteboard: '{"items":[1]}' }));
|
|
1698
|
+
});
|
|
1699
|
+
|
|
1522
1700
|
it('omits the field when the prior session does not exist', async () => {
|
|
1523
1701
|
setupHappyStart();
|
|
1524
1702
|
const { provider, runView } = makeRestoreProvider({
|
|
@@ -1527,7 +1705,7 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior cha
|
|
|
1527
1705
|
currentProvider = provider;
|
|
1528
1706
|
const resolver = makeResolver();
|
|
1529
1707
|
|
|
1530
|
-
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
1708
|
+
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_GONE);
|
|
1531
1709
|
|
|
1532
1710
|
expect(result.PriorChannelStatesJson).toBeUndefined();
|
|
1533
1711
|
expect(restoreQueryCalls(runView)).toHaveLength(0);
|
|
@@ -1539,7 +1717,7 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior cha
|
|
|
1539
1717
|
currentProvider = provider;
|
|
1540
1718
|
const resolver = makeResolver();
|
|
1541
1719
|
|
|
1542
|
-
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
1720
|
+
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
1543
1721
|
expect(result.PriorChannelStatesJson).toBeUndefined();
|
|
1544
1722
|
});
|
|
1545
1723
|
|
|
@@ -1554,7 +1732,7 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior cha
|
|
|
1554
1732
|
currentProvider = provider;
|
|
1555
1733
|
const resolver = makeResolver();
|
|
1556
1734
|
|
|
1557
|
-
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
1735
|
+
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
1558
1736
|
|
|
1559
1737
|
expect(JSON.parse(result.PriorChannelStatesJson as string)).toEqual({ Whiteboard: '{"items":[]}' });
|
|
1560
1738
|
});
|
|
@@ -1570,7 +1748,7 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior cha
|
|
|
1570
1748
|
currentProvider = provider;
|
|
1571
1749
|
const resolver = makeResolver();
|
|
1572
1750
|
|
|
1573
|
-
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
1751
|
+
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
1574
1752
|
|
|
1575
1753
|
const states = JSON.parse(result.PriorChannelStatesJson as string) as Record<string, string>;
|
|
1576
1754
|
expect(Object.keys(states)).toEqual(['First']);
|
|
@@ -1582,7 +1760,7 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior cha
|
|
|
1582
1760
|
currentProvider = provider;
|
|
1583
1761
|
const resolver = makeResolver();
|
|
1584
1762
|
|
|
1585
|
-
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
1763
|
+
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
1586
1764
|
|
|
1587
1765
|
expect(result.AgentSessionId).toBe('session-restore');
|
|
1588
1766
|
expect(result.PriorChannelStatesJson).toBeUndefined();
|
|
@@ -1598,7 +1776,7 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior cha
|
|
|
1598
1776
|
};
|
|
1599
1777
|
const resolver = makeResolver();
|
|
1600
1778
|
|
|
1601
|
-
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
1779
|
+
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
1602
1780
|
|
|
1603
1781
|
expect(result.AgentSessionId).toBe('session-restore');
|
|
1604
1782
|
expect(result.PriorChannelStatesJson).toBeUndefined();
|
|
@@ -2044,6 +2222,7 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior-tra
|
|
|
2044
2222
|
interface ChainRow {
|
|
2045
2223
|
UserID: string;
|
|
2046
2224
|
LastSessionID?: string | null;
|
|
2225
|
+
ExternalID?: string | null;
|
|
2047
2226
|
}
|
|
2048
2227
|
|
|
2049
2228
|
interface DetailQueryRow {
|
|
@@ -2072,7 +2251,16 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior-tra
|
|
|
2072
2251
|
return { Success: true, Results: [] }; // session-channel restore — none
|
|
2073
2252
|
});
|
|
2074
2253
|
const provider = {
|
|
2075
|
-
GetEntityObject: vi.fn(async () => {
|
|
2254
|
+
GetEntityObject: vi.fn(async (entityName?: string) => {
|
|
2255
|
+
if (entityName === 'MJ: Conversations') {
|
|
2256
|
+
const conv = makeSessionEntity();
|
|
2257
|
+
conv.Load = vi.fn(async (convId: string) => {
|
|
2258
|
+
const row = opts.chain[convId];
|
|
2259
|
+
conv.ExternalID = row?.ExternalID ?? 'scope-res-1';
|
|
2260
|
+
return true;
|
|
2261
|
+
});
|
|
2262
|
+
return conv;
|
|
2263
|
+
}
|
|
2076
2264
|
const entity = makeSessionEntity();
|
|
2077
2265
|
entity.Load = vi.fn(async (id: string) => {
|
|
2078
2266
|
const row = opts.chain[id];
|
|
@@ -2080,6 +2268,7 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior-tra
|
|
|
2080
2268
|
return false;
|
|
2081
2269
|
}
|
|
2082
2270
|
entity.ID = id;
|
|
2271
|
+
entity.ConversationID = id;
|
|
2083
2272
|
entity.UserID = row.UserID;
|
|
2084
2273
|
entity.LastSessionID = row.LastSessionID ?? null;
|
|
2085
2274
|
return true;
|
|
@@ -2108,13 +2297,13 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior-tra
|
|
|
2108
2297
|
it('threads the prior leg\'s role-tagged transcript into PrepareClientSession.PriorTranscript', async () => {
|
|
2109
2298
|
setupHappyStart();
|
|
2110
2299
|
const { provider } = makeHydrationProvider({
|
|
2111
|
-
chain: {
|
|
2300
|
+
chain: { [PRIOR_1]: { UserID: 'user-1' } },
|
|
2112
2301
|
detailRows: [turn('User', 'hello there'), turn('AI', 'hi! how can I help?')],
|
|
2113
2302
|
});
|
|
2114
2303
|
currentProvider = provider;
|
|
2115
2304
|
const resolver = makeResolver();
|
|
2116
2305
|
|
|
2117
|
-
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
2306
|
+
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
2118
2307
|
|
|
2119
2308
|
expect(prepTranscriptArg()).toBe('User: hello there\nAssistant: hi! how can I help?');
|
|
2120
2309
|
});
|
|
@@ -2123,23 +2312,23 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior-tra
|
|
|
2123
2312
|
setupHappyStart();
|
|
2124
2313
|
const { provider, runView } = makeHydrationProvider({
|
|
2125
2314
|
chain: {
|
|
2126
|
-
|
|
2127
|
-
|
|
2315
|
+
[PRIOR_2]: { UserID: 'user-1', LastSessionID: PRIOR_1 },
|
|
2316
|
+
[PRIOR_1]: { UserID: 'user-1' },
|
|
2128
2317
|
},
|
|
2129
2318
|
detailRows: [turn('User', 'first leg'), turn('AI', 'second leg')],
|
|
2130
2319
|
});
|
|
2131
2320
|
currentProvider = provider;
|
|
2132
2321
|
const resolver = makeResolver();
|
|
2133
2322
|
|
|
2134
|
-
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
2323
|
+
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_2);
|
|
2135
2324
|
|
|
2136
2325
|
const detailCall = runView.mock.calls.find(
|
|
2137
2326
|
(c) => (c[0] as { EntityName: string }).EntityName === 'MJ: Conversation Details',
|
|
2138
2327
|
);
|
|
2139
2328
|
expect(detailCall).toBeDefined();
|
|
2140
2329
|
const filter = (detailCall![0] as { ExtraFilter: string }).ExtraFilter;
|
|
2141
|
-
expect(filter).toContain(
|
|
2142
|
-
expect(filter).toContain(
|
|
2330
|
+
expect(filter).toContain(`'${PRIOR_2}'`);
|
|
2331
|
+
expect(filter).toContain(`'${PRIOR_1}'`);
|
|
2143
2332
|
expect(prepTranscriptArg()).toBe('User: first leg\nAssistant: second leg');
|
|
2144
2333
|
});
|
|
2145
2334
|
|
|
@@ -2147,15 +2336,15 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior-tra
|
|
|
2147
2336
|
setupHappyStart();
|
|
2148
2337
|
const { provider } = makeHydrationProvider({
|
|
2149
2338
|
chain: {
|
|
2150
|
-
|
|
2151
|
-
|
|
2339
|
+
[PRIOR_A]: { UserID: 'user-1', LastSessionID: PRIOR_B },
|
|
2340
|
+
[PRIOR_B]: { UserID: 'user-1', LastSessionID: PRIOR_A },
|
|
2152
2341
|
},
|
|
2153
2342
|
detailRows: [turn('User', 'looped once')],
|
|
2154
2343
|
});
|
|
2155
2344
|
currentProvider = provider;
|
|
2156
2345
|
const resolver = makeResolver();
|
|
2157
2346
|
|
|
2158
|
-
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
2347
|
+
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_A);
|
|
2159
2348
|
|
|
2160
2349
|
expect(result.AgentSessionId).toBe('session-new');
|
|
2161
2350
|
expect(prepTranscriptArg()).toBe('User: looped once');
|
|
@@ -2164,11 +2353,11 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior-tra
|
|
|
2164
2353
|
it('keeps only the NEWEST 30 turns (oldest dropped)', async () => {
|
|
2165
2354
|
setupHappyStart();
|
|
2166
2355
|
const rows = Array.from({ length: 35 }, (_, i) => turn('User', `turn ${i}`, `d-${i}`));
|
|
2167
|
-
const { provider } = makeHydrationProvider({ chain: {
|
|
2356
|
+
const { provider } = makeHydrationProvider({ chain: { [PRIOR_1]: { UserID: 'user-1' } }, detailRows: rows });
|
|
2168
2357
|
currentProvider = provider;
|
|
2169
2358
|
const resolver = makeResolver();
|
|
2170
2359
|
|
|
2171
|
-
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
2360
|
+
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
2172
2361
|
|
|
2173
2362
|
const lines = (prepTranscriptArg() ?? '').split('\n');
|
|
2174
2363
|
expect(lines).toHaveLength(30);
|
|
@@ -2180,11 +2369,11 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior-tra
|
|
|
2180
2369
|
setupHappyStart();
|
|
2181
2370
|
// 10 turns of 1000 chars each — only the newest ~7 fit the 8000-char budget.
|
|
2182
2371
|
const rows = Array.from({ length: 10 }, (_, i) => turn('User', `${i}`.padEnd(1000, 'x'), `d-${i}`));
|
|
2183
|
-
const { provider } = makeHydrationProvider({ chain: {
|
|
2372
|
+
const { provider } = makeHydrationProvider({ chain: { [PRIOR_1]: { UserID: 'user-1' } }, detailRows: rows });
|
|
2184
2373
|
currentProvider = provider;
|
|
2185
2374
|
const resolver = makeResolver();
|
|
2186
2375
|
|
|
2187
|
-
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
2376
|
+
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
2188
2377
|
|
|
2189
2378
|
const transcript = prepTranscriptArg() ?? '';
|
|
2190
2379
|
expect(transcript.length).toBeLessThanOrEqual(8000);
|
|
@@ -2197,7 +2386,7 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior-tra
|
|
|
2197
2386
|
it('skips hidden, error-role, and empty rows', async () => {
|
|
2198
2387
|
setupHappyStart();
|
|
2199
2388
|
const { provider } = makeHydrationProvider({
|
|
2200
|
-
chain: {
|
|
2389
|
+
chain: { [PRIOR_1]: { UserID: 'user-1' } },
|
|
2201
2390
|
detailRows: [
|
|
2202
2391
|
{ ID: 'd-1', Role: 'Error', Message: 'boom', HiddenToUser: false },
|
|
2203
2392
|
{ ID: 'd-2', Role: 'AI', Message: 'secret', HiddenToUser: true },
|
|
@@ -2209,7 +2398,7 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior-tra
|
|
|
2209
2398
|
currentProvider = provider;
|
|
2210
2399
|
const resolver = makeResolver();
|
|
2211
2400
|
|
|
2212
|
-
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
2401
|
+
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
2213
2402
|
|
|
2214
2403
|
expect(prepTranscriptArg()).toBe('User: visible');
|
|
2215
2404
|
});
|
|
@@ -2228,50 +2417,85 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior-tra
|
|
|
2228
2417
|
it('omits PriorTranscript (no leak) when the prior session belongs to ANOTHER user', async () => {
|
|
2229
2418
|
setupHappyStart();
|
|
2230
2419
|
const { provider } = makeHydrationProvider({
|
|
2231
|
-
chain: {
|
|
2420
|
+
chain: { [PRIOR_1]: { UserID: 'someone-else' } },
|
|
2232
2421
|
detailRows: [turn('User', 'their words')],
|
|
2233
2422
|
});
|
|
2234
2423
|
currentProvider = provider;
|
|
2235
2424
|
const resolver = makeResolver();
|
|
2236
2425
|
|
|
2237
|
-
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
2426
|
+
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
2238
2427
|
|
|
2239
2428
|
expect(prepTranscriptArg()).toBeUndefined();
|
|
2240
2429
|
});
|
|
2241
2430
|
|
|
2431
|
+
it('skips prior legs owned by a different anonymous visitor (same UserID, different AnonymousVisitorID) and allows the matching visitor', async () => {
|
|
2432
|
+
setupHappyStart();
|
|
2433
|
+
const anonGuest = (visitorId: string): UserInfo =>
|
|
2434
|
+
({
|
|
2435
|
+
ID: 'anon-1',
|
|
2436
|
+
Email: 'anonymous@magic-link.local',
|
|
2437
|
+
IsMagicLinkAnonymous: true,
|
|
2438
|
+
// An unscoped multi-use invite: redemptions are told apart by mj_sid (a resource scope would be shared).
|
|
2439
|
+
AnonymousVisitorID: visitorId,
|
|
2440
|
+
}) as unknown as UserInfo;
|
|
2441
|
+
const makeAnonResolver = (visitorId: string): RealtimeClientSessionResolver => {
|
|
2442
|
+
const r = new RealtimeClientSessionResolver();
|
|
2443
|
+
(r as unknown as { GetUserFromPayload: () => unknown }).GetUserFromPayload = () => anonGuest(visitorId);
|
|
2444
|
+
return r;
|
|
2445
|
+
};
|
|
2446
|
+
|
|
2447
|
+
const { provider: provider2 } = makeHydrationProvider({
|
|
2448
|
+
chain: { [PRIOR_1]: { UserID: 'anon-1', ExternalID: 'visitor-1' } },
|
|
2449
|
+
detailRows: [turn('User', 'visitor 1 secret')],
|
|
2450
|
+
});
|
|
2451
|
+
currentProvider = provider2;
|
|
2452
|
+
await makeAnonResolver('visitor-2').StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
2453
|
+
expect(prepTranscriptArg()).toBeUndefined();
|
|
2454
|
+
|
|
2455
|
+
prepareClientSessionMock.mockClear();
|
|
2456
|
+
setupHappyStart();
|
|
2457
|
+
const { provider: provider1 } = makeHydrationProvider({
|
|
2458
|
+
chain: { [PRIOR_1]: { UserID: 'anon-1', ExternalID: 'visitor-1' } },
|
|
2459
|
+
detailRows: [turn('User', 'visitor 1 secret')],
|
|
2460
|
+
});
|
|
2461
|
+
currentProvider = provider1;
|
|
2462
|
+
await makeAnonResolver('visitor-1').StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
2463
|
+
expect(prepTranscriptArg()).toBe('User: visitor 1 secret');
|
|
2464
|
+
});
|
|
2465
|
+
|
|
2242
2466
|
it('stops the chain walk at the first UNOWNED deeper leg but keeps the owned legs', async () => {
|
|
2243
2467
|
setupHappyStart();
|
|
2244
2468
|
const { provider, runView } = makeHydrationProvider({
|
|
2245
2469
|
chain: {
|
|
2246
|
-
|
|
2247
|
-
|
|
2470
|
+
[PRIOR_2]: { UserID: 'user-1', LastSessionID: PRIOR_1 },
|
|
2471
|
+
[PRIOR_1]: { UserID: 'someone-else' },
|
|
2248
2472
|
},
|
|
2249
2473
|
detailRows: [turn('User', 'owned leg only')],
|
|
2250
2474
|
});
|
|
2251
2475
|
currentProvider = provider;
|
|
2252
2476
|
const resolver = makeResolver();
|
|
2253
2477
|
|
|
2254
|
-
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
2478
|
+
await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_2);
|
|
2255
2479
|
|
|
2256
2480
|
const detailCall = runView.mock.calls.find(
|
|
2257
2481
|
(c) => (c[0] as { EntityName: string }).EntityName === 'MJ: Conversation Details',
|
|
2258
2482
|
);
|
|
2259
2483
|
const filter = (detailCall![0] as { ExtraFilter: string }).ExtraFilter;
|
|
2260
|
-
expect(filter).toContain(
|
|
2261
|
-
expect(filter).not.toContain(
|
|
2484
|
+
expect(filter).toContain(`'${PRIOR_2}'`);
|
|
2485
|
+
expect(filter).not.toContain(`'${PRIOR_1}'`);
|
|
2262
2486
|
expect(prepTranscriptArg()).toBe('User: owned leg only');
|
|
2263
2487
|
});
|
|
2264
2488
|
|
|
2265
2489
|
it('tolerates a failed details query (start succeeds, no hydration)', async () => {
|
|
2266
2490
|
setupHappyStart();
|
|
2267
2491
|
const { provider } = makeHydrationProvider({
|
|
2268
|
-
chain: {
|
|
2492
|
+
chain: { [PRIOR_1]: { UserID: 'user-1' } },
|
|
2269
2493
|
detailQueryFails: true,
|
|
2270
2494
|
});
|
|
2271
2495
|
currentProvider = provider;
|
|
2272
2496
|
const resolver = makeResolver();
|
|
2273
2497
|
|
|
2274
|
-
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
2498
|
+
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
2275
2499
|
|
|
2276
2500
|
expect(result.AgentSessionId).toBe('session-new');
|
|
2277
2501
|
expect(prepTranscriptArg()).toBeUndefined();
|
|
@@ -2287,7 +2511,7 @@ describe('RealtimeClientSessionResolver.StartRealtimeClientSession — prior-tra
|
|
|
2287
2511
|
};
|
|
2288
2512
|
const resolver = makeResolver();
|
|
2289
2513
|
|
|
2290
|
-
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined,
|
|
2514
|
+
const result = await resolver.StartRealtimeClientSession('target-1', makeCtx(), undefined, PRIOR_1);
|
|
2291
2515
|
|
|
2292
2516
|
expect(result.AgentSessionId).toBe('session-new');
|
|
2293
2517
|
expect(prepTranscriptArg()).toBeUndefined();
|
|
@@ -3106,6 +3330,7 @@ describe('RealtimeClientSessionResolver — delegated runs get the browser sessi
|
|
|
3106
3330
|
ID: 'anon-1',
|
|
3107
3331
|
Email: 'anonymous@magic-link.local',
|
|
3108
3332
|
IsMagicLinkAnonymous: true,
|
|
3333
|
+
AnonymousVisitorID: 'scope-res-1',
|
|
3109
3334
|
MagicLinkScope: { ResourceID: 'scope-res-1' },
|
|
3110
3335
|
} as UserInfo;
|
|
3111
3336
|
/** A resolver whose caller is a scoped anonymous magic-link visitor. */
|
|
@@ -3157,6 +3382,7 @@ describe('RealtimeClientSessionResolver — scoped-anonymous elevation (issue #3
|
|
|
3157
3382
|
ID: 'anon-1',
|
|
3158
3383
|
Email: 'anonymous@magic-link.local',
|
|
3159
3384
|
IsMagicLinkAnonymous: true,
|
|
3385
|
+
AnonymousVisitorID: 'scope-res-1',
|
|
3160
3386
|
MagicLinkScope: { ResourceID: 'scope-res-1' },
|
|
3161
3387
|
};
|
|
3162
3388
|
const SYSTEM_USER = { ID: 'system-1', Email: 'system@system.org' } as UserInfo;
|
|
@@ -3369,6 +3595,33 @@ describe('RealtimeClientSessionResolver — scoped-anonymous elevation (issue #3
|
|
|
3369
3595
|
expect(getEntityObjectSpy()).not.toHaveBeenCalledWith('MJ: Conversation Details', SYSTEM_USER);
|
|
3370
3596
|
});
|
|
3371
3597
|
|
|
3598
|
+
it("does not write a hidden tool-turn Conversation Detail in a conversation the caller doesn't own, while still mirroring to the prompt run", async () => {
|
|
3599
|
+
const session = makeSessionEntity({
|
|
3600
|
+
UserID: 'anon-1',
|
|
3601
|
+
ConversationID: 'conv-shared',
|
|
3602
|
+
Config_: JSON.stringify({ targetAgentID: 'target-1', coAgentRunID: CO_RUN_ID, promptRunID: PROMPT_RUN_ID }),
|
|
3603
|
+
});
|
|
3604
|
+
const conversation = makeSessionEntity({ ID: 'conv-shared', UserID: 'someone-else' });
|
|
3605
|
+
const promptRun = makeSessionEntity({ ID: PROMPT_RUN_ID, Messages: null });
|
|
3606
|
+
const detail = makeSessionEntity({ ID: 'detail-shared-tool' });
|
|
3607
|
+
currentProvider = {
|
|
3608
|
+
GetEntityObject: vi.fn(async (name: string) => {
|
|
3609
|
+
if (name === 'MJ: AI Prompt Runs') return promptRun;
|
|
3610
|
+
if (name === 'MJ: Conversations') return conversation;
|
|
3611
|
+
if (name === 'MJ: Conversation Details') return detail;
|
|
3612
|
+
return session;
|
|
3613
|
+
}),
|
|
3614
|
+
...sessionRunLookups([CO_RUN_ID], PROMPT_RUN_ID),
|
|
3615
|
+
};
|
|
3616
|
+
const resolver = makeAnonResolver();
|
|
3617
|
+
|
|
3618
|
+
const ok = await resolver.RelayRealtimeToolTurn('session-1', 'browser_navigate', makeCtx(), '{"url":"x"}');
|
|
3619
|
+
|
|
3620
|
+
expect(ok).toBe(true);
|
|
3621
|
+
expect(promptRun.Save).toHaveBeenCalled();
|
|
3622
|
+
expect(detail.Save).not.toHaveBeenCalled();
|
|
3623
|
+
});
|
|
3624
|
+
|
|
3372
3625
|
it('prepares the session with observability under the SYSTEM user while UserID stays the visitor', async () => {
|
|
3373
3626
|
hasPermissionMock.mockResolvedValue(true);
|
|
3374
3627
|
currentProvider = makeProvider(() => makeSessionEntity({ UserID: 'anon-1' }));
|
|
@@ -3507,6 +3760,24 @@ describe('RealtimeClientSessionResolver — scoped-anonymous elevation (issue #3
|
|
|
3507
3760
|
expect(deleteSegmentsMock).not.toHaveBeenCalled();
|
|
3508
3761
|
});
|
|
3509
3762
|
|
|
3763
|
+
it('reports a file that uploaded but was not stamped on the session as a failure and keeps the shards', async () => {
|
|
3764
|
+
// The store returns the uploaded file id together with the stamp failure (#6010): the session does not
|
|
3765
|
+
// point at the file, so the crash-recovery shards are the only copy the janitor can still recover.
|
|
3766
|
+
currentProvider = makeRecordingProvider();
|
|
3767
|
+
storeRecordingMock.mockResolvedValue({
|
|
3768
|
+
FileID: 'file-unstamped',
|
|
3769
|
+
ErrorMessage: 'file file-unstamped uploaded and linked, but not stamped on session session-1: proc missing',
|
|
3770
|
+
});
|
|
3771
|
+
const resolver = makeAnonResolver();
|
|
3772
|
+
|
|
3773
|
+
const result = await resolver.UploadRealtimeRecording('session-1', AUDIO_B64, 'audio/wav', makeCtx(), 1000, true);
|
|
3774
|
+
|
|
3775
|
+
expect(result.Success).toBe(false);
|
|
3776
|
+
expect(result.FileID).toBe('file-unstamped');
|
|
3777
|
+
expect(result.ErrorMessage).toContain('not stamped on session session-1');
|
|
3778
|
+
expect(deleteSegmentsMock).not.toHaveBeenCalled();
|
|
3779
|
+
});
|
|
3780
|
+
|
|
3510
3781
|
it('falls back to the generic message only when no layer supplied a reason', async () => {
|
|
3511
3782
|
currentProvider = makeRecordingProvider();
|
|
3512
3783
|
storeRecordingMock.mockResolvedValue({ FileID: null, ErrorMessage: null });
|
|
@@ -3728,6 +3999,7 @@ describe('RealtimeClientSessionResolver — colleague authorization (allowedAgen
|
|
|
3728
3999
|
ID: 'anon-1',
|
|
3729
4000
|
Email: 'anonymous@magic-link.local',
|
|
3730
4001
|
IsMagicLinkAnonymous: true,
|
|
4002
|
+
AnonymousVisitorID: 'scope-res-1',
|
|
3731
4003
|
MagicLinkScope: { ResourceID: 'scope-res-1' },
|
|
3732
4004
|
};
|
|
3733
4005
|
currentProvider = makeProvider(() => makeMultiTargetSession('anon-1'));
|
|
@@ -3842,7 +4114,10 @@ describe('RealtimeClientSessionResolver — conversation-history hydration', ()
|
|
|
3842
4114
|
return { Success: true, Results: [] };
|
|
3843
4115
|
});
|
|
3844
4116
|
const provider = {
|
|
3845
|
-
GetEntityObject: vi.fn(async () => {
|
|
4117
|
+
GetEntityObject: vi.fn(async (entityName: string) => {
|
|
4118
|
+
if (entityName === 'MJ: Conversations') {
|
|
4119
|
+
return makeSessionEntity();
|
|
4120
|
+
}
|
|
3846
4121
|
const entity = makeSessionEntity();
|
|
3847
4122
|
entity.Load = vi.fn(async (id: string) => {
|
|
3848
4123
|
const r = opts.chain?.[id];
|
|
@@ -3996,17 +4271,18 @@ describe('RealtimeClientSessionResolver — conversation-history hydration', ()
|
|
|
3996
4271
|
it('DROPS turns the resumed transcript already framed, so the prompt carries one account of them', async () => {
|
|
3997
4272
|
// The whole reason the leg ids travel with the prior transcript. A voice turn is an ordinary
|
|
3998
4273
|
// Conversation Detail, so without this the resumed leg arrives twice in one prompt.
|
|
4274
|
+
const prior1 = '00000000-0000-0000-0000-000000000501';
|
|
3999
4275
|
const { provider } = makeProvider({
|
|
4000
|
-
chain: {
|
|
4276
|
+
chain: { [prior1]: { UserID: 'user-1' } },
|
|
4001
4277
|
legRows: [{ ID: 'l-1', Role: 'User', Message: 'said on the last call', HiddenToUser: false }],
|
|
4002
4278
|
historyRows: [
|
|
4003
|
-
row('User', 'said on the last call', { AgentSessionID:
|
|
4279
|
+
row('User', 'said on the last call', { AgentSessionID: prior1 }),
|
|
4004
4280
|
row('User', 'typed afterwards'),
|
|
4005
4281
|
],
|
|
4006
4282
|
});
|
|
4007
4283
|
currentProvider = provider;
|
|
4008
4284
|
|
|
4009
|
-
await makeResolver().StartRealtimeClientSession('target-1', makeCtx(), 'conv-1',
|
|
4285
|
+
await makeResolver().StartRealtimeClientSession('target-1', makeCtx(), 'conv-1', prior1);
|
|
4010
4286
|
|
|
4011
4287
|
expect(historyArg().map((m) => m.content)).toEqual(['typed afterwards']);
|
|
4012
4288
|
const prior = (prepareClientSessionMock.mock.calls[0][0] as { PriorTranscript?: string }).PriorTranscript;
|
|
@@ -4016,14 +4292,16 @@ describe('RealtimeClientSessionResolver — conversation-history hydration', ()
|
|
|
4016
4292
|
it('matches covered session ids case-insensitively', async () => {
|
|
4017
4293
|
// SQL Server returns uppercase ids and PostgreSQL lowercase; a case-sensitive compare would
|
|
4018
4294
|
// double-inject the resumed leg on one of the two.
|
|
4295
|
+
const prior1Lower = '00000000-0000-0000-0000-000000000501';
|
|
4296
|
+
const prior1Upper = prior1Lower.toUpperCase();
|
|
4019
4297
|
const { provider } = makeProvider({
|
|
4020
|
-
chain: {
|
|
4298
|
+
chain: { [prior1Upper]: { UserID: 'user-1' } },
|
|
4021
4299
|
legRows: [{ ID: 'l-1', Role: 'User', Message: 'said on the last call', HiddenToUser: false }],
|
|
4022
|
-
historyRows: [row('User', 'said on the last call', { AgentSessionID:
|
|
4300
|
+
historyRows: [row('User', 'said on the last call', { AgentSessionID: prior1Lower })],
|
|
4023
4301
|
});
|
|
4024
4302
|
currentProvider = provider;
|
|
4025
4303
|
|
|
4026
|
-
await makeResolver().StartRealtimeClientSession('target-1', makeCtx(), 'conv-1',
|
|
4304
|
+
await makeResolver().StartRealtimeClientSession('target-1', makeCtx(), 'conv-1', prior1Upper);
|
|
4027
4305
|
|
|
4028
4306
|
expect(historyArg()).toEqual([]);
|
|
4029
4307
|
});
|