@taskforcehq/taskforce 0.3.305 → 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/dist/Taskforce.module.css +295 -89
- package/dist/TaskforceCore.js +215 -43
- package/dist/TaskforceCore.test.js +433 -28
- 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 +46 -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/DocumentViewer.d.ts +5 -1
- package/dist/components/features/DocumentViewer.js +60 -3
- package/dist/components/features/DocumentWorkspace.js +1 -1
- package/dist/components/features/DocumentWorkspace.test.js +33 -0
- package/dist/components/features/documentWorkspaceModel.d.ts +1 -1
- 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.js +85 -90
- package/dist/components/task/TaskForm.test.js +110 -8
- package/dist/components/task/TaskKanban.d.ts +2 -1
- package/dist/components/task/TaskKanban.js +94 -13
- 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 -14
- package/dist/components/views/PlanComparisonPage.test.js +238 -0
- package/dist/components/views/PlansPage.js +127 -36
- package/dist/components/views/PlansPage.test.js +294 -12
- package/dist/components/views/StandaloneLayout.js +283 -533
- 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 +26 -1
- package/dist/components/views/standalone/modals/AccountHubModal.js +76 -2
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +20 -3
- 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/core/AiProfileService.d.ts +39 -2
- package/dist/core/AiProfileService.js +639 -73
- package/dist/core/AiProfileService.test.js +19 -1
- package/dist/core/AiProfiles.test.js +566 -7
- 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.js +3 -3
- package/dist/core/EntitlementsPolicy.test.js +2 -2
- package/dist/core/GlobalSettingsService.js +115 -6
- package/dist/core/PlanEntitlementService.d.ts +23 -2
- package/dist/core/PlanEntitlementService.js +343 -57
- package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
- package/dist/core/PlanFeatureCatalog.test.js +84 -0
- package/dist/core/PlanVersionPolicy.test.js +35 -2
- package/dist/core/PlanningEntities.test.js +52 -0
- package/dist/core/SignupMonetizationSettings.test.js +199 -27
- package/dist/core/SyncReconciliationService.js +2 -65
- package/dist/core/SystemAdmin.test.js +42 -42
- package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
- package/dist/core/Taskforce.d.ts +225 -14
- package/dist/core/Taskforce.js +765 -192
- 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 +117 -16
- package/dist/core/shared.d.ts +1 -1
- package/dist/core/shared.js +5 -1
- package/dist/core/types.d.ts +68 -2
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -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 +15 -3
- package/dist/hooks/sync/transfers.js +179 -62
- 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 +9 -2
- package/dist/hooks/useSyncOrchestrator.js +359 -178
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +407 -0
- package/dist/hooks/useTaskData.js +8 -3
- package/dist/hooks/useTaskData.test.js +45 -0
- package/dist/hooks/useTaskforce.d.ts +60 -2
- package/dist/hooks/useTaskforce.js +549 -240
- 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 +2380 -212
- package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
- package/dist/hooks/useWorkspaceSyncController.js +6 -1
- 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/documentAssetRegistrar.js +3 -3
- package/dist/mcp/runtime.d.ts +2 -0
- package/dist/mcp/runtime.js +131 -64
- package/dist/mcp/runtime.test.js +291 -12
- 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 +3 -0
- package/dist/server/routes/admin.js +272 -19
- package/dist/server/routes/auth.d.ts +15 -2
- package/dist/server/routes/auth.js +994 -57
- package/dist/server/routes/authSupport.d.ts +1 -1
- package/dist/server/routes/authSupport.js +1 -1
- package/dist/server/routes/billing.js +361 -35
- package/dist/server/routes/billing.test.js +592 -12
- package/dist/server/routes/documents.d.ts +1 -0
- package/dist/server/routes/documents.js +172 -70
- package/dist/server/routes/shared.d.ts +28 -2
- package/dist/server/routes/shared.js +69 -4
- package/dist/server/routes/sync.integration.test.js +437 -39
- package/dist/server/routes/sync.js +61 -605
- 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.js +43 -1
- package/dist/server/routes/workspaces.js +36 -5
- package/dist/server/routes.js +60 -3
- package/dist/server/routes.test.js +1824 -171
- 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 +17 -70
- package/dist/sync/syncApplyHandlers.d.ts +7 -0
- package/dist/sync/syncApplyHandlers.js +28 -13
- package/dist/sync/syncApplyHandlers.test.js +70 -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 +5 -0
- package/dist/sync/syncService.js +52 -0
- package/dist/sync/syncService.test.js +57 -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 +45 -0
- package/dist/sync/workspaceSyncModel.js +183 -28
- package/dist/sync/workspaceSyncModel.test.js +291 -12
- package/dist/sync/workspaceSyncState.d.ts +17 -0
- package/dist/sync/workspaceSyncState.js +2 -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-BG6P_GVW.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-Dhm7M8e7.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-ln0aF7xc.js → TaskSettings-DZX4jk7e.js} +1 -1
- package/dist/ui/assets/{WorkflowsModule-BcS4afRn.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-BSUaRwF8.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.js +3 -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 +3 -2
- package/scripts/export-sqlite-to-postgres.mjs +7 -9
- 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-N2MnQBZk.js +0 -1
- package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +0 -1
- package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +0 -252
- package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
- package/dist/ui/assets/PlansPage-Cq0zXj3I.js +0 -1
- package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
- package/dist/ui/assets/index-C2-OXZV7.css +0 -1
- package/dist/ui/assets/index-DcSI5F86.js +0 -6
- 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,
|
|
@@ -109,6 +70,7 @@ export function useSyncOrchestrator(input) {
|
|
|
109
70
|
const workspaceInitiativesRef = useRef(initiatives || []);
|
|
110
71
|
const workspaceWorkstreamsRef = useRef(workstreams || []);
|
|
111
72
|
const workspaceSyncMarkdownDocumentsRef = useRef([]);
|
|
73
|
+
const workspaceSyncKnownDocumentPathsRef = useRef(new Set());
|
|
112
74
|
const [workspaceSyncAiProfiles, setWorkspaceSyncAiProfiles] = useState([]);
|
|
113
75
|
const [workspaceSyncAiProfilesFingerprint, setWorkspaceSyncAiProfilesFingerprint] = useState('');
|
|
114
76
|
const [workspaceSyncAiProfilesLastFetchAt, setWorkspaceSyncAiProfilesLastFetchAt] = useState(null);
|
|
@@ -119,6 +81,7 @@ export function useSyncOrchestrator(input) {
|
|
|
119
81
|
const [workspaceSyncAssets, setWorkspaceSyncAssets] = useState([]);
|
|
120
82
|
const [workspaceSyncAssetsFingerprint, setWorkspaceSyncAssetsFingerprint] = useState('');
|
|
121
83
|
const workspaceSyncAssetsRef = useRef([]);
|
|
84
|
+
const workspaceSyncKnownAssetPathsRef = useRef(new Set());
|
|
122
85
|
const [workspaceSyncDocumentReviewSessions, setWorkspaceSyncDocumentReviewSessions] = useState([]);
|
|
123
86
|
const [workspaceSyncDocumentReviewSessionsFingerprint, setWorkspaceSyncDocumentReviewSessionsFingerprint] = useState('');
|
|
124
87
|
const workspaceSyncDocumentReviewSessionsRef = useRef([]);
|
|
@@ -137,6 +100,7 @@ export function useSyncOrchestrator(input) {
|
|
|
137
100
|
});
|
|
138
101
|
const userSettingsSyncInFlightRef = useRef(false);
|
|
139
102
|
const userSettingsRetryTimeoutRef = useRef(null);
|
|
103
|
+
const workspaceBaselineResetReasonRef = useRef('missing-baseline');
|
|
140
104
|
const userSettingsRetryAttemptRef = useRef(0);
|
|
141
105
|
const applyingRemoteUserSettingsRef = useRef(false);
|
|
142
106
|
const initialUserSettingsSyncCompletedRef = useRef(new Set());
|
|
@@ -150,6 +114,7 @@ export function useSyncOrchestrator(input) {
|
|
|
150
114
|
const realtimePullDebounceRef = useRef(null);
|
|
151
115
|
const realtimeSuppressedEventIdsRef = useRef(new Set());
|
|
152
116
|
const realtimeSuppressedEventQueueRef = useRef([]);
|
|
117
|
+
const workspacePendingPushReplayRef = useRef(null);
|
|
153
118
|
const retryWorkspaceSyncRef = useRef(() => Promise.resolve(false));
|
|
154
119
|
const syncUserGlobalSettingsRef = useRef(async () => { });
|
|
155
120
|
const bootstrapPhaseStartedAtRef = useRef(null);
|
|
@@ -178,73 +143,6 @@ export function useSyncOrchestrator(input) {
|
|
|
178
143
|
void syncUserGlobalSettingsRef.current({ preferCloudOnFirstSync: false });
|
|
179
144
|
}, delayMs);
|
|
180
145
|
}, [clearUserSettingsRetry]);
|
|
181
|
-
useEffect(() => {
|
|
182
|
-
workspaceSyncMarkdownDocumentsFingerprintRef.current = '';
|
|
183
|
-
workspaceSyncAiProfilesFingerprintRef.current = '';
|
|
184
|
-
workspaceSyncAssetsFingerprintRef.current = '';
|
|
185
|
-
workspaceSyncDocumentReviewSessionsFingerprintRef.current = '';
|
|
186
|
-
workspaceSyncAnnotatedAttachmentSessionsFingerprintRef.current = '';
|
|
187
|
-
workspaceAttachBootstrapBaselineSignatureRef.current = '';
|
|
188
|
-
workspaceCaptureAttachBootstrapBaselineRef.current = false;
|
|
189
|
-
setWorkspaceSyncMarkdownDocuments([]);
|
|
190
|
-
setWorkspaceSyncMarkdownDocumentsFingerprint('');
|
|
191
|
-
workspaceSyncAiProfilesRef.current = [];
|
|
192
|
-
setWorkspaceSyncAiProfiles([]);
|
|
193
|
-
setWorkspaceSyncAiProfilesFingerprint('');
|
|
194
|
-
setWorkspaceSyncAssets([]);
|
|
195
|
-
setWorkspaceSyncAssetsFingerprint('');
|
|
196
|
-
workspaceSyncDocumentReviewSessionsRef.current = [];
|
|
197
|
-
workspaceSyncDocumentReviewCommentsRef.current = [];
|
|
198
|
-
setWorkspaceSyncDocumentReviewSessions([]);
|
|
199
|
-
setWorkspaceSyncDocumentReviewSessionsFingerprint('');
|
|
200
|
-
workspaceSyncAnnotatedAttachmentSessionsRef.current = [];
|
|
201
|
-
setWorkspaceSyncAnnotatedAttachmentSessions([]);
|
|
202
|
-
setWorkspaceSyncAnnotatedAttachmentSessionsFingerprint('');
|
|
203
|
-
setWorkspaceSyncReferenceSnapshotStatus(EMPTY_WORKSPACE_SYNC_REFERENCE_SNAPSHOT_STATUS);
|
|
204
|
-
setWorkspaceLastErrorMessage(null);
|
|
205
|
-
setWorkspaceLastErrorAt(null);
|
|
206
|
-
setWorkspaceSyncPendingChanges(0);
|
|
207
|
-
realtimeSuppressedEventIdsRef.current = new Set();
|
|
208
|
-
realtimeSuppressedEventQueueRef.current = [];
|
|
209
|
-
// Reset watermarks for the new workspace, then restore from localStorage if recent
|
|
210
|
-
// enough. This prevents a full resync on every page reload while still catching any
|
|
211
|
-
// changes that happened while the app was closed.
|
|
212
|
-
workspaceLastPushedWatermarksRef.current = new Map();
|
|
213
|
-
workspaceLastPushedInitiativeIdsRef.current = new Set();
|
|
214
|
-
workspaceLastPushedInitiativeWatermarksRef.current = new Map();
|
|
215
|
-
workspaceLastPushedWorkstreamIdsRef.current = new Set();
|
|
216
|
-
workspaceLastPushedWorkstreamWatermarksRef.current = new Map();
|
|
217
|
-
workspaceLastPushedAiProfileIdsRef.current = new Set();
|
|
218
|
-
workspaceLastPushedAiProfileWatermarksRef.current = new Map();
|
|
219
|
-
workspaceLastPushedDocumentPathsRef.current = new Set();
|
|
220
|
-
workspaceLastPushedDocumentWatermarksRef.current = new Map();
|
|
221
|
-
workspaceLastPushedAssetPathsRef.current = new Set();
|
|
222
|
-
workspaceLastPushedAssetWatermarksRef.current = new Map();
|
|
223
|
-
workspaceLastPushedDocumentReviewSessionIdsRef.current = new Set();
|
|
224
|
-
workspaceLastPushedDocumentReviewSessionWatermarksRef.current = new Map();
|
|
225
|
-
workspaceLastPushedDocumentReviewCommentIdsRef.current = new Set();
|
|
226
|
-
workspaceLastPushedDocumentReviewCommentWatermarksRef.current = new Map();
|
|
227
|
-
workspaceLastPushedAnnotatedAttachmentSessionIdsRef.current = new Set();
|
|
228
|
-
workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef.current = new Map();
|
|
229
|
-
workspaceLastPushedTaskEventWatermarksRef.current = new Map();
|
|
230
|
-
workspaceLastPushedAnnotatedAttachmentSessionIdsRef.current = new Set();
|
|
231
|
-
workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef.current = new Map();
|
|
232
|
-
workspaceLastPushedTaskEventWatermarksRef.current = new Map();
|
|
233
|
-
workspaceStartupFullReconcileRanRef.current = false;
|
|
234
|
-
workspaceFullReconcileInFlightRef.current = false;
|
|
235
|
-
const persisted = readPersistedSyncWatermarks(currentWorkspaceId, taxonomyStateFingerprint);
|
|
236
|
-
if (persisted) {
|
|
237
|
-
workspaceLastPushedWatermarksRef.current = persisted.taskWatermarks;
|
|
238
|
-
workspaceLastPushedInitiativeWatermarksRef.current = persisted.initiativeWatermarks;
|
|
239
|
-
workspaceLastPushedWorkstreamWatermarksRef.current = persisted.workstreamWatermarks;
|
|
240
|
-
workspaceLastPushedDocumentWatermarksRef.current = persisted.documentWatermarks;
|
|
241
|
-
workspaceLastPushedAssetWatermarksRef.current = persisted.assetWatermarks;
|
|
242
|
-
workspaceLastPushedDocumentReviewSessionWatermarksRef.current = persisted.documentReviewSessionWatermarks;
|
|
243
|
-
workspaceLastPushedDocumentReviewCommentWatermarksRef.current = persisted.documentReviewCommentWatermarks;
|
|
244
|
-
workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef.current = persisted.annotatedAttachmentSessionWatermarks;
|
|
245
|
-
workspaceLastPushedTaskEventWatermarksRef.current = persisted.taskEventWatermarks;
|
|
246
|
-
}
|
|
247
|
-
}, [currentWorkspaceId, taxonomyStateFingerprint]);
|
|
248
146
|
const markWorkspaceSyncReferenceSnapshotReady = useCallback((key) => {
|
|
249
147
|
setWorkspaceSyncReferenceSnapshotStatus((current) => (current[key]
|
|
250
148
|
? current
|
|
@@ -254,7 +152,7 @@ export function useSyncOrchestrator(input) {
|
|
|
254
152
|
}));
|
|
255
153
|
}, []);
|
|
256
154
|
const workspaceSyncReferenceSnapshotsReady = Object.values(workspaceSyncReferenceSnapshotStatus).every(Boolean);
|
|
257
|
-
const { workspaceRetryAt, isWorkspaceRetryPending, workspacePushInFlightRef, workspacePullInFlightRef, workspaceLastPushedSignatureRef, workspacePendingSignatureRef, workspaceLastPushedTaskIdsRef, workspaceDeletedTaskIdsRef, workspaceDeletedTaskWatermarksRef, workspacePullCursorRef, clearWorkspaceRetry, scheduleWorkspaceRetry, persistWorkspaceSyncPatch, applyWorkspaceSyncPatchLocally, loadWorkspaceSyncState, applyWorkspaceSyncStateSnapshot: applyWorkspaceSyncStateSnapshotBase, 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({
|
|
258
156
|
currentWorkspaceId,
|
|
259
157
|
setWorkspaceCloudSyncEnabled,
|
|
260
158
|
setWorkspaceSyncPhase,
|
|
@@ -274,12 +172,14 @@ export function useSyncOrchestrator(input) {
|
|
|
274
172
|
applyWorkspaceSyncStateSnapshotBase(nextState);
|
|
275
173
|
}, [applyWorkspaceSyncStateSnapshotBase]);
|
|
276
174
|
const workspaceLastPushedWatermarksRef = useRef(new Map());
|
|
175
|
+
const workspaceLastPushedTaskChangeKeysRef = useRef(new Map());
|
|
277
176
|
const workspaceLastPushedInitiativeIdsRef = useRef(new Set());
|
|
278
177
|
const workspaceLastPushedInitiativeWatermarksRef = useRef(new Map());
|
|
279
178
|
const workspaceLastPushedWorkstreamIdsRef = useRef(new Set());
|
|
280
179
|
const workspaceLastPushedWorkstreamWatermarksRef = useRef(new Map());
|
|
281
180
|
const workspaceLastPushedAiProfileIdsRef = useRef(new Set());
|
|
282
181
|
const workspaceLastPushedAiProfileWatermarksRef = useRef(new Map());
|
|
182
|
+
const workspaceLastPushedAiProfileChangeKeysRef = useRef(new Map());
|
|
283
183
|
const workspaceLastPushedDocumentPathsRef = useRef(new Set());
|
|
284
184
|
const workspaceLastPushedDocumentWatermarksRef = useRef(new Map());
|
|
285
185
|
const workspaceLastPushedAssetPathsRef = useRef(new Set());
|
|
@@ -291,6 +191,48 @@ export function useSyncOrchestrator(input) {
|
|
|
291
191
|
const workspaceLastPushedAnnotatedAttachmentSessionIdsRef = useRef(new Set());
|
|
292
192
|
const workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef = useRef(new Map());
|
|
293
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
|
+
}), []);
|
|
294
236
|
const workspaceStartupRepairRanRef = useRef(false);
|
|
295
237
|
const workspaceStartupFullReconcileRanRef = useRef(false);
|
|
296
238
|
const workspaceRepairModeRef = useRef(false);
|
|
@@ -311,36 +253,22 @@ export function useSyncOrchestrator(input) {
|
|
|
311
253
|
return workspaceLastPushAt;
|
|
312
254
|
return null;
|
|
313
255
|
}, [workspaceLastPullAt, workspaceLastPushAt]);
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
return 'attention';
|
|
322
|
-
}
|
|
323
|
-
return 'healthy';
|
|
324
|
-
}, [
|
|
325
|
-
workspaceCloudSyncEnabled,
|
|
326
|
-
workspaceSyncBusy,
|
|
327
|
-
workspaceSyncPhase,
|
|
328
|
-
workspaceRetryAt,
|
|
329
|
-
workspaceLastErrorMessage
|
|
330
|
-
]);
|
|
331
|
-
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
|
+
}), [
|
|
332
263
|
workspaceCloudSyncEnabled,
|
|
333
264
|
workspaceSyncPhase,
|
|
334
265
|
workspaceSyncBusy,
|
|
335
266
|
workspaceRetryAt,
|
|
336
267
|
workspaceLastErrorMessage
|
|
337
268
|
]);
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
workspaceRetryAt,
|
|
342
|
-
workspaceLastErrorMessage
|
|
343
|
-
]);
|
|
269
|
+
const workspaceSyncStatus = workspaceSyncPresentation.status;
|
|
270
|
+
const workspaceSyncSummary = workspaceSyncPresentation.summary;
|
|
271
|
+
const workspaceSyncRecommendedAction = workspaceSyncPresentation.recommendedAction;
|
|
344
272
|
const getLocalUserSyncUpdatedAt = useCallback((userId) => {
|
|
345
273
|
const key = String(userId || '').trim();
|
|
346
274
|
if (!key)
|
|
@@ -531,6 +459,102 @@ export function useSyncOrchestrator(input) {
|
|
|
531
459
|
const persistWorkspaceSyncPatchSafely = useCallback((patch) => {
|
|
532
460
|
void persistWorkspaceSyncPatchBestEffort(patch).catch(() => { });
|
|
533
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
|
+
]);
|
|
534
558
|
const scheduleWorkspaceSyncRetry = useCallback((minDelayMs) => {
|
|
535
559
|
scheduleWorkspaceRetry(() => retryWorkspaceSyncRef.current(), { minDelayMs });
|
|
536
560
|
}, [scheduleWorkspaceRetry]);
|
|
@@ -592,13 +616,16 @@ export function useSyncOrchestrator(input) {
|
|
|
592
616
|
workspaceArchivedTasksRef.current = archivedTasks || [];
|
|
593
617
|
}, [archivedTasks]);
|
|
594
618
|
useEffect(() => {
|
|
619
|
+
workspaceDeletedTaskIdsRef.current = new Set((deletedTasks || [])
|
|
620
|
+
.map((entry) => String(entry?.taskId || '').trim())
|
|
621
|
+
.filter((taskId) => taskId.length > 0));
|
|
595
622
|
workspaceDeletedTaskWatermarksRef.current = new Map((deletedTasks || [])
|
|
596
623
|
.map((entry) => [
|
|
597
624
|
String(entry?.taskId || '').trim(),
|
|
598
625
|
String(entry?.deletedAt || '').trim()
|
|
599
626
|
])
|
|
600
627
|
.filter(([taskId, deletedAt]) => taskId.length > 0 && Number.isFinite(Date.parse(deletedAt))));
|
|
601
|
-
}, [deletedTasks, workspaceDeletedTaskWatermarksRef]);
|
|
628
|
+
}, [deletedTasks, workspaceDeletedTaskIdsRef, workspaceDeletedTaskWatermarksRef]);
|
|
602
629
|
useEffect(() => {
|
|
603
630
|
workspaceInitiativesRef.current = initiatives || [];
|
|
604
631
|
}, [initiatives]);
|
|
@@ -630,8 +657,11 @@ export function useSyncOrchestrator(input) {
|
|
|
630
657
|
aiProfiles: workspaceSyncAiProfilesRef.current,
|
|
631
658
|
lastPushedAiProfileIds: forceAllAiProfiles ? new Set() : workspaceLastPushedAiProfileIdsRef.current,
|
|
632
659
|
lastPushedAiProfileWatermarks: forceAllAiProfiles ? new Map() : workspaceLastPushedAiProfileWatermarksRef.current,
|
|
660
|
+
lastPushedAiProfileChangeKeys: forceAllAiProfiles ? new Map() : workspaceLastPushedAiProfileChangeKeysRef.current,
|
|
633
661
|
documents: workspaceSyncMarkdownDocumentsRef.current,
|
|
634
662
|
assets: workspaceSyncAssetsRef.current,
|
|
663
|
+
currentDocumentPaths: workspaceSyncKnownDocumentPathsRef.current,
|
|
664
|
+
currentAssetPaths: workspaceSyncKnownAssetPathsRef.current,
|
|
635
665
|
documentReviewSessions: workspaceSyncDocumentReviewSessionsRef.current,
|
|
636
666
|
documentReviewComments: workspaceSyncDocumentReviewCommentsRef.current,
|
|
637
667
|
annotatedAttachmentSessions: workspaceSyncAnnotatedAttachmentSessionsRef.current,
|
|
@@ -646,25 +676,42 @@ export function useSyncOrchestrator(input) {
|
|
|
646
676
|
lastPushedAnnotatedAttachmentSessionIds: workspaceLastPushedAnnotatedAttachmentSessionIdsRef.current,
|
|
647
677
|
lastPushedAnnotatedAttachmentSessionWatermarks: workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef.current,
|
|
648
678
|
lastPushedTaskEventWatermarks: workspaceLastPushedTaskEventWatermarksRef.current,
|
|
649
|
-
|
|
679
|
+
baselineResetReason: workspaceBaselineResetReasonRef.current,
|
|
680
|
+
lastPushedWatermarks: workspaceLastPushedWatermarksRef.current,
|
|
681
|
+
lastPushedTaskChangeKeys: workspaceLastPushedTaskChangeKeysRef.current
|
|
650
682
|
});
|
|
651
683
|
}, [taxonomies, taxonomyState]);
|
|
652
684
|
const reportSyncEvent = useCallback((workspaceId, event) => {
|
|
653
685
|
if (runtimeMode !== 'local')
|
|
654
686
|
return;
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
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) {
|
|
661
708
|
logSyncDebug('sync_event_post_failed', {
|
|
662
709
|
workspaceId,
|
|
663
710
|
eventType: event.eventType,
|
|
664
711
|
status: event.status,
|
|
665
712
|
error: String(error?.message || error || '')
|
|
666
713
|
});
|
|
667
|
-
}
|
|
714
|
+
}
|
|
668
715
|
}, [runtimeMode]);
|
|
669
716
|
const buildWorkspaceSyncSignature = useCallback(() => {
|
|
670
717
|
// Read documents and assets from refs for the same stale-closure reason as
|
|
@@ -728,9 +775,16 @@ export function useSyncOrchestrator(input) {
|
|
|
728
775
|
action: String(event.action || '').trim(),
|
|
729
776
|
}))
|
|
730
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)),
|
|
731
785
|
taxonomies: Array.isArray(taxonomies) ? taxonomies : [],
|
|
732
786
|
taxonomyState: taxonomyState && typeof taxonomyState === 'object' ? taxonomyState : null
|
|
733
|
-
}), [tasks, archivedTasks, initiatives, workstreams, taxonomies, taxonomyState]);
|
|
787
|
+
}), [tasks, archivedTasks, deletedTasks, initiatives, workstreams, taxonomies, taxonomyState]);
|
|
734
788
|
// Fingerprint of per-task attachment counts — changes when the MCP tool saves a new
|
|
735
789
|
// attachment and the React app polls the updated task. Used to trigger an immediate
|
|
736
790
|
// asset snapshot refresh without requiring a cross-process DOM event.
|
|
@@ -797,6 +851,11 @@ export function useSyncOrchestrator(input) {
|
|
|
797
851
|
}))
|
|
798
852
|
.filter((entry) => entry.path.length > 0)
|
|
799
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));
|
|
800
859
|
const fingerprint = typeof data?.fingerprint === 'string'
|
|
801
860
|
? data.fingerprint
|
|
802
861
|
: JSON.stringify(docs.map((entry) => `${entry.path}:${entry.updatedAt}:${entry.content.length}`).sort());
|
|
@@ -804,6 +863,7 @@ export function useSyncOrchestrator(input) {
|
|
|
804
863
|
return;
|
|
805
864
|
workspaceSyncMarkdownDocumentsFingerprintRef.current = fingerprint;
|
|
806
865
|
workspaceSyncMarkdownDocumentsRef.current = docs;
|
|
866
|
+
workspaceSyncKnownDocumentPathsRef.current = knownDocumentPaths;
|
|
807
867
|
setWorkspaceSyncMarkdownDocuments(docs);
|
|
808
868
|
setWorkspaceSyncMarkdownDocumentsFingerprint(fingerprint);
|
|
809
869
|
}
|
|
@@ -853,7 +913,16 @@ export function useSyncOrchestrator(input) {
|
|
|
853
913
|
setWorkspaceSyncAiProfilesLastFetchAt(new Date().toISOString());
|
|
854
914
|
const fingerprint = typeof data?.fingerprint === 'string'
|
|
855
915
|
? data.fingerprint
|
|
856
|
-
: 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());
|
|
857
926
|
if (fingerprint === workspaceSyncAiProfilesFingerprintRef.current)
|
|
858
927
|
return;
|
|
859
928
|
workspaceSyncAiProfilesFingerprintRef.current = fingerprint;
|
|
@@ -910,6 +979,11 @@ export function useSyncOrchestrator(input) {
|
|
|
910
979
|
}))
|
|
911
980
|
.filter((entry) => entry.path.length > 0 && entry.contentBase64.length > 0)
|
|
912
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));
|
|
913
987
|
const fingerprint = typeof data?.fingerprint === 'string'
|
|
914
988
|
? data.fingerprint
|
|
915
989
|
: JSON.stringify(assets.map((entry) => `${entry.path}:${entry.updatedAt}:${entry.contentBase64.length}`).sort());
|
|
@@ -917,6 +991,7 @@ export function useSyncOrchestrator(input) {
|
|
|
917
991
|
return;
|
|
918
992
|
workspaceSyncAssetsFingerprintRef.current = fingerprint;
|
|
919
993
|
workspaceSyncAssetsRef.current = assets;
|
|
994
|
+
workspaceSyncKnownAssetPathsRef.current = knownAssetPaths;
|
|
920
995
|
setWorkspaceSyncAssets(assets);
|
|
921
996
|
setWorkspaceSyncAssetsFingerprint(fingerprint);
|
|
922
997
|
}
|
|
@@ -1059,6 +1134,38 @@ export function useSyncOrchestrator(input) {
|
|
|
1059
1134
|
}
|
|
1060
1135
|
}, [runtimeMode, currentWorkspaceId, cloudAuthConfigured, authSessionResolved, isAuthenticated, markWorkspaceSyncReferenceSnapshotReady]);
|
|
1061
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));
|
|
1062
1169
|
workspaceLastPushedDocumentPathsRef.current = new Set(workspaceSyncMarkdownDocumentsRef.current
|
|
1063
1170
|
.map((entry) => String(entry?.path || '').trim())
|
|
1064
1171
|
.filter((path) => path.length > 0));
|
|
@@ -1092,7 +1199,9 @@ export function useSyncOrchestrator(input) {
|
|
|
1092
1199
|
workspaceLastPushedTaskEventWatermarksRef.current = new Map(flattenTaskEvents(workspaceTasksRef.current, workspaceArchivedTasksRef.current)
|
|
1093
1200
|
.map((entry) => [String(entry?.id || '').trim(), String(entry?.createdAt || '').trim()])
|
|
1094
1201
|
.filter(([id]) => id.length > 0));
|
|
1095
|
-
|
|
1202
|
+
const persisted = persistWorkspaceSyncWatermarksSnapshotBestEffort();
|
|
1203
|
+
workspaceBaselineResetReasonRef.current = persisted ? null : 'storage-write-failed';
|
|
1204
|
+
}, [persistWorkspaceSyncWatermarksSnapshotBestEffort]);
|
|
1096
1205
|
const refreshLocalWorkspaceTaskCollections = useCallback(async () => {
|
|
1097
1206
|
const [tasksRes, archiveRes] = await Promise.all([
|
|
1098
1207
|
fetch('/api/taskforce/tasks', {
|
|
@@ -1163,7 +1272,6 @@ export function useSyncOrchestrator(input) {
|
|
|
1163
1272
|
workspaceCloudSyncEnabled,
|
|
1164
1273
|
currentWorkspaceId,
|
|
1165
1274
|
workspaceSyncPhase,
|
|
1166
|
-
taxonomyStateFingerprint,
|
|
1167
1275
|
isAuthenticated,
|
|
1168
1276
|
checkAuthSession,
|
|
1169
1277
|
resolveCloudAuthUrl,
|
|
@@ -1176,6 +1284,7 @@ export function useSyncOrchestrator(input) {
|
|
|
1176
1284
|
scheduleWorkspaceSyncRetry,
|
|
1177
1285
|
persistWorkspaceSyncPatchSafely,
|
|
1178
1286
|
persistWorkspaceSyncPatchBestEffort,
|
|
1287
|
+
persistWorkspaceSyncWatermarksSnapshotBestEffort,
|
|
1179
1288
|
reportSyncEvent,
|
|
1180
1289
|
acquireWorkspaceSyncLease,
|
|
1181
1290
|
releaseWorkspaceSyncLease,
|
|
@@ -1195,6 +1304,7 @@ export function useSyncOrchestrator(input) {
|
|
|
1195
1304
|
workspaceForceFullAiProfilePushRef,
|
|
1196
1305
|
workspacePushInFlightRef,
|
|
1197
1306
|
workspacePullInFlightRef,
|
|
1307
|
+
workspacePendingPushReplayRef,
|
|
1198
1308
|
workspaceLastPushedSignatureRef,
|
|
1199
1309
|
workspacePendingSignatureRef,
|
|
1200
1310
|
workspaceLastPushedTaskIdsRef,
|
|
@@ -1202,12 +1312,14 @@ export function useSyncOrchestrator(input) {
|
|
|
1202
1312
|
workspaceDeletedTaskWatermarksRef,
|
|
1203
1313
|
workspacePullCursorRef,
|
|
1204
1314
|
workspaceLastPushedWatermarksRef,
|
|
1315
|
+
workspaceLastPushedTaskChangeKeysRef,
|
|
1205
1316
|
workspaceLastPushedInitiativeIdsRef,
|
|
1206
1317
|
workspaceLastPushedInitiativeWatermarksRef,
|
|
1207
1318
|
workspaceLastPushedWorkstreamIdsRef,
|
|
1208
1319
|
workspaceLastPushedWorkstreamWatermarksRef,
|
|
1209
1320
|
workspaceLastPushedAiProfileIdsRef,
|
|
1210
1321
|
workspaceLastPushedAiProfileWatermarksRef,
|
|
1322
|
+
workspaceLastPushedAiProfileChangeKeysRef,
|
|
1211
1323
|
workspaceLastPushedDocumentPathsRef,
|
|
1212
1324
|
workspaceLastPushedDocumentWatermarksRef,
|
|
1213
1325
|
workspaceLastPushedAssetPathsRef,
|
|
@@ -1219,6 +1331,7 @@ export function useSyncOrchestrator(input) {
|
|
|
1219
1331
|
workspaceLastPushedAnnotatedAttachmentSessionIdsRef,
|
|
1220
1332
|
workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef,
|
|
1221
1333
|
workspaceLastPushedTaskEventWatermarksRef,
|
|
1334
|
+
workspaceBaselineResetReasonRef,
|
|
1222
1335
|
workspaceCaptureAttachBootstrapBaselineRef,
|
|
1223
1336
|
workspaceStartupFullReconcileRanRef,
|
|
1224
1337
|
workspaceFullReconcileInFlightRef,
|
|
@@ -1252,6 +1365,7 @@ export function useSyncOrchestrator(input) {
|
|
|
1252
1365
|
scheduleWorkspaceSyncRetry,
|
|
1253
1366
|
persistWorkspaceSyncPatchSafely,
|
|
1254
1367
|
persistWorkspaceSyncPatchBestEffort,
|
|
1368
|
+
persistWorkspaceSyncWatermarksSnapshotBestEffort,
|
|
1255
1369
|
reportSyncEvent,
|
|
1256
1370
|
acquireWorkspaceSyncLease,
|
|
1257
1371
|
releaseWorkspaceSyncLease,
|
|
@@ -1339,7 +1453,6 @@ export function useSyncOrchestrator(input) {
|
|
|
1339
1453
|
}, [syncUserGlobalSettings]);
|
|
1340
1454
|
const recoveryCoordinator = useMemo(() => createSyncRecoveryCoordinator({
|
|
1341
1455
|
currentWorkspaceId,
|
|
1342
|
-
taxonomyStateFingerprint,
|
|
1343
1456
|
workspaceCloudSyncEnabled,
|
|
1344
1457
|
workspaceSyncPhase,
|
|
1345
1458
|
workspaceLastPullAt,
|
|
@@ -1355,12 +1468,14 @@ export function useSyncOrchestrator(input) {
|
|
|
1355
1468
|
workspacePendingSignatureRef,
|
|
1356
1469
|
workspaceLastPushedSignatureRef,
|
|
1357
1470
|
workspaceLastPushedWatermarksRef,
|
|
1471
|
+
workspaceLastPushedTaskChangeKeysRef,
|
|
1358
1472
|
workspaceLastPushedInitiativeIdsRef,
|
|
1359
1473
|
workspaceLastPushedInitiativeWatermarksRef,
|
|
1360
1474
|
workspaceLastPushedWorkstreamIdsRef,
|
|
1361
1475
|
workspaceLastPushedWorkstreamWatermarksRef,
|
|
1362
1476
|
workspaceLastPushedAiProfileIdsRef,
|
|
1363
1477
|
workspaceLastPushedAiProfileWatermarksRef,
|
|
1478
|
+
workspaceLastPushedAiProfileChangeKeysRef,
|
|
1364
1479
|
workspaceLastPushedDocumentPathsRef,
|
|
1365
1480
|
workspaceLastPushedDocumentWatermarksRef,
|
|
1366
1481
|
workspaceLastPushedAssetPathsRef,
|
|
@@ -1372,6 +1487,7 @@ export function useSyncOrchestrator(input) {
|
|
|
1372
1487
|
workspaceLastPushedAnnotatedAttachmentSessionIdsRef,
|
|
1373
1488
|
workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef,
|
|
1374
1489
|
workspaceLastPushedTaskEventWatermarksRef,
|
|
1490
|
+
workspaceBaselineResetReasonRef,
|
|
1375
1491
|
bootstrapPhaseStartedAtRef,
|
|
1376
1492
|
bootstrapLastProgressAtRef,
|
|
1377
1493
|
acquireWorkspaceSyncLease,
|
|
@@ -1380,7 +1496,9 @@ export function useSyncOrchestrator(input) {
|
|
|
1380
1496
|
clearWorkspaceRetry,
|
|
1381
1497
|
clearWorkspaceIssue,
|
|
1382
1498
|
reportWorkspaceWindowContention,
|
|
1499
|
+
reportSyncEvent,
|
|
1383
1500
|
persistWorkspaceSyncPatch,
|
|
1501
|
+
persistWorkspaceSyncWatermarksSnapshotBestEffort,
|
|
1384
1502
|
buildWorkspaceSyncSignature,
|
|
1385
1503
|
pullWorkspaceChangesFromCloud,
|
|
1386
1504
|
pushWorkspaceChangesToCloud,
|
|
@@ -1400,7 +1518,9 @@ export function useSyncOrchestrator(input) {
|
|
|
1400
1518
|
clearWorkspaceRetry,
|
|
1401
1519
|
clearWorkspaceIssue,
|
|
1402
1520
|
reportWorkspaceWindowContention,
|
|
1521
|
+
reportSyncEvent,
|
|
1403
1522
|
persistWorkspaceSyncPatch,
|
|
1523
|
+
persistWorkspaceSyncWatermarksSnapshotBestEffort,
|
|
1404
1524
|
buildWorkspaceSyncSignature,
|
|
1405
1525
|
pullWorkspaceChangesFromCloud,
|
|
1406
1526
|
pushWorkspaceChangesToCloud,
|
|
@@ -1412,6 +1532,39 @@ export function useSyncOrchestrator(input) {
|
|
|
1412
1532
|
const { retryWorkspaceCloudSync, resetWorkspaceSyncCursorAndPull } = recoveryCoordinator;
|
|
1413
1533
|
// === SECTION: Diagnostics & reactive effects ===
|
|
1414
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
|
+
});
|
|
1415
1568
|
return {
|
|
1416
1569
|
workspaceId: currentWorkspaceId,
|
|
1417
1570
|
enabled: workspaceCloudSyncEnabled,
|
|
@@ -1425,6 +1578,13 @@ export function useSyncOrchestrator(input) {
|
|
|
1425
1578
|
pendingChanges: workspaceSyncPendingChanges,
|
|
1426
1579
|
lastErrorMessage: workspaceLastErrorMessage,
|
|
1427
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,
|
|
1428
1588
|
documentSnapshotCount: workspaceSyncMarkdownDocuments.length,
|
|
1429
1589
|
aiProfileSnapshotCount: workspaceSyncAiProfiles.length,
|
|
1430
1590
|
aiProfileSnapshotRawCount: workspaceSyncAiProfilesRawCount,
|
|
@@ -1441,9 +1601,12 @@ export function useSyncOrchestrator(input) {
|
|
|
1441
1601
|
forceFullAiProfilePushQueued: workspaceForceFullAiProfilePushRef.current === true
|
|
1442
1602
|
};
|
|
1443
1603
|
}, [
|
|
1604
|
+
cloudAuthConfigured,
|
|
1444
1605
|
currentWorkspaceId,
|
|
1606
|
+
runtimeMode,
|
|
1445
1607
|
workspaceCloudSyncEnabled,
|
|
1446
1608
|
workspaceSyncPhase,
|
|
1609
|
+
isAuthenticated,
|
|
1447
1610
|
workspaceSyncStatus,
|
|
1448
1611
|
workspaceSyncSummary,
|
|
1449
1612
|
workspaceLastSuccessfulSyncAt,
|
|
@@ -1463,7 +1626,8 @@ export function useSyncOrchestrator(input) {
|
|
|
1463
1626
|
workspaceSyncDocumentReviewSessions.length,
|
|
1464
1627
|
workspaceSyncDocumentReviewSessionsFingerprint,
|
|
1465
1628
|
workspaceSyncAnnotatedAttachmentSessions.length,
|
|
1466
|
-
workspaceSyncAnnotatedAttachmentSessionsFingerprint
|
|
1629
|
+
workspaceSyncAnnotatedAttachmentSessionsFingerprint,
|
|
1630
|
+
isWorkspaceRetryPending
|
|
1467
1631
|
]);
|
|
1468
1632
|
useEffect(() => {
|
|
1469
1633
|
retryWorkspaceSyncRef.current = retryWorkspaceCloudSync;
|
|
@@ -1545,11 +1709,14 @@ export function useSyncOrchestrator(input) {
|
|
|
1545
1709
|
refreshWorkspaceSyncContextAssetSnapshots();
|
|
1546
1710
|
}, [cloudAuthConfigured, runtimeMode, workspaceCloudSyncEnabled, authSessionResolved, isAuthenticated, taskAttachmentsFingerprint, refreshWorkspaceSyncContextAssetSnapshots]);
|
|
1547
1711
|
useEffect(() => {
|
|
1548
|
-
if (!
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1712
|
+
if (!shouldScheduleWorkspaceReactivePush({
|
|
1713
|
+
cloudAuthConfigured,
|
|
1714
|
+
runtimeMode,
|
|
1715
|
+
workspaceCloudSyncEnabled,
|
|
1716
|
+
authSessionResolved,
|
|
1717
|
+
workspaceSyncPhase,
|
|
1718
|
+
workspaceSyncReferenceSnapshotsReady
|
|
1719
|
+
}))
|
|
1553
1720
|
return;
|
|
1554
1721
|
const signature = buildWorkspaceSyncSignature();
|
|
1555
1722
|
if (shouldSuppressAttachBootstrapPush(signature))
|
|
@@ -1587,11 +1754,14 @@ export function useSyncOrchestrator(input) {
|
|
|
1587
1754
|
workspacePullInFlightRef
|
|
1588
1755
|
]);
|
|
1589
1756
|
useEffect(() => {
|
|
1590
|
-
if (!
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1757
|
+
if (!shouldStartWorkspacePushWatchdog({
|
|
1758
|
+
cloudAuthConfigured,
|
|
1759
|
+
runtimeMode,
|
|
1760
|
+
workspaceCloudSyncEnabled,
|
|
1761
|
+
authSessionResolved,
|
|
1762
|
+
workspaceSyncPhase,
|
|
1763
|
+
workspaceSyncReferenceSnapshotsReady
|
|
1764
|
+
}))
|
|
1595
1765
|
return;
|
|
1596
1766
|
const intervalId = window.setInterval(() => {
|
|
1597
1767
|
if (workspacePushInFlightRef.current || workspacePullInFlightRef.current)
|
|
@@ -1633,14 +1803,18 @@ export function useSyncOrchestrator(input) {
|
|
|
1633
1803
|
clearWorkspaceRetry
|
|
1634
1804
|
]);
|
|
1635
1805
|
useEffect(() => {
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
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)
|
|
1641
1816
|
return;
|
|
1642
|
-
|
|
1643
|
-
if (shouldKickOffPull) {
|
|
1817
|
+
if (pullPolicy.shouldKickOffImmediately) {
|
|
1644
1818
|
void pullWorkspaceChangesFromCloud();
|
|
1645
1819
|
}
|
|
1646
1820
|
const intervalId = window.setInterval(() => {
|
|
@@ -1661,9 +1835,14 @@ export function useSyncOrchestrator(input) {
|
|
|
1661
1835
|
workspacePullIntervalMs
|
|
1662
1836
|
]);
|
|
1663
1837
|
useEffect(() => {
|
|
1664
|
-
if (!
|
|
1665
|
-
|
|
1666
|
-
|
|
1838
|
+
if (!shouldStartWorkspacePeriodicFullReconcile({
|
|
1839
|
+
cloudAuthConfigured,
|
|
1840
|
+
runtimeMode,
|
|
1841
|
+
workspaceCloudSyncEnabled,
|
|
1842
|
+
authSessionResolved,
|
|
1843
|
+
isAuthenticated,
|
|
1844
|
+
workspaceSyncPhase
|
|
1845
|
+
}))
|
|
1667
1846
|
return;
|
|
1668
1847
|
const intervalId = window.setInterval(() => {
|
|
1669
1848
|
if (workspaceFullReconcileInFlightRef.current)
|
|
@@ -1726,13 +1905,15 @@ export function useSyncOrchestrator(input) {
|
|
|
1726
1905
|
// missing or stale-tombstoned metadata — a common result of a crash between file write
|
|
1727
1906
|
// and DB upsert. The repair only adds missing records; it never deletes files.
|
|
1728
1907
|
useEffect(() => {
|
|
1729
|
-
if (!
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1908
|
+
if (!shouldRunWorkspaceStartupRepair({
|
|
1909
|
+
cloudAuthConfigured,
|
|
1910
|
+
runtimeMode,
|
|
1911
|
+
workspaceCloudSyncEnabled,
|
|
1912
|
+
authSessionResolved,
|
|
1913
|
+
isAuthenticated,
|
|
1914
|
+
workspaceSyncPhase,
|
|
1915
|
+
alreadyRan: workspaceStartupRepairRanRef.current
|
|
1916
|
+
}))
|
|
1736
1917
|
return;
|
|
1737
1918
|
workspaceStartupRepairRanRef.current = true;
|
|
1738
1919
|
fetch('/api/taskforce/sync/workspace/repair-startup', {
|