@taskforcehq/taskforce 0.3.329 → 0.3.330

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 (321) hide show
  1. package/dist/Taskforce.module.css +290 -45
  2. package/dist/components/features/AiProfilesModule.js +3 -5
  3. package/dist/components/features/TaskSettings.js +70 -14
  4. package/dist/components/features/TaskforceAgentsModule.d.ts +1 -2
  5. package/dist/components/features/TaskforceAgentsModule.js +586 -116
  6. package/dist/components/features/agentConnections/AgentConnectionControl.d.ts +15 -0
  7. package/dist/components/features/agentConnections/AgentConnectionControl.js +125 -0
  8. package/dist/components/features/agentConnections/ModelConnectionsPanel.d.ts +18 -0
  9. package/dist/components/features/agentConnections/ModelConnectionsPanel.js +393 -0
  10. package/dist/components/features/agentConnections/modelConnectionsApi.d.ts +65 -0
  11. package/dist/components/features/agentConnections/modelConnectionsApi.js +129 -0
  12. package/dist/components/features/taskforceAgentAssignments/AgentBehaviorAssignments.js +15 -24
  13. package/dist/components/features/taskforceAgentEditorModel.d.ts +2 -1
  14. package/dist/components/features/taskforceAgentEditorModel.js +3 -1
  15. package/dist/components/features/workflowManager/workflowManagerApi.d.ts +1 -1
  16. package/dist/components/features/workflowManager/workflowManagerApi.js +3 -0
  17. package/dist/components/task/TaskWorkflowAssignmentField.js +68 -10
  18. package/dist/components/views/StandaloneLayout.js +27 -26
  19. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +4 -3
  20. package/dist/components/views/standalone/modals/SyncStatusModal.js +33 -38
  21. package/dist/config/envSchema.js +48 -1
  22. package/dist/core/McpTokenService.d.ts +23 -1
  23. package/dist/core/McpTokenService.js +93 -10
  24. package/dist/core/TaskChecklistCommandService.d.ts +27 -0
  25. package/dist/core/TaskChecklistCommandService.js +83 -5
  26. package/dist/core/TaskLifecycleCommandService.d.ts +3 -1
  27. package/dist/core/TaskLifecycleCommandService.js +6 -2
  28. package/dist/core/Taskforce.d.ts +32 -0
  29. package/dist/core/Taskforce.js +125 -4
  30. package/dist/core/types.d.ts +8 -0
  31. package/dist/documentReviews/commandService.d.ts +2 -1
  32. package/dist/documentReviews/commandService.js +6 -3
  33. package/dist/hooks/sync/orchestratorShared.d.ts +1 -0
  34. package/dist/hooks/sync/orchestratorShared.js +2 -0
  35. package/dist/hooks/sync/useLocalSyncCoordinatorSnapshot.d.ts +3 -0
  36. package/dist/hooks/sync/useLocalSyncCoordinatorSnapshot.js +85 -9
  37. package/dist/hooks/sync/useSyncStatusControls.d.ts +4 -1
  38. package/dist/hooks/sync/useSyncStatusControls.js +47 -11
  39. package/dist/hooks/useSyncOrchestrator.d.ts +3 -0
  40. package/dist/hooks/useSyncOrchestrator.js +111 -2
  41. package/dist/hooks/useTaskData.d.ts +31 -1
  42. package/dist/hooks/useTaskData.js +29 -7
  43. package/dist/hooks/useTaskMutations.d.ts +3 -1
  44. package/dist/hooks/useTaskMutations.js +46 -12
  45. package/dist/hooks/useTaskforce.d.ts +2 -0
  46. package/dist/hooks/useTaskforce.js +11 -2
  47. package/dist/mcp/clientRegistry.d.ts +2 -2
  48. package/dist/mcp/clientRegistry.js +1 -1
  49. package/dist/mcp/documentAssetRegistrar.js +9 -6
  50. package/dist/mcp/durableTaskMutationRouter.d.ts +4 -1
  51. package/dist/mcp/durableTaskMutationRouter.js +10 -0
  52. package/dist/mcp/durableTaskRelationshipMutationRouter.js +4 -0
  53. package/dist/mcp/httpProtocolRouting.d.ts +10 -0
  54. package/dist/mcp/httpProtocolRouting.js +19 -0
  55. package/dist/mcp/localCliHealth.d.ts +2 -2
  56. package/dist/mcp/localCliHealth.js +2 -2
  57. package/dist/mcp/localServiceProxy.d.ts +1 -0
  58. package/dist/mcp/localServiceProxy.js +530 -68
  59. package/dist/mcp/planningAttachmentCommandAdapter.d.ts +3 -1
  60. package/dist/mcp/planningAttachmentCommandAdapter.js +20 -2
  61. package/dist/mcp/runtime.d.ts +54 -79
  62. package/dist/mcp/runtime.js +384 -97
  63. package/dist/mcp/taskPlanningRegistrar.js +23 -4
  64. package/dist/mcp/toolProfiles.d.ts +2 -0
  65. package/dist/mcp/toolProfiles.js +10 -0
  66. package/dist/mcp/toolRegistry.d.ts +1 -0
  67. package/dist/mcp/toolRegistry.js +1 -0
  68. package/dist/mcp/workflowExecutionRegistrar.js +4 -4
  69. package/dist/migrations/taskSchemaMigrations.d.ts +5 -0
  70. package/dist/migrations/taskSchemaMigrations.js +415 -0
  71. package/dist/runtime/appGates.d.ts +2 -1
  72. package/dist/runtime/appGates.js +7 -0
  73. package/dist/server/index.d.ts +3 -0
  74. package/dist/server/index.js +108 -11
  75. package/dist/server/localDatabaseIdentity.js +4 -1
  76. package/dist/server/localServiceLease.js +2 -1
  77. package/dist/server/localServiceSessions.d.ts +14 -17
  78. package/dist/server/localServiceSessions.js +308 -94
  79. package/dist/server/localWorkspaceSyncServerRuntime.js +12 -14
  80. package/dist/server/routes/admin.d.ts +4 -0
  81. package/dist/server/routes/admin.js +112 -3
  82. package/dist/server/routes/agents.d.ts +7 -0
  83. package/dist/server/routes/agents.js +277 -24
  84. package/dist/server/routes/billing.js +5 -1
  85. package/dist/server/routes/durableTaskHttpRouters.d.ts +2 -0
  86. package/dist/server/routes/executorPhaseAApprovals.d.ts +19 -0
  87. package/dist/server/routes/executorPhaseAApprovals.js +140 -0
  88. package/dist/server/routes/executorPhaseAIssuerRedemptions.d.ts +11 -0
  89. package/dist/server/routes/executorPhaseAIssuerRedemptions.js +24 -0
  90. package/dist/server/routes/githubReviewEvidence.d.ts +19 -0
  91. package/dist/server/routes/githubReviewEvidence.js +116 -0
  92. package/dist/server/routes/localService.d.ts +15 -4
  93. package/dist/server/routes/localService.js +136 -40
  94. package/dist/server/routes/modelConnections.d.ts +24 -0
  95. package/dist/server/routes/modelConnections.js +205 -0
  96. package/dist/server/routes/shared.d.ts +19 -0
  97. package/dist/server/routes/shared.js +95 -19
  98. package/dist/server/routes/sync.js +1 -0
  99. package/dist/server/routes/syncAuxRoutes.js +5 -1
  100. package/dist/server/routes/syncPushRoutes.d.ts +1 -0
  101. package/dist/server/routes/syncPushRoutes.js +1 -0
  102. package/dist/server/routes/syncV3FeedRoutes.js +10 -1
  103. package/dist/server/routes/syncV3OperationRoutes.js +35 -9
  104. package/dist/server/routes/tasks.js +21 -2
  105. package/dist/server/routes/workflows.js +9 -1
  106. package/dist/server/routes.js +59 -0
  107. package/dist/services/codexAppServerModelGateway.d.ts +54 -5
  108. package/dist/services/codexAppServerModelGateway.js +246 -21
  109. package/dist/services/codexDeviceCodeLoginManager.d.ts +39 -0
  110. package/dist/services/codexDeviceCodeLoginManager.js +265 -0
  111. package/dist/services/codexModelProviderConnectionLifecycle.d.ts +8 -0
  112. package/dist/services/codexModelProviderConnectionLifecycle.js +73 -0
  113. package/dist/services/executorPhaseAApprovalRuntime.d.ts +14 -0
  114. package/dist/services/executorPhaseAApprovalRuntime.js +24 -0
  115. package/dist/services/executorPhaseAApprovalService.d.ts +40 -0
  116. package/dist/services/executorPhaseAApprovalService.js +609 -0
  117. package/dist/services/executorPhaseADriftEvaluator.d.ts +78 -0
  118. package/dist/services/executorPhaseADriftEvaluator.js +93 -0
  119. package/dist/services/executorPhaseAIssuerCore.d.ts +126 -0
  120. package/dist/services/executorPhaseAIssuerCore.js +295 -0
  121. package/dist/services/executorPhaseAIssuerRedemptionService.d.ts +34 -0
  122. package/dist/services/executorPhaseAIssuerRedemptionService.js +158 -0
  123. package/dist/services/githubReviewEvidenceRuntime.d.ts +14 -0
  124. package/dist/services/githubReviewEvidenceRuntime.js +30 -0
  125. package/dist/services/githubReviewEvidenceService.d.ts +93 -0
  126. package/dist/services/githubReviewEvidenceService.js +336 -0
  127. package/dist/services/modelGateway.d.ts +7 -0
  128. package/dist/services/modelProviderConnectionService.d.ts +71 -0
  129. package/dist/services/modelProviderConnectionService.js +279 -0
  130. package/dist/services/modelProviderCredentialHome.d.ts +32 -0
  131. package/dist/services/modelProviderCredentialHome.js +271 -0
  132. package/dist/services/ownerScopedModelConnectionGateway.d.ts +12 -0
  133. package/dist/services/ownerScopedModelConnectionGateway.js +30 -0
  134. package/dist/services/taskforceAgentConnectionBindingService.d.ts +66 -0
  135. package/dist/services/taskforceAgentConnectionBindingService.js +185 -0
  136. package/dist/services/taskforceAgentMcpBridge.d.ts +1 -1
  137. package/dist/services/taskforceAgentMcpBridge.js +7 -24
  138. package/dist/shared/adminEdgeProxy.d.ts +5 -0
  139. package/dist/shared/adminEdgeProxy.js +94 -0
  140. package/dist/shared/adminProxySessionCookies.d.ts +16 -0
  141. package/dist/shared/adminProxySessionCookies.js +86 -0
  142. package/dist/shared/aiProfileColors.d.ts +31 -1
  143. package/dist/shared/aiProfileColors.js +14 -6
  144. package/dist/shared/executorPhaseAApprovalGoldenVector.json +269 -0
  145. package/dist/shared/executorPhaseAApprovalReceipt.d.ts +112 -0
  146. package/dist/shared/executorPhaseAApprovalReceipt.js +58 -0
  147. package/dist/shared/executorPhaseAApprovalTrustPolicy.json +10 -0
  148. package/dist/shared/executorPhaseADriftContracts.d.ts +37 -0
  149. package/dist/shared/executorPhaseADriftContracts.js +63 -0
  150. package/dist/shared/executorPhaseAIssuerContracts.d.ts +75 -0
  151. package/dist/shared/executorPhaseAIssuerContracts.js +90 -0
  152. package/dist/shared/executorPhaseARunAuthorization.d.ts +120 -0
  153. package/dist/shared/executorPhaseARunAuthorization.js +232 -0
  154. package/dist/shared/executorPhaseASchemaValidation.d.ts +7 -0
  155. package/dist/shared/executorPhaseASchemaValidation.js +53 -0
  156. package/dist/shared/executorPhaseASchemas/approval-receipt.schema.json +106 -0
  157. package/dist/shared/executorPhaseASchemas/drift-attestation.schema.json +54 -0
  158. package/dist/shared/executorPhaseASchemas/drift-request.schema.json +47 -0
  159. package/dist/shared/executorPhaseASchemas/foundation-manifest.schema.json +29 -0
  160. package/dist/shared/executorPhaseASchemas/issuer-audit-record.schema.json +24 -0
  161. package/dist/shared/executorPhaseASchemas/issuer-proof.schema.json +43 -0
  162. package/dist/shared/executorPhaseASchemas/issuer-redemption-request.schema.json +18 -0
  163. package/dist/shared/executorPhaseASchemas/issuer-redemption-response.schema.json +39 -0
  164. package/dist/shared/executorPhaseASchemas/logical-envelope.schema.json +149 -0
  165. package/dist/shared/executorPhaseASchemas/preflight-report.schema.json +216 -0
  166. package/dist/shared/executorPhaseASchemas/run-capability.schema.json +111 -0
  167. package/dist/shared/executorPhaseASchemas/sender-proof.schema.json +37 -0
  168. package/dist/shared/executorPhaseASchemas/workload-grant.schema.json +68 -0
  169. package/dist/shared/executorPhaseASchemas/workload-jwt-claims.schema.json +67 -0
  170. package/dist/shared/executorPhaseATiming.d.ts +1 -0
  171. package/dist/shared/executorPhaseATiming.js +1 -0
  172. package/dist/shared/planningIdentity.js +1 -1
  173. package/dist/shared/productLogoKeys.d.ts +2 -0
  174. package/dist/shared/productLogoKeys.js +18 -0
  175. package/dist/shared/taskforceAgentDefinition.d.ts +0 -1
  176. package/dist/shared/taskforceAgentDefinition.js +6 -9
  177. package/dist/shared/taskforceAgentModels.js +7 -3
  178. package/dist/shared/userFeatureGrants.d.ts +4 -0
  179. package/dist/shared/userFeatureGrants.js +7 -0
  180. package/dist/storage/executorPhaseAApprovalReceiptStore.d.ts +37 -0
  181. package/dist/storage/executorPhaseAApprovalReceiptStore.js +81 -0
  182. package/dist/storage/executorPhaseARunAuthorizationStore.d.ts +103 -0
  183. package/dist/storage/executorPhaseARunAuthorizationStore.js +492 -0
  184. package/dist/storage/githubReviewEvidenceInstallationStore.d.ts +26 -0
  185. package/dist/storage/githubReviewEvidenceInstallationStore.js +89 -0
  186. package/dist/storage/modelProviderConnectionStore.d.ts +99 -0
  187. package/dist/storage/modelProviderConnectionStore.js +446 -0
  188. package/dist/storage/postgresAdapter.d.ts +4 -0
  189. package/dist/storage/postgresAdapter.js +11 -0
  190. package/dist/storage/postgresWorker.js +9 -1
  191. package/dist/storage/postgresWorkerProtocol.d.ts +1 -0
  192. package/dist/storage/taskforceAgentConnectionBindingStore.d.ts +31 -0
  193. package/dist/storage/taskforceAgentConnectionBindingStore.js +84 -0
  194. package/dist/sync/coordinator/localSyncExecutionPermitStore.js +60 -29
  195. package/dist/sync/coordinator/localWorkspaceSyncPendingV2PushProgress.d.ts +10 -0
  196. package/dist/sync/coordinator/localWorkspaceSyncPendingV2PushProgress.js +29 -0
  197. package/dist/sync/coordinator/localWorkspaceSyncRunnerState.d.ts +15 -1
  198. package/dist/sync/coordinator/localWorkspaceSyncRunnerState.js +99 -2
  199. package/dist/sync/coordinator/workspaceSyncCredentialBroker.d.ts +1 -0
  200. package/dist/sync/coordinator/workspaceSyncCredentialBroker.js +33 -4
  201. package/dist/sync/engine/workspaceSyncContentLifecycleAdapter.d.ts +2 -1
  202. package/dist/sync/engine/workspaceSyncContentVersionStore.d.ts +38 -0
  203. package/dist/sync/engine/workspaceSyncContentVersionStore.js +50 -1
  204. package/dist/sync/engine/workspaceSyncEngineLifecyclePolicy.js +6 -6
  205. package/dist/sync/engine/workspaceSyncEnginePushCompletion.d.ts +14 -0
  206. package/dist/sync/engine/workspaceSyncEnginePushCompletion.js +84 -0
  207. package/dist/sync/engine/workspaceSyncEngineRecovery.js +1 -0
  208. package/dist/sync/engine/workspaceSyncEngineRuntime.d.ts +1 -0
  209. package/dist/sync/engine/workspaceSyncEngineRuntime.js +1 -0
  210. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +112 -38
  211. package/dist/sync/engine/workspaceSyncEngineServerSnapshotReader.js +6 -0
  212. package/dist/sync/engine/workspaceSyncEngineTransfers.js +3 -1
  213. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.d.ts +1 -0
  214. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +29 -0
  215. package/dist/sync/syncApplyHandlers.d.ts +2 -0
  216. package/dist/sync/syncApplyHandlers.js +10 -1
  217. package/dist/sync/syncService.d.ts +15 -0
  218. package/dist/sync/syncService.js +83 -18
  219. package/dist/sync/taskforceAgentSyncAuthorization.d.ts +16 -0
  220. package/dist/sync/taskforceAgentSyncAuthorization.js +20 -0
  221. package/dist/sync/v3/combinedFeedCapability.js +4 -0
  222. package/dist/sync/v3/durableInitiativeMutationRouter.js +18 -10
  223. package/dist/sync/v3/durableOperationReplay.d.ts +14 -0
  224. package/dist/sync/v3/durableOperationReplay.js +18 -0
  225. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +3 -0
  226. package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +2 -1
  227. package/dist/sync/v3/durableTaskChecklistMutationRouter.js +58 -15
  228. package/dist/sync/v3/durableTaskCommentMutationRouter.js +3 -0
  229. package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +1 -1
  230. package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +10 -0
  231. package/dist/sync/v3/durableTaxonomyMutationRouter.js +5 -0
  232. package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.d.ts +11 -0
  233. package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.js +39 -10
  234. package/dist/sync/v3/durableWorkstreamMutationRouter.js +18 -10
  235. package/dist/sync/v3/durableWorkstreamTaskOrderMutationRouter.js +16 -5
  236. package/dist/sync/v3/localOutboxDispatchRuntime.js +2 -2
  237. package/dist/sync/v3/localTaskChecklistOutboxHandler.js +16 -2
  238. package/dist/sync/v3/localTaskChecklistOutboxService.d.ts +4 -0
  239. package/dist/sync/v3/localTaskChecklistOutboxService.js +14 -1
  240. package/dist/sync/v3/localTaskStatusOutboxService.d.ts +8 -0
  241. package/dist/sync/v3/localTaskStatusOutboxService.js +219 -12
  242. package/dist/sync/v3/syncDurabilityStore.d.ts +34 -0
  243. package/dist/sync/v3/syncDurabilityStore.js +168 -2
  244. package/dist/sync/v3/taskChecklistMutationService.d.ts +11 -0
  245. package/dist/sync/v3/taskChecklistMutationService.js +44 -1
  246. package/dist/sync/v3/taskStatusMutationService.d.ts +29 -1
  247. package/dist/sync/v3/taskStatusMutationService.js +119 -3
  248. package/dist/sync/v3/workflowAssignmentSync.d.ts +5 -0
  249. package/dist/sync/v3/workflowAssignmentSync.js +27 -0
  250. package/dist/sync/v3/workspaceSyncJournalReader.js +1 -1
  251. package/dist/sync/v3/workspaceSyncOperationRetentionService.d.ts +16 -0
  252. package/dist/sync/v3/workspaceSyncOperationRetentionService.js +83 -0
  253. package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.d.ts +1 -0
  254. package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +9 -2
  255. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +7 -0
  256. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +44 -32
  257. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +9 -5
  258. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +54 -38
  259. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +10 -0
  260. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +70 -31
  261. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +2 -1
  262. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +16 -0
  263. package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +6 -0
  264. package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +3 -0
  265. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +5 -0
  266. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +36 -2
  267. package/dist/sync/v3/workspaceSyncV3RepairSnapshotRetentionService.d.ts +17 -0
  268. package/dist/sync/v3/workspaceSyncV3RepairSnapshotRetentionService.js +64 -0
  269. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverCoverage.d.ts +7 -0
  270. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverCoverage.js +33 -0
  271. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverWireContract.d.ts +7 -0
  272. package/dist/sync/v3/workspaceSyncV3WorkflowTaskCoverWireContract.js +8 -0
  273. package/dist/sync/workspaceRepair.d.ts +5 -2
  274. package/dist/sync/workspaceRepair.js +161 -6
  275. package/dist/sync/workspaceSyncModel.d.ts +21 -0
  276. package/dist/sync/workspaceSyncModel.js +35 -7
  277. package/dist/sync/workspaceSyncState.d.ts +1 -0
  278. package/dist/sync/workspaceSyncV2SnapshotBuilder.d.ts +6 -0
  279. package/dist/sync/workspaceSyncV2SnapshotBuilder.js +5 -0
  280. package/dist/ui/assets/AiIdentityRosterCard-OcSz1PkK.js +1 -0
  281. package/dist/ui/assets/AiProfilesModule-ByhYW5Xi.js +1 -0
  282. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-B68Wg-RV.js → AnnotatedAttachmentWorkspace-BTM6nIg_.js} +1 -1
  283. package/dist/ui/assets/{AssetTraySortControl-C8Rztyal.js → AssetTraySortControl-DabEG8L4.js} +1 -1
  284. package/dist/ui/assets/{ContextAttachmentManager-Cj3c7X9y.js → ContextAttachmentManager-Coa8yVGM.js} +1 -1
  285. package/dist/ui/assets/{DocumentWorkspace-B5uvzems.js → DocumentWorkspace-BB0qhdix.js} +1 -1
  286. package/dist/ui/assets/{EntityActivityTimeline-BVFvCB5R.js → EntityActivityTimeline-CZ8x4UJE.js} +1 -1
  287. package/dist/ui/assets/{PlanningModule-B5DWQOGz.js → PlanningModule-pRPjR7v5.js} +1 -1
  288. package/dist/ui/assets/{PlansPage-AY4cGWco.js → PlansPage-D86pZSSl.js} +1 -1
  289. package/dist/ui/assets/{TaskContextUpload-DZxTVesW.js → TaskContextUpload-BVIkU9yT.js} +1 -1
  290. package/dist/ui/assets/TaskSettings-Cv1mNhv_.js +12 -0
  291. package/dist/ui/assets/TaskforceAgentsModule-b-8R25cJ.css +1 -0
  292. package/dist/ui/assets/TaskforceAgentsModule-gmH-PD2q.js +24 -0
  293. package/dist/ui/assets/{WorkflowManagerModule-DEr5di_r.js → WorkflowManagerModule-DbfnHJ-f.js} +1 -1
  294. package/dist/ui/assets/index-BZ-hdZSk.css +1 -0
  295. package/dist/ui/assets/index-CTsDBaef.js +7 -0
  296. package/dist/ui/assets/{vendor-icons-D9Lpw-j4.js → vendor-icons-Bsq-mcEn.js} +1 -1
  297. package/dist/ui/branding/logos/letterhead_dark.png +0 -0
  298. package/dist/ui/branding/logos/letterhead_light.png +0 -0
  299. package/dist/ui/branding/logos/signature_dark.png +0 -0
  300. package/dist/ui/branding/logos/signature_light.png +0 -0
  301. package/dist/ui/index.html +3 -3
  302. package/dist/utils/aiProfileDefaultLogo.js +16 -48
  303. package/dist/utils/httpSetCookie.d.ts +1 -0
  304. package/dist/utils/httpSetCookie.js +4 -1
  305. package/dist/utils/localRuntimeUrl.d.ts +1 -0
  306. package/dist/utils/localRuntimeUrl.js +14 -0
  307. package/dist/utils/productLogoRegistry.d.ts +2 -0
  308. package/dist/utils/productLogoRegistry.js +36 -0
  309. package/dist/utils/syncStatusPresentation.d.ts +4 -0
  310. package/dist/utils/syncStatusPresentation.js +17 -0
  311. package/dist/utils/taskEvents.d.ts +2 -0
  312. package/dist/utils/workspaceSyncPresentation.d.ts +1 -1
  313. package/dist/utils/workspaceSyncPresentation.js +23 -17
  314. package/package.json +22 -4
  315. package/dist/ui/assets/AiIdentityRosterCard-BZM4aQnM.js +0 -1
  316. package/dist/ui/assets/AiProfilesModule-BbAEWtHH.js +0 -1
  317. package/dist/ui/assets/TaskSettings-BeWAuyZj.js +0 -12
  318. package/dist/ui/assets/TaskforceAgentsModule-BifCYtjR.js +0 -21
  319. package/dist/ui/assets/TaskforceAgentsModule-D6IC0S7Q.css +0 -1
  320. package/dist/ui/assets/index-I5zhdtgt.js +0 -7
  321. package/dist/ui/assets/index-VRFsx3wz.css +0 -1
@@ -1,10 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
- import { AlertTriangle, Beaker, Bot, BriefcaseBusiness, CheckCircle, ChevronLeft, Database, ExternalLink, Loader2, MessageSquare, Pencil, Plug, Plus, RotateCcw, Save, Send, Sparkles, X } from 'lucide-react';
3
+ import { AlertTriangle, Beaker, Bot, BriefcaseBusiness, Check, CheckCircle, ChevronLeft, Copy, Database, ExternalLink, Loader2, Pencil, Plug, Plus, RotateCcw, Save, Send, Sparkles, X } from 'lucide-react';
4
4
  import styles from '../../Taskforce.module.css';
5
- import { AI_PROFILE_COLOR_TOKENS, assignedAiProfileColor } from '../../shared/aiProfileColors.js';
6
- import { COLOR_PALETTE } from '../../shared/colorPalette.js';
7
- import { DEFAULT_TASKFORCE_AGENT_MODEL_KEY, DEFAULT_TASKFORCE_AGENT_PROVIDER_KEY, TASKFORCE_AGENT_PROVIDER_OPTIONS, labelForTaskforceAgentModel, labelForTaskforceAgentProvider, modelsForTaskforceAgentProvider, normalizeTaskforceAgentModelKey, normalizeTaskforceAgentProviderKey, } from '../../shared/taskforceAgentModels';
5
+ import { AI_PROFILE_SIGNATURE_COLOR_OPTIONS, assignedAiProfileColor } from '../../shared/aiProfileColors.js';
6
+ import { DEFAULT_TASKFORCE_AGENT_MODEL_KEY, DEFAULT_TASKFORCE_AGENT_PROVIDER_KEY, TASKFORCE_AGENT_MODEL_OPTIONS, TASKFORCE_AGENT_PROVIDER_OPTIONS, isTaskforceAgentModelVisible, labelForTaskforceAgentModel, labelForTaskforceAgentProvider, normalizeTaskforceAgentProviderKey, } from '../../shared/taskforceAgentModels';
8
7
  import { dispatchTaskforceAgentsMutated, TASKFORCE_AGENTS_MUTATED_EVENT } from '../../utils/taskforceAgentEvents';
9
8
  import { dispatchTaskforceTasksMutated } from '../../utils/taskEvents';
10
9
  import { buildVersionedAvatarUrl } from '../../utils/avatarDisplay';
@@ -23,10 +22,48 @@ import { AVATAR_ART_STYLE_PRESETS, buildTaskforceAgentDefinitionFromDraft, creat
23
22
  import { SkillManagerPanel } from './agentSkills/SkillManagerPanel.js';
24
23
  import { EffectiveConfigurationSummary, FutureLibrarySummary, FutureWorkspacePanel, ToolAccessSelector, } from './agentCapabilities/AgentCapabilitiesPrototype.js';
25
24
  import { applyCloudWorkflowCompletionProjection, } from './workflowManager/workflowManagerApi.js';
25
+ import { ModelConnectionsPanel } from './agentConnections/ModelConnectionsPanel.js';
26
+ import { bindAgentConnection, clearAgentConnectionBinding, inspectAgentConnectionBinding, listModelConnections, ModelConnectionsApiError, } from './agentConnections/modelConnectionsApi.js';
26
27
  const DEFAULT_SYSTEM_PROMPT = 'Be concise and actionable.';
27
28
  const DEFAULT_AGENT_NAME = 'New Taskforce Agent';
28
29
  const TEST_PROMPT_PLACEHOLDER = 'Message this agent...';
29
30
  const AGENT_ROSTER_REFRESH_INTERVAL_MS = 10_000;
31
+ const AGENT_MODEL_SOURCE_OPTIONS = [
32
+ { key: 'taskforce_managed', label: 'Taskforce Managed' },
33
+ ];
34
+ const CONNECTION_SOURCE_PREFIX = 'connection:';
35
+ const LOCAL_AGENT_BINDING_SYNC_RETRIES = 20;
36
+ const LOCAL_AGENT_BINDING_SYNC_DELAY_MS = 250;
37
+ async function bindAgentConnectionAfterLocalSync(agentId, connectionId, context, waitForCloudSync) {
38
+ for (let attempt = 0;; attempt += 1) {
39
+ try {
40
+ return await bindAgentConnection(agentId, connectionId, context);
41
+ }
42
+ catch (error) {
43
+ const retryable = waitForCloudSync
44
+ && error instanceof ModelConnectionsApiError
45
+ && error.code === 'TASKFORCE_AGENT_NOT_FOUND'
46
+ && attempt < LOCAL_AGENT_BINDING_SYNC_RETRIES;
47
+ if (!retryable)
48
+ throw error;
49
+ await new Promise((resolve) => window.setTimeout(resolve, LOCAL_AGENT_BINDING_SYNC_DELAY_MS));
50
+ }
51
+ }
52
+ }
53
+ function modelConnectionDisplayName(connection) {
54
+ return String(connection.displayMetadata.displayName
55
+ || connection.displayMetadata.accountLabel
56
+ || connection.displayMetadata.accountEmail
57
+ || 'ChatGPT subscription');
58
+ }
59
+ function modelSourceGroupForOption(model) {
60
+ return model?.source === 'personal_byom' || model?.source === 'subscription_dev'
61
+ ? 'personal_connection'
62
+ : 'taskforce_managed';
63
+ }
64
+ function modelMatchesSourceGroup(model, sourceGroup) {
65
+ return modelSourceGroupForOption(model) === sourceGroup;
66
+ }
30
67
  const AGENT_BUILDER_TABS = [
31
68
  { id: 'agents', label: 'Agents' },
32
69
  { id: 'roles', label: 'Roles' },
@@ -100,17 +137,18 @@ function formatModelAvailability(availability) {
100
137
  return 'Unavailable';
101
138
  }
102
139
  const SENSITIVE_TOOL_ARG_PATTERN = /(token|secret|password|credential|api[-_]?key|access[-_]?key)/i;
103
- function sanitizeToolLogValue(value) {
140
+ const MAX_COPIED_TOOL_RESULT_CHARS = 20_000;
141
+ function sanitizeToolLogValue(value, truncateStrings = true) {
104
142
  if (Array.isArray(value))
105
- return value.map((item) => sanitizeToolLogValue(item));
143
+ return value.map((item) => sanitizeToolLogValue(item, truncateStrings));
106
144
  if (!value || typeof value !== 'object') {
107
145
  if (typeof value !== 'string')
108
146
  return value;
109
- return value.length > 240 ? `${value.slice(0, 237).trim()}...` : value;
147
+ return truncateStrings && value.length > 240 ? `${value.slice(0, 237).trim()}...` : value;
110
148
  }
111
149
  return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
112
150
  key,
113
- SENSITIVE_TOOL_ARG_PATTERN.test(key) ? '[redacted]' : sanitizeToolLogValue(entry),
151
+ SENSITIVE_TOOL_ARG_PATTERN.test(key) ? '[redacted]' : sanitizeToolLogValue(entry, truncateStrings),
114
152
  ]));
115
153
  }
116
154
  function formatToolLogJson(value) {
@@ -124,6 +162,20 @@ function formatToolLogResult(value) {
124
162
  return 'No result text returned.';
125
163
  return text.length > 320 ? `${text.slice(0, 317).trim()}...` : text;
126
164
  }
165
+ function formatToolActivityClipboard(toolCall) {
166
+ const rawResult = String(toolCall.resultText || 'No result text returned.');
167
+ const result = rawResult.length > MAX_COPIED_TOOL_RESULT_CHARS
168
+ ? `${rawResult.slice(0, MAX_COPIED_TOOL_RESULT_CHARS)}\n[Result truncated after ${MAX_COPIED_TOOL_RESULT_CHARS.toLocaleString()} characters]`
169
+ : rawResult;
170
+ return [
171
+ `Tool: ${toolCall.name}`,
172
+ `Status: ${toolCall.ok ? 'ok' : 'failed'}`,
173
+ 'Arguments',
174
+ JSON.stringify(sanitizeToolLogValue(toolCall.arguments || {}, false), null, 2),
175
+ 'Result',
176
+ result,
177
+ ].join('\n');
178
+ }
127
179
  function formatToolActivitySummary(toolActivity) {
128
180
  const count = toolActivity.length;
129
181
  if (count === 0)
@@ -210,7 +262,7 @@ function formatConversationOption(summary, fallbackContext = null) {
210
262
  const label = count > 0 ? `${title} (${count})` : title;
211
263
  return taskContext ? `${formatTaskContextLabel(taskContext)} · ${label}` : label;
212
264
  }
213
- export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true, onCloseAgentRosterTray, launchContext = null, onClearLaunchContext, onOpenTaskContext, taskReferences, surface = 'module', runtimeMode = 'local', cloudAuthConfigured = false, authSessionResolved = true, isAuthenticated = false, resolveCloudAuthUrl, theme = 'dark', rememberedAgentId, rememberedConversationId, rememberedConversationByAgentId, rememberedSelectionReady = true, onRememberSelection, onRenderDrawerHeader, onOpenConversation, }) {
265
+ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true, onCloseAgentRosterTray, launchContext = null, onClearLaunchContext, onOpenTaskContext, taskReferences, surface = 'module', runtimeMode = 'local', cloudAuthConfigured = false, authSessionResolved = true, isAuthenticated = false, resolveCloudAuthUrl, theme = 'dark', rememberedAgentId, rememberedConversationId, rememberedConversationByAgentId, rememberedSelectionReady = true, onRememberSelection, onRenderDrawerHeader, }) {
214
266
  const logIdRef = useRef(1);
215
267
  const initialAgentWorkspaceRef = useRef(null);
216
268
  const loadedAgentWorkspaceRef = useRef(null);
@@ -220,6 +272,18 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
220
272
  const conversationEndRef = useRef(null);
221
273
  const conversationRenameRunIdRef = useRef(0);
222
274
  const draftTestRunIdRef = useRef(0);
275
+ const conversationHistoryRunIdRef = useRef(0);
276
+ const conversationMutationRevisionRef = useRef(0);
277
+ const conversationCreateRunIdRef = useRef(0);
278
+ const conversationCreateScope = [
279
+ workspaceId,
280
+ String(launchContext?.taskId || ''),
281
+ String(launchContext?.taskReference || ''),
282
+ String(launchContext?.taskTitle || ''),
283
+ ].join('\n');
284
+ const conversationCreateScopeRef = useRef(conversationCreateScope);
285
+ conversationCreateScopeRef.current = conversationCreateScope;
286
+ const conversationHistoryLoadRef = useRef(null);
223
287
  const builderTabRefs = useRef({
224
288
  agents: null,
225
289
  roles: null,
@@ -228,12 +292,26 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
228
292
  resources: null,
229
293
  });
230
294
  const [activeBuilderTab, setActiveBuilderTab] = useState('agents');
295
+ const activateBuilderTab = useCallback((tab, focus = false) => {
296
+ setActiveBuilderTab(tab);
297
+ if (focus)
298
+ window.setTimeout(() => builderTabRefs.current[tab]?.focus(), 0);
299
+ }, []);
300
+ const [connectionRefreshKey, setConnectionRefreshKey] = useState(0);
231
301
  const [roleLibraryPortalTarget, setRoleLibraryPortalTarget] = useState(null);
232
302
  const [skillLibraryPortalTarget, setSkillLibraryPortalTarget] = useState(null);
303
+ const [connectionLibraryPortalTarget, setConnectionLibraryPortalTarget] = useState(null);
233
304
  const [agents, setAgents] = useState([]);
305
+ const [availableModelOptions, setAvailableModelOptions] = useState(() => (runtimeMode === 'local'
306
+ ? TASKFORCE_AGENT_MODEL_OPTIONS.filter(isTaskforceAgentModelVisible)
307
+ : []));
234
308
  const [selectedAgentId, setSelectedAgentId] = useState(null);
235
309
  const [agentName, setAgentName] = useState('');
236
310
  const [modelProvider, setModelProvider] = useState('taskforce_hq');
311
+ const [modelSourceGroup, setModelSourceGroup] = useState('taskforce_managed');
312
+ const [modelConnections, setModelConnections] = useState([]);
313
+ const [selectedConnectionId, setSelectedConnectionId] = useState('');
314
+ const [agentConnectionBinding, setAgentConnectionBinding] = useState(null);
237
315
  const [providerKey, setProviderKey] = useState(DEFAULT_TASKFORCE_AGENT_PROVIDER_KEY);
238
316
  const [modelKey, setModelKey] = useState(DEFAULT_TASKFORCE_AGENT_MODEL_KEY);
239
317
  const [modelTier, setModelTier] = useState('balanced');
@@ -248,7 +326,6 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
248
326
  }, []);
249
327
  const [signatureColor, setSignatureColor] = useState('');
250
328
  const [signatureColorDirty, setSignatureColorDirty] = useState(false);
251
- const [signatureColorReset, setSignatureColorReset] = useState(false);
252
329
  const [prompt, setPrompt] = useState('');
253
330
  const [conversationId, setConversationId] = useState(null);
254
331
  const [conversationSummaries, setConversationSummaries] = useState([]);
@@ -259,6 +336,7 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
259
336
  const [savingConversationTitle, setSavingConversationTitle] = useState(false);
260
337
  const [result, setResult] = useState(null);
261
338
  const [toolActivity, setToolActivity] = useState([]);
339
+ const [toolCopyStatus, setToolCopyStatus] = useState(null);
262
340
  const [promptStatus, setPromptStatus] = useState('');
263
341
  const [logs, setLogs] = useState(() => [
264
342
  {
@@ -275,7 +353,11 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
275
353
  const [draftTestToolActivity, setDraftTestToolActivity] = useState([]);
276
354
  const [draftTestError, setDraftTestError] = useState('');
277
355
  const [testingDraft, setTestingDraft] = useState(false);
356
+ const [testPanelOpen, setTestPanelOpen] = useState(false);
278
357
  const [loadingAgents, setLoadingAgents] = useState(false);
358
+ const [modelCatalogError, setModelCatalogError] = useState('');
359
+ const [modelCatalogRefreshKey, setModelCatalogRefreshKey] = useState(0);
360
+ const cloudModelCatalogActiveRef = useRef(false);
279
361
  const [loadingConversationList, setLoadingConversationList] = useState(false);
280
362
  const [loadingConversation, setLoadingConversation] = useState(false);
281
363
  const [savingAgent, setSavingAgent] = useState(false);
@@ -290,9 +372,66 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
290
372
  const [agentProfileTrayOpen, setAgentProfileTrayOpen] = useState(false);
291
373
  const [avatarSyncingAgentIds, setAvatarSyncingAgentIds] = useState(() => new Set());
292
374
  const pendingCloudAvatarRevisionByAgentIdRef = useRef(new Map());
293
- const configuredProviderOptions = useMemo(() => TASKFORCE_AGENT_PROVIDER_OPTIONS.filter((provider) => modelsForTaskforceAgentProvider(provider.key).length > 0), []);
294
- const modelOptions = useMemo(() => modelsForTaskforceAgentProvider(providerKey), [providerKey]);
375
+ const bindingMutationAgentIdRef = useRef(null);
376
+ const bindingInspectionGenerationRef = useRef(0);
377
+ const configuredModelSourceOptions = useMemo(() => {
378
+ const options = AGENT_MODEL_SOURCE_OPTIONS
379
+ .filter((source) => availableModelOptions.some((model) => modelMatchesSourceGroup(model, source.key)))
380
+ .map((source) => ({ key: source.key, label: source.label }));
381
+ const activeConnections = modelConnections.filter((connection) => (connection.lifecycleStatus === 'active'
382
+ && availableModelOptions.some((model) => (model.source === 'subscription_dev'
383
+ && model.providerKey === connection.providerKey))));
384
+ for (const connection of activeConnections) {
385
+ options.push({
386
+ key: `${CONNECTION_SOURCE_PREFIX}${connection.id}`,
387
+ label: `${modelConnectionDisplayName(connection)} · ${labelForTaskforceAgentProvider(connection.providerKey)}`,
388
+ });
389
+ }
390
+ if (modelSourceGroup === 'personal_connection' && selectedConnectionId
391
+ && !activeConnections.some((connection) => connection.id === selectedConnectionId)) {
392
+ const unavailable = modelConnections.find((connection) => connection.id === selectedConnectionId);
393
+ options.push({
394
+ key: `${CONNECTION_SOURCE_PREFIX}${selectedConnectionId}`,
395
+ label: `${unavailable ? modelConnectionDisplayName(unavailable) : 'Connected account'} (unavailable)`,
396
+ disabled: true,
397
+ });
398
+ }
399
+ else if (modelSourceGroup === 'personal_connection' && !selectedConnectionId) {
400
+ options.push({
401
+ key: agentConnectionBinding?.bound && agentConnectionBinding.ownerIsCaller === false
402
+ ? 'connected-account-private'
403
+ : 'connected-account-unavailable',
404
+ label: agentConnectionBinding?.bound && agentConnectionBinding.ownerIsCaller === false
405
+ ? 'Connected account managed by another user'
406
+ : 'Connected account unavailable',
407
+ disabled: true,
408
+ });
409
+ }
410
+ return options;
411
+ }, [agentConnectionBinding?.bound, agentConnectionBinding?.ownerIsCaller, availableModelOptions, modelConnections, modelSourceGroup, selectedConnectionId]);
412
+ const modelSourceSelectionKey = modelSourceGroup === 'taskforce_managed'
413
+ ? 'taskforce_managed'
414
+ : selectedConnectionId
415
+ ? `${CONNECTION_SOURCE_PREFIX}${selectedConnectionId}`
416
+ : agentConnectionBinding?.bound && agentConnectionBinding.ownerIsCaller === false
417
+ ? 'connected-account-private'
418
+ : 'connected-account-unavailable';
419
+ const selectedModelConnection = useMemo(() => modelConnections.find((connection) => connection.id === selectedConnectionId) || null, [modelConnections, selectedConnectionId]);
420
+ const sourceModelOptions = useMemo(() => availableModelOptions.filter((model) => (modelMatchesSourceGroup(model, modelSourceGroup)
421
+ && (modelSourceGroup === 'taskforce_managed'
422
+ || !selectedModelConnection
423
+ || model.providerKey === selectedModelConnection.providerKey))), [availableModelOptions, modelSourceGroup, selectedModelConnection]);
424
+ const configuredProviderOptions = useMemo(() => TASKFORCE_AGENT_PROVIDER_OPTIONS.filter((provider) => (sourceModelOptions.some((model) => model.providerKey === provider.key)
425
+ || provider.key === providerKey)), [providerKey, sourceModelOptions]);
426
+ const modelOptions = useMemo(() => sourceModelOptions.filter((model) => model.providerKey === providerKey), [providerKey, sourceModelOptions]);
427
+ const normalizeAvailableModelKey = useCallback((value, provider) => {
428
+ const normalizedProvider = normalizeTaskforceAgentProviderKey(provider);
429
+ const providerModels = availableModelOptions.filter((model) => model.providerKey === normalizedProvider);
430
+ const key = String(value || '').trim();
431
+ return key || providerModels[0]?.key || '';
432
+ }, [availableModelOptions]);
295
433
  const selectedModel = useMemo(() => modelOptions.find((model) => model.key === modelKey) || null, [modelKey, modelOptions]);
434
+ const subscriptionConnectionsEligible = useMemo(() => (availableModelOptions.some((model) => model.source === 'subscription_dev')), [availableModelOptions]);
296
435
  const selectedAgent = useMemo(() => agents.find((agent) => agent.id === selectedAgentId) || null, [agents, selectedAgentId]);
297
436
  const normalizedRememberedAgentId = useMemo(() => String(rememberedAgentId || '').trim(), [rememberedAgentId]);
298
437
  const normalizedRememberedConversationId = useMemo(() => String(rememberedConversationId || '').trim(), [rememberedConversationId]);
@@ -313,6 +452,11 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
313
452
  const shouldUseCloudAgentRuntime = runtimeMode === 'local' && cloudAuthConfigured;
314
453
  const canUseCloudAgentRuntime = !shouldUseCloudAgentRuntime
315
454
  || (authSessionResolved && isAuthenticated);
455
+ const modelConnectionCloudAccessAvailable = runtimeMode === 'cloud'
456
+ || (cloudAuthConfigured && authSessionResolved && isAuthenticated && Boolean(resolveCloudAuthUrl));
457
+ const modelConnectionCloudAccessPending = runtimeMode === 'local'
458
+ && cloudAuthConfigured
459
+ && !authSessionResolved;
316
460
  const selectedAgentAvatarUrl = useMemo(() => getAgentAvatarUrl(selectedAgent), [selectedAgent]);
317
461
  const selectedAgentAvatarSourceUrl = useMemo(() => getAgentAvatarSourceUrl(selectedAgent), [selectedAgent]);
318
462
  const isSelectedAgentAvatarSyncing = Boolean(selectedAgentId && avatarSyncingAgentIds.has(selectedAgentId));
@@ -388,21 +532,30 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
388
532
  setTestingDraft(false);
389
533
  }, []);
390
534
  const applyAgent = useCallback((agent) => {
535
+ const switchingAgents = selectedAgentIdRef.current !== agent.id;
391
536
  conversationRenameRunIdRef.current += 1;
537
+ conversationCreateRunIdRef.current += 1;
538
+ if (switchingAgents)
539
+ bindingInspectionGenerationRef.current += 1;
392
540
  configurationDraftDirtyRef.current = false;
393
541
  selectedAgentIdRef.current = agent.id;
394
542
  setSelectedAgentId(agent.id);
395
543
  setAgentName(agent.name);
396
544
  setModelProvider('taskforce_hq');
545
+ const agentModel = availableModelOptions.find((model) => model.key === agent.modelKey);
546
+ setModelSourceGroup(modelSourceGroupForOption(agentModel || { source: agent.definition?.model.modelSource }));
547
+ if (switchingAgents) {
548
+ setSelectedConnectionId('');
549
+ setAgentConnectionBinding(null);
550
+ }
397
551
  setProviderKey(normalizeTaskforceAgentProviderKey(agent.providerKey));
398
- setModelKey(normalizeTaskforceAgentModelKey(agent.modelKey, agent.providerKey));
552
+ setModelKey(normalizeAvailableModelKey(agent.modelKey, agent.providerKey));
399
553
  setModelTier(agent.modelTier);
400
554
  setDefinitionDraft(createTaskforceAgentEditorDraft(agent.definition, {
401
555
  systemPrompt: agent.systemPrompt,
402
556
  }));
403
557
  setSignatureColor(agent.color || assignedAiProfileColor(agent.id));
404
558
  setSignatureColorDirty(false);
405
- setSignatureColorReset(false);
406
559
  signatureColorEditPendingRef.current = false;
407
560
  setConversationId(null);
408
561
  setConversationSummaries([]);
@@ -415,7 +568,7 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
415
568
  setToolActivity([]);
416
569
  setConfigFeedback(null);
417
570
  resetDraftTest();
418
- }, [resetDraftTest]);
571
+ }, [availableModelOptions, normalizeAvailableModelKey, resetDraftTest]);
419
572
  const selectAgent = useCallback((agent) => {
420
573
  applyAgent(agent);
421
574
  rememberSelection(agent.id, normalizedRememberedConversationByAgentId[agent.id] || null);
@@ -447,11 +600,13 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
447
600
  }, []);
448
601
  const resetAgentDraft = useCallback(() => {
449
602
  conversationRenameRunIdRef.current += 1;
603
+ conversationCreateRunIdRef.current += 1;
450
604
  configurationDraftDirtyRef.current = false;
451
605
  selectedAgentIdRef.current = null;
452
606
  setSelectedAgentId(null);
453
607
  setAgentName('');
454
608
  setModelProvider('taskforce_hq');
609
+ setModelSourceGroup('taskforce_managed');
455
610
  setProviderKey(DEFAULT_TASKFORCE_AGENT_PROVIDER_KEY);
456
611
  setModelKey(DEFAULT_TASKFORCE_AGENT_MODEL_KEY);
457
612
  setModelTier('balanced');
@@ -460,7 +615,6 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
460
615
  }));
461
616
  setSignatureColor('');
462
617
  setSignatureColorDirty(false);
463
- setSignatureColorReset(false);
464
618
  signatureColorEditPendingRef.current = false;
465
619
  setConversationId(null);
466
620
  setConversationSummaries([]);
@@ -510,7 +664,7 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
510
664
  window.addEventListener('beforeunload', handleBeforeUnload);
511
665
  return () => window.removeEventListener('beforeunload', handleBeforeUnload);
512
666
  }, []);
513
- const loadConversation = useCallback(async (agentId, nextConversationId) => {
667
+ const loadConversation = useCallback(async (agentId, nextConversationId, isCurrent = () => selectedAgentIdRef.current === agentId) => {
514
668
  setLoadingConversation(true);
515
669
  try {
516
670
  const res = await fetch(`/api/taskforce/agents/${encodeURIComponent(agentId)}/conversation?workspaceId=${encodeURIComponent(workspaceId)}&conversationId=${encodeURIComponent(nextConversationId)}`, {
@@ -520,6 +674,8 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
520
674
  ? await res.clone().text().catch(() => '')
521
675
  : '';
522
676
  const payload = await res.json().catch(() => ({}));
677
+ if (!isCurrent())
678
+ return;
523
679
  if (!res.ok || payload?.success === false) {
524
680
  const fallback = res.status === 404
525
681
  ? 'Taskforce agent conversation API route is not available. Restart the app server so the latest routes are active.'
@@ -527,8 +683,6 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
527
683
  appendLog('error', String(payload?.error || responseText || fallback));
528
684
  return;
529
685
  }
530
- if (selectedAgentIdRef.current !== agentId)
531
- return;
532
686
  const nextConversation = payload?.conversation || null;
533
687
  applyConversation(nextConversation);
534
688
  if (nextConversation)
@@ -537,13 +691,16 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
537
691
  setToolActivity([]);
538
692
  }
539
693
  catch (err) {
540
- appendLog('error', String(err?.message || err || 'Unable to load saved agent conversation.'));
694
+ if (isCurrent()) {
695
+ appendLog('error', String(err?.message || err || 'Unable to load saved agent conversation.'));
696
+ }
541
697
  }
542
698
  finally {
543
- setLoadingConversation(false);
699
+ if (isCurrent())
700
+ setLoadingConversation(false);
544
701
  }
545
702
  }, [appendLog, applyConversation, rememberSelection, upsertConversationSummary, workspaceId]);
546
- const loadConversationHistory = useCallback(async (agentId) => {
703
+ const loadConversationHistory = useCallback(async (agentId, isCurrent) => {
547
704
  setLoadingConversationList(true);
548
705
  setLoadingConversation(true);
549
706
  try {
@@ -554,6 +711,8 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
554
711
  ? await res.clone().text().catch(() => '')
555
712
  : '';
556
713
  const payload = await res.json().catch(() => ({}));
714
+ if (!isCurrent())
715
+ return;
557
716
  if (!res.ok || payload?.success === false) {
558
717
  const fallback = res.status === 404
559
718
  ? 'Taskforce agent conversation API route is not available. Restart the app server so the latest routes are active.'
@@ -561,8 +720,6 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
561
720
  appendLog('error', String(payload?.error || responseText || fallback));
562
721
  return;
563
722
  }
564
- if (selectedAgentIdRef.current !== agentId)
565
- return;
566
723
  const summaries = Array.isArray(payload?.conversations) ? payload.conversations : [];
567
724
  setConversationSummaries(summaries);
568
725
  if (summaries.length > 0) {
@@ -576,7 +733,7 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
576
733
  : null;
577
734
  const nextConversationId = restoredConversation?.id || taskConversation?.id || (taskLaunchContext ? null : summaries[0].id);
578
735
  if (nextConversationId) {
579
- await loadConversation(agentId, nextConversationId);
736
+ await loadConversation(agentId, nextConversationId, isCurrent);
580
737
  }
581
738
  else {
582
739
  applyConversation(null);
@@ -591,13 +748,45 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
591
748
  }
592
749
  }
593
750
  catch (err) {
594
- appendLog('error', String(err?.message || err || 'Unable to load saved agent conversations.'));
751
+ if (isCurrent()) {
752
+ appendLog('error', String(err?.message || err || 'Unable to load saved agent conversations.'));
753
+ }
595
754
  }
596
755
  finally {
597
- setLoadingConversationList(false);
598
- setLoadingConversation(false);
756
+ if (isCurrent()) {
757
+ setLoadingConversationList(false);
758
+ setLoadingConversation(false);
759
+ }
599
760
  }
600
761
  }, [appendLog, applyConversation, loadConversation, normalizedRememberedAgentId, normalizedRememberedConversationByAgentId, normalizedRememberedConversationId, rememberSelection, taskLaunchContext, workspaceId]);
762
+ const startConversationHistoryLoad = useCallback((agentId, force = false) => {
763
+ const current = conversationHistoryLoadRef.current;
764
+ if (current?.agentId === agentId
765
+ && current.loader === loadConversationHistory
766
+ && !force) {
767
+ return current.promise || Promise.resolve();
768
+ }
769
+ const historyRunId = ++conversationHistoryRunIdRef.current;
770
+ const mutationRevision = conversationMutationRevisionRef.current;
771
+ const isCurrent = () => (selectedAgentIdRef.current === agentId
772
+ && conversationHistoryRunIdRef.current === historyRunId
773
+ && conversationMutationRevisionRef.current === mutationRevision);
774
+ const promise = loadConversationHistory(agentId, isCurrent).finally(() => {
775
+ if (conversationHistoryLoadRef.current?.promise === promise) {
776
+ conversationHistoryLoadRef.current = {
777
+ agentId,
778
+ loader: loadConversationHistory,
779
+ promise: null,
780
+ };
781
+ }
782
+ });
783
+ conversationHistoryLoadRef.current = {
784
+ agentId,
785
+ loader: loadConversationHistory,
786
+ promise,
787
+ };
788
+ return promise;
789
+ }, [loadConversationHistory]);
601
790
  const loadAgents = useCallback(async (options) => {
602
791
  const quiet = options?.quiet === true;
603
792
  if (!quiet) {
@@ -623,6 +812,17 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
623
812
  return;
624
813
  }
625
814
  const nextAgents = Array.isArray(payload?.agents) ? payload.agents : [];
815
+ const cloudModelCatalogExpected = runtimeMode === 'local'
816
+ && cloudAuthConfigured
817
+ && authSessionResolved
818
+ && isAuthenticated
819
+ && Boolean(resolveCloudAuthUrl);
820
+ if (Array.isArray(payload?.modelCatalog) && !cloudModelCatalogExpected) {
821
+ setAvailableModelOptions(payload.modelCatalog);
822
+ }
823
+ else if (runtimeMode === 'cloud') {
824
+ setAvailableModelOptions([]);
825
+ }
626
826
  loadedAgentWorkspaceRef.current = workspaceId;
627
827
  setAgents((current) => {
628
828
  const currentById = new Map(current.map((agent) => [agent.id, agent]));
@@ -677,13 +877,129 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
677
877
  if (!quiet)
678
878
  setLoadingAgents(false);
679
879
  }
680
- }, [appendLog, workspaceId]);
880
+ }, [appendLog, authSessionResolved, cloudAuthConfigured, isAuthenticated, resolveCloudAuthUrl, runtimeMode, workspaceId]);
681
881
  useEffect(() => {
682
882
  resetDraftTest();
683
883
  }, [resetDraftTest, workspaceId]);
684
884
  useEffect(() => {
685
885
  void loadAgents();
686
886
  }, [loadAgents]);
887
+ useEffect(() => {
888
+ if (runtimeMode !== 'local')
889
+ return;
890
+ const localModelCatalog = TASKFORCE_AGENT_MODEL_OPTIONS.filter(isTaskforceAgentModelVisible);
891
+ if (!cloudAuthConfigured
892
+ || !authSessionResolved
893
+ || !isAuthenticated
894
+ || !resolveCloudAuthUrl) {
895
+ if (cloudModelCatalogActiveRef.current)
896
+ setAvailableModelOptions(localModelCatalog);
897
+ cloudModelCatalogActiveRef.current = false;
898
+ setModelCatalogError('');
899
+ return;
900
+ }
901
+ cloudModelCatalogActiveRef.current = true;
902
+ setAvailableModelOptions([]);
903
+ setModelCatalogError('');
904
+ let cancelled = false;
905
+ void (async () => {
906
+ try {
907
+ const response = await fetch(resolveCloudAuthUrl('/api/taskforce/agents/model-catalog'), {
908
+ credentials: 'include',
909
+ });
910
+ const payload = await response.json().catch(() => ({}));
911
+ if (!response.ok || payload?.success === false || !Array.isArray(payload?.modelCatalog)) {
912
+ throw new Error('Cloud model catalog unavailable.');
913
+ }
914
+ if (!cancelled)
915
+ setAvailableModelOptions(payload.modelCatalog);
916
+ }
917
+ catch {
918
+ if (!cancelled) {
919
+ setModelCatalogError('Unable to load models from Taskforce Cloud. Check your connection and try again.');
920
+ }
921
+ }
922
+ })();
923
+ return () => {
924
+ cancelled = true;
925
+ };
926
+ }, [authSessionResolved, cloudAuthConfigured, isAuthenticated, modelCatalogRefreshKey, resolveCloudAuthUrl, runtimeMode, workspaceId]);
927
+ useEffect(() => {
928
+ if (!subscriptionConnectionsEligible || !modelConnectionCloudAccessAvailable) {
929
+ setModelConnections([]);
930
+ return;
931
+ }
932
+ let cancelled = false;
933
+ void listModelConnections({ workspaceId, resolveApiUrl: resolveCloudAuthUrl })
934
+ .then((connections) => {
935
+ if (!cancelled)
936
+ setModelConnections(connections);
937
+ })
938
+ .catch(() => {
939
+ if (!cancelled)
940
+ setModelConnections([]);
941
+ });
942
+ return () => {
943
+ cancelled = true;
944
+ };
945
+ }, [activeBuilderTab, connectionRefreshKey, modelConnectionCloudAccessAvailable, resolveCloudAuthUrl, subscriptionConnectionsEligible, workspaceId]);
946
+ useEffect(() => {
947
+ const usesConnectedAccount = selectedAgent?.definition?.model.modelSource === 'subscription_dev';
948
+ if (!selectedAgentId || !usesConnectedAccount || !modelConnectionCloudAccessAvailable) {
949
+ setAgentConnectionBinding(null);
950
+ if (!usesConnectedAccount)
951
+ setSelectedConnectionId('');
952
+ return;
953
+ }
954
+ if (bindingMutationAgentIdRef.current === selectedAgentId)
955
+ return;
956
+ const generation = ++bindingInspectionGenerationRef.current;
957
+ let cancelled = false;
958
+ void inspectAgentConnectionBinding(selectedAgentId, { workspaceId, resolveApiUrl: resolveCloudAuthUrl })
959
+ .then((binding) => {
960
+ if (cancelled
961
+ || generation !== bindingInspectionGenerationRef.current
962
+ || bindingMutationAgentIdRef.current === selectedAgentId
963
+ || configurationDraftDirtyRef.current)
964
+ return;
965
+ setAgentConnectionBinding(binding);
966
+ setSelectedConnectionId(binding.ownerIsCaller ? String(binding.connectionId || '') : '');
967
+ })
968
+ .catch(() => {
969
+ if (cancelled
970
+ || generation !== bindingInspectionGenerationRef.current
971
+ || bindingMutationAgentIdRef.current === selectedAgentId
972
+ || configurationDraftDirtyRef.current)
973
+ return;
974
+ setAgentConnectionBinding(null);
975
+ setSelectedConnectionId('');
976
+ });
977
+ return () => {
978
+ cancelled = true;
979
+ };
980
+ }, [modelConnectionCloudAccessAvailable, resolveCloudAuthUrl, selectedAgent?.definition?.model.modelSource, selectedAgentId, workspaceId]);
981
+ useEffect(() => {
982
+ if (availableModelOptions.length === 0)
983
+ return;
984
+ const currentModel = availableModelOptions.find((model) => model.key === modelKey);
985
+ if (currentModel) {
986
+ const currentSourceGroup = modelSourceGroupForOption(currentModel);
987
+ if (currentSourceGroup !== modelSourceGroup)
988
+ setModelSourceGroup(currentSourceGroup);
989
+ if (currentModel.providerKey !== providerKey)
990
+ setProviderKey(currentModel.providerKey);
991
+ return;
992
+ }
993
+ if (selectedAgentId && modelKey)
994
+ return;
995
+ const nextModel = sourceModelOptions.find((model) => model.providerKey === providerKey)
996
+ || sourceModelOptions[0]
997
+ || availableModelOptions[0];
998
+ setModelSourceGroup(modelSourceGroupForOption(nextModel));
999
+ setProviderKey(nextModel.providerKey);
1000
+ setModelKey(nextModel.key);
1001
+ setModelTier(nextModel.defaultTier);
1002
+ }, [availableModelOptions, modelKey, modelSourceGroup, providerKey, selectedAgentId, sourceModelOptions]);
687
1003
  useEffect(() => {
688
1004
  if (!rememberedSelectionReady
689
1005
  || loadedAgentWorkspaceRef.current !== workspaceId
@@ -720,12 +1036,12 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
720
1036
  const eventAgentId = String(detail?.agentId || '').trim();
721
1037
  const activeAgentId = selectedAgentIdRef.current;
722
1038
  if (activeAgentId && (!eventAgentId || eventAgentId === activeAgentId)) {
723
- void loadConversationHistory(activeAgentId);
1039
+ void startConversationHistoryLoad(activeAgentId, true);
724
1040
  }
725
1041
  };
726
1042
  window.addEventListener(TASKFORCE_AGENTS_MUTATED_EVENT, handleAgentsMutated);
727
1043
  return () => window.removeEventListener(TASKFORCE_AGENTS_MUTATED_EVENT, handleAgentsMutated);
728
- }, [loadAgents, loadConversationHistory, shouldUseCloudAvatarGeneration, workspaceId]);
1044
+ }, [loadAgents, shouldUseCloudAvatarGeneration, startConversationHistoryLoad, workspaceId]);
729
1045
  useEffect(() => {
730
1046
  const handleAiProfilesMutated = (event) => {
731
1047
  const detail = event.detail;
@@ -768,8 +1084,8 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
768
1084
  useEffect(() => {
769
1085
  if (!selectedAgentId)
770
1086
  return;
771
- void loadConversationHistory(selectedAgentId);
772
- }, [loadConversationHistory, selectedAgentId]);
1087
+ void startConversationHistoryLoad(selectedAgentId);
1088
+ }, [selectedAgentId, startConversationHistoryLoad]);
773
1089
  useEffect(() => {
774
1090
  if (avatarSyncingAgentIds.size === 0)
775
1091
  return;
@@ -817,15 +1133,26 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
817
1133
  providerKey,
818
1134
  modelKey,
819
1135
  tier: selectedModel?.defaultTier || modelTier,
1136
+ connectionScope: selectedModel?.source === 'subscription_dev'
1137
+ ? (modelConnectionCloudAccessAvailable ? 'personal' : 'local_dev')
1138
+ : undefined,
820
1139
  })), [
821
1140
  agentName,
822
1141
  definitionDraft,
823
1142
  modelKey,
824
1143
  modelTier,
1144
+ modelConnectionCloudAccessAvailable,
825
1145
  providerKey,
826
1146
  selectedAgent?.definition,
827
1147
  selectedModel?.defaultTier,
1148
+ selectedModel?.source,
828
1149
  ]);
1150
+ const handleConnectionActivated = useCallback(async (_connection) => {
1151
+ setConnectionRefreshKey((current) => current + 1);
1152
+ }, []);
1153
+ const handleConnectionUnavailable = useCallback((connectionId) => {
1154
+ setModelConnections((current) => current.filter((connection) => connection.id !== connectionId));
1155
+ }, []);
829
1156
  const handleSaveAgent = async () => {
830
1157
  const nextAgentName = agentName.trim();
831
1158
  const nextPurpose = definitionDraft.purpose.trim();
@@ -833,6 +1160,17 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
833
1160
  return;
834
1161
  const isNewAgent = !selectedAgentId;
835
1162
  const definition = buildCurrentDraftDefinition();
1163
+ const usesConnectedAccount = definition.model.modelSource === 'subscription_dev';
1164
+ const preservesPrivateAssignment = Boolean(selectedAgentId
1165
+ && agentConnectionBinding?.bound
1166
+ && agentConnectionBinding.ownerIsCaller === false
1167
+ && selectedAgent?.definition?.model.modelSource === 'subscription_dev'
1168
+ && selectedAgent.providerKey === definition.model.providerKey);
1169
+ const connectionIdForSave = selectedConnectionId;
1170
+ if (usesConnectedAccount && !connectionIdForSave && !preservesPrivateAssignment) {
1171
+ setConfigFeedback({ type: 'error', message: 'Select a connected model account before saving this Agent.' });
1172
+ return;
1173
+ }
836
1174
  setSavingAgent(true);
837
1175
  setConfigFeedback(null);
838
1176
  try {
@@ -852,7 +1190,6 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
852
1190
  expectedDefinitionHash: selectedAgent?.definitionHash,
853
1191
  } : {}),
854
1192
  ...(signatureColorDirty && signatureColor ? { signatureColor } : {}),
855
- ...(signatureColorReset ? { resetSignatureColor: true } : {}),
856
1193
  }),
857
1194
  });
858
1195
  const payload = await res.json().catch(() => ({}));
@@ -879,19 +1216,66 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
879
1216
  setAgentName(savedAgent.name);
880
1217
  setModelProvider('taskforce_hq');
881
1218
  setProviderKey(normalizeTaskforceAgentProviderKey(savedAgent.providerKey));
882
- setModelKey(normalizeTaskforceAgentModelKey(savedAgent.modelKey, savedAgent.providerKey));
1219
+ setModelKey(normalizeAvailableModelKey(savedAgent.modelKey, savedAgent.providerKey));
883
1220
  setModelTier(savedAgent.modelTier);
884
1221
  setDefinitionDraft(createTaskforceAgentEditorDraft(savedAgent.definition, {
885
1222
  systemPrompt: savedAgent.systemPrompt,
886
1223
  }));
887
1224
  setSignatureColor(savedAgent.color || assignedAiProfileColor(savedAgent.id));
888
1225
  setSignatureColorDirty(false);
889
- setSignatureColorReset(false);
890
1226
  signatureColorEditPendingRef.current = false;
891
1227
  }
892
- const message = `Saved ${savedAgent.name}.`;
893
- appendLog('success', message);
894
- setConfigFeedback({ type: 'success', message });
1228
+ let assignmentError = '';
1229
+ if (usesConnectedAccount && connectionIdForSave) {
1230
+ const mutationGeneration = ++bindingInspectionGenerationRef.current;
1231
+ bindingMutationAgentIdRef.current = savedAgent.id;
1232
+ try {
1233
+ const binding = await bindAgentConnectionAfterLocalSync(savedAgent.id, connectionIdForSave, { workspaceId, resolveApiUrl: resolveCloudAuthUrl }, runtimeMode === 'local');
1234
+ if (selectedAgentIdRef.current === savedAgent.id
1235
+ && bindingInspectionGenerationRef.current === mutationGeneration) {
1236
+ setAgentConnectionBinding(binding);
1237
+ setSelectedConnectionId(connectionIdForSave);
1238
+ }
1239
+ }
1240
+ catch (error) {
1241
+ assignmentError = error.message || 'The selected model account could not be assigned.';
1242
+ }
1243
+ finally {
1244
+ if (bindingMutationAgentIdRef.current === savedAgent.id) {
1245
+ bindingMutationAgentIdRef.current = null;
1246
+ }
1247
+ }
1248
+ }
1249
+ else if (!usesConnectedAccount && agentConnectionBinding?.bound && agentConnectionBinding.ownerIsCaller) {
1250
+ const mutationGeneration = ++bindingInspectionGenerationRef.current;
1251
+ bindingMutationAgentIdRef.current = savedAgent.id;
1252
+ try {
1253
+ await clearAgentConnectionBinding(savedAgent.id, {
1254
+ workspaceId,
1255
+ resolveApiUrl: resolveCloudAuthUrl,
1256
+ });
1257
+ if (selectedAgentIdRef.current === savedAgent.id
1258
+ && bindingInspectionGenerationRef.current === mutationGeneration) {
1259
+ setAgentConnectionBinding(null);
1260
+ setSelectedConnectionId('');
1261
+ }
1262
+ }
1263
+ catch {
1264
+ // The stale binding cannot authorize managed-model execution and can be cleaned up later.
1265
+ }
1266
+ finally {
1267
+ if (bindingMutationAgentIdRef.current === savedAgent.id) {
1268
+ bindingMutationAgentIdRef.current = null;
1269
+ }
1270
+ }
1271
+ }
1272
+ const message = assignmentError
1273
+ ? `Saved ${savedAgent.name}, but the model account could not be assigned. ${assignmentError}`
1274
+ : `Saved ${savedAgent.name}.`;
1275
+ appendLog(assignmentError ? 'error' : 'success', message);
1276
+ if (selectedAgentIdRef.current === savedAgent.id) {
1277
+ setConfigFeedback({ type: assignmentError ? 'error' : 'success', message });
1278
+ }
895
1279
  dispatchTaskforceAgentsMutated({
896
1280
  workspaceId,
897
1281
  agentId: savedAgent.id,
@@ -1219,17 +1603,38 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1219
1603
  }
1220
1604
  };
1221
1605
  const handleNewAgentDraft = () => requestAgentNavigation({ type: 'new' });
1606
+ const handleModelSourceChange = (nextSourceKey) => {
1607
+ configurationDraftDirtyRef.current = true;
1608
+ const nextConnectionId = nextSourceKey.startsWith(CONNECTION_SOURCE_PREFIX)
1609
+ ? nextSourceKey.slice(CONNECTION_SOURCE_PREFIX.length)
1610
+ : '';
1611
+ const nextSourceGroup = nextConnectionId
1612
+ ? 'personal_connection'
1613
+ : 'taskforce_managed';
1614
+ const nextConnection = nextConnectionId
1615
+ ? modelConnections.find((connection) => connection.id === nextConnectionId) || null
1616
+ : null;
1617
+ const nextModels = availableModelOptions.filter((model) => modelMatchesSourceGroup(model, nextSourceGroup));
1618
+ const nextModel = nextModels.find((model) => !nextConnection || model.providerKey === nextConnection.providerKey) || null;
1619
+ setModelSourceGroup(nextSourceGroup);
1620
+ setSelectedConnectionId(nextConnectionId);
1621
+ if (!nextModel)
1622
+ return;
1623
+ setProviderKey(nextModel.providerKey);
1624
+ setModelKey(nextModel.key);
1625
+ setModelTier(nextModel.defaultTier);
1626
+ };
1222
1627
  const handleProviderChange = (nextProviderKeyRaw) => {
1223
1628
  configurationDraftDirtyRef.current = true;
1224
1629
  const nextProviderKey = normalizeTaskforceAgentProviderKey(nextProviderKeyRaw);
1225
- const nextModels = modelsForTaskforceAgentProvider(nextProviderKey);
1630
+ const nextModels = sourceModelOptions.filter((model) => model.providerKey === nextProviderKey);
1226
1631
  setProviderKey(nextProviderKey);
1227
1632
  setModelKey(nextModels[0]?.key || '');
1228
1633
  setModelTier(nextModels[0]?.defaultTier || 'balanced');
1229
1634
  };
1230
1635
  const handleModelChange = (nextModelKeyRaw) => {
1231
1636
  configurationDraftDirtyRef.current = true;
1232
- const nextModelKey = normalizeTaskforceAgentModelKey(nextModelKeyRaw, providerKey);
1637
+ const nextModelKey = normalizeAvailableModelKey(nextModelKeyRaw, providerKey);
1233
1638
  const nextModel = modelOptions.find((model) => model.key === nextModelKey);
1234
1639
  setModelKey(nextModelKey);
1235
1640
  if (nextModel)
@@ -1316,10 +1721,20 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1316
1721
  appendLog('info', 'Started a new unsaved chat.');
1317
1722
  return;
1318
1723
  }
1724
+ const targetAgentId = selectedAgentId;
1725
+ const targetCreateScope = conversationCreateScope;
1726
+ const createRunId = ++conversationCreateRunIdRef.current;
1727
+ const isCurrentCreate = () => (selectedAgentIdRef.current === targetAgentId
1728
+ && conversationCreateRunIdRef.current === createRunId
1729
+ && conversationCreateScopeRef.current === targetCreateScope);
1730
+ await startConversationHistoryLoad(targetAgentId);
1731
+ if (!isCurrentCreate())
1732
+ return;
1733
+ const mutationRevision = ++conversationMutationRevisionRef.current;
1319
1734
  setLoadingConversation(true);
1320
1735
  setToolActivity([]);
1321
1736
  try {
1322
- const res = await fetch(`/api/taskforce/agents/${encodeURIComponent(selectedAgentId)}/conversation/new`, {
1737
+ const res = await fetch(`/api/taskforce/agents/${encodeURIComponent(targetAgentId)}/conversation/new`, {
1323
1738
  method: 'POST',
1324
1739
  headers: { 'Content-Type': 'application/json' },
1325
1740
  credentials: 'include',
@@ -1333,29 +1748,37 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1333
1748
  }),
1334
1749
  });
1335
1750
  const payload = await res.json().catch(() => ({}));
1751
+ if (!isCurrentCreate()
1752
+ || conversationMutationRevisionRef.current !== mutationRevision)
1753
+ return;
1336
1754
  if (!res.ok || payload?.success === false) {
1337
1755
  appendLog('error', String(payload?.error || 'Unable to start a new chat.'));
1338
1756
  return;
1339
1757
  }
1340
1758
  setPrompt('');
1341
1759
  const nextConversation = payload?.conversation || null;
1760
+ conversationMutationRevisionRef.current += 1;
1342
1761
  applyConversation(nextConversation);
1343
- rememberSelection(selectedAgentId, nextConversation?.id || null);
1762
+ rememberSelection(targetAgentId, nextConversation?.id || null);
1344
1763
  if (nextConversation)
1345
1764
  upsertConversationSummary(nextConversation);
1346
1765
  dispatchTaskforceAgentsMutated({
1347
1766
  workspaceId,
1348
- agentId: selectedAgentId,
1767
+ agentId: targetAgentId,
1349
1768
  conversationId: payload?.conversation?.id || null,
1350
1769
  reason: 'conversation-create',
1351
1770
  });
1352
1771
  appendLog('info', 'Started a new saved chat.');
1353
1772
  }
1354
1773
  catch (err) {
1355
- appendLog('error', String(err?.message || err || 'Unable to start a new chat.'));
1774
+ if (isCurrentCreate()) {
1775
+ appendLog('error', String(err?.message || err || 'Unable to start a new chat.'));
1776
+ }
1356
1777
  }
1357
1778
  finally {
1358
- setLoadingConversation(false);
1779
+ if (isCurrentCreate()) {
1780
+ setLoadingConversation(false);
1781
+ }
1359
1782
  }
1360
1783
  };
1361
1784
  const handleDraftTest = async () => {
@@ -1370,6 +1793,11 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1370
1793
  : 'Cloud sign-in is still loading. Try again in a moment.');
1371
1794
  return;
1372
1795
  }
1796
+ if (draftDefinition.model.modelSource === 'subscription_dev'
1797
+ && (!selectedModelConnection || selectedModelConnection.lifecycleStatus !== 'active')) {
1798
+ setDraftTestError('Select an active connected model account before testing this Agent.');
1799
+ return;
1800
+ }
1373
1801
  setTestingDraft(true);
1374
1802
  const runId = ++draftTestRunIdRef.current;
1375
1803
  setDraftTestResult(null);
@@ -1389,6 +1817,9 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1389
1817
  prompt: nextPrompt,
1390
1818
  messages: [{ role: 'user', content: nextPrompt }],
1391
1819
  draftDefinition,
1820
+ ...(draftDefinition.model.modelSource === 'subscription_dev'
1821
+ ? { connectionId: selectedConnectionId }
1822
+ : {}),
1392
1823
  }),
1393
1824
  });
1394
1825
  const payload = await res.json().catch(() => ({}));
@@ -1433,6 +1864,12 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1433
1864
  setTestingDraft(false);
1434
1865
  }
1435
1866
  };
1867
+ const handleDraftTestKeyDown = (event) => {
1868
+ if (event.key !== 'Enter' || event.shiftKey || event.nativeEvent.isComposing)
1869
+ return;
1870
+ event.preventDefault();
1871
+ void handleDraftTest();
1872
+ };
1436
1873
  const handleTestModelGateway = async () => {
1437
1874
  const nextPrompt = prompt.trim();
1438
1875
  const nextSystemPrompt = systemPrompt.trim();
@@ -1460,35 +1897,49 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1460
1897
  setToolActivity([]);
1461
1898
  appendLog('info', `Started test prompt for ${nextAgentName}.`);
1462
1899
  try {
1463
- const res = await fetch('/api/taskforce/agents/model-gateway/test', {
1464
- method: 'POST',
1465
- headers: { 'Content-Type': 'application/json' },
1466
- credentials: 'include',
1467
- body: JSON.stringify({
1468
- workspaceId,
1469
- agentId: selectedAgentId,
1470
- conversationId,
1471
- agentName: nextAgentName,
1472
- providerKey,
1473
- modelKey,
1474
- prompt: nextPrompt,
1475
- messages: nextMessages,
1476
- system: nextSystemPrompt,
1477
- tier: selectedModel?.defaultTier || modelTier,
1478
- ...(activeTaskContext ? {
1479
- taskId: activeTaskContext.taskId,
1480
- taskReference: activeTaskContext.taskReference,
1481
- taskTitle: activeTaskContext.taskTitle,
1482
- } : {}),
1483
- }),
1484
- });
1485
- const payload = await res.json().catch(() => ({}));
1900
+ let res;
1901
+ let payload;
1902
+ let syncAttempt = 0;
1903
+ for (;;) {
1904
+ res = await fetch('/api/taskforce/agents/model-gateway/test', {
1905
+ method: 'POST',
1906
+ headers: { 'Content-Type': 'application/json' },
1907
+ credentials: 'include',
1908
+ body: JSON.stringify({
1909
+ workspaceId,
1910
+ agentId: selectedAgentId,
1911
+ conversationId,
1912
+ agentName: nextAgentName,
1913
+ providerKey,
1914
+ modelKey,
1915
+ prompt: nextPrompt,
1916
+ messages: nextMessages,
1917
+ system: nextSystemPrompt,
1918
+ tier: selectedModel?.defaultTier || modelTier,
1919
+ ...(activeTaskContext ? {
1920
+ taskId: activeTaskContext.taskId,
1921
+ taskReference: activeTaskContext.taskReference,
1922
+ taskTitle: activeTaskContext.taskTitle,
1923
+ } : {}),
1924
+ }),
1925
+ });
1926
+ payload = await res.json().catch(() => ({}));
1927
+ const awaitingCloudSync = runtimeMode === 'local'
1928
+ && payload?.code === 'AGENT_NOT_AVAILABLE_IN_CLOUD'
1929
+ && syncAttempt < LOCAL_AGENT_BINDING_SYNC_RETRIES;
1930
+ if (!awaitingCloudSync)
1931
+ break;
1932
+ syncAttempt += 1;
1933
+ setPromptStatus('Waiting for this Agent to synchronize with Taskforce Cloud...');
1934
+ await new Promise((resolve) => window.setTimeout(resolve, LOCAL_AGENT_BINDING_SYNC_DELAY_MS));
1935
+ }
1486
1936
  if (!res.ok || payload?.success === false) {
1487
1937
  const message = String(payload?.error || 'Model gateway test failed.');
1488
1938
  setPromptStatus(message);
1489
1939
  appendLog('error', message);
1490
1940
  return;
1491
1941
  }
1942
+ setPromptStatus('');
1492
1943
  const nextResult = payload?.result || null;
1493
1944
  setResult(nextResult);
1494
1945
  const mcpToolCalls = Array.isArray(payload?.mcpToolCalls)
@@ -1521,6 +1972,7 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1521
1972
  nextToolActivity.forEach((toolCall) => {
1522
1973
  if ((toolCall.name === 'add_comment'
1523
1974
  || toolCall.name === 'add_task_checklist_item'
1975
+ || toolCall.name === 'update_task_checklist_item'
1524
1976
  || toolCall.name === 'replace_task_checklist'
1525
1977
  || toolCall.name === 'complete_workflow_step') && toolCall.ok) {
1526
1978
  dispatchTaskforceTasksMutated({
@@ -1539,6 +1991,9 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1539
1991
  ...(toolCall.name === 'complete_workflow_step' && installedWorkflowTask
1540
1992
  ? { authoritativeTask: installedWorkflowTask }
1541
1993
  : {}),
1994
+ ...(toolCall.name === 'complete_workflow_step' && workflowCompletion?.assignmentSnapshot
1995
+ ? { authoritativeWorkflowAssignment: workflowCompletion.assignmentSnapshot }
1996
+ : {}),
1542
1997
  });
1543
1998
  }
1544
1999
  });
@@ -1594,7 +2049,27 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1594
2049
  return;
1595
2050
  void loadConversation(selectedAgentId, nextConversationId);
1596
2051
  };
1597
- const renderToolActivityList = (items) => (_jsx("div", { className: styles.taskforceAgentToolActivityList, children: items.map((toolCall) => (_jsxs("details", { className: `${styles.taskforceAgentToolCall} ${toolCall.ok ? '' : styles.taskforceAgentToolCallFailed}`.trim(), children: [_jsxs("summary", { children: [toolCall.ok ? _jsx(CheckCircle, { size: 13 }) : _jsx(AlertTriangle, { size: 13 }), _jsx("span", { className: styles.taskforceAgentToolCallName, children: toolCall.name }), _jsx("span", { className: styles.taskforceAgentToolCallStatus, children: toolCall.ok ? 'ok' : 'failed' })] }), _jsxs("div", { className: styles.taskforceAgentToolDetailsGrid, children: [_jsx("span", { children: "Arguments" }), _jsx("pre", { children: formatToolLogJson(toolCall.arguments) }), _jsx("span", { children: "Result" }), _jsx("pre", { children: formatToolLogResult(toolCall.resultText) })] })] }, toolCall.id))) }));
2052
+ const handleCopyToolActivity = async (toolCall) => {
2053
+ if (!navigator.clipboard?.writeText) {
2054
+ setToolCopyStatus({ id: toolCall.id, status: 'failed' });
2055
+ return;
2056
+ }
2057
+ try {
2058
+ await navigator.clipboard.writeText(formatToolActivityClipboard(toolCall));
2059
+ setToolCopyStatus({ id: toolCall.id, status: 'copied' });
2060
+ window.setTimeout(() => {
2061
+ setToolCopyStatus((current) => current?.id === toolCall.id ? null : current);
2062
+ }, 1600);
2063
+ }
2064
+ catch {
2065
+ setToolCopyStatus({ id: toolCall.id, status: 'failed' });
2066
+ }
2067
+ };
2068
+ const renderToolActivityList = (items) => (_jsx("div", { className: styles.taskforceAgentToolActivityList, children: items.map((toolCall) => (_jsxs("details", { className: `${styles.taskforceAgentToolCall} ${toolCall.ok ? '' : styles.taskforceAgentToolCallFailed}`.trim(), children: [_jsxs("summary", { children: [toolCall.ok ? _jsx(CheckCircle, { size: 13 }) : _jsx(AlertTriangle, { size: 13 }), _jsx("span", { className: styles.taskforceAgentToolCallName, children: toolCall.name }), _jsx("span", { className: styles.taskforceAgentToolCallStatus, children: toolCall.ok ? 'ok' : 'failed' })] }), _jsxs("div", { className: styles.taskforceAgentToolDetailsGrid, children: [_jsxs("button", { type: "button", className: styles.taskforceAgentToolCopyButton, onClick: () => { void handleCopyToolActivity(toolCall); }, "aria-label": `Copy ${toolCall.name} tool call details`, title: "Copy diagnostic details, including the full result up to 20,000 characters", children: [toolCopyStatus?.id === toolCall.id && toolCopyStatus.status === 'copied'
2069
+ ? _jsx(Check, { size: 13 })
2070
+ : _jsx(Copy, { size: 13 }), _jsx("span", { "aria-live": "polite", children: toolCopyStatus?.id === toolCall.id
2071
+ ? (toolCopyStatus.status === 'copied' ? 'Copied' : 'Copy failed')
2072
+ : 'Copy' })] }), _jsx("span", { children: "Arguments" }), _jsx("pre", { children: formatToolLogJson(toolCall.arguments) }), _jsx("span", { children: "Result preview" }), _jsx("pre", { children: formatToolLogResult(toolCall.resultText) })] })] }, toolCall.id))) }));
1598
2073
  const renderToolActivityDetails = (items, label = 'Tool Activity') => (_jsxs("details", { className: styles.taskforceAgentToolActivity, children: [_jsxs("summary", { children: [_jsx("span", { children: label }), _jsx("strong", { children: formatToolActivitySummary(items) })] }), renderToolActivityList(items)] }));
1599
2074
  const activeLibrary = activeBuilderTab === 'agents'
1600
2075
  ? { icon: _jsx(Bot, { size: 14 }), title: 'Agent Roster', empty: 'No agents configured yet.' }
@@ -1607,7 +2082,9 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1607
2082
  : { icon: _jsx(Database, { size: 14 }), title: 'Resource Library', empty: 'Resource Collections are not available yet.' };
1608
2083
  const builderTabs = (_jsxs("div", { className: styles.taskforceAgentBuilderNavigation, children: [_jsx("div", { className: styles.taskforceAgentBuilderTabs, role: "tablist", "aria-label": "Taskforce Agent builders", children: AGENT_BUILDER_TABS.map((tab, index) => {
1609
2084
  const active = activeBuilderTab === tab.id;
1610
- return (_jsx("button", { ref: (element) => { builderTabRefs.current[tab.id] = element; }, type: "button", className: `${styles.taskforceAgentBuilderTab} ${active ? styles.taskforceAgentBuilderTabActive : ''}`.trim(), role: "tab", "aria-selected": active, "aria-controls": `taskforce-agent-builder-${tab.id}`, id: `taskforce-agent-builder-tab-${tab.id}`, tabIndex: active ? 0 : -1, onClick: () => setActiveBuilderTab(tab.id), onKeyDown: (event) => {
2085
+ return (_jsx("button", { ref: (element) => { builderTabRefs.current[tab.id] = element; }, type: "button", className: `${styles.taskforceAgentBuilderTab} ${active ? styles.taskforceAgentBuilderTabActive : ''}`.trim(), role: "tab", "aria-selected": active, "aria-controls": `taskforce-agent-builder-${tab.id}`, id: `taskforce-agent-builder-tab-${tab.id}`, tabIndex: active ? 0 : -1, onClick: () => {
2086
+ setActiveBuilderTab(tab.id);
2087
+ }, onKeyDown: (event) => {
1611
2088
  let nextIndex = index;
1612
2089
  if (event.key === 'ArrowRight')
1613
2090
  nextIndex = (index + 1) % AGENT_BUILDER_TABS.length;
@@ -1625,7 +2102,8 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1625
2102
  builderTabRefs.current[nextTab.id]?.focus();
1626
2103
  }, children: tab.label }, tab.id));
1627
2104
  }) }), activeBuilderTab === 'agents' ? (_jsxs("button", { type: "button", className: `${styles.secondaryHeaderBtn} ${styles.taskforceAgentBuilderCreateBtn}`.trim(), onClick: handleNewAgentDraft, children: [_jsx(Plus, { size: 14 }), "New Agent"] })) : null] }));
1628
- const libraryTray = (_jsxs("aside", { className: `${styles.docTrayPanel} ${agentRosterTrayOpen ? styles.docTrayPanelOpen : ''}`.trim(), "aria-hidden": !agentRosterTrayOpen, "aria-label": activeLibrary.title, children: [_jsxs("div", { className: styles.taskforceAgentRosterHeader, children: [_jsxs("span", { className: styles.taskforceAgentRosterTitle, children: [activeLibrary.icon, activeLibrary.title] }), activeBuilderTab === 'agents' && loadingAgents && _jsx(Loader2, { size: 14, className: styles.spinner }), onCloseAgentRosterTray ? (_jsx("button", { type: "button", className: "tf-control-icon", onClick: onCloseAgentRosterTray, title: `Collapse ${activeLibrary.title.toLowerCase()}`, "aria-label": `Collapse ${activeLibrary.title.toLowerCase()}`, children: _jsx(ChevronLeft, { size: 16 }) })) : null] }), _jsx("div", { className: `${styles.taskforceAgentRosterContent} tf-scrollbar tf-tray-scroll-viewport`, children: activeBuilderTab === 'roles' ? (_jsx("div", { ref: setRoleLibraryPortalTarget, className: styles.taskforceAgentLibraryPortal })) : activeBuilderTab === 'skills' ? (_jsx("div", { ref: setSkillLibraryPortalTarget, className: styles.taskforceAgentLibraryPortal })) : activeBuilderTab === 'connections' ? (_jsx(FutureLibrarySummary, { kind: "connections" })) : activeBuilderTab === 'resources' ? (_jsx(FutureLibrarySummary, { kind: "resources" })) : activeBuilderTab === 'agents' && agents.length > 0 ? (_jsx("div", { className: styles.taskforceAgentList, children: agents.map((agent) => {
2105
+ const libraryTrayVisible = agentRosterTrayOpen;
2106
+ const libraryTray = (_jsxs("aside", { className: `${styles.docTrayPanel} ${libraryTrayVisible ? styles.docTrayPanelOpen : ''}`.trim(), "aria-hidden": !libraryTrayVisible, "aria-label": activeLibrary.title, children: [_jsxs("div", { className: styles.taskforceAgentRosterHeader, children: [_jsxs("span", { className: styles.taskforceAgentRosterTitle, children: [activeLibrary.icon, activeLibrary.title] }), activeBuilderTab === 'agents' && loadingAgents && _jsx(Loader2, { size: 14, className: styles.spinner }), onCloseAgentRosterTray ? (_jsx("button", { type: "button", className: "tf-control-icon", onClick: onCloseAgentRosterTray, title: `Collapse ${activeLibrary.title.toLowerCase()}`, "aria-label": `Collapse ${activeLibrary.title.toLowerCase()}`, children: _jsx(ChevronLeft, { size: 16 }) })) : null] }), _jsx("div", { className: `${styles.taskforceAgentRosterContent} tf-scrollbar tf-tray-scroll-viewport`, children: activeBuilderTab === 'roles' ? (_jsx("div", { ref: setRoleLibraryPortalTarget, className: styles.taskforceAgentLibraryPortal })) : activeBuilderTab === 'skills' ? (_jsx("div", { ref: setSkillLibraryPortalTarget, className: styles.taskforceAgentLibraryPortal })) : activeBuilderTab === 'connections' ? (_jsx("div", { ref: setConnectionLibraryPortalTarget, className: styles.taskforceAgentLibraryPortal })) : activeBuilderTab === 'resources' ? (_jsx(FutureLibrarySummary, { kind: "resources" })) : activeBuilderTab === 'agents' && agents.length > 0 ? (_jsx("div", { className: styles.taskforceAgentList, children: agents.map((agent) => {
1629
2107
  const avatarUrl = getAgentAvatarUrl(agent);
1630
2108
  const color = agent.color || assignedAiProfileColor(agent.id);
1631
2109
  return (_jsx(AiIdentityRosterCard, { name: agent.name, username: agent.username, subtitle: "Taskforce Agent", avatar: avatarUrl
@@ -1663,57 +2141,33 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1663
2141
  setPromptStatus('');
1664
2142
  }, onKeyDown: handlePromptKeyDown, disabled: testing || loadingConversation || !selectedAgentId, placeholder: testing ? 'Waiting for response...' : TEST_PROMPT_PLACEHOLDER, maxLength: 4000 })] }), _jsxs("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: () => { void handleTestModelGateway(); }, disabled: testing || loadingConversation || !prompt.trim() || !modelKey || !selectedAgentId, children: [testing ? _jsx(Loader2, { size: 14, className: styles.spinner }) : _jsx(Send, { size: 14 }), "Send"] }), promptStatus ? (_jsx("div", { className: styles.taskforceAgentDrawerPromptStatus, role: "status", children: promptStatus })) : null] }), _jsxs("div", { className: styles.taskforceAgentDrawerMetrics, "aria-label": "Response metrics", children: [_jsxs("span", { children: [_jsx("strong", { children: "Usage" }), formatModelGatewayUsage(result)] }), _jsxs("span", { children: [_jsx("strong", { children: "Latency" }), formatModelGatewayLatency(result)] })] })] }));
1665
2143
  }
1666
- return (_jsxs("div", { className: `${styles.standalonePage} ${styles.standaloneContent} ${styles.taskforceAgentsModuleRoot} ${agentRosterTrayOpen ? styles.taskforceAgentsModuleTrayOpen : ''}`.trim(), children: [libraryTray, _jsxs("div", { className: `${styles.taskforceAgentsModuleMain} tf-scrollbar`, children: [_jsxs("div", { className: styles.settingsContent, style: { maxWidth: 1180, margin: '0 auto', width: '100%' }, children: [builderTabs, _jsx("div", { className: `${styles.settingGroup} ${styles.taskforceAgentBuilderPanel}`, hidden: activeBuilderTab !== 'agents', children: _jsxs("div", { className: styles.taskforceAgentTestGrid, role: "tabpanel", id: "taskforce-agent-builder-agents", "aria-labelledby": "taskforce-agent-builder-tab-agents", children: [_jsxs("div", { className: styles.settingGroup, children: [_jsxs("div", { className: styles.taskforceAgentAvatarPanel, children: [_jsxs("div", { className: styles.taskforceAgentAvatarEditControl, children: [_jsx(EditableAvatarButton, { label: selectedAgent ? `Edit ${selectedAgent.name} photo` : 'Photo editing is unavailable until agent creation', imageUrl: selectedAgentAvatarUrl, fallback: _jsx(Bot, { size: 28 }), accentColor: signatureColor || selectedAgent?.color, size: 88, editBadgeSize: 28, editIconSize: 14, disabled: !selectedAgentId, loading: Boolean(selectedAgentId && generatingAvatarAgentIds.has(selectedAgentId)), loadingLabel: `Generating ${selectedAgent?.name || 'agent'} photo`, error: Boolean(selectedAgentGenerationFeedback?.error), errorLabel: selectedAgentGenerationFeedback?.error || 'Agent photo generation failed', onClick: () => {
2144
+ return (_jsxs("div", { className: `${styles.standalonePage} ${styles.standaloneContent} ${styles.taskforceAgentsModuleRoot} ${libraryTrayVisible ? styles.taskforceAgentsModuleTrayOpen : ''}`.trim(), children: [libraryTray, _jsxs("div", { className: `${styles.taskforceAgentsModuleMain} tf-scrollbar`, children: [_jsxs("div", { className: styles.settingsContent, style: { maxWidth: 1180, margin: '0 auto', width: '100%' }, children: [builderTabs, _jsx("div", { className: `${styles.settingGroup} ${styles.taskforceAgentBuilderPanel}`, hidden: activeBuilderTab !== 'agents', children: _jsxs("div", { className: styles.taskforceAgentTestGrid, role: "tabpanel", id: "taskforce-agent-builder-agents", "aria-labelledby": "taskforce-agent-builder-tab-agents", children: [_jsxs("div", { className: styles.settingGroup, children: [_jsxs("div", { className: styles.taskforceAgentAvatarPanel, children: [_jsxs("div", { className: styles.taskforceAgentAvatarEditControl, children: [_jsx(EditableAvatarButton, { label: selectedAgent ? `Edit ${selectedAgent.name} photo` : 'Photo editing is unavailable until agent creation', imageUrl: selectedAgentAvatarUrl, fallback: _jsx(Bot, { size: 28 }), accentColor: signatureColor || selectedAgent?.color, size: 88, editBadgeSize: 28, editIconSize: 14, disabled: !selectedAgentId, loading: Boolean(selectedAgentId && generatingAvatarAgentIds.has(selectedAgentId)), loadingLabel: `Generating ${selectedAgent?.name || 'agent'} photo`, error: Boolean(selectedAgentGenerationFeedback?.error), errorLabel: selectedAgentGenerationFeedback?.error || 'Agent photo generation failed', onClick: () => {
1667
2145
  setAgentAvatarError(null);
1668
2146
  setAgentAvatarNotice(null);
1669
2147
  setAvatarEditorOpen(true);
1670
- } }), isSelectedAgentAvatarSyncing ? _jsx(Loader2, { size: 16, className: `${styles.spinner} ${styles.taskforceAgentAvatarSyncIcon}` }) : null] }), _jsxs("div", { className: styles.taskforceAgentAvatarCopy, children: [_jsx("strong", { children: agentName.trim() || selectedAgent?.name || DEFAULT_AGENT_NAME }), _jsx("span", { className: styles.taskforceAgentAvatarRole, children: "Taskforce Agent" }), isSelectedAgentAvatarSyncing ? _jsx("small", { children: "Syncing avatar..." }) : null] }), _jsxs("div", { className: styles.taskforceAgentToolbar, children: [configFeedback ? (_jsxs("span", { className: configFeedback.type === 'error' ? 'tf-chip-danger' : 'tf-chip-success', role: configFeedback.type === 'error' ? 'alert' : 'status', "aria-label": "Agent configuration status", children: [configFeedback.type === 'error' ? _jsx(AlertTriangle, { size: 12 }) : _jsx(CheckCircle, { size: 12 }), configFeedback.message] })) : null, _jsxs("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: () => { void handleSaveAgent(); }, disabled: savingAgent || !agentName.trim() || !definitionDraft.purpose.trim() || !modelKey, children: [savingAgent ? _jsx(Loader2, { size: 14, className: styles.spinner }) : _jsx(Save, { size: 14 }), "Save"] })] })] }), selectedAgent?.configurationState && selectedAgent.configurationState !== 'valid' ? (_jsxs("div", { className: styles.taskforceAgentTestError, role: "alert", children: [_jsx(AlertTriangle, { size: 14 }), _jsxs("span", { children: ["This Agent configuration is ", selectedAgent.configurationState.replace(/-/g, ' '), ".", selectedAgent.configurationIssues?.[0]?.message
2148
+ } }), isSelectedAgentAvatarSyncing ? _jsx(Loader2, { size: 16, className: `${styles.spinner} ${styles.taskforceAgentAvatarSyncIcon}` }) : null] }), _jsxs("div", { className: styles.taskforceAgentAvatarCopy, children: [_jsx("strong", { children: agentName.trim() || selectedAgent?.name || DEFAULT_AGENT_NAME }), _jsx("span", { className: styles.taskforceAgentAvatarRole, children: "Taskforce Agent" }), isSelectedAgentAvatarSyncing ? _jsx("small", { children: "Syncing avatar..." }) : null] }), _jsxs("div", { className: styles.taskforceAgentToolbar, children: [configFeedback ? (_jsxs("span", { className: configFeedback.type === 'error' ? 'tf-chip-danger' : 'tf-chip-success', role: configFeedback.type === 'error' ? 'alert' : 'status', "aria-label": "Agent configuration status", children: [configFeedback.type === 'error' ? _jsx(AlertTriangle, { size: 12 }) : _jsx(CheckCircle, { size: 12 }), configFeedback.message] })) : null, _jsxs("button", { type: "button", className: styles.secondaryHeaderBtn, "aria-expanded": testPanelOpen, "aria-controls": "taskforce-agent-test-panel", title: "Uses the current draft, including unsaved changes. Test messages are not saved and cannot modify tasks.", onClick: () => setTestPanelOpen((open) => !open), children: [_jsx(Beaker, { size: 14 }), "Test Agent"] }), _jsxs("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: () => { void handleSaveAgent(); }, disabled: savingAgent || !agentName.trim() || !definitionDraft.purpose.trim() || !modelKey || !selectedModel, children: [savingAgent ? _jsx(Loader2, { size: 14, className: styles.spinner }) : _jsx(Save, { size: 14 }), "Save"] })] })] }), testPanelOpen ? (_jsxs("section", { id: "taskforce-agent-test-panel", className: styles.taskforceAgentHeaderTestPanel, "aria-label": "Isolated Agent test", children: [_jsxs("div", { className: styles.taskforceAgentTestComposer, children: [_jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Test Prompt" }), _jsx("textarea", { className: styles.input, value: draftTestPrompt, onChange: (event) => setDraftTestPrompt(event.target.value), onKeyDown: handleDraftTestKeyDown, placeholder: "Ask the draft agent a representative question...", rows: 4, disabled: testingDraft })] }), _jsx("button", { type: "button", className: `tf-control-icon ${styles.taskforceAgentTestSendButton}`, "aria-label": "Send test prompt", title: "Send test prompt", onClick: () => { void handleDraftTest(); }, disabled: testingDraft || !draftTestPrompt.trim() || !definitionDraft.purpose.trim() || !modelKey, children: testingDraft ? _jsx(Loader2, { size: 15, className: styles.spinner }) : _jsx(Send, { size: 15 }) })] }), !canUseCloudAgentRuntime ? (_jsx("p", { className: styles.taskforceAgentTestRestriction, children: authSessionResolved
2149
+ ? 'Sign in to Taskforce Cloud to run agent tests.'
2150
+ : 'Checking Taskforce Cloud sign-in...' })) : null, draftTestError ? (_jsxs("div", { className: styles.taskforceAgentTestError, role: "alert", children: [_jsx(AlertTriangle, { size: 14 }), _jsx("span", { children: draftTestError })] })) : null, testingDraft ? (_jsxs("div", { className: styles.taskforceAgentTestPending, role: "status", children: [_jsx(Loader2, { size: 15, className: styles.spinner }), _jsx("span", { children: "Testing agent..." })] })) : null, draftTestResult ? (_jsxs("div", { className: styles.taskforceAgentTestResult, role: "status", "aria-live": "polite", children: [_jsxs("div", { className: styles.taskforceAgentTestResultHeader, children: [_jsxs("span", { children: [_jsx(CheckCircle, { size: 14 }), " Test completed"] }), _jsxs("span", { children: [formatModelGatewayLatency(draftTestResult), " \u00B7 ", formatModelGatewayUsage(draftTestResult)] })] }), _jsx("div", { className: styles.taskforceAgentTestResponse, children: _jsx(Markdown, { variant: "conversation", children: draftTestResult.text }) }), _jsxs("details", { className: styles.taskforceAgentToolActivity, children: [_jsxs("summary", { children: [_jsx("span", { children: "Response Details" }), _jsx("strong", { children: labelForTaskforceAgentModel(draftTestSnapshot?.modelKey || modelKey) })] }), _jsxs("div", { className: styles.taskforceAgentMetaGrid, children: [_jsxs("div", { children: [_jsx("span", { children: "Tested agent" }), _jsx("strong", { children: draftTestSnapshot?.agentName || agentName.trim() || DEFAULT_AGENT_NAME })] }), _jsxs("div", { children: [_jsx("span", { children: "Provider" }), _jsx("strong", { children: labelForTaskforceAgentProvider(draftTestSnapshot?.providerKey || providerKey) })] }), _jsxs("div", { children: [_jsx("span", { children: "Runtime model" }), _jsx("code", { children: draftTestResult.modelId || 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Test prompt" }), _jsx("strong", { children: draftTestSnapshot?.prompt || 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Tested purpose" }), _jsx("strong", { children: draftTestSnapshot?.definition.purpose || 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Tested working guidelines" }), _jsx("strong", { children: draftTestSnapshot?.definition.behavior.workingGuidelines || 'None' })] }), _jsxs("div", { children: [_jsx("span", { children: "Stop reason" }), _jsx("strong", { children: draftTestResult.stopReason || 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Input tokens" }), _jsx("strong", { children: draftTestResult.usage?.inputTokens ?? 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Output tokens" }), _jsx("strong", { children: draftTestResult.usage?.outputTokens ?? 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Total tokens" }), _jsx("strong", { children: draftTestResult.usage?.totalTokens ?? 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Latency" }), _jsx("strong", { children: formatModelGatewayLatency(draftTestResult) })] })] }), draftTestToolActivity.length > 0
2151
+ ? renderToolActivityDetails(draftTestToolActivity, 'Managed Tool Activity')
2152
+ : null] })] })) : null] })) : null, selectedAgent?.configurationState && selectedAgent.configurationState !== 'valid' ? (_jsxs("div", { className: styles.taskforceAgentTestError, role: "alert", children: [_jsx(AlertTriangle, { size: 14 }), _jsxs("span", { children: ["This Agent configuration is ", selectedAgent.configurationState.replace(/-/g, ' '), ".", selectedAgent.configurationIssues?.[0]?.message
1671
2153
  ? ` ${selectedAgent.configurationIssues[0].message}`
1672
- : ' Repair the highlighted configuration before testing it.'] })] })) : null, _jsxs("div", { className: styles.topRow, children: [_jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Agent Name" }), _jsx("input", { className: styles.input, value: agentName, onChange: (event) => {
2154
+ : ' Repair the highlighted configuration before testing it.'] })] })) : null, modelCatalogError ? (_jsxs("div", { className: styles.taskforceAgentTestError, role: "alert", children: [_jsx(AlertTriangle, { size: 14 }), _jsx("span", { children: modelCatalogError }), _jsxs("button", { type: "button", className: `${styles.secondaryHeaderBtn} ${styles.taskforceAgentRetryButton}`, onClick: () => setModelCatalogRefreshKey((current) => current + 1), children: [_jsx(RotateCcw, { size: 14 }), "Retry"] })] })) : null, _jsxs("div", { className: styles.topRow, children: [_jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Agent Name" }), _jsx("input", { className: styles.input, value: agentName, onChange: (event) => {
1673
2155
  configurationDraftDirtyRef.current = true;
1674
2156
  setAgentName(event.target.value);
1675
- }, placeholder: DEFAULT_AGENT_NAME, required: true, maxLength: 80 })] }), _jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Platform" }), _jsx("div", { className: styles.taskforceAgentReadonlyField, children: formatAgentProvider(modelProvider) })] }), _jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Provider" }), _jsx("select", { className: styles.select, value: providerKey, onChange: (event) => handleProviderChange(event.target.value), children: configuredProviderOptions.map((provider) => (_jsx("option", { value: provider.key, children: provider.label }, provider.key))) })] }), _jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Model" }), _jsx("select", { className: styles.select, value: modelKey, onChange: (event) => handleModelChange(event.target.value), disabled: modelOptions.length === 0, children: modelOptions.length > 0 ? (modelOptions.map((model) => (_jsx("option", { value: model.key, children: model.label }, model.key)))) : (_jsxs("option", { value: "", children: ["No configured ", labelForTaskforceAgentProvider(providerKey), " models yet"] })) })] }), _jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Default Tier" }), _jsx("div", { className: styles.taskforceAgentReadonlyField, children: selectedModel ? formatModelTier(selectedModel.defaultTier) : 'No configured tier' })] })] }), _jsxs("div", { className: styles.taskforceAgentSignatureColorField, children: [_jsxs("div", { children: [_jsx("span", { className: styles.label, children: "Signature Color" }), _jsx("small", { children: "Identifies this agent across Taskforce." })] }), _jsxs("div", { className: styles.aiProfileColorPicker, "aria-label": "Signature color options", children: [AI_PROFILE_COLOR_TOKENS.map((token) => {
1676
- const color = COLOR_PALETTE[token];
1677
- return (_jsx("button", { type: "button", className: styles.aiProfileColorOption, style: { backgroundColor: color }, "aria-label": `Use ${token}`, "aria-pressed": signatureColor.toLowerCase() === color.toLowerCase(), onClick: () => {
1678
- configurationDraftDirtyRef.current = true;
1679
- setSignatureColor(color);
1680
- setSignatureColorDirty(true);
1681
- setSignatureColorReset(false);
1682
- signatureColorEditPendingRef.current = true;
1683
- } }, token));
1684
- }), selectedAgent ? (_jsx("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: () => {
2157
+ }, placeholder: DEFAULT_AGENT_NAME, required: true, maxLength: 80 })] }), _jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Platform" }), _jsx("div", { className: styles.taskforceAgentReadonlyField, "aria-label": "Platform", children: formatAgentProvider(modelProvider) })] }), _jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Model Source" }), _jsx("select", { className: styles.select, value: modelSourceSelectionKey, onChange: (event) => handleModelSourceChange(event.target.value), children: configuredModelSourceOptions.map((source) => (_jsx("option", { value: source.key, disabled: source.disabled, children: source.label }, source.key))) })] }), _jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Provider" }), _jsx("select", { className: styles.select, value: providerKey, onChange: (event) => handleProviderChange(event.target.value), children: configuredProviderOptions.map((provider) => (_jsx("option", { value: provider.key, children: provider.label }, provider.key))) })] }), _jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Model" }), _jsxs("select", { className: styles.select, value: modelKey, onChange: (event) => handleModelChange(event.target.value), disabled: modelOptions.length === 0, children: [!selectedModel && modelKey ? (_jsxs("option", { value: modelKey, disabled: true, children: [labelForTaskforceAgentModel(modelKey), " (unavailable)"] })) : null, modelOptions.map((model) => (_jsx("option", { value: model.key, children: model.label }, model.key))), modelOptions.length === 0 && !modelKey ? (_jsxs("option", { value: "", children: ["No configured ", labelForTaskforceAgentProvider(providerKey), " models yet"] })) : null] })] }), _jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Default Tier" }), _jsx("div", { className: styles.taskforceAgentReadonlyField, children: selectedModel ? formatModelTier(selectedModel.defaultTier) : 'No configured tier' })] })] }), _jsxs("div", { className: styles.taskforceAgentSignatureColorField, children: [_jsx("span", { className: styles.label, title: "Identifies this agent across Taskforce.", "aria-label": "Signature Color. Identifies this agent across Taskforce.", children: "Signature Color" }), _jsx("div", { className: styles.aiProfileColorPicker, "aria-label": "Signature color options", children: AI_PROFILE_SIGNATURE_COLOR_OPTIONS.map(({ name, value: color }) => {
2158
+ return (_jsx("button", { type: "button", className: styles.aiProfileColorOption, style: { backgroundColor: color }, "aria-label": `Use ${name}`, "aria-pressed": signatureColor.toLowerCase() === color.toLowerCase(), onClick: () => {
1685
2159
  configurationDraftDirtyRef.current = true;
1686
- setSignatureColor(assignedAiProfileColor(selectedAgent.id));
1687
- setSignatureColorDirty(false);
1688
- setSignatureColorReset(true);
2160
+ setSignatureColor(color);
2161
+ setSignatureColorDirty(true);
1689
2162
  signatureColorEditPendingRef.current = true;
1690
- }, children: "Reset" })) : null] })] }), _jsxs("div", { className: styles.taskforceAgentBehaviorGrid, children: [_jsxs("label", { className: `${styles.field} ${styles.taskforceAgentBehaviorWide}`, children: [_jsx("span", { className: styles.label, children: "Purpose" }), _jsx("small", { className: styles.taskforceAgentFieldHelper, children: "What should this agent help with?" }), _jsx("textarea", { className: styles.input, "aria-label": "Purpose", value: definitionDraft.purpose, onChange: (event) => {
2163
+ } }, color));
2164
+ }) })] }), _jsxs("div", { className: styles.taskforceAgentBehaviorGrid, children: [_jsxs("label", { className: `${styles.field} ${styles.taskforceAgentBehaviorWide}`, children: [_jsx("span", { className: styles.label, children: "Purpose" }), _jsx("small", { className: styles.taskforceAgentFieldHelper, children: "What should this agent help with?" }), _jsx("textarea", { className: styles.input, "aria-label": "Purpose", value: definitionDraft.purpose, onChange: (event) => {
1691
2165
  configurationDraftDirtyRef.current = true;
1692
2166
  setDefinitionDraft((current) => ({
1693
2167
  ...current,
1694
2168
  purpose: event.target.value,
1695
2169
  }));
1696
- }, maxLength: 1000, rows: 3, required: true })] }), _jsx("div", { className: styles.taskforceAgentBehaviorWide, children: _jsx(AgentBehaviorAssignments, { workspaceId: workspaceId, roleRef: definitionDraft.roleRef, skillRefs: definitionDraft.skillRefs, onRoleRefChange: (roleRef) => {
1697
- configurationDraftDirtyRef.current = true;
1698
- setDefinitionDraft((current) => ({
1699
- ...current,
1700
- roleRef,
1701
- }));
1702
- }, onSkillRefsChange: (skillRefs) => {
1703
- configurationDraftDirtyRef.current = true;
1704
- setDefinitionDraft((current) => ({
1705
- ...current,
1706
- skillRefs,
1707
- }));
1708
- }, onManageRoles: () => setActiveBuilderTab('roles'), onManageSkills: () => setActiveBuilderTab('skills') }) }), _jsx("div", { className: styles.taskforceAgentBehaviorWide, children: _jsx(ToolAccessSelector, { selectedKeys: definitionDraft.requestedCapabilities, hasCustomOverrides: Boolean(selectedAgent?.definition?.toolPolicy.toolOverrides?.length
1709
- && !definitionDraft.toolAccessChanged), onChange: (requestedCapabilities) => {
1710
- configurationDraftDirtyRef.current = true;
1711
- setDefinitionDraft((current) => ({
1712
- ...current,
1713
- requestedCapabilities,
1714
- toolAccessChanged: true,
1715
- }));
1716
- } }) }), _jsx("div", { className: styles.taskforceAgentBehaviorWide, children: _jsx(EffectiveConfigurationSummary, { hasRole: Boolean(definitionDraft.roleRef), directSkillCount: definitionDraft.skillRefs.length, toolKeys: definitionDraft.requestedCapabilities }) }), _jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Personality and Voice" }), _jsx("small", { className: styles.taskforceAgentFieldHelper, children: "How should this agent behave or portray itself?" }), _jsxs("select", { className: styles.select, "aria-label": "Personality and Voice", value: definitionDraft.personalitySelection, onChange: (event) => {
2170
+ }, maxLength: 1000, rows: 3, required: true })] }), _jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Personality and Voice" }), _jsx("small", { className: styles.taskforceAgentFieldHelper, children: "How should this agent behave or portray itself?" }), _jsxs("select", { className: styles.select, "aria-label": "Personality and Voice", value: definitionDraft.personalitySelection, onChange: (event) => {
1717
2171
  configurationDraftDirtyRef.current = true;
1718
2172
  setDefinitionDraft((current) => ({
1719
2173
  ...current,
@@ -1764,13 +2218,29 @@ export function TaskforceAgentsModule({ workspaceId, agentRosterTrayOpen = true,
1764
2218
  ...current,
1765
2219
  avatarArtStyleCustom: event.target.value,
1766
2220
  }));
1767
- }, maxLength: 4000, rows: 3, placeholder: "Describe medium, palette, lighting, period, or atmosphere." })] })) : null] }), _jsxs("details", { className: styles.taskforceAgentToolActivity, children: [_jsxs("summary", { children: [_jsx("span", { children: "Technical model details" }), _jsx("strong", { children: selectedModel ? formatModelAvailability(selectedModel.availability) : 'Unavailable' })] }), _jsxs("div", { className: styles.taskforceAgentMetaGrid, children: [_jsxs("div", { children: [_jsx("span", { children: "Model ID" }), _jsx("code", { children: selectedModel?.runtimeModelId || 'No configured model' })] }), _jsxs("div", { children: [_jsx("span", { children: "Max Output" }), _jsx("strong", { children: formatMaxOutputTokens(selectedModel?.defaultMaxTokens) })] })] })] })] }), _jsx("div", { className: styles.taskforceAgentOutputStack, children: _jsxs("section", { className: styles.taskforceAgentOutputPanel, "aria-labelledby": "taskforce-agent-test-title", children: [_jsx("div", { className: styles.settingTitleRow, children: _jsxs("div", { className: styles.taskforceAgentTestTitle, children: [_jsx("span", { className: styles.taskforceAgentBuilderEmptyIcon, "aria-hidden": "true", children: _jsx(Beaker, { size: 18 }) }), _jsxs("div", { children: [_jsx("h4", { id: "taskforce-agent-test-title", className: styles.settingSubTitle, children: "Test Agent" }), _jsx("p", { children: "Run one isolated prompt against the current draft, including unsaved changes." })] })] }) }), _jsxs("div", { className: styles.taskforceAgentTestNotice, children: [_jsx(CheckCircle, { size: 14, "aria-hidden": "true" }), _jsx("span", { children: "Test messages are not saved to conversations and cannot write to task-scoped tools." })] }), _jsxs("label", { className: styles.field, children: [_jsx("span", { className: styles.label, children: "Test Prompt" }), _jsx("textarea", { className: styles.input, value: draftTestPrompt, onChange: (event) => setDraftTestPrompt(event.target.value), placeholder: "Ask the draft agent a representative question...", rows: 5, disabled: testingDraft })] }), _jsxs("div", { className: styles.taskforceAgentTestActions, children: [_jsxs("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: resetDraftTest, disabled: testingDraft || (!draftTestPrompt && !draftTestResult && !draftTestError), children: [_jsx(RotateCcw, { size: 14 }), "Reset"] }), selectedAgentId && onOpenConversation ? (_jsxs("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: () => onOpenConversation(selectedAgentId), disabled: testingDraft, children: [_jsx(MessageSquare, { size: 14 }), "Open in Conversation"] })) : null, _jsxs("button", { type: "button", className: styles.primaryUpdateBtn, onClick: () => { void handleDraftTest(); }, disabled: testingDraft || !draftTestPrompt.trim() || !definitionDraft.purpose.trim() || !modelKey, children: [testingDraft ? _jsx(Loader2, { size: 14, className: styles.spinner }) : _jsx(Beaker, { size: 14 }), testingDraft ? 'Testing...' : 'Run Test'] })] }), !canUseCloudAgentRuntime ? (_jsx("p", { className: styles.taskforceAgentTestRestriction, children: authSessionResolved
1768
- ? 'Sign in to Taskforce Cloud to run agent tests.'
1769
- : 'Checking Taskforce Cloud sign-in...' })) : null, draftTestError ? (_jsxs("div", { className: styles.taskforceAgentTestError, role: "alert", children: [_jsx(AlertTriangle, { size: 14 }), _jsx("span", { children: draftTestError })] })) : null, testingDraft ? (_jsxs("div", { className: styles.taskforceAgentTestPending, role: "status", children: [_jsx(Loader2, { size: 15, className: styles.spinner }), _jsx("span", { children: "Running isolated test..." })] })) : null, draftTestResult ? (_jsxs("div", { className: styles.taskforceAgentTestResult, role: "status", "aria-live": "polite", children: [_jsxs("div", { className: styles.taskforceAgentTestResultHeader, children: [_jsxs("span", { children: [_jsx(CheckCircle, { size: 14 }), " Test completed"] }), _jsxs("span", { children: [formatModelGatewayLatency(draftTestResult), " \u00B7 ", formatModelGatewayUsage(draftTestResult)] })] }), _jsx("div", { className: styles.taskforceAgentTestResponse, children: _jsx(Markdown, { variant: "conversation", children: draftTestResult.text }) }), _jsxs("details", { className: styles.taskforceAgentToolActivity, children: [_jsxs("summary", { children: [_jsx("span", { children: "Response Details" }), _jsx("strong", { children: labelForTaskforceAgentModel(draftTestSnapshot?.modelKey || modelKey) })] }), _jsxs("div", { className: styles.taskforceAgentMetaGrid, children: [_jsxs("div", { children: [_jsx("span", { children: "Tested agent" }), _jsx("strong", { children: draftTestSnapshot?.agentName || agentName.trim() || DEFAULT_AGENT_NAME })] }), _jsxs("div", { children: [_jsx("span", { children: "Provider" }), _jsx("strong", { children: labelForTaskforceAgentProvider(draftTestSnapshot?.providerKey || providerKey) })] }), _jsxs("div", { children: [_jsx("span", { children: "Runtime model" }), _jsx("code", { children: draftTestResult.modelId || 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Test prompt" }), _jsx("strong", { children: draftTestSnapshot?.prompt || 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Tested purpose" }), _jsx("strong", { children: draftTestSnapshot?.definition.purpose || 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Tested working guidelines" }), _jsx("strong", { children: draftTestSnapshot?.definition.behavior.workingGuidelines || 'None' })] }), _jsxs("div", { children: [_jsx("span", { children: "Stop reason" }), _jsx("strong", { children: draftTestResult.stopReason || 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Input tokens" }), _jsx("strong", { children: draftTestResult.usage?.inputTokens ?? 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Output tokens" }), _jsx("strong", { children: draftTestResult.usage?.outputTokens ?? 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Total tokens" }), _jsx("strong", { children: draftTestResult.usage?.totalTokens ?? 'Not reported' })] }), _jsxs("div", { children: [_jsx("span", { children: "Latency" }), _jsx("strong", { children: formatModelGatewayLatency(draftTestResult) })] })] }), draftTestToolActivity.length > 0
1770
- ? renderToolActivityDetails(draftTestToolActivity, 'Managed Tool Activity')
1771
- : null] })] })) : null] }) })] }) }), _jsx("section", { className: styles.taskforceAgentBuilderPanel, role: "tabpanel", id: "taskforce-agent-builder-roles", "aria-labelledby": "taskforce-agent-builder-tab-roles", hidden: activeBuilderTab !== 'roles', children: activeBuilderTab === 'roles'
2221
+ }, maxLength: 4000, rows: 3, placeholder: "Describe medium, palette, lighting, period, or atmosphere." })] })) : null] }), _jsxs("details", { className: styles.taskforceAgentToolActivity, children: [_jsxs("summary", { children: [_jsx("span", { children: "Technical model details" }), _jsx("strong", { children: selectedModel ? formatModelAvailability(selectedModel.availability) : 'Unavailable' })] }), _jsxs("div", { className: styles.taskforceAgentMetaGrid, children: [_jsxs("div", { children: [_jsx("span", { children: "Model ID" }), _jsx("code", { children: selectedModel?.runtimeModelId || 'No configured model' })] }), _jsxs("div", { children: [_jsx("span", { children: "Max Output" }), _jsx("strong", { children: formatMaxOutputTokens(selectedModel?.defaultMaxTokens) })] })] })] })] }), _jsxs("aside", { className: styles.taskforceAgentOutputStack, "aria-label": "Agent Role and Capabilities", children: [_jsx(AgentBehaviorAssignments, { workspaceId: workspaceId, roleRef: definitionDraft.roleRef, skillRefs: definitionDraft.skillRefs, onRoleRefChange: (roleRef) => {
2222
+ configurationDraftDirtyRef.current = true;
2223
+ setDefinitionDraft((current) => ({
2224
+ ...current,
2225
+ roleRef,
2226
+ }));
2227
+ }, onSkillRefsChange: (skillRefs) => {
2228
+ configurationDraftDirtyRef.current = true;
2229
+ setDefinitionDraft((current) => ({
2230
+ ...current,
2231
+ skillRefs,
2232
+ }));
2233
+ }, onManageRoles: () => setActiveBuilderTab('roles'), onManageSkills: () => setActiveBuilderTab('skills') }), _jsx(ToolAccessSelector, { selectedKeys: definitionDraft.requestedCapabilities, hasCustomOverrides: Boolean(selectedAgent?.definition?.toolPolicy.toolOverrides?.length
2234
+ && !definitionDraft.toolAccessChanged), onChange: (requestedCapabilities) => {
2235
+ configurationDraftDirtyRef.current = true;
2236
+ setDefinitionDraft((current) => ({
2237
+ ...current,
2238
+ requestedCapabilities,
2239
+ toolAccessChanged: true,
2240
+ }));
2241
+ } }), _jsx(EffectiveConfigurationSummary, { hasRole: Boolean(definitionDraft.roleRef), directSkillCount: definitionDraft.skillRefs.length, toolKeys: definitionDraft.requestedCapabilities })] })] }) }), _jsx("section", { className: styles.taskforceAgentBuilderPanel, role: "tabpanel", id: "taskforce-agent-builder-roles", "aria-labelledby": "taskforce-agent-builder-tab-roles", hidden: activeBuilderTab !== 'roles', children: activeBuilderTab === 'roles'
1772
2242
  ? (_jsx(RoleManagerPanel, { workspaceId: workspaceId, libraryPortalTarget: roleLibraryPortalTarget }))
1773
- : null }), _jsx("section", { className: styles.taskforceAgentBuilderPanel, role: "tabpanel", id: "taskforce-agent-builder-skills", "aria-labelledby": "taskforce-agent-builder-tab-skills", hidden: activeBuilderTab !== 'skills', children: _jsx(SkillManagerPanel, { workspaceId: workspaceId, active: activeBuilderTab === 'skills', libraryPortalTarget: skillLibraryPortalTarget }, workspaceId) }), _jsx("section", { className: styles.taskforceAgentBuilderPanel, role: "tabpanel", id: "taskforce-agent-builder-connections", "aria-labelledby": "taskforce-agent-builder-tab-connections", hidden: activeBuilderTab !== 'connections', children: activeBuilderTab === 'connections' ? _jsx(FutureWorkspacePanel, { kind: "connections" }) : null }), _jsx("section", { className: styles.taskforceAgentBuilderPanel, role: "tabpanel", id: "taskforce-agent-builder-resources", "aria-labelledby": "taskforce-agent-builder-tab-resources", hidden: activeBuilderTab !== 'resources', children: activeBuilderTab === 'resources' ? _jsx(FutureWorkspacePanel, { kind: "resources" }) : null })] }), _jsx(Modal, { isOpen: Boolean(pendingAgentNavigation), onClose: () => setPendingAgentNavigation(null), title: "Discard unsaved changes?", size: "sm", theme: theme, closeOnOverlayClick: false, footer: (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "tf-button-secondary", onClick: () => setPendingAgentNavigation(null), "data-modal-initial-focus": true, children: "Keep editing" }), _jsx("button", { type: "button", className: "tf-button-destructive", onClick: discardDraftAndNavigate, children: "Discard changes" })] })), children: _jsx("p", { children: "Your unsaved Agent changes will be lost." }) }), _jsx(AvatarImageManagerModal, { isOpen: avatarEditorOpen, theme: theme, title: "Edit Agent Profile Photo", currentImageUrl: selectedAgentAvatarUrl, editorImageUrl: selectedAgentAvatarSourceUrl, fallbackInitial: (selectedAgent?.name || 'Agent').charAt(0).toUpperCase(), accept: "image/png,image/jpeg,image/webp,image/gif", busy: agentAvatarBusy, generating: Boolean(selectedAgentId && generatingAvatarAgentIds.has(selectedAgentId)), generateLabel: selectedAgent?.avatarUrl ? 'Regenerate' : 'Generate', hasPendingImage: false, canRemove: Boolean(selectedAgent?.avatarUrl), error: agentAvatarError || selectedAgentGenerationFeedback?.error || null, notice: agentAvatarNotice || selectedAgentGenerationFeedback?.notice || null, onClose: () => {
2243
+ : null }), _jsx("section", { className: styles.taskforceAgentBuilderPanel, role: "tabpanel", id: "taskforce-agent-builder-skills", "aria-labelledby": "taskforce-agent-builder-tab-skills", hidden: activeBuilderTab !== 'skills', children: _jsx(SkillManagerPanel, { workspaceId: workspaceId, active: activeBuilderTab === 'skills', libraryPortalTarget: skillLibraryPortalTarget }, workspaceId) }), _jsx("section", { className: styles.taskforceAgentBuilderPanel, role: "tabpanel", id: "taskforce-agent-builder-connections", "aria-labelledby": "taskforce-agent-builder-tab-connections", hidden: activeBuilderTab !== 'connections', children: activeBuilderTab === 'connections' ? (_jsx(ModelConnectionsPanel, { workspaceId: workspaceId, active: true, runtimeMode: runtimeMode, eligible: subscriptionConnectionsEligible, cloudAccessAvailable: modelConnectionCloudAccessAvailable, cloudAccessPending: modelConnectionCloudAccessPending, cloudAuthConfigured: cloudAuthConfigured, resolveCloudAuthUrl: resolveCloudAuthUrl, onConnectionActivated: handleConnectionActivated, onConnectionUnavailable: handleConnectionUnavailable, onReturnToAgent: () => activateBuilderTab('agents', true), libraryPortalTarget: connectionLibraryPortalTarget })) : null }), _jsx("section", { className: styles.taskforceAgentBuilderPanel, role: "tabpanel", id: "taskforce-agent-builder-resources", "aria-labelledby": "taskforce-agent-builder-tab-resources", hidden: activeBuilderTab !== 'resources', children: activeBuilderTab === 'resources' ? _jsx(FutureWorkspacePanel, { kind: "resources" }) : null })] }), _jsx(Modal, { isOpen: Boolean(pendingAgentNavigation), onClose: () => setPendingAgentNavigation(null), title: "Discard unsaved changes?", size: "sm", theme: theme, closeOnOverlayClick: false, footer: (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "tf-button-secondary", onClick: () => setPendingAgentNavigation(null), "data-modal-initial-focus": true, children: "Keep editing" }), _jsx("button", { type: "button", className: "tf-button-destructive", onClick: discardDraftAndNavigate, children: "Discard changes" })] })), children: _jsx("p", { children: "Your unsaved Agent changes will be lost." }) }), _jsx(AvatarImageManagerModal, { isOpen: avatarEditorOpen, theme: theme, title: "Edit Agent Profile Photo", currentImageUrl: selectedAgentAvatarUrl, editorImageUrl: selectedAgentAvatarSourceUrl, fallbackInitial: (selectedAgent?.name || 'Agent').charAt(0).toUpperCase(), accept: "image/png,image/jpeg,image/webp,image/gif", busy: agentAvatarBusy, generating: Boolean(selectedAgentId && generatingAvatarAgentIds.has(selectedAgentId)), generateLabel: selectedAgent?.avatarUrl ? 'Regenerate' : 'Generate', hasPendingImage: false, canRemove: Boolean(selectedAgent?.avatarUrl), error: agentAvatarError || selectedAgentGenerationFeedback?.error || null, notice: agentAvatarNotice || selectedAgentGenerationFeedback?.notice || null, onClose: () => {
1774
2244
  if (agentAvatarBusy)
1775
2245
  return;
1776
2246
  setAvatarEditorOpen(false);