@taskforcehq/taskforce 0.3.328 → 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 (264) hide show
  1. package/dist/Taskforce.module.css +233 -34
  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/AnnotatedAttachmentWorkspace.js +24 -8
  8. package/dist/components/features/TaskSettings.d.ts +12 -1
  9. package/dist/components/features/TaskSettings.js +147 -40
  10. package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +1 -1
  11. package/dist/components/features/planning/PlanningModule.js +2 -5
  12. package/dist/components/features/planning/PlanningTaskOrderList.d.ts +1 -1
  13. package/dist/components/features/planning/PlanningTaskOrderList.js +17 -6
  14. package/dist/components/features/planning/planningTaskArrangement.d.ts +1 -0
  15. package/dist/components/features/planning/planningTaskArrangement.js +5 -5
  16. package/dist/components/features/taskforceAgentEditorModel.js +3 -1
  17. package/dist/components/task/TaskCard.d.ts +5 -0
  18. package/dist/components/task/TaskCard.js +65 -10
  19. package/dist/components/task/TaskImageReviews.js +14 -5
  20. package/dist/components/task/TaskKanban.d.ts +6 -1
  21. package/dist/components/task/TaskKanban.js +152 -6
  22. package/dist/components/task/TaskList.d.ts +5 -0
  23. package/dist/components/task/TaskList.js +3 -3
  24. package/dist/components/ui/PasswordInput.d.ts +6 -0
  25. package/dist/components/ui/PasswordInput.js +11 -0
  26. package/dist/components/ui/TopNoticeLayer.js +1 -1
  27. package/dist/components/views/AppShellHeader.js +32 -11
  28. package/dist/components/views/PlanComparisonPage.js +12 -0
  29. package/dist/components/views/StandaloneLayout.js +7 -31
  30. package/dist/components/views/WidgetView.js +3 -3
  31. package/dist/components/views/panels/PlanningDrawer.js +5 -12
  32. package/dist/components/views/standalone/modals/AccountSettingsModal.js +26 -17
  33. package/dist/config/envSchema.js +12 -0
  34. package/dist/config/localServiceRuntimeContract.d.ts +13 -0
  35. package/dist/config/localServiceRuntimeContract.js +135 -0
  36. package/dist/core/AiProfileService.js +46 -8
  37. package/dist/core/DeletedTaskLifecycleService.d.ts +1 -0
  38. package/dist/core/DeletedTaskLifecycleService.js +37 -6
  39. package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +3 -2
  40. package/dist/core/TaskAttachmentLinksDurableMutationService.js +33 -9
  41. package/dist/core/Taskforce.d.ts +62 -2
  42. package/dist/core/Taskforce.js +635 -32
  43. package/dist/core/types.d.ts +6 -0
  44. package/dist/hooks/sync/useDurableLocalOutboxDrain.d.ts +1 -1
  45. package/dist/hooks/sync/useDurableTaskCreateMutation.js +1 -1
  46. package/dist/hooks/sync/useDurableTaskMetadataMutation.d.ts +1 -0
  47. package/dist/hooks/sync/useDurableTaskMetadataMutation.js +1 -0
  48. package/dist/hooks/sync/useDurableTaskStatusMutation.d.ts +1 -0
  49. package/dist/hooks/tasks/taskHttpMutation.js +1 -0
  50. package/dist/hooks/tasks/useCloudTaskSearch.d.ts +12 -0
  51. package/dist/hooks/tasks/useCloudTaskSearch.js +71 -0
  52. package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.d.ts +16 -0
  53. package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.js +197 -0
  54. package/dist/hooks/tasks/useTaskEditorNavigation.d.ts +16 -3
  55. package/dist/hooks/tasks/useTaskEditorNavigation.js +72 -13
  56. package/dist/hooks/tasks/useTaskFormActions.d.ts +6 -0
  57. package/dist/hooks/tasks/useTaskFormActions.js +65 -12
  58. package/dist/hooks/ui/useSettingsModel.d.ts +24 -2
  59. package/dist/hooks/ui/useSettingsModel.js +13 -2
  60. package/dist/hooks/ui/useSettingsPersistence.d.ts +12 -1
  61. package/dist/hooks/ui/useSettingsPersistence.js +74 -47
  62. package/dist/hooks/useFilterSort.d.ts +7 -3
  63. package/dist/hooks/useFilterSort.js +15 -5
  64. package/dist/hooks/useTaskData.d.ts +10 -1
  65. package/dist/hooks/useTaskData.js +216 -7
  66. package/dist/hooks/useTaskMutations.d.ts +10 -2
  67. package/dist/hooks/useTaskMutations.js +223 -43
  68. package/dist/hooks/useTaskforce.d.ts +37 -9
  69. package/dist/hooks/useTaskforce.js +349 -39
  70. package/dist/hooks/useUiNotice.d.ts +4 -0
  71. package/dist/hooks/useUiNotice.js +4 -0
  72. package/dist/mcp/adminWorkspaceRegistrar.js +3 -3
  73. package/dist/mcp/documentAssetRegistrar.js +44 -35
  74. package/dist/mcp/documentHelpers.d.ts +1 -0
  75. package/dist/mcp/documentHelpers.js +7 -3
  76. package/dist/mcp/runtime.js +172 -42
  77. package/dist/mcp/taskPlanningRegistrar.js +41 -38
  78. package/dist/mcp/toolCallCompatibility.js +38 -1
  79. package/dist/mcp/toolCatalog.d.ts +1 -0
  80. package/dist/migrations/taskSchemaMigrations.js +12 -0
  81. package/dist/runtime/appGateDefinitions.d.ts +30 -6
  82. package/dist/runtime/appGateDefinitions.js +6 -6
  83. package/dist/runtime/appGates.d.ts +12 -13
  84. package/dist/runtime/appGates.js +29 -13
  85. package/dist/runtime/nodeAppGates.d.ts +8 -0
  86. package/dist/runtime/nodeAppGates.js +27 -0
  87. package/dist/server/cloudCollectionRead.d.ts +1 -1
  88. package/dist/server/cloudCollectionRead.js +2 -2
  89. package/dist/server/index.d.ts +3 -0
  90. package/dist/server/index.js +15 -3
  91. package/dist/server/localServiceLease.d.ts +3 -0
  92. package/dist/server/localServiceLease.js +9 -1
  93. package/dist/server/localWorkspaceSyncServerRuntime.js +10 -3
  94. package/dist/server/rateLimit.d.ts +1 -0
  95. package/dist/server/rateLimit.js +4 -0
  96. package/dist/server/realtimeSyncWs.js +36 -12
  97. package/dist/server/routes/admin.js +1 -0
  98. package/dist/server/routes/agentRoles.js +3 -2
  99. package/dist/server/routes/agentSkills.js +3 -2
  100. package/dist/server/routes/agents.js +118 -16
  101. package/dist/server/routes/annotatedAttachments.js +3 -2
  102. package/dist/server/routes/auth.js +3 -1
  103. package/dist/server/routes/durableTaskHttpRouters.d.ts +9 -2
  104. package/dist/server/routes/localService.js +2 -0
  105. package/dist/server/routes/primitives.d.ts +2 -0
  106. package/dist/server/routes/syncAuxRoutes.js +5 -2
  107. package/dist/server/routes/syncPullApplyRoutes.js +23 -4
  108. package/dist/server/routes/syncV3FeedRoutes.js +18 -16
  109. package/dist/server/routes/syncV3LocalCaptureRoutes.js +28 -2
  110. package/dist/server/routes/syncV3LocalFeedApplyRoutes.d.ts +1 -0
  111. package/dist/server/routes/syncV3LocalFeedApplyRoutes.js +26 -0
  112. package/dist/server/routes/syncV3OperationRoutes.js +10 -0
  113. package/dist/server/routes/tasks.js +103 -11
  114. package/dist/server/routes/workspaces.js +43 -1
  115. package/dist/server/workspaceDeletionAdmission.d.ts +23 -0
  116. package/dist/server/workspaceDeletionAdmission.js +50 -0
  117. package/dist/service/localServiceCli.js +16 -0
  118. package/dist/service/localServiceClientLifecycle.js +36 -10
  119. package/dist/services/codexAppServerModelGateway.d.ts +112 -0
  120. package/dist/services/codexAppServerModelGateway.js +438 -0
  121. package/dist/services/taskforceAgentMcpBridge.d.ts +7 -1
  122. package/dist/services/taskforceAgentMcpBridge.js +23 -3
  123. package/dist/shared/taskforceAgentDefinition.js +7 -1
  124. package/dist/shared/taskforceAgentModels.d.ts +1 -0
  125. package/dist/shared/taskforceAgentModels.js +30 -1
  126. package/dist/storage/postgresAdapter.d.ts +20 -3
  127. package/dist/storage/postgresAdapter.js +106 -14
  128. package/dist/storage/postgresWorker.js +13 -4
  129. package/dist/storage/postgresWorkerProtocol.d.ts +29 -0
  130. package/dist/storage/postgresWorkerProtocol.js +30 -0
  131. package/dist/sync/cloudSyncApi.d.ts +4 -1
  132. package/dist/sync/cloudSyncApi.js +7 -2
  133. package/dist/sync/contentSyncState.d.ts +1 -1
  134. package/dist/sync/coordinator/localSyncExecutionPermitStore.d.ts +1 -0
  135. package/dist/sync/coordinator/localSyncExecutionPermitStore.js +7 -1
  136. package/dist/sync/coordinator/localWorkspaceSyncRunner.js +6 -2
  137. package/dist/sync/engine/workspaceSyncEngineBrowserGatewayRemoteServices.js +20 -2
  138. package/dist/sync/engine/workspaceSyncEngineLocalServices.d.ts +4 -0
  139. package/dist/sync/engine/workspaceSyncEngineLocalServices.js +11 -0
  140. package/dist/sync/engine/workspaceSyncEngineRemoteServices.d.ts +11 -0
  141. package/dist/sync/engine/workspaceSyncEngineRemoteServices.js +2 -1
  142. package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +31 -4
  143. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.d.ts +3 -1
  144. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +25 -8
  145. package/dist/sync/engine/workspaceSyncEngineTransfers.d.ts +1 -1
  146. package/dist/sync/engine/workspaceSyncEngineTransfers.js +8 -3
  147. package/dist/sync/syncService.d.ts +2 -0
  148. package/dist/sync/syncService.js +4 -1
  149. package/dist/sync/v3/combinedFeedCapability.js +4 -0
  150. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.d.ts +2 -0
  151. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +11 -1
  152. package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +2 -0
  153. package/dist/sync/v3/durableTaskChecklistMutationRouter.js +4 -0
  154. package/dist/sync/v3/durableTaskHttpBulkMutationRouter.d.ts +1 -0
  155. package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +30 -8
  156. package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +5 -3
  157. package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +27 -2
  158. package/dist/sync/v3/localTaskAttachmentLinksOutboxService.d.ts +2 -0
  159. package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +47 -7
  160. package/dist/sync/v3/localTaskCreateOutboxService.js +15 -6
  161. package/dist/sync/v3/localTaskMetadataClient.d.ts +1 -0
  162. package/dist/sync/v3/localTaskMetadataClient.js +1 -0
  163. package/dist/sync/v3/localTaskMetadataOutboxHandler.js +5 -1
  164. package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -0
  165. package/dist/sync/v3/localTaskMetadataOutboxService.js +7 -0
  166. package/dist/sync/v3/localTaskRestoreOutboxService.js +14 -6
  167. package/dist/sync/v3/localTaskStatusClient.d.ts +1 -0
  168. package/dist/sync/v3/localTaskStatusClient.js +6 -1
  169. package/dist/sync/v3/localTaskStatusOutboxService.d.ts +2 -0
  170. package/dist/sync/v3/localTaskStatusOutboxService.js +54 -13
  171. package/dist/sync/v3/syncDurabilityStore.d.ts +7 -0
  172. package/dist/sync/v3/syncDurabilityStore.js +31 -11
  173. package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +6 -4
  174. package/dist/sync/v3/taskAttachmentLinksMutationService.js +19 -10
  175. package/dist/sync/v3/taskChecklistMutationService.d.ts +4 -2
  176. package/dist/sync/v3/taskChecklistMutationService.js +4 -0
  177. package/dist/sync/v3/taskCreateMutationService.d.ts +2 -2
  178. package/dist/sync/v3/taskCreateMutationService.js +7 -4
  179. package/dist/sync/v3/taskMetadataMutationService.d.ts +3 -0
  180. package/dist/sync/v3/taskMetadataMutationService.js +4 -0
  181. package/dist/sync/v3/taskRestoreMutationService.d.ts +2 -2
  182. package/dist/sync/v3/taskRestoreMutationService.js +7 -4
  183. package/dist/sync/v3/taskStatusMutationService.d.ts +2 -0
  184. package/dist/sync/v3/taskStatusMutationService.js +4 -0
  185. package/dist/sync/v3/workflowAssignmentSync.d.ts +1 -0
  186. package/dist/sync/v3/workflowAssignmentSync.js +3 -0
  187. package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.d.ts +1 -1
  188. package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +13 -3
  189. package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.d.ts +11 -0
  190. package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.js +27 -10
  191. package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.d.ts +2 -1
  192. package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.js +52 -10
  193. package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +9 -1
  194. package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +8 -3
  195. package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +51 -10
  196. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +7 -0
  197. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +30 -18
  198. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +9 -4
  199. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +98 -47
  200. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +10 -0
  201. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +58 -21
  202. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +3 -1
  203. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +18 -0
  204. package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +6 -0
  205. package/dist/sync/v3/workspaceSyncV3FeedProfile.d.ts +1 -1
  206. package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.d.ts +1 -0
  207. package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.js +23 -10
  208. package/dist/sync/v3/workspaceSyncV3NestedTaskApply.d.ts +3 -0
  209. package/dist/sync/v3/workspaceSyncV3NestedTaskApply.js +64 -13
  210. package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.d.ts +9 -0
  211. package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.js +26 -0
  212. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +15 -5
  213. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +50 -14
  214. package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.d.ts +2 -1
  215. package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.js +11 -3
  216. package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +2 -0
  217. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +2 -0
  218. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +19 -7
  219. package/dist/sync/v3/workspaceSyncV3TaskCommentAudit.js +9 -2
  220. package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.d.ts +7 -0
  221. package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.js +37 -0
  222. package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.d.ts +6 -0
  223. package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.js +9 -0
  224. package/dist/sync/workspacePullFeed.js +2 -1
  225. package/dist/sync/workspaceRepair.d.ts +3 -0
  226. package/dist/sync/workspaceRepair.js +67 -6
  227. package/dist/types.d.ts +47 -1
  228. package/dist/types.js +6 -0
  229. package/dist/ui/assets/{AiIdentityRosterCard-MZs7lVED.js → AiIdentityRosterCard-BZM4aQnM.js} +1 -1
  230. package/dist/ui/assets/AiProfilesModule-BbAEWtHH.js +1 -0
  231. package/dist/ui/assets/AnnotatedAttachmentWorkspace-B68Wg-RV.js +3 -0
  232. package/dist/ui/assets/AnnotatedAttachmentWorkspace-TzYu7pkG.css +1 -0
  233. package/dist/ui/assets/{AssetTraySortControl-Kf1pelSy.js → AssetTraySortControl-C8Rztyal.js} +1 -1
  234. package/dist/ui/assets/{ContextAttachmentManager-B8boOs6Q.js → ContextAttachmentManager-Cj3c7X9y.js} +2 -2
  235. package/dist/ui/assets/{DocumentWorkspace-DXH4XUsB.js → DocumentWorkspace-B5uvzems.js} +1 -1
  236. package/dist/ui/assets/{EntityActivityTimeline-BtSTq0ON.js → EntityActivityTimeline-BVFvCB5R.js} +1 -1
  237. package/dist/ui/assets/PlanningModule-B5DWQOGz.js +1 -0
  238. package/dist/ui/assets/PlansPage-AY4cGWco.js +1 -0
  239. package/dist/ui/assets/TaskContextUpload-DZxTVesW.js +1 -0
  240. package/dist/ui/assets/TaskSettings-BeWAuyZj.js +12 -0
  241. package/dist/ui/assets/{TaskforceAgentsModule-BazdUwxZ.js → TaskforceAgentsModule-BifCYtjR.js} +6 -6
  242. package/dist/ui/assets/{WorkflowManagerModule-DWFspC-o.js → WorkflowManagerModule-DEr5di_r.js} +1 -1
  243. package/dist/ui/assets/index-I5zhdtgt.js +7 -0
  244. package/dist/ui/assets/index-VRFsx3wz.css +1 -0
  245. package/dist/ui/assets/{vendor-icons-BkFLXavV.js → vendor-icons-D9Lpw-j4.js} +1 -1
  246. package/dist/ui/index.html +3 -3
  247. package/dist/utils/taskActivity.d.ts +3 -2
  248. package/dist/utils/taskActivity.js +19 -2
  249. package/dist/utils/taskEditorReconciliation.d.ts +40 -0
  250. package/dist/utils/taskEditorReconciliation.js +217 -0
  251. package/dist/utils/taskNormalization.d.ts +1 -0
  252. package/dist/utils/taskNormalization.js +39 -0
  253. package/dist/utils/taskSearch.js +2 -2
  254. package/dist/utils/uiNotice.d.ts +4 -0
  255. package/package.json +16 -4
  256. package/dist/ui/assets/AiProfilesModule-C3i0LOxe.js +0 -1
  257. package/dist/ui/assets/AnnotatedAttachmentWorkspace-CBRZ8rca.css +0 -1
  258. package/dist/ui/assets/AnnotatedAttachmentWorkspace-DVZk5Tln.js +0 -3
  259. package/dist/ui/assets/PlanningModule-BPVyEa-f.js +0 -1
  260. package/dist/ui/assets/PlansPage-ChqgxALc.js +0 -1
  261. package/dist/ui/assets/TaskContextUpload-CB3ol0sN.js +0 -1
  262. package/dist/ui/assets/TaskSettings-g4ECKNOz.js +0 -12
  263. package/dist/ui/assets/index-BUplSsv_.js +0 -7
  264. package/dist/ui/assets/index-gz2EXuoK.css +0 -1
@@ -8,12 +8,13 @@ import { serializeTaskRootForSync, projectWorkspaceSyncV3TaskRootJournalRow } fr
8
8
  import { projectWorkspaceSyncV3TaxonomyJournalRow } from './workspaceSyncV3TaxonomyFeedProjection.js';
9
9
  import { projectWorkspaceSyncV3WorkstreamJournalRow } from './workspaceSyncV3WorkstreamProjection.js';
10
10
  import { buildWorkspaceSyncV3NestedTaskRepairCandidates, } from './workspaceSyncV3NestedTaskRepairSnapshot.js';
11
- import { projectWorkspaceSyncV3NestedTaskJournalRow } from './workspaceSyncV3NestedTaskProjection.js';
11
+ import { projectWorkspaceSyncV3NestedTaskJournalRow, projectWorkspaceSyncV3NestedTaskV2JournalRow, } from './workspaceSyncV3NestedTaskProjection.js';
12
12
  import { validateWorkspaceSyncV3NestedCoverageFeedEntry, WORKSPACE_SYNC_V3_NESTED_COVERAGE_PROFILE, } from './workspaceSyncV3NestedCoverage.js';
13
13
  import { serializeWorkflowExecutionEventSyncPayload, serializeWorkflowExecutionRootSyncPayload, serializeWorkflowPublishedDefinitionSyncPayload, } from './workflowRuntimeSync.js';
14
14
  import { projectWorkspaceSyncV3WorkflowRuntimeJournalRow, } from './workspaceSyncV3WorkflowRuntimeProjection.js';
15
15
  import { validateWorkspaceSyncV3WorkflowCoverageFeedEntry, WORKSPACE_SYNC_V3_WORKFLOW_COVERAGE_PROFILE, } from './workspaceSyncV3WorkflowCoverage.js';
16
16
  import { validateWorkspaceSyncV3PlanningOrderCoverageFeedEntry, WORKSPACE_SYNC_V3_PLANNING_ORDER_COVERAGE_PROFILE, } from './workspaceSyncV3PlanningOrderCoverage.js';
17
+ import { validateWorkspaceSyncV3TaskCoverCoverageFeedEntry, WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_PROFILE, } from './workspaceSyncV3TaskCoverCoverage.js';
17
18
  import { projectWorkspaceSyncV3WorkstreamTaskOrderJournalRow } from './workspaceSyncV3WorkstreamTaskOrderProjection.js';
18
19
  import { serializeWorkstreamTaskOrderForSync } from './workstreamTaskOrderSync.js';
19
20
  import { serializeTaxonomyStateForSync } from './taxonomySyncPayload.js';
@@ -28,6 +29,8 @@ const WORKFLOW_PROJECTION_RANK = new Map(WORKSPACE_SYNC_V3_WORKFLOW_COVERAGE_PRO
28
29
  .map((projection, index) => [projection.projectionId, index]));
29
30
  const PLANNING_ORDER_PROJECTION_RANK = new Map(WORKSPACE_SYNC_V3_PLANNING_ORDER_COVERAGE_PROFILE.projections
30
31
  .map((projection, index) => [projection.projectionId, index]));
32
+ const TASK_COVER_PROJECTION_RANK = new Map(WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_PROFILE.projections
33
+ .map((projection, index) => [projection.projectionId, index]));
31
34
  export class WorkspaceSyncV3CombinedRepairBaselineRequiredError extends Error {
32
35
  missing;
33
36
  code = 'WORKSPACE_SYNC_V3_COMBINED_REPAIR_BASELINE_REQUIRED';
@@ -42,11 +45,13 @@ function compareCodeUnits(left, right) {
42
45
  function projectionRank(entry, coverage) {
43
46
  const ranks = coverage === 'workflow'
44
47
  ? WORKFLOW_PROJECTION_RANK
45
- : coverage === 'planning-order'
46
- ? PLANNING_ORDER_PROJECTION_RANK
47
- : coverage === 'nested'
48
- ? NESTED_PROJECTION_RANK
49
- : COMBINED_PROJECTION_RANK;
48
+ : coverage === 'task-cover'
49
+ ? TASK_COVER_PROJECTION_RANK
50
+ : coverage === 'planning-order'
51
+ ? PLANNING_ORDER_PROJECTION_RANK
52
+ : coverage === 'nested'
53
+ ? NESTED_PROJECTION_RANK
54
+ : COMBINED_PROJECTION_RANK;
50
55
  const rank = ranks.get(entry.projection);
51
56
  if (rank === undefined)
52
57
  throw new Error(`Unsupported combined repair projection: ${entry.projection}.`);
@@ -151,8 +156,7 @@ function listLiveEvents(core, workspaceId, liveEntityIds) {
151
156
  cursor = { watermark: last.createdAt, id: eventCursorId(last) };
152
157
  }
153
158
  }
154
- /** Candidate v4 inventory. The coverage-v3 inventory above intentionally remains unchanged. */
155
- export function listWorkspaceSyncV3NestedRepairCandidates(core, workspaceId) {
159
+ function listNestedRepairCandidates(core, workspaceId, attachmentProjection) {
156
160
  const startedAt = Date.now();
157
161
  const tasks = listTasks(core, workspaceId);
158
162
  const tasksCompletedAt = Date.now();
@@ -185,6 +189,7 @@ export function listWorkspaceSyncV3NestedRepairCandidates(core, workspaceId) {
185
189
  tasks,
186
190
  events,
187
191
  attachmentLinksByTaskId,
192
+ attachmentProjection,
188
193
  }),
189
194
  ];
190
195
  const completedAt = Date.now();
@@ -210,6 +215,14 @@ export function listWorkspaceSyncV3NestedRepairCandidates(core, workspaceId) {
210
215
  }
211
216
  return candidates;
212
217
  }
218
+ /** Candidate v4 inventory. The coverage-v3 inventory above intentionally remains unchanged. */
219
+ export function listWorkspaceSyncV3NestedRepairCandidates(core, workspaceId) {
220
+ return listNestedRepairCandidates(core, workspaceId, 'task-attachment-links/full-v1');
221
+ }
222
+ /** Candidate v7 inventory. It is the v4 family with attachment-link schema v2. */
223
+ export function listWorkspaceSyncV3TaskCoverRepairCandidates(core, workspaceId) {
224
+ return listNestedRepairCandidates(core, workspaceId, 'task-attachment-links/full-v2');
225
+ }
213
226
  /** Candidate v5 inventory. Coverage-v3/v4 inventories intentionally remain unchanged. */
214
227
  export function listWorkspaceSyncV3WorkflowRepairCandidates(core, workspaceId) {
215
228
  const workflowStore = new WorkflowStore(core.getDatabaseAdapter(), core.getTenantId());
@@ -380,7 +393,7 @@ function taskEntry(candidate, row, priorFullRow) {
380
393
  return { ...projected, mutationKind, payload: candidate.payload,
381
394
  payloadFingerprint: fingerprintWorkspaceSyncPayload(candidate.payload) };
382
395
  }
383
- function entry(candidate, row, taskFullProvenance) {
396
+ function entry(candidate, row, taskFullProvenance, taskCoverCoverage = false) {
384
397
  if (candidate.domain === 'workstream-task-order') {
385
398
  const projected = projectWorkspaceSyncV3WorkstreamTaskOrderJournalRow(row);
386
399
  if (!projected
@@ -401,7 +414,9 @@ function entry(candidate, row, taskFullProvenance) {
401
414
  }
402
415
  if (candidate.domain === 'task-comments' || candidate.domain === 'task-checklist'
403
416
  || candidate.domain === 'task-attachment-links' || candidate.domain === 'entity-event') {
404
- const projected = projectWorkspaceSyncV3NestedTaskJournalRow(row);
417
+ const projected = taskCoverCoverage && candidate.domain === 'task-attachment-links'
418
+ ? projectWorkspaceSyncV3NestedTaskV2JournalRow(row)
419
+ : projectWorkspaceSyncV3NestedTaskJournalRow(row);
405
420
  if (!projected) {
406
421
  throw new Error(`${candidate.domain} repair revision does not match canonical state: ${candidate.entityId}.`);
407
422
  }
@@ -475,22 +490,26 @@ export function buildWorkspaceSyncV3CombinedRepairSnapshotFromLockedCandidates(i
475
490
  .map((candidate) => candidate.entityId);
476
491
  const taskFullProvenance = readTaskFullProvenanceRows({ db, tenantId, workspaceId, taskIds: remappedTaskIds });
477
492
  const provenanceCompletedAt = Date.now();
478
- const entries = inventory.map((candidate) => entry(candidate, rows.get(`${candidate.domain}\0${candidate.entityId}`), taskFullProvenance));
493
+ const entries = inventory.map((candidate) => entry(candidate, rows.get(`${candidate.domain}\0${candidate.entityId}`), taskFullProvenance, input.taskCoverCoverage === true));
479
494
  const entriesCompletedAt = Date.now();
480
495
  const coverage = input.workflowCoverage
481
496
  ? 'workflow'
482
- : input.planningOrderCoverage
483
- ? 'planning-order'
484
- : input.nestedCoverage
485
- ? 'nested'
486
- : 'combined';
497
+ : input.taskCoverCoverage
498
+ ? 'task-cover'
499
+ : input.planningOrderCoverage
500
+ ? 'planning-order'
501
+ : input.nestedCoverage
502
+ ? 'nested'
503
+ : 'combined';
487
504
  entries.forEach(coverage === 'workflow'
488
505
  ? validateWorkspaceSyncV3WorkflowCoverageFeedEntry
489
- : coverage === 'planning-order'
490
- ? validateWorkspaceSyncV3PlanningOrderCoverageFeedEntry
491
- : coverage === 'nested'
492
- ? validateWorkspaceSyncV3NestedCoverageFeedEntry
493
- : validateWorkspaceSyncV3CombinedFeedEntry);
506
+ : coverage === 'task-cover'
507
+ ? validateWorkspaceSyncV3TaskCoverCoverageFeedEntry
508
+ : coverage === 'planning-order'
509
+ ? validateWorkspaceSyncV3PlanningOrderCoverageFeedEntry
510
+ : coverage === 'nested'
511
+ ? validateWorkspaceSyncV3NestedCoverageFeedEntry
512
+ : validateWorkspaceSyncV3CombinedFeedEntry);
494
513
  const validationCompletedAt = Date.now();
495
514
  if (entries.some((candidate) => BigInt(candidate.sequence) > BigInt(snapshotSequence))) {
496
515
  throw new Error('Combined repair entry exceeds the locked snapshot sequence.');
@@ -564,3 +583,21 @@ export function buildWorkspaceSyncV3PlanningOrderRepairSnapshot(input) {
564
583
  });
565
584
  })();
566
585
  }
586
+ export function buildWorkspaceSyncV3TaskCoverRepairSnapshot(input) {
587
+ const workspaceId = String(input.workspaceId || '').trim();
588
+ if (!workspaceId)
589
+ throw new Error('Workspace id is required.');
590
+ const tenantId = input.core.getTenantId();
591
+ const db = input.core.getDatabaseAdapter();
592
+ return db.transaction(() => {
593
+ const snapshotSequence = lockAndReadHighWatermark(db, tenantId, workspaceId);
594
+ const inventory = listWorkspaceSyncV3TaskCoverRepairCandidates(input.core, workspaceId);
595
+ return buildWorkspaceSyncV3CombinedRepairSnapshotFromLockedCandidates({
596
+ core: input.core,
597
+ workspaceId,
598
+ snapshotSequence,
599
+ inventory,
600
+ taskCoverCoverage: true,
601
+ });
602
+ })();
603
+ }
@@ -2,6 +2,7 @@ import type { WorkspaceSyncV2ProjectionExclusion } from './workspaceSyncV2Projec
2
2
  import { WORKSPACE_SYNC_V3_NESTED_COVERAGE_SELECTOR } from './workspaceSyncV3NestedWireContract.js';
3
3
  import { WORKSPACE_SYNC_V3_WORKFLOW_COVERAGE_SELECTOR } from './workspaceSyncV3WorkflowWireContract.js';
4
4
  import { WORKSPACE_SYNC_V3_PLANNING_ORDER_COVERAGE_SELECTOR } from './workspaceSyncV3PlanningOrderWireContract.js';
5
+ import { WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_SELECTOR } from './workspaceSyncV3TaskCoverWireContract.js';
5
6
  export declare const WORKSPACE_SYNC_V3_COMBINED_COVERAGE_SELECTOR: Readonly<{
6
7
  coverageProfileId: "task-planning-metadata";
7
8
  coverageVersion: 3;
@@ -9,13 +10,14 @@ export declare const WORKSPACE_SYNC_V3_COMBINED_COVERAGE_SELECTOR: Readonly<{
9
10
  }>;
10
11
  export declare const WORKSPACE_SYNC_V3_COMBINED_CAPABILITY_VERSION: 1;
11
12
  export declare const WORKSPACE_SYNC_V3_COMBINED_EXCLUDED_V2_PROJECTIONS: readonly ["task/root-v1", "task-relationship/full-v1", "taxonomy/full-v1", "initiative/full-v1", "workstream/full-v1"];
12
- export type WorkspaceSyncV3CombinedCoverageSelector = typeof WORKSPACE_SYNC_V3_COMBINED_COVERAGE_SELECTOR | typeof WORKSPACE_SYNC_V3_NESTED_COVERAGE_SELECTOR | typeof WORKSPACE_SYNC_V3_WORKFLOW_COVERAGE_SELECTOR | typeof WORKSPACE_SYNC_V3_PLANNING_ORDER_COVERAGE_SELECTOR;
13
+ export type WorkspaceSyncV3CombinedCoverageSelector = typeof WORKSPACE_SYNC_V3_COMBINED_COVERAGE_SELECTOR | typeof WORKSPACE_SYNC_V3_NESTED_COVERAGE_SELECTOR | typeof WORKSPACE_SYNC_V3_WORKFLOW_COVERAGE_SELECTOR | typeof WORKSPACE_SYNC_V3_PLANNING_ORDER_COVERAGE_SELECTOR | typeof WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_SELECTOR;
13
14
  export interface WorkspaceSyncV3CombinedWireContract {
14
15
  coverage: WorkspaceSyncV3CombinedCoverageSelector;
15
16
  excludedProjections: readonly WorkspaceSyncV2ProjectionExclusion[];
16
17
  nestedWriterPrerequisitesRequired: boolean;
17
18
  workflowWriterPrerequisitesRequired: boolean;
18
19
  planningOrderWriterPrerequisitesRequired: boolean;
20
+ taskCoverProjectionRequired: boolean;
19
21
  }
20
22
  export declare function resolveWorkspaceSyncV3CombinedWireContract(input: {
21
23
  coverageProfileId: unknown;
@@ -1,6 +1,7 @@
1
1
  import { WORKSPACE_SYNC_V3_NESTED_COVERAGE_SELECTOR, WORKSPACE_SYNC_V3_NESTED_EXCLUDED_V2_PROJECTIONS, } from './workspaceSyncV3NestedWireContract.js';
2
2
  import { WORKSPACE_SYNC_V3_WORKFLOW_COVERAGE_SELECTOR, WORKSPACE_SYNC_V3_WORKFLOW_EXCLUDED_V2_PROJECTIONS, } from './workspaceSyncV3WorkflowWireContract.js';
3
3
  import { WORKSPACE_SYNC_V3_PLANNING_ORDER_COVERAGE_SELECTOR, WORKSPACE_SYNC_V3_PLANNING_ORDER_EXCLUDED_V2_PROJECTIONS, } from './workspaceSyncV3PlanningOrderWireContract.js';
4
+ import { WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_SELECTOR, WORKSPACE_SYNC_V3_TASK_COVER_EXCLUDED_V2_PROJECTIONS, } from './workspaceSyncV3TaskCoverWireContract.js';
4
5
  export const WORKSPACE_SYNC_V3_COMBINED_COVERAGE_SELECTOR = Object.freeze({
5
6
  coverageProfileId: 'task-planning-metadata',
6
7
  coverageVersion: 3,
@@ -27,6 +28,7 @@ export function resolveWorkspaceSyncV3CombinedWireContract(input) {
27
28
  nestedWriterPrerequisitesRequired: false,
28
29
  workflowWriterPrerequisitesRequired: false,
29
30
  planningOrderWriterPrerequisitesRequired: false,
31
+ taskCoverProjectionRequired: false,
30
32
  };
31
33
  }
32
34
  if (profileId === WORKSPACE_SYNC_V3_NESTED_COVERAGE_SELECTOR.coverageProfileId
@@ -38,6 +40,7 @@ export function resolveWorkspaceSyncV3CombinedWireContract(input) {
38
40
  nestedWriterPrerequisitesRequired: true,
39
41
  workflowWriterPrerequisitesRequired: false,
40
42
  planningOrderWriterPrerequisitesRequired: false,
43
+ taskCoverProjectionRequired: false,
41
44
  };
42
45
  }
43
46
  if (profileId === WORKSPACE_SYNC_V3_WORKFLOW_COVERAGE_SELECTOR.coverageProfileId
@@ -49,6 +52,7 @@ export function resolveWorkspaceSyncV3CombinedWireContract(input) {
49
52
  nestedWriterPrerequisitesRequired: true,
50
53
  workflowWriterPrerequisitesRequired: true,
51
54
  planningOrderWriterPrerequisitesRequired: false,
55
+ taskCoverProjectionRequired: false,
52
56
  };
53
57
  }
54
58
  if (profileId === WORKSPACE_SYNC_V3_PLANNING_ORDER_COVERAGE_SELECTOR.coverageProfileId
@@ -60,6 +64,19 @@ export function resolveWorkspaceSyncV3CombinedWireContract(input) {
60
64
  nestedWriterPrerequisitesRequired: true,
61
65
  workflowWriterPrerequisitesRequired: false,
62
66
  planningOrderWriterPrerequisitesRequired: true,
67
+ taskCoverProjectionRequired: false,
68
+ };
69
+ }
70
+ if (profileId === WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_SELECTOR.coverageProfileId
71
+ && version === WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_SELECTOR.coverageVersion
72
+ && digest === WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_SELECTOR.coverageDigest) {
73
+ return {
74
+ coverage: WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_SELECTOR,
75
+ excludedProjections: WORKSPACE_SYNC_V3_TASK_COVER_EXCLUDED_V2_PROJECTIONS,
76
+ nestedWriterPrerequisitesRequired: true,
77
+ workflowWriterPrerequisitesRequired: false,
78
+ planningOrderWriterPrerequisitesRequired: false,
79
+ taskCoverProjectionRequired: true,
63
80
  };
64
81
  }
65
82
  return null;
@@ -70,6 +87,7 @@ export function resolveWorkspaceSyncV3CombinedCoverageForExclusions(exclusions)
70
87
  WORKSPACE_SYNC_V3_NESTED_COVERAGE_SELECTOR,
71
88
  WORKSPACE_SYNC_V3_WORKFLOW_COVERAGE_SELECTOR,
72
89
  WORKSPACE_SYNC_V3_PLANNING_ORDER_COVERAGE_SELECTOR,
90
+ WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_SELECTOR,
73
91
  ]) {
74
92
  const contract = resolveWorkspaceSyncV3CombinedWireContract(coverage);
75
93
  if (exclusions.length === contract.excludedProjections.length
@@ -2,6 +2,7 @@ import { WORKSPACE_SYNC_V3_COMBINED_COVERAGE_POLICY, WORKSPACE_SYNC_V3_COMBINED_
2
2
  import { WORKSPACE_SYNC_V3_NESTED_COVERAGE_POLICY, WORKSPACE_SYNC_V3_NESTED_COVERAGE_PROFILE, validateWorkspaceSyncV3NestedCoverageFeedEntry, } from './workspaceSyncV3NestedCoverage.js';
3
3
  import { WORKSPACE_SYNC_V3_WORKFLOW_COVERAGE_POLICY, WORKSPACE_SYNC_V3_WORKFLOW_COVERAGE_PROFILE, validateWorkspaceSyncV3WorkflowCoverageFeedEntry, } from './workspaceSyncV3WorkflowCoverage.js';
4
4
  import { WORKSPACE_SYNC_V3_PLANNING_ORDER_COVERAGE_POLICY, WORKSPACE_SYNC_V3_PLANNING_ORDER_COVERAGE_PROFILE, validateWorkspaceSyncV3PlanningOrderCoverageFeedEntry, } from './workspaceSyncV3PlanningOrderCoverage.js';
5
+ import { WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_POLICY, WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_PROFILE, validateWorkspaceSyncV3TaskCoverCoverageFeedEntry, } from './workspaceSyncV3TaskCoverCoverage.js';
5
6
  export const WORKSPACE_SYNC_V3_REGISTERED_COVERAGES = Object.freeze([
6
7
  Object.freeze({
7
8
  profile: WORKSPACE_SYNC_V3_COMBINED_COVERAGE_PROFILE,
@@ -23,6 +24,11 @@ export const WORKSPACE_SYNC_V3_REGISTERED_COVERAGES = Object.freeze([
23
24
  policy: WORKSPACE_SYNC_V3_PLANNING_ORDER_COVERAGE_POLICY,
24
25
  validateEntry: validateWorkspaceSyncV3PlanningOrderCoverageFeedEntry,
25
26
  }),
27
+ Object.freeze({
28
+ profile: WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_PROFILE,
29
+ policy: WORKSPACE_SYNC_V3_TASK_COVER_COVERAGE_POLICY,
30
+ validateEntry: validateWorkspaceSyncV3TaskCoverCoverageFeedEntry,
31
+ }),
26
32
  ]);
27
33
  export function resolveWorkspaceSyncV3RegisteredCoverage(input) {
28
34
  const profileId = String(input.coverageProfileId || '').trim();
@@ -1,4 +1,4 @@
1
- export type WorkspaceSyncV3ProjectionDescriptor = 'initiative/full-v1' | 'workstream/full-v1' | 'task/root-v1' | 'task-comments/full-v1' | 'task-checklist/full-v1' | 'task-attachment-links/full-v1' | 'entity-event/full-v1' | 'task-relationship/full-v1' | 'taxonomy/full-v1' | 'workflow-published-definition/full-v2' | 'workflow-execution/root-v1' | 'workflow-execution-event/full-v1' | 'workstream-task-order/full-v1';
1
+ export type WorkspaceSyncV3ProjectionDescriptor = 'initiative/full-v1' | 'workstream/full-v1' | 'task/root-v1' | 'task-comments/full-v1' | 'task-checklist/full-v1' | 'task-attachment-links/full-v1' | 'task-attachment-links/full-v2' | 'entity-event/full-v1' | 'task-relationship/full-v1' | 'taxonomy/full-v1' | 'workflow-published-definition/full-v2' | 'workflow-execution/root-v1' | 'workflow-execution-event/full-v1' | 'workstream-task-order/full-v1';
2
2
  export type WorkspaceSyncV3JournalProjectionDescriptor = WorkspaceSyncV3ProjectionDescriptor | 'task/taxonomy-remap-v1';
3
3
  export interface WorkspaceSyncV3ProjectionCoverageDescriptor {
4
4
  projectionId: WorkspaceSyncV3ProjectionDescriptor;
@@ -7,4 +7,5 @@ export declare function resolveWorkspaceSyncV3AttestedV2Exclusions(input: {
7
7
  coverage: unknown;
8
8
  excludedProjections: unknown;
9
9
  env?: NodeJS.ProcessEnv;
10
+ runtimeMode?: 'local' | 'cloud';
10
11
  }): readonly WorkspaceSyncV2ProjectionExclusion[] | null;
@@ -1,5 +1,5 @@
1
- import { isWorkspaceSyncV3CombinedLegacyFenceEnabled, normalizeWorkspaceSyncV2ProjectionExclusions, WORKSPACE_SYNC_V3_COMBINED_V2_EXCLUSIONS, } from './workspaceSyncV2ProjectionExclusions.js';
2
- import { resolveWorkspaceSyncV3CombinedWireContract, } from './workspaceSyncV3CombinedWireContract.js';
1
+ import { isWorkspaceSyncV3CombinedLegacyFenceConfigured, isWorkspaceSyncV3CombinedLegacyFenceEnabled, normalizeWorkspaceSyncV2ProjectionExclusions, WORKSPACE_SYNC_V3_COMBINED_V2_EXCLUSIONS, } from './workspaceSyncV2ProjectionExclusions.js';
2
+ import { WORKSPACE_SYNC_V3_COMBINED_COVERAGE_SELECTOR, resolveWorkspaceSyncV3CombinedWireContract, } from './workspaceSyncV3CombinedWireContract.js';
3
3
  import { resolveCombinedFeedCapability, resolveRequestedCombinedFeedCoverage } from './combinedFeedCapability.js';
4
4
  export function isWorkspaceSyncV3NestedLegacyFenceEnabled(env = process.env) {
5
5
  return /^(1|true|on|yes)$/i.test(String(env.TASKFORCE_SYNC_V3_NESTED_LEGACY_FENCE_ENABLED || '').trim());
@@ -17,17 +17,30 @@ export function resolveWorkspaceSyncV3RequiredCloudCoverage(env = process.env) {
17
17
  export const resolveWorkspaceSyncV3RequiredPushCoverage = resolveWorkspaceSyncV3RequiredCloudCoverage;
18
18
  export function resolveWorkspaceSyncV3AttestedV2Exclusions(input) {
19
19
  const env = input.env || process.env;
20
- if (!isWorkspaceSyncV3CombinedLegacyFenceEnabled(env))
21
- return input.coverage == null ? [] : null;
22
- if (input.coverage == null)
23
- return WORKSPACE_SYNC_V3_COMBINED_V2_EXCLUSIONS;
20
+ const runtimeMode = input.runtimeMode || 'cloud';
21
+ const localFenceEnabled = (coverage) => (isWorkspaceSyncV3CombinedLegacyFenceConfigured(env)
22
+ && resolveCombinedFeedCapability({ runtimeMode: 'local', env, coverage })
23
+ .combinedFeed.activationReady);
24
+ if (input.coverage == null) {
25
+ const fenceEnabled = runtimeMode === 'local'
26
+ ? localFenceEnabled(WORKSPACE_SYNC_V3_COMBINED_COVERAGE_SELECTOR)
27
+ : isWorkspaceSyncV3CombinedLegacyFenceEnabled(env);
28
+ return fenceEnabled ? WORKSPACE_SYNC_V3_COMBINED_V2_EXCLUSIONS : [];
29
+ }
24
30
  if (!input.coverage || typeof input.coverage !== 'object' || Array.isArray(input.coverage))
25
31
  return null;
26
32
  const contract = resolveWorkspaceSyncV3CombinedWireContract(input.coverage);
27
- if (!contract || (contract.nestedWriterPrerequisitesRequired
28
- && (!isWorkspaceSyncV3NestedLegacyFenceEnabled(env)
29
- || !resolveCombinedFeedCapability({ runtimeMode: 'cloud', env, coverage: contract.coverage })
30
- .combinedFeed.activationReady)))
33
+ if (!contract)
34
+ return null;
35
+ const fenceEnabled = runtimeMode === 'local'
36
+ ? localFenceEnabled(contract.coverage)
37
+ : isWorkspaceSyncV3CombinedLegacyFenceEnabled(env)
38
+ && (!contract.nestedWriterPrerequisitesRequired
39
+ || (isWorkspaceSyncV3NestedLegacyFenceEnabled(env)
40
+ && resolveCombinedFeedCapability({
41
+ runtimeMode: 'cloud', env, coverage: contract.coverage,
42
+ }).combinedFeed.activationReady));
43
+ if (!fenceEnabled)
31
44
  return null;
32
45
  if (!Array.isArray(input.excludedProjections))
33
46
  return null;
@@ -23,6 +23,7 @@ export interface WorkspaceSyncV3NestedTaskApplyCore {
23
23
  resolveTaskAttachmentLinksForDurableSync(links: ReturnType<typeof serializeWorkspaceSyncV3TaskAttachmentLinksPayload>['links'], workspaceId: string, options?: {
24
24
  adoptAuthoritativeReferenceNumber?: boolean;
25
25
  ignoreAssetMetadataSnapshots?: boolean;
26
+ restoreDeletedExternalAssetForAuthoritativeSync?: boolean;
26
27
  }): AttachmentItem[];
27
28
  listTaskAttachmentLinksForDurableSync(taskId: string, workspaceId: string): ReturnType<typeof serializeWorkspaceSyncV3TaskAttachmentLinksPayload>['links'];
28
29
  getEntityEventForSync(input: {
@@ -71,12 +72,14 @@ export declare function resolveWorkspaceSyncV3AttachmentApplyObligations(input:
71
72
  } | {
72
73
  success: false;
73
74
  error: string;
75
+ disposition?: 'retryable' | 'terminal-unavailable';
74
76
  }>;
75
77
  onInstalled?: (taskId: string) => void;
76
78
  }): Promise<{
77
79
  attempted: number;
78
80
  resolved: number;
79
81
  remaining: number;
82
+ terminal: number;
80
83
  nextAttemptAt: string | null;
81
84
  resolvedTaskIds: string[];
82
85
  lastError: string | null;
@@ -2,7 +2,7 @@ import { DurableTaskAttachmentLinksApplyError } from '../../core/TaskAttachmentL
2
2
  import { canonicalWorkspaceSyncJson } from './canonicalWorkspaceSyncJson.js';
3
3
  import { fingerprintWorkspaceSyncPayload, hasCompletedRecoveryForSupersededEntityEvent, WorkspaceSyncDurabilityStore, } from './syncDurabilityStore.js';
4
4
  import { calculateAttachmentApplyObligationRetryDelayMs, WorkspaceSyncV3AttachmentApplyObligationStore, } from './workspaceSyncV3AttachmentApplyObligationStore.js';
5
- import { serializeWorkspaceSyncV3TaskAttachmentLinksPayload, serializeWorkspaceSyncV3TaskChecklistPayload, serializeWorkspaceSyncV3TaskCommentsPayload, serializeWorkspaceSyncV3EntityEventPayload, validateWorkspaceSyncV3EntityEventPayload, validateWorkspaceSyncV3TaskAttachmentLinksPayload, validateWorkspaceSyncV3TaskChecklistPayload, validateWorkspaceSyncV3TaskCommentAppendPayload, validateWorkspaceSyncV3TaskCommentsPayload, } from './workspaceSyncV3NestedTaskCodecs.js';
5
+ import { serializeWorkspaceSyncV3TaskAttachmentLinksPayload, serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload, serializeWorkspaceSyncV3TaskChecklistPayload, serializeWorkspaceSyncV3TaskCommentsPayload, serializeWorkspaceSyncV3EntityEventPayload, validateWorkspaceSyncV3EntityEventPayload, validateWorkspaceSyncV3TaskAttachmentLinksPayload, validateWorkspaceSyncV3TaskAttachmentLinksV2Payload, validateWorkspaceSyncV3TaskChecklistPayload, validateWorkspaceSyncV3TaskCommentAppendPayload, validateWorkspaceSyncV3TaskCommentsPayload, } from './workspaceSyncV3NestedTaskCodecs.js';
6
6
  import { validateWorkspaceSyncV3NestedTaskFeedEntry } from './workspaceSyncV3NestedTaskProjection.js';
7
7
  export class WorkspaceSyncV3TaskCommentAggregateDivergenceError extends Error {
8
8
  taskId;
@@ -50,6 +50,7 @@ function serializeTaskAttachmentLinkOwnership(payload) {
50
50
  order: link.order,
51
51
  ...(link.displayName !== undefined ? { displayName: link.displayName } : {}),
52
52
  })),
53
+ ...('cardCoverAssetId' in payload ? { cardCoverAssetId: payload.cardCoverAssetId } : {}),
53
54
  };
54
55
  }
55
56
  function applyEntry(core, workspaceId, entry, now, authoritativeEntityEventRecoveryIds) {
@@ -85,16 +86,22 @@ function applyEntry(core, workspaceId, entry, now, authoritativeEntityEventRecov
85
86
  assertInstalledFingerprint(serializeWorkspaceSyncV3TaskChecklistPayload({ taskId: task.id, items: installed.checklistItems || [] }), entry.canonicalFingerprint || '', 'Task checklist');
86
87
  return;
87
88
  }
88
- if (entry.projection === 'task-attachment-links/full-v1') {
89
- const payload = validateWorkspaceSyncV3TaskAttachmentLinksPayload(entry.payload);
89
+ if (entry.projection === 'task-attachment-links/full-v1'
90
+ || entry.projection === 'task-attachment-links/full-v2') {
91
+ const v2Payload = entry.projection === 'task-attachment-links/full-v2'
92
+ ? validateWorkspaceSyncV3TaskAttachmentLinksV2Payload(entry.payload)
93
+ : null;
94
+ const payload = v2Payload ?? validateWorkspaceSyncV3TaskAttachmentLinksPayload(entry.payload);
95
+ const cardCoverAssetId = v2Payload?.cardCoverAssetId;
90
96
  const task = taskOrThrow(core, payload.taskId, workspaceId);
91
97
  const ownershipFingerprint = fingerprintWorkspaceSyncPayload(serializeTaskAttachmentLinkOwnership(payload));
92
98
  const obligations = new WorkspaceSyncV3AttachmentApplyObligationStore(core.getDatabaseAdapter());
93
99
  let attachments;
94
100
  try {
95
- attachments = core.resolveTaskAttachmentLinksForDurableSync(payload.links, workspaceId, {
101
+ attachments = core.getDatabaseAdapter().transaction(() => (core.resolveTaskAttachmentLinksForDurableSync(payload.links, workspaceId, {
96
102
  ignoreAssetMetadataSnapshots: true,
97
- });
103
+ restoreDeletedExternalAssetForAuthoritativeSync: true,
104
+ })))();
98
105
  }
99
106
  catch (error) {
100
107
  if (error instanceof DurableTaskAttachmentLinksApplyError
@@ -122,10 +129,19 @@ function applyEntry(core, workspaceId, entry, now, authoritativeEntityEventRecov
122
129
  }
123
130
  throw error;
124
131
  }
125
- installTaskNestedFields(core, workspaceId, task, { attachments });
126
- const installedPayload = serializeWorkspaceSyncV3TaskAttachmentLinksPayload({
127
- taskId: task.id, links: core.listTaskAttachmentLinksForDurableSync(task.id, workspaceId),
132
+ const installed = installTaskNestedFields(core, workspaceId, task, {
133
+ attachments,
134
+ ...(cardCoverAssetId !== undefined ? { cardCoverAssetId } : {}),
128
135
  });
136
+ const installedPayload = cardCoverAssetId !== undefined
137
+ ? serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload({
138
+ taskId: task.id,
139
+ links: core.listTaskAttachmentLinksForDurableSync(task.id, workspaceId),
140
+ cardCoverAssetId: installed.cardCoverAssetId ?? null,
141
+ })
142
+ : serializeWorkspaceSyncV3TaskAttachmentLinksPayload({
143
+ taskId: task.id, links: core.listTaskAttachmentLinksForDurableSync(task.id, workspaceId),
144
+ });
129
145
  // Captions, original filenames, and readable references are snapshots of
130
146
  // asset-owned metadata. A historical link event must remain installable
131
147
  // after the asset is renamed or receives a new readable reference.
@@ -210,7 +226,14 @@ export async function resolveWorkspaceSyncV3AttachmentApplyObligations(input) {
210
226
  let lastError = null;
211
227
  for (const obligation of due) {
212
228
  try {
213
- const payload = validateWorkspaceSyncV3TaskAttachmentLinksPayload(JSON.parse(obligation.payloadJson));
229
+ const rawPayload = JSON.parse(obligation.payloadJson);
230
+ const hasCardCover = Object.prototype.hasOwnProperty.call(rawPayload, 'cardCoverAssetId');
231
+ const payload = hasCardCover
232
+ ? validateWorkspaceSyncV3TaskAttachmentLinksV2Payload(rawPayload)
233
+ : validateWorkspaceSyncV3TaskAttachmentLinksPayload(rawPayload);
234
+ const cardCoverAssetId = hasCardCover
235
+ ? payload.cardCoverAssetId
236
+ : undefined;
214
237
  const installMutation = () => {
215
238
  const task = input.core.getTask(payload.taskId, input.workspaceId);
216
239
  if (!task) {
@@ -222,11 +245,22 @@ export async function resolveWorkspaceSyncV3AttachmentApplyObligations(input) {
222
245
  }
223
246
  const attachments = input.core.resolveTaskAttachmentLinksForDurableSync(payload.links, input.workspaceId, {
224
247
  ignoreAssetMetadataSnapshots: true,
248
+ restoreDeletedExternalAssetForAuthoritativeSync: true,
225
249
  });
226
- installTaskNestedFields(input.core, input.workspaceId, task, { attachments });
227
- const installedPayload = serializeWorkspaceSyncV3TaskAttachmentLinksPayload({
228
- taskId: task.id, links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.workspaceId),
250
+ const installed = installTaskNestedFields(input.core, input.workspaceId, task, {
251
+ attachments,
252
+ ...(cardCoverAssetId !== undefined ? { cardCoverAssetId } : {}),
229
253
  });
254
+ const installedPayload = cardCoverAssetId !== undefined
255
+ ? serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload({
256
+ taskId: task.id,
257
+ links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.workspaceId),
258
+ cardCoverAssetId: installed.cardCoverAssetId ?? null,
259
+ })
260
+ : serializeWorkspaceSyncV3TaskAttachmentLinksPayload({
261
+ taskId: task.id,
262
+ links: input.core.listTaskAttachmentLinksForDurableSync(task.id, input.workspaceId),
263
+ });
230
264
  assertInstalledFingerprint(serializeTaskAttachmentLinkOwnership(installedPayload), fingerprintWorkspaceSyncPayload(serializeTaskAttachmentLinkOwnership(payload)), `Task attachment links for ${task.id}`);
231
265
  obligationStore.resolve({
232
266
  tenantId, workspaceId: input.workspaceId, taskId: obligation.taskId,
@@ -292,8 +326,20 @@ export async function resolveWorkspaceSyncV3AttachmentApplyObligations(input) {
292
326
  if (!input.hydrateAsset || !assetId || hydratedAssetIds.has(assetId))
293
327
  throw error;
294
328
  const hydration = await input.hydrateAsset(assetId);
295
- if (!hydration.success)
329
+ if (!hydration.success) {
330
+ if (hydration.disposition === 'terminal-unavailable') {
331
+ obligationStore.markTerminalUnavailable({
332
+ tenantId,
333
+ workspaceId: input.workspaceId,
334
+ taskId: obligation.taskId,
335
+ canonicalFingerprint: obligation.canonicalFingerprint,
336
+ reason: hydration.error,
337
+ now: input.now,
338
+ });
339
+ throw new TerminalAttachmentApplyObligationError(hydration.error);
340
+ }
296
341
  throw new Error(hydration.error);
342
+ }
297
343
  hydratedAssetIds.add(assetId);
298
344
  }
299
345
  }
@@ -304,6 +350,8 @@ export async function resolveWorkspaceSyncV3AttachmentApplyObligations(input) {
304
350
  }
305
351
  catch (error) {
306
352
  lastError = error instanceof Error ? error.message : 'Deferred attachment-link retry failed.';
353
+ if (error instanceof TerminalAttachmentApplyObligationError)
354
+ continue;
307
355
  const nextAttemptAt = new Date(Date.parse(input.now) + calculateAttachmentApplyObligationRetryDelayMs(obligation.attempts + 1)).toISOString();
308
356
  obligationStore.recordAttemptFailure({
309
357
  tenantId, workspaceId: input.workspaceId, taskId: obligation.taskId,
@@ -318,8 +366,11 @@ export async function resolveWorkspaceSyncV3AttachmentApplyObligations(input) {
318
366
  attempted: due.length,
319
367
  resolved,
320
368
  remaining: summary.open,
369
+ terminal: summary.terminal,
321
370
  nextAttemptAt: summary.nextAttemptAt,
322
371
  resolvedTaskIds,
323
372
  lastError: summary.lastError || lastError,
324
373
  };
325
374
  }
375
+ class TerminalAttachmentApplyObligationError extends Error {
376
+ }
@@ -18,6 +18,9 @@ export type WorkspaceSyncV3TaskAttachmentLinksPayload = {
18
18
  taskId: string;
19
19
  links: WorkspaceSyncV3TaskAttachmentLink[];
20
20
  };
21
+ export type WorkspaceSyncV3TaskAttachmentLinksV2Payload = WorkspaceSyncV3TaskAttachmentLinksPayload & {
22
+ cardCoverAssetId: string | null;
23
+ };
21
24
  export type WorkspaceSyncV3TaskAttachmentLink = {
22
25
  assetId: string;
23
26
  role: 'attachment' | 'image' | 'reference';
@@ -55,5 +58,11 @@ export declare function serializeWorkspaceSyncV3TaskAttachmentLinksPayload(input
55
58
  links: unknown;
56
59
  }): WorkspaceSyncV3TaskAttachmentLinksPayload;
57
60
  export declare function validateWorkspaceSyncV3TaskAttachmentLinksPayload(value: unknown): WorkspaceSyncV3TaskAttachmentLinksPayload;
61
+ export declare function serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload(input: {
62
+ taskId: string;
63
+ links: unknown;
64
+ cardCoverAssetId: unknown;
65
+ }): WorkspaceSyncV3TaskAttachmentLinksV2Payload;
66
+ export declare function validateWorkspaceSyncV3TaskAttachmentLinksV2Payload(value: unknown): WorkspaceSyncV3TaskAttachmentLinksV2Payload;
58
67
  export declare function serializeWorkspaceSyncV3EntityEventPayload(event: EntityEvent): WorkspaceSyncEntityEventSnapshot;
59
68
  export declare function validateWorkspaceSyncV3EntityEventPayload(value: unknown): WorkspaceSyncEntityEventSnapshot;
@@ -279,6 +279,32 @@ export function validateWorkspaceSyncV3TaskAttachmentLinksPayload(value) {
279
279
  }
280
280
  return canonical;
281
281
  }
282
+ export function serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload(input) {
283
+ const canonical = serializeWorkspaceSyncV3TaskAttachmentLinksPayload(input);
284
+ const cardCoverAssetId = input.cardCoverAssetId == null
285
+ ? null
286
+ : requiredId(input.cardCoverAssetId, 'Task attachment links cardCoverAssetId');
287
+ if (cardCoverAssetId !== null && !canonical.links.some((link) => (link.assetId === cardCoverAssetId && link.role === 'image'))) {
288
+ throw new Error('Task attachment links cardCoverAssetId must reference an image link.');
289
+ }
290
+ return { ...canonical, cardCoverAssetId };
291
+ }
292
+ export function validateWorkspaceSyncV3TaskAttachmentLinksV2Payload(value) {
293
+ const source = record(value, 'Task attachment links v2 payload');
294
+ exactKeys(source, new Set(['taskId', 'links', 'cardCoverAssetId']), 'Task attachment links v2 payload');
295
+ if (!Object.prototype.hasOwnProperty.call(source, 'cardCoverAssetId')) {
296
+ throw new Error('Task attachment links v2 payload must explicitly carry cardCoverAssetId.');
297
+ }
298
+ const canonical = serializeWorkspaceSyncV3TaskAttachmentLinksV2Payload({
299
+ taskId: requiredId(source.taskId, 'Task attachment links taskId'),
300
+ links: source.links,
301
+ cardCoverAssetId: source.cardCoverAssetId,
302
+ });
303
+ if (canonicalWorkspaceSyncJson(canonical) !== canonicalWorkspaceSyncJson(value)) {
304
+ throw new Error('Task attachment links v2 payload is not canonically ordered or normalized.');
305
+ }
306
+ return canonical;
307
+ }
282
308
  export function serializeWorkspaceSyncV3EntityEventPayload(event) {
283
309
  const serialized = serializeEntityActivityForSync(event);
284
310
  assertMetadataOnly(serialized.details, 'Entity event details');
@@ -33,15 +33,25 @@ export declare const WORKSPACE_SYNC_V3_NESTED_TASK_PROJECTIONS: readonly [Readon
33
33
  dependencies: readonly ["initiative/full-v1", "workstream/full-v1", "task/root-v1"];
34
34
  operationAtomic: true;
35
35
  }>];
36
+ export declare const WORKSPACE_SYNC_V3_TASK_ATTACHMENT_LINKS_V2_PROJECTION: Readonly<{
37
+ readonly projectionId: "task-attachment-links/full-v2";
38
+ readonly domain: "task-attachment-links";
39
+ readonly schemaVersion: 2;
40
+ readonly mutationKinds: readonly string[];
41
+ readonly fields: readonly string[];
42
+ readonly dependencies: readonly ["task/root-v1"];
43
+ readonly operationAtomic: true;
44
+ }>;
36
45
  export declare function projectWorkspaceSyncV3NestedTaskJournalRow(row: WorkspaceSyncJournalRow): WorkspaceSyncV3FeedEntry | null;
46
+ export declare function projectWorkspaceSyncV3NestedTaskV2JournalRow(row: WorkspaceSyncJournalRow): WorkspaceSyncV3FeedEntry | null;
37
47
  export declare function validateWorkspaceSyncV3NestedTaskFeedEntry(entry: WorkspaceSyncV3FeedEntry): void;
38
- /**
39
- * Operation projector for nested/event coverage. Ordinary writers emit
40
- * parent-first operations; repair baselines and comment reconcile may emit
41
- * nested-only groups that still project when every row is a nested domain.
42
- */
43
48
  export declare function projectWorkspaceSyncV3NestedTaskOperation(input: {
44
49
  rows: readonly WorkspaceSyncJournalRow[];
45
50
  cycleHighWatermark: string;
46
51
  followingRow?: WorkspaceSyncJournalRow | null;
47
52
  }): WorkspaceSyncV3FeedEntry[] | null;
53
+ export declare function projectWorkspaceSyncV3NestedTaskV2Operation(input: {
54
+ rows: readonly WorkspaceSyncJournalRow[];
55
+ cycleHighWatermark: string;
56
+ followingRow?: WorkspaceSyncJournalRow | null;
57
+ }): WorkspaceSyncV3FeedEntry[] | null;