@taskforcehq/taskforce 0.3.305 → 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 (337) hide show
  1. package/dist/Taskforce.module.css +295 -89
  2. package/dist/TaskforceCore.js +215 -43
  3. package/dist/TaskforceCore.test.js +433 -28
  4. package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
  5. package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  6. package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
  7. package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
  8. package/dist/assets/fonts/Noir_medium.woff2 +0 -0
  9. package/dist/assets/fonts/Noir_regular.woff2 +0 -0
  10. package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
  11. package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
  12. package/dist/compat/workspaceSyncCompat.js +46 -2
  13. package/dist/compat/workspaceSyncCompat.test.js +38 -1
  14. package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
  15. package/dist/components/context/ContextAttachmentManager.js +549 -0
  16. package/dist/components/features/AgentsModule.d.ts +10 -1
  17. package/dist/components/features/AgentsModule.js +260 -15
  18. package/dist/components/features/AgentsModule.test.js +255 -5
  19. package/dist/components/features/DocumentViewer.d.ts +5 -1
  20. package/dist/components/features/DocumentViewer.js +60 -3
  21. package/dist/components/features/DocumentWorkspace.js +1 -1
  22. package/dist/components/features/DocumentWorkspace.test.js +33 -0
  23. package/dist/components/features/documentWorkspaceModel.d.ts +1 -1
  24. package/dist/components/task/TaskCard.js +19 -28
  25. package/dist/components/task/TaskCard.test.js +38 -3
  26. package/dist/components/task/TaskContextUpload.js +4 -526
  27. package/dist/components/task/TaskForm.js +85 -90
  28. package/dist/components/task/TaskForm.test.js +110 -8
  29. package/dist/components/task/TaskKanban.d.ts +2 -1
  30. package/dist/components/task/TaskKanban.js +94 -13
  31. package/dist/components/task/TaskKanban.test.js +36 -0
  32. package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
  33. package/dist/components/ui/EditableAvatarButton.js +18 -0
  34. package/dist/components/views/PlanComparisonPage.d.ts +9 -1
  35. package/dist/components/views/PlanComparisonPage.js +70 -14
  36. package/dist/components/views/PlanComparisonPage.test.js +238 -0
  37. package/dist/components/views/PlansPage.js +127 -36
  38. package/dist/components/views/PlansPage.test.js +294 -12
  39. package/dist/components/views/StandaloneLayout.js +283 -533
  40. package/dist/components/views/panels/PlanningDrawer.d.ts +8 -1
  41. package/dist/components/views/panels/PlanningDrawer.js +18 -11
  42. package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
  43. package/dist/components/views/panels/PlanningDrawer.test.js +141 -0
  44. package/dist/components/views/planningScope.d.ts +20 -0
  45. package/dist/components/views/planningScope.js +25 -0
  46. package/dist/components/views/planningScope.test.d.ts +1 -0
  47. package/dist/components/views/planningScope.test.js +60 -0
  48. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +26 -1
  49. package/dist/components/views/standalone/modals/AccountHubModal.js +76 -2
  50. package/dist/components/views/standalone/modals/AccountHubModal.test.js +20 -3
  51. package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
  52. package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
  53. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
  54. package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
  55. package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
  56. package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
  57. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
  58. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
  59. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
  60. package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
  61. package/dist/core/AiProfileService.d.ts +39 -2
  62. package/dist/core/AiProfileService.js +639 -73
  63. package/dist/core/AiProfileService.test.js +19 -1
  64. package/dist/core/AiProfiles.test.js +566 -7
  65. package/dist/core/AttachmentLinkService.js +57 -15
  66. package/dist/core/AuthIdentityService.d.ts +107 -0
  67. package/dist/core/AuthIdentityService.js +284 -0
  68. package/dist/core/AuthTokenService.d.ts +4 -0
  69. package/dist/core/AuthTokenService.js +34 -6
  70. package/dist/core/AuthTokenService.test.js +3 -3
  71. package/dist/core/EntitlementsPolicy.test.js +2 -2
  72. package/dist/core/GlobalSettingsService.js +115 -6
  73. package/dist/core/PlanEntitlementService.d.ts +23 -2
  74. package/dist/core/PlanEntitlementService.js +343 -57
  75. package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
  76. package/dist/core/PlanFeatureCatalog.test.js +84 -0
  77. package/dist/core/PlanVersionPolicy.test.js +35 -2
  78. package/dist/core/PlanningEntities.test.js +52 -0
  79. package/dist/core/SignupMonetizationSettings.test.js +199 -27
  80. package/dist/core/SyncReconciliationService.js +2 -65
  81. package/dist/core/SystemAdmin.test.js +42 -42
  82. package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
  83. package/dist/core/Taskforce.d.ts +225 -14
  84. package/dist/core/Taskforce.js +765 -192
  85. package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
  86. package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
  87. package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
  88. package/dist/core/WorkspaceLifecycleService.js +0 -5
  89. package/dist/core/WorkspacePermissions.test.js +25 -1
  90. package/dist/core/WorkspacePlanMode.test.js +117 -16
  91. package/dist/core/shared.d.ts +1 -1
  92. package/dist/core/shared.js +5 -1
  93. package/dist/core/types.d.ts +68 -2
  94. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -1
  95. package/dist/hooks/sync/bootstrap.js +2 -0
  96. package/dist/hooks/sync/controlPlane.d.ts +37 -0
  97. package/dist/hooks/sync/controlPlane.js +78 -0
  98. package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
  99. package/dist/hooks/sync/controlPlane.test.js +121 -0
  100. package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
  101. package/dist/hooks/sync/lifecyclePolicy.js +93 -0
  102. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
  103. package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
  104. package/dist/hooks/sync/orchestratorShared.d.ts +10 -1
  105. package/dist/hooks/sync/orchestratorShared.js +68 -47
  106. package/dist/hooks/sync/orchestratorShared.test.js +50 -1
  107. package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
  108. package/dist/hooks/sync/pullLifecycle.js +24 -0
  109. package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
  110. package/dist/hooks/sync/pullLifecycle.test.js +80 -0
  111. package/dist/hooks/sync/recovery.d.ts +16 -2
  112. package/dist/hooks/sync/recovery.js +171 -53
  113. package/dist/hooks/sync/recovery.test.d.ts +1 -0
  114. package/dist/hooks/sync/recovery.test.js +292 -0
  115. package/dist/hooks/sync/transfers.d.ts +15 -3
  116. package/dist/hooks/sync/transfers.js +179 -62
  117. package/dist/hooks/sync/transfers.test.d.ts +1 -0
  118. package/dist/hooks/sync/transfers.test.js +396 -0
  119. package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
  120. package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
  121. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
  122. package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
  123. package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
  124. package/dist/hooks/sync/useSyncStatusActions.js +88 -0
  125. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
  126. package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
  127. package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
  128. package/dist/hooks/sync/useSyncStatusControls.js +60 -0
  129. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
  130. package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
  131. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
  132. package/dist/hooks/useSyncOrchestrator.d.ts +9 -2
  133. package/dist/hooks/useSyncOrchestrator.js +359 -178
  134. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +407 -0
  135. package/dist/hooks/useTaskData.js +8 -3
  136. package/dist/hooks/useTaskData.test.js +45 -0
  137. package/dist/hooks/useTaskforce.d.ts +60 -2
  138. package/dist/hooks/useTaskforce.js +549 -240
  139. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
  140. package/dist/hooks/useTaskforce.runtime-routing.test.js +152 -0
  141. package/dist/hooks/useTaskforce.sync-behavior.test.js +2380 -212
  142. package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
  143. package/dist/hooks/useWorkspaceSyncController.js +6 -1
  144. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
  145. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +316 -35
  146. package/dist/localization/locales/en-US.d.ts +0 -1
  147. package/dist/localization/locales/en-US.js +0 -1
  148. package/dist/localization/locales/es-419.js +0 -1
  149. package/dist/localization/locales/pt-BR.js +0 -1
  150. package/dist/mcp/documentAssetRegistrar.js +3 -3
  151. package/dist/mcp/runtime.d.ts +2 -0
  152. package/dist/mcp/runtime.js +131 -64
  153. package/dist/mcp/runtime.test.js +291 -12
  154. package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
  155. package/dist/migrations/taskSchemaMigrations.js +171 -61
  156. package/dist/migrations/taskSchemaMigrations.test.js +15 -0
  157. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  158. package/dist/resources/templates/workflows/collaborate.yaml +1 -1
  159. package/dist/resources/templates/workflows/evaluate.yaml +2 -2
  160. package/dist/resources/templates/workflows/plan.yaml +1 -1
  161. package/dist/resources/templates/workflows/review.yaml +2 -2
  162. package/dist/server/annotatedAttachmentsRoutes.test.js +3 -3
  163. package/dist/server/auth/providers/apple.d.ts +33 -0
  164. package/dist/server/auth/providers/apple.js +82 -0
  165. package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
  166. package/dist/server/auth/providers/appleClientSecret.js +65 -0
  167. package/dist/server/auth/providers/github.d.ts +26 -0
  168. package/dist/server/auth/providers/github.js +86 -0
  169. package/dist/server/auth/providers/google.d.ts +21 -0
  170. package/dist/server/auth/providers/google.js +56 -0
  171. package/dist/server/auth/providers/types.d.ts +21 -0
  172. package/dist/server/auth/providers/types.js +1 -0
  173. package/dist/server/auth.d.ts +2 -0
  174. package/dist/server/auth.js +6 -3
  175. package/dist/server/documentReviewRoutes.test.js +36 -3
  176. package/dist/server/index.cookieProxy.test.js +1 -0
  177. package/dist/server/index.d.ts +12 -0
  178. package/dist/server/index.js +120 -9
  179. package/dist/server/index.rateLimit.test.js +3 -0
  180. package/dist/server/index.test.js +106 -3
  181. package/dist/server/routes/admin.d.ts +3 -0
  182. package/dist/server/routes/admin.js +272 -19
  183. package/dist/server/routes/auth.d.ts +15 -2
  184. package/dist/server/routes/auth.js +994 -57
  185. package/dist/server/routes/authSupport.d.ts +1 -1
  186. package/dist/server/routes/authSupport.js +1 -1
  187. package/dist/server/routes/billing.js +361 -35
  188. package/dist/server/routes/billing.test.js +592 -12
  189. package/dist/server/routes/documents.d.ts +1 -0
  190. package/dist/server/routes/documents.js +172 -70
  191. package/dist/server/routes/shared.d.ts +28 -2
  192. package/dist/server/routes/shared.js +69 -4
  193. package/dist/server/routes/sync.integration.test.js +437 -39
  194. package/dist/server/routes/sync.js +61 -605
  195. package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
  196. package/dist/server/routes/syncAuxRoutes.js +91 -0
  197. package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
  198. package/dist/server/routes/syncAuxRoutes.test.js +158 -0
  199. package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
  200. package/dist/server/routes/syncPullApplyRoutes.js +204 -0
  201. package/dist/server/routes/syncPushRoutes.d.ts +24 -0
  202. package/dist/server/routes/syncPushRoutes.js +212 -0
  203. package/dist/server/routes/syncRouteGuards.d.ts +36 -0
  204. package/dist/server/routes/syncRouteGuards.js +67 -0
  205. package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
  206. package/dist/server/routes/syncRouteGuards.test.js +94 -0
  207. package/dist/server/routes/syncRouteTypes.d.ts +13 -0
  208. package/dist/server/routes/syncRouteTypes.js +1 -0
  209. package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
  210. package/dist/server/routes/syncSnapshotRoutes.js +180 -0
  211. package/dist/server/routes/tasks.js +43 -1
  212. package/dist/server/routes/workspaces.js +36 -5
  213. package/dist/server/routes.js +60 -3
  214. package/dist/server/routes.test.js +1824 -171
  215. package/dist/services/workflowExportSnapshots.test.js +2 -0
  216. package/dist/shared/aiProfileSeatScope.d.ts +5 -0
  217. package/dist/shared/aiProfileSeatScope.js +21 -0
  218. package/dist/shared/passwordPolicy.d.ts +13 -0
  219. package/dist/shared/passwordPolicy.js +84 -0
  220. package/dist/shared/passwordPolicy.test.d.ts +1 -0
  221. package/dist/shared/passwordPolicy.test.js +34 -0
  222. package/dist/storage/workspaceAssetStore.d.ts +15 -3
  223. package/dist/storage/workspaceAssetStore.js +132 -44
  224. package/dist/storage/workspaceAssetStore.test.js +93 -6
  225. package/dist/styles/fonts.css +70 -0
  226. package/dist/sync/collaborationSyncPayload.d.ts +6 -1
  227. package/dist/sync/collaborationSyncPayload.js +17 -11
  228. package/dist/sync/collaborationSyncPayload.test.js +13 -1
  229. package/dist/sync/contentSyncState.d.ts +4 -8
  230. package/dist/sync/contentSyncState.js +17 -70
  231. package/dist/sync/syncApplyHandlers.d.ts +7 -0
  232. package/dist/sync/syncApplyHandlers.js +28 -13
  233. package/dist/sync/syncApplyHandlers.test.js +70 -3
  234. package/dist/sync/syncFieldSemantics.d.ts +12 -0
  235. package/dist/sync/syncFieldSemantics.js +55 -0
  236. package/dist/sync/syncResourceAdapters.d.ts +44 -0
  237. package/dist/sync/syncResourceAdapters.js +77 -0
  238. package/dist/sync/syncService.d.ts +5 -0
  239. package/dist/sync/syncService.js +52 -0
  240. package/dist/sync/syncService.test.js +57 -3
  241. package/dist/sync/taskSyncChangeKey.d.ts +2 -0
  242. package/dist/sync/taskSyncChangeKey.js +143 -0
  243. package/dist/sync/taskSyncPayload.js +3 -8
  244. package/dist/sync/workspacePullFeed.js +4 -3
  245. package/dist/sync/workspacePullFeed.test.js +43 -0
  246. package/dist/sync/workspaceRepair.js +5 -2
  247. package/dist/sync/workspaceSyncModel.d.ts +45 -0
  248. package/dist/sync/workspaceSyncModel.js +183 -28
  249. package/dist/sync/workspaceSyncModel.test.js +291 -12
  250. package/dist/sync/workspaceSyncState.d.ts +17 -0
  251. package/dist/sync/workspaceSyncState.js +2 -1
  252. package/dist/sync/workspaceSyncSurface.js +3 -8
  253. package/dist/types.d.ts +17 -0
  254. package/dist/ui/assets/AgentsModule-DrLawwNl.js +1 -0
  255. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BG6P_GVW.js → AnnotatedAttachmentWorkspace-Z9_whf7F.js} +1 -1
  256. package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
  257. package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
  258. package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
  259. package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
  260. package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +252 -0
  261. package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +1 -0
  262. package/dist/ui/assets/{InitiativesModule-Dhm7M8e7.js → InitiativesModule-9E6ParrY.js} +1 -1
  263. package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
  264. package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
  265. package/dist/ui/assets/PlansPage-CvfnMiWq.css +1 -0
  266. package/dist/ui/assets/PlansPage-DRXscYxH.js +1 -0
  267. package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
  268. package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
  269. package/dist/ui/assets/{TaskSettings-ln0aF7xc.js → TaskSettings-DZX4jk7e.js} +1 -1
  270. package/dist/ui/assets/{WorkflowsModule-BcS4afRn.js → WorkflowsModule-BA7jcEpH.js} +1 -1
  271. package/dist/ui/assets/index-9eT8e0Lu.css +1 -0
  272. package/dist/ui/assets/index-C6k75jr8.js +6 -0
  273. package/dist/ui/assets/{vendor-icons-BSUaRwF8.js → vendor-icons-DuEd_65c.js} +1 -1
  274. package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
  275. package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
  276. package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
  277. package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  278. package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
  279. package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
  280. package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
  281. package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
  282. package/dist/ui/fonts/Noir_medium.otf +0 -0
  283. package/dist/ui/fonts/Noir_medium.woff2 +0 -0
  284. package/dist/ui/fonts/Noir_regular.otf +0 -0
  285. package/dist/ui/fonts/Noir_regular.woff2 +0 -0
  286. package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
  287. package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
  288. package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
  289. package/dist/ui/index.html +3 -3
  290. package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
  291. package/dist/utils/assignees.d.ts +2 -0
  292. package/dist/utils/assignees.js +2 -0
  293. package/dist/utils/avatarUpload.d.ts +27 -0
  294. package/dist/utils/avatarUpload.js +132 -0
  295. package/dist/utils/avatarUpload.test.d.ts +1 -0
  296. package/dist/utils/avatarUpload.test.js +40 -0
  297. package/dist/utils/bootstrapTrace.d.ts +7 -0
  298. package/dist/utils/bootstrapTrace.js +29 -0
  299. package/dist/utils/commercialLifecyclePresentation.js +3 -0
  300. package/dist/utils/contextAssetEvents.d.ts +2 -0
  301. package/dist/utils/syncEventDetails.d.ts +3 -0
  302. package/dist/utils/syncEventDetails.js +64 -0
  303. package/dist/utils/syncEventDetails.test.d.ts +1 -0
  304. package/dist/utils/syncEventDetails.test.js +23 -0
  305. package/dist/utils/syncEventPresentation.d.ts +15 -0
  306. package/dist/utils/syncEventPresentation.js +129 -0
  307. package/dist/utils/syncEventPresentation.test.d.ts +1 -0
  308. package/dist/utils/syncEventPresentation.test.js +64 -0
  309. package/dist/utils/syncStatusPresentation.d.ts +72 -0
  310. package/dist/utils/syncStatusPresentation.js +217 -0
  311. package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
  312. package/dist/utils/syncStatusPresentation.test.js +164 -0
  313. package/dist/utils/taskActivity.d.ts +2 -0
  314. package/dist/utils/taskActivity.js +228 -22
  315. package/dist/utils/taskActivity.test.js +119 -8
  316. package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
  317. package/dist/utils/workspaceSyncPresentation.js +68 -2
  318. package/dist/utils/workspaceSyncPresentation.test.js +36 -1
  319. package/package.json +3 -2
  320. package/scripts/export-sqlite-to-postgres.mjs +7 -9
  321. package/scripts/run-e2e-realtime.sh +17 -3
  322. package/scripts/run-smoke.sh +17 -5
  323. package/scripts/run-soak.sh +17 -5
  324. package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  325. package/src/resources/templates/workflows/collaborate.yaml +1 -1
  326. package/src/resources/templates/workflows/evaluate.yaml +2 -2
  327. package/src/resources/templates/workflows/plan.yaml +1 -1
  328. package/src/resources/templates/workflows/review.yaml +2 -2
  329. package/dist/ui/assets/AgentsModule-N2MnQBZk.js +0 -1
  330. package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +0 -1
  331. package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +0 -252
  332. package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
  333. package/dist/ui/assets/PlansPage-Cq0zXj3I.js +0 -1
  334. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  335. package/dist/ui/assets/index-C2-OXZV7.css +0 -1
  336. package/dist/ui/assets/index-DcSI5F86.js +0 -6
  337. package/scripts/migrate-planning-model.ts +0 -233
@@ -15,6 +15,7 @@
15
15
  * GET /api/taskforce/context-metadata
16
16
  * POST /api/taskforce/context/purge-expired
17
17
  * GET /api/taskforce/documents
18
+ * POST /api/taskforce/documents/:assetId/attach
18
19
  * DELETE /api/taskforce/documents/:assetId
19
20
  * PATCH /api/taskforce/documents/:assetId
20
21
  * POST /api/taskforce/documents/save
@@ -15,6 +15,7 @@
15
15
  * GET /api/taskforce/context-metadata
16
16
  * POST /api/taskforce/context/purge-expired
17
17
  * GET /api/taskforce/documents
18
+ * POST /api/taskforce/documents/:assetId/attach
18
19
  * DELETE /api/taskforce/documents/:assetId
19
20
  * PATCH /api/taskforce/documents/:assetId
20
21
  * POST /api/taskforce/documents/save
@@ -30,6 +31,75 @@ import { parsePlan } from '../../utils/planParser.js';
30
31
  import { getCanonicalDocumentName, getReadableDocumentName, isGeneratedDocumentName } from '../../utils/documentNames.js';
31
32
  import { requestRuntimeMode } from './shared.js';
32
33
  import { parseJsonBody } from './primitives.js';
34
+ import { resolveDeploymentConfig } from '../../config/deployment.js';
35
+ function isUserProfileAvatarPath(relativePath) {
36
+ return /^users\/[^/]+\/profile\/avatar\/[^/]+$/i.test(String(relativePath || '').replace(/\\/g, '/'));
37
+ }
38
+ function isAiProfileAvatarPath(relativePath) {
39
+ return /^workspaces\/[^/]+\/assets\/images\/ai-profiles\/[^/]+\/avatar\/[^/]+$/i.test(String(relativePath || '').replace(/\\/g, '/'));
40
+ }
41
+ function resolveRequestOrigin(req) {
42
+ const host = String(req.headers.host || '').trim();
43
+ if (!host)
44
+ return '';
45
+ const proto = String(req.headers['x-forwarded-proto'] || '').trim().toLowerCase() === 'https'
46
+ ? 'https'
47
+ : (req.socket?.encrypted ? 'https' : 'http');
48
+ return `${proto}://${host.replace(/\/+$/, '')}`;
49
+ }
50
+ async function proxyCloudUserProfileAvatar(req, res, relativePath, contentTypeForExt) {
51
+ if (!isUserProfileAvatarPath(relativePath))
52
+ return false;
53
+ const deployment = resolveDeploymentConfig(process.env);
54
+ const cloudBaseUrl = String(deployment.cloudBaseUrl || deployment.cloudAuthBaseUrl || '').trim().replace(/\/+$/, '');
55
+ if (!cloudBaseUrl)
56
+ return false;
57
+ if (cloudBaseUrl === resolveRequestOrigin(req))
58
+ return false;
59
+ const requestUrl = new URL(req.url || '', 'http://localhost');
60
+ const upstreamUrl = `${cloudBaseUrl}/api/taskforce/context/${encodeURIComponent(relativePath)}${requestUrl.search || ''}`;
61
+ const headers = new Headers();
62
+ const cookie = String(req.headers.cookie || '').trim();
63
+ const accept = String(req.headers.accept || '').trim();
64
+ const userAgent = String(req.headers['user-agent'] || '').trim();
65
+ if (cookie)
66
+ headers.set('cookie', cookie);
67
+ if (accept)
68
+ headers.set('accept', accept);
69
+ if (userAgent)
70
+ headers.set('user-agent', userAgent);
71
+ headers.set('x-taskforce-origin-runtime', 'local-proxy');
72
+ const cfClientId = String(process.env.TASKFORCE_CF_ACCESS_CLIENT_ID || process.env.CF_ACCESS_CLIENT_ID || '').trim();
73
+ const cfClientSecret = String(process.env.TASKFORCE_CF_ACCESS_CLIENT_SECRET || process.env.CF_ACCESS_CLIENT_SECRET || '').trim();
74
+ if (cfClientId && cfClientSecret) {
75
+ headers.set('CF-Access-Client-Id', cfClientId);
76
+ headers.set('CF-Access-Client-Secret', cfClientSecret);
77
+ }
78
+ try {
79
+ const upstream = await fetch(upstreamUrl, {
80
+ method: 'GET',
81
+ headers
82
+ });
83
+ if (upstream.status === 404)
84
+ return false;
85
+ const outgoingHeaders = {};
86
+ for (const [key, value] of upstream.headers.entries()) {
87
+ const name = key.toLowerCase();
88
+ if (name === 'set-cookie' || name === 'content-length' || name === 'transfer-encoding')
89
+ continue;
90
+ outgoingHeaders[key] = value;
91
+ }
92
+ if (!outgoingHeaders['content-type']) {
93
+ outgoingHeaders['Content-Type'] = contentTypeForExt(path.extname(relativePath).toLowerCase());
94
+ }
95
+ res.writeHead(upstream.status, outgoingHeaders);
96
+ res.end(Buffer.from(await upstream.arrayBuffer()));
97
+ return true;
98
+ }
99
+ catch {
100
+ return false;
101
+ }
102
+ }
33
103
  export function registerDocumentRoutes(deps) {
34
104
  const { addRoute, core, context, requestWorkspaceId, ensureAdminRole, resolveEffectiveObjectStorageConfig, getObjectStorageClient, resolveStorageWorkspaceId, ensureContextUploadAllowed, parseStorageFolderSegments, buildCanonicalWorkspaceAssetRelativePath, inferAssetKindFromPath, appendStorageTelemetry, scanAndRecordDocument, buildTaskAttachmentPayload, workspaceAssetStore, taskAssetStore, normalizeDocumentPathInput, getAssetMetadata, getCanonicalAssetMetadata, documentRetentionDays, appendDocumentPurgeHistory, markAssetDeleted, isAssetDeleted, clearAssetDeleted, ensureWithinDir, sanitizeDownloadFilename, contentTypeForExt, inferInlineDocumentFromPath, resolveCanonicalDocumentRead, inferWorkspaceIdFromStoragePath, inferTaskIdFromStoragePath, } = deps;
35
105
  const removeDocumentAttachmentFromTaskAttachments = (attachmentsRaw, asset) => {
@@ -396,9 +466,14 @@ export function registerDocumentRoutes(deps) {
396
466
  return;
397
467
  }
398
468
  if (objectStorageClient && normalizedRelative && resolvedObjectStorage.provider === 'r2' && resolvedObjectStorage.r2) {
399
- if (shouldProxyInlineDocument) {
469
+ const shouldProxyInlineUserAvatar = isUserProfileAvatarPath(normalizedRelative);
470
+ const shouldProxyInlineAiAvatar = isAiProfileAvatarPath(normalizedRelative);
471
+ if (shouldProxyInlineDocument || shouldProxyInlineUserAvatar || shouldProxyInlineAiAvatar) {
400
472
  const object = await objectStorageClient.getObject(normalizedRelative);
401
473
  if (!object) {
474
+ if (shouldProxyInlineUserAvatar && await proxyCloudUserProfileAvatar(req, res, normalizedRelative, contentTypeForExt)) {
475
+ return;
476
+ }
402
477
  res.writeHead(404, { 'Content-Type': 'text/plain' });
403
478
  res.end('Context file not found');
404
479
  return;
@@ -423,6 +498,9 @@ export function registerDocumentRoutes(deps) {
423
498
  res.end(fs.readFileSync(filePath));
424
499
  return;
425
500
  }
501
+ if (normalizedRelative && await proxyCloudUserProfileAvatar(req, res, normalizedRelative, contentTypeForExt)) {
502
+ return;
503
+ }
426
504
  res.writeHead(404, { 'Content-Type': 'text/plain' });
427
505
  res.end('Context file not found');
428
506
  });
@@ -590,9 +668,6 @@ export function registerDocumentRoutes(deps) {
590
668
  });
591
669
  addRoute('GET', '/api/taskforce/documents', async (req, res) => {
592
670
  const workspaceId = requestWorkspaceId(req);
593
- const { basePath, projectRoot } = core.getPaths();
594
- const planningDocsDir = path.join(basePath, 'docs');
595
- const runtimeMode = context.authConfig?.runtimeMode === 'cloud' ? 'cloud' : 'local';
596
671
  function inferDocType(filename) {
597
672
  const base = path.basename(filename, path.extname(filename)).toLowerCase();
598
673
  if (base.includes('implementation-plan') || base.includes('implementation_plan'))
@@ -630,12 +705,9 @@ export function registerDocumentRoutes(deps) {
630
705
  : (taskId ? 1 : 0);
631
706
  const attachmentState = input.attachmentState === 'attached'
632
707
  || input.attachmentState === 'unattached'
633
- || input.attachmentState === 'standalone'
634
708
  ? (attachmentCount > 0 || taskId
635
709
  ? 'attached'
636
- : input.attachmentState === 'standalone'
637
- ? 'standalone'
638
- : 'unattached')
710
+ : 'unattached')
639
711
  : (attachmentCount > 0 || taskId ? 'attached' : 'unattached');
640
712
  return {
641
713
  id: input.id,
@@ -658,12 +730,6 @@ export function registerDocumentRoutes(deps) {
658
730
  attachmentState
659
731
  };
660
732
  }
661
- function safeRelative(fullPath) {
662
- const rel = path.relative(projectRoot, fullPath);
663
- if (rel.startsWith('..'))
664
- return null;
665
- return rel;
666
- }
667
733
  const documents = [];
668
734
  const seenDocumentPaths = new Set();
669
735
  const pushDocument = (doc) => {
@@ -674,6 +740,9 @@ export function registerDocumentRoutes(deps) {
674
740
  seenDocumentPaths.add(key);
675
741
  documents.push(normalizedDoc);
676
742
  };
743
+ // Shared document listing is canonical-document-only. Raw files under
744
+ // .taskforce/docs are intentionally excluded because they are not
745
+ // workspace-scoped managed documents.
677
746
  const canonicalDocuments = workspaceAssetStore?.listAllByWorkspace(workspaceId, {
678
747
  includeDeleted: false,
679
748
  kind: 'document'
@@ -689,8 +758,11 @@ export function registerDocumentRoutes(deps) {
689
758
  });
690
759
  const activeLinks = (workspaceAssetStore?.listLinksForAsset(asset.assetId, workspaceId) || [])
691
760
  .filter((link) => !link.deletedAt)
761
+ .filter((link) => link.targetType !== 'task' || (!!link.taskId && !!core.getTask(link.taskId, workspaceId)));
762
+ const activeTaskLinks = activeLinks
763
+ .filter((link) => link.targetType === 'task' && !!link.taskId)
692
764
  .filter((link) => !!core.getTask(link.taskId, workspaceId));
693
- const firstLink = activeLinks[0] || null;
765
+ const firstLink = activeTaskLinks[0] || null;
694
766
  const repairedLogicalName = isGeneratedDocumentName(asset.logicalName || null, asset.assetId || null)
695
767
  ? (String(firstLink?.displayName || '').trim()
696
768
  || repairedReadableName)
@@ -742,58 +814,6 @@ export function registerDocumentRoutes(deps) {
742
814
  attachmentState: activeLinks.length > 0 ? 'attached' : 'unattached',
743
815
  });
744
816
  }
745
- if (runtimeMode !== 'cloud') {
746
- const walkMarkdownFiles = (dir, visitor) => {
747
- let entries = [];
748
- try {
749
- entries = fs.readdirSync(dir, { withFileTypes: true });
750
- }
751
- catch {
752
- return;
753
- }
754
- for (const entry of entries) {
755
- const fullPath = path.join(dir, entry.name);
756
- if (entry.isDirectory()) {
757
- walkMarkdownFiles(fullPath, visitor);
758
- continue;
759
- }
760
- if (!entry.isFile() || path.extname(entry.name).toLowerCase() !== '.md') {
761
- continue;
762
- }
763
- let stat;
764
- try {
765
- stat = fs.statSync(fullPath);
766
- }
767
- catch {
768
- continue;
769
- }
770
- const relativePath = safeRelative(fullPath);
771
- if (!relativePath)
772
- continue;
773
- visitor({ relativePath, entryName: entry.name, stat });
774
- }
775
- };
776
- walkMarkdownFiles(planningDocsDir, ({ relativePath, entryName, stat }) => {
777
- pushDocument({
778
- id: relativePath,
779
- assetId: null,
780
- referenceNumber: null,
781
- referenceLabel: null,
782
- fsPath: relativePath,
783
- apiUrl: `/api/taskforce/context-link?path=${encodeURIComponent(relativePath)}`,
784
- taskId: null,
785
- displayName: inferTitle(entryName),
786
- originalFilename: entryName,
787
- title: inferTitle(entryName),
788
- docType: 'planning',
789
- sizeBytes: stat.size,
790
- updatedAt: stat.mtime.toISOString(),
791
- editable: true,
792
- attachmentCount: 0,
793
- attachmentState: 'standalone',
794
- });
795
- });
796
- }
797
817
  documents.sort((a, b) => {
798
818
  if (!a.updatedAt && !b.updatedAt)
799
819
  return 0;
@@ -806,6 +826,85 @@ export function registerDocumentRoutes(deps) {
806
826
  res.writeHead(200, { 'Content-Type': 'application/json' });
807
827
  res.end(JSON.stringify({ documents }));
808
828
  });
829
+ addRoute('POST', '/api/taskforce/documents/:assetId/attach', async (req, res, params) => {
830
+ const workspaceId = requestWorkspaceId(req);
831
+ const assetId = String(params.assetId || '').trim();
832
+ const body = await parseJsonBody(req);
833
+ const targetType = String(body?.targetType || 'task').trim();
834
+ const targetId = String(body?.targetId || '').trim();
835
+ if (!workspaceAssetStore || !assetId) {
836
+ res.writeHead(404, { 'Content-Type': 'application/json' });
837
+ res.end(JSON.stringify({ error: 'Document not found.' }));
838
+ return;
839
+ }
840
+ if (!targetId) {
841
+ res.writeHead(400, { 'Content-Type': 'application/json' });
842
+ res.end(JSON.stringify({ error: 'Missing required targetId.' }));
843
+ return;
844
+ }
845
+ if (targetType !== 'task') {
846
+ res.writeHead(400, { 'Content-Type': 'application/json' });
847
+ res.end(JSON.stringify({ error: 'Only task attachments are supported from the document manager right now.' }));
848
+ return;
849
+ }
850
+ const asset = workspaceAssetStore.get(assetId, workspaceId);
851
+ if (!asset || asset.kind !== 'document' || asset.deletedAt) {
852
+ res.writeHead(404, { 'Content-Type': 'application/json' });
853
+ res.end(JSON.stringify({ error: 'Document not found.' }));
854
+ return;
855
+ }
856
+ const task = core.getTask(targetId, workspaceId);
857
+ if (!task) {
858
+ res.writeHead(404, { 'Content-Type': 'application/json' });
859
+ res.end(JSON.stringify({ error: 'Task not found.' }));
860
+ return;
861
+ }
862
+ const existingLinks = workspaceAssetStore.listLinksForTarget('task', targetId, workspaceId)
863
+ .filter((link) => !link.deletedAt);
864
+ const alreadyAttached = existingLinks.some((link) => link.assetId === asset.assetId && link.role === 'attachment');
865
+ if (alreadyAttached) {
866
+ res.writeHead(200, { 'Content-Type': 'application/json' });
867
+ res.end(JSON.stringify({
868
+ success: true,
869
+ alreadyAttached: true,
870
+ targetType: 'task',
871
+ targetId,
872
+ attachmentCount: existingLinks.length,
873
+ }));
874
+ return;
875
+ }
876
+ const payload = buildTaskAttachmentPayload(asset.storageKey, {
877
+ caption: getCanonicalDocumentName({
878
+ assetId: asset.assetId,
879
+ fsPath: asset.storageKey,
880
+ logicalName: asset.logicalName || null,
881
+ title: asset.logicalName || asset.originalFilename,
882
+ originalFilename: asset.originalFilename,
883
+ }),
884
+ assetId: asset.assetId,
885
+ taskId: targetId,
886
+ });
887
+ if (!payload) {
888
+ res.writeHead(500, { 'Content-Type': 'application/json' });
889
+ res.end(JSON.stringify({ error: 'Failed to build attachment payload for this document.' }));
890
+ return;
891
+ }
892
+ core.updateTask(targetId, {
893
+ attachments: [...(Array.isArray(task.attachments) ? task.attachments : []), payload],
894
+ }, workspaceId, {
895
+ actorRef: 'user',
896
+ saveSource: 'manual',
897
+ });
898
+ core.notifyDocumentMetadataMutation(workspaceId, [asset.storageKey], 'upsert');
899
+ res.writeHead(200, { 'Content-Type': 'application/json' });
900
+ res.end(JSON.stringify({
901
+ success: true,
902
+ attached: true,
903
+ alreadyAttached: false,
904
+ targetType: 'task',
905
+ targetId,
906
+ }));
907
+ });
809
908
  addRoute('DELETE', '/api/taskforce/documents/:assetId', async (req, res, params) => {
810
909
  const workspaceId = requestWorkspaceId(req);
811
910
  const assetId = String(params.assetId || '').trim();
@@ -824,6 +923,7 @@ export function registerDocumentRoutes(deps) {
824
923
  }
825
924
  const activeLinks = workspaceAssetStore.listLinksForAsset(asset.assetId, workspaceId)
826
925
  .filter((link) => !link.deletedAt)
926
+ .filter((link) => link.targetType === 'task' && !!link.taskId)
827
927
  .filter((link) => !!core.getTask(link.taskId, workspaceId));
828
928
  if (activeLinks.length > 0) {
829
929
  if (!forceDetach) {
@@ -836,11 +936,12 @@ export function registerDocumentRoutes(deps) {
836
936
  return;
837
937
  }
838
938
  for (const link of activeLinks) {
839
- const task = core.getTask(link.taskId, workspaceId);
939
+ const taskId = link.taskId;
940
+ const task = core.getTask(taskId, workspaceId);
840
941
  if (!task)
841
942
  continue;
842
943
  const updatedAttachments = removeDocumentAttachmentFromTaskAttachments(task.attachments, asset);
843
- core.updateTask(link.taskId, { attachments: updatedAttachments }, workspaceId, {
944
+ core.updateTask(taskId, { attachments: updatedAttachments }, workspaceId, {
844
945
  actorRef: 'user',
845
946
  saveSource: 'manual'
846
947
  });
@@ -855,7 +956,8 @@ export function registerDocumentRoutes(deps) {
855
956
  workspaceAssetStore.deleteLink({
856
957
  workspaceId,
857
958
  assetId: asset.assetId,
858
- taskId: link.taskId,
959
+ targetType: link.targetType,
960
+ targetId: link.targetId,
859
961
  role: link.role
860
962
  });
861
963
  }
@@ -10,7 +10,32 @@ import { TaskforceCore } from '../../core/Taskforce.js';
10
10
  import type { McpAccessTokenAuthResult, McpOAuthAccessTokenAuthResult, McpAccessTokenScope } from '../../core/Taskforce.js';
11
11
  import type { RouteContext } from './primitives.js';
12
12
  import type { AiProfileBootstrapConfig } from '../../mcp/aiProfileBootstrap.js';
13
+ import type { AiProfileSeatScope } from '../../shared/aiProfileSeatScope.js';
13
14
  export declare function resolveErrorStatusCode(error: unknown, fallback?: number): number;
15
+ export declare const BOOTSTRAP_ROUTE_SLOW_THRESHOLD_MS = 1500;
16
+ export declare const BOOTSTRAP_ROUTE_SEVERE_THRESHOLD_MS = 5000;
17
+ export type ServerTimingMetric = {
18
+ name: string;
19
+ durationMs: number;
20
+ };
21
+ export declare function getRouteTimingNow(): number;
22
+ export declare function buildServerTimingMetric(name: string, durationMs: number): string;
23
+ export declare function setServerTimingHeader(res: ServerResponse, metrics: ServerTimingMetric[]): void;
24
+ export declare function serializeJsonWithTiming(payload: unknown, startedAt?: number): {
25
+ body: string;
26
+ byteLength: number;
27
+ durationMs: number;
28
+ };
29
+ export declare function requestBootstrapTraceId(req: IncomingMessage): string;
30
+ export declare function logSlowBootstrapRoute(input: {
31
+ route: string;
32
+ workspaceId: string;
33
+ durationMs: number;
34
+ traceId?: string | null;
35
+ thresholdMs?: number;
36
+ severeThresholdMs?: number;
37
+ details?: Record<string, unknown>;
38
+ }): void;
14
39
  export declare function buildCloudMcpRuntimeAuth(input: {
15
40
  req: IncomingMessage;
16
41
  auth: {
@@ -34,6 +59,7 @@ export declare function buildCloudMcpRuntimeAuth(input: {
34
59
  aiProfileProvider?: AiProfileBootstrapConfig['aiProfileProvider'];
35
60
  aiProfileModel?: AiProfileBootstrapConfig['aiProfileModel'];
36
61
  aiProfileSurfaceType?: AiProfileBootstrapConfig['aiProfileSurfaceType'];
62
+ aiProfileSeatScope?: AiProfileSeatScope | null;
37
63
  actorType?: 'local' | 'user' | 'token' | 'oauth';
38
64
  requestId?: string | null;
39
65
  ip?: string | null;
@@ -51,7 +77,7 @@ export declare function makeResolveAuthenticatedUserId(core: TaskforceCore, cont
51
77
  export declare function makeResolveAuthenticatedMutationUserId(core: TaskforceCore, context: RouteContext, requestWorkspaceId: (req: IncomingMessage) => string): (req: IncomingMessage) => string;
52
78
  export declare function makeEnsureAppAccess(core: TaskforceCore, context: RouteContext): (req: IncomingMessage, res: ServerResponse) => boolean;
53
79
  export declare function makeEnsureAdminRole(core: TaskforceCore, context: RouteContext): (req: IncomingMessage) => boolean;
54
- export declare function makeEnsureAuthenticatedUser(): (req: IncomingMessage) => boolean;
80
+ export declare function makeEnsureAuthenticatedUser(authConfig?: RouteContext['authConfig']): (req: IncomingMessage) => boolean;
55
81
  export declare function ensureOwnerRole(req: IncomingMessage): boolean;
56
82
  export declare function ensureWorkspaceAdminRole(req: IncomingMessage): boolean;
57
83
  export declare function makeResolveAdminRequestActor(core: TaskforceCore, context: RouteContext): (req: IncomingMessage) => {
@@ -60,6 +86,6 @@ export declare function makeResolveAdminRequestActor(core: TaskforceCore, contex
60
86
  systemRole: "system_admin" | "user";
61
87
  };
62
88
  export declare function makeAuditAdminAction(core: TaskforceCore, context: RouteContext): (req: IncomingMessage, action: string, details: Record<string, unknown>) => void;
63
- export declare function makeAuditAuthAction(core: TaskforceCore, requestWorkspaceId: (req: IncomingMessage) => string, context: RouteContext): (req: IncomingMessage, action: string, details: Record<string, unknown>) => void;
89
+ export declare function makeAuditAuthAction(core: TaskforceCore, requestWorkspaceId: (req: IncomingMessage) => string, context: RouteContext): (req: IncomingMessage, action: string, details: Record<string, unknown>, workspaceIdOverride?: string | null) => void;
64
90
  export declare function makeRequireCloudRuntime(context: RouteContext): (res: ServerResponse, feature: string) => boolean;
65
91
  export declare function makeEnsureCloudPlanData(context: RouteContext): (req: IncomingMessage, res: ServerResponse) => boolean;
@@ -17,6 +17,57 @@ export function resolveErrorStatusCode(error, fallback = 400) {
17
17
  }
18
18
  return fallback;
19
19
  }
20
+ // === SECTION: Route timing helpers ===
21
+ export const BOOTSTRAP_ROUTE_SLOW_THRESHOLD_MS = 1500;
22
+ export const BOOTSTRAP_ROUTE_SEVERE_THRESHOLD_MS = 5000;
23
+ function sanitizeServerTimingMetricName(name) {
24
+ return String(name || 'taskforce-route')
25
+ .trim()
26
+ .replace(/[^A-Za-z0-9_-]+/g, '-')
27
+ .replace(/^-+|-+$/g, '')
28
+ || 'taskforce-route';
29
+ }
30
+ export function getRouteTimingNow() {
31
+ return Date.now();
32
+ }
33
+ export function buildServerTimingMetric(name, durationMs) {
34
+ return `${sanitizeServerTimingMetricName(name)};dur=${Math.max(0, Math.round(durationMs))}`;
35
+ }
36
+ export function setServerTimingHeader(res, metrics) {
37
+ const value = metrics
38
+ .filter((metric) => String(metric.name || '').trim().length > 0)
39
+ .map((metric) => buildServerTimingMetric(metric.name, metric.durationMs))
40
+ .join(', ');
41
+ if (value)
42
+ res.setHeader('Server-Timing', value);
43
+ }
44
+ export function serializeJsonWithTiming(payload, startedAt = getRouteTimingNow()) {
45
+ const body = JSON.stringify(payload);
46
+ return {
47
+ body,
48
+ byteLength: Buffer.byteLength(body, 'utf8'),
49
+ durationMs: Math.max(0, getRouteTimingNow() - startedAt),
50
+ };
51
+ }
52
+ export function requestBootstrapTraceId(req) {
53
+ return requestHeader(req, 'x-taskforce-bootstrap-trace-id', '').trim();
54
+ }
55
+ export function logSlowBootstrapRoute(input) {
56
+ const thresholdMs = Number(input.thresholdMs) > 0 ? Number(input.thresholdMs) : BOOTSTRAP_ROUTE_SLOW_THRESHOLD_MS;
57
+ const severeThresholdMs = Number(input.severeThresholdMs) > 0 ? Number(input.severeThresholdMs) : BOOTSTRAP_ROUTE_SEVERE_THRESHOLD_MS;
58
+ const durationMs = Math.max(0, Math.round(input.durationMs));
59
+ if (durationMs < thresholdMs)
60
+ return;
61
+ const payload = {
62
+ route: input.route,
63
+ workspaceId: String(input.workspaceId || '').trim() || 'default',
64
+ traceId: String(input.traceId || '').trim() || null,
65
+ durationMs,
66
+ severity: durationMs >= severeThresholdMs ? 'high' : 'warning',
67
+ ...(input.details || {}),
68
+ };
69
+ console.warn('[Taskforce Bootstrap Timing]', payload);
70
+ }
20
71
  export function buildCloudMcpRuntimeAuth(input) {
21
72
  const { req, auth, requestHeader, requestId, authScopes } = input;
22
73
  const actorType = auth.authenticatedViaToken
@@ -37,6 +88,7 @@ export function buildCloudMcpRuntimeAuth(input) {
37
88
  aiProfileProvider: auth.aiProfileProvider,
38
89
  aiProfileModel: auth.aiProfileModel,
39
90
  aiProfileSurfaceType: auth.aiProfileSurfaceType,
91
+ aiProfileSeatScope: 'cloud_metered',
40
92
  actorType,
41
93
  requestId: String(requestId || req.headers['x-request-id'] || req.headers['x-railway-request-id'] || '').trim() || undefined,
42
94
  ip: req.socket?.remoteAddress || null,
@@ -216,8 +268,14 @@ export function makeEnsureAdminRole(core, context) {
216
268
  return actor.authenticated && actor.systemRole === 'system_admin';
217
269
  };
218
270
  }
219
- export function makeEnsureAuthenticatedUser() {
271
+ export function makeEnsureAuthenticatedUser(authConfig) {
220
272
  return function ensureAuthenticatedUser(req) {
273
+ if (authConfig?.enabled) {
274
+ const access = resolveRequestAccess(req, authConfig);
275
+ if (access.authenticated && access.userId && access.userId !== 'anonymous') {
276
+ return true;
277
+ }
278
+ }
221
279
  const userId = String(requestUserId(req) || '').trim();
222
280
  return userId.length > 0 && userId !== 'anonymous';
223
281
  };
@@ -286,21 +344,28 @@ export function makeAuditAdminAction(core, context) {
286
344
  };
287
345
  }
288
346
  export function makeAuditAuthAction(core, requestWorkspaceId, context) {
289
- return function auditAuthAction(req, action, details) {
347
+ return function auditAuthAction(req, action, details, workspaceIdOverride) {
290
348
  const authConfig = context.authConfig;
291
349
  const resolvedAccess = authConfig?.enabled ? resolveRequestAccess(req, authConfig) : null;
292
350
  const actorUserId = resolvedAccess?.authenticated && resolvedAccess.userId && resolvedAccess.userId !== 'anonymous'
293
351
  ? resolvedAccess.userId
294
352
  : requestUserId(req);
295
- const actorRole = resolvedAccess?.authenticated
353
+ let actorRole = resolvedAccess?.authenticated
296
354
  ? String(resolvedAccess.role || '').trim().toLowerCase() || requestRole(req)
297
355
  : requestRole(req);
356
+ const workspaceId = String(workspaceIdOverride || requestWorkspaceId(req) || '').trim() || 'default';
357
+ if (workspaceIdOverride && actorUserId && actorUserId !== 'anonymous') {
358
+ const membership = core.listWorkspaceUsers(workspaceId).find((user) => String(user.userId || '').trim() === actorUserId);
359
+ const role = String(membership?.role || '').trim().toLowerCase();
360
+ if (role)
361
+ actorRole = role;
362
+ }
298
363
  const payload = {
299
364
  ts: new Date().toISOString(),
300
365
  action,
301
366
  actorUserId,
302
367
  actorRole,
303
- workspaceId: requestWorkspaceId(req),
368
+ workspaceId,
304
369
  details
305
370
  };
306
371
  try {