@sixb/pg 0.1.2 → 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/dist/agents/agent-storage.d.ts +5 -3
- 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 +18 -1
- package/dist/agents/rows.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +847 -30
- package/dist/index.js.map +12 -10
- package/dist/migrations.d.ts.map +1 -1
- package/dist/ontology-storage/materialization-state.d.ts.map +1 -1
- package/dist/ontology-storage/materialization-writer.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/package.json +3 -3
- package/src/agents/agent-storage.ts +6 -3
- 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 +45 -0
- package/src/index.ts +7 -1
- 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 +10 -0
- package/src/ontology-storage/materialization-state.ts +2 -0
- package/src/ontology-storage/materialization-writer.ts +5 -3
- package/src/pg-ai-cost-storage.ts +707 -0
package/dist/migrations.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAGV,YAAY,EACZ,aAAa,EACb,oBAAoB,EACrB,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAGV,YAAY,EACZ,aAAa,EACb,oBAAoB,EACrB,MAAM,oBAAoB,CAAA;AAmE3B,OAAO,KAAK,EAAE,GAAG,EAAa,MAAM,aAAa,CAAA;AAEjD,MAAM,WAAW,wBAAwB;IACvC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACrC;AAED,eAAO,MAAM,2BAA2B,wBAAwB,CAAA;AAEhE,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,wBAAwB,CAAC,CAE1F;AAED,wBAAgB,MAAM,CACpB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAC/D,OAAO,GAAE,oBAAyB,GACjC,aAAa,CAAC,wBAAwB,CAAC,CAEzC;AAED,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,GACjB,SAAS,eAAe,EAAE,CAQ5B;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAA;IACjB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC,wBAAwB,CAAC,CAAA;CAC5D,GAAG,eAAe,CAqBlB;AAED,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMxF;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5E;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAKrD;AA6KD,eAAO,MAAM,yBAAyB,wCAgCpC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"materialization-state.d.ts","sourceRoot":"","sources":["../../src/ontology-storage/materialization-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,eAAe,EACf,iBAAiB,EAEjB,kBAAkB,EACnB,MAAM,qCAAqC,CAAA;AAgB5C,OAAO,KAAK,EACV,6BAA6B,EAC7B,wBAAwB,EACxB,0BAA0B,EAC1B,0BAA0B,EAC1B,4BAA4B,EAO5B,oBAAoB,EACrB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAW7C,eAAO,MAAM,6BAA6B,kCAAkC,CAAA;AAC5E,eAAO,MAAM,yBAAyB,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"materialization-state.d.ts","sourceRoot":"","sources":["../../src/ontology-storage/materialization-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"materialization-writer.d.ts","sourceRoot":"","sources":["../../src/ontology-storage/materialization-writer.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAG7C,oFAAoF;AACpF,qBAAa,uBAAuB;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAErC,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;YAOlF,cAAc;
|
|
1
|
+
{"version":3,"file":"materialization-writer.d.ts","sourceRoot":"","sources":["../../src/ontology-storage/materialization-writer.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAG7C,oFAAoF;AACpF,qBAAa,uBAAuB;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAErC,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;YAOlF,cAAc;YA2Kd,cAAc;YAqDd,YAAY;YA+DZ,UAAU;YAqEV,eAAe;YA+Ff,WAAW;CAwC1B"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type AiAccountingOverview, type AiCostStorage, type AiCostSummary, type AiModelCallCostRecord, type ListAiModelCallAccountingInput, type ListAiModelCallAccountingResult, type QueryAiAccountingOverviewInput, type SummarizeAiCostExecutionsInput } from "@sixb/core/storage";
|
|
2
|
+
import type { PgStoreClient } from "./transactions";
|
|
3
|
+
/** PostgreSQL-backed immutable valuations stored in one append-only table. */
|
|
4
|
+
export declare class PgAiCostStorage implements AiCostStorage {
|
|
5
|
+
private readonly sql;
|
|
6
|
+
constructor(sql: PgStoreClient);
|
|
7
|
+
recordModelCallCost(input: AiModelCallCostRecord): Promise<void>;
|
|
8
|
+
summarizeExecutions(input: SummarizeAiCostExecutionsInput): Promise<readonly AiCostSummary[]>;
|
|
9
|
+
queryProjectOverview(input: QueryAiAccountingOverviewInput): Promise<AiAccountingOverview>;
|
|
10
|
+
listModelCalls(input: ListAiModelCallAccountingInput): Promise<ListAiModelCallAccountingResult>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=pg-ai-cost-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pg-ai-cost-storage.d.ts","sourceRoot":"","sources":["../src/pg-ai-cost-storage.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAElB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAE1B,KAAK,8BAA8B,EACnC,KAAK,+BAA+B,EACpC,KAAK,8BAA8B,EACnC,KAAK,8BAA8B,EACpC,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAGnD,8EAA8E;AAC9E,qBAAa,eAAgB,YAAW,aAAa;IACvC,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,aAAa;IAEzC,mBAAmB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BhE,mBAAmB,CACvB,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC;IAsB9B,oBAAoB,CAAC,KAAK,EAAE,8BAA8B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA6G1F,cAAc,CAClB,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,+BAA+B,CAAC;CAsF5C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sixb/pg",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "PostgreSQL storage provider for Sixb",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sixb",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"postgres": "^3.4.5"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@sixb/core": "0.1.
|
|
43
|
+
"@sixb/core": "0.1.3"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@sixb/core": "0.1.
|
|
46
|
+
"@sixb/core": "0.1.3",
|
|
47
47
|
"@types/bun": "latest",
|
|
48
48
|
"typescript": "^5.7.0"
|
|
49
49
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentStorage, ExecutionStorage } from "@sixb/core/storage"
|
|
2
2
|
import type { PgStoreClient } from "../transactions"
|
|
3
|
+
import { PgAgentContextCheckpointStore } from "./checkpoints"
|
|
3
4
|
import { PgAgentMessageStore } from "./messages"
|
|
4
5
|
import { PgAgentRunStore } from "./runs"
|
|
5
6
|
import { PgAgentThreadStore } from "./threads"
|
|
@@ -10,18 +11,20 @@ export interface PgAgentStorageOptions {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
|
-
* PostgreSQL-backed agent persistence: thread
|
|
14
|
-
* Cross-table writes
|
|
15
|
-
*
|
|
14
|
+
* PostgreSQL-backed agent persistence: thread, run, message, and context-checkpoint sub-stores
|
|
15
|
+
* sharing one connection. Cross-table writes run inside `runPgTransaction` with `SELECT ... FOR
|
|
16
|
+
* UPDATE` row locks for single-flight and checkpoint compare-and-swap safety.
|
|
16
17
|
*/
|
|
17
18
|
export class PgAgentStorage implements AgentStorage {
|
|
18
19
|
readonly threads: PgAgentThreadStore
|
|
19
20
|
readonly runs: PgAgentRunStore
|
|
20
21
|
readonly messages: PgAgentMessageStore
|
|
22
|
+
readonly checkpoints: PgAgentContextCheckpointStore
|
|
21
23
|
|
|
22
24
|
constructor(options: PgAgentStorageOptions) {
|
|
23
25
|
this.threads = new PgAgentThreadStore(options.sql)
|
|
24
26
|
this.runs = new PgAgentRunStore(options.sql, options.executions)
|
|
25
27
|
this.messages = new PgAgentMessageStore(options.sql)
|
|
28
|
+
this.checkpoints = new PgAgentContextCheckpointStore(options.sql)
|
|
26
29
|
}
|
|
27
30
|
}
|
|
@@ -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
|
@@ -2,6 +2,7 @@ import type { AgentMessagePart, JsonValue, Principal } from "@sixb/core"
|
|
|
2
2
|
import { parseSixbFailure } from "@sixb/core/internal/errors"
|
|
3
3
|
import {
|
|
4
4
|
AGENT_RUN_FAILURE_CODES,
|
|
5
|
+
type AgentContextCheckpointRecord,
|
|
5
6
|
type AgentMessageRecord,
|
|
6
7
|
type AgentRunDiagnostic,
|
|
7
8
|
type AgentRunRecord,
|
|
@@ -65,6 +66,23 @@ export interface AgentMessageRow {
|
|
|
65
66
|
completed_at: Date | string | null
|
|
66
67
|
}
|
|
67
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
|
+
|
|
68
86
|
// ── row → record mappers ────────────────────────────────────────────────────────────────────────
|
|
69
87
|
|
|
70
88
|
export function rowToThreadRecord(row: AgentThreadRow): AgentThreadRecord {
|
|
@@ -134,6 +152,33 @@ export function rowToMessageRecord(row: AgentMessageRow): AgentMessageRecord {
|
|
|
134
152
|
}
|
|
135
153
|
}
|
|
136
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
|
+
|
|
137
182
|
function principalFromColumns(
|
|
138
183
|
type: Principal["type"] | null | undefined,
|
|
139
184
|
id: string | null | undefined
|
package/src/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
createWorkflowRunOperationScope,
|
|
20
20
|
} from "@sixb/core/internal/storage-operation-scope"
|
|
21
21
|
import {
|
|
22
|
+
type AiCostStorage,
|
|
22
23
|
type AiUsageStorage,
|
|
23
24
|
createTransactionStorageProxy,
|
|
24
25
|
type ObjectStorage,
|
|
@@ -31,6 +32,7 @@ import { PgConnectorConnectionStorage } from "./connector-connection-storage"
|
|
|
31
32
|
import { createPostgresStorageMigrators, dropSchema } from "./migrations"
|
|
32
33
|
import { PgOntologyStorage, type PgOntologyTransactionContext } from "./ontology-storage"
|
|
33
34
|
import { PgActionRunStorage } from "./pg-action-run-storage"
|
|
35
|
+
import { PgAiCostStorage } from "./pg-ai-cost-storage"
|
|
34
36
|
import { PgAiUsageStorage } from "./pg-ai-usage-storage"
|
|
35
37
|
import { createPgClient, type SQL, type SQLClient } from "./pg-client"
|
|
36
38
|
import { PgExecutionStorage } from "./pg-execution-storage"
|
|
@@ -110,7 +112,7 @@ export interface PostgresStorageOptions {
|
|
|
110
112
|
/**
|
|
111
113
|
* PostgreSQL storage provider for Sixb.
|
|
112
114
|
*
|
|
113
|
-
* Bundles Sixb storage adapters, including the AI
|
|
115
|
+
* Bundles Sixb storage adapters, including the AI accounting ledger, behind a shared PostgreSQL
|
|
114
116
|
* connection pool (porsager `postgres`), which reliably reclaims connections under load.
|
|
115
117
|
*
|
|
116
118
|
* The storage exposes a core `StorageMigrator`. Sixb CLI startup and
|
|
@@ -135,6 +137,7 @@ export class PostgresStorage implements MigrationCapableStorage {
|
|
|
135
137
|
readonly executions: PgExecutionStorage
|
|
136
138
|
readonly agents: PgAgentStorage
|
|
137
139
|
readonly aiUsage: AiUsageStorage
|
|
140
|
+
readonly aiCosts: AiCostStorage
|
|
138
141
|
readonly actionRuns: PgActionRunStorage
|
|
139
142
|
readonly pipelineRuns: PgPipelineRunStorage
|
|
140
143
|
readonly workflowRuns: PgWorkflowRunStorage
|
|
@@ -208,6 +211,7 @@ export class PostgresStorage implements MigrationCapableStorage {
|
|
|
208
211
|
this.executions = createOperationScopedFacade(stores.executions, scope)
|
|
209
212
|
this.agents = createAgentOperationScope(stores.agents, scope)
|
|
210
213
|
this.aiUsage = createOperationScopedFacade(stores.aiUsage, scope)
|
|
214
|
+
this.aiCosts = createOperationScopedFacade(stores.aiCosts, scope)
|
|
211
215
|
this.actionRuns = createOperationScopedFacade(stores.actionRuns, scope)
|
|
212
216
|
this.pipelineRuns = createOperationScopedFacade(stores.pipelineRuns, scope)
|
|
213
217
|
this.workflowRuns = createWorkflowRunOperationScope(stores.workflowRuns, scope)
|
|
@@ -337,6 +341,7 @@ function createPostgresStores(
|
|
|
337
341
|
executions,
|
|
338
342
|
agents: new PgAgentStorage({ sql, executions }),
|
|
339
343
|
aiUsage: new PgAiUsageStorage(sql),
|
|
344
|
+
aiCosts: new PgAiCostStorage(sql),
|
|
340
345
|
actionRuns: new PgActionRunStorage(sql, executions),
|
|
341
346
|
pipelineRuns: new PgPipelineRunStorage(sql, executions),
|
|
342
347
|
workflowRuns: new PgWorkflowRunStorage(sql, executions),
|
|
@@ -357,6 +362,7 @@ interface PostgresStoreSet {
|
|
|
357
362
|
readonly executions: PgExecutionStorage
|
|
358
363
|
readonly agents: PgAgentStorage
|
|
359
364
|
readonly aiUsage: PgAiUsageStorage
|
|
365
|
+
readonly aiCosts: PgAiCostStorage
|
|
360
366
|
readonly actionRuns: PgActionRunStorage
|
|
361
367
|
readonly pipelineRuns: PgPipelineRunStorage
|
|
362
368
|
readonly workflowRuns: PgWorkflowRunStorage
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
CREATE TABLE ai_model_call_valuations (
|
|
2
|
+
project_id TEXT NOT NULL CHECK (length(btrim(project_id)) > 0),
|
|
3
|
+
usage_record_id TEXT NOT NULL CHECK (length(btrim(usage_record_id)) > 0),
|
|
4
|
+
status TEXT NOT NULL CHECK (status IN ('rated', 'unpriceable')),
|
|
5
|
+
provider_id TEXT CHECK (provider_id IS NULL OR length(btrim(provider_id)) > 0),
|
|
6
|
+
model_id TEXT CHECK (model_id IS NULL OR length(btrim(model_id)) > 0),
|
|
7
|
+
currency TEXT CHECK (currency IS NULL OR currency ~ '^[A-Z]{3}$'),
|
|
8
|
+
amount_nanos BIGINT CHECK (amount_nanos IS NULL OR amount_nanos >= 0),
|
|
9
|
+
reason TEXT CHECK (
|
|
10
|
+
reason IS NULL OR reason IN (
|
|
11
|
+
'missingBillingIdentity',
|
|
12
|
+
'missingCatalogEntry',
|
|
13
|
+
'missingUsageMeter',
|
|
14
|
+
'unsupportedPricingDimension',
|
|
15
|
+
'invalidUsageForFormula'
|
|
16
|
+
)
|
|
17
|
+
),
|
|
18
|
+
details JSONB NOT NULL CHECK (jsonb_typeof(details) = 'object'),
|
|
19
|
+
rated_at TIMESTAMPTZ NOT NULL,
|
|
20
|
+
PRIMARY KEY (project_id, usage_record_id),
|
|
21
|
+
FOREIGN KEY (project_id, usage_record_id)
|
|
22
|
+
REFERENCES ai_model_call_usage(project_id, id)
|
|
23
|
+
ON DELETE RESTRICT,
|
|
24
|
+
CHECK (
|
|
25
|
+
(
|
|
26
|
+
status = 'rated'
|
|
27
|
+
AND provider_id IS NOT NULL
|
|
28
|
+
AND model_id IS NOT NULL
|
|
29
|
+
AND currency IS NOT NULL
|
|
30
|
+
AND amount_nanos IS NOT NULL
|
|
31
|
+
AND reason IS NULL
|
|
32
|
+
)
|
|
33
|
+
OR
|
|
34
|
+
(
|
|
35
|
+
status = 'unpriceable'
|
|
36
|
+
AND currency IS NULL
|
|
37
|
+
AND amount_nanos IS NULL
|
|
38
|
+
AND reason IS NOT NULL
|
|
39
|
+
AND (
|
|
40
|
+
(reason = 'missingBillingIdentity' AND provider_id IS NULL AND model_id IS NULL)
|
|
41
|
+
OR
|
|
42
|
+
(reason <> 'missingBillingIdentity' AND provider_id IS NOT NULL AND model_id IS NOT NULL)
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
CREATE INDEX idx_ai_model_call_usage_provider_model_time
|
|
49
|
+
ON ai_model_call_usage (project_id, provider_id, requested_model_id, occurred_at, id);
|
|
50
|
+
CREATE INDEX idx_ai_model_call_usage_model_time
|
|
51
|
+
ON ai_model_call_usage (project_id, requested_model_id, occurred_at, id);
|
|
52
|
+
CREATE INDEX idx_ai_model_call_valuations_summary
|
|
53
|
+
ON ai_model_call_valuations (project_id, status, currency, usage_record_id);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
CREATE TABLE agent_context_checkpoints (
|
|
2
|
+
project_id TEXT NOT NULL,
|
|
3
|
+
id TEXT NOT NULL,
|
|
4
|
+
thread_id TEXT NOT NULL,
|
|
5
|
+
created_by_run_id TEXT NOT NULL,
|
|
6
|
+
previous_checkpoint_id TEXT,
|
|
7
|
+
reason TEXT NOT NULL CHECK (reason IN ('threshold', 'overflow')),
|
|
8
|
+
summary TEXT NOT NULL CHECK (length(trim(summary)) > 0),
|
|
9
|
+
summary_format_version INTEGER NOT NULL CHECK (summary_format_version = 1),
|
|
10
|
+
summarized_through_seq INTEGER NOT NULL CHECK (summarized_through_seq >= 1),
|
|
11
|
+
observed_head_seq INTEGER NOT NULL,
|
|
12
|
+
estimated_input_tokens_before BIGINT NOT NULL CHECK (estimated_input_tokens_before >= 0),
|
|
13
|
+
estimated_input_tokens_after BIGINT NOT NULL CHECK (estimated_input_tokens_after >= 0),
|
|
14
|
+
summary_model_id TEXT NOT NULL CHECK (length(trim(summary_model_id)) > 0),
|
|
15
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
16
|
+
PRIMARY KEY (project_id, id),
|
|
17
|
+
UNIQUE (project_id, created_by_run_id),
|
|
18
|
+
FOREIGN KEY (project_id, thread_id)
|
|
19
|
+
REFERENCES agent_threads(project_id, id) ON DELETE CASCADE,
|
|
20
|
+
FOREIGN KEY (project_id, created_by_run_id)
|
|
21
|
+
REFERENCES agent_runs(project_id, id) ON DELETE RESTRICT,
|
|
22
|
+
FOREIGN KEY (project_id, previous_checkpoint_id)
|
|
23
|
+
REFERENCES agent_context_checkpoints(project_id, id) ON DELETE RESTRICT,
|
|
24
|
+
CHECK (summarized_through_seq < observed_head_seq)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE INDEX idx_agent_context_checkpoints_thread_latest
|
|
28
|
+
ON agent_context_checkpoints (
|
|
29
|
+
project_id,
|
|
30
|
+
thread_id,
|
|
31
|
+
summarized_through_seq DESC,
|
|
32
|
+
created_at DESC,
|
|
33
|
+
id DESC
|
|
34
|
+
);
|
package/src/migrations.ts
CHANGED
|
@@ -66,6 +66,13 @@ import ontologyCommitExecutionsSql from "./migrations/024-ontology-commit-execut
|
|
|
66
66
|
import connectorConnectionsSql from "./migrations/025-connector-connections.sql" with {
|
|
67
67
|
type: "text",
|
|
68
68
|
}
|
|
69
|
+
import aiCostAccountingSql from "./migrations/026-ai-cost-accounting.sql" with { type: "text" }
|
|
70
|
+
import agentContextCheckpointsSql from "./migrations/027-agent-context-checkpoints.sql" with {
|
|
71
|
+
type: "text",
|
|
72
|
+
}
|
|
73
|
+
import objectOverrideEditTimesSql from "./migrations/028-object-override-edit-times.sql" with {
|
|
74
|
+
type: "text",
|
|
75
|
+
}
|
|
69
76
|
import type { SQL, SQLClient } from "./pg-client"
|
|
70
77
|
|
|
71
78
|
export interface PostgresMigrationContext {
|
|
@@ -344,6 +351,9 @@ export const postgresStorageMigrations = defineMigrations<PostgresMigrationConte
|
|
|
344
351
|
pgSql("023-webhook-executions", webhookExecutionsSql),
|
|
345
352
|
pgSql("024-ontology-commit-executions", ontologyCommitExecutionsSql),
|
|
346
353
|
pgSql("025-connector-connections", connectorConnectionsSql),
|
|
354
|
+
pgSql("026-ai-cost-accounting", aiCostAccountingSql),
|
|
355
|
+
pgSql("027-agent-context-checkpoints", agentContextCheckpointsSql),
|
|
356
|
+
pgSql("028-object-override-edit-times", objectOverrideEditTimesSql),
|
|
347
357
|
],
|
|
348
358
|
})
|
|
349
359
|
|
|
@@ -98,6 +98,7 @@ interface LinkScopeRow extends EffectiveLinkRow {
|
|
|
98
98
|
interface ObjectOverrideRow extends PgStoredOverrideRow {
|
|
99
99
|
readonly object_type_id: string
|
|
100
100
|
readonly primary_id: string
|
|
101
|
+
readonly edited_at: unknown
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
interface LinkOverrideRow extends PgStoredOverrideRow {
|
|
@@ -1060,6 +1061,7 @@ function storedObjectOverride(row: ObjectOverrideRow): StoredObjectOverride {
|
|
|
1060
1061
|
return {
|
|
1061
1062
|
ref,
|
|
1062
1063
|
value: structuredClone(row.value) as StoredObjectOverride["value"],
|
|
1064
|
+
editedAt: structuredClone(row.edited_at) as Readonly<Record<string, string>>,
|
|
1063
1065
|
lastCommitId: row.last_commit_id,
|
|
1064
1066
|
updatedAt: toIsoString(row.updated_at),
|
|
1065
1067
|
}
|