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