@taskforcehq/taskforce 0.3.305 → 0.3.307

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 (440) hide show
  1. package/README.md +0 -2
  2. package/dist/Taskforce.module.css +603 -107
  3. package/dist/TaskforceCore.js +216 -43
  4. package/dist/TaskforceCore.test.js +611 -28
  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 +46 -2
  14. package/dist/compat/workspaceSyncCompat.test.js +38 -1
  15. package/dist/components/activity/EntityActivityTimeline.d.ts +34 -0
  16. package/dist/components/activity/EntityActivityTimeline.js +81 -0
  17. package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
  18. package/dist/components/context/ContextAttachmentManager.js +561 -0
  19. package/dist/components/features/AgentsModule.d.ts +14 -1
  20. package/dist/components/features/AgentsModule.js +366 -18
  21. package/dist/components/features/AgentsModule.test.js +507 -8
  22. package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +3 -1
  23. package/dist/components/features/AnnotatedAttachmentWorkspace.js +118 -4
  24. package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +52 -0
  25. package/dist/components/features/DocumentIndex.d.ts +2 -2
  26. package/dist/components/features/DocumentIndex.js +3 -4
  27. package/dist/components/features/DocumentViewer.d.ts +7 -1
  28. package/dist/components/features/DocumentViewer.js +62 -4
  29. package/dist/components/features/DocumentWorkspace.d.ts +10 -2
  30. package/dist/components/features/DocumentWorkspace.js +15 -4
  31. package/dist/components/features/DocumentWorkspace.test.js +51 -1
  32. package/dist/components/features/TaskSettings.d.ts +2 -0
  33. package/dist/components/features/TaskSettings.js +83 -78
  34. package/dist/components/features/TaskSettings.test.js +171 -82
  35. package/dist/components/features/documentWorkspaceModel.d.ts +1 -1
  36. package/dist/components/task/TaskActionHeader.js +11 -5
  37. package/dist/components/task/TaskActionHeader.test.js +10 -0
  38. package/dist/components/task/TaskCard.js +24 -29
  39. package/dist/components/task/TaskCard.test.js +38 -3
  40. package/dist/components/task/TaskContextUpload.js +4 -526
  41. package/dist/components/task/TaskContextUpload.test.js +3 -3
  42. package/dist/components/task/TaskForm.js +61 -116
  43. package/dist/components/task/TaskForm.test.js +110 -8
  44. package/dist/components/task/TaskKanban.d.ts +2 -1
  45. package/dist/components/task/TaskKanban.js +94 -21
  46. package/dist/components/task/TaskKanban.test.js +36 -0
  47. package/dist/components/task/TaskList.js +5 -1
  48. package/dist/components/task/TaskList.test.js +4 -0
  49. package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
  50. package/dist/components/ui/EditableAvatarButton.js +18 -0
  51. package/dist/components/ui/HierarchyActionBadge.d.ts +10 -0
  52. package/dist/components/ui/HierarchyActionBadge.js +5 -0
  53. package/dist/components/views/AppShellHeader.d.ts +3 -1
  54. package/dist/components/views/AppShellHeader.js +27 -11
  55. package/dist/components/views/AppShellHeader.test.js +18 -1
  56. package/dist/components/views/PlanComparisonPage.d.ts +9 -1
  57. package/dist/components/views/PlanComparisonPage.js +78 -14
  58. package/dist/components/views/PlanComparisonPage.test.js +257 -0
  59. package/dist/components/views/PlansPage.js +127 -36
  60. package/dist/components/views/PlansPage.test.js +294 -12
  61. package/dist/components/views/StandaloneLayout.js +529 -592
  62. package/dist/components/views/WidgetView.js +10 -8
  63. package/dist/components/views/panels/PlanningDrawer.d.ts +25 -5
  64. package/dist/components/views/panels/PlanningDrawer.js +270 -55
  65. package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
  66. package/dist/components/views/panels/PlanningDrawer.test.js +529 -0
  67. package/dist/components/views/planningScope.d.ts +20 -0
  68. package/dist/components/views/planningScope.js +25 -0
  69. package/dist/components/views/planningScope.test.d.ts +1 -0
  70. package/dist/components/views/planningScope.test.js +60 -0
  71. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +26 -1
  72. package/dist/components/views/standalone/modals/AccountHubModal.js +76 -2
  73. package/dist/components/views/standalone/modals/AccountHubModal.test.js +20 -3
  74. package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
  75. package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
  76. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
  77. package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
  78. package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
  79. package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
  80. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
  81. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
  82. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
  83. package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
  84. package/dist/config/envSchema.js +1 -0
  85. package/dist/core/AiProfileService.d.ts +46 -2
  86. package/dist/core/AiProfileService.js +1073 -82
  87. package/dist/core/AiProfileService.test.js +19 -1
  88. package/dist/core/AiProfiles.test.js +1249 -44
  89. package/dist/core/AttachmentLinkService.d.ts +3 -0
  90. package/dist/core/AttachmentLinkService.js +110 -23
  91. package/dist/core/AuthIdentityService.d.ts +107 -0
  92. package/dist/core/AuthIdentityService.js +284 -0
  93. package/dist/core/AuthTokenService.d.ts +4 -0
  94. package/dist/core/AuthTokenService.js +34 -6
  95. package/dist/core/AuthTokenService.test.js +3 -3
  96. package/dist/core/EntitlementsPolicy.test.js +2 -2
  97. package/dist/core/GlobalSettingsService.js +127 -6
  98. package/dist/core/McpTokenService.d.ts +12 -0
  99. package/dist/core/McpTokenService.js +44 -1
  100. package/dist/core/PlanEntitlementService.d.ts +23 -2
  101. package/dist/core/PlanEntitlementService.js +343 -57
  102. package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
  103. package/dist/core/PlanFeatureCatalog.test.js +84 -0
  104. package/dist/core/PlanVersionPolicy.test.js +35 -2
  105. package/dist/core/PlanningEntities.test.js +418 -4
  106. package/dist/core/SignupMonetizationSettings.test.js +199 -27
  107. package/dist/core/SyncReconciliationService.js +2 -65
  108. package/dist/core/SystemAdmin.test.js +42 -42
  109. package/dist/core/TaskActivityService.d.ts +28 -1
  110. package/dist/core/TaskActivityService.js +227 -46
  111. package/dist/core/TaskActivitySync.test.d.ts +1 -0
  112. package/dist/core/TaskActivitySync.test.js +90 -0
  113. package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
  114. package/dist/core/TaskAuditTimeline.test.js +96 -0
  115. package/dist/core/Taskforce.d.ts +415 -40
  116. package/dist/core/Taskforce.ids.test.js +58 -0
  117. package/dist/core/Taskforce.js +1543 -266
  118. package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
  119. package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
  120. package/dist/core/Taskforce.mcpAuth.test.js +109 -0
  121. package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
  122. package/dist/core/WorkspaceLifecycleService.js +0 -5
  123. package/dist/core/WorkspacePermissions.test.js +25 -1
  124. package/dist/core/WorkspacePlanMode.test.js +117 -16
  125. package/dist/core/shared.d.ts +1 -1
  126. package/dist/core/shared.js +5 -1
  127. package/dist/core/types.d.ts +75 -2
  128. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +28 -2
  129. package/dist/hooks/bootstrapTimeouts.d.ts +4 -0
  130. package/dist/hooks/bootstrapTimeouts.js +8 -0
  131. package/dist/hooks/bootstrapTimeouts.test.d.ts +1 -0
  132. package/dist/hooks/bootstrapTimeouts.test.js +12 -0
  133. package/dist/hooks/sync/bootstrap.js +2 -0
  134. package/dist/hooks/sync/controlPlane.d.ts +37 -0
  135. package/dist/hooks/sync/controlPlane.js +78 -0
  136. package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
  137. package/dist/hooks/sync/controlPlane.test.js +121 -0
  138. package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
  139. package/dist/hooks/sync/lifecyclePolicy.js +93 -0
  140. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
  141. package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
  142. package/dist/hooks/sync/orchestratorShared.d.ts +13 -4
  143. package/dist/hooks/sync/orchestratorShared.js +129 -48
  144. package/dist/hooks/sync/orchestratorShared.test.js +50 -1
  145. package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
  146. package/dist/hooks/sync/pullLifecycle.js +24 -0
  147. package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
  148. package/dist/hooks/sync/pullLifecycle.test.js +80 -0
  149. package/dist/hooks/sync/recovery.d.ts +16 -2
  150. package/dist/hooks/sync/recovery.js +179 -53
  151. package/dist/hooks/sync/recovery.test.d.ts +1 -0
  152. package/dist/hooks/sync/recovery.test.js +292 -0
  153. package/dist/hooks/sync/transfers.d.ts +16 -3
  154. package/dist/hooks/sync/transfers.js +210 -66
  155. package/dist/hooks/sync/transfers.test.d.ts +1 -0
  156. package/dist/hooks/sync/transfers.test.js +928 -0
  157. package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
  158. package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
  159. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
  160. package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
  161. package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
  162. package/dist/hooks/sync/useSyncStatusActions.js +88 -0
  163. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
  164. package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
  165. package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
  166. package/dist/hooks/sync/useSyncStatusControls.js +60 -0
  167. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
  168. package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
  169. package/dist/hooks/useFilterSort.js +13 -13
  170. package/dist/hooks/useFilterSort.test.js +87 -1
  171. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
  172. package/dist/hooks/useSyncOrchestrator.d.ts +9 -2
  173. package/dist/hooks/useSyncOrchestrator.js +409 -183
  174. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +407 -0
  175. package/dist/hooks/useTaskData.js +8 -3
  176. package/dist/hooks/useTaskData.test.js +45 -0
  177. package/dist/hooks/useTaskforce.d.ts +145 -52
  178. package/dist/hooks/useTaskforce.js +592 -253
  179. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
  180. package/dist/hooks/useTaskforce.runtime-routing.test.js +246 -0
  181. package/dist/hooks/useTaskforce.sync-behavior.test.js +2468 -201
  182. package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
  183. package/dist/hooks/useWorkspaceSyncController.js +13 -2
  184. package/dist/hooks/useWorkspaceSyncController.test.js +48 -0
  185. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
  186. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +328 -40
  187. package/dist/localization/locales/en-US.d.ts +0 -1
  188. package/dist/localization/locales/en-US.js +0 -1
  189. package/dist/localization/locales/es-419.js +0 -1
  190. package/dist/localization/locales/pt-BR.js +0 -1
  191. package/dist/mcp/canonicalAssetHelpers.d.ts +16 -4
  192. package/dist/mcp/canonicalAssetHelpers.js +18 -10
  193. package/dist/mcp/canonicalAssetHelpers.test.d.ts +1 -0
  194. package/dist/mcp/canonicalAssetHelpers.test.js +67 -0
  195. package/dist/mcp/clientIntegrations.d.ts +1 -0
  196. package/dist/mcp/clientIntegrations.js +25 -0
  197. package/dist/mcp/clientIntegrations.test.js +14 -3
  198. package/dist/mcp/clientProfileDefaults.js +3 -3
  199. package/dist/mcp/collaborationRegistrar.js +7 -7
  200. package/dist/mcp/documentAssetRegistrar.js +116 -10
  201. package/dist/mcp/runtime.d.ts +3 -0
  202. package/dist/mcp/runtime.js +1099 -185
  203. package/dist/mcp/runtime.test.js +1491 -140
  204. package/dist/mcp/taskAttachmentTypes.d.ts +1 -1
  205. package/dist/mcp/taskPlanningRegistrar.js +48 -0
  206. package/dist/mcp/toolRegistry.d.ts +56 -0
  207. package/dist/mcp/toolRegistry.js +8 -0
  208. package/dist/mcp/toolRegistry.test.js +4 -0
  209. package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
  210. package/dist/migrations/taskSchemaMigrations.js +192 -61
  211. package/dist/migrations/taskSchemaMigrations.test.js +15 -0
  212. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  213. package/dist/resources/templates/workflows/collaborate.yaml +1 -1
  214. package/dist/resources/templates/workflows/evaluate.yaml +2 -2
  215. package/dist/resources/templates/workflows/plan.yaml +1 -1
  216. package/dist/resources/templates/workflows/review.yaml +2 -2
  217. package/dist/server/aiProfileAvatarAssets.d.ts +49 -0
  218. package/dist/server/aiProfileAvatarAssets.js +319 -0
  219. package/dist/server/aiProfileAvatarAssets.test.d.ts +1 -0
  220. package/dist/server/aiProfileAvatarAssets.test.js +213 -0
  221. package/dist/server/annotatedAttachmentsRoutes.test.js +69 -10
  222. package/dist/server/auth/providers/apple.d.ts +33 -0
  223. package/dist/server/auth/providers/apple.js +82 -0
  224. package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
  225. package/dist/server/auth/providers/appleClientSecret.js +65 -0
  226. package/dist/server/auth/providers/github.d.ts +26 -0
  227. package/dist/server/auth/providers/github.js +86 -0
  228. package/dist/server/auth/providers/google.d.ts +21 -0
  229. package/dist/server/auth/providers/google.js +56 -0
  230. package/dist/server/auth/providers/types.d.ts +21 -0
  231. package/dist/server/auth/providers/types.js +1 -0
  232. package/dist/server/auth.d.ts +2 -0
  233. package/dist/server/auth.js +6 -3
  234. package/dist/server/documentReviewRoutes.test.js +36 -3
  235. package/dist/server/index.cookieProxy.test.js +1 -0
  236. package/dist/server/index.d.ts +12 -0
  237. package/dist/server/index.js +124 -9
  238. package/dist/server/index.rateLimit.test.js +3 -0
  239. package/dist/server/index.test.js +111 -3
  240. package/dist/server/routes/admin.d.ts +3 -0
  241. package/dist/server/routes/admin.js +459 -28
  242. package/dist/server/routes/annotatedAttachments.js +51 -0
  243. package/dist/server/routes/auth.d.ts +26 -2
  244. package/dist/server/routes/auth.js +1170 -71
  245. package/dist/server/routes/authSupport.d.ts +5 -1
  246. package/dist/server/routes/authSupport.js +13 -2
  247. package/dist/server/routes/billing.js +372 -35
  248. package/dist/server/routes/billing.test.js +595 -12
  249. package/dist/server/routes/documents.d.ts +1 -0
  250. package/dist/server/routes/documents.js +182 -70
  251. package/dist/server/routes/shared.d.ts +28 -2
  252. package/dist/server/routes/shared.js +69 -14
  253. package/dist/server/routes/sync.integration.test.js +658 -39
  254. package/dist/server/routes/sync.js +74 -608
  255. package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
  256. package/dist/server/routes/syncAuxRoutes.js +91 -0
  257. package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
  258. package/dist/server/routes/syncAuxRoutes.test.js +158 -0
  259. package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
  260. package/dist/server/routes/syncPullApplyRoutes.js +204 -0
  261. package/dist/server/routes/syncPushRoutes.d.ts +24 -0
  262. package/dist/server/routes/syncPushRoutes.js +212 -0
  263. package/dist/server/routes/syncRouteGuards.d.ts +36 -0
  264. package/dist/server/routes/syncRouteGuards.js +67 -0
  265. package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
  266. package/dist/server/routes/syncRouteGuards.test.js +94 -0
  267. package/dist/server/routes/syncRouteTypes.d.ts +13 -0
  268. package/dist/server/routes/syncRouteTypes.js +1 -0
  269. package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
  270. package/dist/server/routes/syncSnapshotRoutes.js +182 -0
  271. package/dist/server/routes/tasks.js +55 -10
  272. package/dist/server/routes/workspaces.js +36 -5
  273. package/dist/server/routes.js +147 -11
  274. package/dist/server/routes.test.js +2704 -183
  275. package/dist/services/workflowExportSnapshots.test.js +2 -0
  276. package/dist/shared/aiProfileSeatScope.d.ts +5 -0
  277. package/dist/shared/aiProfileSeatScope.js +21 -0
  278. package/dist/shared/aiProfileSurfaceType.d.ts +1 -1
  279. package/dist/shared/aiProfileSurfaceType.js +10 -8
  280. package/dist/shared/passwordPolicy.d.ts +13 -0
  281. package/dist/shared/passwordPolicy.js +84 -0
  282. package/dist/shared/passwordPolicy.test.d.ts +1 -0
  283. package/dist/shared/passwordPolicy.test.js +34 -0
  284. package/dist/storage/objectStorage.d.ts +1 -0
  285. package/dist/storage/objectStorage.js +2 -1
  286. package/dist/storage/objectStorageClient.d.ts +1 -0
  287. package/dist/storage/objectStorageClient.js +41 -8
  288. package/dist/storage/objectStorageClient.test.js +26 -1
  289. package/dist/storage/objectStorageConfig.js +3 -1
  290. package/dist/storage/objectStorageConfig.test.js +13 -1
  291. package/dist/storage/workspaceAssetStore.d.ts +15 -3
  292. package/dist/storage/workspaceAssetStore.js +132 -44
  293. package/dist/storage/workspaceAssetStore.test.js +93 -6
  294. package/dist/styles/fonts.css +70 -0
  295. package/dist/sync/collaborationSyncPayload.d.ts +6 -1
  296. package/dist/sync/collaborationSyncPayload.js +19 -11
  297. package/dist/sync/collaborationSyncPayload.test.js +14 -1
  298. package/dist/sync/collaborationSyncState.d.ts +3 -3
  299. package/dist/sync/collaborationSyncState.js +19 -12
  300. package/dist/sync/contentSyncPayload.d.ts +5 -1
  301. package/dist/sync/contentSyncPayload.js +9 -0
  302. package/dist/sync/contentSyncPayload.test.js +39 -0
  303. package/dist/sync/contentSyncState.d.ts +9 -9
  304. package/dist/sync/contentSyncState.js +248 -107
  305. package/dist/sync/planningSyncPayload.js +5 -0
  306. package/dist/sync/planningSyncPayload.test.js +24 -0
  307. package/dist/sync/syncApplyHandlers.d.ts +11 -4
  308. package/dist/sync/syncApplyHandlers.js +115 -42
  309. package/dist/sync/syncApplyHandlers.test.js +251 -4
  310. package/dist/sync/syncFieldSemantics.d.ts +12 -0
  311. package/dist/sync/syncFieldSemantics.js +55 -0
  312. package/dist/sync/syncResourceAdapters.d.ts +46 -0
  313. package/dist/sync/syncResourceAdapters.js +79 -0
  314. package/dist/sync/syncService.d.ts +6 -0
  315. package/dist/sync/syncService.js +65 -1
  316. package/dist/sync/syncService.test.js +96 -3
  317. package/dist/sync/taskSyncChangeKey.d.ts +2 -0
  318. package/dist/sync/taskSyncChangeKey.js +143 -0
  319. package/dist/sync/taskSyncPayload.js +3 -8
  320. package/dist/sync/workspacePullFeed.d.ts +4 -0
  321. package/dist/sync/workspacePullFeed.js +132 -72
  322. package/dist/sync/workspacePullFeed.test.js +92 -0
  323. package/dist/sync/workspaceRepair.js +5 -2
  324. package/dist/sync/workspaceSyncModel.d.ts +73 -6
  325. package/dist/sync/workspaceSyncModel.js +342 -73
  326. package/dist/sync/workspaceSyncModel.test.js +559 -14
  327. package/dist/sync/workspaceSyncState.d.ts +17 -0
  328. package/dist/sync/workspaceSyncState.js +2 -1
  329. package/dist/sync/workspaceSyncSurface.js +3 -8
  330. package/dist/types.d.ts +50 -2
  331. package/dist/ui/assets/AgentsModule-2y82_3DO.js +1 -0
  332. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BTLZZCiQ.js +3 -0
  333. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BVKew-mH.css +1 -0
  334. package/dist/ui/assets/ContextAttachmentManager-D2epuNnG.js +3 -0
  335. package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
  336. package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
  337. package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
  338. package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
  339. package/dist/ui/assets/DocumentWorkspace-D4rJKXzd.css +1 -0
  340. package/dist/ui/assets/DocumentWorkspace-sVa-3Do6.js +252 -0
  341. package/dist/ui/assets/EntityActivityTimeline-DyqvvhuF.css +1 -0
  342. package/dist/ui/assets/EntityActivityTimeline-Zods27y_.js +1 -0
  343. package/dist/ui/assets/{InitiativesModule-Dhm7M8e7.js → InitiativesModule-D4XseTwW.js} +1 -1
  344. package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
  345. package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
  346. package/dist/ui/assets/PlansPage-D5AcbO0L.js +1 -0
  347. package/dist/ui/assets/PlansPage-Dvqsz5zc.css +1 -0
  348. package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
  349. package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
  350. package/dist/ui/assets/TaskContextUpload-uC5qx4Bv.js +1 -0
  351. package/dist/ui/assets/TaskSettings-CqvPAsTv.js +9 -0
  352. package/dist/ui/assets/{WorkflowsModule-BcS4afRn.js → WorkflowsModule-oOA9bcdK.js} +4 -4
  353. package/dist/ui/assets/documentReferences-DPZuTgHh.js +1 -0
  354. package/dist/ui/assets/index-607WPo_X.js +4 -0
  355. package/dist/ui/assets/index-CYyodXsg.css +1 -0
  356. package/dist/ui/assets/{vendor-icons-BSUaRwF8.js → vendor-icons-B32hGuF2.js} +1 -1
  357. package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
  358. package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
  359. package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
  360. package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  361. package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
  362. package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
  363. package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
  364. package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
  365. package/dist/ui/fonts/Noir_medium.otf +0 -0
  366. package/dist/ui/fonts/Noir_medium.woff2 +0 -0
  367. package/dist/ui/fonts/Noir_regular.otf +0 -0
  368. package/dist/ui/fonts/Noir_regular.woff2 +0 -0
  369. package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
  370. package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
  371. package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
  372. package/dist/ui/index.html +3 -3
  373. package/dist/ui/product/agent manager.png +0 -0
  374. package/dist/ui/product/agent_manager_02.png +0 -0
  375. package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
  376. package/dist/utils/aiProfileEvents.d.ts +7 -0
  377. package/dist/utils/aiProfileEvents.js +6 -0
  378. package/dist/utils/assignees.d.ts +8 -0
  379. package/dist/utils/assignees.js +10 -1
  380. package/dist/utils/assignees.test.d.ts +1 -0
  381. package/dist/utils/assignees.test.js +25 -0
  382. package/dist/utils/avatarDisplay.d.ts +1 -0
  383. package/dist/utils/avatarDisplay.js +15 -0
  384. package/dist/utils/avatarUpload.d.ts +27 -0
  385. package/dist/utils/avatarUpload.js +132 -0
  386. package/dist/utils/avatarUpload.test.d.ts +1 -0
  387. package/dist/utils/avatarUpload.test.js +40 -0
  388. package/dist/utils/bootstrapTrace.d.ts +7 -0
  389. package/dist/utils/bootstrapTrace.js +29 -0
  390. package/dist/utils/commercialLifecyclePresentation.js +3 -0
  391. package/dist/utils/contextAssetEvents.d.ts +2 -0
  392. package/dist/utils/contextFiles.d.ts +3 -1
  393. package/dist/utils/contextFiles.js +2 -1
  394. package/dist/utils/syncEventDetails.d.ts +3 -0
  395. package/dist/utils/syncEventDetails.js +64 -0
  396. package/dist/utils/syncEventDetails.test.d.ts +1 -0
  397. package/dist/utils/syncEventDetails.test.js +23 -0
  398. package/dist/utils/syncEventPresentation.d.ts +15 -0
  399. package/dist/utils/syncEventPresentation.js +129 -0
  400. package/dist/utils/syncEventPresentation.test.d.ts +1 -0
  401. package/dist/utils/syncEventPresentation.test.js +64 -0
  402. package/dist/utils/syncStatusPresentation.d.ts +72 -0
  403. package/dist/utils/syncStatusPresentation.js +217 -0
  404. package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
  405. package/dist/utils/syncStatusPresentation.test.js +164 -0
  406. package/dist/utils/taskActivity.d.ts +9 -1
  407. package/dist/utils/taskActivity.js +290 -22
  408. package/dist/utils/taskActivity.test.js +141 -8
  409. package/dist/utils/taskSearch.d.ts +22 -0
  410. package/dist/utils/taskSearch.js +87 -0
  411. package/dist/utils/taskSearch.test.d.ts +1 -0
  412. package/dist/utils/taskSearch.test.js +69 -0
  413. package/dist/utils/taskUtils.js +10 -3
  414. package/dist/utils/taskUtils.test.d.ts +1 -0
  415. package/dist/utils/taskUtils.test.js +19 -0
  416. package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
  417. package/dist/utils/workspaceSyncPresentation.js +68 -2
  418. package/dist/utils/workspaceSyncPresentation.test.js +36 -1
  419. package/package.json +3 -2
  420. package/scripts/export-sqlite-to-postgres.mjs +7 -9
  421. package/scripts/run-e2e-realtime.sh +17 -3
  422. package/scripts/run-smoke.sh +17 -5
  423. package/scripts/run-soak.sh +17 -5
  424. package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  425. package/src/resources/templates/workflows/collaborate.yaml +1 -1
  426. package/src/resources/templates/workflows/evaluate.yaml +2 -2
  427. package/src/resources/templates/workflows/plan.yaml +1 -1
  428. package/src/resources/templates/workflows/review.yaml +2 -2
  429. package/dist/ui/assets/AgentsModule-N2MnQBZk.js +0 -1
  430. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BG6P_GVW.js +0 -3
  431. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +0 -1
  432. package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +0 -1
  433. package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +0 -252
  434. package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
  435. package/dist/ui/assets/PlansPage-Cq0zXj3I.js +0 -1
  436. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  437. package/dist/ui/assets/TaskSettings-ln0aF7xc.js +0 -9
  438. package/dist/ui/assets/index-C2-OXZV7.css +0 -1
  439. package/dist/ui/assets/index-DcSI5F86.js +0 -6
  440. 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',
@@ -888,6 +910,48 @@ describe('TaskforceCore Integration', () => {
888
910
  });
889
911
  expect(container.querySelector('[aria-pressed]')).not.toBeInTheDocument();
890
912
  });
913
+ it('renders the cloud login route even while protected startup config is pending', async () => {
914
+ const hangingConfig = new Promise(() => { });
915
+ const fetchMock = vi.fn((url) => {
916
+ if (url.includes('/api/taskforce/auth/runtime-config')) {
917
+ return Promise.resolve({
918
+ ok: true,
919
+ json: () => Promise.resolve({
920
+ config: {
921
+ runtimeMode: 'cloud',
922
+ workspaceMode: 'multi-cloud',
923
+ authSource: 'cloud',
924
+ workspaceSwitchingEnabled: true
925
+ }
926
+ })
927
+ });
928
+ }
929
+ if (url.includes('/api/taskforce/auth/session')) {
930
+ return Promise.resolve({
931
+ ok: true,
932
+ json: () => Promise.resolve({
933
+ authenticated: false,
934
+ runtimeMode: 'cloud'
935
+ })
936
+ });
937
+ }
938
+ if (url.includes('/api/taskforce/config')) {
939
+ return hangingConfig;
940
+ }
941
+ if (url.includes('/api/taskforce/auth/providers')) {
942
+ return Promise.resolve({ ok: true, json: () => Promise.resolve({ providers: [] }) });
943
+ }
944
+ return Promise.resolve({ ok: true, json: () => Promise.resolve({}) });
945
+ });
946
+ vi.stubGlobal('fetch', fetchMock);
947
+ render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=login&next=%2Ftaskforce%2F'], children: _jsx(TaskforceCore, {}) }));
948
+ await waitFor(() => {
949
+ expect(screen.getByPlaceholderText('Email')).toBeInTheDocument();
950
+ expect(screen.getByPlaceholderText('Password')).toBeInTheDocument();
951
+ expect(screen.getByRole('button', { name: 'Sign In' })).toBeInTheDocument();
952
+ });
953
+ expect(screen.queryByText('Loading Taskforce')).not.toBeInTheDocument();
954
+ });
891
955
  it('allows public access to /pricing in cloud mode without login redirect', async () => {
892
956
  const fetchMock = vi.fn((url) => {
893
957
  if (url.includes('/api/taskforce/auth/session')) {
@@ -1085,7 +1149,6 @@ describe('TaskforceCore Integration', () => {
1085
1149
  render(_jsx(MemoryRouter, { initialEntries: ['/'], children: _jsx(TaskforceCore, {}) }));
1086
1150
  await waitFor(() => {
1087
1151
  expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
1088
- expect(screen.getByText('Choose a plan to continue.')).toBeInTheDocument();
1089
1152
  });
1090
1153
  });
1091
1154
  it('redirects authenticated users with a blocking account access gate to the plans page', async () => {
@@ -1789,11 +1852,12 @@ describe('TaskforceCore Integration', () => {
1789
1852
  if (url.includes('/api/taskforce/workspace/assignee-options')) {
1790
1853
  return jsonResponse({ assignees: [] });
1791
1854
  }
1792
- if (url.includes('/api/taskforce/initiatives')) {
1793
- return jsonResponse([]);
1794
- }
1795
- if (url.includes('/api/taskforce/workstreams')) {
1796
- return jsonResponse([]);
1855
+ if (url.includes('/api/taskforce/planning/bootstrap')) {
1856
+ return jsonResponse({
1857
+ initiatives: [],
1858
+ workstreams: [],
1859
+ workstreamTaskSummaries: [],
1860
+ });
1797
1861
  }
1798
1862
  return jsonResponse({});
1799
1863
  });
@@ -1820,7 +1884,8 @@ describe('TaskforceCore Integration', () => {
1820
1884
  await user.click(screen.getByRole('button', { name: 'Register' }));
1821
1885
  await user.type(await screen.findByPlaceholderText('Email'), accounts.newcomer.email);
1822
1886
  await user.type(screen.getByPlaceholderText('Display name'), accounts.newcomer.displayName);
1823
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
1887
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
1888
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
1824
1889
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
1825
1890
  await waitFor(() => {
1826
1891
  expect(screen.getByTitle(/Account/i)).toBeInTheDocument();
@@ -1902,6 +1967,7 @@ describe('TaskforceCore Integration', () => {
1902
1967
  expect(screen.getByText('Create your Taskforce account.')).toBeInTheDocument();
1903
1968
  expect(screen.getByLabelText('Email')).toBeInTheDocument();
1904
1969
  expect(screen.getByLabelText('Display name')).toBeInTheDocument();
1970
+ expect(screen.getByLabelText('Confirm password')).toBeInTheDocument();
1905
1971
  expect(screen.getByPlaceholderText('Email')).toBeInTheDocument();
1906
1972
  expect(screen.getByPlaceholderText('Display name')).toBeInTheDocument();
1907
1973
  expect(screen.getByRole('button', { name: 'Create Account' })).toBeInTheDocument();
@@ -1913,6 +1979,56 @@ describe('TaskforceCore Integration', () => {
1913
1979
  });
1914
1980
  expect(container.querySelector('[aria-pressed]')).not.toBeInTheDocument();
1915
1981
  });
1982
+ it('blocks registration when the confirmation does not match', async () => {
1983
+ const user = userEvent.setup();
1984
+ const fetchMock = vi.fn((url) => {
1985
+ if (url.includes('/api/taskforce/auth/session')) {
1986
+ return Promise.resolve({
1987
+ ok: true,
1988
+ json: () => Promise.resolve({
1989
+ authenticated: false,
1990
+ runtimeMode: 'cloud',
1991
+ authRequiredForApi: false
1992
+ })
1993
+ });
1994
+ }
1995
+ if (url.includes('/api/taskforce/config')) {
1996
+ return Promise.resolve({
1997
+ ok: true,
1998
+ json: () => Promise.resolve({
1999
+ ...mockConfig,
2000
+ runtimeMode: 'cloud',
2001
+ authRequiredForApi: false,
2002
+ setupState: {
2003
+ globalSetupState: 'present',
2004
+ workspaceSetupState: 'present',
2005
+ runtimeMode: 'cloud',
2006
+ workspaceId: 'workspace-local-active',
2007
+ mode: 'core',
2008
+ forceSetup: false,
2009
+ forceGlobalSetup: false,
2010
+ forceWorkspaceSetup: false
2011
+ }
2012
+ })
2013
+ });
2014
+ }
2015
+ if (url.includes('/api/taskforce/workflow-templates')) {
2016
+ return Promise.resolve({ ok: true, json: () => Promise.resolve({ templates: [] }) });
2017
+ }
2018
+ return Promise.resolve({ ok: true, json: () => Promise.resolve({}) });
2019
+ });
2020
+ vi.stubGlobal('fetch', fetchMock);
2021
+ render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
2022
+ await user.type(await screen.findByPlaceholderText('Email'), 'mismatch@example.com');
2023
+ await user.type(screen.getByPlaceholderText('Display name'), 'Mismatch User');
2024
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
2025
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'DifferentPassphrase123!');
2026
+ await user.click(screen.getByRole('button', { name: 'Create Account' }));
2027
+ await waitFor(() => {
2028
+ expect(screen.getByText('Passwords do not match.')).toBeInTheDocument();
2029
+ });
2030
+ expect(fetchMock.mock.calls.some((call) => String(call[0]).includes('/api/taskforce/auth/register'))).toBe(false);
2031
+ });
1916
2032
  it('keeps the verification screen focused on verification and lets the user request a fresh email token', async () => {
1917
2033
  const fetchMock = vi.fn((url) => {
1918
2034
  if (url.includes('/api/taskforce/auth/session')) {
@@ -2204,7 +2320,8 @@ describe('TaskforceCore Integration', () => {
2204
2320
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2Fplans'], children: _jsx(TaskforceCore, {}) }));
2205
2321
  await user.type(await screen.findByPlaceholderText('Email'), 'local@example.com');
2206
2322
  await user.type(screen.getByPlaceholderText('Display name'), 'Local User');
2207
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
2323
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
2324
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2208
2325
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2209
2326
  await waitFor(() => {
2210
2327
  expect(screen.getByText('Configure your new workspace.')).toBeInTheDocument();
@@ -2362,11 +2479,11 @@ describe('TaskforceCore Integration', () => {
2362
2479
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
2363
2480
  await user.type(await screen.findByPlaceholderText('Email'), 'pending@example.com');
2364
2481
  await user.type(screen.getByPlaceholderText('Display name'), 'Pending User');
2365
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
2482
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
2483
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2366
2484
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2367
2485
  await waitFor(() => {
2368
2486
  expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
2369
- expect(screen.getByText('Choose a plan to continue.')).toBeInTheDocument();
2370
2487
  });
2371
2488
  expect(fetchMock.mock.calls.some(([url]) => String(url).includes('/api/taskforce/billing/checkout-session'))).toBe(false);
2372
2489
  });
@@ -2478,11 +2595,148 @@ describe('TaskforceCore Integration', () => {
2478
2595
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
2479
2596
  await user.type(await screen.findByPlaceholderText('Email'), 'brandnew@example.com');
2480
2597
  await user.type(screen.getByPlaceholderText('Display name'), 'Brand New');
2481
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
2598
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
2599
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2482
2600
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2483
2601
  await waitFor(() => {
2484
2602
  expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
2485
- expect(screen.getByText('Choose a plan to continue.')).toBeInTheDocument();
2603
+ });
2604
+ expect(screen.queryByText('Configure your new workspace.')).not.toBeInTheDocument();
2605
+ });
2606
+ it('keeps registration on plan selection while the post-auth cloud session is still resolving for a commercially gated user with no workspace', async () => {
2607
+ const user = userEvent.setup();
2608
+ let authenticated = false;
2609
+ let authSessionCallCount = 0;
2610
+ let releaseInitialSession = null;
2611
+ const initialSessionGate = new Promise((resolve) => {
2612
+ releaseInitialSession = resolve;
2613
+ });
2614
+ const fetchMock = vi.fn((input) => {
2615
+ const url = String(input);
2616
+ if (url.includes('/api/taskforce/auth/runtime-config')) {
2617
+ return jsonResponse({
2618
+ config: {
2619
+ baseUrl: 'https://app.taskforce.example',
2620
+ apiBaseUrl: 'https://app.taskforce.example/api',
2621
+ cloudAuthBaseUrl: 'https://app.taskforce.example',
2622
+ runtimeMode: 'cloud',
2623
+ authSource: 'cloud',
2624
+ workspaceMode: 'multi-cloud',
2625
+ workspaceSwitchingEnabled: true,
2626
+ authRequiredForApi: true
2627
+ }
2628
+ });
2629
+ }
2630
+ if (url.includes('/api/taskforce/auth/session')) {
2631
+ authSessionCallCount += 1;
2632
+ const payload = () => ({
2633
+ authenticated,
2634
+ runtimeMode: 'cloud',
2635
+ authRequiredForApi: true,
2636
+ userId: authenticated ? 'user-gated-pending' : null,
2637
+ workspaceId: null,
2638
+ betaAccess: true
2639
+ });
2640
+ if (authSessionCallCount === 1) {
2641
+ return initialSessionGate.then(() => jsonResponse(payload()));
2642
+ }
2643
+ return jsonResponse(payload());
2644
+ }
2645
+ if (url.includes('/api/taskforce/config')) {
2646
+ return jsonResponse({
2647
+ ...mockConfig,
2648
+ runtimeMode: 'cloud',
2649
+ authRequiredForApi: true,
2650
+ workspaceSwitchingEnabled: true,
2651
+ setupState: {
2652
+ globalSetupState: 'present',
2653
+ workspaceSetupState: 'missing',
2654
+ runtimeMode: 'cloud',
2655
+ workspaceId: 'default',
2656
+ mode: 'core',
2657
+ forceSetup: true,
2658
+ forceGlobalSetup: false,
2659
+ forceWorkspaceSetup: true
2660
+ }
2661
+ });
2662
+ }
2663
+ if (url.includes('/api/taskforce/workspaces')) {
2664
+ return jsonResponse({
2665
+ success: true,
2666
+ workspaces: [],
2667
+ currentWorkspaceId: 'default'
2668
+ });
2669
+ }
2670
+ if (url.includes('/api/taskforce/account/access-status')) {
2671
+ return jsonResponse({
2672
+ gate: 'plan_selection_required',
2673
+ hasEntitlement: true,
2674
+ canAccessApp: false,
2675
+ canAccessPlans: true,
2676
+ message: 'Choose a plan to continue.'
2677
+ });
2678
+ }
2679
+ if (url.includes('/api/taskforce/account/profile-summary')) {
2680
+ return jsonResponse({
2681
+ entitlementState: 'pending_plan_selection',
2682
+ gate: 'plan_selection_required',
2683
+ message: 'Choose a plan to continue.',
2684
+ workspaceId: null
2685
+ });
2686
+ }
2687
+ if (url.includes('/api/taskforce/public/pricing')) {
2688
+ return jsonResponse({ plans: [] });
2689
+ }
2690
+ if (url.includes('/api/taskforce/auth/register')) {
2691
+ authenticated = true;
2692
+ return jsonResponse({
2693
+ success: true,
2694
+ workspaceSetupRequired: true,
2695
+ verificationRequired: false,
2696
+ planSelectionRequired: true,
2697
+ commercialState: 'pending_plan_selection'
2698
+ });
2699
+ }
2700
+ if (url.includes('/api/taskforce/categories'))
2701
+ return jsonResponse(mockCategories);
2702
+ if (url.includes('/api/taskforce/tasks'))
2703
+ return jsonResponse({ tasks: mockTasks });
2704
+ if (url.includes('/api/taskforce/archive'))
2705
+ return jsonResponse({ archived: [] });
2706
+ if (url.includes('/api/taskforce/data-version'))
2707
+ return jsonResponse({ dataVersion: 1 });
2708
+ if (url.includes('/api/taskforce/types'))
2709
+ return jsonResponse({ types: [] });
2710
+ if (url.includes('/api/taskforce/priorities'))
2711
+ return jsonResponse({ priorities: [] });
2712
+ if (url.includes('/api/taskforce/approaches'))
2713
+ return jsonResponse({ approaches: [] });
2714
+ if (url.includes('/api/taskforce/specialists'))
2715
+ return jsonResponse({ specialists: [] });
2716
+ if (url.includes('/api/taskforce/workflow-templates'))
2717
+ return jsonResponse({ templates: [] });
2718
+ return jsonResponse({});
2719
+ });
2720
+ vi.stubGlobal('fetch', fetchMock);
2721
+ render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
2722
+ await user.type(await screen.findByPlaceholderText('Email'), 'gated-pending@example.com');
2723
+ await user.type(screen.getByPlaceholderText('Display name'), 'Gated Pending');
2724
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
2725
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2726
+ await user.click(screen.getByRole('button', { name: 'Create Account' }));
2727
+ await act(async () => {
2728
+ await Promise.resolve();
2729
+ });
2730
+ await waitFor(() => {
2731
+ expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
2732
+ });
2733
+ expect(screen.queryByText('Configure your new workspace.')).not.toBeInTheDocument();
2734
+ await act(async () => {
2735
+ releaseInitialSession?.();
2736
+ await Promise.resolve();
2737
+ });
2738
+ await waitFor(() => {
2739
+ expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
2486
2740
  });
2487
2741
  expect(screen.queryByText('Configure your new workspace.')).not.toBeInTheDocument();
2488
2742
  });
@@ -2653,7 +2907,8 @@ describe('TaskforceCore Integration', () => {
2653
2907
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&planId=basic&planVersionId=basic-v1&interval=year&next=%2F'], children: _jsx(TaskforceCore, {}) }));
2654
2908
  await user.type(await screen.findByPlaceholderText('Email'), 'checkout@example.com');
2655
2909
  await user.type(screen.getByPlaceholderText('Display name'), 'Checkout User');
2656
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
2910
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
2911
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2657
2912
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2658
2913
  await waitFor(() => {
2659
2914
  expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
@@ -2786,7 +3041,8 @@ describe('TaskforceCore Integration', () => {
2786
3041
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
2787
3042
  await user.type(await screen.findByPlaceholderText('Email'), 'free-user@example.com');
2788
3043
  await user.type(screen.getByPlaceholderText('Display name'), 'Free User');
2789
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
3044
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
3045
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2790
3046
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2791
3047
  await act(async () => {
2792
3048
  await Promise.resolve();
@@ -2912,7 +3168,8 @@ describe('TaskforceCore Integration', () => {
2912
3168
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
2913
3169
  await user.type(await screen.findByPlaceholderText('Email'), 'existing-workspace@example.com');
2914
3170
  await user.type(screen.getByPlaceholderText('Display name'), 'Existing Workspace User');
2915
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
3171
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
3172
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2916
3173
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2917
3174
  await act(async () => {
2918
3175
  await Promise.resolve();
@@ -2982,7 +3239,8 @@ describe('TaskforceCore Integration', () => {
2982
3239
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2Fplans'], children: _jsx(TaskforceCore, {}) }));
2983
3240
  await user.type(await screen.findByPlaceholderText('Email'), 'local@example.com');
2984
3241
  await user.type(screen.getByPlaceholderText('Display name'), 'Local User');
2985
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
3242
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
3243
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
2986
3244
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
2987
3245
  await waitFor(() => {
2988
3246
  expect(screen.getByPlaceholderText('Verification token')).toBeInTheDocument();
@@ -2990,6 +3248,156 @@ describe('TaskforceCore Integration', () => {
2990
3248
  expect(screen.queryByRole('button', { name: 'Create Account' })).not.toBeInTheDocument();
2991
3249
  expect(screen.queryByText('Subscription Plans')).not.toBeInTheDocument();
2992
3250
  });
3251
+ it('keeps verified registration on plan selection when commercial onboarding is still blocked', async () => {
3252
+ const user = userEvent.setup();
3253
+ let authenticated = false;
3254
+ const fetchMock = vi.fn((input) => {
3255
+ const url = String(input);
3256
+ if (url.includes('/api/taskforce/auth/runtime-config')) {
3257
+ return jsonResponse({
3258
+ config: {
3259
+ baseUrl: 'https://app.taskforce.example',
3260
+ apiBaseUrl: 'https://app.taskforce.example/api',
3261
+ cloudAuthBaseUrl: 'https://app.taskforce.example',
3262
+ runtimeMode: 'cloud',
3263
+ authSource: 'cloud',
3264
+ workspaceMode: 'multi-cloud',
3265
+ workspaceSwitchingEnabled: true,
3266
+ authRequiredForApi: true
3267
+ }
3268
+ });
3269
+ }
3270
+ if (url.includes('/api/taskforce/auth/session')) {
3271
+ return jsonResponse({
3272
+ authenticated,
3273
+ runtimeMode: 'cloud',
3274
+ authRequiredForApi: true,
3275
+ userId: authenticated ? 'user-verify' : null,
3276
+ workspaceId: authenticated ? 'default' : null,
3277
+ betaAccess: true
3278
+ });
3279
+ }
3280
+ if (url.includes('/api/taskforce/config')) {
3281
+ return jsonResponse({
3282
+ ...mockConfig,
3283
+ runtimeMode: 'cloud',
3284
+ authRequiredForApi: true,
3285
+ workspaceSwitchingEnabled: true,
3286
+ setupState: {
3287
+ globalSetupState: 'present',
3288
+ workspaceSetupState: authenticated ? 'missing' : 'present',
3289
+ runtimeMode: 'cloud',
3290
+ workspaceId: 'default',
3291
+ mode: 'core',
3292
+ forceSetup: false,
3293
+ forceGlobalSetup: false,
3294
+ forceWorkspaceSetup: false
3295
+ }
3296
+ });
3297
+ }
3298
+ if (url.includes('/api/taskforce/workspaces')) {
3299
+ return jsonResponse({
3300
+ success: true,
3301
+ workspaces: [],
3302
+ currentWorkspaceId: null
3303
+ });
3304
+ }
3305
+ if (url.includes('/api/taskforce/account/access-status')) {
3306
+ return jsonResponse({
3307
+ gate: 'plan_selection_required',
3308
+ hasEntitlement: true,
3309
+ canAccessApp: false,
3310
+ canAccessPlans: true,
3311
+ message: 'Choose a plan to continue.'
3312
+ });
3313
+ }
3314
+ if (url.includes('/api/taskforce/account/profile-summary')) {
3315
+ return jsonResponse({
3316
+ entitlementState: 'pending_plan_selection',
3317
+ gate: 'plan_selection_required',
3318
+ message: 'Choose a plan to continue.',
3319
+ workspaceId: null
3320
+ });
3321
+ }
3322
+ if (url.includes('/api/taskforce/public/pricing')) {
3323
+ return jsonResponse({
3324
+ plans: [{
3325
+ planId: 'free',
3326
+ displayName: 'Free',
3327
+ status: 'active',
3328
+ defaultVersion: {
3329
+ planVersionId: 'free-v1',
3330
+ versionNumber: 1,
3331
+ seatLimit: null,
3332
+ metadata: {}
3333
+ },
3334
+ features: [],
3335
+ pricing: {
3336
+ month: {
3337
+ planVersionId: 'free-v1',
3338
+ interval: 'month',
3339
+ pricingType: 'free',
3340
+ stripePriceId: 'free:free-v1:month',
3341
+ displayAmount: '$0.00',
3342
+ unitAmount: 0,
3343
+ currency: 'usd'
3344
+ },
3345
+ year: null
3346
+ }
3347
+ }]
3348
+ });
3349
+ }
3350
+ if (url.includes('/api/taskforce/auth/register')) {
3351
+ return jsonResponse({
3352
+ success: true,
3353
+ workspaceSetupRequired: true,
3354
+ verificationRequired: true,
3355
+ verificationToken: 'verify-token',
3356
+ emailSent: true,
3357
+ planSelectionRequired: true,
3358
+ commercialState: 'pending_plan_selection'
3359
+ });
3360
+ }
3361
+ if (url.includes('/api/taskforce/auth/verify-email/confirm')) {
3362
+ authenticated = true;
3363
+ return jsonResponse({ success: true });
3364
+ }
3365
+ if (url.includes('/api/taskforce/categories'))
3366
+ return jsonResponse(mockCategories);
3367
+ if (url.includes('/api/taskforce/tasks'))
3368
+ return jsonResponse({ tasks: mockTasks });
3369
+ if (url.includes('/api/taskforce/archive'))
3370
+ return jsonResponse({ archived: [] });
3371
+ if (url.includes('/api/taskforce/data-version'))
3372
+ return jsonResponse({ dataVersion: 1 });
3373
+ if (url.includes('/api/taskforce/types'))
3374
+ return jsonResponse({ types: [] });
3375
+ if (url.includes('/api/taskforce/priorities'))
3376
+ return jsonResponse({ priorities: [] });
3377
+ if (url.includes('/api/taskforce/approaches'))
3378
+ return jsonResponse({ approaches: [] });
3379
+ if (url.includes('/api/taskforce/specialists'))
3380
+ return jsonResponse({ specialists: [] });
3381
+ if (url.includes('/api/taskforce/workflow-templates'))
3382
+ return jsonResponse({ templates: [] });
3383
+ return jsonResponse({});
3384
+ });
3385
+ vi.stubGlobal('fetch', fetchMock);
3386
+ render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
3387
+ await user.type(await screen.findByPlaceholderText('Email'), 'verify-gated@example.com');
3388
+ await user.type(screen.getByPlaceholderText('Display name'), 'Verify Gated User');
3389
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
3390
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
3391
+ await user.click(screen.getByRole('button', { name: 'Create Account' }));
3392
+ await waitFor(() => {
3393
+ expect(screen.getByPlaceholderText('Verification token')).toHaveValue('verify-token');
3394
+ });
3395
+ await user.click(screen.getByRole('button', { name: 'Verify Email' }));
3396
+ await waitFor(() => {
3397
+ expect(screen.getByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
3398
+ });
3399
+ expect(screen.queryByText('Configure your new workspace.')).not.toBeInTheDocument();
3400
+ });
2993
3401
  it('allows returning to sign-in/register from forgot mode in local runtime', async () => {
2994
3402
  const user = userEvent.setup();
2995
3403
  const fetchMock = vi.fn((url) => {
@@ -3291,7 +3699,8 @@ describe('TaskforceCore Integration', () => {
3291
3699
  render(_jsx(MemoryRouter, { initialEntries: ['/login?mode=register&next=%2F'], children: _jsx(TaskforceCore, {}) }));
3292
3700
  await user.type(await screen.findByPlaceholderText('Email'), 'local@example.com');
3293
3701
  await user.type(screen.getByPlaceholderText('Display name'), 'Local User');
3294
- await user.type(screen.getByPlaceholderText('Password'), 'Password123!');
3702
+ await user.type(screen.getByPlaceholderText('Password'), 'GoodPassphrase123!');
3703
+ await user.type(screen.getByPlaceholderText('Confirm password'), 'GoodPassphrase123!');
3295
3704
  await user.click(screen.getByRole('button', { name: 'Create Account' }));
3296
3705
  await waitFor(() => {
3297
3706
  expect(screen.getByText('Choose a plan to continue.')).toBeInTheDocument();
@@ -3677,6 +4086,7 @@ describe('TaskforceCore Integration', () => {
3677
4086
  await waitFor(() => {
3678
4087
  expect(screen.getByText('Create your account to join this workspace.')).toBeInTheDocument();
3679
4088
  expect(screen.getByPlaceholderText('Create password')).toBeInTheDocument();
4089
+ expect(screen.getByPlaceholderText('Confirm password')).toBeInTheDocument();
3680
4090
  expect(screen.getByRole('button', { name: 'Create Account and Join' })).toBeInTheDocument();
3681
4091
  });
3682
4092
  });
@@ -5417,6 +5827,15 @@ describe('TaskforceCore Integration', () => {
5417
5827
  expect(useTaskforceSource).toContain("|| path.startsWith('/api/taskforce/account/')");
5418
5828
  expect(useTaskforceSource).toContain("|| path.startsWith('/api/taskforce/billing/')");
5419
5829
  });
5830
+ it('keeps plans on a dedicated surface while preserving the faster exit flow', () => {
5831
+ const standaloneLayoutPath = path.join(process.cwd(), 'src/components/views/StandaloneLayout.tsx');
5832
+ const standaloneLayoutSource = fs.readFileSync(standaloneLayoutPath, 'utf8');
5833
+ expect(standaloneLayoutSource).toContain('{!isPlansScreen && <WorkspaceHeaderActions actions={universalFilterActions} />}');
5834
+ expect(standaloneLayoutSource).toContain('{!isPlansScreen && showFilters && workspaceFilterBar}');
5835
+ expect(standaloneLayoutSource).not.toMatch(/\{isPlansScreen \? \([\s\S]*\{workspaceToolRail\}[\s\S]*<PlansPage/);
5836
+ expect(standaloneLayoutSource).toContain('closePlansScreen();');
5837
+ expect(standaloneLayoutSource).toContain('void (async () => {');
5838
+ });
5420
5839
  it('uses shared /login flow for local sign in instead of a duplicate modal', async () => {
5421
5840
  const standaloneLayoutPath = path.join(process.cwd(), 'src/components/views/StandaloneLayout.tsx');
5422
5841
  const standaloneLayoutSource = fs.readFileSync(standaloneLayoutPath, 'utf8');
@@ -5601,11 +6020,12 @@ describe('TaskforceCore Integration', () => {
5601
6020
  if (url.includes('/api/taskforce/workflow-templates')) {
5602
6021
  return jsonResponse({ templates: [] });
5603
6022
  }
5604
- if (url.includes('/api/taskforce/initiatives')) {
5605
- return jsonResponse([]);
5606
- }
5607
- if (url.includes('/api/taskforce/workstreams')) {
5608
- return jsonResponse([]);
6023
+ if (url.includes('/api/taskforce/planning/bootstrap')) {
6024
+ return jsonResponse({
6025
+ initiatives: [],
6026
+ workstreams: [],
6027
+ workstreamTaskSummaries: [],
6028
+ });
5609
6029
  }
5610
6030
  return jsonResponse({});
5611
6031
  });
@@ -5624,6 +6044,142 @@ describe('TaskforceCore Integration', () => {
5624
6044
  await user.click(screen.getByTitle(/Account/i));
5625
6045
  expect(await screen.findByText('Sign In / Register')).toBeInTheDocument();
5626
6046
  });
6047
+ it('returns to the login screen after signing out from the cloud plans screen', async () => {
6048
+ const user = userEvent.setup();
6049
+ let signedIn = true;
6050
+ const fetchMock = vi.fn((url) => {
6051
+ if (url.includes('/api/taskforce/auth/runtime-config')) {
6052
+ return jsonResponse({ config: { runtimeMode: 'cloud', workspaceMode: 'team', authSource: 'cloud' } });
6053
+ }
6054
+ if (url.includes('/api/taskforce/auth/session')) {
6055
+ return jsonResponse({
6056
+ authenticated: signedIn,
6057
+ runtimeMode: 'cloud',
6058
+ authRequiredForApi: true,
6059
+ email: signedIn ? 'member@example.com' : '',
6060
+ userId: signedIn ? 'user-member' : 'anonymous',
6061
+ workspaceId: signedIn ? 'workspace-cloud-active' : '',
6062
+ betaAccess: true
6063
+ });
6064
+ }
6065
+ if (url.includes('/api/taskforce/auth/logout')) {
6066
+ signedIn = false;
6067
+ return jsonResponse({ success: true });
6068
+ }
6069
+ if (url.includes('/api/taskforce/config')) {
6070
+ return jsonResponse({
6071
+ ...mockConfig,
6072
+ runtimeMode: 'cloud',
6073
+ authRequiredForApi: true,
6074
+ workspaceId: 'workspace-cloud-active',
6075
+ workspaceSwitchingEnabled: true,
6076
+ setupState: {
6077
+ globalSetupState: 'present',
6078
+ workspaceSetupState: 'present',
6079
+ runtimeMode: 'cloud',
6080
+ workspaceId: 'workspace-cloud-active',
6081
+ mode: 'core',
6082
+ forceSetup: false,
6083
+ forceGlobalSetup: false,
6084
+ forceWorkspaceSetup: false
6085
+ }
6086
+ });
6087
+ }
6088
+ if (url.includes('/api/taskforce/account/access-status')) {
6089
+ return jsonResponse({
6090
+ gate: 'plan_selection_required',
6091
+ hasEntitlement: false,
6092
+ canAccessApp: false,
6093
+ canAccessPlans: true
6094
+ });
6095
+ }
6096
+ if (url.includes('/api/taskforce/public/pricing')) {
6097
+ return jsonResponse({
6098
+ plans: [
6099
+ {
6100
+ planId: 'free',
6101
+ displayName: 'Free',
6102
+ status: 'active',
6103
+ features: [],
6104
+ pricing: {
6105
+ month: { pricingType: 'free', stripePriceId: null, unitAmount: 0, currency: null, interval: 'month' },
6106
+ year: null
6107
+ },
6108
+ defaultVersion: {
6109
+ planVersionId: 'free-v1',
6110
+ versionNumber: 1,
6111
+ seatLimit: null,
6112
+ metadata: {}
6113
+ }
6114
+ }
6115
+ ]
6116
+ });
6117
+ }
6118
+ if (url.includes('/api/taskforce/account/profile-summary')) {
6119
+ return jsonResponse({
6120
+ planId: 'free',
6121
+ planName: 'Free',
6122
+ planVersionId: 'free-v1',
6123
+ entitlementState: 'pending_plan_selection',
6124
+ gate: 'plan_selection_required',
6125
+ planSelectionRequired: true
6126
+ });
6127
+ }
6128
+ if (url.includes('/api/taskforce/workspaces')) {
6129
+ return jsonResponse({ workspaces: [] });
6130
+ }
6131
+ if (url.includes('/api/taskforce/ui-state?key=app')) {
6132
+ return jsonResponse({ success: true, state: {} });
6133
+ }
6134
+ if (url.includes('/api/taskforce/ui-state?key=workspace-sync')) {
6135
+ return jsonResponse({ success: true, state: {} });
6136
+ }
6137
+ if (url.includes('/api/taskforce/categories')) {
6138
+ return jsonResponse(mockCategories);
6139
+ }
6140
+ if (url.includes('/api/taskforce/tasks')) {
6141
+ return jsonResponse({ tasks: mockTasks });
6142
+ }
6143
+ if (url.includes('/api/taskforce/archive')) {
6144
+ return jsonResponse({ archived: [] });
6145
+ }
6146
+ if (url.includes('/api/taskforce/types')) {
6147
+ return jsonResponse({ types: [] });
6148
+ }
6149
+ if (url.includes('/api/taskforce/priorities')) {
6150
+ return jsonResponse({ priorities: [] });
6151
+ }
6152
+ if (url.includes('/api/taskforce/approaches')) {
6153
+ return jsonResponse({ approaches: [] });
6154
+ }
6155
+ if (url.includes('/api/taskforce/specialists')) {
6156
+ return jsonResponse({ specialists: [] });
6157
+ }
6158
+ if (url.includes('/api/taskforce/workflow-templates')) {
6159
+ return jsonResponse({ templates: [] });
6160
+ }
6161
+ if (url.includes('/api/taskforce/workspace/assignee-options')) {
6162
+ return jsonResponse({ assignees: [] });
6163
+ }
6164
+ if (url.includes('/api/taskforce/planning/bootstrap')) {
6165
+ return jsonResponse({
6166
+ initiatives: [],
6167
+ workstreams: [],
6168
+ workstreamTaskSummaries: [],
6169
+ });
6170
+ }
6171
+ return jsonResponse({});
6172
+ });
6173
+ vi.stubGlobal('fetch', fetchMock);
6174
+ render(_jsx(MemoryRouter, { initialEntries: ['/?screen=plans&gate=plan_selection_required'], children: _jsx(TaskforceCore, { config: { cloudAuthBaseUrl: 'https://cloud.example.com' } }) }));
6175
+ expect(await screen.findByRole('heading', { name: 'Subscription Plans' })).toBeInTheDocument();
6176
+ await user.click(screen.getByTitle(/Account/i));
6177
+ await user.click(await screen.findByRole('menuitem', { name: 'Sign Out' }));
6178
+ await waitFor(() => {
6179
+ expect(fetchMock.mock.calls.some((call) => String(call[0]).includes('/api/taskforce/auth/logout'))).toBe(true);
6180
+ expect(screen.getByText('Sign in with your account credentials.')).toBeInTheDocument();
6181
+ });
6182
+ });
5627
6183
  it('uses an explicit cloud sign-out redirect and broader cloud logout reset path', () => {
5628
6184
  const standaloneLayoutSource = fs.readFileSync(path.join(__dirname, 'components/views/StandaloneLayout.tsx'), 'utf8');
5629
6185
  const useTaskforceSource = fs.readFileSync(path.join(__dirname, 'hooks/useTaskforce.ts'), 'utf8');
@@ -5947,9 +6503,9 @@ describe('TaskforceCore Integration', () => {
5947
6503
  occurredAt: '2026-03-15T14:28:17.000Z',
5948
6504
  eventType: 'apply',
5949
6505
  status: 'error',
5950
- errorMessage: 'Detected asset reference number mismatch during normal sync.',
6506
+ errorMessage: 'Detected task reference number mismatch during normal sync.',
5951
6507
  details: {
5952
- path: 'workspaces/workspace-local-active/assets/images/hero.png',
6508
+ taskId: 'task-1',
5953
6509
  existingReferenceNumber: 12,
5954
6510
  incomingReferenceNumber: 18
5955
6511
  }
@@ -7183,6 +7739,7 @@ describe('TaskforceCore Integration', () => {
7183
7739
  const user = userEvent.setup();
7184
7740
  let savedDisplayName = 'Original Name';
7185
7741
  let savedAvatarUrl = '';
7742
+ const cloudAuthBaseUrl = 'https://performance-app.taskforcehq.ai';
7186
7743
  const fetchMock = vi.fn((input, init) => {
7187
7744
  const url = String(input);
7188
7745
  if (url.includes('/api/taskforce/auth/runtime-config')) {
@@ -7222,6 +7779,9 @@ describe('TaskforceCore Integration', () => {
7222
7779
  ...mockConfig,
7223
7780
  runtimeMode: 'local',
7224
7781
  authRequiredForApi: false,
7782
+ apiBaseUrl: cloudAuthBaseUrl,
7783
+ cloudAuthBaseUrl,
7784
+ cloudAuthViaLocalProxy: true,
7225
7785
  workspaceId: 'workspace-local-active',
7226
7786
  setupState: {
7227
7787
  globalSetupState: 'present',
@@ -7257,13 +7817,12 @@ describe('TaskforceCore Integration', () => {
7257
7817
  return jsonResponse({
7258
7818
  success: true,
7259
7819
  draftId: 'avatar-draft-1',
7260
- uploadUrl: 'https://upload.example.com/avatar-draft-1',
7261
7820
  relativePath: 'users/user-1/profile/avatar/avatar-draft-1-avatar.png',
7262
7821
  path: '/api/taskforce/context/users%2Fuser-1%2Fprofile%2Favatar%2Favatar-draft-1-avatar.png'
7263
7822
  });
7264
7823
  }
7265
- if (url.includes('https://upload.example.com/avatar-draft-1')) {
7266
- return Promise.resolve({ ok: true, status: 200, json: async () => ({}) });
7824
+ if (url.includes('/api/taskforce/auth/profile/avatar/upload')) {
7825
+ return jsonResponse({ success: true, draftId: 'avatar-draft-1' });
7267
7826
  }
7268
7827
  if (url.includes('/api/taskforce/auth/profile/avatar/finalize')) {
7269
7828
  return jsonResponse({
@@ -7304,8 +7863,21 @@ describe('TaskforceCore Integration', () => {
7304
7863
  const displayNameInput = within(modal).getByPlaceholderText('Display name');
7305
7864
  await user.clear(displayNameInput);
7306
7865
  await user.type(displayNameInput, 'Updated Name');
7307
- const avatarInput = within(modal).getByLabelText('Upload profile photo');
7866
+ const getContextSpy = vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue({
7867
+ drawImage: () => undefined
7868
+ });
7869
+ const toBlobSpy = vi.spyOn(HTMLCanvasElement.prototype, 'toBlob').mockImplementation(function toBlob(callback, type) {
7870
+ callback(new Blob([new Uint8Array([1, 2, 3, 4])], { type: type || 'image/webp' }));
7871
+ });
7872
+ await user.click(within(modal).getByLabelText('Edit profile photo'));
7873
+ const avatarManager = await screen.findByText('Edit Profile Photo');
7874
+ const avatarManagerModal = avatarManager.closest(`.${modalStyles.modal}`);
7875
+ const avatarInput = within(avatarManagerModal).getByLabelText('Choose profile photo');
7308
7876
  await user.upload(avatarInput, new File(['avatar'], 'avatar.png', { type: 'image/png' }));
7877
+ await user.click(within(avatarManagerModal).getByRole('button', { name: 'Apply Photo' }));
7878
+ await waitFor(() => {
7879
+ expect(screen.queryByText('Edit Profile Photo')).not.toBeInTheDocument();
7880
+ });
7309
7881
  await user.click(within(modal).getByRole('button', { name: 'Save Profile' }));
7310
7882
  await waitFor(() => {
7311
7883
  const profileCall = fetchMock.mock.calls.find((call) => {
@@ -7317,6 +7889,17 @@ describe('TaskforceCore Integration', () => {
7317
7889
  expect(payload.displayName).toBe('Updated Name');
7318
7890
  expect(payload.avatarDraftId).toBe('avatar-draft-1');
7319
7891
  });
7892
+ expect(fetchMock).toHaveBeenCalledWith('/api/taskforce/auth/profile/avatar/init', expect.objectContaining({ method: 'POST' }));
7893
+ const avatarUploadCall = fetchMock.mock.calls.find((call) => String(call[0]) === '/api/taskforce/auth/profile/avatar/upload');
7894
+ expect(avatarUploadCall).toBeTruthy();
7895
+ expect(avatarUploadCall?.[1]).toEqual(expect.objectContaining({ method: 'POST' }));
7896
+ const avatarUploadHeaders = new Headers(avatarUploadCall?.[1]?.headers);
7897
+ expect(avatarUploadHeaders.get('Content-Type')).toBe('image/webp');
7898
+ expect(avatarUploadHeaders.get('x-taskforce-avatar-draft-id')).toBe('avatar-draft-1');
7899
+ expect(fetchMock).toHaveBeenCalledWith('/api/taskforce/auth/profile/avatar/finalize', expect.objectContaining({ method: 'POST' }));
7900
+ expect(fetchMock.mock.calls.some((call) => String(call[0]).startsWith(`${cloudAuthBaseUrl}/api/taskforce/auth/profile/avatar/`))).toBe(false);
7901
+ getContextSpy.mockRestore();
7902
+ toBlobSpy.mockRestore();
7320
7903
  await waitFor(() => {
7321
7904
  expect(screen.queryByText('Edit Profile')).not.toBeInTheDocument();
7322
7905
  });