@taskforcehq/taskforce 0.3.304 → 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/README.md +140 -247
- package/dist/Taskforce.module.css +295 -89
- package/dist/TaskforceCore.js +378 -111
- package/dist/TaskforceCore.test.js +1221 -107
- 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 +52 -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/DocumentIndex.d.ts +1 -1
- package/dist/components/features/DocumentIndex.js +1 -1
- package/dist/components/features/DocumentViewer.d.ts +6 -2
- package/dist/components/features/DocumentViewer.js +90 -10
- package/dist/components/features/DocumentWorkspace.d.ts +5 -22
- package/dist/components/features/DocumentWorkspace.js +12 -155
- package/dist/components/features/DocumentWorkspace.test.js +226 -6
- package/dist/components/features/TaskSettings.js +29 -3
- package/dist/components/features/TaskSettings.test.js +42 -1
- 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/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.d.ts +8 -8
- package/dist/components/task/TaskForm.js +362 -230
- package/dist/components/task/TaskForm.test.js +558 -381
- package/dist/components/task/TaskKanban.d.ts +2 -1
- package/dist/components/task/TaskKanban.js +96 -14
- 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 -17
- package/dist/components/views/PlanComparisonPage.test.js +289 -41
- package/dist/components/views/PlansPage.js +239 -106
- package/dist/components/views/PlansPage.test.js +593 -42
- package/dist/components/views/StandaloneLayout.js +310 -554
- package/dist/components/views/panels/FilterToolbar.test.js +6 -4
- 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 +27 -14
- package/dist/components/views/standalone/modals/AccountHubModal.js +87 -15
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +42 -2
- 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/config/envSchema.js +1 -1
- package/dist/core/AiProfileService.d.ts +45 -3
- package/dist/core/AiProfileService.js +774 -63
- package/dist/core/AiProfileService.test.js +22 -2
- package/dist/core/AiProfiles.test.js +761 -2
- 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.d.ts +1 -0
- package/dist/core/AuthTokenService.test.js +78 -0
- package/dist/core/EntitlementsPolicy.test.js +77 -15
- package/dist/core/GlobalSettingsService.js +115 -6
- package/dist/core/PlanEntitlementService.d.ts +30 -3
- package/dist/core/PlanEntitlementService.js +454 -93
- package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
- package/dist/core/PlanFeatureCatalog.test.js +84 -0
- package/dist/core/PlanVersionPolicy.test.js +54 -28
- package/dist/core/PlanningEntities.test.js +52 -0
- package/dist/core/SignupMonetizationSettings.test.js +241 -44
- package/dist/core/SyncReconciliationService.js +2 -65
- package/dist/core/SystemAdmin.test.js +244 -88
- package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
- package/dist/core/TaskTaxonomyValidation.test.js +53 -0
- package/dist/core/Taskforce.d.ts +228 -14
- package/dist/core/Taskforce.js +814 -201
- 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 +128 -27
- package/dist/core/shared.d.ts +3 -1
- package/dist/core/shared.js +16 -1
- package/dist/core/types.d.ts +68 -2
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -1
- package/dist/hooks/sync/auth.js +3 -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 +16 -3
- package/dist/hooks/sync/transfers.js +186 -65
- 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 +16 -6
- package/dist/hooks/useSyncOrchestrator.js +424 -175
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +487 -10
- package/dist/hooks/useTaskData.js +8 -3
- package/dist/hooks/useTaskData.test.js +45 -0
- package/dist/hooks/useTaskMutations.d.ts +2 -1
- package/dist/hooks/useTaskMutations.js +12 -1
- package/dist/hooks/useTaskMutations.test.js +30 -1
- package/dist/hooks/useTaskforce.d.ts +65 -4
- package/dist/hooks/useTaskforce.js +564 -243
- 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 +2454 -208
- package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
- package/dist/hooks/useWorkspaceSyncController.js +17 -2
- package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
- 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/adminWorkspaceRegistrar.d.ts +2 -0
- package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
- 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/runtime.d.ts +2 -0
- package/dist/mcp/runtime.js +2433 -3534
- package/dist/mcp/runtime.test.js +440 -2
- 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/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 +4 -5
- package/dist/server/routes/admin.js +410 -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 +16 -6
- package/dist/server/routes/auth.js +1015 -118
- 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 +276 -34
- package/dist/server/routes/billing.test.js +760 -52
- package/dist/server/routes/documentReviews.d.ts +1 -1
- package/dist/server/routes/documentReviews.js +1 -1
- package/dist/server/routes/documents.d.ts +5 -2
- package/dist/server/routes/documents.js +242 -74
- 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 +29 -3
- package/dist/server/routes/shared.js +69 -4
- package/dist/server/routes/sync.d.ts +1 -1
- package/dist/server/routes/sync.integration.test.js +437 -39
- package/dist/server/routes/sync.js +62 -606
- 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.d.ts +1 -1
- package/dist/server/routes/tasks.js +44 -2
- package/dist/server/routes/workspaces.d.ts +1 -1
- package/dist/server/routes/workspaces.js +37 -6
- package/dist/server/routes.d.ts +2 -9
- package/dist/server/routes.js +136 -165
- package/dist/server/routes.test.js +2457 -162
- 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 +33 -77
- package/dist/sync/syncApplyHandlers.d.ts +7 -0
- package/dist/sync/syncApplyHandlers.js +28 -13
- package/dist/sync/syncApplyHandlers.test.js +72 -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 +6 -0
- package/dist/sync/syncService.js +60 -0
- package/dist/sync/syncService.test.js +71 -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 +48 -0
- package/dist/sync/workspaceSyncModel.js +195 -31
- package/dist/sync/workspaceSyncModel.test.js +325 -12
- package/dist/sync/workspaceSyncState.d.ts +19 -0
- package/dist/sync/workspaceSyncState.js +3 -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-BlS-cqnl.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-BjR6iBf9.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-DZX4jk7e.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-DnFqdUME.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-I7ZwG1z_.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.d.ts +19 -0
- package/dist/utils/commercialLifecyclePresentation.js +199 -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 +10 -2
- package/scripts/check-cloud-mcp.mjs +83 -0
- package/scripts/export-sqlite-to-postgres.mjs +7 -9
- package/scripts/playwright-auth.sh +5 -1
- package/scripts/run-billing-performance-smoke.sh +24 -0
- 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-BLWVbuKP.js +0 -1
- package/dist/ui/assets/DocumentWorkspace-BH_6DF6x.js +0 -250
- package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
- package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
- package/dist/ui/assets/PlansPage-CSDQ4sLa.js +0 -1
- package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
- package/dist/ui/assets/TaskSettings-CEDy34Jo.js +0 -9
- package/dist/ui/assets/index-DXUdtH1B.js +0 -6
- package/dist/ui/assets/index-Ii0B0rTO.css +0 -1
- package/scripts/migrate-planning-model.ts +0 -233
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { TaskforceCore } from './Taskforce';
|
|
6
|
+
const createdRoots = [];
|
|
7
|
+
const originalHome = process.env.HOME;
|
|
8
|
+
function createProjectRoot(prefix = 'taskforce-list-tasks-slim-') {
|
|
9
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
10
|
+
process.env.HOME = root;
|
|
11
|
+
createdRoots.push(root);
|
|
12
|
+
return root;
|
|
13
|
+
}
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
process.env.HOME = originalHome;
|
|
16
|
+
while (createdRoots.length > 0) {
|
|
17
|
+
const root = createdRoots.pop();
|
|
18
|
+
if (root && fs.existsSync(root)) {
|
|
19
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
describe('TaskforceCore.listTasksSlim', () => {
|
|
24
|
+
it('does not hydrate comments, task events, attachments, or checklist items', () => {
|
|
25
|
+
const projectRoot = createProjectRoot();
|
|
26
|
+
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
27
|
+
const task = core.createTask({ title: 'Heavy task' }, { workspaceId: 'default' });
|
|
28
|
+
core.addComment(task.id, 'note from reviewer', 'user', 'default');
|
|
29
|
+
core.replaceChecklistItems(task.id, [{ title: 'step one', isCompleted: false }], 'default');
|
|
30
|
+
const slim = core.listTasksSlim('default');
|
|
31
|
+
const entry = slim.find((t) => t.id === task.id);
|
|
32
|
+
expect(entry).toBeDefined();
|
|
33
|
+
expect(entry).not.toHaveProperty('comments');
|
|
34
|
+
expect(entry).not.toHaveProperty('taskEvents');
|
|
35
|
+
expect(entry).not.toHaveProperty('activity');
|
|
36
|
+
expect(entry).not.toHaveProperty('attachments');
|
|
37
|
+
expect(entry).not.toHaveProperty('checklistItems');
|
|
38
|
+
expect(entry).not.toHaveProperty('description');
|
|
39
|
+
expect(entry).not.toHaveProperty('createdByActor');
|
|
40
|
+
expect(entry).not.toHaveProperty('assigneeActor');
|
|
41
|
+
expect(entry).toMatchObject({
|
|
42
|
+
id: task.id,
|
|
43
|
+
title: 'Heavy task',
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
it('pushes assignee, status, openOnly, type, and priority filters into the query', () => {
|
|
47
|
+
const projectRoot = createProjectRoot();
|
|
48
|
+
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
49
|
+
const mine = core.createTask({ title: 'Mine open', status: 'in-progress', assignee: 'user-a', priority: 3, type: 'issue' }, { workspaceId: 'default' });
|
|
50
|
+
core.createTask({ title: 'Other assigned', status: 'task', assignee: 'user-b', priority: 3 }, { workspaceId: 'default' });
|
|
51
|
+
const mineDone = core.createTask({ title: 'Mine done', status: 'done', assignee: 'user-a', priority: 1 }, { workspaceId: 'default' });
|
|
52
|
+
core.createTask({ title: 'Mine unassigned' }, { workspaceId: 'default' });
|
|
53
|
+
const byAssignee = core.listTasksSlim('default', { assignee: 'user-a' }).map((t) => t.id);
|
|
54
|
+
expect(byAssignee).toEqual(expect.arrayContaining([mine.id, mineDone.id]));
|
|
55
|
+
expect(byAssignee).toHaveLength(2);
|
|
56
|
+
const openOnly = core.listTasksSlim('default', { assignee: 'user-a', openOnly: true }).map((t) => t.id);
|
|
57
|
+
expect(openOnly).toEqual([mine.id]);
|
|
58
|
+
const byStatus = core.listTasksSlim('default', { status: 'done' }).map((t) => t.id);
|
|
59
|
+
expect(byStatus).toEqual([mineDone.id]);
|
|
60
|
+
const byType = core.listTasksSlim('default', { type: 'issue' }).map((t) => t.id);
|
|
61
|
+
expect(byType).toEqual([mine.id]);
|
|
62
|
+
const byPriority = core.listTasksSlim('default', { priority: 3 }).map((t) => t.id).sort();
|
|
63
|
+
expect(byPriority).toHaveLength(2);
|
|
64
|
+
const byCategory = core.listTasksSlim('default', { category: 'default' }).map((t) => t.id);
|
|
65
|
+
expect(byCategory).toHaveLength(4);
|
|
66
|
+
});
|
|
67
|
+
it('matches unassigned filter against null, empty-string, and "unassigned" values', () => {
|
|
68
|
+
const projectRoot = createProjectRoot();
|
|
69
|
+
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
70
|
+
const unassigned = core.createTask({ title: 'no one' }, { workspaceId: 'default' });
|
|
71
|
+
const assigned = core.createTask({ title: 'someone', assignee: 'user-x' }, { workspaceId: 'default' });
|
|
72
|
+
const db = core.getDatabaseAdapter();
|
|
73
|
+
// Inject a row with assignee stored as empty string to exercise the fallback branch.
|
|
74
|
+
db.prepare(`UPDATE tasks SET assignee = '' WHERE id = ?`).run(unassigned.id);
|
|
75
|
+
const results = core.listTasksSlim('default', { assignee: 'unassigned' }).map((t) => t.id);
|
|
76
|
+
expect(results).toContain(unassigned.id);
|
|
77
|
+
expect(results).not.toContain(assigned.id);
|
|
78
|
+
});
|
|
79
|
+
it('applies sort, order, and limit in SQL', () => {
|
|
80
|
+
const projectRoot = createProjectRoot();
|
|
81
|
+
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
82
|
+
const low = core.createTask({ title: 'low', priority: 1 }, { workspaceId: 'default' });
|
|
83
|
+
const mid = core.createTask({ title: 'mid', priority: 2 }, { workspaceId: 'default' });
|
|
84
|
+
const high = core.createTask({ title: 'high', priority: 4 }, { workspaceId: 'default' });
|
|
85
|
+
const byPriorityDesc = core.listTasksSlim('default', { sort: 'priority', order: 'desc' }).map((t) => t.id);
|
|
86
|
+
expect(byPriorityDesc.slice(0, 3)).toEqual([high.id, mid.id, low.id]);
|
|
87
|
+
const limited = core.listTasksSlim('default', { sort: 'priority', order: 'desc', limit: 1 });
|
|
88
|
+
expect(limited).toHaveLength(1);
|
|
89
|
+
expect(limited[0]?.id).toBe(high.id);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -24,7 +24,7 @@ function seedPlans(core) {
|
|
|
24
24
|
billing: {
|
|
25
25
|
onboardingPolicy: {
|
|
26
26
|
mode: 'auto_grant_free',
|
|
27
|
-
|
|
27
|
+
planId: 'starter'
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
});
|
|
@@ -44,7 +44,7 @@ describe('TaskforceCore user profile avatar drafts', () => {
|
|
|
44
44
|
try {
|
|
45
45
|
const created = core.registerPasswordUser({
|
|
46
46
|
email: 'avatar-user@example.com',
|
|
47
|
-
password: '
|
|
47
|
+
password: 'ValidPassphrase123!'
|
|
48
48
|
});
|
|
49
49
|
const updated = core.updateUserProfile(created.userId, {
|
|
50
50
|
displayName: 'Avatar User',
|
|
@@ -62,13 +62,40 @@ describe('TaskforceCore user profile avatar drafts', () => {
|
|
|
62
62
|
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
63
63
|
}
|
|
64
64
|
});
|
|
65
|
+
it('exposes cloud-owned user avatarUrl on live workspace member records', () => {
|
|
66
|
+
const projectRoot = makeProjectRoot();
|
|
67
|
+
const core = openCore(projectRoot);
|
|
68
|
+
try {
|
|
69
|
+
core.createWorkspace({ workspaceId: 'workspace-1', name: 'Workspace 1' });
|
|
70
|
+
const created = core.registerPasswordUser({
|
|
71
|
+
email: 'member-avatar@example.com',
|
|
72
|
+
password: 'ValidPassphrase123!',
|
|
73
|
+
workspaceId: 'workspace-1'
|
|
74
|
+
});
|
|
75
|
+
const avatarUrl = `/api/taskforce/context/${encodeURIComponent(`users/${created.userId}/profile/avatar/headshot.webp`)}`;
|
|
76
|
+
core.updateUserProfile(created.userId, {
|
|
77
|
+
displayName: 'Member Avatar',
|
|
78
|
+
avatarUrl
|
|
79
|
+
});
|
|
80
|
+
const members = core.listWorkspaceUsers('workspace-1');
|
|
81
|
+
expect(members.find((member) => member.userId === created.userId)).toEqual(expect.objectContaining({
|
|
82
|
+
userId: created.userId,
|
|
83
|
+
displayName: 'Member Avatar',
|
|
84
|
+
avatarUrl
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
core.close();
|
|
89
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
65
92
|
it('tracks, finalizes, discards, and cleans up stale avatar upload drafts', () => {
|
|
66
93
|
const projectRoot = makeProjectRoot();
|
|
67
94
|
const core = openCore(projectRoot);
|
|
68
95
|
try {
|
|
69
96
|
const created = core.registerPasswordUser({
|
|
70
97
|
email: 'draft-user@example.com',
|
|
71
|
-
password: '
|
|
98
|
+
password: 'ValidPassphrase123!'
|
|
72
99
|
});
|
|
73
100
|
const draft = core.createUserAvatarUploadDraft({
|
|
74
101
|
userId: created.userId,
|
|
@@ -55,11 +55,6 @@ export function deleteWorkspaceService(deps, input) {
|
|
|
55
55
|
const result = deps.db.prepare(sql).run(...params);
|
|
56
56
|
counts[tableName] = Number(result?.changes || 0);
|
|
57
57
|
};
|
|
58
|
-
deps.db.prepare(`
|
|
59
|
-
UPDATE tasks
|
|
60
|
-
SET parent_task_id = NULL
|
|
61
|
-
WHERE tenant_id = ? AND workspace_id = ? AND parent_task_id IS NOT NULL
|
|
62
|
-
`).run(deps.tenantId, normalizedWorkspaceId);
|
|
63
58
|
runDelete('comments', 'DELETE FROM comments WHERE tenant_id = ? AND workspace_id = ?', deps.tenantId, normalizedWorkspaceId);
|
|
64
59
|
runDelete('task_events', 'DELETE FROM task_events WHERE tenant_id = ? AND workspace_id = ?', deps.tenantId, normalizedWorkspaceId);
|
|
65
60
|
runDelete('deleted_tasks', 'DELETE FROM deleted_tasks WHERE tenant_id = ? AND workspace_id = ?', deps.tenantId, normalizedWorkspaceId);
|
|
@@ -124,10 +124,11 @@ describe('TaskforceCore workspace permission invariants', () => {
|
|
|
124
124
|
cleanup();
|
|
125
125
|
}
|
|
126
126
|
});
|
|
127
|
-
it('marks deactivated
|
|
127
|
+
it('marks deactivated workspace memberships as disabled without disabling the global account', () => {
|
|
128
128
|
const { core, cleanup } = makeCore();
|
|
129
129
|
try {
|
|
130
130
|
core.createWorkspace({ workspaceId: 'workspace-1', name: 'Workspace 1' });
|
|
131
|
+
core.createWorkspace({ workspaceId: 'workspace-home', name: 'Workspace Home' });
|
|
131
132
|
core.ensureBootstrapUserAccess({
|
|
132
133
|
userId: 'owner-1',
|
|
133
134
|
workspaceId: 'workspace-1',
|
|
@@ -140,9 +141,32 @@ describe('TaskforceCore workspace permission invariants', () => {
|
|
|
140
141
|
role: 'member',
|
|
141
142
|
email: 'member@example.com'
|
|
142
143
|
});
|
|
144
|
+
core.ensureBootstrapUserAccess({
|
|
145
|
+
userId: 'member-1',
|
|
146
|
+
workspaceId: 'workspace-home',
|
|
147
|
+
role: 'owner',
|
|
148
|
+
email: 'member@example.com'
|
|
149
|
+
});
|
|
143
150
|
expect(core.setWorkspaceUserDisabled('member-1', 'workspace-1', true)).toBe(true);
|
|
144
151
|
const access = core.resolveUserAccess('member-1', 'workspace-1', 'member');
|
|
145
152
|
expect(access?.disabled).toBe(true);
|
|
153
|
+
expect(access?.accountDisabled).toBe(false);
|
|
154
|
+
expect(access?.workspaceDisabled).toBe(true);
|
|
155
|
+
expect(core.resolveUserAccess('member-1', 'workspace-home', 'owner')).toEqual(expect.objectContaining({
|
|
156
|
+
role: 'owner',
|
|
157
|
+
disabled: false,
|
|
158
|
+
accountDisabled: false,
|
|
159
|
+
workspaceDisabled: false
|
|
160
|
+
}));
|
|
161
|
+
expect(core.listUserWorkspaces('member-1').map((workspace) => workspace.id)).toEqual(['workspace-home']);
|
|
162
|
+
expect(core.listWorkspaceUsers('workspace-1').find((user) => user.userId === 'member-1')).toEqual(expect.objectContaining({
|
|
163
|
+
status: 'disabled',
|
|
164
|
+
disabled: true
|
|
165
|
+
}));
|
|
166
|
+
const row = core.db.prepare(`
|
|
167
|
+
SELECT is_disabled FROM users WHERE tenant_id = ? AND id = ? LIMIT 1
|
|
168
|
+
`).get('default', 'member-1');
|
|
169
|
+
expect(Boolean(row?.is_disabled)).toBe(false);
|
|
146
170
|
}
|
|
147
171
|
finally {
|
|
148
172
|
cleanup();
|
|
@@ -20,30 +20,30 @@ function openCore(projectRoot) {
|
|
|
20
20
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
21
21
|
`).run('default', planVersionId, 'month', `free:${planVersionId}:month`, 'free', 1, 0, null, now, now);
|
|
22
22
|
};
|
|
23
|
-
core.upsertPlanCatalog({ planId: '
|
|
24
|
-
addFreeMapping('
|
|
25
|
-
core.upsertPlanCatalog({ planId: '
|
|
23
|
+
core.upsertPlanCatalog({ planId: 'free', displayName: 'Free' });
|
|
24
|
+
addFreeMapping('free-v1');
|
|
25
|
+
core.upsertPlanCatalog({ planId: 'free', displayName: 'Free', status: 'active' });
|
|
26
26
|
core.updatePlanVersion({
|
|
27
|
-
planId: '
|
|
27
|
+
planId: 'free',
|
|
28
28
|
versionNumber: 1,
|
|
29
29
|
seatLimit: 1
|
|
30
30
|
});
|
|
31
31
|
core.updatePlanVersionFeatures({
|
|
32
|
-
planVersionId: '
|
|
32
|
+
planVersionId: 'free-v1',
|
|
33
33
|
features: [
|
|
34
|
-
{ featureKey: 'workspace.
|
|
34
|
+
{ featureKey: 'workspace.workspaces', access: 'enabled' }
|
|
35
35
|
]
|
|
36
36
|
});
|
|
37
|
-
core.upsertPlanCatalog({ planId: '
|
|
38
|
-
addFreeMapping('
|
|
39
|
-
core.upsertPlanCatalog({ planId: '
|
|
37
|
+
core.upsertPlanCatalog({ planId: 'team-5', displayName: 'Team 5' });
|
|
38
|
+
addFreeMapping('team-5-v1');
|
|
39
|
+
core.upsertPlanCatalog({ planId: 'team-5', displayName: 'Team 5', status: 'active' });
|
|
40
40
|
core.updatePlanVersion({
|
|
41
|
-
planId: '
|
|
41
|
+
planId: 'team-5',
|
|
42
42
|
versionNumber: 1,
|
|
43
43
|
seatLimit: 5
|
|
44
44
|
});
|
|
45
45
|
core.updatePlanVersionFeatures({
|
|
46
|
-
planVersionId: '
|
|
46
|
+
planVersionId: 'team-5-v1',
|
|
47
47
|
features: [
|
|
48
48
|
{ featureKey: 'collaboration.invites', access: 'enabled' },
|
|
49
49
|
{ featureKey: 'collaboration.team_management', access: 'enabled' }
|
|
@@ -71,17 +71,17 @@ describe('TaskforceCore account-level plan mode and invite revoke', () => {
|
|
|
71
71
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
72
72
|
`).run('workspace-u1-second', 'default', 'workspace-u1-second', 'Second U1 Workspace', now, now);
|
|
73
73
|
db.prepare(`
|
|
74
|
-
INSERT INTO users (id, tenant_id, email, display_name,
|
|
75
|
-
VALUES (?, ?, ?, ?,
|
|
76
|
-
`).run('u1', 'default', 'u1@example.com', 'U1',
|
|
74
|
+
INSERT INTO users (id, tenant_id, email, display_name, is_disabled, created_at, updated_at)
|
|
75
|
+
VALUES (?, ?, ?, ?, 0, ?, ?)
|
|
76
|
+
`).run('u1', 'default', 'u1@example.com', 'U1', now, now);
|
|
77
77
|
db.prepare(`
|
|
78
|
-
INSERT INTO users (id, tenant_id, email, display_name,
|
|
79
|
-
VALUES (?, ?, ?, ?,
|
|
80
|
-
`).run('u2', 'default', 'u2@example.com', 'U2',
|
|
78
|
+
INSERT INTO users (id, tenant_id, email, display_name, is_disabled, created_at, updated_at)
|
|
79
|
+
VALUES (?, ?, ?, ?, 0, ?, ?)
|
|
80
|
+
`).run('u2', 'default', 'u2@example.com', 'U2', now, now);
|
|
81
81
|
db.prepare(`
|
|
82
|
-
INSERT INTO users (id, tenant_id, email, display_name,
|
|
83
|
-
VALUES (?, ?, ?, ?,
|
|
84
|
-
`).run('u3', 'default', 'u3@example.com', 'U3',
|
|
82
|
+
INSERT INTO users (id, tenant_id, email, display_name, is_disabled, created_at, updated_at)
|
|
83
|
+
VALUES (?, ?, ?, ?, 0, ?, ?)
|
|
84
|
+
`).run('u3', 'default', 'u3@example.com', 'U3', now, now);
|
|
85
85
|
db.prepare(`
|
|
86
86
|
INSERT INTO workspace_memberships (id, tenant_id, workspace_id, user_id, role, status, created_at, updated_at)
|
|
87
87
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
@@ -153,7 +153,7 @@ describe('TaskforceCore account-level plan mode and invite revoke', () => {
|
|
|
153
153
|
});
|
|
154
154
|
core.ensurePasswordUser({
|
|
155
155
|
email: 'existing@example.com',
|
|
156
|
-
password: '
|
|
156
|
+
password: 'HarborLanternOrbit!2026',
|
|
157
157
|
workspaceId: 'workspace-invite',
|
|
158
158
|
role: 'member'
|
|
159
159
|
});
|
|
@@ -190,7 +190,7 @@ describe('TaskforceCore account-level plan mode and invite revoke', () => {
|
|
|
190
190
|
core.setUserAccountPlanMode('owner-user', 'team');
|
|
191
191
|
const existing = core.ensurePasswordUser({
|
|
192
192
|
email: 'existing@example.com',
|
|
193
|
-
password: '
|
|
193
|
+
password: 'HarborLanternOrbit!2026',
|
|
194
194
|
workspaceId: 'workspace-invite',
|
|
195
195
|
role: 'member'
|
|
196
196
|
});
|
|
@@ -202,7 +202,8 @@ describe('TaskforceCore account-level plan mode and invite revoke', () => {
|
|
|
202
202
|
WHERE tenant_id = ? AND user_id = ? AND workspace_id = ?
|
|
203
203
|
`).run('default', existing.userId, 'workspace-invite');
|
|
204
204
|
const before = db.prepare(`
|
|
205
|
-
SELECT password_hash FROM
|
|
205
|
+
SELECT password_hash FROM user_auth_identities
|
|
206
|
+
WHERE tenant_id = ? AND user_id = ? AND provider = 'password' LIMIT 1
|
|
206
207
|
`).get('default', existing.userId);
|
|
207
208
|
const invite = core.requestInviteAcceptance({ userId: existing.userId, workspaceId: 'workspace-invite' });
|
|
208
209
|
const accepted = core.joinInviteWithAuthenticatedUser({
|
|
@@ -210,7 +211,8 @@ describe('TaskforceCore account-level plan mode and invite revoke', () => {
|
|
|
210
211
|
userId: existing.userId
|
|
211
212
|
});
|
|
212
213
|
const after = db.prepare(`
|
|
213
|
-
SELECT password_hash FROM
|
|
214
|
+
SELECT password_hash FROM user_auth_identities
|
|
215
|
+
WHERE tenant_id = ? AND user_id = ? AND provider = 'password' LIMIT 1
|
|
214
216
|
`).get('default', existing.userId);
|
|
215
217
|
const membership = db.prepare(`
|
|
216
218
|
SELECT status FROM workspace_memberships WHERE tenant_id = ? AND user_id = ? AND workspace_id = ? LIMIT 1
|
|
@@ -233,7 +235,7 @@ describe('TaskforceCore account-level plan mode and invite revoke', () => {
|
|
|
233
235
|
billing: {
|
|
234
236
|
onboardingPolicy: {
|
|
235
237
|
mode: 'auto_grant_free',
|
|
236
|
-
|
|
238
|
+
planId: 'free'
|
|
237
239
|
}
|
|
238
240
|
}
|
|
239
241
|
});
|
|
@@ -261,12 +263,111 @@ describe('TaskforceCore account-level plan mode and invite revoke', () => {
|
|
|
261
263
|
const invite = core.requestInviteAcceptance({ userId: 'invited-user', workspaceId: 'workspace-invite' });
|
|
262
264
|
const accepted = core.acceptInviteWithToken({
|
|
263
265
|
token: String(invite.token),
|
|
264
|
-
password: '
|
|
266
|
+
password: 'LanternHarborOrbit!2026'
|
|
265
267
|
});
|
|
266
268
|
expect(accepted.workspaceId).toBe('workspace-invite');
|
|
267
269
|
expect(core.hasAccountEntitlement('invited-user')).toBe(true);
|
|
268
270
|
expect(core.getAccountEntitlement('invited-user')).toEqual(expect.objectContaining({
|
|
269
|
-
planVersionId: '
|
|
271
|
+
planVersionId: 'free-v1',
|
|
272
|
+
state: 'active'
|
|
273
|
+
}));
|
|
274
|
+
expect(core.getAccountAccessStatus('invited-user')).toEqual(expect.objectContaining({
|
|
275
|
+
gate: 'ok',
|
|
276
|
+
canAccessApp: true
|
|
277
|
+
}));
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
core.close();
|
|
281
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
it('grants the free personal entitlement to newly accepted invited accounts even without explicit self-serve onboarding policy', () => {
|
|
285
|
+
const projectRoot = makeProjectRoot();
|
|
286
|
+
const core = openCore(projectRoot);
|
|
287
|
+
try {
|
|
288
|
+
core.createWorkspace({ workspaceId: 'workspace-invite', name: 'Invite Workspace' });
|
|
289
|
+
core.ensureBootstrapUserAccess({
|
|
290
|
+
userId: 'owner-user',
|
|
291
|
+
workspaceId: 'workspace-invite',
|
|
292
|
+
role: 'owner',
|
|
293
|
+
email: 'owner@example.com'
|
|
294
|
+
});
|
|
295
|
+
core.setUserAccountPlanMode('owner-user', 'team');
|
|
296
|
+
core.ensureBootstrapUserAccess({
|
|
297
|
+
userId: 'invited-user',
|
|
298
|
+
workspaceId: 'workspace-invite',
|
|
299
|
+
role: 'member',
|
|
300
|
+
email: 'invited-no-policy@example.com'
|
|
301
|
+
});
|
|
302
|
+
core.setWorkspaceUserDisabled('invited-user', 'workspace-invite', true);
|
|
303
|
+
const db = core.db;
|
|
304
|
+
db.prepare(`
|
|
305
|
+
UPDATE workspace_memberships
|
|
306
|
+
SET status = 'invited'
|
|
307
|
+
WHERE tenant_id = ? AND user_id = ? AND workspace_id = ?
|
|
308
|
+
`).run('default', 'invited-user', 'workspace-invite');
|
|
309
|
+
const invite = core.requestInviteAcceptance({ userId: 'invited-user', workspaceId: 'workspace-invite' });
|
|
310
|
+
const accepted = core.acceptInviteWithToken({
|
|
311
|
+
token: String(invite.token),
|
|
312
|
+
password: 'LanternHarborOrbit!2026'
|
|
313
|
+
});
|
|
314
|
+
expect(accepted.workspaceId).toBe('workspace-invite');
|
|
315
|
+
expect(core.hasAccountEntitlement('invited-user')).toBe(true);
|
|
316
|
+
expect(core.getAccountEntitlement('invited-user')).toEqual(expect.objectContaining({
|
|
317
|
+
planVersionId: 'free-v1',
|
|
318
|
+
state: 'active'
|
|
319
|
+
}));
|
|
320
|
+
expect(core.getAccountAccessStatus('invited-user')).toEqual(expect.objectContaining({
|
|
321
|
+
gate: 'ok',
|
|
322
|
+
canAccessApp: true
|
|
323
|
+
}));
|
|
324
|
+
}
|
|
325
|
+
finally {
|
|
326
|
+
core.close();
|
|
327
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
it('reactivates blocked placeholder entitlements when a new invited user accepts a reused seat', () => {
|
|
331
|
+
const projectRoot = makeProjectRoot();
|
|
332
|
+
const core = openCore(projectRoot);
|
|
333
|
+
try {
|
|
334
|
+
core.createWorkspace({ workspaceId: 'workspace-invite', name: 'Invite Workspace' });
|
|
335
|
+
core.ensureBootstrapUserAccess({
|
|
336
|
+
userId: 'owner-user',
|
|
337
|
+
workspaceId: 'workspace-invite',
|
|
338
|
+
role: 'owner',
|
|
339
|
+
email: 'owner@example.com'
|
|
340
|
+
});
|
|
341
|
+
core.setUserAccountPlanMode('owner-user', 'team');
|
|
342
|
+
core.ensureBootstrapUserAccess({
|
|
343
|
+
userId: 'invited-user',
|
|
344
|
+
workspaceId: 'workspace-invite',
|
|
345
|
+
role: 'member',
|
|
346
|
+
email: 'invited@example.com'
|
|
347
|
+
});
|
|
348
|
+
core.setWorkspaceUserDisabled('invited-user', 'workspace-invite', true);
|
|
349
|
+
const db = core.db;
|
|
350
|
+
db.prepare(`
|
|
351
|
+
UPDATE workspace_memberships
|
|
352
|
+
SET status = 'invited'
|
|
353
|
+
WHERE tenant_id = ? AND user_id = ? AND workspace_id = ?
|
|
354
|
+
`).run('default', 'invited-user', 'workspace-invite');
|
|
355
|
+
core.updateAccountEntitlement({
|
|
356
|
+
accountId: 'invited-user',
|
|
357
|
+
planVersionId: 'free-v1',
|
|
358
|
+
entitlementState: 'canceled',
|
|
359
|
+
actorUserId: 'system-admin',
|
|
360
|
+
actorRole: 'system_admin',
|
|
361
|
+
reason: 'invite-reaccept-regression'
|
|
362
|
+
});
|
|
363
|
+
const invite = core.requestInviteAcceptance({ userId: 'invited-user', workspaceId: 'workspace-invite' });
|
|
364
|
+
const accepted = core.acceptInviteWithToken({
|
|
365
|
+
token: String(invite.token),
|
|
366
|
+
password: 'LanternHarborOrbit!2026'
|
|
367
|
+
});
|
|
368
|
+
expect(accepted.workspaceId).toBe('workspace-invite');
|
|
369
|
+
expect(core.getAccountEntitlement('invited-user')).toEqual(expect.objectContaining({
|
|
370
|
+
planVersionId: 'free-v1',
|
|
270
371
|
state: 'active'
|
|
271
372
|
}));
|
|
272
373
|
expect(core.getAccountAccessStatus('invited-user')).toEqual(expect.objectContaining({
|
package/dist/core/shared.d.ts
CHANGED
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import type { McpAccessTokenScope, PlanFeatureCatalogItem } from './types.js';
|
|
18
18
|
export declare const DOCUMENT_CLOUD_STORAGE_FEATURE_KEY = "context.uploads";
|
|
19
|
-
export declare const WORKSPACES_FEATURE_KEY = "workspace.
|
|
19
|
+
export declare const WORKSPACES_FEATURE_KEY = "workspace.workspaces";
|
|
20
|
+
export declare const AI_PROFILES_FEATURE_KEY = "collaboration.ai_profiles";
|
|
20
21
|
export declare const DOCUMENT_CLOUD_STORAGE_LIMIT_CONFIG_KEY = "storageLimitMb";
|
|
21
22
|
export declare const WORKSPACES_LIMIT_CONFIG_KEY = "maxWorkspaces";
|
|
23
|
+
export declare const AI_PROFILES_LIMIT_CONFIG_KEY = "maxAiProfiles";
|
|
22
24
|
export declare const MCP_ACCESS_TOKEN_MAX_ACTIVE_PER_USER_WORKSPACE = 10;
|
|
23
25
|
export declare const MCP_ACCESS_TOKEN_DEFAULT_PAGE_SIZE = 20;
|
|
24
26
|
export declare const MCP_ACCESS_TOKEN_MAX_PAGE_SIZE = 100;
|
package/dist/core/shared.js
CHANGED
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
import { createHash, pbkdf2Sync, randomBytes, timingSafeEqual } from 'crypto';
|
|
18
18
|
// === SECTION: Constants ===
|
|
19
19
|
export const DOCUMENT_CLOUD_STORAGE_FEATURE_KEY = 'context.uploads';
|
|
20
|
-
export const WORKSPACES_FEATURE_KEY = 'workspace.
|
|
20
|
+
export const WORKSPACES_FEATURE_KEY = 'workspace.workspaces';
|
|
21
|
+
export const AI_PROFILES_FEATURE_KEY = 'collaboration.ai_profiles';
|
|
21
22
|
export const DOCUMENT_CLOUD_STORAGE_LIMIT_CONFIG_KEY = 'storageLimitMb';
|
|
22
23
|
export const WORKSPACES_LIMIT_CONFIG_KEY = 'maxWorkspaces';
|
|
24
|
+
export const AI_PROFILES_LIMIT_CONFIG_KEY = 'maxAiProfiles';
|
|
23
25
|
export const MCP_ACCESS_TOKEN_MAX_ACTIVE_PER_USER_WORKSPACE = 10;
|
|
24
26
|
export const MCP_ACCESS_TOKEN_DEFAULT_PAGE_SIZE = 20;
|
|
25
27
|
export const MCP_ACCESS_TOKEN_MAX_PAGE_SIZE = 100;
|
|
@@ -30,6 +32,7 @@ export const DEFAULT_PLAN_FEATURE_CATALOG = [
|
|
|
30
32
|
featureKey: 'collaboration.team_management',
|
|
31
33
|
label: 'Team Management',
|
|
32
34
|
description: 'Allow managing members, invites, roles, and collaboration settings.',
|
|
35
|
+
publicDisplayOrder: 40,
|
|
33
36
|
allowedAccessModes: ['enabled', 'disabled'],
|
|
34
37
|
configTemplate: {}
|
|
35
38
|
},
|
|
@@ -37,6 +40,7 @@ export const DEFAULT_PLAN_FEATURE_CATALOG = [
|
|
|
37
40
|
featureKey: DOCUMENT_CLOUD_STORAGE_FEATURE_KEY,
|
|
38
41
|
label: 'Document Cloud Storage',
|
|
39
42
|
description: 'Allow cloud document storage with an optional storage allowance in MB.',
|
|
43
|
+
publicDisplayOrder: 30,
|
|
40
44
|
allowedAccessModes: ['enabled', 'disabled'],
|
|
41
45
|
configTemplate: {
|
|
42
46
|
[DOCUMENT_CLOUD_STORAGE_LIMIT_CONFIG_KEY]: 1024
|
|
@@ -46,10 +50,21 @@ export const DEFAULT_PLAN_FEATURE_CATALOG = [
|
|
|
46
50
|
featureKey: WORKSPACES_FEATURE_KEY,
|
|
47
51
|
label: 'Workspaces',
|
|
48
52
|
description: 'Allow multiple workspaces with a configurable workspace limit.',
|
|
53
|
+
publicDisplayOrder: 10,
|
|
49
54
|
allowedAccessModes: ['enabled', 'disabled'],
|
|
50
55
|
configTemplate: {
|
|
51
56
|
[WORKSPACES_LIMIT_CONFIG_KEY]: 1
|
|
52
57
|
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
featureKey: AI_PROFILES_FEATURE_KEY,
|
|
61
|
+
label: 'AI Profiles',
|
|
62
|
+
description: 'Allow registered AI collaborator profiles with an optional per-workspace limit.',
|
|
63
|
+
publicDisplayOrder: 20,
|
|
64
|
+
allowedAccessModes: ['enabled', 'limited', 'disabled'],
|
|
65
|
+
configTemplate: {
|
|
66
|
+
[AI_PROFILES_LIMIT_CONFIG_KEY]: 1
|
|
67
|
+
}
|
|
53
68
|
}
|
|
54
69
|
];
|
|
55
70
|
export const PLAN_FEATURE_DEPENDENCIES = Object.freeze(DEFAULT_PLAN_FEATURE_CATALOG.reduce((acc, feature) => {
|
package/dist/core/types.d.ts
CHANGED
|
@@ -287,6 +287,25 @@ export interface GlobalTaskforceSettings {
|
|
|
287
287
|
signedUploadTtlSeconds?: number;
|
|
288
288
|
signedDownloadTtlSeconds?: number;
|
|
289
289
|
};
|
|
290
|
+
oauthProviders?: {
|
|
291
|
+
google?: {
|
|
292
|
+
enabled?: boolean;
|
|
293
|
+
clientId?: string;
|
|
294
|
+
clientSecret?: string;
|
|
295
|
+
};
|
|
296
|
+
github?: {
|
|
297
|
+
enabled?: boolean;
|
|
298
|
+
clientId?: string;
|
|
299
|
+
clientSecret?: string;
|
|
300
|
+
};
|
|
301
|
+
apple?: {
|
|
302
|
+
enabled?: boolean;
|
|
303
|
+
servicesId?: string;
|
|
304
|
+
teamId?: string;
|
|
305
|
+
keyId?: string;
|
|
306
|
+
privateKeyPem?: string;
|
|
307
|
+
};
|
|
308
|
+
};
|
|
290
309
|
}
|
|
291
310
|
export interface SettingsSplitPayload {
|
|
292
311
|
system: GlobalTaskforceSettings;
|
|
@@ -303,11 +322,11 @@ export interface UserGlobalSyncSettings {
|
|
|
303
322
|
export type OnboardingPolicyMode = 'require_plan_selection' | 'auto_grant_free' | 'auto_start_trial';
|
|
304
323
|
export interface OnboardingPolicy {
|
|
305
324
|
mode: OnboardingPolicyMode;
|
|
306
|
-
|
|
325
|
+
planId: string | null;
|
|
307
326
|
}
|
|
308
327
|
export interface OnboardingPolicyValidationResult {
|
|
309
328
|
valid: boolean;
|
|
310
|
-
code?: '
|
|
329
|
+
code?: 'ONBOARDING_PLAN_REQUIRED' | 'ONBOARDING_PLAN_NOT_FOUND' | 'ONBOARDING_PLAN_NOT_ENABLED' | 'ONBOARDING_PLAN_MUST_BE_FREE' | 'ONBOARDING_PLAN_TRIAL_REQUIRED';
|
|
311
330
|
message?: string;
|
|
312
331
|
details?: Record<string, unknown>;
|
|
313
332
|
}
|
|
@@ -316,6 +335,36 @@ export type SystemRole = 'system_admin' | 'user';
|
|
|
316
335
|
export type PermissionMode = 'read-only' | 'read-write';
|
|
317
336
|
export type AuthTokenPurpose = 'email_verification' | 'password_reset' | 'invite_accept';
|
|
318
337
|
export type McpAccessTokenScope = 'tasks:read' | 'tasks:write' | 'workspace:read';
|
|
338
|
+
export type UserAuthProvider = 'password' | 'google' | 'github' | 'apple';
|
|
339
|
+
export interface UserAuthIdentity {
|
|
340
|
+
id: string;
|
|
341
|
+
tenantId: string;
|
|
342
|
+
userId: string;
|
|
343
|
+
provider: UserAuthProvider;
|
|
344
|
+
providerSubject: string | null;
|
|
345
|
+
providerEmail: string | null;
|
|
346
|
+
providerEmailVerified: boolean | null;
|
|
347
|
+
displayNameSnapshot: string | null;
|
|
348
|
+
avatarUrlSnapshot: string | null;
|
|
349
|
+
createdAt: string;
|
|
350
|
+
updatedAt: string;
|
|
351
|
+
lastLoginAt: string | null;
|
|
352
|
+
linkedAt: string | null;
|
|
353
|
+
disabledAt: string | null;
|
|
354
|
+
}
|
|
355
|
+
export type InviteeState = 'pending_setup' | 'existing_account';
|
|
356
|
+
export interface InviteAcceptanceDetails {
|
|
357
|
+
state: 'valid' | 'expired' | 'used' | 'missing' | 'revoked';
|
|
358
|
+
email?: string;
|
|
359
|
+
userId?: string;
|
|
360
|
+
workspaceId?: string;
|
|
361
|
+
inviteeState?: InviteeState;
|
|
362
|
+
availableMethods?: UserAuthProvider[];
|
|
363
|
+
/** @deprecated use inviteeState */
|
|
364
|
+
inviteeKind?: 'existing_user' | 'new_user';
|
|
365
|
+
/** @deprecated use availableMethods */
|
|
366
|
+
passwordRequired?: boolean;
|
|
367
|
+
}
|
|
319
368
|
export type McpAccessTokenStatus = 'active' | 'revoked' | 'expired';
|
|
320
369
|
export type McpOAuthProvider = 'chatgpt' | 'claude';
|
|
321
370
|
export type McpOAuthClientStatus = 'active' | 'revoked';
|
|
@@ -326,6 +375,8 @@ export interface UserAccessRecord {
|
|
|
326
375
|
role: AccessRole;
|
|
327
376
|
permissionMode: PermissionMode;
|
|
328
377
|
disabled: boolean;
|
|
378
|
+
accountDisabled?: boolean;
|
|
379
|
+
workspaceDisabled?: boolean;
|
|
329
380
|
betaAccess: boolean;
|
|
330
381
|
}
|
|
331
382
|
export interface UserIdentityRecord {
|
|
@@ -340,6 +391,7 @@ export interface WorkspaceUserRecord {
|
|
|
340
391
|
userId: string;
|
|
341
392
|
email: string;
|
|
342
393
|
displayName?: string | null;
|
|
394
|
+
avatarUrl?: string | null;
|
|
343
395
|
role: AccessRole;
|
|
344
396
|
permissionMode: PermissionMode;
|
|
345
397
|
betaAccess: boolean;
|
|
@@ -453,6 +505,9 @@ export interface PlanFeatureCatalogItem {
|
|
|
453
505
|
featureKey: string;
|
|
454
506
|
label: string;
|
|
455
507
|
description: string;
|
|
508
|
+
publicLabel?: string | null;
|
|
509
|
+
publicDescription?: string | null;
|
|
510
|
+
publicDisplayOrder?: number | null;
|
|
456
511
|
allowedAccessModes: PlanFeatureAccess[];
|
|
457
512
|
configTemplate: Record<string, unknown>;
|
|
458
513
|
dependsOn?: string[];
|
|
@@ -470,10 +525,20 @@ export interface PlanVersionSnapshot {
|
|
|
470
525
|
export interface PlanCatalogSnapshot {
|
|
471
526
|
planId: string;
|
|
472
527
|
displayName: string;
|
|
528
|
+
description: string | null;
|
|
473
529
|
status: 'active' | 'archived';
|
|
474
530
|
createdAt: string;
|
|
475
531
|
updatedAt: string;
|
|
476
532
|
}
|
|
533
|
+
export interface OnboardingPlanOption {
|
|
534
|
+
planId: string;
|
|
535
|
+
planDisplayName: string;
|
|
536
|
+
defaultPlanVersionId: string;
|
|
537
|
+
defaultVersionNumber: number;
|
|
538
|
+
planStatus: 'active' | 'archived';
|
|
539
|
+
supportsAutoGrantFree: boolean;
|
|
540
|
+
supportsAutoStartTrial: boolean;
|
|
541
|
+
}
|
|
477
542
|
export interface AccountEntitlementSnapshot {
|
|
478
543
|
accountId: string;
|
|
479
544
|
planVersionId: string;
|
|
@@ -520,6 +585,7 @@ export interface PasswordAuthResult {
|
|
|
520
585
|
userId: string;
|
|
521
586
|
workspaceId: string;
|
|
522
587
|
role: AccessRole;
|
|
588
|
+
identityId?: string;
|
|
523
589
|
};
|
|
524
590
|
code?: 'INVALID_CREDENTIALS' | 'EMAIL_NOT_VERIFIED';
|
|
525
591
|
}
|