@taskforcehq/taskforce 0.3.304 → 0.3.306

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 (383) hide show
  1. package/README.md +140 -247
  2. package/dist/Taskforce.module.css +295 -89
  3. package/dist/TaskforceCore.js +378 -111
  4. package/dist/TaskforceCore.test.js +1221 -107
  5. package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
  6. package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  7. package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
  8. package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
  9. package/dist/assets/fonts/Noir_medium.woff2 +0 -0
  10. package/dist/assets/fonts/Noir_regular.woff2 +0 -0
  11. package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
  12. package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
  13. package/dist/compat/workspaceSyncCompat.js +52 -2
  14. package/dist/compat/workspaceSyncCompat.test.js +38 -1
  15. package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
  16. package/dist/components/context/ContextAttachmentManager.js +549 -0
  17. package/dist/components/features/AgentsModule.d.ts +10 -1
  18. package/dist/components/features/AgentsModule.js +260 -15
  19. package/dist/components/features/AgentsModule.test.js +255 -5
  20. package/dist/components/features/DocumentIndex.d.ts +1 -1
  21. package/dist/components/features/DocumentIndex.js +1 -1
  22. package/dist/components/features/DocumentViewer.d.ts +6 -2
  23. package/dist/components/features/DocumentViewer.js +90 -10
  24. package/dist/components/features/DocumentWorkspace.d.ts +5 -22
  25. package/dist/components/features/DocumentWorkspace.js +12 -155
  26. package/dist/components/features/DocumentWorkspace.test.js +226 -6
  27. package/dist/components/features/TaskSettings.js +29 -3
  28. package/dist/components/features/TaskSettings.test.js +42 -1
  29. package/dist/components/features/documentWorkspaceModel.d.ts +24 -0
  30. package/dist/components/features/documentWorkspaceModel.js +57 -0
  31. package/dist/components/features/useDocumentWorkspaceController.d.ts +34 -0
  32. package/dist/components/features/useDocumentWorkspaceController.js +113 -0
  33. package/dist/components/task/TaskCard.js +19 -28
  34. package/dist/components/task/TaskCard.test.js +38 -3
  35. package/dist/components/task/TaskContextUpload.js +4 -526
  36. package/dist/components/task/TaskForm.d.ts +8 -8
  37. package/dist/components/task/TaskForm.js +362 -230
  38. package/dist/components/task/TaskForm.test.js +558 -381
  39. package/dist/components/task/TaskKanban.d.ts +2 -1
  40. package/dist/components/task/TaskKanban.js +96 -14
  41. package/dist/components/task/TaskKanban.test.js +36 -0
  42. package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
  43. package/dist/components/ui/EditableAvatarButton.js +18 -0
  44. package/dist/components/views/PlanComparisonPage.d.ts +9 -1
  45. package/dist/components/views/PlanComparisonPage.js +70 -17
  46. package/dist/components/views/PlanComparisonPage.test.js +289 -41
  47. package/dist/components/views/PlansPage.js +239 -106
  48. package/dist/components/views/PlansPage.test.js +593 -42
  49. package/dist/components/views/StandaloneLayout.js +310 -554
  50. package/dist/components/views/panels/FilterToolbar.test.js +6 -4
  51. package/dist/components/views/panels/PlanningDrawer.d.ts +8 -1
  52. package/dist/components/views/panels/PlanningDrawer.js +18 -11
  53. package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
  54. package/dist/components/views/panels/PlanningDrawer.test.js +141 -0
  55. package/dist/components/views/planningScope.d.ts +20 -0
  56. package/dist/components/views/planningScope.js +25 -0
  57. package/dist/components/views/planningScope.test.d.ts +1 -0
  58. package/dist/components/views/planningScope.test.js +60 -0
  59. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +27 -14
  60. package/dist/components/views/standalone/modals/AccountHubModal.js +87 -15
  61. package/dist/components/views/standalone/modals/AccountHubModal.test.js +42 -2
  62. package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
  63. package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
  64. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
  65. package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
  66. package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
  67. package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
  68. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
  69. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
  70. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
  71. package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
  72. package/dist/config/envSchema.js +1 -1
  73. package/dist/core/AiProfileService.d.ts +45 -3
  74. package/dist/core/AiProfileService.js +774 -63
  75. package/dist/core/AiProfileService.test.js +22 -2
  76. package/dist/core/AiProfiles.test.js +761 -2
  77. package/dist/core/AttachmentLinkService.js +57 -15
  78. package/dist/core/AuthIdentityService.d.ts +107 -0
  79. package/dist/core/AuthIdentityService.js +284 -0
  80. package/dist/core/AuthTokenService.d.ts +4 -0
  81. package/dist/core/AuthTokenService.js +34 -6
  82. package/dist/core/AuthTokenService.test.d.ts +1 -0
  83. package/dist/core/AuthTokenService.test.js +78 -0
  84. package/dist/core/EntitlementsPolicy.test.js +77 -15
  85. package/dist/core/GlobalSettingsService.js +115 -6
  86. package/dist/core/PlanEntitlementService.d.ts +30 -3
  87. package/dist/core/PlanEntitlementService.js +454 -93
  88. package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
  89. package/dist/core/PlanFeatureCatalog.test.js +84 -0
  90. package/dist/core/PlanVersionPolicy.test.js +54 -28
  91. package/dist/core/PlanningEntities.test.js +52 -0
  92. package/dist/core/SignupMonetizationSettings.test.js +241 -44
  93. package/dist/core/SyncReconciliationService.js +2 -65
  94. package/dist/core/SystemAdmin.test.js +244 -88
  95. package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
  96. package/dist/core/TaskTaxonomyValidation.test.js +53 -0
  97. package/dist/core/Taskforce.d.ts +228 -14
  98. package/dist/core/Taskforce.js +814 -201
  99. package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
  100. package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
  101. package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
  102. package/dist/core/WorkspaceLifecycleService.js +0 -5
  103. package/dist/core/WorkspacePermissions.test.js +25 -1
  104. package/dist/core/WorkspacePlanMode.test.js +128 -27
  105. package/dist/core/shared.d.ts +3 -1
  106. package/dist/core/shared.js +16 -1
  107. package/dist/core/types.d.ts +68 -2
  108. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -1
  109. package/dist/hooks/sync/auth.js +3 -1
  110. package/dist/hooks/sync/bootstrap.js +2 -0
  111. package/dist/hooks/sync/controlPlane.d.ts +37 -0
  112. package/dist/hooks/sync/controlPlane.js +78 -0
  113. package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
  114. package/dist/hooks/sync/controlPlane.test.js +121 -0
  115. package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
  116. package/dist/hooks/sync/lifecyclePolicy.js +93 -0
  117. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
  118. package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
  119. package/dist/hooks/sync/orchestratorShared.d.ts +10 -1
  120. package/dist/hooks/sync/orchestratorShared.js +68 -47
  121. package/dist/hooks/sync/orchestratorShared.test.js +50 -1
  122. package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
  123. package/dist/hooks/sync/pullLifecycle.js +24 -0
  124. package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
  125. package/dist/hooks/sync/pullLifecycle.test.js +80 -0
  126. package/dist/hooks/sync/recovery.d.ts +16 -2
  127. package/dist/hooks/sync/recovery.js +171 -53
  128. package/dist/hooks/sync/recovery.test.d.ts +1 -0
  129. package/dist/hooks/sync/recovery.test.js +292 -0
  130. package/dist/hooks/sync/transfers.d.ts +16 -3
  131. package/dist/hooks/sync/transfers.js +186 -65
  132. package/dist/hooks/sync/transfers.test.d.ts +1 -0
  133. package/dist/hooks/sync/transfers.test.js +396 -0
  134. package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
  135. package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
  136. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
  137. package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
  138. package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
  139. package/dist/hooks/sync/useSyncStatusActions.js +88 -0
  140. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
  141. package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
  142. package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
  143. package/dist/hooks/sync/useSyncStatusControls.js +60 -0
  144. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
  145. package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
  146. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
  147. package/dist/hooks/useSyncOrchestrator.d.ts +16 -6
  148. package/dist/hooks/useSyncOrchestrator.js +424 -175
  149. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +487 -10
  150. package/dist/hooks/useTaskData.js +8 -3
  151. package/dist/hooks/useTaskData.test.js +45 -0
  152. package/dist/hooks/useTaskMutations.d.ts +2 -1
  153. package/dist/hooks/useTaskMutations.js +12 -1
  154. package/dist/hooks/useTaskMutations.test.js +30 -1
  155. package/dist/hooks/useTaskforce.d.ts +65 -4
  156. package/dist/hooks/useTaskforce.js +564 -243
  157. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
  158. package/dist/hooks/useTaskforce.runtime-routing.test.js +152 -0
  159. package/dist/hooks/useTaskforce.sync-behavior.test.js +2454 -208
  160. package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
  161. package/dist/hooks/useWorkspaceSyncController.js +17 -2
  162. package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
  163. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
  164. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +316 -35
  165. package/dist/localization/locales/en-US.d.ts +0 -1
  166. package/dist/localization/locales/en-US.js +0 -1
  167. package/dist/localization/locales/es-419.js +0 -1
  168. package/dist/localization/locales/pt-BR.js +0 -1
  169. package/dist/mcp/adminWorkspaceRegistrar.d.ts +2 -0
  170. package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
  171. package/dist/mcp/collaborationRegistrar.d.ts +2 -0
  172. package/dist/mcp/collaborationRegistrar.js +71 -0
  173. package/dist/mcp/documentAssetRegistrar.d.ts +2 -0
  174. package/dist/mcp/documentAssetRegistrar.js +346 -0
  175. package/dist/mcp/runtime.d.ts +2 -0
  176. package/dist/mcp/runtime.js +2433 -3534
  177. package/dist/mcp/runtime.test.js +440 -2
  178. package/dist/mcp/taskPlanningRegistrar.d.ts +2 -0
  179. package/dist/mcp/taskPlanningRegistrar.js +418 -0
  180. package/dist/mcp/toolCatalog.d.ts +35 -0
  181. package/dist/mcp/toolCatalog.js +3 -0
  182. package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
  183. package/dist/migrations/taskSchemaMigrations.js +171 -61
  184. package/dist/migrations/taskSchemaMigrations.test.js +15 -0
  185. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  186. package/dist/resources/templates/workflows/collaborate.yaml +1 -1
  187. package/dist/resources/templates/workflows/evaluate.yaml +2 -2
  188. package/dist/resources/templates/workflows/plan.yaml +1 -1
  189. package/dist/resources/templates/workflows/review.yaml +2 -2
  190. package/dist/server/annotatedAttachmentsRoutes.test.js +3 -3
  191. package/dist/server/auth/providers/apple.d.ts +33 -0
  192. package/dist/server/auth/providers/apple.js +82 -0
  193. package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
  194. package/dist/server/auth/providers/appleClientSecret.js +65 -0
  195. package/dist/server/auth/providers/github.d.ts +26 -0
  196. package/dist/server/auth/providers/github.js +86 -0
  197. package/dist/server/auth/providers/google.d.ts +21 -0
  198. package/dist/server/auth/providers/google.js +56 -0
  199. package/dist/server/auth/providers/types.d.ts +21 -0
  200. package/dist/server/auth/providers/types.js +1 -0
  201. package/dist/server/auth.d.ts +2 -0
  202. package/dist/server/auth.js +6 -3
  203. package/dist/server/documentReviewRoutes.test.js +36 -3
  204. package/dist/server/index.cookieProxy.test.js +1 -0
  205. package/dist/server/index.d.ts +12 -0
  206. package/dist/server/index.js +120 -9
  207. package/dist/server/index.rateLimit.test.js +3 -0
  208. package/dist/server/index.test.js +106 -3
  209. package/dist/server/routes/admin.d.ts +4 -5
  210. package/dist/server/routes/admin.js +410 -80
  211. package/dist/server/routes/annotatedAttachments.d.ts +1 -1
  212. package/dist/server/routes/annotatedAttachments.js +1 -1
  213. package/dist/server/routes/auth.d.ts +16 -6
  214. package/dist/server/routes/auth.js +1015 -118
  215. package/dist/server/routes/authSupport.d.ts +30 -0
  216. package/dist/server/routes/authSupport.js +81 -0
  217. package/dist/server/routes/billing.d.ts +1 -1
  218. package/dist/server/routes/billing.js +276 -34
  219. package/dist/server/routes/billing.test.js +760 -52
  220. package/dist/server/routes/documentReviews.d.ts +1 -1
  221. package/dist/server/routes/documentReviews.js +1 -1
  222. package/dist/server/routes/documents.d.ts +5 -2
  223. package/dist/server/routes/documents.js +242 -74
  224. package/dist/server/routes/primitives.d.ts +11 -0
  225. package/dist/server/routes/primitives.js +73 -0
  226. package/dist/server/routes/resources.d.ts +1 -1
  227. package/dist/server/routes/resources.js +1 -1
  228. package/dist/server/routes/shared.d.ts +29 -3
  229. package/dist/server/routes/shared.js +69 -4
  230. package/dist/server/routes/sync.d.ts +1 -1
  231. package/dist/server/routes/sync.integration.test.js +437 -39
  232. package/dist/server/routes/sync.js +62 -606
  233. package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
  234. package/dist/server/routes/syncAuxRoutes.js +91 -0
  235. package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
  236. package/dist/server/routes/syncAuxRoutes.test.js +158 -0
  237. package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
  238. package/dist/server/routes/syncPullApplyRoutes.js +204 -0
  239. package/dist/server/routes/syncPushRoutes.d.ts +24 -0
  240. package/dist/server/routes/syncPushRoutes.js +212 -0
  241. package/dist/server/routes/syncRouteGuards.d.ts +36 -0
  242. package/dist/server/routes/syncRouteGuards.js +67 -0
  243. package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
  244. package/dist/server/routes/syncRouteGuards.test.js +94 -0
  245. package/dist/server/routes/syncRouteTypes.d.ts +13 -0
  246. package/dist/server/routes/syncRouteTypes.js +1 -0
  247. package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
  248. package/dist/server/routes/syncSnapshotRoutes.js +180 -0
  249. package/dist/server/routes/tasks.d.ts +1 -1
  250. package/dist/server/routes/tasks.js +44 -2
  251. package/dist/server/routes/workspaces.d.ts +1 -1
  252. package/dist/server/routes/workspaces.js +37 -6
  253. package/dist/server/routes.d.ts +2 -9
  254. package/dist/server/routes.js +136 -165
  255. package/dist/server/routes.test.js +2457 -162
  256. package/dist/services/workflowExportSnapshots.test.js +2 -0
  257. package/dist/shared/aiProfileSeatScope.d.ts +5 -0
  258. package/dist/shared/aiProfileSeatScope.js +21 -0
  259. package/dist/shared/passwordPolicy.d.ts +13 -0
  260. package/dist/shared/passwordPolicy.js +84 -0
  261. package/dist/shared/passwordPolicy.test.d.ts +1 -0
  262. package/dist/shared/passwordPolicy.test.js +34 -0
  263. package/dist/storage/workspaceAssetStore.d.ts +15 -3
  264. package/dist/storage/workspaceAssetStore.js +132 -44
  265. package/dist/storage/workspaceAssetStore.test.js +93 -6
  266. package/dist/styles/fonts.css +70 -0
  267. package/dist/sync/collaborationSyncPayload.d.ts +6 -1
  268. package/dist/sync/collaborationSyncPayload.js +17 -11
  269. package/dist/sync/collaborationSyncPayload.test.js +13 -1
  270. package/dist/sync/contentSyncState.d.ts +4 -8
  271. package/dist/sync/contentSyncState.js +33 -77
  272. package/dist/sync/syncApplyHandlers.d.ts +7 -0
  273. package/dist/sync/syncApplyHandlers.js +28 -13
  274. package/dist/sync/syncApplyHandlers.test.js +72 -3
  275. package/dist/sync/syncFieldSemantics.d.ts +12 -0
  276. package/dist/sync/syncFieldSemantics.js +55 -0
  277. package/dist/sync/syncResourceAdapters.d.ts +44 -0
  278. package/dist/sync/syncResourceAdapters.js +77 -0
  279. package/dist/sync/syncService.d.ts +6 -0
  280. package/dist/sync/syncService.js +60 -0
  281. package/dist/sync/syncService.test.js +71 -3
  282. package/dist/sync/taskSyncChangeKey.d.ts +2 -0
  283. package/dist/sync/taskSyncChangeKey.js +143 -0
  284. package/dist/sync/taskSyncPayload.js +3 -8
  285. package/dist/sync/workspacePullFeed.js +4 -3
  286. package/dist/sync/workspacePullFeed.test.js +43 -0
  287. package/dist/sync/workspaceRepair.js +5 -2
  288. package/dist/sync/workspaceSyncModel.d.ts +48 -0
  289. package/dist/sync/workspaceSyncModel.js +195 -31
  290. package/dist/sync/workspaceSyncModel.test.js +325 -12
  291. package/dist/sync/workspaceSyncState.d.ts +19 -0
  292. package/dist/sync/workspaceSyncState.js +3 -1
  293. package/dist/sync/workspaceSyncSurface.js +3 -8
  294. package/dist/types.d.ts +17 -0
  295. package/dist/ui/assets/AgentsModule-DrLawwNl.js +1 -0
  296. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BlS-cqnl.js → AnnotatedAttachmentWorkspace-Z9_whf7F.js} +1 -1
  297. package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
  298. package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
  299. package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
  300. package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
  301. package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +252 -0
  302. package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +1 -0
  303. package/dist/ui/assets/{InitiativesModule-BjR6iBf9.js → InitiativesModule-9E6ParrY.js} +1 -1
  304. package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
  305. package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
  306. package/dist/ui/assets/PlansPage-CvfnMiWq.css +1 -0
  307. package/dist/ui/assets/PlansPage-DRXscYxH.js +1 -0
  308. package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
  309. package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
  310. package/dist/ui/assets/TaskSettings-DZX4jk7e.js +9 -0
  311. package/dist/ui/assets/{WorkflowsModule-DnFqdUME.js → WorkflowsModule-BA7jcEpH.js} +1 -1
  312. package/dist/ui/assets/index-9eT8e0Lu.css +1 -0
  313. package/dist/ui/assets/index-C6k75jr8.js +6 -0
  314. package/dist/ui/assets/{vendor-icons-I7ZwG1z_.js → vendor-icons-DuEd_65c.js} +1 -1
  315. package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
  316. package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
  317. package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
  318. package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  319. package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
  320. package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
  321. package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
  322. package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
  323. package/dist/ui/fonts/Noir_medium.otf +0 -0
  324. package/dist/ui/fonts/Noir_medium.woff2 +0 -0
  325. package/dist/ui/fonts/Noir_regular.otf +0 -0
  326. package/dist/ui/fonts/Noir_regular.woff2 +0 -0
  327. package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
  328. package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
  329. package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
  330. package/dist/ui/index.html +3 -3
  331. package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
  332. package/dist/utils/assignees.d.ts +2 -0
  333. package/dist/utils/assignees.js +2 -0
  334. package/dist/utils/avatarUpload.d.ts +27 -0
  335. package/dist/utils/avatarUpload.js +132 -0
  336. package/dist/utils/avatarUpload.test.d.ts +1 -0
  337. package/dist/utils/avatarUpload.test.js +40 -0
  338. package/dist/utils/bootstrapTrace.d.ts +7 -0
  339. package/dist/utils/bootstrapTrace.js +29 -0
  340. package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
  341. package/dist/utils/commercialLifecyclePresentation.js +199 -0
  342. package/dist/utils/contextAssetEvents.d.ts +2 -0
  343. package/dist/utils/syncEventDetails.d.ts +3 -0
  344. package/dist/utils/syncEventDetails.js +64 -0
  345. package/dist/utils/syncEventDetails.test.d.ts +1 -0
  346. package/dist/utils/syncEventDetails.test.js +23 -0
  347. package/dist/utils/syncEventPresentation.d.ts +15 -0
  348. package/dist/utils/syncEventPresentation.js +129 -0
  349. package/dist/utils/syncEventPresentation.test.d.ts +1 -0
  350. package/dist/utils/syncEventPresentation.test.js +64 -0
  351. package/dist/utils/syncStatusPresentation.d.ts +72 -0
  352. package/dist/utils/syncStatusPresentation.js +217 -0
  353. package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
  354. package/dist/utils/syncStatusPresentation.test.js +164 -0
  355. package/dist/utils/taskActivity.d.ts +2 -0
  356. package/dist/utils/taskActivity.js +228 -22
  357. package/dist/utils/taskActivity.test.js +119 -8
  358. package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
  359. package/dist/utils/workspaceSyncPresentation.js +68 -2
  360. package/dist/utils/workspaceSyncPresentation.test.js +36 -1
  361. package/package.json +10 -2
  362. package/scripts/check-cloud-mcp.mjs +83 -0
  363. package/scripts/export-sqlite-to-postgres.mjs +7 -9
  364. package/scripts/playwright-auth.sh +5 -1
  365. package/scripts/run-billing-performance-smoke.sh +24 -0
  366. package/scripts/run-e2e-realtime.sh +17 -3
  367. package/scripts/run-smoke.sh +17 -5
  368. package/scripts/run-soak.sh +17 -5
  369. package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  370. package/src/resources/templates/workflows/collaborate.yaml +1 -1
  371. package/src/resources/templates/workflows/evaluate.yaml +2 -2
  372. package/src/resources/templates/workflows/plan.yaml +1 -1
  373. package/src/resources/templates/workflows/review.yaml +2 -2
  374. package/dist/ui/assets/AgentsModule-BLWVbuKP.js +0 -1
  375. package/dist/ui/assets/DocumentWorkspace-BH_6DF6x.js +0 -250
  376. package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
  377. package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
  378. package/dist/ui/assets/PlansPage-CSDQ4sLa.js +0 -1
  379. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  380. package/dist/ui/assets/TaskSettings-CEDy34Jo.js +0 -9
  381. package/dist/ui/assets/index-DXUdtH1B.js +0 -6
  382. package/dist/ui/assets/index-Ii0B0rTO.css +0 -1
  383. package/scripts/migrate-planning-model.ts +0 -233
@@ -7,6 +7,7 @@
7
7
  *
8
8
  * Routes registered here:
9
9
  * GET/POST /api/platform/plans
10
+ * GET /api/platform/onboarding-plan-options
10
11
  * GET/POST /api/platform/plans/:planId/versions
11
12
  * GET /api/platform/features
12
13
  * GET/PATCH /api/platform/plans/:planId/versions/:versionId/features
@@ -23,6 +24,8 @@
23
24
  * GET/POST /api/taskforce/admin/email-settings
24
25
  * GET /api/taskforce/admin/email-settings/health
25
26
  * POST /api/taskforce/admin/email-settings/test
27
+ * GET/POST /api/taskforce/admin/auth-provider-settings
28
+ * GET /api/taskforce/admin/auth-provider-settings/health
26
29
  * GET/POST /api/taskforce/admin/system-settings
27
30
  * GET/POST /api/taskforce/admin/object-storage-settings
28
31
  * GET /api/taskforce/admin/object-storage-telemetry
@@ -50,9 +53,9 @@
50
53
  import * as fs from 'fs';
51
54
  import * as path from 'path';
52
55
  import { TaskforceRuleError } from '../../core/Taskforce.js';
53
- import { parseJsonBody } from '../routes.js';
56
+ import { escapeHtml, parseFormBody, parseJsonBody } from './primitives.js';
54
57
  import { resolveRequestAccess } from '../auth.js';
55
- import { resolveEffectiveEmailConfig, sendEmail } from '../email.js';
58
+ import { sendEmail } from '../email.js';
56
59
  import { WorkspaceAssetStore } from '../../storage/workspaceAssetStore.js';
57
60
  import { scanDocumentIntegrity } from '../../storage/documentIntegrity.js';
58
61
  import { normalizeStorageIntegrityScheduleSettings, resolveStorageIntegrityScheduleSettings, } from '../../storage/integritySchedule.js';
@@ -64,67 +67,19 @@ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/
64
67
  import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
65
68
  import { randomUUID } from 'crypto';
66
69
  import { resolveDeploymentConfig } from '../../config/deployment.js';
67
- import { getTaskforceCloudEnvironmentConfig, resolveTaskforceCloudEnvironmentFromBaseUrl } from '../../config/cloudEnvironment.js';
70
+ import { getTaskforceCloudEnvironmentConfig, resolveTaskforceCloudEnvironmentFromBaseUrl, resolveTaskforceMcpBaseUrlFromBaseUrl } from '../../config/cloudEnvironment.js';
68
71
  import { buildCloudMcpRuntimeAuth, resolveErrorStatusCode } from './shared.js';
72
+ import { buildAuthBehavior, buildSiteBehavior, getRouteEmailConfig, resolvePublicAuthBaseUrl, resolveRuntimeConfigBaseUrl, } from './authSupport.js';
69
73
  import { applyWorkspaceRepair, scanWorkspaceRepair } from '../../sync/workspaceRepair.js';
74
+ import { testMintAppleClientSecret } from '../auth/providers/appleClientSecret.js';
70
75
  // === SECTION: Registration ===
71
76
  export function registerAdminRoutes(deps) {
72
- const { addRoute, core, context, requestWorkspaceId, requestUserId, requestRole, requestHeader, requestRuntimeMode, ensureAppAccess, ensureAdminRole, ensureCloudPlanData, requireCloudRuntime, resolveAuthenticatedUserId, resolveAdminRequestActor, auditAdminAction, resolveEffectiveObjectStorageConfig, getObjectStorageClient, taskAssetStore, normalizeDocumentPathInput, ensureWithinDir, getAssetMetadata, markAssetDeleted, clearAssetDeleted, removeAssetMetadata, getStorageTelemetry, appendStorageTelemetry, getStorageIntegrityHistory, appendStorageIntegrityHistory, getDocumentPurgeHistory, appendDocumentPurgeHistory, getCollaborationTelemetry, isCollaborationPolicyEnabled, resolveRuntimeConfigBaseUrl, resolvePublicAuthBaseUrl, parseFormBody, escapeHtml, normalizeMcpOauthScopeString, MCP_TOKEN_SCOPE_VALUES, MCP_CLOUD_TOOL_SCOPE_REQUIREMENTS, MCP_WRITE_RATE_LIMIT_ENABLED, MCP_WRITE_RATE_LIMIT_ACTOR_MAX_REQUESTS, MCP_WRITE_RATE_LIMIT_WORKSPACE_MAX_REQUESTS, MCP_WRITE_RATE_LIMIT_WINDOW_MS, } = deps;
77
+ const { addRoute, core, context, requestWorkspaceId, requestUserId, requestRole, requestHeader, requestRuntimeMode, ensureAppAccess, ensureAdminRole, ensureCloudPlanData, requireCloudRuntime, resolveAuthenticatedUserId, resolveAdminRequestActor, auditAdminAction, resolveEffectiveObjectStorageConfig, getObjectStorageClient, taskAssetStore, normalizeDocumentPathInput, ensureWithinDir, getAssetMetadata, markAssetDeleted, clearAssetDeleted, removeAssetMetadata, getStorageTelemetry, appendStorageTelemetry, getStorageIntegrityHistory, appendStorageIntegrityHistory, getDocumentPurgeHistory, appendDocumentPurgeHistory, getCollaborationTelemetry, isCollaborationPolicyEnabled, normalizeMcpOauthScopeString, MCP_TOKEN_SCOPE_VALUES, MCP_CLOUD_TOOL_SCOPE_REQUIREMENTS, MCP_WRITE_RATE_LIMIT_ENABLED, MCP_WRITE_RATE_LIMIT_ACTOR_MAX_REQUESTS, MCP_WRITE_RATE_LIMIT_WORKSPACE_MAX_REQUESTS, MCP_WRITE_RATE_LIMIT_WINDOW_MS, } = deps;
73
78
  const baseVirusScanConfig = resolveVirusScanConfigFromEnv(process.env);
74
79
  const workspaceAssetStore = typeof core.getDatabaseAdapter === 'function' && typeof core.getTenantId === 'function'
75
80
  ? new WorkspaceAssetStore(core.getDatabaseAdapter(), core.getTenantId())
76
81
  : null;
77
82
  // === SECTION: Email helpers ===
78
- const getEmailConfig = () => {
79
- if (context.authConfig?.runtimeMode !== 'cloud') {
80
- return resolveEffectiveEmailConfig(process.env, { provider: 'console' });
81
- }
82
- const globalSettings = core.getGlobalSettings();
83
- return resolveEffectiveEmailConfig(process.env, globalSettings.email || null);
84
- };
85
- const resolveBooleanSetting = (value, fallback) => {
86
- if (typeof value === 'boolean')
87
- return value;
88
- if (typeof value === 'number')
89
- return value !== 0;
90
- const normalized = String(value || '').trim().toLowerCase();
91
- if (!normalized)
92
- return fallback;
93
- if (['1', 'true', 'yes', 'on'].includes(normalized))
94
- return true;
95
- if (['0', 'false', 'no', 'off'].includes(normalized))
96
- return false;
97
- return fallback;
98
- };
99
- const getAuthBehavior = () => {
100
- const globalSettings = core.getGlobalSettings();
101
- const authSettings = globalSettings.auth || {};
102
- const authRateLimitMaxRequestsRaw = Number(authSettings.authRateLimitMaxRequests);
103
- const authRateLimitWindowMsRaw = Number(authSettings.authRateLimitWindowMs);
104
- const onboardingPolicy = core.getOnboardingPolicy();
105
- return {
106
- allowSelfRegistration: resolveBooleanSetting(authSettings.allowSelfRegistration, true),
107
- requireVerifiedEmail: resolveBooleanSetting(authSettings.requireVerifiedEmail, true),
108
- defaultNewUserBetaAccess: resolveBooleanSetting(authSettings.defaultNewUserBetaAccess, false),
109
- authRateLimitEnabled: resolveBooleanSetting(authSettings.authRateLimitEnabled, true),
110
- authRateLimitMaxRequests: Number.isFinite(authRateLimitMaxRequestsRaw) && authRateLimitMaxRequestsRaw >= 1
111
- ? Math.floor(authRateLimitMaxRequestsRaw)
112
- : 10,
113
- authRateLimitWindowMs: Number.isFinite(authRateLimitWindowMsRaw) && authRateLimitWindowMsRaw >= 1000
114
- ? Math.floor(authRateLimitWindowMsRaw)
115
- : 15 * 60_000,
116
- onboardingPolicyMode: onboardingPolicy.mode,
117
- onboardingPlanVersionId: onboardingPolicy.planVersionId
118
- };
119
- };
120
- const getSiteBehavior = () => {
121
- const globalSettings = core.getGlobalSettings();
122
- const siteSettings = globalSettings.site || {};
123
- return {
124
- showRunTaskforceLocally: resolveBooleanSetting(siteSettings.showRunTaskforceLocally, true),
125
- pricingPageEnabled: resolveBooleanSetting(siteSettings.pricingPageEnabled, true)
126
- };
127
- };
128
83
  const resolveEffectiveVirusScanSettings = () => {
129
84
  const settings = core.getGlobalSettings().objectStorage || {};
130
85
  const asyncEnabled = typeof settings.virusScanAsyncEnabled === 'boolean'
@@ -139,6 +94,7 @@ export function registerAdminRoutes(deps) {
139
94
  };
140
95
  };
141
96
  // === SECTION: MCP helpers ===
97
+ const MCP_CONNECTION_TEST_TIMEOUT_MS = 5000;
142
98
  const writeMcpHttpError = (res, statusCode, message) => {
143
99
  res.writeHead(statusCode, { 'Content-Type': 'application/json' });
144
100
  res.end(JSON.stringify({
@@ -171,6 +127,112 @@ export function registerAdminRoutes(deps) {
171
127
  const resolveMcpOauthTokenUrl = (req) => `${resolveMcpOauthIssuer(req)}/oauth/mcp/token`;
172
128
  const resolveMcpOauthRegisterUrl = (req) => `${resolveMcpOauthIssuer(req)}/oauth/mcp/register`;
173
129
  const resolveMcpOauthResourceUrl = (req) => `${resolveMcpOauthIssuer(req)}/mcp`;
130
+ const resolveCloudMcpEndpoint = (req) => {
131
+ const requestBaseUrl = resolveRuntimeConfigBaseUrl(req);
132
+ const deployment = resolveDeploymentConfig(process.env, { requestBaseUrl });
133
+ const inferredMcpBaseUrl = resolveTaskforceMcpBaseUrlFromBaseUrl(deployment.cloudMcpBaseUrl
134
+ || deployment.cloudAuthBaseUrl
135
+ || deployment.cloudBaseUrl
136
+ || requestBaseUrl
137
+ || '');
138
+ const baseUrl = String(inferredMcpBaseUrl || deployment.cloudMcpBaseUrl || resolveMcpOauthIssuer(req)).trim().replace(/\/+$/, '');
139
+ return `${baseUrl}/mcp`;
140
+ };
141
+ const probeCloudMcpEndpoint = async (req, token) => {
142
+ const endpoint = resolveCloudMcpEndpoint(req);
143
+ const controller = new AbortController();
144
+ const timeout = setTimeout(() => controller.abort(), MCP_CONNECTION_TEST_TIMEOUT_MS);
145
+ try {
146
+ const response = await fetch(endpoint, {
147
+ method: 'POST',
148
+ headers: {
149
+ Authorization: `Bearer ${token}`,
150
+ Accept: 'application/json, text/event-stream',
151
+ 'Content-Type': 'application/json'
152
+ },
153
+ body: JSON.stringify({
154
+ jsonrpc: '2.0',
155
+ id: `mcp-connection-test-${randomUUID()}`,
156
+ method: 'initialize',
157
+ params: {
158
+ protocolVersion: '2025-03-26',
159
+ capabilities: {},
160
+ clientInfo: {
161
+ name: 'taskforce-settings-test',
162
+ version: '1.0.0'
163
+ }
164
+ }
165
+ }),
166
+ signal: controller.signal
167
+ });
168
+ const responseText = await response.text();
169
+ let payload = null;
170
+ try {
171
+ payload = responseText ? JSON.parse(responseText) : null;
172
+ }
173
+ catch {
174
+ payload = null;
175
+ }
176
+ const remoteMessage = typeof payload?.error?.message === 'string'
177
+ ? payload.error.message.trim()
178
+ : '';
179
+ if (!response.ok) {
180
+ if (response.status >= 500) {
181
+ return {
182
+ ok: false,
183
+ statusCode: 502,
184
+ code: 'MCP_ENDPOINT_GATEWAY_ERROR',
185
+ error: `Cloud MCP endpoint ${endpoint} returned ${response.status}. The dedicated MCP host may be degraded or unavailable.`,
186
+ details: {
187
+ endpoint,
188
+ upstreamStatus: response.status,
189
+ remoteMessage: remoteMessage || null
190
+ }
191
+ };
192
+ }
193
+ return {
194
+ ok: false,
195
+ statusCode: 502,
196
+ code: 'MCP_ENDPOINT_REJECTED_VALIDATED_TOKEN',
197
+ error: `Cloud MCP endpoint ${endpoint} rejected a token that validated locally. This usually means the MCP service is stale, routed incorrectly, or out of sync with the auth database.`,
198
+ details: {
199
+ endpoint,
200
+ upstreamStatus: response.status,
201
+ remoteMessage: remoteMessage || null
202
+ }
203
+ };
204
+ }
205
+ return {
206
+ ok: true,
207
+ endpoint,
208
+ serverName: typeof payload?.result?.serverInfo?.name === 'string'
209
+ ? payload.result.serverInfo.name
210
+ : null,
211
+ protocolVersion: typeof payload?.result?.protocolVersion === 'string'
212
+ ? payload.result.protocolVersion
213
+ : null
214
+ };
215
+ }
216
+ catch (error) {
217
+ const timedOut = error?.name === 'AbortError';
218
+ return {
219
+ ok: false,
220
+ statusCode: 502,
221
+ code: timedOut ? 'MCP_ENDPOINT_TIMEOUT' : 'MCP_ENDPOINT_UNREACHABLE',
222
+ error: timedOut
223
+ ? `Cloud MCP endpoint ${endpoint} did not respond within ${MCP_CONNECTION_TEST_TIMEOUT_MS}ms.`
224
+ : `Could not reach cloud MCP endpoint ${endpoint}. The dedicated MCP host may be unavailable or misconfigured.`,
225
+ details: {
226
+ endpoint,
227
+ timeoutMs: MCP_CONNECTION_TEST_TIMEOUT_MS,
228
+ reason: String(error?.message || error || '')
229
+ }
230
+ };
231
+ }
232
+ finally {
233
+ clearTimeout(timeout);
234
+ }
235
+ };
174
236
  const renderMcpOauthAuthorizePage = (input) => {
175
237
  const options = input.workspaces.map((workspace) => {
176
238
  const selected = workspace.id === input.preselectedWorkspaceId ? ' selected' : '';
@@ -513,6 +575,18 @@ export function registerAdminRoutes(deps) {
513
575
  res.writeHead(200, { 'Content-Type': 'application/json' });
514
576
  res.end(JSON.stringify({ success: true, plans }));
515
577
  });
578
+ addRoute('GET', '/api/platform/onboarding-plan-options', async (req, res) => {
579
+ if (!ensureCloudPlanData(req, res))
580
+ return;
581
+ if (!ensureAdminRole(req)) {
582
+ res.writeHead(403, { 'Content-Type': 'application/json' });
583
+ res.end(JSON.stringify({ error: 'Forbidden: system admin role required.' }));
584
+ return;
585
+ }
586
+ const options = core.listOnboardingPlanOptions();
587
+ res.writeHead(200, { 'Content-Type': 'application/json' });
588
+ res.end(JSON.stringify({ success: true, options }));
589
+ });
516
590
  addRoute('POST', '/api/platform/plans', async (req, res) => {
517
591
  if (!ensureCloudPlanData(req, res))
518
592
  return;
@@ -522,10 +596,19 @@ export function registerAdminRoutes(deps) {
522
596
  return;
523
597
  }
524
598
  const body = await parseJsonBody(req);
599
+ const description = body?.description === undefined || body?.description === null
600
+ ? body?.description
601
+ : String(body.description);
602
+ if (typeof description === 'string' && description.trim().length > 240) {
603
+ res.writeHead(400, { 'Content-Type': 'application/json' });
604
+ res.end(JSON.stringify({ success: false, error: 'Plan description must be 240 characters or fewer.', code: 'PLAN_DESCRIPTION_TOO_LONG' }));
605
+ return;
606
+ }
525
607
  try {
526
608
  const plan = core.upsertPlanCatalog({
527
609
  planId: body?.planId,
528
610
  displayName: body?.displayName,
611
+ description,
529
612
  status: body?.status,
530
613
  initialMode: body?.initialMode,
531
614
  initialTrialDays: body?.initialTrialDays === undefined ? undefined : Number(body?.initialTrialDays),
@@ -581,6 +664,62 @@ export function registerAdminRoutes(deps) {
581
664
  res.end(JSON.stringify({ success: false, error: String(error?.message || 'Unable to list feature catalog'), code: error?.code }));
582
665
  }
583
666
  });
667
+ addRoute('PATCH', '/api/platform/features/:featureKey', async (req, res, params) => {
668
+ if (!ensureCloudPlanData(req, res))
669
+ return;
670
+ if (!ensureAdminRole(req)) {
671
+ res.writeHead(403, { 'Content-Type': 'application/json' });
672
+ res.end(JSON.stringify({ error: 'Forbidden: system admin role required.' }));
673
+ return;
674
+ }
675
+ const body = await parseJsonBody(req);
676
+ const featureKey = String(params.featureKey || '').trim();
677
+ const publicLabel = body?.publicLabel === undefined || body?.publicLabel === null
678
+ ? body?.publicLabel
679
+ : String(body.publicLabel);
680
+ const publicDescription = body?.publicDescription === undefined || body?.publicDescription === null
681
+ ? body?.publicDescription
682
+ : String(body.publicDescription);
683
+ const publicDisplayOrder = body?.publicDisplayOrder === undefined || body?.publicDisplayOrder === null || body?.publicDisplayOrder === ''
684
+ ? null
685
+ : Math.max(0, Math.floor(Number(body.publicDisplayOrder)));
686
+ if (typeof publicLabel === 'string' && publicLabel.trim().length > 120) {
687
+ res.writeHead(400, { 'Content-Type': 'application/json' });
688
+ res.end(JSON.stringify({ success: false, error: 'Public label must be 120 characters or fewer.', code: 'PLAN_FEATURE_PUBLIC_LABEL_TOO_LONG' }));
689
+ return;
690
+ }
691
+ if (typeof publicDescription === 'string' && publicDescription.trim().length > 280) {
692
+ res.writeHead(400, { 'Content-Type': 'application/json' });
693
+ res.end(JSON.stringify({ success: false, error: 'Public description must be 280 characters or fewer.', code: 'PLAN_FEATURE_PUBLIC_DESCRIPTION_TOO_LONG' }));
694
+ return;
695
+ }
696
+ if (publicDisplayOrder !== null && !Number.isFinite(publicDisplayOrder)) {
697
+ res.writeHead(400, { 'Content-Type': 'application/json' });
698
+ res.end(JSON.stringify({ success: false, error: 'Public display order must be a non-negative number.', code: 'PLAN_FEATURE_PUBLIC_DISPLAY_ORDER_INVALID' }));
699
+ return;
700
+ }
701
+ try {
702
+ const feature = core.updatePlanFeatureCatalogMarketingCopy({
703
+ featureKey,
704
+ publicLabel,
705
+ publicDescription,
706
+ publicDisplayOrder
707
+ });
708
+ auditAdminAction(req, 'platform-plan-feature-catalog-updated', {
709
+ featureKey,
710
+ publicLabel: feature.publicLabel ?? null,
711
+ publicDescription: feature.publicDescription ?? null,
712
+ publicDisplayOrder: feature.publicDisplayOrder ?? null
713
+ });
714
+ res.writeHead(200, { 'Content-Type': 'application/json' });
715
+ res.end(JSON.stringify({ success: true, feature }));
716
+ }
717
+ catch (error) {
718
+ const statusCode = resolveErrorStatusCode(error, 400);
719
+ res.writeHead(statusCode, { 'Content-Type': 'application/json' });
720
+ res.end(JSON.stringify({ success: false, error: String(error?.message || 'Unable to update feature catalog entry'), code: error?.code }));
721
+ }
722
+ });
584
723
  addRoute('POST', '/api/platform/plans/:planId/versions', async (req, res, params) => {
585
724
  if (!ensureCloudPlanData(req, res))
586
725
  return;
@@ -797,9 +936,23 @@ export function registerAdminRoutes(deps) {
797
936
  res.end(JSON.stringify({ error: 'Forbidden: system admin role required.' }));
798
937
  return;
799
938
  }
800
- const users = core.listSystemUsers();
939
+ const reqUrl = new URL(req.url || '/api/taskforce/admin/system-users', 'http://localhost');
940
+ const query = String(reqUrl.searchParams.get('q') || '').trim();
941
+ const sort = reqUrl.searchParams.get('sort') === 'oldest' ? 'oldest' : 'newest';
942
+ const limitRaw = Number(reqUrl.searchParams.get('limit') || '50');
943
+ const offsetRaw = Number(reqUrl.searchParams.get('offset') || '0');
944
+ const limit = Number.isFinite(limitRaw) ? Math.max(1, Math.min(200, Math.floor(limitRaw))) : 50;
945
+ const offset = Number.isFinite(offsetRaw) ? Math.max(0, Math.floor(offsetRaw)) : 0;
946
+ const total = core.countSystemUsers({ query });
947
+ const users = core.listSystemUsers({ query, sort, limit, offset });
801
948
  res.writeHead(200, { 'Content-Type': 'application/json' });
802
- res.end(JSON.stringify({ users }));
949
+ res.end(JSON.stringify({
950
+ users,
951
+ total,
952
+ limit,
953
+ offset,
954
+ hasMore: offset + users.length < total
955
+ }));
803
956
  });
804
957
  // GET /api/taskforce/admin/audit-logs - Read recent admin audit events
805
958
  addRoute('GET', '/api/taskforce/admin/audit-logs', async (req, res) => {
@@ -1013,7 +1166,7 @@ export function registerAdminRoutes(deps) {
1013
1166
  return;
1014
1167
  }
1015
1168
  if (context.authConfig?.runtimeMode !== 'cloud') {
1016
- const effective = getEmailConfig();
1169
+ const effective = getRouteEmailConfig(core, context);
1017
1170
  res.writeHead(200, { 'Content-Type': 'application/json' });
1018
1171
  res.end(JSON.stringify({
1019
1172
  settings: { provider: 'console', from: effective.from, replyTo: '' },
@@ -1022,7 +1175,7 @@ export function registerAdminRoutes(deps) {
1022
1175
  return;
1023
1176
  }
1024
1177
  const settings = core.getGlobalSettings().email || {};
1025
- const effective = getEmailConfig();
1178
+ const effective = getRouteEmailConfig(core, context);
1026
1179
  res.writeHead(200, { 'Content-Type': 'application/json' });
1027
1180
  res.end(JSON.stringify({
1028
1181
  settings: {
@@ -1047,7 +1200,7 @@ export function registerAdminRoutes(deps) {
1047
1200
  return;
1048
1201
  }
1049
1202
  const settings = core.getGlobalSettings().email || {};
1050
- const effective = getEmailConfig();
1203
+ const effective = getRouteEmailConfig(core, context);
1051
1204
  const keyRequired = effective.provider === 'resend' || effective.provider === 'google-workspace';
1052
1205
  const ready = keyRequired ? Boolean(effective.providerApiKey) : true;
1053
1206
  const reason = ready
@@ -1078,8 +1231,8 @@ export function registerAdminRoutes(deps) {
1078
1231
  }
1079
1232
  const globalSettings = core.getGlobalSettings();
1080
1233
  const authSettings = globalSettings.auth || {};
1081
- const billingSettings = globalSettings.billing || {};
1082
- const effective = getAuthBehavior();
1234
+ const onboardingPolicy = core.getOnboardingPolicy();
1235
+ const effective = buildAuthBehavior(core);
1083
1236
  res.writeHead(200, { 'Content-Type': 'application/json' });
1084
1237
  res.end(JSON.stringify({
1085
1238
  settings: {
@@ -1089,14 +1242,8 @@ export function registerAdminRoutes(deps) {
1089
1242
  ...(typeof authSettings.authRateLimitEnabled === 'boolean' ? { authRateLimitEnabled: authSettings.authRateLimitEnabled } : {}),
1090
1243
  ...(typeof authSettings.authRateLimitMaxRequests === 'number' ? { authRateLimitMaxRequests: authSettings.authRateLimitMaxRequests } : {}),
1091
1244
  ...(typeof authSettings.authRateLimitWindowMs === 'number' ? { authRateLimitWindowMs: authSettings.authRateLimitWindowMs } : {}),
1092
- ...((billingSettings.onboardingPolicy?.mode === 'require_plan_selection'
1093
- || billingSettings.onboardingPolicy?.mode === 'auto_grant_free'
1094
- || billingSettings.onboardingPolicy?.mode === 'auto_start_trial')
1095
- ? { onboardingPolicyMode: billingSettings.onboardingPolicy.mode }
1096
- : {}),
1097
- ...(typeof billingSettings.onboardingPolicy?.planVersionId === 'string' || billingSettings.onboardingPolicy?.planVersionId === null
1098
- ? { onboardingPlanVersionId: billingSettings.onboardingPolicy?.planVersionId ?? null }
1099
- : {})
1245
+ onboardingPolicyMode: onboardingPolicy.mode,
1246
+ onboardingPlanId: onboardingPolicy.planId
1100
1247
  },
1101
1248
  status: {
1102
1249
  allowSelfRegistration: effective.allowSelfRegistration,
@@ -1106,7 +1253,7 @@ export function registerAdminRoutes(deps) {
1106
1253
  authRateLimitMaxRequests: effective.authRateLimitMaxRequests,
1107
1254
  authRateLimitWindowMs: effective.authRateLimitWindowMs,
1108
1255
  onboardingPolicyMode: effective.onboardingPolicyMode,
1109
- onboardingPlanVersionId: effective.onboardingPlanVersionId
1256
+ onboardingPlanId: effective.onboardingPlanId
1110
1257
  }
1111
1258
  }));
1112
1259
  });
@@ -1117,7 +1264,7 @@ export function registerAdminRoutes(deps) {
1117
1264
  return;
1118
1265
  }
1119
1266
  const siteSettings = core.getGlobalSettings().site || {};
1120
- const siteBehavior = getSiteBehavior();
1267
+ const siteBehavior = buildSiteBehavior(core);
1121
1268
  res.writeHead(200, { 'Content-Type': 'application/json' });
1122
1269
  res.end(JSON.stringify({
1123
1270
  settings: {
@@ -1413,7 +1560,8 @@ export function registerAdminRoutes(deps) {
1413
1560
  workspaceAssetStore.deleteLink({
1414
1561
  workspaceId,
1415
1562
  assetId,
1416
- taskId: link.taskId,
1563
+ targetType: link.targetType,
1564
+ targetId: link.targetId,
1417
1565
  role: link.role
1418
1566
  });
1419
1567
  }
@@ -1738,14 +1886,14 @@ export function registerAdminRoutes(deps) {
1738
1886
  : body?.onboardingPolicyMode === 'require_plan_selection'
1739
1887
  ? 'require_plan_selection'
1740
1888
  : currentOnboardingPolicy.mode;
1741
- const onboardingPlanVersionId = typeof body?.onboardingPlanVersionId === 'string'
1742
- ? body.onboardingPlanVersionId.trim() || null
1743
- : body?.onboardingPlanVersionId === null
1889
+ const onboardingPlanId = typeof body?.onboardingPlanId === 'string'
1890
+ ? body.onboardingPlanId.trim().toLowerCase() || null
1891
+ : body?.onboardingPlanId === null
1744
1892
  ? null
1745
- : currentOnboardingPolicy.planVersionId;
1893
+ : currentOnboardingPolicy.planId;
1746
1894
  const validation = core.validateOnboardingPolicy({
1747
1895
  mode: onboardingPolicyMode,
1748
- planVersionId: onboardingPlanVersionId
1896
+ planId: onboardingPlanId
1749
1897
  });
1750
1898
  if (!validation.valid) {
1751
1899
  res.writeHead(400, { 'Content-Type': 'application/json' });
@@ -1766,7 +1914,7 @@ export function registerAdminRoutes(deps) {
1766
1914
  billing: {
1767
1915
  onboardingPolicy: {
1768
1916
  mode: onboardingPolicyMode,
1769
- planVersionId: onboardingPlanVersionId ?? null
1917
+ planId: onboardingPlanId ?? null
1770
1918
  }
1771
1919
  }
1772
1920
  });
@@ -1842,7 +1990,7 @@ export function registerAdminRoutes(deps) {
1842
1990
  res.end(JSON.stringify({ success: false, error: 'Valid test email address is required.' }));
1843
1991
  return;
1844
1992
  }
1845
- const config = getEmailConfig();
1993
+ const config = getRouteEmailConfig(core, context);
1846
1994
  const result = await sendEmail({
1847
1995
  to,
1848
1996
  subject: 'Taskforce email settings test',
@@ -1856,6 +2004,169 @@ export function registerAdminRoutes(deps) {
1856
2004
  res.writeHead(200, { 'Content-Type': 'application/json' });
1857
2005
  res.end(JSON.stringify({ success: true, provider: result.provider, messageId: result.messageId || null }));
1858
2006
  });
2007
+ // === SECTION: Admin auth provider settings ===
2008
+ // GET /api/taskforce/admin/auth-provider-settings - Read OAuth provider config (secrets masked)
2009
+ addRoute('GET', '/api/taskforce/admin/auth-provider-settings', async (req, res) => {
2010
+ if (!ensureAdminRole(req)) {
2011
+ res.writeHead(403, { 'Content-Type': 'application/json' });
2012
+ res.end(JSON.stringify({ error: 'Forbidden: admin role required.' }));
2013
+ return;
2014
+ }
2015
+ if (context.authConfig?.runtimeMode !== 'cloud') {
2016
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2017
+ res.end(JSON.stringify({
2018
+ status: {
2019
+ google: { enabled: false, clientId: '', hasClientSecret: false, ready: false },
2020
+ github: { enabled: false, clientId: '', hasClientSecret: false, ready: false },
2021
+ apple: { enabled: false, servicesId: '', teamId: '', keyId: '', hasPrivateKeyPem: false, ready: false }
2022
+ }
2023
+ }));
2024
+ return;
2025
+ }
2026
+ const providers = core.getGlobalSettings().oauthProviders || {};
2027
+ const g = providers.google || {};
2028
+ const gh = providers.github || {};
2029
+ const ap = providers.apple || {};
2030
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2031
+ res.end(JSON.stringify({
2032
+ status: {
2033
+ google: {
2034
+ enabled: g.enabled === true,
2035
+ clientId: String(g.clientId || ''),
2036
+ hasClientSecret: typeof g.clientSecret === 'string' && g.clientSecret.trim().length > 0,
2037
+ ready: g.enabled === true && Boolean(g.clientId) && typeof g.clientSecret === 'string' && g.clientSecret.trim().length > 0
2038
+ },
2039
+ github: {
2040
+ enabled: gh.enabled === true,
2041
+ clientId: String(gh.clientId || ''),
2042
+ hasClientSecret: typeof gh.clientSecret === 'string' && gh.clientSecret.trim().length > 0,
2043
+ ready: gh.enabled === true && Boolean(gh.clientId) && typeof gh.clientSecret === 'string' && gh.clientSecret.trim().length > 0
2044
+ },
2045
+ apple: {
2046
+ enabled: ap.enabled === true,
2047
+ servicesId: String(ap.servicesId || ''),
2048
+ teamId: String(ap.teamId || ''),
2049
+ keyId: String(ap.keyId || ''),
2050
+ hasPrivateKeyPem: typeof ap.privateKeyPem === 'string' && ap.privateKeyPem.trim().length > 0,
2051
+ ready: ap.enabled === true && Boolean(ap.servicesId) && Boolean(ap.teamId) && Boolean(ap.keyId)
2052
+ && typeof ap.privateKeyPem === 'string' && ap.privateKeyPem.trim().length > 0
2053
+ }
2054
+ }
2055
+ }));
2056
+ });
2057
+ // GET /api/taskforce/admin/auth-provider-settings/health - Per-provider readiness check
2058
+ addRoute('GET', '/api/taskforce/admin/auth-provider-settings/health', async (req, res) => {
2059
+ if (!ensureAdminRole(req)) {
2060
+ res.writeHead(403, { 'Content-Type': 'application/json' });
2061
+ res.end(JSON.stringify({ error: 'Forbidden: admin role required.' }));
2062
+ return;
2063
+ }
2064
+ const providers = core.getGlobalSettings().oauthProviders || {};
2065
+ const g = providers.google || {};
2066
+ const gh = providers.github || {};
2067
+ const ap = providers.apple || {};
2068
+ const googleReady = g.enabled === true && Boolean(g.clientId) && typeof g.clientSecret === 'string' && g.clientSecret.trim().length > 0;
2069
+ const googleReason = !g.enabled
2070
+ ? 'Provider disabled.'
2071
+ : !g.clientId ? 'Missing Client ID.'
2072
+ : !googleReady ? 'Missing Client Secret.'
2073
+ : null;
2074
+ const githubReady = gh.enabled === true && Boolean(gh.clientId) && typeof gh.clientSecret === 'string' && gh.clientSecret.trim().length > 0;
2075
+ const githubReason = !gh.enabled
2076
+ ? 'Provider disabled.'
2077
+ : !gh.clientId ? 'Missing Client ID.'
2078
+ : !githubReady ? 'Missing Client Secret.'
2079
+ : null;
2080
+ const appleBaseReady = ap.enabled === true && Boolean(ap.servicesId) && Boolean(ap.teamId) && Boolean(ap.keyId)
2081
+ && typeof ap.privateKeyPem === 'string' && ap.privateKeyPem.trim().length > 0;
2082
+ let appleSecretMintOk;
2083
+ let appleReason = null;
2084
+ if (!ap.enabled) {
2085
+ appleReason = 'Provider disabled.';
2086
+ }
2087
+ else if (!ap.servicesId || !ap.teamId || !ap.keyId) {
2088
+ appleReason = 'Missing Services ID, Team ID, or Key ID.';
2089
+ }
2090
+ else if (!appleBaseReady) {
2091
+ appleReason = 'Missing private key PEM.';
2092
+ }
2093
+ else {
2094
+ // Attempt a test-mint to verify the key is valid
2095
+ const mintResult = testMintAppleClientSecret({
2096
+ teamId: ap.teamId,
2097
+ servicesId: ap.servicesId,
2098
+ keyId: ap.keyId,
2099
+ privateKeyPem: ap.privateKeyPem
2100
+ });
2101
+ appleSecretMintOk = mintResult.ok;
2102
+ if (!mintResult.ok) {
2103
+ appleReason = `Client-secret JWT mint failed: ${mintResult.error}`;
2104
+ }
2105
+ }
2106
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2107
+ res.end(JSON.stringify({
2108
+ success: true,
2109
+ health: {
2110
+ google: { enabled: g.enabled === true, ready: googleReady, reason: googleReason },
2111
+ github: { enabled: gh.enabled === true, ready: githubReady, reason: githubReason },
2112
+ apple: {
2113
+ enabled: ap.enabled === true,
2114
+ ready: appleBaseReady && appleSecretMintOk !== false,
2115
+ reason: appleReason,
2116
+ ...(typeof appleSecretMintOk === 'boolean' ? { secretMintOk: appleSecretMintOk } : {})
2117
+ }
2118
+ }
2119
+ }));
2120
+ });
2121
+ // POST /api/taskforce/admin/auth-provider-settings - Save OAuth provider config (including secrets)
2122
+ addRoute('POST', '/api/taskforce/admin/auth-provider-settings', async (req, res) => {
2123
+ if (!ensureAdminRole(req)) {
2124
+ res.writeHead(403, { 'Content-Type': 'application/json' });
2125
+ res.end(JSON.stringify({ error: 'Forbidden: admin role required.' }));
2126
+ return;
2127
+ }
2128
+ if (!requireCloudRuntime(res, 'Auth provider settings'))
2129
+ return;
2130
+ const body = await parseJsonBody(req);
2131
+ const update = {};
2132
+ if (body?.google && typeof body.google === 'object') {
2133
+ const g = body.google;
2134
+ update.google = {
2135
+ ...(typeof g.enabled === 'boolean' ? { enabled: g.enabled } : {}),
2136
+ ...(typeof g.clientId === 'string' ? { clientId: g.clientId } : {}),
2137
+ ...(typeof g.clientSecret === 'string' ? { clientSecret: g.clientSecret } : {})
2138
+ };
2139
+ }
2140
+ if (body?.github && typeof body.github === 'object') {
2141
+ const gh = body.github;
2142
+ update.github = {
2143
+ ...(typeof gh.enabled === 'boolean' ? { enabled: gh.enabled } : {}),
2144
+ ...(typeof gh.clientId === 'string' ? { clientId: gh.clientId } : {}),
2145
+ ...(typeof gh.clientSecret === 'string' ? { clientSecret: gh.clientSecret } : {})
2146
+ };
2147
+ }
2148
+ if (body?.apple && typeof body.apple === 'object') {
2149
+ const ap = body.apple;
2150
+ update.apple = {
2151
+ ...(typeof ap.enabled === 'boolean' ? { enabled: ap.enabled } : {}),
2152
+ ...(typeof ap.servicesId === 'string' ? { servicesId: ap.servicesId } : {}),
2153
+ ...(typeof ap.teamId === 'string' ? { teamId: ap.teamId } : {}),
2154
+ ...(typeof ap.keyId === 'string' ? { keyId: ap.keyId } : {}),
2155
+ ...(typeof ap.privateKeyPem === 'string' ? { privateKeyPem: ap.privateKeyPem } : {})
2156
+ };
2157
+ }
2158
+ if (Object.keys(update).length === 0) {
2159
+ res.writeHead(400, { 'Content-Type': 'application/json' });
2160
+ res.end(JSON.stringify({ error: 'No provider settings provided.' }));
2161
+ return;
2162
+ }
2163
+ core.updateGlobalSettings({ oauthProviders: update });
2164
+ auditAdminAction(req, 'auth-provider-settings.update', {
2165
+ providers: Object.keys(update)
2166
+ });
2167
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2168
+ res.end(JSON.stringify({ success: true }));
2169
+ });
1859
2170
  // === SECTION: Cloud MCP HTTP transport ===
1860
2171
  addRoute('POST', '/mcp', async (req, res) => { await handleCloudMcpRequest(req, res); });
1861
2172
  addRoute('POST', '/mcp/', async (req, res) => { await handleCloudMcpRequest(req, res); });
@@ -2267,9 +2578,28 @@ export function registerAdminRoutes(deps) {
2267
2578
  res.end(JSON.stringify({ success: false, error: 'Token is missing required scopes.', code: 'MCP_TOKEN_MISSING_SCOPES', details: { requiredScopes, missingScopes, grantedScopes: inspection.scopes } }));
2268
2579
  return;
2269
2580
  }
2581
+ const probe = await probeCloudMcpEndpoint(req, token);
2582
+ if (!probe.ok) {
2583
+ res.writeHead(probe.statusCode, { 'Content-Type': 'application/json' });
2584
+ res.end(JSON.stringify({ success: false, error: probe.error, code: probe.code, details: probe.details }));
2585
+ return;
2586
+ }
2270
2587
  const workspace = core.getWorkspaceProfile(wid);
2271
2588
  res.writeHead(200, { 'Content-Type': 'application/json' });
2272
- res.end(JSON.stringify({ success: true, result: { tokenId: inspection.tokenId, tokenName: inspection.name, workspaceId: wid, workspaceName: String(workspace?.name || wid), scopes: inspection.scopes, requiredScopes, endpoint: '/mcp' } }));
2589
+ res.end(JSON.stringify({
2590
+ success: true,
2591
+ result: {
2592
+ tokenId: inspection.tokenId,
2593
+ tokenName: inspection.name,
2594
+ workspaceId: wid,
2595
+ workspaceName: String(workspace?.name || wid),
2596
+ scopes: inspection.scopes,
2597
+ requiredScopes,
2598
+ endpoint: probe.endpoint,
2599
+ serverName: probe.serverName,
2600
+ protocolVersion: probe.protocolVersion
2601
+ }
2602
+ }));
2273
2603
  }
2274
2604
  catch (error) {
2275
2605
  const statusCode = resolveErrorStatusCode(error);