@sixb/sqlite 0.0.1
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/LICENSE +21 -0
- package/README.md +57 -0
- package/dist/action-run-storage.d.ts +29 -0
- package/dist/action-run-storage.d.ts.map +1 -0
- package/dist/agents/agent-storage.d.ts +25 -0
- package/dist/agents/agent-storage.d.ts.map +1 -0
- package/dist/agents/index.d.ts +6 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/messages.d.ts +14 -0
- package/dist/agents/messages.d.ts.map +1 -0
- package/dist/agents/rows.d.ts +77 -0
- package/dist/agents/rows.d.ts.map +1 -0
- package/dist/agents/runs.d.ts +26 -0
- package/dist/agents/runs.d.ts.map +1 -0
- package/dist/agents/threads.d.ts +14 -0
- package/dist/agents/threads.d.ts.map +1 -0
- package/dist/auth-storage/access-tokens.d.ts +33 -0
- package/dist/auth-storage/access-tokens.d.ts.map +1 -0
- package/dist/auth-storage/auth-storage.d.ts +46 -0
- package/dist/auth-storage/auth-storage.d.ts.map +1 -0
- package/dist/auth-storage/group-memberships.d.ts +21 -0
- package/dist/auth-storage/group-memberships.d.ts.map +1 -0
- package/dist/auth-storage/identities.d.ts +17 -0
- package/dist/auth-storage/identities.d.ts.map +1 -0
- package/dist/auth-storage/index.d.ts +3 -0
- package/dist/auth-storage/index.d.ts.map +1 -0
- package/dist/auth-storage/invitations.d.ts +28 -0
- package/dist/auth-storage/invitations.d.ts.map +1 -0
- package/dist/auth-storage/magic-links.d.ts +28 -0
- package/dist/auth-storage/magic-links.d.ts.map +1 -0
- package/dist/auth-storage/oidc-attempts.d.ts +18 -0
- package/dist/auth-storage/oidc-attempts.d.ts.map +1 -0
- package/dist/auth-storage/rows.d.ts +134 -0
- package/dist/auth-storage/rows.d.ts.map +1 -0
- package/dist/auth-storage/service-account-group-memberships.d.ts +17 -0
- package/dist/auth-storage/service-account-group-memberships.d.ts.map +1 -0
- package/dist/auth-storage/service-accounts.d.ts +14 -0
- package/dist/auth-storage/service-accounts.d.ts.map +1 -0
- package/dist/auth-storage/sessions.d.ts +55 -0
- package/dist/auth-storage/sessions.d.ts.map +1 -0
- package/dist/auth-storage/shared.d.ts +131 -0
- package/dist/auth-storage/shared.d.ts.map +1 -0
- package/dist/auth-storage/users.d.ts +19 -0
- package/dist/auth-storage/users.d.ts.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10204 -0
- package/dist/index.js.map +54 -0
- package/dist/latest-run-query.d.ts +22 -0
- package/dist/latest-run-query.d.ts.map +1 -0
- package/dist/migrations.d.ts +18 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/object-query-compiler.d.ts +44 -0
- package/dist/object-query-compiler.d.ts.map +1 -0
- package/dist/object-storage.d.ts +93 -0
- package/dist/object-storage.d.ts.map +1 -0
- package/dist/ontology-storage/commits.d.ts +14 -0
- package/dist/ontology-storage/commits.d.ts.map +1 -0
- package/dist/ontology-storage/index.d.ts +22 -0
- package/dist/ontology-storage/index.d.ts.map +1 -0
- package/dist/ontology-storage/materialization-session.d.ts +49 -0
- package/dist/ontology-storage/materialization-session.d.ts.map +1 -0
- package/dist/ontology-storage/materialization-state.d.ts +63 -0
- package/dist/ontology-storage/materialization-state.d.ts.map +1 -0
- package/dist/ontology-storage/materialization-writer.d.ts +15 -0
- package/dist/ontology-storage/materialization-writer.d.ts.map +1 -0
- package/dist/ontology-storage/materializations.d.ts +32 -0
- package/dist/ontology-storage/materializations.d.ts.map +1 -0
- package/dist/ontology-storage/outbox.d.ts +14 -0
- package/dist/ontology-storage/outbox.d.ts.map +1 -0
- package/dist/ontology-storage/shared.d.ts +91 -0
- package/dist/ontology-storage/shared.d.ts.map +1 -0
- package/dist/ontology-storage/sources.d.ts +26 -0
- package/dist/ontology-storage/sources.d.ts.map +1 -0
- package/dist/pipeline-run-storage.d.ts +26 -0
- package/dist/pipeline-run-storage.d.ts.map +1 -0
- package/dist/projection-run-storage.d.ts +32 -0
- package/dist/projection-run-storage.d.ts.map +1 -0
- package/dist/rules-storage.d.ts +33 -0
- package/dist/rules-storage.d.ts.map +1 -0
- package/dist/run-list-query.d.ts +27 -0
- package/dist/run-list-query.d.ts.map +1 -0
- package/dist/storage-errors.d.ts +2 -0
- package/dist/storage-errors.d.ts.map +1 -0
- package/dist/sync-run-storage.d.ts +23 -0
- package/dist/sync-run-storage.d.ts.map +1 -0
- package/dist/timeseries-storage.d.ts +41 -0
- package/dist/timeseries-storage.d.ts.map +1 -0
- package/dist/transactions.d.ts +15 -0
- package/dist/transactions.d.ts.map +1 -0
- package/dist/tsconfig.json +5 -0
- package/dist/validation.d.ts +5 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/webhook-delivery-storage.d.ts +27 -0
- package/dist/webhook-delivery-storage.d.ts.map +1 -0
- package/dist/webhook-run-storage.d.ts +22 -0
- package/dist/webhook-run-storage.d.ts.map +1 -0
- package/dist/workflow-intervention-storage.d.ts +26 -0
- package/dist/workflow-intervention-storage.d.ts.map +1 -0
- package/dist/workflow-run-storage.d.ts +63 -0
- package/dist/workflow-run-storage.d.ts.map +1 -0
- package/package.json +58 -0
- package/src/action-run-storage.ts +761 -0
- package/src/agents/agent-storage.ts +46 -0
- package/src/agents/index.ts +5 -0
- package/src/agents/messages.ts +162 -0
- package/src/agents/rows.ts +205 -0
- package/src/agents/runs.ts +389 -0
- package/src/agents/threads.ts +133 -0
- package/src/auth-storage/access-tokens.ts +289 -0
- package/src/auth-storage/auth-storage.ts +784 -0
- package/src/auth-storage/group-memberships.ts +52 -0
- package/src/auth-storage/identities.ts +98 -0
- package/src/auth-storage/index.ts +2 -0
- package/src/auth-storage/invitations.ts +258 -0
- package/src/auth-storage/magic-links.ts +149 -0
- package/src/auth-storage/oidc-attempts.ts +109 -0
- package/src/auth-storage/rows.ts +338 -0
- package/src/auth-storage/service-account-group-memberships.ts +161 -0
- package/src/auth-storage/service-accounts.ts +168 -0
- package/src/auth-storage/sessions.ts +230 -0
- package/src/auth-storage/shared.ts +707 -0
- package/src/auth-storage/users.ts +226 -0
- package/src/index.ts +292 -0
- package/src/latest-run-query.ts +49 -0
- package/src/migrations/001-initial-schema.sql +1068 -0
- package/src/migrations.ts +245 -0
- package/src/object-query-compiler.ts +1198 -0
- package/src/object-storage.ts +605 -0
- package/src/ontology-storage/commits.ts +127 -0
- package/src/ontology-storage/index.ts +35 -0
- package/src/ontology-storage/materialization-session.ts +393 -0
- package/src/ontology-storage/materialization-state.ts +901 -0
- package/src/ontology-storage/materialization-writer.ts +394 -0
- package/src/ontology-storage/materializations.ts +838 -0
- package/src/ontology-storage/outbox.ts +237 -0
- package/src/ontology-storage/shared.ts +269 -0
- package/src/ontology-storage/sources.ts +653 -0
- package/src/pipeline-run-storage.ts +506 -0
- package/src/projection-run-storage.ts +592 -0
- package/src/rules-storage.ts +375 -0
- package/src/run-list-query.ts +98 -0
- package/src/sql.d.ts +4 -0
- package/src/storage-errors.ts +3 -0
- package/src/sync-run-storage.ts +299 -0
- package/src/timeseries-storage.ts +212 -0
- package/src/transactions.ts +96 -0
- package/src/validation.ts +30 -0
- package/src/webhook-delivery-storage.ts +272 -0
- package/src/webhook-run-storage.ts +240 -0
- package/src/workflow-intervention-storage.ts +398 -0
- package/src/workflow-run-storage.ts +1148 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { AgentStorage } from "@sixb/core/storage"
|
|
2
|
+
import { installFreshSqliteSchema } from "../migrations"
|
|
3
|
+
import {
|
|
4
|
+
closeSqliteStoreConnection,
|
|
5
|
+
openSqliteStoreConnection,
|
|
6
|
+
type SqliteStoreConnection,
|
|
7
|
+
} from "../transactions"
|
|
8
|
+
import { SqliteAgentMessageStore } from "./messages"
|
|
9
|
+
import { SqliteAgentRunStore } from "./runs"
|
|
10
|
+
import { SqliteAgentThreadStore } from "./threads"
|
|
11
|
+
|
|
12
|
+
export interface SqliteAgentStorageOptions {
|
|
13
|
+
/** Path to SQLite database file. Defaults to ':memory:' for in-memory database. */
|
|
14
|
+
path?: string
|
|
15
|
+
/** Internal shared connection used by bundled SqliteStorage. */
|
|
16
|
+
connection?: SqliteStoreConnection
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* SQLite-backed agent persistence: thread / run / message sub-stores sharing one connection. The
|
|
21
|
+
* cross-table writes (reserve + finish touch the thread anchor, append bumps thread stats) run in
|
|
22
|
+
* SQLite transactions; the bundled {@link SqliteStorage} serializes all of them on one connection.
|
|
23
|
+
*/
|
|
24
|
+
export class SqliteAgentStorage implements AgentStorage {
|
|
25
|
+
readonly threads: SqliteAgentThreadStore
|
|
26
|
+
readonly runs: SqliteAgentRunStore
|
|
27
|
+
readonly messages: SqliteAgentMessageStore
|
|
28
|
+
|
|
29
|
+
private readonly connection: SqliteStoreConnection
|
|
30
|
+
|
|
31
|
+
constructor(options: SqliteAgentStorageOptions = {}) {
|
|
32
|
+
this.connection = openSqliteStoreConnection(options)
|
|
33
|
+
|
|
34
|
+
if (this.connection.installFreshSchema) {
|
|
35
|
+
installFreshSqliteSchema(this.connection.db)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.threads = new SqliteAgentThreadStore(this.connection.db)
|
|
39
|
+
this.runs = new SqliteAgentRunStore(this.connection.db)
|
|
40
|
+
this.messages = new SqliteAgentMessageStore(this.connection.db)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
close(): void {
|
|
44
|
+
closeSqliteStoreConnection(this.connection)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { SqliteAgentStorageOptions } from "./agent-storage"
|
|
2
|
+
export { SqliteAgentStorage } from "./agent-storage"
|
|
3
|
+
export { SqliteAgentMessageStore } from "./messages"
|
|
4
|
+
export { SqliteAgentRunStore } from "./runs"
|
|
5
|
+
export { SqliteAgentThreadStore } from "./threads"
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite"
|
|
2
|
+
import { AGENT_MESSAGE_CONTENT_VERSION } from "@sixb/core/internal/agents"
|
|
3
|
+
import {
|
|
4
|
+
type AgentMessageRecord,
|
|
5
|
+
type AgentMessageStore,
|
|
6
|
+
AgentStorageError,
|
|
7
|
+
type AppendAgentMessageInput,
|
|
8
|
+
type ListAgentMessagesInput,
|
|
9
|
+
type ListAgentMessagesResult,
|
|
10
|
+
} from "@sixb/core/storage"
|
|
11
|
+
import type { SqliteValue } from "../run-list-query"
|
|
12
|
+
import { isUniqueConstraintError } from "../storage-errors"
|
|
13
|
+
import { type AgentMessageRow, queryAgentList, rowToMessageRecord } from "./rows"
|
|
14
|
+
|
|
15
|
+
export class SqliteAgentMessageStore implements AgentMessageStore {
|
|
16
|
+
constructor(private readonly db: Database) {}
|
|
17
|
+
|
|
18
|
+
async append(input: AppendAgentMessageInput): Promise<AgentMessageRecord> {
|
|
19
|
+
const createdAt = input.createdAt ?? new Date()
|
|
20
|
+
|
|
21
|
+
return this.db.transaction(() => {
|
|
22
|
+
const thread = this.db
|
|
23
|
+
.query("SELECT 1 AS present FROM agent_threads WHERE project_id = ? AND id = ?")
|
|
24
|
+
.get(input.projectId, input.threadId) as { present: number } | null
|
|
25
|
+
|
|
26
|
+
if (!thread) {
|
|
27
|
+
throw new AgentStorageError(
|
|
28
|
+
"thread_not_found",
|
|
29
|
+
`[SixbSqlite] Agent thread '${input.threadId}' not found for project '${input.projectId}'.`
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (input.runId !== null) {
|
|
34
|
+
const run = this.db
|
|
35
|
+
.query("SELECT 1 AS present FROM agent_runs WHERE project_id = ? AND id = ?")
|
|
36
|
+
.get(input.projectId, input.runId) as { present: number } | null
|
|
37
|
+
|
|
38
|
+
if (!run) {
|
|
39
|
+
throw new AgentStorageError(
|
|
40
|
+
"run_not_found",
|
|
41
|
+
`[SixbSqlite] Agent run '${input.runId}' not found for project '${input.projectId}'.`
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const seqRow = this.db
|
|
47
|
+
.query(
|
|
48
|
+
"SELECT COALESCE(MAX(seq), 0) + 1 AS next FROM agent_messages WHERE project_id = ? AND thread_id = ?"
|
|
49
|
+
)
|
|
50
|
+
.get(input.projectId, input.threadId) as { next: number }
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
this.db
|
|
54
|
+
.query(
|
|
55
|
+
`
|
|
56
|
+
INSERT INTO agent_messages (
|
|
57
|
+
project_id,
|
|
58
|
+
id,
|
|
59
|
+
thread_id,
|
|
60
|
+
run_id,
|
|
61
|
+
role,
|
|
62
|
+
author_principal_type,
|
|
63
|
+
author_principal_id,
|
|
64
|
+
seq,
|
|
65
|
+
parts,
|
|
66
|
+
metadata,
|
|
67
|
+
content_version,
|
|
68
|
+
created_at,
|
|
69
|
+
completed_at
|
|
70
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
71
|
+
`
|
|
72
|
+
)
|
|
73
|
+
.run(
|
|
74
|
+
input.projectId,
|
|
75
|
+
input.id,
|
|
76
|
+
input.threadId,
|
|
77
|
+
input.runId,
|
|
78
|
+
input.role,
|
|
79
|
+
input.authorPrincipal?.type ?? null,
|
|
80
|
+
input.authorPrincipal?.id ?? null,
|
|
81
|
+
seqRow.next,
|
|
82
|
+
JSON.stringify(input.parts),
|
|
83
|
+
input.metadata === undefined ? null : JSON.stringify(input.metadata),
|
|
84
|
+
AGENT_MESSAGE_CONTENT_VERSION,
|
|
85
|
+
createdAt.toISOString(),
|
|
86
|
+
input.completedAt ? input.completedAt.toISOString() : null
|
|
87
|
+
)
|
|
88
|
+
} catch (error) {
|
|
89
|
+
if (isUniqueConstraintError(error)) {
|
|
90
|
+
throw new AgentStorageError(
|
|
91
|
+
"duplicate_id",
|
|
92
|
+
`[SixbSqlite] Agent message '${input.id}' already exists for project '${input.projectId}'.`
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
throw error
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.db
|
|
100
|
+
.query(
|
|
101
|
+
`
|
|
102
|
+
UPDATE agent_threads
|
|
103
|
+
SET message_count = message_count + 1, last_message_at = ?, updated_at = ?
|
|
104
|
+
WHERE project_id = ? AND id = ?
|
|
105
|
+
`
|
|
106
|
+
)
|
|
107
|
+
.run(createdAt.toISOString(), createdAt.toISOString(), input.projectId, input.threadId)
|
|
108
|
+
|
|
109
|
+
return this.requireMessage(input.projectId, input.id)
|
|
110
|
+
})()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async getById(params: { projectId: string; id: string }): Promise<AgentMessageRecord | null> {
|
|
114
|
+
const row = this.db
|
|
115
|
+
.query("SELECT * FROM agent_messages WHERE project_id = ? AND id = ?")
|
|
116
|
+
.get(params.projectId, params.id) as AgentMessageRow | null
|
|
117
|
+
|
|
118
|
+
return row ? rowToMessageRecord(row) : null
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async list(input: ListAgentMessagesInput): Promise<ListAgentMessagesResult> {
|
|
122
|
+
if (input.roles !== undefined && input.roles.length === 0) {
|
|
123
|
+
return { messages: [], hasMore: false, total: 0 }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const whereClauses = ["project_id = ?", "thread_id = ?"]
|
|
127
|
+
const args: SqliteValue[] = [input.projectId, input.threadId]
|
|
128
|
+
|
|
129
|
+
if (input.roles) {
|
|
130
|
+
whereClauses.push(`role IN (${input.roles.map(() => "?").join(", ")})`)
|
|
131
|
+
args.push(...input.roles)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const dir = input.order === "desc" ? "DESC" : "ASC"
|
|
135
|
+
const { rows, total, hasMore } = queryAgentList<AgentMessageRow>({
|
|
136
|
+
db: this.db,
|
|
137
|
+
table: "agent_messages",
|
|
138
|
+
whereClauses,
|
|
139
|
+
args,
|
|
140
|
+
orderBy: `seq ${dir}`,
|
|
141
|
+
limit: input.limit,
|
|
142
|
+
offset: input.offset,
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
return { messages: rows.map(rowToMessageRecord), hasMore, total }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private requireMessage(projectId: string, id: string): AgentMessageRecord {
|
|
149
|
+
const row = this.db
|
|
150
|
+
.query("SELECT * FROM agent_messages WHERE project_id = ? AND id = ?")
|
|
151
|
+
.get(projectId, id) as AgentMessageRow | null
|
|
152
|
+
|
|
153
|
+
if (!row) {
|
|
154
|
+
throw new AgentStorageError(
|
|
155
|
+
"invalid_state",
|
|
156
|
+
`[SixbSqlite] Failed to load agent message '${id}' for project '${projectId}'.`
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return rowToMessageRecord(row)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite"
|
|
2
|
+
import { type AgentMessagePart, type Principal, SYSTEM_PRINCIPAL } from "@sixb/core"
|
|
3
|
+
import {
|
|
4
|
+
type AgentMessageRecord,
|
|
5
|
+
type AgentRunDiagnostic,
|
|
6
|
+
type AgentRunRecord,
|
|
7
|
+
type AgentRunUsage,
|
|
8
|
+
type AgentThreadRecord,
|
|
9
|
+
coerceAgentRunFinishReason,
|
|
10
|
+
} from "@sixb/core/storage"
|
|
11
|
+
import type { SqliteValue } from "../run-list-query"
|
|
12
|
+
|
|
13
|
+
// ── row shapes (snake_case, as stored) ──────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
export interface AgentThreadRow {
|
|
16
|
+
project_id: string
|
|
17
|
+
id: string
|
|
18
|
+
agent_id: string
|
|
19
|
+
owner_principal_type: Principal["type"]
|
|
20
|
+
owner_principal_id: string
|
|
21
|
+
title: string | null
|
|
22
|
+
status: AgentThreadRecord["status"]
|
|
23
|
+
active_run_id: string | null
|
|
24
|
+
last_message_at: string | null
|
|
25
|
+
message_count: number
|
|
26
|
+
created_at: string
|
|
27
|
+
updated_at: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface AgentRunRow {
|
|
31
|
+
project_id: string
|
|
32
|
+
id: string
|
|
33
|
+
thread_id: string
|
|
34
|
+
agent_id: string
|
|
35
|
+
trigger_message_id: string
|
|
36
|
+
requested_by_principal_type: Principal["type"] | null
|
|
37
|
+
requested_by_principal_id: string | null
|
|
38
|
+
execution_principal_type: "serviceAccount" | null
|
|
39
|
+
execution_principal_id: string | null
|
|
40
|
+
status: AgentRunRecord["status"]
|
|
41
|
+
model_id: string | null
|
|
42
|
+
finish_reason: string | null
|
|
43
|
+
usage_input_tokens: number | null
|
|
44
|
+
usage_output_tokens: number | null
|
|
45
|
+
usage_total_tokens: number | null
|
|
46
|
+
usage_reasoning_tokens: number | null
|
|
47
|
+
usage_cached_input_tokens: number | null
|
|
48
|
+
error: string | null
|
|
49
|
+
diagnostics: string | null
|
|
50
|
+
attempt: number
|
|
51
|
+
execution_token: string | null
|
|
52
|
+
execution_queue_lease_expires_at: string | null
|
|
53
|
+
created_at: string
|
|
54
|
+
started_at: string | null
|
|
55
|
+
completed_at: string | null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface AgentMessageRow {
|
|
59
|
+
project_id: string
|
|
60
|
+
id: string
|
|
61
|
+
thread_id: string
|
|
62
|
+
run_id: string | null
|
|
63
|
+
role: AgentMessageRecord["role"]
|
|
64
|
+
author_principal_type: Principal["type"] | null
|
|
65
|
+
author_principal_id: string | null
|
|
66
|
+
seq: number
|
|
67
|
+
parts: string
|
|
68
|
+
metadata: string | null
|
|
69
|
+
content_version: number
|
|
70
|
+
created_at: string
|
|
71
|
+
completed_at: string | null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── row → record mappers ────────────────────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
export function rowToThreadRecord(row: AgentThreadRow): AgentThreadRecord {
|
|
77
|
+
return {
|
|
78
|
+
id: row.id,
|
|
79
|
+
projectId: row.project_id,
|
|
80
|
+
agentId: row.agent_id,
|
|
81
|
+
ownerPrincipal: { type: row.owner_principal_type, id: row.owner_principal_id },
|
|
82
|
+
title: row.title ?? undefined,
|
|
83
|
+
status: row.status,
|
|
84
|
+
activeRunId: row.active_run_id,
|
|
85
|
+
lastMessageAt: row.last_message_at ? new Date(row.last_message_at) : undefined,
|
|
86
|
+
messageCount: row.message_count,
|
|
87
|
+
createdAt: new Date(row.created_at),
|
|
88
|
+
updatedAt: new Date(row.updated_at),
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function rowToRunRecord(row: AgentRunRow): AgentRunRecord {
|
|
93
|
+
return {
|
|
94
|
+
id: row.id,
|
|
95
|
+
projectId: row.project_id,
|
|
96
|
+
threadId: row.thread_id,
|
|
97
|
+
agentId: row.agent_id,
|
|
98
|
+
triggerMessageId: row.trigger_message_id,
|
|
99
|
+
requestedByPrincipal:
|
|
100
|
+
principalFromColumns(row.requested_by_principal_type, row.requested_by_principal_id) ??
|
|
101
|
+
SYSTEM_PRINCIPAL,
|
|
102
|
+
executionPrincipal: serviceAccountPrincipalFromColumns(
|
|
103
|
+
row.execution_principal_type,
|
|
104
|
+
row.execution_principal_id
|
|
105
|
+
),
|
|
106
|
+
status: row.status,
|
|
107
|
+
modelId: row.model_id ?? undefined,
|
|
108
|
+
finishReason: coerceAgentRunFinishReason(row.finish_reason),
|
|
109
|
+
usage: rowToUsage(row),
|
|
110
|
+
error: row.error ?? undefined,
|
|
111
|
+
diagnostics:
|
|
112
|
+
row.diagnostics === null ? undefined : (JSON.parse(row.diagnostics) as AgentRunDiagnostic[]),
|
|
113
|
+
attempt: row.attempt,
|
|
114
|
+
execution:
|
|
115
|
+
row.execution_token && row.execution_queue_lease_expires_at
|
|
116
|
+
? {
|
|
117
|
+
token: row.execution_token,
|
|
118
|
+
queueLeaseExpiresAt: new Date(row.execution_queue_lease_expires_at),
|
|
119
|
+
}
|
|
120
|
+
: undefined,
|
|
121
|
+
createdAt: new Date(row.created_at),
|
|
122
|
+
startedAt: row.started_at ? new Date(row.started_at) : undefined,
|
|
123
|
+
completedAt: row.completed_at ? new Date(row.completed_at) : undefined,
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function rowToMessageRecord(row: AgentMessageRow): AgentMessageRecord {
|
|
128
|
+
return {
|
|
129
|
+
id: row.id,
|
|
130
|
+
projectId: row.project_id,
|
|
131
|
+
threadId: row.thread_id,
|
|
132
|
+
runId: row.run_id,
|
|
133
|
+
role: row.role,
|
|
134
|
+
authorPrincipal: principalFromColumns(row.author_principal_type, row.author_principal_id),
|
|
135
|
+
seq: row.seq,
|
|
136
|
+
parts: JSON.parse(row.parts) as AgentMessagePart[],
|
|
137
|
+
// A null/absent column and an explicit JSON `null` both mean "no metadata" (jsonb cannot tell
|
|
138
|
+
// SQL NULL from a json null on read, so both backends normalise to `undefined` for parity).
|
|
139
|
+
metadata: row.metadata === null ? undefined : (JSON.parse(row.metadata) ?? undefined),
|
|
140
|
+
contentVersion: row.content_version,
|
|
141
|
+
createdAt: new Date(row.created_at),
|
|
142
|
+
completedAt: row.completed_at ? new Date(row.completed_at) : undefined,
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function principalFromColumns(
|
|
147
|
+
type: Principal["type"] | null | undefined,
|
|
148
|
+
id: string | null | undefined
|
|
149
|
+
): Principal | undefined {
|
|
150
|
+
return type && id ? { type, id } : undefined
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function serviceAccountPrincipalFromColumns(
|
|
154
|
+
type: "serviceAccount" | null | undefined,
|
|
155
|
+
id: string | null | undefined
|
|
156
|
+
): Extract<Principal, { readonly type: "serviceAccount" }> | undefined {
|
|
157
|
+
return type === "serviceAccount" && id ? { type, id } : undefined
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function rowToUsage(row: AgentRunRow): AgentRunUsage | undefined {
|
|
161
|
+
const usage: AgentRunUsage = {
|
|
162
|
+
...(row.usage_input_tokens === null ? {} : { inputTokens: row.usage_input_tokens }),
|
|
163
|
+
...(row.usage_output_tokens === null ? {} : { outputTokens: row.usage_output_tokens }),
|
|
164
|
+
...(row.usage_total_tokens === null ? {} : { totalTokens: row.usage_total_tokens }),
|
|
165
|
+
...(row.usage_reasoning_tokens === null ? {} : { reasoningTokens: row.usage_reasoning_tokens }),
|
|
166
|
+
...(row.usage_cached_input_tokens === null
|
|
167
|
+
? {}
|
|
168
|
+
: { cachedInputTokens: row.usage_cached_input_tokens }),
|
|
169
|
+
}
|
|
170
|
+
return Object.keys(usage).length === 0 ? undefined : usage
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ── offset pagination (threads / messages order by their own column, not started_at) ─────────────
|
|
174
|
+
|
|
175
|
+
export function queryAgentList<TRow>(input: {
|
|
176
|
+
readonly db: Database
|
|
177
|
+
readonly table: "agent_threads" | "agent_messages"
|
|
178
|
+
readonly whereClauses: readonly string[]
|
|
179
|
+
readonly args: readonly SqliteValue[]
|
|
180
|
+
readonly orderBy: string
|
|
181
|
+
readonly limit?: number
|
|
182
|
+
readonly offset?: number
|
|
183
|
+
}): { readonly rows: readonly TRow[]; readonly total: number; readonly hasMore: boolean } {
|
|
184
|
+
const where = `WHERE ${input.whereClauses.join(" AND ")}`
|
|
185
|
+
const offset = input.offset ?? 0
|
|
186
|
+
|
|
187
|
+
const totalRow = input.db
|
|
188
|
+
.query(`SELECT COUNT(*) AS count FROM ${input.table} ${where}`)
|
|
189
|
+
.get(...input.args) as { count: number }
|
|
190
|
+
|
|
191
|
+
let query = `SELECT * FROM ${input.table} ${where} ORDER BY ${input.orderBy}`
|
|
192
|
+
const queryArgs = [...input.args]
|
|
193
|
+
|
|
194
|
+
if (input.limit !== undefined) {
|
|
195
|
+
query += " LIMIT ? OFFSET ?"
|
|
196
|
+
queryArgs.push(input.limit, offset)
|
|
197
|
+
} else if (offset > 0) {
|
|
198
|
+
query += " LIMIT -1 OFFSET ?"
|
|
199
|
+
queryArgs.push(offset)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const rows = input.db.query(query).all(...queryArgs) as TRow[]
|
|
203
|
+
const total = totalRow.count
|
|
204
|
+
return { rows, total, hasMore: offset + rows.length < total }
|
|
205
|
+
}
|