@contractspec/example.agent-console 0.0.0-canary-20260113170453

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 (306) hide show
  1. package/.turbo/turbo-build$colon$bundle.log +280 -0
  2. package/.turbo/turbo-build.log +281 -0
  3. package/CHANGELOG.md +368 -0
  4. package/LICENSE +21 -0
  5. package/README.md +86 -0
  6. package/dist/agent/agent.entity.d.ts +55 -0
  7. package/dist/agent/agent.entity.d.ts.map +1 -0
  8. package/dist/agent/agent.entity.js +136 -0
  9. package/dist/agent/agent.entity.js.map +1 -0
  10. package/dist/agent/agent.enum.d.ts +18 -0
  11. package/dist/agent/agent.enum.d.ts.map +1 -0
  12. package/dist/agent/agent.enum.js +34 -0
  13. package/dist/agent/agent.enum.js.map +1 -0
  14. package/dist/agent/agent.event.d.ts +128 -0
  15. package/dist/agent/agent.event.d.ts.map +1 -0
  16. package/dist/agent/agent.event.js +210 -0
  17. package/dist/agent/agent.event.js.map +1 -0
  18. package/dist/agent/agent.handler.d.ts +100 -0
  19. package/dist/agent/agent.handler.d.ts.map +1 -0
  20. package/dist/agent/agent.handler.js +84 -0
  21. package/dist/agent/agent.handler.js.map +1 -0
  22. package/dist/agent/agent.operation.d.ts +471 -0
  23. package/dist/agent/agent.operation.d.ts.map +1 -0
  24. package/dist/agent/agent.operation.js +486 -0
  25. package/dist/agent/agent.operation.js.map +1 -0
  26. package/dist/agent/agent.presentation.d.ts +18 -0
  27. package/dist/agent/agent.presentation.d.ts.map +1 -0
  28. package/dist/agent/agent.presentation.js +89 -0
  29. package/dist/agent/agent.presentation.js.map +1 -0
  30. package/dist/agent/agent.schema.d.ts +401 -0
  31. package/dist/agent/agent.schema.d.ts.map +1 -0
  32. package/dist/agent/agent.schema.js +406 -0
  33. package/dist/agent/agent.schema.js.map +1 -0
  34. package/dist/agent/agent.test-spec.d.ts +8 -0
  35. package/dist/agent/agent.test-spec.d.ts.map +1 -0
  36. package/dist/agent/agent.test-spec.js +65 -0
  37. package/dist/agent/agent.test-spec.js.map +1 -0
  38. package/dist/agent/index.d.ts +8 -0
  39. package/dist/agent/index.js +9 -0
  40. package/dist/agent.capability.d.ts +7 -0
  41. package/dist/agent.capability.d.ts.map +1 -0
  42. package/dist/agent.capability.js +20 -0
  43. package/dist/agent.capability.js.map +1 -0
  44. package/dist/agent.feature.d.ts +12 -0
  45. package/dist/agent.feature.d.ts.map +1 -0
  46. package/dist/agent.feature.js +305 -0
  47. package/dist/agent.feature.js.map +1 -0
  48. package/dist/docs/agent-console.docblock.d.ts +1 -0
  49. package/dist/docs/agent-console.docblock.js +113 -0
  50. package/dist/docs/agent-console.docblock.js.map +1 -0
  51. package/dist/docs/index.d.ts +1 -0
  52. package/dist/docs/index.js +1 -0
  53. package/dist/example.d.ts +7 -0
  54. package/dist/example.d.ts.map +1 -0
  55. package/dist/example.js +58 -0
  56. package/dist/example.js.map +1 -0
  57. package/dist/handlers/agent.handlers.d.ts +135 -0
  58. package/dist/handlers/agent.handlers.d.ts.map +1 -0
  59. package/dist/handlers/agent.handlers.js +263 -0
  60. package/dist/handlers/agent.handlers.js.map +1 -0
  61. package/dist/handlers/index.d.ts +5 -0
  62. package/dist/handlers/index.js +6 -0
  63. package/dist/index.d.ts +48 -0
  64. package/dist/index.js +50 -0
  65. package/dist/presentations/index.d.ts +4 -0
  66. package/dist/presentations/index.js +5 -0
  67. package/dist/run/index.d.ts +8 -0
  68. package/dist/run/index.js +9 -0
  69. package/dist/run/run.entity.d.ts +82 -0
  70. package/dist/run/run.entity.d.ts.map +1 -0
  71. package/dist/run/run.entity.js +205 -0
  72. package/dist/run/run.entity.js.map +1 -0
  73. package/dist/run/run.enum.d.ts +22 -0
  74. package/dist/run/run.enum.d.ts.map +1 -0
  75. package/dist/run/run.enum.js +45 -0
  76. package/dist/run/run.enum.js.map +1 -0
  77. package/dist/run/run.event.d.ts +290 -0
  78. package/dist/run/run.event.d.ts.map +1 -0
  79. package/dist/run/run.event.js +434 -0
  80. package/dist/run/run.event.js.map +1 -0
  81. package/dist/run/run.handler.d.ts +203 -0
  82. package/dist/run/run.handler.d.ts.map +1 -0
  83. package/dist/run/run.handler.js +83 -0
  84. package/dist/run/run.handler.js.map +1 -0
  85. package/dist/run/run.operation.d.ts +720 -0
  86. package/dist/run/run.operation.d.ts.map +1 -0
  87. package/dist/run/run.operation.js +626 -0
  88. package/dist/run/run.operation.js.map +1 -0
  89. package/dist/run/run.presentation.d.ts +14 -0
  90. package/dist/run/run.presentation.d.ts.map +1 -0
  91. package/dist/run/run.presentation.js +65 -0
  92. package/dist/run/run.presentation.js.map +1 -0
  93. package/dist/run/run.schema.d.ts +416 -0
  94. package/dist/run/run.schema.d.ts.map +1 -0
  95. package/dist/run/run.schema.js +338 -0
  96. package/dist/run/run.schema.js.map +1 -0
  97. package/dist/run/run.test-spec.d.ts +8 -0
  98. package/dist/run/run.test-spec.d.ts.map +1 -0
  99. package/dist/run/run.test-spec.js +65 -0
  100. package/dist/run/run.test-spec.js.map +1 -0
  101. package/dist/seeders/index.d.ts +10 -0
  102. package/dist/seeders/index.d.ts.map +1 -0
  103. package/dist/seeders/index.js +20 -0
  104. package/dist/seeders/index.js.map +1 -0
  105. package/dist/shared/index.d.ts +4 -0
  106. package/dist/shared/index.js +5 -0
  107. package/dist/shared/mock-agents.d.ts +88 -0
  108. package/dist/shared/mock-agents.d.ts.map +1 -0
  109. package/dist/shared/mock-agents.js +94 -0
  110. package/dist/shared/mock-agents.js.map +1 -0
  111. package/dist/shared/mock-runs.d.ts +120 -0
  112. package/dist/shared/mock-runs.d.ts.map +1 -0
  113. package/dist/shared/mock-runs.js +118 -0
  114. package/dist/shared/mock-runs.js.map +1 -0
  115. package/dist/shared/mock-tools.d.ts +244 -0
  116. package/dist/shared/mock-tools.d.ts.map +1 -0
  117. package/dist/shared/mock-tools.js +181 -0
  118. package/dist/shared/mock-tools.js.map +1 -0
  119. package/dist/shared/overlay-types.d.ts +34 -0
  120. package/dist/shared/overlay-types.d.ts.map +1 -0
  121. package/dist/shared/overlay-types.js +0 -0
  122. package/dist/tool/index.d.ts +8 -0
  123. package/dist/tool/index.js +9 -0
  124. package/dist/tool/tool.entity.d.ts +42 -0
  125. package/dist/tool/tool.entity.d.ts.map +1 -0
  126. package/dist/tool/tool.entity.js +105 -0
  127. package/dist/tool/tool.entity.js.map +1 -0
  128. package/dist/tool/tool.enum.d.ts +18 -0
  129. package/dist/tool/tool.enum.d.ts.map +1 -0
  130. package/dist/tool/tool.enum.js +35 -0
  131. package/dist/tool/tool.enum.js.map +1 -0
  132. package/dist/tool/tool.event.d.ts +103 -0
  133. package/dist/tool/tool.event.d.ts.map +1 -0
  134. package/dist/tool/tool.event.js +159 -0
  135. package/dist/tool/tool.event.js.map +1 -0
  136. package/dist/tool/tool.handler.d.ts +315 -0
  137. package/dist/tool/tool.handler.d.ts.map +1 -0
  138. package/dist/tool/tool.handler.js +87 -0
  139. package/dist/tool/tool.handler.js.map +1 -0
  140. package/dist/tool/tool.operation.d.ts +411 -0
  141. package/dist/tool/tool.operation.d.ts.map +1 -0
  142. package/dist/tool/tool.operation.js +406 -0
  143. package/dist/tool/tool.operation.js.map +1 -0
  144. package/dist/tool/tool.presentation.d.ts +14 -0
  145. package/dist/tool/tool.presentation.d.ts.map +1 -0
  146. package/dist/tool/tool.presentation.js +65 -0
  147. package/dist/tool/tool.presentation.js.map +1 -0
  148. package/dist/tool/tool.schema.d.ts +218 -0
  149. package/dist/tool/tool.schema.d.ts.map +1 -0
  150. package/dist/tool/tool.schema.js +236 -0
  151. package/dist/tool/tool.schema.js.map +1 -0
  152. package/dist/tool/tool.test-spec.d.ts +8 -0
  153. package/dist/tool/tool.test-spec.d.ts.map +1 -0
  154. package/dist/tool/tool.test-spec.js +65 -0
  155. package/dist/tool/tool.test-spec.js.map +1 -0
  156. package/dist/ui/AgentDashboard.d.ts +7 -0
  157. package/dist/ui/AgentDashboard.d.ts.map +1 -0
  158. package/dist/ui/AgentDashboard.js +420 -0
  159. package/dist/ui/AgentDashboard.js.map +1 -0
  160. package/dist/ui/AgentRunList.d.ts +2 -0
  161. package/dist/ui/AgentRunList.js +5 -0
  162. package/dist/ui/AgentToolRegistry.d.ts +2 -0
  163. package/dist/ui/AgentToolRegistry.js +5 -0
  164. package/dist/ui/hooks/index.d.ts +6 -0
  165. package/dist/ui/hooks/index.js +8 -0
  166. package/dist/ui/hooks/useAgentList.d.ts +28 -0
  167. package/dist/ui/hooks/useAgentList.d.ts.map +1 -0
  168. package/dist/ui/hooks/useAgentList.js +66 -0
  169. package/dist/ui/hooks/useAgentList.js.map +1 -0
  170. package/dist/ui/hooks/useAgentMutations.d.ts +29 -0
  171. package/dist/ui/hooks/useAgentMutations.d.ts.map +1 -0
  172. package/dist/ui/hooks/useAgentMutations.js +124 -0
  173. package/dist/ui/hooks/useAgentMutations.js.map +1 -0
  174. package/dist/ui/hooks/useRunList.d.ts +24 -0
  175. package/dist/ui/hooks/useRunList.d.ts.map +1 -0
  176. package/dist/ui/hooks/useRunList.js +66 -0
  177. package/dist/ui/hooks/useRunList.js.map +1 -0
  178. package/dist/ui/hooks/useToolList.d.ts +40 -0
  179. package/dist/ui/hooks/useToolList.d.ts.map +1 -0
  180. package/dist/ui/hooks/useToolList.js +96 -0
  181. package/dist/ui/hooks/useToolList.js.map +1 -0
  182. package/dist/ui/index.d.ts +24 -0
  183. package/dist/ui/index.js +24 -0
  184. package/dist/ui/modals/AgentActionsModal.d.ts +27 -0
  185. package/dist/ui/modals/AgentActionsModal.d.ts.map +1 -0
  186. package/dist/ui/modals/AgentActionsModal.js +262 -0
  187. package/dist/ui/modals/AgentActionsModal.js.map +1 -0
  188. package/dist/ui/modals/CreateAgentModal.d.ts +25 -0
  189. package/dist/ui/modals/CreateAgentModal.d.ts.map +1 -0
  190. package/dist/ui/modals/CreateAgentModal.js +214 -0
  191. package/dist/ui/modals/CreateAgentModal.js.map +1 -0
  192. package/dist/ui/modals/index.d.ts +3 -0
  193. package/dist/ui/modals/index.js +4 -0
  194. package/dist/ui/overlays/demo-overlays.d.ts +19 -0
  195. package/dist/ui/overlays/demo-overlays.d.ts.map +1 -0
  196. package/dist/ui/overlays/demo-overlays.js +73 -0
  197. package/dist/ui/overlays/demo-overlays.js.map +1 -0
  198. package/dist/ui/overlays/index.d.ts +2 -0
  199. package/dist/ui/overlays/index.js +3 -0
  200. package/dist/ui/renderers/agent-list.markdown.d.ts +15 -0
  201. package/dist/ui/renderers/agent-list.markdown.d.ts.map +1 -0
  202. package/dist/ui/renderers/agent-list.markdown.js +51 -0
  203. package/dist/ui/renderers/agent-list.markdown.js.map +1 -0
  204. package/dist/ui/renderers/agent-list.renderer.d.ts +11 -0
  205. package/dist/ui/renderers/agent-list.renderer.d.ts.map +1 -0
  206. package/dist/ui/renderers/agent-list.renderer.js +19 -0
  207. package/dist/ui/renderers/agent-list.renderer.js.map +1 -0
  208. package/dist/ui/renderers/dashboard.markdown.d.ts +15 -0
  209. package/dist/ui/renderers/dashboard.markdown.d.ts.map +1 -0
  210. package/dist/ui/renderers/dashboard.markdown.js +100 -0
  211. package/dist/ui/renderers/dashboard.markdown.js.map +1 -0
  212. package/dist/ui/renderers/index.d.ts +6 -0
  213. package/dist/ui/renderers/index.js +7 -0
  214. package/dist/ui/renderers/run-list.markdown.d.ts +15 -0
  215. package/dist/ui/renderers/run-list.markdown.d.ts.map +1 -0
  216. package/dist/ui/renderers/run-list.markdown.js +44 -0
  217. package/dist/ui/renderers/run-list.markdown.js.map +1 -0
  218. package/dist/ui/renderers/tool-registry.markdown.d.ts +15 -0
  219. package/dist/ui/renderers/tool-registry.markdown.d.ts.map +1 -0
  220. package/dist/ui/renderers/tool-registry.markdown.js +55 -0
  221. package/dist/ui/renderers/tool-registry.markdown.js.map +1 -0
  222. package/dist/ui/views/AgentListView.d.ts +7 -0
  223. package/dist/ui/views/AgentListView.d.ts.map +1 -0
  224. package/dist/ui/views/AgentListView.js +93 -0
  225. package/dist/ui/views/AgentListView.js.map +1 -0
  226. package/dist/ui/views/RunListView.d.ts +14 -0
  227. package/dist/ui/views/RunListView.d.ts.map +1 -0
  228. package/dist/ui/views/RunListView.js +165 -0
  229. package/dist/ui/views/RunListView.js.map +1 -0
  230. package/dist/ui/views/ToolRegistryView.d.ts +14 -0
  231. package/dist/ui/views/ToolRegistryView.d.ts.map +1 -0
  232. package/dist/ui/views/ToolRegistryView.js +97 -0
  233. package/dist/ui/views/ToolRegistryView.js.map +1 -0
  234. package/dist/ui/views/index.d.ts +4 -0
  235. package/dist/ui/views/index.js +5 -0
  236. package/example.ts +1 -0
  237. package/package.json +155 -0
  238. package/src/agent/agent.entity.ts +137 -0
  239. package/src/agent/agent.enum.ts +31 -0
  240. package/src/agent/agent.event.ts +142 -0
  241. package/src/agent/agent.handler.ts +178 -0
  242. package/src/agent/agent.operation.ts +444 -0
  243. package/src/agent/agent.presentation.ts +80 -0
  244. package/src/agent/agent.schema.ts +214 -0
  245. package/src/agent/agent.test-spec.ts +55 -0
  246. package/src/agent/index.ts +67 -0
  247. package/src/agent.capability.ts +13 -0
  248. package/src/agent.feature.ts +147 -0
  249. package/src/docs/agent-console.docblock.ts +97 -0
  250. package/src/docs/index.ts +1 -0
  251. package/src/example.ts +41 -0
  252. package/src/handlers/agent.handlers.ts +572 -0
  253. package/src/handlers/index.ts +30 -0
  254. package/src/index.ts +32 -0
  255. package/src/presentations/index.ts +26 -0
  256. package/src/run/index.ts +68 -0
  257. package/src/run/run.entity.ts +175 -0
  258. package/src/run/run.enum.ts +43 -0
  259. package/src/run/run.event.ts +264 -0
  260. package/src/run/run.handler.ts +138 -0
  261. package/src/run/run.operation.ts +524 -0
  262. package/src/run/run.presentation.ts +54 -0
  263. package/src/run/run.schema.ts +169 -0
  264. package/src/run/run.test-spec.ts +55 -0
  265. package/src/seeders/index.ts +29 -0
  266. package/src/shared/index.ts +6 -0
  267. package/src/shared/mock-agents.ts +81 -0
  268. package/src/shared/mock-runs.ts +107 -0
  269. package/src/shared/mock-tools.ts +145 -0
  270. package/src/shared/overlay-types.ts +39 -0
  271. package/src/tool/index.ts +60 -0
  272. package/src/tool/tool.entity.ts +99 -0
  273. package/src/tool/tool.enum.ts +32 -0
  274. package/src/tool/tool.event.ts +119 -0
  275. package/src/tool/tool.handler.ts +154 -0
  276. package/src/tool/tool.operation.ts +366 -0
  277. package/src/tool/tool.presentation.ts +55 -0
  278. package/src/tool/tool.schema.ts +133 -0
  279. package/src/tool/tool.test-spec.ts +55 -0
  280. package/src/ui/AgentDashboard.tsx +416 -0
  281. package/src/ui/AgentRunList.tsx +8 -0
  282. package/src/ui/AgentToolRegistry.tsx +8 -0
  283. package/src/ui/hooks/index.ts +14 -0
  284. package/src/ui/hooks/useAgentList.ts +80 -0
  285. package/src/ui/hooks/useAgentMutations.ts +156 -0
  286. package/src/ui/hooks/useRunList.ts +81 -0
  287. package/src/ui/hooks/useToolList.ts +122 -0
  288. package/src/ui/index.ts +21 -0
  289. package/src/ui/modals/AgentActionsModal.tsx +306 -0
  290. package/src/ui/modals/CreateAgentModal.tsx +257 -0
  291. package/src/ui/modals/index.ts +2 -0
  292. package/src/ui/overlays/demo-overlays.ts +77 -0
  293. package/src/ui/overlays/index.ts +1 -0
  294. package/src/ui/renderers/agent-list.markdown.ts +84 -0
  295. package/src/ui/renderers/agent-list.renderer.tsx +27 -0
  296. package/src/ui/renderers/dashboard.markdown.ts +169 -0
  297. package/src/ui/renderers/index.ts +12 -0
  298. package/src/ui/renderers/run-list.markdown.ts +75 -0
  299. package/src/ui/renderers/tool-registry.markdown.ts +91 -0
  300. package/src/ui/views/AgentListView.tsx +113 -0
  301. package/src/ui/views/RunListView.tsx +173 -0
  302. package/src/ui/views/ToolRegistryView.tsx +140 -0
  303. package/src/ui/views/index.ts +6 -0
  304. package/tsconfig.json +10 -0
  305. package/tsconfig.tsbuildinfo +1 -0
  306. package/tsdown.config.js +7 -0
@@ -0,0 +1,416 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Agent Console Dashboard
5
+ *
6
+ * Fully integrated with ContractSpec example handlers,
7
+ * design-system components, and command mutations.
8
+ *
9
+ * Commands wired:
10
+ * - CreateAgentCommand -> Create Agent button + modal
11
+ * - UpdateAgentCommand -> Status changes via modal
12
+ * - ExecuteAgentCommand -> Execute agent via modal
13
+ */
14
+ import { useState, useMemo, useCallback } from 'react';
15
+ import {
16
+ StatCard,
17
+ StatCardGroup,
18
+ Button,
19
+ } from '@contractspec/lib.design-system';
20
+ // import { AgentListView } from './views/AgentListView';
21
+ import { RunListView } from './views/RunListView';
22
+ import { ToolRegistryView } from './views/ToolRegistryView';
23
+ import { useRunList, type RunMetrics } from './hooks/useRunList';
24
+ import { useAgentList, type Agent } from './hooks/useAgentList';
25
+ import { useAgentMutations } from './hooks/useAgentMutations';
26
+ import { CreateAgentModal } from './modals/CreateAgentModal';
27
+ import { AgentActionsModal } from './modals/AgentActionsModal';
28
+
29
+ type Tab = 'runs' | 'agents' | 'tools' | 'metrics';
30
+
31
+ export function AgentDashboard() {
32
+ const [activeTab, setActiveTab] = useState<Tab>('runs');
33
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
34
+ const [selectedAgent, setSelectedAgent] = useState<Agent | null>(null);
35
+ const [isAgentActionsOpen, setIsAgentActionsOpen] = useState(false);
36
+
37
+ const { metrics, refetch: refetchRuns } = useRunList();
38
+ const { refetch: refetchAgents } = useAgentList();
39
+
40
+ const mutations = useAgentMutations({
41
+ onSuccess: () => {
42
+ refetchAgents();
43
+ refetchRuns();
44
+ },
45
+ });
46
+
47
+ const handleAgentClick = useCallback((agent: Agent) => {
48
+ setSelectedAgent(agent);
49
+ setIsAgentActionsOpen(true);
50
+ }, []);
51
+
52
+ const tabs: { id: Tab; label: string; icon: string }[] = [
53
+ { id: 'runs', label: 'Runs', icon: '▶' },
54
+ { id: 'agents', label: 'Agents', icon: '🤖' },
55
+ { id: 'tools', label: 'Tools', icon: '🔧' },
56
+ { id: 'metrics', label: 'Metrics', icon: '📊' },
57
+ ];
58
+
59
+ // Compute summary stats from metrics
60
+ const summaryStats = useMemo(() => {
61
+ if (!metrics) {
62
+ return [
63
+ { label: 'Total Runs', value: '-', hint: 'Loading...' },
64
+ { label: 'Success Rate', value: '-', hint: '' },
65
+ { label: 'Total Tokens', value: '-', hint: '' },
66
+ { label: 'Total Cost', value: '-', hint: '' },
67
+ ];
68
+ }
69
+ return [
70
+ {
71
+ label: 'Total Runs',
72
+ value: metrics.totalRuns.toLocaleString(),
73
+ hint: `${(metrics.successRate * 100).toFixed(0)}% success`,
74
+ },
75
+ {
76
+ label: 'Success Rate',
77
+ value: `${(metrics.successRate * 100).toFixed(0)}%`,
78
+ hint: 'of all runs',
79
+ },
80
+ {
81
+ label: 'Total Tokens',
82
+ value:
83
+ metrics.totalTokens >= 1000000
84
+ ? `${(metrics.totalTokens / 1000000).toFixed(1)}M`
85
+ : `${(metrics.totalTokens / 1000).toFixed(0)}K`,
86
+ hint: 'This period',
87
+ },
88
+ {
89
+ label: 'Total Cost',
90
+ value: `$${metrics.totalCostUsd.toFixed(2)}`,
91
+ hint: 'This period',
92
+ },
93
+ ];
94
+ }, [metrics]);
95
+
96
+ return (
97
+ <div className="space-y-6">
98
+ {/* Header */}
99
+ <div className="flex items-center justify-between">
100
+ <h2 className="text-2xl font-bold">AI Agent Console</h2>
101
+ <Button onPress={() => setIsCreateModalOpen(true)}>
102
+ <span className="mr-2">+</span> New Agent
103
+ </Button>
104
+ </div>
105
+
106
+ {/* Summary Stats Row */}
107
+ <StatCardGroup>
108
+ {summaryStats.map((stat, i) => (
109
+ <StatCard
110
+ key={i}
111
+ label={stat.label}
112
+ value={stat.value}
113
+ hint={stat.hint}
114
+ />
115
+ ))}
116
+ </StatCardGroup>
117
+
118
+ {/* Navigation Tabs */}
119
+ <nav className="bg-muted flex gap-1 rounded-lg p-1" role="tablist">
120
+ {tabs.map((tab) => (
121
+ <button
122
+ key={tab.id}
123
+ type="button"
124
+ role="tab"
125
+ aria-selected={activeTab === tab.id}
126
+ onClick={() => setActiveTab(tab.id)}
127
+ className={`flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors ${
128
+ activeTab === tab.id
129
+ ? 'bg-background text-foreground shadow-sm'
130
+ : 'text-muted-foreground hover:text-foreground'
131
+ }`}
132
+ >
133
+ <span>{tab.icon}</span>
134
+ {tab.label}
135
+ </button>
136
+ ))}
137
+ </nav>
138
+
139
+ {/* Tab Content */}
140
+ <div className="min-h-[400px]" role="tabpanel">
141
+ {activeTab === 'runs' && <RunListView />}
142
+ {activeTab === 'agents' && (
143
+ <AgentListViewWithActions onAgentClick={handleAgentClick} />
144
+ )}
145
+ {activeTab === 'tools' && <ToolRegistryView />}
146
+ {activeTab === 'metrics' && <MetricsView metrics={metrics} />}
147
+ </div>
148
+
149
+ {/* Create Agent Modal */}
150
+ <CreateAgentModal
151
+ isOpen={isCreateModalOpen}
152
+ onClose={() => setIsCreateModalOpen(false)}
153
+ onSubmit={async (input) => {
154
+ await mutations.createAgent(input);
155
+ }}
156
+ isLoading={mutations.createState.loading}
157
+ />
158
+
159
+ {/* Agent Actions Modal */}
160
+ <AgentActionsModal
161
+ isOpen={isAgentActionsOpen}
162
+ agent={selectedAgent}
163
+ onClose={() => {
164
+ setIsAgentActionsOpen(false);
165
+ setSelectedAgent(null);
166
+ }}
167
+ onActivate={async (agentId) => {
168
+ await mutations.activateAgent(agentId);
169
+ }}
170
+ onPause={async (agentId) => {
171
+ await mutations.pauseAgent(agentId);
172
+ }}
173
+ onArchive={async (agentId) => {
174
+ await mutations.archiveAgent(agentId);
175
+ }}
176
+ onExecute={async (agentId, message) => {
177
+ await mutations.executeAgent({ agentId, message });
178
+ }}
179
+ isLoading={mutations.isLoading}
180
+ />
181
+ </div>
182
+ );
183
+ }
184
+
185
+ /**
186
+ * Agent List View with click handler
187
+ */
188
+ function AgentListViewWithActions({
189
+ onAgentClick,
190
+ }: {
191
+ onAgentClick: (agent: Agent) => void;
192
+ }) {
193
+ const { data, loading, error, stats, refetch } = useAgentList();
194
+
195
+ if (loading && !data) {
196
+ return (
197
+ <div className="text-muted-foreground flex h-64 items-center justify-center">
198
+ Loading agents...
199
+ </div>
200
+ );
201
+ }
202
+
203
+ if (error) {
204
+ return (
205
+ <div className="text-destructive flex h-64 flex-col items-center justify-center">
206
+ <p>Failed to load agents: {error.message}</p>
207
+ <Button variant="outline" onPress={refetch} className="mt-2">
208
+ Retry
209
+ </Button>
210
+ </div>
211
+ );
212
+ }
213
+
214
+ if (!data?.items.length) {
215
+ return (
216
+ <div className="text-muted-foreground flex h-64 flex-col items-center justify-center">
217
+ <p className="text-lg font-medium">No agents yet</p>
218
+ <p className="text-sm">Create your first AI agent to get started.</p>
219
+ </div>
220
+ );
221
+ }
222
+
223
+ return (
224
+ <div className="space-y-4">
225
+ {/* Stats */}
226
+ {stats && (
227
+ <div className="flex gap-4 text-sm">
228
+ <span>Total: {stats.total}</span>
229
+ <span className="text-green-600">Active: {stats.active}</span>
230
+ <span className="text-yellow-600">Paused: {stats.paused}</span>
231
+ <span className="text-blue-600">Draft: {stats.draft}</span>
232
+ </div>
233
+ )}
234
+
235
+ {/* Agent Grid */}
236
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
237
+ {data.items.map((agent) => (
238
+ <AgentCard
239
+ key={agent.id}
240
+ agent={agent}
241
+ onClick={() => onAgentClick(agent)}
242
+ />
243
+ ))}
244
+ </div>
245
+ </div>
246
+ );
247
+ }
248
+
249
+ /**
250
+ * Agent Card Component
251
+ */
252
+ function AgentCard({ agent, onClick }: { agent: Agent; onClick: () => void }) {
253
+ const statusColors: Record<string, string> = {
254
+ ACTIVE:
255
+ 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400',
256
+ DRAFT: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400',
257
+ PAUSED:
258
+ 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400',
259
+ ARCHIVED: 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400',
260
+ };
261
+
262
+ return (
263
+ <div
264
+ onClick={onClick}
265
+ className="border-border bg-card cursor-pointer rounded-xl border p-4 transition-all hover:shadow-md"
266
+ role="button"
267
+ tabIndex={0}
268
+ onKeyDown={(e) => {
269
+ if (e.key === 'Enter' || e.key === ' ') onClick();
270
+ }}
271
+ >
272
+ <div className="flex items-start justify-between">
273
+ <div className="min-w-0 flex-1">
274
+ <h3 className="truncate font-semibold">{agent.name}</h3>
275
+ <p className="text-muted-foreground text-sm">
276
+ {agent.modelProvider} / {agent.modelName}
277
+ </p>
278
+ </div>
279
+ <span
280
+ className={`rounded-full px-2 py-0.5 text-xs font-medium ${statusColors[agent.status]}`}
281
+ >
282
+ {agent.status}
283
+ </span>
284
+ </div>
285
+ {agent.description && (
286
+ <p className="text-muted-foreground mt-2 line-clamp-2 text-sm">
287
+ {agent.description}
288
+ </p>
289
+ )}
290
+ <div className="mt-3 flex items-center justify-between">
291
+ <span className="text-muted-foreground text-xs">{agent.modelName}</span>
292
+ <Button variant="ghost" size="sm" onPress={onClick}>
293
+ Actions
294
+ </Button>
295
+ </div>
296
+ </div>
297
+ );
298
+ }
299
+
300
+ /**
301
+ * Metrics View - Shows usage analytics
302
+ */
303
+ function MetricsView({ metrics }: { metrics: RunMetrics | null }) {
304
+ if (!metrics) {
305
+ return (
306
+ <div className="text-muted-foreground flex h-64 items-center justify-center">
307
+ Loading metrics...
308
+ </div>
309
+ );
310
+ }
311
+
312
+ // Calculate derived metrics
313
+ const completedRuns = Math.round(metrics.totalRuns * metrics.successRate);
314
+ const failedRuns = metrics.totalRuns - completedRuns;
315
+
316
+ return (
317
+ <div className="space-y-6">
318
+ <h3 className="text-lg font-semibold">Usage Analytics</h3>
319
+
320
+ <div className="grid gap-6 md:grid-cols-2">
321
+ {/* Success/Failure breakdown */}
322
+ <div className="border-border bg-card rounded-xl border p-4">
323
+ <h4 className="font-medium">Run Outcomes</h4>
324
+ <div className="mt-4 space-y-3">
325
+ <ProgressBar
326
+ label="Completed"
327
+ value={completedRuns}
328
+ total={metrics.totalRuns}
329
+ color="bg-green-500"
330
+ />
331
+ <ProgressBar
332
+ label="Failed"
333
+ value={failedRuns}
334
+ total={metrics.totalRuns}
335
+ color="bg-red-500"
336
+ />
337
+ </div>
338
+ </div>
339
+
340
+ {/* Key Stats */}
341
+ <div className="border-border bg-card rounded-xl border p-4">
342
+ <h4 className="font-medium">Performance</h4>
343
+ <dl className="mt-4 grid grid-cols-2 gap-4">
344
+ <div>
345
+ <dt className="text-muted-foreground text-sm">Avg Duration</dt>
346
+ <dd className="text-xl font-semibold">
347
+ {(metrics.averageDurationMs / 1000).toFixed(1)}s
348
+ </dd>
349
+ </div>
350
+ <div>
351
+ <dt className="text-muted-foreground text-sm">Success Rate</dt>
352
+ <dd className="text-xl font-semibold">
353
+ {(metrics.successRate * 100).toFixed(0)}%
354
+ </dd>
355
+ </div>
356
+ </dl>
357
+ </div>
358
+ </div>
359
+
360
+ {/* Key Metrics */}
361
+ <div className="border-border bg-card rounded-xl border p-4">
362
+ <h4 className="font-medium">Key Metrics</h4>
363
+ <dl className="mt-4 grid gap-4 sm:grid-cols-3">
364
+ <div>
365
+ <dt className="text-muted-foreground text-sm">Total Runs</dt>
366
+ <dd className="text-2xl font-semibold">
367
+ {metrics.totalRuns.toLocaleString()}
368
+ </dd>
369
+ </div>
370
+ <div>
371
+ <dt className="text-muted-foreground text-sm">Total Tokens</dt>
372
+ <dd className="text-2xl font-semibold">
373
+ {(metrics.totalTokens / 1000).toFixed(0)}K
374
+ </dd>
375
+ </div>
376
+ <div>
377
+ <dt className="text-muted-foreground text-sm">Cost per Run</dt>
378
+ <dd className="text-2xl font-semibold">
379
+ $
380
+ {metrics.totalRuns > 0
381
+ ? (metrics.totalCostUsd / metrics.totalRuns).toFixed(4)
382
+ : '0'}
383
+ </dd>
384
+ </div>
385
+ </dl>
386
+ </div>
387
+ </div>
388
+ );
389
+ }
390
+
391
+ function ProgressBar({
392
+ label,
393
+ value,
394
+ total,
395
+ color,
396
+ }: {
397
+ label: string;
398
+ value: number;
399
+ total: number;
400
+ color: string;
401
+ }) {
402
+ const pct = total > 0 ? (value / total) * 100 : 0;
403
+ return (
404
+ <div>
405
+ <div className="flex justify-between text-sm">
406
+ <span>{label}</span>
407
+ <span className="text-muted-foreground">
408
+ {value} ({pct.toFixed(0)}%)
409
+ </span>
410
+ </div>
411
+ <div className="bg-muted mt-1 h-2 overflow-hidden rounded-full">
412
+ <div className={`h-full ${color}`} style={{ width: `${pct}%` }} />
413
+ </div>
414
+ </div>
415
+ );
416
+ }
@@ -0,0 +1,8 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Agent Run List - Standalone component for run history
5
+ *
6
+ * Re-exports RunListView for backward compatibility
7
+ */
8
+ export { RunListView as AgentRunList } from './views/RunListView';
@@ -0,0 +1,8 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Agent Tool Registry - Standalone component for tool management
5
+ *
6
+ * Re-exports ToolRegistryView for backward compatibility
7
+ */
8
+ export { ToolRegistryView as AgentToolRegistry } from './views/ToolRegistryView';
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Data hooks for agent-console template
5
+ */
6
+ export { useAgentList, type UseAgentListOptions } from './useAgentList';
7
+ export { useRunList, type UseRunListOptions } from './useRunList';
8
+ export { useToolList, type UseToolListOptions } from './useToolList';
9
+ export {
10
+ useAgentMutations,
11
+ type UseAgentMutationsOptions,
12
+ type CreateAgentInput,
13
+ type UpdateAgentInput,
14
+ } from './useAgentMutations';
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Hook for fetching and managing agent list data
3
+ *
4
+ * Uses runtime-local database-backed handlers.
5
+ */
6
+ import { useCallback, useEffect, useMemo, useState } from 'react';
7
+ import { useTemplateRuntime } from '@contractspec/lib.example-shared-ui';
8
+ import type {
9
+ Agent as RuntimeAgent,
10
+ ListAgentsOutput as RuntimeListAgentsOutput,
11
+ } from '../../handlers/agent.handlers';
12
+ import type { AgentHandlers } from '../../handlers/agent.handlers';
13
+
14
+ // Re-export types for convenience
15
+ export type Agent = RuntimeAgent;
16
+ export type ListAgentsOutput = RuntimeListAgentsOutput;
17
+
18
+ export interface UseAgentListOptions {
19
+ search?: string;
20
+ status?: 'DRAFT' | 'ACTIVE' | 'PAUSED' | 'ARCHIVED' | 'all';
21
+ limit?: number;
22
+ }
23
+
24
+ export function useAgentList(options: UseAgentListOptions = {}) {
25
+ const { handlers, projectId } = useTemplateRuntime<{
26
+ agent: AgentHandlers;
27
+ }>();
28
+ const { agent } = handlers;
29
+
30
+ const [data, setData] = useState<ListAgentsOutput | null>(null);
31
+ const [loading, setLoading] = useState(true);
32
+ const [error, setError] = useState<Error | null>(null);
33
+ const [page, setPage] = useState(1);
34
+
35
+ const fetchData = useCallback(async () => {
36
+ setLoading(true);
37
+ setError(null);
38
+
39
+ try {
40
+ const result = await agent.listAgents({
41
+ projectId,
42
+ search: options.search,
43
+ status: options.status === 'all' ? undefined : options.status,
44
+ limit: options.limit ?? 20,
45
+ offset: (page - 1) * (options.limit ?? 20),
46
+ });
47
+ setData(result);
48
+ } catch (err) {
49
+ setError(err instanceof Error ? err : new Error('Unknown error'));
50
+ } finally {
51
+ setLoading(false);
52
+ }
53
+ }, [agent, projectId, options.search, options.status, options.limit, page]);
54
+
55
+ useEffect(() => {
56
+ fetchData();
57
+ }, [fetchData]);
58
+
59
+ // Calculate stats
60
+ const stats = useMemo(() => {
61
+ if (!data) return null;
62
+ return {
63
+ total: data.total,
64
+ active: data.items.filter((a) => a.status === 'ACTIVE').length,
65
+ paused: data.items.filter((a) => a.status === 'PAUSED').length,
66
+ draft: data.items.filter((a) => a.status === 'DRAFT').length,
67
+ };
68
+ }, [data]);
69
+
70
+ return {
71
+ data,
72
+ loading,
73
+ error,
74
+ stats,
75
+ page,
76
+ refetch: fetchData,
77
+ nextPage: () => setPage((p) => p + 1),
78
+ prevPage: () => page > 1 && setPage((p) => p - 1),
79
+ };
80
+ }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Hook for Agent Console mutations (commands)
3
+ *
4
+ * Uses runtime-local database-backed handlers for:
5
+ * - CreateAgentCommand
6
+ * - UpdateAgentCommand
7
+ */
8
+ import { useCallback, useState } from 'react';
9
+ import { useTemplateRuntime } from '@contractspec/lib.example-shared-ui';
10
+ import type {
11
+ Agent,
12
+ CreateAgentInput,
13
+ UpdateAgentInput,
14
+ AgentHandlers,
15
+ } from '../../handlers/agent.handlers';
16
+
17
+ export interface MutationState<T> {
18
+ loading: boolean;
19
+ error: Error | null;
20
+ data: T | null;
21
+ }
22
+
23
+ export interface UseAgentMutationsOptions {
24
+ onSuccess?: () => void;
25
+ onError?: (error: Error) => void;
26
+ }
27
+
28
+ export function useAgentMutations(options: UseAgentMutationsOptions = {}) {
29
+ const { handlers, projectId } = useTemplateRuntime<{
30
+ agent: AgentHandlers;
31
+ }>();
32
+ const { agent } = handlers;
33
+
34
+ const [createState, setCreateState] = useState<MutationState<Agent>>({
35
+ loading: false,
36
+ error: null,
37
+ data: null,
38
+ });
39
+
40
+ const [updateState, setUpdateState] = useState<MutationState<Agent>>({
41
+ loading: false,
42
+ error: null,
43
+ data: null,
44
+ });
45
+
46
+ /**
47
+ * Create a new agent
48
+ */
49
+ const createAgent = useCallback(
50
+ async (input: CreateAgentInput): Promise<Agent | null> => {
51
+ setCreateState({ loading: true, error: null, data: null });
52
+ try {
53
+ const result = await agent.createAgent(input, {
54
+ projectId,
55
+ organizationId: 'demo-org',
56
+ });
57
+ setCreateState({ loading: false, error: null, data: result });
58
+ options.onSuccess?.();
59
+ return result;
60
+ } catch (err) {
61
+ const error =
62
+ err instanceof Error ? err : new Error('Failed to create agent');
63
+ setCreateState({ loading: false, error, data: null });
64
+ options.onError?.(error);
65
+ return null;
66
+ }
67
+ },
68
+ [agent, projectId, options]
69
+ );
70
+
71
+ /**
72
+ * Update an agent (name, status)
73
+ */
74
+ const updateAgent = useCallback(
75
+ async (input: UpdateAgentInput): Promise<Agent | null> => {
76
+ setUpdateState({ loading: true, error: null, data: null });
77
+ try {
78
+ const result = await agent.updateAgent(input);
79
+ setUpdateState({ loading: false, error: null, data: result });
80
+ options.onSuccess?.();
81
+ return result;
82
+ } catch (err) {
83
+ const error =
84
+ err instanceof Error ? err : new Error('Failed to update agent');
85
+ setUpdateState({ loading: false, error, data: null });
86
+ options.onError?.(error);
87
+ return null;
88
+ }
89
+ },
90
+ [agent, options]
91
+ );
92
+
93
+ /**
94
+ * Activate an agent
95
+ */
96
+ const activateAgent = useCallback(
97
+ async (agentId: string): Promise<Agent | null> => {
98
+ return updateAgent({ id: agentId, status: 'ACTIVE' });
99
+ },
100
+ [updateAgent]
101
+ );
102
+
103
+ /**
104
+ * Pause an agent
105
+ */
106
+ const pauseAgent = useCallback(
107
+ async (agentId: string): Promise<Agent | null> => {
108
+ return updateAgent({ id: agentId, status: 'PAUSED' });
109
+ },
110
+ [updateAgent]
111
+ );
112
+
113
+ /**
114
+ * Archive an agent
115
+ */
116
+ const archiveAgent = useCallback(
117
+ async (agentId: string): Promise<Agent | null> => {
118
+ return updateAgent({ id: agentId, status: 'ARCHIVED' });
119
+ },
120
+ [updateAgent]
121
+ );
122
+
123
+ /**
124
+ * Execute an agent (placeholder - needs run handler)
125
+ * Note: Execute functionality requires adding createRun/executeRun to agent handlers
126
+ */
127
+ const executeAgent = useCallback(
128
+ async (input: { agentId: string; message: string }): Promise<null> => {
129
+ // TODO: Implement execute when run creation handler is added to runtime-local
130
+ console.log('Execute agent:', input);
131
+ options.onSuccess?.();
132
+ return null;
133
+ },
134
+ [options]
135
+ );
136
+
137
+ return {
138
+ // Mutations
139
+ createAgent,
140
+ updateAgent,
141
+ activateAgent,
142
+ pauseAgent,
143
+ archiveAgent,
144
+ executeAgent,
145
+
146
+ // State
147
+ createState,
148
+ updateState,
149
+
150
+ // Convenience
151
+ isLoading: createState.loading || updateState.loading,
152
+ };
153
+ }
154
+
155
+ // Re-export types for convenience
156
+ export type { CreateAgentInput, UpdateAgentInput, Agent };