@contractspec/example.agent-console 1.46.0 → 1.47.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 (204) hide show
  1. package/.turbo/turbo-build$colon$bundle.log +275 -128
  2. package/.turbo/turbo-build.log +274 -127
  3. package/CHANGELOG.md +46 -0
  4. package/dist/agent/agent.entity.d.ts +36 -36
  5. package/dist/agent/agent.entity.d.ts.map +1 -1
  6. package/dist/agent/agent.enum.d.ts +4 -4
  7. package/dist/agent/agent.enum.d.ts.map +1 -1
  8. package/dist/agent/agent.event.d.ts +31 -31
  9. package/dist/agent/agent.event.d.ts.map +1 -1
  10. package/dist/agent/agent.event.js +5 -5
  11. package/dist/agent/agent.event.js.map +1 -1
  12. package/dist/agent/agent.handler.js.map +1 -1
  13. package/dist/agent/agent.operation.d.ts +117 -117
  14. package/dist/agent/agent.operation.d.ts.map +1 -1
  15. package/dist/agent/agent.presentation.d.ts +4 -5
  16. package/dist/agent/agent.presentation.d.ts.map +1 -1
  17. package/dist/agent/agent.presentation.js +7 -7
  18. package/dist/agent/agent.presentation.js.map +1 -1
  19. package/dist/agent/agent.schema.d.ts +95 -95
  20. package/dist/agent/agent.schema.d.ts.map +1 -1
  21. package/dist/agent/agent.test-spec.d.ts +8 -0
  22. package/dist/agent/agent.test-spec.d.ts.map +1 -0
  23. package/dist/agent/agent.test-spec.js +65 -0
  24. package/dist/agent/agent.test-spec.js.map +1 -0
  25. package/dist/agent.capability.d.ts +7 -0
  26. package/dist/agent.capability.d.ts.map +1 -0
  27. package/dist/agent.capability.js +20 -0
  28. package/dist/agent.capability.js.map +1 -0
  29. package/dist/agent.feature.d.ts.map +1 -1
  30. package/dist/agent.feature.js +4 -2
  31. package/dist/agent.feature.js.map +1 -1
  32. package/dist/example.d.ts +2 -2
  33. package/dist/example.d.ts.map +1 -1
  34. package/dist/example.js +4 -2
  35. package/dist/example.js.map +1 -1
  36. package/dist/handlers/agent.handlers.d.ts +135 -0
  37. package/dist/handlers/agent.handlers.d.ts.map +1 -0
  38. package/dist/handlers/agent.handlers.js +263 -0
  39. package/dist/handlers/agent.handlers.js.map +1 -0
  40. package/dist/handlers/index.d.ts +2 -1
  41. package/dist/handlers/index.js +2 -1
  42. package/dist/index.d.ts +19 -1
  43. package/dist/index.js +19 -1
  44. package/dist/run/run.entity.d.ts +56 -56
  45. package/dist/run/run.enum.d.ts +5 -5
  46. package/dist/run/run.event.d.ts +71 -71
  47. package/dist/run/run.event.d.ts.map +1 -1
  48. package/dist/run/run.event.js +8 -8
  49. package/dist/run/run.event.js.map +1 -1
  50. package/dist/run/run.operation.d.ts +175 -175
  51. package/dist/run/run.operation.d.ts.map +1 -1
  52. package/dist/run/run.presentation.d.ts +3 -4
  53. package/dist/run/run.presentation.d.ts.map +1 -1
  54. package/dist/run/run.presentation.js +5 -5
  55. package/dist/run/run.presentation.js.map +1 -1
  56. package/dist/run/run.schema.d.ts +99 -99
  57. package/dist/run/run.test-spec.d.ts +8 -0
  58. package/dist/run/run.test-spec.d.ts.map +1 -0
  59. package/dist/run/run.test-spec.js +65 -0
  60. package/dist/run/run.test-spec.js.map +1 -0
  61. package/dist/seeders/index.d.ts +10 -0
  62. package/dist/seeders/index.d.ts.map +1 -0
  63. package/dist/seeders/index.js +20 -0
  64. package/dist/seeders/index.js.map +1 -0
  65. package/dist/shared/overlay-types.d.ts +34 -0
  66. package/dist/shared/overlay-types.d.ts.map +1 -0
  67. package/dist/shared/overlay-types.js +0 -0
  68. package/dist/tool/tool.entity.d.ts +24 -24
  69. package/dist/tool/tool.enum.d.ts +4 -4
  70. package/dist/tool/tool.event.d.ts +25 -25
  71. package/dist/tool/tool.event.js +4 -4
  72. package/dist/tool/tool.event.js.map +1 -1
  73. package/dist/tool/tool.handler.d.ts.map +1 -1
  74. package/dist/tool/tool.operation.d.ts +101 -101
  75. package/dist/tool/tool.presentation.d.ts +3 -4
  76. package/dist/tool/tool.presentation.d.ts.map +1 -1
  77. package/dist/tool/tool.presentation.js +5 -5
  78. package/dist/tool/tool.presentation.js.map +1 -1
  79. package/dist/tool/tool.schema.d.ts +52 -52
  80. package/dist/tool/tool.schema.d.ts.map +1 -1
  81. package/dist/tool/tool.test-spec.d.ts +8 -0
  82. package/dist/tool/tool.test-spec.d.ts.map +1 -0
  83. package/dist/tool/tool.test-spec.js +65 -0
  84. package/dist/tool/tool.test-spec.js.map +1 -0
  85. package/dist/ui/AgentDashboard.d.ts +7 -0
  86. package/dist/ui/AgentDashboard.d.ts.map +1 -0
  87. package/dist/ui/AgentDashboard.js +420 -0
  88. package/dist/ui/AgentDashboard.js.map +1 -0
  89. package/dist/ui/AgentRunList.d.ts +2 -0
  90. package/dist/ui/AgentRunList.js +5 -0
  91. package/dist/ui/AgentToolRegistry.d.ts +2 -0
  92. package/dist/ui/AgentToolRegistry.js +5 -0
  93. package/dist/ui/hooks/index.d.ts +6 -0
  94. package/dist/ui/hooks/index.js +8 -0
  95. package/dist/ui/hooks/useAgentList.d.ts +28 -0
  96. package/dist/ui/hooks/useAgentList.d.ts.map +1 -0
  97. package/dist/ui/hooks/useAgentList.js +66 -0
  98. package/dist/ui/hooks/useAgentList.js.map +1 -0
  99. package/dist/ui/hooks/useAgentMutations.d.ts +29 -0
  100. package/dist/ui/hooks/useAgentMutations.d.ts.map +1 -0
  101. package/dist/ui/hooks/useAgentMutations.js +124 -0
  102. package/dist/ui/hooks/useAgentMutations.js.map +1 -0
  103. package/dist/ui/hooks/useRunList.d.ts +24 -0
  104. package/dist/ui/hooks/useRunList.d.ts.map +1 -0
  105. package/dist/ui/hooks/useRunList.js +66 -0
  106. package/dist/ui/hooks/useRunList.js.map +1 -0
  107. package/dist/ui/hooks/useToolList.d.ts +40 -0
  108. package/dist/ui/hooks/useToolList.d.ts.map +1 -0
  109. package/dist/ui/hooks/useToolList.js +96 -0
  110. package/dist/ui/hooks/useToolList.js.map +1 -0
  111. package/dist/ui/index.d.ts +24 -0
  112. package/dist/ui/index.js +24 -0
  113. package/dist/ui/modals/AgentActionsModal.d.ts +27 -0
  114. package/dist/ui/modals/AgentActionsModal.d.ts.map +1 -0
  115. package/dist/ui/modals/AgentActionsModal.js +262 -0
  116. package/dist/ui/modals/AgentActionsModal.js.map +1 -0
  117. package/dist/ui/modals/CreateAgentModal.d.ts +25 -0
  118. package/dist/ui/modals/CreateAgentModal.d.ts.map +1 -0
  119. package/dist/ui/modals/CreateAgentModal.js +214 -0
  120. package/dist/ui/modals/CreateAgentModal.js.map +1 -0
  121. package/dist/ui/modals/index.d.ts +3 -0
  122. package/dist/ui/modals/index.js +4 -0
  123. package/dist/ui/overlays/demo-overlays.d.ts +19 -0
  124. package/dist/ui/overlays/demo-overlays.d.ts.map +1 -0
  125. package/dist/ui/overlays/demo-overlays.js +73 -0
  126. package/dist/ui/overlays/demo-overlays.js.map +1 -0
  127. package/dist/ui/overlays/index.d.ts +2 -0
  128. package/dist/ui/overlays/index.js +3 -0
  129. package/dist/ui/renderers/agent-list.markdown.d.ts +15 -0
  130. package/dist/ui/renderers/agent-list.markdown.d.ts.map +1 -0
  131. package/dist/ui/renderers/agent-list.markdown.js +51 -0
  132. package/dist/ui/renderers/agent-list.markdown.js.map +1 -0
  133. package/dist/ui/renderers/agent-list.renderer.d.ts +11 -0
  134. package/dist/ui/renderers/agent-list.renderer.d.ts.map +1 -0
  135. package/dist/ui/renderers/agent-list.renderer.js +19 -0
  136. package/dist/ui/renderers/agent-list.renderer.js.map +1 -0
  137. package/dist/ui/renderers/dashboard.markdown.d.ts +15 -0
  138. package/dist/ui/renderers/dashboard.markdown.d.ts.map +1 -0
  139. package/dist/ui/renderers/dashboard.markdown.js +100 -0
  140. package/dist/ui/renderers/dashboard.markdown.js.map +1 -0
  141. package/dist/ui/renderers/index.d.ts +6 -0
  142. package/dist/ui/renderers/index.js +7 -0
  143. package/dist/ui/renderers/run-list.markdown.d.ts +15 -0
  144. package/dist/ui/renderers/run-list.markdown.d.ts.map +1 -0
  145. package/dist/ui/renderers/run-list.markdown.js +44 -0
  146. package/dist/ui/renderers/run-list.markdown.js.map +1 -0
  147. package/dist/ui/renderers/tool-registry.markdown.d.ts +15 -0
  148. package/dist/ui/renderers/tool-registry.markdown.d.ts.map +1 -0
  149. package/dist/ui/renderers/tool-registry.markdown.js +55 -0
  150. package/dist/ui/renderers/tool-registry.markdown.js.map +1 -0
  151. package/dist/ui/views/AgentListView.d.ts +7 -0
  152. package/dist/ui/views/AgentListView.d.ts.map +1 -0
  153. package/dist/ui/views/AgentListView.js +93 -0
  154. package/dist/ui/views/AgentListView.js.map +1 -0
  155. package/dist/ui/views/RunListView.d.ts +14 -0
  156. package/dist/ui/views/RunListView.d.ts.map +1 -0
  157. package/dist/ui/views/RunListView.js +165 -0
  158. package/dist/ui/views/RunListView.js.map +1 -0
  159. package/dist/ui/views/ToolRegistryView.d.ts +14 -0
  160. package/dist/ui/views/ToolRegistryView.d.ts.map +1 -0
  161. package/dist/ui/views/ToolRegistryView.js +97 -0
  162. package/dist/ui/views/ToolRegistryView.js.map +1 -0
  163. package/dist/ui/views/index.d.ts +4 -0
  164. package/dist/ui/views/index.js +5 -0
  165. package/package.json +46 -10
  166. package/src/agent/agent.presentation.ts +7 -8
  167. package/src/agent/agent.test-spec.ts +55 -0
  168. package/src/agent.capability.ts +13 -0
  169. package/src/agent.feature.ts +3 -2
  170. package/src/example.ts +3 -3
  171. package/src/handlers/agent.handlers.ts +572 -0
  172. package/src/handlers/index.ts +3 -0
  173. package/src/index.ts +5 -0
  174. package/src/run/run.presentation.ts +5 -6
  175. package/src/run/run.test-spec.ts +55 -0
  176. package/src/seeders/index.ts +29 -0
  177. package/src/shared/overlay-types.ts +39 -0
  178. package/src/tool/tool.presentation.ts +5 -6
  179. package/src/tool/tool.test-spec.ts +55 -0
  180. package/src/ui/AgentDashboard.tsx +416 -0
  181. package/src/ui/AgentRunList.tsx +8 -0
  182. package/src/ui/AgentToolRegistry.tsx +8 -0
  183. package/src/ui/hooks/index.ts +14 -0
  184. package/src/ui/hooks/useAgentList.ts +80 -0
  185. package/src/ui/hooks/useAgentMutations.ts +156 -0
  186. package/src/ui/hooks/useRunList.ts +81 -0
  187. package/src/ui/hooks/useToolList.ts +122 -0
  188. package/src/ui/index.ts +21 -0
  189. package/src/ui/modals/AgentActionsModal.tsx +306 -0
  190. package/src/ui/modals/CreateAgentModal.tsx +257 -0
  191. package/src/ui/modals/index.ts +2 -0
  192. package/src/ui/overlays/demo-overlays.ts +77 -0
  193. package/src/ui/overlays/index.ts +1 -0
  194. package/src/ui/renderers/agent-list.markdown.ts +84 -0
  195. package/src/ui/renderers/agent-list.renderer.tsx +27 -0
  196. package/src/ui/renderers/dashboard.markdown.ts +169 -0
  197. package/src/ui/renderers/index.ts +12 -0
  198. package/src/ui/renderers/run-list.markdown.ts +75 -0
  199. package/src/ui/renderers/tool-registry.markdown.ts +91 -0
  200. package/src/ui/views/AgentListView.tsx +113 -0
  201. package/src/ui/views/RunListView.tsx +173 -0
  202. package/src/ui/views/ToolRegistryView.tsx +140 -0
  203. package/src/ui/views/index.ts +6 -0
  204. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,39 @@
1
+ export interface OverlayDefinition {
2
+ overlayId: string;
3
+ version: string;
4
+ description: string;
5
+ appliesTo: Record<string, string>;
6
+ modifications: OverlayModification[];
7
+ }
8
+
9
+ export type OverlayModification =
10
+ | HideFieldModification
11
+ | RenameLabelModification
12
+ | AddBadgeModification
13
+ | SetLimitModification;
14
+
15
+ export interface HideFieldModification {
16
+ type: 'hideField';
17
+ field: string;
18
+ reason?: string;
19
+ }
20
+
21
+ export interface RenameLabelModification {
22
+ type: 'renameLabel';
23
+ field: string;
24
+ newLabel: string;
25
+ }
26
+
27
+ export interface AddBadgeModification {
28
+ type: 'addBadge';
29
+ position: 'header' | 'footer';
30
+ label: string;
31
+ variant: 'warning' | 'info' | 'error' | 'success' | 'default';
32
+ }
33
+
34
+ export interface SetLimitModification {
35
+ type: 'setLimit';
36
+ field: string;
37
+ max: number;
38
+ message: string;
39
+ }
@@ -1,11 +1,10 @@
1
- import type { PresentationSpec } from '@contractspec/lib.contracts';
2
- import { StabilityEnum } from '@contractspec/lib.contracts';
1
+ import { StabilityEnum, definePresentation } from '@contractspec/lib.contracts';
3
2
  import { ToolSummaryModel } from './tool.schema';
4
3
 
5
4
  /**
6
5
  * Presentation for displaying a list of tools.
7
6
  */
8
- export const ToolListPresentation: PresentationSpec = {
7
+ export const ToolListPresentation = definePresentation({
9
8
  meta: {
10
9
  key: 'agent-console.tool.list',
11
10
  version: '1.0.0',
@@ -27,12 +26,12 @@ export const ToolListPresentation: PresentationSpec = {
27
26
  },
28
27
  targets: ['react', 'markdown', 'application/json'],
29
28
  policy: { flags: ['agent-console.enabled'] },
30
- };
29
+ });
31
30
 
32
31
  /**
33
32
  * Presentation for tool detail view.
34
33
  */
35
- export const ToolDetailPresentation: PresentationSpec = {
34
+ export const ToolDetailPresentation = definePresentation({
36
35
  meta: {
37
36
  key: 'agent-console.tool.detail',
38
37
  version: '1.0.0',
@@ -53,4 +52,4 @@ export const ToolDetailPresentation: PresentationSpec = {
53
52
  },
54
53
  targets: ['react', 'markdown'],
55
54
  policy: { flags: ['agent-console.enabled'] },
56
- };
55
+ });
@@ -0,0 +1,55 @@
1
+ import { defineTestSpec } from '@contractspec/lib.contracts';
2
+
3
+ export const toolListTest = defineTestSpec({
4
+ meta: {
5
+ key: 'test.agent.tool.list',
6
+ version: '1.0.0',
7
+ owners: ['@agent-console-team'],
8
+ description: 'Test for listing tools',
9
+ stability: 'stable',
10
+ tags: ['test'],
11
+ },
12
+ target: {
13
+ type: 'operation',
14
+ operation: { key: 'agent.tool.list', version: '1.0.0' },
15
+ },
16
+ scenarios: [
17
+ {
18
+ key: 'success',
19
+ when: { operation: { key: 'agent.tool.list' } },
20
+ then: [{ type: 'expectOutput', match: {} }],
21
+ },
22
+ {
23
+ key: 'error',
24
+ when: { operation: { key: 'agent.tool.list' } },
25
+ then: [{ type: 'expectError' }],
26
+ },
27
+ ],
28
+ });
29
+
30
+ export const toolGetTest = defineTestSpec({
31
+ meta: {
32
+ key: 'test.agent.tool.get',
33
+ version: '1.0.0',
34
+ owners: ['@agent-console-team'],
35
+ description: 'Test for getting tool',
36
+ stability: 'stable',
37
+ tags: ['test'],
38
+ },
39
+ target: {
40
+ type: 'operation',
41
+ operation: { key: 'agent.tool.get', version: '1.0.0' },
42
+ },
43
+ scenarios: [
44
+ {
45
+ key: 'success',
46
+ when: { operation: { key: 'agent.tool.get' } },
47
+ then: [{ type: 'expectOutput', match: {} }],
48
+ },
49
+ {
50
+ key: 'error',
51
+ when: { operation: { key: 'agent.tool.get' } },
52
+ then: [{ type: 'expectError' }],
53
+ },
54
+ ],
55
+ });
@@ -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
+ }