@taskforcehq/taskforce 0.3.329 → 0.3.332
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 +7 -2
- package/dist/Taskforce.module.css +290 -45
- package/dist/components/features/AiProfilesModule.js +3 -5
- package/dist/components/features/TaskSettings.js +70 -14
- package/dist/components/features/TaskforceAgentsModule.d.ts +1 -2
- package/dist/components/features/TaskforceAgentsModule.js +586 -116
- package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +2 -2
- package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.js +2 -2
- package/dist/components/features/agentConnections/AgentConnectionControl.d.ts +15 -0
- package/dist/components/features/agentConnections/AgentConnectionControl.js +125 -0
- package/dist/components/features/agentConnections/ModelConnectionsPanel.d.ts +18 -0
- package/dist/components/features/agentConnections/ModelConnectionsPanel.js +393 -0
- package/dist/components/features/agentConnections/modelConnectionsApi.d.ts +65 -0
- package/dist/components/features/agentConnections/modelConnectionsApi.js +129 -0
- package/dist/components/features/planning/PlanningModule.js +15 -3
- package/dist/components/features/planning/planningMetadata.d.ts +10 -0
- package/dist/components/features/planning/planningMetadata.js +8 -0
- package/dist/components/features/taskforceAgentAssignments/AgentBehaviorAssignments.js +15 -24
- package/dist/components/features/taskforceAgentEditorModel.d.ts +2 -1
- package/dist/components/features/taskforceAgentEditorModel.js +3 -1
- package/dist/components/features/workflowManager/workflowManagerApi.d.ts +1 -1
- package/dist/components/features/workflowManager/workflowManagerApi.js +3 -0
- package/dist/components/task/TaskWorkflowAssignmentField.js +68 -10
- package/dist/components/views/StandaloneLayout.js +62 -27
- package/dist/components/views/panels/PlanningDrawer.d.ts +2 -1
- package/dist/components/views/panels/PlanningDrawer.js +12 -8
- package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +4 -3
- package/dist/components/views/standalone/modals/SyncStatusModal.js +33 -38
- package/dist/config/envSchema.js +55 -1
- package/dist/core/McpTokenService.d.ts +25 -1
- package/dist/core/McpTokenService.js +106 -15
- package/dist/core/TaskChecklistCommandService.d.ts +27 -0
- package/dist/core/TaskChecklistCommandService.js +83 -5
- package/dist/core/TaskLifecycleCommandService.d.ts +3 -1
- package/dist/core/TaskLifecycleCommandService.js +6 -2
- package/dist/core/Taskforce.d.ts +43 -3
- package/dist/core/Taskforce.js +158 -7
- package/dist/core/types.d.ts +9 -0
- package/dist/documentReviews/DocumentReviewCommentStore.d.ts +2 -0
- package/dist/documentReviews/DocumentReviewCommentStore.js +28 -9
- package/dist/documentReviews/commandService.d.ts +2 -1
- package/dist/documentReviews/commandService.js +6 -3
- package/dist/hooks/sync/orchestratorShared.d.ts +1 -0
- package/dist/hooks/sync/orchestratorShared.js +2 -0
- package/dist/hooks/sync/useLocalSyncCoordinatorSnapshot.d.ts +3 -0
- package/dist/hooks/sync/useLocalSyncCoordinatorSnapshot.js +85 -9
- package/dist/hooks/sync/useSyncStatusControls.d.ts +4 -1
- package/dist/hooks/sync/useSyncStatusControls.js +47 -11
- package/dist/hooks/useSyncOrchestrator.d.ts +3 -0
- package/dist/hooks/useSyncOrchestrator.js +111 -2
- package/dist/hooks/useTaskData.d.ts +31 -1
- package/dist/hooks/useTaskData.js +29 -7
- package/dist/hooks/useTaskMutations.d.ts +3 -1
- package/dist/hooks/useTaskMutations.js +46 -12
- package/dist/hooks/useTaskforce.d.ts +2 -0
- package/dist/hooks/useTaskforce.js +11 -2
- package/dist/mcp/clientRegistry.d.ts +2 -2
- package/dist/mcp/clientRegistry.js +1 -1
- package/dist/mcp/documentAssetRegistrar.js +9 -6
- package/dist/mcp/durableTaskMutationRouter.d.ts +4 -1
- package/dist/mcp/durableTaskMutationRouter.js +10 -0
- package/dist/mcp/durableTaskRelationshipMutationRouter.js +4 -0
- package/dist/mcp/httpProtocolRouting.d.ts +11 -0
- package/dist/mcp/httpProtocolRouting.js +22 -0
- package/dist/mcp/localCliHealth.d.ts +2 -2
- package/dist/mcp/localCliHealth.js +2 -2
- package/dist/mcp/localServiceProxy.d.ts +1 -0
- package/dist/mcp/localServiceProxy.js +530 -68
- package/dist/mcp/planningAttachmentCommandAdapter.d.ts +3 -1
- package/dist/mcp/planningAttachmentCommandAdapter.js +20 -2
- package/dist/mcp/runtime.d.ts +54 -79
- package/dist/mcp/runtime.js +408 -102
- package/dist/mcp/taskPlanningRegistrar.js +25 -6
- package/dist/mcp/toolProfiles.d.ts +2 -0
- package/dist/mcp/toolProfiles.js +11 -0
- package/dist/mcp/toolRegistry.d.ts +1 -0
- package/dist/mcp/toolRegistry.js +9 -8
- package/dist/mcp/workflowExecutionRegistrar.js +4 -4
- package/dist/migrations/taskSchemaMigrations.d.ts +5 -0
- package/dist/migrations/taskSchemaMigrations.js +415 -0
- package/dist/runtime/appGates.d.ts +2 -1
- package/dist/runtime/appGates.js +7 -0
- package/dist/server/buildInfo.d.ts +1 -0
- package/dist/server/buildInfo.js +5 -1
- package/dist/server/cloudMcpCapacityTelemetry.d.ts +68 -0
- package/dist/server/cloudMcpCapacityTelemetry.js +221 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +108 -11
- package/dist/server/localDatabaseIdentity.js +4 -1
- package/dist/server/localServiceLease.js +2 -1
- package/dist/server/localServiceSessions.d.ts +14 -17
- package/dist/server/localServiceSessions.js +308 -94
- package/dist/server/localWorkspaceSyncServerRuntime.js +25 -19
- package/dist/server/routes/admin.d.ts +4 -0
- package/dist/server/routes/admin.js +126 -5
- package/dist/server/routes/agents.d.ts +7 -0
- package/dist/server/routes/agents.js +277 -24
- package/dist/server/routes/billing.js +5 -1
- package/dist/server/routes/durableTaskHttpRouters.d.ts +2 -0
- package/dist/server/routes/executorPhaseAApprovals.d.ts +19 -0
- package/dist/server/routes/executorPhaseAApprovals.js +140 -0
- package/dist/server/routes/executorPhaseAIssuerRedemptions.d.ts +11 -0
- package/dist/server/routes/executorPhaseAIssuerRedemptions.js +24 -0
- package/dist/server/routes/githubReviewEvidence.d.ts +19 -0
- package/dist/server/routes/githubReviewEvidence.js +116 -0
- package/dist/server/routes/localService.d.ts +15 -4
- package/dist/server/routes/localService.js +136 -40
- package/dist/server/routes/modelConnections.d.ts +24 -0
- package/dist/server/routes/modelConnections.js +205 -0
- package/dist/server/routes/shared.d.ts +22 -0
- package/dist/server/routes/shared.js +298 -182
- package/dist/server/routes/sync.js +1 -0
- package/dist/server/routes/syncAuxRoutes.js +5 -1
- package/dist/server/routes/syncPushRoutes.d.ts +1 -0
- package/dist/server/routes/syncPushRoutes.js +1 -0
- package/dist/server/routes/syncV3FeedRoutes.js +10 -1
- package/dist/server/routes/syncV3OperationRoutes.js +35 -9
- package/dist/server/routes/tasks.js +21 -2
- package/dist/server/routes/workflows.js +9 -1
- package/dist/server/routes.js +59 -0
- package/dist/service/localServiceCli.d.ts +2 -2
- package/dist/service/localServiceCli.js +4 -3
- package/dist/services/codexAppServerModelGateway.d.ts +54 -5
- package/dist/services/codexAppServerModelGateway.js +246 -21
- package/dist/services/codexDeviceCodeLoginManager.d.ts +39 -0
- package/dist/services/codexDeviceCodeLoginManager.js +265 -0
- package/dist/services/codexModelProviderConnectionLifecycle.d.ts +8 -0
- package/dist/services/codexModelProviderConnectionLifecycle.js +73 -0
- package/dist/services/executorPhaseAApprovalRuntime.d.ts +14 -0
- package/dist/services/executorPhaseAApprovalRuntime.js +24 -0
- package/dist/services/executorPhaseAApprovalService.d.ts +40 -0
- package/dist/services/executorPhaseAApprovalService.js +609 -0
- package/dist/services/executorPhaseADriftEvaluator.d.ts +78 -0
- package/dist/services/executorPhaseADriftEvaluator.js +93 -0
- package/dist/services/executorPhaseAIssuerCore.d.ts +139 -0
- package/dist/services/executorPhaseAIssuerCore.js +402 -0
- package/dist/services/executorPhaseAIssuerRedemptionService.d.ts +34 -0
- package/dist/services/executorPhaseAIssuerRedemptionService.js +158 -0
- package/dist/services/githubReviewEvidenceRuntime.d.ts +14 -0
- package/dist/services/githubReviewEvidenceRuntime.js +30 -0
- package/dist/services/githubReviewEvidenceService.d.ts +93 -0
- package/dist/services/githubReviewEvidenceService.js +336 -0
- package/dist/services/modelGateway.d.ts +7 -0
- package/dist/services/modelProviderConnectionService.d.ts +71 -0
- package/dist/services/modelProviderConnectionService.js +279 -0
- package/dist/services/modelProviderCredentialHome.d.ts +32 -0
- package/dist/services/modelProviderCredentialHome.js +271 -0
- package/dist/services/ownerScopedModelConnectionGateway.d.ts +12 -0
- package/dist/services/ownerScopedModelConnectionGateway.js +30 -0
- package/dist/services/taskforceAgentConnectionBindingService.d.ts +66 -0
- package/dist/services/taskforceAgentConnectionBindingService.js +185 -0
- package/dist/services/taskforceAgentMcpBridge.d.ts +2 -2
- package/dist/services/taskforceAgentMcpBridge.js +42 -29
- package/dist/shared/adminEdgeProxy.d.ts +5 -0
- package/dist/shared/adminEdgeProxy.js +94 -0
- package/dist/shared/adminProxySessionCookies.d.ts +16 -0
- package/dist/shared/adminProxySessionCookies.js +86 -0
- package/dist/shared/aiProfileColors.d.ts +31 -1
- package/dist/shared/aiProfileColors.js +14 -6
- package/dist/shared/executorPhaseAApprovalGoldenVector.json +269 -0
- package/dist/shared/executorPhaseAApprovalReceipt.d.ts +112 -0
- package/dist/shared/executorPhaseAApprovalReceipt.js +58 -0
- package/dist/shared/executorPhaseAApprovalTrustPolicy.json +10 -0
- package/dist/shared/executorPhaseADriftContracts.d.ts +37 -0
- package/dist/shared/executorPhaseADriftContracts.js +63 -0
- package/dist/shared/executorPhaseAIssuerContracts.d.ts +75 -0
- package/dist/shared/executorPhaseAIssuerContracts.js +90 -0
- package/dist/shared/executorPhaseARunAuthorization.d.ts +120 -0
- package/dist/shared/executorPhaseARunAuthorization.js +232 -0
- package/dist/shared/executorPhaseASchemaValidation.d.ts +7 -0
- package/dist/shared/executorPhaseASchemaValidation.js +53 -0
- package/dist/shared/executorPhaseASchemas/approval-receipt.schema.json +106 -0
- package/dist/shared/executorPhaseASchemas/drift-attestation.schema.json +54 -0
- package/dist/shared/executorPhaseASchemas/drift-request.schema.json +47 -0
- package/dist/shared/executorPhaseASchemas/foundation-manifest.schema.json +29 -0
- package/dist/shared/executorPhaseASchemas/issuer-audit-record.schema.json +24 -0
- package/dist/shared/executorPhaseASchemas/issuer-proof.schema.json +43 -0
- package/dist/shared/executorPhaseASchemas/issuer-redemption-request.schema.json +18 -0
- package/dist/shared/executorPhaseASchemas/issuer-redemption-response.schema.json +39 -0
- package/dist/shared/executorPhaseASchemas/logical-envelope.schema.json +149 -0
- package/dist/shared/executorPhaseASchemas/preflight-report.schema.json +216 -0
- package/dist/shared/executorPhaseASchemas/run-capability.schema.json +111 -0
- package/dist/shared/executorPhaseASchemas/sender-proof.schema.json +37 -0
- package/dist/shared/executorPhaseASchemas/workload-grant.schema.json +68 -0
- package/dist/shared/executorPhaseASchemas/workload-jwt-claims.schema.json +67 -0
- package/dist/shared/executorPhaseATiming.d.ts +1 -0
- package/dist/shared/executorPhaseATiming.js +1 -0
- package/dist/shared/planningIdentity.js +1 -1
- package/dist/shared/productLogoKeys.d.ts +2 -0
- package/dist/shared/productLogoKeys.js +18 -0
- package/dist/shared/taskforceAgentDefinition.d.ts +0 -1
- package/dist/shared/taskforceAgentDefinition.js +6 -9
- package/dist/shared/taskforceAgentModels.js +7 -3
- package/dist/shared/userFeatureGrants.d.ts +4 -0
- package/dist/shared/userFeatureGrants.js +7 -0
- package/dist/storage/executorPhaseAApprovalReceiptStore.d.ts +37 -0
- package/dist/storage/executorPhaseAApprovalReceiptStore.js +81 -0
- package/dist/storage/executorPhaseARunAuthorizationStore.d.ts +103 -0
- package/dist/storage/executorPhaseARunAuthorizationStore.js +492 -0
- package/dist/storage/githubReviewEvidenceInstallationStore.d.ts +26 -0
- package/dist/storage/githubReviewEvidenceInstallationStore.js +89 -0
- package/dist/storage/modelProviderConnectionStore.d.ts +99 -0
- package/dist/storage/modelProviderConnectionStore.js +446 -0
- package/dist/storage/postgresAdapter.d.ts +4 -0
- package/dist/storage/postgresAdapter.js +14 -1
- package/dist/storage/postgresRequestDiagnostics.d.ts +9 -1
- package/dist/storage/postgresRequestDiagnostics.js +15 -2
- package/dist/storage/postgresWorker.js +20 -2
- package/dist/storage/postgresWorkerProtocol.d.ts +7 -0
- package/dist/storage/taskforceAgentConnectionBindingStore.d.ts +31 -0
- package/dist/storage/taskforceAgentConnectionBindingStore.js +84 -0
- package/dist/storage/workflowStore.d.ts +2 -0
- package/dist/storage/workflowStore.js +39 -2
- package/dist/sync/cloudSyncApi.js +4 -2
- package/dist/sync/collaborationSyncState.js +5 -0
- package/dist/sync/coordinator/localSyncExecutionPermitStore.js +60 -29
- package/dist/sync/coordinator/localWorkspaceSyncPendingV2PushProgress.d.ts +10 -0
- package/dist/sync/coordinator/localWorkspaceSyncPendingV2PushProgress.js +29 -0
- package/dist/sync/coordinator/localWorkspaceSyncRunner.d.ts +3 -1
- package/dist/sync/coordinator/localWorkspaceSyncRunner.js +18 -0
- package/dist/sync/coordinator/localWorkspaceSyncRunnerState.d.ts +15 -1
- package/dist/sync/coordinator/localWorkspaceSyncRunnerState.js +99 -2
- package/dist/sync/coordinator/workspaceSyncCredentialBroker.d.ts +1 -0
- package/dist/sync/coordinator/workspaceSyncCredentialBroker.js +33 -4
- package/dist/sync/engine/workspaceSyncContentLifecycleAdapter.d.ts +2 -1
- package/dist/sync/engine/workspaceSyncContentVersionStore.d.ts +38 -0
- package/dist/sync/engine/workspaceSyncContentVersionStore.js +50 -1
- package/dist/sync/engine/workspaceSyncEngineLifecyclePolicy.js +6 -6
- package/dist/sync/engine/workspaceSyncEnginePushCompletion.d.ts +14 -0
- package/dist/sync/engine/workspaceSyncEnginePushCompletion.js +84 -0
- package/dist/sync/engine/workspaceSyncEngineRecovery.js +1 -0
- package/dist/sync/engine/workspaceSyncEngineRuntime.d.ts +1 -0
- package/dist/sync/engine/workspaceSyncEngineRuntime.js +1 -0
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +112 -38
- package/dist/sync/engine/workspaceSyncEngineServerSnapshotReader.js +6 -0
- package/dist/sync/engine/workspaceSyncEngineTransfers.js +3 -1
- package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.d.ts +1 -0
- package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +31 -0
- package/dist/sync/syncApplyHandlers.d.ts +3 -0
- package/dist/sync/syncApplyHandlers.js +81 -15
- package/dist/sync/syncService.d.ts +15 -0
- package/dist/sync/syncService.js +83 -18
- package/dist/sync/taskforceAgentSyncAuthorization.d.ts +16 -0
- package/dist/sync/taskforceAgentSyncAuthorization.js +20 -0
- package/dist/sync/v3/combinedFeedCapability.js +4 -0
- package/dist/sync/v3/durableInitiativeMutationRouter.js +18 -10
- package/dist/sync/v3/durableOperationReplay.d.ts +14 -0
- package/dist/sync/v3/durableOperationReplay.js +18 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +3 -0
- package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +2 -1
- package/dist/sync/v3/durableTaskChecklistMutationRouter.js +58 -15
- package/dist/sync/v3/durableTaskCommentMutationRouter.js +3 -0
- package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +1 -1
- package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +10 -0
- package/dist/sync/v3/durableTaxonomyMutationRouter.js +5 -0
- package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.d.ts +18 -7
- package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.js +89 -23
- package/dist/sync/v3/durableWorkstreamMutationRouter.js +18 -10
- package/dist/sync/v3/durableWorkstreamTaskOrderMutationRouter.js +16 -5
- package/dist/sync/v3/localOutboxDispatchRuntime.js +2 -2
- package/dist/sync/v3/localOutboxRecoveryPreparationService.js +8 -2
- package/dist/sync/v3/localTaskChecklistOutboxHandler.js +16 -2
- package/dist/sync/v3/localTaskChecklistOutboxService.d.ts +4 -0
- package/dist/sync/v3/localTaskChecklistOutboxService.js +14 -1
- package/dist/sync/v3/localTaskCreateOutboxService.d.ts +3 -1
- package/dist/sync/v3/localTaskCreateOutboxService.js +6 -1
- package/dist/sync/v3/localTaskDeleteOutboxService.js +1 -0
- package/dist/sync/v3/localTaskLifecycleOutboxService.d.ts +2 -1
- package/dist/sync/v3/localTaskLifecycleOutboxService.js +5 -1
- package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -1
- package/dist/sync/v3/localTaskMetadataOutboxService.js +6 -2
- package/dist/sync/v3/localTaskStatusOutboxService.d.ts +10 -1
- package/dist/sync/v3/localTaskStatusOutboxService.js +238 -16
- package/dist/sync/v3/syncDurabilityStore.d.ts +36 -1
- package/dist/sync/v3/syncDurabilityStore.js +202 -8
- package/dist/sync/v3/taskChecklistMutationService.d.ts +11 -0
- package/dist/sync/v3/taskChecklistMutationService.js +44 -1
- package/dist/sync/v3/taskStatusMutationService.d.ts +29 -1
- package/dist/sync/v3/taskStatusMutationService.js +119 -3
- package/dist/sync/v3/workflowAssignmentSync.d.ts +5 -0
- package/dist/sync/v3/workflowAssignmentSync.js +27 -0
- package/dist/sync/v3/workflowRuntimeMutationService.d.ts +5 -0
- package/dist/sync/v3/workflowRuntimeMutationService.js +124 -71
- package/dist/sync/v3/workspaceSyncJournalReader.js +1 -1
- package/dist/sync/v3/workspaceSyncOperationRetentionService.d.ts +16 -0
- package/dist/sync/v3/workspaceSyncOperationRetentionService.js +83 -0
- package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +9 -2
- package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +25 -3
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +7 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +44 -32
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +9 -5
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +54 -38
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +10 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +70 -31
- package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +2 -1
- package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +16 -0
- package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +6 -0
- package/dist/sync/v3/workspaceSyncV3Feed.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3Feed.js +6 -1
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +49 -18
- package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +3 -0
- package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +5 -0
- package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +36 -2
- package/dist/sync/v3/workspaceSyncV3RepairSnapshotRetentionService.d.ts +17 -0
- package/dist/sync/v3/workspaceSyncV3RepairSnapshotRetentionService.js +64 -0
- package/dist/sync/v3/workspaceSyncV3WorkflowRuntimeApply.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3WorkflowRuntimeApply.js +50 -5
- package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverCoverage.d.ts +7 -0
- package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverCoverage.js +33 -0
- package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverWireContract.d.ts +7 -0
- package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverWireContract.js +8 -0
- package/dist/sync/workspaceRepair.d.ts +5 -2
- package/dist/sync/workspaceRepair.js +161 -6
- package/dist/sync/workspaceSyncModel.d.ts +21 -0
- package/dist/sync/workspaceSyncModel.js +35 -7
- package/dist/sync/workspaceSyncState.d.ts +1 -0
- package/dist/sync/workspaceSyncV2SnapshotBuilder.d.ts +6 -0
- package/dist/sync/workspaceSyncV2SnapshotBuilder.js +5 -0
- package/dist/ui/assets/AiIdentityRosterCard-CDMPtnbC.js +1 -0
- package/dist/ui/assets/AiProfilesModule-Cww85C4E.js +1 -0
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-B68Wg-RV.js → AnnotatedAttachmentWorkspace-Dt9TKYSH.js} +1 -1
- package/dist/ui/assets/{AssetTraySortControl-C8Rztyal.js → AssetTraySortControl-CKgapnxu.js} +1 -1
- package/dist/ui/assets/{ContextAttachmentManager-Cj3c7X9y.js → ContextAttachmentManager-T-cEVcOZ.js} +2 -2
- package/dist/ui/assets/{DocumentWorkspace-B5uvzems.js → DocumentWorkspace-HD4MUjAZ.js} +1 -1
- package/dist/ui/assets/{EntityActivityTimeline-BVFvCB5R.js → EntityActivityTimeline-_V0zfMuh.js} +1 -1
- package/dist/ui/assets/PlanningModule-mTkBygsB.js +1 -0
- package/dist/ui/assets/{PlansPage-AY4cGWco.js → PlansPage-Btiq06VO.js} +1 -1
- package/dist/ui/assets/{TaskContextUpload-DZxTVesW.js → TaskContextUpload-C00NVUpn.js} +1 -1
- package/dist/ui/assets/TaskSettings-Cp-L-4xh.js +12 -0
- package/dist/ui/assets/TaskforceAgentsModule-Coq9-PUp.js +24 -0
- package/dist/ui/assets/TaskforceAgentsModule-b-8R25cJ.css +1 -0
- package/dist/ui/assets/{WorkflowManagerModule-DEr5di_r.js → WorkflowManagerModule-CuqRzMmM.js} +1 -1
- package/dist/ui/assets/index-BZ-hdZSk.css +1 -0
- package/dist/ui/assets/index-DSQINwt9.js +7 -0
- package/dist/ui/assets/{vendor-icons-D9Lpw-j4.js → vendor-icons-Bsq-mcEn.js} +1 -1
- package/dist/ui/branding/logos/letterhead_dark.png +0 -0
- package/dist/ui/branding/logos/letterhead_light.png +0 -0
- package/dist/ui/branding/logos/signature_dark.png +0 -0
- package/dist/ui/branding/logos/signature_light.png +0 -0
- package/dist/ui/index.html +3 -3
- package/dist/utils/aiProfileDefaultLogo.js +16 -48
- package/dist/utils/httpSetCookie.d.ts +1 -0
- package/dist/utils/httpSetCookie.js +4 -1
- package/dist/utils/localRuntimeUrl.d.ts +1 -0
- package/dist/utils/localRuntimeUrl.js +14 -0
- package/dist/utils/productLogoRegistry.d.ts +2 -0
- package/dist/utils/productLogoRegistry.js +36 -0
- package/dist/utils/syncStatusPresentation.d.ts +4 -0
- package/dist/utils/syncStatusPresentation.js +17 -0
- package/dist/utils/taskEvents.d.ts +2 -0
- package/dist/utils/workspaceSyncPresentation.d.ts +1 -1
- package/dist/utils/workspaceSyncPresentation.js +23 -17
- package/package.json +24 -4
- package/dist/ui/assets/AiIdentityRosterCard-BZM4aQnM.js +0 -1
- package/dist/ui/assets/AiProfilesModule-BbAEWtHH.js +0 -1
- package/dist/ui/assets/PlanningModule-B5DWQOGz.js +0 -1
- package/dist/ui/assets/TaskSettings-BeWAuyZj.js +0 -12
- package/dist/ui/assets/TaskforceAgentsModule-BifCYtjR.js +0 -21
- package/dist/ui/assets/TaskforceAgentsModule-D6IC0S7Q.css +0 -1
- package/dist/ui/assets/index-I5zhdtgt.js +0 -7
- package/dist/ui/assets/index-VRFsx3wz.css +0 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { DatabaseAdapter } from './databaseAdapter.js';
|
|
2
|
+
export type ModelProviderConnectionLifecycleStatus = 'pending' | 'active' | 'reconnect_required' | 'disconnected' | 'revoked';
|
|
3
|
+
export interface ModelProviderConnectionDisplayMetadata {
|
|
4
|
+
displayName?: string;
|
|
5
|
+
accountLabel?: string;
|
|
6
|
+
accountEmail?: string;
|
|
7
|
+
planLabel?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ModelProviderConnectionRecord {
|
|
10
|
+
id: string;
|
|
11
|
+
tenantId: string;
|
|
12
|
+
ownerUserId: string;
|
|
13
|
+
providerKey: string;
|
|
14
|
+
authenticationType: string;
|
|
15
|
+
lifecycleStatus: ModelProviderConnectionLifecycleStatus;
|
|
16
|
+
displayMetadata: ModelProviderConnectionDisplayMetadata;
|
|
17
|
+
credentialHomeRef: string;
|
|
18
|
+
lastVerifiedAt: string | null;
|
|
19
|
+
lastRefreshedAt: string | null;
|
|
20
|
+
lastFailureAt: string | null;
|
|
21
|
+
failureCode: string | null;
|
|
22
|
+
disconnectedAt: string | null;
|
|
23
|
+
revokedAt: string | null;
|
|
24
|
+
createdAt: string;
|
|
25
|
+
updatedAt: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ModelProviderConnectionLease {
|
|
28
|
+
connectionId: string;
|
|
29
|
+
token: string;
|
|
30
|
+
operation: string;
|
|
31
|
+
acquiredAt: string;
|
|
32
|
+
expiresAt: string;
|
|
33
|
+
}
|
|
34
|
+
export declare class ModelProviderConnectionError extends Error {
|
|
35
|
+
readonly statusCode: number;
|
|
36
|
+
readonly code: string;
|
|
37
|
+
constructor(message: string, statusCode: number, code: string);
|
|
38
|
+
}
|
|
39
|
+
export declare function sanitizeModelProviderConnectionDisplayMetadata(value: unknown): ModelProviderConnectionDisplayMetadata;
|
|
40
|
+
export declare class ModelProviderConnectionStore {
|
|
41
|
+
private readonly db;
|
|
42
|
+
private readonly tenantId;
|
|
43
|
+
private readonly now;
|
|
44
|
+
constructor(db: DatabaseAdapter, tenantId: string, now?: () => Date);
|
|
45
|
+
private leaseNow;
|
|
46
|
+
private leaseClockPredicate;
|
|
47
|
+
create(input: {
|
|
48
|
+
ownerUserId: string;
|
|
49
|
+
providerKey: string;
|
|
50
|
+
authenticationType: string;
|
|
51
|
+
displayMetadata?: unknown;
|
|
52
|
+
}): ModelProviderConnectionRecord;
|
|
53
|
+
listOwned(ownerUserIdRaw: string): ModelProviderConnectionRecord[];
|
|
54
|
+
getOwned(ownerUserIdRaw: string, connectionIdRaw: string): ModelProviderConnectionRecord | null;
|
|
55
|
+
requireOwned(ownerUserId: string, connectionId: string): ModelProviderConnectionRecord;
|
|
56
|
+
renameOwned(ownerUserId: string, connectionId: string, displayNameRaw: unknown): ModelProviderConnectionRecord;
|
|
57
|
+
requireOwnedUnderLease(ownerUserId: string, connectionId: string, leaseToken: string): ModelProviderConnectionRecord;
|
|
58
|
+
deletePendingOwned(ownerUserId: string, connectionId: string): boolean;
|
|
59
|
+
acquireLease(input: {
|
|
60
|
+
ownerUserId: string;
|
|
61
|
+
connectionId: string;
|
|
62
|
+
operation: string;
|
|
63
|
+
durationMs?: number;
|
|
64
|
+
}): ModelProviderConnectionLease;
|
|
65
|
+
renewLease(lease: ModelProviderConnectionLease, durationMs?: number): boolean;
|
|
66
|
+
releaseLease(lease: ModelProviderConnectionLease): boolean;
|
|
67
|
+
completeLease(lease: ModelProviderConnectionLease): boolean;
|
|
68
|
+
deleteCleanableUnderLease(input: {
|
|
69
|
+
ownerUserId: string;
|
|
70
|
+
connectionId: string;
|
|
71
|
+
leaseToken: string;
|
|
72
|
+
}): boolean;
|
|
73
|
+
markVerified(input: {
|
|
74
|
+
ownerUserId: string;
|
|
75
|
+
connectionId: string;
|
|
76
|
+
leaseToken: string;
|
|
77
|
+
displayMetadata?: unknown;
|
|
78
|
+
}): ModelProviderConnectionRecord;
|
|
79
|
+
markRefreshed(input: {
|
|
80
|
+
ownerUserId: string;
|
|
81
|
+
connectionId: string;
|
|
82
|
+
leaseToken: string;
|
|
83
|
+
displayMetadata?: unknown;
|
|
84
|
+
}): ModelProviderConnectionRecord;
|
|
85
|
+
markReconnectRequired(input: {
|
|
86
|
+
ownerUserId: string;
|
|
87
|
+
connectionId: string;
|
|
88
|
+
leaseToken: string;
|
|
89
|
+
failureCode: string;
|
|
90
|
+
}): ModelProviderConnectionRecord;
|
|
91
|
+
markDisconnected(input: {
|
|
92
|
+
ownerUserId: string;
|
|
93
|
+
connectionId: string;
|
|
94
|
+
leaseToken: string;
|
|
95
|
+
}): ModelProviderConnectionRecord;
|
|
96
|
+
private updateUnderLease;
|
|
97
|
+
private updateUnderLeaseTransaction;
|
|
98
|
+
private insertEvent;
|
|
99
|
+
}
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
export class ModelProviderConnectionError extends Error {
|
|
3
|
+
statusCode;
|
|
4
|
+
code;
|
|
5
|
+
constructor(message, statusCode, code) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.statusCode = statusCode;
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.name = 'ModelProviderConnectionError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
const SAFE_METADATA_KEYS = ['accountLabel', 'accountEmail', 'planLabel'];
|
|
13
|
+
const MAX_DISPLAY_NAME_LENGTH = 80;
|
|
14
|
+
const POSTGRES_LEASE_CLOCK_SQL = `TO_CHAR(
|
|
15
|
+
clock_timestamp() AT TIME ZONE 'UTC',
|
|
16
|
+
'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
|
|
17
|
+
)`;
|
|
18
|
+
const POSTGRES_LEASE_EXPIRY_SQL = `TO_CHAR(
|
|
19
|
+
(clock_timestamp() + (CAST(? AS BIGINT) * INTERVAL '1 millisecond')) AT TIME ZONE 'UTC',
|
|
20
|
+
'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
|
|
21
|
+
)`;
|
|
22
|
+
function randomOpaqueId(prefix) {
|
|
23
|
+
return `${prefix}_${randomBytes(24).toString('base64url')}`;
|
|
24
|
+
}
|
|
25
|
+
function normalizeIdentifier(value, label) {
|
|
26
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
27
|
+
if (!/^[a-z][a-z0-9._-]{0,63}$/.test(normalized)) {
|
|
28
|
+
throw new ModelProviderConnectionError(`${label} is invalid.`, 400, 'MODEL_CONNECTION_INVALID_INPUT');
|
|
29
|
+
}
|
|
30
|
+
return normalized;
|
|
31
|
+
}
|
|
32
|
+
export function sanitizeModelProviderConnectionDisplayMetadata(value) {
|
|
33
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
34
|
+
return {};
|
|
35
|
+
const source = value;
|
|
36
|
+
const result = {};
|
|
37
|
+
for (const key of SAFE_METADATA_KEYS) {
|
|
38
|
+
const normalized = typeof source[key] === 'string'
|
|
39
|
+
? String(source[key]).trim().slice(0, 200)
|
|
40
|
+
: '';
|
|
41
|
+
const sanitized = normalized
|
|
42
|
+
.replace(/sk-[A-Za-z0-9_-]{8,}/g, '[redacted]')
|
|
43
|
+
.replace(/(Bearer\s+)[^\s]+/gi, '$1[redacted]')
|
|
44
|
+
.replace(/\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}\b/g, '[redacted]');
|
|
45
|
+
if (sanitized)
|
|
46
|
+
result[key] = sanitized;
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
function normalizeDisplayName(value) {
|
|
51
|
+
if (typeof value !== 'string') {
|
|
52
|
+
throw new ModelProviderConnectionError('Display name must be a nonempty string of at most 80 characters.', 400, 'MODEL_CONNECTION_INVALID_DISPLAY_NAME');
|
|
53
|
+
}
|
|
54
|
+
const displayName = value.trim();
|
|
55
|
+
if (!displayName || displayName.length > MAX_DISPLAY_NAME_LENGTH) {
|
|
56
|
+
throw new ModelProviderConnectionError('Display name must be a nonempty string of at most 80 characters.', 400, 'MODEL_CONNECTION_INVALID_DISPLAY_NAME');
|
|
57
|
+
}
|
|
58
|
+
return displayName;
|
|
59
|
+
}
|
|
60
|
+
function parseDisplayMetadata(value) {
|
|
61
|
+
try {
|
|
62
|
+
const parsed = JSON.parse(String(value || '{}'));
|
|
63
|
+
const metadata = sanitizeModelProviderConnectionDisplayMetadata(parsed);
|
|
64
|
+
if (typeof parsed.displayName === 'string') {
|
|
65
|
+
const displayName = parsed.displayName.trim().slice(0, MAX_DISPLAY_NAME_LENGTH);
|
|
66
|
+
if (displayName)
|
|
67
|
+
metadata.displayName = displayName;
|
|
68
|
+
}
|
|
69
|
+
return metadata;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return {};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function mapRow(row) {
|
|
76
|
+
const status = String(row.lifecycle_status || 'pending');
|
|
77
|
+
return {
|
|
78
|
+
id: String(row.id || ''),
|
|
79
|
+
tenantId: String(row.tenant_id || 'default'),
|
|
80
|
+
ownerUserId: String(row.owner_user_id || ''),
|
|
81
|
+
providerKey: String(row.provider_key || ''),
|
|
82
|
+
authenticationType: String(row.authentication_type || ''),
|
|
83
|
+
lifecycleStatus: status,
|
|
84
|
+
displayMetadata: parseDisplayMetadata(row.display_metadata_json),
|
|
85
|
+
credentialHomeRef: String(row.credential_home_ref || ''),
|
|
86
|
+
lastVerifiedAt: row.last_verified_at ? String(row.last_verified_at) : null,
|
|
87
|
+
lastRefreshedAt: row.last_refreshed_at ? String(row.last_refreshed_at) : null,
|
|
88
|
+
lastFailureAt: row.last_failure_at ? String(row.last_failure_at) : null,
|
|
89
|
+
failureCode: row.failure_code ? String(row.failure_code) : null,
|
|
90
|
+
disconnectedAt: row.disconnected_at ? String(row.disconnected_at) : null,
|
|
91
|
+
revokedAt: row.revoked_at ? String(row.revoked_at) : null,
|
|
92
|
+
createdAt: String(row.created_at || ''),
|
|
93
|
+
updatedAt: String(row.updated_at || ''),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export class ModelProviderConnectionStore {
|
|
97
|
+
db;
|
|
98
|
+
tenantId;
|
|
99
|
+
now;
|
|
100
|
+
constructor(db, tenantId, now = () => new Date()) {
|
|
101
|
+
this.db = db;
|
|
102
|
+
this.tenantId = tenantId;
|
|
103
|
+
this.now = now;
|
|
104
|
+
}
|
|
105
|
+
leaseNow() {
|
|
106
|
+
if (this.db.provider === 'sqlite')
|
|
107
|
+
return this.now();
|
|
108
|
+
const row = this.db.prepare(`
|
|
109
|
+
SELECT ${POSTGRES_LEASE_CLOCK_SQL} AS current_time
|
|
110
|
+
`).get();
|
|
111
|
+
const currentTime = new Date(String(row?.current_time || ''));
|
|
112
|
+
if (Number.isNaN(currentTime.getTime())) {
|
|
113
|
+
throw new Error('[Taskforce] PostgreSQL did not return a valid lease clock value.');
|
|
114
|
+
}
|
|
115
|
+
return currentTime;
|
|
116
|
+
}
|
|
117
|
+
leaseClockPredicate(nowIso) {
|
|
118
|
+
return this.db.provider === 'postgres'
|
|
119
|
+
? { sql: POSTGRES_LEASE_CLOCK_SQL, params: [] }
|
|
120
|
+
: { sql: '?', params: [nowIso] };
|
|
121
|
+
}
|
|
122
|
+
create(input) {
|
|
123
|
+
const ownerUserId = String(input.ownerUserId || '').trim();
|
|
124
|
+
if (!ownerUserId) {
|
|
125
|
+
throw new ModelProviderConnectionError('Authentication is required.', 401, 'AUTH_REQUIRED');
|
|
126
|
+
}
|
|
127
|
+
const providerKey = normalizeIdentifier(input.providerKey, 'Provider key');
|
|
128
|
+
const authenticationType = normalizeIdentifier(input.authenticationType, 'Authentication type');
|
|
129
|
+
const id = randomOpaqueId('mpc');
|
|
130
|
+
const credentialHomeRef = randomOpaqueId('home');
|
|
131
|
+
const now = this.now().toISOString();
|
|
132
|
+
const displayMetadata = sanitizeModelProviderConnectionDisplayMetadata(input.displayMetadata);
|
|
133
|
+
this.db.transaction(() => {
|
|
134
|
+
this.db.prepare(`
|
|
135
|
+
INSERT INTO model_provider_connections (
|
|
136
|
+
id, tenant_id, owner_user_id, provider_key, authentication_type,
|
|
137
|
+
lifecycle_status, display_metadata_json, credential_home_ref,
|
|
138
|
+
created_at, updated_at
|
|
139
|
+
) VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, ?, ?)
|
|
140
|
+
`).run(id, this.tenantId, ownerUserId, providerKey, authenticationType, JSON.stringify(displayMetadata), credentialHomeRef, now, now);
|
|
141
|
+
this.insertEvent({
|
|
142
|
+
connectionId: id,
|
|
143
|
+
ownerUserId,
|
|
144
|
+
actorUserId: ownerUserId,
|
|
145
|
+
eventType: 'created',
|
|
146
|
+
outcome: 'success',
|
|
147
|
+
createdAt: now,
|
|
148
|
+
});
|
|
149
|
+
})();
|
|
150
|
+
return this.requireOwned(ownerUserId, id);
|
|
151
|
+
}
|
|
152
|
+
listOwned(ownerUserIdRaw) {
|
|
153
|
+
const ownerUserId = String(ownerUserIdRaw || '').trim();
|
|
154
|
+
if (!ownerUserId)
|
|
155
|
+
return [];
|
|
156
|
+
const rows = this.db.prepare(`
|
|
157
|
+
SELECT *
|
|
158
|
+
FROM model_provider_connections
|
|
159
|
+
WHERE tenant_id = ? AND owner_user_id = ?
|
|
160
|
+
ORDER BY created_at ASC, id ASC
|
|
161
|
+
`).all(this.tenantId, ownerUserId);
|
|
162
|
+
return rows.map(mapRow);
|
|
163
|
+
}
|
|
164
|
+
getOwned(ownerUserIdRaw, connectionIdRaw) {
|
|
165
|
+
const ownerUserId = String(ownerUserIdRaw || '').trim();
|
|
166
|
+
const connectionId = String(connectionIdRaw || '').trim();
|
|
167
|
+
if (!ownerUserId || !connectionId)
|
|
168
|
+
return null;
|
|
169
|
+
const row = this.db.prepare(`
|
|
170
|
+
SELECT *
|
|
171
|
+
FROM model_provider_connections
|
|
172
|
+
WHERE tenant_id = ? AND owner_user_id = ? AND id = ?
|
|
173
|
+
LIMIT 1
|
|
174
|
+
`).get(this.tenantId, ownerUserId, connectionId);
|
|
175
|
+
return row ? mapRow(row) : null;
|
|
176
|
+
}
|
|
177
|
+
requireOwned(ownerUserId, connectionId) {
|
|
178
|
+
const connection = this.getOwned(ownerUserId, connectionId);
|
|
179
|
+
if (!connection) {
|
|
180
|
+
throw new ModelProviderConnectionError('Model connection was not found.', 404, 'MODEL_CONNECTION_NOT_FOUND');
|
|
181
|
+
}
|
|
182
|
+
return connection;
|
|
183
|
+
}
|
|
184
|
+
renameOwned(ownerUserId, connectionId, displayNameRaw) {
|
|
185
|
+
const displayName = normalizeDisplayName(displayNameRaw);
|
|
186
|
+
return this.db.transaction(() => {
|
|
187
|
+
const current = this.requireOwned(ownerUserId, connectionId);
|
|
188
|
+
const now = this.now().toISOString();
|
|
189
|
+
const result = this.db.prepare(`
|
|
190
|
+
UPDATE model_provider_connections
|
|
191
|
+
SET display_metadata_json = ?, updated_at = ?
|
|
192
|
+
WHERE tenant_id = ? AND owner_user_id = ? AND id = ?
|
|
193
|
+
`).run(JSON.stringify({ ...current.displayMetadata, displayName }), now, this.tenantId, ownerUserId, connectionId);
|
|
194
|
+
if (Number(result?.changes || 0) !== 1) {
|
|
195
|
+
throw new ModelProviderConnectionError('Model connection was not found.', 404, 'MODEL_CONNECTION_NOT_FOUND');
|
|
196
|
+
}
|
|
197
|
+
this.insertEvent({
|
|
198
|
+
connectionId,
|
|
199
|
+
ownerUserId,
|
|
200
|
+
actorUserId: ownerUserId,
|
|
201
|
+
eventType: 'renamed',
|
|
202
|
+
outcome: 'success',
|
|
203
|
+
createdAt: now,
|
|
204
|
+
});
|
|
205
|
+
return this.requireOwned(ownerUserId, connectionId);
|
|
206
|
+
})();
|
|
207
|
+
}
|
|
208
|
+
requireOwnedUnderLease(ownerUserId, connectionId, leaseToken) {
|
|
209
|
+
const now = this.leaseNow().toISOString();
|
|
210
|
+
const clock = this.leaseClockPredicate(now);
|
|
211
|
+
const row = this.db.prepare(`
|
|
212
|
+
SELECT connection.*
|
|
213
|
+
FROM model_provider_connections connection
|
|
214
|
+
WHERE connection.tenant_id = ? AND connection.owner_user_id = ? AND connection.id = ?
|
|
215
|
+
AND EXISTS (
|
|
216
|
+
SELECT 1 FROM model_provider_connection_leases lease
|
|
217
|
+
WHERE lease.tenant_id = connection.tenant_id
|
|
218
|
+
AND lease.connection_id = connection.id
|
|
219
|
+
AND lease.lease_token = ?
|
|
220
|
+
AND lease.expires_at > ${clock.sql}
|
|
221
|
+
)
|
|
222
|
+
LIMIT 1
|
|
223
|
+
`).get(this.tenantId, ownerUserId, connectionId, leaseToken, ...clock.params);
|
|
224
|
+
if (!row) {
|
|
225
|
+
throw new ModelProviderConnectionError('Model connection operation lease is no longer valid.', 409, 'MODEL_CONNECTION_LEASE_LOST');
|
|
226
|
+
}
|
|
227
|
+
return mapRow(row);
|
|
228
|
+
}
|
|
229
|
+
deletePendingOwned(ownerUserId, connectionId) {
|
|
230
|
+
return this.db.transaction(() => {
|
|
231
|
+
const result = this.db.prepare(`
|
|
232
|
+
DELETE FROM model_provider_connections
|
|
233
|
+
WHERE tenant_id = ? AND owner_user_id = ? AND id = ? AND lifecycle_status = 'pending'
|
|
234
|
+
`).run(this.tenantId, ownerUserId, connectionId);
|
|
235
|
+
if (Number(result?.changes || 0) === 1) {
|
|
236
|
+
this.db.prepare(`
|
|
237
|
+
DELETE FROM model_provider_connection_events
|
|
238
|
+
WHERE tenant_id = ? AND connection_id = ?
|
|
239
|
+
`).run(this.tenantId, connectionId);
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
return false;
|
|
243
|
+
})();
|
|
244
|
+
}
|
|
245
|
+
acquireLease(input) {
|
|
246
|
+
this.requireOwned(input.ownerUserId, input.connectionId);
|
|
247
|
+
const operation = normalizeIdentifier(input.operation, 'Connection operation');
|
|
248
|
+
const durationMs = Math.min(10 * 60_000, Math.max(1_000, input.durationMs ?? 120_000));
|
|
249
|
+
const token = randomOpaqueId('lease');
|
|
250
|
+
if (this.db.provider === 'postgres') {
|
|
251
|
+
const row = this.db.prepare(`
|
|
252
|
+
INSERT INTO model_provider_connection_leases (
|
|
253
|
+
tenant_id, connection_id, lease_token, operation, acquired_at, expires_at, updated_at
|
|
254
|
+
) VALUES (
|
|
255
|
+
?, ?, ?, ?, ${POSTGRES_LEASE_CLOCK_SQL}, ${POSTGRES_LEASE_EXPIRY_SQL}, ${POSTGRES_LEASE_CLOCK_SQL}
|
|
256
|
+
)
|
|
257
|
+
ON CONFLICT (tenant_id, connection_id) DO UPDATE SET
|
|
258
|
+
lease_token = excluded.lease_token,
|
|
259
|
+
operation = excluded.operation,
|
|
260
|
+
acquired_at = ${POSTGRES_LEASE_CLOCK_SQL},
|
|
261
|
+
expires_at = ${POSTGRES_LEASE_EXPIRY_SQL},
|
|
262
|
+
updated_at = ${POSTGRES_LEASE_CLOCK_SQL}
|
|
263
|
+
WHERE model_provider_connection_leases.expires_at <= ${POSTGRES_LEASE_CLOCK_SQL}
|
|
264
|
+
RETURNING acquired_at, expires_at
|
|
265
|
+
`).get(this.tenantId, input.connectionId, token, operation, durationMs, durationMs);
|
|
266
|
+
if (!row) {
|
|
267
|
+
throw new ModelProviderConnectionError('Model connection is busy. Try again shortly.', 409, 'MODEL_CONNECTION_BUSY');
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
connectionId: input.connectionId,
|
|
271
|
+
token,
|
|
272
|
+
operation,
|
|
273
|
+
acquiredAt: String(row.acquired_at || ''),
|
|
274
|
+
expiresAt: String(row.expires_at || ''),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
const acquiredAt = this.leaseNow();
|
|
278
|
+
const acquiredAtIso = acquiredAt.toISOString();
|
|
279
|
+
const expiresAt = new Date(acquiredAt.getTime() + durationMs).toISOString();
|
|
280
|
+
const result = this.db.prepare(`
|
|
281
|
+
INSERT INTO model_provider_connection_leases (
|
|
282
|
+
tenant_id, connection_id, lease_token, operation, acquired_at, expires_at, updated_at
|
|
283
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
284
|
+
ON CONFLICT (tenant_id, connection_id) DO UPDATE SET
|
|
285
|
+
lease_token = excluded.lease_token,
|
|
286
|
+
operation = excluded.operation,
|
|
287
|
+
acquired_at = excluded.acquired_at,
|
|
288
|
+
expires_at = excluded.expires_at,
|
|
289
|
+
updated_at = excluded.updated_at
|
|
290
|
+
WHERE model_provider_connection_leases.expires_at <= excluded.acquired_at
|
|
291
|
+
`).run(this.tenantId, input.connectionId, token, operation, acquiredAtIso, expiresAt, acquiredAtIso);
|
|
292
|
+
if (Number(result?.changes || 0) !== 1) {
|
|
293
|
+
throw new ModelProviderConnectionError('Model connection is busy. Try again shortly.', 409, 'MODEL_CONNECTION_BUSY');
|
|
294
|
+
}
|
|
295
|
+
return { connectionId: input.connectionId, token, operation, acquiredAt: acquiredAtIso, expiresAt };
|
|
296
|
+
}
|
|
297
|
+
renewLease(lease, durationMs = 120_000) {
|
|
298
|
+
const boundedDurationMs = Math.min(10 * 60_000, Math.max(1_000, durationMs));
|
|
299
|
+
if (this.db.provider === 'postgres') {
|
|
300
|
+
const row = this.db.prepare(`
|
|
301
|
+
UPDATE model_provider_connection_leases
|
|
302
|
+
SET expires_at = ${POSTGRES_LEASE_EXPIRY_SQL},
|
|
303
|
+
updated_at = ${POSTGRES_LEASE_CLOCK_SQL}
|
|
304
|
+
WHERE tenant_id = ? AND connection_id = ? AND lease_token = ?
|
|
305
|
+
AND expires_at > ${POSTGRES_LEASE_CLOCK_SQL}
|
|
306
|
+
RETURNING expires_at
|
|
307
|
+
`).get(boundedDurationMs, this.tenantId, lease.connectionId, lease.token);
|
|
308
|
+
return Boolean(row);
|
|
309
|
+
}
|
|
310
|
+
const now = this.leaseNow();
|
|
311
|
+
const nowIso = now.toISOString();
|
|
312
|
+
const expiresAt = new Date(now.getTime() + boundedDurationMs).toISOString();
|
|
313
|
+
const clock = this.leaseClockPredicate(nowIso);
|
|
314
|
+
const result = this.db.prepare(`
|
|
315
|
+
UPDATE model_provider_connection_leases
|
|
316
|
+
SET expires_at = ?, updated_at = ?
|
|
317
|
+
WHERE tenant_id = ? AND connection_id = ? AND lease_token = ? AND expires_at > ${clock.sql}
|
|
318
|
+
`).run(expiresAt, nowIso, this.tenantId, lease.connectionId, lease.token, ...clock.params);
|
|
319
|
+
return Number(result?.changes || 0) === 1;
|
|
320
|
+
}
|
|
321
|
+
releaseLease(lease) {
|
|
322
|
+
const result = this.db.prepare(`
|
|
323
|
+
DELETE FROM model_provider_connection_leases
|
|
324
|
+
WHERE tenant_id = ? AND connection_id = ? AND lease_token = ?
|
|
325
|
+
`).run(this.tenantId, lease.connectionId, lease.token);
|
|
326
|
+
return Number(result?.changes || 0) === 1;
|
|
327
|
+
}
|
|
328
|
+
completeLease(lease) {
|
|
329
|
+
const now = this.leaseNow().toISOString();
|
|
330
|
+
const clock = this.leaseClockPredicate(now);
|
|
331
|
+
const result = this.db.prepare(`
|
|
332
|
+
DELETE FROM model_provider_connection_leases
|
|
333
|
+
WHERE tenant_id = ? AND connection_id = ? AND lease_token = ? AND expires_at > ${clock.sql}
|
|
334
|
+
`).run(this.tenantId, lease.connectionId, lease.token, ...clock.params);
|
|
335
|
+
return Number(result?.changes || 0) === 1;
|
|
336
|
+
}
|
|
337
|
+
deleteCleanableUnderLease(input) {
|
|
338
|
+
return this.db.transaction(() => {
|
|
339
|
+
const now = this.leaseNow().toISOString();
|
|
340
|
+
const clock = this.leaseClockPredicate(now);
|
|
341
|
+
const result = this.db.prepare(`
|
|
342
|
+
DELETE FROM model_provider_connections
|
|
343
|
+
WHERE tenant_id = ? AND owner_user_id = ? AND id = ?
|
|
344
|
+
AND lifecycle_status IN ('pending', 'reconnect_required', 'disconnected', 'revoked')
|
|
345
|
+
AND EXISTS (
|
|
346
|
+
SELECT 1 FROM model_provider_connection_leases lease
|
|
347
|
+
WHERE lease.tenant_id = model_provider_connections.tenant_id
|
|
348
|
+
AND lease.connection_id = model_provider_connections.id
|
|
349
|
+
AND lease.lease_token = ?
|
|
350
|
+
AND lease.expires_at > ${clock.sql}
|
|
351
|
+
)
|
|
352
|
+
`).run(this.tenantId, input.ownerUserId, input.connectionId, input.leaseToken, ...clock.params);
|
|
353
|
+
if (Number(result?.changes || 0) !== 1)
|
|
354
|
+
return false;
|
|
355
|
+
this.insertEvent({
|
|
356
|
+
connectionId: input.connectionId,
|
|
357
|
+
ownerUserId: input.ownerUserId,
|
|
358
|
+
actorUserId: input.ownerUserId,
|
|
359
|
+
eventType: 'cleaned_up',
|
|
360
|
+
outcome: 'success',
|
|
361
|
+
createdAt: now,
|
|
362
|
+
});
|
|
363
|
+
return true;
|
|
364
|
+
})();
|
|
365
|
+
}
|
|
366
|
+
markVerified(input) {
|
|
367
|
+
return this.updateUnderLease(input, 'active', {
|
|
368
|
+
displayMetadata: input.displayMetadata,
|
|
369
|
+
verified: true,
|
|
370
|
+
eventType: 'verified',
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
markRefreshed(input) {
|
|
374
|
+
return this.updateUnderLease(input, 'active', {
|
|
375
|
+
displayMetadata: input.displayMetadata,
|
|
376
|
+
refreshed: true,
|
|
377
|
+
eventType: 'refreshed',
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
markReconnectRequired(input) {
|
|
381
|
+
return this.updateUnderLease(input, 'reconnect_required', {
|
|
382
|
+
failureCode: normalizeIdentifier(input.failureCode, 'Failure code'),
|
|
383
|
+
eventType: 'reconnect_required',
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
markDisconnected(input) {
|
|
387
|
+
return this.updateUnderLease(input, 'disconnected', {
|
|
388
|
+
disconnected: true,
|
|
389
|
+
eventType: 'disconnected',
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
updateUnderLease(input, lifecycleStatus, options) {
|
|
393
|
+
return this.db.transaction(() => this.updateUnderLeaseTransaction(input, lifecycleStatus, options))();
|
|
394
|
+
}
|
|
395
|
+
updateUnderLeaseTransaction(input, lifecycleStatus, options) {
|
|
396
|
+
this.requireOwned(input.ownerUserId, input.connectionId);
|
|
397
|
+
const now = this.leaseNow().toISOString();
|
|
398
|
+
const clock = this.leaseClockPredicate(now);
|
|
399
|
+
const current = this.requireOwned(input.ownerUserId, input.connectionId);
|
|
400
|
+
const displayMetadata = options.displayMetadata === undefined
|
|
401
|
+
? current.displayMetadata
|
|
402
|
+
: {
|
|
403
|
+
...current.displayMetadata,
|
|
404
|
+
...sanitizeModelProviderConnectionDisplayMetadata(options.displayMetadata),
|
|
405
|
+
};
|
|
406
|
+
const result = this.db.prepare(`
|
|
407
|
+
UPDATE model_provider_connections
|
|
408
|
+
SET lifecycle_status = ?, display_metadata_json = ?,
|
|
409
|
+
last_verified_at = CASE WHEN ? = 1 THEN ? ELSE last_verified_at END,
|
|
410
|
+
last_refreshed_at = CASE WHEN ? = 1 THEN ? ELSE last_refreshed_at END,
|
|
411
|
+
last_failure_at = CASE WHEN CAST(? AS TEXT) IS NOT NULL THEN ? ELSE NULL END,
|
|
412
|
+
failure_code = ?,
|
|
413
|
+
disconnected_at = CASE WHEN ? = 1 THEN ? ELSE disconnected_at END,
|
|
414
|
+
updated_at = ?
|
|
415
|
+
WHERE tenant_id = ? AND owner_user_id = ? AND id = ?
|
|
416
|
+
AND EXISTS (
|
|
417
|
+
SELECT 1 FROM model_provider_connection_leases lease
|
|
418
|
+
WHERE lease.tenant_id = model_provider_connections.tenant_id
|
|
419
|
+
AND lease.connection_id = model_provider_connections.id
|
|
420
|
+
AND lease.lease_token = ?
|
|
421
|
+
AND lease.expires_at > ${clock.sql}
|
|
422
|
+
)
|
|
423
|
+
`).run(lifecycleStatus, JSON.stringify(displayMetadata), options.verified ? 1 : 0, now, options.refreshed ? 1 : 0, now, options.failureCode || null, now, options.failureCode || null, options.disconnected ? 1 : 0, now, now, this.tenantId, input.ownerUserId, input.connectionId, input.leaseToken, ...clock.params);
|
|
424
|
+
if (Number(result?.changes || 0) !== 1) {
|
|
425
|
+
throw new ModelProviderConnectionError('Model connection operation lease is no longer valid.', 409, 'MODEL_CONNECTION_LEASE_LOST');
|
|
426
|
+
}
|
|
427
|
+
this.insertEvent({
|
|
428
|
+
connectionId: input.connectionId,
|
|
429
|
+
ownerUserId: input.ownerUserId,
|
|
430
|
+
actorUserId: input.ownerUserId,
|
|
431
|
+
eventType: options.eventType,
|
|
432
|
+
outcome: options.failureCode ? 'failure' : 'success',
|
|
433
|
+
reasonCode: options.failureCode,
|
|
434
|
+
createdAt: now,
|
|
435
|
+
});
|
|
436
|
+
return this.requireOwned(input.ownerUserId, input.connectionId);
|
|
437
|
+
}
|
|
438
|
+
insertEvent(input) {
|
|
439
|
+
this.db.prepare(`
|
|
440
|
+
INSERT INTO model_provider_connection_events (
|
|
441
|
+
id, tenant_id, connection_id, owner_user_id, actor_user_id,
|
|
442
|
+
event_type, outcome, reason_code, created_at
|
|
443
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
444
|
+
`).run(randomOpaqueId('mpce'), this.tenantId, input.connectionId, input.ownerUserId, input.actorUserId, input.eventType, input.outcome, input.reasonCode || null, input.createdAt);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
@@ -9,6 +9,10 @@ export declare class PostgresWorkerUnavailableError extends Error {
|
|
|
9
9
|
readonly code = "POSTGRES_WORKER_UNAVAILABLE";
|
|
10
10
|
constructor();
|
|
11
11
|
}
|
|
12
|
+
export declare class PostgresDatabaseError extends Error {
|
|
13
|
+
readonly code: string;
|
|
14
|
+
constructor(message: string, code: string);
|
|
15
|
+
}
|
|
12
16
|
export declare function replaceQMarkParams(sql: string, params: unknown[]): {
|
|
13
17
|
text: string;
|
|
14
18
|
values: unknown[];
|
|
@@ -27,6 +27,14 @@ export class PostgresWorkerUnavailableError extends Error {
|
|
|
27
27
|
this.name = 'PostgresWorkerUnavailableError';
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
export class PostgresDatabaseError extends Error {
|
|
31
|
+
code;
|
|
32
|
+
constructor(message, code) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.code = code;
|
|
35
|
+
this.name = 'PostgresDatabaseError';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
30
38
|
function isPlainObject(value) {
|
|
31
39
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
32
40
|
}
|
|
@@ -171,8 +179,9 @@ export class PostgresAdapter {
|
|
|
171
179
|
}
|
|
172
180
|
const waitStartedAt = performance.now();
|
|
173
181
|
const waitResult = Atomics.wait(status, RESPONSE_STATUS_INDEX, RESPONSE_INITIAL, this.deadlines.responseTimeoutMs);
|
|
174
|
-
|
|
182
|
+
const workerWaitMs = performance.now() - waitStartedAt;
|
|
175
183
|
if (waitResult === 'timed-out') {
|
|
184
|
+
recordPostgresWorkerWait(payload.op, workerWaitMs);
|
|
176
185
|
this.quarantineWorker(worker);
|
|
177
186
|
throw new PostgresWorkerOutcomeUnknownError(payload.op);
|
|
178
187
|
}
|
|
@@ -182,6 +191,7 @@ export class PostgresAdapter {
|
|
|
182
191
|
}
|
|
183
192
|
const body = this.decoder.decode(responseBytes.subarray(0, responseLength));
|
|
184
193
|
const parsed = JSON.parse(body);
|
|
194
|
+
recordPostgresWorkerWait(payload.op, workerWaitMs, parsed.pool);
|
|
185
195
|
this.consecutiveUnexpectedExits = 0;
|
|
186
196
|
if (!parsed.ok && parsed.errorCode === 'RESPONSE_BUFFER_TOO_SMALL') {
|
|
187
197
|
const requiredBytes = Number(parsed.requiredBytes);
|
|
@@ -195,6 +205,9 @@ export class PostgresAdapter {
|
|
|
195
205
|
return this.callWorker({ op: 'read-response', responseToken }, requiredBytes);
|
|
196
206
|
}
|
|
197
207
|
if (!parsed.ok) {
|
|
208
|
+
if (parsed.databaseErrorCode) {
|
|
209
|
+
throw new PostgresDatabaseError(parsed.error || 'Unknown Postgres database error', parsed.databaseErrorCode);
|
|
210
|
+
}
|
|
198
211
|
throw new Error(parsed.error || 'Unknown Postgres worker error');
|
|
199
212
|
}
|
|
200
213
|
return parsed;
|
|
@@ -2,7 +2,15 @@ export type RequestPerformanceDiagnostics = {
|
|
|
2
2
|
postgresQueryCount: number;
|
|
3
3
|
postgresWorkerCallCount: number;
|
|
4
4
|
postgresWorkerWaitMs: number;
|
|
5
|
+
postgresWorkerWaitMaxMs: number;
|
|
6
|
+
postgresPoolWaitingMax: number;
|
|
7
|
+
postgresPoolTotalMax: number;
|
|
8
|
+
postgresPoolIdleMin: number | null;
|
|
5
9
|
};
|
|
6
10
|
export declare function runWithRequestPerformanceDiagnostics<T>(enabled: boolean, callback: () => T): T;
|
|
7
|
-
export declare function recordPostgresWorkerWait(operation: string, durationMs: number
|
|
11
|
+
export declare function recordPostgresWorkerWait(operation: string, durationMs: number, pool?: {
|
|
12
|
+
totalCount: number;
|
|
13
|
+
idleCount: number;
|
|
14
|
+
waitingCount: number;
|
|
15
|
+
}): void;
|
|
8
16
|
export declare function getRequestPerformanceDiagnostics(): RequestPerformanceDiagnostics | null;
|
|
@@ -7,16 +7,29 @@ export function runWithRequestPerformanceDiagnostics(enabled, callback) {
|
|
|
7
7
|
postgresQueryCount: 0,
|
|
8
8
|
postgresWorkerCallCount: 0,
|
|
9
9
|
postgresWorkerWaitMs: 0,
|
|
10
|
+
postgresWorkerWaitMaxMs: 0,
|
|
11
|
+
postgresPoolWaitingMax: 0,
|
|
12
|
+
postgresPoolTotalMax: 0,
|
|
13
|
+
postgresPoolIdleMin: null,
|
|
10
14
|
}, callback);
|
|
11
15
|
}
|
|
12
|
-
export function recordPostgresWorkerWait(operation, durationMs) {
|
|
16
|
+
export function recordPostgresWorkerWait(operation, durationMs, pool) {
|
|
13
17
|
const diagnostics = requestPerformanceStorage.getStore();
|
|
14
18
|
if (!diagnostics)
|
|
15
19
|
return;
|
|
16
20
|
diagnostics.postgresWorkerCallCount += 1;
|
|
17
21
|
if (operation === 'query')
|
|
18
22
|
diagnostics.postgresQueryCount += 1;
|
|
19
|
-
|
|
23
|
+
const boundedDurationMs = Math.max(0, durationMs);
|
|
24
|
+
diagnostics.postgresWorkerWaitMs += boundedDurationMs;
|
|
25
|
+
diagnostics.postgresWorkerWaitMaxMs = Math.max(diagnostics.postgresWorkerWaitMaxMs, boundedDurationMs);
|
|
26
|
+
if (pool) {
|
|
27
|
+
diagnostics.postgresPoolWaitingMax = Math.max(diagnostics.postgresPoolWaitingMax, pool.waitingCount);
|
|
28
|
+
diagnostics.postgresPoolTotalMax = Math.max(diagnostics.postgresPoolTotalMax, pool.totalCount);
|
|
29
|
+
diagnostics.postgresPoolIdleMin = diagnostics.postgresPoolIdleMin === null
|
|
30
|
+
? pool.idleCount
|
|
31
|
+
: Math.min(diagnostics.postgresPoolIdleMin, pool.idleCount);
|
|
32
|
+
}
|
|
20
33
|
}
|
|
21
34
|
export function getRequestPerformanceDiagnostics() {
|
|
22
35
|
const diagnostics = requestPerformanceStorage.getStore();
|