@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,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { fireEvent, render, screen } from '@testing-library/react';
2
+ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
3
3
  import userEvent from '@testing-library/user-event';
4
4
  import { describe, it, expect, vi } from 'vitest';
5
5
  import { TaskSettings } from './TaskSettings';
@@ -109,8 +109,22 @@ describe('TaskSettings Component', () => {
109
109
  };
110
110
  return render(_jsx(TaskSettings, { ...props }));
111
111
  };
112
+ const renderMcpComponent = (overrides = {}) => {
113
+ const { settingsModel: settingsModelOverrides = {}, ...rest } = overrides;
114
+ return renderComponent({
115
+ ...rest,
116
+ renderMode: 'mcp-only',
117
+ settingsModel: {
118
+ ...settingsModelOverrides,
119
+ initialSection: 'mcp'
120
+ }
121
+ });
122
+ };
112
123
  const openMcpSettings = async (user) => {
113
- await user.click(screen.getByRole('button', { name: /MCP/i }));
124
+ const mcpButton = screen.queryByRole('button', { name: /^MCP$/i });
125
+ if (mcpButton) {
126
+ await user.click(mcpButton);
127
+ }
114
128
  };
115
129
  const openCloudMcpSettings = async (user) => {
116
130
  await openMcpSettings(user);
@@ -139,7 +153,7 @@ describe('TaskSettings Component', () => {
139
153
  expect(screen.queryByRole('button', { name: /workflows/i })).not.toBeInTheDocument();
140
154
  expect(screen.queryByRole('button', { name: /agents/i })).not.toBeInTheDocument();
141
155
  expect(screen.getByRole('button', { name: /taxonomy/i })).toBeInTheDocument();
142
- expect(screen.getByRole('button', { name: /^mcp$/i })).toBeInTheDocument();
156
+ expect(screen.queryByRole('button', { name: /^mcp$/i })).not.toBeInTheDocument();
143
157
  expect(screen.getByRole('button', { name: /integrations/i })).toBeInTheDocument();
144
158
  });
145
159
  it('4.1b General tab removes old profile/account chrome', () => {
@@ -852,7 +866,7 @@ describe('TaskSettings Component', () => {
852
866
  json: async () => ({ items: [] })
853
867
  }));
854
868
  const user = userEvent.setup();
855
- renderComponent({
869
+ renderMcpComponent({
856
870
  settingsModel: {
857
871
  runtimeMode: 'cloud',
858
872
  isAuthenticated: true,
@@ -860,8 +874,14 @@ describe('TaskSettings Component', () => {
860
874
  fetchCloudAuthApi
861
875
  }
862
876
  });
863
- await user.click(screen.getByRole('button', { name: /MCP/i }));
877
+ await openMcpSettings(user);
864
878
  expect(await screen.findByText('Cloud MCP Configuration')).toBeInTheDocument();
879
+ expect(screen.getByText(/Cloud MCP connects agents to Taskforce through the hosted MCP endpoint/i)).toBeInTheDocument();
880
+ expect(screen.getByText(/Once the MCP is configured, prompt your agent:/i)).toBeInTheDocument();
881
+ expect(screen.getByRole('button', { name: 'Resolve your profile with Taskforce.' })).toBeInTheDocument();
882
+ const cloudInstructionLabel = screen.getByText('General MCP Configuration Instructions');
883
+ const cloudProfilePrompt = screen.getByText(/Once the MCP is configured, prompt your agent:/i);
884
+ expect(cloudInstructionLabel.compareDocumentPosition(cloudProfilePrompt) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
865
885
  expect(screen.getByRole('button', { name: 'Generate Token' })).toBeInTheDocument();
866
886
  expect(screen.queryByRole('button', { name: 'Local MCP' })).not.toBeInTheDocument();
867
887
  expect(screen.queryByText('MCP Path Mapping (Host Root)')).not.toBeInTheDocument();
@@ -870,13 +890,28 @@ describe('TaskSettings Component', () => {
870
890
  credentials: 'include'
871
891
  }));
872
892
  }, 10000);
893
+ it('4.20a MCP setup tells users how to register the agent profile', async () => {
894
+ renderMcpComponent({
895
+ settingsModel: {
896
+ runtimeMode: 'local',
897
+ isAuthenticated: false
898
+ }
899
+ });
900
+ expect(screen.getByText(/Local MCP connects through your local Taskforce install/i)).toBeInTheDocument();
901
+ expect(screen.getByText(/Once the MCP is configured, prompt your agent:/i)).toBeInTheDocument();
902
+ expect(screen.getByRole('button', { name: 'Resolve your profile with Taskforce.' })).toBeInTheDocument();
903
+ const localInstructionLabel = screen.getByText('General MCP Configuration Instructions');
904
+ const localProfilePrompt = screen.getByText(/Once the MCP is configured, prompt your agent:/i);
905
+ expect(localInstructionLabel.compareDocumentPosition(localProfilePrompt) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
906
+ expect(screen.queryByText('Ask Your Agent to Register')).not.toBeInTheDocument();
907
+ });
873
908
  it('4.21 Local authenticated runtime defaults MCP tab to local with local-first toggle', async () => {
874
909
  const fetchCloudAuthApi = vi.fn(async () => ({
875
910
  ok: true,
876
911
  json: async () => ({ items: [] })
877
912
  }));
878
913
  const user = userEvent.setup();
879
- renderComponent({
914
+ renderMcpComponent({
880
915
  settingsModel: {
881
916
  runtimeMode: 'local',
882
917
  isAuthenticated: true,
@@ -885,7 +920,7 @@ describe('TaskSettings Component', () => {
885
920
  fetchCloudAuthApi
886
921
  }
887
922
  });
888
- await user.click(screen.getByRole('button', { name: /MCP/i }));
923
+ await openMcpSettings(user);
889
924
  const localButton = await screen.findByRole('button', { name: 'Local MCP' });
890
925
  const cloudButton = screen.getByRole('button', { name: 'Cloud MCP' });
891
926
  expect(localButton.compareDocumentPosition(cloudButton) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
@@ -926,7 +961,7 @@ describe('TaskSettings Component', () => {
926
961
  };
927
962
  });
928
963
  const user = userEvent.setup();
929
- renderComponent({
964
+ renderMcpComponent({
930
965
  settingsModel: {
931
966
  runtimeMode: 'local',
932
967
  isAuthenticated: true,
@@ -947,11 +982,11 @@ describe('TaskSettings Component', () => {
947
982
  expiresAt: null
948
983
  })
949
984
  }));
950
- expect((await screen.findAllByText('tfmcp_example_secret')).length).toBeGreaterThan(0);
985
+ expect(await screen.findByDisplayValue('tfmcp_example_secret')).toBeInTheDocument();
951
986
  }, 10000);
952
987
  it('4.23c MCP token name defaults to the workspace name', async () => {
953
988
  const user = userEvent.setup();
954
- renderComponent({
989
+ renderMcpComponent({
955
990
  settingsModel: {
956
991
  runtimeMode: 'local',
957
992
  isAuthenticated: true,
@@ -982,7 +1017,7 @@ describe('TaskSettings Component', () => {
982
1017
  };
983
1018
  });
984
1019
  const user = userEvent.setup();
985
- renderComponent({
1020
+ renderMcpComponent({
986
1021
  settingsModel: {
987
1022
  runtimeMode: 'local',
988
1023
  isAuthenticated: true,
@@ -991,9 +1026,9 @@ describe('TaskSettings Component', () => {
991
1026
  }
992
1027
  });
993
1028
  await openCloudMcpSettings(user);
994
- await user.click(screen.getByRole('button', { name: 'Test Connection' }));
995
- await user.type(await screen.findByPlaceholderText('tfmcp_xxxxx_replace-me'), 'tfmcp_example_secret');
996
- await user.click(screen.getAllByRole('button', { name: 'Test Connection' })[1]);
1029
+ expect(screen.queryByText('Test Connection')).not.toBeInTheDocument();
1030
+ await user.type(await screen.findByPlaceholderText('Generate a token to populate the full secret'), 'tfmcp_example_secret');
1031
+ await user.click(screen.getByRole('button', { name: 'Test Token' }));
997
1032
  expect(fetchCloudAuthApi).toHaveBeenCalledWith('/api/taskforce/settings/mcp/test-connection', expect.objectContaining({
998
1033
  method: 'POST',
999
1034
  credentials: 'include'
@@ -1023,7 +1058,7 @@ describe('TaskSettings Component', () => {
1023
1058
  };
1024
1059
  });
1025
1060
  const user = userEvent.setup();
1026
- renderComponent({
1061
+ renderMcpComponent({
1027
1062
  settingsModel: {
1028
1063
  runtimeMode: 'local',
1029
1064
  isAuthenticated: true,
@@ -1033,9 +1068,8 @@ describe('TaskSettings Component', () => {
1033
1068
  }
1034
1069
  });
1035
1070
  await openCloudMcpSettings(user);
1036
- await user.click(screen.getByRole('button', { name: 'Test Connection' }));
1037
- await user.type(await screen.findByPlaceholderText('tfmcp_xxxxx_replace-me'), 'tfmcp_example_secret');
1038
- await user.click(screen.getAllByRole('button', { name: 'Test Connection' })[1]);
1071
+ await user.type(await screen.findByPlaceholderText('Generate a token to populate the full secret'), 'tfmcp_example_secret');
1072
+ await user.click(screen.getByRole('button', { name: 'Test Token' }));
1039
1073
  expect(await screen.findByText('The dedicated MCP host is responding with a gateway error.')).toBeInTheDocument();
1040
1074
  expect(screen.getByText(/Endpoint: https:\/\/performance-mcp\.taskforcehq\.ai\/mcp/i)).toBeInTheDocument();
1041
1075
  expect(screen.getByText(/Error code: MCP_ENDPOINT_GATEWAY_ERROR/i)).toBeInTheDocument();
@@ -1047,7 +1081,7 @@ describe('TaskSettings Component', () => {
1047
1081
  json: async () => ({ items: [] })
1048
1082
  }));
1049
1083
  const user = userEvent.setup();
1050
- renderComponent({
1084
+ renderMcpComponent({
1051
1085
  settingsModel: {
1052
1086
  runtimeMode: 'local',
1053
1087
  isAuthenticated: true,
@@ -1069,7 +1103,7 @@ describe('TaskSettings Component', () => {
1069
1103
  json: async () => ({ items: [] })
1070
1104
  }));
1071
1105
  const user = userEvent.setup();
1072
- renderComponent({
1106
+ renderMcpComponent({
1073
1107
  settingsModel: {
1074
1108
  runtimeMode: 'local',
1075
1109
  isAuthenticated: true,
@@ -1089,7 +1123,7 @@ describe('TaskSettings Component', () => {
1089
1123
  json: async () => ({ items: [] })
1090
1124
  }));
1091
1125
  const user = userEvent.setup();
1092
- renderComponent({
1126
+ renderMcpComponent({
1093
1127
  settingsModel: {
1094
1128
  runtimeMode: 'local',
1095
1129
  isAuthenticated: true,
@@ -1109,7 +1143,7 @@ describe('TaskSettings Component', () => {
1109
1143
  json: async () => ({ items: [] })
1110
1144
  }));
1111
1145
  const user = userEvent.setup();
1112
- renderComponent({
1146
+ renderMcpComponent({
1113
1147
  settingsModel: {
1114
1148
  runtimeMode: 'local',
1115
1149
  isAuthenticated: true,
@@ -1131,7 +1165,7 @@ describe('TaskSettings Component', () => {
1131
1165
  json: async () => ({ items: [] })
1132
1166
  }));
1133
1167
  const user = userEvent.setup();
1134
- renderComponent({
1168
+ renderMcpComponent({
1135
1169
  settingsModel: {
1136
1170
  runtimeMode: 'local',
1137
1171
  isAuthenticated: true,
@@ -1153,7 +1187,7 @@ describe('TaskSettings Component', () => {
1153
1187
  json: async () => ({ items: [] })
1154
1188
  }));
1155
1189
  const user = userEvent.setup();
1156
- renderComponent({
1190
+ renderMcpComponent({
1157
1191
  settingsModel: {
1158
1192
  runtimeMode: 'local',
1159
1193
  isAuthenticated: true,
@@ -1175,7 +1209,7 @@ describe('TaskSettings Component', () => {
1175
1209
  json: async () => ({ items: [] })
1176
1210
  }));
1177
1211
  const user = userEvent.setup();
1178
- renderComponent({
1212
+ renderMcpComponent({
1179
1213
  settingsModel: {
1180
1214
  runtimeMode: 'local',
1181
1215
  isAuthenticated: true,
@@ -1197,7 +1231,7 @@ describe('TaskSettings Component', () => {
1197
1231
  json: async () => ({ items: [] })
1198
1232
  }));
1199
1233
  const user = userEvent.setup();
1200
- renderComponent({
1234
+ renderMcpComponent({
1201
1235
  settingsModel: {
1202
1236
  runtimeMode: 'local',
1203
1237
  isAuthenticated: true,
@@ -1219,7 +1253,7 @@ describe('TaskSettings Component', () => {
1219
1253
  json: async () => ({ items: [] })
1220
1254
  }));
1221
1255
  const user = userEvent.setup();
1222
- renderComponent({
1256
+ renderMcpComponent({
1223
1257
  settingsModel: {
1224
1258
  runtimeMode: 'local',
1225
1259
  isAuthenticated: true,
@@ -1242,7 +1276,7 @@ describe('TaskSettings Component', () => {
1242
1276
  json: async () => ({ items: [] })
1243
1277
  }));
1244
1278
  const user = userEvent.setup();
1245
- renderComponent({
1279
+ renderMcpComponent({
1246
1280
  settingsModel: {
1247
1281
  runtimeMode: 'local',
1248
1282
  isAuthenticated: true,
@@ -1269,7 +1303,7 @@ describe('TaskSettings Component', () => {
1269
1303
  json: async () => ({ items: [] })
1270
1304
  }));
1271
1305
  const user = userEvent.setup();
1272
- renderComponent({
1306
+ renderMcpComponent({
1273
1307
  settingsModel: {
1274
1308
  runtimeMode: 'local',
1275
1309
  isAuthenticated: true,
@@ -1346,7 +1380,7 @@ describe('TaskSettings Component', () => {
1346
1380
  json: async () => ({ items: [] })
1347
1381
  }));
1348
1382
  const user = userEvent.setup();
1349
- renderComponent({
1383
+ renderMcpComponent({
1350
1384
  settingsModel: {
1351
1385
  runtimeMode: 'local',
1352
1386
  isAuthenticated: true,
@@ -1361,7 +1395,7 @@ describe('TaskSettings Component', () => {
1361
1395
  }, 10000);
1362
1396
  it('4.23g Cloud MCP configuration offers snippet and full-config copy actions', async () => {
1363
1397
  const user = userEvent.setup();
1364
- renderComponent({
1398
+ renderMcpComponent({
1365
1399
  settingsModel: {
1366
1400
  runtimeMode: 'local',
1367
1401
  isAuthenticated: true,
@@ -1374,7 +1408,7 @@ describe('TaskSettings Component', () => {
1374
1408
  }, 10000);
1375
1409
  it('4.23g1 Cloud MCP copy warns before a token is generated', async () => {
1376
1410
  const user = userEvent.setup();
1377
- renderComponent({
1411
+ renderMcpComponent({
1378
1412
  settingsModel: {
1379
1413
  runtimeMode: 'local',
1380
1414
  isAuthenticated: true,
@@ -1388,7 +1422,7 @@ describe('TaskSettings Component', () => {
1388
1422
  }, 10000);
1389
1423
  it('4.23g2 Cloud MCP settings do not surface automation identity controls', async () => {
1390
1424
  const user = userEvent.setup();
1391
- renderComponent({
1425
+ renderMcpComponent({
1392
1426
  settingsModel: {
1393
1427
  runtimeMode: 'local',
1394
1428
  isAuthenticated: true,
@@ -1402,7 +1436,7 @@ describe('TaskSettings Component', () => {
1402
1436
  }, 10000);
1403
1437
  it('4.23g3 Cloud MCP config does not include AI identity headers by default', async () => {
1404
1438
  const user = userEvent.setup();
1405
- renderComponent({
1439
+ renderMcpComponent({
1406
1440
  settingsModel: {
1407
1441
  runtimeMode: 'local',
1408
1442
  isAuthenticated: true,
@@ -1415,34 +1449,52 @@ describe('TaskSettings Component', () => {
1415
1449
  expect(screen.queryByText(/X-Taskforce-AI-Name/i)).not.toBeInTheDocument();
1416
1450
  expect(screen.queryByText(/X-Taskforce-AI-Profile-Token/i)).not.toBeInTheDocument();
1417
1451
  }, 10000);
1418
- it('4.23h Active tokens sort first and revoked tokens do not show revoke or regenerate actions', async () => {
1452
+ it('4.23h Active tokens sort first and inactive tokens only show delete actions', async () => {
1419
1453
  const user = userEvent.setup();
1420
- const fetchCloudAuthApi = vi.fn(async () => ({
1421
- ok: true,
1422
- json: async () => ({
1423
- items: [
1424
- {
1425
- id: 'token-revoked',
1426
- name: 'Old Token',
1427
- scopes: ['tasks:read'],
1428
- tokenPrefix: 'tfmcp_old',
1429
- status: 'revoked',
1430
- createdAt: '2026-03-01T00:00:00.000Z',
1431
- lastUsedAt: null,
1432
- },
1433
- {
1434
- id: 'token-active',
1435
- name: 'Current Token',
1436
- scopes: ['tasks:read', 'tasks:write'],
1437
- tokenPrefix: 'tfmcp_new',
1438
- status: 'active',
1439
- createdAt: '2026-03-02T00:00:00.000Z',
1440
- lastUsedAt: null,
1441
- }
1442
- ]
1443
- })
1444
- }));
1445
- renderComponent({
1454
+ const confirmSpy = vi.spyOn(window, 'confirm').mockImplementation(() => true);
1455
+ const fetchCloudAuthApi = vi.fn(async (path, init) => {
1456
+ if (path === '/api/taskforce/settings/mcp/tokens/token-expired' && init?.method === 'DELETE') {
1457
+ return {
1458
+ ok: true,
1459
+ json: async () => ({ success: true, deleted: true })
1460
+ };
1461
+ }
1462
+ return {
1463
+ ok: true,
1464
+ json: async () => ({
1465
+ items: [
1466
+ {
1467
+ id: 'token-expired',
1468
+ name: 'Expired Token',
1469
+ scopes: ['tasks:read'],
1470
+ tokenPrefix: 'tfmcp_expired',
1471
+ status: 'expired',
1472
+ createdAt: '2026-03-03T00:00:00.000Z',
1473
+ lastUsedAt: null,
1474
+ },
1475
+ {
1476
+ id: 'token-revoked',
1477
+ name: 'Old Token',
1478
+ scopes: ['tasks:read'],
1479
+ tokenPrefix: 'tfmcp_old',
1480
+ status: 'revoked',
1481
+ createdAt: '2026-03-01T00:00:00.000Z',
1482
+ lastUsedAt: null,
1483
+ },
1484
+ {
1485
+ id: 'token-active',
1486
+ name: 'Current Token',
1487
+ scopes: ['tasks:read', 'tasks:write'],
1488
+ tokenPrefix: 'tfmcp_new',
1489
+ status: 'active',
1490
+ createdAt: '2026-03-02T00:00:00.000Z',
1491
+ lastUsedAt: null,
1492
+ }
1493
+ ]
1494
+ })
1495
+ };
1496
+ });
1497
+ renderMcpComponent({
1446
1498
  settingsModel: {
1447
1499
  runtimeMode: 'local',
1448
1500
  isAuthenticated: true,
@@ -1452,16 +1504,27 @@ describe('TaskSettings Component', () => {
1452
1504
  });
1453
1505
  await openCloudMcpSettings(user);
1454
1506
  expect(await screen.findByText('Current Token')).toBeInTheDocument();
1507
+ expect(screen.getByText('Expired Token')).toBeInTheDocument();
1455
1508
  expect(screen.getByText('Old Token')).toBeInTheDocument();
1456
- const tokenLabels = screen.getAllByText(/^(Current Token|Old Token)$/);
1509
+ const tokenLabels = screen.getAllByText(/^(Current Token|Expired Token|Old Token)$/);
1457
1510
  expect(tokenLabels[0]).toHaveTextContent('Current Token');
1458
- expect(tokenLabels[1]).toHaveTextContent('Old Token');
1511
+ expect(tokenLabels[1]).toHaveTextContent('Expired Token');
1512
+ expect(tokenLabels[2]).toHaveTextContent('Old Token');
1459
1513
  expect(screen.getAllByRole('button', { name: 'Regenerate' })).toHaveLength(1);
1460
1514
  expect(screen.getAllByRole('button', { name: 'Revoke' })).toHaveLength(1);
1515
+ expect(screen.getAllByRole('button', { name: 'Delete' })).toHaveLength(2);
1516
+ await user.click(screen.getAllByRole('button', { name: 'Delete' })[0]);
1517
+ expect(confirmSpy).toHaveBeenCalledWith('Delete this token record? This does not affect active tokens.');
1518
+ expect(fetchCloudAuthApi).toHaveBeenCalledWith('/api/taskforce/settings/mcp/tokens/token-expired', expect.objectContaining({
1519
+ method: 'DELETE',
1520
+ credentials: 'include'
1521
+ }));
1522
+ await waitFor(() => expect(screen.queryByText('Expired Token')).not.toBeInTheDocument());
1523
+ confirmSpy.mockRestore();
1461
1524
  }, 10000);
1462
1525
  it('4.23f Cloud MCP snippet keeps the server id short enough for client tool-name limits', async () => {
1463
1526
  const user = userEvent.setup();
1464
- renderComponent({
1527
+ renderMcpComponent({
1465
1528
  settingsModel: {
1466
1529
  runtimeMode: 'local',
1467
1530
  isAuthenticated: true,
@@ -1480,33 +1543,59 @@ describe('TaskSettings Component', () => {
1480
1543
  expect(serverId.length).toBeLessThanOrEqual(33);
1481
1544
  expect(`mcp_${serverId}_set_schedule_preferences`.length).toBeLessThanOrEqual(64);
1482
1545
  }, 10000);
1483
- it('4.24 Local signed-out runtime shows local MCP only', async () => {
1546
+ it('4.24 Local signed-out runtime keeps Cloud MCP visible with an auth prompt', async () => {
1484
1547
  const user = userEvent.setup();
1485
- renderComponent({
1548
+ const fetchCloudAuthApi = vi.fn(async () => ({
1549
+ ok: true,
1550
+ json: async () => ({ items: [] })
1551
+ }));
1552
+ renderMcpComponent({
1486
1553
  settingsModel: {
1487
1554
  runtimeMode: 'local',
1488
- isAuthenticated: false
1555
+ isAuthenticated: false,
1556
+ fetchCloudAuthApi
1489
1557
  }
1490
1558
  });
1491
- await user.click(screen.getByRole('button', { name: /MCP/i }));
1492
- expect(screen.getByText('Local MCP')).toBeInTheDocument();
1559
+ await openMcpSettings(user);
1560
+ expect(screen.getByRole('button', { name: 'Local MCP' })).toBeInTheDocument();
1561
+ expect(screen.getByRole('button', { name: 'Cloud MCP' })).toBeInTheDocument();
1493
1562
  expect(screen.getByRole('combobox', { name: 'MCP client' })).toHaveValue('general');
1494
1563
  expect(screen.getByText('General MCP Configuration Instructions')).toBeInTheDocument();
1495
1564
  expect(screen.queryByText('MCP Path Mapping (Host Root)')).not.toBeInTheDocument();
1496
1565
  await user.click(screen.getByRole('button', { name: 'Advanced' }));
1497
1566
  expect(screen.getByText('MCP Path Mapping (Host Root)')).toBeInTheDocument();
1567
+ await user.click(screen.getByRole('button', { name: 'Cloud MCP' }));
1568
+ expect(screen.getByText('Sign in to use Cloud MCP')).toBeInTheDocument();
1569
+ expect(screen.getByText(/Register or sign in to generate cloud MCP tokens/i)).toBeInTheDocument();
1498
1570
  expect(screen.queryByText('Cloud MCP Configuration')).not.toBeInTheDocument();
1499
- expect(screen.queryByRole('button', { name: 'Cloud MCP' })).not.toBeInTheDocument();
1571
+ expect(fetchCloudAuthApi).not.toHaveBeenCalled();
1500
1572
  });
1501
- it('4.24a Local MCP selector offers General first and keeps the rest alphabetical', async () => {
1573
+ it('4.24a Local signed-out Cloud MCP prompt can open login and registration', async () => {
1502
1574
  const user = userEvent.setup();
1503
- renderComponent({
1575
+ const onOpenCloudAuth = vi.fn();
1576
+ renderMcpComponent({
1577
+ settingsModel: {
1578
+ runtimeMode: 'local',
1579
+ isAuthenticated: false,
1580
+ onOpenCloudAuth
1581
+ }
1582
+ });
1583
+ await openMcpSettings(user);
1584
+ await user.click(screen.getByRole('button', { name: 'Cloud MCP' }));
1585
+ await user.click(screen.getByRole('button', { name: 'Sign In' }));
1586
+ expect(onOpenCloudAuth).toHaveBeenCalledWith('login');
1587
+ await user.click(screen.getByRole('button', { name: 'Register' }));
1588
+ expect(onOpenCloudAuth).toHaveBeenCalledWith('register');
1589
+ });
1590
+ it('4.24b Local MCP selector offers General first and keeps the rest alphabetical', async () => {
1591
+ const user = userEvent.setup();
1592
+ renderMcpComponent({
1504
1593
  settingsModel: {
1505
1594
  runtimeMode: 'local',
1506
1595
  isAuthenticated: false
1507
1596
  }
1508
1597
  });
1509
- await user.click(screen.getByRole('button', { name: /MCP/i }));
1598
+ await openMcpSettings(user);
1510
1599
  const options = screen.getAllByRole('option').map((option) => option.textContent);
1511
1600
  expect(options).toEqual([
1512
1601
  'General',
@@ -1521,9 +1610,9 @@ describe('TaskSettings Component', () => {
1521
1610
  'Windsurf'
1522
1611
  ]);
1523
1612
  });
1524
- it('4.24b Local Codex shows config.toml MCP output without token headers', async () => {
1613
+ it('4.24c Local Codex shows config.toml MCP output without token headers', async () => {
1525
1614
  const user = userEvent.setup();
1526
- renderComponent({
1615
+ renderMcpComponent({
1527
1616
  settingsModel: {
1528
1617
  runtimeMode: 'local',
1529
1618
  isAuthenticated: false,
@@ -1534,7 +1623,7 @@ describe('TaskSettings Component', () => {
1534
1623
  mcpScriptPath: '/usr/local/bin/taskforce'
1535
1624
  }
1536
1625
  });
1537
- await user.click(screen.getByRole('button', { name: /MCP/i }));
1626
+ await openMcpSettings(user);
1538
1627
  await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'codex');
1539
1628
  expect(await screen.findByText(/Codex Configuration Instructions/i)).toBeInTheDocument();
1540
1629
  expect((await screen.findAllByText(/\.codex\/config\.toml/i)).length).toBeGreaterThan(0);
@@ -1542,9 +1631,9 @@ describe('TaskSettings Component', () => {
1542
1631
  expect(screen.getByText(/command = "taskforce"/i)).toBeInTheDocument();
1543
1632
  expect(screen.queryByText(/Authorization/i)).not.toBeInTheDocument();
1544
1633
  });
1545
- it('4.24c Local VS Code uses stdio config under servers', async () => {
1634
+ it('4.24d Local VS Code uses stdio config under servers', async () => {
1546
1635
  const user = userEvent.setup();
1547
- renderComponent({
1636
+ renderMcpComponent({
1548
1637
  settingsModel: {
1549
1638
  runtimeMode: 'local',
1550
1639
  isAuthenticated: false,
@@ -1555,16 +1644,16 @@ describe('TaskSettings Component', () => {
1555
1644
  mcpScriptPath: '/usr/local/bin/taskforce'
1556
1645
  }
1557
1646
  });
1558
- await user.click(screen.getByRole('button', { name: /MCP/i }));
1647
+ await openMcpSettings(user);
1559
1648
  await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'vscode');
1560
1649
  expect(await screen.findByText(/VS Code Configuration Instructions/i)).toBeInTheDocument();
1561
1650
  expect(screen.getByText(/"servers"/)).toBeInTheDocument();
1562
1651
  expect(screen.getByText(/"type"/)).toBeInTheDocument();
1563
1652
  expect(screen.getByText(/"stdio"/)).toBeInTheDocument();
1564
1653
  });
1565
- it('4.24d Local Claude Code shows a stdio add command', async () => {
1654
+ it('4.24e Local Claude Code shows a stdio add command', async () => {
1566
1655
  const user = userEvent.setup();
1567
- renderComponent({
1656
+ renderMcpComponent({
1568
1657
  settingsModel: {
1569
1658
  runtimeMode: 'local',
1570
1659
  isAuthenticated: false,
@@ -1575,7 +1664,7 @@ describe('TaskSettings Component', () => {
1575
1664
  mcpScriptPath: '/usr/local/bin/taskforce'
1576
1665
  }
1577
1666
  });
1578
- await user.click(screen.getByRole('button', { name: /MCP/i }));
1667
+ await openMcpSettings(user);
1579
1668
  await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'claude-code');
1580
1669
  expect(await screen.findByText(/Claude Code Configuration Instructions/i)).toBeInTheDocument();
1581
1670
  expect(screen.getByText(/claude mcp add taskforce-/i)).toBeInTheDocument();
@@ -15,7 +15,7 @@ export interface DocMeta {
15
15
  updatedAt: string | null;
16
16
  editable?: boolean;
17
17
  attachmentCount?: number;
18
- attachmentState?: 'attached' | 'unattached' | 'standalone';
18
+ attachmentState?: 'attached' | 'unattached';
19
19
  }
20
20
  export declare function normalizeDocPath(value: string): string;
21
21
  export declare function docPathMatches(candidate: string, requested: string): boolean;
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
- import { Check, Loader2, RotateCcw, Plus, Trash2, X } from 'lucide-react';
3
+ import { Check, Link2, Loader2, Plus, RotateCcw, Trash2, Unlink, X } from 'lucide-react';
4
4
  import styles from '../../Taskforce.module.css';
5
5
  import { t } from '../../localization';
6
6
  import { TaskStatusActions } from './TaskStatusActions';
7
7
  import { getTaskReferenceDisplayState } from '../../utils/taskReferences';
8
8
  import { TaskReferenceBadge } from './TaskReferenceBadge';
9
+ import { HierarchyActionBadge } from '../ui/HierarchyActionBadge';
9
10
  import { formatInitiativeReference, getWorkstreamReferenceLabel } from '../../utils/planningReferences';
10
11
  export function TaskActionHeader({ editingTaskId, loading, autoSaveState = 'idle', title, handleSubmit, resetForm, handleCopyId, copiedId, tasks, currentTask: currentTaskProp, currentTaskWorkstream, currentTaskInitiative, workstreamInput = '', onWorkstreamInputChange, onSetWorkstreamForCurrentTask, handleToggleInProgress, handleToggleReview, handleToggleComplete, handleToggleCancel, handleSetStatus, handleArchiveTask, handleUnarchiveTask, handleRestoreDeletedTask, handlePermanentlyDeleteDeletedTask }) {
11
12
  const [showWorkstreamEditor, setShowWorkstreamEditor] = React.useState(false);
@@ -32,10 +33,15 @@ export function TaskActionHeader({ editingTaskId, loading, autoSaveState = 'idle
32
33
  return;
33
34
  onSetWorkstreamForCurrentTask?.();
34
35
  }, [onSetWorkstreamForCurrentTask, workstreamInput]);
35
- return (_jsx("div", { className: styles.stickyActionHeader, children: editingTaskId ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: styles.taskHierarchyHeader, children: [_jsxs("div", { className: styles.taskHierarchyBadgeRow, children: [currentTaskInitiative ? (_jsxs(_Fragment, { children: [_jsx(TaskReferenceBadge, { copied: copiedId === currentInitiativeReferenceLabel, onClick: (e) => handleCopyId(e, currentInitiativeReferenceLabel), disabled: loading, title: "Copy initiative reference", label: currentInitiativeReferenceLabel }), _jsx("span", { className: styles.taskHierarchyDivider, children: "/" })] })) : null, currentTaskWorkstream ? (_jsxs(_Fragment, { children: [_jsx(TaskReferenceBadge, { copied: copiedId === currentWorkstreamReferenceLabel, onClick: (e) => handleCopyId(e, currentWorkstreamReferenceLabel), disabled: loading, title: "Copy workstream reference", label: currentWorkstreamReferenceLabel }), _jsx("span", { className: styles.taskHierarchyDivider, children: "/" })] })) : (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: `${styles.taskIdBadge} ${styles.taskHierarchyAddBtn}`, onClick: () => {
36
- onWorkstreamInputChange?.('');
37
- setShowWorkstreamEditor((value) => !value);
38
- }, disabled: loading || isArchivedReadOnly || isDeletedReadOnly, title: "Attach to workstream", "aria-label": "Attach to workstream", children: _jsx(Plus, { size: 13 }) }), _jsx("span", { className: styles.taskHierarchyDivider, children: "/" })] })), currentTaskReferenceLabel ? (_jsx(TaskReferenceBadge, { copied: canCopyCurrentTaskReference && copiedId === currentTaskReference.label, onClick: (e) => handleCopyId(e, currentTaskReference.label), disabled: loading || !canCopyCurrentTaskReference, title: canCopyCurrentTaskReference ? t('actionHeader.copyTaskIdTitle') : 'Task reference pending sync', label: currentTaskReferenceLabel })) : null, currentTaskReferenceIsProvisional ? (_jsx("span", { className: styles.taskHierarchyMeta, title: "Temporary local reference until cloud sync assigns the final task number.", children: "Pending sync" })) : null] }), showWorkstreamEditor && !currentTaskWorkstream ? (_jsxs("div", { className: styles.taskHierarchyEditor, children: [_jsx("input", { type: "text", className: `${styles.input} ${styles.taskHierarchyInput}`, value: workstreamInput, onChange: (event) => onWorkstreamInputChange?.(event.target.value), placeholder: "WS-123", "aria-label": "Attach workstream reference", onKeyDown: (event) => {
36
+ const handleWorkstreamRemove = React.useCallback(() => {
37
+ setShowWorkstreamEditor(false);
38
+ onWorkstreamInputChange?.('');
39
+ onSetWorkstreamForCurrentTask?.(null);
40
+ }, [onSetWorkstreamForCurrentTask, onWorkstreamInputChange]);
41
+ return (_jsx("div", { className: styles.stickyActionHeader, children: editingTaskId ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: styles.taskHierarchyHeader, children: [_jsxs("div", { className: styles.taskHierarchyBadgeRow, children: [currentTaskWorkstream ? (_jsx(_Fragment, { children: _jsx(HierarchyActionBadge, { onClick: handleWorkstreamRemove, disabled: loading || isArchivedReadOnly || isDeletedReadOnly, title: "Remove from workstream", ariaLabel: "Remove from workstream", children: _jsx(Unlink, { size: 13 }) }) })) : null, currentTaskInitiative ? (_jsxs("span", { className: styles.taskHierarchySegment, children: [_jsx("span", { className: styles.taskHierarchyDivider, children: "/" }), _jsx(TaskReferenceBadge, { copied: copiedId === currentInitiativeReferenceLabel, onClick: (e) => handleCopyId(e, currentInitiativeReferenceLabel), disabled: loading, title: "Copy initiative reference", label: currentInitiativeReferenceLabel })] })) : null, currentTaskWorkstream ? (_jsxs("span", { className: styles.taskHierarchySegment, children: [_jsx("span", { className: styles.taskHierarchyDivider, children: "/" }), _jsx(TaskReferenceBadge, { copied: copiedId === currentWorkstreamReferenceLabel, onClick: (e) => handleCopyId(e, currentWorkstreamReferenceLabel), disabled: loading, title: "Copy workstream reference", label: currentWorkstreamReferenceLabel })] })) : (_jsx(_Fragment, { children: _jsx(HierarchyActionBadge, { onClick: () => {
42
+ onWorkstreamInputChange?.('');
43
+ setShowWorkstreamEditor((value) => !value);
44
+ }, disabled: loading || isArchivedReadOnly || isDeletedReadOnly, title: "Attach to workstream", ariaLabel: "Attach to workstream", children: _jsx(Link2, { size: 13 }) }) })), currentTaskReferenceLabel ? (_jsxs("span", { className: styles.taskHierarchySegment, children: [_jsx("span", { className: styles.taskHierarchyDivider, children: "/" }), _jsx(TaskReferenceBadge, { copied: canCopyCurrentTaskReference && copiedId === currentTaskReference.label, onClick: (e) => handleCopyId(e, currentTaskReference.label), disabled: loading || !canCopyCurrentTaskReference, title: canCopyCurrentTaskReference ? t('actionHeader.copyTaskIdTitle') : 'Task reference pending sync', label: currentTaskReferenceLabel })] })) : null, currentTaskReferenceIsProvisional ? (_jsx("span", { className: styles.taskHierarchyMeta, title: "Temporary local reference until cloud sync assigns the final task number.", children: "Pending sync" })) : null] }), showWorkstreamEditor && !currentTaskWorkstream ? (_jsxs("div", { className: styles.taskHierarchyEditor, children: [_jsx("input", { type: "text", className: `${styles.input} ${styles.taskHierarchyInput}`, value: workstreamInput, onChange: (event) => onWorkstreamInputChange?.(event.target.value), placeholder: "WS-123", "aria-label": "Attach workstream reference", onKeyDown: (event) => {
39
45
  if (event.key === 'Enter') {
40
46
  event.preventDefault();
41
47
  handleWorkstreamConfirm();
@@ -60,6 +60,16 @@ describe('TaskActionHeader', () => {
60
60
  expect(screen.getByText('IN-5')).toBeInTheDocument();
61
61
  expect(screen.getByText('WS-12')).toBeInTheDocument();
62
62
  expect(screen.getByText('T-1')).toBeInTheDocument();
63
+ expect(screen.getByRole('button', { name: /remove from workstream/i })).toBeInTheDocument();
64
+ });
65
+ it('removes the current workstream from the hierarchy header', async () => {
66
+ const user = userEvent.setup();
67
+ const onSetWorkstreamForCurrentTask = vi.fn();
68
+ const onWorkstreamInputChange = vi.fn();
69
+ render(_jsx(TaskActionHeader, { ...baseProps, editingTaskId: "task-1", title: "Polish task modal", currentTask: baseTask, currentTaskWorkstream: workstream, onSetWorkstreamForCurrentTask: onSetWorkstreamForCurrentTask, onWorkstreamInputChange: onWorkstreamInputChange }));
70
+ await user.click(screen.getByRole('button', { name: /remove from workstream/i }));
71
+ expect(onWorkstreamInputChange).toHaveBeenCalledWith('');
72
+ expect(onSetWorkstreamForCurrentTask).toHaveBeenCalledWith(null);
63
73
  });
64
74
  it('opens and cancels the inline workstream attach editor for unlinked tasks', async () => {
65
75
  const user = userEvent.setup();