@taskforcehq/taskforce 0.3.170 → 0.3.300

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 (696) hide show
  1. package/README.md +52 -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 +2212 -1194
  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 +66 -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 +46 -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 +239 -0
  269. package/dist/hooks/useTaskData.test.d.ts +1 -0
  270. package/dist/hooks/useTaskData.test.js +119 -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 +3293 -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-TVmCgizS.js +1 -0
  489. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +1 -0
  490. package/dist/ui/assets/AnnotatedAttachmentWorkspace-CAyhnPeR.js +3 -0
  491. package/dist/ui/assets/DocumentWorkspace-BOCw5e4D.js +250 -0
  492. package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +1 -0
  493. package/dist/ui/assets/InitiativesModule-CN0r02Oi.js +1 -0
  494. package/dist/ui/assets/PlansPage-BlVC_lRq.css +1 -0
  495. package/dist/ui/assets/PlansPage-FcQSNLZd.js +1 -0
  496. package/dist/ui/assets/TaskSettings-BwB9NtmJ.css +1 -0
  497. package/dist/ui/assets/TaskSettings-Cw5zYD1L.js +8 -0
  498. package/dist/ui/assets/WorkflowsModule-CY9R2zPB.js +5 -0
  499. package/dist/ui/assets/index-Bzqy5QGm.js +6 -0
  500. package/dist/ui/assets/index-DxRiI3tY.css +1 -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 +28 -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/migrate-planning-model.ts +233 -0
  610. package/scripts/migrate-task-data-assets.mjs +408 -0
  611. package/scripts/playwright-auth.sh +11 -0
  612. package/scripts/run-dev-proxy.sh +2 -0
  613. package/scripts/update-homebrew-formula.sh +2 -2
  614. package/scripts/with-esbuild.sh +11 -0
  615. package/src/resources/templates/WORKFLOW_V2_POLICY.md +1 -1
  616. package/src/resources/templates/workflow-sources/workflowDocs.mjs +62 -52
  617. package/src/resources/templates/workflows/collaborate.yaml +11 -5
  618. package/src/resources/templates/workflows/do.yaml +8 -2
  619. package/src/resources/templates/workflows/evaluate.yaml +13 -7
  620. package/src/resources/templates/workflows/execute.yaml +40 -23
  621. package/src/resources/templates/workflows/plan.yaml +15 -8
  622. package/src/resources/templates/workflows/review.yaml +14 -8
  623. package/dist/TaskforceScreenshot.d.ts +0 -11
  624. package/dist/TaskforceScreenshot.js +0 -113
  625. package/dist/compat/priorityCompat.d.ts +0 -2
  626. package/dist/compat/priorityCompat.js +0 -24
  627. package/dist/components/features/settings/TaxonomyManager 2.d.ts +0 -52
  628. package/dist/components/features/settings/TaxonomyManager 2.js +0 -75
  629. package/dist/components/task/TaskKanban 2.d.ts +0 -76
  630. package/dist/components/task/TaskKanban 2.js +0 -1004
  631. package/dist/components/task/TaskScreenshotSection.d.ts +0 -15
  632. package/dist/components/task/TaskScreenshotSection.js +0 -22
  633. package/dist/components/views/StandaloneLayout 2.d.ts +0 -8
  634. package/dist/components/views/StandaloneLayout 2.js +0 -1660
  635. package/dist/core/InitiativeTemplates.test.js +0 -50
  636. package/dist/core/ParentLifecycle.test.js +0 -98
  637. package/dist/migrations/clientMigrations.d.ts +0 -9
  638. package/dist/migrations/clientMigrations.js +0 -74
  639. package/dist/migrations/flags.d.ts +0 -6
  640. package/dist/migrations/flags.js +0 -8
  641. package/dist/migrations/taskMigrations.d.ts +0 -71
  642. package/dist/migrations/taskMigrations.js +0 -236
  643. package/dist/migrations/taskMigrations.test.js +0 -34
  644. package/dist/server/routes.billing.js +0 -991
  645. package/dist/server/routes.billing.test.js +0 -387
  646. package/dist/server/routes.sync.integration.test.js +0 -470
  647. package/dist/server/routes.sync.js +0 -1141
  648. package/dist/soak/syncSoakHarness.d.ts +0 -6
  649. package/dist/soak/syncSoakHarness.js +0 -10
  650. package/dist/ui/assets/index-0W5vyCpn.css +0 -1
  651. package/dist/ui/assets/index-MCZuNNfn.js +0 -23
  652. package/dist/ui/assets/logo_white_01-0Vx-iIIv.png +0 -0
  653. package/dist/ui/assets/vendor-dnd-BI1K77C1.js +0 -5
  654. package/dist/ui/assets/vendor-react-DdIDsh7f.js +0 -9
  655. package/dist/ui/assets/vendor-router-BtiNU7Dl.js +0 -3
  656. package/dist/utils/taskHierarchy.d.ts +0 -4
  657. package/dist/utils/taskHierarchy.js +0 -69
  658. package/dist/utils/taskHierarchy.test.js +0 -38
  659. package/dist/utils/taskParenting.d.ts +0 -3
  660. package/dist/utils/taskParenting.js +0 -24
  661. package/dist/utils/taskParenting.test.js +0 -21
  662. package/dist/utils/taskRelations.d.ts +0 -6
  663. package/dist/utils/taskRelations.js +0 -24
  664. package/dist/utils/taskRelations.test.js +0 -34
  665. package/scripts/check-compat-boundaries.js +0 -48
  666. /package/dist/{__tests__/planParser.test.d.ts → TaskforceCore.filter-persistence.test.d.ts} +0 -0
  667. /package/dist/{__tests__/releaseVersion.test.d.ts → TaskforceCore.routing.test.d.ts} +0 -0
  668. /package/dist/{__tests__/structuredCommentValidation.test.d.ts → annotatedAttachments/AnnotatedAttachmentSessionStore.test.d.ts} +0 -0
  669. /package/dist/{__tests__/structuredDocValidation.test.d.ts → annotatedAttachments/payload.test.d.ts} +0 -0
  670. /package/dist/{__tests__/templateEngine.test.d.ts → annotatedAttachments/types.js} +0 -0
  671. /package/dist/{__tests__/templates.test.d.ts → compat/workspaceSyncCompat.test.d.ts} +0 -0
  672. /package/dist/{__tests__/workflowDocumentFixtures.test.d.ts → components/features/AgentsModule.test.d.ts} +0 -0
  673. /package/dist/{__tests__/workflowExportMatrix.test.d.ts → components/features/AnnotatedAttachmentWorkspace.test.d.ts} +0 -0
  674. /package/dist/{__tests__/workflowExportSnapshots.test.d.ts → components/features/DocumentWorkspace.test.d.ts} +0 -0
  675. /package/dist/{__tests__/workflowGeneration.test.d.ts → components/features/settings/TaxonomyLibraryManager.test.d.ts} +0 -0
  676. /package/dist/{__tests__/workflowV2Policy.test.d.ts → components/task/TaskActionHeader.test.d.ts} +0 -0
  677. /package/dist/{core/InitiativeTemplates.test.d.ts → components/task/TaskKanban.planning-drop.test.d.ts} +0 -0
  678. /package/dist/{core/ParentLifecycle.test.d.ts → components/task/TaskStatusActions.test.d.ts} +0 -0
  679. /package/dist/{migrations/taskMigrations.test.d.ts → components/ui/TopNoticeLayer.test.d.ts} +0 -0
  680. /package/dist/{server/routes.billing.test.d.ts → components/views/AppShellHeader.test.d.ts} +0 -0
  681. /package/dist/{server/routes.sync.integration.test.d.ts → components/views/PlanComparisonPage.test.d.ts} +0 -0
  682. /package/dist/{utils/taskHierarchy.test.d.ts → components/views/chrome/WorkspaceChromeSections.test.d.ts} +0 -0
  683. /package/dist/{utils/taskParenting.test.d.ts → components/views/chrome/workspaceChrome.test.d.ts} +0 -0
  684. /package/dist/{utils/taskRelations.test.d.ts → components/views/panels/DocumentFilterToolbar.test.d.ts} +0 -0
  685. /package/dist/public/{android-chrome-192x192.png → archive/phoenix/android-chrome-192x192.png} +0 -0
  686. /package/dist/public/{android-chrome-512x512.png → archive/phoenix/android-chrome-512x512.png} +0 -0
  687. /package/dist/public/{favicon-16x16.png → archive/phoenix/favicon-16x16.png} +0 -0
  688. /package/dist/public/{favicon-32x32.png → archive/phoenix/favicon-32x32.png} +0 -0
  689. /package/dist/public/{favicon.png → archive/phoenix/favicon.png} +0 -0
  690. /package/dist/{__tests__ → services}/workflowExportSnapshots.test.js +0 -0
  691. /package/dist/{__tests__ → services}/workflowGeneration.test.js +0 -0
  692. /package/dist/ui/favicon/{android-chrome-192x192.png → archive/phoenix/android-chrome-192x192.png} +0 -0
  693. /package/dist/ui/favicon/{android-chrome-512x512.png → archive/phoenix/android-chrome-512x512.png} +0 -0
  694. /package/dist/ui/favicon/{favicon-16x16.png → archive/phoenix/favicon-16x16.png} +0 -0
  695. /package/dist/ui/favicon/{favicon-32x32.png → archive/phoenix/favicon-32x32.png} +0 -0
  696. /package/dist/ui/favicon/{favicon.png → archive/phoenix/favicon.png} +0 -0
@@ -0,0 +1,921 @@
1
+ import { randomBytes, createHash } from 'crypto';
2
+ import { TaskforceRuleError } from './types.js';
3
+ import { hashMcpAccessTokenSecret, verifyMcpAccessTokenSecret, hashMcpOAuthSecret, verifyMcpOAuthSecret, generateUuidV7, } from './shared.js';
4
+ // ─── Constants ───────────────────────────────────────────────────────────────
5
+ const MCP_ACCESS_TOKEN_MAX_ACTIVE_PER_USER_WORKSPACE = 10;
6
+ const MCP_ACCESS_TOKEN_DEFAULT_PAGE_SIZE = 20;
7
+ const MCP_ACCESS_TOKEN_MAX_PAGE_SIZE = 100;
8
+ const MCP_ACCESS_TOKEN_ALLOWED_SCOPES = ['tasks:read', 'tasks:write', 'workspace:read'];
9
+ const MCP_OAUTH_AUTH_CODE_TTL_MS = 10 * 60 * 1000;
10
+ /**
11
+ * Handles MCP direct-access tokens and MCP OAuth connector flow
12
+ * (client registration, authorization codes, token exchange, grant lifecycle).
13
+ */
14
+ export class McpTokenService {
15
+ db;
16
+ tenantId;
17
+ deps;
18
+ constructor(db, tenantId, deps) {
19
+ this.db = db;
20
+ this.tenantId = tenantId;
21
+ this.deps = deps;
22
+ }
23
+ // ─── Pure helpers (no external deps) ────────────────────────────────────
24
+ generateEntityId(prefix) {
25
+ return `${prefix}-${generateUuidV7()}`;
26
+ }
27
+ normalizeAccessRole(role, fallback = 'member') {
28
+ const normalized = String(role || '').trim().toLowerCase();
29
+ if (normalized === 'owner' || normalized === 'admin' || normalized === 'member' || normalized === 'read-only') {
30
+ return normalized;
31
+ }
32
+ if (normalized === 'readonly')
33
+ return 'read-only';
34
+ return fallback;
35
+ }
36
+ normalizePermissionMode(mode, fallback = 'read-write') {
37
+ const normalized = String(mode || '').trim().toLowerCase();
38
+ if (normalized === 'read-only' || normalized === 'readonly')
39
+ return 'read-only';
40
+ if (normalized === 'read-write' || normalized === 'readwrite')
41
+ return 'read-write';
42
+ return fallback;
43
+ }
44
+ normalizeMcpAccessTokenScopes(scopes) {
45
+ const values = Array.isArray(scopes) ? scopes : [];
46
+ const normalized = values
47
+ .map((value) => String(value || '').trim())
48
+ .filter((value) => MCP_ACCESS_TOKEN_ALLOWED_SCOPES.includes(value));
49
+ const unique = Array.from(new Set(normalized));
50
+ if (unique.length === 0) {
51
+ throw new TaskforceRuleError('At least one valid MCP token scope is required', 400, 'MCP_TOKEN_SCOPE_REQUIRED');
52
+ }
53
+ return unique;
54
+ }
55
+ createMcpAccessTokenPrefix() {
56
+ return `tfmcp_${randomBytes(6).toString('hex')}`;
57
+ }
58
+ parseMcpAccessToken(rawToken) {
59
+ const normalized = String(rawToken || '').trim();
60
+ if (!normalized.startsWith('tfmcp_'))
61
+ return null;
62
+ const parts = normalized.split('_');
63
+ if (parts.length < 3)
64
+ return null;
65
+ const tokenPrefix = `${parts[0]}_${parts[1]}`;
66
+ const secret = parts.slice(2).join('_').trim();
67
+ if (!tokenPrefix || !secret)
68
+ return null;
69
+ return { tokenPrefix, secret };
70
+ }
71
+ normalizeMcpOAuthProvider(raw) {
72
+ const normalized = String(raw || '').trim().toLowerCase();
73
+ if (normalized === 'chatgpt')
74
+ return 'chatgpt';
75
+ if (normalized === 'claude')
76
+ return 'claude';
77
+ throw new TaskforceRuleError('Unsupported MCP OAuth provider', 400, 'MCP_OAUTH_PROVIDER_INVALID');
78
+ }
79
+ defaultMcpOAuthClientName(provider) {
80
+ return provider === 'claude' ? 'Claude Desktop Connector' : 'ChatGPT Connector';
81
+ }
82
+ createMcpOAuthCodePrefix() {
83
+ return `tfmcpoauthcode_${randomBytes(6).toString('hex')}`;
84
+ }
85
+ createMcpOAuthAccessTokenPrefix() {
86
+ return `tfmcpoauth_${randomBytes(6).toString('hex')}`;
87
+ }
88
+ parseMcpOAuthOpaqueSecret(rawToken, expectedPrefix) {
89
+ const normalized = String(rawToken || '').trim();
90
+ if (!normalized.startsWith(expectedPrefix))
91
+ return null;
92
+ const marker = expectedPrefix.endsWith('_') ? expectedPrefix.slice(0, -1) : expectedPrefix;
93
+ const parts = normalized.split('_');
94
+ if (parts.length < 3)
95
+ return null;
96
+ const tokenPrefix = `${marker}_${parts[1]}`;
97
+ const secret = parts.slice(2).join('_').trim();
98
+ if (!tokenPrefix || !secret)
99
+ return null;
100
+ return { tokenPrefix, secret };
101
+ }
102
+ normalizeRedirectUris(value) {
103
+ const redirectUris = Array.isArray(value) ? value : [];
104
+ const normalized = Array.from(new Set(redirectUris
105
+ .map((uri) => String(uri || '').trim())
106
+ .filter(Boolean)
107
+ .filter((uri) => {
108
+ try {
109
+ const parsed = new URL(uri);
110
+ return parsed.protocol === 'https:' || parsed.protocol === 'http:';
111
+ }
112
+ catch {
113
+ return false;
114
+ }
115
+ })));
116
+ if (normalized.length === 0) {
117
+ throw new TaskforceRuleError('At least one valid redirect URI is required', 400, 'MCP_OAUTH_REDIRECT_URI_REQUIRED');
118
+ }
119
+ return normalized;
120
+ }
121
+ normalizePkceMethod(raw) {
122
+ const normalized = String(raw || '').trim().toUpperCase();
123
+ if (!normalized || normalized === 'S256')
124
+ return 'S256';
125
+ throw new TaskforceRuleError('Unsupported PKCE code challenge method', 400, 'MCP_OAUTH_PKCE_METHOD_INVALID');
126
+ }
127
+ resolveMcpOAuthGrantStatus(row) {
128
+ if (String(row.status || '').trim().toLowerCase() === 'revoked')
129
+ return 'revoked';
130
+ if (row.revoked_at)
131
+ return 'revoked';
132
+ return 'active';
133
+ }
134
+ getMcpOAuthClientRow(clientId, provider) {
135
+ const normalizedClientId = String(clientId || '').trim();
136
+ if (!normalizedClientId)
137
+ return null;
138
+ if (provider) {
139
+ return this.db.prepare(`
140
+ SELECT id, provider, client_name, redirect_uris_json, status, created_at, updated_at
141
+ FROM mcp_oauth_clients
142
+ WHERE tenant_id = ? AND id = ? AND provider = ?
143
+ LIMIT 1
144
+ `).get(this.tenantId, normalizedClientId, provider) || null;
145
+ }
146
+ return this.db.prepare(`
147
+ SELECT id, provider, client_name, redirect_uris_json, status, created_at, updated_at
148
+ FROM mcp_oauth_clients
149
+ WHERE tenant_id = ? AND id = ?
150
+ LIMIT 1
151
+ `).get(this.tenantId, normalizedClientId) || null;
152
+ }
153
+ rowToMcpOAuthClientRecord(row) {
154
+ let redirectUris = [];
155
+ try {
156
+ redirectUris = this.normalizeRedirectUris(JSON.parse(String(row?.redirect_uris_json || '[]')));
157
+ }
158
+ catch {
159
+ redirectUris = [];
160
+ }
161
+ return {
162
+ id: String(row?.id || ''),
163
+ provider: this.normalizeMcpOAuthProvider(row?.provider),
164
+ clientName: String(row?.client_name || ''),
165
+ redirectUris,
166
+ status: String(row?.status || '').trim().toLowerCase() === 'revoked' ? 'revoked' : 'active',
167
+ createdAt: String(row?.created_at || ''),
168
+ updatedAt: row?.updated_at ? String(row.updated_at) : null,
169
+ };
170
+ }
171
+ rowToMcpOAuthConnectorGrantRecord(row) {
172
+ let scopes = [];
173
+ try {
174
+ scopes = this.normalizeMcpAccessTokenScopes(JSON.parse(String(row?.scopes_json || '[]')));
175
+ }
176
+ catch {
177
+ scopes = [];
178
+ }
179
+ return {
180
+ id: String(row?.id || ''),
181
+ provider: this.normalizeMcpOAuthProvider(row?.provider),
182
+ clientId: String(row?.client_id || ''),
183
+ clientName: String(row?.client_name || ''),
184
+ workspaceId: String(row?.workspace_id || ''),
185
+ userId: String(row?.user_id || ''),
186
+ scopes,
187
+ tokenPrefix: String(row?.token_prefix || ''),
188
+ status: this.resolveMcpOAuthGrantStatus(row),
189
+ lastUsedAt: row?.last_used_at ? String(row.last_used_at) : null,
190
+ revokedAt: row?.revoked_at ? String(row.revoked_at) : null,
191
+ revokedByUserId: row?.revoked_by_user_id ? String(row.revoked_by_user_id) : null,
192
+ createdAt: String(row?.created_at || ''),
193
+ updatedAt: row?.updated_at ? String(row.updated_at) : null,
194
+ };
195
+ }
196
+ getActiveWorkspaceMemberForMcp(userId, workspaceId) {
197
+ const normalizedUserId = String(userId || '').trim();
198
+ const normalizedWorkspaceId = String(workspaceId || '').trim();
199
+ if (!normalizedUserId) {
200
+ throw new TaskforceRuleError('userId is required', 400, 'USER_ID_REQUIRED');
201
+ }
202
+ if (!normalizedWorkspaceId || normalizedWorkspaceId.toLowerCase() === 'default') {
203
+ throw new TaskforceRuleError('A non-default workspaceId is required', 400, 'WORKSPACE_ID_REQUIRED');
204
+ }
205
+ const row = this.db.prepare(`
206
+ SELECT
207
+ u.id AS user_id,
208
+ u.email,
209
+ u.display_name,
210
+ COALESCE(u.is_disabled, 0) AS is_disabled,
211
+ m.workspace_id,
212
+ COALESCE(m.role, 'member') AS role,
213
+ COALESCE(m.permission_mode, 'read-write') AS permission_mode,
214
+ COALESCE(m.status, 'active') AS membership_status
215
+ FROM workspace_memberships m
216
+ JOIN users u ON u.tenant_id = m.tenant_id AND u.id = m.user_id
217
+ WHERE m.tenant_id = ?
218
+ AND m.workspace_id = ?
219
+ AND m.user_id = ?
220
+ LIMIT 1
221
+ `).get(this.tenantId, normalizedWorkspaceId, normalizedUserId);
222
+ if (!row?.user_id || !row.workspace_id || !row.email) {
223
+ throw new TaskforceRuleError('Workspace membership not found', 404, 'WORKSPACE_MEMBERSHIP_NOT_FOUND');
224
+ }
225
+ if (Boolean(row.is_disabled)) {
226
+ throw new TaskforceRuleError('User is disabled', 403, 'USER_DISABLED');
227
+ }
228
+ if (String(row.membership_status || 'active').toLowerCase() !== 'active') {
229
+ throw new TaskforceRuleError('Workspace membership is not active', 403, 'WORKSPACE_MEMBERSHIP_INACTIVE');
230
+ }
231
+ return {
232
+ userId: row.user_id,
233
+ workspaceId: row.workspace_id,
234
+ email: row.email,
235
+ displayName: row.display_name || null,
236
+ role: this.normalizeAccessRole(row.role || 'member', 'member'),
237
+ permissionMode: this.normalizePermissionMode(row.permission_mode, 'read-write'),
238
+ disabled: Boolean(row.is_disabled),
239
+ };
240
+ }
241
+ isMcpPrincipalActive(userId, workspaceId) {
242
+ try {
243
+ this.getActiveWorkspaceMemberForMcp(userId, workspaceId);
244
+ }
245
+ catch {
246
+ return false;
247
+ }
248
+ try {
249
+ const accountAccess = this.deps.getAccountAccessStatus(userId);
250
+ return accountAccess.canAccessApp;
251
+ }
252
+ catch {
253
+ return false;
254
+ }
255
+ }
256
+ getMcpAccessTokenRow(tokenId, workspaceId) {
257
+ const normalizedTokenId = String(tokenId || '').trim();
258
+ const normalizedWorkspaceId = String(workspaceId || '').trim();
259
+ if (!normalizedTokenId || !normalizedWorkspaceId)
260
+ return null;
261
+ const row = this.db.prepare(`
262
+ SELECT
263
+ t.id,
264
+ t.workspace_id,
265
+ t.user_id,
266
+ u.email AS user_email,
267
+ t.name,
268
+ t.scopes_json,
269
+ t.token_prefix,
270
+ t.token_hash,
271
+ t.status,
272
+ t.last_used_at,
273
+ t.last_used_ip,
274
+ t.last_used_user_agent,
275
+ t.created_by_ip,
276
+ t.expires_at,
277
+ t.revoked_at,
278
+ t.revoked_by_user_id,
279
+ t.created_at,
280
+ t.updated_at
281
+ FROM mcp_access_tokens t
282
+ LEFT JOIN users u ON u.tenant_id = t.tenant_id AND u.id = t.user_id
283
+ WHERE t.tenant_id = ?
284
+ AND t.workspace_id = ?
285
+ AND t.id = ?
286
+ LIMIT 1
287
+ `).get(this.tenantId, normalizedWorkspaceId, normalizedTokenId);
288
+ return row ? row : null;
289
+ }
290
+ resolveMcpAccessTokenStatus(row) {
291
+ const now = new Date().toISOString();
292
+ if (row.revoked_at || String(row.status || '').toLowerCase() === 'revoked')
293
+ return 'revoked';
294
+ if (row.expires_at && String(row.expires_at) < now)
295
+ return 'expired';
296
+ return 'active';
297
+ }
298
+ rowToMcpAccessTokenRecord(row) {
299
+ let scopes = [];
300
+ try {
301
+ const parsed = JSON.parse(String(row?.scopes_json || '[]'));
302
+ scopes = this.normalizeMcpAccessTokenScopes(parsed);
303
+ }
304
+ catch {
305
+ scopes = [];
306
+ }
307
+ return {
308
+ id: String(row.id || ''),
309
+ workspaceId: String(row.workspace_id || ''),
310
+ userId: String(row.user_id || ''),
311
+ userEmail: row.user_email ? String(row.user_email) : null,
312
+ name: String(row.name || ''),
313
+ scopes,
314
+ tokenPrefix: String(row.token_prefix || ''),
315
+ status: this.resolveMcpAccessTokenStatus(row),
316
+ lastUsedAt: row.last_used_at ? String(row.last_used_at) : null,
317
+ lastUsedIp: row.last_used_ip ? String(row.last_used_ip) : null,
318
+ lastUsedUserAgent: row.last_used_user_agent ? String(row.last_used_user_agent) : null,
319
+ createdByIp: row.created_by_ip ? String(row.created_by_ip) : null,
320
+ expiresAt: row.expires_at ? String(row.expires_at) : null,
321
+ revokedAt: row.revoked_at ? String(row.revoked_at) : null,
322
+ revokedByUserId: row.revoked_by_user_id ? String(row.revoked_by_user_id) : null,
323
+ createdAt: String(row.created_at || ''),
324
+ updatedAt: row.updated_at ? String(row.updated_at) : null,
325
+ };
326
+ }
327
+ assertCanManageOwnMcpAccessToken(actorUserId, token) {
328
+ const normalizedActorUserId = String(actorUserId || '').trim();
329
+ if (!normalizedActorUserId) {
330
+ throw new TaskforceRuleError('userId is required', 400, 'USER_ID_REQUIRED');
331
+ }
332
+ if (String(token.user_id || '').trim() !== normalizedActorUserId) {
333
+ throw new TaskforceRuleError('Forbidden', 403, 'FORBIDDEN');
334
+ }
335
+ }
336
+ countActiveMcpAccessTokens(userId, workspaceId) {
337
+ const now = new Date().toISOString();
338
+ const row = this.db.prepare(`
339
+ SELECT COUNT(*) AS token_count
340
+ FROM mcp_access_tokens
341
+ WHERE tenant_id = ?
342
+ AND workspace_id = ?
343
+ AND user_id = ?
344
+ AND status = 'active'
345
+ AND revoked_at IS NULL
346
+ AND (expires_at IS NULL OR expires_at >= ?)
347
+ `).get(this.tenantId, workspaceId, userId, now);
348
+ return Number(row?.token_count || 0);
349
+ }
350
+ // ─── Credential revocation (called from workspace/membership operations) ─
351
+ revokeMcpCredentialsForUserWorkspace(userId, workspaceId, revokedByUserId) {
352
+ const normalizedUserId = String(userId || '').trim();
353
+ const normalizedWorkspaceId = String(workspaceId || '').trim();
354
+ if (!normalizedUserId || !normalizedWorkspaceId)
355
+ return;
356
+ const now = new Date().toISOString();
357
+ const revokedBy = revokedByUserId ? String(revokedByUserId).trim() || null : null;
358
+ this.db.prepare(`
359
+ UPDATE mcp_access_tokens
360
+ SET status = 'revoked',
361
+ revoked_at = COALESCE(revoked_at, ?),
362
+ revoked_by_user_id = COALESCE(revoked_by_user_id, ?),
363
+ updated_at = ?
364
+ WHERE tenant_id = ?
365
+ AND workspace_id = ?
366
+ AND user_id = ?
367
+ AND COALESCE(status, 'active') = 'active'
368
+ `).run(now, revokedBy, now, this.tenantId, normalizedWorkspaceId, normalizedUserId);
369
+ this.db.prepare(`
370
+ UPDATE mcp_oauth_connector_grants
371
+ SET status = 'revoked',
372
+ revoked_at = COALESCE(revoked_at, ?),
373
+ revoked_by_user_id = COALESCE(revoked_by_user_id, ?),
374
+ updated_at = ?
375
+ WHERE tenant_id = ?
376
+ AND workspace_id = ?
377
+ AND user_id = ?
378
+ AND COALESCE(status, 'active') = 'active'
379
+ `).run(now, revokedBy, now, this.tenantId, normalizedWorkspaceId, normalizedUserId);
380
+ }
381
+ revokeMcpCredentialsForWorkspace(workspaceId, revokedByUserId) {
382
+ const normalizedWorkspaceId = String(workspaceId || '').trim();
383
+ if (!normalizedWorkspaceId)
384
+ return;
385
+ const now = new Date().toISOString();
386
+ const revokedBy = revokedByUserId ? String(revokedByUserId).trim() || null : null;
387
+ this.db.prepare(`
388
+ UPDATE mcp_access_tokens
389
+ SET status = 'revoked',
390
+ revoked_at = COALESCE(revoked_at, ?),
391
+ revoked_by_user_id = COALESCE(revoked_by_user_id, ?),
392
+ updated_at = ?
393
+ WHERE tenant_id = ?
394
+ AND workspace_id = ?
395
+ AND COALESCE(status, 'active') = 'active'
396
+ `).run(now, revokedBy, now, this.tenantId, normalizedWorkspaceId);
397
+ this.db.prepare(`
398
+ UPDATE mcp_oauth_connector_grants
399
+ SET status = 'revoked',
400
+ revoked_at = COALESCE(revoked_at, ?),
401
+ revoked_by_user_id = COALESCE(revoked_by_user_id, ?),
402
+ updated_at = ?
403
+ WHERE tenant_id = ?
404
+ AND workspace_id = ?
405
+ AND COALESCE(status, 'active') = 'active'
406
+ `).run(now, revokedBy, now, this.tenantId, normalizedWorkspaceId);
407
+ }
408
+ // ─── Public methods — MCP access tokens ──────────────────────────────────
409
+ listMcpAccessTokens(input) {
410
+ const member = this.getActiveWorkspaceMemberForMcp(input.userId, input.workspaceId);
411
+ const page = Math.max(1, Math.floor(Number(input.page) || 1));
412
+ const pageSize = Math.max(1, Math.min(MCP_ACCESS_TOKEN_MAX_PAGE_SIZE, Math.floor(Number(input.pageSize) || MCP_ACCESS_TOKEN_DEFAULT_PAGE_SIZE)));
413
+ const offset = (page - 1) * pageSize;
414
+ const totalRow = this.db.prepare(`
415
+ SELECT COUNT(*) AS total
416
+ FROM mcp_access_tokens
417
+ WHERE tenant_id = ?
418
+ AND workspace_id = ?
419
+ AND user_id = ?
420
+ `).get(this.tenantId, member.workspaceId, member.userId);
421
+ const rows = this.db.prepare(`
422
+ SELECT
423
+ t.id,
424
+ t.workspace_id,
425
+ t.user_id,
426
+ u.email AS user_email,
427
+ t.name,
428
+ t.scopes_json,
429
+ t.token_prefix,
430
+ t.token_hash,
431
+ t.status,
432
+ t.last_used_at,
433
+ t.last_used_ip,
434
+ t.last_used_user_agent,
435
+ t.created_by_ip,
436
+ t.expires_at,
437
+ t.revoked_at,
438
+ t.revoked_by_user_id,
439
+ t.created_at,
440
+ t.updated_at
441
+ FROM mcp_access_tokens t
442
+ LEFT JOIN users u ON u.tenant_id = t.tenant_id AND u.id = t.user_id
443
+ WHERE t.tenant_id = ?
444
+ AND t.workspace_id = ?
445
+ AND t.user_id = ?
446
+ ORDER BY t.created_at DESC
447
+ LIMIT ? OFFSET ?
448
+ `).all(this.tenantId, member.workspaceId, member.userId, pageSize, offset);
449
+ return {
450
+ items: rows.map((row) => this.rowToMcpAccessTokenRecord(row)),
451
+ total: Number(totalRow?.total || 0),
452
+ page,
453
+ pageSize,
454
+ };
455
+ }
456
+ createMcpAccessToken(input) {
457
+ const member = this.getActiveWorkspaceMemberForMcp(input.userId, input.workspaceId);
458
+ const name = String(input.name || '').trim();
459
+ if (!name) {
460
+ throw new TaskforceRuleError('Token name is required', 400, 'MCP_TOKEN_NAME_REQUIRED');
461
+ }
462
+ const scopes = this.normalizeMcpAccessTokenScopes(input.scopes);
463
+ const expiresAt = input.expiresAt ? String(input.expiresAt).trim() : null;
464
+ const now = new Date().toISOString();
465
+ if (expiresAt && expiresAt <= now) {
466
+ throw new TaskforceRuleError('expiresAt must be in the future', 400, 'MCP_TOKEN_EXPIRY_INVALID');
467
+ }
468
+ if (this.countActiveMcpAccessTokens(member.userId, member.workspaceId) >= MCP_ACCESS_TOKEN_MAX_ACTIVE_PER_USER_WORKSPACE) {
469
+ throw new TaskforceRuleError(`Maximum ${MCP_ACCESS_TOKEN_MAX_ACTIVE_PER_USER_WORKSPACE} active MCP tokens allowed per user per workspace`, 400, 'MCP_TOKEN_LIMIT_REACHED');
470
+ }
471
+ const tokenId = this.generateEntityId('mcp-token');
472
+ let tokenPrefix = this.createMcpAccessTokenPrefix();
473
+ while (this.db.prepare(`
474
+ SELECT 1 FROM mcp_access_tokens WHERE tenant_id = ? AND token_prefix = ? LIMIT 1
475
+ `).get(this.tenantId, tokenPrefix)) {
476
+ tokenPrefix = this.createMcpAccessTokenPrefix();
477
+ }
478
+ const secret = randomBytes(32).toString('base64url');
479
+ const token = `${tokenPrefix}_${secret}`;
480
+ const tokenHash = hashMcpAccessTokenSecret(secret);
481
+ const createdByIp = input.createdByIp ? String(input.createdByIp).trim() : null;
482
+ this.db.prepare(`
483
+ INSERT INTO mcp_access_tokens (
484
+ id, tenant_id, workspace_id, user_id, name, scopes_json, token_prefix, token_hash, status,
485
+ last_used_at, last_used_ip, last_used_user_agent, created_by_ip, expires_at, revoked_at,
486
+ revoked_by_user_id, created_at, updated_at
487
+ )
488
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'active', NULL, NULL, NULL, ?, ?, NULL, NULL, ?, ?)
489
+ `).run(tokenId, this.tenantId, member.workspaceId, member.userId, name, JSON.stringify(scopes), tokenPrefix, tokenHash, createdByIp, expiresAt, now, now);
490
+ const row = this.getMcpAccessTokenRow(tokenId, member.workspaceId);
491
+ if (!row) {
492
+ throw new TaskforceRuleError('Failed to create MCP token', 500, 'MCP_TOKEN_CREATE_FAILED');
493
+ }
494
+ return {
495
+ token,
496
+ record: this.rowToMcpAccessTokenRecord(row),
497
+ };
498
+ }
499
+ revokeMcpAccessToken(input) {
500
+ this.getActiveWorkspaceMemberForMcp(input.actorUserId, input.workspaceId);
501
+ const row = this.getMcpAccessTokenRow(input.tokenId, input.workspaceId);
502
+ if (!row) {
503
+ throw new TaskforceRuleError('MCP token not found', 404, 'MCP_TOKEN_NOT_FOUND');
504
+ }
505
+ this.assertCanManageOwnMcpAccessToken(input.actorUserId, row);
506
+ const now = new Date().toISOString();
507
+ this.db.prepare(`
508
+ UPDATE mcp_access_tokens
509
+ SET status = 'revoked',
510
+ revoked_at = COALESCE(revoked_at, ?),
511
+ revoked_by_user_id = COALESCE(revoked_by_user_id, ?),
512
+ updated_at = ?
513
+ WHERE tenant_id = ? AND id = ?
514
+ `).run(now, input.actorUserId, now, this.tenantId, row.id);
515
+ const updated = this.getMcpAccessTokenRow(row.id, input.workspaceId);
516
+ if (!updated) {
517
+ throw new TaskforceRuleError('MCP token not found', 404, 'MCP_TOKEN_NOT_FOUND');
518
+ }
519
+ return this.rowToMcpAccessTokenRecord(updated);
520
+ }
521
+ regenerateMcpAccessToken(input) {
522
+ this.getActiveWorkspaceMemberForMcp(input.actorUserId, input.workspaceId);
523
+ const existing = this.getMcpAccessTokenRow(input.tokenId, input.workspaceId);
524
+ if (!existing) {
525
+ throw new TaskforceRuleError('MCP token not found', 404, 'MCP_TOKEN_NOT_FOUND');
526
+ }
527
+ this.assertCanManageOwnMcpAccessToken(input.actorUserId, existing);
528
+ const tx = this.db.transaction(() => {
529
+ const revoked = this.revokeMcpAccessToken({
530
+ actorUserId: input.actorUserId,
531
+ workspaceId: input.workspaceId,
532
+ tokenId: input.tokenId,
533
+ });
534
+ return this.createMcpAccessToken({
535
+ userId: revoked.userId,
536
+ workspaceId: revoked.workspaceId,
537
+ name: revoked.name,
538
+ scopes: revoked.scopes,
539
+ expiresAt: revoked.expiresAt || null,
540
+ createdByIp: input.createdByIp || null,
541
+ });
542
+ });
543
+ return tx();
544
+ }
545
+ authenticateMcpAccessToken(token, workspaceId) {
546
+ const parsed = this.parseMcpAccessToken(token);
547
+ if (!parsed)
548
+ return null;
549
+ const row = this.db.prepare(`
550
+ SELECT id, workspace_id, user_id, scopes_json, token_hash, status, expires_at, revoked_at
551
+ FROM mcp_access_tokens
552
+ WHERE tenant_id = ?
553
+ AND token_prefix = ?
554
+ LIMIT 1
555
+ `).get(this.tenantId, parsed.tokenPrefix);
556
+ if (!row?.id || !row.workspace_id || !row.user_id || !row.token_hash)
557
+ return null;
558
+ if (workspaceId && String(workspaceId).trim() && String(row.workspace_id).trim() !== String(workspaceId).trim()) {
559
+ return null;
560
+ }
561
+ if (!verifyMcpAccessTokenSecret(parsed.secret, row.token_hash))
562
+ return null;
563
+ if (!this.isMcpPrincipalActive(row.user_id, row.workspace_id))
564
+ return null;
565
+ const status = this.resolveMcpAccessTokenStatus(row);
566
+ let scopes = [];
567
+ try {
568
+ scopes = this.normalizeMcpAccessTokenScopes(JSON.parse(String(row.scopes_json || '[]')));
569
+ }
570
+ catch {
571
+ return null;
572
+ }
573
+ return {
574
+ tokenId: row.id,
575
+ workspaceId: row.workspace_id,
576
+ userId: row.user_id,
577
+ scopes,
578
+ status,
579
+ };
580
+ }
581
+ inspectMcpAccessToken(token) {
582
+ const parsed = this.parseMcpAccessToken(token);
583
+ if (!parsed)
584
+ return null;
585
+ const row = this.db.prepare(`
586
+ SELECT id, workspace_id, user_id, name, scopes_json, token_hash, status, expires_at, revoked_at
587
+ FROM mcp_access_tokens
588
+ WHERE tenant_id = ?
589
+ AND token_prefix = ?
590
+ LIMIT 1
591
+ `).get(this.tenantId, parsed.tokenPrefix);
592
+ if (!row?.id || !row.workspace_id || !row.user_id || !row.token_hash)
593
+ return null;
594
+ if (!verifyMcpAccessTokenSecret(parsed.secret, row.token_hash))
595
+ return null;
596
+ let scopes = [];
597
+ try {
598
+ scopes = this.normalizeMcpAccessTokenScopes(JSON.parse(String(row.scopes_json || '[]')));
599
+ }
600
+ catch {
601
+ return null;
602
+ }
603
+ return {
604
+ tokenId: row.id,
605
+ tokenPrefix: parsed.tokenPrefix,
606
+ workspaceId: row.workspace_id,
607
+ userId: row.user_id,
608
+ name: String(row.name || ''),
609
+ scopes,
610
+ status: this.resolveMcpAccessTokenStatus(row),
611
+ };
612
+ }
613
+ recordMcpAccessTokenUsage(input) {
614
+ const tokenId = String(input.tokenId || '').trim();
615
+ const workspaceId = String(input.workspaceId || '').trim();
616
+ if (!tokenId || !workspaceId)
617
+ return false;
618
+ const now = new Date().toISOString();
619
+ const result = this.db.prepare(`
620
+ UPDATE mcp_access_tokens
621
+ SET last_used_at = ?,
622
+ last_used_ip = ?,
623
+ last_used_user_agent = ?,
624
+ updated_at = ?
625
+ WHERE tenant_id = ? AND workspace_id = ? AND id = ?
626
+ `).run(now, input.ip ? String(input.ip).trim() : null, input.userAgent ? String(input.userAgent).trim() : null, now, this.tenantId, workspaceId, tokenId);
627
+ return Number(result?.changes || 0) > 0;
628
+ }
629
+ // ─── Public methods — MCP OAuth ───────────────────────────────────────────
630
+ registerMcpOAuthClient(input) {
631
+ const provider = this.normalizeMcpOAuthProvider(input.provider);
632
+ const clientName = String(input.clientName || '').trim() || this.defaultMcpOAuthClientName(provider);
633
+ const redirectUris = this.normalizeRedirectUris(input.redirectUris);
634
+ const now = new Date().toISOString();
635
+ const clientId = this.generateEntityId(`mcp-oauth-client-${provider}`);
636
+ this.db.prepare(`
637
+ INSERT INTO mcp_oauth_clients (
638
+ id, tenant_id, provider, client_name, redirect_uris_json, status, created_at, updated_at
639
+ )
640
+ VALUES (?, ?, ?, ?, ?, 'active', ?, ?)
641
+ `).run(clientId, this.tenantId, provider, clientName, JSON.stringify(redirectUris), now, now);
642
+ const row = this.getMcpOAuthClientRow(clientId, provider);
643
+ if (!row) {
644
+ throw new TaskforceRuleError('Failed to register MCP OAuth client', 500, 'MCP_OAUTH_CLIENT_CREATE_FAILED');
645
+ }
646
+ return this.rowToMcpOAuthClientRecord(row);
647
+ }
648
+ getMcpOAuthClient(clientId, provider) {
649
+ const normalizedProvider = provider ? this.normalizeMcpOAuthProvider(provider) : undefined;
650
+ const row = this.getMcpOAuthClientRow(clientId, normalizedProvider);
651
+ return row ? this.rowToMcpOAuthClientRecord(row) : null;
652
+ }
653
+ issueMcpOAuthAuthorizationCode(input) {
654
+ const provider = this.normalizeMcpOAuthProvider(input.provider);
655
+ const client = this.getMcpOAuthClientRow(input.clientId, provider);
656
+ if (!client) {
657
+ throw new TaskforceRuleError('OAuth client not found', 404, 'MCP_OAUTH_CLIENT_NOT_FOUND');
658
+ }
659
+ if (String(client.status || '').trim().toLowerCase() === 'revoked') {
660
+ throw new TaskforceRuleError('OAuth client is revoked', 403, 'MCP_OAUTH_CLIENT_REVOKED');
661
+ }
662
+ const member = this.getActiveWorkspaceMemberForMcp(input.userId, input.workspaceId);
663
+ const redirectUri = String(input.redirectUri || '').trim();
664
+ const redirectUris = this.rowToMcpOAuthClientRecord(client).redirectUris;
665
+ if (!redirectUri || !redirectUris.includes(redirectUri)) {
666
+ throw new TaskforceRuleError('Redirect URI is not registered for this OAuth client', 400, 'MCP_OAUTH_REDIRECT_URI_INVALID');
667
+ }
668
+ const scopes = this.normalizeMcpAccessTokenScopes(input.scopes);
669
+ const codeChallenge = String(input.codeChallenge || '').trim();
670
+ if (!codeChallenge) {
671
+ throw new TaskforceRuleError('PKCE code challenge is required', 400, 'MCP_OAUTH_CODE_CHALLENGE_REQUIRED');
672
+ }
673
+ const codeChallengeMethod = this.normalizePkceMethod(input.codeChallengeMethod);
674
+ const codeId = this.generateEntityId(`mcp-oauth-code-${provider}`);
675
+ let codePrefix = this.createMcpOAuthCodePrefix();
676
+ while (this.db.prepare(`
677
+ SELECT 1 FROM mcp_oauth_auth_codes WHERE tenant_id = ? AND code_prefix = ? LIMIT 1
678
+ `).get(this.tenantId, codePrefix)) {
679
+ codePrefix = this.createMcpOAuthCodePrefix();
680
+ }
681
+ const secret = randomBytes(32).toString('base64url');
682
+ const code = `${codePrefix}_${secret}`;
683
+ const codeHash = hashMcpOAuthSecret(secret);
684
+ const now = new Date();
685
+ const expiresAt = new Date(now.getTime() + MCP_OAUTH_AUTH_CODE_TTL_MS).toISOString();
686
+ this.db.prepare(`
687
+ INSERT INTO mcp_oauth_auth_codes (
688
+ id, tenant_id, provider, client_id, user_id, workspace_id, redirect_uri, scopes_json,
689
+ code_prefix, code_hash, code_challenge, code_challenge_method, expires_at, used_at, created_at
690
+ )
691
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?)
692
+ `).run(codeId, this.tenantId, provider, String(client.id || ''), member.userId, member.workspaceId, redirectUri, JSON.stringify(scopes), codePrefix, codeHash, codeChallenge, codeChallengeMethod, expiresAt, now.toISOString());
693
+ return { code, expiresAt };
694
+ }
695
+ exchangeMcpOAuthAuthorizationCode(input) {
696
+ const provider = this.normalizeMcpOAuthProvider(input.provider);
697
+ const client = this.getMcpOAuthClientRow(input.clientId, provider);
698
+ if (!client) {
699
+ throw new TaskforceRuleError('OAuth client not found', 404, 'MCP_OAUTH_CLIENT_NOT_FOUND');
700
+ }
701
+ const parsed = this.parseMcpOAuthOpaqueSecret(input.code, 'tfmcpoauthcode_');
702
+ if (!parsed) {
703
+ throw new TaskforceRuleError('Invalid authorization code', 400, 'MCP_OAUTH_CODE_INVALID');
704
+ }
705
+ const row = this.db.prepare(`
706
+ SELECT id, client_id, user_id, workspace_id, redirect_uri, scopes_json, code_hash, code_challenge, code_challenge_method, expires_at, used_at
707
+ FROM mcp_oauth_auth_codes
708
+ WHERE tenant_id = ? AND provider = ? AND code_prefix = ?
709
+ LIMIT 1
710
+ `).get(this.tenantId, provider, parsed.tokenPrefix);
711
+ if (!row?.id || !row.client_id || !row.user_id || !row.workspace_id || !row.code_hash) {
712
+ throw new TaskforceRuleError('Invalid authorization code', 400, 'MCP_OAUTH_CODE_INVALID');
713
+ }
714
+ if (String(row.client_id) !== String(client.id)) {
715
+ throw new TaskforceRuleError('OAuth client mismatch', 400, 'MCP_OAUTH_CLIENT_MISMATCH');
716
+ }
717
+ if (row.used_at) {
718
+ throw new TaskforceRuleError('Authorization code already used', 400, 'MCP_OAUTH_CODE_ALREADY_USED');
719
+ }
720
+ const now = new Date().toISOString();
721
+ if (!row.expires_at || String(row.expires_at) < now) {
722
+ throw new TaskforceRuleError('Authorization code expired', 400, 'MCP_OAUTH_CODE_EXPIRED');
723
+ }
724
+ if (String(row.redirect_uri || '').trim() !== String(input.redirectUri || '').trim()) {
725
+ throw new TaskforceRuleError('Redirect URI mismatch', 400, 'MCP_OAUTH_REDIRECT_URI_INVALID');
726
+ }
727
+ if (!verifyMcpOAuthSecret(parsed.secret, String(row.code_hash))) {
728
+ throw new TaskforceRuleError('Invalid authorization code', 400, 'MCP_OAUTH_CODE_INVALID');
729
+ }
730
+ const codeVerifier = String(input.codeVerifier || '').trim();
731
+ if (!codeVerifier) {
732
+ throw new TaskforceRuleError('PKCE code verifier is required', 400, 'MCP_OAUTH_CODE_VERIFIER_REQUIRED');
733
+ }
734
+ const derivedChallenge = createHash('sha256').update(codeVerifier).digest('base64url');
735
+ if (String(row.code_challenge_method || 'S256').trim().toUpperCase() !== 'S256' || derivedChallenge !== String(row.code_challenge || '')) {
736
+ throw new TaskforceRuleError('PKCE verification failed', 400, 'MCP_OAUTH_PKCE_VERIFICATION_FAILED');
737
+ }
738
+ let scopes = [];
739
+ try {
740
+ scopes = this.normalizeMcpAccessTokenScopes(JSON.parse(String(row.scopes_json || '[]')));
741
+ }
742
+ catch {
743
+ throw new TaskforceRuleError('Authorization code scopes are invalid', 400, 'MCP_OAUTH_SCOPE_INVALID');
744
+ }
745
+ const clientName = String(client.client_name || this.defaultMcpOAuthClientName(provider)).trim() || this.defaultMcpOAuthClientName(provider);
746
+ const tx = this.db.transaction(() => {
747
+ const existingRows = this.db.prepare(`
748
+ SELECT id
749
+ FROM mcp_oauth_connector_grants
750
+ WHERE tenant_id = ?
751
+ AND provider = ?
752
+ AND user_id = ?
753
+ AND workspace_id = ?
754
+ AND status = 'active'
755
+ `).all(this.tenantId, provider, String(row.user_id), String(row.workspace_id));
756
+ const revokeTime = new Date().toISOString();
757
+ existingRows.forEach((existing) => {
758
+ if (!existing?.id)
759
+ return;
760
+ this.db.prepare(`
761
+ UPDATE mcp_oauth_connector_grants
762
+ SET status = 'revoked',
763
+ revoked_at = COALESCE(revoked_at, ?),
764
+ revoked_by_user_id = COALESCE(revoked_by_user_id, ?),
765
+ updated_at = ?
766
+ WHERE tenant_id = ? AND id = ?
767
+ `).run(revokeTime, String(row.user_id), revokeTime, this.tenantId, String(existing.id));
768
+ });
769
+ const grantId = this.generateEntityId(`mcp-oauth-grant-${provider}`);
770
+ let tokenPrefix = this.createMcpOAuthAccessTokenPrefix();
771
+ while (this.db.prepare(`
772
+ SELECT 1 FROM mcp_oauth_connector_grants WHERE tenant_id = ? AND token_prefix = ? LIMIT 1
773
+ `).get(this.tenantId, tokenPrefix)) {
774
+ tokenPrefix = this.createMcpOAuthAccessTokenPrefix();
775
+ }
776
+ const secret = randomBytes(32).toString('base64url');
777
+ const accessToken = `${tokenPrefix}_${secret}`;
778
+ const tokenHash = hashMcpOAuthSecret(secret);
779
+ this.db.prepare(`
780
+ INSERT INTO mcp_oauth_connector_grants (
781
+ id, tenant_id, provider, client_id, client_name, user_id, workspace_id, scopes_json,
782
+ token_prefix, token_hash, status, last_used_at, revoked_at, revoked_by_user_id, created_at, updated_at
783
+ )
784
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', NULL, NULL, NULL, ?, ?)
785
+ `).run(grantId, this.tenantId, provider, String(client.id || ''), clientName, String(row.user_id), String(row.workspace_id), JSON.stringify(scopes), tokenPrefix, tokenHash, now, now);
786
+ this.db.prepare(`
787
+ UPDATE mcp_oauth_auth_codes
788
+ SET used_at = ?
789
+ WHERE tenant_id = ? AND id = ?
790
+ `).run(now, this.tenantId, String(row.id));
791
+ const grantRow = this.db.prepare(`
792
+ SELECT *
793
+ FROM mcp_oauth_connector_grants
794
+ WHERE tenant_id = ? AND id = ?
795
+ LIMIT 1
796
+ `).get(this.tenantId, grantId);
797
+ if (!grantRow) {
798
+ throw new TaskforceRuleError('Failed to create OAuth connector grant', 500, 'MCP_OAUTH_GRANT_CREATE_FAILED');
799
+ }
800
+ return {
801
+ accessToken,
802
+ grant: this.rowToMcpOAuthConnectorGrantRecord(grantRow),
803
+ };
804
+ });
805
+ const result = tx();
806
+ return {
807
+ accessToken: result.accessToken,
808
+ tokenType: 'Bearer',
809
+ scopes,
810
+ grant: result.grant,
811
+ };
812
+ }
813
+ listMcpOAuthConnectorGrants(input) {
814
+ const member = this.getActiveWorkspaceMemberForMcp(input.userId, input.workspaceId);
815
+ const provider = input.provider ? this.normalizeMcpOAuthProvider(input.provider) : null;
816
+ const rows = provider
817
+ ? this.db.prepare(`
818
+ SELECT *
819
+ FROM mcp_oauth_connector_grants
820
+ WHERE tenant_id = ?
821
+ AND workspace_id = ?
822
+ AND user_id = ?
823
+ AND provider = ?
824
+ ORDER BY created_at DESC
825
+ `).all(this.tenantId, member.workspaceId, member.userId, provider)
826
+ : this.db.prepare(`
827
+ SELECT *
828
+ FROM mcp_oauth_connector_grants
829
+ WHERE tenant_id = ?
830
+ AND workspace_id = ?
831
+ AND user_id = ?
832
+ ORDER BY created_at DESC
833
+ `).all(this.tenantId, member.workspaceId, member.userId);
834
+ return rows.map((row) => this.rowToMcpOAuthConnectorGrantRecord(row));
835
+ }
836
+ revokeMcpOAuthConnectorGrant(input) {
837
+ const member = this.getActiveWorkspaceMemberForMcp(input.actorUserId, input.workspaceId);
838
+ const row = this.db.prepare(`
839
+ SELECT *
840
+ FROM mcp_oauth_connector_grants
841
+ WHERE tenant_id = ? AND workspace_id = ? AND id = ?
842
+ LIMIT 1
843
+ `).get(this.tenantId, member.workspaceId, String(input.grantId || '').trim());
844
+ if (!row?.id) {
845
+ throw new TaskforceRuleError('MCP connector grant not found', 404, 'MCP_OAUTH_GRANT_NOT_FOUND');
846
+ }
847
+ if (String(row.user_id || '').trim() !== member.userId) {
848
+ throw new TaskforceRuleError('Forbidden', 403, 'FORBIDDEN');
849
+ }
850
+ const now = new Date().toISOString();
851
+ this.db.prepare(`
852
+ UPDATE mcp_oauth_connector_grants
853
+ SET status = 'revoked',
854
+ revoked_at = COALESCE(revoked_at, ?),
855
+ revoked_by_user_id = COALESCE(revoked_by_user_id, ?),
856
+ updated_at = ?
857
+ WHERE tenant_id = ? AND id = ?
858
+ `).run(now, member.userId, now, this.tenantId, String(row.id));
859
+ const updated = this.db.prepare(`
860
+ SELECT *
861
+ FROM mcp_oauth_connector_grants
862
+ WHERE tenant_id = ? AND id = ?
863
+ LIMIT 1
864
+ `).get(this.tenantId, String(row.id));
865
+ if (!updated) {
866
+ throw new TaskforceRuleError('MCP connector grant not found', 404, 'MCP_OAUTH_GRANT_NOT_FOUND');
867
+ }
868
+ return this.rowToMcpOAuthConnectorGrantRecord(updated);
869
+ }
870
+ authenticateMcpOAuthAccessToken(token, workspaceId) {
871
+ const parsed = this.parseMcpOAuthOpaqueSecret(token, 'tfmcpoauth_');
872
+ if (!parsed)
873
+ return null;
874
+ const row = this.db.prepare(`
875
+ SELECT id, provider, client_id, user_id, workspace_id, scopes_json, token_hash, status, revoked_at
876
+ FROM mcp_oauth_connector_grants
877
+ WHERE tenant_id = ?
878
+ AND token_prefix = ?
879
+ LIMIT 1
880
+ `).get(this.tenantId, parsed.tokenPrefix);
881
+ if (!row?.id || !row?.workspace_id || !row?.user_id || !row?.client_id || !row?.token_hash)
882
+ return null;
883
+ if (workspaceId && String(workspaceId).trim() && String(row.workspace_id).trim() !== String(workspaceId).trim()) {
884
+ return null;
885
+ }
886
+ if (!verifyMcpOAuthSecret(parsed.secret, String(row.token_hash)))
887
+ return null;
888
+ if (!this.isMcpPrincipalActive(String(row.user_id), String(row.workspace_id)))
889
+ return null;
890
+ let scopes = [];
891
+ try {
892
+ scopes = this.normalizeMcpAccessTokenScopes(JSON.parse(String(row.scopes_json || '[]')));
893
+ }
894
+ catch {
895
+ return null;
896
+ }
897
+ return {
898
+ grantId: String(row.id),
899
+ workspaceId: String(row.workspace_id),
900
+ userId: String(row.user_id),
901
+ clientId: String(row.client_id),
902
+ provider: this.normalizeMcpOAuthProvider(row.provider),
903
+ scopes,
904
+ status: this.resolveMcpOAuthGrantStatus(row),
905
+ };
906
+ }
907
+ recordMcpOAuthConnectorGrantUsage(input) {
908
+ const grantId = String(input.grantId || '').trim();
909
+ const workspaceId = String(input.workspaceId || '').trim();
910
+ if (!grantId || !workspaceId)
911
+ return false;
912
+ const now = new Date().toISOString();
913
+ const result = this.db.prepare(`
914
+ UPDATE mcp_oauth_connector_grants
915
+ SET last_used_at = ?,
916
+ updated_at = ?
917
+ WHERE tenant_id = ? AND workspace_id = ? AND id = ?
918
+ `).run(now, now, this.tenantId, workspaceId, grantId);
919
+ return Number(result?.changes || 0) > 0;
920
+ }
921
+ }