@taskforcehq/taskforce 0.3.304 → 0.3.306
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/README.md +140 -247
- package/dist/Taskforce.module.css +295 -89
- package/dist/TaskforceCore.js +378 -111
- package/dist/TaskforceCore.test.js +1221 -107
- package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
- package/dist/assets/fonts/Noir_medium.woff2 +0 -0
- package/dist/assets/fonts/Noir_regular.woff2 +0 -0
- package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
- package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
- package/dist/compat/workspaceSyncCompat.js +52 -2
- package/dist/compat/workspaceSyncCompat.test.js +38 -1
- package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
- package/dist/components/context/ContextAttachmentManager.js +549 -0
- package/dist/components/features/AgentsModule.d.ts +10 -1
- package/dist/components/features/AgentsModule.js +260 -15
- package/dist/components/features/AgentsModule.test.js +255 -5
- package/dist/components/features/DocumentIndex.d.ts +1 -1
- package/dist/components/features/DocumentIndex.js +1 -1
- package/dist/components/features/DocumentViewer.d.ts +6 -2
- package/dist/components/features/DocumentViewer.js +90 -10
- package/dist/components/features/DocumentWorkspace.d.ts +5 -22
- package/dist/components/features/DocumentWorkspace.js +12 -155
- package/dist/components/features/DocumentWorkspace.test.js +226 -6
- package/dist/components/features/TaskSettings.js +29 -3
- package/dist/components/features/TaskSettings.test.js +42 -1
- package/dist/components/features/documentWorkspaceModel.d.ts +24 -0
- package/dist/components/features/documentWorkspaceModel.js +57 -0
- package/dist/components/features/useDocumentWorkspaceController.d.ts +34 -0
- package/dist/components/features/useDocumentWorkspaceController.js +113 -0
- package/dist/components/task/TaskCard.js +19 -28
- package/dist/components/task/TaskCard.test.js +38 -3
- package/dist/components/task/TaskContextUpload.js +4 -526
- package/dist/components/task/TaskForm.d.ts +8 -8
- package/dist/components/task/TaskForm.js +362 -230
- package/dist/components/task/TaskForm.test.js +558 -381
- package/dist/components/task/TaskKanban.d.ts +2 -1
- package/dist/components/task/TaskKanban.js +96 -14
- package/dist/components/task/TaskKanban.test.js +36 -0
- package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
- package/dist/components/ui/EditableAvatarButton.js +18 -0
- package/dist/components/views/PlanComparisonPage.d.ts +9 -1
- package/dist/components/views/PlanComparisonPage.js +70 -17
- package/dist/components/views/PlanComparisonPage.test.js +289 -41
- package/dist/components/views/PlansPage.js +239 -106
- package/dist/components/views/PlansPage.test.js +593 -42
- package/dist/components/views/StandaloneLayout.js +310 -554
- package/dist/components/views/panels/FilterToolbar.test.js +6 -4
- package/dist/components/views/panels/PlanningDrawer.d.ts +8 -1
- package/dist/components/views/panels/PlanningDrawer.js +18 -11
- package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
- package/dist/components/views/panels/PlanningDrawer.test.js +141 -0
- package/dist/components/views/planningScope.d.ts +20 -0
- package/dist/components/views/planningScope.js +25 -0
- package/dist/components/views/planningScope.test.d.ts +1 -0
- package/dist/components/views/planningScope.test.js +60 -0
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +27 -14
- package/dist/components/views/standalone/modals/AccountHubModal.js +87 -15
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +42 -2
- package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
- package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
- package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
- package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
- package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
- package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
- package/dist/config/envSchema.js +1 -1
- package/dist/core/AiProfileService.d.ts +45 -3
- package/dist/core/AiProfileService.js +774 -63
- package/dist/core/AiProfileService.test.js +22 -2
- package/dist/core/AiProfiles.test.js +761 -2
- package/dist/core/AttachmentLinkService.js +57 -15
- package/dist/core/AuthIdentityService.d.ts +107 -0
- package/dist/core/AuthIdentityService.js +284 -0
- package/dist/core/AuthTokenService.d.ts +4 -0
- package/dist/core/AuthTokenService.js +34 -6
- package/dist/core/AuthTokenService.test.d.ts +1 -0
- package/dist/core/AuthTokenService.test.js +78 -0
- package/dist/core/EntitlementsPolicy.test.js +77 -15
- package/dist/core/GlobalSettingsService.js +115 -6
- package/dist/core/PlanEntitlementService.d.ts +30 -3
- package/dist/core/PlanEntitlementService.js +454 -93
- package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
- package/dist/core/PlanFeatureCatalog.test.js +84 -0
- package/dist/core/PlanVersionPolicy.test.js +54 -28
- package/dist/core/PlanningEntities.test.js +52 -0
- package/dist/core/SignupMonetizationSettings.test.js +241 -44
- package/dist/core/SyncReconciliationService.js +2 -65
- package/dist/core/SystemAdmin.test.js +244 -88
- package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
- package/dist/core/TaskTaxonomyValidation.test.js +53 -0
- package/dist/core/Taskforce.d.ts +228 -14
- package/dist/core/Taskforce.js +814 -201
- package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
- package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
- package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
- package/dist/core/WorkspaceLifecycleService.js +0 -5
- package/dist/core/WorkspacePermissions.test.js +25 -1
- package/dist/core/WorkspacePlanMode.test.js +128 -27
- package/dist/core/shared.d.ts +3 -1
- package/dist/core/shared.js +16 -1
- package/dist/core/types.d.ts +68 -2
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -1
- package/dist/hooks/sync/auth.js +3 -1
- package/dist/hooks/sync/bootstrap.js +2 -0
- package/dist/hooks/sync/controlPlane.d.ts +37 -0
- package/dist/hooks/sync/controlPlane.js +78 -0
- package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
- package/dist/hooks/sync/controlPlane.test.js +121 -0
- package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
- package/dist/hooks/sync/lifecyclePolicy.js +93 -0
- package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
- package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +10 -1
- package/dist/hooks/sync/orchestratorShared.js +68 -47
- package/dist/hooks/sync/orchestratorShared.test.js +50 -1
- package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
- package/dist/hooks/sync/pullLifecycle.js +24 -0
- package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
- package/dist/hooks/sync/pullLifecycle.test.js +80 -0
- package/dist/hooks/sync/recovery.d.ts +16 -2
- package/dist/hooks/sync/recovery.js +171 -53
- package/dist/hooks/sync/recovery.test.d.ts +1 -0
- package/dist/hooks/sync/recovery.test.js +292 -0
- package/dist/hooks/sync/transfers.d.ts +16 -3
- package/dist/hooks/sync/transfers.js +186 -65
- package/dist/hooks/sync/transfers.test.d.ts +1 -0
- package/dist/hooks/sync/transfers.test.js +396 -0
- package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
- package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
- package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
- package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
- package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
- package/dist/hooks/sync/useSyncStatusActions.js +88 -0
- package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
- package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
- package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
- package/dist/hooks/sync/useSyncStatusControls.js +60 -0
- package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
- package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +16 -6
- package/dist/hooks/useSyncOrchestrator.js +424 -175
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +487 -10
- package/dist/hooks/useTaskData.js +8 -3
- package/dist/hooks/useTaskData.test.js +45 -0
- package/dist/hooks/useTaskMutations.d.ts +2 -1
- package/dist/hooks/useTaskMutations.js +12 -1
- package/dist/hooks/useTaskMutations.test.js +30 -1
- package/dist/hooks/useTaskforce.d.ts +65 -4
- package/dist/hooks/useTaskforce.js +564 -243
- package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
- package/dist/hooks/useTaskforce.runtime-routing.test.js +152 -0
- package/dist/hooks/useTaskforce.sync-behavior.test.js +2454 -208
- package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
- package/dist/hooks/useWorkspaceSyncController.js +17 -2
- package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +316 -35
- package/dist/localization/locales/en-US.d.ts +0 -1
- package/dist/localization/locales/en-US.js +0 -1
- package/dist/localization/locales/es-419.js +0 -1
- package/dist/localization/locales/pt-BR.js +0 -1
- package/dist/mcp/adminWorkspaceRegistrar.d.ts +2 -0
- package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
- package/dist/mcp/collaborationRegistrar.d.ts +2 -0
- package/dist/mcp/collaborationRegistrar.js +71 -0
- package/dist/mcp/documentAssetRegistrar.d.ts +2 -0
- package/dist/mcp/documentAssetRegistrar.js +346 -0
- package/dist/mcp/runtime.d.ts +2 -0
- package/dist/mcp/runtime.js +2433 -3534
- package/dist/mcp/runtime.test.js +440 -2
- package/dist/mcp/taskPlanningRegistrar.d.ts +2 -0
- package/dist/mcp/taskPlanningRegistrar.js +418 -0
- package/dist/mcp/toolCatalog.d.ts +35 -0
- package/dist/mcp/toolCatalog.js +3 -0
- package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
- package/dist/migrations/taskSchemaMigrations.js +171 -61
- package/dist/migrations/taskSchemaMigrations.test.js +15 -0
- package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
- package/dist/resources/templates/workflows/collaborate.yaml +1 -1
- package/dist/resources/templates/workflows/evaluate.yaml +2 -2
- package/dist/resources/templates/workflows/plan.yaml +1 -1
- package/dist/resources/templates/workflows/review.yaml +2 -2
- package/dist/server/annotatedAttachmentsRoutes.test.js +3 -3
- package/dist/server/auth/providers/apple.d.ts +33 -0
- package/dist/server/auth/providers/apple.js +82 -0
- package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
- package/dist/server/auth/providers/appleClientSecret.js +65 -0
- package/dist/server/auth/providers/github.d.ts +26 -0
- package/dist/server/auth/providers/github.js +86 -0
- package/dist/server/auth/providers/google.d.ts +21 -0
- package/dist/server/auth/providers/google.js +56 -0
- package/dist/server/auth/providers/types.d.ts +21 -0
- package/dist/server/auth/providers/types.js +1 -0
- package/dist/server/auth.d.ts +2 -0
- package/dist/server/auth.js +6 -3
- package/dist/server/documentReviewRoutes.test.js +36 -3
- package/dist/server/index.cookieProxy.test.js +1 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.js +120 -9
- package/dist/server/index.rateLimit.test.js +3 -0
- package/dist/server/index.test.js +106 -3
- package/dist/server/routes/admin.d.ts +4 -5
- package/dist/server/routes/admin.js +410 -80
- package/dist/server/routes/annotatedAttachments.d.ts +1 -1
- package/dist/server/routes/annotatedAttachments.js +1 -1
- package/dist/server/routes/auth.d.ts +16 -6
- package/dist/server/routes/auth.js +1015 -118
- package/dist/server/routes/authSupport.d.ts +30 -0
- package/dist/server/routes/authSupport.js +81 -0
- package/dist/server/routes/billing.d.ts +1 -1
- package/dist/server/routes/billing.js +276 -34
- package/dist/server/routes/billing.test.js +760 -52
- package/dist/server/routes/documentReviews.d.ts +1 -1
- package/dist/server/routes/documentReviews.js +1 -1
- package/dist/server/routes/documents.d.ts +5 -2
- package/dist/server/routes/documents.js +242 -74
- package/dist/server/routes/primitives.d.ts +11 -0
- package/dist/server/routes/primitives.js +73 -0
- package/dist/server/routes/resources.d.ts +1 -1
- package/dist/server/routes/resources.js +1 -1
- package/dist/server/routes/shared.d.ts +29 -3
- package/dist/server/routes/shared.js +69 -4
- package/dist/server/routes/sync.d.ts +1 -1
- package/dist/server/routes/sync.integration.test.js +437 -39
- package/dist/server/routes/sync.js +62 -606
- package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
- package/dist/server/routes/syncAuxRoutes.js +91 -0
- package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
- package/dist/server/routes/syncAuxRoutes.test.js +158 -0
- package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
- package/dist/server/routes/syncPullApplyRoutes.js +204 -0
- package/dist/server/routes/syncPushRoutes.d.ts +24 -0
- package/dist/server/routes/syncPushRoutes.js +212 -0
- package/dist/server/routes/syncRouteGuards.d.ts +36 -0
- package/dist/server/routes/syncRouteGuards.js +67 -0
- package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
- package/dist/server/routes/syncRouteGuards.test.js +94 -0
- package/dist/server/routes/syncRouteTypes.d.ts +13 -0
- package/dist/server/routes/syncRouteTypes.js +1 -0
- package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
- package/dist/server/routes/syncSnapshotRoutes.js +180 -0
- package/dist/server/routes/tasks.d.ts +1 -1
- package/dist/server/routes/tasks.js +44 -2
- package/dist/server/routes/workspaces.d.ts +1 -1
- package/dist/server/routes/workspaces.js +37 -6
- package/dist/server/routes.d.ts +2 -9
- package/dist/server/routes.js +136 -165
- package/dist/server/routes.test.js +2457 -162
- package/dist/services/workflowExportSnapshots.test.js +2 -0
- package/dist/shared/aiProfileSeatScope.d.ts +5 -0
- package/dist/shared/aiProfileSeatScope.js +21 -0
- package/dist/shared/passwordPolicy.d.ts +13 -0
- package/dist/shared/passwordPolicy.js +84 -0
- package/dist/shared/passwordPolicy.test.d.ts +1 -0
- package/dist/shared/passwordPolicy.test.js +34 -0
- package/dist/storage/workspaceAssetStore.d.ts +15 -3
- package/dist/storage/workspaceAssetStore.js +132 -44
- package/dist/storage/workspaceAssetStore.test.js +93 -6
- package/dist/styles/fonts.css +70 -0
- package/dist/sync/collaborationSyncPayload.d.ts +6 -1
- package/dist/sync/collaborationSyncPayload.js +17 -11
- package/dist/sync/collaborationSyncPayload.test.js +13 -1
- package/dist/sync/contentSyncState.d.ts +4 -8
- package/dist/sync/contentSyncState.js +33 -77
- package/dist/sync/syncApplyHandlers.d.ts +7 -0
- package/dist/sync/syncApplyHandlers.js +28 -13
- package/dist/sync/syncApplyHandlers.test.js +72 -3
- package/dist/sync/syncFieldSemantics.d.ts +12 -0
- package/dist/sync/syncFieldSemantics.js +55 -0
- package/dist/sync/syncResourceAdapters.d.ts +44 -0
- package/dist/sync/syncResourceAdapters.js +77 -0
- package/dist/sync/syncService.d.ts +6 -0
- package/dist/sync/syncService.js +60 -0
- package/dist/sync/syncService.test.js +71 -3
- package/dist/sync/taskSyncChangeKey.d.ts +2 -0
- package/dist/sync/taskSyncChangeKey.js +143 -0
- package/dist/sync/taskSyncPayload.js +3 -8
- package/dist/sync/workspacePullFeed.js +4 -3
- package/dist/sync/workspacePullFeed.test.js +43 -0
- package/dist/sync/workspaceRepair.js +5 -2
- package/dist/sync/workspaceSyncModel.d.ts +48 -0
- package/dist/sync/workspaceSyncModel.js +195 -31
- package/dist/sync/workspaceSyncModel.test.js +325 -12
- package/dist/sync/workspaceSyncState.d.ts +19 -0
- package/dist/sync/workspaceSyncState.js +3 -1
- package/dist/sync/workspaceSyncSurface.js +3 -8
- package/dist/types.d.ts +17 -0
- package/dist/ui/assets/AgentsModule-DrLawwNl.js +1 -0
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BlS-cqnl.js → AnnotatedAttachmentWorkspace-Z9_whf7F.js} +1 -1
- package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
- package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +252 -0
- package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +1 -0
- package/dist/ui/assets/{InitiativesModule-BjR6iBf9.js → InitiativesModule-9E6ParrY.js} +1 -1
- package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
- package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
- package/dist/ui/assets/PlansPage-CvfnMiWq.css +1 -0
- package/dist/ui/assets/PlansPage-DRXscYxH.js +1 -0
- package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
- package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
- package/dist/ui/assets/TaskSettings-DZX4jk7e.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-DnFqdUME.js → WorkflowsModule-BA7jcEpH.js} +1 -1
- package/dist/ui/assets/index-9eT8e0Lu.css +1 -0
- package/dist/ui/assets/index-C6k75jr8.js +6 -0
- package/dist/ui/assets/{vendor-icons-I7ZwG1z_.js → vendor-icons-DuEd_65c.js} +1 -1
- package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
- package/dist/ui/fonts/Noir_medium.otf +0 -0
- package/dist/ui/fonts/Noir_medium.woff2 +0 -0
- package/dist/ui/fonts/Noir_regular.otf +0 -0
- package/dist/ui/fonts/Noir_regular.woff2 +0 -0
- package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
- package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
- package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
- package/dist/ui/index.html +3 -3
- package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
- package/dist/utils/assignees.d.ts +2 -0
- package/dist/utils/assignees.js +2 -0
- package/dist/utils/avatarUpload.d.ts +27 -0
- package/dist/utils/avatarUpload.js +132 -0
- package/dist/utils/avatarUpload.test.d.ts +1 -0
- package/dist/utils/avatarUpload.test.js +40 -0
- package/dist/utils/bootstrapTrace.d.ts +7 -0
- package/dist/utils/bootstrapTrace.js +29 -0
- package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
- package/dist/utils/commercialLifecyclePresentation.js +199 -0
- package/dist/utils/contextAssetEvents.d.ts +2 -0
- package/dist/utils/syncEventDetails.d.ts +3 -0
- package/dist/utils/syncEventDetails.js +64 -0
- package/dist/utils/syncEventDetails.test.d.ts +1 -0
- package/dist/utils/syncEventDetails.test.js +23 -0
- package/dist/utils/syncEventPresentation.d.ts +15 -0
- package/dist/utils/syncEventPresentation.js +129 -0
- package/dist/utils/syncEventPresentation.test.d.ts +1 -0
- package/dist/utils/syncEventPresentation.test.js +64 -0
- package/dist/utils/syncStatusPresentation.d.ts +72 -0
- package/dist/utils/syncStatusPresentation.js +217 -0
- package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
- package/dist/utils/syncStatusPresentation.test.js +164 -0
- package/dist/utils/taskActivity.d.ts +2 -0
- package/dist/utils/taskActivity.js +228 -22
- package/dist/utils/taskActivity.test.js +119 -8
- package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
- package/dist/utils/workspaceSyncPresentation.js +68 -2
- package/dist/utils/workspaceSyncPresentation.test.js +36 -1
- package/package.json +10 -2
- package/scripts/check-cloud-mcp.mjs +83 -0
- package/scripts/export-sqlite-to-postgres.mjs +7 -9
- package/scripts/playwright-auth.sh +5 -1
- package/scripts/run-billing-performance-smoke.sh +24 -0
- package/scripts/run-e2e-realtime.sh +17 -3
- package/scripts/run-smoke.sh +17 -5
- package/scripts/run-soak.sh +17 -5
- package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
- package/src/resources/templates/workflows/collaborate.yaml +1 -1
- package/src/resources/templates/workflows/evaluate.yaml +2 -2
- package/src/resources/templates/workflows/plan.yaml +1 -1
- package/src/resources/templates/workflows/review.yaml +2 -2
- package/dist/ui/assets/AgentsModule-BLWVbuKP.js +0 -1
- package/dist/ui/assets/DocumentWorkspace-BH_6DF6x.js +0 -250
- package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
- package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
- package/dist/ui/assets/PlansPage-CSDQ4sLa.js +0 -1
- package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
- package/dist/ui/assets/TaskSettings-CEDy34Jo.js +0 -9
- package/dist/ui/assets/index-DXUdtH1B.js +0 -6
- package/dist/ui/assets/index-Ii0B0rTO.css +0 -1
- package/scripts/migrate-planning-model.ts +0 -233
|
@@ -28,55 +28,16 @@ import { useRealtimeSync } from './useRealtimeSync';
|
|
|
28
28
|
import { TASKFORCE_CONTEXT_ASSETS_MUTATED_EVENT } from '../utils/contextAssetEvents';
|
|
29
29
|
import { setLocale as setAppLocale } from '../localization';
|
|
30
30
|
import { normalizeTaskFromApi } from '../utils/taskNormalization.js';
|
|
31
|
+
import { normalizeSyncEventDetails } from '../utils/syncEventDetails';
|
|
31
32
|
import { createSyncAuthCoordinator } from './sync/auth';
|
|
32
33
|
import { createBootstrapTimeoutMonitor, syncBootstrapPhaseTracking } from './sync/bootstrap';
|
|
33
|
-
import {
|
|
34
|
+
import { resolvePullSyncReadiness, resolvePushSyncReadiness, resolveRetrySyncReadiness } from './sync/controlPlane';
|
|
35
|
+
import { resolveWorkspaceAutomaticPullPolicy, resolveWorkspaceSyncPresentation, shouldRunWorkspaceStartupRepair, shouldScheduleWorkspaceReactivePush, shouldStartWorkspacePeriodicFullReconcile, shouldStartWorkspacePushWatchdog, } from './sync/lifecyclePolicy';
|
|
36
|
+
import { buildPersistedSyncWatermarksSnapshot, buildTaxonomyStateFingerprint, flattenDocumentReviewComments, flattenTaskEvents, isValidWorkspaceKey, logSyncDebug, normalizeWorkspaceSyncAiProfileSnapshotEntry, parsePersistedSyncWatermarksSnapshot, readPersistedSyncWatermarks, readUserGlobalSyncMeta, savePersistedSyncWatermarks, WORKSPACE_CONCURRENT_WINDOW_MESSAGE, WORKSPACE_FULL_RECONCILE_INTERVAL_MS, writeUserGlobalSyncMeta } from './sync/orchestratorShared';
|
|
34
37
|
import { createRealtimeSyncBridge } from './sync/realtime';
|
|
35
38
|
import { createSyncRecoveryCoordinator } from './sync/recovery';
|
|
36
39
|
import { createWorkspaceTransferCoordinator } from './sync/transfers';
|
|
37
|
-
|
|
38
|
-
function summarizeSyncPhase(enabled, phase, busy, retryAt, lastErrorMessage) {
|
|
39
|
-
if (!enabled)
|
|
40
|
-
return 'Sync is turned off for this workspace.';
|
|
41
|
-
if (phase === 'provision-local') {
|
|
42
|
-
return busy
|
|
43
|
-
? 'Uploading this workspace to cloud for the first time.'
|
|
44
|
-
: 'Preparing the first upload to cloud.';
|
|
45
|
-
}
|
|
46
|
-
if (phase === 'attach-cloud') {
|
|
47
|
-
return busy
|
|
48
|
-
? 'Pulling the cloud workspace into this device.'
|
|
49
|
-
: 'Waiting for the first cloud pull into this device.';
|
|
50
|
-
}
|
|
51
|
-
if (retryAt) {
|
|
52
|
-
return 'Sync hit a temporary problem and will retry automatically.';
|
|
53
|
-
}
|
|
54
|
-
if (lastErrorMessage) {
|
|
55
|
-
return 'Sync needs attention before it can continue.';
|
|
56
|
-
}
|
|
57
|
-
if (busy) {
|
|
58
|
-
return 'Sync is currently running.';
|
|
59
|
-
}
|
|
60
|
-
return 'Sync is healthy.';
|
|
61
|
-
}
|
|
62
|
-
function recommendSyncAction(enabled, phase, retryAt, lastErrorMessage) {
|
|
63
|
-
if (!enabled) {
|
|
64
|
-
return 'Turn on sync when you are ready to back up this workspace.';
|
|
65
|
-
}
|
|
66
|
-
if (phase === 'provision-local') {
|
|
67
|
-
return 'Keep this window open while the first upload finishes. If it seems stalled, press Sync.';
|
|
68
|
-
}
|
|
69
|
-
if (phase === 'attach-cloud') {
|
|
70
|
-
return 'Keep this window open for the first cloud pull. If it does not move, press Repair.';
|
|
71
|
-
}
|
|
72
|
-
if (retryAt) {
|
|
73
|
-
return 'Wait for the automatic retry, or press Sync to retry now.';
|
|
74
|
-
}
|
|
75
|
-
if (lastErrorMessage) {
|
|
76
|
-
return 'Press Sync to retry. If the same error keeps returning, press Repair.';
|
|
77
|
-
}
|
|
78
|
-
return 'No action needed.';
|
|
79
|
-
}
|
|
40
|
+
import { aiProfileSyncResourceAdapter, taskSyncResourceAdapter } from '../sync/syncResourceAdapters.js';
|
|
80
41
|
const EMPTY_WORKSPACE_SYNC_REFERENCE_SNAPSHOT_STATUS = {
|
|
81
42
|
documents: false,
|
|
82
43
|
aiProfiles: false,
|
|
@@ -86,7 +47,7 @@ const EMPTY_WORKSPACE_SYNC_REFERENCE_SNAPSHOT_STATUS = {
|
|
|
86
47
|
};
|
|
87
48
|
export { normalizeWorkspaceSyncAiProfileSnapshotEntry };
|
|
88
49
|
export function useSyncOrchestrator(input) {
|
|
89
|
-
const { currentWorkspaceId, cloudAuthConfigured, runtimeMode, authSessionResolved, isAuthenticated, authUserId, projectName, resolveCloudAuthUrl, resolveWebSocketUrl, realtimeSyncEnabled, tasks, archivedTasks, initiatives, workstreams, taxonomies, taxonomyState, setupState, globalTheme, locale, globalWeekStartsOn, themeUseGlobalDefault, setTasks, setArchivedTasks, setAuthBlocked, setIsAuthenticated, checkAuthSession, setGlobalTheme, setCurrentTheme, setSetupState, setLocale, setGlobalWeekStartsOn, fetchPlanningEntities } = input;
|
|
50
|
+
const { currentWorkspaceId, cloudAuthConfigured, runtimeMode, authSessionResolved, isAuthenticated, authUserId, projectName, resolveCloudAuthUrl, resolveWebSocketUrl, realtimeSyncEnabled, tasks, archivedTasks, deletedTasks = [], initiatives, workstreams, taxonomies, taxonomyState, setupState, globalTheme, locale, globalWeekStartsOn, themeUseGlobalDefault, setTasks, setArchivedTasks, setAuthBlocked, setIsAuthenticated, checkAuthSession, setGlobalTheme, setCurrentTheme, setSetupState, setLocale, setGlobalWeekStartsOn, fetchPlanningEntities } = input;
|
|
90
51
|
const taxonomyStateFingerprint = useMemo(() => buildTaxonomyStateFingerprint(taxonomyState), [taxonomyState]);
|
|
91
52
|
// === SECTION: State initialization ===
|
|
92
53
|
const [userGlobalSyncStatus, setUserGlobalSyncStatus] = useState('disconnected');
|
|
@@ -97,6 +58,7 @@ export function useSyncOrchestrator(input) {
|
|
|
97
58
|
const [workspaceLastErrorMessage, setWorkspaceLastErrorMessage] = useState(null);
|
|
98
59
|
const [workspaceCloudSyncEnabled, setWorkspaceCloudSyncEnabled] = useState(false);
|
|
99
60
|
const [workspaceSyncPhase, setWorkspaceSyncPhase] = useState('idle');
|
|
61
|
+
const [workspaceSyncSetupIntent, setWorkspaceSyncSetupIntent] = useState(null);
|
|
100
62
|
const [workspaceSyncBusy, setWorkspaceSyncBusy] = useState(false);
|
|
101
63
|
const [workspaceSyncPendingChanges, setWorkspaceSyncPendingChanges] = useState(0);
|
|
102
64
|
const [workspaceSyncMarkdownDocuments, setWorkspaceSyncMarkdownDocuments] = useState([]);
|
|
@@ -108,6 +70,7 @@ export function useSyncOrchestrator(input) {
|
|
|
108
70
|
const workspaceInitiativesRef = useRef(initiatives || []);
|
|
109
71
|
const workspaceWorkstreamsRef = useRef(workstreams || []);
|
|
110
72
|
const workspaceSyncMarkdownDocumentsRef = useRef([]);
|
|
73
|
+
const workspaceSyncKnownDocumentPathsRef = useRef(new Set());
|
|
111
74
|
const [workspaceSyncAiProfiles, setWorkspaceSyncAiProfiles] = useState([]);
|
|
112
75
|
const [workspaceSyncAiProfilesFingerprint, setWorkspaceSyncAiProfilesFingerprint] = useState('');
|
|
113
76
|
const [workspaceSyncAiProfilesLastFetchAt, setWorkspaceSyncAiProfilesLastFetchAt] = useState(null);
|
|
@@ -118,6 +81,7 @@ export function useSyncOrchestrator(input) {
|
|
|
118
81
|
const [workspaceSyncAssets, setWorkspaceSyncAssets] = useState([]);
|
|
119
82
|
const [workspaceSyncAssetsFingerprint, setWorkspaceSyncAssetsFingerprint] = useState('');
|
|
120
83
|
const workspaceSyncAssetsRef = useRef([]);
|
|
84
|
+
const workspaceSyncKnownAssetPathsRef = useRef(new Set());
|
|
121
85
|
const [workspaceSyncDocumentReviewSessions, setWorkspaceSyncDocumentReviewSessions] = useState([]);
|
|
122
86
|
const [workspaceSyncDocumentReviewSessionsFingerprint, setWorkspaceSyncDocumentReviewSessionsFingerprint] = useState('');
|
|
123
87
|
const workspaceSyncDocumentReviewSessionsRef = useRef([]);
|
|
@@ -136,6 +100,7 @@ export function useSyncOrchestrator(input) {
|
|
|
136
100
|
});
|
|
137
101
|
const userSettingsSyncInFlightRef = useRef(false);
|
|
138
102
|
const userSettingsRetryTimeoutRef = useRef(null);
|
|
103
|
+
const workspaceBaselineResetReasonRef = useRef('missing-baseline');
|
|
139
104
|
const userSettingsRetryAttemptRef = useRef(0);
|
|
140
105
|
const applyingRemoteUserSettingsRef = useRef(false);
|
|
141
106
|
const initialUserSettingsSyncCompletedRef = useRef(new Set());
|
|
@@ -149,6 +114,7 @@ export function useSyncOrchestrator(input) {
|
|
|
149
114
|
const realtimePullDebounceRef = useRef(null);
|
|
150
115
|
const realtimeSuppressedEventIdsRef = useRef(new Set());
|
|
151
116
|
const realtimeSuppressedEventQueueRef = useRef([]);
|
|
117
|
+
const workspacePendingPushReplayRef = useRef(null);
|
|
152
118
|
const retryWorkspaceSyncRef = useRef(() => Promise.resolve(false));
|
|
153
119
|
const syncUserGlobalSettingsRef = useRef(async () => { });
|
|
154
120
|
const bootstrapPhaseStartedAtRef = useRef(null);
|
|
@@ -177,73 +143,6 @@ export function useSyncOrchestrator(input) {
|
|
|
177
143
|
void syncUserGlobalSettingsRef.current({ preferCloudOnFirstSync: false });
|
|
178
144
|
}, delayMs);
|
|
179
145
|
}, [clearUserSettingsRetry]);
|
|
180
|
-
useEffect(() => {
|
|
181
|
-
workspaceSyncMarkdownDocumentsFingerprintRef.current = '';
|
|
182
|
-
workspaceSyncAiProfilesFingerprintRef.current = '';
|
|
183
|
-
workspaceSyncAssetsFingerprintRef.current = '';
|
|
184
|
-
workspaceSyncDocumentReviewSessionsFingerprintRef.current = '';
|
|
185
|
-
workspaceSyncAnnotatedAttachmentSessionsFingerprintRef.current = '';
|
|
186
|
-
workspaceAttachBootstrapBaselineSignatureRef.current = '';
|
|
187
|
-
workspaceCaptureAttachBootstrapBaselineRef.current = false;
|
|
188
|
-
setWorkspaceSyncMarkdownDocuments([]);
|
|
189
|
-
setWorkspaceSyncMarkdownDocumentsFingerprint('');
|
|
190
|
-
workspaceSyncAiProfilesRef.current = [];
|
|
191
|
-
setWorkspaceSyncAiProfiles([]);
|
|
192
|
-
setWorkspaceSyncAiProfilesFingerprint('');
|
|
193
|
-
setWorkspaceSyncAssets([]);
|
|
194
|
-
setWorkspaceSyncAssetsFingerprint('');
|
|
195
|
-
workspaceSyncDocumentReviewSessionsRef.current = [];
|
|
196
|
-
workspaceSyncDocumentReviewCommentsRef.current = [];
|
|
197
|
-
setWorkspaceSyncDocumentReviewSessions([]);
|
|
198
|
-
setWorkspaceSyncDocumentReviewSessionsFingerprint('');
|
|
199
|
-
workspaceSyncAnnotatedAttachmentSessionsRef.current = [];
|
|
200
|
-
setWorkspaceSyncAnnotatedAttachmentSessions([]);
|
|
201
|
-
setWorkspaceSyncAnnotatedAttachmentSessionsFingerprint('');
|
|
202
|
-
setWorkspaceSyncReferenceSnapshotStatus(EMPTY_WORKSPACE_SYNC_REFERENCE_SNAPSHOT_STATUS);
|
|
203
|
-
setWorkspaceLastErrorMessage(null);
|
|
204
|
-
setWorkspaceLastErrorAt(null);
|
|
205
|
-
setWorkspaceSyncPendingChanges(0);
|
|
206
|
-
realtimeSuppressedEventIdsRef.current = new Set();
|
|
207
|
-
realtimeSuppressedEventQueueRef.current = [];
|
|
208
|
-
// Reset watermarks for the new workspace, then restore from localStorage if recent
|
|
209
|
-
// enough. This prevents a full resync on every page reload while still catching any
|
|
210
|
-
// changes that happened while the app was closed.
|
|
211
|
-
workspaceLastPushedWatermarksRef.current = new Map();
|
|
212
|
-
workspaceLastPushedInitiativeIdsRef.current = new Set();
|
|
213
|
-
workspaceLastPushedInitiativeWatermarksRef.current = new Map();
|
|
214
|
-
workspaceLastPushedWorkstreamIdsRef.current = new Set();
|
|
215
|
-
workspaceLastPushedWorkstreamWatermarksRef.current = new Map();
|
|
216
|
-
workspaceLastPushedAiProfileIdsRef.current = new Set();
|
|
217
|
-
workspaceLastPushedAiProfileWatermarksRef.current = new Map();
|
|
218
|
-
workspaceLastPushedDocumentPathsRef.current = new Set();
|
|
219
|
-
workspaceLastPushedDocumentWatermarksRef.current = new Map();
|
|
220
|
-
workspaceLastPushedAssetPathsRef.current = new Set();
|
|
221
|
-
workspaceLastPushedAssetWatermarksRef.current = new Map();
|
|
222
|
-
workspaceLastPushedDocumentReviewSessionIdsRef.current = new Set();
|
|
223
|
-
workspaceLastPushedDocumentReviewSessionWatermarksRef.current = new Map();
|
|
224
|
-
workspaceLastPushedDocumentReviewCommentIdsRef.current = new Set();
|
|
225
|
-
workspaceLastPushedDocumentReviewCommentWatermarksRef.current = new Map();
|
|
226
|
-
workspaceLastPushedAnnotatedAttachmentSessionIdsRef.current = new Set();
|
|
227
|
-
workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef.current = new Map();
|
|
228
|
-
workspaceLastPushedTaskEventWatermarksRef.current = new Map();
|
|
229
|
-
workspaceLastPushedAnnotatedAttachmentSessionIdsRef.current = new Set();
|
|
230
|
-
workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef.current = new Map();
|
|
231
|
-
workspaceLastPushedTaskEventWatermarksRef.current = new Map();
|
|
232
|
-
workspaceStartupFullReconcileRanRef.current = false;
|
|
233
|
-
workspaceFullReconcileInFlightRef.current = false;
|
|
234
|
-
const persisted = readPersistedSyncWatermarks(currentWorkspaceId, taxonomyStateFingerprint);
|
|
235
|
-
if (persisted) {
|
|
236
|
-
workspaceLastPushedWatermarksRef.current = persisted.taskWatermarks;
|
|
237
|
-
workspaceLastPushedInitiativeWatermarksRef.current = persisted.initiativeWatermarks;
|
|
238
|
-
workspaceLastPushedWorkstreamWatermarksRef.current = persisted.workstreamWatermarks;
|
|
239
|
-
workspaceLastPushedDocumentWatermarksRef.current = persisted.documentWatermarks;
|
|
240
|
-
workspaceLastPushedAssetWatermarksRef.current = persisted.assetWatermarks;
|
|
241
|
-
workspaceLastPushedDocumentReviewSessionWatermarksRef.current = persisted.documentReviewSessionWatermarks;
|
|
242
|
-
workspaceLastPushedDocumentReviewCommentWatermarksRef.current = persisted.documentReviewCommentWatermarks;
|
|
243
|
-
workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef.current = persisted.annotatedAttachmentSessionWatermarks;
|
|
244
|
-
workspaceLastPushedTaskEventWatermarksRef.current = persisted.taskEventWatermarks;
|
|
245
|
-
}
|
|
246
|
-
}, [currentWorkspaceId, taxonomyStateFingerprint]);
|
|
247
146
|
const markWorkspaceSyncReferenceSnapshotReady = useCallback((key) => {
|
|
248
147
|
setWorkspaceSyncReferenceSnapshotStatus((current) => (current[key]
|
|
249
148
|
? current
|
|
@@ -253,21 +152,34 @@ export function useSyncOrchestrator(input) {
|
|
|
253
152
|
}));
|
|
254
153
|
}, []);
|
|
255
154
|
const workspaceSyncReferenceSnapshotsReady = Object.values(workspaceSyncReferenceSnapshotStatus).every(Boolean);
|
|
256
|
-
const { workspaceRetryAt, isWorkspaceRetryPending, workspacePushInFlightRef, workspacePullInFlightRef, workspaceLastPushedSignatureRef, workspacePendingSignatureRef, workspaceLastPushedTaskIdsRef, workspaceDeletedTaskIdsRef, workspacePullCursorRef, clearWorkspaceRetry, scheduleWorkspaceRetry, persistWorkspaceSyncPatch, applyWorkspaceSyncPatchLocally, loadWorkspaceSyncState, applyWorkspaceSyncStateSnapshot, saveWorkspaceCloudSyncSettings: saveWorkspaceCloudSyncSettingsBase, acquireWorkspaceSyncLease, releaseWorkspaceSyncLease, forceClearWorkspaceSyncLease } = useWorkspaceSyncController({
|
|
155
|
+
const { workspaceRetryAt, isWorkspaceRetryPending, workspacePushInFlightRef, workspacePullInFlightRef, workspaceLastPushedSignatureRef, workspacePendingSignatureRef, workspaceLastPushedTaskIdsRef, workspaceDeletedTaskIdsRef, workspaceDeletedTaskWatermarksRef, workspacePullCursorRef, clearWorkspaceRetry, scheduleWorkspaceRetry, persistWorkspaceSyncPatch, applyWorkspaceSyncPatchLocally, readWorkspaceSyncStateSnapshot, loadWorkspaceSyncState, applyWorkspaceSyncStateSnapshot: applyWorkspaceSyncStateSnapshotBase, saveWorkspaceCloudSyncSettings: saveWorkspaceCloudSyncSettingsBase, acquireWorkspaceSyncLease, releaseWorkspaceSyncLease, forceClearWorkspaceSyncLease } = useWorkspaceSyncController({
|
|
257
156
|
currentWorkspaceId,
|
|
258
157
|
setWorkspaceCloudSyncEnabled,
|
|
259
158
|
setWorkspaceSyncPhase,
|
|
159
|
+
setWorkspaceSyncSetupIntent,
|
|
260
160
|
setWorkspaceLastPullAt,
|
|
261
161
|
setWorkspaceLastPushAt,
|
|
262
162
|
setWorkspaceLastErrorMessage
|
|
263
163
|
});
|
|
164
|
+
const applyWorkspaceSyncStateSnapshot = useCallback((nextState) => {
|
|
165
|
+
if (nextState.phase === 'attach-cloud' || nextState.phase === 'provision-local') {
|
|
166
|
+
lastBootstrapPhaseRef.current = nextState.phase;
|
|
167
|
+
if (bootstrapPhaseStartedAtRef.current === null) {
|
|
168
|
+
bootstrapPhaseStartedAtRef.current = Date.now();
|
|
169
|
+
}
|
|
170
|
+
bootstrapLastProgressAtRef.current = null;
|
|
171
|
+
}
|
|
172
|
+
applyWorkspaceSyncStateSnapshotBase(nextState);
|
|
173
|
+
}, [applyWorkspaceSyncStateSnapshotBase]);
|
|
264
174
|
const workspaceLastPushedWatermarksRef = useRef(new Map());
|
|
175
|
+
const workspaceLastPushedTaskChangeKeysRef = useRef(new Map());
|
|
265
176
|
const workspaceLastPushedInitiativeIdsRef = useRef(new Set());
|
|
266
177
|
const workspaceLastPushedInitiativeWatermarksRef = useRef(new Map());
|
|
267
178
|
const workspaceLastPushedWorkstreamIdsRef = useRef(new Set());
|
|
268
179
|
const workspaceLastPushedWorkstreamWatermarksRef = useRef(new Map());
|
|
269
180
|
const workspaceLastPushedAiProfileIdsRef = useRef(new Set());
|
|
270
181
|
const workspaceLastPushedAiProfileWatermarksRef = useRef(new Map());
|
|
182
|
+
const workspaceLastPushedAiProfileChangeKeysRef = useRef(new Map());
|
|
271
183
|
const workspaceLastPushedDocumentPathsRef = useRef(new Set());
|
|
272
184
|
const workspaceLastPushedDocumentWatermarksRef = useRef(new Map());
|
|
273
185
|
const workspaceLastPushedAssetPathsRef = useRef(new Set());
|
|
@@ -279,6 +191,48 @@ export function useSyncOrchestrator(input) {
|
|
|
279
191
|
const workspaceLastPushedAnnotatedAttachmentSessionIdsRef = useRef(new Set());
|
|
280
192
|
const workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef = useRef(new Map());
|
|
281
193
|
const workspaceLastPushedTaskEventWatermarksRef = useRef(new Map());
|
|
194
|
+
const hydrateWorkspacePushBaselines = useCallback((persisted) => {
|
|
195
|
+
if (!persisted)
|
|
196
|
+
return false;
|
|
197
|
+
workspaceLastPushedTaskIdsRef.current = new Set(persisted.taskIds);
|
|
198
|
+
workspaceLastPushedWatermarksRef.current = persisted.taskWatermarks;
|
|
199
|
+
workspaceLastPushedTaskChangeKeysRef.current = persisted.taskChangeKeys || new Map();
|
|
200
|
+
workspaceLastPushedInitiativeIdsRef.current = new Set(Array.from(persisted.initiativeWatermarks.keys()));
|
|
201
|
+
workspaceLastPushedInitiativeWatermarksRef.current = persisted.initiativeWatermarks;
|
|
202
|
+
workspaceLastPushedWorkstreamIdsRef.current = new Set(Array.from(persisted.workstreamWatermarks.keys()));
|
|
203
|
+
workspaceLastPushedWorkstreamWatermarksRef.current = persisted.workstreamWatermarks;
|
|
204
|
+
workspaceLastPushedAiProfileIdsRef.current = new Set(Array.from(persisted.aiProfileWatermarks.keys()));
|
|
205
|
+
workspaceLastPushedAiProfileWatermarksRef.current = persisted.aiProfileWatermarks;
|
|
206
|
+
workspaceLastPushedAiProfileChangeKeysRef.current = persisted.aiProfileChangeKeys || new Map();
|
|
207
|
+
workspaceLastPushedDocumentPathsRef.current = new Set(Array.from(persisted.documentWatermarks.keys()));
|
|
208
|
+
workspaceLastPushedDocumentWatermarksRef.current = persisted.documentWatermarks;
|
|
209
|
+
workspaceLastPushedAssetPathsRef.current = new Set(Array.from(persisted.assetWatermarks.keys()));
|
|
210
|
+
workspaceLastPushedAssetWatermarksRef.current = persisted.assetWatermarks;
|
|
211
|
+
workspaceLastPushedDocumentReviewSessionIdsRef.current = new Set(Array.from(persisted.documentReviewSessionWatermarks.keys()));
|
|
212
|
+
workspaceLastPushedDocumentReviewSessionWatermarksRef.current = persisted.documentReviewSessionWatermarks;
|
|
213
|
+
workspaceLastPushedDocumentReviewCommentIdsRef.current = new Set(Array.from(persisted.documentReviewCommentWatermarks.keys()));
|
|
214
|
+
workspaceLastPushedDocumentReviewCommentWatermarksRef.current = persisted.documentReviewCommentWatermarks;
|
|
215
|
+
workspaceLastPushedAnnotatedAttachmentSessionIdsRef.current = new Set(Array.from(persisted.annotatedAttachmentSessionWatermarks.keys()));
|
|
216
|
+
workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef.current = persisted.annotatedAttachmentSessionWatermarks;
|
|
217
|
+
workspaceLastPushedTaskEventWatermarksRef.current = persisted.taskEventWatermarks;
|
|
218
|
+
workspaceBaselineResetReasonRef.current = null;
|
|
219
|
+
return true;
|
|
220
|
+
}, [workspaceLastPushedTaskIdsRef]);
|
|
221
|
+
const buildCurrentPersistedWorkspaceSyncWatermarks = useCallback(() => ({
|
|
222
|
+
taskIds: workspaceLastPushedTaskIdsRef.current,
|
|
223
|
+
taskWatermarks: workspaceLastPushedWatermarksRef.current,
|
|
224
|
+
taskChangeKeys: workspaceLastPushedTaskChangeKeysRef.current,
|
|
225
|
+
initiativeWatermarks: workspaceLastPushedInitiativeWatermarksRef.current,
|
|
226
|
+
workstreamWatermarks: workspaceLastPushedWorkstreamWatermarksRef.current,
|
|
227
|
+
aiProfileWatermarks: workspaceLastPushedAiProfileWatermarksRef.current,
|
|
228
|
+
aiProfileChangeKeys: workspaceLastPushedAiProfileChangeKeysRef.current,
|
|
229
|
+
documentWatermarks: workspaceLastPushedDocumentWatermarksRef.current,
|
|
230
|
+
assetWatermarks: workspaceLastPushedAssetWatermarksRef.current,
|
|
231
|
+
documentReviewSessionWatermarks: workspaceLastPushedDocumentReviewSessionWatermarksRef.current,
|
|
232
|
+
documentReviewCommentWatermarks: workspaceLastPushedDocumentReviewCommentWatermarksRef.current,
|
|
233
|
+
annotatedAttachmentSessionWatermarks: workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef.current,
|
|
234
|
+
taskEventWatermarks: workspaceLastPushedTaskEventWatermarksRef.current,
|
|
235
|
+
}), []);
|
|
282
236
|
const workspaceStartupRepairRanRef = useRef(false);
|
|
283
237
|
const workspaceStartupFullReconcileRanRef = useRef(false);
|
|
284
238
|
const workspaceRepairModeRef = useRef(false);
|
|
@@ -286,6 +240,7 @@ export function useSyncOrchestrator(input) {
|
|
|
286
240
|
const workspaceCaptureAttachBootstrapBaselineRef = useRef(false);
|
|
287
241
|
const workspaceDocumentsRefreshIntervalMs = 30_000;
|
|
288
242
|
const workspacePullIntervalMs = realtimeSyncEnabled ? 30_000 : 15_000;
|
|
243
|
+
const workspacePushWatchdogIntervalMs = Math.max(30_000, workspacePullIntervalMs);
|
|
289
244
|
const workspaceLastSuccessfulSyncAt = useMemo(() => {
|
|
290
245
|
const pullMs = workspaceLastPullAt ? Date.parse(workspaceLastPullAt) : NaN;
|
|
291
246
|
const pushMs = workspaceLastPushAt ? Date.parse(workspaceLastPushAt) : NaN;
|
|
@@ -298,36 +253,22 @@ export function useSyncOrchestrator(input) {
|
|
|
298
253
|
return workspaceLastPushAt;
|
|
299
254
|
return null;
|
|
300
255
|
}, [workspaceLastPullAt, workspaceLastPushAt]);
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
return 'attention';
|
|
309
|
-
}
|
|
310
|
-
return 'healthy';
|
|
311
|
-
}, [
|
|
312
|
-
workspaceCloudSyncEnabled,
|
|
313
|
-
workspaceSyncBusy,
|
|
314
|
-
workspaceSyncPhase,
|
|
315
|
-
workspaceRetryAt,
|
|
316
|
-
workspaceLastErrorMessage
|
|
317
|
-
]);
|
|
318
|
-
const workspaceSyncSummary = useMemo(() => summarizeSyncPhase(workspaceCloudSyncEnabled, workspaceSyncPhase, workspaceSyncBusy, workspaceRetryAt, workspaceLastErrorMessage), [
|
|
256
|
+
const workspaceSyncPresentation = useMemo(() => resolveWorkspaceSyncPresentation({
|
|
257
|
+
enabled: workspaceCloudSyncEnabled,
|
|
258
|
+
phase: workspaceSyncPhase,
|
|
259
|
+
busy: workspaceSyncBusy,
|
|
260
|
+
retryAt: workspaceRetryAt,
|
|
261
|
+
lastErrorMessage: workspaceLastErrorMessage
|
|
262
|
+
}), [
|
|
319
263
|
workspaceCloudSyncEnabled,
|
|
320
264
|
workspaceSyncPhase,
|
|
321
265
|
workspaceSyncBusy,
|
|
322
266
|
workspaceRetryAt,
|
|
323
267
|
workspaceLastErrorMessage
|
|
324
268
|
]);
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
workspaceRetryAt,
|
|
329
|
-
workspaceLastErrorMessage
|
|
330
|
-
]);
|
|
269
|
+
const workspaceSyncStatus = workspaceSyncPresentation.status;
|
|
270
|
+
const workspaceSyncSummary = workspaceSyncPresentation.summary;
|
|
271
|
+
const workspaceSyncRecommendedAction = workspaceSyncPresentation.recommendedAction;
|
|
331
272
|
const getLocalUserSyncUpdatedAt = useCallback((userId) => {
|
|
332
273
|
const key = String(userId || '').trim();
|
|
333
274
|
if (!key)
|
|
@@ -518,6 +459,102 @@ export function useSyncOrchestrator(input) {
|
|
|
518
459
|
const persistWorkspaceSyncPatchSafely = useCallback((patch) => {
|
|
519
460
|
void persistWorkspaceSyncPatchBestEffort(patch).catch(() => { });
|
|
520
461
|
}, [persistWorkspaceSyncPatchBestEffort]);
|
|
462
|
+
const persistWorkspaceSyncWatermarksSnapshotBestEffort = useCallback(() => {
|
|
463
|
+
const snapshotData = {
|
|
464
|
+
...buildCurrentPersistedWorkspaceSyncWatermarks(),
|
|
465
|
+
taxonomyStateFingerprint
|
|
466
|
+
};
|
|
467
|
+
const persisted = savePersistedSyncWatermarks(currentWorkspaceId, snapshotData);
|
|
468
|
+
void persistWorkspaceSyncPatchBestEffort({
|
|
469
|
+
pushBaseline: buildPersistedSyncWatermarksSnapshot(snapshotData)
|
|
470
|
+
}).catch(() => { });
|
|
471
|
+
return persisted;
|
|
472
|
+
}, [
|
|
473
|
+
buildCurrentPersistedWorkspaceSyncWatermarks,
|
|
474
|
+
currentWorkspaceId,
|
|
475
|
+
persistWorkspaceSyncPatchBestEffort,
|
|
476
|
+
taxonomyStateFingerprint
|
|
477
|
+
]);
|
|
478
|
+
useEffect(() => {
|
|
479
|
+
workspaceSyncMarkdownDocumentsFingerprintRef.current = '';
|
|
480
|
+
workspaceSyncAiProfilesFingerprintRef.current = '';
|
|
481
|
+
workspaceSyncAssetsFingerprintRef.current = '';
|
|
482
|
+
workspaceSyncDocumentReviewSessionsFingerprintRef.current = '';
|
|
483
|
+
workspaceSyncAnnotatedAttachmentSessionsFingerprintRef.current = '';
|
|
484
|
+
workspaceAttachBootstrapBaselineSignatureRef.current = '';
|
|
485
|
+
workspaceCaptureAttachBootstrapBaselineRef.current = false;
|
|
486
|
+
setWorkspaceSyncMarkdownDocuments([]);
|
|
487
|
+
setWorkspaceSyncMarkdownDocumentsFingerprint('');
|
|
488
|
+
workspaceSyncAiProfilesRef.current = [];
|
|
489
|
+
setWorkspaceSyncAiProfiles([]);
|
|
490
|
+
setWorkspaceSyncAiProfilesFingerprint('');
|
|
491
|
+
setWorkspaceSyncAssets([]);
|
|
492
|
+
setWorkspaceSyncAssetsFingerprint('');
|
|
493
|
+
workspaceSyncDocumentReviewSessionsRef.current = [];
|
|
494
|
+
workspaceSyncDocumentReviewCommentsRef.current = [];
|
|
495
|
+
setWorkspaceSyncDocumentReviewSessions([]);
|
|
496
|
+
setWorkspaceSyncDocumentReviewSessionsFingerprint('');
|
|
497
|
+
workspaceSyncAnnotatedAttachmentSessionsRef.current = [];
|
|
498
|
+
setWorkspaceSyncAnnotatedAttachmentSessions([]);
|
|
499
|
+
setWorkspaceSyncAnnotatedAttachmentSessionsFingerprint('');
|
|
500
|
+
setWorkspaceSyncReferenceSnapshotStatus(EMPTY_WORKSPACE_SYNC_REFERENCE_SNAPSHOT_STATUS);
|
|
501
|
+
setWorkspaceLastErrorMessage(null);
|
|
502
|
+
setWorkspaceLastErrorAt(null);
|
|
503
|
+
setWorkspaceSyncPendingChanges(0);
|
|
504
|
+
realtimeSuppressedEventIdsRef.current = new Set();
|
|
505
|
+
realtimeSuppressedEventQueueRef.current = [];
|
|
506
|
+
// Reset watermarks for the new workspace, then restore from localStorage if recent
|
|
507
|
+
// enough. This prevents a full resync on every page reload while still catching any
|
|
508
|
+
// changes that happened while the app was closed.
|
|
509
|
+
workspaceLastPushedWatermarksRef.current = new Map();
|
|
510
|
+
workspaceLastPushedTaskChangeKeysRef.current = new Map();
|
|
511
|
+
workspaceLastPushedInitiativeIdsRef.current = new Set();
|
|
512
|
+
workspaceLastPushedInitiativeWatermarksRef.current = new Map();
|
|
513
|
+
workspaceLastPushedWorkstreamIdsRef.current = new Set();
|
|
514
|
+
workspaceLastPushedWorkstreamWatermarksRef.current = new Map();
|
|
515
|
+
workspaceLastPushedAiProfileIdsRef.current = new Set();
|
|
516
|
+
workspaceLastPushedAiProfileWatermarksRef.current = new Map();
|
|
517
|
+
workspaceLastPushedAiProfileChangeKeysRef.current = new Map();
|
|
518
|
+
workspaceLastPushedDocumentPathsRef.current = new Set();
|
|
519
|
+
workspaceLastPushedDocumentWatermarksRef.current = new Map();
|
|
520
|
+
workspaceLastPushedAssetPathsRef.current = new Set();
|
|
521
|
+
workspaceLastPushedAssetWatermarksRef.current = new Map();
|
|
522
|
+
workspaceLastPushedDocumentReviewSessionIdsRef.current = new Set();
|
|
523
|
+
workspaceLastPushedDocumentReviewSessionWatermarksRef.current = new Map();
|
|
524
|
+
workspaceLastPushedDocumentReviewCommentIdsRef.current = new Set();
|
|
525
|
+
workspaceLastPushedDocumentReviewCommentWatermarksRef.current = new Map();
|
|
526
|
+
workspaceLastPushedAnnotatedAttachmentSessionIdsRef.current = new Set();
|
|
527
|
+
workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef.current = new Map();
|
|
528
|
+
workspaceLastPushedTaskEventWatermarksRef.current = new Map();
|
|
529
|
+
workspaceStartupFullReconcileRanRef.current = false;
|
|
530
|
+
workspaceFullReconcileInFlightRef.current = false;
|
|
531
|
+
const persisted = readPersistedSyncWatermarks(currentWorkspaceId, taxonomyStateFingerprint);
|
|
532
|
+
if (!hydrateWorkspacePushBaselines(persisted)) {
|
|
533
|
+
workspaceBaselineResetReasonRef.current = 'missing-baseline';
|
|
534
|
+
}
|
|
535
|
+
}, [currentWorkspaceId, hydrateWorkspacePushBaselines, taxonomyStateFingerprint]);
|
|
536
|
+
useEffect(() => {
|
|
537
|
+
if (workspaceBaselineResetReasonRef.current !== 'missing-baseline')
|
|
538
|
+
return;
|
|
539
|
+
const persistedLocal = readPersistedSyncWatermarks(currentWorkspaceId, taxonomyStateFingerprint);
|
|
540
|
+
if (hydrateWorkspacePushBaselines(persistedLocal))
|
|
541
|
+
return;
|
|
542
|
+
const persistedRemote = parsePersistedSyncWatermarksSnapshot(readWorkspaceSyncStateSnapshot().pushBaseline, taxonomyStateFingerprint);
|
|
543
|
+
if (!hydrateWorkspacePushBaselines(persistedRemote))
|
|
544
|
+
return;
|
|
545
|
+
savePersistedSyncWatermarks(currentWorkspaceId, {
|
|
546
|
+
...buildCurrentPersistedWorkspaceSyncWatermarks(),
|
|
547
|
+
taxonomyStateFingerprint
|
|
548
|
+
});
|
|
549
|
+
}, [
|
|
550
|
+
buildCurrentPersistedWorkspaceSyncWatermarks,
|
|
551
|
+
currentWorkspaceId,
|
|
552
|
+
hydrateWorkspacePushBaselines,
|
|
553
|
+
readWorkspaceSyncStateSnapshot,
|
|
554
|
+
taxonomyStateFingerprint,
|
|
555
|
+
workspaceCloudSyncEnabled,
|
|
556
|
+
workspaceSyncPhase
|
|
557
|
+
]);
|
|
521
558
|
const scheduleWorkspaceSyncRetry = useCallback((minDelayMs) => {
|
|
522
559
|
scheduleWorkspaceRetry(() => retryWorkspaceSyncRef.current(), { minDelayMs });
|
|
523
560
|
}, [scheduleWorkspaceRetry]);
|
|
@@ -560,7 +597,8 @@ export function useSyncOrchestrator(input) {
|
|
|
560
597
|
isAuthenticated,
|
|
561
598
|
resolveCloudAuthUrl,
|
|
562
599
|
workspaceId: currentWorkspaceId,
|
|
563
|
-
workspaceName: projectName || currentWorkspaceId
|
|
600
|
+
workspaceName: projectName || currentWorkspaceId,
|
|
601
|
+
allowProvisionOnMismatch: workspaceSyncPhase !== 'attach-cloud'
|
|
564
602
|
});
|
|
565
603
|
}, [
|
|
566
604
|
cloudAuthConfigured,
|
|
@@ -568,7 +606,8 @@ export function useSyncOrchestrator(input) {
|
|
|
568
606
|
isAuthenticated,
|
|
569
607
|
resolveCloudAuthUrl,
|
|
570
608
|
currentWorkspaceId,
|
|
571
|
-
projectName
|
|
609
|
+
projectName,
|
|
610
|
+
workspaceSyncPhase
|
|
572
611
|
]);
|
|
573
612
|
useEffect(() => {
|
|
574
613
|
workspaceTasksRef.current = tasks || [];
|
|
@@ -576,6 +615,17 @@ export function useSyncOrchestrator(input) {
|
|
|
576
615
|
useEffect(() => {
|
|
577
616
|
workspaceArchivedTasksRef.current = archivedTasks || [];
|
|
578
617
|
}, [archivedTasks]);
|
|
618
|
+
useEffect(() => {
|
|
619
|
+
workspaceDeletedTaskIdsRef.current = new Set((deletedTasks || [])
|
|
620
|
+
.map((entry) => String(entry?.taskId || '').trim())
|
|
621
|
+
.filter((taskId) => taskId.length > 0));
|
|
622
|
+
workspaceDeletedTaskWatermarksRef.current = new Map((deletedTasks || [])
|
|
623
|
+
.map((entry) => [
|
|
624
|
+
String(entry?.taskId || '').trim(),
|
|
625
|
+
String(entry?.deletedAt || '').trim()
|
|
626
|
+
])
|
|
627
|
+
.filter(([taskId, deletedAt]) => taskId.length > 0 && Number.isFinite(Date.parse(deletedAt))));
|
|
628
|
+
}, [deletedTasks, workspaceDeletedTaskIdsRef, workspaceDeletedTaskWatermarksRef]);
|
|
579
629
|
useEffect(() => {
|
|
580
630
|
workspaceInitiativesRef.current = initiatives || [];
|
|
581
631
|
}, [initiatives]);
|
|
@@ -595,6 +645,7 @@ export function useSyncOrchestrator(input) {
|
|
|
595
645
|
taskEvents: flattenTaskEvents(workspaceTasksRef.current, workspaceArchivedTasksRef.current),
|
|
596
646
|
lastPushedTaskIds: workspaceLastPushedTaskIdsRef.current,
|
|
597
647
|
pendingDeletedTaskIds: workspaceDeletedTaskIdsRef.current,
|
|
648
|
+
pendingDeletedTaskWatermarks: workspaceDeletedTaskWatermarksRef.current,
|
|
598
649
|
initiatives: workspaceInitiativesRef.current,
|
|
599
650
|
workstreams: workspaceWorkstreamsRef.current,
|
|
600
651
|
taxonomies: taxonomies || [],
|
|
@@ -606,8 +657,11 @@ export function useSyncOrchestrator(input) {
|
|
|
606
657
|
aiProfiles: workspaceSyncAiProfilesRef.current,
|
|
607
658
|
lastPushedAiProfileIds: forceAllAiProfiles ? new Set() : workspaceLastPushedAiProfileIdsRef.current,
|
|
608
659
|
lastPushedAiProfileWatermarks: forceAllAiProfiles ? new Map() : workspaceLastPushedAiProfileWatermarksRef.current,
|
|
660
|
+
lastPushedAiProfileChangeKeys: forceAllAiProfiles ? new Map() : workspaceLastPushedAiProfileChangeKeysRef.current,
|
|
609
661
|
documents: workspaceSyncMarkdownDocumentsRef.current,
|
|
610
662
|
assets: workspaceSyncAssetsRef.current,
|
|
663
|
+
currentDocumentPaths: workspaceSyncKnownDocumentPathsRef.current,
|
|
664
|
+
currentAssetPaths: workspaceSyncKnownAssetPathsRef.current,
|
|
611
665
|
documentReviewSessions: workspaceSyncDocumentReviewSessionsRef.current,
|
|
612
666
|
documentReviewComments: workspaceSyncDocumentReviewCommentsRef.current,
|
|
613
667
|
annotatedAttachmentSessions: workspaceSyncAnnotatedAttachmentSessionsRef.current,
|
|
@@ -622,25 +676,42 @@ export function useSyncOrchestrator(input) {
|
|
|
622
676
|
lastPushedAnnotatedAttachmentSessionIds: workspaceLastPushedAnnotatedAttachmentSessionIdsRef.current,
|
|
623
677
|
lastPushedAnnotatedAttachmentSessionWatermarks: workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef.current,
|
|
624
678
|
lastPushedTaskEventWatermarks: workspaceLastPushedTaskEventWatermarksRef.current,
|
|
625
|
-
|
|
679
|
+
baselineResetReason: workspaceBaselineResetReasonRef.current,
|
|
680
|
+
lastPushedWatermarks: workspaceLastPushedWatermarksRef.current,
|
|
681
|
+
lastPushedTaskChangeKeys: workspaceLastPushedTaskChangeKeysRef.current
|
|
626
682
|
});
|
|
627
683
|
}, [taxonomies, taxonomyState]);
|
|
628
684
|
const reportSyncEvent = useCallback((workspaceId, event) => {
|
|
629
685
|
if (runtimeMode !== 'local')
|
|
630
686
|
return;
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
687
|
+
try {
|
|
688
|
+
const normalizedEvent = {
|
|
689
|
+
...event,
|
|
690
|
+
workspaceId,
|
|
691
|
+
details: normalizeSyncEventDetails(event.details)
|
|
692
|
+
};
|
|
693
|
+
void fetch('/api/taskforce/sync/events', {
|
|
694
|
+
method: 'POST',
|
|
695
|
+
credentials: 'include',
|
|
696
|
+
headers: { 'Content-Type': 'application/json' },
|
|
697
|
+
body: JSON.stringify(normalizedEvent)
|
|
698
|
+
}).catch((error) => {
|
|
699
|
+
logSyncDebug('sync_event_post_failed', {
|
|
700
|
+
workspaceId,
|
|
701
|
+
eventType: event.eventType,
|
|
702
|
+
status: event.status,
|
|
703
|
+
error: String(error?.message || error || '')
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
catch (error) {
|
|
637
708
|
logSyncDebug('sync_event_post_failed', {
|
|
638
709
|
workspaceId,
|
|
639
710
|
eventType: event.eventType,
|
|
640
711
|
status: event.status,
|
|
641
712
|
error: String(error?.message || error || '')
|
|
642
713
|
});
|
|
643
|
-
}
|
|
714
|
+
}
|
|
644
715
|
}, [runtimeMode]);
|
|
645
716
|
const buildWorkspaceSyncSignature = useCallback(() => {
|
|
646
717
|
// Read documents and assets from refs for the same stale-closure reason as
|
|
@@ -704,9 +775,16 @@ export function useSyncOrchestrator(input) {
|
|
|
704
775
|
action: String(event.action || '').trim(),
|
|
705
776
|
}))
|
|
706
777
|
.sort((left, right) => left.id.localeCompare(right.id)),
|
|
778
|
+
deletedTasks: (deletedTasks || [])
|
|
779
|
+
.map((entry) => ({
|
|
780
|
+
taskId: String(entry?.taskId || '').trim(),
|
|
781
|
+
deletedAt: String(entry?.deletedAt || '').trim()
|
|
782
|
+
}))
|
|
783
|
+
.filter((entry) => entry.taskId.length > 0 && Number.isFinite(Date.parse(entry.deletedAt)))
|
|
784
|
+
.sort((left, right) => left.taskId.localeCompare(right.taskId)),
|
|
707
785
|
taxonomies: Array.isArray(taxonomies) ? taxonomies : [],
|
|
708
786
|
taxonomyState: taxonomyState && typeof taxonomyState === 'object' ? taxonomyState : null
|
|
709
|
-
}), [tasks, archivedTasks, initiatives, workstreams, taxonomies, taxonomyState]);
|
|
787
|
+
}), [tasks, archivedTasks, deletedTasks, initiatives, workstreams, taxonomies, taxonomyState]);
|
|
710
788
|
// Fingerprint of per-task attachment counts — changes when the MCP tool saves a new
|
|
711
789
|
// attachment and the React app polls the updated task. Used to trigger an immediate
|
|
712
790
|
// asset snapshot refresh without requiring a cross-process DOM event.
|
|
@@ -773,6 +851,11 @@ export function useSyncOrchestrator(input) {
|
|
|
773
851
|
}))
|
|
774
852
|
.filter((entry) => entry.path.length > 0)
|
|
775
853
|
: [];
|
|
854
|
+
const knownDocumentPaths = new Set(Array.isArray(data?.knownPaths)
|
|
855
|
+
? data.knownPaths
|
|
856
|
+
.map((entry) => String(entry || '').trim())
|
|
857
|
+
.filter((entry) => entry.length > 0)
|
|
858
|
+
: docs.map((entry) => entry.path));
|
|
776
859
|
const fingerprint = typeof data?.fingerprint === 'string'
|
|
777
860
|
? data.fingerprint
|
|
778
861
|
: JSON.stringify(docs.map((entry) => `${entry.path}:${entry.updatedAt}:${entry.content.length}`).sort());
|
|
@@ -780,6 +863,7 @@ export function useSyncOrchestrator(input) {
|
|
|
780
863
|
return;
|
|
781
864
|
workspaceSyncMarkdownDocumentsFingerprintRef.current = fingerprint;
|
|
782
865
|
workspaceSyncMarkdownDocumentsRef.current = docs;
|
|
866
|
+
workspaceSyncKnownDocumentPathsRef.current = knownDocumentPaths;
|
|
783
867
|
setWorkspaceSyncMarkdownDocuments(docs);
|
|
784
868
|
setWorkspaceSyncMarkdownDocumentsFingerprint(fingerprint);
|
|
785
869
|
}
|
|
@@ -829,7 +913,16 @@ export function useSyncOrchestrator(input) {
|
|
|
829
913
|
setWorkspaceSyncAiProfilesLastFetchAt(new Date().toISOString());
|
|
830
914
|
const fingerprint = typeof data?.fingerprint === 'string'
|
|
831
915
|
? data.fingerprint
|
|
832
|
-
: JSON.stringify(aiProfiles.map((entry) =>
|
|
916
|
+
: JSON.stringify(aiProfiles.map((entry) => [
|
|
917
|
+
entry.id,
|
|
918
|
+
entry.updatedAt,
|
|
919
|
+
entry.name,
|
|
920
|
+
entry.username,
|
|
921
|
+
entry.archivedAt ?? '',
|
|
922
|
+
entry.archivedReason ?? '',
|
|
923
|
+
entry.mergedIntoProfileId ?? '',
|
|
924
|
+
entry.rosterStateUpdatedAt ?? ''
|
|
925
|
+
].join(':')).sort());
|
|
833
926
|
if (fingerprint === workspaceSyncAiProfilesFingerprintRef.current)
|
|
834
927
|
return;
|
|
835
928
|
workspaceSyncAiProfilesFingerprintRef.current = fingerprint;
|
|
@@ -886,6 +979,11 @@ export function useSyncOrchestrator(input) {
|
|
|
886
979
|
}))
|
|
887
980
|
.filter((entry) => entry.path.length > 0 && entry.contentBase64.length > 0)
|
|
888
981
|
: [];
|
|
982
|
+
const knownAssetPaths = new Set(Array.isArray(data?.knownPaths)
|
|
983
|
+
? data.knownPaths
|
|
984
|
+
.map((entry) => String(entry || '').trim())
|
|
985
|
+
.filter((entry) => entry.length > 0)
|
|
986
|
+
: assets.map((entry) => entry.path));
|
|
889
987
|
const fingerprint = typeof data?.fingerprint === 'string'
|
|
890
988
|
? data.fingerprint
|
|
891
989
|
: JSON.stringify(assets.map((entry) => `${entry.path}:${entry.updatedAt}:${entry.contentBase64.length}`).sort());
|
|
@@ -893,6 +991,7 @@ export function useSyncOrchestrator(input) {
|
|
|
893
991
|
return;
|
|
894
992
|
workspaceSyncAssetsFingerprintRef.current = fingerprint;
|
|
895
993
|
workspaceSyncAssetsRef.current = assets;
|
|
994
|
+
workspaceSyncKnownAssetPathsRef.current = knownAssetPaths;
|
|
896
995
|
setWorkspaceSyncAssets(assets);
|
|
897
996
|
setWorkspaceSyncAssetsFingerprint(fingerprint);
|
|
898
997
|
}
|
|
@@ -1035,6 +1134,38 @@ export function useSyncOrchestrator(input) {
|
|
|
1035
1134
|
}
|
|
1036
1135
|
}, [runtimeMode, currentWorkspaceId, cloudAuthConfigured, authSessionResolved, isAuthenticated, markWorkspaceSyncReferenceSnapshotReady]);
|
|
1037
1136
|
const seedWorkspaceSnapshotPushBaseline = useCallback(() => {
|
|
1137
|
+
workspaceLastPushedTaskIdsRef.current = new Set([...workspaceTasksRef.current, ...workspaceArchivedTasksRef.current]
|
|
1138
|
+
.map((entry) => String(entry?.id || '').trim())
|
|
1139
|
+
.filter((id) => id.length > 0));
|
|
1140
|
+
workspaceLastPushedWatermarksRef.current = new Map([...workspaceTasksRef.current, ...workspaceArchivedTasksRef.current]
|
|
1141
|
+
.map((entry) => [String(entry?.id || '').trim(), taskSyncResourceAdapter.getWatermark(entry)])
|
|
1142
|
+
.filter(([id]) => id.length > 0));
|
|
1143
|
+
workspaceLastPushedTaskChangeKeysRef.current = new Map([
|
|
1144
|
+
...workspaceTasksRef.current.map((entry) => [String(entry?.id || '').trim(), taskSyncResourceAdapter.getChangeKey({ ...entry, isArchived: false })]),
|
|
1145
|
+
...workspaceArchivedTasksRef.current.map((entry) => [String(entry?.id || '').trim(), taskSyncResourceAdapter.getChangeKey({ ...entry, isArchived: true })])
|
|
1146
|
+
]
|
|
1147
|
+
.filter(([id]) => id.length > 0));
|
|
1148
|
+
workspaceLastPushedInitiativeIdsRef.current = new Set(workspaceInitiativesRef.current
|
|
1149
|
+
.map((entry) => String(entry?.id || '').trim())
|
|
1150
|
+
.filter((id) => id.length > 0));
|
|
1151
|
+
workspaceLastPushedInitiativeWatermarksRef.current = new Map(workspaceInitiativesRef.current
|
|
1152
|
+
.map((entry) => [String(entry?.id || '').trim(), String(entry?.updatedAt || entry?.createdAt || '').trim()])
|
|
1153
|
+
.filter(([id]) => id.length > 0));
|
|
1154
|
+
workspaceLastPushedWorkstreamIdsRef.current = new Set(workspaceWorkstreamsRef.current
|
|
1155
|
+
.map((entry) => String(entry?.id || '').trim())
|
|
1156
|
+
.filter((id) => id.length > 0));
|
|
1157
|
+
workspaceLastPushedWorkstreamWatermarksRef.current = new Map(workspaceWorkstreamsRef.current
|
|
1158
|
+
.map((entry) => [String(entry?.id || '').trim(), String(entry?.updatedAt || entry?.createdAt || '').trim()])
|
|
1159
|
+
.filter(([id]) => id.length > 0));
|
|
1160
|
+
workspaceLastPushedAiProfileIdsRef.current = new Set(workspaceSyncAiProfilesRef.current
|
|
1161
|
+
.map((entry) => String(entry?.id || '').trim())
|
|
1162
|
+
.filter((id) => id.length > 0));
|
|
1163
|
+
workspaceLastPushedAiProfileWatermarksRef.current = new Map(workspaceSyncAiProfilesRef.current
|
|
1164
|
+
.map((entry) => [String(entry?.id || '').trim(), aiProfileSyncResourceAdapter.getWatermark(entry)])
|
|
1165
|
+
.filter(([id]) => id.length > 0));
|
|
1166
|
+
workspaceLastPushedAiProfileChangeKeysRef.current = new Map(workspaceSyncAiProfilesRef.current
|
|
1167
|
+
.map((entry) => [String(entry?.id || '').trim(), aiProfileSyncResourceAdapter.getChangeKey(entry)])
|
|
1168
|
+
.filter(([id]) => id.length > 0));
|
|
1038
1169
|
workspaceLastPushedDocumentPathsRef.current = new Set(workspaceSyncMarkdownDocumentsRef.current
|
|
1039
1170
|
.map((entry) => String(entry?.path || '').trim())
|
|
1040
1171
|
.filter((path) => path.length > 0));
|
|
@@ -1068,7 +1199,9 @@ export function useSyncOrchestrator(input) {
|
|
|
1068
1199
|
workspaceLastPushedTaskEventWatermarksRef.current = new Map(flattenTaskEvents(workspaceTasksRef.current, workspaceArchivedTasksRef.current)
|
|
1069
1200
|
.map((entry) => [String(entry?.id || '').trim(), String(entry?.createdAt || '').trim()])
|
|
1070
1201
|
.filter(([id]) => id.length > 0));
|
|
1071
|
-
|
|
1202
|
+
const persisted = persistWorkspaceSyncWatermarksSnapshotBestEffort();
|
|
1203
|
+
workspaceBaselineResetReasonRef.current = persisted ? null : 'storage-write-failed';
|
|
1204
|
+
}, [persistWorkspaceSyncWatermarksSnapshotBestEffort]);
|
|
1072
1205
|
const refreshLocalWorkspaceTaskCollections = useCallback(async () => {
|
|
1073
1206
|
const [tasksRes, archiveRes] = await Promise.all([
|
|
1074
1207
|
fetch('/api/taskforce/tasks', {
|
|
@@ -1139,7 +1272,6 @@ export function useSyncOrchestrator(input) {
|
|
|
1139
1272
|
workspaceCloudSyncEnabled,
|
|
1140
1273
|
currentWorkspaceId,
|
|
1141
1274
|
workspaceSyncPhase,
|
|
1142
|
-
taxonomyStateFingerprint,
|
|
1143
1275
|
isAuthenticated,
|
|
1144
1276
|
checkAuthSession,
|
|
1145
1277
|
resolveCloudAuthUrl,
|
|
@@ -1152,6 +1284,7 @@ export function useSyncOrchestrator(input) {
|
|
|
1152
1284
|
scheduleWorkspaceSyncRetry,
|
|
1153
1285
|
persistWorkspaceSyncPatchSafely,
|
|
1154
1286
|
persistWorkspaceSyncPatchBestEffort,
|
|
1287
|
+
persistWorkspaceSyncWatermarksSnapshotBestEffort,
|
|
1155
1288
|
reportSyncEvent,
|
|
1156
1289
|
acquireWorkspaceSyncLease,
|
|
1157
1290
|
releaseWorkspaceSyncLease,
|
|
@@ -1171,18 +1304,22 @@ export function useSyncOrchestrator(input) {
|
|
|
1171
1304
|
workspaceForceFullAiProfilePushRef,
|
|
1172
1305
|
workspacePushInFlightRef,
|
|
1173
1306
|
workspacePullInFlightRef,
|
|
1307
|
+
workspacePendingPushReplayRef,
|
|
1174
1308
|
workspaceLastPushedSignatureRef,
|
|
1175
1309
|
workspacePendingSignatureRef,
|
|
1176
1310
|
workspaceLastPushedTaskIdsRef,
|
|
1177
1311
|
workspaceDeletedTaskIdsRef,
|
|
1312
|
+
workspaceDeletedTaskWatermarksRef,
|
|
1178
1313
|
workspacePullCursorRef,
|
|
1179
1314
|
workspaceLastPushedWatermarksRef,
|
|
1315
|
+
workspaceLastPushedTaskChangeKeysRef,
|
|
1180
1316
|
workspaceLastPushedInitiativeIdsRef,
|
|
1181
1317
|
workspaceLastPushedInitiativeWatermarksRef,
|
|
1182
1318
|
workspaceLastPushedWorkstreamIdsRef,
|
|
1183
1319
|
workspaceLastPushedWorkstreamWatermarksRef,
|
|
1184
1320
|
workspaceLastPushedAiProfileIdsRef,
|
|
1185
1321
|
workspaceLastPushedAiProfileWatermarksRef,
|
|
1322
|
+
workspaceLastPushedAiProfileChangeKeysRef,
|
|
1186
1323
|
workspaceLastPushedDocumentPathsRef,
|
|
1187
1324
|
workspaceLastPushedDocumentWatermarksRef,
|
|
1188
1325
|
workspaceLastPushedAssetPathsRef,
|
|
@@ -1194,6 +1331,7 @@ export function useSyncOrchestrator(input) {
|
|
|
1194
1331
|
workspaceLastPushedAnnotatedAttachmentSessionIdsRef,
|
|
1195
1332
|
workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef,
|
|
1196
1333
|
workspaceLastPushedTaskEventWatermarksRef,
|
|
1334
|
+
workspaceBaselineResetReasonRef,
|
|
1197
1335
|
workspaceCaptureAttachBootstrapBaselineRef,
|
|
1198
1336
|
workspaceStartupFullReconcileRanRef,
|
|
1199
1337
|
workspaceFullReconcileInFlightRef,
|
|
@@ -1227,6 +1365,7 @@ export function useSyncOrchestrator(input) {
|
|
|
1227
1365
|
scheduleWorkspaceSyncRetry,
|
|
1228
1366
|
persistWorkspaceSyncPatchSafely,
|
|
1229
1367
|
persistWorkspaceSyncPatchBestEffort,
|
|
1368
|
+
persistWorkspaceSyncWatermarksSnapshotBestEffort,
|
|
1230
1369
|
reportSyncEvent,
|
|
1231
1370
|
acquireWorkspaceSyncLease,
|
|
1232
1371
|
releaseWorkspaceSyncLease,
|
|
@@ -1314,7 +1453,6 @@ export function useSyncOrchestrator(input) {
|
|
|
1314
1453
|
}, [syncUserGlobalSettings]);
|
|
1315
1454
|
const recoveryCoordinator = useMemo(() => createSyncRecoveryCoordinator({
|
|
1316
1455
|
currentWorkspaceId,
|
|
1317
|
-
taxonomyStateFingerprint,
|
|
1318
1456
|
workspaceCloudSyncEnabled,
|
|
1319
1457
|
workspaceSyncPhase,
|
|
1320
1458
|
workspaceLastPullAt,
|
|
@@ -1330,12 +1468,14 @@ export function useSyncOrchestrator(input) {
|
|
|
1330
1468
|
workspacePendingSignatureRef,
|
|
1331
1469
|
workspaceLastPushedSignatureRef,
|
|
1332
1470
|
workspaceLastPushedWatermarksRef,
|
|
1471
|
+
workspaceLastPushedTaskChangeKeysRef,
|
|
1333
1472
|
workspaceLastPushedInitiativeIdsRef,
|
|
1334
1473
|
workspaceLastPushedInitiativeWatermarksRef,
|
|
1335
1474
|
workspaceLastPushedWorkstreamIdsRef,
|
|
1336
1475
|
workspaceLastPushedWorkstreamWatermarksRef,
|
|
1337
1476
|
workspaceLastPushedAiProfileIdsRef,
|
|
1338
1477
|
workspaceLastPushedAiProfileWatermarksRef,
|
|
1478
|
+
workspaceLastPushedAiProfileChangeKeysRef,
|
|
1339
1479
|
workspaceLastPushedDocumentPathsRef,
|
|
1340
1480
|
workspaceLastPushedDocumentWatermarksRef,
|
|
1341
1481
|
workspaceLastPushedAssetPathsRef,
|
|
@@ -1347,6 +1487,7 @@ export function useSyncOrchestrator(input) {
|
|
|
1347
1487
|
workspaceLastPushedAnnotatedAttachmentSessionIdsRef,
|
|
1348
1488
|
workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef,
|
|
1349
1489
|
workspaceLastPushedTaskEventWatermarksRef,
|
|
1490
|
+
workspaceBaselineResetReasonRef,
|
|
1350
1491
|
bootstrapPhaseStartedAtRef,
|
|
1351
1492
|
bootstrapLastProgressAtRef,
|
|
1352
1493
|
acquireWorkspaceSyncLease,
|
|
@@ -1355,7 +1496,9 @@ export function useSyncOrchestrator(input) {
|
|
|
1355
1496
|
clearWorkspaceRetry,
|
|
1356
1497
|
clearWorkspaceIssue,
|
|
1357
1498
|
reportWorkspaceWindowContention,
|
|
1499
|
+
reportSyncEvent,
|
|
1358
1500
|
persistWorkspaceSyncPatch,
|
|
1501
|
+
persistWorkspaceSyncWatermarksSnapshotBestEffort,
|
|
1359
1502
|
buildWorkspaceSyncSignature,
|
|
1360
1503
|
pullWorkspaceChangesFromCloud,
|
|
1361
1504
|
pushWorkspaceChangesToCloud,
|
|
@@ -1375,7 +1518,9 @@ export function useSyncOrchestrator(input) {
|
|
|
1375
1518
|
clearWorkspaceRetry,
|
|
1376
1519
|
clearWorkspaceIssue,
|
|
1377
1520
|
reportWorkspaceWindowContention,
|
|
1521
|
+
reportSyncEvent,
|
|
1378
1522
|
persistWorkspaceSyncPatch,
|
|
1523
|
+
persistWorkspaceSyncWatermarksSnapshotBestEffort,
|
|
1379
1524
|
buildWorkspaceSyncSignature,
|
|
1380
1525
|
pullWorkspaceChangesFromCloud,
|
|
1381
1526
|
pushWorkspaceChangesToCloud,
|
|
@@ -1387,6 +1532,39 @@ export function useSyncOrchestrator(input) {
|
|
|
1387
1532
|
const { retryWorkspaceCloudSync, resetWorkspaceSyncCursorAndPull } = recoveryCoordinator;
|
|
1388
1533
|
// === SECTION: Diagnostics & reactive effects ===
|
|
1389
1534
|
const getWorkspaceSyncDiagnostics = useCallback(() => {
|
|
1535
|
+
const pushReadiness = resolvePushSyncReadiness({
|
|
1536
|
+
cloudAuthConfigured,
|
|
1537
|
+
runtimeMode,
|
|
1538
|
+
workspaceCloudSyncEnabled,
|
|
1539
|
+
currentWorkspaceId,
|
|
1540
|
+
workspaceSyncPhase,
|
|
1541
|
+
isAuthenticated,
|
|
1542
|
+
repairModeActive: workspaceRepairModeRef.current === true,
|
|
1543
|
+
repairModeBypass: false,
|
|
1544
|
+
pushInFlight: workspacePushInFlightRef.current,
|
|
1545
|
+
pullInFlight: workspacePullInFlightRef.current
|
|
1546
|
+
});
|
|
1547
|
+
const retryPending = isWorkspaceRetryPending();
|
|
1548
|
+
const pullReadiness = resolvePullSyncReadiness({
|
|
1549
|
+
cloudAuthConfigured,
|
|
1550
|
+
runtimeMode,
|
|
1551
|
+
workspaceCloudSyncEnabled,
|
|
1552
|
+
currentWorkspaceId,
|
|
1553
|
+
workspaceSyncPhase,
|
|
1554
|
+
isAuthenticated,
|
|
1555
|
+
repairModeActive: workspaceRepairModeRef.current === true,
|
|
1556
|
+
repairModeBypass: false,
|
|
1557
|
+
retryPending,
|
|
1558
|
+
pushInFlight: workspacePushInFlightRef.current,
|
|
1559
|
+
pullInFlight: workspacePullInFlightRef.current
|
|
1560
|
+
});
|
|
1561
|
+
const retryReadiness = resolveRetrySyncReadiness({
|
|
1562
|
+
cloudAuthConfigured,
|
|
1563
|
+
runtimeMode,
|
|
1564
|
+
workspaceCloudSyncEnabled,
|
|
1565
|
+
currentWorkspaceId,
|
|
1566
|
+
isAuthenticated
|
|
1567
|
+
});
|
|
1390
1568
|
return {
|
|
1391
1569
|
workspaceId: currentWorkspaceId,
|
|
1392
1570
|
enabled: workspaceCloudSyncEnabled,
|
|
@@ -1400,6 +1578,13 @@ export function useSyncOrchestrator(input) {
|
|
|
1400
1578
|
pendingChanges: workspaceSyncPendingChanges,
|
|
1401
1579
|
lastErrorMessage: workspaceLastErrorMessage,
|
|
1402
1580
|
recommendedAction: workspaceSyncRecommendedAction,
|
|
1581
|
+
pushBlockedReason: pushReadiness.allowed ? null : pushReadiness.reason,
|
|
1582
|
+
pullBlockedReason: pullReadiness.allowed ? null : pullReadiness.reason,
|
|
1583
|
+
retryBlockedReason: retryReadiness.allowed ? null : retryReadiness.reason,
|
|
1584
|
+
repairActive: workspaceRepairModeRef.current === true,
|
|
1585
|
+
pushInFlight: workspacePushInFlightRef.current === true,
|
|
1586
|
+
pullInFlight: workspacePullInFlightRef.current === true,
|
|
1587
|
+
retryPending,
|
|
1403
1588
|
documentSnapshotCount: workspaceSyncMarkdownDocuments.length,
|
|
1404
1589
|
aiProfileSnapshotCount: workspaceSyncAiProfiles.length,
|
|
1405
1590
|
aiProfileSnapshotRawCount: workspaceSyncAiProfilesRawCount,
|
|
@@ -1416,9 +1601,12 @@ export function useSyncOrchestrator(input) {
|
|
|
1416
1601
|
forceFullAiProfilePushQueued: workspaceForceFullAiProfilePushRef.current === true
|
|
1417
1602
|
};
|
|
1418
1603
|
}, [
|
|
1604
|
+
cloudAuthConfigured,
|
|
1419
1605
|
currentWorkspaceId,
|
|
1606
|
+
runtimeMode,
|
|
1420
1607
|
workspaceCloudSyncEnabled,
|
|
1421
1608
|
workspaceSyncPhase,
|
|
1609
|
+
isAuthenticated,
|
|
1422
1610
|
workspaceSyncStatus,
|
|
1423
1611
|
workspaceSyncSummary,
|
|
1424
1612
|
workspaceLastSuccessfulSyncAt,
|
|
@@ -1438,7 +1626,8 @@ export function useSyncOrchestrator(input) {
|
|
|
1438
1626
|
workspaceSyncDocumentReviewSessions.length,
|
|
1439
1627
|
workspaceSyncDocumentReviewSessionsFingerprint,
|
|
1440
1628
|
workspaceSyncAnnotatedAttachmentSessions.length,
|
|
1441
|
-
workspaceSyncAnnotatedAttachmentSessionsFingerprint
|
|
1629
|
+
workspaceSyncAnnotatedAttachmentSessionsFingerprint,
|
|
1630
|
+
isWorkspaceRetryPending
|
|
1442
1631
|
]);
|
|
1443
1632
|
useEffect(() => {
|
|
1444
1633
|
retryWorkspaceSyncRef.current = retryWorkspaceCloudSync;
|
|
@@ -1520,11 +1709,14 @@ export function useSyncOrchestrator(input) {
|
|
|
1520
1709
|
refreshWorkspaceSyncContextAssetSnapshots();
|
|
1521
1710
|
}, [cloudAuthConfigured, runtimeMode, workspaceCloudSyncEnabled, authSessionResolved, isAuthenticated, taskAttachmentsFingerprint, refreshWorkspaceSyncContextAssetSnapshots]);
|
|
1522
1711
|
useEffect(() => {
|
|
1523
|
-
if (!
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1712
|
+
if (!shouldScheduleWorkspaceReactivePush({
|
|
1713
|
+
cloudAuthConfigured,
|
|
1714
|
+
runtimeMode,
|
|
1715
|
+
workspaceCloudSyncEnabled,
|
|
1716
|
+
authSessionResolved,
|
|
1717
|
+
workspaceSyncPhase,
|
|
1718
|
+
workspaceSyncReferenceSnapshotsReady
|
|
1719
|
+
}))
|
|
1528
1720
|
return;
|
|
1529
1721
|
const signature = buildWorkspaceSyncSignature();
|
|
1530
1722
|
if (shouldSuppressAttachBootstrapPush(signature))
|
|
@@ -1561,6 +1753,44 @@ export function useSyncOrchestrator(input) {
|
|
|
1561
1753
|
workspacePushInFlightRef,
|
|
1562
1754
|
workspacePullInFlightRef
|
|
1563
1755
|
]);
|
|
1756
|
+
useEffect(() => {
|
|
1757
|
+
if (!shouldStartWorkspacePushWatchdog({
|
|
1758
|
+
cloudAuthConfigured,
|
|
1759
|
+
runtimeMode,
|
|
1760
|
+
workspaceCloudSyncEnabled,
|
|
1761
|
+
authSessionResolved,
|
|
1762
|
+
workspaceSyncPhase,
|
|
1763
|
+
workspaceSyncReferenceSnapshotsReady
|
|
1764
|
+
}))
|
|
1765
|
+
return;
|
|
1766
|
+
const intervalId = window.setInterval(() => {
|
|
1767
|
+
if (workspacePushInFlightRef.current || workspacePullInFlightRef.current)
|
|
1768
|
+
return;
|
|
1769
|
+
if (isWorkspaceRetryPending())
|
|
1770
|
+
return;
|
|
1771
|
+
const signature = workspacePendingSignatureRef.current || buildWorkspaceSyncSignature();
|
|
1772
|
+
if (!signature || signature === workspaceLastPushedSignatureRef.current)
|
|
1773
|
+
return;
|
|
1774
|
+
const pendingPayload = buildWorkspaceSyncPayload();
|
|
1775
|
+
setWorkspaceSyncPendingChanges(pendingPayload.changes.length);
|
|
1776
|
+
if (pendingPayload.changes.length === 0)
|
|
1777
|
+
return;
|
|
1778
|
+
void pushWorkspaceChangesToCloud(signature);
|
|
1779
|
+
}, workspacePushWatchdogIntervalMs);
|
|
1780
|
+
return () => window.clearInterval(intervalId);
|
|
1781
|
+
}, [
|
|
1782
|
+
cloudAuthConfigured,
|
|
1783
|
+
runtimeMode,
|
|
1784
|
+
workspaceCloudSyncEnabled,
|
|
1785
|
+
authSessionResolved,
|
|
1786
|
+
workspaceSyncPhase,
|
|
1787
|
+
workspaceSyncReferenceSnapshotsReady,
|
|
1788
|
+
buildWorkspaceSyncSignature,
|
|
1789
|
+
buildWorkspaceSyncPayload,
|
|
1790
|
+
pushWorkspaceChangesToCloud,
|
|
1791
|
+
isWorkspaceRetryPending,
|
|
1792
|
+
workspacePushWatchdogIntervalMs
|
|
1793
|
+
]);
|
|
1564
1794
|
useEffect(() => {
|
|
1565
1795
|
if (cloudAuthConfigured && runtimeMode === 'local' && workspaceCloudSyncEnabled)
|
|
1566
1796
|
return;
|
|
@@ -1573,31 +1803,46 @@ export function useSyncOrchestrator(input) {
|
|
|
1573
1803
|
clearWorkspaceRetry
|
|
1574
1804
|
]);
|
|
1575
1805
|
useEffect(() => {
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1806
|
+
const pullPolicy = resolveWorkspaceAutomaticPullPolicy({
|
|
1807
|
+
cloudAuthConfigured,
|
|
1808
|
+
runtimeMode,
|
|
1809
|
+
workspaceCloudSyncEnabled,
|
|
1810
|
+
authSessionResolved,
|
|
1811
|
+
workspaceSyncPhase,
|
|
1812
|
+
lastPullAt: workspaceLastPullAt,
|
|
1813
|
+
retryPending: isWorkspaceRetryPending()
|
|
1814
|
+
});
|
|
1815
|
+
if (!pullPolicy.shouldStart)
|
|
1581
1816
|
return;
|
|
1582
|
-
|
|
1817
|
+
if (pullPolicy.shouldKickOffImmediately) {
|
|
1818
|
+
void pullWorkspaceChangesFromCloud();
|
|
1819
|
+
}
|
|
1583
1820
|
const intervalId = window.setInterval(() => {
|
|
1584
1821
|
void pullWorkspaceChangesFromCloud();
|
|
1585
1822
|
}, workspacePullIntervalMs);
|
|
1586
|
-
return () =>
|
|
1823
|
+
return () => {
|
|
1824
|
+
window.clearInterval(intervalId);
|
|
1825
|
+
};
|
|
1587
1826
|
}, [
|
|
1588
1827
|
cloudAuthConfigured,
|
|
1589
1828
|
runtimeMode,
|
|
1590
1829
|
workspaceCloudSyncEnabled,
|
|
1591
1830
|
authSessionResolved,
|
|
1592
1831
|
workspaceSyncPhase,
|
|
1832
|
+
workspaceLastPullAt,
|
|
1593
1833
|
isWorkspaceRetryPending,
|
|
1594
1834
|
pullWorkspaceChangesFromCloud,
|
|
1595
1835
|
workspacePullIntervalMs
|
|
1596
1836
|
]);
|
|
1597
1837
|
useEffect(() => {
|
|
1598
|
-
if (!
|
|
1599
|
-
|
|
1600
|
-
|
|
1838
|
+
if (!shouldStartWorkspacePeriodicFullReconcile({
|
|
1839
|
+
cloudAuthConfigured,
|
|
1840
|
+
runtimeMode,
|
|
1841
|
+
workspaceCloudSyncEnabled,
|
|
1842
|
+
authSessionResolved,
|
|
1843
|
+
isAuthenticated,
|
|
1844
|
+
workspaceSyncPhase
|
|
1845
|
+
}))
|
|
1601
1846
|
return;
|
|
1602
1847
|
const intervalId = window.setInterval(() => {
|
|
1603
1848
|
if (workspaceFullReconcileInFlightRef.current)
|
|
@@ -1660,13 +1905,15 @@ export function useSyncOrchestrator(input) {
|
|
|
1660
1905
|
// missing or stale-tombstoned metadata — a common result of a crash between file write
|
|
1661
1906
|
// and DB upsert. The repair only adds missing records; it never deletes files.
|
|
1662
1907
|
useEffect(() => {
|
|
1663
|
-
if (!
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1908
|
+
if (!shouldRunWorkspaceStartupRepair({
|
|
1909
|
+
cloudAuthConfigured,
|
|
1910
|
+
runtimeMode,
|
|
1911
|
+
workspaceCloudSyncEnabled,
|
|
1912
|
+
authSessionResolved,
|
|
1913
|
+
isAuthenticated,
|
|
1914
|
+
workspaceSyncPhase,
|
|
1915
|
+
alreadyRan: workspaceStartupRepairRanRef.current
|
|
1916
|
+
}))
|
|
1670
1917
|
return;
|
|
1671
1918
|
workspaceStartupRepairRanRef.current = true;
|
|
1672
1919
|
fetch('/api/taskforce/sync/workspace/repair-startup', {
|
|
@@ -1718,6 +1965,7 @@ export function useSyncOrchestrator(input) {
|
|
|
1718
1965
|
workspaceLastSuccessfulSyncAt,
|
|
1719
1966
|
workspaceCloudSyncEnabled,
|
|
1720
1967
|
workspaceSyncPhase,
|
|
1968
|
+
workspaceSyncSetupIntent,
|
|
1721
1969
|
workspaceSyncStatus,
|
|
1722
1970
|
workspaceSyncSummary,
|
|
1723
1971
|
workspaceSyncRecommendedAction,
|
|
@@ -1732,6 +1980,7 @@ export function useSyncOrchestrator(input) {
|
|
|
1732
1980
|
workspacePendingSignatureRef,
|
|
1733
1981
|
workspaceLastPushedTaskIdsRef,
|
|
1734
1982
|
workspaceDeletedTaskIdsRef,
|
|
1983
|
+
workspaceDeletedTaskWatermarksRef,
|
|
1735
1984
|
workspacePullCursorRef,
|
|
1736
1985
|
clearWorkspaceRetry,
|
|
1737
1986
|
persistWorkspaceSyncPatch,
|