@taskforcehq/taskforce 0.3.305 → 0.3.306

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (337) hide show
  1. package/dist/Taskforce.module.css +295 -89
  2. package/dist/TaskforceCore.js +215 -43
  3. package/dist/TaskforceCore.test.js +433 -28
  4. package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
  5. package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  6. package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
  7. package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
  8. package/dist/assets/fonts/Noir_medium.woff2 +0 -0
  9. package/dist/assets/fonts/Noir_regular.woff2 +0 -0
  10. package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
  11. package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
  12. package/dist/compat/workspaceSyncCompat.js +46 -2
  13. package/dist/compat/workspaceSyncCompat.test.js +38 -1
  14. package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
  15. package/dist/components/context/ContextAttachmentManager.js +549 -0
  16. package/dist/components/features/AgentsModule.d.ts +10 -1
  17. package/dist/components/features/AgentsModule.js +260 -15
  18. package/dist/components/features/AgentsModule.test.js +255 -5
  19. package/dist/components/features/DocumentViewer.d.ts +5 -1
  20. package/dist/components/features/DocumentViewer.js +60 -3
  21. package/dist/components/features/DocumentWorkspace.js +1 -1
  22. package/dist/components/features/DocumentWorkspace.test.js +33 -0
  23. package/dist/components/features/documentWorkspaceModel.d.ts +1 -1
  24. package/dist/components/task/TaskCard.js +19 -28
  25. package/dist/components/task/TaskCard.test.js +38 -3
  26. package/dist/components/task/TaskContextUpload.js +4 -526
  27. package/dist/components/task/TaskForm.js +85 -90
  28. package/dist/components/task/TaskForm.test.js +110 -8
  29. package/dist/components/task/TaskKanban.d.ts +2 -1
  30. package/dist/components/task/TaskKanban.js +94 -13
  31. package/dist/components/task/TaskKanban.test.js +36 -0
  32. package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
  33. package/dist/components/ui/EditableAvatarButton.js +18 -0
  34. package/dist/components/views/PlanComparisonPage.d.ts +9 -1
  35. package/dist/components/views/PlanComparisonPage.js +70 -14
  36. package/dist/components/views/PlanComparisonPage.test.js +238 -0
  37. package/dist/components/views/PlansPage.js +127 -36
  38. package/dist/components/views/PlansPage.test.js +294 -12
  39. package/dist/components/views/StandaloneLayout.js +283 -533
  40. package/dist/components/views/panels/PlanningDrawer.d.ts +8 -1
  41. package/dist/components/views/panels/PlanningDrawer.js +18 -11
  42. package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
  43. package/dist/components/views/panels/PlanningDrawer.test.js +141 -0
  44. package/dist/components/views/planningScope.d.ts +20 -0
  45. package/dist/components/views/planningScope.js +25 -0
  46. package/dist/components/views/planningScope.test.d.ts +1 -0
  47. package/dist/components/views/planningScope.test.js +60 -0
  48. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +26 -1
  49. package/dist/components/views/standalone/modals/AccountHubModal.js +76 -2
  50. package/dist/components/views/standalone/modals/AccountHubModal.test.js +20 -3
  51. package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
  52. package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
  53. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
  54. package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
  55. package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
  56. package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
  57. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
  58. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
  59. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
  60. package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
  61. package/dist/core/AiProfileService.d.ts +39 -2
  62. package/dist/core/AiProfileService.js +639 -73
  63. package/dist/core/AiProfileService.test.js +19 -1
  64. package/dist/core/AiProfiles.test.js +566 -7
  65. package/dist/core/AttachmentLinkService.js +57 -15
  66. package/dist/core/AuthIdentityService.d.ts +107 -0
  67. package/dist/core/AuthIdentityService.js +284 -0
  68. package/dist/core/AuthTokenService.d.ts +4 -0
  69. package/dist/core/AuthTokenService.js +34 -6
  70. package/dist/core/AuthTokenService.test.js +3 -3
  71. package/dist/core/EntitlementsPolicy.test.js +2 -2
  72. package/dist/core/GlobalSettingsService.js +115 -6
  73. package/dist/core/PlanEntitlementService.d.ts +23 -2
  74. package/dist/core/PlanEntitlementService.js +343 -57
  75. package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
  76. package/dist/core/PlanFeatureCatalog.test.js +84 -0
  77. package/dist/core/PlanVersionPolicy.test.js +35 -2
  78. package/dist/core/PlanningEntities.test.js +52 -0
  79. package/dist/core/SignupMonetizationSettings.test.js +199 -27
  80. package/dist/core/SyncReconciliationService.js +2 -65
  81. package/dist/core/SystemAdmin.test.js +42 -42
  82. package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
  83. package/dist/core/Taskforce.d.ts +225 -14
  84. package/dist/core/Taskforce.js +765 -192
  85. package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
  86. package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
  87. package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
  88. package/dist/core/WorkspaceLifecycleService.js +0 -5
  89. package/dist/core/WorkspacePermissions.test.js +25 -1
  90. package/dist/core/WorkspacePlanMode.test.js +117 -16
  91. package/dist/core/shared.d.ts +1 -1
  92. package/dist/core/shared.js +5 -1
  93. package/dist/core/types.d.ts +68 -2
  94. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -1
  95. package/dist/hooks/sync/bootstrap.js +2 -0
  96. package/dist/hooks/sync/controlPlane.d.ts +37 -0
  97. package/dist/hooks/sync/controlPlane.js +78 -0
  98. package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
  99. package/dist/hooks/sync/controlPlane.test.js +121 -0
  100. package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
  101. package/dist/hooks/sync/lifecyclePolicy.js +93 -0
  102. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
  103. package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
  104. package/dist/hooks/sync/orchestratorShared.d.ts +10 -1
  105. package/dist/hooks/sync/orchestratorShared.js +68 -47
  106. package/dist/hooks/sync/orchestratorShared.test.js +50 -1
  107. package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
  108. package/dist/hooks/sync/pullLifecycle.js +24 -0
  109. package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
  110. package/dist/hooks/sync/pullLifecycle.test.js +80 -0
  111. package/dist/hooks/sync/recovery.d.ts +16 -2
  112. package/dist/hooks/sync/recovery.js +171 -53
  113. package/dist/hooks/sync/recovery.test.d.ts +1 -0
  114. package/dist/hooks/sync/recovery.test.js +292 -0
  115. package/dist/hooks/sync/transfers.d.ts +15 -3
  116. package/dist/hooks/sync/transfers.js +179 -62
  117. package/dist/hooks/sync/transfers.test.d.ts +1 -0
  118. package/dist/hooks/sync/transfers.test.js +396 -0
  119. package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
  120. package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
  121. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
  122. package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
  123. package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
  124. package/dist/hooks/sync/useSyncStatusActions.js +88 -0
  125. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
  126. package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
  127. package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
  128. package/dist/hooks/sync/useSyncStatusControls.js +60 -0
  129. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
  130. package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
  131. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
  132. package/dist/hooks/useSyncOrchestrator.d.ts +9 -2
  133. package/dist/hooks/useSyncOrchestrator.js +359 -178
  134. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +407 -0
  135. package/dist/hooks/useTaskData.js +8 -3
  136. package/dist/hooks/useTaskData.test.js +45 -0
  137. package/dist/hooks/useTaskforce.d.ts +60 -2
  138. package/dist/hooks/useTaskforce.js +549 -240
  139. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
  140. package/dist/hooks/useTaskforce.runtime-routing.test.js +152 -0
  141. package/dist/hooks/useTaskforce.sync-behavior.test.js +2380 -212
  142. package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
  143. package/dist/hooks/useWorkspaceSyncController.js +6 -1
  144. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
  145. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +316 -35
  146. package/dist/localization/locales/en-US.d.ts +0 -1
  147. package/dist/localization/locales/en-US.js +0 -1
  148. package/dist/localization/locales/es-419.js +0 -1
  149. package/dist/localization/locales/pt-BR.js +0 -1
  150. package/dist/mcp/documentAssetRegistrar.js +3 -3
  151. package/dist/mcp/runtime.d.ts +2 -0
  152. package/dist/mcp/runtime.js +131 -64
  153. package/dist/mcp/runtime.test.js +291 -12
  154. package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
  155. package/dist/migrations/taskSchemaMigrations.js +171 -61
  156. package/dist/migrations/taskSchemaMigrations.test.js +15 -0
  157. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  158. package/dist/resources/templates/workflows/collaborate.yaml +1 -1
  159. package/dist/resources/templates/workflows/evaluate.yaml +2 -2
  160. package/dist/resources/templates/workflows/plan.yaml +1 -1
  161. package/dist/resources/templates/workflows/review.yaml +2 -2
  162. package/dist/server/annotatedAttachmentsRoutes.test.js +3 -3
  163. package/dist/server/auth/providers/apple.d.ts +33 -0
  164. package/dist/server/auth/providers/apple.js +82 -0
  165. package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
  166. package/dist/server/auth/providers/appleClientSecret.js +65 -0
  167. package/dist/server/auth/providers/github.d.ts +26 -0
  168. package/dist/server/auth/providers/github.js +86 -0
  169. package/dist/server/auth/providers/google.d.ts +21 -0
  170. package/dist/server/auth/providers/google.js +56 -0
  171. package/dist/server/auth/providers/types.d.ts +21 -0
  172. package/dist/server/auth/providers/types.js +1 -0
  173. package/dist/server/auth.d.ts +2 -0
  174. package/dist/server/auth.js +6 -3
  175. package/dist/server/documentReviewRoutes.test.js +36 -3
  176. package/dist/server/index.cookieProxy.test.js +1 -0
  177. package/dist/server/index.d.ts +12 -0
  178. package/dist/server/index.js +120 -9
  179. package/dist/server/index.rateLimit.test.js +3 -0
  180. package/dist/server/index.test.js +106 -3
  181. package/dist/server/routes/admin.d.ts +3 -0
  182. package/dist/server/routes/admin.js +272 -19
  183. package/dist/server/routes/auth.d.ts +15 -2
  184. package/dist/server/routes/auth.js +994 -57
  185. package/dist/server/routes/authSupport.d.ts +1 -1
  186. package/dist/server/routes/authSupport.js +1 -1
  187. package/dist/server/routes/billing.js +361 -35
  188. package/dist/server/routes/billing.test.js +592 -12
  189. package/dist/server/routes/documents.d.ts +1 -0
  190. package/dist/server/routes/documents.js +172 -70
  191. package/dist/server/routes/shared.d.ts +28 -2
  192. package/dist/server/routes/shared.js +69 -4
  193. package/dist/server/routes/sync.integration.test.js +437 -39
  194. package/dist/server/routes/sync.js +61 -605
  195. package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
  196. package/dist/server/routes/syncAuxRoutes.js +91 -0
  197. package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
  198. package/dist/server/routes/syncAuxRoutes.test.js +158 -0
  199. package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
  200. package/dist/server/routes/syncPullApplyRoutes.js +204 -0
  201. package/dist/server/routes/syncPushRoutes.d.ts +24 -0
  202. package/dist/server/routes/syncPushRoutes.js +212 -0
  203. package/dist/server/routes/syncRouteGuards.d.ts +36 -0
  204. package/dist/server/routes/syncRouteGuards.js +67 -0
  205. package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
  206. package/dist/server/routes/syncRouteGuards.test.js +94 -0
  207. package/dist/server/routes/syncRouteTypes.d.ts +13 -0
  208. package/dist/server/routes/syncRouteTypes.js +1 -0
  209. package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
  210. package/dist/server/routes/syncSnapshotRoutes.js +180 -0
  211. package/dist/server/routes/tasks.js +43 -1
  212. package/dist/server/routes/workspaces.js +36 -5
  213. package/dist/server/routes.js +60 -3
  214. package/dist/server/routes.test.js +1824 -171
  215. package/dist/services/workflowExportSnapshots.test.js +2 -0
  216. package/dist/shared/aiProfileSeatScope.d.ts +5 -0
  217. package/dist/shared/aiProfileSeatScope.js +21 -0
  218. package/dist/shared/passwordPolicy.d.ts +13 -0
  219. package/dist/shared/passwordPolicy.js +84 -0
  220. package/dist/shared/passwordPolicy.test.d.ts +1 -0
  221. package/dist/shared/passwordPolicy.test.js +34 -0
  222. package/dist/storage/workspaceAssetStore.d.ts +15 -3
  223. package/dist/storage/workspaceAssetStore.js +132 -44
  224. package/dist/storage/workspaceAssetStore.test.js +93 -6
  225. package/dist/styles/fonts.css +70 -0
  226. package/dist/sync/collaborationSyncPayload.d.ts +6 -1
  227. package/dist/sync/collaborationSyncPayload.js +17 -11
  228. package/dist/sync/collaborationSyncPayload.test.js +13 -1
  229. package/dist/sync/contentSyncState.d.ts +4 -8
  230. package/dist/sync/contentSyncState.js +17 -70
  231. package/dist/sync/syncApplyHandlers.d.ts +7 -0
  232. package/dist/sync/syncApplyHandlers.js +28 -13
  233. package/dist/sync/syncApplyHandlers.test.js +70 -3
  234. package/dist/sync/syncFieldSemantics.d.ts +12 -0
  235. package/dist/sync/syncFieldSemantics.js +55 -0
  236. package/dist/sync/syncResourceAdapters.d.ts +44 -0
  237. package/dist/sync/syncResourceAdapters.js +77 -0
  238. package/dist/sync/syncService.d.ts +5 -0
  239. package/dist/sync/syncService.js +52 -0
  240. package/dist/sync/syncService.test.js +57 -3
  241. package/dist/sync/taskSyncChangeKey.d.ts +2 -0
  242. package/dist/sync/taskSyncChangeKey.js +143 -0
  243. package/dist/sync/taskSyncPayload.js +3 -8
  244. package/dist/sync/workspacePullFeed.js +4 -3
  245. package/dist/sync/workspacePullFeed.test.js +43 -0
  246. package/dist/sync/workspaceRepair.js +5 -2
  247. package/dist/sync/workspaceSyncModel.d.ts +45 -0
  248. package/dist/sync/workspaceSyncModel.js +183 -28
  249. package/dist/sync/workspaceSyncModel.test.js +291 -12
  250. package/dist/sync/workspaceSyncState.d.ts +17 -0
  251. package/dist/sync/workspaceSyncState.js +2 -1
  252. package/dist/sync/workspaceSyncSurface.js +3 -8
  253. package/dist/types.d.ts +17 -0
  254. package/dist/ui/assets/AgentsModule-DrLawwNl.js +1 -0
  255. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BG6P_GVW.js → AnnotatedAttachmentWorkspace-Z9_whf7F.js} +1 -1
  256. package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
  257. package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
  258. package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
  259. package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
  260. package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +252 -0
  261. package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +1 -0
  262. package/dist/ui/assets/{InitiativesModule-Dhm7M8e7.js → InitiativesModule-9E6ParrY.js} +1 -1
  263. package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
  264. package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
  265. package/dist/ui/assets/PlansPage-CvfnMiWq.css +1 -0
  266. package/dist/ui/assets/PlansPage-DRXscYxH.js +1 -0
  267. package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
  268. package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
  269. package/dist/ui/assets/{TaskSettings-ln0aF7xc.js → TaskSettings-DZX4jk7e.js} +1 -1
  270. package/dist/ui/assets/{WorkflowsModule-BcS4afRn.js → WorkflowsModule-BA7jcEpH.js} +1 -1
  271. package/dist/ui/assets/index-9eT8e0Lu.css +1 -0
  272. package/dist/ui/assets/index-C6k75jr8.js +6 -0
  273. package/dist/ui/assets/{vendor-icons-BSUaRwF8.js → vendor-icons-DuEd_65c.js} +1 -1
  274. package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
  275. package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
  276. package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
  277. package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  278. package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
  279. package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
  280. package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
  281. package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
  282. package/dist/ui/fonts/Noir_medium.otf +0 -0
  283. package/dist/ui/fonts/Noir_medium.woff2 +0 -0
  284. package/dist/ui/fonts/Noir_regular.otf +0 -0
  285. package/dist/ui/fonts/Noir_regular.woff2 +0 -0
  286. package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
  287. package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
  288. package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
  289. package/dist/ui/index.html +3 -3
  290. package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
  291. package/dist/utils/assignees.d.ts +2 -0
  292. package/dist/utils/assignees.js +2 -0
  293. package/dist/utils/avatarUpload.d.ts +27 -0
  294. package/dist/utils/avatarUpload.js +132 -0
  295. package/dist/utils/avatarUpload.test.d.ts +1 -0
  296. package/dist/utils/avatarUpload.test.js +40 -0
  297. package/dist/utils/bootstrapTrace.d.ts +7 -0
  298. package/dist/utils/bootstrapTrace.js +29 -0
  299. package/dist/utils/commercialLifecyclePresentation.js +3 -0
  300. package/dist/utils/contextAssetEvents.d.ts +2 -0
  301. package/dist/utils/syncEventDetails.d.ts +3 -0
  302. package/dist/utils/syncEventDetails.js +64 -0
  303. package/dist/utils/syncEventDetails.test.d.ts +1 -0
  304. package/dist/utils/syncEventDetails.test.js +23 -0
  305. package/dist/utils/syncEventPresentation.d.ts +15 -0
  306. package/dist/utils/syncEventPresentation.js +129 -0
  307. package/dist/utils/syncEventPresentation.test.d.ts +1 -0
  308. package/dist/utils/syncEventPresentation.test.js +64 -0
  309. package/dist/utils/syncStatusPresentation.d.ts +72 -0
  310. package/dist/utils/syncStatusPresentation.js +217 -0
  311. package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
  312. package/dist/utils/syncStatusPresentation.test.js +164 -0
  313. package/dist/utils/taskActivity.d.ts +2 -0
  314. package/dist/utils/taskActivity.js +228 -22
  315. package/dist/utils/taskActivity.test.js +119 -8
  316. package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
  317. package/dist/utils/workspaceSyncPresentation.js +68 -2
  318. package/dist/utils/workspaceSyncPresentation.test.js +36 -1
  319. package/package.json +3 -2
  320. package/scripts/export-sqlite-to-postgres.mjs +7 -9
  321. package/scripts/run-e2e-realtime.sh +17 -3
  322. package/scripts/run-smoke.sh +17 -5
  323. package/scripts/run-soak.sh +17 -5
  324. package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  325. package/src/resources/templates/workflows/collaborate.yaml +1 -1
  326. package/src/resources/templates/workflows/evaluate.yaml +2 -2
  327. package/src/resources/templates/workflows/plan.yaml +1 -1
  328. package/src/resources/templates/workflows/review.yaml +2 -2
  329. package/dist/ui/assets/AgentsModule-N2MnQBZk.js +0 -1
  330. package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +0 -1
  331. package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +0 -252
  332. package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
  333. package/dist/ui/assets/PlansPage-Cq0zXj3I.js +0 -1
  334. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  335. package/dist/ui/assets/index-C2-OXZV7.css +0 -1
  336. package/dist/ui/assets/index-DcSI5F86.js +0 -6
  337. package/scripts/migrate-planning-model.ts +0 -233
@@ -7,6 +7,28 @@ import { TaskforceCore } from './TaskforceCore';
7
7
  import fs from 'fs';
8
8
  import path from 'path';
9
9
  import modalStyles from './components/ui/Modal.module.css';
10
+ vi.mock('react-avatar-editor', async () => {
11
+ const React = await import('react');
12
+ return {
13
+ default: React.forwardRef((_props, ref) => {
14
+ React.useImperativeHandle(ref, () => ({
15
+ getImageScaledToCanvas: () => ({
16
+ width: 1024,
17
+ height: 1024,
18
+ getContext: () => ({
19
+ drawImage: () => undefined
20
+ }),
21
+ toBlob: (callback, type) => {
22
+ callback(new Blob([new Uint8Array([1, 2, 3, 4])], { type: type || 'image/webp' }));
23
+ }
24
+ }),
25
+ getImage: () => document.createElement('canvas'),
26
+ getCroppingRect: () => ({ x: 0, y: 0, width: 1, height: 1 })
27
+ }));
28
+ return _jsx("div", { "data-testid": "avatar-editor" });
29
+ })
30
+ };
31
+ });
10
32
  const mockTasks = [
11
33
  {
12
34
  id: 'task-20260130-abc123',
@@ -1085,7 +1107,6 @@ describe('TaskforceCore Integration', () => {
1085
1107
  render(_jsx(MemoryRouter, { initialEntries: ['/'], children: _jsx(TaskforceCore, {}) }));
1086
1108
  await waitFor(() => {
1087
1109
  expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
1088
- expect(screen.getByText('Choose a plan to continue.')).toBeInTheDocument();
1089
1110
  });
1090
1111
  });
1091
1112
  it('redirects authenticated users with a blocking account access gate to the plans page', async () => {
@@ -1789,11 +1810,12 @@ describe('TaskforceCore Integration', () => {
1789
1810
  if (url.includes('/api/taskforce/workspace/assignee-options')) {
1790
1811
  return jsonResponse({ assignees: [] });
1791
1812
  }
1792
- if (url.includes('/api/taskforce/initiatives')) {
1793
- return jsonResponse([]);
1794
- }
1795
- if (url.includes('/api/taskforce/workstreams')) {
1796
- return jsonResponse([]);
1813
+ if (url.includes('/api/taskforce/planning/bootstrap')) {
1814
+ return jsonResponse({
1815
+ initiatives: [],
1816
+ workstreams: [],
1817
+ workstreamTaskSummaries: [],
1818
+ });
1797
1819
  }
1798
1820
  return jsonResponse({});
1799
1821
  });
@@ -1820,7 +1842,8 @@ describe('TaskforceCore Integration', () => {
1820
1842
  await user.click(screen.getByRole('button', { name: 'Register' }));
1821
1843
  await user.type(await screen.findByPlaceholderText('Email'), accounts.newcomer.email);
1822
1844
  await user.type(screen.getByPlaceholderText('Display name'), accounts.newcomer.displayName);
1823
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
1845
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
1846
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
1824
1847
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
1825
1848
  await waitFor(() => {
1826
1849
  expect(screen.getByTitle(/Account/i)).toBeInTheDocument();
@@ -1902,6 +1925,7 @@ describe('TaskforceCore Integration', () => {
1902
1925
  expect(screen.getByText('Create your Taskforce account.')).toBeInTheDocument();
1903
1926
  expect(screen.getByLabelText('Email')).toBeInTheDocument();
1904
1927
  expect(screen.getByLabelText('Display name')).toBeInTheDocument();
1928
+ expect(screen.getByLabelText('Confirm password')).toBeInTheDocument();
1905
1929
  expect(screen.getByPlaceholderText('Email')).toBeInTheDocument();
1906
1930
  expect(screen.getByPlaceholderText('Display name')).toBeInTheDocument();
1907
1931
  expect(screen.getByRole('button', { name: 'Create Account' })).toBeInTheDocument();
@@ -1913,6 +1937,56 @@ describe('TaskforceCore Integration', () => {
1913
1937
  });
1914
1938
  expect(container.querySelector('[aria-pressed]')).not.toBeInTheDocument();
1915
1939
  });
1940
+ it('blocks registration when the confirmation does not match', async () => {
1941
+ const user = userEvent.setup();
1942
+ const fetchMock = vi.fn((url) => {
1943
+ if (url.includes('/api/taskforce/auth/session')) {
1944
+ return Promise.resolve({
1945
+ ok: true,
1946
+ json: () => Promise.resolve({
1947
+ authenticated: false,
1948
+ runtimeMode: 'cloud',
1949
+ authRequiredForApi: false
1950
+ })
1951
+ });
1952
+ }
1953
+ if (url.includes('/api/taskforce/config')) {
1954
+ return Promise.resolve({
1955
+ ok: true,
1956
+ json: () => Promise.resolve({
1957
+ ...mockConfig,
1958
+ runtimeMode: 'cloud',
1959
+ authRequiredForApi: false,
1960
+ setupState: {
1961
+ globalSetupState: 'present',
1962
+ workspaceSetupState: 'present',
1963
+ runtimeMode: 'cloud',
1964
+ workspaceId: 'workspace-local-active',
1965
+ mode: 'core',
1966
+ forceSetup: false,
1967
+ forceGlobalSetup: false,
1968
+ forceWorkspaceSetup: false
1969
+ }
1970
+ })
1971
+ });
1972
+ }
1973
+ if (url.includes('/api/taskforce/workflow-templates')) {
1974
+ return Promise.resolve({ ok: true, json: () => Promise.resolve({ templates: [] }) });
1975
+ }
1976
+ return Promise.resolve({ ok: true, json: () => Promise.resolve({}) });
1977
+ });
1978
+ vi.stubGlobal('fetch', fetchMock);
1979
+ render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
1980
+ await user.type(await screen.findByPlaceholderText('Email'), 'mismatch@example.com');
1981
+ await user.type(screen.getByPlaceholderText('Display name'), 'Mismatch User');
1982
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
1983
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'DifferentPassphrase123!');
1984
+ await user.click(screen.getByRole('button', { name: 'Create Account' }));
1985
+ await waitFor(() => {
1986
+ expect(screen.getByText('Passwords do not match.')).toBeInTheDocument();
1987
+ });
1988
+ expect(fetchMock.mock.calls.some((call) => String(call[0]).includes('/api/taskforce/auth/register'))).toBe(false);
1989
+ });
1916
1990
  it('keeps the verification screen focused on verification and lets the user request a fresh email token', async () => {
1917
1991
  const fetchMock = vi.fn((url) => {
1918
1992
  if (url.includes('/api/taskforce/auth/session')) {
@@ -2204,7 +2278,8 @@ describe('TaskforceCore Integration', () => {
2204
2278
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2Fplans'], children: _jsx(TaskforceCore, {}) }));
2205
2279
  await user.type(await screen.findByPlaceholderText('Email'), 'local@example.com');
2206
2280
  await user.type(screen.getByPlaceholderText('Display name'), 'Local User');
2207
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
2281
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
2282
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2208
2283
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2209
2284
  await waitFor(() => {
2210
2285
  expect(screen.getByText('Configure your new workspace.')).toBeInTheDocument();
@@ -2362,11 +2437,11 @@ describe('TaskforceCore Integration', () => {
2362
2437
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
2363
2438
  await user.type(await screen.findByPlaceholderText('Email'), 'pending@example.com');
2364
2439
  await user.type(screen.getByPlaceholderText('Display name'), 'Pending User');
2365
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
2440
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
2441
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2366
2442
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2367
2443
  await waitFor(() => {
2368
2444
  expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
2369
- expect(screen.getByText('Choose a plan to continue.')).toBeInTheDocument();
2370
2445
  });
2371
2446
  expect(fetchMock.mock.calls.some(([url]) => String(url).includes('/api/taskforce/billing/checkout-session'))).toBe(false);
2372
2447
  });
@@ -2478,11 +2553,148 @@ describe('TaskforceCore Integration', () => {
2478
2553
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
2479
2554
  await user.type(await screen.findByPlaceholderText('Email'), 'brandnew@example.com');
2480
2555
  await user.type(screen.getByPlaceholderText('Display name'), 'Brand New');
2481
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
2556
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
2557
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2482
2558
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2483
2559
  await waitFor(() => {
2484
2560
  expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
2485
- expect(screen.getByText('Choose a plan to continue.')).toBeInTheDocument();
2561
+ });
2562
+ expect(screen.queryByText('Configure your new workspace.')).not.toBeInTheDocument();
2563
+ });
2564
+ it('keeps registration on plan selection while the post-auth cloud session is still resolving for a commercially gated user with no workspace', async () => {
2565
+ const user = userEvent.setup();
2566
+ let authenticated = false;
2567
+ let authSessionCallCount = 0;
2568
+ let releaseInitialSession = null;
2569
+ const initialSessionGate = new Promise((resolve) => {
2570
+ releaseInitialSession = resolve;
2571
+ });
2572
+ const fetchMock = vi.fn((input) => {
2573
+ const url = String(input);
2574
+ if (url.includes('/api/taskforce/auth/runtime-config')) {
2575
+ return jsonResponse({
2576
+ config: {
2577
+ baseUrl: 'https://app.taskforce.example',
2578
+ apiBaseUrl: 'https://app.taskforce.example/api',
2579
+ cloudAuthBaseUrl: 'https://app.taskforce.example',
2580
+ runtimeMode: 'cloud',
2581
+ authSource: 'cloud',
2582
+ workspaceMode: 'multi-cloud',
2583
+ workspaceSwitchingEnabled: true,
2584
+ authRequiredForApi: true
2585
+ }
2586
+ });
2587
+ }
2588
+ if (url.includes('/api/taskforce/auth/session')) {
2589
+ authSessionCallCount += 1;
2590
+ const payload = () => ({
2591
+ authenticated,
2592
+ runtimeMode: 'cloud',
2593
+ authRequiredForApi: true,
2594
+ userId: authenticated ? 'user-gated-pending' : null,
2595
+ workspaceId: null,
2596
+ betaAccess: true
2597
+ });
2598
+ if (authSessionCallCount === 1) {
2599
+ return initialSessionGate.then(() => jsonResponse(payload()));
2600
+ }
2601
+ return jsonResponse(payload());
2602
+ }
2603
+ if (url.includes('/api/taskforce/config')) {
2604
+ return jsonResponse({
2605
+ ...mockConfig,
2606
+ runtimeMode: 'cloud',
2607
+ authRequiredForApi: true,
2608
+ workspaceSwitchingEnabled: true,
2609
+ setupState: {
2610
+ globalSetupState: 'present',
2611
+ workspaceSetupState: 'missing',
2612
+ runtimeMode: 'cloud',
2613
+ workspaceId: 'default',
2614
+ mode: 'core',
2615
+ forceSetup: true,
2616
+ forceGlobalSetup: false,
2617
+ forceWorkspaceSetup: true
2618
+ }
2619
+ });
2620
+ }
2621
+ if (url.includes('/api/taskforce/workspaces')) {
2622
+ return jsonResponse({
2623
+ success: true,
2624
+ workspaces: [],
2625
+ currentWorkspaceId: 'default'
2626
+ });
2627
+ }
2628
+ if (url.includes('/api/taskforce/account/access-status')) {
2629
+ return jsonResponse({
2630
+ gate: 'plan_selection_required',
2631
+ hasEntitlement: true,
2632
+ canAccessApp: false,
2633
+ canAccessPlans: true,
2634
+ message: 'Choose a plan to continue.'
2635
+ });
2636
+ }
2637
+ if (url.includes('/api/taskforce/account/profile-summary')) {
2638
+ return jsonResponse({
2639
+ entitlementState: 'pending_plan_selection',
2640
+ gate: 'plan_selection_required',
2641
+ message: 'Choose a plan to continue.',
2642
+ workspaceId: null
2643
+ });
2644
+ }
2645
+ if (url.includes('/api/taskforce/public/pricing')) {
2646
+ return jsonResponse({ plans: [] });
2647
+ }
2648
+ if (url.includes('/api/taskforce/auth/register')) {
2649
+ authenticated = true;
2650
+ return jsonResponse({
2651
+ success: true,
2652
+ workspaceSetupRequired: true,
2653
+ verificationRequired: false,
2654
+ planSelectionRequired: true,
2655
+ commercialState: 'pending_plan_selection'
2656
+ });
2657
+ }
2658
+ if (url.includes('/api/taskforce/categories'))
2659
+ return jsonResponse(mockCategories);
2660
+ if (url.includes('/api/taskforce/tasks'))
2661
+ return jsonResponse({ tasks: mockTasks });
2662
+ if (url.includes('/api/taskforce/archive'))
2663
+ return jsonResponse({ archived: [] });
2664
+ if (url.includes('/api/taskforce/data-version'))
2665
+ return jsonResponse({ dataVersion: 1 });
2666
+ if (url.includes('/api/taskforce/types'))
2667
+ return jsonResponse({ types: [] });
2668
+ if (url.includes('/api/taskforce/priorities'))
2669
+ return jsonResponse({ priorities: [] });
2670
+ if (url.includes('/api/taskforce/approaches'))
2671
+ return jsonResponse({ approaches: [] });
2672
+ if (url.includes('/api/taskforce/specialists'))
2673
+ return jsonResponse({ specialists: [] });
2674
+ if (url.includes('/api/taskforce/workflow-templates'))
2675
+ return jsonResponse({ templates: [] });
2676
+ return jsonResponse({});
2677
+ });
2678
+ vi.stubGlobal('fetch', fetchMock);
2679
+ render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
2680
+ await user.type(await screen.findByPlaceholderText('Email'), 'gated-pending@example.com');
2681
+ await user.type(screen.getByPlaceholderText('Display name'), 'Gated Pending');
2682
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
2683
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2684
+ await user.click(screen.getByRole('button', { name: 'Create Account' }));
2685
+ await act(async () => {
2686
+ await Promise.resolve();
2687
+ });
2688
+ await waitFor(() => {
2689
+ expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
2690
+ });
2691
+ expect(screen.queryByText('Configure your new workspace.')).not.toBeInTheDocument();
2692
+ await act(async () => {
2693
+ releaseInitialSession?.();
2694
+ await Promise.resolve();
2695
+ });
2696
+ await waitFor(() => {
2697
+ expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
2486
2698
  });
2487
2699
  expect(screen.queryByText('Configure your new workspace.')).not.toBeInTheDocument();
2488
2700
  });
@@ -2653,7 +2865,8 @@ describe('TaskforceCore Integration', () => {
2653
2865
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&planId=basic&planVersionId=basic-v1&interval=year&next=%2F'], children: _jsx(TaskforceCore, {}) }));
2654
2866
  await user.type(await screen.findByPlaceholderText('Email'), 'checkout@example.com');
2655
2867
  await user.type(screen.getByPlaceholderText('Display name'), 'Checkout User');
2656
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
2868
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
2869
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2657
2870
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2658
2871
  await waitFor(() => {
2659
2872
  expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
@@ -2786,7 +2999,8 @@ describe('TaskforceCore Integration', () => {
2786
2999
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
2787
3000
  await user.type(await screen.findByPlaceholderText('Email'), 'free-user@example.com');
2788
3001
  await user.type(screen.getByPlaceholderText('Display name'), 'Free User');
2789
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
3002
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
3003
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2790
3004
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2791
3005
  await act(async () => {
2792
3006
  await Promise.resolve();
@@ -2912,7 +3126,8 @@ describe('TaskforceCore Integration', () => {
2912
3126
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
2913
3127
  await user.type(await screen.findByPlaceholderText('Email'), 'existing-workspace@example.com');
2914
3128
  await user.type(screen.getByPlaceholderText('Display name'), 'Existing Workspace User');
2915
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
3129
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
3130
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2916
3131
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2917
3132
  await act(async () => {
2918
3133
  await Promise.resolve();
@@ -2982,7 +3197,8 @@ describe('TaskforceCore Integration', () => {
2982
3197
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2Fplans'], children: _jsx(TaskforceCore, {}) }));
2983
3198
  await user.type(await screen.findByPlaceholderText('Email'), 'local@example.com');
2984
3199
  await user.type(screen.getByPlaceholderText('Display name'), 'Local User');
2985
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
3200
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
3201
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2986
3202
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2987
3203
  await waitFor(() => {
2988
3204
  expect(screen.getByPlaceholderText('Verification token')).toBeInTheDocument();
@@ -2990,6 +3206,156 @@ describe('TaskforceCore Integration', () => {
2990
3206
  expect(screen.queryByRole('button', { name: 'Create Account' })).not.toBeInTheDocument();
2991
3207
  expect(screen.queryByText('Subscription Plans')).not.toBeInTheDocument();
2992
3208
  });
3209
+ it('keeps verified registration on plan selection when commercial onboarding is still blocked', async () => {
3210
+ const user = userEvent.setup();
3211
+ let authenticated = false;
3212
+ const fetchMock = vi.fn((input) => {
3213
+ const url = String(input);
3214
+ if (url.includes('/api/taskforce/auth/runtime-config')) {
3215
+ return jsonResponse({
3216
+ config: {
3217
+ baseUrl: 'https://app.taskforce.example',
3218
+ apiBaseUrl: 'https://app.taskforce.example/api',
3219
+ cloudAuthBaseUrl: 'https://app.taskforce.example',
3220
+ runtimeMode: 'cloud',
3221
+ authSource: 'cloud',
3222
+ workspaceMode: 'multi-cloud',
3223
+ workspaceSwitchingEnabled: true,
3224
+ authRequiredForApi: true
3225
+ }
3226
+ });
3227
+ }
3228
+ if (url.includes('/api/taskforce/auth/session')) {
3229
+ return jsonResponse({
3230
+ authenticated,
3231
+ runtimeMode: 'cloud',
3232
+ authRequiredForApi: true,
3233
+ userId: authenticated ? 'user-verify' : null,
3234
+ workspaceId: authenticated ? 'default' : null,
3235
+ betaAccess: true
3236
+ });
3237
+ }
3238
+ if (url.includes('/api/taskforce/config')) {
3239
+ return jsonResponse({
3240
+ ...mockConfig,
3241
+ runtimeMode: 'cloud',
3242
+ authRequiredForApi: true,
3243
+ workspaceSwitchingEnabled: true,
3244
+ setupState: {
3245
+ globalSetupState: 'present',
3246
+ workspaceSetupState: authenticated ? 'missing' : 'present',
3247
+ runtimeMode: 'cloud',
3248
+ workspaceId: 'default',
3249
+ mode: 'core',
3250
+ forceSetup: false,
3251
+ forceGlobalSetup: false,
3252
+ forceWorkspaceSetup: false
3253
+ }
3254
+ });
3255
+ }
3256
+ if (url.includes('/api/taskforce/workspaces')) {
3257
+ return jsonResponse({
3258
+ success: true,
3259
+ workspaces: [],
3260
+ currentWorkspaceId: null
3261
+ });
3262
+ }
3263
+ if (url.includes('/api/taskforce/account/access-status')) {
3264
+ return jsonResponse({
3265
+ gate: 'plan_selection_required',
3266
+ hasEntitlement: true,
3267
+ canAccessApp: false,
3268
+ canAccessPlans: true,
3269
+ message: 'Choose a plan to continue.'
3270
+ });
3271
+ }
3272
+ if (url.includes('/api/taskforce/account/profile-summary')) {
3273
+ return jsonResponse({
3274
+ entitlementState: 'pending_plan_selection',
3275
+ gate: 'plan_selection_required',
3276
+ message: 'Choose a plan to continue.',
3277
+ workspaceId: null
3278
+ });
3279
+ }
3280
+ if (url.includes('/api/taskforce/public/pricing')) {
3281
+ return jsonResponse({
3282
+ plans: [{
3283
+ planId: 'free',
3284
+ displayName: 'Free',
3285
+ status: 'active',
3286
+ defaultVersion: {
3287
+ planVersionId: 'free-v1',
3288
+ versionNumber: 1,
3289
+ seatLimit: null,
3290
+ metadata: {}
3291
+ },
3292
+ features: [],
3293
+ pricing: {
3294
+ month: {
3295
+ planVersionId: 'free-v1',
3296
+ interval: 'month',
3297
+ pricingType: 'free',
3298
+ stripePriceId: 'free:free-v1:month',
3299
+ displayAmount: '$0.00',
3300
+ unitAmount: 0,
3301
+ currency: 'usd'
3302
+ },
3303
+ year: null
3304
+ }
3305
+ }]
3306
+ });
3307
+ }
3308
+ if (url.includes('/api/taskforce/auth/register')) {
3309
+ return jsonResponse({
3310
+ success: true,
3311
+ workspaceSetupRequired: true,
3312
+ verificationRequired: true,
3313
+ verificationToken: 'verify-token',
3314
+ emailSent: true,
3315
+ planSelectionRequired: true,
3316
+ commercialState: 'pending_plan_selection'
3317
+ });
3318
+ }
3319
+ if (url.includes('/api/taskforce/auth/verify-email/confirm')) {
3320
+ authenticated = true;
3321
+ return jsonResponse({ success: true });
3322
+ }
3323
+ if (url.includes('/api/taskforce/categories'))
3324
+ return jsonResponse(mockCategories);
3325
+ if (url.includes('/api/taskforce/tasks'))
3326
+ return jsonResponse({ tasks: mockTasks });
3327
+ if (url.includes('/api/taskforce/archive'))
3328
+ return jsonResponse({ archived: [] });
3329
+ if (url.includes('/api/taskforce/data-version'))
3330
+ return jsonResponse({ dataVersion: 1 });
3331
+ if (url.includes('/api/taskforce/types'))
3332
+ return jsonResponse({ types: [] });
3333
+ if (url.includes('/api/taskforce/priorities'))
3334
+ return jsonResponse({ priorities: [] });
3335
+ if (url.includes('/api/taskforce/approaches'))
3336
+ return jsonResponse({ approaches: [] });
3337
+ if (url.includes('/api/taskforce/specialists'))
3338
+ return jsonResponse({ specialists: [] });
3339
+ if (url.includes('/api/taskforce/workflow-templates'))
3340
+ return jsonResponse({ templates: [] });
3341
+ return jsonResponse({});
3342
+ });
3343
+ vi.stubGlobal('fetch', fetchMock);
3344
+ render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
3345
+ await user.type(await screen.findByPlaceholderText('Email'), 'verify-gated@example.com');
3346
+ await user.type(screen.getByPlaceholderText('Display name'), 'Verify Gated User');
3347
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
3348
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
3349
+ await user.click(screen.getByRole('button', { name: 'Create Account' }));
3350
+ await waitFor(() => {
3351
+ expect(screen.getByPlaceholderText('Verification token')).toHaveValue('verify-token');
3352
+ });
3353
+ await user.click(screen.getByRole('button', { name: 'Verify Email' }));
3354
+ await waitFor(() => {
3355
+ expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
3356
+ });
3357
+ expect(screen.queryByText('Configure your new workspace.')).not.toBeInTheDocument();
3358
+ });
2993
3359
  it('allows returning to sign-in/register from forgot mode in local runtime', async () => {
2994
3360
  const user = userEvent.setup();
2995
3361
  const fetchMock = vi.fn((url) => {
@@ -3291,7 +3657,8 @@ describe('TaskforceCore Integration', () => {
3291
3657
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
3292
3658
  await user.type(await screen.findByPlaceholderText('Email'), 'local@example.com');
3293
3659
  await user.type(screen.getByPlaceholderText('Display name'), 'Local User');
3294
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
3660
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
3661
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
3295
3662
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
3296
3663
  await waitFor(() => {
3297
3664
  expect(screen.getByText('Choose a plan to continue.')).toBeInTheDocument();
@@ -3677,6 +4044,7 @@ describe('TaskforceCore Integration', () => {
3677
4044
  await waitFor(() => {
3678
4045
  expect(screen.getByText('Create your account to join this workspace.')).toBeInTheDocument();
3679
4046
  expect(screen.getByPlaceholderText('Create password')).toBeInTheDocument();
4047
+ expect(screen.getByPlaceholderText('Confirm password')).toBeInTheDocument();
3680
4048
  expect(screen.getByRole('button', { name: 'Create Account and Join' })).toBeInTheDocument();
3681
4049
  });
3682
4050
  });
@@ -5417,6 +5785,15 @@ describe('TaskforceCore Integration', () => {
5417
5785
  expect(useTaskforceSource).toContain("|| path.startsWith('/api/taskforce/account/')");
5418
5786
  expect(useTaskforceSource).toContain("|| path.startsWith('/api/taskforce/billing/')");
5419
5787
  });
5788
+ it('keeps plans on a dedicated surface while preserving the faster exit flow', () => {
5789
+ const standaloneLayoutPath = path.join(process.cwd(), 'src/components/views/StandaloneLayout.tsx');
5790
+ const standaloneLayoutSource = fs.readFileSync(standaloneLayoutPath, 'utf8');
5791
+ expect(standaloneLayoutSource).toContain('{!isPlansScreen && <WorkspaceHeaderActions actions={universalFilterActions} />}');
5792
+ expect(standaloneLayoutSource).toContain('{!isPlansScreen && showFilters && workspaceFilterBar}');
5793
+ expect(standaloneLayoutSource).not.toMatch(/\{isPlansScreen \? \([\s\S]*\{workspaceToolRail\}[\s\S]*<PlansPage/);
5794
+ expect(standaloneLayoutSource).toContain('closePlansScreen();');
5795
+ expect(standaloneLayoutSource).toContain('void (async () => {');
5796
+ });
5420
5797
  it('uses shared /login flow for local sign in instead of a duplicate modal', async () => {
5421
5798
  const standaloneLayoutPath = path.join(process.cwd(), 'src/components/views/StandaloneLayout.tsx');
5422
5799
  const standaloneLayoutSource = fs.readFileSync(standaloneLayoutPath, 'utf8');
@@ -5601,11 +5978,12 @@ describe('TaskforceCore Integration', () => {
5601
5978
  if (url.includes('/api/taskforce/workflow-templates')) {
5602
5979
  return jsonResponse({ templates: [] });
5603
5980
  }
5604
- if (url.includes('/api/taskforce/initiatives')) {
5605
- return jsonResponse([]);
5606
- }
5607
- if (url.includes('/api/taskforce/workstreams')) {
5608
- return jsonResponse([]);
5981
+ if (url.includes('/api/taskforce/planning/bootstrap')) {
5982
+ return jsonResponse({
5983
+ initiatives: [],
5984
+ workstreams: [],
5985
+ workstreamTaskSummaries: [],
5986
+ });
5609
5987
  }
5610
5988
  return jsonResponse({});
5611
5989
  });
@@ -5947,9 +6325,9 @@ describe('TaskforceCore Integration', () => {
5947
6325
  occurredAt: '2026-03-15T14:28:17.000Z',
5948
6326
  eventType: 'apply',
5949
6327
  status: 'error',
5950
- errorMessage: 'Detected asset reference number mismatch during normal sync.',
6328
+ errorMessage: 'Detected task reference number mismatch during normal sync.',
5951
6329
  details: {
5952
- path: 'workspaces/workspace-local-active/assets/images/hero.png',
6330
+ taskId: 'task-1',
5953
6331
  existingReferenceNumber: 12,
5954
6332
  incomingReferenceNumber: 18
5955
6333
  }
@@ -7183,6 +7561,7 @@ describe('TaskforceCore Integration', () => {
7183
7561
  const user = userEvent.setup();
7184
7562
  let savedDisplayName = 'Original Name';
7185
7563
  let savedAvatarUrl = '';
7564
+ const cloudAuthBaseUrl = 'https://performance-app.taskforcehq.ai';
7186
7565
  const fetchMock = vi.fn((input, init) => {
7187
7566
  const url = String(input);
7188
7567
  if (url.includes('/api/taskforce/auth/runtime-config')) {
@@ -7222,6 +7601,9 @@ describe('TaskforceCore Integration', () => {
7222
7601
  ...mockConfig,
7223
7602
  runtimeMode: 'local',
7224
7603
  authRequiredForApi: false,
7604
+ apiBaseUrl: cloudAuthBaseUrl,
7605
+ cloudAuthBaseUrl,
7606
+ cloudAuthViaLocalProxy: true,
7225
7607
  workspaceId: 'workspace-local-active',
7226
7608
  setupState: {
7227
7609
  globalSetupState: 'present',
@@ -7257,13 +7639,12 @@ describe('TaskforceCore Integration', () => {
7257
7639
  return jsonResponse({
7258
7640
  success: true,
7259
7641
  draftId: 'avatar-draft-1',
7260
- uploadUrl: 'https://upload.example.com/avatar-draft-1',
7261
7642
  relativePath: 'users/user-1/profile/avatar/avatar-draft-1-avatar.png',
7262
7643
  path: '/api/taskforce/context/users%2Fuser-1%2Fprofile%2Favatar%2Favatar-draft-1-avatar.png'
7263
7644
  });
7264
7645
  }
7265
- if (url.includes('https://upload.example.com/avatar-draft-1')) {
7266
- return Promise.resolve({ ok: true, status: 200, json: async () => ({}) });
7646
+ if (url.includes('/api/taskforce/auth/profile/avatar/upload')) {
7647
+ return jsonResponse({ success: true, draftId: 'avatar-draft-1' });
7267
7648
  }
7268
7649
  if (url.includes('/api/taskforce/auth/profile/avatar/finalize')) {
7269
7650
  return jsonResponse({
@@ -7304,8 +7685,21 @@ describe('TaskforceCore Integration', () => {
7304
7685
  const displayNameInput = within(modal).getByPlaceholderText('Display name');
7305
7686
  await user.clear(displayNameInput);
7306
7687
  await user.type(displayNameInput, 'Updated Name');
7307
- const avatarInput = within(modal).getByLabelText('Upload profile photo');
7688
+ const getContextSpy = vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue({
7689
+ drawImage: () => undefined
7690
+ });
7691
+ const toBlobSpy = vi.spyOn(HTMLCanvasElement.prototype, 'toBlob').mockImplementation(function toBlob(callback, type) {
7692
+ callback(new Blob([new Uint8Array([1, 2, 3, 4])], { type: type || 'image/webp' }));
7693
+ });
7694
+ await user.click(within(modal).getByLabelText('Edit profile photo'));
7695
+ const avatarManager = await screen.findByText('Edit Profile Photo');
7696
+ const avatarManagerModal = avatarManager.closest(`.${modalStyles.modal}`);
7697
+ const avatarInput = within(avatarManagerModal).getByLabelText('Choose profile photo');
7308
7698
  await user.upload(avatarInput, new File(['avatar'], 'avatar.png', { type: 'image/png' }));
7699
+ await user.click(within(avatarManagerModal).getByRole('button', { name: 'Apply Photo' }));
7700
+ await waitFor(() => {
7701
+ expect(screen.queryByText('Edit Profile Photo')).not.toBeInTheDocument();
7702
+ });
7309
7703
  await user.click(within(modal).getByRole('button', { name: 'Save Profile' }));
7310
7704
  await waitFor(() => {
7311
7705
  const profileCall = fetchMock.mock.calls.find((call) => {
@@ -7317,6 +7711,17 @@ describe('TaskforceCore Integration', () => {
7317
7711
  expect(payload.displayName).toBe('Updated Name');
7318
7712
  expect(payload.avatarDraftId).toBe('avatar-draft-1');
7319
7713
  });
7714
+ expect(fetchMock).toHaveBeenCalledWith('/api/taskforce/auth/profile/avatar/init', expect.objectContaining({ method: 'POST' }));
7715
+ const avatarUploadCall = fetchMock.mock.calls.find((call) => String(call[0]) === '/api/taskforce/auth/profile/avatar/upload');
7716
+ expect(avatarUploadCall).toBeTruthy();
7717
+ expect(avatarUploadCall?.[1]).toEqual(expect.objectContaining({ method: 'POST' }));
7718
+ const avatarUploadHeaders = new Headers(avatarUploadCall?.[1]?.headers);
7719
+ expect(avatarUploadHeaders.get('Content-Type')).toBe('image/webp');
7720
+ expect(avatarUploadHeaders.get('x-taskforce-avatar-draft-id')).toBe('avatar-draft-1');
7721
+ expect(fetchMock).toHaveBeenCalledWith('/api/taskforce/auth/profile/avatar/finalize', expect.objectContaining({ method: 'POST' }));
7722
+ expect(fetchMock.mock.calls.some((call) => String(call[0]).startsWith(`${cloudAuthBaseUrl}/api/taskforce/auth/profile/avatar/`))).toBe(false);
7723
+ getContextSpy.mockRestore();
7724
+ toBlobSpy.mockRestore();
7320
7725
  await waitFor(() => {
7321
7726
  expect(screen.queryByText('Edit Profile')).not.toBeInTheDocument();
7322
7727
  });