@taskforcehq/taskforce 0.3.303 → 0.3.305
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 +140 -247
- package/dist/Taskforce.module.css +102 -5
- package/dist/TaskforceCore.js +635 -238
- package/dist/TaskforceCore.test.js +2243 -495
- package/dist/annotatedAttachments/service.d.ts +1 -1
- package/dist/annotatedAttachments/types.d.ts +1 -1
- package/dist/compat/workspaceSyncCompat.js +6 -0
- package/dist/components/features/AgentsModule.js +1 -1
- package/dist/components/features/AgentsModule.test.js +1 -1
- package/dist/components/features/DocumentIndex.d.ts +1 -1
- package/dist/components/features/DocumentIndex.js +1 -1
- package/dist/components/features/DocumentViewer.d.ts +1 -1
- package/dist/components/features/DocumentViewer.js +32 -9
- package/dist/components/features/DocumentWorkspace.d.ts +5 -22
- package/dist/components/features/DocumentWorkspace.js +12 -155
- package/dist/components/features/DocumentWorkspace.test.js +193 -6
- package/dist/components/features/TaskSettings.js +87 -41
- package/dist/components/features/TaskSettings.test.js +180 -40
- package/dist/components/features/documentWorkspaceModel.d.ts +24 -0
- package/dist/components/features/documentWorkspaceModel.js +57 -0
- package/dist/components/features/useDocumentWorkspaceController.d.ts +34 -0
- package/dist/components/features/useDocumentWorkspaceController.js +113 -0
- package/dist/components/task/TaskActionHeader.d.ts +2 -1
- package/dist/components/task/TaskActionHeader.js +3 -3
- package/dist/components/task/TaskActionHeader.test.js +8 -0
- package/dist/components/task/TaskCard.js +16 -1
- package/dist/components/task/TaskCard.test.js +31 -0
- package/dist/components/task/TaskForm.d.ts +9 -8
- package/dist/components/task/TaskForm.js +374 -219
- package/dist/components/task/TaskForm.test.js +482 -361
- package/dist/components/task/TaskKanban.d.ts +1 -1
- package/dist/components/task/TaskKanban.js +33 -161
- package/dist/components/task/TaskStatusActions.d.ts +6 -1
- package/dist/components/task/TaskStatusActions.js +8 -3
- package/dist/components/task/TaskStatusActions.test.js +9 -0
- package/dist/components/task/taskKanbanDropRules.d.ts +58 -0
- package/dist/components/task/taskKanbanDropRules.js +187 -0
- package/dist/components/task/taskKanbanDropRules.test.d.ts +1 -0
- package/dist/components/task/taskKanbanDropRules.test.js +114 -0
- package/dist/components/ui/Modal.d.ts +1 -1
- package/dist/components/ui/Modal.js +1 -0
- package/dist/components/views/AppShellHeader.js +5 -1
- package/dist/components/views/AppShellHeader.test.js +4 -0
- package/dist/components/views/PlanComparisonPage.d.ts +3 -2
- package/dist/components/views/PlanComparisonPage.js +9 -10
- package/dist/components/views/PlanComparisonPage.test.js +55 -41
- package/dist/components/views/PlansPage.d.ts +5 -1
- package/dist/components/views/PlansPage.js +150 -81
- package/dist/components/views/PlansPage.test.js +367 -41
- package/dist/components/views/StandaloneLayout.js +135 -54
- package/dist/components/views/WidgetView.js +11 -2
- package/dist/components/views/panels/FilterToolbar.test.js +6 -4
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +2 -14
- package/dist/components/views/standalone/modals/AccountHubModal.js +12 -14
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +24 -1
- package/dist/components/views/standalone/modals/SyncStatusModal.js +1 -1
- package/dist/config/envSchema.js +1 -1
- package/dist/core/AiProfileService.d.ts +6 -1
- package/dist/core/AiProfileService.js +161 -16
- package/dist/core/AiProfileService.test.js +3 -1
- package/dist/core/AiProfiles.test.js +200 -0
- package/dist/core/AuthTokenService.test.d.ts +1 -0
- package/dist/core/AuthTokenService.test.js +78 -0
- package/dist/core/DeletedTaskLifecycleService.d.ts +2 -1
- package/dist/core/EntitlementsPolicy.test.js +75 -13
- package/dist/core/PlanEntitlementService.d.ts +9 -1
- package/dist/core/PlanEntitlementService.js +144 -19
- package/dist/core/PlanVersionPolicy.test.js +19 -26
- package/dist/core/SignupMonetizationSettings.test.js +75 -15
- package/dist/core/SystemAdmin.test.js +212 -56
- package/dist/core/TaskActivityService.d.ts +4 -1
- package/dist/core/TaskActivityService.js +45 -0
- package/dist/core/TaskAuditTimeline.test.js +52 -1
- package/dist/core/TaskTaxonomyValidation.test.js +53 -0
- package/dist/core/Taskforce.d.ts +18 -11
- package/dist/core/Taskforce.js +93 -12
- package/dist/core/WorkspacePlanMode.test.js +73 -8
- package/dist/core/shared.d.ts +2 -0
- package/dist/core/shared.js +11 -0
- package/dist/documentReviews/service.d.ts +1 -1
- package/dist/documentReviews/types.d.ts +1 -1
- package/dist/hooks/auth/useTaskforceAuthBootstrap.d.ts +48 -0
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +219 -0
- package/dist/hooks/sync/auth.d.ts +37 -0
- package/dist/hooks/sync/auth.js +63 -0
- package/dist/hooks/sync/bootstrap.d.ts +34 -0
- package/dist/hooks/sync/bootstrap.js +52 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +36 -0
- package/dist/hooks/sync/orchestratorShared.js +209 -0
- package/dist/hooks/sync/orchestratorShared.test.d.ts +1 -0
- package/dist/hooks/sync/orchestratorShared.test.js +49 -0
- package/dist/hooks/sync/realtime.d.ts +25 -0
- package/dist/hooks/sync/realtime.js +60 -0
- package/dist/hooks/sync/recovery.d.ts +69 -0
- package/dist/hooks/sync/recovery.js +118 -0
- package/dist/hooks/sync/transfers.d.ts +123 -0
- package/dist/hooks/sync/transfers.js +435 -0
- package/dist/hooks/sync/useDataVersionRefresh.d.ts +16 -0
- package/dist/hooks/sync/useDataVersionRefresh.js +73 -0
- package/dist/hooks/ui/useResourceExport.d.ts +3 -1
- package/dist/hooks/ui/useResourceExport.js +5 -2
- package/dist/hooks/ui/useWorkflowTemplates.js +11 -6
- package/dist/hooks/useRealtimeSync.js +2 -1
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +8 -0
- package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +1 -0
- package/dist/hooks/useSyncOrchestrator.context-assets.test.js +228 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +12 -8
- package/dist/hooks/useSyncOrchestrator.js +388 -879
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +641 -6
- package/dist/hooks/useTaskMutations.d.ts +2 -1
- package/dist/hooks/useTaskMutations.js +57 -7
- package/dist/hooks/useTaskMutations.test.js +176 -0
- package/dist/hooks/useTaskforce.d.ts +13 -4
- package/dist/hooks/useTaskforce.js +302 -817
- package/dist/hooks/useTaskforce.sync-behavior.test.js +78 -0
- package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
- package/dist/hooks/useWorkspaceSyncController.js +33 -11
- package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
- package/dist/hooks/workspace/useTaskforceApiRouting.test.js +17 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +166 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +434 -0
- package/dist/hooks/workspace/workspaceLocalState.d.ts +19 -0
- package/dist/hooks/workspace/workspaceLocalState.js +100 -0
- package/dist/hooks/workspace/workspaceLocalState.test.d.ts +1 -0
- package/dist/hooks/workspace/workspaceLocalState.test.js +59 -0
- package/dist/localization/locales/en-US.js +2 -2
- package/dist/mcp/adminWorkspaceRegistrar.d.ts +2 -0
- package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
- package/dist/mcp/canonicalAssetHelpers.d.ts +70 -0
- package/dist/mcp/canonicalAssetHelpers.js +259 -0
- package/dist/mcp/clientIntegrations.d.ts +12 -1
- package/dist/mcp/clientIntegrations.js +161 -6
- package/dist/mcp/clientIntegrations.test.js +76 -4
- package/dist/mcp/collaborationRegistrar.d.ts +2 -0
- package/dist/mcp/collaborationRegistrar.js +71 -0
- package/dist/mcp/documentAssetRegistrar.d.ts +2 -0
- package/dist/mcp/documentAssetRegistrar.js +346 -0
- package/dist/mcp/documentHelpers.d.ts +100 -0
- package/dist/mcp/documentHelpers.js +161 -0
- package/dist/mcp/runtime.js +2455 -3925
- package/dist/mcp/runtime.test.js +240 -8
- package/dist/mcp/taskAttachmentHelpers.d.ts +41 -0
- package/dist/mcp/taskAttachmentHelpers.js +60 -0
- package/dist/mcp/taskAttachmentTypes.d.ts +37 -0
- package/dist/mcp/taskAttachmentTypes.js +1 -0
- package/dist/mcp/taskPlanningRegistrar.d.ts +2 -0
- package/dist/mcp/taskPlanningRegistrar.js +418 -0
- package/dist/mcp/toolCatalog.d.ts +35 -0
- package/dist/mcp/toolCatalog.js +3 -0
- package/dist/plugins/vite.js +1 -1
- package/dist/runtime/appGateDefinitions.d.ts +1 -1
- package/dist/runtime/appGateDefinitions.js +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +46 -3
- package/dist/server/index.test.js +12 -1
- package/dist/server/routes/admin.d.ts +1 -5
- package/dist/server/routes/admin.js +151 -80
- package/dist/server/routes/annotatedAttachments.d.ts +1 -1
- package/dist/server/routes/annotatedAttachments.js +1 -1
- package/dist/server/routes/auth.d.ts +1 -4
- package/dist/server/routes/auth.js +59 -64
- package/dist/server/routes/authSupport.d.ts +30 -0
- package/dist/server/routes/authSupport.js +81 -0
- package/dist/server/routes/billing.d.ts +1 -1
- package/dist/server/routes/billing.js +122 -255
- package/dist/server/routes/billing.test.js +257 -86
- package/dist/server/routes/documentReviews.d.ts +1 -1
- package/dist/server/routes/documentReviews.js +1 -1
- package/dist/server/routes/documents.d.ts +4 -2
- package/dist/server/routes/documents.js +73 -7
- package/dist/server/routes/primitives.d.ts +11 -0
- package/dist/server/routes/primitives.js +73 -0
- package/dist/server/routes/resources.d.ts +1 -1
- package/dist/server/routes/resources.js +1 -1
- package/dist/server/routes/shared.d.ts +31 -1
- package/dist/server/routes/shared.js +26 -0
- package/dist/server/routes/sync.d.ts +1 -1
- package/dist/server/routes/sync.integration.test.js +20 -0
- package/dist/server/routes/sync.js +111 -238
- package/dist/server/routes/tasks.d.ts +1 -1
- package/dist/server/routes/tasks.js +50 -8
- package/dist/server/routes/workspaces.d.ts +1 -1
- package/dist/server/routes/workspaces.js +1 -1
- package/dist/server/routes.d.ts +2 -9
- package/dist/server/routes.js +84 -181
- package/dist/server/routes.test.js +912 -32
- package/dist/shared/taskActor.d.ts +10 -0
- package/dist/shared/taskActor.js +1 -0
- package/dist/sync/collaborationSyncPayload.js +4 -0
- package/dist/sync/collaborationSyncPayload.test.js +8 -0
- package/dist/sync/collaborationSyncState.d.ts +3 -1
- package/dist/sync/collaborationSyncState.js +19 -0
- package/dist/sync/contentSyncState.js +28 -17
- package/dist/sync/syncApplyHandlers.d.ts +87 -1
- package/dist/sync/syncApplyHandlers.js +370 -8
- package/dist/sync/syncApplyHandlers.test.d.ts +1 -0
- package/dist/sync/syncApplyHandlers.test.js +379 -0
- package/dist/sync/syncService.d.ts +9 -1
- package/dist/sync/syncService.js +44 -2
- package/dist/sync/syncService.test.js +62 -0
- package/dist/sync/workspacePullFeed.d.ts +1 -0
- package/dist/sync/workspacePullFeed.js +66 -59
- package/dist/sync/workspacePullFeed.test.js +72 -4
- package/dist/sync/workspaceSyncModel.d.ts +25 -1
- package/dist/sync/workspaceSyncModel.js +77 -3
- package/dist/sync/workspaceSyncModel.test.js +153 -0
- package/dist/sync/workspaceSyncState.d.ts +2 -0
- package/dist/sync/workspaceSyncState.js +1 -0
- package/dist/types.d.ts +2 -10
- package/dist/ui/assets/AgentsModule-N2MnQBZk.js +1 -0
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-C_TmXZwA.js → AnnotatedAttachmentWorkspace-BG6P_GVW.js} +1 -1
- package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +1 -0
- package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +252 -0
- package/dist/ui/assets/{InitiativesModule-4-Rh2K2n.js → InitiativesModule-Dhm7M8e7.js} +1 -1
- package/dist/ui/assets/{PlansPage-BlVC_lRq.css → PlansPage-C2dd2n1X.css} +1 -1
- package/dist/ui/assets/PlansPage-Cq0zXj3I.js +1 -0
- package/dist/ui/assets/TaskSettings-ln0aF7xc.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-CGk9s3NJ.js → WorkflowsModule-BcS4afRn.js} +1 -1
- package/dist/ui/assets/index-C2-OXZV7.css +1 -0
- package/dist/ui/assets/index-DcSI5F86.js +6 -0
- package/dist/ui/assets/{vendor-icons-pWocEipT.js → vendor-icons-BSUaRwF8.js} +1 -1
- package/dist/ui/index.html +4 -4
- package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
- package/dist/utils/commercialLifecyclePresentation.js +196 -0
- package/dist/utils/taskActivity.js +4 -0
- package/dist/utils/taskActivity.test.js +25 -1
- package/package.json +10 -1
- package/scripts/check-cloud-mcp.mjs +83 -0
- package/scripts/playwright-auth.sh +9 -1
- package/scripts/run-billing-performance-smoke.sh +24 -0
- package/dist/ui/assets/AgentsModule-CpsTmrIW.js +0 -1
- package/dist/ui/assets/DocumentWorkspace-C3GyzrWm.js +0 -250
- package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
- package/dist/ui/assets/PlansPage-BP7AYOqr.js +0 -1
- package/dist/ui/assets/TaskSettings-BOC5F6Ag.js +0 -8
- package/dist/ui/assets/index-CLIMgR6g.js +0 -6
- package/dist/ui/assets/index-DneETxcu.css +0 -1
|
@@ -148,6 +148,14 @@ export async function buildWorkspacePullFeed(input) {
|
|
|
148
148
|
sessionId: String(session.id || '').trim()
|
|
149
149
|
}))
|
|
150
150
|
.filter((entry) => Number.isFinite(Date.parse(entry.watermark)) && entry.sessionId.length > 0);
|
|
151
|
+
const taskEventEntries = collaborationSyncState.listWorkspaceSyncTaskEventsSnapshot(workspaceId)
|
|
152
|
+
.map((event) => ({
|
|
153
|
+
kind: 'task-event',
|
|
154
|
+
watermark: String(event.createdAt || '').trim(),
|
|
155
|
+
sortId: `task-event:${String(event.id || '').trim()}`,
|
|
156
|
+
event
|
|
157
|
+
}))
|
|
158
|
+
.filter((entry) => Number.isFinite(Date.parse(entry.watermark)) && entry.event.id.length > 0 && entry.event.taskId.length > 0);
|
|
151
159
|
const assetIndex = await contentSyncState.listWorkspaceSyncBinaryAssetIndex(workspaceId);
|
|
152
160
|
const assetEntries = assetIndex
|
|
153
161
|
.map((asset) => ({
|
|
@@ -190,6 +198,7 @@ export async function buildWorkspacePullFeed(input) {
|
|
|
190
198
|
...documentReviewCommentDeleteEntries,
|
|
191
199
|
...annotatedAttachmentSessionEntries,
|
|
192
200
|
...annotatedAttachmentSessionDeleteEntries,
|
|
201
|
+
...taskEventEntries,
|
|
193
202
|
...assetEntries,
|
|
194
203
|
...assetDeleteEntries
|
|
195
204
|
];
|
|
@@ -238,16 +247,11 @@ export async function buildWorkspacePullFeed(input) {
|
|
|
238
247
|
}
|
|
239
248
|
}));
|
|
240
249
|
const hydrationMs = Date.now() - hydrationStartedAtMs;
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
break;
|
|
247
|
-
effectivePage.push(entry);
|
|
248
|
-
}
|
|
249
|
-
const hasMore = afterCursor.length > effectivePage.length;
|
|
250
|
-
const last = effectivePage.length > 0 ? effectivePage[effectivePage.length - 1] : null;
|
|
250
|
+
const skippedHydrationEntries = page.filter((entry) => ((entry.kind === 'document' && !hydratedDocuments.get(entry.path))
|
|
251
|
+
|| (entry.kind === 'asset' && !hydratedAssets.get(entry.path)))).length;
|
|
252
|
+
const effectivePage = page;
|
|
253
|
+
const hasMore = afterCursor.length > page.length;
|
|
254
|
+
const last = page.length > 0 ? page[page.length - 1] : null;
|
|
251
255
|
const nextCursor = encodeWorkspaceSyncCursor(last
|
|
252
256
|
? { u: last.watermark, id: String(last.sortId || '') }
|
|
253
257
|
: cursor);
|
|
@@ -280,55 +284,57 @@ export async function buildWorkspacePullFeed(input) {
|
|
|
280
284
|
? [{ op: 'annotated-attachment-session-upsert', session: entry.session, watermark: entry.watermark }]
|
|
281
285
|
: entry.kind === 'annotated-attachment-session-delete'
|
|
282
286
|
? [{ op: 'annotated-attachment-session-delete', sessionId: entry.sessionId, deletedAt: entry.watermark, watermark: entry.watermark }]
|
|
283
|
-
: entry.kind === '
|
|
284
|
-
?
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
: entry.kind === '
|
|
308
|
-
?
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
:
|
|
287
|
+
: entry.kind === 'task-event'
|
|
288
|
+
? [{ op: 'task-event-upsert', event: entry.event, watermark: entry.watermark }]
|
|
289
|
+
: entry.kind === 'document'
|
|
290
|
+
? (() => {
|
|
291
|
+
const payload = hydratedDocuments.get(entry.path);
|
|
292
|
+
if (!payload)
|
|
293
|
+
return [];
|
|
294
|
+
return [{
|
|
295
|
+
op: 'document-upsert',
|
|
296
|
+
path: entry.path,
|
|
297
|
+
updatedAt: entry.watermark,
|
|
298
|
+
content: payload.content,
|
|
299
|
+
assetId: payload.assetId,
|
|
300
|
+
documentId: payload.documentId,
|
|
301
|
+
referenceNumber: payload.referenceNumber,
|
|
302
|
+
version: payload.version,
|
|
303
|
+
taskId: payload.taskId,
|
|
304
|
+
logicalName: payload.logicalName,
|
|
305
|
+
caption: payload.caption,
|
|
306
|
+
originalFilename: payload.originalFilename,
|
|
307
|
+
linkRole: payload.linkRole === 'reference' ? 'reference' : 'attachment',
|
|
308
|
+
watermark: entry.watermark
|
|
309
|
+
}];
|
|
310
|
+
})()
|
|
311
|
+
: entry.kind === 'document-delete'
|
|
312
|
+
? [{ op: 'document-delete', path: entry.path, assetId: entry.assetId, deletedAt: entry.watermark, watermark: entry.watermark }]
|
|
313
|
+
: entry.kind === 'asset'
|
|
314
|
+
? (() => {
|
|
315
|
+
const payload = hydratedAssets.get(entry.path);
|
|
316
|
+
if (!payload)
|
|
317
|
+
return [];
|
|
318
|
+
return [{
|
|
319
|
+
op: 'asset-upsert',
|
|
320
|
+
path: entry.path,
|
|
321
|
+
updatedAt: entry.watermark,
|
|
322
|
+
contentBase64: payload.contentBase64,
|
|
323
|
+
assetId: payload.assetId,
|
|
324
|
+
kind: payload.kind,
|
|
325
|
+
mimeType: payload.mimeType,
|
|
326
|
+
referenceNumber: payload.referenceNumber,
|
|
327
|
+
taskId: payload.taskId,
|
|
328
|
+
logicalName: payload.logicalName,
|
|
329
|
+
caption: payload.caption,
|
|
330
|
+
originalFilename: payload.originalFilename,
|
|
331
|
+
linkRole: payload.linkRole,
|
|
332
|
+
watermark: entry.watermark
|
|
333
|
+
}];
|
|
334
|
+
})()
|
|
335
|
+
: entry.kind === 'asset-delete'
|
|
336
|
+
? [{ op: 'asset-delete', path: entry.path, assetId: entry.assetId, deletedAt: entry.watermark, watermark: entry.watermark }]
|
|
337
|
+
: [{ op: 'upsert', task: entry.task, archived: Boolean(entry.archived), watermark: entry.watermark }]));
|
|
332
338
|
const encodeMs = Date.now() - encodeStartedAtMs;
|
|
333
339
|
return {
|
|
334
340
|
workspaceMembers,
|
|
@@ -338,6 +344,7 @@ export async function buildWorkspacePullFeed(input) {
|
|
|
338
344
|
diagnostics: {
|
|
339
345
|
totalEntries: all.length,
|
|
340
346
|
pageEntries: page.length,
|
|
347
|
+
skippedHydrationEntries,
|
|
341
348
|
documentIndexCount: documentIndex.length,
|
|
342
349
|
assetIndexCount: assetIndex.length,
|
|
343
350
|
indexMs,
|
|
@@ -29,6 +29,7 @@ function createCollaborationSyncStateMock() {
|
|
|
29
29
|
listWorkspaceSyncDocumentReviewSessionsSnapshot: () => [],
|
|
30
30
|
listWorkspaceSyncDocumentReviewCommentsSnapshot: () => [],
|
|
31
31
|
listWorkspaceSyncAnnotatedAttachmentSessionsSnapshot: () => [],
|
|
32
|
+
listWorkspaceSyncTaskEventsSnapshot: () => [],
|
|
32
33
|
};
|
|
33
34
|
}
|
|
34
35
|
function encodeCursor(cursor) {
|
|
@@ -234,9 +235,22 @@ describe('workspacePullFeed', () => {
|
|
|
234
235
|
expect(loadedDocs).toEqual(['docs/a.md']);
|
|
235
236
|
expect(loadedAssets).toEqual(['workspaces/ws/assets/images/a.png']);
|
|
236
237
|
});
|
|
237
|
-
it('
|
|
238
|
+
it('skips hydration misses without blocking later feed progress', async () => {
|
|
238
239
|
const core = createCoreMock();
|
|
239
240
|
const content = createContentSyncStateMock();
|
|
241
|
+
core.listTasksForSync = () => [
|
|
242
|
+
{
|
|
243
|
+
archived: false,
|
|
244
|
+
task: {
|
|
245
|
+
id: 'task-after-miss',
|
|
246
|
+
title: 'Task after miss',
|
|
247
|
+
status: 'task',
|
|
248
|
+
category: 'general',
|
|
249
|
+
type: 'task',
|
|
250
|
+
createdAt: '2026-04-12T10:00:00.000Z',
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
];
|
|
240
254
|
content.listWorkspaceSyncDocumentIndex = async () => [
|
|
241
255
|
{
|
|
242
256
|
path: 'docs/a.md',
|
|
@@ -260,7 +274,7 @@ describe('workspacePullFeed', () => {
|
|
|
260
274
|
core,
|
|
261
275
|
workspaceId: 'workspace-4',
|
|
262
276
|
cursor: null,
|
|
263
|
-
pageSize:
|
|
277
|
+
pageSize: 3,
|
|
264
278
|
encodeWorkspaceSyncCursor: encodeCursor,
|
|
265
279
|
contentSyncState: content,
|
|
266
280
|
collaborationSyncState: createCollaborationSyncStateMock(),
|
|
@@ -270,9 +284,63 @@ describe('workspacePullFeed', () => {
|
|
|
270
284
|
op: 'document-upsert',
|
|
271
285
|
path: 'docs/a.md',
|
|
272
286
|
content: 'content:docs/a.md'
|
|
287
|
+
}),
|
|
288
|
+
expect.objectContaining({
|
|
289
|
+
op: 'upsert',
|
|
290
|
+
task: expect.objectContaining({
|
|
291
|
+
id: 'task-after-miss'
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
]);
|
|
295
|
+
expect(result.nextCursor).toBe('2026-04-12T10:00:00.000Z|task-after-miss');
|
|
296
|
+
expect(result.hasMore).toBe(false);
|
|
297
|
+
expect(result.diagnostics.skippedHydrationEntries).toBe(1);
|
|
298
|
+
});
|
|
299
|
+
it('includes task-event upserts in the pull feed ordered by event watermark', async () => {
|
|
300
|
+
const core = createCoreMock();
|
|
301
|
+
const collaboration = createCollaborationSyncStateMock();
|
|
302
|
+
collaboration.listWorkspaceSyncTaskEventsSnapshot = () => [
|
|
303
|
+
{
|
|
304
|
+
id: 'task-event-2',
|
|
305
|
+
taskId: 'task-2',
|
|
306
|
+
workspaceId: 'workspace-5',
|
|
307
|
+
action: 'task-archived',
|
|
308
|
+
actor: 'ai-profile-2',
|
|
309
|
+
actorType: 'ai',
|
|
310
|
+
createdAt: '2026-04-12T10:06:00.000Z',
|
|
311
|
+
details: {}
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
id: 'task-event-1',
|
|
315
|
+
taskId: 'task-1',
|
|
316
|
+
workspaceId: 'workspace-5',
|
|
317
|
+
action: 'task-status-changed',
|
|
318
|
+
actor: 'ai-profile-1',
|
|
319
|
+
actorType: 'ai',
|
|
320
|
+
createdAt: '2026-04-12T10:05:00.000Z',
|
|
321
|
+
details: {}
|
|
322
|
+
}
|
|
323
|
+
];
|
|
324
|
+
const result = await buildWorkspacePullFeed({
|
|
325
|
+
core,
|
|
326
|
+
workspaceId: 'workspace-5',
|
|
327
|
+
cursor: null,
|
|
328
|
+
pageSize: 5,
|
|
329
|
+
encodeWorkspaceSyncCursor: encodeCursor,
|
|
330
|
+
contentSyncState: createContentSyncStateMock(),
|
|
331
|
+
collaborationSyncState: collaboration,
|
|
332
|
+
});
|
|
333
|
+
expect(result.changes).toEqual([
|
|
334
|
+
expect.objectContaining({
|
|
335
|
+
op: 'task-event-upsert',
|
|
336
|
+
watermark: '2026-04-12T10:05:00.000Z',
|
|
337
|
+
event: expect.objectContaining({ id: 'task-event-1' })
|
|
338
|
+
}),
|
|
339
|
+
expect.objectContaining({
|
|
340
|
+
op: 'task-event-upsert',
|
|
341
|
+
watermark: '2026-04-12T10:06:00.000Z',
|
|
342
|
+
event: expect.objectContaining({ id: 'task-event-2' })
|
|
273
343
|
})
|
|
274
344
|
]);
|
|
275
|
-
expect(result.nextCursor).toBe('2026-04-10T10:00:00.000Z|doc:docs/a.md');
|
|
276
|
-
expect(result.hasMore).toBe(true);
|
|
277
345
|
});
|
|
278
346
|
});
|
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
import type { InitiativeItem, TaskItem, WorkstreamItem } from '../types';
|
|
1
|
+
import type { InitiativeItem, TaskEvent, TaskItem, WorkstreamItem } from '../types';
|
|
2
2
|
import type { DocumentReviewCommentRecord, DocumentReviewSessionStatus } from '../documentReviews/types.js';
|
|
3
3
|
import type { AnnotatedAttachmentAnnotation } from '../annotatedAttachments/types.js';
|
|
4
4
|
import type { WorkspaceTaxonomyState } from '../shared/taxonomyState.js';
|
|
5
5
|
import { type AiProfileSurfaceType } from '../shared/aiProfileSurfaceType.js';
|
|
6
6
|
import { type TaskSyncPayload } from './taskSyncPayload.js';
|
|
7
7
|
export type WorkspaceSyncTaskSnapshot = TaskSyncPayload;
|
|
8
|
+
export interface WorkspaceSyncTaskEventSnapshot {
|
|
9
|
+
id: string;
|
|
10
|
+
taskId: string;
|
|
11
|
+
workspaceId: string;
|
|
12
|
+
action: string;
|
|
13
|
+
actor: string;
|
|
14
|
+
actorType: 'system' | 'human' | 'ai';
|
|
15
|
+
details?: TaskEvent['details'];
|
|
16
|
+
createdAt: string;
|
|
17
|
+
}
|
|
8
18
|
export interface WorkspaceSyncAiProfileSnapshot {
|
|
9
19
|
id: string;
|
|
10
20
|
workspaceId: string;
|
|
@@ -13,6 +23,10 @@ export interface WorkspaceSyncAiProfileSnapshot {
|
|
|
13
23
|
username: string;
|
|
14
24
|
icon: string;
|
|
15
25
|
color: string;
|
|
26
|
+
description?: string | null;
|
|
27
|
+
role?: string | null;
|
|
28
|
+
provider?: string | null;
|
|
29
|
+
model?: string | null;
|
|
16
30
|
surfaceType?: AiProfileSurfaceType | null;
|
|
17
31
|
providerMetadata?: Record<string, unknown> | null;
|
|
18
32
|
createdAt: string;
|
|
@@ -104,6 +118,8 @@ export type WorkspaceSyncChange = {
|
|
|
104
118
|
} | {
|
|
105
119
|
op: 'delete';
|
|
106
120
|
taskId: string;
|
|
121
|
+
deletedAt?: string;
|
|
122
|
+
watermark?: string;
|
|
107
123
|
} | {
|
|
108
124
|
op: 'initiative-upsert';
|
|
109
125
|
initiative: WorkspaceSyncInitiativeSnapshot;
|
|
@@ -177,6 +193,9 @@ export type WorkspaceSyncChange = {
|
|
|
177
193
|
op: 'annotated-attachment-session-delete';
|
|
178
194
|
sessionId: string;
|
|
179
195
|
deletedAt?: string;
|
|
196
|
+
} | {
|
|
197
|
+
op: 'task-event-upsert';
|
|
198
|
+
event: WorkspaceSyncTaskEventSnapshot;
|
|
180
199
|
};
|
|
181
200
|
export interface WorkspaceSyncPayload {
|
|
182
201
|
changes: WorkspaceSyncChange[];
|
|
@@ -203,6 +222,7 @@ export interface WorkspaceSyncPayload {
|
|
|
203
222
|
currentAnnotatedAttachmentSessionIds: Set<string>;
|
|
204
223
|
deleteAnnotatedAttachmentSessionIds: Set<string>;
|
|
205
224
|
currentAnnotatedAttachmentSessionWatermarks: Map<string, string>;
|
|
225
|
+
currentTaskEventWatermarks: Map<string, string>;
|
|
206
226
|
/** Watermarks for every task upsert included in this payload (id → updatedAt|createdAt). */
|
|
207
227
|
pushedWatermarks: Map<string, string>;
|
|
208
228
|
}
|
|
@@ -240,6 +260,7 @@ export declare function buildWorkspaceSyncPayloadModel(input: {
|
|
|
240
260
|
archivedTasks: TaskItem[];
|
|
241
261
|
lastPushedTaskIds: Set<string>;
|
|
242
262
|
pendingDeletedTaskIds: Set<string>;
|
|
263
|
+
pendingDeletedTaskWatermarks?: Map<string, string>;
|
|
243
264
|
initiatives?: InitiativeItem[];
|
|
244
265
|
workstreams?: WorkstreamItem[];
|
|
245
266
|
taxonomies?: unknown[];
|
|
@@ -250,6 +271,7 @@ export declare function buildWorkspaceSyncPayloadModel(input: {
|
|
|
250
271
|
documentReviewSessions?: WorkspaceSyncDocumentReviewSessionSnapshot[];
|
|
251
272
|
documentReviewComments?: WorkspaceSyncDocumentReviewCommentSnapshot[];
|
|
252
273
|
annotatedAttachmentSessions?: WorkspaceSyncAnnotatedAttachmentSessionSnapshot[];
|
|
274
|
+
taskEvents?: WorkspaceSyncTaskEventSnapshot[];
|
|
253
275
|
lastPushedInitiativeIds?: Set<string>;
|
|
254
276
|
lastPushedInitiativeWatermarks?: Map<string, string>;
|
|
255
277
|
lastPushedWorkstreamIds?: Set<string>;
|
|
@@ -266,6 +288,7 @@ export declare function buildWorkspaceSyncPayloadModel(input: {
|
|
|
266
288
|
lastPushedDocumentReviewCommentWatermarks?: Map<string, string>;
|
|
267
289
|
lastPushedAnnotatedAttachmentSessionIds?: Set<string>;
|
|
268
290
|
lastPushedAnnotatedAttachmentSessionWatermarks?: Map<string, string>;
|
|
291
|
+
lastPushedTaskEventWatermarks?: Map<string, string>;
|
|
269
292
|
/**
|
|
270
293
|
* Watermark (updatedAt|createdAt) of each task as of its last successful push.
|
|
271
294
|
* When provided, only tasks whose watermark differs from this map are included
|
|
@@ -289,4 +312,5 @@ export declare function buildWorkspaceSyncSignatureModel(input: {
|
|
|
289
312
|
documentReviewSessions?: WorkspaceSyncDocumentReviewSessionSnapshot[];
|
|
290
313
|
documentReviewComments?: WorkspaceSyncDocumentReviewCommentSnapshot[];
|
|
291
314
|
annotatedAttachmentSessions?: WorkspaceSyncAnnotatedAttachmentSessionSnapshot[];
|
|
315
|
+
taskEvents?: WorkspaceSyncTaskEventSnapshot[];
|
|
292
316
|
}): string;
|
|
@@ -3,6 +3,16 @@ import { serializeTaskForSync } from './taskSyncPayload.js';
|
|
|
3
3
|
export function toWorkspaceSyncTaskSnapshot(task) {
|
|
4
4
|
return serializeTaskForSync(task);
|
|
5
5
|
}
|
|
6
|
+
function sortTaskEventsForSync(taskEvents) {
|
|
7
|
+
return [...taskEvents].sort((left, right) => {
|
|
8
|
+
const leftCreatedAt = String(left?.createdAt || '').trim();
|
|
9
|
+
const rightCreatedAt = String(right?.createdAt || '').trim();
|
|
10
|
+
const watermarkDelta = leftCreatedAt.localeCompare(rightCreatedAt);
|
|
11
|
+
if (watermarkDelta !== 0)
|
|
12
|
+
return watermarkDelta;
|
|
13
|
+
return String(left?.id || '').trim().localeCompare(String(right?.id || '').trim());
|
|
14
|
+
});
|
|
15
|
+
}
|
|
6
16
|
function sortTasksForSync(tasks) {
|
|
7
17
|
return [...tasks].sort((left, right) => {
|
|
8
18
|
const leftId = String(left?.id || '').trim();
|
|
@@ -15,6 +25,9 @@ export function buildWorkspaceSyncPayloadModel(input) {
|
|
|
15
25
|
const pendingDeleteIds = new Set(Array.from(input.pendingDeletedTaskIds)
|
|
16
26
|
.map((taskId) => String(taskId || '').trim())
|
|
17
27
|
.filter((taskId) => taskId.length > 0));
|
|
28
|
+
const pendingDeleteWatermarks = new Map(Array.from(input.pendingDeletedTaskWatermarks || new Map())
|
|
29
|
+
.map(([taskId, deletedAt]) => [String(taskId || '').trim(), String(deletedAt || '').trim()])
|
|
30
|
+
.filter(([taskId, deletedAt]) => taskId.length > 0 && Number.isFinite(Date.parse(deletedAt))));
|
|
18
31
|
const lastWatermarks = input.lastPushedWatermarks;
|
|
19
32
|
const isDirty = (task) => {
|
|
20
33
|
if ((task?.referenceNumber === undefined || task?.referenceNumber === null) && Number.isFinite(Number(task?.localReferenceNumber))) {
|
|
@@ -64,12 +77,14 @@ export function buildWorkspaceSyncPayloadModel(input) {
|
|
|
64
77
|
.filter((taskId) => !currentTaskIds.has(taskId))
|
|
65
78
|
.map((taskId) => ({
|
|
66
79
|
op: 'delete',
|
|
67
|
-
taskId
|
|
80
|
+
taskId,
|
|
81
|
+
deletedAt: pendingDeleteWatermarks.get(taskId) || payloadBuiltAt
|
|
68
82
|
}));
|
|
69
83
|
const queuedDeleteChanges = Array.from(pendingDeleteIds)
|
|
70
84
|
.map((taskId) => ({
|
|
71
85
|
op: 'delete',
|
|
72
|
-
taskId
|
|
86
|
+
taskId,
|
|
87
|
+
deletedAt: pendingDeleteWatermarks.get(taskId) || payloadBuiltAt
|
|
73
88
|
}));
|
|
74
89
|
const deleteTaskIds = new Set([
|
|
75
90
|
...deleteChanges.map((change) => String(change.taskId)),
|
|
@@ -175,6 +190,10 @@ export function buildWorkspaceSyncPayloadModel(input) {
|
|
|
175
190
|
username: String(profile.username || '').trim(),
|
|
176
191
|
icon: String(profile.icon || 'Bot').trim() || 'Bot',
|
|
177
192
|
color: String(profile.color || '#8b5cf6').trim() || '#8b5cf6',
|
|
193
|
+
description: typeof profile.description === 'string' ? profile.description.trim() || null : null,
|
|
194
|
+
role: typeof profile.role === 'string' ? profile.role.trim() || null : null,
|
|
195
|
+
provider: typeof profile.provider === 'string' ? profile.provider.trim() || null : null,
|
|
196
|
+
model: typeof profile.model === 'string' ? profile.model.trim() || null : null,
|
|
178
197
|
surfaceType: parseAiProfileSurfaceType(profile.surfaceType),
|
|
179
198
|
providerMetadata: profile.providerMetadata && typeof profile.providerMetadata === 'object' && !Array.isArray(profile.providerMetadata)
|
|
180
199
|
? profile.providerMetadata
|
|
@@ -391,6 +410,40 @@ export function buildWorkspaceSyncPayloadModel(input) {
|
|
|
391
410
|
return true;
|
|
392
411
|
return previousAnnotatedAttachmentSessionWatermarks.get(change.session.id) !== change.session.updatedAt;
|
|
393
412
|
});
|
|
413
|
+
const taskEvents = sortTaskEventsForSync(Array.isArray(input.taskEvents) ? input.taskEvents : []);
|
|
414
|
+
const currentTaskEventWatermarks = new Map(taskEvents
|
|
415
|
+
.map((event) => [String(event?.id || '').trim(), String(event?.createdAt || '').trim()])
|
|
416
|
+
.filter(([id]) => id.length > 0));
|
|
417
|
+
const previousTaskEventWatermarks = input.lastPushedTaskEventWatermarks;
|
|
418
|
+
const shouldSendAllTaskEvents = !previousTaskEventWatermarks || previousTaskEventWatermarks.size === 0;
|
|
419
|
+
const taskEventUpsertChanges = taskEvents
|
|
420
|
+
.map((event) => ({
|
|
421
|
+
op: 'task-event-upsert',
|
|
422
|
+
event: {
|
|
423
|
+
id: String(event.id || '').trim(),
|
|
424
|
+
taskId: String(event.taskId || '').trim(),
|
|
425
|
+
workspaceId: String(event.workspaceId || '').trim(),
|
|
426
|
+
action: String(event.action || '').trim(),
|
|
427
|
+
actor: String(event.actor || 'system').trim() || 'system',
|
|
428
|
+
actorType: event.actorType === 'ai' || event.actorType === 'human' || event.actorType === 'system'
|
|
429
|
+
? event.actorType
|
|
430
|
+
: 'system',
|
|
431
|
+
details: event.details && typeof event.details === 'object' && !Array.isArray(event.details)
|
|
432
|
+
? event.details
|
|
433
|
+
: {},
|
|
434
|
+
createdAt: String(event.createdAt || '').trim(),
|
|
435
|
+
}
|
|
436
|
+
}))
|
|
437
|
+
.filter((change) => (change.event.id.length > 0
|
|
438
|
+
&& change.event.taskId.length > 0
|
|
439
|
+
&& change.event.workspaceId.length > 0
|
|
440
|
+
&& change.event.action.length > 0
|
|
441
|
+
&& Number.isFinite(Date.parse(change.event.createdAt))))
|
|
442
|
+
.filter((change) => {
|
|
443
|
+
if (shouldSendAllTaskEvents)
|
|
444
|
+
return true;
|
|
445
|
+
return previousTaskEventWatermarks.get(change.event.id) !== change.event.createdAt;
|
|
446
|
+
});
|
|
394
447
|
const changes = [
|
|
395
448
|
...initiativeUpsertChanges,
|
|
396
449
|
...workstreamUpsertChanges,
|
|
@@ -438,9 +491,14 @@ export function buildWorkspaceSyncPayloadModel(input) {
|
|
|
438
491
|
sessionId,
|
|
439
492
|
deletedAt: payloadBuiltAt
|
|
440
493
|
})),
|
|
494
|
+
...taskEventUpsertChanges,
|
|
441
495
|
...activeChanges,
|
|
442
496
|
...archivedChanges,
|
|
443
|
-
...Array.from(deleteTaskIds).map((taskId) => ({
|
|
497
|
+
...Array.from(deleteTaskIds).map((taskId) => ({
|
|
498
|
+
op: 'delete',
|
|
499
|
+
taskId,
|
|
500
|
+
deletedAt: pendingDeleteWatermarks.get(taskId) || payloadBuiltAt
|
|
501
|
+
}))
|
|
444
502
|
];
|
|
445
503
|
return {
|
|
446
504
|
changes,
|
|
@@ -467,6 +525,7 @@ export function buildWorkspaceSyncPayloadModel(input) {
|
|
|
467
525
|
currentAnnotatedAttachmentSessionIds,
|
|
468
526
|
deleteAnnotatedAttachmentSessionIds,
|
|
469
527
|
currentAnnotatedAttachmentSessionWatermarks,
|
|
528
|
+
currentTaskEventWatermarks,
|
|
470
529
|
pushedWatermarks
|
|
471
530
|
};
|
|
472
531
|
}
|
|
@@ -516,6 +575,21 @@ export function buildWorkspaceSyncSignatureModel(input) {
|
|
|
516
575
|
.filter((profile) => profile.id.length > 0)
|
|
517
576
|
.sort((a, b) => a.id.localeCompare(b.id))
|
|
518
577
|
: [],
|
|
578
|
+
taskEvents: Array.isArray(input.taskEvents)
|
|
579
|
+
? input.taskEvents
|
|
580
|
+
.map((event) => ({
|
|
581
|
+
id: String(event.id || '').trim(),
|
|
582
|
+
taskId: String(event.taskId || '').trim(),
|
|
583
|
+
action: String(event.action || '').trim(),
|
|
584
|
+
actor: String(event.actor || '').trim(),
|
|
585
|
+
actorType: event.actorType === 'ai' || event.actorType === 'human' || event.actorType === 'system'
|
|
586
|
+
? event.actorType
|
|
587
|
+
: 'system',
|
|
588
|
+
createdAt: String(event.createdAt || '').trim()
|
|
589
|
+
}))
|
|
590
|
+
.filter((event) => event.id.length > 0 && event.taskId.length > 0)
|
|
591
|
+
.sort((a, b) => a.id.localeCompare(b.id))
|
|
592
|
+
: [],
|
|
519
593
|
documents: Array.isArray(input.documents)
|
|
520
594
|
? input.documents
|
|
521
595
|
.map((doc) => ({
|