@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
@@ -12,7 +12,6 @@ export const ptBR = {
12
12
  updatedLabel: 'Atualizado:',
13
13
  completedLabel: 'Concluido:',
14
14
  emptyValue: '—',
15
- parentCannotSchedule: 'Tarefas pai nao podem ser agendadas. Agende as tarefas folha.',
16
15
  dueBeforeScheduled: 'A data de vencimento e anterior a data agendada.',
17
16
  overdue: 'Esta tarefa esta atrasada.'
18
17
  },
@@ -1,4 +1,4 @@
1
- import type { WorkspaceAssetEntry, WorkspaceAssetKind, WorkspaceAssetLinkRole, WorkspaceAssetScanStatus, WorkspaceAssetStore } from '../storage/workspaceAssetStore.js';
1
+ import type { WorkspaceAssetEntry, WorkspaceAssetKind, WorkspaceAssetLinkRole, WorkspaceAssetLinkTargetType, WorkspaceAssetScanStatus, WorkspaceAssetStore } from '../storage/workspaceAssetStore.js';
2
2
  import type { McpTaskAttachmentDescriptor, TaskAttachmentRecord } from './taskAttachmentTypes.js';
3
3
  export interface CanonicalAssetHelpersDeps {
4
4
  activeWorkspaceId: string;
@@ -9,6 +9,7 @@ export interface CanonicalAssetHelpersDeps {
9
9
  getObject(storageKey: string): Promise<{
10
10
  body: Buffer;
11
11
  } | null>;
12
+ putObject?(storageKey: string, body: Buffer, contentType: string): Promise<void>;
12
13
  } | null;
13
14
  sanitizeFilenameForPath: (filenameRaw: unknown, fallback?: string) => string;
14
15
  sanitizeDisplayName: (valueRaw: unknown, fallback: string) => string;
@@ -31,7 +32,7 @@ export declare function createCanonicalAssetHelpers(deps: CanonicalAssetHelpersD
31
32
  getAttachmentAssetRecord: (attachment: TaskAttachmentRecord) => WorkspaceAssetEntry | null;
32
33
  buildAttachmentDownloadUrl: (attachment: McpTaskAttachmentDescriptor) => string;
33
34
  buildDocumentDownloadUrl: (asset: WorkspaceAssetEntry) => string;
34
- readCanonicalAttachmentBuffer: (taskId: string, attachment: McpTaskAttachmentDescriptor) => Promise<Buffer>;
35
+ readCanonicalAttachmentBuffer: (taskId: string, attachment: McpTaskAttachmentDescriptor, targetLabel?: string) => Promise<Buffer>;
35
36
  readCanonicalDocumentBuffer: (asset: WorkspaceAssetEntry) => Promise<Buffer>;
36
37
  toTaskAttachmentRecord: (attachment: string | TaskAttachmentRecord) => TaskAttachmentRecord;
37
38
  resolveAttachmentLogicalFilename: (attachmentInput: string | TaskAttachmentRecord, assetOverride?: ReturnType<(attachment: TaskAttachmentRecord) => WorkspaceAssetEntry | null> | null) => string;
@@ -47,7 +48,7 @@ export declare function createCanonicalAssetHelpers(deps: CanonicalAssetHelpersD
47
48
  absolutePath: string;
48
49
  logicalFilename: string;
49
50
  };
50
- ensureCanonicalTaskAttachment: (relativePathRaw: string, taskIdRaw: string, captionRaw: string | undefined, timestampRaw: string | undefined, role: WorkspaceAssetLinkRole, options?: {
51
+ ensureCanonicalAttachment: (relativePathRaw: string, targetTypeRaw: WorkspaceAssetLinkTargetType, targetIdRaw: string, captionRaw: string | undefined, timestampRaw: string | undefined, role: WorkspaceAssetLinkRole, options?: {
51
52
  originalFilename?: string;
52
53
  logicalName?: string;
53
54
  assetId?: string;
@@ -57,7 +58,18 @@ export declare function createCanonicalAssetHelpers(deps: CanonicalAssetHelpersD
57
58
  scanStatus?: WorkspaceAssetScanStatus;
58
59
  scanEngine?: string | null;
59
60
  scanDetails?: string | null;
60
- }) => WorkspaceAssetEntry | null;
61
+ }) => Promise<WorkspaceAssetEntry | null>;
62
+ ensureCanonicalTaskAttachment: (relativePathRaw: string, taskIdRaw: string, captionRaw: string | undefined, timestampRaw: string | undefined, role: WorkspaceAssetLinkRole, options?: Parameters<(relativePathRaw: string, targetTypeRaw: WorkspaceAssetLinkTargetType, targetIdRaw: string, captionRaw: string | undefined, timestampRaw: string | undefined, role: WorkspaceAssetLinkRole, options?: {
63
+ originalFilename?: string;
64
+ logicalName?: string;
65
+ assetId?: string;
66
+ documentId?: string | null;
67
+ createdAt?: string;
68
+ createdByUserId?: string | null;
69
+ scanStatus?: WorkspaceAssetScanStatus;
70
+ scanEngine?: string | null;
71
+ scanDetails?: string | null;
72
+ }) => Promise<WorkspaceAssetEntry | null>>[6]) => Promise<WorkspaceAssetEntry | null>;
61
73
  buildCanonicalWorkspaceAssetRelativePath: (params: {
62
74
  workspaceIdRaw: unknown;
63
75
  kind: WorkspaceAssetKind;
@@ -65,13 +65,13 @@ export function createCanonicalAssetHelpers(deps) {
65
65
  ];
66
66
  return parts.join(', ');
67
67
  };
68
- const readCanonicalAttachmentBuffer = async (taskId, attachment) => {
68
+ const readCanonicalAttachmentBuffer = async (taskId, attachment, targetLabel = 'task') => {
69
69
  const assetId = String(attachment.assetId || '').trim();
70
70
  const asset = assetId && deps.workspaceAssetStore
71
71
  ? deps.workspaceAssetStore.get(assetId, deps.activeWorkspaceId)
72
72
  : null;
73
73
  if (!asset || asset.deletedAt) {
74
- throw new Error(`Attachment ${attachment.attachmentId} not found on task ${taskId}`);
74
+ throw new Error(`Attachment ${attachment.attachmentId} not found on ${targetLabel} ${taskId}`);
75
75
  }
76
76
  if (asset.scanStatus === 'infected') {
77
77
  throw new Error('Attachment is quarantined');
@@ -93,16 +93,16 @@ export function createCanonicalAssetHelpers(deps) {
93
93
  throw new Error(`Attachment read failed from R2 (${attachmentContext}): ${message || 'Unknown object storage error'}`);
94
94
  }
95
95
  if (!object) {
96
- throw new Error(`Attachment ${attachment.attachmentId} not found on task ${taskId}`);
96
+ throw new Error(`Attachment ${attachment.attachmentId} not found on ${targetLabel} ${taskId}`);
97
97
  }
98
98
  return object.body;
99
99
  }
100
100
  const resolvedPath = path.resolve(deps.basePath, asset.storageKey);
101
101
  if (!resolvedPath.startsWith(path.resolve(deps.basePath))) {
102
- throw new Error(`Attachment ${attachment.attachmentId} not found on task ${taskId}`);
102
+ throw new Error(`Attachment ${attachment.attachmentId} not found on ${targetLabel} ${taskId}`);
103
103
  }
104
104
  if (!fs.existsSync(resolvedPath) || !fs.statSync(resolvedPath).isFile()) {
105
- throw new Error(`Attachment ${attachment.attachmentId} not found on task ${taskId}`);
105
+ throw new Error(`Attachment ${attachment.attachmentId} not found on ${targetLabel} ${taskId}`);
106
106
  }
107
107
  return fs.readFileSync(resolvedPath);
108
108
  };
@@ -189,12 +189,13 @@ export function createCanonicalAssetHelpers(deps) {
189
189
  }
190
190
  return null;
191
191
  };
192
- const ensureCanonicalTaskAttachment = (relativePathRaw, taskIdRaw, captionRaw, timestampRaw, role, options) => {
192
+ const ensureCanonicalAttachment = async (relativePathRaw, targetTypeRaw, targetIdRaw, captionRaw, timestampRaw, role, options) => {
193
193
  if (!deps.workspaceAssetStore)
194
194
  return null;
195
195
  const relativePath = String(relativePathRaw || '').trim();
196
- const taskId = String(taskIdRaw || '').trim();
197
- if (!relativePath || !taskId)
196
+ const targetType = String(targetTypeRaw || 'task').trim();
197
+ const targetId = String(targetIdRaw || '').trim();
198
+ if (!relativePath || !targetId)
198
199
  return null;
199
200
  const absolutePath = path.resolve(deps.projectRoot, relativePath);
200
201
  if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile())
@@ -206,6 +207,10 @@ export function createCanonicalAssetHelpers(deps) {
206
207
  const kind = deps.inferAssetKind(relativePath, mimeType);
207
208
  const timestamp = String(timestampRaw || new Date().toISOString()).trim() || new Date().toISOString();
208
209
  const existing = deps.workspaceAssetStore.getByStorageKey(storageKey, deps.activeWorkspaceId);
210
+ const shouldPersistToObjectStorage = typeof deps.objectStorageClient?.putObject === 'function';
211
+ if (shouldPersistToObjectStorage) {
212
+ await deps.objectStorageClient.putObject(storageKey, buffer, mimeType || 'application/octet-stream');
213
+ }
209
214
  const originalFilename = deps.sanitizeFilenameForPath(options?.originalFilename || existing?.originalFilename || path.basename(relativePath));
210
215
  const logicalName = deps.sanitizeDisplayName(options?.logicalName || existing?.logicalName || originalFilename, originalFilename);
211
216
  const asset = deps.workspaceAssetStore.upsertAsset({
@@ -215,7 +220,7 @@ export function createCanonicalAssetHelpers(deps) {
215
220
  logicalName,
216
221
  originalFilename,
217
222
  mimeType,
218
- storageProvider: 'local',
223
+ storageProvider: shouldPersistToObjectStorage ? 'r2' : 'local',
219
224
  storageKey,
220
225
  contentSha256: createHash('sha256').update(buffer).digest('hex'),
221
226
  sizeBytes: stats.size,
@@ -234,7 +239,8 @@ export function createCanonicalAssetHelpers(deps) {
234
239
  deps.workspaceAssetStore.upsertLink({
235
240
  workspaceId: deps.activeWorkspaceId,
236
241
  assetId: asset.assetId,
237
- taskId,
242
+ targetType,
243
+ targetId,
238
244
  role,
239
245
  displayName: kind === 'document' ? null : (captionRaw || null),
240
246
  updatedAt: timestamp,
@@ -242,6 +248,7 @@ export function createCanonicalAssetHelpers(deps) {
242
248
  });
243
249
  return asset;
244
250
  };
251
+ const ensureCanonicalTaskAttachment = (relativePathRaw, taskIdRaw, captionRaw, timestampRaw, role, options) => ensureCanonicalAttachment(relativePathRaw, 'task', taskIdRaw, captionRaw, timestampRaw, role, options);
245
252
  return {
246
253
  normalizeAttachmentStorageKey,
247
254
  buildSyntheticAttachmentId,
@@ -253,6 +260,7 @@ export function createCanonicalAssetHelpers(deps) {
253
260
  toTaskAttachmentRecord,
254
261
  resolveAttachmentLogicalFilename,
255
262
  resolveCanonicalOverwriteTarget,
263
+ ensureCanonicalAttachment,
256
264
  ensureCanonicalTaskAttachment,
257
265
  buildCanonicalWorkspaceAssetRelativePath: deps.buildCanonicalWorkspaceAssetRelativePath,
258
266
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,67 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import fs from 'fs';
3
+ import os from 'os';
4
+ import path from 'path';
5
+ import { TaskforceCore } from '../core/Taskforce';
6
+ import { createCanonicalAssetHelpers } from './canonicalAssetHelpers';
7
+ function makeCore() {
8
+ const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'taskforce-mcp-canonical-assets-'));
9
+ const core = new TaskforceCore({
10
+ projectRoot,
11
+ storagePath: path.join(projectRoot, '.taskforce')
12
+ });
13
+ return {
14
+ projectRoot,
15
+ core,
16
+ cleanup: () => fs.rmSync(projectRoot, { recursive: true, force: true })
17
+ };
18
+ }
19
+ describe('MCP canonical asset helpers', () => {
20
+ it('uploads canonical attachments to object storage before recording durable cloud metadata', async () => {
21
+ const { projectRoot, core, cleanup } = makeCore();
22
+ try {
23
+ core.createWorkspace({ workspaceId: 'workspace-1', name: 'Workspace 1' });
24
+ const task = core.createTask({ title: 'Object-backed attachment' }, { workspaceId: 'workspace-1' });
25
+ const relativePath = '.taskforce/workspaces/workspace-1/assets/documents/asset-test-note.md';
26
+ const absolutePath = path.join(projectRoot, relativePath);
27
+ fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
28
+ fs.writeFileSync(absolutePath, '# Stored in object storage\n', 'utf8');
29
+ const putObject = vi.fn(async () => { });
30
+ const helpers = createCanonicalAssetHelpers({
31
+ activeWorkspaceId: 'workspace-1',
32
+ projectRoot,
33
+ basePath: core.getPaths().basePath,
34
+ workspaceAssetStore: core.getWorkspaceAssetStore(),
35
+ objectStorageClient: {
36
+ getObject: vi.fn(async () => null),
37
+ putObject,
38
+ },
39
+ sanitizeFilenameForPath: (value, fallback = 'file.txt') => String(value || fallback).trim() || fallback,
40
+ sanitizeDisplayName: (value, fallback) => String(value || fallback).trim() || fallback,
41
+ toPosixPath: (value) => value.replace(/\\/g, '/'),
42
+ inferMimeTypeFromPath: (value) => value.endsWith('.md') ? 'text/markdown' : 'application/octet-stream',
43
+ inferAssetKind: (value, mimeType) => (value.endsWith('.md') || mimeType === 'text/markdown' ? 'document' : 'file'),
44
+ buildCanonicalWorkspaceAssetRelativePath: () => ({
45
+ assetId: 'asset-test-note',
46
+ relativePath: 'workspaces/workspace-1/assets/documents/asset-test-note.md'
47
+ })
48
+ });
49
+ const asset = await helpers.ensureCanonicalTaskAttachment(relativePath, task.id, 'note.md', '2026-04-27T01:00:00.000Z', 'attachment', { originalFilename: 'note.md', logicalName: 'note.md' });
50
+ expect(putObject).toHaveBeenCalledWith('workspaces/workspace-1/assets/documents/asset-test-note.md', Buffer.from('# Stored in object storage\n', 'utf8'), 'text/markdown');
51
+ expect(asset?.storageProvider).toBe('r2');
52
+ expect(core.getWorkspaceAssetStore().get(asset.assetId, 'workspace-1')?.storageProvider).toBe('r2');
53
+ expect(core.getWorkspaceAssetStore().listLinksForTask(task.id, 'workspace-1')).toEqual(expect.arrayContaining([
54
+ expect.objectContaining({
55
+ assetId: asset?.assetId,
56
+ targetType: 'task',
57
+ role: 'attachment',
58
+ deletedAt: null,
59
+ })
60
+ ]));
61
+ }
62
+ finally {
63
+ core.close();
64
+ cleanup();
65
+ }
66
+ });
67
+ });
@@ -31,6 +31,7 @@ export interface CloudMcpClientIntegration {
31
31
  serverConfig?: unknown;
32
32
  renderedText?: string;
33
33
  }
34
+ export declare function buildLocalAiProfileEnv(clientId: McpClientId): Record<string, string>;
34
35
  export declare const MCP_CLIENT_OPTIONS: McpClientOption[];
35
36
  export declare function sanitizeMcpId(value: string, fallback: string): string;
36
37
  export declare function buildCloudMcpServerId(workspaceName?: string | null, projectName?: string | null): string;
@@ -1,4 +1,29 @@
1
1
  import { buildAiProfileBootstrapHeaders } from './aiProfileBootstrap.js';
2
+ const LOCAL_AI_PROFILE_DEFAULTS = {
3
+ general: { name: 'Taskforce MCP Agent', icon: 'Bot', role: 'AI collaborator', provider: 'MCP client', model: 'Local MCP', surfaceType: 'agent' },
4
+ cursor: { name: 'Cursor', icon: 'Bot', role: 'IDE coding assistant', provider: 'Cursor', model: 'Cursor', surfaceType: 'ide' },
5
+ cline: { name: 'Cline', icon: 'Bot', role: 'IDE coding assistant', provider: 'Cline', model: 'Cline', surfaceType: 'ide' },
6
+ vscode: { name: 'VS Code Copilot', icon: 'Bot', role: 'IDE coding assistant', provider: 'Microsoft', model: 'Copilot', surfaceType: 'ide' },
7
+ windsurf: { name: 'Windsurf', icon: 'Bot', role: 'IDE coding assistant', provider: 'Windsurf', model: 'Windsurf', surfaceType: 'ide' },
8
+ antigravity: { name: 'Antigravity', icon: 'Bot', role: 'Coding agent', provider: 'Google', model: 'Antigravity', surfaceType: 'coding_tool' },
9
+ openclaw: { name: 'OpenClaw', icon: 'Bot', role: 'AI agent', provider: 'OpenClaw', model: 'OpenClaw', surfaceType: 'agent' },
10
+ codex: { name: 'Codex', icon: 'Bot', role: 'Coding agent', provider: 'OpenAI', model: 'Codex', surfaceType: 'cli' },
11
+ 'claude-code': { name: 'Claude Code', icon: 'Bot', role: 'Coding agent', provider: 'Anthropic', model: 'Claude Code', surfaceType: 'cli' },
12
+ 'gemini-cli': { name: 'Gemini CLI', icon: 'Bot', role: 'Coding agent', provider: 'Google', model: 'Gemini CLI', surfaceType: 'cli' },
13
+ 'claude-desktop': { name: 'Claude Desktop', icon: 'Bot', role: 'Desktop AI collaborator', provider: 'Anthropic', model: 'Claude Desktop', surfaceType: 'chat_app' },
14
+ 'chatgpt-desktop': { name: 'ChatGPT Desktop', icon: 'Bot', role: 'Desktop AI collaborator', provider: 'OpenAI', model: 'ChatGPT Desktop', surfaceType: 'chat_app' },
15
+ };
16
+ export function buildLocalAiProfileEnv(clientId) {
17
+ const defaults = LOCAL_AI_PROFILE_DEFAULTS[clientId] || LOCAL_AI_PROFILE_DEFAULTS.general;
18
+ return {
19
+ TASKFORCE_AI_NAME: defaults.name,
20
+ TASKFORCE_AI_ICON: defaults.icon,
21
+ TASKFORCE_AI_ROLE: defaults.role,
22
+ TASKFORCE_AI_PROVIDER: defaults.provider,
23
+ TASKFORCE_AI_MODEL: defaults.model,
24
+ TASKFORCE_AI_SURFACE_TYPE: defaults.surfaceType,
25
+ };
26
+ }
2
27
  export const MCP_CLIENT_OPTIONS = [
3
28
  { id: 'general', label: 'General' },
4
29
  { id: 'antigravity', label: 'Antigravity' },
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { MCP_CLIENT_OPTIONS, buildCloudMcpClientIntegration, buildLocalMcpClientIntegration, buildCloudMcpServerId, } from './clientIntegrations';
2
+ import { MCP_CLIENT_OPTIONS, buildCloudMcpClientIntegration, buildLocalMcpClientIntegration, buildCloudMcpServerId, buildLocalAiProfileEnv, } from './clientIntegrations';
3
3
  describe('clientIntegrations', () => {
4
4
  it('builds a readable cloud MCP server id from workspace name', () => {
5
5
  expect(buildCloudMcpServerId('Workspace Alpha', null)).toBe('taskforce-workspace-alpha');
@@ -78,7 +78,7 @@ describe('clientIntegrations', () => {
78
78
  aiProfileRole: 'Coding Agent',
79
79
  aiProfileProvider: 'OpenAI',
80
80
  aiProfileModel: 'Codex',
81
- aiProfileSurfaceType: 'coding_tool',
81
+ aiProfileSurfaceType: 'cli',
82
82
  });
83
83
  const vscode = buildCloudMcpClientIntegration({
84
84
  clientId: 'vscode',
@@ -98,7 +98,7 @@ describe('clientIntegrations', () => {
98
98
  expect(codex.renderedText).toContain('X-Taskforce-AI-Role = "Coding Agent"');
99
99
  expect(codex.renderedText).toContain('X-Taskforce-AI-Provider = "OpenAI"');
100
100
  expect(codex.renderedText).toContain('X-Taskforce-AI-Model = "Codex"');
101
- expect(codex.renderedText).toContain('X-Taskforce-AI-Surface-Type = "coding_tool"');
101
+ expect(codex.renderedText).toContain('X-Taskforce-AI-Surface-Type = "cli"');
102
102
  expect(codex.instructionText).toContain('Use the generated config exactly as shown.');
103
103
  expect(codex.instructionText).toContain('generated AI bootstrap headers');
104
104
  expect(vscode.serverConfig).toEqual({
@@ -180,6 +180,17 @@ describe('clientIntegrations', () => {
180
180
  expect(integration.renderedText).toContain('TASKFORCE_TENANT_ID = "tenant-alpha"');
181
181
  expect(integration.renderedText).not.toContain('Authorization');
182
182
  });
183
+ it('builds non-secret local AI profile env metadata for stdio clients', () => {
184
+ expect(buildLocalAiProfileEnv('codex')).toEqual({
185
+ TASKFORCE_AI_NAME: 'Codex',
186
+ TASKFORCE_AI_ICON: 'Bot',
187
+ TASKFORCE_AI_ROLE: 'Coding agent',
188
+ TASKFORCE_AI_PROVIDER: 'OpenAI',
189
+ TASKFORCE_AI_MODEL: 'Codex',
190
+ TASKFORCE_AI_SURFACE_TYPE: 'cli',
191
+ });
192
+ expect(buildLocalAiProfileEnv('codex')).not.toHaveProperty('TASKFORCE_AI_PROFILE_TOKEN');
193
+ });
183
194
  it('builds local VS Code config with stdio transport', () => {
184
195
  const integration = buildLocalMcpClientIntegration({
185
196
  clientId: 'vscode',
@@ -7,15 +7,15 @@ const CLIENT_LABELS = {
7
7
  export function getDefaultAiProfileMetadataForClient(clientId) {
8
8
  switch (clientId) {
9
9
  case 'codex':
10
- return { icon: 'Code2', color: '#10a37f', description: 'OpenAI Codex coding agent connected through MCP.', role: 'Coding Agent', provider: 'OpenAI', model: 'Codex', surfaceType: 'coding_tool' };
10
+ return { icon: 'Code2', color: '#10a37f', description: 'OpenAI Codex CLI coding agent connected through MCP.', role: 'CLI Coding Agent', provider: 'OpenAI', model: 'Codex', surfaceType: 'cli' };
11
11
  case 'chatgpt-desktop':
12
12
  return { icon: 'MessageSquare', color: '#10a37f', description: 'ChatGPT desktop app connected through the Taskforce MCP connector.', role: 'AI Assistant', provider: 'OpenAI', model: 'ChatGPT', surfaceType: 'chat_app' };
13
13
  case 'claude-code':
14
- return { icon: 'Sparkles', color: '#d97706', description: 'Claude Code coding agent connected through MCP.', role: 'Coding Agent', provider: 'Anthropic', model: 'Claude Code', surfaceType: 'coding_tool' };
14
+ return { icon: 'Sparkles', color: '#d97706', description: 'Claude Code CLI coding agent connected through MCP.', role: 'CLI Coding Agent', provider: 'Anthropic', model: 'Claude Code', surfaceType: 'cli' };
15
15
  case 'claude-desktop':
16
16
  return { icon: 'MessageSquareMore', color: '#d97706', description: 'Claude Desktop connected through the Taskforce MCP connector.', role: 'AI Assistant', provider: 'Anthropic', model: 'Claude', surfaceType: 'chat_app' };
17
17
  case 'gemini-cli':
18
- return { icon: 'Gem', color: '#2563eb', description: 'Gemini CLI coding agent connected through MCP.', role: 'Coding Agent', provider: 'Google', model: 'Gemini', surfaceType: 'coding_tool' };
18
+ return { icon: 'Gem', color: '#2563eb', description: 'Gemini CLI coding agent connected through MCP.', role: 'CLI Coding Agent', provider: 'Google', model: 'Gemini', surfaceType: 'cli' };
19
19
  case 'vscode':
20
20
  return { icon: 'Code2', color: '#2563eb', description: 'VS Code MCP-connected coding agent.', role: 'Coding Agent', provider: null, model: null, surfaceType: 'ide' };
21
21
  case 'cline':
@@ -27,15 +27,15 @@ export function registerCollaborationTools(registerTool, executeTool) {
27
27
  inputSchema: { properties: {} },
28
28
  }));
29
29
  register('get_current_profile', (context) => ({
30
- description: context.describeTool('Inspect the effective AI profile for this session. Use this when an agent needs to know its current profile id before filtering for tasks assigned to itself. If no profile is currently bound, this tool explains how to bootstrap with resolve_profile. You may also pass profileToken to inspect a stateless AI profile without mutating session state. The response includes the profileToken so automations can persist it after first bootstrap.'),
30
+ description: context.describeTool('Inspect the effective AI profile for this session. Use this when an agent needs to know its current profile id before filtering for tasks assigned to itself. If no profile is currently bound, this tool explains how to bootstrap with resolve_profile. You may also pass your own profileToken to inspect a stateless AI profile without mutating session state. The response includes your profileToken only for private agent/client storage; do not write it to repos, task comments, task attachments, logs, generated docs, or shared output.'),
31
31
  inputSchema: {
32
32
  properties: {
33
- profileToken: stringProperty('Optional AI profile token to inspect when no profile is currently bound in session state.'),
33
+ profileToken: stringProperty('Optional AI profile token to inspect when no profile is currently bound in session state. Pass only a token previously issued to this AI identity.'),
34
34
  },
35
35
  },
36
36
  }));
37
37
  register('resolve_profile', (context) => ({
38
- description: context.describeTool('Resolve or create the current AI collaborator profile for this workspace. On first bootstrap, omit profileToken and Taskforce will create a named AI profile, issue a stable profileToken, and return the profile id to use in assignee, createdBy, and comment author fields on later calls. When a valid profileToken is provided, Taskforce resolves the existing profile and updates any supplied presentation fields such as name, icon, color, description, role, provider, model, and surfaceType. Update semantics: omitted optional fields leave existing stored values unchanged. For description, role, provider, and model, empty strings are ignored rather than clearing. For surfaceType, null or an empty string clears the stored value; valid enum values replace it.'),
38
+ description: context.describeTool('Resolve or create the current AI collaborator profile for this workspace. On first bootstrap, omit profileToken and Taskforce will create a named AI profile, issue a stable profileToken, and return the profile id to use in assignee, createdBy, and comment author fields on later calls. Store the returned profileToken only in private agent/client state and include it on future mutating calls when your MCP client does not preserve session identity. Never write profileToken values to repos, task comments, task attachments, logs, generated docs, or shared output. When a valid profileToken is provided, Taskforce resolves the existing profile and updates any supplied presentation fields such as name, icon, color, description, role, provider, model, and surfaceType. Update semantics: omitted optional fields leave existing stored values unchanged. For description, role, provider, and model, empty strings are ignored rather than clearing. For surfaceType, null or an empty string clears the stored value; valid enum values replace it.'),
39
39
  inputSchema: {
40
40
  properties: {
41
41
  name: stringProperty('Display name for the AI collaborator.'),
@@ -50,9 +50,9 @@ export function registerCollaborationTools(registerTool, executeTool) {
50
50
  { type: 'string', enum: [...AI_PROFILE_SURFACE_TYPES] },
51
51
  { type: 'null' },
52
52
  ],
53
- description: 'Optional self-declared AI surface type. Use agent for a persistent named AI collaborator in Taskforce, coding_tool for the coding surface or terminal tool itself, chat_app for a conversational app identity, ide for an IDE-native copilot identity, and other only when none of those fit. If you are unsure and you are registering a named collaborator such as a teammate-style AI persona, choose agent. Omit to leave unchanged; pass null or an empty string to clear.',
53
+ description: 'Optional self-declared AI surface type. Use agent for a persistent named AI collaborator in Taskforce, cli for terminal-native coding agents, coding_tool for coding surfaces that are not specifically CLI or IDE-native, chat_app for a conversational app identity, and ide for an IDE-native copilot identity. If you are unsure or none of those fit, choose agent. Omit to leave unchanged; pass null or an empty string to clear.',
54
54
  },
55
- profileToken: stringProperty('Previously issued Taskforce profile token. When provided, Taskforce resolves the existing profile instead of creating a new one, and applies any supplied presentation field updates to that profile.'),
55
+ profileToken: stringProperty('Previously issued Taskforce profile token for this AI identity. Keep it in private agent/client state; when provided, Taskforce resolves the existing profile instead of creating a new one, and applies any supplied presentation field updates to that profile.'),
56
56
  },
57
57
  required: ['name'],
58
58
  },
@@ -63,9 +63,9 @@ export function registerCollaborationTools(registerTool, executeTool) {
63
63
  properties: {
64
64
  id: stringProperty('Task ID or task reference such as T-123.'),
65
65
  text: stringProperty('Comment text.'),
66
- author: stringProperty('Comment author identity. Use your AI profile id from resolve_profile, or use user for human-authored comments.'),
66
+ author: stringProperty('Optional comment author identity. Defaults to your resolved AI profile. Prefer stable actor IDs for overrides (`ai-profile-*`, workspace user IDs, `user`, `system`). Display names are not authoritative and may be canonicalized back to the bound actor; use `user` only for human-authored comments.'),
67
67
  },
68
- required: ['id', 'text', 'author'],
68
+ required: ['id', 'text'],
69
69
  },
70
70
  }));
71
71
  }
@@ -35,8 +35,8 @@ export function registerDocumentAssetTools(registerTool, executeTool) {
35
35
  inputSchema: {
36
36
  properties: {
37
37
  taskId: stringProperty('Task ID or task reference such as T-123.'),
38
+ id: stringProperty('Alias for taskId. Task ID or task reference such as T-123.'),
38
39
  },
39
- required: ['taskId'],
40
40
  },
41
41
  }));
42
42
  register('read_task_attachment', (context) => ({
@@ -50,6 +50,96 @@ export function registerDocumentAssetTools(registerTool, executeTool) {
50
50
  required: ['taskId', 'attachmentId'],
51
51
  },
52
52
  }));
53
+ register('get_workstream_attachments', (context) => ({
54
+ description: context.describeTool('List attachment descriptors for one workstream. This returns metadata only; use read_workstream_attachment for file contents.'),
55
+ inputSchema: {
56
+ properties: {
57
+ id: stringProperty('Workstream ID or workstream reference such as WS-123.'),
58
+ },
59
+ required: ['id'],
60
+ },
61
+ }));
62
+ register('read_workstream_attachment', (context) => ({
63
+ description: context.describeTool('Read one workstream attachment by attachmentId. Prefer this tool over opening attachment path URLs directly.'),
64
+ inputSchema: {
65
+ properties: {
66
+ workstreamId: stringProperty('Workstream ID or workstream reference such as WS-123.'),
67
+ attachmentId: stringProperty('Attachment descriptor id from get_workstream_attachments.'),
68
+ format: stringProperty('Read format. Use metadata for descriptors or download for a download URL.', ['text', 'metadata', 'download']),
69
+ maxChars: numberProperty('Optional max inline character count for readable text attachments.'),
70
+ },
71
+ required: ['workstreamId', 'attachmentId'],
72
+ },
73
+ }));
74
+ register('save_workstream_attachment', (context) => ({
75
+ description: context.describeTool('Save a file and attach it to a workstream. Use this for generated content or existing files from disk. Set overwrite=true to replace an existing canonical attachment with the same filename.'),
76
+ inputSchema: {
77
+ properties: {
78
+ id: stringProperty('Workstream ID or workstream reference such as WS-123.'),
79
+ filename: stringProperty('Destination filename for the attachment.'),
80
+ content: stringProperty('Inline file content; use this for AI-authored markdown/text attachments.'),
81
+ sourcePath: stringProperty('Path to an existing source file. Must be within project root, a Taskforce-managed attachment source, or an approved agent workspace root.'),
82
+ caption: stringProperty('Optional attachment caption.'),
83
+ overwrite: booleanProperty('When true, overwrite an existing canonical workstream attachment with the same filename instead of creating a new one.'),
84
+ },
85
+ required: ['id', 'filename'],
86
+ },
87
+ }));
88
+ register('delete_workstream_attachment', (context) => ({
89
+ description: context.describeTool('Delete one workstream attachment. Use get_workstream_attachments first to inspect attachment ids before removing one.'),
90
+ inputSchema: {
91
+ properties: {
92
+ workstreamId: stringProperty('Workstream ID or workstream reference such as WS-123.'),
93
+ attachmentId: stringProperty('Attachment descriptor id from get_workstream_attachments.'),
94
+ },
95
+ required: ['workstreamId', 'attachmentId'],
96
+ },
97
+ }));
98
+ register('get_initiative_attachments', (context) => ({
99
+ description: context.describeTool('List attachment descriptors for one initiative. This returns metadata only; use read_initiative_attachment for file contents.'),
100
+ inputSchema: {
101
+ properties: {
102
+ id: stringProperty('Initiative ID or initiative reference such as IN-123.'),
103
+ },
104
+ required: ['id'],
105
+ },
106
+ }));
107
+ register('read_initiative_attachment', (context) => ({
108
+ description: context.describeTool('Read one initiative attachment by attachmentId. Prefer this tool over opening attachment path URLs directly.'),
109
+ inputSchema: {
110
+ properties: {
111
+ initiativeId: stringProperty('Initiative ID or initiative reference such as IN-123.'),
112
+ attachmentId: stringProperty('Attachment descriptor id from get_initiative_attachments.'),
113
+ format: stringProperty('Read format. Use metadata for descriptors or download for a download URL.', ['text', 'metadata', 'download']),
114
+ maxChars: numberProperty('Optional max inline character count for readable text attachments.'),
115
+ },
116
+ required: ['initiativeId', 'attachmentId'],
117
+ },
118
+ }));
119
+ register('save_initiative_attachment', (context) => ({
120
+ description: context.describeTool('Save a file and attach it to an initiative. Use this for generated content or existing files from disk. Set overwrite=true to replace an existing canonical attachment with the same filename.'),
121
+ inputSchema: {
122
+ properties: {
123
+ id: stringProperty('Initiative ID or initiative reference such as IN-123.'),
124
+ filename: stringProperty('Destination filename for the attachment.'),
125
+ content: stringProperty('Inline file content; use this for AI-authored markdown/text attachments.'),
126
+ sourcePath: stringProperty('Path to an existing source file. Must be within project root, a Taskforce-managed attachment source, or an approved agent workspace root.'),
127
+ caption: stringProperty('Optional attachment caption.'),
128
+ overwrite: booleanProperty('When true, overwrite an existing canonical initiative attachment with the same filename instead of creating a new one.'),
129
+ },
130
+ required: ['id', 'filename'],
131
+ },
132
+ }));
133
+ register('delete_initiative_attachment', (context) => ({
134
+ description: context.describeTool('Delete one initiative attachment. Use get_initiative_attachments first to inspect attachment ids before removing one.'),
135
+ inputSchema: {
136
+ properties: {
137
+ initiativeId: stringProperty('Initiative ID or initiative reference such as IN-123.'),
138
+ attachmentId: stringProperty('Attachment descriptor id from get_initiative_attachments.'),
139
+ },
140
+ required: ['initiativeId', 'attachmentId'],
141
+ },
142
+ }));
53
143
  register('search_images', (context) => ({
54
144
  description: context.describeTool('Search image assets by keyword, title, filename, caption, or I-123 reference. Use this before get_image when you only know a rough identifier.'),
55
145
  inputSchema: {
@@ -61,12 +151,12 @@ export function registerDocumentAssetTools(registerTool, executeTool) {
61
151
  },
62
152
  }));
63
153
  register('get_image', (context) => ({
64
- description: context.describeTool('Get a single image by asset id or image reference such as I-123. Use this first when the user references an image, then use get_annotated_attachment_sessions and read_annotated_attachment_payload for annotation details.'),
154
+ description: context.describeTool('Get a single image by asset id or image reference such as I-123. Use this first when the user references an image, then use get_annotated_attachment_sessions and read_annotated_attachment_payload for annotation details. When the image is being used for UI, placement, or visual correction feedback, read the saved annotation payload before making changes.'),
65
155
  inputSchema: {
66
156
  properties: {
67
157
  id: stringProperty('Image asset id or image reference such as I-123.'),
158
+ imageId: stringProperty('Alias for id. Image asset id or image reference such as I-123.'),
68
159
  },
69
- required: ['id'],
70
160
  },
71
161
  }));
72
162
  register('search_documents', (context) => ({
@@ -84,8 +174,8 @@ export function registerDocumentAssetTools(registerTool, executeTool) {
84
174
  inputSchema: {
85
175
  properties: {
86
176
  id: stringProperty('Document asset id or document reference such as D-123.'),
177
+ documentId: stringProperty('Alias for id. Document asset id or document reference such as D-123.'),
87
178
  },
88
- required: ['id'],
89
179
  },
90
180
  }));
91
181
  register('read_document_content', (context) => ({
@@ -99,7 +189,7 @@ export function registerDocumentAssetTools(registerTool, executeTool) {
99
189
  },
100
190
  }));
101
191
  register('get_annotated_attachment_sessions', (context) => ({
102
- description: context.describeTool('List saved annotation sessions for one image. Use this after get_image when you need the saved annotation sessions before opening one payload.'),
192
+ description: context.describeTool('List saved annotation sessions for one image. Use this after get_image when you need the saved annotation sessions before opening one payload. If the user is pointing at a visual target in I-123, do not stop at the screenshot alone; inspect the saved session payload next.'),
103
193
  inputSchema: {
104
194
  properties: {
105
195
  imageId: stringProperty('Image asset id or image reference such as I-123.'),
@@ -108,7 +198,7 @@ export function registerDocumentAssetTools(registerTool, executeTool) {
108
198
  },
109
199
  }));
110
200
  register('read_annotated_attachment_payload', (context) => ({
111
- description: context.describeTool('Read the structured payload for one annotated attachment session, including markers, geometry, and derived crop hints.'),
201
+ description: context.describeTool('Read the structured payload for one annotated attachment session, including markers, geometry, and derived crop hints. Use this as the authoritative instruction source when I-123 is being used for UI placement or visual change requests.'),
112
202
  inputSchema: {
113
203
  properties: {
114
204
  sessionId: stringProperty('Annotated attachment session id.'),
@@ -161,13 +251,14 @@ export function registerDocumentAssetTools(registerTool, executeTool) {
161
251
  properties: {
162
252
  sessionId: stringProperty('Annotated attachment session id.'),
163
253
  type: stringProperty('Marker type.'),
254
+ kind: stringProperty('Alias for type. Marker kind: pin, box, arrow, or text-note.'),
164
255
  x: numberProperty('Normalized x coordinate in the 0..1 range.'),
165
256
  y: numberProperty('Normalized y coordinate in the 0..1 range.'),
166
257
  width: numberProperty('Optional normalized width in the 0..1 range.'),
167
258
  height: numberProperty('Optional normalized height in the 0..1 range.'),
168
259
  text: stringProperty('Optional marker text or note.'),
260
+ instruction: stringProperty('Alias for text. Marker instruction or note.'),
169
261
  },
170
- required: ['sessionId', 'type', 'x', 'y'],
171
262
  },
172
263
  }));
173
264
  register('bulk_add_annotated_attachment_markers', (context) => ({
@@ -179,13 +270,14 @@ export function registerDocumentAssetTools(registerTool, executeTool) {
179
270
  type: 'object',
180
271
  properties: {
181
272
  type: { type: 'string' },
273
+ kind: { type: 'string' },
182
274
  x: { type: 'number' },
183
275
  y: { type: 'number' },
184
276
  width: { type: 'number' },
185
277
  height: { type: 'number' },
186
278
  text: { type: 'string' },
279
+ instruction: { type: 'string' },
187
280
  },
188
- required: ['type', 'x', 'y'],
189
281
  }),
190
282
  },
191
283
  required: ['sessionId', 'markers'],
@@ -273,9 +365,19 @@ export function registerDocumentAssetTools(registerTool, executeTool) {
273
365
  inputSchema: {
274
366
  properties: {
275
367
  documentId: stringProperty('Document asset id or document reference such as D-123.'),
368
+ id: stringProperty('Alias for documentId. Document asset id or document reference such as D-123.'),
369
+ sessionId: stringProperty('Optional existing review session id.'),
370
+ title: stringProperty('Optional review session title when a new session is created.'),
276
371
  prompt: stringProperty('Review instructions or focus areas.'),
372
+ comments: arrayProperty('Explicit review comments to add. When omitted, prompt is saved as a single review comment.', {
373
+ type: 'object',
374
+ properties: {
375
+ body: { type: 'string' },
376
+ anchor: { type: 'object' },
377
+ },
378
+ required: ['body'],
379
+ }),
277
380
  },
278
- required: ['documentId', 'prompt'],
279
381
  },
280
382
  }));
281
383
  register('add_document_review_comment', (context) => ({
@@ -320,13 +422,17 @@ export function registerDocumentAssetTools(registerTool, executeTool) {
320
422
  },
321
423
  }));
322
424
  register('save_task_attachment', (context) => ({
323
- description: context.describeTool('Save a file and attach it to a task. Use this for generated content or existing files from disk. Set overwrite=true to replace an existing canonical attachment with the same filename. For AI-authored markdown/text, use inline content for AI-authored markdown/text rather than writing an external temp file first.'),
425
+ description: context.describeTool('Save a file and attach it to a task. Use this for generated content, existing files from disk, or staged remote uploads for binary files in cloud MCP. Set overwrite=true to replace an existing canonical attachment with the same filename. For AI-authored markdown/text, use inline content for AI-authored markdown/text rather than writing an external temp file first.'),
324
426
  inputSchema: {
325
427
  properties: {
326
428
  id: stringProperty('Task ID or task reference such as T-123.'),
327
429
  filename: stringProperty('Destination filename for the attachment.'),
328
430
  content: stringProperty('Inline file content; use this for AI-authored markdown/text attachments.'),
329
431
  sourcePath: stringProperty('Path to an existing source file. Must be within project root, a Taskforce-managed attachment source, or an approved agent workspace root.'),
432
+ uploadedPath: stringProperty('Finalize a previously staged remote upload by passing the uploaded Taskforce storage path returned by save_task_attachment init mode.'),
433
+ uploadDraftId: stringProperty('Finalize a previously staged remote upload by passing the uploadDraftId returned by save_task_attachment init mode.'),
434
+ mimeType: stringProperty('Mime type for staged remote uploads such as image/png or image/jpeg. Provide this with sizeBytes to start a staged upload.'),
435
+ sizeBytes: numberProperty('File size in bytes for staged remote uploads. Provide this with mimeType to start a staged upload.'),
330
436
  caption: stringProperty('Optional attachment caption.'),
331
437
  overwrite: booleanProperty('When true, overwrite an existing canonical task attachment with the same filename instead of creating a new one.'),
332
438
  },
@@ -2,10 +2,12 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
3
  import { McpAccessTokenScope, TaskforceCore } from '../core/Taskforce.js';
4
4
  import { type AiProfileBootstrapConfig } from './aiProfileBootstrap.js';
5
+ import { type AiProfileSeatScope } from '../shared/aiProfileSeatScope.js';
5
6
  export interface TaskforceMcpServerOptions {
6
7
  projectRoot?: string;
7
8
  tenantId?: string;
8
9
  workspaceId?: string;
10
+ core?: TaskforceCore;
9
11
  serverName?: string;
10
12
  version?: string;
11
13
  logStartup?: boolean;
@@ -23,6 +25,7 @@ export interface TaskforceMcpServerOptions {
23
25
  aiProfileProvider?: AiProfileBootstrapConfig['aiProfileProvider'];
24
26
  aiProfileModel?: AiProfileBootstrapConfig['aiProfileModel'];
25
27
  aiProfileSurfaceType?: AiProfileBootstrapConfig['aiProfileSurfaceType'];
28
+ aiProfileSeatScope?: AiProfileSeatScope | null;
26
29
  actorType?: 'local' | 'user' | 'token' | 'oauth';
27
30
  requestId?: string | null;
28
31
  ip?: string | null;