@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":"AIContextsTab.js","names":[],"sources":["../../../src/components/ai/AIContextsTab.tsx"],"sourcesContent":["import { useState, useEffect, useCallback, useMemo } from 'react';\nimport { Plus, X, FileText, Trash2, Pencil } from 'lucide-react';\nimport {\n Card,\n LoadingState,\n ErrorState,\n EmptyState,\n ConfirmDialog,\n SelectionCheckbox,\n} from '@/components/shared';\nimport { EntityShareButton } from '@/components/sharing';\nimport type { AIContext, AgentRef } from '@/services/aiApi';\nimport { fetchContexts, createContext, updateContext, deleteContext } from '@/services/aiApi';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AIContextsTabProps {\n agent: AgentRef;\n}\n\nconst contextTypes = [\n { value: 'all', label: 'All Types' },\n { value: 'system', label: 'System' },\n { value: 'user', label: 'User' },\n { value: 'file', label: 'File' },\n { value: 'project', label: 'Project' },\n { value: 'custom', label: 'Custom' },\n];\n\ninterface ContextFormData {\n name: string;\n type: string;\n content: string;\n tags: string[];\n}\n\nconst emptyForm: ContextFormData = { name: '', type: 'custom', content: '', tags: [] };\n\nexport function AIContextsTab({ agent }: AIContextsTabProps) {\n const tr = useTr();\n const [contexts, setContexts] = useState<AIContext[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [typeFilter, setTypeFilter] = useState('all');\n const [showForm, setShowForm] = useState(false);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [formData, setFormData] = useState<ContextFormData>(emptyForm);\n const [tagInput, setTagInput] = useState('');\n const [saving, setSaving] = useState(false);\n const [deleteIds, setDeleteIds] = useState<string[]>([]);\n const [deleting, setDeleting] = useState(false);\n const [selectedContextIds, setSelectedContextIds] = useState<Set<string>>(new Set());\n\n const loadContexts = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const params: Record<string, unknown> = {};\n if (typeFilter !== 'all') params.type = typeFilter;\n const result = await fetchContexts(agent, params);\n setContexts(result);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load contexts');\n } finally {\n setLoading(false);\n }\n }, [agent, typeFilter]);\n\n useEffect(() => {\n loadContexts();\n }, [loadContexts]);\n\n const selectedContexts = useMemo(\n () => contexts.filter((context) => selectedContextIds.has(context.id)),\n [contexts, selectedContextIds]\n );\n const allVisibleSelected =\n contexts.length > 0 && contexts.every((context) => selectedContextIds.has(context.id));\n const partiallySelected =\n contexts.some((context) => selectedContextIds.has(context.id)) && !allVisibleSelected;\n\n const openCreate = () => {\n setEditingId(null);\n setFormData(emptyForm);\n setTagInput('');\n setShowForm(true);\n };\n\n const openEdit = (ctx: AIContext) => {\n setEditingId(ctx.id);\n setFormData({ name: ctx.name, type: ctx.type, content: ctx.content, tags: ctx.tags ?? [] });\n setTagInput('');\n setShowForm(true);\n };\n\n const handleSave = async () => {\n setSaving(true);\n try {\n if (editingId) {\n await updateContext(agent, editingId, formData);\n } else {\n await createContext(agent, formData);\n }\n setShowForm(false);\n await loadContexts();\n } catch (err) {\n console.error('Failed to save context:', err);\n } finally {\n setSaving(false);\n }\n };\n\n const handleDelete = async () => {\n if (deleteIds.length === 0) return;\n setDeleting(true);\n try {\n await Promise.allSettled(deleteIds.map((deleteId) => deleteContext(agent, deleteId)));\n setSelectedContextIds((prev) => {\n const next = new Set(prev);\n deleteIds.forEach((deleteId) => next.delete(deleteId));\n return next;\n });\n setDeleteIds([]);\n await loadContexts();\n } catch (err) {\n console.error('Failed to delete context:', err);\n } finally {\n setDeleting(false);\n }\n };\n\n const addTag = () => {\n const tag = tagInput.trim();\n if (tag && !formData.tags.includes(tag)) {\n setFormData((prev) => ({ ...prev, tags: [...prev.tags, tag] }));\n }\n setTagInput('');\n };\n\n const removeTag = (tag: string) => {\n setFormData((prev) => ({ ...prev, tags: prev.tags.filter((t) => t !== tag) }));\n };\n\n const toggleContextSelection = (contextId: string, checked: boolean) => {\n setSelectedContextIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(contextId);\n } else {\n next.delete(contextId);\n }\n return next;\n });\n };\n\n const toggleAllVisibleContexts = (checked: boolean) => {\n setSelectedContextIds((prev) => {\n const next = new Set(prev);\n contexts.forEach((context) => {\n if (checked) {\n next.add(context.id);\n } else {\n next.delete(context.id);\n }\n });\n return next;\n });\n };\n\n const clearSelection = () => setSelectedContextIds(new Set());\n\n if (loading)\n return <LoadingState message={tr('vibecontrols.ai.contexts.loading', 'Loading contexts...')} />;\n if (error)\n return (\n <ErrorState\n title={tr('vibecontrols.ai.contexts.loadFailed', 'Failed to load contexts')}\n message={error}\n onRetry={loadContexts}\n />\n );\n\n return (\n <div className=\"space-y-4\">\n {/* Toolbar */}\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-2\">\n <select\n value={typeFilter}\n onChange={(e) => setTypeFilter(e.target.value)}\n className=\"px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm\"\n >\n {contextTypes.map((t) => (\n <option key={t.value} value={t.value}>\n {t.label}\n </option>\n ))}\n </select>\n <div className=\"flex-1\" />\n <button\n type=\"button\"\n onClick={openCreate}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors text-sm\"\n >\n <Plus className=\"size-4\" />\n {tr('vibecontrols.ai.contexts.newContext', 'New Context')}\n </button>\n </div>\n\n {contexts.length > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleContexts}\n ariaLabel=\"Select all visible contexts\"\n />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('vibecontrols.ai.contexts.selectAllVisible', 'Select all visible')}\n </span>\n </div>\n {selectedContextIds.size > 0 && (\n <button\n type=\"button\"\n onClick={clearSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n {tr('vibecontrols.ai.contexts.clearSelection', 'Clear selection')}\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedContextIds.size} {tr('vibecontrols.ai.contexts.selected', 'selected')}\n </span>\n <button\n type=\"button\"\n onClick={() => setDeleteIds(selectedContexts.map((context) => context.id))}\n disabled={selectedContexts.length === 0}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr('vibecontrols.ai.contexts.deleteSelected', 'Delete Selected')}\n {selectedContexts.length > 0 ? ` (${selectedContexts.length})` : ''}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Context List */}\n {contexts.length === 0 ? (\n <EmptyState\n icon={<FileText className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.ai.contexts.emptyTitle', 'No contexts yet')}\n description={tr(\n 'vibecontrols.ai.contexts.emptyDescription',\n 'Create contexts to provide additional information to your AI sessions'\n )}\n action={\n <button\n type=\"button\"\n onClick={openCreate}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('vibecontrols.ai.contexts.newContext', 'New Context')}\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {contexts.map((ctx) => (\n <Card\n key={ctx.id}\n className={`p-4 ${selectedContextIds.has(ctx.id) ? 'border-action-primary-bg ring-1 ring-action-primary-bg/25' : ''}`}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-3 min-w-0 flex-1\">\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selectedContextIds.has(ctx.id)}\n onChange={(checked) => toggleContextSelection(ctx.id, checked)}\n ariaLabel={`Select context ${ctx.name}`}\n />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <h4 className=\"font-semibold text-text-primary truncate\">{ctx.name}</h4>\n <span className=\"px-1.5 py-0.5 text-xs bg-action-primary-bg/10 text-action-primary-bg rounded\">\n {ctx.type}\n </span>\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-1 flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => openEdit(ctx)}\n className=\"p-1.5 hover:bg-bg-sunken rounded-md transition-colors\"\n title=\"Edit\"\n >\n <Pencil className=\"size-3.5 text-text-secondary\" />\n </button>\n <EntityShareButton\n entity=\"aicontext\"\n resource={{ id: ctx.id, name: ctx.name }}\n label={tr('vibecontrols.ai.contexts.share', 'Share')}\n />\n <button\n type=\"button\"\n onClick={() => setDeleteIds([ctx.id])}\n className=\"p-1.5 hover:bg-status-error-bg rounded-md transition-colors\"\n title=\"Delete\"\n >\n <Trash2 className=\"size-3.5 text-status-error-text\" />\n </button>\n </div>\n </div>\n <pre className=\"mt-2 text-xs text-text-secondary bg-bg-sunken rounded p-2 max-h-20 overflow-auto whitespace-pre-wrap\">\n {ctx.content}\n </pre>\n {ctx.tags.length > 0 && (\n <div className=\"mt-2 flex flex-wrap gap-1\">\n {ctx.tags.map((tag) => (\n <span\n key={tag}\n className=\"px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n </Card>\n ))}\n </div>\n )}\n\n {/* Create/Edit Dialog */}\n {showForm && (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => setShowForm(false))();\n }\n }}\n className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\"\n onClick={() => setShowForm(false)}\n >\n <div\n className=\"bg-bg-surface border border-border-default rounded-lg shadow-xl w-full max-w-lg max-h-[90vh] overflow-y-auto p-6\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {editingId\n ? tr('vibecontrols.ai.contexts.editContext', 'Edit Context')\n : tr('vibecontrols.ai.contexts.newContext', 'New Context')}\n </h3>\n <button\n type=\"button\"\n onClick={() => setShowForm(false)}\n className=\"p-1 hover:bg-bg-sunken rounded-md\"\n >\n <X className=\"size-4 text-text-secondary\" />\n </button>\n </div>\n\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.ai.contexts.name', 'Name')}\n </label>\n <input\n aria-label=\"control\"\n type=\"text\"\n value={formData.name}\n onChange={(e) => setFormData({ ...formData, name: e.target.value })}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:border-border-strong\"\n placeholder={tr('vibecontrols.ai.contexts.namePlaceholder', 'Context name')}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.ai.contexts.type', 'Type')}\n </label>\n <select\n value={formData.type}\n onChange={(e) => setFormData({ ...formData, type: e.target.value })}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm\"\n >\n {contextTypes\n .filter((t) => t.value !== 'all')\n .map((t) => (\n <option key={t.value} value={t.value}>\n {t.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.ai.contexts.content', 'Content')}\n </label>\n <textarea\n aria-label=\"control\"\n value={formData.content}\n onChange={(e) => setFormData({ ...formData, content: e.target.value })}\n rows={8}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm font-mono focus:outline-none focus:border-border-strong\"\n placeholder={tr(\n 'vibecontrols.ai.contexts.contentPlaceholder',\n 'Context content...'\n )}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.ai.contexts.tags', 'Tags')}\n </label>\n <div className=\"flex flex-wrap gap-1 mb-2\">\n {formData.tags.map((tag) => (\n <span\n key={tag}\n className=\"flex items-center gap-1 px-2 py-0.5 text-xs bg-bg-sunken text-text-primary rounded\"\n >\n {tag}\n <button type=\"button\" onClick={() => removeTag(tag)}>\n <X className=\"size-3 text-text-secondary hover:text-status-error-text\" />\n </button>\n </span>\n ))}\n </div>\n <div className=\"flex gap-2\">\n <input\n aria-label=\"control\"\n type=\"text\"\n value={tagInput}\n onChange={(e) => setTagInput(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n addTag();\n }\n }}\n className=\"flex-1 px-3 py-1.5 bg-bg-sunken border border-border-default rounded text-sm text-text-primary focus:outline-none focus:border-border-strong\"\n placeholder={tr('vibecontrols.ai.contexts.addTagPlaceholder', 'Add a tag...')}\n />\n <button\n type=\"button\"\n onClick={addTag}\n className=\"px-3 py-1.5 text-xs bg-bg-sunken border border-border-default rounded hover:bg-bg-surface transition-colors\"\n >\n {tr('vibecontrols.ai.contexts.add', 'Add')}\n </button>\n </div>\n </div>\n </div>\n\n <div className=\"flex justify-end gap-2 mt-6\">\n <button\n type=\"button\"\n onClick={() => setShowForm(false)}\n className=\"px-4 py-2 text-sm text-text-secondary hover:bg-bg-sunken rounded-md transition-colors\"\n >\n {tr('vibecontrols.ai.contexts.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={saving || !formData.name || !formData.content}\n className=\"px-4 py-2 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {saving\n ? tr('vibecontrols.ai.contexts.saving', 'Saving...')\n : editingId\n ? tr('vibecontrols.ai.contexts.update', 'Update')\n : tr('vibecontrols.ai.contexts.create', 'Create')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Delete Confirmation */}\n <ConfirmDialog\n isOpen={deleteIds.length > 0}\n title={deleteIds.length > 1 ? 'Delete Contexts' : 'Delete Context'}\n message={\n deleteIds.length > 1\n ? `Are you sure you want to delete ${deleteIds.length} selected contexts? This action cannot be undone.`\n : 'Are you sure you want to delete this context? This action cannot be undone.'\n }\n confirmLabel={deleting ? 'Deleting...' : 'Delete'}\n variant=\"danger\"\n onConfirm={handleDelete}\n onClose={() => setDeleteIds([])}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAmBA,IAAM,IAAe;CACnB;EAAE,OAAO;EAAO,OAAO;EAAa;CACpC;EAAE,OAAO;EAAU,OAAO;EAAU;CACpC;EAAE,OAAO;EAAQ,OAAO;EAAQ;CAChC;EAAE,OAAO;EAAQ,OAAO;EAAQ;CAChC;EAAE,OAAO;EAAW,OAAO;EAAW;CACtC;EAAE,OAAO;EAAU,OAAO;EAAU;CACrC,EASK,IAA6B;CAAE,MAAM;CAAI,MAAM;CAAU,SAAS;CAAI,MAAM,EAAE;CAAE;AAEtF,SAAgB,EAAc,EAAE,YAA6B;CAC3D,IAAM,IAAK,GAAO,EACZ,CAAC,GAAU,MAAe,EAAsB,EAAE,CAAC,EACnD,CAAC,GAAS,KAAc,EAAS,GAAK,EACtC,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,CAAC,GAAY,KAAiB,EAAS,MAAM,EAC7C,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAW,KAAgB,EAAwB,KAAK,EACzD,CAAC,GAAU,KAAe,EAA0B,EAAU,EAC9D,CAAC,GAAU,KAAe,EAAS,GAAG,EACtC,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,CAAC,GAAW,KAAgB,EAAmB,EAAE,CAAC,EAClD,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAoB,KAAyB,kBAAsB,IAAI,KAAK,CAAC,EAE9E,IAAe,EAAY,YAAY;AAE3C,EADA,EAAW,GAAK,EAChB,EAAS,KAAK;AACd,MAAI;GACF,IAAM,IAAkC,EAAE;AAG1C,GAFI,MAAe,UAAO,EAAO,OAAO,IAExC,GADe,MAAM,EAAc,GAAO,EAAO,CAC9B;WACZ,GAAK;AACZ,KAAS,aAAe,QAAQ,EAAI,UAAU,0BAA0B;YAChE;AACR,KAAW,GAAM;;IAElB,CAAC,GAAO,EAAW,CAAC;AAEvB,SAAgB;AACd,KAAc;IACb,CAAC,EAAa,CAAC;CAElB,IAAM,IAAmB,QACjB,EAAS,QAAQ,MAAY,EAAmB,IAAI,EAAQ,GAAG,CAAC,EACtE,CAAC,GAAU,EAAmB,CAC/B,EACK,IACJ,EAAS,SAAS,KAAK,EAAS,OAAO,MAAY,EAAmB,IAAI,EAAQ,GAAG,CAAC,EAClF,KACJ,EAAS,MAAM,MAAY,EAAmB,IAAI,EAAQ,GAAG,CAAC,IAAI,CAAC,GAE/D,UAAmB;AAIvB,EAHA,EAAa,KAAK,EAClB,EAAY,EAAU,EACtB,EAAY,GAAG,EACf,EAAY,GAAK;IAGb,MAAY,MAAmB;AAInC,EAHA,EAAa,EAAI,GAAG,EACpB,EAAY;GAAE,MAAM,EAAI;GAAM,MAAM,EAAI;GAAM,SAAS,EAAI;GAAS,MAAM,EAAI,QAAQ,EAAE;GAAE,CAAC,EAC3F,EAAY,GAAG,EACf,EAAY,GAAK;IAGb,KAAa,YAAY;AAC7B,IAAU,GAAK;AACf,MAAI;AAOF,GANI,IACF,MAAM,EAAc,GAAO,GAAW,EAAS,GAE/C,MAAM,EAAc,GAAO,EAAS,EAEtC,EAAY,GAAM,EAClB,MAAM,GAAc;WACb,GAAK;AACZ,WAAQ,MAAM,2BAA2B,EAAI;YACrC;AACR,KAAU,GAAM;;IAId,KAAe,YAAY;AAC3B,QAAU,WAAW,GACzB;KAAY,GAAK;AACjB,OAAI;AAQF,IAPA,MAAM,QAAQ,WAAW,EAAU,KAAK,MAAa,EAAc,GAAO,EAAS,CAAC,CAAC,EACrF,GAAuB,MAAS;KAC9B,IAAM,IAAO,IAAI,IAAI,EAAK;AAE1B,YADA,EAAU,SAAS,MAAa,EAAK,OAAO,EAAS,CAAC,EAC/C;MACP,EACF,EAAa,EAAE,CAAC,EAChB,MAAM,GAAc;YACb,GAAK;AACZ,YAAQ,MAAM,6BAA6B,EAAI;aACvC;AACR,MAAY,GAAM;;;IAIhB,UAAe;EACnB,IAAM,IAAM,EAAS,MAAM;AAI3B,EAHI,KAAO,CAAC,EAAS,KAAK,SAAS,EAAI,IACrC,GAAa,OAAU;GAAE,GAAG;GAAM,MAAM,CAAC,GAAG,EAAK,MAAM,EAAI;GAAE,EAAE,EAEjE,EAAY,GAAG;IAGX,MAAa,MAAgB;AACjC,KAAa,OAAU;GAAE,GAAG;GAAM,MAAM,EAAK,KAAK,QAAQ,MAAM,MAAM,EAAI;GAAE,EAAE;IAG1E,MAA0B,GAAmB,MAAqB;AACtE,KAAuB,MAAS;GAC9B,IAAM,IAAO,IAAI,IAAI,EAAK;AAM1B,UALI,IACF,EAAK,IAAI,EAAU,GAEnB,EAAK,OAAO,EAAU,EAEjB;IACP;;AA8BJ,QAXI,IACK,kBAAC,GAAD,EAAc,SAAS,EAAG,oCAAoC,sBAAsB,EAAI,CAAA,GAC7F,IAEA,kBAAC,GAAD;EACE,OAAO,EAAG,uCAAuC,0BAA0B;EAC3E,SAAS;EACT,SAAS;EACT,CAAA,GAIJ,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,UAAD;MACE,OAAO;MACP,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;MAC9C,WAAU;gBAET,EAAa,KAAK,MACjB,kBAAC,UAAD;OAAsB,OAAO,EAAE;iBAC5B,EAAE;OACI,EAFI,EAAE,MAEN,CACT;MACK,CAAA;KACT,kBAAC,OAAD,EAAK,WAAU,UAAW,CAAA;KAC1B,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;gBAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,EAAG,uCAAuC,cAAc,CAClD;;KACL;;GAEL,EAAS,SAAS,KACjB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QACE,SAAS;QACT,eAAe;QACf,WA7DkB,MAAqB;AACrD,YAAuB,MAAS;UAC9B,IAAM,IAAO,IAAI,IAAI,EAAK;AAQ1B,iBAPA,EAAS,SAAS,MAAY;AAC5B,WAAI,IACF,EAAK,IAAI,EAAQ,GAAG,GAEpB,EAAK,OAAO,EAAQ,GAAG;YAEzB,EACK;WACP;;QAmDY,WAAU;QACV,CAAA,EACF,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAG,6CAA6C,qBAAqB;QACjE,CAAA,CACH;UACL,EAAmB,OAAO,KACzB,kBAAC,UAAD;OACE,MAAK;OACL,eAzDa,kBAAsB,IAAI,KAAK,CAAC;OA0D7C,WAAU;iBAET,EAAG,2CAA2C,kBAAkB;OAC1D,CAAA,CAEP;SAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACG,EAAmB;QAAK;QAAE,EAAG,qCAAqC,WAAW;QACzE;UACP,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAa,EAAiB,KAAK,MAAY,EAAQ,GAAG,CAAC;OAC1E,UAAU,EAAiB,WAAW;OACtC,WAAU;iBAJZ,CAMG,EAAG,2CAA2C,kBAAkB,EAChE,EAAiB,SAAS,IAAI,KAAK,EAAiB,OAAO,KAAK,GAC1D;SACL;QACF;;IACF,CAAA;GAIP,EAAS,WAAW,IACnB,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAU,WAAU,8BAA+B,CAAA;IACzD,OAAO,EAAG,uCAAuC,kBAAkB;IACnE,aAAa,EACX,6CACA,wEACD;IACD,QACE,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,EAAG,uCAAuC,cAAc,CAClD;;IAEX,CAAA,GAEF,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAS,KAAK,MACb,kBAAC,GAAD;KAEE,WAAW,OAAO,EAAmB,IAAI,EAAI,GAAG,GAAG,8DAA8D;eAFnH;MAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,UAAU,MAAU,EAAM,iBAAiB;mBAC9C,kBAAC,GAAD;UACE,SAAS,EAAmB,IAAI,EAAI,GAAG;UACvC,WAAW,MAAY,GAAuB,EAAI,IAAI,EAAQ;UAC9D,WAAW,kBAAkB,EAAI;UACjC,CAAA;SACE,CAAA,EACN,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,MAAD;WAAI,WAAU;qBAA4C,EAAI;WAAU,CAAA,EACxE,kBAAC,QAAD;WAAM,WAAU;qBACb,EAAI;WACA,CAAA,CACH;;SACF,CAAA,CACF;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,GAAS,EAAI;UAC5B,WAAU;UACV,OAAM;oBAEN,kBAAC,GAAD,EAAQ,WAAU,gCAAiC,CAAA;UAC5C,CAAA;SACT,kBAAC,IAAD;UACE,QAAO;UACP,UAAU;WAAE,IAAI,EAAI;WAAI,MAAM,EAAI;WAAM;UACxC,OAAO,EAAG,kCAAkC,QAAQ;UACpD,CAAA;SACF,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAa,CAAC,EAAI,GAAG,CAAC;UACrC,WAAU;UACV,OAAM;oBAEN,kBAAC,GAAD,EAAQ,WAAU,mCAAoC,CAAA;UAC/C,CAAA;SACL;UACF;;MACN,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAI;OACD,CAAA;MACL,EAAI,KAAK,SAAS,KACjB,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAI,KAAK,KAAK,MACb,kBAAC,QAAD;QAEE,WAAU;kBAET;QACI,EAJA,EAIA,CACP;OACE,CAAA;MAEH;OA5DA,EAAI,GA4DJ,CACP;IACE,CAAA;GAIP,KACC,kBAAC,OAAD;IACE,MAAK;IACL,UAAU;IACV,YAAY,MAAM;AAChB,MAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,gBAAgB,EACX,EAAY,GAAM;;IAG7B,WAAU;IACV,eAAe,EAAY,GAAM;cAEjC,kBAAC,OAAD;KACE,WAAU;KACV,UAAU,MAAM,EAAE,iBAAiB;eAFrC;MAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBACX,IACG,EAAG,wCAAwC,eAAe,GAC1D,EAAG,uCAAuC,cAAc;QACzD,CAAA,EACL,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAY,GAAM;QACjC,WAAU;kBAEV,kBAAC,GAAD,EAAG,WAAU,8BAA+B,CAAA;QACrC,CAAA,CACL;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAU;mBACd,EAAG,iCAAiC,OAAO;SACtC,CAAA,EACR,kBAAC,SAAD;SACE,cAAW;SACX,MAAK;SACL,OAAO,EAAS;SAChB,WAAW,MAAM,EAAY;UAAE,GAAG;UAAU,MAAM,EAAE,OAAO;UAAO,CAAC;SACnE,WAAU;SACV,aAAa,EAAG,4CAA4C,eAAe;SAC3E,CAAA,CACE,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAU;mBACd,EAAG,iCAAiC,OAAO;SACtC,CAAA,EACR,kBAAC,UAAD;SACE,OAAO,EAAS;SAChB,WAAW,MAAM,EAAY;UAAE,GAAG;UAAU,MAAM,EAAE,OAAO;UAAO,CAAC;SACnE,WAAU;mBAET,EACE,QAAQ,MAAM,EAAE,UAAU,MAAM,CAChC,KAAK,MACJ,kBAAC,UAAD;UAAsB,OAAO,EAAE;oBAC5B,EAAE;UACI,EAFI,EAAE,MAEN,CACT;SACG,CAAA,CACL,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAU;mBACd,EAAG,oCAAoC,UAAU;SAC5C,CAAA,EACR,kBAAC,YAAD;SACE,cAAW;SACX,OAAO,EAAS;SAChB,WAAW,MAAM,EAAY;UAAE,GAAG;UAAU,SAAS,EAAE,OAAO;UAAO,CAAC;SACtE,MAAM;SACN,WAAU;SACV,aAAa,EACX,+CACA,qBACD;SACD,CAAA,CACE,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA;SACE,kBAAC,SAAD;UAAO,WAAU;oBACd,EAAG,iCAAiC,OAAO;UACtC,CAAA;SACR,kBAAC,OAAD;UAAK,WAAU;oBACZ,EAAS,KAAK,KAAK,MAClB,kBAAC,QAAD;WAEE,WAAU;qBAFZ,CAIG,GACD,kBAAC,UAAD;YAAQ,MAAK;YAAS,eAAe,GAAU,EAAI;sBACjD,kBAAC,GAAD,EAAG,WAAU,2DAA4D,CAAA;YAClE,CAAA,CACJ;aAPA,EAOA,CACP;UACE,CAAA;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,SAAD;WACE,cAAW;WACX,MAAK;WACL,OAAO;WACP,WAAW,MAAM,EAAY,EAAE,OAAO,MAAM;WAC5C,YAAY,MAAM;AAChB,YAAI,EAAE,QAAQ,YACZ,EAAE,gBAAgB,EAClB,GAAQ;;WAGZ,WAAU;WACV,aAAa,EAAG,8CAA8C,eAAe;WAC7E,CAAA,EACF,kBAAC,UAAD;WACE,MAAK;WACL,SAAS;WACT,WAAU;qBAET,EAAG,gCAAgC,MAAM;WACnC,CAAA,CACL;;SACF,EAAA,CAAA;QACF;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAY,GAAM;QACjC,WAAU;kBAET,EAAG,mCAAmC,SAAS;QACzC,CAAA,EACT,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,UAAU,KAAU,CAAC,EAAS,QAAQ,CAAC,EAAS;QAChD,WAAU;kBAET,IACG,EAAG,mCAAmC,YAAY,GAClD,IACE,EAAG,mCAAmC,SAAS,GAC/C,EAAG,mCAAmC,SAAS;QAC9C,CAAA,CACL;;MACF;;IACF,CAAA;GAIR,kBAAC,GAAD;IACE,QAAQ,EAAU,SAAS;IAC3B,OAAO,EAAU,SAAS,IAAI,oBAAoB;IAClD,SACE,EAAU,SAAS,IACf,mCAAmC,EAAU,OAAO,qDACpD;IAEN,cAAc,IAAW,gBAAgB;IACzC,SAAQ;IACR,WAAW;IACX,eAAe,EAAa,EAAE,CAAC;IAC/B,CAAA;GACE"}
1
+ {"version":3,"file":"AIContextsTab.js","names":[],"sources":["../../../src/components/ai/AIContextsTab.tsx"],"sourcesContent":["import { useState, useEffect, useCallback, useMemo } from 'react';\nimport { Plus, X, FileText, Trash2, Pencil } from 'lucide-react';\nimport {\n Card,\n LoadingState,\n ErrorState,\n EmptyState,\n ConfirmDialog,\n SelectionCheckbox,\n} from '@/components/shared';\nimport { EntityShareButton } from '@/components/sharing';\nimport type { AIContext, AgentRef } from '@/services/aiApi';\nimport { fetchContexts, createContext, updateContext, deleteContext } from '@/services/aiApi';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AIContextsTabProps {\n agent: AgentRef;\n}\n\nconst contextTypes = [\n { value: 'all', label: 'All Types' },\n { value: 'system', label: 'System' },\n { value: 'user', label: 'User' },\n { value: 'file', label: 'File' },\n { value: 'project', label: 'Project' },\n { value: 'custom', label: 'Custom' },\n];\n\ninterface ContextFormData {\n name: string;\n type: string;\n content: string;\n tags: string[];\n}\n\nconst emptyForm: ContextFormData = { name: '', type: 'custom', content: '', tags: [] };\n\nexport function AIContextsTab({ agent }: AIContextsTabProps) {\n const tr = useTr();\n const [contexts, setContexts] = useState<AIContext[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [typeFilter, setTypeFilter] = useState('all');\n const [showForm, setShowForm] = useState(false);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [formData, setFormData] = useState<ContextFormData>(emptyForm);\n const [tagInput, setTagInput] = useState('');\n const [saving, setSaving] = useState(false);\n const [deleteIds, setDeleteIds] = useState<string[]>([]);\n const [deleting, setDeleting] = useState(false);\n const [selectedContextIds, setSelectedContextIds] = useState<Set<string>>(new Set());\n\n const loadContexts = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const params: Record<string, unknown> = {};\n if (typeFilter !== 'all') params.type = typeFilter;\n const result = await fetchContexts(agent, params);\n setContexts(result);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load contexts');\n } finally {\n setLoading(false);\n }\n }, [agent, typeFilter]);\n\n useEffect(() => {\n loadContexts();\n }, [loadContexts]);\n\n const selectedContexts = useMemo(\n () => contexts.filter((context) => selectedContextIds.has(context.id)),\n [contexts, selectedContextIds]\n );\n const allVisibleSelected =\n contexts.length > 0 && contexts.every((context) => selectedContextIds.has(context.id));\n const partiallySelected =\n contexts.some((context) => selectedContextIds.has(context.id)) && !allVisibleSelected;\n\n const openCreate = () => {\n setEditingId(null);\n setFormData(emptyForm);\n setTagInput('');\n setShowForm(true);\n };\n\n const openEdit = (ctx: AIContext) => {\n setEditingId(ctx.id);\n setFormData({ name: ctx.name, type: ctx.type, content: ctx.content, tags: ctx.tags ?? [] });\n setTagInput('');\n setShowForm(true);\n };\n\n const handleSave = async () => {\n setSaving(true);\n try {\n if (editingId) {\n await updateContext(agent, editingId, formData);\n } else {\n await createContext(agent, formData);\n }\n setShowForm(false);\n await loadContexts();\n } catch (err) {\n console.error('Failed to save context:', err);\n } finally {\n setSaving(false);\n }\n };\n\n const handleDelete = async () => {\n if (deleteIds.length === 0) return;\n setDeleting(true);\n try {\n await Promise.allSettled(deleteIds.map((deleteId) => deleteContext(agent, deleteId)));\n setSelectedContextIds((prev) => {\n const next = new Set(prev);\n deleteIds.forEach((deleteId) => next.delete(deleteId));\n return next;\n });\n setDeleteIds([]);\n await loadContexts();\n } catch (err) {\n console.error('Failed to delete context:', err);\n } finally {\n setDeleting(false);\n }\n };\n\n const addTag = () => {\n const tag = tagInput.trim();\n if (tag && !formData.tags.includes(tag)) {\n setFormData((prev) => ({ ...prev, tags: [...prev.tags, tag] }));\n }\n setTagInput('');\n };\n\n const removeTag = (tag: string) => {\n setFormData((prev) => ({ ...prev, tags: prev.tags.filter((t) => t !== tag) }));\n };\n\n const toggleContextSelection = (contextId: string, checked: boolean) => {\n setSelectedContextIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(contextId);\n } else {\n next.delete(contextId);\n }\n return next;\n });\n };\n\n const toggleAllVisibleContexts = (checked: boolean) => {\n setSelectedContextIds((prev) => {\n const next = new Set(prev);\n contexts.forEach((context) => {\n if (checked) {\n next.add(context.id);\n } else {\n next.delete(context.id);\n }\n });\n return next;\n });\n };\n\n const clearSelection = () => setSelectedContextIds(new Set());\n\n if (loading)\n return <LoadingState message={tr('vibecontrols.ai.contexts.loading', 'Loading contexts...')} />;\n if (error)\n return (\n <ErrorState\n title={tr('vibecontrols.ai.contexts.loadFailed', 'Failed to load contexts')}\n message={error}\n onRetry={loadContexts}\n />\n );\n\n return (\n <div className=\"space-y-4\">\n {/* Toolbar */}\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-2\">\n <select\n value={typeFilter}\n onChange={(e) => setTypeFilter(e.target.value)}\n className=\"px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm\"\n >\n {contextTypes.map((t) => (\n <option key={t.value} value={t.value}>\n {t.label}\n </option>\n ))}\n </select>\n <div className=\"flex-1\" />\n <button\n type=\"button\"\n onClick={openCreate}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors text-sm\"\n >\n <Plus className=\"size-4\" />\n {tr('vibecontrols.ai.contexts.newContext', 'New Context')}\n </button>\n </div>\n\n {contexts.length > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleContexts}\n ariaLabel=\"Select all visible contexts\"\n />\n <span className=\"text-sm font-medium text-text-primary\">\n {tr('vibecontrols.ai.contexts.selectAllVisible', 'Select all visible')}\n </span>\n </div>\n {selectedContextIds.size > 0 && (\n <button\n type=\"button\"\n onClick={clearSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n {tr('vibecontrols.ai.contexts.clearSelection', 'Clear selection')}\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedContextIds.size} {tr('vibecontrols.ai.contexts.selected', 'selected')}\n </span>\n <button\n type=\"button\"\n onClick={() => setDeleteIds(selectedContexts.map((context) => context.id))}\n disabled={selectedContexts.length === 0}\n className=\"rounded-md border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr('vibecontrols.ai.contexts.deleteSelected', 'Delete Selected')}\n {selectedContexts.length > 0 ? ` (${selectedContexts.length})` : ''}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Context List */}\n {contexts.length === 0 ? (\n <EmptyState\n icon={<FileText className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.ai.contexts.emptyTitle', 'No contexts yet')}\n description={tr(\n 'vibecontrols.ai.contexts.emptyDescription',\n 'Create contexts to provide additional information to your AI sessions'\n )}\n action={\n <button\n type=\"button\"\n onClick={openCreate}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('vibecontrols.ai.contexts.newContext', 'New Context')}\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {contexts.map((ctx) => (\n <Card\n key={ctx.id}\n className={`p-4 ${selectedContextIds.has(ctx.id) ? 'border-action-primary-bg ring-1 ring-action-primary-bg/25' : ''}`}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-3 min-w-0 flex-1\">\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selectedContextIds.has(ctx.id)}\n onChange={(checked) => toggleContextSelection(ctx.id, checked)}\n ariaLabel={`Select context ${ctx.name}`}\n />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <h4 className=\"font-semibold text-text-primary truncate\">{ctx.name}</h4>\n <span className=\"px-1.5 py-0.5 text-xs bg-[var(--color-accent-soft)] text-action-primary-bg rounded\">\n {ctx.type}\n </span>\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-1 flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => openEdit(ctx)}\n className=\"p-1.5 hover:bg-bg-sunken rounded-md transition-colors\"\n title=\"Edit\"\n >\n <Pencil className=\"size-3.5 text-text-secondary\" />\n </button>\n <EntityShareButton\n entity=\"aicontext\"\n resource={{ id: ctx.id, name: ctx.name }}\n label={tr('vibecontrols.ai.contexts.share', 'Share')}\n />\n <button\n type=\"button\"\n onClick={() => setDeleteIds([ctx.id])}\n className=\"p-1.5 hover:bg-status-error-bg rounded-md transition-colors\"\n title=\"Delete\"\n >\n <Trash2 className=\"size-3.5 text-status-error-text\" />\n </button>\n </div>\n </div>\n <pre className=\"mt-2 text-xs text-text-secondary bg-bg-sunken rounded p-2 max-h-20 overflow-auto whitespace-pre-wrap\">\n {ctx.content}\n </pre>\n {ctx.tags.length > 0 && (\n <div className=\"mt-2 flex flex-wrap gap-1\">\n {ctx.tags.map((tag) => (\n <span\n key={tag}\n className=\"px-1.5 py-0.5 text-xs bg-bg-sunken text-text-secondary rounded\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n </Card>\n ))}\n </div>\n )}\n\n {/* Create/Edit Dialog */}\n {showForm && (\n <div\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (() => setShowForm(false))();\n }\n }}\n className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\"\n onClick={() => setShowForm(false)}\n >\n <div\n className=\"bg-bg-surface border border-border-default rounded-lg shadow-xl w-full max-w-lg max-h-[90vh] overflow-y-auto p-6\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {editingId\n ? tr('vibecontrols.ai.contexts.editContext', 'Edit Context')\n : tr('vibecontrols.ai.contexts.newContext', 'New Context')}\n </h3>\n <button\n type=\"button\"\n onClick={() => setShowForm(false)}\n className=\"p-1 hover:bg-bg-sunken rounded-md\"\n >\n <X className=\"size-4 text-text-secondary\" />\n </button>\n </div>\n\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.ai.contexts.name', 'Name')}\n </label>\n <input\n aria-label=\"control\"\n type=\"text\"\n value={formData.name}\n onChange={(e) => setFormData({ ...formData, name: e.target.value })}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm focus:outline-none focus:border-border-strong\"\n placeholder={tr('vibecontrols.ai.contexts.namePlaceholder', 'Context name')}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.ai.contexts.type', 'Type')}\n </label>\n <select\n value={formData.type}\n onChange={(e) => setFormData({ ...formData, type: e.target.value })}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm\"\n >\n {contextTypes\n .filter((t) => t.value !== 'all')\n .map((t) => (\n <option key={t.value} value={t.value}>\n {t.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.ai.contexts.content', 'Content')}\n </label>\n <textarea\n aria-label=\"control\"\n value={formData.content}\n onChange={(e) => setFormData({ ...formData, content: e.target.value })}\n rows={8}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm font-mono focus:outline-none focus:border-border-strong\"\n placeholder={tr(\n 'vibecontrols.ai.contexts.contentPlaceholder',\n 'Context content...'\n )}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.ai.contexts.tags', 'Tags')}\n </label>\n <div className=\"flex flex-wrap gap-1 mb-2\">\n {formData.tags.map((tag) => (\n <span\n key={tag}\n className=\"flex items-center gap-1 px-2 py-0.5 text-xs bg-bg-sunken text-text-primary rounded\"\n >\n {tag}\n <button type=\"button\" onClick={() => removeTag(tag)}>\n <X className=\"size-3 text-text-secondary hover:text-status-error-text\" />\n </button>\n </span>\n ))}\n </div>\n <div className=\"flex gap-2\">\n <input\n aria-label=\"control\"\n type=\"text\"\n value={tagInput}\n onChange={(e) => setTagInput(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n addTag();\n }\n }}\n className=\"flex-1 px-3 py-1.5 bg-bg-sunken border border-border-default rounded text-sm text-text-primary focus:outline-none focus:border-border-strong\"\n placeholder={tr('vibecontrols.ai.contexts.addTagPlaceholder', 'Add a tag...')}\n />\n <button\n type=\"button\"\n onClick={addTag}\n className=\"px-3 py-1.5 text-xs bg-bg-sunken border border-border-default rounded hover:bg-bg-surface transition-colors\"\n >\n {tr('vibecontrols.ai.contexts.add', 'Add')}\n </button>\n </div>\n </div>\n </div>\n\n <div className=\"flex justify-end gap-2 mt-6\">\n <button\n type=\"button\"\n onClick={() => setShowForm(false)}\n className=\"px-4 py-2 text-sm text-text-secondary hover:bg-bg-sunken rounded-md transition-colors\"\n >\n {tr('vibecontrols.ai.contexts.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={saving || !formData.name || !formData.content}\n className=\"px-4 py-2 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {saving\n ? tr('vibecontrols.ai.contexts.saving', 'Saving...')\n : editingId\n ? tr('vibecontrols.ai.contexts.update', 'Update')\n : tr('vibecontrols.ai.contexts.create', 'Create')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Delete Confirmation */}\n <ConfirmDialog\n isOpen={deleteIds.length > 0}\n title={deleteIds.length > 1 ? 'Delete Contexts' : 'Delete Context'}\n message={\n deleteIds.length > 1\n ? `Are you sure you want to delete ${deleteIds.length} selected contexts? This action cannot be undone.`\n : 'Are you sure you want to delete this context? This action cannot be undone.'\n }\n confirmLabel={deleting ? 'Deleting...' : 'Delete'}\n variant=\"danger\"\n onConfirm={handleDelete}\n onClose={() => setDeleteIds([])}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -11,10 +11,10 @@ import { jsx as m, jsxs as h } from "react/jsx-runtime";
11
11
  import { MotionItem as g, MotionList as _ } from "@burdenoff/fe-libs/shared/motion";
12
12
  //#region src/components/ai/AIOverviewTab.tsx
13
13
  var v = {
14
- active: "bg-status-success-bg text-status-success-text",
15
- idle: "bg-status-warning-bg text-status-warning-text",
16
- processing: "bg-action-primary-bg/20 text-action-primary-bg",
17
- error: "bg-status-error-bg text-status-error-text",
14
+ active: "bg-status-success-bg-subtle text-status-success-text",
15
+ idle: "bg-status-warning-bg-subtle text-status-warning-text",
16
+ processing: "bg-[var(--color-accent-soft)] text-action-primary-bg",
17
+ error: "bg-status-error-bg-subtle text-status-error-text",
18
18
  terminated: "bg-bg-sunken text-text-secondary"
19
19
  };
20
20
  function y({ agent: y, onNavigateToSession: b }) {
@@ -1 +1 @@
1
- {"version":3,"file":"AIOverviewTab.js","names":[],"sources":["../../../src/components/ai/AIOverviewTab.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { Activity, Zap, Clock, MessageSquare } from 'lucide-react';\nimport { StatCard, Card, LoadingState, ErrorState } from '@/components/shared';\nimport { MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport type { AIStatsOverview, AISession, AIDispatch, AgentRef } from '@/services/aiApi';\nimport { fetchStatsOverview, fetchSessions, fetchDispatchHistory } from '@/services/aiApi';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AIOverviewTabProps {\n agent: AgentRef;\n onNavigateToSession?: (sessionId: string) => void;\n}\n\nconst statusColors: Record<string, string> = {\n active: 'bg-status-success-bg text-status-success-text',\n idle: 'bg-status-warning-bg text-status-warning-text',\n processing: 'bg-action-primary-bg/20 text-action-primary-bg',\n error: 'bg-status-error-bg text-status-error-text',\n terminated: 'bg-bg-sunken text-text-secondary',\n};\n\nexport function AIOverviewTab({ agent, onNavigateToSession }: AIOverviewTabProps) {\n const tr = useTr();\n const [stats, setStats] = useState<AIStatsOverview | null>(null);\n const [recentSessions, setRecentSessions] = useState<AISession[]>([]);\n const [recentPrompts, setRecentPrompts] = useState<AIDispatch[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n async function load() {\n setLoading(true);\n setError(null);\n try {\n const [s, sess, hist] = await Promise.all([\n fetchStatsOverview(agent),\n fetchSessions(agent, { limit: 5, sort: 'updatedAt:desc' }),\n fetchDispatchHistory(agent, { limit: 5 }),\n ]);\n if (cancelled) return;\n setStats(s);\n setRecentSessions(sess);\n setRecentPrompts(hist);\n } catch (err) {\n if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to load overview');\n } finally {\n if (!cancelled) setLoading(false);\n }\n }\n load();\n return () => {\n cancelled = true;\n };\n }, [agent.id, agent.profile]);\n\n if (loading)\n return (\n <LoadingState message={tr('vibecontrols.ai.overview.loading', 'Loading AI overview...')} />\n );\n if (error)\n return (\n <ErrorState\n title={tr('vibecontrols.ai.overview.loadFailed', 'Failed to load overview')}\n message={error}\n />\n );\n\n return (\n <div className=\"space-y-6\">\n {/* Quick Stats */}\n <MotionList stagger={0.04} className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n <MotionItem>\n <StatCard\n title={tr('vibecontrols.ai.overview.activeSessions', 'Active Sessions')}\n value={stats?.activeSessions ?? 0}\n icon={<Activity className=\"size-5\" />}\n variant=\"success\"\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('vibecontrols.ai.overview.totalTokens', 'Total Tokens')}\n value={stats?.totalTokens?.toLocaleString() ?? '0'}\n subtitle={`In: ${stats?.inputTokens?.toLocaleString() ?? 0} / Out: ${stats?.outputTokens?.toLocaleString() ?? 0}`}\n icon={<Zap className=\"size-5\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('vibecontrols.ai.overview.queuedPrompts', 'Queued Prompts')}\n value={stats?.queuedPrompts ?? 0}\n icon={<Clock className=\"size-5\" />}\n variant={stats?.queuedPrompts && stats.queuedPrompts > 0 ? 'warning' : 'default'}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('vibecontrols.ai.overview.totalSessions', 'Total Sessions')}\n value={stats?.totalSessions ?? 0}\n icon={<MessageSquare className=\"size-5\" />}\n />\n </MotionItem>\n </MotionList>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Recent Sessions */}\n <Card className=\"p-0\">\n <div className=\"px-4 py-3 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibecontrols.ai.overview.recentSessions', 'Recent Sessions')}\n </h3>\n </div>\n <div className=\"divide-y divide-border-default\">\n {recentSessions.length === 0 ? (\n <p className=\"px-4 py-6 text-sm text-text-secondary text-center\">\n {tr('vibecontrols.ai.overview.noSessions', 'No sessions yet')}\n </p>\n ) : (\n recentSessions.map((session) => (\n <button\n type=\"button\"\n key={session.id}\n onClick={() => onNavigateToSession?.(session.id)}\n className=\"flex w-full flex-col items-start gap-2 px-4 py-3 text-left transition-colors hover:bg-bg-sunken sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"font-medium text-text-primary text-sm truncate\">\n {session.name}\n </div>\n <div className=\"text-xs text-text-secondary\">{session.agentType}</div>\n </div>\n <span\n className={`rounded-full px-2 py-0.5 text-xs font-medium ${statusColors[session.status] ?? 'bg-bg-sunken text-text-secondary'}`}\n >\n {session.status}\n </span>\n </button>\n ))\n )}\n </div>\n </Card>\n\n {/* Recent Prompts */}\n <Card className=\"p-0\">\n <div className=\"px-4 py-3 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibecontrols.ai.overview.recentPrompts', 'Recent Prompts')}\n </h3>\n </div>\n <div className=\"divide-y divide-border-default\">\n {recentPrompts.length === 0 ? (\n <p className=\"px-4 py-6 text-sm text-text-secondary text-center\">\n {tr('vibecontrols.ai.overview.noPrompts', 'No prompts dispatched yet')}\n </p>\n ) : (\n recentPrompts.map((dispatch) => (\n <div key={dispatch.id} className=\"px-4 py-3\">\n <div className=\"text-sm text-text-primary line-clamp-2\">{dispatch.prompt}</div>\n <div className=\"mt-1 flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n <span>{dispatch.status}</span>\n {dispatch.createdAt && (\n <span>{new Date(dispatch.createdAt).toLocaleString()}</span>\n )}\n </div>\n </div>\n ))\n )}\n </div>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AIOverviewTab.js","names":[],"sources":["../../../src/components/ai/AIOverviewTab.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { Activity, Zap, Clock, MessageSquare } from 'lucide-react';\nimport { StatCard, Card, LoadingState, ErrorState } from '@/components/shared';\nimport { MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport type { AIStatsOverview, AISession, AIDispatch, AgentRef } from '@/services/aiApi';\nimport { fetchStatsOverview, fetchSessions, fetchDispatchHistory } from '@/services/aiApi';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AIOverviewTabProps {\n agent: AgentRef;\n onNavigateToSession?: (sessionId: string) => void;\n}\n\nconst statusColors: Record<string, string> = {\n active: 'bg-status-success-bg-subtle text-status-success-text',\n idle: 'bg-status-warning-bg-subtle text-status-warning-text',\n processing: 'bg-[var(--color-accent-soft)] text-action-primary-bg',\n error: 'bg-status-error-bg-subtle text-status-error-text',\n terminated: 'bg-bg-sunken text-text-secondary',\n};\n\nexport function AIOverviewTab({ agent, onNavigateToSession }: AIOverviewTabProps) {\n const tr = useTr();\n const [stats, setStats] = useState<AIStatsOverview | null>(null);\n const [recentSessions, setRecentSessions] = useState<AISession[]>([]);\n const [recentPrompts, setRecentPrompts] = useState<AIDispatch[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n async function load() {\n setLoading(true);\n setError(null);\n try {\n const [s, sess, hist] = await Promise.all([\n fetchStatsOverview(agent),\n fetchSessions(agent, { limit: 5, sort: 'updatedAt:desc' }),\n fetchDispatchHistory(agent, { limit: 5 }),\n ]);\n if (cancelled) return;\n setStats(s);\n setRecentSessions(sess);\n setRecentPrompts(hist);\n } catch (err) {\n if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to load overview');\n } finally {\n if (!cancelled) setLoading(false);\n }\n }\n load();\n return () => {\n cancelled = true;\n };\n }, [agent.id, agent.profile]);\n\n if (loading)\n return (\n <LoadingState message={tr('vibecontrols.ai.overview.loading', 'Loading AI overview...')} />\n );\n if (error)\n return (\n <ErrorState\n title={tr('vibecontrols.ai.overview.loadFailed', 'Failed to load overview')}\n message={error}\n />\n );\n\n return (\n <div className=\"space-y-6\">\n {/* Quick Stats */}\n <MotionList stagger={0.04} className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n <MotionItem>\n <StatCard\n title={tr('vibecontrols.ai.overview.activeSessions', 'Active Sessions')}\n value={stats?.activeSessions ?? 0}\n icon={<Activity className=\"size-5\" />}\n variant=\"success\"\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('vibecontrols.ai.overview.totalTokens', 'Total Tokens')}\n value={stats?.totalTokens?.toLocaleString() ?? '0'}\n subtitle={`In: ${stats?.inputTokens?.toLocaleString() ?? 0} / Out: ${stats?.outputTokens?.toLocaleString() ?? 0}`}\n icon={<Zap className=\"size-5\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('vibecontrols.ai.overview.queuedPrompts', 'Queued Prompts')}\n value={stats?.queuedPrompts ?? 0}\n icon={<Clock className=\"size-5\" />}\n variant={stats?.queuedPrompts && stats.queuedPrompts > 0 ? 'warning' : 'default'}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('vibecontrols.ai.overview.totalSessions', 'Total Sessions')}\n value={stats?.totalSessions ?? 0}\n icon={<MessageSquare className=\"size-5\" />}\n />\n </MotionItem>\n </MotionList>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Recent Sessions */}\n <Card className=\"p-0\">\n <div className=\"px-4 py-3 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibecontrols.ai.overview.recentSessions', 'Recent Sessions')}\n </h3>\n </div>\n <div className=\"divide-y divide-border-default\">\n {recentSessions.length === 0 ? (\n <p className=\"px-4 py-6 text-sm text-text-secondary text-center\">\n {tr('vibecontrols.ai.overview.noSessions', 'No sessions yet')}\n </p>\n ) : (\n recentSessions.map((session) => (\n <button\n type=\"button\"\n key={session.id}\n onClick={() => onNavigateToSession?.(session.id)}\n className=\"flex w-full flex-col items-start gap-2 px-4 py-3 text-left transition-colors hover:bg-bg-sunken sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"font-medium text-text-primary text-sm truncate\">\n {session.name}\n </div>\n <div className=\"text-xs text-text-secondary\">{session.agentType}</div>\n </div>\n <span\n className={`rounded-full px-2 py-0.5 text-xs font-medium ${statusColors[session.status] ?? 'bg-bg-sunken text-text-secondary'}`}\n >\n {session.status}\n </span>\n </button>\n ))\n )}\n </div>\n </Card>\n\n {/* Recent Prompts */}\n <Card className=\"p-0\">\n <div className=\"px-4 py-3 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibecontrols.ai.overview.recentPrompts', 'Recent Prompts')}\n </h3>\n </div>\n <div className=\"divide-y divide-border-default\">\n {recentPrompts.length === 0 ? (\n <p className=\"px-4 py-6 text-sm text-text-secondary text-center\">\n {tr('vibecontrols.ai.overview.noPrompts', 'No prompts dispatched yet')}\n </p>\n ) : (\n recentPrompts.map((dispatch) => (\n <div key={dispatch.id} className=\"px-4 py-3\">\n <div className=\"text-sm text-text-primary line-clamp-2\">{dispatch.prompt}</div>\n <div className=\"mt-1 flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n <span>{dispatch.status}</span>\n {dispatch.createdAt && (\n <span>{new Date(dispatch.createdAt).toLocaleString()}</span>\n )}\n </div>\n </div>\n ))\n )}\n </div>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -94,7 +94,7 @@ function O({ agent: e, tunnelUrl: t }) {
94
94
  className: "flex items-center gap-2 p-6 text-text-secondary",
95
95
  children: [/* @__PURE__ */ T(v, { className: "size-4 animate-spin" }), /* @__PURE__ */ T("span", { children: r("plan.loadingStatus", "Loading plan status…") })]
96
96
  }) : H === "status-error" ? /* @__PURE__ */ T("div", {
97
- className: "rounded-md border border-status-error-border bg-status-error-bg p-4 text-sm text-status-error-text",
97
+ className: "rounded-md border border-status-error-border bg-status-error-bg-subtle p-4 text-sm text-status-error-text",
98
98
  children: /* @__PURE__ */ E("div", {
99
99
  className: "flex items-start gap-2",
100
100
  children: [/* @__PURE__ */ T(h, { className: "size-4 mt-0.5 shrink-0" }), /* @__PURE__ */ E("div", {
@@ -126,12 +126,12 @@ function O({ agent: e, tunnelUrl: t }) {
126
126
  children: [
127
127
  N && /* @__PURE__ */ T("div", {
128
128
  role: "status",
129
- className: "rounded-md border border-status-success-border bg-status-success-bg px-3 py-2 text-xs text-status-success-text",
129
+ className: "rounded-md border border-status-success-border bg-status-success-bg-subtle px-3 py-2 text-xs text-status-success-text",
130
130
  children: N
131
131
  }),
132
132
  F && /* @__PURE__ */ T("div", {
133
133
  role: "alert",
134
- className: "rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-xs text-status-error-text",
134
+ className: "rounded-md border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text",
135
135
  children: F
136
136
  }),
137
137
  H === "install-cli" && /* @__PURE__ */ T(k, {
@@ -304,7 +304,7 @@ function j({ agent: n, agents: r, tunnelUrl: i, runningSessions: a, onReconfigur
304
304
  });
305
305
  }, []);
306
306
  if (!i) return /* @__PURE__ */ T("div", {
307
- className: "rounded-lg border border-status-warning-border bg-status-warning-bg p-4 text-sm text-status-warning-text",
307
+ className: "rounded-lg border border-status-warning-border bg-status-warning-bg-subtle p-4 text-sm text-status-warning-text",
308
308
  children: s("plan.tunnelMissing", "Agent tunnel URL is missing. Bring the agent online and refresh.")
309
309
  });
310
310
  let I = r.filter((e) => e.hookConfigured);
@@ -1 +1 @@
1
- {"version":3,"file":"AIPlanPanel.js","names":[],"sources":["../../../src/components/ai/AIPlanPanel.tsx"],"sourcesContent":["/**\n * AIPlanPanel — the body of /ai-tools/plan.\n *\n * Five UI states gated in order:\n * 1. installing-plugin → vibe-plugin-plan + vibe-plugin-plan-plannotator\n * not installed on the agent. Renders the PluginGate install card.\n * 2. installing-cli → plugins installed but plannotator binary\n * missing on disk. Renders an Install CLI CTA.\n * 3. wizard → CLI installed but no AI-agent hook configured\n * yet. Renders the Claude/OpenCode/Codex/Pi/Gemini wizard.\n * 4. ready → all prereqs satisfied. Renders the tunnel\n * iframe pointing at ${tunnelUrl}/plan/, with postMessage auth\n * handoff on iframe load (mirrors PluginUIPanel.tsx).\n * 5. error → unrecoverable load failure for the status\n * query. Renders an error banner with retry.\n *\n * Auth handoff (matches PluginUIPanel.tsx:194–210):\n * onLoad → iframe.contentWindow.postMessage({type:'vibe-auth', apiKey},\n * new URL(tunnelUrl).origin)\n *\n * Surface contracts:\n * GET /api/plan-plannotator/status → PlannotatorStatus\n * POST /api/plan-plannotator/install → install binary\n * GET /api/plan-plannotator/agents/supported → AgentDetectionResult[]\n * POST /api/plan-plannotator/agents/:agent/configure-hook\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n AlertCircle,\n CheckCircle2,\n Inbox,\n Loader2,\n PlayCircle,\n RefreshCcw,\n Settings,\n Wifi,\n WifiOff,\n} from 'lucide-react';\n\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PluginGate } from '@/components/shared';\nimport { PLAN_META_PACKAGE, PLAN_PLANNOTATOR_PACKAGE } from '@/constants/pluginCatalog';\nimport {\n configureAgentHook,\n getPlannotatorStatus,\n installPlannotatorBinary,\n listSupportedAgents,\n type AgentDetectionResult,\n type PlannotatorStatus,\n} from '@/services/planApi';\nimport type { AgentRef } from '@/services/aiApi';\nimport { useMintAgentCapabilityMutation } from '@/generated/wspace-operations';\nimport { AgentCapabilityScope } from '@/generated/wspace-types';\n\ninterface AIPlanPanelProps {\n agent: AgentRef;\n tunnelUrl: string | null;\n}\n\ntype ConnectionStatus = 'connecting' | 'connected' | 'error';\n\nexport function AIPlanPanel({ agent, tunnelUrl }: AIPlanPanelProps) {\n const { t } = useI18n();\n // PluginGate renders eyebrow/title/description verbatim (no i18n lookup of\n // its own), so resolve them here with English fallbacks until plan.* keys exist.\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n return (\n <PluginGate\n agentId={agent.id}\n required={[PLAN_META_PACKAGE, PLAN_PLANNOTATOR_PACKAGE]}\n mode=\"all\"\n eyebrow={tr('plan.pluginRequiredEyebrow', 'Plan plugin required')}\n title={tr('plan.installPlanProviderTitle', 'Install the plan provider')}\n description={tr(\n 'plan.installPlanProviderDesc',\n 'Plan mode routes plan sessions through a plan provider plugin on your agent. Install the orchestrator and a provider (Plannotator) below to continue.'\n )}\n >\n <PlanPanelInner agent={agent} tunnelUrl={tunnelUrl} />\n </PluginGate>\n );\n}\n\nfunction PlanPanelInner({ agent, tunnelUrl }: AIPlanPanelProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const [status, setStatus] = useState<PlannotatorStatus | null>(null);\n const [statusError, setStatusError] = useState<string | null>(null);\n const [statusLoading, setStatusLoading] = useState(true);\n const [agents, setAgents] = useState<AgentDetectionResult[]>([]);\n const [installing, setInstalling] = useState(false);\n const [configuring, setConfiguring] = useState<string | null>(null);\n const [actionMessage, setActionMessage] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n // \"Manage hooks\" forces the wizard back open even when a hook is already\n // configured — the only way to re-write a stale/broken hook (wrong path or\n // a key from a prior agent identity), which otherwise locks the panel into\n // the ready view forever.\n const [forceWizard, setForceWizard] = useState(false);\n\n const refresh = useCallback(\n // `silent` background refreshes (polling) only update session status: they\n // skip the loading/error toggles AND the agent re-detection, so a transient\n // listSupportedAgents failure can't demote the ready view to the wizard.\n async (silent = false) => {\n if (!silent) {\n setStatusLoading(true);\n setStatusError(null);\n }\n try {\n if (silent) {\n setStatus(await getPlannotatorStatus(agent));\n return;\n }\n const [statusResult, agentsResult] = await Promise.all([\n getPlannotatorStatus(agent),\n listSupportedAgents(agent).catch(() => ({ agents: [] })),\n ]);\n setStatus(statusResult);\n setAgents(agentsResult.agents ?? []);\n } catch (err) {\n if (!silent) {\n setStatusError(err instanceof Error ? err.message : 'Failed to load plan status');\n }\n } finally {\n if (!silent) setStatusLoading(false);\n }\n },\n [agent]\n );\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n const handleInstallCli = useCallback(async () => {\n setInstalling(true);\n setActionError(null);\n setActionMessage(null);\n try {\n const result = await installPlannotatorBinary(agent);\n if (result.ok) {\n setActionMessage(tr('plan.cliInstalled', 'Plannotator CLI installed.'));\n await refresh();\n } else {\n setActionError(result.error ?? 'Install failed');\n }\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Install failed');\n } finally {\n setInstalling(false);\n }\n }, [agent, refresh, tr]);\n\n const handleConfigureHook = useCallback(\n async (agentKind: AgentDetectionResult['agent']) => {\n setConfiguring(agentKind);\n setActionError(null);\n setActionMessage(null);\n try {\n const result = await configureAgentHook(agent, agentKind);\n if (result.ok) {\n setActionMessage(tr('plan.hookConfigured', `Hook configured for ${agentKind}.`));\n await refresh();\n } else {\n setActionError(result.error ?? 'Hook configure failed');\n }\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Hook configure failed');\n } finally {\n setConfiguring(null);\n }\n },\n [agent, refresh, tr]\n );\n\n // Derived render state — guarded ordering means we always render the\n // most prerequisite-bound CTA first.\n const view = useMemo<'loading' | 'status-error' | 'install-cli' | 'wizard' | 'ready'>(() => {\n if (statusLoading) return 'loading';\n if (statusError) return 'status-error';\n if (!status?.installed) return 'install-cli';\n const anyConfigured = agents.some((a) => a.hookConfigured);\n if (!anyConfigured || forceWizard) return 'wizard';\n return 'ready';\n }, [status, statusError, statusLoading, agents, forceWizard]);\n\n // While the ready panel is shown, poll status in the background so a plan\n // session created by a coding agent's hook (or one that ends) is reflected\n // without a manual reload — `runningSessions` gates the iframe vs the empty\n // state below.\n useEffect(() => {\n if (view !== 'ready') return;\n // Skip ticks while the tab is hidden — no point polling a panel nobody sees.\n const id = setInterval(() => {\n if (!document.hidden) void refresh(true);\n }, 5000);\n return () => clearInterval(id);\n }, [view, refresh]);\n\n if (view === 'loading') {\n return (\n <div className=\"flex items-center gap-2 p-6 text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin\" />\n <span>{tr('plan.loadingStatus', 'Loading plan status…')}</span>\n </div>\n );\n }\n\n if (view === 'status-error') {\n return (\n <div className=\"rounded-md border border-status-error-border bg-status-error-bg p-4 text-sm text-status-error-text\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle className=\"size-4 mt-0.5 shrink-0\" />\n <div className=\"flex-1\">\n <p className=\"font-medium\">\n {tr('plan.statusError', 'Could not load plan status from the agent.')}\n </p>\n <p className=\"text-xs opacity-80 mt-1\">{statusError}</p>\n <button\n type=\"button\"\n onClick={() => void refresh()}\n className=\"mt-3 inline-flex items-center gap-1 text-xs font-medium hover:underline\"\n >\n <RefreshCcw className=\"size-3\" /> {tr('plan.retry', 'Retry')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n {actionMessage && (\n <div\n role=\"status\"\n className=\"rounded-md border border-status-success-border bg-status-success-bg px-3 py-2 text-xs text-status-success-text\"\n >\n {actionMessage}\n </div>\n )}\n {actionError && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-xs text-status-error-text\"\n >\n {actionError}\n </div>\n )}\n\n {view === 'install-cli' && (\n <InstallCliCard installing={installing} onInstall={handleInstallCli} tr={tr} />\n )}\n\n {view === 'wizard' && (\n <WizardCard\n agents={agents}\n configuring={configuring}\n onConfigure={handleConfigureHook}\n // Offer \"Done\" only when a ready view exists to return to — i.e.\n // at least one hook is configured (we got here via Manage hooks),\n // not during the genuine first-run wizard.\n canExit={forceWizard && agents.some((a) => a.hookConfigured)}\n onExit={() => setForceWizard(false)}\n tr={tr}\n />\n )}\n\n {view === 'ready' && (\n <PlanReadyPanel\n agent={agent}\n agents={agents}\n tunnelUrl={tunnelUrl}\n runningSessions={status?.runningSessions ?? []}\n onReconfigure={() => {\n setActionMessage(null);\n setForceWizard(true);\n }}\n tr={tr}\n />\n )}\n </div>\n );\n}\n\ninterface InstallCliCardProps {\n installing: boolean;\n onInstall: () => void;\n tr: (key: string, fallback: string) => string;\n}\n\nfunction InstallCliCard({ installing, onInstall, tr }: InstallCliCardProps) {\n return (\n <div className=\"rounded-lg border border-border-default bg-bg-elevated p-6\">\n <div className=\"flex items-start gap-3\">\n <PlayCircle className=\"size-6 text-action-primary-bg shrink-0\" />\n <div className=\"flex-1\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {tr('plan.installCliTitle', 'Install the plannotator CLI')}\n </h3>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'plan.installCliDesc',\n 'Plan mode runs a local plannotator binary on the agent and proxies its UI back here. The plugin installs the binary into ~/.local/bin/plannotator (sha256-pinned). Roughly 30 seconds.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={onInstall}\n disabled={installing}\n className=\"mt-4 inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bgHover disabled:opacity-50\"\n >\n {installing ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('plan.installing', 'Installing…')}\n </>\n ) : (\n tr('plan.installCliCta', 'Install plannotator CLI')\n )}\n </button>\n </div>\n </div>\n </div>\n );\n}\n\ninterface WizardCardProps {\n agents: AgentDetectionResult[];\n configuring: string | null;\n onConfigure: (agentKind: AgentDetectionResult['agent']) => void;\n canExit?: boolean;\n onExit?: () => void;\n tr: (key: string, fallback: string) => string;\n}\n\nfunction WizardCard({ agents, configuring, onConfigure, canExit, onExit, tr }: WizardCardProps) {\n const installed = agents.filter((a) => a.cliInstalled);\n return (\n <div className=\"rounded-lg border border-border-default bg-bg-elevated p-6\">\n <div className=\"flex items-start gap-3\">\n <Settings className=\"size-6 text-action-primary-bg shrink-0\" />\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between gap-3\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {tr('plan.wizardTitle', 'Connect an AI coding agent')}\n </h3>\n {canExit && onExit && (\n <button\n type=\"button\"\n onClick={onExit}\n className=\"shrink-0 inline-flex items-center gap-1 text-xs text-text-secondary hover:text-text-primary\"\n >\n {tr('plan.wizardDone', 'Done')}\n </button>\n )}\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'plan.wizardDesc',\n 'Configure a plannotator hook for at least one installed AI coding agent. The hook forwards plan content from the agent to plannotator on every plan-finalisation step.'\n )}\n </p>\n {installed.length === 0 && (\n <p className=\"mt-4 text-sm text-status-warning-text\">\n {tr(\n 'plan.wizardNoAgents',\n 'No AI coding agents detected in this sandbox. Install one (Claude Code, OpenCode, Codex, Pi, or Gemini) and refresh.'\n )}\n </p>\n )}\n <ul className=\"mt-4 space-y-2\">\n {agents.map((entry) => (\n <li\n key={entry.agent}\n className=\"flex items-start gap-3 p-3 border border-border-default rounded-md bg-bg-surface\"\n >\n {entry.hookConfigured ? (\n <CheckCircle2 className=\"size-5 text-status-success-text shrink-0 mt-0.5\" />\n ) : (\n <div className=\"size-5 rounded-full border-2 border-border-default shrink-0 mt-0.5\" />\n )}\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">{entry.name}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">{entry.instructions}</p>\n {!entry.cliInstalled && (\n <p className=\"text-xs text-status-warning-text mt-1\">\n {tr('plan.agentCliMissing', 'CLI not detected on this sandbox.')}\n </p>\n )}\n </div>\n <button\n type=\"button\"\n // Re-enabled when already configured: re-writing is the only\n // way to repair a stale hook (wrong path / old agent key).\n disabled={!entry.cliInstalled || configuring === entry.agent}\n onClick={() => onConfigure(entry.agent)}\n className=\"px-3 py-1.5 text-xs rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover disabled:opacity-50\"\n >\n {configuring === entry.agent\n ? tr('plan.configuring', 'Configuring…')\n : entry.hookConfigured\n ? tr('plan.reconfigureHook', 'Reconfigure')\n : tr('plan.configureHook', 'Configure hook')}\n </button>\n </li>\n ))}\n </ul>\n </div>\n </div>\n </div>\n );\n}\n\ninterface PlanReadyPanelProps {\n agent: AgentRef;\n agents: AgentDetectionResult[];\n tunnelUrl: string | null;\n runningSessions: PlannotatorStatus['runningSessions'];\n onReconfigure: () => void;\n tr: (key: string, fallback: string) => string;\n}\n\nfunction PlanReadyPanel({\n agent,\n agents,\n tunnelUrl,\n runningSessions,\n onReconfigure,\n tr,\n}: PlanReadyPanelProps) {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const [connection, setConnection] = useState<ConnectionStatus>('connecting');\n\n // plannotator is session-scoped — it only serves a plan at /plan/<sessionId>/.\n // A session exists once a connected coding agent finalises a plan (its hook\n // POSTs to the agent). With none running there is nothing to embed (the bare\n // /plan/ root 404s), so we show an empty state instead. The status endpoint\n // doesn't guarantee ordering, so pick the most recently started session.\n const activeSession = useMemo(\n () =>\n runningSessions.length > 0\n ? // `|| 0` keeps the sort deterministic if a startedAt is malformed (Date.parse → NaN).\n [...runningSessions].sort(\n (a, b) => (Date.parse(b.startedAt) || 0) - (Date.parse(a.startedAt) || 0)\n )[0]\n : null,\n [runningSessions]\n );\n const activeSessionId = activeSession?.sessionId ?? null;\n\n // F4 Wave B — mint a short-lived IFRAME_PLUGIN capability instead of\n // embedding the deprecated long-lived `agentApiKey` in `?apiKey=`. The\n // agent's `/plan/<sessionId>` bootstrap exchanges the `#vt=` fragment\n // for a scoped HttpOnly cookie before proxying to plannotator.\n const [mintAgentCapability] = useMintAgentCapabilityMutation();\n const [iframeToken, setIframeToken] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n if (!agent.id || !activeSessionId) {\n setIframeToken(null);\n return undefined;\n }\n mintAgentCapability({\n variables: {\n agentId: agent.id,\n profile: agent.profile ?? 'default',\n // PLAN_SESSION binds the ticket to `/plan/<sessionId>` — the path the\n // iframe actually loads. IFRAME_PLUGIN would scope it to `/ui/<id>`,\n // which never matches the plan proxy URL (the bug that left the iframe\n // 401'd). The agent's `/plan/*` shim verifies this `/plan/<sid>` ticket.\n scope: AgentCapabilityScope.PlanSession,\n resourceId: activeSessionId,\n ttlSeconds: 60,\n },\n })\n .then((res) => {\n if (cancelled) return;\n setIframeToken(res.data?.mintAgentCapability?.token ?? null);\n })\n .catch(() => {\n if (!cancelled) setIframeToken(null);\n });\n return () => {\n cancelled = true;\n };\n }, [agent.id, agent.profile, activeSessionId, mintAgentCapability]);\n\n const iframeSrc = useMemo(() => {\n if (!tunnelUrl || !iframeToken || !activeSessionId) return null;\n const base = tunnelUrl.replace(/\\/$/, '');\n return `${base}/plan/${activeSessionId}/#vt=${encodeURIComponent(iframeToken)}`;\n }, [tunnelUrl, iframeToken, activeSessionId]);\n\n // Reset the connection indicator when the embedded session changes so a\n // prior session's 'error'/'connected' state doesn't bleed into the next one.\n useEffect(() => {\n setConnection('connecting');\n }, [activeSessionId]);\n\n const sendAuth = useCallback(() => {\n // No-op post-F4: the iframe authenticates via the `#vt=` fragment +\n // agent-issued cookie. Kept as a stable reference for handleLoad.\n }, []);\n\n const handleLoad = useCallback(() => {\n setConnection('connected');\n sendAuth();\n }, [sendAuth]);\n\n const handleError = useCallback(() => {\n setConnection('error');\n }, []);\n\n const handleRetry = useCallback(() => {\n const iframe = iframeRef.current;\n if (!iframe) return;\n setConnection('connecting');\n const src = iframe.src;\n iframe.src = 'about:blank';\n requestAnimationFrame(() => {\n iframe.src = src;\n });\n }, []);\n\n // Used elsewhere for analytics / breadcrumbs.\n void agent;\n\n if (!tunnelUrl) {\n return (\n <div className=\"rounded-lg border border-status-warning-border bg-status-warning-bg p-4 text-sm text-status-warning-text\">\n {tr(\n 'plan.tunnelMissing',\n 'Agent tunnel URL is missing. Bring the agent online and refresh.'\n )}\n </div>\n );\n }\n\n const configuredAgents = agents.filter((a) => a.hookConfigured);\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n {activeSession && (\n <>\n <span className=\"inline-flex items-center gap-1\">\n {connection === 'connected' && <Wifi className=\"size-3.5 text-status-success-text\" />}\n {connection === 'connecting' && (\n <Loader2 className=\"size-3.5 animate-spin text-status-warning-text\" />\n )}\n {connection === 'error' && <WifiOff className=\"size-3.5 text-status-error-text\" />}\n {tr(`plan.connection.${connection}`, connection)}\n </span>\n <span aria-hidden=\"true\">·</span>\n </>\n )}\n <span>{tr('plan.hookCount', `${configuredAgents.length} hook(s) configured`)}</span>\n <button\n type=\"button\"\n onClick={onReconfigure}\n className=\"ml-auto inline-flex items-center gap-1 hover:text-text-primary\"\n >\n <Settings className=\"size-3.5\" />\n {tr('plan.reopenWizard', 'Manage hooks')}\n </button>\n </div>\n\n <div className=\"border border-border-default rounded-lg overflow-hidden bg-bg-sunken relative h-[calc(100vh-320px)] min-h-[500px]\">\n {!activeSession && (\n <div className=\"absolute inset-0 flex flex-col items-center justify-center gap-2 p-6 text-center\">\n <Inbox className=\"size-8 text-text-muted\" />\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('plan.noSessionTitle', 'No active plan session')}\n </p>\n <p className=\"max-w-md text-xs text-text-secondary\">\n {tr(\n 'plan.noSessionDesc',\n 'Start a plan in a connected coding agent — when it finalises a plan, the configured hook forwards it here for review.'\n )}\n </p>\n </div>\n )}\n {iframeSrc && (\n /* The src carries ?apiKey= for the initial auth (an iframe can't set\n request headers). Drop popups/top-navigation and send no referrer\n so that key can't leak out of the frame via a navigation or popup. */\n <iframe\n ref={iframeRef}\n src={iframeSrc}\n title={tr('plan.iframeTitle', 'Plannotator plan workspace')}\n className=\"w-full h-full border-0\"\n allow=\"clipboard-read; clipboard-write\"\n referrerPolicy=\"no-referrer\"\n sandbox=\"allow-same-origin allow-scripts allow-forms\"\n onLoad={handleLoad}\n onError={handleError}\n data-testid=\"plan-iframe\"\n />\n )}\n {activeSession && connection === 'connecting' && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-bg-sunken/80 pointer-events-none\">\n <Loader2 className=\"size-6 animate-spin text-action-primary-bg\" />\n </div>\n )}\n {activeSession && connection === 'error' && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-bg-sunken p-6 text-center\">\n <div>\n <AlertCircle className=\"size-6 text-status-error-text mx-auto\" />\n <p className=\"text-sm font-medium text-text-primary mt-2\">\n {tr('plan.iframeErrorTitle', 'Could not load plannotator.')}\n </p>\n <p className=\"text-xs text-text-secondary mt-1\">\n {tr(\n 'plan.iframeErrorDesc',\n 'The agent reverse proxy returned an error. Check the agent status and try again.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={handleRetry}\n className=\"mt-3 inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1.5 text-sm text-action-primary-text hover:bg-action-primary-bgHover\"\n >\n <RefreshCcw className=\"size-3.5\" /> {tr('plan.retry', 'Retry')}\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AIPlanPanel.js","names":[],"sources":["../../../src/components/ai/AIPlanPanel.tsx"],"sourcesContent":["/**\n * AIPlanPanel — the body of /ai-tools/plan.\n *\n * Five UI states gated in order:\n * 1. installing-plugin → vibe-plugin-plan + vibe-plugin-plan-plannotator\n * not installed on the agent. Renders the PluginGate install card.\n * 2. installing-cli → plugins installed but plannotator binary\n * missing on disk. Renders an Install CLI CTA.\n * 3. wizard → CLI installed but no AI-agent hook configured\n * yet. Renders the Claude/OpenCode/Codex/Pi/Gemini wizard.\n * 4. ready → all prereqs satisfied. Renders the tunnel\n * iframe pointing at ${tunnelUrl}/plan/, with postMessage auth\n * handoff on iframe load (mirrors PluginUIPanel.tsx).\n * 5. error → unrecoverable load failure for the status\n * query. Renders an error banner with retry.\n *\n * Auth handoff (matches PluginUIPanel.tsx:194–210):\n * onLoad → iframe.contentWindow.postMessage({type:'vibe-auth', apiKey},\n * new URL(tunnelUrl).origin)\n *\n * Surface contracts:\n * GET /api/plan-plannotator/status → PlannotatorStatus\n * POST /api/plan-plannotator/install → install binary\n * GET /api/plan-plannotator/agents/supported → AgentDetectionResult[]\n * POST /api/plan-plannotator/agents/:agent/configure-hook\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n AlertCircle,\n CheckCircle2,\n Inbox,\n Loader2,\n PlayCircle,\n RefreshCcw,\n Settings,\n Wifi,\n WifiOff,\n} from 'lucide-react';\n\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PluginGate } from '@/components/shared';\nimport { PLAN_META_PACKAGE, PLAN_PLANNOTATOR_PACKAGE } from '@/constants/pluginCatalog';\nimport {\n configureAgentHook,\n getPlannotatorStatus,\n installPlannotatorBinary,\n listSupportedAgents,\n type AgentDetectionResult,\n type PlannotatorStatus,\n} from '@/services/planApi';\nimport type { AgentRef } from '@/services/aiApi';\nimport { useMintAgentCapabilityMutation } from '@/generated/wspace-operations';\nimport { AgentCapabilityScope } from '@/generated/wspace-types';\n\ninterface AIPlanPanelProps {\n agent: AgentRef;\n tunnelUrl: string | null;\n}\n\ntype ConnectionStatus = 'connecting' | 'connected' | 'error';\n\nexport function AIPlanPanel({ agent, tunnelUrl }: AIPlanPanelProps) {\n const { t } = useI18n();\n // PluginGate renders eyebrow/title/description verbatim (no i18n lookup of\n // its own), so resolve them here with English fallbacks until plan.* keys exist.\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n return (\n <PluginGate\n agentId={agent.id}\n required={[PLAN_META_PACKAGE, PLAN_PLANNOTATOR_PACKAGE]}\n mode=\"all\"\n eyebrow={tr('plan.pluginRequiredEyebrow', 'Plan plugin required')}\n title={tr('plan.installPlanProviderTitle', 'Install the plan provider')}\n description={tr(\n 'plan.installPlanProviderDesc',\n 'Plan mode routes plan sessions through a plan provider plugin on your agent. Install the orchestrator and a provider (Plannotator) below to continue.'\n )}\n >\n <PlanPanelInner agent={agent} tunnelUrl={tunnelUrl} />\n </PluginGate>\n );\n}\n\nfunction PlanPanelInner({ agent, tunnelUrl }: AIPlanPanelProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const [status, setStatus] = useState<PlannotatorStatus | null>(null);\n const [statusError, setStatusError] = useState<string | null>(null);\n const [statusLoading, setStatusLoading] = useState(true);\n const [agents, setAgents] = useState<AgentDetectionResult[]>([]);\n const [installing, setInstalling] = useState(false);\n const [configuring, setConfiguring] = useState<string | null>(null);\n const [actionMessage, setActionMessage] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n // \"Manage hooks\" forces the wizard back open even when a hook is already\n // configured — the only way to re-write a stale/broken hook (wrong path or\n // a key from a prior agent identity), which otherwise locks the panel into\n // the ready view forever.\n const [forceWizard, setForceWizard] = useState(false);\n\n const refresh = useCallback(\n // `silent` background refreshes (polling) only update session status: they\n // skip the loading/error toggles AND the agent re-detection, so a transient\n // listSupportedAgents failure can't demote the ready view to the wizard.\n async (silent = false) => {\n if (!silent) {\n setStatusLoading(true);\n setStatusError(null);\n }\n try {\n if (silent) {\n setStatus(await getPlannotatorStatus(agent));\n return;\n }\n const [statusResult, agentsResult] = await Promise.all([\n getPlannotatorStatus(agent),\n listSupportedAgents(agent).catch(() => ({ agents: [] })),\n ]);\n setStatus(statusResult);\n setAgents(agentsResult.agents ?? []);\n } catch (err) {\n if (!silent) {\n setStatusError(err instanceof Error ? err.message : 'Failed to load plan status');\n }\n } finally {\n if (!silent) setStatusLoading(false);\n }\n },\n [agent]\n );\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n const handleInstallCli = useCallback(async () => {\n setInstalling(true);\n setActionError(null);\n setActionMessage(null);\n try {\n const result = await installPlannotatorBinary(agent);\n if (result.ok) {\n setActionMessage(tr('plan.cliInstalled', 'Plannotator CLI installed.'));\n await refresh();\n } else {\n setActionError(result.error ?? 'Install failed');\n }\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Install failed');\n } finally {\n setInstalling(false);\n }\n }, [agent, refresh, tr]);\n\n const handleConfigureHook = useCallback(\n async (agentKind: AgentDetectionResult['agent']) => {\n setConfiguring(agentKind);\n setActionError(null);\n setActionMessage(null);\n try {\n const result = await configureAgentHook(agent, agentKind);\n if (result.ok) {\n setActionMessage(tr('plan.hookConfigured', `Hook configured for ${agentKind}.`));\n await refresh();\n } else {\n setActionError(result.error ?? 'Hook configure failed');\n }\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Hook configure failed');\n } finally {\n setConfiguring(null);\n }\n },\n [agent, refresh, tr]\n );\n\n // Derived render state — guarded ordering means we always render the\n // most prerequisite-bound CTA first.\n const view = useMemo<'loading' | 'status-error' | 'install-cli' | 'wizard' | 'ready'>(() => {\n if (statusLoading) return 'loading';\n if (statusError) return 'status-error';\n if (!status?.installed) return 'install-cli';\n const anyConfigured = agents.some((a) => a.hookConfigured);\n if (!anyConfigured || forceWizard) return 'wizard';\n return 'ready';\n }, [status, statusError, statusLoading, agents, forceWizard]);\n\n // While the ready panel is shown, poll status in the background so a plan\n // session created by a coding agent's hook (or one that ends) is reflected\n // without a manual reload — `runningSessions` gates the iframe vs the empty\n // state below.\n useEffect(() => {\n if (view !== 'ready') return;\n // Skip ticks while the tab is hidden — no point polling a panel nobody sees.\n const id = setInterval(() => {\n if (!document.hidden) void refresh(true);\n }, 5000);\n return () => clearInterval(id);\n }, [view, refresh]);\n\n if (view === 'loading') {\n return (\n <div className=\"flex items-center gap-2 p-6 text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin\" />\n <span>{tr('plan.loadingStatus', 'Loading plan status…')}</span>\n </div>\n );\n }\n\n if (view === 'status-error') {\n return (\n <div className=\"rounded-md border border-status-error-border bg-status-error-bg-subtle p-4 text-sm text-status-error-text\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle className=\"size-4 mt-0.5 shrink-0\" />\n <div className=\"flex-1\">\n <p className=\"font-medium\">\n {tr('plan.statusError', 'Could not load plan status from the agent.')}\n </p>\n <p className=\"text-xs opacity-80 mt-1\">{statusError}</p>\n <button\n type=\"button\"\n onClick={() => void refresh()}\n className=\"mt-3 inline-flex items-center gap-1 text-xs font-medium hover:underline\"\n >\n <RefreshCcw className=\"size-3\" /> {tr('plan.retry', 'Retry')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n {actionMessage && (\n <div\n role=\"status\"\n className=\"rounded-md border border-status-success-border bg-status-success-bg-subtle px-3 py-2 text-xs text-status-success-text\"\n >\n {actionMessage}\n </div>\n )}\n {actionError && (\n <div\n role=\"alert\"\n className=\"rounded-md border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text\"\n >\n {actionError}\n </div>\n )}\n\n {view === 'install-cli' && (\n <InstallCliCard installing={installing} onInstall={handleInstallCli} tr={tr} />\n )}\n\n {view === 'wizard' && (\n <WizardCard\n agents={agents}\n configuring={configuring}\n onConfigure={handleConfigureHook}\n // Offer \"Done\" only when a ready view exists to return to — i.e.\n // at least one hook is configured (we got here via Manage hooks),\n // not during the genuine first-run wizard.\n canExit={forceWizard && agents.some((a) => a.hookConfigured)}\n onExit={() => setForceWizard(false)}\n tr={tr}\n />\n )}\n\n {view === 'ready' && (\n <PlanReadyPanel\n agent={agent}\n agents={agents}\n tunnelUrl={tunnelUrl}\n runningSessions={status?.runningSessions ?? []}\n onReconfigure={() => {\n setActionMessage(null);\n setForceWizard(true);\n }}\n tr={tr}\n />\n )}\n </div>\n );\n}\n\ninterface InstallCliCardProps {\n installing: boolean;\n onInstall: () => void;\n tr: (key: string, fallback: string) => string;\n}\n\nfunction InstallCliCard({ installing, onInstall, tr }: InstallCliCardProps) {\n return (\n <div className=\"rounded-lg border border-border-default bg-bg-elevated p-6\">\n <div className=\"flex items-start gap-3\">\n <PlayCircle className=\"size-6 text-action-primary-bg shrink-0\" />\n <div className=\"flex-1\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {tr('plan.installCliTitle', 'Install the plannotator CLI')}\n </h3>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'plan.installCliDesc',\n 'Plan mode runs a local plannotator binary on the agent and proxies its UI back here. The plugin installs the binary into ~/.local/bin/plannotator (sha256-pinned). Roughly 30 seconds.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={onInstall}\n disabled={installing}\n className=\"mt-4 inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bgHover disabled:opacity-50\"\n >\n {installing ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('plan.installing', 'Installing…')}\n </>\n ) : (\n tr('plan.installCliCta', 'Install plannotator CLI')\n )}\n </button>\n </div>\n </div>\n </div>\n );\n}\n\ninterface WizardCardProps {\n agents: AgentDetectionResult[];\n configuring: string | null;\n onConfigure: (agentKind: AgentDetectionResult['agent']) => void;\n canExit?: boolean;\n onExit?: () => void;\n tr: (key: string, fallback: string) => string;\n}\n\nfunction WizardCard({ agents, configuring, onConfigure, canExit, onExit, tr }: WizardCardProps) {\n const installed = agents.filter((a) => a.cliInstalled);\n return (\n <div className=\"rounded-lg border border-border-default bg-bg-elevated p-6\">\n <div className=\"flex items-start gap-3\">\n <Settings className=\"size-6 text-action-primary-bg shrink-0\" />\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between gap-3\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {tr('plan.wizardTitle', 'Connect an AI coding agent')}\n </h3>\n {canExit && onExit && (\n <button\n type=\"button\"\n onClick={onExit}\n className=\"shrink-0 inline-flex items-center gap-1 text-xs text-text-secondary hover:text-text-primary\"\n >\n {tr('plan.wizardDone', 'Done')}\n </button>\n )}\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'plan.wizardDesc',\n 'Configure a plannotator hook for at least one installed AI coding agent. The hook forwards plan content from the agent to plannotator on every plan-finalisation step.'\n )}\n </p>\n {installed.length === 0 && (\n <p className=\"mt-4 text-sm text-status-warning-text\">\n {tr(\n 'plan.wizardNoAgents',\n 'No AI coding agents detected in this sandbox. Install one (Claude Code, OpenCode, Codex, Pi, or Gemini) and refresh.'\n )}\n </p>\n )}\n <ul className=\"mt-4 space-y-2\">\n {agents.map((entry) => (\n <li\n key={entry.agent}\n className=\"flex items-start gap-3 p-3 border border-border-default rounded-md bg-bg-surface\"\n >\n {entry.hookConfigured ? (\n <CheckCircle2 className=\"size-5 text-status-success-text shrink-0 mt-0.5\" />\n ) : (\n <div className=\"size-5 rounded-full border-2 border-border-default shrink-0 mt-0.5\" />\n )}\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">{entry.name}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">{entry.instructions}</p>\n {!entry.cliInstalled && (\n <p className=\"text-xs text-status-warning-text mt-1\">\n {tr('plan.agentCliMissing', 'CLI not detected on this sandbox.')}\n </p>\n )}\n </div>\n <button\n type=\"button\"\n // Re-enabled when already configured: re-writing is the only\n // way to repair a stale hook (wrong path / old agent key).\n disabled={!entry.cliInstalled || configuring === entry.agent}\n onClick={() => onConfigure(entry.agent)}\n className=\"px-3 py-1.5 text-xs rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover disabled:opacity-50\"\n >\n {configuring === entry.agent\n ? tr('plan.configuring', 'Configuring…')\n : entry.hookConfigured\n ? tr('plan.reconfigureHook', 'Reconfigure')\n : tr('plan.configureHook', 'Configure hook')}\n </button>\n </li>\n ))}\n </ul>\n </div>\n </div>\n </div>\n );\n}\n\ninterface PlanReadyPanelProps {\n agent: AgentRef;\n agents: AgentDetectionResult[];\n tunnelUrl: string | null;\n runningSessions: PlannotatorStatus['runningSessions'];\n onReconfigure: () => void;\n tr: (key: string, fallback: string) => string;\n}\n\nfunction PlanReadyPanel({\n agent,\n agents,\n tunnelUrl,\n runningSessions,\n onReconfigure,\n tr,\n}: PlanReadyPanelProps) {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const [connection, setConnection] = useState<ConnectionStatus>('connecting');\n\n // plannotator is session-scoped — it only serves a plan at /plan/<sessionId>/.\n // A session exists once a connected coding agent finalises a plan (its hook\n // POSTs to the agent). With none running there is nothing to embed (the bare\n // /plan/ root 404s), so we show an empty state instead. The status endpoint\n // doesn't guarantee ordering, so pick the most recently started session.\n const activeSession = useMemo(\n () =>\n runningSessions.length > 0\n ? // `|| 0` keeps the sort deterministic if a startedAt is malformed (Date.parse → NaN).\n [...runningSessions].sort(\n (a, b) => (Date.parse(b.startedAt) || 0) - (Date.parse(a.startedAt) || 0)\n )[0]\n : null,\n [runningSessions]\n );\n const activeSessionId = activeSession?.sessionId ?? null;\n\n // F4 Wave B — mint a short-lived IFRAME_PLUGIN capability instead of\n // embedding the deprecated long-lived `agentApiKey` in `?apiKey=`. The\n // agent's `/plan/<sessionId>` bootstrap exchanges the `#vt=` fragment\n // for a scoped HttpOnly cookie before proxying to plannotator.\n const [mintAgentCapability] = useMintAgentCapabilityMutation();\n const [iframeToken, setIframeToken] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n if (!agent.id || !activeSessionId) {\n setIframeToken(null);\n return undefined;\n }\n mintAgentCapability({\n variables: {\n agentId: agent.id,\n profile: agent.profile ?? 'default',\n // PLAN_SESSION binds the ticket to `/plan/<sessionId>` — the path the\n // iframe actually loads. IFRAME_PLUGIN would scope it to `/ui/<id>`,\n // which never matches the plan proxy URL (the bug that left the iframe\n // 401'd). The agent's `/plan/*` shim verifies this `/plan/<sid>` ticket.\n scope: AgentCapabilityScope.PlanSession,\n resourceId: activeSessionId,\n ttlSeconds: 60,\n },\n })\n .then((res) => {\n if (cancelled) return;\n setIframeToken(res.data?.mintAgentCapability?.token ?? null);\n })\n .catch(() => {\n if (!cancelled) setIframeToken(null);\n });\n return () => {\n cancelled = true;\n };\n }, [agent.id, agent.profile, activeSessionId, mintAgentCapability]);\n\n const iframeSrc = useMemo(() => {\n if (!tunnelUrl || !iframeToken || !activeSessionId) return null;\n const base = tunnelUrl.replace(/\\/$/, '');\n return `${base}/plan/${activeSessionId}/#vt=${encodeURIComponent(iframeToken)}`;\n }, [tunnelUrl, iframeToken, activeSessionId]);\n\n // Reset the connection indicator when the embedded session changes so a\n // prior session's 'error'/'connected' state doesn't bleed into the next one.\n useEffect(() => {\n setConnection('connecting');\n }, [activeSessionId]);\n\n const sendAuth = useCallback(() => {\n // No-op post-F4: the iframe authenticates via the `#vt=` fragment +\n // agent-issued cookie. Kept as a stable reference for handleLoad.\n }, []);\n\n const handleLoad = useCallback(() => {\n setConnection('connected');\n sendAuth();\n }, [sendAuth]);\n\n const handleError = useCallback(() => {\n setConnection('error');\n }, []);\n\n const handleRetry = useCallback(() => {\n const iframe = iframeRef.current;\n if (!iframe) return;\n setConnection('connecting');\n const src = iframe.src;\n iframe.src = 'about:blank';\n requestAnimationFrame(() => {\n iframe.src = src;\n });\n }, []);\n\n // Used elsewhere for analytics / breadcrumbs.\n void agent;\n\n if (!tunnelUrl) {\n return (\n <div className=\"rounded-lg border border-status-warning-border bg-status-warning-bg-subtle p-4 text-sm text-status-warning-text\">\n {tr(\n 'plan.tunnelMissing',\n 'Agent tunnel URL is missing. Bring the agent online and refresh.'\n )}\n </div>\n );\n }\n\n const configuredAgents = agents.filter((a) => a.hookConfigured);\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n {activeSession && (\n <>\n <span className=\"inline-flex items-center gap-1\">\n {connection === 'connected' && <Wifi className=\"size-3.5 text-status-success-text\" />}\n {connection === 'connecting' && (\n <Loader2 className=\"size-3.5 animate-spin text-status-warning-text\" />\n )}\n {connection === 'error' && <WifiOff className=\"size-3.5 text-status-error-text\" />}\n {tr(`plan.connection.${connection}`, connection)}\n </span>\n <span aria-hidden=\"true\">·</span>\n </>\n )}\n <span>{tr('plan.hookCount', `${configuredAgents.length} hook(s) configured`)}</span>\n <button\n type=\"button\"\n onClick={onReconfigure}\n className=\"ml-auto inline-flex items-center gap-1 hover:text-text-primary\"\n >\n <Settings className=\"size-3.5\" />\n {tr('plan.reopenWizard', 'Manage hooks')}\n </button>\n </div>\n\n <div className=\"border border-border-default rounded-lg overflow-hidden bg-bg-sunken relative h-[calc(100vh-320px)] min-h-[500px]\">\n {!activeSession && (\n <div className=\"absolute inset-0 flex flex-col items-center justify-center gap-2 p-6 text-center\">\n <Inbox className=\"size-8 text-text-muted\" />\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('plan.noSessionTitle', 'No active plan session')}\n </p>\n <p className=\"max-w-md text-xs text-text-secondary\">\n {tr(\n 'plan.noSessionDesc',\n 'Start a plan in a connected coding agent — when it finalises a plan, the configured hook forwards it here for review.'\n )}\n </p>\n </div>\n )}\n {iframeSrc && (\n /* The src carries ?apiKey= for the initial auth (an iframe can't set\n request headers). Drop popups/top-navigation and send no referrer\n so that key can't leak out of the frame via a navigation or popup. */\n <iframe\n ref={iframeRef}\n src={iframeSrc}\n title={tr('plan.iframeTitle', 'Plannotator plan workspace')}\n className=\"w-full h-full border-0\"\n allow=\"clipboard-read; clipboard-write\"\n referrerPolicy=\"no-referrer\"\n sandbox=\"allow-same-origin allow-scripts allow-forms\"\n onLoad={handleLoad}\n onError={handleError}\n data-testid=\"plan-iframe\"\n />\n )}\n {activeSession && connection === 'connecting' && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-bg-sunken/80 pointer-events-none\">\n <Loader2 className=\"size-6 animate-spin text-action-primary-bg\" />\n </div>\n )}\n {activeSession && connection === 'error' && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-bg-sunken p-6 text-center\">\n <div>\n <AlertCircle className=\"size-6 text-status-error-text mx-auto\" />\n <p className=\"text-sm font-medium text-text-primary mt-2\">\n {tr('plan.iframeErrorTitle', 'Could not load plannotator.')}\n </p>\n <p className=\"text-xs text-text-secondary mt-1\">\n {tr(\n 'plan.iframeErrorDesc',\n 'The agent reverse proxy returned an error. Check the agent status and try again.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={handleRetry}\n className=\"mt-3 inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1.5 text-sm text-action-primary-text hover:bg-action-primary-bgHover\"\n >\n <RefreshCcw className=\"size-3.5\" /> {tr('plan.retry', 'Retry')}\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -206,7 +206,7 @@ function y({ agent: y }) {
206
206
  className: "font-semibold text-text-primary",
207
207
  children: b("vibecontrols.ai.playground.response", "Response")
208
208
  }), F !== null && /* @__PURE__ */ _("span", {
209
- className: `px-2 py-0.5 text-xs rounded font-mono ${F >= 200 && F < 300 ? "bg-status-success-bg text-status-success-text" : F >= 400 ? "bg-status-error-bg text-status-error-text" : "bg-status-warning-bg text-status-warning-text"}`,
209
+ className: `px-2 py-0.5 text-xs rounded font-mono ${F >= 200 && F < 300 ? "bg-status-success-bg-subtle text-status-success-text" : F >= 400 ? "bg-status-error-bg-subtle text-status-error-text" : "bg-status-warning-bg-subtle text-status-warning-text"}`,
210
210
  children: F
211
211
  })]
212
212
  }), N === null ? /* @__PURE__ */ _("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"AIPlaygroundTab.js","names":[],"sources":["../../../src/components/ai/AIPlaygroundTab.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from 'react';\nimport { Play, Copy, Check, Terminal } from 'lucide-react';\nimport { Card, LoadingState, ErrorState, EmptyState } from '@/components/shared';\nimport type { AIApiEndpoint, AgentRef } from '@/services/aiApi';\nimport { fetchApiDescriptor, agentApiPath } from '@/services/aiApi';\nimport { getStoredAuthToken, getStoredWorkspaceToken } from '@burdenoff/fe-libs/shared/graphql';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AIPlaygroundTabProps {\n agent: AgentRef;\n}\n\nexport function AIPlaygroundTab({ agent }: AIPlaygroundTabProps) {\n const tr = useTr();\n const [endpoints, setEndpoints] = useState<AIApiEndpoint[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [selectedEndpoint, setSelectedEndpoint] = useState<AIApiEndpoint | null>(null);\n const [params, setParams] = useState<Record<string, string>>({});\n const [executing, setExecuting] = useState(false);\n const [response, setResponse] = useState<string | null>(null);\n const [responseStatus, setResponseStatus] = useState<number | null>(null);\n const [copied, setCopied] = useState(false);\n\n const loadEndpoints = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await fetchApiDescriptor(agent);\n setEndpoints(result);\n if (result.length > 0) {\n selectEndpoint(result[0]);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load API descriptor');\n } finally {\n setLoading(false);\n }\n }, [agent]);\n\n useEffect(() => {\n loadEndpoints();\n }, [loadEndpoints]);\n\n const selectEndpoint = (ep: AIApiEndpoint) => {\n setSelectedEndpoint(ep);\n const newParams: Record<string, string> = {};\n ep.parameters?.forEach((p) => {\n newParams[p.name] = '';\n });\n setParams(newParams);\n setResponse(null);\n setResponseStatus(null);\n };\n\n const handleExecute = async () => {\n if (!selectedEndpoint) return;\n setExecuting(true);\n setResponse(null);\n setResponseStatus(null);\n try {\n // Build path with path params replaced\n let path = selectedEndpoint.path;\n const bodyParams: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(params)) {\n if (path.includes(`:${key}`)) {\n path = path.replace(`:${key}`, encodeURIComponent(value));\n } else if (value) {\n bodyParams[key] = value;\n }\n }\n\n const method = selectedEndpoint.method.toUpperCase();\n const hasBody = method === 'POST' || method === 'PATCH' || method === 'PUT';\n\n const authToken = getStoredAuthToken();\n const workspaceToken = getStoredWorkspaceToken();\n const workspaceId = new URLSearchParams(window.location.search).get('workspace') || '';\n const graphRes = await fetch('/workspaces/graphql', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),\n ...(workspaceToken ? { 'X-Workspace-Authorization': `Bearer ${workspaceToken}` } : {}),\n ...(workspaceId ? { 'x-workspace-id': workspaceId } : {}),\n },\n body: JSON.stringify({\n query: `mutation AgentAiMutate($agentId: ID!, $path: String!, $method: String!, $body: JSON) {\n agentAiMutate(agentId: $agentId, path: $path, method: $method, body: $body) {\n ok\n status\n data\n text\n error\n }\n }`,\n variables: {\n agentId: agent.id,\n path: agentApiPath(agent.profile, path),\n method,\n body: hasBody && Object.keys(bodyParams).length > 0 ? bodyParams : null,\n },\n }),\n });\n\n const graphJson = await graphRes.json();\n const proxy = graphJson.data?.agentAiMutate;\n if (!proxy) throw new Error(graphJson.errors?.[0]?.message || 'Request failed');\n\n setResponseStatus(proxy.status);\n setResponse(\n JSON.stringify(\n proxy.data ?? (proxy.text ? { text: proxy.text } : { error: proxy.error }),\n null,\n 2\n )\n );\n } catch (err) {\n setResponse(`Error: ${err instanceof Error ? err.message : 'Request failed'}`);\n setResponseStatus(0);\n } finally {\n setExecuting(false);\n }\n };\n\n const getCurlCommand = (): string => {\n if (!selectedEndpoint) return '';\n let path = selectedEndpoint.path;\n const bodyParams: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(params)) {\n if (path.includes(`:${key}`)) {\n path = path.replace(`:${key}`, encodeURIComponent(value || `{${key}}`));\n } else if (value) {\n bodyParams[key] = value;\n }\n }\n\n const method = selectedEndpoint.method.toUpperCase();\n const canonicalPath = agentApiPath(agent.profile, path);\n return `mutation AgentAiMutate {\n agentAiMutate(\n agentId: \"${agent.id}\"\n path: \"${canonicalPath}\"\n method: \"${method}\"\n body: ${Object.keys(bodyParams).length > 0 ? JSON.stringify(bodyParams) : 'null'}\n ) {\n ok\n status\n data\n error\n }\n}`;\n };\n\n const handleCopy = () => {\n navigator.clipboard.writeText(getCurlCommand());\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n if (loading)\n return (\n <LoadingState\n message={tr('vibecontrols.ai.playground.loading', 'Loading API descriptor...')}\n />\n );\n if (error)\n return (\n <ErrorState\n title={tr('vibecontrols.ai.playground.loadFailed', 'Failed to load API')}\n message={error}\n onRetry={loadEndpoints}\n />\n );\n if (endpoints.length === 0) {\n return (\n <EmptyState\n icon={<Terminal className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.ai.playground.noEndpoints', 'No API endpoints')}\n description={tr(\n 'vibecontrols.ai.playground.noEndpointsDesc',\n 'The agent does not expose any AI API endpoints'\n )}\n />\n );\n }\n\n return (\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Left: Endpoint selection and params */}\n <div className=\"space-y-4\">\n <Card className=\"p-4\">\n <h3 className=\"font-semibold text-text-primary mb-3\">\n {tr('vibecontrols.ai.playground.endpoint', 'Endpoint')}\n </h3>\n <select\n value={selectedEndpoint?.id ?? ''}\n onChange={(e) => {\n const ep = endpoints.find((ep) => ep.id === e.target.value);\n if (ep) selectEndpoint(ep);\n }}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm\"\n >\n {endpoints.map((ep) => (\n <option key={ep.id} value={ep.id}>\n {ep.method.toUpperCase()} {ep.path} - {ep.description}\n </option>\n ))}\n </select>\n\n {selectedEndpoint?.description && (\n <p className=\"mt-2 text-xs text-text-secondary\">{selectedEndpoint.description}</p>\n )}\n </Card>\n\n {/* Parameters */}\n {selectedEndpoint?.parameters && selectedEndpoint.parameters.length > 0 && (\n <Card className=\"p-4\">\n <h3 className=\"font-semibold text-text-primary mb-3\">\n {tr('vibecontrols.ai.playground.parameters', 'Parameters')}\n </h3>\n <div className=\"space-y-3\">\n {selectedEndpoint.parameters.map((p) => (\n <div key={p.name}>\n <label className=\"flex items-center gap-1.5 text-sm font-medium text-text-primary mb-1\">\n {p.name}\n {p.required && <span className=\"text-status-error-text text-xs\">*</span>}\n <span className=\"text-xs text-text-muted font-normal\">({p.type})</span>\n </label>\n {p.description && (\n <p className=\"text-xs text-text-secondary mb-1\">{p.description}</p>\n )}\n <input\n aria-label=\"control\"\n type=\"text\"\n value={params[p.name] ?? ''}\n onChange={(e) => setParams((prev) => ({ ...prev, [p.name]: e.target.value }))}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm font-mono focus:outline-none focus:border-border-strong\"\n placeholder={p.required ? 'Required' : 'Optional'}\n />\n </div>\n ))}\n </div>\n </Card>\n )}\n\n <button\n type=\"button\"\n onClick={handleExecute}\n disabled={executing}\n className=\"w-full flex items-center justify-center gap-2 px-4 py-2.5 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50 text-sm font-medium\"\n >\n <Play className=\"size-4\" />\n {executing\n ? tr('vibecontrols.ai.playground.executing', 'Executing...')\n : tr('vibecontrols.ai.playground.execute', 'Execute')}\n </button>\n\n {/* Curl Command */}\n <Card className=\"p-4\">\n <div className=\"mb-2 flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wide\">\n cURL\n </h3>\n <button\n type=\"button\"\n onClick={handleCopy}\n className=\"flex items-center gap-1 px-2 py-1 text-xs text-text-secondary hover:bg-bg-sunken rounded transition-colors\"\n >\n {copied ? (\n <>\n <Check className=\"size-3 text-status-success-text\" /> Copied\n </>\n ) : (\n <>\n <Copy className=\"size-3\" /> Copy\n </>\n )}\n </button>\n </div>\n <pre className=\"text-xs text-text-primary bg-bg-sunken rounded p-3 overflow-x-auto whitespace-pre-wrap font-mono\">\n {getCurlCommand()}\n </pre>\n </Card>\n </div>\n\n {/* Right: Response */}\n <div>\n <Card className=\"p-4 h-full\">\n <div className=\"mb-3 flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibecontrols.ai.playground.response', 'Response')}\n </h3>\n {responseStatus !== null && (\n <span\n className={`px-2 py-0.5 text-xs rounded font-mono ${\n responseStatus >= 200 && responseStatus < 300\n ? 'bg-status-success-bg text-status-success-text'\n : responseStatus >= 400\n ? 'bg-status-error-bg text-status-error-text'\n : 'bg-status-warning-bg text-status-warning-text'\n }`}\n >\n {responseStatus}\n </span>\n )}\n </div>\n {response === null ? (\n <div className=\"flex items-center justify-center h-48 text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.ai.playground.executeToSee',\n 'Execute a request to see the response'\n )}\n </div>\n ) : (\n <pre className=\"text-xs text-text-primary bg-bg-sunken rounded p-3 overflow-auto max-h-[60vh] whitespace-pre-wrap font-mono\">\n {response}\n </pre>\n )}\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AIPlaygroundTab.js","names":[],"sources":["../../../src/components/ai/AIPlaygroundTab.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from 'react';\nimport { Play, Copy, Check, Terminal } from 'lucide-react';\nimport { Card, LoadingState, ErrorState, EmptyState } from '@/components/shared';\nimport type { AIApiEndpoint, AgentRef } from '@/services/aiApi';\nimport { fetchApiDescriptor, agentApiPath } from '@/services/aiApi';\nimport { getStoredAuthToken, getStoredWorkspaceToken } from '@burdenoff/fe-libs/shared/graphql';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface AIPlaygroundTabProps {\n agent: AgentRef;\n}\n\nexport function AIPlaygroundTab({ agent }: AIPlaygroundTabProps) {\n const tr = useTr();\n const [endpoints, setEndpoints] = useState<AIApiEndpoint[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [selectedEndpoint, setSelectedEndpoint] = useState<AIApiEndpoint | null>(null);\n const [params, setParams] = useState<Record<string, string>>({});\n const [executing, setExecuting] = useState(false);\n const [response, setResponse] = useState<string | null>(null);\n const [responseStatus, setResponseStatus] = useState<number | null>(null);\n const [copied, setCopied] = useState(false);\n\n const loadEndpoints = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await fetchApiDescriptor(agent);\n setEndpoints(result);\n if (result.length > 0) {\n selectEndpoint(result[0]);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load API descriptor');\n } finally {\n setLoading(false);\n }\n }, [agent]);\n\n useEffect(() => {\n loadEndpoints();\n }, [loadEndpoints]);\n\n const selectEndpoint = (ep: AIApiEndpoint) => {\n setSelectedEndpoint(ep);\n const newParams: Record<string, string> = {};\n ep.parameters?.forEach((p) => {\n newParams[p.name] = '';\n });\n setParams(newParams);\n setResponse(null);\n setResponseStatus(null);\n };\n\n const handleExecute = async () => {\n if (!selectedEndpoint) return;\n setExecuting(true);\n setResponse(null);\n setResponseStatus(null);\n try {\n // Build path with path params replaced\n let path = selectedEndpoint.path;\n const bodyParams: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(params)) {\n if (path.includes(`:${key}`)) {\n path = path.replace(`:${key}`, encodeURIComponent(value));\n } else if (value) {\n bodyParams[key] = value;\n }\n }\n\n const method = selectedEndpoint.method.toUpperCase();\n const hasBody = method === 'POST' || method === 'PATCH' || method === 'PUT';\n\n const authToken = getStoredAuthToken();\n const workspaceToken = getStoredWorkspaceToken();\n const workspaceId = new URLSearchParams(window.location.search).get('workspace') || '';\n const graphRes = await fetch('/workspaces/graphql', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),\n ...(workspaceToken ? { 'X-Workspace-Authorization': `Bearer ${workspaceToken}` } : {}),\n ...(workspaceId ? { 'x-workspace-id': workspaceId } : {}),\n },\n body: JSON.stringify({\n query: `mutation AgentAiMutate($agentId: ID!, $path: String!, $method: String!, $body: JSON) {\n agentAiMutate(agentId: $agentId, path: $path, method: $method, body: $body) {\n ok\n status\n data\n text\n error\n }\n }`,\n variables: {\n agentId: agent.id,\n path: agentApiPath(agent.profile, path),\n method,\n body: hasBody && Object.keys(bodyParams).length > 0 ? bodyParams : null,\n },\n }),\n });\n\n const graphJson = await graphRes.json();\n const proxy = graphJson.data?.agentAiMutate;\n if (!proxy) throw new Error(graphJson.errors?.[0]?.message || 'Request failed');\n\n setResponseStatus(proxy.status);\n setResponse(\n JSON.stringify(\n proxy.data ?? (proxy.text ? { text: proxy.text } : { error: proxy.error }),\n null,\n 2\n )\n );\n } catch (err) {\n setResponse(`Error: ${err instanceof Error ? err.message : 'Request failed'}`);\n setResponseStatus(0);\n } finally {\n setExecuting(false);\n }\n };\n\n const getCurlCommand = (): string => {\n if (!selectedEndpoint) return '';\n let path = selectedEndpoint.path;\n const bodyParams: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(params)) {\n if (path.includes(`:${key}`)) {\n path = path.replace(`:${key}`, encodeURIComponent(value || `{${key}}`));\n } else if (value) {\n bodyParams[key] = value;\n }\n }\n\n const method = selectedEndpoint.method.toUpperCase();\n const canonicalPath = agentApiPath(agent.profile, path);\n return `mutation AgentAiMutate {\n agentAiMutate(\n agentId: \"${agent.id}\"\n path: \"${canonicalPath}\"\n method: \"${method}\"\n body: ${Object.keys(bodyParams).length > 0 ? JSON.stringify(bodyParams) : 'null'}\n ) {\n ok\n status\n data\n error\n }\n}`;\n };\n\n const handleCopy = () => {\n navigator.clipboard.writeText(getCurlCommand());\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n if (loading)\n return (\n <LoadingState\n message={tr('vibecontrols.ai.playground.loading', 'Loading API descriptor...')}\n />\n );\n if (error)\n return (\n <ErrorState\n title={tr('vibecontrols.ai.playground.loadFailed', 'Failed to load API')}\n message={error}\n onRetry={loadEndpoints}\n />\n );\n if (endpoints.length === 0) {\n return (\n <EmptyState\n icon={<Terminal className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.ai.playground.noEndpoints', 'No API endpoints')}\n description={tr(\n 'vibecontrols.ai.playground.noEndpointsDesc',\n 'The agent does not expose any AI API endpoints'\n )}\n />\n );\n }\n\n return (\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Left: Endpoint selection and params */}\n <div className=\"space-y-4\">\n <Card className=\"p-4\">\n <h3 className=\"font-semibold text-text-primary mb-3\">\n {tr('vibecontrols.ai.playground.endpoint', 'Endpoint')}\n </h3>\n <select\n value={selectedEndpoint?.id ?? ''}\n onChange={(e) => {\n const ep = endpoints.find((ep) => ep.id === e.target.value);\n if (ep) selectEndpoint(ep);\n }}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm\"\n >\n {endpoints.map((ep) => (\n <option key={ep.id} value={ep.id}>\n {ep.method.toUpperCase()} {ep.path} - {ep.description}\n </option>\n ))}\n </select>\n\n {selectedEndpoint?.description && (\n <p className=\"mt-2 text-xs text-text-secondary\">{selectedEndpoint.description}</p>\n )}\n </Card>\n\n {/* Parameters */}\n {selectedEndpoint?.parameters && selectedEndpoint.parameters.length > 0 && (\n <Card className=\"p-4\">\n <h3 className=\"font-semibold text-text-primary mb-3\">\n {tr('vibecontrols.ai.playground.parameters', 'Parameters')}\n </h3>\n <div className=\"space-y-3\">\n {selectedEndpoint.parameters.map((p) => (\n <div key={p.name}>\n <label className=\"flex items-center gap-1.5 text-sm font-medium text-text-primary mb-1\">\n {p.name}\n {p.required && <span className=\"text-status-error-text text-xs\">*</span>}\n <span className=\"text-xs text-text-muted font-normal\">({p.type})</span>\n </label>\n {p.description && (\n <p className=\"text-xs text-text-secondary mb-1\">{p.description}</p>\n )}\n <input\n aria-label=\"control\"\n type=\"text\"\n value={params[p.name] ?? ''}\n onChange={(e) => setParams((prev) => ({ ...prev, [p.name]: e.target.value }))}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary text-sm font-mono focus:outline-none focus:border-border-strong\"\n placeholder={p.required ? 'Required' : 'Optional'}\n />\n </div>\n ))}\n </div>\n </Card>\n )}\n\n <button\n type=\"button\"\n onClick={handleExecute}\n disabled={executing}\n className=\"w-full flex items-center justify-center gap-2 px-4 py-2.5 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50 text-sm font-medium\"\n >\n <Play className=\"size-4\" />\n {executing\n ? tr('vibecontrols.ai.playground.executing', 'Executing...')\n : tr('vibecontrols.ai.playground.execute', 'Execute')}\n </button>\n\n {/* Curl Command */}\n <Card className=\"p-4\">\n <div className=\"mb-2 flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"text-xs font-semibold text-text-secondary uppercase tracking-wide\">\n cURL\n </h3>\n <button\n type=\"button\"\n onClick={handleCopy}\n className=\"flex items-center gap-1 px-2 py-1 text-xs text-text-secondary hover:bg-bg-sunken rounded transition-colors\"\n >\n {copied ? (\n <>\n <Check className=\"size-3 text-status-success-text\" /> Copied\n </>\n ) : (\n <>\n <Copy className=\"size-3\" /> Copy\n </>\n )}\n </button>\n </div>\n <pre className=\"text-xs text-text-primary bg-bg-sunken rounded p-3 overflow-x-auto whitespace-pre-wrap font-mono\">\n {getCurlCommand()}\n </pre>\n </Card>\n </div>\n\n {/* Right: Response */}\n <div>\n <Card className=\"p-4 h-full\">\n <div className=\"mb-3 flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibecontrols.ai.playground.response', 'Response')}\n </h3>\n {responseStatus !== null && (\n <span\n className={`px-2 py-0.5 text-xs rounded font-mono ${\n responseStatus >= 200 && responseStatus < 300\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : responseStatus >= 400\n ? 'bg-status-error-bg-subtle text-status-error-text'\n : 'bg-status-warning-bg-subtle text-status-warning-text'\n }`}\n >\n {responseStatus}\n </span>\n )}\n </div>\n {response === null ? (\n <div className=\"flex items-center justify-center h-48 text-sm text-text-secondary\">\n {tr(\n 'vibecontrols.ai.playground.executeToSee',\n 'Execute a request to see the response'\n )}\n </div>\n ) : (\n <pre className=\"text-xs text-text-primary bg-bg-sunken rounded p-3 overflow-auto max-h-[60vh] whitespace-pre-wrap font-mono\">\n {response}\n </pre>\n )}\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -59,7 +59,7 @@ function y({ agent: y }) {
59
59
  className: "space-y-6",
60
60
  children: [
61
61
  k && /* @__PURE__ */ _("div", {
62
- className: "bg-status-success-bg text-status-success-text px-4 py-2 rounded-md text-sm",
62
+ className: "bg-status-success-bg-subtle text-status-success-text px-4 py-2 rounded-md text-sm",
63
63
  children: k
64
64
  }),
65
65
  /* @__PURE__ */ v("div", {
@@ -77,7 +77,7 @@ function y({ agent: y }) {
77
77
  }), /* @__PURE__ */ v("button", {
78
78
  type: "button",
79
79
  onClick: () => M(!j),
80
- className: "flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-action-primary-bg bg-action-primary-bg/10 rounded-md hover:bg-action-primary-bg/20 transition-colors",
80
+ className: "flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-action-primary-bg bg-[var(--color-accent-soft)] rounded-md hover:bg-[var(--color-accent-soft)] transition-colors",
81
81
  children: [/* @__PURE__ */ _(g, { className: "size-3.5" }), b("vibecontrols.ai.prompts.generateFromTemplate", "Generate from Template")]
82
82
  })]
83
83
  }),