@taskforcehq/taskforce 0.3.305 → 0.3.306
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/dist/Taskforce.module.css +295 -89
- package/dist/TaskforceCore.js +215 -43
- package/dist/TaskforceCore.test.js +433 -28
- package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
- package/dist/assets/fonts/Noir_medium.woff2 +0 -0
- package/dist/assets/fonts/Noir_regular.woff2 +0 -0
- package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
- package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
- package/dist/compat/workspaceSyncCompat.js +46 -2
- package/dist/compat/workspaceSyncCompat.test.js +38 -1
- package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
- package/dist/components/context/ContextAttachmentManager.js +549 -0
- package/dist/components/features/AgentsModule.d.ts +10 -1
- package/dist/components/features/AgentsModule.js +260 -15
- package/dist/components/features/AgentsModule.test.js +255 -5
- package/dist/components/features/DocumentViewer.d.ts +5 -1
- package/dist/components/features/DocumentViewer.js +60 -3
- package/dist/components/features/DocumentWorkspace.js +1 -1
- package/dist/components/features/DocumentWorkspace.test.js +33 -0
- package/dist/components/features/documentWorkspaceModel.d.ts +1 -1
- package/dist/components/task/TaskCard.js +19 -28
- package/dist/components/task/TaskCard.test.js +38 -3
- package/dist/components/task/TaskContextUpload.js +4 -526
- package/dist/components/task/TaskForm.js +85 -90
- package/dist/components/task/TaskForm.test.js +110 -8
- package/dist/components/task/TaskKanban.d.ts +2 -1
- package/dist/components/task/TaskKanban.js +94 -13
- package/dist/components/task/TaskKanban.test.js +36 -0
- package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
- package/dist/components/ui/EditableAvatarButton.js +18 -0
- package/dist/components/views/PlanComparisonPage.d.ts +9 -1
- package/dist/components/views/PlanComparisonPage.js +70 -14
- package/dist/components/views/PlanComparisonPage.test.js +238 -0
- package/dist/components/views/PlansPage.js +127 -36
- package/dist/components/views/PlansPage.test.js +294 -12
- package/dist/components/views/StandaloneLayout.js +283 -533
- package/dist/components/views/panels/PlanningDrawer.d.ts +8 -1
- package/dist/components/views/panels/PlanningDrawer.js +18 -11
- package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
- package/dist/components/views/panels/PlanningDrawer.test.js +141 -0
- package/dist/components/views/planningScope.d.ts +20 -0
- package/dist/components/views/planningScope.js +25 -0
- package/dist/components/views/planningScope.test.d.ts +1 -0
- package/dist/components/views/planningScope.test.js +60 -0
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +26 -1
- package/dist/components/views/standalone/modals/AccountHubModal.js +76 -2
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +20 -3
- package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
- package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
- package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
- package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
- package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
- package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
- package/dist/core/AiProfileService.d.ts +39 -2
- package/dist/core/AiProfileService.js +639 -73
- package/dist/core/AiProfileService.test.js +19 -1
- package/dist/core/AiProfiles.test.js +566 -7
- package/dist/core/AttachmentLinkService.js +57 -15
- package/dist/core/AuthIdentityService.d.ts +107 -0
- package/dist/core/AuthIdentityService.js +284 -0
- package/dist/core/AuthTokenService.d.ts +4 -0
- package/dist/core/AuthTokenService.js +34 -6
- package/dist/core/AuthTokenService.test.js +3 -3
- package/dist/core/EntitlementsPolicy.test.js +2 -2
- package/dist/core/GlobalSettingsService.js +115 -6
- package/dist/core/PlanEntitlementService.d.ts +23 -2
- package/dist/core/PlanEntitlementService.js +343 -57
- package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
- package/dist/core/PlanFeatureCatalog.test.js +84 -0
- package/dist/core/PlanVersionPolicy.test.js +35 -2
- package/dist/core/PlanningEntities.test.js +52 -0
- package/dist/core/SignupMonetizationSettings.test.js +199 -27
- package/dist/core/SyncReconciliationService.js +2 -65
- package/dist/core/SystemAdmin.test.js +42 -42
- package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
- package/dist/core/Taskforce.d.ts +225 -14
- package/dist/core/Taskforce.js +765 -192
- package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
- package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
- package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
- package/dist/core/WorkspaceLifecycleService.js +0 -5
- package/dist/core/WorkspacePermissions.test.js +25 -1
- package/dist/core/WorkspacePlanMode.test.js +117 -16
- package/dist/core/shared.d.ts +1 -1
- package/dist/core/shared.js +5 -1
- package/dist/core/types.d.ts +68 -2
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -1
- package/dist/hooks/sync/bootstrap.js +2 -0
- package/dist/hooks/sync/controlPlane.d.ts +37 -0
- package/dist/hooks/sync/controlPlane.js +78 -0
- package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
- package/dist/hooks/sync/controlPlane.test.js +121 -0
- package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
- package/dist/hooks/sync/lifecyclePolicy.js +93 -0
- package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
- package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +10 -1
- package/dist/hooks/sync/orchestratorShared.js +68 -47
- package/dist/hooks/sync/orchestratorShared.test.js +50 -1
- package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
- package/dist/hooks/sync/pullLifecycle.js +24 -0
- package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
- package/dist/hooks/sync/pullLifecycle.test.js +80 -0
- package/dist/hooks/sync/recovery.d.ts +16 -2
- package/dist/hooks/sync/recovery.js +171 -53
- package/dist/hooks/sync/recovery.test.d.ts +1 -0
- package/dist/hooks/sync/recovery.test.js +292 -0
- package/dist/hooks/sync/transfers.d.ts +15 -3
- package/dist/hooks/sync/transfers.js +179 -62
- package/dist/hooks/sync/transfers.test.d.ts +1 -0
- package/dist/hooks/sync/transfers.test.js +396 -0
- package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
- package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
- package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
- package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
- package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
- package/dist/hooks/sync/useSyncStatusActions.js +88 -0
- package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
- package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
- package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
- package/dist/hooks/sync/useSyncStatusControls.js +60 -0
- package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
- package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +9 -2
- package/dist/hooks/useSyncOrchestrator.js +359 -178
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +407 -0
- package/dist/hooks/useTaskData.js +8 -3
- package/dist/hooks/useTaskData.test.js +45 -0
- package/dist/hooks/useTaskforce.d.ts +60 -2
- package/dist/hooks/useTaskforce.js +549 -240
- package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
- package/dist/hooks/useTaskforce.runtime-routing.test.js +152 -0
- package/dist/hooks/useTaskforce.sync-behavior.test.js +2380 -212
- package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
- package/dist/hooks/useWorkspaceSyncController.js +6 -1
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +316 -35
- package/dist/localization/locales/en-US.d.ts +0 -1
- package/dist/localization/locales/en-US.js +0 -1
- package/dist/localization/locales/es-419.js +0 -1
- package/dist/localization/locales/pt-BR.js +0 -1
- package/dist/mcp/documentAssetRegistrar.js +3 -3
- package/dist/mcp/runtime.d.ts +2 -0
- package/dist/mcp/runtime.js +131 -64
- package/dist/mcp/runtime.test.js +291 -12
- package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
- package/dist/migrations/taskSchemaMigrations.js +171 -61
- package/dist/migrations/taskSchemaMigrations.test.js +15 -0
- package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
- package/dist/resources/templates/workflows/collaborate.yaml +1 -1
- package/dist/resources/templates/workflows/evaluate.yaml +2 -2
- package/dist/resources/templates/workflows/plan.yaml +1 -1
- package/dist/resources/templates/workflows/review.yaml +2 -2
- package/dist/server/annotatedAttachmentsRoutes.test.js +3 -3
- package/dist/server/auth/providers/apple.d.ts +33 -0
- package/dist/server/auth/providers/apple.js +82 -0
- package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
- package/dist/server/auth/providers/appleClientSecret.js +65 -0
- package/dist/server/auth/providers/github.d.ts +26 -0
- package/dist/server/auth/providers/github.js +86 -0
- package/dist/server/auth/providers/google.d.ts +21 -0
- package/dist/server/auth/providers/google.js +56 -0
- package/dist/server/auth/providers/types.d.ts +21 -0
- package/dist/server/auth/providers/types.js +1 -0
- package/dist/server/auth.d.ts +2 -0
- package/dist/server/auth.js +6 -3
- package/dist/server/documentReviewRoutes.test.js +36 -3
- package/dist/server/index.cookieProxy.test.js +1 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.js +120 -9
- package/dist/server/index.rateLimit.test.js +3 -0
- package/dist/server/index.test.js +106 -3
- package/dist/server/routes/admin.d.ts +3 -0
- package/dist/server/routes/admin.js +272 -19
- package/dist/server/routes/auth.d.ts +15 -2
- package/dist/server/routes/auth.js +994 -57
- package/dist/server/routes/authSupport.d.ts +1 -1
- package/dist/server/routes/authSupport.js +1 -1
- package/dist/server/routes/billing.js +361 -35
- package/dist/server/routes/billing.test.js +592 -12
- package/dist/server/routes/documents.d.ts +1 -0
- package/dist/server/routes/documents.js +172 -70
- package/dist/server/routes/shared.d.ts +28 -2
- package/dist/server/routes/shared.js +69 -4
- package/dist/server/routes/sync.integration.test.js +437 -39
- package/dist/server/routes/sync.js +61 -605
- package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
- package/dist/server/routes/syncAuxRoutes.js +91 -0
- package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
- package/dist/server/routes/syncAuxRoutes.test.js +158 -0
- package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
- package/dist/server/routes/syncPullApplyRoutes.js +204 -0
- package/dist/server/routes/syncPushRoutes.d.ts +24 -0
- package/dist/server/routes/syncPushRoutes.js +212 -0
- package/dist/server/routes/syncRouteGuards.d.ts +36 -0
- package/dist/server/routes/syncRouteGuards.js +67 -0
- package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
- package/dist/server/routes/syncRouteGuards.test.js +94 -0
- package/dist/server/routes/syncRouteTypes.d.ts +13 -0
- package/dist/server/routes/syncRouteTypes.js +1 -0
- package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
- package/dist/server/routes/syncSnapshotRoutes.js +180 -0
- package/dist/server/routes/tasks.js +43 -1
- package/dist/server/routes/workspaces.js +36 -5
- package/dist/server/routes.js +60 -3
- package/dist/server/routes.test.js +1824 -171
- package/dist/services/workflowExportSnapshots.test.js +2 -0
- package/dist/shared/aiProfileSeatScope.d.ts +5 -0
- package/dist/shared/aiProfileSeatScope.js +21 -0
- package/dist/shared/passwordPolicy.d.ts +13 -0
- package/dist/shared/passwordPolicy.js +84 -0
- package/dist/shared/passwordPolicy.test.d.ts +1 -0
- package/dist/shared/passwordPolicy.test.js +34 -0
- package/dist/storage/workspaceAssetStore.d.ts +15 -3
- package/dist/storage/workspaceAssetStore.js +132 -44
- package/dist/storage/workspaceAssetStore.test.js +93 -6
- package/dist/styles/fonts.css +70 -0
- package/dist/sync/collaborationSyncPayload.d.ts +6 -1
- package/dist/sync/collaborationSyncPayload.js +17 -11
- package/dist/sync/collaborationSyncPayload.test.js +13 -1
- package/dist/sync/contentSyncState.d.ts +4 -8
- package/dist/sync/contentSyncState.js +17 -70
- package/dist/sync/syncApplyHandlers.d.ts +7 -0
- package/dist/sync/syncApplyHandlers.js +28 -13
- package/dist/sync/syncApplyHandlers.test.js +70 -3
- package/dist/sync/syncFieldSemantics.d.ts +12 -0
- package/dist/sync/syncFieldSemantics.js +55 -0
- package/dist/sync/syncResourceAdapters.d.ts +44 -0
- package/dist/sync/syncResourceAdapters.js +77 -0
- package/dist/sync/syncService.d.ts +5 -0
- package/dist/sync/syncService.js +52 -0
- package/dist/sync/syncService.test.js +57 -3
- package/dist/sync/taskSyncChangeKey.d.ts +2 -0
- package/dist/sync/taskSyncChangeKey.js +143 -0
- package/dist/sync/taskSyncPayload.js +3 -8
- package/dist/sync/workspacePullFeed.js +4 -3
- package/dist/sync/workspacePullFeed.test.js +43 -0
- package/dist/sync/workspaceRepair.js +5 -2
- package/dist/sync/workspaceSyncModel.d.ts +45 -0
- package/dist/sync/workspaceSyncModel.js +183 -28
- package/dist/sync/workspaceSyncModel.test.js +291 -12
- package/dist/sync/workspaceSyncState.d.ts +17 -0
- package/dist/sync/workspaceSyncState.js +2 -1
- package/dist/sync/workspaceSyncSurface.js +3 -8
- package/dist/types.d.ts +17 -0
- package/dist/ui/assets/AgentsModule-DrLawwNl.js +1 -0
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BG6P_GVW.js → AnnotatedAttachmentWorkspace-Z9_whf7F.js} +1 -1
- package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
- package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +252 -0
- package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +1 -0
- package/dist/ui/assets/{InitiativesModule-Dhm7M8e7.js → InitiativesModule-9E6ParrY.js} +1 -1
- package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
- package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
- package/dist/ui/assets/PlansPage-CvfnMiWq.css +1 -0
- package/dist/ui/assets/PlansPage-DRXscYxH.js +1 -0
- package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
- package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
- package/dist/ui/assets/{TaskSettings-ln0aF7xc.js → TaskSettings-DZX4jk7e.js} +1 -1
- package/dist/ui/assets/{WorkflowsModule-BcS4afRn.js → WorkflowsModule-BA7jcEpH.js} +1 -1
- package/dist/ui/assets/index-9eT8e0Lu.css +1 -0
- package/dist/ui/assets/index-C6k75jr8.js +6 -0
- package/dist/ui/assets/{vendor-icons-BSUaRwF8.js → vendor-icons-DuEd_65c.js} +1 -1
- package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
- package/dist/ui/fonts/Noir_medium.otf +0 -0
- package/dist/ui/fonts/Noir_medium.woff2 +0 -0
- package/dist/ui/fonts/Noir_regular.otf +0 -0
- package/dist/ui/fonts/Noir_regular.woff2 +0 -0
- package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
- package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
- package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
- package/dist/ui/index.html +3 -3
- package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
- package/dist/utils/assignees.d.ts +2 -0
- package/dist/utils/assignees.js +2 -0
- package/dist/utils/avatarUpload.d.ts +27 -0
- package/dist/utils/avatarUpload.js +132 -0
- package/dist/utils/avatarUpload.test.d.ts +1 -0
- package/dist/utils/avatarUpload.test.js +40 -0
- package/dist/utils/bootstrapTrace.d.ts +7 -0
- package/dist/utils/bootstrapTrace.js +29 -0
- package/dist/utils/commercialLifecyclePresentation.js +3 -0
- package/dist/utils/contextAssetEvents.d.ts +2 -0
- package/dist/utils/syncEventDetails.d.ts +3 -0
- package/dist/utils/syncEventDetails.js +64 -0
- package/dist/utils/syncEventDetails.test.d.ts +1 -0
- package/dist/utils/syncEventDetails.test.js +23 -0
- package/dist/utils/syncEventPresentation.d.ts +15 -0
- package/dist/utils/syncEventPresentation.js +129 -0
- package/dist/utils/syncEventPresentation.test.d.ts +1 -0
- package/dist/utils/syncEventPresentation.test.js +64 -0
- package/dist/utils/syncStatusPresentation.d.ts +72 -0
- package/dist/utils/syncStatusPresentation.js +217 -0
- package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
- package/dist/utils/syncStatusPresentation.test.js +164 -0
- package/dist/utils/taskActivity.d.ts +2 -0
- package/dist/utils/taskActivity.js +228 -22
- package/dist/utils/taskActivity.test.js +119 -8
- package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
- package/dist/utils/workspaceSyncPresentation.js +68 -2
- package/dist/utils/workspaceSyncPresentation.test.js +36 -1
- package/package.json +3 -2
- package/scripts/export-sqlite-to-postgres.mjs +7 -9
- package/scripts/run-e2e-realtime.sh +17 -3
- package/scripts/run-smoke.sh +17 -5
- package/scripts/run-soak.sh +17 -5
- package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
- package/src/resources/templates/workflows/collaborate.yaml +1 -1
- package/src/resources/templates/workflows/evaluate.yaml +2 -2
- package/src/resources/templates/workflows/plan.yaml +1 -1
- package/src/resources/templates/workflows/review.yaml +2 -2
- package/dist/ui/assets/AgentsModule-N2MnQBZk.js +0 -1
- package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +0 -1
- package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +0 -252
- package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
- package/dist/ui/assets/PlansPage-Cq0zXj3I.js +0 -1
- package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
- package/dist/ui/assets/index-C2-OXZV7.css +0 -1
- package/dist/ui/assets/index-DcSI5F86.js +0 -6
- package/scripts/migrate-planning-model.ts +0 -233
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
@font-face {
|
|
2
|
+
font-family: "Russo One";
|
|
3
|
+
src: url("../assets/fonts/RussoOne-Regular.woff2") format("woff2");
|
|
4
|
+
font-weight: 400;
|
|
5
|
+
font-style: normal;
|
|
6
|
+
font-display: swap;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
@font-face {
|
|
10
|
+
font-family: "Courier Prime";
|
|
11
|
+
src: url("../assets/fonts/CourierPrime-Regular.woff2") format("woff2");
|
|
12
|
+
font-weight: 400;
|
|
13
|
+
font-style: normal;
|
|
14
|
+
font-display: swap;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@font-face {
|
|
18
|
+
font-family: "Courier Prime";
|
|
19
|
+
src: url("../assets/fonts/CourierPrime-Italic.woff2") format("woff2");
|
|
20
|
+
font-weight: 400;
|
|
21
|
+
font-style: italic;
|
|
22
|
+
font-display: swap;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@font-face {
|
|
26
|
+
font-family: "Courier Prime";
|
|
27
|
+
src: url("../assets/fonts/CourierPrime-Bold.woff2") format("woff2");
|
|
28
|
+
font-weight: 700;
|
|
29
|
+
font-style: normal;
|
|
30
|
+
font-display: swap;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@font-face {
|
|
34
|
+
font-family: "Courier Prime";
|
|
35
|
+
src: url("../assets/fonts/CourierPrime-BoldItalic.woff2") format("woff2");
|
|
36
|
+
font-weight: 700;
|
|
37
|
+
font-style: italic;
|
|
38
|
+
font-display: swap;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@font-face {
|
|
42
|
+
font-family: "Special Elite";
|
|
43
|
+
src: url("../assets/fonts/SpecialElite-Regular.woff2") format("woff2");
|
|
44
|
+
font-weight: 400;
|
|
45
|
+
font-style: normal;
|
|
46
|
+
font-display: swap;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@font-face {
|
|
50
|
+
font-family: "Noir";
|
|
51
|
+
src: url("../assets/fonts/Noir_regular.woff2") format("woff2");
|
|
52
|
+
font-weight: 400;
|
|
53
|
+
font-style: normal;
|
|
54
|
+
font-display: swap;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@font-face {
|
|
58
|
+
font-family: "Noir";
|
|
59
|
+
src: url("../assets/fonts/Noir_medium.woff2") format("woff2");
|
|
60
|
+
font-weight: 500;
|
|
61
|
+
font-style: normal;
|
|
62
|
+
font-display: swap;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
:root {
|
|
66
|
+
--font-brand: "Russo One", sans-serif;
|
|
67
|
+
--font-typewriter: "Courier Prime", "Courier New", Courier, monospace;
|
|
68
|
+
--font-stamped: "Special Elite", "Courier Prime", "Courier New", Courier, monospace;
|
|
69
|
+
--font-noir: "Noir", var(--font-sans, ui-sans-serif, system-ui, sans-serif);
|
|
70
|
+
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import type { WorkspaceSyncAiProfileSnapshot, WorkspaceSyncAnnotatedAttachmentSessionSnapshot, WorkspaceSyncDocumentReviewCommentSnapshot, WorkspaceSyncDocumentReviewSessionSnapshot } from './workspaceSyncModel.js';
|
|
2
2
|
import type { AiProfileSurfaceType } from '../shared/aiProfileSurfaceType.js';
|
|
3
|
+
import type { AiProfileSeatScope } from '../shared/aiProfileSeatScope.js';
|
|
3
4
|
export declare function normalizeAiProfileSyncPayload(raw: unknown, workspaceId: string, normalizeSurfaceType: (value: unknown, context: {
|
|
4
5
|
source: string;
|
|
5
6
|
profileId?: string;
|
|
6
7
|
workspaceId?: string;
|
|
7
|
-
}) => AiProfileSurfaceType | null,
|
|
8
|
+
}) => AiProfileSurfaceType | null, normalizeSeatScope: (value: unknown, context: {
|
|
9
|
+
source: string;
|
|
10
|
+
profileId?: string;
|
|
11
|
+
workspaceId?: string;
|
|
12
|
+
}) => AiProfileSeatScope | null, source: string): WorkspaceSyncAiProfileSnapshot | null;
|
|
8
13
|
export declare function normalizeDocumentReviewSessionSyncPayload(raw: unknown, sourceWatermark?: string): WorkspaceSyncDocumentReviewSessionSnapshot | null;
|
|
9
14
|
export declare function normalizeDocumentReviewCommentSyncPayload(raw: unknown, options?: {
|
|
10
15
|
sessionId?: string;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
+
import { hasOwnSyncField, normalizeSyncNullableString, normalizeSyncOptionalString, readSyncNullableIdField, readSyncNullableIsoStringField, readSyncNullableStringField, readSyncSameOriginAssetUrlField, } from './syncFieldSemantics.js';
|
|
1
2
|
function toOptionalTrimmedString(value) {
|
|
2
|
-
|
|
3
|
-
return undefined;
|
|
4
|
-
const normalized = value.trim();
|
|
5
|
-
return normalized.length > 0 ? normalized : undefined;
|
|
3
|
+
return normalizeSyncOptionalString(value);
|
|
6
4
|
}
|
|
7
5
|
function toNullableTrimmedString(value) {
|
|
8
|
-
return
|
|
6
|
+
return normalizeSyncNullableString(value);
|
|
9
7
|
}
|
|
10
8
|
function toOptionalPositiveInteger(value) {
|
|
11
9
|
if (value === undefined || value === null || value === '')
|
|
@@ -23,7 +21,7 @@ function toOptionalNonNegativeInteger(value) {
|
|
|
23
21
|
return 0;
|
|
24
22
|
return Math.max(0, Math.floor(normalized));
|
|
25
23
|
}
|
|
26
|
-
export function normalizeAiProfileSyncPayload(raw, workspaceId, normalizeSurfaceType, source) {
|
|
24
|
+
export function normalizeAiProfileSyncPayload(raw, workspaceId, normalizeSurfaceType, normalizeSeatScope, source) {
|
|
27
25
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
28
26
|
return null;
|
|
29
27
|
const profile = raw;
|
|
@@ -39,17 +37,25 @@ export function normalizeAiProfileSyncPayload(raw, workspaceId, normalizeSurface
|
|
|
39
37
|
username: String(profile.username || '').trim(),
|
|
40
38
|
icon: String(profile.icon || 'Bot').trim() || 'Bot',
|
|
41
39
|
color: String(profile.color || '#8b5cf6').trim() || '#8b5cf6',
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
...(hasOwnSyncField(profile, 'avatarUrl') ? { avatarUrl: readSyncSameOriginAssetUrlField(profile, 'avatarUrl') ?? null } : {}),
|
|
41
|
+
...(hasOwnSyncField(profile, 'avatarSourceUrl') ? { avatarSourceUrl: readSyncSameOriginAssetUrlField(profile, 'avatarSourceUrl') ?? null } : {}),
|
|
42
|
+
description: readSyncNullableStringField(profile, 'description') ?? null,
|
|
43
|
+
role: readSyncNullableStringField(profile, 'role') ?? null,
|
|
44
|
+
provider: readSyncNullableStringField(profile, 'provider') ?? null,
|
|
45
|
+
model: readSyncNullableStringField(profile, 'model') ?? null,
|
|
46
46
|
surfaceType: normalizeSurfaceType(profile.surfaceType, { source, profileId, workspaceId: profileWorkspaceId }),
|
|
47
|
+
seatScope: normalizeSeatScope(profile.seatScope, { source, profileId, workspaceId: profileWorkspaceId }),
|
|
47
48
|
providerMetadata: profile.providerMetadata && typeof profile.providerMetadata === 'object' && !Array.isArray(profile.providerMetadata)
|
|
48
49
|
? profile.providerMetadata
|
|
49
50
|
: null,
|
|
51
|
+
archivedAt: readSyncNullableIsoStringField(profile, 'archivedAt') ?? null,
|
|
52
|
+
archivedBy: readSyncNullableIdField(profile, 'archivedBy') ?? null,
|
|
53
|
+
archivedReason: readSyncNullableStringField(profile, 'archivedReason') ?? null,
|
|
54
|
+
mergedIntoProfileId: readSyncNullableIdField(profile, 'mergedIntoProfileId') ?? null,
|
|
55
|
+
rosterStateUpdatedAt: readSyncNullableIsoStringField(profile, 'rosterStateUpdatedAt') ?? null,
|
|
50
56
|
createdAt: String(profile.createdAt || '').trim(),
|
|
51
57
|
updatedAt: String(profile.updatedAt || profile.createdAt || '').trim(),
|
|
52
|
-
lastActiveAt: toNullableTrimmedString(profile.lastActiveAt),
|
|
58
|
+
lastActiveAt: readSyncNullableIsoStringField(profile, 'lastActiveAt') ?? toNullableTrimmedString(profile.lastActiveAt),
|
|
53
59
|
};
|
|
54
60
|
}
|
|
55
61
|
export function normalizeDocumentReviewSessionSyncPayload(raw, sourceWatermark) {
|
|
@@ -6,6 +6,11 @@ describe('collaborationSyncPayload', () => {
|
|
|
6
6
|
return null;
|
|
7
7
|
return String(value).trim();
|
|
8
8
|
};
|
|
9
|
+
const normalizeSeatScope = (value) => {
|
|
10
|
+
if (value === undefined || value === null || value === '')
|
|
11
|
+
return null;
|
|
12
|
+
return String(value).trim();
|
|
13
|
+
};
|
|
9
14
|
it('normalizes ai profile sync payloads with workspace and surface type', () => {
|
|
10
15
|
const profile = normalizeAiProfileSyncPayload({
|
|
11
16
|
id: 'profile-1',
|
|
@@ -20,11 +25,12 @@ describe('collaborationSyncPayload', () => {
|
|
|
20
25
|
provider: ' OpenAI ',
|
|
21
26
|
model: ' gpt-5.4 ',
|
|
22
27
|
surfaceType: 'agent',
|
|
28
|
+
seatScope: 'cloud_metered',
|
|
23
29
|
providerMetadata: { foo: 'bar' },
|
|
24
30
|
createdAt: '2026-04-10T10:00:00.000Z',
|
|
25
31
|
updatedAt: '2026-04-10T11:00:00.000Z',
|
|
26
32
|
lastActiveAt: '2026-04-10T12:00:00.000Z',
|
|
27
|
-
}, 'workspace-a', normalizeSurfaceType, 'test.ai');
|
|
33
|
+
}, 'workspace-a', normalizeSurfaceType, normalizeSeatScope, 'test.ai');
|
|
28
34
|
expect(profile).toEqual({
|
|
29
35
|
id: 'profile-1',
|
|
30
36
|
workspaceId: 'workspace-a',
|
|
@@ -38,7 +44,13 @@ describe('collaborationSyncPayload', () => {
|
|
|
38
44
|
provider: 'OpenAI',
|
|
39
45
|
model: 'gpt-5.4',
|
|
40
46
|
surfaceType: 'agent',
|
|
47
|
+
seatScope: 'cloud_metered',
|
|
41
48
|
providerMetadata: { foo: 'bar' },
|
|
49
|
+
archivedAt: null,
|
|
50
|
+
archivedBy: null,
|
|
51
|
+
archivedReason: null,
|
|
52
|
+
mergedIntoProfileId: null,
|
|
53
|
+
rosterStateUpdatedAt: null,
|
|
42
54
|
createdAt: '2026-04-10T10:00:00.000Z',
|
|
43
55
|
updatedAt: '2026-04-10T11:00:00.000Z',
|
|
44
56
|
lastActiveAt: '2026-04-10T12:00:00.000Z',
|
|
@@ -28,12 +28,8 @@ export declare function resolveCanonicalAssetSyncLinkState(deps: ContentSyncStat
|
|
|
28
28
|
effectiveUpdatedAt: string;
|
|
29
29
|
};
|
|
30
30
|
export declare function getCanonicalSyncDocumentMetadata(deps: ContentSyncStateDeps, relativePath: string, workspaceId: string): Omit<DocumentSyncPayload, 'path' | 'updatedAt' | 'content'>;
|
|
31
|
-
export declare function hasDocumentSyncMetadataDrift(deps: ContentSyncStateDeps, workspaceId: string, doc: DocumentSyncPayload
|
|
32
|
-
|
|
33
|
-
}): boolean;
|
|
34
|
-
export declare function hasBinaryAssetSyncMetadataDrift(deps: ContentSyncStateDeps, workspaceId: string, asset: BinaryAssetSyncPayload, options?: {
|
|
35
|
-
allowReferenceNumberReassignment?: boolean;
|
|
36
|
-
}): boolean;
|
|
31
|
+
export declare function hasDocumentSyncMetadataDrift(deps: ContentSyncStateDeps, workspaceId: string, doc: DocumentSyncPayload): boolean;
|
|
32
|
+
export declare function hasBinaryAssetSyncMetadataDrift(deps: ContentSyncStateDeps, workspaceId: string, asset: BinaryAssetSyncPayload): boolean;
|
|
37
33
|
export declare function reconcileWorkspaceSyncLinks(deps: ContentSyncStateDeps, input: {
|
|
38
34
|
workspaceId: string;
|
|
39
35
|
assetId: string;
|
|
@@ -64,10 +60,10 @@ export declare function createContentSyncState(deps: ContentSyncStateDeps): {
|
|
|
64
60
|
effectiveUpdatedAt: string;
|
|
65
61
|
};
|
|
66
62
|
getCanonicalSyncDocumentMetadata: (relativePath: string, workspaceId: string) => Omit<DocumentSyncPayload, "path" | "updatedAt" | "content">;
|
|
67
|
-
hasDocumentSyncMetadataDrift: (workspaceId: string, doc: DocumentSyncPayload,
|
|
63
|
+
hasDocumentSyncMetadataDrift: (workspaceId: string, doc: DocumentSyncPayload, _options?: {
|
|
68
64
|
allowReferenceNumberReassignment?: boolean;
|
|
69
65
|
}) => boolean;
|
|
70
|
-
hasBinaryAssetSyncMetadataDrift: (workspaceId: string, asset: BinaryAssetSyncPayload,
|
|
66
|
+
hasBinaryAssetSyncMetadataDrift: (workspaceId: string, asset: BinaryAssetSyncPayload, _options?: {
|
|
71
67
|
allowReferenceNumberReassignment?: boolean;
|
|
72
68
|
}) => boolean;
|
|
73
69
|
reconcileWorkspaceSyncLinks: (input: {
|
|
@@ -38,7 +38,7 @@ export function getCanonicalSyncDocumentMetadata(deps, relativePath, workspaceId
|
|
|
38
38
|
linkRole: primaryLink?.role === 'reference' ? 'reference' : 'attachment'
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
-
export function hasDocumentSyncMetadataDrift(deps, workspaceId, doc
|
|
41
|
+
export function hasDocumentSyncMetadataDrift(deps, workspaceId, doc) {
|
|
42
42
|
if (!deps.workspaceAssetStore)
|
|
43
43
|
return false;
|
|
44
44
|
const existingAsset = deps.workspaceAssetStore.getByStorageKey(doc.path, workspaceId);
|
|
@@ -67,20 +67,10 @@ export function hasDocumentSyncMetadataDrift(deps, workspaceId, doc, options) {
|
|
|
67
67
|
? doc.taskId.trim()
|
|
68
68
|
: null;
|
|
69
69
|
const normalizedIncomingLinkRole = doc.linkRole === 'reference' ? 'reference' : 'attachment';
|
|
70
|
-
const allowReferenceNumberReassignment = options?.allowReferenceNumberReassignment === true;
|
|
71
70
|
const referenceNumberMismatch = Boolean(existingAsset.referenceNumber
|
|
72
71
|
&& normalizedIncomingReference
|
|
73
72
|
&& existingAsset.referenceNumber !== normalizedIncomingReference);
|
|
74
|
-
|
|
75
|
-
deps.recordSyncDiagnostic(workspaceId, 'apply', 'Detected document reference number mismatch during normal sync.', {
|
|
76
|
-
source: 'documents.referenceNumber.mismatch',
|
|
77
|
-
path: doc.path,
|
|
78
|
-
assetId: existingAsset.assetId,
|
|
79
|
-
existingReferenceNumber: existingAsset.referenceNumber,
|
|
80
|
-
incomingReferenceNumber: normalizedIncomingReference
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
return (allowReferenceNumberReassignment && existingAsset.referenceNumber !== normalizedIncomingReference)
|
|
73
|
+
return referenceNumberMismatch
|
|
84
74
|
|| existingAsset.version !== normalizedIncomingVersion
|
|
85
75
|
|| (existingAsset.documentId || null) !== normalizedIncomingDocumentId
|
|
86
76
|
|| (existingAsset.logicalName || null) !== normalizedIncomingLogicalName
|
|
@@ -89,7 +79,7 @@ export function hasDocumentSyncMetadataDrift(deps, workspaceId, doc, options) {
|
|
|
89
79
|
|| (primaryLink?.displayName || null) !== normalizedIncomingCaption
|
|
90
80
|
|| ((primaryLink?.role === 'reference' ? 'reference' : 'attachment')) !== normalizedIncomingLinkRole;
|
|
91
81
|
}
|
|
92
|
-
export function hasBinaryAssetSyncMetadataDrift(deps, workspaceId, asset
|
|
82
|
+
export function hasBinaryAssetSyncMetadataDrift(deps, workspaceId, asset) {
|
|
93
83
|
if (!deps.workspaceAssetStore)
|
|
94
84
|
return false;
|
|
95
85
|
const existingAsset = deps.workspaceAssetStore.getByStorageKey(asset.path, workspaceId);
|
|
@@ -116,20 +106,10 @@ export function hasBinaryAssetSyncMetadataDrift(deps, workspaceId, asset, option
|
|
|
116
106
|
: asset.kind === 'image'
|
|
117
107
|
? 'image'
|
|
118
108
|
: 'attachment';
|
|
119
|
-
const allowReferenceNumberReassignment = options?.allowReferenceNumberReassignment === true;
|
|
120
109
|
const referenceNumberMismatch = Boolean(existingAsset.referenceNumber
|
|
121
110
|
&& normalizedIncomingReference
|
|
122
111
|
&& existingAsset.referenceNumber !== normalizedIncomingReference);
|
|
123
|
-
|
|
124
|
-
deps.recordSyncDiagnostic(workspaceId, 'apply', 'Detected asset reference number mismatch during normal sync.', {
|
|
125
|
-
source: 'assets.referenceNumber.mismatch',
|
|
126
|
-
path: asset.path,
|
|
127
|
-
assetId: existingAsset.assetId,
|
|
128
|
-
existingReferenceNumber: existingAsset.referenceNumber,
|
|
129
|
-
incomingReferenceNumber: normalizedIncomingReference
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
return (allowReferenceNumberReassignment && existingAsset.referenceNumber !== normalizedIncomingReference)
|
|
112
|
+
return referenceNumberMismatch
|
|
133
113
|
|| (existingAsset.logicalName || null) !== normalizedIncomingLogicalName
|
|
134
114
|
|| (existingAsset.originalFilename || null) !== normalizedIncomingFilename
|
|
135
115
|
|| (primaryLink?.taskId || null) !== normalizedIncomingTaskId
|
|
@@ -175,7 +155,8 @@ export function reconcileWorkspaceSyncLinks(deps, input) {
|
|
|
175
155
|
deps.workspaceAssetStore.deleteLink({
|
|
176
156
|
workspaceId,
|
|
177
157
|
assetId,
|
|
178
|
-
|
|
158
|
+
targetType: link.targetType,
|
|
159
|
+
targetId: link.targetId,
|
|
179
160
|
role: link.role
|
|
180
161
|
});
|
|
181
162
|
}
|
|
@@ -205,26 +186,9 @@ export async function listWorkspaceSyncDocumentsSnapshot(deps, basePath, workspa
|
|
|
205
186
|
const root = path.resolve(basePath);
|
|
206
187
|
const objectStorageClient = deps.getObjectStorageClient();
|
|
207
188
|
const candidatePaths = new Set();
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
return;
|
|
212
|
-
const entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
213
|
-
for (const entry of entries) {
|
|
214
|
-
const absolutePath = path.join(directory, entry.name);
|
|
215
|
-
if (entry.isDirectory()) {
|
|
216
|
-
collectMarkdownPaths(absolutePath);
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
if (!entry.isFile())
|
|
220
|
-
continue;
|
|
221
|
-
const relativeFromRoot = path.relative(root, absolutePath).replace(/\\/g, '/');
|
|
222
|
-
const normalized = deps.normalizeWorkspaceSyncDocumentPath(relativeFromRoot);
|
|
223
|
-
if (normalized)
|
|
224
|
-
candidatePaths.add(normalized);
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
collectMarkdownPaths(docsRoot);
|
|
189
|
+
// Shared document sync is canonical-document-only. Raw .taskforce/docs files
|
|
190
|
+
// are intentionally excluded because they are not workspace-scoped and must
|
|
191
|
+
// not re-enter cross-runtime shared document sync.
|
|
228
192
|
if (deps.workspaceAssetStore) {
|
|
229
193
|
const canonicalAssets = deps.workspaceAssetStore.listAllByWorkspace(workspaceId, {
|
|
230
194
|
includeDeleted: false,
|
|
@@ -345,28 +309,9 @@ export async function listWorkspaceSyncDocumentsSnapshot(deps, basePath, workspa
|
|
|
345
309
|
return docs;
|
|
346
310
|
}
|
|
347
311
|
export async function listWorkspaceSyncDocumentIndex(deps, basePath, workspaceId = 'default') {
|
|
348
|
-
const root = path.resolve(basePath);
|
|
349
312
|
const candidatePaths = new Set();
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
if (!fs.existsSync(directory))
|
|
353
|
-
return;
|
|
354
|
-
const entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
355
|
-
for (const entry of entries) {
|
|
356
|
-
const absolutePath = path.join(directory, entry.name);
|
|
357
|
-
if (entry.isDirectory()) {
|
|
358
|
-
collectMarkdownPaths(absolutePath);
|
|
359
|
-
continue;
|
|
360
|
-
}
|
|
361
|
-
if (!entry.isFile())
|
|
362
|
-
continue;
|
|
363
|
-
const relativeFromRoot = path.relative(root, absolutePath).replace(/\\/g, '/');
|
|
364
|
-
const normalized = deps.normalizeWorkspaceSyncDocumentPath(relativeFromRoot);
|
|
365
|
-
if (normalized)
|
|
366
|
-
candidatePaths.add(normalized);
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
collectMarkdownPaths(docsRoot);
|
|
313
|
+
// Keep the document index aligned with the snapshot invariant above: only
|
|
314
|
+
// canonical workspace document assets may participate in shared document sync.
|
|
370
315
|
if (deps.workspaceAssetStore) {
|
|
371
316
|
const canonicalAssets = deps.workspaceAssetStore.listAllByWorkspace(workspaceId, {
|
|
372
317
|
includeDeleted: false,
|
|
@@ -926,7 +871,8 @@ export function deleteWorkspaceSyncBinaryAsset(deps, basePath, assetPath, worksp
|
|
|
926
871
|
deps.workspaceAssetStore.deleteLink({
|
|
927
872
|
workspaceId,
|
|
928
873
|
assetId: existing.assetId,
|
|
929
|
-
|
|
874
|
+
targetType: link.targetType,
|
|
875
|
+
targetId: link.targetId,
|
|
930
876
|
role: link.role
|
|
931
877
|
});
|
|
932
878
|
}
|
|
@@ -976,7 +922,8 @@ export function deleteWorkspaceSyncDocument(deps, basePath, docPath, workspaceId
|
|
|
976
922
|
deps.workspaceAssetStore.deleteLink({
|
|
977
923
|
workspaceId,
|
|
978
924
|
assetId: existing.assetId,
|
|
979
|
-
|
|
925
|
+
targetType: link.targetType,
|
|
926
|
+
targetId: link.targetId,
|
|
980
927
|
role: link.role
|
|
981
928
|
});
|
|
982
929
|
}
|
|
@@ -996,8 +943,8 @@ export function createContentSyncState(deps) {
|
|
|
996
943
|
return {
|
|
997
944
|
resolveCanonicalAssetSyncLinkState: (asset, workspaceId) => resolveCanonicalAssetSyncLinkState(deps, asset, workspaceId),
|
|
998
945
|
getCanonicalSyncDocumentMetadata: (relativePath, workspaceId) => getCanonicalSyncDocumentMetadata(deps, relativePath, workspaceId),
|
|
999
|
-
hasDocumentSyncMetadataDrift: (workspaceId, doc,
|
|
1000
|
-
hasBinaryAssetSyncMetadataDrift: (workspaceId, asset,
|
|
946
|
+
hasDocumentSyncMetadataDrift: (workspaceId, doc, _options) => hasDocumentSyncMetadataDrift(deps, workspaceId, doc),
|
|
947
|
+
hasBinaryAssetSyncMetadataDrift: (workspaceId, asset, _options) => hasBinaryAssetSyncMetadataDrift(deps, workspaceId, asset),
|
|
1001
948
|
reconcileWorkspaceSyncLinks: (input) => reconcileWorkspaceSyncLinks(deps, input),
|
|
1002
949
|
listWorkspaceSyncDocumentDeletesSnapshot: (workspaceId = 'default') => listWorkspaceSyncDocumentDeletesSnapshot(deps, workspaceId),
|
|
1003
950
|
listWorkspaceSyncDocumentIndex: (basePath, workspaceId = 'default') => listWorkspaceSyncDocumentIndex(deps, basePath, workspaceId),
|
|
@@ -2,6 +2,7 @@ import type { TaskforceCore, TaskforceSyncCapable } from '../core/Taskforce.js';
|
|
|
2
2
|
import type { WorkspaceSyncAiProfileSnapshot, WorkspaceSyncAnnotatedAttachmentSessionSnapshot, WorkspaceSyncChange, WorkspaceSyncDocumentReviewCommentSnapshot, WorkspaceSyncDocumentReviewSessionSnapshot, WorkspaceSyncInitiativeSnapshot, WorkspaceSyncTaskEventSnapshot, 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
|
+
import type { AiProfileSeatScope } from '../shared/aiProfileSeatScope.js';
|
|
5
6
|
type SyncCore = TaskforceCore & TaskforceSyncCapable;
|
|
6
7
|
type SyncRouteError = Error & {
|
|
7
8
|
code?: string;
|
|
@@ -18,6 +19,11 @@ type NormalizeIncomingAiProfileSurfaceType = (value: unknown, context: {
|
|
|
18
19
|
profileId?: string;
|
|
19
20
|
workspaceId?: string;
|
|
20
21
|
}) => AiProfileSurfaceType | null;
|
|
22
|
+
type NormalizeIncomingAiProfileSeatScope = (value: unknown, context: {
|
|
23
|
+
source: string;
|
|
24
|
+
profileId?: string;
|
|
25
|
+
workspaceId?: string;
|
|
26
|
+
}) => AiProfileSeatScope | null;
|
|
21
27
|
type NormalizeTaskEventSyncPayload = (raw: unknown, workspaceId: string, sourceWatermark?: string) => WorkspaceSyncTaskEventSnapshot | null;
|
|
22
28
|
export interface WorkspaceSyncIncomingTaskMutation {
|
|
23
29
|
archived: boolean;
|
|
@@ -68,6 +74,7 @@ export declare function classifyWorkspaceSyncChanges(input: {
|
|
|
68
74
|
normalizeWorkspaceSyncDocumentPath: (rawPath: unknown) => string | null;
|
|
69
75
|
normalizeTaskAssetPath: (rawPath: unknown) => string | null;
|
|
70
76
|
normalizeIncomingAiProfileSurfaceType: NormalizeIncomingAiProfileSurfaceType;
|
|
77
|
+
normalizeIncomingAiProfileSeatScope: NormalizeIncomingAiProfileSeatScope;
|
|
71
78
|
normalizeTaskEventSyncPayload: NormalizeTaskEventSyncPayload;
|
|
72
79
|
recordSyncDiagnostic: RecordSyncDiagnostic;
|
|
73
80
|
}): WorkspaceSyncIncomingApplyModel;
|
|
@@ -83,7 +83,7 @@ function recordTaskReferenceNumberMismatch(input) {
|
|
|
83
83
|
return true;
|
|
84
84
|
}
|
|
85
85
|
export function classifyWorkspaceSyncChanges(input) {
|
|
86
|
-
const { workspaceId, incomingChanges, normalizeWorkspaceSyncDocumentPath, normalizeTaskAssetPath, normalizeIncomingAiProfileSurfaceType, normalizeTaskEventSyncPayload, recordSyncDiagnostic, } = input;
|
|
86
|
+
const { workspaceId, incomingChanges, normalizeWorkspaceSyncDocumentPath, normalizeTaskAssetPath, normalizeIncomingAiProfileSurfaceType, normalizeIncomingAiProfileSeatScope, normalizeTaskEventSyncPayload, recordSyncDiagnostic, } = input;
|
|
87
87
|
const model = createIncomingApplyModel();
|
|
88
88
|
for (const change of incomingChanges) {
|
|
89
89
|
const raw = change;
|
|
@@ -111,7 +111,7 @@ export function classifyWorkspaceSyncChanges(input) {
|
|
|
111
111
|
continue;
|
|
112
112
|
}
|
|
113
113
|
if (op === 'ai-profile-upsert') {
|
|
114
|
-
const profile = normalizeAiProfileSyncPayload(raw.profile, workspaceId, normalizeIncomingAiProfileSurfaceType, 'apply.aiProfileUpsert');
|
|
114
|
+
const profile = normalizeAiProfileSyncPayload(raw.profile, workspaceId, normalizeIncomingAiProfileSurfaceType, normalizeIncomingAiProfileSeatScope, 'apply.aiProfileUpsert');
|
|
115
115
|
const profileId = String(profile?.id || '').trim();
|
|
116
116
|
if (profileId && profile) {
|
|
117
117
|
const profileWorkspaceId = String(profile.workspaceId || '').trim();
|
|
@@ -382,12 +382,20 @@ export function applyAiProfileAndMemberSyncChanges(input) {
|
|
|
382
382
|
username: profile.username,
|
|
383
383
|
icon: profile.icon,
|
|
384
384
|
color: profile.color,
|
|
385
|
+
...(Object.prototype.hasOwnProperty.call(profile, 'avatarUrl') ? { avatarUrl: profile.avatarUrl ?? null } : {}),
|
|
386
|
+
...(Object.prototype.hasOwnProperty.call(profile, 'avatarSourceUrl') ? { avatarSourceUrl: profile.avatarSourceUrl ?? null } : {}),
|
|
385
387
|
description: profile.description ?? null,
|
|
386
388
|
role: profile.role ?? null,
|
|
387
389
|
provider: profile.provider ?? null,
|
|
388
390
|
model: profile.model ?? null,
|
|
389
391
|
surfaceType: profile.surfaceType,
|
|
392
|
+
seatScope: profile.seatScope ?? null,
|
|
390
393
|
providerMetadata: profile.providerMetadata,
|
|
394
|
+
archivedAt: profile.archivedAt ?? null,
|
|
395
|
+
archivedBy: profile.archivedBy ?? null,
|
|
396
|
+
archivedReason: profile.archivedReason ?? null,
|
|
397
|
+
mergedIntoProfileId: profile.mergedIntoProfileId ?? null,
|
|
398
|
+
rosterStateUpdatedAt: profile.rosterStateUpdatedAt ?? null,
|
|
391
399
|
createdAt: profile.createdAt,
|
|
392
400
|
updatedAt: profile.updatedAt,
|
|
393
401
|
lastActiveAt: profile.lastActiveAt
|
|
@@ -434,14 +442,18 @@ export async function applyContentSyncChanges(input) {
|
|
|
434
442
|
for (const doc of incomingDocuments) {
|
|
435
443
|
try {
|
|
436
444
|
const existingDoc = core.getDocumentWatermark(workspaceId, doc.path);
|
|
437
|
-
const existingDocFallback =
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
? createHash('sha256').update(fs.readFileSync(localDocumentPath, 'utf8')).digest('hex')
|
|
441
|
-
: '';
|
|
445
|
+
const existingDocFallback = (!existingDoc && getExistingDocument)
|
|
446
|
+
? getExistingDocument(doc.path, workspaceId)
|
|
447
|
+
: null;
|
|
442
448
|
const incomingHash = createHash('sha256').update(String(doc.content || '')).digest('hex');
|
|
443
449
|
const existingUpdatedAt = String(existingDoc?.updatedAt || existingDocFallback?.updatedAt || '').trim();
|
|
444
|
-
|
|
450
|
+
let existingHash = String(existingDoc?.contentHash || existingDocFallback?.contentHash || '').trim();
|
|
451
|
+
if (!existingHash && !hasObjectStorageClient()) {
|
|
452
|
+
const localDocumentPath = path.resolve(basePath, doc.path);
|
|
453
|
+
if (fs.existsSync(localDocumentPath)) {
|
|
454
|
+
existingHash = createHash('sha256').update(fs.readFileSync(localDocumentPath, 'utf8')).digest('hex');
|
|
455
|
+
}
|
|
456
|
+
}
|
|
445
457
|
const compare = resolveSyncConflictOrdering(doc.updatedAt, existingUpdatedAt, incomingHash, existingHash);
|
|
446
458
|
const metadataDrift = hasDocumentSyncMetadataDrift(workspaceId, doc, options);
|
|
447
459
|
if (compare < 0)
|
|
@@ -450,7 +462,9 @@ export async function applyContentSyncChanges(input) {
|
|
|
450
462
|
continue;
|
|
451
463
|
await writeWorkspaceSyncDocument(basePath, doc, workspaceId, {
|
|
452
464
|
...options,
|
|
453
|
-
allowReferenceNumberReassignment: options?.allowReferenceNumberReassignment === true
|
|
465
|
+
allowReferenceNumberReassignment: options?.allowReferenceNumberReassignment === true
|
|
466
|
+
|| existingHash !== incomingHash
|
|
467
|
+
|| metadataDrift
|
|
454
468
|
});
|
|
455
469
|
}
|
|
456
470
|
catch (error) {
|
|
@@ -481,11 +495,11 @@ export async function applyContentSyncChanges(input) {
|
|
|
481
495
|
const existing = getExistingBinaryAsset(asset.path, workspaceId);
|
|
482
496
|
const incomingHash = createHash('sha256').update(Buffer.from(String(asset.contentBase64 || ''), 'base64')).digest('hex');
|
|
483
497
|
const localFileMissing = !hasObjectStorageClient() && !fs.existsSync(localAssetPath);
|
|
484
|
-
const localAssetHash = !hasObjectStorageClient() && !localFileMissing
|
|
485
|
-
? createHash('sha256').update(fs.readFileSync(localAssetPath)).digest('hex')
|
|
486
|
-
: '';
|
|
487
498
|
const existingUpdatedAt = String(existing?.updatedAt || '').trim();
|
|
488
|
-
|
|
499
|
+
let existingHash = String(existing?.contentSha256 || '').trim();
|
|
500
|
+
if (!existingHash && !hasObjectStorageClient() && !localFileMissing) {
|
|
501
|
+
existingHash = createHash('sha256').update(fs.readFileSync(localAssetPath)).digest('hex');
|
|
502
|
+
}
|
|
489
503
|
const compare = resolveSyncConflictOrdering(asset.updatedAt, existingUpdatedAt, incomingHash, existingHash);
|
|
490
504
|
const metadataDrift = hasBinaryAssetSyncMetadataDrift(workspaceId, asset, options);
|
|
491
505
|
if (compare < 0)
|
|
@@ -497,6 +511,7 @@ export async function applyContentSyncChanges(input) {
|
|
|
497
511
|
allowReferenceNumberReassignment: options?.allowReferenceNumberReassignment === true
|
|
498
512
|
|| existingHash !== incomingHash
|
|
499
513
|
|| localFileMissing
|
|
514
|
+
|| metadataDrift
|
|
500
515
|
});
|
|
501
516
|
}
|
|
502
517
|
catch (error) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { describe, expect, it, vi, afterEach } from 'vitest';
|
|
2
4
|
import { applyCollaborationSyncChanges, applyContentSyncChanges, applyPreparedTaskSyncSnapshot, applyTaskSyncDeletes, classifyWorkspaceSyncChanges, prepareTaskSyncSnapshotApply, } from './syncApplyHandlers.js';
|
|
3
5
|
function createCoreFixture(overrides = {}) {
|
|
4
6
|
return {
|
|
@@ -29,6 +31,9 @@ function createCoreFixture(overrides = {}) {
|
|
|
29
31
|
};
|
|
30
32
|
}
|
|
31
33
|
describe('syncApplyHandlers', () => {
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
vi.restoreAllMocks();
|
|
36
|
+
});
|
|
32
37
|
it('classifies task and workstream changes, then prepares and applies the task snapshot pipeline', () => {
|
|
33
38
|
const diagnostics = [];
|
|
34
39
|
const workspaceId = 'workspace-1';
|
|
@@ -87,6 +92,7 @@ describe('syncApplyHandlers', () => {
|
|
|
87
92
|
normalizeWorkspaceSyncDocumentPath: (rawPath) => typeof rawPath === 'string' ? rawPath : null,
|
|
88
93
|
normalizeTaskAssetPath: (rawPath) => typeof rawPath === 'string' ? rawPath : null,
|
|
89
94
|
normalizeIncomingAiProfileSurfaceType: () => null,
|
|
95
|
+
normalizeIncomingAiProfileSeatScope: () => null,
|
|
90
96
|
normalizeTaskEventSyncPayload: () => null,
|
|
91
97
|
recordSyncDiagnostic: (targetWorkspaceId, eventType, message, details) => {
|
|
92
98
|
diagnostics.push({ workspaceId: targetWorkspaceId, eventType, message, details });
|
|
@@ -231,7 +237,7 @@ describe('syncApplyHandlers', () => {
|
|
|
231
237
|
expect(core.notifyDocumentMetadataMutation).toHaveBeenNthCalledWith(1, 'workspace-1', ['docs/spec.md', 'assets/proof.png'], 'upsert');
|
|
232
238
|
expect(core.notifyDocumentMetadataMutation).toHaveBeenNthCalledWith(2, 'workspace-1', ['docs/stale.md', 'assets/stale.png'], 'delete');
|
|
233
239
|
});
|
|
234
|
-
it('
|
|
240
|
+
it('allows reference reassignment when metadata drift requires normal sync convergence', async () => {
|
|
235
241
|
const core = createCoreFixture({
|
|
236
242
|
getDocumentWatermark: vi.fn().mockReturnValue(null),
|
|
237
243
|
});
|
|
@@ -280,9 +286,70 @@ describe('syncApplyHandlers', () => {
|
|
|
280
286
|
}),
|
|
281
287
|
recordSyncDiagnostic: vi.fn(),
|
|
282
288
|
});
|
|
283
|
-
expect(writeWorkspaceSyncDocument).toHaveBeenCalledWith('/tmp/taskforce-sync-content', expect.objectContaining({ path: 'docs/spec.md' }), 'workspace-1', { allowReferenceNumberReassignment:
|
|
289
|
+
expect(writeWorkspaceSyncDocument).toHaveBeenCalledWith('/tmp/taskforce-sync-content', expect.objectContaining({ path: 'docs/spec.md' }), 'workspace-1', { allowReferenceNumberReassignment: true });
|
|
284
290
|
expect(writeWorkspaceSyncBinaryAsset).toHaveBeenCalledWith('/tmp/taskforce-sync-content', expect.objectContaining({ path: 'assets/proof.png' }), 'workspace-1', { allowReferenceNumberReassignment: true });
|
|
285
291
|
});
|
|
292
|
+
it('reuses persisted content hashes instead of re-reading unchanged local files', async () => {
|
|
293
|
+
const existingDocContent = '# Spec';
|
|
294
|
+
const existingAssetBytes = Buffer.from('png-bytes');
|
|
295
|
+
const core = createCoreFixture({
|
|
296
|
+
getDocumentWatermark: vi.fn().mockReturnValue({
|
|
297
|
+
updatedAt: '2026-04-10T10:00:00.000Z',
|
|
298
|
+
contentHash: createHash('sha256').update(existingDocContent).digest('hex'),
|
|
299
|
+
}),
|
|
300
|
+
});
|
|
301
|
+
const writeWorkspaceSyncDocument = vi.fn().mockResolvedValue(undefined);
|
|
302
|
+
const writeWorkspaceSyncBinaryAsset = vi.fn().mockResolvedValue(undefined);
|
|
303
|
+
const readSpy = vi.spyOn(fs, 'readFileSync').mockImplementation(((targetPath) => {
|
|
304
|
+
const value = String(targetPath);
|
|
305
|
+
if (value.endsWith('spec.md'))
|
|
306
|
+
return existingDocContent;
|
|
307
|
+
return existingAssetBytes;
|
|
308
|
+
}));
|
|
309
|
+
await applyContentSyncChanges({
|
|
310
|
+
core,
|
|
311
|
+
workspaceId: 'workspace-1',
|
|
312
|
+
incomingDocuments: [
|
|
313
|
+
{
|
|
314
|
+
path: 'docs/spec.md',
|
|
315
|
+
updatedAt: '2026-04-10T10:00:00.000Z',
|
|
316
|
+
content: existingDocContent,
|
|
317
|
+
assetId: 'asset-doc-1',
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
incomingDocumentDeletes: new Map(),
|
|
321
|
+
incomingAssets: [
|
|
322
|
+
{
|
|
323
|
+
path: 'assets/proof.png',
|
|
324
|
+
updatedAt: '2026-04-10T10:05:00.000Z',
|
|
325
|
+
contentBase64: existingAssetBytes.toString('base64'),
|
|
326
|
+
assetId: 'asset-image-1',
|
|
327
|
+
kind: 'image',
|
|
328
|
+
mimeType: 'image/png',
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
incomingAssetDeletes: new Map(),
|
|
332
|
+
basePath: '/tmp/taskforce-sync-content',
|
|
333
|
+
hasObjectStorageClient: () => true,
|
|
334
|
+
hasDocumentSyncMetadataDrift: () => false,
|
|
335
|
+
hasBinaryAssetSyncMetadataDrift: () => false,
|
|
336
|
+
getExistingDocument: () => {
|
|
337
|
+
throw new Error('document fallback should not be used when persisted metadata exists');
|
|
338
|
+
},
|
|
339
|
+
writeWorkspaceSyncDocument,
|
|
340
|
+
deleteWorkspaceSyncDocument: vi.fn(),
|
|
341
|
+
writeWorkspaceSyncBinaryAsset,
|
|
342
|
+
deleteWorkspaceSyncBinaryAsset: vi.fn(),
|
|
343
|
+
getExistingBinaryAsset: () => ({
|
|
344
|
+
updatedAt: '2026-04-10T10:05:00.000Z',
|
|
345
|
+
contentSha256: createHash('sha256').update(existingAssetBytes).digest('hex'),
|
|
346
|
+
}),
|
|
347
|
+
recordSyncDiagnostic: vi.fn(),
|
|
348
|
+
});
|
|
349
|
+
expect(readSpy).not.toHaveBeenCalled();
|
|
350
|
+
expect(writeWorkspaceSyncDocument).not.toHaveBeenCalled();
|
|
351
|
+
expect(writeWorkspaceSyncBinaryAsset).not.toHaveBeenCalled();
|
|
352
|
+
});
|
|
286
353
|
it('applies collaboration upserts and deletes through the collaboration handlers', () => {
|
|
287
354
|
const core = createCoreFixture();
|
|
288
355
|
applyCollaborationSyncChanges({
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function hasOwnSyncField(source: unknown, field: string): boolean;
|
|
2
|
+
export declare function normalizeSyncOptionalString(value: unknown): string | undefined;
|
|
3
|
+
export declare function normalizeSyncNullableString(value: unknown): string | null;
|
|
4
|
+
export declare function normalizeSyncOptionalId(value: unknown): string | undefined;
|
|
5
|
+
export declare function normalizeSyncNullableId(value: unknown): string | null;
|
|
6
|
+
export declare function normalizeSyncNullableIsoString(value: unknown): string | null;
|
|
7
|
+
export declare function normalizeSyncSameOriginAssetUrl(value: unknown): string | null;
|
|
8
|
+
export declare function readSyncOptionalStringField(source: Record<string, unknown>, field: string): string | undefined;
|
|
9
|
+
export declare function readSyncNullableStringField(source: Record<string, unknown>, field: string): string | null | undefined;
|
|
10
|
+
export declare function readSyncNullableIdField(source: Record<string, unknown>, field: string): string | null | undefined;
|
|
11
|
+
export declare function readSyncNullableIsoStringField(source: Record<string, unknown>, field: string): string | null | undefined;
|
|
12
|
+
export declare function readSyncSameOriginAssetUrlField(source: Record<string, unknown>, field: string): string | null | undefined;
|