@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
@@ -1,5 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { buildWorkspaceSyncPayloadModel, buildWorkspaceSyncSignatureModel } from './workspaceSyncModel';
3
+ import { aiProfileSyncResourceAdapter, taskSyncResourceAdapter } from './syncResourceAdapters';
3
4
  describe('workspaceSyncModel', () => {
4
5
  describe('buildWorkspaceSyncPayloadModel', () => {
5
6
  it('emits document-delete when a previously pushed document path is now missing', () => {
@@ -20,6 +21,28 @@ describe('workspaceSyncModel', () => {
20
21
  expect.objectContaining({ op: 'document-delete', path: 'docs/remove.md' })
21
22
  ]));
22
23
  });
24
+ it('does not emit document-delete when a canonical path is still known but missing from the readable snapshot', () => {
25
+ const payload = buildWorkspaceSyncPayloadModel({
26
+ tasks: [],
27
+ archivedTasks: [],
28
+ lastPushedTaskIds: new Set(),
29
+ pendingDeletedTaskIds: new Set(),
30
+ documents: [
31
+ { path: 'docs/keep.md', updatedAt: '2026-02-01T00:00:00.000Z', content: 'keep' }
32
+ ],
33
+ currentDocumentPaths: new Set(['docs/keep.md', 'docs/missing-content.md']),
34
+ lastPushedDocumentPaths: new Set(['docs/keep.md', 'docs/missing-content.md', 'docs/remove.md'])
35
+ });
36
+ expect(Array.from(payload.currentDocumentPaths).sort()).toEqual(['docs/keep.md', 'docs/missing-content.md']);
37
+ expect(Array.from(payload.deleteDocumentPaths).sort()).toEqual(['docs/remove.md']);
38
+ expect(payload.changes).toEqual(expect.arrayContaining([
39
+ expect.objectContaining({ op: 'document-upsert', path: 'docs/keep.md' }),
40
+ expect.objectContaining({ op: 'document-delete', path: 'docs/remove.md' })
41
+ ]));
42
+ expect(payload.changes).not.toEqual(expect.arrayContaining([
43
+ expect.objectContaining({ op: 'document-delete', path: 'docs/missing-content.md' })
44
+ ]));
45
+ });
23
46
  it('emits task-event-upsert changes for new activity events and tracks event watermarks', () => {
24
47
  const payload = buildWorkspaceSyncPayloadModel({
25
48
  tasks: [],
@@ -124,6 +147,32 @@ describe('workspaceSyncModel', () => {
124
147
  ['docs/new.md', '2026-02-03T00:00:00.000Z']
125
148
  ]);
126
149
  });
150
+ it('reports send-all diagnostics when a domain has no baseline', () => {
151
+ const payload = buildWorkspaceSyncPayloadModel({
152
+ tasks: [],
153
+ archivedTasks: [],
154
+ lastPushedTaskIds: new Set(),
155
+ pendingDeletedTaskIds: new Set(),
156
+ documents: [
157
+ { path: 'docs/a.md', updatedAt: '2026-02-01T00:00:00.000Z', content: 'a' },
158
+ { path: 'docs/b.md', updatedAt: '2026-02-02T00:00:00.000Z', content: 'b' }
159
+ ],
160
+ baselineResetReason: 'repair-reset'
161
+ });
162
+ expect(payload.deltaDiagnostics).toEqual(expect.objectContaining({
163
+ totalChanges: 2,
164
+ domains: expect.objectContaining({
165
+ documents: expect.objectContaining({
166
+ mode: 'send-all',
167
+ reason: 'repair-reset',
168
+ currentCount: 2,
169
+ baselineCount: 0,
170
+ changeCount: 2
171
+ })
172
+ })
173
+ }));
174
+ expect(payload.deltaDiagnostics?.byOp['document-upsert']).toBe(2);
175
+ });
127
176
  it('preserves canonical document metadata on document-upsert changes', () => {
128
177
  const payload = buildWorkspaceSyncPayloadModel({
129
178
  tasks: [],
@@ -196,6 +245,81 @@ describe('workspaceSyncModel', () => {
196
245
  })
197
246
  ]));
198
247
  });
248
+ it('does not emit asset-delete when a canonical asset path is still known but missing from the readable snapshot', () => {
249
+ const payload = buildWorkspaceSyncPayloadModel({
250
+ tasks: [],
251
+ archivedTasks: [],
252
+ lastPushedTaskIds: new Set(),
253
+ pendingDeletedTaskIds: new Set(),
254
+ assets: [
255
+ {
256
+ path: 'workspaces/default/assets/images/asset-image-1-diagram.png',
257
+ updatedAt: '2026-03-17T12:00:00.000Z',
258
+ contentBase64: 'aW1hZ2UtYnl0ZXM=',
259
+ assetId: 'asset-image-1',
260
+ kind: 'image',
261
+ mimeType: 'image/png',
262
+ taskId: 'task-1',
263
+ originalFilename: 'diagram.png',
264
+ linkRole: 'image'
265
+ }
266
+ ],
267
+ currentAssetPaths: new Set([
268
+ 'workspaces/default/assets/images/asset-image-1-diagram.png',
269
+ 'workspaces/default/assets/files/asset-file-missing.pdf'
270
+ ]),
271
+ lastPushedAssetPaths: new Set([
272
+ 'workspaces/default/assets/images/asset-image-1-diagram.png',
273
+ 'workspaces/default/assets/files/asset-file-missing.pdf',
274
+ 'workspaces/default/assets/files/asset-file-old-document.pdf'
275
+ ])
276
+ });
277
+ expect(Array.from(payload.currentAssetPaths).sort()).toEqual([
278
+ 'workspaces/default/assets/files/asset-file-missing.pdf',
279
+ 'workspaces/default/assets/images/asset-image-1-diagram.png'
280
+ ]);
281
+ expect(Array.from(payload.deleteAssetPaths)).toEqual(['workspaces/default/assets/files/asset-file-old-document.pdf']);
282
+ expect(payload.changes).not.toEqual(expect.arrayContaining([
283
+ expect.objectContaining({
284
+ op: 'asset-delete',
285
+ path: 'workspaces/default/assets/files/asset-file-missing.pdf'
286
+ })
287
+ ]));
288
+ });
289
+ it('emits timestamped task deletes for previously pushed tasks that are now missing', () => {
290
+ const payload = buildWorkspaceSyncPayloadModel({
291
+ tasks: [],
292
+ archivedTasks: [],
293
+ lastPushedTaskIds: new Set(['task-delete-1']),
294
+ pendingDeletedTaskIds: new Set()
295
+ });
296
+ expect(payload.deleteTaskIds).toEqual(new Set(['task-delete-1']));
297
+ expect(payload.changes).toEqual(expect.arrayContaining([
298
+ expect.objectContaining({
299
+ op: 'delete',
300
+ taskId: 'task-delete-1',
301
+ deletedAt: expect.any(String)
302
+ })
303
+ ]));
304
+ });
305
+ it('preserves stored task delete watermarks on pending delete changes', () => {
306
+ const payload = buildWorkspaceSyncPayloadModel({
307
+ tasks: [],
308
+ archivedTasks: [],
309
+ lastPushedTaskIds: new Set(),
310
+ pendingDeletedTaskIds: new Set(['task-delete-2']),
311
+ pendingDeletedTaskWatermarks: new Map([
312
+ ['task-delete-2', '2026-04-10T11:00:00.000Z']
313
+ ])
314
+ });
315
+ expect(payload.changes).toEqual(expect.arrayContaining([
316
+ expect.objectContaining({
317
+ op: 'delete',
318
+ taskId: 'task-delete-2',
319
+ deletedAt: '2026-04-10T11:00:00.000Z'
320
+ })
321
+ ]));
322
+ });
199
323
  it('orders task upserts deterministically by task id', () => {
200
324
  const payload = buildWorkspaceSyncPayloadModel({
201
325
  tasks: [
@@ -243,6 +367,7 @@ describe('workspaceSyncModel', () => {
243
367
  provider: 'OpenAI',
244
368
  model: 'gpt-5.4',
245
369
  surfaceType: 'coding_tool',
370
+ rosterStateUpdatedAt: '2026-03-22T10:00:00.000Z',
246
371
  createdAt: '2026-03-22T10:00:00.000Z',
247
372
  updatedAt: '2026-03-22T10:05:00.000Z',
248
373
  lastActiveAt: '2026-03-22T10:05:00.000Z'
@@ -259,14 +384,49 @@ describe('workspaceSyncModel', () => {
259
384
  role: 'Implementer',
260
385
  provider: 'OpenAI',
261
386
  model: 'gpt-5.4',
262
- surfaceType: 'coding_tool'
387
+ surfaceType: 'coding_tool',
388
+ rosterStateUpdatedAt: '2026-03-22T10:00:00.000Z'
263
389
  })
264
390
  })
265
391
  ]));
266
392
  expect(Array.from(payload.currentAiProfileIds)).toEqual(['ai-profile-1']);
267
393
  expect(payload.currentAiProfileWatermarks.get('ai-profile-1')).toBe('2026-03-22T10:05:00.000Z');
268
394
  });
269
- it('filters unchanged ai profiles when ai watermark state already matches', () => {
395
+ it('filters unchanged ai profiles when ai change-key state already matches', () => {
396
+ const profile = {
397
+ id: 'ai-profile-1',
398
+ workspaceId: 'ws-1',
399
+ profileToken: 'tfp_test',
400
+ name: 'Codex',
401
+ username: 'codex',
402
+ icon: 'Bot',
403
+ color: '#0ea5e9',
404
+ description: 'Terminal-first coding agent',
405
+ role: 'Implementer',
406
+ provider: 'OpenAI',
407
+ model: 'gpt-5.4',
408
+ surfaceType: 'coding_tool',
409
+ rosterStateUpdatedAt: '2026-03-22T10:00:00.000Z',
410
+ createdAt: '2026-03-22T10:00:00.000Z',
411
+ updatedAt: '2026-03-22T10:05:00.000Z',
412
+ lastActiveAt: '2026-03-22T10:05:00.000Z'
413
+ };
414
+ const payload = buildWorkspaceSyncPayloadModel({
415
+ tasks: [],
416
+ archivedTasks: [],
417
+ lastPushedTaskIds: new Set(),
418
+ pendingDeletedTaskIds: new Set(),
419
+ aiProfiles: [profile],
420
+ lastPushedAiProfileIds: new Set(['ai-profile-1']),
421
+ lastPushedAiProfileWatermarks: new Map([['ai-profile-1', '2026-03-22T10:05:00.000Z']]),
422
+ lastPushedAiProfileChangeKeys: new Map([
423
+ ['ai-profile-1', aiProfileSyncResourceAdapter.getChangeKey(profile)]
424
+ ])
425
+ });
426
+ expect(payload.changes.some((change) => change.op === 'ai-profile-upsert')).toBe(false);
427
+ expect(Array.from(payload.currentAiProfileIds)).toEqual(['ai-profile-1']);
428
+ });
429
+ it('resends ai profiles when a legacy watermark did not include avatar fields', () => {
270
430
  const payload = buildWorkspaceSyncPayloadModel({
271
431
  tasks: [],
272
432
  archivedTasks: [],
@@ -281,11 +441,14 @@ describe('workspaceSyncModel', () => {
281
441
  username: 'codex',
282
442
  icon: 'Bot',
283
443
  color: '#0ea5e9',
444
+ avatarUrl: '/api/taskforce/context/workspaces%2Fws-1%2Fassets%2Fimages%2Fai-profiles%2Fai-profile-1%2Favatar%2Favatar.webp',
445
+ avatarSourceUrl: '/api/taskforce/context/workspaces%2Fws-1%2Fassets%2Fimages%2Fai-profiles%2Fai-profile-1%2Favatar%2Fsource.webp',
284
446
  description: 'Terminal-first coding agent',
285
447
  role: 'Implementer',
286
448
  provider: 'OpenAI',
287
449
  model: 'gpt-5.4',
288
450
  surfaceType: 'coding_tool',
451
+ rosterStateUpdatedAt: '2026-03-22T10:00:00.000Z',
289
452
  createdAt: '2026-03-22T10:00:00.000Z',
290
453
  updatedAt: '2026-03-22T10:05:00.000Z',
291
454
  lastActiveAt: '2026-03-22T10:05:00.000Z'
@@ -294,8 +457,82 @@ describe('workspaceSyncModel', () => {
294
457
  lastPushedAiProfileIds: new Set(['ai-profile-1']),
295
458
  lastPushedAiProfileWatermarks: new Map([['ai-profile-1', '2026-03-22T10:05:00.000Z']])
296
459
  });
297
- expect(payload.changes.some((change) => change.op === 'ai-profile-upsert')).toBe(false);
298
- expect(Array.from(payload.currentAiProfileIds)).toEqual(['ai-profile-1']);
460
+ expect(payload.changes).toEqual(expect.arrayContaining([
461
+ expect.objectContaining({
462
+ op: 'ai-profile-upsert',
463
+ profile: expect.objectContaining({
464
+ id: 'ai-profile-1',
465
+ avatarUrl: expect.stringContaining('/api/taskforce/context/'),
466
+ avatarSourceUrl: expect.stringContaining('/api/taskforce/context/')
467
+ })
468
+ })
469
+ ]));
470
+ expect(payload.currentAiProfileWatermarks.get('ai-profile-1')).toBe('2026-03-22T10:05:00.000Z');
471
+ expect(payload.currentAiProfileChangeKeys?.get('ai-profile-1')).toBe(aiProfileSyncResourceAdapter.getChangeKey({
472
+ id: 'ai-profile-1',
473
+ workspaceId: 'ws-1',
474
+ profileToken: 'tfp_test',
475
+ name: 'Codex',
476
+ username: 'codex',
477
+ icon: 'Bot',
478
+ color: '#0ea5e9',
479
+ avatarUrl: '/api/taskforce/context/workspaces%2Fws-1%2Fassets%2Fimages%2Fai-profiles%2Fai-profile-1%2Favatar%2Favatar.webp',
480
+ avatarSourceUrl: '/api/taskforce/context/workspaces%2Fws-1%2Fassets%2Fimages%2Fai-profiles%2Fai-profile-1%2Favatar%2Fsource.webp',
481
+ description: 'Terminal-first coding agent',
482
+ role: 'Implementer',
483
+ provider: 'OpenAI',
484
+ model: 'gpt-5.4',
485
+ surfaceType: 'coding_tool',
486
+ rosterStateUpdatedAt: '2026-03-22T10:00:00.000Z',
487
+ createdAt: '2026-03-22T10:00:00.000Z',
488
+ updatedAt: '2026-03-22T10:05:00.000Z',
489
+ lastActiveAt: '2026-03-22T10:05:00.000Z'
490
+ }));
491
+ });
492
+ it('resends ai profiles when lifecycle state advances beyond metadata updatedAt', () => {
493
+ const payload = buildWorkspaceSyncPayloadModel({
494
+ tasks: [],
495
+ archivedTasks: [],
496
+ lastPushedTaskIds: new Set(),
497
+ pendingDeletedTaskIds: new Set(),
498
+ aiProfiles: [
499
+ {
500
+ id: 'ai-profile-1',
501
+ workspaceId: 'ws-1',
502
+ profileToken: 'tfp_test',
503
+ name: 'Codex',
504
+ username: 'codex',
505
+ icon: 'Bot',
506
+ color: '#0ea5e9',
507
+ description: 'Terminal-first coding agent',
508
+ role: 'Implementer',
509
+ provider: 'OpenAI',
510
+ model: 'gpt-5.4',
511
+ surfaceType: 'coding_tool',
512
+ archivedAt: '2026-03-22T10:10:00.000Z',
513
+ archivedBy: 'owner-1',
514
+ archivedReason: 'manual_archive',
515
+ rosterStateUpdatedAt: '2026-03-22T10:10:00.000Z',
516
+ createdAt: '2026-03-22T10:00:00.000Z',
517
+ updatedAt: '2026-03-22T10:05:00.000Z',
518
+ lastActiveAt: '2026-03-22T10:05:00.000Z'
519
+ }
520
+ ],
521
+ lastPushedAiProfileIds: new Set(['ai-profile-1']),
522
+ lastPushedAiProfileWatermarks: new Map([['ai-profile-1', '2026-03-22T10:05:00.000Z']])
523
+ });
524
+ expect(payload.changes).toEqual(expect.arrayContaining([
525
+ expect.objectContaining({
526
+ op: 'ai-profile-upsert',
527
+ profile: expect.objectContaining({
528
+ id: 'ai-profile-1',
529
+ archivedAt: '2026-03-22T10:10:00.000Z',
530
+ rosterStateUpdatedAt: '2026-03-22T10:10:00.000Z',
531
+ updatedAt: '2026-03-22T10:05:00.000Z'
532
+ })
533
+ })
534
+ ]));
535
+ expect(payload.currentAiProfileWatermarks.get('ai-profile-1')).toBe('2026-03-22T10:10:00.000Z');
299
536
  });
300
537
  it('emits initiative and workstream upserts and tracks their watermarks', () => {
301
538
  const payload = buildWorkspaceSyncPayloadModel({
@@ -802,15 +1039,16 @@ describe('workspaceSyncModel', () => {
802
1039
  });
803
1040
  });
804
1041
  describe('dirty task watermarks', () => {
805
- it('emits an upsert when a status-only server-confirmed update changes updatedAt', () => {
1042
+ it('resends once when a legacy watermark-only task baseline exists', () => {
1043
+ const task = {
1044
+ id: 'task-1',
1045
+ title: 'Task 1',
1046
+ status: 'in-progress',
1047
+ createdAt: '2026-03-16T10:00:00.000Z',
1048
+ updatedAt: '2026-03-16T10:05:00.000Z'
1049
+ };
806
1050
  const payload = buildWorkspaceSyncPayloadModel({
807
- tasks: [{
808
- id: 'task-1',
809
- title: 'Task 1',
810
- status: 'in-progress',
811
- createdAt: '2026-03-16T10:00:00.000Z',
812
- updatedAt: '2026-03-16T10:05:00.000Z'
813
- }],
1051
+ tasks: [task],
814
1052
  archivedTasks: [],
815
1053
  lastPushedTaskIds: new Set(['task-1']),
816
1054
  pendingDeletedTaskIds: new Set(),
@@ -829,6 +1067,81 @@ describe('workspaceSyncModel', () => {
829
1067
  })
830
1068
  ]));
831
1069
  expect(payload.pushedWatermarks.get('task-1')).toBe('2026-03-16T10:05:00.000Z');
1070
+ expect(payload.pushedTaskChangeKeys.get('task-1')).toBe(taskSyncResourceAdapter.getChangeKey({ ...task, isArchived: false }));
1071
+ });
1072
+ it('does not resend a task when only updatedAt changes but the replicated change key matches', () => {
1073
+ const task = {
1074
+ id: 'task-1',
1075
+ title: 'Task 1',
1076
+ status: 'in-progress',
1077
+ createdAt: '2026-03-16T10:00:00.000Z',
1078
+ updatedAt: '2026-03-16T10:05:00.000Z'
1079
+ };
1080
+ const payload = buildWorkspaceSyncPayloadModel({
1081
+ tasks: [task],
1082
+ archivedTasks: [],
1083
+ lastPushedTaskIds: new Set(['task-1']),
1084
+ pendingDeletedTaskIds: new Set(),
1085
+ lastPushedWatermarks: new Map([
1086
+ ['task-1', '2026-03-16T10:00:00.000Z']
1087
+ ]),
1088
+ lastPushedTaskChangeKeys: new Map([
1089
+ ['task-1', taskSyncResourceAdapter.getChangeKey({ ...task, isArchived: false })]
1090
+ ])
1091
+ });
1092
+ expect(payload.changes.some((change) => change.op === 'upsert' && change.task.id === 'task-1')).toBe(false);
1093
+ expect(payload.currentTaskWatermarks.get('task-1')).toBe('2026-03-16T10:05:00.000Z');
1094
+ });
1095
+ it('treats comment and attachment changes as replicated task-state changes', () => {
1096
+ const task = {
1097
+ id: 'task-1',
1098
+ title: 'Task 1',
1099
+ status: 'task',
1100
+ createdAt: '2026-03-16T10:00:00.000Z',
1101
+ updatedAt: '2026-03-16T10:05:00.000Z',
1102
+ comments: [{
1103
+ id: 'comment-1',
1104
+ author: 'ai-profile-1',
1105
+ text: 'Shipped',
1106
+ timestamp: '2026-03-16T10:05:00.000Z'
1107
+ }],
1108
+ attachments: [{
1109
+ path: 'workspaces/ws-1/assets/files/asset-1.txt',
1110
+ caption: 'Spec',
1111
+ timestamp: '2026-03-16T10:05:00.000Z'
1112
+ }]
1113
+ };
1114
+ const previousTask = {
1115
+ ...task,
1116
+ comments: [],
1117
+ attachments: []
1118
+ };
1119
+ const payload = buildWorkspaceSyncPayloadModel({
1120
+ tasks: [task],
1121
+ archivedTasks: [],
1122
+ lastPushedTaskIds: new Set(['task-1']),
1123
+ pendingDeletedTaskIds: new Set(),
1124
+ lastPushedWatermarks: new Map([
1125
+ ['task-1', '2026-03-16T10:05:00.000Z']
1126
+ ]),
1127
+ lastPushedTaskChangeKeys: new Map([
1128
+ ['task-1', taskSyncResourceAdapter.getChangeKey({ ...previousTask, isArchived: false })]
1129
+ ])
1130
+ });
1131
+ expect(payload.changes).toEqual(expect.arrayContaining([
1132
+ expect.objectContaining({
1133
+ op: 'upsert',
1134
+ task: expect.objectContaining({
1135
+ id: 'task-1',
1136
+ comments: expect.arrayContaining([
1137
+ expect.objectContaining({ id: 'comment-1' })
1138
+ ]),
1139
+ attachments: expect.arrayContaining([
1140
+ expect.objectContaining({ path: 'workspaces/ws-1/assets/files/asset-1.txt' })
1141
+ ])
1142
+ })
1143
+ })
1144
+ ]));
832
1145
  });
833
1146
  it('keeps provisional local-reference tasks dirty until a canonical reference is assigned', () => {
834
1147
  const payload = buildWorkspaceSyncPayloadModel({
@@ -1,12 +1,31 @@
1
1
  export type WorkspaceSyncPhase = 'idle' | 'provision-local' | 'attach-cloud' | 'active' | 'error';
2
+ export type WorkspaceSyncSetupIntent = 'attach-cloud-import' | null;
3
+ export interface WorkspaceSyncPushBaselineSnapshot {
4
+ taskIds?: string[];
5
+ taskWatermarks: Array<[string, string]>;
6
+ taskChangeKeys?: Array<[string, string]>;
7
+ initiativeWatermarks: Array<[string, string]>;
8
+ workstreamWatermarks: Array<[string, string]>;
9
+ aiProfileWatermarks: Array<[string, string]>;
10
+ aiProfileChangeKeys?: Array<[string, string]>;
11
+ documentWatermarks: Array<[string, string]>;
12
+ assetWatermarks: Array<[string, string]>;
13
+ documentReviewSessionWatermarks: Array<[string, string]>;
14
+ documentReviewCommentWatermarks: Array<[string, string]>;
15
+ annotatedAttachmentSessionWatermarks: Array<[string, string]>;
16
+ taskEventWatermarks: Array<[string, string]>;
17
+ taxonomyStateFingerprint: string;
18
+ }
2
19
  export interface WorkspaceSyncStateV2 {
3
20
  version: 2;
4
21
  enabled: boolean;
5
22
  phase: WorkspaceSyncPhase;
23
+ setupIntent?: WorkspaceSyncSetupIntent;
6
24
  pullCursor: string | null;
7
25
  lastPullAt: string | null;
8
26
  lastPushAt: string | null;
9
27
  lastSyncedAt: string | null;
10
28
  lastErrorMessage: string | null;
29
+ pushBaseline?: WorkspaceSyncPushBaselineSnapshot | null;
11
30
  }
12
31
  export declare function createDefaultWorkspaceSyncState(): WorkspaceSyncStateV2;
@@ -3,10 +3,12 @@ export function createDefaultWorkspaceSyncState() {
3
3
  version: 2,
4
4
  enabled: false,
5
5
  phase: 'idle',
6
+ setupIntent: null,
6
7
  pullCursor: null,
7
8
  lastPullAt: null,
8
9
  lastPushAt: null,
9
10
  lastSyncedAt: null,
10
- lastErrorMessage: null
11
+ lastErrorMessage: null,
12
+ pushBaseline: null
11
13
  };
12
14
  }
@@ -1,3 +1,4 @@
1
+ import { taskSyncResourceAdapter } from './syncResourceAdapters.js';
1
2
  export function compareWorkspaceMutationOrdering(incomingWatermark, existingWatermark, incomingTieBreak, existingTieBreak) {
2
3
  const incomingMs = Date.parse(String(incomingWatermark || '').trim());
3
4
  const existingMs = Date.parse(String(existingWatermark || '').trim());
@@ -18,16 +19,10 @@ export function compareWorkspaceMutationOrdering(incomingWatermark, existingWate
18
19
  return String(incomingTieBreak || '').localeCompare(String(existingTieBreak || ''));
19
20
  }
20
21
  export function getTaskMutationWatermark(task) {
21
- return String(task?.updatedAt || task?.createdAt || '').trim();
22
+ return taskSyncResourceAdapter.getWatermark(task || {});
22
23
  }
23
24
  export function getTaskMutationTieBreak(task, archived) {
24
- const source = task || {};
25
- return JSON.stringify({
26
- id: String(source.id || ''),
27
- title: String(source.title || ''),
28
- status: String(source.status || ''),
29
- archived: archived ? 1 : 0
30
- });
25
+ return taskSyncResourceAdapter.getChangeKey({ ...(task || {}), isArchived: archived });
31
26
  }
32
27
  export function getDocumentMutationWatermark(document) {
33
28
  return String(document?.updatedAt || '').trim();
package/dist/types.d.ts CHANGED
@@ -143,6 +143,23 @@ export interface WorkstreamItem {
143
143
  order?: number | null;
144
144
  isArchived?: boolean;
145
145
  }
146
+ export interface PlanningBootstrapTaskPreview {
147
+ id: string;
148
+ referenceNumber?: number | null;
149
+ title: string;
150
+ status?: string | null;
151
+ }
152
+ export interface PlanningBootstrapWorkstreamTaskSummary {
153
+ workstreamId: string;
154
+ taskCount: number;
155
+ completedTaskCount: number;
156
+ tasks: PlanningBootstrapTaskPreview[];
157
+ }
158
+ export interface PlanningBootstrapPayload {
159
+ initiatives: InitiativeItem[];
160
+ workstreams: WorkstreamItem[];
161
+ workstreamTaskSummaries: PlanningBootstrapWorkstreamTaskSummary[];
162
+ }
146
163
  export interface ChecklistItem {
147
164
  id: string;
148
165
  taskId: string;
@@ -0,0 +1 @@
1
+ import{r as c,j as e}from"./vendor-react-CKJs5o3c.js";import{u as se,p as oe,v as ne,w as ce,x as _,y as de,z as pe,t as r,E as fe,B as me,C as K}from"./index-C6k75jr8.js";import{h as ue,B as V,k as U,b as he,$ as ve,a3 as ge}from"./vendor-icons-DuEd_65c.js";import"./vendor-markdown-BUxTU7dS.js";import"./vendor-dnd-DRzYolkg.js";import"./vendor-router-BbWMxlnO.js";function J(d){return!d||typeof d!="object"?null:{used:Math.max(0,Number(d.used||0)),limit:d.limit===null||d.limit===void 0?null:Math.max(0,Number(d.limit||0)),remaining:d.remaining===null||d.remaining===void 0?null:Math.max(0,Number(d.remaining||0))}}function W(d,P){const x=P?.variant==="inline"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconInline}`:P?.variant==="detail"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconDetail}`:r.aiProfileSeatScopeIcon;return d==="cloud_metered"?e.jsx("span",{className:`${x} ${r.aiProfileSeatScopeIconCloud}`,title:"Cloud MCP","aria-label":"Cloud MCP",children:e.jsx(ve,{size:20})}):d==="local_unmetered"?e.jsx("span",{className:`${x} ${r.aiProfileSeatScopeIconLocal}`,title:"Local MCP","aria-label":"Local MCP",children:e.jsx(ge,{size:20})}):null}function Ie({workspaceId:d,cloudAuthConfigured:P=!1,authSessionResolved:x=!1,isAuthenticated:Y=!1,cloudAiProfileSeatUsage:q=null}){const[S,L]=c.useState([]),[Q,X]=c.useState(null),[M,$]=c.useState(!1),[f,j]=c.useState(null),[I,v]=c.useState(null),[N,A]=c.useState(null),[g,b]=c.useState(null),[E,T]=c.useState(null),[B,D]=c.useState(!1),[Z,m]=c.useState(null),w=c.useCallback(async()=>{if(d){$(!0);try{const a=`/api/taskforce/workspace/assignee-options?workspaceId=${encodeURIComponent(d)}`,i=await fetch(a,{credentials:"include"}),t=i.ok?await i.json().catch(()=>({})):{},s=J(t?.aiProfileSeatUsage),n=Array.isArray(t?.assignees)?t.assignees.filter(l=>l.kind==="agent").map(l=>({id:String(l.value||""),name:String(l.label||l.value||"Unknown Agent"),username:String(l.username||l.value||""),icon:String(l.icon||"Bot"),color:String(l.color||"#6B7280"),avatarUrl:typeof l.avatarUrl=="string"?l.avatarUrl:null,avatarSourceUrl:typeof l.avatarSourceUrl=="string"?l.avatarSourceUrl:null,kind:String(l.kind||"agent"),description:typeof l.description=="string"?l.description:null,role:typeof l.role=="string"?l.role:null,provider:typeof l.provider=="string"?l.provider:null,model:typeof l.model=="string"?l.model:null,surfaceType:oe(l.surfaceType),seatScope:se(l.seatScope),archivedAt:typeof l.archivedAt=="string"?l.archivedAt:null,createdAt:String(l.createdAt||""),updatedAt:String(l.updatedAt||l.createdAt||""),lastActiveAt:typeof l.lastActiveAt=="string"?l.lastActiveAt:null})):[];L(n),X(s),A(l=>l&&!n.some(h=>h.id===l.profileId)?null:l)}finally{$(!1)}}},[d]);c.useEffect(()=>{w()},[w]);const k=c.useMemo(()=>{const a=new Map;for(const i of S){const t=ne(i.surfaceType);a.has(t)||a.set(t,new Map);const s=a.get(t),n=i.name.toLowerCase();s.has(n)||s.set(n,[]),s.get(n).push(i)}return ce.map(i=>({section:i,label:_(i),groups:Array.from(a.get(i)?.entries()||[]).map(([t,s])=>({groupId:`${i}:${t}`,section:i,sectionLabel:_(i),profiles:s,primaryProfile:s[0]}))})).filter(i=>i.groups.length>0)},[S]),u=c.useMemo(()=>k.flatMap(a=>a.groups),[k]),o=c.useMemo(()=>u.find(a=>a.groupId===g)||u[0]||null,[u,g]),p=c.useMemo(()=>S.find(a=>a.id===E)||null,[S,E]);c.useEffect(()=>{if(!u.length){g!==null&&b(null);return}(!g||!u.some(a=>a.groupId===g))&&b(u[0].groupId)},[u,g]);const ee=async(a,i)=>{v(null),A(null);try{const t=await fetch("/api/taskforce/workspace/ai-profiles/merge",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({keepId:a,mergeId:i})}),s=await t.json().catch(()=>({}));if(!t.ok){v({type:"error",message:String(s?.error||"Failed to merge AI profiles.")});return}j(null),v({type:"success",message:"AI profiles merged."}),await w()}catch{v({type:"error",message:"Failed to merge AI profiles."})}},G=async a=>{if(window.confirm(`Remove ${a.name} from the active roster? This frees an AI profile seat and preserves task and comment history.`)){A(null),v(null);try{const t=await fetch("/api/taskforce/workspace/ai-profiles/archive",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a.id,reason:"manual_archive"})}),s=await t.json().catch(()=>({}));if(!t.ok){A({profileId:a.id,message:String(s?.error||"Failed to remove AI profile from roster.")});return}(f?.keepId===a.id||f?.mergeId===a.id)&&j(null),v({type:"success",message:"AI profile removed from active roster."}),await w()}catch{A({profileId:a.id,message:"Failed to remove AI profile from roster."})}}},ae=a=>new Promise((i,t)=>{const s=new FileReader;s.onload=()=>i(String(s.result||"")),s.onerror=()=>t(new Error("Failed to read image file.")),s.readAsDataURL(a)}),re=a=>{const i=String(a?.id||"").trim();i&&L(t=>t.map(s=>s.id===i?{...s,avatarUrl:typeof a.avatarUrl=="string"?a.avatarUrl:null,avatarSourceUrl:typeof a.avatarSourceUrl=="string"?a.avatarSourceUrl:null,updatedAt:typeof a.updatedAt=="string"?a.updatedAt:s.updatedAt}:s))},F=async(a,i,t)=>{const s=await fetch("/api/taskforce/workspace/ai-profiles/avatar",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a,avatarUrl:i,...t!==void 0?{avatarSourceUrl:t}:{}})}),n=await s.json().catch(()=>({}));if(!s.ok||!n?.profile)throw new Error(String(n?.error||"Failed to update AI profile avatar."));re(n.profile)},z=async(a,i,t)=>{const s=await ae(a),n=await fetch("/api/taskforce/context-upload",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({workspaceId:d,file:s,originalName:`${t}-${a.name}`,folderPath:`ai-profiles/${i.id}/avatar`})}),l=await n.json().catch(()=>({})),h=typeof l?.path=="string"?l.path:"";if(!n.ok||!h)throw new Error(String(l?.error||"Failed to upload AI profile photo."));return h},ie=async(a,i)=>{if(!p)return!1;if(!a.type.startsWith("image/"))return m("AI profile photo must be an image file."),!1;D(!0),m(null);try{const t=await K(a,{maxBytes:5242880});if(t.exceededLimit)throw new Error(a.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile photo must be 5 MB or smaller.");const s=t.file,n=await z(s,p,"cropped");let l;if(i){const h=await K(i,{maxBytes:5242880});if(h.exceededLimit)throw new Error(i.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile source photo must be 5 MB or smaller.");l=i.type==="image/gif"?n:await z(h.file,p,"source")}return await F(p.id,n,l),!0}catch(t){return m(String(t?.message||"Failed to update AI profile photo.")),!1}finally{D(!1)}},te=async()=>{if(p){D(!0),m(null);try{await F(p.id,null,null)}catch(a){m(String(a?.message||"Failed to remove AI profile photo."))}finally{D(!1)}}},y=a=>{const i=String(a||"").trim();if(!i)return"Unknown";const t=Date.parse(i);return Number.isFinite(t)?new Date(t).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):i},H=c.useMemo(()=>{if(!o)return{attributes:[],dates:[]};const a=o.primaryProfile,i=t=>t||"Not set";return{attributes:[{label:"Category",value:a.surfaceType?de(a.surfaceType):""},{label:"Connection",value:a.seatScope?pe(a.seatScope):""},{label:"Provider",value:a.provider||""},{label:"Model",value:a.model||""}].map(t=>({...t,value:i(t.value)})),dates:[{label:"Recruited",value:y(a.createdAt)},{label:"Last updated",value:y(a.updatedAt)},{label:"Last active",value:y(a.lastActiveAt)},{label:"Status",value:a.archivedAt?"Retired":"Active"}].map(t=>({...t,value:i(t.value)}))}},[o]),le=!!o&&o.profiles.length>1,O=P&&x&&!Y,C=P?J(q):Q,R=O?"Log into account for Cloud agents":C?`${C.used}/${C.limit===null?"Unlimited":C.limit}`:null;return e.jsxs("section",{className:r.agentsModuleRoot,children:[e.jsx("div",{className:r.agentsModuleContent,children:e.jsx("div",{className:`${r.settingGroup} ${r.agentsModuleGroup}`,children:e.jsxs("div",{children:[e.jsx("h4",{className:r.settingSubTitle,children:"Registered AI Profiles"}),e.jsx("p",{className:`${r.settingsHint} ${r.marginBottom12}`,children:"AI agents register profiles when connecting via MCP. Merge duplicates created when a token was lost, or remove inactive profiles from the active roster to free seats while preserving history."}),R&&e.jsx("div",{className:r.aiProfileSeatSummary,children:O?R:`Registered Cloud Agents: ${R}`}),M&&e.jsxs("div",{className:r.settingsHint,children:[e.jsx(ue,{size:13,className:r.spinner})," Loading profiles…"]}),!M&&S.length===0&&e.jsx("div",{className:r.settingsHint,children:"No AI profiles registered yet."}),!M&&k.length>0&&e.jsxs("div",{className:r.aiProfilesExplorer,children:[e.jsx("div",{className:`${r.aiProfilesListPane} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.aiProfilesList,children:k.map(a=>e.jsxs("div",{className:r.aiProfilesSection,children:[e.jsx("div",{className:r.aiProfilesSectionHeader,children:a.label}),a.groups.map(i=>{const t=o?.groupId===i.groupId,s=i.primaryProfile;return e.jsxs("div",{role:"button",tabIndex:0,className:`${r.aiProfileGroup} ${i.profiles.length>1?r.aiProfileGroupDuplicate:""} ${t?r.aiProfileGroupSelected:""}`,onClick:()=>b(i.groupId),onKeyDown:n=>{n.key!=="Enter"&&n.key!==" "||(n.preventDefault(),b(i.groupId))},"aria-pressed":t,children:[W(s.seatScope),e.jsxs("div",{className:r.aiProfileGroupHeader,children:[e.jsx("span",{className:r.aiProfileGroupAvatar,style:{color:s.color},children:s.avatarUrl?e.jsx("img",{src:s.avatarUrl,alt:""}):e.jsx(V,{size:22})}),e.jsxs("span",{className:r.aiProfileGroupIdentity,children:[e.jsx("span",{className:r.aiProfileName,children:s.name}),e.jsxs("span",{className:r.aiProfileHandle,children:["@",s.username]}),e.jsx("span",{className:r.aiProfileRole,children:s.role||"Role not set"}),i.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDuplicateBadge,children:[e.jsx(U,{size:11})," ",i.profiles.length," duplicates"]})]})]})]},i.groupId)})]},a.section))})}),o&&e.jsx("div",{className:r.aiProfileDetailPane,children:e.jsxs("div",{className:r.aiProfileDetailCard,children:[W(o.primaryProfile.seatScope,{variant:"detail"}),e.jsxs("div",{className:r.aiProfileDetailHero,children:[e.jsx(fe,{label:"Edit AI profile photo",imageUrl:o.primaryProfile.avatarUrl,fallback:e.jsx(V,{size:38}),accentColor:o.primaryProfile.color,size:176,width:153,height:207,radius:6,editBadgeSize:28,editIconSize:14,className:r.aiProfileDetailAvatar,onClick:()=>{m(null),T(o.primaryProfile.id)}}),e.jsxs("div",{className:r.aiProfileDetailHeading,children:[e.jsx("div",{className:r.aiProfileDetailTitleRow,children:e.jsx("h5",{className:r.aiProfileDetailTitle,children:o.primaryProfile.name})}),e.jsxs("div",{className:r.aiProfileDetailMetaRow,children:[e.jsxs("span",{className:r.aiProfileDetailHandle,children:["@",o.primaryProfile.username]}),o.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDetailLinkedCount,children:[o.profiles.length," linked"]})]}),e.jsxs("div",{className:r.aiProfileDetailRole,children:["Role: ",o.primaryProfile.role||"Not set"]}),o.primaryProfile.description&&e.jsx("div",{className:r.aiProfileDetailDescription,children:o.primaryProfile.description})]})]}),e.jsxs("div",{className:r.aiProfileDetailDataList,children:[e.jsx("div",{className:r.aiProfileDetailDataGroup,children:H.attributes.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))}),e.jsx("div",{className:`${r.aiProfileDetailDataGroup} ${r.aiProfileDetailDateGroup}`,children:H.dates.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Status"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent roster status",value:o.primaryProfile.archivedAt?"retired":"active",onChange:i=>{i.target.value==="retire"&&G(o.primaryProfile)},children:[e.jsx("option",{value:"active",children:"Active"}),o.primaryProfile.archivedAt?e.jsx("option",{value:"retired",children:"Retired"}):e.jsx("option",{value:"retire",children:"Retire from roster"})]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))})]}),le?e.jsxs("div",{className:r.aiProfileInstanceSection,children:[e.jsxs("div",{className:r.aiProfileInstanceSectionHeader,children:[e.jsx("span",{children:"Profile instances"}),e.jsx("span",{className:r.settingsHint,children:"Choose a keeper here if duplicates need to be merged."})]}),e.jsx("div",{className:r.aiProfileInstanceList,children:o.profiles.map(a=>e.jsxs("div",{className:r.aiProfileInstanceCard,children:[e.jsxs("div",{className:r.aiProfileInstanceTopRow,children:[e.jsxs("div",{children:[e.jsxs("div",{className:r.aiProfileInstanceName,children:["@",a.username]}),e.jsx("div",{className:r.aiProfileIdChip,children:a.id})]}),f?.keepId===a.id&&e.jsx("span",{className:r.aiProfileKeepBadge,children:"Keeping"})]}),e.jsxs("div",{className:r.aiProfileInstanceMeta,children:[e.jsxs("span",{children:["Created ",y(a.createdAt)]}),e.jsxs("span",{children:["Updated ",y(a.updatedAt)]})]}),f?.keepId!==a.id&&e.jsx("button",{className:r.secondaryHeaderBtn,title:"Keep this profile, merge others into it",onClick:()=>{const i=o.profiles.find(t=>t.id!==a.id)?.id;i&&j({keepId:a.id,mergeId:i})},children:"Keep this"}),N?.profileId===a.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(U,{size:12}),e.jsx("span",{children:N.message})]}),e.jsx("button",{className:r.aiProfileDangerTextButton,title:"Remove this profile from the active roster",onClick:()=>{G(a)},children:"Remove from roster"})]},a.id))}),f&&o.profiles.some(a=>a.id===f.keepId)&&e.jsxs("div",{className:r.aiProfileMergeActions,children:[e.jsx("button",{className:r.dangerBtn,onClick:()=>{ee(f.keepId,f.mergeId)},children:"Merge duplicates"}),e.jsx("button",{className:r.secondaryHeaderBtn,onClick:()=>j(null),children:"Cancel"})]})]}):e.jsxs("div",{className:r.aiProfileIdFooter,children:[e.jsx("div",{className:r.aiProfileIdFooterValue,children:o.primaryProfile.id}),N?.profileId===o.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(U,{size:12}),e.jsx("span",{children:N.message})]})]})]})})]}),I&&e.jsxs("div",{className:`${I.type==="success"?r.successMessage:r.errorMessage} ${r.marginTop12}`,children:[I.type==="success"?e.jsx(he,{size:14}):e.jsx(U,{size:14}),I.message]})]})})}),e.jsx(me,{isOpen:!!p,theme:"dark",title:"Edit AI Profile Photo",currentImageUrl:p?.avatarUrl||"",editorImageUrl:p?.avatarSourceUrl||"",fallbackInitial:(p?.name||"AI").charAt(0).toUpperCase(),accept:"image/png,image/jpeg,image/webp,image/gif",busy:B,hasPendingImage:!1,canRemove:!!p?.avatarUrl,error:Z,notice:null,onClose:()=>{B||(T(null),m(null))},onApplyImage:ie,onRemoveImage:te})]})}export{Ie as AgentsModule};