@sixb/pg 0.1.1 → 0.1.3
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/README.md +7 -6
- package/dist/agents/agent-storage.d.ts +7 -4
- package/dist/agents/agent-storage.d.ts.map +1 -1
- package/dist/agents/checkpoints.d.ts +12 -0
- package/dist/agents/checkpoints.d.ts.map +1 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/messages.d.ts +2 -1
- package/dist/agents/messages.d.ts.map +1 -1
- package/dist/agents/rows.d.ts +22 -12
- package/dist/agents/rows.d.ts.map +1 -1
- package/dist/agents/runs.d.ts +3 -2
- package/dist/agents/runs.d.ts.map +1 -1
- package/dist/auth-storage/service-accounts.d.ts.map +1 -1
- package/dist/auth-storage/shared.d.ts +0 -4
- package/dist/auth-storage/shared.d.ts.map +1 -1
- package/dist/connector-connection-storage.d.ts +9 -0
- package/dist/connector-connection-storage.d.ts.map +1 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5010 -1837
- package/dist/index.js.map +31 -26
- package/dist/latest-run-query.d.ts.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/ontology-storage/materialization-session.d.ts.map +1 -1
- package/dist/ontology-storage/materialization-state.d.ts +0 -2
- package/dist/ontology-storage/materialization-state.d.ts.map +1 -1
- package/dist/ontology-storage/materialization-writer.d.ts.map +1 -1
- package/dist/ontology-storage/materializations.d.ts.map +1 -1
- package/dist/ontology-storage/outbox.d.ts.map +1 -1
- package/dist/ontology-storage/shared.d.ts +3 -6
- package/dist/ontology-storage/shared.d.ts.map +1 -1
- package/dist/pg-action-run-storage.d.ts +3 -2
- package/dist/pg-action-run-storage.d.ts.map +1 -1
- package/dist/pg-ai-cost-storage.d.ts +12 -0
- package/dist/pg-ai-cost-storage.d.ts.map +1 -0
- package/dist/pg-ai-usage-storage.d.ts +16 -0
- package/dist/pg-ai-usage-storage.d.ts.map +1 -0
- package/dist/pg-execution-storage.d.ts +14 -0
- package/dist/pg-execution-storage.d.ts.map +1 -0
- package/dist/pg-pipeline-run-storage.d.ts +5 -2
- package/dist/pg-pipeline-run-storage.d.ts.map +1 -1
- package/dist/pg-projection-run-storage.d.ts +5 -2
- package/dist/pg-projection-run-storage.d.ts.map +1 -1
- package/dist/pg-sync-run-storage.d.ts +5 -2
- package/dist/pg-sync-run-storage.d.ts.map +1 -1
- package/dist/pg-webhook-run-storage.d.ts +5 -2
- package/dist/pg-webhook-run-storage.d.ts.map +1 -1
- package/dist/pg-workflow-run-storage.d.ts +5 -3
- package/dist/pg-workflow-run-storage.d.ts.map +1 -1
- package/dist/run-list-query.d.ts +1 -1
- package/dist/run-list-query.d.ts.map +1 -1
- package/dist/testing.d.ts +6 -0
- package/dist/testing.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/agents/agent-storage.ts +9 -5
- package/src/agents/checkpoints.ts +175 -0
- package/src/agents/index.ts +1 -0
- package/src/agents/messages.ts +71 -13
- package/src/agents/rows.ts +57 -43
- package/src/agents/runs.ts +29 -15
- package/src/auth-storage/service-accounts.ts +14 -15
- package/src/auth-storage/shared.ts +0 -8
- package/src/connector-connection-storage.ts +760 -0
- package/src/index.ts +38 -15
- package/src/latest-run-query.ts +7 -1
- package/src/migrations/004-executions.sql +140 -0
- package/src/migrations/005-workflow-executions.sql +10 -0
- package/src/migrations/006-narrow-ontology-source-root-index.sql +4 -0
- package/src/migrations/007-split-overrides.sql +153 -0
- package/src/migrations/008-action-executions.sql +7 -0
- package/src/migrations/009-agent-executions.sql +21 -0
- package/src/migrations/010-ai-usage-accounting-foundation.sql +76 -0
- package/src/migrations/011-sync-failure-record.sql +23 -0
- package/src/migrations/012-pipeline-failure-record.sql +47 -0
- package/src/migrations/013-workflow-failure-record.sql +45 -0
- package/src/migrations/014-agent-failure-record.sql +56 -0
- package/src/migrations/015-projection-failure-record.sql +20 -0
- package/src/migrations/016-webhook-run-failure-record.sql +19 -0
- package/src/migrations/017-action-failure-record.sql +65 -0
- package/src/migrations/018-ontology-outbox-failure-record.sql +29 -0
- package/src/migrations/019-webhook-delivery-failure-record.sql +27 -0
- package/src/migrations/020-drop-run-usage-projections.sql +8 -0
- package/src/migrations/021-sync-pipeline-executions.sql +50 -0
- package/src/migrations/022-projection-executions.sql +115 -0
- package/src/migrations/023-webhook-executions.sql +30 -0
- package/src/migrations/024-ontology-commit-executions.sql +12 -0
- package/src/migrations/025-connector-connections.sql +231 -0
- package/src/migrations/026-ai-cost-accounting.sql +53 -0
- package/src/migrations/027-agent-context-checkpoints.sql +34 -0
- package/src/migrations/028-object-override-edit-times.sql +3 -0
- package/src/migrations.ts +82 -0
- package/src/ontology-storage/materialization-session.ts +25 -28
- package/src/ontology-storage/materialization-state.ts +161 -43
- package/src/ontology-storage/materialization-writer.ts +149 -76
- package/src/ontology-storage/materializations.ts +3 -1
- package/src/ontology-storage/outbox.ts +9 -2
- package/src/ontology-storage/shared.ts +10 -20
- package/src/pg-action-run-storage.ts +43 -67
- package/src/pg-ai-cost-storage.ts +707 -0
- package/src/pg-ai-usage-storage.ts +290 -0
- package/src/pg-execution-storage.ts +147 -0
- package/src/pg-pipeline-run-storage.ts +107 -41
- package/src/pg-projection-run-storage.ts +159 -91
- package/src/pg-sync-run-storage.ts +100 -28
- package/src/pg-webhook-run-storage.ts +89 -57
- package/src/pg-workflow-run-storage.ts +97 -130
- package/src/run-list-query.ts +9 -4
- package/src/testing.ts +18 -0
- package/dist/pg-webhook-delivery-storage.d.ts +0 -17
- package/dist/pg-webhook-delivery-storage.d.ts.map +0 -1
- package/src/pg-webhook-delivery-storage.ts +0 -180
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import {
|
|
2
|
+
agentContextCheckpointMatchesCreateInput,
|
|
3
|
+
assertAgentContextCheckpointAnchors,
|
|
4
|
+
assertAgentContextCheckpointAuthority,
|
|
5
|
+
assertAgentContextCheckpointReplayState,
|
|
6
|
+
assertCreateAgentContextCheckpointInput,
|
|
7
|
+
} from "@sixb/core/internal/agent-run-storage-provider"
|
|
8
|
+
import {
|
|
9
|
+
type AgentContextCheckpointRecord,
|
|
10
|
+
type AgentContextCheckpointStore,
|
|
11
|
+
AgentStorageError,
|
|
12
|
+
type CreateAgentContextCheckpointInput,
|
|
13
|
+
} from "@sixb/core/storage"
|
|
14
|
+
import { isUniqueViolation } from "../storage-errors"
|
|
15
|
+
import { type PgStoreClient, runPgTransaction } from "../transactions"
|
|
16
|
+
import {
|
|
17
|
+
type AgentContextCheckpointRow,
|
|
18
|
+
type AgentMessageRow,
|
|
19
|
+
type AgentRunRow,
|
|
20
|
+
type AgentThreadRow,
|
|
21
|
+
rowToContextCheckpointRecord,
|
|
22
|
+
rowToMessageRecord,
|
|
23
|
+
rowToRunRecord,
|
|
24
|
+
rowToThreadRecord,
|
|
25
|
+
} from "./rows"
|
|
26
|
+
|
|
27
|
+
export class PgAgentContextCheckpointStore implements AgentContextCheckpointStore {
|
|
28
|
+
constructor(private readonly sql: PgStoreClient) {}
|
|
29
|
+
|
|
30
|
+
async create(input: CreateAgentContextCheckpointInput): Promise<AgentContextCheckpointRecord> {
|
|
31
|
+
assertCreateAgentContextCheckpointInput(input, "SixbPg")
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
return await runPgTransaction(this.sql, async (tx) => {
|
|
35
|
+
// Match run finalization's lock order (run, then thread) to avoid a cross-operation deadlock.
|
|
36
|
+
const [runRow] = await tx<AgentRunRow[]>`
|
|
37
|
+
SELECT * FROM agent_runs
|
|
38
|
+
WHERE project_id = ${input.projectId} AND id = ${input.createdByRunId}
|
|
39
|
+
FOR UPDATE
|
|
40
|
+
`
|
|
41
|
+
const [threadRow] = await tx<AgentThreadRow[]>`
|
|
42
|
+
SELECT * FROM agent_threads
|
|
43
|
+
WHERE project_id = ${input.projectId} AND id = ${input.threadId}
|
|
44
|
+
FOR UPDATE
|
|
45
|
+
`
|
|
46
|
+
assertAgentContextCheckpointAuthority({
|
|
47
|
+
create: input,
|
|
48
|
+
run: runRow ? rowToRunRecord(runRow) : null,
|
|
49
|
+
thread: threadRow ? rowToThreadRecord(threadRow) : null,
|
|
50
|
+
prefix: "SixbPg",
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const [latestRow] = await tx<AgentContextCheckpointRow[]>`
|
|
54
|
+
SELECT * FROM agent_context_checkpoints
|
|
55
|
+
WHERE project_id = ${input.projectId} AND thread_id = ${input.threadId}
|
|
56
|
+
ORDER BY summarized_through_seq DESC, created_at DESC, id DESC
|
|
57
|
+
LIMIT 1
|
|
58
|
+
`
|
|
59
|
+
const latest = latestRow ? rowToContextCheckpointRecord(latestRow) : null
|
|
60
|
+
const [headRow] = await tx<{ seq: number | string }[]>`
|
|
61
|
+
SELECT COALESCE(MAX(seq), 0) AS seq FROM agent_messages
|
|
62
|
+
WHERE project_id = ${input.projectId} AND thread_id = ${input.threadId}
|
|
63
|
+
`
|
|
64
|
+
const headSeq = Number(headRow?.seq ?? 0)
|
|
65
|
+
|
|
66
|
+
const [existingForRunRow] = await tx<AgentContextCheckpointRow[]>`
|
|
67
|
+
SELECT * FROM agent_context_checkpoints
|
|
68
|
+
WHERE project_id = ${input.projectId} AND created_by_run_id = ${input.createdByRunId}
|
|
69
|
+
`
|
|
70
|
+
if (existingForRunRow) {
|
|
71
|
+
const record = rowToContextCheckpointRecord(existingForRunRow)
|
|
72
|
+
if (agentContextCheckpointMatchesCreateInput(record, input)) {
|
|
73
|
+
assertAgentContextCheckpointReplayState({
|
|
74
|
+
create: input,
|
|
75
|
+
existing: record,
|
|
76
|
+
latest,
|
|
77
|
+
headSeq,
|
|
78
|
+
prefix: "SixbPg",
|
|
79
|
+
})
|
|
80
|
+
return record
|
|
81
|
+
}
|
|
82
|
+
throw new AgentStorageError(
|
|
83
|
+
"invalid_state",
|
|
84
|
+
`[SixbPg] Agent run '${input.createdByRunId}' already created a different context checkpoint.`
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const [duplicateId] = await tx<{ present: number }[]>`
|
|
89
|
+
SELECT 1 AS present FROM agent_context_checkpoints
|
|
90
|
+
WHERE project_id = ${input.projectId} AND id = ${input.id}
|
|
91
|
+
`
|
|
92
|
+
if (duplicateId) {
|
|
93
|
+
throw new AgentStorageError(
|
|
94
|
+
"duplicate_id",
|
|
95
|
+
`[SixbPg] Agent context checkpoint '${input.id}' already exists for project '${input.projectId}'.`
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const [firstRetainedRow] = await tx<AgentMessageRow[]>`
|
|
100
|
+
SELECT * FROM agent_messages
|
|
101
|
+
WHERE
|
|
102
|
+
project_id = ${input.projectId}
|
|
103
|
+
AND thread_id = ${input.threadId}
|
|
104
|
+
AND seq > ${input.summarizedThroughSeq}
|
|
105
|
+
ORDER BY seq ASC
|
|
106
|
+
LIMIT 1
|
|
107
|
+
`
|
|
108
|
+
assertAgentContextCheckpointAnchors({
|
|
109
|
+
create: input,
|
|
110
|
+
latest,
|
|
111
|
+
headSeq,
|
|
112
|
+
firstRetained: firstRetainedRow ? rowToMessageRecord(firstRetainedRow) : null,
|
|
113
|
+
prefix: "SixbPg",
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const [row] = await tx<AgentContextCheckpointRow[]>`
|
|
117
|
+
INSERT INTO agent_context_checkpoints (
|
|
118
|
+
project_id,
|
|
119
|
+
id,
|
|
120
|
+
thread_id,
|
|
121
|
+
created_by_run_id,
|
|
122
|
+
previous_checkpoint_id,
|
|
123
|
+
reason,
|
|
124
|
+
summary,
|
|
125
|
+
summary_format_version,
|
|
126
|
+
summarized_through_seq,
|
|
127
|
+
observed_head_seq,
|
|
128
|
+
estimated_input_tokens_before,
|
|
129
|
+
estimated_input_tokens_after,
|
|
130
|
+
summary_model_id,
|
|
131
|
+
created_at
|
|
132
|
+
) VALUES (
|
|
133
|
+
${input.projectId},
|
|
134
|
+
${input.id},
|
|
135
|
+
${input.threadId},
|
|
136
|
+
${input.createdByRunId},
|
|
137
|
+
${input.expectedPreviousCheckpointId},
|
|
138
|
+
${input.reason},
|
|
139
|
+
${input.summary},
|
|
140
|
+
${input.summaryFormatVersion},
|
|
141
|
+
${input.summarizedThroughSeq},
|
|
142
|
+
${input.observedHeadSeq},
|
|
143
|
+
${input.estimatedInputTokensBefore},
|
|
144
|
+
${input.estimatedInputTokensAfter},
|
|
145
|
+
${input.summaryModelId},
|
|
146
|
+
${input.createdAt ?? new Date()}
|
|
147
|
+
)
|
|
148
|
+
RETURNING *
|
|
149
|
+
`
|
|
150
|
+
return rowToContextCheckpointRecord(row)
|
|
151
|
+
})
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (isUniqueViolation(error)) {
|
|
154
|
+
throw new AgentStorageError(
|
|
155
|
+
"duplicate_id",
|
|
156
|
+
`[SixbPg] Agent context checkpoint '${input.id}' already exists for project '${input.projectId}'.`
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
throw error
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async getLatest(input: {
|
|
164
|
+
readonly projectId: string
|
|
165
|
+
readonly threadId: string
|
|
166
|
+
}): Promise<AgentContextCheckpointRecord | null> {
|
|
167
|
+
const [row] = await this.sql<AgentContextCheckpointRow[]>`
|
|
168
|
+
SELECT * FROM agent_context_checkpoints
|
|
169
|
+
WHERE project_id = ${input.projectId} AND thread_id = ${input.threadId}
|
|
170
|
+
ORDER BY summarized_through_seq DESC, created_at DESC, id DESC
|
|
171
|
+
LIMIT 1
|
|
172
|
+
`
|
|
173
|
+
return row ? rowToContextCheckpointRecord(row) : null
|
|
174
|
+
}
|
|
175
|
+
}
|
package/src/agents/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type { PgAgentStorageOptions } from "./agent-storage"
|
|
2
2
|
export { PgAgentStorage } from "./agent-storage"
|
|
3
|
+
export { PgAgentContextCheckpointStore } from "./checkpoints"
|
|
3
4
|
export { PgAgentMessageStore } from "./messages"
|
|
4
5
|
export { PgAgentRunStore } from "./runs"
|
|
5
6
|
export { PgAgentThreadStore } from "./threads"
|
package/src/agents/messages.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type AgentMessageStore,
|
|
5
5
|
AgentStorageError,
|
|
6
6
|
type AppendAgentMessageInput,
|
|
7
|
+
type DeleteAgentMessagesByRunInput,
|
|
7
8
|
type ListAgentMessagesInput,
|
|
8
9
|
type ListAgentMessagesResult,
|
|
9
10
|
} from "@sixb/core/storage"
|
|
@@ -20,6 +21,23 @@ export class PgAgentMessageStore implements AgentMessageStore {
|
|
|
20
21
|
|
|
21
22
|
try {
|
|
22
23
|
return await runPgTransaction(this.sql, async (tx) => {
|
|
24
|
+
// Assistant finalization appends and finishes in one outer transaction. Lock its run first
|
|
25
|
+
// so every operation that needs both rows follows the same run -> thread order.
|
|
26
|
+
if (input.runId !== null) {
|
|
27
|
+
const [run] = await tx<{ id: string }[]>`
|
|
28
|
+
SELECT id FROM agent_runs
|
|
29
|
+
WHERE project_id = ${input.projectId} AND id = ${input.runId}
|
|
30
|
+
FOR UPDATE
|
|
31
|
+
`
|
|
32
|
+
|
|
33
|
+
if (!run) {
|
|
34
|
+
throw new AgentStorageError(
|
|
35
|
+
"run_not_found",
|
|
36
|
+
`[SixbPg] Agent run '${input.runId}' not found for project '${input.projectId}'.`
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
23
41
|
const [thread] = await tx<{ id: string }[]>`
|
|
24
42
|
SELECT id FROM agent_threads
|
|
25
43
|
WHERE project_id = ${input.projectId} AND id = ${input.threadId}
|
|
@@ -33,19 +51,6 @@ export class PgAgentMessageStore implements AgentMessageStore {
|
|
|
33
51
|
)
|
|
34
52
|
}
|
|
35
53
|
|
|
36
|
-
if (input.runId !== null) {
|
|
37
|
-
const [run] = await tx<{ id: string }[]>`
|
|
38
|
-
SELECT id FROM agent_runs WHERE project_id = ${input.projectId} AND id = ${input.runId}
|
|
39
|
-
`
|
|
40
|
-
|
|
41
|
-
if (!run) {
|
|
42
|
-
throw new AgentStorageError(
|
|
43
|
-
"run_not_found",
|
|
44
|
-
`[SixbPg] Agent run '${input.runId}' not found for project '${input.projectId}'.`
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
54
|
const [seqRow] = await tx<{ next: number | string }[]>`
|
|
50
55
|
SELECT COALESCE(MAX(seq), 0) + 1 AS next FROM agent_messages
|
|
51
56
|
WHERE project_id = ${input.projectId} AND thread_id = ${input.threadId}
|
|
@@ -104,6 +109,54 @@ export class PgAgentMessageStore implements AgentMessageStore {
|
|
|
104
109
|
}
|
|
105
110
|
}
|
|
106
111
|
|
|
112
|
+
async deleteByRunId(input: DeleteAgentMessagesByRunInput): Promise<number> {
|
|
113
|
+
return runPgTransaction(this.sql, async (tx) => {
|
|
114
|
+
const [thread] = await tx<{ id: string }[]>`
|
|
115
|
+
SELECT id FROM agent_threads
|
|
116
|
+
WHERE project_id = ${input.projectId} AND id = ${input.threadId}
|
|
117
|
+
FOR UPDATE
|
|
118
|
+
`
|
|
119
|
+
|
|
120
|
+
if (!thread) {
|
|
121
|
+
throw new AgentStorageError(
|
|
122
|
+
"thread_not_found",
|
|
123
|
+
`[SixbPg] Agent thread '${input.threadId}' not found for project '${input.projectId}'.`
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const deleted = await tx<{ id: string }[]>`
|
|
128
|
+
DELETE FROM agent_messages
|
|
129
|
+
WHERE project_id = ${input.projectId}
|
|
130
|
+
AND thread_id = ${input.threadId}
|
|
131
|
+
AND run_id = ${input.runId}
|
|
132
|
+
RETURNING id
|
|
133
|
+
`
|
|
134
|
+
if (deleted.length === 0) {
|
|
135
|
+
return 0
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const updatedAt = new Date()
|
|
139
|
+
await tx`
|
|
140
|
+
UPDATE agent_threads
|
|
141
|
+
SET
|
|
142
|
+
message_count = (
|
|
143
|
+
SELECT COUNT(*) FROM agent_messages
|
|
144
|
+
WHERE project_id = ${input.projectId} AND thread_id = ${input.threadId}
|
|
145
|
+
),
|
|
146
|
+
last_message_at = (
|
|
147
|
+
SELECT created_at FROM agent_messages
|
|
148
|
+
WHERE project_id = ${input.projectId} AND thread_id = ${input.threadId}
|
|
149
|
+
ORDER BY seq DESC
|
|
150
|
+
LIMIT 1
|
|
151
|
+
),
|
|
152
|
+
updated_at = ${updatedAt}
|
|
153
|
+
WHERE project_id = ${input.projectId} AND id = ${input.threadId}
|
|
154
|
+
`
|
|
155
|
+
|
|
156
|
+
return deleted.length
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
107
160
|
async getById(params: { projectId: string; id: string }): Promise<AgentMessageRecord | null> {
|
|
108
161
|
const [row] = await this.sql<AgentMessageRow[]>`
|
|
109
162
|
SELECT * FROM agent_messages WHERE project_id = ${params.projectId} AND id = ${params.id}
|
|
@@ -121,6 +174,11 @@ export class PgAgentMessageStore implements AgentMessageStore {
|
|
|
121
174
|
const params: SqlParameter[] = [input.projectId, input.threadId]
|
|
122
175
|
let index = 3
|
|
123
176
|
|
|
177
|
+
if (input.afterSeq !== undefined) {
|
|
178
|
+
whereClauses.push(`seq > $${index++}`)
|
|
179
|
+
params.push(input.afterSeq)
|
|
180
|
+
}
|
|
181
|
+
|
|
124
182
|
if (input.roles) {
|
|
125
183
|
const placeholders = input.roles.map(() => `$${index++}`)
|
|
126
184
|
whereClauses.push(`role IN (${placeholders.join(", ")})`)
|
package/src/agents/rows.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AgentMessagePart, JsonValue, Principal } from "@sixb/core"
|
|
2
|
+
import { parseSixbFailure } from "@sixb/core/internal/errors"
|
|
2
3
|
import {
|
|
4
|
+
AGENT_RUN_FAILURE_CODES,
|
|
5
|
+
type AgentContextCheckpointRecord,
|
|
3
6
|
type AgentMessageRecord,
|
|
4
7
|
type AgentRunDiagnostic,
|
|
5
8
|
type AgentRunRecord,
|
|
6
|
-
type AgentRunUsage,
|
|
7
9
|
type AgentThreadRecord,
|
|
8
10
|
coerceAgentRunFinishReason,
|
|
9
11
|
} from "@sixb/core/storage"
|
|
@@ -30,22 +32,15 @@ export interface AgentThreadRow {
|
|
|
30
32
|
export interface AgentRunRow {
|
|
31
33
|
project_id: string
|
|
32
34
|
id: string
|
|
35
|
+
execution_id: string
|
|
33
36
|
thread_id: string
|
|
34
37
|
agent_id: string
|
|
35
38
|
trigger_message_id: string
|
|
36
|
-
|
|
37
|
-
requested_by_principal_id: string | null
|
|
38
|
-
execution_principal_type: "serviceAccount" | null
|
|
39
|
-
execution_principal_id: string | null
|
|
39
|
+
requester_group_ids: string[] | string
|
|
40
40
|
status: AgentRunRecord["status"]
|
|
41
41
|
model_id: string | null
|
|
42
42
|
finish_reason: string | null
|
|
43
|
-
|
|
44
|
-
usage_output_tokens: number | string | null
|
|
45
|
-
usage_total_tokens: number | string | null
|
|
46
|
-
usage_reasoning_tokens: number | string | null
|
|
47
|
-
usage_cached_input_tokens: number | string | null
|
|
48
|
-
error: string | null
|
|
43
|
+
error: JsonValue | null
|
|
49
44
|
diagnostics: AgentRunDiagnostic[] | string | null
|
|
50
45
|
attempt: number | string
|
|
51
46
|
execution_token: string | null
|
|
@@ -71,6 +66,23 @@ export interface AgentMessageRow {
|
|
|
71
66
|
completed_at: Date | string | null
|
|
72
67
|
}
|
|
73
68
|
|
|
69
|
+
export interface AgentContextCheckpointRow {
|
|
70
|
+
project_id: string
|
|
71
|
+
id: string
|
|
72
|
+
thread_id: string
|
|
73
|
+
created_by_run_id: string
|
|
74
|
+
previous_checkpoint_id: string | null
|
|
75
|
+
reason: AgentContextCheckpointRecord["reason"]
|
|
76
|
+
summary: string
|
|
77
|
+
summary_format_version: number | string
|
|
78
|
+
summarized_through_seq: number | string
|
|
79
|
+
observed_head_seq: number | string
|
|
80
|
+
estimated_input_tokens_before: number | string
|
|
81
|
+
estimated_input_tokens_after: number | string
|
|
82
|
+
summary_model_id: string
|
|
83
|
+
created_at: Date | string
|
|
84
|
+
}
|
|
85
|
+
|
|
74
86
|
// ── row → record mappers ────────────────────────────────────────────────────────────────────────
|
|
75
87
|
|
|
76
88
|
export function rowToThreadRecord(row: AgentThreadRow): AgentThreadRecord {
|
|
@@ -93,21 +105,18 @@ export function rowToRunRecord(row: AgentRunRow): AgentRunRecord {
|
|
|
93
105
|
return {
|
|
94
106
|
id: row.id,
|
|
95
107
|
projectId: row.project_id,
|
|
108
|
+
executionId: row.execution_id,
|
|
96
109
|
threadId: row.thread_id,
|
|
97
110
|
agentId: row.agent_id,
|
|
98
111
|
triggerMessageId: row.trigger_message_id,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
row.execution_principal_type,
|
|
104
|
-
row.execution_principal_id
|
|
105
|
-
),
|
|
112
|
+
requesterGroupIds:
|
|
113
|
+
typeof row.requester_group_ids === "string"
|
|
114
|
+
? (JSON.parse(row.requester_group_ids) as string[])
|
|
115
|
+
: row.requester_group_ids,
|
|
106
116
|
status: row.status,
|
|
107
117
|
modelId: row.model_id ?? undefined,
|
|
108
118
|
finishReason: coerceAgentRunFinishReason(row.finish_reason),
|
|
109
|
-
|
|
110
|
-
error: row.error ?? undefined,
|
|
119
|
+
error: row.error === null ? undefined : parseSixbFailure(row.error, AGENT_RUN_FAILURE_CODES),
|
|
111
120
|
diagnostics: normalizeDiagnostics(row.diagnostics),
|
|
112
121
|
attempt: Number(row.attempt),
|
|
113
122
|
execution:
|
|
@@ -143,6 +152,33 @@ export function rowToMessageRecord(row: AgentMessageRow): AgentMessageRecord {
|
|
|
143
152
|
}
|
|
144
153
|
}
|
|
145
154
|
|
|
155
|
+
export function rowToContextCheckpointRecord(
|
|
156
|
+
row: AgentContextCheckpointRow
|
|
157
|
+
): AgentContextCheckpointRecord {
|
|
158
|
+
const summaryFormatVersion = Number(row.summary_format_version)
|
|
159
|
+
if (summaryFormatVersion !== 1) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
`[SixbPg] Unsupported agent context checkpoint summary format version '${summaryFormatVersion}'.`
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
id: row.id,
|
|
166
|
+
projectId: row.project_id,
|
|
167
|
+
threadId: row.thread_id,
|
|
168
|
+
createdByRunId: row.created_by_run_id,
|
|
169
|
+
previousCheckpointId: row.previous_checkpoint_id ?? undefined,
|
|
170
|
+
reason: row.reason,
|
|
171
|
+
summary: row.summary,
|
|
172
|
+
summaryFormatVersion: 1,
|
|
173
|
+
summarizedThroughSeq: Number(row.summarized_through_seq),
|
|
174
|
+
observedHeadSeq: Number(row.observed_head_seq),
|
|
175
|
+
estimatedInputTokensBefore: Number(row.estimated_input_tokens_before),
|
|
176
|
+
estimatedInputTokensAfter: Number(row.estimated_input_tokens_after),
|
|
177
|
+
summaryModelId: row.summary_model_id,
|
|
178
|
+
createdAt: new Date(row.created_at),
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
146
182
|
function principalFromColumns(
|
|
147
183
|
type: Principal["type"] | null | undefined,
|
|
148
184
|
id: string | null | undefined
|
|
@@ -150,28 +186,6 @@ function principalFromColumns(
|
|
|
150
186
|
return type && id ? { type, id } : undefined
|
|
151
187
|
}
|
|
152
188
|
|
|
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: Number(row.usage_input_tokens) }),
|
|
163
|
-
...(row.usage_output_tokens === null ? {} : { outputTokens: Number(row.usage_output_tokens) }),
|
|
164
|
-
...(row.usage_total_tokens === null ? {} : { totalTokens: Number(row.usage_total_tokens) }),
|
|
165
|
-
...(row.usage_reasoning_tokens === null
|
|
166
|
-
? {}
|
|
167
|
-
: { reasoningTokens: Number(row.usage_reasoning_tokens) }),
|
|
168
|
-
...(row.usage_cached_input_tokens === null
|
|
169
|
-
? {}
|
|
170
|
-
: { cachedInputTokens: Number(row.usage_cached_input_tokens) }),
|
|
171
|
-
}
|
|
172
|
-
return Object.keys(usage).length === 0 ? undefined : usage
|
|
173
|
-
}
|
|
174
|
-
|
|
175
189
|
function normalizeMetadata(value: unknown): AgentMessageRecord["metadata"] {
|
|
176
190
|
if (value === null || value === undefined) {
|
|
177
191
|
return undefined
|
package/src/agents/runs.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { assertAgentRunExecution } from "@sixb/core/internal/agent-run-storage-provider"
|
|
2
|
+
import { normalizeRequesterGroupIds } from "@sixb/core/internal/auth"
|
|
3
|
+
import { serializeSixbFailure } from "@sixb/core/internal/errors"
|
|
1
4
|
import {
|
|
5
|
+
AGENT_RUN_FAILURE_CODES,
|
|
2
6
|
type AgentRunRecord,
|
|
3
7
|
type AgentRunStore,
|
|
4
8
|
AgentStorageError,
|
|
5
9
|
type ConfirmAgentRunExecutionOwnershipInput,
|
|
6
10
|
type CreateAgentRunInput,
|
|
11
|
+
type ExecutionStorage,
|
|
7
12
|
type FinishAgentRunInput,
|
|
8
13
|
type FinishQueuedAgentRunInput,
|
|
9
14
|
type ListAgentRunsInput,
|
|
@@ -20,10 +25,20 @@ import { type AgentRunRow, rowToRunRecord } from "./rows"
|
|
|
20
25
|
const PG_RUN_ID_BATCH_SIZE = 10_000
|
|
21
26
|
|
|
22
27
|
export class PgAgentRunStore implements AgentRunStore {
|
|
23
|
-
constructor(
|
|
28
|
+
constructor(
|
|
29
|
+
private readonly sql: PgStoreClient,
|
|
30
|
+
private readonly executions: ExecutionStorage
|
|
31
|
+
) {}
|
|
24
32
|
|
|
25
33
|
async create(input: CreateAgentRunInput): Promise<AgentRunRecord> {
|
|
26
34
|
const createdAt = input.createdAt ?? new Date()
|
|
35
|
+
await assertAgentRunExecution({
|
|
36
|
+
executions: this.executions,
|
|
37
|
+
projectId: input.projectId,
|
|
38
|
+
executionId: input.executionId,
|
|
39
|
+
runId: input.id,
|
|
40
|
+
agentId: input.agentId,
|
|
41
|
+
})
|
|
27
42
|
|
|
28
43
|
try {
|
|
29
44
|
return await runPgTransaction(this.sql, async (tx) => {
|
|
@@ -51,22 +66,22 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
51
66
|
INSERT INTO agent_runs (
|
|
52
67
|
project_id,
|
|
53
68
|
id,
|
|
69
|
+
execution_id,
|
|
54
70
|
thread_id,
|
|
55
71
|
agent_id,
|
|
56
72
|
trigger_message_id,
|
|
57
|
-
|
|
58
|
-
requested_by_principal_id,
|
|
73
|
+
requester_group_ids,
|
|
59
74
|
status,
|
|
60
75
|
attempt,
|
|
61
76
|
created_at
|
|
62
77
|
) VALUES (
|
|
63
78
|
${input.projectId},
|
|
64
79
|
${input.id},
|
|
80
|
+
${input.executionId},
|
|
65
81
|
${input.threadId},
|
|
66
82
|
${input.agentId},
|
|
67
83
|
${input.triggerMessageId},
|
|
68
|
-
${input.
|
|
69
|
-
${input.requestedByPrincipal.id},
|
|
84
|
+
${JSON.stringify(normalizeRequesterGroupIds(input.requesterGroupIds))}::text::jsonb,
|
|
70
85
|
${"queued"},
|
|
71
86
|
${0},
|
|
72
87
|
${createdAt}
|
|
@@ -101,8 +116,6 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
101
116
|
UPDATE agent_runs
|
|
102
117
|
SET
|
|
103
118
|
status = ${"running"},
|
|
104
|
-
execution_principal_type = ${input.executionPrincipal?.type ?? null},
|
|
105
|
-
execution_principal_id = ${input.executionPrincipal?.id ?? null},
|
|
106
119
|
model_id = ${input.modelId ?? null},
|
|
107
120
|
attempt = ${1},
|
|
108
121
|
execution_token = ${input.execution.token},
|
|
@@ -121,7 +134,10 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
121
134
|
const run = await this.lockStatus(tx, input.projectId, input.id, "queued")
|
|
122
135
|
const [row] = await tx<AgentRunRow[]>`
|
|
123
136
|
UPDATE agent_runs
|
|
124
|
-
SET
|
|
137
|
+
SET
|
|
138
|
+
status = ${input.status},
|
|
139
|
+
error = ${input.error === undefined ? null : serializeSixbFailure(input.error, AGENT_RUN_FAILURE_CODES)}::text::jsonb,
|
|
140
|
+
completed_at = ${completedAt}
|
|
125
141
|
WHERE project_id = ${input.projectId} AND id = ${input.id}
|
|
126
142
|
RETURNING *
|
|
127
143
|
`
|
|
@@ -174,7 +190,10 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
174
190
|
|
|
175
191
|
async finish(input: FinishAgentRunInput): Promise<AgentRunRecord> {
|
|
176
192
|
const completedAt = input.completedAt ?? new Date()
|
|
177
|
-
const errorValue =
|
|
193
|
+
const errorValue =
|
|
194
|
+
input.status === "succeeded" || input.error === undefined
|
|
195
|
+
? null
|
|
196
|
+
: serializeSixbFailure(input.error, AGENT_RUN_FAILURE_CODES)
|
|
178
197
|
|
|
179
198
|
return runPgTransaction(this.sql, async (tx) => {
|
|
180
199
|
const run = await this.lockRunning(tx, input.projectId, input.id)
|
|
@@ -192,12 +211,7 @@ export class PgAgentRunStore implements AgentRunStore {
|
|
|
192
211
|
status = ${input.status},
|
|
193
212
|
model_id = COALESCE(${input.modelId ?? null}, model_id),
|
|
194
213
|
finish_reason = ${input.finishReason ?? null},
|
|
195
|
-
|
|
196
|
-
usage_output_tokens = ${input.usage?.outputTokens ?? null},
|
|
197
|
-
usage_total_tokens = ${input.usage?.totalTokens ?? null},
|
|
198
|
-
usage_reasoning_tokens = ${input.usage?.reasoningTokens ?? null},
|
|
199
|
-
usage_cached_input_tokens = ${input.usage?.cachedInputTokens ?? null},
|
|
200
|
-
error = ${errorValue},
|
|
214
|
+
error = ${errorValue}::text::jsonb,
|
|
201
215
|
diagnostics = ${input.diagnostics === undefined ? null : JSON.stringify(input.diagnostics)}::text::jsonb,
|
|
202
216
|
execution_token = ${null},
|
|
203
217
|
execution_queue_lease_expires_at = ${null},
|
|
@@ -85,28 +85,27 @@ export class PgAuthServiceAccountStore implements AuthServiceAccountStore {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
async update(input: UpdateAuthServiceAccountInput): Promise<ServiceAccountRecord> {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
throw new AuthStorageError(
|
|
91
|
-
"missing_service_account",
|
|
92
|
-
`[Sixb] Service account '${input.id}' not found for project '${input.projectId}'.`
|
|
93
|
-
)
|
|
94
|
-
}
|
|
95
|
-
|
|
88
|
+
const id = assertNonEmpty(input.id, "Service account id")
|
|
89
|
+
const projectId = assertNonEmpty(input.projectId, "Project id")
|
|
96
90
|
const updatedAt = dateOrNow(input.updatedAt)
|
|
97
91
|
const name =
|
|
98
|
-
input.name === undefined ?
|
|
99
|
-
const description = input.description === undefined ? existing.description : input.description
|
|
92
|
+
input.name === undefined ? null : assertNonEmpty(input.name, "Service account name")
|
|
100
93
|
const [row] = await this.sql<PgAuthServiceAccountRow[]>`
|
|
101
94
|
UPDATE auth_service_accounts
|
|
102
|
-
SET name = ${name},
|
|
103
|
-
description = ${description ?? null},
|
|
104
|
-
status = ${input.status ??
|
|
95
|
+
SET name = COALESCE(${name}, name),
|
|
96
|
+
description = COALESCE(${input.description ?? null}, description),
|
|
97
|
+
status = COALESCE(${input.status ?? null}, status),
|
|
105
98
|
updated_at = ${updatedAt}
|
|
106
|
-
WHERE project_id = ${
|
|
107
|
-
AND id = ${
|
|
99
|
+
WHERE project_id = ${projectId}
|
|
100
|
+
AND id = ${id}
|
|
108
101
|
RETURNING *
|
|
109
102
|
`
|
|
103
|
+
if (!row) {
|
|
104
|
+
throw new AuthStorageError(
|
|
105
|
+
"missing_service_account",
|
|
106
|
+
`[Sixb] Service account '${id}' not found for project '${projectId}'.`
|
|
107
|
+
)
|
|
108
|
+
}
|
|
110
109
|
|
|
111
110
|
return rowToServiceAccountRecord(row)
|
|
112
111
|
}
|
|
@@ -128,14 +128,6 @@ export async function getUserRowByEmail(
|
|
|
128
128
|
return row ?? null
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
export async function getUserByEmail(
|
|
132
|
-
sql: SQLClient,
|
|
133
|
-
params: { readonly projectId: string; readonly email: string }
|
|
134
|
-
): Promise<UserRecord | null> {
|
|
135
|
-
const row = await getUserRowByEmail(sql, params)
|
|
136
|
-
return row ? rowToUserRecord(row) : null
|
|
137
|
-
}
|
|
138
|
-
|
|
139
131
|
export async function requireUserById(
|
|
140
132
|
sql: SQLClient,
|
|
141
133
|
params: { readonly projectId: string; readonly id: string }
|