@taskforcehq/taskforce 0.3.304 → 0.3.306

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. package/README.md +140 -247
  2. package/dist/Taskforce.module.css +295 -89
  3. package/dist/TaskforceCore.js +378 -111
  4. package/dist/TaskforceCore.test.js +1221 -107
  5. package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
  6. package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  7. package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
  8. package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
  9. package/dist/assets/fonts/Noir_medium.woff2 +0 -0
  10. package/dist/assets/fonts/Noir_regular.woff2 +0 -0
  11. package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
  12. package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
  13. package/dist/compat/workspaceSyncCompat.js +52 -2
  14. package/dist/compat/workspaceSyncCompat.test.js +38 -1
  15. package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
  16. package/dist/components/context/ContextAttachmentManager.js +549 -0
  17. package/dist/components/features/AgentsModule.d.ts +10 -1
  18. package/dist/components/features/AgentsModule.js +260 -15
  19. package/dist/components/features/AgentsModule.test.js +255 -5
  20. package/dist/components/features/DocumentIndex.d.ts +1 -1
  21. package/dist/components/features/DocumentIndex.js +1 -1
  22. package/dist/components/features/DocumentViewer.d.ts +6 -2
  23. package/dist/components/features/DocumentViewer.js +90 -10
  24. package/dist/components/features/DocumentWorkspace.d.ts +5 -22
  25. package/dist/components/features/DocumentWorkspace.js +12 -155
  26. package/dist/components/features/DocumentWorkspace.test.js +226 -6
  27. package/dist/components/features/TaskSettings.js +29 -3
  28. package/dist/components/features/TaskSettings.test.js +42 -1
  29. package/dist/components/features/documentWorkspaceModel.d.ts +24 -0
  30. package/dist/components/features/documentWorkspaceModel.js +57 -0
  31. package/dist/components/features/useDocumentWorkspaceController.d.ts +34 -0
  32. package/dist/components/features/useDocumentWorkspaceController.js +113 -0
  33. package/dist/components/task/TaskCard.js +19 -28
  34. package/dist/components/task/TaskCard.test.js +38 -3
  35. package/dist/components/task/TaskContextUpload.js +4 -526
  36. package/dist/components/task/TaskForm.d.ts +8 -8
  37. package/dist/components/task/TaskForm.js +362 -230
  38. package/dist/components/task/TaskForm.test.js +558 -381
  39. package/dist/components/task/TaskKanban.d.ts +2 -1
  40. package/dist/components/task/TaskKanban.js +96 -14
  41. package/dist/components/task/TaskKanban.test.js +36 -0
  42. package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
  43. package/dist/components/ui/EditableAvatarButton.js +18 -0
  44. package/dist/components/views/PlanComparisonPage.d.ts +9 -1
  45. package/dist/components/views/PlanComparisonPage.js +70 -17
  46. package/dist/components/views/PlanComparisonPage.test.js +289 -41
  47. package/dist/components/views/PlansPage.js +239 -106
  48. package/dist/components/views/PlansPage.test.js +593 -42
  49. package/dist/components/views/StandaloneLayout.js +310 -554
  50. package/dist/components/views/panels/FilterToolbar.test.js +6 -4
  51. package/dist/components/views/panels/PlanningDrawer.d.ts +8 -1
  52. package/dist/components/views/panels/PlanningDrawer.js +18 -11
  53. package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
  54. package/dist/components/views/panels/PlanningDrawer.test.js +141 -0
  55. package/dist/components/views/planningScope.d.ts +20 -0
  56. package/dist/components/views/planningScope.js +25 -0
  57. package/dist/components/views/planningScope.test.d.ts +1 -0
  58. package/dist/components/views/planningScope.test.js +60 -0
  59. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +27 -14
  60. package/dist/components/views/standalone/modals/AccountHubModal.js +87 -15
  61. package/dist/components/views/standalone/modals/AccountHubModal.test.js +42 -2
  62. package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
  63. package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
  64. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
  65. package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
  66. package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
  67. package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
  68. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
  69. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
  70. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
  71. package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
  72. package/dist/config/envSchema.js +1 -1
  73. package/dist/core/AiProfileService.d.ts +45 -3
  74. package/dist/core/AiProfileService.js +774 -63
  75. package/dist/core/AiProfileService.test.js +22 -2
  76. package/dist/core/AiProfiles.test.js +761 -2
  77. package/dist/core/AttachmentLinkService.js +57 -15
  78. package/dist/core/AuthIdentityService.d.ts +107 -0
  79. package/dist/core/AuthIdentityService.js +284 -0
  80. package/dist/core/AuthTokenService.d.ts +4 -0
  81. package/dist/core/AuthTokenService.js +34 -6
  82. package/dist/core/AuthTokenService.test.d.ts +1 -0
  83. package/dist/core/AuthTokenService.test.js +78 -0
  84. package/dist/core/EntitlementsPolicy.test.js +77 -15
  85. package/dist/core/GlobalSettingsService.js +115 -6
  86. package/dist/core/PlanEntitlementService.d.ts +30 -3
  87. package/dist/core/PlanEntitlementService.js +454 -93
  88. package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
  89. package/dist/core/PlanFeatureCatalog.test.js +84 -0
  90. package/dist/core/PlanVersionPolicy.test.js +54 -28
  91. package/dist/core/PlanningEntities.test.js +52 -0
  92. package/dist/core/SignupMonetizationSettings.test.js +241 -44
  93. package/dist/core/SyncReconciliationService.js +2 -65
  94. package/dist/core/SystemAdmin.test.js +244 -88
  95. package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
  96. package/dist/core/TaskTaxonomyValidation.test.js +53 -0
  97. package/dist/core/Taskforce.d.ts +228 -14
  98. package/dist/core/Taskforce.js +814 -201
  99. package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
  100. package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
  101. package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
  102. package/dist/core/WorkspaceLifecycleService.js +0 -5
  103. package/dist/core/WorkspacePermissions.test.js +25 -1
  104. package/dist/core/WorkspacePlanMode.test.js +128 -27
  105. package/dist/core/shared.d.ts +3 -1
  106. package/dist/core/shared.js +16 -1
  107. package/dist/core/types.d.ts +68 -2
  108. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -1
  109. package/dist/hooks/sync/auth.js +3 -1
  110. package/dist/hooks/sync/bootstrap.js +2 -0
  111. package/dist/hooks/sync/controlPlane.d.ts +37 -0
  112. package/dist/hooks/sync/controlPlane.js +78 -0
  113. package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
  114. package/dist/hooks/sync/controlPlane.test.js +121 -0
  115. package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
  116. package/dist/hooks/sync/lifecyclePolicy.js +93 -0
  117. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
  118. package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
  119. package/dist/hooks/sync/orchestratorShared.d.ts +10 -1
  120. package/dist/hooks/sync/orchestratorShared.js +68 -47
  121. package/dist/hooks/sync/orchestratorShared.test.js +50 -1
  122. package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
  123. package/dist/hooks/sync/pullLifecycle.js +24 -0
  124. package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
  125. package/dist/hooks/sync/pullLifecycle.test.js +80 -0
  126. package/dist/hooks/sync/recovery.d.ts +16 -2
  127. package/dist/hooks/sync/recovery.js +171 -53
  128. package/dist/hooks/sync/recovery.test.d.ts +1 -0
  129. package/dist/hooks/sync/recovery.test.js +292 -0
  130. package/dist/hooks/sync/transfers.d.ts +16 -3
  131. package/dist/hooks/sync/transfers.js +186 -65
  132. package/dist/hooks/sync/transfers.test.d.ts +1 -0
  133. package/dist/hooks/sync/transfers.test.js +396 -0
  134. package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
  135. package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
  136. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
  137. package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
  138. package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
  139. package/dist/hooks/sync/useSyncStatusActions.js +88 -0
  140. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
  141. package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
  142. package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
  143. package/dist/hooks/sync/useSyncStatusControls.js +60 -0
  144. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
  145. package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
  146. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
  147. package/dist/hooks/useSyncOrchestrator.d.ts +16 -6
  148. package/dist/hooks/useSyncOrchestrator.js +424 -175
  149. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +487 -10
  150. package/dist/hooks/useTaskData.js +8 -3
  151. package/dist/hooks/useTaskData.test.js +45 -0
  152. package/dist/hooks/useTaskMutations.d.ts +2 -1
  153. package/dist/hooks/useTaskMutations.js +12 -1
  154. package/dist/hooks/useTaskMutations.test.js +30 -1
  155. package/dist/hooks/useTaskforce.d.ts +65 -4
  156. package/dist/hooks/useTaskforce.js +564 -243
  157. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
  158. package/dist/hooks/useTaskforce.runtime-routing.test.js +152 -0
  159. package/dist/hooks/useTaskforce.sync-behavior.test.js +2454 -208
  160. package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
  161. package/dist/hooks/useWorkspaceSyncController.js +17 -2
  162. package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
  163. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
  164. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +316 -35
  165. package/dist/localization/locales/en-US.d.ts +0 -1
  166. package/dist/localization/locales/en-US.js +0 -1
  167. package/dist/localization/locales/es-419.js +0 -1
  168. package/dist/localization/locales/pt-BR.js +0 -1
  169. package/dist/mcp/adminWorkspaceRegistrar.d.ts +2 -0
  170. package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
  171. package/dist/mcp/collaborationRegistrar.d.ts +2 -0
  172. package/dist/mcp/collaborationRegistrar.js +71 -0
  173. package/dist/mcp/documentAssetRegistrar.d.ts +2 -0
  174. package/dist/mcp/documentAssetRegistrar.js +346 -0
  175. package/dist/mcp/runtime.d.ts +2 -0
  176. package/dist/mcp/runtime.js +2433 -3534
  177. package/dist/mcp/runtime.test.js +440 -2
  178. package/dist/mcp/taskPlanningRegistrar.d.ts +2 -0
  179. package/dist/mcp/taskPlanningRegistrar.js +418 -0
  180. package/dist/mcp/toolCatalog.d.ts +35 -0
  181. package/dist/mcp/toolCatalog.js +3 -0
  182. package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
  183. package/dist/migrations/taskSchemaMigrations.js +171 -61
  184. package/dist/migrations/taskSchemaMigrations.test.js +15 -0
  185. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  186. package/dist/resources/templates/workflows/collaborate.yaml +1 -1
  187. package/dist/resources/templates/workflows/evaluate.yaml +2 -2
  188. package/dist/resources/templates/workflows/plan.yaml +1 -1
  189. package/dist/resources/templates/workflows/review.yaml +2 -2
  190. package/dist/server/annotatedAttachmentsRoutes.test.js +3 -3
  191. package/dist/server/auth/providers/apple.d.ts +33 -0
  192. package/dist/server/auth/providers/apple.js +82 -0
  193. package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
  194. package/dist/server/auth/providers/appleClientSecret.js +65 -0
  195. package/dist/server/auth/providers/github.d.ts +26 -0
  196. package/dist/server/auth/providers/github.js +86 -0
  197. package/dist/server/auth/providers/google.d.ts +21 -0
  198. package/dist/server/auth/providers/google.js +56 -0
  199. package/dist/server/auth/providers/types.d.ts +21 -0
  200. package/dist/server/auth/providers/types.js +1 -0
  201. package/dist/server/auth.d.ts +2 -0
  202. package/dist/server/auth.js +6 -3
  203. package/dist/server/documentReviewRoutes.test.js +36 -3
  204. package/dist/server/index.cookieProxy.test.js +1 -0
  205. package/dist/server/index.d.ts +12 -0
  206. package/dist/server/index.js +120 -9
  207. package/dist/server/index.rateLimit.test.js +3 -0
  208. package/dist/server/index.test.js +106 -3
  209. package/dist/server/routes/admin.d.ts +4 -5
  210. package/dist/server/routes/admin.js +410 -80
  211. package/dist/server/routes/annotatedAttachments.d.ts +1 -1
  212. package/dist/server/routes/annotatedAttachments.js +1 -1
  213. package/dist/server/routes/auth.d.ts +16 -6
  214. package/dist/server/routes/auth.js +1015 -118
  215. package/dist/server/routes/authSupport.d.ts +30 -0
  216. package/dist/server/routes/authSupport.js +81 -0
  217. package/dist/server/routes/billing.d.ts +1 -1
  218. package/dist/server/routes/billing.js +276 -34
  219. package/dist/server/routes/billing.test.js +760 -52
  220. package/dist/server/routes/documentReviews.d.ts +1 -1
  221. package/dist/server/routes/documentReviews.js +1 -1
  222. package/dist/server/routes/documents.d.ts +5 -2
  223. package/dist/server/routes/documents.js +242 -74
  224. package/dist/server/routes/primitives.d.ts +11 -0
  225. package/dist/server/routes/primitives.js +73 -0
  226. package/dist/server/routes/resources.d.ts +1 -1
  227. package/dist/server/routes/resources.js +1 -1
  228. package/dist/server/routes/shared.d.ts +29 -3
  229. package/dist/server/routes/shared.js +69 -4
  230. package/dist/server/routes/sync.d.ts +1 -1
  231. package/dist/server/routes/sync.integration.test.js +437 -39
  232. package/dist/server/routes/sync.js +62 -606
  233. package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
  234. package/dist/server/routes/syncAuxRoutes.js +91 -0
  235. package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
  236. package/dist/server/routes/syncAuxRoutes.test.js +158 -0
  237. package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
  238. package/dist/server/routes/syncPullApplyRoutes.js +204 -0
  239. package/dist/server/routes/syncPushRoutes.d.ts +24 -0
  240. package/dist/server/routes/syncPushRoutes.js +212 -0
  241. package/dist/server/routes/syncRouteGuards.d.ts +36 -0
  242. package/dist/server/routes/syncRouteGuards.js +67 -0
  243. package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
  244. package/dist/server/routes/syncRouteGuards.test.js +94 -0
  245. package/dist/server/routes/syncRouteTypes.d.ts +13 -0
  246. package/dist/server/routes/syncRouteTypes.js +1 -0
  247. package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
  248. package/dist/server/routes/syncSnapshotRoutes.js +180 -0
  249. package/dist/server/routes/tasks.d.ts +1 -1
  250. package/dist/server/routes/tasks.js +44 -2
  251. package/dist/server/routes/workspaces.d.ts +1 -1
  252. package/dist/server/routes/workspaces.js +37 -6
  253. package/dist/server/routes.d.ts +2 -9
  254. package/dist/server/routes.js +136 -165
  255. package/dist/server/routes.test.js +2457 -162
  256. package/dist/services/workflowExportSnapshots.test.js +2 -0
  257. package/dist/shared/aiProfileSeatScope.d.ts +5 -0
  258. package/dist/shared/aiProfileSeatScope.js +21 -0
  259. package/dist/shared/passwordPolicy.d.ts +13 -0
  260. package/dist/shared/passwordPolicy.js +84 -0
  261. package/dist/shared/passwordPolicy.test.d.ts +1 -0
  262. package/dist/shared/passwordPolicy.test.js +34 -0
  263. package/dist/storage/workspaceAssetStore.d.ts +15 -3
  264. package/dist/storage/workspaceAssetStore.js +132 -44
  265. package/dist/storage/workspaceAssetStore.test.js +93 -6
  266. package/dist/styles/fonts.css +70 -0
  267. package/dist/sync/collaborationSyncPayload.d.ts +6 -1
  268. package/dist/sync/collaborationSyncPayload.js +17 -11
  269. package/dist/sync/collaborationSyncPayload.test.js +13 -1
  270. package/dist/sync/contentSyncState.d.ts +4 -8
  271. package/dist/sync/contentSyncState.js +33 -77
  272. package/dist/sync/syncApplyHandlers.d.ts +7 -0
  273. package/dist/sync/syncApplyHandlers.js +28 -13
  274. package/dist/sync/syncApplyHandlers.test.js +72 -3
  275. package/dist/sync/syncFieldSemantics.d.ts +12 -0
  276. package/dist/sync/syncFieldSemantics.js +55 -0
  277. package/dist/sync/syncResourceAdapters.d.ts +44 -0
  278. package/dist/sync/syncResourceAdapters.js +77 -0
  279. package/dist/sync/syncService.d.ts +6 -0
  280. package/dist/sync/syncService.js +60 -0
  281. package/dist/sync/syncService.test.js +71 -3
  282. package/dist/sync/taskSyncChangeKey.d.ts +2 -0
  283. package/dist/sync/taskSyncChangeKey.js +143 -0
  284. package/dist/sync/taskSyncPayload.js +3 -8
  285. package/dist/sync/workspacePullFeed.js +4 -3
  286. package/dist/sync/workspacePullFeed.test.js +43 -0
  287. package/dist/sync/workspaceRepair.js +5 -2
  288. package/dist/sync/workspaceSyncModel.d.ts +48 -0
  289. package/dist/sync/workspaceSyncModel.js +195 -31
  290. package/dist/sync/workspaceSyncModel.test.js +325 -12
  291. package/dist/sync/workspaceSyncState.d.ts +19 -0
  292. package/dist/sync/workspaceSyncState.js +3 -1
  293. package/dist/sync/workspaceSyncSurface.js +3 -8
  294. package/dist/types.d.ts +17 -0
  295. package/dist/ui/assets/AgentsModule-DrLawwNl.js +1 -0
  296. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BlS-cqnl.js → AnnotatedAttachmentWorkspace-Z9_whf7F.js} +1 -1
  297. package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
  298. package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
  299. package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
  300. package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
  301. package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +252 -0
  302. package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +1 -0
  303. package/dist/ui/assets/{InitiativesModule-BjR6iBf9.js → InitiativesModule-9E6ParrY.js} +1 -1
  304. package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
  305. package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
  306. package/dist/ui/assets/PlansPage-CvfnMiWq.css +1 -0
  307. package/dist/ui/assets/PlansPage-DRXscYxH.js +1 -0
  308. package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
  309. package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
  310. package/dist/ui/assets/TaskSettings-DZX4jk7e.js +9 -0
  311. package/dist/ui/assets/{WorkflowsModule-DnFqdUME.js → WorkflowsModule-BA7jcEpH.js} +1 -1
  312. package/dist/ui/assets/index-9eT8e0Lu.css +1 -0
  313. package/dist/ui/assets/index-C6k75jr8.js +6 -0
  314. package/dist/ui/assets/{vendor-icons-I7ZwG1z_.js → vendor-icons-DuEd_65c.js} +1 -1
  315. package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
  316. package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
  317. package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
  318. package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  319. package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
  320. package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
  321. package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
  322. package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
  323. package/dist/ui/fonts/Noir_medium.otf +0 -0
  324. package/dist/ui/fonts/Noir_medium.woff2 +0 -0
  325. package/dist/ui/fonts/Noir_regular.otf +0 -0
  326. package/dist/ui/fonts/Noir_regular.woff2 +0 -0
  327. package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
  328. package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
  329. package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
  330. package/dist/ui/index.html +3 -3
  331. package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
  332. package/dist/utils/assignees.d.ts +2 -0
  333. package/dist/utils/assignees.js +2 -0
  334. package/dist/utils/avatarUpload.d.ts +27 -0
  335. package/dist/utils/avatarUpload.js +132 -0
  336. package/dist/utils/avatarUpload.test.d.ts +1 -0
  337. package/dist/utils/avatarUpload.test.js +40 -0
  338. package/dist/utils/bootstrapTrace.d.ts +7 -0
  339. package/dist/utils/bootstrapTrace.js +29 -0
  340. package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
  341. package/dist/utils/commercialLifecyclePresentation.js +199 -0
  342. package/dist/utils/contextAssetEvents.d.ts +2 -0
  343. package/dist/utils/syncEventDetails.d.ts +3 -0
  344. package/dist/utils/syncEventDetails.js +64 -0
  345. package/dist/utils/syncEventDetails.test.d.ts +1 -0
  346. package/dist/utils/syncEventDetails.test.js +23 -0
  347. package/dist/utils/syncEventPresentation.d.ts +15 -0
  348. package/dist/utils/syncEventPresentation.js +129 -0
  349. package/dist/utils/syncEventPresentation.test.d.ts +1 -0
  350. package/dist/utils/syncEventPresentation.test.js +64 -0
  351. package/dist/utils/syncStatusPresentation.d.ts +72 -0
  352. package/dist/utils/syncStatusPresentation.js +217 -0
  353. package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
  354. package/dist/utils/syncStatusPresentation.test.js +164 -0
  355. package/dist/utils/taskActivity.d.ts +2 -0
  356. package/dist/utils/taskActivity.js +228 -22
  357. package/dist/utils/taskActivity.test.js +119 -8
  358. package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
  359. package/dist/utils/workspaceSyncPresentation.js +68 -2
  360. package/dist/utils/workspaceSyncPresentation.test.js +36 -1
  361. package/package.json +10 -2
  362. package/scripts/check-cloud-mcp.mjs +83 -0
  363. package/scripts/export-sqlite-to-postgres.mjs +7 -9
  364. package/scripts/playwright-auth.sh +5 -1
  365. package/scripts/run-billing-performance-smoke.sh +24 -0
  366. package/scripts/run-e2e-realtime.sh +17 -3
  367. package/scripts/run-smoke.sh +17 -5
  368. package/scripts/run-soak.sh +17 -5
  369. package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  370. package/src/resources/templates/workflows/collaborate.yaml +1 -1
  371. package/src/resources/templates/workflows/evaluate.yaml +2 -2
  372. package/src/resources/templates/workflows/plan.yaml +1 -1
  373. package/src/resources/templates/workflows/review.yaml +2 -2
  374. package/dist/ui/assets/AgentsModule-BLWVbuKP.js +0 -1
  375. package/dist/ui/assets/DocumentWorkspace-BH_6DF6x.js +0 -250
  376. package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
  377. package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
  378. package/dist/ui/assets/PlansPage-CSDQ4sLa.js +0 -1
  379. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  380. package/dist/ui/assets/TaskSettings-CEDy34Jo.js +0 -9
  381. package/dist/ui/assets/index-DXUdtH1B.js +0 -6
  382. package/dist/ui/assets/index-Ii0B0rTO.css +0 -1
  383. package/scripts/migrate-planning-model.ts +0 -233
@@ -53,6 +53,7 @@ interface TaskKanbanProps {
53
53
  filterPriorities?: number[];
54
54
  filterStatus?: string[];
55
55
  filterAssignees?: TaskAssignee[];
56
+ filtersReady?: boolean;
56
57
  scheduleFilteredTaskIds?: string[];
57
58
  categories?: CategoryConfig[];
58
59
  compressed?: boolean;
@@ -71,5 +72,5 @@ interface TaskKanbanProps {
71
72
  workstreams?: WorkstreamItem[];
72
73
  initiatives?: InitiativeItem[];
73
74
  }
74
- export declare function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery, copiedId, taxonomies, types, priorities, approaches, assigneeOptions, onUpdateTask, onTaskClick, onOpenTaskById, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onUnarchive, onDelete, allTasks, onAddTaskToColumn, emptyColumnMode, filterCategories, filterTypes, filterPriorities, filterStatus, filterAssignees, scheduleFilteredTaskIds, categories, compressed, readOnlyMode, recentlyChangedTaskIds, scheduleDates, onScheduleDaySelected, persistedScrollLeft, onScrollLeftChange, sortBy, sortOrder, planningDropTargets, onAssignTaskToWorkstream, onAssignWorkstreamToInitiative, showTaskCardStatusLabel, workstreams, initiatives, }: TaskKanbanProps): import("react/jsx-runtime").JSX.Element;
75
+ export declare function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery, copiedId, taxonomies, types, priorities, approaches, assigneeOptions, onUpdateTask, onTaskClick, onOpenTaskById, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onUnarchive, onDelete, allTasks, onAddTaskToColumn, emptyColumnMode, filterCategories, filterTypes, filterPriorities, filterStatus, filterAssignees, filtersReady, scheduleFilteredTaskIds, categories, compressed, readOnlyMode, recentlyChangedTaskIds, scheduleDates, onScheduleDaySelected, persistedScrollLeft, onScrollLeftChange, sortBy, sortOrder, planningDropTargets, onAssignTaskToWorkstream, onAssignWorkstreamToInitiative, showTaskCardStatusLabel, workstreams, initiatives, }: TaskKanbanProps): import("react/jsx-runtime").JSX.Element;
75
76
  export {};
@@ -11,7 +11,7 @@ import { buildScheduleDropResult, buildStatusTransitionUpdates, getGroupedTaskSt
11
11
  import * as Icons from 'lucide-react';
12
12
  import { IconMap, resolveColor } from '../../utils/constants';
13
13
  const EXPIRED_CARD_ID_PREFIX = 'expired::';
14
- export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery, copiedId, taxonomies, types, priorities, approaches, assigneeOptions = [], onUpdateTask, onTaskClick, onOpenTaskById, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onUnarchive, onDelete, allTasks = [], onAddTaskToColumn, emptyColumnMode = 'show', filterCategories = [], filterTypes = [], filterPriorities = [], filterStatus = [], filterAssignees = [], scheduleFilteredTaskIds, categories = [], compressed = false, readOnlyMode = null, recentlyChangedTaskIds = [], scheduleDates, onScheduleDaySelected, persistedScrollLeft, onScrollLeftChange, sortBy = 'created', sortOrder = 'desc', planningDropTargets = null, onAssignTaskToWorkstream, onAssignWorkstreamToInitiative, showTaskCardStatusLabel = true, workstreams = [], initiatives = [], }) {
14
+ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery, copiedId, taxonomies, types, priorities, approaches, assigneeOptions = [], onUpdateTask, onTaskClick, onOpenTaskById, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onUnarchive, onDelete, allTasks = [], onAddTaskToColumn, emptyColumnMode = 'show', filterCategories = [], filterTypes = [], filterPriorities = [], filterStatus = [], filterAssignees = [], filtersReady = true, scheduleFilteredTaskIds, categories = [], compressed = false, readOnlyMode = null, recentlyChangedTaskIds = [], scheduleDates, onScheduleDaySelected, persistedScrollLeft, onScrollLeftChange, sortBy = 'created', sortOrder = 'desc', planningDropTargets = null, onAssignTaskToWorkstream, onAssignWorkstreamToInitiative, showTaskCardStatusLabel = true, workstreams = [], initiatives = [], }) {
15
15
  const [activeId, setActiveId] = useState(null);
16
16
  const [isHeaderPanning, setIsHeaderPanning] = useState(false);
17
17
  const [headerPanEnabled, setHeaderPanEnabled] = useState(false);
@@ -53,7 +53,8 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
53
53
  // Sensors
54
54
  const sensors = useSensors(useSensor(PointerSensor, {
55
55
  activationConstraint: {
56
- distance: 5, // Prevent accidental drags
56
+ // Give normal clicks a bit more tolerance so slight trackpad drift still opens the task.
57
+ distance: 10,
57
58
  },
58
59
  }), useSensor(KeyboardSensor, {
59
60
  coordinateGetter: sortableKeyboardCoordinates,
@@ -129,20 +130,99 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
129
130
  ]);
130
131
  return dateToColumn.get(scheduledDate) || '__offweek';
131
132
  };
133
+ const inferredKanbanColumns = useMemo(() => {
134
+ if (groupBy === 'schedule')
135
+ return [];
136
+ const existingValues = new Set(kanbanColumns.map((column) => String(column.value)));
137
+ const existingLabels = new Set(kanbanColumns.map((column) => column.label));
138
+ const inferred = [];
139
+ const buildColumn = (task) => {
140
+ const rawValue = groupBy === 'status'
141
+ ? getTaskStatus(task)
142
+ : task[groupBy];
143
+ const value = String(rawValue ?? '');
144
+ if (!value || existingValues.has(value) || existingLabels.has(value))
145
+ return null;
146
+ if (groupBy === 'category') {
147
+ const category = categories.find((entry) => entry.value === value || entry.label === value);
148
+ return {
149
+ value,
150
+ label: category?.label || value,
151
+ icon: category?.icon || 'Folder',
152
+ color: category?.color,
153
+ };
154
+ }
155
+ if (groupBy === 'type') {
156
+ const type = types?.find((entry) => entry.value === value || entry.label === value);
157
+ return {
158
+ value,
159
+ label: type?.label || value,
160
+ icon: type?.icon || 'CheckSquare',
161
+ color: type?.color,
162
+ };
163
+ }
164
+ if (groupBy === 'priority') {
165
+ const priority = priorities?.find((entry) => Number(entry.value) === Number(value));
166
+ return {
167
+ value: Number.isFinite(Number(value)) ? Number(value) : value,
168
+ label: priority?.label || value,
169
+ icon: priority?.icon || 'Flag',
170
+ color: priority?.color,
171
+ };
172
+ }
173
+ if (groupBy === 'complexity') {
174
+ return {
175
+ value: Number.isFinite(Number(value)) ? Number(value) : value,
176
+ label: value,
177
+ icon: 'Gauge',
178
+ };
179
+ }
180
+ if (groupBy === 'status') {
181
+ return {
182
+ value,
183
+ label: value,
184
+ icon: 'Circle',
185
+ };
186
+ }
187
+ if (groupBy === 'assignee') {
188
+ const option = assigneeOptions.find((entry) => entry.value === value);
189
+ return {
190
+ value,
191
+ label: option?.label || value,
192
+ icon: option?.icon === 'HelpCircle' ? 'Circle' : option?.icon,
193
+ color: option?.color,
194
+ };
195
+ }
196
+ return {
197
+ value,
198
+ label: value,
199
+ };
200
+ };
201
+ for (const task of tasks) {
202
+ const column = buildColumn(task);
203
+ if (!column)
204
+ continue;
205
+ inferred.push(column);
206
+ existingValues.add(String(column.value));
207
+ existingLabels.add(column.label);
208
+ }
209
+ return inferred;
210
+ }, [assigneeOptions, categories, groupBy, kanbanColumns, priorities, tasks, types]);
211
+ const effectiveKanbanColumns = useMemo(() => [...kanbanColumns, ...inferredKanbanColumns], [inferredKanbanColumns, kanbanColumns]);
132
212
  const columnLookup = useMemo(() => {
133
213
  const map = new Map();
134
- for (const column of kanbanColumns) {
214
+ for (const column of effectiveKanbanColumns) {
135
215
  map.set(String(column.value), column);
136
216
  if (!map.has(column.label)) {
137
217
  map.set(column.label, column);
138
218
  }
139
219
  }
140
220
  return map;
141
- }, [kanbanColumns]);
221
+ }, [effectiveKanbanColumns]);
142
222
  // Totals for "X / Y" display
143
223
  const columnTotals = useMemo(() => {
144
224
  const counts = {};
145
- kanbanColumns.forEach(col => { counts[String(col.value)] = 0; });
225
+ effectiveKanbanColumns.forEach(col => { counts[String(col.value)] = 0; });
146
226
  const totalsSource = allTasks.length > 0 ? allTasks : tasks;
147
227
  totalsSource.forEach(task => {
148
228
  let stringValue = '';
@@ -169,12 +249,12 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
169
249
  }
170
250
  });
171
251
  return counts;
172
- }, [allTasks, tasks, kanbanColumns, groupBy, columnLookup, todayDateOnly]);
252
+ }, [allTasks, tasks, effectiveKanbanColumns, groupBy, columnLookup, todayDateOnly]);
173
253
  // Group items by the groupBy field
174
254
  const groupedTasks = useMemo(() => {
175
255
  const groups = {};
176
256
  // Initialize all columns
177
- kanbanColumns.forEach(col => {
257
+ effectiveKanbanColumns.forEach(col => {
178
258
  groups[String(col.value)] = [];
179
259
  });
180
260
  // Distribute tasks
@@ -231,7 +311,7 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
231
311
  });
232
312
  }
233
313
  return groups;
234
- }, [tasks, allTasks, kanbanColumns, groupBy, columnLookup, scheduleFilteredTaskIdSet, todayDateOnly, sortBy, sortOrder]);
314
+ }, [tasks, allTasks, effectiveKanbanColumns, groupBy, columnLookup, scheduleFilteredTaskIdSet, todayDateOnly, sortBy, sortOrder]);
235
315
  const activeTaskId = activeId ? getTaskIdFromCardId(activeId) : null;
236
316
  const activeTaskOverlay = activeTaskId ? tasks.find(t => t.id === activeTaskId) : null;
237
317
  // Scroll Syncing Refs and Logic
@@ -247,6 +327,8 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
247
327
  const lastReportedScrollLeftRef = useRef(-1);
248
328
  // Calculate total width for scroll syncing
249
329
  const getIsColumnHidden = (columnValue, title) => {
330
+ if (!filtersReady)
331
+ return false;
250
332
  if (groupBy === 'category')
251
333
  return !filterCategories.includes(String(columnValue));
252
334
  if (groupBy === 'type')
@@ -268,7 +350,7 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
268
350
  const COL_WIDTH = 320;
269
351
  const COL_COLLAPSED = 72;
270
352
  let width = 0;
271
- kanbanColumns.forEach((col, index) => {
353
+ effectiveKanbanColumns.forEach((col, index) => {
272
354
  const items = groupedTasks[String(col.value)] || [];
273
355
  const isExplicitlyHidden = getIsColumnHidden(col.value, col.label);
274
356
  const isAutoHidden = groupBy !== 'schedule' && emptyColumnMode === 'hide' && items.length === 0;
@@ -279,7 +361,7 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
279
361
  width += GAP;
280
362
  });
281
363
  return width;
282
- }, [kanbanColumns, groupedTasks, emptyColumnMode, groupBy, filterCategories, filterTypes, filterPriorities, filterStatus]);
364
+ }, [effectiveKanbanColumns, groupedTasks, emptyColumnMode, groupBy, filterCategories, filterTypes, filterPriorities, filterStatus, filterAssignees, filtersReady]);
283
365
  const emitScrollLeft = useCallback((scrollLeft) => {
284
366
  if (!onScrollLeftChange)
285
367
  return;
@@ -352,7 +434,7 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
352
434
  return () => {
353
435
  window.removeEventListener('resize', updatePanAvailability);
354
436
  };
355
- }, [totalWidth, kanbanColumns.length, groupedTasks]);
437
+ }, [totalWidth, effectiveKanbanColumns.length, groupedTasks]);
356
438
  useEffect(() => {
357
439
  const handlePointerMove = (event) => {
358
440
  const pan = panStateRef.current;
@@ -473,7 +555,7 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
473
555
  return;
474
556
  // Find the column definition
475
557
  const column = columnLookup.get(containerId)
476
- || kanbanColumns.find(c => String(c.value) === containerId || c.label === containerId);
558
+ || effectiveKanbanColumns.find(c => String(c.value) === containerId || c.label === containerId);
477
559
  if (column) {
478
560
  if (groupBy === 'schedule') {
479
561
  const targetColumn = String(column.value);
@@ -680,12 +762,12 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
680
762
  return (_jsxs(DndContext, { sensors: sensors, collisionDetection: customCollisionDetection, onDragStart: handleDragStart, onDragOver: handleDragOver, onDragEnd: handleDragEnd, onDragCancel: (event) => {
681
763
  lastPlanningOverRef.current = null;
682
764
  setActiveId(null);
683
- }, children: [planningDropTargets, _jsxs("div", { className: styles.kanbanWrapper, children: [_jsx("div", { ref: topScrollRef, className: `${styles.kanbanTopScroll} tf-scrollbar`, children: _jsx("div", { className: styles.kanbanTopScrollSpacer, style: { width: `${totalWidth}px` } }) }), _jsx("div", { className: styles.kanbanContainer, ref: containerRef, children: kanbanColumns.map((column) => {
765
+ }, children: [planningDropTargets, _jsxs("div", { className: styles.kanbanWrapper, children: [_jsx("div", { ref: topScrollRef, className: `${styles.kanbanTopScroll} tf-scrollbar`, children: _jsx("div", { className: styles.kanbanTopScrollSpacer, style: { width: `${totalWidth}px` } }) }), _jsx("div", { className: styles.kanbanContainer, ref: containerRef, children: effectiveKanbanColumns.map((column) => {
684
766
  const columnTasks = groupedTasks[String(column.value)] || [];
685
767
  const isExplicitlyHidden = getIsColumnHidden(column.value, column.label);
686
768
  const isAutoHidden = groupBy !== 'schedule' && emptyColumnMode === 'hide' && columnTasks.length === 0;
687
769
  const isScheduleMode = groupBy === 'schedule';
688
- const hasBacklogColumn = isScheduleMode && kanbanColumns.some((entry) => String(entry.value) === 'backlog');
770
+ const hasBacklogColumn = isScheduleMode && effectiveKanbanColumns.some((entry) => String(entry.value) === 'backlog');
689
771
  const isBacklog = isScheduleMode && String(column.value) === 'backlog';
690
772
  const isExpired = isScheduleMode && String(column.value) === 'expired';
691
773
  const backlogTasks = groupedTasks.backlog || [];
@@ -61,3 +61,39 @@ describe('TaskKanban schedule expired behavior', () => {
61
61
  expect(screen.getByText('1 / 2')).toBeInTheDocument();
62
62
  });
63
63
  });
64
+ describe('TaskKanban filter bootstrap behavior', () => {
65
+ it('keeps task columns visible while filters are still initializing', () => {
66
+ const task = makeTask({
67
+ id: 'task-filter-bootstrap',
68
+ title: 'Filter bootstrap visible task',
69
+ category: 'shared'
70
+ });
71
+ render(_jsx(TaskKanban, { tasks: [task], allTasks: [task], columns: [
72
+ { value: 'shared', label: 'Shared', color: '#3b82f6', icon: 'Folder' }
73
+ ], groupBy: "category", filterCategories: [], filtersReady: false, onUpdateTask: vi.fn(), onTaskClick: vi.fn() }));
74
+ expect(screen.getByText('Shared')).toBeInTheDocument();
75
+ expect(screen.getByText('Filter bootstrap visible task')).toBeInTheDocument();
76
+ });
77
+ it('renders a discoverable task when category columns are not loaded yet', () => {
78
+ const task = makeTask({
79
+ id: 'task-missing-category-columns',
80
+ title: 'Visible before categories load',
81
+ category: 'default'
82
+ });
83
+ render(_jsx(TaskKanban, { tasks: [task], allTasks: [task], columns: [], groupBy: "category", filterCategories: [], filtersReady: false, onUpdateTask: vi.fn(), onTaskClick: vi.fn() }));
84
+ expect(screen.getByText('default')).toBeInTheDocument();
85
+ expect(screen.getByText('Visible before categories load')).toBeInTheDocument();
86
+ });
87
+ it('hides filtered-out columns after filters initialize', () => {
88
+ const task = makeTask({
89
+ id: 'task-filter-ready',
90
+ title: 'Filter ready hidden task',
91
+ category: 'shared'
92
+ });
93
+ render(_jsx(TaskKanban, { tasks: [task], allTasks: [task], columns: [
94
+ { value: 'shared', label: 'Shared', color: '#3b82f6', icon: 'Folder' }
95
+ ], groupBy: "category", filterCategories: [], filtersReady: true, onUpdateTask: vi.fn(), onTaskClick: vi.fn() }));
96
+ expect(screen.queryByText('Shared')).not.toBeInTheDocument();
97
+ expect(screen.queryByText('Filter ready hidden task')).not.toBeInTheDocument();
98
+ });
99
+ });
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ interface EditableAvatarButtonProps {
3
+ label: string;
4
+ imageUrl?: string | null;
5
+ fallback?: React.ReactNode;
6
+ accentColor?: string | null;
7
+ size?: number;
8
+ width?: number;
9
+ height?: number;
10
+ radius?: number | string;
11
+ editBadgeSize?: number | string;
12
+ editIconSize?: number;
13
+ disabled?: boolean;
14
+ className?: string;
15
+ onClick: () => void;
16
+ }
17
+ export declare function EditableAvatarButton({ label, imageUrl, fallback, accentColor, size, width, height, radius, editBadgeSize, editIconSize, disabled, className, onClick, }: EditableAvatarButtonProps): import("react/jsx-runtime").JSX.Element;
18
+ export {};
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Pencil } from 'lucide-react';
3
+ import styles from './EditableAvatarButton.module.css';
4
+ export function EditableAvatarButton({ label, imageUrl, fallback, accentColor, size = 52, width, height, radius, editBadgeSize, editIconSize = 16, disabled = false, className = '', onClick, }) {
5
+ const normalizedImageUrl = String(imageUrl || '').trim();
6
+ const resolvedWidth = width ?? size;
7
+ const resolvedHeight = height ?? size;
8
+ const resolvedRadius = typeof radius === 'number' ? `${radius}px` : radius;
9
+ const resolvedEditBadgeSize = typeof editBadgeSize === 'number' ? `${editBadgeSize}px` : editBadgeSize;
10
+ return (_jsxs("button", { type: "button", className: `${styles.editableAvatarButton} ${className}`.trim(), "aria-label": label, title: label, onClick: onClick, disabled: disabled, style: {
11
+ ['--editable-avatar-size']: `${size}px`,
12
+ ['--editable-avatar-width']: `${resolvedWidth}px`,
13
+ ['--editable-avatar-height']: `${resolvedHeight}px`,
14
+ ...(resolvedRadius ? { ['--editable-avatar-radius']: resolvedRadius } : {}),
15
+ ...(resolvedEditBadgeSize ? { ['--editable-avatar-badge-size']: resolvedEditBadgeSize } : {}),
16
+ ...(accentColor ? { ['--editable-avatar-accent']: accentColor } : {})
17
+ }, children: [normalizedImageUrl ? (_jsx("img", { src: normalizedImageUrl, alt: "", className: styles.editableAvatarImage })) : (_jsx("span", { className: styles.editableAvatarFallback, children: fallback })), _jsx("span", { className: styles.editableAvatarBadge, "aria-hidden": "true", children: _jsx(Pencil, { size: editIconSize }) })] }));
18
+ }
@@ -4,6 +4,7 @@ export type BillingPricingType = 'stripe' | 'free';
4
4
  export interface PublicPricingPlan {
5
5
  planId: string;
6
6
  displayName: string;
7
+ description?: string | null;
7
8
  status: string;
8
9
  defaultVersion: {
9
10
  planVersionId: string;
@@ -15,6 +16,10 @@ export interface PublicPricingPlan {
15
16
  featureKey: string;
16
17
  access: string;
17
18
  config?: Record<string, unknown>;
19
+ label?: string | null;
20
+ description?: string | null;
21
+ publicLabel?: string | null;
22
+ publicDescription?: string | null;
18
23
  }>;
19
24
  pricing: {
20
25
  month: {
@@ -55,6 +60,7 @@ interface PlanComparisonPageProps {
55
60
  currentPlanId?: string | null;
56
61
  currentPlanVersionId?: string | null;
57
62
  currentEntitlementState?: string | null;
63
+ markCurrentPlan?: boolean;
58
64
  canManageCurrentPlan?: boolean;
59
65
  pricingEndpoint?: string;
60
66
  statusBanner?: {
@@ -67,8 +73,10 @@ interface PlanComparisonPageProps {
67
73
  topActions?: React.ReactNode;
68
74
  shellOwnsScroll?: boolean;
69
75
  showPageBackButton?: boolean;
76
+ showHeaderBarWithChrome?: boolean;
77
+ currentPlanCardActionMode?: 'manage' | 'indicator' | 'hidden';
70
78
  fallbackPricingSource?: PricingSourceDetails | null;
71
79
  onSelectPlan: (intent: PlanSelectionIntent) => void | Promise<void>;
72
80
  }
73
- export declare function PlanComparisonPage({ heading, subtitle, variant, chrome, footer, isAuthenticated, authSessionResolved, currentPlanId, currentPlanVersionId, currentEntitlementState, canManageCurrentPlan, pricingEndpoint, statusBanner, onBack, backLabel, backDisabled, topActions, shellOwnsScroll, showPageBackButton, fallbackPricingSource, onSelectPlan }: PlanComparisonPageProps): import("react/jsx-runtime").JSX.Element;
81
+ export declare function PlanComparisonPage({ heading, subtitle, variant, chrome, footer, isAuthenticated, authSessionResolved, currentPlanId, currentPlanVersionId, currentEntitlementState, markCurrentPlan, canManageCurrentPlan, pricingEndpoint, statusBanner, onBack, backLabel, backDisabled, topActions, shellOwnsScroll, showPageBackButton, showHeaderBarWithChrome, currentPlanCardActionMode, fallbackPricingSource, onSelectPlan }: PlanComparisonPageProps): import("react/jsx-runtime").JSX.Element;
74
82
  export {};
@@ -3,6 +3,7 @@ import { useEffect, useMemo, useState } from 'react';
3
3
  import { CheckCircle2, Loader2 } from 'lucide-react';
4
4
  import taskforceStyles from '../../Taskforce.module.css';
5
5
  import styles from './PlansPage.module.css';
6
+ import { isCommercialEntitlementActive } from '../../utils/commercialLifecyclePresentation';
6
7
  function isDebugModeEnabled() {
7
8
  const runtimeOverride = globalThis.__DEBUG_MODE__;
8
9
  if (typeof runtimeOverride === 'boolean')
@@ -60,11 +61,49 @@ function getPlanSortRank(plan) {
60
61
  return Math.min(...numericAmounts);
61
62
  return Number.POSITIVE_INFINITY;
62
63
  }
63
- function isActiveEntitlementState(raw) {
64
- const value = String(raw || '').trim().toLowerCase();
65
- return value === 'active' || value === 'trialing' || value === 'grace';
64
+ function formatFeatureKeyFallback(featureKey) {
65
+ return String(featureKey || '')
66
+ .replace(/[_\-.]+/g, ' ')
67
+ .replace(/\b\w/g, (c) => c.toUpperCase());
66
68
  }
67
- export function PlanComparisonPage({ heading, subtitle, variant = 'app', chrome, footer, isAuthenticated, authSessionResolved = true, currentPlanId, currentPlanVersionId, currentEntitlementState, canManageCurrentPlan = true, pricingEndpoint, statusBanner, onBack, backLabel, backDisabled = false, topActions, shellOwnsScroll = false, showPageBackButton = true, fallbackPricingSource = null, onSelectPlan }) {
69
+ function readConfiguredFeatureLimit(config, key) {
70
+ const rawValue = config?.[key];
71
+ const numericValue = Number(rawValue);
72
+ if (!Number.isFinite(numericValue))
73
+ return null;
74
+ const normalized = Math.max(1, Math.floor(numericValue));
75
+ return normalized;
76
+ }
77
+ function getFeatureDisplayParts(feature, seatLimit) {
78
+ const baseLabel = String(feature.publicLabel || feature.label || '').trim() || formatFeatureKeyFallback(feature.featureKey);
79
+ const config = feature.config && typeof feature.config === 'object' ? feature.config : {};
80
+ if (feature.featureKey === 'context.uploads') {
81
+ const storageLimitMb = readConfiguredFeatureLimit(config, 'storageLimitMb');
82
+ if (storageLimitMb !== null) {
83
+ return { limitValue: String(storageLimitMb), limitUnit: 'MB', baseLabel };
84
+ }
85
+ }
86
+ if (feature.featureKey === 'workspace.workspaces') {
87
+ const maxWorkspaces = readConfiguredFeatureLimit(config, 'maxWorkspaces');
88
+ if (maxWorkspaces !== null) {
89
+ return { limitValue: String(maxWorkspaces), limitUnit: 'x', baseLabel };
90
+ }
91
+ }
92
+ if (feature.featureKey === 'collaboration.ai_profiles') {
93
+ const maxAiProfiles = readConfiguredFeatureLimit(config, 'maxAiProfiles');
94
+ if (maxAiProfiles !== null) {
95
+ return { limitValue: String(maxAiProfiles), limitUnit: 'x', baseLabel };
96
+ }
97
+ }
98
+ if (feature.featureKey === 'collaboration.team_management') {
99
+ const normalizedSeatLimit = Number(seatLimit);
100
+ if (Number.isFinite(normalizedSeatLimit) && normalizedSeatLimit > 0) {
101
+ return { limitValue: String(Math.floor(normalizedSeatLimit)), limitUnit: 'x', baseLabel };
102
+ }
103
+ }
104
+ return { limitValue: null, limitUnit: null, baseLabel };
105
+ }
106
+ export function PlanComparisonPage({ heading, subtitle, variant = 'app', chrome, footer, isAuthenticated, authSessionResolved = true, currentPlanId, currentPlanVersionId, currentEntitlementState, markCurrentPlan, canManageCurrentPlan = true, pricingEndpoint, statusBanner, onBack, backLabel, backDisabled = false, topActions, shellOwnsScroll = false, showPageBackButton = true, showHeaderBarWithChrome = false, currentPlanCardActionMode = 'manage', fallbackPricingSource = null, onSelectPlan }) {
68
107
  const [plans, setPlans] = useState([]);
69
108
  const [pricingSource, setPricingSource] = useState(null);
70
109
  const [loading, setLoading] = useState(true);
@@ -117,6 +156,7 @@ export function PlanComparisonPage({ heading, subtitle, variant = 'app', chrome,
117
156
  return String(a.displayName || a.planId).localeCompare(String(b.displayName || b.planId));
118
157
  }), [plans]);
119
158
  const pricingSourceLabel = useMemo(() => formatPricingSource(pricingSource || fallbackPricingSource), [fallbackPricingSource, pricingSource]);
159
+ const planSelectionEnabled = authSessionResolved;
120
160
  const contentClassName = `${styles.plansWrapper} ${shellOwnsScroll ? styles.shellOwnsScroll : 'tf-scrollbar'} ${variant === 'site' ? styles.siteVariant : ''} ${chrome ? styles.withChrome : ''}`.trim();
121
161
  const wrapPage = (content) => {
122
162
  if (shellOwnsScroll) {
@@ -124,13 +164,13 @@ export function PlanComparisonPage({ heading, subtitle, variant = 'app', chrome,
124
164
  }
125
165
  return (_jsxs("div", { className: taskforceStyles.standaloneWrapper, "data-theme": "dark", children: [chrome, content, footer] }));
126
166
  };
127
- if (loading || !authSessionResolved) {
167
+ if (loading) {
128
168
  return wrapPage(_jsx(_Fragment, { children: _jsx("div", { className: contentClassName, children: _jsx("div", { className: styles.contentFrame, children: _jsxs("div", { className: styles.loader, children: [_jsx(Loader2, { size: 48, className: styles.spinner }), _jsx("p", { children: "Loading plans..." })] }) }) }) }));
129
169
  }
130
170
  if (error) {
131
171
  return wrapPage(_jsx(_Fragment, { children: _jsx("div", { className: contentClassName, children: _jsx("div", { className: styles.contentFrame, children: _jsxs("div", { className: styles.emptyState, children: [_jsx("h2", { children: "Connecting to plan pricing..." }), _jsx("p", { children: error }), _jsx("button", { className: styles.backBtn, onClick: () => setRetryNonce((value) => value + 1), children: "Retry" })] }) }) }) }));
132
172
  }
133
- return wrapPage(_jsx(_Fragment, { children: _jsx("div", { className: contentClassName, children: _jsxs("div", { className: styles.contentFrame, children: [_jsxs("div", { className: styles.header, children: [!chrome && (showPageBackButton || topActions) && (_jsxs("div", { className: styles.headerBar, children: [_jsx("div", { children: showPageBackButton ? (_jsx("button", { className: styles.backBtn, onClick: onBack, disabled: backDisabled, children: backLabel })) : null }), _jsx("div", { className: styles.headerActions, children: topActions })] })), _jsx("h1", { className: styles.title, children: heading }), subtitle && _jsx("p", { className: styles.subtitle, children: subtitle }), isDebugModeEnabled() && pricingSourceLabel ? (_jsxs("p", { className: styles.debugMeta, children: ["Pricing source: ", pricingSourceLabel] })) : null, statusBanner && (_jsx("div", { className: `${styles.statusBanner} ${statusBanner.type === 'success'
173
+ return wrapPage(_jsx(_Fragment, { children: _jsx("div", { className: contentClassName, children: _jsxs("div", { className: styles.contentFrame, children: [_jsxs("div", { className: styles.header, children: [(!chrome || showHeaderBarWithChrome) && (showPageBackButton || topActions) && (_jsxs("div", { className: styles.headerBar, children: [_jsx("div", { children: showPageBackButton ? (_jsx("button", { className: styles.backBtn, onClick: onBack, disabled: backDisabled, children: backLabel })) : null }), _jsx("div", { className: styles.headerActions, children: topActions })] })), _jsx("h1", { className: styles.title, children: heading }), subtitle && _jsx("p", { className: styles.subtitle, children: subtitle }), isDebugModeEnabled() && pricingSourceLabel ? (_jsxs("p", { className: styles.debugMeta, children: ["Pricing source: ", pricingSourceLabel] })) : null, statusBanner && (_jsx("div", { className: `${styles.statusBanner} ${statusBanner.type === 'success'
134
174
  ? styles.successBanner
135
175
  : (statusBanner.type === 'info' ? styles.infoBanner : styles.errorBanner)}`, children: statusBanner.message }))] }), sortedPlans.length === 0 ? (_jsxs("div", { className: `${styles.emptyState} ${styles.marketingEmptyState}`, children: [_jsx("span", { className: styles.emptyBadge, children: "Coming Soon" }), _jsx("h2", { children: "Paid plans are getting their final polish." }), _jsx("p", { children: "Taskforce pricing is on the way with flexible options for solo operators, teams, and larger rollouts. Check back soon for launch tiers, feature bundles, and early access details." }), _jsxs("div", { className: styles.emptyHighlights, "aria-label": "Upcoming pricing highlights", children: [_jsx("span", { children: "Launch-ready tiers" }), _jsx("span", { children: "Team billing controls" }), _jsx("span", { children: "Feature-based packaging" })] })] })) : (_jsx("div", { className: styles.grid, children: sortedPlans.map((plan) => {
136
176
  const monthPrice = plan.pricing.month;
@@ -138,18 +178,28 @@ export function PlanComparisonPage({ heading, subtitle, variant = 'app', chrome,
138
178
  const hasMonth = isSelectablePrice(monthPrice);
139
179
  const hasYear = isSelectablePrice(yearPrice);
140
180
  const planVersionId = String(plan.defaultVersion?.planVersionId || '').trim();
141
- const isCurrent = Boolean(planVersionId
181
+ const hasActiveEntitlement = isCommercialEntitlementActive(currentEntitlementState);
182
+ const shouldMarkCurrentPlan = markCurrentPlan ?? hasActiveEntitlement;
183
+ const hasKnownLinkedPlan = Boolean(planVersionId
142
184
  && currentPlanVersionId
143
- && currentPlanVersionId === planVersionId
144
- && isActiveEntitlementState(currentEntitlementState)) || (!currentPlanVersionId
185
+ && currentPlanVersionId === planVersionId) || Boolean(!currentPlanVersionId
145
186
  && Boolean(currentPlanId && currentPlanId === plan.planId));
187
+ const isCurrent = shouldMarkCurrentPlan && hasKnownLinkedPlan && currentEntitlementState !== 'canceled';
146
188
  const defaultInterval = hasMonth ? 'month' : (hasYear ? 'year' : null);
147
189
  const defaultPrice = defaultInterval === 'year' ? yearPrice : monthPrice;
148
- const canSelectCard = Boolean(planVersionId) && (isCurrent ? canManageCurrentPlan : Boolean(defaultInterval));
190
+ const currentPlanShowsManageAction = currentPlanCardActionMode === 'manage';
191
+ const currentPlanShowsIndicator = currentPlanCardActionMode === 'indicator';
192
+ const canSelectCard = planSelectionEnabled
193
+ && Boolean(planVersionId)
194
+ && (isCurrent
195
+ ? (currentPlanShowsManageAction && canManageCurrentPlan)
196
+ : Boolean(defaultInterval));
149
197
  const handleCardSelect = () => {
150
198
  if (!planVersionId)
151
199
  return;
152
200
  if (isCurrent) {
201
+ if (!currentPlanShowsManageAction)
202
+ return;
153
203
  if (!canManageCurrentPlan)
154
204
  return;
155
205
  void onSelectPlan({
@@ -176,23 +226,26 @@ export function PlanComparisonPage({ heading, subtitle, variant = 'app', chrome,
176
226
  event.preventDefault();
177
227
  handleCardSelect();
178
228
  }
179
- } : undefined, children: [isCurrent && _jsx("span", { className: styles.currentBadge, children: "Current Plan" }), _jsx("h3", { className: styles.planName, children: plan.displayName }), _jsx("div", { className: styles.featuresList, children: Array.isArray(plan.features) && plan.features.length > 0 ? plan.features.map((feature, idx) => (_jsxs("div", { className: styles.featureItem, children: [_jsx(CheckCircle2, { className: styles.featureIcon }), _jsx("span", { className: styles.featureText, children: String(feature.featureKey || '').replace(/[_\-.]+/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()) })] }, `${feature.featureKey}-${idx}`))) : (_jsx("span", { className: styles.featureText, children: "No additional features included" })) }), _jsxs("div", { className: styles.priceRows, children: [_jsxs("div", { className: styles.priceRow, children: [_jsx("span", { className: styles.priceRowAmount, children: renderPriceLabel(monthPrice) }), _jsx("span", { className: styles.priceRowInterval, children: "/ month" })] }), _jsxs("div", { className: styles.priceRow, children: [_jsx("span", { className: styles.priceRowAmount, children: renderPriceLabel(yearPrice) }), _jsx("span", { className: styles.priceRowInterval, children: "/ year" })] })] }), isCurrent ? (_jsx("button", { className: `${styles.callToAction} ${styles.currentCta}`, "data-testid": `plan-manage-${plan.planId}`, onClick: (event) => {
229
+ } : undefined, children: [isCurrent && _jsx("span", { className: styles.currentBadge, children: "Current Plan" }), _jsx("h3", { className: styles.planName, children: plan.displayName }), String(plan.description || '').trim() && (_jsx("p", { className: styles.planDescription, children: String(plan.description || '').trim() })), _jsx("div", { className: styles.featuresList, children: Array.isArray(plan.features) && plan.features.length > 0 ? plan.features.map((feature, idx) => (_jsxs("div", { className: styles.featureItem, children: [_jsx(CheckCircle2, { className: styles.featureIcon }), _jsxs("div", { className: styles.featureTextBlock, children: [(() => {
230
+ const { limitValue, limitUnit, baseLabel } = getFeatureDisplayParts(feature, plan.defaultVersion?.seatLimit);
231
+ return (_jsxs("span", { className: styles.featureText, children: [limitValue && (_jsxs("span", { className: styles.featureLimitAccent, children: [_jsx("span", { children: limitValue }), limitUnit ? (_jsx("span", { className: limitUnit === 'MB' ? styles.featureLimitUnit : undefined, children: limitUnit })) : null] })), _jsx("span", { children: limitValue ? ` ${baseLabel}` : baseLabel })] }));
232
+ })(), String(feature.publicDescription || feature.description || '').trim() && (_jsx("span", { className: styles.featureDescription, children: String(feature.publicDescription || feature.description || '').trim() }))] })] }, `${feature.featureKey}-${idx}`))) : (_jsx("span", { className: styles.featureText, children: "No additional features included" })) }), (hasMonth || hasYear) ? (_jsxs("div", { className: styles.priceRows, children: [hasMonth ? (_jsxs("div", { className: styles.priceRow, children: [_jsx("span", { className: styles.priceRowAmount, children: renderPriceLabel(monthPrice) }), _jsx("span", { className: styles.priceRowInterval, children: "/ month" })] })) : null, hasYear ? (_jsxs("div", { className: styles.priceRow, children: [_jsx("span", { className: styles.priceRowAmount, children: renderPriceLabel(yearPrice) }), _jsx("span", { className: styles.priceRowInterval, children: "/ year" })] })) : null] })) : null, isCurrent ? (currentPlanShowsManageAction ? (_jsx("button", { className: `${styles.callToAction} ${styles.currentCta}`, "data-testid": `plan-manage-${plan.planId}`, onClick: (event) => {
180
233
  event.stopPropagation();
181
234
  if (!canManageCurrentPlan)
182
235
  return;
183
236
  void onSelectPlan({ planId: plan.planId, planVersionId, interval: defaultInterval || 'month', pricingType: defaultPrice?.pricingType || 'stripe', isCurrentPlan: true });
184
- }, disabled: !planVersionId || !canManageCurrentPlan, children: isAuthenticated
237
+ }, disabled: !planVersionId || !canManageCurrentPlan || !planSelectionEnabled, children: isAuthenticated
185
238
  ? (canManageCurrentPlan ? 'Manage Plan' : 'Current Plan')
186
- : 'Current Plan' })) : (_jsxs("div", { className: styles.ctaRow, children: [_jsx("button", { className: `${styles.callToAction} ${styles.secondaryCta}`, "data-testid": `plan-select-${plan.planId}-month`, onClick: (event) => {
239
+ : 'Current Plan' })) : (currentPlanShowsIndicator ? (_jsx("button", { className: `${styles.callToAction} ${styles.currentCta}`, "data-testid": `plan-current-${plan.planId}`, disabled: true, children: "Current Plan" })) : null)) : (_jsxs("div", { className: styles.ctaRow, children: [hasMonth ? (_jsx("button", { className: `${styles.callToAction} ${styles.secondaryCta}`, "data-testid": `plan-select-${plan.planId}-month`, onClick: (event) => {
187
240
  event.stopPropagation();
188
241
  void onSelectPlan({ planId: plan.planId, planVersionId, interval: 'month', pricingType: monthPrice?.pricingType || 'stripe', isCurrentPlan: false });
189
- }, disabled: !planVersionId || !hasMonth, children: monthPrice?.pricingType === 'free'
242
+ }, disabled: !planVersionId || !planSelectionEnabled, children: monthPrice?.pricingType === 'free'
190
243
  ? (isAuthenticated ? 'Select Free' : 'Get Started Free')
191
- : (isAuthenticated ? 'Choose Monthly' : 'Get Started Monthly') }), _jsx("button", { className: `${styles.callToAction} ${styles.primaryCta}`, "data-testid": `plan-select-${plan.planId}-year`, onClick: (event) => {
244
+ : (isAuthenticated ? 'Choose Monthly' : 'Get Started Monthly') })) : null, hasYear ? (_jsx("button", { className: `${styles.callToAction} ${styles.primaryCta}`, "data-testid": `plan-select-${plan.planId}-year`, onClick: (event) => {
192
245
  event.stopPropagation();
193
246
  void onSelectPlan({ planId: plan.planId, planVersionId, interval: 'year', pricingType: yearPrice?.pricingType || 'stripe', isCurrentPlan: false });
194
- }, disabled: !planVersionId || !hasYear, children: yearPrice?.pricingType === 'free'
247
+ }, disabled: !planVersionId || !planSelectionEnabled, children: yearPrice?.pricingType === 'free'
195
248
  ? (isAuthenticated ? 'Select Free' : 'Get Started Free')
196
- : (isAuthenticated ? 'Choose Yearly' : 'Get Started Yearly') })] }))] }, plan.planId));
249
+ : (isAuthenticated ? 'Choose Yearly' : 'Get Started Yearly') })) : null] }))] }, plan.planId));
197
250
  }) }))] }) }) }));
198
251
  }