@taskforcehq/taskforce 0.3.302 → 0.3.304

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/dist/Taskforce.module.css +113 -5
  2. package/dist/TaskforceCore.js +560 -208
  3. package/dist/TaskforceCore.test.js +1833 -91
  4. package/dist/annotatedAttachments/service.d.ts +1 -1
  5. package/dist/annotatedAttachments/types.d.ts +1 -1
  6. package/dist/components/features/AgentsModule.js +1 -1
  7. package/dist/components/features/AgentsModule.test.js +1 -1
  8. package/dist/components/features/TaskSettings.js +58 -38
  9. package/dist/components/features/TaskSettings.test.js +139 -40
  10. package/dist/components/task/TaskActionHeader.d.ts +2 -1
  11. package/dist/components/task/TaskActionHeader.js +3 -3
  12. package/dist/components/task/TaskActionHeader.test.js +8 -0
  13. package/dist/components/task/TaskCard.js +16 -1
  14. package/dist/components/task/TaskCard.test.js +31 -0
  15. package/dist/components/task/TaskForm.d.ts +2 -1
  16. package/dist/components/task/TaskForm.js +22 -4
  17. package/dist/components/task/TaskForm.test.js +46 -0
  18. package/dist/components/task/TaskKanban.d.ts +1 -1
  19. package/dist/components/task/TaskKanban.js +31 -160
  20. package/dist/components/task/TaskKanban.test.js +17 -0
  21. package/dist/components/task/TaskStatusActions.d.ts +6 -1
  22. package/dist/components/task/TaskStatusActions.js +8 -3
  23. package/dist/components/task/TaskStatusActions.test.js +9 -0
  24. package/dist/components/task/taskKanbanDropRules.d.ts +58 -0
  25. package/dist/components/task/taskKanbanDropRules.js +187 -0
  26. package/dist/components/task/taskKanbanDropRules.test.d.ts +1 -0
  27. package/dist/components/task/taskKanbanDropRules.test.js +114 -0
  28. package/dist/components/ui/Modal.d.ts +1 -1
  29. package/dist/components/ui/Modal.js +1 -0
  30. package/dist/components/views/AppShellHeader.js +5 -1
  31. package/dist/components/views/AppShellHeader.test.js +4 -0
  32. package/dist/components/views/PlanComparisonPage.d.ts +3 -2
  33. package/dist/components/views/PlanComparisonPage.js +4 -2
  34. package/dist/components/views/PlanComparisonPage.test.js +4 -0
  35. package/dist/components/views/PlansPage.d.ts +7 -1
  36. package/dist/components/views/PlansPage.js +139 -53
  37. package/dist/components/views/PlansPage.test.d.ts +1 -0
  38. package/dist/components/views/PlansPage.test.js +157 -0
  39. package/dist/components/views/StandaloneLayout.d.ts +1 -0
  40. package/dist/components/views/StandaloneLayout.js +216 -186
  41. package/dist/components/views/WidgetView.js +11 -2
  42. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +8 -3
  43. package/dist/components/views/standalone/modals/AccountHubModal.js +16 -2
  44. package/dist/components/views/standalone/modals/AccountHubModal.test.d.ts +1 -0
  45. package/dist/components/views/standalone/modals/AccountHubModal.test.js +26 -0
  46. package/dist/components/views/standalone/modals/SyncStatusModal.js +1 -1
  47. package/dist/core/DeletedTaskLifecycleService.d.ts +2 -1
  48. package/dist/core/EntitlementsPolicy.test.js +2 -2
  49. package/dist/core/GlobalSettingsService.js +78 -18
  50. package/dist/core/PlanEntitlementService.d.ts +31 -5
  51. package/dist/core/PlanEntitlementService.js +350 -222
  52. package/dist/core/SignupMonetizationSettings.test.js +208 -49
  53. package/dist/core/SystemAdmin.test.js +186 -96
  54. package/dist/core/TaskActivityService.d.ts +4 -1
  55. package/dist/core/TaskActivityService.js +45 -0
  56. package/dist/core/TaskAuditTimeline.test.js +52 -1
  57. package/dist/core/Taskforce.d.ts +65 -20
  58. package/dist/core/Taskforce.js +261 -51
  59. package/dist/core/Taskforce.mcpAuth.test.js +0 -2
  60. package/dist/core/UserProfileAvatarDrafts.test.js +12 -3
  61. package/dist/core/WorkspacePlanMode.test.js +65 -0
  62. package/dist/core/types.d.ts +11 -12
  63. package/dist/documentReviews/service.d.ts +1 -1
  64. package/dist/documentReviews/types.d.ts +1 -1
  65. package/dist/hooks/auth/useTaskforceAuthBootstrap.d.ts +48 -0
  66. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +219 -0
  67. package/dist/hooks/sync/auth.d.ts +37 -0
  68. package/dist/hooks/sync/auth.js +61 -0
  69. package/dist/hooks/sync/bootstrap.d.ts +34 -0
  70. package/dist/hooks/sync/bootstrap.js +52 -0
  71. package/dist/hooks/sync/orchestratorShared.d.ts +36 -0
  72. package/dist/hooks/sync/orchestratorShared.js +209 -0
  73. package/dist/hooks/sync/orchestratorShared.test.d.ts +1 -0
  74. package/dist/hooks/sync/orchestratorShared.test.js +49 -0
  75. package/dist/hooks/sync/realtime.d.ts +25 -0
  76. package/dist/hooks/sync/realtime.js +60 -0
  77. package/dist/hooks/sync/recovery.d.ts +69 -0
  78. package/dist/hooks/sync/recovery.js +118 -0
  79. package/dist/hooks/sync/transfers.d.ts +122 -0
  80. package/dist/hooks/sync/transfers.js +431 -0
  81. package/dist/hooks/sync/useDataVersionRefresh.d.ts +16 -0
  82. package/dist/hooks/sync/useDataVersionRefresh.js +73 -0
  83. package/dist/hooks/ui/useResourceExport.d.ts +3 -1
  84. package/dist/hooks/ui/useResourceExport.js +5 -2
  85. package/dist/hooks/ui/useWorkflowTemplates.js +11 -6
  86. package/dist/hooks/useRealtimeSync.js +2 -1
  87. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +8 -0
  88. package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +1 -0
  89. package/dist/hooks/useSyncOrchestrator.context-assets.test.js +228 -0
  90. package/dist/hooks/useSyncOrchestrator.d.ts +5 -4
  91. package/dist/hooks/useSyncOrchestrator.js +315 -860
  92. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +665 -8
  93. package/dist/hooks/useTaskMutations.js +45 -6
  94. package/dist/hooks/useTaskMutations.test.js +147 -0
  95. package/dist/hooks/useTaskforce.d.ts +11 -2
  96. package/dist/hooks/useTaskforce.js +290 -812
  97. package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
  98. package/dist/hooks/useWorkspaceSyncController.js +22 -10
  99. package/dist/hooks/workspace/useTaskforceApiRouting.test.js +17 -0
  100. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +166 -0
  101. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +434 -0
  102. package/dist/hooks/workspace/workspaceLocalState.d.ts +19 -0
  103. package/dist/hooks/workspace/workspaceLocalState.js +100 -0
  104. package/dist/hooks/workspace/workspaceLocalState.test.d.ts +1 -0
  105. package/dist/hooks/workspace/workspaceLocalState.test.js +59 -0
  106. package/dist/localization/locales/en-US.js +2 -2
  107. package/dist/mcp/canonicalAssetHelpers.d.ts +70 -0
  108. package/dist/mcp/canonicalAssetHelpers.js +259 -0
  109. package/dist/mcp/clientIntegrations.d.ts +12 -1
  110. package/dist/mcp/clientIntegrations.js +161 -6
  111. package/dist/mcp/clientIntegrations.test.js +76 -4
  112. package/dist/mcp/documentHelpers.d.ts +100 -0
  113. package/dist/mcp/documentHelpers.js +161 -0
  114. package/dist/mcp/runtime.js +264 -505
  115. package/dist/mcp/runtime.test.js +353 -89
  116. package/dist/mcp/taskAttachmentHelpers.d.ts +41 -0
  117. package/dist/mcp/taskAttachmentHelpers.js +60 -0
  118. package/dist/mcp/taskAttachmentTypes.d.ts +37 -0
  119. package/dist/mcp/taskAttachmentTypes.js +1 -0
  120. package/dist/migrations/billingSchemaParity.test.js +3 -0
  121. package/dist/migrations/taskSchemaMigrations.js +15 -0
  122. package/dist/plugins/vite.js +1 -1
  123. package/dist/runtime/appGateDefinitions.d.ts +1 -1
  124. package/dist/runtime/appGateDefinitions.js +1 -1
  125. package/dist/server/index.cookieProxy.test.js +1 -0
  126. package/dist/server/index.d.ts +1 -0
  127. package/dist/server/index.js +48 -3
  128. package/dist/server/index.test.js +14 -2
  129. package/dist/server/mockStripe.d.ts +1 -0
  130. package/dist/server/mockStripe.js +2 -0
  131. package/dist/server/routes/admin.js +91 -40
  132. package/dist/server/routes/auth.d.ts +1 -0
  133. package/dist/server/routes/auth.js +231 -55
  134. package/dist/server/routes/billing.js +473 -39
  135. package/dist/server/routes/billing.test.js +972 -37
  136. package/dist/server/routes/shared.d.ts +30 -0
  137. package/dist/server/routes/shared.js +31 -3
  138. package/dist/server/routes/sync.integration.test.js +180 -0
  139. package/dist/server/routes/sync.js +125 -242
  140. package/dist/server/routes/tasks.js +49 -7
  141. package/dist/server/routes.js +11 -22
  142. package/dist/server/routes.test.js +530 -81
  143. package/dist/shared/taskActor.d.ts +10 -0
  144. package/dist/shared/taskActor.js +1 -0
  145. package/dist/sync/cloudSyncApi.d.ts +1 -0
  146. package/dist/sync/collaborationSyncPayload.js +4 -0
  147. package/dist/sync/collaborationSyncPayload.test.js +8 -0
  148. package/dist/sync/collaborationSyncState.d.ts +3 -1
  149. package/dist/sync/collaborationSyncState.js +19 -0
  150. package/dist/sync/contentSyncState.d.ts +10 -0
  151. package/dist/sync/contentSyncState.js +277 -14
  152. package/dist/sync/syncApplyHandlers.d.ts +87 -1
  153. package/dist/sync/syncApplyHandlers.js +370 -8
  154. package/dist/sync/syncApplyHandlers.test.d.ts +1 -0
  155. package/dist/sync/syncApplyHandlers.test.js +377 -0
  156. package/dist/sync/syncService.d.ts +9 -1
  157. package/dist/sync/syncService.js +57 -10
  158. package/dist/sync/syncService.test.js +48 -0
  159. package/dist/sync/workspacePullFeed.d.ts +11 -0
  160. package/dist/sync/workspacePullFeed.js +123 -68
  161. package/dist/sync/workspacePullFeed.test.js +180 -0
  162. package/dist/sync/workspaceSyncModel.d.ts +22 -1
  163. package/dist/sync/workspaceSyncModel.js +65 -0
  164. package/dist/sync/workspaceSyncModel.test.js +119 -0
  165. package/dist/types.d.ts +2 -10
  166. package/dist/ui/assets/AgentsModule-BLWVbuKP.js +1 -0
  167. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-Dvi02Y7q.js → AnnotatedAttachmentWorkspace-BlS-cqnl.js} +1 -1
  168. package/dist/ui/assets/{DocumentWorkspace-iys3zfca.js → DocumentWorkspace-BH_6DF6x.js} +1 -1
  169. package/dist/ui/assets/{InitiativesModule-DJvLJI3A.js → InitiativesModule-BjR6iBf9.js} +1 -1
  170. package/dist/ui/assets/{PlansPage-BlVC_lRq.css → PlansPage-C2dd2n1X.css} +1 -1
  171. package/dist/ui/assets/PlansPage-CSDQ4sLa.js +1 -0
  172. package/dist/ui/assets/TaskSettings-CEDy34Jo.js +9 -0
  173. package/dist/ui/assets/{WorkflowsModule-BguHeQaV.js → WorkflowsModule-DnFqdUME.js} +1 -1
  174. package/dist/ui/assets/index-DXUdtH1B.js +6 -0
  175. package/dist/ui/assets/index-Ii0B0rTO.css +1 -0
  176. package/dist/ui/assets/{vendor-icons-C8EED3Oh.js → vendor-icons-I7ZwG1z_.js} +1 -1
  177. package/dist/ui/index.html +4 -4
  178. package/dist/utils/accountProfileSummaryCache.d.ts +31 -0
  179. package/dist/utils/accountProfileSummaryCache.js +97 -0
  180. package/dist/utils/accountProfileSummaryCache.test.d.ts +1 -0
  181. package/dist/utils/accountProfileSummaryCache.test.js +63 -0
  182. package/dist/utils/taskActivity.js +4 -0
  183. package/dist/utils/taskActivity.test.js +25 -1
  184. package/package.json +5 -3
  185. package/scripts/playwright-auth.sh +4 -0
  186. package/dist/ui/assets/AgentsModule-JG5jc3he.js +0 -1
  187. package/dist/ui/assets/PlansPage-BMPlpFpw.js +0 -1
  188. package/dist/ui/assets/TaskSettings-Bqd4cCzW.js +0 -8
  189. package/dist/ui/assets/index-MXeWoebC.css +0 -1
  190. package/dist/ui/assets/index-YJ4k5QNa.js +0 -6
  191. package/dist/utils/billingStatusCache.d.ts +0 -21
  192. package/dist/utils/billingStatusCache.js +0 -70
@@ -1,10 +1,285 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import { createHash } from 'crypto';
4
- import { compareWorkspaceMutationOrdering } from './workspaceSyncSurface.js';
4
+ import { normalizeBinaryAssetDeleteSyncPayload, normalizeBinaryAssetSyncPayload, normalizeDocumentDeleteSyncPayload, normalizeDocumentSyncPayload, } from './contentSyncPayload.js';
5
+ import { normalizeAiProfileSyncPayload, normalizeAnnotatedAttachmentSessionSyncPayload, normalizeDeleteSyncPayload, normalizeDocumentReviewCommentSyncPayload, normalizeDocumentReviewSessionSyncPayload, } from './collaborationSyncPayload.js';
6
+ import { normalizeInitiativeSyncPayload, normalizeTaxonomySyncPayload, normalizeWorkstreamSyncPayload, } from './planningSyncPayload.js';
7
+ import { normalizeTaskSyncPayload, taskSyncPayloadToCoreTask, } from './taskSyncPayload.js';
8
+ import { compareWorkspaceMutationOrdering, getTaskMutationTieBreak, getTaskMutationWatermark } from './workspaceSyncSurface.js';
9
+ function createIncomingApplyModel() {
10
+ return {
11
+ taskMutations: {
12
+ upserts: [],
13
+ deletes: new Map(),
14
+ },
15
+ planning: {
16
+ incomingTaxonomies: null,
17
+ incomingTaxonomyState: null,
18
+ incomingTaxonomiesUpdatedAt: '',
19
+ incomingInitiatives: new Map(),
20
+ incomingWorkstreams: new Map(),
21
+ },
22
+ profiles: {
23
+ incomingAiProfiles: new Map(),
24
+ },
25
+ content: {
26
+ incomingDocuments: [],
27
+ incomingDocumentDeletes: new Map(),
28
+ incomingAssets: [],
29
+ incomingAssetDeletes: new Map(),
30
+ },
31
+ collaboration: {
32
+ incomingDocumentReviewSessions: new Map(),
33
+ incomingDocumentReviewSessionDeletes: new Map(),
34
+ incomingDocumentReviewComments: new Map(),
35
+ incomingDocumentReviewCommentDeletes: new Map(),
36
+ incomingAnnotatedAttachmentSessions: new Map(),
37
+ incomingAnnotatedAttachmentSessionDeletes: new Map(),
38
+ },
39
+ taskActivity: {
40
+ incomingTaskEvents: new Map(),
41
+ },
42
+ };
43
+ }
5
44
  export function resolveSyncConflictOrdering(incomingWatermark, existingWatermark, incomingTieBreak, existingTieBreak) {
6
45
  return compareWorkspaceMutationOrdering(String(incomingWatermark || '').trim(), String(existingWatermark || '').trim(), String(incomingTieBreak || '').trim(), String(existingTieBreak || '').trim());
7
46
  }
47
+ function shouldSkipTaskClassification(op) {
48
+ return (op === 'delete'
49
+ || op === 'initiative-upsert'
50
+ || op === 'workstream-upsert'
51
+ || op === 'document-upsert'
52
+ || op === 'document-delete'
53
+ || op === 'asset-upsert'
54
+ || op === 'asset-delete'
55
+ || op === 'document-review-session-upsert'
56
+ || op === 'document-review-session-delete'
57
+ || op === 'document-review-comment-upsert'
58
+ || op === 'document-review-comment-delete'
59
+ || op === 'annotated-attachment-session-upsert'
60
+ || op === 'annotated-attachment-session-delete');
61
+ }
62
+ function recordTaskReferenceNumberMismatch(input) {
63
+ const { core, workspaceId, task, recordSyncDiagnostic, } = input;
64
+ const taskId = String(task?.id || '').trim();
65
+ const incomingReferenceNumber = Number.isFinite(Number(task?.referenceNumber))
66
+ ? Math.max(1, Math.floor(Number(task?.referenceNumber)))
67
+ : null;
68
+ if (!incomingReferenceNumber)
69
+ return false;
70
+ const existingTask = core.resolveTaskIdentifier(`T-${incomingReferenceNumber}`, workspaceId);
71
+ if (!existingTask || existingTask.id === taskId)
72
+ return false;
73
+ recordSyncDiagnostic(workspaceId, 'apply', 'Detected task reference number mismatch during normal sync.', {
74
+ source: 'tasks.referenceNumber.mismatch',
75
+ taskId: taskId || null,
76
+ taskTitle: typeof task?.title === 'string' ? task.title.trim() || null : null,
77
+ existingTaskId: existingTask.id,
78
+ existingTaskTitle: existingTask.title || null,
79
+ referenceLabel: `T-${incomingReferenceNumber}`,
80
+ existingReferenceNumber: existingTask.referenceNumber ?? incomingReferenceNumber,
81
+ incomingReferenceNumber
82
+ });
83
+ return true;
84
+ }
85
+ export function classifyWorkspaceSyncChanges(input) {
86
+ const { workspaceId, incomingChanges, normalizeWorkspaceSyncDocumentPath, normalizeTaskAssetPath, normalizeIncomingAiProfileSurfaceType, normalizeTaskEventSyncPayload, recordSyncDiagnostic, } = input;
87
+ const model = createIncomingApplyModel();
88
+ for (const change of incomingChanges) {
89
+ const raw = change;
90
+ const op = String(raw.op || '').trim().toLowerCase();
91
+ if (op === 'taxonomy-upsert') {
92
+ const taxonomy = normalizeTaxonomySyncPayload(raw);
93
+ if (taxonomy?.taxonomyState)
94
+ model.planning.incomingTaxonomyState = taxonomy.taxonomyState;
95
+ if (Array.isArray(taxonomy?.taxonomies))
96
+ model.planning.incomingTaxonomies = taxonomy.taxonomies;
97
+ if (taxonomy?.updatedAt)
98
+ model.planning.incomingTaxonomiesUpdatedAt = taxonomy.updatedAt;
99
+ continue;
100
+ }
101
+ if (op === 'initiative-upsert') {
102
+ const initiative = normalizeInitiativeSyncPayload(raw.initiative, String(raw.updatedAt || raw.watermark || '').trim());
103
+ if (initiative)
104
+ model.planning.incomingInitiatives.set(initiative.id, initiative);
105
+ continue;
106
+ }
107
+ if (op === 'workstream-upsert') {
108
+ const workstream = normalizeWorkstreamSyncPayload(raw.workstream, String(raw.updatedAt || raw.watermark || '').trim());
109
+ if (workstream)
110
+ model.planning.incomingWorkstreams.set(workstream.id, workstream);
111
+ continue;
112
+ }
113
+ if (op === 'ai-profile-upsert') {
114
+ const profile = normalizeAiProfileSyncPayload(raw.profile, workspaceId, normalizeIncomingAiProfileSurfaceType, 'apply.aiProfileUpsert');
115
+ const profileId = String(profile?.id || '').trim();
116
+ if (profileId && profile) {
117
+ const profileWorkspaceId = String(profile.workspaceId || '').trim();
118
+ if (profileWorkspaceId && profileWorkspaceId !== workspaceId) {
119
+ recordSyncDiagnostic(workspaceId, 'apply', `Rejected ai-profile-upsert for profile ${profileId}: workspace mismatch (expected ${workspaceId}, got ${profileWorkspaceId}).`, {
120
+ source: 'apply.aiProfileWorkspaceMismatch',
121
+ workspaceId,
122
+ profileId,
123
+ expectedWorkspaceId: workspaceId,
124
+ receivedWorkspaceId: profileWorkspaceId
125
+ });
126
+ continue;
127
+ }
128
+ model.profiles.incomingAiProfiles.set(profileId, {
129
+ ...profile,
130
+ workspaceId,
131
+ createdAt: profile.createdAt || String(raw.updatedAt || raw.watermark || '').trim(),
132
+ updatedAt: profile.updatedAt || String(raw.updatedAt || raw.watermark || '').trim(),
133
+ });
134
+ }
135
+ continue;
136
+ }
137
+ if (op === 'document-upsert') {
138
+ const document = normalizeDocumentSyncPayload(raw, normalizeWorkspaceSyncDocumentPath);
139
+ if (document)
140
+ model.content.incomingDocuments.push(document);
141
+ continue;
142
+ }
143
+ if (op === 'document-delete') {
144
+ const deletedDocument = normalizeDocumentDeleteSyncPayload(raw, normalizeWorkspaceSyncDocumentPath);
145
+ if (deletedDocument)
146
+ model.content.incomingDocumentDeletes.set(deletedDocument.path, deletedDocument.deletedAt);
147
+ continue;
148
+ }
149
+ if (op === 'asset-upsert') {
150
+ const asset = normalizeBinaryAssetSyncPayload(raw, normalizeTaskAssetPath);
151
+ if (asset)
152
+ model.content.incomingAssets.push(asset);
153
+ continue;
154
+ }
155
+ if (op === 'asset-delete') {
156
+ const deletedAsset = normalizeBinaryAssetDeleteSyncPayload(raw, normalizeTaskAssetPath);
157
+ if (deletedAsset)
158
+ model.content.incomingAssetDeletes.set(deletedAsset.path, deletedAsset.deletedAt);
159
+ continue;
160
+ }
161
+ if (op === 'document-review-session-upsert') {
162
+ const session = normalizeDocumentReviewSessionSyncPayload(raw.session, String(raw.updatedAt || raw.watermark || '').trim());
163
+ if (session)
164
+ model.collaboration.incomingDocumentReviewSessions.set(session.id, session);
165
+ continue;
166
+ }
167
+ if (op === 'document-review-session-delete') {
168
+ const deletedSession = normalizeDeleteSyncPayload(raw, 'sessionId');
169
+ if (deletedSession)
170
+ model.collaboration.incomingDocumentReviewSessionDeletes.set(deletedSession.id, deletedSession.deletedAt);
171
+ continue;
172
+ }
173
+ if (op === 'document-review-comment-upsert') {
174
+ const comment = normalizeDocumentReviewCommentSyncPayload(raw.comment, { sourceWatermark: String(raw.updatedAt || raw.watermark || '').trim() });
175
+ if (comment)
176
+ model.collaboration.incomingDocumentReviewComments.set(comment.id, comment);
177
+ continue;
178
+ }
179
+ if (op === 'document-review-comment-delete') {
180
+ const deletedComment = normalizeDeleteSyncPayload(raw, 'commentId');
181
+ if (deletedComment)
182
+ model.collaboration.incomingDocumentReviewCommentDeletes.set(deletedComment.id, deletedComment.deletedAt);
183
+ continue;
184
+ }
185
+ if (op === 'annotated-attachment-session-upsert') {
186
+ const session = normalizeAnnotatedAttachmentSessionSyncPayload(raw.session, workspaceId, String(raw.updatedAt || raw.watermark || '').trim());
187
+ if (session)
188
+ model.collaboration.incomingAnnotatedAttachmentSessions.set(session.id, session);
189
+ continue;
190
+ }
191
+ if (op === 'annotated-attachment-session-delete') {
192
+ const deletedSession = normalizeDeleteSyncPayload(raw, 'sessionId');
193
+ if (deletedSession)
194
+ model.collaboration.incomingAnnotatedAttachmentSessionDeletes.set(deletedSession.id, deletedSession.deletedAt);
195
+ continue;
196
+ }
197
+ if (op === 'task-event-upsert') {
198
+ const event = normalizeTaskEventSyncPayload(raw.event, workspaceId, String(raw.updatedAt || raw.watermark || '').trim());
199
+ if (event)
200
+ model.taskActivity.incomingTaskEvents.set(event.id, event);
201
+ continue;
202
+ }
203
+ if (op !== 'delete')
204
+ continue;
205
+ const task = normalizeTaskSyncPayload(raw.task);
206
+ const deleteTaskId = String(raw.taskId || raw.id || task?.id || '').trim();
207
+ if (deleteTaskId) {
208
+ model.taskMutations.deletes.set(deleteTaskId, String(raw.deletedAt || raw.watermark || new Date().toISOString()).trim());
209
+ }
210
+ }
211
+ for (const change of incomingChanges) {
212
+ const raw = change;
213
+ const op = String(raw.op || '').trim().toLowerCase();
214
+ if (shouldSkipTaskClassification(op))
215
+ continue;
216
+ const task = normalizeTaskSyncPayload(raw.task);
217
+ if (!task)
218
+ continue;
219
+ model.taskMutations.upserts.push({
220
+ archived: raw.archived === true || task.isArchived === true,
221
+ task,
222
+ });
223
+ }
224
+ return model;
225
+ }
226
+ export function prepareTaskSyncSnapshotApply(input) {
227
+ const { core, workspaceId, taskMutations, recordSyncDiagnostic, } = input;
228
+ const prepared = {
229
+ tasks: [],
230
+ archived: [],
231
+ };
232
+ for (const mutation of taskMutations.upserts) {
233
+ const task = mutation.task;
234
+ const taskId = String(task.id || '').trim();
235
+ recordTaskReferenceNumberMismatch({
236
+ core,
237
+ workspaceId,
238
+ task: task,
239
+ recordSyncDiagnostic,
240
+ });
241
+ const deleteWatermark = taskId ? String(taskMutations.deletes.get(taskId) || '').trim() : '';
242
+ if (taskId && deleteWatermark) {
243
+ const compareAgainstDelete = resolveSyncConflictOrdering(deleteWatermark, String(task.updatedAt || task.createdAt || '').trim(), `delete:${taskId}`, getTaskMutationTieBreak(task, mutation.archived));
244
+ if (compareAgainstDelete >= 0)
245
+ continue;
246
+ }
247
+ if (mutation.archived)
248
+ prepared.archived.push(task);
249
+ else
250
+ prepared.tasks.push(task);
251
+ }
252
+ return prepared;
253
+ }
254
+ export function applyTaskSyncDeletes(input) {
255
+ const { core, workspaceId, taskMutations, recordSyncDiagnostic, } = input;
256
+ for (const [taskId, deleteWatermark] of taskMutations.deletes.entries()) {
257
+ try {
258
+ const existingTask = core.getTask(taskId, workspaceId);
259
+ if (existingTask) {
260
+ const compareAgainstExisting = resolveSyncConflictOrdering(deleteWatermark, getTaskMutationWatermark(existingTask), `delete:${taskId}`, getTaskMutationTieBreak(existingTask, Boolean(existingTask.isArchived)));
261
+ if (compareAgainstExisting < 0)
262
+ continue;
263
+ }
264
+ core.deleteTaskForSync(taskId, workspaceId, { persistBackup: false });
265
+ }
266
+ catch (error) {
267
+ recordSyncDiagnostic(workspaceId, 'apply', 'Failed to delete task during sync apply.', {
268
+ source: 'apply.deleteTask',
269
+ taskId,
270
+ error: String(error?.message || error || '')
271
+ });
272
+ }
273
+ }
274
+ return taskMutations.deletes.size;
275
+ }
276
+ export function applyPreparedTaskSyncSnapshot(input) {
277
+ const { core, workspaceId, prepared, } = input;
278
+ return core.applyWorkspaceSyncSnapshot({
279
+ tasks: prepared.tasks.map(taskSyncPayloadToCoreTask),
280
+ archived: prepared.archived.map(taskSyncPayloadToCoreTask)
281
+ }, workspaceId);
282
+ }
8
283
  export function applyPlanningSyncChanges(input) {
9
284
  const { core, workspaceId, incomingInitiatives, incomingWorkstreams, incomingTaxonomies, incomingTaxonomyState, incomingTaxonomiesUpdatedAt, workspaceTaxonomySyncMeta, recordSyncDiagnostic, } = input;
10
285
  if (incomingInitiatives.size > 0) {
@@ -107,6 +382,10 @@ export function applyAiProfileAndMemberSyncChanges(input) {
107
382
  username: profile.username,
108
383
  icon: profile.icon,
109
384
  color: profile.color,
385
+ description: profile.description ?? null,
386
+ role: profile.role ?? null,
387
+ provider: profile.provider ?? null,
388
+ model: profile.model ?? null,
110
389
  surfaceType: profile.surfaceType,
111
390
  providerMetadata: profile.providerMetadata,
112
391
  createdAt: profile.createdAt,
@@ -150,21 +429,29 @@ export function applyAiProfileAndMemberSyncChanges(input) {
150
429
  }
151
430
  }
152
431
  export async function applyContentSyncChanges(input) {
153
- const { core, workspaceId, incomingDocuments, incomingDocumentDeletes, incomingAssets, incomingAssetDeletes, basePath, hasObjectStorageClient, hasDocumentSyncMetadataDrift, hasBinaryAssetSyncMetadataDrift, writeWorkspaceSyncDocument, deleteWorkspaceSyncDocument, writeWorkspaceSyncBinaryAsset, deleteWorkspaceSyncBinaryAsset, getExistingBinaryAsset, recordSyncDiagnostic, options, } = input;
432
+ const { core, workspaceId, incomingDocuments, incomingDocumentDeletes, incomingAssets, incomingAssetDeletes, basePath, hasObjectStorageClient, hasDocumentSyncMetadataDrift, hasBinaryAssetSyncMetadataDrift, getExistingDocument, writeWorkspaceSyncDocument, deleteWorkspaceSyncDocument, writeWorkspaceSyncBinaryAsset, deleteWorkspaceSyncBinaryAsset, getExistingBinaryAsset, recordSyncDiagnostic, options, } = input;
154
433
  if (incomingDocuments.length > 0) {
155
434
  for (const doc of incomingDocuments) {
156
435
  try {
157
436
  const existingDoc = core.getDocumentWatermark(workspaceId, doc.path);
437
+ const existingDocFallback = getExistingDocument ? getExistingDocument(doc.path, workspaceId) : null;
438
+ const localDocumentPath = path.resolve(basePath, doc.path);
439
+ const localDocumentHash = !hasObjectStorageClient() && fs.existsSync(localDocumentPath)
440
+ ? createHash('sha256').update(fs.readFileSync(localDocumentPath, 'utf8')).digest('hex')
441
+ : '';
158
442
  const incomingHash = createHash('sha256').update(String(doc.content || '')).digest('hex');
159
- const existingUpdatedAt = String(existingDoc?.updatedAt || '').trim();
160
- const existingHash = String(existingDoc?.contentHash || '').trim();
443
+ const existingUpdatedAt = String(existingDoc?.updatedAt || existingDocFallback?.updatedAt || '').trim();
444
+ const existingHash = String(existingDoc?.contentHash || localDocumentHash || existingDocFallback?.contentHash || '').trim();
161
445
  const compare = resolveSyncConflictOrdering(doc.updatedAt, existingUpdatedAt, incomingHash, existingHash);
162
446
  const metadataDrift = hasDocumentSyncMetadataDrift(workspaceId, doc, options);
163
447
  if (compare < 0)
164
448
  continue;
165
449
  if (compare === 0 && existingHash === incomingHash && !metadataDrift)
166
450
  continue;
167
- await writeWorkspaceSyncDocument(basePath, doc, workspaceId, options);
451
+ await writeWorkspaceSyncDocument(basePath, doc, workspaceId, {
452
+ ...options,
453
+ allowReferenceNumberReassignment: options?.allowReferenceNumberReassignment === true || existingHash !== incomingHash
454
+ });
168
455
  }
169
456
  catch (error) {
170
457
  recordSyncDiagnostic(workspaceId, 'apply', 'Failed to apply synced document change.', {
@@ -190,18 +477,27 @@ export async function applyContentSyncChanges(input) {
190
477
  if (incomingAssets.length > 0) {
191
478
  for (const asset of incomingAssets) {
192
479
  try {
480
+ const localAssetPath = path.resolve(basePath, asset.path);
193
481
  const existing = getExistingBinaryAsset(asset.path, workspaceId);
194
482
  const incomingHash = createHash('sha256').update(Buffer.from(String(asset.contentBase64 || ''), 'base64')).digest('hex');
483
+ const localFileMissing = !hasObjectStorageClient() && !fs.existsSync(localAssetPath);
484
+ const localAssetHash = !hasObjectStorageClient() && !localFileMissing
485
+ ? createHash('sha256').update(fs.readFileSync(localAssetPath)).digest('hex')
486
+ : '';
195
487
  const existingUpdatedAt = String(existing?.updatedAt || '').trim();
196
- const existingHash = String(existing?.contentSha256 || '').trim();
197
- const localFileMissing = !hasObjectStorageClient() && !fs.existsSync(path.resolve(basePath, asset.path));
488
+ const existingHash = String(localAssetHash || existing?.contentSha256 || '').trim();
198
489
  const compare = resolveSyncConflictOrdering(asset.updatedAt, existingUpdatedAt, incomingHash, existingHash);
199
490
  const metadataDrift = hasBinaryAssetSyncMetadataDrift(workspaceId, asset, options);
200
491
  if (compare < 0)
201
492
  continue;
202
493
  if (compare === 0 && existingHash === incomingHash && !localFileMissing && !metadataDrift)
203
494
  continue;
204
- await writeWorkspaceSyncBinaryAsset(basePath, asset, workspaceId, options);
495
+ await writeWorkspaceSyncBinaryAsset(basePath, asset, workspaceId, {
496
+ ...options,
497
+ allowReferenceNumberReassignment: options?.allowReferenceNumberReassignment === true
498
+ || existingHash !== incomingHash
499
+ || localFileMissing
500
+ });
205
501
  }
206
502
  catch (error) {
207
503
  recordSyncDiagnostic(workspaceId, 'apply', 'Failed to apply synced asset change.', {
@@ -443,3 +739,69 @@ export function applyCollaborationSyncChanges(input) {
443
739
  }
444
740
  }
445
741
  }
742
+ export function applyTaskActivitySyncChanges(input) {
743
+ const { core, workspaceId, incomingTaskEvents, recordSyncDiagnostic, createSyncRouteError, } = input;
744
+ if (incomingTaskEvents.size <= 0)
745
+ return;
746
+ for (const event of incomingTaskEvents.values()) {
747
+ try {
748
+ const taskId = String(event.taskId || '').trim();
749
+ if (!taskId)
750
+ continue;
751
+ const existingTask = core.getTask(taskId, workspaceId);
752
+ if (!existingTask) {
753
+ recordSyncDiagnostic(workspaceId, 'apply', 'Skipped task event during sync apply because the task does not exist locally.', {
754
+ source: 'apply.taskEventMissingTask',
755
+ eventId: event.id,
756
+ taskId,
757
+ });
758
+ continue;
759
+ }
760
+ const existing = core.listTaskEvents(taskId, workspaceId).find((entry) => entry.id === event.id) || null;
761
+ const incomingTieBreak = createHash('sha256')
762
+ .update(JSON.stringify({
763
+ taskId,
764
+ action: event.action,
765
+ actor: event.actor,
766
+ actorType: event.actorType,
767
+ details: event.details ?? {},
768
+ }))
769
+ .digest('hex');
770
+ const existingTieBreak = existing
771
+ ? createHash('sha256')
772
+ .update(JSON.stringify({
773
+ taskId: existing.taskId,
774
+ action: existing.action,
775
+ actor: existing.actor,
776
+ actorType: existing.actorType,
777
+ details: existing.details ?? {},
778
+ }))
779
+ .digest('hex')
780
+ : '';
781
+ const compare = resolveSyncConflictOrdering(event.createdAt, String(existing?.createdAt || '').trim(), incomingTieBreak, existingTieBreak);
782
+ if (compare < 0)
783
+ continue;
784
+ core.upsertTaskEventForSync({
785
+ id: event.id,
786
+ taskId,
787
+ workspaceId,
788
+ action: event.action,
789
+ actor: event.actor,
790
+ actorType: event.actorType,
791
+ details: event.details,
792
+ createdAt: event.createdAt,
793
+ });
794
+ }
795
+ catch (error) {
796
+ const details = {
797
+ source: 'apply.taskEventUpsert',
798
+ eventId: event.id,
799
+ taskId: event.taskId,
800
+ error: String(error?.message || error || ''),
801
+ errorCode: String(error?.code || '').trim() || null
802
+ };
803
+ recordSyncDiagnostic(workspaceId, 'apply', 'Failed to upsert task event during sync apply.', details);
804
+ throw createSyncRouteError(`Failed to persist task event ${event.id} during sync apply: ${String(error?.message || error || 'Unknown error')}`, details, { code: 'TASK_EVENT_SYNC_APPLY_FAILED', statusCode: 500 });
805
+ }
806
+ }
807
+ }
@@ -0,0 +1 @@
1
+ export {};