@taskforcehq/taskforce 0.3.306 → 0.3.308
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/dist/Taskforce.module.css +317 -27
- package/dist/TaskforceCore.js +1 -0
- package/dist/TaskforceCore.test.js +178 -0
- package/dist/components/activity/EntityActivityTimeline.d.ts +34 -0
- package/dist/components/activity/EntityActivityTimeline.js +81 -0
- package/dist/components/context/ContextAttachmentManager.js +15 -3
- package/dist/components/features/AgentsModule.d.ts +5 -1
- package/dist/components/features/AgentsModule.js +142 -39
- package/dist/components/features/AgentsModule.test.js +260 -11
- package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +3 -1
- package/dist/components/features/AnnotatedAttachmentWorkspace.js +118 -4
- package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +52 -0
- package/dist/components/features/DocumentIndex.d.ts +2 -2
- package/dist/components/features/DocumentIndex.js +3 -4
- package/dist/components/features/DocumentViewer.d.ts +3 -1
- package/dist/components/features/DocumentViewer.js +6 -5
- package/dist/components/features/DocumentWorkspace.d.ts +10 -2
- package/dist/components/features/DocumentWorkspace.js +15 -4
- package/dist/components/features/DocumentWorkspace.test.js +20 -3
- package/dist/components/features/TaskSettings.d.ts +2 -0
- package/dist/components/features/TaskSettings.js +83 -78
- package/dist/components/features/TaskSettings.test.js +171 -82
- package/dist/components/task/TaskActionHeader.js +11 -5
- package/dist/components/task/TaskActionHeader.test.js +10 -0
- package/dist/components/task/TaskCard.js +6 -2
- package/dist/components/task/TaskContextUpload.test.js +3 -3
- package/dist/components/task/TaskForm.js +14 -64
- package/dist/components/task/TaskKanban.js +0 -8
- package/dist/components/task/TaskList.js +5 -1
- package/dist/components/task/TaskList.test.js +4 -0
- package/dist/components/ui/HierarchyActionBadge.d.ts +10 -0
- package/dist/components/ui/HierarchyActionBadge.js +5 -0
- package/dist/components/views/AppShellHeader.d.ts +3 -1
- package/dist/components/views/AppShellHeader.js +27 -11
- package/dist/components/views/AppShellHeader.test.js +18 -1
- package/dist/components/views/PlanComparisonPage.d.ts +20 -15
- package/dist/components/views/PlanComparisonPage.js +221 -82
- package/dist/components/views/PlanComparisonPage.test.js +268 -14
- package/dist/components/views/PlansPage.js +16 -10
- package/dist/components/views/PlansPage.test.js +4 -2
- package/dist/components/views/StandaloneLayout.js +256 -69
- package/dist/components/views/WidgetView.js +10 -8
- package/dist/components/views/panels/PlanningDrawer.d.ts +19 -6
- package/dist/components/views/panels/PlanningDrawer.js +259 -51
- package/dist/components/views/panels/PlanningDrawer.test.js +392 -4
- package/dist/config/envSchema.js +1 -0
- package/dist/core/AiProfileService.d.ts +7 -0
- package/dist/core/AiProfileService.js +460 -35
- package/dist/core/AiProfiles.test.js +648 -2
- package/dist/core/AttachmentLinkService.d.ts +3 -0
- package/dist/core/AttachmentLinkService.js +58 -13
- package/dist/core/GlobalSettingsService.js +30 -0
- package/dist/core/GlobalSettingsService.test.d.ts +1 -0
- package/dist/core/GlobalSettingsService.test.js +45 -0
- package/dist/core/McpTokenService.d.ts +12 -0
- package/dist/core/McpTokenService.js +44 -1
- package/dist/core/PlanEntitlementService.d.ts +20 -0
- package/dist/core/PlanEntitlementService.js +215 -19
- package/dist/core/PlanFeatureCatalog.test.js +55 -0
- package/dist/core/PlanningEntities.test.js +366 -4
- package/dist/core/TaskActivityService.d.ts +28 -1
- package/dist/core/TaskActivityService.js +227 -46
- package/dist/core/TaskActivitySync.test.d.ts +1 -0
- package/dist/core/TaskActivitySync.test.js +90 -0
- package/dist/core/TaskAuditTimeline.test.js +96 -0
- package/dist/core/Taskforce.d.ts +204 -26
- package/dist/core/Taskforce.ids.test.js +58 -0
- package/dist/core/Taskforce.js +781 -71
- package/dist/core/Taskforce.mcpAuth.test.js +109 -0
- package/dist/core/types.d.ts +10 -0
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +2 -1
- package/dist/hooks/bootstrapTimeouts.d.ts +4 -0
- package/dist/hooks/bootstrapTimeouts.js +8 -0
- package/dist/hooks/bootstrapTimeouts.test.d.ts +1 -0
- package/dist/hooks/bootstrapTimeouts.test.js +12 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +3 -3
- package/dist/hooks/sync/orchestratorShared.js +61 -1
- package/dist/hooks/sync/recovery.js +8 -0
- package/dist/hooks/sync/recovery.test.js +5 -5
- package/dist/hooks/sync/transfers.d.ts +2 -1
- package/dist/hooks/sync/transfers.js +36 -9
- package/dist/hooks/sync/transfers.test.js +534 -2
- package/dist/hooks/useFilterSort.js +13 -13
- package/dist/hooks/useFilterSort.test.js +87 -1
- package/dist/hooks/useSyncOrchestrator.js +50 -5
- package/dist/hooks/useTaskforce.d.ts +85 -50
- package/dist/hooks/useTaskforce.js +44 -14
- package/dist/hooks/useTaskforce.runtime-routing.test.js +94 -0
- package/dist/hooks/useTaskforce.sync-behavior.test.js +99 -0
- package/dist/hooks/useWorkspaceSyncController.js +7 -1
- package/dist/hooks/useWorkspaceSyncController.test.js +48 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +14 -7
- package/dist/mcp/canonicalAssetHelpers.d.ts +16 -4
- package/dist/mcp/canonicalAssetHelpers.js +18 -10
- package/dist/mcp/canonicalAssetHelpers.test.d.ts +1 -0
- package/dist/mcp/canonicalAssetHelpers.test.js +67 -0
- package/dist/mcp/clientIntegrations.d.ts +1 -0
- package/dist/mcp/clientIntegrations.js +25 -0
- package/dist/mcp/clientIntegrations.test.js +14 -3
- package/dist/mcp/clientProfileDefaults.js +3 -3
- package/dist/mcp/collaborationRegistrar.js +7 -7
- package/dist/mcp/documentAssetRegistrar.js +113 -7
- package/dist/mcp/runtime.d.ts +1 -0
- package/dist/mcp/runtime.js +969 -122
- package/dist/mcp/runtime.test.js +1083 -11
- package/dist/mcp/taskAttachmentTypes.d.ts +1 -1
- package/dist/mcp/taskPlanningRegistrar.js +48 -0
- package/dist/mcp/toolRegistry.d.ts +56 -0
- package/dist/mcp/toolRegistry.js +8 -0
- package/dist/mcp/toolRegistry.test.js +4 -0
- package/dist/migrations/taskSchemaMigrations.js +58 -15
- package/dist/server/aiProfileAvatarAssets.d.ts +49 -0
- package/dist/server/aiProfileAvatarAssets.js +319 -0
- package/dist/server/aiProfileAvatarAssets.test.d.ts +1 -0
- package/dist/server/aiProfileAvatarAssets.test.js +213 -0
- package/dist/server/annotatedAttachmentsRoutes.test.js +66 -7
- package/dist/server/index.js +4 -0
- package/dist/server/index.test.js +6 -1
- package/dist/server/routes/admin.js +328 -9
- package/dist/server/routes/annotatedAttachments.js +51 -0
- package/dist/server/routes/auth.d.ts +11 -0
- package/dist/server/routes/auth.js +189 -27
- package/dist/server/routes/authSupport.d.ts +5 -0
- package/dist/server/routes/authSupport.js +13 -1
- package/dist/server/routes/billing.d.ts +1 -0
- package/dist/server/routes/billing.js +116 -63
- package/dist/server/routes/billing.test.js +162 -11
- package/dist/server/routes/documents.js +10 -0
- package/dist/server/routes/shared.js +0 -10
- package/dist/server/routes/sync.integration.test.js +221 -0
- package/dist/server/routes/sync.js +13 -3
- package/dist/server/routes/syncSnapshotRoutes.js +2 -0
- package/dist/server/routes/tasks.js +12 -9
- package/dist/server/routes.js +87 -8
- package/dist/server/routes.test.js +972 -23
- package/dist/shared/aiProfileSurfaceType.d.ts +1 -1
- package/dist/shared/aiProfileSurfaceType.js +10 -8
- package/dist/storage/objectStorage.d.ts +1 -0
- package/dist/storage/objectStorage.js +2 -1
- package/dist/storage/objectStorageClient.d.ts +1 -0
- package/dist/storage/objectStorageClient.js +41 -8
- package/dist/storage/objectStorageClient.test.js +26 -1
- package/dist/storage/objectStorageConfig.js +3 -1
- package/dist/storage/objectStorageConfig.test.js +13 -1
- package/dist/sync/collaborationSyncPayload.js +2 -0
- package/dist/sync/collaborationSyncPayload.test.js +1 -0
- package/dist/sync/collaborationSyncState.d.ts +3 -3
- package/dist/sync/collaborationSyncState.js +19 -12
- package/dist/sync/contentSyncPayload.d.ts +5 -1
- package/dist/sync/contentSyncPayload.js +9 -0
- package/dist/sync/contentSyncPayload.test.js +39 -0
- package/dist/sync/contentSyncState.d.ts +5 -1
- package/dist/sync/contentSyncState.js +231 -37
- package/dist/sync/planningSyncPayload.js +5 -0
- package/dist/sync/planningSyncPayload.test.js +24 -0
- package/dist/sync/syncApplyHandlers.d.ts +4 -4
- package/dist/sync/syncApplyHandlers.js +89 -31
- package/dist/sync/syncApplyHandlers.test.js +184 -4
- package/dist/sync/syncResourceAdapters.d.ts +2 -0
- package/dist/sync/syncResourceAdapters.js +2 -0
- package/dist/sync/syncService.d.ts +1 -0
- package/dist/sync/syncService.js +13 -1
- package/dist/sync/syncService.test.js +39 -0
- package/dist/sync/workspacePullFeed.d.ts +4 -0
- package/dist/sync/workspacePullFeed.js +129 -70
- package/dist/sync/workspacePullFeed.test.js +49 -0
- package/dist/sync/workspaceSyncModel.d.ts +28 -6
- package/dist/sync/workspaceSyncModel.js +159 -45
- package/dist/sync/workspaceSyncModel.test.js +269 -3
- package/dist/types.d.ts +33 -2
- package/dist/ui/agent-logos/ChatGPT.png +0 -0
- package/dist/ui/agent-logos/Gemini CLI.jpeg +0 -0
- package/dist/ui/agent-logos/antigravity.jpeg +0 -0
- package/dist/ui/agent-logos/claude-code.jpeg +0 -0
- package/dist/ui/agent-logos/codex.jpeg +0 -0
- package/dist/ui/agent-logos/cursor.png +0 -0
- package/dist/ui/agent-logos/openclaw.jpeg +0 -0
- package/dist/ui/agent-logos/windsurf.png +0 -0
- package/dist/ui/assets/AgentsModule-BS4Pi_nC.js +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BVKew-mH.css +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-CCckzWYZ.js +3 -0
- package/dist/ui/assets/ContextAttachmentManager-CfG_ER7C.js +3 -0
- package/dist/ui/assets/DocumentWorkspace-BVaWPA25.js +252 -0
- package/dist/ui/assets/DocumentWorkspace-D4rJKXzd.css +1 -0
- package/dist/ui/assets/EntityActivityTimeline-DSj0ThaK.js +1 -0
- package/dist/ui/assets/EntityActivityTimeline-DyqvvhuF.css +1 -0
- package/dist/ui/assets/{InitiativesModule-9E6ParrY.js → InitiativesModule-AxMkWtxH.js} +1 -1
- package/dist/ui/assets/PlansPage-B_z-D6Nh.css +1 -0
- package/dist/ui/assets/PlansPage-Ndt7mYfo.js +1 -0
- package/dist/ui/assets/TaskContextUpload-Dw4rTF4i.js +1 -0
- package/dist/ui/assets/TaskSettings-f2ga42_V.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-BA7jcEpH.js → WorkflowsModule-E4UL3mov.js} +4 -4
- package/dist/ui/assets/documentReferences-BOUcJm6-.js +1 -0
- package/dist/ui/assets/index-BvAbqx5Q.css +1 -0
- package/dist/ui/assets/index-MAHKvFqp.js +4 -0
- package/dist/ui/assets/{vendor-icons-DuEd_65c.js → vendor-icons-B32hGuF2.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/ui/og-image.png +0 -0
- package/dist/ui/product/agent manager.png +0 -0
- package/dist/ui/product/agent_manager_02.png +0 -0
- package/dist/utils/aiProfileEvents.d.ts +7 -0
- package/dist/utils/aiProfileEvents.js +6 -0
- package/dist/utils/assignees.d.ts +6 -0
- package/dist/utils/assignees.js +8 -1
- package/dist/utils/assignees.test.d.ts +1 -0
- package/dist/utils/assignees.test.js +25 -0
- package/dist/utils/avatarDisplay.d.ts +1 -0
- package/dist/utils/avatarDisplay.js +15 -0
- package/dist/utils/contextFiles.d.ts +3 -1
- package/dist/utils/contextFiles.js +2 -1
- package/dist/utils/taskActivity.d.ts +7 -1
- package/dist/utils/taskActivity.js +63 -1
- package/dist/utils/taskActivity.test.js +23 -1
- package/dist/utils/taskSearch.d.ts +22 -0
- package/dist/utils/taskSearch.js +87 -0
- package/dist/utils/taskSearch.test.d.ts +1 -0
- package/dist/utils/taskSearch.test.js +69 -0
- package/dist/utils/taskUtils.js +10 -3
- package/dist/utils/taskUtils.test.d.ts +1 -0
- package/dist/utils/taskUtils.test.js +19 -0
- package/package.json +1 -1
- package/dist/ui/assets/AgentsModule-DrLawwNl.js +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-Z9_whf7F.js +0 -3
- package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +0 -252
- package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +0 -1
- package/dist/ui/assets/PlansPage-CvfnMiWq.css +0 -1
- package/dist/ui/assets/PlansPage-DRXscYxH.js +0 -1
- package/dist/ui/assets/TaskSettings-DZX4jk7e.js +0 -9
- package/dist/ui/assets/index-9eT8e0Lu.css +0 -1
- package/dist/ui/assets/index-C6k75jr8.js +0 -6
|
@@ -289,6 +289,82 @@ describe('TaskforceCore MCP auth invalidation', () => {
|
|
|
289
289
|
cleanup();
|
|
290
290
|
}
|
|
291
291
|
});
|
|
292
|
+
it('does not delete active MCP access tokens', () => {
|
|
293
|
+
const { core, cleanup } = makeCore();
|
|
294
|
+
try {
|
|
295
|
+
setupWorkspace(core, 'owner-1', 'workspace-1');
|
|
296
|
+
const issued = core.createMcpAccessToken({
|
|
297
|
+
userId: 'owner-1',
|
|
298
|
+
workspaceId: 'workspace-1',
|
|
299
|
+
name: 'Active Token',
|
|
300
|
+
scopes: ['tasks:read']
|
|
301
|
+
});
|
|
302
|
+
expect(() => core.deleteMcpAccessToken({
|
|
303
|
+
actorUserId: 'owner-1',
|
|
304
|
+
workspaceId: 'workspace-1',
|
|
305
|
+
tokenId: issued.record.id
|
|
306
|
+
})).toThrow('Active MCP tokens must be revoked before deletion');
|
|
307
|
+
expect(core.listMcpAccessTokens({ userId: 'owner-1', workspaceId: 'workspace-1' }).items).toHaveLength(1);
|
|
308
|
+
}
|
|
309
|
+
finally {
|
|
310
|
+
cleanup();
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
it('deletes revoked MCP access token records', () => {
|
|
314
|
+
const { core, cleanup } = makeCore();
|
|
315
|
+
try {
|
|
316
|
+
setupWorkspace(core, 'owner-1', 'workspace-1');
|
|
317
|
+
const issued = core.createMcpAccessToken({
|
|
318
|
+
userId: 'owner-1',
|
|
319
|
+
workspaceId: 'workspace-1',
|
|
320
|
+
name: 'Revoked Token',
|
|
321
|
+
scopes: ['tasks:read']
|
|
322
|
+
});
|
|
323
|
+
core.revokeMcpAccessToken({
|
|
324
|
+
actorUserId: 'owner-1',
|
|
325
|
+
workspaceId: 'workspace-1',
|
|
326
|
+
tokenId: issued.record.id
|
|
327
|
+
});
|
|
328
|
+
expect(core.deleteMcpAccessToken({
|
|
329
|
+
actorUserId: 'owner-1',
|
|
330
|
+
workspaceId: 'workspace-1',
|
|
331
|
+
tokenId: issued.record.id
|
|
332
|
+
})).toBe(true);
|
|
333
|
+
expect(core.listMcpAccessTokens({ userId: 'owner-1', workspaceId: 'workspace-1' }).items).toHaveLength(0);
|
|
334
|
+
expect(core.authenticateMcpAccessToken(issued.token)).toBeNull();
|
|
335
|
+
}
|
|
336
|
+
finally {
|
|
337
|
+
cleanup();
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
it('deletes expired MCP access token records', () => {
|
|
341
|
+
const { core, cleanup } = makeCore();
|
|
342
|
+
try {
|
|
343
|
+
setupWorkspace(core, 'owner-1', 'workspace-1');
|
|
344
|
+
const issued = core.createMcpAccessToken({
|
|
345
|
+
userId: 'owner-1',
|
|
346
|
+
workspaceId: 'workspace-1',
|
|
347
|
+
name: 'Expired Token',
|
|
348
|
+
scopes: ['tasks:read'],
|
|
349
|
+
expiresAt: new Date(Date.now() + 60_000).toISOString()
|
|
350
|
+
});
|
|
351
|
+
core.db.prepare(`
|
|
352
|
+
UPDATE mcp_access_tokens
|
|
353
|
+
SET expires_at = ?
|
|
354
|
+
WHERE tenant_id = ? AND id = ?
|
|
355
|
+
`).run(new Date(Date.now() - 60_000).toISOString(), 'default', issued.record.id);
|
|
356
|
+
expect(core.listMcpAccessTokens({ userId: 'owner-1', workspaceId: 'workspace-1' }).items[0]?.status).toBe('expired');
|
|
357
|
+
expect(core.deleteMcpAccessToken({
|
|
358
|
+
actorUserId: 'owner-1',
|
|
359
|
+
workspaceId: 'workspace-1',
|
|
360
|
+
tokenId: issued.record.id
|
|
361
|
+
})).toBe(true);
|
|
362
|
+
expect(core.listMcpAccessTokens({ userId: 'owner-1', workspaceId: 'workspace-1' }).items).toHaveLength(0);
|
|
363
|
+
}
|
|
364
|
+
finally {
|
|
365
|
+
cleanup();
|
|
366
|
+
}
|
|
367
|
+
});
|
|
292
368
|
it('denies OAuth authentication after the authorized member is disabled', () => {
|
|
293
369
|
const { core, cleanup } = makeCore();
|
|
294
370
|
try {
|
|
@@ -308,6 +384,39 @@ describe('TaskforceCore MCP auth invalidation', () => {
|
|
|
308
384
|
cleanup();
|
|
309
385
|
}
|
|
310
386
|
});
|
|
387
|
+
it('returns persisted AI profile binding on OAuth connector authentication', () => {
|
|
388
|
+
const { core, cleanup } = makeCore();
|
|
389
|
+
try {
|
|
390
|
+
setupWorkspace(core, 'owner-1', 'workspace-1');
|
|
391
|
+
const accessToken = createOauthGrantToken(core, 'owner-1', 'workspace-1');
|
|
392
|
+
const auth = core.authenticateMcpOAuthAccessToken(accessToken);
|
|
393
|
+
expect(auth?.aiProfileToken).toBeFalsy();
|
|
394
|
+
const resolved = core.resolveAiProfile({
|
|
395
|
+
workspaceId: 'workspace-1',
|
|
396
|
+
name: 'Codex Connector',
|
|
397
|
+
icon: 'Code',
|
|
398
|
+
color: '#0ea5e9',
|
|
399
|
+
}, {
|
|
400
|
+
trustedSeatScope: 'cloud_metered',
|
|
401
|
+
});
|
|
402
|
+
expect(core.bindMcpOAuthConnectorGrantAiProfile({
|
|
403
|
+
grantId: auth.grantId,
|
|
404
|
+
workspaceId: 'workspace-1',
|
|
405
|
+
profileId: resolved.profile.id,
|
|
406
|
+
profileToken: resolved.profile.profileToken,
|
|
407
|
+
profileName: resolved.profile.name,
|
|
408
|
+
})).toBe(true);
|
|
409
|
+
expect(core.authenticateMcpOAuthAccessToken(accessToken)).toEqual(expect.objectContaining({
|
|
410
|
+
grantId: auth.grantId,
|
|
411
|
+
aiProfileId: resolved.profile.id,
|
|
412
|
+
aiProfileToken: resolved.profile.profileToken,
|
|
413
|
+
aiProfileName: 'Codex Connector',
|
|
414
|
+
}));
|
|
415
|
+
}
|
|
416
|
+
finally {
|
|
417
|
+
cleanup();
|
|
418
|
+
}
|
|
419
|
+
});
|
|
311
420
|
it('denies OAuth authentication after account access is suspended', () => {
|
|
312
421
|
const { core, cleanup } = makeCore();
|
|
313
422
|
try {
|
package/dist/core/types.d.ts
CHANGED
|
@@ -275,6 +275,11 @@ export interface GlobalTaskforceSettings {
|
|
|
275
275
|
site?: {
|
|
276
276
|
showRunTaskforceLocally?: boolean;
|
|
277
277
|
pricingPageEnabled?: boolean;
|
|
278
|
+
animatedBackgroundEnabled?: boolean;
|
|
279
|
+
siteTheme?: 'dark' | 'light';
|
|
280
|
+
pricingPageTitle?: string;
|
|
281
|
+
pricingPageDescription?: string;
|
|
282
|
+
pricingPageShowPublicDescriptions?: boolean;
|
|
278
283
|
};
|
|
279
284
|
setup?: {
|
|
280
285
|
mode?: 'core' | 'operations';
|
|
@@ -505,8 +510,10 @@ export interface PlanFeatureCatalogItem {
|
|
|
505
510
|
featureKey: string;
|
|
506
511
|
label: string;
|
|
507
512
|
description: string;
|
|
513
|
+
isCustom?: boolean;
|
|
508
514
|
publicLabel?: string | null;
|
|
509
515
|
publicDescription?: string | null;
|
|
516
|
+
publicDescriptionVisible?: boolean | null;
|
|
510
517
|
publicDisplayOrder?: number | null;
|
|
511
518
|
allowedAccessModes: PlanFeatureAccess[];
|
|
512
519
|
configTemplate: Record<string, unknown>;
|
|
@@ -678,4 +685,7 @@ export interface McpOAuthAccessTokenAuthResult {
|
|
|
678
685
|
provider: McpOAuthProvider;
|
|
679
686
|
scopes: McpAccessTokenScope[];
|
|
680
687
|
status: McpOAuthGrantStatus;
|
|
688
|
+
aiProfileId?: string | null;
|
|
689
|
+
aiProfileToken?: string | null;
|
|
690
|
+
aiProfileName?: string | null;
|
|
681
691
|
}
|
|
@@ -2,6 +2,7 @@ import { useCallback, useEffect } from 'react';
|
|
|
2
2
|
import { createDefaultAssigneeOptions } from '../../utils/assignees';
|
|
3
3
|
import { logBootstrapDebug } from '../workspace/workspaceLocalState';
|
|
4
4
|
import { persistLocalMutationActorUserId, readLocalMutationActorUserId } from '../../utils/taskforceApiClient';
|
|
5
|
+
import { AUTH_SESSION_REQUEST_TIMEOUT_MS } from '../bootstrapTimeouts';
|
|
5
6
|
function getBootstrapTimestamp() {
|
|
6
7
|
return typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
7
8
|
}
|
|
@@ -48,7 +49,7 @@ export function useTaskforceAuthBootstrap({ runtimeConfigReady, shouldProbeCloud
|
|
|
48
49
|
const startedAt = getBootstrapTimestamp();
|
|
49
50
|
const abortController = new AbortController();
|
|
50
51
|
const timeoutId = typeof window !== 'undefined'
|
|
51
|
-
? window.setTimeout(() => abortController.abort(),
|
|
52
|
+
? window.setTimeout(() => abortController.abort(), AUTH_SESSION_REQUEST_TIMEOUT_MS)
|
|
52
53
|
: null;
|
|
53
54
|
const isStaleRequest = () => authSessionEpochRef.current !== requestEpoch;
|
|
54
55
|
const logResult = (event, details) => {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const BOOTSTRAP_REQUEST_TIMEOUT_MS = 12000;
|
|
2
|
+
export declare const CLOUD_BOOTSTRAP_REQUEST_TIMEOUT_MS = 30000;
|
|
3
|
+
export declare const AUTH_SESSION_REQUEST_TIMEOUT_MS = 20000;
|
|
4
|
+
export declare function resolveBootstrapRequestTimeoutMs(runtimeMode: 'local' | 'cloud'): number;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const BOOTSTRAP_REQUEST_TIMEOUT_MS = 12_000;
|
|
2
|
+
export const CLOUD_BOOTSTRAP_REQUEST_TIMEOUT_MS = 30_000;
|
|
3
|
+
export const AUTH_SESSION_REQUEST_TIMEOUT_MS = 20_000;
|
|
4
|
+
export function resolveBootstrapRequestTimeoutMs(runtimeMode) {
|
|
5
|
+
return runtimeMode === 'cloud'
|
|
6
|
+
? CLOUD_BOOTSTRAP_REQUEST_TIMEOUT_MS
|
|
7
|
+
: BOOTSTRAP_REQUEST_TIMEOUT_MS;
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { AUTH_SESSION_REQUEST_TIMEOUT_MS, BOOTSTRAP_REQUEST_TIMEOUT_MS, CLOUD_BOOTSTRAP_REQUEST_TIMEOUT_MS, resolveBootstrapRequestTimeoutMs } from './bootstrapTimeouts';
|
|
3
|
+
describe('bootstrap timeout budgets', () => {
|
|
4
|
+
it('keeps local bootstrap checks short', () => {
|
|
5
|
+
expect(resolveBootstrapRequestTimeoutMs('local')).toBe(BOOTSTRAP_REQUEST_TIMEOUT_MS);
|
|
6
|
+
});
|
|
7
|
+
it('gives cloud bootstrap checks enough room for edge delays', () => {
|
|
8
|
+
expect(resolveBootstrapRequestTimeoutMs('cloud')).toBe(CLOUD_BOOTSTRAP_REQUEST_TIMEOUT_MS);
|
|
9
|
+
expect(CLOUD_BOOTSTRAP_REQUEST_TIMEOUT_MS).toBeGreaterThan(BOOTSTRAP_REQUEST_TIMEOUT_MS);
|
|
10
|
+
expect(AUTH_SESSION_REQUEST_TIMEOUT_MS).toBeGreaterThan(BOOTSTRAP_REQUEST_TIMEOUT_MS);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { WorkspaceTaxonomyState } from '../../shared/taxonomyState.js';
|
|
2
|
-
import type { WorkspaceSyncAiProfileSnapshot, WorkspaceSyncDocumentReviewCommentSnapshot, WorkspaceSyncDocumentReviewSessionSnapshot,
|
|
2
|
+
import type { WorkspaceSyncAiProfileSnapshot, WorkspaceSyncDocumentReviewCommentSnapshot, WorkspaceSyncDocumentReviewSessionSnapshot, WorkspaceSyncEntityEventSnapshot } from '../../sync/workspaceSyncModel';
|
|
3
3
|
import type { WorkspaceSyncPushBaselineSnapshot } from '../../sync/workspaceSyncState.js';
|
|
4
|
-
import type { TaskItem } from '../../types';
|
|
4
|
+
import type { InitiativeItem, TaskItem, WorkstreamItem } from '../../types';
|
|
5
5
|
export declare const USER_GLOBAL_SYNC_META_KEY = "taskforce.userGlobalSyncMeta.v1";
|
|
6
6
|
export declare const SYNC_DEBUG_LOG_KEY = "taskforce.syncDebug.v1";
|
|
7
7
|
export declare const WORKSPACE_BOOTSTRAP_TIMEOUT_MS = 120000;
|
|
@@ -19,7 +19,7 @@ export declare function writeUserGlobalSyncMeta(next: Record<string, {
|
|
|
19
19
|
}>): void;
|
|
20
20
|
export declare function buildTaxonomyStateFingerprint(taxonomyState?: WorkspaceTaxonomyState): string;
|
|
21
21
|
export declare function flattenDocumentReviewComments(sessions: WorkspaceSyncDocumentReviewSessionSnapshot[]): WorkspaceSyncDocumentReviewCommentSnapshot[];
|
|
22
|
-
export declare function flattenTaskEvents(tasks: TaskItem[], archivedTasks: TaskItem[]):
|
|
22
|
+
export declare function flattenTaskEvents(tasks: TaskItem[], archivedTasks: TaskItem[], initiatives?: InitiativeItem[], workstreams?: WorkstreamItem[]): WorkspaceSyncEntityEventSnapshot[];
|
|
23
23
|
export interface PersistedSyncWatermarks {
|
|
24
24
|
taskIds: Set<string>;
|
|
25
25
|
taskWatermarks: Map<string, string>;
|
|
@@ -40,6 +40,8 @@ export function normalizeWorkspaceSyncAiProfileSnapshotEntry(entry) {
|
|
|
40
40
|
color: String(entry?.color || '#8b5cf6').trim() || '#8b5cf6',
|
|
41
41
|
avatarUrl: normalizeSyncSameOriginAssetUrl(entry?.avatarUrl ?? entry?.avatar_url),
|
|
42
42
|
avatarSourceUrl: normalizeSyncSameOriginAssetUrl(entry?.avatarSourceUrl ?? entry?.avatar_source_url),
|
|
43
|
+
avatarRevision: Number.isFinite(Number(entry?.avatarRevision ?? entry?.avatar_revision)) ? Math.max(0, Math.floor(Number(entry?.avatarRevision ?? entry?.avatar_revision))) : 0,
|
|
44
|
+
avatarUpdatedAt: normalizeSyncNullableIsoString(entry?.avatarUpdatedAt ?? entry?.avatar_updated_at),
|
|
43
45
|
description: normalizeSyncNullableString(entry?.description),
|
|
44
46
|
role: normalizeSyncNullableString(entry?.role),
|
|
45
47
|
provider: normalizeSyncNullableString(entry?.provider),
|
|
@@ -113,7 +115,7 @@ export function flattenDocumentReviewComments(sessions) {
|
|
|
113
115
|
.filter((comment) => comment.id.length > 0 && comment.sessionId.length > 0);
|
|
114
116
|
});
|
|
115
117
|
}
|
|
116
|
-
export function flattenTaskEvents(tasks, archivedTasks) {
|
|
118
|
+
export function flattenTaskEvents(tasks, archivedTasks, initiatives = [], workstreams = []) {
|
|
117
119
|
const deduped = new Map();
|
|
118
120
|
for (const task of [...(tasks || []), ...(archivedTasks || [])]) {
|
|
119
121
|
const taskEvents = Array.isArray(task?.taskEvents) ? task.taskEvents : [];
|
|
@@ -126,8 +128,66 @@ export function flattenTaskEvents(tasks, archivedTasks) {
|
|
|
126
128
|
continue;
|
|
127
129
|
const nextEvent = {
|
|
128
130
|
id,
|
|
131
|
+
workspaceId,
|
|
132
|
+
entityType: 'task',
|
|
133
|
+
entityId: taskId,
|
|
129
134
|
taskId,
|
|
135
|
+
action: String(event?.action || '').trim(),
|
|
136
|
+
actor: String(event?.actor || 'system').trim() || 'system',
|
|
137
|
+
actorType: event?.actorType === 'ai' || event?.actorType === 'human' || event?.actorType === 'system'
|
|
138
|
+
? event.actorType
|
|
139
|
+
: 'system',
|
|
140
|
+
details: event?.details,
|
|
141
|
+
createdAt,
|
|
142
|
+
};
|
|
143
|
+
const existing = deduped.get(id);
|
|
144
|
+
if (!existing || nextEvent.createdAt > existing.createdAt) {
|
|
145
|
+
deduped.set(id, nextEvent);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const initiative of initiatives || []) {
|
|
150
|
+
const entityEvents = Array.isArray(initiative?.entityEvents) ? initiative.entityEvents : [];
|
|
151
|
+
for (const event of entityEvents) {
|
|
152
|
+
const id = String(event?.id || '').trim();
|
|
153
|
+
const entityId = String(event?.entityId || initiative?.id || '').trim();
|
|
154
|
+
const workspaceId = String(event?.workspaceId || '').trim();
|
|
155
|
+
const createdAt = String(event?.createdAt || '').trim();
|
|
156
|
+
if (!id || !entityId || !workspaceId || !createdAt)
|
|
157
|
+
continue;
|
|
158
|
+
const nextEvent = {
|
|
159
|
+
id,
|
|
160
|
+
workspaceId,
|
|
161
|
+
entityType: 'initiative',
|
|
162
|
+
entityId,
|
|
163
|
+
action: String(event?.action || '').trim(),
|
|
164
|
+
actor: String(event?.actor || 'system').trim() || 'system',
|
|
165
|
+
actorType: event?.actorType === 'ai' || event?.actorType === 'human' || event?.actorType === 'system'
|
|
166
|
+
? event.actorType
|
|
167
|
+
: 'system',
|
|
168
|
+
details: event?.details,
|
|
169
|
+
createdAt,
|
|
170
|
+
};
|
|
171
|
+
const existing = deduped.get(id);
|
|
172
|
+
if (!existing || nextEvent.createdAt > existing.createdAt) {
|
|
173
|
+
deduped.set(id, nextEvent);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
for (const workstream of workstreams || []) {
|
|
178
|
+
const entityEvents = Array.isArray(workstream?.entityEvents) ? workstream.entityEvents : [];
|
|
179
|
+
for (const event of entityEvents) {
|
|
180
|
+
const id = String(event?.id || '').trim();
|
|
181
|
+
const entityId = String(event?.entityId || workstream?.id || '').trim();
|
|
182
|
+
const workspaceId = String(event?.workspaceId || '').trim();
|
|
183
|
+
const createdAt = String(event?.createdAt || '').trim();
|
|
184
|
+
if (!id || !entityId || !workspaceId || !createdAt)
|
|
185
|
+
continue;
|
|
186
|
+
const nextEvent = {
|
|
187
|
+
id,
|
|
130
188
|
workspaceId,
|
|
189
|
+
entityType: 'workstream',
|
|
190
|
+
entityId,
|
|
131
191
|
action: String(event?.action || '').trim(),
|
|
132
192
|
actor: String(event?.actor || 'system').trim() || 'system',
|
|
133
193
|
actorType: event?.actorType === 'ai' || event?.actorType === 'human' || event?.actorType === 'system'
|
|
@@ -10,6 +10,10 @@ function resetWorkspaceSyncStatePushBaselines(args) {
|
|
|
10
10
|
args.workspaceLastPushedAiProfileIdsRef.current = new Set();
|
|
11
11
|
args.workspaceLastPushedAiProfileWatermarksRef.current = new Map();
|
|
12
12
|
args.workspaceLastPushedAiProfileChangeKeysRef.current = new Map();
|
|
13
|
+
args.workspaceLastPushedDocumentPathsRef.current = new Set();
|
|
14
|
+
args.workspaceLastPushedDocumentWatermarksRef.current = new Map();
|
|
15
|
+
args.workspaceLastPushedAssetPathsRef.current = new Set();
|
|
16
|
+
args.workspaceLastPushedAssetWatermarksRef.current = new Map();
|
|
13
17
|
args.workspaceBaselineResetReasonRef.current = 'repair-reset';
|
|
14
18
|
}
|
|
15
19
|
function resetAllWorkspacePushBaselines(args) {
|
|
@@ -187,6 +191,10 @@ export function createSyncRecoveryCoordinator({ currentWorkspaceId, workspaceClo
|
|
|
187
191
|
workspaceLastPushedAiProfileIdsRef,
|
|
188
192
|
workspaceLastPushedAiProfileWatermarksRef,
|
|
189
193
|
workspaceLastPushedAiProfileChangeKeysRef,
|
|
194
|
+
workspaceLastPushedDocumentPathsRef,
|
|
195
|
+
workspaceLastPushedDocumentWatermarksRef,
|
|
196
|
+
workspaceLastPushedAssetPathsRef,
|
|
197
|
+
workspaceLastPushedAssetWatermarksRef,
|
|
190
198
|
workspaceBaselineResetReasonRef,
|
|
191
199
|
});
|
|
192
200
|
}
|
|
@@ -168,7 +168,7 @@ describe('createSyncRecoveryCoordinator', () => {
|
|
|
168
168
|
expect(args.workspaceBaselineResetReasonRef.current).toBeNull();
|
|
169
169
|
expect(args.workspaceRepairModeRef.current).toBe(false);
|
|
170
170
|
});
|
|
171
|
-
it('resets sync-state push baselines during active repair
|
|
171
|
+
it('resets sync-state and content push baselines during active repair', async () => {
|
|
172
172
|
const taskWatermarks = new Map([['task-1', '2026-03-15T17:00:00.000Z']]);
|
|
173
173
|
const taskChangeKeys = new Map([['task-1', 'task-change-key-1']]);
|
|
174
174
|
const initiativeWatermarks = new Map([['initiative-1', '2026-03-15T17:00:00.000Z']]);
|
|
@@ -213,8 +213,8 @@ describe('createSyncRecoveryCoordinator', () => {
|
|
|
213
213
|
expect(args.workspaceLastPushedWorkstreamWatermarksRef.current.size).toBe(0);
|
|
214
214
|
expect(args.workspaceLastPushedAiProfileWatermarksRef.current.size).toBe(0);
|
|
215
215
|
expect(args.workspaceLastPushedAiProfileChangeKeysRef.current.size).toBe(0);
|
|
216
|
-
expect(args.workspaceLastPushedDocumentWatermarksRef.current).toBe(
|
|
217
|
-
expect(args.workspaceLastPushedAssetWatermarksRef.current).toBe(
|
|
216
|
+
expect(args.workspaceLastPushedDocumentWatermarksRef.current.size).toBe(0);
|
|
217
|
+
expect(args.workspaceLastPushedAssetWatermarksRef.current.size).toBe(0);
|
|
218
218
|
expect(pullWorkspaceChangesFromCloud).toHaveBeenCalledWith({ repairMode: true });
|
|
219
219
|
expect(pushWorkspaceChangesToCloud).toHaveBeenCalledWith('signature-1', {
|
|
220
220
|
forceAllAiProfiles: true,
|
|
@@ -227,8 +227,8 @@ describe('createSyncRecoveryCoordinator', () => {
|
|
|
227
227
|
expect(persisted?.workstreamWatermarks.size).toBe(0);
|
|
228
228
|
expect(persisted?.aiProfileWatermarks.size).toBe(0);
|
|
229
229
|
expect(persisted?.aiProfileChangeKeys?.size).toBe(0);
|
|
230
|
-
expect(persisted?.documentWatermarks.
|
|
231
|
-
expect(persisted?.assetWatermarks.
|
|
230
|
+
expect(persisted?.documentWatermarks.size).toBe(0);
|
|
231
|
+
expect(persisted?.assetWatermarks.size).toBe(0);
|
|
232
232
|
expect(persisted?.taskEventWatermarks.get('event-1')).toBe('2026-03-15T17:00:00.000Z');
|
|
233
233
|
});
|
|
234
234
|
it('does not push during attach-cloud repair', async () => {
|
|
@@ -60,6 +60,7 @@ interface CreateWorkspaceTransferCoordinatorArgs {
|
|
|
60
60
|
refreshWorkspaceSyncMarkdownDocumentsSnapshot: () => Promise<void>;
|
|
61
61
|
refreshWorkspaceSyncAiProfilesSnapshot: () => Promise<void>;
|
|
62
62
|
refreshWorkspaceSyncAssetsSnapshot: () => Promise<void>;
|
|
63
|
+
refreshWorkspaceSyncPlanningEntitiesSnapshot: () => Promise<void>;
|
|
63
64
|
refreshWorkspaceSyncDocumentReviewSessionsSnapshot: () => Promise<void>;
|
|
64
65
|
refreshWorkspaceSyncAnnotatedAttachmentSessionsSnapshot: () => Promise<void>;
|
|
65
66
|
refreshLocalWorkspaceTaskCollections: () => Promise<void>;
|
|
@@ -122,7 +123,7 @@ interface CreateWorkspaceTransferCoordinatorArgs {
|
|
|
122
123
|
setTasks: Dispatch<SetStateAction<any[]>>;
|
|
123
124
|
setArchivedTasks: Dispatch<SetStateAction<any[]>>;
|
|
124
125
|
}
|
|
125
|
-
export declare function createWorkspaceTransferCoordinator({ cloudAuthConfigured, runtimeMode, workspaceCloudSyncEnabled, currentWorkspaceId, workspaceSyncPhase, isAuthenticated, checkAuthSession, resolveCloudAuthUrl, ensureCloudWorkspaceReadyForSync, ensureWorkspaceSyncReady, handleSyncAuthFailure, clearWorkspaceRetry, clearWorkspaceIssue, isWorkspaceRetryPending, scheduleWorkspaceSyncRetry, persistWorkspaceSyncPatchSafely, persistWorkspaceSyncPatchBestEffort, persistWorkspaceSyncWatermarksSnapshotBestEffort, reportSyncEvent, acquireWorkspaceSyncLease, releaseWorkspaceSyncLease, reportWorkspaceWindowContention, refreshWorkspaceSyncMarkdownDocumentsSnapshot, refreshWorkspaceSyncAiProfilesSnapshot, refreshWorkspaceSyncAssetsSnapshot, refreshWorkspaceSyncDocumentReviewSessionsSnapshot, refreshWorkspaceSyncAnnotatedAttachmentSessionsSnapshot, refreshLocalWorkspaceTaskCollections, buildWorkspaceSyncPayload, buildWorkspaceSyncSignature, seedWorkspaceSnapshotPushBaseline, shouldSuppressAttachBootstrapPush, recordSuppressedEventIds, workspaceRepairModeRef, workspaceForceFullAiProfilePushRef, workspacePushInFlightRef, workspacePullInFlightRef, workspacePendingPushReplayRef, workspaceLastPushedSignatureRef, workspacePendingSignatureRef, workspaceLastPushedTaskIdsRef, workspaceDeletedTaskIdsRef, workspaceDeletedTaskWatermarksRef, workspacePullCursorRef, workspaceLastPushedWatermarksRef, workspaceLastPushedTaskChangeKeysRef, workspaceLastPushedInitiativeIdsRef, workspaceLastPushedInitiativeWatermarksRef, workspaceLastPushedWorkstreamIdsRef, workspaceLastPushedWorkstreamWatermarksRef, workspaceLastPushedAiProfileIdsRef, workspaceLastPushedAiProfileWatermarksRef, workspaceLastPushedAiProfileChangeKeysRef, workspaceLastPushedDocumentPathsRef, workspaceLastPushedDocumentWatermarksRef, workspaceLastPushedAssetPathsRef, workspaceLastPushedAssetWatermarksRef, workspaceLastPushedDocumentReviewSessionIdsRef, workspaceLastPushedDocumentReviewSessionWatermarksRef, workspaceLastPushedDocumentReviewCommentIdsRef, workspaceLastPushedDocumentReviewCommentWatermarksRef, workspaceLastPushedAnnotatedAttachmentSessionIdsRef, workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef, workspaceLastPushedTaskEventWatermarksRef, workspaceBaselineResetReasonRef, workspaceCaptureAttachBootstrapBaselineRef, workspaceStartupFullReconcileRanRef, workspaceFullReconcileInFlightRef, bootstrapLastProgressAtRef, realtimeSuppressedEventQueueRef, setWorkspaceSyncBusy, setUserGlobalSyncStatus, setUserGlobalSyncError, setWorkspaceLastErrorMessage, setWorkspaceLastErrorAt, setWorkspaceLastPullAt, setWorkspaceLastPushAt, setWorkspaceSyncPendingChanges, setTasks, setArchivedTasks }: CreateWorkspaceTransferCoordinatorArgs): {
|
|
126
|
+
export declare function createWorkspaceTransferCoordinator({ cloudAuthConfigured, runtimeMode, workspaceCloudSyncEnabled, currentWorkspaceId, workspaceSyncPhase, isAuthenticated, checkAuthSession, resolveCloudAuthUrl, ensureCloudWorkspaceReadyForSync, ensureWorkspaceSyncReady, handleSyncAuthFailure, clearWorkspaceRetry, clearWorkspaceIssue, isWorkspaceRetryPending, scheduleWorkspaceSyncRetry, persistWorkspaceSyncPatchSafely, persistWorkspaceSyncPatchBestEffort, persistWorkspaceSyncWatermarksSnapshotBestEffort, reportSyncEvent, acquireWorkspaceSyncLease, releaseWorkspaceSyncLease, reportWorkspaceWindowContention, refreshWorkspaceSyncMarkdownDocumentsSnapshot, refreshWorkspaceSyncAiProfilesSnapshot, refreshWorkspaceSyncAssetsSnapshot, refreshWorkspaceSyncPlanningEntitiesSnapshot, refreshWorkspaceSyncDocumentReviewSessionsSnapshot, refreshWorkspaceSyncAnnotatedAttachmentSessionsSnapshot, refreshLocalWorkspaceTaskCollections, buildWorkspaceSyncPayload, buildWorkspaceSyncSignature, seedWorkspaceSnapshotPushBaseline, shouldSuppressAttachBootstrapPush, recordSuppressedEventIds, workspaceRepairModeRef, workspaceForceFullAiProfilePushRef, workspacePushInFlightRef, workspacePullInFlightRef, workspacePendingPushReplayRef, workspaceLastPushedSignatureRef, workspacePendingSignatureRef, workspaceLastPushedTaskIdsRef, workspaceDeletedTaskIdsRef, workspaceDeletedTaskWatermarksRef, workspacePullCursorRef, workspaceLastPushedWatermarksRef, workspaceLastPushedTaskChangeKeysRef, workspaceLastPushedInitiativeIdsRef, workspaceLastPushedInitiativeWatermarksRef, workspaceLastPushedWorkstreamIdsRef, workspaceLastPushedWorkstreamWatermarksRef, workspaceLastPushedAiProfileIdsRef, workspaceLastPushedAiProfileWatermarksRef, workspaceLastPushedAiProfileChangeKeysRef, workspaceLastPushedDocumentPathsRef, workspaceLastPushedDocumentWatermarksRef, workspaceLastPushedAssetPathsRef, workspaceLastPushedAssetWatermarksRef, workspaceLastPushedDocumentReviewSessionIdsRef, workspaceLastPushedDocumentReviewSessionWatermarksRef, workspaceLastPushedDocumentReviewCommentIdsRef, workspaceLastPushedDocumentReviewCommentWatermarksRef, workspaceLastPushedAnnotatedAttachmentSessionIdsRef, workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef, workspaceLastPushedTaskEventWatermarksRef, workspaceBaselineResetReasonRef, workspaceCaptureAttachBootstrapBaselineRef, workspaceStartupFullReconcileRanRef, workspaceFullReconcileInFlightRef, bootstrapLastProgressAtRef, realtimeSuppressedEventQueueRef, setWorkspaceSyncBusy, setUserGlobalSyncStatus, setUserGlobalSyncError, setWorkspaceLastErrorMessage, setWorkspaceLastErrorAt, setWorkspaceLastPullAt, setWorkspaceLastPushAt, setWorkspaceSyncPendingChanges, setTasks, setArchivedTasks }: CreateWorkspaceTransferCoordinatorArgs): {
|
|
126
127
|
pushWorkspaceChangesToCloud: (signature: string, options?: {
|
|
127
128
|
forceAllAiProfiles?: boolean;
|
|
128
129
|
repairMode?: boolean;
|
|
@@ -3,7 +3,7 @@ import { isTransientWorkspaceSyncStatus, runWorkspacePullSyncService, runWorkspa
|
|
|
3
3
|
import { createWorkspaceSyncLeaseHeldDecision, resolvePullSyncReadiness, resolvePushSyncReadiness } from './controlPlane';
|
|
4
4
|
import { resolveWorkspacePullRequestPlan, resolveWorkspacePullSuccessPlan } from './pullLifecycle';
|
|
5
5
|
import { logSyncDebug } from './orchestratorShared';
|
|
6
|
-
export function createWorkspaceTransferCoordinator({ cloudAuthConfigured, runtimeMode, workspaceCloudSyncEnabled, currentWorkspaceId, workspaceSyncPhase, isAuthenticated, checkAuthSession, resolveCloudAuthUrl, ensureCloudWorkspaceReadyForSync, ensureWorkspaceSyncReady, handleSyncAuthFailure, clearWorkspaceRetry, clearWorkspaceIssue, isWorkspaceRetryPending, scheduleWorkspaceSyncRetry, persistWorkspaceSyncPatchSafely, persistWorkspaceSyncPatchBestEffort, persistWorkspaceSyncWatermarksSnapshotBestEffort, reportSyncEvent, acquireWorkspaceSyncLease, releaseWorkspaceSyncLease, reportWorkspaceWindowContention, refreshWorkspaceSyncMarkdownDocumentsSnapshot, refreshWorkspaceSyncAiProfilesSnapshot, refreshWorkspaceSyncAssetsSnapshot, refreshWorkspaceSyncDocumentReviewSessionsSnapshot, refreshWorkspaceSyncAnnotatedAttachmentSessionsSnapshot, refreshLocalWorkspaceTaskCollections, buildWorkspaceSyncPayload, buildWorkspaceSyncSignature, seedWorkspaceSnapshotPushBaseline, shouldSuppressAttachBootstrapPush, recordSuppressedEventIds, workspaceRepairModeRef, workspaceForceFullAiProfilePushRef, workspacePushInFlightRef, workspacePullInFlightRef, workspacePendingPushReplayRef, workspaceLastPushedSignatureRef, workspacePendingSignatureRef, workspaceLastPushedTaskIdsRef, workspaceDeletedTaskIdsRef, workspaceDeletedTaskWatermarksRef, workspacePullCursorRef, workspaceLastPushedWatermarksRef, workspaceLastPushedTaskChangeKeysRef, workspaceLastPushedInitiativeIdsRef, workspaceLastPushedInitiativeWatermarksRef, workspaceLastPushedWorkstreamIdsRef, workspaceLastPushedWorkstreamWatermarksRef, workspaceLastPushedAiProfileIdsRef, workspaceLastPushedAiProfileWatermarksRef, workspaceLastPushedAiProfileChangeKeysRef, workspaceLastPushedDocumentPathsRef, workspaceLastPushedDocumentWatermarksRef, workspaceLastPushedAssetPathsRef, workspaceLastPushedAssetWatermarksRef, workspaceLastPushedDocumentReviewSessionIdsRef, workspaceLastPushedDocumentReviewSessionWatermarksRef, workspaceLastPushedDocumentReviewCommentIdsRef, workspaceLastPushedDocumentReviewCommentWatermarksRef, workspaceLastPushedAnnotatedAttachmentSessionIdsRef, workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef, workspaceLastPushedTaskEventWatermarksRef, workspaceBaselineResetReasonRef, workspaceCaptureAttachBootstrapBaselineRef, workspaceStartupFullReconcileRanRef, workspaceFullReconcileInFlightRef, bootstrapLastProgressAtRef, realtimeSuppressedEventQueueRef, setWorkspaceSyncBusy, setUserGlobalSyncStatus, setUserGlobalSyncError, setWorkspaceLastErrorMessage, setWorkspaceLastErrorAt, setWorkspaceLastPullAt, setWorkspaceLastPushAt, setWorkspaceSyncPendingChanges, setTasks, setArchivedTasks }) {
|
|
6
|
+
export function createWorkspaceTransferCoordinator({ cloudAuthConfigured, runtimeMode, workspaceCloudSyncEnabled, currentWorkspaceId, workspaceSyncPhase, isAuthenticated, checkAuthSession, resolveCloudAuthUrl, ensureCloudWorkspaceReadyForSync, ensureWorkspaceSyncReady, handleSyncAuthFailure, clearWorkspaceRetry, clearWorkspaceIssue, isWorkspaceRetryPending, scheduleWorkspaceSyncRetry, persistWorkspaceSyncPatchSafely, persistWorkspaceSyncPatchBestEffort, persistWorkspaceSyncWatermarksSnapshotBestEffort, reportSyncEvent, acquireWorkspaceSyncLease, releaseWorkspaceSyncLease, reportWorkspaceWindowContention, refreshWorkspaceSyncMarkdownDocumentsSnapshot, refreshWorkspaceSyncAiProfilesSnapshot, refreshWorkspaceSyncAssetsSnapshot, refreshWorkspaceSyncPlanningEntitiesSnapshot, refreshWorkspaceSyncDocumentReviewSessionsSnapshot, refreshWorkspaceSyncAnnotatedAttachmentSessionsSnapshot, refreshLocalWorkspaceTaskCollections, buildWorkspaceSyncPayload, buildWorkspaceSyncSignature, seedWorkspaceSnapshotPushBaseline, shouldSuppressAttachBootstrapPush, recordSuppressedEventIds, workspaceRepairModeRef, workspaceForceFullAiProfilePushRef, workspacePushInFlightRef, workspacePullInFlightRef, workspacePendingPushReplayRef, workspaceLastPushedSignatureRef, workspacePendingSignatureRef, workspaceLastPushedTaskIdsRef, workspaceDeletedTaskIdsRef, workspaceDeletedTaskWatermarksRef, workspacePullCursorRef, workspaceLastPushedWatermarksRef, workspaceLastPushedTaskChangeKeysRef, workspaceLastPushedInitiativeIdsRef, workspaceLastPushedInitiativeWatermarksRef, workspaceLastPushedWorkstreamIdsRef, workspaceLastPushedWorkstreamWatermarksRef, workspaceLastPushedAiProfileIdsRef, workspaceLastPushedAiProfileWatermarksRef, workspaceLastPushedAiProfileChangeKeysRef, workspaceLastPushedDocumentPathsRef, workspaceLastPushedDocumentWatermarksRef, workspaceLastPushedAssetPathsRef, workspaceLastPushedAssetWatermarksRef, workspaceLastPushedDocumentReviewSessionIdsRef, workspaceLastPushedDocumentReviewSessionWatermarksRef, workspaceLastPushedDocumentReviewCommentIdsRef, workspaceLastPushedDocumentReviewCommentWatermarksRef, workspaceLastPushedAnnotatedAttachmentSessionIdsRef, workspaceLastPushedAnnotatedAttachmentSessionWatermarksRef, workspaceLastPushedTaskEventWatermarksRef, workspaceBaselineResetReasonRef, workspaceCaptureAttachBootstrapBaselineRef, workspaceStartupFullReconcileRanRef, workspaceFullReconcileInFlightRef, bootstrapLastProgressAtRef, realtimeSuppressedEventQueueRef, setWorkspaceSyncBusy, setUserGlobalSyncStatus, setUserGlobalSyncError, setWorkspaceLastErrorMessage, setWorkspaceLastErrorAt, setWorkspaceLastPullAt, setWorkspaceLastPushAt, setWorkspaceSyncPendingChanges, setTasks, setArchivedTasks }) {
|
|
7
7
|
const pushWorkspaceChangesToCloud = async (signature, options) => {
|
|
8
8
|
let pushReadiness = resolvePushSyncReadiness({
|
|
9
9
|
cloudAuthConfigured,
|
|
@@ -61,16 +61,29 @@ export function createWorkspaceTransferCoordinator({ cloudAuthConfigured, runtim
|
|
|
61
61
|
const repairMode = options?.repairMode === true || workspaceRepairModeRef.current === true;
|
|
62
62
|
let payloadForAttempt = null;
|
|
63
63
|
let replayedPayload = false;
|
|
64
|
+
let attemptSignature = signature;
|
|
64
65
|
try {
|
|
65
66
|
await refreshWorkspaceSyncMarkdownDocumentsSnapshot();
|
|
66
67
|
await refreshWorkspaceSyncAiProfilesSnapshot();
|
|
67
68
|
await refreshWorkspaceSyncAssetsSnapshot();
|
|
69
|
+
await refreshWorkspaceSyncPlanningEntitiesSnapshot();
|
|
68
70
|
await refreshWorkspaceSyncDocumentReviewSessionsSnapshot();
|
|
69
71
|
const ready = await ensureWorkspaceSyncReady();
|
|
70
72
|
if (!ready)
|
|
71
73
|
return false;
|
|
74
|
+
const refreshedSignature = buildWorkspaceSyncSignature();
|
|
75
|
+
if (refreshedSignature) {
|
|
76
|
+
attemptSignature = refreshedSignature;
|
|
77
|
+
}
|
|
78
|
+
if (shouldSuppressAttachBootstrapPush(attemptSignature)) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
72
81
|
const pendingReplay = workspacePendingPushReplayRef.current;
|
|
73
|
-
const
|
|
82
|
+
const canReplayPayload = (pendingReplay
|
|
83
|
+
&& pendingReplay.signature === attemptSignature
|
|
84
|
+
&& (!forceAllAiProfiles || pendingReplay.forceAllAiProfiles === true)
|
|
85
|
+
&& (!repairMode || pendingReplay.repairMode === true));
|
|
86
|
+
const replayPayload = canReplayPayload
|
|
74
87
|
? pendingReplay
|
|
75
88
|
: null;
|
|
76
89
|
const payload = replayPayload?.payload || buildWorkspaceSyncPayload({ forceAllAiProfiles });
|
|
@@ -111,10 +124,10 @@ export function createWorkspaceTransferCoordinator({ cloudAuthConfigured, runtim
|
|
|
111
124
|
replayedPayload
|
|
112
125
|
}
|
|
113
126
|
});
|
|
114
|
-
workspacePendingSignatureRef.current =
|
|
127
|
+
workspacePendingSignatureRef.current = attemptSignature;
|
|
115
128
|
if (pushResult.transient || isTransientWorkspaceSyncStatus(pushResult.status)) {
|
|
116
129
|
workspacePendingPushReplayRef.current = {
|
|
117
|
-
signature,
|
|
130
|
+
signature: attemptSignature,
|
|
118
131
|
payload,
|
|
119
132
|
forceAllAiProfiles: replayPayload?.forceAllAiProfiles ?? forceAllAiProfiles,
|
|
120
133
|
repairMode: replayPayload?.repairMode ?? repairMode
|
|
@@ -130,7 +143,7 @@ export function createWorkspaceTransferCoordinator({ cloudAuthConfigured, runtim
|
|
|
130
143
|
return false;
|
|
131
144
|
}
|
|
132
145
|
workspacePendingPushReplayRef.current = null;
|
|
133
|
-
workspaceLastPushedSignatureRef.current =
|
|
146
|
+
workspaceLastPushedSignatureRef.current = attemptSignature;
|
|
134
147
|
workspaceLastPushedTaskIdsRef.current = pushResult.currentTaskIds;
|
|
135
148
|
workspaceLastPushedWatermarksRef.current = pushResult.currentTaskWatermarks
|
|
136
149
|
? new Map(pushResult.currentTaskWatermarks)
|
|
@@ -247,10 +260,10 @@ export function createWorkspaceTransferCoordinator({ cloudAuthConfigured, runtim
|
|
|
247
260
|
setUserGlobalSyncError(message);
|
|
248
261
|
setWorkspaceLastErrorMessage(message);
|
|
249
262
|
setWorkspaceLastErrorAt(new Date().toISOString());
|
|
250
|
-
workspacePendingSignatureRef.current =
|
|
263
|
+
workspacePendingSignatureRef.current = attemptSignature;
|
|
251
264
|
if (payloadForAttempt) {
|
|
252
265
|
workspacePendingPushReplayRef.current = {
|
|
253
|
-
signature,
|
|
266
|
+
signature: attemptSignature,
|
|
254
267
|
payload: payloadForAttempt,
|
|
255
268
|
forceAllAiProfiles,
|
|
256
269
|
repairMode
|
|
@@ -435,7 +448,8 @@ export function createWorkspaceTransferCoordinator({ cloudAuthConfigured, runtim
|
|
|
435
448
|
if (pullResult.appliedChanges > 0
|
|
436
449
|
&& (pullResult.pulledActiveUpserts
|
|
437
450
|
|| pullResult.pulledArchivedUpserts
|
|
438
|
-
|| pullResult.pulledTaskEventUpserts
|
|
451
|
+
|| pullResult.pulledTaskEventUpserts
|
|
452
|
+
|| pullResult.pulledPlanningUpserts)) {
|
|
439
453
|
try {
|
|
440
454
|
await refreshLocalWorkspaceTaskCollections();
|
|
441
455
|
}
|
|
@@ -508,7 +522,20 @@ export function createWorkspaceTransferCoordinator({ cloudAuthConfigured, runtim
|
|
|
508
522
|
});
|
|
509
523
|
}, 250);
|
|
510
524
|
}
|
|
511
|
-
|
|
525
|
+
const shouldReassertAiProfilesAfterFullReconcile = (options?.forceCursorNull === true
|
|
526
|
+
&& options?.repairMode !== true
|
|
527
|
+
&& workspaceSyncPhase === 'active');
|
|
528
|
+
if (shouldReassertAiProfilesAfterFullReconcile) {
|
|
529
|
+
const reassertSignature = buildWorkspaceSyncSignature();
|
|
530
|
+
if (reassertSignature) {
|
|
531
|
+
workspaceForceFullAiProfilePushRef.current = true;
|
|
532
|
+
workspacePendingSignatureRef.current = reassertSignature;
|
|
533
|
+
window.setTimeout(() => {
|
|
534
|
+
void pushWorkspaceChangesToCloud(reassertSignature, { forceAllAiProfiles: true });
|
|
535
|
+
}, 120);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
else if (pullSuccessPlan.deferredPushSignature) {
|
|
512
539
|
const deferredPushSignature = pullSuccessPlan.deferredPushSignature;
|
|
513
540
|
window.setTimeout(() => {
|
|
514
541
|
void pushWorkspaceChangesToCloud(deferredPushSignature);
|