@contractspec/example.agent-console 3.7.6 → 3.8.2

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 (288) hide show
  1. package/.turbo/turbo-build.log +126 -105
  2. package/AGENTS.md +52 -31
  3. package/CHANGELOG.md +29 -0
  4. package/README.md +112 -83
  5. package/dist/agent/agent.event.js +1 -1
  6. package/dist/agent/agent.handler.d.ts +3 -0
  7. package/dist/agent/agent.handler.js +730 -1
  8. package/dist/agent/agent.operation.js +1 -1
  9. package/dist/agent/index.d.ts +5 -5
  10. package/dist/agent/index.js +74 -73
  11. package/dist/agent.feature.js +179 -0
  12. package/dist/browser/agent/agent.event.js +1 -1
  13. package/dist/browser/agent/agent.handler.js +730 -1
  14. package/dist/browser/agent/agent.operation.js +1 -1
  15. package/dist/browser/agent/index.js +74 -73
  16. package/dist/browser/agent.feature.js +179 -0
  17. package/dist/browser/docs/agent-console.docblock.js +11 -8
  18. package/dist/browser/docs/index.js +11 -8
  19. package/dist/browser/example.js +2 -3
  20. package/dist/browser/handlers/agent.handlers.js +1883 -2
  21. package/dist/browser/handlers/index.js +2142 -8
  22. package/dist/browser/index.js +4075 -3161
  23. package/dist/browser/presentations/index.js +51 -51
  24. package/dist/browser/run/index.js +380 -374
  25. package/dist/browser/run/run.event.js +2 -2
  26. package/dist/browser/run/run.handler.js +666 -1
  27. package/dist/browser/run/run.presentation.js +2 -2
  28. package/dist/browser/shared/index.js +293 -1
  29. package/dist/browser/shared/mock-runs.js +5 -0
  30. package/dist/browser/tool/index.js +161 -161
  31. package/dist/browser/tool/tool.event.js +1 -1
  32. package/dist/browser/tool/tool.handler.js +479 -3
  33. package/dist/browser/tool/tool.presentation.js +2 -2
  34. package/dist/browser/ui/AgentDashboard.js +1816 -931
  35. package/dist/browser/ui/AgentDashboard.visualizations.js +217 -0
  36. package/dist/browser/ui/AgentRunList.js +360 -128
  37. package/dist/browser/ui/AgentToolRegistry.js +9 -9
  38. package/dist/browser/ui/hooks/index.js +611 -161
  39. package/dist/browser/ui/hooks/useAgentList.js +1 -1
  40. package/dist/browser/ui/hooks/useAgentMutations.js +444 -9
  41. package/dist/browser/ui/hooks/useRunList.js +26 -11
  42. package/dist/browser/ui/hooks/useToolList.js +1 -1
  43. package/dist/browser/ui/index.js +2161 -1258
  44. package/dist/browser/ui/modals/AgentActionsModal.js +13 -13
  45. package/dist/browser/ui/modals/CreateAgentModal.js +15 -15
  46. package/dist/browser/ui/modals/index.js +297 -297
  47. package/dist/browser/ui/renderers/agent-list.markdown.js +14 -5
  48. package/dist/browser/ui/renderers/agent-list.renderer.js +7 -7
  49. package/dist/browser/ui/renderers/dashboard.markdown.js +207 -36
  50. package/dist/browser/ui/renderers/index.js +359 -163
  51. package/dist/browser/ui/renderers/run-list.markdown.js +9 -4
  52. package/dist/browser/ui/renderers/tool-registry.markdown.js +15 -4
  53. package/dist/browser/ui/views/AgentListView.js +7 -7
  54. package/dist/browser/ui/views/RunDataTable.js +326 -0
  55. package/dist/browser/ui/views/RunListView.js +360 -128
  56. package/dist/browser/ui/views/ToolRegistryView.js +9 -9
  57. package/dist/browser/ui/views/index.js +478 -246
  58. package/dist/browser/ui/views/run-data-table.columns.js +271 -0
  59. package/dist/browser/ui/views/run-list.shared.js +177 -0
  60. package/dist/browser/visualizations/catalog.js +134 -0
  61. package/dist/browser/visualizations/index.js +187 -0
  62. package/dist/browser/visualizations/selectors.js +181 -0
  63. package/dist/docs/agent-console.docblock.js +11 -8
  64. package/dist/docs/index.js +11 -8
  65. package/dist/example.js +2 -3
  66. package/dist/example.test.d.ts +1 -0
  67. package/dist/handlers/agent.handlers.d.ts +2 -0
  68. package/dist/handlers/agent.handlers.js +1883 -2
  69. package/dist/handlers/index.d.ts +2 -4
  70. package/dist/handlers/index.js +2142 -8
  71. package/dist/handlers/mock-handlers.test.d.ts +1 -0
  72. package/dist/index.d.ts +6 -4
  73. package/dist/index.js +4075 -3161
  74. package/dist/node/agent/agent.event.js +1 -1
  75. package/dist/node/agent/agent.handler.js +730 -1
  76. package/dist/node/agent/agent.operation.js +1 -1
  77. package/dist/node/agent/index.js +74 -73
  78. package/dist/node/agent.feature.js +179 -0
  79. package/dist/node/docs/agent-console.docblock.js +11 -8
  80. package/dist/node/docs/index.js +11 -8
  81. package/dist/node/example.js +2 -3
  82. package/dist/node/handlers/agent.handlers.js +1883 -2
  83. package/dist/node/handlers/index.js +2142 -8
  84. package/dist/node/index.js +4075 -3161
  85. package/dist/node/presentations/index.js +51 -51
  86. package/dist/node/run/index.js +380 -374
  87. package/dist/node/run/run.event.js +2 -2
  88. package/dist/node/run/run.handler.js +666 -1
  89. package/dist/node/run/run.presentation.js +2 -2
  90. package/dist/node/shared/index.js +293 -1
  91. package/dist/node/shared/mock-runs.js +5 -0
  92. package/dist/node/tool/index.js +161 -161
  93. package/dist/node/tool/tool.event.js +1 -1
  94. package/dist/node/tool/tool.handler.js +479 -3
  95. package/dist/node/tool/tool.presentation.js +2 -2
  96. package/dist/node/ui/AgentDashboard.js +1816 -931
  97. package/dist/node/ui/AgentDashboard.visualizations.js +217 -0
  98. package/dist/node/ui/AgentRunList.js +360 -128
  99. package/dist/node/ui/AgentToolRegistry.js +9 -9
  100. package/dist/node/ui/hooks/index.js +611 -161
  101. package/dist/node/ui/hooks/useAgentList.js +1 -1
  102. package/dist/node/ui/hooks/useAgentMutations.js +444 -9
  103. package/dist/node/ui/hooks/useRunList.js +26 -11
  104. package/dist/node/ui/hooks/useToolList.js +1 -1
  105. package/dist/node/ui/index.js +2161 -1258
  106. package/dist/node/ui/modals/AgentActionsModal.js +13 -13
  107. package/dist/node/ui/modals/CreateAgentModal.js +15 -15
  108. package/dist/node/ui/modals/index.js +297 -297
  109. package/dist/node/ui/renderers/agent-list.markdown.js +14 -5
  110. package/dist/node/ui/renderers/agent-list.renderer.js +7 -7
  111. package/dist/node/ui/renderers/dashboard.markdown.js +207 -36
  112. package/dist/node/ui/renderers/index.js +359 -163
  113. package/dist/node/ui/renderers/run-list.markdown.js +9 -4
  114. package/dist/node/ui/renderers/tool-registry.markdown.js +15 -4
  115. package/dist/node/ui/views/AgentListView.js +7 -7
  116. package/dist/node/ui/views/RunDataTable.js +326 -0
  117. package/dist/node/ui/views/RunListView.js +360 -128
  118. package/dist/node/ui/views/ToolRegistryView.js +9 -9
  119. package/dist/node/ui/views/index.js +478 -246
  120. package/dist/node/ui/views/run-data-table.columns.js +271 -0
  121. package/dist/node/ui/views/run-list.shared.js +177 -0
  122. package/dist/node/visualizations/catalog.js +134 -0
  123. package/dist/node/visualizations/index.js +187 -0
  124. package/dist/node/visualizations/selectors.js +181 -0
  125. package/dist/presentations/index.d.ts +3 -5
  126. package/dist/presentations/index.js +51 -51
  127. package/dist/proof/index.d.ts +2 -0
  128. package/dist/proof/meetup-proof.d.ts +10 -0
  129. package/dist/proof/meetup-proof.runtime.d.ts +22 -0
  130. package/dist/proof/meetup-proof.scenario.d.ts +2 -0
  131. package/dist/proof/meetup-proof.suite.d.ts +1 -0
  132. package/dist/proof/meetup-proof.test.d.ts +1 -0
  133. package/dist/run/index.d.ts +7 -7
  134. package/dist/run/index.js +380 -374
  135. package/dist/run/run.event.js +2 -2
  136. package/dist/run/run.handler.d.ts +7 -0
  137. package/dist/run/run.handler.js +666 -1
  138. package/dist/run/run.presentation.js +2 -2
  139. package/dist/shared/demo-dashboard-data.d.ts +16 -0
  140. package/dist/shared/demo-runtime-seed.d.ts +17 -0
  141. package/dist/shared/demo-runtime.d.ts +8 -0
  142. package/dist/shared/demo-runtime.test.d.ts +1 -0
  143. package/dist/shared/index.d.ts +4 -1
  144. package/dist/shared/index.js +293 -1
  145. package/dist/shared/mock-runs.d.ts +4 -0
  146. package/dist/shared/mock-runs.js +5 -0
  147. package/dist/tool/index.d.ts +7 -7
  148. package/dist/tool/index.js +161 -161
  149. package/dist/tool/tool.event.js +1 -1
  150. package/dist/tool/tool.handler.d.ts +3 -0
  151. package/dist/tool/tool.handler.js +479 -3
  152. package/dist/tool/tool.presentation.js +2 -2
  153. package/dist/ui/AgentDashboard.js +1816 -931
  154. package/dist/ui/AgentDashboard.sandbox.test.d.ts +1 -0
  155. package/dist/ui/AgentDashboard.visualizations.d.ts +4 -0
  156. package/dist/ui/AgentDashboard.visualizations.js +218 -0
  157. package/dist/ui/AgentRunList.js +360 -128
  158. package/dist/ui/AgentToolRegistry.js +9 -9
  159. package/dist/ui/hooks/index.d.ts +4 -4
  160. package/dist/ui/hooks/index.js +611 -161
  161. package/dist/ui/hooks/useAgentList.d.ts +5 -0
  162. package/dist/ui/hooks/useAgentList.js +1 -1
  163. package/dist/ui/hooks/useAgentMutations.d.ts +9 -2
  164. package/dist/ui/hooks/useAgentMutations.js +444 -9
  165. package/dist/ui/hooks/useRunList.d.ts +13 -2
  166. package/dist/ui/hooks/useRunList.js +26 -11
  167. package/dist/ui/hooks/useToolList.d.ts +5 -0
  168. package/dist/ui/hooks/useToolList.js +1 -1
  169. package/dist/ui/index.d.ts +3 -3
  170. package/dist/ui/index.js +2161 -1258
  171. package/dist/ui/modals/AgentActionsModal.js +13 -13
  172. package/dist/ui/modals/CreateAgentModal.js +15 -15
  173. package/dist/ui/modals/index.d.ts +1 -1
  174. package/dist/ui/modals/index.js +297 -297
  175. package/dist/ui/renderers/agent-list.markdown.d.ts +5 -0
  176. package/dist/ui/renderers/agent-list.markdown.js +14 -5
  177. package/dist/ui/renderers/agent-list.renderer.js +7 -7
  178. package/dist/ui/renderers/dashboard.markdown.d.ts +5 -0
  179. package/dist/ui/renderers/dashboard.markdown.js +207 -36
  180. package/dist/ui/renderers/index.d.ts +2 -2
  181. package/dist/ui/renderers/index.js +359 -163
  182. package/dist/ui/renderers/run-list.markdown.d.ts +5 -0
  183. package/dist/ui/renderers/run-list.markdown.js +9 -4
  184. package/dist/ui/renderers/tool-registry.markdown.d.ts +6 -1
  185. package/dist/ui/renderers/tool-registry.markdown.js +15 -4
  186. package/dist/ui/views/AgentListView.js +7 -7
  187. package/dist/ui/views/RunDataTable.d.ts +18 -0
  188. package/dist/ui/views/RunDataTable.js +327 -0
  189. package/dist/ui/views/RunListView.js +360 -128
  190. package/dist/ui/views/ToolRegistryView.js +9 -9
  191. package/dist/ui/views/index.js +478 -246
  192. package/dist/ui/views/run-data-table.columns.d.ts +3 -0
  193. package/dist/ui/views/run-data-table.columns.js +272 -0
  194. package/dist/ui/views/run-list.shared.d.ts +14 -0
  195. package/dist/ui/views/run-list.shared.js +178 -0
  196. package/dist/visualizations/catalog.d.ts +10 -0
  197. package/dist/visualizations/catalog.js +135 -0
  198. package/dist/visualizations/index.d.ts +2 -0
  199. package/dist/visualizations/index.js +188 -0
  200. package/dist/visualizations/selectors.d.ts +3 -0
  201. package/dist/visualizations/selectors.js +182 -0
  202. package/dist/visualizations/selectors.test.d.ts +1 -0
  203. package/package.json +114 -12
  204. package/proofs/agent-console-meetup.replay.json +220 -0
  205. package/src/agent/agent.entity.ts +111 -111
  206. package/src/agent/agent.enum.ts +12 -12
  207. package/src/agent/agent.event.ts +91 -91
  208. package/src/agent/agent.handler.ts +144 -127
  209. package/src/agent/agent.operation.ts +400 -400
  210. package/src/agent/agent.presentation.ts +62 -62
  211. package/src/agent/agent.schema.ts +175 -175
  212. package/src/agent/agent.test-spec.ts +48 -48
  213. package/src/agent/index.ts +46 -51
  214. package/src/agent.capability.ts +11 -11
  215. package/src/agent.feature.ts +134 -131
  216. package/src/docs/agent-console.docblock.ts +52 -49
  217. package/src/example.test.ts +75 -0
  218. package/src/example.ts +34 -35
  219. package/src/handlers/agent.handlers.ts +576 -522
  220. package/src/handlers/index.ts +30 -14
  221. package/src/handlers/mock-handlers.test.ts +77 -0
  222. package/src/index.ts +10 -9
  223. package/src/presentations/index.ts +11 -13
  224. package/src/proof/index.ts +2 -0
  225. package/src/proof/meetup-proof.runtime.ts +196 -0
  226. package/src/proof/meetup-proof.scenario.ts +99 -0
  227. package/src/proof/meetup-proof.suite.ts +29 -0
  228. package/src/proof/meetup-proof.test.ts +28 -0
  229. package/src/proof/meetup-proof.ts +130 -0
  230. package/src/run/index.ts +49 -54
  231. package/src/run/run.entity.ts +137 -137
  232. package/src/run/run.enum.ts +18 -18
  233. package/src/run/run.event.ts +174 -174
  234. package/src/run/run.handler.ts +113 -96
  235. package/src/run/run.operation.ts +474 -474
  236. package/src/run/run.presentation.ts +42 -42
  237. package/src/run/run.schema.ts +126 -126
  238. package/src/run/run.test-spec.ts +48 -48
  239. package/src/seeders/index.ts +21 -21
  240. package/src/shared/demo-dashboard-data.ts +58 -0
  241. package/src/shared/demo-runtime-seed.ts +139 -0
  242. package/src/shared/demo-runtime.test.ts +169 -0
  243. package/src/shared/demo-runtime.ts +260 -0
  244. package/src/shared/index.ts +12 -1
  245. package/src/shared/mock-agents.ts +76 -76
  246. package/src/shared/mock-runs.ts +107 -102
  247. package/src/shared/mock-tools.ts +140 -140
  248. package/src/shared/overlay-types.ts +23 -23
  249. package/src/tool/index.ts +39 -44
  250. package/src/tool/tool.entity.ts +73 -73
  251. package/src/tool/tool.enum.ts +13 -13
  252. package/src/tool/tool.event.ts +80 -80
  253. package/src/tool/tool.handler.ts +124 -107
  254. package/src/tool/tool.operation.ts +328 -328
  255. package/src/tool/tool.presentation.ts +43 -43
  256. package/src/tool/tool.schema.ts +106 -106
  257. package/src/tool/tool.test-spec.ts +48 -48
  258. package/src/ui/AgentDashboard.sandbox.test.tsx +312 -0
  259. package/src/ui/AgentDashboard.tsx +351 -348
  260. package/src/ui/AgentDashboard.visualizations.tsx +35 -0
  261. package/src/ui/hooks/index.ts +7 -7
  262. package/src/ui/hooks/useAgentList.ts +57 -56
  263. package/src/ui/hooks/useAgentMutations.ts +168 -159
  264. package/src/ui/hooks/useRunList.ts +90 -57
  265. package/src/ui/hooks/useToolList.ts +102 -101
  266. package/src/ui/index.ts +6 -9
  267. package/src/ui/modals/AgentActionsModal.tsx +262 -262
  268. package/src/ui/modals/CreateAgentModal.tsx +232 -232
  269. package/src/ui/modals/index.ts +1 -1
  270. package/src/ui/overlays/demo-overlays.ts +52 -52
  271. package/src/ui/renderers/agent-list.markdown.ts +81 -61
  272. package/src/ui/renderers/agent-list.renderer.tsx +14 -14
  273. package/src/ui/renderers/dashboard.markdown.ts +135 -139
  274. package/src/ui/renderers/index.ts +3 -4
  275. package/src/ui/renderers/run-list.markdown.ts +56 -47
  276. package/src/ui/renderers/tool-registry.markdown.ts +79 -66
  277. package/src/ui/views/AgentListView.tsx +90 -90
  278. package/src/ui/views/RunDataTable.tsx +74 -0
  279. package/src/ui/views/RunListView.tsx +84 -158
  280. package/src/ui/views/ToolRegistryView.tsx +113 -113
  281. package/src/ui/views/run-data-table.columns.tsx +102 -0
  282. package/src/ui/views/run-list.shared.tsx +139 -0
  283. package/src/visualizations/catalog.ts +132 -0
  284. package/src/visualizations/index.ts +2 -0
  285. package/src/visualizations/selectors.test.ts +12 -0
  286. package/src/visualizations/selectors.ts +70 -0
  287. package/tsconfig.json +7 -8
  288. package/tsdown.config.js +24 -3
@@ -3,604 +3,658 @@
3
3
  *
4
4
  * Database-backed handlers for agent management and runs.
5
5
  */
6
+
6
7
  import type { DatabasePort } from '@contractspec/lib.runtime-sandbox';
7
8
  import { web } from '@contractspec/lib.runtime-sandbox';
9
+ import {
10
+ CreateAgentCommand,
11
+ GetAgentQuery,
12
+ ListAgentsQuery,
13
+ UpdateAgentCommand,
14
+ } from '../agent/agent.operation';
15
+ import {
16
+ ExecuteAgentCommand,
17
+ GetRunMetricsQuery,
18
+ ListRunsQuery,
19
+ } from '../run/run.operation';
20
+ import { ListToolsQuery } from '../tool/tool.operation';
21
+
8
22
  const { generateId } = web;
23
+ const RUNTIME_AGENT_HANDLER_CONTRACTS = [
24
+ CreateAgentCommand,
25
+ GetAgentQuery,
26
+ ListAgentsQuery,
27
+ UpdateAgentCommand,
28
+ ExecuteAgentCommand,
29
+ ListToolsQuery,
30
+ ListRunsQuery,
31
+ GetRunMetricsQuery,
32
+ ] as const;
33
+ void RUNTIME_AGENT_HANDLER_CONTRACTS;
9
34
 
10
35
  // ============ Types ============
11
36
 
12
37
  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;
38
+ id: string;
39
+ projectId: string;
40
+ organizationId: string;
41
+ name: string;
42
+ description?: string;
43
+ modelProvider: string;
44
+ modelName: string;
45
+ systemPrompt?: string;
46
+ temperature: number;
47
+ maxTokens: number;
48
+ status: 'DRAFT' | 'ACTIVE' | 'PAUSED' | 'ARCHIVED';
49
+ createdAt: Date;
50
+ updatedAt: Date;
26
51
  }
27
52
 
28
53
  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;
54
+ id: string;
55
+ projectId: string;
56
+ organizationId: string;
57
+ name: string;
58
+ description?: string;
59
+ version: string;
60
+ category:
61
+ | 'RETRIEVAL'
62
+ | 'COMPUTATION'
63
+ | 'COMMUNICATION'
64
+ | 'INTEGRATION'
65
+ | 'UTILITY'
66
+ | 'CUSTOM';
67
+ status: 'ACTIVE' | 'DISABLED' | 'DEPRECATED' | 'DRAFT';
68
+ inputSchema?: string;
69
+ outputSchema?: string;
70
+ endpoint?: string;
71
+ createdAt: Date;
72
+ updatedAt: Date;
48
73
  }
49
74
 
50
75
  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;
76
+ id: string;
77
+ projectId: string;
78
+ agentId: string;
79
+ agentName?: string;
80
+ status: 'QUEUED' | 'RUNNING' | 'COMPLETED' | 'FAILED' | 'CANCELLED';
81
+ input?: string;
82
+ output?: string;
83
+ totalTokens: number;
84
+ promptTokens: number;
85
+ completionTokens: number;
86
+ estimatedCostUsd: number;
87
+ durationMs?: number;
88
+ errorMessage?: string;
89
+ queuedAt: Date;
90
+ startedAt?: Date;
91
+ completedAt?: Date;
67
92
  }
68
93
 
69
94
  export interface RunMetrics {
70
- totalRuns: number;
71
- successRate: number;
72
- averageDurationMs: number;
73
- totalTokens: number;
74
- totalCostUsd: number;
95
+ totalRuns: number;
96
+ successRate: number;
97
+ averageDurationMs: number;
98
+ totalTokens: number;
99
+ totalCostUsd: number;
75
100
  }
76
101
 
77
102
  export interface ListAgentsInput {
78
- projectId: string;
79
- organizationId?: string;
80
- status?: Agent['status'] | 'all';
81
- search?: string;
82
- limit?: number;
83
- offset?: number;
103
+ projectId: string;
104
+ organizationId?: string;
105
+ status?: Agent['status'] | 'all';
106
+ search?: string;
107
+ limit?: number;
108
+ offset?: number;
84
109
  }
85
110
 
86
111
  export interface ListAgentsOutput {
87
- items: Agent[];
88
- total: number;
89
- hasMore: boolean;
112
+ items: Agent[];
113
+ total: number;
114
+ hasMore: boolean;
90
115
  }
91
116
 
92
117
  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;
118
+ projectId: string;
119
+ organizationId?: string;
120
+ category?: Tool['category'] | 'all';
121
+ status?: Tool['status'] | 'all';
122
+ search?: string;
123
+ limit?: number;
124
+ offset?: number;
100
125
  }
101
126
 
102
127
  export interface ListToolsOutput {
103
- items: Tool[];
104
- total: number;
105
- hasMore: boolean;
128
+ items: Tool[];
129
+ total: number;
130
+ hasMore: boolean;
106
131
  }
107
132
 
108
133
  export interface ListRunsInput {
109
- projectId: string;
110
- organizationId?: string;
111
- agentId?: string;
112
- status?: Run['status'] | 'all';
113
- limit?: number;
114
- offset?: number;
134
+ projectId: string;
135
+ organizationId?: string;
136
+ agentId?: string;
137
+ status?: Run['status'] | 'all';
138
+ sortBy?:
139
+ | 'queuedAt'
140
+ | 'totalTokens'
141
+ | 'durationMs'
142
+ | 'estimatedCostUsd'
143
+ | 'status'
144
+ | 'agentName';
145
+ sortDirection?: 'asc' | 'desc';
146
+ limit?: number;
147
+ offset?: number;
115
148
  }
116
149
 
117
150
  export interface ListRunsOutput {
118
- items: Run[];
119
- total: number;
120
- hasMore: boolean;
151
+ items: Run[];
152
+ total: number;
153
+ hasMore: boolean;
121
154
  }
122
155
 
123
156
  export interface CreateAgentInput {
124
- name: string;
125
- description?: string;
126
- modelProvider?: string;
127
- modelName?: string;
128
- systemPrompt?: string;
129
- temperature?: number;
130
- maxTokens?: number;
157
+ name: string;
158
+ description?: string;
159
+ modelProvider?: string;
160
+ modelName?: string;
161
+ systemPrompt?: string;
162
+ temperature?: number;
163
+ maxTokens?: number;
131
164
  }
132
165
 
133
166
  export interface UpdateAgentInput {
134
- id: string;
135
- name?: string;
136
- description?: string;
137
- status?: Agent['status'];
167
+ id: string;
168
+ name?: string;
169
+ description?: string;
170
+ status?: Agent['status'];
138
171
  }
139
172
 
140
173
  // ============ Row Types ============
141
174
 
142
175
  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;
176
+ id: string;
177
+ projectId: string;
178
+ organizationId: string;
179
+ name: string;
180
+ description: string | null;
181
+ modelProvider: string;
182
+ modelName: string;
183
+ systemPrompt: string | null;
184
+ temperature: number;
185
+ maxTokens: number;
186
+ status: string;
187
+ createdAt: string;
188
+ updatedAt: string;
156
189
  }
157
190
 
158
191
  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;
192
+ id: string;
193
+ projectId: string;
194
+ organizationId: string;
195
+ name: string;
196
+ description: string | null;
197
+ version: string;
198
+ category: string;
199
+ status: string;
200
+ inputSchema: string | null;
201
+ outputSchema: string | null;
202
+ endpoint: string | null;
203
+ createdAt: string;
204
+ updatedAt: string;
172
205
  }
173
206
 
174
207
  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;
208
+ id: string;
209
+ projectId: string;
210
+ agentId: string;
211
+ status: string;
212
+ input: string | null;
213
+ output: string | null;
214
+ totalTokens: number;
215
+ promptTokens: number;
216
+ completionTokens: number;
217
+ estimatedCostUsd: number;
218
+ durationMs: number | null;
219
+ errorMessage: string | null;
220
+ queuedAt: string;
221
+ startedAt: string | null;
222
+ completedAt: string | null;
190
223
  }
191
224
 
225
+ const RUN_SORT_COLUMNS = {
226
+ queuedAt: 'r.queuedAt',
227
+ totalTokens: 'r.totalTokens',
228
+ durationMs: 'r.durationMs',
229
+ estimatedCostUsd: 'r.estimatedCostUsd',
230
+ status: 'r.status',
231
+ agentName: 'a.name',
232
+ } as const;
233
+
192
234
  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
- };
235
+ return {
236
+ id: row.id,
237
+ projectId: row.projectId,
238
+ organizationId: row.organizationId,
239
+ name: row.name,
240
+ description: row.description ?? undefined,
241
+ modelProvider: row.modelProvider,
242
+ modelName: row.modelName,
243
+ systemPrompt: row.systemPrompt ?? undefined,
244
+ temperature: row.temperature,
245
+ maxTokens: row.maxTokens,
246
+ status: row.status as Agent['status'],
247
+ createdAt: new Date(row.createdAt),
248
+ updatedAt: new Date(row.updatedAt),
249
+ };
208
250
  }
209
251
 
210
252
  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
- };
253
+ return {
254
+ id: row.id,
255
+ projectId: row.projectId,
256
+ organizationId: row.organizationId,
257
+ name: row.name,
258
+ description: row.description ?? undefined,
259
+ version: row.version,
260
+ category: row.category as Tool['category'],
261
+ status: row.status as Tool['status'],
262
+ inputSchema: row.inputSchema ?? undefined,
263
+ outputSchema: row.outputSchema ?? undefined,
264
+ endpoint: row.endpoint ?? undefined,
265
+ createdAt: new Date(row.createdAt),
266
+ updatedAt: new Date(row.updatedAt),
267
+ };
226
268
  }
227
269
 
228
270
  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
- };
271
+ return {
272
+ id: row.id,
273
+ projectId: row.projectId,
274
+ agentId: row.agentId,
275
+ agentName,
276
+ status: row.status as Run['status'],
277
+ input: row.input ?? undefined,
278
+ output: row.output ?? undefined,
279
+ totalTokens: row.totalTokens,
280
+ promptTokens: row.promptTokens,
281
+ completionTokens: row.completionTokens,
282
+ estimatedCostUsd: row.estimatedCostUsd,
283
+ durationMs: row.durationMs ?? undefined,
284
+ errorMessage: row.errorMessage ?? undefined,
285
+ queuedAt: new Date(row.queuedAt),
286
+ startedAt: row.startedAt ? new Date(row.startedAt) : undefined,
287
+ completedAt: row.completedAt ? new Date(row.completedAt) : undefined,
288
+ };
247
289
  }
248
290
 
249
291
  // ============ Handler Factory ============
250
292
 
251
293
  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)
294
+ /**
295
+ * List agents
296
+ */
297
+ async function listAgents(input: ListAgentsInput): Promise<ListAgentsOutput> {
298
+ const {
299
+ projectId,
300
+ organizationId,
301
+ status,
302
+ search,
303
+ limit = 20,
304
+ offset = 0,
305
+ } = input;
306
+
307
+ let whereClause = 'WHERE projectId = ?';
308
+ const params: (string | number)[] = [projectId];
309
+
310
+ if (organizationId) {
311
+ whereClause += ' AND organizationId = ?';
312
+ params.push(organizationId);
313
+ }
314
+
315
+ if (status && status !== 'all') {
316
+ whereClause += ' AND status = ?';
317
+ params.push(status);
318
+ }
319
+
320
+ if (search) {
321
+ whereClause += ' AND (name LIKE ? OR description LIKE ?)';
322
+ params.push(`%${search}%`, `%${search}%`);
323
+ }
324
+
325
+ const countResult = (
326
+ await db.query(
327
+ `SELECT COUNT(*) as count FROM agent_definition ${whereClause}`,
328
+ params
329
+ )
330
+ ).rows as unknown as { count: number }[];
331
+ const total = countResult[0]?.count ?? 0;
332
+
333
+ const rows = (
334
+ await db.query(
335
+ `SELECT * FROM agent_definition ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,
336
+ [...params, limit, offset]
337
+ )
338
+ ).rows as unknown as AgentRow[];
339
+
340
+ return {
341
+ items: rows.map(rowToAgent),
342
+ total,
343
+ hasMore: offset + rows.length < total,
344
+ };
345
+ }
346
+
347
+ /**
348
+ * Get a single agent
349
+ */
350
+ async function getAgent(id: string): Promise<Agent | null> {
351
+ const rows = (
352
+ await db.query(`SELECT * FROM agent_definition WHERE id = ?`, [id])
353
+ ).rows as unknown as AgentRow[];
354
+ return rows[0] ? rowToAgent(rows[0]) : null;
355
+ }
356
+
357
+ /**
358
+ * Create an agent
359
+ */
360
+ async function createAgent(
361
+ input: CreateAgentInput,
362
+ context: { projectId: string; organizationId: string }
363
+ ): Promise<Agent> {
364
+ const id = generateId('agent');
365
+ const now = new Date().toISOString();
366
+
367
+ await db.execute(
368
+ `INSERT INTO agent_definition (id, projectId, organizationId, name, description, modelProvider, modelName, systemPrompt, temperature, maxTokens, status, createdAt, updatedAt)
327
369
  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
370
+ [
371
+ id,
372
+ context.projectId,
373
+ context.organizationId,
374
+ input.name,
375
+ input.description ?? null,
376
+ input.modelProvider ?? 'openai',
377
+ input.modelName ?? 'gpt-4',
378
+ input.systemPrompt ?? null,
379
+ input.temperature ?? 0.7,
380
+ input.maxTokens ?? 4096,
381
+ 'DRAFT',
382
+ now,
383
+ now,
384
+ ]
385
+ );
386
+
387
+ const rows = (
388
+ await db.query(`SELECT * FROM agent_definition WHERE id = ?`, [id])
389
+ ).rows as unknown as AgentRow[];
390
+
391
+ const row = rows[0];
392
+ if (!row) {
393
+ throw new Error('Failed to retrieve created agent');
394
+ }
395
+
396
+ return rowToAgent(row);
397
+ }
398
+
399
+ /**
400
+ * Update an agent
401
+ */
402
+ async function updateAgent(input: UpdateAgentInput): Promise<Agent> {
403
+ const now = new Date().toISOString();
404
+ const updates: string[] = ['updatedAt = ?'];
405
+ const params: (string | null)[] = [now];
406
+
407
+ if (input.name !== undefined) {
408
+ updates.push('name = ?');
409
+ params.push(input.name);
410
+ }
411
+
412
+ if (input.description !== undefined) {
413
+ updates.push('description = ?');
414
+ params.push(input.description);
415
+ }
416
+
417
+ if (input.status !== undefined) {
418
+ updates.push('status = ?');
419
+ params.push(input.status);
420
+ }
421
+
422
+ params.push(input.id);
423
+
424
+ await db.execute(
425
+ `UPDATE agent_definition SET ${updates.join(', ')} WHERE id = ?`,
426
+ params
427
+ );
428
+
429
+ const rows = (
430
+ await db.query(`SELECT * FROM agent_definition WHERE id = ?`, [input.id])
431
+ ).rows as unknown as AgentRow[];
432
+
433
+ if (!rows[0]) {
434
+ throw new Error('NOT_FOUND');
435
+ }
436
+
437
+ return rowToAgent(rows[0]);
438
+ }
439
+
440
+ /**
441
+ * List tools
442
+ */
443
+ async function listTools(input: ListToolsInput): Promise<ListToolsOutput> {
444
+ const {
445
+ projectId,
446
+ organizationId,
447
+ category,
448
+ status,
449
+ search,
450
+ limit = 50,
451
+ offset = 0,
452
+ } = input;
453
+
454
+ let whereClause = 'WHERE projectId = ?';
455
+ const params: (string | number)[] = [projectId];
456
+
457
+ if (organizationId) {
458
+ whereClause += ' AND organizationId = ?';
459
+ params.push(organizationId);
460
+ }
461
+
462
+ if (category && category !== 'all') {
463
+ whereClause += ' AND category = ?';
464
+ params.push(category);
465
+ }
466
+
467
+ if (status && status !== 'all') {
468
+ whereClause += ' AND status = ?';
469
+ params.push(status);
470
+ }
471
+
472
+ if (search) {
473
+ whereClause += ' AND (name LIKE ? OR description LIKE ?)';
474
+ params.push(`%${search}%`, `%${search}%`);
475
+ }
476
+
477
+ const countResult = (
478
+ await db.query(
479
+ `SELECT COUNT(*) as count FROM agent_tool ${whereClause}`,
480
+ params
481
+ )
482
+ ).rows as unknown as { count: number }[];
483
+ const total = countResult[0]?.count ?? 0;
484
+
485
+ const rows = (
486
+ await db.query(
487
+ `SELECT * FROM agent_tool ${whereClause} ORDER BY name ASC LIMIT ? OFFSET ?`,
488
+ [...params, limit, offset]
489
+ )
490
+ ).rows as unknown as ToolRow[];
491
+
492
+ return {
493
+ items: rows.map(rowToTool),
494
+ total,
495
+ hasMore: offset + rows.length < total,
496
+ };
497
+ }
498
+
499
+ /**
500
+ * List runs
501
+ */
502
+ async function listRuns(input: ListRunsInput): Promise<ListRunsOutput> {
503
+ const {
504
+ projectId,
505
+ agentId,
506
+ status,
507
+ sortBy,
508
+ sortDirection = 'desc',
509
+ limit = 20,
510
+ offset = 0,
511
+ } = input;
512
+
513
+ let whereClause = 'WHERE r.projectId = ?';
514
+ const params: (string | number)[] = [projectId];
515
+
516
+ if (agentId) {
517
+ whereClause += ' AND r.agentId = ?';
518
+ params.push(agentId);
519
+ }
520
+
521
+ if (status && status !== 'all') {
522
+ whereClause += ' AND r.status = ?';
523
+ params.push(status);
524
+ }
525
+
526
+ const countResult = (
527
+ await db.query(
528
+ `SELECT COUNT(*) as count FROM agent_run r ${whereClause}`,
529
+ params
530
+ )
531
+ ).rows as unknown as { count: number }[];
532
+ const total = countResult[0]?.count ?? 0;
533
+ const sortColumn = sortBy
534
+ ? RUN_SORT_COLUMNS[sortBy]
535
+ : RUN_SORT_COLUMNS.queuedAt;
536
+ const direction = sortDirection === 'asc' ? 'ASC' : 'DESC';
537
+
538
+ const rows = (
539
+ await db.query(
540
+ `SELECT r.*, a.name as agentName
487
541
  FROM agent_run r
488
542
  LEFT JOIN agent_definition a ON r.agentId = a.id
489
543
  ${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
544
+ ORDER BY ${sortColumn} ${direction}, r.queuedAt DESC LIMIT ? OFFSET ?`,
545
+ [...params, limit, offset]
546
+ )
547
+ ).rows as unknown as (RunRow & { agentName: string })[];
548
+
549
+ return {
550
+ items: rows.map((r) => rowToRun(r, r.agentName)),
551
+ total,
552
+ hasMore: offset + rows.length < total,
553
+ };
554
+ }
555
+
556
+ /**
557
+ * Get run metrics
558
+ */
559
+ async function getRunMetrics(input: {
560
+ projectId: string;
561
+ agentId?: string;
562
+ startDate?: Date;
563
+ endDate?: Date;
564
+ }): Promise<RunMetrics> {
565
+ let whereClause = 'WHERE projectId = ?';
566
+ const params: (string | number)[] = [input.projectId];
567
+
568
+ if (input.agentId) {
569
+ whereClause += ' AND agentId = ?';
570
+ params.push(input.agentId);
571
+ }
572
+
573
+ if (input.startDate) {
574
+ whereClause += ' AND queuedAt >= ?';
575
+ params.push(input.startDate.toISOString());
576
+ }
577
+
578
+ if (input.endDate) {
579
+ whereClause += ' AND queuedAt <= ?';
580
+ params.push(input.endDate.toISOString());
581
+ }
582
+
583
+ const result = (
584
+ await db.query(
585
+ `SELECT
532
586
  COUNT(*) as totalRuns,
533
587
  SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) as completedRuns,
534
588
  AVG(CASE WHEN status = 'COMPLETED' THEN durationMs ELSE NULL END) as avgDuration,
535
589
  COALESCE(SUM(totalTokens), 0) as totalTokens,
536
590
  COALESCE(SUM(estimatedCostUsd), 0) as totalCost
537
591
  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
- /**
562
- * Execute an agent (create a run and queue it).
563
- */
564
- async function executeAgent(input: {
565
- agentId: string;
566
- message: string;
567
- context?: { projectId: string; organizationId: string };
568
- }): Promise<Run> {
569
- const agent = await getAgent(input.agentId);
570
- if (!agent) throw new Error('AGENT_NOT_FOUND');
571
- if (agent.status !== 'ACTIVE') throw new Error('AGENT_NOT_ACTIVE');
572
-
573
- const id = generateId('run');
574
- const now = new Date().toISOString();
575
- const pid = input.context?.projectId ?? agent.projectId;
576
-
577
- await db.execute(
578
- `INSERT INTO agent_run (id, projectId, agentId, status, input, totalTokens, promptTokens, completionTokens, estimatedCostUsd, queuedAt)
592
+ params
593
+ )
594
+ ).rows as unknown as {
595
+ totalRuns: number;
596
+ completedRuns: number;
597
+ avgDuration: number | null;
598
+ totalTokens: number;
599
+ totalCost: number;
600
+ }[];
601
+
602
+ const data = result[0];
603
+ const totalRuns = data?.totalRuns ?? 0;
604
+ const completedRuns = data?.completedRuns ?? 0;
605
+
606
+ return {
607
+ totalRuns,
608
+ successRate: totalRuns > 0 ? completedRuns / totalRuns : 0,
609
+ averageDurationMs: data?.avgDuration ?? 0,
610
+ totalTokens: data?.totalTokens ?? 0,
611
+ totalCostUsd: data?.totalCost ?? 0,
612
+ };
613
+ }
614
+
615
+ /**
616
+ * Execute an agent (create a run and queue it).
617
+ */
618
+ async function executeAgent(input: {
619
+ agentId: string;
620
+ message: string;
621
+ context?: { projectId: string; organizationId: string };
622
+ }): Promise<Run> {
623
+ const agent = await getAgent(input.agentId);
624
+ if (!agent) throw new Error('AGENT_NOT_FOUND');
625
+ if (agent.status !== 'ACTIVE') throw new Error('AGENT_NOT_ACTIVE');
626
+
627
+ const id = generateId('run');
628
+ const now = new Date().toISOString();
629
+ const pid = input.context?.projectId ?? agent.projectId;
630
+
631
+ await db.execute(
632
+ `INSERT INTO agent_run (id, projectId, agentId, status, input, totalTokens, promptTokens, completionTokens, estimatedCostUsd, queuedAt)
579
633
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
580
- [id, pid, input.agentId, 'QUEUED', input.message, 0, 0, 0, 0, now]
581
- );
582
-
583
- const rows = (
584
- await db.query(
585
- `SELECT r.*, a.name as agentName FROM agent_run r LEFT JOIN agent_definition a ON r.agentId = a.id WHERE r.id = ?`,
586
- [id]
587
- )
588
- ).rows as unknown as (RunRow & { agentName: string })[];
589
-
590
- if (!rows[0]) throw new Error('Failed to retrieve created run');
591
- return rowToRun(rows[0], rows[0].agentName);
592
- }
593
-
594
- return {
595
- listAgents,
596
- getAgent,
597
- createAgent,
598
- updateAgent,
599
- executeAgent,
600
- listTools,
601
- listRuns,
602
- getRunMetrics,
603
- };
634
+ [id, pid, input.agentId, 'QUEUED', input.message, 0, 0, 0, 0, now]
635
+ );
636
+
637
+ const rows = (
638
+ await db.query(
639
+ `SELECT r.*, a.name as agentName FROM agent_run r LEFT JOIN agent_definition a ON r.agentId = a.id WHERE r.id = ?`,
640
+ [id]
641
+ )
642
+ ).rows as unknown as (RunRow & { agentName: string })[];
643
+
644
+ if (!rows[0]) throw new Error('Failed to retrieve created run');
645
+ return rowToRun(rows[0], rows[0].agentName);
646
+ }
647
+
648
+ return {
649
+ listAgents,
650
+ getAgent,
651
+ createAgent,
652
+ updateAgent,
653
+ executeAgent,
654
+ listTools,
655
+ listRuns,
656
+ getRunMetrics,
657
+ };
604
658
  }
605
659
 
606
660
  export type AgentHandlers = ReturnType<typeof createAgentHandlers>;