@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
|
@@ -1,6 +1,64 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { createHash, randomUUID } from 'crypto';
|
|
4
|
+
function decodeTaskforceContextPath(value, normalizePath) {
|
|
5
|
+
const normalized = String(value || '').trim();
|
|
6
|
+
if (!normalized.startsWith('/api/taskforce/context/'))
|
|
7
|
+
return null;
|
|
8
|
+
const encoded = normalized.slice('/api/taskforce/context/'.length).split(/[?#]/, 1)[0] || '';
|
|
9
|
+
if (!encoded)
|
|
10
|
+
return null;
|
|
11
|
+
try {
|
|
12
|
+
return normalizePath(decodeURIComponent(encoded));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function isAiProfileAvatarAssetPath(value, workspaceId) {
|
|
19
|
+
const normalized = String(value || '').replace(/\\/g, '/');
|
|
20
|
+
return normalized.startsWith(`workspaces/${workspaceId}/assets/images/ai-profiles/`)
|
|
21
|
+
&& normalized.includes('/avatar/');
|
|
22
|
+
}
|
|
23
|
+
function inferBinaryAssetMimeType(assetPath) {
|
|
24
|
+
const ext = path.extname(assetPath).toLowerCase();
|
|
25
|
+
if (ext === '.png')
|
|
26
|
+
return 'image/png';
|
|
27
|
+
if (ext === '.jpg' || ext === '.jpeg')
|
|
28
|
+
return 'image/jpeg';
|
|
29
|
+
if (ext === '.webp')
|
|
30
|
+
return 'image/webp';
|
|
31
|
+
if (ext === '.gif')
|
|
32
|
+
return 'image/gif';
|
|
33
|
+
return 'application/octet-stream';
|
|
34
|
+
}
|
|
35
|
+
function inferAssetIdFromPath(assetPath) {
|
|
36
|
+
const match = String(assetPath || '').match(/(^|\/)(asset-[a-f0-9]{32})(?=[^/]*$)/i);
|
|
37
|
+
return match?.[2];
|
|
38
|
+
}
|
|
39
|
+
function listReferencedAiProfileAvatarAssetPaths(deps, workspaceId) {
|
|
40
|
+
const profiles = typeof deps.core.listAiProfiles === 'function'
|
|
41
|
+
? deps.core.listAiProfiles(workspaceId, { includeArchived: true })
|
|
42
|
+
: [];
|
|
43
|
+
const referenced = new Map();
|
|
44
|
+
for (const profile of Array.isArray(profiles) ? profiles : []) {
|
|
45
|
+
const updatedAt = String(profile?.updatedAt || profile?.createdAt || '').trim();
|
|
46
|
+
for (const url of [profile?.avatarUrl, profile?.avatarSourceUrl]) {
|
|
47
|
+
const assetPath = decodeTaskforceContextPath(url, deps.normalizeTaskDataRelativePath);
|
|
48
|
+
if (!assetPath || !isAiProfileAvatarAssetPath(assetPath, workspaceId))
|
|
49
|
+
continue;
|
|
50
|
+
if (referenced.has(assetPath))
|
|
51
|
+
continue;
|
|
52
|
+
referenced.set(assetPath, {
|
|
53
|
+
updatedAt,
|
|
54
|
+
assetId: inferAssetIdFromPath(assetPath),
|
|
55
|
+
originalFilename: path.basename(assetPath),
|
|
56
|
+
mimeType: inferBinaryAssetMimeType(assetPath)
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return referenced;
|
|
61
|
+
}
|
|
4
62
|
export function resolveCanonicalAssetSyncLinkState(deps, asset, workspaceId) {
|
|
5
63
|
if (!deps.workspaceAssetStore) {
|
|
6
64
|
return {
|
|
@@ -19,6 +77,28 @@ export function resolveCanonicalAssetSyncLinkState(deps, asset, workspaceId) {
|
|
|
19
77
|
}
|
|
20
78
|
return { primaryLink, effectiveUpdatedAt };
|
|
21
79
|
}
|
|
80
|
+
function resolveSyncLinkTarget(primaryLink) {
|
|
81
|
+
if (!primaryLink)
|
|
82
|
+
return null;
|
|
83
|
+
const targetId = String(primaryLink.targetId || primaryLink.taskId || '').trim();
|
|
84
|
+
if (!targetId)
|
|
85
|
+
return null;
|
|
86
|
+
const targetType = primaryLink.targetType === 'initiative' || primaryLink.targetType === 'workstream'
|
|
87
|
+
? primaryLink.targetType
|
|
88
|
+
: 'task';
|
|
89
|
+
return { targetType, targetId };
|
|
90
|
+
}
|
|
91
|
+
function resolveIncomingSyncLinkTarget(input) {
|
|
92
|
+
const linkTargetType = input.linkTargetType === 'initiative' || input.linkTargetType === 'workstream' || input.linkTargetType === 'task'
|
|
93
|
+
? input.linkTargetType
|
|
94
|
+
: null;
|
|
95
|
+
const linkTargetId = String(input.linkTargetId || '').trim();
|
|
96
|
+
if (linkTargetType && linkTargetId) {
|
|
97
|
+
return { targetType: linkTargetType, targetId: linkTargetId };
|
|
98
|
+
}
|
|
99
|
+
const taskId = String(input.taskId || '').trim();
|
|
100
|
+
return taskId ? { targetType: 'task', targetId: taskId } : null;
|
|
101
|
+
}
|
|
22
102
|
export function getCanonicalSyncDocumentMetadata(deps, relativePath, workspaceId) {
|
|
23
103
|
if (!deps.workspaceAssetStore)
|
|
24
104
|
return {};
|
|
@@ -26,12 +106,15 @@ export function getCanonicalSyncDocumentMetadata(deps, relativePath, workspaceId
|
|
|
26
106
|
if (!asset || asset.kind !== 'document')
|
|
27
107
|
return {};
|
|
28
108
|
const { primaryLink } = resolveCanonicalAssetSyncLinkState(deps, asset, workspaceId);
|
|
109
|
+
const linkTarget = resolveSyncLinkTarget(primaryLink);
|
|
29
110
|
return {
|
|
30
111
|
assetId: asset.assetId,
|
|
31
112
|
documentId: asset.documentId,
|
|
32
113
|
referenceNumber: asset.referenceNumber,
|
|
33
114
|
version: asset.version,
|
|
34
|
-
taskId:
|
|
115
|
+
taskId: linkTarget?.targetType === 'task' ? linkTarget.targetId : null,
|
|
116
|
+
linkTargetType: linkTarget?.targetType || null,
|
|
117
|
+
linkTargetId: linkTarget?.targetId || null,
|
|
35
118
|
logicalName: asset.logicalName || null,
|
|
36
119
|
caption: primaryLink?.displayName || null,
|
|
37
120
|
originalFilename: asset.originalFilename || path.basename(relativePath),
|
|
@@ -63,9 +146,8 @@ export function hasDocumentSyncMetadataDrift(deps, workspaceId, doc) {
|
|
|
63
146
|
const normalizedIncomingFilename = typeof doc.originalFilename === 'string' && doc.originalFilename.trim().length > 0
|
|
64
147
|
? doc.originalFilename.trim()
|
|
65
148
|
: null;
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
: null;
|
|
149
|
+
const incomingLinkTarget = resolveIncomingSyncLinkTarget(doc);
|
|
150
|
+
const primaryLinkTarget = resolveSyncLinkTarget(primaryLink);
|
|
69
151
|
const normalizedIncomingLinkRole = doc.linkRole === 'reference' ? 'reference' : 'attachment';
|
|
70
152
|
const referenceNumberMismatch = Boolean(existingAsset.referenceNumber
|
|
71
153
|
&& normalizedIncomingReference
|
|
@@ -75,7 +157,8 @@ export function hasDocumentSyncMetadataDrift(deps, workspaceId, doc) {
|
|
|
75
157
|
|| (existingAsset.documentId || null) !== normalizedIncomingDocumentId
|
|
76
158
|
|| (existingAsset.logicalName || null) !== normalizedIncomingLogicalName
|
|
77
159
|
|| (existingAsset.originalFilename || null) !== normalizedIncomingFilename
|
|
78
|
-
|| (
|
|
160
|
+
|| (primaryLinkTarget?.targetType || null) !== (incomingLinkTarget?.targetType || null)
|
|
161
|
+
|| (primaryLinkTarget?.targetId || null) !== (incomingLinkTarget?.targetId || null)
|
|
79
162
|
|| (primaryLink?.displayName || null) !== normalizedIncomingCaption
|
|
80
163
|
|| ((primaryLink?.role === 'reference' ? 'reference' : 'attachment')) !== normalizedIncomingLinkRole;
|
|
81
164
|
}
|
|
@@ -98,9 +181,8 @@ export function hasBinaryAssetSyncMetadataDrift(deps, workspaceId, asset) {
|
|
|
98
181
|
const normalizedIncomingFilename = typeof asset.originalFilename === 'string' && asset.originalFilename.trim().length > 0
|
|
99
182
|
? asset.originalFilename.trim()
|
|
100
183
|
: null;
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
: null;
|
|
184
|
+
const incomingLinkTarget = resolveIncomingSyncLinkTarget(asset);
|
|
185
|
+
const primaryLinkTarget = resolveSyncLinkTarget(primaryLink);
|
|
104
186
|
const normalizedIncomingLinkRole = asset.linkRole === 'reference'
|
|
105
187
|
? 'reference'
|
|
106
188
|
: asset.kind === 'image'
|
|
@@ -112,7 +194,8 @@ export function hasBinaryAssetSyncMetadataDrift(deps, workspaceId, asset) {
|
|
|
112
194
|
return referenceNumberMismatch
|
|
113
195
|
|| (existingAsset.logicalName || null) !== normalizedIncomingLogicalName
|
|
114
196
|
|| (existingAsset.originalFilename || null) !== normalizedIncomingFilename
|
|
115
|
-
|| (
|
|
197
|
+
|| (primaryLinkTarget?.targetType || null) !== (incomingLinkTarget?.targetType || null)
|
|
198
|
+
|| (primaryLinkTarget?.targetId || null) !== (incomingLinkTarget?.targetId || null)
|
|
116
199
|
|| (primaryLink?.displayName || null) !== normalizedIncomingCaption
|
|
117
200
|
|| ((primaryLink?.role === 'reference'
|
|
118
201
|
? 'reference'
|
|
@@ -127,7 +210,7 @@ export function reconcileWorkspaceSyncLinks(deps, input) {
|
|
|
127
210
|
const assetId = String(input.assetId || '').trim();
|
|
128
211
|
if (!assetId)
|
|
129
212
|
return;
|
|
130
|
-
const
|
|
213
|
+
const desiredLinkTarget = resolveIncomingSyncLinkTarget(input);
|
|
131
214
|
const desiredRole = input.role === 'reference'
|
|
132
215
|
? 'reference'
|
|
133
216
|
: input.role === 'image'
|
|
@@ -135,21 +218,25 @@ export function reconcileWorkspaceSyncLinks(deps, input) {
|
|
|
135
218
|
: 'attachment';
|
|
136
219
|
const linkUpdatedAt = String(input.updatedAt || '').trim() || new Date().toISOString();
|
|
137
220
|
const existingLinks = deps.workspaceAssetStore.listLinksForAsset(assetId, workspaceId, { includeDeleted: true });
|
|
138
|
-
if (
|
|
221
|
+
if (desiredLinkTarget) {
|
|
139
222
|
deps.workspaceAssetStore.upsertLink({
|
|
140
223
|
workspaceId,
|
|
141
224
|
assetId,
|
|
142
|
-
|
|
225
|
+
targetType: desiredLinkTarget.targetType,
|
|
226
|
+
targetId: desiredLinkTarget.targetId,
|
|
143
227
|
role: desiredRole,
|
|
144
228
|
displayName: typeof input.displayName === 'string' && input.displayName.trim().length > 0 ? input.displayName.trim() : null,
|
|
145
229
|
updatedAt: linkUpdatedAt,
|
|
146
230
|
deletedAt: null
|
|
147
231
|
});
|
|
148
232
|
}
|
|
149
|
-
if (
|
|
233
|
+
if (desiredLinkTarget) {
|
|
150
234
|
for (const link of existingLinks) {
|
|
151
|
-
if (link.
|
|
235
|
+
if (link.targetType === desiredLinkTarget.targetType
|
|
236
|
+
&& link.targetId === desiredLinkTarget.targetId
|
|
237
|
+
&& link.role === desiredRole) {
|
|
152
238
|
continue;
|
|
239
|
+
}
|
|
153
240
|
if (link.deletedAt)
|
|
154
241
|
continue;
|
|
155
242
|
deps.workspaceAssetStore.deleteLink({
|
|
@@ -364,14 +451,13 @@ export async function listWorkspaceSyncDocumentIndex(deps, basePath, workspaceId
|
|
|
364
451
|
return docs;
|
|
365
452
|
}
|
|
366
453
|
export async function listWorkspaceSyncBinaryAssetsSnapshot(deps, basePath, workspaceId = 'default') {
|
|
367
|
-
if (!deps.workspaceAssetStore)
|
|
368
|
-
return [];
|
|
369
454
|
const root = path.resolve(basePath);
|
|
370
455
|
const objectStorageClient = deps.getObjectStorageClient();
|
|
371
|
-
const assets = deps.workspaceAssetStore
|
|
456
|
+
const assets = deps.workspaceAssetStore?.listAllByWorkspace(workspaceId, {
|
|
372
457
|
includeDeleted: false
|
|
373
|
-
});
|
|
458
|
+
}) || [];
|
|
374
459
|
const snapshots = [];
|
|
460
|
+
const emittedPaths = new Set();
|
|
375
461
|
for (const asset of assets) {
|
|
376
462
|
if (asset.kind !== 'image' && asset.kind !== 'file')
|
|
377
463
|
continue;
|
|
@@ -382,6 +468,7 @@ export async function listWorkspaceSyncBinaryAssetsSnapshot(deps, basePath, work
|
|
|
382
468
|
continue;
|
|
383
469
|
if (!deps.normalizeTaskDataRelativePath(storageKey))
|
|
384
470
|
continue;
|
|
471
|
+
emittedPaths.add(storageKey);
|
|
385
472
|
try {
|
|
386
473
|
let buffer = null;
|
|
387
474
|
if (asset.storageProvider === 'r2' && objectStorageClient) {
|
|
@@ -412,6 +499,7 @@ export async function listWorkspaceSyncBinaryAssetsSnapshot(deps, basePath, work
|
|
|
412
499
|
continue;
|
|
413
500
|
}
|
|
414
501
|
const { primaryLink, effectiveUpdatedAt } = resolveCanonicalAssetSyncLinkState(deps, asset, workspaceId);
|
|
502
|
+
const linkTarget = resolveSyncLinkTarget(primaryLink);
|
|
415
503
|
snapshots.push({
|
|
416
504
|
path: storageKey,
|
|
417
505
|
updatedAt: effectiveUpdatedAt,
|
|
@@ -420,7 +508,9 @@ export async function listWorkspaceSyncBinaryAssetsSnapshot(deps, basePath, work
|
|
|
420
508
|
kind: asset.kind,
|
|
421
509
|
mimeType: asset.mimeType || 'application/octet-stream',
|
|
422
510
|
referenceNumber: asset.referenceNumber,
|
|
423
|
-
taskId:
|
|
511
|
+
taskId: linkTarget?.targetType === 'task' ? linkTarget.targetId : null,
|
|
512
|
+
linkTargetType: linkTarget?.targetType || null,
|
|
513
|
+
linkTargetId: linkTarget?.targetId || null,
|
|
424
514
|
logicalName: asset.logicalName || null,
|
|
425
515
|
caption: primaryLink?.displayName || null,
|
|
426
516
|
originalFilename: asset.originalFilename || path.basename(storageKey),
|
|
@@ -435,15 +525,56 @@ export async function listWorkspaceSyncBinaryAssetsSnapshot(deps, basePath, work
|
|
|
435
525
|
});
|
|
436
526
|
}
|
|
437
527
|
}
|
|
528
|
+
const referencedAiAvatarAssets = listReferencedAiProfileAvatarAssetPaths(deps, workspaceId);
|
|
529
|
+
for (const [assetPath, metadata] of referencedAiAvatarAssets.entries()) {
|
|
530
|
+
if (emittedPaths.has(assetPath))
|
|
531
|
+
continue;
|
|
532
|
+
const absolutePath = path.resolve(root, assetPath);
|
|
533
|
+
if (absolutePath === root || !absolutePath.startsWith(`${root}${path.sep}`))
|
|
534
|
+
continue;
|
|
535
|
+
try {
|
|
536
|
+
let buffer = null;
|
|
537
|
+
if (objectStorageClient) {
|
|
538
|
+
const object = await objectStorageClient.getObject(assetPath);
|
|
539
|
+
buffer = object?.body || null;
|
|
540
|
+
}
|
|
541
|
+
else if (fs.existsSync(absolutePath)) {
|
|
542
|
+
buffer = fs.readFileSync(absolutePath);
|
|
543
|
+
}
|
|
544
|
+
if (!buffer)
|
|
545
|
+
continue;
|
|
546
|
+
snapshots.push({
|
|
547
|
+
path: assetPath,
|
|
548
|
+
updatedAt: metadata.updatedAt || new Date().toISOString(),
|
|
549
|
+
contentBase64: buffer.toString('base64'),
|
|
550
|
+
...(metadata.assetId ? { assetId: metadata.assetId } : {}),
|
|
551
|
+
kind: 'image',
|
|
552
|
+
mimeType: metadata.mimeType,
|
|
553
|
+
referenceNumber: null,
|
|
554
|
+
taskId: null,
|
|
555
|
+
linkTargetType: null,
|
|
556
|
+
linkTargetId: null,
|
|
557
|
+
logicalName: null,
|
|
558
|
+
caption: null,
|
|
559
|
+
originalFilename: metadata.originalFilename,
|
|
560
|
+
linkRole: 'image'
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
catch (error) {
|
|
564
|
+
deps.recordSyncDiagnostic(workspaceId, 'pull', 'Failed to read referenced AI profile avatar asset.', {
|
|
565
|
+
source: 'assets.snapshot.aiProfileAvatar',
|
|
566
|
+
path: assetPath,
|
|
567
|
+
error: String(error?.message || error || '')
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
}
|
|
438
571
|
return snapshots;
|
|
439
572
|
}
|
|
440
573
|
export async function listWorkspaceSyncBinaryAssetIndex(deps, workspaceId = 'default') {
|
|
441
|
-
|
|
442
|
-
return [];
|
|
443
|
-
const assets = deps.workspaceAssetStore.listAllByWorkspace(workspaceId, {
|
|
574
|
+
const assets = deps.workspaceAssetStore?.listAllByWorkspace(workspaceId, {
|
|
444
575
|
includeDeleted: false
|
|
445
|
-
});
|
|
446
|
-
|
|
576
|
+
}) || [];
|
|
577
|
+
const indexed = assets
|
|
447
578
|
.filter((asset) => asset.kind === 'image' || asset.kind === 'file')
|
|
448
579
|
.filter((asset) => asset.storageProvider !== 'external')
|
|
449
580
|
.filter((asset) => {
|
|
@@ -453,6 +584,7 @@ export async function listWorkspaceSyncBinaryAssetIndex(deps, workspaceId = 'def
|
|
|
453
584
|
.map((asset) => {
|
|
454
585
|
const storageKey = String(asset.storageKey || '').trim();
|
|
455
586
|
const { primaryLink, effectiveUpdatedAt } = resolveCanonicalAssetSyncLinkState(deps, asset, workspaceId);
|
|
587
|
+
const linkTarget = resolveSyncLinkTarget(primaryLink);
|
|
456
588
|
const assetKind = asset.kind === 'image' ? 'image' : 'file';
|
|
457
589
|
return {
|
|
458
590
|
path: storageKey,
|
|
@@ -461,7 +593,9 @@ export async function listWorkspaceSyncBinaryAssetIndex(deps, workspaceId = 'def
|
|
|
461
593
|
kind: assetKind,
|
|
462
594
|
mimeType: asset.mimeType || 'application/octet-stream',
|
|
463
595
|
referenceNumber: asset.referenceNumber,
|
|
464
|
-
taskId:
|
|
596
|
+
taskId: linkTarget?.targetType === 'task' ? linkTarget.targetId : null,
|
|
597
|
+
linkTargetType: linkTarget?.targetType || null,
|
|
598
|
+
linkTargetId: linkTarget?.targetId || null,
|
|
465
599
|
logicalName: asset.logicalName || null,
|
|
466
600
|
caption: primaryLink?.displayName || null,
|
|
467
601
|
originalFilename: asset.originalFilename || path.basename(storageKey),
|
|
@@ -469,6 +603,29 @@ export async function listWorkspaceSyncBinaryAssetIndex(deps, workspaceId = 'def
|
|
|
469
603
|
};
|
|
470
604
|
})
|
|
471
605
|
.filter((entry) => Number.isFinite(Date.parse(entry.updatedAt)) && entry.path.length > 0);
|
|
606
|
+
const indexedPaths = new Set(indexed.map((entry) => entry.path));
|
|
607
|
+
for (const [assetPath, metadata] of listReferencedAiProfileAvatarAssetPaths(deps, workspaceId).entries()) {
|
|
608
|
+
if (indexedPaths.has(assetPath))
|
|
609
|
+
continue;
|
|
610
|
+
if (!Number.isFinite(Date.parse(metadata.updatedAt)))
|
|
611
|
+
continue;
|
|
612
|
+
indexed.push({
|
|
613
|
+
path: assetPath,
|
|
614
|
+
updatedAt: metadata.updatedAt,
|
|
615
|
+
...(metadata.assetId ? { assetId: metadata.assetId } : {}),
|
|
616
|
+
kind: 'image',
|
|
617
|
+
mimeType: metadata.mimeType,
|
|
618
|
+
referenceNumber: null,
|
|
619
|
+
taskId: null,
|
|
620
|
+
linkTargetType: null,
|
|
621
|
+
linkTargetId: null,
|
|
622
|
+
logicalName: null,
|
|
623
|
+
caption: null,
|
|
624
|
+
originalFilename: metadata.originalFilename,
|
|
625
|
+
linkRole: 'image'
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
return indexed;
|
|
472
629
|
}
|
|
473
630
|
export async function loadWorkspaceSyncDocumentPayload(deps, basePath, workspaceId, relativePath) {
|
|
474
631
|
const normalizedPath = deps.normalizeWorkspaceSyncDocumentPath(relativePath);
|
|
@@ -571,16 +728,18 @@ export async function loadWorkspaceSyncBinaryAssetPayload(deps, basePath, worksp
|
|
|
571
728
|
const normalizedPath = deps.normalizeTaskDataRelativePath(assetPath);
|
|
572
729
|
if (!normalizedPath)
|
|
573
730
|
return null;
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
731
|
+
const asset = deps.workspaceAssetStore?.getByStorageKey(normalizedPath, workspaceId) || null;
|
|
732
|
+
const referencedAiAvatar = !asset
|
|
733
|
+
? listReferencedAiProfileAvatarAssetPaths(deps, workspaceId).get(normalizedPath) || null
|
|
734
|
+
: null;
|
|
735
|
+
if (!referencedAiAvatar
|
|
736
|
+
&& (!asset || (asset.kind !== 'image' && asset.kind !== 'file') || asset.storageProvider === 'external')) {
|
|
578
737
|
return null;
|
|
579
738
|
}
|
|
580
739
|
const objectStorageClient = deps.getObjectStorageClient();
|
|
581
740
|
let buffer = null;
|
|
582
741
|
try {
|
|
583
|
-
if (asset
|
|
742
|
+
if ((asset?.storageProvider === 'r2' || referencedAiAvatar) && objectStorageClient) {
|
|
584
743
|
const object = await objectStorageClient.getObject(normalizedPath);
|
|
585
744
|
buffer = object?.body || null;
|
|
586
745
|
}
|
|
@@ -593,28 +752,59 @@ export async function loadWorkspaceSyncBinaryAssetPayload(deps, basePath, worksp
|
|
|
593
752
|
return null;
|
|
594
753
|
buffer = fs.readFileSync(absolutePath);
|
|
595
754
|
}
|
|
755
|
+
if (!buffer && referencedAiAvatar && objectStorageClient) {
|
|
756
|
+
const root = path.resolve(basePath);
|
|
757
|
+
const absolutePath = path.resolve(root, normalizedPath);
|
|
758
|
+
if (absolutePath !== root && absolutePath.startsWith(`${root}${path.sep}`) && fs.existsSync(absolutePath)) {
|
|
759
|
+
buffer = fs.readFileSync(absolutePath);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
596
762
|
}
|
|
597
763
|
catch (error) {
|
|
598
764
|
deps.recordSyncDiagnostic(workspaceId, 'pull', 'Failed to load targeted binary asset payload.', {
|
|
599
765
|
source: 'assets.snapshot.loadPayload',
|
|
600
766
|
path: normalizedPath,
|
|
601
|
-
assetId: asset
|
|
767
|
+
assetId: asset?.assetId || referencedAiAvatar?.assetId,
|
|
602
768
|
error: String(error?.message || error || '')
|
|
603
769
|
});
|
|
604
770
|
return null;
|
|
605
771
|
}
|
|
606
772
|
if (!buffer)
|
|
607
773
|
return null;
|
|
774
|
+
if (!asset && referencedAiAvatar) {
|
|
775
|
+
return {
|
|
776
|
+
path: normalizedPath,
|
|
777
|
+
updatedAt: referencedAiAvatar.updatedAt || new Date().toISOString(),
|
|
778
|
+
contentBase64: buffer.toString('base64'),
|
|
779
|
+
...(referencedAiAvatar.assetId ? { assetId: referencedAiAvatar.assetId } : {}),
|
|
780
|
+
kind: 'image',
|
|
781
|
+
mimeType: referencedAiAvatar.mimeType,
|
|
782
|
+
referenceNumber: null,
|
|
783
|
+
taskId: null,
|
|
784
|
+
linkTargetType: null,
|
|
785
|
+
linkTargetId: null,
|
|
786
|
+
logicalName: null,
|
|
787
|
+
caption: null,
|
|
788
|
+
originalFilename: referencedAiAvatar.originalFilename,
|
|
789
|
+
linkRole: 'image'
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
if (!asset)
|
|
793
|
+
return null;
|
|
608
794
|
const { primaryLink, effectiveUpdatedAt } = resolveCanonicalAssetSyncLinkState(deps, asset, workspaceId);
|
|
795
|
+
const linkTarget = resolveSyncLinkTarget(primaryLink);
|
|
796
|
+
const assetKind = asset.kind === 'image' ? 'image' : 'file';
|
|
609
797
|
return {
|
|
610
798
|
path: normalizedPath,
|
|
611
799
|
updatedAt: effectiveUpdatedAt,
|
|
612
800
|
contentBase64: buffer.toString('base64'),
|
|
613
801
|
assetId: asset.assetId,
|
|
614
|
-
kind:
|
|
802
|
+
kind: assetKind,
|
|
615
803
|
mimeType: asset.mimeType || 'application/octet-stream',
|
|
616
804
|
referenceNumber: asset.referenceNumber,
|
|
617
|
-
taskId:
|
|
805
|
+
taskId: linkTarget?.targetType === 'task' ? linkTarget.targetId : null,
|
|
806
|
+
linkTargetType: linkTarget?.targetType || null,
|
|
807
|
+
linkTargetId: linkTarget?.targetId || null,
|
|
618
808
|
logicalName: asset.logicalName || null,
|
|
619
809
|
caption: primaryLink?.displayName || null,
|
|
620
810
|
originalFilename: asset.originalFilename || path.basename(normalizedPath),
|
|
@@ -719,11 +909,13 @@ export async function writeWorkspaceSyncDocument(deps, basePath, doc, workspaceI
|
|
|
719
909
|
purgeAfter: null,
|
|
720
910
|
allowReferenceNumberReassignment
|
|
721
911
|
});
|
|
722
|
-
const
|
|
912
|
+
const linkTarget = resolveIncomingSyncLinkTarget(doc);
|
|
723
913
|
reconcileWorkspaceSyncLinks(deps, {
|
|
724
914
|
workspaceId,
|
|
725
915
|
assetId: asset.assetId,
|
|
726
|
-
taskId,
|
|
916
|
+
taskId: linkTarget?.targetType === 'task' ? linkTarget.targetId : null,
|
|
917
|
+
linkTargetType: linkTarget?.targetType || null,
|
|
918
|
+
linkTargetId: linkTarget?.targetId || null,
|
|
727
919
|
displayName: typeof doc.caption === 'string' && doc.caption.trim().length > 0 ? doc.caption.trim() : null,
|
|
728
920
|
role: doc.linkRole === 'reference' ? 'reference' : 'attachment',
|
|
729
921
|
updatedAt: effectiveUpdatedAt
|
|
@@ -811,11 +1003,13 @@ export async function writeWorkspaceSyncBinaryAsset(deps, basePath, asset, works
|
|
|
811
1003
|
purgeAfter: null,
|
|
812
1004
|
allowReferenceNumberReassignment
|
|
813
1005
|
});
|
|
814
|
-
const
|
|
1006
|
+
const linkTarget = resolveIncomingSyncLinkTarget(asset);
|
|
815
1007
|
reconcileWorkspaceSyncLinks(deps, {
|
|
816
1008
|
workspaceId,
|
|
817
1009
|
assetId: assetRecord.assetId,
|
|
818
|
-
taskId,
|
|
1010
|
+
taskId: linkTarget?.targetType === 'task' ? linkTarget.targetId : null,
|
|
1011
|
+
linkTargetType: linkTarget?.targetType || null,
|
|
1012
|
+
linkTargetId: linkTarget?.targetId || null,
|
|
819
1013
|
displayName: typeof asset.caption === 'string' && asset.caption.trim().length > 0 ? asset.caption.trim() : null,
|
|
820
1014
|
role: asset.linkRole === 'reference' ? 'reference' : asset.kind === 'image' ? 'image' : 'attachment',
|
|
821
1015
|
updatedAt: effectiveUpdatedAt
|
|
@@ -23,6 +23,9 @@ function toOptionalInteger(value) {
|
|
|
23
23
|
return null;
|
|
24
24
|
return Math.floor(normalized);
|
|
25
25
|
}
|
|
26
|
+
function clonePlanningAttachments(value) {
|
|
27
|
+
return Array.isArray(value) ? value : undefined;
|
|
28
|
+
}
|
|
26
29
|
export function normalizeTaxonomySyncPayload(raw) {
|
|
27
30
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
28
31
|
return null;
|
|
@@ -56,6 +59,7 @@ export function normalizeInitiativeSyncPayload(raw, sourceWatermark) {
|
|
|
56
59
|
createdAt: String(source.createdAt || sourceWatermark || '').trim(),
|
|
57
60
|
updatedAt: String(source.updatedAt || sourceWatermark || source.createdAt || '').trim(),
|
|
58
61
|
order: toOptionalInteger(source.order),
|
|
62
|
+
attachments: clonePlanningAttachments(source.attachments),
|
|
59
63
|
isArchived: Boolean(source.isArchived),
|
|
60
64
|
};
|
|
61
65
|
}
|
|
@@ -77,6 +81,7 @@ export function normalizeWorkstreamSyncPayload(raw, sourceWatermark) {
|
|
|
77
81
|
createdAt: String(source.createdAt || sourceWatermark || '').trim(),
|
|
78
82
|
updatedAt: String(source.updatedAt || sourceWatermark || source.createdAt || '').trim(),
|
|
79
83
|
order: toOptionalInteger(source.order),
|
|
84
|
+
attachments: clonePlanningAttachments(source.attachments),
|
|
80
85
|
isArchived: Boolean(source.isArchived),
|
|
81
86
|
};
|
|
82
87
|
}
|
|
@@ -22,6 +22,12 @@ describe('planningSyncPayload', () => {
|
|
|
22
22
|
createdAt: '2026-04-10T10:00:00.000Z',
|
|
23
23
|
updatedAt: '2026-04-10T11:00:00.000Z',
|
|
24
24
|
order: '2',
|
|
25
|
+
attachments: [{
|
|
26
|
+
path: 'workspaces/ws-1/assets/documents/asset-initiative.md',
|
|
27
|
+
assetId: 'asset-initiative',
|
|
28
|
+
referenceLabel: 'D-10',
|
|
29
|
+
caption: 'Launch plan',
|
|
30
|
+
}],
|
|
25
31
|
isArchived: true,
|
|
26
32
|
});
|
|
27
33
|
expect(initiative).toEqual({
|
|
@@ -34,6 +40,12 @@ describe('planningSyncPayload', () => {
|
|
|
34
40
|
createdAt: '2026-04-10T10:00:00.000Z',
|
|
35
41
|
updatedAt: '2026-04-10T11:00:00.000Z',
|
|
36
42
|
order: 2,
|
|
43
|
+
attachments: [{
|
|
44
|
+
path: 'workspaces/ws-1/assets/documents/asset-initiative.md',
|
|
45
|
+
assetId: 'asset-initiative',
|
|
46
|
+
referenceLabel: 'D-10',
|
|
47
|
+
caption: 'Launch plan',
|
|
48
|
+
}],
|
|
37
49
|
isArchived: true,
|
|
38
50
|
});
|
|
39
51
|
});
|
|
@@ -49,6 +61,12 @@ describe('planningSyncPayload', () => {
|
|
|
49
61
|
createdAt: '2026-04-10T10:00:00.000Z',
|
|
50
62
|
updatedAt: '2026-04-10T11:00:00.000Z',
|
|
51
63
|
order: '5',
|
|
64
|
+
attachments: [{
|
|
65
|
+
path: 'workspaces/ws-1/assets/documents/asset-workstream.md',
|
|
66
|
+
assetId: 'asset-workstream',
|
|
67
|
+
referenceLabel: 'D-11',
|
|
68
|
+
caption: 'Backend plan',
|
|
69
|
+
}],
|
|
52
70
|
isArchived: false,
|
|
53
71
|
});
|
|
54
72
|
expect(workstream).toEqual({
|
|
@@ -62,6 +80,12 @@ describe('planningSyncPayload', () => {
|
|
|
62
80
|
createdAt: '2026-04-10T10:00:00.000Z',
|
|
63
81
|
updatedAt: '2026-04-10T11:00:00.000Z',
|
|
64
82
|
order: 5,
|
|
83
|
+
attachments: [{
|
|
84
|
+
path: 'workspaces/ws-1/assets/documents/asset-workstream.md',
|
|
85
|
+
assetId: 'asset-workstream',
|
|
86
|
+
referenceLabel: 'D-11',
|
|
87
|
+
caption: 'Backend plan',
|
|
88
|
+
}],
|
|
65
89
|
isArchived: false,
|
|
66
90
|
});
|
|
67
91
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TaskforceCore, TaskforceSyncCapable } from '../core/Taskforce.js';
|
|
2
|
-
import type { WorkspaceSyncAiProfileSnapshot, WorkspaceSyncAnnotatedAttachmentSessionSnapshot, WorkspaceSyncChange, WorkspaceSyncDocumentReviewCommentSnapshot, WorkspaceSyncDocumentReviewSessionSnapshot,
|
|
2
|
+
import type { WorkspaceSyncAiProfileSnapshot, WorkspaceSyncAnnotatedAttachmentSessionSnapshot, WorkspaceSyncChange, WorkspaceSyncDocumentReviewCommentSnapshot, WorkspaceSyncDocumentReviewSessionSnapshot, WorkspaceSyncEntityEventSnapshot, WorkspaceSyncInitiativeSnapshot, WorkspaceSyncTaskSnapshot, WorkspaceSyncWorkspaceMemberSnapshot, WorkspaceSyncWorkstreamSnapshot } from './workspaceSyncModel.js';
|
|
3
3
|
import type { BinaryAssetSyncPayload, DocumentSyncPayload } from './contentSyncPayload.js';
|
|
4
4
|
import type { AiProfileSurfaceType } from '../shared/aiProfileSurfaceType.js';
|
|
5
5
|
import type { AiProfileSeatScope } from '../shared/aiProfileSeatScope.js';
|
|
@@ -24,7 +24,7 @@ type NormalizeIncomingAiProfileSeatScope = (value: unknown, context: {
|
|
|
24
24
|
profileId?: string;
|
|
25
25
|
workspaceId?: string;
|
|
26
26
|
}) => AiProfileSeatScope | null;
|
|
27
|
-
type NormalizeTaskEventSyncPayload = (raw: unknown, workspaceId: string, sourceWatermark?: string) =>
|
|
27
|
+
type NormalizeTaskEventSyncPayload = (raw: unknown, workspaceId: string, sourceWatermark?: string) => WorkspaceSyncEntityEventSnapshot | null;
|
|
28
28
|
export interface WorkspaceSyncIncomingTaskMutation {
|
|
29
29
|
archived: boolean;
|
|
30
30
|
task: WorkspaceSyncTaskSnapshot;
|
|
@@ -60,7 +60,7 @@ export interface WorkspaceSyncIncomingApplyModel {
|
|
|
60
60
|
incomingAnnotatedAttachmentSessionDeletes: Map<string, string>;
|
|
61
61
|
};
|
|
62
62
|
taskActivity: {
|
|
63
|
-
incomingTaskEvents: Map<string,
|
|
63
|
+
incomingTaskEvents: Map<string, WorkspaceSyncEntityEventSnapshot>;
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
export interface PreparedWorkspaceTaskSyncSnapshot {
|
|
@@ -169,7 +169,7 @@ export declare function applyCollaborationSyncChanges(input: {
|
|
|
169
169
|
export declare function applyTaskActivitySyncChanges(input: {
|
|
170
170
|
core: SyncCore;
|
|
171
171
|
workspaceId: string;
|
|
172
|
-
incomingTaskEvents: Map<string,
|
|
172
|
+
incomingTaskEvents: Map<string, WorkspaceSyncEntityEventSnapshot>;
|
|
173
173
|
recordSyncDiagnostic: RecordSyncDiagnostic;
|
|
174
174
|
createSyncRouteError: CreateSyncRouteError;
|
|
175
175
|
}): void;
|