@earendil-works/pi-session-backend-sqlite-node 0.84.4 → 0.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/README.md +26 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/sqlite/index.d.ts +3 -4
- package/dist/sqlite/index.d.ts.map +1 -1
- package/dist/sqlite/index.js +2 -3
- package/dist/sqlite/index.js.map +1 -1
- package/dist/sqlite/migrations/001_initial.sql +88 -89
- package/dist/sqlite/migrations.d.ts +1 -7
- package/dist/sqlite/migrations.d.ts.map +1 -1
- package/dist/sqlite/migrations.js +3 -35
- package/dist/sqlite/migrations.js.map +1 -1
- package/dist/sqlite/repo.d.ts +38 -38
- package/dist/sqlite/repo.d.ts.map +1 -1
- package/dist/sqlite/repo.js +353 -695
- package/dist/sqlite/repo.js.map +1 -1
- package/dist/sqlite/session/branch-entries.d.ts +6 -0
- package/dist/sqlite/session/branch-entries.d.ts.map +1 -0
- package/dist/sqlite/session/branch-entries.js +213 -0
- package/dist/sqlite/session/branch-entries.js.map +1 -0
- package/dist/sqlite/session/entries.d.ts +24 -0
- package/dist/sqlite/session/entries.d.ts.map +1 -0
- package/dist/sqlite/session/entries.js +126 -0
- package/dist/sqlite/session/entries.js.map +1 -0
- package/dist/sqlite/session/session-row.d.ts +23 -0
- package/dist/sqlite/session/session-row.d.ts.map +1 -0
- package/dist/sqlite/session/session-row.js +69 -0
- package/dist/sqlite/session/session-row.js.map +1 -0
- package/dist/sqlite/session/session-sequences.d.ts +4 -0
- package/dist/sqlite/session/session-sequences.d.ts.map +1 -0
- package/dist/sqlite/session/session-sequences.js +13 -0
- package/dist/sqlite/session/session-sequences.js.map +1 -0
- package/dist/sqlite/session/session-stats.d.ts +6 -0
- package/dist/sqlite/session/session-stats.d.ts.map +1 -0
- package/dist/sqlite/session/session-stats.js +39 -0
- package/dist/sqlite/session/session-stats.js.map +1 -0
- package/dist/sqlite/session/usage-ledger.d.ts +20 -0
- package/dist/sqlite/session/usage-ledger.d.ts.map +1 -0
- package/dist/sqlite/session/usage-ledger.js +50 -0
- package/dist/sqlite/session/usage-ledger.js.map +1 -0
- package/dist/sqlite/session/values.d.ts +23 -0
- package/dist/sqlite/session/values.d.ts.map +1 -0
- package/dist/sqlite/session/values.js +91 -0
- package/dist/sqlite/session/values.js.map +1 -0
- package/dist/sqlite/session.d.ts +42 -0
- package/dist/sqlite/session.d.ts.map +1 -0
- package/dist/sqlite/session.js +163 -0
- package/dist/sqlite/session.js.map +1 -0
- package/dist/sqlite/storage.d.ts +38 -0
- package/dist/sqlite/storage.d.ts.map +1 -0
- package/dist/sqlite/storage.js +161 -0
- package/dist/sqlite/storage.js.map +1 -0
- package/dist/sqlite/types.d.ts +6 -22
- package/dist/sqlite/types.d.ts.map +1 -1
- package/dist/sqlite/types.js.map +1 -1
- package/package.json +5 -4
- package/dist/sqlite/branch-cache.d.ts +0 -6
- package/dist/sqlite/branch-cache.d.ts.map +0 -1
- package/dist/sqlite/branch-cache.js +0 -70
- package/dist/sqlite/branch-cache.js.map +0 -1
- package/dist/sqlite/search-backend.d.ts +0 -14
- package/dist/sqlite/search-backend.d.ts.map +0 -1
- package/dist/sqlite/search-backend.js +0 -153
- package/dist/sqlite/search-backend.js.map +0 -1
- package/dist/sqlite/storage/branch-entries.d.ts +0 -41
- package/dist/sqlite/storage/branch-entries.d.ts.map +0 -1
- package/dist/sqlite/storage/branch-entries.js +0 -112
- package/dist/sqlite/storage/branch-entries.js.map +0 -1
- package/dist/sqlite/storage/branch-tips.d.ts +0 -7
- package/dist/sqlite/storage/branch-tips.d.ts.map +0 -1
- package/dist/sqlite/storage/branch-tips.js +0 -22
- package/dist/sqlite/storage/branch-tips.js.map +0 -1
- package/dist/sqlite/storage/entries.d.ts +0 -34
- package/dist/sqlite/storage/entries.d.ts.map +0 -1
- package/dist/sqlite/storage/entries.js +0 -37
- package/dist/sqlite/storage/entries.js.map +0 -1
- package/dist/sqlite/storage/facts.d.ts +0 -20
- package/dist/sqlite/storage/facts.d.ts.map +0 -1
- package/dist/sqlite/storage/facts.js +0 -38
- package/dist/sqlite/storage/facts.js.map +0 -1
- package/dist/sqlite/storage/lanes.d.ts +0 -35
- package/dist/sqlite/storage/lanes.d.ts.map +0 -1
- package/dist/sqlite/storage/lanes.js +0 -95
- package/dist/sqlite/storage/lanes.js.map +0 -1
- package/dist/sqlite/storage/records.d.ts +0 -38
- package/dist/sqlite/storage/records.d.ts.map +0 -1
- package/dist/sqlite/storage/records.js +0 -49
- package/dist/sqlite/storage/records.js.map +0 -1
- package/dist/sqlite/storage/session-sequences.d.ts +0 -7
- package/dist/sqlite/storage/session-sequences.d.ts.map +0 -1
- package/dist/sqlite/storage/session-sequences.js +0 -22
- package/dist/sqlite/storage/session-sequences.js.map +0 -1
- package/dist/sqlite/storage/session-stats.d.ts +0 -17
- package/dist/sqlite/storage/session-stats.d.ts.map +0 -1
- package/dist/sqlite/storage/session-stats.js +0 -40
- package/dist/sqlite/storage/session-stats.js.map +0 -1
- package/dist/sqlite/storage/sessions.d.ts +0 -26
- package/dist/sqlite/storage/sessions.d.ts.map +0 -1
- package/dist/sqlite/storage/sessions.js +0 -99
- package/dist/sqlite/storage/sessions.js.map +0 -1
- package/dist/sqlite/storage/writer-leases.d.ts +0 -15
- package/dist/sqlite/storage/writer-leases.d.ts.map +0 -1
- package/dist/sqlite/storage/writer-leases.js +0 -31
- package/dist/sqlite/storage/writer-leases.js.map +0 -1
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { joinSqlFragments, sql } from "../sql.js";
|
|
2
|
+
function entryPayload(entry) {
|
|
3
|
+
switch (entry.type) {
|
|
4
|
+
case "message": {
|
|
5
|
+
const payload = {
|
|
6
|
+
message: entry.message,
|
|
7
|
+
...(entry.terminate === undefined ? {} : { terminate: entry.terminate }),
|
|
8
|
+
};
|
|
9
|
+
return payload;
|
|
10
|
+
}
|
|
11
|
+
case "compaction": {
|
|
12
|
+
const payload = {
|
|
13
|
+
summary: entry.summary,
|
|
14
|
+
retainedTail: entry.retainedTail,
|
|
15
|
+
tokensBefore: entry.tokensBefore,
|
|
16
|
+
...(entry.details === undefined ? {} : { details: entry.details }),
|
|
17
|
+
...(entry.usage === undefined ? {} : { usage: entry.usage }),
|
|
18
|
+
fromHook: entry.fromHook,
|
|
19
|
+
};
|
|
20
|
+
return payload;
|
|
21
|
+
}
|
|
22
|
+
case "branch_summary": {
|
|
23
|
+
const payload = {
|
|
24
|
+
fromId: entry.fromId,
|
|
25
|
+
summary: entry.summary,
|
|
26
|
+
...(entry.details === undefined ? {} : { details: entry.details }),
|
|
27
|
+
...(entry.usage === undefined ? {} : { usage: entry.usage }),
|
|
28
|
+
fromHook: entry.fromHook,
|
|
29
|
+
};
|
|
30
|
+
return payload;
|
|
31
|
+
}
|
|
32
|
+
case "custom": {
|
|
33
|
+
const payload = entry.data === undefined ? {} : { data: entry.data };
|
|
34
|
+
return payload;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function parsePayload(row) {
|
|
39
|
+
return JSON.parse(row.payload);
|
|
40
|
+
}
|
|
41
|
+
const INSERT_ENTRY_SQL = `INSERT INTO entries (session_id, id, parent_id, seq, type, custom_type, timestamp, payload)
|
|
42
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
|
|
43
|
+
function entryRowParams(sessionId, entry) {
|
|
44
|
+
return [
|
|
45
|
+
sessionId,
|
|
46
|
+
entry.id,
|
|
47
|
+
entry.parentId,
|
|
48
|
+
entry.seq,
|
|
49
|
+
entry.type,
|
|
50
|
+
entry.type === "custom" ? entry.customType : null,
|
|
51
|
+
entry.timestamp,
|
|
52
|
+
JSON.stringify(entryPayload(entry)),
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
export class EntryRowWriter {
|
|
56
|
+
insertStatement;
|
|
57
|
+
sessionId;
|
|
58
|
+
constructor(db, sessionId) {
|
|
59
|
+
this.insertStatement = db.prepare(INSERT_ENTRY_SQL);
|
|
60
|
+
this.sessionId = sessionId;
|
|
61
|
+
}
|
|
62
|
+
insert(entry) {
|
|
63
|
+
this.insertStatement.run(...entryRowParams(this.sessionId, entry));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function insertEntryRow(db, sessionId, entry) {
|
|
67
|
+
db.prepare(INSERT_ENTRY_SQL).run(...entryRowParams(sessionId, entry));
|
|
68
|
+
}
|
|
69
|
+
export function decodeEntryRow(row) {
|
|
70
|
+
const base = {
|
|
71
|
+
id: row.id,
|
|
72
|
+
parentId: row.parent_id,
|
|
73
|
+
seq: row.seq,
|
|
74
|
+
timestamp: row.timestamp,
|
|
75
|
+
};
|
|
76
|
+
switch (row.type) {
|
|
77
|
+
case "message":
|
|
78
|
+
return { ...base, type: "message", ...parsePayload(row) };
|
|
79
|
+
case "compaction":
|
|
80
|
+
return { ...base, type: "compaction", ...parsePayload(row) };
|
|
81
|
+
case "branch_summary":
|
|
82
|
+
return { ...base, type: "branch_summary", ...parsePayload(row) };
|
|
83
|
+
case "custom":
|
|
84
|
+
if (row.custom_type === null)
|
|
85
|
+
throw new Error(`Custom entry ${row.id} is missing custom_type`);
|
|
86
|
+
return { ...base, type: "custom", customType: row.custom_type, ...parsePayload(row) };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function entryStructureFromRow(row) {
|
|
90
|
+
return {
|
|
91
|
+
id: row.id,
|
|
92
|
+
parentId: row.parent_id,
|
|
93
|
+
seq: row.seq,
|
|
94
|
+
timestamp: row.timestamp,
|
|
95
|
+
type: row.type,
|
|
96
|
+
...(row.custom_type === null ? {} : { customType: row.custom_type }),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export function readEntryRows(db, sessionId, ids) {
|
|
100
|
+
if (ids.length === 0)
|
|
101
|
+
return [];
|
|
102
|
+
const placeholders = joinSqlFragments(ids.map((id) => sql `${id}`), ", ");
|
|
103
|
+
return sql `SELECT id, parent_id, seq, type, custom_type, timestamp, payload
|
|
104
|
+
FROM entries
|
|
105
|
+
WHERE session_id = ${sessionId} AND id IN (${placeholders})`.all(db);
|
|
106
|
+
}
|
|
107
|
+
export function readAllEntryRows(db, sessionId) {
|
|
108
|
+
return sql `SELECT id, parent_id, seq, type, custom_type, timestamp, payload
|
|
109
|
+
FROM entries WHERE session_id = ${sessionId} ORDER BY seq ASC`.all(db);
|
|
110
|
+
}
|
|
111
|
+
export function scanEntryRows(db, sessionId, query) {
|
|
112
|
+
const filters = [sql `session_id = ${sessionId}`];
|
|
113
|
+
if (query.type !== undefined)
|
|
114
|
+
filters.push(sql `type = ${query.type}`);
|
|
115
|
+
if (query.customType !== undefined)
|
|
116
|
+
filters.push(sql `custom_type = ${query.customType}`);
|
|
117
|
+
if (query.fromSeq !== undefined)
|
|
118
|
+
filters.push(sql `seq >= ${query.fromSeq}`);
|
|
119
|
+
if (query.toSeq !== undefined)
|
|
120
|
+
filters.push(sql `seq <= ${query.toSeq}`);
|
|
121
|
+
const order = query.order === "desc" ? sql `ORDER BY seq DESC` : sql `ORDER BY seq ASC`;
|
|
122
|
+
const limit = query.limit === undefined ? sql `` : sql `LIMIT ${Math.max(0, query.limit)}`;
|
|
123
|
+
return sql `SELECT id, parent_id, seq, type, custom_type, timestamp, payload
|
|
124
|
+
FROM entries WHERE ${joinSqlFragments(filters, " AND ")} ${order} ${limit}`.all(db);
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=entries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entries.js","sourceRoot":"","sources":["../../../src/sqlite/session/entries.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tBranchSummaryEntry,\n\tCompactionEntry,\n\tCustomEntry,\n\tEntry,\n\tEntryScan,\n\tEntryStructure,\n\tMessageEntry,\n} from \"@earendil-works/pi-agent-core\";\nimport { joinSqlFragments, type SqlQuery, sql } from \"../sql.ts\";\nimport type { SqliteDatabase, SqliteStatement } from \"../types.ts\";\n\nexport interface EntryRow {\n\tid: string;\n\tparent_id: string | null;\n\tseq: number;\n\ttype: Entry[\"type\"];\n\tcustom_type: string | null;\n\ttimestamp: number;\n\tpayload: string;\n}\n\ntype StoredEntryPayload<TEntry extends Entry> = Omit<\n\tTEntry,\n\t\"id\" | \"parentId\" | \"seq\" | \"timestamp\" | \"type\" | \"customType\"\n>;\n\nfunction entryPayload(entry: Entry): StoredEntryPayload<Entry> {\n\tswitch (entry.type) {\n\t\tcase \"message\": {\n\t\t\tconst payload: StoredEntryPayload<MessageEntry> = {\n\t\t\t\tmessage: entry.message,\n\t\t\t\t...(entry.terminate === undefined ? {} : { terminate: entry.terminate }),\n\t\t\t};\n\t\t\treturn payload;\n\t\t}\n\t\tcase \"compaction\": {\n\t\t\tconst payload: StoredEntryPayload<CompactionEntry> = {\n\t\t\t\tsummary: entry.summary,\n\t\t\t\tretainedTail: entry.retainedTail,\n\t\t\t\ttokensBefore: entry.tokensBefore,\n\t\t\t\t...(entry.details === undefined ? {} : { details: entry.details }),\n\t\t\t\t...(entry.usage === undefined ? {} : { usage: entry.usage }),\n\t\t\t\tfromHook: entry.fromHook,\n\t\t\t};\n\t\t\treturn payload;\n\t\t}\n\t\tcase \"branch_summary\": {\n\t\t\tconst payload: StoredEntryPayload<BranchSummaryEntry> = {\n\t\t\t\tfromId: entry.fromId,\n\t\t\t\tsummary: entry.summary,\n\t\t\t\t...(entry.details === undefined ? {} : { details: entry.details }),\n\t\t\t\t...(entry.usage === undefined ? {} : { usage: entry.usage }),\n\t\t\t\tfromHook: entry.fromHook,\n\t\t\t};\n\t\t\treturn payload;\n\t\t}\n\t\tcase \"custom\": {\n\t\t\tconst payload: StoredEntryPayload<CustomEntry> = entry.data === undefined ? {} : { data: entry.data };\n\t\t\treturn payload;\n\t\t}\n\t}\n}\n\nfunction parsePayload<TEntry extends Entry>(row: EntryRow): StoredEntryPayload<TEntry> {\n\treturn JSON.parse(row.payload) as StoredEntryPayload<TEntry>;\n}\n\nconst INSERT_ENTRY_SQL = `INSERT INTO entries (session_id, id, parent_id, seq, type, custom_type, timestamp, payload)\n\tVALUES (?, ?, ?, ?, ?, ?, ?, ?)`;\n\nfunction entryRowParams(sessionId: string, entry: Entry): unknown[] {\n\treturn [\n\t\tsessionId,\n\t\tentry.id,\n\t\tentry.parentId,\n\t\tentry.seq,\n\t\tentry.type,\n\t\tentry.type === \"custom\" ? entry.customType : null,\n\t\tentry.timestamp,\n\t\tJSON.stringify(entryPayload(entry)),\n\t];\n}\n\nexport class EntryRowWriter {\n\tprivate readonly insertStatement: SqliteStatement;\n\tprivate readonly sessionId: string;\n\n\tconstructor(db: SqliteDatabase, sessionId: string) {\n\t\tthis.insertStatement = db.prepare(INSERT_ENTRY_SQL);\n\t\tthis.sessionId = sessionId;\n\t}\n\n\tinsert(entry: Entry): void {\n\t\tthis.insertStatement.run(...entryRowParams(this.sessionId, entry));\n\t}\n}\n\nexport function insertEntryRow(db: SqliteDatabase, sessionId: string, entry: Entry): void {\n\tdb.prepare(INSERT_ENTRY_SQL).run(...entryRowParams(sessionId, entry));\n}\n\nexport function decodeEntryRow(row: EntryRow): Entry {\n\tconst base = {\n\t\tid: row.id,\n\t\tparentId: row.parent_id,\n\t\tseq: row.seq,\n\t\ttimestamp: row.timestamp,\n\t};\n\tswitch (row.type) {\n\t\tcase \"message\":\n\t\t\treturn { ...base, type: \"message\", ...parsePayload<MessageEntry>(row) };\n\t\tcase \"compaction\":\n\t\t\treturn { ...base, type: \"compaction\", ...parsePayload<CompactionEntry>(row) };\n\t\tcase \"branch_summary\":\n\t\t\treturn { ...base, type: \"branch_summary\", ...parsePayload<BranchSummaryEntry>(row) };\n\t\tcase \"custom\":\n\t\t\tif (row.custom_type === null) throw new Error(`Custom entry ${row.id} is missing custom_type`);\n\t\t\treturn { ...base, type: \"custom\", customType: row.custom_type, ...parsePayload<CustomEntry>(row) };\n\t}\n}\n\nexport function entryStructureFromRow(row: EntryRow): EntryStructure {\n\treturn {\n\t\tid: row.id,\n\t\tparentId: row.parent_id,\n\t\tseq: row.seq,\n\t\ttimestamp: row.timestamp,\n\t\ttype: row.type,\n\t\t...(row.custom_type === null ? {} : { customType: row.custom_type }),\n\t};\n}\n\nexport function readEntryRows(db: SqliteDatabase, sessionId: string, ids: readonly string[]): EntryRow[] {\n\tif (ids.length === 0) return [];\n\tconst placeholders = joinSqlFragments(\n\t\tids.map((id) => sql`${id}`),\n\t\t\", \",\n\t);\n\treturn sql`SELECT id, parent_id, seq, type, custom_type, timestamp, payload\n\t\tFROM entries\n\t\tWHERE session_id = ${sessionId} AND id IN (${placeholders})`.all<EntryRow>(db);\n}\n\nexport function readAllEntryRows(db: SqliteDatabase, sessionId: string): EntryRow[] {\n\treturn sql`SELECT id, parent_id, seq, type, custom_type, timestamp, payload\n\t\tFROM entries WHERE session_id = ${sessionId} ORDER BY seq ASC`.all<EntryRow>(db);\n}\n\nexport function scanEntryRows(db: SqliteDatabase, sessionId: string, query: EntryScan): EntryRow[] {\n\tconst filters: SqlQuery[] = [sql`session_id = ${sessionId}`];\n\tif (query.type !== undefined) filters.push(sql`type = ${query.type}`);\n\tif (query.customType !== undefined) filters.push(sql`custom_type = ${query.customType}`);\n\tif (query.fromSeq !== undefined) filters.push(sql`seq >= ${query.fromSeq}`);\n\tif (query.toSeq !== undefined) filters.push(sql`seq <= ${query.toSeq}`);\n\n\tconst order = query.order === \"desc\" ? sql`ORDER BY seq DESC` : sql`ORDER BY seq ASC`;\n\tconst limit = query.limit === undefined ? sql`` : sql`LIMIT ${Math.max(0, query.limit)}`;\n\treturn sql`SELECT id, parent_id, seq, type, custom_type, timestamp, payload\n\t\tFROM entries WHERE ${joinSqlFragments(filters, \" AND \")} ${order} ${limit}`.all<EntryRow>(db);\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SessionMetadata } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import type { SqliteDatabase } from "../types.ts";
|
|
3
|
+
export interface SessionRow {
|
|
4
|
+
id: string;
|
|
5
|
+
created_at: number;
|
|
6
|
+
parent_session_id: string | null;
|
|
7
|
+
storage_version: number;
|
|
8
|
+
metadata: string | null;
|
|
9
|
+
message_count: number;
|
|
10
|
+
usage_payload: string;
|
|
11
|
+
next_seq: number;
|
|
12
|
+
}
|
|
13
|
+
export interface SqliteSessionMetadata extends SessionMetadata {
|
|
14
|
+
/** SQLite container/shard path containing this session. */
|
|
15
|
+
path: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function readSessionRow(db: SqliteDatabase, sessionId: string): SessionRow;
|
|
18
|
+
export declare function readAllSessionRows(db: SqliteDatabase): SessionRow[];
|
|
19
|
+
export declare function hasSessionRow(db: SqliteDatabase, sessionId: string): boolean;
|
|
20
|
+
export declare function metadataFromSessionRow(path: string, row: SessionRow, currentStorageVersion: number): SqliteSessionMetadata;
|
|
21
|
+
export declare function insertSessionRow(db: SqliteDatabase, metadata: SqliteSessionMetadata, storageVersion: number, nextSeq: number): void;
|
|
22
|
+
export declare function deleteSessionRows(db: SqliteDatabase, sessionId: string): void;
|
|
23
|
+
//# sourceMappingURL=session-row.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-row.d.ts","sourceRoot":"","sources":["../../../src/sqlite/session/session-row.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAGrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC7D,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;CACb;AAaD,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAOhF;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,GAAG,UAAU,EAAE,CAInE;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAE5E;AAED,wBAAgB,sBAAsB,CACrC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,UAAU,EACf,qBAAqB,EAAE,MAAM,GAC3B,qBAAqB,CAcvB;AAED,wBAAgB,gBAAgB,CAC/B,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,qBAAqB,EAC/B,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,GACb,IAAI,CAaN;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAU7E","sourcesContent":["import type { SessionMetadata } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai\";\nimport { sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface SessionRow {\n\tid: string;\n\tcreated_at: number;\n\tparent_session_id: string | null;\n\tstorage_version: number;\n\tmetadata: string | null;\n\tmessage_count: number;\n\tusage_payload: string;\n\tnext_seq: number;\n}\n\nexport interface SqliteSessionMetadata extends SessionMetadata {\n\t/** SQLite container/shard path containing this session. */\n\tpath: string;\n}\n\nfunction zeroUsage(): Usage {\n\treturn {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheRead: 0,\n\t\tcacheWrite: 0,\n\t\ttotalTokens: 0,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t};\n}\n\nexport function readSessionRow(db: SqliteDatabase, sessionId: string): SessionRow {\n\tconst row = sql`SELECT id, created_at, parent_session_id, storage_version, metadata,\n\t\t\tmessage_count, usage_payload, next_seq\n\t\tFROM sessions\n\t\tWHERE id = ${sessionId}`.get<SessionRow>(db);\n\tif (row === undefined) throw new Error(`Unknown SQLite session: ${sessionId}`);\n\treturn row;\n}\n\nexport function readAllSessionRows(db: SqliteDatabase): SessionRow[] {\n\treturn sql`SELECT id, created_at, parent_session_id, storage_version, metadata,\n\t\t\tmessage_count, usage_payload, next_seq\n\t\tFROM sessions`.all<SessionRow>(db);\n}\n\nexport function hasSessionRow(db: SqliteDatabase, sessionId: string): boolean {\n\treturn sql`SELECT id FROM sessions WHERE id = ${sessionId}`.get<{ id: string }>(db) !== undefined;\n}\n\nexport function metadataFromSessionRow(\n\tpath: string,\n\trow: SessionRow,\n\tcurrentStorageVersion: number,\n): SqliteSessionMetadata {\n\tif (row.storage_version > currentStorageVersion) {\n\t\tthrow new Error(`SQLite session storage version ${row.storage_version} is newer than ${currentStorageVersion}`);\n\t}\n\tif (row.storage_version < currentStorageVersion) {\n\t\tthrow new Error(`SQLite session storage version ${row.storage_version} requires migrations`);\n\t}\n\treturn {\n\t\tid: row.id,\n\t\tcreatedAt: row.created_at,\n\t\tstorageVersion: row.storage_version,\n\t\t...(row.parent_session_id === null ? {} : { parentSessionId: row.parent_session_id }),\n\t\tpath,\n\t};\n}\n\nexport function insertSessionRow(\n\tdb: SqliteDatabase,\n\tmetadata: SqliteSessionMetadata,\n\tstorageVersion: number,\n\tnextSeq: number,\n): void {\n\tsql`INSERT INTO sessions\n\t\t\t(id, created_at, parent_session_id, storage_version, metadata, message_count, usage_payload, next_seq)\n\t\tVALUES (\n\t\t\t${metadata.id},\n\t\t\t${metadata.createdAt},\n\t\t\t${metadata.parentSessionId ?? null},\n\t\t\t${storageVersion},\n\t\t\t${null},\n\t\t\t${0},\n\t\t\t${JSON.stringify(zeroUsage())},\n\t\t\t${nextSeq}\n\t\t)`.run(db);\n}\n\nexport function deleteSessionRows(db: SqliteDatabase, sessionId: string): void {\n\tsql`DELETE FROM entries WHERE session_id = ${sessionId}`.run(db);\n\tsql`DELETE FROM scalar_values WHERE session_id = ${sessionId}`.run(db);\n\tsql`DELETE FROM list_values WHERE session_id = ${sessionId}`.run(db);\n\tsql`DELETE FROM usage_ledger WHERE session_id = ${sessionId}`.run(db);\n\tsql`DELETE FROM branch_entries WHERE session_id = ${sessionId}`.run(db);\n\tsql`DELETE FROM branch_meta WHERE session_id = ${sessionId}`.run(db);\n\tconst result = sql`DELETE FROM sessions WHERE id = ${sessionId}`.run(db);\n\tif (result.changes !== 1)\n\t\tthrow new Error(`Expected to delete one SQLite session ${sessionId}, deleted ${result.changes}`);\n}\n"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { sql } from "../sql.js";
|
|
2
|
+
function zeroUsage() {
|
|
3
|
+
return {
|
|
4
|
+
input: 0,
|
|
5
|
+
output: 0,
|
|
6
|
+
cacheRead: 0,
|
|
7
|
+
cacheWrite: 0,
|
|
8
|
+
totalTokens: 0,
|
|
9
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function readSessionRow(db, sessionId) {
|
|
13
|
+
const row = sql `SELECT id, created_at, parent_session_id, storage_version, metadata,
|
|
14
|
+
message_count, usage_payload, next_seq
|
|
15
|
+
FROM sessions
|
|
16
|
+
WHERE id = ${sessionId}`.get(db);
|
|
17
|
+
if (row === undefined)
|
|
18
|
+
throw new Error(`Unknown SQLite session: ${sessionId}`);
|
|
19
|
+
return row;
|
|
20
|
+
}
|
|
21
|
+
export function readAllSessionRows(db) {
|
|
22
|
+
return sql `SELECT id, created_at, parent_session_id, storage_version, metadata,
|
|
23
|
+
message_count, usage_payload, next_seq
|
|
24
|
+
FROM sessions`.all(db);
|
|
25
|
+
}
|
|
26
|
+
export function hasSessionRow(db, sessionId) {
|
|
27
|
+
return sql `SELECT id FROM sessions WHERE id = ${sessionId}`.get(db) !== undefined;
|
|
28
|
+
}
|
|
29
|
+
export function metadataFromSessionRow(path, row, currentStorageVersion) {
|
|
30
|
+
if (row.storage_version > currentStorageVersion) {
|
|
31
|
+
throw new Error(`SQLite session storage version ${row.storage_version} is newer than ${currentStorageVersion}`);
|
|
32
|
+
}
|
|
33
|
+
if (row.storage_version < currentStorageVersion) {
|
|
34
|
+
throw new Error(`SQLite session storage version ${row.storage_version} requires migrations`);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
id: row.id,
|
|
38
|
+
createdAt: row.created_at,
|
|
39
|
+
storageVersion: row.storage_version,
|
|
40
|
+
...(row.parent_session_id === null ? {} : { parentSessionId: row.parent_session_id }),
|
|
41
|
+
path,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function insertSessionRow(db, metadata, storageVersion, nextSeq) {
|
|
45
|
+
sql `INSERT INTO sessions
|
|
46
|
+
(id, created_at, parent_session_id, storage_version, metadata, message_count, usage_payload, next_seq)
|
|
47
|
+
VALUES (
|
|
48
|
+
${metadata.id},
|
|
49
|
+
${metadata.createdAt},
|
|
50
|
+
${metadata.parentSessionId ?? null},
|
|
51
|
+
${storageVersion},
|
|
52
|
+
${null},
|
|
53
|
+
${0},
|
|
54
|
+
${JSON.stringify(zeroUsage())},
|
|
55
|
+
${nextSeq}
|
|
56
|
+
)`.run(db);
|
|
57
|
+
}
|
|
58
|
+
export function deleteSessionRows(db, sessionId) {
|
|
59
|
+
sql `DELETE FROM entries WHERE session_id = ${sessionId}`.run(db);
|
|
60
|
+
sql `DELETE FROM scalar_values WHERE session_id = ${sessionId}`.run(db);
|
|
61
|
+
sql `DELETE FROM list_values WHERE session_id = ${sessionId}`.run(db);
|
|
62
|
+
sql `DELETE FROM usage_ledger WHERE session_id = ${sessionId}`.run(db);
|
|
63
|
+
sql `DELETE FROM branch_entries WHERE session_id = ${sessionId}`.run(db);
|
|
64
|
+
sql `DELETE FROM branch_meta WHERE session_id = ${sessionId}`.run(db);
|
|
65
|
+
const result = sql `DELETE FROM sessions WHERE id = ${sessionId}`.run(db);
|
|
66
|
+
if (result.changes !== 1)
|
|
67
|
+
throw new Error(`Expected to delete one SQLite session ${sessionId}, deleted ${result.changes}`);
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=session-row.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-row.js","sourceRoot":"","sources":["../../../src/sqlite/session/session-row.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionMetadata } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai\";\nimport { sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface SessionRow {\n\tid: string;\n\tcreated_at: number;\n\tparent_session_id: string | null;\n\tstorage_version: number;\n\tmetadata: string | null;\n\tmessage_count: number;\n\tusage_payload: string;\n\tnext_seq: number;\n}\n\nexport interface SqliteSessionMetadata extends SessionMetadata {\n\t/** SQLite container/shard path containing this session. */\n\tpath: string;\n}\n\nfunction zeroUsage(): Usage {\n\treturn {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheRead: 0,\n\t\tcacheWrite: 0,\n\t\ttotalTokens: 0,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t};\n}\n\nexport function readSessionRow(db: SqliteDatabase, sessionId: string): SessionRow {\n\tconst row = sql`SELECT id, created_at, parent_session_id, storage_version, metadata,\n\t\t\tmessage_count, usage_payload, next_seq\n\t\tFROM sessions\n\t\tWHERE id = ${sessionId}`.get<SessionRow>(db);\n\tif (row === undefined) throw new Error(`Unknown SQLite session: ${sessionId}`);\n\treturn row;\n}\n\nexport function readAllSessionRows(db: SqliteDatabase): SessionRow[] {\n\treturn sql`SELECT id, created_at, parent_session_id, storage_version, metadata,\n\t\t\tmessage_count, usage_payload, next_seq\n\t\tFROM sessions`.all<SessionRow>(db);\n}\n\nexport function hasSessionRow(db: SqliteDatabase, sessionId: string): boolean {\n\treturn sql`SELECT id FROM sessions WHERE id = ${sessionId}`.get<{ id: string }>(db) !== undefined;\n}\n\nexport function metadataFromSessionRow(\n\tpath: string,\n\trow: SessionRow,\n\tcurrentStorageVersion: number,\n): SqliteSessionMetadata {\n\tif (row.storage_version > currentStorageVersion) {\n\t\tthrow new Error(`SQLite session storage version ${row.storage_version} is newer than ${currentStorageVersion}`);\n\t}\n\tif (row.storage_version < currentStorageVersion) {\n\t\tthrow new Error(`SQLite session storage version ${row.storage_version} requires migrations`);\n\t}\n\treturn {\n\t\tid: row.id,\n\t\tcreatedAt: row.created_at,\n\t\tstorageVersion: row.storage_version,\n\t\t...(row.parent_session_id === null ? {} : { parentSessionId: row.parent_session_id }),\n\t\tpath,\n\t};\n}\n\nexport function insertSessionRow(\n\tdb: SqliteDatabase,\n\tmetadata: SqliteSessionMetadata,\n\tstorageVersion: number,\n\tnextSeq: number,\n): void {\n\tsql`INSERT INTO sessions\n\t\t\t(id, created_at, parent_session_id, storage_version, metadata, message_count, usage_payload, next_seq)\n\t\tVALUES (\n\t\t\t${metadata.id},\n\t\t\t${metadata.createdAt},\n\t\t\t${metadata.parentSessionId ?? null},\n\t\t\t${storageVersion},\n\t\t\t${null},\n\t\t\t${0},\n\t\t\t${JSON.stringify(zeroUsage())},\n\t\t\t${nextSeq}\n\t\t)`.run(db);\n}\n\nexport function deleteSessionRows(db: SqliteDatabase, sessionId: string): void {\n\tsql`DELETE FROM entries WHERE session_id = ${sessionId}`.run(db);\n\tsql`DELETE FROM scalar_values WHERE session_id = ${sessionId}`.run(db);\n\tsql`DELETE FROM list_values WHERE session_id = ${sessionId}`.run(db);\n\tsql`DELETE FROM usage_ledger WHERE session_id = ${sessionId}`.run(db);\n\tsql`DELETE FROM branch_entries WHERE session_id = ${sessionId}`.run(db);\n\tsql`DELETE FROM branch_meta WHERE session_id = ${sessionId}`.run(db);\n\tconst result = sql`DELETE FROM sessions WHERE id = ${sessionId}`.run(db);\n\tif (result.changes !== 1)\n\t\tthrow new Error(`Expected to delete one SQLite session ${sessionId}, deleted ${result.changes}`);\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SqliteDatabase } from "../types.ts";
|
|
2
|
+
export declare function readNextSeq(db: SqliteDatabase, sessionId: string): number;
|
|
3
|
+
export declare function advanceNextSeq(db: SqliteDatabase, sessionId: string, nextSeq: number): void;
|
|
4
|
+
//# sourceMappingURL=session-sequences.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-sequences.d.ts","sourceRoot":"","sources":["../../../src/sqlite/session/session-sequences.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAIzE;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAI3F","sourcesContent":["import { sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport function readNextSeq(db: SqliteDatabase, sessionId: string): number {\n\tconst row = sql`SELECT next_seq FROM sessions WHERE id = ${sessionId}`.get<{ next_seq: number }>(db);\n\tif (row === undefined) throw new Error(`Unknown SQLite session: ${sessionId}`);\n\treturn row.next_seq;\n}\n\nexport function advanceNextSeq(db: SqliteDatabase, sessionId: string, nextSeq: number): void {\n\tconst result = sql`UPDATE sessions SET next_seq = ${nextSeq} WHERE id = ${sessionId}`.run(db);\n\tif (result.changes !== 1)\n\t\tthrow new Error(`Expected to update one SQLite session ${sessionId}, updated ${result.changes}`);\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { sql } from "../sql.js";
|
|
2
|
+
export function readNextSeq(db, sessionId) {
|
|
3
|
+
const row = sql `SELECT next_seq FROM sessions WHERE id = ${sessionId}`.get(db);
|
|
4
|
+
if (row === undefined)
|
|
5
|
+
throw new Error(`Unknown SQLite session: ${sessionId}`);
|
|
6
|
+
return row.next_seq;
|
|
7
|
+
}
|
|
8
|
+
export function advanceNextSeq(db, sessionId, nextSeq) {
|
|
9
|
+
const result = sql `UPDATE sessions SET next_seq = ${nextSeq} WHERE id = ${sessionId}`.run(db);
|
|
10
|
+
if (result.changes !== 1)
|
|
11
|
+
throw new Error(`Expected to update one SQLite session ${sessionId}, updated ${result.changes}`);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=session-sequences.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-sequences.js","sourceRoot":"","sources":["../../../src/sqlite/session/session-sequences.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAGhC,MAAM,UAAU,WAAW,CAAC,EAAkB,EAAE,SAAiB,EAAU;IAC1E,MAAM,GAAG,GAAG,GAAG,CAAA,4CAA4C,SAAS,EAAE,CAAC,GAAG,CAAuB,EAAE,CAAC,CAAC;IACrG,IAAI,GAAG,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,SAAS,EAAE,CAAC,CAAC;IAC/E,OAAO,GAAG,CAAC,QAAQ,CAAC;AAAA,CACpB;AAED,MAAM,UAAU,cAAc,CAAC,EAAkB,EAAE,SAAiB,EAAE,OAAe,EAAQ;IAC5F,MAAM,MAAM,GAAG,GAAG,CAAA,kCAAkC,OAAO,eAAe,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9F,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,yCAAyC,SAAS,aAAa,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;AAAA,CAClG","sourcesContent":["import { sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport function readNextSeq(db: SqliteDatabase, sessionId: string): number {\n\tconst row = sql`SELECT next_seq FROM sessions WHERE id = ${sessionId}`.get<{ next_seq: number }>(db);\n\tif (row === undefined) throw new Error(`Unknown SQLite session: ${sessionId}`);\n\treturn row.next_seq;\n}\n\nexport function advanceNextSeq(db: SqliteDatabase, sessionId: string, nextSeq: number): void {\n\tconst result = sql`UPDATE sessions SET next_seq = ${nextSeq} WHERE id = ${sessionId}`.run(db);\n\tif (result.changes !== 1)\n\t\tthrow new Error(`Expected to update one SQLite session ${sessionId}, updated ${result.changes}`);\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SessionStats, UsageRow } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import type { SqliteDatabase } from "../types.ts";
|
|
3
|
+
export declare function readSessionStats(db: SqliteDatabase, sessionId: string): SessionStats;
|
|
4
|
+
export declare function incrementMessageCount(db: SqliteDatabase, sessionId: string): void;
|
|
5
|
+
export declare function addUsageToSessionStats(db: SqliteDatabase, sessionId: string, usage: UsageRow["usage"]): void;
|
|
6
|
+
//# sourceMappingURL=session-stats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-stats.d.ts","sourceRoot":"","sources":["../../../src/sqlite/session/session-stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAG5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA0BlD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,CAMpF;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAEjF;AAED,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAG5G","sourcesContent":["import type { SessionStats, UsageRow } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai\";\nimport { sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\nimport { readSessionRow } from \"./session-row.ts\";\n\nfunction addUsage(left: Usage, right: Usage): Usage {\n\treturn {\n\t\tinput: left.input + right.input,\n\t\toutput: left.output + right.output,\n\t\tcacheRead: left.cacheRead + right.cacheRead,\n\t\tcacheWrite: left.cacheWrite + right.cacheWrite,\n\t\t...(left.cacheWrite1h === undefined && right.cacheWrite1h === undefined\n\t\t\t? {}\n\t\t\t: { cacheWrite1h: (left.cacheWrite1h ?? 0) + (right.cacheWrite1h ?? 0) }),\n\t\t...(left.reasoning === undefined && right.reasoning === undefined\n\t\t\t? {}\n\t\t\t: { reasoning: (left.reasoning ?? 0) + (right.reasoning ?? 0) }),\n\t\ttotalTokens: left.totalTokens + right.totalTokens,\n\t\tcost: {\n\t\t\tinput: left.cost.input + right.cost.input,\n\t\t\toutput: left.cost.output + right.cost.output,\n\t\t\tcacheRead: left.cost.cacheRead + right.cost.cacheRead,\n\t\t\tcacheWrite: left.cost.cacheWrite + right.cost.cacheWrite,\n\t\t\ttotal: left.cost.total + right.cost.total,\n\t\t},\n\t};\n}\n\nexport function readSessionStats(db: SqliteDatabase, sessionId: string): SessionStats {\n\tconst row = readSessionRow(db, sessionId);\n\treturn {\n\t\tmessageCount: row.message_count,\n\t\tusage: JSON.parse(row.usage_payload) as Usage,\n\t};\n}\n\nexport function incrementMessageCount(db: SqliteDatabase, sessionId: string): void {\n\tsql`UPDATE sessions SET message_count = message_count + 1 WHERE id = ${sessionId}`.run(db);\n}\n\nexport function addUsageToSessionStats(db: SqliteDatabase, sessionId: string, usage: UsageRow[\"usage\"]): void {\n\tconst current = readSessionStats(db, sessionId).usage;\n\tsql`UPDATE sessions SET usage_payload = ${JSON.stringify(addUsage(current, usage))} WHERE id = ${sessionId}`.run(db);\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { sql } from "../sql.js";
|
|
2
|
+
import { readSessionRow } from "./session-row.js";
|
|
3
|
+
function addUsage(left, right) {
|
|
4
|
+
return {
|
|
5
|
+
input: left.input + right.input,
|
|
6
|
+
output: left.output + right.output,
|
|
7
|
+
cacheRead: left.cacheRead + right.cacheRead,
|
|
8
|
+
cacheWrite: left.cacheWrite + right.cacheWrite,
|
|
9
|
+
...(left.cacheWrite1h === undefined && right.cacheWrite1h === undefined
|
|
10
|
+
? {}
|
|
11
|
+
: { cacheWrite1h: (left.cacheWrite1h ?? 0) + (right.cacheWrite1h ?? 0) }),
|
|
12
|
+
...(left.reasoning === undefined && right.reasoning === undefined
|
|
13
|
+
? {}
|
|
14
|
+
: { reasoning: (left.reasoning ?? 0) + (right.reasoning ?? 0) }),
|
|
15
|
+
totalTokens: left.totalTokens + right.totalTokens,
|
|
16
|
+
cost: {
|
|
17
|
+
input: left.cost.input + right.cost.input,
|
|
18
|
+
output: left.cost.output + right.cost.output,
|
|
19
|
+
cacheRead: left.cost.cacheRead + right.cost.cacheRead,
|
|
20
|
+
cacheWrite: left.cost.cacheWrite + right.cost.cacheWrite,
|
|
21
|
+
total: left.cost.total + right.cost.total,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function readSessionStats(db, sessionId) {
|
|
26
|
+
const row = readSessionRow(db, sessionId);
|
|
27
|
+
return {
|
|
28
|
+
messageCount: row.message_count,
|
|
29
|
+
usage: JSON.parse(row.usage_payload),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function incrementMessageCount(db, sessionId) {
|
|
33
|
+
sql `UPDATE sessions SET message_count = message_count + 1 WHERE id = ${sessionId}`.run(db);
|
|
34
|
+
}
|
|
35
|
+
export function addUsageToSessionStats(db, sessionId, usage) {
|
|
36
|
+
const current = readSessionStats(db, sessionId).usage;
|
|
37
|
+
sql `UPDATE sessions SET usage_payload = ${JSON.stringify(addUsage(current, usage))} WHERE id = ${sessionId}`.run(db);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=session-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-stats.js","sourceRoot":"","sources":["../../../src/sqlite/session/session-stats.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,SAAS,QAAQ,CAAC,IAAW,EAAE,KAAY,EAAS;IACnD,OAAO;QACN,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK;QAC/B,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM;QAClC,SAAS,EAAE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS;QAC3C,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU;QAC9C,GAAG,CAAC,IAAI,CAAC,YAAY,KAAK,SAAS,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS;YACtE,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,CAAC,EAAE,CAAC;QAC1E,GAAG,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;YAChE,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC;QACjE,WAAW,EAAE,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW;QACjD,IAAI,EAAE;YACL,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK;YACzC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM;YAC5C,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS;YACrD,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU;YACxD,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK;SACzC;KACD,CAAC;AAAA,CACF;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAkB,EAAE,SAAiB,EAAgB;IACrF,MAAM,GAAG,GAAG,cAAc,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAC1C,OAAO;QACN,YAAY,EAAE,GAAG,CAAC,aAAa;QAC/B,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAAU;KAC7C,CAAC;AAAA,CACF;AAED,MAAM,UAAU,qBAAqB,CAAC,EAAkB,EAAE,SAAiB,EAAQ;IAClF,GAAG,CAAA,oEAAoE,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CAC3F;AAED,MAAM,UAAU,sBAAsB,CAAC,EAAkB,EAAE,SAAiB,EAAE,KAAwB,EAAQ;IAC7G,MAAM,OAAO,GAAG,gBAAgB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC,KAAK,CAAC;IACtD,GAAG,CAAA,uCAAuC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,eAAe,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAAA,CACrH","sourcesContent":["import type { SessionStats, UsageRow } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai\";\nimport { sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\nimport { readSessionRow } from \"./session-row.ts\";\n\nfunction addUsage(left: Usage, right: Usage): Usage {\n\treturn {\n\t\tinput: left.input + right.input,\n\t\toutput: left.output + right.output,\n\t\tcacheRead: left.cacheRead + right.cacheRead,\n\t\tcacheWrite: left.cacheWrite + right.cacheWrite,\n\t\t...(left.cacheWrite1h === undefined && right.cacheWrite1h === undefined\n\t\t\t? {}\n\t\t\t: { cacheWrite1h: (left.cacheWrite1h ?? 0) + (right.cacheWrite1h ?? 0) }),\n\t\t...(left.reasoning === undefined && right.reasoning === undefined\n\t\t\t? {}\n\t\t\t: { reasoning: (left.reasoning ?? 0) + (right.reasoning ?? 0) }),\n\t\ttotalTokens: left.totalTokens + right.totalTokens,\n\t\tcost: {\n\t\t\tinput: left.cost.input + right.cost.input,\n\t\t\toutput: left.cost.output + right.cost.output,\n\t\t\tcacheRead: left.cost.cacheRead + right.cost.cacheRead,\n\t\t\tcacheWrite: left.cost.cacheWrite + right.cost.cacheWrite,\n\t\t\ttotal: left.cost.total + right.cost.total,\n\t\t},\n\t};\n}\n\nexport function readSessionStats(db: SqliteDatabase, sessionId: string): SessionStats {\n\tconst row = readSessionRow(db, sessionId);\n\treturn {\n\t\tmessageCount: row.message_count,\n\t\tusage: JSON.parse(row.usage_payload) as Usage,\n\t};\n}\n\nexport function incrementMessageCount(db: SqliteDatabase, sessionId: string): void {\n\tsql`UPDATE sessions SET message_count = message_count + 1 WHERE id = ${sessionId}`.run(db);\n}\n\nexport function addUsageToSessionStats(db: SqliteDatabase, sessionId: string, usage: UsageRow[\"usage\"]): void {\n\tconst current = readSessionStats(db, sessionId).usage;\n\tsql`UPDATE sessions SET usage_payload = ${JSON.stringify(addUsage(current, usage))} WHERE id = ${sessionId}`.run(db);\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { UsageRow, UsageScan } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import type { SqliteDatabase } from "../types.ts";
|
|
3
|
+
export interface UsageLedgerRow {
|
|
4
|
+
id: string;
|
|
5
|
+
seq: number;
|
|
6
|
+
entry_id: string | null;
|
|
7
|
+
adjustment: number;
|
|
8
|
+
usage: string;
|
|
9
|
+
details: string | null;
|
|
10
|
+
}
|
|
11
|
+
export declare class UsageLedgerRowWriter {
|
|
12
|
+
private readonly insertStatement;
|
|
13
|
+
private readonly sessionId;
|
|
14
|
+
constructor(db: SqliteDatabase, sessionId: string);
|
|
15
|
+
insert(row: UsageRow): void;
|
|
16
|
+
}
|
|
17
|
+
export declare function insertUsageLedgerRow(db: SqliteDatabase, sessionId: string, row: UsageRow): void;
|
|
18
|
+
export declare function decodeUsageLedgerRow(row: UsageLedgerRow): UsageRow;
|
|
19
|
+
export declare function scanUsageLedgerRows(db: SqliteDatabase, sessionId: string, query: UsageScan): UsageLedgerRow[];
|
|
20
|
+
//# sourceMappingURL=usage-ledger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-ledger.d.ts","sourceRoot":"","sources":["../../../src/sqlite/session/usage-ledger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAEzE,OAAO,KAAK,EAAE,cAAc,EAAmB,MAAM,aAAa,CAAC;AAEnE,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAiBD,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,YAAY,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAGhD;IAED,MAAM,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAE1B;CACD;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,CAE/F;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,QAAQ,CASlE;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,cAAc,EAAE,CAS7G","sourcesContent":["import type { UsageRow, UsageScan } from \"@earendil-works/pi-agent-core\";\nimport { joinSqlFragments, type SqlQuery, sql } from \"../sql.ts\";\nimport type { SqliteDatabase, SqliteStatement } from \"../types.ts\";\n\nexport interface UsageLedgerRow {\n\tid: string;\n\tseq: number;\n\tentry_id: string | null;\n\tadjustment: number;\n\tusage: string;\n\tdetails: string | null;\n}\n\nconst INSERT_USAGE_LEDGER_SQL = `INSERT INTO usage_ledger (session_id, id, seq, entry_id, adjustment, usage, details)\n\tVALUES (?, ?, ?, ?, ?, ?, ?)`;\n\nfunction usageLedgerRowParams(sessionId: string, row: UsageRow): unknown[] {\n\treturn [\n\t\tsessionId,\n\t\trow.id,\n\t\trow.seq,\n\t\trow.entryId ?? null,\n\t\trow.adjustment ? 1 : 0,\n\t\tJSON.stringify(row.usage),\n\t\trow.details === undefined ? null : JSON.stringify(row.details),\n\t];\n}\n\nexport class UsageLedgerRowWriter {\n\tprivate readonly insertStatement: SqliteStatement;\n\tprivate readonly sessionId: string;\n\n\tconstructor(db: SqliteDatabase, sessionId: string) {\n\t\tthis.insertStatement = db.prepare(INSERT_USAGE_LEDGER_SQL);\n\t\tthis.sessionId = sessionId;\n\t}\n\n\tinsert(row: UsageRow): void {\n\t\tthis.insertStatement.run(...usageLedgerRowParams(this.sessionId, row));\n\t}\n}\n\nexport function insertUsageLedgerRow(db: SqliteDatabase, sessionId: string, row: UsageRow): void {\n\tdb.prepare(INSERT_USAGE_LEDGER_SQL).run(...usageLedgerRowParams(sessionId, row));\n}\n\nexport function decodeUsageLedgerRow(row: UsageLedgerRow): UsageRow {\n\treturn {\n\t\tid: row.id,\n\t\tseq: row.seq,\n\t\tusage: JSON.parse(row.usage) as UsageRow[\"usage\"],\n\t\t...(row.entry_id === null ? {} : { entryId: row.entry_id }),\n\t\tadjustment: row.adjustment !== 0,\n\t\t...(row.details === null ? {} : { details: JSON.parse(row.details) as UsageRow[\"details\"] }),\n\t};\n}\n\nexport function scanUsageLedgerRows(db: SqliteDatabase, sessionId: string, query: UsageScan): UsageLedgerRow[] {\n\tconst filters: SqlQuery[] = [sql`session_id = ${sessionId}`];\n\tif (query.fromSeq !== undefined) filters.push(sql`seq >= ${query.fromSeq}`);\n\tif (query.toSeq !== undefined) filters.push(sql`seq <= ${query.toSeq}`);\n\n\tconst order = query.order === \"desc\" ? sql`ORDER BY seq DESC` : sql`ORDER BY seq ASC`;\n\tconst limit = query.limit === undefined ? sql`` : sql`LIMIT ${Math.max(0, query.limit)}`;\n\treturn sql`SELECT id, seq, entry_id, adjustment, usage, details\n\t\tFROM usage_ledger WHERE ${joinSqlFragments(filters, \" AND \")} ${order} ${limit}`.all<UsageLedgerRow>(db);\n}\n"]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { joinSqlFragments, sql } from "../sql.js";
|
|
2
|
+
const INSERT_USAGE_LEDGER_SQL = `INSERT INTO usage_ledger (session_id, id, seq, entry_id, adjustment, usage, details)
|
|
3
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`;
|
|
4
|
+
function usageLedgerRowParams(sessionId, row) {
|
|
5
|
+
return [
|
|
6
|
+
sessionId,
|
|
7
|
+
row.id,
|
|
8
|
+
row.seq,
|
|
9
|
+
row.entryId ?? null,
|
|
10
|
+
row.adjustment ? 1 : 0,
|
|
11
|
+
JSON.stringify(row.usage),
|
|
12
|
+
row.details === undefined ? null : JSON.stringify(row.details),
|
|
13
|
+
];
|
|
14
|
+
}
|
|
15
|
+
export class UsageLedgerRowWriter {
|
|
16
|
+
insertStatement;
|
|
17
|
+
sessionId;
|
|
18
|
+
constructor(db, sessionId) {
|
|
19
|
+
this.insertStatement = db.prepare(INSERT_USAGE_LEDGER_SQL);
|
|
20
|
+
this.sessionId = sessionId;
|
|
21
|
+
}
|
|
22
|
+
insert(row) {
|
|
23
|
+
this.insertStatement.run(...usageLedgerRowParams(this.sessionId, row));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function insertUsageLedgerRow(db, sessionId, row) {
|
|
27
|
+
db.prepare(INSERT_USAGE_LEDGER_SQL).run(...usageLedgerRowParams(sessionId, row));
|
|
28
|
+
}
|
|
29
|
+
export function decodeUsageLedgerRow(row) {
|
|
30
|
+
return {
|
|
31
|
+
id: row.id,
|
|
32
|
+
seq: row.seq,
|
|
33
|
+
usage: JSON.parse(row.usage),
|
|
34
|
+
...(row.entry_id === null ? {} : { entryId: row.entry_id }),
|
|
35
|
+
adjustment: row.adjustment !== 0,
|
|
36
|
+
...(row.details === null ? {} : { details: JSON.parse(row.details) }),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function scanUsageLedgerRows(db, sessionId, query) {
|
|
40
|
+
const filters = [sql `session_id = ${sessionId}`];
|
|
41
|
+
if (query.fromSeq !== undefined)
|
|
42
|
+
filters.push(sql `seq >= ${query.fromSeq}`);
|
|
43
|
+
if (query.toSeq !== undefined)
|
|
44
|
+
filters.push(sql `seq <= ${query.toSeq}`);
|
|
45
|
+
const order = query.order === "desc" ? sql `ORDER BY seq DESC` : sql `ORDER BY seq ASC`;
|
|
46
|
+
const limit = query.limit === undefined ? sql `` : sql `LIMIT ${Math.max(0, query.limit)}`;
|
|
47
|
+
return sql `SELECT id, seq, entry_id, adjustment, usage, details
|
|
48
|
+
FROM usage_ledger WHERE ${joinSqlFragments(filters, " AND ")} ${order} ${limit}`.all(db);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=usage-ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-ledger.js","sourceRoot":"","sources":["../../../src/sqlite/session/usage-ledger.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UsageRow, UsageScan } from \"@earendil-works/pi-agent-core\";\nimport { joinSqlFragments, type SqlQuery, sql } from \"../sql.ts\";\nimport type { SqliteDatabase, SqliteStatement } from \"../types.ts\";\n\nexport interface UsageLedgerRow {\n\tid: string;\n\tseq: number;\n\tentry_id: string | null;\n\tadjustment: number;\n\tusage: string;\n\tdetails: string | null;\n}\n\nconst INSERT_USAGE_LEDGER_SQL = `INSERT INTO usage_ledger (session_id, id, seq, entry_id, adjustment, usage, details)\n\tVALUES (?, ?, ?, ?, ?, ?, ?)`;\n\nfunction usageLedgerRowParams(sessionId: string, row: UsageRow): unknown[] {\n\treturn [\n\t\tsessionId,\n\t\trow.id,\n\t\trow.seq,\n\t\trow.entryId ?? null,\n\t\trow.adjustment ? 1 : 0,\n\t\tJSON.stringify(row.usage),\n\t\trow.details === undefined ? null : JSON.stringify(row.details),\n\t];\n}\n\nexport class UsageLedgerRowWriter {\n\tprivate readonly insertStatement: SqliteStatement;\n\tprivate readonly sessionId: string;\n\n\tconstructor(db: SqliteDatabase, sessionId: string) {\n\t\tthis.insertStatement = db.prepare(INSERT_USAGE_LEDGER_SQL);\n\t\tthis.sessionId = sessionId;\n\t}\n\n\tinsert(row: UsageRow): void {\n\t\tthis.insertStatement.run(...usageLedgerRowParams(this.sessionId, row));\n\t}\n}\n\nexport function insertUsageLedgerRow(db: SqliteDatabase, sessionId: string, row: UsageRow): void {\n\tdb.prepare(INSERT_USAGE_LEDGER_SQL).run(...usageLedgerRowParams(sessionId, row));\n}\n\nexport function decodeUsageLedgerRow(row: UsageLedgerRow): UsageRow {\n\treturn {\n\t\tid: row.id,\n\t\tseq: row.seq,\n\t\tusage: JSON.parse(row.usage) as UsageRow[\"usage\"],\n\t\t...(row.entry_id === null ? {} : { entryId: row.entry_id }),\n\t\tadjustment: row.adjustment !== 0,\n\t\t...(row.details === null ? {} : { details: JSON.parse(row.details) as UsageRow[\"details\"] }),\n\t};\n}\n\nexport function scanUsageLedgerRows(db: SqliteDatabase, sessionId: string, query: UsageScan): UsageLedgerRow[] {\n\tconst filters: SqlQuery[] = [sql`session_id = ${sessionId}`];\n\tif (query.fromSeq !== undefined) filters.push(sql`seq >= ${query.fromSeq}`);\n\tif (query.toSeq !== undefined) filters.push(sql`seq <= ${query.toSeq}`);\n\n\tconst order = query.order === \"desc\" ? sql`ORDER BY seq DESC` : sql`ORDER BY seq ASC`;\n\tconst limit = query.limit === undefined ? sql`` : sql`LIMIT ${Math.max(0, query.limit)}`;\n\treturn sql`SELECT id, seq, entry_id, adjustment, usage, details\n\t\tFROM usage_ledger WHERE ${joinSqlFragments(filters, \" AND \")} ${order} ${limit}`.all<UsageLedgerRow>(db);\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ListElement, type ListReadOptions, type StoredValue, type Value, type ValueList } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { type SqlQuery } from "../sql.ts";
|
|
3
|
+
import type { SqliteDatabase } from "../types.ts";
|
|
4
|
+
export interface ScalarValueRow {
|
|
5
|
+
namespace: string;
|
|
6
|
+
key: string;
|
|
7
|
+
seq: number;
|
|
8
|
+
value: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ListValueRow {
|
|
11
|
+
seq: number;
|
|
12
|
+
value: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function setScalarValueRow(db: SqliteDatabase, sessionId: string, namespace: string, key: string, seq: number, storedValue: unknown): void;
|
|
15
|
+
export declare function deleteScalarValueRow(db: SqliteDatabase, sessionId: string, namespace: string, key: string): void;
|
|
16
|
+
export declare function appendListValueRow(db: SqliteDatabase, sessionId: string, namespace: string, key: string, seq: number, element: unknown): void;
|
|
17
|
+
export declare function deleteListValueRows(db: SqliteDatabase, sessionId: string, namespace: string, key: string): void;
|
|
18
|
+
export declare function readScalarValueRow<T>(db: SqliteDatabase, sessionId: string, address: Value<T>): StoredValue<T> | undefined;
|
|
19
|
+
export declare function readAllScalarValueRows(db: SqliteDatabase, sessionId: string): StoredValue<unknown>[];
|
|
20
|
+
export declare function scanScalarValueRows<T>(db: SqliteDatabase, sessionId: string, prefix: Value<T>): StoredValue<T>[];
|
|
21
|
+
export declare function listValueReadQuery<T>(sessionId: string, address: ValueList<T>, options?: ListReadOptions): SqlQuery;
|
|
22
|
+
export declare function readListValueRows<T>(db: SqliteDatabase, sessionId: string, address: ValueList<T>, options?: ListReadOptions): ListElement<T>[];
|
|
23
|
+
//# sourceMappingURL=values.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../../../src/sqlite/session/values.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,WAAW,EAChB,KAAK,eAAe,EAEpB,KAAK,WAAW,EAChB,KAAK,KAAK,EACV,KAAK,SAAS,EAEd,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,KAAK,QAAQ,EAAO,MAAM,WAAW,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,cAAc;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,iBAAiB,CAChC,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,OAAO,GAClB,IAAI,CAIN;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAGhH;AAED,wBAAgB,kBAAkB,CACjC,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,GACd,IAAI,CAGN;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAG/G;AASD,wBAAgB,kBAAkB,CAAC,CAAC,EACnC,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,GACf,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAM5B;AAED,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,EAAE,CASpG;AAgBD,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,CAWhH;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,QAAQ,CAkBnH;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAClC,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EACrB,OAAO,CAAC,EAAE,eAAe,GACvB,WAAW,CAAC,CAAC,CAAC,EAAE,CAIlB","sourcesContent":["import {\n\ttype ListElement,\n\ttype ListReadOptions,\n\tresolveListReadOptions,\n\ttype StoredValue,\n\ttype Value,\n\ttype ValueList,\n\tvalue,\n} from \"@earendil-works/pi-agent-core\";\nimport { type SqlQuery, sql } from \"../sql.ts\";\nimport type { SqliteDatabase } from \"../types.ts\";\n\nexport interface ScalarValueRow {\n\tnamespace: string;\n\tkey: string;\n\tseq: number;\n\tvalue: string;\n}\n\nexport interface ListValueRow {\n\tseq: number;\n\tvalue: string;\n}\n\nexport function setScalarValueRow(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tnamespace: string,\n\tkey: string,\n\tseq: number,\n\tstoredValue: unknown,\n): void {\n\tsql`INSERT INTO scalar_values (session_id, namespace, key, seq, value)\n\t\tVALUES (${sessionId}, ${namespace}, ${key}, ${seq}, ${JSON.stringify(storedValue)})\n\t\tON CONFLICT(session_id, namespace, key) DO UPDATE SET seq = excluded.seq, value = excluded.value`.run(db);\n}\n\nexport function deleteScalarValueRow(db: SqliteDatabase, sessionId: string, namespace: string, key: string): void {\n\tsql`DELETE FROM scalar_values\n\t\tWHERE session_id = ${sessionId} AND namespace = ${namespace} AND key = ${key}`.run(db);\n}\n\nexport function appendListValueRow(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tnamespace: string,\n\tkey: string,\n\tseq: number,\n\telement: unknown,\n): void {\n\tsql`INSERT INTO list_values (session_id, namespace, key, seq, value)\n\t\tVALUES (${sessionId}, ${namespace}, ${key}, ${seq}, ${JSON.stringify(element)})`.run(db);\n}\n\nexport function deleteListValueRows(db: SqliteDatabase, sessionId: string, namespace: string, key: string): void {\n\tsql`DELETE FROM list_values\n\t\tWHERE session_id = ${sessionId} AND namespace = ${namespace} AND key = ${key}`.run(db);\n}\n\nfunction decodeScalarValueRow<T>(address: Value<T>, row: ScalarValueRow): StoredValue<T> {\n\tif (row.namespace !== address.namespace || row.key !== address.key) {\n\t\tthrow new Error(`Expected value ${address.namespace}:${address.key}, found ${row.namespace}:${row.key}`);\n\t}\n\treturn { address, seq: row.seq, value: JSON.parse(row.value) as T };\n}\n\nexport function readScalarValueRow<T>(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\taddress: Value<T>,\n): StoredValue<T> | undefined {\n\tconst row = sql`SELECT namespace, key, seq, value FROM scalar_values\n\t\tWHERE session_id = ${sessionId} AND namespace = ${address.namespace} AND key = ${address.key}`.get<ScalarValueRow>(\n\t\tdb,\n\t);\n\treturn row === undefined ? undefined : decodeScalarValueRow(address, row);\n}\n\nexport function readAllScalarValueRows(db: SqliteDatabase, sessionId: string): StoredValue<unknown>[] {\n\treturn sql`SELECT namespace, key, seq, value FROM scalar_values\n\t\tWHERE session_id = ${sessionId} ORDER BY seq ASC`\n\t\t.all<ScalarValueRow>(db)\n\t\t.map((row) => ({\n\t\t\taddress: value<unknown>(row.namespace, row.key),\n\t\t\tseq: row.seq,\n\t\t\tvalue: JSON.parse(row.value),\n\t\t}));\n}\n\nfunction nextPrefixBoundary(prefix: string): string | undefined {\n\tif (prefix === \"\") return undefined;\n\tconst codePoints = Array.from(prefix);\n\tfor (let index = codePoints.length - 1; index >= 0; index--) {\n\t\tconst codePoint = codePoints[index]?.codePointAt(0);\n\t\tif (codePoint === undefined) throw new Error(\"Invalid value key prefix\");\n\t\tif (codePoint < 0x10ffff) {\n\t\t\tconst nextCodePoint = codePoint >= 0xd7ff && codePoint < 0xe000 ? 0xe000 : codePoint + 1;\n\t\t\treturn `${codePoints.slice(0, index).join(\"\")}${String.fromCodePoint(nextCodePoint)}`;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport function scanScalarValueRows<T>(db: SqliteDatabase, sessionId: string, prefix: Value<T>): StoredValue<T>[] {\n\tconst upperBound = nextPrefixBoundary(prefix.key);\n\tconst rows =\n\t\tupperBound === undefined\n\t\t\t? sql`SELECT namespace, key, seq, value FROM scalar_values\n\t\t\t\tWHERE session_id = ${sessionId} AND namespace = ${prefix.namespace} AND key >= ${prefix.key}\n\t\t\t\tORDER BY key ASC`.all<ScalarValueRow>(db)\n\t\t\t: sql`SELECT namespace, key, seq, value FROM scalar_values\n\t\t\t\tWHERE session_id = ${sessionId} AND namespace = ${prefix.namespace} AND key >= ${prefix.key} AND key < ${upperBound}\n\t\t\t\tORDER BY key ASC`.all<ScalarValueRow>(db);\n\treturn rows.map((row) => decodeScalarValueRow(value<T>(row.namespace, row.key), row));\n}\n\nexport function listValueReadQuery<T>(sessionId: string, address: ValueList<T>, options?: ListReadOptions): SqlQuery {\n\tconst resolved = resolveListReadOptions(options);\n\tif (resolved.order === \"asc\") {\n\t\treturn resolved.cursor === undefined\n\t\t\t? sql`SELECT seq, value FROM list_values\n\t\t\t\tWHERE session_id = ${sessionId} AND namespace = ${address.namespace} AND key = ${address.key}\n\t\t\t\tORDER BY seq ASC LIMIT ${resolved.limit}`\n\t\t\t: sql`SELECT seq, value FROM list_values\n\t\t\t\tWHERE session_id = ${sessionId} AND namespace = ${address.namespace} AND key = ${address.key} AND seq > ${resolved.cursor.seq}\n\t\t\t\tORDER BY seq ASC LIMIT ${resolved.limit}`;\n\t}\n\treturn resolved.cursor === undefined\n\t\t? sql`SELECT seq, value FROM list_values\n\t\t\tWHERE session_id = ${sessionId} AND namespace = ${address.namespace} AND key = ${address.key}\n\t\t\tORDER BY seq DESC LIMIT ${resolved.limit}`\n\t\t: sql`SELECT seq, value FROM list_values\n\t\t\tWHERE session_id = ${sessionId} AND namespace = ${address.namespace} AND key = ${address.key} AND seq < ${resolved.cursor.seq}\n\t\t\tORDER BY seq DESC LIMIT ${resolved.limit}`;\n}\n\nexport function readListValueRows<T>(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\taddress: ValueList<T>,\n\toptions?: ListReadOptions,\n): ListElement<T>[] {\n\treturn listValueReadQuery(sessionId, address, options)\n\t\t.all<ListValueRow>(db)\n\t\t.map((row) => ({ seq: row.seq, value: JSON.parse(row.value) as T }));\n}\n"]}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { resolveListReadOptions, value, } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { sql } from "../sql.js";
|
|
3
|
+
export function setScalarValueRow(db, sessionId, namespace, key, seq, storedValue) {
|
|
4
|
+
sql `INSERT INTO scalar_values (session_id, namespace, key, seq, value)
|
|
5
|
+
VALUES (${sessionId}, ${namespace}, ${key}, ${seq}, ${JSON.stringify(storedValue)})
|
|
6
|
+
ON CONFLICT(session_id, namespace, key) DO UPDATE SET seq = excluded.seq, value = excluded.value`.run(db);
|
|
7
|
+
}
|
|
8
|
+
export function deleteScalarValueRow(db, sessionId, namespace, key) {
|
|
9
|
+
sql `DELETE FROM scalar_values
|
|
10
|
+
WHERE session_id = ${sessionId} AND namespace = ${namespace} AND key = ${key}`.run(db);
|
|
11
|
+
}
|
|
12
|
+
export function appendListValueRow(db, sessionId, namespace, key, seq, element) {
|
|
13
|
+
sql `INSERT INTO list_values (session_id, namespace, key, seq, value)
|
|
14
|
+
VALUES (${sessionId}, ${namespace}, ${key}, ${seq}, ${JSON.stringify(element)})`.run(db);
|
|
15
|
+
}
|
|
16
|
+
export function deleteListValueRows(db, sessionId, namespace, key) {
|
|
17
|
+
sql `DELETE FROM list_values
|
|
18
|
+
WHERE session_id = ${sessionId} AND namespace = ${namespace} AND key = ${key}`.run(db);
|
|
19
|
+
}
|
|
20
|
+
function decodeScalarValueRow(address, row) {
|
|
21
|
+
if (row.namespace !== address.namespace || row.key !== address.key) {
|
|
22
|
+
throw new Error(`Expected value ${address.namespace}:${address.key}, found ${row.namespace}:${row.key}`);
|
|
23
|
+
}
|
|
24
|
+
return { address, seq: row.seq, value: JSON.parse(row.value) };
|
|
25
|
+
}
|
|
26
|
+
export function readScalarValueRow(db, sessionId, address) {
|
|
27
|
+
const row = sql `SELECT namespace, key, seq, value FROM scalar_values
|
|
28
|
+
WHERE session_id = ${sessionId} AND namespace = ${address.namespace} AND key = ${address.key}`.get(db);
|
|
29
|
+
return row === undefined ? undefined : decodeScalarValueRow(address, row);
|
|
30
|
+
}
|
|
31
|
+
export function readAllScalarValueRows(db, sessionId) {
|
|
32
|
+
return sql `SELECT namespace, key, seq, value FROM scalar_values
|
|
33
|
+
WHERE session_id = ${sessionId} ORDER BY seq ASC`
|
|
34
|
+
.all(db)
|
|
35
|
+
.map((row) => ({
|
|
36
|
+
address: value(row.namespace, row.key),
|
|
37
|
+
seq: row.seq,
|
|
38
|
+
value: JSON.parse(row.value),
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
function nextPrefixBoundary(prefix) {
|
|
42
|
+
if (prefix === "")
|
|
43
|
+
return undefined;
|
|
44
|
+
const codePoints = Array.from(prefix);
|
|
45
|
+
for (let index = codePoints.length - 1; index >= 0; index--) {
|
|
46
|
+
const codePoint = codePoints[index]?.codePointAt(0);
|
|
47
|
+
if (codePoint === undefined)
|
|
48
|
+
throw new Error("Invalid value key prefix");
|
|
49
|
+
if (codePoint < 0x10ffff) {
|
|
50
|
+
const nextCodePoint = codePoint >= 0xd7ff && codePoint < 0xe000 ? 0xe000 : codePoint + 1;
|
|
51
|
+
return `${codePoints.slice(0, index).join("")}${String.fromCodePoint(nextCodePoint)}`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
export function scanScalarValueRows(db, sessionId, prefix) {
|
|
57
|
+
const upperBound = nextPrefixBoundary(prefix.key);
|
|
58
|
+
const rows = upperBound === undefined
|
|
59
|
+
? sql `SELECT namespace, key, seq, value FROM scalar_values
|
|
60
|
+
WHERE session_id = ${sessionId} AND namespace = ${prefix.namespace} AND key >= ${prefix.key}
|
|
61
|
+
ORDER BY key ASC`.all(db)
|
|
62
|
+
: sql `SELECT namespace, key, seq, value FROM scalar_values
|
|
63
|
+
WHERE session_id = ${sessionId} AND namespace = ${prefix.namespace} AND key >= ${prefix.key} AND key < ${upperBound}
|
|
64
|
+
ORDER BY key ASC`.all(db);
|
|
65
|
+
return rows.map((row) => decodeScalarValueRow(value(row.namespace, row.key), row));
|
|
66
|
+
}
|
|
67
|
+
export function listValueReadQuery(sessionId, address, options) {
|
|
68
|
+
const resolved = resolveListReadOptions(options);
|
|
69
|
+
if (resolved.order === "asc") {
|
|
70
|
+
return resolved.cursor === undefined
|
|
71
|
+
? sql `SELECT seq, value FROM list_values
|
|
72
|
+
WHERE session_id = ${sessionId} AND namespace = ${address.namespace} AND key = ${address.key}
|
|
73
|
+
ORDER BY seq ASC LIMIT ${resolved.limit}`
|
|
74
|
+
: sql `SELECT seq, value FROM list_values
|
|
75
|
+
WHERE session_id = ${sessionId} AND namespace = ${address.namespace} AND key = ${address.key} AND seq > ${resolved.cursor.seq}
|
|
76
|
+
ORDER BY seq ASC LIMIT ${resolved.limit}`;
|
|
77
|
+
}
|
|
78
|
+
return resolved.cursor === undefined
|
|
79
|
+
? sql `SELECT seq, value FROM list_values
|
|
80
|
+
WHERE session_id = ${sessionId} AND namespace = ${address.namespace} AND key = ${address.key}
|
|
81
|
+
ORDER BY seq DESC LIMIT ${resolved.limit}`
|
|
82
|
+
: sql `SELECT seq, value FROM list_values
|
|
83
|
+
WHERE session_id = ${sessionId} AND namespace = ${address.namespace} AND key = ${address.key} AND seq < ${resolved.cursor.seq}
|
|
84
|
+
ORDER BY seq DESC LIMIT ${resolved.limit}`;
|
|
85
|
+
}
|
|
86
|
+
export function readListValueRows(db, sessionId, address, options) {
|
|
87
|
+
return listValueReadQuery(sessionId, address, options)
|
|
88
|
+
.all(db)
|
|
89
|
+
.map((row) => ({ seq: row.seq, value: JSON.parse(row.value) }));
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=values.js.map
|