@taskforcehq/taskforce 0.3.329 → 0.3.332

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/README.md +7 -2
  2. package/dist/Taskforce.module.css +290 -45
  3. package/dist/components/features/AiProfilesModule.js +3 -5
  4. package/dist/components/features/TaskSettings.js +70 -14
  5. package/dist/components/features/TaskforceAgentsModule.d.ts +1 -2
  6. package/dist/components/features/TaskforceAgentsModule.js +586 -116
  7. package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +2 -2
  8. package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.js +2 -2
  9. package/dist/components/features/agentConnections/AgentConnectionControl.d.ts +15 -0
  10. package/dist/components/features/agentConnections/AgentConnectionControl.js +125 -0
  11. package/dist/components/features/agentConnections/ModelConnectionsPanel.d.ts +18 -0
  12. package/dist/components/features/agentConnections/ModelConnectionsPanel.js +393 -0
  13. package/dist/components/features/agentConnections/modelConnectionsApi.d.ts +65 -0
  14. package/dist/components/features/agentConnections/modelConnectionsApi.js +129 -0
  15. package/dist/components/features/planning/PlanningModule.js +15 -3
  16. package/dist/components/features/planning/planningMetadata.d.ts +10 -0
  17. package/dist/components/features/planning/planningMetadata.js +8 -0
  18. package/dist/components/features/taskforceAgentAssignments/AgentBehaviorAssignments.js +15 -24
  19. package/dist/components/features/taskforceAgentEditorModel.d.ts +2 -1
  20. package/dist/components/features/taskforceAgentEditorModel.js +3 -1
  21. package/dist/components/features/workflowManager/workflowManagerApi.d.ts +1 -1
  22. package/dist/components/features/workflowManager/workflowManagerApi.js +3 -0
  23. package/dist/components/task/TaskWorkflowAssignmentField.js +68 -10
  24. package/dist/components/views/StandaloneLayout.js +62 -27
  25. package/dist/components/views/panels/PlanningDrawer.d.ts +2 -1
  26. package/dist/components/views/panels/PlanningDrawer.js +12 -8
  27. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +4 -3
  28. package/dist/components/views/standalone/modals/SyncStatusModal.js +33 -38
  29. package/dist/config/envSchema.js +55 -1
  30. package/dist/core/McpTokenService.d.ts +25 -1
  31. package/dist/core/McpTokenService.js +106 -15
  32. package/dist/core/TaskChecklistCommandService.d.ts +27 -0
  33. package/dist/core/TaskChecklistCommandService.js +83 -5
  34. package/dist/core/TaskLifecycleCommandService.d.ts +3 -1
  35. package/dist/core/TaskLifecycleCommandService.js +6 -2
  36. package/dist/core/Taskforce.d.ts +43 -3
  37. package/dist/core/Taskforce.js +158 -7
  38. package/dist/core/types.d.ts +9 -0
  39. package/dist/documentReviews/DocumentReviewCommentStore.d.ts +2 -0
  40. package/dist/documentReviews/DocumentReviewCommentStore.js +28 -9
  41. package/dist/documentReviews/commandService.d.ts +2 -1
  42. package/dist/documentReviews/commandService.js +6 -3
  43. package/dist/hooks/sync/orchestratorShared.d.ts +1 -0
  44. package/dist/hooks/sync/orchestratorShared.js +2 -0
  45. package/dist/hooks/sync/useLocalSyncCoordinatorSnapshot.d.ts +3 -0
  46. package/dist/hooks/sync/useLocalSyncCoordinatorSnapshot.js +85 -9
  47. package/dist/hooks/sync/useSyncStatusControls.d.ts +4 -1
  48. package/dist/hooks/sync/useSyncStatusControls.js +47 -11
  49. package/dist/hooks/useSyncOrchestrator.d.ts +3 -0
  50. package/dist/hooks/useSyncOrchestrator.js +111 -2
  51. package/dist/hooks/useTaskData.d.ts +31 -1
  52. package/dist/hooks/useTaskData.js +29 -7
  53. package/dist/hooks/useTaskMutations.d.ts +3 -1
  54. package/dist/hooks/useTaskMutations.js +46 -12
  55. package/dist/hooks/useTaskforce.d.ts +2 -0
  56. package/dist/hooks/useTaskforce.js +11 -2
  57. package/dist/mcp/clientRegistry.d.ts +2 -2
  58. package/dist/mcp/clientRegistry.js +1 -1
  59. package/dist/mcp/documentAssetRegistrar.js +9 -6
  60. package/dist/mcp/durableTaskMutationRouter.d.ts +4 -1
  61. package/dist/mcp/durableTaskMutationRouter.js +10 -0
  62. package/dist/mcp/durableTaskRelationshipMutationRouter.js +4 -0
  63. package/dist/mcp/httpProtocolRouting.d.ts +11 -0
  64. package/dist/mcp/httpProtocolRouting.js +22 -0
  65. package/dist/mcp/localCliHealth.d.ts +2 -2
  66. package/dist/mcp/localCliHealth.js +2 -2
  67. package/dist/mcp/localServiceProxy.d.ts +1 -0
  68. package/dist/mcp/localServiceProxy.js +530 -68
  69. package/dist/mcp/planningAttachmentCommandAdapter.d.ts +3 -1
  70. package/dist/mcp/planningAttachmentCommandAdapter.js +20 -2
  71. package/dist/mcp/runtime.d.ts +54 -79
  72. package/dist/mcp/runtime.js +408 -102
  73. package/dist/mcp/taskPlanningRegistrar.js +25 -6
  74. package/dist/mcp/toolProfiles.d.ts +2 -0
  75. package/dist/mcp/toolProfiles.js +11 -0
  76. package/dist/mcp/toolRegistry.d.ts +1 -0
  77. package/dist/mcp/toolRegistry.js +9 -8
  78. package/dist/mcp/workflowExecutionRegistrar.js +4 -4
  79. package/dist/migrations/taskSchemaMigrations.d.ts +5 -0
  80. package/dist/migrations/taskSchemaMigrations.js +415 -0
  81. package/dist/runtime/appGates.d.ts +2 -1
  82. package/dist/runtime/appGates.js +7 -0
  83. package/dist/server/buildInfo.d.ts +1 -0
  84. package/dist/server/buildInfo.js +5 -1
  85. package/dist/server/cloudMcpCapacityTelemetry.d.ts +68 -0
  86. package/dist/server/cloudMcpCapacityTelemetry.js +221 -0
  87. package/dist/server/index.d.ts +3 -0
  88. package/dist/server/index.js +108 -11
  89. package/dist/server/localDatabaseIdentity.js +4 -1
  90. package/dist/server/localServiceLease.js +2 -1
  91. package/dist/server/localServiceSessions.d.ts +14 -17
  92. package/dist/server/localServiceSessions.js +308 -94
  93. package/dist/server/localWorkspaceSyncServerRuntime.js +25 -19
  94. package/dist/server/routes/admin.d.ts +4 -0
  95. package/dist/server/routes/admin.js +126 -5
  96. package/dist/server/routes/agents.d.ts +7 -0
  97. package/dist/server/routes/agents.js +277 -24
  98. package/dist/server/routes/billing.js +5 -1
  99. package/dist/server/routes/durableTaskHttpRouters.d.ts +2 -0
  100. package/dist/server/routes/executorPhaseAApprovals.d.ts +19 -0
  101. package/dist/server/routes/executorPhaseAApprovals.js +140 -0
  102. package/dist/server/routes/executorPhaseAIssuerRedemptions.d.ts +11 -0
  103. package/dist/server/routes/executorPhaseAIssuerRedemptions.js +24 -0
  104. package/dist/server/routes/githubReviewEvidence.d.ts +19 -0
  105. package/dist/server/routes/githubReviewEvidence.js +116 -0
  106. package/dist/server/routes/localService.d.ts +15 -4
  107. package/dist/server/routes/localService.js +136 -40
  108. package/dist/server/routes/modelConnections.d.ts +24 -0
  109. package/dist/server/routes/modelConnections.js +205 -0
  110. package/dist/server/routes/shared.d.ts +22 -0
  111. package/dist/server/routes/shared.js +298 -182
  112. package/dist/server/routes/sync.js +1 -0
  113. package/dist/server/routes/syncAuxRoutes.js +5 -1
  114. package/dist/server/routes/syncPushRoutes.d.ts +1 -0
  115. package/dist/server/routes/syncPushRoutes.js +1 -0
  116. package/dist/server/routes/syncV3FeedRoutes.js +10 -1
  117. package/dist/server/routes/syncV3OperationRoutes.js +35 -9
  118. package/dist/server/routes/tasks.js +21 -2
  119. package/dist/server/routes/workflows.js +9 -1
  120. package/dist/server/routes.js +59 -0
  121. package/dist/service/localServiceCli.d.ts +2 -2
  122. package/dist/service/localServiceCli.js +4 -3
  123. package/dist/services/codexAppServerModelGateway.d.ts +54 -5
  124. package/dist/services/codexAppServerModelGateway.js +246 -21
  125. package/dist/services/codexDeviceCodeLoginManager.d.ts +39 -0
  126. package/dist/services/codexDeviceCodeLoginManager.js +265 -0
  127. package/dist/services/codexModelProviderConnectionLifecycle.d.ts +8 -0
  128. package/dist/services/codexModelProviderConnectionLifecycle.js +73 -0
  129. package/dist/services/executorPhaseAApprovalRuntime.d.ts +14 -0
  130. package/dist/services/executorPhaseAApprovalRuntime.js +24 -0
  131. package/dist/services/executorPhaseAApprovalService.d.ts +40 -0
  132. package/dist/services/executorPhaseAApprovalService.js +609 -0
  133. package/dist/services/executorPhaseADriftEvaluator.d.ts +78 -0
  134. package/dist/services/executorPhaseADriftEvaluator.js +93 -0
  135. package/dist/services/executorPhaseAIssuerCore.d.ts +139 -0
  136. package/dist/services/executorPhaseAIssuerCore.js +402 -0
  137. package/dist/services/executorPhaseAIssuerRedemptionService.d.ts +34 -0
  138. package/dist/services/executorPhaseAIssuerRedemptionService.js +158 -0
  139. package/dist/services/githubReviewEvidenceRuntime.d.ts +14 -0
  140. package/dist/services/githubReviewEvidenceRuntime.js +30 -0
  141. package/dist/services/githubReviewEvidenceService.d.ts +93 -0
  142. package/dist/services/githubReviewEvidenceService.js +336 -0
  143. package/dist/services/modelGateway.d.ts +7 -0
  144. package/dist/services/modelProviderConnectionService.d.ts +71 -0
  145. package/dist/services/modelProviderConnectionService.js +279 -0
  146. package/dist/services/modelProviderCredentialHome.d.ts +32 -0
  147. package/dist/services/modelProviderCredentialHome.js +271 -0
  148. package/dist/services/ownerScopedModelConnectionGateway.d.ts +12 -0
  149. package/dist/services/ownerScopedModelConnectionGateway.js +30 -0
  150. package/dist/services/taskforceAgentConnectionBindingService.d.ts +66 -0
  151. package/dist/services/taskforceAgentConnectionBindingService.js +185 -0
  152. package/dist/services/taskforceAgentMcpBridge.d.ts +2 -2
  153. package/dist/services/taskforceAgentMcpBridge.js +42 -29
  154. package/dist/shared/adminEdgeProxy.d.ts +5 -0
  155. package/dist/shared/adminEdgeProxy.js +94 -0
  156. package/dist/shared/adminProxySessionCookies.d.ts +16 -0
  157. package/dist/shared/adminProxySessionCookies.js +86 -0
  158. package/dist/shared/aiProfileColors.d.ts +31 -1
  159. package/dist/shared/aiProfileColors.js +14 -6
  160. package/dist/shared/executorPhaseAApprovalGoldenVector.json +269 -0
  161. package/dist/shared/executorPhaseAApprovalReceipt.d.ts +112 -0
  162. package/dist/shared/executorPhaseAApprovalReceipt.js +58 -0
  163. package/dist/shared/executorPhaseAApprovalTrustPolicy.json +10 -0
  164. package/dist/shared/executorPhaseADriftContracts.d.ts +37 -0
  165. package/dist/shared/executorPhaseADriftContracts.js +63 -0
  166. package/dist/shared/executorPhaseAIssuerContracts.d.ts +75 -0
  167. package/dist/shared/executorPhaseAIssuerContracts.js +90 -0
  168. package/dist/shared/executorPhaseARunAuthorization.d.ts +120 -0
  169. package/dist/shared/executorPhaseARunAuthorization.js +232 -0
  170. package/dist/shared/executorPhaseASchemaValidation.d.ts +7 -0
  171. package/dist/shared/executorPhaseASchemaValidation.js +53 -0
  172. package/dist/shared/executorPhaseASchemas/approval-receipt.schema.json +106 -0
  173. package/dist/shared/executorPhaseASchemas/drift-attestation.schema.json +54 -0
  174. package/dist/shared/executorPhaseASchemas/drift-request.schema.json +47 -0
  175. package/dist/shared/executorPhaseASchemas/foundation-manifest.schema.json +29 -0
  176. package/dist/shared/executorPhaseASchemas/issuer-audit-record.schema.json +24 -0
  177. package/dist/shared/executorPhaseASchemas/issuer-proof.schema.json +43 -0
  178. package/dist/shared/executorPhaseASchemas/issuer-redemption-request.schema.json +18 -0
  179. package/dist/shared/executorPhaseASchemas/issuer-redemption-response.schema.json +39 -0
  180. package/dist/shared/executorPhaseASchemas/logical-envelope.schema.json +149 -0
  181. package/dist/shared/executorPhaseASchemas/preflight-report.schema.json +216 -0
  182. package/dist/shared/executorPhaseASchemas/run-capability.schema.json +111 -0
  183. package/dist/shared/executorPhaseASchemas/sender-proof.schema.json +37 -0
  184. package/dist/shared/executorPhaseASchemas/workload-grant.schema.json +68 -0
  185. package/dist/shared/executorPhaseASchemas/workload-jwt-claims.schema.json +67 -0
  186. package/dist/shared/executorPhaseATiming.d.ts +1 -0
  187. package/dist/shared/executorPhaseATiming.js +1 -0
  188. package/dist/shared/planningIdentity.js +1 -1
  189. package/dist/shared/productLogoKeys.d.ts +2 -0
  190. package/dist/shared/productLogoKeys.js +18 -0
  191. package/dist/shared/taskforceAgentDefinition.d.ts +0 -1
  192. package/dist/shared/taskforceAgentDefinition.js +6 -9
  193. package/dist/shared/taskforceAgentModels.js +7 -3
  194. package/dist/shared/userFeatureGrants.d.ts +4 -0
  195. package/dist/shared/userFeatureGrants.js +7 -0
  196. package/dist/storage/executorPhaseAApprovalReceiptStore.d.ts +37 -0
  197. package/dist/storage/executorPhaseAApprovalReceiptStore.js +81 -0
  198. package/dist/storage/executorPhaseARunAuthorizationStore.d.ts +103 -0
  199. package/dist/storage/executorPhaseARunAuthorizationStore.js +492 -0
  200. package/dist/storage/githubReviewEvidenceInstallationStore.d.ts +26 -0
  201. package/dist/storage/githubReviewEvidenceInstallationStore.js +89 -0
  202. package/dist/storage/modelProviderConnectionStore.d.ts +99 -0
  203. package/dist/storage/modelProviderConnectionStore.js +446 -0
  204. package/dist/storage/postgresAdapter.d.ts +4 -0
  205. package/dist/storage/postgresAdapter.js +14 -1
  206. package/dist/storage/postgresRequestDiagnostics.d.ts +9 -1
  207. package/dist/storage/postgresRequestDiagnostics.js +15 -2
  208. package/dist/storage/postgresWorker.js +20 -2
  209. package/dist/storage/postgresWorkerProtocol.d.ts +7 -0
  210. package/dist/storage/taskforceAgentConnectionBindingStore.d.ts +31 -0
  211. package/dist/storage/taskforceAgentConnectionBindingStore.js +84 -0
  212. package/dist/storage/workflowStore.d.ts +2 -0
  213. package/dist/storage/workflowStore.js +39 -2
  214. package/dist/sync/cloudSyncApi.js +4 -2
  215. package/dist/sync/collaborationSyncState.js +5 -0
  216. package/dist/sync/coordinator/localSyncExecutionPermitStore.js +60 -29
  217. package/dist/sync/coordinator/localWorkspaceSyncPendingV2PushProgress.d.ts +10 -0
  218. package/dist/sync/coordinator/localWorkspaceSyncPendingV2PushProgress.js +29 -0
  219. package/dist/sync/coordinator/localWorkspaceSyncRunner.d.ts +3 -1
  220. package/dist/sync/coordinator/localWorkspaceSyncRunner.js +18 -0
  221. package/dist/sync/coordinator/localWorkspaceSyncRunnerState.d.ts +15 -1
  222. package/dist/sync/coordinator/localWorkspaceSyncRunnerState.js +99 -2
  223. package/dist/sync/coordinator/workspaceSyncCredentialBroker.d.ts +1 -0
  224. package/dist/sync/coordinator/workspaceSyncCredentialBroker.js +33 -4
  225. package/dist/sync/engine/workspaceSyncContentLifecycleAdapter.d.ts +2 -1
  226. package/dist/sync/engine/workspaceSyncContentVersionStore.d.ts +38 -0
  227. package/dist/sync/engine/workspaceSyncContentVersionStore.js +50 -1
  228. package/dist/sync/engine/workspaceSyncEngineLifecyclePolicy.js +6 -6
  229. package/dist/sync/engine/workspaceSyncEnginePushCompletion.d.ts +14 -0
  230. package/dist/sync/engine/workspaceSyncEnginePushCompletion.js +84 -0
  231. package/dist/sync/engine/workspaceSyncEngineRecovery.js +1 -0
  232. package/dist/sync/engine/workspaceSyncEngineRuntime.d.ts +1 -0
  233. package/dist/sync/engine/workspaceSyncEngineRuntime.js +1 -0
  234. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +112 -38
  235. package/dist/sync/engine/workspaceSyncEngineServerSnapshotReader.js +6 -0
  236. package/dist/sync/engine/workspaceSyncEngineTransfers.js +3 -1
  237. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.d.ts +1 -0
  238. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +31 -0
  239. package/dist/sync/syncApplyHandlers.d.ts +3 -0
  240. package/dist/sync/syncApplyHandlers.js +81 -15
  241. package/dist/sync/syncService.d.ts +15 -0
  242. package/dist/sync/syncService.js +83 -18
  243. package/dist/sync/taskforceAgentSyncAuthorization.d.ts +16 -0
  244. package/dist/sync/taskforceAgentSyncAuthorization.js +20 -0
  245. package/dist/sync/v3/combinedFeedCapability.js +4 -0
  246. package/dist/sync/v3/durableInitiativeMutationRouter.js +18 -10
  247. package/dist/sync/v3/durableOperationReplay.d.ts +14 -0
  248. package/dist/sync/v3/durableOperationReplay.js +18 -0
  249. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +3 -0
  250. package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +2 -1
  251. package/dist/sync/v3/durableTaskChecklistMutationRouter.js +58 -15
  252. package/dist/sync/v3/durableTaskCommentMutationRouter.js +3 -0
  253. package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +1 -1
  254. package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +10 -0
  255. package/dist/sync/v3/durableTaxonomyMutationRouter.js +5 -0
  256. package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.d.ts +18 -7
  257. package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.js +89 -23
  258. package/dist/sync/v3/durableWorkstreamMutationRouter.js +18 -10
  259. package/dist/sync/v3/durableWorkstreamTaskOrderMutationRouter.js +16 -5
  260. package/dist/sync/v3/localOutboxDispatchRuntime.js +2 -2
  261. package/dist/sync/v3/localOutboxRecoveryPreparationService.js +8 -2
  262. package/dist/sync/v3/localTaskChecklistOutboxHandler.js +16 -2
  263. package/dist/sync/v3/localTaskChecklistOutboxService.d.ts +4 -0
  264. package/dist/sync/v3/localTaskChecklistOutboxService.js +14 -1
  265. package/dist/sync/v3/localTaskCreateOutboxService.d.ts +3 -1
  266. package/dist/sync/v3/localTaskCreateOutboxService.js +6 -1
  267. package/dist/sync/v3/localTaskDeleteOutboxService.js +1 -0
  268. package/dist/sync/v3/localTaskLifecycleOutboxService.d.ts +2 -1
  269. package/dist/sync/v3/localTaskLifecycleOutboxService.js +5 -1
  270. package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -1
  271. package/dist/sync/v3/localTaskMetadataOutboxService.js +6 -2
  272. package/dist/sync/v3/localTaskStatusOutboxService.d.ts +10 -1
  273. package/dist/sync/v3/localTaskStatusOutboxService.js +238 -16
  274. package/dist/sync/v3/syncDurabilityStore.d.ts +36 -1
  275. package/dist/sync/v3/syncDurabilityStore.js +202 -8
  276. package/dist/sync/v3/taskChecklistMutationService.d.ts +11 -0
  277. package/dist/sync/v3/taskChecklistMutationService.js +44 -1
  278. package/dist/sync/v3/taskStatusMutationService.d.ts +29 -1
  279. package/dist/sync/v3/taskStatusMutationService.js +119 -3
  280. package/dist/sync/v3/workflowAssignmentSync.d.ts +5 -0
  281. package/dist/sync/v3/workflowAssignmentSync.js +27 -0
  282. package/dist/sync/v3/workflowRuntimeMutationService.d.ts +5 -0
  283. package/dist/sync/v3/workflowRuntimeMutationService.js +124 -71
  284. package/dist/sync/v3/workspaceSyncJournalReader.js +1 -1
  285. package/dist/sync/v3/workspaceSyncOperationRetentionService.d.ts +16 -0
  286. package/dist/sync/v3/workspaceSyncOperationRetentionService.js +83 -0
  287. package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.d.ts +1 -0
  288. package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +9 -2
  289. package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +25 -3
  290. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +7 -0
  291. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +44 -32
  292. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +9 -5
  293. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +54 -38
  294. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +10 -0
  295. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +70 -31
  296. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +2 -1
  297. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +16 -0
  298. package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +6 -0
  299. package/dist/sync/v3/workspaceSyncV3Feed.d.ts +1 -0
  300. package/dist/sync/v3/workspaceSyncV3Feed.js +6 -1
  301. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +2 -0
  302. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +49 -18
  303. package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +3 -0
  304. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +5 -0
  305. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +36 -2
  306. package/dist/sync/v3/workspaceSyncV3RepairSnapshotRetentionService.d.ts +17 -0
  307. package/dist/sync/v3/workspaceSyncV3RepairSnapshotRetentionService.js +64 -0
  308. package/dist/sync/v3/workspaceSyncV3WorkflowRuntimeApply.d.ts +1 -0
  309. package/dist/sync/v3/workspaceSyncV3WorkflowRuntimeApply.js +50 -5
  310. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverCoverage.d.ts +7 -0
  311. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverCoverage.js +33 -0
  312. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverWireContract.d.ts +7 -0
  313. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverWireContract.js +8 -0
  314. package/dist/sync/workspaceRepair.d.ts +5 -2
  315. package/dist/sync/workspaceRepair.js +161 -6
  316. package/dist/sync/workspaceSyncModel.d.ts +21 -0
  317. package/dist/sync/workspaceSyncModel.js +35 -7
  318. package/dist/sync/workspaceSyncState.d.ts +1 -0
  319. package/dist/sync/workspaceSyncV2SnapshotBuilder.d.ts +6 -0
  320. package/dist/sync/workspaceSyncV2SnapshotBuilder.js +5 -0
  321. package/dist/ui/assets/AiIdentityRosterCard-CDMPtnbC.js +1 -0
  322. package/dist/ui/assets/AiProfilesModule-Cww85C4E.js +1 -0
  323. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-B68Wg-RV.js → AnnotatedAttachmentWorkspace-Dt9TKYSH.js} +1 -1
  324. package/dist/ui/assets/{AssetTraySortControl-C8Rztyal.js → AssetTraySortControl-CKgapnxu.js} +1 -1
  325. package/dist/ui/assets/{ContextAttachmentManager-Cj3c7X9y.js → ContextAttachmentManager-T-cEVcOZ.js} +2 -2
  326. package/dist/ui/assets/{DocumentWorkspace-B5uvzems.js → DocumentWorkspace-HD4MUjAZ.js} +1 -1
  327. package/dist/ui/assets/{EntityActivityTimeline-BVFvCB5R.js → EntityActivityTimeline-_V0zfMuh.js} +1 -1
  328. package/dist/ui/assets/PlanningModule-mTkBygsB.js +1 -0
  329. package/dist/ui/assets/{PlansPage-AY4cGWco.js → PlansPage-Btiq06VO.js} +1 -1
  330. package/dist/ui/assets/{TaskContextUpload-DZxTVesW.js → TaskContextUpload-C00NVUpn.js} +1 -1
  331. package/dist/ui/assets/TaskSettings-Cp-L-4xh.js +12 -0
  332. package/dist/ui/assets/TaskforceAgentsModule-Coq9-PUp.js +24 -0
  333. package/dist/ui/assets/TaskforceAgentsModule-b-8R25cJ.css +1 -0
  334. package/dist/ui/assets/{WorkflowManagerModule-DEr5di_r.js → WorkflowManagerModule-CuqRzMmM.js} +1 -1
  335. package/dist/ui/assets/index-BZ-hdZSk.css +1 -0
  336. package/dist/ui/assets/index-DSQINwt9.js +7 -0
  337. package/dist/ui/assets/{vendor-icons-D9Lpw-j4.js → vendor-icons-Bsq-mcEn.js} +1 -1
  338. package/dist/ui/branding/logos/letterhead_dark.png +0 -0
  339. package/dist/ui/branding/logos/letterhead_light.png +0 -0
  340. package/dist/ui/branding/logos/signature_dark.png +0 -0
  341. package/dist/ui/branding/logos/signature_light.png +0 -0
  342. package/dist/ui/index.html +3 -3
  343. package/dist/utils/aiProfileDefaultLogo.js +16 -48
  344. package/dist/utils/httpSetCookie.d.ts +1 -0
  345. package/dist/utils/httpSetCookie.js +4 -1
  346. package/dist/utils/localRuntimeUrl.d.ts +1 -0
  347. package/dist/utils/localRuntimeUrl.js +14 -0
  348. package/dist/utils/productLogoRegistry.d.ts +2 -0
  349. package/dist/utils/productLogoRegistry.js +36 -0
  350. package/dist/utils/syncStatusPresentation.d.ts +4 -0
  351. package/dist/utils/syncStatusPresentation.js +17 -0
  352. package/dist/utils/taskEvents.d.ts +2 -0
  353. package/dist/utils/workspaceSyncPresentation.d.ts +1 -1
  354. package/dist/utils/workspaceSyncPresentation.js +23 -17
  355. package/package.json +24 -4
  356. package/dist/ui/assets/AiIdentityRosterCard-BZM4aQnM.js +0 -1
  357. package/dist/ui/assets/AiProfilesModule-BbAEWtHH.js +0 -1
  358. package/dist/ui/assets/PlanningModule-B5DWQOGz.js +0 -1
  359. package/dist/ui/assets/TaskSettings-BeWAuyZj.js +0 -12
  360. package/dist/ui/assets/TaskforceAgentsModule-BifCYtjR.js +0 -21
  361. package/dist/ui/assets/TaskforceAgentsModule-D6IC0S7Q.css +0 -1
  362. package/dist/ui/assets/index-I5zhdtgt.js +0 -7
  363. package/dist/ui/assets/index-VRFsx3wz.css +0 -1
@@ -1,6 +1,5 @@
1
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
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';
@@ -80,7 +79,7 @@ import { createDurableTaskChecklistMutationRouter, DurableTaskChecklistRouterInP
80
79
  import { DurableTaskChecklistMutationError } from '../sync/v3/taskChecklistMutationService.js';
81
80
  import { createDurableTaskAttachmentLinksMutationRouter, DurableTaskAttachmentLinksRouterInProgressError, } from '../sync/v3/durableTaskAttachmentLinksMutationRouter.js';
82
81
  import { DurableTaskAttachmentLinksMutationError } from '../sync/v3/taskAttachmentLinksMutationService.js';
83
- import { createDurableWorkflowRuntimeMutationRouter, resolveWorkflowRuntimeMutationCapability, } from '../sync/v3/durableWorkflowRuntimeMutationRouter.js';
82
+ import { areWorkflowRuntimeMutationsFenced, createDurableWorkflowRuntimeMutationRouter, resolveWorkflowRuntimeMutationCapability, } from '../sync/v3/durableWorkflowRuntimeMutationRouter.js';
84
83
  import { serializeCanonicalPlanningAttachmentLinks } from '../sync/planningCanonicalFields.js';
85
84
  import { buildCanonicalTaxonomyStateMutation, } from '../sync/v3/taxonomyStateMutation.js';
86
85
  import { buildTaskDeepLink, buildTaskforceDeepLink, normalizeTaskforceApplicationBaseUrl, } from '../shared/taskDeepLinks.js';
@@ -120,7 +119,10 @@ class ToolProfileHiddenError extends Error {
120
119
  suggestedTool;
121
120
  constructor(name, activeProfile) {
122
121
  const requiredProfiles = getMcpToolRequiredProfiles(name);
123
- const suggestedTool = getMcpToolSuccessor(name);
122
+ const configuredSuccessor = getMcpToolSuccessor(name);
123
+ const suggestedTool = configuredSuccessor && isMcpToolVisibleInProfile(configuredSuccessor.name, activeProfile)
124
+ ? configuredSuccessor
125
+ : null;
124
126
  const suffix = suggestedTool ? ` Use ${suggestedTool.name} instead.` : ` Request one of these profiles to use it: ${requiredProfiles.join(', ')}.`;
125
127
  super(`Tool ${name} is not available in the ${activeProfile} MCP tool profile.${suffix}`);
126
128
  this.activeProfile = activeProfile;
@@ -747,6 +749,9 @@ function createOwnedTaskforceMcpCore(projectRoot, tenantId, localDataOwnerPermit
747
749
  },
748
750
  });
749
751
  }
752
+ export function createStatelessTaskforceMcpServer(options = {}, factory = createTaskforceMcpServer) {
753
+ return factory({ ...options, resourceSubscriptionMode: 'disabled' });
754
+ }
750
755
  export function createTaskforceMcpServer(options = {}) {
751
756
  // In dev (tsx), this module lives in src/mcp/.
752
757
  // In prod, it lives in dist/mcp/.
@@ -754,6 +759,9 @@ export function createTaskforceMcpServer(options = {}) {
754
759
  const PROJECT_NAME = path.basename(PROJECT_ROOT);
755
760
  const logStartup = options.logStartup !== false;
756
761
  const runtimeMode = options.runtimeMode === 'cloud' ? 'cloud' : 'local';
762
+ const modernProtocol = options.protocolEra === 'modern';
763
+ const supportsLegacyResourceSubscriptions = !modernProtocol
764
+ && options.resourceSubscriptionMode === 'legacy-long-lived';
757
765
  const applicationBaseUrl = normalizeTaskforceApplicationBaseUrl(options.applicationBaseUrl)
758
766
  || (runtimeMode === 'cloud'
759
767
  ? resolveCloudApplicationBaseUrl()
@@ -836,8 +844,6 @@ export function createTaskforceMcpServer(options = {}) {
836
844
  };
837
845
  const operationIdentityScope = options.operationIdentityScope
838
846
  ? {
839
- generation: String(options.operationIdentityScope.generation || '').trim(),
840
- sessionId: String(options.operationIdentityScope.sessionId || '').trim(),
841
847
  connectionInstance: String(options.operationIdentityScope.connectionInstance || '').trim(),
842
848
  }
843
849
  : null;
@@ -845,7 +851,7 @@ export function createTaskforceMcpServer(options = {}) {
845
851
  throw new Error('Shared MCP operation identity scope is incomplete.');
846
852
  }
847
853
  const runtimeRequestNamespace = operationIdentityScope
848
- ? fingerprintWorkspaceSyncPayload({ kind: 'shared-mcp-session', ...operationIdentityScope })
854
+ ? fingerprintWorkspaceSyncPayload({ kind: 'shared-mcp-process', ...operationIdentityScope })
849
855
  : runtimeMode === 'cloud'
850
856
  ? String(options.auth?.requestId || '').trim() || randomUUID()
851
857
  : randomUUID();
@@ -1204,8 +1210,8 @@ export function createTaskforceMcpServer(options = {}) {
1204
1210
  }, {
1205
1211
  capabilities: {
1206
1212
  resources: {
1207
- subscribe: true,
1208
- listChanged: true,
1213
+ ...(supportsLegacyResourceSubscriptions ? { subscribe: true } : {}),
1214
+ ...(!modernProtocol ? { listChanged: true } : {}),
1209
1215
  },
1210
1216
  tools: {},
1211
1217
  },
@@ -1881,6 +1887,9 @@ export function createTaskforceMcpServer(options = {}) {
1881
1887
  } : {}),
1882
1888
  title: task?.title || null,
1883
1889
  status: task?.status || null,
1890
+ isArchived: Boolean(task?.isArchived),
1891
+ completedAt: task?.completedAt || null,
1892
+ canceledReason: task?.canceledReason || null,
1884
1893
  category: task?.category || null,
1885
1894
  type: task?.type || null,
1886
1895
  priority: task?.priority ?? null,
@@ -2225,42 +2234,38 @@ export function createTaskforceMcpServer(options = {}) {
2225
2234
  return null;
2226
2235
  return resolved;
2227
2236
  }
2228
- function resolveFinishTaskReviewer(task) {
2237
+ function resolveFinishTaskReviewer(task, reviewer) {
2238
+ if (reviewer !== undefined) {
2239
+ const resolvedReviewer = resolveRuntimeTaskAssigneeInput(reviewer);
2240
+ const actor = resolvedReviewer
2241
+ ? core.resolveWorkspaceActor(ACTIVE_WORKSPACE_ID, resolvedReviewer)
2242
+ : null;
2243
+ const eligible = actor?.kind === 'ai'
2244
+ ? core.listAiProfiles(ACTIVE_WORKSPACE_ID).some((profile) => profile.id === actor.id)
2245
+ : core.listWorkspaceUsers(ACTIVE_WORKSPACE_ID).some((user) => (user.userId === actor?.id
2246
+ && String(user.status || '').trim().toLowerCase() === 'active'
2247
+ && user.disabled !== true));
2248
+ if (!actor || actor.meta?.unresolved === true || !eligible) {
2249
+ 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');
2250
+ }
2251
+ return { assignee: actor.id, source: 'explicit' };
2252
+ }
2229
2253
  const existingAssignee = String(task?.assignee || '').trim();
2230
2254
  if (existingAssignee) {
2231
2255
  const existingActor = core.resolveWorkspaceActor(ACTIVE_WORKSPACE_ID, existingAssignee);
2232
- if (existingActor?.kind === 'human' && existingActor.meta?.unresolved !== true) {
2256
+ const eligibleExistingHuman = existingActor?.kind === 'human'
2257
+ && existingActor.meta?.unresolved !== true
2258
+ && core.listWorkspaceUsers(ACTIVE_WORKSPACE_ID).some((user) => (user.userId === existingActor.id
2259
+ && String(user.status || '').trim().toLowerCase() === 'active'
2260
+ && user.disabled !== true));
2261
+ if (eligibleExistingHuman) {
2233
2262
  return {
2234
2263
  assignee: existingActor.id,
2235
2264
  source: 'existing_human_owner',
2236
2265
  };
2237
2266
  }
2238
2267
  }
2239
- if (runtimeMode === 'cloud' && auditActorUserId !== 'anonymous') {
2240
- const currentUser = core.listWorkspaceUsers(ACTIVE_WORKSPACE_ID)
2241
- .find((user) => user.userId === auditActorUserId
2242
- && String(user.status || '').trim().toLowerCase() === 'active'
2243
- && user.disabled !== true);
2244
- if (currentUser) {
2245
- return { assignee: currentUser.userId, source: 'current_user' };
2246
- }
2247
- }
2248
- const workspaceOwner = core.listWorkspaceUsers(ACTIVE_WORKSPACE_ID)
2249
- .filter((user) => String(user.status || '').trim().toLowerCase() === 'active' && user.disabled !== true)
2250
- .sort((left, right) => {
2251
- const leftRank = left.role === 'owner' ? 0 : left.role === 'admin' ? 1 : 2;
2252
- const rightRank = right.role === 'owner' ? 0 : right.role === 'admin' ? 1 : 2;
2253
- if (leftRank !== rightRank)
2254
- return leftRank - rightRank;
2255
- return String(left.displayName || left.email || left.userId).localeCompare(String(right.displayName || right.email || right.userId), undefined, { sensitivity: 'base' });
2256
- })[0];
2257
- if (workspaceOwner && (workspaceOwner.role === 'owner' || workspaceOwner.role === 'admin')) {
2258
- return {
2259
- assignee: workspaceOwner.userId,
2260
- source: 'workspace_owner',
2261
- };
2262
- }
2263
- 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');
2268
+ 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');
2264
2269
  }
2265
2270
  function getActiveMcpActorRef() {
2266
2271
  if (activeAiProfileId)
@@ -2301,13 +2306,19 @@ export function createTaskforceMcpServer(options = {}) {
2301
2306
  }).task;
2302
2307
  }
2303
2308
  if (!useStatusBoundary) {
2304
- return core.updateTask(input.task.id, input.updates, ACTIVE_WORKSPACE_ID, { actorRef: getActiveMcpActorRef() });
2309
+ return core.updateTask(input.task.id, input.updates, ACTIVE_WORKSPACE_ID, {
2310
+ actorRef: getActiveMcpActorRef(),
2311
+ ...(input.deferNotifications ? { persistBackup: false, emitRealtime: false } : {}),
2312
+ });
2305
2313
  }
2306
2314
  const unsupported = Object.keys(input.updates).filter((field) => (!durableStatusControlFields.has(field) && !durableTaskMetadataFieldNames.has(field)));
2307
2315
  if (unsupported.length > 0) {
2308
2316
  throw new TaskforceRuleError(`Combined durable task status update contains unsupported fields: ${unsupported.sort().join(', ')}.`, 400, 'TASK_STATUS_INVALID_PAYLOAD');
2309
2317
  }
2310
2318
  const status = String(hasStatus ? input.updates.status : input.task.status || 'task').trim();
2319
+ if (status === 'review' && String(input.task.status || '').trim() !== 'review' && !input.reviewHandoff) {
2320
+ throw new McpPublicError('Moving a task to review requires finish_task with a nonblank evidence comment.', 'TASK_REVIEW_HANDOFF_REQUIRED', 400);
2321
+ }
2311
2322
  const metadataPatch = selectDurableTaskMetadataPatch(input.updates);
2312
2323
  delete metadataPatch.assignee;
2313
2324
  const assigneeIntent = input.assigneeIntent || (Object.prototype.hasOwnProperty.call(input.updates, 'assignee')
@@ -2322,10 +2333,73 @@ export function createTaskforceMcpServer(options = {}) {
2322
2333
  : null;
2323
2334
  return durableTaskMutationRouter.setStatus({
2324
2335
  taskId: input.task.id,
2325
- payload: { status, assigneeIntent, canceledReason, metadataPatch },
2336
+ payload: {
2337
+ status,
2338
+ assigneeIntent,
2339
+ canceledReason,
2340
+ metadataPatch,
2341
+ ...(input.reviewHandoff ? { reviewHandoff: input.reviewHandoff } : {}),
2342
+ },
2326
2343
  actorRef: getActiveMcpActorRef(),
2327
2344
  }).task;
2328
2345
  }
2346
+ function mutateTaskReviewHandoffThroughBoundary(input) {
2347
+ const validation = validateStructuredCommentForAdd(input.handoffComment);
2348
+ if (!validation.ok)
2349
+ throw new McpPublicError(validation.message);
2350
+ const actorRef = getActiveMcpActorRef();
2351
+ const operationId = requireStableMcpRequestId('operation', `review-handoff-comment:${input.task.id}`);
2352
+ const commentId = requireStableMcpRequestId('comment', `review-handoff:${input.task.id}`);
2353
+ const comment = {
2354
+ id: commentId,
2355
+ author: actorRef,
2356
+ submittedAuthor: actorRef,
2357
+ text: input.handoffComment,
2358
+ };
2359
+ if (durableTaskMutationRouter.enabled) {
2360
+ return mutateTaskThroughStatusBoundary({
2361
+ task: input.task,
2362
+ updates: input.updates,
2363
+ assigneeIntent: input.assigneeIntent,
2364
+ reviewHandoff: { comment },
2365
+ });
2366
+ }
2367
+ let updated;
2368
+ try {
2369
+ updated = core.getDatabaseAdapter().transaction(() => {
2370
+ mutateTaskThroughStatusBoundary({
2371
+ task: input.task,
2372
+ updates: input.updates,
2373
+ assigneeIntent: input.assigneeIntent,
2374
+ deferNotifications: true,
2375
+ });
2376
+ const durableRouter = getDurableTaskCommentMutationRouter(actorRef, operationId);
2377
+ const task = durableRouter.enabled
2378
+ ? durableRouter.append({
2379
+ taskId: input.task.id,
2380
+ comment: { ...comment, timestamp: new Date().toISOString() },
2381
+ }).task
2382
+ : core.addComment(input.task.id, input.handoffComment, actorRef, ACTIVE_WORKSPACE_ID, {
2383
+ actorRef,
2384
+ persistBackup: false,
2385
+ emitRealtime: false,
2386
+ commentId,
2387
+ });
2388
+ if (!task)
2389
+ throw new Error(`Task ${input.task.id} not found`);
2390
+ return task;
2391
+ })();
2392
+ }
2393
+ catch (error) {
2394
+ try {
2395
+ core.notifyDurableTaskMetadataCommitted(ACTIVE_WORKSPACE_ID, input.task.id);
2396
+ }
2397
+ catch { /* Preserve the original handoff failure. */ }
2398
+ throw error;
2399
+ }
2400
+ core.notifyDurableTaskMetadataCommitted(ACTIVE_WORKSPACE_ID, input.task.id);
2401
+ return updated;
2402
+ }
2329
2403
  function mutateTaskLifecycleThroughBoundary(input) {
2330
2404
  if (!durableTaskMutationRouter.enabled)
2331
2405
  return input.legacy();
@@ -3706,7 +3780,7 @@ export function createTaskforceMcpServer(options = {}) {
3706
3780
  const hydratedWorkstream = core.getWorkstream(workstream.id, ACTIVE_WORKSPACE_ID) || workstream;
3707
3781
  return { id: hydratedWorkstream.id, workstream: hydratedWorkstream };
3708
3782
  }
3709
- server.setRequestHandler(ListResourcesRequestSchema, async () => {
3783
+ server.setRequestHandler('resources/list', async () => {
3710
3784
  const resources = [
3711
3785
  {
3712
3786
  uri: RESOURCE_CONFIG_URI,
@@ -3753,7 +3827,7 @@ export function createTaskforceMcpServer(options = {}) {
3753
3827
  resources,
3754
3828
  };
3755
3829
  });
3756
- server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
3830
+ server.setRequestHandler('resources/templates/list', async () => {
3757
3831
  if (!hasPermission(RESOURCE_PERMISSIONS.task)) {
3758
3832
  return { resourceTemplates: [] };
3759
3833
  }
@@ -3785,7 +3859,7 @@ export function createTaskforceMcpServer(options = {}) {
3785
3859
  ],
3786
3860
  };
3787
3861
  });
3788
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
3862
+ server.setRequestHandler('resources/read', async (request) => {
3789
3863
  const uri = request.params.uri;
3790
3864
  assertResourcePermission(uri);
3791
3865
  if (uri === RESOURCE_CONFIG_URI) {
@@ -3891,24 +3965,26 @@ export function createTaskforceMcpServer(options = {}) {
3891
3965
  }
3892
3966
  throw new Error(`Unknown resource URI: ${uri}`);
3893
3967
  });
3894
- server.setRequestHandler(SubscribeRequestSchema, async (request) => {
3895
- const uri = request.params.uri;
3896
- if (!isSupportedResourceUri(uri)) {
3897
- throw new Error(`Unsupported resource URI for subscribe: ${uri}`);
3898
- }
3899
- assertResourcePermission(uri);
3900
- resourceSubscriptions.add(uri);
3901
- return {};
3902
- });
3903
- server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
3904
- const uri = request.params.uri;
3905
- if (!isSupportedResourceUri(uri)) {
3906
- throw new Error(`Unsupported resource URI for unsubscribe: ${uri}`);
3907
- }
3908
- assertResourcePermission(uri);
3909
- resourceSubscriptions.delete(uri);
3910
- return {};
3911
- });
3968
+ if (supportsLegacyResourceSubscriptions) {
3969
+ server.setRequestHandler('resources/subscribe', async (request) => {
3970
+ const uri = request.params.uri;
3971
+ if (!isSupportedResourceUri(uri)) {
3972
+ throw new Error(`Unsupported resource URI for subscribe: ${uri}`);
3973
+ }
3974
+ assertResourcePermission(uri);
3975
+ resourceSubscriptions.add(uri);
3976
+ return {};
3977
+ });
3978
+ server.setRequestHandler('resources/unsubscribe', async (request) => {
3979
+ const uri = request.params.uri;
3980
+ if (!isSupportedResourceUri(uri)) {
3981
+ throw new Error(`Unsupported resource URI for unsubscribe: ${uri}`);
3982
+ }
3983
+ assertResourcePermission(uri);
3984
+ resourceSubscriptions.delete(uri);
3985
+ return {};
3986
+ });
3987
+ }
3912
3988
  /**
3913
3989
  * Tool Definitions
3914
3990
  */
@@ -4024,16 +4100,19 @@ export function createTaskforceMcpServer(options = {}) {
4024
4100
  const tool = registeredTools.find((candidate) => candidate.name === name);
4025
4101
  return tool ? materializeToolDescriptor(tool, buildToolDescriptorContext()) : null;
4026
4102
  };
4027
- server.setRequestHandler(ListToolsRequestSchema, async () => {
4103
+ const listTools = async () => {
4028
4104
  const context = buildToolDescriptorContext();
4029
4105
  const tools = registeredTools
4030
4106
  .map((tool) => materializeToolDescriptor(tool, context))
4031
4107
  .filter((tool) => (isMcpToolVisibleInProfile(tool.name, activeToolProfile)
4032
4108
  && hasPermission(TOOL_PERMISSIONS[tool.name])));
4033
4109
  return {
4034
- tools,
4110
+ // Descriptors are validated by Taskforce's JSON Schema contract before
4111
+ // registration; v2 narrows the equivalent recursive JSON value type.
4112
+ tools: tools,
4035
4113
  };
4036
- });
4114
+ };
4115
+ server.setRequestHandler('tools/list', listTools);
4037
4116
  const resolvePlanningAttachmentTarget = (targetType, inputId) => {
4038
4117
  if (targetType === 'initiative') {
4039
4118
  const { id, initiative } = resolveInitiativeByIdentifierOrThrow(inputId);
@@ -4953,7 +5032,9 @@ export function createTaskforceMcpServer(options = {}) {
4953
5032
  const taskLifecycleCommandService = createTaskLifecycleCommandService({
4954
5033
  formatTaskIdentifier,
4955
5034
  resolveAssigneeLabel: (assignee) => core.resolveWorkspaceActor(ACTIVE_WORKSPACE_ID, assignee)?.label || assignee,
4956
- setStatus: (input) => mutateTaskThroughStatusBoundary(input),
5035
+ setStatus: (input) => input.handoffComment
5036
+ ? mutateTaskReviewHandoffThroughBoundary(input)
5037
+ : mutateTaskThroughStatusBoundary(input),
4957
5038
  mutateLifecycle: (input) => mutateTaskLifecycleThroughBoundary({
4958
5039
  taskId: input.taskId,
4959
5040
  mutationKind: input.mutationKind,
@@ -4973,7 +5054,7 @@ export function createTaskforceMcpServer(options = {}) {
4973
5054
  });
4974
5055
  },
4975
5056
  }),
4976
- resolveReviewer: (task) => resolveFinishTaskReviewer(task),
5057
+ resolveReviewer: (task, reviewer) => resolveFinishTaskReviewer(task, reviewer),
4977
5058
  });
4978
5059
  const planningCommandPreparationService = new PlanningCommandPreparationService({
4979
5060
  resolveOwner: resolvePlanningOwnerInput,
@@ -4986,12 +5067,17 @@ export function createTaskforceMcpServer(options = {}) {
4986
5067
  listItems: (taskId) => core.listChecklistItems(taskId, ACTIVE_WORKSPACE_ID),
4987
5068
  createId: (scope, key) => requireStableMcpRequestId(scope, key),
4988
5069
  now: () => new Date().toISOString(),
4989
- replace: ({ taskId, items, operationId }) => {
5070
+ replace: ({ taskId, items, operationId, expectedDetailRevision, requireDurableIdempotency, itemUpdateIntent, }) => {
4990
5071
  const actorRef = getActiveMcpActorRef();
4991
5072
  const durableRouter = getDurableTaskChecklistMutationRouter(actorRef, operationId);
5073
+ if (requireDurableIdempotency) {
5074
+ requireDurableExplicitIdempotency(durableRouter.enabled, 'task checklist');
5075
+ }
4992
5076
  return durableRouter.enabled
4993
- ? durableRouter.replace({ taskId, items }).task
4994
- : core.updateTask(taskId, { checklistItems: items }, ACTIVE_WORKSPACE_ID, { actorRef });
5077
+ ? durableRouter.replace({ taskId, items, expectedDetailRevision, itemUpdateIntent }).task
5078
+ : expectedDetailRevision
5079
+ ? core.updateTaskIfDetailRevisionMatches(taskId, expectedDetailRevision, { checklistItems: items }, ACTIVE_WORKSPACE_ID, { actorRef })
5080
+ : core.updateTask(taskId, { checklistItems: items }, ACTIVE_WORKSPACE_ID, { actorRef });
4995
5081
  },
4996
5082
  });
4997
5083
  const annotatedAttachmentCommandService = createAnnotatedAttachmentCommandService({
@@ -5014,7 +5100,7 @@ export function createTaskforceMcpServer(options = {}) {
5014
5100
  }
5015
5101
  function rethrowChecklistCommandError(error) {
5016
5102
  if (error instanceof TaskChecklistCommandError) {
5017
- if (error.code === 'TASK_NOT_FOUND') {
5103
+ if (error.code === 'TASK_NOT_FOUND' || error.code === 'TASK_CHECKLIST_ITEM_NOT_FOUND') {
5018
5104
  throw new McpPublicError(error.message, 'MCP_NOT_FOUND', error.statusCode);
5019
5105
  }
5020
5106
  throw new McpPublicError(error.message);
@@ -5247,6 +5333,7 @@ export function createTaskforceMcpServer(options = {}) {
5247
5333
  const avatarSuggestion = resolved.created
5248
5334
  && !configuredClientBlocksGeneration
5249
5335
  && isAiProfileAvatarGenerationEligible(resolved.profile)
5336
+ && isMcpToolVisibleInProfile('generate_profile_avatar', activeToolProfile)
5250
5337
  ? {
5251
5338
  avatarSuggestion: {
5252
5339
  nextTool: 'generate_profile_avatar',
@@ -5455,6 +5542,9 @@ export function createTaskforceMcpServer(options = {}) {
5455
5542
  requiresCommentEvidence: step.requiresCommentEvidence,
5456
5543
  } : {}),
5457
5544
  }));
5545
+ const currentStep = assignment.execution.status === 'running'
5546
+ ? assignment.steps.find((step) => step.id === assignment.execution.currentStepId)
5547
+ : undefined;
5458
5548
  return {
5459
5549
  content: [{ type: 'text', text: JSON.stringify({
5460
5550
  task: withTaskUrl({ id: task.id, referenceLabel: formatTaskIdentifier(task), title: task.title }),
@@ -5466,11 +5556,23 @@ export function createTaskforceMcpServer(options = {}) {
5466
5556
  currentStepId: assignment.execution.currentStepId || null,
5467
5557
  steps,
5468
5558
  },
5559
+ completionCall: currentStep ? {
5560
+ tool: 'complete_workflow_step',
5561
+ arguments: {
5562
+ taskId,
5563
+ executionId: assignment.execution.id,
5564
+ stepId: currentStep.id,
5565
+ },
5566
+ } : null,
5469
5567
  }, null, 2) }],
5470
5568
  };
5471
5569
  }
5472
5570
  case "complete_workflow_step": {
5473
5571
  const { taskId, executionId, stepId, evidenceCommentId } = args;
5572
+ const normalizedEvidenceCommentId = evidenceCommentId == null ? undefined : evidenceCommentId;
5573
+ if (areWorkflowRuntimeMutationsFenced()) {
5574
+ throw new McpPublicError('Workflow execution changes are temporarily paused.', 'WORKFLOW_RUNTIME_MUTATIONS_FENCED', 503);
5575
+ }
5474
5576
  const { task } = resolveTaskByIdentifierOrThrow(taskId);
5475
5577
  ensureResolvedProfileForSessionBootstrap();
5476
5578
  const agentProfileId = String(activeAiProfileId || '').trim();
@@ -5483,7 +5585,7 @@ export function createTaskforceMcpServer(options = {}) {
5483
5585
  throw new McpPublicError('The workflow execution does not belong to this task.', 'WORKFLOW_TASK_MISMATCH', 409);
5484
5586
  }
5485
5587
  const idempotencyKey = `agent-step-${createHash('sha256')
5486
- .update(`${executionId}:${stepId}:${agentProfileId}:${String(evidenceCommentId || '')}`)
5588
+ .update(`${executionId}:${stepId}:${agentProfileId}:${String(normalizedEvidenceCommentId || '')}`)
5487
5589
  .digest('hex')
5488
5590
  .slice(0, 32)}`;
5489
5591
  let result;
@@ -5505,7 +5607,7 @@ export function createTaskforceMcpServer(options = {}) {
5505
5607
  }).completeStep({
5506
5608
  executionId,
5507
5609
  expectedStepId: stepId,
5508
- evidenceCommentId,
5610
+ evidenceCommentId: normalizedEvidenceCommentId,
5509
5611
  })
5510
5612
  : executionService.completeCurrentStep({
5511
5613
  workspaceId: ACTIVE_WORKSPACE_ID,
@@ -5513,7 +5615,7 @@ export function createTaskforceMcpServer(options = {}) {
5513
5615
  actorId: agentProfileId,
5514
5616
  actorType: 'agent',
5515
5617
  expectedStepId: stepId,
5516
- evidenceCommentId,
5618
+ evidenceCommentId: normalizedEvidenceCommentId,
5517
5619
  idempotencyKey,
5518
5620
  });
5519
5621
  }
@@ -6170,12 +6272,14 @@ export function createTaskforceMcpServer(options = {}) {
6170
6272
  const { taskId } = args;
6171
6273
  const { id: resolvedTaskId } = resolveTaskByIdentifierOrThrow(taskId);
6172
6274
  const checklist = core.listChecklistItems(resolvedTaskId, ACTIVE_WORKSPACE_ID);
6275
+ const detailRevision = core.getTaskDetailRevision(resolvedTaskId, ACTIVE_WORKSPACE_ID);
6173
6276
  return {
6174
6277
  content: [{
6175
6278
  type: "text",
6176
6279
  text: JSON.stringify({
6177
6280
  taskId: resolvedTaskId,
6178
6281
  ...(buildTaskUrl({ id: resolvedTaskId }) ? { url: buildTaskUrl({ id: resolvedTaskId }) } : {}),
6282
+ detailRevision,
6179
6283
  items: checklist,
6180
6284
  }, null, 2),
6181
6285
  }],
@@ -6245,6 +6349,49 @@ export function createTaskforceMcpServer(options = {}) {
6245
6349
  }],
6246
6350
  };
6247
6351
  }
6352
+ case "update_task_checklist_item": {
6353
+ const { taskId, itemId, text, done, order, expectedDetailRevision, idempotencyKey: rawIdempotencyKey, } = args;
6354
+ const idempotencyKey = normalizeExplicitIdempotencyKey(rawIdempotencyKey);
6355
+ const operationId = idempotencyKey
6356
+ ? stableExplicitMutationId('operation', 'update_task_checklist_item', idempotencyKey)
6357
+ : requireStableMcpRequestId('operation', 'task-checklist-update');
6358
+ let result;
6359
+ try {
6360
+ result = taskChecklistCommandService.update({
6361
+ taskId,
6362
+ itemId,
6363
+ text,
6364
+ done,
6365
+ order,
6366
+ expectedDetailRevision,
6367
+ operationId,
6368
+ requireDurableIdempotency: Boolean(idempotencyKey),
6369
+ });
6370
+ }
6371
+ catch (error) {
6372
+ rethrowChecklistCommandError(error);
6373
+ }
6374
+ const detailRevision = core.getTaskDetailRevision(result.taskId, ACTIVE_WORKSPACE_ID);
6375
+ recordMcpWriteAudit('update_task_checklist_item', {
6376
+ taskId: result.taskId,
6377
+ itemId: result.item.id,
6378
+ });
6379
+ await notifyResourceMutation({
6380
+ includeTasks: true,
6381
+ taskIds: [result.taskId],
6382
+ });
6383
+ return {
6384
+ content: [{
6385
+ type: "text",
6386
+ text: JSON.stringify({
6387
+ taskId: result.taskId,
6388
+ ...(buildTaskUrl({ id: result.taskId }) ? { url: buildTaskUrl({ id: result.taskId }) } : {}),
6389
+ detailRevision,
6390
+ item: result.item,
6391
+ }, null, 2),
6392
+ }],
6393
+ };
6394
+ }
6248
6395
  case "get_task_attachments": {
6249
6396
  const { id, taskId, attachmentId } = args;
6250
6397
  const taskIdentifier = taskId ?? id;
@@ -6978,14 +7125,40 @@ export function createTaskforceMcpServer(options = {}) {
6978
7125
  };
6979
7126
  }
6980
7127
  case "review_document": {
6981
- const { id, documentId, sessionId, title, comments, prompt } = args;
7128
+ const { id, documentId, sessionId, title, comments, prompt, parentCommentId, threadCommentId, threadStatus, } = args;
6982
7129
  const asset = resolveDocumentAssetByIdentifierOrThrow(documentId ?? id);
6983
7130
  const normalizedComments = Array.isArray(comments)
6984
7131
  ? comments
6985
7132
  : (typeof prompt === 'string' && prompt.trim()
6986
7133
  ? [{ body: prompt.trim() }]
6987
7134
  : []);
6988
- if (normalizedComments.length === 0) {
7135
+ const normalizedSessionId = typeof sessionId === 'string' ? sessionId.trim() : '';
7136
+ const normalizedParentCommentId = typeof parentCommentId === 'string' ? parentCommentId.trim() : '';
7137
+ const normalizedThreadCommentId = typeof threadCommentId === 'string' ? threadCommentId.trim() : '';
7138
+ const hasThreadStatus = threadStatus !== undefined;
7139
+ const hasThreadUpdate = Boolean(normalizedThreadCommentId) || hasThreadStatus;
7140
+ const hasCommentsArgument = Object.prototype.hasOwnProperty.call(args, 'comments');
7141
+ const hasPromptArgument = Object.prototype.hasOwnProperty.call(args, 'prompt');
7142
+ const hasTitleArgument = Object.prototype.hasOwnProperty.call(args, 'title');
7143
+ if (hasThreadUpdate && (!normalizedThreadCommentId || (threadStatus !== 'open' && threadStatus !== 'resolved'))) {
7144
+ throw new McpPublicError('threadCommentId and threadStatus are required together');
7145
+ }
7146
+ if ((normalizedParentCommentId || hasThreadUpdate) && !normalizedSessionId) {
7147
+ throw new McpPublicError('sessionId is required for reply and thread-status updates');
7148
+ }
7149
+ if (hasThreadUpdate && (normalizedParentCommentId || hasCommentsArgument || hasPromptArgument || hasTitleArgument)) {
7150
+ throw new McpPublicError('Thread-status updates cannot be combined with comments, replies, or a session title');
7151
+ }
7152
+ if (normalizedParentCommentId && hasTitleArgument) {
7153
+ throw new McpPublicError('Replies cannot set a session title');
7154
+ }
7155
+ if (hasCommentsArgument && hasPromptArgument) {
7156
+ throw new McpPublicError('comments and prompt cannot be combined');
7157
+ }
7158
+ if (normalizedParentCommentId && normalizedComments.length !== 1) {
7159
+ throw new McpPublicError('Replies require exactly one comment or prompt');
7160
+ }
7161
+ if (!hasThreadUpdate && normalizedComments.length === 0) {
6989
7162
  throw new McpPublicError('comments array is required');
6990
7163
  }
6991
7164
  for (const comment of normalizedComments) {
@@ -6997,13 +7170,115 @@ export function createTaskforceMcpServer(options = {}) {
6997
7170
  throw new McpPublicError(anchorError);
6998
7171
  }
6999
7172
  }
7173
+ if (normalizedParentCommentId && normalizedComments[0]?.anchor !== undefined && normalizedComments[0]?.anchor !== null) {
7174
+ throw new McpPublicError('Replies cannot have anchors');
7175
+ }
7176
+ if (hasThreadUpdate) {
7177
+ const existingSession = core.getDocumentReviewSession({
7178
+ workspaceId: ACTIVE_WORKSPACE_ID,
7179
+ sessionId: normalizedSessionId,
7180
+ });
7181
+ if (!existingSession) {
7182
+ throw new McpPublicError(`Document review session ${normalizedSessionId} not found.`, 'MCP_NOT_FOUND', 404);
7183
+ }
7184
+ if (existingSession.assetId !== asset.assetId) {
7185
+ throw new McpPublicError(`Document review session ${normalizedSessionId} is not linked to document ${getDocumentReferenceLabel(asset) || asset.assetId}.`, 'MCP_ATTACHMENT_CONFLICT', 409);
7186
+ }
7187
+ const updatedSession = core.updateDocumentReviewThreadStatus({
7188
+ workspaceId: ACTIVE_WORKSPACE_ID,
7189
+ sessionId: normalizedSessionId,
7190
+ commentId: normalizedThreadCommentId,
7191
+ status: threadStatus,
7192
+ actorId: getActiveMcpActorRef(),
7193
+ });
7194
+ const updatedThread = updatedSession.comments.find((entry) => entry.id === normalizedThreadCommentId) || null;
7195
+ const sessionSummary = buildDocumentReviewSessionSummary(updatedSession);
7196
+ recordMcpWriteAudit('review_document', {
7197
+ documentAssetId: asset.assetId,
7198
+ sessionId: updatedSession.id,
7199
+ sessionAction: 'reused',
7200
+ threadCommentId: normalizedThreadCommentId,
7201
+ threadStatus,
7202
+ });
7203
+ return {
7204
+ content: [{
7205
+ type: 'text',
7206
+ text: JSON.stringify({
7207
+ document: {
7208
+ assetId: asset.assetId,
7209
+ referenceLabel: getDocumentReferenceLabel(asset),
7210
+ },
7211
+ sessionId: updatedSession.id,
7212
+ sessionAction: 'reused',
7213
+ postedCommentIds: [],
7214
+ commentCount: 0,
7215
+ thread: buildCompactDocumentReviewComment(updatedThread),
7216
+ unresolvedThreadCount: sessionSummary.unresolvedThreadCount,
7217
+ nextTool: 'get_document_reviews',
7218
+ suggestedArgs: {
7219
+ documentId: getDocumentReferenceLabel(asset) || asset.assetId,
7220
+ sessionId: updatedSession.id,
7221
+ },
7222
+ }, null, 2),
7223
+ }],
7224
+ };
7225
+ }
7226
+ if (normalizedParentCommentId) {
7227
+ const existingSession = core.getDocumentReviewSession({
7228
+ workspaceId: ACTIVE_WORKSPACE_ID,
7229
+ sessionId: normalizedSessionId,
7230
+ });
7231
+ if (!existingSession) {
7232
+ throw new McpPublicError(`Document review session ${normalizedSessionId} not found.`, 'MCP_NOT_FOUND', 404);
7233
+ }
7234
+ if (existingSession.assetId !== asset.assetId) {
7235
+ throw new McpPublicError(`Document review session ${normalizedSessionId} is not linked to document ${getDocumentReferenceLabel(asset) || asset.assetId}.`, 'MCP_ATTACHMENT_CONFLICT', 409);
7236
+ }
7237
+ const repliedSession = core.addDocumentReviewReply({
7238
+ workspaceId: ACTIVE_WORKSPACE_ID,
7239
+ sessionId: normalizedSessionId,
7240
+ parentCommentId: normalizedParentCommentId,
7241
+ body: normalizedComments[0].body,
7242
+ actorId: getActiveMcpActorRef(),
7243
+ });
7244
+ const postedReply = repliedSession.comments.at(-1) || null;
7245
+ recordMcpWriteAudit('review_document', {
7246
+ documentAssetId: asset.assetId,
7247
+ sessionId: repliedSession.id,
7248
+ sessionAction: 'reused',
7249
+ parentCommentId: normalizedParentCommentId,
7250
+ commentCount: 1,
7251
+ });
7252
+ return {
7253
+ content: [{
7254
+ type: 'text',
7255
+ text: JSON.stringify({
7256
+ document: {
7257
+ assetId: asset.assetId,
7258
+ referenceLabel: getDocumentReferenceLabel(asset),
7259
+ },
7260
+ sessionId: repliedSession.id,
7261
+ sessionAction: 'reused',
7262
+ postedCommentIds: postedReply ? [postedReply.id] : [],
7263
+ commentCount: postedReply ? 1 : 0,
7264
+ reply: buildCompactDocumentReviewComment(postedReply),
7265
+ nextTool: 'get_document_reviews',
7266
+ suggestedArgs: {
7267
+ documentId: getDocumentReferenceLabel(asset) || asset.assetId,
7268
+ sessionId: repliedSession.id,
7269
+ },
7270
+ }, null, 2),
7271
+ }],
7272
+ };
7273
+ }
7000
7274
  let commandResult;
7001
7275
  try {
7002
7276
  commandResult = documentReviewCommandService.review({
7003
7277
  workspaceId: ACTIVE_WORKSPACE_ID,
7004
7278
  assetId: asset.assetId,
7279
+ documentVersion: asset.version,
7005
7280
  documentLabel: getDocumentReferenceLabel(asset) || asset.assetId,
7006
- sessionId: sessionId && typeof sessionId === 'string' ? sessionId : null,
7281
+ sessionId: normalizedSessionId || null,
7007
7282
  title: typeof title === 'string' ? title : null,
7008
7283
  comments: normalizedComments,
7009
7284
  actorId: getActiveMcpActorRef(),
@@ -7029,6 +7304,7 @@ export function createTaskforceMcpServer(options = {}) {
7029
7304
  },
7030
7305
  sessionId: currentSession.id,
7031
7306
  sessionAction,
7307
+ documentVersion: currentSession.documentVersion,
7032
7308
  postedCommentIds,
7033
7309
  commentCount: postedCommentIds.length,
7034
7310
  nextTool: 'get_document_reviews',
@@ -7353,13 +7629,6 @@ export function createTaskforceMcpServer(options = {}) {
7353
7629
  actorRef: getActiveMcpActorRef(),
7354
7630
  provisionalLocalReference: shouldUseProvisionalTaskReferences(runtimeMode)
7355
7631
  }), replayed: false };
7356
- if ('operationId' in task) {
7357
- options.onOperationAccepted?.({
7358
- operationId: task.operationId,
7359
- mutationKind: 'create',
7360
- payloadFingerprint: fingerprintWorkspaceSyncPayload(prepared.durableTask || {}),
7361
- });
7362
- }
7363
7632
  recordMcpWriteAudit('create_task', {
7364
7633
  taskId: task.task?.id || null,
7365
7634
  title: task.task?.title || null,
@@ -7478,6 +7747,9 @@ export function createTaskforceMcpServer(options = {}) {
7478
7747
  const inputId = args.id;
7479
7748
  const { id, task: existingTask } = resolveTaskByIdentifierOrThrow(inputId);
7480
7749
  const prepared = taskRootCommandPreparationService.prepareUpdate(args);
7750
+ if (prepared.updates.status === 'review' && existingTask.status !== 'review') {
7751
+ 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);
7752
+ }
7481
7753
  const updated = mutateTaskThroughStatusBoundary({ task: existingTask, updates: prepared.updates });
7482
7754
  if (!updated) {
7483
7755
  throw new Error(`Task ${id} not found`);
@@ -7516,13 +7788,13 @@ export function createTaskforceMcpServer(options = {}) {
7516
7788
  }
7517
7789
  case "finish_task": {
7518
7790
  try {
7519
- const { id: inputId, outcome, comment } = args;
7791
+ const { id: inputId, outcome, comment, reviewer } = args;
7520
7792
  const normalizedOutcome = String(outcome || '').trim().toLowerCase();
7521
7793
  if (normalizedOutcome !== 'done' && normalizedOutcome !== 'review') {
7522
7794
  throw new McpPublicError("outcome must be 'done' or 'review'");
7523
7795
  }
7524
7796
  const { id, task: existingTask } = resolveTaskByIdentifierOrThrow(inputId);
7525
- const result = taskLifecycleCommandService.finish(existingTask, { outcome: normalizedOutcome, comment });
7797
+ const result = taskLifecycleCommandService.finish(existingTask, { outcome: normalizedOutcome, comment, reviewer });
7526
7798
  recordMcpWriteAudit('finish_task', {
7527
7799
  taskId: id,
7528
7800
  outcome: normalizedOutcome,
@@ -7560,7 +7832,7 @@ export function createTaskforceMcpServer(options = {}) {
7560
7832
  const { id: inputId, status, reason } = args;
7561
7833
  const { id, task } = resolveTaskByIdentifierOrThrow(inputId);
7562
7834
  const isCancelled = status === 'cancelled';
7563
- taskLifecycleCommandService.archive(task, { status, reason });
7835
+ const result = taskLifecycleCommandService.archive(task, { status, reason });
7564
7836
  recordMcpWriteAudit('archive_task', { taskId: id, status, cancelled: isCancelled });
7565
7837
  await notifyResourceMutation({
7566
7838
  taskIds: [id],
@@ -7568,7 +7840,15 @@ export function createTaskforceMcpServer(options = {}) {
7568
7840
  includeStats: true,
7569
7841
  });
7570
7842
  return {
7571
- content: [{ type: "text", text: `Task ${formatTaskIdentifier(task)} archived as ${status}` }],
7843
+ content: [{ type: "text", text: JSON.stringify({
7844
+ task: buildTaskWriteSummary(result.task),
7845
+ archive: {
7846
+ archived: true,
7847
+ status: result.status,
7848
+ cancelled: result.cancelled,
7849
+ reason: isCancelled ? String(reason || '').trim() || null : null,
7850
+ },
7851
+ }, null, 2) }],
7572
7852
  };
7573
7853
  }
7574
7854
  case "get_config": {
@@ -7791,8 +8071,9 @@ export function createTaskforceMcpServer(options = {}) {
7791
8071
  case "unarchive_task": {
7792
8072
  const { id: inputId } = args;
7793
8073
  const { id, task: existingTask } = resolveTaskByIdentifierOrThrow(inputId, { notFoundMessage: buildTaskLookupFailureMessage(String(inputId || '').trim()) });
8074
+ let result;
7794
8075
  try {
7795
- taskLifecycleCommandService.unarchive(existingTask);
8076
+ result = taskLifecycleCommandService.unarchive(existingTask);
7796
8077
  }
7797
8078
  catch (error) {
7798
8079
  if (error instanceof TaskLifecycleCommandError && error.code === 'TASK_NOT_FOUND') {
@@ -7807,7 +8088,10 @@ export function createTaskforceMcpServer(options = {}) {
7807
8088
  includeStats: true,
7808
8089
  });
7809
8090
  return {
7810
- content: [{ type: "text", text: `Task ${formatTaskIdentifier(existingTask)} unarchived` }],
8091
+ content: [{ type: "text", text: JSON.stringify({
8092
+ task: buildTaskWriteSummary(result.task),
8093
+ unarchive: { unarchived: true },
8094
+ }, null, 2) }],
7811
8095
  };
7812
8096
  }
7813
8097
  case "bulk_archive": {
@@ -7906,6 +8190,14 @@ export function createTaskforceMcpServer(options = {}) {
7906
8190
  update.complexity = resolveNumberOption('complexity', update.complexity, COMPLEXITY_OPTIONS);
7907
8191
  }
7908
8192
  const preview = core.bulkUpdateFieldsPreview(resolvedList, ACTIVE_WORKSPACE_ID);
8193
+ for (const item of preview.toApply) {
8194
+ if (item.fields.status !== 'review')
8195
+ continue;
8196
+ const existingTask = core.getTask(item.id, ACTIVE_WORKSPACE_ID);
8197
+ if (existingTask?.status !== 'review') {
8198
+ 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);
8199
+ }
8200
+ }
7909
8201
  const forceStatusBoundary = (durableTaskMutationRouter.enabled
7910
8202
  && preview.toApply.some((item) => Object.prototype.hasOwnProperty.call(item.fields, 'status')));
7911
8203
  const durableBatch = forceStatusBoundary || durableTaskRootMutationRouter.metadataEnabled;
@@ -8029,8 +8321,8 @@ export function createTaskforceMcpServer(options = {}) {
8029
8321
  };
8030
8322
  const toolHandlers = new Map(registeredTools.map((tool) => [tool.name, tool.handler]));
8031
8323
  const mcpInputValidator = createMcpInputValidator();
8032
- server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
8033
- const { name, arguments: args } = resolveMcpToolCallCompatibility(request.params.name, request.params.arguments);
8324
+ const callTool = async (params, context = {}) => {
8325
+ const { name, arguments: args } = resolveMcpToolCallCompatibility(params.name, params.arguments);
8034
8326
  const toolStartedAtMs = Date.now();
8035
8327
  let preparationMs = 0;
8036
8328
  let validationMs = 0;
@@ -8073,11 +8365,11 @@ export function createTaskforceMcpServer(options = {}) {
8073
8365
  }
8074
8366
  });
8075
8367
  let result;
8076
- if (extra.requestId === undefined || extra.requestId === null) {
8368
+ if (context.requestId === undefined || context.requestId === null) {
8077
8369
  result = await execute();
8078
8370
  }
8079
8371
  else {
8080
- const transportSessionId = String(extra.sessionId || '').trim();
8372
+ const transportSessionId = String(context.sessionId || '').trim();
8081
8373
  const requestIdentity = operationIdentityScope
8082
8374
  ? fingerprintWorkspaceSyncPayload({
8083
8375
  kind: 'shared-mcp-request',
@@ -8086,18 +8378,18 @@ export function createTaskforceMcpServer(options = {}) {
8086
8378
  || options.auth?.mcpConnectionId
8087
8379
  || options.auth?.mcpClientId
8088
8380
  || 'mcp',
8089
- requestId: { type: typeof extra.requestId, value: extra.requestId },
8381
+ requestId: { type: typeof context.requestId, value: context.requestId },
8090
8382
  tool: name,
8091
8383
  })
8092
8384
  : [
8093
8385
  ...(runtimeMode === 'local' || !transportSessionId ? [runtimeRequestNamespace] : []),
8094
8386
  String(transportSessionId || options.auth?.mcpConnectionId || options.auth?.mcpClientId || 'mcp'),
8095
- String(extra.requestId),
8387
+ String(context.requestId),
8096
8388
  name,
8097
8389
  ].join(':');
8098
8390
  const logicalCloudCreateIdentity = [
8099
8391
  String(transportSessionId || options.auth?.mcpConnectionId || options.auth?.mcpClientId || 'mcp'),
8100
- String(extra.requestId),
8392
+ String(context.requestId),
8101
8393
  name,
8102
8394
  ].join(':');
8103
8395
  result = await cloudCreateRequestIdentity.run(logicalCloudCreateIdentity, () => mcpRequestIdentity.run(requestIdentity, execute));
@@ -8116,14 +8408,14 @@ export function createTaskforceMcpServer(options = {}) {
8116
8408
  responseBytes = measureToolResultBytes(result);
8117
8409
  return result;
8118
8410
  }
8119
- const result = buildUnexpectedToolErrorResult(auditRequestId || extra.requestId);
8411
+ const result = buildUnexpectedToolErrorResult(auditRequestId || context.requestId);
8120
8412
  responseBytes = measureToolResultBytes(result);
8121
8413
  return result;
8122
8414
  }
8123
8415
  finally {
8124
8416
  try {
8125
8417
  options.onToolCallTiming?.({
8126
- requestId: String(auditRequestId || extra.requestId || '').trim() || null,
8418
+ requestId: String(auditRequestId || context.requestId || '').trim() || null,
8127
8419
  toolName: name,
8128
8420
  isWrite: isMcpWriteToolName(name),
8129
8421
  outcome: timingOutcome,
@@ -8139,7 +8431,11 @@ export function createTaskforceMcpServer(options = {}) {
8139
8431
  // Observability must never change tool behavior.
8140
8432
  }
8141
8433
  }
8142
- });
8434
+ };
8435
+ server.setRequestHandler('tools/call', (request, ctx) => callTool(request.params, {
8436
+ requestId: ctx.mcpReq.id,
8437
+ sessionId: ctx.sessionId,
8438
+ }));
8143
8439
  return {
8144
8440
  server,
8145
8441
  core,
@@ -8149,6 +8445,16 @@ export function createTaskforceMcpServer(options = {}) {
8149
8445
  tenantId,
8150
8446
  version: MCP_SERVER_VERSION,
8151
8447
  toolProfile: activeToolProfile,
8448
+ listTools,
8449
+ callTool,
8450
+ };
8451
+ }
8452
+ export async function connectMcpRuntimeToStdio(runtime, transport = new StdioServerTransport()) {
8453
+ await runtime.server.connect(transport);
8454
+ return {
8455
+ ...runtime,
8456
+ transport,
8457
+ close: () => runtime.server.close(),
8152
8458
  };
8153
8459
  }
8154
8460
  export async function startTaskforceMcpStdioServer(options = {}) {
@@ -8164,18 +8470,18 @@ export async function startTaskforceMcpStdioServer(options = {}) {
8164
8470
  projectRoot,
8165
8471
  tenantId,
8166
8472
  core,
8473
+ resourceSubscriptionMode: options.resourceSubscriptionMode ?? 'legacy-long-lived',
8167
8474
  auth: {
8168
8475
  ...envAuth,
8169
8476
  ...(options.auth || {}),
8170
8477
  },
8171
8478
  });
8172
- const transport = new StdioServerTransport();
8173
- await runtime.server.connect(transport);
8479
+ const connected = await connectMcpRuntimeToStdio(runtime);
8174
8480
  const close = createTaskforceMcpStdioClose({
8175
- closeServer: () => runtime.server.close(),
8481
+ closeServer: connected.close,
8176
8482
  ...(ownsCore ? { closeOwnedCore: () => core.close() } : {}),
8177
8483
  });
8178
- return { ...runtime, transport, close };
8484
+ return { ...runtime, transport: connected.transport, close };
8179
8485
  }
8180
8486
  catch (error) {
8181
8487
  try {