@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.
Files changed (232) hide show
  1. package/README.md +0 -2
  2. package/dist/Taskforce.module.css +317 -27
  3. package/dist/TaskforceCore.js +1 -0
  4. package/dist/TaskforceCore.test.js +178 -0
  5. package/dist/components/activity/EntityActivityTimeline.d.ts +34 -0
  6. package/dist/components/activity/EntityActivityTimeline.js +81 -0
  7. package/dist/components/context/ContextAttachmentManager.js +15 -3
  8. package/dist/components/features/AgentsModule.d.ts +5 -1
  9. package/dist/components/features/AgentsModule.js +142 -39
  10. package/dist/components/features/AgentsModule.test.js +260 -11
  11. package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +3 -1
  12. package/dist/components/features/AnnotatedAttachmentWorkspace.js +118 -4
  13. package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +52 -0
  14. package/dist/components/features/DocumentIndex.d.ts +2 -2
  15. package/dist/components/features/DocumentIndex.js +3 -4
  16. package/dist/components/features/DocumentViewer.d.ts +3 -1
  17. package/dist/components/features/DocumentViewer.js +6 -5
  18. package/dist/components/features/DocumentWorkspace.d.ts +10 -2
  19. package/dist/components/features/DocumentWorkspace.js +15 -4
  20. package/dist/components/features/DocumentWorkspace.test.js +20 -3
  21. package/dist/components/features/TaskSettings.d.ts +2 -0
  22. package/dist/components/features/TaskSettings.js +83 -78
  23. package/dist/components/features/TaskSettings.test.js +171 -82
  24. package/dist/components/task/TaskActionHeader.js +11 -5
  25. package/dist/components/task/TaskActionHeader.test.js +10 -0
  26. package/dist/components/task/TaskCard.js +6 -2
  27. package/dist/components/task/TaskContextUpload.test.js +3 -3
  28. package/dist/components/task/TaskForm.js +14 -64
  29. package/dist/components/task/TaskKanban.js +0 -8
  30. package/dist/components/task/TaskList.js +5 -1
  31. package/dist/components/task/TaskList.test.js +4 -0
  32. package/dist/components/ui/HierarchyActionBadge.d.ts +10 -0
  33. package/dist/components/ui/HierarchyActionBadge.js +5 -0
  34. package/dist/components/views/AppShellHeader.d.ts +3 -1
  35. package/dist/components/views/AppShellHeader.js +27 -11
  36. package/dist/components/views/AppShellHeader.test.js +18 -1
  37. package/dist/components/views/PlanComparisonPage.d.ts +20 -15
  38. package/dist/components/views/PlanComparisonPage.js +221 -82
  39. package/dist/components/views/PlanComparisonPage.test.js +268 -14
  40. package/dist/components/views/PlansPage.js +16 -10
  41. package/dist/components/views/PlansPage.test.js +4 -2
  42. package/dist/components/views/StandaloneLayout.js +256 -69
  43. package/dist/components/views/WidgetView.js +10 -8
  44. package/dist/components/views/panels/PlanningDrawer.d.ts +19 -6
  45. package/dist/components/views/panels/PlanningDrawer.js +259 -51
  46. package/dist/components/views/panels/PlanningDrawer.test.js +392 -4
  47. package/dist/config/envSchema.js +1 -0
  48. package/dist/core/AiProfileService.d.ts +7 -0
  49. package/dist/core/AiProfileService.js +460 -35
  50. package/dist/core/AiProfiles.test.js +648 -2
  51. package/dist/core/AttachmentLinkService.d.ts +3 -0
  52. package/dist/core/AttachmentLinkService.js +58 -13
  53. package/dist/core/GlobalSettingsService.js +30 -0
  54. package/dist/core/GlobalSettingsService.test.d.ts +1 -0
  55. package/dist/core/GlobalSettingsService.test.js +45 -0
  56. package/dist/core/McpTokenService.d.ts +12 -0
  57. package/dist/core/McpTokenService.js +44 -1
  58. package/dist/core/PlanEntitlementService.d.ts +20 -0
  59. package/dist/core/PlanEntitlementService.js +215 -19
  60. package/dist/core/PlanFeatureCatalog.test.js +55 -0
  61. package/dist/core/PlanningEntities.test.js +366 -4
  62. package/dist/core/TaskActivityService.d.ts +28 -1
  63. package/dist/core/TaskActivityService.js +227 -46
  64. package/dist/core/TaskActivitySync.test.d.ts +1 -0
  65. package/dist/core/TaskActivitySync.test.js +90 -0
  66. package/dist/core/TaskAuditTimeline.test.js +96 -0
  67. package/dist/core/Taskforce.d.ts +204 -26
  68. package/dist/core/Taskforce.ids.test.js +58 -0
  69. package/dist/core/Taskforce.js +781 -71
  70. package/dist/core/Taskforce.mcpAuth.test.js +109 -0
  71. package/dist/core/types.d.ts +10 -0
  72. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +2 -1
  73. package/dist/hooks/bootstrapTimeouts.d.ts +4 -0
  74. package/dist/hooks/bootstrapTimeouts.js +8 -0
  75. package/dist/hooks/bootstrapTimeouts.test.d.ts +1 -0
  76. package/dist/hooks/bootstrapTimeouts.test.js +12 -0
  77. package/dist/hooks/sync/orchestratorShared.d.ts +3 -3
  78. package/dist/hooks/sync/orchestratorShared.js +61 -1
  79. package/dist/hooks/sync/recovery.js +8 -0
  80. package/dist/hooks/sync/recovery.test.js +5 -5
  81. package/dist/hooks/sync/transfers.d.ts +2 -1
  82. package/dist/hooks/sync/transfers.js +36 -9
  83. package/dist/hooks/sync/transfers.test.js +534 -2
  84. package/dist/hooks/useFilterSort.js +13 -13
  85. package/dist/hooks/useFilterSort.test.js +87 -1
  86. package/dist/hooks/useSyncOrchestrator.js +50 -5
  87. package/dist/hooks/useTaskforce.d.ts +85 -50
  88. package/dist/hooks/useTaskforce.js +44 -14
  89. package/dist/hooks/useTaskforce.runtime-routing.test.js +94 -0
  90. package/dist/hooks/useTaskforce.sync-behavior.test.js +99 -0
  91. package/dist/hooks/useWorkspaceSyncController.js +7 -1
  92. package/dist/hooks/useWorkspaceSyncController.test.js +48 -0
  93. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +14 -7
  94. package/dist/mcp/canonicalAssetHelpers.d.ts +16 -4
  95. package/dist/mcp/canonicalAssetHelpers.js +18 -10
  96. package/dist/mcp/canonicalAssetHelpers.test.d.ts +1 -0
  97. package/dist/mcp/canonicalAssetHelpers.test.js +67 -0
  98. package/dist/mcp/clientIntegrations.d.ts +1 -0
  99. package/dist/mcp/clientIntegrations.js +25 -0
  100. package/dist/mcp/clientIntegrations.test.js +14 -3
  101. package/dist/mcp/clientProfileDefaults.js +3 -3
  102. package/dist/mcp/collaborationRegistrar.js +7 -7
  103. package/dist/mcp/documentAssetRegistrar.js +113 -7
  104. package/dist/mcp/runtime.d.ts +1 -0
  105. package/dist/mcp/runtime.js +969 -122
  106. package/dist/mcp/runtime.test.js +1083 -11
  107. package/dist/mcp/taskAttachmentTypes.d.ts +1 -1
  108. package/dist/mcp/taskPlanningRegistrar.js +48 -0
  109. package/dist/mcp/toolRegistry.d.ts +56 -0
  110. package/dist/mcp/toolRegistry.js +8 -0
  111. package/dist/mcp/toolRegistry.test.js +4 -0
  112. package/dist/migrations/taskSchemaMigrations.js +58 -15
  113. package/dist/server/aiProfileAvatarAssets.d.ts +49 -0
  114. package/dist/server/aiProfileAvatarAssets.js +319 -0
  115. package/dist/server/aiProfileAvatarAssets.test.d.ts +1 -0
  116. package/dist/server/aiProfileAvatarAssets.test.js +213 -0
  117. package/dist/server/annotatedAttachmentsRoutes.test.js +66 -7
  118. package/dist/server/index.js +4 -0
  119. package/dist/server/index.test.js +6 -1
  120. package/dist/server/routes/admin.js +328 -9
  121. package/dist/server/routes/annotatedAttachments.js +51 -0
  122. package/dist/server/routes/auth.d.ts +11 -0
  123. package/dist/server/routes/auth.js +189 -27
  124. package/dist/server/routes/authSupport.d.ts +5 -0
  125. package/dist/server/routes/authSupport.js +13 -1
  126. package/dist/server/routes/billing.d.ts +1 -0
  127. package/dist/server/routes/billing.js +116 -63
  128. package/dist/server/routes/billing.test.js +162 -11
  129. package/dist/server/routes/documents.js +10 -0
  130. package/dist/server/routes/shared.js +0 -10
  131. package/dist/server/routes/sync.integration.test.js +221 -0
  132. package/dist/server/routes/sync.js +13 -3
  133. package/dist/server/routes/syncSnapshotRoutes.js +2 -0
  134. package/dist/server/routes/tasks.js +12 -9
  135. package/dist/server/routes.js +87 -8
  136. package/dist/server/routes.test.js +972 -23
  137. package/dist/shared/aiProfileSurfaceType.d.ts +1 -1
  138. package/dist/shared/aiProfileSurfaceType.js +10 -8
  139. package/dist/storage/objectStorage.d.ts +1 -0
  140. package/dist/storage/objectStorage.js +2 -1
  141. package/dist/storage/objectStorageClient.d.ts +1 -0
  142. package/dist/storage/objectStorageClient.js +41 -8
  143. package/dist/storage/objectStorageClient.test.js +26 -1
  144. package/dist/storage/objectStorageConfig.js +3 -1
  145. package/dist/storage/objectStorageConfig.test.js +13 -1
  146. package/dist/sync/collaborationSyncPayload.js +2 -0
  147. package/dist/sync/collaborationSyncPayload.test.js +1 -0
  148. package/dist/sync/collaborationSyncState.d.ts +3 -3
  149. package/dist/sync/collaborationSyncState.js +19 -12
  150. package/dist/sync/contentSyncPayload.d.ts +5 -1
  151. package/dist/sync/contentSyncPayload.js +9 -0
  152. package/dist/sync/contentSyncPayload.test.js +39 -0
  153. package/dist/sync/contentSyncState.d.ts +5 -1
  154. package/dist/sync/contentSyncState.js +231 -37
  155. package/dist/sync/planningSyncPayload.js +5 -0
  156. package/dist/sync/planningSyncPayload.test.js +24 -0
  157. package/dist/sync/syncApplyHandlers.d.ts +4 -4
  158. package/dist/sync/syncApplyHandlers.js +89 -31
  159. package/dist/sync/syncApplyHandlers.test.js +184 -4
  160. package/dist/sync/syncResourceAdapters.d.ts +2 -0
  161. package/dist/sync/syncResourceAdapters.js +2 -0
  162. package/dist/sync/syncService.d.ts +1 -0
  163. package/dist/sync/syncService.js +13 -1
  164. package/dist/sync/syncService.test.js +39 -0
  165. package/dist/sync/workspacePullFeed.d.ts +4 -0
  166. package/dist/sync/workspacePullFeed.js +129 -70
  167. package/dist/sync/workspacePullFeed.test.js +49 -0
  168. package/dist/sync/workspaceSyncModel.d.ts +28 -6
  169. package/dist/sync/workspaceSyncModel.js +159 -45
  170. package/dist/sync/workspaceSyncModel.test.js +269 -3
  171. package/dist/types.d.ts +33 -2
  172. package/dist/ui/agent-logos/ChatGPT.png +0 -0
  173. package/dist/ui/agent-logos/Gemini CLI.jpeg +0 -0
  174. package/dist/ui/agent-logos/antigravity.jpeg +0 -0
  175. package/dist/ui/agent-logos/claude-code.jpeg +0 -0
  176. package/dist/ui/agent-logos/codex.jpeg +0 -0
  177. package/dist/ui/agent-logos/cursor.png +0 -0
  178. package/dist/ui/agent-logos/openclaw.jpeg +0 -0
  179. package/dist/ui/agent-logos/windsurf.png +0 -0
  180. package/dist/ui/assets/AgentsModule-BS4Pi_nC.js +1 -0
  181. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BVKew-mH.css +1 -0
  182. package/dist/ui/assets/AnnotatedAttachmentWorkspace-CCckzWYZ.js +3 -0
  183. package/dist/ui/assets/ContextAttachmentManager-CfG_ER7C.js +3 -0
  184. package/dist/ui/assets/DocumentWorkspace-BVaWPA25.js +252 -0
  185. package/dist/ui/assets/DocumentWorkspace-D4rJKXzd.css +1 -0
  186. package/dist/ui/assets/EntityActivityTimeline-DSj0ThaK.js +1 -0
  187. package/dist/ui/assets/EntityActivityTimeline-DyqvvhuF.css +1 -0
  188. package/dist/ui/assets/{InitiativesModule-9E6ParrY.js → InitiativesModule-AxMkWtxH.js} +1 -1
  189. package/dist/ui/assets/PlansPage-B_z-D6Nh.css +1 -0
  190. package/dist/ui/assets/PlansPage-Ndt7mYfo.js +1 -0
  191. package/dist/ui/assets/TaskContextUpload-Dw4rTF4i.js +1 -0
  192. package/dist/ui/assets/TaskSettings-f2ga42_V.js +9 -0
  193. package/dist/ui/assets/{WorkflowsModule-BA7jcEpH.js → WorkflowsModule-E4UL3mov.js} +4 -4
  194. package/dist/ui/assets/documentReferences-BOUcJm6-.js +1 -0
  195. package/dist/ui/assets/index-BvAbqx5Q.css +1 -0
  196. package/dist/ui/assets/index-MAHKvFqp.js +4 -0
  197. package/dist/ui/assets/{vendor-icons-DuEd_65c.js → vendor-icons-B32hGuF2.js} +1 -1
  198. package/dist/ui/index.html +3 -3
  199. package/dist/ui/og-image.png +0 -0
  200. package/dist/ui/product/agent manager.png +0 -0
  201. package/dist/ui/product/agent_manager_02.png +0 -0
  202. package/dist/utils/aiProfileEvents.d.ts +7 -0
  203. package/dist/utils/aiProfileEvents.js +6 -0
  204. package/dist/utils/assignees.d.ts +6 -0
  205. package/dist/utils/assignees.js +8 -1
  206. package/dist/utils/assignees.test.d.ts +1 -0
  207. package/dist/utils/assignees.test.js +25 -0
  208. package/dist/utils/avatarDisplay.d.ts +1 -0
  209. package/dist/utils/avatarDisplay.js +15 -0
  210. package/dist/utils/contextFiles.d.ts +3 -1
  211. package/dist/utils/contextFiles.js +2 -1
  212. package/dist/utils/taskActivity.d.ts +7 -1
  213. package/dist/utils/taskActivity.js +63 -1
  214. package/dist/utils/taskActivity.test.js +23 -1
  215. package/dist/utils/taskSearch.d.ts +22 -0
  216. package/dist/utils/taskSearch.js +87 -0
  217. package/dist/utils/taskSearch.test.d.ts +1 -0
  218. package/dist/utils/taskSearch.test.js +69 -0
  219. package/dist/utils/taskUtils.js +10 -3
  220. package/dist/utils/taskUtils.test.d.ts +1 -0
  221. package/dist/utils/taskUtils.test.js +19 -0
  222. package/package.json +1 -1
  223. package/dist/ui/assets/AgentsModule-DrLawwNl.js +0 -1
  224. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +0 -1
  225. package/dist/ui/assets/AnnotatedAttachmentWorkspace-Z9_whf7F.js +0 -3
  226. package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +0 -252
  227. package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +0 -1
  228. package/dist/ui/assets/PlansPage-CvfnMiWq.css +0 -1
  229. package/dist/ui/assets/PlansPage-DRXscYxH.js +0 -1
  230. package/dist/ui/assets/TaskSettings-DZX4jk7e.js +0 -9
  231. package/dist/ui/assets/index-9eT8e0Lu.css +0 -1
  232. 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: primaryLink?.taskId || null,
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 normalizedIncomingTaskId = typeof doc.taskId === 'string' && doc.taskId.trim().length > 0
67
- ? doc.taskId.trim()
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
- || (primaryLink?.taskId || null) !== normalizedIncomingTaskId
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 normalizedIncomingTaskId = typeof asset.taskId === 'string' && asset.taskId.trim().length > 0
102
- ? asset.taskId.trim()
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
- || (primaryLink?.taskId || null) !== normalizedIncomingTaskId
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 desiredTaskId = String(input.taskId || '').trim();
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 (desiredTaskId) {
221
+ if (desiredLinkTarget) {
139
222
  deps.workspaceAssetStore.upsertLink({
140
223
  workspaceId,
141
224
  assetId,
142
- taskId: desiredTaskId,
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 (desiredTaskId) {
233
+ if (desiredLinkTarget) {
150
234
  for (const link of existingLinks) {
151
- if (link.taskId === desiredTaskId && link.role === desiredRole)
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.listAllByWorkspace(workspaceId, {
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: primaryLink?.taskId || null,
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
- if (!deps.workspaceAssetStore)
442
- return [];
443
- const assets = deps.workspaceAssetStore.listAllByWorkspace(workspaceId, {
574
+ const assets = deps.workspaceAssetStore?.listAllByWorkspace(workspaceId, {
444
575
  includeDeleted: false
445
- });
446
- return assets
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: primaryLink?.taskId || null,
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
- if (!deps.workspaceAssetStore)
575
- return null;
576
- const asset = deps.workspaceAssetStore.getByStorageKey(normalizedPath, workspaceId);
577
- if (!asset || (asset.kind !== 'image' && asset.kind !== 'file') || asset.storageProvider === 'external') {
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.storageProvider === 'r2' && objectStorageClient) {
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.assetId,
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: asset.kind,
802
+ kind: assetKind,
615
803
  mimeType: asset.mimeType || 'application/octet-stream',
616
804
  referenceNumber: asset.referenceNumber,
617
- taskId: primaryLink?.taskId || null,
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 taskId = String(doc.taskId || '').trim();
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 taskId = String(asset.taskId || '').trim();
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, WorkspaceSyncInitiativeSnapshot, WorkspaceSyncTaskEventSnapshot, WorkspaceSyncTaskSnapshot, WorkspaceSyncWorkspaceMemberSnapshot, WorkspaceSyncWorkstreamSnapshot } from './workspaceSyncModel.js';
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) => WorkspaceSyncTaskEventSnapshot | null;
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, WorkspaceSyncTaskEventSnapshot>;
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, WorkspaceSyncTaskEventSnapshot>;
172
+ incomingTaskEvents: Map<string, WorkspaceSyncEntityEventSnapshot>;
173
173
  recordSyncDiagnostic: RecordSyncDiagnostic;
174
174
  createSyncRouteError: CreateSyncRouteError;
175
175
  }): void;