@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,572 @@
1
+ /**
2
+ * Runtime-local Agent Console handlers
3
+ *
4
+ * Database-backed handlers for agent management and runs.
5
+ */
6
+ import type { DatabasePort } from '@contractspec/lib.runtime-sandbox';
7
+ import { web } from '@contractspec/lib.runtime-sandbox';
8
+ const { generateId } = web;
9
+
10
+ // ============ Types ============
11
+
12
+ export interface Agent {
13
+ id: string;
14
+ projectId: string;
15
+ organizationId: string;
16
+ name: string;
17
+ description?: string;
18
+ modelProvider: string;
19
+ modelName: string;
20
+ systemPrompt?: string;
21
+ temperature: number;
22
+ maxTokens: number;
23
+ status: 'DRAFT' | 'ACTIVE' | 'PAUSED' | 'ARCHIVED';
24
+ createdAt: Date;
25
+ updatedAt: Date;
26
+ }
27
+
28
+ export interface Tool {
29
+ id: string;
30
+ projectId: string;
31
+ organizationId: string;
32
+ name: string;
33
+ description?: string;
34
+ version: string;
35
+ category:
36
+ | 'RETRIEVAL'
37
+ | 'COMPUTATION'
38
+ | 'COMMUNICATION'
39
+ | 'INTEGRATION'
40
+ | 'UTILITY'
41
+ | 'CUSTOM';
42
+ status: 'ACTIVE' | 'DISABLED' | 'DEPRECATED' | 'DRAFT';
43
+ inputSchema?: string;
44
+ outputSchema?: string;
45
+ endpoint?: string;
46
+ createdAt: Date;
47
+ updatedAt: Date;
48
+ }
49
+
50
+ export interface Run {
51
+ id: string;
52
+ projectId: string;
53
+ agentId: string;
54
+ agentName?: string;
55
+ status: 'QUEUED' | 'RUNNING' | 'COMPLETED' | 'FAILED' | 'CANCELLED';
56
+ input?: string;
57
+ output?: string;
58
+ totalTokens: number;
59
+ promptTokens: number;
60
+ completionTokens: number;
61
+ estimatedCostUsd: number;
62
+ durationMs?: number;
63
+ errorMessage?: string;
64
+ queuedAt: Date;
65
+ startedAt?: Date;
66
+ completedAt?: Date;
67
+ }
68
+
69
+ export interface RunMetrics {
70
+ totalRuns: number;
71
+ successRate: number;
72
+ averageDurationMs: number;
73
+ totalTokens: number;
74
+ totalCostUsd: number;
75
+ }
76
+
77
+ export interface ListAgentsInput {
78
+ projectId: string;
79
+ organizationId?: string;
80
+ status?: Agent['status'] | 'all';
81
+ search?: string;
82
+ limit?: number;
83
+ offset?: number;
84
+ }
85
+
86
+ export interface ListAgentsOutput {
87
+ items: Agent[];
88
+ total: number;
89
+ hasMore: boolean;
90
+ }
91
+
92
+ export interface ListToolsInput {
93
+ projectId: string;
94
+ organizationId?: string;
95
+ category?: Tool['category'] | 'all';
96
+ status?: Tool['status'] | 'all';
97
+ search?: string;
98
+ limit?: number;
99
+ offset?: number;
100
+ }
101
+
102
+ export interface ListToolsOutput {
103
+ items: Tool[];
104
+ total: number;
105
+ hasMore: boolean;
106
+ }
107
+
108
+ export interface ListRunsInput {
109
+ projectId: string;
110
+ organizationId?: string;
111
+ agentId?: string;
112
+ status?: Run['status'] | 'all';
113
+ limit?: number;
114
+ offset?: number;
115
+ }
116
+
117
+ export interface ListRunsOutput {
118
+ items: Run[];
119
+ total: number;
120
+ hasMore: boolean;
121
+ }
122
+
123
+ export interface CreateAgentInput {
124
+ name: string;
125
+ description?: string;
126
+ modelProvider?: string;
127
+ modelName?: string;
128
+ systemPrompt?: string;
129
+ temperature?: number;
130
+ maxTokens?: number;
131
+ }
132
+
133
+ export interface UpdateAgentInput {
134
+ id: string;
135
+ name?: string;
136
+ description?: string;
137
+ status?: Agent['status'];
138
+ }
139
+
140
+ // ============ Row Types ============
141
+
142
+ interface AgentRow extends Record<string, unknown> {
143
+ id: string;
144
+ projectId: string;
145
+ organizationId: string;
146
+ name: string;
147
+ description: string | null;
148
+ modelProvider: string;
149
+ modelName: string;
150
+ systemPrompt: string | null;
151
+ temperature: number;
152
+ maxTokens: number;
153
+ status: string;
154
+ createdAt: string;
155
+ updatedAt: string;
156
+ }
157
+
158
+ interface ToolRow extends Record<string, unknown> {
159
+ id: string;
160
+ projectId: string;
161
+ organizationId: string;
162
+ name: string;
163
+ description: string | null;
164
+ version: string;
165
+ category: string;
166
+ status: string;
167
+ inputSchema: string | null;
168
+ outputSchema: string | null;
169
+ endpoint: string | null;
170
+ createdAt: string;
171
+ updatedAt: string;
172
+ }
173
+
174
+ interface RunRow extends Record<string, unknown> {
175
+ id: string;
176
+ projectId: string;
177
+ agentId: string;
178
+ status: string;
179
+ input: string | null;
180
+ output: string | null;
181
+ totalTokens: number;
182
+ promptTokens: number;
183
+ completionTokens: number;
184
+ estimatedCostUsd: number;
185
+ durationMs: number | null;
186
+ errorMessage: string | null;
187
+ queuedAt: string;
188
+ startedAt: string | null;
189
+ completedAt: string | null;
190
+ }
191
+
192
+ function rowToAgent(row: AgentRow): Agent {
193
+ return {
194
+ id: row.id,
195
+ projectId: row.projectId,
196
+ organizationId: row.organizationId,
197
+ name: row.name,
198
+ description: row.description ?? undefined,
199
+ modelProvider: row.modelProvider,
200
+ modelName: row.modelName,
201
+ systemPrompt: row.systemPrompt ?? undefined,
202
+ temperature: row.temperature,
203
+ maxTokens: row.maxTokens,
204
+ status: row.status as Agent['status'],
205
+ createdAt: new Date(row.createdAt),
206
+ updatedAt: new Date(row.updatedAt),
207
+ };
208
+ }
209
+
210
+ function rowToTool(row: ToolRow): Tool {
211
+ return {
212
+ id: row.id,
213
+ projectId: row.projectId,
214
+ organizationId: row.organizationId,
215
+ name: row.name,
216
+ description: row.description ?? undefined,
217
+ version: row.version,
218
+ category: row.category as Tool['category'],
219
+ status: row.status as Tool['status'],
220
+ inputSchema: row.inputSchema ?? undefined,
221
+ outputSchema: row.outputSchema ?? undefined,
222
+ endpoint: row.endpoint ?? undefined,
223
+ createdAt: new Date(row.createdAt),
224
+ updatedAt: new Date(row.updatedAt),
225
+ };
226
+ }
227
+
228
+ function rowToRun(row: RunRow, agentName?: string): Run {
229
+ return {
230
+ id: row.id,
231
+ projectId: row.projectId,
232
+ agentId: row.agentId,
233
+ agentName,
234
+ status: row.status as Run['status'],
235
+ input: row.input ?? undefined,
236
+ output: row.output ?? undefined,
237
+ totalTokens: row.totalTokens,
238
+ promptTokens: row.promptTokens,
239
+ completionTokens: row.completionTokens,
240
+ estimatedCostUsd: row.estimatedCostUsd,
241
+ durationMs: row.durationMs ?? undefined,
242
+ errorMessage: row.errorMessage ?? undefined,
243
+ queuedAt: new Date(row.queuedAt),
244
+ startedAt: row.startedAt ? new Date(row.startedAt) : undefined,
245
+ completedAt: row.completedAt ? new Date(row.completedAt) : undefined,
246
+ };
247
+ }
248
+
249
+ // ============ Handler Factory ============
250
+
251
+ export function createAgentHandlers(db: DatabasePort) {
252
+ /**
253
+ * List agents
254
+ */
255
+ async function listAgents(input: ListAgentsInput): Promise<ListAgentsOutput> {
256
+ const {
257
+ projectId,
258
+ organizationId,
259
+ status,
260
+ search,
261
+ limit = 20,
262
+ offset = 0,
263
+ } = input;
264
+
265
+ let whereClause = 'WHERE projectId = ?';
266
+ const params: (string | number)[] = [projectId];
267
+
268
+ if (organizationId) {
269
+ whereClause += ' AND organizationId = ?';
270
+ params.push(organizationId);
271
+ }
272
+
273
+ if (status && status !== 'all') {
274
+ whereClause += ' AND status = ?';
275
+ params.push(status);
276
+ }
277
+
278
+ if (search) {
279
+ whereClause += ' AND (name LIKE ? OR description LIKE ?)';
280
+ params.push(`%${search}%`, `%${search}%`);
281
+ }
282
+
283
+ const countResult = (
284
+ await db.query(
285
+ `SELECT COUNT(*) as count FROM agent_definition ${whereClause}`,
286
+ params
287
+ )
288
+ ).rows as unknown as { count: number }[];
289
+ const total = countResult[0]?.count ?? 0;
290
+
291
+ const rows = (
292
+ await db.query(
293
+ `SELECT * FROM agent_definition ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
294
+ [...params, limit, offset]
295
+ )
296
+ ).rows as unknown as AgentRow[];
297
+
298
+ return {
299
+ items: rows.map(rowToAgent),
300
+ total,
301
+ hasMore: offset + rows.length < total,
302
+ };
303
+ }
304
+
305
+ /**
306
+ * Get a single agent
307
+ */
308
+ async function getAgent(id: string): Promise<Agent | null> {
309
+ const rows = (
310
+ await db.query(`SELECT * FROM agent_definition WHERE id = ?`, [id])
311
+ ).rows as unknown as AgentRow[];
312
+ return rows[0] ? rowToAgent(rows[0]) : null;
313
+ }
314
+
315
+ /**
316
+ * Create an agent
317
+ */
318
+ async function createAgent(
319
+ input: CreateAgentInput,
320
+ context: { projectId: string; organizationId: string }
321
+ ): Promise<Agent> {
322
+ const id = generateId('agent');
323
+ const now = new Date().toISOString();
324
+
325
+ await db.execute(
326
+ `INSERT INTO agent_definition (id, projectId, organizationId, name, description, modelProvider, modelName, systemPrompt, temperature, maxTokens, status, createdAt, updatedAt)
327
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
328
+ [
329
+ id,
330
+ context.projectId,
331
+ context.organizationId,
332
+ input.name,
333
+ input.description ?? null,
334
+ input.modelProvider ?? 'openai',
335
+ input.modelName ?? 'gpt-4',
336
+ input.systemPrompt ?? null,
337
+ input.temperature ?? 0.7,
338
+ input.maxTokens ?? 4096,
339
+ 'DRAFT',
340
+ now,
341
+ now,
342
+ ]
343
+ );
344
+
345
+ const rows = (
346
+ await db.query(`SELECT * FROM agent_definition WHERE id = ?`, [id])
347
+ ).rows as unknown as AgentRow[];
348
+
349
+ const row = rows[0];
350
+ if (!row) {
351
+ throw new Error('Failed to retrieve created agent');
352
+ }
353
+
354
+ return rowToAgent(row);
355
+ }
356
+
357
+ /**
358
+ * Update an agent
359
+ */
360
+ async function updateAgent(input: UpdateAgentInput): Promise<Agent> {
361
+ const now = new Date().toISOString();
362
+ const updates: string[] = ['updatedAt = ?'];
363
+ const params: (string | null)[] = [now];
364
+
365
+ if (input.name !== undefined) {
366
+ updates.push('name = ?');
367
+ params.push(input.name);
368
+ }
369
+
370
+ if (input.description !== undefined) {
371
+ updates.push('description = ?');
372
+ params.push(input.description);
373
+ }
374
+
375
+ if (input.status !== undefined) {
376
+ updates.push('status = ?');
377
+ params.push(input.status);
378
+ }
379
+
380
+ params.push(input.id);
381
+
382
+ await db.execute(
383
+ `UPDATE agent_definition SET ${updates.join(', ')} WHERE id = ?`,
384
+ params
385
+ );
386
+
387
+ const rows = (
388
+ await db.query(`SELECT * FROM agent_definition WHERE id = ?`, [input.id])
389
+ ).rows as unknown as AgentRow[];
390
+
391
+ if (!rows[0]) {
392
+ throw new Error('NOT_FOUND');
393
+ }
394
+
395
+ return rowToAgent(rows[0]);
396
+ }
397
+
398
+ /**
399
+ * List tools
400
+ */
401
+ async function listTools(input: ListToolsInput): Promise<ListToolsOutput> {
402
+ const {
403
+ projectId,
404
+ organizationId,
405
+ category,
406
+ status,
407
+ search,
408
+ limit = 50,
409
+ offset = 0,
410
+ } = input;
411
+
412
+ let whereClause = 'WHERE projectId = ?';
413
+ const params: (string | number)[] = [projectId];
414
+
415
+ if (organizationId) {
416
+ whereClause += ' AND organizationId = ?';
417
+ params.push(organizationId);
418
+ }
419
+
420
+ if (category && category !== 'all') {
421
+ whereClause += ' AND category = ?';
422
+ params.push(category);
423
+ }
424
+
425
+ if (status && status !== 'all') {
426
+ whereClause += ' AND status = ?';
427
+ params.push(status);
428
+ }
429
+
430
+ if (search) {
431
+ whereClause += ' AND (name LIKE ? OR description LIKE ?)';
432
+ params.push(`%${search}%`, `%${search}%`);
433
+ }
434
+
435
+ const countResult = (
436
+ await db.query(
437
+ `SELECT COUNT(*) as count FROM agent_tool ${whereClause}`,
438
+ params
439
+ )
440
+ ).rows as unknown as { count: number }[];
441
+ const total = countResult[0]?.count ?? 0;
442
+
443
+ const rows = (
444
+ await db.query(
445
+ `SELECT * FROM agent_tool ${whereClause} ORDER BY name ASC LIMIT ? OFFSET ?`,
446
+ [...params, limit, offset]
447
+ )
448
+ ).rows as unknown as ToolRow[];
449
+
450
+ return {
451
+ items: rows.map(rowToTool),
452
+ total,
453
+ hasMore: offset + rows.length < total,
454
+ };
455
+ }
456
+
457
+ /**
458
+ * List runs
459
+ */
460
+ async function listRuns(input: ListRunsInput): Promise<ListRunsOutput> {
461
+ const { projectId, agentId, status, limit = 20, offset = 0 } = input;
462
+
463
+ let whereClause = 'WHERE r.projectId = ?';
464
+ const params: (string | number)[] = [projectId];
465
+
466
+ if (agentId) {
467
+ whereClause += ' AND r.agentId = ?';
468
+ params.push(agentId);
469
+ }
470
+
471
+ if (status && status !== 'all') {
472
+ whereClause += ' AND r.status = ?';
473
+ params.push(status);
474
+ }
475
+
476
+ const countResult = (
477
+ await db.query(
478
+ `SELECT COUNT(*) as count FROM agent_run r ${whereClause}`,
479
+ params
480
+ )
481
+ ).rows as unknown as { count: number }[];
482
+ const total = countResult[0]?.count ?? 0;
483
+
484
+ const rows = (
485
+ await db.query(
486
+ `SELECT r.*, a.name as agentName
487
+ FROM agent_run r
488
+ LEFT JOIN agent_definition a ON r.agentId = a.id
489
+ ${whereClause}
490
+ ORDER BY r.queuedAt DESC LIMIT ? OFFSET ?`,
491
+ [...params, limit, offset]
492
+ )
493
+ ).rows as unknown as (RunRow & { agentName: string })[];
494
+
495
+ return {
496
+ items: rows.map((r) => rowToRun(r, r.agentName)),
497
+ total,
498
+ hasMore: offset + rows.length < total,
499
+ };
500
+ }
501
+
502
+ /**
503
+ * Get run metrics
504
+ */
505
+ async function getRunMetrics(input: {
506
+ projectId: string;
507
+ agentId?: string;
508
+ startDate?: Date;
509
+ endDate?: Date;
510
+ }): Promise<RunMetrics> {
511
+ let whereClause = 'WHERE projectId = ?';
512
+ const params: (string | number)[] = [input.projectId];
513
+
514
+ if (input.agentId) {
515
+ whereClause += ' AND agentId = ?';
516
+ params.push(input.agentId);
517
+ }
518
+
519
+ if (input.startDate) {
520
+ whereClause += ' AND queuedAt >= ?';
521
+ params.push(input.startDate.toISOString());
522
+ }
523
+
524
+ if (input.endDate) {
525
+ whereClause += ' AND queuedAt <= ?';
526
+ params.push(input.endDate.toISOString());
527
+ }
528
+
529
+ const result = (
530
+ await db.query(
531
+ `SELECT
532
+ COUNT(*) as totalRuns,
533
+ SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) as completedRuns,
534
+ AVG(CASE WHEN status = 'COMPLETED' THEN durationMs ELSE NULL END) as avgDuration,
535
+ COALESCE(SUM(totalTokens), 0) as totalTokens,
536
+ COALESCE(SUM(estimatedCostUsd), 0) as totalCost
537
+ FROM agent_run ${whereClause}`,
538
+ params
539
+ )
540
+ ).rows as unknown as {
541
+ totalRuns: number;
542
+ completedRuns: number;
543
+ avgDuration: number | null;
544
+ totalTokens: number;
545
+ totalCost: number;
546
+ }[];
547
+
548
+ const data = result[0];
549
+ const totalRuns = data?.totalRuns ?? 0;
550
+ const completedRuns = data?.completedRuns ?? 0;
551
+
552
+ return {
553
+ totalRuns,
554
+ successRate: totalRuns > 0 ? completedRuns / totalRuns : 0,
555
+ averageDurationMs: data?.avgDuration ?? 0,
556
+ totalTokens: data?.totalTokens ?? 0,
557
+ totalCostUsd: data?.totalCost ?? 0,
558
+ };
559
+ }
560
+
561
+ return {
562
+ listAgents,
563
+ getAgent,
564
+ createAgent,
565
+ updateAgent,
566
+ listTools,
567
+ listRuns,
568
+ getRunMetrics,
569
+ };
570
+ }
571
+
572
+ export type AgentHandlers = ReturnType<typeof createAgentHandlers>;
@@ -25,3 +25,6 @@ export {
25
25
  mockListToolsHandler,
26
26
  type ToolSummary,
27
27
  } from '../tool/tool.handler';
28
+
29
+ // Runtime handlers (PGLite)
30
+ export * from './agent.handlers';
package/src/index.ts CHANGED
@@ -20,6 +20,11 @@ export * from './agent';
20
20
  export * from './run';
21
21
  export * from './tool';
22
22
  export * from './shared';
23
+ export * from './ui';
24
+ export {
25
+ createAgentHandlers,
26
+ type AgentHandlers,
27
+ } from './handlers/agent.handlers';
23
28
 
24
29
  // Feature spec export
25
30
  export * from './agent.feature';
@@ -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 { RunSummaryModel } from './run.schema';
4
3
 
5
4
  /**
6
5
  * Presentation for displaying a list of agent runs.
7
6
  */
8
- export const RunListPresentation: PresentationSpec = {
7
+ export const RunListPresentation = definePresentation({
9
8
  meta: {
10
9
  key: 'agent-console.run.list',
11
10
  version: '1.0.0',
@@ -27,12 +26,12 @@ export const RunListPresentation: 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 run detail view.
34
33
  */
35
- export const RunDetailPresentation: PresentationSpec = {
34
+ export const RunDetailPresentation = definePresentation({
36
35
  meta: {
37
36
  key: 'agent-console.run.detail',
38
37
  version: '1.0.0',
@@ -52,4 +51,4 @@ export const RunDetailPresentation: PresentationSpec = {
52
51
  },
53
52
  targets: ['react', 'markdown'],
54
53
  policy: { flags: ['agent-console.enabled'] },
55
- };
54
+ });
@@ -0,0 +1,55 @@
1
+ import { defineTestSpec } from '@contractspec/lib.contracts';
2
+
3
+ export const runListTest = defineTestSpec({
4
+ meta: {
5
+ key: 'test.agent.run.list',
6
+ version: '1.0.0',
7
+ owners: ['@agent-console-team'],
8
+ description: 'Test for listing runs',
9
+ stability: 'stable',
10
+ tags: ['test'],
11
+ },
12
+ target: {
13
+ type: 'operation',
14
+ operation: { key: 'agent.run.list', version: '1.0.0' },
15
+ },
16
+ scenarios: [
17
+ {
18
+ key: 'success',
19
+ when: { operation: { key: 'agent.run.list' } },
20
+ then: [{ type: 'expectOutput', match: {} }],
21
+ },
22
+ {
23
+ key: 'error',
24
+ when: { operation: { key: 'agent.run.list' } },
25
+ then: [{ type: 'expectError' }],
26
+ },
27
+ ],
28
+ });
29
+
30
+ export const runGetTest = defineTestSpec({
31
+ meta: {
32
+ key: 'test.agent.run.get',
33
+ version: '1.0.0',
34
+ owners: ['@agent-console-team'],
35
+ description: 'Test for getting run',
36
+ stability: 'stable',
37
+ tags: ['test'],
38
+ },
39
+ target: {
40
+ type: 'operation',
41
+ operation: { key: 'agent.run.get', version: '1.0.0' },
42
+ },
43
+ scenarios: [
44
+ {
45
+ key: 'success',
46
+ when: { operation: { key: 'agent.run.get' } },
47
+ then: [{ type: 'expectOutput', match: {} }],
48
+ },
49
+ {
50
+ key: 'error',
51
+ when: { operation: { key: 'agent.run.get' } },
52
+ then: [{ type: 'expectError' }],
53
+ },
54
+ ],
55
+ });
@@ -0,0 +1,29 @@
1
+ import type { DatabasePort } from '@contractspec/lib.runtime-sandbox';
2
+
3
+ export async function seedAgentConsole(params: {
4
+ projectId: string;
5
+ db: DatabasePort;
6
+ }) {
7
+ const { projectId, db } = params;
8
+
9
+ const existing = await db.query(
10
+ `SELECT COUNT(*) as count FROM agent_definition WHERE "projectId" = $1`,
11
+ [projectId]
12
+ );
13
+ if ((existing.rows[0]?.count as number) > 0) return;
14
+
15
+ await db.execute(
16
+ `INSERT INTO agent_definition (id, "projectId", "organizationId", name, description, "modelProvider", "modelName", status)
17
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
18
+ [
19
+ 'agent_1',
20
+ projectId,
21
+ 'org_demo',
22
+ 'Demo Agent',
23
+ 'A demo AI agent',
24
+ 'openai',
25
+ 'gpt-4',
26
+ 'ACTIVE',
27
+ ]
28
+ );
29
+ }