@contractspec/example.agent-console 1.46.1 → 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 (201) hide show
  1. package/.turbo/turbo-build$colon$bundle.log +275 -128
  2. package/.turbo/turbo-build.log +274 -127
  3. package/CHANGELOG.md +37 -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.js +5 -5
  10. package/dist/agent/agent.event.js.map +1 -1
  11. package/dist/agent/agent.handler.js.map +1 -1
  12. package/dist/agent/agent.operation.d.ts +117 -117
  13. package/dist/agent/agent.operation.d.ts.map +1 -1
  14. package/dist/agent/agent.presentation.d.ts +4 -5
  15. package/dist/agent/agent.presentation.d.ts.map +1 -1
  16. package/dist/agent/agent.presentation.js +7 -7
  17. package/dist/agent/agent.presentation.js.map +1 -1
  18. package/dist/agent/agent.schema.d.ts +95 -95
  19. package/dist/agent/agent.schema.d.ts.map +1 -1
  20. package/dist/agent/agent.test-spec.d.ts +8 -0
  21. package/dist/agent/agent.test-spec.d.ts.map +1 -0
  22. package/dist/agent/agent.test-spec.js +65 -0
  23. package/dist/agent/agent.test-spec.js.map +1 -0
  24. package/dist/agent.capability.d.ts +7 -0
  25. package/dist/agent.capability.d.ts.map +1 -0
  26. package/dist/agent.capability.js +20 -0
  27. package/dist/agent.capability.js.map +1 -0
  28. package/dist/agent.feature.d.ts.map +1 -1
  29. package/dist/agent.feature.js +4 -2
  30. package/dist/agent.feature.js.map +1 -1
  31. package/dist/example.d.ts +2 -2
  32. package/dist/example.d.ts.map +1 -1
  33. package/dist/example.js +4 -2
  34. package/dist/example.js.map +1 -1
  35. package/dist/handlers/agent.handlers.d.ts +135 -0
  36. package/dist/handlers/agent.handlers.d.ts.map +1 -0
  37. package/dist/handlers/agent.handlers.js +263 -0
  38. package/dist/handlers/agent.handlers.js.map +1 -0
  39. package/dist/handlers/index.d.ts +2 -1
  40. package/dist/handlers/index.js +2 -1
  41. package/dist/index.d.ts +19 -1
  42. package/dist/index.js +19 -1
  43. package/dist/run/run.entity.d.ts +56 -56
  44. package/dist/run/run.enum.d.ts +5 -5
  45. package/dist/run/run.event.d.ts +71 -71
  46. package/dist/run/run.event.js +8 -8
  47. package/dist/run/run.event.js.map +1 -1
  48. package/dist/run/run.operation.d.ts +175 -175
  49. package/dist/run/run.presentation.d.ts +3 -4
  50. package/dist/run/run.presentation.d.ts.map +1 -1
  51. package/dist/run/run.presentation.js +5 -5
  52. package/dist/run/run.presentation.js.map +1 -1
  53. package/dist/run/run.schema.d.ts +99 -99
  54. package/dist/run/run.test-spec.d.ts +8 -0
  55. package/dist/run/run.test-spec.d.ts.map +1 -0
  56. package/dist/run/run.test-spec.js +65 -0
  57. package/dist/run/run.test-spec.js.map +1 -0
  58. package/dist/seeders/index.d.ts +10 -0
  59. package/dist/seeders/index.d.ts.map +1 -0
  60. package/dist/seeders/index.js +20 -0
  61. package/dist/seeders/index.js.map +1 -0
  62. package/dist/shared/overlay-types.d.ts +34 -0
  63. package/dist/shared/overlay-types.d.ts.map +1 -0
  64. package/dist/shared/overlay-types.js +0 -0
  65. package/dist/tool/tool.entity.d.ts +24 -24
  66. package/dist/tool/tool.enum.d.ts +4 -4
  67. package/dist/tool/tool.event.d.ts +25 -25
  68. package/dist/tool/tool.event.js +4 -4
  69. package/dist/tool/tool.event.js.map +1 -1
  70. package/dist/tool/tool.handler.d.ts.map +1 -1
  71. package/dist/tool/tool.operation.d.ts +101 -101
  72. package/dist/tool/tool.presentation.d.ts +3 -4
  73. package/dist/tool/tool.presentation.d.ts.map +1 -1
  74. package/dist/tool/tool.presentation.js +5 -5
  75. package/dist/tool/tool.presentation.js.map +1 -1
  76. package/dist/tool/tool.schema.d.ts +52 -52
  77. package/dist/tool/tool.schema.d.ts.map +1 -1
  78. package/dist/tool/tool.test-spec.d.ts +8 -0
  79. package/dist/tool/tool.test-spec.d.ts.map +1 -0
  80. package/dist/tool/tool.test-spec.js +65 -0
  81. package/dist/tool/tool.test-spec.js.map +1 -0
  82. package/dist/ui/AgentDashboard.d.ts +7 -0
  83. package/dist/ui/AgentDashboard.d.ts.map +1 -0
  84. package/dist/ui/AgentDashboard.js +420 -0
  85. package/dist/ui/AgentDashboard.js.map +1 -0
  86. package/dist/ui/AgentRunList.d.ts +2 -0
  87. package/dist/ui/AgentRunList.js +5 -0
  88. package/dist/ui/AgentToolRegistry.d.ts +2 -0
  89. package/dist/ui/AgentToolRegistry.js +5 -0
  90. package/dist/ui/hooks/index.d.ts +6 -0
  91. package/dist/ui/hooks/index.js +8 -0
  92. package/dist/ui/hooks/useAgentList.d.ts +28 -0
  93. package/dist/ui/hooks/useAgentList.d.ts.map +1 -0
  94. package/dist/ui/hooks/useAgentList.js +66 -0
  95. package/dist/ui/hooks/useAgentList.js.map +1 -0
  96. package/dist/ui/hooks/useAgentMutations.d.ts +29 -0
  97. package/dist/ui/hooks/useAgentMutations.d.ts.map +1 -0
  98. package/dist/ui/hooks/useAgentMutations.js +124 -0
  99. package/dist/ui/hooks/useAgentMutations.js.map +1 -0
  100. package/dist/ui/hooks/useRunList.d.ts +24 -0
  101. package/dist/ui/hooks/useRunList.d.ts.map +1 -0
  102. package/dist/ui/hooks/useRunList.js +66 -0
  103. package/dist/ui/hooks/useRunList.js.map +1 -0
  104. package/dist/ui/hooks/useToolList.d.ts +40 -0
  105. package/dist/ui/hooks/useToolList.d.ts.map +1 -0
  106. package/dist/ui/hooks/useToolList.js +96 -0
  107. package/dist/ui/hooks/useToolList.js.map +1 -0
  108. package/dist/ui/index.d.ts +24 -0
  109. package/dist/ui/index.js +24 -0
  110. package/dist/ui/modals/AgentActionsModal.d.ts +27 -0
  111. package/dist/ui/modals/AgentActionsModal.d.ts.map +1 -0
  112. package/dist/ui/modals/AgentActionsModal.js +262 -0
  113. package/dist/ui/modals/AgentActionsModal.js.map +1 -0
  114. package/dist/ui/modals/CreateAgentModal.d.ts +25 -0
  115. package/dist/ui/modals/CreateAgentModal.d.ts.map +1 -0
  116. package/dist/ui/modals/CreateAgentModal.js +214 -0
  117. package/dist/ui/modals/CreateAgentModal.js.map +1 -0
  118. package/dist/ui/modals/index.d.ts +3 -0
  119. package/dist/ui/modals/index.js +4 -0
  120. package/dist/ui/overlays/demo-overlays.d.ts +19 -0
  121. package/dist/ui/overlays/demo-overlays.d.ts.map +1 -0
  122. package/dist/ui/overlays/demo-overlays.js +73 -0
  123. package/dist/ui/overlays/demo-overlays.js.map +1 -0
  124. package/dist/ui/overlays/index.d.ts +2 -0
  125. package/dist/ui/overlays/index.js +3 -0
  126. package/dist/ui/renderers/agent-list.markdown.d.ts +15 -0
  127. package/dist/ui/renderers/agent-list.markdown.d.ts.map +1 -0
  128. package/dist/ui/renderers/agent-list.markdown.js +51 -0
  129. package/dist/ui/renderers/agent-list.markdown.js.map +1 -0
  130. package/dist/ui/renderers/agent-list.renderer.d.ts +11 -0
  131. package/dist/ui/renderers/agent-list.renderer.d.ts.map +1 -0
  132. package/dist/ui/renderers/agent-list.renderer.js +19 -0
  133. package/dist/ui/renderers/agent-list.renderer.js.map +1 -0
  134. package/dist/ui/renderers/dashboard.markdown.d.ts +15 -0
  135. package/dist/ui/renderers/dashboard.markdown.d.ts.map +1 -0
  136. package/dist/ui/renderers/dashboard.markdown.js +100 -0
  137. package/dist/ui/renderers/dashboard.markdown.js.map +1 -0
  138. package/dist/ui/renderers/index.d.ts +6 -0
  139. package/dist/ui/renderers/index.js +7 -0
  140. package/dist/ui/renderers/run-list.markdown.d.ts +15 -0
  141. package/dist/ui/renderers/run-list.markdown.d.ts.map +1 -0
  142. package/dist/ui/renderers/run-list.markdown.js +44 -0
  143. package/dist/ui/renderers/run-list.markdown.js.map +1 -0
  144. package/dist/ui/renderers/tool-registry.markdown.d.ts +15 -0
  145. package/dist/ui/renderers/tool-registry.markdown.d.ts.map +1 -0
  146. package/dist/ui/renderers/tool-registry.markdown.js +55 -0
  147. package/dist/ui/renderers/tool-registry.markdown.js.map +1 -0
  148. package/dist/ui/views/AgentListView.d.ts +7 -0
  149. package/dist/ui/views/AgentListView.d.ts.map +1 -0
  150. package/dist/ui/views/AgentListView.js +93 -0
  151. package/dist/ui/views/AgentListView.js.map +1 -0
  152. package/dist/ui/views/RunListView.d.ts +14 -0
  153. package/dist/ui/views/RunListView.d.ts.map +1 -0
  154. package/dist/ui/views/RunListView.js +165 -0
  155. package/dist/ui/views/RunListView.js.map +1 -0
  156. package/dist/ui/views/ToolRegistryView.d.ts +14 -0
  157. package/dist/ui/views/ToolRegistryView.d.ts.map +1 -0
  158. package/dist/ui/views/ToolRegistryView.js +97 -0
  159. package/dist/ui/views/ToolRegistryView.js.map +1 -0
  160. package/dist/ui/views/index.d.ts +4 -0
  161. package/dist/ui/views/index.js +5 -0
  162. package/package.json +46 -10
  163. package/src/agent/agent.presentation.ts +7 -8
  164. package/src/agent/agent.test-spec.ts +55 -0
  165. package/src/agent.capability.ts +13 -0
  166. package/src/agent.feature.ts +3 -2
  167. package/src/example.ts +3 -3
  168. package/src/handlers/agent.handlers.ts +572 -0
  169. package/src/handlers/index.ts +3 -0
  170. package/src/index.ts +5 -0
  171. package/src/run/run.presentation.ts +5 -6
  172. package/src/run/run.test-spec.ts +55 -0
  173. package/src/seeders/index.ts +29 -0
  174. package/src/shared/overlay-types.ts +39 -0
  175. package/src/tool/tool.presentation.ts +5 -6
  176. package/src/tool/tool.test-spec.ts +55 -0
  177. package/src/ui/AgentDashboard.tsx +416 -0
  178. package/src/ui/AgentRunList.tsx +8 -0
  179. package/src/ui/AgentToolRegistry.tsx +8 -0
  180. package/src/ui/hooks/index.ts +14 -0
  181. package/src/ui/hooks/useAgentList.ts +80 -0
  182. package/src/ui/hooks/useAgentMutations.ts +156 -0
  183. package/src/ui/hooks/useRunList.ts +81 -0
  184. package/src/ui/hooks/useToolList.ts +122 -0
  185. package/src/ui/index.ts +21 -0
  186. package/src/ui/modals/AgentActionsModal.tsx +306 -0
  187. package/src/ui/modals/CreateAgentModal.tsx +257 -0
  188. package/src/ui/modals/index.ts +2 -0
  189. package/src/ui/overlays/demo-overlays.ts +77 -0
  190. package/src/ui/overlays/index.ts +1 -0
  191. package/src/ui/renderers/agent-list.markdown.ts +84 -0
  192. package/src/ui/renderers/agent-list.renderer.tsx +27 -0
  193. package/src/ui/renderers/dashboard.markdown.ts +169 -0
  194. package/src/ui/renderers/index.ts +12 -0
  195. package/src/ui/renderers/run-list.markdown.ts +75 -0
  196. package/src/ui/renderers/tool-registry.markdown.ts +91 -0
  197. package/src/ui/views/AgentListView.tsx +113 -0
  198. package/src/ui/views/RunListView.tsx +173 -0
  199. package/src/ui/views/ToolRegistryView.tsx +140 -0
  200. package/src/ui/views/index.ts +6 -0
  201. package/tsconfig.tsbuildinfo +1 -1
@@ -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 };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Hook for fetching and managing run list data
3
+ *
4
+ * Uses runtime-local database-backed handlers.
5
+ */
6
+ import { useCallback, useEffect, useState } from 'react';
7
+ import { useTemplateRuntime } from '@contractspec/lib.example-shared-ui';
8
+ import type {
9
+ ListRunsOutput as RuntimeListRunsOutput,
10
+ Run as RuntimeRun,
11
+ RunMetrics as RuntimeRunMetrics,
12
+ AgentHandlers,
13
+ } from '../../handlers/agent.handlers';
14
+
15
+ // Re-export types for convenience
16
+ export type Run = RuntimeRun;
17
+ export type ListRunsOutput = RuntimeListRunsOutput;
18
+ export type RunMetrics = RuntimeRunMetrics;
19
+
20
+ export interface UseRunListOptions {
21
+ agentId?: string;
22
+ status?: Run['status'] | 'all';
23
+ limit?: number;
24
+ }
25
+
26
+ export function useRunList(options: UseRunListOptions = {}) {
27
+ const { handlers, projectId } = useTemplateRuntime<{
28
+ agent: AgentHandlers;
29
+ }>();
30
+ const { agent } = handlers;
31
+
32
+ const [data, setData] = useState<ListRunsOutput | null>(null);
33
+ const [metrics, setMetrics] = useState<RunMetrics | null>(null);
34
+ const [loading, setLoading] = useState(true);
35
+ const [error, setError] = useState<Error | null>(null);
36
+ const [page, setPage] = useState(1);
37
+
38
+ const fetchData = useCallback(async () => {
39
+ setLoading(true);
40
+ setError(null);
41
+
42
+ try {
43
+ const [runsResult, metricsResult] = await Promise.all([
44
+ agent.listRuns({
45
+ projectId,
46
+ agentId: options.agentId,
47
+ status: options.status === 'all' ? undefined : options.status,
48
+ limit: options.limit ?? 20,
49
+ offset: (page - 1) * (options.limit ?? 20),
50
+ }),
51
+ agent.getRunMetrics({
52
+ projectId,
53
+ agentId: options.agentId,
54
+ startDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), // 30 days ago
55
+ endDate: new Date(),
56
+ }),
57
+ ]);
58
+ setData(runsResult);
59
+ setMetrics(metricsResult);
60
+ } catch (err) {
61
+ setError(err instanceof Error ? err : new Error('Unknown error'));
62
+ } finally {
63
+ setLoading(false);
64
+ }
65
+ }, [agent, projectId, options.agentId, options.status, options.limit, page]);
66
+
67
+ useEffect(() => {
68
+ fetchData();
69
+ }, [fetchData]);
70
+
71
+ return {
72
+ data,
73
+ metrics,
74
+ loading,
75
+ error,
76
+ page,
77
+ refetch: fetchData,
78
+ nextPage: () => setPage((p) => p + 1),
79
+ prevPage: () => page > 1 && setPage((p) => p - 1),
80
+ };
81
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Hook for fetching and managing tool 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
+ ListToolsOutput as RuntimeListToolsOutput,
10
+ Tool as RuntimeTool,
11
+ AgentHandlers,
12
+ } from '../../handlers/agent.handlers';
13
+
14
+ // Re-export types for convenience
15
+ export type Tool = RuntimeTool;
16
+ export type ListToolsOutput = RuntimeListToolsOutput;
17
+
18
+ export type ToolCategory = Tool['category'];
19
+ export type ToolStatus = Tool['status'];
20
+
21
+ export interface UseToolListOptions {
22
+ search?: string;
23
+ category?: ToolCategory;
24
+ status?: ToolStatus | 'all';
25
+ limit?: number;
26
+ }
27
+
28
+ export function useToolList(options: UseToolListOptions = {}) {
29
+ const { handlers, projectId } = useTemplateRuntime<{
30
+ agent: AgentHandlers;
31
+ }>();
32
+ const { agent } = handlers;
33
+
34
+ const [data, setData] = useState<ListToolsOutput | null>(null);
35
+ const [loading, setLoading] = useState(true);
36
+ const [error, setError] = useState<Error | null>(null);
37
+ const [page, setPage] = useState(1);
38
+
39
+ const fetchData = useCallback(async () => {
40
+ setLoading(true);
41
+ setError(null);
42
+
43
+ try {
44
+ const result = await agent.listTools({
45
+ projectId,
46
+ search: options.search,
47
+ category: options.category,
48
+ status: options.status === 'all' ? undefined : options.status,
49
+ limit: options.limit ?? 50,
50
+ offset: (page - 1) * (options.limit ?? 50),
51
+ });
52
+ setData(result);
53
+ } catch (err) {
54
+ setError(err instanceof Error ? err : new Error('Unknown error'));
55
+ } finally {
56
+ setLoading(false);
57
+ }
58
+ }, [
59
+ agent,
60
+ projectId,
61
+ options.search,
62
+ options.category,
63
+ options.status,
64
+ options.limit,
65
+ page,
66
+ ]);
67
+
68
+ useEffect(() => {
69
+ fetchData();
70
+ }, [fetchData]);
71
+
72
+ // Calculate stats and grouping
73
+ const { stats, groupedByCategory, categoryStats } = useMemo(() => {
74
+ if (!data) return { stats: null, groupedByCategory: {}, categoryStats: [] };
75
+ const items = data.items;
76
+
77
+ const active = items.filter((t) => t.status === 'ACTIVE').length;
78
+ const deprecated = items.filter((t) => t.status === 'DEPRECATED').length;
79
+ const disabled = items.filter((t) => t.status === 'DISABLED').length;
80
+
81
+ // Group by category
82
+ const grouped: Record<string, Tool[]> = {};
83
+ const byCategory: Record<string, number> = {};
84
+
85
+ items.forEach((t) => {
86
+ const cat = t.category;
87
+ if (!grouped[cat]) grouped[cat] = [];
88
+ grouped[cat].push(t);
89
+ byCategory[cat] = (byCategory[cat] || 0) + 1;
90
+ });
91
+
92
+ // Category stats sorted by count
93
+ const catStats = Object.entries(byCategory)
94
+ .map(([category, count]) => ({ category, count }))
95
+ .sort((a, b) => b.count - a.count);
96
+
97
+ return {
98
+ stats: {
99
+ total: data.total,
100
+ active,
101
+ deprecated,
102
+ disabled,
103
+ topCategories: catStats.slice(0, 5),
104
+ },
105
+ groupedByCategory: grouped,
106
+ categoryStats: catStats,
107
+ };
108
+ }, [data]);
109
+
110
+ return {
111
+ data,
112
+ loading,
113
+ error,
114
+ stats,
115
+ groupedByCategory,
116
+ categoryStats,
117
+ page,
118
+ refetch: fetchData,
119
+ nextPage: () => setPage((p) => p + 1),
120
+ prevPage: () => page > 1 && setPage((p) => p - 1),
121
+ };
122
+ }
@@ -0,0 +1,21 @@
1
+ // Main dashboard component
2
+ export * from './AgentDashboard';
3
+
4
+ // Backward-compatible exports
5
+ export * from './AgentRunList';
6
+ export * from './AgentToolRegistry';
7
+
8
+ // View components
9
+ export * from './views';
10
+
11
+ // Modals
12
+ export * from './modals';
13
+
14
+ // Data hooks
15
+ export * from './hooks';
16
+
17
+ // Renderers
18
+ export * from './renderers';
19
+
20
+ // Overlays
21
+ export * from './overlays';
@@ -0,0 +1,306 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * AgentActionsModal - Actions for a specific agent
5
+ *
6
+ * Wires to UpdateAgentCommand via useAgentMutations hook.
7
+ */
8
+ import { useState } from 'react';
9
+ import { Button } from '@contractspec/lib.design-system';
10
+ import type { Agent } from '../hooks/useAgentList';
11
+
12
+ type ActionMode = 'menu' | 'execute' | 'confirm';
13
+
14
+ interface AgentActionsModalProps {
15
+ isOpen: boolean;
16
+ agent: Agent | null;
17
+ onClose: () => void;
18
+ onActivate: (agentId: string) => Promise<void>;
19
+ onPause: (agentId: string) => Promise<void>;
20
+ onArchive: (agentId: string) => Promise<void>;
21
+ onExecute: (agentId: string, message: string) => Promise<void>;
22
+ isLoading?: boolean;
23
+ }
24
+
25
+ function getStatusColor(status: Agent['status']): string {
26
+ switch (status) {
27
+ case 'ACTIVE':
28
+ return 'text-green-600 bg-green-100 dark:text-green-400 dark:bg-green-900/30';
29
+ case 'DRAFT':
30
+ return 'text-blue-600 bg-blue-100 dark:text-blue-400 dark:bg-blue-900/30';
31
+ case 'PAUSED':
32
+ return 'text-yellow-600 bg-yellow-100 dark:text-yellow-400 dark:bg-yellow-900/30';
33
+ case 'ARCHIVED':
34
+ return 'text-gray-600 bg-gray-100 dark:text-gray-400 dark:bg-gray-700';
35
+ default:
36
+ return 'text-gray-600 bg-gray-100';
37
+ }
38
+ }
39
+
40
+ export function AgentActionsModal({
41
+ isOpen,
42
+ agent,
43
+ onClose,
44
+ onActivate,
45
+ onPause,
46
+ onArchive,
47
+ onExecute,
48
+ isLoading = false,
49
+ }: AgentActionsModalProps) {
50
+ const [mode, setMode] = useState<ActionMode>('menu');
51
+ const [message, setMessage] = useState('');
52
+ const [confirmAction, setConfirmAction] = useState<'archive' | null>(null);
53
+ const [error, setError] = useState<string | null>(null);
54
+
55
+ const resetForm = () => {
56
+ setMode('menu');
57
+ setMessage('');
58
+ setConfirmAction(null);
59
+ setError(null);
60
+ };
61
+
62
+ const handleClose = () => {
63
+ resetForm();
64
+ onClose();
65
+ };
66
+
67
+ const handleExecute = async () => {
68
+ if (!agent) return;
69
+ setError(null);
70
+
71
+ if (!message.trim()) {
72
+ setError('Please enter a message');
73
+ return;
74
+ }
75
+
76
+ try {
77
+ await onExecute(agent.id, message.trim());
78
+ handleClose();
79
+ } catch (err) {
80
+ setError(err instanceof Error ? err.message : 'Failed to execute agent');
81
+ }
82
+ };
83
+
84
+ const handleStatusChange = async (
85
+ action: 'activate' | 'pause' | 'archive'
86
+ ) => {
87
+ if (!agent) return;
88
+ setError(null);
89
+
90
+ try {
91
+ switch (action) {
92
+ case 'activate':
93
+ await onActivate(agent.id);
94
+ break;
95
+ case 'pause':
96
+ await onPause(agent.id);
97
+ break;
98
+ case 'archive':
99
+ await onArchive(agent.id);
100
+ break;
101
+ }
102
+ handleClose();
103
+ } catch (err) {
104
+ setError(
105
+ err instanceof Error ? err.message : `Failed to ${action} agent`
106
+ );
107
+ }
108
+ };
109
+
110
+ if (!isOpen || !agent) return null;
111
+
112
+ return (
113
+ <div className="fixed inset-0 z-50 flex items-center justify-center">
114
+ {/* Backdrop */}
115
+ <div
116
+ className="bg-background/80 absolute inset-0 backdrop-blur-sm"
117
+ onClick={handleClose}
118
+ role="button"
119
+ tabIndex={0}
120
+ onKeyDown={(e) => {
121
+ if (e.key === 'Enter' || e.key === ' ') handleClose();
122
+ }}
123
+ aria-label="Close modal"
124
+ />
125
+
126
+ {/* Modal */}
127
+ <div className="bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl">
128
+ {/* Agent Header */}
129
+ <div className="border-border mb-4 border-b pb-4">
130
+ <h2 className="text-xl font-semibold">{agent.name}</h2>
131
+ <div className="mt-1 flex items-center gap-2">
132
+ <span className="text-muted-foreground text-sm">
133
+ {agent.modelProvider} / {agent.modelName}
134
+ </span>
135
+ <span
136
+ className={`rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(agent.status)}`}
137
+ >
138
+ {agent.status}
139
+ </span>
140
+ </div>
141
+ {agent.description && (
142
+ <p className="text-muted-foreground mt-2 text-sm">
143
+ {agent.description}
144
+ </p>
145
+ )}
146
+ </div>
147
+
148
+ {/* Main Menu */}
149
+ {mode === 'menu' && (
150
+ <div className="space-y-3">
151
+ {/* Execute - Only for active agents */}
152
+ {agent.status === 'ACTIVE' && (
153
+ <Button
154
+ className="w-full justify-start"
155
+ variant="ghost"
156
+ onPress={() => setMode('execute')}
157
+ >
158
+ <span className="mr-2">▶️</span> Execute Agent
159
+ </Button>
160
+ )}
161
+
162
+ {/* Status Changes */}
163
+ {(agent.status === 'DRAFT' || agent.status === 'PAUSED') && (
164
+ <Button
165
+ className="w-full justify-start"
166
+ variant="ghost"
167
+ onPress={() => handleStatusChange('activate')}
168
+ disabled={isLoading}
169
+ >
170
+ <span className="mr-2">🟢</span> Activate Agent
171
+ </Button>
172
+ )}
173
+
174
+ {agent.status === 'ACTIVE' && (
175
+ <Button
176
+ className="w-full justify-start"
177
+ variant="ghost"
178
+ onPress={() => handleStatusChange('pause')}
179
+ disabled={isLoading}
180
+ >
181
+ <span className="mr-2">⏸️</span> Pause Agent
182
+ </Button>
183
+ )}
184
+
185
+ {agent.status !== 'ARCHIVED' && (
186
+ <Button
187
+ className="w-full justify-start text-yellow-600 hover:text-yellow-700"
188
+ variant="ghost"
189
+ onPress={() => {
190
+ setConfirmAction('archive');
191
+ setMode('confirm');
192
+ }}
193
+ >
194
+ <span className="mr-2">📦</span> Archive Agent
195
+ </Button>
196
+ )}
197
+
198
+ {agent.status === 'ARCHIVED' && (
199
+ <Button
200
+ className="w-full justify-start"
201
+ variant="ghost"
202
+ onPress={() => handleStatusChange('activate')}
203
+ disabled={isLoading}
204
+ >
205
+ <span className="mr-2">🔄</span> Restore Agent
206
+ </Button>
207
+ )}
208
+
209
+ {error && (
210
+ <div className="bg-destructive/10 text-destructive rounded-md p-3 text-sm">
211
+ {error}
212
+ </div>
213
+ )}
214
+
215
+ <div className="border-border border-t pt-3">
216
+ <Button
217
+ className="w-full"
218
+ variant="outline"
219
+ onPress={handleClose}
220
+ >
221
+ Close
222
+ </Button>
223
+ </div>
224
+ </div>
225
+ )}
226
+
227
+ {/* Execute Form */}
228
+ {mode === 'execute' && (
229
+ <div className="space-y-4">
230
+ <div>
231
+ <label
232
+ htmlFor="execute-message"
233
+ className="text-muted-foreground mb-1 block text-sm font-medium"
234
+ >
235
+ Message *
236
+ </label>
237
+ <textarea
238
+ id="execute-message"
239
+ value={message}
240
+ onChange={(e) => setMessage(e.target.value)}
241
+ placeholder="Enter your message to the agent..."
242
+ rows={4}
243
+ disabled={isLoading}
244
+ className="border-input bg-background focus:ring-ring w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50"
245
+ />
246
+ </div>
247
+
248
+ {error && (
249
+ <div className="bg-destructive/10 text-destructive rounded-md p-3 text-sm">
250
+ {error}
251
+ </div>
252
+ )}
253
+
254
+ <div className="flex justify-end gap-3 pt-2">
255
+ <Button
256
+ variant="ghost"
257
+ onPress={() => setMode('menu')}
258
+ disabled={isLoading}
259
+ >
260
+ Back
261
+ </Button>
262
+ <Button onPress={handleExecute} disabled={isLoading}>
263
+ {isLoading ? 'Executing...' : '▶️ Execute'}
264
+ </Button>
265
+ </div>
266
+ </div>
267
+ )}
268
+
269
+ {/* Confirm Action */}
270
+ {mode === 'confirm' && confirmAction === 'archive' && (
271
+ <div className="space-y-4">
272
+ <p className="text-muted-foreground">
273
+ Are you sure you want to archive{' '}
274
+ <span className="text-foreground font-medium">{agent.name}</span>?
275
+ </p>
276
+ <p className="text-muted-foreground text-sm">
277
+ Archived agents cannot be executed but can be restored later.
278
+ </p>
279
+
280
+ {error && (
281
+ <div className="bg-destructive/10 text-destructive rounded-md p-3 text-sm">
282
+ {error}
283
+ </div>
284
+ )}
285
+
286
+ <div className="flex justify-end gap-3 pt-2">
287
+ <Button
288
+ variant="ghost"
289
+ onPress={() => setMode('menu')}
290
+ disabled={isLoading}
291
+ >
292
+ Cancel
293
+ </Button>
294
+ <Button
295
+ onPress={() => handleStatusChange('archive')}
296
+ disabled={isLoading}
297
+ >
298
+ {isLoading ? 'Archiving...' : '📦 Archive'}
299
+ </Button>
300
+ </div>
301
+ </div>
302
+ )}
303
+ </div>
304
+ </div>
305
+ );
306
+ }