@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
|
@@ -34,58 +34,6 @@ export function ensureTenantSchema(db) {
|
|
|
34
34
|
CREATE INDEX IF NOT EXISTS idx_comments_tenant_task ON comments(tenant_id, task_id);
|
|
35
35
|
`);
|
|
36
36
|
}
|
|
37
|
-
export function ensureParentTaskSchema(db) {
|
|
38
|
-
const columns = db.prepare(`PRAGMA table_info(tasks)`).all();
|
|
39
|
-
const hasParentColumn = columns.some((c) => c.name === 'parent_task_id');
|
|
40
|
-
const hasChildDisplayOrderColumn = columns.some((c) => c.name === 'child_display_order');
|
|
41
|
-
if (!hasParentColumn) {
|
|
42
|
-
db.exec(`ALTER TABLE tasks ADD COLUMN parent_task_id TEXT`);
|
|
43
|
-
}
|
|
44
|
-
if (!hasChildDisplayOrderColumn) {
|
|
45
|
-
db.exec(`ALTER TABLE tasks ADD COLUMN child_display_order INTEGER`);
|
|
46
|
-
}
|
|
47
|
-
db.exec(`
|
|
48
|
-
CREATE INDEX IF NOT EXISTS idx_tasks_parent_task_id ON tasks(parent_task_id);
|
|
49
|
-
CREATE INDEX IF NOT EXISTS idx_tasks_parent_child_order ON tasks(parent_task_id, child_display_order);
|
|
50
|
-
`);
|
|
51
|
-
// SQLite-only runtime guard triggers. Postgres path enforces constraints in core logic.
|
|
52
|
-
if (db.provider === 'sqlite') {
|
|
53
|
-
db.exec(`
|
|
54
|
-
CREATE TRIGGER IF NOT EXISTS trg_tasks_parent_guard_insert
|
|
55
|
-
BEFORE INSERT ON tasks
|
|
56
|
-
FOR EACH ROW
|
|
57
|
-
WHEN NEW.parent_task_id IS NOT NULL AND TRIM(NEW.parent_task_id) <> ''
|
|
58
|
-
BEGIN
|
|
59
|
-
SELECT CASE
|
|
60
|
-
WHEN NEW.parent_task_id = NEW.id THEN RAISE(ABORT, 'Task cannot parent itself')
|
|
61
|
-
WHEN (SELECT id FROM tasks WHERE id = NEW.parent_task_id) IS NULL THEN RAISE(ABORT, 'Parent task not found')
|
|
62
|
-
END;
|
|
63
|
-
END;
|
|
64
|
-
`);
|
|
65
|
-
db.exec(`
|
|
66
|
-
CREATE TRIGGER IF NOT EXISTS trg_tasks_parent_guard_update
|
|
67
|
-
BEFORE UPDATE OF parent_task_id ON tasks
|
|
68
|
-
FOR EACH ROW
|
|
69
|
-
WHEN NEW.parent_task_id IS NOT NULL AND TRIM(NEW.parent_task_id) <> ''
|
|
70
|
-
BEGIN
|
|
71
|
-
SELECT CASE
|
|
72
|
-
WHEN NEW.parent_task_id = NEW.id THEN RAISE(ABORT, 'Task cannot parent itself')
|
|
73
|
-
WHEN (SELECT id FROM tasks WHERE id = NEW.parent_task_id) IS NULL THEN RAISE(ABORT, 'Parent task not found')
|
|
74
|
-
END;
|
|
75
|
-
END;
|
|
76
|
-
`);
|
|
77
|
-
// Block deletion of parent tasks while children still point to them.
|
|
78
|
-
db.exec(`
|
|
79
|
-
CREATE TRIGGER IF NOT EXISTS trg_tasks_block_delete_with_children
|
|
80
|
-
BEFORE DELETE ON tasks
|
|
81
|
-
FOR EACH ROW
|
|
82
|
-
WHEN EXISTS (SELECT 1 FROM tasks WHERE parent_task_id = OLD.id)
|
|
83
|
-
BEGIN
|
|
84
|
-
SELECT RAISE(ABORT, 'Cannot delete parent task with children');
|
|
85
|
-
END;
|
|
86
|
-
`);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
37
|
export function ensureScheduleTaskSchema(db) {
|
|
90
38
|
const columns = db.prepare(`PRAGMA table_info(tasks)`).all();
|
|
91
39
|
const hasScheduledDateColumn = columns.some((c) => c.name === 'scheduled_date');
|
|
@@ -226,9 +174,6 @@ export function ensureWorkspaceSchema(db) {
|
|
|
226
174
|
email TEXT NOT NULL,
|
|
227
175
|
display_name TEXT,
|
|
228
176
|
avatar_url TEXT,
|
|
229
|
-
auth_provider TEXT,
|
|
230
|
-
external_auth_id TEXT,
|
|
231
|
-
password_hash TEXT,
|
|
232
177
|
email_verified_at TEXT,
|
|
233
178
|
beta_access BOOLEAN NOT NULL DEFAULT TRUE,
|
|
234
179
|
is_disabled BOOLEAN NOT NULL DEFAULT FALSE,
|
|
@@ -319,9 +264,6 @@ export function ensureWorkspaceSchema(db) {
|
|
|
319
264
|
const postgresUserRepairs = [
|
|
320
265
|
`ALTER TABLE users ADD COLUMN IF NOT EXISTS display_name TEXT`,
|
|
321
266
|
`ALTER TABLE users ADD COLUMN IF NOT EXISTS avatar_url TEXT`,
|
|
322
|
-
`ALTER TABLE users ADD COLUMN IF NOT EXISTS auth_provider TEXT`,
|
|
323
|
-
`ALTER TABLE users ADD COLUMN IF NOT EXISTS external_auth_id TEXT`,
|
|
324
|
-
`ALTER TABLE users ADD COLUMN IF NOT EXISTS password_hash TEXT`,
|
|
325
267
|
`ALTER TABLE users ADD COLUMN IF NOT EXISTS email_verified_at TEXT`,
|
|
326
268
|
`ALTER TABLE users ADD COLUMN IF NOT EXISTS beta_access BOOLEAN NOT NULL DEFAULT TRUE`,
|
|
327
269
|
`ALTER TABLE users ADD COLUMN IF NOT EXISTS is_disabled BOOLEAN NOT NULL DEFAULT FALSE`,
|
|
@@ -357,9 +299,6 @@ export function ensureWorkspaceSchema(db) {
|
|
|
357
299
|
const sqliteUserRepairs = [
|
|
358
300
|
{ name: 'display_name', sql: `ALTER TABLE users ADD COLUMN display_name TEXT` },
|
|
359
301
|
{ name: 'avatar_url', sql: `ALTER TABLE users ADD COLUMN avatar_url TEXT` },
|
|
360
|
-
{ name: 'auth_provider', sql: `ALTER TABLE users ADD COLUMN auth_provider TEXT` },
|
|
361
|
-
{ name: 'external_auth_id', sql: `ALTER TABLE users ADD COLUMN external_auth_id TEXT` },
|
|
362
|
-
{ name: 'password_hash', sql: `ALTER TABLE users ADD COLUMN password_hash TEXT` },
|
|
363
302
|
{ name: 'email_verified_at', sql: `ALTER TABLE users ADD COLUMN email_verified_at TEXT` },
|
|
364
303
|
{ name: 'beta_access', sql: `ALTER TABLE users ADD COLUMN beta_access BOOLEAN NOT NULL DEFAULT TRUE` },
|
|
365
304
|
{ name: 'is_disabled', sql: `ALTER TABLE users ADD COLUMN is_disabled BOOLEAN NOT NULL DEFAULT FALSE` },
|
|
@@ -697,6 +636,7 @@ export function ensureEntitlementsSchema(db) {
|
|
|
697
636
|
plan_id TEXT PRIMARY KEY,
|
|
698
637
|
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
699
638
|
display_name TEXT NOT NULL,
|
|
639
|
+
description TEXT,
|
|
700
640
|
status TEXT NOT NULL DEFAULT 'active',
|
|
701
641
|
created_at TEXT NOT NULL,
|
|
702
642
|
updated_at TEXT NOT NULL
|
|
@@ -727,6 +667,17 @@ export function ensureEntitlementsSchema(db) {
|
|
|
727
667
|
FOREIGN KEY (plan_version_id) REFERENCES plan_versions(plan_version_id) ON DELETE CASCADE
|
|
728
668
|
);
|
|
729
669
|
|
|
670
|
+
CREATE TABLE IF NOT EXISTS plan_feature_catalog_overrides (
|
|
671
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
672
|
+
feature_key TEXT NOT NULL,
|
|
673
|
+
public_label TEXT,
|
|
674
|
+
public_description TEXT,
|
|
675
|
+
public_display_order INTEGER,
|
|
676
|
+
created_at TEXT NOT NULL,
|
|
677
|
+
updated_at TEXT NOT NULL,
|
|
678
|
+
PRIMARY KEY (tenant_id, feature_key)
|
|
679
|
+
);
|
|
680
|
+
|
|
730
681
|
CREATE TABLE IF NOT EXISTS account_entitlements (
|
|
731
682
|
account_id TEXT PRIMARY KEY,
|
|
732
683
|
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
@@ -763,6 +714,8 @@ export function ensureEntitlementsSchema(db) {
|
|
|
763
714
|
ON plan_versions(tenant_id, plan_id, is_default);
|
|
764
715
|
CREATE INDEX IF NOT EXISTS idx_plan_feature_matrix_plan_version
|
|
765
716
|
ON plan_feature_matrix(tenant_id, plan_version_id);
|
|
717
|
+
CREATE INDEX IF NOT EXISTS idx_plan_feature_catalog_overrides_tenant
|
|
718
|
+
ON plan_feature_catalog_overrides(tenant_id, updated_at);
|
|
766
719
|
CREATE INDEX IF NOT EXISTS idx_account_entitlements_state
|
|
767
720
|
ON account_entitlements(tenant_id, entitlement_state);
|
|
768
721
|
CREATE INDEX IF NOT EXISTS idx_account_entitlements_plan_version
|
|
@@ -774,6 +727,15 @@ export function ensureEntitlementsSchema(db) {
|
|
|
774
727
|
CREATE INDEX IF NOT EXISTS idx_entitlement_audit_log_actor
|
|
775
728
|
ON entitlement_audit_log(tenant_id, actor_user_id, created_at);
|
|
776
729
|
`);
|
|
730
|
+
const planCatalogColumns = db.prepare(`PRAGMA table_info(plan_catalog)`).all();
|
|
731
|
+
const hasDescriptionColumn = planCatalogColumns.some((c) => c.name === 'description');
|
|
732
|
+
if (!hasDescriptionColumn) {
|
|
733
|
+
db.exec(`ALTER TABLE plan_catalog ADD COLUMN description TEXT`);
|
|
734
|
+
}
|
|
735
|
+
const planFeatureCatalogOverrideColumns = db.prepare(`PRAGMA table_info(plan_feature_catalog_overrides)`).all();
|
|
736
|
+
if (!planFeatureCatalogOverrideColumns.some((column) => column.name === 'public_display_order')) {
|
|
737
|
+
db.exec(`ALTER TABLE plan_feature_catalog_overrides ADD COLUMN public_display_order INTEGER`);
|
|
738
|
+
}
|
|
777
739
|
}
|
|
778
740
|
export function ensureTaskAssetsSchema(db) {
|
|
779
741
|
db.exec(`
|
|
@@ -853,6 +815,7 @@ export function ensureWorkspaceAssetsSchema(db) {
|
|
|
853
815
|
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
854
816
|
workspace_id TEXT NOT NULL,
|
|
855
817
|
asset_id TEXT NOT NULL,
|
|
818
|
+
target_type TEXT NOT NULL DEFAULT 'task',
|
|
856
819
|
task_id TEXT NOT NULL,
|
|
857
820
|
role TEXT NOT NULL,
|
|
858
821
|
display_name TEXT,
|
|
@@ -873,6 +836,15 @@ export function ensureWorkspaceAssetsSchema(db) {
|
|
|
873
836
|
if (!hasDisplayNameColumn) {
|
|
874
837
|
db.exec(`ALTER TABLE workspace_asset_links ADD COLUMN display_name TEXT`);
|
|
875
838
|
}
|
|
839
|
+
const hasTargetTypeColumn = linkColumns.some((c) => c.name === 'target_type');
|
|
840
|
+
if (!hasTargetTypeColumn) {
|
|
841
|
+
db.exec(`ALTER TABLE workspace_asset_links ADD COLUMN target_type TEXT NOT NULL DEFAULT 'task'`);
|
|
842
|
+
db.exec(`UPDATE workspace_asset_links SET target_type = 'task' WHERE target_type IS NULL OR TRIM(target_type) = ''`);
|
|
843
|
+
}
|
|
844
|
+
db.exec(`
|
|
845
|
+
CREATE INDEX IF NOT EXISTS idx_workspace_asset_links_target
|
|
846
|
+
ON workspace_asset_links(tenant_id, workspace_id, target_type, task_id, deleted_at, updated_at)
|
|
847
|
+
`);
|
|
876
848
|
}
|
|
877
849
|
export function ensureDocumentReferenceSchema(db) {
|
|
878
850
|
const columns = db.prepare(`PRAGMA table_info(workspace_assets)`).all();
|
|
@@ -1160,3 +1132,141 @@ export function ensureDocumentReviewSessionsSchema(db) {
|
|
|
1160
1132
|
}
|
|
1161
1133
|
}
|
|
1162
1134
|
}
|
|
1135
|
+
export function ensureAuthIdentitySchema(db) {
|
|
1136
|
+
db.exec(`
|
|
1137
|
+
CREATE TABLE IF NOT EXISTS user_auth_identities (
|
|
1138
|
+
id TEXT PRIMARY KEY,
|
|
1139
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
1140
|
+
user_id TEXT NOT NULL,
|
|
1141
|
+
provider TEXT NOT NULL,
|
|
1142
|
+
provider_subject TEXT,
|
|
1143
|
+
provider_email TEXT,
|
|
1144
|
+
provider_email_verified INTEGER,
|
|
1145
|
+
password_hash TEXT,
|
|
1146
|
+
display_name_snapshot TEXT,
|
|
1147
|
+
avatar_url_snapshot TEXT,
|
|
1148
|
+
metadata_json TEXT,
|
|
1149
|
+
created_at TEXT NOT NULL,
|
|
1150
|
+
updated_at TEXT NOT NULL,
|
|
1151
|
+
last_login_at TEXT,
|
|
1152
|
+
linked_at TEXT,
|
|
1153
|
+
disabled_at TEXT,
|
|
1154
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
1155
|
+
);
|
|
1156
|
+
|
|
1157
|
+
CREATE TABLE IF NOT EXISTS auth_oauth_states (
|
|
1158
|
+
state TEXT PRIMARY KEY,
|
|
1159
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
1160
|
+
provider TEXT NOT NULL,
|
|
1161
|
+
flow_mode TEXT NOT NULL,
|
|
1162
|
+
user_id TEXT,
|
|
1163
|
+
invite_token TEXT,
|
|
1164
|
+
return_to TEXT,
|
|
1165
|
+
pkce_verifier TEXT,
|
|
1166
|
+
nonce TEXT,
|
|
1167
|
+
expires_at TEXT NOT NULL,
|
|
1168
|
+
consumed_at TEXT,
|
|
1169
|
+
created_at TEXT NOT NULL,
|
|
1170
|
+
created_ip TEXT
|
|
1171
|
+
);
|
|
1172
|
+
`);
|
|
1173
|
+
if (db.provider === 'postgres') {
|
|
1174
|
+
try {
|
|
1175
|
+
db.exec(`
|
|
1176
|
+
ALTER TABLE user_auth_identities
|
|
1177
|
+
ADD CONSTRAINT uq_auth_identity_provider_subject
|
|
1178
|
+
UNIQUE (tenant_id, provider, provider_subject)
|
|
1179
|
+
`);
|
|
1180
|
+
}
|
|
1181
|
+
catch { /* already exists */ }
|
|
1182
|
+
try {
|
|
1183
|
+
db.exec(`
|
|
1184
|
+
ALTER TABLE user_auth_identities
|
|
1185
|
+
ADD CONSTRAINT uq_auth_identity_user_provider
|
|
1186
|
+
UNIQUE (tenant_id, user_id, provider)
|
|
1187
|
+
`);
|
|
1188
|
+
}
|
|
1189
|
+
catch { /* already exists */ }
|
|
1190
|
+
}
|
|
1191
|
+
else {
|
|
1192
|
+
// SQLite: create unique indexes if they don't already exist
|
|
1193
|
+
db.exec(`
|
|
1194
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_auth_identity_provider_subject
|
|
1195
|
+
ON user_auth_identities(tenant_id, provider, provider_subject)
|
|
1196
|
+
WHERE provider_subject IS NOT NULL;
|
|
1197
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_auth_identity_user_provider
|
|
1198
|
+
ON user_auth_identities(tenant_id, user_id, provider);
|
|
1199
|
+
`);
|
|
1200
|
+
}
|
|
1201
|
+
db.exec(`
|
|
1202
|
+
CREATE INDEX IF NOT EXISTS idx_auth_identities_user
|
|
1203
|
+
ON user_auth_identities(tenant_id, user_id);
|
|
1204
|
+
CREATE INDEX IF NOT EXISTS idx_auth_oauth_states_expires
|
|
1205
|
+
ON auth_oauth_states(expires_at);
|
|
1206
|
+
`);
|
|
1207
|
+
// ── Backfill: migrate any remaining users.password_hash rows into user_auth_identities.
|
|
1208
|
+
// Guarded: wrapped in try/catch — if the legacy column was already dropped (T-837),
|
|
1209
|
+
// the SELECT will throw and we skip cleanly. Idempotent via unique index.
|
|
1210
|
+
try {
|
|
1211
|
+
const now = new Date().toISOString();
|
|
1212
|
+
const usersWithPassword = db.prepare(`
|
|
1213
|
+
SELECT id, tenant_id, password_hash
|
|
1214
|
+
FROM users
|
|
1215
|
+
WHERE password_hash IS NOT NULL
|
|
1216
|
+
AND TRIM(COALESCE(password_hash, '')) != ''
|
|
1217
|
+
`).all();
|
|
1218
|
+
const hasIdentity = db.prepare(`
|
|
1219
|
+
SELECT id FROM user_auth_identities
|
|
1220
|
+
WHERE tenant_id = ? AND user_id = ? AND provider = 'password'
|
|
1221
|
+
LIMIT 1
|
|
1222
|
+
`);
|
|
1223
|
+
const insertIdentity = db.prepare(`
|
|
1224
|
+
INSERT INTO user_auth_identities
|
|
1225
|
+
(id, tenant_id, user_id, provider, password_hash, created_at, updated_at, linked_at)
|
|
1226
|
+
VALUES (?, ?, ?, 'password', ?, ?, ?, ?)
|
|
1227
|
+
`);
|
|
1228
|
+
for (const user of usersWithPassword) {
|
|
1229
|
+
const existing = hasIdentity.get(user.tenant_id, user.id);
|
|
1230
|
+
if (existing?.id)
|
|
1231
|
+
continue;
|
|
1232
|
+
const identityId = `identity-${user.id}-pwd`;
|
|
1233
|
+
try {
|
|
1234
|
+
insertIdentity.run(identityId, user.tenant_id, user.id, user.password_hash, now, now, now);
|
|
1235
|
+
}
|
|
1236
|
+
catch {
|
|
1237
|
+
// Concurrent startup or duplicate — safe to skip.
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
catch {
|
|
1242
|
+
// users.password_hash column already absent — backfill not needed.
|
|
1243
|
+
}
|
|
1244
|
+
// ── Drop legacy auth columns from users (T-837).
|
|
1245
|
+
// Safe to run repeatedly: column-existence checks prevent double-drop errors.
|
|
1246
|
+
if (db.provider === 'postgres') {
|
|
1247
|
+
// PostgreSQL: DROP COLUMN IF EXISTS is a single atomic statement.
|
|
1248
|
+
try {
|
|
1249
|
+
db.exec(`
|
|
1250
|
+
ALTER TABLE users
|
|
1251
|
+
DROP COLUMN IF EXISTS auth_provider,
|
|
1252
|
+
DROP COLUMN IF EXISTS external_auth_id,
|
|
1253
|
+
DROP COLUMN IF EXISTS password_hash
|
|
1254
|
+
`);
|
|
1255
|
+
}
|
|
1256
|
+
catch { /* best-effort — ignore if already absent */ }
|
|
1257
|
+
}
|
|
1258
|
+
else {
|
|
1259
|
+
// SQLite: DROP COLUMN requires 3.35.0+ (2021-03-12).
|
|
1260
|
+
// better-sqlite3 ships its own SQLite which satisfies this requirement.
|
|
1261
|
+
// Guard each drop individually to tolerate partial completion.
|
|
1262
|
+
const currentUserCols = new Set(db.prepare(`PRAGMA table_info(users)`).all().map(c => c.name));
|
|
1263
|
+
for (const col of ['auth_provider', 'external_auth_id', 'password_hash']) {
|
|
1264
|
+
if (currentUserCols.has(col)) {
|
|
1265
|
+
try {
|
|
1266
|
+
db.exec(`ALTER TABLE users DROP COLUMN ${col}`);
|
|
1267
|
+
}
|
|
1268
|
+
catch { /* ignore — concurrent drop or already gone */ }
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
2
4
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
5
|
import { ensureAnnotatedAttachmentSessionsSchema, ensureDocumentReviewSessionsSchema, ensureWorkspaceSchema } from './taskSchemaMigrations';
|
|
4
6
|
const openDbs = [];
|
|
@@ -208,3 +210,16 @@ describe('ensureDocumentReviewSessionsSchema', () => {
|
|
|
208
210
|
expect(row?.author_actor_id).toBe('user-1');
|
|
209
211
|
});
|
|
210
212
|
});
|
|
213
|
+
describe('postgres bootstrap parity', () => {
|
|
214
|
+
it('includes workspace asset tables and target_type indexes', () => {
|
|
215
|
+
const sqlPath = path.resolve(process.cwd(), 'src/migrations/postgres/001_init.sql');
|
|
216
|
+
const sql = fs.readFileSync(sqlPath, 'utf8');
|
|
217
|
+
expect(sql).toContain('CREATE TABLE IF NOT EXISTS workspace_assets');
|
|
218
|
+
expect(sql).toContain('CREATE TABLE IF NOT EXISTS workspace_asset_links');
|
|
219
|
+
expect(sql).toContain('target_type TEXT NOT NULL DEFAULT \'task\'');
|
|
220
|
+
expect(sql).toContain('display_name TEXT');
|
|
221
|
+
expect(sql).toContain('reference_number INTEGER');
|
|
222
|
+
expect(sql).toContain('CREATE INDEX IF NOT EXISTS idx_workspace_asset_links_target');
|
|
223
|
+
expect(sql).toContain('CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_assets_workspace_kind_reference');
|
|
224
|
+
});
|
|
225
|
+
});
|
|
@@ -284,7 +284,7 @@ export const workflowDocs = [
|
|
|
284
284
|
' - `### Next Action`',
|
|
285
285
|
'6. Ask whether to save as `{artifacts.implementationPlan.path}`.',
|
|
286
286
|
'7. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "implementation-plan.md"`.',
|
|
287
|
-
' - Use `{{MCP_PREFIX}}link_task_context` only for
|
|
287
|
+
' - Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.',
|
|
288
288
|
'8. If setting status to `task` or `on-hold`, add explicit comment justification using `{{MCP_PREFIX}}add_comment`.',
|
|
289
289
|
'9. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.',
|
|
290
290
|
'10. Print `prompts.handoffPrompt` and stop.'
|
|
@@ -358,7 +358,7 @@ export const workflowDocs = [
|
|
|
358
358
|
' - `### Next Step`',
|
|
359
359
|
'4. Ask whether to save as `{artifacts.collaborationBlueprint.path}`.',
|
|
360
360
|
'5. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "collaboration-blueprint.md"`.',
|
|
361
|
-
' - Use `{{MCP_PREFIX}}link_task_context` only for
|
|
361
|
+
' - Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.',
|
|
362
362
|
'6. Ask concise clarification questions only if required fields above are missing.',
|
|
363
363
|
'7. If setting status to `task` or `on-hold`, add explicit comment justification using `{{MCP_PREFIX}}add_comment`.',
|
|
364
364
|
'8. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.',
|
|
@@ -435,14 +435,14 @@ export const workflowDocs = [
|
|
|
435
435
|
' - `### Next Step`',
|
|
436
436
|
'4. Ask whether to save as `{artifacts.evaluationReport.path}`.',
|
|
437
437
|
'5. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "evaluation.md"`.',
|
|
438
|
-
' - Use `{{MCP_PREFIX}}link_task_context` only for
|
|
438
|
+
' - Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.',
|
|
439
439
|
'6. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.',
|
|
440
440
|
'',
|
|
441
441
|
'## 3. Optional Report Artifact',
|
|
442
442
|
'',
|
|
443
443
|
'1. Use report files only when user opts in.',
|
|
444
444
|
'2. Save to `task attachment "evaluation.md"` and attach to task context when approved.',
|
|
445
|
-
' - If the document
|
|
445
|
+
' - If the document should become a shared durable document, save it through the canonical document/document-manager flow instead of a raw `docs/` file.',
|
|
446
446
|
'3. Stop.'
|
|
447
447
|
].join('\n')
|
|
448
448
|
}
|
|
@@ -513,7 +513,7 @@ export const workflowDocs = [
|
|
|
513
513
|
' - `### Next Status`',
|
|
514
514
|
'3. Ask whether to save as `{artifacts.reviewReport.path}`.',
|
|
515
515
|
'4. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "review.md"`.',
|
|
516
|
-
' - Use `{{MCP_PREFIX}}link_task_context` only for
|
|
516
|
+
' - Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.',
|
|
517
517
|
'5. Keep status within allowed statuses (`task`, `on-hold`, `in-progress`, `review`, `done`) unless user explicitly instructs otherwise.',
|
|
518
518
|
' - If setting status to `task` or `on-hold`, add explicit comment justification using `{{MCP_PREFIX}}add_comment`.',
|
|
519
519
|
'6. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.',
|
|
@@ -522,7 +522,7 @@ export const workflowDocs = [
|
|
|
522
522
|
'',
|
|
523
523
|
'1. Use report files only when user opts in.',
|
|
524
524
|
'2. Save to `task attachment "review.md"` and attach to task context when approved.',
|
|
525
|
-
' - If the document
|
|
525
|
+
' - If the document should become a shared durable document, save it through the canonical document/document-manager flow instead of a raw `docs/` file.',
|
|
526
526
|
'3. Stop.'
|
|
527
527
|
].join('\n')
|
|
528
528
|
}
|
|
@@ -102,7 +102,7 @@ content:
|
|
|
102
102
|
- `### Next Step`
|
|
103
103
|
4. Ask whether to save as `{artifacts.collaborationBlueprint.path}`.
|
|
104
104
|
5. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "collaboration-blueprint.md"`.
|
|
105
|
-
- Use `{{MCP_PREFIX}}link_task_context` only for
|
|
105
|
+
- Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.
|
|
106
106
|
6. Ask concise clarification questions only if required fields above are missing.
|
|
107
107
|
7. If setting status to `task` or `on-hold`, add explicit comment justification using `{{MCP_PREFIX}}add_comment`.
|
|
108
108
|
8. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.
|
|
@@ -106,12 +106,12 @@ content:
|
|
|
106
106
|
- `### Next Step`
|
|
107
107
|
4. Ask whether to save as `{artifacts.evaluationReport.path}`.
|
|
108
108
|
5. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "evaluation.md"`.
|
|
109
|
-
- Use `{{MCP_PREFIX}}link_task_context` only for
|
|
109
|
+
- Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.
|
|
110
110
|
6. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.
|
|
111
111
|
|
|
112
112
|
## 3. Optional Report Artifact
|
|
113
113
|
|
|
114
114
|
1. Use report files only when user opts in.
|
|
115
115
|
2. Save to `task attachment "evaluation.md"` and attach to task context when approved.
|
|
116
|
-
- If the document
|
|
116
|
+
- If the document should become a shared durable document, save it through the canonical document/document-manager flow instead of a raw `docs/` file.
|
|
117
117
|
3. Stop.
|
|
@@ -113,7 +113,7 @@ content:
|
|
|
113
113
|
- `### Next Action`
|
|
114
114
|
6. Ask whether to save as `{artifacts.implementationPlan.path}`.
|
|
115
115
|
7. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "implementation-plan.md"`.
|
|
116
|
-
- Use `{{MCP_PREFIX}}link_task_context` only for
|
|
116
|
+
- Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.
|
|
117
117
|
8. If setting status to `task` or `on-hold`, add explicit comment justification using `{{MCP_PREFIX}}add_comment`.
|
|
118
118
|
9. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.
|
|
119
119
|
10. Print `prompts.handoffPrompt` and stop.
|
|
@@ -102,7 +102,7 @@ content:
|
|
|
102
102
|
- `### Next Status`
|
|
103
103
|
3. Ask whether to save as `{artifacts.reviewReport.path}`.
|
|
104
104
|
4. If user confirms, persist task-specific content via `{{MCP_PREFIX}}save_task_attachment` using `filename: "review.md"`.
|
|
105
|
-
- Use `{{MCP_PREFIX}}link_task_context` only for
|
|
105
|
+
- Use `{{MCP_PREFIX}}link_task_context` only for external http(s) URLs that should stay as references.
|
|
106
106
|
5. Keep status within allowed statuses (`task`, `on-hold`, `in-progress`, `review`, `done`) unless user explicitly instructs otherwise.
|
|
107
107
|
- If setting status to `task` or `on-hold`, add explicit comment justification using `{{MCP_PREFIX}}add_comment`.
|
|
108
108
|
6. Never set `cancelled`, and never call `{{MCP_PREFIX}}archive_task`, unless explicitly instructed by the user.
|
|
@@ -111,5 +111,5 @@ content:
|
|
|
111
111
|
|
|
112
112
|
1. Use report files only when user opts in.
|
|
113
113
|
2. Save to `task attachment "review.md"` and attach to task context when approved.
|
|
114
|
-
- If the document
|
|
114
|
+
- If the document should become a shared durable document, save it through the canonical document/document-manager flow instead of a raw `docs/` file.
|
|
115
115
|
3. Stop.
|
|
@@ -209,9 +209,9 @@ describe('annotated attachment routes', () => {
|
|
|
209
209
|
core.createWorkspace({ workspaceId: 'workspace-1', name: 'Workspace 1' });
|
|
210
210
|
const now = new Date().toISOString();
|
|
211
211
|
core.db.prepare(`
|
|
212
|
-
INSERT INTO users (id, tenant_id, email, display_name,
|
|
213
|
-
VALUES (?, ?, ?, ?, ?, ?, ?,
|
|
214
|
-
`).run('user-local-1', core.getTenantId(), 'local@example.com', 'Local User',
|
|
212
|
+
INSERT INTO users (id, tenant_id, email, display_name, beta_access, is_disabled, created_at, updated_at)
|
|
213
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
214
|
+
`).run('user-local-1', core.getTenantId(), 'local@example.com', 'Local User', 1, 0, now, now);
|
|
215
215
|
core.db.prepare(`
|
|
216
216
|
INSERT INTO workspace_memberships (id, tenant_id, workspace_id, user_id, role, status, created_at, updated_at)
|
|
217
217
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apple Sign In adapter.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Redirect user to buildAppleAuthUrl() with response_mode=form_post.
|
|
6
|
+
* 2. Apple POSTs `code`, `state`, `id_token` and optional `user` JSON to the callback.
|
|
7
|
+
* 3. Call exchangeAppleCode() which mints the client-secret JWT, exchanges the code,
|
|
8
|
+
* and decodes the id_token payload for the canonical profile.
|
|
9
|
+
*
|
|
10
|
+
* Notes:
|
|
11
|
+
* - Apple never re-sends the user's name after the very first authorisation.
|
|
12
|
+
* If the user JSON arrives on first login it is captured; subsequent logins
|
|
13
|
+
* use the snapshot stored in user_auth_identities.
|
|
14
|
+
* - Private-relay email addresses contain `.privaterelay.appleid.com`; the
|
|
15
|
+
* `is_private_email` claim is set to `true` in that case.
|
|
16
|
+
*/
|
|
17
|
+
import type { NormalizedOAuthProfile } from './types.js';
|
|
18
|
+
export declare function buildAppleAuthUrl(input: {
|
|
19
|
+
servicesId: string;
|
|
20
|
+
redirectUri: string;
|
|
21
|
+
state: string;
|
|
22
|
+
nonce: string;
|
|
23
|
+
}): string;
|
|
24
|
+
export declare function exchangeAppleCode(input: {
|
|
25
|
+
code: string;
|
|
26
|
+
redirectUri: string;
|
|
27
|
+
servicesId: string;
|
|
28
|
+
teamId: string;
|
|
29
|
+
keyId: string;
|
|
30
|
+
privateKeyPem: string;
|
|
31
|
+
/** Optional: JSON string Apple sends on first authorisation containing { name }. */
|
|
32
|
+
userJson?: string | null;
|
|
33
|
+
}): Promise<NormalizedOAuthProfile>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { getAppleClientSecret } from './appleClientSecret.js';
|
|
2
|
+
export function buildAppleAuthUrl(input) {
|
|
3
|
+
const params = new URLSearchParams({
|
|
4
|
+
client_id: input.servicesId,
|
|
5
|
+
redirect_uri: input.redirectUri,
|
|
6
|
+
response_type: 'code id_token',
|
|
7
|
+
scope: 'name email',
|
|
8
|
+
state: input.state,
|
|
9
|
+
nonce: input.nonce,
|
|
10
|
+
response_mode: 'form_post',
|
|
11
|
+
});
|
|
12
|
+
return `https://appleid.apple.com/auth/authorize?${params.toString()}`;
|
|
13
|
+
}
|
|
14
|
+
export async function exchangeAppleCode(input) {
|
|
15
|
+
const clientSecret = getAppleClientSecret({
|
|
16
|
+
servicesId: input.servicesId,
|
|
17
|
+
teamId: input.teamId,
|
|
18
|
+
keyId: input.keyId,
|
|
19
|
+
privateKeyPem: input.privateKeyPem,
|
|
20
|
+
});
|
|
21
|
+
// Step 1: exchange authorisation code for tokens
|
|
22
|
+
const tokenRes = await fetch('https://appleid.apple.com/auth/token', {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
25
|
+
body: new URLSearchParams({
|
|
26
|
+
grant_type: 'authorization_code',
|
|
27
|
+
code: input.code,
|
|
28
|
+
redirect_uri: input.redirectUri,
|
|
29
|
+
client_id: input.servicesId,
|
|
30
|
+
client_secret: clientSecret,
|
|
31
|
+
}).toString(),
|
|
32
|
+
});
|
|
33
|
+
if (!tokenRes.ok) {
|
|
34
|
+
const body = await tokenRes.text().catch(() => '');
|
|
35
|
+
throw new Error(`Apple token exchange failed (${tokenRes.status}): ${body}`);
|
|
36
|
+
}
|
|
37
|
+
const tokens = await tokenRes.json();
|
|
38
|
+
const idToken = String(tokens.id_token || '').trim();
|
|
39
|
+
if (!idToken)
|
|
40
|
+
throw new Error('Apple token exchange: missing id_token');
|
|
41
|
+
// Step 2: decode id_token payload (trust Apple — skip signature verification here)
|
|
42
|
+
const parts = idToken.split('.');
|
|
43
|
+
if (parts.length !== 3)
|
|
44
|
+
throw new Error('Apple id_token: not a valid JWT');
|
|
45
|
+
let payload;
|
|
46
|
+
try {
|
|
47
|
+
payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
throw new Error('Apple id_token: could not decode payload');
|
|
51
|
+
}
|
|
52
|
+
const sub = String(payload.sub || '').trim();
|
|
53
|
+
if (!sub)
|
|
54
|
+
throw new Error('Apple id_token: missing sub');
|
|
55
|
+
const email = String(payload.email || '').trim().toLowerCase() || null;
|
|
56
|
+
const emailVerified = payload.email_verified === true || payload.email_verified === 'true';
|
|
57
|
+
const isPrivateEmail = payload.is_private_email === true || payload.is_private_email === 'true';
|
|
58
|
+
// Step 3: extract display name from one-time `user` JSON (first-login only)
|
|
59
|
+
let displayName = null;
|
|
60
|
+
if (input.userJson) {
|
|
61
|
+
try {
|
|
62
|
+
const userObj = JSON.parse(input.userJson);
|
|
63
|
+
const firstName = String(userObj?.name?.firstName || '').trim();
|
|
64
|
+
const lastName = String(userObj?.name?.lastName || '').trim();
|
|
65
|
+
const full = [firstName, lastName].filter(Boolean).join(' ');
|
|
66
|
+
if (full)
|
|
67
|
+
displayName = full;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// malformed — ignore
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
provider: 'apple',
|
|
75
|
+
providerSubject: sub,
|
|
76
|
+
email,
|
|
77
|
+
emailVerified,
|
|
78
|
+
displayName,
|
|
79
|
+
avatarUrl: null, // Apple never provides an avatar
|
|
80
|
+
isPrivateEmail,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apple OAuth client-secret JWT minter.
|
|
3
|
+
*
|
|
4
|
+
* Apple Sign In requires a short-lived JWT as the OAuth `client_secret` rather
|
|
5
|
+
* than a static secret. The JWT must be:
|
|
6
|
+
* - Signed with ES256 (ECDSA P-256) using the private key registered in App Store Connect
|
|
7
|
+
* - Valid for at most 6 months (Apple enforces this; we use 10 min for freshness)
|
|
8
|
+
* - Cached so callers don't re-sign on every request
|
|
9
|
+
*
|
|
10
|
+
* Reference: https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
|
|
11
|
+
*/
|
|
12
|
+
export interface AppleClientSecretConfig {
|
|
13
|
+
/** 10-character Apple Team ID */
|
|
14
|
+
teamId: string;
|
|
15
|
+
/** Services ID (bundle ID) registered in App Store Connect */
|
|
16
|
+
servicesId: string;
|
|
17
|
+
/** Key ID of the registered Sign In with Apple key */
|
|
18
|
+
keyId: string;
|
|
19
|
+
/** PEM-encoded EC P-256 private key downloaded from App Store Connect */
|
|
20
|
+
privateKeyPem: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Returns a cached client-secret JWT for the given Apple config, minting a
|
|
24
|
+
* fresh one if the cache entry is absent or within 60 s of expiry.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getAppleClientSecret(cfg: AppleClientSecretConfig): string;
|
|
27
|
+
/**
|
|
28
|
+
* Force-mints a fresh JWT (bypasses cache) for health-check purposes.
|
|
29
|
+
* Returns success/error without updating the cache.
|
|
30
|
+
*/
|
|
31
|
+
export declare function testMintAppleClientSecret(cfg: AppleClientSecretConfig): {
|
|
32
|
+
ok: true;
|
|
33
|
+
token: string;
|
|
34
|
+
} | {
|
|
35
|
+
ok: false;
|
|
36
|
+
error: string;
|
|
37
|
+
};
|