@taskforcehq/taskforce 0.3.327 → 0.3.329

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/dist/Taskforce.module.css +257 -87
  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/activity/EntityActivityTimeline.js +15 -5
  8. package/dist/components/context/AttachmentPreviewHost.js +1 -1
  9. package/dist/components/context/ContextAttachmentManager.d.ts +3 -0
  10. package/dist/components/context/ContextAttachmentManager.js +116 -20
  11. package/dist/components/features/AnnotatedAttachmentWorkspace.js +24 -8
  12. package/dist/components/features/TaskSettings.d.ts +12 -1
  13. package/dist/components/features/TaskSettings.js +147 -40
  14. package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +1 -1
  15. package/dist/components/features/planning/PlanningEntitySummary.d.ts +3 -1
  16. package/dist/components/features/planning/PlanningEntitySummary.js +3 -2
  17. package/dist/components/features/planning/PlanningLinkPicker.d.ts +3 -1
  18. package/dist/components/features/planning/PlanningLinkPicker.js +5 -3
  19. package/dist/components/features/planning/PlanningModule.js +28 -14
  20. package/dist/components/features/planning/PlanningTaskOrderList.d.ts +1 -1
  21. package/dist/components/features/planning/PlanningTaskOrderList.js +17 -6
  22. package/dist/components/features/planning/PlanningVisualIdentity.d.ts +14 -0
  23. package/dist/components/features/planning/PlanningVisualIdentity.js +106 -0
  24. package/dist/components/features/planning/planningTaskArrangement.d.ts +1 -0
  25. package/dist/components/features/planning/planningTaskArrangement.js +5 -5
  26. package/dist/components/features/planning/planningWorkspaceModel.d.ts +11 -0
  27. package/dist/components/features/taskforceAgentEditorModel.js +3 -1
  28. package/dist/components/task/TaskActionHeader.d.ts +2 -1
  29. package/dist/components/task/TaskActionHeader.js +2 -2
  30. package/dist/components/task/TaskCard.d.ts +5 -0
  31. package/dist/components/task/TaskCard.js +101 -13
  32. package/dist/components/task/TaskContextUpload.d.ts +3 -0
  33. package/dist/components/task/TaskContextUpload.js +2 -2
  34. package/dist/components/task/TaskForm.d.ts +4 -0
  35. package/dist/components/task/TaskForm.js +32 -7
  36. package/dist/components/task/TaskImageReviews.js +28 -19
  37. package/dist/components/task/TaskKanban.d.ts +6 -1
  38. package/dist/components/task/TaskKanban.js +152 -6
  39. package/dist/components/task/TaskList.d.ts +5 -0
  40. package/dist/components/task/TaskList.js +3 -3
  41. package/dist/components/task/TaskStatusActions.js +11 -2
  42. package/dist/components/task/TaskWorkstreamPicker.js +2 -1
  43. package/dist/components/ui/DocumentTypeIcon.d.ts +7 -0
  44. package/dist/components/ui/DocumentTypeIcon.js +8 -0
  45. package/dist/components/ui/PasswordInput.d.ts +6 -0
  46. package/dist/components/ui/PasswordInput.js +11 -0
  47. package/dist/components/ui/TaxonomyDropdown.d.ts +9 -1
  48. package/dist/components/ui/TaxonomyDropdown.js +30 -3
  49. package/dist/components/ui/TopNoticeLayer.js +1 -1
  50. package/dist/components/views/AppShellHeader.js +32 -11
  51. package/dist/components/views/PlanComparisonPage.js +12 -0
  52. package/dist/components/views/StandaloneLayout.js +68 -39
  53. package/dist/components/views/WidgetView.js +10 -7
  54. package/dist/components/views/panels/PlanningDrawer.d.ts +7 -1
  55. package/dist/components/views/panels/PlanningDrawer.js +51 -54
  56. package/dist/components/views/standalone/modals/AccountSettingsModal.js +26 -17
  57. package/dist/config/envSchema.js +13 -0
  58. package/dist/config/localServiceCloudTarget.d.ts +10 -0
  59. package/dist/config/localServiceCloudTarget.js +76 -0
  60. package/dist/config/localServiceRuntimeContract.d.ts +13 -0
  61. package/dist/config/localServiceRuntimeContract.js +135 -0
  62. package/dist/core/AiProfileService.d.ts +1 -0
  63. package/dist/core/AiProfileService.js +48 -9
  64. package/dist/core/AttachmentLinkService.js +7 -4
  65. package/dist/core/DeletedTaskLifecycleService.d.ts +5 -0
  66. package/dist/core/DeletedTaskLifecycleService.js +55 -3
  67. package/dist/core/McpTokenService.d.ts +4 -0
  68. package/dist/core/McpTokenService.js +52 -0
  69. package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +8 -2
  70. package/dist/core/TaskAttachmentLinksDurableMutationService.js +51 -18
  71. package/dist/core/Taskforce.d.ts +124 -2
  72. package/dist/core/Taskforce.js +1175 -87
  73. package/dist/core/types.d.ts +8 -1
  74. package/dist/documentReviews/DocumentReviewCommentStore.d.ts +11 -0
  75. package/dist/documentReviews/DocumentReviewCommentStore.js +35 -2
  76. package/dist/documentReviews/service.d.ts +4 -0
  77. package/dist/documentReviews/service.js +141 -46
  78. package/dist/hooks/sync/useDurableLocalOutboxDrain.d.ts +1 -1
  79. package/dist/hooks/sync/useDurableTaskCreateMutation.js +1 -1
  80. package/dist/hooks/sync/useDurableTaskMetadataMutation.d.ts +1 -0
  81. package/dist/hooks/sync/useDurableTaskMetadataMutation.js +1 -0
  82. package/dist/hooks/sync/useDurableTaskStatusMutation.d.ts +1 -0
  83. package/dist/hooks/tasks/taskHttpMutation.js +1 -0
  84. package/dist/hooks/tasks/useCloudTaskSearch.d.ts +12 -0
  85. package/dist/hooks/tasks/useCloudTaskSearch.js +71 -0
  86. package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.d.ts +16 -0
  87. package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.js +197 -0
  88. package/dist/hooks/tasks/useTaskEditorNavigation.d.ts +16 -3
  89. package/dist/hooks/tasks/useTaskEditorNavigation.js +72 -13
  90. package/dist/hooks/tasks/useTaskFormActions.d.ts +9 -0
  91. package/dist/hooks/tasks/useTaskFormActions.js +96 -17
  92. package/dist/hooks/ui/useSettingsModel.d.ts +24 -2
  93. package/dist/hooks/ui/useSettingsModel.js +13 -2
  94. package/dist/hooks/ui/useSettingsPersistence.d.ts +12 -1
  95. package/dist/hooks/ui/useSettingsPersistence.js +74 -47
  96. package/dist/hooks/useFilterSort.d.ts +7 -3
  97. package/dist/hooks/useFilterSort.js +15 -5
  98. package/dist/hooks/useTaskData.d.ts +10 -1
  99. package/dist/hooks/useTaskData.js +216 -7
  100. package/dist/hooks/useTaskMutations.d.ts +10 -2
  101. package/dist/hooks/useTaskMutations.js +223 -43
  102. package/dist/hooks/useTaskforce.d.ts +94 -57
  103. package/dist/hooks/useTaskforce.js +428 -16
  104. package/dist/hooks/useUiNotice.d.ts +4 -0
  105. package/dist/hooks/useUiNotice.js +4 -0
  106. package/dist/mcp/adminWorkspaceRegistrar.js +3 -3
  107. package/dist/mcp/canonicalAssetHelpers.d.ts +13 -0
  108. package/dist/mcp/canonicalAssetHelpers.js +39 -5
  109. package/dist/mcp/collaborationRegistrar.js +15 -13
  110. package/dist/mcp/documentAssetRegistrar.js +60 -44
  111. package/dist/mcp/documentHelpers.d.ts +1 -0
  112. package/dist/mcp/documentHelpers.js +7 -3
  113. package/dist/mcp/localServiceProxy.js +4 -1
  114. package/dist/mcp/runtime.d.ts +16 -0
  115. package/dist/mcp/runtime.js +735 -210
  116. package/dist/mcp/taskAttachmentCommandAdapter.d.ts +2 -0
  117. package/dist/mcp/taskAttachmentCommandAdapter.js +14 -8
  118. package/dist/mcp/taskPlanningRegistrar.js +78 -67
  119. package/dist/mcp/toolCallCompatibility.js +38 -1
  120. package/dist/mcp/toolCatalog.d.ts +1 -0
  121. package/dist/mcp/toolRegistry.js +2 -2
  122. package/dist/migrations/taskSchemaMigrations.js +47 -0
  123. package/dist/runtime/appGateDefinitions.d.ts +30 -6
  124. package/dist/runtime/appGateDefinitions.js +6 -6
  125. package/dist/runtime/appGates.d.ts +12 -13
  126. package/dist/runtime/appGates.js +29 -13
  127. package/dist/runtime/nodeAppGates.d.ts +8 -0
  128. package/dist/runtime/nodeAppGates.js +27 -0
  129. package/dist/server/appAccess.d.ts +12 -0
  130. package/dist/server/appAccess.js +21 -0
  131. package/dist/server/authorizedWorkspaceAccess.d.ts +12 -0
  132. package/dist/server/authorizedWorkspaceAccess.js +40 -0
  133. package/dist/server/cloudCollectionRead.d.ts +25 -0
  134. package/dist/server/cloudCollectionRead.js +120 -0
  135. package/dist/server/cloudCollectionReadPool.d.ts +52 -0
  136. package/dist/server/cloudCollectionReadPool.js +140 -0
  137. package/dist/server/cloudCollectionReadWorker.d.ts +1 -0
  138. package/dist/server/cloudCollectionReadWorker.js +36 -0
  139. package/dist/server/index.d.ts +6 -11
  140. package/dist/server/index.js +67 -42
  141. package/dist/server/localServiceLease.d.ts +6 -0
  142. package/dist/server/localServiceLease.js +17 -1
  143. package/dist/server/localWorkspaceSyncServerRuntime.js +10 -3
  144. package/dist/server/rateLimit.d.ts +1 -0
  145. package/dist/server/rateLimit.js +4 -0
  146. package/dist/server/realtimeSyncWs.js +36 -12
  147. package/dist/server/routes/admin.js +1 -0
  148. package/dist/server/routes/agentRoles.js +3 -2
  149. package/dist/server/routes/agentSkills.js +3 -2
  150. package/dist/server/routes/agents.js +118 -16
  151. package/dist/server/routes/annotatedAttachments.js +3 -2
  152. package/dist/server/routes/auth.js +3 -1
  153. package/dist/server/routes/documents.d.ts +5 -2
  154. package/dist/server/routes/documents.js +160 -13
  155. package/dist/server/routes/durableTaskHttpRouters.d.ts +11 -2
  156. package/dist/server/routes/durableTaskHttpRouters.js +2 -2
  157. package/dist/server/routes/localService.js +4 -0
  158. package/dist/server/routes/primitives.d.ts +4 -0
  159. package/dist/server/routes/shared.d.ts +24 -0
  160. package/dist/server/routes/shared.js +114 -18
  161. package/dist/server/routes/sync.js +22 -0
  162. package/dist/server/routes/syncAuxRoutes.js +5 -2
  163. package/dist/server/routes/syncPullApplyRoutes.js +23 -4
  164. package/dist/server/routes/syncV3FeedRoutes.js +22 -18
  165. package/dist/server/routes/syncV3LocalCaptureRoutes.js +48 -4
  166. package/dist/server/routes/syncV3LocalFeedApplyRoutes.d.ts +1 -0
  167. package/dist/server/routes/syncV3LocalFeedApplyRoutes.js +26 -0
  168. package/dist/server/routes/syncV3OperationRoutes.js +27 -2
  169. package/dist/server/routes/tasks.js +299 -97
  170. package/dist/server/routes/workspaces.js +43 -1
  171. package/dist/server/routes.js +35 -7
  172. package/dist/server/workspaceDeletionAdmission.d.ts +23 -0
  173. package/dist/server/workspaceDeletionAdmission.js +50 -0
  174. package/dist/service/localServiceCli.js +52 -25
  175. package/dist/service/localServiceClientLifecycle.js +93 -6
  176. package/dist/services/codexAppServerModelGateway.d.ts +112 -0
  177. package/dist/services/codexAppServerModelGateway.js +438 -0
  178. package/dist/services/taskforceAgentMcpBridge.d.ts +7 -1
  179. package/dist/services/taskforceAgentMcpBridge.js +23 -3
  180. package/dist/shared/planningIdentity.d.ts +20 -0
  181. package/dist/shared/planningIdentity.js +79 -0
  182. package/dist/shared/taskforceAgentDefinition.js +7 -1
  183. package/dist/shared/taskforceAgentModels.d.ts +1 -0
  184. package/dist/shared/taskforceAgentModels.js +30 -1
  185. package/dist/storage/postgresAdapter.d.ts +20 -3
  186. package/dist/storage/postgresAdapter.js +110 -14
  187. package/dist/storage/postgresRequestDiagnostics.d.ts +8 -0
  188. package/dist/storage/postgresRequestDiagnostics.js +24 -0
  189. package/dist/storage/postgresWorker.js +13 -4
  190. package/dist/storage/postgresWorkerProtocol.d.ts +29 -0
  191. package/dist/storage/postgresWorkerProtocol.js +30 -0
  192. package/dist/storage/workspaceAssetStore.d.ts +32 -0
  193. package/dist/storage/workspaceAssetStore.js +70 -0
  194. package/dist/sync/cloudSyncApi.d.ts +4 -1
  195. package/dist/sync/cloudSyncApi.js +7 -2
  196. package/dist/sync/contentSyncState.d.ts +1 -1
  197. package/dist/sync/coordinator/localSyncExecutionPermitStore.d.ts +17 -0
  198. package/dist/sync/coordinator/localSyncExecutionPermitStore.js +77 -23
  199. package/dist/sync/coordinator/localWorkspaceSyncRunner.js +6 -2
  200. package/dist/sync/coordinator/workspaceSyncTransportGateway.js +4 -2
  201. package/dist/sync/engine/workspaceSyncEngineBrowserGatewayRemoteServices.js +20 -2
  202. package/dist/sync/engine/workspaceSyncEngineLocalServices.d.ts +4 -0
  203. package/dist/sync/engine/workspaceSyncEngineLocalServices.js +11 -0
  204. package/dist/sync/engine/workspaceSyncEngineRemoteServices.d.ts +16 -0
  205. package/dist/sync/engine/workspaceSyncEngineRemoteServices.js +2 -1
  206. package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.d.ts +1 -1
  207. package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +46 -4
  208. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.d.ts +3 -1
  209. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +51 -11
  210. package/dist/sync/engine/workspaceSyncEngineTransfers.d.ts +1 -1
  211. package/dist/sync/engine/workspaceSyncEngineTransfers.js +8 -3
  212. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +2 -0
  213. package/dist/sync/syncApplyHandlers.d.ts +3 -1
  214. package/dist/sync/syncApplyHandlers.js +29 -0
  215. package/dist/sync/syncService.d.ts +2 -0
  216. package/dist/sync/syncService.js +13 -1
  217. package/dist/sync/taskSyncPayload.d.ts +1 -0
  218. package/dist/sync/taskSyncPayload.js +6 -0
  219. package/dist/sync/v3/combinedFeedCapability.js +4 -0
  220. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.d.ts +3 -0
  221. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +31 -3
  222. package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +2 -0
  223. package/dist/sync/v3/durableTaskChecklistMutationRouter.js +4 -0
  224. package/dist/sync/v3/durableTaskHttpBulkMutationRouter.d.ts +1 -0
  225. package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +47 -12
  226. package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +5 -3
  227. package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +27 -2
  228. package/dist/sync/v3/localInitiativeOutboxService.js +4 -2
  229. package/dist/sync/v3/localOutboxDispatcher.js +5 -1
  230. package/dist/sync/v3/localTaskAttachmentLinksOutboxHandler.js +12 -2
  231. package/dist/sync/v3/localTaskAttachmentLinksOutboxService.d.ts +3 -0
  232. package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +62 -10
  233. package/dist/sync/v3/localTaskCreateOutboxService.js +15 -6
  234. package/dist/sync/v3/localTaskMetadataClient.d.ts +1 -0
  235. package/dist/sync/v3/localTaskMetadataClient.js +1 -0
  236. package/dist/sync/v3/localTaskMetadataOutboxHandler.js +5 -1
  237. package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -0
  238. package/dist/sync/v3/localTaskMetadataOutboxService.js +7 -0
  239. package/dist/sync/v3/localTaskRestoreOutboxService.js +14 -6
  240. package/dist/sync/v3/localTaskStatusClient.d.ts +1 -0
  241. package/dist/sync/v3/localTaskStatusClient.js +6 -1
  242. package/dist/sync/v3/localTaskStatusOutboxService.d.ts +2 -0
  243. package/dist/sync/v3/localTaskStatusOutboxService.js +54 -13
  244. package/dist/sync/v3/localWorkstreamOutboxService.js +4 -2
  245. package/dist/sync/v3/syncDurabilityStore.d.ts +15 -0
  246. package/dist/sync/v3/syncDurabilityStore.js +201 -43
  247. package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +7 -4
  248. package/dist/sync/v3/taskAttachmentLinksMutationService.js +37 -12
  249. package/dist/sync/v3/taskChecklistMutationService.d.ts +4 -2
  250. package/dist/sync/v3/taskChecklistMutationService.js +4 -0
  251. package/dist/sync/v3/taskCreateMutationService.d.ts +2 -2
  252. package/dist/sync/v3/taskCreateMutationService.js +7 -4
  253. package/dist/sync/v3/taskMetadataCoexistence.js +4 -0
  254. package/dist/sync/v3/taskMetadataMutationService.d.ts +3 -0
  255. package/dist/sync/v3/taskMetadataMutationService.js +4 -0
  256. package/dist/sync/v3/taskRestoreMutationService.d.ts +2 -2
  257. package/dist/sync/v3/taskRestoreMutationService.js +7 -4
  258. package/dist/sync/v3/taskStatusMutationService.d.ts +2 -0
  259. package/dist/sync/v3/taskStatusMutationService.js +4 -0
  260. package/dist/sync/v3/workflowAssignmentSync.d.ts +1 -0
  261. package/dist/sync/v3/workflowAssignmentSync.js +3 -0
  262. package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.d.ts +1 -1
  263. package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +14 -12
  264. package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.d.ts +11 -0
  265. package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.js +27 -10
  266. package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.d.ts +2 -1
  267. package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.js +52 -10
  268. package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +10 -1
  269. package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +15 -4
  270. package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +66 -13
  271. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +11 -0
  272. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +57 -22
  273. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +10 -4
  274. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +124 -51
  275. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +10 -0
  276. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +120 -28
  277. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +3 -1
  278. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +18 -0
  279. package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +6 -0
  280. package/dist/sync/v3/workspaceSyncV3FeedProfile.d.ts +1 -1
  281. package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.d.ts +1 -0
  282. package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.js +23 -10
  283. package/dist/sync/v3/workspaceSyncV3NestedTaskApply.d.ts +3 -0
  284. package/dist/sync/v3/workspaceSyncV3NestedTaskApply.js +64 -13
  285. package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.d.ts +9 -0
  286. package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.js +26 -0
  287. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +15 -5
  288. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +50 -14
  289. package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.d.ts +2 -1
  290. package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.js +11 -3
  291. package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +3 -0
  292. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +2 -0
  293. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +19 -7
  294. package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.d.ts +2 -0
  295. package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.js +10 -0
  296. package/dist/sync/v3/workspaceSyncV3TaskCommentAudit.js +9 -2
  297. package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.d.ts +7 -0
  298. package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.js +37 -0
  299. package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.d.ts +6 -0
  300. package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.js +9 -0
  301. package/dist/sync/v3/workspaceSyncV3TaskRootProjection.d.ts +1 -1
  302. package/dist/sync/v3/workspaceSyncV3TaskRootProjection.js +1 -1
  303. package/dist/sync/workspacePullFeed.d.ts +6 -1
  304. package/dist/sync/workspacePullFeed.js +174 -162
  305. package/dist/sync/workspaceRepair.d.ts +3 -0
  306. package/dist/sync/workspaceRepair.js +67 -6
  307. package/dist/sync/workspaceSyncModel.d.ts +11 -0
  308. package/dist/sync/workspaceSyncModel.js +27 -2
  309. package/dist/types.d.ts +55 -1
  310. package/dist/types.js +6 -0
  311. package/dist/ui/assets/{AiIdentityRosterCard-DB1BLO_j.js → AiIdentityRosterCard-BZM4aQnM.js} +1 -1
  312. package/dist/ui/assets/AiProfilesModule-BbAEWtHH.js +1 -0
  313. package/dist/ui/assets/AnnotatedAttachmentWorkspace-B68Wg-RV.js +3 -0
  314. package/dist/ui/assets/AnnotatedAttachmentWorkspace-TzYu7pkG.css +1 -0
  315. package/dist/ui/assets/AssetTraySortControl-C8Rztyal.js +1 -0
  316. package/dist/ui/assets/ContextAttachmentManager-Cj3c7X9y.js +3 -0
  317. package/dist/ui/assets/{DocumentWorkspace-BC7sy5TG.js → DocumentWorkspace-B5uvzems.js} +4 -4
  318. package/dist/ui/assets/EntityActivityTimeline-BVFvCB5R.js +1 -0
  319. package/dist/ui/assets/PlanningModule-B5DWQOGz.js +1 -0
  320. package/dist/ui/assets/PlanningModule-CoIR9hbV.css +1 -0
  321. package/dist/ui/assets/PlansPage-AY4cGWco.js +1 -0
  322. package/dist/ui/assets/TaskContextUpload-DZxTVesW.js +1 -0
  323. package/dist/ui/assets/TaskContextUpload-qmBeUw3u.css +1 -0
  324. package/dist/ui/assets/TaskSettings-BeWAuyZj.js +12 -0
  325. package/dist/ui/assets/{TaskforceAgentsModule-CkAxW19K.js → TaskforceAgentsModule-BifCYtjR.js} +6 -6
  326. package/dist/ui/assets/WorkflowManagerModule-DEr5di_r.js +1 -0
  327. package/dist/ui/assets/index-I5zhdtgt.js +7 -0
  328. package/dist/ui/assets/index-VRFsx3wz.css +1 -0
  329. package/dist/ui/assets/{vendor-icons-B8ZNsH1g.js → vendor-icons-D9Lpw-j4.js} +1 -1
  330. package/dist/ui/index.html +3 -3
  331. package/dist/utils/constants.d.ts +2 -2
  332. package/dist/utils/constants.js +1 -1
  333. package/dist/utils/contextAttachmentUpload.d.ts +2 -0
  334. package/dist/utils/contextAttachmentUpload.js +7 -0
  335. package/dist/utils/contextFiles.d.ts +1 -1
  336. package/dist/utils/contextFiles.js +4 -4
  337. package/dist/utils/taskActivity.d.ts +3 -2
  338. package/dist/utils/taskActivity.js +19 -2
  339. package/dist/utils/taskEditorReconciliation.d.ts +40 -0
  340. package/dist/utils/taskEditorReconciliation.js +217 -0
  341. package/dist/utils/taskNormalization.d.ts +1 -0
  342. package/dist/utils/taskNormalization.js +39 -0
  343. package/dist/utils/taskSearch.js +2 -2
  344. package/dist/utils/uiNotice.d.ts +4 -0
  345. package/package.json +21 -4
  346. package/dist/ui/assets/AiProfilesModule-DPxQeF9N.js +0 -1
  347. package/dist/ui/assets/AnnotatedAttachmentWorkspace-CBRZ8rca.css +0 -1
  348. package/dist/ui/assets/AnnotatedAttachmentWorkspace-DHNYaNg6.js +0 -3
  349. package/dist/ui/assets/AssetTraySortControl-DphbyHVc.js +0 -1
  350. package/dist/ui/assets/ContextAttachmentManager-CMxSzHqc.js +0 -3
  351. package/dist/ui/assets/EntityActivityTimeline-pJ2ZjZ-k.js +0 -1
  352. package/dist/ui/assets/PlanningModule-CeTqXMMz.css +0 -1
  353. package/dist/ui/assets/PlanningModule-t5GbvVR9.js +0 -1
  354. package/dist/ui/assets/PlansPage-BVH_NfgT.js +0 -1
  355. package/dist/ui/assets/TaskContextUpload-Ds4LnDzV.css +0 -1
  356. package/dist/ui/assets/TaskContextUpload-bl4fOOUz.js +0 -1
  357. package/dist/ui/assets/TaskSettings-xVsxSa0g.js +0 -12
  358. package/dist/ui/assets/WorkflowManagerModule-BKqIGVnF.js +0 -1
  359. package/dist/ui/assets/index-BzJlVnNS.css +0 -1
  360. package/dist/ui/assets/index-Cx7vZJHL.js +0 -7
@@ -28,10 +28,12 @@ function requiredText(value, label) {
28
28
  export class DurableTaskHttpRootMutationError extends Error {
29
29
  code;
30
30
  statusCode;
31
- constructor(message, code, statusCode) {
31
+ details;
32
+ constructor(message, code, statusCode, details) {
32
33
  super(message);
33
34
  this.code = code;
34
35
  this.statusCode = statusCode;
36
+ this.details = details;
35
37
  this.name = 'DurableTaskHttpRootMutationError';
36
38
  }
37
39
  }
@@ -120,6 +122,11 @@ export function createDurableTaskHttpRootMutationRouter(input) {
120
122
  rethrowDurableReplayError(error, 'Committed durable task HTTP response is invalid.');
121
123
  }
122
124
  };
125
+ const detailRevisionPrecondition = (taskId, expectedDetailRevision) => {
126
+ if (!expectedDetailRevision)
127
+ return undefined;
128
+ return () => input.core.assertTaskDetailRevisionForUpdate(taskId, expectedDetailRevision, input.workspaceId);
129
+ };
123
130
  const lifecycle = (taskIdInput, mutationKind, payload) => {
124
131
  const taskId = requiredText(taskIdInput, 'Task id');
125
132
  const operation = context();
@@ -144,6 +151,12 @@ export function createDurableTaskHttpRootMutationRouter(input) {
144
151
  };
145
152
  return {
146
153
  enabled,
154
+ hasCommittedOperation() {
155
+ const operationId = String(input.operationId || '').trim();
156
+ if (!operationId)
157
+ return false;
158
+ return Boolean(store.readCommittedOperationResponse(operationIdentity(operationId)));
159
+ },
147
160
  create(candidate) {
148
161
  const source = candidate && typeof candidate === 'object' && !Array.isArray(candidate)
149
162
  ? candidate
@@ -183,7 +196,7 @@ export function createDurableTaskHttpRootMutationRouter(input) {
183
196
  throw new DurableTaskHttpRootMutationError('The durable task mutation is already in progress.', 'WORKSPACE_SYNC_OPERATION_IN_PROGRESS', 409);
184
197
  return result.response.authoritative.payload;
185
198
  },
186
- update(taskIdInput, candidate, saveSource, activityAction) {
199
+ update(taskIdInput, candidate, saveSource, activityAction, expectedDetailRevision) {
187
200
  const taskId = requiredText(taskIdInput, 'Task id');
188
201
  if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
189
202
  throw new DurableTaskHttpRootMutationError('Task patch must be an object.', 'DURABLE_TASK_PATCH_INVALID', 400);
@@ -224,6 +237,8 @@ export function createDurableTaskHttpRootMutationRouter(input) {
224
237
  now: operation.acceptedAtIso,
225
238
  leaseExpiresAt: new Date(operation.acceptedAt.getTime() + OPERATION_LEASE_MS).toISOString(),
226
239
  actorRef: input.actorRef,
240
+ expectedDetailRevision,
241
+ assertPrecondition: detailRevisionPrecondition(taskId, expectedDetailRevision),
227
242
  onPostCommitError: input.onPostCommitError,
228
243
  });
229
244
  if (result.state === 'in_progress')
@@ -283,6 +298,8 @@ export function createDurableTaskHttpRootMutationRouter(input) {
283
298
  actorRef: input.actorRef,
284
299
  saveSource,
285
300
  activityAction,
301
+ expectedDetailRevision,
302
+ assertPrecondition: detailRevisionPrecondition(taskId, expectedDetailRevision),
286
303
  onPostCommitError: input.onPostCommitError,
287
304
  });
288
305
  if (result.state === 'in_progress')
@@ -341,6 +358,14 @@ export function createDurableTaskHttpRootMutationRouter(input) {
341
358
  if (options?.atomicRecovery) {
342
359
  const notifications = [];
343
360
  const result = input.core.getDatabaseAdapter().transaction(() => {
361
+ // Child durable mutations lock the workspace sequence before
362
+ // touching task rows. Take the same lock first when a batch
363
+ // may combine a legacy root write with durable child owners.
364
+ store.lockWorkspaceSequenceForMutation({
365
+ tenantId: input.tenantId,
366
+ workspaceId: input.workspaceId,
367
+ now: operation.acceptedAtIso,
368
+ });
344
369
  const value = apply({
345
370
  runChild: (mutation) => input.core.getDatabaseAdapter().transaction(mutation)(),
346
371
  deferPostCommitNotification: (notify) => notifications.push(notify),
@@ -294,8 +294,9 @@ function validateActivitySemantics(input) {
294
294
  const authoritativeAttachmentLinks = validateCanonicalPlanningAttachmentLinks(input.authoritative.attachments);
295
295
  const comparableExpectedDetails = normalizePlanningActivityDetailsForComparison(expectedDetails, input.record.mutationKind, authoritativeAttachmentLinks);
296
296
  const comparableAuthoritativeDetails = normalizePlanningActivityDetailsForComparison((input.activity.details || {}), input.record.mutationKind, authoritativeAttachmentLinks);
297
- if (fingerprintWorkspaceSyncPayload(comparableExpectedDetails)
298
- !== fingerprintWorkspaceSyncPayload(comparableAuthoritativeDetails)) {
297
+ if (!input.boundedAttachmentRebase
298
+ && fingerprintWorkspaceSyncPayload(comparableExpectedDetails)
299
+ !== fingerprintWorkspaceSyncPayload(comparableAuthoritativeDetails)) {
299
300
  throw new LocalAuthoritativeInitiativeError('Authoritative initiative activity details differ from the captured mutation.');
300
301
  }
301
302
  }
@@ -437,6 +438,7 @@ export function applyAuthoritativeInitiativeAndAck(input) {
437
438
  payload: payload,
438
439
  authoritative: initiative,
439
440
  activity,
441
+ boundedAttachmentRebase,
440
442
  });
441
443
  const revision = validateInitiativeActivityAuthoritativeRevision({
442
444
  response: response,
@@ -279,7 +279,11 @@ export async function dispatchLocalOutboxBatch(input) {
279
279
  const details = errorDetails(result.data, `WORKSPACE_SYNC_TRANSPORT_${result.status || 'NETWORK'}`, 'Workspace sync delivery failed.');
280
280
  const stableErrorCode = result.status === 401 ? 'WORKSPACE_SYNC_TRANSPORT_401' : details.code;
281
281
  const contentPushRequired = result.status === 425
282
- && stableErrorCode === 'TASK_ATTACHMENT_ASSET_NOT_READY';
282
+ && [
283
+ 'TASK_ATTACHMENT_ASSET_NOT_READY',
284
+ 'WORKSTREAM_ATTACHMENT_NOT_READY',
285
+ 'INITIATIVE_ATTACHMENT_NOT_READY',
286
+ ].includes(stableErrorCode);
283
287
  const inProgressDelay = operationRetryAt(result.data, now().getTime());
284
288
  const cloudV3Disabled = result.status === 404 && isCloudV3Disabled(record, result.data);
285
289
  // A first metadata edit may dispatch before legacy reconciliation has
@@ -17,8 +17,13 @@ export function buildTaskAttachmentLinksEnvelope(record) {
17
17
  }
18
18
  const source = payload;
19
19
  if (!Object.prototype.hasOwnProperty.call(source, 'links')
20
- || Object.keys(source).some((key) => key !== 'links' && key !== 'originActor')) {
21
- throw new Error('Local task attachment-link outbox payload must contain links and optional originActor.');
20
+ || Object.keys(source).some((key) => !['links', 'cardCoverAssetId', 'originActor'].includes(key))) {
21
+ throw new Error('Local task attachment-link outbox payload must contain links and optional card cover metadata.');
22
+ }
23
+ const hasCardCoverAssetId = Object.prototype.hasOwnProperty.call(source, 'cardCoverAssetId');
24
+ if (hasCardCoverAssetId && source.cardCoverAssetId !== null
25
+ && (typeof source.cardCoverAssetId !== 'string' || !source.cardCoverAssetId.trim())) {
26
+ throw new Error('Local task attachment-link card cover must be a non-empty string or null.');
22
27
  }
23
28
  const aggregate = validateWorkspaceSyncV3TaskAttachmentLinksPayload({ taskId: record.entityId, links: source.links });
24
29
  return {
@@ -31,6 +36,11 @@ export function buildTaskAttachmentLinksEnvelope(record) {
31
36
  baseEntityRevision: record.baseEntityRevision, baseLifecycleRevision: '0',
32
37
  payload: {
33
38
  links: aggregate.links,
39
+ ...(hasCardCoverAssetId ? {
40
+ cardCoverAssetId: source.cardCoverAssetId === null
41
+ ? null
42
+ : String(source.cardCoverAssetId).trim(),
43
+ } : {}),
34
44
  ...(source.originActor !== undefined
35
45
  ? { originActor: parseWorkspaceSyncV3OriginActor(source.originActor) }
36
46
  : {}),
@@ -1,8 +1,10 @@
1
1
  import type { TaskEvent, TaskItem } from '../../core/Taskforce.js';
2
+ import type { DatabaseAdapter } from '../../storage/databaseAdapter.js';
2
3
  import type { DurableTaskAttachmentLinksServiceCore } from './taskAttachmentLinksMutationService.js';
3
4
  import { WorkspaceSyncDurabilityStore, type WorkspaceSyncOperationIdentity } from './syncDurabilityStore.js';
4
5
  import { type WorkspaceSyncV3TaskAttachmentLink } from './workspaceSyncV3NestedTaskCodecs.js';
5
6
  export interface LocalTaskAttachmentLinksOutboxCore extends DurableTaskAttachmentLinksServiceCore {
7
+ getDatabaseAdapter(): DatabaseAdapter;
6
8
  getWorkspaceAssetDispatchReadiness(assetId: string, workspaceId: string): {
7
9
  ready: boolean;
8
10
  retryable: boolean;
@@ -26,6 +28,7 @@ export declare function enqueueLocalTaskAttachmentLinksMutation(input: {
26
28
  identity: WorkspaceSyncOperationIdentity;
27
29
  taskId: string;
28
30
  links: readonly WorkspaceSyncV3TaskAttachmentLink[];
31
+ cardCoverAssetId?: string | null;
29
32
  now: string;
30
33
  actorRef: string;
31
34
  requireV3CombinedOwnership?: boolean;
@@ -1,11 +1,13 @@
1
- import { projectResolvedTaskAttachmentLinksForDurableSync } from '../../core/TaskAttachmentLinksDurableMutationService.js';
1
+ import { projectResolvedTaskAttachmentLinksForDurableSync, resolveTaskCardCoverAssetIdForLinks, } from '../../core/TaskAttachmentLinksDurableMutationService.js';
2
2
  import { selectDurableTaskMetadataPatch } from '../../core/TaskDurableSyncMutationService.js';
3
3
  import { canonicalWorkspaceSyncJson } from './canonicalWorkspaceSyncJson.js';
4
4
  import { buildTaskAttachmentLinksActivityEventId } from './taskAttachmentLinksActivityIdentity.js';
5
5
  import { canonicalizeEntityActivityDetailsForSync, serializeTaskActivityForSync, } from './taskActivityJournal.js';
6
6
  import { fingerprintWorkspaceSyncPayload, } from './syncDurabilityStore.js';
7
7
  import { serializeTaskRootForSync } from './workspaceSyncV3TaskRootProjection.js';
8
- import { serializeWorkspaceSyncV3TaskAttachmentLinksPayload, } from './workspaceSyncV3NestedTaskCodecs.js';
8
+ import { resolveWorkspaceSyncV3RegisteredCoverage } from './workspaceSyncV3CoverageRegistry.js';
9
+ import { WorkspaceSyncV3CombinedProjectionStateStore } from './workspaceSyncV3CombinedProjectionState.js';
10
+ import { serializeWorkspaceSyncV3TaskAttachmentLinksPayload, serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload, validateWorkspaceSyncV3TaskAttachmentLinksPayload, validateWorkspaceSyncV3TaskAttachmentLinksV2Payload, } from './workspaceSyncV3NestedTaskCodecs.js';
9
11
  import { withOriginActorPayload } from './workspaceSyncV3OriginActor.js';
10
12
  export class LocalAuthoritativeTaskAttachmentLinksError extends Error {
11
13
  code = 'WORKSPACE_SYNC_AUTHORITATIVE_RESPONSE_INVALID';
@@ -90,6 +92,24 @@ function requireRevision(value, label) {
90
92
  throw new LocalAuthoritativeTaskAttachmentLinksError(`${label} is invalid.`);
91
93
  return text;
92
94
  }
95
+ function resolveOwnedAttachmentProjection(input) {
96
+ const state = new WorkspaceSyncV3CombinedProjectionStateStore(input.core.getDatabaseAdapter()).read({
97
+ tenantId: input.identity.tenantId,
98
+ workspaceId: input.identity.workspaceId,
99
+ });
100
+ if (state.owner !== 'v3')
101
+ return null;
102
+ const coverage = resolveWorkspaceSyncV3RegisteredCoverage(state)?.profile;
103
+ if (!coverage)
104
+ throw new LocalAuthoritativeTaskAttachmentLinksError('Owned task attachment-link coverage is not registered.');
105
+ if (coverage.projections.some(({ projectionId }) => projectionId === 'task-attachment-links/full-v2')) {
106
+ return 'task-attachment-links/full-v2';
107
+ }
108
+ if (coverage.projections.some(({ projectionId }) => projectionId === 'task-attachment-links/full-v1')) {
109
+ return 'task-attachment-links/full-v1';
110
+ }
111
+ return null;
112
+ }
93
113
  export function enqueueLocalTaskAttachmentLinksMutation(input) {
94
114
  const requestedLinks = canonicalLinks(input.taskId, input.links);
95
115
  let attachments;
@@ -111,7 +131,12 @@ export function enqueueLocalTaskAttachmentLinksMutation(input) {
111
131
  const result = input.store.enqueueLocalOperation({
112
132
  identity: input.identity,
113
133
  domain: 'task-attachment-links', entityId: input.taskId, mutationKind: 'replace',
114
- revisionSource: 'current', payload: withOriginActorPayload({ links }, input.actorRef),
134
+ revisionSource: 'current', payload: withOriginActorPayload({
135
+ links,
136
+ ...(Object.prototype.hasOwnProperty.call(input, 'cardCoverAssetId')
137
+ ? { cardCoverAssetId: input.cardCoverAssetId ?? null }
138
+ : {}),
139
+ }, input.actorRef),
115
140
  additionalTargets: [{
116
141
  domain: 'task', entityId: input.taskId, mutationKind: 'metadata-update',
117
142
  baseEntityRevision: rootRevision.entityRevision, baseLifecycleRevision: rootRevision.lifecycleRevision,
@@ -127,11 +152,18 @@ export function enqueueLocalTaskAttachmentLinksMutation(input) {
127
152
  if (existing.isArchived)
128
153
  throw new LocalAuthoritativeTaskAttachmentLinksError('Archived tasks are read-only.');
129
154
  const previousLinks = input.core.listTaskAttachmentLinksForDurableSync(input.taskId, input.identity.workspaceId);
155
+ const cardCoverAssetId = resolveTaskCardCoverAssetIdForLinks({
156
+ currentCardCoverAssetId: existing.cardCoverAssetId,
157
+ ...(Object.prototype.hasOwnProperty.call(input, 'cardCoverAssetId')
158
+ ? { requestedCardCoverAssetId: input.cardCoverAssetId }
159
+ : {}),
160
+ links,
161
+ });
130
162
  const currentMs = Date.parse(String(existing.updatedAt || existing.createdAt));
131
163
  const requestedMs = Date.parse(input.now);
132
164
  const updatedAt = Number.isFinite(currentMs) && currentMs >= requestedMs
133
165
  ? new Date(currentMs + 1).toISOString() : input.now;
134
- const task = input.core.updateTask(input.taskId, { attachments }, input.identity.workspaceId, {
166
+ const task = input.core.updateTask(input.taskId, { attachments, cardCoverAssetId }, input.identity.workspaceId, {
135
167
  persistBackup: false, emitRealtime: false, syncUpdatedAt: updatedAt,
136
168
  actorRef: input.actorRef, taskEventId: activityEventId, taskEventAction: 'task-attachments-changed',
137
169
  taskEventDetails: canonicalizeEntityActivityDetailsForSync({
@@ -194,9 +226,20 @@ export function applyAuthoritativeTaskAttachmentLinksAndAck(input) {
194
226
  throw new LocalAuthoritativeTaskAttachmentLinksError('Authoritative task attachment-link operation ordering is invalid.');
195
227
  }
196
228
  const rootPayload = serializeTaskRootForSync(root.payload);
197
- const attachmentLinksPayload = serializeWorkspaceSyncV3TaskAttachmentLinksPayload({
198
- taskId: attachmentLinks.entityId, links: attachmentLinks.payload.links,
199
- });
229
+ const rawAttachmentPayload = attachmentLinks.payload;
230
+ const hasAuthoritativeCardCover = Object.prototype.hasOwnProperty.call(rawAttachmentPayload, 'cardCoverAssetId');
231
+ const validatedAttachmentLinksPayload = hasAuthoritativeCardCover
232
+ ? validateWorkspaceSyncV3TaskAttachmentLinksV2Payload(rawAttachmentPayload)
233
+ : validateWorkspaceSyncV3TaskAttachmentLinksPayload(rawAttachmentPayload);
234
+ const ownedAttachmentProjection = resolveOwnedAttachmentProjection(input);
235
+ const attachmentLinksPayload = ownedAttachmentProjection === 'task-attachment-links/full-v1'
236
+ ? serializeWorkspaceSyncV3TaskAttachmentLinksPayload(validatedAttachmentLinksPayload)
237
+ : validatedAttachmentLinksPayload;
238
+ const authoritativeCardCoverAssetId = hasAuthoritativeCardCover
239
+ && ownedAttachmentProjection !== 'task-attachment-links/full-v1'
240
+ ? validatedAttachmentLinksPayload
241
+ .cardCoverAssetId
242
+ : undefined;
200
243
  const activityPayload = serializeTaskActivityForSync(activity);
201
244
  const expectedActivityId = buildTaskAttachmentLinksActivityEventId({
202
245
  workspaceId: input.identity.workspaceId, clientId: input.identity.clientId,
@@ -256,6 +299,9 @@ export function applyAuthoritativeTaskAttachmentLinksAndAck(input) {
256
299
  completedAt: rootPayload.completedAt,
257
300
  canceledReason: rootPayload.canceledReason,
258
301
  attachments,
302
+ ...(authoritativeCardCoverAssetId !== undefined
303
+ ? { cardCoverAssetId: authoritativeCardCoverAssetId }
304
+ : {}),
259
305
  };
260
306
  const updated = input.core.updateTask(root.entityId, rootPatch, input.identity.workspaceId, {
261
307
  persistBackup: false, emitRealtime: false, skipTaskEvents: true,
@@ -270,9 +316,15 @@ export function applyAuthoritativeTaskAttachmentLinksAndAck(input) {
270
316
  input.core.upsertEntityEventForSync(activity, { allowAuthoritativeReplacement: true });
271
317
  const installedLinks = input.core.listTaskAttachmentLinksForDurableSync(updated.id, input.identity.workspaceId);
272
318
  if (canonicalWorkspaceSyncJson(serializeTaskRootForSync(updated)) !== canonicalWorkspaceSyncJson(rootPayload)
273
- || canonicalWorkspaceSyncJson(serializeWorkspaceSyncV3TaskAttachmentLinksPayload({
274
- taskId: updated.id, links: installedLinks,
275
- })) !== canonicalWorkspaceSyncJson(attachmentLinksPayload)) {
319
+ || canonicalWorkspaceSyncJson(authoritativeCardCoverAssetId !== undefined
320
+ ? serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload({
321
+ taskId: updated.id,
322
+ links: installedLinks,
323
+ cardCoverAssetId: updated.cardCoverAssetId ?? null,
324
+ })
325
+ : serializeWorkspaceSyncV3TaskAttachmentLinksPayload({
326
+ taskId: updated.id, links: installedLinks,
327
+ })) !== canonicalWorkspaceSyncJson(attachmentLinksPayload)) {
276
328
  throw new LocalAuthoritativeTaskAttachmentLinksError('Installed task attachment-link state does not match authority.');
277
329
  }
278
330
  return updated;
@@ -5,7 +5,7 @@ import { validateTaskActivityAuthoritativeRevision } from './taskActivityAuthori
5
5
  import { serializeEntityActivityForSync, serializeTaskActivityForSync, WORKSPACE_SYNC_ENTITY_EVENT_APPEND, WORKSPACE_SYNC_ENTITY_EVENT_DOMAIN, } from './taskActivityJournal.js';
6
6
  import { fingerprintWorkspaceSyncPayload, } from './syncDurabilityStore.js';
7
7
  import { serializeTaskRootForSync } from './workspaceSyncV3TaskRootProjection.js';
8
- import { serializeWorkspaceSyncV3TaskAttachmentLinksPayload, serializeWorkspaceSyncV3TaskChecklistPayload, serializeWorkspaceSyncV3TaskCommentsPayload, validateWorkspaceSyncV3TaskAttachmentLinksPayload, validateWorkspaceSyncV3TaskChecklistPayload, } from './workspaceSyncV3NestedTaskCodecs.js';
8
+ import { serializeWorkspaceSyncV3TaskAttachmentLinksPayload, serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload, serializeWorkspaceSyncV3TaskChecklistPayload, serializeWorkspaceSyncV3TaskCommentsPayload, validateWorkspaceSyncV3TaskAttachmentLinksPayload, validateWorkspaceSyncV3TaskAttachmentLinksV2Payload, validateWorkspaceSyncV3TaskChecklistPayload, } from './workspaceSyncV3NestedTaskCodecs.js';
9
9
  export class LocalAuthoritativeTaskCreateError extends Error {
10
10
  code = 'WORKSPACE_SYNC_AUTHORITATIVE_RESPONSE_INVALID';
11
11
  constructor(message) {
@@ -264,7 +264,9 @@ export function applyAuthoritativeTaskCreateAndAck(input) {
264
264
  throw new LocalAuthoritativeTaskCreateError('Authoritative task create attachment-link projection is invalid.');
265
265
  }
266
266
  try {
267
- canonicalAttachmentProjection = validateWorkspaceSyncV3TaskAttachmentLinksPayload(attachmentProjection.payload);
267
+ canonicalAttachmentProjection = Object.prototype.hasOwnProperty.call(attachmentProjection.payload, 'cardCoverAssetId')
268
+ ? validateWorkspaceSyncV3TaskAttachmentLinksV2Payload(attachmentProjection.payload)
269
+ : validateWorkspaceSyncV3TaskAttachmentLinksPayload(attachmentProjection.payload);
268
270
  }
269
271
  catch {
270
272
  throw new LocalAuthoritativeTaskCreateError('Authoritative task create attachment-link payload is invalid.');
@@ -272,10 +274,16 @@ export function applyAuthoritativeTaskCreateAndAck(input) {
272
274
  if (canonicalAttachmentProjection.taskId !== task.id) {
273
275
  throw new LocalAuthoritativeTaskCreateError('Authoritative task create attachment-link identity is invalid.');
274
276
  }
275
- const capturedAttachmentProjection = serializeWorkspaceSyncV3TaskAttachmentLinksPayload({
276
- taskId: task.id,
277
- links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.identity.workspaceId),
278
- });
277
+ const capturedAttachmentProjection = 'cardCoverAssetId' in canonicalAttachmentProjection
278
+ ? serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload({
279
+ taskId: task.id,
280
+ links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.identity.workspaceId),
281
+ cardCoverAssetId: task.cardCoverAssetId ?? null,
282
+ })
283
+ : serializeWorkspaceSyncV3TaskAttachmentLinksPayload({
284
+ taskId: task.id,
285
+ links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.identity.workspaceId),
286
+ });
279
287
  const relationshipIntent = (projection) => ({
280
288
  taskId: projection.taskId,
281
289
  links: projection.links.map(({ assetId, role, order, displayName }) => ({
@@ -284,6 +292,7 @@ export function applyAuthoritativeTaskCreateAndAck(input) {
284
292
  order,
285
293
  ...(displayName ? { displayName } : {}),
286
294
  })),
295
+ ...('cardCoverAssetId' in projection ? { cardCoverAssetId: projection.cardCoverAssetId } : {}),
287
296
  });
288
297
  if (fingerprintWorkspaceSyncPayload(relationshipIntent(canonicalAttachmentProjection))
289
298
  !== fingerprintWorkspaceSyncPayload(relationshipIntent(capturedAttachmentProjection))) {
@@ -7,6 +7,7 @@ export declare function createLocalTaskMetadataClient(input?: LocalTaskOperation
7
7
  entityId: string;
8
8
  patch: Partial<TaskItem>;
9
9
  saveSource?: "manual" | "autosave";
10
+ expectedDetailRevision?: string;
10
11
  isAuthenticated: boolean;
11
12
  dispatchEnabled?: boolean;
12
13
  }): Promise<LocalTaskMetadataMutationResult>;
@@ -15,6 +15,7 @@ export function createLocalTaskMetadataClient(input = {}) {
15
15
  payload: {
16
16
  patch,
17
17
  ...(inputValue.saveSource ? { saveSource: inputValue.saveSource } : {}),
18
+ ...(inputValue.expectedDetailRevision ? { expectedDetailRevision: inputValue.expectedDetailRevision } : {}),
18
19
  },
19
20
  };
20
21
  return client.execute({
@@ -18,12 +18,15 @@ function buildTaskMetadataEnvelope(record) {
18
18
  throw new Error('Local outbox payload must be an object.');
19
19
  }
20
20
  const payloadRecord = payload;
21
- const allowedKeys = new Set(['patch', 'saveSource', 'originActor']);
21
+ const allowedKeys = new Set(['patch', 'saveSource', 'expectedDetailRevision', 'originActor']);
22
22
  if (!Object.prototype.hasOwnProperty.call(payloadRecord, 'patch')
23
23
  || Object.keys(payloadRecord).some((key) => !allowedKeys.has(key))) {
24
24
  throw new Error('Local task metadata outbox payload must contain patch and optional saveSource/originActor.');
25
25
  }
26
26
  const saveSource = payloadRecord.saveSource;
27
+ const expectedDetailRevision = typeof payloadRecord.expectedDetailRevision === 'string'
28
+ ? payloadRecord.expectedDetailRevision
29
+ : undefined;
27
30
  if (saveSource !== undefined && saveSource !== 'manual' && saveSource !== 'autosave') {
28
31
  throw new Error('Local task metadata outbox saveSource must be manual or autosave.');
29
32
  }
@@ -41,6 +44,7 @@ function buildTaskMetadataEnvelope(record) {
41
44
  payload: {
42
45
  patch: validateDurableTaskMetadataPatch(payloadRecord.patch),
43
46
  ...(saveSource ? { saveSource } : {}),
47
+ ...(expectedDetailRevision ? { expectedDetailRevision } : {}),
44
48
  ...(payloadRecord.originActor !== undefined
45
49
  ? { originActor: parseWorkspaceSyncV3OriginActor(payloadRecord.originActor) }
46
50
  : {}),
@@ -17,6 +17,7 @@ export interface LocalTaskMetadataOutboxCore {
17
17
  allowAuthoritativeReplacement?: boolean;
18
18
  }): unknown;
19
19
  notifyDurableTaskMetadataCommitted(workspaceId: string, taskId: string): void;
20
+ assertTaskDetailRevisionForUpdate?(id: string, expectedDetailRevision: string, workspaceId?: string): void;
20
21
  }
21
22
  export declare class LocalAuthoritativeTaskMetadataError extends Error {
22
23
  readonly code = "WORKSPACE_SYNC_AUTHORITATIVE_RESPONSE_INVALID";
@@ -29,6 +30,7 @@ export declare function enqueueLocalTaskMetadataMutation(input: {
29
30
  taskId: string;
30
31
  patch: Partial<TaskItem>;
31
32
  saveSource?: 'manual' | 'autosave';
33
+ expectedDetailRevision?: string;
32
34
  now: string;
33
35
  actorRef: string;
34
36
  requireV3CombinedOwnership?: boolean;
@@ -30,10 +30,17 @@ export function enqueueLocalTaskMetadataMutation(input) {
30
30
  payload: withOriginActorPayload({
31
31
  patch,
32
32
  ...(input.saveSource ? { saveSource: input.saveSource } : {}),
33
+ ...(input.expectedDetailRevision ? { expectedDetailRevision: input.expectedDetailRevision } : {}),
33
34
  }, input.actorRef),
34
35
  ...(input.requireV3CombinedOwnership ? { requireV3PlanningOwnership: 'combined' } : {}),
35
36
  now: input.now,
36
37
  applyLocalMutation: () => {
38
+ if (input.expectedDetailRevision) {
39
+ if (!input.core.assertTaskDetailRevisionForUpdate) {
40
+ throw new DurableTaskMetadataMutationError('Task detail revision checks are unavailable.', 'DURABLE_TASK_PATCH_INVALID', 500);
41
+ }
42
+ input.core.assertTaskDetailRevisionForUpdate(input.taskId, input.expectedDetailRevision, input.identity.workspaceId);
43
+ }
37
44
  const task = input.core.updateTask(input.taskId, patch, input.identity.workspaceId, {
38
45
  persistBackup: false,
39
46
  emitRealtime: false,
@@ -5,7 +5,7 @@ import { buildTaskLifecycleActivityEventId } from './taskLifecycleActivityIdenti
5
5
  import { validateTaskActivityAuthoritativeRevision } from './taskActivityAuthoritativeRevision.js';
6
6
  import { serializeTaskActivityForSync, WORKSPACE_SYNC_ENTITY_EVENT_APPEND, WORKSPACE_SYNC_ENTITY_EVENT_DOMAIN, } from './taskActivityJournal.js';
7
7
  import { fingerprintWorkspaceSyncPayload, WorkspaceSyncIdempotencyConflictError, } from './syncDurabilityStore.js';
8
- import { serializeWorkspaceSyncV3TaskAttachmentLinksPayload, serializeWorkspaceSyncV3TaskChecklistPayload, serializeWorkspaceSyncV3TaskCommentsPayload, validateWorkspaceSyncV3TaskAttachmentLinksPayload, validateWorkspaceSyncV3TaskChecklistPayload, validateWorkspaceSyncV3TaskCommentsPayload, } from './workspaceSyncV3NestedTaskCodecs.js';
8
+ import { serializeWorkspaceSyncV3TaskAttachmentLinksPayload, serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload, serializeWorkspaceSyncV3TaskChecklistPayload, serializeWorkspaceSyncV3TaskCommentsPayload, validateWorkspaceSyncV3TaskAttachmentLinksV2Payload, validateWorkspaceSyncV3TaskAttachmentLinksPayload, validateWorkspaceSyncV3TaskChecklistPayload, validateWorkspaceSyncV3TaskCommentsPayload, } from './workspaceSyncV3NestedTaskCodecs.js';
9
9
  export class LocalAuthoritativeTaskRestoreError extends Error {
10
10
  code = 'WORKSPACE_SYNC_AUTHORITATIVE_RESPONSE_INVALID';
11
11
  constructor(message) {
@@ -190,7 +190,9 @@ export function applyAuthoritativeTaskRestoreAndAck(input) {
190
190
  let canonicalAttachments;
191
191
  try {
192
192
  canonicalChecklist = validateWorkspaceSyncV3TaskChecklistPayload(checklist.payload);
193
- canonicalAttachments = validateWorkspaceSyncV3TaskAttachmentLinksPayload(attachments.payload);
193
+ canonicalAttachments = Object.prototype.hasOwnProperty.call(attachments.payload, 'cardCoverAssetId')
194
+ ? validateWorkspaceSyncV3TaskAttachmentLinksV2Payload(attachments.payload)
195
+ : validateWorkspaceSyncV3TaskAttachmentLinksPayload(attachments.payload);
194
196
  }
195
197
  catch {
196
198
  throw new LocalAuthoritativeTaskRestoreError('Authoritative task restore nested projection is invalid.');
@@ -214,10 +216,16 @@ export function applyAuthoritativeTaskRestoreAndAck(input) {
214
216
  !== fingerprintWorkspaceSyncPayload(serializeWorkspaceSyncV3TaskChecklistPayload({ taskId: task.id, items: task.checklistItems || [] }))) {
215
217
  throw new LocalAuthoritativeTaskRestoreError('Authoritative task restore projections do not match the task payload.');
216
218
  }
217
- const capturedAttachments = serializeWorkspaceSyncV3TaskAttachmentLinksPayload({
218
- taskId: task.id,
219
- links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.identity.workspaceId),
220
- });
219
+ const capturedAttachments = 'cardCoverAssetId' in canonicalAttachments
220
+ ? serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload({
221
+ taskId: task.id,
222
+ links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.identity.workspaceId),
223
+ cardCoverAssetId: task.cardCoverAssetId ?? null,
224
+ })
225
+ : serializeWorkspaceSyncV3TaskAttachmentLinksPayload({
226
+ taskId: task.id,
227
+ links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.identity.workspaceId),
228
+ });
221
229
  if (fingerprintWorkspaceSyncPayload(canonicalAttachments)
222
230
  !== fingerprintWorkspaceSyncPayload(capturedAttachments)) {
223
231
  throw new LocalAuthoritativeTaskRestoreError('Authoritative task restore attachment links do not match the captured projection.');
@@ -9,6 +9,7 @@ export type LocalTaskStatusMutationInput = {
9
9
  assigneeIntent: DurableTaskStatusAssigneeIntent;
10
10
  canceledReason: string | null;
11
11
  metadataPatch?: Partial<TaskItem>;
12
+ expectedDetailRevision?: string;
12
13
  isAuthenticated: boolean;
13
14
  dispatchEnabled?: boolean;
14
15
  };
@@ -20,7 +20,12 @@ export function createLocalTaskStatusClient(input = {}) {
20
20
  domain: 'task',
21
21
  entityId,
22
22
  mutationKind: 'set-status',
23
- payload,
23
+ payload: {
24
+ ...payload,
25
+ ...(inputValue.expectedDetailRevision
26
+ ? { expectedDetailRevision: inputValue.expectedDetailRevision }
27
+ : {}),
28
+ },
24
29
  };
25
30
  return client.execute({
26
31
  workspaceId,
@@ -3,6 +3,7 @@ import { type DurableTaskStatusMutationCore, type DurableTaskStatusMutationPaylo
3
3
  import { WorkspaceSyncDurabilityStore, type WorkspaceSyncOperationIdentity, type WorkspaceSyncOutboxRecord } from './syncDurabilityStore.js';
4
4
  import type { DatabaseAdapter } from '../../storage/databaseAdapter.js';
5
5
  export interface LocalTaskStatusOutboxCore extends DurableTaskStatusMutationCore {
6
+ assertTaskDetailRevisionForUpdate?(id: string, expectedDetailRevision: string, workspaceId?: string): void;
6
7
  getDatabaseAdapter(): DatabaseAdapter;
7
8
  getTenantId(): string;
8
9
  installDurableTaskStatusAuthoritativeForSync(task: TaskItem, payload: DurableTaskStatusMutationPayload, installAssignee: boolean, workspaceId: string): TaskItem;
@@ -33,6 +34,7 @@ export declare function enqueueLocalTaskStatusMutation(input: {
33
34
  identity: WorkspaceSyncOperationIdentity;
34
35
  taskId: string;
35
36
  payload: unknown;
37
+ expectedDetailRevision?: string;
36
38
  now: string;
37
39
  actorRef: string;
38
40
  requireV3CombinedOwnership?: boolean;
@@ -74,23 +74,42 @@ export function enqueueLocalTaskStatusMutation(input) {
74
74
  taskLifecycle: true,
75
75
  taskAssignee: payload.assigneeIntent.kind !== 'preserve',
76
76
  },
77
- payload: withOriginActorPayload({ ...payload }, input.actorRef),
77
+ payload: withOriginActorPayload({
78
+ ...payload,
79
+ ...(input.expectedDetailRevision
80
+ ? { expectedDetailRevision: input.expectedDetailRevision }
81
+ : {}),
82
+ }, input.actorRef),
78
83
  ...(payload.workflowAssignment ? {
79
84
  coalesceWorkflowAssignmentExecutionId: payload.workflowAssignment.execution.id,
80
85
  } : {}),
81
86
  ...(input.requireV3CombinedOwnership ? { requireV3PlanningOwnership: 'combined' } : {}),
82
87
  now: input.now,
83
- applyLocalMutation: () => applyTaskStatusMutationForDurableSync({
84
- core: input.core,
85
- workspaceId: input.identity.workspaceId,
86
- taskId,
87
- targetStatus: statusPayload.status,
88
- metadataPatch: statusPayload.metadataPatch,
89
- assigneeIntent: statusPayload.assigneeIntent,
90
- canceledReason: statusPayload.canceledReason,
91
- updatedAt: input.now,
92
- actorRef: input.actorRef,
93
- taskEventId: activityEventId,
88
+ applyLocalMutation: () => {
89
+ if (input.expectedDetailRevision) {
90
+ if (!input.core.assertTaskDetailRevisionForUpdate) {
91
+ throw new Error('Task detail revision checks are unavailable.');
92
+ }
93
+ input.core.assertTaskDetailRevisionForUpdate(taskId, input.expectedDetailRevision, input.identity.workspaceId);
94
+ }
95
+ return applyTaskStatusMutationForDurableSync({
96
+ core: input.core,
97
+ workspaceId: input.identity.workspaceId,
98
+ taskId,
99
+ targetStatus: statusPayload.status,
100
+ metadataPatch: statusPayload.metadataPatch,
101
+ assigneeIntent: statusPayload.assigneeIntent,
102
+ canceledReason: statusPayload.canceledReason,
103
+ updatedAt: input.now,
104
+ actorRef: input.actorRef,
105
+ taskEventId: activityEventId,
106
+ });
107
+ },
108
+ buildCanonicalIntent: (result) => ({
109
+ metadataPatch: Object.fromEntries(Object.keys(payload.metadataPatch).map((key) => [
110
+ key,
111
+ result.task[key] ?? null,
112
+ ])),
94
113
  }),
95
114
  });
96
115
  if (!result.enqueued)
@@ -189,9 +208,31 @@ export function applyAuthoritativeTaskStatusAndAck(input) {
189
208
  const localCanonical = input.core.getTask(task.id, input.identity.workspaceId);
190
209
  if (!localCanonical)
191
210
  throw new LocalAuthoritativeTaskStatusError('Local task status projection is missing.');
211
+ let canonicalMetadataIntent = null;
212
+ if (input.record.canonicalIntentJson) {
213
+ try {
214
+ const parsed = JSON.parse(input.record.canonicalIntentJson);
215
+ canonicalMetadataIntent = parsed.metadataPatch
216
+ && typeof parsed.metadataPatch === 'object'
217
+ && !Array.isArray(parsed.metadataPatch)
218
+ ? parsed.metadataPatch
219
+ : null;
220
+ }
221
+ catch {
222
+ canonicalMetadataIntent = null;
223
+ }
224
+ if (!canonicalMetadataIntent) {
225
+ throw new LocalAuthoritativeTaskStatusError('Persisted canonical task status intent is invalid.');
226
+ }
227
+ }
192
228
  for (const key of Object.keys(payload.metadataPatch)) {
229
+ if (canonicalMetadataIntent && !Object.prototype.hasOwnProperty.call(canonicalMetadataIntent, key)) {
230
+ throw new LocalAuthoritativeTaskStatusError('Persisted canonical task status intent is incomplete.');
231
+ }
193
232
  const actual = task[key];
194
- const expected = localCanonical[key];
233
+ const expected = canonicalMetadataIntent
234
+ ? canonicalMetadataIntent[key]
235
+ : localCanonical[key];
195
236
  if (fingerprintWorkspaceSyncPayload(actual ?? null) !== fingerprintWorkspaceSyncPayload(expected ?? null)) {
196
237
  throw new LocalAuthoritativeTaskStatusError('Authoritative task metadata does not match the persisted request.');
197
238
  }
@@ -300,8 +300,9 @@ function validateActivitySemantics(input) {
300
300
  const authoritativeAttachmentLinks = validateCanonicalPlanningAttachmentLinks(input.authoritative.attachments);
301
301
  const comparableExpectedDetails = normalizePlanningActivityDetailsForComparison(expectedDetails, input.record.mutationKind, authoritativeAttachmentLinks);
302
302
  const comparableAuthoritativeDetails = normalizePlanningActivityDetailsForComparison((input.activity.details || {}), input.record.mutationKind, authoritativeAttachmentLinks);
303
- if (fingerprintWorkspaceSyncPayload(comparableExpectedDetails)
304
- !== fingerprintWorkspaceSyncPayload(comparableAuthoritativeDetails)) {
303
+ if (!input.boundedAttachmentRebase
304
+ && fingerprintWorkspaceSyncPayload(comparableExpectedDetails)
305
+ !== fingerprintWorkspaceSyncPayload(comparableAuthoritativeDetails)) {
305
306
  throw new LocalAuthoritativeWorkstreamError('Authoritative workstream activity details differ from the captured mutation.');
306
307
  }
307
308
  }
@@ -500,6 +501,7 @@ export function applyAuthoritativeWorkstreamAndAck(input) {
500
501
  payload: payload,
501
502
  authoritative: workstream,
502
503
  activity,
504
+ boundedAttachmentRebase,
503
505
  });
504
506
  const revision = validateWorkstreamActivityAuthoritativeRevision({
505
507
  response: response,