@pikku/kysely 0.13.24 → 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.
Files changed (78) hide show
  1. package/CHANGELOG.md +235 -0
  2. package/dist/src/index.d.ts +4 -1
  3. package/dist/src/index.js +3 -1
  4. package/dist/src/kysely-analytics-service.d.ts +30 -0
  5. package/dist/src/kysely-analytics-service.js +80 -0
  6. package/dist/src/kysely-feature-flag-store.d.ts +37 -0
  7. package/dist/src/kysely-feature-flag-store.js +239 -0
  8. package/dist/src/kysely-tables.d.ts +45 -0
  9. package/dist/src/schema/analytics.schema.d.ts +17 -0
  10. package/dist/src/schema/analytics.schema.js +48 -0
  11. package/dist/src/schema/flag.schema.d.ts +16 -0
  12. package/dist/src/schema/flag.schema.js +62 -0
  13. package/dist/src/schema/index.d.ts +2 -0
  14. package/dist/src/schema/index.js +6 -0
  15. package/package.json +11 -6
  16. package/run-tests.sh +9 -1
  17. package/bin/pikku-kysely-pure.ts +0 -59
  18. package/dist/src/kysely-lock-vault.d.ts +0 -34
  19. package/dist/src/kysely-lock-vault.js +0 -77
  20. package/dist/src/schema/data-lock.schema.d.ts +0 -15
  21. package/dist/src/schema/data-lock.schema.js +0 -27
  22. package/dist/tsconfig.tsbuildinfo +0 -1
  23. package/src/classification-crypto.test.ts +0 -143
  24. package/src/classification-crypto.ts +0 -163
  25. package/src/classification-plugin.pglite.test.ts +0 -226
  26. package/src/classification-plugin.test.ts +0 -265
  27. package/src/classification-plugin.ts +0 -249
  28. package/src/coercion-plugin.test.ts +0 -160
  29. package/src/coercion-plugin.ts +0 -187
  30. package/src/create-audited-kysely.test.ts +0 -560
  31. package/src/create-audited-kysely.ts +0 -254
  32. package/src/index.ts +0 -81
  33. package/src/kysely-agent-run-scores.ts +0 -47
  34. package/src/kysely-agent-run-service.ts +0 -244
  35. package/src/kysely-agent-run-state-service.ts +0 -200
  36. package/src/kysely-agent-storage-service.ts +0 -632
  37. package/src/kysely-audit-service.test.ts +0 -352
  38. package/src/kysely-audit-service.ts +0 -237
  39. package/src/kysely-channel-store.ts +0 -107
  40. package/src/kysely-credential-service.ts +0 -286
  41. package/src/kysely-deployment-service.ts +0 -163
  42. package/src/kysely-eventhub-store.ts +0 -53
  43. package/src/kysely-json.ts +0 -5
  44. package/src/kysely-scope-service.test.ts +0 -418
  45. package/src/kysely-scope-service.ts +0 -425
  46. package/src/kysely-secret-service.ts +0 -242
  47. package/src/kysely-services.test.ts +0 -405
  48. package/src/kysely-session-store.ts +0 -57
  49. package/src/kysely-tables.test.ts +0 -110
  50. package/src/kysely-tables.ts +0 -434
  51. package/src/kysely-virtual-user-run-store.test.ts +0 -369
  52. package/src/kysely-virtual-user-run-store.ts +0 -235
  53. package/src/kysely-virtual-user-schedule-store.test.ts +0 -187
  54. package/src/kysely-virtual-user-schedule-store.ts +0 -187
  55. package/src/kysely-webhook-service.test.ts +0 -153
  56. package/src/kysely-webhook-service.ts +0 -139
  57. package/src/kysely-workflow-mirror.test.ts +0 -259
  58. package/src/kysely-workflow-mirror.ts +0 -374
  59. package/src/kysely-workflow-run-service.ts +0 -305
  60. package/src/kysely-workflow-service.test.ts +0 -544
  61. package/src/kysely-workflow-service.ts +0 -820
  62. package/src/schema/agent.schema.ts +0 -162
  63. package/src/schema/audit.schema.ts +0 -57
  64. package/src/schema/channel.schema.ts +0 -38
  65. package/src/schema/credential.schema.ts +0 -58
  66. package/src/schema/deployment.schema.ts +0 -47
  67. package/src/schema/index.ts +0 -441
  68. package/src/schema/pikku-schema.types.ts +0 -169
  69. package/src/schema/schema.test.ts +0 -591
  70. package/src/schema/scope.schema.ts +0 -110
  71. package/src/schema/secret.schema.ts +0 -49
  72. package/src/schema/session.schema.ts +0 -20
  73. package/src/schema/virtual-user-schedule.schema.ts +0 -48
  74. package/src/schema/virtual-user.schema.ts +0 -106
  75. package/src/schema/webhook.schema.ts +0 -58
  76. package/src/schema/workflow.schema.ts +0 -150
  77. package/src/serialize-plugin.ts +0 -233
  78. package/tsconfig.json +0 -17
@@ -1,254 +0,0 @@
1
- import type { AuditLog } from '@pikku/core/services'
2
- import type {
3
- Kysely,
4
- KyselyPlugin,
5
- PluginTransformQueryArgs,
6
- PluginTransformResultArgs,
7
- QueryResult,
8
- RootOperationNode,
9
- UnknownRow,
10
- } from 'kysely'
11
-
12
- export interface CreateAuditedKyselyOptions {
13
- audit: AuditLog
14
- auditReads?: boolean
15
- eventType?: string
16
- transactionId?: string | null
17
- queryIdPrefix?: string
18
- }
19
-
20
- type AuditedQuery = {
21
- queryKind: 'select' | 'insert' | 'update' | 'delete'
22
- queryId: string
23
- tables: string[]
24
- changedColumns?: string[]
25
- changes?: unknown
26
- }
27
-
28
- const QUERY_KIND_BY_NODE: Record<
29
- string,
30
- AuditedQuery['queryKind'] | undefined
31
- > = {
32
- SelectQueryNode: 'select',
33
- InsertQueryNode: 'insert',
34
- UpdateQueryNode: 'update',
35
- DeleteQueryNode: 'delete',
36
- }
37
-
38
- function getIdentifierName(node: unknown): string | undefined {
39
- if (!node || typeof node !== 'object') return undefined
40
- const candidate = node as {
41
- name?: string
42
- column?: { name?: string }
43
- identifier?: { name?: string }
44
- table?: { identifier?: { name?: string } }
45
- }
46
- return (
47
- candidate.name ??
48
- candidate.column?.name ??
49
- candidate.identifier?.name ??
50
- candidate.table?.identifier?.name
51
- )
52
- }
53
-
54
- function collectTableNames(node: unknown, out: Set<string>): void {
55
- if (!node || typeof node !== 'object') return
56
-
57
- const candidate = node as {
58
- kind?: string
59
- table?: { identifier?: { name?: string } }
60
- }
61
-
62
- if (
63
- candidate.kind === 'TableNode' &&
64
- typeof candidate.table?.identifier?.name === 'string'
65
- ) {
66
- out.add(candidate.table.identifier.name)
67
- }
68
-
69
- for (const value of Object.values(node as Record<string, unknown>)) {
70
- if (Array.isArray(value)) {
71
- for (const item of value) collectTableNames(item, out)
72
- continue
73
- }
74
- collectTableNames(value, out)
75
- }
76
- }
77
-
78
- function extractValue(node: unknown): unknown {
79
- if (!node || typeof node !== 'object') return node
80
-
81
- const candidate = node as {
82
- kind?: string
83
- value?: unknown
84
- values?: unknown[]
85
- left?: unknown
86
- right?: unknown
87
- }
88
-
89
- switch (candidate.kind) {
90
- case 'ValueNode':
91
- return candidate.value
92
- case 'PrimitiveValueListNode':
93
- case 'ValueListNode':
94
- return (candidate.values ?? []).map(extractValue)
95
- case 'DefaultInsertValueNode':
96
- return '[default]'
97
- case 'ColumnNode':
98
- case 'ReferenceNode':
99
- return `[${candidate.kind}]`
100
- case 'RawNode':
101
- return '[raw]'
102
- case 'SelectQueryNode':
103
- return '[subquery]'
104
- case 'ParensNode':
105
- return extractValue((candidate as { node?: unknown }).node)
106
- case 'BinaryOperationNode':
107
- return {
108
- left: extractValue(candidate.left),
109
- right: extractValue(candidate.right),
110
- }
111
- default:
112
- return `[${candidate.kind ?? 'expression'}]`
113
- }
114
- }
115
-
116
- function extractInsertChanges(node: any): {
117
- changedColumns?: string[]
118
- changes?: unknown
119
- } {
120
- const columns = Array.isArray(node.columns)
121
- ? node.columns
122
- .map((column: unknown) => getIdentifierName(column))
123
- .filter(Boolean)
124
- : []
125
- const values = Array.isArray(node.values?.values) ? node.values.values : []
126
- if (columns.length === 0 || values.length === 0) {
127
- return {
128
- changedColumns: columns.length > 0 ? columns : undefined,
129
- }
130
- }
131
-
132
- const rows = values.map((row: any) => {
133
- const rowValues = Array.isArray(row?.values)
134
- ? row.values.map(extractValue)
135
- : []
136
- return Object.fromEntries(
137
- columns.map((column, index) => [column, rowValues[index]])
138
- )
139
- })
140
-
141
- return {
142
- changedColumns: columns,
143
- changes: rows.length === 1 ? rows[0] : rows,
144
- }
145
- }
146
-
147
- function extractUpdateChanges(node: any): {
148
- changedColumns?: string[]
149
- changes?: unknown
150
- } {
151
- const updates = Array.isArray(node.updates) ? node.updates : []
152
- const entries = updates
153
- .map((update: any) => {
154
- const column = getIdentifierName(update.column)
155
- if (!column) return null
156
- return [column, extractValue(update.value)] as const
157
- })
158
- .filter(Boolean) as Array<readonly [string, unknown]>
159
-
160
- if (entries.length === 0) {
161
- return {}
162
- }
163
-
164
- return {
165
- changedColumns: entries.map(([column]) => column),
166
- changes: Object.fromEntries(entries),
167
- }
168
- }
169
-
170
- function extractChangeSet(
171
- node: RootOperationNode,
172
- queryKind: AuditedQuery['queryKind']
173
- ) {
174
- if (queryKind === 'insert') {
175
- return extractInsertChanges(node as any)
176
- }
177
- if (queryKind === 'update') {
178
- return extractUpdateChanges(node as any)
179
- }
180
- return {}
181
- }
182
-
183
- class AuditedKyselyPlugin implements KyselyPlugin {
184
- private readonly pending = new Map<unknown, AuditedQuery>()
185
- private queryCounter = 0
186
-
187
- constructor(private readonly options: CreateAuditedKyselyOptions) {}
188
-
189
- transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
190
- const queryKind =
191
- QUERY_KIND_BY_NODE[(args.node as { kind?: string }).kind ?? '']
192
- if (!queryKind) {
193
- return args.node
194
- }
195
-
196
- if (queryKind === 'select' && this.options.auditReads === false) {
197
- return args.node
198
- }
199
-
200
- const tables = new Set<string>()
201
- collectTableNames(args.node, tables)
202
- const queryId = `${this.options.queryIdPrefix ?? 'q'}-${++this.queryCounter}`
203
- this.pending.set(args.queryId, {
204
- queryKind,
205
- queryId,
206
- tables: [...tables].sort(),
207
- ...extractChangeSet(args.node, queryKind),
208
- })
209
- return args.node
210
- }
211
-
212
- async transformResult(
213
- args: PluginTransformResultArgs
214
- ): Promise<QueryResult<UnknownRow>> {
215
- const pending = this.pending.get(args.queryId)
216
- if (!pending) {
217
- return args.result
218
- }
219
-
220
- this.pending.delete(args.queryId)
221
- const rawResult = args.result as QueryResult<UnknownRow> & {
222
- numAffectedRows?: bigint
223
- numUpdatedOrDeletedRows?: bigint
224
- }
225
- const rowCount =
226
- rawResult.rows.length > 0
227
- ? rawResult.rows.length
228
- : Number(
229
- rawResult.numUpdatedOrDeletedRows ?? rawResult.numAffectedRows ?? 0n
230
- )
231
-
232
- await this.options.audit.write({
233
- type: this.options.eventType ?? 'db.query',
234
- source: 'auto',
235
- transactionId: this.options.transactionId ?? null,
236
- queryId: pending.queryId,
237
- metadata: {
238
- queryKind: pending.queryKind,
239
- tables: pending.tables,
240
- changedColumns: pending.changedColumns,
241
- changes: pending.changes,
242
- rowCount,
243
- },
244
- })
245
- return rawResult
246
- }
247
- }
248
-
249
- export function createAuditedKysely<DB>(
250
- db: Kysely<DB>,
251
- options: CreateAuditedKyselyOptions
252
- ): Kysely<DB> {
253
- return db.withPlugin(new AuditedKyselyPlugin(options))
254
- }
package/src/index.ts DELETED
@@ -1,81 +0,0 @@
1
- export { KyselyChannelStore } from './kysely-channel-store.js'
2
- export { KyselyEventHubStore } from './kysely-eventhub-store.js'
3
- export { KyselyWorkflowService } from './kysely-workflow-service.js'
4
- export { KyselyWorkflowRunService } from './kysely-workflow-run-service.js'
5
- export { KyselyWorkflowMirror } from './kysely-workflow-mirror.js'
6
- export { KyselyDeploymentService } from './kysely-deployment-service.js'
7
- export { KyselyAgentStorageService } from './kysely-agent-storage-service.js'
8
- export { KyselyAgentRunService } from './kysely-agent-run-service.js'
9
- export { KyselyAgentRunStateService } from './kysely-agent-run-state-service.js'
10
- export { KyselySecretService } from './kysely-secret-service.js'
11
- export { KyselyCredentialService } from './kysely-credential-service.js'
12
- export { KyselySessionStore } from './kysely-session-store.js'
13
- export { KyselyScopeService } from './kysely-scope-service.js'
14
- export { KyselyWebhookService } from './kysely-webhook-service.js'
15
- export {
16
- createAuditedKysely,
17
- type CreateAuditedKyselyOptions,
18
- } from './create-audited-kysely.js'
19
- export { KyselyAuditService } from './kysely-audit-service.js'
20
- export { KyselyVirtualUserRunStore } from './kysely-virtual-user-run-store.js'
21
- export { KyselyVirtualUserScheduleStore } from './kysely-virtual-user-schedule-store.js'
22
-
23
- export {
24
- SerializePlugin,
25
- BaseSerializePlugin,
26
- type Serializer,
27
- type Deserializer,
28
- } from './serialize-plugin.js'
29
- export {
30
- createCoercionPlugin,
31
- type ColumnKind,
32
- type CoercionMap,
33
- type CreateCoercionPluginOptions,
34
- } from './coercion-plugin.js'
35
- export {
36
- createClassificationPlugin,
37
- type CreateClassificationPluginOptions,
38
- } from './classification-plugin.js'
39
- export {
40
- ClassificationCrypto,
41
- DEFAULT_KEY_ID,
42
- isColumnEnvelope,
43
- parseColumnEnvelope,
44
- type ClassificationCryptoOptions,
45
- type ColumnEnvelope,
46
- type KEKResolver,
47
- type ResolvedKEK,
48
- } from './classification-crypto.js'
49
- export {
50
- agentSchema,
51
- auditSchema,
52
- channelSchema,
53
- credentialSchema,
54
- deploymentSchema,
55
- scopeSchema,
56
- secretSchema,
57
- sessionSchema,
58
- virtualUserSchema,
59
- webhookSchema,
60
- workflowSchema,
61
- pikkuSchemas,
62
- requiredPikkuSchemas,
63
- applyPikkuSchemas,
64
- compilePikkuSchemas,
65
- requirePikkuSchema,
66
- resolveRequirements,
67
- type PikkuSchema,
68
- type RequiredTypes,
69
- type UnmetRequirement,
70
- type SchemaStatementFactory,
71
- } from './schema/index.js'
72
-
73
- export type { KyselyPikkuDB } from './kysely-tables.js'
74
- export type { WorkflowRunService } from '@pikku/core/workflow'
75
- export type { AgentRunService, AgentRunRow } from '@pikku/core/agent'
76
- export type {
77
- VirtualUserRunStore,
78
- VirtualUserRunRecord,
79
- VirtualUserScheduleStore,
80
- VirtualUserScheduleRecord,
81
- } from '@pikku/core/virtual-user'
@@ -1,47 +0,0 @@
1
- import type { Kysely } from 'kysely'
2
- import type { KyselyPikkuDB } from './kysely-tables.js'
3
- import type { AgentRunScore } from '@pikku/core/agent-scorer'
4
- import type { SaveScoreInput } from '@pikku/core/services'
5
- import { parseJson } from './kysely-json.js'
6
-
7
- /**
8
- * Shared by the two Kysely services that implement `AgentRunStateService` — they
9
- * differ in how they store approvals, not in how they store grades.
10
- */
11
- export const saveRunScore = async (
12
- db: Kysely<KyselyPikkuDB>,
13
- score: SaveScoreInput
14
- ): Promise<void> => {
15
- await db
16
- .insertInto('agentRunScore')
17
- .values({
18
- id: crypto.randomUUID(),
19
- runId: score.runId,
20
- scorerName: score.scorerName,
21
- score: score.score,
22
- reason: score.reason ?? null,
23
- metadata: score.metadata ? JSON.stringify(score.metadata) : null,
24
- })
25
- .execute()
26
- }
27
-
28
- export const getRunScores = async (
29
- db: Kysely<KyselyPikkuDB>,
30
- runId: string
31
- ): Promise<AgentRunScore[]> => {
32
- const rows = await db
33
- .selectFrom('agentRunScore')
34
- .selectAll()
35
- .where('runId', '=', runId)
36
- .orderBy('createdAt', 'asc')
37
- .execute()
38
-
39
- return rows.map((row) => ({
40
- runId: row.runId,
41
- scorerName: row.scorerName,
42
- score: Number(row.score),
43
- reason: row.reason ?? undefined,
44
- metadata: parseJson(row.metadata),
45
- createdAt: new Date(row.createdAt),
46
- }))
47
- }
@@ -1,244 +0,0 @@
1
- import type {
2
- AgentThread,
3
- AgentMessage,
4
- AgentRunRow,
5
- AgentRunService,
6
- } from '@pikku/core/agent'
7
- import type { Kysely } from 'kysely'
8
- import { sql } from 'kysely'
9
- import type { KyselyPikkuDB } from './kysely-tables.js'
10
- import { parseJson } from './kysely-json.js'
11
-
12
- // Owner ids routinely contain `_` (e.g. `user_123`), which LIKE treats as a
13
- // single-character wildcard — so a raw prefix would match a foreign owner.
14
- const escapeLikePattern = (value: string) =>
15
- value.replace(/[\\%_]/g, (char) => `\\${char}`)
16
-
17
- export class KyselyAgentRunService implements AgentRunService {
18
- constructor(private db: Kysely<KyselyPikkuDB>) {}
19
-
20
- async listThreads(options?: {
21
- agentName?: string
22
- resourceId?: string
23
- owners?: string[]
24
- limit?: number
25
- offset?: number
26
- }): Promise<AgentThread[]> {
27
- const {
28
- agentName,
29
- resourceId,
30
- owners,
31
- limit = 50,
32
- offset = 0,
33
- } = options ?? {}
34
-
35
- // An owners constraint is an authorization boundary, so an empty list must
36
- // match nothing rather than degrade to "no filter".
37
- if (owners && owners.length === 0) return []
38
-
39
- let query = this.db
40
- .selectFrom('agentThreads as t')
41
- .select([
42
- 't.id',
43
- 't.resourceId',
44
- 't.title',
45
- 't.metadata',
46
- 't.createdAt',
47
- 't.updatedAt',
48
- ])
49
-
50
- if (agentName) {
51
- query = query.where(
52
- 't.id',
53
- 'in',
54
- this.db
55
- .selectFrom('agentRun')
56
- .select('threadId')
57
- .where('agentName', '=', agentName)
58
- .distinct()
59
- )
60
- }
61
-
62
- if (resourceId) {
63
- query = query.where('t.resourceId', '=', resourceId)
64
- }
65
-
66
- if (owners) {
67
- query = query.where((eb) =>
68
- eb.or(
69
- owners.flatMap((owner) => [
70
- eb('t.resourceId', '=', owner),
71
- eb(
72
- 't.resourceId',
73
- 'like',
74
- sql<string>`${`${escapeLikePattern(owner)}:%`} escape '\\'`
75
- ),
76
- ])
77
- )
78
- )
79
- }
80
-
81
- const result = await query
82
- .orderBy('t.updatedAt', 'desc')
83
- .limit(limit)
84
- .offset(offset)
85
- .execute()
86
-
87
- return result.map((row) => this.mapThreadRow(row))
88
- }
89
-
90
- async getThread(threadId: string): Promise<AgentThread | null> {
91
- const row = await this.db
92
- .selectFrom('agentThreads')
93
- .select([
94
- 'id',
95
- 'resourceId',
96
- 'title',
97
- 'metadata',
98
- 'createdAt',
99
- 'updatedAt',
100
- ])
101
- .where('id', '=', threadId)
102
- .executeTakeFirst()
103
-
104
- if (!row) return null
105
- return this.mapThreadRow(row)
106
- }
107
-
108
- async getThreadMessages(threadId: string): Promise<AgentMessage[]> {
109
- const [msgResult, tcResult] = await Promise.all([
110
- this.db
111
- .selectFrom('agentMessage')
112
- .select(['id', 'role', 'content', 'createdAt'])
113
- .where('threadId', '=', threadId)
114
- .orderBy('createdAt', 'asc')
115
- .execute(),
116
- this.db
117
- .selectFrom('agentToolCall')
118
- .select(['id', 'messageId', 'toolName', 'args', 'result'])
119
- .where('threadId', '=', threadId)
120
- .orderBy('createdAt', 'asc')
121
- .execute(),
122
- ])
123
-
124
- const tcByMessage = new Map<string, (typeof tcResult)[number][]>()
125
- for (const tc of tcResult) {
126
- const msgId = tc.messageId
127
- if (!tcByMessage.has(msgId)) tcByMessage.set(msgId, [])
128
- tcByMessage.get(msgId)!.push(tc)
129
- }
130
-
131
- const messages: AgentMessage[] = []
132
- for (const row of msgResult) {
133
- const msg: AgentMessage = {
134
- id: row.id,
135
- role: row.role as AgentMessage['role'],
136
- content: row.content ?? undefined,
137
- createdAt: new Date(row.createdAt as unknown as string),
138
- }
139
-
140
- const tcs = tcByMessage.get(msg.id)
141
- if (tcs?.length) {
142
- msg.toolCalls = tcs.map((tc) => ({
143
- id: tc.id,
144
- name: tc.toolName,
145
- args: parseJson(tc.args) as Record<string, unknown>,
146
- }))
147
-
148
- const completed = tcs.filter((tc) => tc.result != null)
149
- if (completed.length) {
150
- messages.push(msg)
151
- messages.push({
152
- id: `tool-results-${msg.id}`,
153
- role: 'tool',
154
- toolResults: completed.map((tc) => ({
155
- id: tc.id,
156
- name: tc.toolName,
157
- result: tc.result!,
158
- })),
159
- createdAt: msg.createdAt,
160
- })
161
- continue
162
- }
163
- }
164
-
165
- messages.push(msg)
166
- }
167
-
168
- return messages
169
- }
170
-
171
- async getThreadRuns(threadId: string): Promise<AgentRunRow[]> {
172
- const result = await this.db
173
- .selectFrom('agentRun')
174
- .select([
175
- 'runId',
176
- 'agentName',
177
- 'threadId',
178
- 'resourceId',
179
- 'status',
180
- 'errorMessage',
181
- 'suspendReason',
182
- 'missingRpcs',
183
- 'usageInputTokens',
184
- 'usageOutputTokens',
185
- 'usageModel',
186
- 'createdAt',
187
- 'updatedAt',
188
- ])
189
- .where('threadId', '=', threadId)
190
- .orderBy('createdAt', 'desc')
191
- .execute()
192
-
193
- return result.map((row) => this.mapRunRow(row))
194
- }
195
-
196
- async deleteThread(threadId: string): Promise<boolean> {
197
- const result = await this.db
198
- .deleteFrom('agentThreads')
199
- .where('id', '=', threadId)
200
- .executeTakeFirst()
201
-
202
- return BigInt(result.numDeletedRows) > 0n
203
- }
204
-
205
- async getDistinctAgentNames(): Promise<string[]> {
206
- const result = await this.db
207
- .selectFrom('agentRun')
208
- .select('agentName')
209
- .distinct()
210
- .orderBy('agentName')
211
- .execute()
212
-
213
- return result.map((row) => row.agentName)
214
- }
215
-
216
- private mapThreadRow(row: any): AgentThread {
217
- return {
218
- id: row.id as string,
219
- resourceId: row.resourceId as string,
220
- title: (row.title as string) ?? undefined,
221
- metadata: parseJson(row.metadata),
222
- createdAt: new Date(row.createdAt as string),
223
- updatedAt: new Date(row.updatedAt as string),
224
- }
225
- }
226
-
227
- private mapRunRow(row: any): AgentRunRow {
228
- return {
229
- runId: row.runId as string,
230
- agentName: row.agentName as string,
231
- threadId: row.threadId as string,
232
- resourceId: row.resourceId as string,
233
- status: row.status as string,
234
- errorMessage: (row.errorMessage as string) ?? undefined,
235
- suspendReason: (row.suspendReason as string) ?? undefined,
236
- missingRpcs: parseJson(row.missingRpcs),
237
- usageInputTokens: Number(row.usageInputTokens),
238
- usageOutputTokens: Number(row.usageOutputTokens),
239
- usageModel: row.usageModel as string,
240
- createdAt: new Date(row.createdAt as string),
241
- updatedAt: new Date(row.updatedAt as string),
242
- }
243
- }
244
- }