@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.
- package/dist/Taskforce.module.css +257 -87
- package/dist/TaskforceCore.js +237 -230
- package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.js +1 -1
- package/dist/cli.js +34 -0
- package/dist/cliCloudTargetGuard.d.ts +5 -0
- package/dist/cliCloudTargetGuard.js +11 -0
- package/dist/components/activity/EntityActivityTimeline.js +15 -5
- package/dist/components/context/AttachmentPreviewHost.js +1 -1
- package/dist/components/context/ContextAttachmentManager.d.ts +3 -0
- package/dist/components/context/ContextAttachmentManager.js +116 -20
- package/dist/components/features/AnnotatedAttachmentWorkspace.js +24 -8
- package/dist/components/features/TaskSettings.d.ts +12 -1
- package/dist/components/features/TaskSettings.js +147 -40
- package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +1 -1
- package/dist/components/features/planning/PlanningEntitySummary.d.ts +3 -1
- package/dist/components/features/planning/PlanningEntitySummary.js +3 -2
- package/dist/components/features/planning/PlanningLinkPicker.d.ts +3 -1
- package/dist/components/features/planning/PlanningLinkPicker.js +5 -3
- package/dist/components/features/planning/PlanningModule.js +28 -14
- package/dist/components/features/planning/PlanningTaskOrderList.d.ts +1 -1
- package/dist/components/features/planning/PlanningTaskOrderList.js +17 -6
- package/dist/components/features/planning/PlanningVisualIdentity.d.ts +14 -0
- package/dist/components/features/planning/PlanningVisualIdentity.js +106 -0
- package/dist/components/features/planning/planningTaskArrangement.d.ts +1 -0
- package/dist/components/features/planning/planningTaskArrangement.js +5 -5
- package/dist/components/features/planning/planningWorkspaceModel.d.ts +11 -0
- package/dist/components/features/taskforceAgentEditorModel.js +3 -1
- package/dist/components/task/TaskActionHeader.d.ts +2 -1
- package/dist/components/task/TaskActionHeader.js +2 -2
- package/dist/components/task/TaskCard.d.ts +5 -0
- package/dist/components/task/TaskCard.js +101 -13
- package/dist/components/task/TaskContextUpload.d.ts +3 -0
- package/dist/components/task/TaskContextUpload.js +2 -2
- package/dist/components/task/TaskForm.d.ts +4 -0
- package/dist/components/task/TaskForm.js +32 -7
- package/dist/components/task/TaskImageReviews.js +28 -19
- package/dist/components/task/TaskKanban.d.ts +6 -1
- package/dist/components/task/TaskKanban.js +152 -6
- package/dist/components/task/TaskList.d.ts +5 -0
- package/dist/components/task/TaskList.js +3 -3
- package/dist/components/task/TaskStatusActions.js +11 -2
- package/dist/components/task/TaskWorkstreamPicker.js +2 -1
- package/dist/components/ui/DocumentTypeIcon.d.ts +7 -0
- package/dist/components/ui/DocumentTypeIcon.js +8 -0
- package/dist/components/ui/PasswordInput.d.ts +6 -0
- package/dist/components/ui/PasswordInput.js +11 -0
- package/dist/components/ui/TaxonomyDropdown.d.ts +9 -1
- package/dist/components/ui/TaxonomyDropdown.js +30 -3
- package/dist/components/ui/TopNoticeLayer.js +1 -1
- package/dist/components/views/AppShellHeader.js +32 -11
- package/dist/components/views/PlanComparisonPage.js +12 -0
- package/dist/components/views/StandaloneLayout.js +68 -39
- package/dist/components/views/WidgetView.js +10 -7
- package/dist/components/views/panels/PlanningDrawer.d.ts +7 -1
- package/dist/components/views/panels/PlanningDrawer.js +51 -54
- package/dist/components/views/standalone/modals/AccountSettingsModal.js +26 -17
- package/dist/config/envSchema.js +13 -0
- package/dist/config/localServiceCloudTarget.d.ts +10 -0
- package/dist/config/localServiceCloudTarget.js +76 -0
- package/dist/config/localServiceRuntimeContract.d.ts +13 -0
- package/dist/config/localServiceRuntimeContract.js +135 -0
- package/dist/core/AiProfileService.d.ts +1 -0
- package/dist/core/AiProfileService.js +48 -9
- package/dist/core/AttachmentLinkService.js +7 -4
- package/dist/core/DeletedTaskLifecycleService.d.ts +5 -0
- package/dist/core/DeletedTaskLifecycleService.js +55 -3
- package/dist/core/McpTokenService.d.ts +4 -0
- package/dist/core/McpTokenService.js +52 -0
- package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +8 -2
- package/dist/core/TaskAttachmentLinksDurableMutationService.js +51 -18
- package/dist/core/Taskforce.d.ts +124 -2
- package/dist/core/Taskforce.js +1175 -87
- package/dist/core/types.d.ts +8 -1
- package/dist/documentReviews/DocumentReviewCommentStore.d.ts +11 -0
- package/dist/documentReviews/DocumentReviewCommentStore.js +35 -2
- package/dist/documentReviews/service.d.ts +4 -0
- package/dist/documentReviews/service.js +141 -46
- package/dist/hooks/sync/useDurableLocalOutboxDrain.d.ts +1 -1
- package/dist/hooks/sync/useDurableTaskCreateMutation.js +1 -1
- package/dist/hooks/sync/useDurableTaskMetadataMutation.d.ts +1 -0
- package/dist/hooks/sync/useDurableTaskMetadataMutation.js +1 -0
- package/dist/hooks/sync/useDurableTaskStatusMutation.d.ts +1 -0
- package/dist/hooks/tasks/taskHttpMutation.js +1 -0
- package/dist/hooks/tasks/useCloudTaskSearch.d.ts +12 -0
- package/dist/hooks/tasks/useCloudTaskSearch.js +71 -0
- package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.d.ts +16 -0
- package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.js +197 -0
- package/dist/hooks/tasks/useTaskEditorNavigation.d.ts +16 -3
- package/dist/hooks/tasks/useTaskEditorNavigation.js +72 -13
- package/dist/hooks/tasks/useTaskFormActions.d.ts +9 -0
- package/dist/hooks/tasks/useTaskFormActions.js +96 -17
- package/dist/hooks/ui/useSettingsModel.d.ts +24 -2
- package/dist/hooks/ui/useSettingsModel.js +13 -2
- package/dist/hooks/ui/useSettingsPersistence.d.ts +12 -1
- package/dist/hooks/ui/useSettingsPersistence.js +74 -47
- package/dist/hooks/useFilterSort.d.ts +7 -3
- package/dist/hooks/useFilterSort.js +15 -5
- package/dist/hooks/useTaskData.d.ts +10 -1
- package/dist/hooks/useTaskData.js +216 -7
- package/dist/hooks/useTaskMutations.d.ts +10 -2
- package/dist/hooks/useTaskMutations.js +223 -43
- package/dist/hooks/useTaskforce.d.ts +94 -57
- package/dist/hooks/useTaskforce.js +428 -16
- package/dist/hooks/useUiNotice.d.ts +4 -0
- package/dist/hooks/useUiNotice.js +4 -0
- package/dist/mcp/adminWorkspaceRegistrar.js +3 -3
- package/dist/mcp/canonicalAssetHelpers.d.ts +13 -0
- package/dist/mcp/canonicalAssetHelpers.js +39 -5
- package/dist/mcp/collaborationRegistrar.js +15 -13
- package/dist/mcp/documentAssetRegistrar.js +60 -44
- package/dist/mcp/documentHelpers.d.ts +1 -0
- package/dist/mcp/documentHelpers.js +7 -3
- package/dist/mcp/localServiceProxy.js +4 -1
- package/dist/mcp/runtime.d.ts +16 -0
- package/dist/mcp/runtime.js +735 -210
- package/dist/mcp/taskAttachmentCommandAdapter.d.ts +2 -0
- package/dist/mcp/taskAttachmentCommandAdapter.js +14 -8
- package/dist/mcp/taskPlanningRegistrar.js +78 -67
- package/dist/mcp/toolCallCompatibility.js +38 -1
- package/dist/mcp/toolCatalog.d.ts +1 -0
- package/dist/mcp/toolRegistry.js +2 -2
- package/dist/migrations/taskSchemaMigrations.js +47 -0
- package/dist/runtime/appGateDefinitions.d.ts +30 -6
- package/dist/runtime/appGateDefinitions.js +6 -6
- package/dist/runtime/appGates.d.ts +12 -13
- package/dist/runtime/appGates.js +29 -13
- package/dist/runtime/nodeAppGates.d.ts +8 -0
- package/dist/runtime/nodeAppGates.js +27 -0
- package/dist/server/appAccess.d.ts +12 -0
- package/dist/server/appAccess.js +21 -0
- package/dist/server/authorizedWorkspaceAccess.d.ts +12 -0
- package/dist/server/authorizedWorkspaceAccess.js +40 -0
- package/dist/server/cloudCollectionRead.d.ts +25 -0
- package/dist/server/cloudCollectionRead.js +120 -0
- package/dist/server/cloudCollectionReadPool.d.ts +52 -0
- package/dist/server/cloudCollectionReadPool.js +140 -0
- package/dist/server/cloudCollectionReadWorker.d.ts +1 -0
- package/dist/server/cloudCollectionReadWorker.js +36 -0
- package/dist/server/index.d.ts +6 -11
- package/dist/server/index.js +67 -42
- package/dist/server/localServiceLease.d.ts +6 -0
- package/dist/server/localServiceLease.js +17 -1
- package/dist/server/localWorkspaceSyncServerRuntime.js +10 -3
- package/dist/server/rateLimit.d.ts +1 -0
- package/dist/server/rateLimit.js +4 -0
- package/dist/server/realtimeSyncWs.js +36 -12
- package/dist/server/routes/admin.js +1 -0
- package/dist/server/routes/agentRoles.js +3 -2
- package/dist/server/routes/agentSkills.js +3 -2
- package/dist/server/routes/agents.js +118 -16
- package/dist/server/routes/annotatedAttachments.js +3 -2
- package/dist/server/routes/auth.js +3 -1
- package/dist/server/routes/documents.d.ts +5 -2
- package/dist/server/routes/documents.js +160 -13
- package/dist/server/routes/durableTaskHttpRouters.d.ts +11 -2
- package/dist/server/routes/durableTaskHttpRouters.js +2 -2
- package/dist/server/routes/localService.js +4 -0
- package/dist/server/routes/primitives.d.ts +4 -0
- package/dist/server/routes/shared.d.ts +24 -0
- package/dist/server/routes/shared.js +114 -18
- package/dist/server/routes/sync.js +22 -0
- package/dist/server/routes/syncAuxRoutes.js +5 -2
- package/dist/server/routes/syncPullApplyRoutes.js +23 -4
- package/dist/server/routes/syncV3FeedRoutes.js +22 -18
- package/dist/server/routes/syncV3LocalCaptureRoutes.js +48 -4
- package/dist/server/routes/syncV3LocalFeedApplyRoutes.d.ts +1 -0
- package/dist/server/routes/syncV3LocalFeedApplyRoutes.js +26 -0
- package/dist/server/routes/syncV3OperationRoutes.js +27 -2
- package/dist/server/routes/tasks.js +299 -97
- package/dist/server/routes/workspaces.js +43 -1
- package/dist/server/routes.js +35 -7
- package/dist/server/workspaceDeletionAdmission.d.ts +23 -0
- package/dist/server/workspaceDeletionAdmission.js +50 -0
- package/dist/service/localServiceCli.js +52 -25
- package/dist/service/localServiceClientLifecycle.js +93 -6
- package/dist/services/codexAppServerModelGateway.d.ts +112 -0
- package/dist/services/codexAppServerModelGateway.js +438 -0
- package/dist/services/taskforceAgentMcpBridge.d.ts +7 -1
- package/dist/services/taskforceAgentMcpBridge.js +23 -3
- package/dist/shared/planningIdentity.d.ts +20 -0
- package/dist/shared/planningIdentity.js +79 -0
- package/dist/shared/taskforceAgentDefinition.js +7 -1
- package/dist/shared/taskforceAgentModels.d.ts +1 -0
- package/dist/shared/taskforceAgentModels.js +30 -1
- package/dist/storage/postgresAdapter.d.ts +20 -3
- package/dist/storage/postgresAdapter.js +110 -14
- package/dist/storage/postgresRequestDiagnostics.d.ts +8 -0
- package/dist/storage/postgresRequestDiagnostics.js +24 -0
- package/dist/storage/postgresWorker.js +13 -4
- package/dist/storage/postgresWorkerProtocol.d.ts +29 -0
- package/dist/storage/postgresWorkerProtocol.js +30 -0
- package/dist/storage/workspaceAssetStore.d.ts +32 -0
- package/dist/storage/workspaceAssetStore.js +70 -0
- package/dist/sync/cloudSyncApi.d.ts +4 -1
- package/dist/sync/cloudSyncApi.js +7 -2
- package/dist/sync/contentSyncState.d.ts +1 -1
- package/dist/sync/coordinator/localSyncExecutionPermitStore.d.ts +17 -0
- package/dist/sync/coordinator/localSyncExecutionPermitStore.js +77 -23
- package/dist/sync/coordinator/localWorkspaceSyncRunner.js +6 -2
- package/dist/sync/coordinator/workspaceSyncTransportGateway.js +4 -2
- package/dist/sync/engine/workspaceSyncEngineBrowserGatewayRemoteServices.js +20 -2
- package/dist/sync/engine/workspaceSyncEngineLocalServices.d.ts +4 -0
- package/dist/sync/engine/workspaceSyncEngineLocalServices.js +11 -0
- package/dist/sync/engine/workspaceSyncEngineRemoteServices.d.ts +16 -0
- package/dist/sync/engine/workspaceSyncEngineRemoteServices.js +2 -1
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.d.ts +1 -1
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +46 -4
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.d.ts +3 -1
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +51 -11
- package/dist/sync/engine/workspaceSyncEngineTransfers.d.ts +1 -1
- package/dist/sync/engine/workspaceSyncEngineTransfers.js +8 -3
- package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +2 -0
- package/dist/sync/syncApplyHandlers.d.ts +3 -1
- package/dist/sync/syncApplyHandlers.js +29 -0
- package/dist/sync/syncService.d.ts +2 -0
- package/dist/sync/syncService.js +13 -1
- package/dist/sync/taskSyncPayload.d.ts +1 -0
- package/dist/sync/taskSyncPayload.js +6 -0
- package/dist/sync/v3/combinedFeedCapability.js +4 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.d.ts +3 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +31 -3
- package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +2 -0
- package/dist/sync/v3/durableTaskChecklistMutationRouter.js +4 -0
- package/dist/sync/v3/durableTaskHttpBulkMutationRouter.d.ts +1 -0
- package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +47 -12
- package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +5 -3
- package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +27 -2
- package/dist/sync/v3/localInitiativeOutboxService.js +4 -2
- package/dist/sync/v3/localOutboxDispatcher.js +5 -1
- package/dist/sync/v3/localTaskAttachmentLinksOutboxHandler.js +12 -2
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.d.ts +3 -0
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +62 -10
- package/dist/sync/v3/localTaskCreateOutboxService.js +15 -6
- package/dist/sync/v3/localTaskMetadataClient.d.ts +1 -0
- package/dist/sync/v3/localTaskMetadataClient.js +1 -0
- package/dist/sync/v3/localTaskMetadataOutboxHandler.js +5 -1
- package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -0
- package/dist/sync/v3/localTaskMetadataOutboxService.js +7 -0
- package/dist/sync/v3/localTaskRestoreOutboxService.js +14 -6
- package/dist/sync/v3/localTaskStatusClient.d.ts +1 -0
- package/dist/sync/v3/localTaskStatusClient.js +6 -1
- package/dist/sync/v3/localTaskStatusOutboxService.d.ts +2 -0
- package/dist/sync/v3/localTaskStatusOutboxService.js +54 -13
- package/dist/sync/v3/localWorkstreamOutboxService.js +4 -2
- package/dist/sync/v3/syncDurabilityStore.d.ts +15 -0
- package/dist/sync/v3/syncDurabilityStore.js +201 -43
- package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +7 -4
- package/dist/sync/v3/taskAttachmentLinksMutationService.js +37 -12
- package/dist/sync/v3/taskChecklistMutationService.d.ts +4 -2
- package/dist/sync/v3/taskChecklistMutationService.js +4 -0
- package/dist/sync/v3/taskCreateMutationService.d.ts +2 -2
- package/dist/sync/v3/taskCreateMutationService.js +7 -4
- package/dist/sync/v3/taskMetadataCoexistence.js +4 -0
- package/dist/sync/v3/taskMetadataMutationService.d.ts +3 -0
- package/dist/sync/v3/taskMetadataMutationService.js +4 -0
- package/dist/sync/v3/taskRestoreMutationService.d.ts +2 -2
- package/dist/sync/v3/taskRestoreMutationService.js +7 -4
- package/dist/sync/v3/taskStatusMutationService.d.ts +2 -0
- package/dist/sync/v3/taskStatusMutationService.js +4 -0
- package/dist/sync/v3/workflowAssignmentSync.d.ts +1 -0
- package/dist/sync/v3/workflowAssignmentSync.js +3 -0
- package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +14 -12
- package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.d.ts +11 -0
- package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.js +27 -10
- package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.d.ts +2 -1
- package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.js +52 -10
- package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +10 -1
- package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +15 -4
- package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +66 -13
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +11 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +57 -22
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +10 -4
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +124 -51
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +10 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +120 -28
- package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +3 -1
- package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +18 -0
- package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +6 -0
- package/dist/sync/v3/workspaceSyncV3FeedProfile.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.js +23 -10
- package/dist/sync/v3/workspaceSyncV3NestedTaskApply.d.ts +3 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskApply.js +64 -13
- package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.d.ts +9 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.js +26 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +15 -5
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +50 -14
- package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.d.ts +2 -1
- package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.js +11 -3
- package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +3 -0
- package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +19 -7
- package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.js +10 -0
- package/dist/sync/v3/workspaceSyncV3TaskCommentAudit.js +9 -2
- package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.d.ts +7 -0
- package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.js +37 -0
- package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.d.ts +6 -0
- package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.js +9 -0
- package/dist/sync/v3/workspaceSyncV3TaskRootProjection.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV3TaskRootProjection.js +1 -1
- package/dist/sync/workspacePullFeed.d.ts +6 -1
- package/dist/sync/workspacePullFeed.js +174 -162
- package/dist/sync/workspaceRepair.d.ts +3 -0
- package/dist/sync/workspaceRepair.js +67 -6
- package/dist/sync/workspaceSyncModel.d.ts +11 -0
- package/dist/sync/workspaceSyncModel.js +27 -2
- package/dist/types.d.ts +55 -1
- package/dist/types.js +6 -0
- package/dist/ui/assets/{AiIdentityRosterCard-DB1BLO_j.js → AiIdentityRosterCard-BZM4aQnM.js} +1 -1
- package/dist/ui/assets/AiProfilesModule-BbAEWtHH.js +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-B68Wg-RV.js +3 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-TzYu7pkG.css +1 -0
- package/dist/ui/assets/AssetTraySortControl-C8Rztyal.js +1 -0
- package/dist/ui/assets/ContextAttachmentManager-Cj3c7X9y.js +3 -0
- package/dist/ui/assets/{DocumentWorkspace-BC7sy5TG.js → DocumentWorkspace-B5uvzems.js} +4 -4
- package/dist/ui/assets/EntityActivityTimeline-BVFvCB5R.js +1 -0
- package/dist/ui/assets/PlanningModule-B5DWQOGz.js +1 -0
- package/dist/ui/assets/PlanningModule-CoIR9hbV.css +1 -0
- package/dist/ui/assets/PlansPage-AY4cGWco.js +1 -0
- package/dist/ui/assets/TaskContextUpload-DZxTVesW.js +1 -0
- package/dist/ui/assets/TaskContextUpload-qmBeUw3u.css +1 -0
- package/dist/ui/assets/TaskSettings-BeWAuyZj.js +12 -0
- package/dist/ui/assets/{TaskforceAgentsModule-CkAxW19K.js → TaskforceAgentsModule-BifCYtjR.js} +6 -6
- package/dist/ui/assets/WorkflowManagerModule-DEr5di_r.js +1 -0
- package/dist/ui/assets/index-I5zhdtgt.js +7 -0
- package/dist/ui/assets/index-VRFsx3wz.css +1 -0
- package/dist/ui/assets/{vendor-icons-B8ZNsH1g.js → vendor-icons-D9Lpw-j4.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/utils/constants.d.ts +2 -2
- package/dist/utils/constants.js +1 -1
- package/dist/utils/contextAttachmentUpload.d.ts +2 -0
- package/dist/utils/contextAttachmentUpload.js +7 -0
- package/dist/utils/contextFiles.d.ts +1 -1
- package/dist/utils/contextFiles.js +4 -4
- package/dist/utils/taskActivity.d.ts +3 -2
- package/dist/utils/taskActivity.js +19 -2
- package/dist/utils/taskEditorReconciliation.d.ts +40 -0
- package/dist/utils/taskEditorReconciliation.js +217 -0
- package/dist/utils/taskNormalization.d.ts +1 -0
- package/dist/utils/taskNormalization.js +39 -0
- package/dist/utils/taskSearch.js +2 -2
- package/dist/utils/uiNotice.d.ts +4 -0
- package/package.json +21 -4
- package/dist/ui/assets/AiProfilesModule-DPxQeF9N.js +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-CBRZ8rca.css +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-DHNYaNg6.js +0 -3
- package/dist/ui/assets/AssetTraySortControl-DphbyHVc.js +0 -1
- package/dist/ui/assets/ContextAttachmentManager-CMxSzHqc.js +0 -3
- package/dist/ui/assets/EntityActivityTimeline-pJ2ZjZ-k.js +0 -1
- package/dist/ui/assets/PlanningModule-CeTqXMMz.css +0 -1
- package/dist/ui/assets/PlanningModule-t5GbvVR9.js +0 -1
- package/dist/ui/assets/PlansPage-BVH_NfgT.js +0 -1
- package/dist/ui/assets/TaskContextUpload-Ds4LnDzV.css +0 -1
- package/dist/ui/assets/TaskContextUpload-bl4fOOUz.js +0 -1
- package/dist/ui/assets/TaskSettings-xVsxSa0g.js +0 -12
- package/dist/ui/assets/WorkflowManagerModule-BKqIGVnF.js +0 -1
- package/dist/ui/assets/index-BzJlVnNS.css +0 -1
- 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
|
-
|
|
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 (
|
|
298
|
-
|
|
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
|
-
&&
|
|
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) =>
|
|
21
|
-
throw new Error('Local task attachment-link outbox payload must contain links and optional
|
|
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 {
|
|
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({
|
|
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
|
|
198
|
-
|
|
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(
|
|
274
|
-
|
|
275
|
-
|
|
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 =
|
|
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 =
|
|
276
|
-
|
|
277
|
-
|
|
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 =
|
|
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 =
|
|
218
|
-
|
|
219
|
-
|
|
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({
|
|
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: () =>
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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 =
|
|
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 (
|
|
304
|
-
|
|
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,
|