@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
@@ -22,29 +22,29 @@ function openCore(projectRoot) {
22
22
  storagePath: path.join(projectRoot, '.taskforce'),
23
23
  tenantId: path.basename(projectRoot)
24
24
  });
25
- core.upsertPlanCatalog({ planId: 'starter', displayName: 'Starter', status: 'active' });
25
+ core.upsertPlanCatalog({ planId: 'free', displayName: 'Free', status: 'active' });
26
26
  core.updatePlanVersion({
27
- planId: 'starter',
27
+ planId: 'free',
28
28
  versionNumber: 1,
29
29
  seatLimit: 1
30
30
  });
31
31
  core.updatePlanVersionFeatures({
32
- planVersionId: 'starter-v1',
32
+ planVersionId: 'free-v1',
33
33
  features: [
34
- { featureKey: 'workspace.switching', access: 'enabled' }
34
+ { featureKey: 'workspace.workspaces', access: 'enabled' }
35
35
  ]
36
36
  });
37
- insertFreePriceMapping(core, 'starter-v1');
37
+ insertFreePriceMapping(core, 'free-v1');
38
38
  return core;
39
39
  }
40
40
  describe('TaskforceCore onboarding policy', () => {
41
- it('defaults to auto_grant_free when a free personal plan version is available', () => {
41
+ it('fails closed to require_plan_selection when no explicit onboarding policy is configured', () => {
42
42
  const projectRoot = makeProjectRoot();
43
43
  const core = openCore(projectRoot);
44
44
  try {
45
45
  expect(core.getOnboardingPolicy()).toEqual({
46
- mode: 'auto_grant_free',
47
- planVersionId: 'starter-v1'
46
+ mode: 'require_plan_selection',
47
+ planId: null
48
48
  });
49
49
  }
50
50
  finally {
@@ -59,22 +59,22 @@ describe('TaskforceCore onboarding policy', () => {
59
59
  storagePath: path.join(projectRoot, '.taskforce'),
60
60
  tenantId: path.basename(projectRoot)
61
61
  });
62
- core.upsertPlanCatalog({ planId: 'starter', displayName: 'Starter', status: 'active' });
62
+ core.upsertPlanCatalog({ planId: 'free', displayName: 'Free', status: 'active' });
63
63
  core.updatePlanVersion({
64
- planId: 'starter',
64
+ planId: 'free',
65
65
  versionNumber: 1,
66
66
  seatLimit: 1
67
67
  });
68
68
  core.updatePlanVersionFeatures({
69
- planVersionId: 'starter-v1',
69
+ planVersionId: 'free-v1',
70
70
  features: [
71
- { featureKey: 'workspace.switching', access: 'enabled' }
71
+ { featureKey: 'workspace.workspaces', access: 'enabled' }
72
72
  ]
73
73
  });
74
74
  try {
75
75
  expect(core.getOnboardingPolicy()).toEqual({
76
76
  mode: 'require_plan_selection',
77
- planVersionId: null
77
+ planId: null
78
78
  });
79
79
  }
80
80
  finally {
@@ -82,16 +82,16 @@ describe('TaskforceCore onboarding policy', () => {
82
82
  fs.rmSync(projectRoot, { recursive: true, force: true });
83
83
  }
84
84
  });
85
- it('fails validation for auto_grant_free without a plan version id', () => {
85
+ it('fails validation for auto_grant_free without a plan id', () => {
86
86
  const projectRoot = makeProjectRoot();
87
87
  const core = openCore(projectRoot);
88
88
  try {
89
89
  expect(core.validateOnboardingPolicy({
90
90
  mode: 'auto_grant_free',
91
- planVersionId: null
91
+ planId: null
92
92
  })).toEqual(expect.objectContaining({
93
93
  valid: false,
94
- code: 'ONBOARDING_PLAN_VERSION_REQUIRED'
94
+ code: 'ONBOARDING_PLAN_REQUIRED'
95
95
  }));
96
96
  }
97
97
  finally {
@@ -99,16 +99,16 @@ describe('TaskforceCore onboarding policy', () => {
99
99
  fs.rmSync(projectRoot, { recursive: true, force: true });
100
100
  }
101
101
  });
102
- it('fails validation when the configured version does not exist', () => {
102
+ it('fails validation when the configured plan does not exist', () => {
103
103
  const projectRoot = makeProjectRoot();
104
104
  const core = openCore(projectRoot);
105
105
  try {
106
106
  expect(core.validateOnboardingPolicy({
107
107
  mode: 'auto_grant_free',
108
- planVersionId: 'missing-v9'
108
+ planId: 'missing'
109
109
  })).toEqual(expect.objectContaining({
110
110
  valid: false,
111
- code: 'ONBOARDING_PLAN_VERSION_NOT_FOUND'
111
+ code: 'ONBOARDING_PLAN_NOT_FOUND'
112
112
  }));
113
113
  }
114
114
  finally {
@@ -116,14 +116,14 @@ describe('TaskforceCore onboarding policy', () => {
116
116
  fs.rmSync(projectRoot, { recursive: true, force: true });
117
117
  }
118
118
  });
119
- it('fails validation when the configured version belongs to a disabled plan', () => {
119
+ it('fails validation when the configured plan is disabled', () => {
120
120
  const projectRoot = makeProjectRoot();
121
121
  const core = openCore(projectRoot);
122
122
  try {
123
- core.upsertPlanCatalog({ planId: 'starter', displayName: 'Starter', status: 'archived' });
123
+ core.upsertPlanCatalog({ planId: 'free', displayName: 'Free', status: 'archived' });
124
124
  expect(core.validateOnboardingPolicy({
125
125
  mode: 'auto_grant_free',
126
- planVersionId: 'starter-v1'
126
+ planId: 'free'
127
127
  })).toEqual(expect.objectContaining({
128
128
  valid: false,
129
129
  code: 'ONBOARDING_PLAN_NOT_ENABLED'
@@ -134,14 +134,14 @@ describe('TaskforceCore onboarding policy', () => {
134
134
  fs.rmSync(projectRoot, { recursive: true, force: true });
135
135
  }
136
136
  });
137
- it('fails validation when auto_grant_free points at a version without free pricing', () => {
137
+ it('fails validation when auto_grant_free points at a plan whose default version lacks free pricing', () => {
138
138
  const projectRoot = makeProjectRoot();
139
139
  const core = openCore(projectRoot);
140
140
  try {
141
- core.upsertPlanCatalog({ planId: 'collab', displayName: 'Collab', status: 'active' });
141
+ core.upsertPlanCatalog({ planId: 'team-5', displayName: 'Team 5', status: 'active' });
142
142
  expect(core.validateOnboardingPolicy({
143
143
  mode: 'auto_grant_free',
144
- planVersionId: 'collab-v1'
144
+ planId: 'team-5'
145
145
  })).toEqual(expect.objectContaining({
146
146
  valid: false,
147
147
  code: 'ONBOARDING_PLAN_MUST_BE_FREE'
@@ -152,13 +152,13 @@ describe('TaskforceCore onboarding policy', () => {
152
152
  fs.rmSync(projectRoot, { recursive: true, force: true });
153
153
  }
154
154
  });
155
- it('passes validation for require_plan_selection without a plan version id', () => {
155
+ it('passes validation for require_plan_selection without a plan id', () => {
156
156
  const projectRoot = makeProjectRoot();
157
157
  const core = openCore(projectRoot);
158
158
  try {
159
159
  expect(core.validateOnboardingPolicy({
160
160
  mode: 'require_plan_selection',
161
- planVersionId: null
161
+ planId: null
162
162
  })).toEqual({ valid: true });
163
163
  }
164
164
  finally {
@@ -166,6 +166,31 @@ describe('TaskforceCore onboarding policy', () => {
166
166
  fs.rmSync(projectRoot, { recursive: true, force: true });
167
167
  }
168
168
  });
169
+ it('requires plan selection during registration when no explicit onboarding policy is configured', () => {
170
+ const projectRoot = makeProjectRoot();
171
+ const core = openCore(projectRoot);
172
+ try {
173
+ const created = core.registerPasswordUser({
174
+ email: 'implicit-onboarding@example.com',
175
+ password: 'ValidPassphrase123!'
176
+ });
177
+ expect(created.planSelectionRequired).toBe(true);
178
+ expect(created.checkoutPending).toBe(false);
179
+ expect(core.getAccountEntitlement(created.userId)).toEqual(expect.objectContaining({
180
+ state: 'pending_plan_selection'
181
+ }));
182
+ expect(core.getAccountAccessStatus(created.userId)).toEqual(expect.objectContaining({
183
+ gate: 'plan_selection_required',
184
+ canAccessApp: false,
185
+ canAccessPlans: true,
186
+ planSelectionRequired: true
187
+ }));
188
+ }
189
+ finally {
190
+ core.close();
191
+ fs.rmSync(projectRoot, { recursive: true, force: true });
192
+ }
193
+ });
169
194
  it('allows backfill transitions to create pending plan selection entitlements', () => {
170
195
  const projectRoot = makeProjectRoot();
171
196
  const tenantId = path.basename(projectRoot);
@@ -176,22 +201,22 @@ describe('TaskforceCore onboarding policy', () => {
176
201
  billing: {
177
202
  onboardingPolicy: {
178
203
  mode: 'require_plan_selection',
179
- planVersionId: null
204
+ planId: null
180
205
  }
181
206
  }
182
207
  });
183
208
  const db = core.getDatabaseAdapter();
184
209
  db.prepare(`
185
- INSERT INTO users (id, tenant_id, email, display_name, auth_provider, external_auth_id, is_disabled, created_at, updated_at)
186
- VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?)
187
- `).run('pending-u1', tenantId, 'pending@example.com', 'Pending', 'token', 'pending-u1', now, now);
210
+ INSERT INTO users (id, tenant_id, email, display_name, is_disabled, created_at, updated_at)
211
+ VALUES (?, ?, ?, ?, 0, ?, ?)
212
+ `).run('pending-u1', tenantId, 'pending@example.com', 'Pending', now, now);
188
213
  db.prepare(`
189
214
  INSERT INTO user_billing (user_id, tenant_id, entitlement_state, created_at, updated_at)
190
215
  VALUES (?, ?, ?, ?, ?)
191
216
  `).run('pending-u1', tenantId, 'pending_plan_selection', now, now);
192
217
  core.transitionCanonicalCommercialState({
193
218
  accountId: 'pending-u1',
194
- planVersionId: 'starter-v1',
219
+ planVersionId: 'free-v1',
195
220
  state: 'pending_plan_selection',
196
221
  source: 'backfill',
197
222
  updatedBy: 'backfill',
@@ -220,19 +245,19 @@ describe('TaskforceCore onboarding policy', () => {
220
245
  try {
221
246
  const now = new Date().toISOString();
222
247
  const checkoutExpiresAt = new Date(Date.now() + (60 * 60 * 1000)).toISOString();
223
- core.upsertPlanCatalog({ planId: 'pro', displayName: 'Pro', status: 'active' });
248
+ core.upsertPlanCatalog({ planId: 'basic', displayName: 'Basic', status: 'active' });
224
249
  const db = core.getDatabaseAdapter();
225
250
  db.prepare(`
226
- INSERT INTO users (id, tenant_id, email, display_name, auth_provider, external_auth_id, is_disabled, created_at, updated_at)
227
- VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?)
228
- `).run('checkout-u1', tenantId, 'checkout@example.com', 'Checkout', 'token', 'checkout-u1', now, now);
251
+ INSERT INTO users (id, tenant_id, email, display_name, is_disabled, created_at, updated_at)
252
+ VALUES (?, ?, ?, ?, 0, ?, ?)
253
+ `).run('checkout-u1', tenantId, 'checkout@example.com', 'Checkout', now, now);
229
254
  core.transitionCanonicalCommercialState({
230
255
  accountId: 'checkout-u1',
231
- planVersionId: 'pro-v1',
256
+ planVersionId: 'basic-v1',
232
257
  state: 'checkout_pending',
233
258
  source: 'checkout-start',
234
259
  updatedBy: 'checkout-start',
235
- billingMirrorPlanId: 'pro',
260
+ billingMirrorPlanId: 'basic',
236
261
  effectiveAt: now,
237
262
  effectiveUntil: checkoutExpiresAt,
238
263
  createdAt: now,
@@ -258,19 +283,19 @@ describe('TaskforceCore onboarding policy', () => {
258
283
  try {
259
284
  const now = new Date().toISOString();
260
285
  const expiredAt = new Date(Date.now() - (60 * 1000)).toISOString();
261
- core.upsertPlanCatalog({ planId: 'pro', displayName: 'Pro', status: 'active' });
286
+ core.upsertPlanCatalog({ planId: 'basic', displayName: 'Basic', status: 'active' });
262
287
  const db = core.getDatabaseAdapter();
263
288
  db.prepare(`
264
- INSERT INTO users (id, tenant_id, email, display_name, auth_provider, external_auth_id, is_disabled, created_at, updated_at)
265
- VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?)
266
- `).run('expired-checkout-u1', tenantId, 'expired@example.com', 'Expired Checkout', 'token', 'expired-checkout-u1', now, now);
289
+ INSERT INTO users (id, tenant_id, email, display_name, is_disabled, created_at, updated_at)
290
+ VALUES (?, ?, ?, ?, 0, ?, ?)
291
+ `).run('expired-checkout-u1', tenantId, 'expired@example.com', 'Expired Checkout', now, now);
267
292
  core.transitionCanonicalCommercialState({
268
293
  accountId: 'expired-checkout-u1',
269
- planVersionId: 'pro-v1',
294
+ planVersionId: 'basic-v1',
270
295
  state: 'checkout_pending',
271
296
  source: 'checkout-start',
272
297
  updatedBy: 'checkout-start',
273
- billingMirrorPlanId: 'pro',
298
+ billingMirrorPlanId: 'basic',
274
299
  effectiveAt: now,
275
300
  effectiveUntil: expiredAt,
276
301
  createdAt: now,
@@ -315,4 +340,176 @@ describe('TaskforceCore onboarding policy', () => {
315
340
  fs.rmSync(projectRoot, { recursive: true, force: true });
316
341
  }
317
342
  });
343
+ it('treats an expired trialing entitlement as requiring plan reselection', () => {
344
+ const projectRoot = makeProjectRoot();
345
+ const tenantId = path.basename(projectRoot);
346
+ const core = openCore(projectRoot);
347
+ try {
348
+ const now = new Date().toISOString();
349
+ const expiredAt = new Date(Date.now() - (60 * 1000)).toISOString();
350
+ core.upsertPlanCatalog({ planId: 'basic', displayName: 'Basic', status: 'active' });
351
+ const db = core.getDatabaseAdapter();
352
+ db.prepare(`
353
+ INSERT INTO users (id, tenant_id, email, display_name, is_disabled, created_at, updated_at)
354
+ VALUES (?, ?, ?, ?, 0, ?, ?)
355
+ `).run('expired-trial-u1', tenantId, 'expired-trial@example.com', 'Expired Trial', now, now);
356
+ core.transitionCanonicalCommercialState({
357
+ accountId: 'expired-trial-u1',
358
+ planVersionId: 'basic-v1',
359
+ state: 'trialing',
360
+ source: 'system',
361
+ updatedBy: 'trial-start',
362
+ billingMirrorPlanId: 'basic',
363
+ effectiveAt: now,
364
+ effectiveUntil: expiredAt,
365
+ createdAt: now,
366
+ updatedAt: now
367
+ });
368
+ expect(core.getAccountAccessStatus('expired-trial-u1')).toEqual(expect.objectContaining({
369
+ gate: 'plan_selection_required',
370
+ canAccessApp: false,
371
+ canAccessPlans: true,
372
+ planSelectionRequired: true,
373
+ message: 'Choose a plan to continue.'
374
+ }));
375
+ expect(core.getAccountEntitlement('expired-trial-u1')).toEqual(expect.objectContaining({
376
+ state: 'pending_plan_selection',
377
+ effectiveUntil: null
378
+ }));
379
+ const entitlementRow = db.prepare(`
380
+ SELECT entitlement_state, effective_until, updated_by
381
+ FROM account_entitlements
382
+ WHERE tenant_id = ? AND account_id = ?
383
+ LIMIT 1
384
+ `).get(tenantId, 'expired-trial-u1');
385
+ expect(entitlementRow).toEqual(expect.objectContaining({
386
+ entitlement_state: 'pending_plan_selection',
387
+ effective_until: null,
388
+ updated_by: 'trial-expired'
389
+ }));
390
+ const billingRow = db.prepare(`
391
+ SELECT entitlement_state, tier, effective_until
392
+ FROM user_billing
393
+ WHERE tenant_id = ? AND user_id = ?
394
+ LIMIT 1
395
+ `).get(tenantId, 'expired-trial-u1');
396
+ expect(billingRow).toEqual(expect.objectContaining({
397
+ entitlement_state: 'pending_plan_selection',
398
+ tier: '',
399
+ effective_until: null
400
+ }));
401
+ }
402
+ finally {
403
+ core.close();
404
+ fs.rmSync(projectRoot, { recursive: true, force: true });
405
+ }
406
+ });
407
+ it('auto-heals a pending-plan-selection entitlement from an active Stripe billing mirror', () => {
408
+ const projectRoot = makeProjectRoot();
409
+ const tenantId = path.basename(projectRoot);
410
+ const core = openCore(projectRoot);
411
+ try {
412
+ const now = new Date().toISOString();
413
+ const nextRenewal = new Date(Date.now() + (24 * 60 * 60 * 1000)).toISOString();
414
+ core.upsertPlanCatalog({ planId: 'team-5', displayName: 'Team 5', status: 'active' });
415
+ const db = core.getDatabaseAdapter();
416
+ db.prepare(`
417
+ INSERT INTO users (id, tenant_id, email, display_name, is_disabled, created_at, updated_at)
418
+ VALUES (?, ?, ?, ?, 0, ?, ?)
419
+ `).run('mirror-heal-u1', tenantId, 'mirror-heal@example.com', 'Mirror Heal', now, now);
420
+ core.transitionCanonicalCommercialState({
421
+ accountId: 'mirror-heal-u1',
422
+ planVersionId: 'free-v1',
423
+ state: 'pending_plan_selection',
424
+ source: 'backfill',
425
+ updatedBy: 'backfill',
426
+ billingMirrorPlanId: null,
427
+ effectiveAt: now,
428
+ createdAt: now,
429
+ updatedAt: now
430
+ });
431
+ db.prepare(`
432
+ UPDATE user_billing
433
+ SET stripe_customer_id = ?, stripe_subscription_id = ?, entitlement_state = ?, tier = ?, effective_until = ?, updated_at = ?
434
+ WHERE tenant_id = ? AND user_id = ?
435
+ `).run('cus_heal', 'sub_heal', 'active', 'team-5', nextRenewal, now, tenantId, 'mirror-heal-u1');
436
+ expect(core.getAccountAccessStatus('mirror-heal-u1')).toEqual(expect.objectContaining({
437
+ gate: 'ok',
438
+ canAccessApp: true,
439
+ planSelectionRequired: false
440
+ }));
441
+ expect(core.getAccountEntitlement('mirror-heal-u1')).toEqual(expect.objectContaining({
442
+ state: 'active',
443
+ planId: 'team-5',
444
+ planVersionId: 'team-5-v1',
445
+ effectiveUntil: nextRenewal
446
+ }));
447
+ const entitlementRow = db.prepare(`
448
+ SELECT entitlement_state, plan_version_id, effective_until, updated_by
449
+ FROM account_entitlements
450
+ WHERE tenant_id = ? AND account_id = ?
451
+ LIMIT 1
452
+ `).get(tenantId, 'mirror-heal-u1');
453
+ expect(entitlementRow).toEqual(expect.objectContaining({
454
+ entitlement_state: 'active',
455
+ plan_version_id: 'team-5-v1',
456
+ effective_until: nextRenewal,
457
+ updated_by: 'billing-mirror-auto-heal'
458
+ }));
459
+ }
460
+ finally {
461
+ core.close();
462
+ fs.rmSync(projectRoot, { recursive: true, force: true });
463
+ }
464
+ });
465
+ it('auto-heals a pending-plan-selection entitlement from Stripe status and price mapping when mirror state is stale', () => {
466
+ const projectRoot = makeProjectRoot();
467
+ const tenantId = path.basename(projectRoot);
468
+ const core = openCore(projectRoot);
469
+ try {
470
+ const now = new Date().toISOString();
471
+ const nextRenewal = new Date(Date.now() + (24 * 60 * 60 * 1000)).toISOString();
472
+ core.upsertPlanCatalog({ planId: 'team-5', displayName: 'Team 5', status: 'active' });
473
+ const db = core.getDatabaseAdapter();
474
+ db.prepare(`
475
+ INSERT INTO users (id, tenant_id, email, display_name, is_disabled, created_at, updated_at)
476
+ VALUES (?, ?, ?, ?, 0, ?, ?)
477
+ `).run('mirror-price-heal-u1', tenantId, 'mirror-price-heal@example.com', 'Mirror Price Heal', now, now);
478
+ core.transitionCanonicalCommercialState({
479
+ accountId: 'mirror-price-heal-u1',
480
+ planVersionId: 'free-v1',
481
+ state: 'pending_plan_selection',
482
+ source: 'backfill',
483
+ updatedBy: 'checkout-expired',
484
+ billingMirrorPlanId: null,
485
+ effectiveAt: now,
486
+ createdAt: now,
487
+ updatedAt: now
488
+ });
489
+ db.prepare(`
490
+ UPDATE user_billing
491
+ SET stripe_customer_id = ?, stripe_subscription_id = ?, stripe_price_id = ?, stripe_status = ?, entitlement_state = ?, tier = ?, effective_until = ?, updated_at = ?
492
+ WHERE tenant_id = ? AND user_id = ?
493
+ `).run('cus_heal_2', 'sub_heal_2', 'price_team_5_month', 'active', 'pending_plan_selection', 'team-5', nextRenewal, now, tenantId, 'mirror-price-heal-u1');
494
+ db.prepare(`
495
+ INSERT INTO billing_price_mappings (tenant_id, plan_version_id, billing_interval, stripe_price_id, active, created_at, updated_at)
496
+ VALUES (?, ?, ?, ?, 1, ?, ?)
497
+ `).run(tenantId, 'team-5-v1', 'month', 'price_team_5_month', now, now);
498
+ expect(core.getAccountAccessStatus('mirror-price-heal-u1')).toEqual(expect.objectContaining({
499
+ gate: 'ok',
500
+ canAccessApp: true,
501
+ planSelectionRequired: false
502
+ }));
503
+ expect(core.getAccountEntitlement('mirror-price-heal-u1')).toEqual(expect.objectContaining({
504
+ state: 'active',
505
+ planId: 'team-5',
506
+ planVersionId: 'team-5-v1',
507
+ effectiveUntil: nextRenewal
508
+ }));
509
+ }
510
+ finally {
511
+ core.close();
512
+ fs.rmSync(projectRoot, { recursive: true, force: true });
513
+ }
514
+ });
318
515
  });
@@ -1,4 +1,5 @@
1
1
  import { DEFAULT_TYPE_VALUE } from '../shared/taxonomyDefaults.js';
2
+ import { buildTaskSyncChangeKey } from '../sync/taskSyncChangeKey.js';
2
3
  export function listTasksForSyncService(deps, workspaceId, cursor, limit) {
3
4
  const normalizedWorkspaceId = deps.normalizeWorkspaceId(workspaceId);
4
5
  const safeLimit = Math.max(1, Math.min(10000, Math.floor(limit)));
@@ -67,72 +68,8 @@ export function listWorkspaceSyncDeletesSinceService(deps, workspaceId, cursor,
67
68
  }))
68
69
  .filter((row) => row.taskId.length > 0 && Number.isFinite(Date.parse(row.deletedAt)));
69
70
  }
70
- function normalizeConflictTaxonomies(value) {
71
- if (!value || typeof value !== 'object')
72
- return {};
73
- const source = value;
74
- return Object.keys(source).sort().reduce((acc, key) => {
75
- const raw = source[key];
76
- if (Array.isArray(raw)) {
77
- acc[key] = raw.map((entry) => String(entry ?? '')).sort();
78
- }
79
- else if (raw === null || raw === undefined) {
80
- acc[key] = '';
81
- }
82
- else {
83
- acc[key] = String(raw);
84
- }
85
- return acc;
86
- }, {});
87
- }
88
- function normalizeConflictChecklistItems(value) {
89
- if (!Array.isArray(value))
90
- return [];
91
- return value
92
- .map((item) => {
93
- const entry = item && typeof item === 'object' ? item : {};
94
- return {
95
- id: String(entry.id || '').trim(),
96
- title: String(entry.title || '').trim(),
97
- isCompleted: Boolean(entry.isCompleted),
98
- order: entry.order === undefined || entry.order === null ? '' : String(entry.order),
99
- };
100
- })
101
- .sort((left, right) => {
102
- const leftOrder = Number.parseInt(left.order, 10);
103
- const rightOrder = Number.parseInt(right.order, 10);
104
- if (Number.isFinite(leftOrder) && Number.isFinite(rightOrder) && leftOrder !== rightOrder) {
105
- return leftOrder - rightOrder;
106
- }
107
- if (left.title !== right.title)
108
- return left.title.localeCompare(right.title);
109
- return left.id.localeCompare(right.id);
110
- });
111
- }
112
71
  function buildSyncConflictKey(task, archivedValue) {
113
- const source = task || {};
114
- return JSON.stringify({
115
- id: String(source.id || ''),
116
- title: String(source.title || ''),
117
- description: source.description === null || source.description === undefined ? '' : String(source.description),
118
- category: String(source.category || ''),
119
- type: String(source.type || ''),
120
- priority: source.priority === undefined || source.priority === null ? '' : String(source.priority),
121
- complexity: source.complexity === undefined || source.complexity === null ? '' : String(source.complexity),
122
- status: String(source.status || ''),
123
- approach: source.approach === undefined || source.approach === null ? '' : String(source.approach),
124
- canceledReason: source.canceledReason === undefined || source.canceledReason === null ? '' : String(source.canceledReason),
125
- scheduledDate: source.scheduledDate === undefined || source.scheduledDate === null ? '' : String(source.scheduledDate),
126
- dueDate: source.dueDate === undefined || source.dueDate === null ? '' : String(source.dueDate),
127
- scheduledWeekKey: source.scheduledWeekKey === undefined || source.scheduledWeekKey === null ? '' : String(source.scheduledWeekKey),
128
- orderInDay: source.orderInDay === undefined || source.orderInDay === null ? '' : String(source.orderInDay),
129
- workstreamId: source.workstreamId === undefined || source.workstreamId === null ? '' : String(source.workstreamId),
130
- referenceNumber: source.referenceNumber === undefined || source.referenceNumber === null ? '' : String(source.referenceNumber),
131
- assignee: source.assignee === undefined || source.assignee === null ? '' : String(source.assignee),
132
- taxonomies: normalizeConflictTaxonomies(source.taxonomies),
133
- checklistItems: normalizeConflictChecklistItems(source.checklistItems),
134
- isArchived: archivedValue ? '1' : '0'
135
- });
72
+ return buildTaskSyncChangeKey(task, archivedValue);
136
73
  }
137
74
  export function applyWorkspaceSyncSnapshotService(deps, input, workspaceId) {
138
75
  const normalizedWorkspaceId = deps.normalizeWorkspaceId(workspaceId);