@taskforcehq/taskforce 0.3.327 → 0.3.329

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 (360) hide show
  1. package/dist/Taskforce.module.css +257 -87
  2. package/dist/TaskforceCore.js +237 -230
  3. package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.js +1 -1
  4. package/dist/cli.js +34 -0
  5. package/dist/cliCloudTargetGuard.d.ts +5 -0
  6. package/dist/cliCloudTargetGuard.js +11 -0
  7. package/dist/components/activity/EntityActivityTimeline.js +15 -5
  8. package/dist/components/context/AttachmentPreviewHost.js +1 -1
  9. package/dist/components/context/ContextAttachmentManager.d.ts +3 -0
  10. package/dist/components/context/ContextAttachmentManager.js +116 -20
  11. package/dist/components/features/AnnotatedAttachmentWorkspace.js +24 -8
  12. package/dist/components/features/TaskSettings.d.ts +12 -1
  13. package/dist/components/features/TaskSettings.js +147 -40
  14. package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +1 -1
  15. package/dist/components/features/planning/PlanningEntitySummary.d.ts +3 -1
  16. package/dist/components/features/planning/PlanningEntitySummary.js +3 -2
  17. package/dist/components/features/planning/PlanningLinkPicker.d.ts +3 -1
  18. package/dist/components/features/planning/PlanningLinkPicker.js +5 -3
  19. package/dist/components/features/planning/PlanningModule.js +28 -14
  20. package/dist/components/features/planning/PlanningTaskOrderList.d.ts +1 -1
  21. package/dist/components/features/planning/PlanningTaskOrderList.js +17 -6
  22. package/dist/components/features/planning/PlanningVisualIdentity.d.ts +14 -0
  23. package/dist/components/features/planning/PlanningVisualIdentity.js +106 -0
  24. package/dist/components/features/planning/planningTaskArrangement.d.ts +1 -0
  25. package/dist/components/features/planning/planningTaskArrangement.js +5 -5
  26. package/dist/components/features/planning/planningWorkspaceModel.d.ts +11 -0
  27. package/dist/components/features/taskforceAgentEditorModel.js +3 -1
  28. package/dist/components/task/TaskActionHeader.d.ts +2 -1
  29. package/dist/components/task/TaskActionHeader.js +2 -2
  30. package/dist/components/task/TaskCard.d.ts +5 -0
  31. package/dist/components/task/TaskCard.js +101 -13
  32. package/dist/components/task/TaskContextUpload.d.ts +3 -0
  33. package/dist/components/task/TaskContextUpload.js +2 -2
  34. package/dist/components/task/TaskForm.d.ts +4 -0
  35. package/dist/components/task/TaskForm.js +32 -7
  36. package/dist/components/task/TaskImageReviews.js +28 -19
  37. package/dist/components/task/TaskKanban.d.ts +6 -1
  38. package/dist/components/task/TaskKanban.js +152 -6
  39. package/dist/components/task/TaskList.d.ts +5 -0
  40. package/dist/components/task/TaskList.js +3 -3
  41. package/dist/components/task/TaskStatusActions.js +11 -2
  42. package/dist/components/task/TaskWorkstreamPicker.js +2 -1
  43. package/dist/components/ui/DocumentTypeIcon.d.ts +7 -0
  44. package/dist/components/ui/DocumentTypeIcon.js +8 -0
  45. package/dist/components/ui/PasswordInput.d.ts +6 -0
  46. package/dist/components/ui/PasswordInput.js +11 -0
  47. package/dist/components/ui/TaxonomyDropdown.d.ts +9 -1
  48. package/dist/components/ui/TaxonomyDropdown.js +30 -3
  49. package/dist/components/ui/TopNoticeLayer.js +1 -1
  50. package/dist/components/views/AppShellHeader.js +32 -11
  51. package/dist/components/views/PlanComparisonPage.js +12 -0
  52. package/dist/components/views/StandaloneLayout.js +68 -39
  53. package/dist/components/views/WidgetView.js +10 -7
  54. package/dist/components/views/panels/PlanningDrawer.d.ts +7 -1
  55. package/dist/components/views/panels/PlanningDrawer.js +51 -54
  56. package/dist/components/views/standalone/modals/AccountSettingsModal.js +26 -17
  57. package/dist/config/envSchema.js +13 -0
  58. package/dist/config/localServiceCloudTarget.d.ts +10 -0
  59. package/dist/config/localServiceCloudTarget.js +76 -0
  60. package/dist/config/localServiceRuntimeContract.d.ts +13 -0
  61. package/dist/config/localServiceRuntimeContract.js +135 -0
  62. package/dist/core/AiProfileService.d.ts +1 -0
  63. package/dist/core/AiProfileService.js +48 -9
  64. package/dist/core/AttachmentLinkService.js +7 -4
  65. package/dist/core/DeletedTaskLifecycleService.d.ts +5 -0
  66. package/dist/core/DeletedTaskLifecycleService.js +55 -3
  67. package/dist/core/McpTokenService.d.ts +4 -0
  68. package/dist/core/McpTokenService.js +52 -0
  69. package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +8 -2
  70. package/dist/core/TaskAttachmentLinksDurableMutationService.js +51 -18
  71. package/dist/core/Taskforce.d.ts +124 -2
  72. package/dist/core/Taskforce.js +1175 -87
  73. package/dist/core/types.d.ts +8 -1
  74. package/dist/documentReviews/DocumentReviewCommentStore.d.ts +11 -0
  75. package/dist/documentReviews/DocumentReviewCommentStore.js +35 -2
  76. package/dist/documentReviews/service.d.ts +4 -0
  77. package/dist/documentReviews/service.js +141 -46
  78. package/dist/hooks/sync/useDurableLocalOutboxDrain.d.ts +1 -1
  79. package/dist/hooks/sync/useDurableTaskCreateMutation.js +1 -1
  80. package/dist/hooks/sync/useDurableTaskMetadataMutation.d.ts +1 -0
  81. package/dist/hooks/sync/useDurableTaskMetadataMutation.js +1 -0
  82. package/dist/hooks/sync/useDurableTaskStatusMutation.d.ts +1 -0
  83. package/dist/hooks/tasks/taskHttpMutation.js +1 -0
  84. package/dist/hooks/tasks/useCloudTaskSearch.d.ts +12 -0
  85. package/dist/hooks/tasks/useCloudTaskSearch.js +71 -0
  86. package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.d.ts +16 -0
  87. package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.js +197 -0
  88. package/dist/hooks/tasks/useTaskEditorNavigation.d.ts +16 -3
  89. package/dist/hooks/tasks/useTaskEditorNavigation.js +72 -13
  90. package/dist/hooks/tasks/useTaskFormActions.d.ts +9 -0
  91. package/dist/hooks/tasks/useTaskFormActions.js +96 -17
  92. package/dist/hooks/ui/useSettingsModel.d.ts +24 -2
  93. package/dist/hooks/ui/useSettingsModel.js +13 -2
  94. package/dist/hooks/ui/useSettingsPersistence.d.ts +12 -1
  95. package/dist/hooks/ui/useSettingsPersistence.js +74 -47
  96. package/dist/hooks/useFilterSort.d.ts +7 -3
  97. package/dist/hooks/useFilterSort.js +15 -5
  98. package/dist/hooks/useTaskData.d.ts +10 -1
  99. package/dist/hooks/useTaskData.js +216 -7
  100. package/dist/hooks/useTaskMutations.d.ts +10 -2
  101. package/dist/hooks/useTaskMutations.js +223 -43
  102. package/dist/hooks/useTaskforce.d.ts +94 -57
  103. package/dist/hooks/useTaskforce.js +428 -16
  104. package/dist/hooks/useUiNotice.d.ts +4 -0
  105. package/dist/hooks/useUiNotice.js +4 -0
  106. package/dist/mcp/adminWorkspaceRegistrar.js +3 -3
  107. package/dist/mcp/canonicalAssetHelpers.d.ts +13 -0
  108. package/dist/mcp/canonicalAssetHelpers.js +39 -5
  109. package/dist/mcp/collaborationRegistrar.js +15 -13
  110. package/dist/mcp/documentAssetRegistrar.js +60 -44
  111. package/dist/mcp/documentHelpers.d.ts +1 -0
  112. package/dist/mcp/documentHelpers.js +7 -3
  113. package/dist/mcp/localServiceProxy.js +4 -1
  114. package/dist/mcp/runtime.d.ts +16 -0
  115. package/dist/mcp/runtime.js +735 -210
  116. package/dist/mcp/taskAttachmentCommandAdapter.d.ts +2 -0
  117. package/dist/mcp/taskAttachmentCommandAdapter.js +14 -8
  118. package/dist/mcp/taskPlanningRegistrar.js +78 -67
  119. package/dist/mcp/toolCallCompatibility.js +38 -1
  120. package/dist/mcp/toolCatalog.d.ts +1 -0
  121. package/dist/mcp/toolRegistry.js +2 -2
  122. package/dist/migrations/taskSchemaMigrations.js +47 -0
  123. package/dist/runtime/appGateDefinitions.d.ts +30 -6
  124. package/dist/runtime/appGateDefinitions.js +6 -6
  125. package/dist/runtime/appGates.d.ts +12 -13
  126. package/dist/runtime/appGates.js +29 -13
  127. package/dist/runtime/nodeAppGates.d.ts +8 -0
  128. package/dist/runtime/nodeAppGates.js +27 -0
  129. package/dist/server/appAccess.d.ts +12 -0
  130. package/dist/server/appAccess.js +21 -0
  131. package/dist/server/authorizedWorkspaceAccess.d.ts +12 -0
  132. package/dist/server/authorizedWorkspaceAccess.js +40 -0
  133. package/dist/server/cloudCollectionRead.d.ts +25 -0
  134. package/dist/server/cloudCollectionRead.js +120 -0
  135. package/dist/server/cloudCollectionReadPool.d.ts +52 -0
  136. package/dist/server/cloudCollectionReadPool.js +140 -0
  137. package/dist/server/cloudCollectionReadWorker.d.ts +1 -0
  138. package/dist/server/cloudCollectionReadWorker.js +36 -0
  139. package/dist/server/index.d.ts +6 -11
  140. package/dist/server/index.js +67 -42
  141. package/dist/server/localServiceLease.d.ts +6 -0
  142. package/dist/server/localServiceLease.js +17 -1
  143. package/dist/server/localWorkspaceSyncServerRuntime.js +10 -3
  144. package/dist/server/rateLimit.d.ts +1 -0
  145. package/dist/server/rateLimit.js +4 -0
  146. package/dist/server/realtimeSyncWs.js +36 -12
  147. package/dist/server/routes/admin.js +1 -0
  148. package/dist/server/routes/agentRoles.js +3 -2
  149. package/dist/server/routes/agentSkills.js +3 -2
  150. package/dist/server/routes/agents.js +118 -16
  151. package/dist/server/routes/annotatedAttachments.js +3 -2
  152. package/dist/server/routes/auth.js +3 -1
  153. package/dist/server/routes/documents.d.ts +5 -2
  154. package/dist/server/routes/documents.js +160 -13
  155. package/dist/server/routes/durableTaskHttpRouters.d.ts +11 -2
  156. package/dist/server/routes/durableTaskHttpRouters.js +2 -2
  157. package/dist/server/routes/localService.js +4 -0
  158. package/dist/server/routes/primitives.d.ts +4 -0
  159. package/dist/server/routes/shared.d.ts +24 -0
  160. package/dist/server/routes/shared.js +114 -18
  161. package/dist/server/routes/sync.js +22 -0
  162. package/dist/server/routes/syncAuxRoutes.js +5 -2
  163. package/dist/server/routes/syncPullApplyRoutes.js +23 -4
  164. package/dist/server/routes/syncV3FeedRoutes.js +22 -18
  165. package/dist/server/routes/syncV3LocalCaptureRoutes.js +48 -4
  166. package/dist/server/routes/syncV3LocalFeedApplyRoutes.d.ts +1 -0
  167. package/dist/server/routes/syncV3LocalFeedApplyRoutes.js +26 -0
  168. package/dist/server/routes/syncV3OperationRoutes.js +27 -2
  169. package/dist/server/routes/tasks.js +299 -97
  170. package/dist/server/routes/workspaces.js +43 -1
  171. package/dist/server/routes.js +35 -7
  172. package/dist/server/workspaceDeletionAdmission.d.ts +23 -0
  173. package/dist/server/workspaceDeletionAdmission.js +50 -0
  174. package/dist/service/localServiceCli.js +52 -25
  175. package/dist/service/localServiceClientLifecycle.js +93 -6
  176. package/dist/services/codexAppServerModelGateway.d.ts +112 -0
  177. package/dist/services/codexAppServerModelGateway.js +438 -0
  178. package/dist/services/taskforceAgentMcpBridge.d.ts +7 -1
  179. package/dist/services/taskforceAgentMcpBridge.js +23 -3
  180. package/dist/shared/planningIdentity.d.ts +20 -0
  181. package/dist/shared/planningIdentity.js +79 -0
  182. package/dist/shared/taskforceAgentDefinition.js +7 -1
  183. package/dist/shared/taskforceAgentModels.d.ts +1 -0
  184. package/dist/shared/taskforceAgentModels.js +30 -1
  185. package/dist/storage/postgresAdapter.d.ts +20 -3
  186. package/dist/storage/postgresAdapter.js +110 -14
  187. package/dist/storage/postgresRequestDiagnostics.d.ts +8 -0
  188. package/dist/storage/postgresRequestDiagnostics.js +24 -0
  189. package/dist/storage/postgresWorker.js +13 -4
  190. package/dist/storage/postgresWorkerProtocol.d.ts +29 -0
  191. package/dist/storage/postgresWorkerProtocol.js +30 -0
  192. package/dist/storage/workspaceAssetStore.d.ts +32 -0
  193. package/dist/storage/workspaceAssetStore.js +70 -0
  194. package/dist/sync/cloudSyncApi.d.ts +4 -1
  195. package/dist/sync/cloudSyncApi.js +7 -2
  196. package/dist/sync/contentSyncState.d.ts +1 -1
  197. package/dist/sync/coordinator/localSyncExecutionPermitStore.d.ts +17 -0
  198. package/dist/sync/coordinator/localSyncExecutionPermitStore.js +77 -23
  199. package/dist/sync/coordinator/localWorkspaceSyncRunner.js +6 -2
  200. package/dist/sync/coordinator/workspaceSyncTransportGateway.js +4 -2
  201. package/dist/sync/engine/workspaceSyncEngineBrowserGatewayRemoteServices.js +20 -2
  202. package/dist/sync/engine/workspaceSyncEngineLocalServices.d.ts +4 -0
  203. package/dist/sync/engine/workspaceSyncEngineLocalServices.js +11 -0
  204. package/dist/sync/engine/workspaceSyncEngineRemoteServices.d.ts +16 -0
  205. package/dist/sync/engine/workspaceSyncEngineRemoteServices.js +2 -1
  206. package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.d.ts +1 -1
  207. package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +46 -4
  208. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.d.ts +3 -1
  209. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +51 -11
  210. package/dist/sync/engine/workspaceSyncEngineTransfers.d.ts +1 -1
  211. package/dist/sync/engine/workspaceSyncEngineTransfers.js +8 -3
  212. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +2 -0
  213. package/dist/sync/syncApplyHandlers.d.ts +3 -1
  214. package/dist/sync/syncApplyHandlers.js +29 -0
  215. package/dist/sync/syncService.d.ts +2 -0
  216. package/dist/sync/syncService.js +13 -1
  217. package/dist/sync/taskSyncPayload.d.ts +1 -0
  218. package/dist/sync/taskSyncPayload.js +6 -0
  219. package/dist/sync/v3/combinedFeedCapability.js +4 -0
  220. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.d.ts +3 -0
  221. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +31 -3
  222. package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +2 -0
  223. package/dist/sync/v3/durableTaskChecklistMutationRouter.js +4 -0
  224. package/dist/sync/v3/durableTaskHttpBulkMutationRouter.d.ts +1 -0
  225. package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +47 -12
  226. package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +5 -3
  227. package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +27 -2
  228. package/dist/sync/v3/localInitiativeOutboxService.js +4 -2
  229. package/dist/sync/v3/localOutboxDispatcher.js +5 -1
  230. package/dist/sync/v3/localTaskAttachmentLinksOutboxHandler.js +12 -2
  231. package/dist/sync/v3/localTaskAttachmentLinksOutboxService.d.ts +3 -0
  232. package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +62 -10
  233. package/dist/sync/v3/localTaskCreateOutboxService.js +15 -6
  234. package/dist/sync/v3/localTaskMetadataClient.d.ts +1 -0
  235. package/dist/sync/v3/localTaskMetadataClient.js +1 -0
  236. package/dist/sync/v3/localTaskMetadataOutboxHandler.js +5 -1
  237. package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -0
  238. package/dist/sync/v3/localTaskMetadataOutboxService.js +7 -0
  239. package/dist/sync/v3/localTaskRestoreOutboxService.js +14 -6
  240. package/dist/sync/v3/localTaskStatusClient.d.ts +1 -0
  241. package/dist/sync/v3/localTaskStatusClient.js +6 -1
  242. package/dist/sync/v3/localTaskStatusOutboxService.d.ts +2 -0
  243. package/dist/sync/v3/localTaskStatusOutboxService.js +54 -13
  244. package/dist/sync/v3/localWorkstreamOutboxService.js +4 -2
  245. package/dist/sync/v3/syncDurabilityStore.d.ts +15 -0
  246. package/dist/sync/v3/syncDurabilityStore.js +201 -43
  247. package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +7 -4
  248. package/dist/sync/v3/taskAttachmentLinksMutationService.js +37 -12
  249. package/dist/sync/v3/taskChecklistMutationService.d.ts +4 -2
  250. package/dist/sync/v3/taskChecklistMutationService.js +4 -0
  251. package/dist/sync/v3/taskCreateMutationService.d.ts +2 -2
  252. package/dist/sync/v3/taskCreateMutationService.js +7 -4
  253. package/dist/sync/v3/taskMetadataCoexistence.js +4 -0
  254. package/dist/sync/v3/taskMetadataMutationService.d.ts +3 -0
  255. package/dist/sync/v3/taskMetadataMutationService.js +4 -0
  256. package/dist/sync/v3/taskRestoreMutationService.d.ts +2 -2
  257. package/dist/sync/v3/taskRestoreMutationService.js +7 -4
  258. package/dist/sync/v3/taskStatusMutationService.d.ts +2 -0
  259. package/dist/sync/v3/taskStatusMutationService.js +4 -0
  260. package/dist/sync/v3/workflowAssignmentSync.d.ts +1 -0
  261. package/dist/sync/v3/workflowAssignmentSync.js +3 -0
  262. package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.d.ts +1 -1
  263. package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +14 -12
  264. package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.d.ts +11 -0
  265. package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.js +27 -10
  266. package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.d.ts +2 -1
  267. package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.js +52 -10
  268. package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +10 -1
  269. package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +15 -4
  270. package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +66 -13
  271. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +11 -0
  272. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +57 -22
  273. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +10 -4
  274. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +124 -51
  275. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +10 -0
  276. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +120 -28
  277. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +3 -1
  278. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +18 -0
  279. package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +6 -0
  280. package/dist/sync/v3/workspaceSyncV3FeedProfile.d.ts +1 -1
  281. package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.d.ts +1 -0
  282. package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.js +23 -10
  283. package/dist/sync/v3/workspaceSyncV3NestedTaskApply.d.ts +3 -0
  284. package/dist/sync/v3/workspaceSyncV3NestedTaskApply.js +64 -13
  285. package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.d.ts +9 -0
  286. package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.js +26 -0
  287. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +15 -5
  288. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +50 -14
  289. package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.d.ts +2 -1
  290. package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.js +11 -3
  291. package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +3 -0
  292. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +2 -0
  293. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +19 -7
  294. package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.d.ts +2 -0
  295. package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.js +10 -0
  296. package/dist/sync/v3/workspaceSyncV3TaskCommentAudit.js +9 -2
  297. package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.d.ts +7 -0
  298. package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.js +37 -0
  299. package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.d.ts +6 -0
  300. package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.js +9 -0
  301. package/dist/sync/v3/workspaceSyncV3TaskRootProjection.d.ts +1 -1
  302. package/dist/sync/v3/workspaceSyncV3TaskRootProjection.js +1 -1
  303. package/dist/sync/workspacePullFeed.d.ts +6 -1
  304. package/dist/sync/workspacePullFeed.js +174 -162
  305. package/dist/sync/workspaceRepair.d.ts +3 -0
  306. package/dist/sync/workspaceRepair.js +67 -6
  307. package/dist/sync/workspaceSyncModel.d.ts +11 -0
  308. package/dist/sync/workspaceSyncModel.js +27 -2
  309. package/dist/types.d.ts +55 -1
  310. package/dist/types.js +6 -0
  311. package/dist/ui/assets/{AiIdentityRosterCard-DB1BLO_j.js → AiIdentityRosterCard-BZM4aQnM.js} +1 -1
  312. package/dist/ui/assets/AiProfilesModule-BbAEWtHH.js +1 -0
  313. package/dist/ui/assets/AnnotatedAttachmentWorkspace-B68Wg-RV.js +3 -0
  314. package/dist/ui/assets/AnnotatedAttachmentWorkspace-TzYu7pkG.css +1 -0
  315. package/dist/ui/assets/AssetTraySortControl-C8Rztyal.js +1 -0
  316. package/dist/ui/assets/ContextAttachmentManager-Cj3c7X9y.js +3 -0
  317. package/dist/ui/assets/{DocumentWorkspace-BC7sy5TG.js → DocumentWorkspace-B5uvzems.js} +4 -4
  318. package/dist/ui/assets/EntityActivityTimeline-BVFvCB5R.js +1 -0
  319. package/dist/ui/assets/PlanningModule-B5DWQOGz.js +1 -0
  320. package/dist/ui/assets/PlanningModule-CoIR9hbV.css +1 -0
  321. package/dist/ui/assets/PlansPage-AY4cGWco.js +1 -0
  322. package/dist/ui/assets/TaskContextUpload-DZxTVesW.js +1 -0
  323. package/dist/ui/assets/TaskContextUpload-qmBeUw3u.css +1 -0
  324. package/dist/ui/assets/TaskSettings-BeWAuyZj.js +12 -0
  325. package/dist/ui/assets/{TaskforceAgentsModule-CkAxW19K.js → TaskforceAgentsModule-BifCYtjR.js} +6 -6
  326. package/dist/ui/assets/WorkflowManagerModule-DEr5di_r.js +1 -0
  327. package/dist/ui/assets/index-I5zhdtgt.js +7 -0
  328. package/dist/ui/assets/index-VRFsx3wz.css +1 -0
  329. package/dist/ui/assets/{vendor-icons-B8ZNsH1g.js → vendor-icons-D9Lpw-j4.js} +1 -1
  330. package/dist/ui/index.html +3 -3
  331. package/dist/utils/constants.d.ts +2 -2
  332. package/dist/utils/constants.js +1 -1
  333. package/dist/utils/contextAttachmentUpload.d.ts +2 -0
  334. package/dist/utils/contextAttachmentUpload.js +7 -0
  335. package/dist/utils/contextFiles.d.ts +1 -1
  336. package/dist/utils/contextFiles.js +4 -4
  337. package/dist/utils/taskActivity.d.ts +3 -2
  338. package/dist/utils/taskActivity.js +19 -2
  339. package/dist/utils/taskEditorReconciliation.d.ts +40 -0
  340. package/dist/utils/taskEditorReconciliation.js +217 -0
  341. package/dist/utils/taskNormalization.d.ts +1 -0
  342. package/dist/utils/taskNormalization.js +39 -0
  343. package/dist/utils/taskSearch.js +2 -2
  344. package/dist/utils/uiNotice.d.ts +4 -0
  345. package/package.json +21 -4
  346. package/dist/ui/assets/AiProfilesModule-DPxQeF9N.js +0 -1
  347. package/dist/ui/assets/AnnotatedAttachmentWorkspace-CBRZ8rca.css +0 -1
  348. package/dist/ui/assets/AnnotatedAttachmentWorkspace-DHNYaNg6.js +0 -3
  349. package/dist/ui/assets/AssetTraySortControl-DphbyHVc.js +0 -1
  350. package/dist/ui/assets/ContextAttachmentManager-CMxSzHqc.js +0 -3
  351. package/dist/ui/assets/EntityActivityTimeline-pJ2ZjZ-k.js +0 -1
  352. package/dist/ui/assets/PlanningModule-CeTqXMMz.css +0 -1
  353. package/dist/ui/assets/PlanningModule-t5GbvVR9.js +0 -1
  354. package/dist/ui/assets/PlansPage-BVH_NfgT.js +0 -1
  355. package/dist/ui/assets/TaskContextUpload-Ds4LnDzV.css +0 -1
  356. package/dist/ui/assets/TaskContextUpload-bl4fOOUz.js +0 -1
  357. package/dist/ui/assets/TaskSettings-xVsxSa0g.js +0 -12
  358. package/dist/ui/assets/WorkflowManagerModule-BKqIGVnF.js +0 -1
  359. package/dist/ui/assets/index-BzJlVnNS.css +0 -1
  360. package/dist/ui/assets/index-Cx7vZJHL.js +0 -7
@@ -57,7 +57,7 @@ import { resolveBuildInfo } from '../buildInfo.js';
57
57
  import { resolveRealtimeSyncFlag } from '../../sync/realtimeFeature.js';
58
58
  import { parseJsonBody } from './primitives.js';
59
59
  import { resolveRequestAccess } from '../auth.js';
60
- import { getRouteTimingNow, logSlowBootstrapRoute, requestBootstrapTraceId, requestHeader, requestRuntimeMode, requestUserId, resolveErrorStatusCode, serializeJsonWithTiming, setServerTimingHeader } from './shared.js';
60
+ import { currentPerformanceTraceDiagnostics, getRouteTimingNow, logSlowBootstrapRoute, performanceTraceServerTimingMetrics, requestBootstrapTraceId, requestHeader, requestHumanActorRef, requestRuntimeMode, respondWithPerformanceTraceJson, resolveErrorStatusCode, runWithPerformanceTrace, serializeJsonWithTiming, setPerformanceTraceHeaders, setServerTimingHeader } from './shared.js';
61
61
  import { listBuiltInCustomTaxonomyLibraryPacks } from '../../shared/customTaxonomyLibrary.js';
62
62
  import { getBuiltInTaxonomyLibraryPack, listBuiltInTaxonomyLibraryPacks } from '../../shared/taxonomyLibrary.js';
63
63
  import { shouldUseProvisionalTaskReferences } from '../../utils/taskReferences.js';
@@ -177,7 +177,7 @@ export function registerTaskRoutes(deps) {
177
177
  surface: 'http',
178
178
  tenantId: core.getTenantId(),
179
179
  workspaceId,
180
- actorRef: runtimeMode === 'cloud' ? (String(requestUserId(req) || '').trim() || 'user') : 'user',
180
+ actorRef: requestHumanActorRef(req),
181
181
  cloudClientId: 'http-cloud',
182
182
  ...(operationId ? { createOperationId: () => operationId } : {}),
183
183
  });
@@ -192,7 +192,7 @@ export function registerTaskRoutes(deps) {
192
192
  surface: 'http',
193
193
  tenantId: core.getTenantId(),
194
194
  workspaceId,
195
- actorRef: runtimeMode === 'cloud' ? (String(requestUserId(req) || '').trim() || 'user') : 'user',
195
+ actorRef: requestHumanActorRef(req),
196
196
  cloudClientId: 'http-cloud',
197
197
  ...(operationId ? { createOperationId: () => operationId } : {}),
198
198
  });
@@ -465,9 +465,70 @@ export function registerTaskRoutes(deps) {
465
465
  if (!ensureAppAccess(req, res))
466
466
  return;
467
467
  const workspaceId = requestWorkspaceId(req);
468
- const data = core.listTasks(workspaceId);
469
- res.writeHead(200, { 'Content-Type': 'application/json' });
470
- res.end(JSON.stringify(data));
468
+ respondWithPerformanceTraceJson({
469
+ req,
470
+ res,
471
+ route: '/api/taskforce/tasks',
472
+ workspaceId,
473
+ // Local V2 sync still builds an outbound snapshot from these collections.
474
+ // Keep its full graph until that writer is retired; cloud is read-authoritative.
475
+ load: () => (context.authConfig?.runtimeMode === 'cloud' ? 'cloud' : requestRuntimeMode(req)) === 'cloud'
476
+ ? core.listActiveTaskSummaries(workspaceId)
477
+ : core.listTasks(workspaceId),
478
+ });
479
+ });
480
+ // GET /api/taskforce/tasks/search - Search lightweight task summaries across lifecycles
481
+ addRoute('GET', '/api/taskforce/tasks/search', async (req, res) => {
482
+ if (!ensureAppAccess(req, res))
483
+ return;
484
+ try {
485
+ const requestUrl = new URL(req.url || '', 'http://localhost');
486
+ const query = String(requestUrl.searchParams.get('query') || '').trim();
487
+ if (!query) {
488
+ throw new TaskforceRuleError('query is required.', 400, 'TASK_SEARCH_QUERY_REQUIRED');
489
+ }
490
+ if (query.length > 200) {
491
+ throw new TaskforceRuleError('query must be 200 characters or fewer.', 400, 'TASK_SEARCH_QUERY_TOO_LONG');
492
+ }
493
+ const scopeInput = String(requestUrl.searchParams.get('scope') || 'all').trim();
494
+ if (!['all', 'active', 'archive'].includes(scopeInput)) {
495
+ throw new TaskforceRuleError('scope must be all, active, or archive.', 400, 'TASK_SEARCH_SCOPE_INVALID');
496
+ }
497
+ const parseBoundedInteger = (name, fallback, max) => {
498
+ const raw = requestUrl.searchParams.get(name);
499
+ if (raw === null || raw === '')
500
+ return fallback;
501
+ const parsed = Number(raw);
502
+ if (!Number.isInteger(parsed) || parsed < (name === 'limit' ? 1 : 0) || parsed > max) {
503
+ throw new TaskforceRuleError(`${name} must be an integer between ${name === 'limit' ? 1 : 0} and ${max}.`, 400, `TASK_SEARCH_${name.toUpperCase()}_INVALID`);
504
+ }
505
+ return parsed;
506
+ };
507
+ const limit = parseBoundedInteger('limit', 50, 50);
508
+ const offset = parseBoundedInteger('offset', 0, 10_000);
509
+ const workspaceId = requestWorkspaceId(req);
510
+ const rows = core.searchTaskSummaries(query, scopeInput, workspaceId, { limit: limit + 1, offset });
511
+ const hasMore = rows.length > limit;
512
+ const matches = rows.slice(0, limit);
513
+ res.writeHead(200, { 'Content-Type': 'application/json' });
514
+ res.end(JSON.stringify({
515
+ matches,
516
+ pagination: {
517
+ limit,
518
+ offset,
519
+ returned: matches.length,
520
+ hasMore,
521
+ nextOffset: hasMore ? offset + matches.length : null,
522
+ },
523
+ }));
524
+ }
525
+ catch (error) {
526
+ const statusCode = resolveErrorStatusCode(error, 400);
527
+ const message = error instanceof Error ? error.message : 'Failed to search tasks.';
528
+ const code = error instanceof TaskforceRuleError ? error.code : error?.code;
529
+ res.writeHead(statusCode, { 'Content-Type': 'application/json' });
530
+ res.end(JSON.stringify({ error: message, code }));
531
+ }
471
532
  });
472
533
  addRoute('GET', '/api/taskforce/task-relationships', async (req, res) => {
473
534
  if (!ensureAppAccess(req, res))
@@ -480,9 +541,13 @@ export function registerTaskRoutes(deps) {
480
541
  if (!ensureAppAccess(req, res))
481
542
  return;
482
543
  const workspaceId = requestWorkspaceId(req);
483
- const data = core.listDeletedTasks(workspaceId);
484
- res.writeHead(200, { 'Content-Type': 'application/json' });
485
- res.end(JSON.stringify(data));
544
+ respondWithPerformanceTraceJson({
545
+ req,
546
+ res,
547
+ route: '/api/taskforce/deleted',
548
+ workspaceId,
549
+ load: () => core.listDeletedTasks(workspaceId),
550
+ });
486
551
  };
487
552
  // GET /api/taskforce/deleted - List deleted tasks
488
553
  addRoute('GET', '/api/taskforce/deleted', handleListDeletedTasks);
@@ -589,7 +654,7 @@ export function registerTaskRoutes(deps) {
589
654
  const durableRouter = createTaskHttpRootRouter(req, workspaceId);
590
655
  const task = durableRouter.enabled ? durableRouter.create(taskInput) : core.createTask(taskInput, {
591
656
  workspaceId,
592
- actorRef: 'user',
657
+ actorRef: requestHumanActorRef(req),
593
658
  provisionalLocalReference: shouldUseProvisionalTaskReferences(requestRuntimeMode(req))
594
659
  });
595
660
  res.writeHead(201, { 'Content-Type': 'application/json' });
@@ -609,6 +674,20 @@ export function registerTaskRoutes(deps) {
609
674
  }));
610
675
  }
611
676
  });
677
+ // GET /api/taskforce/task/:id - Read complete active or archived task detail
678
+ addRoute('GET', '/api/taskforce/task/:id', async (req, res, params) => {
679
+ if (!ensureAppAccess(req, res))
680
+ return;
681
+ const workspaceId = requestWorkspaceId(req);
682
+ const task = core.getTaskDetailSnapshot(params.id, workspaceId);
683
+ if (!task) {
684
+ res.writeHead(404, { 'Content-Type': 'application/json' });
685
+ res.end(JSON.stringify({ error: 'Task not found' }));
686
+ return;
687
+ }
688
+ res.writeHead(200, { 'Content-Type': 'application/json' });
689
+ res.end(JSON.stringify(task));
690
+ });
612
691
  // PATCH /api/taskforce/task/:id - Update a task
613
692
  addRoute('PATCH', '/api/taskforce/task/:id', async (req, res, params) => {
614
693
  if (!ensureAppAccess(req, res))
@@ -617,8 +696,15 @@ export function registerTaskRoutes(deps) {
617
696
  try {
618
697
  const workspaceId = requestWorkspaceId(req);
619
698
  const saveSource = body?.saveSource === 'autosave' ? 'autosave' : 'manual';
699
+ const expectedDetailRevision = typeof body?.expectedDetailRevision === 'string'
700
+ ? body.expectedDetailRevision.trim()
701
+ : null;
702
+ if (body && Object.prototype.hasOwnProperty.call(body, 'expectedDetailRevision')
703
+ && !expectedDetailRevision) {
704
+ throw new TaskforceRuleError('expectedDetailRevision must be a non-empty string.', 400, 'TASK_DETAIL_REVISION_INVALID');
705
+ }
620
706
  const updates = body && typeof body === 'object'
621
- ? Object.fromEntries(Object.entries(body).filter(([key]) => key !== 'saveSource'))
707
+ ? Object.fromEntries(Object.entries(body).filter(([key]) => (key !== 'saveSource' && key !== 'expectedDetailRevision')))
622
708
  : body;
623
709
  const durableRouters = createTaskHttpDurableRouters({ core, context, req, workspaceId });
624
710
  const durableRouter = durableRouters.root;
@@ -630,22 +716,34 @@ export function registerTaskRoutes(deps) {
630
716
  if (hasAttachments && !Array.isArray(updates.attachments)) {
631
717
  throw new TaskforceRuleError('attachments must be an array.', 400, 'TASK_ATTACHMENT_LINKS_INVALID');
632
718
  }
719
+ const hasCardCover = Object.prototype.hasOwnProperty.call(updates || {}, 'cardCoverAssetId');
720
+ if (hasCardCover && updates.cardCoverAssetId !== null
721
+ && (typeof updates.cardCoverAssetId !== 'string' || !updates.cardCoverAssetId.trim())) {
722
+ throw new TaskforceRuleError('cardCoverAssetId must be a non-empty string or null.', 400, 'TASK_CARD_COVER_INVALID');
723
+ }
633
724
  const checklistDurable = hasChecklist && durableRouters.checklist.enabled;
634
- const attachmentLinksDurable = hasAttachments && durableRouters.attachmentLinks.enabled;
725
+ const attachmentLinksDurable = (hasAttachments || hasCardCover) && durableRouters.attachmentLinks.enabled;
635
726
  const rawChecklistItems = hasChecklist ? updates.checklistItems : [];
636
- const rawAttachments = hasAttachments ? updates.attachments : [];
727
+ const rawAttachments = hasAttachments
728
+ ? updates.attachments
729
+ : (core.getTask(params.id, workspaceId)?.attachments || []);
637
730
  const remainingUpdates = checklistDurable || attachmentLinksDurable
638
731
  ? Object.fromEntries(Object.entries(updates).filter(([key]) => (!(checklistDurable && key === 'checklistItems')
639
- && !(attachmentLinksDurable && key === 'attachments'))))
732
+ && !(attachmentLinksDurable && key === 'attachments')
733
+ && !(attachmentLinksDurable && key === 'cardCoverAssetId'))))
640
734
  : updates;
641
735
  let updated;
642
736
  const durableTargetCount = Number(checklistDurable)
643
737
  + Number(attachmentLinksDurable)
644
738
  + Number(Object.keys(remainingUpdates).length > 0);
645
- if (durableTargetCount > 1) {
739
+ if (durableTargetCount > 1 || Boolean(expectedDetailRevision && (checklistDurable || attachmentLinksDurable))) {
646
740
  const aggregate = durableRouters.bulk.updateFields([
647
741
  { id: params.id, ...updates },
648
- ], { saveSource, atomicItemFailures: true });
742
+ ], {
743
+ saveSource,
744
+ atomicItemFailures: true,
745
+ ...(expectedDetailRevision ? { expectedDetailRevision } : {}),
746
+ });
649
747
  const failure = aggregate.errors[0];
650
748
  if (failure) {
651
749
  throw new TaskforceRuleError(failure.message, failure.statusCode, failure.code || 'TASK_UPDATE_FAILED', failure.details);
@@ -655,11 +753,13 @@ export function registerTaskRoutes(deps) {
655
753
  else {
656
754
  updated = Object.keys(remainingUpdates || {}).length > 0
657
755
  ? (durableRouter.enabled
658
- ? durableRouter.update(params.id, remainingUpdates, saveSource)
659
- : core.updateTask(params.id, remainingUpdates, workspaceId, {
660
- actorRef: 'user',
661
- saveSource,
662
- }))
756
+ ? durableRouter.update(params.id, remainingUpdates, saveSource, undefined, expectedDetailRevision || undefined)
757
+ : expectedDetailRevision !== null
758
+ ? core.updateTaskIfDetailRevisionMatches(params.id, expectedDetailRevision, remainingUpdates, workspaceId, { actorRef: requestHumanActorRef(req), saveSource })
759
+ : core.updateTask(params.id, remainingUpdates, workspaceId, {
760
+ actorRef: requestHumanActorRef(req),
761
+ saveSource,
762
+ }))
663
763
  : core.getTask(params.id, workspaceId);
664
764
  if (updated && checklistDurable) {
665
765
  const checklistRouter = durableRouters.createChecklistRouter(durableRouters.deriveChildOperationId('task-patch-checklist', 0, params.id));
@@ -679,7 +779,15 @@ export function registerTaskRoutes(deps) {
679
779
  core,
680
780
  workspaceId,
681
781
  });
682
- updated = attachmentLinksRouter.replace({ taskId: params.id, links }).task;
782
+ updated = attachmentLinksRouter.replace({
783
+ taskId: params.id,
784
+ links,
785
+ ...(hasCardCover ? {
786
+ cardCoverAssetId: updates.cardCoverAssetId === null
787
+ ? null
788
+ : String(updates.cardCoverAssetId).trim(),
789
+ } : {}),
790
+ }).task;
683
791
  }
684
792
  }
685
793
  if (!updated) {
@@ -687,8 +795,14 @@ export function registerTaskRoutes(deps) {
687
795
  res.end(JSON.stringify({ error: 'Task not found' }));
688
796
  return;
689
797
  }
798
+ const authoritative = core.getTaskDetailSnapshot(params.id, workspaceId);
799
+ if (!authoritative) {
800
+ res.writeHead(404, { 'Content-Type': 'application/json' });
801
+ res.end(JSON.stringify({ error: 'Task not found' }));
802
+ return;
803
+ }
690
804
  res.writeHead(200, { 'Content-Type': 'application/json' });
691
- res.end(JSON.stringify(updated));
805
+ res.end(JSON.stringify(authoritative));
692
806
  }
693
807
  catch (error) {
694
808
  const statusCode = resolveErrorStatusCode(error, 400);
@@ -766,43 +880,53 @@ export function registerTaskRoutes(deps) {
766
880
  addRoute('GET', '/api/taskforce/planning/bootstrap', async (req, res) => {
767
881
  if (!ensureAppAccess(req, res))
768
882
  return;
769
- const startedAt = getRouteTimingNow();
770
- const workspaceId = requestWorkspaceId(req);
771
- const traceId = requestBootstrapTraceId(req);
772
- const diagnostics = {};
773
- const data = core.listPlanningBootstrap(workspaceId, diagnostics);
774
- const serializeStartedAt = getRouteTimingNow();
775
- const serialized = serializeJsonWithTiming(data, serializeStartedAt);
776
- const totalDurationMs = Math.max(0, getRouteTimingNow() - startedAt);
777
- const details = diagnostics;
778
- setServerTimingHeader(res, [
779
- { name: 'planning-bootstrap', durationMs: totalDurationMs },
780
- { name: 'planning-bootstrap-initiatives', durationMs: details.initiativeLoadMs ?? 0 },
781
- { name: 'planning-bootstrap-workstreams', durationMs: details.workstreamLoadMs ?? 0 },
782
- { name: 'planning-bootstrap-tasks', durationMs: details.taskSummaryQueryMs ?? 0 },
783
- { name: 'planning-bootstrap-build', durationMs: details.taskSummaryBuildMs ?? 0 },
784
- { name: 'planning-bootstrap-json', durationMs: serialized.durationMs },
785
- ]);
786
- logSlowBootstrapRoute({
787
- route: '/api/taskforce/planning/bootstrap',
788
- workspaceId,
789
- traceId,
790
- durationMs: totalDurationMs,
791
- details: {
792
- initiativeCount: details.initiativeCount ?? 0,
793
- workstreamCount: details.workstreamCount ?? 0,
794
- workstreamLinkedTaskCount: details.workstreamLinkedTaskCount ?? 0,
795
- workstreamTaskSummaryCount: details.workstreamTaskSummaryCount ?? 0,
796
- responseBytes: serialized.byteLength,
797
- initiativeLoadMs: details.initiativeLoadMs ?? 0,
798
- workstreamLoadMs: details.workstreamLoadMs ?? 0,
799
- taskSummaryQueryMs: details.taskSummaryQueryMs ?? 0,
800
- taskSummaryBuildMs: details.taskSummaryBuildMs ?? 0,
801
- jsonSerializeMs: serialized.durationMs,
802
- },
883
+ runWithPerformanceTrace(req, () => {
884
+ const startedAt = getRouteTimingNow();
885
+ const workspaceId = requestWorkspaceId(req);
886
+ const traceId = requestBootstrapTraceId(req);
887
+ const diagnostics = {};
888
+ const data = core.listPlanningBootstrap(workspaceId, diagnostics);
889
+ const serializeStartedAt = getRouteTimingNow();
890
+ const serialized = serializeJsonWithTiming(data, serializeStartedAt);
891
+ const totalDurationMs = Math.max(0, getRouteTimingNow() - startedAt);
892
+ const details = diagnostics;
893
+ const performanceDiagnostics = currentPerformanceTraceDiagnostics();
894
+ setServerTimingHeader(res, [
895
+ { name: 'planning-bootstrap', durationMs: totalDurationMs },
896
+ { name: 'planning-bootstrap-initiatives', durationMs: details.initiativeLoadMs ?? 0 },
897
+ { name: 'planning-bootstrap-workstreams', durationMs: details.workstreamLoadMs ?? 0 },
898
+ { name: 'planning-bootstrap-tasks', durationMs: details.taskSummaryQueryMs ?? 0 },
899
+ { name: 'planning-bootstrap-build', durationMs: details.taskSummaryBuildMs ?? 0 },
900
+ { name: 'planning-bootstrap-json', durationMs: serialized.durationMs },
901
+ ...performanceTraceServerTimingMetrics(performanceDiagnostics),
902
+ ]);
903
+ setPerformanceTraceHeaders(res, performanceDiagnostics, serialized.byteLength);
904
+ logSlowBootstrapRoute({
905
+ route: '/api/taskforce/planning/bootstrap',
906
+ workspaceId,
907
+ traceId,
908
+ durationMs: totalDurationMs,
909
+ details: {
910
+ initiativeCount: details.initiativeCount ?? 0,
911
+ workstreamCount: details.workstreamCount ?? 0,
912
+ workstreamLinkedTaskCount: details.workstreamLinkedTaskCount ?? 0,
913
+ workstreamTaskSummaryCount: details.workstreamTaskSummaryCount ?? 0,
914
+ responseBytes: serialized.byteLength,
915
+ initiativeLoadMs: details.initiativeLoadMs ?? 0,
916
+ workstreamLoadMs: details.workstreamLoadMs ?? 0,
917
+ taskSummaryQueryMs: details.taskSummaryQueryMs ?? 0,
918
+ taskSummaryBuildMs: details.taskSummaryBuildMs ?? 0,
919
+ jsonSerializeMs: serialized.durationMs,
920
+ ...(performanceDiagnostics ? {
921
+ postgresQueryCount: performanceDiagnostics.postgresQueryCount,
922
+ postgresWorkerCallCount: performanceDiagnostics.postgresWorkerCallCount,
923
+ postgresWorkerWaitMs: Math.round(performanceDiagnostics.postgresWorkerWaitMs),
924
+ } : {}),
925
+ },
926
+ });
927
+ res.writeHead(200, { 'Content-Type': 'application/json' });
928
+ res.end(serialized.body);
803
929
  });
804
- res.writeHead(200, { 'Content-Type': 'application/json' });
805
- res.end(serialized.body);
806
930
  });
807
931
  // POST /api/taskforce/initiative - Create initiative
808
932
  addRoute('POST', '/api/taskforce/initiative', async (req, res) => {
@@ -812,7 +936,7 @@ export function registerTaskRoutes(deps) {
812
936
  try {
813
937
  const workspaceId = requestWorkspaceId(req);
814
938
  const router = createInitiativeRouter(req, workspaceId);
815
- const allowed = new Set(['id', 'title', 'description', 'ownerId', 'order', 'attachments', 'createdAt']);
939
+ const allowed = new Set(['id', 'title', 'description', 'ownerId', 'order', 'attachments', 'createdAt', 'icon', 'color']);
816
940
  const unsupported = Object.keys(body || {}).filter((key) => !allowed.has(key));
817
941
  if (router.enabled && unsupported.length > 0) {
818
942
  const error = new Error(`Unsupported initiative fields: ${unsupported.sort().join(', ')}`);
@@ -826,16 +950,28 @@ export function registerTaskRoutes(deps) {
826
950
  error.statusCode = 400;
827
951
  throw error;
828
952
  }
829
- const initiative = router.enabled
830
- ? initiativeResult(router.create(String(body?.id || '').trim() || `initiative-${randomUUID()}`, {
831
- title: body?.title,
832
- description: body?.description ?? null,
833
- ownerId: body?.ownerId ?? null,
834
- order: body?.order ?? null,
835
- attachments: serializeCanonicalPlanningAttachmentLinks(body?.attachments ?? []),
836
- createdAt: String(body?.createdAt || '').trim() || new Date().toISOString(),
837
- }))
838
- : core.createInitiative(body || {}, workspaceId, { actorRef: 'user' });
953
+ const initiative = core.getDatabaseAdapter().transaction(() => {
954
+ const created = router.enabled
955
+ ? initiativeResult(router.create(String(body?.id || '').trim() || `initiative-${randomUUID()}`, {
956
+ title: body?.title,
957
+ description: body?.description ?? null,
958
+ ownerId: body?.ownerId ?? null,
959
+ order: body?.order ?? null,
960
+ attachments: serializeCanonicalPlanningAttachmentLinks(body?.attachments ?? []),
961
+ createdAt: String(body?.createdAt || '').trim() || new Date().toISOString(),
962
+ }))
963
+ : core.createInitiative(body || {}, workspaceId, { actorRef: requestHumanActorRef(req) });
964
+ if (!created)
965
+ throw new Error('Durable initiative capture did not return a provisional initiative.');
966
+ const hasIdentity = Object.prototype.hasOwnProperty.call(body || {}, 'icon')
967
+ || Object.prototype.hasOwnProperty.call(body || {}, 'color');
968
+ return hasIdentity
969
+ ? core.updatePlanningEntityIdentity('initiative', created.id, {
970
+ ...(Object.prototype.hasOwnProperty.call(body || {}, 'icon') ? { icon: body.icon } : {}),
971
+ ...(Object.prototype.hasOwnProperty.call(body || {}, 'color') ? { color: body.color } : {}),
972
+ }, workspaceId)
973
+ : created;
974
+ })();
839
975
  if (!initiative)
840
976
  throw new Error('Durable initiative capture did not return a provisional initiative.');
841
977
  res.writeHead(201, { 'Content-Type': 'application/json' });
@@ -883,7 +1019,7 @@ export function registerTaskRoutes(deps) {
883
1019
  };
884
1020
  const updated = router.enabled
885
1021
  ? initiativeResult(router.updateMetadata(params.id, durablePatch))
886
- : core.updateInitiative(params.id, body || {}, workspaceId, { actorRef: 'user' });
1022
+ : core.updateInitiative(params.id, body || {}, workspaceId, { actorRef: requestHumanActorRef(req) });
887
1023
  if (!updated)
888
1024
  throw new Error('Durable initiative capture did not return a provisional initiative.');
889
1025
  res.writeHead(200, { 'Content-Type': 'application/json' });
@@ -893,6 +1029,30 @@ export function registerTaskRoutes(deps) {
893
1029
  writeInitiativeMutationError(res, error, 'Failed to update initiative');
894
1030
  }
895
1031
  });
1032
+ addRoute('PATCH', '/api/taskforce/initiative/:id/identity', async (req, res, params) => {
1033
+ if (!ensureAppAccess(req, res))
1034
+ return;
1035
+ const body = await parseJsonBody(req);
1036
+ try {
1037
+ const workspaceId = requestWorkspaceId(req);
1038
+ const source = body && typeof body === 'object' && !Array.isArray(body) ? body : {};
1039
+ const keys = Object.keys(source);
1040
+ if (keys.length === 0 || keys.some((key) => key !== 'icon' && key !== 'color')) {
1041
+ throw new TaskforceRuleError('Planning identity update must contain only icon or color.', 400, 'PLANNING_IDENTITY_INVALID');
1042
+ }
1043
+ const updated = core.updatePlanningEntityIdentity('initiative', params.id, source, workspaceId);
1044
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1045
+ res.end(JSON.stringify(updated));
1046
+ }
1047
+ catch (error) {
1048
+ const statusCode = resolveErrorStatusCode(error, 400);
1049
+ res.writeHead(statusCode, { 'Content-Type': 'application/json' });
1050
+ res.end(JSON.stringify({
1051
+ error: error instanceof Error ? error.message : 'Failed to update initiative identity',
1052
+ code: error instanceof TaskforceRuleError ? error.code : undefined,
1053
+ }));
1054
+ }
1055
+ });
896
1056
  addRoute('POST', '/api/taskforce/initiative/:id/archive', async (req, res, params) => {
897
1057
  if (!ensureAppAccess(req, res))
898
1058
  return;
@@ -901,7 +1061,7 @@ export function registerTaskRoutes(deps) {
901
1061
  const router = createInitiativeRouter(req, workspaceId);
902
1062
  const archived = router.enabled
903
1063
  ? initiativeResult(router.archive(params.id))
904
- : core.archiveInitiative(params.id, workspaceId, { actorRef: 'user' });
1064
+ : core.archiveInitiative(params.id, workspaceId, { actorRef: requestHumanActorRef(req) });
905
1065
  if (!archived)
906
1066
  throw new Error('Durable initiative capture did not return a provisional initiative.');
907
1067
  res.writeHead(200, { 'Content-Type': 'application/json' });
@@ -919,7 +1079,7 @@ export function registerTaskRoutes(deps) {
919
1079
  const router = createInitiativeRouter(req, workspaceId);
920
1080
  const restored = router.enabled
921
1081
  ? initiativeResult(router.unarchive(params.id))
922
- : core.unarchiveInitiative(params.id, workspaceId, { actorRef: 'user' });
1082
+ : core.unarchiveInitiative(params.id, workspaceId, { actorRef: requestHumanActorRef(req) });
923
1083
  if (!restored)
924
1084
  throw new Error('Durable initiative capture did not return a provisional initiative.');
925
1085
  res.writeHead(200, { 'Content-Type': 'application/json' });
@@ -946,7 +1106,7 @@ export function registerTaskRoutes(deps) {
946
1106
  try {
947
1107
  const workspaceId = requestWorkspaceId(req);
948
1108
  const router = createWorkstreamRouter(req, workspaceId);
949
- const allowed = new Set(['id', 'initiativeId', 'title', 'description', 'ownerId', 'order', 'attachments', 'createdAt']);
1109
+ const allowed = new Set(['id', 'initiativeId', 'title', 'description', 'ownerId', 'order', 'attachments', 'createdAt', 'icon', 'color']);
950
1110
  const unsupported = Object.keys(body || {}).filter((key) => !allowed.has(key));
951
1111
  if (router.enabled && unsupported.length > 0) {
952
1112
  const error = new Error(`Unsupported workstream fields: ${unsupported.sort().join(', ')}`);
@@ -960,17 +1120,29 @@ export function registerTaskRoutes(deps) {
960
1120
  error.statusCode = 400;
961
1121
  throw error;
962
1122
  }
963
- const workstream = router.enabled
964
- ? workstreamResult(router.create(String(body?.id || '').trim(), {
965
- initiativeId: body?.initiativeId ?? null,
966
- title: body?.title,
967
- description: body?.description ?? null,
968
- ownerId: body?.ownerId ?? null,
969
- order: body?.order ?? null,
970
- attachments: serializeCanonicalPlanningAttachmentLinks(body?.attachments ?? []),
971
- createdAt: String(body?.createdAt || '').trim(),
972
- }))
973
- : core.createWorkstream(body || {}, workspaceId, { actorRef: 'user' });
1123
+ const workstream = core.getDatabaseAdapter().transaction(() => {
1124
+ const created = router.enabled
1125
+ ? workstreamResult(router.create(String(body?.id || '').trim(), {
1126
+ initiativeId: body?.initiativeId ?? null,
1127
+ title: body?.title,
1128
+ description: body?.description ?? null,
1129
+ ownerId: body?.ownerId ?? null,
1130
+ order: body?.order ?? null,
1131
+ attachments: serializeCanonicalPlanningAttachmentLinks(body?.attachments ?? []),
1132
+ createdAt: String(body?.createdAt || '').trim(),
1133
+ }))
1134
+ : core.createWorkstream(body || {}, workspaceId, { actorRef: requestHumanActorRef(req) });
1135
+ if (!created)
1136
+ throw new Error('Durable workstream capture did not return a provisional workstream.');
1137
+ const hasIdentity = Object.prototype.hasOwnProperty.call(body || {}, 'icon')
1138
+ || Object.prototype.hasOwnProperty.call(body || {}, 'color');
1139
+ return hasIdentity
1140
+ ? core.updatePlanningEntityIdentity('workstream', created.id, {
1141
+ ...(Object.prototype.hasOwnProperty.call(body || {}, 'icon') ? { icon: body.icon } : {}),
1142
+ ...(Object.prototype.hasOwnProperty.call(body || {}, 'color') ? { color: body.color } : {}),
1143
+ }, workspaceId)
1144
+ : created;
1145
+ })();
974
1146
  if (!workstream)
975
1147
  throw new Error('Durable workstream capture did not return a provisional workstream.');
976
1148
  res.writeHead(201, { 'Content-Type': 'application/json' });
@@ -1018,7 +1190,7 @@ export function registerTaskRoutes(deps) {
1018
1190
  };
1019
1191
  const updated = router.enabled
1020
1192
  ? workstreamResult(router.updateMetadata(params.id, durablePatch))
1021
- : core.updateWorkstream(params.id, body || {}, workspaceId, { actorRef: 'user' });
1193
+ : core.updateWorkstream(params.id, body || {}, workspaceId, { actorRef: requestHumanActorRef(req) });
1022
1194
  if (!updated)
1023
1195
  throw new Error('Durable workstream capture did not return a provisional workstream.');
1024
1196
  res.writeHead(200, { 'Content-Type': 'application/json' });
@@ -1028,6 +1200,30 @@ export function registerTaskRoutes(deps) {
1028
1200
  writeWorkstreamMutationError(res, error, 'Failed to update workstream');
1029
1201
  }
1030
1202
  });
1203
+ addRoute('PATCH', '/api/taskforce/workstream/:id/identity', async (req, res, params) => {
1204
+ if (!ensureAppAccess(req, res))
1205
+ return;
1206
+ const body = await parseJsonBody(req);
1207
+ try {
1208
+ const workspaceId = requestWorkspaceId(req);
1209
+ const source = body && typeof body === 'object' && !Array.isArray(body) ? body : {};
1210
+ const keys = Object.keys(source);
1211
+ if (keys.length === 0 || keys.some((key) => key !== 'icon' && key !== 'color')) {
1212
+ throw new TaskforceRuleError('Planning identity update must contain only icon or color.', 400, 'PLANNING_IDENTITY_INVALID');
1213
+ }
1214
+ const updated = core.updatePlanningEntityIdentity('workstream', params.id, source, workspaceId);
1215
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1216
+ res.end(JSON.stringify(updated));
1217
+ }
1218
+ catch (error) {
1219
+ const statusCode = resolveErrorStatusCode(error, 400);
1220
+ res.writeHead(statusCode, { 'Content-Type': 'application/json' });
1221
+ res.end(JSON.stringify({
1222
+ error: error instanceof Error ? error.message : 'Failed to update workstream identity',
1223
+ code: error instanceof TaskforceRuleError ? error.code : undefined,
1224
+ }));
1225
+ }
1226
+ });
1031
1227
  addRoute('POST', '/api/taskforce/workstream/:id/archive', async (req, res, params) => {
1032
1228
  if (!ensureAppAccess(req, res))
1033
1229
  return;
@@ -1036,7 +1232,7 @@ export function registerTaskRoutes(deps) {
1036
1232
  const router = createWorkstreamRouter(req, workspaceId);
1037
1233
  const archived = router.enabled
1038
1234
  ? workstreamResult(router.archive(params.id))
1039
- : core.archiveWorkstream(params.id, workspaceId, { actorRef: 'user' });
1235
+ : core.archiveWorkstream(params.id, workspaceId, { actorRef: requestHumanActorRef(req) });
1040
1236
  if (!archived)
1041
1237
  throw new Error('Durable workstream capture did not return a provisional workstream.');
1042
1238
  res.writeHead(200, { 'Content-Type': 'application/json' });
@@ -1054,7 +1250,7 @@ export function registerTaskRoutes(deps) {
1054
1250
  const router = createWorkstreamRouter(req, workspaceId);
1055
1251
  const restored = router.enabled
1056
1252
  ? workstreamResult(router.unarchive(params.id))
1057
- : core.unarchiveWorkstream(params.id, workspaceId, { actorRef: 'user' });
1253
+ : core.unarchiveWorkstream(params.id, workspaceId, { actorRef: requestHumanActorRef(req) });
1058
1254
  if (!restored)
1059
1255
  throw new Error('Durable workstream capture did not return a provisional workstream.');
1060
1256
  res.writeHead(200, { 'Content-Type': 'application/json' });
@@ -1180,9 +1376,15 @@ export function registerTaskRoutes(deps) {
1180
1376
  if (!ensureAppAccess(req, res))
1181
1377
  return;
1182
1378
  const workspaceId = requestWorkspaceId(req);
1183
- const data = core.listArchive(workspaceId);
1184
- res.writeHead(200, { 'Content-Type': 'application/json' });
1185
- res.end(JSON.stringify(data));
1379
+ respondWithPerformanceTraceJson({
1380
+ req,
1381
+ res,
1382
+ route: '/api/taskforce/archive',
1383
+ workspaceId,
1384
+ load: () => (context.authConfig?.runtimeMode === 'cloud' ? 'cloud' : requestRuntimeMode(req)) === 'cloud'
1385
+ ? core.listArchiveSummaries(workspaceId)
1386
+ : core.listArchive(workspaceId),
1387
+ });
1186
1388
  });
1187
1389
  // POST /api/taskforce/task/:id/complete - Archive as complete
1188
1390
  addRoute('POST', '/api/taskforce/task/:id/complete', async (req, res, params) => {
@@ -1193,7 +1395,7 @@ export function registerTaskRoutes(deps) {
1193
1395
  const durableRouter = createTaskHttpRootRouter(req, workspaceId);
1194
1396
  const archived = durableRouter.enabled
1195
1397
  ? durableRouter.archive(params.id, false, null)
1196
- : core.archiveTask(params.id, false, undefined, workspaceId, { actorRef: 'user' });
1398
+ : core.archiveTask(params.id, false, undefined, workspaceId, { actorRef: requestHumanActorRef(req) });
1197
1399
  if (!archived) {
1198
1400
  res.writeHead(404, { 'Content-Type': 'application/json' });
1199
1401
  res.end(JSON.stringify({ error: 'Task not found' }));
@@ -1221,7 +1423,7 @@ export function registerTaskRoutes(deps) {
1221
1423
  const durableRouter = createTaskHttpRootRouter(req, workspaceId);
1222
1424
  const archived = durableRouter.enabled
1223
1425
  ? durableRouter.archive(params.id, true, reason)
1224
- : core.archiveTask(params.id, true, reason, workspaceId, { actorRef: 'user' });
1426
+ : core.archiveTask(params.id, true, reason, workspaceId, { actorRef: requestHumanActorRef(req) });
1225
1427
  if (!archived) {
1226
1428
  res.writeHead(404, { 'Content-Type': 'application/json' });
1227
1429
  res.end(JSON.stringify({ error: 'Task not found' }));
@@ -1247,7 +1449,7 @@ export function registerTaskRoutes(deps) {
1247
1449
  const durableRouter = createTaskHttpRootRouter(req, workspaceId);
1248
1450
  const deleted = durableRouter.enabled
1249
1451
  ? durableRouter.delete(params.id)
1250
- : core.softDeleteTask(params.id, workspaceId, { actorRef: 'user', source: 'ui' });
1452
+ : core.softDeleteTask(params.id, workspaceId, { actorRef: requestHumanActorRef(req), source: 'ui' });
1251
1453
  if (!deleted) {
1252
1454
  res.writeHead(404, { 'Content-Type': 'application/json' });
1253
1455
  res.end(JSON.stringify({ error: 'Task not found' }));
@@ -1272,7 +1474,7 @@ export function registerTaskRoutes(deps) {
1272
1474
  const durableRouter = createTaskHttpRootRouter(req, workspaceId);
1273
1475
  const restored = durableRouter.enabled
1274
1476
  ? durableRouter.restore(params.id)
1275
- : core.restoreDeletedTask(params.id, workspaceId, { actorRef: 'user' });
1477
+ : core.restoreDeletedTask(params.id, workspaceId, { actorRef: requestHumanActorRef(req) });
1276
1478
  if (!restored) {
1277
1479
  res.writeHead(404, { 'Content-Type': 'application/json' });
1278
1480
  res.end(JSON.stringify({ error: 'Deleted task not found' }));
@@ -1362,7 +1564,7 @@ export function registerTaskRoutes(deps) {
1362
1564
  continue;
1363
1565
  }
1364
1566
  const task = core.restoreDeletedTask(deletedRecordId, workspaceId, {
1365
- actorRef: 'user',
1567
+ actorRef: requestHumanActorRef(req),
1366
1568
  restoreDestination,
1367
1569
  });
1368
1570
  if (!task) {
@@ -1533,7 +1735,7 @@ export function registerTaskRoutes(deps) {
1533
1735
  const durableRouter = createTaskHttpRootRouter(req, workspaceId);
1534
1736
  const restored = durableRouter.enabled
1535
1737
  ? durableRouter.unarchive(params.id)
1536
- : core.unarchiveTask(params.id, workspaceId, { actorRef: 'user' });
1738
+ : core.unarchiveTask(params.id, workspaceId, { actorRef: requestHumanActorRef(req) });
1537
1739
  if (!restored) {
1538
1740
  res.writeHead(404, { 'Content-Type': 'application/json' });
1539
1741
  res.end(JSON.stringify({ error: 'Archived task not found' }));