@taskforcehq/taskforce 0.3.305 → 0.3.307

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (440) hide show
  1. package/README.md +0 -2
  2. package/dist/Taskforce.module.css +603 -107
  3. package/dist/TaskforceCore.js +216 -43
  4. package/dist/TaskforceCore.test.js +611 -28
  5. package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
  6. package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  7. package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
  8. package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
  9. package/dist/assets/fonts/Noir_medium.woff2 +0 -0
  10. package/dist/assets/fonts/Noir_regular.woff2 +0 -0
  11. package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
  12. package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
  13. package/dist/compat/workspaceSyncCompat.js +46 -2
  14. package/dist/compat/workspaceSyncCompat.test.js +38 -1
  15. package/dist/components/activity/EntityActivityTimeline.d.ts +34 -0
  16. package/dist/components/activity/EntityActivityTimeline.js +81 -0
  17. package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
  18. package/dist/components/context/ContextAttachmentManager.js +561 -0
  19. package/dist/components/features/AgentsModule.d.ts +14 -1
  20. package/dist/components/features/AgentsModule.js +366 -18
  21. package/dist/components/features/AgentsModule.test.js +507 -8
  22. package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +3 -1
  23. package/dist/components/features/AnnotatedAttachmentWorkspace.js +118 -4
  24. package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +52 -0
  25. package/dist/components/features/DocumentIndex.d.ts +2 -2
  26. package/dist/components/features/DocumentIndex.js +3 -4
  27. package/dist/components/features/DocumentViewer.d.ts +7 -1
  28. package/dist/components/features/DocumentViewer.js +62 -4
  29. package/dist/components/features/DocumentWorkspace.d.ts +10 -2
  30. package/dist/components/features/DocumentWorkspace.js +15 -4
  31. package/dist/components/features/DocumentWorkspace.test.js +51 -1
  32. package/dist/components/features/TaskSettings.d.ts +2 -0
  33. package/dist/components/features/TaskSettings.js +83 -78
  34. package/dist/components/features/TaskSettings.test.js +171 -82
  35. package/dist/components/features/documentWorkspaceModel.d.ts +1 -1
  36. package/dist/components/task/TaskActionHeader.js +11 -5
  37. package/dist/components/task/TaskActionHeader.test.js +10 -0
  38. package/dist/components/task/TaskCard.js +24 -29
  39. package/dist/components/task/TaskCard.test.js +38 -3
  40. package/dist/components/task/TaskContextUpload.js +4 -526
  41. package/dist/components/task/TaskContextUpload.test.js +3 -3
  42. package/dist/components/task/TaskForm.js +61 -116
  43. package/dist/components/task/TaskForm.test.js +110 -8
  44. package/dist/components/task/TaskKanban.d.ts +2 -1
  45. package/dist/components/task/TaskKanban.js +94 -21
  46. package/dist/components/task/TaskKanban.test.js +36 -0
  47. package/dist/components/task/TaskList.js +5 -1
  48. package/dist/components/task/TaskList.test.js +4 -0
  49. package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
  50. package/dist/components/ui/EditableAvatarButton.js +18 -0
  51. package/dist/components/ui/HierarchyActionBadge.d.ts +10 -0
  52. package/dist/components/ui/HierarchyActionBadge.js +5 -0
  53. package/dist/components/views/AppShellHeader.d.ts +3 -1
  54. package/dist/components/views/AppShellHeader.js +27 -11
  55. package/dist/components/views/AppShellHeader.test.js +18 -1
  56. package/dist/components/views/PlanComparisonPage.d.ts +9 -1
  57. package/dist/components/views/PlanComparisonPage.js +78 -14
  58. package/dist/components/views/PlanComparisonPage.test.js +257 -0
  59. package/dist/components/views/PlansPage.js +127 -36
  60. package/dist/components/views/PlansPage.test.js +294 -12
  61. package/dist/components/views/StandaloneLayout.js +529 -592
  62. package/dist/components/views/WidgetView.js +10 -8
  63. package/dist/components/views/panels/PlanningDrawer.d.ts +25 -5
  64. package/dist/components/views/panels/PlanningDrawer.js +270 -55
  65. package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
  66. package/dist/components/views/panels/PlanningDrawer.test.js +529 -0
  67. package/dist/components/views/planningScope.d.ts +20 -0
  68. package/dist/components/views/planningScope.js +25 -0
  69. package/dist/components/views/planningScope.test.d.ts +1 -0
  70. package/dist/components/views/planningScope.test.js +60 -0
  71. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +26 -1
  72. package/dist/components/views/standalone/modals/AccountHubModal.js +76 -2
  73. package/dist/components/views/standalone/modals/AccountHubModal.test.js +20 -3
  74. package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
  75. package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
  76. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
  77. package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
  78. package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
  79. package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
  80. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
  81. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
  82. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
  83. package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
  84. package/dist/config/envSchema.js +1 -0
  85. package/dist/core/AiProfileService.d.ts +46 -2
  86. package/dist/core/AiProfileService.js +1073 -82
  87. package/dist/core/AiProfileService.test.js +19 -1
  88. package/dist/core/AiProfiles.test.js +1249 -44
  89. package/dist/core/AttachmentLinkService.d.ts +3 -0
  90. package/dist/core/AttachmentLinkService.js +110 -23
  91. package/dist/core/AuthIdentityService.d.ts +107 -0
  92. package/dist/core/AuthIdentityService.js +284 -0
  93. package/dist/core/AuthTokenService.d.ts +4 -0
  94. package/dist/core/AuthTokenService.js +34 -6
  95. package/dist/core/AuthTokenService.test.js +3 -3
  96. package/dist/core/EntitlementsPolicy.test.js +2 -2
  97. package/dist/core/GlobalSettingsService.js +127 -6
  98. package/dist/core/McpTokenService.d.ts +12 -0
  99. package/dist/core/McpTokenService.js +44 -1
  100. package/dist/core/PlanEntitlementService.d.ts +23 -2
  101. package/dist/core/PlanEntitlementService.js +343 -57
  102. package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
  103. package/dist/core/PlanFeatureCatalog.test.js +84 -0
  104. package/dist/core/PlanVersionPolicy.test.js +35 -2
  105. package/dist/core/PlanningEntities.test.js +418 -4
  106. package/dist/core/SignupMonetizationSettings.test.js +199 -27
  107. package/dist/core/SyncReconciliationService.js +2 -65
  108. package/dist/core/SystemAdmin.test.js +42 -42
  109. package/dist/core/TaskActivityService.d.ts +28 -1
  110. package/dist/core/TaskActivityService.js +227 -46
  111. package/dist/core/TaskActivitySync.test.d.ts +1 -0
  112. package/dist/core/TaskActivitySync.test.js +90 -0
  113. package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
  114. package/dist/core/TaskAuditTimeline.test.js +96 -0
  115. package/dist/core/Taskforce.d.ts +415 -40
  116. package/dist/core/Taskforce.ids.test.js +58 -0
  117. package/dist/core/Taskforce.js +1543 -266
  118. package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
  119. package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
  120. package/dist/core/Taskforce.mcpAuth.test.js +109 -0
  121. package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
  122. package/dist/core/WorkspaceLifecycleService.js +0 -5
  123. package/dist/core/WorkspacePermissions.test.js +25 -1
  124. package/dist/core/WorkspacePlanMode.test.js +117 -16
  125. package/dist/core/shared.d.ts +1 -1
  126. package/dist/core/shared.js +5 -1
  127. package/dist/core/types.d.ts +75 -2
  128. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +28 -2
  129. package/dist/hooks/bootstrapTimeouts.d.ts +4 -0
  130. package/dist/hooks/bootstrapTimeouts.js +8 -0
  131. package/dist/hooks/bootstrapTimeouts.test.d.ts +1 -0
  132. package/dist/hooks/bootstrapTimeouts.test.js +12 -0
  133. package/dist/hooks/sync/bootstrap.js +2 -0
  134. package/dist/hooks/sync/controlPlane.d.ts +37 -0
  135. package/dist/hooks/sync/controlPlane.js +78 -0
  136. package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
  137. package/dist/hooks/sync/controlPlane.test.js +121 -0
  138. package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
  139. package/dist/hooks/sync/lifecyclePolicy.js +93 -0
  140. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
  141. package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
  142. package/dist/hooks/sync/orchestratorShared.d.ts +13 -4
  143. package/dist/hooks/sync/orchestratorShared.js +129 -48
  144. package/dist/hooks/sync/orchestratorShared.test.js +50 -1
  145. package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
  146. package/dist/hooks/sync/pullLifecycle.js +24 -0
  147. package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
  148. package/dist/hooks/sync/pullLifecycle.test.js +80 -0
  149. package/dist/hooks/sync/recovery.d.ts +16 -2
  150. package/dist/hooks/sync/recovery.js +179 -53
  151. package/dist/hooks/sync/recovery.test.d.ts +1 -0
  152. package/dist/hooks/sync/recovery.test.js +292 -0
  153. package/dist/hooks/sync/transfers.d.ts +16 -3
  154. package/dist/hooks/sync/transfers.js +210 -66
  155. package/dist/hooks/sync/transfers.test.d.ts +1 -0
  156. package/dist/hooks/sync/transfers.test.js +928 -0
  157. package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
  158. package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
  159. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
  160. package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
  161. package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
  162. package/dist/hooks/sync/useSyncStatusActions.js +88 -0
  163. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
  164. package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
  165. package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
  166. package/dist/hooks/sync/useSyncStatusControls.js +60 -0
  167. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
  168. package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
  169. package/dist/hooks/useFilterSort.js +13 -13
  170. package/dist/hooks/useFilterSort.test.js +87 -1
  171. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
  172. package/dist/hooks/useSyncOrchestrator.d.ts +9 -2
  173. package/dist/hooks/useSyncOrchestrator.js +409 -183
  174. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +407 -0
  175. package/dist/hooks/useTaskData.js +8 -3
  176. package/dist/hooks/useTaskData.test.js +45 -0
  177. package/dist/hooks/useTaskforce.d.ts +145 -52
  178. package/dist/hooks/useTaskforce.js +592 -253
  179. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
  180. package/dist/hooks/useTaskforce.runtime-routing.test.js +246 -0
  181. package/dist/hooks/useTaskforce.sync-behavior.test.js +2468 -201
  182. package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
  183. package/dist/hooks/useWorkspaceSyncController.js +13 -2
  184. package/dist/hooks/useWorkspaceSyncController.test.js +48 -0
  185. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
  186. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +328 -40
  187. package/dist/localization/locales/en-US.d.ts +0 -1
  188. package/dist/localization/locales/en-US.js +0 -1
  189. package/dist/localization/locales/es-419.js +0 -1
  190. package/dist/localization/locales/pt-BR.js +0 -1
  191. package/dist/mcp/canonicalAssetHelpers.d.ts +16 -4
  192. package/dist/mcp/canonicalAssetHelpers.js +18 -10
  193. package/dist/mcp/canonicalAssetHelpers.test.d.ts +1 -0
  194. package/dist/mcp/canonicalAssetHelpers.test.js +67 -0
  195. package/dist/mcp/clientIntegrations.d.ts +1 -0
  196. package/dist/mcp/clientIntegrations.js +25 -0
  197. package/dist/mcp/clientIntegrations.test.js +14 -3
  198. package/dist/mcp/clientProfileDefaults.js +3 -3
  199. package/dist/mcp/collaborationRegistrar.js +7 -7
  200. package/dist/mcp/documentAssetRegistrar.js +116 -10
  201. package/dist/mcp/runtime.d.ts +3 -0
  202. package/dist/mcp/runtime.js +1099 -185
  203. package/dist/mcp/runtime.test.js +1491 -140
  204. package/dist/mcp/taskAttachmentTypes.d.ts +1 -1
  205. package/dist/mcp/taskPlanningRegistrar.js +48 -0
  206. package/dist/mcp/toolRegistry.d.ts +56 -0
  207. package/dist/mcp/toolRegistry.js +8 -0
  208. package/dist/mcp/toolRegistry.test.js +4 -0
  209. package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
  210. package/dist/migrations/taskSchemaMigrations.js +192 -61
  211. package/dist/migrations/taskSchemaMigrations.test.js +15 -0
  212. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  213. package/dist/resources/templates/workflows/collaborate.yaml +1 -1
  214. package/dist/resources/templates/workflows/evaluate.yaml +2 -2
  215. package/dist/resources/templates/workflows/plan.yaml +1 -1
  216. package/dist/resources/templates/workflows/review.yaml +2 -2
  217. package/dist/server/aiProfileAvatarAssets.d.ts +49 -0
  218. package/dist/server/aiProfileAvatarAssets.js +319 -0
  219. package/dist/server/aiProfileAvatarAssets.test.d.ts +1 -0
  220. package/dist/server/aiProfileAvatarAssets.test.js +213 -0
  221. package/dist/server/annotatedAttachmentsRoutes.test.js +69 -10
  222. package/dist/server/auth/providers/apple.d.ts +33 -0
  223. package/dist/server/auth/providers/apple.js +82 -0
  224. package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
  225. package/dist/server/auth/providers/appleClientSecret.js +65 -0
  226. package/dist/server/auth/providers/github.d.ts +26 -0
  227. package/dist/server/auth/providers/github.js +86 -0
  228. package/dist/server/auth/providers/google.d.ts +21 -0
  229. package/dist/server/auth/providers/google.js +56 -0
  230. package/dist/server/auth/providers/types.d.ts +21 -0
  231. package/dist/server/auth/providers/types.js +1 -0
  232. package/dist/server/auth.d.ts +2 -0
  233. package/dist/server/auth.js +6 -3
  234. package/dist/server/documentReviewRoutes.test.js +36 -3
  235. package/dist/server/index.cookieProxy.test.js +1 -0
  236. package/dist/server/index.d.ts +12 -0
  237. package/dist/server/index.js +124 -9
  238. package/dist/server/index.rateLimit.test.js +3 -0
  239. package/dist/server/index.test.js +111 -3
  240. package/dist/server/routes/admin.d.ts +3 -0
  241. package/dist/server/routes/admin.js +459 -28
  242. package/dist/server/routes/annotatedAttachments.js +51 -0
  243. package/dist/server/routes/auth.d.ts +26 -2
  244. package/dist/server/routes/auth.js +1170 -71
  245. package/dist/server/routes/authSupport.d.ts +5 -1
  246. package/dist/server/routes/authSupport.js +13 -2
  247. package/dist/server/routes/billing.js +372 -35
  248. package/dist/server/routes/billing.test.js +595 -12
  249. package/dist/server/routes/documents.d.ts +1 -0
  250. package/dist/server/routes/documents.js +182 -70
  251. package/dist/server/routes/shared.d.ts +28 -2
  252. package/dist/server/routes/shared.js +69 -14
  253. package/dist/server/routes/sync.integration.test.js +658 -39
  254. package/dist/server/routes/sync.js +74 -608
  255. package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
  256. package/dist/server/routes/syncAuxRoutes.js +91 -0
  257. package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
  258. package/dist/server/routes/syncAuxRoutes.test.js +158 -0
  259. package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
  260. package/dist/server/routes/syncPullApplyRoutes.js +204 -0
  261. package/dist/server/routes/syncPushRoutes.d.ts +24 -0
  262. package/dist/server/routes/syncPushRoutes.js +212 -0
  263. package/dist/server/routes/syncRouteGuards.d.ts +36 -0
  264. package/dist/server/routes/syncRouteGuards.js +67 -0
  265. package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
  266. package/dist/server/routes/syncRouteGuards.test.js +94 -0
  267. package/dist/server/routes/syncRouteTypes.d.ts +13 -0
  268. package/dist/server/routes/syncRouteTypes.js +1 -0
  269. package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
  270. package/dist/server/routes/syncSnapshotRoutes.js +182 -0
  271. package/dist/server/routes/tasks.js +55 -10
  272. package/dist/server/routes/workspaces.js +36 -5
  273. package/dist/server/routes.js +147 -11
  274. package/dist/server/routes.test.js +2704 -183
  275. package/dist/services/workflowExportSnapshots.test.js +2 -0
  276. package/dist/shared/aiProfileSeatScope.d.ts +5 -0
  277. package/dist/shared/aiProfileSeatScope.js +21 -0
  278. package/dist/shared/aiProfileSurfaceType.d.ts +1 -1
  279. package/dist/shared/aiProfileSurfaceType.js +10 -8
  280. package/dist/shared/passwordPolicy.d.ts +13 -0
  281. package/dist/shared/passwordPolicy.js +84 -0
  282. package/dist/shared/passwordPolicy.test.d.ts +1 -0
  283. package/dist/shared/passwordPolicy.test.js +34 -0
  284. package/dist/storage/objectStorage.d.ts +1 -0
  285. package/dist/storage/objectStorage.js +2 -1
  286. package/dist/storage/objectStorageClient.d.ts +1 -0
  287. package/dist/storage/objectStorageClient.js +41 -8
  288. package/dist/storage/objectStorageClient.test.js +26 -1
  289. package/dist/storage/objectStorageConfig.js +3 -1
  290. package/dist/storage/objectStorageConfig.test.js +13 -1
  291. package/dist/storage/workspaceAssetStore.d.ts +15 -3
  292. package/dist/storage/workspaceAssetStore.js +132 -44
  293. package/dist/storage/workspaceAssetStore.test.js +93 -6
  294. package/dist/styles/fonts.css +70 -0
  295. package/dist/sync/collaborationSyncPayload.d.ts +6 -1
  296. package/dist/sync/collaborationSyncPayload.js +19 -11
  297. package/dist/sync/collaborationSyncPayload.test.js +14 -1
  298. package/dist/sync/collaborationSyncState.d.ts +3 -3
  299. package/dist/sync/collaborationSyncState.js +19 -12
  300. package/dist/sync/contentSyncPayload.d.ts +5 -1
  301. package/dist/sync/contentSyncPayload.js +9 -0
  302. package/dist/sync/contentSyncPayload.test.js +39 -0
  303. package/dist/sync/contentSyncState.d.ts +9 -9
  304. package/dist/sync/contentSyncState.js +248 -107
  305. package/dist/sync/planningSyncPayload.js +5 -0
  306. package/dist/sync/planningSyncPayload.test.js +24 -0
  307. package/dist/sync/syncApplyHandlers.d.ts +11 -4
  308. package/dist/sync/syncApplyHandlers.js +115 -42
  309. package/dist/sync/syncApplyHandlers.test.js +251 -4
  310. package/dist/sync/syncFieldSemantics.d.ts +12 -0
  311. package/dist/sync/syncFieldSemantics.js +55 -0
  312. package/dist/sync/syncResourceAdapters.d.ts +46 -0
  313. package/dist/sync/syncResourceAdapters.js +79 -0
  314. package/dist/sync/syncService.d.ts +6 -0
  315. package/dist/sync/syncService.js +65 -1
  316. package/dist/sync/syncService.test.js +96 -3
  317. package/dist/sync/taskSyncChangeKey.d.ts +2 -0
  318. package/dist/sync/taskSyncChangeKey.js +143 -0
  319. package/dist/sync/taskSyncPayload.js +3 -8
  320. package/dist/sync/workspacePullFeed.d.ts +4 -0
  321. package/dist/sync/workspacePullFeed.js +132 -72
  322. package/dist/sync/workspacePullFeed.test.js +92 -0
  323. package/dist/sync/workspaceRepair.js +5 -2
  324. package/dist/sync/workspaceSyncModel.d.ts +73 -6
  325. package/dist/sync/workspaceSyncModel.js +342 -73
  326. package/dist/sync/workspaceSyncModel.test.js +559 -14
  327. package/dist/sync/workspaceSyncState.d.ts +17 -0
  328. package/dist/sync/workspaceSyncState.js +2 -1
  329. package/dist/sync/workspaceSyncSurface.js +3 -8
  330. package/dist/types.d.ts +50 -2
  331. package/dist/ui/assets/AgentsModule-2y82_3DO.js +1 -0
  332. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BTLZZCiQ.js +3 -0
  333. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BVKew-mH.css +1 -0
  334. package/dist/ui/assets/ContextAttachmentManager-D2epuNnG.js +3 -0
  335. package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
  336. package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
  337. package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
  338. package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
  339. package/dist/ui/assets/DocumentWorkspace-D4rJKXzd.css +1 -0
  340. package/dist/ui/assets/DocumentWorkspace-sVa-3Do6.js +252 -0
  341. package/dist/ui/assets/EntityActivityTimeline-DyqvvhuF.css +1 -0
  342. package/dist/ui/assets/EntityActivityTimeline-Zods27y_.js +1 -0
  343. package/dist/ui/assets/{InitiativesModule-Dhm7M8e7.js → InitiativesModule-D4XseTwW.js} +1 -1
  344. package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
  345. package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
  346. package/dist/ui/assets/PlansPage-D5AcbO0L.js +1 -0
  347. package/dist/ui/assets/PlansPage-Dvqsz5zc.css +1 -0
  348. package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
  349. package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
  350. package/dist/ui/assets/TaskContextUpload-uC5qx4Bv.js +1 -0
  351. package/dist/ui/assets/TaskSettings-CqvPAsTv.js +9 -0
  352. package/dist/ui/assets/{WorkflowsModule-BcS4afRn.js → WorkflowsModule-oOA9bcdK.js} +4 -4
  353. package/dist/ui/assets/documentReferences-DPZuTgHh.js +1 -0
  354. package/dist/ui/assets/index-607WPo_X.js +4 -0
  355. package/dist/ui/assets/index-CYyodXsg.css +1 -0
  356. package/dist/ui/assets/{vendor-icons-BSUaRwF8.js → vendor-icons-B32hGuF2.js} +1 -1
  357. package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
  358. package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
  359. package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
  360. package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  361. package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
  362. package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
  363. package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
  364. package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
  365. package/dist/ui/fonts/Noir_medium.otf +0 -0
  366. package/dist/ui/fonts/Noir_medium.woff2 +0 -0
  367. package/dist/ui/fonts/Noir_regular.otf +0 -0
  368. package/dist/ui/fonts/Noir_regular.woff2 +0 -0
  369. package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
  370. package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
  371. package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
  372. package/dist/ui/index.html +3 -3
  373. package/dist/ui/product/agent manager.png +0 -0
  374. package/dist/ui/product/agent_manager_02.png +0 -0
  375. package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
  376. package/dist/utils/aiProfileEvents.d.ts +7 -0
  377. package/dist/utils/aiProfileEvents.js +6 -0
  378. package/dist/utils/assignees.d.ts +8 -0
  379. package/dist/utils/assignees.js +10 -1
  380. package/dist/utils/assignees.test.d.ts +1 -0
  381. package/dist/utils/assignees.test.js +25 -0
  382. package/dist/utils/avatarDisplay.d.ts +1 -0
  383. package/dist/utils/avatarDisplay.js +15 -0
  384. package/dist/utils/avatarUpload.d.ts +27 -0
  385. package/dist/utils/avatarUpload.js +132 -0
  386. package/dist/utils/avatarUpload.test.d.ts +1 -0
  387. package/dist/utils/avatarUpload.test.js +40 -0
  388. package/dist/utils/bootstrapTrace.d.ts +7 -0
  389. package/dist/utils/bootstrapTrace.js +29 -0
  390. package/dist/utils/commercialLifecyclePresentation.js +3 -0
  391. package/dist/utils/contextAssetEvents.d.ts +2 -0
  392. package/dist/utils/contextFiles.d.ts +3 -1
  393. package/dist/utils/contextFiles.js +2 -1
  394. package/dist/utils/syncEventDetails.d.ts +3 -0
  395. package/dist/utils/syncEventDetails.js +64 -0
  396. package/dist/utils/syncEventDetails.test.d.ts +1 -0
  397. package/dist/utils/syncEventDetails.test.js +23 -0
  398. package/dist/utils/syncEventPresentation.d.ts +15 -0
  399. package/dist/utils/syncEventPresentation.js +129 -0
  400. package/dist/utils/syncEventPresentation.test.d.ts +1 -0
  401. package/dist/utils/syncEventPresentation.test.js +64 -0
  402. package/dist/utils/syncStatusPresentation.d.ts +72 -0
  403. package/dist/utils/syncStatusPresentation.js +217 -0
  404. package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
  405. package/dist/utils/syncStatusPresentation.test.js +164 -0
  406. package/dist/utils/taskActivity.d.ts +9 -1
  407. package/dist/utils/taskActivity.js +290 -22
  408. package/dist/utils/taskActivity.test.js +141 -8
  409. package/dist/utils/taskSearch.d.ts +22 -0
  410. package/dist/utils/taskSearch.js +87 -0
  411. package/dist/utils/taskSearch.test.d.ts +1 -0
  412. package/dist/utils/taskSearch.test.js +69 -0
  413. package/dist/utils/taskUtils.js +10 -3
  414. package/dist/utils/taskUtils.test.d.ts +1 -0
  415. package/dist/utils/taskUtils.test.js +19 -0
  416. package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
  417. package/dist/utils/workspaceSyncPresentation.js +68 -2
  418. package/dist/utils/workspaceSyncPresentation.test.js +36 -1
  419. package/package.json +3 -2
  420. package/scripts/export-sqlite-to-postgres.mjs +7 -9
  421. package/scripts/run-e2e-realtime.sh +17 -3
  422. package/scripts/run-smoke.sh +17 -5
  423. package/scripts/run-soak.sh +17 -5
  424. package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  425. package/src/resources/templates/workflows/collaborate.yaml +1 -1
  426. package/src/resources/templates/workflows/evaluate.yaml +2 -2
  427. package/src/resources/templates/workflows/plan.yaml +1 -1
  428. package/src/resources/templates/workflows/review.yaml +2 -2
  429. package/dist/ui/assets/AgentsModule-N2MnQBZk.js +0 -1
  430. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BG6P_GVW.js +0 -3
  431. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +0 -1
  432. package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +0 -1
  433. package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +0 -252
  434. package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
  435. package/dist/ui/assets/PlansPage-Cq0zXj3I.js +0 -1
  436. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  437. package/dist/ui/assets/TaskSettings-ln0aF7xc.js +0 -9
  438. package/dist/ui/assets/index-C2-OXZV7.css +0 -1
  439. package/dist/ui/assets/index-DcSI5F86.js +0 -6
  440. package/scripts/migrate-planning-model.ts +0 -233
@@ -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,22 @@ 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
+ }
477
+ if (shouldProxyInlineAiAvatar && filePath && fs.existsSync(filePath)) {
478
+ res.writeHead(200, {
479
+ 'Content-Type': contentTypeForExt(ext),
480
+ 'Content-Disposition': contentDisposition
481
+ });
482
+ res.end(fs.readFileSync(filePath));
483
+ return;
484
+ }
402
485
  res.writeHead(404, { 'Content-Type': 'text/plain' });
403
486
  res.end('Context file not found');
404
487
  return;
@@ -423,6 +506,9 @@ export function registerDocumentRoutes(deps) {
423
506
  res.end(fs.readFileSync(filePath));
424
507
  return;
425
508
  }
509
+ if (normalizedRelative && await proxyCloudUserProfileAvatar(req, res, normalizedRelative, contentTypeForExt)) {
510
+ return;
511
+ }
426
512
  res.writeHead(404, { 'Content-Type': 'text/plain' });
427
513
  res.end('Context file not found');
428
514
  });
@@ -590,9 +676,6 @@ export function registerDocumentRoutes(deps) {
590
676
  });
591
677
  addRoute('GET', '/api/taskforce/documents', async (req, res) => {
592
678
  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
679
  function inferDocType(filename) {
597
680
  const base = path.basename(filename, path.extname(filename)).toLowerCase();
598
681
  if (base.includes('implementation-plan') || base.includes('implementation_plan'))
@@ -630,12 +713,9 @@ export function registerDocumentRoutes(deps) {
630
713
  : (taskId ? 1 : 0);
631
714
  const attachmentState = input.attachmentState === 'attached'
632
715
  || input.attachmentState === 'unattached'
633
- || input.attachmentState === 'standalone'
634
716
  ? (attachmentCount > 0 || taskId
635
717
  ? 'attached'
636
- : input.attachmentState === 'standalone'
637
- ? 'standalone'
638
- : 'unattached')
718
+ : 'unattached')
639
719
  : (attachmentCount > 0 || taskId ? 'attached' : 'unattached');
640
720
  return {
641
721
  id: input.id,
@@ -658,12 +738,6 @@ export function registerDocumentRoutes(deps) {
658
738
  attachmentState
659
739
  };
660
740
  }
661
- function safeRelative(fullPath) {
662
- const rel = path.relative(projectRoot, fullPath);
663
- if (rel.startsWith('..'))
664
- return null;
665
- return rel;
666
- }
667
741
  const documents = [];
668
742
  const seenDocumentPaths = new Set();
669
743
  const pushDocument = (doc) => {
@@ -674,6 +748,9 @@ export function registerDocumentRoutes(deps) {
674
748
  seenDocumentPaths.add(key);
675
749
  documents.push(normalizedDoc);
676
750
  };
751
+ // Shared document listing is canonical-document-only. Raw files under
752
+ // .taskforce/docs are intentionally excluded because they are not
753
+ // workspace-scoped managed documents.
677
754
  const canonicalDocuments = workspaceAssetStore?.listAllByWorkspace(workspaceId, {
678
755
  includeDeleted: false,
679
756
  kind: 'document'
@@ -689,8 +766,11 @@ export function registerDocumentRoutes(deps) {
689
766
  });
690
767
  const activeLinks = (workspaceAssetStore?.listLinksForAsset(asset.assetId, workspaceId) || [])
691
768
  .filter((link) => !link.deletedAt)
769
+ .filter((link) => link.targetType !== 'task' || (!!link.taskId && !!core.getTask(link.taskId, workspaceId)));
770
+ const activeTaskLinks = activeLinks
771
+ .filter((link) => link.targetType === 'task' && !!link.taskId)
692
772
  .filter((link) => !!core.getTask(link.taskId, workspaceId));
693
- const firstLink = activeLinks[0] || null;
773
+ const firstLink = activeTaskLinks[0] || null;
694
774
  const repairedLogicalName = isGeneratedDocumentName(asset.logicalName || null, asset.assetId || null)
695
775
  ? (String(firstLink?.displayName || '').trim()
696
776
  || repairedReadableName)
@@ -742,58 +822,6 @@ export function registerDocumentRoutes(deps) {
742
822
  attachmentState: activeLinks.length > 0 ? 'attached' : 'unattached',
743
823
  });
744
824
  }
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
825
  documents.sort((a, b) => {
798
826
  if (!a.updatedAt && !b.updatedAt)
799
827
  return 0;
@@ -806,6 +834,87 @@ export function registerDocumentRoutes(deps) {
806
834
  res.writeHead(200, { 'Content-Type': 'application/json' });
807
835
  res.end(JSON.stringify({ documents }));
808
836
  });
837
+ addRoute('POST', '/api/taskforce/documents/:assetId/attach', async (req, res, params) => {
838
+ const workspaceId = requestWorkspaceId(req);
839
+ const assetId = String(params.assetId || '').trim();
840
+ const body = await parseJsonBody(req);
841
+ const targetType = String(body?.targetType || 'task').trim();
842
+ const targetId = String(body?.targetId || '').trim();
843
+ if (!workspaceAssetStore || !assetId) {
844
+ res.writeHead(404, { 'Content-Type': 'application/json' });
845
+ res.end(JSON.stringify({ error: 'Document not found.' }));
846
+ return;
847
+ }
848
+ if (!targetId) {
849
+ res.writeHead(400, { 'Content-Type': 'application/json' });
850
+ res.end(JSON.stringify({ error: 'Missing required targetId.' }));
851
+ return;
852
+ }
853
+ if (targetType !== 'task') {
854
+ res.writeHead(400, { 'Content-Type': 'application/json' });
855
+ res.end(JSON.stringify({ error: 'Only task attachments are supported from the document manager right now.' }));
856
+ return;
857
+ }
858
+ const asset = workspaceAssetStore.get(assetId, workspaceId);
859
+ if (!asset || asset.kind !== 'document' || asset.deletedAt) {
860
+ res.writeHead(404, { 'Content-Type': 'application/json' });
861
+ res.end(JSON.stringify({ error: 'Document not found.' }));
862
+ return;
863
+ }
864
+ const task = core.getTask(targetId, workspaceId);
865
+ if (!task) {
866
+ res.writeHead(404, { 'Content-Type': 'application/json' });
867
+ res.end(JSON.stringify({ error: 'Task not found.' }));
868
+ return;
869
+ }
870
+ const existingLinks = workspaceAssetStore.listLinksForTarget('task', targetId, workspaceId)
871
+ .filter((link) => !link.deletedAt);
872
+ const alreadyAttached = existingLinks.some((link) => link.assetId === asset.assetId && link.role === 'attachment');
873
+ if (alreadyAttached) {
874
+ res.writeHead(200, { 'Content-Type': 'application/json' });
875
+ res.end(JSON.stringify({
876
+ success: true,
877
+ alreadyAttached: true,
878
+ targetType: 'task',
879
+ targetId,
880
+ attachmentCount: existingLinks.length,
881
+ }));
882
+ return;
883
+ }
884
+ const attachedAt = new Date().toISOString();
885
+ const payload = buildTaskAttachmentPayload(asset.storageKey, {
886
+ caption: getCanonicalDocumentName({
887
+ assetId: asset.assetId,
888
+ fsPath: asset.storageKey,
889
+ logicalName: asset.logicalName || null,
890
+ title: asset.logicalName || asset.originalFilename,
891
+ originalFilename: asset.originalFilename,
892
+ }),
893
+ assetId: asset.assetId,
894
+ taskId: targetId,
895
+ timestamp: attachedAt,
896
+ });
897
+ if (!payload) {
898
+ res.writeHead(500, { 'Content-Type': 'application/json' });
899
+ res.end(JSON.stringify({ error: 'Failed to build attachment payload for this document.' }));
900
+ return;
901
+ }
902
+ core.updateTask(targetId, {
903
+ attachments: [...(Array.isArray(task.attachments) ? task.attachments : []), payload],
904
+ }, workspaceId, {
905
+ actorRef: 'user',
906
+ saveSource: 'manual',
907
+ });
908
+ core.notifyDocumentMetadataMutation(workspaceId, [asset.storageKey], 'upsert');
909
+ res.writeHead(200, { 'Content-Type': 'application/json' });
910
+ res.end(JSON.stringify({
911
+ success: true,
912
+ attached: true,
913
+ alreadyAttached: false,
914
+ targetType: 'task',
915
+ targetId,
916
+ }));
917
+ });
809
918
  addRoute('DELETE', '/api/taskforce/documents/:assetId', async (req, res, params) => {
810
919
  const workspaceId = requestWorkspaceId(req);
811
920
  const assetId = String(params.assetId || '').trim();
@@ -824,6 +933,7 @@ export function registerDocumentRoutes(deps) {
824
933
  }
825
934
  const activeLinks = workspaceAssetStore.listLinksForAsset(asset.assetId, workspaceId)
826
935
  .filter((link) => !link.deletedAt)
936
+ .filter((link) => link.targetType === 'task' && !!link.taskId)
827
937
  .filter((link) => !!core.getTask(link.taskId, workspaceId));
828
938
  if (activeLinks.length > 0) {
829
939
  if (!forceDetach) {
@@ -836,11 +946,12 @@ export function registerDocumentRoutes(deps) {
836
946
  return;
837
947
  }
838
948
  for (const link of activeLinks) {
839
- const task = core.getTask(link.taskId, workspaceId);
949
+ const taskId = link.taskId;
950
+ const task = core.getTask(taskId, workspaceId);
840
951
  if (!task)
841
952
  continue;
842
953
  const updatedAttachments = removeDocumentAttachmentFromTaskAttachments(task.attachments, asset);
843
- core.updateTask(link.taskId, { attachments: updatedAttachments }, workspaceId, {
954
+ core.updateTask(taskId, { attachments: updatedAttachments }, workspaceId, {
844
955
  actorRef: 'user',
845
956
  saveSource: 'manual'
846
957
  });
@@ -855,7 +966,8 @@ export function registerDocumentRoutes(deps) {
855
966
  workspaceAssetStore.deleteLink({
856
967
  workspaceId,
857
968
  assetId: asset.assetId,
858
- taskId: link.taskId,
969
+ targetType: link.targetType,
970
+ targetId: link.targetId,
859
971
  role: link.role
860
972
  });
861
973
  }
@@ -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,
@@ -164,16 +216,6 @@ export function makeResolveAuthenticatedMutationUserId(core, context, requestWor
164
216
  const explicitUserId = String(requestUserId(req) || '').trim();
165
217
  if (explicitUserId && explicitUserId !== 'anonymous')
166
218
  return explicitUserId;
167
- if (requestRuntimeMode(req) === 'local') {
168
- const workspaceId = String(requestWorkspaceId(req) || '').trim() || 'default';
169
- const fallbackUser = core.listWorkspaceUsers(workspaceId).find((user) => {
170
- const status = String(user.status || '').trim().toLowerCase();
171
- return status === 'active' && user.disabled !== true;
172
- });
173
- const fallbackUserId = String(fallbackUser?.userId || '').trim();
174
- if (fallbackUserId)
175
- return fallbackUserId;
176
- }
177
219
  throw new TaskforceRuleError('Authenticated user required for mutation attribution.', 401, 'MUTATION_AUTH_REQUIRED');
178
220
  };
179
221
  }
@@ -216,8 +258,14 @@ export function makeEnsureAdminRole(core, context) {
216
258
  return actor.authenticated && actor.systemRole === 'system_admin';
217
259
  };
218
260
  }
219
- export function makeEnsureAuthenticatedUser() {
261
+ export function makeEnsureAuthenticatedUser(authConfig) {
220
262
  return function ensureAuthenticatedUser(req) {
263
+ if (authConfig?.enabled) {
264
+ const access = resolveRequestAccess(req, authConfig);
265
+ if (access.authenticated && access.userId && access.userId !== 'anonymous') {
266
+ return true;
267
+ }
268
+ }
221
269
  const userId = String(requestUserId(req) || '').trim();
222
270
  return userId.length > 0 && userId !== 'anonymous';
223
271
  };
@@ -286,21 +334,28 @@ export function makeAuditAdminAction(core, context) {
286
334
  };
287
335
  }
288
336
  export function makeAuditAuthAction(core, requestWorkspaceId, context) {
289
- return function auditAuthAction(req, action, details) {
337
+ return function auditAuthAction(req, action, details, workspaceIdOverride) {
290
338
  const authConfig = context.authConfig;
291
339
  const resolvedAccess = authConfig?.enabled ? resolveRequestAccess(req, authConfig) : null;
292
340
  const actorUserId = resolvedAccess?.authenticated && resolvedAccess.userId && resolvedAccess.userId !== 'anonymous'
293
341
  ? resolvedAccess.userId
294
342
  : requestUserId(req);
295
- const actorRole = resolvedAccess?.authenticated
343
+ let actorRole = resolvedAccess?.authenticated
296
344
  ? String(resolvedAccess.role || '').trim().toLowerCase() || requestRole(req)
297
345
  : requestRole(req);
346
+ const workspaceId = String(workspaceIdOverride || requestWorkspaceId(req) || '').trim() || 'default';
347
+ if (workspaceIdOverride && actorUserId && actorUserId !== 'anonymous') {
348
+ const membership = core.listWorkspaceUsers(workspaceId).find((user) => String(user.userId || '').trim() === actorUserId);
349
+ const role = String(membership?.role || '').trim().toLowerCase();
350
+ if (role)
351
+ actorRole = role;
352
+ }
298
353
  const payload = {
299
354
  ts: new Date().toISOString(),
300
355
  action,
301
356
  actorUserId,
302
357
  actorRole,
303
- workspaceId: requestWorkspaceId(req),
358
+ workspaceId,
304
359
  details
305
360
  };
306
361
  try {