@polderlabs/bizar 6.2.5 → 7.0.0

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 (540) hide show
  1. package/.claude/CLAUDE.md +15 -0
  2. package/.claude/agents/_shared/AGENT_BASELINE.md +168 -0
  3. package/.claude/agents/_shared/CLAUDE_TOOLS.md +412 -0
  4. package/.claude/agents/_shared/SKILLS.md +109 -0
  5. package/.claude/agents/agent-browser.md +80 -0
  6. package/.claude/agents/baldr.md +49 -0
  7. package/.claude/agents/forseti.md +51 -0
  8. package/.claude/agents/frigg.md +42 -0
  9. package/.claude/agents/heimdall.md +33 -0
  10. package/.claude/agents/hermod.md +53 -0
  11. package/.claude/agents/mimir.md +49 -0
  12. package/.claude/agents/odin.md +287 -0
  13. package/.claude/agents/quick.md +34 -0
  14. package/.claude/agents/semble-search.md +50 -0
  15. package/.claude/agents/thor.md +53 -0
  16. package/.claude/agents/tyr.md +56 -0
  17. package/.claude/agents/vidarr.md +54 -0
  18. package/.claude/agents/vor.md +53 -0
  19. package/.claude/commands/audit.md +25 -0
  20. package/.claude/commands/bizar.md +22 -0
  21. package/.claude/commands/explain.md +17 -0
  22. package/.claude/commands/init.md +29 -0
  23. package/.claude/commands/learn.md +44 -0
  24. package/.claude/commands/plan.md +35 -0
  25. package/.claude/commands/plow-through.md +50 -0
  26. package/.claude/commands/pr-review.md +49 -0
  27. package/.claude/commands/setup-provider.md +96 -0
  28. package/.claude/commands/tailscale-serve.md +100 -0
  29. package/.claude/commands/team.md +132 -0
  30. package/.claude/commands/test.md +62 -0
  31. package/.claude/commands/validate.md +68 -0
  32. package/.claude/commands/visual-plan.md +24 -0
  33. package/.claude/hooks/README.md +92 -0
  34. package/.claude/hooks/posttooluse-editwrite.mjs +91 -0
  35. package/.claude/hooks/pretooluse-bash.mjs +81 -0
  36. package/.claude/hooks/pretooluse-editwrite.mjs +139 -0
  37. package/.claude/hooks/sessionend-recall.mjs +74 -0
  38. package/.claude/hooks/sessionstart-prime.mjs +80 -0
  39. package/.claude/hooks/userpromptsubmit-tag.mjs +80 -0
  40. package/.claude/hooks/worker-suggest.mjs +110 -0
  41. package/.claude/settings.json +125 -0
  42. package/.claude/skills/9router/SKILL.md +80 -0
  43. package/.claude/skills/9router-chat/SKILL.md +73 -0
  44. package/.claude/skills/9router-embeddings/SKILL.md +69 -0
  45. package/.claude/skills/9router-image/SKILL.md +86 -0
  46. package/.claude/skills/9router-stt/SKILL.md +79 -0
  47. package/.claude/skills/9router-tts/SKILL.md +80 -0
  48. package/.claude/skills/9router-web-fetch/SKILL.md +99 -0
  49. package/.claude/skills/9router-web-search/SKILL.md +91 -0
  50. package/.claude/skills/bizar/README.md +9 -0
  51. package/.claude/skills/bizar/SKILL.md +450 -0
  52. package/.claude/skills/cpp-coding-standards/README.md +28 -0
  53. package/.claude/skills/cpp-coding-standards/SKILL.md +634 -0
  54. package/.claude/skills/cpp-coding-standards/references/concurrency.md +320 -0
  55. package/.claude/skills/cpp-coding-standards/references/error-handling.md +229 -0
  56. package/.claude/skills/cpp-coding-standards/references/memory-safety.md +216 -0
  57. package/.claude/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
  58. package/.claude/skills/cpp-coding-standards/references/review-checklist.md +96 -0
  59. package/.claude/skills/cpp-testing/README.md +28 -0
  60. package/.claude/skills/cpp-testing/SKILL.md +304 -0
  61. package/.claude/skills/cpp-testing/references/coverage.md +370 -0
  62. package/.claude/skills/cpp-testing/references/framework-compare.md +175 -0
  63. package/.claude/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
  64. package/.claude/skills/cpp-testing/references/mocking.md +364 -0
  65. package/.claude/skills/cpp-testing/references/tdd-workflow.md +308 -0
  66. package/.claude/skills/cubesandbox/SKILL.md +148 -0
  67. package/.claude/skills/embedded-esp-idf/README.md +41 -0
  68. package/.claude/skills/embedded-esp-idf/SKILL.md +439 -0
  69. package/.claude/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
  70. package/.claude/skills/embedded-esp-idf/references/host-tests.md +164 -0
  71. package/.claude/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
  72. package/.claude/skills/embedded-esp-idf/references/kconfig.md +159 -0
  73. package/.claude/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
  74. package/.claude/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
  75. package/.claude/skills/embedded-esp-idf/references/nvs.md +121 -0
  76. package/.claude/skills/embedded-esp-idf/references/packed-structs.md +192 -0
  77. package/.claude/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
  78. package/.claude/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
  79. package/.claude/skills/glyph/SKILL.md +163 -0
  80. package/.claude/skills/harness-engineering/SKILL.md +143 -0
  81. package/.claude/skills/lightrag/SKILL.md +81 -0
  82. package/.claude/skills/memory-protocol/SKILL.md +105 -0
  83. package/.claude/skills/obsidian/SKILL.md +306 -0
  84. package/.claude/skills/read-the-damn-docs/SKILL.md +113 -0
  85. package/.claude/skills/self-improvement/SKILL.md +64 -0
  86. package/README.md +87 -59
  87. package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js → EnvVarsSection-CW6fWKds.js} +3 -3
  88. package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js.map → EnvVarsSection-CW6fWKds.js.map} +1 -1
  89. package/bizar-dash/dist/assets/MobileChat-D7zRhU9K.js +1 -0
  90. package/bizar-dash/dist/assets/{MobileChat-BnKN_Ks_.js.map → MobileChat-D7zRhU9K.js.map} +1 -1
  91. package/bizar-dash/dist/assets/MobileSettings-K-sNsn_r.js +1 -0
  92. package/bizar-dash/dist/assets/{MobileSettings-DjCPxC-Q.js.map → MobileSettings-K-sNsn_r.js.map} +1 -1
  93. package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js → Toast-WTxc3m2g.js} +1 -1
  94. package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js.map → Toast-WTxc3m2g.js.map} +1 -1
  95. package/bizar-dash/dist/assets/flow-BpU0-gK2.js +1 -0
  96. package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +1 -0
  97. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js +1 -0
  98. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js.map +1 -0
  99. package/bizar-dash/dist/assets/{icons-Do5N2jmX.js → icons-B0AKnYI3.js} +98 -103
  100. package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +1 -0
  101. package/bizar-dash/dist/assets/main-C-Md3DaY.css +1 -0
  102. package/bizar-dash/dist/assets/main-CO8fIxQK.js +18 -0
  103. package/bizar-dash/dist/assets/main-CO8fIxQK.js.map +1 -0
  104. package/bizar-dash/dist/assets/markdown-w2bDDkAH.js +1 -0
  105. package/bizar-dash/dist/assets/{markdown-C6mXtQxD.js.map → markdown-w2bDDkAH.js.map} +1 -1
  106. package/bizar-dash/dist/assets/mobile-DXYCA0hC.js +1 -0
  107. package/bizar-dash/dist/assets/{mobile-CMHqtLV2.js.map → mobile-DXYCA0hC.js.map} +1 -1
  108. package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js +2 -0
  109. package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js.map +1 -0
  110. package/bizar-dash/dist/assets/react-vendor-BnuhLJ6X.css +1 -0
  111. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js +46 -0
  112. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +1 -0
  113. package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js +2 -0
  114. package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js.map +1 -0
  115. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js +45 -0
  116. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +1 -0
  117. package/bizar-dash/dist/index.html +12 -10
  118. package/bizar-dash/dist/mobile.html +7 -4
  119. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  120. package/bizar-dash/src/server/api.mjs +15 -4
  121. package/bizar-dash/src/server/background-store.mjs +15 -16
  122. package/bizar-dash/src/server/bg-poller.mjs +25 -35
  123. package/bizar-dash/src/server/bg-retry.mjs +66 -194
  124. package/bizar-dash/src/server/claude-bg-spawner.mjs +390 -0
  125. package/bizar-dash/src/server/claude-info.mjs +412 -0
  126. package/bizar-dash/src/server/{cline-runner.mjs → claude-runner.mjs} +53 -48
  127. package/bizar-dash/src/server/claude-sdk.mjs +191 -0
  128. package/bizar-dash/src/server/memory-consolidator.mjs +218 -0
  129. package/bizar-dash/src/server/memory-distillation.mjs +296 -0
  130. package/bizar-dash/src/server/providers-store.mjs +22 -49
  131. package/bizar-dash/src/server/routes/background.mjs +24 -20
  132. package/bizar-dash/src/server/routes/chat.mjs +223 -326
  133. package/bizar-dash/src/server/routes/claude-session-detail.mjs +329 -0
  134. package/bizar-dash/src/server/routes/claude-sessions.mjs +259 -0
  135. package/bizar-dash/src/server/routes/distill.mjs +129 -0
  136. package/bizar-dash/src/server/routes/goal-planner.mjs +236 -0
  137. package/bizar-dash/src/server/routes/tasks.mjs +20 -28
  138. package/bizar-dash/src/server/task-delegator.mjs +76 -77
  139. package/bizar-dash/src/web/App.tsx +4 -0
  140. package/bizar-dash/src/web/components/Tag.tsx +18 -5
  141. package/bizar-dash/src/web/components/Topbar.tsx +3 -0
  142. package/bizar-dash/src/web/components/agents/CommunicationLog.tsx +126 -0
  143. package/bizar-dash/src/web/components/agents/DependencyGraph.tsx +163 -0
  144. package/bizar-dash/src/web/components/agents/QualityGates.tsx +123 -0
  145. package/bizar-dash/src/web/components/agents/RealTimeEventLog.tsx +127 -0
  146. package/bizar-dash/src/web/components/agents/RoutingDecisions.test.tsx +148 -0
  147. package/bizar-dash/src/web/components/agents/RoutingDecisions.tsx +160 -0
  148. package/bizar-dash/src/web/components/chat/useChat.ts +23 -27
  149. package/bizar-dash/src/web/components/goals/GoalInput.tsx +86 -0
  150. package/bizar-dash/src/web/components/goals/PlanVisualization.tsx +111 -0
  151. package/bizar-dash/src/web/lib/goapPlanner.ts +614 -0
  152. package/bizar-dash/src/web/lib/types.ts +25 -0
  153. package/bizar-dash/src/web/styles/agents-redesign.css +311 -0
  154. package/bizar-dash/src/web/styles/main.css +251 -0
  155. package/bizar-dash/src/web/styles/tasks-redesign.css +231 -0
  156. package/bizar-dash/src/web/ui/controls/Button.tsx +77 -0
  157. package/bizar-dash/src/web/ui/controls/Checkbox.tsx +95 -0
  158. package/bizar-dash/src/web/ui/controls/IconButton.tsx +66 -0
  159. package/bizar-dash/src/web/ui/controls/Kbd.tsx +24 -0
  160. package/bizar-dash/src/web/ui/controls/NumberInput.tsx +77 -0
  161. package/bizar-dash/src/web/ui/controls/RadioGroup.tsx +109 -0
  162. package/bizar-dash/src/web/ui/controls/SearchInput.tsx +91 -0
  163. package/bizar-dash/src/web/ui/controls/Select.tsx +92 -0
  164. package/bizar-dash/src/web/ui/controls/Slider.tsx +70 -0
  165. package/bizar-dash/src/web/ui/controls/TextInput.tsx +67 -0
  166. package/bizar-dash/src/web/ui/controls/Toggle.tsx +79 -0
  167. package/bizar-dash/src/web/ui/controls/controls.css +454 -0
  168. package/bizar-dash/src/web/ui/controls/index.ts +33 -0
  169. package/bizar-dash/src/web/ui/data/BarChart.tsx +60 -0
  170. package/bizar-dash/src/web/ui/data/DataTable.tsx +216 -0
  171. package/bizar-dash/src/web/ui/data/EmptyState.tsx +60 -0
  172. package/bizar-dash/src/web/ui/data/ErrorState.tsx +56 -0
  173. package/bizar-dash/src/web/ui/data/KeyValueList.tsx +106 -0
  174. package/bizar-dash/src/web/ui/data/LoadingState.tsx +65 -0
  175. package/bizar-dash/src/web/ui/data/Sparkline.tsx +87 -0
  176. package/bizar-dash/src/web/ui/data/StatTile.tsx +87 -0
  177. package/bizar-dash/src/web/ui/data/data.css +535 -0
  178. package/bizar-dash/src/web/ui/data/index.ts +31 -0
  179. package/bizar-dash/src/web/ui/feedback/Badge.tsx +49 -0
  180. package/bizar-dash/src/web/ui/feedback/Dialog.tsx +209 -0
  181. package/bizar-dash/src/web/ui/feedback/ProgressBar.tsx +69 -0
  182. package/bizar-dash/src/web/ui/feedback/StatusDot.tsx +50 -0
  183. package/bizar-dash/src/web/ui/feedback/Toast.tsx +200 -0
  184. package/bizar-dash/src/web/ui/feedback/Tooltip.tsx +126 -0
  185. package/bizar-dash/src/web/ui/feedback/feedback.css +314 -0
  186. package/bizar-dash/src/web/ui/feedback/index.ts +43 -0
  187. package/bizar-dash/src/web/ui/index.ts +56 -0
  188. package/bizar-dash/src/web/ui/layout/AppShell.tsx +51 -0
  189. package/bizar-dash/src/web/ui/layout/Breadcrumbs.tsx +97 -0
  190. package/bizar-dash/src/web/ui/layout/Panel.tsx +73 -0
  191. package/bizar-dash/src/web/ui/layout/PanelHeader.tsx +39 -0
  192. package/bizar-dash/src/web/ui/layout/Sidebar.tsx +131 -0
  193. package/bizar-dash/src/web/ui/layout/Tabs.tsx +133 -0
  194. package/bizar-dash/src/web/ui/layout/Topbar.tsx +42 -0
  195. package/bizar-dash/src/web/ui/layout/index.ts +33 -0
  196. package/bizar-dash/src/web/ui/layout/layout.css +413 -0
  197. package/bizar-dash/src/web/ui/navigation/CommandPalette.tsx +199 -0
  198. package/bizar-dash/src/web/ui/navigation/NavGroup.tsx +95 -0
  199. package/bizar-dash/src/web/ui/navigation/NavLink.tsx +93 -0
  200. package/bizar-dash/src/web/ui/navigation/index.ts +17 -0
  201. package/bizar-dash/src/web/ui/navigation/navigation.css +343 -0
  202. package/bizar-dash/src/web/ui/primitives/Box.tsx +91 -0
  203. package/bizar-dash/src/web/ui/primitives/Grid.tsx +49 -0
  204. package/bizar-dash/src/web/ui/primitives/Inline.tsx +55 -0
  205. package/bizar-dash/src/web/ui/primitives/Separator.tsx +46 -0
  206. package/bizar-dash/src/web/ui/primitives/Stack.tsx +54 -0
  207. package/bizar-dash/src/web/ui/primitives/VisuallyHidden.tsx +26 -0
  208. package/bizar-dash/src/web/ui/primitives/index.ts +23 -0
  209. package/bizar-dash/src/web/ui/primitives/primitives.css +183 -0
  210. package/bizar-dash/src/web/ui/styles/globals.css +86 -0
  211. package/bizar-dash/src/web/ui/styles/reset.css +125 -0
  212. package/bizar-dash/src/web/ui/styles/tokens.css +195 -0
  213. package/bizar-dash/src/web/ui/theme/ThemeProvider.tsx +142 -0
  214. package/bizar-dash/src/web/ui/theme/tokens.ts +131 -0
  215. package/bizar-dash/src/web/ui/theme/useTheme.ts +22 -0
  216. package/bizar-dash/src/web/ui/utils/cx.ts +42 -0
  217. package/bizar-dash/src/web/views/Agents.tsx +506 -266
  218. package/bizar-dash/src/web/views/Chat.tsx +8 -8
  219. package/bizar-dash/src/web/views/GoalPlanner.tsx +158 -0
  220. package/bizar-dash/src/web/views/Overview.tsx +996 -448
  221. package/bizar-dash/src/web/views/Tasks.tsx +277 -273
  222. package/bizar-dash/tests/communication-log.test.tsx +67 -0
  223. package/bizar-dash/tests/dep-graph.test.tsx +40 -0
  224. package/bizar-dash/tests/event-log.test.tsx +61 -0
  225. package/bizar-dash/tests/goal-input.test.tsx +72 -0
  226. package/bizar-dash/tests/goap-planner.test.ts +68 -0
  227. package/bizar-dash/tests/quality-gates.test.tsx +59 -0
  228. package/bizar-dash/tests/setup.ts +31 -1
  229. package/bizar-dash/tests/ui/controls/Button.test.tsx +50 -0
  230. package/bizar-dash/tests/ui/controls/Checkbox.test.tsx +44 -0
  231. package/bizar-dash/tests/ui/controls/IconButton.test.tsx +37 -0
  232. package/bizar-dash/tests/ui/controls/Kbd.test.tsx +39 -0
  233. package/bizar-dash/tests/ui/controls/NumberInput.test.tsx +56 -0
  234. package/bizar-dash/tests/ui/controls/RadioGroup.test.tsx +58 -0
  235. package/bizar-dash/tests/ui/controls/SearchInput.test.tsx +52 -0
  236. package/bizar-dash/tests/ui/controls/Select.test.tsx +66 -0
  237. package/bizar-dash/tests/ui/controls/Slider.test.tsx +41 -0
  238. package/bizar-dash/tests/ui/controls/TextInput.test.tsx +44 -0
  239. package/bizar-dash/tests/ui/controls/Toggle.test.tsx +39 -0
  240. package/bizar-dash/tests/ui/data/BarChart.test.tsx +55 -0
  241. package/bizar-dash/tests/ui/data/DataTable.test.tsx +112 -0
  242. package/bizar-dash/tests/ui/data/EmptyState.test.tsx +57 -0
  243. package/bizar-dash/tests/ui/data/ErrorState.test.tsx +47 -0
  244. package/bizar-dash/tests/ui/data/KeyValueList.test.tsx +59 -0
  245. package/bizar-dash/tests/ui/data/LoadingState.test.tsx +37 -0
  246. package/bizar-dash/tests/ui/data/Sparkline.test.tsx +56 -0
  247. package/bizar-dash/tests/ui/data/StatTile.test.tsx +69 -0
  248. package/bizar-dash/tests/ui/feedback/Badge.test.tsx +41 -0
  249. package/bizar-dash/tests/ui/feedback/Dialog.test.tsx +99 -0
  250. package/bizar-dash/tests/ui/feedback/ProgressBar.test.tsx +66 -0
  251. package/bizar-dash/tests/ui/feedback/StatusDot.test.tsx +41 -0
  252. package/bizar-dash/tests/ui/feedback/Toast.test.tsx +124 -0
  253. package/bizar-dash/tests/ui/feedback/Tooltip.test.tsx +102 -0
  254. package/bizar-dash/tests/ui/layout/AppShell.test.tsx +50 -0
  255. package/bizar-dash/tests/ui/layout/Breadcrumbs.test.tsx +85 -0
  256. package/bizar-dash/tests/ui/layout/Panel.test.tsx +52 -0
  257. package/bizar-dash/tests/ui/layout/PanelHeader.test.tsx +32 -0
  258. package/bizar-dash/tests/ui/layout/Sidebar.test.tsx +95 -0
  259. package/bizar-dash/tests/ui/layout/Tabs.test.tsx +87 -0
  260. package/bizar-dash/tests/ui/layout/Topbar.test.tsx +41 -0
  261. package/bizar-dash/tests/ui/navigation/CommandPalette.test.tsx +105 -0
  262. package/bizar-dash/tests/ui/navigation/NavGroup.test.tsx +55 -0
  263. package/bizar-dash/tests/ui/navigation/NavLink.test.tsx +61 -0
  264. package/bizar-dash/tests/ui/primitives/Box.test.tsx +49 -0
  265. package/bizar-dash/tests/ui/primitives/Grid.test.tsx +37 -0
  266. package/bizar-dash/tests/ui/primitives/Inline.test.tsx +33 -0
  267. package/bizar-dash/tests/ui/primitives/Separator.test.tsx +34 -0
  268. package/bizar-dash/tests/ui/primitives/Stack.test.tsx +45 -0
  269. package/bizar-dash/tests/ui/primitives/VisuallyHidden.test.tsx +27 -0
  270. package/bizar-dash/tests/views/Agents.test.tsx +359 -0
  271. package/bizar-dash/tests/views/Overview.test.tsx +413 -0
  272. package/bizar-dash/tests/views/Tasks.test.tsx +250 -0
  273. package/bizar-dash/vitest.config.ts +3 -1
  274. package/cli/__tests__/cost-gate.test.mjs +301 -0
  275. package/cli/__tests__/feature-list-bridge.test.mjs +371 -0
  276. package/cli/bin.mjs +55 -18
  277. package/cli/commands/claim.mjs +282 -0
  278. package/cli/commands/claude-cmd.mjs +348 -0
  279. package/cli/commands/cost.mjs +279 -0
  280. package/cli/commands/install.mjs +12 -11
  281. package/cli/commands/validate.mjs +252 -325
  282. package/cli/cost-gate.mjs +501 -0
  283. package/cli/dev-link.test.mjs +11 -11
  284. package/cli/doctor.mjs +131 -213
  285. package/cli/feature-list-bridge.mjs +400 -0
  286. package/cli/install.mjs +33 -37
  287. package/cli/install.test.mjs +9 -9
  288. package/cli/memory-constants.mjs +17 -0
  289. package/cli/provision-claude.mjs +893 -0
  290. package/cli/provision.mjs +224 -227
  291. package/cli/utils.mjs +72 -58
  292. package/cli/worker-dispatcher.mjs +259 -0
  293. package/cli/worker-dispatcher.test.mjs +204 -0
  294. package/config/AGENTS.md +28 -28
  295. package/config/trigger-patterns.json +186 -0
  296. package/install.sh +66 -44
  297. package/package.json +19 -11
  298. package/packages/sdk/ARCHITECTURE.md +40 -36
  299. package/packages/sdk/package.json +15 -5
  300. package/packages/sdk/src/agent-registry.ts +304 -0
  301. package/packages/sdk/src/consensus/byzantine.ts +587 -0
  302. package/packages/sdk/src/consensus/index.ts +147 -0
  303. package/packages/sdk/src/consensus/queen.ts +173 -0
  304. package/packages/sdk/src/consensus/types.ts +199 -0
  305. package/{plugins/bizar → packages/sdk}/src/dangerous-patterns.ts +6 -28
  306. package/packages/sdk/src/federation/audit.ts +156 -0
  307. package/packages/sdk/src/federation/budget.ts +269 -0
  308. package/packages/sdk/src/federation/envelope.ts +144 -0
  309. package/packages/sdk/src/federation/hmac.ts +160 -0
  310. package/packages/sdk/src/federation/index.ts +443 -0
  311. package/packages/sdk/src/federation/pii.ts +241 -0
  312. package/packages/sdk/src/federation/policy.ts +112 -0
  313. package/packages/sdk/src/federation/trust.ts +146 -0
  314. package/packages/sdk/src/fingerprint.ts +0 -0
  315. package/packages/sdk/src/index.ts +101 -69
  316. package/packages/sdk/src/mcp/bin.ts +51 -0
  317. package/packages/sdk/src/mcp/server.ts +848 -0
  318. package/{plugins/bizar/src/memory-vault.ts → packages/sdk/src/memory/index.ts} +18 -21
  319. package/packages/sdk/src/router/codemod-intent.ts +125 -0
  320. package/packages/sdk/src/router/index.ts +247 -0
  321. package/packages/sdk/src/router/memory-distillation-shim.mjs +18 -0
  322. package/packages/sdk/src/router/memory-distillation.ts +249 -0
  323. package/packages/sdk/src/router/model-router.ts +235 -0
  324. package/packages/sdk/src/router/q-learning-router.ts +304 -0
  325. package/packages/sdk/src/swarm-topology.ts +279 -0
  326. package/packages/sdk/tests/agent-registry.test.ts +285 -0
  327. package/packages/sdk/tests/consensus.test.ts +376 -0
  328. package/packages/sdk/tests/federation/audit.test.ts +164 -0
  329. package/packages/sdk/tests/federation/budget.test.ts +189 -0
  330. package/packages/sdk/tests/federation/envelope.test.ts +120 -0
  331. package/packages/sdk/tests/federation/hmac.test.ts +194 -0
  332. package/packages/sdk/tests/federation/orchestrator.test.ts +176 -0
  333. package/packages/sdk/tests/federation/pii.test.ts +106 -0
  334. package/packages/sdk/tests/federation/policy.test.ts +140 -0
  335. package/packages/sdk/tests/federation/trust.test.ts +124 -0
  336. package/packages/sdk/tests/mcp-tools.test.ts +204 -0
  337. package/packages/sdk/tests/memory-distillation.test.mjs +186 -0
  338. package/packages/sdk/tests/model-router.test.mjs +108 -0
  339. package/packages/sdk/tests/q-learning-router.test.mjs +100 -0
  340. package/packages/sdk/tests/router-orchestrator.test.mjs +172 -0
  341. package/packages/sdk/tests/router.test.mjs +91 -0
  342. package/packages/sdk/tests/sdk.test.mjs +172 -0
  343. package/packages/sdk/tests/swarm-topology.test.ts +258 -0
  344. package/packages/sdk/vitest.config.ts +1 -1
  345. package/scripts/bh-full-e2e.mjs +167 -368
  346. package/scripts/check-deps.mjs +28 -46
  347. package/scripts/mirror-claude-md.sh +78 -0
  348. package/scripts/test-in-container.sh +31 -9
  349. package/templates/clean-state-checklist.md +3 -3
  350. package/templates/sprint-contract.md +2 -2
  351. package/bizar-dash/.bizar/activity.log +0 -3
  352. package/bizar-dash/.omx/logs/omx-2026-07-07.jsonl +0 -1
  353. package/bizar-dash/.omx/state/session.json +0 -10
  354. package/bizar-dash/bizar-design/EXPLANATION.md +0 -307
  355. package/bizar-dash/bizar-design/INSTRUCTIONS.md +0 -149
  356. package/bizar-dash/bizar-design/canvas.html +0 -281
  357. package/bizar-dash/bizar-design/canvas.html.artifact.json +0 -19
  358. package/bizar-dash/bizar-design/components.css +0 -1665
  359. package/bizar-dash/bizar-design/components.html +0 -557
  360. package/bizar-dash/bizar-design/components.html.artifact.json +0 -19
  361. package/bizar-dash/bizar-design/desktop/agents.html +0 -74
  362. package/bizar-dash/bizar-design/desktop/agents.html.artifact.json +0 -19
  363. package/bizar-dash/bizar-design/desktop/memory.html +0 -75
  364. package/bizar-dash/bizar-design/desktop/memory.html.artifact.json +0 -19
  365. package/bizar-dash/bizar-design/desktop/metrics.html +0 -63
  366. package/bizar-dash/bizar-design/desktop/metrics.html.artifact.json +0 -19
  367. package/bizar-dash/bizar-design/desktop/overview.html +0 -95
  368. package/bizar-dash/bizar-design/desktop/overview.html.artifact.json +0 -19
  369. package/bizar-dash/bizar-design/desktop/settings.html +0 -70
  370. package/bizar-dash/bizar-design/desktop/settings.html.artifact.json +0 -19
  371. package/bizar-dash/bizar-design/icons.svg +0 -44
  372. package/bizar-dash/bizar-design/index.html +0 -192
  373. package/bizar-dash/bizar-design/index.html.artifact.json +0 -19
  374. package/bizar-dash/bizar-design/mobile/agents.html +0 -29
  375. package/bizar-dash/bizar-design/mobile/agents.html.artifact.json +0 -19
  376. package/bizar-dash/bizar-design/mobile/memory.html +0 -28
  377. package/bizar-dash/bizar-design/mobile/memory.html.artifact.json +0 -19
  378. package/bizar-dash/bizar-design/mobile/metrics.html +0 -29
  379. package/bizar-dash/bizar-design/mobile/metrics.html.artifact.json +0 -19
  380. package/bizar-dash/bizar-design/mobile/overview.html +0 -33
  381. package/bizar-dash/bizar-design/mobile/overview.html.artifact.json +0 -19
  382. package/bizar-dash/bizar-design/mobile/settings.html +0 -28
  383. package/bizar-dash/bizar-design/mobile/settings.html.artifact.json +0 -19
  384. package/bizar-dash/bizar-design/prototype.js +0 -114
  385. package/bizar-dash/bizar-design/tokens.css +0 -106
  386. package/bizar-dash/dist/assets/MobileChat-BnKN_Ks_.js +0 -1
  387. package/bizar-dash/dist/assets/MobileSettings-DjCPxC-Q.js +0 -1
  388. package/bizar-dash/dist/assets/icons-Do5N2jmX.js.map +0 -1
  389. package/bizar-dash/dist/assets/main-DYiZqMrn.js +0 -18
  390. package/bizar-dash/dist/assets/main-DYiZqMrn.js.map +0 -1
  391. package/bizar-dash/dist/assets/main-ietCEg_R.css +0 -1
  392. package/bizar-dash/dist/assets/markdown-C6mXtQxD.js +0 -1
  393. package/bizar-dash/dist/assets/mobile-CMHqtLV2.js +0 -1
  394. package/bizar-dash/dist/assets/mobile-layout-3jIhHX_p.js +0 -2
  395. package/bizar-dash/dist/assets/mobile-layout-3jIhHX_p.js.map +0 -1
  396. package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js +0 -40
  397. package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js.map +0 -1
  398. package/bizar-dash/dist/assets/useSlashCommands-DtITw8Xv.js +0 -2
  399. package/bizar-dash/dist/assets/useSlashCommands-DtITw8Xv.js.map +0 -1
  400. package/bizar-dash/dist/assets/vendor-CeHGtduv.js +0 -29
  401. package/bizar-dash/dist/assets/vendor-CeHGtduv.js.map +0 -1
  402. package/bizar-dash/node_modules/.package-lock.json +0 -6
  403. package/bizar-dash/package-lock.json +0 -6
  404. package/bizar-dash/src/server/cline-sdk.mjs +0 -132
  405. package/bizar-dash/src/server/routes/cline-session-detail.mjs +0 -559
  406. package/bizar-dash/src/server/routes/cline-sessions.mjs +0 -291
  407. package/cli/commands/cline-cmd.mjs +0 -289
  408. package/cli/commands/validate.test.mjs +0 -379
  409. package/cli/doctor.test.mjs +0 -350
  410. package/config/cline.json.template +0 -342
  411. package/packages/sdk/src/client.ts +0 -188
  412. package/packages/sdk/src/cline-events.ts +0 -134
  413. package/packages/sdk/src/cline-types.ts +0 -66
  414. package/packages/sdk/src/cline.ts +0 -339
  415. package/packages/sdk/src/errors.ts +0 -129
  416. package/packages/sdk/src/events.ts +0 -153
  417. package/packages/sdk/src/types.ts +0 -176
  418. package/packages/sdk/tests/client.test.ts +0 -217
  419. package/packages/sdk/tests/errors.test.ts +0 -108
  420. package/packages/sdk/tests/events.test.ts +0 -139
  421. package/packages/sdk/tests/fixtures/fetch-mock.ts +0 -152
  422. package/packages/sdk/tests/fixtures/sse-mock.ts +0 -30
  423. package/plugins/bizar/ARCHITECTURE.md +0 -142
  424. package/plugins/bizar/CONSTRAINTS.md +0 -67
  425. package/plugins/bizar/LICENSE +0 -21
  426. package/plugins/bizar/README.md +0 -448
  427. package/plugins/bizar/index.ts +0 -925
  428. package/plugins/bizar/package.json +0 -39
  429. package/plugins/bizar/scripts/check-forbidden-imports.sh +0 -33
  430. package/plugins/bizar/src/background-state.ts +0 -641
  431. package/plugins/bizar/src/background.ts +0 -1806
  432. package/plugins/bizar/src/cline-runner.ts +0 -203
  433. package/plugins/bizar/src/clineruntime.ts +0 -227
  434. package/plugins/bizar/src/commands-impl.ts +0 -151
  435. package/plugins/bizar/src/commands.ts +0 -1799
  436. package/plugins/bizar/src/compaction.d.mts +0 -48
  437. package/plugins/bizar/src/compaction.mjs +0 -192
  438. package/plugins/bizar/src/dashboard-client.ts +0 -233
  439. package/plugins/bizar/src/event-stream.ts +0 -606
  440. package/plugins/bizar/src/fingerprint.ts +0 -120
  441. package/plugins/bizar/src/handoff.ts +0 -79
  442. package/plugins/bizar/src/hooks/memory-flush-on-compact.ts +0 -123
  443. package/plugins/bizar/src/hooks/memory-inject.ts +0 -247
  444. package/plugins/bizar/src/hooks/memory-write-on-end.ts +0 -188
  445. package/plugins/bizar/src/hooks/skill-curator.ts +0 -180
  446. package/plugins/bizar/src/http-client.ts +0 -467
  447. package/plugins/bizar/src/key-rotation.ts +0 -218
  448. package/plugins/bizar/src/logger.ts +0 -144
  449. package/plugins/bizar/src/loop-engineering.ts +0 -241
  450. package/plugins/bizar/src/loop.ts +0 -176
  451. package/plugins/bizar/src/mistake-recovery.ts +0 -98
  452. package/plugins/bizar/src/odin.ts +0 -227
  453. package/plugins/bizar/src/options.ts +0 -470
  454. package/plugins/bizar/src/plan-fs.ts +0 -323
  455. package/plugins/bizar/src/reasoning-clean.ts +0 -454
  456. package/plugins/bizar/src/report.ts +0 -178
  457. package/plugins/bizar/src/research-prompt.ts +0 -35
  458. package/plugins/bizar/src/serve-info.ts +0 -228
  459. package/plugins/bizar/src/serve.ts +0 -496
  460. package/plugins/bizar/src/settings.ts +0 -349
  461. package/plugins/bizar/src/state.ts +0 -298
  462. package/plugins/bizar/src/tool-discipline.ts +0 -105
  463. package/plugins/bizar/src/tools/agent-browser.ts +0 -315
  464. package/plugins/bizar/src/tools/bg-collect.ts +0 -131
  465. package/plugins/bizar/src/tools/bg-get-comments.ts +0 -266
  466. package/plugins/bizar/src/tools/bg-kill.ts +0 -116
  467. package/plugins/bizar/src/tools/bg-pause.ts +0 -99
  468. package/plugins/bizar/src/tools/bg-report-progress.ts +0 -115
  469. package/plugins/bizar/src/tools/bg-resume.ts +0 -94
  470. package/plugins/bizar/src/tools/bg-send-message.ts +0 -223
  471. package/plugins/bizar/src/tools/bg-spawn.ts +0 -502
  472. package/plugins/bizar/src/tools/bg-status.ts +0 -130
  473. package/plugins/bizar/src/tools/graph-query.ts +0 -278
  474. package/plugins/bizar/src/tools/loop-engineering.ts +0 -193
  475. package/plugins/bizar/src/tools/memory-list.ts +0 -43
  476. package/plugins/bizar/src/tools/memory-read.ts +0 -69
  477. package/plugins/bizar/src/tools/memory-search.ts +0 -47
  478. package/plugins/bizar/src/tools/memory-write.ts +0 -54
  479. package/plugins/bizar/src/tools/open-kb.ts +0 -198
  480. package/plugins/bizar/src/tools/plan-action.ts +0 -785
  481. package/plugins/bizar/src/tools/read-glyph-feedback.ts +0 -191
  482. package/plugins/bizar/src/tools/sandbox.ts +0 -232
  483. package/plugins/bizar/src/tools/team-spawn.ts +0 -73
  484. package/plugins/bizar/src/tools/team-status.ts +0 -76
  485. package/plugins/bizar/src/tools/wait-for-feedback.ts +0 -415
  486. package/plugins/bizar/src/trajectory.ts +0 -104
  487. package/plugins/bizar/tests/README.md +0 -99
  488. package/plugins/bizar/tests/attach-handler-bug.test.ts +0 -169
  489. package/plugins/bizar/tests/background-state.test.ts +0 -277
  490. package/plugins/bizar/tests/background.test.ts +0 -402
  491. package/plugins/bizar/tests/block.test.ts +0 -195
  492. package/plugins/bizar/tests/canonical-key-order.test.ts +0 -75
  493. package/plugins/bizar/tests/clineruntime-config.test.ts +0 -283
  494. package/plugins/bizar/tests/commands-impl.test.ts +0 -316
  495. package/plugins/bizar/tests/commands.test.ts +0 -584
  496. package/plugins/bizar/tests/compaction.test.ts +0 -264
  497. package/plugins/bizar/tests/config.test.ts +0 -128
  498. package/plugins/bizar/tests/dashboard-client.test.ts +0 -159
  499. package/plugins/bizar/tests/dispose.test.ts +0 -336
  500. package/plugins/bizar/tests/event-stream.test.ts +0 -409
  501. package/plugins/bizar/tests/event.test.ts +0 -262
  502. package/plugins/bizar/tests/fingerprint.test.ts +0 -190
  503. package/plugins/bizar/tests/http-client.test.ts +0 -404
  504. package/plugins/bizar/tests/init-helpers.test.ts +0 -203
  505. package/plugins/bizar/tests/integration/slash-command.test.ts +0 -349
  506. package/plugins/bizar/tests/integration/tool-routing.test.ts +0 -98
  507. package/plugins/bizar/tests/key-rotation.test.ts +0 -396
  508. package/plugins/bizar/tests/loop-engineering.test.ts +0 -168
  509. package/plugins/bizar/tests/loop.test.ts +0 -397
  510. package/plugins/bizar/tests/memory-write-on-end.test.ts +0 -92
  511. package/plugins/bizar/tests/mistake-recovery.test.ts +0 -116
  512. package/plugins/bizar/tests/odin.test.ts +0 -125
  513. package/plugins/bizar/tests/options.test.ts +0 -329
  514. package/plugins/bizar/tests/reasoning-clean.test.ts +0 -422
  515. package/plugins/bizar/tests/safety.test.ts +0 -281
  516. package/plugins/bizar/tests/serve.test.ts +0 -339
  517. package/plugins/bizar/tests/settings.test.ts +0 -351
  518. package/plugins/bizar/tests/stall-think.test.ts +0 -750
  519. package/plugins/bizar/tests/state.test.ts +0 -276
  520. package/plugins/bizar/tests/tool-discipline.test.ts +0 -77
  521. package/plugins/bizar/tests/tools/agent-browser.test.ts +0 -98
  522. package/plugins/bizar/tests/tools/bg-collect.test.ts +0 -337
  523. package/plugins/bizar/tests/tools/bg-get-comments.test.ts +0 -485
  524. package/plugins/bizar/tests/tools/bg-kill.test.ts +0 -235
  525. package/plugins/bizar/tests/tools/bg-pause.test.ts +0 -61
  526. package/plugins/bizar/tests/tools/bg-report-progress.test.ts +0 -79
  527. package/plugins/bizar/tests/tools/bg-resume.test.ts +0 -40
  528. package/plugins/bizar/tests/tools/bg-send-message.test.ts +0 -116
  529. package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +0 -147
  530. package/plugins/bizar/tests/tools/bg-spawn-http.test.ts +0 -233
  531. package/plugins/bizar/tests/tools/bg-spawn.test.ts +0 -311
  532. package/plugins/bizar/tests/tools/bg-status.test.ts +0 -217
  533. package/plugins/bizar/tests/tools/cline-runner.test.ts +0 -115
  534. package/plugins/bizar/tests/tools/plan-action.test.ts +0 -599
  535. package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +0 -253
  536. package/plugins/bizar/tests/tools/sandbox.test.ts +0 -117
  537. package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +0 -390
  538. package/plugins/bizar/tests/update-deadlock.test.ts +0 -151
  539. package/plugins/bizar/tsconfig.json +0 -29
  540. package/scripts/mirror-agents-md.sh +0 -69
@@ -1,4 +1,13 @@
1
- // src/views/Agents.tsx — v3.1.0 agents with tags, categories, real-time status.
1
+ // src/views/Agents.tsx — Agent roster view (Wave 3 redesign).
2
+ //
3
+ // Wave 3 migrates the legacy card-grid agent roster to the Bizar design
4
+ // system: a split-pane layout with a sortable DataTable on the left and a
5
+ // detail Panel on the right that surfaces the selected agent's metadata,
6
+ // actions (Edit / Delete / Restart) and system prompt. Modals still use the
7
+ // legacy Modal component (the design-system Dialog will land in a later
8
+ // wave), and the public function signature, export name, API paths, and
9
+ // WS event subscriptions remain unchanged so callers and the server are
10
+ // not disturbed by this UI-only refactor.
2
11
  import { useEffect, useMemo, useState } from 'react';
3
12
  import {
4
13
  Bot,
@@ -8,29 +17,33 @@ import {
8
17
  Trash2,
9
18
  Play,
10
19
  Save,
11
- X,
12
20
  RotateCw,
13
- Activity,
14
- Tag as TagIcon,
15
- Folder,
16
- CheckCircle2,
17
- Clock,
18
- AlertTriangle,
19
- Circle,
20
- ChevronDown,
21
- ChevronRight,
21
+ CircleAlert,
22
22
  } from 'lucide-react';
23
23
  import { Button } from '../components/Button';
24
- import { Card, CardTitle } from '../components/Card';
25
- import { EmptyState } from '../components/EmptyState';
26
- import { Spinner } from '../components/Spinner';
27
- import { StatusBadge } from '../components/StatusBadge';
28
24
  import { useModal } from '../components/Modal';
29
25
  import { useToast } from '../components/Toast';
30
26
  import { api } from '../lib/api';
31
- import { cn, formatRelative, truncate } from '../lib/utils';
27
+ import { formatRelative, truncate } from '../lib/utils';
32
28
  import type { Agent, Settings, Snapshot } from '../lib/types';
33
29
 
30
+ // Design-system consumers — Wave 3 first real view to use them.
31
+ import { Stack, Inline, Box } from '../ui/primitives';
32
+ import {
33
+ DataTable,
34
+ KeyValueList,
35
+ EmptyState,
36
+ LoadingState,
37
+ type DataTableColumn,
38
+ type KeyValueItem,
39
+ } from '../ui/data';
40
+ import { Button as UIButton } from '../ui/controls';
41
+ import { Badge, StatusDot } from '../ui/feedback';
42
+ import { Panel } from '../ui/layout';
43
+ import { cx } from '../ui/utils/cx';
44
+
45
+ import '../styles/agents-redesign.css';
46
+
34
47
  type Props = {
35
48
  snapshot: Snapshot;
36
49
  settings: Settings;
@@ -50,35 +63,54 @@ const MODELS = [
50
63
  ];
51
64
 
52
65
  const CATEGORIES = [
53
- { id: 'reasoning', label: 'Reasoning', color: 'var(--accent)' },
54
- { id: 'code', label: 'Code', color: 'var(--info)' },
55
- { id: 'design', label: 'Design', color: 'var(--success)' },
56
- { id: 'planning', label: 'Planning', color: 'var(--warning)' },
57
- { id: 'gitops', label: 'GitOps', color: 'var(--error)' },
58
- { id: 'analysis', label: 'Analysis', color: 'var(--text-dim)' },
66
+ { id: 'reasoning', label: 'Reasoning', variant: 'accent' as const },
67
+ { id: 'code', label: 'Code', variant: 'info' as const },
68
+ { id: 'design', label: 'Design', variant: 'success' as const },
69
+ { id: 'planning', label: 'Planning', variant: 'warning' as const },
70
+ { id: 'gitops', label: 'GitOps', variant: 'danger' as const },
71
+ { id: 'analysis', label: 'Analysis', variant: 'neutral' as const },
59
72
  ];
60
73
 
61
- function categoryColor(cat: string | undefined): string {
62
- return CATEGORIES.find((c) => c.id === cat)?.color || 'var(--text-dim)';
74
+ function categoryMeta(cat: string | undefined): { label: string; variant: 'neutral' | 'info' | 'success' | 'warning' | 'danger' | 'accent' } {
75
+ if (!cat) return { label: '—', variant: 'neutral' };
76
+ const found = CATEGORIES.find((c) => c.id === cat);
77
+ return found
78
+ ? { label: found.label, variant: found.variant }
79
+ : { label: cat, variant: 'neutral' };
63
80
  }
64
81
 
65
- function StatusDot({ status, isStuck }: { status?: string; isStuck?: boolean }) {
66
- const color =
67
- isStuck ? 'var(--error)'
68
- : status === 'working' ? 'var(--info)'
69
- : status === 'error' ? 'var(--error)'
70
- : 'var(--text-dim)';
71
- return <span className="agent-status-dot" style={{ background: color }} />;
82
+ type StatusKind = 'idle' | 'working' | 'error' | 'stuck';
83
+
84
+ function statusKind(agent: Agent): StatusKind {
85
+ if (agent.isStuck) return 'stuck';
86
+ const s = (agent.status || 'idle') as string;
87
+ if (s === 'working') return 'working';
88
+ if (s === 'error') return 'error';
89
+ return 'idle';
72
90
  }
73
91
 
74
- function StatusPill({ agent }: { agent: Agent }) {
75
- if (agent.isStuck) {
76
- return <StatusBadge kind="error" dot>stuck</StatusBadge>;
77
- }
78
- const status = agent.status || 'idle';
79
- if (status === 'working') return <StatusBadge kind="info" dot>working</StatusBadge>;
80
- if (status === 'error') return <StatusBadge kind="error" dot>error</StatusBadge>;
81
- return <StatusBadge kind="neutral" dot>idle</StatusBadge>;
92
+ function statusLabel(kind: StatusKind): string {
93
+ if (kind === 'stuck') return 'stuck';
94
+ return kind;
95
+ }
96
+
97
+ function statusDotVariant(kind: StatusKind): 'neutral' | 'info' | 'danger' {
98
+ if (kind === 'working') return 'info';
99
+ if (kind === 'error' || kind === 'stuck') return 'danger';
100
+ return 'neutral';
101
+ }
102
+
103
+ function lastSeenTs(agent: Agent): number | undefined {
104
+ // The Agent type exposes `lastSeen` (legacy) and a future `lastSeenAt`.
105
+ // Either is acceptable; missing fields render as "—".
106
+ const a = agent as unknown as { lastSeenAt?: number };
107
+ return a.lastSeenAt ?? agent.lastSeen;
108
+ }
109
+
110
+ function tagsToString(tags: string[] | undefined, max = 4): string {
111
+ if (!tags || tags.length === 0) return '—';
112
+ const shown = tags.slice(0, max).join(', ');
113
+ return tags.length > max ? `${shown} +${tags.length - max}` : shown;
82
114
  }
83
115
 
84
116
  export function Agents({ snapshot, refreshSnapshot }: Props) {
@@ -88,13 +120,22 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
88
120
  const [loading, setLoading] = useState(!snapshot.agents);
89
121
  const [categoryFilter, setCategoryFilter] = useState<string>('');
90
122
  const [search, setSearch] = useState('');
123
+ const [selectedName, setSelectedName] = useState<string | null>(null);
91
124
 
92
125
  useEffect(() => {
93
126
  setAgents(snapshot.agents || []);
94
127
  setLoading(!snapshot.agents);
95
128
  }, [snapshot.agents]);
96
129
 
97
- const reload = async () => {
130
+ // If the selected agent disappears from the list (deleted elsewhere, WS
131
+ // swap), clear the selection so the detail panel reverts to the placeholder.
132
+ useEffect(() => {
133
+ if (selectedName && !agents.some((a) => a.name === selectedName)) {
134
+ setSelectedName(null);
135
+ }
136
+ }, [agents, selectedName]);
137
+
138
+ const reload = async (): Promise<void> => {
98
139
  try {
99
140
  const d = await api.get<{ agents: Agent[] }>('/agents');
100
141
  setAgents(d.agents || []);
@@ -108,7 +149,11 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
108
149
  const sorted = useMemo(() => {
109
150
  let out = [...agents];
110
151
  if (categoryFilter) {
111
- out = out.filter((a) => categoryFilter === '__none__' ? !(a.category || '') : (a.category || '') === categoryFilter);
152
+ out = out.filter((a) =>
153
+ categoryFilter === '__none__'
154
+ ? !(a.category || '')
155
+ : (a.category || '') === categoryFilter,
156
+ );
112
157
  }
113
158
  if (search) {
114
159
  const q = search.toLowerCase();
@@ -122,13 +167,16 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
122
167
  return out.sort((a, b) => a.name.localeCompare(b.name));
123
168
  }, [agents, categoryFilter, search]);
124
169
 
125
- const allTags = useMemo(() => {
126
- const set = new Set<string>();
127
- for (const a of agents) for (const t of a.tags || []) set.add(t);
128
- return Array.from(set).sort();
129
- }, [agents]);
170
+ const hasLastSeen = sorted.some((a) => typeof lastSeenTs(a) === 'number');
171
+
172
+ const selected = useMemo(
173
+ () => sorted.find((a) => a.name === selectedName) || null,
174
+ [sorted, selectedName],
175
+ );
130
176
 
131
- const onCreate = () => {
177
+ // ─── Handlers (modal flows kept on the legacy Modal) ───────────────────
178
+
179
+ const onCreate = (): void => {
132
180
  let nameEl: HTMLInputElement | null = null;
133
181
  let descEl: HTMLInputElement | null = null;
134
182
  let modelEl: HTMLSelectElement | null = null;
@@ -251,6 +299,7 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
251
299
  prompt: promptEl?.value || '',
252
300
  });
253
301
  setAgents((cur) => [...cur, created]);
302
+ setSelectedName(created.name);
254
303
  toast.success('Agent created.');
255
304
  modal.close();
256
305
  await refreshSnapshot();
@@ -266,7 +315,7 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
266
315
  });
267
316
  };
268
317
 
269
- const onEdit = async (a: Agent) => {
318
+ const onEdit = async (a: Agent): Promise<void> => {
270
319
  let descEl: HTMLInputElement | null = null;
271
320
  let modelEl: HTMLSelectElement | null = null;
272
321
  let modeEl: HTMLSelectElement | null = null;
@@ -345,7 +394,7 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
345
394
  <input type="checkbox" value={t} aria-label={t} defaultChecked={full.tools?.includes(t)} />
346
395
  <span>{t}</span>
347
396
  </label>
348
- ))}
397
+ ))}
349
398
  </div>
350
399
  </fieldset>
351
400
  <label className="field-label" htmlFor="agent-edit-prompt">System prompt</label>
@@ -404,7 +453,7 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
404
453
  }
405
454
  };
406
455
 
407
- const onDelete = async (a: Agent) => {
456
+ const onDelete = async (a: Agent): Promise<void> => {
408
457
  if (!confirm(`Delete agent "${a.name}"? This removes ${a.path}.`)) return;
409
458
  try {
410
459
  await api.del(`/agents/${encodeURIComponent(a.name)}`);
@@ -415,7 +464,7 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
415
464
  }
416
465
  };
417
466
 
418
- const onInvoke = async (a: Agent) => {
467
+ const onInvoke = (a: Agent): void => {
419
468
  let promptEl: HTMLTextAreaElement | null = null;
420
469
  modal.open({
421
470
  title: `Invoke ${a.name}`,
@@ -461,7 +510,7 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
461
510
  });
462
511
  };
463
512
 
464
- const onRestart = async (a: Agent) => {
513
+ const onRestart = async (a: Agent): Promise<void> => {
465
514
  try {
466
515
  const updated = await api.post<Agent>(`/agents/${encodeURIComponent(a.name)}/restart`);
467
516
  setAgents((cur) => cur.map((x) => (x.name === a.name ? updated : x)));
@@ -471,7 +520,7 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
471
520
  }
472
521
  };
473
522
 
474
- const onSetStatus = async (a: Agent, status: 'idle' | 'working' | 'error') => {
523
+ const onSetStatus = async (a: Agent, status: 'idle' | 'working' | 'error'): Promise<void> => {
475
524
  try {
476
525
  const updated = await api.post<Agent>(`/agents/${encodeURIComponent(a.name)}/status`, { status });
477
526
  setAgents((cur) => cur.map((x) => (x.name === a.name ? updated : x)));
@@ -480,237 +529,428 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
480
529
  }
481
530
  };
482
531
 
532
+ // ─── Table column definitions ──────────────────────────────────────────
533
+
534
+ type Col = DataTableColumn<Agent>;
535
+
536
+ const columns: Col[] = [
537
+ {
538
+ key: 'name',
539
+ header: 'Name',
540
+ sortable: true,
541
+ accessor: (a) => a.name,
542
+ render: (a) => (
543
+ <span className="agents-name-cell">
544
+ <span
545
+ className={cx(
546
+ 'agents-name-cell__swatch',
547
+ !a.color && 'agents-name-cell__swatch--empty',
548
+ )}
549
+ style={a.color ? { background: a.color } : undefined}
550
+ aria-hidden="true"
551
+ />
552
+ {a.name}
553
+ </span>
554
+ ),
555
+ },
556
+ {
557
+ key: 'status',
558
+ header: 'Status',
559
+ sortable: true,
560
+ accessor: (a) => statusKind(a),
561
+ render: (a) => {
562
+ const kind = statusKind(a);
563
+ return (
564
+ <span className="agents-status-cell">
565
+ <StatusDot
566
+ variant={statusDotVariant(kind)}
567
+ size="sm"
568
+ pulse={kind === 'stuck'}
569
+ label={statusLabel(kind)}
570
+ />
571
+ <span
572
+ className={cx(
573
+ 'agents-status-cell__label',
574
+ kind === 'stuck' && 'agents-status-cell__label--stuck',
575
+ )}
576
+ >
577
+ {statusLabel(kind)}
578
+ </span>
579
+ </span>
580
+ );
581
+ },
582
+ },
583
+ {
584
+ key: 'category',
585
+ header: 'Category',
586
+ sortable: true,
587
+ accessor: (a) => a.category || '',
588
+ render: (a) => {
589
+ const meta = categoryMeta(a.category);
590
+ if (!a.category) return <span className="muted">—</span>;
591
+ return (
592
+ <span className="agents-category-cell">
593
+ <Badge variant={meta.variant} size="sm">{meta.label}</Badge>
594
+ </span>
595
+ );
596
+ },
597
+ },
598
+ {
599
+ key: 'tags',
600
+ header: 'Tags',
601
+ accessor: (a) => (a.tags || []).join(','),
602
+ render: (a) => (
603
+ <span className="agents-tags-cell" title={(a.tags || []).join(', ')}>
604
+ {tagsToString(a.tags)}
605
+ </span>
606
+ ),
607
+ },
608
+ ];
609
+
610
+ if (hasLastSeen) {
611
+ columns.push({
612
+ key: 'lastSeen',
613
+ header: 'Last seen',
614
+ sortable: true,
615
+ accessor: (a) => lastSeenTs(a) || 0,
616
+ render: (a) => {
617
+ const ts = lastSeenTs(a);
618
+ return <span className="muted tabular-nums">{ts ? formatRelative(ts) : '—'}</span>;
619
+ },
620
+ });
621
+ }
622
+
623
+ // ─── Detail panel ─────────────────────────────────────────────────────
624
+
625
+ function renderDetailBody(): React.JSX.Element {
626
+ if (!selected) {
627
+ return (
628
+ <div className="agents-detail__placeholder">
629
+ <span className="agents-detail__placeholder-icon" aria-hidden="true">
630
+ <Bot size={28} />
631
+ </span>
632
+ <span className="agents-detail__placeholder-title">No agent selected</span>
633
+ <span>Pick a row from the table to see its metadata, actions, and prompt.</span>
634
+ </div>
635
+ );
636
+ }
637
+
638
+ const a = selected;
639
+ const kind = statusKind(a);
640
+ const catMeta = categoryMeta(a.category);
641
+ const canRestart = a.isStuck || a.status === 'working' || a.status === 'error';
642
+
643
+ const metaItems: KeyValueItem[] = [
644
+ {
645
+ key: 'status',
646
+ label: 'Status',
647
+ value: (
648
+ <span className="agents-status-cell">
649
+ <StatusDot
650
+ variant={statusDotVariant(kind)}
651
+ size="sm"
652
+ pulse={kind === 'stuck'}
653
+ label={statusLabel(kind)}
654
+ />
655
+ <span className="agents-status-cell__label">{statusLabel(kind)}</span>
656
+ </span>
657
+ ),
658
+ },
659
+ {
660
+ key: 'category',
661
+ label: 'Category',
662
+ value: a.category
663
+ ? <Badge variant={catMeta.variant} size="sm">{catMeta.label}</Badge>
664
+ : <span className="muted">—</span>,
665
+ },
666
+ {
667
+ key: 'tags',
668
+ label: 'Tags',
669
+ value: a.tags && a.tags.length > 0 ? (
670
+ <Inline gap={1} className="agents-detail__tags">
671
+ {a.tags.map((t) => (
672
+ <Badge key={t} variant="neutral" size="sm">{t}</Badge>
673
+ ))}
674
+ </Inline>
675
+ ) : <span className="muted">—</span>,
676
+ },
677
+ {
678
+ key: 'tools',
679
+ label: 'Tools',
680
+ value: a.tools && a.tools.length > 0 ? (
681
+ <Inline gap={1} className="agents-detail__tools">
682
+ {a.tools.map((t) => (
683
+ <Badge key={t} variant="info" size="sm">{t}</Badge>
684
+ ))}
685
+ </Inline>
686
+ ) : <span className="muted">—</span>,
687
+ },
688
+ {
689
+ key: 'model',
690
+ label: 'Model',
691
+ mono: true,
692
+ copyable: true,
693
+ value: a.model || '—',
694
+ },
695
+ {
696
+ key: 'mode',
697
+ label: 'Mode',
698
+ value: a.mode || 'subagent',
699
+ },
700
+ {
701
+ key: 'color',
702
+ label: 'Color',
703
+ value: a.color ? (
704
+ <Inline gap={2} align="center">
705
+ <span
706
+ className="agents-detail__name-swatch"
707
+ style={{ background: a.color }}
708
+ aria-hidden="true"
709
+ />
710
+ <code className="mono">{a.color}</code>
711
+ </Inline>
712
+ ) : <span className="muted">—</span>,
713
+ },
714
+ {
715
+ key: 'description',
716
+ label: 'Description',
717
+ value: a.description
718
+ ? <div className="agents-detail__description">{truncate(a.description, 600)}</div>
719
+ : <span className="muted">—</span>,
720
+ },
721
+ ];
722
+
723
+ if (typeof lastSeenTs(a) === 'number') {
724
+ metaItems.push({
725
+ key: 'lastSeen',
726
+ label: 'Last seen',
727
+ mono: true,
728
+ value: formatRelative(lastSeenTs(a) as number),
729
+ });
730
+ }
731
+
732
+ if (a.lastError?.message) {
733
+ metaItems.push({
734
+ key: 'lastError',
735
+ label: 'Last error',
736
+ value: (
737
+ <div className="agents-detail__error">{a.lastError.message}</div>
738
+ ),
739
+ });
740
+ }
741
+
742
+ return (
743
+ <Stack gap={4} className="agents-detail__meta">
744
+ <KeyValueList items={metaItems} orientation="horizontal" />
745
+
746
+ <Box className="agents-detail__prompt">
747
+ <span className="agents-detail__prompt-label">System prompt</span>
748
+ {a.prompt ? (
749
+ <pre className="agents-detail__prompt-body">{a.prompt}</pre>
750
+ ) : (
751
+ <span className="agents-detail__prompt-empty">No prompt configured.</span>
752
+ )}
753
+ </Box>
754
+
755
+ <Inline gap={2} wrap className="agents-detail__actions">
756
+ <UIButton
757
+ variant="secondary"
758
+ size="sm"
759
+ icon={<Pencil size={12} />}
760
+ onClick={() => onEdit(a)}
761
+ aria-label={`Edit ${a.name}`}
762
+ data-testid={`agents-detail-edit-${a.name}`}
763
+ >
764
+ Edit
765
+ </UIButton>
766
+ <UIButton
767
+ variant="secondary"
768
+ size="sm"
769
+ icon={<Play size={12} />}
770
+ onClick={() => onInvoke(a)}
771
+ aria-label={`Invoke ${a.name}`}
772
+ data-testid={`agents-detail-invoke-${a.name}`}
773
+ >
774
+ Invoke
775
+ </UIButton>
776
+ {canRestart && (
777
+ <UIButton
778
+ variant="ghost"
779
+ size="sm"
780
+ icon={<RotateCw size={12} />}
781
+ onClick={() => onRestart(a)}
782
+ aria-label={`Restart ${a.name}`}
783
+ data-testid={`agents-detail-restart-${a.name}`}
784
+ >
785
+ Restart
786
+ </UIButton>
787
+ )}
788
+ <UIButton
789
+ variant="danger"
790
+ size="sm"
791
+ icon={<Trash2 size={12} />}
792
+ onClick={() => onDelete(a)}
793
+ aria-label={`Delete ${a.name}`}
794
+ data-testid={`agents-detail-delete-${a.name}`}
795
+ >
796
+ Delete
797
+ </UIButton>
798
+ </Inline>
799
+
800
+ {a.isStuck && (
801
+ <Inline gap={2} align="center" className="agents-detail__error">
802
+ <CircleAlert size={12} />
803
+ <span>
804
+ Agent is stuck — restart to reset state.
805
+ </span>
806
+ </Inline>
807
+ )}
808
+ </Stack>
809
+ );
810
+ }
811
+
812
+ // ─── Render ────────────────────────────────────────────────────────────
813
+
814
+ const categoryOptions = [
815
+ { value: '', label: 'All categories' },
816
+ ...CATEGORIES.map((c) => ({ value: c.id, label: c.label })),
817
+ { value: '__none__', label: '(no category)' },
818
+ ];
819
+
483
820
  return (
484
- <div className="view view-agents">
485
- <header className="view-header">
486
- <div className="view-header-text">
487
- <h2 className="view-title">
488
- <Bot size={18} /> Agents ({sorted.length})
821
+ <div className="agents-redesign" data-testid="agents-view">
822
+ <header className="agents-redesign__header">
823
+ <div className="agents-redesign__header-text">
824
+ <h2 className="agents-redesign__title">
825
+ <Bot size={18} aria-hidden="true" />
826
+ <span>Agents</span>
827
+ <span className="agents-redesign__title-count" data-testid="agents-count">
828
+ ({sorted.length})
829
+ </span>
489
830
  </h2>
490
- <p className="view-subtitle">
491
- The Norse pantheon — click <kbd>Edit</kbd> to modify or <kbd>Invoke</kbd> to dispatch.
831
+ <p className="agents-redesign__subtitle">
832
+ The Norse pantheon — pick a row to inspect, <kbd>Edit</kbd> to modify, <kbd>Invoke</kbd> to dispatch.
492
833
  </p>
493
834
  </div>
494
- <div className="view-actions">
495
- <div className="search-input">
496
- <label htmlFor="agents-search" className="sr-only">Search agents</label>
835
+ <div className="agents-redesign__controls">
836
+ <span className="agents-redesign__control" style={{ minWidth: 180 }}>
837
+ <label htmlFor="agents-search" className="vh">Search agents</label>
497
838
  <input
498
839
  id="agents-search"
499
840
  className="input"
500
- type="text"
501
- placeholder="Search…"
841
+ type="search"
842
+ placeholder="Search name, description, tag…"
502
843
  value={search}
503
844
  onChange={(e) => setSearch(e.target.value)}
504
845
  aria-label="Search agents"
846
+ data-testid="agents-search"
505
847
  />
506
- </div>
507
- <label htmlFor="agents-category-filter" className="sr-only">Filter by category</label>
508
- <select
509
- id="agents-category-filter"
510
- className="select select-sm"
511
- value={categoryFilter}
512
- onChange={(e) => setCategoryFilter(e.target.value)}
848
+ </span>
849
+ <span className="agents-redesign__control" style={{ minWidth: 180 }}>
850
+ <label htmlFor="agents-category-filter" className="vh">Filter by category</label>
851
+ <select
852
+ id="agents-category-filter"
853
+ className="select"
854
+ value={categoryFilter}
855
+ onChange={(e) => setCategoryFilter(e.target.value)}
856
+ data-testid="agents-category-filter"
857
+ >
858
+ {categoryOptions.map((opt) => (
859
+ <option key={opt.value || '__all__'} value={opt.value}>{opt.label}</option>
860
+ ))}
861
+ </select>
862
+ </span>
863
+ <UIButton
864
+ variant="ghost"
865
+ size="sm"
866
+ icon={<RefreshCw size={12} />}
867
+ onClick={reload}
868
+ aria-label="Refresh"
869
+ data-testid="agents-refresh"
513
870
  >
514
- <option value="">All categories</option>
515
- {CATEGORIES.map((c) => (
516
- <option key={c.id} value={c.id}>{c.label}</option>
517
- ))}
518
- <option value="__none__">(no category)</option>
519
- </select>
520
- <Button variant="secondary" size="sm" onClick={reload}>
521
- <RefreshCw size={14} /> Refresh
522
- </Button>
523
- <Button variant="primary" size="sm" onClick={onCreate}>
524
- <Plus size={14} /> New agent
525
- </Button>
871
+ Refresh
872
+ </UIButton>
873
+ <UIButton
874
+ variant="primary"
875
+ size="sm"
876
+ icon={<Plus size={12} />}
877
+ onClick={onCreate}
878
+ aria-label="New agent"
879
+ data-testid="agents-new"
880
+ >
881
+ New agent
882
+ </UIButton>
526
883
  </div>
527
884
  </header>
528
885
 
529
- {allTags.length > 0 && (
530
- <div className="agent-tags-row">
531
- <TagIcon size={12} />
532
- {allTags.map((t) => (
533
- <span key={t} className="tag">{t}</span>
534
- ))}
535
- </div>
536
- )}
537
-
538
- {loading ? (
539
- <div className="view-loading"><Spinner size="lg" /></div>
540
- ) : sorted.length === 0 ? (
541
- <EmptyState
542
- icon={<Bot size={32} />}
543
- title="No agents found"
544
- message="Run bizar in the terminal to install Bizar."
545
- />
546
- ) : (
547
- <div className="agent-grid">
548
- {sorted.map((a) => (
549
- <AgentCard
550
- key={a.name}
551
- agent={a}
552
- onInvoke={() => onInvoke(a)}
553
- onEdit={() => onEdit(a)}
554
- onDelete={() => onDelete(a)}
555
- onRestart={() => onRestart(a)}
556
- onSetStatus={(s) => onSetStatus(a, s)}
886
+ <div className="agents-redesign__body">
887
+ <Box className="agents-roster">
888
+ {loading ? (
889
+ <LoadingState label="Loading agents…" rows={3} />
890
+ ) : sorted.length === 0 ? (
891
+ <EmptyState
892
+ icon={<Bot size={32} />}
893
+ title="No agents found"
894
+ description={
895
+ agents.length === 0
896
+ ? 'Run bizar in the terminal to install Bizar.'
897
+ : 'No agents match the current filters — try clearing the search or category.'
898
+ }
899
+ action={
900
+ agents.length === 0
901
+ ? undefined
902
+ : {
903
+ label: 'Clear filters',
904
+ onClick: () => {
905
+ setSearch('');
906
+ setCategoryFilter('');
907
+ },
908
+ }
909
+ }
557
910
  />
558
- ))}
559
- </div>
560
- )}
561
- </div>
562
- );
563
- }
564
-
565
- function AgentCard({
566
- agent,
567
- onInvoke,
568
- onEdit,
569
- onDelete,
570
- onRestart,
571
- onSetStatus,
572
- }: {
573
- agent: Agent;
574
- onInvoke: () => void;
575
- onEdit: () => void;
576
- onDelete: () => void;
577
- onRestart: () => void;
578
- onSetStatus: (s: 'idle' | 'working' | 'error') => void;
579
- }) {
580
- const [expanded, setExpanded] = useState(false);
581
- const catColor = categoryColor(agent.category);
582
- const isWorking = (agent.status === 'working' || !!agent.currentTaskId) && !agent.isStuck;
583
- return (
584
- <Card
585
- variant="elevated"
586
- interactive
587
- className={cn(
588
- 'agent-card',
589
- isWorking && 'is-working',
590
- agent.isStuck && 'is-stuck',
591
- )}
592
- >
593
- <div className="agent-card-head">
594
- <div className="agent-card-name">
595
- <StatusDot status={agent.status} isStuck={agent.isStuck} />
596
- {agent.name}
597
- </div>
598
- <div className="agent-card-badges">
599
- {agent.category && (
600
- <span
601
- className="agent-card-category"
602
- style={{ background: `color-mix(in srgb, ${catColor} 18%, transparent)`, color: catColor }}
603
- >
604
- {agent.category}
605
- </span>
606
- )}
607
- <StatusPill agent={agent} />
608
- </div>
609
- </div>
610
- <p className="agent-card-desc">{truncate(agent.description, 200)}</p>
611
- <div className="agent-card-meta">
612
- <span className="mono" title={agent.model || ''}>
613
- {agent.model || '—'}
614
- </span>
615
- <span className="tabular-nums muted">{formatRelative(agent.mtime)}</span>
616
- </div>
617
- {agent.tags && agent.tags.length > 0 && (
618
- <div className="agent-card-tags">
619
- {agent.tags.map((t) => (
620
- <span key={t} className="agent-card-tag">{t}</span>
621
- ))}
622
- </div>
623
- )}
624
- {(isWorking || agent.lastTask) && (
625
- <div className="agent-card-activity">
626
- {agent.currentTaskId && (
627
- <div className="agent-card-row">
628
- <Activity size={12} />
629
- <span className="muted">Working on</span>
630
- <code className="mono">{agent.currentTaskId}</code>
631
- </div>
632
- )}
633
- {agent.lastTask && !agent.currentTaskId && (
634
- <div className="agent-card-row">
635
- <CheckCircle2 size={12} />
636
- <span className="muted">Last</span>
637
- <code className="mono">{agent.lastTask.id}</code>
638
- <span className="muted tabular-nums">{formatRelative(agent.lastTask.finishedAt)}</span>
639
- </div>
640
- )}
641
- {agent.tasksTotal != null && agent.tasksTotal > 0 && (
642
- <div className="agent-card-row">
643
- <Circle size={12} />
644
- <span className="muted">Success rate</span>
645
- <span className="tabular-nums">{Math.round((agent.successRate || 0) * 100)}%</span>
646
- <span className="muted tabular-nums">({agent.tasksSucceeded}/{agent.tasksTotal})</span>
911
+ ) : (
912
+ <div className="agents-roster__table" data-testid="agents-table">
913
+ <DataTable<Agent>
914
+ columns={columns}
915
+ data={sorted}
916
+ rowKey={(a) => a.name}
917
+ compact
918
+ onRowClick={(a) => setSelectedName(a.name)}
919
+ />
647
920
  </div>
648
921
  )}
649
- </div>
650
- )}
651
- {agent.lastError && (
652
- <div className="agent-card-error">
653
- <AlertTriangle size={12} />
654
- <span className="muted">Last error: {agent.lastError.message}</span>
655
- </div>
656
- )}
657
- <div className="agent-card-actions">
658
- <Button variant="primary" size="sm" onClick={onInvoke}>
659
- <Play size={12} /> Invoke
660
- </Button>
661
- <Button variant="secondary" size="sm" onClick={onEdit}>
662
- <Pencil size={12} /> Edit
663
- </Button>
664
- {(agent.isStuck || agent.status === 'working' || agent.status === 'error') && (
665
- <Button variant="ghost" size="sm" onClick={onRestart} title="Reset agent status">
666
- <RotateCw size={12} /> Restart
667
- </Button>
668
- )}
669
- <Button variant="ghost" size="sm" onClick={onDelete}>
670
- <Trash2 size={12} />
671
- </Button>
672
- <button
673
- type="button"
674
- className="icon-btn"
675
- aria-label={expanded ? 'Collapse' : 'Expand'}
676
- onClick={() => setExpanded((v) => !v)}
677
- style={{ marginLeft: 'auto' }}
922
+ </Box>
923
+
924
+ <Panel
925
+ title={
926
+ selected ? (
927
+ <span className="agents-detail__name">
928
+ <span
929
+ className={cx(
930
+ 'agents-detail__name-swatch',
931
+ !selected.color && 'agents-detail__name-swatch--empty',
932
+ )}
933
+ style={selected.color ? { background: selected.color } : undefined}
934
+ aria-hidden="true"
935
+ />
936
+ <span>{selected.name}</span>
937
+ </span>
938
+ ) : (
939
+ 'Agent detail'
940
+ )
941
+ }
942
+ description={
943
+ selected
944
+ ? selected.path
945
+ : 'Select an agent from the roster to see its metadata.'
946
+ }
947
+ padding={4}
948
+ variant="outlined"
949
+ className="agents-detail"
678
950
  >
679
- {expanded ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
680
- </button>
951
+ {renderDetailBody()}
952
+ </Panel>
681
953
  </div>
682
- {expanded && (
683
- <div className="agent-card-expanded">
684
- <div className="agent-card-status-actions">
685
- <span className="muted text-sm">Set status:</span>
686
- <Button
687
- variant={agent.status === 'idle' ? 'primary' : 'ghost'}
688
- size="sm"
689
- onClick={() => onSetStatus('idle')}
690
- >
691
- Idle
692
- </Button>
693
- <Button
694
- variant={agent.status === 'working' ? 'primary' : 'ghost'}
695
- size="sm"
696
- onClick={() => onSetStatus('working')}
697
- >
698
- Working
699
- </Button>
700
- <Button
701
- variant={agent.status === 'error' ? 'primary' : 'ghost'}
702
- size="sm"
703
- onClick={() => onSetStatus('error')}
704
- >
705
- Error
706
- </Button>
707
- </div>
708
- <div className="agent-card-meta">
709
- <Folder size={11} />
710
- <code className="mono agent-card-path">{agent.path}</code>
711
- </div>
712
- </div>
713
- )}
714
- </Card>
954
+ </div>
715
955
  );
716
- }
956
+ }