@taskforcehq/taskforce 0.3.327 → 0.3.329
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Taskforce.module.css +257 -87
- package/dist/TaskforceCore.js +237 -230
- package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.js +1 -1
- package/dist/cli.js +34 -0
- package/dist/cliCloudTargetGuard.d.ts +5 -0
- package/dist/cliCloudTargetGuard.js +11 -0
- package/dist/components/activity/EntityActivityTimeline.js +15 -5
- package/dist/components/context/AttachmentPreviewHost.js +1 -1
- package/dist/components/context/ContextAttachmentManager.d.ts +3 -0
- package/dist/components/context/ContextAttachmentManager.js +116 -20
- package/dist/components/features/AnnotatedAttachmentWorkspace.js +24 -8
- package/dist/components/features/TaskSettings.d.ts +12 -1
- package/dist/components/features/TaskSettings.js +147 -40
- package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +1 -1
- package/dist/components/features/planning/PlanningEntitySummary.d.ts +3 -1
- package/dist/components/features/planning/PlanningEntitySummary.js +3 -2
- package/dist/components/features/planning/PlanningLinkPicker.d.ts +3 -1
- package/dist/components/features/planning/PlanningLinkPicker.js +5 -3
- package/dist/components/features/planning/PlanningModule.js +28 -14
- package/dist/components/features/planning/PlanningTaskOrderList.d.ts +1 -1
- package/dist/components/features/planning/PlanningTaskOrderList.js +17 -6
- package/dist/components/features/planning/PlanningVisualIdentity.d.ts +14 -0
- package/dist/components/features/planning/PlanningVisualIdentity.js +106 -0
- package/dist/components/features/planning/planningTaskArrangement.d.ts +1 -0
- package/dist/components/features/planning/planningTaskArrangement.js +5 -5
- package/dist/components/features/planning/planningWorkspaceModel.d.ts +11 -0
- package/dist/components/features/taskforceAgentEditorModel.js +3 -1
- package/dist/components/task/TaskActionHeader.d.ts +2 -1
- package/dist/components/task/TaskActionHeader.js +2 -2
- package/dist/components/task/TaskCard.d.ts +5 -0
- package/dist/components/task/TaskCard.js +101 -13
- package/dist/components/task/TaskContextUpload.d.ts +3 -0
- package/dist/components/task/TaskContextUpload.js +2 -2
- package/dist/components/task/TaskForm.d.ts +4 -0
- package/dist/components/task/TaskForm.js +32 -7
- package/dist/components/task/TaskImageReviews.js +28 -19
- package/dist/components/task/TaskKanban.d.ts +6 -1
- package/dist/components/task/TaskKanban.js +152 -6
- package/dist/components/task/TaskList.d.ts +5 -0
- package/dist/components/task/TaskList.js +3 -3
- package/dist/components/task/TaskStatusActions.js +11 -2
- package/dist/components/task/TaskWorkstreamPicker.js +2 -1
- package/dist/components/ui/DocumentTypeIcon.d.ts +7 -0
- package/dist/components/ui/DocumentTypeIcon.js +8 -0
- package/dist/components/ui/PasswordInput.d.ts +6 -0
- package/dist/components/ui/PasswordInput.js +11 -0
- package/dist/components/ui/TaxonomyDropdown.d.ts +9 -1
- package/dist/components/ui/TaxonomyDropdown.js +30 -3
- package/dist/components/ui/TopNoticeLayer.js +1 -1
- package/dist/components/views/AppShellHeader.js +32 -11
- package/dist/components/views/PlanComparisonPage.js +12 -0
- package/dist/components/views/StandaloneLayout.js +68 -39
- package/dist/components/views/WidgetView.js +10 -7
- package/dist/components/views/panels/PlanningDrawer.d.ts +7 -1
- package/dist/components/views/panels/PlanningDrawer.js +51 -54
- package/dist/components/views/standalone/modals/AccountSettingsModal.js +26 -17
- package/dist/config/envSchema.js +13 -0
- package/dist/config/localServiceCloudTarget.d.ts +10 -0
- package/dist/config/localServiceCloudTarget.js +76 -0
- package/dist/config/localServiceRuntimeContract.d.ts +13 -0
- package/dist/config/localServiceRuntimeContract.js +135 -0
- package/dist/core/AiProfileService.d.ts +1 -0
- package/dist/core/AiProfileService.js +48 -9
- package/dist/core/AttachmentLinkService.js +7 -4
- package/dist/core/DeletedTaskLifecycleService.d.ts +5 -0
- package/dist/core/DeletedTaskLifecycleService.js +55 -3
- package/dist/core/McpTokenService.d.ts +4 -0
- package/dist/core/McpTokenService.js +52 -0
- package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +8 -2
- package/dist/core/TaskAttachmentLinksDurableMutationService.js +51 -18
- package/dist/core/Taskforce.d.ts +124 -2
- package/dist/core/Taskforce.js +1175 -87
- package/dist/core/types.d.ts +8 -1
- package/dist/documentReviews/DocumentReviewCommentStore.d.ts +11 -0
- package/dist/documentReviews/DocumentReviewCommentStore.js +35 -2
- package/dist/documentReviews/service.d.ts +4 -0
- package/dist/documentReviews/service.js +141 -46
- package/dist/hooks/sync/useDurableLocalOutboxDrain.d.ts +1 -1
- package/dist/hooks/sync/useDurableTaskCreateMutation.js +1 -1
- package/dist/hooks/sync/useDurableTaskMetadataMutation.d.ts +1 -0
- package/dist/hooks/sync/useDurableTaskMetadataMutation.js +1 -0
- package/dist/hooks/sync/useDurableTaskStatusMutation.d.ts +1 -0
- package/dist/hooks/tasks/taskHttpMutation.js +1 -0
- package/dist/hooks/tasks/useCloudTaskSearch.d.ts +12 -0
- package/dist/hooks/tasks/useCloudTaskSearch.js +71 -0
- package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.d.ts +16 -0
- package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.js +197 -0
- package/dist/hooks/tasks/useTaskEditorNavigation.d.ts +16 -3
- package/dist/hooks/tasks/useTaskEditorNavigation.js +72 -13
- package/dist/hooks/tasks/useTaskFormActions.d.ts +9 -0
- package/dist/hooks/tasks/useTaskFormActions.js +96 -17
- package/dist/hooks/ui/useSettingsModel.d.ts +24 -2
- package/dist/hooks/ui/useSettingsModel.js +13 -2
- package/dist/hooks/ui/useSettingsPersistence.d.ts +12 -1
- package/dist/hooks/ui/useSettingsPersistence.js +74 -47
- package/dist/hooks/useFilterSort.d.ts +7 -3
- package/dist/hooks/useFilterSort.js +15 -5
- package/dist/hooks/useTaskData.d.ts +10 -1
- package/dist/hooks/useTaskData.js +216 -7
- package/dist/hooks/useTaskMutations.d.ts +10 -2
- package/dist/hooks/useTaskMutations.js +223 -43
- package/dist/hooks/useTaskforce.d.ts +94 -57
- package/dist/hooks/useTaskforce.js +428 -16
- package/dist/hooks/useUiNotice.d.ts +4 -0
- package/dist/hooks/useUiNotice.js +4 -0
- package/dist/mcp/adminWorkspaceRegistrar.js +3 -3
- package/dist/mcp/canonicalAssetHelpers.d.ts +13 -0
- package/dist/mcp/canonicalAssetHelpers.js +39 -5
- package/dist/mcp/collaborationRegistrar.js +15 -13
- package/dist/mcp/documentAssetRegistrar.js +60 -44
- package/dist/mcp/documentHelpers.d.ts +1 -0
- package/dist/mcp/documentHelpers.js +7 -3
- package/dist/mcp/localServiceProxy.js +4 -1
- package/dist/mcp/runtime.d.ts +16 -0
- package/dist/mcp/runtime.js +735 -210
- package/dist/mcp/taskAttachmentCommandAdapter.d.ts +2 -0
- package/dist/mcp/taskAttachmentCommandAdapter.js +14 -8
- package/dist/mcp/taskPlanningRegistrar.js +78 -67
- package/dist/mcp/toolCallCompatibility.js +38 -1
- package/dist/mcp/toolCatalog.d.ts +1 -0
- package/dist/mcp/toolRegistry.js +2 -2
- package/dist/migrations/taskSchemaMigrations.js +47 -0
- package/dist/runtime/appGateDefinitions.d.ts +30 -6
- package/dist/runtime/appGateDefinitions.js +6 -6
- package/dist/runtime/appGates.d.ts +12 -13
- package/dist/runtime/appGates.js +29 -13
- package/dist/runtime/nodeAppGates.d.ts +8 -0
- package/dist/runtime/nodeAppGates.js +27 -0
- package/dist/server/appAccess.d.ts +12 -0
- package/dist/server/appAccess.js +21 -0
- package/dist/server/authorizedWorkspaceAccess.d.ts +12 -0
- package/dist/server/authorizedWorkspaceAccess.js +40 -0
- package/dist/server/cloudCollectionRead.d.ts +25 -0
- package/dist/server/cloudCollectionRead.js +120 -0
- package/dist/server/cloudCollectionReadPool.d.ts +52 -0
- package/dist/server/cloudCollectionReadPool.js +140 -0
- package/dist/server/cloudCollectionReadWorker.d.ts +1 -0
- package/dist/server/cloudCollectionReadWorker.js +36 -0
- package/dist/server/index.d.ts +6 -11
- package/dist/server/index.js +67 -42
- package/dist/server/localServiceLease.d.ts +6 -0
- package/dist/server/localServiceLease.js +17 -1
- package/dist/server/localWorkspaceSyncServerRuntime.js +10 -3
- package/dist/server/rateLimit.d.ts +1 -0
- package/dist/server/rateLimit.js +4 -0
- package/dist/server/realtimeSyncWs.js +36 -12
- package/dist/server/routes/admin.js +1 -0
- package/dist/server/routes/agentRoles.js +3 -2
- package/dist/server/routes/agentSkills.js +3 -2
- package/dist/server/routes/agents.js +118 -16
- package/dist/server/routes/annotatedAttachments.js +3 -2
- package/dist/server/routes/auth.js +3 -1
- package/dist/server/routes/documents.d.ts +5 -2
- package/dist/server/routes/documents.js +160 -13
- package/dist/server/routes/durableTaskHttpRouters.d.ts +11 -2
- package/dist/server/routes/durableTaskHttpRouters.js +2 -2
- package/dist/server/routes/localService.js +4 -0
- package/dist/server/routes/primitives.d.ts +4 -0
- package/dist/server/routes/shared.d.ts +24 -0
- package/dist/server/routes/shared.js +114 -18
- package/dist/server/routes/sync.js +22 -0
- package/dist/server/routes/syncAuxRoutes.js +5 -2
- package/dist/server/routes/syncPullApplyRoutes.js +23 -4
- package/dist/server/routes/syncV3FeedRoutes.js +22 -18
- package/dist/server/routes/syncV3LocalCaptureRoutes.js +48 -4
- package/dist/server/routes/syncV3LocalFeedApplyRoutes.d.ts +1 -0
- package/dist/server/routes/syncV3LocalFeedApplyRoutes.js +26 -0
- package/dist/server/routes/syncV3OperationRoutes.js +27 -2
- package/dist/server/routes/tasks.js +299 -97
- package/dist/server/routes/workspaces.js +43 -1
- package/dist/server/routes.js +35 -7
- package/dist/server/workspaceDeletionAdmission.d.ts +23 -0
- package/dist/server/workspaceDeletionAdmission.js +50 -0
- package/dist/service/localServiceCli.js +52 -25
- package/dist/service/localServiceClientLifecycle.js +93 -6
- package/dist/services/codexAppServerModelGateway.d.ts +112 -0
- package/dist/services/codexAppServerModelGateway.js +438 -0
- package/dist/services/taskforceAgentMcpBridge.d.ts +7 -1
- package/dist/services/taskforceAgentMcpBridge.js +23 -3
- package/dist/shared/planningIdentity.d.ts +20 -0
- package/dist/shared/planningIdentity.js +79 -0
- package/dist/shared/taskforceAgentDefinition.js +7 -1
- package/dist/shared/taskforceAgentModels.d.ts +1 -0
- package/dist/shared/taskforceAgentModels.js +30 -1
- package/dist/storage/postgresAdapter.d.ts +20 -3
- package/dist/storage/postgresAdapter.js +110 -14
- package/dist/storage/postgresRequestDiagnostics.d.ts +8 -0
- package/dist/storage/postgresRequestDiagnostics.js +24 -0
- package/dist/storage/postgresWorker.js +13 -4
- package/dist/storage/postgresWorkerProtocol.d.ts +29 -0
- package/dist/storage/postgresWorkerProtocol.js +30 -0
- package/dist/storage/workspaceAssetStore.d.ts +32 -0
- package/dist/storage/workspaceAssetStore.js +70 -0
- package/dist/sync/cloudSyncApi.d.ts +4 -1
- package/dist/sync/cloudSyncApi.js +7 -2
- package/dist/sync/contentSyncState.d.ts +1 -1
- package/dist/sync/coordinator/localSyncExecutionPermitStore.d.ts +17 -0
- package/dist/sync/coordinator/localSyncExecutionPermitStore.js +77 -23
- package/dist/sync/coordinator/localWorkspaceSyncRunner.js +6 -2
- package/dist/sync/coordinator/workspaceSyncTransportGateway.js +4 -2
- package/dist/sync/engine/workspaceSyncEngineBrowserGatewayRemoteServices.js +20 -2
- package/dist/sync/engine/workspaceSyncEngineLocalServices.d.ts +4 -0
- package/dist/sync/engine/workspaceSyncEngineLocalServices.js +11 -0
- package/dist/sync/engine/workspaceSyncEngineRemoteServices.d.ts +16 -0
- package/dist/sync/engine/workspaceSyncEngineRemoteServices.js +2 -1
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.d.ts +1 -1
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +46 -4
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.d.ts +3 -1
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +51 -11
- package/dist/sync/engine/workspaceSyncEngineTransfers.d.ts +1 -1
- package/dist/sync/engine/workspaceSyncEngineTransfers.js +8 -3
- package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +2 -0
- package/dist/sync/syncApplyHandlers.d.ts +3 -1
- package/dist/sync/syncApplyHandlers.js +29 -0
- package/dist/sync/syncService.d.ts +2 -0
- package/dist/sync/syncService.js +13 -1
- package/dist/sync/taskSyncPayload.d.ts +1 -0
- package/dist/sync/taskSyncPayload.js +6 -0
- package/dist/sync/v3/combinedFeedCapability.js +4 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.d.ts +3 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +31 -3
- package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +2 -0
- package/dist/sync/v3/durableTaskChecklistMutationRouter.js +4 -0
- package/dist/sync/v3/durableTaskHttpBulkMutationRouter.d.ts +1 -0
- package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +47 -12
- package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +5 -3
- package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +27 -2
- package/dist/sync/v3/localInitiativeOutboxService.js +4 -2
- package/dist/sync/v3/localOutboxDispatcher.js +5 -1
- package/dist/sync/v3/localTaskAttachmentLinksOutboxHandler.js +12 -2
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.d.ts +3 -0
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +62 -10
- package/dist/sync/v3/localTaskCreateOutboxService.js +15 -6
- package/dist/sync/v3/localTaskMetadataClient.d.ts +1 -0
- package/dist/sync/v3/localTaskMetadataClient.js +1 -0
- package/dist/sync/v3/localTaskMetadataOutboxHandler.js +5 -1
- package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -0
- package/dist/sync/v3/localTaskMetadataOutboxService.js +7 -0
- package/dist/sync/v3/localTaskRestoreOutboxService.js +14 -6
- package/dist/sync/v3/localTaskStatusClient.d.ts +1 -0
- package/dist/sync/v3/localTaskStatusClient.js +6 -1
- package/dist/sync/v3/localTaskStatusOutboxService.d.ts +2 -0
- package/dist/sync/v3/localTaskStatusOutboxService.js +54 -13
- package/dist/sync/v3/localWorkstreamOutboxService.js +4 -2
- package/dist/sync/v3/syncDurabilityStore.d.ts +15 -0
- package/dist/sync/v3/syncDurabilityStore.js +201 -43
- package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +7 -4
- package/dist/sync/v3/taskAttachmentLinksMutationService.js +37 -12
- package/dist/sync/v3/taskChecklistMutationService.d.ts +4 -2
- package/dist/sync/v3/taskChecklistMutationService.js +4 -0
- package/dist/sync/v3/taskCreateMutationService.d.ts +2 -2
- package/dist/sync/v3/taskCreateMutationService.js +7 -4
- package/dist/sync/v3/taskMetadataCoexistence.js +4 -0
- package/dist/sync/v3/taskMetadataMutationService.d.ts +3 -0
- package/dist/sync/v3/taskMetadataMutationService.js +4 -0
- package/dist/sync/v3/taskRestoreMutationService.d.ts +2 -2
- package/dist/sync/v3/taskRestoreMutationService.js +7 -4
- package/dist/sync/v3/taskStatusMutationService.d.ts +2 -0
- package/dist/sync/v3/taskStatusMutationService.js +4 -0
- package/dist/sync/v3/workflowAssignmentSync.d.ts +1 -0
- package/dist/sync/v3/workflowAssignmentSync.js +3 -0
- package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +14 -12
- package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.d.ts +11 -0
- package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.js +27 -10
- package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.d.ts +2 -1
- package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.js +52 -10
- package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +10 -1
- package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +15 -4
- package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +66 -13
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +11 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +57 -22
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +10 -4
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +124 -51
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +10 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +120 -28
- package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +3 -1
- package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +18 -0
- package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +6 -0
- package/dist/sync/v3/workspaceSyncV3FeedProfile.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.js +23 -10
- package/dist/sync/v3/workspaceSyncV3NestedTaskApply.d.ts +3 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskApply.js +64 -13
- package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.d.ts +9 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.js +26 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +15 -5
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +50 -14
- package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.d.ts +2 -1
- package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.js +11 -3
- package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +3 -0
- package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +19 -7
- package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.js +10 -0
- package/dist/sync/v3/workspaceSyncV3TaskCommentAudit.js +9 -2
- package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.d.ts +7 -0
- package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.js +37 -0
- package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.d.ts +6 -0
- package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.js +9 -0
- package/dist/sync/v3/workspaceSyncV3TaskRootProjection.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV3TaskRootProjection.js +1 -1
- package/dist/sync/workspacePullFeed.d.ts +6 -1
- package/dist/sync/workspacePullFeed.js +174 -162
- package/dist/sync/workspaceRepair.d.ts +3 -0
- package/dist/sync/workspaceRepair.js +67 -6
- package/dist/sync/workspaceSyncModel.d.ts +11 -0
- package/dist/sync/workspaceSyncModel.js +27 -2
- package/dist/types.d.ts +55 -1
- package/dist/types.js +6 -0
- package/dist/ui/assets/{AiIdentityRosterCard-DB1BLO_j.js → AiIdentityRosterCard-BZM4aQnM.js} +1 -1
- package/dist/ui/assets/AiProfilesModule-BbAEWtHH.js +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-B68Wg-RV.js +3 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-TzYu7pkG.css +1 -0
- package/dist/ui/assets/AssetTraySortControl-C8Rztyal.js +1 -0
- package/dist/ui/assets/ContextAttachmentManager-Cj3c7X9y.js +3 -0
- package/dist/ui/assets/{DocumentWorkspace-BC7sy5TG.js → DocumentWorkspace-B5uvzems.js} +4 -4
- package/dist/ui/assets/EntityActivityTimeline-BVFvCB5R.js +1 -0
- package/dist/ui/assets/PlanningModule-B5DWQOGz.js +1 -0
- package/dist/ui/assets/PlanningModule-CoIR9hbV.css +1 -0
- package/dist/ui/assets/PlansPage-AY4cGWco.js +1 -0
- package/dist/ui/assets/TaskContextUpload-DZxTVesW.js +1 -0
- package/dist/ui/assets/TaskContextUpload-qmBeUw3u.css +1 -0
- package/dist/ui/assets/TaskSettings-BeWAuyZj.js +12 -0
- package/dist/ui/assets/{TaskforceAgentsModule-CkAxW19K.js → TaskforceAgentsModule-BifCYtjR.js} +6 -6
- package/dist/ui/assets/WorkflowManagerModule-DEr5di_r.js +1 -0
- package/dist/ui/assets/index-I5zhdtgt.js +7 -0
- package/dist/ui/assets/index-VRFsx3wz.css +1 -0
- package/dist/ui/assets/{vendor-icons-B8ZNsH1g.js → vendor-icons-D9Lpw-j4.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/utils/constants.d.ts +2 -2
- package/dist/utils/constants.js +1 -1
- package/dist/utils/contextAttachmentUpload.d.ts +2 -0
- package/dist/utils/contextAttachmentUpload.js +7 -0
- package/dist/utils/contextFiles.d.ts +1 -1
- package/dist/utils/contextFiles.js +4 -4
- package/dist/utils/taskActivity.d.ts +3 -2
- package/dist/utils/taskActivity.js +19 -2
- package/dist/utils/taskEditorReconciliation.d.ts +40 -0
- package/dist/utils/taskEditorReconciliation.js +217 -0
- package/dist/utils/taskNormalization.d.ts +1 -0
- package/dist/utils/taskNormalization.js +39 -0
- package/dist/utils/taskSearch.js +2 -2
- package/dist/utils/uiNotice.d.ts +4 -0
- package/package.json +21 -4
- package/dist/ui/assets/AiProfilesModule-DPxQeF9N.js +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-CBRZ8rca.css +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-DHNYaNg6.js +0 -3
- package/dist/ui/assets/AssetTraySortControl-DphbyHVc.js +0 -1
- package/dist/ui/assets/ContextAttachmentManager-CMxSzHqc.js +0 -3
- package/dist/ui/assets/EntityActivityTimeline-pJ2ZjZ-k.js +0 -1
- package/dist/ui/assets/PlanningModule-CeTqXMMz.css +0 -1
- package/dist/ui/assets/PlanningModule-t5GbvVR9.js +0 -1
- package/dist/ui/assets/PlansPage-BVH_NfgT.js +0 -1
- package/dist/ui/assets/TaskContextUpload-Ds4LnDzV.css +0 -1
- package/dist/ui/assets/TaskContextUpload-bl4fOOUz.js +0 -1
- package/dist/ui/assets/TaskSettings-xVsxSa0g.js +0 -12
- package/dist/ui/assets/WorkflowManagerModule-BKqIGVnF.js +0 -1
- package/dist/ui/assets/index-BzJlVnNS.css +0 -1
- package/dist/ui/assets/index-Cx7vZJHL.js +0 -7
package/dist/config/envSchema.js
CHANGED
|
@@ -11,6 +11,7 @@ const SPECS = [
|
|
|
11
11
|
{ key: 'TASKFORCE_DB_PROVIDER', description: 'Database provider (sqlite|postgres).', category: 'Core', defaultValue: 'sqlite', example: 'postgres', targets: ['server', 'all'] },
|
|
12
12
|
{ key: 'TASKFORCE_BILLING_ENABLED', description: 'Enable Stripe billing routes and webhook processing.', category: 'Core', defaultValue: 'false', targets: ['server', 'all'] },
|
|
13
13
|
{ key: 'TASKFORCE_DATABASE_URL', description: 'Postgres connection string when provider=postgres.', category: 'Core', example: 'postgresql://user:pass@host:5432/db', secret: true, targets: ['server', 'all'] },
|
|
14
|
+
{ key: 'TASKFORCE_CLOUD_READ_WORKERS', description: 'Bounded cloud collection-read worker count (1-8, or 0 to disable).', category: 'Core', defaultValue: '2', targets: ['server', 'all'] },
|
|
14
15
|
{ key: 'TASKFORCE_ALLOW_SQLITE_IN_CLOUD', description: 'Emergency override to allow sqlite in cloud runtime.', category: 'Core', defaultValue: 'false', targets: ['server', 'all'] },
|
|
15
16
|
{ key: 'TASKFORCE_FORCE_SETUP', description: 'Force local setup gate for development.', category: 'Core', defaultValue: 'false', targets: ['server', 'all'] },
|
|
16
17
|
{ key: 'TASKFORCE_FORCE_GLOBAL_SETUP', description: 'Force global setup step for development.', category: 'Core', defaultValue: 'false', targets: ['server', 'all'] },
|
|
@@ -142,10 +143,15 @@ const SPECS = [
|
|
|
142
143
|
{ key: 'TASKFORCE_BEDROCK_SMOKE_MAX_TOKENS', description: 'Maximum output tokens for live Bedrock smoke tests.', category: 'AI', defaultValue: '128', targets: ['server', 'all'] },
|
|
143
144
|
{ key: 'TASKFORCE_BEDROCK_MANTLE_MODEL_KEY', description: 'Optional model key for Bedrock Mantle smoke tests (openai.gpt-5-4|xai.grok-4-3).', category: 'AI', defaultValue: 'openai.gpt-5-4', targets: ['server', 'all'] },
|
|
144
145
|
{ key: 'TASKFORCE_BEDROCK_ENV_FILE', description: 'Optional env file path for the Bedrock smoke script.', category: 'AI', defaultValue: '.env.local', targets: ['server', 'all'] },
|
|
146
|
+
{ key: 'TASKFORCE_AGENT_SUBSCRIPTION_DEV_MODELS', description: 'Enable internal local-development-only Taskforce Agent subscription-auth model adapters such as OpenAI Codex app-server.', category: 'AI', defaultValue: 'false', targets: ['server', 'all'] },
|
|
147
|
+
{ key: 'TASKFORCE_CODEX_APP_SERVER_COMMAND', description: 'Optional Codex CLI command path for the internal OpenAI Codex app-server Taskforce Agent adapter.', category: 'AI', defaultValue: 'codex', targets: ['server', 'all'] },
|
|
148
|
+
{ key: 'TASKFORCE_CODEX_APP_SERVER_CWD', description: 'Optional working directory for the internal Codex app-server subprocess. Defaults to the system temp directory.', category: 'AI', targets: ['server', 'all'] },
|
|
145
149
|
{ key: 'TASKFORCE_ALLOWED_ORIGINS', description: 'Allowed CORS origins (comma-separated).', category: 'Security', targets: ['server', 'all'] },
|
|
146
150
|
{ key: 'TASKFORCE_API_RATE_LIMIT_ENABLED', description: 'Enable API rate limiter.', category: 'Security', defaultValue: 'true', targets: ['server', 'all'] },
|
|
147
151
|
{ key: 'TASKFORCE_API_RATE_LIMIT_MAX_REQUESTS', description: 'API rate limit max requests per window.', category: 'Security', defaultValue: '120', targets: ['server', 'all'] },
|
|
148
152
|
{ key: 'TASKFORCE_API_RATE_LIMIT_WINDOW_MS', description: 'API rate limit window size in ms.', category: 'Security', defaultValue: '60000', targets: ['server', 'all'] },
|
|
153
|
+
{ key: 'TASKFORCE_SYNC_FEED_RATE_LIMIT_MAX_REQUESTS', description: 'Workspace sync feed read limit per window.', category: 'Security', defaultValue: '600', targets: ['server', 'all'] },
|
|
154
|
+
{ key: 'TASKFORCE_SYNC_FEED_RATE_LIMIT_WINDOW_MS', description: 'Workspace sync feed read limit window size in ms.', category: 'Security', defaultValue: '60000', targets: ['server', 'all'] },
|
|
149
155
|
{ key: 'TASKFORCE_STRIPE_WEBHOOK_RATE_LIMIT_MAX_REQUESTS', description: 'Stripe webhook-specific rate limit max requests per window.', category: 'Security', defaultValue: '600', targets: ['server', 'all'] },
|
|
150
156
|
{ key: 'TASKFORCE_STRIPE_WEBHOOK_RATE_LIMIT_WINDOW_MS', description: 'Stripe webhook-specific rate limit window size in ms.', category: 'Security', defaultValue: '60000', targets: ['server', 'all'] },
|
|
151
157
|
{ key: 'TASKFORCE_MAX_PAYLOAD_BYTES', description: 'Maximum accepted API payload size in bytes.', category: 'Security', defaultValue: '1048576', targets: ['server', 'all'] },
|
|
@@ -167,6 +173,8 @@ const SPECS = [
|
|
|
167
173
|
{ key: 'TASKFORCE_BUILD_DEPLOY_ID', description: 'Build deploy ID metadata.', category: 'Build', targets: ['server', 'all'] },
|
|
168
174
|
{ key: 'TASKFORCE_UI_DIST_DIR', description: 'Override the standalone UI distribution directory for isolated local QA runtimes.', category: 'Build', targets: ['server', 'all'] },
|
|
169
175
|
{ key: 'TASKFORCE_REQUIRE_EXACT_PORT', description: 'Require the standalone server to use only its requested port.', category: 'Core', defaultValue: 'false', targets: ['server', 'all'] },
|
|
176
|
+
{ key: 'TASKFORCE_QA_EXPECTED_WORKSPACE_ID', description: 'Require isolated cloud QA to resolve the exact expected workspace ID before mutation.', category: 'Core', targets: ['server', 'all'] },
|
|
177
|
+
{ key: 'TASKFORCE_QA_EXPECTED_WORKSPACE_SLUG', description: 'Require isolated cloud QA to resolve the expected workspace slug before mutation.', category: 'Core', targets: ['server', 'all'] },
|
|
170
178
|
{ key: 'DEBUG_MODE', description: 'Expose debug-only client diagnostics such as pricing source metadata.', category: 'Build', defaultValue: 'false', targets: ['all'] },
|
|
171
179
|
{ key: 'VITE_TASKFORCE_BASE_URL', description: 'Client base URL for both API and cloud auth when specific overrides are not set.', category: 'Client', example: 'https://performance-app.taskforcehq.ai', targets: ['all'] },
|
|
172
180
|
{ key: 'VITE_TASKFORCE_API_BASE_URL', description: 'Client API base URL (overrides VITE_TASKFORCE_BASE_URL when set).', category: 'Client', example: 'https://performance-app.taskforcehq.ai', targets: ['all'] },
|
|
@@ -184,17 +192,20 @@ const SPECS = [
|
|
|
184
192
|
{ key: 'SYNC_SOAK_PROFILE', description: 'Soak profile (smoke|stress|overnight).', category: 'Soak', defaultValue: 'smoke', targets: ['soak', 'all'] },
|
|
185
193
|
{ key: 'SYNC_SOAK_DURATION_MIN', description: 'Soak duration in minutes.', category: 'Soak', defaultValue: '60', targets: ['soak', 'all'] },
|
|
186
194
|
{ key: 'SYNC_SOAK_SEED', description: 'Deterministic soak seed.', category: 'Soak', example: '4242', targets: ['soak', 'all'] },
|
|
195
|
+
{ key: 'SYNC_SOAK_TASK_ID_NAMESPACE', description: 'Optional per-run namespace for deterministic generated soak task IDs.', category: 'Soak', targets: ['soak', 'all'] },
|
|
187
196
|
{ key: 'SYNC_SOAK_ARTIFACT_DIR', description: 'Soak artifacts output directory.', category: 'Soak', defaultValue: '.taskforce/soak-artifacts', targets: ['soak', 'all'] },
|
|
188
197
|
{ key: 'SYNC_SOAK_ENABLE_SYNC_DEBUG', description: 'Enable sync debug diagnostics during soak.', category: 'Soak', defaultValue: 'false', targets: ['soak', 'all'] },
|
|
189
198
|
{ key: 'SYNC_SOAK_STRESS_ACTIONS', description: 'Stress action count.', category: 'Soak', defaultValue: '150', targets: ['soak', 'all'] },
|
|
190
199
|
{ key: 'SYNC_SOAK_STRESS_CHECKPOINT_INTERVAL', description: 'Stress checkpoint interval.', category: 'Soak', defaultValue: '50', targets: ['soak', 'all'] },
|
|
191
200
|
{ key: 'SYNC_SOAK_STRESS_SETTLE_RETRIES', description: 'Stress settle retry count.', category: 'Soak', defaultValue: '3', targets: ['soak', 'all'] },
|
|
192
201
|
{ key: 'SYNC_SOAK_STRESS_SETTLE_WAIT_MS', description: 'Stress settle wait time in ms.', category: 'Soak', defaultValue: '500', targets: ['soak', 'all'] },
|
|
202
|
+
{ key: 'SYNC_SOAK_STRESS_BOOTSTRAP_PUSH_SETTLE', description: 'Enable legacy bootstrap-push checkpoint settlement in stress and overnight profiles.', category: 'Soak', defaultValue: 'true', targets: ['soak', 'all'] },
|
|
193
203
|
{ key: 'SYNC_SOAK_STRESS_HARD_RESET_SETTLE', description: 'Enable hard reset settle path in stress.', category: 'Soak', defaultValue: 'true', targets: ['soak', 'all'] },
|
|
194
204
|
{ key: 'SYNC_SOAK_OVERNIGHT_ACTION_INTERVAL_MS', description: 'Overnight action pacing interval in ms.', category: 'Soak', defaultValue: '60000', targets: ['soak', 'all'] },
|
|
195
205
|
{ key: 'SYNC_SOAK_OVERNIGHT_CHECKPOINT_MIN', description: 'Overnight checkpoint interval in minutes.', category: 'Soak', defaultValue: '15', targets: ['soak', 'all'] },
|
|
196
206
|
{ key: 'SYNC_SOAK_OVERNIGHT_MAX_ACTIONS', description: 'Overnight hard cap on action count.', category: 'Soak', defaultValue: '10000', targets: ['soak', 'all'] },
|
|
197
207
|
{ key: 'SYNC_SOAK_OVERNIGHT_MIN_ACTIONS', description: 'Overnight minimum actions expected before pass.', category: 'Soak', defaultValue: '80', targets: ['soak', 'all'] },
|
|
208
|
+
{ key: 'SYNC_SOAK_OVERNIGHT_CLEAN_BASELINE_EVERY_ACTION', description: 'Require coordinator settlement before each accelerated overnight action.', category: 'Soak', defaultValue: 'false', targets: ['soak', 'all'] },
|
|
198
209
|
{ key: 'CF_ACCESS_CLIENT_ID', description: 'Cloudflare Access service token client id (soak).', category: 'Cloudflare', secret: true, targets: ['soak', 'all'] },
|
|
199
210
|
{ key: 'CF_ACCESS_CLIENT_SECRET', description: 'Cloudflare Access service token client secret (soak).', category: 'Cloudflare', secret: true, targets: ['soak', 'all'] }
|
|
200
211
|
];
|
|
@@ -254,6 +265,8 @@ function validateCommonNumbers(env, errors) {
|
|
|
254
265
|
'PORT',
|
|
255
266
|
'TASKFORCE_API_RATE_LIMIT_MAX_REQUESTS',
|
|
256
267
|
'TASKFORCE_API_RATE_LIMIT_WINDOW_MS',
|
|
268
|
+
'TASKFORCE_SYNC_FEED_RATE_LIMIT_MAX_REQUESTS',
|
|
269
|
+
'TASKFORCE_SYNC_FEED_RATE_LIMIT_WINDOW_MS',
|
|
257
270
|
'TASKFORCE_STRIPE_WEBHOOK_RATE_LIMIT_MAX_REQUESTS',
|
|
258
271
|
'TASKFORCE_STRIPE_WEBHOOK_RATE_LIMIT_WINDOW_MS',
|
|
259
272
|
'TASKFORCE_MAX_PAYLOAD_BYTES',
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type TaskforceCloudEnvironment } from './cloudEnvironment.js';
|
|
2
|
+
export interface LocalServiceCloudTarget {
|
|
3
|
+
environment: TaskforceCloudEnvironment | null;
|
|
4
|
+
cloudProxyBaseUrl: string;
|
|
5
|
+
cloudAuthBaseUrl: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveLocalServiceCloudTarget(env: NodeJS.ProcessEnv): LocalServiceCloudTarget;
|
|
8
|
+
export declare function applyLocalServiceCloudTarget(env: NodeJS.ProcessEnv, target: LocalServiceCloudTarget): void;
|
|
9
|
+
export declare function parseLocalServiceCloudTarget(value: unknown): LocalServiceCloudTarget | null;
|
|
10
|
+
export declare function localServiceCloudTargetsEqual(left: LocalServiceCloudTarget | null | undefined, right: LocalServiceCloudTarget | null | undefined): boolean;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { TASKFORCE_CLOUD_ENVIRONMENTS, parseTaskforceCloudEnvironment, resolveTaskforceCloudEnvironmentFromBaseUrl, } from './cloudEnvironment.js';
|
|
2
|
+
import { resolveDeploymentConfig } from './deployment.js';
|
|
3
|
+
function normalizeCloudBaseUrl(raw, label) {
|
|
4
|
+
let parsed;
|
|
5
|
+
try {
|
|
6
|
+
parsed = new URL(String(raw || '').trim());
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
throw new Error(`${label} must be a valid HTTP(S) URL.`);
|
|
10
|
+
}
|
|
11
|
+
if (!['http:', 'https:'].includes(parsed.protocol) || parsed.username || parsed.password) {
|
|
12
|
+
throw new Error(`${label} must be an HTTP(S) URL without embedded credentials.`);
|
|
13
|
+
}
|
|
14
|
+
if (parsed.search || parsed.hash) {
|
|
15
|
+
throw new Error(`${label} must not include a query string or fragment.`);
|
|
16
|
+
}
|
|
17
|
+
const pathname = parsed.pathname.replace(/\/+$/, '');
|
|
18
|
+
return pathname ? `${parsed.origin}${pathname}` : parsed.origin;
|
|
19
|
+
}
|
|
20
|
+
export function resolveLocalServiceCloudTarget(env) {
|
|
21
|
+
const rawEnvironment = String(env.TASKFORCE_CLOUD_ENVIRONMENT || '').trim();
|
|
22
|
+
const explicitEnvironment = parseTaskforceCloudEnvironment(rawEnvironment);
|
|
23
|
+
if (rawEnvironment && !explicitEnvironment) {
|
|
24
|
+
throw new Error(`Unknown Taskforce cloud environment: ${rawEnvironment}.`);
|
|
25
|
+
}
|
|
26
|
+
const deployment = resolveDeploymentConfig(env);
|
|
27
|
+
const productionOrigin = TASKFORCE_CLOUD_ENVIRONMENTS.production.appBaseUrl;
|
|
28
|
+
const cloudProxyBaseUrl = normalizeCloudBaseUrl(deployment.cloudBaseUrl || productionOrigin, 'Taskforce cloud proxy URL');
|
|
29
|
+
const cloudAuthBaseUrl = normalizeCloudBaseUrl(deployment.cloudAuthBaseUrl || cloudProxyBaseUrl, 'Taskforce cloud auth URL');
|
|
30
|
+
const proxyEnvironment = resolveTaskforceCloudEnvironmentFromBaseUrl(cloudProxyBaseUrl);
|
|
31
|
+
const authEnvironment = resolveTaskforceCloudEnvironmentFromBaseUrl(cloudAuthBaseUrl);
|
|
32
|
+
const knownEnvironments = [explicitEnvironment, proxyEnvironment, authEnvironment]
|
|
33
|
+
.filter((value) => Boolean(value));
|
|
34
|
+
if (new Set(knownEnvironments).size > 1) {
|
|
35
|
+
throw new Error('Taskforce cloud environment, proxy URL, and auth URL must identify the same cloud target.');
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
environment: explicitEnvironment || proxyEnvironment || authEnvironment,
|
|
39
|
+
cloudProxyBaseUrl,
|
|
40
|
+
cloudAuthBaseUrl,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function applyLocalServiceCloudTarget(env, target) {
|
|
44
|
+
if (target.environment)
|
|
45
|
+
env.TASKFORCE_CLOUD_ENVIRONMENT = target.environment;
|
|
46
|
+
env.TASKFORCE_CLOUD_PROXY_BASE_URL ||= target.cloudProxyBaseUrl;
|
|
47
|
+
env.VITE_TASKFORCE_CLOUD_AUTH_BASE_URL ||= target.cloudAuthBaseUrl;
|
|
48
|
+
}
|
|
49
|
+
export function parseLocalServiceCloudTarget(value) {
|
|
50
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
51
|
+
return null;
|
|
52
|
+
const record = value;
|
|
53
|
+
const environment = record.environment === null
|
|
54
|
+
? null
|
|
55
|
+
: parseTaskforceCloudEnvironment(record.environment);
|
|
56
|
+
if (record.environment !== null && !environment)
|
|
57
|
+
return null;
|
|
58
|
+
if (typeof record.cloudProxyBaseUrl !== 'string' || typeof record.cloudAuthBaseUrl !== 'string')
|
|
59
|
+
return null;
|
|
60
|
+
try {
|
|
61
|
+
const cloudProxyBaseUrl = normalizeCloudBaseUrl(record.cloudProxyBaseUrl, 'Taskforce cloud proxy base URL');
|
|
62
|
+
const cloudAuthBaseUrl = normalizeCloudBaseUrl(record.cloudAuthBaseUrl, 'Taskforce cloud auth base URL');
|
|
63
|
+
if (cloudProxyBaseUrl !== record.cloudProxyBaseUrl || cloudAuthBaseUrl !== record.cloudAuthBaseUrl)
|
|
64
|
+
return null;
|
|
65
|
+
return { environment, cloudProxyBaseUrl, cloudAuthBaseUrl };
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function localServiceCloudTargetsEqual(left, right) {
|
|
72
|
+
return Boolean(left && right
|
|
73
|
+
&& left.environment === right.environment
|
|
74
|
+
&& left.cloudProxyBaseUrl === right.cloudProxyBaseUrl
|
|
75
|
+
&& left.cloudAuthBaseUrl === right.cloudAuthBaseUrl);
|
|
76
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface LocalServiceRuntimeContract {
|
|
2
|
+
schemaVersion: 1;
|
|
3
|
+
fingerprint: string;
|
|
4
|
+
coverageVersion: number | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function hydrateSourceCheckoutServiceEnvironment(input: {
|
|
7
|
+
env: NodeJS.ProcessEnv;
|
|
8
|
+
projectRoot: string;
|
|
9
|
+
packageRoot: string;
|
|
10
|
+
}): string | null;
|
|
11
|
+
export declare function resolveLocalServiceRuntimeContract(env: NodeJS.ProcessEnv): LocalServiceRuntimeContract;
|
|
12
|
+
export declare function parseLocalServiceRuntimeContract(value: unknown): LocalServiceRuntimeContract | null;
|
|
13
|
+
export declare function localServiceRuntimeContractsEqual(left: LocalServiceRuntimeContract | null | undefined, right: LocalServiceRuntimeContract | null | undefined): boolean;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { parseTaskforceCloudEnvironment } from './cloudEnvironment.js';
|
|
5
|
+
const SERVICE_ENV_EXACT_KEYS = new Set([
|
|
6
|
+
'CF_ACCESS_CLIENT_ID',
|
|
7
|
+
'CF_ACCESS_CLIENT_SECRET',
|
|
8
|
+
'TASKFORCE_CF_ACCESS_CLIENT_ID',
|
|
9
|
+
'TASKFORCE_CF_ACCESS_CLIENT_SECRET',
|
|
10
|
+
'TASKFORCE_REALTIME_SYNC',
|
|
11
|
+
'TASKFORCE_RUNTIME_MODE',
|
|
12
|
+
]);
|
|
13
|
+
function isServiceEnvironmentKey(key) {
|
|
14
|
+
if (SERVICE_ENV_EXACT_KEYS.has(key))
|
|
15
|
+
return true;
|
|
16
|
+
if (key.startsWith('TASKFORCE_E2E_') || key.startsWith('TASKFORCE_PLAYWRIGHT_'))
|
|
17
|
+
return false;
|
|
18
|
+
return key.startsWith('TASKFORCE_SYNC_')
|
|
19
|
+
|| key.startsWith('TASKFORCE_LOCAL_SYNC_')
|
|
20
|
+
|| key.startsWith('VITE_TASKFORCE_');
|
|
21
|
+
}
|
|
22
|
+
function isContractKey(key) {
|
|
23
|
+
return key === 'TASKFORCE_RUNTIME_MODE'
|
|
24
|
+
|| key === 'TASKFORCE_REALTIME_SYNC'
|
|
25
|
+
|| key.startsWith('TASKFORCE_SYNC_')
|
|
26
|
+
|| key.startsWith('TASKFORCE_LOCAL_SYNC_');
|
|
27
|
+
}
|
|
28
|
+
function stripQuotes(value) {
|
|
29
|
+
const trimmed = value.trim();
|
|
30
|
+
if (trimmed.length >= 2) {
|
|
31
|
+
const first = trimmed[0];
|
|
32
|
+
const last = trimmed[trimmed.length - 1];
|
|
33
|
+
if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
|
|
34
|
+
return trimmed.slice(1, -1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return trimmed;
|
|
38
|
+
}
|
|
39
|
+
function isInside(parent, child) {
|
|
40
|
+
const relative = path.relative(parent, child);
|
|
41
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
42
|
+
}
|
|
43
|
+
function loadTrustedEnvFile(filePath, packageRoot, loaded, visited) {
|
|
44
|
+
const resolved = fs.realpathSync(filePath);
|
|
45
|
+
if (!isInside(packageRoot, resolved) || !path.basename(resolved).startsWith('.env')) {
|
|
46
|
+
throw new Error('Taskforce source-checkout environment includes must remain trusted .env files inside the package root.');
|
|
47
|
+
}
|
|
48
|
+
if (visited.has(resolved))
|
|
49
|
+
return;
|
|
50
|
+
visited.add(resolved);
|
|
51
|
+
const lines = fs.readFileSync(resolved, 'utf8').split(/\r?\n/);
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
const trimmed = line.trim();
|
|
54
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
55
|
+
continue;
|
|
56
|
+
const include = trimmed.match(/^(?:\.|source)\s+([^\s]+)$/);
|
|
57
|
+
if (include) {
|
|
58
|
+
const includePath = path.resolve(path.dirname(resolved), stripQuotes(include[1]));
|
|
59
|
+
loadTrustedEnvFile(includePath, packageRoot, loaded, visited);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const normalized = trimmed.startsWith('export ') ? trimmed.slice(7).trim() : trimmed;
|
|
63
|
+
const separator = normalized.indexOf('=');
|
|
64
|
+
if (separator <= 0)
|
|
65
|
+
continue;
|
|
66
|
+
const key = normalized.slice(0, separator).trim();
|
|
67
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(key) || !isServiceEnvironmentKey(key))
|
|
68
|
+
continue;
|
|
69
|
+
loaded.set(key, stripQuotes(normalized.slice(separator + 1)));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export function hydrateSourceCheckoutServiceEnvironment(input) {
|
|
73
|
+
const projectRoot = fs.realpathSync(input.projectRoot);
|
|
74
|
+
const packageRoot = fs.realpathSync(input.packageRoot);
|
|
75
|
+
if (projectRoot !== packageRoot)
|
|
76
|
+
return null;
|
|
77
|
+
const environment = parseTaskforceCloudEnvironment(input.env.TASKFORCE_CLOUD_ENVIRONMENT);
|
|
78
|
+
if (environment !== 'performance' && environment !== 'staging')
|
|
79
|
+
return null;
|
|
80
|
+
const envFile = path.join(packageRoot, `.env.${environment}`);
|
|
81
|
+
if (!fs.existsSync(envFile))
|
|
82
|
+
return null;
|
|
83
|
+
const explicitKeys = new Set(Object.keys(input.env));
|
|
84
|
+
const loaded = new Map();
|
|
85
|
+
loadTrustedEnvFile(envFile, packageRoot, loaded, new Set());
|
|
86
|
+
for (const [key, value] of loaded) {
|
|
87
|
+
if (!explicitKeys.has(key))
|
|
88
|
+
input.env[key] = value;
|
|
89
|
+
}
|
|
90
|
+
return envFile;
|
|
91
|
+
}
|
|
92
|
+
export function resolveLocalServiceRuntimeContract(env) {
|
|
93
|
+
const effectiveEnv = {
|
|
94
|
+
...env,
|
|
95
|
+
TASKFORCE_RUNTIME_MODE: String(env.TASKFORCE_RUNTIME_MODE || 'local').trim().toLowerCase(),
|
|
96
|
+
};
|
|
97
|
+
const entries = Object.entries(effectiveEnv)
|
|
98
|
+
.filter(([key, value]) => isContractKey(key) && value !== undefined)
|
|
99
|
+
.map(([key, value]) => [key, String(value)])
|
|
100
|
+
.sort(([left], [right]) => left.localeCompare(right));
|
|
101
|
+
const fingerprint = createHash('sha256')
|
|
102
|
+
.update(JSON.stringify(entries))
|
|
103
|
+
.digest('hex');
|
|
104
|
+
const rawCoverageVersion = Number.parseInt(String(env.TASKFORCE_SYNC_V3_COMBINED_FEED_COVERAGE_VERSION || ''), 10);
|
|
105
|
+
return {
|
|
106
|
+
schemaVersion: 1,
|
|
107
|
+
fingerprint,
|
|
108
|
+
coverageVersion: Number.isSafeInteger(rawCoverageVersion) && rawCoverageVersion > 0
|
|
109
|
+
? rawCoverageVersion
|
|
110
|
+
: null,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export function parseLocalServiceRuntimeContract(value) {
|
|
114
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
115
|
+
return null;
|
|
116
|
+
const record = value;
|
|
117
|
+
if (record.schemaVersion !== 1
|
|
118
|
+
|| typeof record.fingerprint !== 'string'
|
|
119
|
+
|| !/^[a-f0-9]{64}$/.test(record.fingerprint)
|
|
120
|
+
|| (record.coverageVersion !== null
|
|
121
|
+
&& (!Number.isSafeInteger(record.coverageVersion) || Number(record.coverageVersion) < 1))) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
schemaVersion: 1,
|
|
126
|
+
fingerprint: record.fingerprint,
|
|
127
|
+
coverageVersion: record.coverageVersion === null ? null : Number(record.coverageVersion),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export function localServiceRuntimeContractsEqual(left, right) {
|
|
131
|
+
return Boolean(left && right
|
|
132
|
+
&& left.schemaVersion === right.schemaVersion
|
|
133
|
+
&& left.fingerprint === right.fingerprint
|
|
134
|
+
&& left.coverageVersion === right.coverageVersion);
|
|
135
|
+
}
|
|
@@ -138,6 +138,7 @@ export declare function getAiProfileByConnectionService(deps: AiProfileServiceDe
|
|
|
138
138
|
workspaceId: string;
|
|
139
139
|
connectionId: string;
|
|
140
140
|
seatScope?: AiProfileSeatScope | null;
|
|
141
|
+
repairLegacyBinding?: boolean;
|
|
141
142
|
}): AiProfileRecordLike | null;
|
|
142
143
|
export declare function archiveAiProfileService(deps: AiProfileServiceDeps, input: {
|
|
143
144
|
workspaceId: string;
|
|
@@ -931,15 +931,27 @@ function reconcileSyncedAiProfileConnectionBindings(deps, workspaceId, profileId
|
|
|
931
931
|
.sort()
|
|
932
932
|
: [];
|
|
933
933
|
const clientId = String(connection.clientId || '').trim() || null;
|
|
934
|
-
|
|
935
|
-
|
|
934
|
+
// Credential-derived bindings are authentication state owned by the cloud
|
|
935
|
+
// runtime. Synced profile metadata must never create, remove, or reassign
|
|
936
|
+
// them; doing so would let a stale replica change write attribution.
|
|
937
|
+
const syncedBindingIds = bindingIds.filter((bindingId) => !isCredentialOwnedConnectionBindingId(bindingId));
|
|
938
|
+
if (syncedBindingIds.length === 0) {
|
|
939
|
+
deps.db.prepare(`DELETE FROM ai_profile_connection_bindings
|
|
940
|
+
WHERE tenant_id = ? AND workspace_id = ? AND profile_id = ?
|
|
941
|
+
AND connection_id NOT LIKE 'oauth:%'
|
|
942
|
+
AND connection_id NOT LIKE 'oauth-client:%'
|
|
943
|
+
AND connection_id NOT LIKE 'token:%'`)
|
|
936
944
|
.run(deps.tenantId, workspaceId, profileId);
|
|
937
945
|
return;
|
|
938
946
|
}
|
|
939
|
-
const placeholders =
|
|
947
|
+
const placeholders = syncedBindingIds.map(() => '?').join(', ');
|
|
940
948
|
deps.db.prepare(`DELETE FROM ai_profile_connection_bindings
|
|
941
|
-
WHERE tenant_id = ? AND workspace_id = ? AND profile_id = ?
|
|
942
|
-
|
|
949
|
+
WHERE tenant_id = ? AND workspace_id = ? AND profile_id = ?
|
|
950
|
+
AND connection_id NOT LIKE 'oauth:%'
|
|
951
|
+
AND connection_id NOT LIKE 'oauth-client:%'
|
|
952
|
+
AND connection_id NOT LIKE 'token:%'
|
|
953
|
+
AND connection_id NOT IN (${placeholders})`)
|
|
954
|
+
.run(deps.tenantId, workspaceId, profileId, ...syncedBindingIds);
|
|
943
955
|
const now = new Date().toISOString();
|
|
944
956
|
const upsert = deps.db.prepare(`INSERT INTO ai_profile_connection_bindings
|
|
945
957
|
(tenant_id, workspace_id, connection_id, profile_id, client_id, created_at, updated_at)
|
|
@@ -948,7 +960,7 @@ function reconcileSyncedAiProfileConnectionBindings(deps, workspaceId, profileId
|
|
|
948
960
|
profile_id = excluded.profile_id,
|
|
949
961
|
client_id = excluded.client_id,
|
|
950
962
|
updated_at = excluded.updated_at`);
|
|
951
|
-
for (const bindingId of
|
|
963
|
+
for (const bindingId of syncedBindingIds) {
|
|
952
964
|
upsert.run(deps.tenantId, workspaceId, bindingId, profileId, clientId, now, now);
|
|
953
965
|
}
|
|
954
966
|
}
|
|
@@ -1282,6 +1294,11 @@ function normalizeConnectionBindingId(deps, value) {
|
|
|
1282
1294
|
}
|
|
1283
1295
|
return normalized;
|
|
1284
1296
|
}
|
|
1297
|
+
function isCredentialOwnedConnectionBindingId(connectionId) {
|
|
1298
|
+
return connectionId.startsWith('oauth:')
|
|
1299
|
+
|| connectionId.startsWith('oauth-client:')
|
|
1300
|
+
|| connectionId.startsWith('token:');
|
|
1301
|
+
}
|
|
1285
1302
|
function parseProviderMetadataRow(row) {
|
|
1286
1303
|
if (typeof row.provider_metadata_json !== 'string' || !row.provider_metadata_json.trim())
|
|
1287
1304
|
return {};
|
|
@@ -1304,10 +1321,29 @@ export function bindAiProfileConnectionService(deps, input) {
|
|
|
1304
1321
|
if (!target)
|
|
1305
1322
|
throw deps.createRuleError(`Profile ${profileId} not found`, 404, 'AI_PROFILE_NOT_FOUND');
|
|
1306
1323
|
const now = new Date().toISOString();
|
|
1307
|
-
deps.db.prepare(`INSERT INTO ai_profile_connection_bindings
|
|
1324
|
+
deps.db.prepare(`INSERT INTO ai_profile_connection_bindings
|
|
1325
|
+
(tenant_id, workspace_id, connection_id, profile_id, client_id, created_at, updated_at)
|
|
1308
1326
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1309
|
-
ON CONFLICT (tenant_id, workspace_id, connection_id) DO
|
|
1327
|
+
ON CONFLICT (tenant_id, workspace_id, connection_id) DO NOTHING`)
|
|
1310
1328
|
.run(deps.tenantId, workspaceId, connectionId, profileId, clientId, now, now);
|
|
1329
|
+
const existing = deps.db.prepare(`SELECT b.profile_id, p.archived_at
|
|
1330
|
+
FROM ai_profile_connection_bindings b
|
|
1331
|
+
LEFT JOIN ai_profiles p ON p.tenant_id = b.tenant_id AND p.workspace_id = b.workspace_id AND p.id = b.profile_id
|
|
1332
|
+
WHERE b.tenant_id = ? AND b.workspace_id = ? AND b.connection_id = ? LIMIT 1`)
|
|
1333
|
+
.get(deps.tenantId, workspaceId, connectionId);
|
|
1334
|
+
if (!existing?.profile_id) {
|
|
1335
|
+
throw deps.createRuleError('Failed to establish the MCP connection binding.', 500, 'AI_PROFILE_CONNECTION_BIND_FAILED');
|
|
1336
|
+
}
|
|
1337
|
+
if (existing && existing.profile_id !== profileId && !existing.archived_at) {
|
|
1338
|
+
throw deps.createRuleError('This MCP connection is already bound to a different active AI profile. Reconnect or use an authorized profile recovery flow.', 409, 'AI_PROFILE_CONNECTION_CONFLICT', { workspaceId, connectionId, existingProfileId: existing.profile_id || null, requestedProfileId: profileId });
|
|
1339
|
+
}
|
|
1340
|
+
const updateResult = deps.db.prepare(`UPDATE ai_profile_connection_bindings
|
|
1341
|
+
SET profile_id = ?, client_id = ?, updated_at = ?
|
|
1342
|
+
WHERE tenant_id = ? AND workspace_id = ? AND connection_id = ? AND profile_id = ?`)
|
|
1343
|
+
.run(profileId, clientId, now, deps.tenantId, workspaceId, connectionId, existing.profile_id);
|
|
1344
|
+
if (updateResult.changes !== 1) {
|
|
1345
|
+
throw deps.createRuleError('The MCP connection binding changed concurrently. Retry after refreshing the intended profile.', 409, 'AI_PROFILE_CONNECTION_CONFLICT', { workspaceId, connectionId, expectedProfileId: existing.profile_id || null, requestedProfileId: profileId });
|
|
1346
|
+
}
|
|
1311
1347
|
const rows = deps.db.prepare(`SELECT * FROM ai_profiles WHERE tenant_id = ? AND workspace_id = ?`).all(deps.tenantId, workspaceId);
|
|
1312
1348
|
for (const row of rows) {
|
|
1313
1349
|
const metadata = parseProviderMetadataRow(row);
|
|
@@ -1351,6 +1387,8 @@ export function getAiProfileByConnectionService(deps, input) {
|
|
|
1351
1387
|
const record = hydrateAiProfileRecord(deps, binding);
|
|
1352
1388
|
return input.seatScope !== undefined && (record.seatScope ?? null) !== input.seatScope ? null : record;
|
|
1353
1389
|
}
|
|
1390
|
+
if (isCredentialOwnedConnectionBindingId(connectionId))
|
|
1391
|
+
return null;
|
|
1354
1392
|
const legacyRows = deps.db.prepare(`SELECT * FROM ai_profiles WHERE tenant_id = ? AND workspace_id = ?`).all(deps.tenantId, workspaceId)
|
|
1355
1393
|
.filter((row) => {
|
|
1356
1394
|
const record = hydrateAiProfileRecord(deps, row);
|
|
@@ -1367,8 +1405,9 @@ export function getAiProfileByConnectionService(deps, input) {
|
|
|
1367
1405
|
if (legacyRows.length === 0)
|
|
1368
1406
|
return null;
|
|
1369
1407
|
const record = hydrateAiProfileRecord(deps, legacyRows[0]);
|
|
1370
|
-
if (!record.archivedAt)
|
|
1408
|
+
if (!record.archivedAt && input.repairLegacyBinding !== false) {
|
|
1371
1409
|
bindAiProfileConnectionService(deps, { workspaceId, profileId: record.id, connectionId });
|
|
1410
|
+
}
|
|
1372
1411
|
return record;
|
|
1373
1412
|
}
|
|
1374
1413
|
export function archiveAiProfileService(deps, input) {
|
|
@@ -262,11 +262,14 @@ export function syncCanonicalAttachmentLinksForTargetService(deps, targetType, t
|
|
|
262
262
|
: String(row?.kind || '').trim() === 'image'
|
|
263
263
|
? 'image'
|
|
264
264
|
: 'attachment';
|
|
265
|
-
const displayName =
|
|
265
|
+
const displayName = typeof attachment === 'string'
|
|
266
266
|
? null
|
|
267
|
-
:
|
|
268
|
-
?
|
|
269
|
-
|
|
267
|
+
: safeTargetType === 'task'
|
|
268
|
+
? String(attachment.displayName || attachment.originalFilename || attachment.caption
|
|
269
|
+
|| (isExternalStorageKey || !storageKey ? '' : path.basename(storageKey))).trim() || null
|
|
270
|
+
: String(row?.kind || '').trim() === 'document' && !isExternalStorageKey
|
|
271
|
+
? null
|
|
272
|
+
: buildAttachmentDisplayName(attachment, isExternalStorageKey || !storageKey ? null : path.basename(storageKey));
|
|
270
273
|
const timestamp = typeof attachment === 'string' ? new Date().toISOString() : (attachment.timestamp || new Date().toISOString());
|
|
271
274
|
upsertLink.run(deps.tenantId, normalizedWorkspaceId, assetId, safeTargetType, normalizedTargetId, role, displayName, index, timestamp, timestamp);
|
|
272
275
|
});
|
|
@@ -8,6 +8,7 @@ export interface DeletedTaskLifecycleServiceDeps {
|
|
|
8
8
|
generateEntityId: (prefix: string) => string;
|
|
9
9
|
normalizeWorkspaceId: (workspaceId: string | undefined) => string;
|
|
10
10
|
resolveWorkspaceActor: (workspaceId: string, actorRef: unknown) => TaskActor | null;
|
|
11
|
+
resolveWorkspaceActors: (workspaceId: string, actorRefs: unknown[]) => Map<string, TaskActor | null>;
|
|
11
12
|
sanitizeOrderInDay: (value: unknown) => number | null;
|
|
12
13
|
sanitizeDateOnly: (value: unknown) => string | null;
|
|
13
14
|
normalizeTaskEventActor: (actorRef: unknown, workspaceId: string) => {
|
|
@@ -16,6 +17,7 @@ export interface DeletedTaskLifecycleServiceDeps {
|
|
|
16
17
|
actorProfile: TaskActor | null;
|
|
17
18
|
};
|
|
18
19
|
runRetentionPurgeTransaction: <T>(workspaceId: string | null, apply: (durableDeleteOwnedKeys: ReadonlySet<string>) => T) => T;
|
|
20
|
+
listDurableDeleteOwnedTaskIds: (workspaceId: string) => ReadonlySet<string>;
|
|
19
21
|
}
|
|
20
22
|
export declare function normalizeTaskSnapshotForRestoreService(deps: DeletedTaskLifecycleServiceDeps, task: TaskItem): TaskItem;
|
|
21
23
|
export declare function normalizeDeletedTaskRestoreLifecycleService(deps: DeletedTaskLifecycleServiceDeps, task: TaskItem): Pick<TaskItem, 'status' | 'isArchived' | 'completedAt' | 'canceledReason'>;
|
|
@@ -33,4 +35,7 @@ export declare function addDeletedTaskRecordService(deps: DeletedTaskLifecycleSe
|
|
|
33
35
|
export declare function listDeletedTasksService(deps: DeletedTaskLifecycleServiceDeps, workspaceId?: string): {
|
|
34
36
|
deleted: DeletedTaskRecord[];
|
|
35
37
|
};
|
|
38
|
+
export declare function listDeletedTasksReadOnlyService(deps: DeletedTaskLifecycleServiceDeps, workspaceId?: string): {
|
|
39
|
+
deleted: DeletedTaskRecord[];
|
|
40
|
+
};
|
|
36
41
|
export declare function getDeletedTaskService(deps: DeletedTaskLifecycleServiceDeps, id: string, workspaceId?: string): DeletedTaskRecord | null;
|
|
@@ -119,6 +119,41 @@ export function mapDeletedTaskRowService(deps, row) {
|
|
|
119
119
|
actorProfile: actor === 'system' ? null : deps.resolveWorkspaceActor(workspaceId, actor)
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
+
function mapDeletedTaskRowsService(deps, rows) {
|
|
123
|
+
const actorProfiles = new Map();
|
|
124
|
+
const actorRefsByWorkspace = new Map();
|
|
125
|
+
for (const row of rows) {
|
|
126
|
+
const workspaceId = deps.normalizeWorkspaceId(typeof row.workspace_id === 'string' ? row.workspace_id : undefined);
|
|
127
|
+
const actorRef = String(row.actor || 'system').trim() || 'system';
|
|
128
|
+
if (actorRef === 'system') {
|
|
129
|
+
actorProfiles.set(`${workspaceId}\u0000${actorRef}`, null);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const workspaceActorRefs = actorRefsByWorkspace.get(workspaceId) ?? new Set();
|
|
133
|
+
workspaceActorRefs.add(actorRef);
|
|
134
|
+
actorRefsByWorkspace.set(workspaceId, workspaceActorRefs);
|
|
135
|
+
}
|
|
136
|
+
for (const [workspaceId, actorRefs] of actorRefsByWorkspace) {
|
|
137
|
+
const resolvedWorkspaceActors = deps.resolveWorkspaceActors(workspaceId, [...actorRefs]);
|
|
138
|
+
for (const actorRef of actorRefs) {
|
|
139
|
+
actorProfiles.set(`${workspaceId}\u0000${actorRef}`, resolvedWorkspaceActors.get(actorRef) ?? null);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const cachedDeps = {
|
|
143
|
+
...deps,
|
|
144
|
+
resolveWorkspaceActor: (workspaceId, actorRef) => {
|
|
145
|
+
const cacheKey = `${workspaceId}\u0000${String(actorRef || '').trim()}`;
|
|
146
|
+
if (actorProfiles.has(cacheKey))
|
|
147
|
+
return actorProfiles.get(cacheKey) ?? null;
|
|
148
|
+
const actorProfile = deps.resolveWorkspaceActor(workspaceId, actorRef);
|
|
149
|
+
actorProfiles.set(cacheKey, actorProfile);
|
|
150
|
+
return actorProfile;
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
return rows
|
|
154
|
+
.map((row) => mapDeletedTaskRowService(cachedDeps, row))
|
|
155
|
+
.filter((row) => Boolean(row));
|
|
156
|
+
}
|
|
122
157
|
function deletedTaskListSnapshotSelect() {
|
|
123
158
|
const snapshotPairs = DELETED_TASK_LIST_SNAPSHOT_FIELDS
|
|
124
159
|
.map((field) => `'${field}', snapshot -> '${field}'`)
|
|
@@ -220,9 +255,26 @@ export function listDeletedTasksService(deps, workspaceId) {
|
|
|
220
255
|
`;
|
|
221
256
|
const rows = deps.db.prepare(listSql).all(deps.tenantId, normalizedWorkspaceId);
|
|
222
257
|
return {
|
|
223
|
-
deleted: rows
|
|
224
|
-
|
|
225
|
-
|
|
258
|
+
deleted: mapDeletedTaskRowsService(deps, rows)
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
export function listDeletedTasksReadOnlyService(deps, workspaceId) {
|
|
262
|
+
const normalizedWorkspaceId = deps.normalizeWorkspaceId(workspaceId);
|
|
263
|
+
const cutoff = new Date(Date.now() - (DELETED_TASK_RETENTION_DAYS * 24 * 60 * 60 * 1000)).toISOString();
|
|
264
|
+
const durableDeleteOwnedTaskIds = deps.listDurableDeleteOwnedTaskIds(normalizedWorkspaceId);
|
|
265
|
+
const listSql = deps.db.provider === 'postgres'
|
|
266
|
+
? deletedTaskListSnapshotSelect()
|
|
267
|
+
: `
|
|
268
|
+
SELECT *
|
|
269
|
+
FROM deleted_tasks
|
|
270
|
+
WHERE tenant_id = ?
|
|
271
|
+
AND workspace_id = ?
|
|
272
|
+
ORDER BY deleted_at DESC, id DESC
|
|
273
|
+
`;
|
|
274
|
+
const rows = deps.db.prepare(listSql).all(deps.tenantId, normalizedWorkspaceId);
|
|
275
|
+
return {
|
|
276
|
+
deleted: mapDeletedTaskRowsService(deps, rows)
|
|
277
|
+
.filter((row) => row.deletedAt >= cutoff || durableDeleteOwnedTaskIds.has(row.taskId))
|
|
226
278
|
};
|
|
227
279
|
}
|
|
228
280
|
export function getDeletedTaskService(deps, id, workspaceId) {
|
|
@@ -124,6 +124,10 @@ export declare class McpTokenService {
|
|
|
124
124
|
profileToken: string;
|
|
125
125
|
profileName: string;
|
|
126
126
|
}): boolean;
|
|
127
|
+
listMcpOAuthRestorableAiProfileIds(input: {
|
|
128
|
+
grantId: string;
|
|
129
|
+
workspaceId: string;
|
|
130
|
+
}): string[];
|
|
127
131
|
recordMcpOAuthConnectorGrantUsage(input: {
|
|
128
132
|
grantId: string;
|
|
129
133
|
workspaceId: string;
|
|
@@ -973,6 +973,58 @@ export class McpTokenService {
|
|
|
973
973
|
`).run(profileId, profileToken, profileName, now, this.tenantId, workspaceId, grantId);
|
|
974
974
|
return Number(result?.changes || 0) > 0;
|
|
975
975
|
}
|
|
976
|
+
listMcpOAuthRestorableAiProfileIds(input) {
|
|
977
|
+
const grantId = String(input.grantId || '').trim();
|
|
978
|
+
const workspaceId = String(input.workspaceId || '').trim();
|
|
979
|
+
if (!grantId || !workspaceId)
|
|
980
|
+
return [];
|
|
981
|
+
const current = this.db.prepare(`
|
|
982
|
+
SELECT current.provider, current.user_id, current_client.client_name,
|
|
983
|
+
current_client.redirect_uris_json, current_client.product_client_id
|
|
984
|
+
FROM mcp_oauth_connector_grants current
|
|
985
|
+
JOIN mcp_oauth_clients current_client
|
|
986
|
+
ON current_client.tenant_id = current.tenant_id
|
|
987
|
+
AND current_client.id = current.client_id
|
|
988
|
+
WHERE current.tenant_id = ?
|
|
989
|
+
AND current.workspace_id = ?
|
|
990
|
+
AND current.id = ?
|
|
991
|
+
AND current.status = 'active'
|
|
992
|
+
LIMIT 1
|
|
993
|
+
`).get(this.tenantId, workspaceId, grantId);
|
|
994
|
+
if (!current?.provider || !current.user_id)
|
|
995
|
+
return [];
|
|
996
|
+
const canonicalRedirectUris = (value) => {
|
|
997
|
+
try {
|
|
998
|
+
const parsed = JSON.parse(String(value || '[]'));
|
|
999
|
+
if (!Array.isArray(parsed))
|
|
1000
|
+
return null;
|
|
1001
|
+
return JSON.stringify([...new Set(parsed.map((uri) => String(uri || '').trim()).filter(Boolean))].sort());
|
|
1002
|
+
}
|
|
1003
|
+
catch {
|
|
1004
|
+
return null;
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
const currentRedirectUris = canonicalRedirectUris(current.redirect_uris_json);
|
|
1008
|
+
if (!currentRedirectUris)
|
|
1009
|
+
return [];
|
|
1010
|
+
const previousRows = this.db.prepare(`
|
|
1011
|
+
SELECT previous.ai_profile_id, previous_client.client_name,
|
|
1012
|
+
previous_client.redirect_uris_json, previous_client.product_client_id
|
|
1013
|
+
FROM mcp_oauth_connector_grants previous
|
|
1014
|
+
JOIN mcp_oauth_clients previous_client
|
|
1015
|
+
ON previous_client.tenant_id = previous.tenant_id
|
|
1016
|
+
AND previous_client.id = previous.client_id
|
|
1017
|
+
WHERE previous.tenant_id = ?
|
|
1018
|
+
AND previous.workspace_id = ?
|
|
1019
|
+
AND previous.user_id = ?
|
|
1020
|
+
AND previous.provider = ?
|
|
1021
|
+
AND previous.id <> ?
|
|
1022
|
+
AND previous.ai_profile_id IS NOT NULL
|
|
1023
|
+
`).all(this.tenantId, workspaceId, String(current.user_id), String(current.provider), grantId);
|
|
1024
|
+
return [...new Set(previousRows.filter((row) => (String(row.client_name || '') === String(current.client_name || '')
|
|
1025
|
+
&& String(row.product_client_id || '') === String(current.product_client_id || '')
|
|
1026
|
+
&& canonicalRedirectUris(row.redirect_uris_json) === currentRedirectUris)).map((row) => String(row.ai_profile_id || '').trim()).filter(Boolean))].sort();
|
|
1027
|
+
}
|
|
976
1028
|
recordMcpOAuthConnectorGrantUsage(input) {
|
|
977
1029
|
const grantId = String(input.grantId || '').trim();
|
|
978
1030
|
const workspaceId = String(input.workspaceId || '').trim();
|