@contractspec/example.agent-console 3.7.5 → 3.7.7

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