@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.
Files changed (485) hide show
  1. package/dist/Taskforce.module.css +523 -79
  2. package/dist/TaskforceCore.js +237 -230
  3. package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.js +1 -1
  4. package/dist/cli.js +34 -0
  5. package/dist/cliCloudTargetGuard.d.ts +5 -0
  6. package/dist/cliCloudTargetGuard.js +11 -0
  7. package/dist/components/features/AiProfilesModule.js +3 -5
  8. package/dist/components/features/AnnotatedAttachmentWorkspace.js +24 -8
  9. package/dist/components/features/TaskSettings.d.ts +12 -1
  10. package/dist/components/features/TaskSettings.js +217 -54
  11. package/dist/components/features/TaskforceAgentsModule.d.ts +1 -2
  12. package/dist/components/features/TaskforceAgentsModule.js +586 -116
  13. package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +1 -1
  14. package/dist/components/features/agentConnections/AgentConnectionControl.d.ts +15 -0
  15. package/dist/components/features/agentConnections/AgentConnectionControl.js +125 -0
  16. package/dist/components/features/agentConnections/ModelConnectionsPanel.d.ts +18 -0
  17. package/dist/components/features/agentConnections/ModelConnectionsPanel.js +393 -0
  18. package/dist/components/features/agentConnections/modelConnectionsApi.d.ts +65 -0
  19. package/dist/components/features/agentConnections/modelConnectionsApi.js +129 -0
  20. package/dist/components/features/planning/PlanningModule.js +2 -5
  21. package/dist/components/features/planning/PlanningTaskOrderList.d.ts +1 -1
  22. package/dist/components/features/planning/PlanningTaskOrderList.js +17 -6
  23. package/dist/components/features/planning/planningTaskArrangement.d.ts +1 -0
  24. package/dist/components/features/planning/planningTaskArrangement.js +5 -5
  25. package/dist/components/features/taskforceAgentAssignments/AgentBehaviorAssignments.js +15 -24
  26. package/dist/components/features/taskforceAgentEditorModel.d.ts +2 -1
  27. package/dist/components/features/taskforceAgentEditorModel.js +5 -1
  28. package/dist/components/features/workflowManager/workflowManagerApi.d.ts +1 -1
  29. package/dist/components/features/workflowManager/workflowManagerApi.js +3 -0
  30. package/dist/components/task/TaskCard.d.ts +5 -0
  31. package/dist/components/task/TaskCard.js +65 -10
  32. package/dist/components/task/TaskImageReviews.js +14 -5
  33. package/dist/components/task/TaskKanban.d.ts +6 -1
  34. package/dist/components/task/TaskKanban.js +152 -6
  35. package/dist/components/task/TaskList.d.ts +5 -0
  36. package/dist/components/task/TaskList.js +3 -3
  37. package/dist/components/task/TaskWorkflowAssignmentField.js +68 -10
  38. package/dist/components/ui/PasswordInput.d.ts +6 -0
  39. package/dist/components/ui/PasswordInput.js +11 -0
  40. package/dist/components/ui/TopNoticeLayer.js +1 -1
  41. package/dist/components/views/AppShellHeader.js +32 -11
  42. package/dist/components/views/PlanComparisonPage.js +12 -0
  43. package/dist/components/views/StandaloneLayout.js +32 -55
  44. package/dist/components/views/WidgetView.js +3 -3
  45. package/dist/components/views/panels/PlanningDrawer.js +5 -12
  46. package/dist/components/views/standalone/modals/AccountSettingsModal.js +26 -17
  47. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +4 -3
  48. package/dist/components/views/standalone/modals/SyncStatusModal.js +33 -38
  49. package/dist/config/envSchema.js +60 -1
  50. package/dist/config/localServiceRuntimeContract.d.ts +13 -0
  51. package/dist/config/localServiceRuntimeContract.js +135 -0
  52. package/dist/core/AiProfileService.js +46 -8
  53. package/dist/core/DeletedTaskLifecycleService.d.ts +1 -0
  54. package/dist/core/DeletedTaskLifecycleService.js +37 -6
  55. package/dist/core/McpTokenService.d.ts +23 -1
  56. package/dist/core/McpTokenService.js +93 -10
  57. package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +3 -2
  58. package/dist/core/TaskAttachmentLinksDurableMutationService.js +33 -9
  59. package/dist/core/TaskChecklistCommandService.d.ts +27 -0
  60. package/dist/core/TaskChecklistCommandService.js +83 -5
  61. package/dist/core/TaskLifecycleCommandService.d.ts +3 -1
  62. package/dist/core/TaskLifecycleCommandService.js +6 -2
  63. package/dist/core/Taskforce.d.ts +94 -2
  64. package/dist/core/Taskforce.js +760 -36
  65. package/dist/core/types.d.ts +14 -0
  66. package/dist/documentReviews/commandService.d.ts +2 -1
  67. package/dist/documentReviews/commandService.js +6 -3
  68. package/dist/hooks/sync/orchestratorShared.d.ts +1 -0
  69. package/dist/hooks/sync/orchestratorShared.js +2 -0
  70. package/dist/hooks/sync/useDurableLocalOutboxDrain.d.ts +1 -1
  71. package/dist/hooks/sync/useDurableTaskCreateMutation.js +1 -1
  72. package/dist/hooks/sync/useDurableTaskMetadataMutation.d.ts +1 -0
  73. package/dist/hooks/sync/useDurableTaskMetadataMutation.js +1 -0
  74. package/dist/hooks/sync/useDurableTaskStatusMutation.d.ts +1 -0
  75. package/dist/hooks/sync/useLocalSyncCoordinatorSnapshot.d.ts +3 -0
  76. package/dist/hooks/sync/useLocalSyncCoordinatorSnapshot.js +85 -9
  77. package/dist/hooks/sync/useSyncStatusControls.d.ts +4 -1
  78. package/dist/hooks/sync/useSyncStatusControls.js +47 -11
  79. package/dist/hooks/tasks/taskHttpMutation.js +1 -0
  80. package/dist/hooks/tasks/useCloudTaskSearch.d.ts +12 -0
  81. package/dist/hooks/tasks/useCloudTaskSearch.js +71 -0
  82. package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.d.ts +16 -0
  83. package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.js +197 -0
  84. package/dist/hooks/tasks/useTaskEditorNavigation.d.ts +16 -3
  85. package/dist/hooks/tasks/useTaskEditorNavigation.js +72 -13
  86. package/dist/hooks/tasks/useTaskFormActions.d.ts +6 -0
  87. package/dist/hooks/tasks/useTaskFormActions.js +65 -12
  88. package/dist/hooks/ui/useSettingsModel.d.ts +24 -2
  89. package/dist/hooks/ui/useSettingsModel.js +13 -2
  90. package/dist/hooks/ui/useSettingsPersistence.d.ts +12 -1
  91. package/dist/hooks/ui/useSettingsPersistence.js +74 -47
  92. package/dist/hooks/useFilterSort.d.ts +7 -3
  93. package/dist/hooks/useFilterSort.js +15 -5
  94. package/dist/hooks/useSyncOrchestrator.d.ts +3 -0
  95. package/dist/hooks/useSyncOrchestrator.js +111 -2
  96. package/dist/hooks/useTaskData.d.ts +40 -1
  97. package/dist/hooks/useTaskData.js +240 -9
  98. package/dist/hooks/useTaskMutations.d.ts +12 -2
  99. package/dist/hooks/useTaskMutations.js +266 -52
  100. package/dist/hooks/useTaskforce.d.ts +39 -9
  101. package/dist/hooks/useTaskforce.js +359 -40
  102. package/dist/hooks/useUiNotice.d.ts +4 -0
  103. package/dist/hooks/useUiNotice.js +4 -0
  104. package/dist/mcp/adminWorkspaceRegistrar.js +3 -3
  105. package/dist/mcp/clientRegistry.d.ts +2 -2
  106. package/dist/mcp/clientRegistry.js +1 -1
  107. package/dist/mcp/documentAssetRegistrar.js +53 -41
  108. package/dist/mcp/documentHelpers.d.ts +1 -0
  109. package/dist/mcp/documentHelpers.js +7 -3
  110. package/dist/mcp/durableTaskMutationRouter.d.ts +4 -1
  111. package/dist/mcp/durableTaskMutationRouter.js +10 -0
  112. package/dist/mcp/durableTaskRelationshipMutationRouter.js +4 -0
  113. package/dist/mcp/httpProtocolRouting.d.ts +10 -0
  114. package/dist/mcp/httpProtocolRouting.js +19 -0
  115. package/dist/mcp/localCliHealth.d.ts +2 -2
  116. package/dist/mcp/localCliHealth.js +2 -2
  117. package/dist/mcp/localServiceProxy.d.ts +1 -0
  118. package/dist/mcp/localServiceProxy.js +530 -68
  119. package/dist/mcp/planningAttachmentCommandAdapter.d.ts +3 -1
  120. package/dist/mcp/planningAttachmentCommandAdapter.js +20 -2
  121. package/dist/mcp/runtime.d.ts +54 -79
  122. package/dist/mcp/runtime.js +556 -139
  123. package/dist/mcp/taskPlanningRegistrar.js +64 -42
  124. package/dist/mcp/toolCallCompatibility.js +38 -1
  125. package/dist/mcp/toolCatalog.d.ts +1 -0
  126. package/dist/mcp/toolProfiles.d.ts +2 -0
  127. package/dist/mcp/toolProfiles.js +10 -0
  128. package/dist/mcp/toolRegistry.d.ts +1 -0
  129. package/dist/mcp/toolRegistry.js +1 -0
  130. package/dist/mcp/workflowExecutionRegistrar.js +4 -4
  131. package/dist/migrations/taskSchemaMigrations.d.ts +5 -0
  132. package/dist/migrations/taskSchemaMigrations.js +427 -0
  133. package/dist/runtime/appGateDefinitions.d.ts +30 -6
  134. package/dist/runtime/appGateDefinitions.js +6 -6
  135. package/dist/runtime/appGates.d.ts +13 -13
  136. package/dist/runtime/appGates.js +36 -13
  137. package/dist/runtime/nodeAppGates.d.ts +8 -0
  138. package/dist/runtime/nodeAppGates.js +27 -0
  139. package/dist/server/cloudCollectionRead.d.ts +1 -1
  140. package/dist/server/cloudCollectionRead.js +2 -2
  141. package/dist/server/index.d.ts +6 -0
  142. package/dist/server/index.js +123 -14
  143. package/dist/server/localDatabaseIdentity.js +4 -1
  144. package/dist/server/localServiceLease.d.ts +3 -0
  145. package/dist/server/localServiceLease.js +11 -2
  146. package/dist/server/localServiceSessions.d.ts +14 -17
  147. package/dist/server/localServiceSessions.js +308 -94
  148. package/dist/server/localWorkspaceSyncServerRuntime.js +12 -7
  149. package/dist/server/rateLimit.d.ts +1 -0
  150. package/dist/server/rateLimit.js +4 -0
  151. package/dist/server/realtimeSyncWs.js +36 -12
  152. package/dist/server/routes/admin.d.ts +4 -0
  153. package/dist/server/routes/admin.js +113 -3
  154. package/dist/server/routes/agentRoles.js +3 -2
  155. package/dist/server/routes/agentSkills.js +3 -2
  156. package/dist/server/routes/agents.d.ts +7 -0
  157. package/dist/server/routes/agents.js +394 -39
  158. package/dist/server/routes/annotatedAttachments.js +3 -2
  159. package/dist/server/routes/auth.js +3 -1
  160. package/dist/server/routes/billing.js +5 -1
  161. package/dist/server/routes/durableTaskHttpRouters.d.ts +11 -2
  162. package/dist/server/routes/executorPhaseAApprovals.d.ts +19 -0
  163. package/dist/server/routes/executorPhaseAApprovals.js +140 -0
  164. package/dist/server/routes/executorPhaseAIssuerRedemptions.d.ts +11 -0
  165. package/dist/server/routes/executorPhaseAIssuerRedemptions.js +24 -0
  166. package/dist/server/routes/githubReviewEvidence.d.ts +19 -0
  167. package/dist/server/routes/githubReviewEvidence.js +116 -0
  168. package/dist/server/routes/localService.d.ts +15 -4
  169. package/dist/server/routes/localService.js +138 -40
  170. package/dist/server/routes/modelConnections.d.ts +24 -0
  171. package/dist/server/routes/modelConnections.js +205 -0
  172. package/dist/server/routes/primitives.d.ts +2 -0
  173. package/dist/server/routes/shared.d.ts +19 -0
  174. package/dist/server/routes/shared.js +95 -19
  175. package/dist/server/routes/sync.js +1 -0
  176. package/dist/server/routes/syncAuxRoutes.js +9 -2
  177. package/dist/server/routes/syncPullApplyRoutes.js +23 -4
  178. package/dist/server/routes/syncPushRoutes.d.ts +1 -0
  179. package/dist/server/routes/syncPushRoutes.js +1 -0
  180. package/dist/server/routes/syncV3FeedRoutes.js +27 -16
  181. package/dist/server/routes/syncV3LocalCaptureRoutes.js +28 -2
  182. package/dist/server/routes/syncV3LocalFeedApplyRoutes.d.ts +1 -0
  183. package/dist/server/routes/syncV3LocalFeedApplyRoutes.js +26 -0
  184. package/dist/server/routes/syncV3OperationRoutes.js +45 -9
  185. package/dist/server/routes/tasks.js +124 -13
  186. package/dist/server/routes/workflows.js +9 -1
  187. package/dist/server/routes/workspaces.js +43 -1
  188. package/dist/server/routes.js +59 -0
  189. package/dist/server/workspaceDeletionAdmission.d.ts +23 -0
  190. package/dist/server/workspaceDeletionAdmission.js +50 -0
  191. package/dist/service/localServiceCli.js +16 -0
  192. package/dist/service/localServiceClientLifecycle.js +36 -10
  193. package/dist/services/codexAppServerModelGateway.d.ts +161 -0
  194. package/dist/services/codexAppServerModelGateway.js +663 -0
  195. package/dist/services/codexDeviceCodeLoginManager.d.ts +39 -0
  196. package/dist/services/codexDeviceCodeLoginManager.js +265 -0
  197. package/dist/services/codexModelProviderConnectionLifecycle.d.ts +8 -0
  198. package/dist/services/codexModelProviderConnectionLifecycle.js +73 -0
  199. package/dist/services/executorPhaseAApprovalRuntime.d.ts +14 -0
  200. package/dist/services/executorPhaseAApprovalRuntime.js +24 -0
  201. package/dist/services/executorPhaseAApprovalService.d.ts +40 -0
  202. package/dist/services/executorPhaseAApprovalService.js +609 -0
  203. package/dist/services/executorPhaseADriftEvaluator.d.ts +78 -0
  204. package/dist/services/executorPhaseADriftEvaluator.js +93 -0
  205. package/dist/services/executorPhaseAIssuerCore.d.ts +126 -0
  206. package/dist/services/executorPhaseAIssuerCore.js +295 -0
  207. package/dist/services/executorPhaseAIssuerRedemptionService.d.ts +34 -0
  208. package/dist/services/executorPhaseAIssuerRedemptionService.js +158 -0
  209. package/dist/services/githubReviewEvidenceRuntime.d.ts +14 -0
  210. package/dist/services/githubReviewEvidenceRuntime.js +30 -0
  211. package/dist/services/githubReviewEvidenceService.d.ts +93 -0
  212. package/dist/services/githubReviewEvidenceService.js +336 -0
  213. package/dist/services/modelGateway.d.ts +7 -0
  214. package/dist/services/modelProviderConnectionService.d.ts +71 -0
  215. package/dist/services/modelProviderConnectionService.js +279 -0
  216. package/dist/services/modelProviderCredentialHome.d.ts +32 -0
  217. package/dist/services/modelProviderCredentialHome.js +271 -0
  218. package/dist/services/ownerScopedModelConnectionGateway.d.ts +12 -0
  219. package/dist/services/ownerScopedModelConnectionGateway.js +30 -0
  220. package/dist/services/taskforceAgentConnectionBindingService.d.ts +66 -0
  221. package/dist/services/taskforceAgentConnectionBindingService.js +185 -0
  222. package/dist/services/taskforceAgentMcpBridge.d.ts +8 -2
  223. package/dist/services/taskforceAgentMcpBridge.js +30 -27
  224. package/dist/shared/adminEdgeProxy.d.ts +5 -0
  225. package/dist/shared/adminEdgeProxy.js +94 -0
  226. package/dist/shared/adminProxySessionCookies.d.ts +16 -0
  227. package/dist/shared/adminProxySessionCookies.js +86 -0
  228. package/dist/shared/aiProfileColors.d.ts +31 -1
  229. package/dist/shared/aiProfileColors.js +14 -6
  230. package/dist/shared/executorPhaseAApprovalGoldenVector.json +269 -0
  231. package/dist/shared/executorPhaseAApprovalReceipt.d.ts +112 -0
  232. package/dist/shared/executorPhaseAApprovalReceipt.js +58 -0
  233. package/dist/shared/executorPhaseAApprovalTrustPolicy.json +10 -0
  234. package/dist/shared/executorPhaseADriftContracts.d.ts +37 -0
  235. package/dist/shared/executorPhaseADriftContracts.js +63 -0
  236. package/dist/shared/executorPhaseAIssuerContracts.d.ts +75 -0
  237. package/dist/shared/executorPhaseAIssuerContracts.js +90 -0
  238. package/dist/shared/executorPhaseARunAuthorization.d.ts +120 -0
  239. package/dist/shared/executorPhaseARunAuthorization.js +232 -0
  240. package/dist/shared/executorPhaseASchemaValidation.d.ts +7 -0
  241. package/dist/shared/executorPhaseASchemaValidation.js +53 -0
  242. package/dist/shared/executorPhaseASchemas/approval-receipt.schema.json +106 -0
  243. package/dist/shared/executorPhaseASchemas/drift-attestation.schema.json +54 -0
  244. package/dist/shared/executorPhaseASchemas/drift-request.schema.json +47 -0
  245. package/dist/shared/executorPhaseASchemas/foundation-manifest.schema.json +29 -0
  246. package/dist/shared/executorPhaseASchemas/issuer-audit-record.schema.json +24 -0
  247. package/dist/shared/executorPhaseASchemas/issuer-proof.schema.json +43 -0
  248. package/dist/shared/executorPhaseASchemas/issuer-redemption-request.schema.json +18 -0
  249. package/dist/shared/executorPhaseASchemas/issuer-redemption-response.schema.json +39 -0
  250. package/dist/shared/executorPhaseASchemas/logical-envelope.schema.json +149 -0
  251. package/dist/shared/executorPhaseASchemas/preflight-report.schema.json +216 -0
  252. package/dist/shared/executorPhaseASchemas/run-capability.schema.json +111 -0
  253. package/dist/shared/executorPhaseASchemas/sender-proof.schema.json +37 -0
  254. package/dist/shared/executorPhaseASchemas/workload-grant.schema.json +68 -0
  255. package/dist/shared/executorPhaseASchemas/workload-jwt-claims.schema.json +67 -0
  256. package/dist/shared/executorPhaseATiming.d.ts +1 -0
  257. package/dist/shared/executorPhaseATiming.js +1 -0
  258. package/dist/shared/planningIdentity.js +1 -1
  259. package/dist/shared/productLogoKeys.d.ts +2 -0
  260. package/dist/shared/productLogoKeys.js +18 -0
  261. package/dist/shared/taskforceAgentDefinition.d.ts +0 -1
  262. package/dist/shared/taskforceAgentDefinition.js +6 -3
  263. package/dist/shared/taskforceAgentModels.d.ts +1 -0
  264. package/dist/shared/taskforceAgentModels.js +36 -3
  265. package/dist/shared/userFeatureGrants.d.ts +4 -0
  266. package/dist/shared/userFeatureGrants.js +7 -0
  267. package/dist/storage/executorPhaseAApprovalReceiptStore.d.ts +37 -0
  268. package/dist/storage/executorPhaseAApprovalReceiptStore.js +81 -0
  269. package/dist/storage/executorPhaseARunAuthorizationStore.d.ts +103 -0
  270. package/dist/storage/executorPhaseARunAuthorizationStore.js +492 -0
  271. package/dist/storage/githubReviewEvidenceInstallationStore.d.ts +26 -0
  272. package/dist/storage/githubReviewEvidenceInstallationStore.js +89 -0
  273. package/dist/storage/modelProviderConnectionStore.d.ts +99 -0
  274. package/dist/storage/modelProviderConnectionStore.js +446 -0
  275. package/dist/storage/postgresAdapter.d.ts +24 -3
  276. package/dist/storage/postgresAdapter.js +117 -14
  277. package/dist/storage/postgresWorker.js +22 -5
  278. package/dist/storage/postgresWorkerProtocol.d.ts +30 -0
  279. package/dist/storage/postgresWorkerProtocol.js +30 -0
  280. package/dist/storage/taskforceAgentConnectionBindingStore.d.ts +31 -0
  281. package/dist/storage/taskforceAgentConnectionBindingStore.js +84 -0
  282. package/dist/sync/cloudSyncApi.d.ts +4 -1
  283. package/dist/sync/cloudSyncApi.js +7 -2
  284. package/dist/sync/contentSyncState.d.ts +1 -1
  285. package/dist/sync/coordinator/localSyncExecutionPermitStore.d.ts +1 -0
  286. package/dist/sync/coordinator/localSyncExecutionPermitStore.js +67 -30
  287. package/dist/sync/coordinator/localWorkspaceSyncPendingV2PushProgress.d.ts +10 -0
  288. package/dist/sync/coordinator/localWorkspaceSyncPendingV2PushProgress.js +29 -0
  289. package/dist/sync/coordinator/localWorkspaceSyncRunner.js +6 -2
  290. package/dist/sync/coordinator/localWorkspaceSyncRunnerState.d.ts +15 -1
  291. package/dist/sync/coordinator/localWorkspaceSyncRunnerState.js +99 -2
  292. package/dist/sync/coordinator/workspaceSyncCredentialBroker.d.ts +1 -0
  293. package/dist/sync/coordinator/workspaceSyncCredentialBroker.js +33 -4
  294. package/dist/sync/engine/workspaceSyncContentLifecycleAdapter.d.ts +2 -1
  295. package/dist/sync/engine/workspaceSyncContentVersionStore.d.ts +38 -0
  296. package/dist/sync/engine/workspaceSyncContentVersionStore.js +50 -1
  297. package/dist/sync/engine/workspaceSyncEngineBrowserGatewayRemoteServices.js +20 -2
  298. package/dist/sync/engine/workspaceSyncEngineLifecyclePolicy.js +6 -6
  299. package/dist/sync/engine/workspaceSyncEngineLocalServices.d.ts +4 -0
  300. package/dist/sync/engine/workspaceSyncEngineLocalServices.js +11 -0
  301. package/dist/sync/engine/workspaceSyncEnginePushCompletion.d.ts +14 -0
  302. package/dist/sync/engine/workspaceSyncEnginePushCompletion.js +84 -0
  303. package/dist/sync/engine/workspaceSyncEngineRecovery.js +1 -0
  304. package/dist/sync/engine/workspaceSyncEngineRemoteServices.d.ts +11 -0
  305. package/dist/sync/engine/workspaceSyncEngineRemoteServices.js +2 -1
  306. package/dist/sync/engine/workspaceSyncEngineRuntime.d.ts +1 -0
  307. package/dist/sync/engine/workspaceSyncEngineRuntime.js +1 -0
  308. package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +31 -4
  309. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.d.ts +3 -1
  310. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +134 -43
  311. package/dist/sync/engine/workspaceSyncEngineServerSnapshotReader.js +6 -0
  312. package/dist/sync/engine/workspaceSyncEngineTransfers.d.ts +1 -1
  313. package/dist/sync/engine/workspaceSyncEngineTransfers.js +11 -4
  314. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.d.ts +1 -0
  315. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +29 -0
  316. package/dist/sync/syncApplyHandlers.d.ts +2 -0
  317. package/dist/sync/syncApplyHandlers.js +10 -1
  318. package/dist/sync/syncService.d.ts +17 -0
  319. package/dist/sync/syncService.js +87 -19
  320. package/dist/sync/taskforceAgentSyncAuthorization.d.ts +16 -0
  321. package/dist/sync/taskforceAgentSyncAuthorization.js +20 -0
  322. package/dist/sync/v3/combinedFeedCapability.js +8 -0
  323. package/dist/sync/v3/durableInitiativeMutationRouter.js +18 -10
  324. package/dist/sync/v3/durableOperationReplay.d.ts +14 -0
  325. package/dist/sync/v3/durableOperationReplay.js +18 -0
  326. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.d.ts +2 -0
  327. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +14 -1
  328. package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +4 -1
  329. package/dist/sync/v3/durableTaskChecklistMutationRouter.js +62 -15
  330. package/dist/sync/v3/durableTaskCommentMutationRouter.js +3 -0
  331. package/dist/sync/v3/durableTaskHttpBulkMutationRouter.d.ts +1 -0
  332. package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +30 -8
  333. package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +5 -3
  334. package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +37 -2
  335. package/dist/sync/v3/durableTaxonomyMutationRouter.js +5 -0
  336. package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.d.ts +11 -0
  337. package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.js +39 -10
  338. package/dist/sync/v3/durableWorkstreamMutationRouter.js +18 -10
  339. package/dist/sync/v3/durableWorkstreamTaskOrderMutationRouter.js +16 -5
  340. package/dist/sync/v3/localOutboxDispatchRuntime.js +2 -2
  341. package/dist/sync/v3/localTaskAttachmentLinksOutboxService.d.ts +2 -0
  342. package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +47 -7
  343. package/dist/sync/v3/localTaskChecklistOutboxHandler.js +16 -2
  344. package/dist/sync/v3/localTaskChecklistOutboxService.d.ts +4 -0
  345. package/dist/sync/v3/localTaskChecklistOutboxService.js +14 -1
  346. package/dist/sync/v3/localTaskCreateOutboxService.js +15 -6
  347. package/dist/sync/v3/localTaskMetadataClient.d.ts +1 -0
  348. package/dist/sync/v3/localTaskMetadataClient.js +1 -0
  349. package/dist/sync/v3/localTaskMetadataOutboxHandler.js +5 -1
  350. package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -0
  351. package/dist/sync/v3/localTaskMetadataOutboxService.js +7 -0
  352. package/dist/sync/v3/localTaskRestoreOutboxService.js +14 -6
  353. package/dist/sync/v3/localTaskStatusClient.d.ts +1 -0
  354. package/dist/sync/v3/localTaskStatusClient.js +6 -1
  355. package/dist/sync/v3/localTaskStatusOutboxService.d.ts +10 -0
  356. package/dist/sync/v3/localTaskStatusOutboxService.js +272 -24
  357. package/dist/sync/v3/syncDurabilityStore.d.ts +41 -0
  358. package/dist/sync/v3/syncDurabilityStore.js +199 -13
  359. package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +6 -4
  360. package/dist/sync/v3/taskAttachmentLinksMutationService.js +19 -10
  361. package/dist/sync/v3/taskChecklistMutationService.d.ts +15 -2
  362. package/dist/sync/v3/taskChecklistMutationService.js +48 -1
  363. package/dist/sync/v3/taskCreateMutationService.d.ts +2 -2
  364. package/dist/sync/v3/taskCreateMutationService.js +7 -4
  365. package/dist/sync/v3/taskMetadataMutationService.d.ts +3 -0
  366. package/dist/sync/v3/taskMetadataMutationService.js +4 -0
  367. package/dist/sync/v3/taskRestoreMutationService.d.ts +2 -2
  368. package/dist/sync/v3/taskRestoreMutationService.js +7 -4
  369. package/dist/sync/v3/taskStatusMutationService.d.ts +31 -1
  370. package/dist/sync/v3/taskStatusMutationService.js +123 -3
  371. package/dist/sync/v3/workflowAssignmentSync.d.ts +6 -0
  372. package/dist/sync/v3/workflowAssignmentSync.js +30 -0
  373. package/dist/sync/v3/workspaceSyncJournalReader.js +1 -1
  374. package/dist/sync/v3/workspaceSyncOperationRetentionService.d.ts +16 -0
  375. package/dist/sync/v3/workspaceSyncOperationRetentionService.js +83 -0
  376. package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.d.ts +1 -1
  377. package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +13 -3
  378. package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.d.ts +11 -0
  379. package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.js +27 -10
  380. package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.d.ts +2 -1
  381. package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.js +52 -10
  382. package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.d.ts +1 -0
  383. package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +18 -3
  384. package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +8 -3
  385. package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +51 -10
  386. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +14 -0
  387. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +49 -25
  388. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +13 -4
  389. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +124 -57
  390. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +20 -0
  391. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +106 -30
  392. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +4 -1
  393. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +34 -0
  394. package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +12 -0
  395. package/dist/sync/v3/workspaceSyncV3FeedProfile.d.ts +1 -1
  396. package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.d.ts +1 -0
  397. package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.js +23 -10
  398. package/dist/sync/v3/workspaceSyncV3NestedTaskApply.d.ts +3 -0
  399. package/dist/sync/v3/workspaceSyncV3NestedTaskApply.js +64 -13
  400. package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.d.ts +9 -0
  401. package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.js +26 -0
  402. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +15 -5
  403. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +50 -14
  404. package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.d.ts +2 -1
  405. package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.js +11 -3
  406. package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +5 -0
  407. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +7 -0
  408. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +55 -9
  409. package/dist/sync/v3/workspaceSyncV3RepairSnapshotRetentionService.d.ts +17 -0
  410. package/dist/sync/v3/workspaceSyncV3RepairSnapshotRetentionService.js +64 -0
  411. package/dist/sync/v3/workspaceSyncV3TaskCommentAudit.js +9 -2
  412. package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.d.ts +7 -0
  413. package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.js +37 -0
  414. package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.d.ts +6 -0
  415. package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.js +9 -0
  416. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverCoverage.d.ts +7 -0
  417. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverCoverage.js +33 -0
  418. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverWireContract.d.ts +7 -0
  419. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverWireContract.js +8 -0
  420. package/dist/sync/workspacePullFeed.js +2 -1
  421. package/dist/sync/workspaceRepair.d.ts +8 -2
  422. package/dist/sync/workspaceRepair.js +222 -6
  423. package/dist/sync/workspaceSyncModel.d.ts +21 -0
  424. package/dist/sync/workspaceSyncModel.js +35 -7
  425. package/dist/sync/workspaceSyncState.d.ts +1 -0
  426. package/dist/sync/workspaceSyncV2SnapshotBuilder.d.ts +6 -0
  427. package/dist/sync/workspaceSyncV2SnapshotBuilder.js +5 -0
  428. package/dist/types.d.ts +47 -1
  429. package/dist/types.js +6 -0
  430. package/dist/ui/assets/AiIdentityRosterCard-OcSz1PkK.js +1 -0
  431. package/dist/ui/assets/AiProfilesModule-ByhYW5Xi.js +1 -0
  432. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BTM6nIg_.js +3 -0
  433. package/dist/ui/assets/AnnotatedAttachmentWorkspace-TzYu7pkG.css +1 -0
  434. package/dist/ui/assets/{AssetTraySortControl-Kf1pelSy.js → AssetTraySortControl-DabEG8L4.js} +1 -1
  435. package/dist/ui/assets/{ContextAttachmentManager-B8boOs6Q.js → ContextAttachmentManager-Coa8yVGM.js} +2 -2
  436. package/dist/ui/assets/{DocumentWorkspace-DXH4XUsB.js → DocumentWorkspace-BB0qhdix.js} +1 -1
  437. package/dist/ui/assets/{EntityActivityTimeline-BtSTq0ON.js → EntityActivityTimeline-CZ8x4UJE.js} +1 -1
  438. package/dist/ui/assets/PlanningModule-pRPjR7v5.js +1 -0
  439. package/dist/ui/assets/PlansPage-D86pZSSl.js +1 -0
  440. package/dist/ui/assets/TaskContextUpload-BVIkU9yT.js +1 -0
  441. package/dist/ui/assets/TaskSettings-Cv1mNhv_.js +12 -0
  442. package/dist/ui/assets/TaskforceAgentsModule-b-8R25cJ.css +1 -0
  443. package/dist/ui/assets/TaskforceAgentsModule-gmH-PD2q.js +24 -0
  444. package/dist/ui/assets/{WorkflowManagerModule-DWFspC-o.js → WorkflowManagerModule-DbfnHJ-f.js} +1 -1
  445. package/dist/ui/assets/index-BZ-hdZSk.css +1 -0
  446. package/dist/ui/assets/index-CTsDBaef.js +7 -0
  447. package/dist/ui/assets/{vendor-icons-BkFLXavV.js → vendor-icons-Bsq-mcEn.js} +1 -1
  448. package/dist/ui/branding/logos/letterhead_dark.png +0 -0
  449. package/dist/ui/branding/logos/letterhead_light.png +0 -0
  450. package/dist/ui/branding/logos/signature_dark.png +0 -0
  451. package/dist/ui/branding/logos/signature_light.png +0 -0
  452. package/dist/ui/index.html +3 -3
  453. package/dist/utils/aiProfileDefaultLogo.js +16 -48
  454. package/dist/utils/httpSetCookie.d.ts +1 -0
  455. package/dist/utils/httpSetCookie.js +4 -1
  456. package/dist/utils/localRuntimeUrl.d.ts +1 -0
  457. package/dist/utils/localRuntimeUrl.js +14 -0
  458. package/dist/utils/productLogoRegistry.d.ts +2 -0
  459. package/dist/utils/productLogoRegistry.js +36 -0
  460. package/dist/utils/syncStatusPresentation.d.ts +4 -0
  461. package/dist/utils/syncStatusPresentation.js +17 -0
  462. package/dist/utils/taskActivity.d.ts +3 -2
  463. package/dist/utils/taskActivity.js +19 -2
  464. package/dist/utils/taskEditorReconciliation.d.ts +40 -0
  465. package/dist/utils/taskEditorReconciliation.js +217 -0
  466. package/dist/utils/taskEvents.d.ts +2 -0
  467. package/dist/utils/taskNormalization.d.ts +1 -0
  468. package/dist/utils/taskNormalization.js +39 -0
  469. package/dist/utils/taskSearch.js +2 -2
  470. package/dist/utils/uiNotice.d.ts +4 -0
  471. package/dist/utils/workspaceSyncPresentation.d.ts +1 -1
  472. package/dist/utils/workspaceSyncPresentation.js +23 -17
  473. package/package.json +36 -6
  474. package/dist/ui/assets/AiIdentityRosterCard-MZs7lVED.js +0 -1
  475. package/dist/ui/assets/AiProfilesModule-C3i0LOxe.js +0 -1
  476. package/dist/ui/assets/AnnotatedAttachmentWorkspace-CBRZ8rca.css +0 -1
  477. package/dist/ui/assets/AnnotatedAttachmentWorkspace-DVZk5Tln.js +0 -3
  478. package/dist/ui/assets/PlanningModule-BPVyEa-f.js +0 -1
  479. package/dist/ui/assets/PlansPage-ChqgxALc.js +0 -1
  480. package/dist/ui/assets/TaskContextUpload-CB3ol0sN.js +0 -1
  481. package/dist/ui/assets/TaskSettings-g4ECKNOz.js +0 -12
  482. package/dist/ui/assets/TaskforceAgentsModule-BazdUwxZ.js +0 -21
  483. package/dist/ui/assets/TaskforceAgentsModule-D6IC0S7Q.css +0 -1
  484. package/dist/ui/assets/index-BUplSsv_.js +0 -7
  485. package/dist/ui/assets/index-gz2EXuoK.css +0 -1
@@ -1,5 +1,5 @@
1
1
  import type { DatabaseAdapter } from '../storage/databaseAdapter.js';
2
- import type { McpOAuthProvider, McpAccessTokenRecord, McpAccessTokenListResult, McpAccessTokenIssueResult, McpAccessTokenAuthResult, McpAccessTokenInspectionResult, McpOAuthClientRecord, McpOAuthConnectorGrantRecord, McpOAuthTokenExchangeResult, McpOAuthAccessTokenAuthResult } from './types.js';
2
+ import type { McpCloudToolProfile, McpOAuthProvider, McpAccessTokenRecord, McpAccessTokenListResult, McpAccessTokenIssueResult, McpAccessTokenAuthResult, McpAccessTokenInspectionResult, McpOAuthClientRecord, McpOAuthConnectorGrantRecord, McpOAuthTokenExchangeResult, McpOAuthAccessTokenAuthResult } from './types.js';
3
3
  export interface McpTokenServiceDeps {
4
4
  /** Resolve plan/account access state for a given user */
5
5
  getAccountAccessStatus: (userId: string) => {
@@ -19,6 +19,8 @@ export declare class McpTokenService {
19
19
  private normalizeAccessRole;
20
20
  private normalizePermissionMode;
21
21
  private normalizeMcpAccessTokenScopes;
22
+ private normalizeMcpCloudToolProfile;
23
+ private requireMcpCloudToolProfile;
22
24
  private createMcpAccessTokenPrefix;
23
25
  private parseMcpAccessToken;
24
26
  private normalizeMcpOAuthProvider;
@@ -38,6 +40,7 @@ export declare class McpTokenService {
38
40
  private resolveMcpAccessTokenStatus;
39
41
  private rowToMcpAccessTokenRecord;
40
42
  private assertCanManageOwnMcpAccessToken;
43
+ private requireMcpToolProfileAdministrator;
41
44
  private countActiveMcpAccessTokens;
42
45
  revokeMcpCredentialsForUserWorkspace(userId: string, workspaceId: string, revokedByUserId?: string | null): void;
43
46
  revokeMcpCredentialsForWorkspace(workspaceId: string, revokedByUserId?: string | null): void;
@@ -52,6 +55,7 @@ export declare class McpTokenService {
52
55
  workspaceId: string;
53
56
  name: string;
54
57
  scopes: unknown;
58
+ toolProfile?: McpCloudToolProfile;
55
59
  expiresAt?: string | null;
56
60
  createdByIp?: string | null;
57
61
  }): McpAccessTokenIssueResult;
@@ -71,6 +75,15 @@ export declare class McpTokenService {
71
75
  tokenId: string;
72
76
  createdByIp?: string | null;
73
77
  }): McpAccessTokenIssueResult;
78
+ updateMcpAccessTokenToolProfile(input: {
79
+ actorUserId: string;
80
+ workspaceId: string;
81
+ tokenId: string;
82
+ toolProfile: unknown;
83
+ }): {
84
+ record: McpAccessTokenRecord;
85
+ previousToolProfile: McpCloudToolProfile;
86
+ };
74
87
  authenticateMcpAccessToken(token: string, workspaceId?: string): McpAccessTokenAuthResult | null;
75
88
  inspectMcpAccessToken(token: string): McpAccessTokenInspectionResult | null;
76
89
  recordMcpAccessTokenUsage(input: {
@@ -116,6 +129,15 @@ export declare class McpTokenService {
116
129
  workspaceId: string;
117
130
  grantId: string;
118
131
  }): McpOAuthConnectorGrantRecord;
132
+ updateMcpOAuthConnectorGrantToolProfile(input: {
133
+ actorUserId: string;
134
+ workspaceId: string;
135
+ grantId: string;
136
+ toolProfile: unknown;
137
+ }): {
138
+ record: McpOAuthConnectorGrantRecord;
139
+ previousToolProfile: McpCloudToolProfile;
140
+ };
119
141
  authenticateMcpOAuthAccessToken(token: string, workspaceId?: string): McpOAuthAccessTokenAuthResult | null;
120
142
  bindMcpOAuthConnectorGrantAiProfile(input: {
121
143
  grantId: string;
@@ -7,6 +7,7 @@ const MCP_ACCESS_TOKEN_DEFAULT_PAGE_SIZE = 20;
7
7
  const MCP_ACCESS_TOKEN_MAX_PAGE_SIZE = 100;
8
8
  const MCP_ACCESS_TOKEN_ALLOWED_SCOPES = ['tasks:read', 'tasks:write', 'workspace:read'];
9
9
  const MCP_OAUTH_AUTH_CODE_TTL_MS = 10 * 60 * 1000;
10
+ const MCP_CLOUD_TOOL_PROFILES = ['default', 'extended'];
10
11
  /**
11
12
  * Handles MCP direct-access tokens and MCP OAuth connector flow
12
13
  * (client registration, authorization codes, token exchange, grant lifecycle).
@@ -52,6 +53,19 @@ export class McpTokenService {
52
53
  }
53
54
  return unique;
54
55
  }
56
+ normalizeMcpCloudToolProfile(value, fallback = 'default') {
57
+ const normalized = String(value || '').trim().toLowerCase();
58
+ return MCP_CLOUD_TOOL_PROFILES.includes(normalized)
59
+ ? normalized
60
+ : fallback;
61
+ }
62
+ requireMcpCloudToolProfile(value) {
63
+ const normalized = String(value || '').trim().toLowerCase();
64
+ if (!MCP_CLOUD_TOOL_PROFILES.includes(normalized)) {
65
+ throw new TaskforceRuleError('toolProfile must be default or extended', 400, 'MCP_TOOL_PROFILE_INVALID');
66
+ }
67
+ return normalized;
68
+ }
55
69
  createMcpAccessTokenPrefix() {
56
70
  return `tfmcp_${randomBytes(6).toString('hex')}`;
57
71
  }
@@ -204,6 +218,7 @@ export class McpTokenService {
204
218
  workspaceId: String(row?.workspace_id || ''),
205
219
  userId: String(row?.user_id || ''),
206
220
  scopes,
221
+ toolProfile: this.normalizeMcpCloudToolProfile(row?.tool_profile),
207
222
  tokenPrefix: String(row?.token_prefix || ''),
208
223
  status: this.resolveMcpOAuthGrantStatus(row),
209
224
  lastUsedAt: row?.last_used_at ? String(row.last_used_at) : null,
@@ -286,6 +301,7 @@ export class McpTokenService {
286
301
  u.email AS user_email,
287
302
  t.name,
288
303
  t.scopes_json,
304
+ t.tool_profile,
289
305
  t.token_prefix,
290
306
  t.token_hash,
291
307
  t.status,
@@ -331,6 +347,7 @@ export class McpTokenService {
331
347
  userEmail: row.user_email ? String(row.user_email) : null,
332
348
  name: String(row.name || ''),
333
349
  scopes,
350
+ toolProfile: this.normalizeMcpCloudToolProfile(row.tool_profile),
334
351
  tokenPrefix: String(row.token_prefix || ''),
335
352
  status: this.resolveMcpAccessTokenStatus(row),
336
353
  lastUsedAt: row.last_used_at ? String(row.last_used_at) : null,
@@ -353,6 +370,12 @@ export class McpTokenService {
353
370
  throw new TaskforceRuleError('Forbidden', 403, 'FORBIDDEN');
354
371
  }
355
372
  }
373
+ requireMcpToolProfileAdministrator(actorUserId, workspaceId) {
374
+ const member = this.getActiveWorkspaceMemberForMcp(actorUserId, workspaceId);
375
+ if (member.role !== 'owner' && member.role !== 'admin') {
376
+ throw new TaskforceRuleError('Workspace owner or admin access is required', 403, 'MCP_TOOL_PROFILE_ADMIN_REQUIRED');
377
+ }
378
+ }
356
379
  countActiveMcpAccessTokens(userId, workspaceId) {
357
380
  const now = new Date().toISOString();
358
381
  const row = this.db.prepare(`
@@ -446,6 +469,7 @@ export class McpTokenService {
446
469
  u.email AS user_email,
447
470
  t.name,
448
471
  t.scopes_json,
472
+ t.tool_profile,
449
473
  t.token_prefix,
450
474
  t.token_hash,
451
475
  t.status,
@@ -480,6 +504,7 @@ export class McpTokenService {
480
504
  throw new TaskforceRuleError('Token name is required', 400, 'MCP_TOKEN_NAME_REQUIRED');
481
505
  }
482
506
  const scopes = this.normalizeMcpAccessTokenScopes(input.scopes);
507
+ const toolProfile = this.normalizeMcpCloudToolProfile(input.toolProfile);
483
508
  const expiresAt = input.expiresAt ? String(input.expiresAt).trim() : null;
484
509
  const now = new Date().toISOString();
485
510
  if (expiresAt && expiresAt <= now) {
@@ -501,12 +526,12 @@ export class McpTokenService {
501
526
  const createdByIp = input.createdByIp ? String(input.createdByIp).trim() : null;
502
527
  this.db.prepare(`
503
528
  INSERT INTO mcp_access_tokens (
504
- id, tenant_id, workspace_id, user_id, name, scopes_json, token_prefix, token_hash, status,
529
+ id, tenant_id, workspace_id, user_id, name, scopes_json, tool_profile, token_prefix, token_hash, status,
505
530
  last_used_at, last_used_ip, last_used_user_agent, created_by_ip, expires_at, revoked_at,
506
531
  revoked_by_user_id, created_at, updated_at
507
532
  )
508
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'active', NULL, NULL, NULL, ?, ?, NULL, NULL, ?, ?)
509
- `).run(tokenId, this.tenantId, member.workspaceId, member.userId, name, JSON.stringify(scopes), tokenPrefix, tokenHash, createdByIp, expiresAt, now, now);
533
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', NULL, NULL, NULL, ?, ?, NULL, NULL, ?, ?)
534
+ `).run(tokenId, this.tenantId, member.workspaceId, member.userId, name, JSON.stringify(scopes), toolProfile, tokenPrefix, tokenHash, createdByIp, expiresAt, now, now);
510
535
  const row = this.getMcpAccessTokenRow(tokenId, member.workspaceId);
511
536
  if (!row) {
512
537
  throw new TaskforceRuleError('Failed to create MCP token', 500, 'MCP_TOKEN_CREATE_FAILED');
@@ -573,18 +598,39 @@ export class McpTokenService {
573
598
  workspaceId: revoked.workspaceId,
574
599
  name: revoked.name,
575
600
  scopes: revoked.scopes,
601
+ toolProfile: revoked.toolProfile,
576
602
  expiresAt: revoked.expiresAt || null,
577
603
  createdByIp: input.createdByIp || null,
578
604
  });
579
605
  });
580
606
  return tx();
581
607
  }
608
+ updateMcpAccessTokenToolProfile(input) {
609
+ this.requireMcpToolProfileAdministrator(input.actorUserId, input.workspaceId);
610
+ const row = this.getMcpAccessTokenRow(input.tokenId, input.workspaceId);
611
+ if (!row) {
612
+ throw new TaskforceRuleError('MCP token not found', 404, 'MCP_TOKEN_NOT_FOUND');
613
+ }
614
+ const previousToolProfile = this.normalizeMcpCloudToolProfile(row.tool_profile);
615
+ const toolProfile = this.requireMcpCloudToolProfile(input.toolProfile);
616
+ const now = new Date().toISOString();
617
+ this.db.prepare(`
618
+ UPDATE mcp_access_tokens
619
+ SET tool_profile = ?, updated_at = ?
620
+ WHERE tenant_id = ? AND workspace_id = ? AND id = ?
621
+ `).run(toolProfile, now, this.tenantId, input.workspaceId, row.id);
622
+ const updated = this.getMcpAccessTokenRow(row.id, input.workspaceId);
623
+ if (!updated) {
624
+ throw new TaskforceRuleError('MCP token not found', 404, 'MCP_TOKEN_NOT_FOUND');
625
+ }
626
+ return { record: this.rowToMcpAccessTokenRecord(updated), previousToolProfile };
627
+ }
582
628
  authenticateMcpAccessToken(token, workspaceId) {
583
629
  const parsed = this.parseMcpAccessToken(token);
584
630
  if (!parsed)
585
631
  return null;
586
632
  const row = this.db.prepare(`
587
- SELECT id, workspace_id, user_id, scopes_json, token_hash, status, expires_at, revoked_at
633
+ SELECT id, workspace_id, user_id, scopes_json, tool_profile, token_hash, status, expires_at, revoked_at
588
634
  FROM mcp_access_tokens
589
635
  WHERE tenant_id = ?
590
636
  AND token_prefix = ?
@@ -612,6 +658,7 @@ export class McpTokenService {
612
658
  workspaceId: row.workspace_id,
613
659
  userId: row.user_id,
614
660
  scopes,
661
+ toolProfile: this.normalizeMcpCloudToolProfile(row.tool_profile),
615
662
  status,
616
663
  };
617
664
  }
@@ -620,7 +667,7 @@ export class McpTokenService {
620
667
  if (!parsed)
621
668
  return null;
622
669
  const row = this.db.prepare(`
623
- SELECT id, workspace_id, user_id, name, scopes_json, token_hash, status, expires_at, revoked_at
670
+ SELECT id, workspace_id, user_id, name, scopes_json, tool_profile, token_hash, status, expires_at, revoked_at
624
671
  FROM mcp_access_tokens
625
672
  WHERE tenant_id = ?
626
673
  AND token_prefix = ?
@@ -644,6 +691,7 @@ export class McpTokenService {
644
691
  userId: row.user_id,
645
692
  name: String(row.name || ''),
646
693
  scopes,
694
+ toolProfile: this.normalizeMcpCloudToolProfile(row.tool_profile),
647
695
  status: this.resolveMcpAccessTokenStatus(row),
648
696
  };
649
697
  }
@@ -783,7 +831,7 @@ export class McpTokenService {
783
831
  const clientName = String(client.client_name || this.defaultMcpOAuthClientName(provider)).trim() || this.defaultMcpOAuthClientName(provider);
784
832
  const tx = this.db.transaction(() => {
785
833
  const existingRows = this.db.prepare(`
786
- SELECT id, ai_profile_id, ai_profile_token, ai_profile_name
834
+ SELECT id, tool_profile, ai_profile_id, ai_profile_token, ai_profile_name
787
835
  FROM mcp_oauth_connector_grants
788
836
  WHERE tenant_id = ?
789
837
  AND provider = ?
@@ -794,6 +842,7 @@ export class McpTokenService {
794
842
  ORDER BY created_at DESC
795
843
  `).all(this.tenantId, provider, String(client.id), String(row.user_id), String(row.workspace_id));
796
844
  const previousBinding = existingRows.find((existing) => existing.ai_profile_id && existing.ai_profile_token && existing.ai_profile_name) || null;
845
+ const toolProfile = this.normalizeMcpCloudToolProfile(existingRows[0]?.tool_profile);
797
846
  const revokeTime = new Date().toISOString();
798
847
  existingRows.forEach((existing) => {
799
848
  if (!existing?.id)
@@ -819,12 +868,12 @@ export class McpTokenService {
819
868
  const tokenHash = hashMcpOAuthSecret(secret);
820
869
  this.db.prepare(`
821
870
  INSERT INTO mcp_oauth_connector_grants (
822
- id, tenant_id, provider, client_id, client_name, product_client_id, user_id, workspace_id, scopes_json,
871
+ id, tenant_id, provider, client_id, client_name, product_client_id, user_id, workspace_id, scopes_json, tool_profile,
823
872
  token_prefix, token_hash, status, last_used_at, revoked_at, revoked_by_user_id,
824
873
  ai_profile_id, ai_profile_token, ai_profile_name, created_at, updated_at
825
874
  )
826
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', NULL, NULL, NULL, ?, ?, ?, ?, ?)
827
- `).run(grantId, this.tenantId, provider, String(client.id || ''), clientName, client.product_client_id ? String(client.product_client_id) : null, String(row.user_id), String(row.workspace_id), JSON.stringify(scopes), tokenPrefix, tokenHash, previousBinding?.ai_profile_id || null, previousBinding?.ai_profile_token || null, previousBinding?.ai_profile_name || null, now, now);
875
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', NULL, NULL, NULL, ?, ?, ?, ?, ?)
876
+ `).run(grantId, this.tenantId, provider, String(client.id || ''), clientName, client.product_client_id ? String(client.product_client_id) : null, String(row.user_id), String(row.workspace_id), JSON.stringify(scopes), toolProfile, tokenPrefix, tokenHash, previousBinding?.ai_profile_id || null, previousBinding?.ai_profile_token || null, previousBinding?.ai_profile_name || null, now, now);
828
877
  this.db.prepare(`
829
878
  UPDATE mcp_oauth_auth_codes
830
879
  SET used_at = ?
@@ -909,12 +958,45 @@ export class McpTokenService {
909
958
  }
910
959
  return this.rowToMcpOAuthConnectorGrantRecord(updated);
911
960
  }
961
+ updateMcpOAuthConnectorGrantToolProfile(input) {
962
+ this.requireMcpToolProfileAdministrator(input.actorUserId, input.workspaceId);
963
+ const row = this.db.prepare(`
964
+ SELECT *
965
+ FROM mcp_oauth_connector_grants
966
+ WHERE tenant_id = ? AND workspace_id = ? AND id = ?
967
+ LIMIT 1
968
+ `).get(this.tenantId, input.workspaceId, String(input.grantId || '').trim());
969
+ if (!row?.id) {
970
+ throw new TaskforceRuleError('MCP connector grant not found', 404, 'MCP_OAUTH_GRANT_NOT_FOUND');
971
+ }
972
+ const previousToolProfile = this.normalizeMcpCloudToolProfile(row.tool_profile);
973
+ const toolProfile = this.requireMcpCloudToolProfile(input.toolProfile);
974
+ const now = new Date().toISOString();
975
+ this.db.prepare(`
976
+ UPDATE mcp_oauth_connector_grants
977
+ SET tool_profile = ?, updated_at = ?
978
+ WHERE tenant_id = ? AND workspace_id = ? AND id = ?
979
+ `).run(toolProfile, now, this.tenantId, input.workspaceId, String(row.id));
980
+ const updated = this.db.prepare(`
981
+ SELECT *
982
+ FROM mcp_oauth_connector_grants
983
+ WHERE tenant_id = ? AND workspace_id = ? AND id = ?
984
+ LIMIT 1
985
+ `).get(this.tenantId, input.workspaceId, String(row.id));
986
+ if (!updated) {
987
+ throw new TaskforceRuleError('MCP connector grant not found', 404, 'MCP_OAUTH_GRANT_NOT_FOUND');
988
+ }
989
+ return {
990
+ record: this.rowToMcpOAuthConnectorGrantRecord(updated),
991
+ previousToolProfile,
992
+ };
993
+ }
912
994
  authenticateMcpOAuthAccessToken(token, workspaceId) {
913
995
  const parsed = this.parseMcpOAuthOpaqueSecret(token, 'tfmcpoauth_');
914
996
  if (!parsed)
915
997
  return null;
916
998
  const row = this.db.prepare(`
917
- SELECT id, provider, client_id, product_client_id, user_id, workspace_id, scopes_json, token_hash, status, revoked_at,
999
+ SELECT id, provider, client_id, product_client_id, user_id, workspace_id, scopes_json, tool_profile, token_hash, status, revoked_at,
918
1000
  ai_profile_id, ai_profile_token, ai_profile_name
919
1001
  FROM mcp_oauth_connector_grants
920
1002
  WHERE tenant_id = ?
@@ -945,6 +1027,7 @@ export class McpTokenService {
945
1027
  provider: this.normalizeMcpOAuthProvider(row.provider),
946
1028
  productClientId: row?.product_client_id ? String(row.product_client_id) : null,
947
1029
  scopes,
1030
+ toolProfile: this.normalizeMcpCloudToolProfile(row.tool_profile),
948
1031
  status: this.resolveMcpOAuthGrantStatus(row),
949
1032
  aiProfileId: row?.ai_profile_id ? String(row.ai_profile_id) : null,
950
1033
  aiProfileToken: row?.ai_profile_token ? String(row.ai_profile_token) : null,
@@ -8,7 +8,7 @@ export declare class DurableTaskAttachmentLinksApplyError extends Error {
8
8
  constructor(message: string, code: 'TASK_ATTACHMENT_ASSET_NOT_READY' | 'TASK_ATTACHMENT_ASSET_INVALID' | 'TASK_ATTACHMENT_LINK_INVALID', statusCode: number, assetId?: string | undefined);
9
9
  }
10
10
  export interface DurableTaskAttachmentLinksCore {
11
- getWorkspaceAssetStore(): Pick<WorkspaceAssetStore, 'get' | 'listLinksForTask' | 'adoptReadableReferenceNumber'>;
11
+ getWorkspaceAssetStore(): Pick<WorkspaceAssetStore, 'get' | 'listLinksForTask' | 'adoptReadableReferenceNumber' | 'restoreDeleted'>;
12
12
  getWorkspaceAssetDispatchReadiness(assetId: string, workspaceId: string): {
13
13
  ready: boolean;
14
14
  retryable: boolean;
@@ -16,7 +16,7 @@ export interface DurableTaskAttachmentLinksCore {
16
16
  };
17
17
  }
18
18
  export interface DurableWorkspaceTaskAttachmentLinksCore extends DurableTaskAttachmentLinksCore {
19
- getWorkspaceAssetStore(): Pick<WorkspaceAssetStore, 'get' | 'listLinksForTask' | 'listLinksForWorkspace' | 'listAllNormalizedByWorkspace' | 'adoptReadableReferenceNumber'>;
19
+ getWorkspaceAssetStore(): Pick<WorkspaceAssetStore, 'get' | 'listLinksForTask' | 'listLinksForWorkspace' | 'listAllNormalizedByWorkspace' | 'adoptReadableReferenceNumber' | 'restoreDeleted'>;
20
20
  }
21
21
  export declare function resolveTaskCardCoverAssetIdForLinks(input: {
22
22
  currentCardCoverAssetId?: string | null;
@@ -29,6 +29,7 @@ export declare function resolveTaskAttachmentLinksForDurableSync(core: DurableTa
29
29
  requireDispatchReady?: boolean;
30
30
  adoptAuthoritativeReferenceNumber?: boolean;
31
31
  ignoreAssetMetadataSnapshots?: boolean;
32
+ restoreDeletedExternalAssetForAuthoritativeSync?: boolean;
32
33
  }): AttachmentItem[];
33
34
  export declare function normalizeTaskAttachmentDescriptorsForDurableSync(core: DurableTaskAttachmentLinksCore, workspaceId: string, attachments: readonly AttachmentItem[], options?: {
34
35
  requireDispatchReady?: boolean;
@@ -53,6 +53,12 @@ function canonicalRole(asset, requested) {
53
53
  }
54
54
  return expected;
55
55
  }
56
+ function canonicalPersistedRole(asset, persisted) {
57
+ // Historical external links predate the reference-only invariant. Repair
58
+ // projections must publish the current canonical shape, while mutation
59
+ // inputs continue through canonicalRole and remain strictly validated.
60
+ return asset.storageProvider === 'external' ? 'reference' : canonicalRole(asset, persisted);
61
+ }
56
62
  function referenceFields(asset) {
57
63
  if (!asset.referenceNumber)
58
64
  return { referenceNumber: null, referenceLabel: null };
@@ -83,9 +89,13 @@ function assertReady(core, workspaceId, asset, options) {
83
89
  }
84
90
  }
85
91
  function canonicalLink(asset, link, fallbackOrder) {
92
+ // Historical task links could persist an external asset as an attachment.
93
+ // External assets are references by contract, so repair/export must emit the
94
+ // canonical role rather than reproducing an un-installable legacy link.
95
+ const role = asset.storageProvider === 'external' ? 'reference' : link.role;
86
96
  return {
87
97
  assetId: asset.assetId,
88
- role: link.role,
98
+ role,
89
99
  order: Number.isSafeInteger(link.sortOrder) && Number(link.sortOrder) >= 0
90
100
  ? Number(link.sortOrder)
91
101
  : fallbackOrder,
@@ -107,7 +117,7 @@ export function listTaskAttachmentLinksForDurableSync(core, workspaceId, taskId)
107
117
  fail(`Task attachment asset is deleted: ${link.assetId}`, 'TASK_ATTACHMENT_ASSET_INVALID', 400);
108
118
  }
109
119
  assertReady(core, workspaceId, asset);
110
- return canonicalLink(asset, link, index);
120
+ return canonicalLink(asset, { ...link, role: canonicalPersistedRole(asset, link.role) }, index);
111
121
  })
112
122
  .sort((left, right) => left.order - right.order
113
123
  || (left.assetId < right.assetId ? -1 : left.assetId > right.assetId ? 1 : 0)
@@ -139,7 +149,7 @@ export function listWorkspaceTaskAttachmentLinksForDurableSync(core, workspaceId
139
149
  fail(`Task attachment asset is deleted: ${link.assetId}`, 'TASK_ATTACHMENT_ASSET_INVALID', 400);
140
150
  }
141
151
  assertReady(core, workspaceId, asset);
142
- return canonicalLink(asset, link, index);
152
+ return canonicalLink(asset, { ...link, role: canonicalPersistedRole(asset, link.role) }, index);
143
153
  }).sort((left, right) => left.order - right.order
144
154
  || (left.assetId < right.assetId ? -1 : left.assetId > right.assetId ? 1 : 0)
145
155
  || (left.role < right.role ? -1 : left.role > right.role ? 1 : 0))
@@ -156,6 +166,14 @@ export function resolveTaskAttachmentLinksForDurableSync(core, workspaceId, link
156
166
  if (!asset) {
157
167
  fail(`Task attachment asset is unavailable because it has not reached this runtime: ${link.assetId}`, 'TASK_ATTACHMENT_ASSET_NOT_READY', 425, link.assetId);
158
168
  }
169
+ if (asset.deletedAt
170
+ && asset.storageProvider === 'external'
171
+ && options?.restoreDeletedExternalAssetForAuthoritativeSync === true) {
172
+ // External pointers do not travel through binary content hydration.
173
+ // An active authoritative link is the lifecycle proof needed to
174
+ // revive the existing local placeholder inside the apply transaction.
175
+ asset = store.restoreDeleted(asset.assetId, workspaceId, asset.updatedAt) || asset;
176
+ }
159
177
  assertReady(core, workspaceId, asset, options);
160
178
  // Replicated apply may align independently hydrated local D-/I- numbers
161
179
  // to cloud authority. Ordinary local/cloud mutation writers must never
@@ -175,8 +193,8 @@ export function resolveTaskAttachmentLinksForDurableSync(core, workspaceId, link
175
193
  const displayName = String(link.displayName
176
194
  || (options?.ignoreAssetMetadataSnapshots === true ? '' : link.originalFilename)
177
195
  || (options?.ignoreAssetMetadataSnapshots === true ? '' : link.caption)
178
- || asset.originalFilename
179
- || asset.logicalName).trim() || null;
196
+ || (options?.ignoreAssetMetadataSnapshots === true ? '' : asset.originalFilename)
197
+ || (options?.ignoreAssetMetadataSnapshots === true ? '' : asset.logicalName)).trim() || null;
180
198
  const projected = canonicalLink(asset, { role, displayName, sortOrder: index }, index);
181
199
  if (options?.ignoreAssetMetadataSnapshots !== true
182
200
  && ((link.referenceNumber ?? null) !== (projected.referenceNumber ?? null)
@@ -187,12 +205,18 @@ export function resolveTaskAttachmentLinksForDurableSync(core, workspaceId, link
187
205
  path: canonicalAssetPath(asset),
188
206
  assetId: asset.assetId,
189
207
  linkRole: role,
190
- ...(asset.kind === 'document' && asset.storageProvider !== 'external' && projected.caption !== undefined
208
+ ...(options?.ignoreAssetMetadataSnapshots !== true
209
+ && asset.kind === 'document'
210
+ && asset.storageProvider !== 'external'
211
+ && projected.caption !== undefined
191
212
  ? { caption: projected.caption } : {}),
192
213
  ...(projected.displayName !== undefined ? { displayName: projected.displayName } : {}),
193
- ...(projected.originalFilename !== undefined ? { originalFilename: projected.originalFilename } : {}),
194
- ...(projected.referenceNumber !== undefined ? { referenceNumber: projected.referenceNumber } : {}),
195
- ...(projected.referenceLabel !== undefined ? { referenceLabel: projected.referenceLabel } : {}),
214
+ ...(options?.ignoreAssetMetadataSnapshots !== true && projected.originalFilename !== undefined
215
+ ? { originalFilename: projected.originalFilename } : {}),
216
+ ...(options?.ignoreAssetMetadataSnapshots !== true && projected.referenceNumber !== undefined
217
+ ? { referenceNumber: projected.referenceNumber } : {}),
218
+ ...(options?.ignoreAssetMetadataSnapshots !== true && projected.referenceLabel !== undefined
219
+ ? { referenceLabel: projected.referenceLabel } : {}),
196
220
  };
197
221
  });
198
222
  }
@@ -9,11 +9,23 @@ export interface TaskChecklistItem {
9
9
  }
10
10
  export interface TaskChecklistTask {
11
11
  id: string;
12
+ checklistItems?: TaskChecklistItem[];
12
13
  }
13
14
  export interface TaskChecklistReplaceMutation {
14
15
  taskId: string;
15
16
  items: TaskChecklistItem[];
16
17
  operationId: string;
18
+ expectedDetailRevision?: string;
19
+ requireDurableIdempotency?: boolean;
20
+ itemUpdateIntent?: {
21
+ kind: 'item-update';
22
+ itemId: string;
23
+ patch: {
24
+ text?: string;
25
+ done?: boolean;
26
+ order?: number;
27
+ };
28
+ };
17
29
  }
18
30
  export interface TaskChecklistCommandServiceDependencies {
19
31
  resolveTask(identifier: unknown): TaskChecklistTask;
@@ -35,6 +47,9 @@ export type ReplaceTaskChecklistCommandResult = {
35
47
  export type AddTaskChecklistItemCommandResult = ReplaceTaskChecklistCommandResult & {
36
48
  item: TaskChecklistItem | null;
37
49
  };
50
+ export type UpdateTaskChecklistItemCommandResult = ReplaceTaskChecklistCommandResult & {
51
+ item: TaskChecklistItem;
52
+ };
38
53
  export declare function createTaskChecklistCommandService(deps: TaskChecklistCommandServiceDependencies): {
39
54
  replace(input: {
40
55
  taskId: unknown;
@@ -47,4 +62,16 @@ export declare function createTaskChecklistCommandService(deps: TaskChecklistCom
47
62
  isCompleted?: unknown;
48
63
  done?: unknown;
49
64
  }): AddTaskChecklistItemCommandResult;
65
+ update(input: {
66
+ taskId: unknown;
67
+ itemId: unknown;
68
+ title?: unknown;
69
+ text?: unknown;
70
+ isCompleted?: unknown;
71
+ done?: unknown;
72
+ order?: unknown;
73
+ expectedDetailRevision?: unknown;
74
+ operationId?: string;
75
+ requireDurableIdempotency?: boolean;
76
+ }): UpdateTaskChecklistItemCommandResult;
50
77
  };
@@ -26,13 +26,21 @@ function requireMutationResult(task, taskId) {
26
26
  throw new TaskChecklistCommandError(`Task ${taskId} not found`, 'TASK_NOT_FOUND', 404, { taskId });
27
27
  }
28
28
  export function createTaskChecklistCommandService(deps) {
29
- const replaceItems = (taskId, items, operationKey) => {
30
- requireMutationResult(deps.replace({
29
+ const replaceItems = (taskId, items, operationKey, options) => {
30
+ const mutated = deps.replace({
31
31
  taskId,
32
32
  items,
33
- operationId: deps.createId('operation', operationKey),
34
- }), taskId);
35
- return deps.listItems(taskId);
33
+ operationId: options?.operationId || deps.createId('operation', operationKey),
34
+ ...(options?.expectedDetailRevision
35
+ ? { expectedDetailRevision: options.expectedDetailRevision }
36
+ : {}),
37
+ ...(options?.requireDurableIdempotency
38
+ ? { requireDurableIdempotency: true }
39
+ : {}),
40
+ ...(options?.itemUpdateIntent ? { itemUpdateIntent: options.itemUpdateIntent } : {}),
41
+ });
42
+ requireMutationResult(mutated, taskId);
43
+ return mutated?.checklistItems || deps.listItems(taskId);
36
44
  };
37
45
  return {
38
46
  replace(input) {
@@ -107,5 +115,75 @@ export function createTaskChecklistCommandService(deps) {
107
115
  items,
108
116
  };
109
117
  },
118
+ update(input) {
119
+ const task = deps.resolveTask(input.taskId);
120
+ const itemId = String(input.itemId ?? '').trim();
121
+ if (!itemId) {
122
+ throw new TaskChecklistCommandError('update_task_checklist_item requires itemId.');
123
+ }
124
+ const currentItems = deps.listItems(task.id);
125
+ const existingItem = currentItems.find((item) => item.id === itemId);
126
+ if (!existingItem) {
127
+ throw new TaskChecklistCommandError(`Checklist item ${itemId} was not found on task ${task.id}.`, 'TASK_CHECKLIST_ITEM_NOT_FOUND', 404, { taskId: task.id, itemId });
128
+ }
129
+ const hasTitle = input.title !== undefined || input.text !== undefined;
130
+ const hasCompleted = input.isCompleted !== undefined || input.done !== undefined;
131
+ const hasOrder = input.order !== undefined;
132
+ if (!hasTitle && !hasCompleted && !hasOrder) {
133
+ throw new TaskChecklistCommandError('update_task_checklist_item requires at least one of text, done, or order.');
134
+ }
135
+ const title = hasTitle
136
+ ? normalizeTitle(input.title ?? input.text, 'update_task_checklist_item text')
137
+ : existingItem.title;
138
+ const completedInput = input.isCompleted ?? input.done;
139
+ if (hasCompleted && typeof completedInput !== 'boolean') {
140
+ throw new TaskChecklistCommandError('update_task_checklist_item done must be a boolean.');
141
+ }
142
+ const isCompleted = hasCompleted ? completedInput : existingItem.isCompleted;
143
+ const parsedOrder = hasOrder ? Number(input.order) : existingItem.order;
144
+ if (hasOrder && (!Number.isInteger(parsedOrder) || parsedOrder < 0)) {
145
+ throw new TaskChecklistCommandError('update_task_checklist_item order must be a non-negative integer.');
146
+ }
147
+ const expectedDetailRevision = input.expectedDetailRevision === undefined
148
+ ? undefined
149
+ : String(input.expectedDetailRevision).trim();
150
+ if (input.expectedDetailRevision !== undefined && !expectedDetailRevision) {
151
+ throw new TaskChecklistCommandError('update_task_checklist_item expectedDetailRevision must contain non-whitespace text.');
152
+ }
153
+ const unchanged = existingItem.title === title
154
+ && existingItem.isCompleted === isCompleted
155
+ && existingItem.order === parsedOrder;
156
+ const nextItem = unchanged
157
+ ? existingItem
158
+ : {
159
+ ...existingItem,
160
+ title,
161
+ isCompleted,
162
+ order: parsedOrder,
163
+ updatedAt: deps.now(),
164
+ };
165
+ const nextItems = currentItems.map((item) => item.id === itemId ? nextItem : item);
166
+ const items = replaceItems(task.id, nextItems, `task-checklist-update:${task.id}:${itemId}`, {
167
+ operationId: input.operationId,
168
+ expectedDetailRevision,
169
+ requireDurableIdempotency: input.requireDurableIdempotency,
170
+ ...(input.requireDurableIdempotency ? {
171
+ itemUpdateIntent: {
172
+ kind: 'item-update',
173
+ itemId,
174
+ patch: {
175
+ ...(hasTitle ? { text: title } : {}),
176
+ ...(hasCompleted ? { done: isCompleted } : {}),
177
+ ...(hasOrder ? { order: parsedOrder } : {}),
178
+ },
179
+ },
180
+ } : {}),
181
+ });
182
+ const item = items.find((candidate) => candidate.id === itemId);
183
+ if (!item) {
184
+ throw new TaskChecklistCommandError(`Checklist item ${itemId} was not found after updating task ${task.id}.`, 'TASK_CHECKLIST_ITEM_NOT_FOUND', 404, { taskId: task.id, itemId });
185
+ }
186
+ return { taskId: task.id, item, items };
187
+ },
110
188
  };
111
189
  }
@@ -30,6 +30,7 @@ export interface TaskLifecycleCommandServiceDependencies {
30
30
  updates: Record<string, unknown>;
31
31
  forceDurable?: boolean;
32
32
  assigneeIntent?: TaskLifecycleAssigneeIntent;
33
+ handoffComment?: string;
33
34
  }): TaskLifecycleTask;
34
35
  mutateLifecycle(input: {
35
36
  taskId: string;
@@ -40,7 +41,7 @@ export interface TaskLifecycleCommandServiceDependencies {
40
41
  reason: string | undefined;
41
42
  };
42
43
  }): TaskLifecycleTask | null;
43
- resolveReviewer(task: TaskLifecycleTask): TaskLifecycleReviewerResolution;
44
+ resolveReviewer(task: TaskLifecycleTask, reviewer?: unknown): TaskLifecycleReviewerResolution;
44
45
  }
45
46
  export declare class TaskLifecycleCommandError extends Error {
46
47
  readonly code: string;
@@ -81,6 +82,7 @@ export declare function createTaskLifecycleCommandService(deps: TaskLifecycleCom
81
82
  finish(task: TaskLifecycleTask, input: {
82
83
  outcome: unknown;
83
84
  comment?: unknown;
85
+ reviewer?: unknown;
84
86
  }): FinishTaskCommandResult;
85
87
  reopen(task: TaskLifecycleTask, input: {
86
88
  activeAiProfileId: string | null | undefined;
@@ -88,8 +88,12 @@ export function createTaskLifecycleCommandService(deps) {
88
88
  nextTool: 'reopen_task',
89
89
  });
90
90
  }
91
+ const normalizedComment = typeof input.comment === 'string' ? input.comment.trim() : '';
92
+ if (normalizedOutcome === 'review' && !normalizedComment) {
93
+ throw new TaskLifecycleCommandError('finish_task requires a nonblank handoff comment when outcome is review.', 'TASK_FINISH_REVIEW_COMMENT_REQUIRED', 400, { taskId: task.id, outcome: normalizedOutcome });
94
+ }
91
95
  const reviewerResolution = normalizedOutcome === 'review'
92
- ? deps.resolveReviewer(task)
96
+ ? deps.resolveReviewer(task, input.reviewer)
93
97
  : undefined;
94
98
  const updated = deps.setStatus({
95
99
  task,
@@ -99,8 +103,8 @@ export function createTaskLifecycleCommandService(deps) {
99
103
  ...(reviewerResolution
100
104
  ? { assigneeIntent: { kind: 'set', value: reviewerResolution.assignee } }
101
105
  : {}),
106
+ ...(normalizedOutcome === 'review' ? { handoffComment: normalizedComment } : {}),
102
107
  });
103
- const normalizedComment = typeof input.comment === 'string' ? input.comment.trim() : '';
104
108
  return {
105
109
  task: updated,
106
110
  completion: {