@pikku/kysely 0.13.23 → 0.13.27
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.
- package/CHANGELOG.md +248 -0
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.js +3 -1
- package/dist/src/kysely-analytics-service.d.ts +30 -0
- package/dist/src/kysely-analytics-service.js +80 -0
- package/dist/src/kysely-feature-flag-store.d.ts +37 -0
- package/dist/src/kysely-feature-flag-store.js +239 -0
- package/dist/src/kysely-tables.d.ts +45 -0
- package/dist/src/kysely-webhook-service.d.ts +1 -1
- package/dist/src/kysely-webhook-service.js +1 -1
- package/dist/src/schema/analytics.schema.d.ts +17 -0
- package/dist/src/schema/analytics.schema.js +48 -0
- package/dist/src/schema/flag.schema.d.ts +16 -0
- package/dist/src/schema/flag.schema.js +62 -0
- package/dist/src/schema/index.d.ts +2 -0
- package/dist/src/schema/index.js +6 -0
- package/package.json +11 -6
- package/run-tests.sh +9 -1
- package/bin/pikku-kysely-pure.ts +0 -59
- package/dist/src/kysely-lock-vault.d.ts +0 -34
- package/dist/src/kysely-lock-vault.js +0 -77
- package/dist/src/schema/data-lock.schema.d.ts +0 -15
- package/dist/src/schema/data-lock.schema.js +0 -27
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/classification-crypto.test.ts +0 -143
- package/src/classification-crypto.ts +0 -163
- package/src/classification-plugin.pglite.test.ts +0 -226
- package/src/classification-plugin.test.ts +0 -265
- package/src/classification-plugin.ts +0 -249
- package/src/coercion-plugin.test.ts +0 -160
- package/src/coercion-plugin.ts +0 -187
- package/src/create-audited-kysely.test.ts +0 -560
- package/src/create-audited-kysely.ts +0 -254
- package/src/index.ts +0 -81
- package/src/kysely-agent-run-scores.ts +0 -47
- package/src/kysely-agent-run-service.ts +0 -244
- package/src/kysely-agent-run-state-service.ts +0 -200
- package/src/kysely-agent-storage-service.ts +0 -632
- package/src/kysely-audit-service.test.ts +0 -352
- package/src/kysely-audit-service.ts +0 -237
- package/src/kysely-channel-store.ts +0 -107
- package/src/kysely-credential-service.ts +0 -286
- package/src/kysely-deployment-service.ts +0 -163
- package/src/kysely-eventhub-store.ts +0 -53
- package/src/kysely-json.ts +0 -5
- package/src/kysely-scope-service.test.ts +0 -418
- package/src/kysely-scope-service.ts +0 -425
- package/src/kysely-secret-service.ts +0 -242
- package/src/kysely-services.test.ts +0 -405
- package/src/kysely-session-store.ts +0 -57
- package/src/kysely-tables.test.ts +0 -110
- package/src/kysely-tables.ts +0 -434
- package/src/kysely-virtual-user-run-store.test.ts +0 -369
- package/src/kysely-virtual-user-run-store.ts +0 -235
- package/src/kysely-virtual-user-schedule-store.test.ts +0 -187
- package/src/kysely-virtual-user-schedule-store.ts +0 -187
- package/src/kysely-webhook-service.test.ts +0 -124
- package/src/kysely-webhook-service.ts +0 -137
- package/src/kysely-workflow-mirror.test.ts +0 -259
- package/src/kysely-workflow-mirror.ts +0 -374
- package/src/kysely-workflow-run-service.ts +0 -305
- package/src/kysely-workflow-service.test.ts +0 -544
- package/src/kysely-workflow-service.ts +0 -820
- package/src/schema/agent.schema.ts +0 -162
- package/src/schema/audit.schema.ts +0 -57
- package/src/schema/channel.schema.ts +0 -38
- package/src/schema/credential.schema.ts +0 -58
- package/src/schema/deployment.schema.ts +0 -47
- package/src/schema/index.ts +0 -441
- package/src/schema/pikku-schema.types.ts +0 -169
- package/src/schema/schema.test.ts +0 -591
- package/src/schema/scope.schema.ts +0 -110
- package/src/schema/secret.schema.ts +0 -49
- package/src/schema/session.schema.ts +0 -20
- package/src/schema/virtual-user-schedule.schema.ts +0 -48
- package/src/schema/virtual-user.schema.ts +0 -106
- package/src/schema/webhook.schema.ts +0 -58
- package/src/schema/workflow.schema.ts +0 -150
- package/src/serialize-plugin.ts +0 -233
- package/tsconfig.json +0 -17
|
@@ -1,632 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AgentMessage,
|
|
3
|
-
AgentRunState,
|
|
4
|
-
AgentThread,
|
|
5
|
-
} from '@pikku/core/agent'
|
|
6
|
-
import type { AgentRunScore } from '@pikku/core/agent-scorer'
|
|
7
|
-
import type {
|
|
8
|
-
AgentRunStateService,
|
|
9
|
-
AgentStorageService,
|
|
10
|
-
CreateRunInput,
|
|
11
|
-
SaveScoreInput,
|
|
12
|
-
} from '@pikku/core/services'
|
|
13
|
-
import type { Kysely } from 'kysely'
|
|
14
|
-
import type { KyselyPikkuDB } from './kysely-tables.js'
|
|
15
|
-
import { parseJson } from './kysely-json.js'
|
|
16
|
-
import { getRunScores, saveRunScore } from './kysely-agent-run-scores.js'
|
|
17
|
-
import { requirePikkuSchema } from './schema/index.js'
|
|
18
|
-
import { agentSchema } from './schema/agent.schema.js'
|
|
19
|
-
|
|
20
|
-
export class KyselyAgentStorageService
|
|
21
|
-
implements AgentStorageService, AgentRunStateService
|
|
22
|
-
{
|
|
23
|
-
private initialized = false
|
|
24
|
-
|
|
25
|
-
constructor(private db: Kysely<KyselyPikkuDB>) {}
|
|
26
|
-
|
|
27
|
-
public async init(): Promise<void> {
|
|
28
|
-
if (this.initialized) return
|
|
29
|
-
await requirePikkuSchema(this.db, agentSchema)
|
|
30
|
-
this.initialized = true
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async createThread(
|
|
34
|
-
resourceId: string,
|
|
35
|
-
options?: {
|
|
36
|
-
threadId?: string
|
|
37
|
-
title?: string
|
|
38
|
-
metadata?: Record<string, unknown>
|
|
39
|
-
}
|
|
40
|
-
): Promise<AgentThread> {
|
|
41
|
-
const id = options?.threadId ?? crypto.randomUUID()
|
|
42
|
-
const now = new Date()
|
|
43
|
-
|
|
44
|
-
await this.db
|
|
45
|
-
.insertInto('agentThreads')
|
|
46
|
-
.values({
|
|
47
|
-
id,
|
|
48
|
-
resourceId: resourceId,
|
|
49
|
-
title: options?.title ?? null,
|
|
50
|
-
metadata: JSON.stringify(options?.metadata ?? null),
|
|
51
|
-
createdAt: now,
|
|
52
|
-
updatedAt: now,
|
|
53
|
-
})
|
|
54
|
-
.execute()
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
id,
|
|
58
|
-
resourceId,
|
|
59
|
-
title: options?.title,
|
|
60
|
-
metadata: options?.metadata,
|
|
61
|
-
createdAt: now,
|
|
62
|
-
updatedAt: now,
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async getThread(threadId: string): Promise<AgentThread> {
|
|
67
|
-
const row = await this.db
|
|
68
|
-
.selectFrom('agentThreads')
|
|
69
|
-
.select([
|
|
70
|
-
'id',
|
|
71
|
-
'resourceId',
|
|
72
|
-
'title',
|
|
73
|
-
'metadata',
|
|
74
|
-
'createdAt',
|
|
75
|
-
'updatedAt',
|
|
76
|
-
])
|
|
77
|
-
.where('id', '=', threadId)
|
|
78
|
-
.executeTakeFirst()
|
|
79
|
-
|
|
80
|
-
if (!row) {
|
|
81
|
-
throw new Error(`Thread not found: ${threadId}`)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
id: row.id,
|
|
86
|
-
resourceId: row.resourceId,
|
|
87
|
-
title: row.title ?? undefined,
|
|
88
|
-
metadata: parseJson(row.metadata),
|
|
89
|
-
createdAt: new Date(row.createdAt),
|
|
90
|
-
updatedAt: new Date(row.updatedAt),
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async getThreads(resourceId: string): Promise<AgentThread[]> {
|
|
95
|
-
const result = await this.db
|
|
96
|
-
.selectFrom('agentThreads')
|
|
97
|
-
.select([
|
|
98
|
-
'id',
|
|
99
|
-
'resourceId',
|
|
100
|
-
'title',
|
|
101
|
-
'metadata',
|
|
102
|
-
'createdAt',
|
|
103
|
-
'updatedAt',
|
|
104
|
-
])
|
|
105
|
-
.where('resourceId', '=', resourceId)
|
|
106
|
-
.orderBy('updatedAt', 'desc')
|
|
107
|
-
.execute()
|
|
108
|
-
|
|
109
|
-
return result.map((row) => ({
|
|
110
|
-
id: row.id,
|
|
111
|
-
resourceId: row.resourceId,
|
|
112
|
-
title: row.title ?? undefined,
|
|
113
|
-
metadata: parseJson(row.metadata),
|
|
114
|
-
createdAt: new Date(row.createdAt),
|
|
115
|
-
updatedAt: new Date(row.updatedAt),
|
|
116
|
-
}))
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async deleteThread(threadId: string): Promise<void> {
|
|
120
|
-
await this.db
|
|
121
|
-
.deleteFrom('agentThreads')
|
|
122
|
-
.where('id', '=', threadId)
|
|
123
|
-
.execute()
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async getMessages(
|
|
127
|
-
threadId: string,
|
|
128
|
-
options?: { lastN?: number; cursor?: string }
|
|
129
|
-
): Promise<AgentMessage[]> {
|
|
130
|
-
let msgQuery = this.db
|
|
131
|
-
.selectFrom('agentMessage')
|
|
132
|
-
.select(['id', 'role', 'content', 'createdAt'])
|
|
133
|
-
.where('threadId', '=', threadId)
|
|
134
|
-
|
|
135
|
-
if (options?.cursor) {
|
|
136
|
-
const cursorRow = await this.db
|
|
137
|
-
.selectFrom('agentMessage')
|
|
138
|
-
.select('createdAt')
|
|
139
|
-
.where('id', '=', options.cursor)
|
|
140
|
-
.executeTakeFirst()
|
|
141
|
-
|
|
142
|
-
if (cursorRow) {
|
|
143
|
-
msgQuery = msgQuery.where('createdAt', '<', cursorRow.createdAt)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
let msgResult
|
|
148
|
-
if (options?.cursor || options?.lastN) {
|
|
149
|
-
const innerResult = await msgQuery
|
|
150
|
-
.orderBy('createdAt', 'desc')
|
|
151
|
-
.limit(options?.lastN ?? 50)
|
|
152
|
-
.execute()
|
|
153
|
-
innerResult.reverse()
|
|
154
|
-
msgResult = innerResult
|
|
155
|
-
} else {
|
|
156
|
-
msgResult = await msgQuery.orderBy('createdAt', 'asc').execute()
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const tcResult = await this.db
|
|
160
|
-
.selectFrom('agentToolCall')
|
|
161
|
-
.select(['id', 'messageId', 'toolName', 'args', 'result'])
|
|
162
|
-
.where('threadId', '=', threadId)
|
|
163
|
-
.orderBy('createdAt', 'asc')
|
|
164
|
-
.execute()
|
|
165
|
-
|
|
166
|
-
const tcByMessage = new Map<string, (typeof tcResult)[number][]>()
|
|
167
|
-
for (const tc of tcResult) {
|
|
168
|
-
const msgId = tc.messageId
|
|
169
|
-
if (!tcByMessage.has(msgId)) tcByMessage.set(msgId, [])
|
|
170
|
-
tcByMessage.get(msgId)!.push(tc)
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const messages: AgentMessage[] = []
|
|
174
|
-
for (const row of msgResult) {
|
|
175
|
-
const rawContent = row.content as string | undefined
|
|
176
|
-
let parsedContent: AgentMessage['content'] = rawContent ?? undefined
|
|
177
|
-
if (rawContent) {
|
|
178
|
-
try {
|
|
179
|
-
const parsed = JSON.parse(rawContent)
|
|
180
|
-
if (Array.isArray(parsed)) {
|
|
181
|
-
parsedContent = parsed
|
|
182
|
-
} else if (typeof parsed === 'string') {
|
|
183
|
-
parsedContent = parsed
|
|
184
|
-
}
|
|
185
|
-
} catch {
|
|
186
|
-
// Not JSON, use raw string
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
const msg: AgentMessage = {
|
|
190
|
-
id: row.id,
|
|
191
|
-
role: row.role as AgentMessage['role'],
|
|
192
|
-
content: parsedContent,
|
|
193
|
-
createdAt: new Date(row.createdAt),
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const tcs = tcByMessage.get(msg.id)
|
|
197
|
-
if (tcs?.length) {
|
|
198
|
-
msg.toolCalls = tcs.map((tc) => ({
|
|
199
|
-
id: tc.id,
|
|
200
|
-
name: tc.toolName,
|
|
201
|
-
args: parseJson(tc.args) as Record<string, unknown>,
|
|
202
|
-
}))
|
|
203
|
-
|
|
204
|
-
const completed = tcs.filter((tc) => tc.result != null)
|
|
205
|
-
if (completed.length) {
|
|
206
|
-
messages.push(msg)
|
|
207
|
-
messages.push({
|
|
208
|
-
id: `tool-results-${msg.id}`,
|
|
209
|
-
role: 'tool',
|
|
210
|
-
toolResults: completed.map((tc) => ({
|
|
211
|
-
id: tc.id,
|
|
212
|
-
name: tc.toolName,
|
|
213
|
-
result: tc.result!,
|
|
214
|
-
})),
|
|
215
|
-
createdAt: msg.createdAt,
|
|
216
|
-
})
|
|
217
|
-
continue
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
messages.push(msg)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return messages
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
async saveMessages(
|
|
228
|
-
threadId: string,
|
|
229
|
-
messages: AgentMessage[]
|
|
230
|
-
): Promise<void> {
|
|
231
|
-
if (messages.length === 0) return
|
|
232
|
-
|
|
233
|
-
const toolMessages = messages.filter((m) => m.role === 'tool')
|
|
234
|
-
const nonToolMessages = messages.filter((m) => m.role !== 'tool')
|
|
235
|
-
|
|
236
|
-
if (nonToolMessages.length > 0) {
|
|
237
|
-
await this.db
|
|
238
|
-
.insertInto('agentMessage')
|
|
239
|
-
.values(
|
|
240
|
-
nonToolMessages.map((msg) => ({
|
|
241
|
-
id: msg.id,
|
|
242
|
-
threadId: threadId,
|
|
243
|
-
role: msg.role,
|
|
244
|
-
content: msg.content != null ? JSON.stringify(msg.content) : null,
|
|
245
|
-
createdAt: msg.createdAt ?? new Date(),
|
|
246
|
-
}))
|
|
247
|
-
)
|
|
248
|
-
.execute()
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const toolCalls = nonToolMessages.flatMap(
|
|
252
|
-
(msg) => msg.toolCalls?.map((tc) => ({ ...tc, messageId: msg.id })) ?? []
|
|
253
|
-
)
|
|
254
|
-
if (toolCalls.length > 0) {
|
|
255
|
-
await this.db
|
|
256
|
-
.insertInto('agentToolCall')
|
|
257
|
-
.values(
|
|
258
|
-
toolCalls.map((tc) => ({
|
|
259
|
-
id: tc.id,
|
|
260
|
-
threadId: threadId,
|
|
261
|
-
messageId: tc.messageId,
|
|
262
|
-
toolName: tc.name,
|
|
263
|
-
args: JSON.stringify(tc.args),
|
|
264
|
-
}))
|
|
265
|
-
)
|
|
266
|
-
.execute()
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
for (const toolMsg of toolMessages) {
|
|
270
|
-
if (!toolMsg.toolResults) continue
|
|
271
|
-
for (const tr of toolMsg.toolResults) {
|
|
272
|
-
await this.db
|
|
273
|
-
.updateTable('agentToolCall')
|
|
274
|
-
.set({ result: tr.result })
|
|
275
|
-
.where('id', '=', tr.id)
|
|
276
|
-
.execute()
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
await this.db
|
|
281
|
-
.updateTable('agentThreads')
|
|
282
|
-
.set({ updatedAt: new Date() })
|
|
283
|
-
.where('id', '=', threadId)
|
|
284
|
-
.execute()
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
async getWorkingMemory(
|
|
288
|
-
id: string,
|
|
289
|
-
scope: 'resource' | 'thread'
|
|
290
|
-
): Promise<Record<string, unknown> | null> {
|
|
291
|
-
const row = await this.db
|
|
292
|
-
.selectFrom('agentWorkingMemory')
|
|
293
|
-
.select('data')
|
|
294
|
-
.where('id', '=', id)
|
|
295
|
-
.where('scope', '=', scope)
|
|
296
|
-
.executeTakeFirst()
|
|
297
|
-
|
|
298
|
-
if (!row) return null
|
|
299
|
-
return parseJson(row.data)
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
async saveWorkingMemory(
|
|
303
|
-
id: string,
|
|
304
|
-
scope: 'resource' | 'thread',
|
|
305
|
-
data: Record<string, unknown>
|
|
306
|
-
): Promise<void> {
|
|
307
|
-
await this.db
|
|
308
|
-
.insertInto('agentWorkingMemory')
|
|
309
|
-
.values({
|
|
310
|
-
id,
|
|
311
|
-
scope,
|
|
312
|
-
data: JSON.stringify(data),
|
|
313
|
-
updatedAt: new Date(),
|
|
314
|
-
})
|
|
315
|
-
.onConflict((oc) =>
|
|
316
|
-
oc.columns(['id', 'scope']).doUpdateSet({
|
|
317
|
-
data: JSON.stringify(data),
|
|
318
|
-
updatedAt: new Date(),
|
|
319
|
-
})
|
|
320
|
-
)
|
|
321
|
-
.execute()
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
async createRun(run: CreateRunInput): Promise<string> {
|
|
325
|
-
const runId = crypto.randomUUID()
|
|
326
|
-
|
|
327
|
-
await this.db
|
|
328
|
-
.insertInto('agentRun')
|
|
329
|
-
.values({
|
|
330
|
-
runId: runId,
|
|
331
|
-
agentName: run.agentName,
|
|
332
|
-
threadId: run.threadId,
|
|
333
|
-
resourceId: run.resourceId,
|
|
334
|
-
status: run.status,
|
|
335
|
-
errorMessage: run.errorMessage ?? null,
|
|
336
|
-
suspendReason: run.suspendReason ?? null,
|
|
337
|
-
missingRpcs: run.missingRpcs ? JSON.stringify(run.missingRpcs) : null,
|
|
338
|
-
usageInputTokens: run.usage.inputTokens,
|
|
339
|
-
usageOutputTokens: run.usage.outputTokens,
|
|
340
|
-
usageModel: run.usage.model,
|
|
341
|
-
createdAt: run.createdAt,
|
|
342
|
-
updatedAt: run.updatedAt,
|
|
343
|
-
})
|
|
344
|
-
.execute()
|
|
345
|
-
|
|
346
|
-
if (run.pendingApprovals?.length) {
|
|
347
|
-
await this.setApprovals(runId, run.pendingApprovals)
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
return runId
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
async updateRun(
|
|
354
|
-
runId: string,
|
|
355
|
-
updates: Partial<AgentRunState>
|
|
356
|
-
): Promise<void> {
|
|
357
|
-
const setValues: Record<string, unknown> = { updatedAt: new Date() }
|
|
358
|
-
|
|
359
|
-
if (updates.status !== undefined) {
|
|
360
|
-
setValues.status = updates.status
|
|
361
|
-
}
|
|
362
|
-
if (updates.errorMessage !== undefined) {
|
|
363
|
-
setValues.errorMessage = updates.errorMessage
|
|
364
|
-
}
|
|
365
|
-
if (updates.suspendReason !== undefined) {
|
|
366
|
-
setValues.suspendReason = updates.suspendReason
|
|
367
|
-
}
|
|
368
|
-
if (updates.missingRpcs !== undefined) {
|
|
369
|
-
setValues.missingRpcs = JSON.stringify(updates.missingRpcs)
|
|
370
|
-
}
|
|
371
|
-
if (updates.usage !== undefined) {
|
|
372
|
-
setValues.usageInputTokens = updates.usage.inputTokens
|
|
373
|
-
setValues.usageOutputTokens = updates.usage.outputTokens
|
|
374
|
-
setValues.usageModel = updates.usage.model
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
await this.db
|
|
378
|
-
.updateTable('agentRun')
|
|
379
|
-
.set(setValues)
|
|
380
|
-
.where('runId', '=', runId)
|
|
381
|
-
.execute()
|
|
382
|
-
|
|
383
|
-
if (updates.pendingApprovals !== undefined) {
|
|
384
|
-
await this.db
|
|
385
|
-
.updateTable('agentToolCall')
|
|
386
|
-
.set({
|
|
387
|
-
approvalStatus: null,
|
|
388
|
-
runId: null,
|
|
389
|
-
approvalType: null,
|
|
390
|
-
agentRunId: null,
|
|
391
|
-
displayToolName: null,
|
|
392
|
-
displayArgs: null,
|
|
393
|
-
})
|
|
394
|
-
.where('runId', '=', runId)
|
|
395
|
-
.where('approvalStatus', 'is not', null)
|
|
396
|
-
.execute()
|
|
397
|
-
|
|
398
|
-
if (updates.pendingApprovals.length) {
|
|
399
|
-
await this.setApprovals(runId, updates.pendingApprovals)
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
async getRun(runId: string): Promise<AgentRunState | null> {
|
|
405
|
-
const row = await this.db
|
|
406
|
-
.selectFrom('agentRun')
|
|
407
|
-
.select([
|
|
408
|
-
'runId',
|
|
409
|
-
'agentName',
|
|
410
|
-
'threadId',
|
|
411
|
-
'resourceId',
|
|
412
|
-
'status',
|
|
413
|
-
'errorMessage',
|
|
414
|
-
'suspendReason',
|
|
415
|
-
'missingRpcs',
|
|
416
|
-
'usageInputTokens',
|
|
417
|
-
'usageOutputTokens',
|
|
418
|
-
'usageModel',
|
|
419
|
-
'createdAt',
|
|
420
|
-
'updatedAt',
|
|
421
|
-
])
|
|
422
|
-
.where('runId', '=', runId)
|
|
423
|
-
.executeTakeFirst()
|
|
424
|
-
|
|
425
|
-
if (!row) return null
|
|
426
|
-
|
|
427
|
-
const approvals = await this.db
|
|
428
|
-
.selectFrom('agentToolCall')
|
|
429
|
-
.select([
|
|
430
|
-
'id',
|
|
431
|
-
'toolName',
|
|
432
|
-
'args',
|
|
433
|
-
'approvalType',
|
|
434
|
-
'agentRunId',
|
|
435
|
-
'displayToolName',
|
|
436
|
-
'displayArgs',
|
|
437
|
-
])
|
|
438
|
-
.where('runId', '=', runId)
|
|
439
|
-
.where('approvalStatus', '=', 'pending')
|
|
440
|
-
.execute()
|
|
441
|
-
|
|
442
|
-
return this.mapRunRow(row, approvals)
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
async getRunsByThread(threadId: string): Promise<AgentRunState[]> {
|
|
446
|
-
const result = await this.db
|
|
447
|
-
.selectFrom('agentRun')
|
|
448
|
-
.select([
|
|
449
|
-
'runId',
|
|
450
|
-
'agentName',
|
|
451
|
-
'threadId',
|
|
452
|
-
'resourceId',
|
|
453
|
-
'status',
|
|
454
|
-
'errorMessage',
|
|
455
|
-
'suspendReason',
|
|
456
|
-
'missingRpcs',
|
|
457
|
-
'usageInputTokens',
|
|
458
|
-
'usageOutputTokens',
|
|
459
|
-
'usageModel',
|
|
460
|
-
'createdAt',
|
|
461
|
-
'updatedAt',
|
|
462
|
-
])
|
|
463
|
-
.where('threadId', '=', threadId)
|
|
464
|
-
.orderBy('createdAt', 'desc')
|
|
465
|
-
.execute()
|
|
466
|
-
|
|
467
|
-
const runs: AgentRunState[] = []
|
|
468
|
-
for (const row of result) {
|
|
469
|
-
const approvals = await this.db
|
|
470
|
-
.selectFrom('agentToolCall')
|
|
471
|
-
.select([
|
|
472
|
-
'id',
|
|
473
|
-
'toolName',
|
|
474
|
-
'args',
|
|
475
|
-
'approvalType',
|
|
476
|
-
'agentRunId',
|
|
477
|
-
'displayToolName',
|
|
478
|
-
'displayArgs',
|
|
479
|
-
])
|
|
480
|
-
.where('runId', '=', row.runId)
|
|
481
|
-
.where('approvalStatus', '=', 'pending')
|
|
482
|
-
.execute()
|
|
483
|
-
runs.push(this.mapRunRow(row, approvals))
|
|
484
|
-
}
|
|
485
|
-
return runs
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
private async setApprovals(
|
|
489
|
-
runId: string,
|
|
490
|
-
approvals: NonNullable<AgentRunState['pendingApprovals']>
|
|
491
|
-
): Promise<void> {
|
|
492
|
-
for (const a of approvals) {
|
|
493
|
-
if (a.type === 'agent-call') {
|
|
494
|
-
await this.db
|
|
495
|
-
.updateTable('agentToolCall')
|
|
496
|
-
.set({
|
|
497
|
-
approvalStatus: 'pending',
|
|
498
|
-
runId: runId,
|
|
499
|
-
approvalType: 'agent-call',
|
|
500
|
-
agentRunId: a.agentRunId,
|
|
501
|
-
displayToolName: a.displayToolName,
|
|
502
|
-
displayArgs: JSON.stringify(a.displayArgs),
|
|
503
|
-
})
|
|
504
|
-
.where('id', '=', a.toolCallId)
|
|
505
|
-
.execute()
|
|
506
|
-
} else {
|
|
507
|
-
await this.db
|
|
508
|
-
.updateTable('agentToolCall')
|
|
509
|
-
.set({
|
|
510
|
-
approvalStatus: 'pending',
|
|
511
|
-
runId: runId,
|
|
512
|
-
approvalType: 'tool-call',
|
|
513
|
-
})
|
|
514
|
-
.where('id', '=', a.toolCallId)
|
|
515
|
-
.execute()
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
private mapRunRow(row: any, approvalRows?: any[]): AgentRunState {
|
|
521
|
-
const pendingApprovals = approvalRows?.length
|
|
522
|
-
? approvalRows.map((a: any) => {
|
|
523
|
-
if (a.approvalType === 'agent-call') {
|
|
524
|
-
return {
|
|
525
|
-
type: 'agent-call' as const,
|
|
526
|
-
toolCallId: a.id as string,
|
|
527
|
-
agentName: a.toolName as string,
|
|
528
|
-
agentRunId: a.agentRunId as string,
|
|
529
|
-
displayToolName: a.displayToolName as string,
|
|
530
|
-
displayArgs: parseJson(a.displayArgs) as unknown,
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
return {
|
|
534
|
-
type: 'tool-call' as const,
|
|
535
|
-
toolCallId: a.id as string,
|
|
536
|
-
toolName: a.toolName as string,
|
|
537
|
-
args: parseJson(a.args) as unknown,
|
|
538
|
-
}
|
|
539
|
-
})
|
|
540
|
-
: undefined
|
|
541
|
-
|
|
542
|
-
return {
|
|
543
|
-
runId: row.runId as string,
|
|
544
|
-
agentName: row.agentName as string,
|
|
545
|
-
threadId: row.threadId as string,
|
|
546
|
-
resourceId: row.resourceId as string,
|
|
547
|
-
status: row.status as AgentRunState['status'],
|
|
548
|
-
errorMessage: row.errorMessage ?? undefined,
|
|
549
|
-
suspendReason: row.suspendReason as AgentRunState['suspendReason'],
|
|
550
|
-
missingRpcs: parseJson(row.missingRpcs),
|
|
551
|
-
pendingApprovals,
|
|
552
|
-
usage: {
|
|
553
|
-
inputTokens: Number(row.usageInputTokens),
|
|
554
|
-
outputTokens: Number(row.usageOutputTokens),
|
|
555
|
-
model: row.usageModel as string,
|
|
556
|
-
},
|
|
557
|
-
createdAt: new Date(row.createdAt as string),
|
|
558
|
-
updatedAt: new Date(row.updatedAt as string),
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
async findRunByToolCallId(toolCallId: string): Promise<{
|
|
563
|
-
run: AgentRunState
|
|
564
|
-
approval: NonNullable<AgentRunState['pendingApprovals']>[number]
|
|
565
|
-
} | null> {
|
|
566
|
-
const tc = await this.db
|
|
567
|
-
.selectFrom('agentToolCall')
|
|
568
|
-
.select([
|
|
569
|
-
'id',
|
|
570
|
-
'toolName',
|
|
571
|
-
'args',
|
|
572
|
-
'runId',
|
|
573
|
-
'approvalType',
|
|
574
|
-
'agentRunId',
|
|
575
|
-
'displayToolName',
|
|
576
|
-
'displayArgs',
|
|
577
|
-
])
|
|
578
|
-
.where('id', '=', toolCallId)
|
|
579
|
-
.where('approvalStatus', '=', 'pending')
|
|
580
|
-
.executeTakeFirst()
|
|
581
|
-
|
|
582
|
-
if (!tc || !tc.runId) return null
|
|
583
|
-
|
|
584
|
-
const run = await this.getRun(tc.runId)
|
|
585
|
-
if (!run) return null
|
|
586
|
-
|
|
587
|
-
let approval: NonNullable<AgentRunState['pendingApprovals']>[number]
|
|
588
|
-
if (tc.approvalType === 'agent-call') {
|
|
589
|
-
approval = {
|
|
590
|
-
type: 'agent-call',
|
|
591
|
-
toolCallId: tc.id,
|
|
592
|
-
agentName: tc.toolName,
|
|
593
|
-
agentRunId: tc.agentRunId!,
|
|
594
|
-
displayToolName: tc.displayToolName!,
|
|
595
|
-
displayArgs: parseJson(tc.displayArgs) as unknown,
|
|
596
|
-
}
|
|
597
|
-
} else {
|
|
598
|
-
approval = {
|
|
599
|
-
type: 'tool-call',
|
|
600
|
-
toolCallId: tc.id,
|
|
601
|
-
toolName: tc.toolName,
|
|
602
|
-
args: parseJson(tc.args) as unknown,
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
return { run, approval }
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
async resolveApproval(
|
|
610
|
-
toolCallId: string,
|
|
611
|
-
status: 'approved' | 'denied'
|
|
612
|
-
): Promise<boolean> {
|
|
613
|
-
// Only the caller that moves the tool call off 'pending' has claimed it.
|
|
614
|
-
const result = await this.db
|
|
615
|
-
.updateTable('agentToolCall')
|
|
616
|
-
.set({ approvalStatus: status })
|
|
617
|
-
.where('id', '=', toolCallId)
|
|
618
|
-
.where('approvalStatus', '=', 'pending')
|
|
619
|
-
.executeTakeFirst()
|
|
620
|
-
return Number(result?.numUpdatedRows ?? 0) > 0
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
async saveScore(score: SaveScoreInput): Promise<void> {
|
|
624
|
-
await saveRunScore(this.db, score)
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
async getScores(runId: string): Promise<AgentRunScore[]> {
|
|
628
|
-
return getRunScores(this.db, runId)
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
public async close(): Promise<void> {}
|
|
632
|
-
}
|