@memberjunction/server 6.2.0-edge.6 → 6.2.0-edge.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/BridgeHostInstance.d.ts +8 -3
- package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -1
- package/dist/agentSessions/BridgeHostInstance.js +14 -4
- package/dist/agentSessions/BridgeHostInstance.js.map +1 -1
- package/dist/agentSessions/HandoffOfferExpiry.d.ts +10 -0
- package/dist/agentSessions/HandoffOfferExpiry.d.ts.map +1 -0
- package/dist/agentSessions/HandoffOfferExpiry.js +18 -0
- package/dist/agentSessions/HandoffOfferExpiry.js.map +1 -0
- package/dist/agentSessions/ReturningVisitorRecap.js +1 -0
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +31 -8
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +85 -41
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +14 -4
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +28 -4
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/agentSessions/conversationBrowsers.d.ts +61 -0
- package/dist/agentSessions/conversationBrowsers.d.ts.map +1 -0
- package/dist/agentSessions/conversationBrowsers.js +118 -0
- package/dist/agentSessions/conversationBrowsers.js.map +1 -0
- package/dist/agentSessions/sessionDeadline.d.ts +43 -0
- package/dist/agentSessions/sessionDeadline.d.ts.map +1 -0
- package/dist/agentSessions/sessionDeadline.js +68 -0
- package/dist/agentSessions/sessionDeadline.js.map +1 -0
- package/dist/auth/APIKeyScopeAuth.d.ts.map +1 -1
- package/dist/auth/APIKeyScopeAuth.js +5 -4
- package/dist/auth/APIKeyScopeAuth.js.map +1 -1
- package/dist/auth/loginEmailResolution.d.ts +41 -0
- package/dist/auth/loginEmailResolution.d.ts.map +1 -0
- package/dist/auth/loginEmailResolution.js +47 -0
- package/dist/auth/loginEmailResolution.js.map +1 -0
- package/dist/auth/magicLink/MagicLinkKeys.d.ts +50 -2
- package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.js +129 -12
- package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.d.ts +3 -0
- package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +23 -5
- package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.d.ts +39 -5
- package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +221 -9
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/magicLink/index.d.ts +1 -1
- package/dist/auth/magicLink/index.d.ts.map +1 -1
- package/dist/auth/magicLink/index.js +1 -1
- package/dist/auth/magicLink/index.js.map +1 -1
- package/dist/auth/magicLink/redeemLanding.d.ts +2 -2
- package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
- package/dist/auth/magicLink/redeemLanding.js +7 -4
- package/dist/auth/magicLink/redeemLanding.js.map +1 -1
- package/dist/auth/magicLink/types.d.ts +27 -3
- package/dist/auth/magicLink/types.d.ts.map +1 -1
- package/dist/auth/magicLink/types.js +0 -4
- package/dist/auth/magicLink/types.js.map +1 -1
- package/dist/auth/newUsers.d.ts.map +1 -1
- package/dist/auth/newUsers.js +1 -0
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/backgroundSweepConfigUnits.d.ts +231 -0
- package/dist/backgroundSweepConfigUnits.d.ts.map +1 -0
- package/dist/backgroundSweepConfigUnits.js +180 -0
- package/dist/backgroundSweepConfigUnits.js.map +1 -0
- package/dist/config.d.ts +906 -8
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +35 -4
- package/dist/context.js.map +1 -1
- package/dist/coreRoutes.d.ts +3 -1
- package/dist/coreRoutes.d.ts.map +1 -1
- package/dist/coreRoutes.js +6 -4
- package/dist/coreRoutes.js.map +1 -1
- package/dist/generated/graphql-schemas/__mj.d.ts +228 -0
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +1163 -6
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts +11 -0
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +27 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +50 -5
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +321 -132
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +4 -0
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +47 -7
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +157 -35
- package/dist/index.js.map +1 -1
- package/dist/integration/ConnectionPause.d.ts +121 -0
- package/dist/integration/ConnectionPause.d.ts.map +1 -0
- package/dist/integration/ConnectionPause.js +183 -0
- package/dist/integration/ConnectionPause.js.map +1 -0
- package/dist/integration/CustomColumnPromoter.d.ts +72 -6
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +87 -46
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/integration/EntityMapLifecycle.d.ts +78 -4
- package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
- package/dist/integration/EntityMapLifecycle.js +199 -5
- package/dist/integration/EntityMapLifecycle.js.map +1 -1
- package/dist/integration/InactiveRowWarnings.d.ts +31 -0
- package/dist/integration/InactiveRowWarnings.d.ts.map +1 -1
- package/dist/integration/InactiveRowWarnings.js +29 -0
- package/dist/integration/InactiveRowWarnings.js.map +1 -1
- package/dist/integration/ObjectNamesByID.d.ts +28 -0
- package/dist/integration/ObjectNamesByID.d.ts.map +1 -0
- package/dist/integration/ObjectNamesByID.js +87 -0
- package/dist/integration/ObjectNamesByID.js.map +1 -0
- package/dist/integration/PostRestartIntrospect.d.ts +26 -0
- package/dist/integration/PostRestartIntrospect.d.ts.map +1 -0
- package/dist/integration/PostRestartIntrospect.js +27 -0
- package/dist/integration/PostRestartIntrospect.js.map +1 -0
- package/dist/integration/RSUPostRestartProgress.d.ts +176 -0
- package/dist/integration/RSUPostRestartProgress.d.ts.map +1 -0
- package/dist/integration/RSUPostRestartProgress.js +323 -0
- package/dist/integration/RSUPostRestartProgress.js.map +1 -0
- package/dist/integration/RSUProgressBridge.d.ts +41 -2
- package/dist/integration/RSUProgressBridge.d.ts.map +1 -1
- package/dist/integration/RSUProgressBridge.js +84 -1
- package/dist/integration/RSUProgressBridge.js.map +1 -1
- package/dist/integration/RunArtifactAuthorization.d.ts +39 -0
- package/dist/integration/RunArtifactAuthorization.d.ts.map +1 -0
- package/dist/integration/RunArtifactAuthorization.js +45 -0
- package/dist/integration/RunArtifactAuthorization.js.map +1 -0
- package/dist/integration/SchemaRefreshLaunch.d.ts +9 -0
- package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -1
- package/dist/integration/SchemaRefreshLaunch.js +3 -2
- package/dist/integration/SchemaRefreshLaunch.js.map +1 -1
- package/dist/integration/ScopedFieldsForBuild.d.ts +46 -0
- package/dist/integration/ScopedFieldsForBuild.d.ts.map +1 -0
- package/dist/integration/ScopedFieldsForBuild.js +91 -0
- package/dist/integration/ScopedFieldsForBuild.js.map +1 -0
- package/dist/integration/TargetConfigGuard.d.ts +40 -0
- package/dist/integration/TargetConfigGuard.d.ts.map +1 -0
- package/dist/integration/TargetConfigGuard.js +40 -0
- package/dist/integration/TargetConfigGuard.js.map +1 -0
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.js +2 -3
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
- package/dist/realtimeSessions/sessionAccess.d.ts +15 -0
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
- package/dist/realtimeSessions/sessionAccess.js +22 -0
- package/dist/realtimeSessions/sessionAccess.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js +2 -0
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
- package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
- package/dist/realtimeWidget/visitorIdentity.js +1 -0
- package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetCore.js +4 -7
- package/dist/realtimeWidget/widgetCore.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/requestOrigin.d.ts +22 -0
- package/dist/requestOrigin.d.ts.map +1 -0
- package/dist/requestOrigin.js +60 -0
- package/dist/requestOrigin.js.map +1 -0
- package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/AgentSessionResolver.js +6 -2
- package/dist/resolvers/AgentSessionResolver.js.map +1 -1
- package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
- package/dist/resolvers/AutotagPipelineResolver.js +2 -1
- package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.js +2 -0
- package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +6 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -1
- package/dist/resolvers/ExecuteRemoteOperationResolver.js +30 -7
- package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +34 -13
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +401 -11
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +2690 -1305
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/MCPResolver.d.ts.map +1 -1
- package/dist/resolvers/MCPResolver.js +13 -2
- package/dist/resolvers/MCPResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +36 -0
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +174 -87
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +1 -0
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +6 -0
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +36 -5
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts +62 -10
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +217 -34
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +24 -8
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +185 -67
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.d.ts +5 -3
- package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.js +19 -18
- package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +3 -1
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.js +2 -0
- package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
- package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
- package/dist/rest/MediaAccessKeys.js +6 -2
- package/dist/rest/MediaAccessKeys.js.map +1 -1
- package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
- package/dist/rest/OAuthCallbackHandler.js +11 -5
- package/dist/rest/OAuthCallbackHandler.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 +2 -4
- package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +2 -1
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts +3 -1
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +11 -2
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -1
- package/dist/services/DurableEntityActionTaskSubmitter.js +6 -1
- package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +2 -26
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/services/TaskGraphAgentRunner.d.ts +6 -0
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphAgentRunner.js +38 -0
- package/dist/services/TaskGraphAgentRunner.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/services/WorkQueueHostService.d.ts +2 -1
- package/dist/services/WorkQueueHostService.d.ts.map +1 -1
- package/dist/services/WorkQueueHostService.js +19 -4
- package/dist/services/WorkQueueHostService.js.map +1 -1
- package/dist/services/workQueueConfig.d.ts +2 -2
- package/dist/services/workQueueConfig.js +5 -5
- package/dist/services/workQueueConfig.js.map +1 -1
- package/dist/sharedCache.js +1 -1
- package/dist/sharedCache.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +98 -98
- package/src/__tests__/APIKeyScopeAuth.test.ts +4 -8
- package/src/__tests__/ActiveOperations.test.ts +68 -0
- package/src/__tests__/AgentSessionResolver.ownership.test.ts +13 -0
- package/src/__tests__/AssertTargetConfigs.test.ts +75 -0
- package/src/__tests__/BatchApplyParity.test.ts +109 -0
- package/src/__tests__/BridgeHostInstance.test.ts +42 -0
- package/src/__tests__/CollectInactiveRowWarnings.test.ts +115 -0
- package/src/__tests__/ConnectionPause.test.ts +192 -0
- package/src/__tests__/ConnectionPauseResolver.test.ts +166 -0
- package/src/__tests__/ConnectionPauseSave.test.ts +143 -0
- package/src/__tests__/CustomColumnPromoterCompletion.test.ts +142 -0
- package/src/__tests__/DeleteConnectionRemovesPerConnectionCatalog.test.ts +51 -0
- package/src/__tests__/DetachedDiscoveryStart.test.ts +65 -0
- package/src/__tests__/DurableEntityActionTaskSubmitter.test.ts +73 -0
- package/src/__tests__/EntityMapLifecycle.rekey.test.ts +252 -0
- package/src/__tests__/ExecuteRemoteOperationResolver.test.ts +213 -0
- package/src/__tests__/HandoffOfferExpiry.test.ts +40 -0
- package/src/__tests__/IntegrationRunArtifactAuthorization.test.ts +126 -0
- package/src/__tests__/MagicLinkKeys.test.ts +102 -0
- package/src/__tests__/MediaAccessKeys.test.ts +57 -10
- package/src/__tests__/NewArrivalsDefaultDisabled.test.ts +50 -0
- package/src/__tests__/ObjectNamesByID.test.ts +176 -0
- package/src/__tests__/PostRestartMapsInCatalogScope.test.ts +146 -0
- package/src/__tests__/QuerySystemUserResolver.childRows.test.ts +80 -0
- package/src/__tests__/QuerySystemUserResolver.collision.test.ts +200 -0
- package/src/__tests__/QuerySystemUserResolver.permissions.test.ts +132 -0
- package/src/__tests__/RSUPostRestartProgress.test.ts +343 -0
- package/src/__tests__/RSUProgressBridge.test.ts +188 -5
- package/src/__tests__/RSUStartupSweepWiring.test.ts +30 -0
- package/src/__tests__/ReactivateRefreshMessage.test.ts +10 -3
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +22 -0
- package/src/__tests__/{RefreshAdoptsNewColumns.test.ts → RefreshReportsNewColumns.test.ts} +25 -16
- package/src/__tests__/RemoteBrowserActionRecovery.test.ts +287 -0
- package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +3 -0
- package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +6 -4
- package/src/__tests__/RemoteBrowserAudioStream.test.ts +3 -0
- package/src/__tests__/RemoteBrowserSnapshot.test.ts +3 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +323 -26
- package/src/__tests__/ResolverBase.encryptedApiZeroReencrypt.test.ts +307 -0
- package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +53 -2
- package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +60 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +4 -0
- package/src/__tests__/RunAIAgentResolver.consultPredecessor.test.ts +289 -0
- package/src/__tests__/RunAIAgentResolver.handoff.test.ts +649 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +2 -0
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +2 -0
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +87 -12
- package/src/__tests__/SchemaEvolutionCustomColumns.test.ts +119 -0
- package/src/__tests__/SchemaRefreshLaunch.test.ts +24 -2
- package/src/__tests__/ScopedFieldsForBuild.test.ts +192 -0
- package/src/__tests__/SessionJanitor.test.ts +195 -4
- package/src/__tests__/SessionManager.test.ts +69 -0
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +2 -2
- package/src/__tests__/TaskGraphAgentRunner.test.ts +272 -16
- package/src/__tests__/ViewOperationsHandler.test.ts +2 -5
- package/src/__tests__/WorkQueueHostService.test.ts +25 -7
- package/src/__tests__/backgroundSweepConfigUnits.test.ts +153 -0
- package/src/__tests__/contextRequestOrigin.test.ts +105 -0
- package/src/__tests__/conversationBrowsers.test.ts +62 -0
- package/src/__tests__/magicLinkExtensionCapabilities.test.ts +413 -0
- package/src/__tests__/orphanedConversationDetailReconciler.start.test.ts +82 -0
- package/src/__tests__/ownedRealtimeSession.test.ts +14 -0
- package/src/__tests__/realtimeRecordingHandler.test.ts +7 -2
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +79 -1
- package/src/__tests__/requestOrigin.test.ts +104 -0
- package/src/__tests__/securityHardeningPr6034.test.ts +120 -0
- package/src/__tests__/sessionDeadline.test.ts +51 -0
- package/src/__tests__/sharedCache.test.ts +11 -0
- package/src/__tests__/unifiedAuth.test.ts +73 -1
- package/src/agentSessions/BridgeHostInstance.ts +20 -5
- package/src/agentSessions/HandoffOfferExpiry.ts +18 -0
- package/src/agentSessions/ReturningVisitorRecap.ts +1 -0
- package/src/agentSessions/SessionJanitor.ts +92 -42
- package/src/agentSessions/SessionManager.ts +38 -8
- package/src/agentSessions/conversationBrowsers.ts +143 -0
- package/src/agentSessions/sessionDeadline.ts +81 -0
- package/src/auth/APIKeyScopeAuth.ts +5 -4
- package/src/auth/__tests__/loginEmailResolution.test.ts +67 -0
- package/src/auth/loginEmailResolution.ts +67 -0
- package/src/auth/magicLink/MagicLinkKeys.ts +146 -14
- package/src/auth/magicLink/MagicLinkRouter.ts +33 -6
- package/src/auth/magicLink/MagicLinkService.ts +250 -10
- package/src/auth/magicLink/index.ts +1 -1
- package/src/auth/magicLink/redeemLanding.ts +7 -4
- package/src/auth/magicLink/types.ts +28 -3
- package/src/auth/newUsers.ts +1 -0
- package/src/backgroundSweepConfigUnits.ts +213 -0
- package/src/config.ts +69 -0
- package/src/context.ts +39 -6
- package/src/coreRoutes.ts +14 -7
- package/src/generated/graphql-schemas/__mj.ts +822 -7
- package/src/generic/OrphanedConversationDetailReconciler.ts +32 -1
- package/src/generic/ResolverBase.ts +414 -143
- package/src/generic/RunViewResolver.ts +96 -49
- package/src/index.ts +201 -35
- package/src/integration/ConnectionPause.ts +222 -0
- package/src/integration/CustomColumnPromoter.ts +123 -53
- package/src/integration/EntityMapLifecycle.ts +229 -6
- package/src/integration/InactiveRowWarnings.ts +50 -0
- package/src/integration/ObjectNamesByID.ts +97 -0
- package/src/integration/PostRestartIntrospect.ts +49 -0
- package/src/integration/RSUPostRestartProgress.ts +359 -0
- package/src/integration/RSUProgressBridge.ts +85 -2
- package/src/integration/RunArtifactAuthorization.ts +65 -0
- package/src/integration/SchemaRefreshLaunch.ts +12 -2
- package/src/integration/ScopedFieldsForBuild.ts +113 -0
- package/src/integration/TargetConfigGuard.ts +71 -0
- package/src/realtimeSessions/ownedRealtimeSession.ts +2 -6
- package/src/realtimeSessions/sessionAccess.ts +30 -0
- package/src/realtimeWidget/WidgetSessionService.ts +2 -0
- package/src/realtimeWidget/visitorIdentity.ts +1 -0
- package/src/realtimeWidget/widgetCore.ts +4 -8
- package/src/realtimeWidget/widgetGuestElevation.ts +1 -0
- package/src/requestOrigin.ts +64 -0
- package/src/resolvers/AgentSessionResolver.ts +6 -2
- package/src/resolvers/AutotagPipelineResolver.ts +2 -1
- package/src/resolvers/ConversationRunEventsResolver.ts +2 -0
- package/src/resolvers/ExecuteRemoteOperationResolver.ts +41 -9
- package/src/resolvers/GetDataResolver.ts +35 -13
- package/src/resolvers/IntegrationDiscoveryResolver.ts +1522 -203
- package/src/resolvers/MCPResolver.ts +15 -2
- package/src/resolvers/QuerySystemUserResolver.ts +197 -98
- package/src/resolvers/RealtimeBridgeResolver.ts +1 -0
- package/src/resolvers/RealtimeClientSessionResolver.ts +40 -7
- package/src/resolvers/RemoteBrowserActionResolver.ts +242 -32
- package/src/resolvers/RunAIAgentResolver.ts +265 -86
- package/src/resolvers/RunAIPromptResolver.ts +18 -18
- package/src/resolvers/RunTemplateResolver.ts +3 -1
- package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +52 -0
- package/src/resolvers/meetingRecordingRegistration.ts +2 -0
- package/src/rest/MediaAccessKeys.ts +6 -2
- package/src/rest/OAuthCallbackHandler.ts +13 -5
- package/src/rest/RealtimeRecordingHandler.ts +5 -9
- package/src/rest/ViewOperationsHandler.ts +2 -1
- package/src/rest/setupRESTEndpoints.ts +11 -2
- package/src/services/DurableEntityActionTaskSubmitter.ts +10 -2
- package/src/services/TaskGraphActionRunner.ts +2 -23
- package/src/services/TaskGraphAgentRunner.ts +43 -0
- package/src/services/TaskGraphContinuationDeliverer.ts +1 -0
- package/src/services/WorkQueueHostService.ts +26 -4
- package/src/services/workQueueConfig.ts +5 -5
- package/src/sharedCache.ts +1 -1
- package/src/types.ts +18 -0
- package/src/services/TaskOrchestration-Integration.md +0 -188
|
@@ -23,6 +23,19 @@ import { PublishStatusUpdate } from '../generic/PushStatusResolver.js';
|
|
|
23
23
|
import { GraphQLJSONObject } from 'graphql-type-json';
|
|
24
24
|
import { configInfo } from '../config.js';
|
|
25
25
|
|
|
26
|
+
const UUID_SHAPE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Validates that a caller-supplied record id is a UUID before it is interpolated into a SQL
|
|
30
|
+
* filter, and returns it unchanged. A validated UUID cannot carry SQL.
|
|
31
|
+
*/
|
|
32
|
+
function sqlUuid(value: string, argName: string): string {
|
|
33
|
+
if (typeof value !== 'string' || !UUID_SHAPE.test(value)) {
|
|
34
|
+
throw new Error(`${argName} must be a UUID`);
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
|
|
26
39
|
/**
|
|
27
40
|
* Input type for syncing MCP tools
|
|
28
41
|
*/
|
|
@@ -1289,7 +1302,7 @@ export class MCPResolver extends ResolverBase {
|
|
|
1289
1302
|
// First get connection to get server ID
|
|
1290
1303
|
const connResult = await rv.RunView<{ MCPServerID: string }>({
|
|
1291
1304
|
EntityName: 'MJ: MCP Server Connections',
|
|
1292
|
-
ExtraFilter: `ID='${connectionId}'`,
|
|
1305
|
+
ExtraFilter: `ID='${sqlUuid(connectionId, 'connectionId')}'`,
|
|
1293
1306
|
Fields: ['MCPServerID'],
|
|
1294
1307
|
ResultType: 'simple'
|
|
1295
1308
|
}, contextUser);
|
|
@@ -1310,7 +1323,7 @@ export class MCPResolver extends ResolverBase {
|
|
|
1310
1323
|
OAuthRequirePKCE: boolean | null;
|
|
1311
1324
|
}>({
|
|
1312
1325
|
EntityName: 'MJ: MCP Servers',
|
|
1313
|
-
ExtraFilter: `ID='${serverId}'`,
|
|
1326
|
+
ExtraFilter: `ID='${sqlUuid(serverId, 'serverId')}'`,
|
|
1314
1327
|
Fields: [
|
|
1315
1328
|
'OAuthIssuerURL',
|
|
1316
1329
|
'OAuthScopes',
|
|
@@ -6,7 +6,7 @@ import { MJQueryResolver, MJQuery_, MJQueryField_, MJQueryParameter_, MJQueryEnt
|
|
|
6
6
|
import { GetReadWriteProvider } from '../util.js';
|
|
7
7
|
import { DeleteOptionsInput } from '../generic/DeleteOptionsInput.js';
|
|
8
8
|
import { MJQueryEntityServer } from '@memberjunction/core-entities-server';
|
|
9
|
-
import { EscapeSQLString } from '@memberjunction/global';
|
|
9
|
+
import { EscapeSQLString, UUIDsEqual } from '@memberjunction/global';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Minimal shape of a query row returned by RunView lookups (plain object, not entity instance).
|
|
@@ -243,6 +243,14 @@ export class DeleteQueryResultType {
|
|
|
243
243
|
Status?: string;
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
+
/** A query's child rows, read from the database. */
|
|
247
|
+
interface StoredQueryChildren {
|
|
248
|
+
readonly Fields: MJQueryFieldEntity[];
|
|
249
|
+
readonly Parameters: MJQueryParameterEntity[];
|
|
250
|
+
readonly Entities: MJQueryEntityEntity[];
|
|
251
|
+
readonly Permissions: MJQueryPermissionEntity[];
|
|
252
|
+
}
|
|
253
|
+
|
|
246
254
|
@Resolver()
|
|
247
255
|
export class MJQueryResolverExtended extends MJQueryResolver {
|
|
248
256
|
/**
|
|
@@ -269,6 +277,7 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
269
277
|
}
|
|
270
278
|
|
|
271
279
|
// Check for existing query with same name in the same category
|
|
280
|
+
const requestedName = input.Name;
|
|
272
281
|
const existingQuery = await this.findExistingQuery(provider, input.Name, finalCategoryID, context.userPayload.userRecord);
|
|
273
282
|
|
|
274
283
|
if (existingQuery) {
|
|
@@ -285,84 +294,118 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
285
294
|
}
|
|
286
295
|
}
|
|
287
296
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
Status: input.Status || 'Approved',
|
|
296
|
-
QualityRank: input.QualityRank || 0,
|
|
297
|
-
UsesTemplate: input.UsesTemplate || false,
|
|
298
|
-
AuditQueryRuns: input.AuditQueryRuns || false,
|
|
299
|
-
CacheEnabled: input.CacheEnabled || false,
|
|
300
|
-
CacheTTLMinutes: input.CacheTTLMinutes || null,
|
|
301
|
-
CacheMaxSize: input.CacheMaxSize || null
|
|
297
|
+
return await this.createQueryAsync(provider, input, requestedName, finalCategoryID, context, pubSub);
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
LogError(err);
|
|
301
|
+
return {
|
|
302
|
+
Success: false,
|
|
303
|
+
ErrorMessage: `MJQueryResolverExtended::CreateQuerySystemUser --- Error creating query: ${err instanceof Error ? err.message : String(err)}`
|
|
302
304
|
};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
303
307
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
record.SetMany(fieldsToSet, true);
|
|
308
|
+
/** The most times a create with `AutoResolveCollision` picks a new name after losing a same-name race. */
|
|
309
|
+
private static readonly maxCollisionRetries = 25;
|
|
307
310
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
+
/**
|
|
312
|
+
* Creates the query, handling a same-name race (#6007): when the insert fails and a query with
|
|
313
|
+
* the name now exists (another request created it in between), `AutoResolveCollision` picks a
|
|
314
|
+
* new unique name and tries again, a bounded number of times, and never returns the other
|
|
315
|
+
* request's query. Without it, the query that now exists is returned (create-or-get). Any other
|
|
316
|
+
* failure, including a save-time refusal of the SQL, returns the save's message.
|
|
317
|
+
*
|
|
318
|
+
* @param requestedName - The name the caller asked for; each retry's unique name is built from it.
|
|
319
|
+
*/
|
|
320
|
+
private async createQueryAsync(
|
|
321
|
+
provider: DatabaseProviderBase,
|
|
322
|
+
input: CreateQuerySystemUserInput,
|
|
323
|
+
requestedName: string,
|
|
324
|
+
categoryID: string | null | undefined,
|
|
325
|
+
context: AppContext,
|
|
326
|
+
pubSub: PubSubEngine
|
|
327
|
+
): Promise<QueryMutationResultType> {
|
|
328
|
+
const user = context.userPayload.userRecord;
|
|
329
|
+
for (let attempt = 0; ; attempt++) {
|
|
330
|
+
const record = await this.newQueryRecordAsync(provider, input, categoryID, context, pubSub);
|
|
331
|
+
if (await record.Save()) {
|
|
332
|
+
return this.completeCreateAsync(provider, input, record, user);
|
|
311
333
|
}
|
|
334
|
+
const existing = await this.findExistingQuery(provider, input.Name, categoryID ?? null, user);
|
|
335
|
+
if (!existing) {
|
|
336
|
+
return { Success: false, ErrorMessage: `Failed to create query: ${record.LatestResult?.CompleteMessage || record.LatestResult?.Message || 'the save reported no reason'}` };
|
|
337
|
+
}
|
|
338
|
+
if (!input.AutoResolveCollision) {
|
|
339
|
+
return this.existingQueryResultAsync(provider, existing, input.Name, user);
|
|
340
|
+
}
|
|
341
|
+
if (attempt >= MJQueryResolverExtended.maxCollisionRetries) {
|
|
342
|
+
return { Success: false, ErrorMessage: `Failed to create query '${input.Name}': another request took each unique name tried, ${attempt + 1} times.` };
|
|
343
|
+
}
|
|
344
|
+
const lost = input.Name;
|
|
345
|
+
input.Name = await this.findUniqueName(provider, requestedName, categoryID ?? null, user);
|
|
346
|
+
LogStatus(`[CreateQuery] Lost a name race for "${lost}"; retrying as "${input.Name}"`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
312
349
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
350
|
+
/** A new, unsaved query record holding the input's values. */
|
|
351
|
+
private async newQueryRecordAsync(
|
|
352
|
+
provider: DatabaseProviderBase,
|
|
353
|
+
input: CreateQuerySystemUserInput,
|
|
354
|
+
categoryID: string | null | undefined,
|
|
355
|
+
context: AppContext,
|
|
356
|
+
pubSub: PubSubEngine
|
|
357
|
+
): Promise<MJQueryEntityServer> {
|
|
358
|
+
const record = await provider.GetEntityObject<MJQueryEntityServer>("MJ: Queries", context.userPayload.userRecord);
|
|
359
|
+
// Destructure out non-database fields, keep only fields to persist
|
|
360
|
+
const { Permissions: _permissions, CategoryPath: _categoryPath, ParameterHints: _parameterHints, AutoResolveCollision: _autoResolve, ...fieldsToSet } = {
|
|
361
|
+
...input,
|
|
362
|
+
CategoryID: categoryID || input.CategoryID,
|
|
363
|
+
Status: input.Status || 'Approved',
|
|
364
|
+
QualityRank: input.QualityRank || 0,
|
|
365
|
+
UsesTemplate: input.UsesTemplate || false,
|
|
366
|
+
AuditQueryRuns: input.AuditQueryRuns || false,
|
|
367
|
+
CacheEnabled: input.CacheEnabled || false,
|
|
368
|
+
CacheTTLMinutes: input.CacheTTLMinutes || null,
|
|
369
|
+
CacheMaxSize: input.CacheMaxSize || null
|
|
370
|
+
};
|
|
371
|
+
// Field-level security: refuse a key the caller may not read, or that names no field
|
|
372
|
+
AssertClientInputWritableForUser(record.EntityInfo, fieldsToSet, context.userPayload.userRecord);
|
|
373
|
+
record.SetMany(fieldsToSet, true);
|
|
374
|
+
// Pass caller-provided parameter sample values (used for new parameters and the described renders)
|
|
375
|
+
if (input.ParameterHints && input.ParameterHints.length > 0) {
|
|
376
|
+
record.ParameterHints = new Map(input.ParameterHints.map(h => [h.Name, h.Value]));
|
|
377
|
+
}
|
|
378
|
+
this.ListenForEntityMessages(record, pubSub, context.userPayload);
|
|
379
|
+
return record;
|
|
380
|
+
}
|
|
328
381
|
|
|
329
|
-
|
|
382
|
+
/** After a successful create: the AfterCreate event, the permissions, and the result. */
|
|
383
|
+
private async completeCreateAsync(provider: DatabaseProviderBase, input: CreateQuerySystemUserInput, record: MJQueryEntityServer, user: UserInfo): Promise<QueryMutationResultType> {
|
|
384
|
+
await this.AfterCreate(provider, input); // fire event
|
|
385
|
+
if (input.Permissions && input.Permissions.length > 0) {
|
|
386
|
+
try {
|
|
387
|
+
await this.createPermissions(provider, input.Permissions, record.ID, user);
|
|
388
|
+
} catch (e) {
|
|
389
|
+
return { Success: false, ErrorMessage: `Query '${record.Name}' (ID ${record.ID}) was created, but its permissions were not: ${e instanceof Error ? e.message : String(e)}` };
|
|
330
390
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const existingQuery = await this.findExistingQuery(provider, input.Name, finalCategoryID, context.userPayload.userRecord);
|
|
335
|
-
|
|
336
|
-
if (existingQuery) {
|
|
337
|
-
// Found the query that was created by another request — load it as a full entity
|
|
338
|
-
// so that related metadata (Fields, Parameters, Entities, Permissions) is populated
|
|
339
|
-
LogStatus(`[CreateQuery] Unique constraint detected for query '${input.Name}'. Using existing query (ID: ${existingQuery.ID}) created by concurrent request.`);
|
|
340
|
-
const existingEntity = await provider.GetEntityObject<MJQueryEntityServer>('MJ: Queries', context.userPayload.userRecord);
|
|
341
|
-
if (await existingEntity.Load(existingQuery.ID)) {
|
|
342
|
-
return await this.buildSuccessResult(existingEntity, context.userPayload.userRecord, provider);
|
|
343
|
-
}
|
|
344
|
-
// Entity load failed after confirming the row exists — extremely rare
|
|
345
|
-
return {
|
|
346
|
-
Success: false,
|
|
347
|
-
ErrorMessage: `Found query '${input.Name}' created by concurrent request (ID: ${existingQuery.ID}) but failed to load it as entity`
|
|
348
|
-
};
|
|
349
|
-
}
|
|
391
|
+
}
|
|
392
|
+
return this.buildSuccessResult(record, user, provider);
|
|
393
|
+
}
|
|
350
394
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
LogError(err);
|
|
361
|
-
return {
|
|
362
|
-
Success: false,
|
|
363
|
-
ErrorMessage: `MJQueryResolverExtended::CreateQuerySystemUser --- Error creating query: ${err instanceof Error ? err.message : String(err)}`
|
|
364
|
-
};
|
|
395
|
+
/** The create-or-get result: the query another request created with this name. */
|
|
396
|
+
private async existingQueryResultAsync(provider: DatabaseProviderBase, existing: QueryViewRow, name: string, user: UserInfo): Promise<QueryMutationResultType> {
|
|
397
|
+
LogStatus(`[CreateQuery] Unique constraint detected for query '${name}'. Using existing query (ID: ${existing.ID}) created by concurrent request.`);
|
|
398
|
+
const existingEntity = await provider.GetEntityObject<MJQueryEntityServer>('MJ: Queries', user);
|
|
399
|
+
if (await existingEntity.Load(existing.ID)) {
|
|
400
|
+
// The other request may have run on another server, or its rows may not have reached this
|
|
401
|
+
// process's query cache yet, so the child rows are read from the database.
|
|
402
|
+
const children = await this.readStoredChildrenAsync(provider, existing.ID, user);
|
|
403
|
+
return this.buildSuccessResult(existingEntity, user, provider, children);
|
|
365
404
|
}
|
|
405
|
+
return {
|
|
406
|
+
Success: false,
|
|
407
|
+
ErrorMessage: `Found query '${name}' created by concurrent request (ID: ${existing.ID}) but failed to load it as entity`
|
|
408
|
+
};
|
|
366
409
|
}
|
|
367
410
|
|
|
368
411
|
/**
|
|
@@ -372,17 +415,39 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
372
415
|
*
|
|
373
416
|
* @param contextUser The caller the result is for. Field security projects the record for them.
|
|
374
417
|
*/
|
|
375
|
-
private async buildSuccessResult(entity: MJQueryEntityServer, contextUser: UserInfo, provider: DatabaseProviderBase): Promise<QueryMutationResultType> {
|
|
418
|
+
private async buildSuccessResult(entity: MJQueryEntityServer, contextUser: UserInfo, provider: DatabaseProviderBase, children?: StoredQueryChildren): Promise<QueryMutationResultType> {
|
|
376
419
|
return {
|
|
377
420
|
Success: true,
|
|
378
421
|
// Mapped for the caller, so the field-security projection applies: the record was just
|
|
379
422
|
// saved, and a save re-reads the whole row.
|
|
380
423
|
// fls-guard: projected for contextUser by MapFieldNamesToCodeNames
|
|
381
424
|
Query: await this.MapFieldNamesToCodeNames('MJ: Queries', entity.GetAll(), contextUser, provider) as MJQuery_,
|
|
382
|
-
Fields: this.mapFields(entity.QueryFields),
|
|
383
|
-
Parameters: this.mapParameters(entity.QueryParameters),
|
|
384
|
-
Entities: this.mapEntities(entity.QueryEntities),
|
|
385
|
-
Permissions: this.mapPermissions(entity.QueryPermissions),
|
|
425
|
+
Fields: this.mapFields(children?.Fields ?? entity.QueryFields),
|
|
426
|
+
Parameters: this.mapParameters(children?.Parameters ?? entity.QueryParameters),
|
|
427
|
+
Entities: this.mapEntities(children?.Entities ?? entity.QueryEntities),
|
|
428
|
+
Permissions: this.mapPermissions(children?.Permissions ?? entity.QueryPermissions),
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* A query's child rows read from the database with every cache bypassed. A collection whose
|
|
434
|
+
* read fails comes back empty, as the cache would have given it.
|
|
435
|
+
*/
|
|
436
|
+
private async readStoredChildrenAsync(provider: DatabaseProviderBase, queryID: string, user: UserInfo): Promise<StoredQueryChildren> {
|
|
437
|
+
const filter = `QueryID='${EscapeSQLString(queryID)}'`;
|
|
438
|
+
const view = (entityName: string, orderBy?: string) => ({ EntityName: entityName, ExtraFilter: filter, OrderBy: orderBy, ResultType: 'entity_object' as const, BypassCache: true, IgnoreMaxRows: true });
|
|
439
|
+
const [fields, parameters, entities, permissions] = await provider.RunViews([
|
|
440
|
+
view('MJ: Query Fields', 'Sequence'),
|
|
441
|
+
view('MJ: Query Parameters', 'Name'),
|
|
442
|
+
view('MJ: Query Entities'),
|
|
443
|
+
view('MJ: Query Permissions'),
|
|
444
|
+
], user);
|
|
445
|
+
const rows = <T>(result: { Success: boolean; Results: T[] } | undefined): T[] => (result?.Success ? result.Results : []);
|
|
446
|
+
return {
|
|
447
|
+
Fields: rows<MJQueryFieldEntity>(fields),
|
|
448
|
+
Parameters: rows<MJQueryParameterEntity>(parameters),
|
|
449
|
+
Entities: rows<MJQueryEntityEntity>(entities),
|
|
450
|
+
Permissions: rows<MJQueryPermissionEntity>(permissions),
|
|
386
451
|
};
|
|
387
452
|
}
|
|
388
453
|
|
|
@@ -398,6 +463,10 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
398
463
|
SourceEntityID: f.SourceEntityID,
|
|
399
464
|
SourceEntity: f.SourceEntity,
|
|
400
465
|
SourceFieldName: f.SourceFieldName,
|
|
466
|
+
SourceEntityFieldID: f.SourceEntityFieldID,
|
|
467
|
+
SourceEntityField: f.SourceEntityField,
|
|
468
|
+
SourceObjectName: f.SourceObjectName,
|
|
469
|
+
IsNullable: f.IsNullable,
|
|
401
470
|
IsComputed: f.IsComputed,
|
|
402
471
|
ComputationDescription: f.ComputationDescription,
|
|
403
472
|
IsSummary: f.IsSummary,
|
|
@@ -421,6 +490,7 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
421
490
|
DefaultValue: p.DefaultValue,
|
|
422
491
|
SampleValue: p.SampleValue,
|
|
423
492
|
ValidationFilters: p.ValidationFilters,
|
|
493
|
+
IsServerSet: p.IsServerSet,
|
|
424
494
|
_mj__CreatedAt: p.__mj_CreatedAt,
|
|
425
495
|
_mj__UpdatedAt: p.__mj_UpdatedAt,
|
|
426
496
|
DetectionMethod: p.DetectionMethod,
|
|
@@ -435,6 +505,8 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
435
505
|
QueryID: e.QueryID,
|
|
436
506
|
EntityID: e.EntityID,
|
|
437
507
|
Entity: e.Entity,
|
|
508
|
+
AlternateBaseViewID: e.AlternateBaseViewID,
|
|
509
|
+
AlternateBaseView: e.AlternateBaseView,
|
|
438
510
|
_mj__CreatedAt: e.__mj_CreatedAt,
|
|
439
511
|
_mj__UpdatedAt: e.__mj_UpdatedAt,
|
|
440
512
|
DetectionMethod: e.DetectionMethod,
|
|
@@ -455,17 +527,43 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
455
527
|
}) as MJQueryPermission_);
|
|
456
528
|
}
|
|
457
529
|
|
|
530
|
+
/**
|
|
531
|
+
* Saves one `MJ: Query Permissions` row per input role for the query.
|
|
532
|
+
*
|
|
533
|
+
* @throws Error naming the role and the save's own message when a row is not saved
|
|
534
|
+
*/
|
|
458
535
|
protected async createPermissions(p: DatabaseProviderBase, permissions: QueryPermissionInputType[], queryID: string, contextUser: UserInfo): Promise<void> {
|
|
459
536
|
for (const perm of permissions) {
|
|
460
537
|
const permissionEntity = await p.GetEntityObject<MJQueryPermissionEntity>('MJ: Query Permissions', contextUser);
|
|
461
538
|
if (permissionEntity) {
|
|
462
539
|
permissionEntity.QueryID = queryID;
|
|
463
540
|
permissionEntity.RoleID = perm.RoleID;
|
|
464
|
-
await permissionEntity.Save()
|
|
541
|
+
if (!await permissionEntity.Save()) {
|
|
542
|
+
throw new Error(MJQueryResolverExtended.permissionWriteError('save', perm.RoleID, permissionEntity));
|
|
543
|
+
}
|
|
465
544
|
}
|
|
466
545
|
}
|
|
467
546
|
}
|
|
468
547
|
|
|
548
|
+
/**
|
|
549
|
+
* Deletes every `MJ: Query Permissions` row of the query.
|
|
550
|
+
*
|
|
551
|
+
* @throws Error naming the role and the delete's own message when a row is not deleted
|
|
552
|
+
*/
|
|
553
|
+
protected async deletePermissions(queryID: string): Promise<void> {
|
|
554
|
+
for (const perm of QueryEngine.Instance.GetQueryPermissions(queryID)) {
|
|
555
|
+
if (!await perm.Delete()) {
|
|
556
|
+
throw new Error(MJQueryResolverExtended.permissionWriteError('delete', perm.RoleID, perm));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/** The message for a permission row that was not saved or deleted, with the entity's own reason. */
|
|
562
|
+
private static permissionWriteError(action: 'save' | 'delete', roleID: string, entity: MJQueryPermissionEntity): string {
|
|
563
|
+
const reason = entity.LatestResult?.CompleteMessage || entity.LatestResult?.Message || `the ${action} reported no reason`;
|
|
564
|
+
return `Failed to ${action} the query permission for role ${roleID}: ${reason}`;
|
|
565
|
+
}
|
|
566
|
+
|
|
469
567
|
/**
|
|
470
568
|
* Updates an existing query with the provided attributes. Requires `query:update` scope for API key users;
|
|
471
569
|
* entity-level update permission on MJ: Queries for all users.
|
|
@@ -498,16 +596,14 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
498
596
|
finalCategoryID = await this.findOrCreateCategoryPath(input.CategoryPath, provider, context.userPayload.userRecord);
|
|
499
597
|
}
|
|
500
598
|
|
|
501
|
-
//
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
if (existingQueryResult.Success && existingQueryResult.Results?.length > 0) {
|
|
507
|
-
// we have a match! Let's return an error
|
|
599
|
+
// A rename, or a move to another category, must not collide with another query's name
|
|
600
|
+
const targetName = input.Name ?? queryEntity.Name;
|
|
601
|
+
const targetCategoryID = finalCategoryID !== undefined ? finalCategoryID : queryEntity.CategoryID;
|
|
602
|
+
const sameName = await this.findExistingQuery(provider, targetName, targetCategoryID ?? null, context.userPayload.userRecord);
|
|
603
|
+
if (sameName && !UUIDsEqual(sameName.ID, queryEntity.ID)) {
|
|
508
604
|
return {
|
|
509
605
|
Success: false,
|
|
510
|
-
ErrorMessage: `Query with name '${
|
|
606
|
+
ErrorMessage: `Query with name '${targetName}' already exists in the specified ${input.CategoryID ? 'category' : 'categoryPath'}`
|
|
511
607
|
};
|
|
512
608
|
}
|
|
513
609
|
|
|
@@ -545,7 +641,7 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
545
641
|
if (!saveResult) {
|
|
546
642
|
return {
|
|
547
643
|
Success: false,
|
|
548
|
-
ErrorMessage: `Failed to update query: ${queryEntity.LatestResult?.Message || '
|
|
644
|
+
ErrorMessage: `Failed to update query: ${queryEntity.LatestResult?.CompleteMessage || queryEntity.LatestResult?.Message || 'the save reported no reason'}`
|
|
549
645
|
};
|
|
550
646
|
}
|
|
551
647
|
|
|
@@ -553,14 +649,13 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
553
649
|
|
|
554
650
|
// Handle permissions update if provided
|
|
555
651
|
if (input.Permissions !== undefined) {
|
|
556
|
-
//
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
await
|
|
652
|
+
// Replace the existing permissions (read from the QueryEngine cache); a failed write stops the update with its reason
|
|
653
|
+
try {
|
|
654
|
+
await this.deletePermissions(queryID);
|
|
655
|
+
await this.createPermissions(provider, input.Permissions, queryID, context.userPayload.userRecord);
|
|
656
|
+
} catch (e) {
|
|
657
|
+
return { Success: false, ErrorMessage: `Failed to update query permissions: ${e instanceof Error ? e.message : String(e)}` };
|
|
560
658
|
}
|
|
561
|
-
|
|
562
|
-
// Create new permissions
|
|
563
|
-
await this.createPermissions(provider, input.Permissions, queryID, context.userPayload.userRecord);
|
|
564
659
|
}
|
|
565
660
|
|
|
566
661
|
return await this.buildSuccessResult(queryEntity, context.userPayload.userRecord, provider);
|
|
@@ -740,12 +835,13 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
740
835
|
): Promise<QueryViewRow | null> {
|
|
741
836
|
try {
|
|
742
837
|
// Query database directly to avoid cache staleness issues
|
|
743
|
-
const categoryFilter = categoryID ? `CategoryID='${categoryID}'` : 'CategoryID IS NULL';
|
|
744
|
-
const nameFilter = `LOWER(Name) = LOWER('${queryName
|
|
838
|
+
const categoryFilter = categoryID ? `CategoryID='${EscapeSQLString(categoryID)}'` : 'CategoryID IS NULL';
|
|
839
|
+
const nameFilter = `LOWER(Name) = LOWER('${EscapeSQLString(queryName)}')`;
|
|
745
840
|
|
|
746
841
|
const result = await provider.RunView<QueryViewRow>({
|
|
747
842
|
EntityName: 'MJ: Queries',
|
|
748
|
-
ExtraFilter: `${nameFilter} AND ${categoryFilter}
|
|
843
|
+
ExtraFilter: `${nameFilter} AND ${categoryFilter}`,
|
|
844
|
+
BypassCache: true
|
|
749
845
|
}, contextUser);
|
|
750
846
|
|
|
751
847
|
if (result.Success && result.Results && result.Results.length > 0) {
|
|
@@ -797,13 +893,16 @@ export class MJQueryResolverExtended extends MJQueryResolver {
|
|
|
797
893
|
private async findCategoryByNameAndParent(provider: DatabaseProviderBase, categoryName: string, parentID: string | null, contextUser: UserInfo): Promise<MJQueryCategoryEntity | null> {
|
|
798
894
|
try {
|
|
799
895
|
// Query database directly to avoid cache staleness issues
|
|
800
|
-
const parentFilter = parentID ? `ParentID='${parentID}'` : 'ParentID IS NULL';
|
|
801
|
-
const nameFilter = `LOWER(Name) = LOWER('${categoryName
|
|
896
|
+
const parentFilter = parentID ? `ParentID='${EscapeSQLString(parentID)}'` : 'ParentID IS NULL';
|
|
897
|
+
const nameFilter = `LOWER(Name) = LOWER('${EscapeSQLString(categoryName)}')`;
|
|
802
898
|
|
|
899
|
+
// Uncached: after losing a race to create the category, the re-check must see the row the
|
|
900
|
+
// other request just wrote.
|
|
803
901
|
const result = await provider.RunView<MJQueryCategoryEntity>({
|
|
804
902
|
EntityName: 'MJ: Query Categories',
|
|
805
903
|
ExtraFilter: `${nameFilter} AND ${parentFilter}`,
|
|
806
|
-
ResultType: 'entity_object'
|
|
904
|
+
ResultType: 'entity_object',
|
|
905
|
+
BypassCache: true
|
|
807
906
|
}, contextUser);
|
|
808
907
|
|
|
809
908
|
if (result.Success && result.Results && result.Results.length > 0) {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
import { Resolver, Mutation, Arg, Ctx, Int, Float, ObjectType, Field, PubSub, PubSubEngine } from 'type-graphql';
|
|
35
35
|
import { AppContext, UserPayload } from '../types.js';
|
|
36
36
|
import { AuthorizationEvaluator, UserInfo, IMetadataProvider, DatabaseProviderBase, LogError, LogErrorEx, LogStatus, RunView, BaseEntity } from '@memberjunction/core';
|
|
37
|
-
import { UUIDsEqual, IsValidUUID } from '@memberjunction/global';
|
|
37
|
+
import { EscapeSQLString, UUIDsEqual, IsValidUUID } from '@memberjunction/global';
|
|
38
38
|
import {
|
|
39
39
|
MJAIAgentEntity,
|
|
40
40
|
MJAIAgentSessionEntity,
|
|
@@ -106,6 +106,14 @@ const SIGNIFICANT_PROGRESS_STEPS = ['prompt_execution', 'action_execution', 'sub
|
|
|
106
106
|
*/
|
|
107
107
|
const MAX_USAGE_DETAILS_JSON_LENGTH = 4096;
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Sessions already logged as having no co-agent prompt run. Usage, transcript and tool-turn relays all read
|
|
111
|
+
* the id, many times per call, so the gap is logged once per session instead of once per relay. Bounded:
|
|
112
|
+
* past {@link MAX_LOGGED_MISSING_PROMPT_RUN_SESSIONS} entries the oldest is dropped (a Set keeps insertion order).
|
|
113
|
+
*/
|
|
114
|
+
const sessionsLoggedWithoutPromptRun = new Set<string>();
|
|
115
|
+
const MAX_LOGGED_MISSING_PROMPT_RUN_SESSIONS = 5000;
|
|
116
|
+
|
|
109
117
|
/** Entity name — centralised so the `MJ:`-prefix convention is applied in exactly one place. */
|
|
110
118
|
const CO_AGENT_ENTITY = 'MJ: AI Agent Co Agents';
|
|
111
119
|
const CONVERSATION_DETAIL_ENTITY = 'MJ: Conversation Details';
|
|
@@ -1330,10 +1338,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
|
|
|
1330
1338
|
return null;
|
|
1331
1339
|
}
|
|
1332
1340
|
if (!config.promptRunID) {
|
|
1333
|
-
|
|
1334
|
-
`RelayRealtimeUsage: session ${session.ID} has no co-agent promptRunID (observability run ` +
|
|
1335
|
-
'creation was skipped or failed) — usage delta dropped.',
|
|
1336
|
-
);
|
|
1341
|
+
this.logMissingPromptRunOnce(session.ID);
|
|
1337
1342
|
return null;
|
|
1338
1343
|
}
|
|
1339
1344
|
const verified = await this.runIDVerifier.Verify(
|
|
@@ -1349,6 +1354,29 @@ export class RealtimeClientSessionResolver extends ResolverBase {
|
|
|
1349
1354
|
return verified.PromptRunID;
|
|
1350
1355
|
}
|
|
1351
1356
|
|
|
1357
|
+
/**
|
|
1358
|
+
* Logs, once per session, that the session has no co-agent prompt run, so its usage, cost, transcript and
|
|
1359
|
+
* tool turns are not recorded on one. The reason (no active co-agent prompt, or a failed run save) was
|
|
1360
|
+
* logged when the session was prepared.
|
|
1361
|
+
*/
|
|
1362
|
+
private logMissingPromptRunOnce(sessionID: string): void {
|
|
1363
|
+
if (sessionsLoggedWithoutPromptRun.has(sessionID)) {
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
sessionsLoggedWithoutPromptRun.add(sessionID);
|
|
1367
|
+
if (sessionsLoggedWithoutPromptRun.size > MAX_LOGGED_MISSING_PROMPT_RUN_SESSIONS) {
|
|
1368
|
+
const oldest = sessionsLoggedWithoutPromptRun.values().next().value;
|
|
1369
|
+
if (oldest !== undefined) {
|
|
1370
|
+
sessionsLoggedWithoutPromptRun.delete(oldest);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
LogError(
|
|
1374
|
+
`RealtimeClientSessionResolver: session ${sessionID} has no co-agent promptRunID, so its usage, cost, ` +
|
|
1375
|
+
'transcript and tool turns are not recorded on a prompt run (the reason was logged when the session ' +
|
|
1376
|
+
'was prepared). Logged once per session.',
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1352
1380
|
/**
|
|
1353
1381
|
* Persist an interactive channel's state of record (e.g. the live whiteboard's serialized
|
|
1354
1382
|
* scene) onto the session's `MJ: AI Agent Session Channels` row.
|
|
@@ -2032,9 +2060,10 @@ export class RealtimeClientSessionResolver extends ResolverBase {
|
|
|
2032
2060
|
const existing = await rv.RunView<MJAIAgentSessionChannelEntity>(
|
|
2033
2061
|
{
|
|
2034
2062
|
EntityName: SESSION_CHANNEL_ENTITY,
|
|
2035
|
-
ExtraFilter: `AgentSessionID='${agentSessionID}' AND ChannelID='${channelID}'`,
|
|
2063
|
+
ExtraFilter: `AgentSessionID='${EscapeSQLString(agentSessionID)}' AND ChannelID='${EscapeSQLString(channelID)}'`,
|
|
2036
2064
|
ResultType: 'entity_object',
|
|
2037
2065
|
MaxRows: 1,
|
|
2066
|
+
SkipTotalRowCount: true,
|
|
2038
2067
|
},
|
|
2039
2068
|
contextUser,
|
|
2040
2069
|
);
|
|
@@ -2423,6 +2452,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
|
|
|
2423
2452
|
Fields: ['ID', 'IsEnabled'],
|
|
2424
2453
|
ResultType: 'simple',
|
|
2425
2454
|
MaxRows: 1,
|
|
2455
|
+
SkipTotalRowCount: true,
|
|
2426
2456
|
},
|
|
2427
2457
|
contextUser,
|
|
2428
2458
|
);
|
|
@@ -2585,6 +2615,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
|
|
|
2585
2615
|
OrderBy: '__mj_CreatedAt DESC',
|
|
2586
2616
|
ResultType: 'simple',
|
|
2587
2617
|
MaxRows: 1,
|
|
2618
|
+
SkipTotalRowCount: true,
|
|
2588
2619
|
},
|
|
2589
2620
|
contextUser,
|
|
2590
2621
|
);
|
|
@@ -2673,9 +2704,10 @@ export class RealtimeClientSessionResolver extends ResolverBase {
|
|
|
2673
2704
|
const existing = await rv.RunView<MJAIAgentSessionChannelEntity>(
|
|
2674
2705
|
{
|
|
2675
2706
|
EntityName: SESSION_CHANNEL_ENTITY,
|
|
2676
|
-
ExtraFilter: `AgentSessionID='${agentSessionID}' AND ChannelID='${channelID}'`,
|
|
2707
|
+
ExtraFilter: `AgentSessionID='${EscapeSQLString(agentSessionID)}' AND ChannelID='${EscapeSQLString(channelID)}'`,
|
|
2677
2708
|
ResultType: 'entity_object',
|
|
2678
2709
|
MaxRows: 1,
|
|
2710
|
+
SkipTotalRowCount: true,
|
|
2679
2711
|
},
|
|
2680
2712
|
contextUser,
|
|
2681
2713
|
);
|
|
@@ -3014,6 +3046,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
|
|
|
3014
3046
|
ExtraFilter: `AgentSessionID='${session.ID}' AND Role='${mappedRole}'`,
|
|
3015
3047
|
OrderBy: '__mj_CreatedAt DESC',
|
|
3016
3048
|
MaxRows: 1,
|
|
3049
|
+
SkipTotalRowCount: true,
|
|
3017
3050
|
ResultType: 'entity_object',
|
|
3018
3051
|
},
|
|
3019
3052
|
writeUser,
|