@taskforcehq/taskforce 0.3.170 → 0.3.300
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 +52 -21
- package/dist/Taskforce.module.css +3394 -5981
- package/dist/TaskforceCore.CategoryFallback.test.js +5 -2
- package/dist/TaskforceCore.filter-persistence.test.js +402 -0
- package/dist/TaskforceCore.js +388 -71
- package/dist/TaskforceCore.routing.test.js +311 -0
- package/dist/TaskforceCore.test.js +3344 -615
- package/dist/TaskforceWidget.js +3 -2
- package/dist/__tests__/localizationCatalogs.test.js +48 -31
- package/dist/__tests__/workflowSimulation.test.js +2 -2
- package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.d.ts +60 -0
- package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.js +324 -0
- package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.test.js +278 -0
- package/dist/annotatedAttachments/payload.d.ts +2 -0
- package/dist/annotatedAttachments/payload.js +54 -0
- package/dist/annotatedAttachments/payload.test.js +67 -0
- package/dist/annotatedAttachments/service.d.ts +49 -0
- package/dist/annotatedAttachments/service.js +121 -0
- package/dist/annotatedAttachments/types.d.ts +86 -0
- package/dist/cli.js +74 -25
- package/dist/compat/deploymentCompat.d.ts +12 -0
- package/dist/compat/deploymentCompat.js +43 -0
- package/dist/compat/workspaceSyncCompat.d.ts +6 -0
- package/dist/compat/workspaceSyncCompat.js +79 -0
- package/dist/compat/workspaceSyncCompat.test.js +87 -0
- package/dist/components/features/AgentsModule.d.ts +5 -0
- package/dist/components/features/AgentsModule.js +131 -0
- package/dist/components/features/AgentsModule.test.js +102 -0
- package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +24 -0
- package/dist/components/features/AnnotatedAttachmentWorkspace.js +1942 -0
- package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +2831 -0
- package/dist/components/features/DocumentGenerateModal.js +8 -16
- package/dist/components/features/DocumentIndex.d.ts +3 -4
- package/dist/components/features/DocumentIndex.js +22 -10
- package/dist/components/features/DocumentViewer.d.ts +6 -2
- package/dist/components/features/DocumentViewer.js +1000 -18
- package/dist/components/features/DocumentWorkspace.d.ts +40 -2
- package/dist/components/features/DocumentWorkspace.js +96 -16
- package/dist/components/features/DocumentWorkspace.test.js +1202 -0
- package/dist/components/features/InitiativesModule.d.ts +22 -0
- package/dist/components/features/InitiativesModule.js +33 -0
- package/dist/components/features/TaskSettings.d.ts +41 -32
- package/dist/components/features/TaskSettings.js +798 -313
- package/dist/components/features/TaskSettings.test.js +1221 -47
- package/dist/components/features/WorkflowsModule.d.ts +27 -0
- package/dist/components/features/WorkflowsModule.js +21 -0
- package/dist/components/features/documentWorkspaceFilters.d.ts +10 -0
- package/dist/components/features/documentWorkspaceFilters.js +11 -0
- package/dist/components/features/settings/CategoryManager.d.ts +2 -0
- package/dist/components/features/settings/CategoryManager.js +15 -8
- package/dist/components/features/settings/PriorityManager.d.ts +18 -0
- package/dist/components/features/settings/PriorityManager.js +51 -0
- package/dist/components/features/settings/TaxonomyEditor.d.ts +2 -1
- package/dist/components/features/settings/TaxonomyEditor.js +41 -18
- package/dist/components/features/settings/TaxonomyLibraryManager.d.ts +26 -0
- package/dist/components/features/settings/TaxonomyLibraryManager.js +301 -0
- package/dist/components/features/settings/TaxonomyLibraryManager.test.js +141 -0
- package/dist/components/features/settings/TaxonomyManager.d.ts +35 -12
- package/dist/components/features/settings/TaxonomyManager.js +59 -27
- package/dist/components/features/settings/TypeManager.d.ts +11 -0
- package/dist/components/features/settings/TypeManager.js +48 -11
- package/dist/components/features/settings/{WorkflowEditorPrototype.d.ts → WorkflowEditor.d.ts} +2 -2
- package/dist/components/features/settings/{WorkflowEditorPrototype.js → WorkflowEditor.js} +3 -3
- package/dist/components/task/TaskActionHeader.d.ts +12 -2
- package/dist/components/task/TaskActionHeader.js +64 -8
- package/dist/components/task/TaskActionHeader.test.js +73 -0
- package/dist/components/task/TaskCard.d.ts +14 -16
- package/dist/components/task/TaskCard.js +144 -174
- package/dist/components/task/TaskCard.test.js +416 -64
- package/dist/components/task/TaskContextUpload.d.ts +6 -5
- package/dist/components/task/TaskContextUpload.js +199 -64
- package/dist/components/task/TaskContextUpload.test.js +331 -14
- package/dist/components/task/TaskForm.d.ts +23 -29
- package/dist/components/task/TaskForm.js +348 -169
- package/dist/components/task/TaskForm.test.js +419 -23
- package/dist/components/task/TaskKanban.d.ts +20 -21
- package/dist/components/task/TaskKanban.js +176 -316
- package/dist/components/task/TaskKanban.planning-drop.test.js +93 -0
- package/dist/components/task/TaskList.d.ts +22 -11
- package/dist/components/task/TaskList.js +98 -12
- package/dist/components/task/TaskList.test.js +206 -7
- package/dist/components/task/TaskReferenceBadge.d.ts +12 -0
- package/dist/components/task/TaskReferenceBadge.js +5 -0
- package/dist/components/task/TaskStatusActions.d.ts +12 -0
- package/dist/components/task/TaskStatusActions.js +18 -0
- package/dist/components/task/TaskStatusActions.test.js +45 -0
- package/dist/components/ui/ImageReferenceBadge.d.ts +12 -0
- package/dist/components/ui/ImageReferenceBadge.js +5 -0
- package/dist/components/ui/LevelSelector.d.ts +2 -1
- package/dist/components/ui/LevelSelector.js +2 -2
- package/dist/components/ui/Markdown.d.ts +1 -2
- package/dist/components/ui/Markdown.js +2 -13
- package/dist/components/ui/Modal.d.ts +3 -2
- package/dist/components/ui/Modal.js +4 -3
- package/dist/components/ui/MultiSelectFilter.d.ts +3 -1
- package/dist/components/ui/MultiSelectFilter.js +2 -2
- package/dist/components/ui/ReferenceBadgeButton.d.ts +13 -0
- package/dist/components/ui/ReferenceBadgeButton.js +6 -0
- package/dist/components/ui/TaxonomyDropdown.d.ts +7 -1
- package/dist/components/ui/TaxonomyDropdown.js +58 -23
- package/dist/components/ui/TopNoticeLayer.d.ts +7 -0
- package/dist/components/ui/TopNoticeLayer.js +14 -0
- package/dist/components/ui/TopNoticeLayer.test.js +25 -0
- package/dist/components/views/AppShellHeader.d.ts +13 -0
- package/dist/components/views/AppShellHeader.js +19 -0
- package/dist/components/views/AppShellHeader.test.js +21 -0
- package/dist/components/views/PlanComparisonPage.d.ts +73 -0
- package/dist/components/views/PlanComparisonPage.js +196 -0
- package/dist/components/views/PlanComparisonPage.test.js +251 -0
- package/dist/components/views/PlansPage.d.ts +14 -0
- package/dist/components/views/PlansPage.js +355 -0
- package/dist/components/views/StandaloneLayout.d.ts +2 -2
- package/dist/components/views/StandaloneLayout.js +2212 -1194
- package/dist/components/views/WidgetView.d.ts +2 -2
- package/dist/components/views/WidgetView.js +149 -43
- package/dist/components/views/chrome/WorkspaceChromeSections.d.ts +57 -0
- package/dist/components/views/chrome/WorkspaceChromeSections.js +38 -0
- package/dist/components/views/chrome/WorkspaceChromeSections.test.js +54 -0
- package/dist/components/views/chrome/workspaceChrome.d.ts +31 -0
- package/dist/components/views/chrome/workspaceChrome.js +108 -0
- package/dist/components/views/chrome/workspaceChrome.test.js +52 -0
- package/dist/components/views/panels/DocumentFilterToolbar.d.ts +10 -0
- package/dist/components/views/panels/DocumentFilterToolbar.js +7 -0
- package/dist/components/views/panels/DocumentFilterToolbar.test.js +19 -0
- package/dist/components/views/panels/FilterToolbar.d.ts +66 -0
- package/dist/components/views/panels/FilterToolbar.js +21 -0
- package/dist/components/views/panels/FilterToolbar.test.d.ts +1 -0
- package/dist/components/views/panels/FilterToolbar.test.js +45 -0
- package/dist/components/views/panels/FilterToolbarControls.d.ts +46 -0
- package/dist/components/views/panels/FilterToolbarControls.js +61 -0
- package/dist/components/views/panels/PlanningDrawer.d.ts +115 -0
- package/dist/components/views/panels/PlanningDrawer.js +220 -0
- package/dist/components/views/panels/ScheduleSidebar.d.ts +52 -0
- package/dist/components/views/panels/ScheduleSidebar.js +127 -0
- package/dist/components/views/panels/scheduleUtils.d.ts +14 -0
- package/dist/components/views/panels/scheduleUtils.js +79 -0
- package/dist/components/views/teamManagementAccess.d.ts +6 -0
- package/dist/components/views/teamManagementAccess.js +18 -0
- package/dist/components/views/teamManagementAccess.test.d.ts +1 -0
- package/dist/components/views/teamManagementAccess.test.js +44 -0
- package/dist/config/clientDeployment.d.ts +2 -0
- package/dist/config/clientDeployment.js +13 -0
- package/dist/config/cloudEnvironment.d.ts +15 -0
- package/dist/config/cloudEnvironment.js +60 -0
- package/dist/config/deployment.d.ts +20 -0
- package/dist/config/deployment.js +67 -0
- package/dist/config/deployment.test.d.ts +1 -0
- package/dist/config/deployment.test.js +88 -0
- package/dist/config/envSchema.js +36 -8
- package/dist/config/envSchema.test.js +28 -0
- package/dist/core/AiProfileService.d.ts +66 -0
- package/dist/core/AiProfileService.js +320 -0
- package/dist/core/AiProfileService.test.d.ts +1 -0
- package/dist/core/AiProfileService.test.js +164 -0
- package/dist/core/AiProfiles.test.d.ts +1 -0
- package/dist/core/AiProfiles.test.js +449 -0
- package/dist/core/AttachmentLinkService.d.ts +15 -0
- package/dist/core/AttachmentLinkService.js +157 -0
- package/dist/core/AuthTokenService.d.ts +44 -0
- package/dist/core/AuthTokenService.js +207 -0
- package/dist/core/CreatedBy.test.js +134 -6
- package/dist/core/DeletedTaskLifecycleService.d.ts +33 -0
- package/dist/core/DeletedTaskLifecycleService.js +158 -0
- package/dist/core/DeletedTaskTrash.test.d.ts +1 -0
- package/dist/core/DeletedTaskTrash.test.js +104 -0
- package/dist/core/EntitlementsPolicy.test.js +78 -5
- package/dist/core/GlobalSettingsService.d.ts +16 -0
- package/dist/core/GlobalSettingsService.js +370 -0
- package/dist/core/JsonBackupRuntime.test.d.ts +1 -0
- package/dist/core/JsonBackupRuntime.test.js +155 -0
- package/dist/core/McpTokenService.d.ts +118 -0
- package/dist/core/McpTokenService.js +921 -0
- package/dist/core/PlanEntitlementService.d.ts +201 -0
- package/dist/core/PlanEntitlementService.js +1442 -0
- package/dist/core/PlanVersionPolicy.test.d.ts +1 -0
- package/dist/core/PlanVersionPolicy.test.js +178 -0
- package/dist/core/PlanningEntities.test.d.ts +1 -0
- package/dist/core/PlanningEntities.test.js +95 -0
- package/dist/core/ScheduleFields.test.js +15 -19
- package/dist/core/SignupMonetizationSettings.test.d.ts +1 -0
- package/dist/core/SignupMonetizationSettings.test.js +159 -0
- package/dist/core/Stats.test.d.ts +1 -0
- package/dist/core/Stats.test.js +36 -0
- package/dist/core/SyncReconciliationService.d.ts +93 -0
- package/dist/core/SyncReconciliationService.js +406 -0
- package/dist/core/SystemAdmin.test.js +578 -13
- package/dist/core/TaskActivityService.d.ts +21 -0
- package/dist/core/TaskActivityService.js +130 -0
- package/dist/core/TaskAttachmentsCanonical.test.d.ts +1 -0
- package/dist/core/TaskAttachmentsCanonical.test.js +306 -0
- package/dist/core/TaskAuditTimeline.test.d.ts +1 -0
- package/dist/core/TaskAuditTimeline.test.js +132 -0
- package/dist/core/TaskTaxonomyValidation.test.js +338 -15
- package/dist/core/Taskforce.d.ts +930 -183
- package/dist/core/Taskforce.ids.test.js +252 -0
- package/dist/core/Taskforce.js +3848 -2532
- package/dist/core/Taskforce.mcpAuth.test.d.ts +1 -0
- package/dist/core/Taskforce.mcpAuth.test.js +343 -0
- package/dist/core/Taskforce.realtimeMutation.test.js +57 -2
- package/dist/core/TaxonomySettingsService.d.ts +18 -0
- package/dist/core/TaxonomySettingsService.js +162 -0
- package/dist/core/WorkspaceDelete.test.js +32 -13
- package/dist/core/WorkspaceLifecycleService.d.ts +11 -0
- package/dist/core/WorkspaceLifecycleService.js +88 -0
- package/dist/core/WorkspacePermissions.test.js +95 -0
- package/dist/core/WorkspacePlanMode.test.js +114 -1
- package/dist/core/WorkspacePreferenceService.d.ts +41 -0
- package/dist/core/WorkspacePreferenceService.js +246 -0
- package/dist/core/WorkspaceSyncSettings.test.d.ts +1 -0
- package/dist/core/WorkspaceSyncSettings.test.js +65 -0
- package/dist/core/__tests__/TaskAttachmentsCanonical.test.d.ts +1 -0
- package/dist/core/__tests__/TaskAttachmentsCanonical.test.js +212 -0
- package/dist/core/shared.d.ts +36 -0
- package/dist/core/shared.js +128 -0
- package/dist/core/types.d.ts +614 -0
- package/dist/core/types.js +27 -0
- package/dist/documentReviews/DocumentReviewCommentStore.d.ts +56 -0
- package/dist/documentReviews/DocumentReviewCommentStore.js +230 -0
- package/dist/documentReviews/DocumentReviewSessionStore.d.ts +52 -0
- package/dist/documentReviews/DocumentReviewSessionStore.js +193 -0
- package/dist/documentReviews/DocumentReviewSessionStore.test.d.ts +1 -0
- package/dist/documentReviews/DocumentReviewSessionStore.test.js +127 -0
- package/dist/documentReviews/service.d.ts +68 -0
- package/dist/documentReviews/service.js +210 -0
- package/dist/documentReviews/types.d.ts +58 -0
- package/dist/documentReviews/types.js +1 -0
- package/dist/hooks/auth/useAuthGuardPolicy.d.ts +2 -0
- package/dist/hooks/auth/useAuthGuardPolicy.js +1 -0
- package/dist/hooks/tasks/useCurrentTaskRelations.d.ts +15 -0
- package/dist/hooks/tasks/useCurrentTaskRelations.js +37 -0
- package/dist/hooks/tasks/useTaskActions.d.ts +3 -0
- package/dist/hooks/tasks/useTaskActions.js +2 -0
- package/dist/hooks/tasks/useTaskEditorNavigation.d.ts +65 -0
- package/dist/hooks/tasks/useTaskEditorNavigation.js +235 -0
- package/dist/hooks/tasks/useTaskFilters.d.ts +2 -0
- package/dist/hooks/tasks/useTaskFilters.js +2 -0
- package/dist/hooks/tasks/useTaskFormActions.d.ts +58 -0
- package/dist/hooks/tasks/useTaskFormActions.js +365 -0
- package/dist/hooks/tasks/useTaskRelationships.d.ts +16 -0
- package/dist/hooks/tasks/useTaskRelationships.js +64 -0
- package/dist/hooks/ui/useResourceExport.d.ts +19 -0
- package/dist/hooks/ui/useResourceExport.js +84 -0
- package/dist/hooks/ui/useSettingsModel.d.ts +203 -0
- package/dist/hooks/ui/useSettingsModel.js +125 -0
- package/dist/hooks/ui/useSettingsPersistence.d.ts +36 -0
- package/dist/hooks/ui/useSettingsPersistence.js +254 -0
- package/dist/hooks/ui/useSettingsTaxonomyManagement.d.ts +119 -0
- package/dist/hooks/ui/useSettingsTaxonomyManagement.js +536 -0
- package/dist/hooks/ui/useUiNotice.d.ts +1 -0
- package/dist/hooks/ui/useUiNotice.js +1 -0
- package/dist/hooks/ui/useWorkflowTemplates.d.ts +37 -0
- package/dist/hooks/ui/useWorkflowTemplates.js +111 -0
- package/dist/hooks/ui/useZenMode.d.ts +5 -0
- package/dist/hooks/ui/useZenMode.js +32 -0
- package/dist/hooks/useFilterSort.d.ts +60 -0
- package/dist/hooks/useFilterSort.js +362 -0
- package/dist/hooks/useFilterSort.test.d.ts +1 -0
- package/dist/hooks/useFilterSort.test.js +333 -0
- package/dist/hooks/useRealtimeSync.d.ts +1 -0
- package/dist/hooks/useRealtimeSync.js +24 -9
- package/dist/hooks/useRealtimeSync.test.js +28 -1
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.d.ts +1 -0
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +34 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +65 -103
- package/dist/hooks/useSyncOrchestrator.js +1538 -670
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +700 -174
- package/dist/hooks/useTaskData.d.ts +35 -0
- package/dist/hooks/useTaskData.js +239 -0
- package/dist/hooks/useTaskData.test.d.ts +1 -0
- package/dist/hooks/useTaskData.test.js +119 -0
- package/dist/hooks/useTaskMutations.d.ts +46 -0
- package/dist/hooks/useTaskMutations.js +472 -0
- package/dist/hooks/useTaskMutations.test.d.ts +1 -0
- package/dist/hooks/useTaskMutations.test.js +80 -0
- package/dist/hooks/useTaskforce.contract.test.d.ts +1 -0
- package/dist/hooks/useTaskforce.contract.test.js +7 -0
- package/dist/hooks/useTaskforce.d.ts +260 -295
- package/dist/hooks/useTaskforce.js +1954 -2623
- package/dist/hooks/useTaskforce.priority-filters.test.js +6 -11
- package/dist/hooks/useTaskforce.sync-behavior.test.d.ts +1 -0
- package/dist/hooks/useTaskforce.sync-behavior.test.js +1120 -0
- package/dist/hooks/useUiNotice.d.ts +16 -0
- package/dist/hooks/useUiNotice.js +49 -0
- package/dist/hooks/useWorkspaceSyncController.d.ts +23 -46
- package/dist/hooks/useWorkspaceSyncController.js +280 -461
- package/dist/hooks/useWorkspaceSyncController.test.js +106 -199
- package/dist/hooks/workspace/useTaskforceApiRouting.d.ts +8 -0
- package/dist/hooks/workspace/useTaskforceApiRouting.js +91 -0
- package/dist/hooks/workspace/useTaskforceApiRouting.test.d.ts +1 -0
- package/dist/hooks/workspace/useTaskforceApiRouting.test.js +60 -0
- package/dist/hooks/workspace/useWorkspaceSyncController.d.ts +1 -0
- package/dist/hooks/workspace/useWorkspaceSyncController.js +1 -0
- package/dist/hooks/workspace/workspaceLocalState.d.ts +30 -0
- package/dist/hooks/workspace/workspaceLocalState.js +101 -0
- package/dist/localization/locales/en-US.d.ts +1 -8
- package/dist/localization/locales/en-US.js +4 -11
- package/dist/localization/locales/es-419.js +2 -9
- package/dist/localization/locales/pt-BR.js +2 -9
- package/dist/localization/localizationCatalogs.test.d.ts +1 -0
- package/dist/localization/localizationCatalogs.test.js +37 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +9 -15
- package/dist/mcp/clientIntegrations.d.ts +33 -0
- package/dist/mcp/clientIntegrations.js +199 -0
- package/dist/mcp/clientIntegrations.test.d.ts +1 -0
- package/dist/mcp/clientIntegrations.test.js +110 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +2 -0
- package/dist/mcp/optionResolution.test.js +6 -2
- package/dist/mcp/runtime.d.ts +120 -0
- package/dist/mcp/runtime.js +5668 -0
- package/dist/mcp/runtime.test.d.ts +1 -0
- package/dist/mcp/runtime.test.js +3293 -0
- package/dist/mcp/server.js +2 -1793
- package/dist/mcp/structuredCommentValidation.test.d.ts +1 -0
- package/dist/{__tests__ → mcp}/structuredCommentValidation.test.js +1 -1
- package/dist/mcp/structuredDocValidation.js +1 -2
- package/dist/mcp/structuredDocValidation.test.d.ts +1 -0
- package/dist/{__tests__ → mcp}/structuredDocValidation.test.js +9 -4
- package/dist/mcp/toolRegistry.d.ts +570 -0
- package/dist/mcp/toolRegistry.js +90 -0
- package/dist/mcp/toolRegistry.test.d.ts +1 -0
- package/dist/mcp/toolRegistry.test.js +30 -0
- package/dist/migrations/billingSchemaParity.test.js +111 -0
- package/dist/migrations/taskSchemaMigrations.d.ts +5 -0
- package/dist/migrations/taskSchemaMigrations.js +681 -39
- package/dist/migrations/taskSchemaMigrations.test.d.ts +1 -0
- package/dist/migrations/taskSchemaMigrations.test.js +210 -0
- package/dist/plugins/vite.js +13 -7
- package/dist/public/apple-touch-icon.png +0 -0
- package/dist/public/archive/phoenix/apple-touch-icon.png +0 -0
- package/dist/public/archive/phoenix/favicon.ico +0 -0
- package/dist/public/archive/phoenix/site.webmanifest +1 -0
- package/dist/public/favicon-96x96.png +0 -0
- package/dist/public/favicon.ico +0 -0
- package/dist/public/favicon.svg +17 -0
- package/dist/public/site.webmanifest +21 -1
- package/dist/public/web-app-manifest-192x192.png +0 -0
- package/dist/public/web-app-manifest-512x512.png +0 -0
- package/dist/releaseVersion.test.d.ts +1 -0
- package/dist/{__tests__/releaseVersion.test.js → releaseVersion.test.js} +1 -1
- package/dist/resources/templates/WORKFLOW_V2_POLICY.md +1 -1
- package/dist/resources/templates/workflow-sources/workflowDocs.mjs +62 -52
- package/dist/resources/templates/workflows/collaborate.yaml +11 -5
- package/dist/resources/templates/workflows/do.yaml +8 -2
- package/dist/resources/templates/workflows/evaluate.yaml +13 -7
- package/dist/resources/templates/workflows/execute.yaml +40 -23
- package/dist/resources/templates/workflows/plan.yaml +15 -8
- package/dist/resources/templates/workflows/review.yaml +14 -8
- package/dist/runtime/appGateDefinitions.d.ts +32 -0
- package/dist/runtime/appGateDefinitions.js +32 -0
- package/dist/runtime/appGates.d.ts +29 -0
- package/dist/runtime/appGates.js +53 -0
- package/dist/runtime/appGates.test.d.ts +1 -0
- package/dist/runtime/appGates.test.js +42 -0
- package/dist/runtime/migrationFlags.d.ts +1 -0
- package/dist/runtime/migrationFlags.js +1 -0
- package/dist/server/annotatedAttachmentsRoutes.test.d.ts +1 -0
- package/dist/server/annotatedAttachmentsRoutes.test.js +473 -0
- package/dist/server/auth.js +14 -2
- package/dist/server/auth.test.js +21 -1
- package/dist/server/cors.js +11 -5
- package/dist/server/cors.test.js +6 -0
- package/dist/server/documentReviewRoutes.test.d.ts +1 -0
- package/dist/server/documentReviewRoutes.test.js +157 -0
- package/dist/server/index.cookieProxy.test.js +46 -1
- package/dist/server/index.d.ts +20 -0
- package/dist/server/index.js +168 -41
- package/dist/server/index.rateLimit.test.js +6 -0
- package/dist/server/index.test.js +133 -2
- package/dist/server/mockStripe.d.ts +72 -0
- package/dist/server/mockStripe.js +357 -0
- package/dist/server/realtimeSyncWs.js +4 -0
- package/dist/server/routes/admin.d.ts +114 -0
- package/dist/server/routes/admin.js +2226 -0
- package/dist/server/routes/annotatedAttachments.d.ts +24 -0
- package/dist/server/routes/annotatedAttachments.js +235 -0
- package/dist/server/routes/auth.d.ts +61 -0
- package/dist/server/routes/auth.js +1296 -0
- package/dist/server/{routes.billing.d.ts → routes/billing.d.ts} +3 -2
- package/dist/server/routes/billing.js +2001 -0
- package/dist/server/routes/billing.test.d.ts +1 -0
- package/dist/server/routes/billing.test.js +1631 -0
- package/dist/server/routes/documentReviews.d.ts +11 -0
- package/dist/server/routes/documentReviews.js +191 -0
- package/dist/server/routes/documents.d.ts +118 -0
- package/dist/server/routes/documents.js +1113 -0
- package/dist/server/routes/resources.d.ts +25 -0
- package/dist/server/routes/resources.js +225 -0
- package/dist/server/routes/shared.d.ts +35 -0
- package/dist/server/routes/shared.js +305 -0
- package/dist/server/routes/shared.test.d.ts +1 -0
- package/dist/server/routes/shared.test.js +32 -0
- package/dist/server/{routes.sync.d.ts → routes/sync.d.ts} +5 -5
- package/dist/server/routes/sync.integration.test.d.ts +1 -0
- package/dist/server/routes/sync.integration.test.js +2003 -0
- package/dist/server/routes/sync.js +3143 -0
- package/dist/server/routes/tasks.d.ts +72 -0
- package/dist/server/routes/tasks.js +1252 -0
- package/dist/server/routes/workspaces.d.ts +32 -0
- package/dist/server/routes/workspaces.js +307 -0
- package/dist/server/routes.d.ts +8 -9
- package/dist/server/routes.js +968 -4092
- package/dist/server/routes.test.js +9043 -2415
- package/dist/server/runtimeMode.d.ts +1 -1
- package/dist/server/runtimeMode.js +3 -18
- package/dist/server/runtimeMode.test.js +3 -3
- package/dist/server/securityHeaders.d.ts +1 -1
- package/dist/server/securityHeaders.js +65 -6
- package/dist/server/securityHeaders.test.js +40 -2
- package/dist/services/templateEngine.test.d.ts +1 -0
- package/dist/{__tests__ → services}/templateEngine.test.js +1 -1
- package/dist/services/templates.test.d.ts +1 -0
- package/dist/{__tests__ → services}/templates.test.js +1 -1
- package/dist/services/workflowDocumentFixtures.test.d.ts +1 -0
- package/dist/{__tests__ → services}/workflowDocumentFixtures.test.js +4 -4
- package/dist/services/workflowExportMatrix.test.d.ts +1 -0
- package/dist/{__tests__ → services}/workflowExportMatrix.test.js +1 -1
- package/dist/services/workflowExportSnapshots.test.d.ts +1 -0
- package/dist/services/workflowGeneration.test.d.ts +1 -0
- package/dist/services/workflowSimulation.test.d.ts +1 -0
- package/dist/services/workflowSimulation.test.js +113 -0
- package/dist/services/workflowV2Policy.test.d.ts +1 -0
- package/dist/{__tests__ → services}/workflowV2Policy.test.js +2 -2
- package/dist/shared/aiProfileSurfaceType.d.ts +9 -0
- package/dist/shared/aiProfileSurfaceType.js +49 -0
- package/dist/shared/customTaxonomyLibrary.d.ts +11 -0
- package/dist/shared/customTaxonomyLibrary.js +276 -0
- package/dist/shared/customTaxonomyLibrary.test.d.ts +1 -0
- package/dist/shared/customTaxonomyLibrary.test.js +20 -0
- package/dist/shared/taxonomyCatalog.d.ts +19 -0
- package/dist/shared/taxonomyCatalog.js +36 -0
- package/dist/shared/taxonomyDefaults.d.ts +29 -0
- package/dist/shared/taxonomyDefaults.js +26 -0
- package/dist/shared/taxonomyLibrary.d.ts +15 -0
- package/dist/shared/taxonomyLibrary.js +98 -0
- package/dist/shared/taxonomyLibrary.test.d.ts +1 -0
- package/dist/shared/taxonomyLibrary.test.js +26 -0
- package/dist/shared/taxonomyState.d.ts +36 -0
- package/dist/shared/taxonomyState.js +73 -0
- package/dist/shared/taxonomyState.test.d.ts +1 -0
- package/dist/shared/taxonomyState.test.js +71 -0
- package/dist/shared/workflowDefaults.d.ts +9 -0
- package/dist/shared/workflowDefaults.js +11 -0
- package/dist/storage/documentIntegrity.d.ts +2 -0
- package/dist/storage/documentIntegrity.js +34 -17
- package/dist/storage/documentPurge.d.ts +2 -1
- package/dist/storage/documentPurge.js +35 -8
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage/objectStorage.d.ts +1 -1
- package/dist/storage/objectStorageClient.d.ts +6 -4
- package/dist/storage/objectStorageClient.js +77 -29
- package/dist/storage/objectStorageClient.test.d.ts +1 -0
- package/dist/storage/objectStorageClient.test.js +57 -0
- package/dist/storage/objectStorageConfig.js +3 -1
- package/dist/storage/objectStorageConfig.test.d.ts +1 -0
- package/dist/storage/objectStorageConfig.test.js +38 -0
- package/dist/storage/postgresAdapter.d.ts +5 -0
- package/dist/storage/postgresAdapter.js +2 -2
- package/dist/storage/postgresAdapter.test.d.ts +1 -0
- package/dist/storage/postgresAdapter.test.js +29 -0
- package/dist/storage/taskAssetStore.d.ts +7 -4
- package/dist/storage/taskAssetStore.js +65 -60
- package/dist/storage/workspaceAssetStore.d.ts +122 -0
- package/dist/storage/workspaceAssetStore.js +496 -0
- package/dist/storage/workspaceAssetStore.test.d.ts +1 -0
- package/dist/storage/workspaceAssetStore.test.js +501 -0
- package/dist/sync/cloudSyncApi.d.ts +33 -2
- package/dist/sync/cloudSyncApi.js +37 -15
- package/dist/sync/cloudSyncApi.test.js +60 -3
- package/dist/sync/index.d.ts +2 -0
- package/dist/sync/index.js +3 -0
- package/dist/sync/syncService.d.ts +25 -0
- package/dist/sync/syncService.js +184 -16
- package/dist/sync/syncService.test.js +289 -25
- package/dist/sync/workspaceRepair.d.ts +114 -0
- package/dist/sync/workspaceRepair.js +566 -0
- package/dist/sync/workspaceSyncModel.d.ts +230 -2
- package/dist/sync/workspaceSyncModel.js +456 -17
- package/dist/sync/workspaceSyncModel.test.js +642 -0
- package/dist/sync/workspaceSyncState.d.ts +12 -0
- package/dist/sync/workspaceSyncState.js +12 -0
- package/dist/sync/workspaceSyncSurface.d.ts +33 -0
- package/dist/sync/workspaceSyncSurface.js +74 -0
- package/dist/types.d.ts +143 -18
- package/dist/types.js +9 -24
- package/dist/ui/assets/AgentsModule-TVmCgizS.js +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-CAyhnPeR.js +3 -0
- package/dist/ui/assets/DocumentWorkspace-BOCw5e4D.js +250 -0
- package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +1 -0
- package/dist/ui/assets/InitiativesModule-CN0r02Oi.js +1 -0
- package/dist/ui/assets/PlansPage-BlVC_lRq.css +1 -0
- package/dist/ui/assets/PlansPage-FcQSNLZd.js +1 -0
- package/dist/ui/assets/TaskSettings-BwB9NtmJ.css +1 -0
- package/dist/ui/assets/TaskSettings-Cw5zYD1L.js +8 -0
- package/dist/ui/assets/WorkflowsModule-CY9R2zPB.js +5 -0
- package/dist/ui/assets/index-Bzqy5QGm.js +6 -0
- package/dist/ui/assets/index-DxRiI3tY.css +1 -0
- package/dist/ui/assets/vendor-dnd-DRzYolkg.js +5 -0
- package/dist/ui/assets/{vendor-icons-ZFG3SGwo.js → vendor-icons-QZyhEwgT.js} +1 -1
- package/dist/ui/assets/{vendor-markdown-CKYkQHfj.js → vendor-markdown-BUxTU7dS.js} +1 -1
- package/dist/ui/assets/vendor-react-CKJs5o3c.js +61 -0
- package/dist/ui/assets/vendor-router-BbWMxlnO.js +3 -0
- package/dist/ui/branding/logos/logo.png +0 -0
- package/dist/ui/branding/logos/logo_dark.svg +27 -0
- package/dist/ui/branding/logos/logo_favicon.svg +35 -0
- package/dist/ui/branding/logos/logo_light.svg +27 -0
- package/dist/ui/favicon/apple-touch-icon.png +0 -0
- package/dist/ui/favicon/archive/phoenix/apple-touch-icon.png +0 -0
- package/dist/ui/favicon/archive/phoenix/favicon.ico +0 -0
- package/dist/ui/favicon/archive/phoenix/site.webmanifest +1 -0
- package/dist/ui/favicon/favicon-96x96.png +0 -0
- package/dist/ui/favicon/favicon.ico +0 -0
- package/dist/ui/favicon/favicon.svg +17 -0
- package/dist/ui/favicon/site.webmanifest +21 -1
- package/dist/ui/favicon/web-app-manifest-192x192.png +0 -0
- package/dist/ui/favicon/web-app-manifest-512x512.png +0 -0
- package/dist/ui/index.html +10 -7
- package/dist/ui/product/screen_01.png +0 -0
- package/dist/ui/product/screen_02.png +0 -0
- package/dist/ui/product/screen_03.png +0 -0
- package/dist/utils/annotatedAttachments.d.ts +20 -0
- package/dist/utils/annotatedAttachments.js +74 -0
- package/dist/utils/annotatedAttachments.test.d.ts +1 -0
- package/dist/utils/annotatedAttachments.test.js +108 -0
- package/dist/utils/assignees.d.ts +26 -0
- package/dist/utils/assignees.js +132 -0
- package/dist/utils/canonicalAssetKind.d.ts +16 -0
- package/dist/utils/canonicalAssetKind.js +39 -0
- package/dist/utils/canonicalAssetKind.test.d.ts +1 -0
- package/dist/utils/canonicalAssetKind.test.js +24 -0
- package/dist/utils/coalescedRefresh.d.ts +5 -0
- package/dist/utils/coalescedRefresh.js +16 -0
- package/dist/utils/coalescedRefresh.test.d.ts +1 -0
- package/dist/utils/coalescedRefresh.test.js +30 -0
- package/dist/utils/constants.d.ts +16 -3
- package/dist/utils/constants.icons.test.js +2 -1
- package/dist/utils/constants.js +70 -13
- package/dist/utils/contextAssetEvents.d.ts +7 -0
- package/dist/utils/contextAssetEvents.js +6 -0
- package/dist/utils/contextFiles.d.ts +14 -3
- package/dist/utils/contextFiles.js +40 -8
- package/dist/utils/documentManagerApi.d.ts +6 -0
- package/dist/utils/documentManagerApi.js +19 -0
- package/dist/utils/documentNames.d.ts +24 -0
- package/dist/utils/documentNames.js +91 -0
- package/dist/utils/documentReferences.d.ts +9 -0
- package/dist/utils/documentReferences.js +11 -0
- package/dist/utils/entityReferences.d.ts +9 -0
- package/dist/utils/entityReferences.js +32 -0
- package/dist/utils/imageReferences.d.ts +9 -0
- package/dist/utils/imageReferences.js +11 -0
- package/dist/utils/planParser.d.ts +2 -2
- package/dist/utils/planParser.js +11 -11
- package/dist/utils/planParser.test.d.ts +1 -0
- package/dist/{__tests__ → utils}/planParser.test.js +22 -22
- package/dist/utils/planningReferences.d.ts +17 -0
- package/dist/utils/planningReferences.js +30 -0
- package/dist/utils/planningReferences.test.d.ts +1 -0
- package/dist/utils/planningReferences.test.js +17 -0
- package/dist/utils/taskActivity.d.ts +9 -0
- package/dist/utils/taskActivity.js +147 -0
- package/dist/utils/taskActivity.test.d.ts +1 -0
- package/dist/utils/taskActivity.test.js +34 -0
- package/dist/utils/taskDraftDefaults.d.ts +21 -0
- package/dist/utils/taskDraftDefaults.js +40 -0
- package/dist/utils/taskDraftDefaults.test.d.ts +1 -0
- package/dist/utils/taskDraftDefaults.test.js +34 -0
- package/dist/utils/taskNormalization.d.ts +25 -0
- package/dist/utils/taskNormalization.js +161 -0
- package/dist/utils/taskNormalization.test.d.ts +1 -0
- package/dist/utils/taskNormalization.test.js +158 -0
- package/dist/utils/taskReferences.d.ts +5 -0
- package/dist/utils/taskReferences.js +11 -0
- package/dist/utils/taskforceApiClient.d.ts +2 -0
- package/dist/utils/taskforceApiClient.js +101 -3
- package/dist/utils/taskforceApiClient.test.d.ts +1 -0
- package/dist/utils/taskforceApiClient.test.js +44 -0
- package/dist/utils/taxonomyFilterOptions.d.ts +2 -0
- package/dist/utils/taxonomyFilterOptions.js +17 -0
- package/dist/utils/taxonomyFormDefaults.d.ts +3 -0
- package/dist/utils/taxonomyFormDefaults.js +28 -0
- package/dist/utils/taxonomyFormDefaults.test.d.ts +1 -0
- package/dist/utils/taxonomyFormDefaults.test.js +63 -0
- package/dist/utils/taxonomyLifecycle.d.ts +3 -0
- package/dist/utils/taxonomyLifecycle.js +17 -0
- package/dist/utils/taxonomySorting.d.ts +7 -0
- package/dist/utils/taxonomySorting.js +74 -0
- package/dist/utils/theme.d.ts +15 -0
- package/dist/utils/theme.js +23 -0
- package/dist/utils/uiNotice.d.ts +7 -0
- package/dist/utils/uiNotice.js +6 -0
- package/dist/utils/uiNotice.test.d.ts +1 -0
- package/dist/utils/uiNotice.test.js +18 -0
- package/dist/utils/workspaceIdentity.d.ts +14 -0
- package/dist/utils/workspaceIdentity.js +21 -0
- package/dist/utils/workspaceIdentity.test.js +30 -1
- package/dist/utils/workspaceSyncPresentation.d.ts +20 -0
- package/dist/utils/workspaceSyncPresentation.js +21 -0
- package/dist/utils/workspaceSyncPresentation.test.d.ts +1 -0
- package/dist/utils/workspaceSyncPresentation.test.js +36 -0
- package/package.json +28 -23
- package/scripts/bootstrap-playwright-auth.mjs +155 -0
- package/scripts/check-compat-boundaries.mjs +41 -0
- package/scripts/check-env.ts +9 -2
- package/scripts/debug-login.mjs +32 -0
- package/scripts/migrate-planning-model.ts +233 -0
- package/scripts/migrate-task-data-assets.mjs +408 -0
- package/scripts/playwright-auth.sh +11 -0
- package/scripts/run-dev-proxy.sh +2 -0
- package/scripts/update-homebrew-formula.sh +2 -2
- package/scripts/with-esbuild.sh +11 -0
- package/src/resources/templates/WORKFLOW_V2_POLICY.md +1 -1
- package/src/resources/templates/workflow-sources/workflowDocs.mjs +62 -52
- package/src/resources/templates/workflows/collaborate.yaml +11 -5
- package/src/resources/templates/workflows/do.yaml +8 -2
- package/src/resources/templates/workflows/evaluate.yaml +13 -7
- package/src/resources/templates/workflows/execute.yaml +40 -23
- package/src/resources/templates/workflows/plan.yaml +15 -8
- package/src/resources/templates/workflows/review.yaml +14 -8
- package/dist/TaskforceScreenshot.d.ts +0 -11
- package/dist/TaskforceScreenshot.js +0 -113
- package/dist/compat/priorityCompat.d.ts +0 -2
- package/dist/compat/priorityCompat.js +0 -24
- package/dist/components/features/settings/TaxonomyManager 2.d.ts +0 -52
- package/dist/components/features/settings/TaxonomyManager 2.js +0 -75
- package/dist/components/task/TaskKanban 2.d.ts +0 -76
- package/dist/components/task/TaskKanban 2.js +0 -1004
- package/dist/components/task/TaskScreenshotSection.d.ts +0 -15
- package/dist/components/task/TaskScreenshotSection.js +0 -22
- package/dist/components/views/StandaloneLayout 2.d.ts +0 -8
- package/dist/components/views/StandaloneLayout 2.js +0 -1660
- package/dist/core/InitiativeTemplates.test.js +0 -50
- package/dist/core/ParentLifecycle.test.js +0 -98
- package/dist/migrations/clientMigrations.d.ts +0 -9
- package/dist/migrations/clientMigrations.js +0 -74
- package/dist/migrations/flags.d.ts +0 -6
- package/dist/migrations/flags.js +0 -8
- package/dist/migrations/taskMigrations.d.ts +0 -71
- package/dist/migrations/taskMigrations.js +0 -236
- package/dist/migrations/taskMigrations.test.js +0 -34
- package/dist/server/routes.billing.js +0 -991
- package/dist/server/routes.billing.test.js +0 -387
- package/dist/server/routes.sync.integration.test.js +0 -470
- package/dist/server/routes.sync.js +0 -1141
- package/dist/soak/syncSoakHarness.d.ts +0 -6
- package/dist/soak/syncSoakHarness.js +0 -10
- package/dist/ui/assets/index-0W5vyCpn.css +0 -1
- package/dist/ui/assets/index-MCZuNNfn.js +0 -23
- package/dist/ui/assets/logo_white_01-0Vx-iIIv.png +0 -0
- package/dist/ui/assets/vendor-dnd-BI1K77C1.js +0 -5
- package/dist/ui/assets/vendor-react-DdIDsh7f.js +0 -9
- package/dist/ui/assets/vendor-router-BtiNU7Dl.js +0 -3
- package/dist/utils/taskHierarchy.d.ts +0 -4
- package/dist/utils/taskHierarchy.js +0 -69
- package/dist/utils/taskHierarchy.test.js +0 -38
- package/dist/utils/taskParenting.d.ts +0 -3
- package/dist/utils/taskParenting.js +0 -24
- package/dist/utils/taskParenting.test.js +0 -21
- package/dist/utils/taskRelations.d.ts +0 -6
- package/dist/utils/taskRelations.js +0 -24
- package/dist/utils/taskRelations.test.js +0 -34
- package/scripts/check-compat-boundaries.js +0 -48
- /package/dist/{__tests__/planParser.test.d.ts → TaskforceCore.filter-persistence.test.d.ts} +0 -0
- /package/dist/{__tests__/releaseVersion.test.d.ts → TaskforceCore.routing.test.d.ts} +0 -0
- /package/dist/{__tests__/structuredCommentValidation.test.d.ts → annotatedAttachments/AnnotatedAttachmentSessionStore.test.d.ts} +0 -0
- /package/dist/{__tests__/structuredDocValidation.test.d.ts → annotatedAttachments/payload.test.d.ts} +0 -0
- /package/dist/{__tests__/templateEngine.test.d.ts → annotatedAttachments/types.js} +0 -0
- /package/dist/{__tests__/templates.test.d.ts → compat/workspaceSyncCompat.test.d.ts} +0 -0
- /package/dist/{__tests__/workflowDocumentFixtures.test.d.ts → components/features/AgentsModule.test.d.ts} +0 -0
- /package/dist/{__tests__/workflowExportMatrix.test.d.ts → components/features/AnnotatedAttachmentWorkspace.test.d.ts} +0 -0
- /package/dist/{__tests__/workflowExportSnapshots.test.d.ts → components/features/DocumentWorkspace.test.d.ts} +0 -0
- /package/dist/{__tests__/workflowGeneration.test.d.ts → components/features/settings/TaxonomyLibraryManager.test.d.ts} +0 -0
- /package/dist/{__tests__/workflowV2Policy.test.d.ts → components/task/TaskActionHeader.test.d.ts} +0 -0
- /package/dist/{core/InitiativeTemplates.test.d.ts → components/task/TaskKanban.planning-drop.test.d.ts} +0 -0
- /package/dist/{core/ParentLifecycle.test.d.ts → components/task/TaskStatusActions.test.d.ts} +0 -0
- /package/dist/{migrations/taskMigrations.test.d.ts → components/ui/TopNoticeLayer.test.d.ts} +0 -0
- /package/dist/{server/routes.billing.test.d.ts → components/views/AppShellHeader.test.d.ts} +0 -0
- /package/dist/{server/routes.sync.integration.test.d.ts → components/views/PlanComparisonPage.test.d.ts} +0 -0
- /package/dist/{utils/taskHierarchy.test.d.ts → components/views/chrome/WorkspaceChromeSections.test.d.ts} +0 -0
- /package/dist/{utils/taskParenting.test.d.ts → components/views/chrome/workspaceChrome.test.d.ts} +0 -0
- /package/dist/{utils/taskRelations.test.d.ts → components/views/panels/DocumentFilterToolbar.test.d.ts} +0 -0
- /package/dist/public/{android-chrome-192x192.png → archive/phoenix/android-chrome-192x192.png} +0 -0
- /package/dist/public/{android-chrome-512x512.png → archive/phoenix/android-chrome-512x512.png} +0 -0
- /package/dist/public/{favicon-16x16.png → archive/phoenix/favicon-16x16.png} +0 -0
- /package/dist/public/{favicon-32x32.png → archive/phoenix/favicon-32x32.png} +0 -0
- /package/dist/public/{favicon.png → archive/phoenix/favicon.png} +0 -0
- /package/dist/{__tests__ → services}/workflowExportSnapshots.test.js +0 -0
- /package/dist/{__tests__ → services}/workflowGeneration.test.js +0 -0
- /package/dist/ui/favicon/{android-chrome-192x192.png → archive/phoenix/android-chrome-192x192.png} +0 -0
- /package/dist/ui/favicon/{android-chrome-512x512.png → archive/phoenix/android-chrome-512x512.png} +0 -0
- /package/dist/ui/favicon/{favicon-16x16.png → archive/phoenix/favicon-16x16.png} +0 -0
- /package/dist/ui/favicon/{favicon-32x32.png → archive/phoenix/favicon-32x32.png} +0 -0
- /package/dist/ui/favicon/{favicon.png → archive/phoenix/favicon.png} +0 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
import { TaskforceRuleError } from './types.js';
|
|
3
|
+
import { hashAuthToken, hashPassword, generateUuidV7 } from './shared.js';
|
|
4
|
+
/**
|
|
5
|
+
* Handles one-time auth token lifecycle (email verification, password reset, invite acceptance)
|
|
6
|
+
* and the thin read/write helpers for email-verification and password state.
|
|
7
|
+
*/
|
|
8
|
+
export class AuthTokenService {
|
|
9
|
+
db;
|
|
10
|
+
tenantId;
|
|
11
|
+
constructor(db, tenantId) {
|
|
12
|
+
this.db = db;
|
|
13
|
+
this.tenantId = tenantId;
|
|
14
|
+
}
|
|
15
|
+
generateEntityId(prefix) {
|
|
16
|
+
return `${prefix}-${generateUuidV7()}`;
|
|
17
|
+
}
|
|
18
|
+
// ─── Internal token primitives ────────────────────────────────────────────
|
|
19
|
+
// Not truly private so that Taskforce.ts invite methods can delegate through
|
|
20
|
+
// this service without needing to know the DB schema.
|
|
21
|
+
createOneTimeAuthToken(userId, purpose, ttlSeconds, workspaceId) {
|
|
22
|
+
const normalizedUserId = String(userId || '').trim();
|
|
23
|
+
if (!normalizedUserId) {
|
|
24
|
+
throw new TaskforceRuleError('userId is required', 400, 'USER_ID_REQUIRED');
|
|
25
|
+
}
|
|
26
|
+
const normalizedWorkspaceId = String(workspaceId || '').trim() || null;
|
|
27
|
+
const safeTtl = Math.max(60, Math.floor(Number(ttlSeconds) || 0));
|
|
28
|
+
const now = new Date();
|
|
29
|
+
const expiresAt = new Date(now.getTime() + safeTtl * 1000).toISOString();
|
|
30
|
+
const token = randomBytes(24).toString('hex');
|
|
31
|
+
const tokenHash = hashAuthToken(token);
|
|
32
|
+
const tokenId = this.generateEntityId(`auth-${purpose}`);
|
|
33
|
+
// Keep previously issued tokens valid until they are consumed or expired.
|
|
34
|
+
// This avoids accidental invalidation when users request a resend and click an older email link.
|
|
35
|
+
this.db.prepare(`
|
|
36
|
+
DELETE FROM auth_tokens
|
|
37
|
+
WHERE tenant_id = ? AND user_id = ? AND purpose = ? AND (used_at IS NOT NULL OR expires_at < ?)
|
|
38
|
+
`).run(this.tenantId, normalizedUserId, purpose, now.toISOString());
|
|
39
|
+
this.db.prepare(`
|
|
40
|
+
INSERT INTO auth_tokens (id, tenant_id, user_id, workspace_id, purpose, token_hash, expires_at, used_at, revoked_at, created_at)
|
|
41
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?)
|
|
42
|
+
`).run(tokenId, this.tenantId, normalizedUserId, normalizedWorkspaceId, purpose, tokenHash, expiresAt, now.toISOString());
|
|
43
|
+
return token;
|
|
44
|
+
}
|
|
45
|
+
consumeOneTimeAuthToken(token, purpose) {
|
|
46
|
+
const normalized = String(token || '').trim();
|
|
47
|
+
if (!normalized)
|
|
48
|
+
return null;
|
|
49
|
+
const tokenHash = hashAuthToken(normalized);
|
|
50
|
+
const now = new Date().toISOString();
|
|
51
|
+
const row = this.db.prepare(`
|
|
52
|
+
SELECT id, user_id, workspace_id, expires_at, used_at, revoked_at
|
|
53
|
+
FROM auth_tokens
|
|
54
|
+
WHERE tenant_id = ? AND purpose = ? AND token_hash = ?
|
|
55
|
+
LIMIT 1
|
|
56
|
+
`).get(this.tenantId, purpose, tokenHash);
|
|
57
|
+
if (!row?.id || !row.user_id || row.used_at || row.revoked_at)
|
|
58
|
+
return null;
|
|
59
|
+
if (!row.expires_at || row.expires_at < now)
|
|
60
|
+
return null;
|
|
61
|
+
this.db.prepare(`
|
|
62
|
+
UPDATE auth_tokens
|
|
63
|
+
SET used_at = ?
|
|
64
|
+
WHERE tenant_id = ? AND id = ?
|
|
65
|
+
`).run(now, this.tenantId, row.id);
|
|
66
|
+
return {
|
|
67
|
+
userId: row.user_id,
|
|
68
|
+
workspaceId: row.workspace_id ? String(row.workspace_id) : undefined
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
inspectOneTimeAuthToken(token, purpose) {
|
|
72
|
+
const normalized = String(token || '').trim();
|
|
73
|
+
if (!normalized)
|
|
74
|
+
return { state: 'missing' };
|
|
75
|
+
const tokenHash = hashAuthToken(normalized);
|
|
76
|
+
const now = new Date().toISOString();
|
|
77
|
+
const row = this.db.prepare(`
|
|
78
|
+
SELECT user_id, workspace_id, expires_at, used_at, revoked_at
|
|
79
|
+
FROM auth_tokens
|
|
80
|
+
WHERE tenant_id = ? AND purpose = ? AND token_hash = ?
|
|
81
|
+
LIMIT 1
|
|
82
|
+
`).get(this.tenantId, purpose, tokenHash);
|
|
83
|
+
if (!row?.user_id)
|
|
84
|
+
return { state: 'missing' };
|
|
85
|
+
const workspaceId = row.workspace_id ? String(row.workspace_id) : undefined;
|
|
86
|
+
if (row.revoked_at)
|
|
87
|
+
return { state: 'revoked', userId: row.user_id, workspaceId };
|
|
88
|
+
if (row.used_at)
|
|
89
|
+
return { state: 'used', userId: row.user_id, workspaceId };
|
|
90
|
+
if (!row.expires_at || row.expires_at < now)
|
|
91
|
+
return { state: 'expired', userId: row.user_id, workspaceId };
|
|
92
|
+
return { state: 'valid', userId: row.user_id, workspaceId };
|
|
93
|
+
}
|
|
94
|
+
// ─── Public methods ───────────────────────────────────────────────────────
|
|
95
|
+
isEmailVerified(email) {
|
|
96
|
+
const normalizedEmail = String(email || '').trim().toLowerCase();
|
|
97
|
+
if (!normalizedEmail)
|
|
98
|
+
return false;
|
|
99
|
+
const row = this.db.prepare(`
|
|
100
|
+
SELECT email_verified_at
|
|
101
|
+
FROM users
|
|
102
|
+
WHERE tenant_id = ? AND email = ?
|
|
103
|
+
LIMIT 1
|
|
104
|
+
`).get(this.tenantId, normalizedEmail);
|
|
105
|
+
return Boolean(row?.email_verified_at);
|
|
106
|
+
}
|
|
107
|
+
markEmailVerified(email) {
|
|
108
|
+
const normalizedEmail = String(email || '').trim().toLowerCase();
|
|
109
|
+
if (!normalizedEmail || !normalizedEmail.includes('@'))
|
|
110
|
+
return false;
|
|
111
|
+
const now = new Date().toISOString();
|
|
112
|
+
const result = this.db.prepare(`
|
|
113
|
+
UPDATE users
|
|
114
|
+
SET email_verified_at = COALESCE(email_verified_at, ?), updated_at = ?
|
|
115
|
+
WHERE tenant_id = ? AND email = ?
|
|
116
|
+
`).run(now, now, this.tenantId, normalizedEmail);
|
|
117
|
+
const changed = Number(result?.changes || 0);
|
|
118
|
+
return changed > 0;
|
|
119
|
+
}
|
|
120
|
+
requestEmailVerification(input) {
|
|
121
|
+
const normalizedEmail = String(input.email || '').trim().toLowerCase();
|
|
122
|
+
if (!normalizedEmail || !normalizedEmail.includes('@')) {
|
|
123
|
+
throw new TaskforceRuleError('Valid email is required', 400, 'EMAIL_REQUIRED');
|
|
124
|
+
}
|
|
125
|
+
const row = this.db.prepare(`
|
|
126
|
+
SELECT id, email_verified_at
|
|
127
|
+
FROM users
|
|
128
|
+
WHERE tenant_id = ? AND email = ?
|
|
129
|
+
LIMIT 1
|
|
130
|
+
`).get(this.tenantId, normalizedEmail);
|
|
131
|
+
if (!row?.id || row.email_verified_at)
|
|
132
|
+
return {};
|
|
133
|
+
const token = this.createOneTimeAuthToken(row.id, 'email_verification', input.ttlSeconds || 60 * 60 * 24);
|
|
134
|
+
return { token };
|
|
135
|
+
}
|
|
136
|
+
verifyEmailWithToken(token) {
|
|
137
|
+
const consumed = this.consumeOneTimeAuthToken(token, 'email_verification');
|
|
138
|
+
if (!consumed?.userId)
|
|
139
|
+
return false;
|
|
140
|
+
const now = new Date().toISOString();
|
|
141
|
+
this.db.prepare(`
|
|
142
|
+
UPDATE users
|
|
143
|
+
SET email_verified_at = ?, updated_at = ?
|
|
144
|
+
WHERE tenant_id = ? AND id = ?
|
|
145
|
+
`).run(now, now, this.tenantId, consumed.userId);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
requestPasswordReset(input) {
|
|
149
|
+
const normalizedEmail = String(input.email || '').trim().toLowerCase();
|
|
150
|
+
if (!normalizedEmail || !normalizedEmail.includes('@')) {
|
|
151
|
+
throw new TaskforceRuleError('Valid email is required', 400, 'EMAIL_REQUIRED');
|
|
152
|
+
}
|
|
153
|
+
const row = this.db.prepare(`
|
|
154
|
+
SELECT id
|
|
155
|
+
FROM users
|
|
156
|
+
WHERE tenant_id = ? AND email = ?
|
|
157
|
+
LIMIT 1
|
|
158
|
+
`).get(this.tenantId, normalizedEmail);
|
|
159
|
+
if (!row?.id)
|
|
160
|
+
return {};
|
|
161
|
+
const token = this.createOneTimeAuthToken(row.id, 'password_reset', input.ttlSeconds || 60 * 60);
|
|
162
|
+
return { token };
|
|
163
|
+
}
|
|
164
|
+
requestInviteAcceptance(input) {
|
|
165
|
+
const userId = String(input.userId || '').trim();
|
|
166
|
+
const workspaceId = String(input.workspaceId || '').trim();
|
|
167
|
+
if (!userId) {
|
|
168
|
+
throw new TaskforceRuleError('userId is required', 400, 'USER_ID_REQUIRED');
|
|
169
|
+
}
|
|
170
|
+
if (!workspaceId) {
|
|
171
|
+
throw new TaskforceRuleError('workspaceId is required', 400, 'WORKSPACE_ID_REQUIRED');
|
|
172
|
+
}
|
|
173
|
+
const user = this.db.prepare(`
|
|
174
|
+
SELECT id FROM users WHERE tenant_id = ? AND id = ? LIMIT 1
|
|
175
|
+
`).get(this.tenantId, userId);
|
|
176
|
+
if (!user?.id) {
|
|
177
|
+
throw new TaskforceRuleError('User not found', 404, 'USER_NOT_FOUND');
|
|
178
|
+
}
|
|
179
|
+
const membership = this.db.prepare(`
|
|
180
|
+
SELECT workspace_id
|
|
181
|
+
FROM workspace_memberships
|
|
182
|
+
WHERE tenant_id = ? AND user_id = ? AND workspace_id = ?
|
|
183
|
+
LIMIT 1
|
|
184
|
+
`).get(this.tenantId, userId, workspaceId);
|
|
185
|
+
if (!membership?.workspace_id) {
|
|
186
|
+
throw new TaskforceRuleError('Workspace membership not found', 404, 'WORKSPACE_MEMBERSHIP_NOT_FOUND');
|
|
187
|
+
}
|
|
188
|
+
const token = this.createOneTimeAuthToken(userId, 'invite_accept', input.ttlSeconds || 60 * 60 * 24 * 7, workspaceId);
|
|
189
|
+
return { token };
|
|
190
|
+
}
|
|
191
|
+
resetPasswordWithToken(input) {
|
|
192
|
+
const password = String(input.password || '');
|
|
193
|
+
if (!password || password.length < 8) {
|
|
194
|
+
throw new TaskforceRuleError('Password must be at least 8 characters', 400, 'PASSWORD_TOO_SHORT');
|
|
195
|
+
}
|
|
196
|
+
const consumed = this.consumeOneTimeAuthToken(input.token, 'password_reset');
|
|
197
|
+
if (!consumed?.userId)
|
|
198
|
+
return false;
|
|
199
|
+
const now = new Date().toISOString();
|
|
200
|
+
this.db.prepare(`
|
|
201
|
+
UPDATE users
|
|
202
|
+
SET password_hash = ?, auth_provider = ?, is_disabled = 0, updated_at = ?
|
|
203
|
+
WHERE tenant_id = ? AND id = ?
|
|
204
|
+
`).run(hashPassword(password), 'password', now, this.tenantId, consumed.userId);
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -18,7 +18,7 @@ afterEach(() => {
|
|
|
18
18
|
}
|
|
19
19
|
});
|
|
20
20
|
describe('TaskforceCore - createdBy Field', () => {
|
|
21
|
-
it('
|
|
21
|
+
it('does not persist new generic AI createdBy values on creation', () => {
|
|
22
22
|
const projectRoot = createProjectRoot();
|
|
23
23
|
createdRoots.push(projectRoot);
|
|
24
24
|
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
@@ -26,7 +26,25 @@ describe('TaskforceCore - createdBy Field', () => {
|
|
|
26
26
|
title: 'AI Task',
|
|
27
27
|
createdBy: 'AI Agent'
|
|
28
28
|
});
|
|
29
|
-
expect(task.createdBy).
|
|
29
|
+
expect(task.createdBy).toBeUndefined();
|
|
30
|
+
});
|
|
31
|
+
it('preserves legacy generic AI createdBy values when hydrating stored tasks', () => {
|
|
32
|
+
const projectRoot = createProjectRoot();
|
|
33
|
+
createdRoots.push(projectRoot);
|
|
34
|
+
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
35
|
+
const task = core.createTask({ title: 'Legacy AI task' });
|
|
36
|
+
core.db.prepare(`
|
|
37
|
+
UPDATE tasks
|
|
38
|
+
SET created_by = ?
|
|
39
|
+
WHERE tenant_id = ? AND workspace_id = ? AND id = ?
|
|
40
|
+
`).run('agent', 'default', 'default', task.id);
|
|
41
|
+
const hydrated = core.getTask(task.id);
|
|
42
|
+
expect(hydrated?.createdBy).toBe('agent');
|
|
43
|
+
expect(hydrated?.createdByActor).toEqual(expect.objectContaining({
|
|
44
|
+
id: 'agent',
|
|
45
|
+
kind: 'ai',
|
|
46
|
+
isNamed: false
|
|
47
|
+
}));
|
|
30
48
|
});
|
|
31
49
|
it('defaults createdBy to undefined if not provided', () => {
|
|
32
50
|
const projectRoot = createProjectRoot();
|
|
@@ -35,7 +53,7 @@ describe('TaskforceCore - createdBy Field', () => {
|
|
|
35
53
|
const task = core.createTask({ title: 'User Task' });
|
|
36
54
|
expect(task.createdBy).toBeUndefined();
|
|
37
55
|
});
|
|
38
|
-
it('
|
|
56
|
+
it('treats legacy [User] title hints as unassigned when assignee is missing', () => {
|
|
39
57
|
const projectRoot = createProjectRoot();
|
|
40
58
|
createdRoots.push(projectRoot);
|
|
41
59
|
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
@@ -43,9 +61,9 @@ describe('TaskforceCore - createdBy Field', () => {
|
|
|
43
61
|
title: '[User] Configure backups',
|
|
44
62
|
createdBy: 'AI Agent'
|
|
45
63
|
});
|
|
46
|
-
expect(task.assignee).toBe('
|
|
64
|
+
expect(task.assignee).toBe('unassigned');
|
|
47
65
|
});
|
|
48
|
-
it('
|
|
66
|
+
it('keeps legacy owner hints unassigned for compatibility', () => {
|
|
49
67
|
const projectRoot = createProjectRoot();
|
|
50
68
|
createdRoots.push(projectRoot);
|
|
51
69
|
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
@@ -55,6 +73,116 @@ describe('TaskforceCore - createdBy Field', () => {
|
|
|
55
73
|
assignee: 'unassigned',
|
|
56
74
|
createdBy: 'AI Agent'
|
|
57
75
|
});
|
|
58
|
-
expect(task.assignee).toBe('
|
|
76
|
+
expect(task.assignee).toBe('unassigned');
|
|
77
|
+
});
|
|
78
|
+
it('defaults AI-authored tasks to unassigned when no explicit assignee is provided', () => {
|
|
79
|
+
const projectRoot = createProjectRoot();
|
|
80
|
+
createdRoots.push(projectRoot);
|
|
81
|
+
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
82
|
+
const task = core.createTask({
|
|
83
|
+
title: 'AI-created task',
|
|
84
|
+
createdBy: 'AI Agent'
|
|
85
|
+
});
|
|
86
|
+
expect(task.assignee).toBe('unassigned');
|
|
87
|
+
});
|
|
88
|
+
it('repairs unknown member assignees to unassigned', () => {
|
|
89
|
+
const projectRoot = createProjectRoot();
|
|
90
|
+
createdRoots.push(projectRoot);
|
|
91
|
+
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
92
|
+
const task = core.createTask({
|
|
93
|
+
title: 'Assign to workspace member',
|
|
94
|
+
assignee: 'member-123',
|
|
95
|
+
createdBy: 'AI Agent'
|
|
96
|
+
});
|
|
97
|
+
expect(task.assignee).toBe('unassigned');
|
|
98
|
+
});
|
|
99
|
+
it('normalizes legacy agent assignees to unassigned on create', () => {
|
|
100
|
+
const projectRoot = createProjectRoot();
|
|
101
|
+
createdRoots.push(projectRoot);
|
|
102
|
+
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
103
|
+
const task = core.createTask({
|
|
104
|
+
title: 'Legacy AI-owned task',
|
|
105
|
+
assignee: 'agent',
|
|
106
|
+
createdBy: 'AI Agent'
|
|
107
|
+
});
|
|
108
|
+
expect(task.assignee).toBe('unassigned');
|
|
109
|
+
});
|
|
110
|
+
it('preserves registered AI profile assignees', () => {
|
|
111
|
+
const projectRoot = createProjectRoot();
|
|
112
|
+
createdRoots.push(projectRoot);
|
|
113
|
+
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
114
|
+
core.createWorkspace({ workspaceId: 'workspace-1', name: 'Workspace 1' });
|
|
115
|
+
const { profile } = core.resolveAiProfile({
|
|
116
|
+
workspaceId: 'workspace-1',
|
|
117
|
+
name: 'Codex'
|
|
118
|
+
});
|
|
119
|
+
const task = core.createTask({
|
|
120
|
+
title: 'Assign to Codex',
|
|
121
|
+
assignee: profile.id
|
|
122
|
+
}, { workspaceId: 'workspace-1' });
|
|
123
|
+
expect(task.assignee).toBe(profile.id);
|
|
124
|
+
});
|
|
125
|
+
it('self-repairs stored assignees that no longer exist back to unassigned', () => {
|
|
126
|
+
const projectRoot = createProjectRoot();
|
|
127
|
+
createdRoots.push(projectRoot);
|
|
128
|
+
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
129
|
+
core.createWorkspace({ workspaceId: 'workspace-1', name: 'Workspace 1' });
|
|
130
|
+
const { profile } = core.resolveAiProfile({
|
|
131
|
+
workspaceId: 'workspace-1',
|
|
132
|
+
name: 'Codex'
|
|
133
|
+
});
|
|
134
|
+
const task = core.createTask({
|
|
135
|
+
title: 'Task with stale assignee',
|
|
136
|
+
assignee: profile.id
|
|
137
|
+
}, { workspaceId: 'workspace-1' });
|
|
138
|
+
core.db.prepare(`
|
|
139
|
+
UPDATE tasks
|
|
140
|
+
SET assignee = ?
|
|
141
|
+
WHERE tenant_id = ? AND workspace_id = ? AND id = ?
|
|
142
|
+
`).run('ghost-assignee', 'default', 'workspace-1', task.id);
|
|
143
|
+
const repaired = core.getTask(task.id, 'workspace-1');
|
|
144
|
+
expect(repaired?.assignee).toBe('unassigned');
|
|
145
|
+
const persisted = core.db.prepare(`
|
|
146
|
+
SELECT assignee
|
|
147
|
+
FROM tasks
|
|
148
|
+
WHERE tenant_id = ? AND workspace_id = ? AND id = ?
|
|
149
|
+
`).get('default', 'workspace-1', task.id);
|
|
150
|
+
expect(persisted?.assignee).toBe('unassigned');
|
|
151
|
+
});
|
|
152
|
+
it('normalizes escaped newlines for AI-authored task descriptions on create', () => {
|
|
153
|
+
const projectRoot = createProjectRoot();
|
|
154
|
+
createdRoots.push(projectRoot);
|
|
155
|
+
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
156
|
+
const task = core.createTask({
|
|
157
|
+
title: 'AI task with escaped description',
|
|
158
|
+
description: 'Phase 1\\n\\nPhase 2',
|
|
159
|
+
createdBy: 'AI Agent'
|
|
160
|
+
});
|
|
161
|
+
expect(task.description).toBe('Phase 1\n\nPhase 2');
|
|
162
|
+
});
|
|
163
|
+
it('preserves literal escaped newlines for manual task descriptions on create', () => {
|
|
164
|
+
const projectRoot = createProjectRoot();
|
|
165
|
+
createdRoots.push(projectRoot);
|
|
166
|
+
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
167
|
+
const task = core.createTask({
|
|
168
|
+
title: 'Manual task with literal backslashes',
|
|
169
|
+
description: 'Keep literal \\n\\n text'
|
|
170
|
+
});
|
|
171
|
+
expect(task.description).toBe('Keep literal \\n\\n text');
|
|
172
|
+
});
|
|
173
|
+
it('normalizes escaped newlines when updating an AI-authored task description', () => {
|
|
174
|
+
const projectRoot = createProjectRoot();
|
|
175
|
+
createdRoots.push(projectRoot);
|
|
176
|
+
const core = new TaskforceCore({ projectRoot, storagePath: '.taskforce' });
|
|
177
|
+
const task = core.createTask({
|
|
178
|
+
title: 'AI task to update',
|
|
179
|
+
createdBy: 'AI Agent'
|
|
180
|
+
});
|
|
181
|
+
const updated = core.updateTask(task.id, {
|
|
182
|
+
description: 'Line 1\\r\\n\\r\\nLine 2',
|
|
183
|
+
createdBy: 'AI Agent'
|
|
184
|
+
});
|
|
185
|
+
expect(updated?.description).toBe('Line 1\n\nLine 2');
|
|
186
|
+
expect(updated?.createdBy).toBeUndefined();
|
|
59
187
|
});
|
|
60
188
|
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { DatabaseAdapter } from '../storage/databaseAdapter.js';
|
|
2
|
+
import type { DeletedTaskDetails, DeletedTaskRecord, DeletedTaskSource, TaskActor, TaskItem } from './Taskforce.js';
|
|
3
|
+
export interface DeletedTaskLifecycleServiceDeps {
|
|
4
|
+
db: DatabaseAdapter;
|
|
5
|
+
tenantId: string;
|
|
6
|
+
createRuleError: (message: string, statusCode?: number, code?: string) => Error;
|
|
7
|
+
generateEntityId: (prefix: string) => string;
|
|
8
|
+
normalizeWorkspaceId: (workspaceId: string | undefined) => string;
|
|
9
|
+
resolveWorkspaceActor: (workspaceId: string, actorRef: unknown) => TaskActor | null;
|
|
10
|
+
sanitizeOrderInDay: (value: unknown) => number | null;
|
|
11
|
+
sanitizeDateOnly: (value: unknown) => string | null;
|
|
12
|
+
normalizeTaskEventActor: (actorRef: unknown, workspaceId: string) => {
|
|
13
|
+
actor: string;
|
|
14
|
+
actorType: 'human' | 'ai' | 'system';
|
|
15
|
+
actorProfile: TaskActor | null;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare function normalizeTaskSnapshotForRestoreService(deps: DeletedTaskLifecycleServiceDeps, task: TaskItem): TaskItem;
|
|
19
|
+
export declare function mapDeletedTaskRowService(deps: DeletedTaskLifecycleServiceDeps, row: Record<string, unknown>): DeletedTaskRecord | null;
|
|
20
|
+
export declare function pruneDeletedTasksService(deps: DeletedTaskLifecycleServiceDeps, workspaceId?: string, nowIso?: string): number;
|
|
21
|
+
export declare function purgeExpiredDeletedTasksService(deps: DeletedTaskLifecycleServiceDeps, workspaceId?: string, retentionDays?: number): number;
|
|
22
|
+
export declare function addDeletedTaskRecordService(deps: DeletedTaskLifecycleServiceDeps, input: {
|
|
23
|
+
task: TaskItem;
|
|
24
|
+
workspaceId: string;
|
|
25
|
+
actor: unknown;
|
|
26
|
+
source?: DeletedTaskSource;
|
|
27
|
+
details?: DeletedTaskDetails;
|
|
28
|
+
deletedAt?: string;
|
|
29
|
+
}): DeletedTaskRecord;
|
|
30
|
+
export declare function listDeletedTasksService(deps: DeletedTaskLifecycleServiceDeps, workspaceId?: string): {
|
|
31
|
+
deleted: DeletedTaskRecord[];
|
|
32
|
+
};
|
|
33
|
+
export declare function getDeletedTaskService(deps: DeletedTaskLifecycleServiceDeps, id: string, workspaceId?: string): DeletedTaskRecord | null;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
const DELETED_TASK_RETENTION_MS = 90 * 24 * 60 * 60 * 1000;
|
|
2
|
+
function normalizeDeletedTaskDetails(details) {
|
|
3
|
+
if (!details || typeof details !== 'object' || Array.isArray(details))
|
|
4
|
+
return {};
|
|
5
|
+
return details;
|
|
6
|
+
}
|
|
7
|
+
export function normalizeTaskSnapshotForRestoreService(deps, task) {
|
|
8
|
+
return {
|
|
9
|
+
...task,
|
|
10
|
+
comments: Array.isArray(task.comments) ? task.comments : [],
|
|
11
|
+
taskEvents: Array.isArray(task.taskEvents) ? task.taskEvents : [],
|
|
12
|
+
activity: Array.isArray(task.activity) ? task.activity : [],
|
|
13
|
+
attachments: Array.isArray(task.attachments) ? task.attachments : [],
|
|
14
|
+
orderInDay: deps.sanitizeOrderInDay(task.orderInDay),
|
|
15
|
+
scheduledDate: deps.sanitizeDateOnly(task.scheduledDate),
|
|
16
|
+
dueDate: deps.sanitizeDateOnly(task.dueDate)
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function mapDeletedTaskRowService(deps, row) {
|
|
20
|
+
const workspaceId = deps.normalizeWorkspaceId(typeof row.workspace_id === 'string' ? row.workspace_id : undefined);
|
|
21
|
+
const actor = String(row.actor || 'system').trim() || 'system';
|
|
22
|
+
const source = String(row.source || 'system').trim();
|
|
23
|
+
let details = {};
|
|
24
|
+
let taskSnapshot = null;
|
|
25
|
+
try {
|
|
26
|
+
details = row.details_json ? JSON.parse(String(row.details_json)) : {};
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
details = {};
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const parsed = row.task_snapshot_json ? JSON.parse(String(row.task_snapshot_json)) : null;
|
|
33
|
+
taskSnapshot = parsed && typeof parsed === 'object'
|
|
34
|
+
? normalizeTaskSnapshotForRestoreService(deps, parsed)
|
|
35
|
+
: null;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
taskSnapshot = null;
|
|
39
|
+
}
|
|
40
|
+
if (!taskSnapshot)
|
|
41
|
+
return null;
|
|
42
|
+
return {
|
|
43
|
+
id: String(row.id || '').trim(),
|
|
44
|
+
taskId: String(row.task_id || '').trim(),
|
|
45
|
+
workspaceId,
|
|
46
|
+
deletedAt: String(row.deleted_at || '').trim(),
|
|
47
|
+
actor,
|
|
48
|
+
actorType: row.actor_type === 'ai' || row.actor_type === 'human' || row.actor_type === 'system'
|
|
49
|
+
? row.actor_type
|
|
50
|
+
: 'system',
|
|
51
|
+
source: source === 'ui' || source === 'mcp' || source === 'sync' || source === 'workspace-delete' ? source : 'system',
|
|
52
|
+
details,
|
|
53
|
+
taskSnapshot,
|
|
54
|
+
actorProfile: actor === 'system' ? null : deps.resolveWorkspaceActor(workspaceId, actor)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function pruneDeletedTasksService(deps, workspaceId, nowIso) {
|
|
58
|
+
const cutoff = new Date((nowIso ? Date.parse(nowIso) : Date.now()) - DELETED_TASK_RETENTION_MS).toISOString();
|
|
59
|
+
if (workspaceId) {
|
|
60
|
+
const normalizedWorkspaceId = deps.normalizeWorkspaceId(workspaceId);
|
|
61
|
+
const result = deps.db.prepare(`
|
|
62
|
+
DELETE FROM deleted_tasks
|
|
63
|
+
WHERE tenant_id = ?
|
|
64
|
+
AND workspace_id = ?
|
|
65
|
+
AND deleted_at < ?
|
|
66
|
+
`).run(deps.tenantId, normalizedWorkspaceId, cutoff);
|
|
67
|
+
return Number(result?.changes || 0);
|
|
68
|
+
}
|
|
69
|
+
const result = deps.db.prepare(`
|
|
70
|
+
DELETE FROM deleted_tasks
|
|
71
|
+
WHERE tenant_id = ?
|
|
72
|
+
AND deleted_at < ?
|
|
73
|
+
`).run(deps.tenantId, cutoff);
|
|
74
|
+
return Number(result?.changes || 0);
|
|
75
|
+
}
|
|
76
|
+
export function purgeExpiredDeletedTasksService(deps, workspaceId, retentionDays = 90) {
|
|
77
|
+
const cutoff = new Date(Date.now() - (retentionDays * 24 * 60 * 60 * 1000)).toISOString();
|
|
78
|
+
if (typeof workspaceId === 'string' && workspaceId.trim().length > 0) {
|
|
79
|
+
const normalizedWorkspaceId = deps.normalizeWorkspaceId(workspaceId);
|
|
80
|
+
const result = deps.db.prepare(`
|
|
81
|
+
DELETE FROM deleted_tasks
|
|
82
|
+
WHERE tenant_id = ?
|
|
83
|
+
AND workspace_id = ?
|
|
84
|
+
AND deleted_at < ?
|
|
85
|
+
`).run(deps.tenantId, normalizedWorkspaceId, cutoff);
|
|
86
|
+
return Number(result?.changes || 0);
|
|
87
|
+
}
|
|
88
|
+
const result = deps.db.prepare(`
|
|
89
|
+
DELETE FROM deleted_tasks
|
|
90
|
+
WHERE tenant_id = ?
|
|
91
|
+
AND deleted_at < ?
|
|
92
|
+
`).run(deps.tenantId, cutoff);
|
|
93
|
+
return Number(result?.changes || 0);
|
|
94
|
+
}
|
|
95
|
+
export function addDeletedTaskRecordService(deps, input) {
|
|
96
|
+
const workspaceId = deps.normalizeWorkspaceId(input.workspaceId);
|
|
97
|
+
const task = normalizeTaskSnapshotForRestoreService(deps, input.task);
|
|
98
|
+
const deletedAt = String(input.deletedAt || '').trim() || new Date().toISOString();
|
|
99
|
+
const { actor, actorType } = deps.normalizeTaskEventActor(input.actor, workspaceId);
|
|
100
|
+
const recordId = deps.generateEntityId('deleted-task');
|
|
101
|
+
const details = normalizeDeletedTaskDetails(input.details);
|
|
102
|
+
deps.db.prepare(`
|
|
103
|
+
INSERT INTO deleted_tasks (
|
|
104
|
+
id, tenant_id, workspace_id, task_id, deleted_at, actor, actor_type, source, details_json, task_snapshot_json
|
|
105
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
106
|
+
ON CONFLICT (tenant_id, workspace_id, task_id) DO UPDATE SET
|
|
107
|
+
deleted_at = excluded.deleted_at,
|
|
108
|
+
actor = excluded.actor,
|
|
109
|
+
actor_type = excluded.actor_type,
|
|
110
|
+
source = excluded.source,
|
|
111
|
+
details_json = excluded.details_json,
|
|
112
|
+
task_snapshot_json = excluded.task_snapshot_json
|
|
113
|
+
`).run(recordId, deps.tenantId, workspaceId, task.id, deletedAt, actor, actorType, input.source || 'system', JSON.stringify(details), JSON.stringify(task));
|
|
114
|
+
const stored = deps.db.prepare(`
|
|
115
|
+
SELECT *
|
|
116
|
+
FROM deleted_tasks
|
|
117
|
+
WHERE tenant_id = ? AND workspace_id = ? AND task_id = ?
|
|
118
|
+
LIMIT 1
|
|
119
|
+
`).get(deps.tenantId, workspaceId, task.id);
|
|
120
|
+
const mapped = stored ? mapDeletedTaskRowService(deps, stored) : null;
|
|
121
|
+
if (!mapped) {
|
|
122
|
+
throw deps.createRuleError('Failed to persist deleted task history', 500, 'DELETED_TASK_PERSIST_FAILED');
|
|
123
|
+
}
|
|
124
|
+
return mapped;
|
|
125
|
+
}
|
|
126
|
+
export function listDeletedTasksService(deps, workspaceId) {
|
|
127
|
+
const normalizedWorkspaceId = deps.normalizeWorkspaceId(workspaceId);
|
|
128
|
+
purgeExpiredDeletedTasksService(deps, normalizedWorkspaceId);
|
|
129
|
+
const rows = deps.db.prepare(`
|
|
130
|
+
SELECT *
|
|
131
|
+
FROM deleted_tasks
|
|
132
|
+
WHERE tenant_id = ?
|
|
133
|
+
AND workspace_id = ?
|
|
134
|
+
ORDER BY deleted_at DESC, id DESC
|
|
135
|
+
`).all(deps.tenantId, normalizedWorkspaceId);
|
|
136
|
+
return {
|
|
137
|
+
deleted: rows
|
|
138
|
+
.map((row) => mapDeletedTaskRowService(deps, row))
|
|
139
|
+
.filter((row) => Boolean(row))
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export function getDeletedTaskService(deps, id, workspaceId) {
|
|
143
|
+
const normalizedWorkspaceId = deps.normalizeWorkspaceId(workspaceId);
|
|
144
|
+
const normalizedId = String(id || '').trim();
|
|
145
|
+
if (!normalizedId)
|
|
146
|
+
return null;
|
|
147
|
+
purgeExpiredDeletedTasksService(deps, normalizedWorkspaceId);
|
|
148
|
+
const row = deps.db.prepare(`
|
|
149
|
+
SELECT *
|
|
150
|
+
FROM deleted_tasks
|
|
151
|
+
WHERE tenant_id = ?
|
|
152
|
+
AND workspace_id = ?
|
|
153
|
+
AND (id = ? OR task_id = ?)
|
|
154
|
+
ORDER BY deleted_at DESC, id DESC
|
|
155
|
+
LIMIT 1
|
|
156
|
+
`).get(deps.tenantId, normalizedWorkspaceId, normalizedId, normalizedId);
|
|
157
|
+
return row ? mapDeletedTaskRowService(deps, row) : null;
|
|
158
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { TaskforceCore } from './Taskforce';
|
|
6
|
+
function makeCore() {
|
|
7
|
+
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'taskforce-deleted-'));
|
|
8
|
+
const core = new TaskforceCore({
|
|
9
|
+
projectRoot,
|
|
10
|
+
storagePath: path.join(projectRoot, '.taskforce')
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
core,
|
|
14
|
+
cleanup: () => {
|
|
15
|
+
core.close();
|
|
16
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
describe('TaskforceCore deleted task lifecycle', () => {
|
|
21
|
+
it('moves deleted tasks into deleted-task history with actor, source, and snapshot data', () => {
|
|
22
|
+
const { core, cleanup } = makeCore();
|
|
23
|
+
try {
|
|
24
|
+
const task = core.createTask({
|
|
25
|
+
title: 'Trash me',
|
|
26
|
+
description: 'Forensics matter',
|
|
27
|
+
createdBy: 'user'
|
|
28
|
+
}, { workspaceId: 'workspace-a', actorRef: 'user' });
|
|
29
|
+
const deleted = core.softDeleteTask(task.id, 'workspace-a', { actorRef: 'user', source: 'ui' });
|
|
30
|
+
expect(deleted).toEqual(expect.objectContaining({
|
|
31
|
+
taskId: task.id,
|
|
32
|
+
source: 'ui',
|
|
33
|
+
actor: 'user',
|
|
34
|
+
actorType: 'human',
|
|
35
|
+
taskSnapshot: expect.objectContaining({
|
|
36
|
+
id: task.id,
|
|
37
|
+
title: 'Trash me',
|
|
38
|
+
description: 'Forensics matter'
|
|
39
|
+
})
|
|
40
|
+
}));
|
|
41
|
+
expect(core.getTask(task.id, 'workspace-a')).toBeNull();
|
|
42
|
+
expect(core.listDeletedTasks('workspace-a').deleted).toHaveLength(1);
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
cleanup();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
it('restores a deleted task back into active tasks and removes it from deleted-task history', () => {
|
|
49
|
+
const { core, cleanup } = makeCore();
|
|
50
|
+
try {
|
|
51
|
+
const task = core.createTask({
|
|
52
|
+
title: 'Bring me back',
|
|
53
|
+
comments: [{ id: 'comment-1', author: 'user', text: 'Keep this note', timestamp: '2026-03-22T10:00:00.000Z' }],
|
|
54
|
+
createdBy: 'user'
|
|
55
|
+
}, { workspaceId: 'workspace-a', actorRef: 'user' });
|
|
56
|
+
const deleted = core.softDeleteTask(task.id, 'workspace-a', { actorRef: 'user', source: 'ui' });
|
|
57
|
+
const restored = core.restoreDeletedTask(deleted.id, 'workspace-a', { actorRef: 'user' });
|
|
58
|
+
expect(restored).toEqual(expect.objectContaining({
|
|
59
|
+
id: task.id,
|
|
60
|
+
title: 'Bring me back',
|
|
61
|
+
comments: expect.arrayContaining([
|
|
62
|
+
expect.objectContaining({ text: 'Keep this note' })
|
|
63
|
+
])
|
|
64
|
+
}));
|
|
65
|
+
expect(core.listDeletedTasks('workspace-a').deleted).toHaveLength(0);
|
|
66
|
+
expect(core.getTask(task.id, 'workspace-a')).not.toBeNull();
|
|
67
|
+
expect(core.listTaskEvents(task.id, 'workspace-a').map((event) => event.action)).toContain('task-restored');
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
cleanup();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
it('purges deleted task records older than the retention window', () => {
|
|
74
|
+
const { core, cleanup } = makeCore();
|
|
75
|
+
try {
|
|
76
|
+
const staleTask = core.createTask({
|
|
77
|
+
title: 'Old deleted task',
|
|
78
|
+
createdBy: 'user'
|
|
79
|
+
}, { workspaceId: 'workspace-a', actorRef: 'user' });
|
|
80
|
+
const freshTask = core.createTask({
|
|
81
|
+
title: 'Fresh deleted task',
|
|
82
|
+
createdBy: 'user'
|
|
83
|
+
}, { workspaceId: 'workspace-a', actorRef: 'user' });
|
|
84
|
+
const staleDeleted = core.softDeleteTask(staleTask.id, 'workspace-a', { actorRef: 'user', source: 'ui' });
|
|
85
|
+
const freshDeleted = core.softDeleteTask(freshTask.id, 'workspace-a', { actorRef: 'user', source: 'ui' });
|
|
86
|
+
const staleDeletedAt = new Date(Date.now() - (91 * 24 * 60 * 60 * 1000)).toISOString();
|
|
87
|
+
core.db.prepare(`
|
|
88
|
+
UPDATE deleted_tasks
|
|
89
|
+
SET deleted_at = ?
|
|
90
|
+
WHERE tenant_id = ? AND workspace_id = ? AND task_id = ?
|
|
91
|
+
`).run(staleDeletedAt, core.getTenantId(), 'workspace-a', staleTask.id);
|
|
92
|
+
const listed = core.listDeletedTasks('workspace-a').deleted;
|
|
93
|
+
expect(listed).toHaveLength(1);
|
|
94
|
+
expect(listed[0]?.taskId).toBe(freshTask.id);
|
|
95
|
+
expect(core.getDeletedTask(staleDeleted.id, 'workspace-a')).toBeNull();
|
|
96
|
+
expect(core.getDeletedTask(freshDeleted.id, 'workspace-a')).toEqual(expect.objectContaining({
|
|
97
|
+
taskId: freshTask.id
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
cleanup();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|