@burdenoff/microfe-vibecontrols 2026.623.1 → 2026.623.2

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 (293) hide show
  1. package/dist/components/actions/ActionCard.js +2 -2
  2. package/dist/components/actions/ActionCard.js.map +1 -1
  3. package/dist/components/actions/ExecutionHistory.js +1 -1
  4. package/dist/components/actions/ExecutionHistory.js.map +1 -1
  5. package/dist/components/agent-manager/AgentManagerAdminBar.js +1 -1
  6. package/dist/components/agent-manager/AgentManagerAdminBar.js.map +1 -1
  7. package/dist/components/agent-manager/AgentManagerChatPane.js +2 -2
  8. package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
  9. package/dist/components/agent-manager/AgentManagerInputArea.js +4 -4
  10. package/dist/components/agent-manager/AgentManagerInputArea.js.map +1 -1
  11. package/dist/components/agent-manager/AgentManagerLayout.js +1 -1
  12. package/dist/components/agent-manager/AgentManagerLayout.js.map +1 -1
  13. package/dist/components/agent-manager/AgentManagerMessageItem.js +1 -1
  14. package/dist/components/agent-manager/AgentManagerMessageItem.js.map +1 -1
  15. package/dist/components/agent-manager/AgentManagerSidebar.js +5 -5
  16. package/dist/components/agent-manager/AgentManagerSidebar.js.map +1 -1
  17. package/dist/components/agent-manager/AgentManagerTab.js +1 -1
  18. package/dist/components/agent-manager/AgentManagerTab.js.map +1 -1
  19. package/dist/components/agent-manager/AgentManagerTabBar.js +1 -1
  20. package/dist/components/agent-manager/AgentManagerTabBar.js.map +1 -1
  21. package/dist/components/agent-manager/BridleConfigPanel.js +23 -23
  22. package/dist/components/agent-manager/BridleConfigPanel.js.map +1 -1
  23. package/dist/components/agent-manager/PluginInstallModal.js +1 -1
  24. package/dist/components/agent-manager/PluginInstallModal.js.map +1 -1
  25. package/dist/components/agent-manager/ProfilesPanel.js +5 -5
  26. package/dist/components/agent-manager/ProfilesPanel.js.map +1 -1
  27. package/dist/components/agent-manager/controls/AutonomySelector.js +1 -1
  28. package/dist/components/agent-manager/controls/AutonomySelector.js.map +1 -1
  29. package/dist/components/agent-manager/controls/SdkSelector.js +1 -1
  30. package/dist/components/agent-manager/controls/SdkSelector.js.map +1 -1
  31. package/dist/components/agent-manager/controls/VoiceToggle.js +1 -1
  32. package/dist/components/agent-manager/controls/VoiceToggle.js.map +1 -1
  33. package/dist/components/agent-manager/detail/SessionConfigPanel.js +6 -6
  34. package/dist/components/agent-manager/detail/SessionConfigPanel.js.map +1 -1
  35. package/dist/components/agent-manager/detail/SessionLogsPanel.js +5 -5
  36. package/dist/components/agent-manager/detail/SessionLogsPanel.js.map +1 -1
  37. package/dist/components/agent-manager/detail/SessionToolsPanel.js +1 -1
  38. package/dist/components/agent-manager/detail/SessionToolsPanel.js.map +1 -1
  39. package/dist/components/agent-manager/dialogs/NewSessionDialog.js +2 -2
  40. package/dist/components/agent-manager/dialogs/NewSessionDialog.js.map +1 -1
  41. package/dist/components/agent-manager/dialogs/RenameSessionDialog.js +1 -1
  42. package/dist/components/agent-manager/dialogs/RenameSessionDialog.js.map +1 -1
  43. package/dist/components/agents/AgentCard.js +14 -11
  44. package/dist/components/agents/AgentCard.js.map +1 -1
  45. package/dist/components/ai/AIContextsTab.js +1 -1
  46. package/dist/components/ai/AIContextsTab.js.map +1 -1
  47. package/dist/components/ai/AIOverviewTab.js +4 -4
  48. package/dist/components/ai/AIOverviewTab.js.map +1 -1
  49. package/dist/components/ai/AIPlanPanel.js +4 -4
  50. package/dist/components/ai/AIPlanPanel.js.map +1 -1
  51. package/dist/components/ai/AIPlaygroundTab.js +1 -1
  52. package/dist/components/ai/AIPlaygroundTab.js.map +1 -1
  53. package/dist/components/ai/AIPromptsTab.js +2 -2
  54. package/dist/components/ai/AIPromptsTab.js.map +1 -1
  55. package/dist/components/ai/AIProviderCredentialsCard.js +4 -4
  56. package/dist/components/ai/AIProviderCredentialsCard.js.map +1 -1
  57. package/dist/components/ai/AIQueueTab.js +4 -4
  58. package/dist/components/ai/AIQueueTab.js.map +1 -1
  59. package/dist/components/ai/AISessionsTab.js +4 -4
  60. package/dist/components/ai/AISessionsTab.js.map +1 -1
  61. package/dist/components/ai/AITasksTab.js +1 -1
  62. package/dist/components/ai/AITasksTab.js.map +1 -1
  63. package/dist/components/ai/ClaudeCredentialsCard.js +3 -3
  64. package/dist/components/ai/ClaudeCredentialsCard.js.map +1 -1
  65. package/dist/components/ai/PluginHarnessPicker.js +3 -3
  66. package/dist/components/ai/PluginHarnessPicker.js.map +1 -1
  67. package/dist/components/analytics/AnalyticsBreakdownChart.js +8 -8
  68. package/dist/components/analytics/AnalyticsBreakdownChart.js.map +1 -1
  69. package/dist/components/analytics/AnalyticsTimeSeriesChart.js +5 -5
  70. package/dist/components/analytics/AnalyticsTimeSeriesChart.js.map +1 -1
  71. package/dist/components/audit/AuditDeleteDialog.js +2 -2
  72. package/dist/components/audit/AuditDeleteDialog.js.map +1 -1
  73. package/dist/components/audit/AuditExportButton.js +5 -5
  74. package/dist/components/audit/AuditExportButton.js.map +1 -1
  75. package/dist/components/audit/AuditLogDetail.js +9 -9
  76. package/dist/components/audit/AuditLogDetail.js.map +1 -1
  77. package/dist/components/audit/AuditLogFilters.js +1 -1
  78. package/dist/components/audit/AuditLogFilters.js.map +1 -1
  79. package/dist/components/audit/AuditLogStats.js +1 -1
  80. package/dist/components/audit/AuditLogStats.js.map +1 -1
  81. package/dist/components/audit/AuditLogTable.js +28 -28
  82. package/dist/components/audit/AuditLogTable.js.map +1 -1
  83. package/dist/components/catalog/CatalogCard.js +3 -3
  84. package/dist/components/catalog/CatalogCard.js.map +1 -1
  85. package/dist/components/catalog/CatalogForm.js +1 -1
  86. package/dist/components/catalog/CatalogForm.js.map +1 -1
  87. package/dist/components/common/ActionAgentTargetNotice.js +6 -6
  88. package/dist/components/common/ActionAgentTargetNotice.js.map +1 -1
  89. package/dist/components/common/CommandEditor.js +1 -1
  90. package/dist/components/common/CommandEditor.js.map +1 -1
  91. package/dist/components/docs/DocsPageTree.js +1 -1
  92. package/dist/components/docs/DocsPageTree.js.map +1 -1
  93. package/dist/components/docs/DocsPreviewPanel.js +1 -1
  94. package/dist/components/docs/DocsPreviewPanel.js.map +1 -1
  95. package/dist/components/docs/DocsRevisionHistory.js +4 -4
  96. package/dist/components/docs/DocsRevisionHistory.js.map +1 -1
  97. package/dist/components/docs/DocsSettingsPanel.js +7 -7
  98. package/dist/components/docs/DocsSettingsPanel.js.map +1 -1
  99. package/dist/components/docs/DocsTabPanel.js +1 -1
  100. package/dist/components/docs/DocsTabPanel.js.map +1 -1
  101. package/dist/components/graph/ConnectionPermissionsDialog.js +2 -2
  102. package/dist/components/graph/ConnectionPermissionsDialog.js.map +1 -1
  103. package/dist/components/graph/MeshDeployWizard.js +3 -3
  104. package/dist/components/graph/MeshDeployWizard.js.map +1 -1
  105. package/dist/components/graph/MeshRequestPanel.js +2 -2
  106. package/dist/components/graph/MeshRequestPanel.js.map +1 -1
  107. package/dist/components/idp-components/ComponentCard.js +5 -5
  108. package/dist/components/idp-components/ComponentCard.js.map +1 -1
  109. package/dist/components/idp-components/ComponentForm.js +1 -1
  110. package/dist/components/idp-components/ComponentForm.js.map +1 -1
  111. package/dist/components/idp-components/DependencyGraphTab.js +7 -7
  112. package/dist/components/idp-components/DependencyGraphTab.js.map +1 -1
  113. package/dist/components/logs/LogViewer.js +14 -14
  114. package/dist/components/logs/LogViewer.js.map +1 -1
  115. package/dist/components/notes/NoteCard.js +1 -1
  116. package/dist/components/notes/NoteCard.js.map +1 -1
  117. package/dist/components/notes/NoteEditor.js +2 -2
  118. package/dist/components/notes/NoteEditor.js.map +1 -1
  119. package/dist/components/notes/NoteList.js +1 -1
  120. package/dist/components/notes/NoteList.js.map +1 -1
  121. package/dist/components/notes/NotesTabPanel.js +1 -1
  122. package/dist/components/notes/NotesTabPanel.js.map +1 -1
  123. package/dist/components/scorecard/ScorecardEmptyState.js +1 -1
  124. package/dist/components/scorecard/ScorecardEmptyState.js.map +1 -1
  125. package/dist/components/scorecard/ScorecardPanel.js +3 -3
  126. package/dist/components/scorecard/ScorecardPanel.js.map +1 -1
  127. package/dist/components/security/SecurityScanList.js +11 -11
  128. package/dist/components/security/SecurityScanList.js.map +1 -1
  129. package/dist/components/sessions/SessionCard.js +26 -19
  130. package/dist/components/sessions/SessionCard.js.map +1 -1
  131. package/dist/components/sessions/SessionStatusBadge.js +7 -7
  132. package/dist/components/sessions/SessionStatusBadge.js.map +1 -1
  133. package/dist/components/shared/AgentSelector.js +4 -4
  134. package/dist/components/shared/AgentSelector.js.map +1 -1
  135. package/dist/components/shared/AgentSetupBanner.js +3 -3
  136. package/dist/components/shared/AgentSetupBanner.js.map +1 -1
  137. package/dist/components/shared/Card.js +4 -4
  138. package/dist/components/shared/Card.js.map +1 -1
  139. package/dist/components/shared/ErrorState.js +15 -9
  140. package/dist/components/shared/ErrorState.js.map +1 -1
  141. package/dist/components/shared/FilterDropdown.js +2 -2
  142. package/dist/components/shared/FilterDropdown.js.map +1 -1
  143. package/dist/components/shared/PageSkeleton.js +6 -6
  144. package/dist/components/shared/PageSkeleton.js.map +1 -1
  145. package/dist/components/shared/SearchInput.js +1 -1
  146. package/dist/components/shared/SearchInput.js.map +1 -1
  147. package/dist/components/shared/StatCard.js +7 -7
  148. package/dist/components/shared/StatCard.js.map +1 -1
  149. package/dist/components/templates/PublishTemplateForm.js +2 -2
  150. package/dist/components/templates/PublishTemplateForm.js.map +1 -1
  151. package/dist/components/templates/TemplateCard.js +5 -5
  152. package/dist/components/templates/TemplateCard.js.map +1 -1
  153. package/dist/components/templates/TemplateWizard.js +2 -2
  154. package/dist/components/templates/TemplateWizard.js.map +1 -1
  155. package/dist/components/tunnels/DomainVerificationBadge.js +4 -4
  156. package/dist/components/tunnels/DomainVerificationBadge.js.map +1 -1
  157. package/dist/components/tunnels/ProviderBadge.js +1 -1
  158. package/dist/components/tunnels/ProviderBadge.js.map +1 -1
  159. package/dist/components/tunnels/TunnelCard.js +16 -16
  160. package/dist/components/tunnels/TunnelCard.js.map +1 -1
  161. package/dist/components/tunnels/TunnelCreateWizard.js +1 -1
  162. package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
  163. package/dist/components/tunnels/TunnelPublicAccessPanel.js +1 -1
  164. package/dist/components/tunnels/TunnelPublicAccessPanel.js.map +1 -1
  165. package/dist/components/tunnels/TunnelStatusBadge.js +4 -4
  166. package/dist/components/tunnels/TunnelStatusBadge.js.map +1 -1
  167. package/dist/components/vibecalendar/LogsDrawer.js +2 -2
  168. package/dist/components/vibecalendar/LogsDrawer.js.map +1 -1
  169. package/dist/components/vibecalendar/ScheduleTaskModal.js +5 -5
  170. package/dist/components/vibecalendar/ScheduleTaskModal.js.map +1 -1
  171. package/dist/components/vibecalendar/VibeCalAgendaView.js +2 -2
  172. package/dist/components/vibecalendar/VibeCalAgendaView.js.map +1 -1
  173. package/dist/components/vibecalendar/VibeCalDayView.js +3 -3
  174. package/dist/components/vibecalendar/VibeCalDayView.js.map +1 -1
  175. package/dist/components/vibecalendar/VibeCalMonthView.js +1 -1
  176. package/dist/components/vibecalendar/VibeCalMonthView.js.map +1 -1
  177. package/dist/components/vibecalendar/VibeCalWeekView.js +4 -4
  178. package/dist/components/vibecalendar/VibeCalWeekView.js.map +1 -1
  179. package/dist/components/vibecalendar/VibeCalendarHeader.js +4 -4
  180. package/dist/components/vibecalendar/VibeCalendarHeader.js.map +1 -1
  181. package/dist/components/vibecalendar/VibeTaskBlock.js +2 -2
  182. package/dist/components/vibecalendar/VibeTaskBlock.js.map +1 -1
  183. package/dist/components/vibecalendar/types.js +6 -6
  184. package/dist/components/vibecalendar/types.js.map +1 -1
  185. package/dist/components/vibedeck/VibeDeckButtonForm.js +9 -9
  186. package/dist/components/vibedeck/VibeDeckButtonForm.js.map +1 -1
  187. package/dist/components/vibedeck/VibeDeckButtonTile.js +6 -6
  188. package/dist/components/vibedeck/VibeDeckButtonTile.js.map +1 -1
  189. package/dist/components/vibedeck/VibeDeckExecutionLog.js +10 -10
  190. package/dist/components/vibedeck/VibeDeckExecutionLog.js.map +1 -1
  191. package/dist/components/vibedeck/VibeDeckForm.js +8 -8
  192. package/dist/components/vibedeck/VibeDeckForm.js.map +1 -1
  193. package/dist/components/vibedeck/VibeDeckGrid.js +2 -2
  194. package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
  195. package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js +2 -2
  196. package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js.map +1 -1
  197. package/dist/components/vibedeck/VibeDeckSwitcher.js +8 -8
  198. package/dist/components/vibedeck/VibeDeckSwitcher.js.map +1 -1
  199. package/dist/components/vibes/VibeCard.js +22 -18
  200. package/dist/components/vibes/VibeCard.js.map +1 -1
  201. package/dist/components/vibes/VibeList.js +16 -12
  202. package/dist/components/vibes/VibeList.js.map +1 -1
  203. package/dist/pages/ActionsPage.js +4 -3
  204. package/dist/pages/ActionsPage.js.map +1 -1
  205. package/dist/pages/AgentDetailsPage.js +9 -9
  206. package/dist/pages/AgentDetailsPage.js.map +1 -1
  207. package/dist/pages/AgentsLandingPage.js +4 -4
  208. package/dist/pages/AgentsLandingPage.js.map +1 -1
  209. package/dist/pages/AgentsPage.js +14 -14
  210. package/dist/pages/AgentsPage.js.map +1 -1
  211. package/dist/pages/AnalyticsPage.js +8 -8
  212. package/dist/pages/AnalyticsPage.js.map +1 -1
  213. package/dist/pages/CatalogDetailPage.js +11 -11
  214. package/dist/pages/CatalogDetailPage.js.map +1 -1
  215. package/dist/pages/CatalogPage.js +12 -9
  216. package/dist/pages/CatalogPage.js.map +1 -1
  217. package/dist/pages/ComponentDetailPage.js +7 -7
  218. package/dist/pages/ComponentDetailPage.js.map +1 -1
  219. package/dist/pages/ComponentsPage.js +12 -9
  220. package/dist/pages/ComponentsPage.js.map +1 -1
  221. package/dist/pages/EnvTopologyPage.js +2 -2
  222. package/dist/pages/EnvTopologyPage.js.map +1 -1
  223. package/dist/pages/GettingStartedPage.js +12 -12
  224. package/dist/pages/GettingStartedPage.js.map +1 -1
  225. package/dist/pages/HealthPage.js +12 -12
  226. package/dist/pages/HealthPage.js.map +1 -1
  227. package/dist/pages/LogsPage.js +4 -4
  228. package/dist/pages/LogsPage.js.map +1 -1
  229. package/dist/pages/NoteDetailsPage.js +5 -5
  230. package/dist/pages/NoteDetailsPage.js.map +1 -1
  231. package/dist/pages/OverviewPage.js +74 -41
  232. package/dist/pages/OverviewPage.js.map +1 -1
  233. package/dist/pages/PluginsCatalogPage.js +6 -3
  234. package/dist/pages/PluginsCatalogPage.js.map +1 -1
  235. package/dist/pages/ScheduledJobDetailPage.js +7 -7
  236. package/dist/pages/ScheduledJobDetailPage.js.map +1 -1
  237. package/dist/pages/SessionDetailsPage.js +21 -21
  238. package/dist/pages/SessionDetailsPage.js.map +1 -1
  239. package/dist/pages/SessionsPage.js +20 -20
  240. package/dist/pages/SessionsPage.js.map +1 -1
  241. package/dist/pages/SettingsPage.js +64 -52
  242. package/dist/pages/SettingsPage.js.map +1 -1
  243. package/dist/pages/SharedSessionPage.js +21 -21
  244. package/dist/pages/SharedSessionPage.js.map +1 -1
  245. package/dist/pages/SharedVibeDeckPage.js +5 -5
  246. package/dist/pages/SharedVibeDeckPage.js.map +1 -1
  247. package/dist/pages/TargetDetailsPage.js +56 -65
  248. package/dist/pages/TargetDetailsPage.js.map +1 -1
  249. package/dist/pages/TargetsPage.js +4 -4
  250. package/dist/pages/TargetsPage.js.map +1 -1
  251. package/dist/pages/TemplateDetailPage.js +10 -10
  252. package/dist/pages/TemplateDetailPage.js.map +1 -1
  253. package/dist/pages/TemplatesPage.js +6 -5
  254. package/dist/pages/TemplatesPage.js.map +1 -1
  255. package/dist/pages/VibeAuditPage.js +3 -3
  256. package/dist/pages/VibeAuditPage.js.map +1 -1
  257. package/dist/pages/VibeCalendarPage.js +3 -3
  258. package/dist/pages/VibeCalendarPage.js.map +1 -1
  259. package/dist/pages/VibeDeckPage.js +3 -3
  260. package/dist/pages/VibeDeckPage.js.map +1 -1
  261. package/dist/pages/VibeDetailsPage.js +15 -15
  262. package/dist/pages/VibeDetailsPage.js.map +1 -1
  263. package/dist/pages/VibeTaskDetailPage.js +5 -5
  264. package/dist/pages/VibeTaskDetailPage.js.map +1 -1
  265. package/dist/pages/VibesPage.js +18 -18
  266. package/dist/pages/VibesPage.js.map +1 -1
  267. package/dist/pages/WebhooksPage.js +11 -11
  268. package/dist/pages/WebhooksPage.js.map +1 -1
  269. package/dist/pages/ai/AIBookmarksPage.js +1 -1
  270. package/dist/pages/ai/AIBookmarksPage.js.map +1 -1
  271. package/dist/pages/ai/AISessionDetailPage.js +6 -6
  272. package/dist/pages/ai/AISessionDetailPage.js.map +1 -1
  273. package/dist/pages/tunnels/AuditTab.js +1 -1
  274. package/dist/pages/tunnels/AuditTab.js.map +1 -1
  275. package/dist/pages/tunnels/DomainsTab.js +1 -1
  276. package/dist/pages/tunnels/DomainsTab.js.map +1 -1
  277. package/dist/pages/tunnels/ProvidersTab.js +1 -1
  278. package/dist/pages/tunnels/ProvidersTab.js.map +1 -1
  279. package/dist/pages/tunnels/SessionsTab.js +1 -1
  280. package/dist/pages/tunnels/SessionsTab.js.map +1 -1
  281. package/dist/pages/tunnels/TunnelDetailsPage.js +3 -3
  282. package/dist/pages/tunnels/TunnelDetailsPage.js.map +1 -1
  283. package/dist/pages/tunnels/UsageTab.js +1 -1
  284. package/dist/pages/tunnels/UsageTab.js.map +1 -1
  285. package/dist/pages/tunnels/tabs/TunnelDomainsTab.js +16 -16
  286. package/dist/pages/tunnels/tabs/TunnelDomainsTab.js.map +1 -1
  287. package/dist/pages/tunnels/tabs/TunnelOverviewTab.js +1 -1
  288. package/dist/pages/tunnels/tabs/TunnelOverviewTab.js.map +1 -1
  289. package/dist/pages/tunnels/tabs/TunnelSessionsTab.js +52 -49
  290. package/dist/pages/tunnels/tabs/TunnelSessionsTab.js.map +1 -1
  291. package/dist/pages/tunnels/tabs/TunnelSettingsTab.js +9 -9
  292. package/dist/pages/tunnels/tabs/TunnelSettingsTab.js.map +1 -1
  293. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"SessionConfigPanel.js","names":[],"sources":["../../../../src/components/agent-manager/detail/SessionConfigPanel.tsx"],"sourcesContent":["/**\n * Session configuration panel.\n *\n * Displays Quick Settings fields for common config options (model,\n * system prompt, max tokens, temperature) above an editable JSON\n * editor. Provides save and terminate actions with a confirmation dialog.\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { Save, Trash2 } from 'lucide-react';\nimport { LoadingState, ErrorState, ConfirmDialog } from '@/components/shared';\nimport {\n fetchSessions,\n updateSessionConfig,\n terminateSession,\n type AISession,\n type AgentRef,\n} from '@/services/aiApi';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface SessionConfigFields {\n model: string;\n systemPrompt: string;\n maxTokens: number | null;\n temperature: number;\n [key: string]: unknown;\n}\n\nfunction parseConfigFields(raw: Record<string, unknown>): SessionConfigFields {\n return {\n model: typeof raw.model === 'string' ? raw.model : '',\n systemPrompt: typeof raw.systemPrompt === 'string' ? raw.systemPrompt : '',\n maxTokens: typeof raw.maxTokens === 'number' ? raw.maxTokens : null,\n temperature: typeof raw.temperature === 'number' ? raw.temperature : 0.7,\n };\n}\n\nfunction mergeFieldsIntoConfig(configText: string, fields: SessionConfigFields): string {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(configText) as Record<string, unknown>;\n } catch {\n parsed = {};\n }\n const merged = { ...parsed };\n if (fields.model) {\n merged.model = fields.model;\n } else {\n delete merged.model;\n }\n if (fields.systemPrompt) {\n merged.systemPrompt = fields.systemPrompt;\n } else {\n delete merged.systemPrompt;\n }\n if (fields.maxTokens !== null) {\n merged.maxTokens = fields.maxTokens;\n } else {\n delete merged.maxTokens;\n }\n merged.temperature = fields.temperature;\n return JSON.stringify(merged, null, 2);\n}\n\ninterface SessionConfigPanelProps {\n sessionId: string;\n agent: AgentRef;\n}\n\nexport function SessionConfigPanel({ sessionId, agent }: SessionConfigPanelProps) {\n const tr = useTr();\n const [session, setSession] = useState<AISession | null>(null);\n const [configText, setConfigText] = useState('');\n const [configFields, setConfigFields] = useState<SessionConfigFields>(parseConfigFields({}));\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [parseError, setParseError] = useState<string | null>(null);\n const [showTerminate, setShowTerminate] = useState(false);\n const [terminating, setTerminating] = useState(false);\n\n const syncFieldsFromJson = useCallback((json: string) => {\n try {\n const parsed = JSON.parse(json) as Record<string, unknown>;\n setConfigFields(parseConfigFields(parsed));\n } catch {\n // Don't update fields if JSON is invalid\n }\n }, []);\n\n const loadConfig = useCallback(async () => {\n try {\n const sessions = await fetchSessions(agent);\n const found = sessions.find((s) => s.id === sessionId);\n if (found) {\n setSession(found);\n const json = JSON.stringify(found.config ?? {}, null, 2);\n setConfigText(json);\n syncFieldsFromJson(json);\n } else {\n // Session row exists in the platform DB but the agent-side\n // session object only materialises after the first message.\n // Treat as empty defaults instead of error so the user can\n // still pre-edit config; the form save flow creates the\n // agent-side row on submit.\n setSession(null);\n setConfigText('{}');\n syncFieldsFromJson('{}');\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load config');\n } finally {\n setLoading(false);\n }\n }, [agent, sessionId, syncFieldsFromJson]);\n\n useEffect(() => {\n setLoading(true);\n loadConfig();\n }, [loadConfig]);\n\n const updateConfigField = useCallback(\n (field: keyof SessionConfigFields, value: string | number | null) => {\n setConfigFields((prev) => {\n const next = { ...prev, [field]: value };\n setConfigText(mergeFieldsIntoConfig(configText, next));\n return next;\n });\n },\n [configText]\n );\n\n const handleSave = useCallback(async () => {\n setParseError(null);\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(configText) as Record<string, unknown>;\n } catch {\n setParseError('Invalid JSON');\n return;\n }\n\n setSaving(true);\n try {\n const updated = await updateSessionConfig(agent, sessionId, parsed);\n setSession(updated);\n const json = JSON.stringify(updated.config ?? {}, null, 2);\n setConfigText(json);\n syncFieldsFromJson(json);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to save config');\n } finally {\n setSaving(false);\n }\n }, [agent, sessionId, configText, syncFieldsFromJson]);\n\n const handleTerminate = useCallback(async () => {\n setTerminating(true);\n try {\n await terminateSession(agent, sessionId);\n setShowTerminate(false);\n // Reload to reflect terminated status\n await loadConfig();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to terminate session');\n } finally {\n setTerminating(false);\n }\n }, [agent, sessionId, loadConfig]);\n\n if (loading) return <LoadingState message=\"Loading config...\" />;\n if (error && !session) return <ErrorState message={error} onRetry={loadConfig} />;\n\n const isTerminated = session?.status === 'terminated';\n\n return (\n <div className=\"flex flex-col h-full\">\n <div className=\"flex-1 p-3 overflow-y-auto\">\n {/* Status */}\n {session && (\n <div className=\"mb-3 flex items-center gap-2\">\n <span className=\"text-xs text-text-tertiary\">Status:</span>\n <span\n className={`px-2 py-0.5 text-xs font-medium rounded-full ${\n session.status === 'active'\n ? 'bg-status-success-bg/10 text-status-success-text'\n : session.status === 'error'\n ? 'bg-status-error-bg/10 text-status-error-text'\n : session.status === 'terminated'\n ? 'bg-bg-sunken text-text-tertiary'\n : 'bg-status-warning-bg/10 text-status-warning-text'\n }`}\n >\n {session.status}\n </span>\n </div>\n )}\n\n {/* Quick Settings */}\n <div className=\"space-y-3 mb-4\">\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agentManager.config.quickSettings', 'Quick Settings')}\n </h4>\n\n {/* Model */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n {tr('vibecontrols.agentManager.config.model', 'Model')}\n </label>\n <input\n aria-label=\"control\"\n type=\"text\"\n value={configFields.model}\n onChange={(e) => updateConfigField('model', e.target.value)}\n placeholder={tr('vibecontrols.agentManager.config.modelPlaceholder', 'Model ID')}\n disabled={isTerminated}\n className=\"w-full px-2 py-1.5 text-sm rounded border border-border-default bg-bg-primary text-text-primary disabled:opacity-50\"\n />\n </div>\n\n {/* System Prompt */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n {tr('vibecontrols.agentManager.config.systemPrompt', 'System Prompt')}\n </label>\n <textarea\n aria-label=\"Instructions for the AI...\"\n value={configFields.systemPrompt}\n onChange={(e) => updateConfigField('systemPrompt', e.target.value)}\n placeholder=\"Instructions for the AI...\"\n rows={2}\n disabled={isTerminated}\n className=\"w-full px-2 py-1.5 text-sm rounded border border-border-default bg-bg-primary text-text-primary resize-none disabled:opacity-50\"\n />\n </div>\n\n {/* Max Tokens */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n {tr('vibecontrols.agentManager.config.maxTokens', 'Max Tokens')}\n </label>\n <input\n aria-label=\"4096\"\n type=\"number\"\n value={configFields.maxTokens ?? ''}\n onChange={(e) =>\n updateConfigField('maxTokens', e.target.value ? Number(e.target.value) : null)\n }\n placeholder=\"4096\"\n disabled={isTerminated}\n className=\"w-full px-2 py-1.5 text-sm rounded border border-border-default bg-bg-primary text-text-primary disabled:opacity-50\"\n />\n </div>\n\n {/* Temperature */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n {tr('vibecontrols.agentManager.config.temperature', 'Temperature (0-1)')}\n </label>\n <input\n aria-label=\"control\"\n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.1\"\n value={configFields.temperature}\n onChange={(e) => updateConfigField('temperature', Number(e.target.value))}\n disabled={isTerminated}\n className=\"w-full\"\n />\n <span className=\"text-xs text-text-tertiary\">{configFields.temperature}</span>\n </div>\n </div>\n\n <div className=\"border-t border-border-default pt-3\">\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2\">\n {tr('vibecontrols.agentManager.config.advancedJson', 'Advanced (JSON)')}\n </h4>\n\n {/* JSON editor */}\n <textarea\n aria-label=\"control\"\n value={configText}\n onChange={(e) => {\n setConfigText(e.target.value);\n setParseError(null);\n syncFieldsFromJson(e.target.value);\n }}\n disabled={isTerminated}\n className=\"w-full h-48 rounded-lg border border-border-default bg-bg-primary px-3 py-2 text-xs font-mono text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors resize-y disabled:opacity-50\"\n spellCheck={false}\n />\n {parseError && <p className=\"mt-1 text-xs text-status-error-text\">{parseError}</p>}\n {error && session && <p className=\"mt-1 text-xs text-status-error-text\">{error}</p>}\n </div>\n </div>\n\n {/* Action buttons */}\n <div className=\"flex items-center gap-2 p-3 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={saving || isTerminated}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n >\n <Save className=\"size-3.5\" />\n {saving\n ? tr('vibecontrols.agentManager.config.saving', 'Saving...')\n : tr('vibecontrols.agentManager.config.save', 'Save')}\n </button>\n <div className=\"flex-1\" />\n <button\n type=\"button\"\n onClick={() => setShowTerminate(true)}\n disabled={isTerminated}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-status-error-bg/10 text-status-error-text hover:bg-status-error-bg/20 disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n >\n <Trash2 className=\"size-3.5\" />\n {tr('vibecontrols.agentManager.config.terminate', 'Terminate')}\n </button>\n </div>\n\n <ConfirmDialog\n isOpen={showTerminate}\n onClose={() => setShowTerminate(false)}\n onConfirm={handleTerminate}\n title={tr('vibecontrols.agentManager.config.terminateSessionTitle', 'Terminate Session')}\n message={tr(\n 'vibecontrols.agentManager.config.terminateConfirm',\n 'Are you sure you want to terminate this session? This action cannot be undone.'\n )}\n confirmLabel={tr('vibecontrols.agentManager.config.terminate', 'Terminate')}\n variant=\"danger\"\n isLoading={terminating}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;AA4BA,SAAS,EAAkB,GAAmD;AAC5E,QAAO;EACL,OAAO,OAAO,EAAI,SAAU,WAAW,EAAI,QAAQ;EACnD,cAAc,OAAO,EAAI,gBAAiB,WAAW,EAAI,eAAe;EACxE,WAAW,OAAO,EAAI,aAAc,WAAW,EAAI,YAAY;EAC/D,aAAa,OAAO,EAAI,eAAgB,WAAW,EAAI,cAAc;EACtE;;AAGH,SAAS,EAAsB,GAAoB,GAAqC;CACtF,IAAI;AACJ,KAAI;AACF,MAAS,KAAK,MAAM,EAAW;SACzB;AACN,MAAS,EAAE;;CAEb,IAAM,IAAS,EAAE,GAAG,GAAQ;AAiB5B,QAhBI,EAAO,QACT,EAAO,QAAQ,EAAO,QAEtB,OAAO,EAAO,OAEZ,EAAO,eACT,EAAO,eAAe,EAAO,eAE7B,OAAO,EAAO,cAEZ,EAAO,cAAc,OAGvB,OAAO,EAAO,YAFd,EAAO,YAAY,EAAO,WAI5B,EAAO,cAAc,EAAO,aACrB,KAAK,UAAU,GAAQ,MAAM,EAAE;;AAQxC,SAAgB,EAAmB,EAAE,cAAW,YAAkC;CAChF,IAAM,IAAK,GAAO,EACZ,CAAC,GAAS,KAAc,EAA2B,KAAK,EACxD,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAc,KAAmB,EAA8B,EAAkB,EAAE,CAAC,CAAC,EACtF,CAAC,GAAS,KAAc,EAAS,GAAK,EACtC,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,CAAC,GAAY,KAAiB,EAAwB,KAAK,EAC3D,CAAC,GAAe,KAAoB,EAAS,GAAM,EACnD,CAAC,GAAa,KAAkB,EAAS,GAAM,EAE/C,IAAqB,GAAa,MAAiB;AACvD,MAAI;AAEF,KAAgB,EADD,KAAK,MAAM,EAAK,CACU,CAAC;UACpC;IAGP,EAAE,CAAC,EAEA,IAAa,EAAY,YAAY;AACzC,MAAI;GAEF,IAAM,KADW,MAAM,EAAc,EAAM,EACpB,MAAM,MAAM,EAAE,OAAO,EAAU;AACtD,OAAI,GAAO;AACT,MAAW,EAAM;IACjB,IAAM,IAAO,KAAK,UAAU,EAAM,UAAU,EAAE,EAAE,MAAM,EAAE;AAExD,IADA,EAAc,EAAK,EACnB,EAAmB,EAAK;SASxB,CAFA,EAAW,KAAK,EAChB,EAAc,KAAK,EACnB,EAAmB,KAAK;WAEnB,GAAK;AACZ,KAAS,aAAe,QAAQ,EAAI,UAAU,wBAAwB;YAC9D;AACR,KAAW,GAAM;;IAElB;EAAC;EAAO;EAAW;EAAmB,CAAC;AAE1C,SAAgB;AAEd,EADA,EAAW,GAAK,EAChB,GAAY;IACX,CAAC,EAAW,CAAC;CAEhB,IAAM,IAAoB,GACvB,GAAkC,MAAkC;AACnE,KAAiB,MAAS;GACxB,IAAM,IAAO;IAAE,GAAG;KAAO,IAAQ;IAAO;AAExC,UADA,EAAc,EAAsB,GAAY,EAAK,CAAC,EAC/C;IACP;IAEJ,CAAC,EAAW,CACb,EAEK,IAAa,EAAY,YAAY;AACzC,IAAc,KAAK;EACnB,IAAI;AACJ,MAAI;AACF,OAAS,KAAK,MAAM,EAAW;UACzB;AACN,KAAc,eAAe;AAC7B;;AAGF,IAAU,GAAK;AACf,MAAI;GACF,IAAM,IAAU,MAAM,EAAoB,GAAO,GAAW,EAAO;AACnE,KAAW,EAAQ;GACnB,IAAM,IAAO,KAAK,UAAU,EAAQ,UAAU,EAAE,EAAE,MAAM,EAAE;AAE1D,GADA,EAAc,EAAK,EACnB,EAAmB,EAAK;WACjB,GAAK;AACZ,KAAS,aAAe,QAAQ,EAAI,UAAU,wBAAwB;YAC9D;AACR,KAAU,GAAM;;IAEjB;EAAC;EAAO;EAAW;EAAY;EAAmB,CAAC,EAEhD,IAAkB,EAAY,YAAY;AAC9C,IAAe,GAAK;AACpB,MAAI;AAIF,GAHA,MAAM,EAAiB,GAAO,EAAU,EACxC,EAAiB,GAAM,EAEvB,MAAM,GAAY;WACX,GAAK;AACZ,KAAS,aAAe,QAAQ,EAAI,UAAU,8BAA8B;YACpE;AACR,KAAe,GAAM;;IAEtB;EAAC;EAAO;EAAW;EAAW,CAAC;AAElC,KAAI,EAAS,QAAO,kBAAC,GAAD,EAAc,SAAQ,qBAAsB,CAAA;AAChE,KAAI,KAAS,CAAC,EAAS,QAAO,kBAAC,GAAD;EAAY,SAAS;EAAO,SAAS;EAAc,CAAA;CAEjF,IAAM,IAAe,GAAS,WAAW;AAEzC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEG,KACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OAAM,WAAU;iBAA6B;OAAc,CAAA,EAC3D,kBAAC,QAAD;OACE,WAAW,gDACT,EAAQ,WAAW,WACf,qDACA,EAAQ,WAAW,UACjB,iDACA,EAAQ,WAAW,eACjB,oCACA;iBAGT,EAAQ;OACJ,CAAA,CACH;;KAIR,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAG,kDAAkD,iBAAiB;QACpE,CAAA;OAGL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,WAAU;kBACd,EAAG,0CAA0C,QAAQ;QAChD,CAAA,EACR,kBAAC,SAAD;QACE,cAAW;QACX,MAAK;QACL,OAAO,EAAa;QACpB,WAAW,MAAM,EAAkB,SAAS,EAAE,OAAO,MAAM;QAC3D,aAAa,EAAG,qDAAqD,WAAW;QAChF,UAAU;QACV,WAAU;QACV,CAAA,CACE,EAAA,CAAA;OAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,WAAU;kBACd,EAAG,iDAAiD,gBAAgB;QAC/D,CAAA,EACR,kBAAC,YAAD;QACE,cAAW;QACX,OAAO,EAAa;QACpB,WAAW,MAAM,EAAkB,gBAAgB,EAAE,OAAO,MAAM;QAClE,aAAY;QACZ,MAAM;QACN,UAAU;QACV,WAAU;QACV,CAAA,CACE,EAAA,CAAA;OAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,WAAU;kBACd,EAAG,8CAA8C,aAAa;QACzD,CAAA,EACR,kBAAC,SAAD;QACE,cAAW;QACX,MAAK;QACL,OAAO,EAAa,aAAa;QACjC,WAAW,MACT,EAAkB,aAAa,EAAE,OAAO,QAAQ,OAAO,EAAE,OAAO,MAAM,GAAG,KAAK;QAEhF,aAAY;QACZ,UAAU;QACV,WAAU;QACV,CAAA,CACE,EAAA,CAAA;OAGN,kBAAC,OAAD,EAAA,UAAA;QACE,kBAAC,SAAD;SAAO,WAAU;mBACd,EAAG,gDAAgD,oBAAoB;SAClE,CAAA;QACR,kBAAC,SAAD;SACE,cAAW;SACX,MAAK;SACL,KAAI;SACJ,KAAI;SACJ,MAAK;SACL,OAAO,EAAa;SACpB,WAAW,MAAM,EAAkB,eAAe,OAAO,EAAE,OAAO,MAAM,CAAC;SACzE,UAAU;SACV,WAAU;SACV,CAAA;QACF,kBAAC,QAAD;SAAM,WAAU;mBAA8B,EAAa;SAAmB,CAAA;QAC1E,EAAA,CAAA;OACF;;KAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAG,iDAAiD,kBAAkB;QACpE,CAAA;OAGL,kBAAC,YAAD;QACE,cAAW;QACX,OAAO;QACP,WAAW,MAAM;AAGf,SAFA,EAAc,EAAE,OAAO,MAAM,EAC7B,EAAc,KAAK,EACnB,EAAmB,EAAE,OAAO,MAAM;;QAEpC,UAAU;QACV,WAAU;QACV,YAAY;QACZ,CAAA;OACD,KAAc,kBAAC,KAAD;QAAG,WAAU;kBAAuC;QAAe,CAAA;OACjF,KAAS,KAAW,kBAAC,KAAD;QAAG,WAAU;kBAAuC;QAAU,CAAA;OAC/E;;KACF;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,UAAU,KAAU;MACpB,WAAU;gBAJZ,CAME,kBAAC,GAAD,EAAM,WAAU,YAAa,CAAA,EAC5B,IACG,EAAG,2CAA2C,YAAY,GAC1D,EAAG,yCAAyC,OAAO,CAChD;;KACT,kBAAC,OAAD,EAAK,WAAU,UAAW,CAAA;KAC1B,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAiB,GAAK;MACrC,UAAU;MACV,WAAU;gBAJZ,CAME,kBAAC,GAAD,EAAQ,WAAU,YAAa,CAAA,EAC9B,EAAG,8CAA8C,YAAY,CACvD;;KACL;;GAEN,kBAAC,GAAD;IACE,QAAQ;IACR,eAAe,EAAiB,GAAM;IACtC,WAAW;IACX,OAAO,EAAG,0DAA0D,oBAAoB;IACxF,SAAS,EACP,qDACA,iFACD;IACD,cAAc,EAAG,8CAA8C,YAAY;IAC3E,SAAQ;IACR,WAAW;IACX,CAAA;GACE"}
1
+ {"version":3,"file":"SessionConfigPanel.js","names":[],"sources":["../../../../src/components/agent-manager/detail/SessionConfigPanel.tsx"],"sourcesContent":["/**\n * Session configuration panel.\n *\n * Displays Quick Settings fields for common config options (model,\n * system prompt, max tokens, temperature) above an editable JSON\n * editor. Provides save and terminate actions with a confirmation dialog.\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { Save, Trash2 } from 'lucide-react';\nimport { LoadingState, ErrorState, ConfirmDialog } from '@/components/shared';\nimport {\n fetchSessions,\n updateSessionConfig,\n terminateSession,\n type AISession,\n type AgentRef,\n} from '@/services/aiApi';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface SessionConfigFields {\n model: string;\n systemPrompt: string;\n maxTokens: number | null;\n temperature: number;\n [key: string]: unknown;\n}\n\nfunction parseConfigFields(raw: Record<string, unknown>): SessionConfigFields {\n return {\n model: typeof raw.model === 'string' ? raw.model : '',\n systemPrompt: typeof raw.systemPrompt === 'string' ? raw.systemPrompt : '',\n maxTokens: typeof raw.maxTokens === 'number' ? raw.maxTokens : null,\n temperature: typeof raw.temperature === 'number' ? raw.temperature : 0.7,\n };\n}\n\nfunction mergeFieldsIntoConfig(configText: string, fields: SessionConfigFields): string {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(configText) as Record<string, unknown>;\n } catch {\n parsed = {};\n }\n const merged = { ...parsed };\n if (fields.model) {\n merged.model = fields.model;\n } else {\n delete merged.model;\n }\n if (fields.systemPrompt) {\n merged.systemPrompt = fields.systemPrompt;\n } else {\n delete merged.systemPrompt;\n }\n if (fields.maxTokens !== null) {\n merged.maxTokens = fields.maxTokens;\n } else {\n delete merged.maxTokens;\n }\n merged.temperature = fields.temperature;\n return JSON.stringify(merged, null, 2);\n}\n\ninterface SessionConfigPanelProps {\n sessionId: string;\n agent: AgentRef;\n}\n\nexport function SessionConfigPanel({ sessionId, agent }: SessionConfigPanelProps) {\n const tr = useTr();\n const [session, setSession] = useState<AISession | null>(null);\n const [configText, setConfigText] = useState('');\n const [configFields, setConfigFields] = useState<SessionConfigFields>(parseConfigFields({}));\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [parseError, setParseError] = useState<string | null>(null);\n const [showTerminate, setShowTerminate] = useState(false);\n const [terminating, setTerminating] = useState(false);\n\n const syncFieldsFromJson = useCallback((json: string) => {\n try {\n const parsed = JSON.parse(json) as Record<string, unknown>;\n setConfigFields(parseConfigFields(parsed));\n } catch {\n // Don't update fields if JSON is invalid\n }\n }, []);\n\n const loadConfig = useCallback(async () => {\n try {\n const sessions = await fetchSessions(agent);\n const found = sessions.find((s) => s.id === sessionId);\n if (found) {\n setSession(found);\n const json = JSON.stringify(found.config ?? {}, null, 2);\n setConfigText(json);\n syncFieldsFromJson(json);\n } else {\n // Session row exists in the platform DB but the agent-side\n // session object only materialises after the first message.\n // Treat as empty defaults instead of error so the user can\n // still pre-edit config; the form save flow creates the\n // agent-side row on submit.\n setSession(null);\n setConfigText('{}');\n syncFieldsFromJson('{}');\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load config');\n } finally {\n setLoading(false);\n }\n }, [agent, sessionId, syncFieldsFromJson]);\n\n useEffect(() => {\n setLoading(true);\n loadConfig();\n }, [loadConfig]);\n\n const updateConfigField = useCallback(\n (field: keyof SessionConfigFields, value: string | number | null) => {\n setConfigFields((prev) => {\n const next = { ...prev, [field]: value };\n setConfigText(mergeFieldsIntoConfig(configText, next));\n return next;\n });\n },\n [configText]\n );\n\n const handleSave = useCallback(async () => {\n setParseError(null);\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(configText) as Record<string, unknown>;\n } catch {\n setParseError('Invalid JSON');\n return;\n }\n\n setSaving(true);\n try {\n const updated = await updateSessionConfig(agent, sessionId, parsed);\n setSession(updated);\n const json = JSON.stringify(updated.config ?? {}, null, 2);\n setConfigText(json);\n syncFieldsFromJson(json);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to save config');\n } finally {\n setSaving(false);\n }\n }, [agent, sessionId, configText, syncFieldsFromJson]);\n\n const handleTerminate = useCallback(async () => {\n setTerminating(true);\n try {\n await terminateSession(agent, sessionId);\n setShowTerminate(false);\n // Reload to reflect terminated status\n await loadConfig();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to terminate session');\n } finally {\n setTerminating(false);\n }\n }, [agent, sessionId, loadConfig]);\n\n if (loading) return <LoadingState message=\"Loading config...\" />;\n if (error && !session) return <ErrorState message={error} onRetry={loadConfig} />;\n\n const isTerminated = session?.status === 'terminated';\n\n return (\n <div className=\"flex flex-col h-full\">\n <div className=\"flex-1 p-3 overflow-y-auto\">\n {/* Status */}\n {session && (\n <div className=\"mb-3 flex items-center gap-2\">\n <span className=\"text-xs text-text-tertiary\">Status:</span>\n <span\n className={`px-2 py-0.5 text-xs font-medium rounded-full ${\n session.status === 'active'\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : session.status === 'error'\n ? 'bg-status-error-bg-subtle text-status-error-text'\n : session.status === 'terminated'\n ? 'bg-bg-sunken text-text-tertiary'\n : 'bg-status-warning-bg-subtle text-status-warning-text'\n }`}\n >\n {session.status}\n </span>\n </div>\n )}\n\n {/* Quick Settings */}\n <div className=\"space-y-3 mb-4\">\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.agentManager.config.quickSettings', 'Quick Settings')}\n </h4>\n\n {/* Model */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n {tr('vibecontrols.agentManager.config.model', 'Model')}\n </label>\n <input\n aria-label=\"control\"\n type=\"text\"\n value={configFields.model}\n onChange={(e) => updateConfigField('model', e.target.value)}\n placeholder={tr('vibecontrols.agentManager.config.modelPlaceholder', 'Model ID')}\n disabled={isTerminated}\n className=\"w-full px-2 py-1.5 text-sm rounded border border-border-default bg-bg-surface text-text-primary disabled:opacity-50\"\n />\n </div>\n\n {/* System Prompt */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n {tr('vibecontrols.agentManager.config.systemPrompt', 'System Prompt')}\n </label>\n <textarea\n aria-label=\"Instructions for the AI...\"\n value={configFields.systemPrompt}\n onChange={(e) => updateConfigField('systemPrompt', e.target.value)}\n placeholder=\"Instructions for the AI...\"\n rows={2}\n disabled={isTerminated}\n className=\"w-full px-2 py-1.5 text-sm rounded border border-border-default bg-bg-surface text-text-primary resize-none disabled:opacity-50\"\n />\n </div>\n\n {/* Max Tokens */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n {tr('vibecontrols.agentManager.config.maxTokens', 'Max Tokens')}\n </label>\n <input\n aria-label=\"4096\"\n type=\"number\"\n value={configFields.maxTokens ?? ''}\n onChange={(e) =>\n updateConfigField('maxTokens', e.target.value ? Number(e.target.value) : null)\n }\n placeholder=\"4096\"\n disabled={isTerminated}\n className=\"w-full px-2 py-1.5 text-sm rounded border border-border-default bg-bg-surface text-text-primary disabled:opacity-50\"\n />\n </div>\n\n {/* Temperature */}\n <div>\n <label className=\"block text-xs text-text-secondary mb-1\">\n {tr('vibecontrols.agentManager.config.temperature', 'Temperature (0-1)')}\n </label>\n <input\n aria-label=\"control\"\n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.1\"\n value={configFields.temperature}\n onChange={(e) => updateConfigField('temperature', Number(e.target.value))}\n disabled={isTerminated}\n className=\"w-full\"\n />\n <span className=\"text-xs text-text-tertiary\">{configFields.temperature}</span>\n </div>\n </div>\n\n <div className=\"border-t border-border-default pt-3\">\n <h4 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wider mb-2\">\n {tr('vibecontrols.agentManager.config.advancedJson', 'Advanced (JSON)')}\n </h4>\n\n {/* JSON editor */}\n <textarea\n aria-label=\"control\"\n value={configText}\n onChange={(e) => {\n setConfigText(e.target.value);\n setParseError(null);\n syncFieldsFromJson(e.target.value);\n }}\n disabled={isTerminated}\n className=\"w-full h-48 rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-xs font-mono text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors resize-y disabled:opacity-50\"\n spellCheck={false}\n />\n {parseError && <p className=\"mt-1 text-xs text-status-error-text\">{parseError}</p>}\n {error && session && <p className=\"mt-1 text-xs text-status-error-text\">{error}</p>}\n </div>\n </div>\n\n {/* Action buttons */}\n <div className=\"flex items-center gap-2 p-3 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={saving || isTerminated}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n >\n <Save className=\"size-3.5\" />\n {saving\n ? tr('vibecontrols.agentManager.config.saving', 'Saving...')\n : tr('vibecontrols.agentManager.config.save', 'Save')}\n </button>\n <div className=\"flex-1\" />\n <button\n type=\"button\"\n onClick={() => setShowTerminate(true)}\n disabled={isTerminated}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-status-error-bg-subtle text-status-error-text hover:bg-bg-sunken disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n >\n <Trash2 className=\"size-3.5\" />\n {tr('vibecontrols.agentManager.config.terminate', 'Terminate')}\n </button>\n </div>\n\n <ConfirmDialog\n isOpen={showTerminate}\n onClose={() => setShowTerminate(false)}\n onConfirm={handleTerminate}\n title={tr('vibecontrols.agentManager.config.terminateSessionTitle', 'Terminate Session')}\n message={tr(\n 'vibecontrols.agentManager.config.terminateConfirm',\n 'Are you sure you want to terminate this session? This action cannot be undone.'\n )}\n confirmLabel={tr('vibecontrols.agentManager.config.terminate', 'Terminate')}\n variant=\"danger\"\n isLoading={terminating}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -16,11 +16,11 @@ var f = [
16
16
  "error",
17
17
  "metadata"
18
18
  ], p = {
19
- input: "bg-action-primary-bg/10 text-action-primary-text",
20
- output: "bg-status-success-bg/10 text-status-success-text",
21
- thinking: "bg-status-warning-bg/10 text-status-warning-text",
19
+ input: "bg-[var(--color-accent-soft)] text-action-primary-bg",
20
+ output: "bg-status-success-bg-subtle text-status-success-text",
21
+ thinking: "bg-status-warning-bg-subtle text-status-warning-text",
22
22
  event: "bg-bg-elevated text-text-secondary",
23
- error: "bg-status-error-bg/10 text-status-error-text",
23
+ error: "bg-status-error-bg-subtle text-status-error-text",
24
24
  metadata: "bg-bg-sunken text-text-tertiary"
25
25
  };
26
26
  function m({ sessionId: m, agent: h }) {
@@ -62,7 +62,7 @@ function m({ sessionId: m, agent: h }) {
62
62
  /* @__PURE__ */ u("button", {
63
63
  type: "button",
64
64
  onClick: () => E((e) => !e),
65
- className: `p-1 rounded transition-colors ${T ? "text-action-primary-text bg-action-primary-bg/10" : "text-text-tertiary hover:text-text-secondary"}`,
65
+ className: `p-1 rounded transition-colors ${T ? "text-action-primary-bg bg-[var(--color-accent-soft)]" : "text-text-tertiary hover:text-text-secondary"}`,
66
66
  title: T ? g("vibecontrols.agentManager.logs.disableAutoRefresh", "Disable auto-refresh") : g("vibecontrols.agentManager.logs.enableAutoRefresh", "Enable auto-refresh"),
67
67
  children: /* @__PURE__ */ u(l, { className: `size-3.5 ${T ? "animate-spin" : ""}` })
68
68
  })
@@ -1 +1 @@
1
- {"version":3,"file":"SessionLogsPanel.js","names":[],"sources":["../../../../src/components/agent-manager/detail/SessionLogsPanel.tsx"],"sourcesContent":["/**\n * Session logs viewer panel.\n *\n * Fetches and displays logs for a session with type filtering\n * and optional auto-refresh.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport { RefreshCw } from 'lucide-react';\nimport { LoadingState, ErrorState } from '@/components/shared';\nimport { fetchSessionLogs, type AISessionLog, type AgentRef } from '@/services/aiApi';\nimport { isSessionNotFoundError } from '@/utils/aiSessionNotFound';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface SessionLogsPanelProps {\n sessionId: string;\n agent: AgentRef;\n}\n\nconst LOG_TYPES = ['input', 'output', 'thinking', 'event', 'error', 'metadata'] as const;\n\nconst TYPE_COLORS: Record<string, string> = {\n input: 'bg-action-primary-bg/10 text-action-primary-text',\n output: 'bg-status-success-bg/10 text-status-success-text',\n thinking: 'bg-status-warning-bg/10 text-status-warning-text',\n event: 'bg-bg-elevated text-text-secondary',\n error: 'bg-status-error-bg/10 text-status-error-text',\n metadata: 'bg-bg-sunken text-text-tertiary',\n};\n\nexport function SessionLogsPanel({ sessionId, agent }: SessionLogsPanelProps) {\n const tr = useTr();\n const [logs, setLogs] = useState<AISessionLog[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [activeTypes, setActiveTypes] = useState<Set<string>>(new Set(LOG_TYPES));\n const [autoRefresh, setAutoRefresh] = useState(false);\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const loadLogs = useCallback(async () => {\n try {\n const data = await fetchSessionLogs(agent, sessionId, { limit: 100 });\n setLogs(data);\n setError(null);\n } catch (err) {\n // Until the user sends the first message the agent has no\n // record of this session, so /api/ai/sessions/:id/logs 404s.\n // Render that as an empty list with a hint, not as an error.\n if (isSessionNotFoundError(err)) {\n setLogs([]);\n setError(null);\n } else {\n setError(err instanceof Error ? err.message : 'Failed to load logs');\n }\n } finally {\n setLoading(false);\n }\n }, [agent, sessionId]);\n\n useEffect(() => {\n setLoading(true);\n loadLogs();\n }, [loadLogs]);\n\n useEffect(() => {\n if (autoRefresh) {\n intervalRef.current = setInterval(loadLogs, 5000);\n }\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n };\n }, [autoRefresh, loadLogs]);\n\n const toggleType = useCallback((type: string) => {\n setActiveTypes((prev) => {\n const next = new Set(prev);\n if (next.has(type)) {\n next.delete(type);\n } else {\n next.add(type);\n }\n return next;\n });\n }, []);\n\n const filteredLogs = logs.filter((log) => activeTypes.has(log.type));\n\n if (loading) return <LoadingState message=\"Loading logs...\" />;\n if (error) return <ErrorState message={error} onRetry={loadLogs} />;\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Filter bar */}\n <div className=\"flex items-center gap-1.5 p-2 border-b border-border-default flex-wrap\">\n {LOG_TYPES.map((type) => (\n <button\n key={type}\n type=\"button\"\n onClick={() => toggleType(type)}\n className={`px-2 py-0.5 text-[10px] font-medium rounded-full transition-colors ${\n activeTypes.has(type)\n ? TYPE_COLORS[type]\n : 'bg-bg-sunken text-text-tertiary opacity-50'\n }`}\n >\n {type}\n </button>\n ))}\n <div className=\"flex-1\" />\n <button\n type=\"button\"\n onClick={() => setAutoRefresh((prev) => !prev)}\n className={`p-1 rounded transition-colors ${\n autoRefresh\n ? 'text-action-primary-text bg-action-primary-bg/10'\n : 'text-text-tertiary hover:text-text-secondary'\n }`}\n title={\n autoRefresh\n ? tr('vibecontrols.agentManager.logs.disableAutoRefresh', 'Disable auto-refresh')\n : tr('vibecontrols.agentManager.logs.enableAutoRefresh', 'Enable auto-refresh')\n }\n >\n <RefreshCw className={`size-3.5 ${autoRefresh ? 'animate-spin' : ''}`} />\n </button>\n </div>\n\n {/* Logs list */}\n <div className=\"flex-1 overflow-y-auto\">\n {filteredLogs.length === 0 ? (\n <div className=\"p-4 text-center text-sm text-text-tertiary\">\n {logs.length === 0\n ? tr(\n 'vibecontrols.agentManager.logs.empty',\n 'Logs will appear here after you send the first message.'\n )\n : tr(\n 'vibecontrols.agentManager.logs.noLogsMatch',\n 'No logs match the selected filters'\n )}\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {filteredLogs.map((log) => (\n <div key={log.id} className=\"px-3 py-2\">\n <div className=\"flex items-center gap-2 mb-1\">\n <span\n className={`px-1.5 py-0.5 text-[10px] font-medium rounded ${TYPE_COLORS[log.type] ?? 'bg-bg-sunken text-text-tertiary'}`}\n >\n {log.type}\n </span>\n <span className=\"text-[10px] text-text-tertiary\">\n {new Date(log.timestamp).toLocaleTimeString()}\n </span>\n {log.tokenCount != null && (\n <span className=\"text-[10px] text-text-tertiary ml-auto\">\n {log.tokenCount} tokens\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary break-words line-clamp-3\">\n {log.message || log.content}\n </p>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SessionLogsPanel.js","names":[],"sources":["../../../../src/components/agent-manager/detail/SessionLogsPanel.tsx"],"sourcesContent":["/**\n * Session logs viewer panel.\n *\n * Fetches and displays logs for a session with type filtering\n * and optional auto-refresh.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport { RefreshCw } from 'lucide-react';\nimport { LoadingState, ErrorState } from '@/components/shared';\nimport { fetchSessionLogs, type AISessionLog, type AgentRef } from '@/services/aiApi';\nimport { isSessionNotFoundError } from '@/utils/aiSessionNotFound';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface SessionLogsPanelProps {\n sessionId: string;\n agent: AgentRef;\n}\n\nconst LOG_TYPES = ['input', 'output', 'thinking', 'event', 'error', 'metadata'] as const;\n\nconst TYPE_COLORS: Record<string, string> = {\n input: 'bg-[var(--color-accent-soft)] text-action-primary-bg',\n output: 'bg-status-success-bg-subtle text-status-success-text',\n thinking: 'bg-status-warning-bg-subtle text-status-warning-text',\n event: 'bg-bg-elevated text-text-secondary',\n error: 'bg-status-error-bg-subtle text-status-error-text',\n metadata: 'bg-bg-sunken text-text-tertiary',\n};\n\nexport function SessionLogsPanel({ sessionId, agent }: SessionLogsPanelProps) {\n const tr = useTr();\n const [logs, setLogs] = useState<AISessionLog[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [activeTypes, setActiveTypes] = useState<Set<string>>(new Set(LOG_TYPES));\n const [autoRefresh, setAutoRefresh] = useState(false);\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const loadLogs = useCallback(async () => {\n try {\n const data = await fetchSessionLogs(agent, sessionId, { limit: 100 });\n setLogs(data);\n setError(null);\n } catch (err) {\n // Until the user sends the first message the agent has no\n // record of this session, so /api/ai/sessions/:id/logs 404s.\n // Render that as an empty list with a hint, not as an error.\n if (isSessionNotFoundError(err)) {\n setLogs([]);\n setError(null);\n } else {\n setError(err instanceof Error ? err.message : 'Failed to load logs');\n }\n } finally {\n setLoading(false);\n }\n }, [agent, sessionId]);\n\n useEffect(() => {\n setLoading(true);\n loadLogs();\n }, [loadLogs]);\n\n useEffect(() => {\n if (autoRefresh) {\n intervalRef.current = setInterval(loadLogs, 5000);\n }\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n };\n }, [autoRefresh, loadLogs]);\n\n const toggleType = useCallback((type: string) => {\n setActiveTypes((prev) => {\n const next = new Set(prev);\n if (next.has(type)) {\n next.delete(type);\n } else {\n next.add(type);\n }\n return next;\n });\n }, []);\n\n const filteredLogs = logs.filter((log) => activeTypes.has(log.type));\n\n if (loading) return <LoadingState message=\"Loading logs...\" />;\n if (error) return <ErrorState message={error} onRetry={loadLogs} />;\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Filter bar */}\n <div className=\"flex items-center gap-1.5 p-2 border-b border-border-default flex-wrap\">\n {LOG_TYPES.map((type) => (\n <button\n key={type}\n type=\"button\"\n onClick={() => toggleType(type)}\n className={`px-2 py-0.5 text-[10px] font-medium rounded-full transition-colors ${\n activeTypes.has(type)\n ? TYPE_COLORS[type]\n : 'bg-bg-sunken text-text-tertiary opacity-50'\n }`}\n >\n {type}\n </button>\n ))}\n <div className=\"flex-1\" />\n <button\n type=\"button\"\n onClick={() => setAutoRefresh((prev) => !prev)}\n className={`p-1 rounded transition-colors ${\n autoRefresh\n ? 'text-action-primary-bg bg-[var(--color-accent-soft)]'\n : 'text-text-tertiary hover:text-text-secondary'\n }`}\n title={\n autoRefresh\n ? tr('vibecontrols.agentManager.logs.disableAutoRefresh', 'Disable auto-refresh')\n : tr('vibecontrols.agentManager.logs.enableAutoRefresh', 'Enable auto-refresh')\n }\n >\n <RefreshCw className={`size-3.5 ${autoRefresh ? 'animate-spin' : ''}`} />\n </button>\n </div>\n\n {/* Logs list */}\n <div className=\"flex-1 overflow-y-auto\">\n {filteredLogs.length === 0 ? (\n <div className=\"p-4 text-center text-sm text-text-tertiary\">\n {logs.length === 0\n ? tr(\n 'vibecontrols.agentManager.logs.empty',\n 'Logs will appear here after you send the first message.'\n )\n : tr(\n 'vibecontrols.agentManager.logs.noLogsMatch',\n 'No logs match the selected filters'\n )}\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {filteredLogs.map((log) => (\n <div key={log.id} className=\"px-3 py-2\">\n <div className=\"flex items-center gap-2 mb-1\">\n <span\n className={`px-1.5 py-0.5 text-[10px] font-medium rounded ${TYPE_COLORS[log.type] ?? 'bg-bg-sunken text-text-tertiary'}`}\n >\n {log.type}\n </span>\n <span className=\"text-[10px] text-text-tertiary\">\n {new Date(log.timestamp).toLocaleTimeString()}\n </span>\n {log.tokenCount != null && (\n <span className=\"text-[10px] text-text-tertiary ml-auto\">\n {log.tokenCount} tokens\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary break-words line-clamp-3\">\n {log.message || log.content}\n </p>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -39,7 +39,7 @@ function f({ agent: f }) {
39
39
  className: "flex-1 text-sm font-medium text-text-primary truncate",
40
40
  children: e.displayName || e.name
41
41
  }), e.installed ? /* @__PURE__ */ d("span", {
42
- className: "flex items-center gap-1 px-1.5 py-0.5 text-[10px] font-medium rounded-full bg-status-success-bg/10 text-status-success-text",
42
+ className: "flex items-center gap-1 px-1.5 py-0.5 text-[10px] font-medium rounded-full bg-status-success-bg-subtle text-status-success-text",
43
43
  children: [/* @__PURE__ */ u(s, { className: "size-3" }), p("vibecontrols.agentManager.tools.installed", "Installed")]
44
44
  }) : /* @__PURE__ */ d("span", {
45
45
  className: "flex items-center gap-1 px-1.5 py-0.5 text-[10px] font-medium rounded-full bg-bg-sunken text-text-tertiary",
@@ -1 +1 @@
1
- {"version":3,"file":"SessionToolsPanel.js","names":[],"sources":["../../../../src/components/agent-manager/detail/SessionToolsPanel.tsx"],"sourcesContent":["/**\n * Session tools list panel.\n *\n * Fetches available tools from the agent and renders them as a list\n * with installed status badges and version info.\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { Wrench, CheckCircle, Circle } from 'lucide-react';\nimport { LoadingState, ErrorState } from '@/components/shared';\nimport { fetchTools, type AITool, type AgentRef } from '@/services/aiApi';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface SessionToolsPanelProps {\n agent: AgentRef;\n}\n\nexport function SessionToolsPanel({ agent }: SessionToolsPanelProps) {\n const tr = useTr();\n const [tools, setTools] = useState<AITool[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const loadTools = useCallback(async () => {\n try {\n const data = await fetchTools(agent);\n setTools(data);\n setError(null);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load tools');\n } finally {\n setLoading(false);\n }\n }, [agent]);\n\n useEffect(() => {\n setLoading(true);\n loadTools();\n }, [loadTools]);\n\n if (loading) return <LoadingState message=\"Loading tools...\" />;\n if (error) return <ErrorState message={error} onRetry={loadTools} />;\n\n if (tools.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center p-6 text-center\">\n <Wrench className=\"size-8 text-text-tertiary mb-2\" />\n <p className=\"text-sm text-text-tertiary\">\n {tr('vibecontrols.agentManager.tools.noToolsAvailable', 'No tools available')}\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"divide-y divide-border-default\">\n {tools.map((tool) => (\n <div key={tool.name} className=\"px-3 py-2.5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex-1 text-sm font-medium text-text-primary truncate\">\n {tool.displayName || tool.name}\n </span>\n {tool.installed ? (\n <span className=\"flex items-center gap-1 px-1.5 py-0.5 text-[10px] font-medium rounded-full bg-status-success-bg/10 text-status-success-text\">\n <CheckCircle className=\"size-3\" />\n {tr('vibecontrols.agentManager.tools.installed', 'Installed')}\n </span>\n ) : (\n <span className=\"flex items-center gap-1 px-1.5 py-0.5 text-[10px] font-medium rounded-full bg-bg-sunken text-text-tertiary\">\n <Circle className=\"size-3\" />\n {tr('vibecontrols.agentManager.tools.notInstalled', 'Not installed')}\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary mt-0.5 line-clamp-2\">{tool.description}</p>\n {tool.version && (\n <span className=\"text-[10px] text-text-tertiary mt-0.5 inline-block\">\n v{tool.version}\n </span>\n )}\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAiBA,SAAgB,EAAkB,EAAE,YAAiC;CACnE,IAAM,IAAK,GAAO,EACZ,CAAC,GAAO,KAAY,EAAmB,EAAE,CAAC,EAC1C,CAAC,GAAS,KAAc,EAAS,GAAK,EACtC,CAAC,GAAO,KAAY,EAAwB,KAAK,EAEjD,IAAY,EAAY,YAAY;AACxC,MAAI;AAGF,GADA,EADa,MAAM,EAAW,EAAM,CACtB,EACd,EAAS,KAAK;WACP,GAAK;AACZ,KAAS,aAAe,QAAQ,EAAI,UAAU,uBAAuB;YAC7D;AACR,KAAW,GAAM;;IAElB,CAAC,EAAM,CAAC;AAqBX,QAnBA,QAAgB;AAEd,EADA,EAAW,GAAK,EAChB,GAAW;IACV,CAAC,EAAU,CAAC,EAEX,IAAgB,kBAAC,GAAD,EAAc,SAAQ,oBAAqB,CAAA,GAC3D,IAAc,kBAAC,GAAD;EAAY,SAAS;EAAO,SAAS;EAAa,CAAA,GAEhE,EAAM,WAAW,IAEjB,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,kCAAmC,CAAA,EACrD,kBAAC,KAAD;GAAG,WAAU;aACV,EAAG,oDAAoD,qBAAqB;GAC3E,CAAA,CACA;MAKR,kBAAC,OAAD;EAAK,WAAU;YACZ,EAAM,KAAK,MACV,kBAAC,OAAD;GAAqB,WAAU;aAA/B;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAK,eAAe,EAAK;MACrB,CAAA,EACN,EAAK,YACJ,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,kBAAC,GAAD,EAAa,WAAU,UAAW,CAAA,EACjC,EAAG,6CAA6C,YAAY,CACxD;UAEP,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA,EAC5B,EAAG,gDAAgD,gBAAgB,CAC/D;QAEL;;IACN,kBAAC,KAAD;KAAG,WAAU;eAAmD,EAAK;KAAgB,CAAA;IACpF,EAAK,WACJ,kBAAC,QAAD;KAAM,WAAU;eAAhB,CAAqE,KACjE,EAAK,QACF;;IAEL;KAvBI,EAAK,KAuBT,CACN;EACE,CAAA"}
1
+ {"version":3,"file":"SessionToolsPanel.js","names":[],"sources":["../../../../src/components/agent-manager/detail/SessionToolsPanel.tsx"],"sourcesContent":["/**\n * Session tools list panel.\n *\n * Fetches available tools from the agent and renders them as a list\n * with installed status badges and version info.\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { Wrench, CheckCircle, Circle } from 'lucide-react';\nimport { LoadingState, ErrorState } from '@/components/shared';\nimport { fetchTools, type AITool, type AgentRef } from '@/services/aiApi';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface SessionToolsPanelProps {\n agent: AgentRef;\n}\n\nexport function SessionToolsPanel({ agent }: SessionToolsPanelProps) {\n const tr = useTr();\n const [tools, setTools] = useState<AITool[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const loadTools = useCallback(async () => {\n try {\n const data = await fetchTools(agent);\n setTools(data);\n setError(null);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load tools');\n } finally {\n setLoading(false);\n }\n }, [agent]);\n\n useEffect(() => {\n setLoading(true);\n loadTools();\n }, [loadTools]);\n\n if (loading) return <LoadingState message=\"Loading tools...\" />;\n if (error) return <ErrorState message={error} onRetry={loadTools} />;\n\n if (tools.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center p-6 text-center\">\n <Wrench className=\"size-8 text-text-tertiary mb-2\" />\n <p className=\"text-sm text-text-tertiary\">\n {tr('vibecontrols.agentManager.tools.noToolsAvailable', 'No tools available')}\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"divide-y divide-border-default\">\n {tools.map((tool) => (\n <div key={tool.name} className=\"px-3 py-2.5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex-1 text-sm font-medium text-text-primary truncate\">\n {tool.displayName || tool.name}\n </span>\n {tool.installed ? (\n <span className=\"flex items-center gap-1 px-1.5 py-0.5 text-[10px] font-medium rounded-full bg-status-success-bg-subtle text-status-success-text\">\n <CheckCircle className=\"size-3\" />\n {tr('vibecontrols.agentManager.tools.installed', 'Installed')}\n </span>\n ) : (\n <span className=\"flex items-center gap-1 px-1.5 py-0.5 text-[10px] font-medium rounded-full bg-bg-sunken text-text-tertiary\">\n <Circle className=\"size-3\" />\n {tr('vibecontrols.agentManager.tools.notInstalled', 'Not installed')}\n </span>\n )}\n </div>\n <p className=\"text-xs text-text-secondary mt-0.5 line-clamp-2\">{tool.description}</p>\n {tool.version && (\n <span className=\"text-[10px] text-text-tertiary mt-0.5 inline-block\">\n v{tool.version}\n </span>\n )}\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAiBA,SAAgB,EAAkB,EAAE,YAAiC;CACnE,IAAM,IAAK,GAAO,EACZ,CAAC,GAAO,KAAY,EAAmB,EAAE,CAAC,EAC1C,CAAC,GAAS,KAAc,EAAS,GAAK,EACtC,CAAC,GAAO,KAAY,EAAwB,KAAK,EAEjD,IAAY,EAAY,YAAY;AACxC,MAAI;AAGF,GADA,EADa,MAAM,EAAW,EAAM,CACtB,EACd,EAAS,KAAK;WACP,GAAK;AACZ,KAAS,aAAe,QAAQ,EAAI,UAAU,uBAAuB;YAC7D;AACR,KAAW,GAAM;;IAElB,CAAC,EAAM,CAAC;AAqBX,QAnBA,QAAgB;AAEd,EADA,EAAW,GAAK,EAChB,GAAW;IACV,CAAC,EAAU,CAAC,EAEX,IAAgB,kBAAC,GAAD,EAAc,SAAQ,oBAAqB,CAAA,GAC3D,IAAc,kBAAC,GAAD;EAAY,SAAS;EAAO,SAAS;EAAa,CAAA,GAEhE,EAAM,WAAW,IAEjB,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,kCAAmC,CAAA,EACrD,kBAAC,KAAD;GAAG,WAAU;aACV,EAAG,oDAAoD,qBAAqB;GAC3E,CAAA,CACA;MAKR,kBAAC,OAAD;EAAK,WAAU;YACZ,EAAM,KAAK,MACV,kBAAC,OAAD;GAAqB,WAAU;aAA/B;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAK,eAAe,EAAK;MACrB,CAAA,EACN,EAAK,YACJ,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,kBAAC,GAAD,EAAa,WAAU,UAAW,CAAA,EACjC,EAAG,6CAA6C,YAAY,CACxD;UAEP,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA,EAC5B,EAAG,gDAAgD,gBAAgB,CAC/D;QAEL;;IACN,kBAAC,KAAD;KAAG,WAAU;eAAmD,EAAK;KAAgB,CAAA;IACpF,EAAK,WACJ,kBAAC,QAAD;KAAM,WAAU;eAAhB,CAAqE,KACjE,EAAK,QACF;;IAEL;KAvBI,EAAK,KAuBT,CACN;EACE,CAAA"}
@@ -172,7 +172,7 @@ function y({ agent: _, isOpen: y, onClose: b }) {
172
172
  value: x,
173
173
  onChange: (e) => S(e.target.value),
174
174
  placeholder: "My session",
175
- className: "w-full rounded-md border border-border-default bg-bg-primary px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors"
175
+ className: "w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors"
176
176
  })] }),
177
177
  /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
178
178
  className: "block text-sm font-medium text-text-primary mb-1",
@@ -203,7 +203,7 @@ function y({ agent: _, isOpen: y, onClose: b }) {
203
203
  value: A,
204
204
  onChange: (e) => j(e.target.value),
205
205
  placeholder: "e.g. frontend, bugfix, prototype",
206
- className: "w-full rounded-md border border-border-default bg-bg-primary px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors"
206
+ className: "w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors"
207
207
  })] }),
208
208
  I && /* @__PURE__ */ h("p", {
209
209
  className: "text-xs text-status-error-text",
@@ -1 +1 @@
1
- {"version":3,"file":"NewSessionDialog.js","names":[],"sources":["../../../../src/components/agent-manager/dialogs/NewSessionDialog.tsx"],"sourcesContent":["/**\n * New session creation dialog.\n *\n * Modal overlay with fields for session name, SDK, model, and mode.\n * Creates a session via aiApi and opens it as a new tab.\n */\n\nimport { useState, useCallback, useEffect } from 'react';\nimport { X } from 'lucide-react';\nimport { motion, AnimatePresence, useReducedMotion } from 'framer-motion';\nimport { createSession, fetchProviders, type AIProvider, type AgentRef } from '@/services/aiApi';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { ModelSelector } from '../controls/ModelSelector';\nimport { SdkSelector } from '../controls/SdkSelector';\nimport { GENERIC_MODEL_FALLBACK, resolveProviderDefaultModel } from '../modelDefaults';\nimport type { AgentSdkType, ProviderMode } from '@/types/agentManager';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface NewSessionDialogProps {\n agent: AgentRef;\n isOpen: boolean;\n onClose: () => void;\n}\n\nconst SDK_VALUES = new Set<string>([\n 'claude',\n 'codex',\n 'gemini',\n 'opencode',\n 'cursor',\n 'openrouter',\n 'minimax',\n 'ollama',\n 'openai-compat',\n 'goose',\n 'amp',\n 'copilot',\n 'crush',\n 'pi',\n 'aider',\n 'plandex',\n]);\n\nfunction providerSdk(provider: AIProvider): AgentSdkType | null {\n const value = (provider.name || provider.pluginName).replace(/^ai-/, '');\n return SDK_VALUES.has(value) ? (value as AgentSdkType) : null;\n}\n\nexport function NewSessionDialog({ agent, isOpen, onClose }: NewSessionDialogProps) {\n const [name, setName] = useState('');\n const [sdk, setSdk] = useState<AgentSdkType>('claude');\n const [model, setModel] = useState(GENERIC_MODEL_FALLBACK);\n const [providers, setProviders] = useState<AIProvider[]>([]);\n\n // Auto-update default model when SDK changes\n const handleSdkChange = (newSdk: AgentSdkType) => {\n setSdk(newSdk);\n void resolveProviderDefaultModel(agent, newSdk, model)\n .then(setModel)\n .catch(() => setModel(model || GENERIC_MODEL_FALLBACK));\n };\n const [tags, setTags] = useState('');\n const [mode, setMode] = useState<ProviderMode>('sdk');\n const [creating, setCreating] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const openSession = useAgentManagerStore((s) => s.openSession);\n const bumpSessionsRefresh = useAgentManagerStore((s) => s.bumpSessionsRefresh);\n const activeChatContext = useAgentManagerStore((s) => s.activeChatContext);\n const setSessionSdk = useAgentManagerStore((s) => s.setSessionSdk);\n const setSessionModel = useAgentManagerStore((s) => s.setSessionModel);\n const setSessionMode = useAgentManagerStore((s) => s.setSessionMode);\n const shouldReduceMotion = useReducedMotion();\n const tr = useTr();\n\n useEffect(() => {\n if (!isOpen) return;\n\n let cancelled = false;\n void fetchProviders(agent)\n .then((items) => {\n if (!cancelled) setProviders(items);\n })\n .catch(() => {\n if (!cancelled) setProviders([]);\n });\n\n return () => {\n cancelled = true;\n };\n }, [agent, isOpen]);\n\n useEffect(() => {\n if (!isOpen || providers.length === 0) return;\n\n const current = providers.find((provider) => providerSdk(provider) === sdk);\n const fallback = providers.find((provider) => provider.isDefault) ?? providers[0];\n const nextProvider = current ?? fallback;\n if (!nextProvider) return;\n\n const nextSdk = providerSdk(nextProvider);\n if (nextSdk && !current) {\n setSdk(nextSdk);\n }\n\n const supportedModes = nextProvider.supportedModes ?? [];\n if (supportedModes.length > 0 && !supportedModes.includes(mode)) {\n setMode(nextProvider.defaultMode ?? nextProvider.currentMode ?? supportedModes[0] ?? 'cli');\n }\n }, [isOpen, mode, providers, sdk]);\n\n useEffect(() => {\n if (!isOpen) return;\n\n let cancelled = false;\n void resolveProviderDefaultModel(agent, sdk, GENERIC_MODEL_FALLBACK)\n .then((nextModel) => {\n if (!cancelled) setModel(nextModel);\n })\n .catch(() => {\n if (!cancelled) setModel((current) => current || GENERIC_MODEL_FALLBACK);\n });\n\n return () => {\n cancelled = true;\n };\n }, [agent, isOpen, sdk]);\n\n const handleCreate = useCallback(async () => {\n if (!name.trim()) return;\n setCreating(true);\n setError(null);\n try {\n const session = await createSession(agent, {\n name: name.trim(),\n agentType: sdk,\n config: {\n model,\n mode,\n // New sessions default to the safe autonomy level; users adjust it\n // per-session via the Autonomy control in the chat toolbar.\n permissionMode: 'acceptEdits',\n tags: tags\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean),\n },\n // Stamp the active \"bucket\" context (agent/vibe) onto the session.\n agentId: agent.id,\n contextLevel: activeChatContext.contextLevel,\n vibeId: activeChatContext.vibeId,\n });\n openSession(session.id, session.name);\n bumpSessionsRefresh();\n setSessionSdk(session.id, sdk);\n setSessionModel(session.id, model);\n setSessionMode(session.id, mode);\n // Reset form\n setName('');\n setSdk('claude');\n setModel(GENERIC_MODEL_FALLBACK);\n setMode('sdk');\n setTags('');\n onClose();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create session');\n } finally {\n setCreating(false);\n }\n }, [\n agent,\n name,\n sdk,\n model,\n mode,\n tags,\n openSession,\n bumpSessionsRefresh,\n activeChatContext,\n setSessionSdk,\n setSessionModel,\n setSessionMode,\n onClose,\n ]);\n\n if (!isOpen) return null;\n\n return (\n <AnimatePresence>\n {isOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <motion.div\n className=\"absolute inset-0 bg-bg-overlay\"\n initial={shouldReduceMotion ? undefined : { opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n onClick={onClose}\n />\n <motion.div\n className=\"relative w-full max-w-md mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl\"\n initial={shouldReduceMotion ? undefined : { opacity: 0, scale: 0.95, y: 8 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: 8 }}\n transition={{ duration: 0.2, ease: [0.2, 0, 0, 1] }}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.agentManager.dialogs.newSession', 'New Session')}\n </h3>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n\n {/* Body */}\n <div className=\"px-6 py-4 space-y-4\">\n {/* Name */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.agentManager.dialogs.name', 'Name')}\n </label>\n <input\n aria-label=\"My session\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"My session\"\n className=\"w-full rounded-md border border-border-default bg-bg-primary px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors\"\n />\n </div>\n\n {/* SDK + Mode */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.agentManager.dialogs.sdkAndMode', 'SDK & Mode')}\n </label>\n <SdkSelector\n sdk={sdk}\n mode={mode}\n providers={providers}\n onSdkChange={handleSdkChange}\n onModeChange={setMode}\n />\n </div>\n\n {/* Model */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.agentManager.dialogs.model', 'Model')}\n </label>\n <ModelSelector\n value={model}\n onChange={setModel}\n provider={sdk}\n agentId={agent.id}\n profile={agent.profile}\n />\n </div>\n\n {/* Tags */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.agentManager.dialogs.tagsLabel', 'Tags (comma-separated)')}\n </label>\n <input\n aria-label=\"e.g. frontend, bugfix, prototype\"\n type=\"text\"\n value={tags}\n onChange={(e) => setTags(e.target.value)}\n placeholder=\"e.g. frontend, bugfix, prototype\"\n className=\"w-full rounded-md border border-border-default bg-bg-primary px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors\"\n />\n </div>\n\n {error && <p className=\"text-xs text-status-error-text\">{error}</p>}\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border-default bg-bg-sunken rounded-b-lg\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={creating}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors disabled:opacity-50\"\n >\n {tr('vibecontrols.agentManager.dialogs.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleCreate}\n disabled={creating || !name.trim()}\n className=\"px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {creating\n ? tr('vibecontrols.agentManager.dialogs.creating', 'Creating...')\n : tr('vibecontrols.agentManager.dialogs.create', 'Create')}\n </button>\n </div>\n </motion.div>\n </div>\n )}\n </AnimatePresence>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"NewSessionDialog.js","names":[],"sources":["../../../../src/components/agent-manager/dialogs/NewSessionDialog.tsx"],"sourcesContent":["/**\n * New session creation dialog.\n *\n * Modal overlay with fields for session name, SDK, model, and mode.\n * Creates a session via aiApi and opens it as a new tab.\n */\n\nimport { useState, useCallback, useEffect } from 'react';\nimport { X } from 'lucide-react';\nimport { motion, AnimatePresence, useReducedMotion } from 'framer-motion';\nimport { createSession, fetchProviders, type AIProvider, type AgentRef } from '@/services/aiApi';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { ModelSelector } from '../controls/ModelSelector';\nimport { SdkSelector } from '../controls/SdkSelector';\nimport { GENERIC_MODEL_FALLBACK, resolveProviderDefaultModel } from '../modelDefaults';\nimport type { AgentSdkType, ProviderMode } from '@/types/agentManager';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface NewSessionDialogProps {\n agent: AgentRef;\n isOpen: boolean;\n onClose: () => void;\n}\n\nconst SDK_VALUES = new Set<string>([\n 'claude',\n 'codex',\n 'gemini',\n 'opencode',\n 'cursor',\n 'openrouter',\n 'minimax',\n 'ollama',\n 'openai-compat',\n 'goose',\n 'amp',\n 'copilot',\n 'crush',\n 'pi',\n 'aider',\n 'plandex',\n]);\n\nfunction providerSdk(provider: AIProvider): AgentSdkType | null {\n const value = (provider.name || provider.pluginName).replace(/^ai-/, '');\n return SDK_VALUES.has(value) ? (value as AgentSdkType) : null;\n}\n\nexport function NewSessionDialog({ agent, isOpen, onClose }: NewSessionDialogProps) {\n const [name, setName] = useState('');\n const [sdk, setSdk] = useState<AgentSdkType>('claude');\n const [model, setModel] = useState(GENERIC_MODEL_FALLBACK);\n const [providers, setProviders] = useState<AIProvider[]>([]);\n\n // Auto-update default model when SDK changes\n const handleSdkChange = (newSdk: AgentSdkType) => {\n setSdk(newSdk);\n void resolveProviderDefaultModel(agent, newSdk, model)\n .then(setModel)\n .catch(() => setModel(model || GENERIC_MODEL_FALLBACK));\n };\n const [tags, setTags] = useState('');\n const [mode, setMode] = useState<ProviderMode>('sdk');\n const [creating, setCreating] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const openSession = useAgentManagerStore((s) => s.openSession);\n const bumpSessionsRefresh = useAgentManagerStore((s) => s.bumpSessionsRefresh);\n const activeChatContext = useAgentManagerStore((s) => s.activeChatContext);\n const setSessionSdk = useAgentManagerStore((s) => s.setSessionSdk);\n const setSessionModel = useAgentManagerStore((s) => s.setSessionModel);\n const setSessionMode = useAgentManagerStore((s) => s.setSessionMode);\n const shouldReduceMotion = useReducedMotion();\n const tr = useTr();\n\n useEffect(() => {\n if (!isOpen) return;\n\n let cancelled = false;\n void fetchProviders(agent)\n .then((items) => {\n if (!cancelled) setProviders(items);\n })\n .catch(() => {\n if (!cancelled) setProviders([]);\n });\n\n return () => {\n cancelled = true;\n };\n }, [agent, isOpen]);\n\n useEffect(() => {\n if (!isOpen || providers.length === 0) return;\n\n const current = providers.find((provider) => providerSdk(provider) === sdk);\n const fallback = providers.find((provider) => provider.isDefault) ?? providers[0];\n const nextProvider = current ?? fallback;\n if (!nextProvider) return;\n\n const nextSdk = providerSdk(nextProvider);\n if (nextSdk && !current) {\n setSdk(nextSdk);\n }\n\n const supportedModes = nextProvider.supportedModes ?? [];\n if (supportedModes.length > 0 && !supportedModes.includes(mode)) {\n setMode(nextProvider.defaultMode ?? nextProvider.currentMode ?? supportedModes[0] ?? 'cli');\n }\n }, [isOpen, mode, providers, sdk]);\n\n useEffect(() => {\n if (!isOpen) return;\n\n let cancelled = false;\n void resolveProviderDefaultModel(agent, sdk, GENERIC_MODEL_FALLBACK)\n .then((nextModel) => {\n if (!cancelled) setModel(nextModel);\n })\n .catch(() => {\n if (!cancelled) setModel((current) => current || GENERIC_MODEL_FALLBACK);\n });\n\n return () => {\n cancelled = true;\n };\n }, [agent, isOpen, sdk]);\n\n const handleCreate = useCallback(async () => {\n if (!name.trim()) return;\n setCreating(true);\n setError(null);\n try {\n const session = await createSession(agent, {\n name: name.trim(),\n agentType: sdk,\n config: {\n model,\n mode,\n // New sessions default to the safe autonomy level; users adjust it\n // per-session via the Autonomy control in the chat toolbar.\n permissionMode: 'acceptEdits',\n tags: tags\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean),\n },\n // Stamp the active \"bucket\" context (agent/vibe) onto the session.\n agentId: agent.id,\n contextLevel: activeChatContext.contextLevel,\n vibeId: activeChatContext.vibeId,\n });\n openSession(session.id, session.name);\n bumpSessionsRefresh();\n setSessionSdk(session.id, sdk);\n setSessionModel(session.id, model);\n setSessionMode(session.id, mode);\n // Reset form\n setName('');\n setSdk('claude');\n setModel(GENERIC_MODEL_FALLBACK);\n setMode('sdk');\n setTags('');\n onClose();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create session');\n } finally {\n setCreating(false);\n }\n }, [\n agent,\n name,\n sdk,\n model,\n mode,\n tags,\n openSession,\n bumpSessionsRefresh,\n activeChatContext,\n setSessionSdk,\n setSessionModel,\n setSessionMode,\n onClose,\n ]);\n\n if (!isOpen) return null;\n\n return (\n <AnimatePresence>\n {isOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <motion.div\n className=\"absolute inset-0 bg-bg-overlay\"\n initial={shouldReduceMotion ? undefined : { opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n onClick={onClose}\n />\n <motion.div\n className=\"relative w-full max-w-md mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl\"\n initial={shouldReduceMotion ? undefined : { opacity: 0, scale: 0.95, y: 8 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: 8 }}\n transition={{ duration: 0.2, ease: [0.2, 0, 0, 1] }}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.agentManager.dialogs.newSession', 'New Session')}\n </h3>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n\n {/* Body */}\n <div className=\"px-6 py-4 space-y-4\">\n {/* Name */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.agentManager.dialogs.name', 'Name')}\n </label>\n <input\n aria-label=\"My session\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"My session\"\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors\"\n />\n </div>\n\n {/* SDK + Mode */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.agentManager.dialogs.sdkAndMode', 'SDK & Mode')}\n </label>\n <SdkSelector\n sdk={sdk}\n mode={mode}\n providers={providers}\n onSdkChange={handleSdkChange}\n onModeChange={setMode}\n />\n </div>\n\n {/* Model */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.agentManager.dialogs.model', 'Model')}\n </label>\n <ModelSelector\n value={model}\n onChange={setModel}\n provider={sdk}\n agentId={agent.id}\n profile={agent.profile}\n />\n </div>\n\n {/* Tags */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.agentManager.dialogs.tagsLabel', 'Tags (comma-separated)')}\n </label>\n <input\n aria-label=\"e.g. frontend, bugfix, prototype\"\n type=\"text\"\n value={tags}\n onChange={(e) => setTags(e.target.value)}\n placeholder=\"e.g. frontend, bugfix, prototype\"\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors\"\n />\n </div>\n\n {error && <p className=\"text-xs text-status-error-text\">{error}</p>}\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border-default bg-bg-sunken rounded-b-lg\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={creating}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors disabled:opacity-50\"\n >\n {tr('vibecontrols.agentManager.dialogs.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleCreate}\n disabled={creating || !name.trim()}\n className=\"px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {creating\n ? tr('vibecontrols.agentManager.dialogs.creating', 'Creating...')\n : tr('vibecontrols.agentManager.dialogs.create', 'Create')}\n </button>\n </div>\n </motion.div>\n </div>\n )}\n </AnimatePresence>\n );\n}\n"],"mappings":"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"}
@@ -77,7 +77,7 @@ function u({ isOpen: u, sessionId: d, currentName: f, onClose: p, onRename: m })
77
77
  e.key === "Enter" && y();
78
78
  },
79
79
  placeholder: "Session name",
80
- className: "w-full rounded-md border border-border-default bg-bg-primary px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors"
80
+ className: "w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors"
81
81
  })
82
82
  }),
83
83
  /* @__PURE__ */ l("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"RenameSessionDialog.js","names":[],"sources":["../../../../src/components/agent-manager/dialogs/RenameSessionDialog.tsx"],"sourcesContent":["/**\n * Rename session dialog.\n *\n * Simple modal with a text input for renaming a session.\n */\n\nimport { useState, useCallback, useEffect } from 'react';\nimport { X } from 'lucide-react';\nimport { motion, AnimatePresence, useReducedMotion } from 'framer-motion';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface RenameSessionDialogProps {\n isOpen: boolean;\n sessionId: string;\n currentName: string;\n onClose: () => void;\n onRename: (sessionId: string, newName: string) => void;\n}\n\nexport function RenameSessionDialog({\n isOpen,\n sessionId,\n currentName,\n onClose,\n onRename,\n}: RenameSessionDialogProps) {\n const [name, setName] = useState(currentName);\n const shouldReduceMotion = useReducedMotion();\n const tr = useTr();\n\n useEffect(() => {\n if (isOpen) {\n setName(currentName);\n }\n }, [isOpen, currentName]);\n\n const handleSave = useCallback(() => {\n if (name.trim() && name.trim() !== currentName) {\n onRename(sessionId, name.trim());\n }\n onClose();\n }, [name, currentName, sessionId, onRename, onClose]);\n\n if (!isOpen) return null;\n\n return (\n <AnimatePresence>\n {isOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <motion.div\n className=\"absolute inset-0 bg-bg-overlay\"\n initial={shouldReduceMotion ? undefined : { opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n onClick={onClose}\n />\n <motion.div\n className=\"relative w-full max-w-sm mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl\"\n initial={shouldReduceMotion ? undefined : { opacity: 0, scale: 0.95, y: 8 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: 8 }}\n transition={{ duration: 0.2, ease: [0.2, 0, 0, 1] }}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.agentManager.dialogs.renameSession', 'Rename Session')}\n </h3>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n\n {/* Body */}\n <div className=\"px-6 py-4\">\n <input\n aria-label=\"Session name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') handleSave();\n }}\n placeholder=\"Session name\"\n className=\"w-full rounded-md border border-border-default bg-bg-primary px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors\"\n />\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border-default bg-bg-sunken rounded-b-lg\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n {tr('vibecontrols.agentManager.dialogs.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={!name.trim()}\n className=\"px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('vibecontrols.agentManager.dialogs.save', 'Save')}\n </button>\n </div>\n </motion.div>\n </div>\n )}\n </AnimatePresence>\n );\n}\n"],"mappings":";;;;;;AAmBA,SAAgB,EAAoB,EAClC,WACA,cACA,gBACA,YACA,eAC2B;CAC3B,IAAM,CAAC,GAAM,KAAW,EAAS,EAAY,EACvC,IAAqB,GAAkB,EACvC,IAAK,GAAO;AAElB,SAAgB;AACd,EAAI,KACF,EAAQ,EAAY;IAErB,CAAC,GAAQ,EAAY,CAAC;CAEzB,IAAM,IAAa,QAAkB;AAInC,EAHI,EAAK,MAAM,IAAI,EAAK,MAAM,KAAK,KACjC,EAAS,GAAW,EAAK,MAAM,CAAC,EAElC,GAAS;IACR;EAAC;EAAM;EAAa;EAAW;EAAU;EAAQ,CAAC;AAIrD,QAFK,IAGH,kBAAC,GAAD,EAAA,UACG,KACC,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,EAAO,KAAR;GACE,WAAU;GACV,SAAS,IAAqB,KAAA,IAAY,EAAE,SAAS,GAAG;GACxD,SAAS,EAAE,SAAS,GAAG;GACvB,MAAM,EAAE,SAAS,GAAG;GACpB,YAAY,EAAE,UAAU,IAAK;GAC7B,SAAS;GACT,CAAA,EACF,kBAAC,EAAO,KAAR;GACE,WAAU;GACV,SAAS,IAAqB,KAAA,IAAY;IAAE,SAAS;IAAG,OAAO;IAAM,GAAG;IAAG;GAC3E,SAAS;IAAE,SAAS;IAAG,OAAO;IAAG,GAAG;IAAG;GACvC,MAAM;IAAE,SAAS;IAAG,OAAO;IAAM,GAAG;IAAG;GACvC,YAAY;IAAE,UAAU;IAAK,MAAM;KAAC;KAAK;KAAG;KAAG;KAAE;IAAE;aALrD;IAQE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAG,mDAAmD,iBAAiB;MACrE,CAAA,EACL,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBAEV,kBAAC,GAAD,EAAG,WAAU,8BAA+B,CAAA;MACrC,CAAA,CACL;;IAGN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,SAAD;MACE,cAAW;MACX,MAAK;MACL,OAAO;MACP,WAAW,MAAM,EAAQ,EAAE,OAAO,MAAM;MACxC,YAAY,MAAM;AAChB,OAAI,EAAE,QAAQ,WAAS,GAAY;;MAErC,aAAY;MACZ,WAAU;MACV,CAAA;KACE,CAAA;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBAET,EAAG,4CAA4C,SAAS;MAClD,CAAA,EACT,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,UAAU,CAAC,EAAK,MAAM;MACtB,WAAU;gBAET,EAAG,0CAA0C,OAAO;MAC9C,CAAA,CACL;;IACK;KACT;KAEQ,CAAA,GAvEA"}
1
+ {"version":3,"file":"RenameSessionDialog.js","names":[],"sources":["../../../../src/components/agent-manager/dialogs/RenameSessionDialog.tsx"],"sourcesContent":["/**\n * Rename session dialog.\n *\n * Simple modal with a text input for renaming a session.\n */\n\nimport { useState, useCallback, useEffect } from 'react';\nimport { X } from 'lucide-react';\nimport { motion, AnimatePresence, useReducedMotion } from 'framer-motion';\nimport { useTr } from '../../../shared/hooks/useTr';\n\ninterface RenameSessionDialogProps {\n isOpen: boolean;\n sessionId: string;\n currentName: string;\n onClose: () => void;\n onRename: (sessionId: string, newName: string) => void;\n}\n\nexport function RenameSessionDialog({\n isOpen,\n sessionId,\n currentName,\n onClose,\n onRename,\n}: RenameSessionDialogProps) {\n const [name, setName] = useState(currentName);\n const shouldReduceMotion = useReducedMotion();\n const tr = useTr();\n\n useEffect(() => {\n if (isOpen) {\n setName(currentName);\n }\n }, [isOpen, currentName]);\n\n const handleSave = useCallback(() => {\n if (name.trim() && name.trim() !== currentName) {\n onRename(sessionId, name.trim());\n }\n onClose();\n }, [name, currentName, sessionId, onRename, onClose]);\n\n if (!isOpen) return null;\n\n return (\n <AnimatePresence>\n {isOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <motion.div\n className=\"absolute inset-0 bg-bg-overlay\"\n initial={shouldReduceMotion ? undefined : { opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n onClick={onClose}\n />\n <motion.div\n className=\"relative w-full max-w-sm mx-4 rounded-lg border border-border-default bg-bg-surface shadow-xl\"\n initial={shouldReduceMotion ? undefined : { opacity: 0, scale: 0.95, y: 8 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: 8 }}\n transition={{ duration: 0.2, ease: [0.2, 0, 0, 1] }}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.agentManager.dialogs.renameSession', 'Rename Session')}\n </h3>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n\n {/* Body */}\n <div className=\"px-6 py-4\">\n <input\n aria-label=\"Session name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') handleSave();\n }}\n placeholder=\"Session name\"\n className=\"w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong transition-colors\"\n />\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border-default bg-bg-sunken rounded-b-lg\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors\"\n >\n {tr('vibecontrols.agentManager.dialogs.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={!name.trim()}\n className=\"px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {tr('vibecontrols.agentManager.dialogs.save', 'Save')}\n </button>\n </div>\n </motion.div>\n </div>\n )}\n </AnimatePresence>\n );\n}\n"],"mappings":";;;;;;AAmBA,SAAgB,EAAoB,EAClC,WACA,cACA,gBACA,YACA,eAC2B;CAC3B,IAAM,CAAC,GAAM,KAAW,EAAS,EAAY,EACvC,IAAqB,GAAkB,EACvC,IAAK,GAAO;AAElB,SAAgB;AACd,EAAI,KACF,EAAQ,EAAY;IAErB,CAAC,GAAQ,EAAY,CAAC;CAEzB,IAAM,IAAa,QAAkB;AAInC,EAHI,EAAK,MAAM,IAAI,EAAK,MAAM,KAAK,KACjC,EAAS,GAAW,EAAK,MAAM,CAAC,EAElC,GAAS;IACR;EAAC;EAAM;EAAa;EAAW;EAAU;EAAQ,CAAC;AAIrD,QAFK,IAGH,kBAAC,GAAD,EAAA,UACG,KACC,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,EAAO,KAAR;GACE,WAAU;GACV,SAAS,IAAqB,KAAA,IAAY,EAAE,SAAS,GAAG;GACxD,SAAS,EAAE,SAAS,GAAG;GACvB,MAAM,EAAE,SAAS,GAAG;GACpB,YAAY,EAAE,UAAU,IAAK;GAC7B,SAAS;GACT,CAAA,EACF,kBAAC,EAAO,KAAR;GACE,WAAU;GACV,SAAS,IAAqB,KAAA,IAAY;IAAE,SAAS;IAAG,OAAO;IAAM,GAAG;IAAG;GAC3E,SAAS;IAAE,SAAS;IAAG,OAAO;IAAG,GAAG;IAAG;GACvC,MAAM;IAAE,SAAS;IAAG,OAAO;IAAM,GAAG;IAAG;GACvC,YAAY;IAAE,UAAU;IAAK,MAAM;KAAC;KAAK;KAAG;KAAG;KAAE;IAAE;aALrD;IAQE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAG,mDAAmD,iBAAiB;MACrE,CAAA,EACL,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBAEV,kBAAC,GAAD,EAAG,WAAU,8BAA+B,CAAA;MACrC,CAAA,CACL;;IAGN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,SAAD;MACE,cAAW;MACX,MAAK;MACL,OAAO;MACP,WAAW,MAAM,EAAQ,EAAE,OAAO,MAAM;MACxC,YAAY,MAAM;AAChB,OAAI,EAAE,QAAQ,WAAS,GAAY;;MAErC,aAAY;MACZ,WAAU;MACV,CAAA;KACE,CAAA;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBAET,EAAG,4CAA4C,SAAS;MAClD,CAAA,EACT,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,UAAU,CAAC,EAAK,MAAM;MACtB,WAAU;gBAET,EAAG,0CAA0C,OAAO;MAC9C,CAAA,CACL;;IACK;KACT;KAEQ,CAAA,GAvEA"}
@@ -48,7 +48,7 @@ var v = ({ agent: v, onSelect: y, onConfigure: b, onSettings: x, onStart: S, onS
48
48
  }), /* @__PURE__ */ g("div", {
49
49
  role: "button",
50
50
  tabIndex: 0,
51
- className: `bg-bg-surface border rounded-card p-4 h-full hover:border-border-strong hover:shadow-[var(--shadow-elevation-1)] transition-[colors,box-shadow] duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-canvas ${E ? "border-action-primary-bg ring-1 ring-action-primary-bg/30" : "border-border-default"} ${O}`,
51
+ className: `bg-bg-surface border rounded-card shadow-elevation-1 p-5 h-full hover:border-border-strong hover:shadow-elevation-2 hover:-translate-y-0.5 transition-all duration-200 ease-[var(--motion-easing-out)] cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-canvas ${E ? "border-transparent ring-1 ring-[var(--color-focus-ring)] bg-[var(--color-accent-soft)]" : "border-border-subtle"} ${O}`,
52
52
  onClick: () => y?.(v),
53
53
  onKeyDown: (e) => {
54
54
  e.target === e.currentTarget && (e.key === "Enter" || e.key === " ") && (e.preventDefault(), y?.(v));
@@ -72,9 +72,9 @@ var v = ({ agent: v, onSelect: y, onConfigure: b, onSettings: x, onStart: S, onS
72
72
  })
73
73
  }),
74
74
  /* @__PURE__ */ h("div", {
75
- className: "flex-shrink-0 size-8 sm:size-10 rounded-lg bg-bg-sunken flex items-center justify-center",
75
+ className: "flex-shrink-0 size-9 sm:size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center",
76
76
  children: /* @__PURE__ */ h(f, {
77
- className: "size-4 sm:size-5 text-text-secondary",
77
+ className: "size-4 sm:size-5 text-action-primary-bg",
78
78
  "aria-hidden": "true"
79
79
  })
80
80
  }),
@@ -84,7 +84,7 @@ var v = ({ agent: v, onSelect: y, onConfigure: b, onSettings: x, onStart: S, onS
84
84
  /* @__PURE__ */ g("div", {
85
85
  className: "flex items-center gap-1.5 sm:gap-2 min-w-0",
86
86
  children: [/* @__PURE__ */ h("h3", {
87
- className: "font-semibold text-text-primary truncate min-w-0 text-sm sm:text-base",
87
+ className: "font-semibold text-text-primary truncate min-w-0 text-sm sm:text-base tracking-tight",
88
88
  children: v.name
89
89
  }), /* @__PURE__ */ h(n, {
90
90
  platform: v.platform,
@@ -133,13 +133,16 @@ var v = ({ agent: v, onSelect: y, onConfigure: b, onSettings: x, onStart: S, onS
133
133
  })]
134
134
  }),
135
135
  /* @__PURE__ */ g("div", {
136
- className: "mt-4 grid grid-cols-2 gap-3 text-sm",
136
+ className: "mt-5 grid grid-cols-2 gap-3 text-sm",
137
137
  children: [/* @__PURE__ */ g("div", {
138
138
  className: "flex items-center gap-2 text-text-secondary",
139
139
  children: [/* @__PURE__ */ h(o, {
140
140
  className: "size-4",
141
141
  "aria-hidden": "true"
142
- }), /* @__PURE__ */ h("span", { children: N(v.lastHeartbeat) })]
142
+ }), /* @__PURE__ */ h("span", {
143
+ className: "tabular-nums",
144
+ children: N(v.lastHeartbeat)
145
+ })]
143
146
  }), v.architecture && /* @__PURE__ */ g("div", {
144
147
  className: "flex items-center gap-2 text-text-secondary",
145
148
  children: [/* @__PURE__ */ h(s, {
@@ -149,7 +152,7 @@ var v = ({ agent: v, onSelect: y, onConfigure: b, onSettings: x, onStart: S, onS
149
152
  })]
150
153
  }),
151
154
  v.tunnelUrl && /* @__PURE__ */ g("div", {
152
- className: "mt-3 flex items-center gap-2 text-sm",
155
+ className: "mt-4 flex items-center gap-2 text-sm",
153
156
  children: [
154
157
  /* @__PURE__ */ h(c, {
155
158
  className: "size-4 flex-shrink-0 text-text-secondary",
@@ -165,18 +168,18 @@ var v = ({ agent: v, onSelect: y, onConfigure: b, onSettings: x, onStart: S, onS
165
168
  rel: "noopener noreferrer",
166
169
  onClick: (e) => e.stopPropagation(),
167
170
  "aria-label": A("agentsPage.tunnelOpensInNewTab", "{{url}} (opens in new tab)", { url: v.tunnelUrl }),
168
- className: "min-w-0 truncate text-text-link hover:text-text-linkHover hover:underline",
171
+ className: "min-w-0 truncate font-mono text-xs text-text-link hover:text-text-linkHover hover:underline",
169
172
  children: v.tunnelUrl
170
173
  })
171
174
  ]
172
175
  }),
173
176
  v.capabilities && v.capabilities.length > 0 && /* @__PURE__ */ g("div", {
174
- className: "mt-3 flex flex-wrap gap-1",
177
+ className: "mt-4 flex flex-wrap gap-1.5",
175
178
  children: [v.capabilities.slice(0, 4).map((e) => /* @__PURE__ */ h("span", {
176
- className: "px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded-pill",
179
+ className: "px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded-full",
177
180
  children: e
178
181
  }, e)), v.capabilities.length > 4 && /* @__PURE__ */ g("span", {
179
- className: "px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded-pill",
182
+ className: "px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded-full",
180
183
  children: ["+", v.capabilities.length - 4]
181
184
  })]
182
185
  })
@@ -1 +1 @@
1
- {"version":3,"file":"AgentCard.js","names":[],"sources":["../../../src/components/agents/AgentCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { Server, Clock, Cpu, Play, Square, Settings, Globe, Link, RefreshCw } from 'lucide-react';\nimport { Checkbox } from '@burdenoff/fe-libs/ui';\nimport { AgentStatusBadge } from './AgentStatusBadge';\nimport type { Agent } from './types';\nimport { getAgentStatus } from './agentStatus';\nimport { classifyAgentHealth } from '../../utils/agentHealth';\nimport { ActionMenu, type ActionMenuItem } from '../shared/ActionMenu';\nimport { PlatformIcon } from '../shared/PlatformIcon';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport interface AgentCardProps {\n agent: Agent;\n onSelect?: (agent: Agent) => void;\n onConfigure?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onEditTunnelUrl?: (agent: Agent) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n className?: string;\n /**\n * Last error from /health/ready or the live probe — shown in the badge\n * tooltip when the agent is degraded so users know WHY without curling\n * the tunnel themselves.\n */\n healthErrorMessage?: string | null;\n}\n\n/**\n * AgentCard - Display agent information in a card format\n *\n * @example\n * ```tsx\n * <AgentCard\n * agent={myAgent}\n * onSelect={handleSelect}\n * onConfigure={handleConfigure}\n * />\n * ```\n */\nexport const AgentCard: FC<AgentCardProps> = ({\n agent,\n onSelect,\n onConfigure,\n onSettings,\n onStart,\n onStop,\n onDisconnect,\n onEditTunnelUrl,\n selected = false,\n onToggleSelect,\n className = '',\n healthErrorMessage,\n}) => {\n const tr = useTr();\n // Support both onConfigure and onSettings\n const handleConfigure = onConfigure || onSettings;\n\n const status = getAgentStatus(agent);\n\n const formatLastHeartbeat = (timestamp: string | null | undefined) => {\n if (!timestamp) return tr('agentsPage.never', 'Never');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('agentsPage.justNow', 'Just now');\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n return `${Math.floor(diff / 86400000)}d ago`;\n };\n\n const menuItems: ActionMenuItem[] = [];\n if (status === 'ACTIVE' && onStop) {\n menuItems.push({\n key: 'stop',\n label: tr('agentsPage.stopAgent', 'Stop Agent'),\n icon: Square,\n onSelect: () => onStop(agent),\n });\n }\n if ((status === 'STOPPED' || status === 'OFFLINE') && onStart) {\n menuItems.push({\n key: 'start',\n label: tr('agentsPage.startAgent', 'Start Agent'),\n icon: Play,\n onSelect: () => onStart(agent),\n });\n }\n if (status === 'DEGRADED' && onStart) {\n // Reconnect on a degraded agent fires the same startVibecontrolsAgent\n // mutation as a fresh start — svc-side that POSTs lifecycle/start to the\n // agent's tunnel, which usually clears transient failures (tunnel hiccup,\n // finalize retry). Labelled \"Reconnect\" not \"Start\" so the user knows\n // it's a recovery action, not creating a new instance.\n menuItems.push({\n key: 'reconnect',\n label: tr('agentsPage.reconnectAgent', 'Reconnect'),\n icon: RefreshCw,\n onSelect: () => onStart(agent),\n });\n }\n if (\n (status === 'ACTIVE' ||\n status === 'STOPPED' ||\n status === 'OFFLINE' ||\n status === 'DEGRADED') &&\n onDisconnect\n ) {\n menuItems.push({\n key: 'disconnect',\n label:\n status === 'OFFLINE'\n ? tr('agentsPage.clearConnection', 'Clear Connection')\n : tr('agentsPage.disconnect', 'Disconnect'),\n icon: Square,\n onSelect: () => onDisconnect(agent),\n variant: 'danger',\n });\n }\n if (onEditTunnelUrl) {\n menuItems.push({\n key: 'edit-tunnel',\n label: tr('agentsPage.editTunnel', 'Edit Tunnel'),\n icon: Link,\n onSelect: () => onEditTunnelUrl(agent),\n });\n }\n if (handleConfigure) {\n menuItems.push({\n key: 'settings',\n label: tr('agentsPage.settings', 'Settings'),\n icon: Settings,\n onSelect: () => handleConfigure(agent),\n });\n }\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n className={`bg-bg-surface border rounded-card p-4 h-full hover:border-border-strong hover:shadow-[var(--shadow-elevation-1)] transition-[colors,box-shadow] duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-canvas ${\n selected\n ? 'border-action-primary-bg ring-1 ring-action-primary-bg/30'\n : 'border-border-default'\n } ${className}`}\n onClick={() => onSelect?.(agent)}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget) return; // children (menu, checkbox, links) handle their own keys\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onSelect?.(agent);\n }\n }}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-2 sm:gap-3 min-w-0 flex-1\">\n {/* Selection Checkbox */}\n {onToggleSelect && (\n <span\n className=\"flex flex-shrink-0 items-center justify-center max-md:min-h-11 max-md:min-w-11 max-md:-m-2 md:mt-0.5\"\n onClick={(e) => {\n e.stopPropagation();\n if (e.target === e.currentTarget) onToggleSelect();\n }}\n >\n <Checkbox\n checked={selected}\n onCheckedChange={() => onToggleSelect()}\n onClick={(e) => e.stopPropagation()}\n aria-label={tr('agentsPage.selectAgent', 'Select {{name}}', { name: agent.name })}\n />\n </span>\n )}\n <div className=\"flex-shrink-0 size-8 sm:size-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Server className=\"size-4 sm:size-5 text-text-secondary\" aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5 sm:gap-2 min-w-0\">\n <h3 className=\"font-semibold text-text-primary truncate min-w-0 text-sm sm:text-base\">\n {agent.name}\n </h3>\n <PlatformIcon\n platform={agent.platform}\n className=\"size-4 flex-shrink-0 text-text-secondary\"\n />\n </div>\n <p className=\"text-xs sm:text-sm text-text-secondary truncate\">\n {agent.hostname || `ID: ${agent.id}`}\n </p>\n {(() => {\n // Always-visible (not just hover) guidance when degraded, so the\n // user knows whether it's self-healing or needs their action.\n const health = classifyAgentHealth(status, healthErrorMessage ?? null);\n if (health.phase === 'none') return null;\n return (\n <p\n className={`mt-1 flex items-start gap-1 text-xs ${\n health.phase === 'attention'\n ? 'text-status-warning-text'\n : 'text-content-tertiary'\n }`}\n >\n {health.inProgress && (\n <RefreshCw\n className=\"size-3 mt-0.5 flex-shrink-0 animate-spin\"\n aria-hidden=\"true\"\n />\n )}\n <span>\n <span className=\"font-medium\">{health.label}.</span> {health.actionHint}\n </span>\n </p>\n );\n })()}\n </div>\n </div>\n {/* Status and Actions - always visible */}\n <div className=\"flex items-center gap-1 sm:gap-2 flex-shrink-0\">\n <AgentStatusBadge\n status={status}\n reason={healthErrorMessage ?? null}\n linkTo={`/vibecontrols/agents/${agent.id}?tab=connection`}\n onClick={(event) => {\n // The card itself has a `onSelect` click target that\n // routes to the agent detail. Stop propagation so a\n // badge click doesn't *also* fire that handler — the\n // anchor's href already routes to the right tab and\n // React Router picks it up via the surrounding shell.\n event.stopPropagation();\n }}\n />\n {(handleConfigure || onStart || onStop) && menuItems.length > 0 && (\n <ActionMenu\n ariaLabel={tr('agentsPage.agentActionsMenu', 'Agent actions menu')}\n title={tr('agentsPage.agentActions', 'Agent actions')}\n align=\"end\"\n items={menuItems}\n />\n )}\n </div>\n </div>\n\n <div className=\"mt-4 grid grid-cols-2 gap-3 text-sm\">\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Clock className=\"size-4\" aria-hidden=\"true\" />\n <span>{formatLastHeartbeat(agent.lastHeartbeat)}</span>\n </div>\n {agent.architecture && (\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Cpu className=\"size-4\" aria-hidden=\"true\" />\n <span>{agent.architecture}</span>\n </div>\n )}\n </div>\n\n {agent.tunnelUrl && (\n // Plain \"Tunnel: <link>\" row. The colored status chip that used\n // to sit here was a redundant second status surface — the\n // canonical state lives in the <AgentStatusBadge> top-right.\n // The Globe glyph stays as a neutral icon (no status color).\n <div className=\"mt-3 flex items-center gap-2 text-sm\">\n <Globe className=\"size-4 flex-shrink-0 text-text-secondary\" aria-hidden=\"true\" />\n <span className=\"text-text-secondary flex-shrink-0\">Tunnel:</span>\n <a\n href={agent.tunnelUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={(e) => e.stopPropagation()}\n aria-label={tr('agentsPage.tunnelOpensInNewTab', '{{url}} (opens in new tab)', {\n url: agent.tunnelUrl,\n })}\n className=\"min-w-0 truncate text-text-link hover:text-text-linkHover hover:underline\"\n >\n {agent.tunnelUrl}\n </a>\n </div>\n )}\n\n {agent.capabilities && agent.capabilities.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-1\">\n {agent.capabilities.slice(0, 4).map((cap) => (\n <span\n key={cap}\n className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded-pill\"\n >\n {cap}\n </span>\n ))}\n {agent.capabilities.length > 4 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded-pill\">\n +{agent.capabilities.length - 4}\n </span>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AgentCard.js","names":[],"sources":["../../../src/components/agents/AgentCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { Server, Clock, Cpu, Play, Square, Settings, Globe, Link, RefreshCw } from 'lucide-react';\nimport { Checkbox } from '@burdenoff/fe-libs/ui';\nimport { AgentStatusBadge } from './AgentStatusBadge';\nimport type { Agent } from './types';\nimport { getAgentStatus } from './agentStatus';\nimport { classifyAgentHealth } from '../../utils/agentHealth';\nimport { ActionMenu, type ActionMenuItem } from '../shared/ActionMenu';\nimport { PlatformIcon } from '../shared/PlatformIcon';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport interface AgentCardProps {\n agent: Agent;\n onSelect?: (agent: Agent) => void;\n onConfigure?: (agent: Agent) => void;\n onSettings?: (agent: Agent) => void;\n onStart?: (agent: Agent) => void;\n onStop?: (agent: Agent) => void;\n onDisconnect?: (agent: Agent) => void;\n onEditTunnelUrl?: (agent: Agent) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n className?: string;\n /**\n * Last error from /health/ready or the live probe — shown in the badge\n * tooltip when the agent is degraded so users know WHY without curling\n * the tunnel themselves.\n */\n healthErrorMessage?: string | null;\n}\n\n/**\n * AgentCard - Display agent information in a card format\n *\n * @example\n * ```tsx\n * <AgentCard\n * agent={myAgent}\n * onSelect={handleSelect}\n * onConfigure={handleConfigure}\n * />\n * ```\n */\nexport const AgentCard: FC<AgentCardProps> = ({\n agent,\n onSelect,\n onConfigure,\n onSettings,\n onStart,\n onStop,\n onDisconnect,\n onEditTunnelUrl,\n selected = false,\n onToggleSelect,\n className = '',\n healthErrorMessage,\n}) => {\n const tr = useTr();\n // Support both onConfigure and onSettings\n const handleConfigure = onConfigure || onSettings;\n\n const status = getAgentStatus(agent);\n\n const formatLastHeartbeat = (timestamp: string | null | undefined) => {\n if (!timestamp) return tr('agentsPage.never', 'Never');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('agentsPage.justNow', 'Just now');\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n return `${Math.floor(diff / 86400000)}d ago`;\n };\n\n const menuItems: ActionMenuItem[] = [];\n if (status === 'ACTIVE' && onStop) {\n menuItems.push({\n key: 'stop',\n label: tr('agentsPage.stopAgent', 'Stop Agent'),\n icon: Square,\n onSelect: () => onStop(agent),\n });\n }\n if ((status === 'STOPPED' || status === 'OFFLINE') && onStart) {\n menuItems.push({\n key: 'start',\n label: tr('agentsPage.startAgent', 'Start Agent'),\n icon: Play,\n onSelect: () => onStart(agent),\n });\n }\n if (status === 'DEGRADED' && onStart) {\n // Reconnect on a degraded agent fires the same startVibecontrolsAgent\n // mutation as a fresh start — svc-side that POSTs lifecycle/start to the\n // agent's tunnel, which usually clears transient failures (tunnel hiccup,\n // finalize retry). Labelled \"Reconnect\" not \"Start\" so the user knows\n // it's a recovery action, not creating a new instance.\n menuItems.push({\n key: 'reconnect',\n label: tr('agentsPage.reconnectAgent', 'Reconnect'),\n icon: RefreshCw,\n onSelect: () => onStart(agent),\n });\n }\n if (\n (status === 'ACTIVE' ||\n status === 'STOPPED' ||\n status === 'OFFLINE' ||\n status === 'DEGRADED') &&\n onDisconnect\n ) {\n menuItems.push({\n key: 'disconnect',\n label:\n status === 'OFFLINE'\n ? tr('agentsPage.clearConnection', 'Clear Connection')\n : tr('agentsPage.disconnect', 'Disconnect'),\n icon: Square,\n onSelect: () => onDisconnect(agent),\n variant: 'danger',\n });\n }\n if (onEditTunnelUrl) {\n menuItems.push({\n key: 'edit-tunnel',\n label: tr('agentsPage.editTunnel', 'Edit Tunnel'),\n icon: Link,\n onSelect: () => onEditTunnelUrl(agent),\n });\n }\n if (handleConfigure) {\n menuItems.push({\n key: 'settings',\n label: tr('agentsPage.settings', 'Settings'),\n icon: Settings,\n onSelect: () => handleConfigure(agent),\n });\n }\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n className={`bg-bg-surface border rounded-card shadow-elevation-1 p-5 h-full hover:border-border-strong hover:shadow-elevation-2 hover:-translate-y-0.5 transition-all duration-200 ease-[var(--motion-easing-out)] cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-canvas ${\n selected\n ? 'border-transparent ring-1 ring-[var(--color-focus-ring)] bg-[var(--color-accent-soft)]'\n : 'border-border-subtle'\n } ${className}`}\n onClick={() => onSelect?.(agent)}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget) return; // children (menu, checkbox, links) handle their own keys\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onSelect?.(agent);\n }\n }}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-2 sm:gap-3 min-w-0 flex-1\">\n {/* Selection Checkbox */}\n {onToggleSelect && (\n <span\n className=\"flex flex-shrink-0 items-center justify-center max-md:min-h-11 max-md:min-w-11 max-md:-m-2 md:mt-0.5\"\n onClick={(e) => {\n e.stopPropagation();\n if (e.target === e.currentTarget) onToggleSelect();\n }}\n >\n <Checkbox\n checked={selected}\n onCheckedChange={() => onToggleSelect()}\n onClick={(e) => e.stopPropagation()}\n aria-label={tr('agentsPage.selectAgent', 'Select {{name}}', { name: agent.name })}\n />\n </span>\n )}\n <div className=\"flex-shrink-0 size-9 sm:size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Server className=\"size-4 sm:size-5 text-action-primary-bg\" aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5 sm:gap-2 min-w-0\">\n <h3 className=\"font-semibold text-text-primary truncate min-w-0 text-sm sm:text-base tracking-tight\">\n {agent.name}\n </h3>\n <PlatformIcon\n platform={agent.platform}\n className=\"size-4 flex-shrink-0 text-text-secondary\"\n />\n </div>\n <p className=\"text-xs sm:text-sm text-text-secondary truncate\">\n {agent.hostname || `ID: ${agent.id}`}\n </p>\n {(() => {\n // Always-visible (not just hover) guidance when degraded, so the\n // user knows whether it's self-healing or needs their action.\n const health = classifyAgentHealth(status, healthErrorMessage ?? null);\n if (health.phase === 'none') return null;\n return (\n <p\n className={`mt-1 flex items-start gap-1 text-xs ${\n health.phase === 'attention'\n ? 'text-status-warning-text'\n : 'text-content-tertiary'\n }`}\n >\n {health.inProgress && (\n <RefreshCw\n className=\"size-3 mt-0.5 flex-shrink-0 animate-spin\"\n aria-hidden=\"true\"\n />\n )}\n <span>\n <span className=\"font-medium\">{health.label}.</span> {health.actionHint}\n </span>\n </p>\n );\n })()}\n </div>\n </div>\n {/* Status and Actions - always visible */}\n <div className=\"flex items-center gap-1 sm:gap-2 flex-shrink-0\">\n <AgentStatusBadge\n status={status}\n reason={healthErrorMessage ?? null}\n linkTo={`/vibecontrols/agents/${agent.id}?tab=connection`}\n onClick={(event) => {\n // The card itself has a `onSelect` click target that\n // routes to the agent detail. Stop propagation so a\n // badge click doesn't *also* fire that handler — the\n // anchor's href already routes to the right tab and\n // React Router picks it up via the surrounding shell.\n event.stopPropagation();\n }}\n />\n {(handleConfigure || onStart || onStop) && menuItems.length > 0 && (\n <ActionMenu\n ariaLabel={tr('agentsPage.agentActionsMenu', 'Agent actions menu')}\n title={tr('agentsPage.agentActions', 'Agent actions')}\n align=\"end\"\n items={menuItems}\n />\n )}\n </div>\n </div>\n\n <div className=\"mt-5 grid grid-cols-2 gap-3 text-sm\">\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Clock className=\"size-4\" aria-hidden=\"true\" />\n <span className=\"tabular-nums\">{formatLastHeartbeat(agent.lastHeartbeat)}</span>\n </div>\n {agent.architecture && (\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Cpu className=\"size-4\" aria-hidden=\"true\" />\n <span>{agent.architecture}</span>\n </div>\n )}\n </div>\n\n {agent.tunnelUrl && (\n // Plain \"Tunnel: <link>\" row. The colored status chip that used\n // to sit here was a redundant second status surface — the\n // canonical state lives in the <AgentStatusBadge> top-right.\n // The Globe glyph stays as a neutral icon (no status color).\n <div className=\"mt-4 flex items-center gap-2 text-sm\">\n <Globe className=\"size-4 flex-shrink-0 text-text-secondary\" aria-hidden=\"true\" />\n <span className=\"text-text-secondary flex-shrink-0\">Tunnel:</span>\n <a\n href={agent.tunnelUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={(e) => e.stopPropagation()}\n aria-label={tr('agentsPage.tunnelOpensInNewTab', '{{url}} (opens in new tab)', {\n url: agent.tunnelUrl,\n })}\n className=\"min-w-0 truncate font-mono text-xs text-text-link hover:text-text-linkHover hover:underline\"\n >\n {agent.tunnelUrl}\n </a>\n </div>\n )}\n\n {agent.capabilities && agent.capabilities.length > 0 && (\n <div className=\"mt-4 flex flex-wrap gap-1.5\">\n {agent.capabilities.slice(0, 4).map((cap) => (\n <span\n key={cap}\n className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded-full\"\n >\n {cap}\n </span>\n ))}\n {agent.capabilities.length > 4 && (\n <span className=\"px-2 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded-full\">\n +{agent.capabilities.length - 4}\n </span>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -219,7 +219,7 @@ function w({ agent: w }) {
219
219
  className: "font-semibold text-text-primary truncate",
220
220
  children: e.name
221
221
  }), /* @__PURE__ */ b("span", {
222
- className: "px-1.5 py-0.5 text-xs bg-action-primary-bg/10 text-action-primary-bg rounded",
222
+ className: "px-1.5 py-0.5 text-xs bg-[var(--color-accent-soft)] text-action-primary-bg rounded",
223
223
  children: e.type
224
224
  })]
225
225
  })