@taskforcehq/taskforce 0.3.305 → 0.3.307

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (440) hide show
  1. package/README.md +0 -2
  2. package/dist/Taskforce.module.css +603 -107
  3. package/dist/TaskforceCore.js +216 -43
  4. package/dist/TaskforceCore.test.js +611 -28
  5. package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
  6. package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  7. package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
  8. package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
  9. package/dist/assets/fonts/Noir_medium.woff2 +0 -0
  10. package/dist/assets/fonts/Noir_regular.woff2 +0 -0
  11. package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
  12. package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
  13. package/dist/compat/workspaceSyncCompat.js +46 -2
  14. package/dist/compat/workspaceSyncCompat.test.js +38 -1
  15. package/dist/components/activity/EntityActivityTimeline.d.ts +34 -0
  16. package/dist/components/activity/EntityActivityTimeline.js +81 -0
  17. package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
  18. package/dist/components/context/ContextAttachmentManager.js +561 -0
  19. package/dist/components/features/AgentsModule.d.ts +14 -1
  20. package/dist/components/features/AgentsModule.js +366 -18
  21. package/dist/components/features/AgentsModule.test.js +507 -8
  22. package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +3 -1
  23. package/dist/components/features/AnnotatedAttachmentWorkspace.js +118 -4
  24. package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +52 -0
  25. package/dist/components/features/DocumentIndex.d.ts +2 -2
  26. package/dist/components/features/DocumentIndex.js +3 -4
  27. package/dist/components/features/DocumentViewer.d.ts +7 -1
  28. package/dist/components/features/DocumentViewer.js +62 -4
  29. package/dist/components/features/DocumentWorkspace.d.ts +10 -2
  30. package/dist/components/features/DocumentWorkspace.js +15 -4
  31. package/dist/components/features/DocumentWorkspace.test.js +51 -1
  32. package/dist/components/features/TaskSettings.d.ts +2 -0
  33. package/dist/components/features/TaskSettings.js +83 -78
  34. package/dist/components/features/TaskSettings.test.js +171 -82
  35. package/dist/components/features/documentWorkspaceModel.d.ts +1 -1
  36. package/dist/components/task/TaskActionHeader.js +11 -5
  37. package/dist/components/task/TaskActionHeader.test.js +10 -0
  38. package/dist/components/task/TaskCard.js +24 -29
  39. package/dist/components/task/TaskCard.test.js +38 -3
  40. package/dist/components/task/TaskContextUpload.js +4 -526
  41. package/dist/components/task/TaskContextUpload.test.js +3 -3
  42. package/dist/components/task/TaskForm.js +61 -116
  43. package/dist/components/task/TaskForm.test.js +110 -8
  44. package/dist/components/task/TaskKanban.d.ts +2 -1
  45. package/dist/components/task/TaskKanban.js +94 -21
  46. package/dist/components/task/TaskKanban.test.js +36 -0
  47. package/dist/components/task/TaskList.js +5 -1
  48. package/dist/components/task/TaskList.test.js +4 -0
  49. package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
  50. package/dist/components/ui/EditableAvatarButton.js +18 -0
  51. package/dist/components/ui/HierarchyActionBadge.d.ts +10 -0
  52. package/dist/components/ui/HierarchyActionBadge.js +5 -0
  53. package/dist/components/views/AppShellHeader.d.ts +3 -1
  54. package/dist/components/views/AppShellHeader.js +27 -11
  55. package/dist/components/views/AppShellHeader.test.js +18 -1
  56. package/dist/components/views/PlanComparisonPage.d.ts +9 -1
  57. package/dist/components/views/PlanComparisonPage.js +78 -14
  58. package/dist/components/views/PlanComparisonPage.test.js +257 -0
  59. package/dist/components/views/PlansPage.js +127 -36
  60. package/dist/components/views/PlansPage.test.js +294 -12
  61. package/dist/components/views/StandaloneLayout.js +529 -592
  62. package/dist/components/views/WidgetView.js +10 -8
  63. package/dist/components/views/panels/PlanningDrawer.d.ts +25 -5
  64. package/dist/components/views/panels/PlanningDrawer.js +270 -55
  65. package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
  66. package/dist/components/views/panels/PlanningDrawer.test.js +529 -0
  67. package/dist/components/views/planningScope.d.ts +20 -0
  68. package/dist/components/views/planningScope.js +25 -0
  69. package/dist/components/views/planningScope.test.d.ts +1 -0
  70. package/dist/components/views/planningScope.test.js +60 -0
  71. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +26 -1
  72. package/dist/components/views/standalone/modals/AccountHubModal.js +76 -2
  73. package/dist/components/views/standalone/modals/AccountHubModal.test.js +20 -3
  74. package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
  75. package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
  76. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
  77. package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
  78. package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
  79. package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
  80. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
  81. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
  82. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
  83. package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
  84. package/dist/config/envSchema.js +1 -0
  85. package/dist/core/AiProfileService.d.ts +46 -2
  86. package/dist/core/AiProfileService.js +1073 -82
  87. package/dist/core/AiProfileService.test.js +19 -1
  88. package/dist/core/AiProfiles.test.js +1249 -44
  89. package/dist/core/AttachmentLinkService.d.ts +3 -0
  90. package/dist/core/AttachmentLinkService.js +110 -23
  91. package/dist/core/AuthIdentityService.d.ts +107 -0
  92. package/dist/core/AuthIdentityService.js +284 -0
  93. package/dist/core/AuthTokenService.d.ts +4 -0
  94. package/dist/core/AuthTokenService.js +34 -6
  95. package/dist/core/AuthTokenService.test.js +3 -3
  96. package/dist/core/EntitlementsPolicy.test.js +2 -2
  97. package/dist/core/GlobalSettingsService.js +127 -6
  98. package/dist/core/McpTokenService.d.ts +12 -0
  99. package/dist/core/McpTokenService.js +44 -1
  100. package/dist/core/PlanEntitlementService.d.ts +23 -2
  101. package/dist/core/PlanEntitlementService.js +343 -57
  102. package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
  103. package/dist/core/PlanFeatureCatalog.test.js +84 -0
  104. package/dist/core/PlanVersionPolicy.test.js +35 -2
  105. package/dist/core/PlanningEntities.test.js +418 -4
  106. package/dist/core/SignupMonetizationSettings.test.js +199 -27
  107. package/dist/core/SyncReconciliationService.js +2 -65
  108. package/dist/core/SystemAdmin.test.js +42 -42
  109. package/dist/core/TaskActivityService.d.ts +28 -1
  110. package/dist/core/TaskActivityService.js +227 -46
  111. package/dist/core/TaskActivitySync.test.d.ts +1 -0
  112. package/dist/core/TaskActivitySync.test.js +90 -0
  113. package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
  114. package/dist/core/TaskAuditTimeline.test.js +96 -0
  115. package/dist/core/Taskforce.d.ts +415 -40
  116. package/dist/core/Taskforce.ids.test.js +58 -0
  117. package/dist/core/Taskforce.js +1543 -266
  118. package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
  119. package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
  120. package/dist/core/Taskforce.mcpAuth.test.js +109 -0
  121. package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
  122. package/dist/core/WorkspaceLifecycleService.js +0 -5
  123. package/dist/core/WorkspacePermissions.test.js +25 -1
  124. package/dist/core/WorkspacePlanMode.test.js +117 -16
  125. package/dist/core/shared.d.ts +1 -1
  126. package/dist/core/shared.js +5 -1
  127. package/dist/core/types.d.ts +75 -2
  128. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +28 -2
  129. package/dist/hooks/bootstrapTimeouts.d.ts +4 -0
  130. package/dist/hooks/bootstrapTimeouts.js +8 -0
  131. package/dist/hooks/bootstrapTimeouts.test.d.ts +1 -0
  132. package/dist/hooks/bootstrapTimeouts.test.js +12 -0
  133. package/dist/hooks/sync/bootstrap.js +2 -0
  134. package/dist/hooks/sync/controlPlane.d.ts +37 -0
  135. package/dist/hooks/sync/controlPlane.js +78 -0
  136. package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
  137. package/dist/hooks/sync/controlPlane.test.js +121 -0
  138. package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
  139. package/dist/hooks/sync/lifecyclePolicy.js +93 -0
  140. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
  141. package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
  142. package/dist/hooks/sync/orchestratorShared.d.ts +13 -4
  143. package/dist/hooks/sync/orchestratorShared.js +129 -48
  144. package/dist/hooks/sync/orchestratorShared.test.js +50 -1
  145. package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
  146. package/dist/hooks/sync/pullLifecycle.js +24 -0
  147. package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
  148. package/dist/hooks/sync/pullLifecycle.test.js +80 -0
  149. package/dist/hooks/sync/recovery.d.ts +16 -2
  150. package/dist/hooks/sync/recovery.js +179 -53
  151. package/dist/hooks/sync/recovery.test.d.ts +1 -0
  152. package/dist/hooks/sync/recovery.test.js +292 -0
  153. package/dist/hooks/sync/transfers.d.ts +16 -3
  154. package/dist/hooks/sync/transfers.js +210 -66
  155. package/dist/hooks/sync/transfers.test.d.ts +1 -0
  156. package/dist/hooks/sync/transfers.test.js +928 -0
  157. package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
  158. package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
  159. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
  160. package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
  161. package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
  162. package/dist/hooks/sync/useSyncStatusActions.js +88 -0
  163. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
  164. package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
  165. package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
  166. package/dist/hooks/sync/useSyncStatusControls.js +60 -0
  167. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
  168. package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
  169. package/dist/hooks/useFilterSort.js +13 -13
  170. package/dist/hooks/useFilterSort.test.js +87 -1
  171. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
  172. package/dist/hooks/useSyncOrchestrator.d.ts +9 -2
  173. package/dist/hooks/useSyncOrchestrator.js +409 -183
  174. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +407 -0
  175. package/dist/hooks/useTaskData.js +8 -3
  176. package/dist/hooks/useTaskData.test.js +45 -0
  177. package/dist/hooks/useTaskforce.d.ts +145 -52
  178. package/dist/hooks/useTaskforce.js +592 -253
  179. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
  180. package/dist/hooks/useTaskforce.runtime-routing.test.js +246 -0
  181. package/dist/hooks/useTaskforce.sync-behavior.test.js +2468 -201
  182. package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
  183. package/dist/hooks/useWorkspaceSyncController.js +13 -2
  184. package/dist/hooks/useWorkspaceSyncController.test.js +48 -0
  185. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
  186. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +328 -40
  187. package/dist/localization/locales/en-US.d.ts +0 -1
  188. package/dist/localization/locales/en-US.js +0 -1
  189. package/dist/localization/locales/es-419.js +0 -1
  190. package/dist/localization/locales/pt-BR.js +0 -1
  191. package/dist/mcp/canonicalAssetHelpers.d.ts +16 -4
  192. package/dist/mcp/canonicalAssetHelpers.js +18 -10
  193. package/dist/mcp/canonicalAssetHelpers.test.d.ts +1 -0
  194. package/dist/mcp/canonicalAssetHelpers.test.js +67 -0
  195. package/dist/mcp/clientIntegrations.d.ts +1 -0
  196. package/dist/mcp/clientIntegrations.js +25 -0
  197. package/dist/mcp/clientIntegrations.test.js +14 -3
  198. package/dist/mcp/clientProfileDefaults.js +3 -3
  199. package/dist/mcp/collaborationRegistrar.js +7 -7
  200. package/dist/mcp/documentAssetRegistrar.js +116 -10
  201. package/dist/mcp/runtime.d.ts +3 -0
  202. package/dist/mcp/runtime.js +1099 -185
  203. package/dist/mcp/runtime.test.js +1491 -140
  204. package/dist/mcp/taskAttachmentTypes.d.ts +1 -1
  205. package/dist/mcp/taskPlanningRegistrar.js +48 -0
  206. package/dist/mcp/toolRegistry.d.ts +56 -0
  207. package/dist/mcp/toolRegistry.js +8 -0
  208. package/dist/mcp/toolRegistry.test.js +4 -0
  209. package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
  210. package/dist/migrations/taskSchemaMigrations.js +192 -61
  211. package/dist/migrations/taskSchemaMigrations.test.js +15 -0
  212. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  213. package/dist/resources/templates/workflows/collaborate.yaml +1 -1
  214. package/dist/resources/templates/workflows/evaluate.yaml +2 -2
  215. package/dist/resources/templates/workflows/plan.yaml +1 -1
  216. package/dist/resources/templates/workflows/review.yaml +2 -2
  217. package/dist/server/aiProfileAvatarAssets.d.ts +49 -0
  218. package/dist/server/aiProfileAvatarAssets.js +319 -0
  219. package/dist/server/aiProfileAvatarAssets.test.d.ts +1 -0
  220. package/dist/server/aiProfileAvatarAssets.test.js +213 -0
  221. package/dist/server/annotatedAttachmentsRoutes.test.js +69 -10
  222. package/dist/server/auth/providers/apple.d.ts +33 -0
  223. package/dist/server/auth/providers/apple.js +82 -0
  224. package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
  225. package/dist/server/auth/providers/appleClientSecret.js +65 -0
  226. package/dist/server/auth/providers/github.d.ts +26 -0
  227. package/dist/server/auth/providers/github.js +86 -0
  228. package/dist/server/auth/providers/google.d.ts +21 -0
  229. package/dist/server/auth/providers/google.js +56 -0
  230. package/dist/server/auth/providers/types.d.ts +21 -0
  231. package/dist/server/auth/providers/types.js +1 -0
  232. package/dist/server/auth.d.ts +2 -0
  233. package/dist/server/auth.js +6 -3
  234. package/dist/server/documentReviewRoutes.test.js +36 -3
  235. package/dist/server/index.cookieProxy.test.js +1 -0
  236. package/dist/server/index.d.ts +12 -0
  237. package/dist/server/index.js +124 -9
  238. package/dist/server/index.rateLimit.test.js +3 -0
  239. package/dist/server/index.test.js +111 -3
  240. package/dist/server/routes/admin.d.ts +3 -0
  241. package/dist/server/routes/admin.js +459 -28
  242. package/dist/server/routes/annotatedAttachments.js +51 -0
  243. package/dist/server/routes/auth.d.ts +26 -2
  244. package/dist/server/routes/auth.js +1170 -71
  245. package/dist/server/routes/authSupport.d.ts +5 -1
  246. package/dist/server/routes/authSupport.js +13 -2
  247. package/dist/server/routes/billing.js +372 -35
  248. package/dist/server/routes/billing.test.js +595 -12
  249. package/dist/server/routes/documents.d.ts +1 -0
  250. package/dist/server/routes/documents.js +182 -70
  251. package/dist/server/routes/shared.d.ts +28 -2
  252. package/dist/server/routes/shared.js +69 -14
  253. package/dist/server/routes/sync.integration.test.js +658 -39
  254. package/dist/server/routes/sync.js +74 -608
  255. package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
  256. package/dist/server/routes/syncAuxRoutes.js +91 -0
  257. package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
  258. package/dist/server/routes/syncAuxRoutes.test.js +158 -0
  259. package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
  260. package/dist/server/routes/syncPullApplyRoutes.js +204 -0
  261. package/dist/server/routes/syncPushRoutes.d.ts +24 -0
  262. package/dist/server/routes/syncPushRoutes.js +212 -0
  263. package/dist/server/routes/syncRouteGuards.d.ts +36 -0
  264. package/dist/server/routes/syncRouteGuards.js +67 -0
  265. package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
  266. package/dist/server/routes/syncRouteGuards.test.js +94 -0
  267. package/dist/server/routes/syncRouteTypes.d.ts +13 -0
  268. package/dist/server/routes/syncRouteTypes.js +1 -0
  269. package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
  270. package/dist/server/routes/syncSnapshotRoutes.js +182 -0
  271. package/dist/server/routes/tasks.js +55 -10
  272. package/dist/server/routes/workspaces.js +36 -5
  273. package/dist/server/routes.js +147 -11
  274. package/dist/server/routes.test.js +2704 -183
  275. package/dist/services/workflowExportSnapshots.test.js +2 -0
  276. package/dist/shared/aiProfileSeatScope.d.ts +5 -0
  277. package/dist/shared/aiProfileSeatScope.js +21 -0
  278. package/dist/shared/aiProfileSurfaceType.d.ts +1 -1
  279. package/dist/shared/aiProfileSurfaceType.js +10 -8
  280. package/dist/shared/passwordPolicy.d.ts +13 -0
  281. package/dist/shared/passwordPolicy.js +84 -0
  282. package/dist/shared/passwordPolicy.test.d.ts +1 -0
  283. package/dist/shared/passwordPolicy.test.js +34 -0
  284. package/dist/storage/objectStorage.d.ts +1 -0
  285. package/dist/storage/objectStorage.js +2 -1
  286. package/dist/storage/objectStorageClient.d.ts +1 -0
  287. package/dist/storage/objectStorageClient.js +41 -8
  288. package/dist/storage/objectStorageClient.test.js +26 -1
  289. package/dist/storage/objectStorageConfig.js +3 -1
  290. package/dist/storage/objectStorageConfig.test.js +13 -1
  291. package/dist/storage/workspaceAssetStore.d.ts +15 -3
  292. package/dist/storage/workspaceAssetStore.js +132 -44
  293. package/dist/storage/workspaceAssetStore.test.js +93 -6
  294. package/dist/styles/fonts.css +70 -0
  295. package/dist/sync/collaborationSyncPayload.d.ts +6 -1
  296. package/dist/sync/collaborationSyncPayload.js +19 -11
  297. package/dist/sync/collaborationSyncPayload.test.js +14 -1
  298. package/dist/sync/collaborationSyncState.d.ts +3 -3
  299. package/dist/sync/collaborationSyncState.js +19 -12
  300. package/dist/sync/contentSyncPayload.d.ts +5 -1
  301. package/dist/sync/contentSyncPayload.js +9 -0
  302. package/dist/sync/contentSyncPayload.test.js +39 -0
  303. package/dist/sync/contentSyncState.d.ts +9 -9
  304. package/dist/sync/contentSyncState.js +248 -107
  305. package/dist/sync/planningSyncPayload.js +5 -0
  306. package/dist/sync/planningSyncPayload.test.js +24 -0
  307. package/dist/sync/syncApplyHandlers.d.ts +11 -4
  308. package/dist/sync/syncApplyHandlers.js +115 -42
  309. package/dist/sync/syncApplyHandlers.test.js +251 -4
  310. package/dist/sync/syncFieldSemantics.d.ts +12 -0
  311. package/dist/sync/syncFieldSemantics.js +55 -0
  312. package/dist/sync/syncResourceAdapters.d.ts +46 -0
  313. package/dist/sync/syncResourceAdapters.js +79 -0
  314. package/dist/sync/syncService.d.ts +6 -0
  315. package/dist/sync/syncService.js +65 -1
  316. package/dist/sync/syncService.test.js +96 -3
  317. package/dist/sync/taskSyncChangeKey.d.ts +2 -0
  318. package/dist/sync/taskSyncChangeKey.js +143 -0
  319. package/dist/sync/taskSyncPayload.js +3 -8
  320. package/dist/sync/workspacePullFeed.d.ts +4 -0
  321. package/dist/sync/workspacePullFeed.js +132 -72
  322. package/dist/sync/workspacePullFeed.test.js +92 -0
  323. package/dist/sync/workspaceRepair.js +5 -2
  324. package/dist/sync/workspaceSyncModel.d.ts +73 -6
  325. package/dist/sync/workspaceSyncModel.js +342 -73
  326. package/dist/sync/workspaceSyncModel.test.js +559 -14
  327. package/dist/sync/workspaceSyncState.d.ts +17 -0
  328. package/dist/sync/workspaceSyncState.js +2 -1
  329. package/dist/sync/workspaceSyncSurface.js +3 -8
  330. package/dist/types.d.ts +50 -2
  331. package/dist/ui/assets/AgentsModule-2y82_3DO.js +1 -0
  332. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BTLZZCiQ.js +3 -0
  333. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BVKew-mH.css +1 -0
  334. package/dist/ui/assets/ContextAttachmentManager-D2epuNnG.js +3 -0
  335. package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
  336. package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
  337. package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
  338. package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
  339. package/dist/ui/assets/DocumentWorkspace-D4rJKXzd.css +1 -0
  340. package/dist/ui/assets/DocumentWorkspace-sVa-3Do6.js +252 -0
  341. package/dist/ui/assets/EntityActivityTimeline-DyqvvhuF.css +1 -0
  342. package/dist/ui/assets/EntityActivityTimeline-Zods27y_.js +1 -0
  343. package/dist/ui/assets/{InitiativesModule-Dhm7M8e7.js → InitiativesModule-D4XseTwW.js} +1 -1
  344. package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
  345. package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
  346. package/dist/ui/assets/PlansPage-D5AcbO0L.js +1 -0
  347. package/dist/ui/assets/PlansPage-Dvqsz5zc.css +1 -0
  348. package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
  349. package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
  350. package/dist/ui/assets/TaskContextUpload-uC5qx4Bv.js +1 -0
  351. package/dist/ui/assets/TaskSettings-CqvPAsTv.js +9 -0
  352. package/dist/ui/assets/{WorkflowsModule-BcS4afRn.js → WorkflowsModule-oOA9bcdK.js} +4 -4
  353. package/dist/ui/assets/documentReferences-DPZuTgHh.js +1 -0
  354. package/dist/ui/assets/index-607WPo_X.js +4 -0
  355. package/dist/ui/assets/index-CYyodXsg.css +1 -0
  356. package/dist/ui/assets/{vendor-icons-BSUaRwF8.js → vendor-icons-B32hGuF2.js} +1 -1
  357. package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
  358. package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
  359. package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
  360. package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  361. package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
  362. package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
  363. package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
  364. package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
  365. package/dist/ui/fonts/Noir_medium.otf +0 -0
  366. package/dist/ui/fonts/Noir_medium.woff2 +0 -0
  367. package/dist/ui/fonts/Noir_regular.otf +0 -0
  368. package/dist/ui/fonts/Noir_regular.woff2 +0 -0
  369. package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
  370. package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
  371. package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
  372. package/dist/ui/index.html +3 -3
  373. package/dist/ui/product/agent manager.png +0 -0
  374. package/dist/ui/product/agent_manager_02.png +0 -0
  375. package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
  376. package/dist/utils/aiProfileEvents.d.ts +7 -0
  377. package/dist/utils/aiProfileEvents.js +6 -0
  378. package/dist/utils/assignees.d.ts +8 -0
  379. package/dist/utils/assignees.js +10 -1
  380. package/dist/utils/assignees.test.d.ts +1 -0
  381. package/dist/utils/assignees.test.js +25 -0
  382. package/dist/utils/avatarDisplay.d.ts +1 -0
  383. package/dist/utils/avatarDisplay.js +15 -0
  384. package/dist/utils/avatarUpload.d.ts +27 -0
  385. package/dist/utils/avatarUpload.js +132 -0
  386. package/dist/utils/avatarUpload.test.d.ts +1 -0
  387. package/dist/utils/avatarUpload.test.js +40 -0
  388. package/dist/utils/bootstrapTrace.d.ts +7 -0
  389. package/dist/utils/bootstrapTrace.js +29 -0
  390. package/dist/utils/commercialLifecyclePresentation.js +3 -0
  391. package/dist/utils/contextAssetEvents.d.ts +2 -0
  392. package/dist/utils/contextFiles.d.ts +3 -1
  393. package/dist/utils/contextFiles.js +2 -1
  394. package/dist/utils/syncEventDetails.d.ts +3 -0
  395. package/dist/utils/syncEventDetails.js +64 -0
  396. package/dist/utils/syncEventDetails.test.d.ts +1 -0
  397. package/dist/utils/syncEventDetails.test.js +23 -0
  398. package/dist/utils/syncEventPresentation.d.ts +15 -0
  399. package/dist/utils/syncEventPresentation.js +129 -0
  400. package/dist/utils/syncEventPresentation.test.d.ts +1 -0
  401. package/dist/utils/syncEventPresentation.test.js +64 -0
  402. package/dist/utils/syncStatusPresentation.d.ts +72 -0
  403. package/dist/utils/syncStatusPresentation.js +217 -0
  404. package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
  405. package/dist/utils/syncStatusPresentation.test.js +164 -0
  406. package/dist/utils/taskActivity.d.ts +9 -1
  407. package/dist/utils/taskActivity.js +290 -22
  408. package/dist/utils/taskActivity.test.js +141 -8
  409. package/dist/utils/taskSearch.d.ts +22 -0
  410. package/dist/utils/taskSearch.js +87 -0
  411. package/dist/utils/taskSearch.test.d.ts +1 -0
  412. package/dist/utils/taskSearch.test.js +69 -0
  413. package/dist/utils/taskUtils.js +10 -3
  414. package/dist/utils/taskUtils.test.d.ts +1 -0
  415. package/dist/utils/taskUtils.test.js +19 -0
  416. package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
  417. package/dist/utils/workspaceSyncPresentation.js +68 -2
  418. package/dist/utils/workspaceSyncPresentation.test.js +36 -1
  419. package/package.json +3 -2
  420. package/scripts/export-sqlite-to-postgres.mjs +7 -9
  421. package/scripts/run-e2e-realtime.sh +17 -3
  422. package/scripts/run-smoke.sh +17 -5
  423. package/scripts/run-soak.sh +17 -5
  424. package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  425. package/src/resources/templates/workflows/collaborate.yaml +1 -1
  426. package/src/resources/templates/workflows/evaluate.yaml +2 -2
  427. package/src/resources/templates/workflows/plan.yaml +1 -1
  428. package/src/resources/templates/workflows/review.yaml +2 -2
  429. package/dist/ui/assets/AgentsModule-N2MnQBZk.js +0 -1
  430. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BG6P_GVW.js +0 -3
  431. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +0 -1
  432. package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +0 -1
  433. package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +0 -252
  434. package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
  435. package/dist/ui/assets/PlansPage-Cq0zXj3I.js +0 -1
  436. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  437. package/dist/ui/assets/TaskSettings-ln0aF7xc.js +0 -9
  438. package/dist/ui/assets/index-C2-OXZV7.css +0 -1
  439. package/dist/ui/assets/index-DcSI5F86.js +0 -6
  440. package/scripts/migrate-planning-model.ts +0 -233
@@ -3,17 +3,18 @@ import { parseJsonBody } from './primitives.js';
3
3
  import { normalizeTaskDataRelativePath, ObjectStorageClient } from '../../storage/objectStorageClient.js';
4
4
  import { WorkspaceAssetStore, } from '../../storage/workspaceAssetStore.js';
5
5
  import { resolveSessionCookieAttributesForRequest } from '../auth.js';
6
- import { decryptDocument, ENCRYPTION_PREFIX } from '../../sync/encryptionService.js';
7
- import { isAiProfileSurfaceType } from '../../shared/aiProfileSurfaceType.js';
8
- import { serializeTaskForSync, } from '../../sync/taskSyncPayload.js';
6
+ import { isAiProfileSurfaceType, parseAiProfileSurfaceType } from '../../shared/aiProfileSurfaceType.js';
7
+ import { isAiProfileSeatScope } from '../../shared/aiProfileSeatScope.js';
9
8
  import { serializeBinaryAssetSyncPayload, serializeDocumentSyncPayload, } from '../../sync/contentSyncPayload.js';
10
9
  import { createContentSyncState } from '../../sync/contentSyncState.js';
11
10
  import { normalizeAiProfileSyncPayload, normalizeAnnotatedAttachmentSessionSyncPayload, normalizeDocumentReviewCommentSyncPayload, normalizeDocumentReviewSessionSyncPayload, } from '../../sync/collaborationSyncPayload.js';
12
11
  import { createCollaborationSyncState } from '../../sync/collaborationSyncState.js';
13
- import { buildWorkspacePullFeed } from '../../sync/workspacePullFeed.js';
14
12
  import { normalizeInitiativeSyncPayload, normalizeTaxonomySyncPayload, normalizeWorkstreamSyncPayload, } from '../../sync/planningSyncPayload.js';
15
13
  import { applyAiProfileAndMemberSyncChanges, applyCollaborationSyncChanges, applyContentSyncChanges, applyPlanningSyncChanges, applyPreparedTaskSyncSnapshot, applyTaskActivitySyncChanges, applyTaskSyncDeletes, classifyWorkspaceSyncChanges, prepareTaskSyncSnapshotApply, } from '../../sync/syncApplyHandlers.js';
16
- import { applyWorkspaceRepair } from '../../sync/workspaceRepair.js';
14
+ import { registerWorkspaceSyncAuxRoutes } from './syncAuxRoutes.js';
15
+ import { registerWorkspacePullApplySyncRoutes } from './syncPullApplyRoutes.js';
16
+ import { registerWorkspacePushSyncRoutes } from './syncPushRoutes.js';
17
+ import { registerWorkspaceSnapshotSyncRoutes } from './syncSnapshotRoutes.js';
17
18
  function decodeWorkspaceSyncCursor(raw) {
18
19
  const value = String(raw || '').trim();
19
20
  if (!value)
@@ -51,6 +52,8 @@ function normalizeIncomingAiProfileSurfaceType(value, context) {
51
52
  const normalized = String(value || '').trim();
52
53
  if (!normalized)
53
54
  return null;
55
+ if (normalized === 'other')
56
+ return parseAiProfileSurfaceType(normalized);
54
57
  if (!isAiProfileSurfaceType(normalized)) {
55
58
  throw createSyncRouteError(`Invalid ai profile surfaceType "${normalized}"`, {
56
59
  ...context,
@@ -59,16 +62,36 @@ function normalizeIncomingAiProfileSurfaceType(value, context) {
59
62
  }
60
63
  return normalized;
61
64
  }
65
+ function normalizeIncomingAiProfileSeatScope(value, context) {
66
+ if (value === undefined || value === null)
67
+ return null;
68
+ const normalized = String(value || '').trim();
69
+ if (!normalized)
70
+ return null;
71
+ if (!isAiProfileSeatScope(normalized)) {
72
+ throw createSyncRouteError(`Invalid ai profile seatScope "${normalized}"`, {
73
+ ...context,
74
+ seatScope: normalized
75
+ }, { code: 'AI_PROFILE_SEAT_SCOPE_INVALID', statusCode: 400 });
76
+ }
77
+ return normalized;
78
+ }
62
79
  function normalizeTaskEventSyncPayload(raw, workspaceId, sourceWatermark) {
63
80
  if (!raw || typeof raw !== 'object' || Array.isArray(raw))
64
81
  return null;
65
82
  const source = raw;
66
83
  const id = String(source.id || '').trim();
84
+ const entityType = source.entityType === 'initiative' || source.entityType === 'workstream'
85
+ ? source.entityType
86
+ : source.entity_type === 'initiative' || source.entity_type === 'workstream'
87
+ ? source.entity_type
88
+ : 'task';
67
89
  const taskId = String(source.taskId || source.task_id || '').trim();
90
+ const entityId = String(source.entityId || source.entity_id || taskId).trim();
68
91
  const action = String(source.action || '').trim();
69
92
  const actor = String(source.actor || 'system').trim() || 'system';
70
93
  const createdAt = String(source.createdAt || source.created_at || sourceWatermark || '').trim();
71
- if (!id || !taskId || !action || !Number.isFinite(Date.parse(createdAt)))
94
+ if (!id || !entityId || !action || !Number.isFinite(Date.parse(createdAt)))
72
95
  return null;
73
96
  const actorType = source.actorType === 'ai' || source.actorType === 'human' || source.actorType === 'system'
74
97
  ? source.actorType
@@ -84,8 +107,10 @@ function normalizeTaskEventSyncPayload(raw, workspaceId, sourceWatermark) {
84
107
  : {};
85
108
  return {
86
109
  id,
87
- taskId,
88
110
  workspaceId,
111
+ entityType,
112
+ entityId,
113
+ taskId: entityType === 'task' ? (taskId || entityId) : undefined,
89
114
  action,
90
115
  actor,
91
116
  actorType,
@@ -155,7 +180,7 @@ export function registerSyncRoutes(deps) {
155
180
  const assertSyncedAiProfilesPersisted = (workspaceId, incomingAiProfiles) => {
156
181
  if (incomingAiProfiles.size === 0)
157
182
  return;
158
- const persistedIds = new Set(core.listAiProfiles(workspaceId)
183
+ const persistedIds = new Set(core.listAiProfiles(workspaceId, { includeArchived: true })
159
184
  .map((profile) => String(profile.id || '').trim())
160
185
  .filter(Boolean));
161
186
  for (const profile of incomingAiProfiles.values()) {
@@ -240,6 +265,7 @@ export function registerSyncRoutes(deps) {
240
265
  normalizeWorkspaceSyncDocumentPath,
241
266
  normalizeTaskAssetPath: (rawPath) => normalizeTaskDataRelativePath(String(rawPath || '').trim()),
242
267
  normalizeIncomingAiProfileSurfaceType,
268
+ normalizeIncomingAiProfileSeatScope,
243
269
  normalizeTaskEventSyncPayload,
244
270
  recordSyncDiagnostic,
245
271
  });
@@ -624,7 +650,7 @@ export function registerSyncRoutes(deps) {
624
650
  workstream
625
651
  })),
626
652
  ...incomingAiProfiles
627
- .map((profile) => normalizeAiProfileSyncPayload(profile, cloudWorkspaceId, normalizeIncomingAiProfileSurfaceType, 'push.aiProfileUpsert'))
653
+ .map((profile) => normalizeAiProfileSyncPayload(profile, cloudWorkspaceId, normalizeIncomingAiProfileSurfaceType, normalizeIncomingAiProfileSeatScope, 'push.aiProfileUpsert'))
628
654
  .filter((profile) => Boolean(profile && profile.id.length > 0))
629
655
  .map((profile) => ({
630
656
  op: 'ai-profile-upsert',
@@ -722,607 +748,47 @@ export function registerSyncRoutes(deps) {
722
748
  }));
723
749
  }
724
750
  });
725
- // GET /api/taskforce/sync/workspace/documents - Snapshot markdown documents only.
726
- // Binary image/file assets travel through the sibling /assets snapshot and asset-* sync ops.
727
- addRoute('GET', '/api/taskforce/sync/workspace/documents', async (req, res) => {
728
- const authConfig = context.authConfig;
729
- if (authConfig?.runtimeMode === 'cloud') {
730
- const access = resolveRequestAccess(req, authConfig);
731
- if (!access.authenticated || !access.userId || access.userId === 'anonymous') {
732
- res.writeHead(401, { 'Content-Type': 'application/json' });
733
- res.end(JSON.stringify({ success: false, error: 'Authentication required.' }));
734
- return;
735
- }
736
- }
737
- const workspaceId = requestWorkspaceId(req) || 'default';
738
- const basePath = core.getPaths().basePath;
739
- const documents = await contentSyncState.listWorkspaceSyncDocumentsSnapshot(basePath, workspaceId);
740
- const fingerprint = JSON.stringify(documents.map((doc) => [
741
- doc.path,
742
- doc.updatedAt,
743
- doc.content.length,
744
- doc.assetId || '',
745
- doc.documentId || '',
746
- String(doc.version ?? ''),
747
- doc.taskId || '',
748
- doc.linkRole || ''
749
- ].join(':')));
750
- res.writeHead(200, { 'Content-Type': 'application/json' });
751
- res.end(JSON.stringify({
752
- success: true,
753
- documents,
754
- fingerprint
755
- }));
756
- });
757
- // GET /api/taskforce/sync/workspace/ai-profiles - Snapshot workspace AI profiles for local->cloud sync.
758
- addRoute('GET', '/api/taskforce/sync/workspace/ai-profiles', async (req, res) => {
759
- const authConfig = context.authConfig;
760
- if (authConfig?.runtimeMode === 'cloud') {
761
- const access = resolveRequestAccess(req, authConfig);
762
- if (!access.authenticated || !access.userId || access.userId === 'anonymous') {
763
- res.writeHead(401, { 'Content-Type': 'application/json' });
764
- res.end(JSON.stringify({ success: false, error: 'Authentication required.' }));
765
- return;
766
- }
767
- }
768
- const workspaceId = requestWorkspaceId(req) || 'default';
769
- const aiProfiles = core.listAiProfiles(workspaceId);
770
- const fingerprint = JSON.stringify(aiProfiles.map((profile) => [
771
- profile.id,
772
- profile.updatedAt,
773
- profile.name,
774
- profile.username,
775
- profile.profileToken,
776
- profile.surfaceType ?? ''
777
- ].join(':')));
778
- res.writeHead(200, { 'Content-Type': 'application/json' });
779
- res.end(JSON.stringify({
780
- success: true,
781
- aiProfiles,
782
- fingerprint
783
- }));
784
- });
785
- // GET /api/taskforce/sync/workspace/document-reviews - Snapshot document review sessions for local->cloud sync.
786
- addRoute('GET', '/api/taskforce/sync/workspace/document-reviews', async (req, res) => {
787
- const authConfig = context.authConfig;
788
- if (authConfig?.runtimeMode === 'cloud') {
789
- const access = resolveRequestAccess(req, authConfig);
790
- if (!access.authenticated || !access.userId || access.userId === 'anonymous') {
791
- res.writeHead(401, { 'Content-Type': 'application/json' });
792
- res.end(JSON.stringify({ success: false, error: 'Authentication required.' }));
793
- return;
794
- }
795
- }
796
- const workspaceId = requestWorkspaceId(req) || 'default';
797
- const sessions = collaborationSyncState.listWorkspaceSyncDocumentReviewSessionsSnapshot(workspaceId);
798
- const comments = collaborationSyncState.listWorkspaceSyncDocumentReviewCommentsSnapshot(workspaceId);
799
- const fingerprint = JSON.stringify([
800
- ...sessions.map((session) => [
801
- 'session',
802
- session.id,
803
- session.updatedAt,
804
- session.status,
805
- session.comments.length
806
- ].join(':')),
807
- ...comments.map((comment) => [
808
- 'comment',
809
- comment.id,
810
- comment.sessionId,
811
- comment.updatedAt,
812
- comment.deletedAt || ''
813
- ].join(':'))
814
- ]);
815
- res.writeHead(200, { 'Content-Type': 'application/json' });
816
- res.end(JSON.stringify({
817
- success: true,
818
- sessions,
819
- comments,
820
- fingerprint
821
- }));
822
- });
823
- // GET /api/taskforce/sync/workspace/annotated-attachments - Snapshot Image Notes sessions for local->cloud sync.
824
- addRoute('GET', '/api/taskforce/sync/workspace/annotated-attachments', async (req, res) => {
825
- const authConfig = context.authConfig;
826
- if (authConfig?.runtimeMode === 'cloud') {
827
- const access = resolveRequestAccess(req, authConfig);
828
- if (!access.authenticated || !access.userId || access.userId === 'anonymous') {
829
- res.writeHead(401, { 'Content-Type': 'application/json' });
830
- res.end(JSON.stringify({ success: false, error: 'Authentication required.' }));
831
- return;
832
- }
833
- }
834
- const workspaceId = requestWorkspaceId(req) || 'default';
835
- const sessions = collaborationSyncState.listWorkspaceSyncAnnotatedAttachmentSessionsSnapshot(workspaceId);
836
- const fingerprint = JSON.stringify(sessions.map((session) => [
837
- session.id,
838
- session.updatedAt,
839
- session.taskId,
840
- session.baseImageAssetId,
841
- session.annotations.length,
842
- session.deletedAt || ''
843
- ].join(':')));
844
- res.writeHead(200, { 'Content-Type': 'application/json' });
845
- res.end(JSON.stringify({
846
- success: true,
847
- sessions,
848
- fingerprint
849
- }));
850
- });
851
- // GET /api/taskforce/sync/workspace/assets - Snapshot non-markdown local assets.
852
- // Markdown remains on the /documents snapshot so document encryption/version semantics stay isolated.
853
- addRoute('GET', '/api/taskforce/sync/workspace/assets', async (req, res) => {
854
- const authConfig = context.authConfig;
855
- if (authConfig?.runtimeMode === 'cloud') {
856
- const access = resolveRequestAccess(req, authConfig);
857
- if (!access.authenticated || !access.userId || access.userId === 'anonymous') {
858
- res.writeHead(401, { 'Content-Type': 'application/json' });
859
- res.end(JSON.stringify({ success: false, error: 'Authentication required.' }));
860
- return;
861
- }
862
- }
863
- const workspaceId = requestWorkspaceId(req) || 'default';
864
- const basePath = core.getPaths().basePath;
865
- const assets = await contentSyncState.listWorkspaceSyncBinaryAssetsSnapshot(basePath, workspaceId);
866
- const fingerprint = JSON.stringify(assets.map((asset) => [
867
- asset.path,
868
- asset.updatedAt,
869
- asset.contentBase64.length,
870
- asset.assetId || '',
871
- asset.kind,
872
- asset.mimeType,
873
- asset.taskId || '',
874
- asset.originalFilename || '',
875
- asset.linkRole || ''
876
- ].join(':')));
877
- res.writeHead(200, { 'Content-Type': 'application/json' });
878
- res.end(JSON.stringify({
879
- success: true,
880
- assets,
881
- fingerprint
882
- }));
883
- });
884
- // GET /api/taskforce/sync/workspace/pull - Incremental workspace task deltas with cursor paging
885
- addRoute('GET', '/api/taskforce/sync/workspace/pull', async (req, res) => {
886
- const authConfig = context.authConfig;
887
- if (!authConfig || !authConfig.enabled) {
888
- res.writeHead(400, { 'Content-Type': 'application/json' });
889
- res.end(JSON.stringify({ success: false, error: 'Sync is unavailable when auth is disabled.' }));
890
- return;
891
- }
892
- const access = resolveRequestAccess(req, authConfig);
893
- if (!access.authenticated || !access.userId || access.userId === 'anonymous') {
894
- auditAuthAction(req, 'sync-workspace-pull-unauthorized', {});
895
- res.writeHead(401, { 'Content-Type': 'application/json' });
896
- res.end(JSON.stringify({ success: false, error: 'Authentication required.' }));
897
- return;
898
- }
899
- const url = new URL(req.url || '/api/taskforce/sync/workspace/pull', 'http://localhost');
900
- const requestedWorkspaceId = String(url.searchParams.get('workspaceId') || '').trim();
901
- if (!requestedWorkspaceId) {
902
- auditAuthAction(req, 'sync-workspace-pull-missing-workspace', { code: 'WORKSPACE_ID_REQUIRED' });
903
- res.writeHead(400, { 'Content-Type': 'application/json' });
904
- res.end(JSON.stringify({
905
- success: false,
906
- error: 'workspaceId is required.',
907
- code: 'WORKSPACE_ID_REQUIRED',
908
- workspaceId: null
909
- }));
910
- return;
911
- }
912
- const allowed = core.listUserWorkspaces(access.userId).find((workspace) => workspace.id === requestedWorkspaceId);
913
- if (!allowed) {
914
- auditAuthAction(req, 'sync-workspace-pull-forbidden', {
915
- requestedWorkspaceId
916
- });
917
- res.writeHead(403, { 'Content-Type': 'application/json' });
918
- res.end(JSON.stringify({
919
- success: false,
920
- error: 'You do not have access to this workspace.',
921
- code: 'WORKSPACE_ACCESS_DENIED',
922
- workspaceId: requestedWorkspaceId
923
- }));
924
- return;
925
- }
926
- const targetWorkspaceId = requestedWorkspaceId;
927
- const cursor = decodeWorkspaceSyncCursor(url.searchParams.get('cursor'));
928
- const requestedLimit = Number.parseInt(String(url.searchParams.get('limit') || ''), 10);
929
- const pageSize = Number.isFinite(requestedLimit) && requestedLimit > 0
930
- ? Math.max(1, Math.min(500, Math.floor(requestedLimit)))
931
- : 200;
932
- const { workspaceMembers, changes, nextCursor, hasMore, diagnostics } = await buildWorkspacePullFeed({
933
- core,
934
- workspaceId: targetWorkspaceId,
935
- cursor,
936
- pageSize,
937
- encodeWorkspaceSyncCursor,
938
- contentSyncState,
939
- collaborationSyncState,
940
- });
941
- res.writeHead(200, { 'Content-Type': 'application/json' });
942
- res.end(JSON.stringify({
943
- success: true,
944
- workspaceId: targetWorkspaceId,
945
- workspaceMembers,
946
- changes,
947
- nextCursor,
948
- hasMore,
949
- diagnostics
950
- }));
951
- auditAuthAction(req, 'sync-workspace-pull-success', {
952
- workspaceId: targetWorkspaceId,
953
- returnedChanges: changes.length,
954
- hasMore,
955
- diagnostics
956
- });
957
- });
958
- // POST /api/taskforce/sync/workspace/apply-local - Apply pulled cloud deltas into local workspace store
959
- addRoute('POST', '/api/taskforce/sync/workspace/apply-local', async (req, res) => {
960
- const startedAtMs = Date.now();
961
- const authConfig = context.authConfig;
962
- if (authConfig?.runtimeMode === 'cloud') {
963
- res.writeHead(400, { 'Content-Type': 'application/json' });
964
- res.end(JSON.stringify({ success: false, error: 'Local apply endpoint is only available in local runtime.' }));
965
- return;
966
- }
967
- const body = await parseJsonBody(req, 50 * 1024 * 1024);
968
- const bodyWorkspaceId = String(body?.workspaceId || '').trim();
969
- const headerWorkspaceId = String(Array.isArray(req.headers['x-taskforce-workspace-id'])
970
- ? req.headers['x-taskforce-workspace-id'][0]
971
- : req.headers['x-taskforce-workspace-id'] || '').trim();
972
- let workspaceId = bodyWorkspaceId || headerWorkspaceId || requestWorkspaceId(req);
973
- if (authConfig?.enabled) {
974
- const access = resolveRequestAccess(req, authConfig);
975
- if (!access.authenticated || !access.userId || access.userId === 'anonymous') {
976
- auditAuthAction(req, 'sync-workspace-apply-unauthorized', {});
977
- res.writeHead(401, { 'Content-Type': 'application/json' });
978
- res.end(JSON.stringify({ success: false, error: 'Authentication required.' }));
979
- return;
980
- }
981
- if (workspaceId && workspaceId !== 'default' && workspaceId !== 'null') {
982
- const allowed = core.listUserWorkspaces(access.userId).find((workspace) => workspace.id === workspaceId);
983
- if (!allowed) {
984
- auditAuthAction(req, 'sync-workspace-apply-forbidden', {
985
- requestedWorkspaceId: workspaceId
986
- });
987
- res.writeHead(403, { 'Content-Type': 'application/json' });
988
- res.end(JSON.stringify({
989
- success: false,
990
- error: 'You do not have access to this workspace.',
991
- code: 'WORKSPACE_ACCESS_DENIED',
992
- workspaceId
993
- }));
994
- return;
995
- }
996
- }
997
- else {
998
- const accessWorkspaceId = String(access.workspaceId || '').trim();
999
- if (accessWorkspaceId && accessWorkspaceId !== 'default') {
1000
- workspaceId = accessWorkspaceId;
1001
- }
1002
- }
1003
- }
1004
- if (!workspaceId || workspaceId === 'default' || workspaceId === 'null') {
1005
- res.writeHead(400, { 'Content-Type': 'application/json' });
1006
- res.end(JSON.stringify({
1007
- success: false,
1008
- error: 'Workspace ID required.',
1009
- code: 'WORKSPACE_ID_REQUIRED'
1010
- }));
1011
- return;
1012
- }
1013
- const incomingChanges = Array.isArray(body?.changes) ? body.changes : [];
1014
- const workspaceMembers = Array.isArray(body?.workspaceMembers)
1015
- ? body.workspaceMembers
1016
- .map((entry) => normalizeWorkspaceMemberSyncSnapshot(workspaceId, entry && typeof entry === 'object' ? entry : null))
1017
- .filter((entry) => Boolean(entry))
1018
- : undefined;
1019
- const allowReferenceNumberReassignment = body?.repair === true;
1020
- try {
1021
- const { result, emittedEventIds } = await dispatchWorkspaceSyncChanges(workspaceId, incomingChanges, {
1022
- allowReferenceNumberReassignment,
1023
- ...(Array.isArray(workspaceMembers) ? { workspaceMembers } : {})
1024
- });
1025
- const durationMs = Date.now() - startedAtMs;
1026
- core.recordSyncEvent(workspaceId, {
1027
- eventType: 'apply',
1028
- status: 'success',
1029
- changeCount: incomingChanges.length,
1030
- requestMs: durationMs
1031
- });
1032
- res.writeHead(200, { 'Content-Type': 'application/json' });
1033
- res.end(JSON.stringify({
1034
- success: true,
1035
- workspaceId,
1036
- result,
1037
- emittedEventIds,
1038
- durationMs
1039
- }));
1040
- }
1041
- catch (error) {
1042
- const statusCode = resolveErrorStatusCode(error, 400);
1043
- const durationMs = Date.now() - startedAtMs;
1044
- core.recordSyncEvent(workspaceId, {
1045
- eventType: 'apply',
1046
- status: 'error',
1047
- statusCode,
1048
- errorMessage: String(error?.message || 'Failed to apply local workspace deltas'),
1049
- changeCount: incomingChanges.length,
1050
- requestMs: durationMs
1051
- });
1052
- res.writeHead(statusCode, { 'Content-Type': 'application/json' });
1053
- res.end(JSON.stringify({
1054
- success: false,
1055
- error: String(error?.message || 'Failed to apply local workspace deltas'),
1056
- code: error?.code,
1057
- details: error?.details || undefined,
1058
- durationMs
1059
- }));
1060
- }
1061
- });
1062
- // POST /api/taskforce/sync/workspace/push - Incremental workspace deltas from local to cloud
1063
- addRoute('POST', '/api/taskforce/sync/workspace/push', async (req, res) => {
1064
- const startedAtMs = Date.now();
1065
- const authConfig = context.authConfig;
1066
- if (!authConfig || !authConfig.enabled) {
1067
- res.writeHead(400, { 'Content-Type': 'application/json' });
1068
- res.end(JSON.stringify({ success: false, error: 'Sync is unavailable when auth is disabled.' }));
1069
- return;
1070
- }
1071
- const access = resolveRequestAccess(req, authConfig);
1072
- if (!access.authenticated || !access.userId || access.userId === 'anonymous') {
1073
- auditAuthAction(req, 'sync-workspace-push-unauthorized', {});
1074
- res.writeHead(401, { 'Content-Type': 'application/json' });
1075
- res.end(JSON.stringify({ success: false, error: 'Authentication required.' }));
1076
- return;
1077
- }
1078
- const body = await parseJsonBody(req, 50 * 1024 * 1024);
1079
- const requestedWorkspaceId = String(body?.workspaceId || '').trim();
1080
- if (!requestedWorkspaceId) {
1081
- auditAuthAction(req, 'sync-workspace-push-missing-workspace', { code: 'WORKSPACE_ID_REQUIRED' });
1082
- res.writeHead(400, { 'Content-Type': 'application/json' });
1083
- res.end(JSON.stringify({
1084
- success: false,
1085
- error: 'workspaceId is required.',
1086
- code: 'WORKSPACE_ID_REQUIRED',
1087
- workspaceId: null,
1088
- durationMs: Date.now() - startedAtMs
1089
- }));
1090
- return;
1091
- }
1092
- const allowed = core.listUserWorkspaces(access.userId).find((workspace) => workspace.id === requestedWorkspaceId);
1093
- if (!allowed) {
1094
- auditAuthAction(req, 'sync-workspace-push-forbidden', {
1095
- requestedWorkspaceId
1096
- });
1097
- res.writeHead(403, { 'Content-Type': 'application/json' });
1098
- res.end(JSON.stringify({
1099
- success: false,
1100
- error: 'You do not have access to this workspace.',
1101
- code: 'WORKSPACE_ACCESS_DENIED',
1102
- workspaceId: requestedWorkspaceId
1103
- }));
1104
- return;
1105
- }
1106
- const targetWorkspaceId = requestedWorkspaceId;
1107
- const idempotencyKey = String(body?.idempotencyKey || '').trim();
1108
- if (idempotencyKey) {
1109
- const replay = readPushIdempotency(targetWorkspaceId, idempotencyKey);
1110
- if (replay) {
1111
- auditAuthAction(req, 'sync-workspace-push-idempotent-replay', {
1112
- workspaceId: targetWorkspaceId,
1113
- idempotencyKey
1114
- });
1115
- res.writeHead(200, { 'Content-Type': 'application/json' });
1116
- res.end(replay);
1117
- return;
1118
- }
1119
- }
1120
- let incomingChanges = Array.isArray(body?.changes) ? body.changes : [];
1121
- const incomingTaskUpsertIds = incomingChanges
1122
- .filter((change) => change?.op === 'upsert')
1123
- .map((change) => String(change.task?.id || '').trim())
1124
- .filter((taskId) => taskId.length > 0);
1125
- const allowReferenceNumberReassignment = body?.repair === true;
1126
- // Decrypt asset and document content if TASKFORCE_SYNC_KEY is configured on this server.
1127
- // The local runtime encrypts content before pushing; the cloud must decrypt before writing to storage.
1128
- if (typeof process !== 'undefined' && process.env?.TASKFORCE_SYNC_KEY) {
1129
- const syncKey = process.env.TASKFORCE_SYNC_KEY;
1130
- const decryptedChanges = [];
1131
- for (const change of incomingChanges) {
1132
- if (change.op === 'asset-upsert' && typeof change.contentBase64 === 'string' && change.contentBase64.startsWith(ENCRYPTION_PREFIX)) {
1133
- try {
1134
- decryptedChanges.push({ ...change, contentBase64: await decryptDocument(change.contentBase64, syncKey) });
1135
- }
1136
- catch {
1137
- decryptedChanges.push(change);
1138
- }
1139
- }
1140
- else if (change.op === 'document-upsert' && typeof change.content === 'string' && change.content.startsWith(ENCRYPTION_PREFIX)) {
1141
- try {
1142
- decryptedChanges.push({ ...change, content: await decryptDocument(change.content, syncKey) });
1143
- }
1144
- catch {
1145
- decryptedChanges.push(change);
1146
- }
1147
- }
1148
- else {
1149
- decryptedChanges.push(change);
1150
- }
1151
- }
1152
- incomingChanges = decryptedChanges;
1153
- }
1154
- const receivedAtBaseMs = Date.now();
1155
- incomingChanges = incomingChanges.map((change, index) => {
1156
- if (change.op !== 'upsert')
1157
- return change;
1158
- const task = change.task;
1159
- const rawReferenceNumber = task.referenceNumber;
1160
- const incomingReferenceNumber = rawReferenceNumber === undefined || rawReferenceNumber === null
1161
- ? null
1162
- : (Number.isFinite(Number(rawReferenceNumber))
1163
- ? Math.max(1, Math.floor(Number(rawReferenceNumber)))
1164
- : null);
1165
- if (incomingReferenceNumber)
1166
- return change;
1167
- return {
1168
- ...change,
1169
- task: {
1170
- ...task,
1171
- updatedAt: new Date(receivedAtBaseMs + index).toISOString()
1172
- }
1173
- };
1174
- });
1175
- try {
1176
- const { result, deleteCount, emittedEventIds } = await dispatchWorkspaceSyncChanges(targetWorkspaceId, incomingChanges, {
1177
- allowReferenceNumberReassignment
1178
- });
1179
- const appliedTaskUpserts = incomingTaskUpsertIds
1180
- .map((taskId) => core.getTask(taskId, targetWorkspaceId))
1181
- .filter((task) => Boolean(task))
1182
- .map((task) => ({
1183
- archived: Boolean(task.isArchived),
1184
- task: serializeTaskForSync(task)
1185
- }));
1186
- const durationMs = Date.now() - startedAtMs;
1187
- const payload = JSON.stringify({
1188
- success: true,
1189
- workspaceId: targetWorkspaceId,
1190
- idempotencyKey: idempotencyKey || null,
1191
- emittedEventIds,
1192
- appliedTaskUpserts,
1193
- result,
1194
- durationMs
1195
- });
1196
- if (idempotencyKey) {
1197
- writePushIdempotency(targetWorkspaceId, idempotencyKey, payload);
1198
- }
1199
- core.recordSyncEvent(targetWorkspaceId, {
1200
- eventType: 'push',
1201
- status: 'success',
1202
- changeCount: incomingChanges.length,
1203
- requestMs: durationMs
1204
- });
1205
- res.writeHead(200, { 'Content-Type': 'application/json' });
1206
- res.end(payload);
1207
- auditAuthAction(req, 'sync-workspace-push-success', {
1208
- workspaceId: targetWorkspaceId,
1209
- idempotencyKey: idempotencyKey || null,
1210
- changeCount: incomingChanges.length,
1211
- deleteCount: deleteCount,
1212
- durationMs
1213
- });
1214
- }
1215
- catch (error) {
1216
- const statusCode = resolveErrorStatusCode(error, 400);
1217
- const durationMs = Date.now() - startedAtMs;
1218
- core.recordSyncEvent(targetWorkspaceId, {
1219
- eventType: 'push',
1220
- status: 'error',
1221
- statusCode,
1222
- errorMessage: String(error?.message || 'Failed to apply workspace deltas'),
1223
- changeCount: incomingChanges.length,
1224
- requestMs: durationMs
1225
- });
1226
- auditAuthAction(req, 'sync-workspace-push-failed', {
1227
- workspaceId: targetWorkspaceId,
1228
- code: error?.code || null,
1229
- statusCode,
1230
- details: error?.details || null,
1231
- durationMs
1232
- });
1233
- res.writeHead(statusCode, { 'Content-Type': 'application/json' });
1234
- res.end(JSON.stringify({
1235
- success: false,
1236
- error: String(error?.message || 'Failed to apply workspace deltas'),
1237
- code: error?.code,
1238
- details: error?.details || undefined,
1239
- durationMs
1240
- }));
1241
- }
751
+ registerWorkspaceSnapshotSyncRoutes({
752
+ addRoute,
753
+ core,
754
+ context,
755
+ resolveRequestAccess,
756
+ requestWorkspaceId,
757
+ contentSyncState,
758
+ collaborationSyncState
1242
759
  });
1243
- // GET /api/taskforce/sync/events - Query recent sync events (local runtime only)
1244
- addRoute('GET', '/api/taskforce/sync/events', async (req, res) => {
1245
- const authConfig = context.authConfig;
1246
- const access = authConfig?.enabled ? resolveRequestAccess(req, authConfig) : null;
1247
- if (access && (!access.authenticated || !access.userId || access.userId === 'anonymous')) {
1248
- res.writeHead(401, { 'Content-Type': 'application/json' });
1249
- res.end(JSON.stringify({ success: false, error: 'Authentication required.' }));
1250
- return;
1251
- }
1252
- const url = new URL(req.url || '/', 'http://localhost');
1253
- const workspaceId = String(url.searchParams.get('workspace_id') || access?.workspaceId || '').trim() || 'default';
1254
- const limit = Math.min(500, Math.max(1, Number(url.searchParams.get('limit') || '100')));
1255
- const events = core.listSyncEvents(workspaceId, limit);
1256
- res.writeHead(200, { 'Content-Type': 'application/json' });
1257
- res.end(JSON.stringify({ success: true, events }));
760
+ registerWorkspacePullApplySyncRoutes({
761
+ addRoute,
762
+ core,
763
+ context,
764
+ auditAuthAction,
765
+ resolveErrorStatusCode,
766
+ requestWorkspaceId,
767
+ resolveRequestAccess,
768
+ contentSyncState,
769
+ collaborationSyncState,
770
+ decodeWorkspaceSyncCursor,
771
+ encodeWorkspaceSyncCursor,
772
+ normalizeWorkspaceMemberSyncSnapshot,
773
+ dispatchWorkspaceSyncChanges
1258
774
  });
1259
- // POST /api/taskforce/sync/events - Record a client-side sync event (pull results, handshake outcomes)
1260
- addRoute('POST', '/api/taskforce/sync/events', async (req, res) => {
1261
- const authConfig = context.authConfig;
1262
- const access = authConfig?.enabled ? resolveRequestAccess(req, authConfig) : null;
1263
- if (access && (!access.authenticated || !access.userId || access.userId === 'anonymous')) {
1264
- res.writeHead(401, { 'Content-Type': 'application/json' });
1265
- res.end(JSON.stringify({ success: false, error: 'Authentication required.' }));
1266
- return;
1267
- }
1268
- const body = await parseJsonBody(req);
1269
- const allowedEventTypes = ['pull', 'handshake', 'bootstrap', 'apply', 'push'];
1270
- const eventType = String(body?.eventType || '').trim().toLowerCase();
1271
- if (!allowedEventTypes.includes(eventType)) {
1272
- res.writeHead(400, { 'Content-Type': 'application/json' });
1273
- res.end(JSON.stringify({ success: false, error: 'Invalid eventType.' }));
1274
- return;
1275
- }
1276
- const workspaceId = String(body?.workspaceId || access?.workspaceId || '').trim() || 'default';
1277
- core.recordSyncEvent(workspaceId, {
1278
- eventType: eventType,
1279
- status: String(body?.status || '') === 'error' ? 'error' : 'success',
1280
- statusCode: typeof body?.statusCode === 'number' ? body.statusCode : undefined,
1281
- errorMessage: typeof body?.errorMessage === 'string' ? body.errorMessage.slice(0, 500) : undefined,
1282
- changeCount: typeof body?.changeCount === 'number' ? body.changeCount : undefined,
1283
- requestMs: typeof body?.requestMs === 'number' ? body.requestMs : undefined,
1284
- details: body?.details && typeof body.details === 'object' && !Array.isArray(body.details)
1285
- ? body.details
1286
- : undefined
1287
- });
1288
- res.writeHead(200, { 'Content-Type': 'application/json' });
1289
- res.end(JSON.stringify({ success: true }));
775
+ registerWorkspacePushSyncRoutes({
776
+ addRoute,
777
+ core,
778
+ context,
779
+ auditAuthAction,
780
+ resolveErrorStatusCode,
781
+ resolveRequestAccess,
782
+ readPushIdempotency,
783
+ writePushIdempotency,
784
+ dispatchWorkspaceSyncChanges
1290
785
  });
1291
- // POST /api/taskforce/sync/workspace/repair-startup - Auto-repair missing asset/document
1292
- // metadata on workspace open. Only safe fixups: re-registers DB records for files that
1293
- // exist on disk but have no (or a stale deleted) metadata entry. Never deletes files.
1294
- // Called once by the sync orchestrator when the workspace becomes active.
1295
- addRoute('POST', '/api/taskforce/sync/workspace/repair-startup', async (req, res) => {
1296
- const authConfig = context.authConfig;
1297
- const access = authConfig?.enabled ? resolveRequestAccess(req, authConfig) : null;
1298
- if (access && (!access.authenticated || !access.userId || access.userId === 'anonymous')) {
1299
- res.writeHead(401, { 'Content-Type': 'application/json' });
1300
- res.end(JSON.stringify({ success: false, error: 'Authentication required.' }));
1301
- return;
1302
- }
1303
- const body = await parseJsonBody(req);
1304
- const workspaceId = String(body?.workspaceId || requestWorkspaceId(req) || '').trim() || 'default';
1305
- if (!workspaceAssetStore) {
1306
- res.writeHead(200, { 'Content-Type': 'application/json' });
1307
- res.end(JSON.stringify({ success: true, applied: 0, skipped: 0, failed: 0, detail: 'Asset store unavailable.' }));
1308
- return;
1309
- }
1310
- const { basePath } = core.getPaths();
1311
- const result = applyWorkspaceRepair({
1312
- workspaceId,
1313
- basePath,
1314
- tasks: core.listTasks(workspaceId)?.tasks || [],
1315
- archivedTasks: core.listArchive(workspaceId)?.archived || [],
1316
- deletedTasks: core.listWorkspaceSyncDeletes(workspaceId) || [],
1317
- workspaceAssetStore,
1318
- upsertDocumentWatermark: (targetWorkspaceId, docPath, contentHash, updatedAt) => {
1319
- core.upsertDocumentWatermark(targetWorkspaceId, docPath, contentHash, updatedAt);
1320
- }
1321
- });
1322
- const applied = result.actionsApplied.filter((a) => a.status === 'applied').length;
1323
- const failed = result.actionsApplied.filter((a) => a.status === 'failed').length;
1324
- const skipped = result.actionsApplied.filter((a) => a.status === 'skipped').length;
1325
- res.writeHead(200, { 'Content-Type': 'application/json' });
1326
- res.end(JSON.stringify({ success: true, applied, skipped, failed }));
786
+ registerWorkspaceSyncAuxRoutes({
787
+ addRoute,
788
+ core,
789
+ context,
790
+ resolveRequestAccess,
791
+ requestWorkspaceId,
792
+ workspaceAssetStore
1327
793
  });
1328
794
  }