@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/mcp/runtime.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { Server } from
|
|
2
|
-
import { StdioServerTransport } from
|
|
3
|
-
import { CallToolRequestSchema, ListResourceTemplatesRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
1
|
+
import { Server } from '@modelcontextprotocol/server';
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
|
|
4
3
|
import * as fs from 'fs';
|
|
5
4
|
import * as path from 'path';
|
|
6
5
|
import { createHash, randomUUID } from 'node:crypto';
|
|
@@ -32,7 +31,8 @@ import { WorkspaceSyncContentVersionStore, } from '../sync/engine/workspaceSyncC
|
|
|
32
31
|
import { WorkspaceSyncPublishedContentResolver } from '../sync/engine/workspaceSyncPublishedContentResolver.js';
|
|
33
32
|
import { createWorkspaceSyncServerContentRuntime } from '../sync/engine/workspaceSyncServerContentRuntime.js';
|
|
34
33
|
import { resolveVirusScanConfigFromEnv, scanBufferForThreats } from '../security/virusScan.js';
|
|
35
|
-
import { ANNOTATED_ATTACHMENTS_WORKSPACE_FEATURE_KEY,
|
|
34
|
+
import { ANNOTATED_ATTACHMENTS_WORKSPACE_FEATURE_KEY, } from '../runtime/appGates.js';
|
|
35
|
+
import { resolveNodeAppGateAccess } from '../runtime/nodeAppGates.js';
|
|
36
36
|
import { getDocumentReferenceLabel, parseDocumentReference } from '../utils/documentReferences.js';
|
|
37
37
|
import { getImageReferenceLabel, parseImageReference } from '../utils/imageReferences.js';
|
|
38
38
|
import { inferCanonicalAssetKind, isValidCanonicalDocumentAsset } from '../utils/canonicalAssetKind.js';
|
|
@@ -79,7 +79,7 @@ import { createDurableTaskChecklistMutationRouter, DurableTaskChecklistRouterInP
|
|
|
79
79
|
import { DurableTaskChecklistMutationError } from '../sync/v3/taskChecklistMutationService.js';
|
|
80
80
|
import { createDurableTaskAttachmentLinksMutationRouter, DurableTaskAttachmentLinksRouterInProgressError, } from '../sync/v3/durableTaskAttachmentLinksMutationRouter.js';
|
|
81
81
|
import { DurableTaskAttachmentLinksMutationError } from '../sync/v3/taskAttachmentLinksMutationService.js';
|
|
82
|
-
import { createDurableWorkflowRuntimeMutationRouter, resolveWorkflowRuntimeMutationCapability, } from '../sync/v3/durableWorkflowRuntimeMutationRouter.js';
|
|
82
|
+
import { areWorkflowRuntimeMutationsFenced, createDurableWorkflowRuntimeMutationRouter, resolveWorkflowRuntimeMutationCapability, } from '../sync/v3/durableWorkflowRuntimeMutationRouter.js';
|
|
83
83
|
import { serializeCanonicalPlanningAttachmentLinks } from '../sync/planningCanonicalFields.js';
|
|
84
84
|
import { buildCanonicalTaxonomyStateMutation, } from '../sync/v3/taxonomyStateMutation.js';
|
|
85
85
|
import { buildTaskDeepLink, buildTaskforceDeepLink, normalizeTaskforceApplicationBaseUrl, } from '../shared/taskDeepLinks.js';
|
|
@@ -746,6 +746,9 @@ function createOwnedTaskforceMcpCore(projectRoot, tenantId, localDataOwnerPermit
|
|
|
746
746
|
},
|
|
747
747
|
});
|
|
748
748
|
}
|
|
749
|
+
export function createStatelessTaskforceMcpServer(options = {}, factory = createTaskforceMcpServer) {
|
|
750
|
+
return factory({ ...options, resourceSubscriptionMode: 'disabled' });
|
|
751
|
+
}
|
|
749
752
|
export function createTaskforceMcpServer(options = {}) {
|
|
750
753
|
// In dev (tsx), this module lives in src/mcp/.
|
|
751
754
|
// In prod, it lives in dist/mcp/.
|
|
@@ -753,6 +756,9 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
753
756
|
const PROJECT_NAME = path.basename(PROJECT_ROOT);
|
|
754
757
|
const logStartup = options.logStartup !== false;
|
|
755
758
|
const runtimeMode = options.runtimeMode === 'cloud' ? 'cloud' : 'local';
|
|
759
|
+
const modernProtocol = options.protocolEra === 'modern';
|
|
760
|
+
const supportsLegacyResourceSubscriptions = !modernProtocol
|
|
761
|
+
&& options.resourceSubscriptionMode === 'legacy-long-lived';
|
|
756
762
|
const applicationBaseUrl = normalizeTaskforceApplicationBaseUrl(options.applicationBaseUrl)
|
|
757
763
|
|| (runtimeMode === 'cloud'
|
|
758
764
|
? resolveCloudApplicationBaseUrl()
|
|
@@ -835,8 +841,6 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
835
841
|
};
|
|
836
842
|
const operationIdentityScope = options.operationIdentityScope
|
|
837
843
|
? {
|
|
838
|
-
generation: String(options.operationIdentityScope.generation || '').trim(),
|
|
839
|
-
sessionId: String(options.operationIdentityScope.sessionId || '').trim(),
|
|
840
844
|
connectionInstance: String(options.operationIdentityScope.connectionInstance || '').trim(),
|
|
841
845
|
}
|
|
842
846
|
: null;
|
|
@@ -844,7 +848,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
844
848
|
throw new Error('Shared MCP operation identity scope is incomplete.');
|
|
845
849
|
}
|
|
846
850
|
const runtimeRequestNamespace = operationIdentityScope
|
|
847
|
-
? fingerprintWorkspaceSyncPayload({ kind: 'shared-mcp-
|
|
851
|
+
? fingerprintWorkspaceSyncPayload({ kind: 'shared-mcp-process', ...operationIdentityScope })
|
|
848
852
|
: runtimeMode === 'cloud'
|
|
849
853
|
? String(options.auth?.requestId || '').trim() || randomUUID()
|
|
850
854
|
: randomUUID();
|
|
@@ -1203,12 +1207,14 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
1203
1207
|
}, {
|
|
1204
1208
|
capabilities: {
|
|
1205
1209
|
resources: {
|
|
1206
|
-
subscribe: true,
|
|
1207
|
-
listChanged: true,
|
|
1210
|
+
...(supportsLegacyResourceSubscriptions ? { subscribe: true } : {}),
|
|
1211
|
+
...(!modernProtocol ? { listChanged: true } : {}),
|
|
1208
1212
|
},
|
|
1209
1213
|
tools: {},
|
|
1210
1214
|
},
|
|
1211
|
-
instructions:
|
|
1215
|
+
instructions: runtimeMode === 'cloud' && options.auth?.actorType === 'token'
|
|
1216
|
+
? `Cloud access-token clients must pass profileToken on every write; obtain it from resolve_profile and keep it private. Use T-, IN-, WS-, I-, and D- references. Link url as [reference](url). For tracked work, prefer start_task and finish_task.`
|
|
1217
|
+
: `Before writes, check get_current_profile; if unbound, use resolve_profile. Keep profileToken private. Use T-, IN-, WS-, I-, and D- references. Link url as [reference](url). For tracked work, prefer start_task and finish_task.`,
|
|
1212
1218
|
});
|
|
1213
1219
|
const TOOL_PERMISSIONS = MCP_TOOL_PERMISSIONS;
|
|
1214
1220
|
const RESOURCE_PERMISSIONS = {
|
|
@@ -1311,9 +1317,10 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
1311
1317
|
function buildBoundedTextContent(content, maxChars) {
|
|
1312
1318
|
const requestedMaxChars = Number(maxChars);
|
|
1313
1319
|
const hasRequestedMaxChars = maxChars !== undefined && maxChars !== null && maxChars !== '';
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1320
|
+
if (hasRequestedMaxChars && (!Number.isInteger(requestedMaxChars) || requestedMaxChars < 1)) {
|
|
1321
|
+
throw new McpPublicError('maxChars must be a positive integer');
|
|
1322
|
+
}
|
|
1323
|
+
const resolvedMaxChars = hasRequestedMaxChars ? requestedMaxChars : 200000;
|
|
1317
1324
|
const characters = Array.from(content);
|
|
1318
1325
|
const totalChars = characters.length;
|
|
1319
1326
|
const boundedContent = totalChars > resolvedMaxChars
|
|
@@ -1405,8 +1412,10 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
1405
1412
|
}
|
|
1406
1413
|
const requestedLimit = Number(options?.limit);
|
|
1407
1414
|
const limit = Number.isFinite(requestedLimit)
|
|
1408
|
-
? Math.max(1, Math.min(
|
|
1415
|
+
? Math.max(1, Math.min(51, Math.trunc(requestedLimit)))
|
|
1409
1416
|
: 10;
|
|
1417
|
+
const requestedOffset = Number(options?.offset);
|
|
1418
|
+
const offset = Number.isFinite(requestedOffset) ? Math.max(0, Math.trunc(requestedOffset)) : 0;
|
|
1410
1419
|
const normalize = (value) => typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
1411
1420
|
const tokenize = (value) => normalize(value).split(/[^a-z0-9]+/i).filter(Boolean);
|
|
1412
1421
|
const allImages = workspaceAssetStore.listAllByWorkspace(ACTIVE_WORKSPACE_ID, { kind: 'image' });
|
|
@@ -1450,8 +1459,10 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
1450
1459
|
return [{ asset, matchedOn: Array.from(matchedOn), score }];
|
|
1451
1460
|
});
|
|
1452
1461
|
return scoredMatches
|
|
1453
|
-
.sort((a, b) => b.score - a.score
|
|
1454
|
-
.
|
|
1462
|
+
.sort((a, b) => b.score - a.score
|
|
1463
|
+
|| String(b.asset.updatedAt || '').localeCompare(String(a.asset.updatedAt || ''))
|
|
1464
|
+
|| a.asset.assetId.localeCompare(b.asset.assetId))
|
|
1465
|
+
.slice(offset, offset + limit);
|
|
1455
1466
|
}
|
|
1456
1467
|
const searchDocumentAssets = documentHelpers.searchDocumentAssets;
|
|
1457
1468
|
function buildImageDescriptor(asset) {
|
|
@@ -1465,7 +1476,9 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
1465
1476
|
sizeBytes: typeof asset.sizeBytes === 'number' ? asset.sizeBytes : null,
|
|
1466
1477
|
...(runtimeMode === 'local' ? { storageKey: asset.storageKey ?? null } : {}),
|
|
1467
1478
|
updatedAt: asset.updatedAt ?? null,
|
|
1468
|
-
|
|
1479
|
+
...(runtimeMode === 'local'
|
|
1480
|
+
? { apiUrl: `/api/taskforce/context/${encodeURIComponent(String(asset.storageKey || '').trim())}` }
|
|
1481
|
+
: {}),
|
|
1469
1482
|
};
|
|
1470
1483
|
return withAttachmentUrl({ ...descriptor, kind: 'image' });
|
|
1471
1484
|
}
|
|
@@ -1518,6 +1531,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
1518
1531
|
|| `Image ${normalizedValue} not found in workspace ${ACTIVE_WORKSPACE_ID}. If you only know a keyword, filename, or rough title, call search_images first.`, 'MCP_NOT_FOUND', 404);
|
|
1519
1532
|
}
|
|
1520
1533
|
const auditActorType = options.auth?.actorType || (runtimeMode === 'cloud' ? 'user' : 'local');
|
|
1534
|
+
const requiresExplicitTokenAuthorship = runtimeMode === 'cloud' && auditActorType === 'token';
|
|
1521
1535
|
const auditActorUserId = String(options.auth?.userId || (runtimeMode === 'cloud' ? 'anonymous' : 'local-mcp')).trim() || 'anonymous';
|
|
1522
1536
|
const auditTokenId = options.auth?.tokenId ? String(options.auth.tokenId).trim() : null;
|
|
1523
1537
|
const configuredAiProfileId = options.auth?.aiProfileId ? String(options.auth.aiProfileId).trim() || null : null;
|
|
@@ -1591,7 +1605,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
1591
1605
|
const shouldBindOAuthGrant = auditActorType === 'oauth'
|
|
1592
1606
|
&& Boolean(auditTokenId)
|
|
1593
1607
|
&& configuredAiProfileId !== profileId;
|
|
1594
|
-
const shouldApplyClientContext = Boolean((configuredMcpClientId && configuredMcpClientId !== 'general')
|
|
1608
|
+
const shouldApplyClientContext = auditActorType !== 'token' && Boolean((configuredMcpClientId && configuredMcpClientId !== 'general')
|
|
1595
1609
|
|| configuredMcpConnectionId);
|
|
1596
1610
|
if (!shouldBindOAuthGrant && !shouldApplyClientContext)
|
|
1597
1611
|
return;
|
|
@@ -1755,6 +1769,10 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
1755
1769
|
return resolved;
|
|
1756
1770
|
}
|
|
1757
1771
|
function findTrustedBoundProfile(options) {
|
|
1772
|
+
// A generic access token proves workspace/tool authorization, not which AI
|
|
1773
|
+
// is acting. Never use its historical connection row as authorship proof.
|
|
1774
|
+
if (requiresExplicitTokenAuthorship)
|
|
1775
|
+
return null;
|
|
1758
1776
|
const grantProfile = findProfileForConfiguredGrant();
|
|
1759
1777
|
const connectionProfile = findProfileForConfiguredConnection(options);
|
|
1760
1778
|
if (grantProfile && connectionProfile && grantProfile.id !== connectionProfile.id) {
|
|
@@ -1771,6 +1789,19 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
1771
1789
|
}
|
|
1772
1790
|
return profile;
|
|
1773
1791
|
}
|
|
1792
|
+
function assertOAuthProfileClientCompatibility(profile) {
|
|
1793
|
+
if (auditActorType !== 'oauth' || !configuredMcpClientId)
|
|
1794
|
+
return;
|
|
1795
|
+
const connection = profile.providerMetadata?.connection;
|
|
1796
|
+
const storedClientId = connection && typeof connection === 'object' && !Array.isArray(connection)
|
|
1797
|
+
? parseMcpClientId(connection.clientId)
|
|
1798
|
+
: null;
|
|
1799
|
+
const inferredClientId = inferLegacyMcpClientIdFromProfile(profile);
|
|
1800
|
+
const profileClientId = storedClientId || inferredClientId;
|
|
1801
|
+
if (!profileClientId || profileClientId === configuredMcpClientId)
|
|
1802
|
+
return;
|
|
1803
|
+
throw new TaskforceRuleError('The supplied AI profile is not compatible with this OAuth client product.', 409, 'AI_PROFILE_SELECTION_INVALID', { profileId: profile.id, profileClientId, configuredClientId: configuredMcpClientId });
|
|
1804
|
+
}
|
|
1774
1805
|
function listSelectableOAuthProfiles(profileName) {
|
|
1775
1806
|
if (auditActorType !== 'oauth')
|
|
1776
1807
|
return [];
|
|
@@ -2197,42 +2228,38 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
2197
2228
|
return null;
|
|
2198
2229
|
return resolved;
|
|
2199
2230
|
}
|
|
2200
|
-
function resolveFinishTaskReviewer(task) {
|
|
2231
|
+
function resolveFinishTaskReviewer(task, reviewer) {
|
|
2232
|
+
if (reviewer !== undefined) {
|
|
2233
|
+
const resolvedReviewer = resolveRuntimeTaskAssigneeInput(reviewer);
|
|
2234
|
+
const actor = resolvedReviewer
|
|
2235
|
+
? core.resolveWorkspaceActor(ACTIVE_WORKSPACE_ID, resolvedReviewer)
|
|
2236
|
+
: null;
|
|
2237
|
+
const eligible = actor?.kind === 'ai'
|
|
2238
|
+
? core.listAiProfiles(ACTIVE_WORKSPACE_ID).some((profile) => profile.id === actor.id)
|
|
2239
|
+
: core.listWorkspaceUsers(ACTIVE_WORKSPACE_ID).some((user) => (user.userId === actor?.id
|
|
2240
|
+
&& String(user.status || '').trim().toLowerCase() === 'active'
|
|
2241
|
+
&& user.disabled !== true));
|
|
2242
|
+
if (!actor || actor.meta?.unresolved === true || !eligible) {
|
|
2243
|
+
throw new McpPublicError('reviewer must identify an active human or AI assignee in the current workspace. Call list_assignees and retry with an assignee ID.', 'MCP_REVIEWER_INVALID');
|
|
2244
|
+
}
|
|
2245
|
+
return { assignee: actor.id, source: 'explicit' };
|
|
2246
|
+
}
|
|
2201
2247
|
const existingAssignee = String(task?.assignee || '').trim();
|
|
2202
2248
|
if (existingAssignee) {
|
|
2203
2249
|
const existingActor = core.resolveWorkspaceActor(ACTIVE_WORKSPACE_ID, existingAssignee);
|
|
2204
|
-
|
|
2250
|
+
const eligibleExistingHuman = existingActor?.kind === 'human'
|
|
2251
|
+
&& existingActor.meta?.unresolved !== true
|
|
2252
|
+
&& core.listWorkspaceUsers(ACTIVE_WORKSPACE_ID).some((user) => (user.userId === existingActor.id
|
|
2253
|
+
&& String(user.status || '').trim().toLowerCase() === 'active'
|
|
2254
|
+
&& user.disabled !== true));
|
|
2255
|
+
if (eligibleExistingHuman) {
|
|
2205
2256
|
return {
|
|
2206
2257
|
assignee: existingActor.id,
|
|
2207
2258
|
source: 'existing_human_owner',
|
|
2208
2259
|
};
|
|
2209
2260
|
}
|
|
2210
2261
|
}
|
|
2211
|
-
|
|
2212
|
-
const currentUser = core.listWorkspaceUsers(ACTIVE_WORKSPACE_ID)
|
|
2213
|
-
.find((user) => user.userId === auditActorUserId
|
|
2214
|
-
&& String(user.status || '').trim().toLowerCase() === 'active'
|
|
2215
|
-
&& user.disabled !== true);
|
|
2216
|
-
if (currentUser) {
|
|
2217
|
-
return { assignee: currentUser.userId, source: 'current_user' };
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
const workspaceOwner = core.listWorkspaceUsers(ACTIVE_WORKSPACE_ID)
|
|
2221
|
-
.filter((user) => String(user.status || '').trim().toLowerCase() === 'active' && user.disabled !== true)
|
|
2222
|
-
.sort((left, right) => {
|
|
2223
|
-
const leftRank = left.role === 'owner' ? 0 : left.role === 'admin' ? 1 : 2;
|
|
2224
|
-
const rightRank = right.role === 'owner' ? 0 : right.role === 'admin' ? 1 : 2;
|
|
2225
|
-
if (leftRank !== rightRank)
|
|
2226
|
-
return leftRank - rightRank;
|
|
2227
|
-
return String(left.displayName || left.email || left.userId).localeCompare(String(right.displayName || right.email || right.userId), undefined, { sensitivity: 'base' });
|
|
2228
|
-
})[0];
|
|
2229
|
-
if (workspaceOwner && (workspaceOwner.role === 'owner' || workspaceOwner.role === 'admin')) {
|
|
2230
|
-
return {
|
|
2231
|
-
assignee: workspaceOwner.userId,
|
|
2232
|
-
source: 'workspace_owner',
|
|
2233
|
-
};
|
|
2234
|
-
}
|
|
2235
|
-
throw new McpPublicError('Could not resolve a human reviewer for review handoff. Resolve a current human user, set a human owner on the task, or ensure the workspace has an active owner/admin.', 'MCP_REVIEWER_REQUIRED');
|
|
2262
|
+
throw new McpPublicError('Review handoff requires an explicit reviewer when the task has no eligible existing human reviewer. Call list_assignees and retry finish_task with reviewer set to a human or AI assignee ID.', 'MCP_REVIEWER_REQUIRED');
|
|
2236
2263
|
}
|
|
2237
2264
|
function getActiveMcpActorRef() {
|
|
2238
2265
|
if (activeAiProfileId)
|
|
@@ -2273,13 +2300,19 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
2273
2300
|
}).task;
|
|
2274
2301
|
}
|
|
2275
2302
|
if (!useStatusBoundary) {
|
|
2276
|
-
return core.updateTask(input.task.id, input.updates, ACTIVE_WORKSPACE_ID, {
|
|
2303
|
+
return core.updateTask(input.task.id, input.updates, ACTIVE_WORKSPACE_ID, {
|
|
2304
|
+
actorRef: getActiveMcpActorRef(),
|
|
2305
|
+
...(input.deferNotifications ? { persistBackup: false, emitRealtime: false } : {}),
|
|
2306
|
+
});
|
|
2277
2307
|
}
|
|
2278
2308
|
const unsupported = Object.keys(input.updates).filter((field) => (!durableStatusControlFields.has(field) && !durableTaskMetadataFieldNames.has(field)));
|
|
2279
2309
|
if (unsupported.length > 0) {
|
|
2280
2310
|
throw new TaskforceRuleError(`Combined durable task status update contains unsupported fields: ${unsupported.sort().join(', ')}.`, 400, 'TASK_STATUS_INVALID_PAYLOAD');
|
|
2281
2311
|
}
|
|
2282
2312
|
const status = String(hasStatus ? input.updates.status : input.task.status || 'task').trim();
|
|
2313
|
+
if (status === 'review' && String(input.task.status || '').trim() !== 'review' && !input.reviewHandoff) {
|
|
2314
|
+
throw new McpPublicError('Moving a task to review requires finish_task with a nonblank evidence comment.', 'TASK_REVIEW_HANDOFF_REQUIRED', 400);
|
|
2315
|
+
}
|
|
2283
2316
|
const metadataPatch = selectDurableTaskMetadataPatch(input.updates);
|
|
2284
2317
|
delete metadataPatch.assignee;
|
|
2285
2318
|
const assigneeIntent = input.assigneeIntent || (Object.prototype.hasOwnProperty.call(input.updates, 'assignee')
|
|
@@ -2294,10 +2327,73 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
2294
2327
|
: null;
|
|
2295
2328
|
return durableTaskMutationRouter.setStatus({
|
|
2296
2329
|
taskId: input.task.id,
|
|
2297
|
-
payload: {
|
|
2330
|
+
payload: {
|
|
2331
|
+
status,
|
|
2332
|
+
assigneeIntent,
|
|
2333
|
+
canceledReason,
|
|
2334
|
+
metadataPatch,
|
|
2335
|
+
...(input.reviewHandoff ? { reviewHandoff: input.reviewHandoff } : {}),
|
|
2336
|
+
},
|
|
2298
2337
|
actorRef: getActiveMcpActorRef(),
|
|
2299
2338
|
}).task;
|
|
2300
2339
|
}
|
|
2340
|
+
function mutateTaskReviewHandoffThroughBoundary(input) {
|
|
2341
|
+
const validation = validateStructuredCommentForAdd(input.handoffComment);
|
|
2342
|
+
if (!validation.ok)
|
|
2343
|
+
throw new McpPublicError(validation.message);
|
|
2344
|
+
const actorRef = getActiveMcpActorRef();
|
|
2345
|
+
const operationId = requireStableMcpRequestId('operation', `review-handoff-comment:${input.task.id}`);
|
|
2346
|
+
const commentId = requireStableMcpRequestId('comment', `review-handoff:${input.task.id}`);
|
|
2347
|
+
const comment = {
|
|
2348
|
+
id: commentId,
|
|
2349
|
+
author: actorRef,
|
|
2350
|
+
submittedAuthor: actorRef,
|
|
2351
|
+
text: input.handoffComment,
|
|
2352
|
+
};
|
|
2353
|
+
if (durableTaskMutationRouter.enabled) {
|
|
2354
|
+
return mutateTaskThroughStatusBoundary({
|
|
2355
|
+
task: input.task,
|
|
2356
|
+
updates: input.updates,
|
|
2357
|
+
assigneeIntent: input.assigneeIntent,
|
|
2358
|
+
reviewHandoff: { comment },
|
|
2359
|
+
});
|
|
2360
|
+
}
|
|
2361
|
+
let updated;
|
|
2362
|
+
try {
|
|
2363
|
+
updated = core.getDatabaseAdapter().transaction(() => {
|
|
2364
|
+
mutateTaskThroughStatusBoundary({
|
|
2365
|
+
task: input.task,
|
|
2366
|
+
updates: input.updates,
|
|
2367
|
+
assigneeIntent: input.assigneeIntent,
|
|
2368
|
+
deferNotifications: true,
|
|
2369
|
+
});
|
|
2370
|
+
const durableRouter = getDurableTaskCommentMutationRouter(actorRef, operationId);
|
|
2371
|
+
const task = durableRouter.enabled
|
|
2372
|
+
? durableRouter.append({
|
|
2373
|
+
taskId: input.task.id,
|
|
2374
|
+
comment: { ...comment, timestamp: new Date().toISOString() },
|
|
2375
|
+
}).task
|
|
2376
|
+
: core.addComment(input.task.id, input.handoffComment, actorRef, ACTIVE_WORKSPACE_ID, {
|
|
2377
|
+
actorRef,
|
|
2378
|
+
persistBackup: false,
|
|
2379
|
+
emitRealtime: false,
|
|
2380
|
+
commentId,
|
|
2381
|
+
});
|
|
2382
|
+
if (!task)
|
|
2383
|
+
throw new Error(`Task ${input.task.id} not found`);
|
|
2384
|
+
return task;
|
|
2385
|
+
})();
|
|
2386
|
+
}
|
|
2387
|
+
catch (error) {
|
|
2388
|
+
try {
|
|
2389
|
+
core.notifyDurableTaskMetadataCommitted(ACTIVE_WORKSPACE_ID, input.task.id);
|
|
2390
|
+
}
|
|
2391
|
+
catch { /* Preserve the original handoff failure. */ }
|
|
2392
|
+
throw error;
|
|
2393
|
+
}
|
|
2394
|
+
core.notifyDurableTaskMetadataCommitted(ACTIVE_WORKSPACE_ID, input.task.id);
|
|
2395
|
+
return updated;
|
|
2396
|
+
}
|
|
2301
2397
|
function mutateTaskLifecycleThroughBoundary(input) {
|
|
2302
2398
|
if (!durableTaskMutationRouter.enabled)
|
|
2303
2399
|
return input.legacy();
|
|
@@ -2383,6 +2479,8 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
2383
2479
|
function ensureResolvedProfileFromConnection(defaults) {
|
|
2384
2480
|
if (activeAiProfileId)
|
|
2385
2481
|
return;
|
|
2482
|
+
if (requiresExplicitTokenAuthorship)
|
|
2483
|
+
return;
|
|
2386
2484
|
const boundProfile = findTrustedBoundProfile({ repairConnection: false });
|
|
2387
2485
|
if (boundProfile) {
|
|
2388
2486
|
if (configuredAiProfileIdentityAsserted) {
|
|
@@ -2457,6 +2555,25 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
2457
2555
|
throw new TaskforceRuleError('Writes are blocked because this MCP session requested an identity that conflicts with its OAuth-bound profile. Reconnect with the intended profile and retry resolve_profile.', 409, 'AI_PROFILE_BINDING_CONFLICT', { reason: 'session_identity_quarantined', nextTool: 'resolve_profile' });
|
|
2458
2556
|
}
|
|
2459
2557
|
const profileToken = resolveProfileTokenFromArgs(args);
|
|
2558
|
+
if (requiresExplicitTokenAuthorship) {
|
|
2559
|
+
if (!profileToken) {
|
|
2560
|
+
throw new McpPublicError('Cloud access-token clients must pass profileToken on every mutating call. Call resolve_profile to obtain it, keep it private, and retry this write.', 'AI_PROFILE_PROOF_REQUIRED', 401);
|
|
2561
|
+
}
|
|
2562
|
+
const tokenProfile = core.getAiProfileByToken({
|
|
2563
|
+
workspaceId: ACTIVE_WORKSPACE_ID,
|
|
2564
|
+
profileToken,
|
|
2565
|
+
seatScope: configuredAiProfileSeatScope,
|
|
2566
|
+
});
|
|
2567
|
+
if (!tokenProfile) {
|
|
2568
|
+
throw new McpPublicError('Invalid AI profile token. Call resolve_profile again and retry.', 'INVALID_AI_PROFILE_TOKEN', 401);
|
|
2569
|
+
}
|
|
2570
|
+
if (activeAiProfileId && activeAiProfileId !== tokenProfile.id) {
|
|
2571
|
+
failAiProfileBindingConflict('The supplied profile token conflicts with the AI profile already active for this request.', { reason: 'request_token_mismatch', boundProfileId: activeAiProfileId, suppliedProfileId: tokenProfile.id });
|
|
2572
|
+
}
|
|
2573
|
+
if (!activeAiProfileId)
|
|
2574
|
+
activateAiProfile(tokenProfile.id, 'token_lookup');
|
|
2575
|
+
return;
|
|
2576
|
+
}
|
|
2460
2577
|
if (activeAiProfileId) {
|
|
2461
2578
|
if (!profileToken)
|
|
2462
2579
|
return;
|
|
@@ -2491,6 +2608,8 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
2491
2608
|
suppliedProfileId: profile.id,
|
|
2492
2609
|
});
|
|
2493
2610
|
}
|
|
2611
|
+
if (!trustedBoundProfile)
|
|
2612
|
+
assertOAuthProfileClientCompatibility(profile);
|
|
2494
2613
|
if (trustedBoundProfile && configuredAiProfileIdentityAsserted) {
|
|
2495
2614
|
assertRequestedIdentityMatchesBoundProfile(trustedBoundProfile, {
|
|
2496
2615
|
name: configuredAiProfileName,
|
|
@@ -2525,7 +2644,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
2525
2644
|
throw new McpPublicError('This tool requires an AI profile. Call resolve_profile first, then retry. If your MCP client is stateless, pass profileToken on mutating calls.', 'AI_PROFILE_REQUIRED');
|
|
2526
2645
|
}
|
|
2527
2646
|
function assertAnnotatedAttachmentReadAccess() {
|
|
2528
|
-
const gate =
|
|
2647
|
+
const gate = resolveNodeAppGateAccess(ANNOTATED_ATTACHMENTS_WORKSPACE_FEATURE_KEY);
|
|
2529
2648
|
if (!gate.allowed) {
|
|
2530
2649
|
throw new TaskforceRuleError('Annotated attachments are unavailable for this workspace.', 403, gate.code || 'ANNOTATED_ATTACHMENTS_UNAVAILABLE');
|
|
2531
2650
|
}
|
|
@@ -3340,6 +3459,17 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
3340
3459
|
offset: Number(normalizedOffset),
|
|
3341
3460
|
};
|
|
3342
3461
|
}
|
|
3462
|
+
function normalizeSearchReadPage(limit, offset, defaultLimit) {
|
|
3463
|
+
const normalizedLimit = limit === undefined ? defaultLimit : limit;
|
|
3464
|
+
const normalizedOffset = offset === undefined ? 0 : offset;
|
|
3465
|
+
if (!Number.isInteger(normalizedLimit) || Number(normalizedLimit) < 1 || Number(normalizedLimit) > 50) {
|
|
3466
|
+
throw new McpPublicError('limit must be an integer from 1 to 50');
|
|
3467
|
+
}
|
|
3468
|
+
if (!Number.isInteger(normalizedOffset) || Number(normalizedOffset) < 0) {
|
|
3469
|
+
throw new McpPublicError('offset must be a non-negative integer');
|
|
3470
|
+
}
|
|
3471
|
+
return { limit: Number(normalizedLimit), offset: Number(normalizedOffset) };
|
|
3472
|
+
}
|
|
3343
3473
|
function buildCompactPlanningHistory(targetType, entity) {
|
|
3344
3474
|
const duplicatedCommentAction = `${targetType}-comment-added`;
|
|
3345
3475
|
const activity = Array.isArray(entity?.activity) ? entity.activity : [];
|
|
@@ -3516,17 +3646,26 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
3516
3646
|
workstreamsByInitiativeId.set(normalizedInitiativeId, current);
|
|
3517
3647
|
}
|
|
3518
3648
|
const taskCountsByWorkstreamId = buildHistoricalTaskCountSnapshot();
|
|
3519
|
-
const
|
|
3520
|
-
.filter((initiative) => matchesInitiativePlanningQuery(initiative, query, workstreamsByInitiativeId.get(initiative.id) || []))
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3649
|
+
const matches = core.listInitiatives(ACTIVE_WORKSPACE_ID)
|
|
3650
|
+
.filter((initiative) => matchesInitiativePlanningQuery(initiative, query, workstreamsByInitiativeId.get(initiative.id) || []));
|
|
3651
|
+
const offset = options?.offset ?? 0;
|
|
3652
|
+
const limit = options?.limit ?? 25;
|
|
3653
|
+
const initiatives = matches.slice(offset, offset + limit).map((initiative) => {
|
|
3654
|
+
const compact = buildInitiativeResourcePayload(initiative, {
|
|
3655
|
+
workstreamsByInitiativeId,
|
|
3656
|
+
taskCountsByWorkstreamId,
|
|
3657
|
+
detail: 'compact',
|
|
3658
|
+
});
|
|
3659
|
+
const { nextDetailOption: _nextDetailOption, ...summary } = compact;
|
|
3660
|
+
return summary;
|
|
3661
|
+
});
|
|
3662
|
+
const pagination = buildPlanningPagination(matches.length, offset, limit, initiatives.length);
|
|
3526
3663
|
return {
|
|
3527
|
-
total:
|
|
3664
|
+
total: matches.length,
|
|
3528
3665
|
...(query ? { query } : {}),
|
|
3529
3666
|
initiatives,
|
|
3667
|
+
pagination,
|
|
3668
|
+
guidance: 'Use get_initiative with the initiative ID or reference for details.',
|
|
3530
3669
|
};
|
|
3531
3670
|
}
|
|
3532
3671
|
function buildWorkstreamsResourcePayload(options) {
|
|
@@ -3536,13 +3675,22 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
3536
3675
|
initiativesById.set(initiative.id, initiative);
|
|
3537
3676
|
}
|
|
3538
3677
|
const taskCountsByWorkstreamId = buildHistoricalTaskCountSnapshot();
|
|
3539
|
-
const
|
|
3540
|
-
.filter((workstream) => matchesWorkstreamPlanningQuery(workstream, query, initiativesById.get(workstream.initiativeId || '')))
|
|
3541
|
-
|
|
3678
|
+
const matches = core.listWorkstreams(ACTIVE_WORKSPACE_ID)
|
|
3679
|
+
.filter((workstream) => matchesWorkstreamPlanningQuery(workstream, query, initiativesById.get(workstream.initiativeId || '')));
|
|
3680
|
+
const offset = options?.offset ?? 0;
|
|
3681
|
+
const limit = options?.limit ?? 25;
|
|
3682
|
+
const workstreams = matches.slice(offset, offset + limit).map((workstream) => {
|
|
3683
|
+
const compact = buildWorkstreamResourcePayload(workstream, { taskCountsByWorkstreamId, detail: 'compact' });
|
|
3684
|
+
const { nextDetailOption: _nextDetailOption, nextTaskTool: _nextTaskTool, suggestedTaskArgs: _suggestedTaskArgs, ...summary } = compact;
|
|
3685
|
+
return summary;
|
|
3686
|
+
});
|
|
3687
|
+
const pagination = buildPlanningPagination(matches.length, offset, limit, workstreams.length);
|
|
3542
3688
|
return {
|
|
3543
|
-
total:
|
|
3689
|
+
total: matches.length,
|
|
3544
3690
|
...(query ? { query } : {}),
|
|
3545
3691
|
workstreams,
|
|
3692
|
+
pagination,
|
|
3693
|
+
guidance: 'Use get_workstream for details or list_tasks with workstreamId for active tasks.',
|
|
3546
3694
|
};
|
|
3547
3695
|
}
|
|
3548
3696
|
function buildPlanningWriteEntitySummary(targetType, entity) {
|
|
@@ -3626,7 +3774,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
3626
3774
|
const hydratedWorkstream = core.getWorkstream(workstream.id, ACTIVE_WORKSPACE_ID) || workstream;
|
|
3627
3775
|
return { id: hydratedWorkstream.id, workstream: hydratedWorkstream };
|
|
3628
3776
|
}
|
|
3629
|
-
server.setRequestHandler(
|
|
3777
|
+
server.setRequestHandler('resources/list', async () => {
|
|
3630
3778
|
const resources = [
|
|
3631
3779
|
{
|
|
3632
3780
|
uri: RESOURCE_CONFIG_URI,
|
|
@@ -3673,7 +3821,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
3673
3821
|
resources,
|
|
3674
3822
|
};
|
|
3675
3823
|
});
|
|
3676
|
-
server.setRequestHandler(
|
|
3824
|
+
server.setRequestHandler('resources/templates/list', async () => {
|
|
3677
3825
|
if (!hasPermission(RESOURCE_PERMISSIONS.task)) {
|
|
3678
3826
|
return { resourceTemplates: [] };
|
|
3679
3827
|
}
|
|
@@ -3705,7 +3853,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
3705
3853
|
],
|
|
3706
3854
|
};
|
|
3707
3855
|
});
|
|
3708
|
-
server.setRequestHandler(
|
|
3856
|
+
server.setRequestHandler('resources/read', async (request) => {
|
|
3709
3857
|
const uri = request.params.uri;
|
|
3710
3858
|
assertResourcePermission(uri);
|
|
3711
3859
|
if (uri === RESOURCE_CONFIG_URI) {
|
|
@@ -3811,24 +3959,26 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
3811
3959
|
}
|
|
3812
3960
|
throw new Error(`Unknown resource URI: ${uri}`);
|
|
3813
3961
|
});
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3962
|
+
if (supportsLegacyResourceSubscriptions) {
|
|
3963
|
+
server.setRequestHandler('resources/subscribe', async (request) => {
|
|
3964
|
+
const uri = request.params.uri;
|
|
3965
|
+
if (!isSupportedResourceUri(uri)) {
|
|
3966
|
+
throw new Error(`Unsupported resource URI for subscribe: ${uri}`);
|
|
3967
|
+
}
|
|
3968
|
+
assertResourcePermission(uri);
|
|
3969
|
+
resourceSubscriptions.add(uri);
|
|
3970
|
+
return {};
|
|
3971
|
+
});
|
|
3972
|
+
server.setRequestHandler('resources/unsubscribe', async (request) => {
|
|
3973
|
+
const uri = request.params.uri;
|
|
3974
|
+
if (!isSupportedResourceUri(uri)) {
|
|
3975
|
+
throw new Error(`Unsupported resource URI for unsubscribe: ${uri}`);
|
|
3976
|
+
}
|
|
3977
|
+
assertResourcePermission(uri);
|
|
3978
|
+
resourceSubscriptions.delete(uri);
|
|
3979
|
+
return {};
|
|
3980
|
+
});
|
|
3981
|
+
}
|
|
3832
3982
|
/**
|
|
3833
3983
|
* Tool Definitions
|
|
3834
3984
|
*/
|
|
@@ -3843,7 +3993,9 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
3843
3993
|
registerAdminWorkspaceTools(registerTool, (name, args) => executeRegisteredTool(name, args));
|
|
3844
3994
|
const mutatingProfileTokenProperty = {
|
|
3845
3995
|
type: 'string',
|
|
3846
|
-
description:
|
|
3996
|
+
description: requiresExplicitTokenAuthorship
|
|
3997
|
+
? 'Required authorship proof for cloud access-token clients; obtain from resolve_profile.'
|
|
3998
|
+
: 'Optional profileToken from resolve_profile for stateless MCP clients.'
|
|
3847
3999
|
};
|
|
3848
4000
|
const nonDestructiveWriteTools = new Set([
|
|
3849
4001
|
'add_annotated_attachment_marker',
|
|
@@ -3876,6 +4028,9 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
3876
4028
|
get priorityDesc() {
|
|
3877
4029
|
return `Priority level: ${getActivePriorities().map((priority) => `${priority.value} (${priority.label})`).join(', ')}`;
|
|
3878
4030
|
},
|
|
4031
|
+
get priorityEnum() {
|
|
4032
|
+
return getActivePriorities().map((priority) => priority.value);
|
|
4033
|
+
},
|
|
3879
4034
|
get typeEnum() {
|
|
3880
4035
|
return getActiveTypes().map((type) => type.value);
|
|
3881
4036
|
},
|
|
@@ -3916,6 +4071,11 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
3916
4071
|
inputSchema: {
|
|
3917
4072
|
...descriptor.inputSchema,
|
|
3918
4073
|
properties,
|
|
4074
|
+
...(requiresExplicitTokenAuthorship
|
|
4075
|
+
&& descriptor.name !== 'resolve_profile'
|
|
4076
|
+
&& isMcpWriteToolName(descriptor.name)
|
|
4077
|
+
? { required: Array.from(new Set([...(descriptor.inputSchema.required || []), 'profileToken'])) }
|
|
4078
|
+
: {}),
|
|
3919
4079
|
additionalProperties: false,
|
|
3920
4080
|
},
|
|
3921
4081
|
annotations: {
|
|
@@ -3934,16 +4094,19 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
3934
4094
|
const tool = registeredTools.find((candidate) => candidate.name === name);
|
|
3935
4095
|
return tool ? materializeToolDescriptor(tool, buildToolDescriptorContext()) : null;
|
|
3936
4096
|
};
|
|
3937
|
-
|
|
4097
|
+
const listTools = async () => {
|
|
3938
4098
|
const context = buildToolDescriptorContext();
|
|
3939
4099
|
const tools = registeredTools
|
|
3940
4100
|
.map((tool) => materializeToolDescriptor(tool, context))
|
|
3941
4101
|
.filter((tool) => (isMcpToolVisibleInProfile(tool.name, activeToolProfile)
|
|
3942
4102
|
&& hasPermission(TOOL_PERMISSIONS[tool.name])));
|
|
3943
4103
|
return {
|
|
3944
|
-
|
|
4104
|
+
// Descriptors are validated by Taskforce's JSON Schema contract before
|
|
4105
|
+
// registration; v2 narrows the equivalent recursive JSON value type.
|
|
4106
|
+
tools: tools,
|
|
3945
4107
|
};
|
|
3946
|
-
}
|
|
4108
|
+
};
|
|
4109
|
+
server.setRequestHandler('tools/list', listTools);
|
|
3947
4110
|
const resolvePlanningAttachmentTarget = (targetType, inputId) => {
|
|
3948
4111
|
if (targetType === 'initiative') {
|
|
3949
4112
|
const { id, initiative } = resolveInitiativeByIdentifierOrThrow(inputId);
|
|
@@ -4863,7 +5026,9 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
4863
5026
|
const taskLifecycleCommandService = createTaskLifecycleCommandService({
|
|
4864
5027
|
formatTaskIdentifier,
|
|
4865
5028
|
resolveAssigneeLabel: (assignee) => core.resolveWorkspaceActor(ACTIVE_WORKSPACE_ID, assignee)?.label || assignee,
|
|
4866
|
-
setStatus: (input) =>
|
|
5029
|
+
setStatus: (input) => input.handoffComment
|
|
5030
|
+
? mutateTaskReviewHandoffThroughBoundary(input)
|
|
5031
|
+
: mutateTaskThroughStatusBoundary(input),
|
|
4867
5032
|
mutateLifecycle: (input) => mutateTaskLifecycleThroughBoundary({
|
|
4868
5033
|
taskId: input.taskId,
|
|
4869
5034
|
mutationKind: input.mutationKind,
|
|
@@ -4883,7 +5048,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
4883
5048
|
});
|
|
4884
5049
|
},
|
|
4885
5050
|
}),
|
|
4886
|
-
resolveReviewer: (task) => resolveFinishTaskReviewer(task),
|
|
5051
|
+
resolveReviewer: (task, reviewer) => resolveFinishTaskReviewer(task, reviewer),
|
|
4887
5052
|
});
|
|
4888
5053
|
const planningCommandPreparationService = new PlanningCommandPreparationService({
|
|
4889
5054
|
resolveOwner: resolvePlanningOwnerInput,
|
|
@@ -4896,12 +5061,17 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
4896
5061
|
listItems: (taskId) => core.listChecklistItems(taskId, ACTIVE_WORKSPACE_ID),
|
|
4897
5062
|
createId: (scope, key) => requireStableMcpRequestId(scope, key),
|
|
4898
5063
|
now: () => new Date().toISOString(),
|
|
4899
|
-
replace: ({ taskId, items, operationId }) => {
|
|
5064
|
+
replace: ({ taskId, items, operationId, expectedDetailRevision, requireDurableIdempotency, itemUpdateIntent, }) => {
|
|
4900
5065
|
const actorRef = getActiveMcpActorRef();
|
|
4901
5066
|
const durableRouter = getDurableTaskChecklistMutationRouter(actorRef, operationId);
|
|
5067
|
+
if (requireDurableIdempotency) {
|
|
5068
|
+
requireDurableExplicitIdempotency(durableRouter.enabled, 'task checklist');
|
|
5069
|
+
}
|
|
4902
5070
|
return durableRouter.enabled
|
|
4903
|
-
? durableRouter.replace({ taskId, items }).task
|
|
4904
|
-
:
|
|
5071
|
+
? durableRouter.replace({ taskId, items, expectedDetailRevision, itemUpdateIntent }).task
|
|
5072
|
+
: expectedDetailRevision
|
|
5073
|
+
? core.updateTaskIfDetailRevisionMatches(taskId, expectedDetailRevision, { checklistItems: items }, ACTIVE_WORKSPACE_ID, { actorRef })
|
|
5074
|
+
: core.updateTask(taskId, { checklistItems: items }, ACTIVE_WORKSPACE_ID, { actorRef });
|
|
4905
5075
|
},
|
|
4906
5076
|
});
|
|
4907
5077
|
const annotatedAttachmentCommandService = createAnnotatedAttachmentCommandService({
|
|
@@ -4924,7 +5094,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
4924
5094
|
}
|
|
4925
5095
|
function rethrowChecklistCommandError(error) {
|
|
4926
5096
|
if (error instanceof TaskChecklistCommandError) {
|
|
4927
|
-
if (error.code === 'TASK_NOT_FOUND') {
|
|
5097
|
+
if (error.code === 'TASK_NOT_FOUND' || error.code === 'TASK_CHECKLIST_ITEM_NOT_FOUND') {
|
|
4928
5098
|
throw new McpPublicError(error.message, 'MCP_NOT_FOUND', error.statusCode);
|
|
4929
5099
|
}
|
|
4930
5100
|
throw new McpPublicError(error.message);
|
|
@@ -4992,15 +5162,15 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
4992
5162
|
: resolveWorkstreamByIdentifierOrThrow(workstreamId).id;
|
|
4993
5163
|
const paginated = resolvedWorkstreamId !== undefined || offset !== undefined;
|
|
4994
5164
|
const parsedOffset = offset === undefined ? 0 : Number(offset);
|
|
4995
|
-
if (!Number.
|
|
4996
|
-
throw new McpPublicError('offset must be a non-negative
|
|
4997
|
-
const normalizedOffset =
|
|
5165
|
+
if (!Number.isInteger(parsedOffset) || parsedOffset < 0)
|
|
5166
|
+
throw new McpPublicError('offset must be a non-negative integer');
|
|
5167
|
+
const normalizedOffset = parsedOffset;
|
|
4998
5168
|
const parsedLimit = limit === undefined ? (paginated ? 50 : 0) : Number(limit);
|
|
4999
|
-
if (!Number.
|
|
5000
|
-
throw new McpPublicError('limit must be
|
|
5169
|
+
if (!Number.isInteger(parsedLimit) || parsedLimit < 0 || parsedLimit > 200)
|
|
5170
|
+
throw new McpPublicError('limit must be an integer from 1 to 200');
|
|
5001
5171
|
const normalizedLimit = paginated
|
|
5002
|
-
?
|
|
5003
|
-
:
|
|
5172
|
+
? (parsedLimit || 50)
|
|
5173
|
+
: parsedLimit;
|
|
5004
5174
|
const queriedTasks = core.listTasksSlim(ACTIVE_WORKSPACE_ID, {
|
|
5005
5175
|
category,
|
|
5006
5176
|
status,
|
|
@@ -5087,6 +5257,15 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
5087
5257
|
failAiProfileBindingConflict('The selected AI profile conflicts with the profile already bound to this MCP session or authenticated connection.', { reason: 'selected_profile_mismatch', boundProfileId: alreadyBoundProfile.id, selectedProfileId: explicitlySelectedProfile.id });
|
|
5088
5258
|
}
|
|
5089
5259
|
const boundProfile = alreadyBoundProfile || explicitlySelectedProfile;
|
|
5260
|
+
if (!boundProfile && suppliedProfileToken) {
|
|
5261
|
+
const suppliedProfile = core.getAiProfileByToken({
|
|
5262
|
+
workspaceId: ACTIVE_WORKSPACE_ID,
|
|
5263
|
+
profileToken: suppliedProfileToken,
|
|
5264
|
+
seatScope: configuredAiProfileSeatScope,
|
|
5265
|
+
});
|
|
5266
|
+
if (suppliedProfile)
|
|
5267
|
+
assertOAuthProfileClientCompatibility(suppliedProfile);
|
|
5268
|
+
}
|
|
5090
5269
|
if (boundProfile && suppliedProfileToken) {
|
|
5091
5270
|
const tokenProfile = core.getAiProfileByToken({
|
|
5092
5271
|
workspaceId: ACTIVE_WORKSPACE_ID,
|
|
@@ -5291,10 +5470,19 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
5291
5470
|
};
|
|
5292
5471
|
}
|
|
5293
5472
|
case "search_tasks": {
|
|
5294
|
-
const { query, scope = "all" } = args;
|
|
5295
|
-
const
|
|
5473
|
+
const { query, scope = "all", limit, offset } = args;
|
|
5474
|
+
const page = normalizeSearchReadPage(limit, offset, 25);
|
|
5475
|
+
const queried = core.searchTaskSummaries(query, scope, ACTIVE_WORKSPACE_ID, {
|
|
5476
|
+
limit: page.limit + 1,
|
|
5477
|
+
offset: page.offset,
|
|
5478
|
+
});
|
|
5479
|
+
const hasMore = queried.length > page.limit;
|
|
5480
|
+
const results = hasMore ? queried.slice(0, page.limit) : queried;
|
|
5296
5481
|
return {
|
|
5297
|
-
content: [{
|
|
5482
|
+
content: [{
|
|
5483
|
+
type: "text",
|
|
5484
|
+
text: `${renderTaskTable(results)}\n\nPage: offset ${page.offset}, limit ${page.limit}, returned ${results.length}, hasMore ${hasMore}${hasMore ? `, nextOffset ${page.offset + results.length}` : ''}`,
|
|
5485
|
+
}],
|
|
5298
5486
|
};
|
|
5299
5487
|
}
|
|
5300
5488
|
case "get_task": {
|
|
@@ -5347,6 +5535,9 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
5347
5535
|
requiresCommentEvidence: step.requiresCommentEvidence,
|
|
5348
5536
|
} : {}),
|
|
5349
5537
|
}));
|
|
5538
|
+
const currentStep = assignment.execution.status === 'running'
|
|
5539
|
+
? assignment.steps.find((step) => step.id === assignment.execution.currentStepId)
|
|
5540
|
+
: undefined;
|
|
5350
5541
|
return {
|
|
5351
5542
|
content: [{ type: 'text', text: JSON.stringify({
|
|
5352
5543
|
task: withTaskUrl({ id: task.id, referenceLabel: formatTaskIdentifier(task), title: task.title }),
|
|
@@ -5358,11 +5549,23 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
5358
5549
|
currentStepId: assignment.execution.currentStepId || null,
|
|
5359
5550
|
steps,
|
|
5360
5551
|
},
|
|
5552
|
+
completionCall: currentStep ? {
|
|
5553
|
+
tool: 'complete_workflow_step',
|
|
5554
|
+
arguments: {
|
|
5555
|
+
taskId,
|
|
5556
|
+
executionId: assignment.execution.id,
|
|
5557
|
+
stepId: currentStep.id,
|
|
5558
|
+
},
|
|
5559
|
+
} : null,
|
|
5361
5560
|
}, null, 2) }],
|
|
5362
5561
|
};
|
|
5363
5562
|
}
|
|
5364
5563
|
case "complete_workflow_step": {
|
|
5365
5564
|
const { taskId, executionId, stepId, evidenceCommentId } = args;
|
|
5565
|
+
const normalizedEvidenceCommentId = evidenceCommentId == null ? undefined : evidenceCommentId;
|
|
5566
|
+
if (areWorkflowRuntimeMutationsFenced()) {
|
|
5567
|
+
throw new McpPublicError('Workflow execution changes are temporarily paused.', 'WORKFLOW_RUNTIME_MUTATIONS_FENCED', 503);
|
|
5568
|
+
}
|
|
5366
5569
|
const { task } = resolveTaskByIdentifierOrThrow(taskId);
|
|
5367
5570
|
ensureResolvedProfileForSessionBootstrap();
|
|
5368
5571
|
const agentProfileId = String(activeAiProfileId || '').trim();
|
|
@@ -5375,7 +5578,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
5375
5578
|
throw new McpPublicError('The workflow execution does not belong to this task.', 'WORKFLOW_TASK_MISMATCH', 409);
|
|
5376
5579
|
}
|
|
5377
5580
|
const idempotencyKey = `agent-step-${createHash('sha256')
|
|
5378
|
-
.update(`${executionId}:${stepId}:${agentProfileId}:${String(
|
|
5581
|
+
.update(`${executionId}:${stepId}:${agentProfileId}:${String(normalizedEvidenceCommentId || '')}`)
|
|
5379
5582
|
.digest('hex')
|
|
5380
5583
|
.slice(0, 32)}`;
|
|
5381
5584
|
let result;
|
|
@@ -5397,7 +5600,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
5397
5600
|
}).completeStep({
|
|
5398
5601
|
executionId,
|
|
5399
5602
|
expectedStepId: stepId,
|
|
5400
|
-
evidenceCommentId,
|
|
5603
|
+
evidenceCommentId: normalizedEvidenceCommentId,
|
|
5401
5604
|
})
|
|
5402
5605
|
: executionService.completeCurrentStep({
|
|
5403
5606
|
workspaceId: ACTIVE_WORKSPACE_ID,
|
|
@@ -5405,7 +5608,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
5405
5608
|
actorId: agentProfileId,
|
|
5406
5609
|
actorType: 'agent',
|
|
5407
5610
|
expectedStepId: stepId,
|
|
5408
|
-
evidenceCommentId,
|
|
5611
|
+
evidenceCommentId: normalizedEvidenceCommentId,
|
|
5409
5612
|
idempotencyKey,
|
|
5410
5613
|
});
|
|
5411
5614
|
}
|
|
@@ -5498,9 +5701,10 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
5498
5701
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5499
5702
|
}
|
|
5500
5703
|
case "list_initiatives": {
|
|
5501
|
-
const { query } = args;
|
|
5704
|
+
const { query, limit, offset } = args;
|
|
5705
|
+
const page = normalizePlanningReadPage(limit, offset);
|
|
5502
5706
|
return {
|
|
5503
|
-
content: [{ type: "text", text: JSON.stringify(buildInitiativesResourcePayload({ query }), null, 2) }],
|
|
5707
|
+
content: [{ type: "text", text: JSON.stringify(buildInitiativesResourcePayload({ query, ...page }), null, 2) }],
|
|
5504
5708
|
};
|
|
5505
5709
|
}
|
|
5506
5710
|
case "get_initiative": {
|
|
@@ -5771,9 +5975,10 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
5771
5975
|
};
|
|
5772
5976
|
}
|
|
5773
5977
|
case "list_workstreams": {
|
|
5774
|
-
const { query } = args;
|
|
5978
|
+
const { query, limit, offset } = args;
|
|
5979
|
+
const page = normalizePlanningReadPage(limit, offset);
|
|
5775
5980
|
return {
|
|
5776
|
-
content: [{ type: "text", text: JSON.stringify(buildWorkstreamsResourcePayload({ query }), null, 2) }],
|
|
5981
|
+
content: [{ type: "text", text: JSON.stringify(buildWorkstreamsResourcePayload({ query, ...page }), null, 2) }],
|
|
5777
5982
|
};
|
|
5778
5983
|
}
|
|
5779
5984
|
case "get_workstream": {
|
|
@@ -6060,12 +6265,14 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
6060
6265
|
const { taskId } = args;
|
|
6061
6266
|
const { id: resolvedTaskId } = resolveTaskByIdentifierOrThrow(taskId);
|
|
6062
6267
|
const checklist = core.listChecklistItems(resolvedTaskId, ACTIVE_WORKSPACE_ID);
|
|
6268
|
+
const detailRevision = core.getTaskDetailRevision(resolvedTaskId, ACTIVE_WORKSPACE_ID);
|
|
6063
6269
|
return {
|
|
6064
6270
|
content: [{
|
|
6065
6271
|
type: "text",
|
|
6066
6272
|
text: JSON.stringify({
|
|
6067
6273
|
taskId: resolvedTaskId,
|
|
6068
6274
|
...(buildTaskUrl({ id: resolvedTaskId }) ? { url: buildTaskUrl({ id: resolvedTaskId }) } : {}),
|
|
6275
|
+
detailRevision,
|
|
6069
6276
|
items: checklist,
|
|
6070
6277
|
}, null, 2),
|
|
6071
6278
|
}],
|
|
@@ -6135,6 +6342,49 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
6135
6342
|
}],
|
|
6136
6343
|
};
|
|
6137
6344
|
}
|
|
6345
|
+
case "update_task_checklist_item": {
|
|
6346
|
+
const { taskId, itemId, text, done, order, expectedDetailRevision, idempotencyKey: rawIdempotencyKey, } = args;
|
|
6347
|
+
const idempotencyKey = normalizeExplicitIdempotencyKey(rawIdempotencyKey);
|
|
6348
|
+
const operationId = idempotencyKey
|
|
6349
|
+
? stableExplicitMutationId('operation', 'update_task_checklist_item', idempotencyKey)
|
|
6350
|
+
: requireStableMcpRequestId('operation', 'task-checklist-update');
|
|
6351
|
+
let result;
|
|
6352
|
+
try {
|
|
6353
|
+
result = taskChecklistCommandService.update({
|
|
6354
|
+
taskId,
|
|
6355
|
+
itemId,
|
|
6356
|
+
text,
|
|
6357
|
+
done,
|
|
6358
|
+
order,
|
|
6359
|
+
expectedDetailRevision,
|
|
6360
|
+
operationId,
|
|
6361
|
+
requireDurableIdempotency: Boolean(idempotencyKey),
|
|
6362
|
+
});
|
|
6363
|
+
}
|
|
6364
|
+
catch (error) {
|
|
6365
|
+
rethrowChecklistCommandError(error);
|
|
6366
|
+
}
|
|
6367
|
+
const detailRevision = core.getTaskDetailRevision(result.taskId, ACTIVE_WORKSPACE_ID);
|
|
6368
|
+
recordMcpWriteAudit('update_task_checklist_item', {
|
|
6369
|
+
taskId: result.taskId,
|
|
6370
|
+
itemId: result.item.id,
|
|
6371
|
+
});
|
|
6372
|
+
await notifyResourceMutation({
|
|
6373
|
+
includeTasks: true,
|
|
6374
|
+
taskIds: [result.taskId],
|
|
6375
|
+
});
|
|
6376
|
+
return {
|
|
6377
|
+
content: [{
|
|
6378
|
+
type: "text",
|
|
6379
|
+
text: JSON.stringify({
|
|
6380
|
+
taskId: result.taskId,
|
|
6381
|
+
...(buildTaskUrl({ id: result.taskId }) ? { url: buildTaskUrl({ id: result.taskId }) } : {}),
|
|
6382
|
+
detailRevision,
|
|
6383
|
+
item: result.item,
|
|
6384
|
+
}, null, 2),
|
|
6385
|
+
}],
|
|
6386
|
+
};
|
|
6387
|
+
}
|
|
6138
6388
|
case "get_task_attachments": {
|
|
6139
6389
|
const { id, taskId, attachmentId } = args;
|
|
6140
6390
|
const taskIdentifier = taskId ?? id;
|
|
@@ -6190,8 +6440,11 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
6190
6440
|
}
|
|
6191
6441
|
case "search_images": {
|
|
6192
6442
|
assertAnnotatedAttachmentReadAccess();
|
|
6193
|
-
const { query, limit } = args;
|
|
6194
|
-
const
|
|
6443
|
+
const { query, limit, offset } = args;
|
|
6444
|
+
const page = normalizeSearchReadPage(limit, offset, 10);
|
|
6445
|
+
const queried = searchImageAssets(query, { limit: page.limit + 1, offset: page.offset });
|
|
6446
|
+
const hasMore = queried.length > page.limit;
|
|
6447
|
+
const results = (hasMore ? queried.slice(0, page.limit) : queried).map(({ asset, matchedOn, score }) => ({
|
|
6195
6448
|
image: buildImageDescriptor(asset),
|
|
6196
6449
|
matchedOn,
|
|
6197
6450
|
score,
|
|
@@ -6207,6 +6460,13 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
6207
6460
|
workspaceId: ACTIVE_WORKSPACE_ID,
|
|
6208
6461
|
query: typeof query === 'string' ? query.trim() : '',
|
|
6209
6462
|
results,
|
|
6463
|
+
pagination: {
|
|
6464
|
+
offset: page.offset,
|
|
6465
|
+
limit: page.limit,
|
|
6466
|
+
returned: results.length,
|
|
6467
|
+
hasMore,
|
|
6468
|
+
...(hasMore ? { nextOffset: page.offset + results.length } : {}),
|
|
6469
|
+
},
|
|
6210
6470
|
guidance: results.length > 0
|
|
6211
6471
|
? 'After picking a match, call get_image with the suggestedArgs.imageId to inspect linked tasks and image notes.'
|
|
6212
6472
|
: 'No matching canonical images were found. Try an I-123 reference, title keyword, or filename fragment.',
|
|
@@ -6277,8 +6537,11 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
6277
6537
|
};
|
|
6278
6538
|
}
|
|
6279
6539
|
case "search_documents": {
|
|
6280
|
-
const { query, limit } = args;
|
|
6281
|
-
const
|
|
6540
|
+
const { query, limit, offset } = args;
|
|
6541
|
+
const page = normalizeSearchReadPage(limit, offset, 10);
|
|
6542
|
+
const queried = searchDocumentAssets(query, { limit: page.limit + 1, offset: page.offset });
|
|
6543
|
+
const hasMore = queried.length > page.limit;
|
|
6544
|
+
const results = (hasMore ? queried.slice(0, page.limit) : queried).map(({ asset, matchedOn, score }) => ({
|
|
6282
6545
|
document: buildDocumentDescriptor(asset),
|
|
6283
6546
|
matchedOn,
|
|
6284
6547
|
score,
|
|
@@ -6295,6 +6558,13 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
6295
6558
|
workspaceId: ACTIVE_WORKSPACE_ID,
|
|
6296
6559
|
query: typeof query === 'string' ? query.trim() : '',
|
|
6297
6560
|
results,
|
|
6561
|
+
pagination: {
|
|
6562
|
+
offset: page.offset,
|
|
6563
|
+
limit: page.limit,
|
|
6564
|
+
returned: results.length,
|
|
6565
|
+
hasMore,
|
|
6566
|
+
...(hasMore ? { nextOffset: page.offset + results.length } : {}),
|
|
6567
|
+
},
|
|
6298
6568
|
guidance: results.length > 0
|
|
6299
6569
|
? 'After picking a match, call get_document with the suggestedArgs.documentId. Pass includeContent=true when you need the body.'
|
|
6300
6570
|
: 'No matching canonical documents were found. Try a D-123 reference, document id, title keyword, or filename fragment.',
|
|
@@ -6848,14 +7118,40 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
6848
7118
|
};
|
|
6849
7119
|
}
|
|
6850
7120
|
case "review_document": {
|
|
6851
|
-
const { id, documentId, sessionId, title, comments, prompt } = args;
|
|
7121
|
+
const { id, documentId, sessionId, title, comments, prompt, parentCommentId, threadCommentId, threadStatus, } = args;
|
|
6852
7122
|
const asset = resolveDocumentAssetByIdentifierOrThrow(documentId ?? id);
|
|
6853
7123
|
const normalizedComments = Array.isArray(comments)
|
|
6854
7124
|
? comments
|
|
6855
7125
|
: (typeof prompt === 'string' && prompt.trim()
|
|
6856
7126
|
? [{ body: prompt.trim() }]
|
|
6857
7127
|
: []);
|
|
6858
|
-
|
|
7128
|
+
const normalizedSessionId = typeof sessionId === 'string' ? sessionId.trim() : '';
|
|
7129
|
+
const normalizedParentCommentId = typeof parentCommentId === 'string' ? parentCommentId.trim() : '';
|
|
7130
|
+
const normalizedThreadCommentId = typeof threadCommentId === 'string' ? threadCommentId.trim() : '';
|
|
7131
|
+
const hasThreadStatus = threadStatus !== undefined;
|
|
7132
|
+
const hasThreadUpdate = Boolean(normalizedThreadCommentId) || hasThreadStatus;
|
|
7133
|
+
const hasCommentsArgument = Object.prototype.hasOwnProperty.call(args, 'comments');
|
|
7134
|
+
const hasPromptArgument = Object.prototype.hasOwnProperty.call(args, 'prompt');
|
|
7135
|
+
const hasTitleArgument = Object.prototype.hasOwnProperty.call(args, 'title');
|
|
7136
|
+
if (hasThreadUpdate && (!normalizedThreadCommentId || (threadStatus !== 'open' && threadStatus !== 'resolved'))) {
|
|
7137
|
+
throw new McpPublicError('threadCommentId and threadStatus are required together');
|
|
7138
|
+
}
|
|
7139
|
+
if ((normalizedParentCommentId || hasThreadUpdate) && !normalizedSessionId) {
|
|
7140
|
+
throw new McpPublicError('sessionId is required for reply and thread-status updates');
|
|
7141
|
+
}
|
|
7142
|
+
if (hasThreadUpdate && (normalizedParentCommentId || hasCommentsArgument || hasPromptArgument || hasTitleArgument)) {
|
|
7143
|
+
throw new McpPublicError('Thread-status updates cannot be combined with comments, replies, or a session title');
|
|
7144
|
+
}
|
|
7145
|
+
if (normalizedParentCommentId && hasTitleArgument) {
|
|
7146
|
+
throw new McpPublicError('Replies cannot set a session title');
|
|
7147
|
+
}
|
|
7148
|
+
if (hasCommentsArgument && hasPromptArgument) {
|
|
7149
|
+
throw new McpPublicError('comments and prompt cannot be combined');
|
|
7150
|
+
}
|
|
7151
|
+
if (normalizedParentCommentId && normalizedComments.length !== 1) {
|
|
7152
|
+
throw new McpPublicError('Replies require exactly one comment or prompt');
|
|
7153
|
+
}
|
|
7154
|
+
if (!hasThreadUpdate && normalizedComments.length === 0) {
|
|
6859
7155
|
throw new McpPublicError('comments array is required');
|
|
6860
7156
|
}
|
|
6861
7157
|
for (const comment of normalizedComments) {
|
|
@@ -6867,13 +7163,115 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
6867
7163
|
throw new McpPublicError(anchorError);
|
|
6868
7164
|
}
|
|
6869
7165
|
}
|
|
7166
|
+
if (normalizedParentCommentId && normalizedComments[0]?.anchor !== undefined && normalizedComments[0]?.anchor !== null) {
|
|
7167
|
+
throw new McpPublicError('Replies cannot have anchors');
|
|
7168
|
+
}
|
|
7169
|
+
if (hasThreadUpdate) {
|
|
7170
|
+
const existingSession = core.getDocumentReviewSession({
|
|
7171
|
+
workspaceId: ACTIVE_WORKSPACE_ID,
|
|
7172
|
+
sessionId: normalizedSessionId,
|
|
7173
|
+
});
|
|
7174
|
+
if (!existingSession) {
|
|
7175
|
+
throw new McpPublicError(`Document review session ${normalizedSessionId} not found.`, 'MCP_NOT_FOUND', 404);
|
|
7176
|
+
}
|
|
7177
|
+
if (existingSession.assetId !== asset.assetId) {
|
|
7178
|
+
throw new McpPublicError(`Document review session ${normalizedSessionId} is not linked to document ${getDocumentReferenceLabel(asset) || asset.assetId}.`, 'MCP_ATTACHMENT_CONFLICT', 409);
|
|
7179
|
+
}
|
|
7180
|
+
const updatedSession = core.updateDocumentReviewThreadStatus({
|
|
7181
|
+
workspaceId: ACTIVE_WORKSPACE_ID,
|
|
7182
|
+
sessionId: normalizedSessionId,
|
|
7183
|
+
commentId: normalizedThreadCommentId,
|
|
7184
|
+
status: threadStatus,
|
|
7185
|
+
actorId: getActiveMcpActorRef(),
|
|
7186
|
+
});
|
|
7187
|
+
const updatedThread = updatedSession.comments.find((entry) => entry.id === normalizedThreadCommentId) || null;
|
|
7188
|
+
const sessionSummary = buildDocumentReviewSessionSummary(updatedSession);
|
|
7189
|
+
recordMcpWriteAudit('review_document', {
|
|
7190
|
+
documentAssetId: asset.assetId,
|
|
7191
|
+
sessionId: updatedSession.id,
|
|
7192
|
+
sessionAction: 'reused',
|
|
7193
|
+
threadCommentId: normalizedThreadCommentId,
|
|
7194
|
+
threadStatus,
|
|
7195
|
+
});
|
|
7196
|
+
return {
|
|
7197
|
+
content: [{
|
|
7198
|
+
type: 'text',
|
|
7199
|
+
text: JSON.stringify({
|
|
7200
|
+
document: {
|
|
7201
|
+
assetId: asset.assetId,
|
|
7202
|
+
referenceLabel: getDocumentReferenceLabel(asset),
|
|
7203
|
+
},
|
|
7204
|
+
sessionId: updatedSession.id,
|
|
7205
|
+
sessionAction: 'reused',
|
|
7206
|
+
postedCommentIds: [],
|
|
7207
|
+
commentCount: 0,
|
|
7208
|
+
thread: buildCompactDocumentReviewComment(updatedThread),
|
|
7209
|
+
unresolvedThreadCount: sessionSummary.unresolvedThreadCount,
|
|
7210
|
+
nextTool: 'get_document_reviews',
|
|
7211
|
+
suggestedArgs: {
|
|
7212
|
+
documentId: getDocumentReferenceLabel(asset) || asset.assetId,
|
|
7213
|
+
sessionId: updatedSession.id,
|
|
7214
|
+
},
|
|
7215
|
+
}, null, 2),
|
|
7216
|
+
}],
|
|
7217
|
+
};
|
|
7218
|
+
}
|
|
7219
|
+
if (normalizedParentCommentId) {
|
|
7220
|
+
const existingSession = core.getDocumentReviewSession({
|
|
7221
|
+
workspaceId: ACTIVE_WORKSPACE_ID,
|
|
7222
|
+
sessionId: normalizedSessionId,
|
|
7223
|
+
});
|
|
7224
|
+
if (!existingSession) {
|
|
7225
|
+
throw new McpPublicError(`Document review session ${normalizedSessionId} not found.`, 'MCP_NOT_FOUND', 404);
|
|
7226
|
+
}
|
|
7227
|
+
if (existingSession.assetId !== asset.assetId) {
|
|
7228
|
+
throw new McpPublicError(`Document review session ${normalizedSessionId} is not linked to document ${getDocumentReferenceLabel(asset) || asset.assetId}.`, 'MCP_ATTACHMENT_CONFLICT', 409);
|
|
7229
|
+
}
|
|
7230
|
+
const repliedSession = core.addDocumentReviewReply({
|
|
7231
|
+
workspaceId: ACTIVE_WORKSPACE_ID,
|
|
7232
|
+
sessionId: normalizedSessionId,
|
|
7233
|
+
parentCommentId: normalizedParentCommentId,
|
|
7234
|
+
body: normalizedComments[0].body,
|
|
7235
|
+
actorId: getActiveMcpActorRef(),
|
|
7236
|
+
});
|
|
7237
|
+
const postedReply = repliedSession.comments.at(-1) || null;
|
|
7238
|
+
recordMcpWriteAudit('review_document', {
|
|
7239
|
+
documentAssetId: asset.assetId,
|
|
7240
|
+
sessionId: repliedSession.id,
|
|
7241
|
+
sessionAction: 'reused',
|
|
7242
|
+
parentCommentId: normalizedParentCommentId,
|
|
7243
|
+
commentCount: 1,
|
|
7244
|
+
});
|
|
7245
|
+
return {
|
|
7246
|
+
content: [{
|
|
7247
|
+
type: 'text',
|
|
7248
|
+
text: JSON.stringify({
|
|
7249
|
+
document: {
|
|
7250
|
+
assetId: asset.assetId,
|
|
7251
|
+
referenceLabel: getDocumentReferenceLabel(asset),
|
|
7252
|
+
},
|
|
7253
|
+
sessionId: repliedSession.id,
|
|
7254
|
+
sessionAction: 'reused',
|
|
7255
|
+
postedCommentIds: postedReply ? [postedReply.id] : [],
|
|
7256
|
+
commentCount: postedReply ? 1 : 0,
|
|
7257
|
+
reply: buildCompactDocumentReviewComment(postedReply),
|
|
7258
|
+
nextTool: 'get_document_reviews',
|
|
7259
|
+
suggestedArgs: {
|
|
7260
|
+
documentId: getDocumentReferenceLabel(asset) || asset.assetId,
|
|
7261
|
+
sessionId: repliedSession.id,
|
|
7262
|
+
},
|
|
7263
|
+
}, null, 2),
|
|
7264
|
+
}],
|
|
7265
|
+
};
|
|
7266
|
+
}
|
|
6870
7267
|
let commandResult;
|
|
6871
7268
|
try {
|
|
6872
7269
|
commandResult = documentReviewCommandService.review({
|
|
6873
7270
|
workspaceId: ACTIVE_WORKSPACE_ID,
|
|
6874
7271
|
assetId: asset.assetId,
|
|
7272
|
+
documentVersion: asset.version,
|
|
6875
7273
|
documentLabel: getDocumentReferenceLabel(asset) || asset.assetId,
|
|
6876
|
-
sessionId:
|
|
7274
|
+
sessionId: normalizedSessionId || null,
|
|
6877
7275
|
title: typeof title === 'string' ? title : null,
|
|
6878
7276
|
comments: normalizedComments,
|
|
6879
7277
|
actorId: getActiveMcpActorRef(),
|
|
@@ -6899,6 +7297,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
6899
7297
|
},
|
|
6900
7298
|
sessionId: currentSession.id,
|
|
6901
7299
|
sessionAction,
|
|
7300
|
+
documentVersion: currentSession.documentVersion,
|
|
6902
7301
|
postedCommentIds,
|
|
6903
7302
|
commentCount: postedCommentIds.length,
|
|
6904
7303
|
nextTool: 'get_document_reviews',
|
|
@@ -7223,13 +7622,6 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
7223
7622
|
actorRef: getActiveMcpActorRef(),
|
|
7224
7623
|
provisionalLocalReference: shouldUseProvisionalTaskReferences(runtimeMode)
|
|
7225
7624
|
}), replayed: false };
|
|
7226
|
-
if ('operationId' in task) {
|
|
7227
|
-
options.onOperationAccepted?.({
|
|
7228
|
-
operationId: task.operationId,
|
|
7229
|
-
mutationKind: 'create',
|
|
7230
|
-
payloadFingerprint: fingerprintWorkspaceSyncPayload(prepared.durableTask || {}),
|
|
7231
|
-
});
|
|
7232
|
-
}
|
|
7233
7625
|
recordMcpWriteAudit('create_task', {
|
|
7234
7626
|
taskId: task.task?.id || null,
|
|
7235
7627
|
title: task.task?.title || null,
|
|
@@ -7348,6 +7740,9 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
7348
7740
|
const inputId = args.id;
|
|
7349
7741
|
const { id, task: existingTask } = resolveTaskByIdentifierOrThrow(inputId);
|
|
7350
7742
|
const prepared = taskRootCommandPreparationService.prepareUpdate(args);
|
|
7743
|
+
if (prepared.updates.status === 'review' && existingTask.status !== 'review') {
|
|
7744
|
+
throw new McpPublicError('update_task cannot move a task to review. Use finish_task with outcome=\'review\' and a nonblank evidence comment so the handoff commits atomically.', 'TASK_REVIEW_HANDOFF_REQUIRED', 400);
|
|
7745
|
+
}
|
|
7351
7746
|
const updated = mutateTaskThroughStatusBoundary({ task: existingTask, updates: prepared.updates });
|
|
7352
7747
|
if (!updated) {
|
|
7353
7748
|
throw new Error(`Task ${id} not found`);
|
|
@@ -7386,13 +7781,13 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
7386
7781
|
}
|
|
7387
7782
|
case "finish_task": {
|
|
7388
7783
|
try {
|
|
7389
|
-
const { id: inputId, outcome, comment } = args;
|
|
7784
|
+
const { id: inputId, outcome, comment, reviewer } = args;
|
|
7390
7785
|
const normalizedOutcome = String(outcome || '').trim().toLowerCase();
|
|
7391
7786
|
if (normalizedOutcome !== 'done' && normalizedOutcome !== 'review') {
|
|
7392
7787
|
throw new McpPublicError("outcome must be 'done' or 'review'");
|
|
7393
7788
|
}
|
|
7394
7789
|
const { id, task: existingTask } = resolveTaskByIdentifierOrThrow(inputId);
|
|
7395
|
-
const result = taskLifecycleCommandService.finish(existingTask, { outcome: normalizedOutcome, comment });
|
|
7790
|
+
const result = taskLifecycleCommandService.finish(existingTask, { outcome: normalizedOutcome, comment, reviewer });
|
|
7396
7791
|
recordMcpWriteAudit('finish_task', {
|
|
7397
7792
|
taskId: id,
|
|
7398
7793
|
outcome: normalizedOutcome,
|
|
@@ -7776,6 +8171,14 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
7776
8171
|
update.complexity = resolveNumberOption('complexity', update.complexity, COMPLEXITY_OPTIONS);
|
|
7777
8172
|
}
|
|
7778
8173
|
const preview = core.bulkUpdateFieldsPreview(resolvedList, ACTIVE_WORKSPACE_ID);
|
|
8174
|
+
for (const item of preview.toApply) {
|
|
8175
|
+
if (item.fields.status !== 'review')
|
|
8176
|
+
continue;
|
|
8177
|
+
const existingTask = core.getTask(item.id, ACTIVE_WORKSPACE_ID);
|
|
8178
|
+
if (existingTask?.status !== 'review') {
|
|
8179
|
+
throw new McpPublicError('bulk_update_fields cannot move a task to review. Use finish_task with outcome=\'review\' and a nonblank evidence comment.', 'TASK_REVIEW_HANDOFF_REQUIRED', 400);
|
|
8180
|
+
}
|
|
8181
|
+
}
|
|
7779
8182
|
const forceStatusBoundary = (durableTaskMutationRouter.enabled
|
|
7780
8183
|
&& preview.toApply.some((item) => Object.prototype.hasOwnProperty.call(item.fields, 'status')));
|
|
7781
8184
|
const durableBatch = forceStatusBoundary || durableTaskRootMutationRouter.metadataEnabled;
|
|
@@ -7899,8 +8302,8 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
7899
8302
|
};
|
|
7900
8303
|
const toolHandlers = new Map(registeredTools.map((tool) => [tool.name, tool.handler]));
|
|
7901
8304
|
const mcpInputValidator = createMcpInputValidator();
|
|
7902
|
-
|
|
7903
|
-
const { name, arguments: args } = resolveMcpToolCallCompatibility(
|
|
8305
|
+
const callTool = async (params, context = {}) => {
|
|
8306
|
+
const { name, arguments: args } = resolveMcpToolCallCompatibility(params.name, params.arguments);
|
|
7904
8307
|
const toolStartedAtMs = Date.now();
|
|
7905
8308
|
let preparationMs = 0;
|
|
7906
8309
|
let validationMs = 0;
|
|
@@ -7943,11 +8346,11 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
7943
8346
|
}
|
|
7944
8347
|
});
|
|
7945
8348
|
let result;
|
|
7946
|
-
if (
|
|
8349
|
+
if (context.requestId === undefined || context.requestId === null) {
|
|
7947
8350
|
result = await execute();
|
|
7948
8351
|
}
|
|
7949
8352
|
else {
|
|
7950
|
-
const transportSessionId = String(
|
|
8353
|
+
const transportSessionId = String(context.sessionId || '').trim();
|
|
7951
8354
|
const requestIdentity = operationIdentityScope
|
|
7952
8355
|
? fingerprintWorkspaceSyncPayload({
|
|
7953
8356
|
kind: 'shared-mcp-request',
|
|
@@ -7956,18 +8359,18 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
7956
8359
|
|| options.auth?.mcpConnectionId
|
|
7957
8360
|
|| options.auth?.mcpClientId
|
|
7958
8361
|
|| 'mcp',
|
|
7959
|
-
requestId: { type: typeof
|
|
8362
|
+
requestId: { type: typeof context.requestId, value: context.requestId },
|
|
7960
8363
|
tool: name,
|
|
7961
8364
|
})
|
|
7962
8365
|
: [
|
|
7963
8366
|
...(runtimeMode === 'local' || !transportSessionId ? [runtimeRequestNamespace] : []),
|
|
7964
8367
|
String(transportSessionId || options.auth?.mcpConnectionId || options.auth?.mcpClientId || 'mcp'),
|
|
7965
|
-
String(
|
|
8368
|
+
String(context.requestId),
|
|
7966
8369
|
name,
|
|
7967
8370
|
].join(':');
|
|
7968
8371
|
const logicalCloudCreateIdentity = [
|
|
7969
8372
|
String(transportSessionId || options.auth?.mcpConnectionId || options.auth?.mcpClientId || 'mcp'),
|
|
7970
|
-
String(
|
|
8373
|
+
String(context.requestId),
|
|
7971
8374
|
name,
|
|
7972
8375
|
].join(':');
|
|
7973
8376
|
result = await cloudCreateRequestIdentity.run(logicalCloudCreateIdentity, () => mcpRequestIdentity.run(requestIdentity, execute));
|
|
@@ -7986,14 +8389,14 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
7986
8389
|
responseBytes = measureToolResultBytes(result);
|
|
7987
8390
|
return result;
|
|
7988
8391
|
}
|
|
7989
|
-
const result = buildUnexpectedToolErrorResult(auditRequestId ||
|
|
8392
|
+
const result = buildUnexpectedToolErrorResult(auditRequestId || context.requestId);
|
|
7990
8393
|
responseBytes = measureToolResultBytes(result);
|
|
7991
8394
|
return result;
|
|
7992
8395
|
}
|
|
7993
8396
|
finally {
|
|
7994
8397
|
try {
|
|
7995
8398
|
options.onToolCallTiming?.({
|
|
7996
|
-
requestId: String(auditRequestId ||
|
|
8399
|
+
requestId: String(auditRequestId || context.requestId || '').trim() || null,
|
|
7997
8400
|
toolName: name,
|
|
7998
8401
|
isWrite: isMcpWriteToolName(name),
|
|
7999
8402
|
outcome: timingOutcome,
|
|
@@ -8009,7 +8412,11 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
8009
8412
|
// Observability must never change tool behavior.
|
|
8010
8413
|
}
|
|
8011
8414
|
}
|
|
8012
|
-
}
|
|
8415
|
+
};
|
|
8416
|
+
server.setRequestHandler('tools/call', (request, ctx) => callTool(request.params, {
|
|
8417
|
+
requestId: ctx.mcpReq.id,
|
|
8418
|
+
sessionId: ctx.sessionId,
|
|
8419
|
+
}));
|
|
8013
8420
|
return {
|
|
8014
8421
|
server,
|
|
8015
8422
|
core,
|
|
@@ -8019,6 +8426,16 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
8019
8426
|
tenantId,
|
|
8020
8427
|
version: MCP_SERVER_VERSION,
|
|
8021
8428
|
toolProfile: activeToolProfile,
|
|
8429
|
+
listTools,
|
|
8430
|
+
callTool,
|
|
8431
|
+
};
|
|
8432
|
+
}
|
|
8433
|
+
export async function connectMcpRuntimeToStdio(runtime, transport = new StdioServerTransport()) {
|
|
8434
|
+
await runtime.server.connect(transport);
|
|
8435
|
+
return {
|
|
8436
|
+
...runtime,
|
|
8437
|
+
transport,
|
|
8438
|
+
close: () => runtime.server.close(),
|
|
8022
8439
|
};
|
|
8023
8440
|
}
|
|
8024
8441
|
export async function startTaskforceMcpStdioServer(options = {}) {
|
|
@@ -8034,18 +8451,18 @@ export async function startTaskforceMcpStdioServer(options = {}) {
|
|
|
8034
8451
|
projectRoot,
|
|
8035
8452
|
tenantId,
|
|
8036
8453
|
core,
|
|
8454
|
+
resourceSubscriptionMode: options.resourceSubscriptionMode ?? 'legacy-long-lived',
|
|
8037
8455
|
auth: {
|
|
8038
8456
|
...envAuth,
|
|
8039
8457
|
...(options.auth || {}),
|
|
8040
8458
|
},
|
|
8041
8459
|
});
|
|
8042
|
-
const
|
|
8043
|
-
await runtime.server.connect(transport);
|
|
8460
|
+
const connected = await connectMcpRuntimeToStdio(runtime);
|
|
8044
8461
|
const close = createTaskforceMcpStdioClose({
|
|
8045
|
-
closeServer:
|
|
8462
|
+
closeServer: connected.close,
|
|
8046
8463
|
...(ownsCore ? { closeOwnedCore: () => core.close() } : {}),
|
|
8047
8464
|
});
|
|
8048
|
-
return { ...runtime, transport, close };
|
|
8465
|
+
return { ...runtime, transport: connected.transport, close };
|
|
8049
8466
|
}
|
|
8050
8467
|
catch (error) {
|
|
8051
8468
|
try {
|