@taskforcehq/taskforce 0.3.303 → 0.3.304

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/Taskforce.module.css +102 -5
  2. package/dist/TaskforceCore.js +514 -212
  3. package/dist/TaskforceCore.test.js +1172 -133
  4. package/dist/annotatedAttachments/service.d.ts +1 -1
  5. package/dist/annotatedAttachments/types.d.ts +1 -1
  6. package/dist/components/features/AgentsModule.js +1 -1
  7. package/dist/components/features/AgentsModule.test.js +1 -1
  8. package/dist/components/features/TaskSettings.js +58 -38
  9. package/dist/components/features/TaskSettings.test.js +139 -40
  10. package/dist/components/task/TaskActionHeader.d.ts +2 -1
  11. package/dist/components/task/TaskActionHeader.js +3 -3
  12. package/dist/components/task/TaskActionHeader.test.js +8 -0
  13. package/dist/components/task/TaskCard.js +16 -1
  14. package/dist/components/task/TaskCard.test.js +31 -0
  15. package/dist/components/task/TaskForm.d.ts +2 -1
  16. package/dist/components/task/TaskForm.js +22 -4
  17. package/dist/components/task/TaskForm.test.js +46 -0
  18. package/dist/components/task/TaskKanban.d.ts +1 -1
  19. package/dist/components/task/TaskKanban.js +31 -160
  20. package/dist/components/task/TaskStatusActions.d.ts +6 -1
  21. package/dist/components/task/TaskStatusActions.js +8 -3
  22. package/dist/components/task/TaskStatusActions.test.js +9 -0
  23. package/dist/components/task/taskKanbanDropRules.d.ts +58 -0
  24. package/dist/components/task/taskKanbanDropRules.js +187 -0
  25. package/dist/components/task/taskKanbanDropRules.test.d.ts +1 -0
  26. package/dist/components/task/taskKanbanDropRules.test.js +114 -0
  27. package/dist/components/ui/Modal.d.ts +1 -1
  28. package/dist/components/ui/Modal.js +1 -0
  29. package/dist/components/views/AppShellHeader.js +5 -1
  30. package/dist/components/views/AppShellHeader.test.js +4 -0
  31. package/dist/components/views/PlanComparisonPage.d.ts +3 -2
  32. package/dist/components/views/PlanComparisonPage.js +4 -2
  33. package/dist/components/views/PlanComparisonPage.test.js +4 -0
  34. package/dist/components/views/PlansPage.d.ts +5 -1
  35. package/dist/components/views/PlansPage.js +39 -12
  36. package/dist/components/views/PlansPage.test.js +59 -2
  37. package/dist/components/views/StandaloneLayout.js +113 -38
  38. package/dist/components/views/WidgetView.js +11 -2
  39. package/dist/components/views/standalone/modals/SyncStatusModal.js +1 -1
  40. package/dist/core/DeletedTaskLifecycleService.d.ts +2 -1
  41. package/dist/core/PlanEntitlementService.d.ts +3 -1
  42. package/dist/core/PlanEntitlementService.js +61 -11
  43. package/dist/core/SignupMonetizationSettings.test.js +37 -2
  44. package/dist/core/TaskActivityService.d.ts +4 -1
  45. package/dist/core/TaskActivityService.js +45 -0
  46. package/dist/core/TaskAuditTimeline.test.js +52 -1
  47. package/dist/core/Taskforce.d.ts +15 -11
  48. package/dist/core/Taskforce.js +43 -2
  49. package/dist/core/WorkspacePlanMode.test.js +65 -0
  50. package/dist/documentReviews/service.d.ts +1 -1
  51. package/dist/documentReviews/types.d.ts +1 -1
  52. package/dist/hooks/auth/useTaskforceAuthBootstrap.d.ts +48 -0
  53. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +219 -0
  54. package/dist/hooks/sync/auth.d.ts +37 -0
  55. package/dist/hooks/sync/auth.js +61 -0
  56. package/dist/hooks/sync/bootstrap.d.ts +34 -0
  57. package/dist/hooks/sync/bootstrap.js +52 -0
  58. package/dist/hooks/sync/orchestratorShared.d.ts +36 -0
  59. package/dist/hooks/sync/orchestratorShared.js +209 -0
  60. package/dist/hooks/sync/orchestratorShared.test.d.ts +1 -0
  61. package/dist/hooks/sync/orchestratorShared.test.js +49 -0
  62. package/dist/hooks/sync/realtime.d.ts +25 -0
  63. package/dist/hooks/sync/realtime.js +60 -0
  64. package/dist/hooks/sync/recovery.d.ts +69 -0
  65. package/dist/hooks/sync/recovery.js +118 -0
  66. package/dist/hooks/sync/transfers.d.ts +122 -0
  67. package/dist/hooks/sync/transfers.js +431 -0
  68. package/dist/hooks/sync/useDataVersionRefresh.d.ts +16 -0
  69. package/dist/hooks/sync/useDataVersionRefresh.js +73 -0
  70. package/dist/hooks/ui/useResourceExport.d.ts +3 -1
  71. package/dist/hooks/ui/useResourceExport.js +5 -2
  72. package/dist/hooks/ui/useWorkflowTemplates.js +11 -6
  73. package/dist/hooks/useRealtimeSync.js +2 -1
  74. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +8 -0
  75. package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +1 -0
  76. package/dist/hooks/useSyncOrchestrator.context-assets.test.js +228 -0
  77. package/dist/hooks/useSyncOrchestrator.d.ts +5 -4
  78. package/dist/hooks/useSyncOrchestrator.js +314 -873
  79. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +568 -3
  80. package/dist/hooks/useTaskMutations.js +45 -6
  81. package/dist/hooks/useTaskMutations.test.js +147 -0
  82. package/dist/hooks/useTaskforce.d.ts +8 -2
  83. package/dist/hooks/useTaskforce.js +285 -812
  84. package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
  85. package/dist/hooks/useWorkspaceSyncController.js +22 -10
  86. package/dist/hooks/workspace/useTaskforceApiRouting.test.js +17 -0
  87. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +166 -0
  88. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +434 -0
  89. package/dist/hooks/workspace/workspaceLocalState.d.ts +19 -0
  90. package/dist/hooks/workspace/workspaceLocalState.js +100 -0
  91. package/dist/hooks/workspace/workspaceLocalState.test.d.ts +1 -0
  92. package/dist/hooks/workspace/workspaceLocalState.test.js +59 -0
  93. package/dist/localization/locales/en-US.js +2 -2
  94. package/dist/mcp/canonicalAssetHelpers.d.ts +70 -0
  95. package/dist/mcp/canonicalAssetHelpers.js +259 -0
  96. package/dist/mcp/clientIntegrations.d.ts +12 -1
  97. package/dist/mcp/clientIntegrations.js +161 -6
  98. package/dist/mcp/clientIntegrations.test.js +76 -4
  99. package/dist/mcp/documentHelpers.d.ts +100 -0
  100. package/dist/mcp/documentHelpers.js +161 -0
  101. package/dist/mcp/runtime.js +174 -476
  102. package/dist/mcp/runtime.test.js +81 -8
  103. package/dist/mcp/taskAttachmentHelpers.d.ts +41 -0
  104. package/dist/mcp/taskAttachmentHelpers.js +60 -0
  105. package/dist/mcp/taskAttachmentTypes.d.ts +37 -0
  106. package/dist/mcp/taskAttachmentTypes.js +1 -0
  107. package/dist/plugins/vite.js +1 -1
  108. package/dist/runtime/appGateDefinitions.d.ts +1 -1
  109. package/dist/runtime/appGateDefinitions.js +1 -1
  110. package/dist/server/index.d.ts +1 -0
  111. package/dist/server/index.js +46 -3
  112. package/dist/server/index.test.js +12 -1
  113. package/dist/server/routes/admin.js +13 -19
  114. package/dist/server/routes/auth.js +37 -2
  115. package/dist/server/routes/billing.js +18 -67
  116. package/dist/server/routes/billing.test.js +43 -0
  117. package/dist/server/routes/shared.d.ts +30 -0
  118. package/dist/server/routes/shared.js +26 -0
  119. package/dist/server/routes/sync.integration.test.js +20 -0
  120. package/dist/server/routes/sync.js +110 -237
  121. package/dist/server/routes/tasks.js +49 -7
  122. package/dist/server/routes.js +8 -19
  123. package/dist/server/routes.test.js +243 -5
  124. package/dist/shared/taskActor.d.ts +10 -0
  125. package/dist/shared/taskActor.js +1 -0
  126. package/dist/sync/collaborationSyncPayload.js +4 -0
  127. package/dist/sync/collaborationSyncPayload.test.js +8 -0
  128. package/dist/sync/collaborationSyncState.d.ts +3 -1
  129. package/dist/sync/collaborationSyncState.js +19 -0
  130. package/dist/sync/contentSyncState.js +12 -10
  131. package/dist/sync/syncApplyHandlers.d.ts +87 -1
  132. package/dist/sync/syncApplyHandlers.js +370 -8
  133. package/dist/sync/syncApplyHandlers.test.d.ts +1 -0
  134. package/dist/sync/syncApplyHandlers.test.js +377 -0
  135. package/dist/sync/syncService.d.ts +8 -1
  136. package/dist/sync/syncService.js +36 -2
  137. package/dist/sync/syncService.test.js +48 -0
  138. package/dist/sync/workspacePullFeed.d.ts +1 -0
  139. package/dist/sync/workspacePullFeed.js +66 -59
  140. package/dist/sync/workspacePullFeed.test.js +72 -4
  141. package/dist/sync/workspaceSyncModel.d.ts +22 -1
  142. package/dist/sync/workspaceSyncModel.js +65 -0
  143. package/dist/sync/workspaceSyncModel.test.js +119 -0
  144. package/dist/types.d.ts +2 -10
  145. package/dist/ui/assets/AgentsModule-BLWVbuKP.js +1 -0
  146. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-C_TmXZwA.js → AnnotatedAttachmentWorkspace-BlS-cqnl.js} +1 -1
  147. package/dist/ui/assets/{DocumentWorkspace-C3GyzrWm.js → DocumentWorkspace-BH_6DF6x.js} +1 -1
  148. package/dist/ui/assets/{InitiativesModule-4-Rh2K2n.js → InitiativesModule-BjR6iBf9.js} +1 -1
  149. package/dist/ui/assets/{PlansPage-BlVC_lRq.css → PlansPage-C2dd2n1X.css} +1 -1
  150. package/dist/ui/assets/PlansPage-CSDQ4sLa.js +1 -0
  151. package/dist/ui/assets/TaskSettings-CEDy34Jo.js +9 -0
  152. package/dist/ui/assets/{WorkflowsModule-CGk9s3NJ.js → WorkflowsModule-DnFqdUME.js} +1 -1
  153. package/dist/ui/assets/index-DXUdtH1B.js +6 -0
  154. package/dist/ui/assets/index-Ii0B0rTO.css +1 -0
  155. package/dist/ui/assets/{vendor-icons-pWocEipT.js → vendor-icons-I7ZwG1z_.js} +1 -1
  156. package/dist/ui/index.html +4 -4
  157. package/dist/utils/taskActivity.js +4 -0
  158. package/dist/utils/taskActivity.test.js +25 -1
  159. package/package.json +3 -1
  160. package/scripts/playwright-auth.sh +4 -0
  161. package/dist/ui/assets/AgentsModule-CpsTmrIW.js +0 -1
  162. package/dist/ui/assets/PlansPage-BP7AYOqr.js +0 -1
  163. package/dist/ui/assets/TaskSettings-BOC5F6Ag.js +0 -8
  164. package/dist/ui/assets/index-CLIMgR6g.js +0 -6
  165. package/dist/ui/assets/index-DneETxcu.css +0 -1
@@ -0,0 +1,61 @@
1
+ import { isTransientWorkspaceSyncStatus } from '../../sync/syncService';
2
+ import { isValidWorkspaceKey } from './orchestratorShared';
3
+ export function createSyncAuthCoordinator({ currentWorkspaceId, workspaceSyncPhase, clearWorkspaceRetry, setWorkspaceSyncBusy, setUserGlobalSyncStatus, setUserGlobalSyncError, setWorkspaceLastErrorMessage, setWorkspaceLastErrorAt, reportSyncEvent, setAuthBlocked, setIsAuthenticated, checkAuthSession, ensureCloudWorkspaceReadyForSync, scheduleWorkspaceSyncRetry, persistWorkspaceSyncPatchSafely }) {
4
+ const handleSyncAuthFailure = async (source, statusCode) => {
5
+ clearWorkspaceRetry();
6
+ setWorkspaceSyncBusy(false);
7
+ setUserGlobalSyncStatus('error');
8
+ const message = 'Session expired. Sign in again to resume cloud sync.';
9
+ setUserGlobalSyncError(message);
10
+ setWorkspaceLastErrorMessage(message);
11
+ setWorkspaceLastErrorAt(new Date().toISOString());
12
+ reportSyncEvent(currentWorkspaceId, {
13
+ eventType: source === 'handshake' ? 'handshake' : source,
14
+ status: 'error',
15
+ statusCode,
16
+ errorMessage: message
17
+ });
18
+ setAuthBlocked(true);
19
+ setIsAuthenticated(false);
20
+ await checkAuthSession();
21
+ };
22
+ const ensureWorkspaceSyncReady = async () => {
23
+ if (!isValidWorkspaceKey(currentWorkspaceId)) {
24
+ const message = 'Workspace sync blocked: local workspace ID is unresolved.';
25
+ setWorkspaceLastErrorMessage(message);
26
+ setWorkspaceLastErrorAt(new Date().toISOString());
27
+ setUserGlobalSyncError(message);
28
+ setUserGlobalSyncStatus('error');
29
+ return false;
30
+ }
31
+ const ensured = await ensureCloudWorkspaceReadyForSync();
32
+ if (ensured.success)
33
+ return true;
34
+ if (ensured.statusCode === 401) {
35
+ await handleSyncAuthFailure('handshake', 401);
36
+ return false;
37
+ }
38
+ const message = ensured.error || 'Workspace sync handshake failed.';
39
+ setWorkspaceLastErrorMessage(message);
40
+ setWorkspaceLastErrorAt(new Date().toISOString());
41
+ setUserGlobalSyncError(message);
42
+ setUserGlobalSyncStatus('error');
43
+ reportSyncEvent(currentWorkspaceId, {
44
+ eventType: 'handshake',
45
+ status: 'error',
46
+ statusCode: ensured.statusCode,
47
+ errorMessage: message
48
+ });
49
+ if (ensured.transient || isTransientWorkspaceSyncStatus(ensured.statusCode)) {
50
+ scheduleWorkspaceSyncRetry(ensured.retryAfterMs);
51
+ }
52
+ else if (workspaceSyncPhase === 'active') {
53
+ persistWorkspaceSyncPatchSafely({ phase: 'error', lastErrorMessage: message });
54
+ }
55
+ return false;
56
+ };
57
+ return {
58
+ handleSyncAuthFailure,
59
+ ensureWorkspaceSyncReady
60
+ };
61
+ }
@@ -0,0 +1,34 @@
1
+ import type { MutableRefObject } from 'react';
2
+ import type { WorkspaceSyncPhase } from '../../sync/workspaceSyncState';
3
+ interface BootstrapPhaseTrackingArgs {
4
+ workspaceSyncPhase: WorkspaceSyncPhase;
5
+ lastBootstrapPhaseRef: MutableRefObject<WorkspaceSyncPhase>;
6
+ bootstrapPhaseStartedAtRef: MutableRefObject<number | null>;
7
+ bootstrapLastProgressAtRef: MutableRefObject<number | null>;
8
+ }
9
+ export declare function syncBootstrapPhaseTracking({ workspaceSyncPhase, lastBootstrapPhaseRef, bootstrapPhaseStartedAtRef, bootstrapLastProgressAtRef }: BootstrapPhaseTrackingArgs): void;
10
+ interface BootstrapTimeoutMonitorArgs {
11
+ currentWorkspaceId: string;
12
+ workspaceCloudSyncEnabled: boolean;
13
+ workspaceSyncPhase: WorkspaceSyncPhase;
14
+ workspacePullInFlightRef: MutableRefObject<boolean>;
15
+ workspacePushInFlightRef: MutableRefObject<boolean>;
16
+ bootstrapPhaseStartedAtRef: MutableRefObject<number | null>;
17
+ bootstrapLastProgressAtRef: MutableRefObject<number | null>;
18
+ setWorkspaceLastErrorMessage: (value: string) => void;
19
+ setWorkspaceLastErrorAt: (value: string) => void;
20
+ setUserGlobalSyncStatus: (value: 'error') => void;
21
+ setUserGlobalSyncError: (value: string) => void;
22
+ setWorkspaceSyncBusy: (value: boolean) => void;
23
+ persistWorkspaceSyncPatchSafely: (patch: {
24
+ phase: 'error';
25
+ lastErrorMessage: string;
26
+ }) => void;
27
+ }
28
+ export declare function createBootstrapTimeoutMonitor({ currentWorkspaceId, workspaceCloudSyncEnabled, workspaceSyncPhase, workspacePullInFlightRef, workspacePushInFlightRef, bootstrapPhaseStartedAtRef, bootstrapLastProgressAtRef, setWorkspaceLastErrorMessage, setWorkspaceLastErrorAt, setUserGlobalSyncStatus, setUserGlobalSyncError, setWorkspaceSyncBusy, persistWorkspaceSyncPatchSafely }: BootstrapTimeoutMonitorArgs): (() => void) | undefined;
29
+ export declare function resolveRepairPhase(args: {
30
+ workspaceSyncPhase: WorkspaceSyncPhase;
31
+ lastBootstrapPhase: WorkspaceSyncPhase;
32
+ workspaceLastPullAt: string | null;
33
+ }): WorkspaceSyncPhase;
34
+ export {};
@@ -0,0 +1,52 @@
1
+ import { WORKSPACE_BOOTSTRAP_TIMEOUT_MS, logSyncDebug } from './orchestratorShared';
2
+ export function syncBootstrapPhaseTracking({ workspaceSyncPhase, lastBootstrapPhaseRef, bootstrapPhaseStartedAtRef, bootstrapLastProgressAtRef }) {
3
+ if (workspaceSyncPhase === 'attach-cloud' || workspaceSyncPhase === 'provision-local') {
4
+ lastBootstrapPhaseRef.current = workspaceSyncPhase;
5
+ if (bootstrapPhaseStartedAtRef.current === null) {
6
+ bootstrapPhaseStartedAtRef.current = Date.now();
7
+ }
8
+ bootstrapLastProgressAtRef.current = null;
9
+ return;
10
+ }
11
+ bootstrapPhaseStartedAtRef.current = null;
12
+ bootstrapLastProgressAtRef.current = null;
13
+ }
14
+ export function createBootstrapTimeoutMonitor({ currentWorkspaceId, workspaceCloudSyncEnabled, workspaceSyncPhase, workspacePullInFlightRef, workspacePushInFlightRef, bootstrapPhaseStartedAtRef, bootstrapLastProgressAtRef, setWorkspaceLastErrorMessage, setWorkspaceLastErrorAt, setUserGlobalSyncStatus, setUserGlobalSyncError, setWorkspaceSyncBusy, persistWorkspaceSyncPatchSafely }) {
15
+ if (!workspaceCloudSyncEnabled)
16
+ return undefined;
17
+ if (workspaceSyncPhase !== 'attach-cloud' && workspaceSyncPhase !== 'provision-local')
18
+ return undefined;
19
+ const intervalId = window.setInterval(() => {
20
+ if (workspaceSyncPhase !== 'attach-cloud' && workspaceSyncPhase !== 'provision-local')
21
+ return;
22
+ if (workspacePullInFlightRef.current || workspacePushInFlightRef.current)
23
+ return;
24
+ const lastActivity = Math.max(bootstrapPhaseStartedAtRef.current ?? 0, bootstrapLastProgressAtRef.current ?? 0);
25
+ if (lastActivity > 0 && (Date.now() - lastActivity) < WORKSPACE_BOOTSTRAP_TIMEOUT_MS)
26
+ return;
27
+ const phaseLabel = workspaceSyncPhase === 'attach-cloud' ? 'initial cloud pull' : 'initial cloud upload';
28
+ const message = `Workspace sync ${phaseLabel} stalled. Press Repair to restart sync for this workspace.`;
29
+ setWorkspaceLastErrorMessage(message);
30
+ setWorkspaceLastErrorAt(new Date().toISOString());
31
+ setUserGlobalSyncStatus('error');
32
+ setUserGlobalSyncError(message);
33
+ setWorkspaceSyncBusy(false);
34
+ persistWorkspaceSyncPatchSafely({ phase: 'error', lastErrorMessage: message });
35
+ logSyncDebug('workspace_sync_bootstrap_timeout', {
36
+ workspaceId: currentWorkspaceId,
37
+ phase: workspaceSyncPhase
38
+ });
39
+ }, 10_000);
40
+ return () => window.clearInterval(intervalId);
41
+ }
42
+ export function resolveRepairPhase(args) {
43
+ const { workspaceSyncPhase, lastBootstrapPhase, workspaceLastPullAt } = args;
44
+ if (workspaceSyncPhase === 'provision-local')
45
+ return 'provision-local';
46
+ if (workspaceSyncPhase === 'attach-cloud')
47
+ return 'attach-cloud';
48
+ if (lastBootstrapPhase === 'provision-local' || lastBootstrapPhase === 'attach-cloud') {
49
+ return lastBootstrapPhase;
50
+ }
51
+ return workspaceLastPullAt ? 'attach-cloud' : 'provision-local';
52
+ }
@@ -0,0 +1,36 @@
1
+ import type { WorkspaceTaxonomyState } from '../../shared/taxonomyState.js';
2
+ import type { WorkspaceSyncAiProfileSnapshot, WorkspaceSyncDocumentReviewCommentSnapshot, WorkspaceSyncDocumentReviewSessionSnapshot, WorkspaceSyncTaskEventSnapshot } from '../../sync/workspaceSyncModel';
3
+ import type { TaskItem } from '../../types';
4
+ export declare const USER_GLOBAL_SYNC_META_KEY = "taskforce.userGlobalSyncMeta.v1";
5
+ export declare const SYNC_DEBUG_LOG_KEY = "taskforce.syncDebug.v1";
6
+ export declare const WORKSPACE_BOOTSTRAP_TIMEOUT_MS = 120000;
7
+ export declare const WORKSPACE_SYNC_WATERMARKS_KEY_PREFIX = "taskforce.syncWatermarks.v3";
8
+ export declare const WORKSPACE_FULL_RECONCILE_INTERVAL_MS: number;
9
+ export declare const WORKSPACE_CONCURRENT_WINDOW_MESSAGE = "Another local window is already syncing this workspace. Wait a few seconds, or press Repair in that window.";
10
+ export declare function isValidWorkspaceKey(workspaceId: string): boolean;
11
+ export declare function logSyncDebug(event: string, details?: Record<string, unknown>): void;
12
+ export declare function normalizeWorkspaceSyncAiProfileSnapshotEntry(entry: any): WorkspaceSyncAiProfileSnapshot;
13
+ export declare function readUserGlobalSyncMeta(): Record<string, {
14
+ updatedAt: string;
15
+ }>;
16
+ export declare function writeUserGlobalSyncMeta(next: Record<string, {
17
+ updatedAt: string;
18
+ }>): void;
19
+ export declare function buildTaxonomyStateFingerprint(taxonomyState?: WorkspaceTaxonomyState): string;
20
+ export declare function flattenDocumentReviewComments(sessions: WorkspaceSyncDocumentReviewSessionSnapshot[]): WorkspaceSyncDocumentReviewCommentSnapshot[];
21
+ export declare function flattenTaskEvents(tasks: TaskItem[], archivedTasks: TaskItem[]): WorkspaceSyncTaskEventSnapshot[];
22
+ export interface PersistedSyncWatermarks {
23
+ taskWatermarks: Map<string, string>;
24
+ initiativeWatermarks: Map<string, string>;
25
+ workstreamWatermarks: Map<string, string>;
26
+ documentWatermarks: Map<string, string>;
27
+ assetWatermarks: Map<string, string>;
28
+ documentReviewSessionWatermarks: Map<string, string>;
29
+ documentReviewCommentWatermarks: Map<string, string>;
30
+ annotatedAttachmentSessionWatermarks: Map<string, string>;
31
+ taskEventWatermarks: Map<string, string>;
32
+ }
33
+ export declare function readPersistedSyncWatermarks(workspaceId: string, taxonomyStateFingerprint?: string): PersistedSyncWatermarks | null;
34
+ export declare function savePersistedSyncWatermarks(workspaceId: string, data: PersistedSyncWatermarks & {
35
+ taxonomyStateFingerprint?: string;
36
+ }): void;
@@ -0,0 +1,209 @@
1
+ import { parseAiProfileSurfaceType } from '../../shared/aiProfileSurfaceType.js';
2
+ export const USER_GLOBAL_SYNC_META_KEY = 'taskforce.userGlobalSyncMeta.v1';
3
+ export const SYNC_DEBUG_LOG_KEY = 'taskforce.syncDebug.v1';
4
+ export const WORKSPACE_BOOTSTRAP_TIMEOUT_MS = 120_000;
5
+ export const WORKSPACE_SYNC_WATERMARKS_KEY_PREFIX = 'taskforce.syncWatermarks.v3';
6
+ export const WORKSPACE_FULL_RECONCILE_INTERVAL_MS = 5 * 60 * 1000;
7
+ export const WORKSPACE_CONCURRENT_WINDOW_MESSAGE = 'Another local window is already syncing this workspace. Wait a few seconds, or press Repair in that window.';
8
+ export function isValidWorkspaceKey(workspaceId) {
9
+ const workspaceKey = String(workspaceId || '').trim();
10
+ return workspaceKey.length > 0 && workspaceKey.toLowerCase() !== 'default';
11
+ }
12
+ function isSyncDebugLoggingEnabled() {
13
+ if (typeof window === 'undefined')
14
+ return false;
15
+ try {
16
+ const raw = String(window.localStorage.getItem(SYNC_DEBUG_LOG_KEY) || '').trim().toLowerCase();
17
+ return raw === '1' || raw === 'true' || raw === 'on' || raw === 'yes';
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ }
23
+ export function logSyncDebug(event, details) {
24
+ if (!isSyncDebugLoggingEnabled())
25
+ return;
26
+ const payload = details && typeof details === 'object' ? details : {};
27
+ console.info('[Taskforce Sync]', event, payload);
28
+ }
29
+ export function normalizeWorkspaceSyncAiProfileSnapshotEntry(entry) {
30
+ const metadata = entry?.providerMetadata ?? entry?.provider_metadata ?? null;
31
+ return {
32
+ id: String(entry?.id || '').trim(),
33
+ workspaceId: String(entry?.workspaceId || entry?.workspace_id || '').trim(),
34
+ profileToken: String(entry?.profileToken || entry?.profile_token || '').trim(),
35
+ name: String(entry?.name || '').trim(),
36
+ username: String(entry?.username || '').trim(),
37
+ icon: String(entry?.icon || 'Bot').trim() || 'Bot',
38
+ color: String(entry?.color || '#8b5cf6').trim() || '#8b5cf6',
39
+ description: typeof entry?.description === 'string' && entry.description.trim().length > 0
40
+ ? entry.description.trim()
41
+ : null,
42
+ role: typeof entry?.role === 'string' && entry.role.trim().length > 0
43
+ ? entry.role.trim()
44
+ : null,
45
+ provider: typeof entry?.provider === 'string' && entry.provider.trim().length > 0
46
+ ? entry.provider.trim()
47
+ : null,
48
+ model: typeof entry?.model === 'string' && entry.model.trim().length > 0
49
+ ? entry.model.trim()
50
+ : null,
51
+ surfaceType: parseAiProfileSurfaceType(entry?.surfaceType ?? entry?.surface_type),
52
+ providerMetadata: metadata && typeof metadata === 'object' && !Array.isArray(metadata)
53
+ ? metadata
54
+ : null,
55
+ createdAt: String(entry?.createdAt || entry?.created_at || '').trim(),
56
+ updatedAt: String(entry?.updatedAt || entry?.updated_at || entry?.createdAt || entry?.created_at || '').trim(),
57
+ lastActiveAt: typeof entry?.lastActiveAt === 'string' && entry.lastActiveAt.trim().length > 0
58
+ ? entry.lastActiveAt.trim()
59
+ : typeof entry?.last_active_at === 'string' && entry.last_active_at.trim().length > 0
60
+ ? entry.last_active_at.trim()
61
+ : null
62
+ };
63
+ }
64
+ export function readUserGlobalSyncMeta() {
65
+ if (typeof window === 'undefined')
66
+ return {};
67
+ try {
68
+ const raw = window.localStorage.getItem(USER_GLOBAL_SYNC_META_KEY);
69
+ if (!raw)
70
+ return {};
71
+ const parsed = JSON.parse(raw);
72
+ return parsed && typeof parsed === 'object'
73
+ ? parsed
74
+ : {};
75
+ }
76
+ catch {
77
+ return {};
78
+ }
79
+ }
80
+ export function writeUserGlobalSyncMeta(next) {
81
+ if (typeof window === 'undefined')
82
+ return;
83
+ try {
84
+ window.localStorage.setItem(USER_GLOBAL_SYNC_META_KEY, JSON.stringify(next));
85
+ }
86
+ catch {
87
+ // best-effort only
88
+ }
89
+ }
90
+ export function buildTaxonomyStateFingerprint(taxonomyState) {
91
+ try {
92
+ return JSON.stringify(taxonomyState && typeof taxonomyState === 'object' ? taxonomyState : null);
93
+ }
94
+ catch {
95
+ return 'null';
96
+ }
97
+ }
98
+ export function flattenDocumentReviewComments(sessions) {
99
+ return sessions.flatMap((session) => {
100
+ const sessionId = String(session?.id || '').trim();
101
+ if (!sessionId || !Array.isArray(session?.comments))
102
+ return [];
103
+ return session.comments
104
+ .map((comment) => ({
105
+ id: String(comment?.id || '').trim(),
106
+ sessionId,
107
+ body: String(comment?.body || ''),
108
+ anchor: comment?.anchor ?? null,
109
+ order: Number.isFinite(Number(comment?.order)) ? Math.max(0, Math.floor(Number(comment.order))) : 0,
110
+ authorActorId: typeof comment?.authorActorId === 'string' && comment.authorActorId.trim().length > 0 ? comment.authorActorId.trim() : null,
111
+ createdAt: String(comment?.createdAt || '').trim(),
112
+ updatedAt: String(comment?.updatedAt || comment?.createdAt || '').trim(),
113
+ deletedAt: typeof comment?.deletedAt === 'string' && comment.deletedAt.trim().length > 0 ? comment.deletedAt.trim() : null,
114
+ }))
115
+ .filter((comment) => comment.id.length > 0 && comment.sessionId.length > 0);
116
+ });
117
+ }
118
+ export function flattenTaskEvents(tasks, archivedTasks) {
119
+ const deduped = new Map();
120
+ for (const task of [...(tasks || []), ...(archivedTasks || [])]) {
121
+ const taskEvents = Array.isArray(task?.taskEvents) ? task.taskEvents : [];
122
+ for (const event of taskEvents) {
123
+ const id = String(event?.id || '').trim();
124
+ const taskId = String(event?.taskId || task?.id || '').trim();
125
+ const workspaceId = String(event?.workspaceId || '').trim();
126
+ const createdAt = String(event?.createdAt || '').trim();
127
+ if (!id || !taskId || !workspaceId || !createdAt)
128
+ continue;
129
+ const nextEvent = {
130
+ id,
131
+ taskId,
132
+ workspaceId,
133
+ action: String(event?.action || '').trim(),
134
+ actor: String(event?.actor || 'system').trim() || 'system',
135
+ actorType: event?.actorType === 'ai' || event?.actorType === 'human' || event?.actorType === 'system'
136
+ ? event.actorType
137
+ : 'system',
138
+ details: event?.details,
139
+ createdAt,
140
+ };
141
+ const existing = deduped.get(id);
142
+ if (!existing || nextEvent.createdAt > existing.createdAt) {
143
+ deduped.set(id, nextEvent);
144
+ }
145
+ }
146
+ }
147
+ return Array.from(deduped.values()).sort((left, right) => {
148
+ if (left.createdAt !== right.createdAt)
149
+ return left.createdAt.localeCompare(right.createdAt);
150
+ return left.id.localeCompare(right.id);
151
+ });
152
+ }
153
+ export function readPersistedSyncWatermarks(workspaceId, taxonomyStateFingerprint) {
154
+ if (typeof window === 'undefined')
155
+ return null;
156
+ try {
157
+ const key = `${WORKSPACE_SYNC_WATERMARKS_KEY_PREFIX}.${workspaceId}`;
158
+ const raw = window.localStorage.getItem(key);
159
+ if (!raw)
160
+ return null;
161
+ const parsed = JSON.parse(raw);
162
+ if (!parsed || (parsed.v !== 2 && parsed.v !== 3 && parsed.v !== 4 && parsed.v !== 5 && parsed.v !== 6 && parsed.v !== 7 && parsed.v !== 8))
163
+ return null;
164
+ const savedTaxonomyStateFingerprint = typeof parsed.taxonomyStateFingerprint === 'string'
165
+ ? parsed.taxonomyStateFingerprint
166
+ : '';
167
+ const taskWatermarks = (!taxonomyStateFingerprint || !savedTaxonomyStateFingerprint || savedTaxonomyStateFingerprint === taxonomyStateFingerprint)
168
+ ? new Map(Array.isArray(parsed.taskWatermarks) ? parsed.taskWatermarks : [])
169
+ : new Map();
170
+ return {
171
+ taskWatermarks,
172
+ initiativeWatermarks: new Map(Array.isArray(parsed.initiativeWatermarks) ? parsed.initiativeWatermarks : []),
173
+ workstreamWatermarks: new Map(Array.isArray(parsed.workstreamWatermarks) ? parsed.workstreamWatermarks : []),
174
+ documentWatermarks: new Map(Array.isArray(parsed.documentWatermarks) ? parsed.documentWatermarks : []),
175
+ assetWatermarks: new Map(Array.isArray(parsed.assetWatermarks) ? parsed.assetWatermarks : []),
176
+ documentReviewSessionWatermarks: new Map(Array.isArray(parsed.documentReviewSessionWatermarks) ? parsed.documentReviewSessionWatermarks : []),
177
+ documentReviewCommentWatermarks: new Map(Array.isArray(parsed.documentReviewCommentWatermarks) ? parsed.documentReviewCommentWatermarks : []),
178
+ annotatedAttachmentSessionWatermarks: new Map(Array.isArray(parsed.annotatedAttachmentSessionWatermarks) ? parsed.annotatedAttachmentSessionWatermarks : []),
179
+ taskEventWatermarks: new Map(Array.isArray(parsed.taskEventWatermarks) ? parsed.taskEventWatermarks : []),
180
+ };
181
+ }
182
+ catch {
183
+ return null;
184
+ }
185
+ }
186
+ export function savePersistedSyncWatermarks(workspaceId, data) {
187
+ if (typeof window === 'undefined')
188
+ return;
189
+ try {
190
+ const key = `${WORKSPACE_SYNC_WATERMARKS_KEY_PREFIX}.${workspaceId}`;
191
+ window.localStorage.setItem(key, JSON.stringify({
192
+ v: 8,
193
+ savedAt: new Date().toISOString(),
194
+ taskWatermarks: Array.from(data.taskWatermarks.entries()),
195
+ initiativeWatermarks: Array.from(data.initiativeWatermarks.entries()),
196
+ workstreamWatermarks: Array.from(data.workstreamWatermarks.entries()),
197
+ documentWatermarks: Array.from(data.documentWatermarks.entries()),
198
+ assetWatermarks: Array.from(data.assetWatermarks.entries()),
199
+ documentReviewSessionWatermarks: Array.from(data.documentReviewSessionWatermarks.entries()),
200
+ documentReviewCommentWatermarks: Array.from(data.documentReviewCommentWatermarks.entries()),
201
+ annotatedAttachmentSessionWatermarks: Array.from(data.annotatedAttachmentSessionWatermarks.entries()),
202
+ taskEventWatermarks: Array.from(data.taskEventWatermarks.entries()),
203
+ taxonomyStateFingerprint: typeof data.taxonomyStateFingerprint === 'string' ? data.taxonomyStateFingerprint : ''
204
+ }));
205
+ }
206
+ catch {
207
+ // localStorage may be full or unavailable; non-fatal
208
+ }
209
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
1
+ import { beforeEach, describe, expect, it } from 'vitest';
2
+ import { readPersistedSyncWatermarks, savePersistedSyncWatermarks, WORKSPACE_SYNC_WATERMARKS_KEY_PREFIX, } from './orchestratorShared';
3
+ describe('orchestratorShared sync watermarks', () => {
4
+ const workspaceId = 'workspace-local-active';
5
+ beforeEach(() => {
6
+ localStorage.clear();
7
+ });
8
+ it('keeps persisted sync watermarks available across long idle periods', () => {
9
+ const key = `${WORKSPACE_SYNC_WATERMARKS_KEY_PREFIX}.${workspaceId}`;
10
+ localStorage.setItem(key, JSON.stringify({
11
+ v: 8,
12
+ savedAt: '2026-04-14T10:00:00.000Z',
13
+ taskWatermarks: [['task-1', '2026-04-14T09:00:00.000Z']],
14
+ initiativeWatermarks: [['initiative-1', '2026-04-14T09:00:00.000Z']],
15
+ workstreamWatermarks: [],
16
+ documentWatermarks: [],
17
+ assetWatermarks: [],
18
+ documentReviewSessionWatermarks: [],
19
+ documentReviewCommentWatermarks: [],
20
+ annotatedAttachmentSessionWatermarks: [],
21
+ taskEventWatermarks: [['event-1', '2026-04-14T09:30:00.000Z']],
22
+ taxonomyStateFingerprint: ''
23
+ }));
24
+ const persisted = readPersistedSyncWatermarks(workspaceId, '');
25
+ expect(persisted).not.toBeNull();
26
+ expect(persisted?.taskWatermarks.get('task-1')).toBe('2026-04-14T09:00:00.000Z');
27
+ expect(persisted?.initiativeWatermarks.get('initiative-1')).toBe('2026-04-14T09:00:00.000Z');
28
+ expect(persisted?.taskEventWatermarks.get('event-1')).toBe('2026-04-14T09:30:00.000Z');
29
+ });
30
+ it('still clears task watermarks when the taxonomy fingerprint changes', () => {
31
+ savePersistedSyncWatermarks(workspaceId, {
32
+ taskWatermarks: new Map([['task-1', '2026-04-14T09:00:00.000Z']]),
33
+ initiativeWatermarks: new Map([['initiative-1', '2026-04-14T09:00:00.000Z']]),
34
+ workstreamWatermarks: new Map(),
35
+ documentWatermarks: new Map(),
36
+ assetWatermarks: new Map(),
37
+ documentReviewSessionWatermarks: new Map(),
38
+ documentReviewCommentWatermarks: new Map(),
39
+ annotatedAttachmentSessionWatermarks: new Map(),
40
+ taskEventWatermarks: new Map([['event-1', '2026-04-14T09:30:00.000Z']]),
41
+ taxonomyStateFingerprint: 'fingerprint-a'
42
+ });
43
+ const persisted = readPersistedSyncWatermarks(workspaceId, 'fingerprint-b');
44
+ expect(persisted).not.toBeNull();
45
+ expect(persisted?.taskWatermarks.size).toBe(0);
46
+ expect(persisted?.initiativeWatermarks.get('initiative-1')).toBe('2026-04-14T09:00:00.000Z');
47
+ expect(persisted?.taskEventWatermarks.get('event-1')).toBe('2026-04-14T09:30:00.000Z');
48
+ });
49
+ });
@@ -0,0 +1,25 @@
1
+ import type { MutableRefObject } from 'react';
2
+ import type { WorkspaceSyncPhase } from '../../sync/workspaceSyncState';
3
+ import type { RealtimeSyncSignal } from '../useRealtimeSync';
4
+ interface CreateRealtimeSyncBridgeArgs {
5
+ cloudAuthConfigured: boolean;
6
+ runtimeMode: 'local' | 'cloud';
7
+ workspaceCloudSyncEnabled: boolean;
8
+ workspaceSyncPhase: WorkspaceSyncPhase;
9
+ workspacePullInFlightRef: MutableRefObject<boolean>;
10
+ realtimePullDebounceRef: MutableRefObject<number | null>;
11
+ realtimeSuppressedEventIdsRef: MutableRefObject<Set<string>>;
12
+ clearWorkspaceRetry: () => void;
13
+ isWorkspaceRetryPending: () => boolean;
14
+ pullWorkspaceChangesFromCloud: () => Promise<boolean>;
15
+ }
16
+ export declare function createRealtimeSyncBridge({ cloudAuthConfigured, runtimeMode, workspaceCloudSyncEnabled, workspaceSyncPhase, workspacePullInFlightRef, realtimePullDebounceRef, realtimeSuppressedEventIdsRef, clearWorkspaceRetry, isWorkspaceRetryPending, pullWorkspaceChangesFromCloud }: CreateRealtimeSyncBridgeArgs): {
17
+ handleRealtimeSignal: (signal: RealtimeSyncSignal) => void;
18
+ recordSuppressedEventIds: (eventIds: Iterable<string>, options?: {
19
+ queueRef?: MutableRefObject<string[]>;
20
+ maxQueueSize?: number;
21
+ ttlMs?: number;
22
+ }) => void;
23
+ clearRealtimePullDebounce: () => void;
24
+ };
25
+ export {};
@@ -0,0 +1,60 @@
1
+ export function createRealtimeSyncBridge({ cloudAuthConfigured, runtimeMode, workspaceCloudSyncEnabled, workspaceSyncPhase, workspacePullInFlightRef, realtimePullDebounceRef, realtimeSuppressedEventIdsRef, clearWorkspaceRetry, isWorkspaceRetryPending, pullWorkspaceChangesFromCloud }) {
2
+ const handleRealtimeSignal = (signal) => {
3
+ if (!cloudAuthConfigured || runtimeMode !== 'local' || !workspaceCloudSyncEnabled)
4
+ return;
5
+ if (workspaceSyncPhase === 'provision-local')
6
+ return;
7
+ const eventId = String(signal?.eventId || '').trim();
8
+ if (eventId && realtimeSuppressedEventIdsRef.current.delete(eventId))
9
+ return;
10
+ if (workspacePullInFlightRef.current)
11
+ return;
12
+ if (isWorkspaceRetryPending()) {
13
+ clearWorkspaceRetry();
14
+ }
15
+ if (realtimePullDebounceRef.current !== null) {
16
+ window.clearTimeout(realtimePullDebounceRef.current);
17
+ }
18
+ realtimePullDebounceRef.current = window.setTimeout(() => {
19
+ realtimePullDebounceRef.current = null;
20
+ void pullWorkspaceChangesFromCloud();
21
+ }, 180);
22
+ };
23
+ const recordSuppressedEventIds = (eventIds, options) => {
24
+ const queueRef = options?.queueRef;
25
+ const maxQueueSize = Number.isFinite(options?.maxQueueSize) ? Math.max(1, Number(options?.maxQueueSize)) : 2048;
26
+ const ttlMs = Number.isFinite(options?.ttlMs) ? Math.max(0, Number(options?.ttlMs)) : null;
27
+ for (const rawEventId of eventIds) {
28
+ const eventId = String(rawEventId || '').trim();
29
+ if (!eventId || realtimeSuppressedEventIdsRef.current.has(eventId))
30
+ continue;
31
+ realtimeSuppressedEventIdsRef.current.add(eventId);
32
+ if (queueRef) {
33
+ queueRef.current.push(eventId);
34
+ }
35
+ if (ttlMs !== null) {
36
+ window.setTimeout(() => {
37
+ realtimeSuppressedEventIdsRef.current.delete(eventId);
38
+ }, ttlMs);
39
+ }
40
+ }
41
+ if (queueRef) {
42
+ while (queueRef.current.length > maxQueueSize) {
43
+ const stale = queueRef.current.shift();
44
+ if (stale)
45
+ realtimeSuppressedEventIdsRef.current.delete(stale);
46
+ }
47
+ }
48
+ };
49
+ const clearRealtimePullDebounce = () => {
50
+ if (realtimePullDebounceRef.current !== null) {
51
+ window.clearTimeout(realtimePullDebounceRef.current);
52
+ realtimePullDebounceRef.current = null;
53
+ }
54
+ };
55
+ return {
56
+ handleRealtimeSignal,
57
+ recordSuppressedEventIds,
58
+ clearRealtimePullDebounce
59
+ };
60
+ }
@@ -0,0 +1,69 @@
1
+ import type { MutableRefObject } from 'react';
2
+ import type { WorkspaceSyncPhase } from '../../sync/workspaceSyncState';
3
+ interface CreateSyncRecoveryCoordinatorArgs {
4
+ currentWorkspaceId: string;
5
+ taxonomyStateFingerprint: string;
6
+ workspaceCloudSyncEnabled: boolean;
7
+ workspaceSyncPhase: WorkspaceSyncPhase;
8
+ workspaceLastPullAt: string | null;
9
+ lastBootstrapPhaseRef: MutableRefObject<WorkspaceSyncPhase>;
10
+ workspaceRepairModeRef: MutableRefObject<boolean>;
11
+ workspaceForceFullAiProfilePushRef: MutableRefObject<boolean>;
12
+ workspaceStartupRepairRanRef: MutableRefObject<boolean>;
13
+ workspaceStartupFullReconcileRanRef: MutableRefObject<boolean>;
14
+ workspaceFullReconcileInFlightRef: MutableRefObject<boolean>;
15
+ workspacePullCursorRef: MutableRefObject<string | null>;
16
+ workspaceAttachBootstrapBaselineSignatureRef: MutableRefObject<string>;
17
+ workspaceCaptureAttachBootstrapBaselineRef: MutableRefObject<boolean>;
18
+ workspacePendingSignatureRef: MutableRefObject<string>;
19
+ workspaceLastPushedSignatureRef: MutableRefObject<string>;
20
+ workspaceLastPushedWatermarksRef: MutableRefObject<Map<string, string>>;
21
+ workspaceLastPushedInitiativeIdsRef: MutableRefObject<Set<string>>;
22
+ workspaceLastPushedInitiativeWatermarksRef: MutableRefObject<Map<string, string>>;
23
+ workspaceLastPushedWorkstreamIdsRef: MutableRefObject<Set<string>>;
24
+ workspaceLastPushedWorkstreamWatermarksRef: MutableRefObject<Map<string, string>>;
25
+ workspaceLastPushedAiProfileIdsRef: MutableRefObject<Set<string>>;
26
+ workspaceLastPushedAiProfileWatermarksRef: MutableRefObject<Map<string, string>>;
27
+ workspaceLastPushedDocumentPathsRef: MutableRefObject<Set<string>>;
28
+ workspaceLastPushedDocumentWatermarksRef: MutableRefObject<Map<string, string>>;
29
+ workspaceLastPushedAssetPathsRef: MutableRefObject<Set<string>>;
30
+ workspaceLastPushedAssetWatermarksRef: MutableRefObject<Map<string, string>>;
31
+ workspaceLastPushedDocumentReviewSessionIdsRef: MutableRefObject<Set<string>>;
32
+ workspaceLastPushedDocumentReviewSessionWatermarksRef: MutableRefObject<Map<string, string>>;
33
+ workspaceLastPushedDocumentReviewCommentIdsRef: MutableRefObject<Set<string>>;
34
+ workspaceLastPushedDocumentReviewCommentWatermarksRef: MutableRefObject<Map<string, string>>;
35
+ workspaceLastPushedAnnotatedAttachmentSessionIdsRef: MutableRefObject<Set<string>>;
36
+ workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef: MutableRefObject<Map<string, string>>;
37
+ workspaceLastPushedTaskEventWatermarksRef: MutableRefObject<Map<string, string>>;
38
+ bootstrapPhaseStartedAtRef: MutableRefObject<number | null>;
39
+ bootstrapLastProgressAtRef: MutableRefObject<number | null>;
40
+ acquireWorkspaceSyncLease: (workspaceId: string, owner: 'pull' | 'push' | 'repair') => Promise<boolean>;
41
+ releaseWorkspaceSyncLease: (workspaceId: string) => void;
42
+ forceClearWorkspaceSyncLease: (workspaceId: string) => void;
43
+ clearWorkspaceRetry: () => void;
44
+ clearWorkspaceIssue: () => void;
45
+ reportWorkspaceWindowContention: (operation: 'pull' | 'push' | 'repair') => void;
46
+ persistWorkspaceSyncPatch: (patch: {
47
+ phase: WorkspaceSyncPhase;
48
+ pullCursor: null;
49
+ lastErrorMessage: null;
50
+ }) => Promise<unknown>;
51
+ buildWorkspaceSyncSignature: () => string;
52
+ pullWorkspaceChangesFromCloud: (options?: {
53
+ repairMode?: boolean;
54
+ forceCursorNull?: boolean;
55
+ }) => Promise<boolean>;
56
+ pushWorkspaceChangesToCloud: (signature: string, options?: {
57
+ forceAllAiProfiles?: boolean;
58
+ repairMode?: boolean;
59
+ }) => Promise<boolean>;
60
+ cloudAuthConfigured: boolean;
61
+ runtimeMode: 'local' | 'cloud';
62
+ isAuthenticated: boolean;
63
+ checkAuthSession: () => Promise<boolean>;
64
+ }
65
+ export declare function createSyncRecoveryCoordinator({ currentWorkspaceId, taxonomyStateFingerprint, workspaceCloudSyncEnabled, workspaceSyncPhase, workspaceLastPullAt, lastBootstrapPhaseRef, workspaceRepairModeRef, workspaceForceFullAiProfilePushRef, workspaceStartupRepairRanRef, workspaceStartupFullReconcileRanRef, workspaceFullReconcileInFlightRef, workspacePullCursorRef, workspaceAttachBootstrapBaselineSignatureRef, workspaceCaptureAttachBootstrapBaselineRef, workspacePendingSignatureRef, workspaceLastPushedSignatureRef, workspaceLastPushedWatermarksRef, workspaceLastPushedInitiativeIdsRef, workspaceLastPushedInitiativeWatermarksRef, workspaceLastPushedWorkstreamIdsRef, workspaceLastPushedWorkstreamWatermarksRef, workspaceLastPushedAiProfileIdsRef, workspaceLastPushedAiProfileWatermarksRef, workspaceLastPushedDocumentPathsRef, workspaceLastPushedDocumentWatermarksRef, workspaceLastPushedAssetPathsRef, workspaceLastPushedAssetWatermarksRef, workspaceLastPushedDocumentReviewSessionIdsRef, workspaceLastPushedDocumentReviewSessionWatermarksRef, workspaceLastPushedDocumentReviewCommentIdsRef, workspaceLastPushedDocumentReviewCommentWatermarksRef, workspaceLastPushedAnnotatedAttachmentSessionIdsRef, workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef, workspaceLastPushedTaskEventWatermarksRef, bootstrapPhaseStartedAtRef, bootstrapLastProgressAtRef, acquireWorkspaceSyncLease, releaseWorkspaceSyncLease, forceClearWorkspaceSyncLease, clearWorkspaceRetry, clearWorkspaceIssue, reportWorkspaceWindowContention, persistWorkspaceSyncPatch, buildWorkspaceSyncSignature, pullWorkspaceChangesFromCloud, pushWorkspaceChangesToCloud, cloudAuthConfigured, runtimeMode, isAuthenticated, checkAuthSession }: CreateSyncRecoveryCoordinatorArgs): {
66
+ retryWorkspaceCloudSync: () => Promise<boolean>;
67
+ resetWorkspaceSyncCursorAndPull: () => Promise<void>;
68
+ };
69
+ export {};