@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
|
@@ -21,6 +21,8 @@ const NON_APPLYING_OPERATION_CLASSES = new Set([
|
|
|
21
21
|
const MAX_DRAIN_INSPECTION_LIMIT = 500;
|
|
22
22
|
export const LOCAL_SYNC_PERMIT_RETENTION_MIN_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
23
23
|
export const LOCAL_SYNC_PERMIT_RETENTION_SOFT_LIMIT = 1_000;
|
|
24
|
+
export const LOCAL_SYNC_PERMIT_RETENTION_HARD_LIMIT = 10_000;
|
|
25
|
+
export const LOCAL_SYNC_OBLIGATION_RETENTION_HARD_LIMIT = 2_000;
|
|
24
26
|
export const LOCAL_SYNC_PERMIT_RETENTION_DELETE_BATCH = 250;
|
|
25
27
|
export const LOCAL_SYNC_PERMIT_RETENTION_SWEEP_LIMIT = 5_000;
|
|
26
28
|
const LOCAL_SYNC_PERMIT_RETENTION_CHECK_INTERVAL_MS = 60 * 60 * 1000;
|
|
@@ -166,11 +168,29 @@ export class LocalSyncExecutionPermitStore {
|
|
|
166
168
|
FROM local_sync_recovery_obligations
|
|
167
169
|
WHERE workspace_id = ? AND resolved_at IS NULL
|
|
168
170
|
`).get(workspaceId);
|
|
171
|
+
const terminalHistory = this.db.prepare(`
|
|
172
|
+
SELECT COUNT(*) AS count, MIN(completed_at) AS oldest_at
|
|
173
|
+
FROM local_sync_execution_permits
|
|
174
|
+
WHERE workspace_id = ?
|
|
175
|
+
AND state IN ('released', 'abandoned-clean', 'abandoned-reconcile-required')
|
|
176
|
+
AND completed_at IS NOT NULL
|
|
177
|
+
`).get(workspaceId);
|
|
178
|
+
const resolvedHistory = this.db.prepare(`
|
|
179
|
+
SELECT COUNT(*) AS count, MIN(resolved_at) AS oldest_at
|
|
180
|
+
FROM local_sync_recovery_obligations
|
|
181
|
+
WHERE workspace_id = ? AND resolved_at IS NOT NULL
|
|
182
|
+
`).get(workspaceId);
|
|
169
183
|
return {
|
|
170
184
|
active,
|
|
171
185
|
byState,
|
|
172
186
|
recoveryObligations: Number(obligation?.count || 0),
|
|
173
187
|
localCommitInvariantViolation: this.invariantBlockedWorkspaces.has(workspaceId),
|
|
188
|
+
history: {
|
|
189
|
+
terminalPermits: Number(terminalHistory?.count || 0),
|
|
190
|
+
oldestTerminalPermitAt: terminalHistory?.oldest_at || null,
|
|
191
|
+
resolvedRecoveryObligations: Number(resolvedHistory?.count || 0),
|
|
192
|
+
oldestResolvedRecoveryObligationAt: resolvedHistory?.oldest_at || null,
|
|
193
|
+
},
|
|
174
194
|
};
|
|
175
195
|
}
|
|
176
196
|
inspectGenerationDrain(input) {
|
|
@@ -417,6 +437,42 @@ export class LocalSyncExecutionPermitStore {
|
|
|
417
437
|
});
|
|
418
438
|
})();
|
|
419
439
|
}
|
|
440
|
+
renewAcceptedApplyCompletion(input) {
|
|
441
|
+
return this.db.transaction(() => {
|
|
442
|
+
const current = this.requirePermit(input.permitId);
|
|
443
|
+
this.assertFence(current, input);
|
|
444
|
+
if (current.ownerMode !== input.ownerMode) {
|
|
445
|
+
throw new LocalSyncExecutionPermitError(`Execution permit ${input.permitId} belongs to ${current.ownerMode}, not ${input.ownerMode}.`, 'SYNC_COORDINATOR_PERMIT_IDENTITY_CONFLICT');
|
|
446
|
+
}
|
|
447
|
+
if (current.state !== 'gateway-complete-awaiting-apply'
|
|
448
|
+
|| current.upstreamDisposition !== 'accepted') {
|
|
449
|
+
throw new LocalSyncExecutionPermitError(`Execution permit ${input.permitId} has no accepted apply completion to renew.`, 'SYNC_COORDINATOR_PERMIT_STATE_CONFLICT');
|
|
450
|
+
}
|
|
451
|
+
const now = this.now();
|
|
452
|
+
const deadlineMs = Date.parse(input.deadlineAt);
|
|
453
|
+
if (!Number.isFinite(deadlineMs)
|
|
454
|
+
|| deadlineMs <= Date.parse(now)
|
|
455
|
+
|| deadlineMs <= Date.parse(current.deadlineAt)) {
|
|
456
|
+
throw new LocalSyncExecutionPermitError(`Execution permit ${input.permitId} renewal deadline is invalid.`, 'SYNC_COORDINATOR_PERMIT_EXPIRED');
|
|
457
|
+
}
|
|
458
|
+
const result = this.db.prepare(`
|
|
459
|
+
UPDATE local_sync_execution_permits
|
|
460
|
+
SET deadline_at = ?,
|
|
461
|
+
last_transition_at = ?
|
|
462
|
+
WHERE permit_id = ?
|
|
463
|
+
AND workspace_id = ?
|
|
464
|
+
AND coordinator_generation = ?
|
|
465
|
+
AND server_instance_id = ?
|
|
466
|
+
AND owner_mode = ?
|
|
467
|
+
AND state = 'gateway-complete-awaiting-apply'
|
|
468
|
+
AND upstream_disposition = 'accepted'
|
|
469
|
+
`).run(input.deadlineAt, now, input.permitId, input.workspaceId, input.coordinatorGeneration, input.serverInstanceId, input.ownerMode);
|
|
470
|
+
if (Number(result?.changes || 0) !== 1) {
|
|
471
|
+
throw new LocalSyncExecutionPermitError(`Execution permit ${input.permitId} changed before its renewal committed.`, 'SYNC_COORDINATOR_PERMIT_STATE_CONFLICT');
|
|
472
|
+
}
|
|
473
|
+
return this.read(input.permitId);
|
|
474
|
+
})();
|
|
475
|
+
}
|
|
420
476
|
completeAcceptedV3Dispatch(input, applyAuthoritativeAndAck) {
|
|
421
477
|
return this.db.transaction(() => {
|
|
422
478
|
const current = this.requirePermit(input.permitId);
|
|
@@ -563,9 +619,15 @@ export class LocalSyncExecutionPermitStore {
|
|
|
563
619
|
if (current.state === 'local-commit-entered') {
|
|
564
620
|
throw new LocalSyncExecutionPermitError(`Execution permit ${input.permitId} is owned by the server local commit.`, 'SYNC_COORDINATOR_PERMIT_STATE_CONFLICT');
|
|
565
621
|
}
|
|
622
|
+
const cleanReadFailure = input.readResponseUnavailableBeforeApply === true
|
|
623
|
+
&& input.errorCode === 'SYNC_COORDINATOR_GATEWAY_REQUEST_FAILED'
|
|
624
|
+
&& current.state === 'gateway-complete-awaiting-apply'
|
|
625
|
+
&& current.upstreamDisposition === 'unknown'
|
|
626
|
+
&& (current.operationClass === 'pull' || current.operationClass === 'repair');
|
|
566
627
|
const clean = current.state === 'issued-idle'
|
|
567
628
|
|| (current.state === 'gateway-complete-awaiting-apply'
|
|
568
|
-
&& current.upstreamDisposition === 'rejected')
|
|
629
|
+
&& current.upstreamDisposition === 'rejected')
|
|
630
|
+
|| cleanReadFailure;
|
|
569
631
|
const disposition = current.upstreamDisposition === 'accepted'
|
|
570
632
|
? 'accepted'
|
|
571
633
|
: current.upstreamDisposition === 'rejected'
|
|
@@ -677,19 +739,14 @@ export class LocalSyncExecutionPermitStore {
|
|
|
677
739
|
WHERE resolved_at IS NOT NULL
|
|
678
740
|
ORDER BY resolved_at DESC, obligation_id DESC
|
|
679
741
|
`).all().map(mapObligation);
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
.map((obligation) => obligation.obligationId));
|
|
683
|
-
for (const obligation of resolvedObligations) {
|
|
684
|
-
if (obligation.resolvedAt && obligation.resolvedAt >= cutoff) {
|
|
685
|
-
protectedObligationIds.add(obligation.obligationId);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
742
|
+
const obligationAgeDeleteCount = Math.max(0, resolvedObligations.length - LOCAL_SYNC_PERMIT_RETENTION_SOFT_LIMIT);
|
|
743
|
+
const obligationHardDeleteCount = Math.max(0, resolvedObligations.length - LOCAL_SYNC_OBLIGATION_RETENTION_HARD_LIMIT);
|
|
688
744
|
const obligationCandidates = resolvedObligations
|
|
689
|
-
.
|
|
690
|
-
&& obligation.resolvedAt < cutoff
|
|
691
|
-
&& !protectedObligationIds.has(obligation.obligationId)))
|
|
745
|
+
.slice()
|
|
692
746
|
.reverse()
|
|
747
|
+
.filter((obligation, index) => (index < obligationHardDeleteCount
|
|
748
|
+
|| (index < obligationAgeDeleteCount
|
|
749
|
+
&& Boolean(obligation.resolvedAt && obligation.resolvedAt < cutoff))))
|
|
693
750
|
.slice(0, boundedDeleteLimit);
|
|
694
751
|
const removeObligation = this.db.prepare(`
|
|
695
752
|
DELETE FROM local_sync_recovery_obligations
|
|
@@ -707,14 +764,7 @@ export class LocalSyncExecutionPermitStore {
|
|
|
707
764
|
AND completed_at IS NOT NULL
|
|
708
765
|
ORDER BY completed_at DESC, permit_id DESC
|
|
709
766
|
`).all().map(mapPermit);
|
|
710
|
-
const protectedPermitIds = new Set(
|
|
711
|
-
.slice(0, LOCAL_SYNC_PERMIT_RETENTION_SOFT_LIMIT)
|
|
712
|
-
.map((permit) => permit.permitId));
|
|
713
|
-
for (const permit of terminalPermits) {
|
|
714
|
-
if (permit.completedAt && permit.completedAt >= cutoff) {
|
|
715
|
-
protectedPermitIds.add(permit.permitId);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
767
|
+
const protectedPermitIds = new Set();
|
|
718
768
|
const referencedPermitRows = this.db.prepare(`
|
|
719
769
|
SELECT permit_id
|
|
720
770
|
FROM local_sync_recovery_obligations
|
|
@@ -723,11 +773,15 @@ export class LocalSyncExecutionPermitStore {
|
|
|
723
773
|
if (row.permit_id)
|
|
724
774
|
protectedPermitIds.add(row.permit_id);
|
|
725
775
|
}
|
|
776
|
+
const permitAgeDeleteCount = Math.max(0, terminalPermits.length - LOCAL_SYNC_PERMIT_RETENTION_SOFT_LIMIT);
|
|
777
|
+
const permitHardDeleteCount = Math.max(0, terminalPermits.length - LOCAL_SYNC_PERMIT_RETENTION_HARD_LIMIT);
|
|
726
778
|
const permitCandidates = terminalPermits
|
|
727
|
-
.
|
|
728
|
-
&& permit.completedAt < cutoff
|
|
729
|
-
&& !protectedPermitIds.has(permit.permitId)))
|
|
779
|
+
.slice()
|
|
730
780
|
.reverse()
|
|
781
|
+
.filter((permit, index) => (!protectedPermitIds.has(permit.permitId)
|
|
782
|
+
&& (index < permitHardDeleteCount
|
|
783
|
+
|| (index < permitAgeDeleteCount
|
|
784
|
+
&& Boolean(permit.completedAt && permit.completedAt < cutoff)))))
|
|
731
785
|
.slice(0, boundedDeleteLimit);
|
|
732
786
|
const removePermit = this.db.prepare(`
|
|
733
787
|
DELETE FROM local_sync_execution_permits
|
|
@@ -17,6 +17,10 @@ const RUNNER_COMMANDS = new Set([
|
|
|
17
17
|
]);
|
|
18
18
|
const SCHEDULED_RETRY_BASE_DELAY_MS = 1_000;
|
|
19
19
|
const SCHEDULED_RETRY_MAX_DELAY_MS = 5 * 60_000;
|
|
20
|
+
const CONTENT_PREREQUISITE_BYPASSED_RETRY_KINDS = new Set([
|
|
21
|
+
'pull',
|
|
22
|
+
'repair',
|
|
23
|
+
]);
|
|
20
24
|
function cloneCheckpoint(checkpoint) {
|
|
21
25
|
return JSON.parse(canonicalWorkspaceSyncJson(checkpoint));
|
|
22
26
|
}
|
|
@@ -159,7 +163,7 @@ export class LocalWorkspaceSyncRunner {
|
|
|
159
163
|
if (!this.context || this.terminated || !this.acceptingWork) {
|
|
160
164
|
return Promise.resolve();
|
|
161
165
|
}
|
|
162
|
-
return this.enqueue(async () => this.runInspection(this.options.operations.pushContentPrerequisite, {
|
|
166
|
+
return this.enqueue(async () => this.runInspection(this.options.operations.pushContentPrerequisite, { bypassRetryKinds: CONTENT_PREREQUISITE_BYPASSED_RETRY_KINDS }));
|
|
163
167
|
}
|
|
164
168
|
observeRemoteHint(observedAt = this.now()) {
|
|
165
169
|
if (!this.context || this.terminated || !this.acceptingWork) {
|
|
@@ -476,7 +480,7 @@ export class LocalWorkspaceSyncRunner {
|
|
|
476
480
|
if (!state.checkpoint.syncEnabled || !this.hasExactCapability(context))
|
|
477
481
|
return;
|
|
478
482
|
if (state.checkpoint.retry
|
|
479
|
-
&& state.checkpoint.retry.kind
|
|
483
|
+
&& !options.bypassRetryKinds?.has(state.checkpoint.retry.kind)
|
|
480
484
|
&& Date.parse(state.checkpoint.retry.retryAt) > Date.parse(this.now())) {
|
|
481
485
|
this.schedulePersistedRetry(state, this.lifecycleToken);
|
|
482
486
|
return;
|
|
@@ -500,10 +500,11 @@ export function workspaceSyncGatewayTimeoutMs(operation) {
|
|
|
500
500
|
if (operation.type === 'initiative-repair'
|
|
501
501
|
|| operation.type === 'combined-repair'
|
|
502
502
|
|| operation.type === 'v3-feed-repair'
|
|
503
|
-
|| (operation.type === 'push' && operation.repairMode === true)
|
|
504
|
-
|| (operation.type === 'pull' && operation.repairMode === true)
|
|
505
503
|
|| (operation.type === 'v3-feed'
|
|
506
504
|
&& (operation.forceRepair === true || Boolean(operation.repairCursor))))
|
|
505
|
+
return 75_000;
|
|
506
|
+
if ((operation.type === 'push' && operation.repairMode === true)
|
|
507
|
+
|| (operation.type === 'pull' && operation.repairMode === true))
|
|
507
508
|
return 180_000;
|
|
508
509
|
if (operation.type === 'push'
|
|
509
510
|
|| operation.type === 'bootstrap-push')
|
|
@@ -638,6 +639,7 @@ const CHANGE_FIELDS = {
|
|
|
638
639
|
'task-relationship-delete': { allowed: ['op', 'relationship'], required: ['relationship'], objectField: 'relationship' },
|
|
639
640
|
'initiative-upsert': { allowed: ['op', 'initiative'], required: ['initiative'], objectField: 'initiative' },
|
|
640
641
|
'workstream-upsert': { allowed: ['op', 'workstream'], required: ['workstream'], objectField: 'workstream' },
|
|
642
|
+
'planning-identity-upsert': { allowed: ['op', 'identity'], required: ['identity'], objectField: 'identity' },
|
|
641
643
|
'taxonomy-upsert': { allowed: ['op', 'taxonomies', 'taxonomyState', 'updatedAt', 'durableRevision'], required: ['updatedAt'] },
|
|
642
644
|
'ai-profile-upsert': { allowed: ['op', 'profile'], required: ['profile'], objectField: 'profile' },
|
|
643
645
|
'document-upsert': { allowed: [
|
|
@@ -40,6 +40,18 @@ function v3Boundary(input) {
|
|
|
40
40
|
...(input.coverage ? { coverage: { ...input.coverage } } : {}),
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
+
function independentContentBoundary(identity) {
|
|
44
|
+
const normalizedIdentity = String(identity || '').trim();
|
|
45
|
+
if (!normalizedIdentity) {
|
|
46
|
+
throw new TypeError('Independent content transfer identity is required.');
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
version: 1,
|
|
50
|
+
projection: 'independent-content',
|
|
51
|
+
cursor: null,
|
|
52
|
+
identity: normalizedIdentity,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
43
55
|
async function decodeGatewayResponse(response, nowMs) {
|
|
44
56
|
const rawText = await response.text();
|
|
45
57
|
let data = {};
|
|
@@ -405,10 +417,12 @@ export function createBrowserGatewayWorkspaceSyncEngineRemoteServices(options) {
|
|
|
405
417
|
});
|
|
406
418
|
},
|
|
407
419
|
pullWorkspaceContent: (input) => {
|
|
408
|
-
const { previousCursor, ...request } = input;
|
|
420
|
+
const { previousCursor, independentCompletionIdentity, ...request } = input;
|
|
409
421
|
return execute({
|
|
410
422
|
operation: { type: 'content-pull', ...request },
|
|
411
|
-
previousBoundary:
|
|
423
|
+
previousBoundary: independentCompletionIdentity
|
|
424
|
+
? independentContentBoundary(independentCompletionIdentity)
|
|
425
|
+
: v2Boundary(previousCursor),
|
|
412
426
|
applying: true,
|
|
413
427
|
});
|
|
414
428
|
},
|
|
@@ -510,6 +524,10 @@ export function createBrowserGatewayWorkspaceSyncEngineRemoteServices(options) {
|
|
|
510
524
|
}));
|
|
511
525
|
},
|
|
512
526
|
buildV2ApplyCompletionGroup: ({ workspaceId, previousCursor, nextCursor, receipts, }) => completionGroup(workspaceId, receipts, v2Boundary(previousCursor), v2Boundary(nextCursor)),
|
|
527
|
+
buildIndependentContentApplyCompletionGroup: ({ workspaceId, identity, receipts, }) => {
|
|
528
|
+
const boundary = independentContentBoundary(identity);
|
|
529
|
+
return completionGroup(workspaceId, receipts, boundary, boundary);
|
|
530
|
+
},
|
|
513
531
|
buildV2PushApplyCompletionGroup: ({ workspaceId, pushBoundary, receipts, }) => completionGroup(workspaceId, receipts, parseWorkspaceSyncEngineV2PushBoundary(pushBoundary), parseWorkspaceSyncEngineV2PushBoundary(pushBoundary)),
|
|
514
532
|
buildV3ApplyCompletionGroup: ({ workspaceId, apply, receipts }) => {
|
|
515
533
|
const metadata = readReceipts(workspaceId, receipts);
|
|
@@ -24,6 +24,10 @@ export interface WorkspaceSyncEngineV2ApplyOptions {
|
|
|
24
24
|
export interface WorkspaceSyncEngineV3ApplyOptions {
|
|
25
25
|
completionGroup?: WorkspaceSyncEngineTransferCompletionGroup;
|
|
26
26
|
}
|
|
27
|
+
export declare function authoritativeV2ApplyFailureMessage(input: {
|
|
28
|
+
status?: number;
|
|
29
|
+
data?: unknown;
|
|
30
|
+
}): string;
|
|
27
31
|
/**
|
|
28
32
|
* Local-runtime operations used by the workspace sync engine.
|
|
29
33
|
*
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { applyLocalWorkspaceChanges, applyLocalWorkspaceSyncV3CombinedProjection, applyLocalWorkspaceSyncV3Projection, applyLocalWorkspaceSyncV3TaskCommentAudit, completeLocalSyncAuthoritativeRepair, getLocalWorkspaceContentPullObligations, recordLocalWorkspaceContentPullObligationFailures, getLocalTaskMetadataOwnership, getLocalWorkspaceContentManifest, getLocalWorkspaceSyncV3CombinedProjection, getLocalWorkspaceSyncV3Projection, getLocalWorkspaceSyncV3TaskCommentAudit, } from '../cloudSyncApi.js';
|
|
2
2
|
import { prepareLocalTaskRootOutboxRecovery, } from '../v3/localOutboxRecoveryClient.js';
|
|
3
|
+
export function authoritativeV2ApplyFailureMessage(input) {
|
|
4
|
+
const data = input.data && typeof input.data === 'object' && !Array.isArray(input.data)
|
|
5
|
+
? input.data
|
|
6
|
+
: {};
|
|
7
|
+
const detail = String(data.error || '').trim();
|
|
8
|
+
const code = String(data.code || '').trim();
|
|
9
|
+
const status = Number(input.status) || 0;
|
|
10
|
+
return detail
|
|
11
|
+
? `Authoritative V2 push echo apply failed (${status}): ${detail}${code ? ` [${code}]` : ''}`
|
|
12
|
+
: `Authoritative V2 push echo apply failed (${status}).`;
|
|
13
|
+
}
|
|
3
14
|
/**
|
|
4
15
|
* Same-origin HTTP implementation used while the browser owns orchestration.
|
|
5
16
|
*/
|
|
@@ -37,6 +37,12 @@ export type WorkspaceSyncEngineContentPullInput = Parameters<typeof pullWorkspac
|
|
|
37
37
|
* commit against one exact prior boundary.
|
|
38
38
|
*/
|
|
39
39
|
previousCursor: string | null;
|
|
40
|
+
/**
|
|
41
|
+
* Separates an independent content lookup from a V2 feed page. The
|
|
42
|
+
* identity becomes an apply boundary that settles the transfer
|
|
43
|
+
* without advancing or validating the runner's V2 cursor.
|
|
44
|
+
*/
|
|
45
|
+
independentCompletionIdentity?: string;
|
|
40
46
|
};
|
|
41
47
|
type RemoteResult<Result> = Awaited<Result> & {
|
|
42
48
|
transferReceipt?: WorkspaceSyncEngineRemoteTransferReceipt;
|
|
@@ -83,12 +89,22 @@ export interface WorkspaceSyncEngineRemoteServices {
|
|
|
83
89
|
workspaceId: string;
|
|
84
90
|
receipts: readonly WorkspaceSyncEngineRemoteTransferReceipt[];
|
|
85
91
|
}): void | Promise<void>;
|
|
92
|
+
/** Keeps accepted applying receipts alive until a bounded transfer reaches its atomic commit. */
|
|
93
|
+
retainTransferReceipts?(input: {
|
|
94
|
+
workspaceId: string;
|
|
95
|
+
receipts: readonly WorkspaceSyncEngineRemoteTransferReceipt[];
|
|
96
|
+
}): void | Promise<void>;
|
|
86
97
|
buildV2ApplyCompletionGroup(input: {
|
|
87
98
|
workspaceId: string;
|
|
88
99
|
previousCursor: string | null;
|
|
89
100
|
nextCursor: string | null;
|
|
90
101
|
receipts: readonly WorkspaceSyncEngineRemoteTransferReceipt[];
|
|
91
102
|
}): WorkspaceSyncEngineTransferCompletionGroup | undefined;
|
|
103
|
+
buildIndependentContentApplyCompletionGroup(input: {
|
|
104
|
+
workspaceId: string;
|
|
105
|
+
identity: string;
|
|
106
|
+
receipts: readonly WorkspaceSyncEngineRemoteTransferReceipt[];
|
|
107
|
+
}): WorkspaceSyncEngineTransferCompletionGroup | undefined;
|
|
92
108
|
buildV2PushApplyCompletionGroup(input: {
|
|
93
109
|
workspaceId: string;
|
|
94
110
|
pushBoundary: WorkspaceSyncEngineV2PushBoundary;
|
|
@@ -6,7 +6,7 @@ export function createBrowserWorkspaceSyncEngineRemoteServices(resolveCloudAuthU
|
|
|
6
6
|
provisionWorkspace: (input) => provisionWorkspace(resolveCloudAuthUrl, input),
|
|
7
7
|
pushWorkspaceChanges: ({ ownership: _ownership, pushBoundary: _pushBoundary, ...input }) => (pushWorkspaceChanges(resolveCloudAuthUrl, input)),
|
|
8
8
|
pullWorkspaceChanges: (input) => pullWorkspaceChanges(resolveCloudAuthUrl, input),
|
|
9
|
-
pullWorkspaceContent: ({ previousCursor: _previousCursor, ...input }) => (pullWorkspaceContent(resolveCloudAuthUrl, input)),
|
|
9
|
+
pullWorkspaceContent: ({ previousCursor: _previousCursor, independentCompletionIdentity: _independentCompletionIdentity, ...input }) => (pullWorkspaceContent(resolveCloudAuthUrl, input)),
|
|
10
10
|
pullInitiativeFeed: (input) => pullWorkspaceSyncV3Feed(resolveCloudAuthUrl, input),
|
|
11
11
|
pullInitiativeRepair: (input) => pullWorkspaceInitiativeRepair(resolveCloudAuthUrl, input),
|
|
12
12
|
pullCombinedFeed: (input) => pullWorkspaceSyncV3CombinedFeed(resolveCloudAuthUrl, input),
|
|
@@ -15,6 +15,7 @@ export function createBrowserWorkspaceSyncEngineRemoteServices(resolveCloudAuthU
|
|
|
15
15
|
abandonTransferReceipts: () => undefined,
|
|
16
16
|
acknowledgeNonApplyingTransferReceipts: () => undefined,
|
|
17
17
|
buildV2ApplyCompletionGroup: () => undefined,
|
|
18
|
+
buildIndependentContentApplyCompletionGroup: () => undefined,
|
|
18
19
|
buildV2PushApplyCompletionGroup: () => undefined,
|
|
19
20
|
buildV3ApplyCompletionGroup: () => undefined,
|
|
20
21
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { LocalSyncExecutionPermitStore } from '../coordinator/localSyncExecutionPermitStore.js';
|
|
2
2
|
import type { WorkspaceSyncTransportGateway } from '../coordinator/workspaceSyncTransportGateway.js';
|
|
3
3
|
import type { WorkspaceSyncEngineRemoteServices } from './workspaceSyncEngineRemoteServices.js';
|
|
4
|
-
type ServerRemotePermitStore = Pick<LocalSyncExecutionPermitStore, 'issueServer' | 'read' | 'acknowledgeNonApplyingCompletion' | 'completeAcceptedPushWithoutEcho' | 'abandonPermit'>;
|
|
4
|
+
type ServerRemotePermitStore = Pick<LocalSyncExecutionPermitStore, 'issueServer' | 'read' | 'acknowledgeNonApplyingCompletion' | 'completeAcceptedPushWithoutEcho' | 'renewAcceptedApplyCompletion' | 'abandonPermit'>;
|
|
5
5
|
type ServerRemoteGateway = Pick<WorkspaceSyncTransportGateway, 'executeServer'>;
|
|
6
6
|
export interface ServerWorkspaceSyncEngineRemoteServicesOptions {
|
|
7
7
|
workspaceId: string;
|
|
@@ -40,6 +40,18 @@ function v3Boundary(input) {
|
|
|
40
40
|
...(input.coverage ? { coverage: { ...input.coverage } } : {}),
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
+
function independentContentBoundary(identity) {
|
|
44
|
+
const normalizedIdentity = String(identity || '').trim();
|
|
45
|
+
if (!normalizedIdentity) {
|
|
46
|
+
throw new TypeError('Independent content transfer identity is required.');
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
version: 1,
|
|
50
|
+
projection: 'independent-content',
|
|
51
|
+
cursor: null,
|
|
52
|
+
identity: normalizedIdentity,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
43
55
|
function decodeGatewayResult(result, nowMs) {
|
|
44
56
|
const rawText = new TextDecoder().decode(result.body);
|
|
45
57
|
let data = {};
|
|
@@ -96,12 +108,15 @@ export function createServerWorkspaceSyncEngineRemoteServices(options) {
|
|
|
96
108
|
throw new TypeError('The server sync operation workspace does not match its runner.');
|
|
97
109
|
}
|
|
98
110
|
};
|
|
99
|
-
const abandon = (permitId, errorCode) => (options.permitStore.abandonPermit({
|
|
111
|
+
const abandon = (permitId, errorCode, readResponseUnavailableBeforeApply = false) => (options.permitStore.abandonPermit({
|
|
100
112
|
permitId,
|
|
101
113
|
workspaceId: options.workspaceId,
|
|
102
114
|
coordinatorGeneration: options.coordinatorGeneration,
|
|
103
115
|
serverInstanceId: options.serverInstanceId,
|
|
104
116
|
errorCode,
|
|
117
|
+
...(readResponseUnavailableBeforeApply
|
|
118
|
+
? { readResponseUnavailableBeforeApply: true }
|
|
119
|
+
: {}),
|
|
105
120
|
}));
|
|
106
121
|
const execute = async (input) => {
|
|
107
122
|
assertWorkspace(input.operation.workspaceId);
|
|
@@ -131,7 +146,7 @@ export function createServerWorkspaceSyncEngineRemoteServices(options) {
|
|
|
131
146
|
});
|
|
132
147
|
}
|
|
133
148
|
catch (error) {
|
|
134
|
-
abandon(permit.permitId, 'SYNC_COORDINATOR_GATEWAY_REQUEST_FAILED');
|
|
149
|
+
abandon(permit.permitId, 'SYNC_COORDINATOR_GATEWAY_REQUEST_FAILED', operationClass === 'pull' || operationClass === 'repair');
|
|
135
150
|
throw error;
|
|
136
151
|
}
|
|
137
152
|
const decoded = decodeGatewayResult(gatewayResult, issuedAt.getTime());
|
|
@@ -171,6 +186,12 @@ export function createServerWorkspaceSyncEngineRemoteServices(options) {
|
|
|
171
186
|
abandon(permit.permitId, 'SYNC_COORDINATOR_APPLYING_REJECTED');
|
|
172
187
|
return result;
|
|
173
188
|
}
|
|
189
|
+
if (!result.ok
|
|
190
|
+
&& completedPermit.upstreamDisposition === 'unknown'
|
|
191
|
+
&& (operationClass === 'pull' || operationClass === 'repair')) {
|
|
192
|
+
abandon(permit.permitId, 'SYNC_COORDINATOR_GATEWAY_REQUEST_FAILED', true);
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
174
195
|
if (!input.previousBoundary) {
|
|
175
196
|
abandon(permit.permitId, 'SYNC_COORDINATOR_APPLY_BOUNDARY_MISSING');
|
|
176
197
|
throw new Error('A server applying request did not declare its prior boundary.');
|
|
@@ -181,6 +202,7 @@ export function createServerWorkspaceSyncEngineRemoteServices(options) {
|
|
|
181
202
|
workspaceId: options.workspaceId,
|
|
182
203
|
previousBoundary: input.previousBoundary,
|
|
183
204
|
operationClass,
|
|
205
|
+
retentionMs: permitDeadlineMs,
|
|
184
206
|
});
|
|
185
207
|
return {
|
|
186
208
|
...result,
|
|
@@ -318,10 +340,12 @@ export function createServerWorkspaceSyncEngineRemoteServices(options) {
|
|
|
318
340
|
});
|
|
319
341
|
},
|
|
320
342
|
pullWorkspaceContent: (input) => {
|
|
321
|
-
const { previousCursor, ...request } = input;
|
|
343
|
+
const { previousCursor, independentCompletionIdentity, ...request } = input;
|
|
322
344
|
return execute({
|
|
323
345
|
operation: { type: 'content-pull', ...request },
|
|
324
|
-
previousBoundary:
|
|
346
|
+
previousBoundary: independentCompletionIdentity
|
|
347
|
+
? independentContentBoundary(independentCompletionIdentity)
|
|
348
|
+
: v2Boundary(previousCursor),
|
|
325
349
|
applying: true,
|
|
326
350
|
});
|
|
327
351
|
},
|
|
@@ -417,7 +441,25 @@ export function createServerWorkspaceSyncEngineRemoteServices(options) {
|
|
|
417
441
|
}
|
|
418
442
|
}
|
|
419
443
|
},
|
|
444
|
+
retainTransferReceipts: ({ workspaceId, receipts }) => {
|
|
445
|
+
const metadata = readReceipts(workspaceId, receipts);
|
|
446
|
+
const retainedAt = now();
|
|
447
|
+
for (const receipt of metadata) {
|
|
448
|
+
options.permitStore.renewAcceptedApplyCompletion({
|
|
449
|
+
permitId: receipt.permitId,
|
|
450
|
+
workspaceId,
|
|
451
|
+
coordinatorGeneration: options.coordinatorGeneration,
|
|
452
|
+
ownerMode: 'server',
|
|
453
|
+
serverInstanceId: options.serverInstanceId,
|
|
454
|
+
deadlineAt: new Date(retainedAt.getTime() + receipt.retentionMs).toISOString(),
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
},
|
|
420
458
|
buildV2ApplyCompletionGroup: ({ workspaceId, previousCursor, nextCursor, receipts, }) => completionGroup(workspaceId, receipts, v2Boundary(previousCursor), v2Boundary(nextCursor)),
|
|
459
|
+
buildIndependentContentApplyCompletionGroup: ({ workspaceId, identity, receipts, }) => {
|
|
460
|
+
const boundary = independentContentBoundary(identity);
|
|
461
|
+
return completionGroup(workspaceId, receipts, boundary, boundary);
|
|
462
|
+
},
|
|
421
463
|
buildV2PushApplyCompletionGroup: ({ workspaceId, pushBoundary, receipts, }) => completionGroup(workspaceId, receipts, parseWorkspaceSyncEngineV2PushBoundary(pushBoundary), parseWorkspaceSyncEngineV2PushBoundary(pushBoundary)),
|
|
422
464
|
buildV3ApplyCompletionGroup: ({ workspaceId, apply, receipts }) => {
|
|
423
465
|
const metadata = readReceipts(workspaceId, receipts);
|
|
@@ -2,7 +2,7 @@ import type { LocalWorkspaceSyncRunnerOperations } from '../coordinator/localWor
|
|
|
2
2
|
import { type EnsureWorkspaceReadyResult } from '../syncService.js';
|
|
3
3
|
import type { WorkspaceSyncV2ProjectionExclusion } from '../v3/workspaceSyncV2ProjectionExclusions.js';
|
|
4
4
|
import type { WorkspaceSyncV3CombinedCoverageSelector } from '../v3/workspaceSyncV3CombinedWireContract.js';
|
|
5
|
-
import type
|
|
5
|
+
import { type WorkspaceSyncEngineLocalServices } from './workspaceSyncEngineLocalServices.js';
|
|
6
6
|
import type { WorkspaceSyncEngineRemoteServices } from './workspaceSyncEngineRemoteServices.js';
|
|
7
7
|
import type { WorkspaceSyncEngineServerPushCandidateProvider } from './workspaceSyncEngineServerSnapshotReader.js';
|
|
8
8
|
import type { WorkspaceSyncEngineServerCheckpointPort } from './workspaceSyncEngineServerApplyBoundary.js';
|
|
@@ -46,11 +46,13 @@ export interface WorkspaceSyncEngineServerAttachmentApplyPort {
|
|
|
46
46
|
} | {
|
|
47
47
|
success: false;
|
|
48
48
|
error: string;
|
|
49
|
+
disposition?: 'retryable' | 'terminal-unavailable';
|
|
49
50
|
}>;
|
|
50
51
|
}): Promise<{
|
|
51
52
|
attempted: number;
|
|
52
53
|
resolved: number;
|
|
53
54
|
remaining: number;
|
|
55
|
+
terminal: number;
|
|
54
56
|
nextAttemptAt: string | null;
|
|
55
57
|
lastError: string | null;
|
|
56
58
|
}>;
|
|
@@ -6,6 +6,7 @@ import { runWorkspaceSyncV3CombinedProjectionCycle } from '../v3/workspaceSyncV3
|
|
|
6
6
|
import { runWorkspaceSyncV3InitiativeProjectionCycle } from '../v3/workspaceSyncV3FeedOrchestrator.js';
|
|
7
7
|
import { normalizeWorkspaceSyncV2ProjectionExclusions } from '../v3/workspaceSyncV2ProjectionExclusions.js';
|
|
8
8
|
import { canonicalWorkspaceSyncJson } from '../v3/canonicalWorkspaceSyncJson.js';
|
|
9
|
+
import { authoritativeV2ApplyFailureMessage, } from './workspaceSyncEngineLocalServices.js';
|
|
9
10
|
import { combinedAttachmentAssetHydrationId, createWorkspaceSyncV3TransferBindings, } from './workspaceSyncEngineTransfers.js';
|
|
10
11
|
import { createWorkspaceSyncTransferEvidence, } from './workspaceSyncTransferEvidence.js';
|
|
11
12
|
const NOOP_CLOUD_RESOLVER = (path) => path;
|
|
@@ -13,6 +14,7 @@ const RETRY_BASE_DELAY_MS = 1_000;
|
|
|
13
14
|
const RETRY_MAX_DELAY_MS = 5 * 60_000;
|
|
14
15
|
const COMBINED_FEED_COVERED_OUTBOX_BLOCK = 'WORKSPACE_SYNC_V3_COVERED_LOCAL_OPERATIONS';
|
|
15
16
|
const REPAIR_OWNERSHIP_FINGERPRINT_VERSION = 2;
|
|
17
|
+
const NON_APPLYING_ACKNOWLEDGEMENT_CHECKPOINT_INTERVAL = 64;
|
|
16
18
|
class WorkspaceSyncEngineServerRunnerOperationError extends Error {
|
|
17
19
|
code;
|
|
18
20
|
retryAfterMs;
|
|
@@ -146,6 +148,20 @@ export function createWorkspaceSyncEngineServerRunnerOperations(options) {
|
|
|
146
148
|
transformCheckpointAtTransferBoundary: (boundary) => (transferBoundaryCheckpointTransform(boundary)),
|
|
147
149
|
},
|
|
148
150
|
});
|
|
151
|
+
const prepareAndDrainRecoveryReplay = async (preparedRecovery) => {
|
|
152
|
+
let recovery = preparedRecovery
|
|
153
|
+
?? await localServices.prepareOutboxRecovery(workspaceId);
|
|
154
|
+
if (recovery.kind === 'recovery' && recovery.state === 'replay_queued') {
|
|
155
|
+
const drainResult = await options.drainV3Outbox?.({
|
|
156
|
+
beforeTransport: () => undefined,
|
|
157
|
+
});
|
|
158
|
+
if (drainResult === 'v2-content-push-required') {
|
|
159
|
+
fail('v2-content-push', 'A referenced attachment must be published before durable repair can continue.', 'SYNC_RUNNER_V2_CONTENT_PUSH_REQUIRED');
|
|
160
|
+
}
|
|
161
|
+
recovery = await localServices.prepareOutboxRecovery(workspaceId);
|
|
162
|
+
}
|
|
163
|
+
return recovery;
|
|
164
|
+
};
|
|
149
165
|
const ensureReady = options.ensureCloudWorkspaceReadyForSync
|
|
150
166
|
|| (async () => {
|
|
151
167
|
const result = await options.remoteServices.handshakeWorkspace(workspaceId);
|
|
@@ -499,7 +515,9 @@ export function createWorkspaceSyncEngineServerRunnerOperations(options) {
|
|
|
499
515
|
? 'repair'
|
|
500
516
|
: 'push';
|
|
501
517
|
activeRetryKind = pushRetryKind;
|
|
502
|
-
|
|
518
|
+
if (checkpoint.controlState !== 'pushing') {
|
|
519
|
+
persist({ ...checkpoint, controlState: 'pushing' });
|
|
520
|
+
}
|
|
503
521
|
let payload;
|
|
504
522
|
let signature;
|
|
505
523
|
if (checkpoint.pendingV2Push) {
|
|
@@ -537,11 +555,13 @@ export function createWorkspaceSyncEngineServerRunnerOperations(options) {
|
|
|
537
555
|
persist({ ...checkpoint, pendingV2Push });
|
|
538
556
|
}
|
|
539
557
|
if (checkpoint.remoteHintObservedAt) {
|
|
540
|
-
|
|
558
|
+
checkpoint = {
|
|
541
559
|
...checkpoint,
|
|
542
560
|
consecutivePushesWhilePullDue: Math.min(Number.MAX_SAFE_INTEGER, checkpoint.consecutivePushesWhilePullDue + 1),
|
|
543
|
-
}
|
|
561
|
+
};
|
|
544
562
|
}
|
|
563
|
+
let persistedAcknowledgementCount = checkpoint.pendingV2Push
|
|
564
|
+
?.acknowledgedNonApplyingBatchOperationIdentities.length || 0;
|
|
545
565
|
const result = await runWorkspacePushSyncService({
|
|
546
566
|
resolveCloudAuthUrl: NOOP_CLOUD_RESOLVER,
|
|
547
567
|
workspaceId,
|
|
@@ -569,13 +589,14 @@ export function createWorkspaceSyncEngineServerRunnerOperations(options) {
|
|
|
569
589
|
],
|
|
570
590
|
},
|
|
571
591
|
});
|
|
592
|
+
persistedAcknowledgementCount = acknowledgedOperationIdentities.length;
|
|
572
593
|
},
|
|
573
594
|
onNonApplyingBatchAcknowledged: ({ acknowledgedOperationIdentities }) => {
|
|
574
595
|
const pendingV2Push = checkpoint.pendingV2Push;
|
|
575
596
|
if (!pendingV2Push) {
|
|
576
597
|
throw new WorkspaceSyncEngineServerRunnerOperationError('The frozen V2 push disappeared while recording batch progress.', 'SYNC_RUNNER_PUSH_CHECKPOINT_INVALID');
|
|
577
598
|
}
|
|
578
|
-
|
|
599
|
+
checkpoint = {
|
|
579
600
|
...checkpoint,
|
|
580
601
|
pendingV2Push: {
|
|
581
602
|
...pendingV2Push,
|
|
@@ -583,19 +604,25 @@ export function createWorkspaceSyncEngineServerRunnerOperations(options) {
|
|
|
583
604
|
...acknowledgedOperationIdentities,
|
|
584
605
|
],
|
|
585
606
|
},
|
|
586
|
-
}
|
|
607
|
+
};
|
|
608
|
+
if (acknowledgedOperationIdentities.length
|
|
609
|
+
- persistedAcknowledgementCount
|
|
610
|
+
>= NON_APPLYING_ACKNOWLEDGEMENT_CHECKPOINT_INTERVAL) {
|
|
611
|
+
persist(checkpoint);
|
|
612
|
+
persistedAcknowledgementCount = acknowledgedOperationIdentities.length;
|
|
613
|
+
}
|
|
587
614
|
},
|
|
588
615
|
});
|
|
589
616
|
onResult?.(result);
|
|
590
617
|
if (!result.success) {
|
|
591
618
|
if (result.batchProgress && checkpoint.pendingV2Push) {
|
|
592
|
-
|
|
619
|
+
checkpoint = {
|
|
593
620
|
...checkpoint,
|
|
594
621
|
pendingV2Push: {
|
|
595
622
|
...checkpoint.pendingV2Push,
|
|
596
623
|
lastBatchProgress: structuredClone(result.batchProgress),
|
|
597
624
|
},
|
|
598
|
-
}
|
|
625
|
+
};
|
|
599
626
|
}
|
|
600
627
|
const message = pushFailureMessage(result);
|
|
601
628
|
if (isAuthFailure(result)) {
|
|
@@ -678,6 +705,8 @@ export function createWorkspaceSyncEngineServerRunnerOperations(options) {
|
|
|
678
705
|
? { durableRevision: entry.durableRevision }
|
|
679
706
|
: {}),
|
|
680
707
|
})), {
|
|
708
|
+
repairMode: attemptOptions.repairMode,
|
|
709
|
+
excludedProjections: result.effectiveV2ExcludedProjections,
|
|
681
710
|
...(completionGroup ? { completionGroup } : {}),
|
|
682
711
|
commitServerCheckpoint: () => {
|
|
683
712
|
const persisted = input.persistCheckpoint(nextCheckpoint, {
|
|
@@ -697,7 +726,9 @@ export function createWorkspaceSyncEngineServerRunnerOperations(options) {
|
|
|
697
726
|
}
|
|
698
727
|
if (!applyResult.ok || !commitState.checkpoint) {
|
|
699
728
|
await abandonPushReceipts('WORKSPACE_SYNC_V2_PUSH_AUTHORITATIVE_APPLY_FAILED');
|
|
700
|
-
throw new WorkspaceSyncEngineServerRunnerOperationError(
|
|
729
|
+
throw new WorkspaceSyncEngineServerRunnerOperationError(!applyResult.ok
|
|
730
|
+
? authoritativeV2ApplyFailureMessage(applyResult)
|
|
731
|
+
: 'Authoritative V2 push echo did not commit the server checkpoint.', 'SYNC_RUNNER_PUSH_AUTHORITATIVE_APPLY_FAILED');
|
|
701
732
|
}
|
|
702
733
|
const committedCheckpoint = commitState.checkpoint;
|
|
703
734
|
if (!committedCheckpoint) {
|
|
@@ -884,8 +915,7 @@ export function createWorkspaceSyncEngineServerRunnerOperations(options) {
|
|
|
884
915
|
}
|
|
885
916
|
if (localRecovery.kind === 'recovery'
|
|
886
917
|
&& localRecovery.state === 'replay_queued') {
|
|
887
|
-
|
|
888
|
-
localRecovery = await localServices.prepareOutboxRecovery(workspaceId);
|
|
918
|
+
localRecovery = await prepareAndDrainRecoveryReplay(localRecovery);
|
|
889
919
|
}
|
|
890
920
|
if (localRecovery.kind === 'recovery'
|
|
891
921
|
&& localRecovery.state === 'replay_queued') {
|
|
@@ -912,7 +942,10 @@ export function createWorkspaceSyncEngineServerRunnerOperations(options) {
|
|
|
912
942
|
const continuationInvalid = continuation !== null && (continuation.coordinatorGeneration !== input.coordinatorGeneration
|
|
913
943
|
|| continuation.ownershipFingerprint !== ownershipFingerprint
|
|
914
944
|
|| (recoveryBatchId !== null
|
|
915
|
-
&& continuation.recoveryBatchId !== recoveryBatchId)
|
|
945
|
+
&& continuation.recoveryBatchId !== recoveryBatchId)
|
|
946
|
+
|| (recoveryBatchId !== null
|
|
947
|
+
&& continuation.kind !== 'v3-combined'
|
|
948
|
+
&& continuation.kind !== 'v2-reconcile'));
|
|
916
949
|
if (continuationInvalid || (checkpoint.repairContinuation !== null && !continuation)) {
|
|
917
950
|
if (checkpoint.pendingV2Push) {
|
|
918
951
|
await push({
|
|
@@ -1030,6 +1063,13 @@ export function createWorkspaceSyncEngineServerRunnerOperations(options) {
|
|
|
1030
1063
|
}
|
|
1031
1064
|
continuation = checkpoint.repairContinuation;
|
|
1032
1065
|
}
|
|
1066
|
+
if (continuation.recoveryBatchId) {
|
|
1067
|
+
const postPullRecovery = await prepareAndDrainRecoveryReplay();
|
|
1068
|
+
if (postPullRecovery.kind === 'recovery'
|
|
1069
|
+
|| Number(options.inspectV3Outbox?.().activeDepth || 0) > 0) {
|
|
1070
|
+
fail('repair', 'Durable recovery replay must settle before authoritative repair continues.', 'SYNC_RUNNER_RECOVERY_REPLAY_PENDING', RETRY_MAX_DELAY_MS);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1033
1073
|
let pushResult = null;
|
|
1034
1074
|
if (continuation.kind === 'final-push') {
|
|
1035
1075
|
pushResult = await push({
|