@taskforcehq/taskforce 0.3.329 → 0.3.332

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 (363) hide show
  1. package/README.md +7 -2
  2. package/dist/Taskforce.module.css +290 -45
  3. package/dist/components/features/AiProfilesModule.js +3 -5
  4. package/dist/components/features/TaskSettings.js +70 -14
  5. package/dist/components/features/TaskforceAgentsModule.d.ts +1 -2
  6. package/dist/components/features/TaskforceAgentsModule.js +586 -116
  7. package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +2 -2
  8. package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.js +2 -2
  9. package/dist/components/features/agentConnections/AgentConnectionControl.d.ts +15 -0
  10. package/dist/components/features/agentConnections/AgentConnectionControl.js +125 -0
  11. package/dist/components/features/agentConnections/ModelConnectionsPanel.d.ts +18 -0
  12. package/dist/components/features/agentConnections/ModelConnectionsPanel.js +393 -0
  13. package/dist/components/features/agentConnections/modelConnectionsApi.d.ts +65 -0
  14. package/dist/components/features/agentConnections/modelConnectionsApi.js +129 -0
  15. package/dist/components/features/planning/PlanningModule.js +15 -3
  16. package/dist/components/features/planning/planningMetadata.d.ts +10 -0
  17. package/dist/components/features/planning/planningMetadata.js +8 -0
  18. package/dist/components/features/taskforceAgentAssignments/AgentBehaviorAssignments.js +15 -24
  19. package/dist/components/features/taskforceAgentEditorModel.d.ts +2 -1
  20. package/dist/components/features/taskforceAgentEditorModel.js +3 -1
  21. package/dist/components/features/workflowManager/workflowManagerApi.d.ts +1 -1
  22. package/dist/components/features/workflowManager/workflowManagerApi.js +3 -0
  23. package/dist/components/task/TaskWorkflowAssignmentField.js +68 -10
  24. package/dist/components/views/StandaloneLayout.js +62 -27
  25. package/dist/components/views/panels/PlanningDrawer.d.ts +2 -1
  26. package/dist/components/views/panels/PlanningDrawer.js +12 -8
  27. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +4 -3
  28. package/dist/components/views/standalone/modals/SyncStatusModal.js +33 -38
  29. package/dist/config/envSchema.js +55 -1
  30. package/dist/core/McpTokenService.d.ts +25 -1
  31. package/dist/core/McpTokenService.js +106 -15
  32. package/dist/core/TaskChecklistCommandService.d.ts +27 -0
  33. package/dist/core/TaskChecklistCommandService.js +83 -5
  34. package/dist/core/TaskLifecycleCommandService.d.ts +3 -1
  35. package/dist/core/TaskLifecycleCommandService.js +6 -2
  36. package/dist/core/Taskforce.d.ts +43 -3
  37. package/dist/core/Taskforce.js +158 -7
  38. package/dist/core/types.d.ts +9 -0
  39. package/dist/documentReviews/DocumentReviewCommentStore.d.ts +2 -0
  40. package/dist/documentReviews/DocumentReviewCommentStore.js +28 -9
  41. package/dist/documentReviews/commandService.d.ts +2 -1
  42. package/dist/documentReviews/commandService.js +6 -3
  43. package/dist/hooks/sync/orchestratorShared.d.ts +1 -0
  44. package/dist/hooks/sync/orchestratorShared.js +2 -0
  45. package/dist/hooks/sync/useLocalSyncCoordinatorSnapshot.d.ts +3 -0
  46. package/dist/hooks/sync/useLocalSyncCoordinatorSnapshot.js +85 -9
  47. package/dist/hooks/sync/useSyncStatusControls.d.ts +4 -1
  48. package/dist/hooks/sync/useSyncStatusControls.js +47 -11
  49. package/dist/hooks/useSyncOrchestrator.d.ts +3 -0
  50. package/dist/hooks/useSyncOrchestrator.js +111 -2
  51. package/dist/hooks/useTaskData.d.ts +31 -1
  52. package/dist/hooks/useTaskData.js +29 -7
  53. package/dist/hooks/useTaskMutations.d.ts +3 -1
  54. package/dist/hooks/useTaskMutations.js +46 -12
  55. package/dist/hooks/useTaskforce.d.ts +2 -0
  56. package/dist/hooks/useTaskforce.js +11 -2
  57. package/dist/mcp/clientRegistry.d.ts +2 -2
  58. package/dist/mcp/clientRegistry.js +1 -1
  59. package/dist/mcp/documentAssetRegistrar.js +9 -6
  60. package/dist/mcp/durableTaskMutationRouter.d.ts +4 -1
  61. package/dist/mcp/durableTaskMutationRouter.js +10 -0
  62. package/dist/mcp/durableTaskRelationshipMutationRouter.js +4 -0
  63. package/dist/mcp/httpProtocolRouting.d.ts +11 -0
  64. package/dist/mcp/httpProtocolRouting.js +22 -0
  65. package/dist/mcp/localCliHealth.d.ts +2 -2
  66. package/dist/mcp/localCliHealth.js +2 -2
  67. package/dist/mcp/localServiceProxy.d.ts +1 -0
  68. package/dist/mcp/localServiceProxy.js +530 -68
  69. package/dist/mcp/planningAttachmentCommandAdapter.d.ts +3 -1
  70. package/dist/mcp/planningAttachmentCommandAdapter.js +20 -2
  71. package/dist/mcp/runtime.d.ts +54 -79
  72. package/dist/mcp/runtime.js +408 -102
  73. package/dist/mcp/taskPlanningRegistrar.js +25 -6
  74. package/dist/mcp/toolProfiles.d.ts +2 -0
  75. package/dist/mcp/toolProfiles.js +11 -0
  76. package/dist/mcp/toolRegistry.d.ts +1 -0
  77. package/dist/mcp/toolRegistry.js +9 -8
  78. package/dist/mcp/workflowExecutionRegistrar.js +4 -4
  79. package/dist/migrations/taskSchemaMigrations.d.ts +5 -0
  80. package/dist/migrations/taskSchemaMigrations.js +415 -0
  81. package/dist/runtime/appGates.d.ts +2 -1
  82. package/dist/runtime/appGates.js +7 -0
  83. package/dist/server/buildInfo.d.ts +1 -0
  84. package/dist/server/buildInfo.js +5 -1
  85. package/dist/server/cloudMcpCapacityTelemetry.d.ts +68 -0
  86. package/dist/server/cloudMcpCapacityTelemetry.js +221 -0
  87. package/dist/server/index.d.ts +3 -0
  88. package/dist/server/index.js +108 -11
  89. package/dist/server/localDatabaseIdentity.js +4 -1
  90. package/dist/server/localServiceLease.js +2 -1
  91. package/dist/server/localServiceSessions.d.ts +14 -17
  92. package/dist/server/localServiceSessions.js +308 -94
  93. package/dist/server/localWorkspaceSyncServerRuntime.js +25 -19
  94. package/dist/server/routes/admin.d.ts +4 -0
  95. package/dist/server/routes/admin.js +126 -5
  96. package/dist/server/routes/agents.d.ts +7 -0
  97. package/dist/server/routes/agents.js +277 -24
  98. package/dist/server/routes/billing.js +5 -1
  99. package/dist/server/routes/durableTaskHttpRouters.d.ts +2 -0
  100. package/dist/server/routes/executorPhaseAApprovals.d.ts +19 -0
  101. package/dist/server/routes/executorPhaseAApprovals.js +140 -0
  102. package/dist/server/routes/executorPhaseAIssuerRedemptions.d.ts +11 -0
  103. package/dist/server/routes/executorPhaseAIssuerRedemptions.js +24 -0
  104. package/dist/server/routes/githubReviewEvidence.d.ts +19 -0
  105. package/dist/server/routes/githubReviewEvidence.js +116 -0
  106. package/dist/server/routes/localService.d.ts +15 -4
  107. package/dist/server/routes/localService.js +136 -40
  108. package/dist/server/routes/modelConnections.d.ts +24 -0
  109. package/dist/server/routes/modelConnections.js +205 -0
  110. package/dist/server/routes/shared.d.ts +22 -0
  111. package/dist/server/routes/shared.js +298 -182
  112. package/dist/server/routes/sync.js +1 -0
  113. package/dist/server/routes/syncAuxRoutes.js +5 -1
  114. package/dist/server/routes/syncPushRoutes.d.ts +1 -0
  115. package/dist/server/routes/syncPushRoutes.js +1 -0
  116. package/dist/server/routes/syncV3FeedRoutes.js +10 -1
  117. package/dist/server/routes/syncV3OperationRoutes.js +35 -9
  118. package/dist/server/routes/tasks.js +21 -2
  119. package/dist/server/routes/workflows.js +9 -1
  120. package/dist/server/routes.js +59 -0
  121. package/dist/service/localServiceCli.d.ts +2 -2
  122. package/dist/service/localServiceCli.js +4 -3
  123. package/dist/services/codexAppServerModelGateway.d.ts +54 -5
  124. package/dist/services/codexAppServerModelGateway.js +246 -21
  125. package/dist/services/codexDeviceCodeLoginManager.d.ts +39 -0
  126. package/dist/services/codexDeviceCodeLoginManager.js +265 -0
  127. package/dist/services/codexModelProviderConnectionLifecycle.d.ts +8 -0
  128. package/dist/services/codexModelProviderConnectionLifecycle.js +73 -0
  129. package/dist/services/executorPhaseAApprovalRuntime.d.ts +14 -0
  130. package/dist/services/executorPhaseAApprovalRuntime.js +24 -0
  131. package/dist/services/executorPhaseAApprovalService.d.ts +40 -0
  132. package/dist/services/executorPhaseAApprovalService.js +609 -0
  133. package/dist/services/executorPhaseADriftEvaluator.d.ts +78 -0
  134. package/dist/services/executorPhaseADriftEvaluator.js +93 -0
  135. package/dist/services/executorPhaseAIssuerCore.d.ts +139 -0
  136. package/dist/services/executorPhaseAIssuerCore.js +402 -0
  137. package/dist/services/executorPhaseAIssuerRedemptionService.d.ts +34 -0
  138. package/dist/services/executorPhaseAIssuerRedemptionService.js +158 -0
  139. package/dist/services/githubReviewEvidenceRuntime.d.ts +14 -0
  140. package/dist/services/githubReviewEvidenceRuntime.js +30 -0
  141. package/dist/services/githubReviewEvidenceService.d.ts +93 -0
  142. package/dist/services/githubReviewEvidenceService.js +336 -0
  143. package/dist/services/modelGateway.d.ts +7 -0
  144. package/dist/services/modelProviderConnectionService.d.ts +71 -0
  145. package/dist/services/modelProviderConnectionService.js +279 -0
  146. package/dist/services/modelProviderCredentialHome.d.ts +32 -0
  147. package/dist/services/modelProviderCredentialHome.js +271 -0
  148. package/dist/services/ownerScopedModelConnectionGateway.d.ts +12 -0
  149. package/dist/services/ownerScopedModelConnectionGateway.js +30 -0
  150. package/dist/services/taskforceAgentConnectionBindingService.d.ts +66 -0
  151. package/dist/services/taskforceAgentConnectionBindingService.js +185 -0
  152. package/dist/services/taskforceAgentMcpBridge.d.ts +2 -2
  153. package/dist/services/taskforceAgentMcpBridge.js +42 -29
  154. package/dist/shared/adminEdgeProxy.d.ts +5 -0
  155. package/dist/shared/adminEdgeProxy.js +94 -0
  156. package/dist/shared/adminProxySessionCookies.d.ts +16 -0
  157. package/dist/shared/adminProxySessionCookies.js +86 -0
  158. package/dist/shared/aiProfileColors.d.ts +31 -1
  159. package/dist/shared/aiProfileColors.js +14 -6
  160. package/dist/shared/executorPhaseAApprovalGoldenVector.json +269 -0
  161. package/dist/shared/executorPhaseAApprovalReceipt.d.ts +112 -0
  162. package/dist/shared/executorPhaseAApprovalReceipt.js +58 -0
  163. package/dist/shared/executorPhaseAApprovalTrustPolicy.json +10 -0
  164. package/dist/shared/executorPhaseADriftContracts.d.ts +37 -0
  165. package/dist/shared/executorPhaseADriftContracts.js +63 -0
  166. package/dist/shared/executorPhaseAIssuerContracts.d.ts +75 -0
  167. package/dist/shared/executorPhaseAIssuerContracts.js +90 -0
  168. package/dist/shared/executorPhaseARunAuthorization.d.ts +120 -0
  169. package/dist/shared/executorPhaseARunAuthorization.js +232 -0
  170. package/dist/shared/executorPhaseASchemaValidation.d.ts +7 -0
  171. package/dist/shared/executorPhaseASchemaValidation.js +53 -0
  172. package/dist/shared/executorPhaseASchemas/approval-receipt.schema.json +106 -0
  173. package/dist/shared/executorPhaseASchemas/drift-attestation.schema.json +54 -0
  174. package/dist/shared/executorPhaseASchemas/drift-request.schema.json +47 -0
  175. package/dist/shared/executorPhaseASchemas/foundation-manifest.schema.json +29 -0
  176. package/dist/shared/executorPhaseASchemas/issuer-audit-record.schema.json +24 -0
  177. package/dist/shared/executorPhaseASchemas/issuer-proof.schema.json +43 -0
  178. package/dist/shared/executorPhaseASchemas/issuer-redemption-request.schema.json +18 -0
  179. package/dist/shared/executorPhaseASchemas/issuer-redemption-response.schema.json +39 -0
  180. package/dist/shared/executorPhaseASchemas/logical-envelope.schema.json +149 -0
  181. package/dist/shared/executorPhaseASchemas/preflight-report.schema.json +216 -0
  182. package/dist/shared/executorPhaseASchemas/run-capability.schema.json +111 -0
  183. package/dist/shared/executorPhaseASchemas/sender-proof.schema.json +37 -0
  184. package/dist/shared/executorPhaseASchemas/workload-grant.schema.json +68 -0
  185. package/dist/shared/executorPhaseASchemas/workload-jwt-claims.schema.json +67 -0
  186. package/dist/shared/executorPhaseATiming.d.ts +1 -0
  187. package/dist/shared/executorPhaseATiming.js +1 -0
  188. package/dist/shared/planningIdentity.js +1 -1
  189. package/dist/shared/productLogoKeys.d.ts +2 -0
  190. package/dist/shared/productLogoKeys.js +18 -0
  191. package/dist/shared/taskforceAgentDefinition.d.ts +0 -1
  192. package/dist/shared/taskforceAgentDefinition.js +6 -9
  193. package/dist/shared/taskforceAgentModels.js +7 -3
  194. package/dist/shared/userFeatureGrants.d.ts +4 -0
  195. package/dist/shared/userFeatureGrants.js +7 -0
  196. package/dist/storage/executorPhaseAApprovalReceiptStore.d.ts +37 -0
  197. package/dist/storage/executorPhaseAApprovalReceiptStore.js +81 -0
  198. package/dist/storage/executorPhaseARunAuthorizationStore.d.ts +103 -0
  199. package/dist/storage/executorPhaseARunAuthorizationStore.js +492 -0
  200. package/dist/storage/githubReviewEvidenceInstallationStore.d.ts +26 -0
  201. package/dist/storage/githubReviewEvidenceInstallationStore.js +89 -0
  202. package/dist/storage/modelProviderConnectionStore.d.ts +99 -0
  203. package/dist/storage/modelProviderConnectionStore.js +446 -0
  204. package/dist/storage/postgresAdapter.d.ts +4 -0
  205. package/dist/storage/postgresAdapter.js +14 -1
  206. package/dist/storage/postgresRequestDiagnostics.d.ts +9 -1
  207. package/dist/storage/postgresRequestDiagnostics.js +15 -2
  208. package/dist/storage/postgresWorker.js +20 -2
  209. package/dist/storage/postgresWorkerProtocol.d.ts +7 -0
  210. package/dist/storage/taskforceAgentConnectionBindingStore.d.ts +31 -0
  211. package/dist/storage/taskforceAgentConnectionBindingStore.js +84 -0
  212. package/dist/storage/workflowStore.d.ts +2 -0
  213. package/dist/storage/workflowStore.js +39 -2
  214. package/dist/sync/cloudSyncApi.js +4 -2
  215. package/dist/sync/collaborationSyncState.js +5 -0
  216. package/dist/sync/coordinator/localSyncExecutionPermitStore.js +60 -29
  217. package/dist/sync/coordinator/localWorkspaceSyncPendingV2PushProgress.d.ts +10 -0
  218. package/dist/sync/coordinator/localWorkspaceSyncPendingV2PushProgress.js +29 -0
  219. package/dist/sync/coordinator/localWorkspaceSyncRunner.d.ts +3 -1
  220. package/dist/sync/coordinator/localWorkspaceSyncRunner.js +18 -0
  221. package/dist/sync/coordinator/localWorkspaceSyncRunnerState.d.ts +15 -1
  222. package/dist/sync/coordinator/localWorkspaceSyncRunnerState.js +99 -2
  223. package/dist/sync/coordinator/workspaceSyncCredentialBroker.d.ts +1 -0
  224. package/dist/sync/coordinator/workspaceSyncCredentialBroker.js +33 -4
  225. package/dist/sync/engine/workspaceSyncContentLifecycleAdapter.d.ts +2 -1
  226. package/dist/sync/engine/workspaceSyncContentVersionStore.d.ts +38 -0
  227. package/dist/sync/engine/workspaceSyncContentVersionStore.js +50 -1
  228. package/dist/sync/engine/workspaceSyncEngineLifecyclePolicy.js +6 -6
  229. package/dist/sync/engine/workspaceSyncEnginePushCompletion.d.ts +14 -0
  230. package/dist/sync/engine/workspaceSyncEnginePushCompletion.js +84 -0
  231. package/dist/sync/engine/workspaceSyncEngineRecovery.js +1 -0
  232. package/dist/sync/engine/workspaceSyncEngineRuntime.d.ts +1 -0
  233. package/dist/sync/engine/workspaceSyncEngineRuntime.js +1 -0
  234. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +112 -38
  235. package/dist/sync/engine/workspaceSyncEngineServerSnapshotReader.js +6 -0
  236. package/dist/sync/engine/workspaceSyncEngineTransfers.js +3 -1
  237. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.d.ts +1 -0
  238. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +31 -0
  239. package/dist/sync/syncApplyHandlers.d.ts +3 -0
  240. package/dist/sync/syncApplyHandlers.js +81 -15
  241. package/dist/sync/syncService.d.ts +15 -0
  242. package/dist/sync/syncService.js +83 -18
  243. package/dist/sync/taskforceAgentSyncAuthorization.d.ts +16 -0
  244. package/dist/sync/taskforceAgentSyncAuthorization.js +20 -0
  245. package/dist/sync/v3/combinedFeedCapability.js +4 -0
  246. package/dist/sync/v3/durableInitiativeMutationRouter.js +18 -10
  247. package/dist/sync/v3/durableOperationReplay.d.ts +14 -0
  248. package/dist/sync/v3/durableOperationReplay.js +18 -0
  249. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +3 -0
  250. package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +2 -1
  251. package/dist/sync/v3/durableTaskChecklistMutationRouter.js +58 -15
  252. package/dist/sync/v3/durableTaskCommentMutationRouter.js +3 -0
  253. package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +1 -1
  254. package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +10 -0
  255. package/dist/sync/v3/durableTaxonomyMutationRouter.js +5 -0
  256. package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.d.ts +18 -7
  257. package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.js +89 -23
  258. package/dist/sync/v3/durableWorkstreamMutationRouter.js +18 -10
  259. package/dist/sync/v3/durableWorkstreamTaskOrderMutationRouter.js +16 -5
  260. package/dist/sync/v3/localOutboxDispatchRuntime.js +2 -2
  261. package/dist/sync/v3/localOutboxRecoveryPreparationService.js +8 -2
  262. package/dist/sync/v3/localTaskChecklistOutboxHandler.js +16 -2
  263. package/dist/sync/v3/localTaskChecklistOutboxService.d.ts +4 -0
  264. package/dist/sync/v3/localTaskChecklistOutboxService.js +14 -1
  265. package/dist/sync/v3/localTaskCreateOutboxService.d.ts +3 -1
  266. package/dist/sync/v3/localTaskCreateOutboxService.js +6 -1
  267. package/dist/sync/v3/localTaskDeleteOutboxService.js +1 -0
  268. package/dist/sync/v3/localTaskLifecycleOutboxService.d.ts +2 -1
  269. package/dist/sync/v3/localTaskLifecycleOutboxService.js +5 -1
  270. package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -1
  271. package/dist/sync/v3/localTaskMetadataOutboxService.js +6 -2
  272. package/dist/sync/v3/localTaskStatusOutboxService.d.ts +10 -1
  273. package/dist/sync/v3/localTaskStatusOutboxService.js +238 -16
  274. package/dist/sync/v3/syncDurabilityStore.d.ts +36 -1
  275. package/dist/sync/v3/syncDurabilityStore.js +202 -8
  276. package/dist/sync/v3/taskChecklistMutationService.d.ts +11 -0
  277. package/dist/sync/v3/taskChecklistMutationService.js +44 -1
  278. package/dist/sync/v3/taskStatusMutationService.d.ts +29 -1
  279. package/dist/sync/v3/taskStatusMutationService.js +119 -3
  280. package/dist/sync/v3/workflowAssignmentSync.d.ts +5 -0
  281. package/dist/sync/v3/workflowAssignmentSync.js +27 -0
  282. package/dist/sync/v3/workflowRuntimeMutationService.d.ts +5 -0
  283. package/dist/sync/v3/workflowRuntimeMutationService.js +124 -71
  284. package/dist/sync/v3/workspaceSyncJournalReader.js +1 -1
  285. package/dist/sync/v3/workspaceSyncOperationRetentionService.d.ts +16 -0
  286. package/dist/sync/v3/workspaceSyncOperationRetentionService.js +83 -0
  287. package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.d.ts +1 -0
  288. package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +9 -2
  289. package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +25 -3
  290. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +7 -0
  291. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +44 -32
  292. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +9 -5
  293. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +54 -38
  294. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +10 -0
  295. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +70 -31
  296. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +2 -1
  297. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +16 -0
  298. package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +6 -0
  299. package/dist/sync/v3/workspaceSyncV3Feed.d.ts +1 -0
  300. package/dist/sync/v3/workspaceSyncV3Feed.js +6 -1
  301. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +2 -0
  302. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +49 -18
  303. package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +3 -0
  304. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +5 -0
  305. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +36 -2
  306. package/dist/sync/v3/workspaceSyncV3RepairSnapshotRetentionService.d.ts +17 -0
  307. package/dist/sync/v3/workspaceSyncV3RepairSnapshotRetentionService.js +64 -0
  308. package/dist/sync/v3/workspaceSyncV3WorkflowRuntimeApply.d.ts +1 -0
  309. package/dist/sync/v3/workspaceSyncV3WorkflowRuntimeApply.js +50 -5
  310. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverCoverage.d.ts +7 -0
  311. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverCoverage.js +33 -0
  312. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverWireContract.d.ts +7 -0
  313. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverWireContract.js +8 -0
  314. package/dist/sync/workspaceRepair.d.ts +5 -2
  315. package/dist/sync/workspaceRepair.js +161 -6
  316. package/dist/sync/workspaceSyncModel.d.ts +21 -0
  317. package/dist/sync/workspaceSyncModel.js +35 -7
  318. package/dist/sync/workspaceSyncState.d.ts +1 -0
  319. package/dist/sync/workspaceSyncV2SnapshotBuilder.d.ts +6 -0
  320. package/dist/sync/workspaceSyncV2SnapshotBuilder.js +5 -0
  321. package/dist/ui/assets/AiIdentityRosterCard-CDMPtnbC.js +1 -0
  322. package/dist/ui/assets/AiProfilesModule-Cww85C4E.js +1 -0
  323. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-B68Wg-RV.js → AnnotatedAttachmentWorkspace-Dt9TKYSH.js} +1 -1
  324. package/dist/ui/assets/{AssetTraySortControl-C8Rztyal.js → AssetTraySortControl-CKgapnxu.js} +1 -1
  325. package/dist/ui/assets/{ContextAttachmentManager-Cj3c7X9y.js → ContextAttachmentManager-T-cEVcOZ.js} +2 -2
  326. package/dist/ui/assets/{DocumentWorkspace-B5uvzems.js → DocumentWorkspace-HD4MUjAZ.js} +1 -1
  327. package/dist/ui/assets/{EntityActivityTimeline-BVFvCB5R.js → EntityActivityTimeline-_V0zfMuh.js} +1 -1
  328. package/dist/ui/assets/PlanningModule-mTkBygsB.js +1 -0
  329. package/dist/ui/assets/{PlansPage-AY4cGWco.js → PlansPage-Btiq06VO.js} +1 -1
  330. package/dist/ui/assets/{TaskContextUpload-DZxTVesW.js → TaskContextUpload-C00NVUpn.js} +1 -1
  331. package/dist/ui/assets/TaskSettings-Cp-L-4xh.js +12 -0
  332. package/dist/ui/assets/TaskforceAgentsModule-Coq9-PUp.js +24 -0
  333. package/dist/ui/assets/TaskforceAgentsModule-b-8R25cJ.css +1 -0
  334. package/dist/ui/assets/{WorkflowManagerModule-DEr5di_r.js → WorkflowManagerModule-CuqRzMmM.js} +1 -1
  335. package/dist/ui/assets/index-BZ-hdZSk.css +1 -0
  336. package/dist/ui/assets/index-DSQINwt9.js +7 -0
  337. package/dist/ui/assets/{vendor-icons-D9Lpw-j4.js → vendor-icons-Bsq-mcEn.js} +1 -1
  338. package/dist/ui/branding/logos/letterhead_dark.png +0 -0
  339. package/dist/ui/branding/logos/letterhead_light.png +0 -0
  340. package/dist/ui/branding/logos/signature_dark.png +0 -0
  341. package/dist/ui/branding/logos/signature_light.png +0 -0
  342. package/dist/ui/index.html +3 -3
  343. package/dist/utils/aiProfileDefaultLogo.js +16 -48
  344. package/dist/utils/httpSetCookie.d.ts +1 -0
  345. package/dist/utils/httpSetCookie.js +4 -1
  346. package/dist/utils/localRuntimeUrl.d.ts +1 -0
  347. package/dist/utils/localRuntimeUrl.js +14 -0
  348. package/dist/utils/productLogoRegistry.d.ts +2 -0
  349. package/dist/utils/productLogoRegistry.js +36 -0
  350. package/dist/utils/syncStatusPresentation.d.ts +4 -0
  351. package/dist/utils/syncStatusPresentation.js +17 -0
  352. package/dist/utils/taskEvents.d.ts +2 -0
  353. package/dist/utils/workspaceSyncPresentation.d.ts +1 -1
  354. package/dist/utils/workspaceSyncPresentation.js +23 -17
  355. package/package.json +24 -4
  356. package/dist/ui/assets/AiIdentityRosterCard-BZM4aQnM.js +0 -1
  357. package/dist/ui/assets/AiProfilesModule-BbAEWtHH.js +0 -1
  358. package/dist/ui/assets/PlanningModule-B5DWQOGz.js +0 -1
  359. package/dist/ui/assets/TaskSettings-BeWAuyZj.js +0 -12
  360. package/dist/ui/assets/TaskforceAgentsModule-BifCYtjR.js +0 -21
  361. package/dist/ui/assets/TaskforceAgentsModule-D6IC0S7Q.css +0 -1
  362. package/dist/ui/assets/index-I5zhdtgt.js +0 -7
  363. package/dist/ui/assets/index-VRFsx3wz.css +0 -1
@@ -0,0 +1,65 @@
1
+ export type ModelConnectionLifecycleStatus = 'pending' | 'active' | 'reconnect_required' | 'revoked' | 'disconnected';
2
+ export type ModelConnection = {
3
+ id: string;
4
+ providerKey: string;
5
+ authenticationType: string;
6
+ lifecycleStatus: ModelConnectionLifecycleStatus;
7
+ displayMetadata: {
8
+ displayName?: string;
9
+ accountEmail?: string;
10
+ accountLabel?: string;
11
+ planLabel?: string;
12
+ };
13
+ lastVerifiedAt: string | null;
14
+ lastRefreshedAt: string | null;
15
+ failureCode: string | null;
16
+ createdAt: string;
17
+ updatedAt: string;
18
+ };
19
+ export type DeviceCodeLoginSession = {
20
+ id: string;
21
+ connectionId: string;
22
+ status: 'pending' | 'active' | 'failed' | 'cancelled' | 'expired';
23
+ userCode: string;
24
+ verificationUrl: string;
25
+ expiresAt: string;
26
+ failureCode?: string;
27
+ };
28
+ export type AgentConnectionBinding = {
29
+ bound: boolean;
30
+ available: boolean;
31
+ providerKey?: string;
32
+ modelSource?: string;
33
+ ownerIsCaller?: boolean;
34
+ connectionId?: string;
35
+ reasonCode?: string;
36
+ };
37
+ export type ModelConnectionsSnapshot = {
38
+ eligible: boolean;
39
+ connections: ModelConnection[];
40
+ };
41
+ export declare class ModelConnectionsApiError extends Error {
42
+ readonly code?: string | undefined;
43
+ readonly status?: number | undefined;
44
+ constructor(message: string, code?: string | undefined, status?: number | undefined);
45
+ }
46
+ export type ModelConnectionsApiContext = {
47
+ apiBaseUrl?: string;
48
+ workspaceId?: string;
49
+ resolveApiUrl?: (path: string) => string;
50
+ };
51
+ export declare function listModelConnections(context: ModelConnectionsApiContext): Promise<ModelConnection[]>;
52
+ export declare function loadModelConnectionsSnapshot(context: ModelConnectionsApiContext): Promise<ModelConnectionsSnapshot>;
53
+ export declare function createOpenAiCodexConnection(context: ModelConnectionsApiContext): Promise<ModelConnection>;
54
+ export declare function startDeviceCodeLogin(connectionId: string, context: ModelConnectionsApiContext): Promise<DeviceCodeLoginSession>;
55
+ export declare function getDeviceCodeLogin(connectionId: string, sessionId: string, context: ModelConnectionsApiContext): Promise<DeviceCodeLoginSession>;
56
+ export declare function cancelDeviceCodeLogin(connectionId: string, sessionId: string, context: ModelConnectionsApiContext): Promise<DeviceCodeLoginSession>;
57
+ export declare const verifyModelConnection: (connectionId: string, context: ModelConnectionsApiContext) => Promise<ModelConnection>;
58
+ export declare const refreshModelConnection: (connectionId: string, context: ModelConnectionsApiContext) => Promise<ModelConnection>;
59
+ export declare const disconnectModelConnection: (connectionId: string, context: ModelConnectionsApiContext) => Promise<ModelConnection>;
60
+ export declare function renameModelConnection(connectionId: string, displayName: string, context: ModelConnectionsApiContext): Promise<ModelConnection>;
61
+ export declare function removeModelConnection(connectionId: string, context: ModelConnectionsApiContext): Promise<void>;
62
+ export declare function inspectAgentConnectionBinding(agentId: string, context: ModelConnectionsApiContext): Promise<AgentConnectionBinding>;
63
+ export declare function bindAgentConnection(agentId: string, connectionId: string, context: ModelConnectionsApiContext): Promise<AgentConnectionBinding>;
64
+ export declare function clearAgentConnectionBinding(agentId: string, context: ModelConnectionsApiContext): Promise<void>;
65
+ export declare function modelConnectionFailureMessage(code: string | null | undefined, fallback?: string): string;
@@ -0,0 +1,129 @@
1
+ import { fetchTaskforceApi } from '../../../utils/taskforceApiClient.js';
2
+ export class ModelConnectionsApiError extends Error {
3
+ code;
4
+ status;
5
+ constructor(message, code, status) {
6
+ super(message);
7
+ this.code = code;
8
+ this.status = status;
9
+ this.name = 'ModelConnectionsApiError';
10
+ }
11
+ }
12
+ async function requestJson(path, init, context) {
13
+ const headers = new Headers(init?.headers || undefined);
14
+ if (init?.body)
15
+ headers.set('Content-Type', 'application/json');
16
+ const workspaceId = String(context.workspaceId || '').trim();
17
+ if (workspaceId && workspaceId !== 'default')
18
+ headers.set('x-taskforce-workspace-id', workspaceId);
19
+ const requestPath = context.resolveApiUrl?.(path) || path;
20
+ const response = await fetchTaskforceApi(requestPath, {
21
+ credentials: 'include',
22
+ ...init,
23
+ headers,
24
+ }, context.apiBaseUrl);
25
+ const payload = await response.json().catch(() => ({}));
26
+ if (!response.ok || payload.success === false) {
27
+ throw new ModelConnectionsApiError(String(payload.error || `Model connection request failed (${response.status}).`), payload.code, response.status);
28
+ }
29
+ return payload;
30
+ }
31
+ export async function listModelConnections(context) {
32
+ const payload = await requestJson('/api/taskforce/model-connections', undefined, context);
33
+ return Array.isArray(payload.connections) ? payload.connections : [];
34
+ }
35
+ export async function loadModelConnectionsSnapshot(context) {
36
+ const payload = await requestJson('/api/taskforce/model-connections', undefined, context);
37
+ return {
38
+ eligible: payload.eligible === true,
39
+ connections: Array.isArray(payload.connections) ? payload.connections : [],
40
+ };
41
+ }
42
+ export async function createOpenAiCodexConnection(context) {
43
+ const payload = await requestJson('/api/taskforce/model-connections', {
44
+ method: 'POST',
45
+ body: JSON.stringify({
46
+ providerKey: 'openai',
47
+ authenticationType: 'chatgpt_device_code',
48
+ }),
49
+ }, context);
50
+ if (!payload.connection)
51
+ throw new ModelConnectionsApiError('Model connection response was incomplete.');
52
+ return payload.connection;
53
+ }
54
+ export async function startDeviceCodeLogin(connectionId, context) {
55
+ const payload = await requestJson(`/api/taskforce/model-connections/${encodeURIComponent(connectionId)}/login`, { method: 'POST' }, context);
56
+ if (!payload.login)
57
+ throw new ModelConnectionsApiError('Device authorization response was incomplete.');
58
+ return payload.login;
59
+ }
60
+ export async function getDeviceCodeLogin(connectionId, sessionId, context) {
61
+ const payload = await requestJson(`/api/taskforce/model-connections/${encodeURIComponent(connectionId)}/login/${encodeURIComponent(sessionId)}`, undefined, context);
62
+ if (!payload.login)
63
+ throw new ModelConnectionsApiError('Device authorization response was incomplete.');
64
+ return payload.login;
65
+ }
66
+ export async function cancelDeviceCodeLogin(connectionId, sessionId, context) {
67
+ const payload = await requestJson(`/api/taskforce/model-connections/${encodeURIComponent(connectionId)}/login/${encodeURIComponent(sessionId)}/cancel`, { method: 'POST' }, context);
68
+ if (!payload.login)
69
+ throw new ModelConnectionsApiError('Device authorization response was incomplete.');
70
+ return payload.login;
71
+ }
72
+ async function runConnectionOperation(connectionId, operation, context) {
73
+ const payload = await requestJson(`/api/taskforce/model-connections/${encodeURIComponent(connectionId)}/${operation}`, { method: 'POST' }, context);
74
+ if (!payload.connection)
75
+ throw new ModelConnectionsApiError('Model connection response was incomplete.');
76
+ return payload.connection;
77
+ }
78
+ export const verifyModelConnection = (connectionId, context) => (runConnectionOperation(connectionId, 'verify', context));
79
+ export const refreshModelConnection = (connectionId, context) => (runConnectionOperation(connectionId, 'refresh', context));
80
+ export const disconnectModelConnection = (connectionId, context) => (runConnectionOperation(connectionId, 'disconnect', context));
81
+ export async function renameModelConnection(connectionId, displayName, context) {
82
+ const payload = await requestJson(`/api/taskforce/model-connections/${encodeURIComponent(connectionId)}`, {
83
+ method: 'PATCH',
84
+ body: JSON.stringify({ displayName }),
85
+ }, context);
86
+ if (!payload.connection)
87
+ throw new ModelConnectionsApiError('Model connection response was incomplete.');
88
+ return payload.connection;
89
+ }
90
+ export async function removeModelConnection(connectionId, context) {
91
+ await requestJson(`/api/taskforce/model-connections/${encodeURIComponent(connectionId)}`, { method: 'DELETE' }, context);
92
+ }
93
+ export async function inspectAgentConnectionBinding(agentId, context) {
94
+ const workspaceId = encodeURIComponent(String(context.workspaceId || '').trim());
95
+ const payload = await requestJson(`/api/taskforce/agents/${encodeURIComponent(agentId)}/connection-binding?workspaceId=${workspaceId}`, undefined, context);
96
+ return payload.binding || { bound: false, available: false, reasonCode: 'MODEL_CONNECTION_REQUIRED' };
97
+ }
98
+ export async function bindAgentConnection(agentId, connectionId, context) {
99
+ const payload = await requestJson(`/api/taskforce/agents/${encodeURIComponent(agentId)}/connection-binding`, {
100
+ method: 'PUT',
101
+ body: JSON.stringify({ workspaceId: context.workspaceId, connectionId }),
102
+ }, context);
103
+ if (!payload.binding)
104
+ throw new ModelConnectionsApiError('Agent connection response was incomplete.');
105
+ return payload.binding;
106
+ }
107
+ export async function clearAgentConnectionBinding(agentId, context) {
108
+ await requestJson(`/api/taskforce/agents/${encodeURIComponent(agentId)}/connection-binding`, {
109
+ method: 'DELETE',
110
+ body: JSON.stringify({ workspaceId: context.workspaceId }),
111
+ }, context);
112
+ }
113
+ export function modelConnectionFailureMessage(code, fallback = 'The connection needs to be authorized again.') {
114
+ switch (String(code || '').trim().toUpperCase()) {
115
+ case 'MODEL_CONNECTION_DEVICE_CODE_UNAVAILABLE':
116
+ return 'ChatGPT device authorization is unavailable for this account. Retry later or use another connection.';
117
+ case 'MODEL_CONNECTION_RATE_LIMITED':
118
+ return 'ChatGPT temporarily rate limited this connection. Wait a moment, then retry.';
119
+ case 'MODEL_CONNECTION_LOGIN_LIMIT_REACHED':
120
+ case 'MODEL_CONNECTION_LOGIN_CAPACITY_REACHED':
121
+ return 'Device authorization capacity is temporarily full. Wait a moment, then retry.';
122
+ case 'MODEL_CONNECTION_LOGIN_CANCELLED':
123
+ return 'Device authorization was cancelled.';
124
+ case 'MODEL_CONNECTION_LEASE_LOST':
125
+ return 'The connection changed while authorization was running. Start again.';
126
+ default:
127
+ return fallback;
128
+ }
129
+ }
@@ -11,6 +11,7 @@ import { PlanningOwnerDropdown, normalizePlanningOwnerOptions, } from './Plannin
11
11
  import { buildPlanningNavigatorResults, PLANNING_NAVIGATOR_SORT_OPTIONS, } from './planningNavigator';
12
12
  import { PlanningEntitySummary, PlanningReferenceBadge, } from './PlanningEntitySummary';
13
13
  import { PlanningVisualIdentity } from './PlanningVisualIdentity';
14
+ import { hasPlanningMetadataChanges } from './planningMetadata';
14
15
  import { PlanningTaskCard } from './PlanningTaskCard';
15
16
  import { PlanningTaskArrangeControl } from './PlanningTaskArrangeControl';
16
17
  import planningDropdownStyles from './PlanningDropdownControl.module.css';
@@ -21,7 +22,7 @@ import { PlanningUnlinkConfirmation, } from './PlanningUnlinkConfirmation';
21
22
  import { PlanningLinkPicker } from './PlanningLinkPicker';
22
23
  import { PlanningEntityActivity, PlanningEntityContext, PlanningEntityDescription, } from './PlanningEntityDetails';
23
24
  import styles from './PlanningModule.module.css';
24
- function PlanningWorkspaceEditor({ entityType, mode, initialTitle = '', initialDescription = '', ownerOptions, fixedInitiativeId, formId, showActions = true, onSavingChange, onCancel, onSave, }) {
25
+ function PlanningWorkspaceEditor({ entityType, mode, initialTitle = '', initialDescription = '', ownerOptions, fixedInitiativeId, formId, showActions = true, onSavingChange, onDirtyChange, onCancel, onSave, }) {
25
26
  const label = entityType === 'initiative' ? 'Initiative' : 'Workstream';
26
27
  const titleInputId = React.useId();
27
28
  const titleErrorId = React.useId();
@@ -35,6 +36,10 @@ function PlanningWorkspaceEditor({ entityType, mode, initialTitle = '', initialD
35
36
  const [titleError, setTitleError] = React.useState(null);
36
37
  const [saveError, setSaveError] = React.useState(null);
37
38
  const [isSaving, setIsSaving] = React.useState(false);
39
+ const isDirty = mode === 'create' || hasPlanningMetadataChanges({ title: initialTitle, description: initialDescription }, { title, description });
40
+ React.useEffect(() => {
41
+ onDirtyChange?.(isDirty);
42
+ }, [isDirty, onDirtyChange]);
38
43
  React.useEffect(() => {
39
44
  titleInputRef.current?.focus();
40
45
  }, []);
@@ -48,6 +53,8 @@ function PlanningWorkspaceEditor({ entityType, mode, initialTitle = '', initialD
48
53
  titleInputRef.current?.focus();
49
54
  return;
50
55
  }
56
+ if (!isDirty)
57
+ return;
51
58
  setTitleError(null);
52
59
  setSaveError(null);
53
60
  setIsSaving(true);
@@ -87,7 +94,7 @@ function PlanningWorkspaceEditor({ entityType, mode, initialTitle = '', initialD
87
94
  setTitleError(null);
88
95
  }, placeholder: entityType === 'initiative' ? 'Enter initiative title' : 'Enter workstream title' }), titleError ? _jsx("p", { id: titleErrorId, className: "tf-text-error", children: titleError }) : null] }), _jsxs("div", { className: styles.workspaceEditorField, children: [_jsxs("label", { htmlFor: descriptionInputId, children: [label, " description"] }), _jsx("textarea", { id: descriptionInputId, value: description, disabled: isSaving, rows: 5, onChange: (event) => setDescription(event.target.value), placeholder: entityType === 'initiative'
89
96
  ? 'Describe the broader outcome this initiative is meant to achieve.'
90
- : 'Describe the lane of work this workstream will coordinate.' })] }), mode === 'create' && ownerOptions ? (_jsxs("div", { className: styles.workspaceEditorField, children: [_jsxs("span", { children: [label, " owner"] }), _jsx(PlanningOwnerDropdown, { value: ownerId, options: ownerOptions, onChange: setOwnerId, disabled: isSaving, ariaLabel: `${label} owner`, className: `${planningDropdownStyles.control} ${styles.selectedEntityOwnerDropdown}` })] })) : null, saveError ? _jsx("p", { className: "tf-text-error", role: "alert", children: saveError }) : null, showActions ? _jsxs("div", { className: styles.workspaceEditorActions, children: [_jsx("button", { type: "button", className: "tf-button-secondary tf-button-compact", onClick: onCancel, disabled: isSaving, children: "Cancel" }), _jsxs("button", { type: "submit", className: "tf-button-primary tf-button-compact", disabled: isSaving, children: [isSaving ? _jsx(Loader2, { size: 14, className: styles.spinner, "aria-hidden": "true" }) : null, mode === 'create' ? `Create ${label}` : 'Save'] })] }) : null] }));
97
+ : 'Describe the lane of work this workstream will coordinate.' })] }), mode === 'create' && ownerOptions ? (_jsxs("div", { className: styles.workspaceEditorField, children: [_jsxs("span", { children: [label, " owner"] }), _jsx(PlanningOwnerDropdown, { value: ownerId, options: ownerOptions, onChange: setOwnerId, disabled: isSaving, ariaLabel: `${label} owner`, className: `${planningDropdownStyles.control} ${styles.selectedEntityOwnerDropdown}` })] })) : null, saveError ? _jsx("p", { className: "tf-text-error", role: "alert", children: saveError }) : null, showActions ? _jsxs("div", { className: styles.workspaceEditorActions, children: [_jsx("button", { type: "button", className: "tf-button-secondary tf-button-compact", onClick: onCancel, disabled: isSaving, children: "Cancel" }), _jsxs("button", { type: "submit", className: "tf-button-primary tf-button-compact", disabled: isSaving || !isDirty, children: [isSaving ? _jsx(Loader2, { size: 14, className: styles.spinner, "aria-hidden": "true" }) : null, mode === 'create' ? `Create ${label}` : 'Save'] })] }) : null] }));
91
98
  }
92
99
  function InitiativeButton({ initiative, ownerOption, ownerOptions, workstreamCount, selected, onSelect, onChangeOwner, onChangeIdentity, }) {
93
100
  return (_jsxs("div", { className: `${styles.entityCard} ${initiative.isArchived ? styles.entityCardArchived : ''} ${selected ? styles.entityCardSelected : ''}`.trim(), children: [_jsx(PlanningReferenceBadge, { label: formatInitiativeReference(initiative), entityType: "initiative", entityName: initiative.title, interactive: false }), _jsx("div", { className: styles.entityCardBody, children: _jsx("div", { role: "button", tabIndex: 0, className: styles.entityCardContentButton, onClick: onSelect, onKeyDown: (event) => {
@@ -115,6 +122,7 @@ function SelectedEntityContext({ item, entityType, ownerOptions, onChangeOwner,
115
122
  const [isSavingOwner, setIsSavingOwner] = React.useState(false);
116
123
  const [isEditing, setIsEditing] = React.useState(false);
117
124
  const [isEditingSaving, setIsEditingSaving] = React.useState(false);
125
+ const [isEditingDirty, setIsEditingDirty] = React.useState(false);
118
126
  const [archiveConfirmationVisible, setArchiveConfirmationVisible] = React.useState(false);
119
127
  const [archiveBusy, setArchiveBusy] = React.useState(false);
120
128
  const editFormId = React.useId();
@@ -140,6 +148,7 @@ function SelectedEntityContext({ item, entityType, ownerOptions, onChangeOwner,
140
148
  React.useEffect(() => {
141
149
  setIsEditing(false);
142
150
  setIsEditingSaving(false);
151
+ setIsEditingDirty(false);
143
152
  setArchiveConfirmationVisible(false);
144
153
  setArchiveBusy(false);
145
154
  }, [item.id]);
@@ -199,7 +208,10 @@ function SelectedEntityContext({ item, entityType, ownerOptions, onChangeOwner,
199
208
  ? archiveConfirmationVisible
200
209
  : undefined, children: archiveState.mode === 'archived'
201
210
  ? _jsx(RotateCcw, { size: 14, "aria-hidden": "true" })
202
- : _jsx(Archive, { size: 14, "aria-hidden": "true" }) })) : null, !isEditing && !item.isArchived && onUpdate ? (_jsx("button", { type: "button", className: "tf-control-icon", onClick: () => setIsEditing(true), "aria-label": `Edit ${label.toLowerCase()}`, title: `Edit ${label.toLowerCase()}`, children: _jsx(Pencil, { size: 14, "aria-hidden": "true" }) })) : null, isEditing ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "tf-button-secondary tf-button-compact", onClick: () => setIsEditing(false), disabled: isEditingSaving, children: "Cancel" }), _jsx("button", { type: "submit", form: editFormId, className: "tf-button-primary tf-button-compact", disabled: isEditingSaving, children: "Save" })] })) : null] })] }), archiveConfirmationVisible && archiveState.mode === 'ready' ? (_jsx(PlanningArchiveConfirmation, { entityType: entityType, title: item.title, activeTaskCount: item.archiveActiveTaskCount, activeWorkstreamCount: item.archiveActiveWorkstreamCount, busy: archiveBusy, onCancel: () => setArchiveConfirmationVisible(false), onConfirm: () => void handleConfirmArchive() })) : null, relationshipContent, isEditing && onUpdate ? (_jsx(PlanningWorkspaceEditor, { entityType: entityType, mode: "edit", formId: editFormId, showActions: false, onSavingChange: setIsEditingSaving, initialTitle: item.title, initialDescription: item.description || '', onCancel: () => setIsEditing(false), onSave: async (input) => {
211
+ : _jsx(Archive, { size: 14, "aria-hidden": "true" }) })) : null, !isEditing && !item.isArchived && onUpdate ? (_jsx("button", { type: "button", className: "tf-control-icon", onClick: () => {
212
+ setIsEditingDirty(false);
213
+ setIsEditing(true);
214
+ }, "aria-label": `Edit ${label.toLowerCase()}`, title: `Edit ${label.toLowerCase()}`, children: _jsx(Pencil, { size: 14, "aria-hidden": "true" }) })) : null, isEditing ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "tf-button-secondary tf-button-compact", onClick: () => setIsEditing(false), disabled: isEditingSaving, children: "Cancel" }), _jsx("button", { type: "submit", form: editFormId, className: "tf-button-primary tf-button-compact", disabled: isEditingSaving || !isEditingDirty, children: "Save" })] })) : null] })] }), archiveConfirmationVisible && archiveState.mode === 'ready' ? (_jsx(PlanningArchiveConfirmation, { entityType: entityType, title: item.title, activeTaskCount: item.archiveActiveTaskCount, activeWorkstreamCount: item.archiveActiveWorkstreamCount, busy: archiveBusy, onCancel: () => setArchiveConfirmationVisible(false), onConfirm: () => void handleConfirmArchive() })) : null, relationshipContent, isEditing && onUpdate ? (_jsx(PlanningWorkspaceEditor, { entityType: entityType, mode: "edit", formId: editFormId, showActions: false, onSavingChange: setIsEditingSaving, onDirtyChange: setIsEditingDirty, initialTitle: item.title, initialDescription: item.description || '', onCancel: () => setIsEditing(false), onSave: async (input) => {
203
215
  await onUpdate(input);
204
216
  setIsEditing(false);
205
217
  } }, `${entityType}:${item.id}`)) : (_jsxs(_Fragment, { children: [_jsx("h3", { className: "tf-heading-card", children: item.title }), _jsx(PlanningEntityDescription, { description: item.description, label: label, taskReferences: taskReferences, className: `${styles.selectedEntityDescription} tf-scrollbar`.trim(), markdownClassName: styles.selectedEntityDescriptionMarkdown })] })), _jsxs("div", { className: styles.selectedEntityOwnerField, children: [_jsx("span", { children: "Owner" }), _jsx(PlanningOwnerDropdown, { value: ownerValue, options: ownerOptions, onChange: (nextOwnerId) => void handleOwnerChange(nextOwnerId), disabled: !onChangeOwner || Boolean(item.isArchived) || isSavingOwner, ariaLabel: `${label} owner`, className: `${planningDropdownStyles.control} ${styles.selectedEntityOwnerDropdown}` })] }), _jsx("div", { className: styles.selectedEntityDetailsSection, children: _jsx(PlanningEntityContext, { entityType: entityType, entityId: item.id, referenceLabel: entityType === 'initiative'
@@ -0,0 +1,10 @@
1
+ export type PlanningMetadataDraft = {
2
+ title: string;
3
+ description?: string | null;
4
+ };
5
+ export declare function hasPlanningMetadataChanges(current: PlanningMetadataDraft, draft: PlanningMetadataDraft): boolean;
6
+ export declare function hasWorkstreamMetadataChanges(current: PlanningMetadataDraft & {
7
+ initiativeId?: string | null;
8
+ }, draft: PlanningMetadataDraft & {
9
+ initiativeId?: string | null;
10
+ }): boolean;
@@ -0,0 +1,8 @@
1
+ export function hasPlanningMetadataChanges(current, draft) {
2
+ return draft.title.trim() !== current.title.trim()
3
+ || (draft.description?.trim() || null) !== (current.description?.trim() || null);
4
+ }
5
+ export function hasWorkstreamMetadataChanges(current, draft) {
6
+ return hasPlanningMetadataChanges(current, draft)
7
+ || (draft.initiativeId || null) !== (current.initiativeId || null);
8
+ }
@@ -6,8 +6,8 @@ import { TASKFORCE_AGENT_SKILLS_MUTATED_EVENT, } from '../../../utils/taskforceA
6
6
  import taskforceStyles from '../../../Taskforce.module.css';
7
7
  import { loadAgentBehaviorCatalogs, } from './agentBehaviorCatalogApi.js';
8
8
  import styles from './AgentBehaviorAssignments.module.css';
9
- function revisionValue(reference) {
10
- return reference.resolution === 'pinned' ? String(reference.revision ?? '') : '';
9
+ function versionValue(reference) {
10
+ return reference.resolution === 'pinned' ? `revision:${String(reference.revision ?? '')}` : 'current';
11
11
  }
12
12
  function pinReference(reference, revision) {
13
13
  return {
@@ -45,6 +45,11 @@ function selectedRevision(value) {
45
45
  const numeric = Number(value);
46
46
  return Number.isInteger(numeric) && String(numeric) === value ? numeric : value;
47
47
  }
48
+ function referenceForVersion(reference, value) {
49
+ if (value === 'current')
50
+ return currentReference(reference);
51
+ return pinReference(reference, selectedRevision(value.slice('revision:'.length)));
52
+ }
48
53
  export function AgentBehaviorAssignments({ workspaceId, roleRef, skillRefs, onRoleRefChange, onSkillRefsChange, onManageRoles, onManageSkills, }) {
49
54
  const [roles, setRoles] = useState([]);
50
55
  const [skills, setSkills] = useState([]);
@@ -113,26 +118,18 @@ export function AgentBehaviorAssignments({ workspaceId, roleRef, skillRefs, onRo
113
118
  ...(roleRef.resolution === 'pinned'
114
119
  && selectedRole
115
120
  && !selectedRole.revisions.some((revision) => String(revision.revision) === String(roleRef.revision))
116
- ? [`Pinned Role revision ${String(roleRef.revision)} is unavailable.`]
121
+ ? [`Role version ${String(roleRef.revision)} is unavailable.`]
117
122
  : []),
118
123
  ]
119
124
  : [];
120
125
  return (_jsxs("div", { className: styles.assignments, children: [_jsxs("section", { className: styles.section, "aria-labelledby": "agent-role-heading", children: [_jsxs("div", { className: styles.sectionHeader, children: [_jsxs("div", { className: styles.sectionHeading, children: [_jsx(BriefcaseBusiness, { size: 17 }), _jsxs("div", { children: [_jsx("strong", { id: "agent-role-heading", children: "Role" }), _jsx("small", { children: "Optional reusable professional profile. An Agent can have one Role." })] })] }), _jsx("button", { type: "button", className: taskforceStyles.secondaryHeaderBtn, onClick: onManageRoles, children: "Manage Roles" })] }), _jsxs("div", { className: styles.roleControls, children: [_jsxs("label", { className: taskforceStyles.field, children: [_jsx("span", { className: taskforceStyles.label, children: "Role" }), _jsxs("select", { className: taskforceStyles.select, "aria-label": "Role", value: roleRef?.id || '', disabled: loading, onChange: (event) => {
121
126
  const id = event.target.value;
122
127
  onRoleRefChange(id ? { id, resolution: 'current' } : null);
123
- }, children: [_jsx("option", { value: "", children: "No Role" }), roleRef && !selectedRole ? (_jsxs("option", { value: roleRef.id, children: ["Missing Role \u00B7 ", roleRef.id] })) : null, roles.map((role) => (_jsxs("option", { value: role.id, disabled: role.lifecycleStatus === 'retired', children: [role.definition.name, role.lifecycleStatus === 'retired' ? ' · Retired' : ''] }, role.id)))] })] }), _jsxs("label", { className: taskforceStyles.field, children: [_jsx("span", { className: taskforceStyles.label, children: "Version" }), _jsxs("select", { className: taskforceStyles.select, "aria-label": "Role version", value: roleRef?.resolution || 'current', disabled: !roleRef || !selectedRole, onChange: (event) => {
124
- if (!roleRef || !selectedRole)
125
- return;
126
- if (event.target.value === 'current') {
127
- onRoleRefChange(currentReference(roleRef));
128
- return;
129
- }
130
- onRoleRefChange(pinReference(roleRef, selectedRole.currentRevision));
131
- }, children: [_jsx("option", { value: "current", children: "Use latest" }), _jsx("option", { value: "pinned", children: "Pin revision" })] })] }), _jsxs("label", { className: taskforceStyles.field, children: [_jsx("span", { className: taskforceStyles.label, children: "Revision" }), _jsxs("select", { className: taskforceStyles.select, "aria-label": "Role pinned revision", value: roleRef ? revisionValue(roleRef) : '', disabled: !roleRef || roleRef.resolution !== 'pinned', onChange: (event) => {
128
+ }, children: [_jsx("option", { value: "", children: "No Role" }), roleRef && !selectedRole ? (_jsxs("option", { value: roleRef.id, children: ["Missing Role \u00B7 ", roleRef.id] })) : null, roles.map((role) => (_jsxs("option", { value: role.id, disabled: role.lifecycleStatus === 'retired', children: [role.definition.name, role.lifecycleStatus === 'retired' ? ' · Retired' : ''] }, role.id)))] })] }), _jsxs("label", { className: taskforceStyles.field, children: [_jsx("span", { className: taskforceStyles.label, children: "Version" }), _jsxs("select", { className: taskforceStyles.select, "aria-label": "Role version", value: roleRef ? versionValue(roleRef) : 'current', disabled: !roleRef || !selectedRole, onChange: (event) => {
132
129
  if (!roleRef)
133
130
  return;
134
- onRoleRefChange(pinReference(roleRef, selectedRevision(event.target.value)));
135
- }, children: [_jsx("option", { value: "", children: "Current" }), roleRef ? availableRevisions(selectedRole, roleRef).map((revision) => (_jsxs("option", { value: revision, children: ["Revision ", revision] }, revision))) : null] })] })] }), selectedRole?.definition.shortDescription ? (_jsx("small", { className: styles.empty, children: selectedRole.definition.shortDescription })) : null, roleIssues.map((message) => (_jsxs("div", { className: styles.issue, role: "alert", children: [_jsx(AlertTriangle, { size: 13 }), _jsxs("span", { children: [message, " Agent execution is blocked until this reference is repaired."] })] }, message)))] }), _jsxs("section", { className: styles.section, "aria-labelledby": "agent-skills-heading", children: [_jsxs("div", { className: styles.sectionHeader, children: [_jsxs("div", { className: styles.sectionHeading, children: [_jsx(Sparkles, { size: 17 }), _jsxs("div", { children: [_jsx("strong", { id: "agent-skills-heading", children: "Skills" }), _jsx("small", { children: "Composable behavior applied in this exact order. Later Skills take precedence when guidance conflicts." })] })] }), _jsx("button", { type: "button", className: taskforceStyles.secondaryHeaderBtn, onClick: onManageSkills, children: "Manage Skills" })] }), _jsxs("div", { className: styles.skillAdd, children: [_jsxs("label", { className: taskforceStyles.field, children: [_jsx("span", { className: taskforceStyles.label, children: "Add Skill" }), _jsxs("select", { className: taskforceStyles.select, "aria-label": "Skill to add", value: skillToAdd, disabled: loading || activeSkillsToAdd.length === 0, onChange: (event) => setSkillToAdd(event.target.value), children: [_jsx("option", { value: "", children: "Select a Skill" }), activeSkillsToAdd.map((skill) => (_jsx("option", { value: skill.id, children: skill.definition.name }, skill.id)))] })] }), _jsx("button", { type: "button", className: "tf-control-icon", disabled: !skillToAdd, "aria-label": "Add selected Skill", title: "Add selected Skill", onClick: () => {
131
+ onRoleRefChange(referenceForVersion(roleRef, event.target.value));
132
+ }, children: [_jsx("option", { value: "current", children: selectedRole ? `Use latest (Version ${selectedRole.currentRevision})` : 'Use latest' }), roleRef ? availableRevisions(selectedRole, roleRef).map((revision) => (_jsxs("option", { value: `revision:${String(revision)}`, children: ["Version ", revision] }, revision))) : null] })] })] }), selectedRole?.definition.shortDescription ? (_jsx("small", { className: styles.empty, children: selectedRole.definition.shortDescription })) : null, roleIssues.map((message) => (_jsxs("div", { className: styles.issue, role: "alert", children: [_jsx(AlertTriangle, { size: 13 }), _jsxs("span", { children: [message, " Agent execution is blocked until this reference is repaired."] })] }, message)))] }), _jsxs("section", { className: styles.section, "aria-labelledby": "agent-skills-heading", children: [_jsxs("div", { className: styles.sectionHeader, children: [_jsxs("div", { className: styles.sectionHeading, children: [_jsx(Sparkles, { size: 17 }), _jsxs("div", { children: [_jsx("strong", { id: "agent-skills-heading", children: "Skills" }), _jsx("small", { children: "Composable behavior applied in this exact order. Later Skills take precedence when guidance conflicts." })] })] }), _jsx("button", { type: "button", className: taskforceStyles.secondaryHeaderBtn, onClick: onManageSkills, children: "Manage Skills" })] }), _jsxs("div", { className: styles.skillAdd, children: [_jsxs("label", { className: taskforceStyles.field, children: [_jsx("span", { className: taskforceStyles.label, children: "Add Skill" }), _jsxs("select", { className: taskforceStyles.select, "aria-label": "Skill to add", value: skillToAdd, disabled: loading || activeSkillsToAdd.length === 0, onChange: (event) => setSkillToAdd(event.target.value), children: [_jsx("option", { value: "", children: "Select a Skill" }), activeSkillsToAdd.map((skill) => (_jsx("option", { value: skill.id, children: skill.definition.name }, skill.id)))] })] }), _jsx("button", { type: "button", className: "tf-control-icon", disabled: !skillToAdd, "aria-label": "Add selected Skill", title: "Add selected Skill", onClick: () => {
136
133
  if (!skillToAdd || selectedSkillIds.has(skillToAdd))
137
134
  return;
138
135
  onSkillRefsChange([...skillRefs, { id: skillToAdd, resolution: 'current' }]);
@@ -145,20 +142,14 @@ export function AgentBehaviorAssignments({ workspaceId, roleRef, skillRefs, onRo
145
142
  ...(reference.resolution === 'pinned'
146
143
  && skill
147
144
  && !skill.revisions.some((revision) => String(revision.revision) === String(reference.revision))
148
- ? [`Pinned Skill revision ${String(reference.revision)} is unavailable.`]
145
+ ? [`Skill version ${String(reference.revision)} is unavailable.`]
149
146
  : []),
150
147
  ];
151
- return (_jsxs("li", { children: [_jsxs("div", { className: styles.referenceRow, children: [_jsxs("div", { className: styles.referenceCopy, children: [_jsxs("strong", { children: [index + 1, ". ", skill?.definition.name || `Missing Skill · ${reference.id}`] }), _jsx("small", { children: skill?.definition.shortDescription || reference.id })] }), _jsxs("select", { className: taskforceStyles.select, "aria-label": `${skill?.definition.name || reference.id} version`, value: reference.resolution, disabled: !skill, onChange: (event) => {
152
- const next = [...skillRefs];
153
- next[index] = event.target.value === 'current'
154
- ? currentReference(reference)
155
- : pinReference(reference, skill?.revision || 1);
156
- onSkillRefsChange(next);
157
- }, children: [_jsx("option", { value: "current", children: "Use latest" }), _jsx("option", { value: "pinned", children: "Pin revision" })] }), _jsxs("select", { className: taskforceStyles.select, "aria-label": `${skill?.definition.name || reference.id} pinned revision`, value: revisionValue(reference), disabled: reference.resolution !== 'pinned', onChange: (event) => {
148
+ return (_jsxs("li", { children: [_jsxs("div", { className: styles.referenceRow, children: [_jsxs("div", { className: styles.referenceCopy, children: [_jsxs("strong", { children: [index + 1, ". ", skill?.definition.name || `Missing Skill · ${reference.id}`] }), _jsx("small", { children: skill?.definition.shortDescription || reference.id })] }), _jsxs("select", { className: taskforceStyles.select, "aria-label": `${skill?.definition.name || reference.id} version`, value: versionValue(reference), disabled: !skill, onChange: (event) => {
158
149
  const next = [...skillRefs];
159
- next[index] = pinReference(reference, selectedRevision(event.target.value));
150
+ next[index] = referenceForVersion(reference, event.target.value);
160
151
  onSkillRefsChange(next);
161
- }, children: [_jsx("option", { value: "", children: "Current" }), availableRevisions(skill, reference).map((revision) => (_jsxs("option", { value: revision, children: ["Revision ", revision] }, revision)))] }), _jsxs("div", { className: styles.rowActions, children: [_jsx("button", { type: "button", className: "tf-control-icon tf-control-icon-compact", "aria-label": `Move ${skill?.definition.name || reference.id} up`, disabled: index === 0, onClick: () => {
152
+ }, children: [_jsx("option", { value: "current", children: skill ? `Use latest (Version ${skill.revision})` : 'Use latest' }), availableRevisions(skill, reference).map((revision) => (_jsxs("option", { value: `revision:${String(revision)}`, children: ["Version ", revision] }, revision)))] }), _jsxs("div", { className: styles.rowActions, children: [_jsx("button", { type: "button", className: "tf-control-icon tf-control-icon-compact", "aria-label": `Move ${skill?.definition.name || reference.id} up`, disabled: index === 0, onClick: () => {
162
153
  if (index === 0)
163
154
  return;
164
155
  const next = [...skillRefs];
@@ -1,5 +1,5 @@
1
1
  import { type AgentDefinitionV1, type BehaviorReference } from '../../shared/taskforceAgentDefinition.js';
2
- import { type TaskforceAgentProviderKey } from '../../shared/taskforceAgentModels.js';
2
+ import { type TaskforceAgentProviderKey, type TaskforceAgentConnectionScope } from '../../shared/taskforceAgentModels.js';
3
3
  import type { ModelGatewayTier } from '../../services/modelGateway.js';
4
4
  export declare const PERSONALITY_PRESETS: readonly [{
5
5
  readonly key: "practical";
@@ -104,6 +104,7 @@ export declare function buildTaskforceAgentDefinitionFromDraft(input: {
104
104
  providerKey: TaskforceAgentProviderKey;
105
105
  modelKey: string;
106
106
  tier: ModelGatewayTier;
107
+ connectionScope?: TaskforceAgentConnectionScope;
107
108
  }): AgentDefinitionV1;
108
109
  export declare function hasUnsavedAvatarPresentation(definition: AgentDefinitionV1 | null | undefined, draft: TaskforceAgentEditorDraft): boolean;
109
110
  export {};
@@ -107,7 +107,9 @@ export function buildTaskforceAgentDefinitionFromDraft(input) {
107
107
  modelKey: input.modelKey,
108
108
  modelSource: selectedModel?.source || 'bedrock',
109
109
  tier: input.tier,
110
- ...(selectedModel?.source === 'subscription_dev' ? { connectionScope: 'local_dev' } : {}),
110
+ ...(selectedModel?.source === 'subscription_dev'
111
+ ? { connectionScope: input.connectionScope || 'local_dev' }
112
+ : {}),
111
113
  },
112
114
  behavior: {
113
115
  ...(personality ? { personality } : {}),
@@ -14,7 +14,7 @@ export declare function getTaskWorkflowAssignment(taskId: string, context?: Work
14
14
  export type WorkflowCompletionProjection = {
15
15
  executionId: string;
16
16
  taskId: string;
17
- assignmentSnapshot: unknown;
17
+ assignmentSnapshot: WorkflowAssignmentDetails;
18
18
  taskSnapshot?: TaskItem | null;
19
19
  taskAuthoritative?: unknown;
20
20
  };
@@ -80,6 +80,9 @@ export async function completeCurrentWorkflowStep(executionId, idempotencyKey, c
80
80
  taskId: payload.result.taskSnapshot.id,
81
81
  reason: 'workflow-step',
82
82
  ...(installedTask ? { authoritativeTask: installedTask } : {}),
83
+ ...(payload.result.assignmentSnapshot
84
+ ? { authoritativeWorkflowAssignment: payload.result.assignmentSnapshot }
85
+ : {}),
83
86
  });
84
87
  }
85
88
  return payload.result;
@@ -11,6 +11,40 @@ const NO_WORKFLOW = '__none__';
11
11
  function statusLabel(status) {
12
12
  return status.charAt(0).toUpperCase() + status.slice(1);
13
13
  }
14
+ function workflowAssignmentFromMutation(value, taskId) {
15
+ if (!value || typeof value !== 'object' || Array.isArray(value))
16
+ return null;
17
+ const assignment = value;
18
+ if (!assignment.execution
19
+ || assignment.execution.taskId !== taskId
20
+ || !assignment.template
21
+ || !assignment.version
22
+ || !Array.isArray(assignment.steps)
23
+ || !Array.isArray(assignment.events))
24
+ return null;
25
+ return assignment;
26
+ }
27
+ function latestWorkflowEventSequence(assignment) {
28
+ return Math.max(0, ...(assignment?.events || []).map((event) => event.sequenceNumber || 0));
29
+ }
30
+ function workflowExecutionCreatedAt(assignment) {
31
+ const timestamp = Date.parse(assignment.execution.createdAt);
32
+ return Number.isFinite(timestamp) ? timestamp : 0;
33
+ }
34
+ function newerWorkflowAssignment(current, candidate) {
35
+ if (!candidate)
36
+ return current;
37
+ if (!current)
38
+ return candidate;
39
+ if (current.execution.id !== candidate.execution.id) {
40
+ return workflowExecutionCreatedAt(candidate) > workflowExecutionCreatedAt(current)
41
+ ? candidate
42
+ : current;
43
+ }
44
+ return latestWorkflowEventSequence(candidate) >= latestWorkflowEventSequence(current)
45
+ ? candidate
46
+ : current;
47
+ }
14
48
  export function TaskWorkflowAssignmentField({ taskId, workspaceId, taskStatus, taskUpdatedAt, comments = [], assigneeOptions = [], currentActorId, apiBaseUrl, runtimeMode = 'local', disabled = false, onActivatePendingWorkflow, onActiveOwnerChange, }) {
15
49
  const [workflows, setWorkflows] = React.useState([]);
16
50
  const [assignment, setAssignment] = React.useState(null);
@@ -21,6 +55,7 @@ export function TaskWorkflowAssignmentField({ taskId, workspaceId, taskStatus, t
21
55
  const [activationRetryToken, setActivationRetryToken] = React.useState(0);
22
56
  const [error, setError] = React.useState('');
23
57
  const requestGenerationRef = React.useRef(0);
58
+ const assignmentRefreshGenerationRef = React.useRef(0);
24
59
  const activationAttemptRef = React.useRef(null);
25
60
  const refreshedTaskRevisionRef = React.useRef({
26
61
  taskId,
@@ -29,6 +64,7 @@ export function TaskWorkflowAssignmentField({ taskId, workspaceId, taskStatus, t
29
64
  const requestContext = React.useMemo(() => ({ workspaceId, apiBaseUrl, runtimeMode }), [apiBaseUrl, runtimeMode, workspaceId]);
30
65
  React.useEffect(() => {
31
66
  const requestGeneration = ++requestGenerationRef.current;
67
+ const assignmentRefreshGeneration = ++assignmentRefreshGenerationRef.current;
32
68
  setLoading(true);
33
69
  setAssigning(false);
34
70
  setCompletingStep(false);
@@ -47,18 +83,23 @@ export function TaskWorkflowAssignmentField({ taskId, workspaceId, taskStatus, t
47
83
  if (workflowResult.status === 'fulfilled') {
48
84
  setWorkflows(workflowResult.value.filter((summary) => (summary.template.status === 'active' && Boolean(summary.latestPublishedVersion))));
49
85
  }
50
- if (assignmentResult.status === 'fulfilled') {
51
- setAssignment(assignmentResult.value);
52
- }
53
- if (assignmentResult.status === 'rejected') {
54
- setError(String(assignmentResult.reason?.message || 'Unable to load workflow assignment.'));
55
- }
56
- else if (!assignmentResult.value && workflowResult.status === 'rejected') {
57
- setError(String(workflowResult.reason?.message || 'Unable to load workflows.'));
86
+ if (assignmentRefreshGeneration === assignmentRefreshGenerationRef.current) {
87
+ if (assignmentResult.status === 'fulfilled') {
88
+ setAssignment(assignmentResult.value);
89
+ if (!assignmentResult.value && workflowResult.status === 'rejected') {
90
+ setError(String(workflowResult.reason?.message || 'Unable to load workflows.'));
91
+ }
92
+ }
93
+ else {
94
+ setError(String(assignmentResult.reason?.message || 'Unable to load workflow assignment.'));
95
+ }
58
96
  }
59
97
  setLoading(false);
60
98
  });
61
- return () => { requestGenerationRef.current += 1; };
99
+ return () => {
100
+ requestGenerationRef.current += 1;
101
+ assignmentRefreshGenerationRef.current += 1;
102
+ };
62
103
  }, [requestContext, taskId]);
63
104
  const latestAssignment = assignment?.execution.taskId === taskId ? assignment : null;
64
105
  const hasActiveExecution = latestAssignment
@@ -186,13 +227,18 @@ export function TaskWorkflowAssignmentField({ taskId, workspaceId, taskStatus, t
186
227
  return;
187
228
  refreshedTaskRevisionRef.current = { taskId, revision: taskRevision };
188
229
  const requestGeneration = requestGenerationRef.current;
230
+ const assignmentRefreshGeneration = ++assignmentRefreshGenerationRef.current;
189
231
  void getTaskWorkflowAssignment(taskId, requestContext).then((nextAssignment) => {
190
232
  if (requestGeneration !== requestGenerationRef.current)
191
233
  return;
234
+ if (assignmentRefreshGeneration !== assignmentRefreshGenerationRef.current)
235
+ return;
192
236
  setAssignment(nextAssignment);
193
237
  }).catch((requestError) => {
194
238
  if (requestGeneration !== requestGenerationRef.current)
195
239
  return;
240
+ if (assignmentRefreshGeneration !== assignmentRefreshGenerationRef.current)
241
+ return;
196
242
  setError(String(requestError?.message || 'Unable to refresh workflow assignment.'));
197
243
  });
198
244
  }, [isPending, requestContext, taskId, taskStatus, taskUpdatedAt]);
@@ -210,15 +256,27 @@ export function TaskWorkflowAssignmentField({ taskId, workspaceId, taskStatus, t
210
256
  const eventTaskId = String(detail.taskId || '').trim();
211
257
  if (eventTaskId !== taskId)
212
258
  return;
213
- const requestGeneration = ++requestGenerationRef.current;
259
+ const authoritativeAssignment = workflowAssignmentFromMutation(detail.authoritativeWorkflowAssignment, taskId);
260
+ if (authoritativeAssignment) {
261
+ ++assignmentRefreshGenerationRef.current;
262
+ setAssignment((current) => newerWorkflowAssignment(current, authoritativeAssignment));
263
+ setError('');
264
+ return;
265
+ }
266
+ const requestGeneration = requestGenerationRef.current;
267
+ const assignmentRefreshGeneration = ++assignmentRefreshGenerationRef.current;
214
268
  void getTaskWorkflowAssignment(taskId, requestContext).then((nextAssignment) => {
215
269
  if (requestGeneration !== requestGenerationRef.current)
216
270
  return;
271
+ if (assignmentRefreshGeneration !== assignmentRefreshGenerationRef.current)
272
+ return;
217
273
  setAssignment(nextAssignment);
218
274
  setError('');
219
275
  }).catch((requestError) => {
220
276
  if (requestGeneration !== requestGenerationRef.current)
221
277
  return;
278
+ if (assignmentRefreshGeneration !== assignmentRefreshGenerationRef.current)
279
+ return;
222
280
  setError(String(requestError?.message || 'Unable to refresh workflow assignment.'));
223
281
  });
224
282
  };