@taskforcehq/taskforce 0.3.328 → 0.3.330
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 +523 -79
- 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/features/AiProfilesModule.js +3 -5
- package/dist/components/features/AnnotatedAttachmentWorkspace.js +24 -8
- package/dist/components/features/TaskSettings.d.ts +12 -1
- package/dist/components/features/TaskSettings.js +217 -54
- 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 +1 -1
- 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 +2 -5
- package/dist/components/features/planning/PlanningTaskOrderList.d.ts +1 -1
- package/dist/components/features/planning/PlanningTaskOrderList.js +17 -6
- package/dist/components/features/planning/planningTaskArrangement.d.ts +1 -0
- package/dist/components/features/planning/planningTaskArrangement.js +5 -5
- package/dist/components/features/taskforceAgentAssignments/AgentBehaviorAssignments.js +15 -24
- package/dist/components/features/taskforceAgentEditorModel.d.ts +2 -1
- package/dist/components/features/taskforceAgentEditorModel.js +5 -1
- package/dist/components/features/workflowManager/workflowManagerApi.d.ts +1 -1
- package/dist/components/features/workflowManager/workflowManagerApi.js +3 -0
- package/dist/components/task/TaskCard.d.ts +5 -0
- package/dist/components/task/TaskCard.js +65 -10
- package/dist/components/task/TaskImageReviews.js +14 -5
- 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/TaskWorkflowAssignmentField.js +68 -10
- package/dist/components/ui/PasswordInput.d.ts +6 -0
- package/dist/components/ui/PasswordInput.js +11 -0
- 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 +32 -55
- package/dist/components/views/WidgetView.js +3 -3
- package/dist/components/views/panels/PlanningDrawer.js +5 -12
- package/dist/components/views/standalone/modals/AccountSettingsModal.js +26 -17
- 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 +60 -1
- package/dist/config/localServiceRuntimeContract.d.ts +13 -0
- package/dist/config/localServiceRuntimeContract.js +135 -0
- package/dist/core/AiProfileService.js +46 -8
- package/dist/core/DeletedTaskLifecycleService.d.ts +1 -0
- package/dist/core/DeletedTaskLifecycleService.js +37 -6
- package/dist/core/McpTokenService.d.ts +23 -1
- package/dist/core/McpTokenService.js +93 -10
- package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +3 -2
- package/dist/core/TaskAttachmentLinksDurableMutationService.js +33 -9
- 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 +94 -2
- package/dist/core/Taskforce.js +760 -36
- package/dist/core/types.d.ts +14 -0
- 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/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/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/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 +6 -0
- package/dist/hooks/tasks/useTaskFormActions.js +65 -12
- 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/useSyncOrchestrator.d.ts +3 -0
- package/dist/hooks/useSyncOrchestrator.js +111 -2
- package/dist/hooks/useTaskData.d.ts +40 -1
- package/dist/hooks/useTaskData.js +240 -9
- package/dist/hooks/useTaskMutations.d.ts +12 -2
- package/dist/hooks/useTaskMutations.js +266 -52
- package/dist/hooks/useTaskforce.d.ts +39 -9
- package/dist/hooks/useTaskforce.js +359 -40
- 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/clientRegistry.d.ts +2 -2
- package/dist/mcp/clientRegistry.js +1 -1
- package/dist/mcp/documentAssetRegistrar.js +53 -41
- package/dist/mcp/documentHelpers.d.ts +1 -0
- package/dist/mcp/documentHelpers.js +7 -3
- 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 +10 -0
- package/dist/mcp/httpProtocolRouting.js +19 -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 +556 -139
- package/dist/mcp/taskPlanningRegistrar.js +64 -42
- package/dist/mcp/toolCallCompatibility.js +38 -1
- package/dist/mcp/toolCatalog.d.ts +1 -0
- package/dist/mcp/toolProfiles.d.ts +2 -0
- package/dist/mcp/toolProfiles.js +10 -0
- package/dist/mcp/toolRegistry.d.ts +1 -0
- package/dist/mcp/toolRegistry.js +1 -0
- package/dist/mcp/workflowExecutionRegistrar.js +4 -4
- package/dist/migrations/taskSchemaMigrations.d.ts +5 -0
- package/dist/migrations/taskSchemaMigrations.js +427 -0
- package/dist/runtime/appGateDefinitions.d.ts +30 -6
- package/dist/runtime/appGateDefinitions.js +6 -6
- package/dist/runtime/appGates.d.ts +13 -13
- package/dist/runtime/appGates.js +36 -13
- package/dist/runtime/nodeAppGates.d.ts +8 -0
- package/dist/runtime/nodeAppGates.js +27 -0
- package/dist/server/cloudCollectionRead.d.ts +1 -1
- package/dist/server/cloudCollectionRead.js +2 -2
- package/dist/server/index.d.ts +6 -0
- package/dist/server/index.js +123 -14
- package/dist/server/localDatabaseIdentity.js +4 -1
- package/dist/server/localServiceLease.d.ts +3 -0
- package/dist/server/localServiceLease.js +11 -2
- package/dist/server/localServiceSessions.d.ts +14 -17
- package/dist/server/localServiceSessions.js +308 -94
- package/dist/server/localWorkspaceSyncServerRuntime.js +12 -7
- 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.d.ts +4 -0
- package/dist/server/routes/admin.js +113 -3
- package/dist/server/routes/agentRoles.js +3 -2
- package/dist/server/routes/agentSkills.js +3 -2
- package/dist/server/routes/agents.d.ts +7 -0
- package/dist/server/routes/agents.js +394 -39
- package/dist/server/routes/annotatedAttachments.js +3 -2
- package/dist/server/routes/auth.js +3 -1
- package/dist/server/routes/billing.js +5 -1
- package/dist/server/routes/durableTaskHttpRouters.d.ts +11 -2
- 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 +138 -40
- package/dist/server/routes/modelConnections.d.ts +24 -0
- package/dist/server/routes/modelConnections.js +205 -0
- package/dist/server/routes/primitives.d.ts +2 -0
- package/dist/server/routes/shared.d.ts +19 -0
- package/dist/server/routes/shared.js +95 -19
- package/dist/server/routes/sync.js +1 -0
- package/dist/server/routes/syncAuxRoutes.js +9 -2
- package/dist/server/routes/syncPullApplyRoutes.js +23 -4
- package/dist/server/routes/syncPushRoutes.d.ts +1 -0
- package/dist/server/routes/syncPushRoutes.js +1 -0
- package/dist/server/routes/syncV3FeedRoutes.js +27 -16
- package/dist/server/routes/syncV3LocalCaptureRoutes.js +28 -2
- package/dist/server/routes/syncV3LocalFeedApplyRoutes.d.ts +1 -0
- package/dist/server/routes/syncV3LocalFeedApplyRoutes.js +26 -0
- package/dist/server/routes/syncV3OperationRoutes.js +45 -9
- package/dist/server/routes/tasks.js +124 -13
- package/dist/server/routes/workflows.js +9 -1
- package/dist/server/routes/workspaces.js +43 -1
- package/dist/server/routes.js +59 -0
- package/dist/server/workspaceDeletionAdmission.d.ts +23 -0
- package/dist/server/workspaceDeletionAdmission.js +50 -0
- package/dist/service/localServiceCli.js +16 -0
- package/dist/service/localServiceClientLifecycle.js +36 -10
- package/dist/services/codexAppServerModelGateway.d.ts +161 -0
- package/dist/services/codexAppServerModelGateway.js +663 -0
- 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 +126 -0
- package/dist/services/executorPhaseAIssuerCore.js +295 -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 +8 -2
- package/dist/services/taskforceAgentMcpBridge.js +30 -27
- 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 -3
- package/dist/shared/taskforceAgentModels.d.ts +1 -0
- package/dist/shared/taskforceAgentModels.js +36 -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 +24 -3
- package/dist/storage/postgresAdapter.js +117 -14
- package/dist/storage/postgresWorker.js +22 -5
- package/dist/storage/postgresWorkerProtocol.d.ts +30 -0
- package/dist/storage/postgresWorkerProtocol.js +30 -0
- package/dist/storage/taskforceAgentConnectionBindingStore.d.ts +31 -0
- package/dist/storage/taskforceAgentConnectionBindingStore.js +84 -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 +1 -0
- package/dist/sync/coordinator/localSyncExecutionPermitStore.js +67 -30
- package/dist/sync/coordinator/localWorkspaceSyncPendingV2PushProgress.d.ts +10 -0
- package/dist/sync/coordinator/localWorkspaceSyncPendingV2PushProgress.js +29 -0
- package/dist/sync/coordinator/localWorkspaceSyncRunner.js +6 -2
- 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/workspaceSyncEngineBrowserGatewayRemoteServices.js +20 -2
- package/dist/sync/engine/workspaceSyncEngineLifecyclePolicy.js +6 -6
- package/dist/sync/engine/workspaceSyncEngineLocalServices.d.ts +4 -0
- package/dist/sync/engine/workspaceSyncEngineLocalServices.js +11 -0
- 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/workspaceSyncEngineRemoteServices.d.ts +11 -0
- package/dist/sync/engine/workspaceSyncEngineRemoteServices.js +2 -1
- package/dist/sync/engine/workspaceSyncEngineRuntime.d.ts +1 -0
- package/dist/sync/engine/workspaceSyncEngineRuntime.js +1 -0
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +31 -4
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.d.ts +3 -1
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +134 -43
- package/dist/sync/engine/workspaceSyncEngineServerSnapshotReader.js +6 -0
- package/dist/sync/engine/workspaceSyncEngineTransfers.d.ts +1 -1
- package/dist/sync/engine/workspaceSyncEngineTransfers.js +11 -4
- package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.d.ts +1 -0
- package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +29 -0
- package/dist/sync/syncApplyHandlers.d.ts +2 -0
- package/dist/sync/syncApplyHandlers.js +10 -1
- package/dist/sync/syncService.d.ts +17 -0
- package/dist/sync/syncService.js +87 -19
- package/dist/sync/taskforceAgentSyncAuthorization.d.ts +16 -0
- package/dist/sync/taskforceAgentSyncAuthorization.js +20 -0
- package/dist/sync/v3/combinedFeedCapability.js +8 -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.d.ts +2 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +14 -1
- package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +4 -1
- package/dist/sync/v3/durableTaskChecklistMutationRouter.js +62 -15
- package/dist/sync/v3/durableTaskCommentMutationRouter.js +3 -0
- package/dist/sync/v3/durableTaskHttpBulkMutationRouter.d.ts +1 -0
- package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +30 -8
- package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +5 -3
- package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +37 -2
- package/dist/sync/v3/durableTaxonomyMutationRouter.js +5 -0
- package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.d.ts +11 -0
- package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.js +39 -10
- 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/localTaskAttachmentLinksOutboxService.d.ts +2 -0
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +47 -7
- 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.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 +10 -0
- package/dist/sync/v3/localTaskStatusOutboxService.js +272 -24
- package/dist/sync/v3/syncDurabilityStore.d.ts +41 -0
- package/dist/sync/v3/syncDurabilityStore.js +199 -13
- package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +6 -4
- package/dist/sync/v3/taskAttachmentLinksMutationService.js +19 -10
- package/dist/sync/v3/taskChecklistMutationService.d.ts +15 -2
- package/dist/sync/v3/taskChecklistMutationService.js +48 -1
- package/dist/sync/v3/taskCreateMutationService.d.ts +2 -2
- package/dist/sync/v3/taskCreateMutationService.js +7 -4
- 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 +31 -1
- package/dist/sync/v3/taskStatusMutationService.js +123 -3
- package/dist/sync/v3/workflowAssignmentSync.d.ts +6 -0
- package/dist/sync/v3/workflowAssignmentSync.js +30 -0
- 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/workspaceSyncV2ProjectionExclusions.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +13 -3
- 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.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +18 -3
- package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +8 -3
- package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +51 -10
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +14 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +49 -25
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +13 -4
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +124 -57
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +20 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +106 -30
- package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +4 -1
- package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +34 -0
- package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +12 -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 +5 -0
- package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +7 -0
- package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +55 -9
- package/dist/sync/v3/workspaceSyncV3RepairSnapshotRetentionService.d.ts +17 -0
- package/dist/sync/v3/workspaceSyncV3RepairSnapshotRetentionService.js +64 -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/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/workspacePullFeed.js +2 -1
- package/dist/sync/workspaceRepair.d.ts +8 -2
- package/dist/sync/workspaceRepair.js +222 -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/types.d.ts +47 -1
- package/dist/types.js +6 -0
- package/dist/ui/assets/AiIdentityRosterCard-OcSz1PkK.js +1 -0
- package/dist/ui/assets/AiProfilesModule-ByhYW5Xi.js +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BTM6nIg_.js +3 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-TzYu7pkG.css +1 -0
- package/dist/ui/assets/{AssetTraySortControl-Kf1pelSy.js → AssetTraySortControl-DabEG8L4.js} +1 -1
- package/dist/ui/assets/{ContextAttachmentManager-B8boOs6Q.js → ContextAttachmentManager-Coa8yVGM.js} +2 -2
- package/dist/ui/assets/{DocumentWorkspace-DXH4XUsB.js → DocumentWorkspace-BB0qhdix.js} +1 -1
- package/dist/ui/assets/{EntityActivityTimeline-BtSTq0ON.js → EntityActivityTimeline-CZ8x4UJE.js} +1 -1
- package/dist/ui/assets/PlanningModule-pRPjR7v5.js +1 -0
- package/dist/ui/assets/PlansPage-D86pZSSl.js +1 -0
- package/dist/ui/assets/TaskContextUpload-BVIkU9yT.js +1 -0
- package/dist/ui/assets/TaskSettings-Cv1mNhv_.js +12 -0
- package/dist/ui/assets/TaskforceAgentsModule-b-8R25cJ.css +1 -0
- package/dist/ui/assets/TaskforceAgentsModule-gmH-PD2q.js +24 -0
- package/dist/ui/assets/{WorkflowManagerModule-DWFspC-o.js → WorkflowManagerModule-DbfnHJ-f.js} +1 -1
- package/dist/ui/assets/index-BZ-hdZSk.css +1 -0
- package/dist/ui/assets/index-CTsDBaef.js +7 -0
- package/dist/ui/assets/{vendor-icons-BkFLXavV.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/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/taskEvents.d.ts +2 -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/dist/utils/workspaceSyncPresentation.d.ts +1 -1
- package/dist/utils/workspaceSyncPresentation.js +23 -17
- package/package.json +36 -6
- package/dist/ui/assets/AiIdentityRosterCard-MZs7lVED.js +0 -1
- package/dist/ui/assets/AiProfilesModule-C3i0LOxe.js +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-CBRZ8rca.css +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-DVZk5Tln.js +0 -3
- package/dist/ui/assets/PlanningModule-BPVyEa-f.js +0 -1
- package/dist/ui/assets/PlansPage-ChqgxALc.js +0 -1
- package/dist/ui/assets/TaskContextUpload-CB3ol0sN.js +0 -1
- package/dist/ui/assets/TaskSettings-g4ECKNOz.js +0 -12
- package/dist/ui/assets/TaskforceAgentsModule-BazdUwxZ.js +0 -21
- package/dist/ui/assets/TaskforceAgentsModule-D6IC0S7Q.css +0 -1
- package/dist/ui/assets/index-BUplSsv_.js +0 -7
- package/dist/ui/assets/index-gz2EXuoK.css +0 -1
package/dist/core/Taskforce.js
CHANGED
|
@@ -27,6 +27,7 @@ import { listTaskAttachmentLinksForDurableSync as listTaskAttachmentLinksForDura
|
|
|
27
27
|
import { EntityEventIdentityCollisionError, addEntityEventService, addTaskEventService, buildEntityActivityService, buildTaskActivityService, getEntityEventForSyncService, getTaskEventForSyncService, listEntityEventsInternalService, listWorkspaceEntityEventsForSyncService, listTaskEventsInternalService, listWorkspaceTaskEventsForSyncService, replaceEntityEventsService, replaceTaskCommentsService, replaceTaskEventsService, upsertEntityEventForSyncService, deleteEntityEventForSyncService, upsertTaskEventForSyncService, } from './TaskActivityService.js';
|
|
28
28
|
import { addDeletedTaskRecordService, getDeletedTaskService, listDeletedTasksService, listDeletedTasksReadOnlyService, mapDeletedTaskRowService, normalizeTaskSnapshotForRestoreService, normalizeDeletedTaskRestoreLifecycleService, purgeExpiredDeletedTasksService, } from './DeletedTaskLifecycleService.js';
|
|
29
29
|
import { DELETED_TASK_RETENTION_DAYS } from '../shared/deletedTaskRetention.js';
|
|
30
|
+
import { INTERNAL_SUBSCRIPTION_MODEL_FEATURE_KEY, isSupportedInternalUserFeatureKey, } from '../shared/userFeatureGrants.js';
|
|
30
31
|
import { applyWorkspaceSyncSnapshotService, claimPushIdempotencyService, completePushIdempotencyClaimService, deleteDocumentWatermarkService, deleteDocumentWatermarkStrictService, getDocumentWatermarkService, listTasksForSyncService, listWorkspaceSyncDeletesSinceService, readPushIdempotencyService, releasePushIdempotencyClaimService, renewPushIdempotencyClaimService, recordSyncEventDurablyService, recordSyncEventService, upsertDocumentWatermarkService, upsertDocumentWatermarkStrictService, writePushIdempotencyService, } from './SyncReconciliationService.js';
|
|
31
32
|
import { getGlobalSettingsService, hasPersistedGlobalSettingsService, readGlobalConfigService, updateGlobalSettingsService, writeGlobalConfigService, } from './GlobalSettingsService.js';
|
|
32
33
|
import { getWorkspaceTaxonomyStateService, upsertWorkspaceTaxonomyStateService, } from './TaxonomySettingsService.js';
|
|
@@ -61,7 +62,8 @@ import { getImageReferenceLabel } from '../utils/imageReferences.js';
|
|
|
61
62
|
import { getCanonicalDocumentName } from '../utils/documentNames.js';
|
|
62
63
|
import { isPathInsideRoot, resolveStorageKeyPathInsideRoot } from '../utils/pathSafety.js';
|
|
63
64
|
import { DEFAULT_TASKFORCE_THEME } from '../utils/theme.js';
|
|
64
|
-
import {
|
|
65
|
+
import { normalizeTaskSearchQuery } from '../utils/taskSearch.js';
|
|
66
|
+
import { ensureAssigneeTaskSchema, ensureAnnotatedAttachmentSessionsSchema, ensureAuthIdentitySchema, backfillPlanningAttachmentAssetIdentities, ensureBillingSchema, ensureDocumentReviewSessionsSchema, ensureDocumentReferenceSchema, ensureEntitlementsSchema, ensurePlanningReferenceSchema, ensureScheduleTaskSchema, ensureWorkstreamTaskOrderSchema, ensureTaskAssetsSchema, ensureTaskApproachRemoved, ensureAgentRolesSchema, ensureGitHubReviewEvidenceInstallationBindingsSchema, ensureExecutorPhaseAApprovalReceiptsSchema, ensureExecutorPhaseARunAuthorizationsSchema, ensureModelProviderConnectionsSchema, ensureTaskforceAgentConnectionBindingsSchema, ensureTaskforceAgentsSchema, ensureTaskforceAgentSkillsSchema, ensureTaskReferenceSchema, ensureTenantSchema, ensureWorkflowSchema, ensureWorkspaceAssetsSchema, ensureWorkspaceSchema, ensureLocalSyncCoordinatorSchema, ensureWorkspaceSyncDurabilitySchema } from '../migrations/taskSchemaMigrations.js';
|
|
65
67
|
import { repairTaskUpdatedAtOnlySyncDrift } from '../migrations/taskUpdatedAtSyncDriftMigration.js';
|
|
66
68
|
export class TaskforceRuleError extends Error {
|
|
67
69
|
statusCode;
|
|
@@ -1456,7 +1458,14 @@ export class TaskforceCore {
|
|
|
1456
1458
|
ensureDocumentReferenceSchema(this.db);
|
|
1457
1459
|
ensureDocumentReviewSessionsSchema(this.db);
|
|
1458
1460
|
ensureAnnotatedAttachmentSessionsSchema(this.db);
|
|
1461
|
+
ensureModelProviderConnectionsSchema(this.db);
|
|
1459
1462
|
ensureTaskforceAgentsSchema(this.db);
|
|
1463
|
+
ensureTaskforceAgentConnectionBindingsSchema(this.db);
|
|
1464
|
+
ensureGitHubReviewEvidenceInstallationBindingsSchema(this.db);
|
|
1465
|
+
if (this.runtimeMode === 'cloud') {
|
|
1466
|
+
ensureExecutorPhaseAApprovalReceiptsSchema(this.db);
|
|
1467
|
+
ensureExecutorPhaseARunAuthorizationsSchema(this.db);
|
|
1468
|
+
}
|
|
1460
1469
|
ensureAgentRolesSchema(this.db);
|
|
1461
1470
|
ensureTaskforceAgentSkillsSchema(this.db);
|
|
1462
1471
|
ensureWorkflowSchema(this.db);
|
|
@@ -1968,6 +1977,18 @@ export class TaskforceCore {
|
|
|
1968
1977
|
PRIMARY KEY (tenant_id, user_id, prefs_key)
|
|
1969
1978
|
);
|
|
1970
1979
|
|
|
1980
|
+
CREATE TABLE IF NOT EXISTS user_feature_grants (
|
|
1981
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
1982
|
+
user_id TEXT NOT NULL,
|
|
1983
|
+
feature_key TEXT NOT NULL,
|
|
1984
|
+
enabled INTEGER NOT NULL DEFAULT 0,
|
|
1985
|
+
created_at TEXT NOT NULL,
|
|
1986
|
+
updated_at TEXT NOT NULL,
|
|
1987
|
+
updated_by TEXT,
|
|
1988
|
+
PRIMARY KEY (tenant_id, user_id, feature_key),
|
|
1989
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
1990
|
+
);
|
|
1991
|
+
|
|
1971
1992
|
CREATE TABLE IF NOT EXISTS admin_audit_logs (
|
|
1972
1993
|
id TEXT PRIMARY KEY,
|
|
1973
1994
|
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
@@ -2046,6 +2067,7 @@ export class TaskforceCore {
|
|
|
2046
2067
|
user_id TEXT NOT NULL,
|
|
2047
2068
|
workspace_id TEXT NOT NULL DEFAULT 'default',
|
|
2048
2069
|
scopes_json TEXT NOT NULL,
|
|
2070
|
+
tool_profile TEXT NOT NULL DEFAULT 'default',
|
|
2049
2071
|
token_prefix TEXT NOT NULL,
|
|
2050
2072
|
token_hash TEXT NOT NULL,
|
|
2051
2073
|
status TEXT NOT NULL DEFAULT 'active',
|
|
@@ -2187,6 +2209,7 @@ export class TaskforceCore {
|
|
|
2187
2209
|
CREATE INDEX IF NOT EXISTS idx_ui_state_workspace_key ON ui_state(workspace_id, state_key);
|
|
2188
2210
|
CREATE INDEX IF NOT EXISTS idx_workspace_settings_workspace_key ON workspace_settings(workspace_id, settings_key);
|
|
2189
2211
|
CREATE INDEX IF NOT EXISTS idx_user_preferences_user_key ON user_preferences(user_id, prefs_key);
|
|
2212
|
+
CREATE INDEX IF NOT EXISTS idx_user_feature_grants_feature_enabled ON user_feature_grants(tenant_id, feature_key, enabled);
|
|
2190
2213
|
CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_tenant_created ON admin_audit_logs(tenant_id, created_at);
|
|
2191
2214
|
CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_workspace_created ON admin_audit_logs(workspace_id, created_at);
|
|
2192
2215
|
CREATE INDEX IF NOT EXISTS idx_task_events_task_created ON task_events(tenant_id, workspace_id, task_id, created_at);
|
|
@@ -2375,6 +2398,9 @@ export class TaskforceCore {
|
|
|
2375
2398
|
if (!mcpOauthGrantColumns.some((c) => c.name === 'ai_profile_name')) {
|
|
2376
2399
|
this.db.exec(`ALTER TABLE mcp_oauth_connector_grants ADD COLUMN ai_profile_name TEXT`);
|
|
2377
2400
|
}
|
|
2401
|
+
if (!mcpOauthGrantColumns.some((c) => c.name === 'tool_profile')) {
|
|
2402
|
+
this.db.exec(`ALTER TABLE mcp_oauth_connector_grants ADD COLUMN tool_profile TEXT NOT NULL DEFAULT 'default'`);
|
|
2403
|
+
}
|
|
2378
2404
|
}
|
|
2379
2405
|
else {
|
|
2380
2406
|
try {
|
|
@@ -2383,6 +2409,7 @@ export class TaskforceCore {
|
|
|
2383
2409
|
this.db.exec(`ALTER TABLE mcp_oauth_connector_grants ADD COLUMN IF NOT EXISTS ai_profile_id TEXT`);
|
|
2384
2410
|
this.db.exec(`ALTER TABLE mcp_oauth_connector_grants ADD COLUMN IF NOT EXISTS ai_profile_token TEXT`);
|
|
2385
2411
|
this.db.exec(`ALTER TABLE mcp_oauth_connector_grants ADD COLUMN IF NOT EXISTS ai_profile_name TEXT`);
|
|
2412
|
+
this.db.exec(`ALTER TABLE mcp_oauth_connector_grants ADD COLUMN IF NOT EXISTS tool_profile TEXT NOT NULL DEFAULT 'default'`);
|
|
2386
2413
|
}
|
|
2387
2414
|
catch {
|
|
2388
2415
|
// Best-effort schema repair for existing deployments.
|
|
@@ -3158,7 +3185,17 @@ export class TaskforceCore {
|
|
|
3158
3185
|
return ['manualComplexityEnabled'];
|
|
3159
3186
|
}
|
|
3160
3187
|
getUserBackedProjectConfigKeys() {
|
|
3161
|
-
return [
|
|
3188
|
+
return [
|
|
3189
|
+
'theme',
|
|
3190
|
+
'jsonBackupEnabled',
|
|
3191
|
+
'checklistDropdownEnabled',
|
|
3192
|
+
'showTaskCardStatusLabel',
|
|
3193
|
+
'showTaskCardDescription',
|
|
3194
|
+
'showTaskCardAttachments',
|
|
3195
|
+
'showTaskCardChecklistProgress',
|
|
3196
|
+
'showTaskCardLatestActivity',
|
|
3197
|
+
'tintTaskCardActivityBackground',
|
|
3198
|
+
];
|
|
3162
3199
|
}
|
|
3163
3200
|
isWorkspaceBackedProjectConfigKey(key) {
|
|
3164
3201
|
return this.getWorkspaceBackedProjectConfigKeys().includes(key);
|
|
@@ -3201,6 +3238,21 @@ export class TaskforceCore {
|
|
|
3201
3238
|
if (typeof source.showTaskCardStatusLabel === 'boolean') {
|
|
3202
3239
|
next.showTaskCardStatusLabel = source.showTaskCardStatusLabel;
|
|
3203
3240
|
}
|
|
3241
|
+
if (typeof source.showTaskCardDescription === 'boolean') {
|
|
3242
|
+
next.showTaskCardDescription = source.showTaskCardDescription;
|
|
3243
|
+
}
|
|
3244
|
+
if (typeof source.showTaskCardAttachments === 'boolean') {
|
|
3245
|
+
next.showTaskCardAttachments = source.showTaskCardAttachments;
|
|
3246
|
+
}
|
|
3247
|
+
if (typeof source.showTaskCardChecklistProgress === 'boolean') {
|
|
3248
|
+
next.showTaskCardChecklistProgress = source.showTaskCardChecklistProgress;
|
|
3249
|
+
}
|
|
3250
|
+
if (typeof source.showTaskCardLatestActivity === 'boolean') {
|
|
3251
|
+
next.showTaskCardLatestActivity = source.showTaskCardLatestActivity;
|
|
3252
|
+
}
|
|
3253
|
+
if (typeof source.tintTaskCardActivityBackground === 'boolean') {
|
|
3254
|
+
next.tintTaskCardActivityBackground = source.tintTaskCardActivityBackground;
|
|
3255
|
+
}
|
|
3204
3256
|
return next;
|
|
3205
3257
|
}
|
|
3206
3258
|
stripPersistedProjectConfigKeys(config) {
|
|
@@ -4019,7 +4071,7 @@ export class TaskforceCore {
|
|
|
4019
4071
|
AND identity.entity_type = 'initiative'
|
|
4020
4072
|
AND identity.entity_id = i.id
|
|
4021
4073
|
WHERE i.tenant_id = ? AND i.workspace_id = ?
|
|
4022
|
-
ORDER BY COALESCE(i.sort_order, 2147483647), i.created_at ASC
|
|
4074
|
+
ORDER BY COALESCE(i.sort_order, 2147483647), i.created_at ASC, i.id ASC
|
|
4023
4075
|
`).all(this.tenantId, normalizedWorkspaceId);
|
|
4024
4076
|
return rows.map((row) => this.rowToInitiative(row));
|
|
4025
4077
|
}
|
|
@@ -4326,7 +4378,7 @@ export class TaskforceCore {
|
|
|
4326
4378
|
AND identity.entity_type = 'workstream'
|
|
4327
4379
|
AND identity.entity_id = w.id
|
|
4328
4380
|
WHERE w.tenant_id = ? AND w.workspace_id = ?
|
|
4329
|
-
ORDER BY COALESCE(w.sort_order, 2147483647), w.created_at ASC
|
|
4381
|
+
ORDER BY COALESCE(w.sort_order, 2147483647), w.created_at ASC, w.id ASC
|
|
4330
4382
|
`).all(this.tenantId, normalizedWorkspaceId);
|
|
4331
4383
|
return rows.map((row) => this.rowToWorkstream(row));
|
|
4332
4384
|
}
|
|
@@ -5493,6 +5545,7 @@ export class TaskforceCore {
|
|
|
5493
5545
|
generateEntityId: (prefix) => this.generateEntityId(prefix),
|
|
5494
5546
|
normalizeWorkspaceId: (workspaceId) => this.normalizeWorkspaceId(workspaceId),
|
|
5495
5547
|
resolveWorkspaceActor: (workspaceId, actorRef) => this.resolveWorkspaceActor(workspaceId, actorRef),
|
|
5548
|
+
resolveWorkspaceActors: (workspaceId, actorRefs) => this.resolveWorkspaceActors(workspaceId, actorRefs),
|
|
5496
5549
|
sanitizeOrderInDay: (value) => this.sanitizeOrderInDay(value),
|
|
5497
5550
|
sanitizeDateOnly: (value) => this.sanitizeDateOnly(value),
|
|
5498
5551
|
normalizeTaskEventActor: (actorRef, workspaceId) => this.normalizeTaskEventActor(actorRef, workspaceId),
|
|
@@ -5572,6 +5625,489 @@ export class TaskforceCore {
|
|
|
5572
5625
|
config: this.readConfig()
|
|
5573
5626
|
};
|
|
5574
5627
|
}
|
|
5628
|
+
listTaskSummaries(workspaceId, archived) {
|
|
5629
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
5630
|
+
const rows = this.db.prepare(`
|
|
5631
|
+
WITH ranked_events AS (
|
|
5632
|
+
SELECT e.entity_id,
|
|
5633
|
+
e.id,
|
|
5634
|
+
e.action,
|
|
5635
|
+
e.actor,
|
|
5636
|
+
e.actor_type,
|
|
5637
|
+
e.details_json,
|
|
5638
|
+
e.created_at,
|
|
5639
|
+
ROW_NUMBER() OVER (
|
|
5640
|
+
PARTITION BY e.entity_id
|
|
5641
|
+
ORDER BY e.created_at DESC, e.id DESC
|
|
5642
|
+
) AS event_rank
|
|
5643
|
+
FROM entity_events e
|
|
5644
|
+
JOIN tasks scoped_task
|
|
5645
|
+
ON scoped_task.tenant_id = e.tenant_id
|
|
5646
|
+
AND scoped_task.workspace_id = e.workspace_id
|
|
5647
|
+
AND scoped_task.id = e.entity_id
|
|
5648
|
+
WHERE e.tenant_id = ? AND e.workspace_id = ?
|
|
5649
|
+
AND e.entity_type = 'task'
|
|
5650
|
+
AND scoped_task.is_archived = ?
|
|
5651
|
+
-- Comment activity is represented by the comment itself; the
|
|
5652
|
+
-- paired audit event is intentionally hidden by taskActivity.
|
|
5653
|
+
AND e.action <> 'task-comment-added'
|
|
5654
|
+
-- Durable text-only autosaves retain an empty task-updated event
|
|
5655
|
+
-- for stable replay identity, but full task activity hides it.
|
|
5656
|
+
-- Match that visibility rule before ranking so an internal event
|
|
5657
|
+
-- cannot displace the latest meaningful card activity. REPLACE is
|
|
5658
|
+
-- intentionally used instead of dialect-specific JSON operators.
|
|
5659
|
+
AND NOT (
|
|
5660
|
+
e.action = 'task-updated'
|
|
5661
|
+
AND REPLACE(COALESCE(e.details_json, ''), ' ', '') LIKE '%"saveSource":"autosave"%'
|
|
5662
|
+
AND REPLACE(COALESCE(e.details_json, ''), ' ', '') LIKE '%"changes":{}%'
|
|
5663
|
+
)
|
|
5664
|
+
)
|
|
5665
|
+
SELECT t.id, t.reference_number, t.local_reference_number, t.title, t.status, t.priority,
|
|
5666
|
+
t.complexity, t.type, t.category, t.created_at, t.updated_at, t.completed_at,
|
|
5667
|
+
t.canceled_reason, t.is_archived, t.scheduled_date, t.due_date,
|
|
5668
|
+
t.scheduled_week_key, t.order_in_day, t.workstream_order, t.workstream_id,
|
|
5669
|
+
t.card_cover_asset_id, t.taxonomies, t.created_by, t.assignee,
|
|
5670
|
+
event.id AS latest_event_id,
|
|
5671
|
+
event.action AS latest_event_action,
|
|
5672
|
+
event.actor AS latest_event_actor,
|
|
5673
|
+
event.actor_type AS latest_event_actor_type,
|
|
5674
|
+
event.details_json AS latest_event_details_json,
|
|
5675
|
+
event.created_at AS latest_event_at
|
|
5676
|
+
FROM tasks t
|
|
5677
|
+
LEFT JOIN ranked_events event
|
|
5678
|
+
ON event.entity_id = t.id AND event.event_rank = 1
|
|
5679
|
+
WHERE t.tenant_id = ? AND t.workspace_id = ? AND t.is_archived = ?
|
|
5680
|
+
ORDER BY ${archived ? 't.completed_at DESC, t.created_at DESC' : 't.created_at DESC'}
|
|
5681
|
+
`).all(this.tenantId, normalizedWorkspaceId, archived ? 1 : 0, this.tenantId, normalizedWorkspaceId, archived ? 1 : 0);
|
|
5682
|
+
const comments = this.db.prepare(`
|
|
5683
|
+
WITH ranked_comments AS (
|
|
5684
|
+
SELECT c.task_id,
|
|
5685
|
+
c.id,
|
|
5686
|
+
c.text,
|
|
5687
|
+
c.author,
|
|
5688
|
+
c.timestamp,
|
|
5689
|
+
COUNT(*) OVER (PARTITION BY c.task_id) AS count,
|
|
5690
|
+
MAX(c.timestamp) OVER (PARTITION BY c.task_id) AS revision,
|
|
5691
|
+
ROW_NUMBER() OVER (
|
|
5692
|
+
PARTITION BY c.task_id
|
|
5693
|
+
ORDER BY c.timestamp DESC, c.id DESC
|
|
5694
|
+
) AS comment_rank
|
|
5695
|
+
FROM comments c
|
|
5696
|
+
JOIN tasks t
|
|
5697
|
+
ON t.tenant_id = c.tenant_id
|
|
5698
|
+
AND t.workspace_id = c.workspace_id
|
|
5699
|
+
AND t.id = c.task_id
|
|
5700
|
+
WHERE c.tenant_id = ? AND c.workspace_id = ? AND t.is_archived = ?
|
|
5701
|
+
)
|
|
5702
|
+
SELECT task_id, id, text, author, timestamp, count, revision
|
|
5703
|
+
FROM ranked_comments
|
|
5704
|
+
WHERE comment_rank = 1
|
|
5705
|
+
`).all(this.tenantId, normalizedWorkspaceId, archived ? 1 : 0);
|
|
5706
|
+
const checklist = this.db.prepare(`
|
|
5707
|
+
SELECT c.task_id, COUNT(*) AS count,
|
|
5708
|
+
SUM(CASE WHEN c.is_completed = 1 THEN 1 ELSE 0 END) AS completed_count,
|
|
5709
|
+
MAX(COALESCE(c.updated_at, c.created_at)) AS revision
|
|
5710
|
+
FROM checklist_items c
|
|
5711
|
+
JOIN tasks t ON t.tenant_id = c.tenant_id AND t.workspace_id = c.workspace_id AND t.id = c.task_id
|
|
5712
|
+
WHERE c.tenant_id = ? AND c.workspace_id = ? AND t.is_archived = ?
|
|
5713
|
+
GROUP BY c.task_id
|
|
5714
|
+
`).all(this.tenantId, normalizedWorkspaceId, archived ? 1 : 0);
|
|
5715
|
+
const attachments = this.db.prepare(`
|
|
5716
|
+
SELECT l.task_id, COUNT(a.asset_id) AS count,
|
|
5717
|
+
MAX(CASE WHEN l.asset_id = t.card_cover_asset_id THEN l.asset_id ELSE NULL END) AS cover_asset_id,
|
|
5718
|
+
MAX(CASE WHEN l.asset_id = t.card_cover_asset_id THEN a.storage_provider ELSE NULL END) AS cover_storage_provider,
|
|
5719
|
+
MAX(CASE WHEN l.asset_id = t.card_cover_asset_id THEN a.storage_key ELSE NULL END) AS cover_storage_key,
|
|
5720
|
+
MAX(CASE WHEN l.asset_id = t.card_cover_asset_id THEN a.original_filename ELSE NULL END) AS cover_original_filename,
|
|
5721
|
+
MAX(CASE WHEN l.asset_id = t.card_cover_asset_id THEN COALESCE(l.display_name, a.logical_name) ELSE NULL END) AS cover_display_name,
|
|
5722
|
+
MAX(CASE
|
|
5723
|
+
WHEN COALESCE(a.updated_at, '') > COALESCE(l.updated_at, '') THEN a.updated_at
|
|
5724
|
+
ELSE l.updated_at
|
|
5725
|
+
END) AS revision
|
|
5726
|
+
FROM workspace_asset_links l
|
|
5727
|
+
JOIN tasks t ON t.tenant_id = l.tenant_id AND t.workspace_id = l.workspace_id AND t.id = l.task_id
|
|
5728
|
+
JOIN workspace_assets a ON a.tenant_id = l.tenant_id AND a.workspace_id = l.workspace_id AND a.asset_id = l.asset_id AND a.deleted_at IS NULL
|
|
5729
|
+
WHERE l.tenant_id = ? AND l.workspace_id = ? AND t.is_archived = ? AND l.target_type = 'task' AND l.deleted_at IS NULL
|
|
5730
|
+
GROUP BY l.task_id
|
|
5731
|
+
`).all(this.tenantId, normalizedWorkspaceId, archived ? 1 : 0);
|
|
5732
|
+
const relationships = this.db.prepare(`
|
|
5733
|
+
SELECT task_id, COUNT(*) AS count, MAX(updated_at) AS revision
|
|
5734
|
+
FROM (
|
|
5735
|
+
SELECT r.source_task_id AS task_id, r.updated_at
|
|
5736
|
+
FROM task_relationships r
|
|
5737
|
+
JOIN tasks t ON t.tenant_id = r.tenant_id AND t.workspace_id = r.workspace_id AND t.id = r.source_task_id
|
|
5738
|
+
WHERE r.tenant_id = ? AND r.workspace_id = ? AND r.deleted_at IS NULL AND t.is_archived = ?
|
|
5739
|
+
UNION ALL
|
|
5740
|
+
SELECT r.target_task_id AS task_id, r.updated_at
|
|
5741
|
+
FROM task_relationships r
|
|
5742
|
+
JOIN tasks t ON t.tenant_id = r.tenant_id AND t.workspace_id = r.workspace_id AND t.id = r.target_task_id
|
|
5743
|
+
WHERE r.tenant_id = ? AND r.workspace_id = ? AND r.deleted_at IS NULL
|
|
5744
|
+
AND r.target_task_id <> r.source_task_id AND t.is_archived = ?
|
|
5745
|
+
) scoped_relationships
|
|
5746
|
+
GROUP BY task_id
|
|
5747
|
+
`).all(this.tenantId, normalizedWorkspaceId, archived ? 1 : 0, this.tenantId, normalizedWorkspaceId, archived ? 1 : 0);
|
|
5748
|
+
const countByTask = (entries) => new Map(entries.map((entry) => [String(entry.task_id || ''), Number(entry.count || 0)]));
|
|
5749
|
+
const commentCountByTask = countByTask(comments);
|
|
5750
|
+
const attachmentCountByTask = countByTask(attachments);
|
|
5751
|
+
const commentRevisionByTask = new Map(comments.map((entry) => [String(entry.task_id || ''), String(entry.revision || '')]));
|
|
5752
|
+
const attachmentRevisionByTask = new Map(attachments.map((entry) => [String(entry.task_id || ''), String(entry.revision || '')]));
|
|
5753
|
+
const coverAttachmentByTask = new Map(attachments.flatMap((entry) => {
|
|
5754
|
+
const taskId = String(entry.task_id || '');
|
|
5755
|
+
const assetId = String(entry.cover_asset_id || '').trim();
|
|
5756
|
+
const storageKey = String(entry.cover_storage_key || '').trim();
|
|
5757
|
+
const storageProvider = String(entry.cover_storage_provider || 'local').trim();
|
|
5758
|
+
if (!taskId || !assetId || !storageKey)
|
|
5759
|
+
return [];
|
|
5760
|
+
const isExternalHttpReference = storageProvider === 'external' && /^https?:\/\//i.test(storageKey);
|
|
5761
|
+
return [[taskId, {
|
|
5762
|
+
assetId,
|
|
5763
|
+
path: isExternalHttpReference
|
|
5764
|
+
? storageKey
|
|
5765
|
+
: storageProvider === 'external'
|
|
5766
|
+
? `/api/taskforce/context-link?path=${encodeURIComponent(storageKey)}`
|
|
5767
|
+
: `/api/taskforce/context/${encodeURIComponent(storageKey)}`,
|
|
5768
|
+
...(isExternalHttpReference ? {} : { fsPath: storageKey }),
|
|
5769
|
+
displayName: entry.cover_display_name ? String(entry.cover_display_name) : undefined,
|
|
5770
|
+
originalFilename: entry.cover_original_filename ? String(entry.cover_original_filename) : undefined,
|
|
5771
|
+
linkRole: 'image',
|
|
5772
|
+
taskId,
|
|
5773
|
+
}]];
|
|
5774
|
+
}));
|
|
5775
|
+
const checklistByTask = new Map(checklist.map((entry) => [String(entry.task_id || ''), {
|
|
5776
|
+
total: Number(entry.count || 0),
|
|
5777
|
+
completed: Number(entry.completed_count || 0),
|
|
5778
|
+
revision: String(entry.revision || ''),
|
|
5779
|
+
}]));
|
|
5780
|
+
const relationshipCountByTask = countByTask(relationships);
|
|
5781
|
+
const relationshipRevisionByTask = new Map(relationships.map((entry) => [
|
|
5782
|
+
String(entry.task_id || ''),
|
|
5783
|
+
String(entry.revision || ''),
|
|
5784
|
+
]));
|
|
5785
|
+
const latestActivityByTask = new Map();
|
|
5786
|
+
const latestActivityRevisionByTask = new Map();
|
|
5787
|
+
const considerLatestActivity = (taskId, item) => {
|
|
5788
|
+
const existing = latestActivityByTask.get(taskId);
|
|
5789
|
+
const nextTimestamp = Date.parse(String(item.timestamp || ''));
|
|
5790
|
+
const existingTimestamp = existing ? Date.parse(String(existing.timestamp || '')) : Number.NaN;
|
|
5791
|
+
const isNewer = !existing
|
|
5792
|
+
|| (Number.isFinite(nextTimestamp) && !Number.isFinite(existingTimestamp))
|
|
5793
|
+
|| (Number.isFinite(nextTimestamp) && Number.isFinite(existingTimestamp) && nextTimestamp > existingTimestamp)
|
|
5794
|
+
|| (nextTimestamp === existingTimestamp && item.id.localeCompare(existing.id) > 0);
|
|
5795
|
+
if (!isNewer)
|
|
5796
|
+
return;
|
|
5797
|
+
latestActivityByTask.set(taskId, item);
|
|
5798
|
+
latestActivityRevisionByTask.set(taskId, `${item.timestamp}:${item.id}`);
|
|
5799
|
+
};
|
|
5800
|
+
for (const entry of comments) {
|
|
5801
|
+
const taskId = String(entry.task_id || '').trim();
|
|
5802
|
+
const commentId = String(entry.id || '').trim();
|
|
5803
|
+
const timestamp = String(entry.timestamp || '');
|
|
5804
|
+
if (!taskId || !commentId)
|
|
5805
|
+
continue;
|
|
5806
|
+
considerLatestActivity(taskId, {
|
|
5807
|
+
id: `comment:${commentId}`,
|
|
5808
|
+
type: 'comment',
|
|
5809
|
+
timestamp,
|
|
5810
|
+
comment: {
|
|
5811
|
+
text: String(entry.text || ''),
|
|
5812
|
+
author: String(entry.author || 'user').trim() || 'user',
|
|
5813
|
+
},
|
|
5814
|
+
});
|
|
5815
|
+
}
|
|
5816
|
+
for (const row of rows) {
|
|
5817
|
+
const taskId = String(row.id || '').trim();
|
|
5818
|
+
const eventId = String(row.latest_event_id || '').trim();
|
|
5819
|
+
const timestamp = String(row.latest_event_at || '');
|
|
5820
|
+
if (!taskId || !eventId)
|
|
5821
|
+
continue;
|
|
5822
|
+
let details = {};
|
|
5823
|
+
try {
|
|
5824
|
+
const parsed = row.latest_event_details_json ? JSON.parse(String(row.latest_event_details_json)) : {};
|
|
5825
|
+
details = parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
5826
|
+
}
|
|
5827
|
+
catch {
|
|
5828
|
+
details = {};
|
|
5829
|
+
}
|
|
5830
|
+
considerLatestActivity(taskId, {
|
|
5831
|
+
id: `event:${eventId}`,
|
|
5832
|
+
type: 'event',
|
|
5833
|
+
timestamp,
|
|
5834
|
+
event: {
|
|
5835
|
+
action: String(row.latest_event_action || ''),
|
|
5836
|
+
actor: String(row.latest_event_actor || 'system').trim() || 'system',
|
|
5837
|
+
actorType: row.latest_event_actor_type === 'human' || row.latest_event_actor_type === 'ai'
|
|
5838
|
+
? row.latest_event_actor_type
|
|
5839
|
+
: 'system',
|
|
5840
|
+
details,
|
|
5841
|
+
},
|
|
5842
|
+
});
|
|
5843
|
+
}
|
|
5844
|
+
return rows.map((row) => {
|
|
5845
|
+
const id = String(row.id || '').trim();
|
|
5846
|
+
const scheduledDate = this.sanitizeDateOnly(row.scheduled_date);
|
|
5847
|
+
const dueDate = this.sanitizeDateOnly(row.due_date);
|
|
5848
|
+
const referenceNumber = this.sanitizeTaskReferenceNumber(row.reference_number);
|
|
5849
|
+
const localReferenceNumber = this.sanitizeTaskReferenceNumber(row.local_reference_number);
|
|
5850
|
+
let taxonomies = {};
|
|
5851
|
+
try {
|
|
5852
|
+
taxonomies = JSON.parse(row.taxonomies || '{}');
|
|
5853
|
+
}
|
|
5854
|
+
catch {
|
|
5855
|
+
taxonomies = {};
|
|
5856
|
+
}
|
|
5857
|
+
const checklistCounts = checklistByTask.get(id) || { total: 0, completed: 0, revision: '' };
|
|
5858
|
+
const commentCount = commentCountByTask.get(id) || 0;
|
|
5859
|
+
const attachmentCount = attachmentCountByTask.get(id) || 0;
|
|
5860
|
+
const relationshipCount = relationshipCountByTask.get(id) || 0;
|
|
5861
|
+
const detailRevision = JSON.stringify([
|
|
5862
|
+
String(row.updated_at || row.created_at || ''),
|
|
5863
|
+
commentCount,
|
|
5864
|
+
commentRevisionByTask.get(id) || '',
|
|
5865
|
+
checklistCounts.total,
|
|
5866
|
+
checklistCounts.completed,
|
|
5867
|
+
checklistCounts.revision,
|
|
5868
|
+
attachmentCount,
|
|
5869
|
+
attachmentRevisionByTask.get(id) || '',
|
|
5870
|
+
relationshipCount,
|
|
5871
|
+
relationshipRevisionByTask.get(id) || '',
|
|
5872
|
+
latestActivityRevisionByTask.get(id) || '',
|
|
5873
|
+
]);
|
|
5874
|
+
return {
|
|
5875
|
+
id,
|
|
5876
|
+
referenceNumber,
|
|
5877
|
+
localReferenceNumber,
|
|
5878
|
+
referenceLabel: deriveTaskReferenceLabel({ referenceNumber, localReferenceNumber }),
|
|
5879
|
+
title: String(row.title || ''),
|
|
5880
|
+
status: this.normalizeStatusValue(row.status),
|
|
5881
|
+
priority: this.normalizePriorityValue(row.priority),
|
|
5882
|
+
complexity: Number(row.complexity || 3),
|
|
5883
|
+
type: String(row.type || 'task'),
|
|
5884
|
+
category: String(row.category || 'default'),
|
|
5885
|
+
createdAt: String(row.created_at || ''),
|
|
5886
|
+
updatedAt: row.updated_at ? String(row.updated_at) : undefined,
|
|
5887
|
+
completedAt: row.completed_at ? String(row.completed_at) : null,
|
|
5888
|
+
canceledReason: row.canceled_reason ? String(row.canceled_reason) : null,
|
|
5889
|
+
isArchived: row.is_archived === 1 || row.is_archived === true,
|
|
5890
|
+
scheduledDate,
|
|
5891
|
+
dueDate,
|
|
5892
|
+
scheduledWeekKey: row.scheduled_week_key || (scheduledDate ? this.dateOnlyToIsoWeekKey(scheduledDate) : null),
|
|
5893
|
+
orderInDay: this.sanitizeOrderInDay(row.order_in_day),
|
|
5894
|
+
workstreamOrder: this.sanitizeOrderInDay(row.workstream_order),
|
|
5895
|
+
workstreamId: this.sanitizeWorkstreamId(row.workstream_id),
|
|
5896
|
+
cardCoverAssetId: typeof row.card_cover_asset_id === 'string' && row.card_cover_asset_id.trim()
|
|
5897
|
+
? row.card_cover_asset_id.trim()
|
|
5898
|
+
: null,
|
|
5899
|
+
cardCoverAttachment: coverAttachmentByTask.get(id) || null,
|
|
5900
|
+
taxonomies,
|
|
5901
|
+
createdBy: this.normalizeCreatedBy(row.created_by, normalizedWorkspaceId, { preserveGenericAi: true }),
|
|
5902
|
+
assignee: this.sanitizeAssignee(row.assignee, normalizedWorkspaceId),
|
|
5903
|
+
isSummary: true,
|
|
5904
|
+
commentCount,
|
|
5905
|
+
checklistCount: checklistCounts.total,
|
|
5906
|
+
completedChecklistCount: checklistCounts.completed,
|
|
5907
|
+
attachmentCount,
|
|
5908
|
+
relationshipCount,
|
|
5909
|
+
latestActivity: latestActivityByTask.get(id) || null,
|
|
5910
|
+
detailRevision,
|
|
5911
|
+
};
|
|
5912
|
+
});
|
|
5913
|
+
}
|
|
5914
|
+
listActiveTaskSummaries(workspaceId) {
|
|
5915
|
+
return { tasks: this.listTaskSummaries(workspaceId, false) };
|
|
5916
|
+
}
|
|
5917
|
+
listArchiveSummaries(workspaceId) {
|
|
5918
|
+
return { archived: this.listTaskSummaries(workspaceId, true) };
|
|
5919
|
+
}
|
|
5920
|
+
getTaskDetailRevision(id, workspaceId) {
|
|
5921
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
5922
|
+
const taskId = String(id || '').trim();
|
|
5923
|
+
if (!taskId)
|
|
5924
|
+
return null;
|
|
5925
|
+
const row = this.db.prepare(`
|
|
5926
|
+
WITH target_task AS (
|
|
5927
|
+
SELECT id, created_at, updated_at
|
|
5928
|
+
FROM tasks
|
|
5929
|
+
WHERE tenant_id = ? AND workspace_id = ? AND id = ?
|
|
5930
|
+
),
|
|
5931
|
+
comment_stats AS (
|
|
5932
|
+
SELECT COUNT(*) AS count, MAX(c.timestamp) AS revision
|
|
5933
|
+
FROM comments c
|
|
5934
|
+
JOIN target_task t ON t.id = c.task_id
|
|
5935
|
+
WHERE c.tenant_id = ? AND c.workspace_id = ?
|
|
5936
|
+
),
|
|
5937
|
+
latest_comment AS (
|
|
5938
|
+
SELECT c.id, c.timestamp
|
|
5939
|
+
FROM comments c
|
|
5940
|
+
JOIN target_task t ON t.id = c.task_id
|
|
5941
|
+
WHERE c.tenant_id = ? AND c.workspace_id = ?
|
|
5942
|
+
ORDER BY c.timestamp DESC, c.id DESC
|
|
5943
|
+
LIMIT 1
|
|
5944
|
+
),
|
|
5945
|
+
checklist_stats AS (
|
|
5946
|
+
SELECT COUNT(*) AS count,
|
|
5947
|
+
SUM(CASE WHEN c.is_completed = 1 THEN 1 ELSE 0 END) AS completed_count,
|
|
5948
|
+
MAX(COALESCE(c.updated_at, c.created_at)) AS revision
|
|
5949
|
+
FROM checklist_items c
|
|
5950
|
+
JOIN target_task t ON t.id = c.task_id
|
|
5951
|
+
WHERE c.tenant_id = ? AND c.workspace_id = ?
|
|
5952
|
+
),
|
|
5953
|
+
attachment_stats AS (
|
|
5954
|
+
SELECT COUNT(a.asset_id) AS count,
|
|
5955
|
+
MAX(CASE
|
|
5956
|
+
WHEN COALESCE(a.updated_at, '') > COALESCE(l.updated_at, '') THEN a.updated_at
|
|
5957
|
+
ELSE l.updated_at
|
|
5958
|
+
END) AS revision
|
|
5959
|
+
FROM workspace_asset_links l
|
|
5960
|
+
JOIN target_task t ON t.id = l.task_id
|
|
5961
|
+
JOIN workspace_assets a
|
|
5962
|
+
ON a.tenant_id = l.tenant_id
|
|
5963
|
+
AND a.workspace_id = l.workspace_id
|
|
5964
|
+
AND a.asset_id = l.asset_id
|
|
5965
|
+
AND a.deleted_at IS NULL
|
|
5966
|
+
WHERE l.tenant_id = ? AND l.workspace_id = ?
|
|
5967
|
+
AND l.target_type = 'task' AND l.deleted_at IS NULL
|
|
5968
|
+
),
|
|
5969
|
+
relationship_rows AS (
|
|
5970
|
+
SELECT r.updated_at
|
|
5971
|
+
FROM task_relationships r
|
|
5972
|
+
JOIN target_task t ON t.id = r.source_task_id
|
|
5973
|
+
WHERE r.tenant_id = ? AND r.workspace_id = ? AND r.deleted_at IS NULL
|
|
5974
|
+
UNION ALL
|
|
5975
|
+
SELECT r.updated_at
|
|
5976
|
+
FROM task_relationships r
|
|
5977
|
+
JOIN target_task t ON t.id = r.target_task_id
|
|
5978
|
+
WHERE r.tenant_id = ? AND r.workspace_id = ? AND r.deleted_at IS NULL
|
|
5979
|
+
AND r.target_task_id <> r.source_task_id
|
|
5980
|
+
),
|
|
5981
|
+
relationship_stats AS (
|
|
5982
|
+
SELECT COUNT(*) AS count, MAX(updated_at) AS revision
|
|
5983
|
+
FROM relationship_rows
|
|
5984
|
+
),
|
|
5985
|
+
latest_event AS (
|
|
5986
|
+
SELECT e.id, e.created_at
|
|
5987
|
+
FROM entity_events e
|
|
5988
|
+
JOIN target_task t ON t.id = e.entity_id
|
|
5989
|
+
WHERE e.tenant_id = ? AND e.workspace_id = ?
|
|
5990
|
+
AND e.entity_type = 'task'
|
|
5991
|
+
AND e.action <> 'task-comment-added'
|
|
5992
|
+
AND NOT (
|
|
5993
|
+
e.action = 'task-updated'
|
|
5994
|
+
AND REPLACE(COALESCE(e.details_json, ''), ' ', '') LIKE '%"saveSource":"autosave"%'
|
|
5995
|
+
AND REPLACE(COALESCE(e.details_json, ''), ' ', '') LIKE '%"changes":{}%'
|
|
5996
|
+
)
|
|
5997
|
+
ORDER BY e.created_at DESC, e.id DESC
|
|
5998
|
+
LIMIT 1
|
|
5999
|
+
)
|
|
6000
|
+
SELECT t.created_at, t.updated_at,
|
|
6001
|
+
comments.count AS comment_count, comments.revision AS comment_revision,
|
|
6002
|
+
latest_comment.id AS latest_comment_id,
|
|
6003
|
+
latest_comment.timestamp AS latest_comment_at,
|
|
6004
|
+
checklist.count AS checklist_count,
|
|
6005
|
+
checklist.completed_count AS completed_checklist_count,
|
|
6006
|
+
checklist.revision AS checklist_revision,
|
|
6007
|
+
attachments.count AS attachment_count,
|
|
6008
|
+
attachments.revision AS attachment_revision,
|
|
6009
|
+
relationships.count AS relationship_count,
|
|
6010
|
+
relationships.revision AS relationship_revision,
|
|
6011
|
+
latest_event.id AS latest_event_id,
|
|
6012
|
+
latest_event.created_at AS latest_event_at
|
|
6013
|
+
FROM target_task t
|
|
6014
|
+
CROSS JOIN comment_stats comments
|
|
6015
|
+
CROSS JOIN checklist_stats checklist
|
|
6016
|
+
CROSS JOIN attachment_stats attachments
|
|
6017
|
+
CROSS JOIN relationship_stats relationships
|
|
6018
|
+
LEFT JOIN latest_comment ON 1 = 1
|
|
6019
|
+
LEFT JOIN latest_event ON 1 = 1
|
|
6020
|
+
`).get(this.tenantId, normalizedWorkspaceId, taskId, this.tenantId, normalizedWorkspaceId, this.tenantId, normalizedWorkspaceId, this.tenantId, normalizedWorkspaceId, this.tenantId, normalizedWorkspaceId, this.tenantId, normalizedWorkspaceId, this.tenantId, normalizedWorkspaceId, this.tenantId, normalizedWorkspaceId);
|
|
6021
|
+
if (!row)
|
|
6022
|
+
return null;
|
|
6023
|
+
const latestCommentId = String(row.latest_comment_id || '').trim();
|
|
6024
|
+
const latestCommentAt = String(row.latest_comment_at || '');
|
|
6025
|
+
const latestEventId = String(row.latest_event_id || '').trim();
|
|
6026
|
+
const latestEventAt = String(row.latest_event_at || '');
|
|
6027
|
+
const commentTimestamp = Date.parse(latestCommentAt);
|
|
6028
|
+
const eventTimestamp = Date.parse(latestEventAt);
|
|
6029
|
+
const commentActivityId = latestCommentId ? `comment:${latestCommentId}` : '';
|
|
6030
|
+
const eventActivityId = latestEventId ? `event:${latestEventId}` : '';
|
|
6031
|
+
const latestActivityRevision = !latestEventId
|
|
6032
|
+
|| (latestCommentId && ((Number.isFinite(commentTimestamp) && !Number.isFinite(eventTimestamp))
|
|
6033
|
+
|| commentTimestamp > eventTimestamp
|
|
6034
|
+
|| (commentTimestamp === eventTimestamp && commentActivityId.localeCompare(eventActivityId) > 0)))
|
|
6035
|
+
? (latestCommentId ? `${latestCommentAt}:${commentActivityId}` : '')
|
|
6036
|
+
: `${latestEventAt}:${eventActivityId}`;
|
|
6037
|
+
return JSON.stringify([
|
|
6038
|
+
String(row.updated_at || row.created_at || ''),
|
|
6039
|
+
Number(row.comment_count || 0),
|
|
6040
|
+
String(row.comment_revision || ''),
|
|
6041
|
+
Number(row.checklist_count || 0),
|
|
6042
|
+
Number(row.completed_checklist_count || 0),
|
|
6043
|
+
String(row.checklist_revision || ''),
|
|
6044
|
+
Number(row.attachment_count || 0),
|
|
6045
|
+
String(row.attachment_revision || ''),
|
|
6046
|
+
Number(row.relationship_count || 0),
|
|
6047
|
+
String(row.relationship_revision || ''),
|
|
6048
|
+
latestActivityRevision,
|
|
6049
|
+
]);
|
|
6050
|
+
}
|
|
6051
|
+
getTaskDetailSnapshot(id, workspaceId) {
|
|
6052
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
6053
|
+
return this.db.transaction(() => {
|
|
6054
|
+
if (this.db.provider === 'postgres') {
|
|
6055
|
+
this.db.exec('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ');
|
|
6056
|
+
}
|
|
6057
|
+
const task = this.getTask(id, normalizedWorkspaceId);
|
|
6058
|
+
if (!task)
|
|
6059
|
+
return null;
|
|
6060
|
+
const detailRevision = this.getTaskDetailRevision(id, normalizedWorkspaceId);
|
|
6061
|
+
return detailRevision ? { ...task, detailRevision } : null;
|
|
6062
|
+
})();
|
|
6063
|
+
}
|
|
6064
|
+
updateTaskIfDetailRevisionMatches(id, expectedDetailRevision, updates, workspaceId, options) {
|
|
6065
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
6066
|
+
const updated = this.db.transaction(() => {
|
|
6067
|
+
if (this.db.provider === 'postgres') {
|
|
6068
|
+
this.db.exec('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE');
|
|
6069
|
+
}
|
|
6070
|
+
const currentDetailRevision = this.getTaskDetailRevision(id, normalizedWorkspaceId);
|
|
6071
|
+
if (currentDetailRevision === null)
|
|
6072
|
+
return null;
|
|
6073
|
+
if (currentDetailRevision !== expectedDetailRevision) {
|
|
6074
|
+
throw new TaskforceRuleError('Task changed after this editor was loaded. Reload the latest task before saving.', 409, 'TASK_DETAIL_REVISION_CONFLICT', { currentDetailRevision });
|
|
6075
|
+
}
|
|
6076
|
+
const task = this.updateTask(id, updates, normalizedWorkspaceId, {
|
|
6077
|
+
actorRef: options?.actorRef,
|
|
6078
|
+
saveSource: options?.saveSource,
|
|
6079
|
+
persistBackup: false,
|
|
6080
|
+
emitRealtime: false,
|
|
6081
|
+
});
|
|
6082
|
+
if (!task)
|
|
6083
|
+
return null;
|
|
6084
|
+
const detailRevision = this.getTaskDetailRevision(id, normalizedWorkspaceId);
|
|
6085
|
+
return detailRevision ? { ...task, detailRevision } : null;
|
|
6086
|
+
})();
|
|
6087
|
+
if (updated) {
|
|
6088
|
+
this.persistBackup();
|
|
6089
|
+
this.emitRealtimeMutation({
|
|
6090
|
+
workspaceId: normalizedWorkspaceId,
|
|
6091
|
+
entityType: 'task',
|
|
6092
|
+
changeType: 'upsert',
|
|
6093
|
+
entityIds: [updated.id],
|
|
6094
|
+
});
|
|
6095
|
+
}
|
|
6096
|
+
return updated;
|
|
6097
|
+
}
|
|
6098
|
+
assertTaskDetailRevisionForUpdate(id, expectedDetailRevision, workspaceId) {
|
|
6099
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
6100
|
+
const lockSql = this.db.provider === 'postgres'
|
|
6101
|
+
? `SELECT id FROM tasks WHERE tenant_id = ? AND workspace_id = ? AND id = ? FOR UPDATE`
|
|
6102
|
+
: `SELECT id FROM tasks WHERE tenant_id = ? AND workspace_id = ? AND id = ?`;
|
|
6103
|
+
const task = this.db.prepare(lockSql).get(this.tenantId, normalizedWorkspaceId, id);
|
|
6104
|
+
if (!task)
|
|
6105
|
+
throw new TaskforceRuleError('Task not found.', 404, 'TASK_NOT_FOUND');
|
|
6106
|
+
const currentDetailRevision = this.getTaskDetailRevision(id, normalizedWorkspaceId);
|
|
6107
|
+
if (currentDetailRevision !== expectedDetailRevision) {
|
|
6108
|
+
throw new TaskforceRuleError('Task changed after this editor was loaded. Reload the latest task before saving.', 409, 'TASK_DETAIL_REVISION_CONFLICT', { currentDetailRevision });
|
|
6109
|
+
}
|
|
6110
|
+
}
|
|
5575
6111
|
/**
|
|
5576
6112
|
* Filter-pushdown listing used by read-only surfaces that don't need the
|
|
5577
6113
|
* full task graph (MCP list_tasks, CLI summaries). Avoids the per-task
|
|
@@ -5750,41 +6286,53 @@ export class TaskforceCore {
|
|
|
5750
6286
|
};
|
|
5751
6287
|
});
|
|
5752
6288
|
}
|
|
5753
|
-
searchTaskSummaries(query, scope = 'all', workspaceId) {
|
|
6289
|
+
searchTaskSummaries(query, scope = 'all', workspaceId, options) {
|
|
5754
6290
|
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
5755
|
-
const normalizedQuery = String(query || '')
|
|
5756
|
-
if (!normalizedQuery)
|
|
6291
|
+
const normalizedQuery = normalizeTaskSearchQuery(String(query || ''));
|
|
6292
|
+
if (!normalizedQuery.raw)
|
|
5757
6293
|
return [];
|
|
5758
|
-
const exactReferenceNumber = parseTaskReference(normalizedQuery);
|
|
5759
|
-
const exactLocalReferenceNumber = parseLocalTaskReference(normalizedQuery);
|
|
5760
|
-
const
|
|
6294
|
+
const exactReferenceNumber = parseTaskReference(normalizedQuery.raw);
|
|
6295
|
+
const exactLocalReferenceNumber = parseLocalTaskReference(normalizedQuery.raw);
|
|
6296
|
+
const searchableText = `
|
|
6297
|
+
COALESCE(title, '') || ' ' ||
|
|
6298
|
+
COALESCE(description, '') || ' ' ||
|
|
6299
|
+
COALESCE(id, '') || ' ' ||
|
|
6300
|
+
CASE WHEN reference_number IS NULL THEN '' ELSE 'T-' || CAST(reference_number AS TEXT) END || ' ' ||
|
|
6301
|
+
CASE WHEN local_reference_number IS NULL THEN '' ELSE 'LT-' || CAST(local_reference_number AS TEXT) END || ' ' ||
|
|
6302
|
+
COALESCE(category, '') || ' ' ||
|
|
6303
|
+
COALESCE(type, '') || ' ' ||
|
|
6304
|
+
COALESCE(CAST(priority AS TEXT), '') || ' ' ||
|
|
6305
|
+
COALESCE(status, '') || ' ' ||
|
|
6306
|
+
COALESCE(assignee, '') || ' ' ||
|
|
6307
|
+
COALESCE(canceled_reason, '')
|
|
6308
|
+
`;
|
|
6309
|
+
const escapedTerms = normalizedQuery.tokens.map((token) => (`%${token.replace(/[\\%_]/g, '\\$&')}%`));
|
|
5761
6310
|
const clauses = [
|
|
5762
6311
|
'tenant_id = ?',
|
|
5763
6312
|
'workspace_id = ?',
|
|
5764
|
-
|
|
6313
|
+
...escapedTerms.map(() => `LOWER(${searchableText}) LIKE ? ESCAPE '\\'`),
|
|
5765
6314
|
];
|
|
5766
6315
|
const params = [
|
|
5767
6316
|
this.tenantId,
|
|
5768
6317
|
normalizedWorkspaceId,
|
|
5769
|
-
|
|
5770
|
-
term,
|
|
5771
|
-
term,
|
|
5772
|
-
exactReferenceNumber || -1,
|
|
5773
|
-
exactLocalReferenceNumber || -1,
|
|
6318
|
+
...escapedTerms,
|
|
5774
6319
|
];
|
|
5775
6320
|
if (scope === 'active')
|
|
5776
6321
|
clauses.push('is_archived = 0');
|
|
5777
6322
|
if (scope === 'archive')
|
|
5778
6323
|
clauses.push('is_archived = 1');
|
|
6324
|
+
const limit = Math.max(1, Math.min(51, Math.trunc(options?.limit ?? 50)));
|
|
6325
|
+
const offset = Math.max(0, Math.trunc(options?.offset ?? 0));
|
|
5779
6326
|
const rows = this.db.prepare(`
|
|
5780
|
-
SELECT id, reference_number, local_reference_number, title, status, assignee, category, priority
|
|
6327
|
+
SELECT id, reference_number, local_reference_number, title, status, assignee, category, priority, is_archived
|
|
5781
6328
|
FROM tasks
|
|
5782
6329
|
WHERE ${clauses.join(' AND ')}
|
|
5783
6330
|
ORDER BY
|
|
5784
6331
|
CASE WHEN id = ? OR reference_number = ? OR local_reference_number = ? THEN 0 ELSE 1 END,
|
|
5785
|
-
created_at DESC
|
|
5786
|
-
|
|
5787
|
-
|
|
6332
|
+
created_at DESC,
|
|
6333
|
+
id DESC
|
|
6334
|
+
LIMIT ? OFFSET ?
|
|
6335
|
+
`).all(...params, normalizedQuery.raw, exactReferenceNumber || -1, exactLocalReferenceNumber || -1, limit, offset);
|
|
5788
6336
|
return rows.map((row) => {
|
|
5789
6337
|
const referenceNumber = this.sanitizeTaskReferenceNumber(row.reference_number);
|
|
5790
6338
|
const localReferenceNumber = this.sanitizeTaskReferenceNumber(row.local_reference_number);
|
|
@@ -5798,6 +6346,7 @@ export class TaskforceCore {
|
|
|
5798
6346
|
assignee: String(row.assignee || '').trim() || null,
|
|
5799
6347
|
category: String(row.category || ''),
|
|
5800
6348
|
priority: this.normalizePriorityValue(row.priority),
|
|
6349
|
+
isArchived: row.is_archived === 1 || row.is_archived === true,
|
|
5801
6350
|
};
|
|
5802
6351
|
});
|
|
5803
6352
|
}
|
|
@@ -6433,7 +6982,7 @@ export class TaskforceCore {
|
|
|
6433
6982
|
OR LOWER(COALESCE(u.system_role, 'user')) LIKE ?
|
|
6434
6983
|
)`
|
|
6435
6984
|
: '';
|
|
6436
|
-
const params = [this.tenantId];
|
|
6985
|
+
const params = [INTERNAL_SUBSCRIPTION_MODEL_FEATURE_KEY, this.tenantId];
|
|
6437
6986
|
if (query) {
|
|
6438
6987
|
params.push(queryPattern, queryPattern, queryPattern, queryPattern, queryPattern, queryPattern, queryPattern);
|
|
6439
6988
|
}
|
|
@@ -6446,6 +6995,14 @@ export class TaskforceCore {
|
|
|
6446
6995
|
COALESCE(u.system_role, 'user') AS system_role,
|
|
6447
6996
|
COALESCE(u.beta_access, 1) AS beta_access,
|
|
6448
6997
|
COALESCE(u.is_disabled, 0) AS is_disabled,
|
|
6998
|
+
CASE WHEN EXISTS (
|
|
6999
|
+
SELECT 1
|
|
7000
|
+
FROM user_feature_grants g
|
|
7001
|
+
WHERE g.tenant_id = u.tenant_id
|
|
7002
|
+
AND g.user_id = u.id
|
|
7003
|
+
AND g.feature_key = ?
|
|
7004
|
+
AND g.enabled = 1
|
|
7005
|
+
) THEN 1 ELSE 0 END AS subscription_model_access,
|
|
6449
7006
|
u.created_at,
|
|
6450
7007
|
u.updated_at,
|
|
6451
7008
|
u.email_verified_at,
|
|
@@ -6477,6 +7034,9 @@ export class TaskforceCore {
|
|
|
6477
7034
|
systemRole: this.normalizeSystemRole(row.system_role, 'user'),
|
|
6478
7035
|
betaAccess: row.beta_access === undefined || row.beta_access === null ? true : Boolean(row.beta_access),
|
|
6479
7036
|
disabled: Boolean(row.is_disabled),
|
|
7037
|
+
internalFeatureGrants: Boolean(row.subscription_model_access)
|
|
7038
|
+
? [INTERNAL_SUBSCRIPTION_MODEL_FEATURE_KEY]
|
|
7039
|
+
: [],
|
|
6480
7040
|
planId: row.plan_id ? String(row.plan_id) : null,
|
|
6481
7041
|
planName: row.plan_name ? String(row.plan_name) : null,
|
|
6482
7042
|
planVersionId: row.plan_version_id ? String(row.plan_version_id) : null,
|
|
@@ -6550,6 +7110,53 @@ export class TaskforceCore {
|
|
|
6550
7110
|
`).run(enabled ? 1 : 0, new Date().toISOString(), this.tenantId, normalizedUserId);
|
|
6551
7111
|
return Number(result?.changes || 0) > 0;
|
|
6552
7112
|
}
|
|
7113
|
+
hasUserFeatureGrant(userId, featureKey) {
|
|
7114
|
+
const normalizedUserId = String(userId || '').trim();
|
|
7115
|
+
if (!normalizedUserId || !isSupportedInternalUserFeatureKey(featureKey))
|
|
7116
|
+
return false;
|
|
7117
|
+
const row = this.db.prepare(`
|
|
7118
|
+
SELECT enabled
|
|
7119
|
+
FROM user_feature_grants
|
|
7120
|
+
WHERE tenant_id = ? AND user_id = ? AND feature_key = ?
|
|
7121
|
+
LIMIT 1
|
|
7122
|
+
`).get(this.tenantId, normalizedUserId, featureKey);
|
|
7123
|
+
return Boolean(row?.enabled);
|
|
7124
|
+
}
|
|
7125
|
+
listUserFeatureGrants(userId) {
|
|
7126
|
+
const normalizedUserId = String(userId || '').trim();
|
|
7127
|
+
if (!normalizedUserId)
|
|
7128
|
+
return [];
|
|
7129
|
+
const rows = this.db.prepare(`
|
|
7130
|
+
SELECT feature_key
|
|
7131
|
+
FROM user_feature_grants
|
|
7132
|
+
WHERE tenant_id = ? AND user_id = ? AND enabled = 1
|
|
7133
|
+
ORDER BY feature_key
|
|
7134
|
+
`).all(this.tenantId, normalizedUserId);
|
|
7135
|
+
return rows
|
|
7136
|
+
.map((row) => row.feature_key)
|
|
7137
|
+
.filter(isSupportedInternalUserFeatureKey);
|
|
7138
|
+
}
|
|
7139
|
+
setUserFeatureGrant(userId, featureKey, enabled, updatedBy) {
|
|
7140
|
+
const normalizedUserId = String(userId || '').trim();
|
|
7141
|
+
if (!normalizedUserId || !isSupportedInternalUserFeatureKey(featureKey))
|
|
7142
|
+
return false;
|
|
7143
|
+
const user = this.db.prepare(`
|
|
7144
|
+
SELECT id FROM users WHERE tenant_id = ? AND id = ? LIMIT 1
|
|
7145
|
+
`).get(this.tenantId, normalizedUserId);
|
|
7146
|
+
if (!user?.id)
|
|
7147
|
+
return false;
|
|
7148
|
+
const now = new Date().toISOString();
|
|
7149
|
+
this.db.prepare(`
|
|
7150
|
+
INSERT INTO user_feature_grants (
|
|
7151
|
+
tenant_id, user_id, feature_key, enabled, created_at, updated_at, updated_by
|
|
7152
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
7153
|
+
ON CONFLICT (tenant_id, user_id, feature_key) DO UPDATE SET
|
|
7154
|
+
enabled = excluded.enabled,
|
|
7155
|
+
updated_at = excluded.updated_at,
|
|
7156
|
+
updated_by = excluded.updated_by
|
|
7157
|
+
`).run(this.tenantId, normalizedUserId, featureKey, enabled ? 1 : 0, now, now, String(updatedBy || '').trim() || null);
|
|
7158
|
+
return true;
|
|
7159
|
+
}
|
|
6553
7160
|
markUserEmailVerified(userId) {
|
|
6554
7161
|
const normalizedUserId = String(userId || '').trim();
|
|
6555
7162
|
if (!normalizedUserId)
|
|
@@ -6656,6 +7263,14 @@ export class TaskforceCore {
|
|
|
6656
7263
|
if (hasCommercialBillingState) {
|
|
6657
7264
|
throw new TaskforceRuleError('Cannot purge a user with active commercial billing state', 409, 'SYSTEM_USER_PURGE_BLOCKED_BILLING_STATE');
|
|
6658
7265
|
}
|
|
7266
|
+
const modelConnectionCount = Number(this.db.prepare(`
|
|
7267
|
+
SELECT COUNT(*) AS count
|
|
7268
|
+
FROM model_provider_connections
|
|
7269
|
+
WHERE tenant_id = ? AND owner_user_id = ?
|
|
7270
|
+
`).get(this.tenantId, targetUserId)?.count || 0);
|
|
7271
|
+
if (modelConnectionCount > 0) {
|
|
7272
|
+
throw new TaskforceRuleError('Disconnect and clean up the user model connections before purging this account', 409, 'SYSTEM_USER_PURGE_BLOCKED_MODEL_CONNECTIONS');
|
|
7273
|
+
}
|
|
6659
7274
|
const now = new Date().toISOString();
|
|
6660
7275
|
const runCount = (sql, ...params) => Number(this.db.prepare(sql).run(...params)?.changes || 0);
|
|
6661
7276
|
const workspaceIds = this.db.prepare(`
|
|
@@ -6755,6 +7370,11 @@ export class TaskforceCore {
|
|
|
6755
7370
|
WHERE tenant_id = ?
|
|
6756
7371
|
AND user_id = ?
|
|
6757
7372
|
`, this.tenantId, targetUserId);
|
|
7373
|
+
const deletedUserFeatureGrantCount = runCount(`
|
|
7374
|
+
DELETE FROM user_feature_grants
|
|
7375
|
+
WHERE tenant_id = ?
|
|
7376
|
+
AND user_id = ?
|
|
7377
|
+
`, this.tenantId, targetUserId);
|
|
6758
7378
|
const deletedUserBillingCount = runCount(`
|
|
6759
7379
|
DELETE FROM user_billing
|
|
6760
7380
|
WHERE tenant_id = ?
|
|
@@ -6782,6 +7402,7 @@ export class TaskforceCore {
|
|
|
6782
7402
|
deletedMcpOauthConnectorGrantCount,
|
|
6783
7403
|
deletedUiStateCount,
|
|
6784
7404
|
deletedUserPreferenceCount,
|
|
7405
|
+
deletedUserFeatureGrantCount,
|
|
6785
7406
|
deletedEntitlementCount,
|
|
6786
7407
|
deletedUserBillingCount,
|
|
6787
7408
|
deletedUserCount
|
|
@@ -7229,6 +7850,20 @@ export class TaskforceCore {
|
|
|
7229
7850
|
runTaskMetadataOwnershipTransaction: (input) => this.runTaskMetadataOwnershipTransaction(input),
|
|
7230
7851
|
};
|
|
7231
7852
|
}
|
|
7853
|
+
buildUnresolvedWorkspaceActor(raw) {
|
|
7854
|
+
const normalized = raw.toLowerCase();
|
|
7855
|
+
const unresolvedKind = (normalized.startsWith('ai-profile-') || this.isKnownAiModelName(normalized)) ? 'ai' : 'human';
|
|
7856
|
+
return {
|
|
7857
|
+
id: raw,
|
|
7858
|
+
kind: unresolvedKind,
|
|
7859
|
+
label: this.formatActorLabel(raw),
|
|
7860
|
+
username: this.normalizeAiProfileUsername(raw, raw),
|
|
7861
|
+
icon: unresolvedKind === 'ai' ? GENERIC_AI_ACTOR_ICON : 'User',
|
|
7862
|
+
color: unresolvedKind === 'ai' ? GENERIC_AI_ACTOR_COLOR : '#22c55e',
|
|
7863
|
+
isNamed: true,
|
|
7864
|
+
meta: { unresolved: true }
|
|
7865
|
+
};
|
|
7866
|
+
}
|
|
7232
7867
|
resolveWorkspaceActor(workspaceId, actorRef) {
|
|
7233
7868
|
const raw = String(actorRef || '').trim();
|
|
7234
7869
|
const normalized = raw.toLowerCase();
|
|
@@ -7258,17 +7893,81 @@ export class TaskforceCore {
|
|
|
7258
7893
|
if (workspaceUser) {
|
|
7259
7894
|
return this.workspaceUserToActor(workspaceUser);
|
|
7260
7895
|
}
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7896
|
+
return this.buildUnresolvedWorkspaceActor(raw);
|
|
7897
|
+
}
|
|
7898
|
+
resolveWorkspaceActors(workspaceId, actorRefs) {
|
|
7899
|
+
const rawActorRefs = [...new Set(actorRefs
|
|
7900
|
+
.map((actorRef) => String(actorRef || '').trim())
|
|
7901
|
+
.filter(Boolean))];
|
|
7902
|
+
const resolvedActors = new Map();
|
|
7903
|
+
const lookupActorRefs = [];
|
|
7904
|
+
for (const raw of rawActorRefs) {
|
|
7905
|
+
const normalized = raw.toLowerCase();
|
|
7906
|
+
if (normalized === 'unassigned' || normalized === 'none' || normalized === 'null') {
|
|
7907
|
+
resolvedActors.set(raw, null);
|
|
7908
|
+
}
|
|
7909
|
+
else if (this.isHumanActorReference(raw)) {
|
|
7910
|
+
resolvedActors.set(raw, this.buildHumanActorFallback());
|
|
7911
|
+
}
|
|
7912
|
+
else if (this.isGenericAiActorReference(raw)) {
|
|
7913
|
+
resolvedActors.set(raw, this.buildGenericAiActor());
|
|
7914
|
+
}
|
|
7915
|
+
else {
|
|
7916
|
+
lookupActorRefs.push(raw);
|
|
7917
|
+
}
|
|
7918
|
+
}
|
|
7919
|
+
if (lookupActorRefs.length === 0)
|
|
7920
|
+
return resolvedActors;
|
|
7921
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
7922
|
+
const aiProfileRows = this.db.prepare(`
|
|
7923
|
+
SELECT *
|
|
7924
|
+
FROM ai_profiles
|
|
7925
|
+
WHERE tenant_id = ?
|
|
7926
|
+
AND workspace_id = ?
|
|
7927
|
+
ORDER BY COALESCE(last_active_at, updated_at, created_at) DESC, id ASC
|
|
7928
|
+
`).all(this.tenantId, normalizedWorkspaceId);
|
|
7929
|
+
const aiProfileRowsById = new Map(aiProfileRows
|
|
7930
|
+
.map((row) => [String(row.id || '').trim(), row])
|
|
7931
|
+
.filter(([id]) => Boolean(id)));
|
|
7932
|
+
const aiProfileRowsByAlias = new Map();
|
|
7933
|
+
for (const row of aiProfileRows) {
|
|
7934
|
+
const aliases = new Set([String(row.name || '').trim().toLowerCase(), String(row.username || '').trim().toLowerCase()]);
|
|
7935
|
+
for (const alias of aliases) {
|
|
7936
|
+
if (!alias)
|
|
7937
|
+
continue;
|
|
7938
|
+
const matches = aiProfileRowsByAlias.get(alias) ?? [];
|
|
7939
|
+
matches.push(row);
|
|
7940
|
+
aiProfileRowsByAlias.set(alias, matches);
|
|
7941
|
+
}
|
|
7942
|
+
}
|
|
7943
|
+
const workspaceUsersById = new Map(this.listWorkspaceUsers(normalizedWorkspaceId)
|
|
7944
|
+
.map((user) => [user.userId, user]));
|
|
7945
|
+
for (const raw of lookupActorRefs) {
|
|
7946
|
+
const directAiProfileRow = aiProfileRowsById.get(raw);
|
|
7947
|
+
if (directAiProfileRow) {
|
|
7948
|
+
resolvedActors.set(raw, this.aiProfileToActor(this.aiProfileRowToRecord(directAiProfileRow)));
|
|
7949
|
+
continue;
|
|
7950
|
+
}
|
|
7951
|
+
const aliasMatches = aiProfileRowsByAlias.get(raw.toLowerCase()) ?? [];
|
|
7952
|
+
const aliasRowsById = new Map(aliasMatches
|
|
7953
|
+
.map((row) => [String(row.id || '').trim(), row])
|
|
7954
|
+
.filter(([id]) => Boolean(id)));
|
|
7955
|
+
const canonicalAliasIds = [...new Set(aliasMatches
|
|
7956
|
+
.map((row) => this.resolveCanonicalAiProfileId(aliasRowsById, String(row.id || '').trim()))
|
|
7957
|
+
.filter(Boolean))];
|
|
7958
|
+
if (canonicalAliasIds.length === 1) {
|
|
7959
|
+
const canonicalAiProfileRow = aiProfileRowsById.get(canonicalAliasIds[0]);
|
|
7960
|
+
if (canonicalAiProfileRow) {
|
|
7961
|
+
resolvedActors.set(raw, this.aiProfileToActor(this.aiProfileRowToRecord(canonicalAiProfileRow)));
|
|
7962
|
+
continue;
|
|
7963
|
+
}
|
|
7964
|
+
}
|
|
7965
|
+
const workspaceUser = workspaceUsersById.get(raw);
|
|
7966
|
+
resolvedActors.set(raw, workspaceUser
|
|
7967
|
+
? this.workspaceUserToActor(workspaceUser)
|
|
7968
|
+
: this.buildUnresolvedWorkspaceActor(raw));
|
|
7969
|
+
}
|
|
7970
|
+
return resolvedActors;
|
|
7272
7971
|
}
|
|
7273
7972
|
listAssignableActors(workspaceId) {
|
|
7274
7973
|
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
@@ -8142,6 +8841,9 @@ export class TaskforceCore {
|
|
|
8142
8841
|
regenerateMcpAccessToken(input) {
|
|
8143
8842
|
return this.mcpTokenService.regenerateMcpAccessToken(input);
|
|
8144
8843
|
}
|
|
8844
|
+
updateMcpAccessTokenToolProfile(input) {
|
|
8845
|
+
return this.mcpTokenService.updateMcpAccessTokenToolProfile(input);
|
|
8846
|
+
}
|
|
8145
8847
|
authenticateMcpAccessToken(token, workspaceId) {
|
|
8146
8848
|
return this.mcpTokenService.authenticateMcpAccessToken(token, workspaceId);
|
|
8147
8849
|
}
|
|
@@ -8169,6 +8871,9 @@ export class TaskforceCore {
|
|
|
8169
8871
|
revokeMcpOAuthConnectorGrant(input) {
|
|
8170
8872
|
return this.mcpTokenService.revokeMcpOAuthConnectorGrant(input);
|
|
8171
8873
|
}
|
|
8874
|
+
updateMcpOAuthConnectorGrantToolProfile(input) {
|
|
8875
|
+
return this.mcpTokenService.updateMcpOAuthConnectorGrantToolProfile(input);
|
|
8876
|
+
}
|
|
8172
8877
|
authenticateMcpOAuthAccessToken(token, workspaceId) {
|
|
8173
8878
|
return this.mcpTokenService.authenticateMcpOAuthAccessToken(token, workspaceId);
|
|
8174
8879
|
}
|
|
@@ -9716,9 +10421,11 @@ export class TaskforceCore {
|
|
|
9716
10421
|
return null;
|
|
9717
10422
|
const patchFields = Object.keys(patch);
|
|
9718
10423
|
const archivedNestedContentWrite = options?.allowArchivedNestedContentSyncWrite === true
|
|
9719
|
-
&& patchFields.some((field) => [
|
|
10424
|
+
&& patchFields.some((field) => [
|
|
10425
|
+
'comments', 'attachments', 'checklistItems', 'cardCoverAssetId',
|
|
10426
|
+
].includes(field))
|
|
9720
10427
|
&& patchFields.every((field) => [
|
|
9721
|
-
'id', 'comments', 'attachments', 'checklistItems',
|
|
10428
|
+
'id', 'comments', 'attachments', 'checklistItems', 'cardCoverAssetId',
|
|
9722
10429
|
].includes(field));
|
|
9723
10430
|
if (existing.isArchived && !archivedNestedContentWrite) {
|
|
9724
10431
|
throw new TaskforceRuleError('Archived tasks are read-only. Unarchive first to make changes.', 409, 'TASK_ARCHIVED_READ_ONLY');
|
|
@@ -10050,8 +10757,25 @@ export class TaskforceCore {
|
|
|
10050
10757
|
return null;
|
|
10051
10758
|
const submittedAuthor = String(author || '').trim() || 'user';
|
|
10052
10759
|
const normalizedAuthor = this.normalizeCommentAuthor(author, normalizedWorkspaceId);
|
|
10760
|
+
const explicitCommentId = String(options?.commentId || '').trim();
|
|
10761
|
+
if (explicitCommentId) {
|
|
10762
|
+
const persisted = this.db.prepare(`
|
|
10763
|
+
SELECT task_id, text, author, submitted_author
|
|
10764
|
+
FROM comments
|
|
10765
|
+
WHERE tenant_id = ? AND workspace_id = ? AND id = ?
|
|
10766
|
+
`).get(this.tenantId, normalizedWorkspaceId, explicitCommentId);
|
|
10767
|
+
if (persisted) {
|
|
10768
|
+
if (persisted.task_id !== id
|
|
10769
|
+
|| persisted.text !== text
|
|
10770
|
+
|| persisted.author !== normalizedAuthor
|
|
10771
|
+
|| String(persisted.submitted_author || persisted.author) !== submittedAuthor) {
|
|
10772
|
+
throw new TaskforceRuleError('Task comment identity is assigned to different content.', 409, 'TASK_COMMENT_IDENTITY_CONFLICT');
|
|
10773
|
+
}
|
|
10774
|
+
return this.getTask(id, normalizedWorkspaceId);
|
|
10775
|
+
}
|
|
10776
|
+
}
|
|
10053
10777
|
const comment = {
|
|
10054
|
-
id: this.generateEntityId('comment'),
|
|
10778
|
+
id: explicitCommentId || this.generateEntityId('comment'),
|
|
10055
10779
|
author: normalizedAuthor,
|
|
10056
10780
|
submittedAuthor,
|
|
10057
10781
|
text,
|
|
@@ -10094,7 +10818,7 @@ export class TaskforceCore {
|
|
|
10094
10818
|
if (options?.persistBackup !== false)
|
|
10095
10819
|
this.persistBackup();
|
|
10096
10820
|
const updated = this.getTask(id, normalizedWorkspaceId);
|
|
10097
|
-
if (updated) {
|
|
10821
|
+
if (updated && options?.emitRealtime !== false) {
|
|
10098
10822
|
this.emitRealtimeMutation({
|
|
10099
10823
|
workspaceId: normalizedWorkspaceId,
|
|
10100
10824
|
entityType: 'task',
|