@taskforcehq/taskforce 0.3.170 → 0.3.301

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 (698) hide show
  1. package/README.md +71 -21
  2. package/dist/Taskforce.module.css +3394 -5981
  3. package/dist/TaskforceCore.CategoryFallback.test.js +5 -2
  4. package/dist/TaskforceCore.filter-persistence.test.js +402 -0
  5. package/dist/TaskforceCore.js +388 -71
  6. package/dist/TaskforceCore.routing.test.js +311 -0
  7. package/dist/TaskforceCore.test.js +3344 -615
  8. package/dist/TaskforceWidget.js +3 -2
  9. package/dist/__tests__/localizationCatalogs.test.js +48 -31
  10. package/dist/__tests__/workflowSimulation.test.js +2 -2
  11. package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.d.ts +60 -0
  12. package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.js +324 -0
  13. package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.test.js +278 -0
  14. package/dist/annotatedAttachments/payload.d.ts +2 -0
  15. package/dist/annotatedAttachments/payload.js +54 -0
  16. package/dist/annotatedAttachments/payload.test.js +67 -0
  17. package/dist/annotatedAttachments/service.d.ts +49 -0
  18. package/dist/annotatedAttachments/service.js +121 -0
  19. package/dist/annotatedAttachments/types.d.ts +86 -0
  20. package/dist/cli.js +74 -25
  21. package/dist/compat/deploymentCompat.d.ts +12 -0
  22. package/dist/compat/deploymentCompat.js +43 -0
  23. package/dist/compat/workspaceSyncCompat.d.ts +6 -0
  24. package/dist/compat/workspaceSyncCompat.js +79 -0
  25. package/dist/compat/workspaceSyncCompat.test.js +87 -0
  26. package/dist/components/features/AgentsModule.d.ts +5 -0
  27. package/dist/components/features/AgentsModule.js +131 -0
  28. package/dist/components/features/AgentsModule.test.js +102 -0
  29. package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +24 -0
  30. package/dist/components/features/AnnotatedAttachmentWorkspace.js +1942 -0
  31. package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +2831 -0
  32. package/dist/components/features/DocumentGenerateModal.js +8 -16
  33. package/dist/components/features/DocumentIndex.d.ts +3 -4
  34. package/dist/components/features/DocumentIndex.js +22 -10
  35. package/dist/components/features/DocumentViewer.d.ts +6 -2
  36. package/dist/components/features/DocumentViewer.js +1000 -18
  37. package/dist/components/features/DocumentWorkspace.d.ts +40 -2
  38. package/dist/components/features/DocumentWorkspace.js +96 -16
  39. package/dist/components/features/DocumentWorkspace.test.js +1202 -0
  40. package/dist/components/features/InitiativesModule.d.ts +22 -0
  41. package/dist/components/features/InitiativesModule.js +33 -0
  42. package/dist/components/features/TaskSettings.d.ts +41 -32
  43. package/dist/components/features/TaskSettings.js +798 -313
  44. package/dist/components/features/TaskSettings.test.js +1221 -47
  45. package/dist/components/features/WorkflowsModule.d.ts +27 -0
  46. package/dist/components/features/WorkflowsModule.js +21 -0
  47. package/dist/components/features/documentWorkspaceFilters.d.ts +10 -0
  48. package/dist/components/features/documentWorkspaceFilters.js +11 -0
  49. package/dist/components/features/settings/CategoryManager.d.ts +2 -0
  50. package/dist/components/features/settings/CategoryManager.js +15 -8
  51. package/dist/components/features/settings/PriorityManager.d.ts +18 -0
  52. package/dist/components/features/settings/PriorityManager.js +51 -0
  53. package/dist/components/features/settings/TaxonomyEditor.d.ts +2 -1
  54. package/dist/components/features/settings/TaxonomyEditor.js +41 -18
  55. package/dist/components/features/settings/TaxonomyLibraryManager.d.ts +26 -0
  56. package/dist/components/features/settings/TaxonomyLibraryManager.js +301 -0
  57. package/dist/components/features/settings/TaxonomyLibraryManager.test.js +141 -0
  58. package/dist/components/features/settings/TaxonomyManager.d.ts +35 -12
  59. package/dist/components/features/settings/TaxonomyManager.js +59 -27
  60. package/dist/components/features/settings/TypeManager.d.ts +11 -0
  61. package/dist/components/features/settings/TypeManager.js +48 -11
  62. package/dist/components/features/settings/{WorkflowEditorPrototype.d.ts → WorkflowEditor.d.ts} +2 -2
  63. package/dist/components/features/settings/{WorkflowEditorPrototype.js → WorkflowEditor.js} +3 -3
  64. package/dist/components/task/TaskActionHeader.d.ts +12 -2
  65. package/dist/components/task/TaskActionHeader.js +64 -8
  66. package/dist/components/task/TaskActionHeader.test.js +73 -0
  67. package/dist/components/task/TaskCard.d.ts +14 -16
  68. package/dist/components/task/TaskCard.js +144 -174
  69. package/dist/components/task/TaskCard.test.js +416 -64
  70. package/dist/components/task/TaskContextUpload.d.ts +6 -5
  71. package/dist/components/task/TaskContextUpload.js +199 -64
  72. package/dist/components/task/TaskContextUpload.test.js +331 -14
  73. package/dist/components/task/TaskForm.d.ts +23 -29
  74. package/dist/components/task/TaskForm.js +348 -169
  75. package/dist/components/task/TaskForm.test.js +419 -23
  76. package/dist/components/task/TaskKanban.d.ts +20 -21
  77. package/dist/components/task/TaskKanban.js +176 -316
  78. package/dist/components/task/TaskKanban.planning-drop.test.js +93 -0
  79. package/dist/components/task/TaskList.d.ts +22 -11
  80. package/dist/components/task/TaskList.js +98 -12
  81. package/dist/components/task/TaskList.test.js +206 -7
  82. package/dist/components/task/TaskReferenceBadge.d.ts +12 -0
  83. package/dist/components/task/TaskReferenceBadge.js +5 -0
  84. package/dist/components/task/TaskStatusActions.d.ts +12 -0
  85. package/dist/components/task/TaskStatusActions.js +18 -0
  86. package/dist/components/task/TaskStatusActions.test.js +45 -0
  87. package/dist/components/ui/ImageReferenceBadge.d.ts +12 -0
  88. package/dist/components/ui/ImageReferenceBadge.js +5 -0
  89. package/dist/components/ui/LevelSelector.d.ts +2 -1
  90. package/dist/components/ui/LevelSelector.js +2 -2
  91. package/dist/components/ui/Markdown.d.ts +1 -2
  92. package/dist/components/ui/Markdown.js +2 -13
  93. package/dist/components/ui/Modal.d.ts +3 -2
  94. package/dist/components/ui/Modal.js +4 -3
  95. package/dist/components/ui/MultiSelectFilter.d.ts +3 -1
  96. package/dist/components/ui/MultiSelectFilter.js +2 -2
  97. package/dist/components/ui/ReferenceBadgeButton.d.ts +13 -0
  98. package/dist/components/ui/ReferenceBadgeButton.js +6 -0
  99. package/dist/components/ui/TaxonomyDropdown.d.ts +7 -1
  100. package/dist/components/ui/TaxonomyDropdown.js +58 -23
  101. package/dist/components/ui/TopNoticeLayer.d.ts +7 -0
  102. package/dist/components/ui/TopNoticeLayer.js +14 -0
  103. package/dist/components/ui/TopNoticeLayer.test.js +25 -0
  104. package/dist/components/views/AppShellHeader.d.ts +13 -0
  105. package/dist/components/views/AppShellHeader.js +19 -0
  106. package/dist/components/views/AppShellHeader.test.js +21 -0
  107. package/dist/components/views/PlanComparisonPage.d.ts +73 -0
  108. package/dist/components/views/PlanComparisonPage.js +196 -0
  109. package/dist/components/views/PlanComparisonPage.test.js +251 -0
  110. package/dist/components/views/PlansPage.d.ts +14 -0
  111. package/dist/components/views/PlansPage.js +355 -0
  112. package/dist/components/views/StandaloneLayout.d.ts +2 -2
  113. package/dist/components/views/StandaloneLayout.js +2227 -1188
  114. package/dist/components/views/WidgetView.d.ts +2 -2
  115. package/dist/components/views/WidgetView.js +149 -43
  116. package/dist/components/views/chrome/WorkspaceChromeSections.d.ts +57 -0
  117. package/dist/components/views/chrome/WorkspaceChromeSections.js +38 -0
  118. package/dist/components/views/chrome/WorkspaceChromeSections.test.js +54 -0
  119. package/dist/components/views/chrome/workspaceChrome.d.ts +31 -0
  120. package/dist/components/views/chrome/workspaceChrome.js +108 -0
  121. package/dist/components/views/chrome/workspaceChrome.test.js +52 -0
  122. package/dist/components/views/panels/DocumentFilterToolbar.d.ts +10 -0
  123. package/dist/components/views/panels/DocumentFilterToolbar.js +7 -0
  124. package/dist/components/views/panels/DocumentFilterToolbar.test.js +19 -0
  125. package/dist/components/views/panels/FilterToolbar.d.ts +68 -0
  126. package/dist/components/views/panels/FilterToolbar.js +21 -0
  127. package/dist/components/views/panels/FilterToolbar.test.d.ts +1 -0
  128. package/dist/components/views/panels/FilterToolbar.test.js +45 -0
  129. package/dist/components/views/panels/FilterToolbarControls.d.ts +47 -0
  130. package/dist/components/views/panels/FilterToolbarControls.js +61 -0
  131. package/dist/components/views/panels/PlanningDrawer.d.ts +115 -0
  132. package/dist/components/views/panels/PlanningDrawer.js +220 -0
  133. package/dist/components/views/panels/ScheduleSidebar.d.ts +52 -0
  134. package/dist/components/views/panels/ScheduleSidebar.js +127 -0
  135. package/dist/components/views/panels/scheduleUtils.d.ts +14 -0
  136. package/dist/components/views/panels/scheduleUtils.js +79 -0
  137. package/dist/components/views/teamManagementAccess.d.ts +6 -0
  138. package/dist/components/views/teamManagementAccess.js +18 -0
  139. package/dist/components/views/teamManagementAccess.test.d.ts +1 -0
  140. package/dist/components/views/teamManagementAccess.test.js +44 -0
  141. package/dist/config/clientDeployment.d.ts +2 -0
  142. package/dist/config/clientDeployment.js +13 -0
  143. package/dist/config/cloudEnvironment.d.ts +15 -0
  144. package/dist/config/cloudEnvironment.js +60 -0
  145. package/dist/config/deployment.d.ts +20 -0
  146. package/dist/config/deployment.js +67 -0
  147. package/dist/config/deployment.test.d.ts +1 -0
  148. package/dist/config/deployment.test.js +88 -0
  149. package/dist/config/envSchema.js +36 -8
  150. package/dist/config/envSchema.test.js +28 -0
  151. package/dist/core/AiProfileService.d.ts +66 -0
  152. package/dist/core/AiProfileService.js +320 -0
  153. package/dist/core/AiProfileService.test.d.ts +1 -0
  154. package/dist/core/AiProfileService.test.js +164 -0
  155. package/dist/core/AiProfiles.test.d.ts +1 -0
  156. package/dist/core/AiProfiles.test.js +449 -0
  157. package/dist/core/AttachmentLinkService.d.ts +15 -0
  158. package/dist/core/AttachmentLinkService.js +157 -0
  159. package/dist/core/AuthTokenService.d.ts +44 -0
  160. package/dist/core/AuthTokenService.js +207 -0
  161. package/dist/core/CreatedBy.test.js +134 -6
  162. package/dist/core/DeletedTaskLifecycleService.d.ts +33 -0
  163. package/dist/core/DeletedTaskLifecycleService.js +158 -0
  164. package/dist/core/DeletedTaskTrash.test.d.ts +1 -0
  165. package/dist/core/DeletedTaskTrash.test.js +104 -0
  166. package/dist/core/EntitlementsPolicy.test.js +78 -5
  167. package/dist/core/GlobalSettingsService.d.ts +16 -0
  168. package/dist/core/GlobalSettingsService.js +370 -0
  169. package/dist/core/JsonBackupRuntime.test.d.ts +1 -0
  170. package/dist/core/JsonBackupRuntime.test.js +155 -0
  171. package/dist/core/McpTokenService.d.ts +118 -0
  172. package/dist/core/McpTokenService.js +921 -0
  173. package/dist/core/PlanEntitlementService.d.ts +201 -0
  174. package/dist/core/PlanEntitlementService.js +1442 -0
  175. package/dist/core/PlanVersionPolicy.test.d.ts +1 -0
  176. package/dist/core/PlanVersionPolicy.test.js +178 -0
  177. package/dist/core/PlanningEntities.test.d.ts +1 -0
  178. package/dist/core/PlanningEntities.test.js +95 -0
  179. package/dist/core/ScheduleFields.test.js +15 -19
  180. package/dist/core/SignupMonetizationSettings.test.d.ts +1 -0
  181. package/dist/core/SignupMonetizationSettings.test.js +159 -0
  182. package/dist/core/Stats.test.d.ts +1 -0
  183. package/dist/core/Stats.test.js +36 -0
  184. package/dist/core/SyncReconciliationService.d.ts +93 -0
  185. package/dist/core/SyncReconciliationService.js +406 -0
  186. package/dist/core/SystemAdmin.test.js +578 -13
  187. package/dist/core/TaskActivityService.d.ts +21 -0
  188. package/dist/core/TaskActivityService.js +130 -0
  189. package/dist/core/TaskAttachmentsCanonical.test.d.ts +1 -0
  190. package/dist/core/TaskAttachmentsCanonical.test.js +306 -0
  191. package/dist/core/TaskAuditTimeline.test.d.ts +1 -0
  192. package/dist/core/TaskAuditTimeline.test.js +132 -0
  193. package/dist/core/TaskTaxonomyValidation.test.js +338 -15
  194. package/dist/core/Taskforce.d.ts +930 -183
  195. package/dist/core/Taskforce.ids.test.js +252 -0
  196. package/dist/core/Taskforce.js +3848 -2532
  197. package/dist/core/Taskforce.mcpAuth.test.d.ts +1 -0
  198. package/dist/core/Taskforce.mcpAuth.test.js +343 -0
  199. package/dist/core/Taskforce.realtimeMutation.test.js +57 -2
  200. package/dist/core/TaxonomySettingsService.d.ts +18 -0
  201. package/dist/core/TaxonomySettingsService.js +162 -0
  202. package/dist/core/WorkspaceDelete.test.js +32 -13
  203. package/dist/core/WorkspaceLifecycleService.d.ts +11 -0
  204. package/dist/core/WorkspaceLifecycleService.js +88 -0
  205. package/dist/core/WorkspacePermissions.test.js +95 -0
  206. package/dist/core/WorkspacePlanMode.test.js +114 -1
  207. package/dist/core/WorkspacePreferenceService.d.ts +41 -0
  208. package/dist/core/WorkspacePreferenceService.js +246 -0
  209. package/dist/core/WorkspaceSyncSettings.test.d.ts +1 -0
  210. package/dist/core/WorkspaceSyncSettings.test.js +65 -0
  211. package/dist/core/__tests__/TaskAttachmentsCanonical.test.d.ts +1 -0
  212. package/dist/core/__tests__/TaskAttachmentsCanonical.test.js +212 -0
  213. package/dist/core/shared.d.ts +36 -0
  214. package/dist/core/shared.js +128 -0
  215. package/dist/core/types.d.ts +614 -0
  216. package/dist/core/types.js +27 -0
  217. package/dist/documentReviews/DocumentReviewCommentStore.d.ts +56 -0
  218. package/dist/documentReviews/DocumentReviewCommentStore.js +230 -0
  219. package/dist/documentReviews/DocumentReviewSessionStore.d.ts +52 -0
  220. package/dist/documentReviews/DocumentReviewSessionStore.js +193 -0
  221. package/dist/documentReviews/DocumentReviewSessionStore.test.d.ts +1 -0
  222. package/dist/documentReviews/DocumentReviewSessionStore.test.js +127 -0
  223. package/dist/documentReviews/service.d.ts +68 -0
  224. package/dist/documentReviews/service.js +210 -0
  225. package/dist/documentReviews/types.d.ts +58 -0
  226. package/dist/documentReviews/types.js +1 -0
  227. package/dist/hooks/auth/useAuthGuardPolicy.d.ts +2 -0
  228. package/dist/hooks/auth/useAuthGuardPolicy.js +1 -0
  229. package/dist/hooks/tasks/useCurrentTaskRelations.d.ts +15 -0
  230. package/dist/hooks/tasks/useCurrentTaskRelations.js +37 -0
  231. package/dist/hooks/tasks/useTaskActions.d.ts +3 -0
  232. package/dist/hooks/tasks/useTaskActions.js +2 -0
  233. package/dist/hooks/tasks/useTaskEditorNavigation.d.ts +65 -0
  234. package/dist/hooks/tasks/useTaskEditorNavigation.js +235 -0
  235. package/dist/hooks/tasks/useTaskFilters.d.ts +2 -0
  236. package/dist/hooks/tasks/useTaskFilters.js +2 -0
  237. package/dist/hooks/tasks/useTaskFormActions.d.ts +58 -0
  238. package/dist/hooks/tasks/useTaskFormActions.js +365 -0
  239. package/dist/hooks/tasks/useTaskRelationships.d.ts +16 -0
  240. package/dist/hooks/tasks/useTaskRelationships.js +64 -0
  241. package/dist/hooks/ui/useResourceExport.d.ts +19 -0
  242. package/dist/hooks/ui/useResourceExport.js +84 -0
  243. package/dist/hooks/ui/useSettingsModel.d.ts +203 -0
  244. package/dist/hooks/ui/useSettingsModel.js +125 -0
  245. package/dist/hooks/ui/useSettingsPersistence.d.ts +36 -0
  246. package/dist/hooks/ui/useSettingsPersistence.js +254 -0
  247. package/dist/hooks/ui/useSettingsTaxonomyManagement.d.ts +119 -0
  248. package/dist/hooks/ui/useSettingsTaxonomyManagement.js +536 -0
  249. package/dist/hooks/ui/useUiNotice.d.ts +1 -0
  250. package/dist/hooks/ui/useUiNotice.js +1 -0
  251. package/dist/hooks/ui/useWorkflowTemplates.d.ts +37 -0
  252. package/dist/hooks/ui/useWorkflowTemplates.js +111 -0
  253. package/dist/hooks/ui/useZenMode.d.ts +5 -0
  254. package/dist/hooks/ui/useZenMode.js +32 -0
  255. package/dist/hooks/useFilterSort.d.ts +60 -0
  256. package/dist/hooks/useFilterSort.js +362 -0
  257. package/dist/hooks/useFilterSort.test.d.ts +1 -0
  258. package/dist/hooks/useFilterSort.test.js +333 -0
  259. package/dist/hooks/useRealtimeSync.d.ts +1 -0
  260. package/dist/hooks/useRealtimeSync.js +24 -9
  261. package/dist/hooks/useRealtimeSync.test.js +28 -1
  262. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.d.ts +1 -0
  263. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +34 -0
  264. package/dist/hooks/useSyncOrchestrator.d.ts +65 -103
  265. package/dist/hooks/useSyncOrchestrator.js +1538 -670
  266. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +700 -174
  267. package/dist/hooks/useTaskData.d.ts +35 -0
  268. package/dist/hooks/useTaskData.js +243 -0
  269. package/dist/hooks/useTaskData.test.d.ts +1 -0
  270. package/dist/hooks/useTaskData.test.js +180 -0
  271. package/dist/hooks/useTaskMutations.d.ts +46 -0
  272. package/dist/hooks/useTaskMutations.js +472 -0
  273. package/dist/hooks/useTaskMutations.test.d.ts +1 -0
  274. package/dist/hooks/useTaskMutations.test.js +80 -0
  275. package/dist/hooks/useTaskforce.contract.test.d.ts +1 -0
  276. package/dist/hooks/useTaskforce.contract.test.js +7 -0
  277. package/dist/hooks/useTaskforce.d.ts +260 -295
  278. package/dist/hooks/useTaskforce.js +1954 -2623
  279. package/dist/hooks/useTaskforce.priority-filters.test.js +6 -11
  280. package/dist/hooks/useTaskforce.sync-behavior.test.d.ts +1 -0
  281. package/dist/hooks/useTaskforce.sync-behavior.test.js +1120 -0
  282. package/dist/hooks/useUiNotice.d.ts +16 -0
  283. package/dist/hooks/useUiNotice.js +49 -0
  284. package/dist/hooks/useWorkspaceSyncController.d.ts +23 -46
  285. package/dist/hooks/useWorkspaceSyncController.js +280 -461
  286. package/dist/hooks/useWorkspaceSyncController.test.js +106 -199
  287. package/dist/hooks/workspace/useTaskforceApiRouting.d.ts +8 -0
  288. package/dist/hooks/workspace/useTaskforceApiRouting.js +91 -0
  289. package/dist/hooks/workspace/useTaskforceApiRouting.test.d.ts +1 -0
  290. package/dist/hooks/workspace/useTaskforceApiRouting.test.js +60 -0
  291. package/dist/hooks/workspace/useWorkspaceSyncController.d.ts +1 -0
  292. package/dist/hooks/workspace/useWorkspaceSyncController.js +1 -0
  293. package/dist/hooks/workspace/workspaceLocalState.d.ts +30 -0
  294. package/dist/hooks/workspace/workspaceLocalState.js +101 -0
  295. package/dist/localization/locales/en-US.d.ts +1 -8
  296. package/dist/localization/locales/en-US.js +4 -11
  297. package/dist/localization/locales/es-419.js +2 -9
  298. package/dist/localization/locales/pt-BR.js +2 -9
  299. package/dist/localization/localizationCatalogs.test.d.ts +1 -0
  300. package/dist/localization/localizationCatalogs.test.js +37 -0
  301. package/dist/main.d.ts +1 -0
  302. package/dist/main.js +9 -15
  303. package/dist/mcp/clientIntegrations.d.ts +33 -0
  304. package/dist/mcp/clientIntegrations.js +199 -0
  305. package/dist/mcp/clientIntegrations.test.d.ts +1 -0
  306. package/dist/mcp/clientIntegrations.test.js +110 -0
  307. package/dist/mcp/index.d.ts +2 -0
  308. package/dist/mcp/index.js +2 -0
  309. package/dist/mcp/optionResolution.test.js +6 -2
  310. package/dist/mcp/runtime.d.ts +120 -0
  311. package/dist/mcp/runtime.js +5668 -0
  312. package/dist/mcp/runtime.test.d.ts +1 -0
  313. package/dist/mcp/runtime.test.js +3299 -0
  314. package/dist/mcp/server.js +2 -1793
  315. package/dist/mcp/structuredCommentValidation.test.d.ts +1 -0
  316. package/dist/{__tests__ → mcp}/structuredCommentValidation.test.js +1 -1
  317. package/dist/mcp/structuredDocValidation.js +1 -2
  318. package/dist/mcp/structuredDocValidation.test.d.ts +1 -0
  319. package/dist/{__tests__ → mcp}/structuredDocValidation.test.js +9 -4
  320. package/dist/mcp/toolRegistry.d.ts +570 -0
  321. package/dist/mcp/toolRegistry.js +90 -0
  322. package/dist/mcp/toolRegistry.test.d.ts +1 -0
  323. package/dist/mcp/toolRegistry.test.js +30 -0
  324. package/dist/migrations/billingSchemaParity.test.js +111 -0
  325. package/dist/migrations/taskSchemaMigrations.d.ts +5 -0
  326. package/dist/migrations/taskSchemaMigrations.js +681 -39
  327. package/dist/migrations/taskSchemaMigrations.test.d.ts +1 -0
  328. package/dist/migrations/taskSchemaMigrations.test.js +210 -0
  329. package/dist/plugins/vite.js +13 -7
  330. package/dist/public/apple-touch-icon.png +0 -0
  331. package/dist/public/archive/phoenix/apple-touch-icon.png +0 -0
  332. package/dist/public/archive/phoenix/favicon.ico +0 -0
  333. package/dist/public/archive/phoenix/site.webmanifest +1 -0
  334. package/dist/public/favicon-96x96.png +0 -0
  335. package/dist/public/favicon.ico +0 -0
  336. package/dist/public/favicon.svg +17 -0
  337. package/dist/public/site.webmanifest +21 -1
  338. package/dist/public/web-app-manifest-192x192.png +0 -0
  339. package/dist/public/web-app-manifest-512x512.png +0 -0
  340. package/dist/releaseVersion.test.d.ts +1 -0
  341. package/dist/{__tests__/releaseVersion.test.js → releaseVersion.test.js} +1 -1
  342. package/dist/resources/templates/WORKFLOW_V2_POLICY.md +1 -1
  343. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +62 -52
  344. package/dist/resources/templates/workflows/collaborate.yaml +11 -5
  345. package/dist/resources/templates/workflows/do.yaml +8 -2
  346. package/dist/resources/templates/workflows/evaluate.yaml +13 -7
  347. package/dist/resources/templates/workflows/execute.yaml +40 -23
  348. package/dist/resources/templates/workflows/plan.yaml +15 -8
  349. package/dist/resources/templates/workflows/review.yaml +14 -8
  350. package/dist/runtime/appGateDefinitions.d.ts +32 -0
  351. package/dist/runtime/appGateDefinitions.js +32 -0
  352. package/dist/runtime/appGates.d.ts +29 -0
  353. package/dist/runtime/appGates.js +53 -0
  354. package/dist/runtime/appGates.test.d.ts +1 -0
  355. package/dist/runtime/appGates.test.js +42 -0
  356. package/dist/runtime/migrationFlags.d.ts +1 -0
  357. package/dist/runtime/migrationFlags.js +1 -0
  358. package/dist/server/annotatedAttachmentsRoutes.test.d.ts +1 -0
  359. package/dist/server/annotatedAttachmentsRoutes.test.js +473 -0
  360. package/dist/server/auth.js +14 -2
  361. package/dist/server/auth.test.js +21 -1
  362. package/dist/server/cors.js +11 -5
  363. package/dist/server/cors.test.js +6 -0
  364. package/dist/server/documentReviewRoutes.test.d.ts +1 -0
  365. package/dist/server/documentReviewRoutes.test.js +157 -0
  366. package/dist/server/index.cookieProxy.test.js +46 -1
  367. package/dist/server/index.d.ts +20 -0
  368. package/dist/server/index.js +168 -41
  369. package/dist/server/index.rateLimit.test.js +6 -0
  370. package/dist/server/index.test.js +133 -2
  371. package/dist/server/mockStripe.d.ts +72 -0
  372. package/dist/server/mockStripe.js +357 -0
  373. package/dist/server/realtimeSyncWs.js +4 -0
  374. package/dist/server/routes/admin.d.ts +114 -0
  375. package/dist/server/routes/admin.js +2226 -0
  376. package/dist/server/routes/annotatedAttachments.d.ts +24 -0
  377. package/dist/server/routes/annotatedAttachments.js +235 -0
  378. package/dist/server/routes/auth.d.ts +61 -0
  379. package/dist/server/routes/auth.js +1296 -0
  380. package/dist/server/{routes.billing.d.ts → routes/billing.d.ts} +3 -2
  381. package/dist/server/routes/billing.js +2001 -0
  382. package/dist/server/routes/billing.test.d.ts +1 -0
  383. package/dist/server/routes/billing.test.js +1631 -0
  384. package/dist/server/routes/documentReviews.d.ts +11 -0
  385. package/dist/server/routes/documentReviews.js +191 -0
  386. package/dist/server/routes/documents.d.ts +118 -0
  387. package/dist/server/routes/documents.js +1113 -0
  388. package/dist/server/routes/resources.d.ts +25 -0
  389. package/dist/server/routes/resources.js +225 -0
  390. package/dist/server/routes/shared.d.ts +35 -0
  391. package/dist/server/routes/shared.js +305 -0
  392. package/dist/server/routes/shared.test.d.ts +1 -0
  393. package/dist/server/routes/shared.test.js +32 -0
  394. package/dist/server/{routes.sync.d.ts → routes/sync.d.ts} +5 -5
  395. package/dist/server/routes/sync.integration.test.d.ts +1 -0
  396. package/dist/server/routes/sync.integration.test.js +2003 -0
  397. package/dist/server/routes/sync.js +3143 -0
  398. package/dist/server/routes/tasks.d.ts +72 -0
  399. package/dist/server/routes/tasks.js +1252 -0
  400. package/dist/server/routes/workspaces.d.ts +32 -0
  401. package/dist/server/routes/workspaces.js +307 -0
  402. package/dist/server/routes.d.ts +8 -9
  403. package/dist/server/routes.js +968 -4092
  404. package/dist/server/routes.test.js +9043 -2415
  405. package/dist/server/runtimeMode.d.ts +1 -1
  406. package/dist/server/runtimeMode.js +3 -18
  407. package/dist/server/runtimeMode.test.js +3 -3
  408. package/dist/server/securityHeaders.d.ts +1 -1
  409. package/dist/server/securityHeaders.js +65 -6
  410. package/dist/server/securityHeaders.test.js +40 -2
  411. package/dist/services/templateEngine.test.d.ts +1 -0
  412. package/dist/{__tests__ → services}/templateEngine.test.js +1 -1
  413. package/dist/services/templates.test.d.ts +1 -0
  414. package/dist/{__tests__ → services}/templates.test.js +1 -1
  415. package/dist/services/workflowDocumentFixtures.test.d.ts +1 -0
  416. package/dist/{__tests__ → services}/workflowDocumentFixtures.test.js +4 -4
  417. package/dist/services/workflowExportMatrix.test.d.ts +1 -0
  418. package/dist/{__tests__ → services}/workflowExportMatrix.test.js +1 -1
  419. package/dist/services/workflowExportSnapshots.test.d.ts +1 -0
  420. package/dist/services/workflowGeneration.test.d.ts +1 -0
  421. package/dist/services/workflowSimulation.test.d.ts +1 -0
  422. package/dist/services/workflowSimulation.test.js +113 -0
  423. package/dist/services/workflowV2Policy.test.d.ts +1 -0
  424. package/dist/{__tests__ → services}/workflowV2Policy.test.js +2 -2
  425. package/dist/shared/aiProfileSurfaceType.d.ts +9 -0
  426. package/dist/shared/aiProfileSurfaceType.js +49 -0
  427. package/dist/shared/customTaxonomyLibrary.d.ts +11 -0
  428. package/dist/shared/customTaxonomyLibrary.js +276 -0
  429. package/dist/shared/customTaxonomyLibrary.test.d.ts +1 -0
  430. package/dist/shared/customTaxonomyLibrary.test.js +20 -0
  431. package/dist/shared/taxonomyCatalog.d.ts +19 -0
  432. package/dist/shared/taxonomyCatalog.js +36 -0
  433. package/dist/shared/taxonomyDefaults.d.ts +29 -0
  434. package/dist/shared/taxonomyDefaults.js +26 -0
  435. package/dist/shared/taxonomyLibrary.d.ts +15 -0
  436. package/dist/shared/taxonomyLibrary.js +98 -0
  437. package/dist/shared/taxonomyLibrary.test.d.ts +1 -0
  438. package/dist/shared/taxonomyLibrary.test.js +26 -0
  439. package/dist/shared/taxonomyState.d.ts +36 -0
  440. package/dist/shared/taxonomyState.js +73 -0
  441. package/dist/shared/taxonomyState.test.d.ts +1 -0
  442. package/dist/shared/taxonomyState.test.js +71 -0
  443. package/dist/shared/workflowDefaults.d.ts +9 -0
  444. package/dist/shared/workflowDefaults.js +11 -0
  445. package/dist/storage/documentIntegrity.d.ts +2 -0
  446. package/dist/storage/documentIntegrity.js +34 -17
  447. package/dist/storage/documentPurge.d.ts +2 -1
  448. package/dist/storage/documentPurge.js +35 -8
  449. package/dist/storage/index.d.ts +4 -0
  450. package/dist/storage/index.js +2 -0
  451. package/dist/storage/objectStorage.d.ts +1 -1
  452. package/dist/storage/objectStorageClient.d.ts +6 -4
  453. package/dist/storage/objectStorageClient.js +77 -29
  454. package/dist/storage/objectStorageClient.test.d.ts +1 -0
  455. package/dist/storage/objectStorageClient.test.js +57 -0
  456. package/dist/storage/objectStorageConfig.js +3 -1
  457. package/dist/storage/objectStorageConfig.test.d.ts +1 -0
  458. package/dist/storage/objectStorageConfig.test.js +38 -0
  459. package/dist/storage/postgresAdapter.d.ts +5 -0
  460. package/dist/storage/postgresAdapter.js +2 -2
  461. package/dist/storage/postgresAdapter.test.d.ts +1 -0
  462. package/dist/storage/postgresAdapter.test.js +29 -0
  463. package/dist/storage/taskAssetStore.d.ts +7 -4
  464. package/dist/storage/taskAssetStore.js +65 -60
  465. package/dist/storage/workspaceAssetStore.d.ts +122 -0
  466. package/dist/storage/workspaceAssetStore.js +496 -0
  467. package/dist/storage/workspaceAssetStore.test.d.ts +1 -0
  468. package/dist/storage/workspaceAssetStore.test.js +501 -0
  469. package/dist/sync/cloudSyncApi.d.ts +33 -2
  470. package/dist/sync/cloudSyncApi.js +37 -15
  471. package/dist/sync/cloudSyncApi.test.js +60 -3
  472. package/dist/sync/index.d.ts +2 -0
  473. package/dist/sync/index.js +3 -0
  474. package/dist/sync/syncService.d.ts +25 -0
  475. package/dist/sync/syncService.js +184 -16
  476. package/dist/sync/syncService.test.js +289 -25
  477. package/dist/sync/workspaceRepair.d.ts +114 -0
  478. package/dist/sync/workspaceRepair.js +566 -0
  479. package/dist/sync/workspaceSyncModel.d.ts +230 -2
  480. package/dist/sync/workspaceSyncModel.js +456 -17
  481. package/dist/sync/workspaceSyncModel.test.js +642 -0
  482. package/dist/sync/workspaceSyncState.d.ts +12 -0
  483. package/dist/sync/workspaceSyncState.js +12 -0
  484. package/dist/sync/workspaceSyncSurface.d.ts +33 -0
  485. package/dist/sync/workspaceSyncSurface.js +74 -0
  486. package/dist/types.d.ts +143 -18
  487. package/dist/types.js +9 -24
  488. package/dist/ui/assets/AgentsModule-Bdq3T1Ts.js +1 -0
  489. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +1 -0
  490. package/dist/ui/assets/AnnotatedAttachmentWorkspace-jKUVo-PJ.js +3 -0
  491. package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +1 -0
  492. package/dist/ui/assets/DocumentWorkspace-DuQa-uIC.js +250 -0
  493. package/dist/ui/assets/InitiativesModule-C8Pi2VTk.js +1 -0
  494. package/dist/ui/assets/PlansPage-B6BDECSi.js +1 -0
  495. package/dist/ui/assets/PlansPage-BlVC_lRq.css +1 -0
  496. package/dist/ui/assets/TaskSettings-BwB9NtmJ.css +1 -0
  497. package/dist/ui/assets/TaskSettings-DUJgRTPU.js +8 -0
  498. package/dist/ui/assets/WorkflowsModule-b6TLuHwJ.js +5 -0
  499. package/dist/ui/assets/index-BiiF1cLQ.css +1 -0
  500. package/dist/ui/assets/index-xBWLPOEb.js +6 -0
  501. package/dist/ui/assets/vendor-dnd-DRzYolkg.js +5 -0
  502. package/dist/ui/assets/{vendor-icons-ZFG3SGwo.js → vendor-icons-QZyhEwgT.js} +1 -1
  503. package/dist/ui/assets/{vendor-markdown-CKYkQHfj.js → vendor-markdown-BUxTU7dS.js} +1 -1
  504. package/dist/ui/assets/vendor-react-CKJs5o3c.js +61 -0
  505. package/dist/ui/assets/vendor-router-BbWMxlnO.js +3 -0
  506. package/dist/ui/branding/logos/logo.png +0 -0
  507. package/dist/ui/branding/logos/logo_dark.svg +27 -0
  508. package/dist/ui/branding/logos/logo_favicon.svg +35 -0
  509. package/dist/ui/branding/logos/logo_light.svg +27 -0
  510. package/dist/ui/favicon/apple-touch-icon.png +0 -0
  511. package/dist/ui/favicon/archive/phoenix/apple-touch-icon.png +0 -0
  512. package/dist/ui/favicon/archive/phoenix/favicon.ico +0 -0
  513. package/dist/ui/favicon/archive/phoenix/site.webmanifest +1 -0
  514. package/dist/ui/favicon/favicon-96x96.png +0 -0
  515. package/dist/ui/favicon/favicon.ico +0 -0
  516. package/dist/ui/favicon/favicon.svg +17 -0
  517. package/dist/ui/favicon/site.webmanifest +21 -1
  518. package/dist/ui/favicon/web-app-manifest-192x192.png +0 -0
  519. package/dist/ui/favicon/web-app-manifest-512x512.png +0 -0
  520. package/dist/ui/index.html +10 -7
  521. package/dist/ui/product/screen_01.png +0 -0
  522. package/dist/ui/product/screen_02.png +0 -0
  523. package/dist/ui/product/screen_03.png +0 -0
  524. package/dist/utils/annotatedAttachments.d.ts +20 -0
  525. package/dist/utils/annotatedAttachments.js +74 -0
  526. package/dist/utils/annotatedAttachments.test.d.ts +1 -0
  527. package/dist/utils/annotatedAttachments.test.js +108 -0
  528. package/dist/utils/assignees.d.ts +26 -0
  529. package/dist/utils/assignees.js +132 -0
  530. package/dist/utils/canonicalAssetKind.d.ts +16 -0
  531. package/dist/utils/canonicalAssetKind.js +39 -0
  532. package/dist/utils/canonicalAssetKind.test.d.ts +1 -0
  533. package/dist/utils/canonicalAssetKind.test.js +24 -0
  534. package/dist/utils/coalescedRefresh.d.ts +5 -0
  535. package/dist/utils/coalescedRefresh.js +16 -0
  536. package/dist/utils/coalescedRefresh.test.d.ts +1 -0
  537. package/dist/utils/coalescedRefresh.test.js +30 -0
  538. package/dist/utils/constants.d.ts +16 -3
  539. package/dist/utils/constants.icons.test.js +2 -1
  540. package/dist/utils/constants.js +70 -13
  541. package/dist/utils/contextAssetEvents.d.ts +7 -0
  542. package/dist/utils/contextAssetEvents.js +6 -0
  543. package/dist/utils/contextFiles.d.ts +14 -3
  544. package/dist/utils/contextFiles.js +40 -8
  545. package/dist/utils/documentManagerApi.d.ts +6 -0
  546. package/dist/utils/documentManagerApi.js +19 -0
  547. package/dist/utils/documentNames.d.ts +24 -0
  548. package/dist/utils/documentNames.js +91 -0
  549. package/dist/utils/documentReferences.d.ts +9 -0
  550. package/dist/utils/documentReferences.js +11 -0
  551. package/dist/utils/entityReferences.d.ts +9 -0
  552. package/dist/utils/entityReferences.js +32 -0
  553. package/dist/utils/imageReferences.d.ts +9 -0
  554. package/dist/utils/imageReferences.js +11 -0
  555. package/dist/utils/planParser.d.ts +2 -2
  556. package/dist/utils/planParser.js +11 -11
  557. package/dist/utils/planParser.test.d.ts +1 -0
  558. package/dist/{__tests__ → utils}/planParser.test.js +22 -22
  559. package/dist/utils/planningReferences.d.ts +17 -0
  560. package/dist/utils/planningReferences.js +30 -0
  561. package/dist/utils/planningReferences.test.d.ts +1 -0
  562. package/dist/utils/planningReferences.test.js +17 -0
  563. package/dist/utils/taskActivity.d.ts +9 -0
  564. package/dist/utils/taskActivity.js +147 -0
  565. package/dist/utils/taskActivity.test.d.ts +1 -0
  566. package/dist/utils/taskActivity.test.js +34 -0
  567. package/dist/utils/taskDraftDefaults.d.ts +21 -0
  568. package/dist/utils/taskDraftDefaults.js +40 -0
  569. package/dist/utils/taskDraftDefaults.test.d.ts +1 -0
  570. package/dist/utils/taskDraftDefaults.test.js +34 -0
  571. package/dist/utils/taskNormalization.d.ts +25 -0
  572. package/dist/utils/taskNormalization.js +161 -0
  573. package/dist/utils/taskNormalization.test.d.ts +1 -0
  574. package/dist/utils/taskNormalization.test.js +158 -0
  575. package/dist/utils/taskReferences.d.ts +5 -0
  576. package/dist/utils/taskReferences.js +11 -0
  577. package/dist/utils/taskforceApiClient.d.ts +2 -0
  578. package/dist/utils/taskforceApiClient.js +101 -3
  579. package/dist/utils/taskforceApiClient.test.d.ts +1 -0
  580. package/dist/utils/taskforceApiClient.test.js +44 -0
  581. package/dist/utils/taxonomyFilterOptions.d.ts +2 -0
  582. package/dist/utils/taxonomyFilterOptions.js +17 -0
  583. package/dist/utils/taxonomyFormDefaults.d.ts +3 -0
  584. package/dist/utils/taxonomyFormDefaults.js +28 -0
  585. package/dist/utils/taxonomyFormDefaults.test.d.ts +1 -0
  586. package/dist/utils/taxonomyFormDefaults.test.js +63 -0
  587. package/dist/utils/taxonomyLifecycle.d.ts +3 -0
  588. package/dist/utils/taxonomyLifecycle.js +17 -0
  589. package/dist/utils/taxonomySorting.d.ts +7 -0
  590. package/dist/utils/taxonomySorting.js +74 -0
  591. package/dist/utils/theme.d.ts +15 -0
  592. package/dist/utils/theme.js +23 -0
  593. package/dist/utils/uiNotice.d.ts +7 -0
  594. package/dist/utils/uiNotice.js +6 -0
  595. package/dist/utils/uiNotice.test.d.ts +1 -0
  596. package/dist/utils/uiNotice.test.js +18 -0
  597. package/dist/utils/workspaceIdentity.d.ts +14 -0
  598. package/dist/utils/workspaceIdentity.js +21 -0
  599. package/dist/utils/workspaceIdentity.test.js +30 -1
  600. package/dist/utils/workspaceSyncPresentation.d.ts +20 -0
  601. package/dist/utils/workspaceSyncPresentation.js +21 -0
  602. package/dist/utils/workspaceSyncPresentation.test.d.ts +1 -0
  603. package/dist/utils/workspaceSyncPresentation.test.js +36 -0
  604. package/package.json +30 -23
  605. package/scripts/bootstrap-playwright-auth.mjs +155 -0
  606. package/scripts/check-compat-boundaries.mjs +41 -0
  607. package/scripts/check-env.ts +9 -2
  608. package/scripts/debug-login.mjs +32 -0
  609. package/scripts/deploy-prod.sh +41 -0
  610. package/scripts/deploy-staging.sh +23 -0
  611. package/scripts/migrate-planning-model.ts +233 -0
  612. package/scripts/migrate-task-data-assets.mjs +408 -0
  613. package/scripts/playwright-auth.sh +11 -0
  614. package/scripts/run-dev-proxy.sh +2 -0
  615. package/scripts/update-homebrew-formula.sh +2 -2
  616. package/scripts/with-esbuild.sh +11 -0
  617. package/src/resources/templates/WORKFLOW_V2_POLICY.md +1 -1
  618. package/src/resources/templates/workflow-sources/workflowDocs.mjs +62 -52
  619. package/src/resources/templates/workflows/collaborate.yaml +11 -5
  620. package/src/resources/templates/workflows/do.yaml +8 -2
  621. package/src/resources/templates/workflows/evaluate.yaml +13 -7
  622. package/src/resources/templates/workflows/execute.yaml +40 -23
  623. package/src/resources/templates/workflows/plan.yaml +15 -8
  624. package/src/resources/templates/workflows/review.yaml +14 -8
  625. package/dist/TaskforceScreenshot.d.ts +0 -11
  626. package/dist/TaskforceScreenshot.js +0 -113
  627. package/dist/compat/priorityCompat.d.ts +0 -2
  628. package/dist/compat/priorityCompat.js +0 -24
  629. package/dist/components/features/settings/TaxonomyManager 2.d.ts +0 -52
  630. package/dist/components/features/settings/TaxonomyManager 2.js +0 -75
  631. package/dist/components/task/TaskKanban 2.d.ts +0 -76
  632. package/dist/components/task/TaskKanban 2.js +0 -1004
  633. package/dist/components/task/TaskScreenshotSection.d.ts +0 -15
  634. package/dist/components/task/TaskScreenshotSection.js +0 -22
  635. package/dist/components/views/StandaloneLayout 2.d.ts +0 -8
  636. package/dist/components/views/StandaloneLayout 2.js +0 -1660
  637. package/dist/core/InitiativeTemplates.test.js +0 -50
  638. package/dist/core/ParentLifecycle.test.js +0 -98
  639. package/dist/migrations/clientMigrations.d.ts +0 -9
  640. package/dist/migrations/clientMigrations.js +0 -74
  641. package/dist/migrations/flags.d.ts +0 -6
  642. package/dist/migrations/flags.js +0 -8
  643. package/dist/migrations/taskMigrations.d.ts +0 -71
  644. package/dist/migrations/taskMigrations.js +0 -236
  645. package/dist/migrations/taskMigrations.test.js +0 -34
  646. package/dist/server/routes.billing.js +0 -991
  647. package/dist/server/routes.billing.test.js +0 -387
  648. package/dist/server/routes.sync.integration.test.js +0 -470
  649. package/dist/server/routes.sync.js +0 -1141
  650. package/dist/soak/syncSoakHarness.d.ts +0 -6
  651. package/dist/soak/syncSoakHarness.js +0 -10
  652. package/dist/ui/assets/index-0W5vyCpn.css +0 -1
  653. package/dist/ui/assets/index-MCZuNNfn.js +0 -23
  654. package/dist/ui/assets/logo_white_01-0Vx-iIIv.png +0 -0
  655. package/dist/ui/assets/vendor-dnd-BI1K77C1.js +0 -5
  656. package/dist/ui/assets/vendor-react-DdIDsh7f.js +0 -9
  657. package/dist/ui/assets/vendor-router-BtiNU7Dl.js +0 -3
  658. package/dist/utils/taskHierarchy.d.ts +0 -4
  659. package/dist/utils/taskHierarchy.js +0 -69
  660. package/dist/utils/taskHierarchy.test.js +0 -38
  661. package/dist/utils/taskParenting.d.ts +0 -3
  662. package/dist/utils/taskParenting.js +0 -24
  663. package/dist/utils/taskParenting.test.js +0 -21
  664. package/dist/utils/taskRelations.d.ts +0 -6
  665. package/dist/utils/taskRelations.js +0 -24
  666. package/dist/utils/taskRelations.test.js +0 -34
  667. package/scripts/check-compat-boundaries.js +0 -48
  668. /package/dist/{__tests__/planParser.test.d.ts → TaskforceCore.filter-persistence.test.d.ts} +0 -0
  669. /package/dist/{__tests__/releaseVersion.test.d.ts → TaskforceCore.routing.test.d.ts} +0 -0
  670. /package/dist/{__tests__/structuredCommentValidation.test.d.ts → annotatedAttachments/AnnotatedAttachmentSessionStore.test.d.ts} +0 -0
  671. /package/dist/{__tests__/structuredDocValidation.test.d.ts → annotatedAttachments/payload.test.d.ts} +0 -0
  672. /package/dist/{__tests__/templateEngine.test.d.ts → annotatedAttachments/types.js} +0 -0
  673. /package/dist/{__tests__/templates.test.d.ts → compat/workspaceSyncCompat.test.d.ts} +0 -0
  674. /package/dist/{__tests__/workflowDocumentFixtures.test.d.ts → components/features/AgentsModule.test.d.ts} +0 -0
  675. /package/dist/{__tests__/workflowExportMatrix.test.d.ts → components/features/AnnotatedAttachmentWorkspace.test.d.ts} +0 -0
  676. /package/dist/{__tests__/workflowExportSnapshots.test.d.ts → components/features/DocumentWorkspace.test.d.ts} +0 -0
  677. /package/dist/{__tests__/workflowGeneration.test.d.ts → components/features/settings/TaxonomyLibraryManager.test.d.ts} +0 -0
  678. /package/dist/{__tests__/workflowV2Policy.test.d.ts → components/task/TaskActionHeader.test.d.ts} +0 -0
  679. /package/dist/{core/InitiativeTemplates.test.d.ts → components/task/TaskKanban.planning-drop.test.d.ts} +0 -0
  680. /package/dist/{core/ParentLifecycle.test.d.ts → components/task/TaskStatusActions.test.d.ts} +0 -0
  681. /package/dist/{migrations/taskMigrations.test.d.ts → components/ui/TopNoticeLayer.test.d.ts} +0 -0
  682. /package/dist/{server/routes.billing.test.d.ts → components/views/AppShellHeader.test.d.ts} +0 -0
  683. /package/dist/{server/routes.sync.integration.test.d.ts → components/views/PlanComparisonPage.test.d.ts} +0 -0
  684. /package/dist/{utils/taskHierarchy.test.d.ts → components/views/chrome/WorkspaceChromeSections.test.d.ts} +0 -0
  685. /package/dist/{utils/taskParenting.test.d.ts → components/views/chrome/workspaceChrome.test.d.ts} +0 -0
  686. /package/dist/{utils/taskRelations.test.d.ts → components/views/panels/DocumentFilterToolbar.test.d.ts} +0 -0
  687. /package/dist/public/{android-chrome-192x192.png → archive/phoenix/android-chrome-192x192.png} +0 -0
  688. /package/dist/public/{android-chrome-512x512.png → archive/phoenix/android-chrome-512x512.png} +0 -0
  689. /package/dist/public/{favicon-16x16.png → archive/phoenix/favicon-16x16.png} +0 -0
  690. /package/dist/public/{favicon-32x32.png → archive/phoenix/favicon-32x32.png} +0 -0
  691. /package/dist/public/{favicon.png → archive/phoenix/favicon.png} +0 -0
  692. /package/dist/{__tests__ → services}/workflowExportSnapshots.test.js +0 -0
  693. /package/dist/{__tests__ → services}/workflowGeneration.test.js +0 -0
  694. /package/dist/ui/favicon/{android-chrome-192x192.png → archive/phoenix/android-chrome-192x192.png} +0 -0
  695. /package/dist/ui/favicon/{android-chrome-512x512.png → archive/phoenix/android-chrome-512x512.png} +0 -0
  696. /package/dist/ui/favicon/{favicon-16x16.png → archive/phoenix/favicon-16x16.png} +0 -0
  697. /package/dist/ui/favicon/{favicon-32x32.png → archive/phoenix/favicon-32x32.png} +0 -0
  698. /package/dist/ui/favicon/{favicon.png → archive/phoenix/favicon.png} +0 -0
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { render, screen } from '@testing-library/react';
2
+ import { fireEvent, render, screen } 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';
6
+ import { TASKFORCE_THEME_OPTIONS } from '../../utils/theme';
6
7
  describe('TaskSettings Component', () => {
7
8
  // Default mock props
8
9
  const defaultSettingsModel = {
@@ -13,13 +14,17 @@ describe('TaskSettings Component', () => {
13
14
  jsonBackupEnabled: false,
14
15
  globalJsonBackupEnabled: false,
15
16
  jsonBackupUseGlobalDefault: false,
17
+ showTaskCardStatusLabel: true,
16
18
  exportWorkflowsPath: '.agent/workflows',
17
- exportSpecialistsPath: '.agent/specialists',
18
19
  exportingResource: null,
19
20
  exportResult: null,
20
21
  pathSaved: false,
21
22
  initialSection: null,
22
23
  exportEnvironment: 'antigravity',
24
+ fetchCloudAuthApi: vi.fn(async () => ({
25
+ ok: true,
26
+ json: async () => ({ items: [] })
27
+ })),
23
28
  // Mock Callbacks
24
29
  onThemeChange: vi.fn(),
25
30
  onSaveTheme: vi.fn(),
@@ -28,11 +33,12 @@ describe('TaskSettings Component', () => {
28
33
  onKeyShortcutChange: vi.fn(),
29
34
  onJsonBackupEnabledChange: vi.fn(),
30
35
  onSaveGlobalJsonBackupEnabled: vi.fn(),
36
+ onSaveGlobalWeekStartsOn: vi.fn(),
37
+ onChecklistDropdownEnabledChange: vi.fn(),
38
+ onShowTaskCardStatusLabelChange: vi.fn(async () => true),
31
39
  onSaveSettings: vi.fn(),
32
40
  onExportWorkflowsPathChange: vi.fn(),
33
- onExportSpecialistsPathChange: vi.fn(),
34
41
  onExportEnvironmentChange: vi.fn(),
35
- onExportResources: vi.fn(),
36
42
  onShowFolderBrowserChange: vi.fn(),
37
43
  onBrowserTargetChange: vi.fn(),
38
44
  onFetchFolders: vi.fn(),
@@ -53,11 +59,12 @@ describe('TaskSettings Component', () => {
53
59
  onUpdateCategoryColor: vi.fn(),
54
60
  // Type Manager Props
55
61
  types: [
56
- { value: 'feature', label: 'Feature' },
57
- { value: 'bug', label: 'Bug' }
62
+ { value: 'task', label: 'Task' },
63
+ { value: 'issue', label: 'Issue' }
58
64
  ],
59
65
  onSaveType: vi.fn(),
60
66
  onRemoveType: vi.fn(),
67
+ onUpdateType: vi.fn(),
61
68
  // Taxonomy Props
62
69
  taxonomies: [],
63
70
  onUpdateTaxonomies: vi.fn(),
@@ -65,9 +72,14 @@ describe('TaskSettings Component', () => {
65
72
  { value: 1, label: 'Low', color: 'teal-500', icon: 'ArrowDown' },
66
73
  { value: 2, label: 'Medium', color: 'blue-500', icon: 'Minus' },
67
74
  { value: 3, label: 'High', color: 'amber-500', icon: 'ArrowUp' },
68
- { value: 4, label: 'Critical', color: 'red-500', icon: 'AlertTriangle' },
69
75
  ],
70
76
  onUpdatePriorities: vi.fn(),
77
+ onAnalyzeSystemTaxonomyPack: vi.fn(() => ({
78
+ unmatchedCategoryValues: [],
79
+ unmatchedTypeValues: [],
80
+ incompatiblePriorityValues: []
81
+ })),
82
+ onApplySystemTaxonomyPack: vi.fn(async () => ({ success: true })),
71
83
  approaches: [
72
84
  { value: 'evaluate', label: 'Evaluate', color: 'amber-500', icon: 'Search' },
73
85
  { value: 'collaborate', label: 'Collaborate', color: 'blue-500', icon: 'Users' },
@@ -95,32 +107,89 @@ describe('TaskSettings Component', () => {
95
107
  };
96
108
  return render(_jsx(TaskSettings, { ...props }));
97
109
  };
110
+ const originalFetch = global.fetch;
98
111
  it('4.1 Open settings - Renders correctly', () => {
99
112
  renderComponent();
100
- expect(screen.getByText('Project Settings')).toBeInTheDocument();
101
- expect(screen.getByText('Taxonomies')).toBeInTheDocument();
113
+ expect(screen.getByText('Sections')).toBeInTheDocument();
114
+ expect(screen.getByRole('button', { name: /general/i })).toBeInTheDocument();
115
+ });
116
+ it('4.1a Sidebar renders all primary settings sections', () => {
117
+ renderComponent();
118
+ expect(screen.getByRole('button', { name: /general/i })).toBeInTheDocument();
119
+ expect(screen.getByRole('button', { name: /appearance/i })).toBeInTheDocument();
120
+ expect(screen.getByRole('button', { name: /application/i })).toBeInTheDocument();
121
+ expect(screen.getByRole('button', { name: /task cards/i })).toBeInTheDocument();
122
+ expect(screen.getByRole('button', { name: /task forms/i })).toBeInTheDocument();
123
+ expect(screen.queryByRole('button', { name: /documents/i })).not.toBeInTheDocument();
124
+ expect(screen.queryByRole('button', { name: /commands/i })).not.toBeInTheDocument();
125
+ expect(screen.queryByRole('button', { name: /initiatives/i })).not.toBeInTheDocument();
126
+ expect(screen.queryByRole('button', { name: /workflows/i })).not.toBeInTheDocument();
127
+ expect(screen.queryByRole('button', { name: /agents/i })).not.toBeInTheDocument();
128
+ expect(screen.getByRole('button', { name: /taxonomy/i })).toBeInTheDocument();
129
+ expect(screen.getByRole('button', { name: /^mcp$/i })).toBeInTheDocument();
130
+ expect(screen.getByRole('button', { name: /integrations/i })).toBeInTheDocument();
131
+ });
132
+ it('4.1b General tab removes old profile/account chrome', () => {
133
+ renderComponent();
134
+ expect(screen.queryByText('Workspace Profile')).not.toBeInTheDocument();
135
+ expect(screen.queryByText('Account Plan')).not.toBeInTheDocument();
136
+ expect(screen.queryByText('Project Settings')).not.toBeInTheDocument();
137
+ expect(screen.getByRole('button', { name: 'Save' })).toBeInTheDocument();
138
+ expect(screen.getByRole('button', { name: 'Reset to Global' })).toBeInTheDocument();
102
139
  });
103
- it('4.2 Version shown', () => {
140
+ it('4.1c Danger zone is hidden in local mode', () => {
141
+ renderComponent({
142
+ settingsModel: {
143
+ runtimeMode: 'local',
144
+ workspaceSwitchingEnabled: true,
145
+ isAuthenticated: true,
146
+ currentWorkspaceRole: 'owner'
147
+ }
148
+ });
149
+ expect(screen.queryByText('Danger Zone')).not.toBeInTheDocument();
150
+ });
151
+ it('4.1d Global tab no longer shows operating mode', async () => {
152
+ const user = userEvent.setup();
104
153
  renderComponent();
105
- expect(screen.getByText(/v\d+\.\d+\.\d+/)).toBeInTheDocument();
154
+ await user.click(screen.getByRole('button', { name: 'Global' }));
155
+ expect(screen.queryByText('Operating Mode')).not.toBeInTheDocument();
106
156
  });
107
157
  it('4.3 Theme toggle', async () => {
108
158
  const user = userEvent.setup();
109
159
  renderComponent();
160
+ await user.click(screen.getByRole('button', { name: /application/i }));
110
161
  const lightBtn = screen.getByRole('button', { name: /^light$/i });
111
162
  await user.click(lightBtn);
112
163
  expect(defaultSettingsModel.onThemeChange).toHaveBeenCalledWith('light');
113
164
  expect(defaultSettingsModel.onSaveTheme).toHaveBeenCalledWith('light');
114
165
  });
166
+ it('4.3a Renders all registered themes in the picker', async () => {
167
+ const user = userEvent.setup();
168
+ renderComponent();
169
+ await user.click(screen.getByRole('button', { name: /application/i }));
170
+ for (const themeOption of TASKFORCE_THEME_OPTIONS) {
171
+ expect(screen.getByRole('button', { name: new RegExp(`^${themeOption.label}$`, 'i') })).toBeInTheDocument();
172
+ }
173
+ });
115
174
  it('4.4 Theme persists', async () => {
116
175
  const user = userEvent.setup();
117
176
  // This tests that the callback is invoked, persistence is handled by parent
118
177
  renderComponent();
178
+ await user.click(screen.getByRole('button', { name: /application/i }));
119
179
  const midnightBtn = screen.getByRole('button', { name: /^midnight$/i });
120
180
  await user.click(midnightBtn);
121
181
  expect(defaultSettingsModel.onThemeChange).toHaveBeenCalledWith('midnight');
122
182
  expect(defaultSettingsModel.onSaveTheme).toHaveBeenCalledWith('midnight');
123
183
  });
184
+ it('4.4a Dawn theme can be selected', async () => {
185
+ const user = userEvent.setup();
186
+ renderComponent();
187
+ await user.click(screen.getByRole('button', { name: /application/i }));
188
+ const dawnBtn = screen.getByRole('button', { name: /^dawn$/i });
189
+ await user.click(dawnBtn);
190
+ expect(defaultSettingsModel.onThemeChange).toHaveBeenCalledWith('dawn');
191
+ expect(defaultSettingsModel.onSaveTheme).toHaveBeenCalledWith('dawn');
192
+ });
124
193
  it('4.4b JSON backup toggle updates setting', async () => {
125
194
  const user = userEvent.setup();
126
195
  renderComponent();
@@ -128,6 +197,37 @@ describe('TaskSettings Component', () => {
128
197
  await user.click(backupToggle);
129
198
  expect(defaultSettingsModel.onJsonBackupEnabledChange).toHaveBeenCalledWith(true);
130
199
  });
200
+ it('4.4c JSON backup controls are hidden in cloud mode', async () => {
201
+ const user = userEvent.setup();
202
+ renderComponent({
203
+ settingsModel: {
204
+ runtimeMode: 'cloud'
205
+ }
206
+ });
207
+ expect(screen.queryByRole('checkbox', { name: /enable automatic json backup/i })).not.toBeInTheDocument();
208
+ await user.click(screen.getByRole('button', { name: 'Global' }));
209
+ expect(screen.queryByRole('checkbox', { name: /enable global json backup default/i })).not.toBeInTheDocument();
210
+ });
211
+ it('4.4d Task forms section owns checklist dropdown setting', async () => {
212
+ const user = userEvent.setup();
213
+ renderComponent();
214
+ await user.click(screen.getByRole('button', { name: /task forms/i }));
215
+ const checklistToggle = screen.getByRole('checkbox', { name: /enable checklist dropdown in task form/i });
216
+ await user.click(checklistToggle);
217
+ expect(defaultSettingsModel.onChecklistDropdownEnabledChange).toHaveBeenCalledWith(false);
218
+ });
219
+ it('4.4e Task cards section owns the status label setting', async () => {
220
+ const user = userEvent.setup();
221
+ renderComponent();
222
+ await user.click(screen.getByRole('button', { name: /task cards/i }));
223
+ const statusLabelToggle = screen.getByRole('checkbox', { name: /show status label/i });
224
+ await user.click(statusLabelToggle);
225
+ expect(defaultSettingsModel.onShowTaskCardStatusLabelChange).toHaveBeenCalledWith(false);
226
+ });
227
+ it('4.4f Initial appearance section opens directly', () => {
228
+ renderComponent({ initialSection: 'appearance-task-forms' });
229
+ expect(screen.getByRole('checkbox', { name: /enable checklist dropdown in task form/i })).toBeInTheDocument();
230
+ });
131
231
  // 4.5 Open category manager - It's always visible in the new design
132
232
  it('4.5 Categories section is visible', () => {
133
233
  renderComponent({ initialSection: 'categories' });
@@ -165,7 +265,7 @@ describe('TaskSettings Component', () => {
165
265
  await user.tab(); // Trigger blur
166
266
  expect(defaultSettingsModel.onUpdateCategory).toHaveBeenCalledWith(expect.objectContaining({ label: 'Backend API' }), 'Backend');
167
267
  });
168
- it('4.8 Delete category', async () => {
268
+ it('4.8 Retire category', async () => {
169
269
  // Mock window.confirm
170
270
  vi.spyOn(window, 'confirm').mockImplementation(() => true);
171
271
  const user = userEvent.setup();
@@ -175,11 +275,28 @@ describe('TaskSettings Component', () => {
175
275
  await user.click(dropdown);
176
276
  const option = screen.getByText('Backend');
177
277
  await user.click(option);
178
- // Click delete
179
- const deleteBtn = screen.getByTitle('Delete Category');
180
- await user.click(deleteBtn);
278
+ // Click retire
279
+ const retireBtn = screen.getByTitle('Retire Category');
280
+ await user.click(retireBtn);
181
281
  expect(defaultSettingsModel.onRemoveCategory).toHaveBeenCalledWith('backend');
182
282
  });
283
+ it('4.8a Protected fallback category cannot be retired', async () => {
284
+ const user = userEvent.setup();
285
+ renderComponent({
286
+ initialSection: 'categories',
287
+ settingsModel: {
288
+ categories: [
289
+ { value: 'default', label: 'General', disabled: false },
290
+ { value: 'backend', label: 'Backend', disabled: false }
291
+ ]
292
+ }
293
+ });
294
+ const dropdown = screen.getByText('Select a category to configure...').closest('button');
295
+ await user.click(dropdown);
296
+ await user.click(screen.getAllByText('General').at(-1));
297
+ expect(screen.queryByTitle('Retire Category')).not.toBeInTheDocument();
298
+ expect(screen.getByText('General is the protected fallback category and cannot be retired.')).toBeInTheDocument();
299
+ });
183
300
  it('4.9 Category icon', async () => {
184
301
  const user = userEvent.setup();
185
302
  renderComponent({ initialSection: 'categories' });
@@ -244,27 +361,459 @@ describe('TaskSettings Component', () => {
244
361
  await user.click(createBtn);
245
362
  expect(defaultSettingsModel.onSaveType).toHaveBeenCalledWith('Refactor');
246
363
  });
247
- it('4.14 Delete type', async () => {
364
+ it('4.14 Retire type', async () => {
248
365
  vi.spyOn(window, 'confirm').mockImplementation(() => true);
249
366
  const user = userEvent.setup();
250
367
  renderComponent({ initialSection: 'types' });
251
368
  const dropdown = screen.getByText('Select a type to configure...').closest('button');
252
369
  await user.click(dropdown);
253
- const option = screen.getByText('Feature');
370
+ const option = screen.getAllByText('Issue').at(-1);
254
371
  await user.click(option);
255
- const deleteBtn = screen.getByTitle('Delete Type');
256
- await user.click(deleteBtn);
257
- expect(defaultSettingsModel.onRemoveType).toHaveBeenCalledWith('feature');
372
+ const retireBtn = screen.getByTitle('Retire Type');
373
+ await user.click(retireBtn);
374
+ expect(defaultSettingsModel.onRemoveType).toHaveBeenCalledWith('issue');
258
375
  });
259
- it('4.15 Export workflows', async () => {
376
+ it('4.14ab Retired types can be restored', async () => {
377
+ const user = userEvent.setup();
378
+ renderComponent({
379
+ initialSection: 'types',
380
+ settingsModel: {
381
+ types: [
382
+ { value: 'task', label: 'Task', color: 'blue-500', icon: 'CheckSquare', status: 'active' },
383
+ { value: 'issue', label: 'Issue', color: 'red-500', icon: 'AlertCircle', status: 'retired' }
384
+ ]
385
+ }
386
+ });
387
+ const dropdown = screen.getByText('Select a type to configure...').closest('button');
388
+ await user.click(dropdown);
389
+ await user.click(screen.getByText('Issue (Retired)'));
390
+ const restoreBtn = screen.getByTitle('Restore Type');
391
+ await user.click(restoreBtn);
392
+ expect(defaultSettingsModel.onUpdateType).toHaveBeenCalledWith('issue', { status: 'active' });
393
+ });
394
+ it('4.14aa Protected fallback type cannot be retired', async () => {
395
+ const user = userEvent.setup();
396
+ renderComponent({
397
+ initialSection: 'types',
398
+ settingsModel: {
399
+ types: [
400
+ { value: 'task', label: 'Task', color: 'blue-500', icon: 'CheckSquare' },
401
+ { value: 'review', label: 'Review', color: 'violet-500', icon: 'Search' }
402
+ ]
403
+ }
404
+ });
405
+ const dropdown = screen.getByText('Select a type to configure...').closest('button');
406
+ await user.click(dropdown);
407
+ await user.click(screen.getByText('Task'));
408
+ expect(screen.queryByTitle('Retire Type')).not.toBeInTheDocument();
409
+ expect(screen.getByText('Task is the protected fallback type and cannot be retired.')).toBeInTheDocument();
410
+ });
411
+ it('4.14a Rename type', async () => {
412
+ const user = userEvent.setup();
413
+ renderComponent({ initialSection: 'types' });
414
+ const dropdown = screen.getByText('Select a type to configure...').closest('button');
415
+ await user.click(dropdown);
416
+ const option = screen.getByText('Task');
417
+ await user.click(option);
418
+ const labelInput = screen.getByDisplayValue('Task');
419
+ await user.clear(labelInput);
420
+ await user.type(labelInput, 'Action Item');
421
+ await user.tab();
422
+ expect(defaultSettingsModel.onUpdateType).toHaveBeenCalledWith('task', { label: 'Action Item' });
423
+ });
424
+ it('4.14b Update type color', async () => {
260
425
  const user = userEvent.setup();
426
+ renderComponent({ initialSection: 'types' });
427
+ const dropdown = screen.getByText('Select a type to configure...').closest('button');
428
+ await user.click(dropdown);
429
+ await user.click(screen.getByText('Task'));
430
+ await user.click(screen.getByTitle('green-500'));
431
+ expect(defaultSettingsModel.onUpdateType).toHaveBeenCalledWith('task', { color: 'green-500' });
432
+ });
433
+ it('4.15 Export workflows', async () => {
261
434
  renderComponent({ initialSection: 'commands' });
262
- expect(screen.getByText('Commands')).toBeInTheDocument();
435
+ expect(screen.getByText('Command Workflows')).toBeInTheDocument();
436
+ });
437
+ it('4.14c Priority editor hides generic taxonomy controls', async () => {
438
+ const user = userEvent.setup();
439
+ renderComponent();
440
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
441
+ const priorityRow = screen.getByText('Priority Levels');
442
+ await user.click(priorityRow.closest('button'));
443
+ const dropdown = screen.getByText('Select a priority level to configure...').closest('button');
444
+ await user.click(dropdown);
445
+ await user.click(screen.getByText('Medium'));
446
+ expect(screen.queryByText('Mark as Required')).not.toBeInTheDocument();
447
+ expect(screen.queryByText('Allow Multi-select')).not.toBeInTheDocument();
448
+ expect(screen.queryByText('Widget Type')).not.toBeInTheDocument();
449
+ expect(screen.getByText(/fixed ordered scale/i)).toBeInTheDocument();
450
+ });
451
+ it('4.14c1 Taxonomy libraries are visible from the taxonomy settings list', async () => {
452
+ const user = userEvent.setup();
453
+ renderComponent();
454
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
455
+ expect(screen.getByRole('button', { name: /system taxonomy library/i })).toBeInTheDocument();
456
+ expect(screen.getByText(/browse curated packs for categories, task types, and priority scales/i)).toBeInTheDocument();
457
+ expect(screen.getByRole('button', { name: /custom taxonomy library/i })).toBeInTheDocument();
458
+ });
459
+ it('4.14c2 System Taxonomy Library opens preview and apply controls', async () => {
460
+ const user = userEvent.setup();
461
+ renderComponent();
462
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
463
+ await user.click(screen.getByRole('button', { name: /system taxonomy library/i }));
464
+ expect(screen.getByText('Browse starter packs before you replace anything.')).toBeInTheDocument();
465
+ expect(screen.getByText('Software Development')).toBeInTheDocument();
466
+ expect(screen.getByText('Apply Selected Sections')).toBeInTheDocument();
467
+ expect(screen.getByRole('checkbox', { name: 'Categories' })).toBeInTheDocument();
468
+ expect(screen.getByRole('checkbox', { name: 'Types' })).toBeInTheDocument();
469
+ expect(screen.getByRole('checkbox', { name: 'Priorities' })).toBeInTheDocument();
470
+ expect(screen.getByRole('checkbox', { name: /remap unmatched existing values to default/i })).toBeInTheDocument();
471
+ });
472
+ it('4.14c2b System Taxonomy Library applies a selected pack', async () => {
473
+ const user = userEvent.setup();
474
+ const onApplySystemTaxonomyPack = vi.fn(async () => ({ success: true }));
475
+ renderComponent({
476
+ settingsModel: {
477
+ onApplySystemTaxonomyPack
478
+ }
479
+ });
480
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
481
+ await user.click(screen.getByRole('button', { name: /system taxonomy library/i }));
482
+ await user.click(screen.getByRole('button', { name: 'Apply Pack' }));
483
+ expect(onApplySystemTaxonomyPack).toHaveBeenCalledWith(expect.objectContaining({
484
+ pack: expect.objectContaining({ id: 'general' }),
485
+ sections: {
486
+ categories: true,
487
+ types: true,
488
+ priorities: true
489
+ },
490
+ remapExistingValuesToDefault: false
491
+ }));
492
+ expect(await screen.findByText(/was applied to the selected taxonomy sections/i)).toBeInTheDocument();
493
+ });
494
+ it('4.14c2c System Taxonomy Library surfaces preflight warnings for unmatched values', async () => {
495
+ const user = userEvent.setup();
496
+ renderComponent({
497
+ settingsModel: {
498
+ onAnalyzeSystemTaxonomyPack: vi.fn(() => ({
499
+ unmatchedCategoryValues: ['marketing'],
500
+ unmatchedTypeValues: ['request', 'incident'],
501
+ incompatiblePriorityValues: [4]
502
+ }))
503
+ }
504
+ });
505
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
506
+ await user.click(screen.getByRole('button', { name: /system taxonomy library/i }));
507
+ expect(screen.getByText(/1 category value will be preserved as legacy/i)).toBeInTheDocument();
508
+ expect(screen.getByText(/2 type values will be preserved as legacy/i)).toBeInTheDocument();
509
+ expect(screen.getByText(/priority replacement will be blocked unless you remap/i)).toBeInTheDocument();
263
510
  });
264
- it('4.16 Export specialists', async () => {
511
+ it('4.14c3 Custom Taxonomy Library opens the custom pack browser', async () => {
265
512
  const user = userEvent.setup();
513
+ renderComponent();
514
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
515
+ await user.click(screen.getByRole('button', { name: /custom taxonomy library/i }));
516
+ expect(screen.getByText('Browse reusable custom taxonomy packs.')).toBeInTheDocument();
517
+ expect(screen.getByText('Choose Taxonomies to Install')).toBeInTheDocument();
518
+ expect(screen.getByRole('button', { name: /install selected taxonomies/i })).toBeInTheDocument();
519
+ });
520
+ it('4.14d Retires custom taxonomy options instead of deleting them', async () => {
521
+ const user = userEvent.setup();
522
+ renderComponent({
523
+ settingsModel: {
524
+ taxonomies: [{
525
+ id: 'review-status',
526
+ label: 'Review Status',
527
+ widgetType: 'select',
528
+ isSystem: false,
529
+ multiSelect: false,
530
+ options: [
531
+ { value: 'pending', label: 'Pending', status: 'active' },
532
+ { value: 'legal-review', label: 'Legal Review', status: 'active' }
533
+ ]
534
+ }]
535
+ }
536
+ });
537
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
538
+ await user.click(screen.getByText('Review Status').closest('button'));
539
+ const dropdown = screen.getByText('Select an option to configure...').closest('button');
540
+ await user.click(dropdown);
541
+ await user.click(screen.getByText('Legal Review'));
542
+ await user.click(screen.getByTitle('Retire Option'));
543
+ expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenCalledWith([
544
+ expect.objectContaining({
545
+ id: 'review-status',
546
+ options: [
547
+ expect.objectContaining({ value: 'pending', status: 'active' }),
548
+ expect.objectContaining({ value: 'legal-review', status: 'retired' })
549
+ ]
550
+ })
551
+ ]);
552
+ });
553
+ it('4.14e Toggles custom taxonomy filter visibility', async () => {
554
+ const user = userEvent.setup();
555
+ renderComponent({
556
+ settingsModel: {
557
+ taxonomies: [{
558
+ id: 'review-status',
559
+ label: 'Review Status',
560
+ widgetType: 'select',
561
+ isSystem: false,
562
+ multiSelect: false,
563
+ filterEnabled: true,
564
+ options: [
565
+ { value: 'pending', label: 'Pending', status: 'active' }
566
+ ]
567
+ }]
568
+ }
569
+ });
570
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
571
+ await user.click(screen.getByText('Review Status').closest('button'));
572
+ const filterToggle = screen.getByRole('checkbox', { name: 'Show in Filters' });
573
+ await user.click(filterToggle);
574
+ expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenCalledWith([
575
+ expect.objectContaining({
576
+ id: 'review-status',
577
+ filterEnabled: false
578
+ })
579
+ ]);
580
+ });
581
+ it('4.14e1 Toggles custom taxonomy task-form visibility', async () => {
582
+ const user = userEvent.setup();
583
+ renderComponent({
584
+ settingsModel: {
585
+ taxonomies: [{
586
+ id: 'review-status',
587
+ label: 'Review Status',
588
+ widgetType: 'select',
589
+ isSystem: false,
590
+ multiSelect: false,
591
+ formEnabled: true,
592
+ options: [
593
+ { value: 'pending', label: 'Pending', status: 'active' }
594
+ ]
595
+ }]
596
+ }
597
+ });
598
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
599
+ await user.click(screen.getByText('Review Status').closest('button'));
600
+ const formToggle = screen.getByRole('checkbox', { name: 'Show in Task Form' });
601
+ await user.click(formToggle);
602
+ expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenCalledWith([
603
+ expect.objectContaining({
604
+ id: 'review-status',
605
+ formEnabled: false
606
+ })
607
+ ]);
608
+ });
609
+ it('4.14f Updates custom taxonomy description', async () => {
610
+ const user = userEvent.setup();
611
+ renderComponent({
612
+ settingsModel: {
613
+ taxonomies: [{
614
+ id: 'review-status',
615
+ label: 'Review Status',
616
+ widgetType: 'select',
617
+ isSystem: false,
618
+ multiSelect: false,
619
+ options: [
620
+ { value: 'pending', label: 'Pending', status: 'active' }
621
+ ]
622
+ }]
623
+ }
624
+ });
625
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
626
+ await user.click(screen.getByText('Review Status').closest('button'));
627
+ const descriptionInput = screen.getByPlaceholderText('Optional guidance for when and how to use this taxonomy...');
628
+ fireEvent.change(descriptionInput, { target: { value: 'Use this to track review routing.' } });
629
+ expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
630
+ expect.objectContaining({
631
+ id: 'review-status',
632
+ description: 'Use this to track review routing.'
633
+ })
634
+ ]);
635
+ });
636
+ it('4.14g Updates custom taxonomy aliases', async () => {
637
+ const user = userEvent.setup();
638
+ renderComponent({
639
+ settingsModel: {
640
+ taxonomies: [{
641
+ id: 'review-status',
642
+ label: 'Review Status',
643
+ widgetType: 'select',
644
+ isSystem: false,
645
+ multiSelect: false,
646
+ options: [
647
+ { value: 'pending', label: 'Pending', status: 'active' }
648
+ ]
649
+ }]
650
+ }
651
+ });
652
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
653
+ await user.click(screen.getByText('Review Status').closest('button'));
654
+ const aliasInputs = screen.getAllByPlaceholderText('Optional aliases, comma-separated');
655
+ fireEvent.change(aliasInputs[0], { target: { value: 'review route, routing' } });
656
+ fireEvent.blur(aliasInputs[0]);
657
+ expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
658
+ expect.objectContaining({
659
+ id: 'review-status',
660
+ aliases: ['review route', 'routing']
661
+ })
662
+ ]);
663
+ });
664
+ it('4.14h Updates custom taxonomy option aliases', async () => {
665
+ const user = userEvent.setup();
666
+ renderComponent({
667
+ settingsModel: {
668
+ taxonomies: [{
669
+ id: 'review-status',
670
+ label: 'Review Status',
671
+ widgetType: 'select',
672
+ isSystem: false,
673
+ multiSelect: false,
674
+ options: [
675
+ { value: 'pending', label: 'Pending', status: 'active' }
676
+ ]
677
+ }]
678
+ }
679
+ });
680
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
681
+ await user.click(screen.getByText('Review Status').closest('button'));
682
+ const dropdown = screen.getByText('Select an option to configure...').closest('button');
683
+ await user.click(dropdown);
684
+ await user.click(screen.getByText('Pending'));
685
+ const aliasInputs = screen.getAllByPlaceholderText('Optional aliases, comma-separated');
686
+ fireEvent.change(aliasInputs[1], { target: { value: 'queued, awaiting review' } });
687
+ fireEvent.blur(aliasInputs[1]);
688
+ expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
689
+ expect.objectContaining({
690
+ id: 'review-status',
691
+ options: [
692
+ expect.objectContaining({
693
+ value: 'pending',
694
+ aliases: ['queued', 'awaiting review']
695
+ })
696
+ ]
697
+ })
698
+ ]);
699
+ });
700
+ it('4.14i Retires whole custom taxonomies instead of deleting them', async () => {
701
+ vi.spyOn(window, 'confirm').mockImplementation(() => true);
702
+ const user = userEvent.setup();
703
+ renderComponent({
704
+ settingsModel: {
705
+ taxonomies: [{
706
+ id: 'review-status',
707
+ label: 'Review Status',
708
+ widgetType: 'select',
709
+ isSystem: false,
710
+ multiSelect: false,
711
+ options: [{ value: 'pending', label: 'Pending', status: 'active' }]
712
+ }]
713
+ }
714
+ });
715
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
716
+ await user.click(screen.getByText('Review Status').closest('button'));
717
+ await user.click(screen.getByRole('button', { name: /retire taxonomy/i }));
718
+ expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
719
+ expect.objectContaining({
720
+ id: 'review-status',
721
+ status: 'retired'
722
+ })
723
+ ]);
724
+ });
725
+ it('4.14j Restores retired custom taxonomies', async () => {
726
+ const user = userEvent.setup();
727
+ renderComponent({
728
+ settingsModel: {
729
+ taxonomies: [{
730
+ id: 'review-status',
731
+ label: 'Review Status',
732
+ status: 'retired',
733
+ widgetType: 'select',
734
+ isSystem: false,
735
+ multiSelect: false,
736
+ options: [{ value: 'pending', label: 'Pending', status: 'active' }]
737
+ }]
738
+ }
739
+ });
740
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
741
+ await user.click(screen.getByText(/Review Status \(Retired\)/).closest('button'));
742
+ await user.click(screen.getByRole('button', { name: /restore taxonomy/i }));
743
+ expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
744
+ expect.objectContaining({
745
+ id: 'review-status',
746
+ status: 'active'
747
+ })
748
+ ]);
749
+ });
750
+ it('4.14k Reorders custom taxonomies from the list view', async () => {
751
+ const user = userEvent.setup();
752
+ renderComponent({
753
+ settingsModel: {
754
+ taxonomies: [
755
+ {
756
+ id: 'alpha',
757
+ label: 'Alpha',
758
+ widgetType: 'select',
759
+ isSystem: false,
760
+ multiSelect: false,
761
+ options: []
762
+ },
763
+ {
764
+ id: 'beta',
765
+ label: 'Beta',
766
+ widgetType: 'select',
767
+ isSystem: false,
768
+ multiSelect: false,
769
+ options: []
770
+ }
771
+ ]
772
+ }
773
+ });
774
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
775
+ await user.click(screen.getAllByTitle('Move taxonomy down').find((button) => !button.hasAttribute('disabled')));
776
+ expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
777
+ expect.objectContaining({ id: 'beta' }),
778
+ expect.objectContaining({ id: 'alpha' })
779
+ ]);
780
+ });
781
+ it('4.14l Reorders custom taxonomy options from the detail view', async () => {
782
+ const user = userEvent.setup();
783
+ renderComponent({
784
+ settingsModel: {
785
+ taxonomies: [{
786
+ id: 'review-status',
787
+ label: 'Review Status',
788
+ widgetType: 'select',
789
+ isSystem: false,
790
+ multiSelect: false,
791
+ options: [
792
+ { value: 'pending', label: 'Pending', status: 'active' },
793
+ { value: 'approved', label: 'Approved', status: 'active' }
794
+ ]
795
+ }]
796
+ }
797
+ });
798
+ await user.click(screen.getByRole('button', { name: /taxonomy/i }));
799
+ await user.click(screen.getByText('Review Status').closest('button'));
800
+ const dropdown = screen.getByText('Select an option to configure...').closest('button');
801
+ await user.click(dropdown);
802
+ await user.click(screen.getByText('Approved'));
803
+ await user.click(screen.getByTitle('Move option up'));
804
+ expect(defaultSettingsModel.onUpdateTaxonomies).toHaveBeenLastCalledWith([
805
+ expect.objectContaining({
806
+ id: 'review-status',
807
+ options: [
808
+ expect.objectContaining({ value: 'approved' }),
809
+ expect.objectContaining({ value: 'pending' })
810
+ ]
811
+ })
812
+ ]);
813
+ });
814
+ it('4.16 Agents section shows registered AI profiles', async () => {
266
815
  renderComponent({ initialSection: 'agents' });
267
- expect(screen.getByText('Agents')).toBeInTheDocument();
816
+ expect(screen.getByText('Registered AI Profiles')).toBeInTheDocument();
268
817
  });
269
818
  it('4.17 initialSection navigates to detail view', () => {
270
819
  renderComponent({ initialSection: 'categories' });
@@ -272,37 +821,662 @@ describe('TaskSettings Component', () => {
272
821
  expect(screen.getByText('Categories')).toBeInTheDocument();
273
822
  expect(screen.queryByText('System Taxonomies')).not.toBeInTheDocument();
274
823
  });
275
- it('4.18 documents tab opens and notifies section change', async () => {
276
- const user = userEvent.setup();
277
- const onSectionChange = vi.fn();
278
- renderComponent({ onSectionChange });
279
- await user.click(screen.getByRole('button', { name: /Documents/i }));
280
- expect(onSectionChange).toHaveBeenCalledWith('documents');
824
+ it('4.18 legacy documents initialSection falls back to general tab', () => {
825
+ renderComponent({ initialSection: 'documents' });
826
+ expect(screen.getByRole('button', { name: /General/i })).toBeInTheDocument();
827
+ expect(screen.queryByText('Cloud Document Storage')).not.toBeInTheDocument();
281
828
  });
282
- it('4.19 document admin controls are not rendered in general tab', () => {
829
+ it('4.19 document settings copy is not rendered in settings', () => {
283
830
  renderComponent();
284
831
  expect(screen.queryByText('Document Storage (Admin)')).not.toBeInTheDocument();
832
+ expect(screen.queryByText('Local Documents')).not.toBeInTheDocument();
833
+ expect(screen.queryByText('Cloud Document Storage')).not.toBeInTheDocument();
834
+ expect(screen.queryByRole('button', { name: 'Open App Admin' })).not.toBeInTheDocument();
285
835
  });
286
- it('4.20 Documents tab routes cloud system settings to app admin for authenticated users', async () => {
287
- const fetchMock = vi.fn(async () => ({
836
+ it('4.20 Cloud runtime shows cloud MCP only', async () => {
837
+ const fetchCloudAuthApi = vi.fn(async () => ({
838
+ ok: true,
839
+ json: async () => ({ items: [] })
840
+ }));
841
+ const user = userEvent.setup();
842
+ renderComponent({
843
+ settingsModel: {
844
+ runtimeMode: 'cloud',
845
+ isAuthenticated: true,
846
+ cloudAuthBaseUrl: 'https://cloud.example.com',
847
+ fetchCloudAuthApi
848
+ }
849
+ });
850
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
851
+ expect(await screen.findByText('Cloud MCP Configuration')).toBeInTheDocument();
852
+ expect(screen.getByRole('button', { name: 'Generate Token' })).toBeInTheDocument();
853
+ expect(screen.queryByRole('button', { name: 'Local MCP' })).not.toBeInTheDocument();
854
+ expect(screen.queryByText('MCP Path Mapping (Host Root)')).not.toBeInTheDocument();
855
+ expect(fetchCloudAuthApi).toHaveBeenCalledWith('/api/taskforce/settings/mcp/tokens', expect.objectContaining({
856
+ method: 'GET',
857
+ credentials: 'include'
858
+ }));
859
+ }, 10000);
860
+ it('4.21 Local authenticated runtime defaults MCP tab to cloud with toggle', async () => {
861
+ const fetchCloudAuthApi = vi.fn(async () => ({
288
862
  ok: true,
289
- json: async () => ({ objectStorage: { provider: 'r2' } })
863
+ json: async () => ({ items: [] })
290
864
  }));
291
- vi.stubGlobal('fetch', fetchMock);
292
865
  const user = userEvent.setup();
293
- renderComponent({ settingsModel: { isAuthenticated: true } });
294
- await user.click(screen.getByRole('button', { name: /Documents/i }));
295
- expect(await screen.findByDisplayValue('Cloud (R2)')).toBeInTheDocument();
296
- expect(screen.getByText('Cloud Document Storage')).toBeInTheDocument();
297
- expect(screen.getByText('System-level cloud storage and integrity controls are managed in App Admin.')).toBeInTheDocument();
298
- expect(screen.getByRole('button', { name: 'Open App Admin' })).toBeInTheDocument();
299
- vi.unstubAllGlobals();
866
+ renderComponent({
867
+ settingsModel: {
868
+ runtimeMode: 'local',
869
+ isAuthenticated: true,
870
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
871
+ cloudAuthBaseUrl: 'https://cloud.example.com',
872
+ fetchCloudAuthApi
873
+ }
874
+ });
875
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
876
+ expect(await screen.findByRole('button', { name: 'Cloud MCP' })).toBeInTheDocument();
877
+ expect(screen.getByRole('button', { name: 'Local MCP' })).toBeInTheDocument();
878
+ expect(screen.getByText('Cloud MCP Configuration')).toBeInTheDocument();
879
+ expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
880
+ expect(screen.queryByText('MCP Path Mapping (Host Root)')).not.toBeInTheDocument();
881
+ await user.click(screen.getByRole('button', { name: 'Local MCP' }));
882
+ expect(screen.queryByText('MCP Path Mapping (Host Root)')).not.toBeInTheDocument();
883
+ await user.click(screen.getByRole('button', { name: 'Advanced' }));
884
+ expect(screen.getByText('MCP Path Mapping (Host Root)')).toBeInTheDocument();
885
+ expect(fetchCloudAuthApi).toHaveBeenCalledWith('/api/taskforce/settings/mcp/tokens', expect.objectContaining({
886
+ method: 'GET',
887
+ credentials: 'include'
888
+ }));
889
+ }, 10000);
890
+ it('4.23b MCP token creation uses the provided cloud auth helper', async () => {
891
+ const fetchCloudAuthApi = vi.fn(async (path, init) => {
892
+ if (path === '/api/taskforce/settings/mcp/tokens' && init?.method === 'POST') {
893
+ return {
894
+ ok: true,
895
+ json: async () => ({
896
+ token: 'tfmcp_example_secret',
897
+ record: {
898
+ id: 'mcp-token-1',
899
+ name: 'Workspace Alpha MCP',
900
+ tokenPrefix: 'tfmcp_example',
901
+ scopes: ['tasks:read', 'tasks:write'],
902
+ status: 'active',
903
+ createdAt: new Date().toISOString()
904
+ }
905
+ })
906
+ };
907
+ }
908
+ return {
909
+ ok: true,
910
+ json: async () => ({ items: [] })
911
+ };
912
+ });
913
+ const user = userEvent.setup();
914
+ renderComponent({
915
+ settingsModel: {
916
+ runtimeMode: 'local',
917
+ isAuthenticated: true,
918
+ currentWorkspaceName: 'Workspace Alpha',
919
+ fetchCloudAuthApi
920
+ }
921
+ });
922
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
923
+ await user.click(await screen.findByRole('button', { name: 'Generate Token' }));
924
+ expect(fetchCloudAuthApi).toHaveBeenCalledWith('/api/taskforce/settings/mcp/tokens', expect.objectContaining({
925
+ method: 'POST',
926
+ credentials: 'include'
927
+ }));
928
+ expect((await screen.findAllByText('tfmcp_example_secret')).length).toBeGreaterThan(0);
929
+ }, 10000);
930
+ it('4.23c MCP token name defaults to the workspace name', async () => {
931
+ const user = userEvent.setup();
932
+ renderComponent({
933
+ settingsModel: {
934
+ runtimeMode: 'local',
935
+ isAuthenticated: true,
936
+ currentWorkspaceName: 'Workspace Alpha'
937
+ }
938
+ });
939
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
940
+ expect(await screen.findByDisplayValue('Workspace Alpha MCP')).toBeInTheDocument();
300
941
  });
301
- it('4.21 Documents tab hides cloud controls when user is not signed in', async () => {
942
+ it('4.23d MCP test connection uses the provided cloud auth helper', async () => {
943
+ const fetchCloudAuthApi = vi.fn(async (path, init) => {
944
+ if (path === '/api/taskforce/settings/mcp/test-connection') {
945
+ return {
946
+ ok: true,
947
+ json: async () => ({
948
+ result: {
949
+ workspaceName: 'Workspace Alpha',
950
+ workspaceId: 'workspace-alpha',
951
+ scopes: ['tasks:read', 'tasks:write']
952
+ }
953
+ })
954
+ };
955
+ }
956
+ return {
957
+ ok: true,
958
+ json: async () => ({ items: [] })
959
+ };
960
+ });
302
961
  const user = userEvent.setup();
303
- renderComponent({ settingsModel: { isAuthenticated: false } });
304
- await user.click(screen.getByRole('button', { name: /Documents/i }));
305
- expect(screen.getByText('Sign in to access cloud document storage controls in App Admin.')).toBeInTheDocument();
306
- expect(screen.queryByRole('button', { name: 'Open App Admin' })).not.toBeInTheDocument();
962
+ renderComponent({
963
+ settingsModel: {
964
+ runtimeMode: 'local',
965
+ isAuthenticated: true,
966
+ currentWorkspaceName: 'Workspace Alpha',
967
+ fetchCloudAuthApi
968
+ }
969
+ });
970
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
971
+ await user.click(screen.getByRole('button', { name: 'Test Connection' }));
972
+ await user.type(await screen.findByPlaceholderText('tfmcp_xxxxx_replace-me'), 'tfmcp_example_secret');
973
+ await user.click(screen.getAllByRole('button', { name: 'Test Connection' })[1]);
974
+ expect(fetchCloudAuthApi).toHaveBeenCalledWith('/api/taskforce/settings/mcp/test-connection', expect.objectContaining({
975
+ method: 'POST',
976
+ credentials: 'include'
977
+ }));
978
+ expect(await screen.findByText('Connection looks good for this workspace.')).toBeInTheDocument();
979
+ expect(screen.getByText(/Granted scopes: tasks:read, tasks:write/i)).toBeInTheDocument();
980
+ }, 10000);
981
+ it('4.23e Antigravity uses serverUrl instead of transport in the cloud snippet', async () => {
982
+ const fetchCloudAuthApi = vi.fn(async () => ({
983
+ ok: true,
984
+ json: async () => ({ items: [] })
985
+ }));
986
+ const user = userEvent.setup();
987
+ renderComponent({
988
+ settingsModel: {
989
+ runtimeMode: 'local',
990
+ isAuthenticated: true,
991
+ currentWorkspaceName: 'Workspace Alpha',
992
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
993
+ fetchCloudAuthApi
994
+ }
995
+ });
996
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
997
+ await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'antigravity');
998
+ const snippetMatches = await screen.findAllByText(/serverUrl/);
999
+ expect(snippetMatches.length).toBeGreaterThan(0);
1000
+ expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
1001
+ expect(screen.queryByText(/"transport"/)).not.toBeInTheDocument();
1002
+ }, 10000);
1003
+ it('4.23e1 falls back to the dedicated MCP hostname when only the cloud app host is configured', async () => {
1004
+ const fetchCloudAuthApi = vi.fn(async () => ({
1005
+ ok: true,
1006
+ json: async () => ({ items: [] })
1007
+ }));
1008
+ const user = userEvent.setup();
1009
+ renderComponent({
1010
+ settingsModel: {
1011
+ runtimeMode: 'local',
1012
+ isAuthenticated: true,
1013
+ currentWorkspaceName: 'Workspace Alpha',
1014
+ cloudAuthBaseUrl: 'https://performance-app.taskforcehq.ai',
1015
+ cloudMcpBaseUrl: '',
1016
+ fetchCloudAuthApi
1017
+ }
1018
+ });
1019
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1020
+ expect(await screen.findByText(/https:\/\/performance-mcp\.taskforcehq\.ai\/mcp/)).toBeInTheDocument();
1021
+ expect(screen.queryByText(/https:\/\/performance-app\.taskforcehq\.ai\/mcp/)).not.toBeInTheDocument();
1022
+ }, 10000);
1023
+ it('4.23e1b prefers the dedicated MCP hostname even when cloudMcpBaseUrl is stale and points at the app host', async () => {
1024
+ const fetchCloudAuthApi = vi.fn(async () => ({
1025
+ ok: true,
1026
+ json: async () => ({ items: [] })
1027
+ }));
1028
+ const user = userEvent.setup();
1029
+ renderComponent({
1030
+ settingsModel: {
1031
+ runtimeMode: 'local',
1032
+ isAuthenticated: true,
1033
+ currentWorkspaceName: 'Workspace Alpha',
1034
+ cloudAuthBaseUrl: 'https://performance-app.taskforcehq.ai',
1035
+ cloudMcpBaseUrl: 'https://performance-app.taskforcehq.ai',
1036
+ fetchCloudAuthApi
1037
+ }
1038
+ });
1039
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1040
+ expect(await screen.findByText(/https:\/\/performance-mcp\.taskforcehq\.ai\/mcp/)).toBeInTheDocument();
1041
+ expect(screen.queryByText(/https:\/\/performance-app\.taskforcehq\.ai\/mcp/)).not.toBeInTheDocument();
1042
+ }, 10000);
1043
+ it('4.23e2 Cursor uses top-level url instead of transport in the cloud config', async () => {
1044
+ const fetchCloudAuthApi = vi.fn(async () => ({
1045
+ ok: true,
1046
+ json: async () => ({ items: [] })
1047
+ }));
1048
+ const user = userEvent.setup();
1049
+ renderComponent({
1050
+ settingsModel: {
1051
+ runtimeMode: 'local',
1052
+ isAuthenticated: true,
1053
+ currentWorkspaceName: 'Workspace Alpha',
1054
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1055
+ fetchCloudAuthApi
1056
+ }
1057
+ });
1058
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1059
+ const urlMatches = await screen.findAllByText(/"url"/);
1060
+ expect(urlMatches.length).toBeGreaterThan(0);
1061
+ expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
1062
+ expect(screen.queryByText(/"transport"/)).not.toBeInTheDocument();
1063
+ expect(screen.queryByText(/serverUrl/)).not.toBeInTheDocument();
1064
+ }, 10000);
1065
+ it('4.23e2b Cline uses streamableHttp in the cloud config', async () => {
1066
+ const fetchCloudAuthApi = vi.fn(async () => ({
1067
+ ok: true,
1068
+ json: async () => ({ items: [] })
1069
+ }));
1070
+ const user = userEvent.setup();
1071
+ renderComponent({
1072
+ settingsModel: {
1073
+ runtimeMode: 'local',
1074
+ isAuthenticated: true,
1075
+ currentWorkspaceName: 'Workspace Alpha',
1076
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1077
+ fetchCloudAuthApi
1078
+ }
1079
+ });
1080
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1081
+ await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'cline');
1082
+ expect(await screen.findByText(/Cline Configuration Instructions/i)).toBeInTheDocument();
1083
+ expect(screen.getByText(/"type"/)).toBeInTheDocument();
1084
+ expect(screen.getByText(/streamableHttp/)).toBeInTheDocument();
1085
+ expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
1086
+ }, 10000);
1087
+ it('4.23e2c VS Code uses servers with http transport in the cloud config', async () => {
1088
+ const fetchCloudAuthApi = vi.fn(async () => ({
1089
+ ok: true,
1090
+ json: async () => ({ items: [] })
1091
+ }));
1092
+ const user = userEvent.setup();
1093
+ renderComponent({
1094
+ settingsModel: {
1095
+ runtimeMode: 'local',
1096
+ isAuthenticated: true,
1097
+ currentWorkspaceName: 'Workspace Alpha',
1098
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1099
+ fetchCloudAuthApi
1100
+ }
1101
+ });
1102
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1103
+ await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'vscode');
1104
+ expect(await screen.findByText(/VS Code Configuration Instructions/i)).toBeInTheDocument();
1105
+ expect(screen.getByText(/"servers"/)).toBeInTheDocument();
1106
+ expect(screen.getByText(/"type"/)).toBeInTheDocument();
1107
+ expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
1108
+ }, 10000);
1109
+ it('4.23e3 Codex shows config.toml MCP output', async () => {
1110
+ const fetchCloudAuthApi = vi.fn(async () => ({
1111
+ ok: true,
1112
+ json: async () => ({ items: [] })
1113
+ }));
1114
+ const user = userEvent.setup();
1115
+ renderComponent({
1116
+ settingsModel: {
1117
+ runtimeMode: 'local',
1118
+ isAuthenticated: true,
1119
+ currentWorkspaceName: 'Workspace Alpha',
1120
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1121
+ fetchCloudAuthApi
1122
+ }
1123
+ });
1124
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1125
+ await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'codex');
1126
+ expect((await screen.findAllByText(/\.codex\/config\.toml/i)).length).toBeGreaterThan(0);
1127
+ expect(screen.getByText(/\[mcp_servers\./)).toBeInTheDocument();
1128
+ expect(screen.getByText(/http_headers = \{ Authorization = "Bearer/i)).toBeInTheDocument();
1129
+ expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
1130
+ }, 10000);
1131
+ it('4.23e3a Codex MCP output includes optional automation identity headers', async () => {
1132
+ const fetchCloudAuthApi = vi.fn(async () => ({
1133
+ ok: true,
1134
+ json: async () => ({ items: [] })
1135
+ }));
1136
+ const user = userEvent.setup();
1137
+ renderComponent({
1138
+ settingsModel: {
1139
+ runtimeMode: 'local',
1140
+ isAuthenticated: true,
1141
+ currentWorkspaceName: 'Workspace Alpha',
1142
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1143
+ fetchCloudAuthApi
1144
+ }
1145
+ });
1146
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1147
+ await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'codex');
1148
+ await user.type(screen.getByPlaceholderText('Automation Codex'), 'Automation Codex');
1149
+ await user.type(screen.getByPlaceholderText('tfp_...'), 'tfp_automation_codex');
1150
+ expect(await screen.findByText(/X-Taskforce-AI-Name = "Automation Codex"/i)).toBeInTheDocument();
1151
+ expect(screen.getByText(/X-Taskforce-AI-Profile-Token = "tfp_automation_codex"/i)).toBeInTheDocument();
1152
+ }, 10000);
1153
+ it('4.23e3aa MCP tab can prefill automation identity from an existing AI profile', async () => {
1154
+ const fetchCloudAuthApi = vi.fn(async () => ({
1155
+ ok: true,
1156
+ json: async () => ({ items: [] })
1157
+ }));
1158
+ const fetchMock = vi.fn(async (input) => {
1159
+ const url = String(input);
1160
+ if (url.includes('/api/taskforce/workspace/assignee-options')) {
1161
+ return {
1162
+ ok: true,
1163
+ json: async () => ({
1164
+ assignees: [
1165
+ {
1166
+ value: 'ai-profile-123',
1167
+ label: 'Automation Codex',
1168
+ profileToken: 'tfp_automation_codex',
1169
+ username: 'automation-codex',
1170
+ icon: 'Bot',
1171
+ color: '#6B7280',
1172
+ kind: 'agent',
1173
+ }
1174
+ ]
1175
+ })
1176
+ };
1177
+ }
1178
+ throw new Error(`Unexpected fetch call: ${url}`);
1179
+ });
1180
+ global.fetch = fetchMock;
1181
+ const user = userEvent.setup();
1182
+ renderComponent({
1183
+ settingsModel: {
1184
+ runtimeMode: 'local',
1185
+ isAuthenticated: true,
1186
+ workspaceId: 'workspace-alpha',
1187
+ currentWorkspaceName: 'Workspace Alpha',
1188
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1189
+ fetchCloudAuthApi
1190
+ }
1191
+ });
1192
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1193
+ await user.selectOptions(await screen.findByRole('combobox', { name: 'Use existing AI profile' }), 'ai-profile-123');
1194
+ await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'codex');
1195
+ expect(await screen.findByDisplayValue('Automation Codex')).toBeInTheDocument();
1196
+ expect(screen.getByDisplayValue('tfp_automation_codex')).toBeInTheDocument();
1197
+ expect(screen.getByText(/X-Taskforce-AI-Name = "Automation Codex"/i)).toBeInTheDocument();
1198
+ expect(screen.getByText(/X-Taskforce-AI-Profile-Token = "tfp_automation_codex"/i)).toBeInTheDocument();
1199
+ global.fetch = originalFetch;
1200
+ }, 10000);
1201
+ it('4.23e3b Claude Code shows a remote MCP add command', async () => {
1202
+ const fetchCloudAuthApi = vi.fn(async () => ({
1203
+ ok: true,
1204
+ json: async () => ({ items: [] })
1205
+ }));
1206
+ const user = userEvent.setup();
1207
+ renderComponent({
1208
+ settingsModel: {
1209
+ runtimeMode: 'local',
1210
+ isAuthenticated: true,
1211
+ currentWorkspaceName: 'Workspace Alpha',
1212
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1213
+ fetchCloudAuthApi
1214
+ }
1215
+ });
1216
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1217
+ await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'claude-code');
1218
+ expect(await screen.findByText(/Claude Code Configuration Instructions/i)).toBeInTheDocument();
1219
+ expect(screen.getByText(/claude mcp add --transport http/i)).toBeInTheDocument();
1220
+ expect(screen.getByText(/claude mcp add --transport http taskforce-/i)).toBeInTheDocument();
1221
+ expect(screen.getByText(/--header "Authorization: Bearer/i)).toBeInTheDocument();
1222
+ }, 10000);
1223
+ it('4.23e3c Gemini CLI shows a remote MCP add command', async () => {
1224
+ const fetchCloudAuthApi = vi.fn(async () => ({
1225
+ ok: true,
1226
+ json: async () => ({ items: [] })
1227
+ }));
1228
+ const user = userEvent.setup();
1229
+ renderComponent({
1230
+ settingsModel: {
1231
+ runtimeMode: 'local',
1232
+ isAuthenticated: true,
1233
+ currentWorkspaceName: 'Workspace Alpha',
1234
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1235
+ fetchCloudAuthApi
1236
+ }
1237
+ });
1238
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1239
+ await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'gemini-cli');
1240
+ expect(await screen.findByText(/Gemini CLI Configuration Instructions/i)).toBeInTheDocument();
1241
+ expect(screen.getByText(/gemini mcp add taskforce-/i)).toBeInTheDocument();
1242
+ expect(screen.getByText(/--transport http/i)).toBeInTheDocument();
1243
+ expect(screen.getByText(/--header "Authorization: Bearer/i)).toBeInTheDocument();
1244
+ expect(screen.getByText(/Add --scope user if you want it available across all projects/i)).toBeInTheDocument();
1245
+ }, 10000);
1246
+ it('4.23e3d OpenClaw shows an openclaw.json MCP snippet', async () => {
1247
+ const fetchCloudAuthApi = vi.fn(async () => ({
1248
+ ok: true,
1249
+ json: async () => ({ items: [] })
1250
+ }));
1251
+ const user = userEvent.setup();
1252
+ renderComponent({
1253
+ settingsModel: {
1254
+ runtimeMode: 'local',
1255
+ isAuthenticated: true,
1256
+ currentWorkspaceName: 'Workspace Alpha',
1257
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1258
+ fetchCloudAuthApi
1259
+ }
1260
+ });
1261
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1262
+ await user.selectOptions(screen.getByRole('combobox', { name: 'MCP client' }), 'openclaw');
1263
+ expect(await screen.findByText(/OpenClaw Configuration Instructions/i)).toBeInTheDocument();
1264
+ expect((await screen.findAllByText(/~\/\.openclaw\/openclaw\.json/i)).length).toBeGreaterThan(0);
1265
+ const configBlocks = Array.from(document.querySelectorAll('pre code'));
1266
+ const openClawConfigBlock = configBlocks.find((block) => block.textContent?.includes('https://mcp.taskforcehq.ai/mcp'));
1267
+ expect(openClawConfigBlock?.textContent).toContain('"mcp"');
1268
+ expect(openClawConfigBlock?.textContent).toContain('"servers"');
1269
+ expect(openClawConfigBlock?.textContent).toContain('"transport": "streamable-http"');
1270
+ expect(openClawConfigBlock?.textContent).toContain('"Authorization": "Bearer');
1271
+ expect(screen.getAllByText(/https:\/\/mcp\.taskforcehq\.ai\/mcp/).length).toBeGreaterThan(0);
1272
+ }, 10000);
1273
+ it('4.23e4 Cloud selector excludes desktop OAuth clients from the MCP client list', async () => {
1274
+ const fetchCloudAuthApi = vi.fn(async () => ({
1275
+ ok: true,
1276
+ json: async () => ({ items: [] })
1277
+ }));
1278
+ const user = userEvent.setup();
1279
+ renderComponent({
1280
+ settingsModel: {
1281
+ runtimeMode: 'local',
1282
+ isAuthenticated: true,
1283
+ currentWorkspaceName: 'Workspace Alpha',
1284
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1285
+ fetchCloudAuthApi
1286
+ }
1287
+ });
1288
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1289
+ expect(screen.queryByRole('option', { name: /Claude Desktop/i })).not.toBeInTheDocument();
1290
+ expect(screen.queryByRole('option', { name: /ChatGPT Desktop/i })).not.toBeInTheDocument();
1291
+ expect(await screen.findByText('Cursor Configuration Instructions')).toBeInTheDocument();
1292
+ }, 10000);
1293
+ it('4.23e4b Integrations tab shows ChatGPT and Claude connector management separately from PAT tokens', async () => {
1294
+ const fetchCloudAuthApi = vi.fn(async (path) => {
1295
+ if (path.startsWith('/api/taskforce/settings/mcp/connectors')) {
1296
+ return {
1297
+ ok: true,
1298
+ json: async () => ({
1299
+ items: [
1300
+ {
1301
+ id: 'grant-1',
1302
+ provider: 'chatgpt',
1303
+ clientId: 'client-1',
1304
+ clientName: 'ChatGPT Connector',
1305
+ workspaceId: 'workspace-alpha',
1306
+ userId: 'user-1',
1307
+ scopes: ['tasks:read', 'tasks:write', 'workspace:read'],
1308
+ status: 'active',
1309
+ createdAt: '2026-03-14T12:00:00.000Z',
1310
+ lastUsedAt: null,
1311
+ },
1312
+ {
1313
+ id: 'grant-2',
1314
+ provider: 'claude',
1315
+ clientId: 'client-2',
1316
+ clientName: 'Claude Desktop Connector',
1317
+ workspaceId: 'workspace-alpha',
1318
+ userId: 'user-1',
1319
+ scopes: ['tasks:read', 'tasks:write', 'workspace:read'],
1320
+ status: 'active',
1321
+ createdAt: '2026-03-14T13:00:00.000Z',
1322
+ lastUsedAt: null,
1323
+ }
1324
+ ]
1325
+ })
1326
+ };
1327
+ }
1328
+ return {
1329
+ ok: true,
1330
+ json: async () => ({ items: [] })
1331
+ };
1332
+ });
1333
+ const user = userEvent.setup();
1334
+ renderComponent({
1335
+ settingsModel: {
1336
+ runtimeMode: 'local',
1337
+ isAuthenticated: true,
1338
+ currentWorkspaceName: 'Workspace Alpha',
1339
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1340
+ fetchCloudAuthApi
1341
+ }
1342
+ });
1343
+ await user.click(screen.getByRole('button', { name: /Integrations/i }));
1344
+ expect(await screen.findByRole('heading', { name: 'Integrations' })).toBeInTheDocument();
1345
+ expect(await screen.findByText('ChatGPT Connector')).toBeInTheDocument();
1346
+ expect(await screen.findByText('Claude Desktop Connector')).toBeInTheDocument();
1347
+ expect(screen.getAllByText(/Taskforce handles OAuth and binds each connector authorization/i)).toHaveLength(2);
1348
+ expect(screen.getAllByRole('button', { name: /Disconnect/i })).toHaveLength(2);
1349
+ }, 10000);
1350
+ it('4.23e4c MCP tab no longer shows connector management cards', async () => {
1351
+ const fetchCloudAuthApi = vi.fn(async () => ({
1352
+ ok: true,
1353
+ json: async () => ({ items: [] })
1354
+ }));
1355
+ const user = userEvent.setup();
1356
+ renderComponent({
1357
+ settingsModel: {
1358
+ runtimeMode: 'local',
1359
+ isAuthenticated: true,
1360
+ currentWorkspaceName: 'Workspace Alpha',
1361
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1362
+ fetchCloudAuthApi
1363
+ }
1364
+ });
1365
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1366
+ expect(await screen.findByText('Cloud MCP Configuration')).toBeInTheDocument();
1367
+ expect(screen.queryByText('ChatGPT Connector')).not.toBeInTheDocument();
1368
+ expect(screen.queryByText(/^Connectors$/)).not.toBeInTheDocument();
1369
+ }, 10000);
1370
+ it('4.23g Cloud MCP configuration offers snippet and full-config copy actions', async () => {
1371
+ const user = userEvent.setup();
1372
+ renderComponent({
1373
+ settingsModel: {
1374
+ runtimeMode: 'local',
1375
+ isAuthenticated: true,
1376
+ currentWorkspaceName: 'Workspace Alpha',
1377
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1378
+ }
1379
+ });
1380
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1381
+ expect(await screen.findByText('Cloud MCP Configuration')).toBeInTheDocument();
1382
+ expect(screen.getAllByRole('button', { name: /copy/i }).length).toBeGreaterThan(0);
1383
+ }, 10000);
1384
+ it('4.23g1 Cloud MCP copy warns before a token is generated', async () => {
1385
+ const user = userEvent.setup();
1386
+ renderComponent({
1387
+ settingsModel: {
1388
+ runtimeMode: 'local',
1389
+ isAuthenticated: true,
1390
+ currentWorkspaceName: 'Workspace Alpha',
1391
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1392
+ }
1393
+ });
1394
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1395
+ await screen.findByText('Cloud MCP Configuration');
1396
+ await user.click(screen.getByTitle('Copy full configuration file'));
1397
+ expect(screen.getByText('Generate a token before copying the Cloud MCP configuration.')).toBeInTheDocument();
1398
+ }, 10000);
1399
+ it('4.23h Active tokens sort first and revoked tokens do not show revoke or regenerate actions', async () => {
1400
+ const user = userEvent.setup();
1401
+ const fetchCloudAuthApi = vi.fn(async () => ({
1402
+ ok: true,
1403
+ json: async () => ({
1404
+ items: [
1405
+ {
1406
+ id: 'token-revoked',
1407
+ name: 'Old Token',
1408
+ scopes: ['tasks:read'],
1409
+ tokenPrefix: 'tfmcp_old',
1410
+ status: 'revoked',
1411
+ createdAt: '2026-03-01T00:00:00.000Z',
1412
+ lastUsedAt: null,
1413
+ },
1414
+ {
1415
+ id: 'token-active',
1416
+ name: 'Current Token',
1417
+ scopes: ['tasks:read', 'tasks:write'],
1418
+ tokenPrefix: 'tfmcp_new',
1419
+ status: 'active',
1420
+ createdAt: '2026-03-02T00:00:00.000Z',
1421
+ lastUsedAt: null,
1422
+ }
1423
+ ]
1424
+ })
1425
+ }));
1426
+ renderComponent({
1427
+ settingsModel: {
1428
+ runtimeMode: 'local',
1429
+ isAuthenticated: true,
1430
+ currentWorkspaceName: 'Workspace Alpha',
1431
+ fetchCloudAuthApi
1432
+ }
1433
+ });
1434
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1435
+ expect(await screen.findByText('Current Token')).toBeInTheDocument();
1436
+ expect(screen.getByText('Old Token')).toBeInTheDocument();
1437
+ const tokenLabels = screen.getAllByText(/^(Current Token|Old Token)$/);
1438
+ expect(tokenLabels[0]).toHaveTextContent('Current Token');
1439
+ expect(tokenLabels[1]).toHaveTextContent('Old Token');
1440
+ expect(screen.getAllByRole('button', { name: 'Regenerate' })).toHaveLength(1);
1441
+ expect(screen.getAllByRole('button', { name: 'Revoke' })).toHaveLength(1);
1442
+ }, 10000);
1443
+ it('4.23f Cloud MCP snippet keeps the server id short enough for client tool-name limits', async () => {
1444
+ const user = userEvent.setup();
1445
+ renderComponent({
1446
+ settingsModel: {
1447
+ runtimeMode: 'local',
1448
+ isAuthenticated: true,
1449
+ workspaceId: 'workspace-019c771a-b6d7-7bc8-b837-c1133f3ba7a2',
1450
+ currentWorkspaceName: 'Workspace Alpha',
1451
+ cloudMcpBaseUrl: 'https://mcp.taskforcehq.ai',
1452
+ }
1453
+ });
1454
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1455
+ await screen.findByText('Cloud MCP Configuration');
1456
+ const codeNodes = Array.from(document.querySelectorAll('code'));
1457
+ const snippetNode = codeNodes.find((node) => node.textContent?.includes('"mcpServers"'));
1458
+ expect(snippetNode).toBeTruthy();
1459
+ const parsed = JSON.parse(snippetNode?.textContent || '{}');
1460
+ const serverId = Object.keys(parsed?.mcpServers || {})[0] || '';
1461
+ expect(serverId.startsWith('taskforce-')).toBe(true);
1462
+ expect(serverId.length).toBeLessThanOrEqual(33);
1463
+ expect(`mcp_${serverId}_set_schedule_preferences`.length).toBeLessThanOrEqual(64);
1464
+ }, 10000);
1465
+ it('4.24 Local signed-out runtime shows local MCP only', async () => {
1466
+ const user = userEvent.setup();
1467
+ renderComponent({
1468
+ settingsModel: {
1469
+ runtimeMode: 'local',
1470
+ isAuthenticated: false
1471
+ }
1472
+ });
1473
+ await user.click(screen.getByRole('button', { name: /MCP/i }));
1474
+ expect(screen.getByText('Local MCP')).toBeInTheDocument();
1475
+ expect(screen.getByText(/Claude Code CLI, Gemini CLI/i)).toBeInTheDocument();
1476
+ expect(screen.queryByText('MCP Path Mapping (Host Root)')).not.toBeInTheDocument();
1477
+ await user.click(screen.getByRole('button', { name: 'Advanced' }));
1478
+ expect(screen.getByText('MCP Path Mapping (Host Root)')).toBeInTheDocument();
1479
+ expect(screen.queryByText('Cloud MCP Configuration')).not.toBeInTheDocument();
1480
+ expect(screen.queryByRole('button', { name: 'Cloud MCP' })).not.toBeInTheDocument();
307
1481
  });
308
1482
  });