@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
@@ -1,16 +1,35 @@
1
1
  function getActivityFieldLabel(field) {
2
+ if (field === 'taskId')
3
+ return 'Task';
2
4
  if (field === 'title')
3
- return 'TITLE';
5
+ return 'Title';
4
6
  if (field === 'description')
5
- return 'DESCRIPTION';
7
+ return 'Description';
6
8
  if (field === 'assignee')
7
- return 'ASSIGNED TO';
9
+ return 'Assigned to';
10
+ if (field === 'ownerId')
11
+ return 'Owner';
12
+ if (field === 'status')
13
+ return 'Status';
14
+ if (field === 'scheduledDate')
15
+ return 'Scheduled date';
16
+ if (field === 'dueDate')
17
+ return 'Due date';
8
18
  if (field === 'workstreamId')
9
- return 'WORKSTREAM';
19
+ return 'Workstream';
10
20
  if (field === 'initiativeId')
11
- return 'INITIATIVE';
21
+ return 'Initiative';
12
22
  return field;
13
23
  }
24
+ function formatStatusValue(value) {
25
+ if (value === 'task')
26
+ return 'Task';
27
+ return value
28
+ .replace(/[-_]+/g, ' ')
29
+ .replace(/\s+/g, ' ')
30
+ .trim()
31
+ .replace(/\b\w/g, (letter) => letter.toUpperCase());
32
+ }
14
33
  function formatValue(value, field, formatter) {
15
34
  const formatted = formatter?.(field, value);
16
35
  if (typeof formatted === 'string')
@@ -23,6 +42,8 @@ function formatValue(value, field, formatter) {
23
42
  }
24
43
  if (typeof value === 'object')
25
44
  return 'updated';
45
+ if (field === 'status')
46
+ return formatStatusValue(String(value));
26
47
  return String(value);
27
48
  }
28
49
  function normalizeChecklistItems(value) {
@@ -75,10 +96,131 @@ function summarizeChecklistChange(change) {
75
96
  }
76
97
  return 'Checklist updated';
77
98
  }
99
+ function stripQueryAndHash(value) {
100
+ return value.split(/[?#]/, 1)[0] || value;
101
+ }
102
+ function basenameFromPath(value) {
103
+ const normalized = stripQueryAndHash(value).replace(/\\/g, '/');
104
+ const segments = normalized.split('/').filter(Boolean);
105
+ return segments[segments.length - 1] || normalized;
106
+ }
107
+ function isImageAttachmentCandidate(value) {
108
+ return /\.(png|jpe?g|webp|gif)$/i.test(stripQueryAndHash(value));
109
+ }
110
+ function isDocumentAttachmentCandidate(value) {
111
+ if (value.includes('/api/taskforce/documents/'))
112
+ return true;
113
+ return /\.(pdf|txt|md|markdown|csv|json|docx?|html?|js|jsx|ts|tsx|css|py|java|go|rs|sh)$/i.test(stripQueryAndHash(value));
114
+ }
115
+ function normalizeAttachmentList(value) {
116
+ if (!Array.isArray(value))
117
+ return [];
118
+ return value.flatMap((entry, index) => {
119
+ const attachment = (entry && typeof entry === 'object' ? entry : null);
120
+ const path = typeof entry === 'string'
121
+ ? entry
122
+ : String(attachment?.path || '').trim();
123
+ const fsPath = String(attachment?.fsPath || '').trim();
124
+ const assetId = String(attachment?.assetId || '').trim();
125
+ const referenceLabel = String(attachment?.referenceLabel || '').trim();
126
+ const label = [
127
+ attachment?.displayName,
128
+ attachment?.originalFilename,
129
+ attachment?.caption,
130
+ referenceLabel,
131
+ fsPath ? basenameFromPath(fsPath) : '',
132
+ path ? basenameFromPath(path) : '',
133
+ ]
134
+ .map((candidate) => String(candidate || '').trim())
135
+ .find(Boolean) || `Attachment ${index + 1}`;
136
+ const key = assetId || fsPath || path || `${label}:${index}`;
137
+ const kindCandidates = [
138
+ path,
139
+ fsPath,
140
+ String(attachment?.originalFilename || '').trim(),
141
+ String(attachment?.displayName || '').trim(),
142
+ referenceLabel,
143
+ ].filter(Boolean);
144
+ const kind = kindCandidates.some((candidate) => isImageAttachmentCandidate(candidate))
145
+ ? 'image'
146
+ : (referenceLabel.startsWith('D-') || kindCandidates.some((candidate) => isDocumentAttachmentCandidate(candidate)))
147
+ ? 'document'
148
+ : 'attachment';
149
+ return [{ key, label, kind }];
150
+ });
151
+ }
152
+ function pluralizeAttachmentKind(kind, count) {
153
+ if (count === 1)
154
+ return kind;
155
+ if (kind === 'image')
156
+ return 'images';
157
+ if (kind === 'document')
158
+ return 'documents';
159
+ return 'attachments';
160
+ }
161
+ function describeAttachmentCollection(entries) {
162
+ if (entries.length === 0)
163
+ return 'attachments';
164
+ const counts = entries.reduce((acc, entry) => {
165
+ acc[entry.kind] += 1;
166
+ return acc;
167
+ }, { image: 0, document: 0, attachment: 0 });
168
+ const nonZeroParts = Object.entries(counts)
169
+ .filter(([, count]) => count > 0)
170
+ .map(([kind, count]) => count === 1 ? kind : `${count} ${pluralizeAttachmentKind(kind, count)}`);
171
+ if (nonZeroParts.length === 1)
172
+ return nonZeroParts[0];
173
+ if (entries.length <= 3 && nonZeroParts.length <= 2)
174
+ return nonZeroParts.join(' and ');
175
+ return `${entries.length} attachments`;
176
+ }
177
+ function capitalizeLeading(value) {
178
+ if (!value)
179
+ return value;
180
+ return value.charAt(0).toUpperCase() + value.slice(1);
181
+ }
182
+ function summarizeAttachmentChange(change) {
183
+ const before = normalizeAttachmentList(change?.from);
184
+ const after = normalizeAttachmentList(change?.to);
185
+ const beforeByKey = new Map(before.map((entry) => [entry.key, entry]));
186
+ const afterByKey = new Map(after.map((entry) => [entry.key, entry]));
187
+ const added = after.filter((entry) => !beforeByKey.has(entry.key));
188
+ const removed = before.filter((entry) => !afterByKey.has(entry.key));
189
+ if (added.length === 1 && removed.length === 0) {
190
+ return `${capitalizeLeading(added[0].kind)} attached: ${added[0].label}`;
191
+ }
192
+ if (removed.length === 1 && added.length === 0) {
193
+ return `${capitalizeLeading(removed[0].kind)} removed: ${removed[0].label}`;
194
+ }
195
+ if (added.length > 0 && removed.length === 0) {
196
+ return `${capitalizeLeading(describeAttachmentCollection(added))} attached`;
197
+ }
198
+ if (removed.length > 0 && added.length === 0) {
199
+ return `${capitalizeLeading(describeAttachmentCollection(removed))} removed`;
200
+ }
201
+ if (added.length === 1 && removed.length === 1) {
202
+ return `${capitalizeLeading(added[0].kind)} replaced: ${removed[0].label} -> ${added[0].label}`;
203
+ }
204
+ if (added.length > 0 || removed.length > 0) {
205
+ return `${capitalizeLeading(describeAttachmentCollection(added))} attached, ${describeAttachmentCollection(removed)} removed`;
206
+ }
207
+ const relabeled = after.filter((entry) => {
208
+ const previous = beforeByKey.get(entry.key);
209
+ return previous && previous.label !== entry.label;
210
+ });
211
+ if (relabeled.length === 1 && before.length === 1 && after.length === 1) {
212
+ const previous = beforeByKey.get(relabeled[0].key);
213
+ return `${capitalizeLeading(relabeled[0].kind)} renamed: ${previous?.label || 'Attachment'} -> ${relabeled[0].label}`;
214
+ }
215
+ return 'Attachment details updated';
216
+ }
78
217
  export function summarizeTaskChange(field, change, formatter) {
79
218
  if (field === 'checklistItems') {
80
219
  return summarizeChecklistChange(change);
81
220
  }
221
+ if (field === 'attachments') {
222
+ return summarizeAttachmentChange(change);
223
+ }
82
224
  const label = getActivityFieldLabel(field);
83
225
  if (field === 'title') {
84
226
  return `${label}: updated`;
@@ -108,22 +250,28 @@ export function summarizeTaskEvent(event, formatter) {
108
250
  if (event.action === 'task-unarchived')
109
251
  return 'Task restored';
110
252
  if (event.action === 'task-status-changed' && changes.status) {
111
- return `Status: ${formatValue(changes.status.from, 'status', formatter)} -> ${formatValue(changes.status.to, 'status', formatter)}`;
253
+ return `Status changed: ${formatValue(changes.status.from, 'status', formatter)} -> ${formatValue(changes.status.to, 'status', formatter)}`;
112
254
  }
113
255
  if (event.action === 'task-schedule-changed') {
114
256
  if (changes.scheduledDate) {
115
- return `Scheduled: ${formatValue(changes.scheduledDate.from, 'scheduledDate', formatter)} -> ${formatValue(changes.scheduledDate.to, 'scheduledDate', formatter)}`;
257
+ return `Scheduled date changed: ${formatValue(changes.scheduledDate.from, 'scheduledDate', formatter)} -> ${formatValue(changes.scheduledDate.to, 'scheduledDate', formatter)}`;
116
258
  }
117
259
  if (changes.dueDate) {
118
- return `Due: ${formatValue(changes.dueDate.from, 'dueDate', formatter)} -> ${formatValue(changes.dueDate.to, 'dueDate', formatter)}`;
260
+ return `Due date changed: ${formatValue(changes.dueDate.from, 'dueDate', formatter)} -> ${formatValue(changes.dueDate.to, 'dueDate', formatter)}`;
119
261
  }
120
262
  return 'Schedule updated';
121
263
  }
122
264
  if (event.action === 'task-relationship-changed' && changes.workstreamId) {
123
- return `Workstream: ${formatValue(changes.workstreamId.from, 'workstreamId', formatter)} -> ${formatValue(changes.workstreamId.to, 'workstreamId', formatter)}`;
265
+ return `Workstream changed: ${formatValue(changes.workstreamId.from, 'workstreamId', formatter)} -> ${formatValue(changes.workstreamId.to, 'workstreamId', formatter)}`;
266
+ }
267
+ if (event.action === 'task-relationship-changed' && changes.initiativeId) {
268
+ return `Initiative changed: ${formatValue(changes.initiativeId.from, 'initiativeId', formatter)} -> ${formatValue(changes.initiativeId.to, 'initiativeId', formatter)}`;
269
+ }
270
+ if (event.action === 'task-attachments-changed') {
271
+ return changes.attachments
272
+ ? summarizeTaskChange('attachments', changes.attachments, formatter)
273
+ : 'Attachments updated';
124
274
  }
125
- if (event.action === 'task-attachments-changed')
126
- return 'Attachments updated';
127
275
  if (fields.length === 1) {
128
276
  const field = fields[0];
129
277
  const change = changes[field];
@@ -133,25 +281,145 @@ export function summarizeTaskEvent(event, formatter) {
133
281
  return `${fields.length} fields updated`;
134
282
  return 'Task updated';
135
283
  }
136
- export function getLatestTaskActivity(task) {
137
- const items = Array.isArray(task.activity) && task.activity.length > 0
138
- ? task.activity
139
- : Array.isArray(task.comments)
140
- ? task.comments.map((comment) => ({
141
- id: `comment:${comment.id}`,
284
+ export function summarizeEntityEvent(event, formatter) {
285
+ if (event.taskId || event.entityType === 'task') {
286
+ return summarizeTaskEvent(event, formatter);
287
+ }
288
+ const changes = event.details?.changes || {};
289
+ const fields = Object.keys(changes);
290
+ const entityLabel = event.entityType === 'initiative' ? 'Initiative' : 'Workstream';
291
+ const taskTitle = typeof event.details?.taskTitle === 'string' && event.details.taskTitle.trim().length > 0
292
+ ? event.details.taskTitle.trim()
293
+ : null;
294
+ if (event.action === 'initiative-created')
295
+ return 'Initiative created';
296
+ if (event.action === 'initiative-archived')
297
+ return 'Initiative archived';
298
+ if (event.action === 'initiative-unarchived')
299
+ return 'Initiative restored';
300
+ if (event.action === 'workstream-created')
301
+ return 'Workstream created';
302
+ if (event.action === 'workstream-archived')
303
+ return 'Workstream archived';
304
+ if (event.action === 'workstream-unarchived')
305
+ return 'Workstream restored';
306
+ if (event.action === 'workstream-task-added')
307
+ return taskTitle ? `Task added: ${taskTitle}` : 'Task added';
308
+ if (event.action === 'workstream-task-removed')
309
+ return taskTitle ? `Task removed: ${taskTitle}` : 'Task removed';
310
+ if (event.action === 'workstream-relationship-changed' && changes.initiativeId) {
311
+ return `Initiative changed: ${formatValue(changes.initiativeId.from, 'initiativeId', formatter)} -> ${formatValue(changes.initiativeId.to, 'initiativeId', formatter)}`;
312
+ }
313
+ if ((event.action === 'initiative-attachments-changed' || event.action === 'workstream-attachments-changed') && changes.attachments) {
314
+ return summarizeTaskChange('attachments', changes.attachments, formatter);
315
+ }
316
+ if (fields.length === 1) {
317
+ return summarizeTaskChange(fields[0], changes[fields[0]], formatter);
318
+ }
319
+ if (fields.length > 1)
320
+ return `${fields.length} fields updated`;
321
+ if (event.action === 'initiative-updated' || event.action === 'workstream-updated')
322
+ return `${entityLabel} updated`;
323
+ return `${entityLabel} activity updated`;
324
+ }
325
+ function activityTimestamp(item) {
326
+ const parsed = Date.parse(String(item.timestamp || ''));
327
+ return Number.isFinite(parsed) ? parsed : null;
328
+ }
329
+ export function getTaskEventPrimaryChangeField(event) {
330
+ const changes = event.details?.changes || {};
331
+ const fields = Object.keys(changes);
332
+ if (event.action === 'task-status-changed' && changes.status)
333
+ return 'status';
334
+ if (event.action === 'task-schedule-changed') {
335
+ if (changes.scheduledDate)
336
+ return 'scheduledDate';
337
+ if (changes.dueDate)
338
+ return 'dueDate';
339
+ }
340
+ if (event.action === 'task-relationship-changed') {
341
+ if (changes.workstreamId)
342
+ return 'workstreamId';
343
+ if (changes.initiativeId)
344
+ return 'initiativeId';
345
+ }
346
+ if (event.action === 'task-attachments-changed' && changes.attachments)
347
+ return 'attachments';
348
+ return fields.length === 1 ? fields[0] : null;
349
+ }
350
+ export function getEntityEventPrimaryChangeField(event) {
351
+ if (event.taskId || event.entityType === 'task') {
352
+ return getTaskEventPrimaryChangeField(event);
353
+ }
354
+ const changes = event.details?.changes || {};
355
+ const fields = Object.keys(changes);
356
+ if ((event.action === 'workstream-task-added' || event.action === 'workstream-task-removed') && event.details?.taskId)
357
+ return 'taskId';
358
+ if (event.action === 'workstream-relationship-changed' && changes.initiativeId)
359
+ return 'initiativeId';
360
+ if ((event.action === 'initiative-attachments-changed' || event.action === 'workstream-attachments-changed') && changes.attachments)
361
+ return 'attachments';
362
+ return fields.length === 1 ? fields[0] : null;
363
+ }
364
+ export function getEntityActivityItems(task) {
365
+ const deduped = new Map();
366
+ const commentIds = new Set();
367
+ const addItem = (item) => {
368
+ if (item.type === 'event' && item.event.action === 'task-comment-added')
369
+ return;
370
+ const key = item.type === 'comment'
371
+ ? `comment:${String(item.comment.id || item.id).replace(/^comment:/, '')}`
372
+ : `event:${String(item.event.id || item.id).replace(/^event:/, '')}`;
373
+ deduped.set(key, { ...item, id: key });
374
+ if (item.type === 'comment') {
375
+ commentIds.add(String(item.comment.id || item.id).replace(/^comment:/, ''));
376
+ }
377
+ };
378
+ if (Array.isArray(task.activity)) {
379
+ task.activity.forEach(addItem);
380
+ }
381
+ if (Array.isArray(task.comments)) {
382
+ task.comments.forEach((comment) => {
383
+ const commentId = String(comment.id || '').trim();
384
+ if (!commentId || commentIds.has(commentId))
385
+ return;
386
+ addItem({
387
+ id: `comment:${commentId}`,
142
388
  type: 'comment',
143
389
  timestamp: comment.timestamp,
144
390
  comment
145
- }))
146
- : [];
391
+ });
392
+ });
393
+ }
394
+ return [...deduped.values()].sort((left, right) => {
395
+ const leftTs = activityTimestamp(left);
396
+ const rightTs = activityTimestamp(right);
397
+ if (leftTs !== null && rightTs !== null && leftTs !== rightTs)
398
+ return leftTs - rightTs;
399
+ if (leftTs !== null && rightTs === null)
400
+ return -1;
401
+ if (leftTs === null && rightTs !== null)
402
+ return 1;
403
+ return String(left.id || '').localeCompare(String(right.id || ''));
404
+ });
405
+ }
406
+ export function getTaskActivityItems(task) {
407
+ return getEntityActivityItems(task);
408
+ }
409
+ export function getLatestTaskActivity(task) {
410
+ const items = getTaskActivityItems(task);
147
411
  if (items.length === 0)
148
412
  return null;
149
413
  return [...items].sort((left, right) => {
150
- const leftTs = Date.parse(String(left.timestamp || ''));
151
- const rightTs = Date.parse(String(right.timestamp || ''));
152
- if (Number.isFinite(leftTs) && Number.isFinite(rightTs) && leftTs !== rightTs) {
414
+ const leftTs = activityTimestamp(left);
415
+ const rightTs = activityTimestamp(right);
416
+ if (leftTs !== null && rightTs !== null && leftTs !== rightTs) {
153
417
  return rightTs - leftTs;
154
418
  }
419
+ if (leftTs !== null && rightTs === null)
420
+ return -1;
421
+ if (leftTs === null && rightTs !== null)
422
+ return 1;
155
423
  return String(right.id || '').localeCompare(String(left.id || ''));
156
424
  })[0] || null;
157
425
  }
@@ -1,32 +1,32 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { summarizeTaskChange, summarizeTaskEvent } from './taskActivity';
2
+ import { getTaskActivityItems, summarizeEntityEvent, summarizeTaskChange, summarizeTaskEvent } from './taskActivity';
3
3
  describe('summarizeTaskChange', () => {
4
4
  it('summarizes title edits without dumping before and after text', () => {
5
5
  expect(summarizeTaskChange('title', {
6
6
  from: 'Old title',
7
7
  to: 'New title',
8
- })).toBe('TITLE: updated');
8
+ })).toBe('Title: updated');
9
9
  });
10
- it('summarizes assignee changes with uppercase labels and formatted values', () => {
10
+ it('summarizes assignee changes with sentence-case labels and formatted values', () => {
11
11
  expect(summarizeTaskChange('assignee', { from: 'unassigned', to: 'member-1' }, (_field, value) => {
12
12
  if (value === 'unassigned')
13
13
  return 'Unassigned';
14
14
  if (value === 'member-1')
15
15
  return 'Emma Peel';
16
16
  return undefined;
17
- })).toBe('ASSIGNED TO: Unassigned -> Emma Peel');
17
+ })).toBe('Assigned to: Unassigned -> Emma Peel');
18
18
  });
19
19
  it('summarizes description additions without rendering the body text', () => {
20
20
  expect(summarizeTaskChange('description', {
21
21
  from: '',
22
22
  to: 'Line one\n\nLine two',
23
- })).toBe('DESCRIPTION: added');
23
+ })).toBe('Description: added');
24
24
  });
25
25
  it('summarizes description clears without rendering the old body text', () => {
26
26
  expect(summarizeTaskChange('description', {
27
27
  from: 'Old body',
28
28
  to: '',
29
- })).toBe('DESCRIPTION: cleared');
29
+ })).toBe('Description: cleared');
30
30
  });
31
31
  it('summarizes checklist completion clearly', () => {
32
32
  expect(summarizeTaskChange('checklistItems', {
@@ -59,7 +59,49 @@ describe('summarizeTaskChange', () => {
59
59
  })).toBe('Checklist reordered');
60
60
  });
61
61
  it('uses readable labels for workstream relationship changes', () => {
62
- expect(summarizeTaskChange('workstreamId', { from: null, to: 'workstream-1' }, (_field, value) => value === 'workstream-1' ? 'WS-12' : undefined)).toBe('WORKSTREAM: none -> WS-12');
62
+ expect(summarizeTaskChange('workstreamId', { from: null, to: 'workstream-1' }, (_field, value) => value === 'workstream-1' ? 'WS-12' : undefined)).toBe('Workstream: none -> WS-12');
63
+ });
64
+ it('summarizes image attachment additions clearly', () => {
65
+ expect(summarizeTaskChange('attachments', {
66
+ from: [],
67
+ to: [{
68
+ path: '/api/taskforce/context/workspaces/default/assets/images/homepage-proof.png',
69
+ caption: 'Homepage proof',
70
+ }],
71
+ })).toBe('Image attached: Homepage proof');
72
+ });
73
+ it('summarizes document attachment removals clearly', () => {
74
+ expect(summarizeTaskChange('attachments', {
75
+ from: [{
76
+ path: '/api/taskforce/documents/asset-doc-1/content',
77
+ originalFilename: 'spec.md',
78
+ assetId: 'asset-doc-1',
79
+ }],
80
+ to: [],
81
+ })).toBe('Document removed: spec.md');
82
+ });
83
+ it('summarizes multiple document attachments concisely', () => {
84
+ expect(summarizeTaskChange('attachments', {
85
+ from: [],
86
+ to: [
87
+ { path: '/api/taskforce/documents/asset-doc-1/content', originalFilename: 'spec.md', assetId: 'asset-doc-1' },
88
+ { path: '/api/taskforce/documents/asset-doc-2/content', originalFilename: 'notes.pdf', assetId: 'asset-doc-2' },
89
+ ],
90
+ })).toBe('2 documents attached');
91
+ });
92
+ it('summarizes attachment renames when the same asset remains attached', () => {
93
+ expect(summarizeTaskChange('attachments', {
94
+ from: [{
95
+ path: '/api/taskforce/documents/asset-doc-1/content',
96
+ assetId: 'asset-doc-1',
97
+ caption: 'Draft spec',
98
+ }],
99
+ to: [{
100
+ path: '/api/taskforce/documents/asset-doc-1/content',
101
+ assetId: 'asset-doc-1',
102
+ caption: 'Final spec',
103
+ }],
104
+ })).toBe('Document renamed: Draft spec -> Final spec');
63
105
  });
64
106
  });
65
107
  describe('summarizeTaskEvent', () => {
@@ -80,6 +122,97 @@ describe('summarizeTaskEvent', () => {
80
122
  },
81
123
  },
82
124
  },
83
- }, (_field, value) => value === 'workstream-1' ? 'WS-12' : undefined)).toBe('Workstream: none -> WS-12');
125
+ }, (_field, value) => value === 'workstream-1' ? 'WS-12' : undefined)).toBe('Workstream changed: none -> WS-12');
126
+ });
127
+ it('uses attachment-specific summaries for attachment activity events', () => {
128
+ expect(summarizeTaskEvent({
129
+ id: 'event-2',
130
+ taskId: 'task-1',
131
+ workspaceId: 'default',
132
+ action: 'task-attachments-changed',
133
+ actor: 'user',
134
+ actorType: 'human',
135
+ createdAt: '2026-04-12T21:42:00.000Z',
136
+ details: {
137
+ changes: {
138
+ attachments: {
139
+ from: [{
140
+ path: '/api/taskforce/context/workspaces/default/assets/images/old-proof.png',
141
+ caption: 'Old proof',
142
+ }],
143
+ to: [{
144
+ path: '/api/taskforce/context/workspaces/default/assets/images/new-proof.png',
145
+ caption: 'New proof',
146
+ }],
147
+ },
148
+ },
149
+ },
150
+ })).toBe('Image replaced: Old proof -> New proof');
151
+ });
152
+ it('uses task-focused summaries for workstream task relationship events', () => {
153
+ expect(summarizeEntityEvent({
154
+ id: 'event-3',
155
+ workspaceId: 'default',
156
+ entityType: 'workstream',
157
+ entityId: 'workstream-1',
158
+ action: 'workstream-task-added',
159
+ actor: 'user',
160
+ actorType: 'human',
161
+ createdAt: '2026-04-12T21:42:00.000Z',
162
+ details: {
163
+ taskId: 'task-1',
164
+ taskTitle: 'Ship launch checklist',
165
+ changes: {
166
+ workstreamId: {
167
+ from: null,
168
+ to: 'workstream-1',
169
+ },
170
+ },
171
+ },
172
+ })).toBe('Task added: Ship launch checklist');
173
+ });
174
+ });
175
+ describe('getTaskActivityItems', () => {
176
+ it('combines comments and audit events while hiding duplicate comment-added events', () => {
177
+ const items = getTaskActivityItems({
178
+ comments: [{
179
+ id: 'comment-1',
180
+ author: 'user',
181
+ text: 'Captured comment',
182
+ timestamp: '2026-03-22T10:00:00.000Z',
183
+ }],
184
+ activity: [
185
+ {
186
+ id: 'event:event-1',
187
+ type: 'event',
188
+ timestamp: '2026-03-22T09:00:00.000Z',
189
+ event: {
190
+ id: 'event-1',
191
+ taskId: 'task-1',
192
+ workspaceId: 'default',
193
+ action: 'task-status-changed',
194
+ actor: 'user',
195
+ actorType: 'human',
196
+ createdAt: '2026-03-22T09:00:00.000Z',
197
+ details: { changes: { status: { from: 'task', to: 'in-progress' } } },
198
+ },
199
+ },
200
+ {
201
+ id: 'event:comment-event-1',
202
+ type: 'event',
203
+ timestamp: '2026-03-22T10:00:00.000Z',
204
+ event: {
205
+ id: 'comment-event-1',
206
+ taskId: 'task-1',
207
+ workspaceId: 'default',
208
+ action: 'task-comment-added',
209
+ actor: 'user',
210
+ actorType: 'human',
211
+ createdAt: '2026-03-22T10:00:00.000Z',
212
+ },
213
+ },
214
+ ],
215
+ });
216
+ expect(items.map((item) => item.id)).toEqual(['event:event-1', 'comment:comment-1']);
84
217
  });
85
218
  });
@@ -0,0 +1,22 @@
1
+ import type { CategoryConfig, PriorityDefinition, TaskItem, TaxonomyDefinition } from '../types';
2
+ import type { TaskAssigneeOption } from './assignees';
3
+ export interface NormalizedTaskSearchQuery {
4
+ raw: string;
5
+ tokens: string[];
6
+ referenceToken: string | null;
7
+ }
8
+ export interface TaskSearchContext {
9
+ categories?: CategoryConfig[];
10
+ types?: Array<{
11
+ value: string | number;
12
+ label: string;
13
+ }>;
14
+ priorities?: PriorityDefinition[];
15
+ assigneeOptions?: TaskAssigneeOption[];
16
+ taxonomies?: TaxonomyDefinition[];
17
+ }
18
+ export declare function normalizeSearchText(value: unknown): string;
19
+ export declare function normalizeTaskReferenceToken(value: unknown): string | null;
20
+ export declare function normalizeTaskSearchQuery(query: string): NormalizedTaskSearchQuery;
21
+ export declare function getTaskSearchFields(task: TaskItem, context?: TaskSearchContext): string[];
22
+ export declare function taskMatchesSearch(task: TaskItem, query: string | NormalizedTaskSearchQuery, context?: TaskSearchContext): boolean;
@@ -0,0 +1,87 @@
1
+ import { STATUS_OPTIONS } from './constants';
2
+ import { getTaskReferenceLabel } from './taskReferences';
3
+ const DASH_LIKE_PATTERN = /[\u2010-\u2015\u2212]/g;
4
+ export function normalizeSearchText(value) {
5
+ return String(value ?? '')
6
+ .trim()
7
+ .replace(DASH_LIKE_PATTERN, '-')
8
+ .replace(/\s+/g, ' ')
9
+ .toLowerCase();
10
+ }
11
+ export function normalizeTaskReferenceToken(value) {
12
+ const normalized = normalizeSearchText(value);
13
+ if (!normalized)
14
+ return null;
15
+ const match = normalized.match(/^(l\s*t|t)\s*(?:-\s*)?(\d+)$/i);
16
+ if (!match)
17
+ return null;
18
+ const prefix = match[1].replace(/\s+/g, '').toLowerCase();
19
+ return `${prefix}-${match[2]}`;
20
+ }
21
+ export function normalizeTaskSearchQuery(query) {
22
+ const raw = normalizeSearchText(query);
23
+ return {
24
+ raw,
25
+ tokens: raw ? raw.split(' ').filter(Boolean) : [],
26
+ referenceToken: normalizeTaskReferenceToken(query)
27
+ };
28
+ }
29
+ function addValue(values, value) {
30
+ const normalized = String(value ?? '').trim();
31
+ if (normalized)
32
+ values.push(normalized);
33
+ }
34
+ function addMappedLabel(values, options, targetValue) {
35
+ const target = String(targetValue ?? '').trim();
36
+ if (!target)
37
+ return;
38
+ const option = options?.find((entry) => String(entry.value) === target);
39
+ addValue(values, option?.label);
40
+ }
41
+ export function getTaskSearchFields(task, context = {}) {
42
+ const values = [];
43
+ addValue(values, task.title);
44
+ addValue(values, task.description);
45
+ addValue(values, task.id);
46
+ addValue(values, getTaskReferenceLabel(task));
47
+ addValue(values, task.category);
48
+ addValue(values, task.type);
49
+ addValue(values, task.priority);
50
+ addValue(values, task.status);
51
+ addValue(values, task.assignee || 'unassigned');
52
+ addValue(values, task.canceledReason);
53
+ addMappedLabel(values, context.categories, task.category);
54
+ addMappedLabel(values, context.types, task.type);
55
+ addMappedLabel(values, context.priorities, task.priority);
56
+ addMappedLabel(values, STATUS_OPTIONS, task.status);
57
+ addMappedLabel(values, context.assigneeOptions, task.assignee || 'unassigned');
58
+ Object.entries(task.taxonomies || {}).forEach(([taxonomyId, taskValue]) => {
59
+ const taxonomy = context.taxonomies?.find((entry) => entry.id === taxonomyId);
60
+ addValue(values, taxonomy?.label);
61
+ const taskValues = Array.isArray(taskValue) ? taskValue : [taskValue];
62
+ taskValues.forEach((value) => {
63
+ addValue(values, value);
64
+ addMappedLabel(values, taxonomy?.options, value);
65
+ });
66
+ });
67
+ return values;
68
+ }
69
+ export function taskMatchesSearch(task, query, context = {}) {
70
+ const normalizedQuery = typeof query === 'string' ? normalizeTaskSearchQuery(query) : query;
71
+ if (!normalizedQuery.raw)
72
+ return true;
73
+ const fields = getTaskSearchFields(task, context);
74
+ const normalizedFields = fields.map(normalizeSearchText).filter(Boolean);
75
+ const joinedFields = normalizedFields.join(' ');
76
+ if (joinedFields.includes(normalizedQuery.raw))
77
+ return true;
78
+ if (normalizedQuery.referenceToken) {
79
+ const referenceMatches = fields
80
+ .map(normalizeTaskReferenceToken)
81
+ .some((referenceToken) => referenceToken?.includes(normalizedQuery.referenceToken));
82
+ if (referenceMatches)
83
+ return true;
84
+ }
85
+ return normalizedQuery.tokens.length > 0
86
+ && normalizedQuery.tokens.every((token) => joinedFields.includes(token));
87
+ }
@@ -0,0 +1 @@
1
+ export {};